system/link: use syscall dispatcher
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Test / Create distribution (push) Successful in 33s
				
			
		
			
				
	
				Test / Sandbox (push) Successful in 2m5s
				
			
		
			
				
	
				Test / Hakurei (push) Successful in 3m4s
				
			
		
			
				
	
				Test / Hpkg (push) Successful in 3m45s
				
			
		
			
				
	
				Test / Sandbox (race detector) (push) Successful in 4m26s
				
			
		
			
				
	
				Test / Hakurei (race detector) (push) Successful in 5m6s
				
			
		
			
				
	
				Test / Flake checks (push) Successful in 1m49s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 33s
				
			Test / Sandbox (push) Successful in 2m5s
				
			Test / Hakurei (push) Successful in 3m4s
				
			Test / Hpkg (push) Successful in 3m45s
				
			Test / Sandbox (race detector) (push) Successful in 4m26s
				
			Test / Hakurei (race detector) (push) Successful in 5m6s
				
			Test / Flake checks (push) Successful in 1m49s
				
			This enables hardlink op methods to be instrumented. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
		
							parent
							
								
									fcd0f2ede7
								
							
						
					
					
						commit
						6cc2b406a4
					
				| @ -54,7 +54,7 @@ func TestACLUpdateOp(t *testing.T) { | |||||||
| 			}, nil}, | 			}, nil}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	checkOpsBuilder(t, "UpdatePerm", []opsBuilderTestCase{ | 	checkOpsBuilder(t, "UpdatePermType", []opsBuilderTestCase{ | ||||||
| 		{"simple", | 		{"simple", | ||||||
| 			0xdeadbeef, | 			0xdeadbeef, | ||||||
| 			func(_ *testing.T, sys *I) { | 			func(_ *testing.T, sys *I) { | ||||||
| @ -65,8 +65,7 @@ func TestACLUpdateOp(t *testing.T) { | |||||||
| 				&aclUpdateOp{Process, "/run/user/1971/hakurei", []acl.Perm{acl.Execute}}, | 				&aclUpdateOp{Process, "/run/user/1971/hakurei", []acl.Perm{acl.Execute}}, | ||||||
| 				&aclUpdateOp{Process, "/tmp/hakurei.0/tmpdir/150", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, | 				&aclUpdateOp{Process, "/tmp/hakurei.0/tmpdir/150", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, | ||||||
| 			}, stub.Expect{}}, | 			}, stub.Expect{}}, | ||||||
| 	}) | 
 | ||||||
| 	checkOpsBuilder(t, "UpdatePermType", []opsBuilderTestCase{ |  | ||||||
| 		{"tmpdirp", 0xdeadbeef, func(_ *testing.T, sys *I) { | 		{"tmpdirp", 0xdeadbeef, func(_ *testing.T, sys *I) { | ||||||
| 			sys.UpdatePermType(User, "/tmp/hakurei.0/tmpdir", acl.Execute) | 			sys.UpdatePermType(User, "/tmp/hakurei.0/tmpdir", acl.Execute) | ||||||
| 		}, []Op{ | 		}, []Op{ | ||||||
|  | |||||||
| @ -1,6 +1,8 @@ | |||||||
| package system | package system | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"os" | ||||||
|  | 
 | ||||||
| 	"hakurei.app/system/acl" | 	"hakurei.app/system/acl" | ||||||
| 	"hakurei.app/system/dbus" | 	"hakurei.app/system/dbus" | ||||||
| ) | ) | ||||||
| @ -13,6 +15,11 @@ type syscallDispatcher interface { | |||||||
| 	// just synchronising access is not enough, as this is for test instrumentation. | 	// just synchronising access is not enough, as this is for test instrumentation. | ||||||
| 	new(f func(k syscallDispatcher)) | 	new(f func(k syscallDispatcher)) | ||||||
| 
 | 
 | ||||||
|  | 	// link provides os.Link. | ||||||
|  | 	link(oldname, newname string) error | ||||||
|  | 	// remove provides os.Remove. | ||||||
|  | 	remove(name string) error | ||||||
|  | 
 | ||||||
| 	// aclUpdate provides [acl.Update]. | 	// aclUpdate provides [acl.Update]. | ||||||
| 	aclUpdate(name string, uid int, perms ...acl.Perm) error | 	aclUpdate(name string, uid int, perms ...acl.Perm) error | ||||||
| 
 | 
 | ||||||
| @ -37,6 +44,9 @@ type direct struct{} | |||||||
| 
 | 
 | ||||||
| func (k direct) new(f func(k syscallDispatcher)) { go f(k) } | func (k direct) new(f func(k syscallDispatcher)) { go f(k) } | ||||||
| 
 | 
 | ||||||
|  | func (k direct) link(oldname, newname string) error { return os.Link(oldname, newname) } | ||||||
|  | func (k direct) remove(name string) error           { return os.Remove(name) } | ||||||
|  | 
 | ||||||
| func (k direct) aclUpdate(name string, uid int, perms ...acl.Perm) error { | func (k direct) aclUpdate(name string, uid int, perms ...acl.Perm) error { | ||||||
| 	return acl.Update(name, uid, perms...) | 	return acl.Update(name, uid, perms...) | ||||||
| } | } | ||||||
|  | |||||||
| @ -192,6 +192,19 @@ type kstub struct{ *stub.Stub[syscallDispatcher] } | |||||||
| 
 | 
 | ||||||
| func (k *kstub) new(f func(k syscallDispatcher)) { k.Helper(); k.New(f) } | func (k *kstub) new(f func(k syscallDispatcher)) { k.Helper(); k.New(f) } | ||||||
| 
 | 
 | ||||||
|  | func (k *kstub) link(oldname, newname string) error { | ||||||
|  | 	k.Helper() | ||||||
|  | 	return k.Expects("link").Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "oldname", oldname, 0), | ||||||
|  | 		stub.CheckArg(k.Stub, "newname", newname, 1)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (k *kstub) remove(name string) error { | ||||||
|  | 	k.Helper() | ||||||
|  | 	return k.Expects("remove").Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "name", name, 0)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (k *kstub) aclUpdate(name string, uid int, perms ...acl.Perm) error { | func (k *kstub) aclUpdate(name string, uid int, perms ...acl.Perm) error { | ||||||
| 	k.Helper() | 	k.Helper() | ||||||
| 	return k.Expects("aclUpdate").Error( | 	return k.Expects("aclUpdate").Error( | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ package system | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Link calls LinkFileType with the [Process] criteria. | // Link calls LinkFileType with the [Process] criteria. | ||||||
| @ -22,17 +21,17 @@ type hardlinkOp struct { | |||||||
| 
 | 
 | ||||||
| func (l *hardlinkOp) Type() Enablement { return l.et } | func (l *hardlinkOp) Type() Enablement { return l.et } | ||||||
| 
 | 
 | ||||||
| func (l *hardlinkOp) apply(*I) error { | func (l *hardlinkOp) apply(sys *I) error { | ||||||
| 	msg.Verbose("linking", l) | 	sys.verbose("linking", l) | ||||||
| 	return newOpError("hardlink", os.Link(l.src, l.dst), false) | 	return newOpError("hardlink", sys.link(l.src, l.dst), false) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l *hardlinkOp) revert(_ *I, ec *Criteria) error { | func (l *hardlinkOp) revert(sys *I, ec *Criteria) error { | ||||||
| 	if ec.hasType(l.Type()) { | 	if ec.hasType(l.Type()) { | ||||||
| 		msg.Verbosef("removing hard link %q", l.dst) | 		sys.verbosef("removing hard link %q", l.dst) | ||||||
| 		return newOpError("hardlink", os.Remove(l.dst), true) | 		return newOpError("hardlink", sys.remove(l.dst), true) | ||||||
| 	} else { | 	} else { | ||||||
| 		msg.Verbosef("skipping hard link %q", l.dst) | 		sys.verbosef("skipping hard link %q", l.dst) | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										84
									
								
								system/link_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								system/link_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | |||||||
|  | package system | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestHardlinkOp(t *testing.T) { | ||||||
|  | 	checkOpBehaviour(t, []opBehaviourTestCase{ | ||||||
|  | 		{"link", 0xdeadbeef, 0xff, &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{ | ||||||
|  | 			call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil), | ||||||
|  | 			call("link", stub.ExpectArgs{"/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}, nil, stub.UniqueError(1)), | ||||||
|  | 		}, &OpError{Op: "hardlink", Err: stub.UniqueError(1)}, nil, nil}, | ||||||
|  | 
 | ||||||
|  | 		{"remove", 0xdeadbeef, 0xff, &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{ | ||||||
|  | 			call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil), | ||||||
|  | 			call("link", stub.ExpectArgs{"/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}, nil, nil), | ||||||
|  | 		}, nil, []stub.Call{ | ||||||
|  | 			call("verbosef", stub.ExpectArgs{"removing hard link %q", []any{"/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}}, nil, nil), | ||||||
|  | 			call("remove", stub.ExpectArgs{"/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}, nil, stub.UniqueError(0)), | ||||||
|  | 		}, &OpError{Op: "hardlink", Err: stub.UniqueError(0), Revert: true}}, | ||||||
|  | 
 | ||||||
|  | 		{"success skip", 0xdeadbeef, EWayland | EX11, &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{ | ||||||
|  | 			call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil), | ||||||
|  | 			call("link", stub.ExpectArgs{"/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}, nil, nil), | ||||||
|  | 		}, nil, []stub.Call{ | ||||||
|  | 			call("verbosef", stub.ExpectArgs{"skipping hard link %q", []any{"/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}}, nil, nil), | ||||||
|  | 		}, nil}, | ||||||
|  | 
 | ||||||
|  | 		{"success", 0xdeadbeef, 0xff, &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{ | ||||||
|  | 			call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil), | ||||||
|  | 			call("link", stub.ExpectArgs{"/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}, nil, nil), | ||||||
|  | 		}, nil, []stub.Call{ | ||||||
|  | 			call("verbosef", stub.ExpectArgs{"removing hard link %q", []any{"/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}}, nil, nil), | ||||||
|  | 			call("remove", stub.ExpectArgs{"/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}, nil, nil), | ||||||
|  | 		}, nil}, | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	checkOpsBuilder(t, "LinkFileType", []opsBuilderTestCase{ | ||||||
|  | 		{"type", 0xcafebabe, func(_ *testing.T, sys *I) { | ||||||
|  | 			sys.LinkFileType(User, "/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse") | ||||||
|  | 		}, []Op{ | ||||||
|  | 			&hardlinkOp{User, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, | ||||||
|  | 		}, stub.Expect{}}, | ||||||
|  | 
 | ||||||
|  | 		{"link", 0xcafebabe, func(_ *testing.T, sys *I) { | ||||||
|  | 			sys.Link("/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse") | ||||||
|  | 		}, []Op{ | ||||||
|  | 			&hardlinkOp{Process, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, | ||||||
|  | 		}, stub.Expect{}}, | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	checkOpIs(t, []opIsTestCase{ | ||||||
|  | 		{"nil", (*hardlinkOp)(nil), (*hardlinkOp)(nil), false}, | ||||||
|  | 		{"zero", new(hardlinkOp), new(hardlinkOp), true}, | ||||||
|  | 
 | ||||||
|  | 		{"src differs", | ||||||
|  | 			&hardlinkOp{Process, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse"}, | ||||||
|  | 			&hardlinkOp{Process, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, | ||||||
|  | 			false}, | ||||||
|  | 
 | ||||||
|  | 		{"dst differs", | ||||||
|  | 			&hardlinkOp{Process, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6", "/run/user/1000/pulse/native"}, | ||||||
|  | 			&hardlinkOp{Process, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, | ||||||
|  | 			false}, | ||||||
|  | 
 | ||||||
|  | 		{"et differs", | ||||||
|  | 			&hardlinkOp{User, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, | ||||||
|  | 			&hardlinkOp{Process, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, | ||||||
|  | 			false}, | ||||||
|  | 
 | ||||||
|  | 		{"equals", | ||||||
|  | 			&hardlinkOp{Process, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, | ||||||
|  | 			&hardlinkOp{Process, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, | ||||||
|  | 			true}, | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	checkOpMeta(t, []opMetaTestCase{ | ||||||
|  | 		{"link", &hardlinkOp{Process, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, | ||||||
|  | 			Process, "/run/user/1000/pulse/native", | ||||||
|  | 			`"/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse" from "/run/user/1000/pulse/native"`}, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
| @ -24,43 +24,6 @@ func TestCopyFile(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestLink(t *testing.T) { |  | ||||||
| 	testCases := []struct { |  | ||||||
| 		dst, src string |  | ||||||
| 	}{ |  | ||||||
| 		{"/tmp/hakurei.1971/f587afe9fce3c8e1ad5b64deb6c41ad5/pulse-cookie", "/home/ophestra/xdg/config/pulse/cookie"}, |  | ||||||
| 		{"/tmp/hakurei.1971/62154f708b5184ab01f9dcc2bbe7a33b/pulse-cookie", "/home/ophestra/xdg/config/pulse/cookie"}, |  | ||||||
| 	} |  | ||||||
| 	for _, tc := range testCases { |  | ||||||
| 		t.Run("link file "+tc.dst+" from "+tc.src, func(t *testing.T) { |  | ||||||
| 			sys := New(t.Context(), 150) |  | ||||||
| 			sys.Link(tc.src, tc.dst) |  | ||||||
| 			(&tcOp{Process, tc.src}).test(t, sys.ops, []Op{ |  | ||||||
| 				&hardlinkOp{Process, tc.dst, tc.src}, |  | ||||||
| 			}, "Link") |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestLinkFileType(t *testing.T) { |  | ||||||
| 	testCases := []struct { |  | ||||||
| 		tcOp |  | ||||||
| 		dst string |  | ||||||
| 	}{ |  | ||||||
| 		{tcOp{User, "/tmp/hakurei.1971/f587afe9fce3c8e1ad5b64deb6c41ad5/pulse-cookie"}, "/home/ophestra/xdg/config/pulse/cookie"}, |  | ||||||
| 		{tcOp{Process, "/tmp/hakurei.1971/62154f708b5184ab01f9dcc2bbe7a33b/pulse-cookie"}, "/home/ophestra/xdg/config/pulse/cookie"}, |  | ||||||
| 	} |  | ||||||
| 	for _, tc := range testCases { |  | ||||||
| 		t.Run("link file "+tc.dst+" from "+tc.path+" with type "+TypeString(tc.et), func(t *testing.T) { |  | ||||||
| 			sys := New(t.Context(), 150) |  | ||||||
| 			sys.LinkFileType(tc.et, tc.path, tc.dst) |  | ||||||
| 			tc.test(t, sys.ops, []Op{ |  | ||||||
| 				&hardlinkOp{tc.et, tc.dst, tc.path}, |  | ||||||
| 			}, "LinkFileType") |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestTmpfile_String(t *testing.T) { | func TestTmpfile_String(t *testing.T) { | ||||||
| 	testCases := []struct { | 	testCases := []struct { | ||||||
| 		src  string | 		src  string | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user