@@ -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,24 @@ 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 {
437
+ read_dir,
438
+ entry : Pointer :: null ( ) ,
439
+ }
440
+ }
441
+ }
442
+
424
443
#[ derive( Debug ) ]
425
444
pub struct DirHandler {
426
445
/// Directory iterators used to emulate libc "directory streams", as used in opendir, readdir,
@@ -432,7 +451,7 @@ pub struct DirHandler {
432
451
/// the corresponding ReadDir iterator from this map, and information from the next
433
452
/// directory entry is returned. When closedir is called, the ReadDir iterator is removed from
434
453
/// the map.
435
- streams : FxHashMap < u64 , ReadDir > ,
454
+ streams : FxHashMap < u64 , OpenDir > ,
436
455
/// ID number to be used by the next call to opendir
437
456
next_id : u64 ,
438
457
}
@@ -441,7 +460,7 @@ impl DirHandler {
441
460
fn insert_new ( & mut self , read_dir : ReadDir ) -> u64 {
442
461
let id = self . next_id ;
443
462
self . next_id += 1 ;
444
- self . streams . try_insert ( id, read_dir) . unwrap ( ) ;
463
+ self . streams . try_insert ( id, OpenDir :: new ( read_dir) ) . unwrap ( ) ;
445
464
id
446
465
}
447
466
}
@@ -1196,6 +1215,85 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1196
1215
}
1197
1216
}
1198
1217
1218
+ fn linux_readdir64 ( & mut self , dirp_op : & OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , Scalar < Tag > > {
1219
+ let this = self . eval_context_mut ( ) ;
1220
+
1221
+ this. assert_target_os ( "linux" , "readdir64" ) ;
1222
+
1223
+ let dirp = this. read_scalar ( dirp_op) ?. to_machine_usize ( this) ?;
1224
+
1225
+ // Reject if isolation is enabled.
1226
+ if let IsolatedOp :: Reject ( reject_with) = this. machine . isolated_op {
1227
+ this. reject_in_isolation ( "`readdir`" , reject_with) ?;
1228
+ let eacc = this. eval_libc ( "EACCES" ) ?;
1229
+ this. set_last_error ( eacc) ?;
1230
+ return Ok ( Scalar :: null_ptr ( this) ) ;
1231
+ }
1232
+
1233
+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1234
+ err_unsup_format ! ( "the DIR pointer passed to readdir64 did not come from opendir" )
1235
+ } ) ?;
1236
+
1237
+ let entry = match open_dir. read_dir . next ( ) {
1238
+ Some ( Ok ( dir_entry) ) => {
1239
+ let ino64_t_layout = this. libc_ty_layout ( "ino64_t" ) ?;
1240
+ let off64_t_layout = this. libc_ty_layout ( "off64_t" ) ?;
1241
+ let c_ushort_layout = this. libc_ty_layout ( "c_ushort" ) ?;
1242
+ let c_uchar_layout = this. libc_ty_layout ( "c_uchar" ) ?;
1243
+
1244
+ // If the host is a Unix system, fill in the inode number with its real value.
1245
+ // If not, use 0 as a fallback value.
1246
+ #[ cfg( unix) ]
1247
+ let ino = std:: os:: unix:: fs:: DirEntryExt :: ino ( & dir_entry) ;
1248
+ #[ cfg( not( unix) ) ]
1249
+ let ino = 0u64 ;
1250
+
1251
+ let file_type = this. file_type_to_d_type ( dir_entry. file_type ( ) ) ?;
1252
+
1253
+ let imms = [
1254
+ immty_from_uint_checked ( ino, ino64_t_layout) ?, // d_ino
1255
+ immty_from_uint_checked ( 0u128 , off64_t_layout) ?, // d_off
1256
+ immty_from_uint_checked ( 0u128 , c_ushort_layout) ?, // d_reclen
1257
+ immty_from_int_checked ( file_type, c_uchar_layout) ?, // d_type
1258
+ ] ;
1259
+ let name = dir_entry. file_name ( ) ;
1260
+ let name_bytes = os_str_to_bytes ( & name) ?;
1261
+ let name_offset = imms. iter ( )
1262
+ . map ( |imm| imm. layout . size . bytes ( ) )
1263
+ . sum :: < u64 > ( ) ;
1264
+ let size = name_offset
1265
+ . checked_add ( u64:: try_from ( name_bytes. len ( ) ) . unwrap ( ) )
1266
+ . unwrap ( )
1267
+ . checked_add ( 1 )
1268
+ . unwrap ( ) ;
1269
+
1270
+ let entry = this. malloc ( size, /*zero_init:*/ false , MiriMemoryKind :: C ) ?;
1271
+ let entry_layout = this. layout_of ( this. tcx . mk_array ( this. tcx . types . u8 , size) ) ?;
1272
+ let entry_place = MPlaceTy :: from_aligned_ptr ( entry, entry_layout) ;
1273
+ this. write_packed_immediates ( & entry_place, & imms) ?;
1274
+
1275
+ let name_ptr = entry. offset ( Size :: from_bytes ( name_offset) , this) ?;
1276
+ this. memory . write_bytes ( name_ptr, name_bytes. iter ( ) . copied ( ) . chain ( std:: iter:: once ( 0 ) ) ) ?;
1277
+
1278
+ entry
1279
+ } ,
1280
+ None => {
1281
+ // end of stream: return NULL
1282
+ Pointer :: null ( )
1283
+ }
1284
+ Some ( Err ( e) ) => {
1285
+ this. set_last_error_from_io_error ( e. kind ( ) ) ?;
1286
+ Pointer :: null ( )
1287
+ }
1288
+ } ;
1289
+
1290
+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . unwrap ( ) ;
1291
+ let old_entry = std:: mem:: replace ( & mut open_dir. entry , entry) ;
1292
+ this. free ( old_entry, MiriMemoryKind :: C ) ?;
1293
+
1294
+ Ok ( Scalar :: from_maybe_pointer ( entry, this) )
1295
+ }
1296
+
1199
1297
fn linux_readdir64_r (
1200
1298
& mut self ,
1201
1299
dirp_op : & OpTy < ' tcx , Tag > ,
@@ -1215,10 +1313,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1215
1313
return this. handle_not_found ( ) ;
1216
1314
}
1217
1315
1218
- let dir_iter = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1316
+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1219
1317
err_unsup_format ! ( "the DIR pointer passed to readdir64_r did not come from opendir" )
1220
1318
} ) ?;
1221
- match dir_iter . next ( ) {
1319
+ match open_dir . read_dir . next ( ) {
1222
1320
Some ( Ok ( dir_entry) ) => {
1223
1321
// Write into entry, write pointer to result, return 0 on success.
1224
1322
// The name is written with write_os_str_to_c_str, while the rest of the
@@ -1314,10 +1412,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1314
1412
return this. handle_not_found ( ) ;
1315
1413
}
1316
1414
1317
- let dir_iter = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1415
+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1318
1416
err_unsup_format ! ( "the DIR pointer passed to readdir_r did not come from opendir" )
1319
1417
} ) ?;
1320
- match dir_iter . next ( ) {
1418
+ match open_dir . read_dir . next ( ) {
1321
1419
Some ( Ok ( dir_entry) ) => {
1322
1420
// Write into entry, write pointer to result, return 0 on success.
1323
1421
// The name is written with write_os_str_to_c_str, while the rest of the
@@ -1408,8 +1506,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1408
1506
return this. handle_not_found ( ) ;
1409
1507
}
1410
1508
1411
- if let Some ( dir_iter) = this. machine . dir_handler . streams . remove ( & dirp) {
1412
- drop ( dir_iter) ;
1509
+ if let Some ( open_dir) = this. machine . dir_handler . streams . remove ( & dirp) {
1510
+ this. free ( open_dir. entry , MiriMemoryKind :: C ) ?;
1511
+ drop ( open_dir) ;
1413
1512
Ok ( 0 )
1414
1513
} else {
1415
1514
this. handle_not_found ( )
0 commit comments