From aa41002e8078a3ab00896cbd403e573c6dd817c2 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sat, 3 Oct 2026 17:49:27 +0900 Subject: test/internal/mountinfo: move from sandbox This also improves its API. Signed-off-by: Ophestra --- .gitea/workflows/test.yml | 2 +- internal/rosa/package/hakurei.az | 3 + internal/workflows/test.go | 2 +- test/internal/mountinfo/mountinfo.go | 177 +++++++++++++++++++++++++++++ test/internal/mountinfo/mountinfo_guard.go | 15 +++ test/internal/mountinfo/mountinfo_test.go | 146 ++++++++++++++++++++++++ test/internal/sandbox/assert.go | 25 ++-- test/internal/sandbox/mount.go | 159 -------------------------- test/internal/sandbox/mount_test.go | 144 ----------------------- test/sandbox/tool/package.nix | 2 +- 10 files changed, 359 insertions(+), 316 deletions(-) create mode 100644 test/internal/mountinfo/mountinfo.go create mode 100644 test/internal/mountinfo/mountinfo_guard.go create mode 100644 test/internal/mountinfo/mountinfo_test.go delete mode 100644 test/internal/sandbox/mount.go delete mode 100644 test/internal/sandbox/mount_test.go diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 3cb08c5b..f1547782 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -1 +1 @@ -{"name":"Test","on":["push"],"jobs":{"hakurei":{"name":"Hakurei","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.hakurei"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-vm-output","path":"result/*","retention-days":1}}]},"race":{"name":"Hakurei (race detector)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.race"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-race-vm-output","path":"result/*","retention-days":1}}]},"sandbox":{"name":"Sandbox","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"sandbox-vm-output","path":"result/*","retention-days":1}}]},"sandbox-race":{"name":"Sandbox (race detector)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox-race"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"sandbox-race-vm-output","path":"result/*","retention-days":1}}]},"sharefs":{"name":"ShareFS","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Set up Go toolchain","uses":"actions/setup-go@v6","with":{"go-version-file":"go.mod"}},{"name":"Install packages","uses":"awalsh128/cache-apt-pkgs-action@v1","with":{"packages":"fuse3 fsmark","version":0,"execute_install_scripts":true}},{"name":"Request distribution","id":"dist","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Install hakurei","run":"HAKUREI_VERSION=\"$(cat cmd/dist/VERSION)-${{ steps.dist.outputs.rev }}\" \u0026\u0026 tar xf result/hakurei-$HAKUREI_VERSION*-amd64.tar.gz \u0026\u0026 ./hakurei-$HAKUREI_VERSION*-amd64/install.sh \u0026\u0026 sudo -u ubuntu hakurei version \u0026\u0026 echo 'Defaults closefrom_override' \u003e /etc/sudoers.d/closefrom_override \u0026\u0026 mkdir /var/empty"},{"name":"Mount sharefs","run":"useradd -ru 1023 -md /var/lib/sdcard -k /var/empty -s /sbin/nologin media_rw \u0026\u0026 install -dm0 /sdcard \u0026\u0026 sharefs -o rw,noexec,nosuid,nodev,noatime,allow_other,mkdir,source=/var/lib/sdcard,setuid=1023,setgid=1023 /sdcard"},{"name":"Compile and run test suite","run":"sharefs -V \u0026\u0026 rm -rf result \u0026\u0026 go run -tags=testsuite ./test/sharefs"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"fs_mark","path":"result/*","retention-days":1}}]},"check":{"name":"Flake checks","needs":["hakurei","race","sandbox","sandbox-race"],"runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run checks","run":"nix --print-build-logs --experimental-features 'nix-command flakes' flake check ./test"}]},"dist":{"name":"Create distribution","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Request distribution","id":"dist-test","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Upload distribution","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-${{ steps.dist-test.outputs.rev }}","path":"result/*","retention-days":1}}]}}} +{"name":"Test","on":["push"],"jobs":{"hakurei":{"name":"Hakurei","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.hakurei"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-vm-output","path":"result/*","retention-days":1}}]},"race":{"name":"Hakurei (race detector)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.race"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-race-vm-output","path":"result/*","retention-days":1}}]},"sandbox":{"name":"Sandbox","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"sandbox-vm-output","path":"result/*","retention-days":1}}]},"sandbox-race":{"name":"Sandbox (race detector)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox-race"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"sandbox-race-vm-output","path":"result/*","retention-days":1}}]},"sharefs":{"name":"ShareFS","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Set up Go toolchain","uses":"actions/setup-go@v6","with":{"go-version-file":"go.mod"}},{"name":"Install packages","uses":"awalsh128/cache-apt-pkgs-action@v1","with":{"packages":"fuse3 fsmark","version":0,"execute_install_scripts":true}},{"name":"Request distribution","id":"dist","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Install hakurei","run":"HAKUREI_VERSION=\"$(cat cmd/dist/VERSION)-${{ steps.dist.outputs.rev }}\" \u0026\u0026 tar xf result/hakurei-$HAKUREI_VERSION*-amd64.tar.gz \u0026\u0026 ./hakurei-$HAKUREI_VERSION*-amd64/install.sh \u0026\u0026 sudo -u ubuntu hakurei version \u0026\u0026 echo 'Defaults closefrom_override' \u003e /etc/sudoers.d/closefrom_override \u0026\u0026 mkdir /var/empty"},{"name":"Mount sharefs","run":"useradd -ru 1023 -md /var/lib/sdcard -k /var/empty -s /sbin/nologin media_rw \u0026\u0026 install -dm0 /sdcard \u0026\u0026 sharefs -o rw,noexec,nosuid,nodev,noatime,allow_other,mkdir,source=/var/lib/sdcard,setuid=1023,setgid=1023 /sdcard"},{"name":"Compile and run test suite","run":"sharefs -V \u0026\u0026 rm -rf result \u0026\u0026 go run -tags=testsuite ./test/sharefs"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"fs_mark","path":"result/*","retention-days":1}}]},"check":{"name":"Flake checks","needs":["hakurei","race","sandbox","sandbox-race"],"runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run checks","run":"nix --print-build-logs --experimental-features 'nix-command flakes' flake check ./test"}]},"dist":{"name":"Create distribution","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Request distribution","id":"dist-test","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Upload distribution","uses":"actions/upload-artifact@v3","with":{"name":"dist-${{ steps.dist-test.outputs.rev }}","path":"result/*","retention-days":1}}]}}} diff --git a/internal/rosa/package/hakurei.az b/internal/rosa/package/hakurei.az index 596eb956..5585002c 100644 --- a/internal/rosa/package/hakurei.az +++ b/internal/rosa/package/hakurei.az @@ -75,6 +75,7 @@ mkdir -p /work/system/bin/ xcb, wayland, wayland-protocols, + util-linux, kernel-headers, ]; @@ -121,6 +122,7 @@ DESTDIR=/work "./${NAME}.sh" xcb, wayland, wayland-protocols, + util-linux, kernel-headers, ]; @@ -168,6 +170,7 @@ DESTDIR=/work "./${NAME}.sh" xcb, wayland, wayland-protocols, + util-linux, kernel-headers, ]; diff --git a/internal/workflows/test.go b/internal/workflows/test.go index 40e1a076..723cf463 100644 --- a/internal/workflows/test.go +++ b/internal/workflows/test.go @@ -117,7 +117,7 @@ var _ = (&Workflow{ newCIRequest("distribution", "dist -o result", "dist-test"), newUploadArtifact( "distribution", - "hakurei-${{ steps.dist-test.outputs.rev }}", + "dist-${{ steps.dist-test.outputs.rev }}", ), }, }}, diff --git a/test/internal/mountinfo/mountinfo.go b/test/internal/mountinfo/mountinfo.go new file mode 100644 index 00000000..19892cd2 --- /dev/null +++ b/test/internal/mountinfo/mountinfo.go @@ -0,0 +1,177 @@ +// Package mountinfo provides util-linux bindings for parsing +// proc_pid_mountinfo(5). +// +// This package must never be used outside integration tests, a much better +// implementation can be found in package vfs. +// +// Attempting to import this package outside testing causes the resulting +// program to panic. +package mountinfo + +/* +#cgo linux pkg-config: --static mount + +#include +#include +#include + +const char *HAKUREI_MOUNTINFO_PATH = "/proc/self/mountinfo"; +*/ +import "C" + +import ( + "errors" + "fmt" + "runtime" + "unsafe" +) + +var ( + // ErrParse is returned by [Open] when encountering a bad record. + ErrParse = errors.New("invalid mountinfo record") + // ErrIter is returned by [Open] if an iterator cannot be allocated. + ErrIter = errors.New("cannot allocate iterator") + // ErrIterAdvance is stored when the iterator is unable to advance. + ErrIterAdvance = errors.New("unable to advance iterator") +) + +type ( + // Iter refers to libmnt iterator state. + Iter struct { + // Last stored error. + err error + // Whether iteration has concluded. + ok bool + // Whether Close had already been called. + closed bool + + tb *C.struct_libmnt_table + itr *C.struct_libmnt_iter + + fs *C.struct_libmnt_fs + } + + // Entry represents deterministic mountinfo parts of a libmnt_fs entry. + Entry struct { + // mount ID: a unique ID for the mount (may be reused after umount(2)). + ID int `json:"id"` + // parent ID: the ID of the parent mount (or of self for the root of + // this mount namespace's mount tree). + Parent int `json:"parent"` + // root: the pathname of the directory in the filesystem which forms the + // root of this mount. + Root string `json:"root"` + // mount point: the pathname of the mount point relative to the + // process's root directory. + Target string `json:"target"` + // mount options: per-mount options (see mount(2)). + VfsOptstr string `json:"vfs_optstr"` + // filesystem type: the filesystem type in the form "type[.subtype]". + FsType string `json:"fstype"` + // mount source: filesystem-specific information or "none". + Source string `json:"source"` + // super options: per-superblock options (see mount(2)). + FsOptstr string `json:"fs_optstr"` + } +) + +// Copy populates v with the current record. +func (m *Iter) Copy(v *Entry) { + if m.fs == nil { + panic("invalid entry") + } + v.ID = int(C.mnt_fs_get_id(m.fs)) + v.Parent = int(C.mnt_fs_get_parent_id(m.fs)) + v.Root = C.GoString(C.mnt_fs_get_root(m.fs)) + v.Target = C.GoString(C.mnt_fs_get_target(m.fs)) + v.VfsOptstr = C.GoString(C.mnt_fs_get_vfs_options(m.fs)) + v.FsType = C.GoString(C.mnt_fs_get_fstype(m.fs)) + v.Source = C.GoString(C.mnt_fs_get_source(m.fs)) + v.FsOptstr = C.GoString(C.mnt_fs_get_fs_options(m.fs)) +} + +// Err returns the saved iterator error. +func (m *Iter) Err() error { return m.err } + +// Open opens a mountinfo document. If name is an empty string, the mountinfo +// document of the current process is opened instead. +func Open(name string) (*Iter, error) { + var m Iter + if name == "" { + m.tb = C.mnt_new_table_from_file(C.HAKUREI_MOUNTINFO_PATH) + } else { + _name := C.CString(name) + m.tb = C.mnt_new_table_from_file(_name) + C.free(unsafe.Pointer(_name)) + } + if m.tb == nil { + return nil, ErrParse + } + m.itr = C.mnt_new_iter(C.MNT_ITER_FORWARD) + if m.itr == nil { + C.mnt_unref_table(m.tb) + return nil, ErrIter + } + m.ok = true + + runtime.SetFinalizer(&m, (*Iter).Close) + return &m, nil +} + +// Close frees the iterator. +func (m *Iter) Close() { + if m.closed { + return + } + if m.tb == nil { + panic("unref called before open") + } + + C.mnt_unref_table(m.tb) + C.mnt_free_iter(m.itr) + m.closed = true + runtime.SetFinalizer(m, nil) +} + +// Reset resets the iterator to the first record for reuse. +func (m *Iter) Reset() { + if m.err != nil { + panic("attempting to reset a faulted iterator") + } + m.ok = true + C.mnt_reset_iter(m.itr, -1) +} + +// Next advances the iterator to the next record. The record may be copied if +// Next returns true. +func (m *Iter) Next() bool { + if !m.ok || m.err != nil { + return false + } + + r := C.mnt_table_next_fs(m.tb, m.itr, &m.fs) + if r < 0 { + m.err = ErrIterAdvance + } + m.ok = r == 0 + return m.ok +} + +// EqualWithIgnore compares e with want, ignoring fields with the specified +// ignore value. +func (e *Entry) EqualWithIgnore(want *Entry, ignore string) bool { + return (e.ID == want.ID || want.ID == -1) && + (e.Parent == want.Parent || want.Parent == -1) && + (e.Root == want.Root || want.Root == ignore) && + (e.Target == want.Target || want.Target == ignore) && + (e.VfsOptstr == want.VfsOptstr || want.VfsOptstr == ignore) && + (e.FsType == want.FsType || want.FsType == ignore) && + (e.Source == want.Source || want.Source == ignore) && + (e.FsOptstr == want.FsOptstr || want.FsOptstr == ignore) +} + +// String returns a text representation of e loosely following the kernel format. +func (e *Entry) String() string { + return fmt.Sprintf("%d %d %s %s %s %s %s %s", + e.ID, e.Parent, e.Root, e.Target, e.VfsOptstr, e.FsType, e.Source, e.FsOptstr) +} diff --git a/test/internal/mountinfo/mountinfo_guard.go b/test/internal/mountinfo/mountinfo_guard.go new file mode 100644 index 00000000..aaf63ac4 --- /dev/null +++ b/test/internal/mountinfo/mountinfo_guard.go @@ -0,0 +1,15 @@ +//go:build !testsuite && !tester + +package mountinfo + +import ( + "os" + "testing" +) + +func init() { + if !testing.Testing() { + println("package mountinfo imported in non-testsuite program") + os.Exit(1) + } +} diff --git a/test/internal/mountinfo/mountinfo_test.go b/test/internal/mountinfo/mountinfo_test.go new file mode 100644 index 00000000..45cfdf5f --- /dev/null +++ b/test/internal/mountinfo/mountinfo_test.go @@ -0,0 +1,146 @@ +package mountinfo_test + +import ( + "os" + "path/filepath" + "testing" + + "hakurei.app/test/internal/mountinfo" +) + +func TestMountinfo(t *testing.T) { + testCases := []struct { + name string + + sample string + want []*mountinfo.Entry + }{ + {"util-linux", `15 20 0:3 / /proc rw,relatime - proc /proc rw +16 20 0:15 / /sys rw,relatime - sysfs /sys rw +17 20 0:5 / /dev rw,relatime - devtmpfs udev rw,size=1983516k,nr_inodes=495879,mode=755 +18 17 0:10 / /dev/pts rw,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000 +19 17 0:16 / /dev/shm rw,relatime - tmpfs tmpfs rw +20 1 8:4 / / rw,noatime - ext3 /dev/sda4 rw,errors=continue,user_xattr,acl,barrier=0,data=ordered +21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755 +22 21 0:18 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd +23 21 0:19 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuset +24 21 0:20 / /sys/fs/cgroup/ns rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,ns +25 21 0:21 / /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpu +26 21 0:22 / /sys/fs/cgroup/cpuacct rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuacct +27 21 0:23 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory +28 21 0:24 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,devices +29 21 0:25 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,freezer +30 21 0:26 / /sys/fs/cgroup/net_cls rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,net_cls +31 21 0:27 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,blkio +32 16 0:28 / /sys/kernel/security rw,relatime - autofs systemd-1 rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct +33 17 0:29 / /dev/hugepages rw,relatime - autofs systemd-1 rw,fd=23,pgrp=1,timeout=300,minproto=5,maxproto=5,direct +34 16 0:30 / /sys/kernel/debug rw,relatime - autofs systemd-1 rw,fd=24,pgrp=1,timeout=300,minproto=5,maxproto=5,direct +35 15 0:31 / /proc/sys/fs/binfmt_misc rw,relatime - autofs systemd-1 rw,fd=25,pgrp=1,timeout=300,minproto=5,maxproto=5,direct +36 17 0:32 / /dev/mqueue rw,relatime - autofs systemd-1 rw,fd=26,pgrp=1,timeout=300,minproto=5,maxproto=5,direct +37 15 0:14 / /proc/bus/usb rw,relatime - usbfs /proc/bus/usb rw +38 33 0:33 / /dev/hugepages rw,relatime - hugetlbfs hugetlbfs rw +39 36 0:12 / /dev/mqueue rw,relatime - mqueue mqueue rw +40 20 8:6 / /boot rw,noatime - ext3 /dev/sda6 rw,errors=continue,barrier=0,data=ordered +41 20 253:0 / /home/kzak rw,noatime - ext4 /dev/mapper/kzak-home rw,barrier=1,data=ordered +42 35 0:34 / /proc/sys/fs/binfmt_misc rw,relatime - binfmt_misc none rw +43 16 0:35 / /sys/fs/fuse/connections rw,relatime - fusectl fusectl rw +44 41 0:36 / /home/kzak/.gvfs rw,nosuid,nodev,relatime - fuse.gvfs-fuse-daemon gvfs-fuse-daemon rw,user_id=500,group_id=500 +45 20 0:37 / /var/lib/nfs/rpc_pipefs rw,relatime - rpc_pipefs sunrpc rw +47 20 0:38 / /mnt/sounds rw,relatime - cifs //foo.home/bar/ rw,unc=\\foo.home\bar,username=kzak,domain=SRGROUP,uid=0,noforceuid,gid=0,noforcegid,addr=192.168.111.1,posixpaths,serverino,acl,rsize=16384,wsize=57344 +49 20 0:56 / /mnt/test/foobar rw,relatime,nosymfollow shared:323 - tmpfs tmpfs rw`, []*mountinfo.Entry{ + e(15, 20, "/", "/proc", "rw,relatime", "proc", "/proc", "rw"), + e(16, 20, "/", "/sys", "rw,relatime", "sysfs", "/sys", "rw"), + e(17, 20, "/", "/dev", "rw,relatime", "devtmpfs", "udev", "rw,size=1983516k,nr_inodes=495879,mode=755"), + e(18, 17, "/", "/dev/pts", "rw,relatime", "devpts", "devpts", "rw,gid=5,mode=620,ptmxmode=000"), + e(19, 17, "/", "/dev/shm", "rw,relatime", "tmpfs", "tmpfs", "rw"), + e(20, 1, "/", "/", "rw,noatime", "ext3", "/dev/sda4", "rw,errors=continue,user_xattr,acl,barrier=0,data=ordered"), + e(21, 16, "/", "/sys/fs/cgroup", "rw,nosuid,nodev,noexec,relatime", "tmpfs", "tmpfs", "rw,mode=755"), + e(22, 21, "/", "/sys/fs/cgroup/systemd", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd"), + e(23, 21, "/", "/sys/fs/cgroup/cpuset", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,cpuset"), + e(24, 21, "/", "/sys/fs/cgroup/ns", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,ns"), + e(25, 21, "/", "/sys/fs/cgroup/cpu", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,cpu"), + e(26, 21, "/", "/sys/fs/cgroup/cpuacct", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,cpuacct"), + e(27, 21, "/", "/sys/fs/cgroup/memory", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,memory"), + e(28, 21, "/", "/sys/fs/cgroup/devices", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,devices"), + e(29, 21, "/", "/sys/fs/cgroup/freezer", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,freezer"), + e(30, 21, "/", "/sys/fs/cgroup/net_cls", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,net_cls"), + e(31, 21, "/", "/sys/fs/cgroup/blkio", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,blkio"), + e(32, 16, "/", "/sys/kernel/security", "rw,relatime", "autofs", "systemd-1", "rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct"), + e(33, 17, "/", "/dev/hugepages", "rw,relatime", "autofs", "systemd-1", "rw,fd=23,pgrp=1,timeout=300,minproto=5,maxproto=5,direct"), + e(34, 16, "/", "/sys/kernel/debug", "rw,relatime", "autofs", "systemd-1", "rw,fd=24,pgrp=1,timeout=300,minproto=5,maxproto=5,direct"), + e(35, 15, "/", "/proc/sys/fs/binfmt_misc", "rw,relatime", "autofs", "systemd-1", "rw,fd=25,pgrp=1,timeout=300,minproto=5,maxproto=5,direct"), + e(36, 17, "/", "/dev/mqueue", "rw,relatime", "autofs", "systemd-1", "rw,fd=26,pgrp=1,timeout=300,minproto=5,maxproto=5,direct"), + e(37, 15, "/", "/proc/bus/usb", "rw,relatime", "usbfs", "/proc/bus/usb", "rw"), + e(38, 33, "/", "/dev/hugepages", "rw,relatime", "hugetlbfs", "hugetlbfs", "rw"), + e(39, 36, "/", "/dev/mqueue", "rw,relatime", "mqueue", "mqueue", "rw"), + e(40, 20, "/", "/boot", "rw,noatime", "ext3", "/dev/sda6", "rw,errors=continue,barrier=0,data=ordered"), + e(41, 20, "/", "/home/kzak", "rw,noatime", "ext4", "/dev/mapper/kzak-home", "rw,barrier=1,data=ordered"), + e(42, 35, "/", "/proc/sys/fs/binfmt_misc", "rw,relatime", "binfmt_misc", "none", "rw"), + e(43, 16, "/", "/sys/fs/fuse/connections", "rw,relatime", "fusectl", "fusectl", "rw"), + e(44, 41, "/", "/home/kzak/.gvfs", "rw,nosuid,nodev,relatime", "fuse.gvfs-fuse-daemon", "gvfs-fuse-daemon", "rw,user_id=500,group_id=500"), + e(45, 20, "/", "/var/lib/nfs/rpc_pipefs", "rw,relatime", "rpc_pipefs", "sunrpc", "rw"), + e(47, 20, "/", "/mnt/sounds", "rw,relatime", "cifs", "//foo.home/bar/", "rw,unc=\\\\foo.home\\bar,username=kzak,domain=SRGROUP,uid=0,noforceuid,gid=0,noforcegid,addr=192.168.111.1,posixpaths,serverino,acl,rsize=16384,wsize=57344"), + e(49, 20, "/", "/mnt/test/foobar", "rw,relatime,nosymfollow", "tmpfs", "tmpfs", "rw"), + }}, + } + + for _, tc := range testCases { + name := filepath.Join(t.TempDir(), "sample") + if err := os.WriteFile(name, []byte(tc.sample), 0400); err != nil { + t.Fatalf("cannot write sample: %v", err) + } + + t.Run(tc.name, func(t *testing.T) { + m, err := mountinfo.Open(name) + if err != nil { + t.Fatalf("Open: error = %v", err) + } + t.Cleanup(m.Close) + + i := 0 + var ent mountinfo.Entry + for m.Next() { + m.Copy(&ent) + + if i == len(tc.want) { + t.Errorf("Next: got more than %d entries", i) + t.FailNow() + } + if !ent.EqualWithIgnore(tc.want[i], "\x00") { + t.Errorf("Next: entry %d\n got: %#v\nwant: %#v", i, + ent, &tc.want[i]) + t.FailNow() + } else { + t.Logf("%s", &ent) + } + + i++ + } + + if err = m.Err(); err != nil { + t.Fatalf("Err: %v", err) + } + }) + + if err := os.Remove(name); err != nil { + t.Fatalf("cannot remove %q: %v", name, err) + } + } +} + +func e( + id, parent int, + root, target, vfsOptstr string, + fsType, source, fsOptstr string, +) *mountinfo.Entry { + return &mountinfo.Entry{ + ID: id, + Parent: parent, + Root: root, + Target: target, + VfsOptstr: vfsOptstr, + FsType: fsType, + Source: source, + FsOptstr: fsOptstr, + } +} diff --git a/test/internal/sandbox/assert.go b/test/internal/sandbox/assert.go index e6fe926b..2125419b 100644 --- a/test/internal/sandbox/assert.go +++ b/test/internal/sandbox/assert.go @@ -19,6 +19,8 @@ import ( "os" "path/filepath" "syscall" + + "hakurei.app/test/internal/mountinfo" ) var ( @@ -31,10 +33,10 @@ func printf(format string, v ...any) { printfFunc(format, v...) } func fatalf(format string, v ...any) { fatalfFunc(format, v...) } type TestCase struct { - Env []string `json:"env"` - FS *FS `json:"fs"` - Mount []*MountinfoEntry `json:"mount"` - Seccomp bool `json:"seccomp"` + Env []string `json:"env"` + FS *FS `json:"fs"` + Mount []*mountinfo.Entry `json:"mount"` + Seccomp bool `json:"seccomp"` TrySocket string `json:"try_socket,omitempty"` SocketAbstract bool `json:"socket_abstract,omitempty"` @@ -120,15 +122,18 @@ func (t *T) MustCheck(want *TestCase) { var fail bool m := mustParseMountinfo(t.MountsPath) i := 0 - for ent := range m.Entries() { + var ent mountinfo.Entry + for m.Next() { + m.Copy(&ent) + if i == len(want.Mount) { fatalf("got more than %d entries", i) } if !ent.EqualWithIgnore(want.Mount[i], "//ignore") { fail = true - printf("[FAIL] %s", ent) + printf("[FAIL] %s", &ent) } else { - printf("[ OK ] %s", ent) + printf("[ OK ] %s", &ent) } i++ @@ -262,9 +267,9 @@ func mustDecode(wantFilePath string, v any) { } } -func mustParseMountinfo(name string) *Mountinfo { - m := NewMountinfo(name) - if err := m.Parse(); err != nil { +func mustParseMountinfo(name string) *mountinfo.Iter { + m, err := mountinfo.Open(name) + if err != nil { fatalf("%v", err) panic("unreachable") } diff --git a/test/internal/sandbox/mount.go b/test/internal/sandbox/mount.go deleted file mode 100644 index 791a68b9..00000000 --- a/test/internal/sandbox/mount.go +++ /dev/null @@ -1,159 +0,0 @@ -//go:build testtool - -package sandbox - -/* -#cgo linux pkg-config: --static mount - -#include -#include -#include - -const char *HAKUREI_MOUNTINFO_PATH = "/proc/self/mountinfo"; -*/ -import "C" - -import ( - "errors" - "fmt" - "iter" - "runtime" - "sync" - "unsafe" -) - -var ( - ErrMountinfoParse = errors.New("invalid mountinfo records") - ErrMountinfoIter = errors.New("cannot allocate iterator") - ErrMountinfoFault = errors.New("cannot iterate on filesystems") -) - -type ( - Mountinfo struct { - mu sync.RWMutex - p string - err error - - tb *C.struct_libmnt_table - itr *C.struct_libmnt_iter - - fs *C.struct_libmnt_fs - } - - // MountinfoEntry represents deterministic mountinfo parts of a libmnt_fs entry. - MountinfoEntry struct { - // mount ID: a unique ID for the mount (may be reused after umount(2)). - ID int `json:"id"` - // parent ID: the ID of the parent mount (or of self for the root of this mount namespace's mount tree). - Parent int `json:"parent"` - // root: the pathname of the directory in the filesystem which forms the root of this mount. - Root string `json:"root"` - // mount point: the pathname of the mount point relative to the process's root directory. - Target string `json:"target"` - // mount options: per-mount options (see mount(2)). - VfsOptstr string `json:"vfs_optstr"` - // filesystem type: the filesystem type in the form "type[.subtype]". - FsType string `json:"fstype"` - // mount source: filesystem-specific information or "none". - Source string `json:"source"` - // super options: per-superblock options (see mount(2)). - FsOptstr string `json:"fs_optstr"` - } -) - -func (m *Mountinfo) copy(v *MountinfoEntry) { - if m.fs == nil { - panic("invalid entry") - } - v.ID = int(C.mnt_fs_get_id(m.fs)) - v.Parent = int(C.mnt_fs_get_parent_id(m.fs)) - v.Root = C.GoString(C.mnt_fs_get_root(m.fs)) - v.Target = C.GoString(C.mnt_fs_get_target(m.fs)) - v.VfsOptstr = C.GoString(C.mnt_fs_get_vfs_options(m.fs)) - v.FsType = C.GoString(C.mnt_fs_get_fstype(m.fs)) - v.Source = C.GoString(C.mnt_fs_get_source(m.fs)) - v.FsOptstr = C.GoString(C.mnt_fs_get_fs_options(m.fs)) -} - -func NewMountinfo(p string) *Mountinfo { m := new(Mountinfo); m.p = p; return m } - -func (m *Mountinfo) Err() error { m.mu.RLock(); defer m.mu.RUnlock(); return m.err } - -func (m *Mountinfo) Parse() error { - m.mu.Lock() - defer m.mu.Unlock() - - if m.tb != nil { - panic("open called twice") - } - - if m.p == "" { - m.tb = C.mnt_new_table_from_file(C.HAKUREI_MOUNTINFO_PATH) - } else { - name := C.CString(m.p) - m.tb = C.mnt_new_table_from_file(name) - C.free(unsafe.Pointer(name)) - } - if m.tb == nil { - return ErrMountinfoParse - } - m.itr = C.mnt_new_iter(C.MNT_ITER_FORWARD) - if m.itr == nil { - C.mnt_unref_table(m.tb) - return ErrMountinfoIter - } - - runtime.SetFinalizer(m, (*Mountinfo).Unref) - return nil -} - -func (m *Mountinfo) Unref() { - m.mu.Lock() - defer m.mu.Unlock() - - if m.tb == nil { - panic("unref called before parse") - } - - C.mnt_unref_table(m.tb) - C.mnt_free_iter(m.itr) - runtime.SetFinalizer(m, nil) -} - -func (m *Mountinfo) Entries() iter.Seq[*MountinfoEntry] { - return func(yield func(*MountinfoEntry) bool) { - m.mu.Lock() - defer m.mu.Unlock() - - C.mnt_reset_iter(m.itr, -1) - - var rc C.int - ent := new(MountinfoEntry) - for rc = C.mnt_table_next_fs(m.tb, m.itr, &m.fs); rc == 0; rc = C.mnt_table_next_fs(m.tb, m.itr, &m.fs) { - m.copy(ent) - if !yield(ent) { - return - } - } - if rc < 0 { - m.err = ErrMountinfoFault - return - } - } -} - -func (e *MountinfoEntry) EqualWithIgnore(want *MountinfoEntry, ignore string) bool { - return (e.ID == want.ID || want.ID == -1) && - (e.Parent == want.Parent || want.Parent == -1) && - (e.Root == want.Root || want.Root == ignore) && - (e.Target == want.Target || want.Target == ignore) && - (e.VfsOptstr == want.VfsOptstr || want.VfsOptstr == ignore) && - (e.FsType == want.FsType || want.FsType == ignore) && - (e.Source == want.Source || want.Source == ignore) && - (e.FsOptstr == want.FsOptstr || want.FsOptstr == ignore) -} - -func (e *MountinfoEntry) String() string { - return fmt.Sprintf("%d %d %s %s %s %s %s %s", - e.ID, e.Parent, e.Root, e.Target, e.VfsOptstr, e.FsType, e.Source, e.FsOptstr) -} diff --git a/test/internal/sandbox/mount_test.go b/test/internal/sandbox/mount_test.go deleted file mode 100644 index a565b0a0..00000000 --- a/test/internal/sandbox/mount_test.go +++ /dev/null @@ -1,144 +0,0 @@ -//go:build testtool - -package sandbox_test - -import ( - "os" - "path/filepath" - "testing" - - "hakurei.app/test/internal/sandbox" -) - -func TestMountinfo(t *testing.T) { - testCases := []struct { - name string - - sample string - want []*sandbox.MountinfoEntry - }{ - {"util-linux", `15 20 0:3 / /proc rw,relatime - proc /proc rw -16 20 0:15 / /sys rw,relatime - sysfs /sys rw -17 20 0:5 / /dev rw,relatime - devtmpfs udev rw,size=1983516k,nr_inodes=495879,mode=755 -18 17 0:10 / /dev/pts rw,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000 -19 17 0:16 / /dev/shm rw,relatime - tmpfs tmpfs rw -20 1 8:4 / / rw,noatime - ext3 /dev/sda4 rw,errors=continue,user_xattr,acl,barrier=0,data=ordered -21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755 -22 21 0:18 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd -23 21 0:19 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuset -24 21 0:20 / /sys/fs/cgroup/ns rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,ns -25 21 0:21 / /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpu -26 21 0:22 / /sys/fs/cgroup/cpuacct rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuacct -27 21 0:23 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory -28 21 0:24 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,devices -29 21 0:25 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,freezer -30 21 0:26 / /sys/fs/cgroup/net_cls rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,net_cls -31 21 0:27 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,blkio -32 16 0:28 / /sys/kernel/security rw,relatime - autofs systemd-1 rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct -33 17 0:29 / /dev/hugepages rw,relatime - autofs systemd-1 rw,fd=23,pgrp=1,timeout=300,minproto=5,maxproto=5,direct -34 16 0:30 / /sys/kernel/debug rw,relatime - autofs systemd-1 rw,fd=24,pgrp=1,timeout=300,minproto=5,maxproto=5,direct -35 15 0:31 / /proc/sys/fs/binfmt_misc rw,relatime - autofs systemd-1 rw,fd=25,pgrp=1,timeout=300,minproto=5,maxproto=5,direct -36 17 0:32 / /dev/mqueue rw,relatime - autofs systemd-1 rw,fd=26,pgrp=1,timeout=300,minproto=5,maxproto=5,direct -37 15 0:14 / /proc/bus/usb rw,relatime - usbfs /proc/bus/usb rw -38 33 0:33 / /dev/hugepages rw,relatime - hugetlbfs hugetlbfs rw -39 36 0:12 / /dev/mqueue rw,relatime - mqueue mqueue rw -40 20 8:6 / /boot rw,noatime - ext3 /dev/sda6 rw,errors=continue,barrier=0,data=ordered -41 20 253:0 / /home/kzak rw,noatime - ext4 /dev/mapper/kzak-home rw,barrier=1,data=ordered -42 35 0:34 / /proc/sys/fs/binfmt_misc rw,relatime - binfmt_misc none rw -43 16 0:35 / /sys/fs/fuse/connections rw,relatime - fusectl fusectl rw -44 41 0:36 / /home/kzak/.gvfs rw,nosuid,nodev,relatime - fuse.gvfs-fuse-daemon gvfs-fuse-daemon rw,user_id=500,group_id=500 -45 20 0:37 / /var/lib/nfs/rpc_pipefs rw,relatime - rpc_pipefs sunrpc rw -47 20 0:38 / /mnt/sounds rw,relatime - cifs //foo.home/bar/ rw,unc=\\foo.home\bar,username=kzak,domain=SRGROUP,uid=0,noforceuid,gid=0,noforcegid,addr=192.168.111.1,posixpaths,serverino,acl,rsize=16384,wsize=57344 -49 20 0:56 / /mnt/test/foobar rw,relatime,nosymfollow shared:323 - tmpfs tmpfs rw`, []*sandbox.MountinfoEntry{ - e(15, 20, "/", "/proc", "rw,relatime", "proc", "/proc", "rw"), - e(16, 20, "/", "/sys", "rw,relatime", "sysfs", "/sys", "rw"), - e(17, 20, "/", "/dev", "rw,relatime", "devtmpfs", "udev", "rw,size=1983516k,nr_inodes=495879,mode=755"), - e(18, 17, "/", "/dev/pts", "rw,relatime", "devpts", "devpts", "rw,gid=5,mode=620,ptmxmode=000"), - e(19, 17, "/", "/dev/shm", "rw,relatime", "tmpfs", "tmpfs", "rw"), - e(20, 1, "/", "/", "rw,noatime", "ext3", "/dev/sda4", "rw,errors=continue,user_xattr,acl,barrier=0,data=ordered"), - e(21, 16, "/", "/sys/fs/cgroup", "rw,nosuid,nodev,noexec,relatime", "tmpfs", "tmpfs", "rw,mode=755"), - e(22, 21, "/", "/sys/fs/cgroup/systemd", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd"), - e(23, 21, "/", "/sys/fs/cgroup/cpuset", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,cpuset"), - e(24, 21, "/", "/sys/fs/cgroup/ns", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,ns"), - e(25, 21, "/", "/sys/fs/cgroup/cpu", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,cpu"), - e(26, 21, "/", "/sys/fs/cgroup/cpuacct", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,cpuacct"), - e(27, 21, "/", "/sys/fs/cgroup/memory", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,memory"), - e(28, 21, "/", "/sys/fs/cgroup/devices", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,devices"), - e(29, 21, "/", "/sys/fs/cgroup/freezer", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,freezer"), - e(30, 21, "/", "/sys/fs/cgroup/net_cls", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,net_cls"), - e(31, 21, "/", "/sys/fs/cgroup/blkio", "rw,nosuid,nodev,noexec,relatime", "cgroup", "cgroup", "rw,blkio"), - e(32, 16, "/", "/sys/kernel/security", "rw,relatime", "autofs", "systemd-1", "rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct"), - e(33, 17, "/", "/dev/hugepages", "rw,relatime", "autofs", "systemd-1", "rw,fd=23,pgrp=1,timeout=300,minproto=5,maxproto=5,direct"), - e(34, 16, "/", "/sys/kernel/debug", "rw,relatime", "autofs", "systemd-1", "rw,fd=24,pgrp=1,timeout=300,minproto=5,maxproto=5,direct"), - e(35, 15, "/", "/proc/sys/fs/binfmt_misc", "rw,relatime", "autofs", "systemd-1", "rw,fd=25,pgrp=1,timeout=300,minproto=5,maxproto=5,direct"), - e(36, 17, "/", "/dev/mqueue", "rw,relatime", "autofs", "systemd-1", "rw,fd=26,pgrp=1,timeout=300,minproto=5,maxproto=5,direct"), - e(37, 15, "/", "/proc/bus/usb", "rw,relatime", "usbfs", "/proc/bus/usb", "rw"), - e(38, 33, "/", "/dev/hugepages", "rw,relatime", "hugetlbfs", "hugetlbfs", "rw"), - e(39, 36, "/", "/dev/mqueue", "rw,relatime", "mqueue", "mqueue", "rw"), - e(40, 20, "/", "/boot", "rw,noatime", "ext3", "/dev/sda6", "rw,errors=continue,barrier=0,data=ordered"), - e(41, 20, "/", "/home/kzak", "rw,noatime", "ext4", "/dev/mapper/kzak-home", "rw,barrier=1,data=ordered"), - e(42, 35, "/", "/proc/sys/fs/binfmt_misc", "rw,relatime", "binfmt_misc", "none", "rw"), - e(43, 16, "/", "/sys/fs/fuse/connections", "rw,relatime", "fusectl", "fusectl", "rw"), - e(44, 41, "/", "/home/kzak/.gvfs", "rw,nosuid,nodev,relatime", "fuse.gvfs-fuse-daemon", "gvfs-fuse-daemon", "rw,user_id=500,group_id=500"), - e(45, 20, "/", "/var/lib/nfs/rpc_pipefs", "rw,relatime", "rpc_pipefs", "sunrpc", "rw"), - e(47, 20, "/", "/mnt/sounds", "rw,relatime", "cifs", "//foo.home/bar/", "rw,unc=\\\\foo.home\\bar,username=kzak,domain=SRGROUP,uid=0,noforceuid,gid=0,noforcegid,addr=192.168.111.1,posixpaths,serverino,acl,rsize=16384,wsize=57344"), - e(49, 20, "/", "/mnt/test/foobar", "rw,relatime,nosymfollow", "tmpfs", "tmpfs", "rw"), - }}, - } - - for _, tc := range testCases { - name := filepath.Join(t.TempDir(), "sample") - if err := os.WriteFile(name, []byte(tc.sample), 0400); err != nil { - t.Fatalf("cannot write sample: %v", err) - } - - t.Run(tc.name, func(t *testing.T) { - m := sandbox.NewMountinfo(name) - if err := m.Parse(); err != nil { - t.Fatalf("Parse: error = %v", err) - } - - i := 0 - for ent := range m.Entries() { - if i == len(tc.want) { - t.Errorf("Entries: got more than %d entries", i) - t.FailNow() - } - if !ent.EqualWithIgnore(tc.want[i], "\x00") { - t.Errorf("Entries: entry %d\n got: %#v\nwant: %#v", i, - ent, &tc.want[i]) - t.FailNow() - } else { - t.Logf("%s", ent) - } - - i++ - } - - if err := m.Err(); err != nil { - t.Fatalf("Mountinfo: error = %v", err) - } - - m.Unref() - }) - - if err := os.Remove(name); err != nil { - t.Fatalf("cannot remove %q: %v", name, err) - } - } -} - -func e( - id, parent int, root, target, vfsOptstr string, fsType, source, fsOptstr string, -) *sandbox.MountinfoEntry { - return &sandbox.MountinfoEntry{ - ID: id, - Parent: parent, - Root: root, - Target: target, - VfsOptstr: vfsOptstr, - FsType: fsType, - Source: source, - FsOptstr: fsOptstr, - } -} diff --git a/test/sandbox/tool/package.nix b/test/sandbox/tool/package.nix index 13e18272..bd57b432 100644 --- a/test/sandbox/tool/package.nix +++ b/test/sandbox/tool/package.nix @@ -17,7 +17,7 @@ buildGoModule rec { }; vendorHash = null; - tags = [ "testtool" ]; + tags = [ "testtool" "tester" ]; buildInputs = [ util-linux ]; nativeBuildInputs = [ pkg-config ]; -- cgit v1.3.1