@@ -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,19 @@ 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 ( ) ;
1261
1269
1262
- let entry_place = this. deref_operand ( entry_op) ?;
1270
+ let entry =
1271
+ this. malloc ( size, /*zero_init:*/ false , MiriMemoryKind :: Runtime ) ?;
1272
+
1273
+ // FIXME: make use of dirent64_layout
1263
1274
let ino64_t_layout = this. libc_ty_layout ( "ino64_t" ) ?;
1264
1275
let off64_t_layout = this. libc_ty_layout ( "off64_t" ) ?;
1265
1276
let c_ushort_layout = this. libc_ty_layout ( "c_ushort" ) ?;
@@ -1277,33 +1288,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1277
1288
let imms = [
1278
1289
immty_from_uint_checked ( ino, ino64_t_layout) ?, // d_ino
1279
1290
immty_from_uint_checked ( 0u128 , off64_t_layout) ?, // d_off
1280
- immty_from_uint_checked ( 0u128 , c_ushort_layout) ?, // d_reclen
1291
+ immty_from_uint_checked ( size , c_ushort_layout) ?, // d_reclen
1281
1292
immty_from_int_checked ( file_type, c_uchar_layout) ?, // d_type
1282
1293
] ;
1294
+ let entry_layout = this. layout_of ( this. tcx . mk_array ( this. tcx . types . u8 , size) ) ?;
1295
+ let entry_place = MPlaceTy :: from_aligned_ptr ( entry, entry_layout) ;
1283
1296
this. write_packed_immediates ( & entry_place, & imms) ?;
1284
1297
1285
- let result_place = this . deref_operand ( result_op ) ?;
1286
- this. write_scalar ( this . read_scalar ( entry_op ) ? , & result_place . into ( ) ) ?;
1298
+ let name_ptr = entry . offset ( Size :: from_bytes ( d_name_offset ) , this ) ?;
1299
+ this. memory . write_bytes ( name_ptr , name_bytes . iter ( ) . copied ( ) ) ?;
1287
1300
1288
- Ok ( 0 )
1301
+ entry
1289
1302
}
1290
1303
None => {
1291
- // end of stream: return 0, assign *result=NULL
1292
- this. write_null ( & this. deref_operand ( result_op) ?. into ( ) ) ?;
1293
- Ok ( 0 )
1304
+ // end of stream: return NULL
1305
+ Pointer :: null ( )
1294
1306
}
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
- }
1307
+ Some ( Err ( e) ) => {
1308
+ this. set_last_error_from_io_error ( e. kind ( ) ) ?;
1309
+ Pointer :: null ( )
1310
+ }
1311
+ } ;
1312
+
1313
+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . unwrap ( ) ;
1314
+ let old_entry = std:: mem:: replace ( & mut open_dir. entry , entry) ;
1315
+ this. free ( old_entry, MiriMemoryKind :: Runtime ) ?;
1316
+
1317
+ Ok ( Scalar :: from_maybe_pointer ( entry, this) )
1307
1318
}
1308
1319
1309
1320
fn macos_readdir_r (
@@ -1325,10 +1336,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1325
1336
return this. handle_not_found ( ) ;
1326
1337
}
1327
1338
1328
- let dir_iter = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1339
+ let open_dir = this. machine . dir_handler . streams . get_mut ( & dirp) . ok_or_else ( || {
1329
1340
err_unsup_format ! ( "the DIR pointer passed to readdir_r did not come from opendir" )
1330
1341
} ) ?;
1331
- match dir_iter . next ( ) {
1342
+ match open_dir . read_dir . next ( ) {
1332
1343
Some ( Ok ( dir_entry) ) => {
1333
1344
// Write into entry, write pointer to result, return 0 on success.
1334
1345
// The name is written with write_os_str_to_c_str, while the rest of the
@@ -1419,8 +1430,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1419
1430
return this. handle_not_found ( ) ;
1420
1431
}
1421
1432
1422
- if let Some ( dir_iter) = this. machine . dir_handler . streams . remove ( & dirp) {
1423
- drop ( dir_iter) ;
1433
+ if let Some ( open_dir) = this. machine . dir_handler . streams . remove ( & dirp) {
1434
+ this. free ( open_dir. entry , MiriMemoryKind :: Runtime ) ?;
1435
+ drop ( open_dir) ;
1424
1436
Ok ( 0 )
1425
1437
} else {
1426
1438
this. handle_not_found ( )
0 commit comments