package shares import ( "fmt" "strings" ) // Header marks generated files so install scripts and humans can tell them apart. const Header = "# Managed by nasmon — edits are overwritten. Change shares at http://:8090/shares" func yesno(b bool) string { if b { return "yes" } return "no" } // RenderSMB produces the complete smb.conf. owner is the Unix account every // SMB session acts as (force user), so files end up with the same owner the // containers use. func RenderSMB(c Config, owner string) string { var b strings.Builder fmt.Fprintf(&b, "%s\n\n[global]\n", Header) g := []string{ "workgroup = " + c.SMB.Workgroup, "server string = " + c.SMB.ServerString, "server role = standalone server", "security = user", "map to guest = Bad User", "guest account = nobody", "server min protocol = SMB2", "unix charset = UTF-8", "logging = file", "log file = /var/log/samba/log.%m", "max log size = 1000", "log level = 1", "load printers = no", "printing = bsd", "printcap name = /dev/null", "disable spoolss = yes", "ea support = yes", "vfs objects = fruit streams_xattr", // macOS clients: resource forks / Finder metadata as xattrs "fruit:metadata = stream", "fruit:model = MacSamba", "dos filemode = yes", } for _, l := range g { fmt.Fprintf(&b, " %s\n", l) } for _, s := range c.SMB.Shares { fmt.Fprintf(&b, "\n[%s]\n", s.Name) lines := []string{ "path = " + s.Path, } if s.Comment != "" { lines = append(lines, "comment = "+s.Comment) } lines = append(lines, "browseable = "+yesno(s.Browseable), "read only = "+yesno(s.ReadOnly), "guest ok = "+yesno(s.Guest), ) if !s.Guest { lines = append(lines, "valid users = "+strings.Join(s.Users, " ")) } lines = append(lines, "force user = "+owner, "force group = "+owner, "create mask = 0664", "directory mask = 0775", ) if s.HideSysFiles { lines = append(lines, "veto files = /._*/.DS_Store/Thumbs.db/desktop.ini/", "delete veto files = yes") } for _, l := range lines { fmt.Fprintf(&b, " %s\n", l) } } return b.String() } // RenderExports produces the exports(5) file. uid/gid are the ids squashed // clients are mapped to. func RenderExports(c Config, uid, gid string) string { var b strings.Builder fmt.Fprintf(&b, "%s\n", Header) for _, e := range c.NFS.Exports { opts := []string{"rw"} if e.ReadOnly { opts = []string{"ro"} } if e.Async { opts = append(opts, "async") } else { opts = append(opts, "sync") } switch e.Squash { case "all": opts = append(opts, "all_squash", "anonuid="+uid, "anongid="+gid) case "none": opts = append(opts, "no_root_squash") default: opts = append(opts, "root_squash", "anonuid="+uid, "anongid="+gid) } // crossmnt: child mounts (ZFS datasets or other filesystems) must be reachable opts = append(opts, "crossmnt", "no_subtree_check") var parts []string for _, cl := range e.Clients { parts = append(parts, cl+"("+strings.Join(opts, ",")+")") } fmt.Fprintf(&b, "%q %s\n", e.Path, strings.Join(parts, " ")) } return b.String() }