feat(samba): Megosztas page + guarded folder picker (R-7 slice 1, Part 3)

New top-nav category with the Halozati megosztas page: enable/server-name card,
household password, shares table (Nev/Mappa/Irasvedett/Felhomentes/Torles), and
a create flow (new folder under <storage>/shares or an existing folder via the
browse modal). Every customer path goes through sharingResolvePath: absolute ->
EvalSymlinks -> containment in a registered live storage root -> deny-listed
system subtree check -> is-a-directory. Refusals are UNIFORM so the picker is
never a filesystem oracle. Deny-list derived from ProtectedHDDPaths (provably a
subset); the drive root is an exact-match denial so user-data folders under it
stay shareable. samba infra metadata + i-share icon. Gates green.
This commit is contained in:
2026-07-18 11:45:18 +02:00
parent 1d26a69dd4
commit 4f08e5e7c3
8 changed files with 807 additions and 0 deletions
+33
View File
@@ -29,6 +29,39 @@ const (
sambaUID = 1000
)
// SharingDeniedRoots returns the SYSTEM subset of ProtectedHDDPaths(root) whose SUBTREES may never
// be exported over SMB: appdata/ (live app databases — writable SMB access to them is the [R3]
// corruption foot-gun), backups/, and the legacy felhom-data nest.
//
// The drive root itself is deliberately NOT in this set: it is denied by an EXACT-match check at the
// call site ("a whole drive is not shareable"). Putting it here would make every path under the drive
// — i.e. every legitimate share — match the subtree rule and be refused.
//
// It is DERIVED from ProtectedHDDPaths, never a parallel list: each candidate is emitted only if that
// guard already contains it, so this set can only ever SHRINK relative to the delete guard — it can
// never drift into a stale second source of truth. media/ and Dokumentumok/ are protected THERE as
// delete targets but are customer data and stay shareable (Scenario B shares media/filmek).
func SharingDeniedRoots(root string) []string {
if root == "" {
return nil
}
protected := ProtectedHDDPaths(root)
candidates := []string{
filepath.Join(root, "appdata"),
filepath.Join(root, "backups"),
filepath.Join(root, felhomDataDir),
filepath.Join(root, felhomDataDir, "appdata"),
filepath.Join(root, felhomDataDir, "backups"),
}
var out []string
for _, c := range candidates {
if protected[c] {
out = append(out, c)
}
}
return out
}
// sambaDir is the samba stack directory.
func (m *Manager) sambaDir() string {
return filepath.Join(m.cfg.Paths.StacksDir, SambaStackName)