From e7982b4ee9cc935d6b89f291087524cdee229d09 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sat, 27 Dec 2025 22:09:55 +0900 Subject: [PATCH] cmd/sharefs: create directory as root This optional behaviour is required on NixOS as it is otherwise impossible to set this up: systemd.mounts breaks startup order somehow even though my unit looks identical to generated ones, fileSystems does not support any kind of initialisation or ordering other than against other mount points. Signed-off-by: Ophestra --- cmd/sharefs/fuse.go | 25 +++++++++++++++++++++++++ cmd/sharefs/test/test.py | 9 +++++++++ nixos.nix | 7 ++----- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/cmd/sharefs/fuse.go b/cmd/sharefs/fuse.go index bbbc144..9a0a014 100644 --- a/cmd/sharefs/fuse.go +++ b/cmd/sharefs/fuse.go @@ -53,6 +53,9 @@ type ( // Whether sharefs_init failed. initFailed bool + // Whether to create source directory as root. + mkdir bool + // Open file descriptor to fuse. Fuse int @@ -157,6 +160,9 @@ func parseOpts(args *fuseArgs, setup *setupState, log *log.Logger) (ok bool) { // Pathname to writable source directory. source *C.char + // Whether to create source directory as root. + mkdir C.int + // Decimal string representation of uid to set when running as root. setuid *C.char // Decimal string representation of gid to set when running as root. @@ -169,6 +175,7 @@ func parseOpts(args *fuseArgs, setup *setupState, log *log.Logger) (ok bool) { if C.fuse_opt_parse(args, unsafe.Pointer(&unsafeOpts), &[]C.struct_fuse_opt{ {templ: C.CString("source=%s"), offset: C.ulong(unsafe.Offsetof(unsafeOpts.source)), value: 0}, + {templ: C.CString("mkdir"), offset: C.ulong(unsafe.Offsetof(unsafeOpts.mkdir)), value: 1}, {templ: C.CString("setuid=%s"), offset: C.ulong(unsafe.Offsetof(unsafeOpts.setuid)), value: 0}, {templ: C.CString("setgid=%s"), offset: C.ulong(unsafe.Offsetof(unsafeOpts.setgid)), value: 0}, @@ -223,6 +230,7 @@ func parseOpts(args *fuseArgs, setup *setupState, log *log.Logger) (ok bool) { } else { setup.Source = a } + setup.mkdir = unsafeOpts.mkdir != 0 if unsafeOpts.setuid == nil { setup.Setuid = -1 @@ -339,6 +347,9 @@ func _main(s ...string) (exitCode int) { } else if setup.Fuse < 3 && (setup.Setuid > 0 || setup.Setgid > 0) { log.Println("setuid and setgid has no effect when not starting as root") return 1 + } else if setup.mkdir { + log.Println("mkdir has no effect when not starting as root") + return 1 } op := C.struct_fuse_operations{ @@ -387,6 +398,20 @@ func _main(s ...string) (exitCode int) { log.Println("setuid and setgid must not be 0") return 5 } + + if setup.mkdir { + if err := os.MkdirAll(setup.Source.String(), 0700); err != nil { + if !errors.Is(err, os.ErrExist) { + log.Println(err) + return 5 + } + // skip setup for existing source directory + } else if err = os.Chown(setup.Source.String(), setup.Setuid, setup.Setgid); err != nil { + log.Println(err) + return 5 + } + } + if err := syscall.Setresgid(setup.Setgid, setup.Setgid, setup.Setgid); err != nil { log.Printf("cannot set gid: %v", err) return 5 diff --git a/cmd/sharefs/test/test.py b/cmd/sharefs/test/test.py index 969bfa5..666feb5 100644 --- a/cmd/sharefs/test/test.py +++ b/cmd/sharefs/test/test.py @@ -27,6 +27,7 @@ check_bad_opts_output("setgid=-1", "sharefs: invalid value for option setgid\n") check_bad_opts_output("setuid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n") check_bad_opts_output("setgid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n") check_bad_opts_output("setuid=1023,setgid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n") +check_bad_opts_output("mkdir", "sharefs: mkdir has no effect when not starting as root\n") # Starting as root without setuid/setgid: check_bad_opts_output("allow_other", "sharefs: setuid and setgid must not be 0\n", privileged=True) @@ -37,6 +38,14 @@ check_bad_opts_output("setgid=1023", "sharefs: setuid and setgid must not be 0\n machine.fail("umount /mnt") machine.succeed("rmdir /mnt") +# Unprivileged mount/unmount: +machine.succeed("sudo -u alice -i mkdir /home/alice/{sdcard,persistent}") +machine.succeed("sudo -u alice -i /etc/sharefs -o source=/home/alice/persistent /home/alice/sdcard") +machine.succeed("sudo -u alice -i touch /home/alice/sdcard/check") +machine.succeed("sudo -u alice -i umount /home/alice/sdcard") +machine.succeed("sudo -u alice -i rm /home/alice/persistent/check") +machine.succeed("sudo -u alice -i rmdir /home/alice/{sdcard,persistent}") + # Benchmark sharefs: machine.succeed("fs_mark -v -d /sdcard/fs_mark -l /tmp/fs_log.txt") machine.copy_from_vm("/tmp/fs_log.txt", "") diff --git a/nixos.nix b/nixos.nix index cccc181..3548667 100644 --- a/nixos.nix +++ b/nixos.nix @@ -73,11 +73,7 @@ in NoNewPrivileges = true; }; script = '' - ${pkgs.coreutils}/bin/install \ - -dm0700 \ - -o ${cfg.sharefs.user} \ - -g ${cfg.sharefs.group} \ - ${cfg.sharefs.source} ${cfg.sharefs.name} + ${pkgs.coreutils}/bin/install -dm0 ${cfg.sharefs.name} exec ${cfg.package}/libexec/sharefs -f \ -o ${ @@ -91,6 +87,7 @@ in "setuid=$(id -u ${cfg.sharefs.user})" "setgid=$(id -g ${cfg.sharefs.group})" "source=${cfg.sharefs.source}" + "mkdir" ] } ${cfg.sharefs.name} '';