Shares: allow any folder, not only ZFS datasets
build / build (push) Successful in 1m7s

SMB shares and NFS exports may now point to any existing directory
(USB disks under /mnt or /media, /srv, home folders...) instead of only
ZFS dataset mountpoints. System trees (/etc, /usr, /var, /boot, /proc,
/root, ...) are refused, also after resolving symlinks. The path
suggestions list mounted data filesystems from /proc/self/mounts in
addition to ZFS mountpoints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 22:48:31 +03:00
co-authored by Claude Opus 5.5
parent 78215ac100
commit b331bc736d
10 changed files with 183 additions and 37 deletions
+55
View File
@@ -0,0 +1,55 @@
package shares
import (
"sort"
"strconv"
"strings"
)
// pseudoFS are filesystem types that never hold user data.
var pseudoFS = map[string]bool{
"proc": true, "sysfs": true, "tmpfs": true, "devtmpfs": true, "devpts": true, "cgroup": true, "cgroup2": true,
"securityfs": true, "pstore": true, "bpf": true, "debugfs": true, "tracefs": true, "configfs": true,
"fusectl": true, "mqueue": true, "hugetlbfs": true, "autofs": true, "binfmt_misc": true, "nsfs": true,
"overlay": true, "squashfs": true, "efivarfs": true, "rpc_pipefs": true, "nfsd": true, "ramfs": true,
}
// ParseMounts extracts shareable mountpoints from /proc/self/mounts content:
// real filesystems outside system directories, sorted and de-duplicated.
func ParseMounts(data string) []string {
seen := map[string]bool{}
var out []string
for _, line := range strings.Split(data, "\n") {
f := strings.Fields(line)
if len(f) < 3 || pseudoFS[f[2]] {
continue
}
mp := unescapeMount(f[1])
if !strings.HasPrefix(mp, "/") || ValidateNotSystem(mp) != nil || seen[mp] {
continue
}
seen[mp] = true
out = append(out, mp)
}
sort.Strings(out)
return out
}
// unescapeMount decodes the octal escapes (\040 etc.) the kernel uses in mounts.
func unescapeMount(s string) string {
if !strings.Contains(s, `\`) {
return s
}
var b strings.Builder
for i := 0; i < len(s); i++ {
if s[i] == '\\' && i+4 <= len(s) {
if n, err := strconv.ParseUint(s[i+1:i+4], 8, 8); err == nil {
b.WriteByte(byte(n))
i += 3
continue
}
}
b.WriteByte(s[i])
}
return b.String()
}