Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions library/std/src/os/linux/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,25 @@
use crate::fs::Metadata;
#[allow(deprecated)]
use crate::os::linux::raw;
use crate::os::raw::{c_uint, c_void};
use crate::sys::AsInner;
use crate::sys::fs::cfg_has_statx;
cfg_has_statx! {{
use crate::sys::fs::FileAttr;
use crate::sys::FromInner;
} else {
use crate::sys::unsupported;
}}

/// This is the [`statx`] mask expected by [`Metadata::from_statx`], which sets both
/// `STATX_BASIC_STATS` and `STATX_BTIME`. See the [Linux man page] for statx for more
/// details.
///
Comment thread
asder8215 marked this conversation as resolved.
/// [`statx`]: https://docs.rs/libc/latest/libc/struct.statx.html
/// [Linux man page]: https://man7.org/linux/man-pages/man2/statx.2.html
#[unstable(feature = "metadata_statx", issue = "156268")]
#[cfg(any(target_env = "gnu", target_env = "musl", target_os = "android", target_os = "l4re"))]
pub const STATX_MASK: c_uint = libc::STATX_BASIC_STATS | libc::STATX_BTIME;
Comment thread
asder8215 marked this conversation as resolved.

/// OS-specific extensions to [`fs::Metadata`].
///
Expand Down Expand Up @@ -42,6 +60,52 @@ pub trait MetadataExt {
#[allow(deprecated)]
fn as_raw_stat(&self) -> &raw::stat;

/// Creates a [`Metadata`] from a const void pointer populated by the [`statx`] syscall.
///
/// Currently [`Metadata::from_statx`] is only supported on Linux platforms.
///
/// # Safety
///
/// The caller must take care to provide a valid const void pointer containing information
/// populated by the [`statx`] syscall. In particular, the provided pointer should contain
/// statx information pertaining to the mask [`STATX_MASK`], so that there will be no
/// uninitialized data encountered in constructing [`Metadata`].
Comment thread
Mark-Simulacrum marked this conversation as resolved.
///
/// Note that the relevant information is copied out of the structure and the pointer is
/// not retained past the call.
///
/// [`Metadata`]: crate::fs::Metadata
/// [`statx`]: https://docs.rs/libc/latest/libc/struct.statx.html
///
/// ```no_run
/// #![feature(metadata_statx)]
/// use libc::statx;
/// use std::ffi::c_void;
/// use std::fs::{write, Metadata};
/// use std::io;
/// use std::os::linux::fs::{MetadataExt, STATX_MASK};
///
/// fn main() -> io::Result<()> {
/// write("hello.txt", "Hello World!")?;
/// let mut buf = Box::<statx>::new_uninit();
/// unsafe {
/// libc::statx(
/// libc::AT_FDCWD,
/// "hello.txt".as_ptr().cast(),
/// libc::AT_STATX_SYNC_AS_STAT,
/// STATX_MASK,
/// buf.as_mut_ptr().cast()
/// );
/// }
/// let statxbuf: Box<statx> = unsafe { buf.assume_init() };
/// let metadata = unsafe { Metadata::from_statx(&*statxbuf as *const statx as *const c_void) };
/// assert_eq!(metadata.len(), 12); // "Hello World!" is 12 bytes
/// Ok(())
/// }
/// ```
#[unstable(feature = "metadata_statx", issue = "156268")]
unsafe fn from_statx(statxbuf: *const c_void) -> Self;

/// Returns the device ID on which this file resides.
///
/// # Examples
Expand Down Expand Up @@ -354,6 +418,17 @@ impl MetadataExt for Metadata {
&*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat)
}
}
cfg_has_statx! {
{
unsafe fn from_statx(statxbuf: *const c_void) -> Metadata {
Metadata::from_inner(FileAttr::from_statx(*(statxbuf as *const libc::statx)))
}
} else {
unsafe fn from_statx(statxbuf: *const c_void) -> Self {
unsupported();
}
}
}
fn st_dev(&self) -> u64 {
self.as_inner().as_inner().st_dev as u64
}
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ cfg_select! {
use unix as imp;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(super) use unix::CachedFileMetadata;
#[cfg(target_os = "linux")]
pub(crate) use unix::cfg_has_statx;
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
pub use unix::chroot;
#[cfg(not(target_os = "wasi"))]
Expand Down
94 changes: 59 additions & 35 deletions library/std/src/sys/fs/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ macro_rules! cfg_has_statx {
};
}

#[cfg(target_os = "linux")]
pub(crate) use cfg_has_statx;

cfg_has_statx! {{
#[derive(Clone)]
pub struct FileAttr {
Expand All @@ -131,7 +134,7 @@ cfg_has_statx! {{
}

#[derive(Clone)]
struct StatxExtraFields {
pub struct StatxExtraFields {
// This is needed to check if btime is supported by the filesystem.
stx_mask: u32,
stx_btime: libc::statx_timestamp,
Expand Down Expand Up @@ -210,40 +213,7 @@ cfg_has_statx! {{
STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
}

// We cannot fill `stat64` exhaustively because of private padding fields.
let mut stat: stat64 = mem::zeroed();
// `c_ulong` on gnu-mips, `dev_t` otherwise
stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _;
stat.st_ino = buf.stx_ino as libc::ino64_t;
stat.st_nlink = buf.stx_nlink as libc::nlink_t;
stat.st_mode = buf.stx_mode as libc::mode_t;
stat.st_uid = buf.stx_uid as libc::uid_t;
stat.st_gid = buf.stx_gid as libc::gid_t;
stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
stat.st_size = buf.stx_size as off64_t;
stat.st_blksize = buf.stx_blksize as libc::blksize_t;
stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t;
stat.st_atime = buf.stx_atime.tv_sec as libc::time_t;
// `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t;
stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t;
stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;

let extra = StatxExtraFields {
stx_mask: buf.stx_mask,
stx_btime: buf.stx_btime,
// Store full times to avoid 32-bit `time_t` truncation.
#[cfg(target_pointer_width = "32")]
stx_atime: buf.stx_atime,
#[cfg(target_pointer_width = "32")]
stx_ctime: buf.stx_ctime,
#[cfg(target_pointer_width = "32")]
stx_mtime: buf.stx_mtime,
};

Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }))
Some(Ok(FileAttr::from_statx(buf)))
}

} else {
Expand Down Expand Up @@ -480,11 +450,65 @@ pub struct DirBuilder {
struct Mode(mode_t);

cfg_has_statx! {{
impl StatxExtraFields {
/// Constructs `StatxExtraFields` from a given `libc::statx` struct
///
/// SAFETY:
/// The caller must take care to provide a `libc::statx` buffer that is
/// populated by the statx syscall with the flags `STATX_BASIC_STATS`
/// and `STATX_BTIME` enabled
pub unsafe fn from_statx(buf: libc::statx) -> Self {
StatxExtraFields {
stx_mask: buf.stx_mask,
stx_btime: buf.stx_btime,
// Store full times to avoid 32-bit `time_t` truncation.
#[cfg(target_pointer_width = "32")]
stx_atime: buf.stx_atime,
#[cfg(target_pointer_width = "32")]
stx_ctime: buf.stx_ctime,
#[cfg(target_pointer_width = "32")]
stx_mtime: buf.stx_mtime,
}
}
}
impl FileAttr {
fn from_stat64(stat: stat64) -> Self {
Self { stat, statx_extra_fields: None }
}

/// Constructs `FileAttr` from a given `libc::statx` struct
///
/// SAFETY:
/// The caller must take care to provide a `libc::statx` buffer that is
/// populated by the statx syscall with the flags `STATX_BASIC_STATS`
/// and `STATX_BTIME` enabled
pub unsafe fn from_statx(buf: libc::statx) -> Self {
// We cannot fill `stat64` exhaustively because of private padding fields.
let mut stat: libc::stat64 = mem::zeroed();
// `c_ulong` on gnu-mips, `dev_t` otherwise
stat.st_dev = libc::makedev((buf).stx_dev_major, (buf).stx_dev_minor) as _;
stat.st_ino = buf.stx_ino as libc::ino64_t;
stat.st_nlink = buf.stx_nlink as libc::nlink_t;
stat.st_mode = buf.stx_mode as libc::mode_t;
stat.st_uid = buf.stx_uid as libc::uid_t;
stat.st_gid = buf.stx_gid as libc::gid_t;
stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
stat.st_size = buf.stx_size as libc::off64_t;
stat.st_blksize = buf.stx_blksize as libc::blksize_t;
stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t;
stat.st_atime = buf.stx_atime.tv_sec as libc::time_t;
// `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t;
stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t;
stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;

let statx_extra_fields = Some(StatxExtraFields::from_statx(buf));

Self {stat, statx_extra_fields}
}

#[cfg(target_pointer_width = "32")]
pub fn stx_mtime(&self) -> Option<&libc::statx_timestamp> {
if let Some(ext) = &self.statx_extra_fields {
Expand Down
Loading