container: wrap mount syscall errno
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Test / Create distribution (push) Successful in 33s
				
			
		
			
				
	
				Test / Sandbox (push) Successful in 1m39s
				
			
		
			
				
	
				Test / Hakurei (push) Successful in 3m16s
				
			
		
			
				
	
				Test / Hpkg (push) Successful in 3m37s
				
			
		
			
				
	
				Test / Sandbox (race detector) (push) Successful in 3m56s
				
			
		
			
				
	
				Test / Hakurei (race detector) (push) Successful in 5m17s
				
			
		
			
				
	
				Test / Flake checks (push) Successful in 1m36s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 33s
				
			Test / Sandbox (push) Successful in 1m39s
				
			Test / Hakurei (push) Successful in 3m16s
				
			Test / Hpkg (push) Successful in 3m37s
				
			Test / Sandbox (race detector) (push) Successful in 3m56s
				
			Test / Hakurei (race detector) (push) Successful in 5m17s
				
			Test / Flake checks (push) Successful in 1m36s
				
			This is the first step to deprecating the generalised error wrapping error message pattern. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
		
							parent
							
								
									b14690aa77
								
							
						
					
					
						commit
						84ad9791e2
					
				| @ -225,7 +225,7 @@ func (direct) pivotRoot(newroot, putold string) (err error) { | ||||
| 	return syscall.PivotRoot(newroot, putold) | ||||
| } | ||||
| func (direct) mount(source, target, fstype string, flags uintptr, data string) (err error) { | ||||
| 	return syscall.Mount(source, target, fstype, flags, data) | ||||
| 	return mount(source, target, fstype, flags, data) | ||||
| } | ||||
| func (direct) unmount(target string, flags int) (err error) { | ||||
| 	return syscall.Unmount(target, flags) | ||||
|  | ||||
							
								
								
									
										60
									
								
								container/errors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								container/errors.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | ||||
| package container | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"os" | ||||
| 	"syscall" | ||||
| ) | ||||
| 
 | ||||
| type MountError struct { | ||||
| 	Source, Target, Fstype string | ||||
| 
 | ||||
| 	Flags uintptr | ||||
| 	Data  string | ||||
| 	syscall.Errno | ||||
| } | ||||
| 
 | ||||
| func (e *MountError) Unwrap() error { | ||||
| 	if e.Errno == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return e.Errno | ||||
| } | ||||
| 
 | ||||
| func (e *MountError) Error() string { | ||||
| 	if e.Flags&syscall.MS_BIND != 0 { | ||||
| 		if e.Flags&syscall.MS_REMOUNT != 0 { | ||||
| 			return "remount " + e.Target + ": " + e.Errno.Error() | ||||
| 		} | ||||
| 		return "bind " + e.Source + " on " + e.Target + ": " + e.Errno.Error() | ||||
| 	} | ||||
| 
 | ||||
| 	if e.Fstype != FstypeNULL { | ||||
| 		return "mount " + e.Fstype + " on " + e.Target + ": " + e.Errno.Error() | ||||
| 	} | ||||
| 
 | ||||
| 	// fallback case: if this is reached, the conditions for it to occur should be handled above | ||||
| 	return "mount " + e.Target + ": " + e.Errno.Error() | ||||
| } | ||||
| 
 | ||||
| // errnoFallback returns the concrete errno from an error, or a [os.PathError] fallback. | ||||
| func errnoFallback(op, path string, err error) (syscall.Errno, *os.PathError) { | ||||
| 	var errno syscall.Errno | ||||
| 	if !errors.As(err, &errno) { | ||||
| 		return 0, &os.PathError{Op: op, Path: path, Err: err} | ||||
| 	} | ||||
| 	return errno, nil | ||||
| } | ||||
| 
 | ||||
| // mount wraps syscall.Mount for error handling. | ||||
| func mount(source, target, fstype string, flags uintptr, data string) error { | ||||
| 	err := syscall.Mount(source, target, fstype, flags, data) | ||||
| 	if err == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if errno, pathError := errnoFallback("mount", target, err); pathError != nil { | ||||
| 		return pathError | ||||
| 	} else { | ||||
| 		return &MountError{source, target, fstype, flags, data, errno} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										109
									
								
								container/errors_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								container/errors_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,109 @@ | ||||
| package container | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"os" | ||||
| 	"reflect" | ||||
| 	"syscall" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| func TestMountError(t *testing.T) { | ||||
| 	testCases := []struct { | ||||
| 		name  string | ||||
| 		err   error | ||||
| 		errno syscall.Errno | ||||
| 		want  string | ||||
| 	}{ | ||||
| 		{"bind", &MountError{ | ||||
| 			Source: "/host/nix/store", | ||||
| 			Target: "/sysroot/nix/store", | ||||
| 			Fstype: FstypeNULL, | ||||
| 			Flags:  syscall.MS_SILENT | syscall.MS_BIND | syscall.MS_REC, | ||||
| 			Data:   zeroString, | ||||
| 			Errno:  syscall.ENOSYS, | ||||
| 		}, syscall.ENOSYS, | ||||
| 			"bind /host/nix/store on /sysroot/nix/store: function not implemented"}, | ||||
| 
 | ||||
| 		{"remount", &MountError{ | ||||
| 			Source: SourceNone, | ||||
| 			Target: "/sysroot/nix/store", | ||||
| 			Fstype: FstypeNULL, | ||||
| 			Flags:  syscall.MS_SILENT | syscall.MS_BIND | syscall.MS_REMOUNT, | ||||
| 			Data:   zeroString, | ||||
| 			Errno:  syscall.EPERM, | ||||
| 		}, syscall.EPERM, | ||||
| 			"remount /sysroot/nix/store: operation not permitted"}, | ||||
| 
 | ||||
| 		{"overlay", &MountError{ | ||||
| 			Source: SourceOverlay, | ||||
| 			Target: sysrootPath, | ||||
| 			Fstype: FstypeOverlay, | ||||
| 			Data:   `lowerdir=/host/var/lib/planterette/base/debian\:f92c9052`, | ||||
| 			Errno:  syscall.EINVAL, | ||||
| 		}, syscall.EINVAL, | ||||
| 			"mount overlay on /sysroot: invalid argument"}, | ||||
| 
 | ||||
| 		{"fallback", &MountError{ | ||||
| 			Source: SourceNone, | ||||
| 			Target: sysrootPath, | ||||
| 			Fstype: FstypeNULL, | ||||
| 			Errno:  syscall.ENOTRECOVERABLE, | ||||
| 		}, syscall.ENOTRECOVERABLE, | ||||
| 			"mount /sysroot: state not recoverable"}, | ||||
| 	} | ||||
| 	for _, tc := range testCases { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| 			t.Run("is", func(t *testing.T) { | ||||
| 				if !errors.Is(tc.err, tc.errno) { | ||||
| 					t.Errorf("Is: %#v is not %v", tc.err, tc.errno) | ||||
| 				} | ||||
| 			}) | ||||
| 			t.Run("error", func(t *testing.T) { | ||||
| 				if got := tc.err.Error(); got != tc.want { | ||||
| 					t.Errorf("Error: %q, want %q", got, tc.want) | ||||
| 				} | ||||
| 			}) | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	t.Run("zero", func(t *testing.T) { | ||||
| 		if errors.Is(new(MountError), syscall.Errno(0)) { | ||||
| 			t.Errorf("Is: zero MountError unexpected true") | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func TestErrnoFallback(t *testing.T) { | ||||
| 	testCases := []struct { | ||||
| 		name      string | ||||
| 		err       error | ||||
| 		wantErrno syscall.Errno | ||||
| 		wantPath  *os.PathError | ||||
| 	}{ | ||||
| 		{"mount", &MountError{ | ||||
| 			Errno: syscall.ENOTRECOVERABLE, | ||||
| 		}, syscall.ENOTRECOVERABLE, nil}, | ||||
| 
 | ||||
| 		{"path errno", &os.PathError{ | ||||
| 			Err: syscall.ETIMEDOUT, | ||||
| 		}, syscall.ETIMEDOUT, nil}, | ||||
| 
 | ||||
| 		{"fallback", errUnique, 0, &os.PathError{ | ||||
| 			Op:   "fallback", | ||||
| 			Path: "/proc/nonexistent", | ||||
| 			Err:  errUnique, | ||||
| 		}}, | ||||
| 	} | ||||
| 	for _, tc := range testCases { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| 			errno, err := errnoFallback(tc.name, Nonexistent, tc.err) | ||||
| 			if errno != tc.wantErrno { | ||||
| 				t.Errorf("errnoFallback: errno = %v, want %v", errno, tc.wantErrno) | ||||
| 			} | ||||
| 			if !reflect.DeepEqual(err, tc.wantPath) { | ||||
| 				t.Errorf("errnoFallback: pathError = %#v, want %#v", err, tc.wantPath) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user