Skip to content

Commit

Permalink
solarish stat following-up, supports for readdir.
Browse files Browse the repository at this point in the history
  • Loading branch information
devnexen committed Dec 6, 2024
1 parent b27f1d6 commit 7e2a9f2
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 27 deletions.
25 changes: 17 additions & 8 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,18 +309,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}

/// Project to the given *named* field (which must be a struct or union type).
fn project_field_named<P: Projectable<'tcx, Provenance>>(
fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
&self,
base: &P,
name: &str,
) -> InterpResult<'tcx, P> {
) -> InterpResult<'tcx, Option<P>> {
let this = self.eval_context_ref();
let adt = base.layout().ty.ty_adt_def().unwrap();
for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() {
if field.name.as_str() == name {
return this.project_field(base, idx);
return interp_ok(Some(this.project_field(base, idx)?));
}
}
interp_ok(None)
}

/// Project to the given *named* field (which must be a struct or union type).
fn project_field_named<P: Projectable<'tcx, Provenance>>(
&self,
base: &P,
name: &str,
) -> InterpResult<'tcx, P> {
if let Some(field) = self.try_project_field_named(base, name)? {
return interp_ok(field);
}
bug!("No field named {} in type {}", name, base.layout().ty);
}

Expand All @@ -330,11 +342,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
base: &P,
name: &str,
) -> bool {
let adt = base.layout().ty.ty_adt_def().unwrap();
for field in adt.non_enum_variant().fields.iter() {
if field.name.as_str() == name {
return true;
}
if let Some(_f) = self.try_project_field_named(base, name).unwrap() {
return true;
}
false
}
Expand Down
44 changes: 32 additions & 12 deletions src/shims/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1048,10 +1048,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}

fn linux_readdir64(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
fn linux_solarish_readdir64(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();

this.assert_target_os("linux", "readdir64");
if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") {
panic!("`linux_solaris_readdir64` should not be called on {}", this.tcx.sess.target.os);
}

let dirp = this.read_target_usize(dirp_op)?;

Expand All @@ -1070,24 +1072,41 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Some(Ok(dir_entry)) => {
// Write the directory entry into a newly allocated buffer.
// The name is written with write_bytes, while the rest of the
// dirent64 struct is written using write_int_fields.
// dirent64 (or dirent) struct is written using write_int_fields.

// For reference:
// On Linux:
// pub struct dirent64 {
// pub d_ino: ino64_t,
// pub d_off: off64_t,
// pub d_reclen: c_ushort,
// pub d_type: c_uchar,
// pub d_name: [c_char; 256],
// }
//
// On Solaris:
// pub struct dirent {
// pub d_ino: ino64_t,
// pub d_off: off64_t,
// pub d_reclen: c_ushort,
// pub d_name: [c_char; 3],
// }

let mut name = dir_entry.file_name(); // not a Path as there are no separators!
name.push("\0"); // Add a NUL terminator
let name_bytes = name.as_encoded_bytes();
let name_len = u64::try_from(name_bytes.len()).unwrap();

let dirent64_layout = this.libc_ty_layout("dirent64");
let d_name_offset = dirent64_layout.fields.offset(4 /* d_name */).bytes();
let is_linux = matches!(&*this.tcx.sess.target.os, "linux");

let dirent64_layout = if is_linux {
this.libc_ty_layout("dirent64")
} else {
this.libc_ty_layout("dirent")
};
let fields = &dirent64_layout.fields;
let last_field = (*fields).count().strict_sub(1);
let d_name_offset = (*fields).offset(last_field).bytes();
let size = d_name_offset.strict_add(name_len);

let entry = this.allocate_ptr(
Expand All @@ -1105,17 +1124,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let ino = 0u64;

let file_type = this.file_type_to_d_type(dir_entry.file_type())?;

this.write_int_fields_named(
&[
("d_ino", ino.into()),
("d_off", 0),
("d_reclen", size.into()),
("d_type", file_type.into()),
],
&[("d_ino", ino.into()), ("d_off", 0), ("d_reclen", size.into())],
&this.ptr_to_mplace(entry, dirent64_layout),
)?;

if let Some(d_type) = this.try_project_field_named(
&this.ptr_to_mplace(entry, dirent64_layout),
"d_type",
)? {
this.write_int(file_type, &d_type)?;
}

let name_ptr = entry.wrapping_offset(Size::from_bytes(d_name_offset), this);
this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?;

Expand Down
2 changes: 1 addition & 1 deletion src/shims/unix/linux/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"readdir64" => {
let [dirp] =
this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
let result = this.linux_readdir64(dirp)?;
let result = this.linux_solarish_readdir64(dirp)?;
this.write_scalar(result, dest)?;
}
"sync_file_range" => {
Expand Down
6 changes: 6 additions & 0 deletions src/shims/unix/solarish/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"readdir" => {
let [dirp] =
this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
let result = this.linux_solarish_readdir64(dirp)?;
this.write_scalar(result, dest)?;
}

// Miscellaneous
"___errno" => {
Expand Down
14 changes: 8 additions & 6 deletions tests/pass/shims/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,8 @@ fn main() {
test_file_sync();
test_errors();
test_rename();
// solarish needs to support readdir/readdir64 for these tests.
if cfg!(not(any(target_os = "solaris", target_os = "illumos"))) {
test_directory();
test_canonicalize();
}
test_directory();
test_canonicalize();
test_from_raw_os_error();
#[cfg(unix)]
test_pread_pwrite();
Expand Down Expand Up @@ -279,7 +276,12 @@ fn test_directory() {
.collect::<BTreeMap<_, _>>()
);
// Deleting the directory should fail, since it is not empty.
assert_eq!(ErrorKind::DirectoryNotEmpty, remove_dir(&dir_path).unwrap_err().kind());

// Solaris/Illumos `rmdir` call set errno to EEXIST if directory contains
// other entries than `.` and `..`.
// https://docs.oracle.com/cd/E86824_01/html/E54765/rmdir-2.html
let err = remove_dir(&dir_path).unwrap_err().kind();
assert!(matches!(err, ErrorKind::AlreadyExists | ErrorKind::DirectoryNotEmpty));
// Clean up the files in the directory
remove_file(&path_1).unwrap();
remove_file(&path_2).unwrap();
Expand Down

0 comments on commit 7e2a9f2

Please sign in to comment.