@@ -16,6 +16,7 @@ use rustc_target::abi::{Align, Size};
16
16
17
17
use crate :: * ;
18
18
use helpers:: { check_arg_count, immty_from_int_checked, immty_from_uint_checked} ;
19
+ use shims:: os_str:: os_str_to_bytes;
19
20
use shims:: time:: system_time_to_duration;
20
21
21
22
#[ derive( Debug ) ]
@@ -421,6 +422,21 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, '
421
422
}
422
423
}
423
424
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
+
424
440
#[ derive( Debug ) ]
425
441
pub struct DirHandler {
426
442
/// Directory iterators used to emulate libc "directory streams", as used in opendir, readdir,
@@ -432,7 +448,7 @@ pub struct DirHandler {
432
448
/// the corresponding ReadDir iterator from this map, and information from the next
433
449
/// directory entry is returned. When closedir is called, the ReadDir iterator is removed from
434
450
/// the map.
435
- streams : FxHashMap < u64 , ReadDir > ,
451
+ streams : FxHashMap < u64 , OpenDir > ,
436
452
/// ID number to be used by the next call to opendir
437
453
next_id : u64 ,
438
454
}
@@ -441,7 +457,7 @@ impl DirHandler {
441
457
fn insert_new ( & mut self , read_dir : ReadDir ) -> u64 {
442
458
let id = self . next_id ;
443
459
self . next_id += 1 ;
444
- self . streams . try_insert ( id, read_dir) . unwrap ( ) ;
460
+ self . streams . try_insert ( id, OpenDir :: new ( read_dir) ) . unwrap ( ) ;
445
461
id
446
462
}
447
463
}
@@ -1207,32 +1223,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1207
1223
}
1208
1224
}
1209
1225
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 > {
1226
+ fn linux_readdir64 ( & mut self , dirp_op : & OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , Scalar < Tag > > {
1216
1227
let this = self . eval_context_mut ( ) ;
1217
1228
1218
- this. assert_target_os ( "linux" , "readdir64_r " ) ;
1229
+ this. assert_target_os ( "linux" , "readdir64 " ) ;
1219
1230
1220
1231
let dirp = this. read_scalar ( dirp_op) ?. to_machine_usize ( this) ?;
1221
1232
1222
1233
// Reject if isolation is enabled.
1223
1234
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 ( ) ;
1235
+ this. reject_in_isolation ( "`readdir`" , reject_with) ?;
1236
+ let eacc = this. eval_libc ( "EACCES" ) ?;
1237
+ this. set_last_error ( eacc) ?;
1238
+ return Ok ( Scalar :: null_ptr ( this) ) ;
1227
1239
}
1228
1240
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" )
1241
+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1242
+ err_unsup_format ! ( "the DIR pointer passed to readdir64 did not come from opendir" )
1231
1243
} ) ?;
1232
- match dir_iter. next ( ) {
1244
+
1245
+ let entry = match open_dir. read_dir . next ( ) {
1233
1246
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
1247
+ // Write the directory entry into a newly allocated buffer .
1248
+ // The name is written with write_bytes , while the rest of the
1236
1249
// dirent64 struct is written using write_packed_immediates.
1237
1250
1238
1251
// For reference:
@@ -1244,22 +1257,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1244
1257
// pub d_name: [c_char; 256],
1245
1258
// }
1246
1259
1247
- let entry_place = this. deref_operand ( entry_op) ?;
1248
- let name_place = this. mplace_field ( & entry_place, 4 ) ?;
1249
-
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
- }
1261
-
1262
- let entry_place = this. deref_operand ( entry_op) ?;
1263
1260
let ino64_t_layout = this. libc_ty_layout ( "ino64_t" ) ?;
1264
1261
let off64_t_layout = this. libc_ty_layout ( "off64_t" ) ?;
1265
1262
let c_ushort_layout = this. libc_ty_layout ( "c_ushort" ) ?;
@@ -1280,30 +1277,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1280
1277
immty_from_uint_checked ( 0u128 , c_ushort_layout) ?, // d_reclen
1281
1278
immty_from_int_checked ( file_type, c_uchar_layout) ?, // d_type
1282
1279
] ;
1280
+ let mut name = dir_entry. file_name ( ) ; // not a Path as there are no separators!
1281
+ name. push ( "\0 " ) ; // Add a NUL terminator
1282
+ let name_bytes = os_str_to_bytes ( & name) ?;
1283
+ let name_offset = imms. iter ( ) . map ( |imm| imm. layout . size . bytes ( ) ) . sum :: < u64 > ( ) ;
1284
+ let size =
1285
+ name_offset. checked_add ( u64:: try_from ( name_bytes. len ( ) ) . unwrap ( ) ) . unwrap ( ) ;
1286
+
1287
+ let entry = this. malloc ( size, /*zero_init:*/ false , MiriMemoryKind :: C ) ?;
1288
+ let entry_layout = this. layout_of ( this. tcx . mk_array ( this. tcx . types . u8 , size) ) ?;
1289
+ let entry_place = MPlaceTy :: from_aligned_ptr ( entry, entry_layout) ;
1283
1290
this. write_packed_immediates ( & entry_place, & imms) ?;
1284
1291
1285
- let result_place = this . deref_operand ( result_op ) ?;
1286
- this. write_scalar ( this . read_scalar ( entry_op ) ? , & result_place . into ( ) ) ?;
1292
+ let name_ptr = entry . offset ( Size :: from_bytes ( name_offset ) , this ) ?;
1293
+ this. memory . write_bytes ( name_ptr , name_bytes . iter ( ) . copied ( ) ) ?;
1287
1294
1288
- Ok ( 0 )
1295
+ entry
1289
1296
}
1290
1297
None => {
1291
- // end of stream: return 0, assign *result=NULL
1292
- this. write_null ( & this. deref_operand ( result_op) ?. into ( ) ) ?;
1293
- Ok ( 0 )
1298
+ // end of stream: return NULL
1299
+ Pointer :: null ( )
1294
1300
}
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
- }
1301
+ Some ( Err ( e) ) => {
1302
+ this. set_last_error_from_io_error ( e. kind ( ) ) ?;
1303
+ Pointer :: null ( )
1304
+ }
1305
+ } ;
1306
+
1307
+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . unwrap ( ) ;
1308
+ let old_entry = std:: mem:: replace ( & mut open_dir. entry , entry) ;
1309
+ this. free ( old_entry, MiriMemoryKind :: C ) ?;
1310
+
1311
+ Ok ( Scalar :: from_maybe_pointer ( entry, this) )
1307
1312
}
1308
1313
1309
1314
fn macos_readdir_r (
@@ -1325,10 +1330,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1325
1330
return this. handle_not_found ( ) ;
1326
1331
}
1327
1332
1328
- let dir_iter = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1333
+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1329
1334
err_unsup_format ! ( "the DIR pointer passed to readdir_r did not come from opendir" )
1330
1335
} ) ?;
1331
- match dir_iter . next ( ) {
1336
+ match open_dir . read_dir . next ( ) {
1332
1337
Some ( Ok ( dir_entry) ) => {
1333
1338
// Write into entry, write pointer to result, return 0 on success.
1334
1339
// The name is written with write_os_str_to_c_str, while the rest of the
@@ -1419,8 +1424,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1419
1424
return this. handle_not_found ( ) ;
1420
1425
}
1421
1426
1422
- if let Some ( dir_iter) = this. machine . dir_handler . streams . remove ( & dirp) {
1423
- drop ( dir_iter) ;
1427
+ if let Some ( open_dir) = this. machine . dir_handler . streams . remove ( & dirp) {
1428
+ this. free ( open_dir. entry , MiriMemoryKind :: C ) ?;
1429
+ drop ( open_dir) ;
1424
1430
Ok ( 0 )
1425
1431
} else {
1426
1432
this. handle_not_found ( )
0 commit comments