summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Gohman <dev@sunfishcode.online>2021-11-19 15:12:57 -0800
committerDan Gohman <dev@sunfishcode.online>2021-12-02 13:03:32 -0800
commit4fc76bbe50ce167546900d1ddf389bfeb1066f66 (patch)
tree4cbe9e221bce6ec7d0b276b6e96545ccc93658ca
parentcdce42d5a3b4c903d850f4aba7664d2885180336 (diff)
downloadrust-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.rs2
-rw-r--r--library/std/src/os/linux/raw.rs41
-rw-r--r--library/std/src/sys/unix/fs.rs200
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> {