1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#![allow(clippy::undocumented_unsafe_blocks)]
// Create a purposefully empty directory and test if the EOF trick is used by the kernel
#[cfg(target_os = "macos")]
#[allow(clippy::expect_used, clippy::let_underscore_must_use)]
fn test_eof() {
use std::os::unix::ffi::OsStrExt as _;
/*
https://github.com/apple-oss-distributions/Libc/blob/899a3b2d52d95d75e05fb286a5e64975ec3de757/gen/FreeBSD/opendir.c#L373-L392
#if __DARWIN_64_BIT_INO_T
/*
* sufficiently recent kernels when the buffer is large enough,
* will use the last bytes of the buffer to return status.
*
* To support older kernels:
* - make sure it's 0 initialized
* - make sure it's past `dd_size` before reading it
*/
getdirentries64_flags_t *gdeflags =
(getdirentries64_flags_t *)(dirp->dd_buf + dirp->dd_len -
sizeof(getdirentries64_flags_t));
*gdeflags = 0;
dirp->dd_size = (long)__getdirentries64(dirp->dd_fd,
dirp->dd_buf, dirp->dd_len, &dirp->dd_td->seekoff);
if (dirp->dd_size >= 0 &&
dirp->dd_size <= dirp->dd_len - sizeof(getdirentries64_flags_t)) {
if (*gdeflags & GETDIRENTRIES64_EOF) {
dirp->dd_flags |= __DTF_ATEND;
}
}
*/
// Tell cargo about the cfg we intend to use so `check-cfg` won't warn.
// link to libc (as done it in main crate)
unsafe extern "C" {
fn __getdirentries64(
fd: libc::c_int,
buf: *mut libc::c_char,
nbytes: libc::size_t,
basep: *mut libc::off_t,
) -> libc::ssize_t;
} // Compile error if this doesn't link.
const BUFFER_SIZE: usize = 4096;
let tmp = std::env::temp_dir();
let empty = tmp.join("MACOSEOFTESTINGDIR");
std::fs::create_dir_all(&empty).expect("MACOS empty dir not created!");
// Guaranteed null terminated
let empty_cstring = std::ffi::CString::new(empty.as_os_str().as_bytes())
.expect("temporary dir Cstring not created!");
const FLAGS: i32 = libc::O_CLOEXEC | libc::O_DIRECTORY | libc::O_NONBLOCK;
// SAFETY: guaranteed null terminated
let get_fd = unsafe { libc::open(empty_cstring.as_ptr(), FLAGS) };
assert!(get_fd > 0, "Unexpected error in opening temporary fd!");
let mut buffer = [0u8; BUFFER_SIZE];
let mut base_pointer = 0i64;
// SAFETY: valid buffer+size+pointer
unsafe {
__getdirentries64(
get_fd,
buffer.as_mut_ptr().cast(),
BUFFER_SIZE,
&raw mut base_pointer,
)
};
let last_four_bytes = &buffer[BUFFER_SIZE - 4..];
// We don't need to care about endianness since macos is LE only
//(well, tbf, I don't care for even googling if some old ass macos from 2002 on a big endian chinese esoteric CPU(on a thinkpad no less...) is BE
let has_eof = last_four_bytes == [1, 0, 0, 0];
// If the last 4 bytes are in this arrangement, we know that the kernel is using the EOF trick.
if has_eof {
println!("cargo:rustc-cfg=has_eof_trick")
}
// SAFETY: trivial.
unsafe { libc::close(get_fd) };
let _ = std::fs::remove_dir_all(&empty);
}
#[allow(clippy::unwrap_used)]
fn check_dirent_has_field(cfg_name: &str) {
// Tell cargo about the cfg we intend to use so `check-cfg` won't warn.
println!("cargo:rustc-check-cfg=cfg({cfg_name})");
let out = std::env::var("OUT_DIR").unwrap();
let c_file = format!("check_{cfg_name}.c");
let src = std::path::PathBuf::from(&out).join(&c_file);
// This C source fails to compile if the struct field is not present.
// We derive the field name from the `cfg_name`, which is of the form `has_<field>`.
let field_name = cfg_name.strip_prefix("has_").unwrap_or(cfg_name).to_owned();
assert!(
field_name.starts_with("d_"),
"Field name must start with d_"
);
let code = format!(
// use stddef.h to get offsetof
"#include <dirent.h>\n#include <stddef.h>\nstatic const size_t off = offsetof(struct dirent, {field_name});\nint main(void) {{ (void)off; return 0; }}\n",
);
std::fs::write(&src, code).unwrap();
let mut build = cc::Build::new();
build.file(&src).cargo_warnings(false).cargo_output(true);
if build.try_compile(&c_file).is_ok() {
// Enable the flag
println!("cargo:rustc-cfg={cfg_name}")
}
}
fn main() {
// Tell cargo about the cfg we intend to use so `check-cfg` won't warn.
println!("cargo:rustc-check-cfg=cfg(has_eof_trick)");
// Re-run build script if filesystem list changes
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
println!("cargo:rustc-env=FDF_PAGE_SIZE={page_size}");
#[cfg(target_os = "macos")]
test_eof();
check_dirent_has_field("has_d_type");
check_dirent_has_field("has_d_reclen");
check_dirent_has_field("has_d_namlen");
check_dirent_has_field("has_d_ino");
}