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