@@ -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,22 @@ 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+ // We rely on `free` being a NOP on null pointers.
437+ Self { read_dir, entry : Pointer :: null ( ) }
438+ }
439+ }
440+
424441#[ derive( Debug ) ]
425442pub struct DirHandler {
426443 /// Directory iterators used to emulate libc "directory streams", as used in opendir, readdir,
@@ -432,7 +449,7 @@ pub struct DirHandler {
432449 /// the corresponding ReadDir iterator from this map, and information from the next
433450 /// directory entry is returned. When closedir is called, the ReadDir iterator is removed from
434451 /// the map.
435- streams : FxHashMap < u64 , ReadDir > ,
452+ streams : FxHashMap < u64 , OpenDir > ,
436453 /// ID number to be used by the next call to opendir
437454 next_id : u64 ,
438455}
@@ -441,7 +458,7 @@ impl DirHandler {
441458 fn insert_new ( & mut self , read_dir : ReadDir ) -> u64 {
442459 let id = self . next_id ;
443460 self . next_id += 1 ;
444- self . streams . try_insert ( id, read_dir) . unwrap ( ) ;
461+ self . streams . try_insert ( id, OpenDir :: new ( read_dir) ) . unwrap ( ) ;
445462 id
446463 }
447464}
@@ -1207,32 +1224,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
12071224 }
12081225 }
12091226
1210- fn linux_readdir64_r (
1211- & mut self ,
1212- dirp_op : & OpTy < ' tcx , Tag > ,
1213- entry_op : & OpTy < ' tcx , Tag > ,
1214- result_op : & OpTy < ' tcx , Tag > ,
1215- ) -> InterpResult < ' tcx , i32 > {
1227+ fn linux_readdir64 ( & mut self , dirp_op : & OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , Scalar < Tag > > {
12161228 let this = self . eval_context_mut ( ) ;
12171229
1218- this. assert_target_os ( "linux" , "readdir64_r " ) ;
1230+ this. assert_target_os ( "linux" , "readdir64 " ) ;
12191231
12201232 let dirp = this. read_scalar ( dirp_op) ?. to_machine_usize ( this) ?;
12211233
12221234 // Reject if isolation is enabled.
12231235 if let IsolatedOp :: Reject ( reject_with) = this. machine . isolated_op {
1224- this. reject_in_isolation ( "`readdir64_r`" , reject_with) ?;
1225- // Set error code as "EBADF" (bad fd)
1226- return this. handle_not_found ( ) ;
1236+ this. reject_in_isolation ( "`readdir`" , reject_with) ?;
1237+ let eacc = this. eval_libc ( "EBADF" ) ?;
1238+ this. set_last_error ( eacc) ?;
1239+ return Ok ( Scalar :: null_ptr ( this) ) ;
12271240 }
12281241
1229- let dir_iter = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1230- err_unsup_format ! ( "the DIR pointer passed to readdir64_r did not come from opendir" )
1242+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1243+ err_unsup_format ! ( "the DIR pointer passed to readdir64 did not come from opendir" )
12311244 } ) ?;
1232- match dir_iter. next ( ) {
1245+
1246+ let entry = match open_dir. read_dir . next ( ) {
12331247 Some ( Ok ( dir_entry) ) => {
1234- // Write into entry, write pointer to result, return 0 on success .
1235- // The name is written with write_os_str_to_c_str , while the rest of the
1248+ // Write the directory entry into a newly allocated buffer .
1249+ // The name is written with write_bytes , while the rest of the
12361250 // dirent64 struct is written using write_packed_immediates.
12371251
12381252 // For reference:
@@ -1244,22 +1258,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
12441258 // pub d_name: [c_char; 256],
12451259 // }
12461260
1247- let entry_place = this. deref_operand ( entry_op) ?;
1248- let name_place = this. mplace_field ( & entry_place, 4 ) ?;
1261+ let mut name = dir_entry. file_name ( ) ; // not a Path as there are no separators!
1262+ name. push ( "\0 " ) ; // Add a NUL terminator
1263+ let name_bytes = os_str_to_bytes ( & name) ?;
1264+ let name_len = u64:: try_from ( name_bytes. len ( ) ) . unwrap ( ) ;
12491265
1250- let file_name = dir_entry. file_name ( ) ; // not a Path as there are no separators!
1251- let ( name_fits, _) = this. write_os_str_to_c_str (
1252- & file_name,
1253- name_place. ptr ,
1254- name_place. layout . size . bytes ( ) ,
1255- ) ?;
1256- if !name_fits {
1257- throw_unsup_format ! (
1258- "a directory entry had a name too large to fit in libc::dirent64"
1259- ) ;
1260- }
1266+ let dirent64_layout = this. libc_ty_layout ( "dirent64" ) ?;
1267+ let d_name_offset = dirent64_layout. fields . offset ( 4 /* d_name */ ) . bytes ( ) ;
1268+ let size = d_name_offset. checked_add ( name_len) . unwrap ( ) ;
1269+
1270+ let entry =
1271+ this. malloc ( size, /*zero_init:*/ false , MiriMemoryKind :: Runtime ) ?;
12611272
1262- let entry_place = this. deref_operand ( entry_op) ?;
12631273 let ino64_t_layout = this. libc_ty_layout ( "ino64_t" ) ?;
12641274 let off64_t_layout = this. libc_ty_layout ( "off64_t" ) ?;
12651275 let c_ushort_layout = this. libc_ty_layout ( "c_ushort" ) ?;
@@ -1277,33 +1287,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
12771287 let imms = [
12781288 immty_from_uint_checked ( ino, ino64_t_layout) ?, // d_ino
12791289 immty_from_uint_checked ( 0u128 , off64_t_layout) ?, // d_off
1280- immty_from_uint_checked ( 0u128 , c_ushort_layout) ?, // d_reclen
1290+ immty_from_uint_checked ( size , c_ushort_layout) ?, // d_reclen
12811291 immty_from_int_checked ( file_type, c_uchar_layout) ?, // d_type
12821292 ] ;
1293+ let entry_layout = this. layout_of ( this. tcx . mk_array ( this. tcx . types . u8 , size) ) ?;
1294+ let entry_place = MPlaceTy :: from_aligned_ptr ( entry, entry_layout) ;
12831295 this. write_packed_immediates ( & entry_place, & imms) ?;
12841296
1285- let result_place = this . deref_operand ( result_op ) ?;
1286- this. write_scalar ( this . read_scalar ( entry_op ) ? , & result_place . into ( ) ) ?;
1297+ let name_ptr = entry . offset ( Size :: from_bytes ( d_name_offset ) , this ) ?;
1298+ this. memory . write_bytes ( name_ptr , name_bytes . iter ( ) . copied ( ) ) ?;
12871299
1288- Ok ( 0 )
1300+ entry
12891301 }
12901302 None => {
1291- // end of stream: return 0, assign *result=NULL
1292- this. write_null ( & this. deref_operand ( result_op) ?. into ( ) ) ?;
1293- Ok ( 0 )
1303+ // end of stream: return NULL
1304+ Pointer :: null ( )
12941305 }
1295- Some ( Err ( e) ) =>
1296- match e. raw_os_error ( ) {
1297- // return positive error number on error
1298- Some ( error) => Ok ( error) ,
1299- None => {
1300- throw_unsup_format ! (
1301- "the error {} couldn't be converted to a return value" ,
1302- e
1303- )
1304- }
1305- } ,
1306- }
1306+ Some ( Err ( e) ) => {
1307+ this. set_last_error_from_io_error ( e. kind ( ) ) ?;
1308+ Pointer :: null ( )
1309+ }
1310+ } ;
1311+
1312+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . unwrap ( ) ;
1313+ let old_entry = std:: mem:: replace ( & mut open_dir. entry , entry) ;
1314+ this. free ( old_entry, MiriMemoryKind :: Runtime ) ?;
1315+
1316+ Ok ( Scalar :: from_maybe_pointer ( entry, this) )
13071317 }
13081318
13091319 fn macos_readdir_r (
@@ -1325,10 +1335,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
13251335 return this. handle_not_found ( ) ;
13261336 }
13271337
1328- let dir_iter = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1338+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
13291339 err_unsup_format ! ( "the DIR pointer passed to readdir_r did not come from opendir" )
13301340 } ) ?;
1331- match dir_iter . next ( ) {
1341+ match open_dir . read_dir . next ( ) {
13321342 Some ( Ok ( dir_entry) ) => {
13331343 // Write into entry, write pointer to result, return 0 on success.
13341344 // The name is written with write_os_str_to_c_str, while the rest of the
@@ -1419,8 +1429,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
14191429 return this. handle_not_found ( ) ;
14201430 }
14211431
1422- if let Some ( dir_iter) = this. machine . dir_handler . streams . remove ( & dirp) {
1423- drop ( dir_iter) ;
1432+ if let Some ( open_dir) = this. machine . dir_handler . streams . remove ( & dirp) {
1433+ this. free ( open_dir. entry , MiriMemoryKind :: Runtime ) ?;
1434+ drop ( open_dir) ;
14241435 Ok ( 0 )
14251436 } else {
14261437 this. handle_not_found ( )
0 commit comments