@@ -16,6 +16,7 @@ use rustc_target::abi::{Align, Size};
1616
1717use crate :: * ;
1818use helpers:: { check_arg_count, immty_from_int_checked, immty_from_uint_checked} ;
19+ use shims:: os_str:: os_str_to_bytes;
1920use shims:: time:: system_time_to_duration;
2021
2122#[ derive( Debug ) ]
@@ -421,6 +422,21 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, '
421422 }
422423}
423424
425+ /// An open directory, tracked by DirHandler.
426+ #[ derive( Debug ) ]
427+ pub struct OpenDir {
428+ /// The directory reader on the host.
429+ read_dir : ReadDir ,
430+ /// The most recent entry returned by readdir()
431+ entry : Pointer < Option < Tag > > ,
432+ }
433+
434+ impl OpenDir {
435+ fn new ( read_dir : ReadDir ) -> Self {
436+ Self { read_dir, entry : Pointer :: null ( ) }
437+ }
438+ }
439+
424440#[ derive( Debug ) ]
425441pub struct DirHandler {
426442 /// Directory iterators used to emulate libc "directory streams", as used in opendir, readdir,
@@ -432,7 +448,7 @@ pub struct DirHandler {
432448 /// the corresponding ReadDir iterator from this map, and information from the next
433449 /// directory entry is returned. When closedir is called, the ReadDir iterator is removed from
434450 /// the map.
435- streams : FxHashMap < u64 , ReadDir > ,
451+ streams : FxHashMap < u64 , OpenDir > ,
436452 /// ID number to be used by the next call to opendir
437453 next_id : u64 ,
438454}
@@ -441,7 +457,7 @@ impl DirHandler {
441457 fn insert_new ( & mut self , read_dir : ReadDir ) -> u64 {
442458 let id = self . next_id ;
443459 self . next_id += 1 ;
444- self . streams . try_insert ( id, read_dir) . unwrap ( ) ;
460+ self . streams . try_insert ( id, OpenDir :: new ( read_dir) ) . unwrap ( ) ;
445461 id
446462 }
447463}
@@ -1196,32 +1212,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11961212 }
11971213 }
11981214
1199- fn linux_readdir64_r (
1200- & mut self ,
1201- dirp_op : & OpTy < ' tcx , Tag > ,
1202- entry_op : & OpTy < ' tcx , Tag > ,
1203- result_op : & OpTy < ' tcx , Tag > ,
1204- ) -> InterpResult < ' tcx , i32 > {
1215+ fn linux_readdir64 ( & mut self , dirp_op : & OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , Scalar < Tag > > {
12051216 let this = self . eval_context_mut ( ) ;
12061217
1207- this. assert_target_os ( "linux" , "readdir64_r " ) ;
1218+ this. assert_target_os ( "linux" , "readdir64 " ) ;
12081219
12091220 let dirp = this. read_scalar ( dirp_op) ?. to_machine_usize ( this) ?;
12101221
12111222 // Reject if isolation is enabled.
12121223 if let IsolatedOp :: Reject ( reject_with) = this. machine . isolated_op {
1213- this. reject_in_isolation ( "`readdir64_r`" , reject_with) ?;
1214- // Set error code as "EBADF" (bad fd)
1215- return this. handle_not_found ( ) ;
1224+ this. reject_in_isolation ( "`readdir`" , reject_with) ?;
1225+ let eacc = this. eval_libc ( "EACCES" ) ?;
1226+ this. set_last_error ( eacc) ?;
1227+ return Ok ( Scalar :: null_ptr ( this) ) ;
12161228 }
12171229
1218- let dir_iter = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1219- err_unsup_format ! ( "the DIR pointer passed to readdir64_r did not come from opendir" )
1230+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1231+ err_unsup_format ! ( "the DIR pointer passed to readdir64 did not come from opendir" )
12201232 } ) ?;
1221- match dir_iter. next ( ) {
1233+
1234+ let entry = match open_dir. read_dir . next ( ) {
12221235 Some ( Ok ( dir_entry) ) => {
1223- // Write into entry, write pointer to result, return 0 on success .
1224- // The name is written with write_os_str_to_c_str , while the rest of the
1236+ // Write the directory entry into a newly allocated buffer .
1237+ // The name is written with write_bytes , while the rest of the
12251238 // dirent64 struct is written using write_packed_immediates.
12261239
12271240 // For reference:
@@ -1233,22 +1246,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
12331246 // pub d_name: [c_char; 256],
12341247 // }
12351248
1236- let entry_place = this. deref_operand ( entry_op) ?;
1237- let name_place = this. mplace_field ( & entry_place, 4 ) ?;
1238-
1239- let file_name = dir_entry. file_name ( ) ; // not a Path as there are no separators!
1240- let ( name_fits, _) = this. write_os_str_to_c_str (
1241- & file_name,
1242- name_place. ptr ,
1243- name_place. layout . size . bytes ( ) ,
1244- ) ?;
1245- if !name_fits {
1246- throw_unsup_format ! (
1247- "a directory entry had a name too large to fit in libc::dirent64"
1248- ) ;
1249- }
1250-
1251- let entry_place = this. deref_operand ( entry_op) ?;
12521249 let ino64_t_layout = this. libc_ty_layout ( "ino64_t" ) ?;
12531250 let off64_t_layout = this. libc_ty_layout ( "off64_t" ) ?;
12541251 let c_ushort_layout = this. libc_ty_layout ( "c_ushort" ) ?;
@@ -1269,30 +1266,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
12691266 immty_from_uint_checked ( 0u128 , c_ushort_layout) ?, // d_reclen
12701267 immty_from_int_checked ( file_type, c_uchar_layout) ?, // d_type
12711268 ] ;
1269+ let mut name = dir_entry. file_name ( ) ; // not a Path as there are no separators!
1270+ name. push ( "\0 " ) ; // Add a NUL terminator
1271+ let name_bytes = os_str_to_bytes ( & name) ?;
1272+ let name_offset = imms. iter ( ) . map ( |imm| imm. layout . size . bytes ( ) ) . sum :: < u64 > ( ) ;
1273+ let size =
1274+ name_offset. checked_add ( u64:: try_from ( name_bytes. len ( ) ) . unwrap ( ) ) . unwrap ( ) ;
1275+
1276+ let entry = this. malloc ( size, /*zero_init:*/ false , MiriMemoryKind :: C ) ?;
1277+ let entry_layout = this. layout_of ( this. tcx . mk_array ( this. tcx . types . u8 , size) ) ?;
1278+ let entry_place = MPlaceTy :: from_aligned_ptr ( entry, entry_layout) ;
12721279 this. write_packed_immediates ( & entry_place, & imms) ?;
12731280
1274- let result_place = this . deref_operand ( result_op ) ?;
1275- this. write_scalar ( this . read_scalar ( entry_op ) ? , & result_place . into ( ) ) ?;
1281+ let name_ptr = entry . offset ( Size :: from_bytes ( name_offset ) , this ) ?;
1282+ this. memory . write_bytes ( name_ptr , name_bytes . iter ( ) . copied ( ) ) ?;
12761283
1277- Ok ( 0 )
1284+ entry
12781285 }
12791286 None => {
1280- // end of stream: return 0, assign *result=NULL
1281- this. write_null ( & this. deref_operand ( result_op) ?. into ( ) ) ?;
1282- Ok ( 0 )
1287+ // end of stream: return NULL
1288+ Pointer :: null ( )
12831289 }
1284- Some ( Err ( e) ) =>
1285- match e. raw_os_error ( ) {
1286- // return positive error number on error
1287- Some ( error) => Ok ( error) ,
1288- None => {
1289- throw_unsup_format ! (
1290- "the error {} couldn't be converted to a return value" ,
1291- e
1292- )
1293- }
1294- } ,
1295- }
1290+ Some ( Err ( e) ) => {
1291+ this. set_last_error_from_io_error ( e. kind ( ) ) ?;
1292+ Pointer :: null ( )
1293+ }
1294+ } ;
1295+
1296+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . unwrap ( ) ;
1297+ let old_entry = std:: mem:: replace ( & mut open_dir. entry , entry) ;
1298+ this. free ( old_entry, MiriMemoryKind :: C ) ?;
1299+
1300+ Ok ( Scalar :: from_maybe_pointer ( entry, this) )
12961301 }
12971302
12981303 fn macos_readdir_r (
@@ -1314,10 +1319,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
13141319 return this. handle_not_found ( ) ;
13151320 }
13161321
1317- let dir_iter = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1322+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
13181323 err_unsup_format ! ( "the DIR pointer passed to readdir_r did not come from opendir" )
13191324 } ) ?;
1320- match dir_iter . next ( ) {
1325+ match open_dir . read_dir . next ( ) {
13211326 Some ( Ok ( dir_entry) ) => {
13221327 // Write into entry, write pointer to result, return 0 on success.
13231328 // The name is written with write_os_str_to_c_str, while the rest of the
@@ -1408,8 +1413,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
14081413 return this. handle_not_found ( ) ;
14091414 }
14101415
1411- if let Some ( dir_iter) = this. machine . dir_handler . streams . remove ( & dirp) {
1412- drop ( dir_iter) ;
1416+ if let Some ( open_dir) = this. machine . dir_handler . streams . remove ( & dirp) {
1417+ this. free ( open_dir. entry , MiriMemoryKind :: C ) ?;
1418+ drop ( open_dir) ;
14131419 Ok ( 0 )
14141420 } else {
14151421 this. handle_not_found ( )
0 commit comments