@@ -328,6 +328,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
328
328
this. stat_or_lstat ( false , path_op, buf_op)
329
329
}
330
330
331
+ fn fstat (
332
+ & mut self ,
333
+ fd_op : OpTy < ' tcx , Tag > ,
334
+ buf_op : OpTy < ' tcx , Tag > ,
335
+ ) -> InterpResult < ' tcx , i32 > {
336
+ let this = self . eval_context_mut ( ) ;
337
+
338
+ this. check_no_isolation ( "fstat" ) ?;
339
+
340
+ if this. tcx . sess . target . target . target_os . to_lowercase ( ) != "macos" {
341
+ throw_unsup_format ! ( "The `fstat` shim is only available for `macos` targets." )
342
+ }
343
+
344
+ let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
345
+
346
+ let metadata = match FileMetadata :: from_fd ( this, fd) ? {
347
+ Some ( metadata) => metadata,
348
+ None => return Ok ( -1 ) ,
349
+ } ;
350
+ stat_macos_write_buf ( this, metadata, buf_op)
351
+ }
352
+
331
353
fn stat_or_lstat (
332
354
& mut self ,
333
355
follow_symlink : bool ,
@@ -343,66 +365,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
343
365
let path_scalar = this. read_scalar ( path_op) ?. not_undef ( ) ?;
344
366
let path: PathBuf = this. read_os_str_from_c_str ( path_scalar) ?. into ( ) ;
345
367
346
- let buf = this. deref_operand ( buf_op) ?;
347
-
348
- let metadata = match FileMetadata :: new ( this, path, follow_symlink) ? {
368
+ let metadata = match FileMetadata :: from_path ( this, path, follow_symlink) ? {
349
369
Some ( metadata) => metadata,
350
370
None => return Ok ( -1 ) ,
351
371
} ;
352
-
353
- let mode: u16 = metadata. mode . to_u16 ( ) ?;
354
-
355
- let ( access_sec, access_nsec) = metadata. accessed . unwrap_or ( ( 0 , 0 ) ) ;
356
- let ( created_sec, created_nsec) = metadata. created . unwrap_or ( ( 0 , 0 ) ) ;
357
- let ( modified_sec, modified_nsec) = metadata. modified . unwrap_or ( ( 0 , 0 ) ) ;
358
-
359
- let dev_t_layout = this. libc_ty_layout ( "dev_t" ) ?;
360
- let mode_t_layout = this. libc_ty_layout ( "mode_t" ) ?;
361
- let nlink_t_layout = this. libc_ty_layout ( "nlink_t" ) ?;
362
- let ino_t_layout = this. libc_ty_layout ( "ino_t" ) ?;
363
- let uid_t_layout = this. libc_ty_layout ( "uid_t" ) ?;
364
- let gid_t_layout = this. libc_ty_layout ( "gid_t" ) ?;
365
- let time_t_layout = this. libc_ty_layout ( "time_t" ) ?;
366
- let long_layout = this. libc_ty_layout ( "c_long" ) ?;
367
- let off_t_layout = this. libc_ty_layout ( "off_t" ) ?;
368
- let blkcnt_t_layout = this. libc_ty_layout ( "blkcnt_t" ) ?;
369
- let blksize_t_layout = this. libc_ty_layout ( "blksize_t" ) ?;
370
- let uint32_t_layout = this. libc_ty_layout ( "uint32_t" ) ?;
371
-
372
- // We need to add 32 bits of padding after `st_rdev` if we are on a 64-bit platform.
373
- let pad_layout = if this. tcx . sess . target . ptr_width == 64 {
374
- uint32_t_layout
375
- } else {
376
- this. layout_of ( this. tcx . mk_unit ( ) ) ?
377
- } ;
378
-
379
- let imms = [
380
- immty_from_uint_checked ( 0u128 , dev_t_layout) ?, // st_dev
381
- immty_from_uint_checked ( mode, mode_t_layout) ?, // st_mode
382
- immty_from_uint_checked ( 0u128 , nlink_t_layout) ?, // st_nlink
383
- immty_from_uint_checked ( 0u128 , ino_t_layout) ?, // st_ino
384
- immty_from_uint_checked ( 0u128 , uid_t_layout) ?, // st_uid
385
- immty_from_uint_checked ( 0u128 , gid_t_layout) ?, // st_gid
386
- immty_from_uint_checked ( 0u128 , dev_t_layout) ?, // st_rdev
387
- immty_from_uint_checked ( 0u128 , pad_layout) ?, // padding for 64-bit targets
388
- immty_from_uint_checked ( access_sec, time_t_layout) ?, // st_atime
389
- immty_from_uint_checked ( access_nsec, long_layout) ?, // st_atime_nsec
390
- immty_from_uint_checked ( modified_sec, time_t_layout) ?, // st_mtime
391
- immty_from_uint_checked ( modified_nsec, long_layout) ?, // st_mtime_nsec
392
- immty_from_uint_checked ( 0u128 , time_t_layout) ?, // st_ctime
393
- immty_from_uint_checked ( 0u128 , long_layout) ?, // st_ctime_nsec
394
- immty_from_uint_checked ( created_sec, time_t_layout) ?, // st_birthtime
395
- immty_from_uint_checked ( created_nsec, long_layout) ?, // st_birthtime_nsec
396
- immty_from_uint_checked ( metadata. size , off_t_layout) ?, // st_size
397
- immty_from_uint_checked ( 0u128 , blkcnt_t_layout) ?, // st_blocks
398
- immty_from_uint_checked ( 0u128 , blksize_t_layout) ?, // st_blksize
399
- immty_from_uint_checked ( 0u128 , uint32_t_layout) ?, // st_flags
400
- immty_from_uint_checked ( 0u128 , uint32_t_layout) ?, // st_gen
401
- ] ;
402
-
403
- this. write_packed_immediates ( & buf, & imms) ?;
404
-
405
- Ok ( 0 )
372
+ stat_macos_write_buf ( this, metadata, buf_op)
406
373
}
407
374
408
375
fn statx (
@@ -454,18 +421,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
454
421
this. read_scalar ( flags_op) ?. to_machine_isize ( & * this. tcx ) ?. try_into ( ) . map_err ( |e| {
455
422
err_unsup_format ! ( "Failed to convert pointer sized operand to integer: {}" , e)
456
423
} ) ?;
424
+ let empty_path_flag = flags & this. eval_libc ( "AT_EMPTY_PATH" ) ?. to_i32 ( ) ? != 0 ;
457
425
// `dirfd` should be a `c_int` but the `syscall` function provides an `isize`.
458
426
let dirfd: i32 =
459
427
this. read_scalar ( dirfd_op) ?. to_machine_isize ( & * this. tcx ) ?. try_into ( ) . map_err ( |e| {
460
428
err_unsup_format ! ( "Failed to convert pointer sized operand to integer: {}" , e)
461
429
} ) ?;
462
- // we only support interpreting `path` as an absolute directory or as a directory relative
463
- // to `dirfd` when the latter is `AT_FDCWD`. The behavior of `statx` with a relative path
464
- // and a directory file descriptor other than `AT_FDCWD` is specified but it cannot be
465
- // tested from `libstd`. If you found this error, please open an issue reporting it.
466
- if !( path. is_absolute ( ) || dirfd == this. eval_libc_i32 ( "AT_FDCWD" ) ?) {
430
+ // We only support:
431
+ // * interpreting `path` as an absolute directory,
432
+ // * interpreting `path` as a path relative to `dirfd` when the latter is `AT_FDCWD`, or
433
+ // * interpreting `dirfd` as any file descriptor when `path` is empty and AT_EMPTY_PATH is
434
+ // set.
435
+ // Other behaviors cannot be tested from `libstd` and thus are not implemented. If you
436
+ // found this error, please open an issue reporting it.
437
+ if !(
438
+ path. is_absolute ( ) ||
439
+ dirfd == this. eval_libc_i32 ( "AT_FDCWD" ) ? ||
440
+ ( path. as_os_str ( ) . is_empty ( ) && empty_path_flag)
441
+ ) {
467
442
throw_unsup_format ! (
468
- "Using statx with a relative path and a file descriptor different from `AT_FDCWD` is not supported"
443
+ "Using statx is only supported with absolute paths, relative paths with the file \
444
+ descriptor `AT_FDCWD`, and empty paths with the `AT_EMPTY_PATH` flag set and any \
445
+ file descriptor"
469
446
)
470
447
}
471
448
@@ -480,7 +457,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
480
457
// symbolic links.
481
458
let follow_symlink = flags & this. eval_libc ( "AT_SYMLINK_NOFOLLOW" ) ?. to_i32 ( ) ? == 0 ;
482
459
483
- let metadata = match FileMetadata :: new ( this, path, follow_symlink) ? {
460
+ // If the path is empty, and the AT_EMPTY_PATH flag is set, we query the open file
461
+ // represented by dirfd, whether it's a directory or otherwise.
462
+ let metadata = if path. as_os_str ( ) . is_empty ( ) && empty_path_flag {
463
+ FileMetadata :: from_fd ( this, dirfd) ?
464
+ } else {
465
+ FileMetadata :: from_path ( this, path, follow_symlink) ?
466
+ } ;
467
+ let metadata = match metadata {
484
468
Some ( metadata) => metadata,
485
469
None => return Ok ( -1 ) ,
486
470
} ;
@@ -549,7 +533,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
549
533
immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_dev_minor
550
534
] ;
551
535
552
- this. write_packed_immediates ( & statxbuf_place, & imms) ?;
536
+ this. write_packed_immediates ( statxbuf_place, & imms) ?;
553
537
554
538
Ok ( 0 )
555
539
}
@@ -589,7 +573,7 @@ struct FileMetadata {
589
573
}
590
574
591
575
impl FileMetadata {
592
- fn new < ' tcx , ' mir > (
576
+ fn from_path < ' tcx , ' mir > (
593
577
ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
594
578
path : PathBuf ,
595
579
follow_symlink : bool
@@ -600,6 +584,27 @@ impl FileMetadata {
600
584
std:: fs:: symlink_metadata ( path)
601
585
} ;
602
586
587
+ FileMetadata :: from_meta ( ecx, metadata)
588
+ }
589
+
590
+ fn from_fd < ' tcx , ' mir > (
591
+ ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
592
+ fd : i32 ,
593
+ ) -> InterpResult < ' tcx , Option < FileMetadata > > {
594
+ let option = ecx. machine . file_handler . handles . get ( & fd) ;
595
+ let handle = match option {
596
+ Some ( handle) => handle,
597
+ None => return ecx. handle_not_found ( ) . map ( |_: i32 | None ) ,
598
+ } ;
599
+ let metadata = handle. file . metadata ( ) ;
600
+
601
+ FileMetadata :: from_meta ( ecx, metadata)
602
+ }
603
+
604
+ fn from_meta < ' tcx , ' mir > (
605
+ ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
606
+ metadata : Result < std:: fs:: Metadata , std:: io:: Error > ,
607
+ ) -> InterpResult < ' tcx , Option < FileMetadata > > {
603
608
let metadata = match metadata {
604
609
Ok ( metadata) => metadata,
605
610
Err ( e) => {
@@ -630,3 +635,64 @@ impl FileMetadata {
630
635
Ok ( Some ( FileMetadata { mode, size, created, accessed, modified } ) )
631
636
}
632
637
}
638
+
639
+ fn stat_macos_write_buf < ' tcx , ' mir > (
640
+ ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
641
+ metadata : FileMetadata ,
642
+ buf_op : OpTy < ' tcx , Tag > ,
643
+ ) -> InterpResult < ' tcx , i32 > {
644
+ let mode: u16 = metadata. mode . to_u16 ( ) ?;
645
+
646
+ let ( access_sec, access_nsec) = metadata. accessed . unwrap_or ( ( 0 , 0 ) ) ;
647
+ let ( created_sec, created_nsec) = metadata. created . unwrap_or ( ( 0 , 0 ) ) ;
648
+ let ( modified_sec, modified_nsec) = metadata. modified . unwrap_or ( ( 0 , 0 ) ) ;
649
+
650
+ let dev_t_layout = ecx. libc_ty_layout ( "dev_t" ) ?;
651
+ let mode_t_layout = ecx. libc_ty_layout ( "mode_t" ) ?;
652
+ let nlink_t_layout = ecx. libc_ty_layout ( "nlink_t" ) ?;
653
+ let ino_t_layout = ecx. libc_ty_layout ( "ino_t" ) ?;
654
+ let uid_t_layout = ecx. libc_ty_layout ( "uid_t" ) ?;
655
+ let gid_t_layout = ecx. libc_ty_layout ( "gid_t" ) ?;
656
+ let time_t_layout = ecx. libc_ty_layout ( "time_t" ) ?;
657
+ let long_layout = ecx. libc_ty_layout ( "c_long" ) ?;
658
+ let off_t_layout = ecx. libc_ty_layout ( "off_t" ) ?;
659
+ let blkcnt_t_layout = ecx. libc_ty_layout ( "blkcnt_t" ) ?;
660
+ let blksize_t_layout = ecx. libc_ty_layout ( "blksize_t" ) ?;
661
+ let uint32_t_layout = ecx. libc_ty_layout ( "uint32_t" ) ?;
662
+
663
+ // We need to add 32 bits of padding after `st_rdev` if we are on a 64-bit platform.
664
+ let pad_layout = if ecx. tcx . sess . target . ptr_width == 64 {
665
+ uint32_t_layout
666
+ } else {
667
+ ecx. layout_of ( ecx. tcx . mk_unit ( ) ) ?
668
+ } ;
669
+
670
+ let imms = [
671
+ immty_from_uint_checked ( 0u128 , dev_t_layout) ?, // st_dev
672
+ immty_from_uint_checked ( mode, mode_t_layout) ?, // st_mode
673
+ immty_from_uint_checked ( 0u128 , nlink_t_layout) ?, // st_nlink
674
+ immty_from_uint_checked ( 0u128 , ino_t_layout) ?, // st_ino
675
+ immty_from_uint_checked ( 0u128 , uid_t_layout) ?, // st_uid
676
+ immty_from_uint_checked ( 0u128 , gid_t_layout) ?, // st_gid
677
+ immty_from_uint_checked ( 0u128 , dev_t_layout) ?, // st_rdev
678
+ immty_from_uint_checked ( 0u128 , pad_layout) ?, // padding for 64-bit targets
679
+ immty_from_uint_checked ( access_sec, time_t_layout) ?, // st_atime
680
+ immty_from_uint_checked ( access_nsec, long_layout) ?, // st_atime_nsec
681
+ immty_from_uint_checked ( modified_sec, time_t_layout) ?, // st_mtime
682
+ immty_from_uint_checked ( modified_nsec, long_layout) ?, // st_mtime_nsec
683
+ immty_from_uint_checked ( 0u128 , time_t_layout) ?, // st_ctime
684
+ immty_from_uint_checked ( 0u128 , long_layout) ?, // st_ctime_nsec
685
+ immty_from_uint_checked ( created_sec, time_t_layout) ?, // st_birthtime
686
+ immty_from_uint_checked ( created_nsec, long_layout) ?, // st_birthtime_nsec
687
+ immty_from_uint_checked ( metadata. size , off_t_layout) ?, // st_size
688
+ immty_from_uint_checked ( 0u128 , blkcnt_t_layout) ?, // st_blocks
689
+ immty_from_uint_checked ( 0u128 , blksize_t_layout) ?, // st_blksize
690
+ immty_from_uint_checked ( 0u128 , uint32_t_layout) ?, // st_flags
691
+ immty_from_uint_checked ( 0u128 , uint32_t_layout) ?, // st_gen
692
+ ] ;
693
+
694
+ let buf = ecx. deref_operand ( buf_op) ?;
695
+ ecx. write_packed_immediates ( buf, & imms) ?;
696
+
697
+ Ok ( 0 )
698
+ }
0 commit comments