@@ -345,7 +345,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
345345
346346 let buf = this. deref_operand ( buf_op) ?;
347347
348- let metadata = match FileMetadata :: new ( this, path, follow_symlink) ? {
348+ let metadata = match FileMetadata :: from_path ( this, path, follow_symlink) ? {
349349 Some ( metadata) => metadata,
350350 None => return Ok ( -1 ) ,
351351 } ;
@@ -454,6 +454,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
454454 this. read_scalar ( flags_op) ?. to_machine_isize ( & * this. tcx ) ?. try_into ( ) . map_err ( |e| {
455455 err_unsup_format ! ( "Failed to convert pointer sized operand to integer: {}" , e)
456456 } ) ?;
457+ let empty_path_flag = flags & this. eval_libc ( "AT_EMPTY_PATH" ) ?. to_i32 ( ) ? != 0 ;
457458 // `dirfd` should be a `c_int` but the `syscall` function provides an `isize`.
458459 let dirfd: i32 =
459460 this. read_scalar ( dirfd_op) ?. to_machine_isize ( & * this. tcx ) ?. try_into ( ) . map_err ( |e| {
@@ -463,7 +464,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
463464 // to `dirfd` when the latter is `AT_FDCWD`. The behavior of `statx` with a relative path
464465 // and a directory file descriptor other than `AT_FDCWD` is specified but it cannot be
465466 // 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" ) ?) {
467+ if !(
468+ path. is_absolute ( ) ||
469+ dirfd == this. eval_libc_i32 ( "AT_FDCWD" ) ? ||
470+ ( path. as_os_str ( ) . is_empty ( ) && empty_path_flag)
471+ ) {
467472 throw_unsup_format ! (
468473 "Using statx with a relative path and a file descriptor different from `AT_FDCWD` is not supported"
469474 )
@@ -480,7 +485,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
480485 // symbolic links.
481486 let follow_symlink = flags & this. eval_libc ( "AT_SYMLINK_NOFOLLOW" ) ?. to_i32 ( ) ? == 0 ;
482487
483- let metadata = match FileMetadata :: new ( this, path, follow_symlink) ? {
488+ // If the path is empty, and the AT_EMPTY_PATH flag is set, we query the open file
489+ // represented by dirfd, whether it's a directory or otherwise.
490+ let metadata = if path. as_os_str ( ) . is_empty ( ) && empty_path_flag {
491+ FileMetadata :: from_fd ( this, dirfd) ?
492+ } else {
493+ FileMetadata :: from_path ( this, path, follow_symlink) ?
494+ } ;
495+ let metadata = match metadata {
484496 Some ( metadata) => metadata,
485497 None => return Ok ( -1 ) ,
486498 } ;
@@ -589,7 +601,7 @@ struct FileMetadata {
589601}
590602
591603impl FileMetadata {
592- fn new < ' tcx , ' mir > (
604+ fn from_path < ' tcx , ' mir > (
593605 ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
594606 path : PathBuf ,
595607 follow_symlink : bool
@@ -600,6 +612,27 @@ impl FileMetadata {
600612 std:: fs:: symlink_metadata ( path)
601613 } ;
602614
615+ FileMetadata :: from_meta ( ecx, metadata)
616+ }
617+
618+ fn from_fd < ' tcx , ' mir > (
619+ ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
620+ fd : i32 ,
621+ ) -> InterpResult < ' tcx , Option < FileMetadata > > {
622+ let option = ecx. machine . file_handler . handles . get ( & fd) ;
623+ let handle = match option {
624+ Some ( handle) => handle,
625+ None => return ecx. handle_not_found ( ) . map ( |_: i32 | None ) ,
626+ } ;
627+ let metadata = handle. file . metadata ( ) ;
628+
629+ FileMetadata :: from_meta ( ecx, metadata)
630+ }
631+
632+ fn from_meta < ' tcx , ' mir > (
633+ ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
634+ metadata : Result < std:: fs:: Metadata , std:: io:: Error > ,
635+ ) -> InterpResult < ' tcx , Option < FileMetadata > > {
603636 let metadata = match metadata {
604637 Ok ( metadata) => metadata,
605638 Err ( e) => {
0 commit comments