-
Notifications
You must be signed in to change notification settings - Fork 38
Return file creation times on Linux (using statx) #248
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
Changes from 6 commits
1307013
0b7dc95
26270cd
c7f65a2
6112c88
29cd8dd
2eca023
5aa6a77
7cee463
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,14 @@ use rustix::fs::{statat, AtFlags}; | |
use std::path::Path; | ||
use std::{fs, io}; | ||
|
||
// TODO: update all these to | ||
// #[cfg(any(target_os = "android", target_os = "linux"))] | ||
// once we're on restix >= v0.34.3. | ||
#[cfg(all(target_os = "linux", target_env = "gnu"))] | ||
use rustix::fs::{statx, StatxFlags}; | ||
#[cfg(all(target_os = "linux", target_env = "gnu"))] | ||
use std::sync::atomic::{AtomicBool, Ordering}; | ||
|
||
/// *Unsandboxed* function similar to `stat`, but which does not perform | ||
/// sandboxing. | ||
pub(crate) fn stat_unchecked( | ||
|
@@ -15,5 +23,26 @@ pub(crate) fn stat_unchecked( | |
FollowSymlinks::No => AtFlags::SYMLINK_NOFOLLOW, | ||
}; | ||
|
||
// `statx` is preferred on Linux because it can return creation times. | ||
// Linux kernels prior to 4.11 don't have `statx` and return `ENOSYS`. We | ||
// store the availability in a global to avoid unnecessary syscalls. | ||
#[cfg(all(target_os = "linux", target_env = "gnu"))] | ||
static HAS_STATX: AtomicBool = AtomicBool::new(true); | ||
|
||
#[cfg(all(target_os = "linux", target_env = "gnu"))] | ||
if HAS_STATX.load(Ordering::Relaxed) { | ||
let statx_result = statx( | ||
start, | ||
path, | ||
atflags, | ||
StatxFlags::BASIC_STATS | StatxFlags::BTIME, | ||
); | ||
match statx_result { | ||
Ok(statx) => return Ok(MetadataExt::from_rustix_statx(statx)), | ||
Err(rustix::io::Error::NOSYS) => HAS_STATX.store(false, Ordering::Relaxed), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw a comment in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rust's own standard library calls Looking into it though, I notice that there is a potential problem with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch. It seems like if we merge this PR now, it'd break metadata for not-very old versions of Docker, so that's probably bad. Why/where does rustix need this logic? I thought it just wrapped There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rustix uses There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I didn't see that earlier because I was searching through an older version, oops. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just simulated the seccomp statx -> EPERM behavior on my system using this Python script (as root): import errno
import os
import seccomp
f = seccomp.SyscallFilter(seccomp.ALLOW)
f.add_rule(seccomp.ERRNO(errno.EPERM), "statx")
f.load()
os.execl(
"target/debug/deps/fs_additional-d7e3e72f4c824747",
"fs_additional",
"--nocapture",
"--test-threads=1",
"metadata_vs_std_fs",
) It's not quite as accurate as getting an old Docker version, but I'd rather not do that :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I updated There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm considering adding something in Rustix to allow or the null-pointer |
||
Err(e) => return Err(e.into()), | ||
} | ||
ongardie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
Ok(statat(start, path, atflags).map(MetadataExt::from_rustix)?) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -872,3 +872,37 @@ fn reopen_fd() { | |
let tmpdir2 = check!(cap_std::fs::Dir::reopen_dir(&tmpdir.as_filelike())); | ||
assert!(tmpdir2.exists("subdir")); | ||
} | ||
|
||
#[test] | ||
fn metadata_created() { | ||
let tmpdir = tmpdir(); | ||
check!(tmpdir.create_dir("dir")); | ||
let dir = check!(tmpdir.open_dir("dir")); | ||
let file = check!(dir.create("file")); | ||
|
||
let cap_std_dir = check!(dir.dir_metadata()); | ||
let cap_std_file = check!(file.metadata()); | ||
let cap_std_dir_entry = { | ||
let mut entries = check!(dir.entries()); | ||
let entry = check!(entries.next().unwrap()); | ||
assert_eq!(entry.file_name(), "file"); | ||
assert!(entries.next().is_none(), "unexpected dir entry"); | ||
check!(entry.metadata()) | ||
}; | ||
|
||
let std_dir = check!(dir.into_std_file().metadata()); | ||
let std_file = check!(file.into_std().metadata()); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test is much nicer now, I think. However, it's a little funny as is. It does all this setup to extract metadata in various ways from cap_std and std, but then it only compares the created times of the metadata and nothing else. Thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On one hand, creation times have an interesting enough implementation landscape that I feel it's justified to have a fair amount of test setup just for that one purpose, so this seems fine. On the other, since we have all this setup here, it'd be nice to expand this to test all of I don't want to bog you down if you're working on a project which just needs the creation times fix. So I'll leave it up to you for now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I expanded this out for all of the accessible Metadata (with only Unix extensions for now). |
||
// If the standard library supports file creation times, then cap-std | ||
// should too. | ||
if let Ok(expected) = std_dir.created() { | ||
println!("std::fs supports file created times"); | ||
assert_eq!(expected, check!(cap_std_dir.created()).into_std()); | ||
} else { | ||
println!("std::fs doesn't support file created times"); | ||
} | ||
if let Ok(expected) = std_file.created() { | ||
assert_eq!(expected, check!(cap_std_file.created()).into_std()); | ||
assert_eq!(expected, check!(cap_std_dir_entry.created()).into_std()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any idea when the rustix version will be updated? I don't have a sense of how big that is, so I didn't want to add it to this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The biggest thing for cap-std is that there are some semver incompatibilities between versions of io-lifetimes, which is tracking changes from I/O safety stabilization. Fortunately, the FCP looks to be almost done. Once it's done, I plan to do an io-lifetimes release and then a rustix 0.35 release, and then update cap-std and various other things.