11use std:: ffi:: { CString , OsStr } ;
2+ use std:: fs:: File ;
23use std:: io:: { self , Read } ;
34use std:: mem:: MaybeUninit ;
45use std:: os:: linux:: net:: SocketAddrExt ;
@@ -57,7 +58,7 @@ fn run_container_init(
5758 format ! ( "failed to connect abstract prepared init control socket: {error}" ) ,
5859 )
5960 } ) ?;
60- let ( plan, canonical_bundle, rootfs) =
61+ let ( plan, canonical_bundle, rootfs, rootfs_file ) =
6162 match prepare_container_init ( config_snapshot, bundle_directory) {
6263 Ok ( prepared) => prepared,
6364 Err ( error) => return reject_before_ready ( & mut control, error) ,
@@ -66,7 +67,7 @@ fn run_container_init(
6667 return reject_before_ready ( & mut control, error) ;
6768 }
6869 if plan. namespaces . requires_child_process ( ) {
69- return run_namespaced_init ( & plan, & canonical_bundle, & rootfs, control) ;
70+ return run_namespaced_init ( & plan, & canonical_bundle, & rootfs, & rootfs_file , control) ;
7071 }
7172 if let Err ( error) = prepare_create_environment ( & plan, & canonical_bundle, & rootfs) {
7273 return reject_before_ready ( & mut control, error) ;
@@ -75,10 +76,10 @@ fn run_container_init(
7576 // PID namespace that changes the runtime-visible process.
7677 let pid = unsafe { libc:: getpid ( ) } ;
7778 write_ready ( & mut control, pid) ?;
78- wait_for_start_and_exec ( & plan, & rootfs , control)
79+ wait_for_start_and_exec ( & plan, & rootfs_file , control)
7980}
8081
81- fn wait_for_start_and_exec ( plan : & InitPlan , rootfs : & Path , mut control : UnixStream ) -> Result < ( ) > {
82+ fn wait_for_start_and_exec ( plan : & InitPlan , rootfs : & File , mut control : UnixStream ) -> Result < ( ) > {
8283 let mut start = [ 0_u8 ; 1 ] ;
8384 control. read_exact ( & mut start) . map_err ( |error| {
8485 init_error (
@@ -100,6 +101,7 @@ fn run_namespaced_init(
100101 plan : & InitPlan ,
101102 bundle_directory : & Path ,
102103 rootfs : & Path ,
104+ rootfs_file : & File ,
103105 mut control : UnixStream ,
104106) -> Result < ( ) > {
105107 match pid:: fork_namespaced_init ( ) {
@@ -113,7 +115,7 @@ fn run_namespaced_init(
113115 return reject_before_ready ( & mut control, error) ;
114116 }
115117 write_ready ( & mut control, runtime_pid) ?;
116- wait_for_start_and_exec ( plan, rootfs , control)
118+ wait_for_start_and_exec ( plan, rootfs_file , control)
117119 }
118120 Err ( error) => reject_before_ready ( & mut control, error) ,
119121 }
@@ -133,7 +135,7 @@ fn reject_before_ready(control: &mut UnixStream, error: Error) -> Result<()> {
133135fn prepare_container_init (
134136 config_snapshot : PathBuf ,
135137 bundle_directory : PathBuf ,
136- ) -> Result < ( InitPlan , PathBuf , PathBuf ) > {
138+ ) -> Result < ( InitPlan , PathBuf , PathBuf , File ) > {
137139 let config_json = read_bounded_config ( & config_snapshot) ?;
138140 let bundle = OciBundle :: from_json ( bundle_directory, config_json) ?;
139141 let plan = InitPlan :: from_bundle ( & bundle, & null_io ( ) ) ?;
@@ -164,7 +166,34 @@ fn prepare_container_init(
164166 ) ,
165167 ) ) ;
166168 }
167- Ok ( ( plan, canonical_bundle, rootfs) )
169+ let rootfs_file = File :: open ( & rootfs) . map_err ( |error| {
170+ init_error (
171+ ErrorCode :: InvalidArgument ,
172+ format ! (
173+ "failed to retain the container rootfs {} before namespace entry: {error}" ,
174+ rootfs. display( )
175+ ) ,
176+ )
177+ } ) ?;
178+ if !rootfs_file
179+ . metadata ( )
180+ . map_err ( |error| {
181+ init_error (
182+ ErrorCode :: InvalidArgument ,
183+ format ! (
184+ "failed to inspect retained container rootfs {}: {error}" ,
185+ rootfs. display( )
186+ ) ,
187+ )
188+ } ) ?
189+ . is_dir ( )
190+ {
191+ return Err ( init_error (
192+ ErrorCode :: InvalidArgument ,
193+ format ! ( "container rootfs is not a directory: {}" , rootfs. display( ) ) ,
194+ ) ) ;
195+ }
196+ Ok ( ( plan, canonical_bundle, rootfs, rootfs_file) )
168197}
169198
170199fn prepare_create_environment (
@@ -173,10 +202,10 @@ fn prepare_create_environment(
173202 rootfs : & Path ,
174203) -> Result < ( ) > {
175204 if let Some ( hostname) = & plan. hostname {
176- if !plan. namespaces . new_uts ( ) {
205+ if !plan. namespaces . has_uts ( ) {
177206 return Err ( init_error (
178207 ErrorCode :: FailedPrecondition ,
179- "refusing to change hostname outside a new UTS namespace" ,
208+ "refusing to change hostname outside a configured UTS namespace" ,
180209 ) ) ;
181210 }
182211 // SAFETY: the byte slice remains live for the call and its exact
@@ -186,10 +215,10 @@ fn prepare_create_environment(
186215 }
187216 }
188217 if let Some ( domainname) = & plan. domainname {
189- if !plan. namespaces . new_uts ( ) {
218+ if !plan. namespaces . has_uts ( ) {
190219 return Err ( init_error (
191220 ErrorCode :: FailedPrecondition ,
192- "refusing to change domainname outside a new UTS namespace" ,
221+ "refusing to change domainname outside a configured UTS namespace" ,
193222 ) ) ;
194223 }
195224 // SAFETY: the byte slice remains live for the call and its exact
@@ -299,7 +328,7 @@ fn read_bounded_config(path: &Path) -> Result<String> {
299328 } )
300329}
301330
302- fn enter_rootfs_and_exec ( plan : & InitPlan , rootfs : & Path ) -> Result < ( ) > {
331+ fn enter_rootfs_and_exec ( plan : & InitPlan , rootfs : & File ) -> Result < ( ) > {
303332 let cwd = CString :: new ( plan. cwd . as_bytes ( ) ) . map_err ( |error| {
304333 init_error (
305334 ErrorCode :: InvalidArgument ,
0 commit comments