Skip to content

Commit d94b88e

Browse files
committed
Add support for AT_EMPTY_PATH to statx shim
1 parent b222677 commit d94b88e

File tree

2 files changed

+39
-4
lines changed

2 files changed

+39
-4
lines changed

src/shims/fs.rs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

591603
impl 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) => {

tests/run-pass/fs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ fn main() {
2929
let mut file = File::create(&path).unwrap();
3030
// Writing 0 bytes should not change the file contents.
3131
file.write(&mut []).unwrap();
32+
assert_eq!(file.metadata().unwrap().len(), 0);
3233

3334
file.write(bytes).unwrap();
35+
assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64);
3436
// Test opening, reading and closing a file.
3537
let mut file = File::open(&path).unwrap();
3638
let mut contents = Vec::new();

0 commit comments

Comments
 (0)