test: move nix files
Test / Create distribution (push) Successful in 56s
Test / Sandbox (push) Successful in 2m53s
Test / ShareFS (push) Successful in 3m51s
Test / Hakurei (push) Successful in 4m11s
Test / Sandbox (race detector) (push) Successful in 5m29s
Test / Hakurei (race detector) (push) Successful in 6m41s
Test / Flake checks (push) Successful in 1m11s
Test / Create distribution (push) Successful in 56s
Test / Sandbox (push) Successful in 2m53s
Test / ShareFS (push) Successful in 3m51s
Test / Hakurei (push) Successful in 4m11s
Test / Sandbox (race detector) (push) Successful in 5m29s
Test / Hakurei (race detector) (push) Successful in 6m41s
Test / Flake checks (push) Successful in 1m11s
These are too much clutter. Move them to test directory until the test suite replacement is upstreamed. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
+1
-1
@@ -54,7 +54,7 @@ testers.nixosTest {
|
||||
# Run with Go race detector:
|
||||
environment.hakurei = lib.mkIf withRace rec {
|
||||
# race detector does not support static linking
|
||||
package = (pkgs.callPackage ../package.nix { }).overrideAttrs (previousAttrs: {
|
||||
package = (pkgs.callPackage ./package.nix { }).overrideAttrs (previousAttrs: {
|
||||
env = previousAttrs.env // {
|
||||
GOFLAGS = previousAttrs.env.GOFLAGS + " -race";
|
||||
};
|
||||
|
||||
Generated
+49
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"nodes": {
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1780361225,
|
||||
"narHash": "sha256-wnV9ttf4fPWNonBIQmvlrSlNpQYgx5HgWWd007mwIFA=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "e28654b71096e08c019d4861ca26acb646f583d8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "release-26.05",
|
||||
"repo": "home-manager",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1780453794,
|
||||
"narHash": "sha256-bXMRa9VTsHSPXL4Cw8R6JJLQeY3Y/IP4+YJCYVmQ7FY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6b316287bae2ee04c9b93c8c858d930fd07d7338",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-26.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"home-manager": "home-manager",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
{
|
||||
description = "hakurei container tool and nixos module";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
|
||||
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager/release-26.05";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
home-manager,
|
||||
}:
|
||||
let
|
||||
supportedSystems = [
|
||||
"aarch64-linux"
|
||||
"i686-linux"
|
||||
"x86_64-linux"
|
||||
];
|
||||
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
||||
in
|
||||
{
|
||||
nixosModules.hakurei = import ./nixos.nix self.packages;
|
||||
|
||||
checks = forAllSystems (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgsFor.${system};
|
||||
|
||||
inherit (pkgs)
|
||||
runCommandLocal
|
||||
callPackage
|
||||
nixfmt
|
||||
deadnix
|
||||
statix
|
||||
;
|
||||
in
|
||||
{
|
||||
hakurei = callPackage ./. { inherit system self; };
|
||||
race = callPackage ./. {
|
||||
inherit system self;
|
||||
withRace = true;
|
||||
};
|
||||
|
||||
sandbox = callPackage ./sandbox { inherit self; };
|
||||
sandbox-race = callPackage ./sandbox {
|
||||
inherit self;
|
||||
withRace = true;
|
||||
};
|
||||
|
||||
sharefs = callPackage ./sharefs { inherit system self; };
|
||||
|
||||
formatting = runCommandLocal "check-formatting" { nativeBuildInputs = [ nixfmt ]; } ''
|
||||
cd ${./.}
|
||||
|
||||
echo "running nixfmt..."
|
||||
nixfmt --width=256 --check .
|
||||
|
||||
touch $out
|
||||
'';
|
||||
|
||||
lint =
|
||||
runCommandLocal "check-lint"
|
||||
{
|
||||
nativeBuildInputs = [
|
||||
deadnix
|
||||
statix
|
||||
];
|
||||
}
|
||||
''
|
||||
cd ${./.}
|
||||
|
||||
echo "running deadnix..."
|
||||
deadnix --fail
|
||||
|
||||
echo "running statix..."
|
||||
statix check .
|
||||
|
||||
touch $out
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
packages = forAllSystems (
|
||||
system:
|
||||
let
|
||||
inherit (self.packages.${system}) hakurei hsu;
|
||||
pkgs = nixpkgsFor.${system};
|
||||
in
|
||||
{
|
||||
default = hakurei;
|
||||
hakurei = pkgs.pkgsStatic.callPackage ./package.nix {
|
||||
inherit (pkgs)
|
||||
# passthru.buildInputs
|
||||
go_1_26
|
||||
clang
|
||||
|
||||
# nativeBuildInputs
|
||||
pkg-config
|
||||
wayland-scanner
|
||||
makeBinaryWrapper
|
||||
|
||||
# appPackages
|
||||
glibc
|
||||
xdg-dbus-proxy
|
||||
|
||||
# for check
|
||||
util-linux
|
||||
nettools
|
||||
;
|
||||
};
|
||||
hsu = pkgs.callPackage ./hsu.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
|
||||
];
|
||||
}
|
||||
''
|
||||
cd $(mktemp -d) \
|
||||
&& cp -r ${hakurei.src}/. . \
|
||||
&& chmod +w cmd && cp -r ${hsu.src}/. cmd/hsu/ \
|
||||
&& chmod -R +w .
|
||||
|
||||
CC="musl-clang -O3 -Werror -Qunused-arguments" \
|
||||
GOCACHE="$(mktemp -d)" \
|
||||
PATH="${pkgs.pkgsStatic.musl.bin}/bin:$PATH" \
|
||||
DESTDIR="$out" \
|
||||
./all.sh
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
devShells = forAllSystems (
|
||||
system:
|
||||
let
|
||||
inherit (self.packages.${system}) hakurei;
|
||||
pkgs = nixpkgsFor.${system};
|
||||
in
|
||||
{
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = hakurei.targetPkgs;
|
||||
hardeningDisable = [ "fortify" ];
|
||||
};
|
||||
withPackage = pkgs.mkShell { buildInputs = [ hakurei ] ++ hakurei.targetPkgs; };
|
||||
|
||||
vm =
|
||||
let
|
||||
nixos = nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
modules = [
|
||||
{
|
||||
environment = {
|
||||
systemPackages = [
|
||||
(pkgs.buildFHSEnv {
|
||||
pname = "hakurei-fhs";
|
||||
inherit (hakurei) version;
|
||||
targetPkgs = _: hakurei.targetPkgs;
|
||||
extraOutputsToInstall = [ "dev" ];
|
||||
profile = ''
|
||||
export PKG_CONFIG_PATH="/usr/share/pkgconfig:$PKG_CONFIG_PATH"
|
||||
'';
|
||||
})
|
||||
];
|
||||
|
||||
hakurei =
|
||||
let
|
||||
# this is used for interactive vm testing during development, where tests might be broken
|
||||
package = self.packages.${pkgs.stdenv.hostPlatform.system}.hakurei.override {
|
||||
buildGo126Module = previousArgs: pkgs.pkgsStatic.buildGo126Module (previousArgs // { doCheck = false; });
|
||||
};
|
||||
in
|
||||
{
|
||||
inherit package;
|
||||
hsuPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.hsu.override { hakurei = package; };
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
./interactive/configuration.nix
|
||||
./interactive/vm.nix
|
||||
./interactive/hakurei.nix
|
||||
./interactive/trace.nix
|
||||
./interactive/raceattr.nix
|
||||
|
||||
self.nixosModules.hakurei
|
||||
home-manager.nixosModules.home-manager
|
||||
];
|
||||
};
|
||||
in
|
||||
pkgs.mkShell {
|
||||
buildInputs = [ nixos.config.system.build.vm ];
|
||||
shellHook = "exec run-nixos-vm $@";
|
||||
};
|
||||
|
||||
generateDoc =
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
|
||||
doc =
|
||||
let
|
||||
eval = lib.evalModules {
|
||||
specialArgs = {
|
||||
inherit pkgs;
|
||||
};
|
||||
modules = [ (import ./options.nix self.packages) ];
|
||||
};
|
||||
cleanEval = lib.filterAttrsRecursive (n: _: n != "_module") eval;
|
||||
in
|
||||
pkgs.nixosOptionsDoc { inherit (cleanEval) options; };
|
||||
docText = pkgs.runCommand "hakurei-module-docs.md" { } ''
|
||||
cat ${doc.optionsCommonMark} > $out
|
||||
sed -i '/*Declared by:*/,+1 d' $out
|
||||
'';
|
||||
in
|
||||
pkgs.mkShell {
|
||||
shellHook = ''
|
||||
exec cat ${docText} > options.md
|
||||
'';
|
||||
};
|
||||
|
||||
generateSyscallTable =
|
||||
let
|
||||
GOARCH = {
|
||||
x86_64-linux = "amd64";
|
||||
aarch64-linux = "arm64";
|
||||
};
|
||||
in
|
||||
pkgs.mkShell {
|
||||
shellHook = "exec ${pkgs.writeShellScript "generate-syscall-table" ''
|
||||
set -e
|
||||
${pkgs.perl}/bin/perl \
|
||||
container/std/mksysnum_linux.pl \
|
||||
${pkgs.linuxHeaders}/include/asm/unistd_64.h | \
|
||||
${pkgs.go}/bin/gofmt > \
|
||||
container/std/syscall_linux_${GOARCH.${system}}.go
|
||||
''}";
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
lib,
|
||||
buildGoModule,
|
||||
hakurei ? abort "hakurei package required",
|
||||
}:
|
||||
|
||||
buildGoModule {
|
||||
pname = "${hakurei.pname}-hsu";
|
||||
inherit (hakurei) version;
|
||||
|
||||
src = ../cmd/hsu;
|
||||
inherit (hakurei) vendorHash;
|
||||
env.CGO_ENABLED = 0;
|
||||
|
||||
preBuild = ''
|
||||
go mod init hsu >& /dev/null
|
||||
'';
|
||||
|
||||
ldflags = lib.attrsets.foldlAttrs (
|
||||
ldflags: name: value:
|
||||
ldflags ++ [ "-X main.${name}=${value}" ]
|
||||
) [ "-s -w" ] { hakureiPath = "${hakurei}/libexec/hakurei"; };
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
src = builtins.path {
|
||||
name = "${pname}-src";
|
||||
path = lib.cleanSource ../../cmd/sharefs/test;
|
||||
path = lib.cleanSource ../sharefs;
|
||||
filter = path: type: (type == "directory") || (type == "regular" && lib.hasSuffix ".go" path);
|
||||
};
|
||||
vendorHash = null;
|
||||
|
||||
+407
@@ -0,0 +1,407 @@
|
||||
packages:
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
lists
|
||||
attrsets
|
||||
mkMerge
|
||||
mkIf
|
||||
mapAttrs
|
||||
foldlAttrs
|
||||
optional
|
||||
optionals
|
||||
;
|
||||
|
||||
cfg = config.environment.hakurei;
|
||||
|
||||
# userid*userOffset + appStart + appid
|
||||
getsubuid = userid: appid: userid * 100000 + 10000 + appid;
|
||||
getsubname = userid: appid: "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
|
||||
|
||||
{
|
||||
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 {
|
||||
assertions = [
|
||||
(
|
||||
let
|
||||
conflictingApps = foldlAttrs (
|
||||
acc: id: app:
|
||||
(
|
||||
acc
|
||||
++ foldlAttrs (
|
||||
acc': id': app':
|
||||
if id == id' || app.shareUid && app'.shareUid || app.identity != app'.identity then acc' else acc' ++ [ id ]
|
||||
) [ ] cfg.apps
|
||||
)
|
||||
) [ ] cfg.apps;
|
||||
in
|
||||
{
|
||||
assertion = (lists.length conflictingApps) == 0;
|
||||
message = "the following hakurei apps have conflicting identities: " + (builtins.concatStringsSep ", " conflictingApps);
|
||||
}
|
||||
)
|
||||
];
|
||||
|
||||
security.wrappers.hsu = {
|
||||
source = "${cfg.hsuPackage}/bin/hsu";
|
||||
setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
};
|
||||
|
||||
environment.etc.hsurc = {
|
||||
mode = "0400";
|
||||
text = foldlAttrs (
|
||||
acc: username: fid:
|
||||
"${toString config.users.users.${username}.uid} ${toString fid}\n" + acc
|
||||
) "" cfg.users;
|
||||
};
|
||||
|
||||
environment.systemPackages = optional (cfg.sharefs.source != null) cfg.sharefs.package;
|
||||
fileSystems = mountpoints;
|
||||
virtualisation.fileSystems = mountpoints;
|
||||
|
||||
home-manager =
|
||||
let
|
||||
privPackages = mapAttrs (_: userid: {
|
||||
home.packages = foldlAttrs (
|
||||
acc: id: app:
|
||||
[
|
||||
(
|
||||
let
|
||||
extendDBusDefault = id: ext: {
|
||||
filter = true;
|
||||
|
||||
talk = [ "org.freedesktop.Notifications" ] ++ ext.talk;
|
||||
own = [
|
||||
"${id}.*"
|
||||
"org.mpris.MediaPlayer2.${id}.*"
|
||||
]
|
||||
++ ext.own;
|
||||
|
||||
inherit (ext) call broadcast;
|
||||
};
|
||||
dbusConfig =
|
||||
let
|
||||
default = {
|
||||
talk = [ ];
|
||||
own = [ ];
|
||||
call = { };
|
||||
broadcast = { };
|
||||
};
|
||||
in
|
||||
{
|
||||
session_bus = if app.dbus.session != null then (app.dbus.session (extendDBusDefault id)) else (extendDBusDefault id default);
|
||||
system_bus = app.dbus.system;
|
||||
};
|
||||
command = if app.command == null then app.name else app.command;
|
||||
script = if app.script == null then ("exec " + command + " $@") else app.script;
|
||||
isGraphical = if app.gpu != null then app.gpu else app.enablements.wayland || app.enablements.x11;
|
||||
|
||||
conf = {
|
||||
inherit id;
|
||||
inherit (app) identity enablements;
|
||||
inherit (dbusConfig) session_bus system_bus;
|
||||
direct_wayland = app.insecureWayland;
|
||||
sched_policy = app.schedPolicy;
|
||||
sched_priority = app.schedPriority;
|
||||
groups = app.groups ++ optional (cfg.sharefs.source != null) cfg.sharefs.group;
|
||||
|
||||
container = {
|
||||
inherit (app)
|
||||
wait_delay
|
||||
devel
|
||||
userns
|
||||
device
|
||||
tty
|
||||
multiarch
|
||||
env
|
||||
;
|
||||
map_real_uid = app.mapRealUid;
|
||||
host_net = app.hostNet;
|
||||
host_abstract = app.hostAbstract;
|
||||
share_runtime = app.shareRuntime;
|
||||
share_tmpdir = app.shareTmpdir;
|
||||
|
||||
filesystem =
|
||||
let
|
||||
bind = src: {
|
||||
type = "bind";
|
||||
inherit src;
|
||||
};
|
||||
optBind = src: {
|
||||
type = "bind";
|
||||
inherit src;
|
||||
optional = true;
|
||||
};
|
||||
optDevBind = src: {
|
||||
type = "bind";
|
||||
inherit src;
|
||||
dev = true;
|
||||
optional = true;
|
||||
};
|
||||
in
|
||||
[
|
||||
(bind "/bin")
|
||||
(bind "/usr/bin")
|
||||
(bind "/nix/store")
|
||||
(optBind "/sys/block")
|
||||
(optBind "/sys/bus")
|
||||
(optBind "/sys/class")
|
||||
(optBind "/sys/dev")
|
||||
(optBind "/sys/devices")
|
||||
]
|
||||
++ optionals app.nix [
|
||||
(bind "/nix/var")
|
||||
]
|
||||
++ optionals isGraphical [
|
||||
(optDevBind "/dev/dri")
|
||||
(optDevBind "/dev/nvidiactl")
|
||||
(optDevBind "/dev/nvidia-modeset")
|
||||
(optDevBind "/dev/nvidia-uvm")
|
||||
(optDevBind "/dev/nvidia-uvm-tools")
|
||||
(optDevBind "/dev/nvidia0")
|
||||
]
|
||||
++ optionals app.useCommonPaths cfg.commonPaths
|
||||
++ app.extraPaths
|
||||
++ [
|
||||
{
|
||||
type = "bind";
|
||||
dst = "/etc/";
|
||||
src = "/etc/";
|
||||
special = true;
|
||||
}
|
||||
{
|
||||
type = "link";
|
||||
dst = "/run/current-system";
|
||||
linkname = "/run/current-system";
|
||||
dereference = true;
|
||||
}
|
||||
]
|
||||
++ optionals (isGraphical && config.hardware.graphics.enable) (
|
||||
[
|
||||
{
|
||||
type = "link";
|
||||
dst = "/run/opengl-driver";
|
||||
linkname = config.systemd.tmpfiles.settings.graphics-driver."/run/opengl-driver"."L+".argument;
|
||||
}
|
||||
]
|
||||
++ optionals (app.multiarch && config.hardware.graphics.enable32Bit) [
|
||||
{
|
||||
type = "link";
|
||||
dst = "/run/opengl-driver-32";
|
||||
linkname = config.systemd.tmpfiles.settings.graphics-driver."/run/opengl-driver-32"."L+".argument;
|
||||
}
|
||||
]
|
||||
)
|
||||
++ [
|
||||
{
|
||||
type = "bind";
|
||||
src = getsubhome userid app.identity;
|
||||
write = true;
|
||||
ensure = true;
|
||||
}
|
||||
];
|
||||
|
||||
username = getsubname userid app.identity;
|
||||
inherit (cfg) shell;
|
||||
home = getsubhome userid app.identity;
|
||||
|
||||
path =
|
||||
if app.path == null then
|
||||
pkgs.writeScript "${app.name}-start" ''
|
||||
#!${pkgs.zsh}${pkgs.zsh.shellPath}
|
||||
${script}
|
||||
''
|
||||
else
|
||||
app.path;
|
||||
args = if app.args == null then [ "${app.name}-start" ] else app.args;
|
||||
};
|
||||
};
|
||||
|
||||
checkedConfig =
|
||||
name: value:
|
||||
let
|
||||
file = pkgs.writeText name (builtins.toJSON value);
|
||||
in
|
||||
pkgs.runCommand "checked-${name}" { nativeBuildInputs = [ cfg.package ]; } ''
|
||||
ln -vs ${file} "$out"
|
||||
hakurei show --no-store ${file}
|
||||
'';
|
||||
in
|
||||
pkgs.writeShellScriptBin app.name ''
|
||||
exec hakurei${if app.verbose then " -v" else ""}${if app.insecureWayland then " --insecure" else ""} run ${checkedConfig "hakurei-app-${app.name}.json" conf} $@
|
||||
''
|
||||
)
|
||||
]
|
||||
++ (
|
||||
let
|
||||
pkg = if app.share != null then app.share else pkgs.${app.name};
|
||||
copy = source: "[ -d '${source}' ] && cp -Lrv '${source}' $out/share || true";
|
||||
in
|
||||
optional (app.enablements.wayland || app.enablements.x11) (
|
||||
pkgs.runCommand "${app.name}-share" { } ''
|
||||
mkdir -p $out/share
|
||||
${copy "${pkg}/share/applications"}
|
||||
${copy "${pkg}/share/pixmaps"}
|
||||
${copy "${pkg}/share/icons"}
|
||||
${copy "${pkg}/share/man"}
|
||||
|
||||
if test -d "$out/share/applications"; then
|
||||
substituteInPlace $out/share/applications/* \
|
||||
--replace-warn '${pkg}/bin/' "" \
|
||||
--replace-warn '${pkg}/libexec/' ""
|
||||
fi
|
||||
''
|
||||
)
|
||||
)
|
||||
++ acc
|
||||
) [ cfg.package ] cfg.apps;
|
||||
}) cfg.users;
|
||||
in
|
||||
{
|
||||
useUserPackages = false; # prevent users.users entries from being added
|
||||
|
||||
users =
|
||||
mkMerge
|
||||
(foldlAttrs
|
||||
(
|
||||
acc: _: fid:
|
||||
foldlAttrs
|
||||
(
|
||||
acc: _: app:
|
||||
(
|
||||
let
|
||||
key = getsubname fid app.identity;
|
||||
in
|
||||
{
|
||||
usernames = acc.usernames // {
|
||||
${key} = true;
|
||||
};
|
||||
merge = acc.merge ++ [
|
||||
{
|
||||
${key} = mkMerge (
|
||||
[
|
||||
app.extraConfig
|
||||
{ home.packages = app.packages; }
|
||||
]
|
||||
++ lib.optional (!attrsets.hasAttrByPath [ key ] acc.usernames) cfg.extraHomeConfig
|
||||
);
|
||||
}
|
||||
];
|
||||
}
|
||||
)
|
||||
)
|
||||
{
|
||||
inherit (acc) usernames;
|
||||
merge = acc.merge ++ [ { ${getsubname fid 0} = cfg.extraHomeConfig; } ];
|
||||
}
|
||||
cfg.apps
|
||||
)
|
||||
{
|
||||
usernames = { };
|
||||
merge = [ privPackages ];
|
||||
}
|
||||
cfg.users
|
||||
).merge;
|
||||
};
|
||||
|
||||
users =
|
||||
let
|
||||
getuser = userid: appid: {
|
||||
isSystemUser = true;
|
||||
createHome = true;
|
||||
description = "Hakurei subordinate user ${toString appid} (u${toString userid})";
|
||||
group = getsubname userid appid;
|
||||
home = getsubhome userid appid;
|
||||
uid = getsubuid userid appid;
|
||||
};
|
||||
getgroup = userid: appid: { gid = getsubuid userid appid; };
|
||||
in
|
||||
{
|
||||
users = mkMerge (
|
||||
foldlAttrs
|
||||
(
|
||||
acc: username: fid:
|
||||
acc
|
||||
++
|
||||
foldlAttrs
|
||||
(
|
||||
acc': _: app:
|
||||
acc' ++ [ { ${getsubname fid app.identity} = getuser fid app.identity; } ]
|
||||
)
|
||||
[
|
||||
{
|
||||
${getsubname fid 0} = getuser fid 0;
|
||||
${username}.extraGroups = [ cfg.sharefs.group ];
|
||||
}
|
||||
]
|
||||
cfg.apps
|
||||
)
|
||||
(optional (cfg.sharefs.source != null) {
|
||||
${cfg.sharefs.user} = {
|
||||
uid = lib.mkDefault 1023;
|
||||
inherit (cfg.sharefs) group;
|
||||
isSystemUser = true;
|
||||
home = cfg.sharefs.source;
|
||||
};
|
||||
})
|
||||
cfg.users
|
||||
);
|
||||
|
||||
groups = mkMerge (
|
||||
foldlAttrs
|
||||
(
|
||||
acc: _: fid:
|
||||
acc
|
||||
++ foldlAttrs (
|
||||
acc': _: app:
|
||||
acc' ++ [ { ${getsubname fid app.identity} = getgroup fid app.identity; } ]
|
||||
) [ { ${getsubname fid 0} = getgroup fid 0; } ] cfg.apps
|
||||
)
|
||||
(optional (cfg.sharefs.source != null) {
|
||||
${cfg.sharefs.group} = {
|
||||
gid = lib.mkDefault 1023;
|
||||
};
|
||||
})
|
||||
cfg.users
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
+1175
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,364 @@
|
||||
packages:
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib) types mkOption mkEnableOption;
|
||||
|
||||
cfg = config.environment.hakurei;
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
environment.hakurei = {
|
||||
enable = mkEnableOption "hakurei";
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = packages.${pkgs.stdenv.hostPlatform.system}.hakurei;
|
||||
description = "The hakurei package to use.";
|
||||
};
|
||||
|
||||
hsuPackage = mkOption {
|
||||
type = types.package;
|
||||
default = packages.${pkgs.stdenv.hostPlatform.system}.hsu;
|
||||
description = "The hsu package to use.";
|
||||
};
|
||||
|
||||
users = mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (types) attrsOf ints;
|
||||
in
|
||||
attrsOf (ints.between 0 99);
|
||||
description = ''
|
||||
Users allowed to spawn hakurei apps and their corresponding hakurei identity.
|
||||
'';
|
||||
};
|
||||
|
||||
extraHomeConfig = mkOption {
|
||||
type = types.anything;
|
||||
description = ''
|
||||
Extra home-manager configuration to merge with all target users.
|
||||
'';
|
||||
};
|
||||
|
||||
sharefs = {
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.linkFarm "sharefs" {
|
||||
"bin/sharefs" = "${cfg.package}/libexec/sharefs";
|
||||
"bin/mount.fuse.sharefs" = "${cfg.package}/libexec/sharefs";
|
||||
};
|
||||
description = "The sharefs package to use.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "sharefs";
|
||||
description = ''
|
||||
Name of the user to run the sharefs daemon as.
|
||||
'';
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "sharefs";
|
||||
description = ''
|
||||
Name of the group to run the sharefs daemon as.
|
||||
'';
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = "/sdcard";
|
||||
description = ''
|
||||
Host path to mount sharefs on.
|
||||
'';
|
||||
};
|
||||
|
||||
source = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
Writable backing directory. Setting this to null disables sharefs.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
apps = mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (types)
|
||||
int
|
||||
ints
|
||||
str
|
||||
bool
|
||||
enum
|
||||
package
|
||||
anything
|
||||
submodule
|
||||
listOf
|
||||
attrsOf
|
||||
nullOr
|
||||
functionTo
|
||||
;
|
||||
in
|
||||
attrsOf (submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = str;
|
||||
description = ''
|
||||
Name of the app's launcher script.
|
||||
'';
|
||||
};
|
||||
|
||||
verbose = mkEnableOption "launchers with verbose output";
|
||||
|
||||
identity = mkOption {
|
||||
type = ints.between 1 9999;
|
||||
description = ''
|
||||
Application identity. Identity 0 is reserved for system services.
|
||||
'';
|
||||
};
|
||||
shareUid = mkEnableOption "sharing identity with another application";
|
||||
|
||||
packages = mkOption {
|
||||
type = listOf package;
|
||||
default = [ ];
|
||||
description = ''
|
||||
List of extra packages to install via home-manager.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = anything;
|
||||
default = { };
|
||||
description = ''
|
||||
Extra home-manager configuration.
|
||||
'';
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Custom executable path.
|
||||
Setting this to null will default to the start script.
|
||||
'';
|
||||
};
|
||||
|
||||
args = mkOption {
|
||||
type = nullOr (listOf str);
|
||||
default = null;
|
||||
description = ''
|
||||
Custom args.
|
||||
Setting this to null will default to script name.
|
||||
'';
|
||||
};
|
||||
|
||||
script = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Application launch script.
|
||||
'';
|
||||
};
|
||||
|
||||
command = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Command to run as the target user.
|
||||
Setting this to null will default command to launcher name.
|
||||
Has no effect when script is set.
|
||||
'';
|
||||
};
|
||||
|
||||
groups = mkOption {
|
||||
type = listOf str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
List of groups to inherit from the privileged user.
|
||||
'';
|
||||
};
|
||||
|
||||
shareRuntime = mkEnableOption "sharing of XDG_RUNTIME_DIR between containers under the same identity";
|
||||
shareTmpdir = mkEnableOption "sharing of TMPDIR between containers under the same identity";
|
||||
|
||||
dbus = {
|
||||
session = mkOption {
|
||||
type = nullOr (functionTo anything);
|
||||
default = null;
|
||||
description = ''
|
||||
D-Bus session bus custom configuration.
|
||||
Setting this to null will enable built-in defaults.
|
||||
'';
|
||||
};
|
||||
|
||||
system = mkOption {
|
||||
type = nullOr anything;
|
||||
default = null;
|
||||
description = ''
|
||||
D-Bus system bus custom configuration.
|
||||
Setting this to null will disable the system bus proxy.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
env = mkOption {
|
||||
type = nullOr (attrsOf str);
|
||||
default = null;
|
||||
description = ''
|
||||
Environment variables to set for the initial process in the sandbox.
|
||||
'';
|
||||
};
|
||||
|
||||
wait_delay = mkOption {
|
||||
type = nullOr int;
|
||||
default = null;
|
||||
description = ''
|
||||
Duration to wait for after interrupting a container's initial process in nanoseconds.
|
||||
A negative value causes the container to be terminated immediately on cancellation.
|
||||
Setting this to null defaults to five seconds.
|
||||
'';
|
||||
};
|
||||
|
||||
devel = mkEnableOption "debugging-related kernel interfaces";
|
||||
userns = mkEnableOption "user namespace creation";
|
||||
tty = mkEnableOption "access to the controlling terminal";
|
||||
multiarch = mkEnableOption "multiarch kernel-level support";
|
||||
|
||||
hostNet = mkEnableOption "share host net namespace" // {
|
||||
default = true;
|
||||
};
|
||||
hostAbstract = mkEnableOption "share abstract unix socket scope";
|
||||
|
||||
schedPolicy = mkOption {
|
||||
type = nullOr (enum [
|
||||
"fifo"
|
||||
"rr"
|
||||
"batch"
|
||||
"idle"
|
||||
"deadline"
|
||||
"ext"
|
||||
]);
|
||||
default = null;
|
||||
description = ''
|
||||
Scheduling policy to set for the container.
|
||||
The zero value retains the current scheduling policy.
|
||||
'';
|
||||
};
|
||||
schedPriority = mkOption {
|
||||
type = nullOr (ints.between 1 99);
|
||||
default = null;
|
||||
description = ''
|
||||
Scheduling priority to set for the container.
|
||||
'';
|
||||
};
|
||||
|
||||
nix = mkEnableOption "nix daemon access";
|
||||
mapRealUid = mkEnableOption "mapping to priv-user uid";
|
||||
device = mkEnableOption "access to all devices";
|
||||
insecureWayland = mkEnableOption "direct access to the Wayland socket";
|
||||
|
||||
gpu = mkOption {
|
||||
type = nullOr bool;
|
||||
default = null;
|
||||
description = ''
|
||||
Target process GPU and driver access.
|
||||
Setting this to null will enable GPU whenever X or Wayland is enabled.
|
||||
'';
|
||||
};
|
||||
|
||||
useCommonPaths = mkEnableOption "common extra paths" // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
extraPaths = mkOption {
|
||||
type = listOf (attrsOf anything);
|
||||
default = [ ];
|
||||
description = ''
|
||||
Extra paths to make available to the container.
|
||||
'';
|
||||
};
|
||||
|
||||
enablements = {
|
||||
wayland = mkOption {
|
||||
type = nullOr bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to share the Wayland server via security-context-v1.
|
||||
'';
|
||||
};
|
||||
|
||||
x11 = mkOption {
|
||||
type = nullOr bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to share the X11 socket and allow connection.
|
||||
'';
|
||||
};
|
||||
|
||||
dbus = mkOption {
|
||||
type = nullOr bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to proxy D-Bus.
|
||||
'';
|
||||
};
|
||||
|
||||
pipewire = mkOption {
|
||||
type = nullOr bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to share the PipeWire server via pipewire-pulse on a SecurityContext socket.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
share = mkOption {
|
||||
type = nullOr package;
|
||||
default = null;
|
||||
description = ''
|
||||
Package containing share files.
|
||||
Setting this to null will default package name to wrapper name.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
default = { };
|
||||
description = ''
|
||||
Declaratively configured hakurei apps.
|
||||
'';
|
||||
};
|
||||
|
||||
commonPaths = mkOption {
|
||||
type = types.listOf (types.attrsOf types.anything);
|
||||
default = [ ];
|
||||
description = ''
|
||||
Common extra paths to make available to the container.
|
||||
'';
|
||||
};
|
||||
|
||||
shell = mkOption {
|
||||
type = types.str;
|
||||
default = "/run/current-system/sw/bin/bash";
|
||||
description = ''
|
||||
Absolute path to preferred shell.
|
||||
'';
|
||||
};
|
||||
|
||||
stateDir = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The state directory where app home directories are stored.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
buildGo126Module,
|
||||
makeBinaryWrapper,
|
||||
xdg-dbus-proxy,
|
||||
pkg-config,
|
||||
libffi,
|
||||
libseccomp,
|
||||
acl,
|
||||
wayland,
|
||||
wayland-protocols,
|
||||
wayland-scanner,
|
||||
|
||||
libxcb,
|
||||
libxau,
|
||||
libxdmcp,
|
||||
|
||||
# for sharefs
|
||||
fuse3,
|
||||
|
||||
# for passthru.buildInputs
|
||||
go_1_26,
|
||||
clang,
|
||||
xorgproto,
|
||||
|
||||
# for check
|
||||
util-linux,
|
||||
nettools,
|
||||
|
||||
glibc, # for ldd
|
||||
withStatic ? stdenv.hostPlatform.isStatic,
|
||||
}:
|
||||
|
||||
buildGo126Module rec {
|
||||
pname = "hakurei";
|
||||
version = with lib.strings; removePrefix "v" (trim (builtins.readFile ../cmd/dist/VERSION));
|
||||
|
||||
srcFiltered = builtins.path {
|
||||
name = "${pname}-src";
|
||||
path = lib.cleanSource ../.;
|
||||
filter = path: type: !(type == "regular" && (lib.hasSuffix ".nix" path || lib.hasSuffix ".py" path)) && !(type == "directory" && lib.hasSuffix "/test" path) && !(type == "directory" && lib.hasSuffix "/cmd/hsu" path);
|
||||
};
|
||||
vendorHash = null;
|
||||
|
||||
src = stdenv.mkDerivation {
|
||||
name = "${pname}-src-full";
|
||||
inherit version;
|
||||
enableParallelBuilding = true;
|
||||
src = srcFiltered;
|
||||
|
||||
buildInputs = [
|
||||
wayland
|
||||
wayland-protocols
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
go_1_26
|
||||
pkg-config
|
||||
wayland-scanner
|
||||
];
|
||||
|
||||
buildPhase = "GOCACHE=$(mktemp -d) go generate ./...";
|
||||
installPhase = "cp -r . $out";
|
||||
};
|
||||
|
||||
ldflags =
|
||||
lib.attrsets.foldlAttrs
|
||||
(
|
||||
ldflags: name: value:
|
||||
ldflags ++ [ "-X hakurei.app/internal/info.${name}=${value}" ]
|
||||
)
|
||||
(
|
||||
[ "-s -w" ]
|
||||
++ lib.optionals withStatic [
|
||||
"-linkmode external"
|
||||
"-extldflags \"-static\""
|
||||
]
|
||||
)
|
||||
{
|
||||
buildVersion = "v${version}";
|
||||
hakureiPath = "${placeholder "out"}/libexec/hakurei";
|
||||
hsuPath = "/run/wrappers/bin/hsu";
|
||||
};
|
||||
|
||||
env = {
|
||||
# use clang instead of gcc
|
||||
CC = "clang -O3 -Werror";
|
||||
};
|
||||
|
||||
buildInputs = [
|
||||
libffi
|
||||
libseccomp
|
||||
fuse3
|
||||
acl
|
||||
wayland
|
||||
|
||||
libxcb
|
||||
libxau
|
||||
libxdmcp
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkg-config
|
||||
makeBinaryWrapper
|
||||
|
||||
# for container example
|
||||
nettools
|
||||
];
|
||||
|
||||
postInstall =
|
||||
let
|
||||
appPackages = [
|
||||
glibc
|
||||
xdg-dbus-proxy
|
||||
];
|
||||
in
|
||||
''
|
||||
install -D --target-directory=$out/share/zsh/site-functions cmd/dist/comp/*
|
||||
|
||||
mkdir "$out/libexec"
|
||||
mv "$out"/bin/* "$out/libexec/"
|
||||
|
||||
makeBinaryWrapper "$out/libexec/hakurei" "$out/bin/hakurei" \
|
||||
--inherit-argv0 --prefix PATH : ${lib.makeBinPath appPackages}
|
||||
'';
|
||||
|
||||
passthru = {
|
||||
go = go_1_26;
|
||||
|
||||
targetPkgs = [
|
||||
go_1_26
|
||||
clang
|
||||
xorgproto
|
||||
util-linux
|
||||
|
||||
# for go generate
|
||||
wayland-protocols
|
||||
wayland-scanner
|
||||
]
|
||||
++ buildInputs
|
||||
++ nativeBuildInputs;
|
||||
};
|
||||
}
|
||||
@@ -14,7 +14,7 @@ testers.nixosTest {
|
||||
# Run with Go race detector:
|
||||
environment.hakurei = lib.mkIf withRace rec {
|
||||
# race detector does not support static linking
|
||||
package = (pkgs.callPackage ../../package.nix { }).overrideAttrs (previousAttrs: {
|
||||
package = (pkgs.callPackage ../package.nix { }).overrideAttrs (previousAttrs: {
|
||||
env = previousAttrs.env // {
|
||||
GOFLAGS = previousAttrs.env.GOFLAGS + " -race";
|
||||
};
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
users.users = {
|
||||
alice = {
|
||||
isNormalUser = true;
|
||||
description = "Alice Foobar";
|
||||
password = "foobar";
|
||||
uid = 1000;
|
||||
};
|
||||
};
|
||||
|
||||
home-manager.users.alice.home.stateVersion = "24.11";
|
||||
|
||||
# Automatically login on tty1 as a normal user:
|
||||
services.getty.autologinUser = "alice";
|
||||
|
||||
environment = {
|
||||
# For benchmarking sharefs:
|
||||
systemPackages = [ pkgs.fsmark ];
|
||||
};
|
||||
|
||||
virtualisation = {
|
||||
# Hopefully reduces spurious test failures:
|
||||
memorySize = if pkgs.stdenv.hostPlatform.is32bit then 2046 else 8192;
|
||||
|
||||
diskSize = 6 * 1024;
|
||||
|
||||
qemu.options = [
|
||||
# Increase test performance:
|
||||
"-smp 16"
|
||||
];
|
||||
};
|
||||
|
||||
environment.hakurei = rec {
|
||||
enable = true;
|
||||
stateDir = "/var/lib/hakurei";
|
||||
sharefs.source = "${stateDir}/sdcard";
|
||||
users.alice = 0;
|
||||
|
||||
extraHomeConfig = {
|
||||
home.stateVersion = "23.05";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
testers,
|
||||
|
||||
system,
|
||||
self,
|
||||
}:
|
||||
testers.nixosTest {
|
||||
name = "sharefs";
|
||||
nodes.machine =
|
||||
{ options, pkgs, ... }:
|
||||
let
|
||||
fhs =
|
||||
let
|
||||
hakurei = options.environment.hakurei.package.default;
|
||||
in
|
||||
pkgs.buildFHSEnv {
|
||||
pname = "hakurei-fhs";
|
||||
inherit (hakurei) version;
|
||||
targetPkgs = _: hakurei.targetPkgs;
|
||||
extraOutputsToInstall = [ "dev" ];
|
||||
profile = ''
|
||||
export PKG_CONFIG_PATH="/usr/share/pkgconfig:$PKG_CONFIG_PATH"
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
environment.systemPackages = [
|
||||
# For go tests:
|
||||
(pkgs.writeShellScriptBin "sharefs-workload-hakurei-tests" ''
|
||||
cp -r "${self.packages.${system}.hakurei.src}" "/sdcard/hakurei" && cd "/sdcard/hakurei"
|
||||
${fhs}/bin/hakurei-fhs -c 'ROSA_SKIP_BINFMT=1 CC="clang -O3 -Werror" go test ./...'
|
||||
'')
|
||||
];
|
||||
|
||||
imports = [
|
||||
./configuration.nix
|
||||
|
||||
self.nixosModules.hakurei
|
||||
self.inputs.home-manager.nixosModules.home-manager
|
||||
];
|
||||
};
|
||||
|
||||
testScript = builtins.readFile ./test.py;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
//go:build raceattr
|
||||
|
||||
// The raceattr program reproduces vfs inode file attribute race.
|
||||
//
|
||||
// Even though libfuse high-level API presents the address of a struct stat
|
||||
// alongside struct fuse_context, file attributes are actually inherent to the
|
||||
// inode, instead of the specific call from userspace. The kernel implementation
|
||||
// in fs/fuse/xattr.c appears to make stale data in the inode (set by a previous
|
||||
// call) impossible or very unlikely to reach userspace via the stat family of
|
||||
// syscalls. However, when using default_permissions to have the VFS check
|
||||
// permissions, this race still happens, despite the resulting struct stat being
|
||||
// correct when overriding the check via capabilities otherwise.
|
||||
//
|
||||
// This program reproduces the failure, but because of its continuous nature, it
|
||||
// is provided independent of the vm integration test suite.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func newStatAs(
|
||||
ctx context.Context, cancel context.CancelFunc,
|
||||
n *atomic.Uint64, ok *atomic.Bool,
|
||||
uid uint32, pathname string,
|
||||
continuous bool,
|
||||
) func() {
|
||||
return func() {
|
||||
runtime.LockOSThread()
|
||||
defer cancel()
|
||||
|
||||
if _, _, errno := syscall.Syscall(
|
||||
syscall.SYS_SETUID, uintptr(uid),
|
||||
0, 0,
|
||||
); errno != 0 {
|
||||
cancel()
|
||||
log.Printf("cannot set uid to %d: %s", uid, errno)
|
||||
}
|
||||
|
||||
var stat syscall.Stat_t
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := syscall.Lstat(pathname, &stat); err != nil {
|
||||
// SHAREFS_PERM_DIR not world executable, or
|
||||
// SHAREFS_PERM_REG not world readable
|
||||
if !continuous {
|
||||
cancel()
|
||||
}
|
||||
ok.Store(true)
|
||||
log.Printf("uid %d: %v", uid, err)
|
||||
} else if stat.Uid != uid {
|
||||
// appears to be unreachable
|
||||
if !continuous {
|
||||
cancel()
|
||||
}
|
||||
ok.Store(true)
|
||||
log.Printf("got uid %d instead of %d", stat.Uid, uid)
|
||||
}
|
||||
n.Add(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("raceattr: ")
|
||||
|
||||
p := flag.String("target", "/sdcard/raceattr", "pathname of test file")
|
||||
u0 := flag.Int("uid0", 1<<10-1, "first uid")
|
||||
u1 := flag.Int("uid1", 1<<10-2, "second uid")
|
||||
count := flag.Int("count", 1, "threads per uid")
|
||||
continuous := flag.Bool("continuous", false, "keep running even after reproduce")
|
||||
flag.Parse()
|
||||
|
||||
if os.Geteuid() != 0 {
|
||||
log.Fatal("this program must run as root")
|
||||
}
|
||||
|
||||
ctx, cancel := signal.NotifyContext(
|
||||
context.Background(),
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGHUP,
|
||||
)
|
||||
|
||||
if err := os.WriteFile(*p, nil, 0); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
|
||||
n atomic.Uint64
|
||||
ok atomic.Bool
|
||||
)
|
||||
|
||||
if *count < 1 {
|
||||
*count = 1
|
||||
}
|
||||
for range *count {
|
||||
wg.Go(newStatAs(ctx, cancel, &n, &ok, uint32(*u0), *p, *continuous))
|
||||
if *u1 >= 0 {
|
||||
wg.Go(newStatAs(ctx, cancel, &n, &ok, uint32(*u1), *p, *continuous))
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
if !*continuous && ok.Load() {
|
||||
log.Printf("reproduced after %d calls", n.Load())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
start_all()
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
# To check sharefs version:
|
||||
print(machine.succeed("sharefs -V"))
|
||||
|
||||
# Make sure sharefs started:
|
||||
machine.wait_for_unit("sdcard.mount")
|
||||
|
||||
machine.succeed("mkdir /mnt")
|
||||
def check_bad_opts_output(opts, want, source="/etc", privileged=False):
|
||||
output = machine.fail(("" if privileged else "sudo -u alice -i ") + f"sharefs -f -o source={source},{opts} /mnt 2>&1")
|
||||
if output != want:
|
||||
raise Exception(f"unexpected output: {output}")
|
||||
|
||||
# Malformed setuid/setgid representation:
|
||||
check_bad_opts_output("setuid=ff", "sharefs: invalid value for option setuid\n")
|
||||
check_bad_opts_output("setgid=ff", "sharefs: invalid value for option setgid\n")
|
||||
|
||||
# Bounds check for setuid/setgid:
|
||||
check_bad_opts_output("setuid=0", "sharefs: invalid value for option setuid\n")
|
||||
check_bad_opts_output("setgid=0", "sharefs: invalid value for option setgid\n")
|
||||
check_bad_opts_output("setuid=-1", "sharefs: invalid value for option setuid\n")
|
||||
check_bad_opts_output("setgid=-1", "sharefs: invalid value for option setgid\n")
|
||||
|
||||
# Non-root setuid/setgid:
|
||||
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)
|
||||
check_bad_opts_output("setuid=1023", "sharefs: setuid and setgid must not be 0\n", privileged=True)
|
||||
check_bad_opts_output("setgid=1023", "sharefs: setuid and setgid must not be 0\n", privileged=True)
|
||||
|
||||
# Make sure nothing actually got mounted:
|
||||
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 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", "")
|
||||
|
||||
# Check permissions:
|
||||
machine.succeed("sudo -u sharefs touch /var/lib/hakurei/sdcard/fs_mark/.check")
|
||||
machine.succeed("sudo -u sharefs rm /var/lib/hakurei/sdcard/fs_mark/.check")
|
||||
machine.succeed("sudo -u alice rm -rf /sdcard/fs_mark")
|
||||
machine.fail("ls /var/lib/hakurei/sdcard/fs_mark")
|
||||
|
||||
# Run hakurei tests on sharefs:
|
||||
machine.succeed("sudo -u alice -i sharefs-workload-hakurei-tests")
|
||||
Reference in New Issue
Block a user