@@ -328,6 +328,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
328328 this. stat_or_lstat ( false , path_op, buf_op)
329329 }
330330
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+
331353 fn stat_or_lstat (
332354 & mut self ,
333355 follow_symlink : bool ,
@@ -343,66 +365,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
343365 let path_scalar = this. read_scalar ( path_op) ?. not_undef ( ) ?;
344366 let path: PathBuf = this. read_os_str_from_c_str ( path_scalar) ?. into ( ) ;
345367
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) ? {
349369 Some ( metadata) => metadata,
350370 None => return Ok ( -1 ) ,
351371 } ;
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)
406373 }
407374
408375 fn statx (
@@ -454,18 +421,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
454421 this. read_scalar ( flags_op) ?. to_machine_isize ( & * this. tcx ) ?. try_into ( ) . map_err ( |e| {
455422 err_unsup_format ! ( "Failed to convert pointer sized operand to integer: {}" , e)
456423 } ) ?;
424+ let empty_path_flag = flags & this. eval_libc ( "AT_EMPTY_PATH" ) ?. to_i32 ( ) ? != 0 ;
457425 // `dirfd` should be a `c_int` but the `syscall` function provides an `isize`.
458426 let dirfd: i32 =
459427 this. read_scalar ( dirfd_op) ?. to_machine_isize ( & * this. tcx ) ?. try_into ( ) . map_err ( |e| {
460428 err_unsup_format ! ( "Failed to convert pointer sized operand to integer: {}" , e)
461429 } ) ?;
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+ ) {
467442 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"
469446 )
470447 }
471448
@@ -480,7 +457,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
480457 // symbolic links.
481458 let follow_symlink = flags & this. eval_libc ( "AT_SYMLINK_NOFOLLOW" ) ?. to_i32 ( ) ? == 0 ;
482459
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 {
484468 Some ( metadata) => metadata,
485469 None => return Ok ( -1 ) ,
486470 } ;
@@ -549,7 +533,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
549533 immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_dev_minor
550534 ] ;
551535
552- this. write_packed_immediates ( & statxbuf_place, & imms) ?;
536+ this. write_packed_immediates ( statxbuf_place, & imms) ?;
553537
554538 Ok ( 0 )
555539 }
@@ -589,7 +573,7 @@ struct FileMetadata {
589573}
590574
591575impl FileMetadata {
592- fn new < ' tcx , ' mir > (
576+ fn from_path < ' tcx , ' mir > (
593577 ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
594578 path : PathBuf ,
595579 follow_symlink : bool
@@ -600,6 +584,27 @@ impl FileMetadata {
600584 std:: fs:: symlink_metadata ( path)
601585 } ;
602586
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 > > {
603608 let metadata = match metadata {
604609 Ok ( metadata) => metadata,
605610 Err ( e) => {
@@ -630,3 +635,64 @@ impl FileMetadata {
630635 Ok ( Some ( FileMetadata { mode, size, created, accessed, modified } ) )
631636 }
632637}
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