Skip to content

Commit 2958f06

Browse files
authored
Merge pull request #4589 from LorrensP-2158466/freebsd-readdir
support `readdir` on FreeBSD
2 parents be1ffc3 + 0660d4c commit 2958f06

File tree

5 files changed

+46
-27
lines changed

5 files changed

+46
-27
lines changed

src/shims/unix/freebsd/foreign_items.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
159159
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
160160
this.write_scalar(result, dest)?;
161161
}
162-
162+
"readdir" | "readdir@FBSD_1.0" => {
163+
let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
164+
let result = this.readdir64("dirent", dirp)?;
165+
this.write_scalar(result, dest)?;
166+
}
163167
// Miscellaneous
164168
"__error" => {
165169
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;

src/shims/unix/fs.rs

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -900,14 +900,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
900900
}
901901
}
902902

903-
fn linux_solarish_readdir64(
904-
&mut self,
905-
dirent_type: &str,
906-
dirp_op: &OpTy<'tcx>,
907-
) -> InterpResult<'tcx, Scalar> {
903+
fn readdir64(&mut self, dirent_type: &str, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
908904
let this = self.eval_context_mut();
909905

910-
if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") {
906+
if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos" | "freebsd") {
911907
panic!("`linux_solaris_readdir64` should not be called on {}", this.tcx.sess.target.os);
912908
}
913909

@@ -926,6 +922,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
926922

927923
let entry = match open_dir.read_dir.next() {
928924
Some(Ok(dir_entry)) => {
925+
// If the host is a Unix system, fill in the inode number with its real value.
926+
// If not, use 0 as a fallback value.
927+
#[cfg(unix)]
928+
let ino = std::os::unix::fs::DirEntryExt::ino(&dir_entry);
929+
#[cfg(not(unix))]
930+
let ino = 0u64;
931+
929932
// Write the directory entry into a newly allocated buffer.
930933
// The name is written with write_bytes, while the rest of the
931934
// dirent64 (or dirent) struct is written using write_int_fields.
@@ -947,6 +950,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
947950
// pub d_reclen: c_ushort,
948951
// pub d_name: [c_char; 3],
949952
// }
953+
//
954+
// On FreeBSD:
955+
// pub struct dirent{
956+
// pub d_fileno: uint32_t,
957+
// pub d_reclen: uint16_t,
958+
// pub d_type: uint8_t,
959+
// pub d_namlen: uint8_t,
960+
// pub d_name: [c_char; 256]
961+
// }
950962

951963
let mut name = dir_entry.file_name(); // not a Path as there are no separators!
952964
name.push("\0"); // Add a NUL terminator
@@ -965,31 +977,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
965977
MiriMemoryKind::Runtime.into(),
966978
AllocInit::Uninit,
967979
)?;
968-
let entry: Pointer = entry.into();
980+
let entry = this.ptr_to_mplace(entry.into(), dirent_layout);
969981

970-
// If the host is a Unix system, fill in the inode number with its real value.
971-
// If not, use 0 as a fallback value.
972-
#[cfg(unix)]
973-
let ino = std::os::unix::fs::DirEntryExt::ino(&dir_entry);
974-
#[cfg(not(unix))]
975-
let ino = 0u64;
976-
977-
let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
982+
// Write common fields
983+
let ino_name =
984+
if this.tcx.sess.target.os == "freebsd" { "d_fileno" } else { "d_ino" };
978985
this.write_int_fields_named(
979-
&[("d_ino", ino.into()), ("d_off", 0), ("d_reclen", size.into())],
980-
&this.ptr_to_mplace(entry, dirent_layout),
986+
&[(ino_name, ino.into()), ("d_reclen", size.into())],
987+
&entry,
981988
)?;
982989

983-
if let Some(d_type) = this
984-
.try_project_field_named(&this.ptr_to_mplace(entry, dirent_layout), "d_type")?
985-
{
990+
// Write "optional" fields.
991+
if let Some(d_off) = this.try_project_field_named(&entry, "d_off")? {
992+
this.write_null(&d_off)?;
993+
}
994+
995+
if let Some(d_namlen) = this.try_project_field_named(&entry, "d_namlen")? {
996+
this.write_int(name_len.strict_sub(1), &d_namlen)?;
997+
}
998+
999+
let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
1000+
if let Some(d_type) = this.try_project_field_named(&entry, "d_type")? {
9861001
this.write_int(file_type, &d_type)?;
9871002
}
9881003

989-
let name_ptr = entry.wrapping_offset(Size::from_bytes(d_name_offset), this);
1004+
// The name is not a normal field, we already computed the offset above.
1005+
let name_ptr = entry.ptr().wrapping_offset(Size::from_bytes(d_name_offset), this);
9901006
this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?;
9911007

992-
Some(entry)
1008+
Some(entry.ptr())
9931009
}
9941010
None => {
9951011
// end of stream: return NULL

src/shims/unix/linux/foreign_items.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
3838
// File related shims
3939
"readdir64" => {
4040
let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
41-
let result = this.linux_solarish_readdir64("dirent64", dirp)?;
41+
let result = this.readdir64("dirent64", dirp)?;
4242
this.write_scalar(result, dest)?;
4343
}
4444
"sync_file_range" => {

src/shims/unix/solarish/foreign_items.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
106106
}
107107
"readdir" => {
108108
let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
109-
let result = this.linux_solarish_readdir64("dirent", dirp)?;
109+
let result = this.readdir64("dirent", dirp)?;
110110
this.write_scalar(result, dest)?;
111111
}
112112

tests/pass/shims/fs.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ fn main() {
2828
test_from_raw_os_error();
2929
test_file_clone();
3030
// Windows file handling is very incomplete.
31-
// FIXME: read_dir broken on FreeBSD (https://github.com/rust-lang/miri/issues/4587)
32-
if cfg!(not(windows)) && !cfg!(target_os = "freebsd") {
31+
if cfg!(not(windows)) {
3332
test_file_set_len();
3433
test_file_sync();
3534
test_rename();

0 commit comments

Comments
 (0)