1- //! Common utilities used by multiple test
1+ //! Common utilities used by multiple tests
22
33use anyhow:: Context ;
44use std:: ffi:: CString ;
5- use std:: fs;
65use std:: fs:: create_dir;
76use std:: os:: unix:: ffi:: OsStrExt ;
87use std:: path:: Path ;
@@ -11,125 +10,57 @@ use std::ptr::null;
1110use crate :: { krun_call, TestSetup } ;
1211use krun_sys:: * ;
1312
14- use nix:: unistd:: { chroot, chdir} ;
15- use std:: path:: PathBuf ;
16-
1713fn copy_guest_agent ( dir : & Path ) -> anyhow:: Result < ( ) > {
1814 let path = std:: env:: var_os ( "KRUN_TEST_GUEST_AGENT_PATH" )
1915 . context ( "KRUN_TEST_GUEST_AGENT_PATH env variable not set" ) ?;
2016
2117 let output_path = dir. join ( "guest-agent" ) ;
22- fs:: copy ( path, output_path) . context ( "Failed to copy executable into vm" ) ?;
18+ std :: fs:: copy ( path, output_path) . context ( "Failed to copy executable into vm" ) ?;
2319 Ok ( ( ) )
2420}
2521
26- /// Common part of most test. This setups an empty root filesystem, copies the guest agent there
27- /// and runs the guest agent in the VM.
28- /// Note that some tests might want to use a different root file system (perhaps a qcow image),
29- /// in which case the test can implement the equivalent functionality itself, or better if there
30- /// are more test doing that, add another utility method in this file.
22+ /// Common setup for most tests. Sets up the root filesystem and runs the guest agent in the VM.
3123///
32- /// The returned object is used for deleting the temporary files.
33- pub fn setup_fs_and_enter ( ctx : u32 , test_setup : TestSetup ) -> anyhow:: Result < ( ) > {
34- let root_dir = test_setup. tmp_dir . join ( "root" ) ;
35- create_dir ( & root_dir) . context ( "Failed to create root directory" ) ?;
36-
37- let path_str = CString :: new ( root_dir. as_os_str ( ) . as_bytes ( ) ) . context ( "CString::new" ) ?;
38- copy_guest_agent ( & root_dir) ?;
39- unsafe {
40- krun_call ! ( krun_set_root( ctx, path_str. as_ptr( ) ) ) ?;
41- krun_call ! ( krun_set_workdir( ctx, c"/" . as_ptr( ) ) ) ?;
42- let test_case_cstr = CString :: new ( test_setup. test_case ) . context ( "CString::new" ) ?;
43- let argv = [ test_case_cstr. as_ptr ( ) , null ( ) ] ;
44- //let envp = [c"RUST_BACKTRACE=1".as_ptr(), null()];
45- let envp = [ null ( ) ] ;
46- krun_call ! ( krun_set_exec(
47- ctx,
48- c"/guest-agent" . as_ptr( ) ,
49- argv. as_ptr( ) ,
50- envp. as_ptr( ) ,
51- ) ) ?;
52- krun_call ! ( krun_start_enter( ctx) ) ?;
53- }
54- unreachable ! ( )
55- }
56-
57- /// Like setup_fs_and_enter, but changes the host process's root to the guest's root
58- /// before entering the VM. This is needed for Unix domain socket TSI tests where the
59- /// host process needs to access socket paths in the guest filesystem.
24+ /// If `requires_namespace` is true, the runner has already created the root directory structure
25+ /// with /dev, /tmp, /sys, guest-agent. After krun_create_ctx loads libraries, we chroot there.
6026///
61- /// This function:
62- /// 1. Creates a new user namespace and mount namespace (unshare CLONE_NEWUSER | CLONE_NEWNS)
63- /// 2. Sets up uid/gid mappings to become root in the namespace
64- /// 3. Changes root to the guest's root directory (chroot)
65- /// 4. Then calls krun_start_enter
66- ///
67- /// The before_enter callback is called after chroot but before krun_start_enter, allowing
68- /// setup of host-side resources (like Unix domain socket servers) that need to be accessible
69- /// at the same paths as the guest will use.
70- ///
71- /// Note: This uses rootless namespaces (user namespaces) so it doesn't require root.
72- pub fn setup_fs_and_enter_with_namespace < F > (
73- ctx : u32 ,
74- test_setup : TestSetup ,
75- before_enter : F ,
76- ) -> anyhow:: Result < ( ) >
77- where
78- F : FnOnce ( ) -> anyhow:: Result < ( ) > ,
79- {
80- let root_dir = test_setup. tmp_dir . join ( "root" ) ;
81- create_dir ( & root_dir) . context ( "Failed to create root directory" ) ?;
82-
83- // Create necessary directories in the guest root
84- create_dir ( root_dir. join ( "tmp" ) ) . context ( "Failed to create tmp directory" ) ?;
85- create_dir ( root_dir. join ( "dev" ) ) . context ( "Failed to create dev directory" ) ?;
86- create_dir ( root_dir. join ( "proc" ) ) . context ( "Failed to create proc directory" ) ?;
87- create_dir ( root_dir. join ( "sys" ) ) . context ( "Failed to create sys directory" ) ?;
88-
89- copy_guest_agent ( & root_dir) ?;
90-
91- // The runner has already set up the namespace for us (user+mount+pid)
92- // We are now root in the user namespace and PID 1 in the PID namespace
93- // Make our mounts private so they don't affect the parent namespace
94- use nix:: mount:: { mount, MsFlags } ;
95- mount (
96- None :: < & str > ,
97- "/" ,
98- None :: < & str > ,
99- MsFlags :: MS_REC | MsFlags :: MS_PRIVATE ,
100- None :: < & str > ,
101- ) . context ( "Failed to make / private" ) ?;
102-
103- // Bind mount /dev into the guest root so /dev/kvm is accessible
104- // (we're root in the namespace now)
105- mount (
106- Some ( "/dev" ) ,
107- root_dir. join ( "dev" ) . as_path ( ) ,
108- None :: < & str > ,
109- MsFlags :: MS_BIND | MsFlags :: MS_REC ,
110- None :: < & str > ,
111- ) . context ( "Failed to bind mount /dev" ) ?;
112-
113- // Now we can chroot
114- let root_path = PathBuf :: from ( & root_dir) ;
115- chroot ( & root_path) . context ( "Failed to chroot to guest root" ) ?;
116- chdir ( "/" ) . context ( "Failed to chdir to /" ) ?;
117-
118- // Mount procfs after chroot with standard proc mount flags
119- mount (
120- Some ( "proc" ) ,
121- "/proc" ,
122- Some ( "proc" ) ,
123- MsFlags :: MS_NOSUID | MsFlags :: MS_NODEV | MsFlags :: MS_NOEXEC ,
124- None :: < & str > ,
125- ) . context ( "Failed to mount procfs" ) ?;
126-
127- // Call the before_enter callback to set up host-side resources
128- before_enter ( ) . context ( "before_enter callback failed" ) ?;
27+ /// If `requires_namespace` is false, this function creates a root directory, copies the
28+ /// guest agent there, and sets it as the VM root.
29+ pub fn setup_fs_and_enter ( ctx : u32 , test_setup : TestSetup ) -> anyhow:: Result < ( ) > {
30+ let root_path = if test_setup. requires_namespace {
31+ // Runner set up the root dir structure, now we chroot after libraries are loaded
32+ use nix:: mount:: { mount, MsFlags } ;
33+ use nix:: unistd:: { chdir, chroot} ;
34+
35+ let root_dir = test_setup. tmp_dir . join ( "root" ) ;
36+
37+ // Chroot into the prepared root
38+ chroot ( & root_dir) . context ( "Failed to chroot" ) ?;
39+ chdir ( "/" ) . context ( "Failed to chdir to /" ) ?;
40+
41+ // Mount procfs after chroot
42+ mount (
43+ Some ( "proc" ) ,
44+ "/proc" ,
45+ Some ( "proc" ) ,
46+ MsFlags :: MS_NOSUID | MsFlags :: MS_NODEV | MsFlags :: MS_NOEXEC ,
47+ None :: < & str > ,
48+ )
49+ . context ( "Failed to mount procfs" ) ?;
50+
51+ CString :: new ( "/" ) . context ( "CString::new" ) ?
52+ } else {
53+ // Create root directory and copy guest agent
54+ let root_dir = test_setup. tmp_dir . join ( "root" ) ;
55+ create_dir ( & root_dir) . context ( "Failed to create root directory" ) ?;
56+ // Create /tmp for tests that use Unix sockets
57+ let _ = create_dir ( root_dir. join ( "tmp" ) ) ;
58+ copy_guest_agent ( & root_dir) ?;
59+ CString :: new ( root_dir. as_os_str ( ) . as_bytes ( ) ) . context ( "CString::new" ) ?
60+ } ;
12961
130- let path_str = CString :: new ( "/" ) . context ( "CString::new" ) ?;
13162 unsafe {
132- krun_call ! ( krun_set_root( ctx, path_str . as_ptr( ) ) ) ?;
63+ krun_call ! ( krun_set_root( ctx, root_path . as_ptr( ) ) ) ?;
13364 krun_call ! ( krun_set_workdir( ctx, c"/" . as_ptr( ) ) ) ?;
13465 let test_case_cstr = CString :: new ( test_setup. test_case ) . context ( "CString::new" ) ?;
13566 let argv = [ test_case_cstr. as_ptr ( ) , null ( ) ] ;
0 commit comments