@@ -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,22 @@ 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
+ // We rely on `free` being a NOP on null pointers.
437
+ Self { read_dir, entry : Pointer :: null ( ) }
438
+ }
439
+ }
440
+
424
441
#[ derive( Debug ) ]
425
442
pub struct DirHandler {
426
443
/// Directory iterators used to emulate libc "directory streams", as used in opendir, readdir,
@@ -432,7 +449,7 @@ pub struct DirHandler {
432
449
/// the corresponding ReadDir iterator from this map, and information from the next
433
450
/// directory entry is returned. When closedir is called, the ReadDir iterator is removed from
434
451
/// the map.
435
- streams : FxHashMap < u64 , ReadDir > ,
452
+ streams : FxHashMap < u64 , OpenDir > ,
436
453
/// ID number to be used by the next call to opendir
437
454
next_id : u64 ,
438
455
}
@@ -441,7 +458,7 @@ impl DirHandler {
441
458
fn insert_new ( & mut self , read_dir : ReadDir ) -> u64 {
442
459
let id = self . next_id ;
443
460
self . next_id += 1 ;
444
- self . streams . try_insert ( id, read_dir) . unwrap ( ) ;
461
+ self . streams . try_insert ( id, OpenDir :: new ( read_dir) ) . unwrap ( ) ;
445
462
id
446
463
}
447
464
}
@@ -1207,32 +1224,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1207
1224
}
1208
1225
}
1209
1226
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 > > {
1216
1228
let this = self . eval_context_mut ( ) ;
1217
1229
1218
- this. assert_target_os ( "linux" , "readdir64_r " ) ;
1230
+ this. assert_target_os ( "linux" , "readdir64 " ) ;
1219
1231
1220
1232
let dirp = this. read_scalar ( dirp_op) ?. to_machine_usize ( this) ?;
1221
1233
1222
1234
// Reject if isolation is enabled.
1223
1235
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) ) ;
1227
1240
}
1228
1241
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" )
1231
1244
} ) ?;
1232
- match dir_iter. next ( ) {
1245
+
1246
+ let entry = match open_dir. read_dir . next ( ) {
1233
1247
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
1236
1250
// dirent64 struct is written using write_packed_immediates.
1237
1251
1238
1252
// For reference:
@@ -1244,22 +1258,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1244
1258
// pub d_name: [c_char; 256],
1245
1259
// }
1246
1260
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 ( ) ;
1249
1265
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 ) ?;
1261
1272
1262
- let entry_place = this. deref_operand ( entry_op) ?;
1263
1273
let ino64_t_layout = this. libc_ty_layout ( "ino64_t" ) ?;
1264
1274
let off64_t_layout = this. libc_ty_layout ( "off64_t" ) ?;
1265
1275
let c_ushort_layout = this. libc_ty_layout ( "c_ushort" ) ?;
@@ -1277,33 +1287,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1277
1287
let imms = [
1278
1288
immty_from_uint_checked ( ino, ino64_t_layout) ?, // d_ino
1279
1289
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
1281
1291
immty_from_int_checked ( file_type, c_uchar_layout) ?, // d_type
1282
1292
] ;
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) ;
1283
1295
this. write_packed_immediates ( & entry_place, & imms) ?;
1284
1296
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 ( ) ) ?;
1287
1299
1288
- Ok ( 0 )
1300
+ entry
1289
1301
}
1290
1302
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 ( )
1294
1305
}
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) )
1307
1317
}
1308
1318
1309
1319
fn macos_readdir_r (
@@ -1325,10 +1335,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1325
1335
return this. handle_not_found ( ) ;
1326
1336
}
1327
1337
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 ( || {
1329
1339
err_unsup_format ! ( "the DIR pointer passed to readdir_r did not come from opendir" )
1330
1340
} ) ?;
1331
- match dir_iter . next ( ) {
1341
+ match open_dir . read_dir . next ( ) {
1332
1342
Some ( Ok ( dir_entry) ) => {
1333
1343
// Write into entry, write pointer to result, return 0 on success.
1334
1344
// 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
1419
1429
return this. handle_not_found ( ) ;
1420
1430
}
1421
1431
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) ;
1424
1435
Ok ( 0 )
1425
1436
} else {
1426
1437
this. handle_not_found ( )
0 commit comments