diff options
author | Dan Gohman <dev@sunfishcode.online> | 2021-11-19 15:12:57 -0800 |
---|---|---|
committer | Dan Gohman <dev@sunfishcode.online> | 2021-12-02 13:03:32 -0800 |
commit | 4fc76bbe50ce167546900d1ddf389bfeb1066f66 (patch) | |
tree | 4cbe9e221bce6ec7d0b276b6e96545ccc93658ca | |
parent | cdce42d5a3b4c903d850f4aba7664d2885180336 (diff) | |
download | rust-4fc76bbe50ce167546900d1ddf389bfeb1066f66.tar.gz |
Port `stat`, `lstat`, `statx`, `readlink`, and `symlink` to rustix.
Concerning the "trick" to call `rustix` with null pointers: benchmarking
suggests that the new trick, calling it with an empty path, is actually
significantly faster.
-rw-r--r-- | library/std/src/os/linux/fs.rs | 2 | ||||
-rw-r--r-- | library/std/src/os/linux/raw.rs | 41 | ||||
-rw-r--r-- | library/std/src/sys/unix/fs.rs | 200 |
3 files changed, 76 insertions, 167 deletions
diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs index 9d18ccbeb24..03501c108dc 100644 --- a/library/std/src/os/linux/fs.rs +++ b/library/std/src/os/linux/fs.rs @@ -329,7 +329,7 @@ pub trait MetadataExt { impl MetadataExt for Metadata { #[allow(deprecated)] fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) } + unsafe { &*(self.as_inner().as_inner() as *const rustix::fs::Stat as *const raw::stat) } } fn st_dev(&self) -> u64 { self.as_inner().as_inner().st_dev as u64 diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index cd92dcabdf5..30bca103014 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -53,46 +53,7 @@ mod arch { #[repr(C)] #[derive(Clone)] #[stable(feature = "raw_ext", since = "1.1.0")] - pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad1: c_short, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __st_ino: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad2: c_uint, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - } + pub use rustix::fs::Stat as stat; } #[cfg(target_arch = "mips")] diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index c0be879d2e8..7c7c244ec8b 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -12,29 +12,22 @@ use crate::sys::fd::FileDesc; use crate::sys::time::SystemTime; use crate::sys::{cvt, cvt_r}; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use rustix::ffi::{ZStr, ZString}; use rustix::fs::AtFlags; +#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] +use rustix::fs::StatxFlags; -#[cfg(any( - all(target_os = "linux", target_env = "gnu"), - target_os = "macos", - target_os = "ios", -))] +#[cfg(any(target_os = "macos", target_os = "ios"))] use crate::sys::weak::syscall; #[cfg(target_os = "macos")] use crate::sys::weak::weak; use libc::{c_int, mode_t}; -#[cfg(any( - target_os = "macos", - target_os = "ios", - all(target_os = "linux", target_env = "gnu") -))] +#[cfg(any(target_os = "macos", target_os = "ios"))] use libc::c_char; #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] use libc::dirfd; -#[cfg(any(target_os = "linux", target_os = "emscripten"))] -use libc::fstatat64; #[cfg(not(any( target_os = "linux", target_os = "emscripten", @@ -45,23 +38,17 @@ use libc::fstatat64; target_os = "redox" )))] use libc::readdir_r as readdir64_r; -#[cfg(target_os = "android")] -use libc::{ - dirent as dirent64, fstat as fstat64, fstatat as fstatat64, - lstat as lstat64, off64_t, open as open64, stat as stat64, -}; #[cfg(not(any( target_os = "linux", target_os = "emscripten", target_os = "l4re", target_os = "android" )))] -use libc::{ - dirent as dirent64, fstat as fstat64, lstat as lstat64, off_t as off64_t, open as open64, - stat as stat64, -}; +use libc::{dirent as dirent64, off_t as off64_t, open as open64}; +#[cfg(target_os = "android")] +use libc::{dirent as dirent64, open as open64}; #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))] -use libc::{dirent64, fstat64, lstat64, off64_t, open64, readdir64_r, stat64}; +use libc::{dirent64, off64_t, open64, readdir64_r}; pub use crate::sys_common::fs::{remove_dir_all, try_exists}; @@ -92,7 +79,7 @@ macro_rules! cfg_has_statx { cfg_has_statx! {{ #[derive(Clone)] pub struct FileAttr { - stat: stat64, + stat: rustix::fs::Stat, statx_extra_fields: Option<StatxExtraFields>, } @@ -100,16 +87,16 @@ cfg_has_statx! {{ struct StatxExtraFields { // This is needed to check if btime is supported by the filesystem. stx_mask: u32, - stx_btime: libc::statx_timestamp, + stx_btime: rustix::time::Timespec, } // We prefer `statx` on Linux if available, which contains file creation time. - // Default `stat64` contains no creation time. - unsafe fn try_statx( - fd: c_int, - path: *const c_char, - flags: i32, - mask: u32, + // Default `Stat` contains no creation time. + unsafe fn try_statx<P: rustix::path::Arg>( + fd: BorrowedFd<'_>, + path: P, + flags: AtFlags, + mask: StatxFlags, ) -> Option<io::Result<FileAttr>> { use crate::sync::atomic::{AtomicU8, Ordering}; @@ -119,67 +106,58 @@ cfg_has_statx! {{ // 1: Not available // 2: Available static STATX_STATE: AtomicU8 = AtomicU8::new(0); - syscall! { - fn statx( - fd: c_int, - pathname: *const c_char, - flags: c_int, - mask: libc::c_uint, - statxbuf: *mut libc::statx - ) -> c_int - } match STATX_STATE.load(Ordering::Relaxed) { 0 => { - // It is a trick to call `statx` with null pointers to check if the syscall - // is available. According to the manual, it is expected to fail with EFAULT. - // We do this mainly for performance, since it is nearly hundreds times - // faster than a normal successful call. - let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut())) - .err() - .and_then(|e| e.raw_os_error()); + // It is a trick to call `statx` with an empty path to check if the syscall + // is available. According to the manual, it is expected to fail with ENOENT. + let res = rustix::fs::statx(&rustix::fs::cwd(), rustix::zstr!(""), AtFlags::empty(), StatxFlags::ALL); // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited // and returns `EPERM`. Listing all possible errors seems not a good idea. // See: https://github.com/rust-lang/rust/issues/65662 - if err != Some(libc::EFAULT) { + if let Err(rustix::io::Error::NOENT) = res { + STATX_STATE.store(2, Ordering::Relaxed); + } else { STATX_STATE.store(1, Ordering::Relaxed); return None; } - STATX_STATE.store(2, Ordering::Relaxed); } 1 => return None, _ => {} } - let mut buf: libc::statx = mem::zeroed(); - if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) { - return Some(Err(err)); - } + let buf = match rustix::fs::statx(&fd, path, flags, mask) { + Ok(buf) => buf, + Err(e) => return Some(Err(e.into())), + }; - // We cannot fill `stat64` exhaustively because of private padding fields. - let mut stat: stat64 = mem::zeroed(); + // We cannot fill `Stat` exhaustively because of private padding fields. + let mut stat: rustix::fs::Stat = 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_dev = rustix::fs::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_rdev = rustix::fs::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; + stat.st_atime = buf.stx_atime.tv_sec as u64; // `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 = buf.stx_mtime.tv_sec as u64; 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 = buf.stx_ctime.tv_sec as u64; stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _; let extra = StatxExtraFields { stx_mask: buf.stx_mask, - stx_btime: buf.stx_btime, + stx_btime: rustix::time::Timespec { + tv_sec: buf.stx_btime.tv_sec, + tv_nsec: buf.stx_btime.tv_nsec as _, + } }; Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) })) @@ -188,7 +166,7 @@ cfg_has_statx! {{ } else { #[derive(Clone)] pub struct FileAttr { - stat: stat64, + stat: rustix::fs::Stat, } }} @@ -261,13 +239,13 @@ pub struct DirBuilder { cfg_has_statx! {{ impl FileAttr { - fn from_stat64(stat: stat64) -> Self { + fn from_stat(stat: rustix::fs::Stat) -> Self { Self { stat, statx_extra_fields: None } } } } else { impl FileAttr { - fn from_stat64(stat: stat64) -> Self { + fn from_stat(stat: rustix::fs::Stat) -> Self { Self { stat } } } @@ -388,8 +366,8 @@ impl FileAttr { } } -impl AsInner<stat64> for FileAttr { - fn as_inner(&self) -> &stat64 { +impl AsInner<rustix::fs::Stat> for FileAttr { + fn as_inner(&self) -> &rustix::fs::Stat { &self.stat } } @@ -546,22 +524,25 @@ impl DirEntry { #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] pub fn metadata(&self) -> io::Result<FileAttr> { let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; - let name = self.entry.d_name.as_ptr(); + let name = unsafe { ZStr::from_ptr(self.entry.d_name.as_ptr().cast()) }; cfg_has_statx! { if let Some(ret) = unsafe { try_statx( - fd, + BorrowedFd::borrow_raw_fd(fd), name, - libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + AtFlags::SYMLINK_NOFOLLOW | AtFlags::STATX_SYNC_AS_STAT, + StatxFlags::ALL, ) } { return ret; } } - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?; - Ok(FileAttr::from_stat64(stat)) + let stat = rustix::fs::statat( + unsafe { &BorrowedFd::borrow_raw_fd(fd) }, + name, + AtFlags::SYMLINK_NOFOLLOW, + )?; + Ok(FileAttr::from_stat(stat)) } #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))] @@ -769,22 +750,19 @@ impl File { } pub fn file_attr(&self) -> io::Result<FileAttr> { - let fd = self.as_raw_fd(); - cfg_has_statx! { if let Some(ret) = unsafe { try_statx( - fd, - b"\0" as *const _ as *const c_char, - libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + self.as_fd(), + rustix::zstr!(""), + AtFlags::EMPTY_PATH | AtFlags::STATX_SYNC_AS_STAT, + StatxFlags::ALL, ) } { return ret; } } - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { fstat64(fd, &mut stat) })?; - Ok(FileAttr::from_stat64(stat)) + let stat = rustix::fs::fstat(self)?; + Ok(FileAttr::from_stat(stat)) } pub fn fsync(&self) -> io::Result<()> { @@ -1087,36 +1065,12 @@ pub fn rmdir(p: &Path) -> io::Result<()> { } pub fn readlink(p: &Path) -> io::Result<PathBuf> { - let c_path = cstr(p)?; - let p = c_path.as_ptr(); - - let mut buf = Vec::with_capacity(256); - - loop { - let buf_read = - cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize; - - unsafe { - buf.set_len(buf_read); - } - - if buf_read != buf.capacity() { - buf.shrink_to_fit(); - - return Ok(PathBuf::from(OsString::from_vec(buf))); - } - - // Trigger the internal buffer resizing logic of `Vec` by requiring - // more space than the current capacity. The length is guaranteed to be - // the same as the capacity due to the if statement above. - buf.reserve(1); - } + let path = rustix::fs::readlinkat(&rustix::fs::cwd(), p, ZString::default())?; + Ok(OsString::from_vec(path.into_bytes()).into()) } pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { - let original = cstr(original)?; - let link = cstr(link)?; - cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) })?; + rustix::fs::symlinkat(original, &rustix::fs::cwd(), link)?; Ok(()) } @@ -1154,41 +1108,35 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { } pub fn stat(p: &Path) -> io::Result<FileAttr> { - let p = cstr(p)?; - cfg_has_statx! { if let Some(ret) = unsafe { try_statx( - libc::AT_FDCWD, - p.as_ptr(), - libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + rustix::fs::cwd(), + p, + AtFlags::STATX_SYNC_AS_STAT, + StatxFlags::ALL, ) } { return ret; } } - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?; - Ok(FileAttr::from_stat64(stat)) + let stat = rustix::fs::statat(&rustix::fs::cwd(), p, AtFlags::empty())?; + Ok(FileAttr::from_stat(stat)) } pub fn lstat(p: &Path) -> io::Result<FileAttr> { - let p = cstr(p)?; - cfg_has_statx! { if let Some(ret) = unsafe { try_statx( - libc::AT_FDCWD, - p.as_ptr(), - libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + rustix::fs::cwd(), + p, + AtFlags::SYMLINK_NOFOLLOW | AtFlags::STATX_SYNC_AS_STAT, + StatxFlags::ALL, ) } { return ret; } } - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?; - Ok(FileAttr::from_stat64(stat)) + let stat = rustix::fs::statat(&rustix::fs::cwd(), p, AtFlags::SYMLINK_NOFOLLOW)?; + Ok(FileAttr::from_stat(stat)) } pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { |