Skip to content

Commit af938e3

Browse files
committed
Implement Metadata:from_statx for Linux MetadataExt trait
1 parent 365c0e1 commit af938e3

3 files changed

Lines changed: 113 additions & 1 deletion

File tree

library/std/src/os/linux/fs.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,20 @@
44
55
#![stable(feature = "metadata_ext", since = "1.1.0")]
66

7+
use core::mem;
8+
79
use crate::fs::Metadata;
810
#[allow(deprecated)]
911
use crate::os::linux::raw;
12+
use crate::os::raw::c_void;
1013
use crate::sys::AsInner;
14+
use crate::sys::fs::cfg_has_statx;
15+
cfg_has_statx! {{
16+
use crate::sys::fs::{FileAttr, StatxExtraFields};
17+
use crate::sys::FromInner;
18+
} else {
19+
use crate::sys::unsupported;
20+
}}
1121

1222
/// OS-specific extensions to [`fs::Metadata`].
1323
///
@@ -41,6 +51,45 @@ pub trait MetadataExt {
4151
#[allow(deprecated)]
4252
fn as_raw_stat(&self) -> &raw::stat;
4353

54+
/// Creates a [`Metadata`] from a const void pointer populated by the [`statx`] syscall.
55+
///
56+
/// # Safety
57+
///
58+
/// The caller must take care to provide a valid const void pointer containing information
59+
/// populated by the [`statx`] syscall.
60+
///
61+
/// [`Metadata`]: crate::fs::Metadata
62+
/// [`statx`]: https://docs.rs/libc/latest/libc/struct.statx.html
63+
///
64+
/// ```no_run
65+
/// #![feature(metadata_statx)]
66+
/// use libc::statx;
67+
/// use std::ffi::c_void;
68+
/// use std::fs::{write, Metadata};
69+
/// use std::io;
70+
/// use std::os::linux::fs::MetadataExt;
71+
///
72+
/// fn main() -> io::Result<()> {
73+
/// write("hello.txt", "Hello World!")?;
74+
/// let mut buf = Box::<statx>::new_uninit();
75+
/// unsafe {
76+
/// libc::statx(
77+
/// libc::AT_FDCWD,
78+
/// "hello.txt".as_ptr().cast(),
79+
/// libc::AT_STATX_SYNC_AS_STAT,
80+
/// libc::STATX_BASIC_STATS,
81+
/// buf.as_mut_ptr().cast()
82+
/// );
83+
/// }
84+
/// let statxbuf: Box<statx> = unsafe { buf.assume_init() };
85+
/// let metadata = unsafe { Metadata::from_statx(&*statxbuf as *const statx as *const c_void) };
86+
/// assert_eq!(metadata.len(), 12); // "Hello World!" is 12 bytes
87+
/// Ok(())
88+
/// }
89+
/// ```
90+
#[unstable(feature = "metadata_statx", issue = "156268")]
91+
unsafe fn from_statx(statxbuf: *const c_void) -> Self;
92+
4493
/// Returns the device ID on which this file resides.
4594
///
4695
/// # Examples
@@ -337,6 +386,39 @@ impl MetadataExt for Metadata {
337386
&*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat)
338387
}
339388
}
389+
cfg_has_statx! {{
390+
unsafe fn from_statx(statxbuf: *const c_void) -> Metadata {
391+
let buf = statxbuf as *const libc::statx;
392+
393+
// We cannot fill `stat64` exhaustively because of private padding fields.
394+
let mut stat: libc::stat64 = mem::zeroed();
395+
// `c_ulong` on gnu-mips, `dev_t` otherwise
396+
stat.st_dev = libc::makedev((*buf).stx_dev_major, (*buf).stx_dev_minor) as _;
397+
stat.st_ino = (*buf).stx_ino as libc::ino64_t;
398+
stat.st_nlink = (*buf).stx_nlink as libc::nlink_t;
399+
stat.st_mode = (*buf).stx_mode as libc::mode_t;
400+
stat.st_uid = (*buf).stx_uid as libc::uid_t;
401+
stat.st_gid = (*buf).stx_gid as libc::gid_t;
402+
stat.st_rdev = libc::makedev((*buf).stx_rdev_major, (*buf).stx_rdev_minor) as _;
403+
stat.st_size = (*buf).stx_size as libc::off64_t;
404+
stat.st_blksize = (*buf).stx_blksize as libc::blksize_t;
405+
stat.st_blocks = (*buf).stx_blocks as libc::blkcnt64_t;
406+
stat.st_atime = (*buf).stx_atime.tv_sec as libc::time_t;
407+
// `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
408+
stat.st_atime_nsec = (*buf).stx_atime.tv_nsec as _;
409+
stat.st_mtime = (*buf).stx_mtime.tv_sec as libc::time_t;
410+
stat.st_mtime_nsec = (*buf).stx_mtime.tv_nsec as _;
411+
stat.st_ctime = (*buf).stx_ctime.tv_sec as libc::time_t;
412+
stat.st_ctime_nsec = (*buf).stx_ctime.tv_nsec as _;
413+
414+
let extra = StatxExtraFields::from_statx(statxbuf);
415+
416+
Metadata::from_inner(FileAttr::from_statx(stat, Some(extra)))
417+
}} else {
418+
unsafe fn from_statx(statxbuf: *const c_void) -> Self {
419+
unsupported();
420+
}
421+
}}
340422
fn st_dev(&self) -> u64 {
341423
self.as_inner().as_inner().st_dev as u64
342424
}

library/std/src/sys/fs/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ cfg_select! {
1818
#[cfg(any(target_os = "linux", target_os = "android"))]
1919
pub(super) use unix::CachedFileMetadata;
2020
use crate::sys::helpers::run_path_with_cstr as with_native_path;
21+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
22+
pub(crate) use unix::{
23+
cfg_has_statx, StatxExtraFields
24+
};
2125
}
2226
target_os = "windows" => {
2327
mod windows;

library/std/src/sys/fs/unix.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ use crate::fmt::{self, Write as _};
8383
use crate::fs::TryLockError;
8484
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
8585
use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
86+
cfg_has_statx! {{
87+
use crate::os::raw::c_void;
88+
} else {}}
8689
#[cfg(target_family = "unix")]
8790
use crate::os::unix::prelude::*;
8891
#[cfg(target_os = "wasi")]
@@ -125,6 +128,8 @@ macro_rules! cfg_has_statx {
125128
};
126129
}
127130

131+
pub(crate) use cfg_has_statx;
132+
128133
cfg_has_statx! {{
129134
#[derive(Clone)]
130135
pub struct FileAttr {
@@ -133,7 +138,7 @@ cfg_has_statx! {{
133138
}
134139

135140
#[derive(Clone)]
136-
struct StatxExtraFields {
141+
pub struct StatxExtraFields {
137142
// This is needed to check if btime is supported by the filesystem.
138143
stx_mask: u32,
139144
stx_btime: libc::statx_timestamp,
@@ -525,11 +530,32 @@ pub struct DirBuilder {
525530
struct Mode(mode_t);
526531

527532
cfg_has_statx! {{
533+
impl StatxExtraFields {
534+
pub unsafe fn from_statx(statxbuf: *const c_void) -> Self {
535+
let buf = statxbuf as *const libc::statx;
536+
537+
StatxExtraFields {
538+
stx_mask: (*buf).stx_mask,
539+
stx_btime: (*buf).stx_btime,
540+
// Store full times to avoid 32-bit `time_t` truncation.
541+
#[cfg(target_pointer_width = "32")]
542+
stx_atime: (*buf).stx_atime,
543+
#[cfg(target_pointer_width = "32")]
544+
stx_ctime: (*buf).stx_ctime,
545+
#[cfg(target_pointer_width = "32")]
546+
stx_mtime: (*buf).stx_mtime,
547+
}
548+
}
549+
}
528550
impl FileAttr {
529551
fn from_stat64(stat: stat64) -> Self {
530552
Self { stat, statx_extra_fields: None }
531553
}
532554

555+
pub fn from_statx(stat: stat64, extra: Option<StatxExtraFields>) -> Self {
556+
Self {stat, statx_extra_fields: extra}
557+
}
558+
533559
#[cfg(target_pointer_width = "32")]
534560
pub fn stx_mtime(&self) -> Option<&libc::statx_timestamp> {
535561
if let Some(ext) = &self.statx_extra_fields {

0 commit comments

Comments
 (0)