container/vfs: wrap decoder errors
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m8s
Test / Hakurei (push) Successful in 3m15s
Test / Hpkg (push) Successful in 3m33s
Test / Sandbox (race detector) (push) Successful in 4m30s
Test / Hakurei (race detector) (push) Successful in 5m19s
Test / Flake checks (push) Successful in 1m35s
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m8s
Test / Hakurei (push) Successful in 3m15s
Test / Hpkg (push) Successful in 3m33s
Test / Sandbox (race detector) (push) Successful in 4m30s
Test / Hakurei (race detector) (push) Successful in 5m19s
Test / Flake checks (push) Successful in 1m35s
This passes line information and handles strconv errors so it reads better. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
905b9f9785
commit
50972096cd
@ -149,7 +149,7 @@ func (p *procPaths) remount(target string, flags uintptr) error {
|
|||||||
return p.mountinfo(func(d *vfs.MountInfoDecoder) error {
|
return p.mountinfo(func(d *vfs.MountInfoDecoder) error {
|
||||||
n, err := d.Unfold(targetKFinal)
|
n, err := d.Unfold(targetKFinal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, ESTALE) {
|
if errors.As(err, new(vfs.UnfoldTargetError)) {
|
||||||
return msg.WrapErr(err,
|
return msg.WrapErr(err,
|
||||||
fmt.Sprintf("mount point %q never appeared in mountinfo", targetKFinal))
|
fmt.Sprintf("mount point %q never appeared in mountinfo", targetKFinal))
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ func TestRemount(t *testing.T) {
|
|||||||
{"close", expectArgs{0xdeadbeef}, nil, errUnique},
|
{"close", expectArgs{0xdeadbeef}, nil, errUnique},
|
||||||
}}, errUnique},
|
}}, errUnique},
|
||||||
|
|
||||||
{"mountinfo stale", func(k syscallDispatcher) error {
|
{"mountinfo no match", func(k syscallDispatcher) error {
|
||||||
return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
|
return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
|
||||||
}, [][]kexpect{{
|
}, [][]kexpect{{
|
||||||
{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/.hakurei", nil},
|
{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/.hakurei", nil},
|
||||||
@ -118,7 +118,7 @@ func TestRemount(t *testing.T) {
|
|||||||
{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/.hakurei", nil},
|
{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/.hakurei", nil},
|
||||||
{"close", expectArgs{0xdeadbeef}, nil, nil},
|
{"close", expectArgs{0xdeadbeef}, nil, nil},
|
||||||
{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil},
|
{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil},
|
||||||
}}, msg.WrapErr(syscall.ESTALE, `mount point "/sysroot/.hakurei" never appeared in mountinfo`)},
|
}}, msg.WrapErr(&vfs.DecoderError{Op: "unfold", Line: -1, Err: vfs.UnfoldTargetError("/sysroot/.hakurei")}, `mount point "/sysroot/.hakurei" never appeared in mountinfo`)},
|
||||||
|
|
||||||
{"mountinfo", func(k syscallDispatcher) error {
|
{"mountinfo", func(k syscallDispatcher) error {
|
||||||
return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
|
return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
|
||||||
@ -128,7 +128,7 @@ func TestRemount(t *testing.T) {
|
|||||||
{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil},
|
{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil},
|
||||||
{"close", expectArgs{0xdeadbeef}, nil, nil},
|
{"close", expectArgs{0xdeadbeef}, nil, nil},
|
||||||
{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile("\x00"), nil},
|
{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile("\x00"), nil},
|
||||||
}}, wrapErrSuffix(vfs.ErrMountInfoFields, `cannot parse mountinfo:`)},
|
}}, wrapErrSuffix(&vfs.DecoderError{Op: "parse", Line: 0, Err: vfs.ErrMountInfoFields}, `cannot parse mountinfo:`)},
|
||||||
|
|
||||||
{"mount", func(k syscallDispatcher) error {
|
{"mount", func(k syscallDispatcher) error {
|
||||||
return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
|
return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
|
||||||
|
@ -248,7 +248,7 @@ func TestProcPaths(t *testing.T) {
|
|||||||
t.Fatalf("WriteFile: error = %v", err)
|
t.Fatalf("WriteFile: error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wantErr := wrapErrSuffix(vfs.ErrMountInfoFields, "cannot parse mountinfo:")
|
wantErr := wrapErrSuffix(&vfs.DecoderError{Op: "parse", Line: 0, Err: vfs.ErrMountInfoFields}, "cannot parse mountinfo:")
|
||||||
if err := newProcPaths(direct{}, tempDir).mountinfo(func(d *vfs.MountInfoDecoder) error { return d.Decode(new(*vfs.MountInfo)) }); !errors.Is(err, wantErr) {
|
if err := newProcPaths(direct{}, tempDir).mountinfo(func(d *vfs.MountInfoDecoder) error { return d.Decode(new(*vfs.MountInfo)) }); !errors.Is(err, wantErr) {
|
||||||
t.Fatalf("mountinfo: error = %v, want %v", err, wantErr)
|
t.Fatalf("mountinfo: error = %v, want %v", err, wantErr)
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,32 @@ var (
|
|||||||
ErrMountInfoSep = errors.New("bad optional fields separator")
|
ErrMountInfoSep = errors.New("bad optional fields separator")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DecoderError struct {
|
||||||
|
Op string
|
||||||
|
Line int
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DecoderError) Unwrap() error { return e.Err }
|
||||||
|
func (e *DecoderError) Error() string {
|
||||||
|
var s string
|
||||||
|
|
||||||
|
var numError *strconv.NumError
|
||||||
|
switch {
|
||||||
|
case errors.As(e.Err, &numError) && numError != nil:
|
||||||
|
s = "numeric field " + strconv.Quote(numError.Num) + " " + numError.Err.Error()
|
||||||
|
|
||||||
|
default:
|
||||||
|
s = e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
var atLine string
|
||||||
|
if e.Line >= 0 {
|
||||||
|
atLine = " at line " + strconv.Itoa(e.Line)
|
||||||
|
}
|
||||||
|
return e.Op + " mountinfo" + atLine + ": " + s
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// A MountInfoDecoder reads and decodes proc_pid_mountinfo(5) entries from an input stream.
|
// A MountInfoDecoder reads and decodes proc_pid_mountinfo(5) entries from an input stream.
|
||||||
MountInfoDecoder struct {
|
MountInfoDecoder struct {
|
||||||
@ -32,6 +58,7 @@ type (
|
|||||||
|
|
||||||
current *MountInfo
|
current *MountInfo
|
||||||
parseErr error
|
parseErr error
|
||||||
|
curLine int
|
||||||
complete bool
|
complete bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,9 +159,12 @@ func (d *MountInfoDecoder) Entries() iter.Seq[*MountInfoEntry] {
|
|||||||
|
|
||||||
func (d *MountInfoDecoder) Err() error {
|
func (d *MountInfoDecoder) Err() error {
|
||||||
if err := d.s.Err(); err != nil {
|
if err := d.s.Err(); err != nil {
|
||||||
return err
|
return &DecoderError{"scan", d.curLine, err}
|
||||||
}
|
}
|
||||||
return d.parseErr
|
if d.parseErr != nil {
|
||||||
|
return &DecoderError{"parse", d.curLine, d.parseErr}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MountInfoDecoder) scan() bool {
|
func (d *MountInfoDecoder) scan() bool {
|
||||||
@ -160,6 +190,7 @@ func (d *MountInfoDecoder) scan() bool {
|
|||||||
d.current.Next = m
|
d.current.Next = m
|
||||||
d.current = d.current.Next
|
d.current = d.current.Next
|
||||||
}
|
}
|
||||||
|
d.curLine++
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"iter"
|
"iter"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
@ -15,62 +16,102 @@ import (
|
|||||||
"hakurei.app/container/vfs"
|
"hakurei.app/container/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestDecoderError(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
err *vfs.DecoderError
|
||||||
|
want string
|
||||||
|
target error
|
||||||
|
targetF error
|
||||||
|
}{
|
||||||
|
{"errno", &vfs.DecoderError{Op: "parse", Line: 0xdeadbeef, Err: syscall.ENOTRECOVERABLE},
|
||||||
|
"parse mountinfo at line 3735928559: state not recoverable", syscall.ENOTRECOVERABLE, syscall.EROFS},
|
||||||
|
|
||||||
|
{"strconv", &vfs.DecoderError{Op: "parse", Line: 0xdeadbeef, Err: &strconv.NumError{Func: "Atoi", Num: "meow", Err: strconv.ErrSyntax}},
|
||||||
|
`parse mountinfo at line 3735928559: numeric field "meow" invalid syntax`, strconv.ErrSyntax, os.ErrInvalid},
|
||||||
|
|
||||||
|
{"unfold", &vfs.DecoderError{Op: "unfold", Line: -1, Err: vfs.UnfoldTargetError("/proc/nonexistent")},
|
||||||
|
"unfold mountinfo: mount point /proc/nonexistent never appeared in mountinfo", vfs.UnfoldTargetError("/proc/nonexistent"), os.ErrNotExist},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Run("error", func(t *testing.T) {
|
||||||
|
if got := tc.err.Error(); got != tc.want {
|
||||||
|
t.Errorf("Error: %s, want %s", got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("is", func(t *testing.T) {
|
||||||
|
if !errors.Is(tc.err, tc.target) {
|
||||||
|
t.Errorf("Is: unexpected false")
|
||||||
|
}
|
||||||
|
if errors.Is(tc.err, tc.targetF) {
|
||||||
|
t.Errorf("Is: unexpected true")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMountInfo(t *testing.T) {
|
func TestMountInfo(t *testing.T) {
|
||||||
testCases := []mountInfoTest{
|
testCases := []mountInfoTest{
|
||||||
{"count", sampleMountinfoBase + `
|
{"count", sampleMountinfoBase + `
|
||||||
21 20 0:53/ /mnt/test rw,relatime - tmpfs rw
|
21 20 0:53/ /mnt/test rw,relatime - tmpfs rw
|
||||||
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
||||||
vfs.ErrMountInfoFields, "", nil, nil, nil},
|
&vfs.DecoderError{Op: "parse", Line: 6, Err: vfs.ErrMountInfoFields},
|
||||||
|
"", nil, nil, nil},
|
||||||
|
|
||||||
{"sep", sampleMountinfoBase + `
|
{"sep", sampleMountinfoBase + `
|
||||||
21 20 0:53 / /mnt/test rw,relatime shared:212 _ tmpfs rw
|
21 20 0:53 / /mnt/test rw,relatime shared:212 _ tmpfs rw
|
||||||
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
||||||
vfs.ErrMountInfoSep, "", nil, nil, nil},
|
&vfs.DecoderError{Op: "parse", Line: 6, Err: vfs.ErrMountInfoSep},
|
||||||
|
"", nil, nil, nil},
|
||||||
|
|
||||||
{"id", sampleMountinfoBase + `
|
{"id", sampleMountinfoBase + `
|
||||||
id 20 0:53 / /mnt/test rw,relatime shared:212 - tmpfs rw
|
id 20 0:53 / /mnt/test rw,relatime shared:212 - tmpfs rw
|
||||||
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
||||||
strconv.ErrSyntax, "", nil, nil, nil},
|
&vfs.DecoderError{Op: "parse", Line: 6, Err: &strconv.NumError{Func: "Atoi", Num: "id", Err: strconv.ErrSyntax}},
|
||||||
|
"", nil, nil, nil},
|
||||||
|
|
||||||
{"parent", sampleMountinfoBase + `
|
{"parent", sampleMountinfoBase + `
|
||||||
21 parent 0:53 / /mnt/test rw,relatime shared:212 - tmpfs rw
|
21 parent 0:53 / /mnt/test rw,relatime shared:212 - tmpfs rw
|
||||||
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
||||||
strconv.ErrSyntax, "", nil, nil, nil},
|
&vfs.DecoderError{Op: "parse", Line: 6, Err: &strconv.NumError{Func: "Atoi", Num: "parent", Err: strconv.ErrSyntax}}, "", nil, nil, nil},
|
||||||
|
|
||||||
{"devno", sampleMountinfoBase + `
|
{"devno", sampleMountinfoBase + `
|
||||||
21 20 053 / /mnt/test rw,relatime shared:212 - tmpfs rw
|
21 20 053 / /mnt/test rw,relatime shared:212 - tmpfs rw
|
||||||
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
||||||
nil, "unexpected EOF", nil, nil, nil},
|
nil, "parse mountinfo at line 6: unexpected EOF", nil, nil, nil},
|
||||||
|
|
||||||
{"maj", sampleMountinfoBase + `
|
{"maj", sampleMountinfoBase + `
|
||||||
21 20 maj:53 / /mnt/test rw,relatime shared:212 - tmpfs rw
|
21 20 maj:53 / /mnt/test rw,relatime shared:212 - tmpfs rw
|
||||||
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
||||||
nil, "expected integer", nil, nil, nil},
|
nil, "parse mountinfo at line 6: expected integer", nil, nil, nil},
|
||||||
|
|
||||||
{"min", sampleMountinfoBase + `
|
{"min", sampleMountinfoBase + `
|
||||||
21 20 0:min / /mnt/test rw,relatime shared:212 - tmpfs rw
|
21 20 0:min / /mnt/test rw,relatime shared:212 - tmpfs rw
|
||||||
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
||||||
nil, "expected integer", nil, nil, nil},
|
nil, "parse mountinfo at line 6: expected integer", nil, nil, nil},
|
||||||
|
|
||||||
{"mountroot", sampleMountinfoBase + `
|
{"mountroot", sampleMountinfoBase + `
|
||||||
21 20 0:53 /mnt/test rw,relatime - tmpfs rw
|
21 20 0:53 /mnt/test rw,relatime - tmpfs rw
|
||||||
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
||||||
vfs.ErrMountInfoEmpty, "", nil, nil, nil},
|
&vfs.DecoderError{Op: "parse", Line: 6, Err: vfs.ErrMountInfoEmpty}, "", nil, nil, nil},
|
||||||
|
|
||||||
{"target", sampleMountinfoBase + `
|
{"target", sampleMountinfoBase + `
|
||||||
21 20 0:53 / rw,relatime - tmpfs rw
|
21 20 0:53 / rw,relatime - tmpfs rw
|
||||||
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
||||||
vfs.ErrMountInfoEmpty, "", nil, nil, nil},
|
&vfs.DecoderError{Op: "parse", Line: 6, Err: vfs.ErrMountInfoEmpty}, "", nil, nil, nil},
|
||||||
|
|
||||||
{"vfs options", sampleMountinfoBase + `
|
{"vfs options", sampleMountinfoBase + `
|
||||||
21 20 0:53 / /mnt/test - tmpfs rw
|
21 20 0:53 / /mnt/test - tmpfs rw
|
||||||
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
||||||
vfs.ErrMountInfoEmpty, "", nil, nil, nil},
|
&vfs.DecoderError{Op: "parse", Line: 6, Err: vfs.ErrMountInfoEmpty}, "", nil, nil, nil},
|
||||||
|
|
||||||
{"FS type", sampleMountinfoBase + `
|
{"FS type", sampleMountinfoBase + `
|
||||||
21 20 0:53 / /mnt/test rw,relatime - rw
|
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755
|
||||||
21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755`,
|
21 20 0:53 / /mnt/test rw,relatime - rw`,
|
||||||
vfs.ErrMountInfoEmpty, "", nil, nil, nil},
|
&vfs.DecoderError{Op: "parse", Line: 7, Err: vfs.ErrMountInfoEmpty}, "", nil, nil, nil},
|
||||||
|
|
||||||
{"base", sampleMountinfoBase, nil, "", []*wantMountInfo{
|
{"base", sampleMountinfoBase, nil, "", []*wantMountInfo{
|
||||||
m(15, 20, 0, 3, "/", "/proc", "rw,relatime", o(), "proc", "/proc", "rw", syscall.MS_RELATIME, nil),
|
m(15, 20, 0, 3, "/", "/proc", "rw,relatime", o(), "proc", "/proc", "rw", syscall.MS_RELATIME, nil),
|
||||||
@ -266,9 +307,9 @@ func (tc *mountInfoTest) check(t *testing.T, d *vfs.MountInfoDecoder, funcName s
|
|||||||
})
|
})
|
||||||
} else if tc.wantNode != nil || tc.wantCollectF != nil {
|
} else if tc.wantNode != nil || tc.wantCollectF != nil {
|
||||||
panic("invalid test case")
|
panic("invalid test case")
|
||||||
} else if _, err := d.Unfold("/"); !errors.Is(err, tc.wantErr) {
|
} else if _, err := d.Unfold("/"); !reflect.DeepEqual(err, tc.wantErr) {
|
||||||
if tc.wantError == "" {
|
if tc.wantError == "" {
|
||||||
t.Errorf("Unfold: error = %v, wantErr %v",
|
t.Errorf("Unfold: error = %#v, wantErr %#v",
|
||||||
err, tc.wantErr)
|
err, tc.wantErr)
|
||||||
} else if err != nil && err.Error() != tc.wantError {
|
} else if err != nil && err.Error() != tc.wantError {
|
||||||
t.Errorf("Unfold: error = %q, wantError %q",
|
t.Errorf("Unfold: error = %q, wantError %q",
|
||||||
@ -276,9 +317,9 @@ func (tc *mountInfoTest) check(t *testing.T, d *vfs.MountInfoDecoder, funcName s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gotErr(); !errors.Is(err, tc.wantErr) {
|
if err := gotErr(); !reflect.DeepEqual(err, tc.wantErr) {
|
||||||
if tc.wantError == "" {
|
if tc.wantError == "" {
|
||||||
t.Errorf("%s: error = %v, wantErr %v",
|
t.Errorf("%s: error = %#v, wantErr %#v",
|
||||||
funcName, err, tc.wantErr)
|
funcName, err, tc.wantErr)
|
||||||
} else if err != nil && err.Error() != tc.wantError {
|
} else if err != nil && err.Error() != tc.wantError {
|
||||||
t.Errorf("%s: error = %q, wantError %q",
|
t.Errorf("%s: error = %q, wantError %q",
|
||||||
|
@ -4,9 +4,14 @@ import (
|
|||||||
"iter"
|
"iter"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type UnfoldTargetError string
|
||||||
|
|
||||||
|
func (e UnfoldTargetError) Error() string {
|
||||||
|
return "mount point " + string(e) + " never appeared in mountinfo"
|
||||||
|
}
|
||||||
|
|
||||||
// MountInfoNode positions a [MountInfoEntry] in its mount hierarchy.
|
// MountInfoNode positions a [MountInfoEntry] in its mount hierarchy.
|
||||||
type MountInfoNode struct {
|
type MountInfoNode struct {
|
||||||
*MountInfoEntry
|
*MountInfoEntry
|
||||||
@ -65,7 +70,8 @@ func (d *MountInfoDecoder) Unfold(target string) (*MountInfoNode, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if targetIndex == -1 {
|
if targetIndex == -1 {
|
||||||
return nil, syscall.ESTALE
|
// target does not exist in parsed mountinfo
|
||||||
|
return nil, &DecoderError{Op: "unfold", Line: -1, Err: UnfoldTargetError(targetClean)}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cur := range mountinfo {
|
for _, cur := range mountinfo {
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package vfs_test
|
package vfs_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/container/vfs"
|
"hakurei.app/container/vfs"
|
||||||
@ -26,7 +24,7 @@ func TestUnfold(t *testing.T) {
|
|||||||
"no match",
|
"no match",
|
||||||
sampleMountinfoBase,
|
sampleMountinfoBase,
|
||||||
"/mnt",
|
"/mnt",
|
||||||
syscall.ESTALE, nil, nil, nil,
|
&vfs.DecoderError{Op: "unfold", Line: -1, Err: vfs.UnfoldTargetError("/mnt")}, nil, nil, nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cover",
|
"cover",
|
||||||
@ -55,7 +53,7 @@ func TestUnfold(t *testing.T) {
|
|||||||
d := vfs.NewMountInfoDecoder(strings.NewReader(tc.sample))
|
d := vfs.NewMountInfoDecoder(strings.NewReader(tc.sample))
|
||||||
got, err := d.Unfold(tc.target)
|
got, err := d.Unfold(tc.target)
|
||||||
|
|
||||||
if !errors.Is(err, tc.wantErr) {
|
if !reflect.DeepEqual(err, tc.wantErr) {
|
||||||
t.Errorf("Unfold: error = %v, wantErr %v",
|
t.Errorf("Unfold: error = %v, wantErr %v",
|
||||||
err, tc.wantErr)
|
err, tc.wantErr)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user