@@ -120,16 +120,7 @@ impl Env {
120
120
let exec_file = arguments
121
121
. single_value ( "exec-file" )
122
122
. ok_or_else ( || Error :: ArgumentParsing ( MissingValue ( "exec-file" . to_string ( ) ) ) ) ?;
123
- let exec_file_path = canonicalize ( & exec_file)
124
- . map_err ( |err| Error :: Canonicalize ( PathBuf :: from ( & exec_file) , err) ) ?;
125
-
126
- if !exec_file_path. is_file ( ) {
127
- return Err ( Error :: NotAFile ( exec_file_path) ) ;
128
- }
129
-
130
- let exec_file_name = exec_file_path
131
- . file_name ( )
132
- . ok_or_else ( || Error :: FileName ( exec_file_path. clone ( ) ) ) ?;
123
+ let ( exec_file_path, exec_file_name) = Env :: validate_exec_file ( exec_file) ?;
133
124
134
125
let chroot_base = arguments
135
126
. single_value ( "chroot-base-dir" )
@@ -169,7 +160,7 @@ impl Env {
169
160
let mut cgroups: Vec < Box < dyn Cgroup > > = Vec :: new ( ) ;
170
161
let parent_cgroup = match arguments. single_value ( "parent-cgroup" ) {
171
162
Some ( parent_cg) => Path :: new ( parent_cg) ,
172
- None => Path :: new ( exec_file_name) ,
163
+ None => Path :: new ( & exec_file_name) ,
173
164
} ;
174
165
if parent_cgroup
175
166
. components ( )
@@ -247,6 +238,29 @@ impl Env {
247
238
self . uid
248
239
}
249
240
241
+ fn validate_exec_file ( exec_file : & str ) -> Result < ( PathBuf , String ) > {
242
+ let exec_file_path = canonicalize ( exec_file)
243
+ . map_err ( |err| Error :: Canonicalize ( PathBuf :: from ( exec_file) , err) ) ?;
244
+
245
+ if !exec_file_path. is_file ( ) {
246
+ return Err ( Error :: NotAFile ( exec_file_path) ) ;
247
+ }
248
+
249
+ let exec_file_name = exec_file_path
250
+ . file_name ( )
251
+ . ok_or_else ( || Error :: ExtractFileName ( exec_file_path. clone ( ) ) ) ?
252
+ . to_str ( )
253
+ // Safe to unwrap as the original `exec_file` is `String`.
254
+ . unwrap ( )
255
+ . to_string ( ) ;
256
+
257
+ if !exec_file_name. contains ( "firecracker" ) {
258
+ return Err ( Error :: ExecFileName ( exec_file_name) ) ;
259
+ }
260
+
261
+ Ok ( ( exec_file_path, exec_file_name) )
262
+ }
263
+
250
264
fn parse_resource_limits ( resource_limits : & mut ResourceLimits , args : & [ String ] ) -> Result < ( ) > {
251
265
for arg in args {
252
266
let ( name, value) = arg
@@ -293,7 +307,7 @@ impl Env {
293
307
fn save_exec_file_pid ( & mut self , pid : i32 , chroot_exec_file : PathBuf ) -> Result < ( ) > {
294
308
let chroot_exec_file_str = chroot_exec_file
295
309
. to_str ( )
296
- . ok_or_else ( || Error :: FileName ( chroot_exec_file. clone ( ) ) ) ?;
310
+ . ok_or_else ( || Error :: ExtractFileName ( chroot_exec_file. clone ( ) ) ) ?;
297
311
let pid_file_path =
298
312
PathBuf :: from ( format ! ( "{}{}" , chroot_exec_file_str, PID_FILE_EXTENSION ) ) ;
299
313
let mut pid_file = OpenOptions :: new ( )
@@ -363,7 +377,7 @@ impl Env {
363
377
let exec_file_name = self
364
378
. exec_file_path
365
379
. file_name ( )
366
- . ok_or_else ( || Error :: FileName ( self . exec_file_path . clone ( ) ) ) ?;
380
+ . ok_or_else ( || Error :: ExtractFileName ( self . exec_file_path . clone ( ) ) ) ?;
367
381
// We do a quick push here to get the global path of the executable inside the chroot,
368
382
// without having to create a new PathBuf. We'll then do a pop to revert to the actual
369
383
// chroot_dir right after the copy.
@@ -620,6 +634,8 @@ mod tests {
620
634
use crate :: build_arg_parser;
621
635
use crate :: cgroup:: test_util:: MockCgroupFs ;
622
636
637
+ const PSEUDO_EXEC_FILE_PATH : & str = "/tmp/pseudo_firecracker_exec_file" ;
638
+
623
639
#[ derive( Clone ) ]
624
640
struct ArgVals < ' a > {
625
641
pub id : & ' a str ,
@@ -637,9 +653,10 @@ mod tests {
637
653
638
654
impl ArgVals < ' _ > {
639
655
pub fn new ( ) -> ArgVals < ' static > {
656
+ File :: create ( PSEUDO_EXEC_FILE_PATH ) . unwrap ( ) ;
640
657
ArgVals {
641
658
id : "bd65600d-8669-4903-8a14-af88203add38" ,
642
- exec_file : "/proc/cpuinfo" ,
659
+ exec_file : PSEUDO_EXEC_FILE_PATH ,
643
660
uid : "1001" ,
644
661
gid : "1002" ,
645
662
chroot_base : "/" ,
@@ -876,6 +893,49 @@ mod tests {
876
893
}
877
894
}
878
895
896
+ #[ test]
897
+ fn test_validate_exec_file ( ) {
898
+ // Success case
899
+ File :: create ( PSEUDO_EXEC_FILE_PATH ) . unwrap ( ) ;
900
+ assert ! ( Env :: validate_exec_file( PSEUDO_EXEC_FILE_PATH ) . is_ok( ) ) ;
901
+
902
+ // Error case 1: No such file exists
903
+ std:: fs:: remove_file ( PSEUDO_EXEC_FILE_PATH ) . unwrap ( ) ;
904
+ assert_eq ! (
905
+ format!(
906
+ "{}" ,
907
+ Env :: validate_exec_file( PSEUDO_EXEC_FILE_PATH ) . unwrap_err( )
908
+ ) ,
909
+ format!(
910
+ "Failed to canonicalize path {}: No such file or directory (os error 2)" ,
911
+ PSEUDO_EXEC_FILE_PATH
912
+ )
913
+ ) ;
914
+
915
+ // Error case 2: Not a file
916
+ std:: fs:: create_dir_all ( "/tmp/firecracker_test_dir" ) . unwrap ( ) ;
917
+ assert_eq ! (
918
+ format!(
919
+ "{}" ,
920
+ Env :: validate_exec_file( "/tmp/firecracker_test_dir" ) . unwrap_err( )
921
+ ) ,
922
+ "/tmp/firecracker_test_dir is not a file"
923
+ ) ;
924
+
925
+ // Error case 3: Filename without "firecracker"
926
+ File :: create ( "/tmp/firecracker_test_dir/foobarbaz" ) . unwrap ( ) ;
927
+ assert_eq ! (
928
+ format!(
929
+ "{}" ,
930
+ Env :: validate_exec_file( "/tmp/firecracker_test_dir/foobarbaz" ) . unwrap_err( )
931
+ ) ,
932
+ "Invalid filename. The filename of `--exec-file` option must contain \" firecracker\" : \
933
+ foobarbaz"
934
+ ) ;
935
+ std:: fs:: remove_file ( "/tmp/firecracker_test_dir/foobarbaz" ) . unwrap ( ) ;
936
+ std:: fs:: remove_dir_all ( "/tmp/firecracker_test_dir" ) . unwrap ( ) ;
937
+ }
938
+
879
939
#[ test]
880
940
fn test_setup_jailed_folder ( ) {
881
941
let mut mock_cgroups = MockCgroupFs :: new ( ) . unwrap ( ) ;
@@ -986,15 +1046,15 @@ mod tests {
986
1046
assert ! ( !mock_cgroups. add_v1_mounts( ) . is_err( ) ) ;
987
1047
988
1048
// Create tmp resources for `exec_file` and `chroot_base`.
989
- let some_file = TempFile :: new_with_prefix ( "/tmp/" ) . unwrap ( ) ;
990
- let some_file_path = some_file . as_path ( ) . to_str ( ) . unwrap ( ) ;
991
- let some_file_name = some_file . as_path ( ) . file_name ( ) . unwrap ( ) ;
1049
+ File :: create ( PSEUDO_EXEC_FILE_PATH ) . unwrap ( ) ;
1050
+ let exec_file_path = PSEUDO_EXEC_FILE_PATH ;
1051
+ let exec_file_name = Path :: new ( exec_file_path ) . file_name ( ) . unwrap ( ) ;
992
1052
let some_dir = TempDir :: new ( ) . unwrap ( ) ;
993
1053
let some_dir_path = some_dir. as_path ( ) . to_str ( ) . unwrap ( ) ;
994
1054
995
1055
let some_arg_vals = ArgVals {
996
1056
id : "bd65600d-8669-4903-8a14-af88203add38" ,
997
- exec_file : some_file_path ,
1057
+ exec_file : exec_file_path ,
998
1058
uid : "1001" ,
999
1059
gid : "1002" ,
1000
1060
chroot_base : some_dir_path,
@@ -1005,7 +1065,7 @@ mod tests {
1005
1065
resource_limits : Vec :: new ( ) ,
1006
1066
parent_cgroup : None ,
1007
1067
} ;
1008
- fs:: write ( some_file_path , "some_content" ) . unwrap ( ) ;
1068
+ fs:: write ( exec_file_path , "some_content" ) . unwrap ( ) ;
1009
1069
args. parse ( & make_args ( & some_arg_vals) ) . unwrap ( ) ;
1010
1070
let mut env = Env :: new ( & args, 0 , 0 ) . unwrap ( ) ;
1011
1071
@@ -1014,10 +1074,10 @@ mod tests {
1014
1074
1015
1075
assert_eq ! (
1016
1076
env. copy_exec_to_chroot( ) . unwrap( ) ,
1017
- some_file_name . to_os_string( )
1077
+ exec_file_name . to_os_string( )
1018
1078
) ;
1019
1079
1020
- let dest_path = env. chroot_dir . join ( some_file_name ) ;
1080
+ let dest_path = env. chroot_dir . join ( exec_file_name ) ;
1021
1081
// Check that `fs::copy()` copied src content and permission bits to destination.
1022
1082
let metadata_src = fs:: metadata ( & env. exec_file_path ) . unwrap ( ) ;
1023
1083
let metadata_dest = fs:: metadata ( & dest_path) . unwrap ( ) ;
0 commit comments