@@ -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
}
@@ -1196,32 +1212,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1196
1212
}
1197
1213
}
1198
1214
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 > > {
1205
1216
let this = self . eval_context_mut ( ) ;
1206
1217
1207
- this. assert_target_os ( "linux" , "readdir64_r " ) ;
1218
+ this. assert_target_os ( "linux" , "readdir64 " ) ;
1208
1219
1209
1220
let dirp = this. read_scalar ( dirp_op) ?. to_machine_usize ( this) ?;
1210
1221
1211
1222
// Reject if isolation is enabled.
1212
1223
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) ) ;
1216
1228
}
1217
1229
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" )
1220
1232
} ) ?;
1221
- match dir_iter. next ( ) {
1233
+
1234
+ let entry = match open_dir. read_dir . next ( ) {
1222
1235
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
1225
1238
// dirent64 struct is written using write_packed_immediates.
1226
1239
1227
1240
// For reference:
@@ -1233,22 +1246,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1233
1246
// pub d_name: [c_char; 256],
1234
1247
// }
1235
1248
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) ?;
1252
1249
let ino64_t_layout = this. libc_ty_layout ( "ino64_t" ) ?;
1253
1250
let off64_t_layout = this. libc_ty_layout ( "off64_t" ) ?;
1254
1251
let c_ushort_layout = this. libc_ty_layout ( "c_ushort" ) ?;
@@ -1269,30 +1266,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1269
1266
immty_from_uint_checked ( 0u128 , c_ushort_layout) ?, // d_reclen
1270
1267
immty_from_int_checked ( file_type, c_uchar_layout) ?, // d_type
1271
1268
] ;
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) ;
1272
1279
this. write_packed_immediates ( & entry_place, & imms) ?;
1273
1280
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 ( ) ) ?;
1276
1283
1277
- Ok ( 0 )
1284
+ entry
1278
1285
}
1279
1286
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 ( )
1283
1289
}
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) )
1296
1301
}
1297
1302
1298
1303
fn macos_readdir_r (
@@ -1314,10 +1319,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1314
1319
return this. handle_not_found ( ) ;
1315
1320
}
1316
1321
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 ( || {
1318
1323
err_unsup_format ! ( "the DIR pointer passed to readdir_r did not come from opendir" )
1319
1324
} ) ?;
1320
- match dir_iter . next ( ) {
1325
+ match open_dir . read_dir . next ( ) {
1321
1326
Some ( Ok ( dir_entry) ) => {
1322
1327
// Write into entry, write pointer to result, return 0 on success.
1323
1328
// 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
1408
1413
return this. handle_not_found ( ) ;
1409
1414
}
1410
1415
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) ;
1413
1419
Ok ( 0 )
1414
1420
} else {
1415
1421
this. handle_not_found ( )
0 commit comments