@@ -269,40 +269,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
269
269
let this = self . eval_context_mut ( ) ;
270
270
271
271
if this. tcx . sess . target . target . target_os . to_lowercase ( ) != "macos" {
272
- throw_unsup_format ! ( "The `stat` shim is only only available in the `macos` platform ." )
272
+ throw_unsup_format ! ( "The `stat` shim is only only available for `macos` targets ." )
273
273
}
274
274
275
275
let path_scalar = this. read_scalar ( path_op) ?. not_undef ( ) ?;
276
- let path = this. read_os_str_from_c_str ( path_scalar) ?;
276
+ let path: PathBuf = this. read_os_str_from_c_str ( path_scalar) ?. into ( ) ;
277
277
278
278
let buf = this. deref_operand ( buf_op) ?;
279
279
280
- let metadata = match std:: fs:: metadata ( path) {
281
- Ok ( metadata) => metadata,
282
- Err ( e) => {
283
- this. set_last_error_from_io_error ( e) ?;
284
- return Ok ( -1 ) ;
285
- }
286
- } ;
287
-
288
- let file_type = metadata. file_type ( ) ;
289
-
290
- let mode_name = if file_type. is_file ( ) {
291
- "S_IFREG"
292
- } else if file_type. is_dir ( ) {
293
- "S_IFDIR"
294
- } else {
295
- "S_IFLNK"
280
+ let stats = match FileStatus :: new ( this, path, false ) ? {
281
+ Some ( stats) => stats,
282
+ None => return Ok ( -1 ) ,
296
283
} ;
297
284
298
285
// FIXME: use Scalar::to_u16
299
- let mode = this. eval_libc ( mode_name) ?. to_bits ( Size :: from_bits ( 16 ) ) ? as u16 ;
300
-
301
- let size = metadata. len ( ) ;
286
+ let mode: u16 = stats. mode . to_bits ( Size :: from_bits ( 16 ) ) ? as u16 ;
302
287
303
- let ( access_sec, access_nsec) = extract_sec_and_nsec ( metadata . accessed ( ) , & mut 0 , 0 ) ? ;
304
- let ( created_sec, created_nsec) = extract_sec_and_nsec ( metadata . created ( ) , & mut 0 , 0 ) ? ;
305
- let ( modified_sec, modified_nsec) = extract_sec_and_nsec ( metadata . modified ( ) , & mut 0 , 0 ) ? ;
288
+ let ( access_sec, access_nsec) = stats . accessed . unwrap_or ( ( 0 , 0 ) ) ;
289
+ let ( created_sec, created_nsec) = stats . created . unwrap_or ( ( 0 , 0 ) ) ;
290
+ let ( modified_sec, modified_nsec) = stats . modified . unwrap_or ( ( 0 , 0 ) ) ;
306
291
307
292
let dev_t_layout = this. libc_ty_layout ( "dev_t" ) ?;
308
293
let mode_t_layout = this. libc_ty_layout ( "mode_t" ) ?;
@@ -341,7 +326,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
341
326
immty_from_uint_checked ( 0u128 , long_layout) ?, // st_ctime_nsec
342
327
immty_from_uint_checked ( created_sec, time_t_layout) ?, // st_birthtime
343
328
immty_from_uint_checked ( created_nsec, long_layout) ?, // st_birthtime_nsec
344
- immty_from_uint_checked ( size, off_t_layout) ?, // st_size
329
+ immty_from_uint_checked ( stats . size , off_t_layout) ?, // st_size
345
330
immty_from_uint_checked ( 0u128 , blkcnt_t_layout) ?, // st_blocks
346
331
immty_from_uint_checked ( 0u128 , blksize_t_layout) ?, // st_blksize
347
332
immty_from_uint_checked ( 0u128 , uint32_t_layout) ?, // st_flags
@@ -365,6 +350,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
365
350
366
351
this. check_no_isolation ( "statx" ) ?;
367
352
353
+ if this. tcx . sess . target . target . target_os . to_lowercase ( ) != "linux" {
354
+ throw_unsup_format ! ( "The `statx` shim is only only available for `linux` targets." )
355
+ }
356
+
368
357
let statxbuf_scalar = this. read_scalar ( statxbuf_op) ?. not_undef ( ) ?;
369
358
let pathname_scalar = this. read_scalar ( pathname_op) ?. not_undef ( ) ?;
370
359
@@ -422,68 +411,50 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
422
411
423
412
// If the `AT_SYMLINK_NOFOLLOW` flag is set, we query the file's metadata without following
424
413
// symbolic links.
425
- let metadata = if flags & this. eval_libc ( "AT_SYMLINK_NOFOLLOW" ) ?. to_i32 ( ) ? != 0 {
426
- // FIXME: metadata for symlinks need testing.
427
- std:: fs:: symlink_metadata ( path)
428
- } else {
429
- std:: fs:: metadata ( path)
430
- } ;
431
-
432
- let metadata = match metadata {
433
- Ok ( metadata) => metadata,
434
- Err ( e) => {
435
- this. set_last_error_from_io_error ( e) ?;
436
- return Ok ( -1 ) ;
437
- }
438
- } ;
439
-
440
- let file_type = metadata. file_type ( ) ;
414
+ let is_symlink = flags & this. eval_libc ( "AT_SYMLINK_NOFOLLOW" ) ?. to_i32 ( ) ? != 0 ;
441
415
442
- let mode_name = if file_type. is_file ( ) {
443
- "S_IFREG"
444
- } else if file_type. is_dir ( ) {
445
- "S_IFDIR"
446
- } else {
447
- "S_IFLNK"
416
+ let stats = match FileStatus :: new ( this, path, is_symlink) ? {
417
+ Some ( stats) => stats,
418
+ None => return Ok ( -1 ) ,
448
419
} ;
449
420
450
421
// The `mode` field specifies the type of the file and the permissions over the file for
451
422
// the owner, its group and other users. Given that we can only provide the file type
452
423
// without using platform specific methods, we only set the bits corresponding to the file
453
424
// type. This should be an `__u16` but `libc` provides its values as `u32`.
454
- let mode: u16 = this
455
- . eval_libc ( mode_name ) ?
425
+ let mode: u16 = stats
426
+ . mode
456
427
. to_u32 ( ) ?
457
428
. try_into ( )
458
- . unwrap_or_else ( |_| bug ! ( "libc contains bad value for `{}` constant" , mode_name) ) ;
459
-
460
- let size = metadata. len ( ) ;
429
+ . unwrap_or_else ( |_| bug ! ( "libc contains bad value for constant" ) ) ;
461
430
462
- let ( access_sec, access_nsec) = extract_sec_and_nsec (
463
- metadata. accessed ( ) ,
464
- & mut mask,
465
- this. eval_libc ( "STATX_ATIME" ) ?. to_u32 ( ) ?,
466
- ) ?;
431
+ let ( access_sec, access_nsec) = if let Some ( tup) = stats. accessed {
432
+ tup
433
+ } else {
434
+ mask |= this. eval_libc ( "STATX_ATIME" ) ?. to_u32 ( ) ?;
435
+ ( 0 , 0 )
436
+ } ;
467
437
468
- let ( created_sec, created_nsec) = extract_sec_and_nsec (
469
- metadata. created ( ) ,
470
- & mut mask,
471
- this. eval_libc ( "STATX_BTIME" ) ?. to_u32 ( ) ?,
472
- ) ?;
438
+ let ( created_sec, created_nsec) = if let Some ( tup) = stats. created {
439
+ tup
440
+ } else {
441
+ mask |= this. eval_libc ( "STATX_BTIME" ) ?. to_u32 ( ) ?;
442
+ ( 0 , 0 )
443
+ } ;
473
444
474
- let ( modified_sec, modified_nsec) = extract_sec_and_nsec (
475
- metadata. modified ( ) ,
476
- & mut mask,
477
- this. eval_libc ( "STATX_MTIME" ) ?. to_u32 ( ) ?,
478
- ) ?;
445
+ let ( modified_sec, modified_nsec) = if let Some ( tup) = stats. modified {
446
+ tup
447
+ } else {
448
+ mask |= this. eval_libc ( "STATX_MTIME" ) ?. to_u32 ( ) ?;
449
+ ( 0 , 0 )
450
+ } ;
479
451
480
452
let __u32_layout = this. libc_ty_layout ( "__u32" ) ?;
481
453
let __u64_layout = this. libc_ty_layout ( "__u64" ) ?;
482
454
let __u16_layout = this. libc_ty_layout ( "__u16" ) ?;
483
455
484
456
// Now we transform all this fields into `ImmTy`s and write them to `statxbuf`. We write a
485
457
// zero for the unavailable fields.
486
- // FIXME: Provide more fields using platform specific methods.
487
458
let imms = [
488
459
immty_from_uint_checked ( mask, __u32_layout) ?, // stx_mask
489
460
immty_from_uint_checked ( 0u128 , __u32_layout) ?, // stx_blksize
@@ -494,7 +465,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
494
465
immty_from_uint_checked ( mode, __u16_layout) ?, // stx_mode
495
466
immty_from_uint_checked ( 0u128 , __u16_layout) ?, // statx padding
496
467
immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_ino
497
- immty_from_uint_checked ( size, __u64_layout) ?, // stx_size
468
+ immty_from_uint_checked ( stats . size , __u64_layout) ?, // stx_size
498
469
immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_blocks
499
470
immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_attributes
500
471
immty_from_uint_checked ( access_sec, __u64_layout) ?, // stx_atime.tv_sec
@@ -532,19 +503,59 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
532
503
}
533
504
}
534
505
535
- // Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch, and
536
- // then sets the `mask` bits determined by `flag` when `time` is Ok. If `time` is an error, it
537
- // returns `(0, 0)` without setting any bits.
538
- fn extract_sec_and_nsec < ' tcx > (
539
- time : std:: io:: Result < SystemTime > ,
540
- mask : & mut u32 ,
541
- flag : u32 ,
542
- ) -> InterpResult < ' tcx , ( u64 , u32 ) > {
543
- if let Ok ( time) = time {
506
+ // Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when
507
+ // `time` is Ok. If `time` is an error, it returns `None`.
508
+ fn extract_sec_and_nsec < ' tcx > ( time : std:: io:: Result < SystemTime > ) -> InterpResult < ' tcx , Option < ( u64 , u32 ) > > {
509
+ time. ok ( ) . map ( |time| {
544
510
let duration = system_time_to_duration ( & time) ?;
545
- * mask |= flag;
546
511
Ok ( ( duration. as_secs ( ) , duration. subsec_nanos ( ) ) )
547
- } else {
548
- Ok ( ( 0 , 0 ) )
512
+ } ) . transpose ( )
513
+ }
514
+
515
+ struct FileStatus {
516
+ mode : Scalar < Tag > ,
517
+ size : u64 ,
518
+ created : Option < ( u64 , u32 ) > ,
519
+ accessed : Option < ( u64 , u32 ) > ,
520
+ modified : Option < ( u64 , u32 ) > ,
521
+ }
522
+
523
+ impl FileStatus {
524
+ fn new < ' tcx , ' mir > ( ecx : & mut MiriEvalContext < ' mir , ' tcx > , path : PathBuf , is_symlink : bool ) -> InterpResult < ' tcx , Option < FileStatus > > {
525
+ let metadata = if is_symlink {
526
+ // FIXME: metadata for symlinks need testing.
527
+ std:: fs:: symlink_metadata ( path)
528
+ } else {
529
+ std:: fs:: metadata ( path)
530
+ } ;
531
+
532
+ let metadata = match metadata {
533
+ Ok ( metadata) => metadata,
534
+ Err ( e) => {
535
+ ecx. set_last_error_from_io_error ( e) ?;
536
+ return Ok ( None ) ;
537
+ }
538
+ } ;
539
+
540
+ let file_type = metadata. file_type ( ) ;
541
+
542
+ let mode_name = if file_type. is_file ( ) {
543
+ "S_IFREG"
544
+ } else if file_type. is_dir ( ) {
545
+ "S_IFDIR"
546
+ } else {
547
+ "S_IFLNK"
548
+ } ;
549
+
550
+ let mode = ecx. eval_libc ( mode_name) ?;
551
+
552
+ let size = metadata. len ( ) ;
553
+
554
+ let created = extract_sec_and_nsec ( metadata. created ( ) ) ?;
555
+ let accessed = extract_sec_and_nsec ( metadata. accessed ( ) ) ?;
556
+ let modified = extract_sec_and_nsec ( metadata. modified ( ) ) ?;
557
+
558
+ // FIXME: Provide more fields using platform specific methods.
559
+ Ok ( Some ( FileStatus { mode, size, created, accessed, modified } ) )
549
560
}
550
561
}
0 commit comments