Compare commits
4 Commits
775a9f57c9
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
c9cd16fd2a
|
|||
|
e42ea32dbe
|
|||
|
e7982b4ee9
|
|||
|
ef1ebf12d9
|
@@ -53,6 +53,9 @@ type (
|
|||||||
// Whether sharefs_init failed.
|
// Whether sharefs_init failed.
|
||||||
initFailed bool
|
initFailed bool
|
||||||
|
|
||||||
|
// Whether to create source directory as root.
|
||||||
|
mkdir bool
|
||||||
|
|
||||||
// Open file descriptor to fuse.
|
// Open file descriptor to fuse.
|
||||||
Fuse int
|
Fuse int
|
||||||
|
|
||||||
@@ -157,6 +160,9 @@ func parseOpts(args *fuseArgs, setup *setupState, log *log.Logger) (ok bool) {
|
|||||||
// Pathname to writable source directory.
|
// Pathname to writable source directory.
|
||||||
source *C.char
|
source *C.char
|
||||||
|
|
||||||
|
// Whether to create source directory as root.
|
||||||
|
mkdir C.int
|
||||||
|
|
||||||
// Decimal string representation of uid to set when running as root.
|
// Decimal string representation of uid to set when running as root.
|
||||||
setuid *C.char
|
setuid *C.char
|
||||||
// Decimal string representation of gid to set when running as root.
|
// 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{
|
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("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("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},
|
{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 {
|
} else {
|
||||||
setup.Source = a
|
setup.Source = a
|
||||||
}
|
}
|
||||||
|
setup.mkdir = unsafeOpts.mkdir != 0
|
||||||
|
|
||||||
if unsafeOpts.setuid == nil {
|
if unsafeOpts.setuid == nil {
|
||||||
setup.Setuid = -1
|
setup.Setuid = -1
|
||||||
@@ -314,7 +322,7 @@ func _main(s ...string) (exitCode int) {
|
|||||||
// hack to keep fuse_parse_cmdline happy in the container
|
// hack to keep fuse_parse_cmdline happy in the container
|
||||||
mountpoint := C.GoString(opts.mountpoint)
|
mountpoint := C.GoString(opts.mountpoint)
|
||||||
pathnameArg := -1
|
pathnameArg := -1
|
||||||
for i, arg := range os.Args {
|
for i, arg := range s {
|
||||||
if arg == mountpoint {
|
if arg == mountpoint {
|
||||||
pathnameArg = i
|
pathnameArg = i
|
||||||
break
|
break
|
||||||
@@ -324,21 +332,43 @@ func _main(s ...string) (exitCode int) {
|
|||||||
log.Println("mountpoint must be absolute")
|
log.Println("mountpoint must be absolute")
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
os.Args[pathnameArg] = container.Nonexistent
|
s[pathnameArg] = container.Nonexistent
|
||||||
}
|
}
|
||||||
|
|
||||||
if !parseOpts(&args, &setup, msg.GetLogger()) {
|
if !parseOpts(&args, &setup, msg.GetLogger()) {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
asRoot := os.Geteuid() == 0
|
||||||
|
|
||||||
if os.Geteuid() == 0 {
|
if asRoot {
|
||||||
if setup.Setuid <= 0 || setup.Setgid <= 0 {
|
if setup.Setuid <= 0 || setup.Setgid <= 0 {
|
||||||
log.Println("setuid and setgid must not be 0")
|
log.Println("setuid and setgid must not be 0")
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if setup.Fuse >= 3 {
|
||||||
|
log.Println("filesystem daemon must not run as root")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if setup.mkdir {
|
||||||
|
if err := os.MkdirAll(setup.Source.String(), 0700); err != nil {
|
||||||
|
if !errors.Is(err, os.ErrExist) {
|
||||||
|
log.Println(err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
// skip setup for existing source directory
|
||||||
|
} else if err = os.Chown(setup.Source.String(), setup.Setuid, setup.Setgid); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if setup.Fuse < 3 && (setup.Setuid > 0 || setup.Setgid > 0) {
|
} else if setup.Fuse < 3 && (setup.Setuid > 0 || setup.Setgid > 0) {
|
||||||
log.Println("setuid and setgid has no effect when not starting as root")
|
log.Println("setuid and setgid has no effect when not starting as root")
|
||||||
return 1
|
return 1
|
||||||
|
} else if setup.mkdir {
|
||||||
|
log.Println("mkdir has no effect when not starting as root")
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
op := C.struct_fuse_operations{
|
op := C.struct_fuse_operations{
|
||||||
@@ -382,11 +412,7 @@ func _main(s ...string) (exitCode int) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if os.Geteuid() == 0 {
|
if asRoot {
|
||||||
if setup.Setuid <= 0 || setup.Setgid <= 0 {
|
|
||||||
log.Println("setuid and setgid must not be 0")
|
|
||||||
return 5
|
|
||||||
}
|
|
||||||
if err := syscall.Setresgid(setup.Setgid, setup.Setgid, setup.Setgid); err != nil {
|
if err := syscall.Setresgid(setup.Setgid, setup.Setgid, setup.Setgid); err != nil {
|
||||||
log.Printf("cannot set gid: %v", err)
|
log.Printf("cannot set gid: %v", err)
|
||||||
return 5
|
return 5
|
||||||
@@ -421,7 +447,7 @@ func _main(s ...string) (exitCode int) {
|
|||||||
} else {
|
} else {
|
||||||
z.Path = a
|
z.Path = a
|
||||||
}
|
}
|
||||||
z.Args = os.Args
|
z.Args = s
|
||||||
z.ForwardCancel = true
|
z.ForwardCancel = true
|
||||||
z.SeccompPresets |= std.PresetStrict
|
z.SeccompPresets |= std.PresetStrict
|
||||||
z.ParentPerm = 0700
|
z.ParentPerm = 0700
|
||||||
|
|||||||
@@ -3,14 +3,29 @@ package main
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// sharefsName is the prefix used by log.std in the sharefs process.
|
// sharefsName is the prefix used by log.std in the sharefs process.
|
||||||
const sharefsName = "sharefs"
|
const sharefsName = "sharefs"
|
||||||
|
|
||||||
|
// handleMountArgs returns an alternative, libfuse-compatible args slice for
|
||||||
|
// args passed by mount -t fuse.sharefs [options] sharefs <mountpoint>.
|
||||||
|
//
|
||||||
|
// In this case, args always has a length of 5 with index 0 being what comes
|
||||||
|
// after "fuse." in the filesystem type, 1 is the uninterpreted string passed
|
||||||
|
// to mount (sharefsName is used as the magic string to enable this hack),
|
||||||
|
// 2 is passed through to libfuse as mountpoint, and 3 is always "-o".
|
||||||
|
func handleMountArgs(args []string) []string {
|
||||||
|
if len(args) == 5 && args[1] == sharefsName && args[3] == "-o" {
|
||||||
|
return []string{sharefsName, args[2], "-o", args[4]}
|
||||||
|
}
|
||||||
|
return slices.Clone(args)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
log.SetPrefix(sharefsName + ": ")
|
log.SetPrefix(sharefsName + ": ")
|
||||||
|
|
||||||
os.Exit(_main(os.Args...))
|
os.Exit(_main(handleMountArgs(os.Args)...))
|
||||||
}
|
}
|
||||||
|
|||||||
29
cmd/sharefs/main_test.go
Normal file
29
cmd/sharefs/main_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMountArgs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{"nil", nil, nil},
|
||||||
|
{"passthrough", []string{"sharefs", "-V"}, []string{"sharefs", "-V"}},
|
||||||
|
{"replace", []string{"/sbin/sharefs", "sharefs", "/sdcard", "-o", "rw"}, []string{"sharefs", "/sdcard", "-o", "rw"}},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if got := handleMountArgs(tc.args); !slices.Equal(got, tc.want) {
|
||||||
|
t.Errorf("handleMountArgs: %q, want %q", got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{ pkgs, config, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
users.users = {
|
users.users = {
|
||||||
alice = {
|
alice = {
|
||||||
@@ -17,9 +17,6 @@
|
|||||||
environment = {
|
environment = {
|
||||||
# For benchmarking sharefs:
|
# For benchmarking sharefs:
|
||||||
systemPackages = [ pkgs.fsmark ];
|
systemPackages = [ pkgs.fsmark ];
|
||||||
|
|
||||||
# For sharefs option checks without adding to PATH:
|
|
||||||
etc.sharefs.source = "${config.environment.hakurei.package}/libexec/sharefs";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
virtualisation = {
|
virtualisation = {
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ start_all()
|
|||||||
machine.wait_for_unit("multi-user.target")
|
machine.wait_for_unit("multi-user.target")
|
||||||
|
|
||||||
# To check sharefs version:
|
# To check sharefs version:
|
||||||
print(machine.succeed("/etc/sharefs -V"))
|
print(machine.succeed("sharefs -V"))
|
||||||
|
|
||||||
# Make sure sharefs did not terminate:
|
# Make sure sharefs started:
|
||||||
machine.wait_for_unit("sharefs.service")
|
machine.wait_for_unit("sdcard.mount")
|
||||||
|
|
||||||
machine.succeed("mkdir /mnt")
|
machine.succeed("mkdir /mnt")
|
||||||
def check_bad_opts_output(opts, want, source="/etc", privileged=False):
|
def check_bad_opts_output(opts, want, source="/etc", privileged=False):
|
||||||
output = machine.fail(("" if privileged else "sudo -u alice -i ") + f"/etc/sharefs -f -o source={source},{opts} /mnt 2>&1")
|
output = machine.fail(("" if privileged else "sudo -u alice -i ") + f"sharefs -f -o source={source},{opts} /mnt 2>&1")
|
||||||
if output != want:
|
if output != want:
|
||||||
raise Exception(f"unexpected output: {output}")
|
raise Exception(f"unexpected output: {output}")
|
||||||
|
|
||||||
@@ -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("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("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("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:
|
# Starting as root without setuid/setgid:
|
||||||
check_bad_opts_output("allow_other", "sharefs: setuid and setgid must not be 0\n", privileged=True)
|
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.fail("umount /mnt")
|
||||||
machine.succeed("rmdir /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 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:
|
# Benchmark sharefs:
|
||||||
machine.succeed("fs_mark -v -d /sdcard/fs_mark -l /tmp/fs_log.txt")
|
machine.succeed("fs_mark -v -d /sdcard/fs_mark -l /tmp/fs_log.txt")
|
||||||
machine.copy_from_vm("/tmp/fs_log.txt", "")
|
machine.copy_from_vm("/tmp/fs_log.txt", "")
|
||||||
|
|||||||
@@ -138,6 +138,10 @@
|
|||||||
;
|
;
|
||||||
};
|
};
|
||||||
hsu = pkgs.callPackage ./cmd/hsu/package.nix { inherit (self.packages.${system}) hakurei; };
|
hsu = pkgs.callPackage ./cmd/hsu/package.nix { inherit (self.packages.${system}) hakurei; };
|
||||||
|
sharefs = pkgs.linkFarm "sharefs" {
|
||||||
|
"bin/sharefs" = "${hakurei}/libexec/sharefs";
|
||||||
|
"bin/mount.fuse.sharefs" = "${hakurei}/libexec/sharefs";
|
||||||
|
};
|
||||||
|
|
||||||
dist = pkgs.runCommand "${hakurei.name}-dist" { buildInputs = hakurei.targetPkgs ++ [ pkgs.pkgsStatic.musl ]; } ''
|
dist = pkgs.runCommand "${hakurei.name}-dist" { buildInputs = hakurei.targetPkgs ++ [ pkgs.pkgsStatic.musl ]; } ''
|
||||||
# go requires XDG_CACHE_HOME for the build cache
|
# go requires XDG_CACHE_HOME for the build cache
|
||||||
|
|||||||
65
nixos.nix
65
nixos.nix
@@ -24,11 +24,38 @@ let
|
|||||||
getsubuid = userid: appid: userid * 100000 + 10000 + appid;
|
getsubuid = userid: appid: userid * 100000 + 10000 + appid;
|
||||||
getsubname = userid: appid: "u${toString userid}_a${toString appid}";
|
getsubname = userid: appid: "u${toString userid}_a${toString appid}";
|
||||||
getsubhome = userid: appid: "${cfg.stateDir}/u${toString userid}/a${toString appid}";
|
getsubhome = userid: appid: "${cfg.stateDir}/u${toString userid}/a${toString appid}";
|
||||||
|
|
||||||
|
mountpoints = {
|
||||||
|
${cfg.sharefs.name} = mkIf (cfg.sharefs.source != null) {
|
||||||
|
depends = [ cfg.sharefs.source ];
|
||||||
|
device = "sharefs";
|
||||||
|
fsType = "fuse.sharefs";
|
||||||
|
noCheck = true;
|
||||||
|
options = [
|
||||||
|
"rw"
|
||||||
|
"noexec"
|
||||||
|
"nosuid"
|
||||||
|
"nodev"
|
||||||
|
"noatime"
|
||||||
|
"allow_other"
|
||||||
|
"mkdir"
|
||||||
|
"source=${cfg.sharefs.source}"
|
||||||
|
"setuid=${toString config.users.users.${cfg.sharefs.user}.uid}"
|
||||||
|
"setgid=${toString config.users.groups.${cfg.sharefs.group}.gid}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [ (import ./options.nix packages) ];
|
imports = [ (import ./options.nix packages) ];
|
||||||
|
|
||||||
|
options = {
|
||||||
|
# Forward declare a dummy option for VM filesystems since the real one won't exist
|
||||||
|
# unless the VM module is actually imported.
|
||||||
|
virtualisation.fileSystems = lib.mkOption { };
|
||||||
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
assertions = [
|
assertions = [
|
||||||
(
|
(
|
||||||
@@ -66,41 +93,9 @@ in
|
|||||||
) "" cfg.users;
|
) "" cfg.users;
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services = {
|
environment.systemPackages = optional (cfg.sharefs.source != null) cfg.sharefs.package;
|
||||||
sharefs = mkIf (cfg.sharefs.source != null) {
|
fileSystems = mountpoints;
|
||||||
unitConfig.RequiresMountsFor = cfg.sharefs.source;
|
virtualisation.fileSystems = mountpoints;
|
||||||
serviceConfig = {
|
|
||||||
NoNewPrivileges = true;
|
|
||||||
};
|
|
||||||
script = ''
|
|
||||||
${pkgs.coreutils}/bin/install \
|
|
||||||
-dm0700 \
|
|
||||||
-o ${cfg.sharefs.user} \
|
|
||||||
-g ${cfg.sharefs.group} \
|
|
||||||
${cfg.sharefs.source} ${cfg.sharefs.name}
|
|
||||||
|
|
||||||
exec ${cfg.package}/libexec/sharefs -f \
|
|
||||||
-o ${
|
|
||||||
lib.join "," [
|
|
||||||
"noexec"
|
|
||||||
"nosuid"
|
|
||||||
"nodev"
|
|
||||||
"noatime"
|
|
||||||
"auto_unmount"
|
|
||||||
"allow_other"
|
|
||||||
"setuid=$(id -u ${cfg.sharefs.user})"
|
|
||||||
"setgid=$(id -g ${cfg.sharefs.group})"
|
|
||||||
"source=${cfg.sharefs.source}"
|
|
||||||
]
|
|
||||||
} ${cfg.sharefs.name}
|
|
||||||
'';
|
|
||||||
|
|
||||||
# do not unmount on configuration changes
|
|
||||||
restartIfChanged = false;
|
|
||||||
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
home-manager =
|
home-manager =
|
||||||
let
|
let
|
||||||
|
|||||||
90
options.md
90
options.md
@@ -809,6 +809,96 @@ package
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## environment\.hakurei\.sharefs\.package
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The sharefs package to use\.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*Type:*
|
||||||
|
package
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*Default:*
|
||||||
|
` <derivation sharefs> `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## environment\.hakurei\.sharefs\.group
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Name of the group to run the sharefs daemon as\.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*Type:*
|
||||||
|
string
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*Default:*
|
||||||
|
` "sharefs" `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## environment\.hakurei\.sharefs\.name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Host path to mount sharefs on\.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*Type:*
|
||||||
|
string
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*Default:*
|
||||||
|
` "/sdcard" `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## environment\.hakurei\.sharefs\.source
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Writable backing directory\. Setting this to null disables sharefs\.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*Type:*
|
||||||
|
null or string
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*Default:*
|
||||||
|
` null `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## environment\.hakurei\.sharefs\.user
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Name of the user to run the sharefs daemon as\.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*Type:*
|
||||||
|
string
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*Default:*
|
||||||
|
` "sharefs" `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## environment\.hakurei\.shell
|
## environment\.hakurei\.shell
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
sharefs = {
|
sharefs = {
|
||||||
|
package = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
default = packages.${pkgs.stdenv.hostPlatform.system}.sharefs;
|
||||||
|
description = "The sharefs package to use.";
|
||||||
|
};
|
||||||
|
|
||||||
user = mkOption {
|
user = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "sharefs";
|
default = "sharefs";
|
||||||
|
|||||||
Reference in New Issue
Block a user