-
-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Follows macOS/APFS "firmlinks" even with .follow_links(false)
#169
Comments
I'm not sure what the expectation is here. If macOS doesn't report them as symlinks, then that's what macOS has decided: they shouldn't be regarded as symlinks. |
Well the programmer normally has an expectation that walking a directory only visits each directory once. But perhaps my chosen wording naturally leaves to a literalist interpretation but even then the option itself talks about "links" not about "symlinks". Perhaps a discussion would include alternatives such as:
|
With respect to naming:
So the docs are already clear that it's just about symlinks. I'm definitely not going to rename it. And renaming it just because macOS decided to introduce some new weird version of links also seems like a bad way to prioritize things. I'm inclined to:
|
And also:
If that's true, then why doesn't macOS report firm links as symlinks? Like, why pin the responsibility on me here and not on macOS? They introduced firmlinks and they decided not to report them as symlinks. |
Out of curiosity I wanted to know how you would go about detecting a 'firmlink', seems possible using libc. You would probably want to follow the 'firmlink' (the short version) and ignore a directory that had a 'firmlink'. It's super confusing but it is doable. #[cfg(test)]
mod tests {
#[test]
fn test_libc_detect_firmlink() {
let app_path_system = std::ffi::CString::new("/System/Volumes/Data/Applications").unwrap();
let app_path_root = std::ffi::CString::new("/Applications").unwrap();
let fd_system = unsafe {
libc::open(
app_path_system.as_ptr(),
libc::O_NONBLOCK,
libc::O_DIRECTORY,
)
};
let fd_root = unsafe {
libc::open(
app_path_system.as_ptr(),
libc::O_NONBLOCK,
libc::O_DIRECTORY,
)
};
assert!(fd_system != -1);
assert!(fd_root != -1);
let mut buffer = vec![0; libc::PATH_MAX as usize];
let r = unsafe { libc::fcntl(fd_system, libc::F_GETPATH, buffer.as_mut_ptr()) };
assert!(r == 0);
let get_path_system = std::ffi::CStr::from_bytes_until_nul(&buffer).unwrap();
println!("(F_GETPATH) -- fd_system: {:?}", get_path_system);
let mut buffer = vec![0; libc::PATH_MAX as usize];
let r = unsafe { libc::fcntl(fd_root, libc::F_GETPATH, buffer.as_mut_ptr()) };
assert!(r == 0);
let get_path_root = std::ffi::CStr::from_bytes_until_nul(&buffer).unwrap();
println!("(F_GETPATH) -- fd_root: {:?}", get_path_root);
let mut buffer = vec![0; libc::PATH_MAX as usize];
let r = unsafe { libc::fcntl(fd_system, libc::F_GETPATH_NOFIRMLINK, buffer.as_mut_ptr()) };
assert!(r == 0);
let get_path_nofirmlink_system = std::ffi::CStr::from_bytes_until_nul(&buffer).unwrap();
println!(
"(F_GETPATH_NOFIRMLINK) -- fd_system: {:?}",
get_path_nofirmlink_system
);
let mut buffer = vec![0; libc::PATH_MAX as usize];
let r = unsafe { libc::fcntl(fd_root, libc::F_GETPATH_NOFIRMLINK, buffer.as_mut_ptr()) };
assert!(r == 0);
let get_path_nofirmlink_root = std::ffi::CStr::from_bytes_until_nul(&buffer).unwrap();
println!(
"(F_GETPATH_NOFIRMLINK) -- fd_root: {:?}",
get_path_nofirmlink_root
);
println!(
"path: {:?} is a firmlink: {}",
app_path_root,
app_path_root.as_c_str() != get_path_nofirmlink_root
);
println!(
"path: {:?} is a firmlink: {}",
app_path_system,
app_path_system.as_c_str() != get_path_nofirmlink_root
);
}
} output: (F_GETPATH) -- fd_system: "/Applications"
(F_GETPATH) -- fd_root: "/Applications"
(F_GETPATH_NOFIRMLINK) -- fd_system: "/System/Volumes/Data/Applications"
(F_GETPATH_NOFIRMLINK) -- fd_root: "/System/Volumes/Data/Applications"
path: "/Applications" is a firmlink: true
path: "/System/Volumes/Data/Applications" is a firmlink: false |
In case anyone is looking for info on detecting firmlinks in Darwin/macOS, apparently the only official way to do it is to use |
macOS with APFS has a feature called "firmlinks" which are sometimes described as being between hardlinks and symlinks. They're used to make two system partitions appear like the old single partition scheme. Certain directories that live in
/System/Volumes/Data/xyz
are firmlinked to/xyz
Swift's standard library is aware of these and its dir walking functionality does not follow them. Rust's walkdir is not aware of them and does follow them. (Note that there's no commandline switches for mac's
ls
that reveal them)I wrote similar code for both Swift and Rust. It's probably not the best, I'm just learning both languages. First argument is path the walk begins, second is a substring to match in the name of a directory to cause it to be printed out.
Rust:
cargo run / LLVM
Swift:
dirwalker / LLVM
Rust code:
Swift code:
The text was updated successfully, but these errors were encountered: