From 9302265bcd88f57d9bbda6dd1173107363aa105a Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela Date: Thu, 10 Feb 2022 00:20:16 +0300 Subject: [PATCH 1/7] Add statx Resolves #1649. --- src/fcntl.rs | 3 + src/fcntl/statx.rs | 371 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 374 insertions(+) create mode 100644 src/fcntl/statx.rs diff --git a/src/fcntl.rs b/src/fcntl.rs index 6c713608ee..13fb13a392 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -889,3 +889,6 @@ pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Resu } } } + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub mod statx; diff --git a/src/fcntl/statx.rs b/src/fcntl/statx.rs new file mode 100644 index 0000000000..ec5153cc09 --- /dev/null +++ b/src/fcntl/statx.rs @@ -0,0 +1,371 @@ +use std::{mem, os::unix::prelude::RawFd}; + +use libc::{self, c_int, c_uint, mode_t}; + +use crate::{errno::Errno, NixPath, Result, sys::stat::{SFlag, Mode}, unistd::{Uid, Gid}}; + +libc_bitflags!( + /// Configuration options for statx. + pub struct Flags: c_int { + /// Allow empty `pathname`, to receive status of the specified file descriptor itself (not a directory entry) instead. + AT_EMPTY_PATH; + + /// Do not automount the last component of the path being queried. + AT_NO_AUTOMOUNT; + + /// Do not follow symlink in the last component of the path being queried. + AT_SYMLINK_NOFOLLOW; + + /// Do whatever usual, non-`x` stat does. The default. + AT_STATX_SYNC_AS_STAT; + + /// Force returned attributes to be up to date (synchonized). May trigger data writeback. + /// May involve additional roudtrips for networked filesystems. + AT_STATX_FORCE_SYNC; + + /// Don't synchronize, use cached data if possible. This may lead to the returned data to be approximate. + AT_STATX_DONT_SYNC; + } +); + +libc_bitflags!( + /// Configuration options for statx. + pub struct Mask: c_uint { + /// Want stx_mode & S_IFMT + STATX_TYPE; + /// Want stx_mode & ~S_IFMT + STATX_MODE; + /// Want stx_nlink + STATX_NLINK; + /// Want stx_uid + STATX_UID; + /// Want stx_gid + STATX_GID; + /// Want stx_atime + STATX_ATIME; + /// Want stx_mtime + STATX_MTIME; + /// Want stx_ctime + STATX_CTIME; + /// Want stx_ino + STATX_INO; + /// Want stx_size + STATX_SIZE; + /// Want stx_blocks + STATX_BLOCKS; + /// [All of the above] + STATX_BASIC_STATS; + /// Want stx_btime + STATX_BTIME; + /// Want stx_mnt_id (since Linux 5.8); + STATX_MNT_ID; + /// [All currently available fields] + STATX_ALL; + } +); + +/* +libc_bitflags!( + pub struct FileAttributes: c_int { + /// The file is compressed by the filesystem and may take extra resources to access. + STATX_ATTR_COMPRESSED; + + /// The file cannot be modified: it cannot be deleted or renamed, no hard links can be created to this file and no data can be written to it. + STATX_ATTR_IMMUTABLE; + + /// The file can only be opened in append mode for writing. Random access writing is not permitted. + STATX_ATTR_APPEND; + + + /// File is not a candidate for backup when a backup program such as `dump` is run. + STATX_ATTR_NODUMP; + + /// A key is required for the file to be encrypted by the filesystem. + STATX_ATTR_ENCRYPTED; + + // /// (since Linux 5.5) + // /// The file has fs-verity enabled. It cannot be written to, and all reads from it will be verified + // /// against a cryptographic hash that covers the entire file + // /// (e.g., via a Merkle tree). + // STATX_ATTR_VERITY; + + // /// (since Linux 5.8) + // /// The file is in the DAX (cpu direct access) state. + // STATX_ATTR_DAX; + } +); +*/ + +/// Attempt to retrieve stats of `pathname`. If `pathname` is relative, `dirfd` is used as starting directory for lookups. If `dirfs` is None, current directory is used. +/// `mask` determines what stats entries should be filled in, but kernel can actually fill more or less than requested. +/// `statx` is not atomic: if attributes are being changed at the time of `statx` is called, the returned attributes set may have items from different moments of time. +pub fn statx( + dirfd: Option, + pathname: &P, + flags: Flags, + mask: Mask, +) -> Result { + let dirfd = dirfd.unwrap_or(libc::AT_FDCWD); + + let mut dst = mem::MaybeUninit::uninit(); + let res = pathname.with_nix_path(|cstr| unsafe { + libc::statx( + dirfd, + cstr.as_ptr(), + flags.bits() as libc::c_int, + mask.bits() as libc::c_uint, + dst.as_mut_ptr(), + ) + })?; + + Errno::result(res)?; + + Ok(Statx { + inner: unsafe { dst.assume_init() }, + }) +} + +/// Get stats of specified file descriptor (not nesessarily directory) without resolving the path. +/// Automaitcally sets the `AT_EMPTY_PATH` flag. +pub fn statx_without_path(dirfd: RawFd, mut flags: Flags, mask: Mask) -> Result { + flags.set(Flags::AT_EMPTY_PATH, true); + let mut dst = mem::MaybeUninit::uninit(); + let res = unsafe { + libc::statx( + dirfd, + b"\0" as *const u8 as *const libc::c_char, + flags.bits() as libc::c_int, + mask.bits() as libc::c_uint, + dst.as_mut_ptr(), + ) + }; + + Errno::result(res)?; + + Ok(Statx { + inner: unsafe { dst.assume_init() }, + }) +} + +#[derive(Debug,Copy,Clone)] +pub struct Statx { + pub inner: libc::statx, +} + +impl Statx { + /// Retrieve file type, if it has been returned by kernel + pub fn r#type(&self) -> Option { + if Mask::STATX_TYPE.bits() & self.inner.stx_mask > 0 { + Some(SFlag::from_bits_truncate(self.inner.stx_mode as mode_t & SFlag::S_IFMT.bits())) + } else { + None + } + } + + /// Retrieve file mode, if it has been returned by kernel. + pub fn mode(&self) -> Option { + if Mask::STATX_MODE.bits() & self.inner.stx_mask > 0 { + Some(Mode::from_bits_truncate(self.inner.stx_mode as mode_t)) + } else { + None + } + } + + /// Retrieve number of hard links, if it has been returned by kernel. + pub fn nlinks(&self) -> Option { + if Mask::STATX_NLINK.bits() & self.inner.stx_mask > 0 { + Some(self.inner.stx_nlink) + } else { + None + } + } + + /// Retrieve uid (owner, user ID), if it has been returned by kernel. + pub fn uid(&self) -> Option { + if Mask::STATX_NLINK.bits() & self.inner.stx_mask > 0 { + Some(Uid::from_raw(self.inner.stx_uid)) + } else { + None + } + } + + /// Retrieve gid (group ID), if it has been returned by kernel. + pub fn gid(&self) -> Option { + if Mask::STATX_NLINK.bits() & self.inner.stx_mask > 0 { + Some(Gid::from_raw(self.inner.stx_uid)) + } else { + None + } + } + + /// Retrieve file access time, if it has been returned by kernel + pub fn atime(&self) -> Option { + if Mask::STATX_ATIME.bits() & self.inner.stx_mask > 0 { + Some(self.inner.stx_atime) + } else { + None + } + } + + /// Retrieve file modification time, if it has been returned by kernel + pub fn mtime(&self) -> Option { + if Mask::STATX_MTIME.bits() & self.inner.stx_mask > 0 { + Some(self.inner.stx_mtime) + } else { + None + } + } + + /// Retrieve file attribute change time, if it has been returned by kernel + pub fn ctime(&self) -> Option { + if Mask::STATX_CTIME.bits() & self.inner.stx_mask > 0 { + Some(self.inner.stx_ctime) + } else { + None + } + } + + /// Retrieve file birth (creation) time, if it has been returned by kernel + pub fn btime(&self) -> Option { + if Mask::STATX_BTIME.bits() & self.inner.stx_mask > 0 { + Some(self.inner.stx_btime) + } else { + None + } + } + + /// Retrieve inode number, if it has been returned by kernel + pub fn ino(&self) -> Option { + if Mask::STATX_INO.bits() & self.inner.stx_mask > 0 { + Some(self.inner.stx_ino) + } else { + None + } + } + + /// Retrieve file size, in bytes, if it has been returned by kernel + pub fn size(&self) -> Option { + if Mask::STATX_SIZE.bits() & self.inner.stx_mask > 0 { + Some(self.inner.stx_size) + } else { + None + } + } + + /// Retrieve file size as a number of blocks, if it has been returned by kernel + pub fn blocks(&self) -> Option { + if Mask::STATX_BLOCKS.bits() & self.inner.stx_mask > 0 { + Some(self.inner.stx_blocks) + } else { + None + } + } + + /// Retrieve file size as a number of blocks, if it has been returned by kernel + pub fn blksize(&self) -> Option { + // I'm not sure what exact mask bit should be used to check presence of block size. + // Actual Linux kernel seems return most of STATX_BASIC_STATS in one go, regarless of which things are asked for. + if Mask::STATX_BASIC_STATS.bits() & self.inner.stx_mask == Mask::STATX_BASIC_STATS.bits() { + Some(self.inner.stx_blocks) + } else { + None + } + } + + /// Retrieve mount ID, if it has been returned by kernel + pub fn mnt_id(&self) -> Option { + if Mask::STATX_MNT_ID.bits() & self.inner.stx_mask > 0 { + Some(self.inner.stx_mnt_id) + } else { + None + } + } + + /// Retrieve device major and minor numbers (first and second elements of the tuple respectively) of the filesystem where the file resides at, if it has been returned by kernel. + pub fn dev_major_minor(&self) -> Option<(u32, u32)> { + // I'm not sure what exact mask bit should be used to check presence of this information. + // Actual Linux kernel seems return most of STATX_BASIC_STATS in one go, regarless of which things are asked for. + if Mask::STATX_BASIC_STATS.bits() & self.inner.stx_mask == Mask::STATX_BASIC_STATS.bits() { + Some((self.inner.stx_dev_major, self.inner.stx_dev_minor)) + } else { + None + } + } + + /// Retrieve pointed-to device major and minor numbers (first and second elements of the tuple respectively) of this character or block device file, if it has been returned by kernel. + /// Note that this function does not check for the file type. It would return `Some` even for regular files. + pub fn rdev_major_minor(&self) -> Option<(u32, u32)> { + // I'm not sure what exact mask bit should be used to check presence of this information. + // Actual Linux kernel seems return most of STATX_BASIC_STATS in one go, regarless of which things are asked for. + if Mask::STATX_BASIC_STATS.bits() & self.inner.stx_mask == Mask::STATX_BASIC_STATS.bits() { + Some((self.inner.stx_dev_major, self.inner.stx_dev_minor)) + } else { + None + } + } + + /// Determine if the file is compressed. None means kernel does not indicate this attrbiute is supported by the filesystem + pub fn compressed(&self) -> Option { + if self.inner.stx_attributes_mask & libc::STATX_ATTR_COMPRESSED as u64 > 0 { + Some(self.inner.stx_attributes & libc::STATX_ATTR_COMPRESSED as u64 > 0) + } else { + None + } + } + + /// Determine if the file is immutable. None means kernel does not indicate this attrbiute is supported by the filesystem + pub fn immutable(&self) -> Option { + if self.inner.stx_attributes_mask & libc::STATX_ATTR_IMMUTABLE as u64 > 0 { + Some(self.inner.stx_attributes & libc::STATX_ATTR_IMMUTABLE as u64 > 0) + } else { + None + } + } + + /// Determine if the file is append-only. None means kernel does not indicate this attrbiute is supported by the filesystem + pub fn append_only(&self) -> Option { + if self.inner.stx_attributes_mask & libc::STATX_ATTR_APPEND as u64 > 0 { + Some(self.inner.stx_attributes & libc::STATX_ATTR_APPEND as u64 > 0) + } else { + None + } + } + + /// Determine if the file is not a candidate for a backup. None means kernel does not indicate this attrbiute is supported by the filesystem + pub fn nodump(&self) -> Option { + if self.inner.stx_attributes_mask & libc::STATX_ATTR_NODUMP as u64 > 0 { + Some(self.inner.stx_attributes & libc::STATX_ATTR_NODUMP as u64 > 0) + } else { + None + } + } + + /// Determine if the file requires a key to be encrypted(?). None means kernel does not indicate this attrbiute is supported by the filesystem + pub fn encrypted(&self) -> Option { + if self.inner.stx_attributes_mask & libc::STATX_ATTR_ENCRYPTED as u64 > 0 { + Some(self.inner.stx_attributes & libc::STATX_ATTR_ENCRYPTED as u64 > 0) + } else { + None + } + } + + /* + /// Determine if the file has fs-verify enabled. None means kernel does not indicate this attrbiute is supported by the filesystem + pub fn verify_enabled(&self) -> Option { + if self.inner.stx_attributes_mask & libc::STATX_ATTR_VERITY as u64 > 0 { + Some(self.inner.stx_attributes & libc::STATX_ATTR_VERITY as u64 > 0) + } else { + None + } + } + + /// Determine if the file is in CPU direct access state. None means kernel does not indicate this attrbiute is supported by the filesystem. + pub fn dax(&self) -> Option { + if self.inner.stx_attributes_mask & libc::STATX_ATTR_DAX as u64 > 0 { + Some(self.inner.stx_attributes & libc::STATX_ATTR_DAX as u64 > 0) + } else { + None + } + } + */ +} From 2ad877cd3a0722111b5a1832bcef184f529be36b Mon Sep 17 00:00:00 2001 From: Vitaly Shukela Date: Sun, 6 Mar 2022 20:24:37 +0300 Subject: [PATCH 2/7] Update src/fcntl/statx.rs Co-authored-by: Alan Somers --- src/fcntl/statx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fcntl/statx.rs b/src/fcntl/statx.rs index ec5153cc09..b2cef6e6f3 100644 --- a/src/fcntl/statx.rs +++ b/src/fcntl/statx.rs @@ -182,7 +182,7 @@ impl Statx { /// Retrieve uid (owner, user ID), if it has been returned by kernel. pub fn uid(&self) -> Option { - if Mask::STATX_NLINK.bits() & self.inner.stx_mask > 0 { + if Mask::STATX_UID.bits() & self.inner.stx_mask > 0 { Some(Uid::from_raw(self.inner.stx_uid)) } else { None From 00d7309391d21a63b3c522b26a4a8e7480634259 Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela Date: Mon, 7 Mar 2022 03:37:34 +0300 Subject: [PATCH 3/7] Fix some code review issues --- src/fcntl.rs | 2 +- src/fcntl/statx.rs | 102 +++++++++++++++++++++------------------------ 2 files changed, 48 insertions(+), 56 deletions(-) diff --git a/src/fcntl.rs b/src/fcntl.rs index 13fb13a392..567efc4a72 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -890,5 +890,5 @@ pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Resu } } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(all(feature="fs", any(target_os = "android", target_os = "linux")))] pub mod statx; diff --git a/src/fcntl/statx.rs b/src/fcntl/statx.rs index b2cef6e6f3..812b2c5fdc 100644 --- a/src/fcntl/statx.rs +++ b/src/fcntl/statx.rs @@ -7,7 +7,8 @@ use crate::{errno::Errno, NixPath, Result, sys::stat::{SFlag, Mode}, unistd::{Ui libc_bitflags!( /// Configuration options for statx. pub struct Flags: c_int { - /// Allow empty `pathname`, to receive status of the specified file descriptor itself (not a directory entry) instead. + /// Allow empty `pathname`, to receive status of the specified file descriptor + /// itself (not a directory entry) instead. AT_EMPTY_PATH; /// Do not automount the last component of the path being queried. @@ -19,11 +20,13 @@ libc_bitflags!( /// Do whatever usual, non-`x` stat does. The default. AT_STATX_SYNC_AS_STAT; - /// Force returned attributes to be up to date (synchonized). May trigger data writeback. + /// Force returned attributes to be up to date (synchonized). + /// May trigger data writeback. /// May involve additional roudtrips for networked filesystems. AT_STATX_FORCE_SYNC; - /// Don't synchronize, use cached data if possible. This may lead to the returned data to be approximate. + /// Don't synchronize, use cached data if possible. This may lead to the + /// returned data to be approximate. AT_STATX_DONT_SYNC; } ); @@ -64,41 +67,16 @@ libc_bitflags!( } ); -/* -libc_bitflags!( - pub struct FileAttributes: c_int { - /// The file is compressed by the filesystem and may take extra resources to access. - STATX_ATTR_COMPRESSED; - - /// The file cannot be modified: it cannot be deleted or renamed, no hard links can be created to this file and no data can be written to it. - STATX_ATTR_IMMUTABLE; - - /// The file can only be opened in append mode for writing. Random access writing is not permitted. - STATX_ATTR_APPEND; - - - /// File is not a candidate for backup when a backup program such as `dump` is run. - STATX_ATTR_NODUMP; - - /// A key is required for the file to be encrypted by the filesystem. - STATX_ATTR_ENCRYPTED; - - // /// (since Linux 5.5) - // /// The file has fs-verity enabled. It cannot be written to, and all reads from it will be verified - // /// against a cryptographic hash that covers the entire file - // /// (e.g., via a Merkle tree). - // STATX_ATTR_VERITY; - - // /// (since Linux 5.8) - // /// The file is in the DAX (cpu direct access) state. - // STATX_ATTR_DAX; - } -); -*/ - -/// Attempt to retrieve stats of `pathname`. If `pathname` is relative, `dirfd` is used as starting directory for lookups. If `dirfs` is None, current directory is used. -/// `mask` determines what stats entries should be filled in, but kernel can actually fill more or less than requested. -/// `statx` is not atomic: if attributes are being changed at the time of `statx` is called, the returned attributes set may have items from different moments of time. +/// Attempt to retrieve stats of `pathname`. If `pathname` is relative, `dirfd` +/// is used as starting directory for lookups. If `dirfs` is None, current +/// directory is used. +/// `pathname` may be empty string. But instead of specifying empty string +/// literal, you are adviced to use zero-terminated `CStr` to avoid extra allocation +/// `mask` determines what stats entries should be filled in, but kernel +/// can actually fill more or less than requested. +/// `statx` is not atomic: if attributes are being changed at the time of +/// `statx` is called, the returned attributes set may have items from +/// different moments of time. pub fn statx( dirfd: Option, pathname: &P, @@ -261,12 +239,13 @@ impl Statx { } } - /// Retrieve file size as a number of blocks, if it has been returned by kernel - pub fn blksize(&self) -> Option { + /// Retrieve size of the block (used in `blocks` function) in bytes + pub fn blksize(&self) -> Option { // I'm not sure what exact mask bit should be used to check presence of block size. - // Actual Linux kernel seems return most of STATX_BASIC_STATS in one go, regarless of which things are asked for. + // Actual Linux kernel seems return most of STATX_BASIC_STATS + // in one go, regarless of which things were asked for. if Mask::STATX_BASIC_STATS.bits() & self.inner.stx_mask == Mask::STATX_BASIC_STATS.bits() { - Some(self.inner.stx_blocks) + Some(self.inner.stx_blksize) } else { None } @@ -281,10 +260,12 @@ impl Statx { } } - /// Retrieve device major and minor numbers (first and second elements of the tuple respectively) of the filesystem where the file resides at, if it has been returned by kernel. + /// Retrieve device major and minor numbers (first and second elements of the + /// tuple respectively) of the filesystem where the file resides at, if it has been returned by kernel. pub fn dev_major_minor(&self) -> Option<(u32, u32)> { // I'm not sure what exact mask bit should be used to check presence of this information. - // Actual Linux kernel seems return most of STATX_BASIC_STATS in one go, regarless of which things are asked for. + // Actual Linux kernel seems return most of STATX_BASIC_STATS in one go, + // regarless of which things were asked for. if Mask::STATX_BASIC_STATS.bits() & self.inner.stx_mask == Mask::STATX_BASIC_STATS.bits() { Some((self.inner.stx_dev_major, self.inner.stx_dev_minor)) } else { @@ -292,19 +273,24 @@ impl Statx { } } - /// Retrieve pointed-to device major and minor numbers (first and second elements of the tuple respectively) of this character or block device file, if it has been returned by kernel. - /// Note that this function does not check for the file type. It would return `Some` even for regular files. + /// Retrieve pointed-to device major and minor numbers (first and second + /// elements of the tuple respectively) of this character or block device + /// file, if it has been returned by kernel. + /// Note that this function does not check for the file type. + /// It would return `Some` even for regular files. pub fn rdev_major_minor(&self) -> Option<(u32, u32)> { // I'm not sure what exact mask bit should be used to check presence of this information. - // Actual Linux kernel seems return most of STATX_BASIC_STATS in one go, regarless of which things are asked for. + // Actual Linux kernel seems return most of STATX_BASIC_STATS + // in one go, regarless of which things were asked for. if Mask::STATX_BASIC_STATS.bits() & self.inner.stx_mask == Mask::STATX_BASIC_STATS.bits() { - Some((self.inner.stx_dev_major, self.inner.stx_dev_minor)) + Some((self.inner.stx_rdev_major, self.inner.stx_rdev_minor)) } else { None } } - /// Determine if the file is compressed. None means kernel does not indicate this attrbiute is supported by the filesystem + /// Determine if the file is compressed. None means kernel does not + /// indicate this attrbiute is supported by the filesystem pub fn compressed(&self) -> Option { if self.inner.stx_attributes_mask & libc::STATX_ATTR_COMPRESSED as u64 > 0 { Some(self.inner.stx_attributes & libc::STATX_ATTR_COMPRESSED as u64 > 0) @@ -313,7 +299,8 @@ impl Statx { } } - /// Determine if the file is immutable. None means kernel does not indicate this attrbiute is supported by the filesystem + /// Determine if the file is immutable. None means kernel does not indicate this + /// attrbiute is supported by the filesystem pub fn immutable(&self) -> Option { if self.inner.stx_attributes_mask & libc::STATX_ATTR_IMMUTABLE as u64 > 0 { Some(self.inner.stx_attributes & libc::STATX_ATTR_IMMUTABLE as u64 > 0) @@ -322,7 +309,8 @@ impl Statx { } } - /// Determine if the file is append-only. None means kernel does not indicate this attrbiute is supported by the filesystem + /// Determine if the file is append-only. None means kernel does not indicate + /// this attrbiute is supported by the filesystem pub fn append_only(&self) -> Option { if self.inner.stx_attributes_mask & libc::STATX_ATTR_APPEND as u64 > 0 { Some(self.inner.stx_attributes & libc::STATX_ATTR_APPEND as u64 > 0) @@ -331,7 +319,8 @@ impl Statx { } } - /// Determine if the file is not a candidate for a backup. None means kernel does not indicate this attrbiute is supported by the filesystem + /// Determine if the file is not a candidate for a backup. None means kernel + /// does not indicate this attrbiute is supported by the filesystem pub fn nodump(&self) -> Option { if self.inner.stx_attributes_mask & libc::STATX_ATTR_NODUMP as u64 > 0 { Some(self.inner.stx_attributes & libc::STATX_ATTR_NODUMP as u64 > 0) @@ -340,7 +329,8 @@ impl Statx { } } - /// Determine if the file requires a key to be encrypted(?). None means kernel does not indicate this attrbiute is supported by the filesystem + /// Determine if the file requires a key to be encrypted(?). + /// None means kernel does not indicate this attrbiute is supported by the filesystem pub fn encrypted(&self) -> Option { if self.inner.stx_attributes_mask & libc::STATX_ATTR_ENCRYPTED as u64 > 0 { Some(self.inner.stx_attributes & libc::STATX_ATTR_ENCRYPTED as u64 > 0) @@ -350,7 +340,8 @@ impl Statx { } /* - /// Determine if the file has fs-verify enabled. None means kernel does not indicate this attrbiute is supported by the filesystem + /// Determine if the file has fs-verify enabled. None means kernel does not + /// indicate this attrbiute is supported by the filesystem pub fn verify_enabled(&self) -> Option { if self.inner.stx_attributes_mask & libc::STATX_ATTR_VERITY as u64 > 0 { Some(self.inner.stx_attributes & libc::STATX_ATTR_VERITY as u64 > 0) @@ -359,7 +350,8 @@ impl Statx { } } - /// Determine if the file is in CPU direct access state. None means kernel does not indicate this attrbiute is supported by the filesystem. + /// Determine if the file is in CPU direct access state. None means kernel + /// does not indicate this attrbiute is supported by the filesystem. pub fn dax(&self) -> Option { if self.inner.stx_attributes_mask & libc::STATX_ATTR_DAX as u64 > 0 { Some(self.inner.stx_attributes & libc::STATX_ATTR_DAX as u64 > 0) From 646e10d7c046f718e3955b733e4237402cd8051d Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela Date: Mon, 7 Mar 2022 03:37:46 +0300 Subject: [PATCH 4/7] Remove statx_without_path --- src/fcntl/statx.rs | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/fcntl/statx.rs b/src/fcntl/statx.rs index 812b2c5fdc..0fb7e8acb3 100644 --- a/src/fcntl/statx.rs +++ b/src/fcntl/statx.rs @@ -103,28 +103,6 @@ pub fn statx( }) } -/// Get stats of specified file descriptor (not nesessarily directory) without resolving the path. -/// Automaitcally sets the `AT_EMPTY_PATH` flag. -pub fn statx_without_path(dirfd: RawFd, mut flags: Flags, mask: Mask) -> Result { - flags.set(Flags::AT_EMPTY_PATH, true); - let mut dst = mem::MaybeUninit::uninit(); - let res = unsafe { - libc::statx( - dirfd, - b"\0" as *const u8 as *const libc::c_char, - flags.bits() as libc::c_int, - mask.bits() as libc::c_uint, - dst.as_mut_ptr(), - ) - }; - - Errno::result(res)?; - - Ok(Statx { - inner: unsafe { dst.assume_init() }, - }) -} - #[derive(Debug,Copy,Clone)] pub struct Statx { pub inner: libc::statx, From 91eda3cb4d9172b93928ffe0cb9bad80972196cc Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela Date: Mon, 7 Mar 2022 03:48:57 +0300 Subject: [PATCH 5/7] Fix more code review issues --- src/fcntl/statx.rs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/fcntl/statx.rs b/src/fcntl/statx.rs index 0fb7e8acb3..91c1135047 100644 --- a/src/fcntl/statx.rs +++ b/src/fcntl/statx.rs @@ -2,7 +2,7 @@ use std::{mem, os::unix::prelude::RawFd}; use libc::{self, c_int, c_uint, mode_t}; -use crate::{errno::Errno, NixPath, Result, sys::stat::{SFlag, Mode}, unistd::{Uid, Gid}}; +use crate::{errno::Errno, NixPath, Result, sys::stat::{SFlag, Mode}, unistd::{Uid, Gid}, sys::time::TimeSpec}; libc_bitflags!( /// Configuration options for statx. @@ -108,9 +108,18 @@ pub struct Statx { pub inner: libc::statx, } +impl From for TimeSpec { + fn from(x: libc::statx_timestamp) -> Self { + TimeSpec::from_timespec(libc::timespec { + tv_sec: x.tv_sec, + tv_nsec: x.tv_nsec.into(), + }) + } +} + impl Statx { /// Retrieve file type, if it has been returned by kernel - pub fn r#type(&self) -> Option { + pub fn filetype(&self) -> Option { if Mask::STATX_TYPE.bits() & self.inner.stx_mask > 0 { Some(SFlag::from_bits_truncate(self.inner.stx_mode as mode_t & SFlag::S_IFMT.bits())) } else { @@ -147,7 +156,7 @@ impl Statx { /// Retrieve gid (group ID), if it has been returned by kernel. pub fn gid(&self) -> Option { - if Mask::STATX_NLINK.bits() & self.inner.stx_mask > 0 { + if Mask::STATX_GID.bits() & self.inner.stx_mask > 0 { Some(Gid::from_raw(self.inner.stx_uid)) } else { None @@ -155,36 +164,36 @@ impl Statx { } /// Retrieve file access time, if it has been returned by kernel - pub fn atime(&self) -> Option { + pub fn atime(&self) -> Option { if Mask::STATX_ATIME.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_atime) + Some(self.inner.stx_atime.into()) } else { None } } /// Retrieve file modification time, if it has been returned by kernel - pub fn mtime(&self) -> Option { + pub fn mtime(&self) -> Option { if Mask::STATX_MTIME.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_mtime) + Some(self.inner.stx_mtime.into()) } else { None } } /// Retrieve file attribute change time, if it has been returned by kernel - pub fn ctime(&self) -> Option { + pub fn ctime(&self) -> Option { if Mask::STATX_CTIME.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_ctime) + Some(self.inner.stx_ctime.into()) } else { None } } /// Retrieve file birth (creation) time, if it has been returned by kernel - pub fn btime(&self) -> Option { + pub fn btime(&self) -> Option { if Mask::STATX_BTIME.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_btime) + Some(self.inner.stx_btime.into()) } else { None } From 3f9a3df5866d94f189053f398615f807197c2491 Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela Date: Mon, 7 Mar 2022 04:35:20 +0300 Subject: [PATCH 6/7] statx: tuple struct --- src/fcntl/statx.rs | 100 ++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/src/fcntl/statx.rs b/src/fcntl/statx.rs index 91c1135047..9379c927d8 100644 --- a/src/fcntl/statx.rs +++ b/src/fcntl/statx.rs @@ -98,15 +98,11 @@ pub fn statx( Errno::result(res)?; - Ok(Statx { - inner: unsafe { dst.assume_init() }, - }) + Ok(Statx ( unsafe { dst.assume_init() } )) } #[derive(Debug,Copy,Clone)] -pub struct Statx { - pub inner: libc::statx, -} +pub struct Statx (pub libc::statx); impl From for TimeSpec { fn from(x: libc::statx_timestamp) -> Self { @@ -120,8 +116,8 @@ impl From for TimeSpec { impl Statx { /// Retrieve file type, if it has been returned by kernel pub fn filetype(&self) -> Option { - if Mask::STATX_TYPE.bits() & self.inner.stx_mask > 0 { - Some(SFlag::from_bits_truncate(self.inner.stx_mode as mode_t & SFlag::S_IFMT.bits())) + if Mask::STATX_TYPE.bits() & self.0.stx_mask > 0 { + Some(SFlag::from_bits_truncate(self.0.stx_mode as mode_t & SFlag::S_IFMT.bits())) } else { None } @@ -129,8 +125,8 @@ impl Statx { /// Retrieve file mode, if it has been returned by kernel. pub fn mode(&self) -> Option { - if Mask::STATX_MODE.bits() & self.inner.stx_mask > 0 { - Some(Mode::from_bits_truncate(self.inner.stx_mode as mode_t)) + if Mask::STATX_MODE.bits() & self.0.stx_mask > 0 { + Some(Mode::from_bits_truncate(self.0.stx_mode as mode_t)) } else { None } @@ -138,8 +134,8 @@ impl Statx { /// Retrieve number of hard links, if it has been returned by kernel. pub fn nlinks(&self) -> Option { - if Mask::STATX_NLINK.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_nlink) + if Mask::STATX_NLINK.bits() & self.0.stx_mask > 0 { + Some(self.0.stx_nlink) } else { None } @@ -147,8 +143,8 @@ impl Statx { /// Retrieve uid (owner, user ID), if it has been returned by kernel. pub fn uid(&self) -> Option { - if Mask::STATX_UID.bits() & self.inner.stx_mask > 0 { - Some(Uid::from_raw(self.inner.stx_uid)) + if Mask::STATX_UID.bits() & self.0.stx_mask > 0 { + Some(Uid::from_raw(self.0.stx_uid)) } else { None } @@ -156,8 +152,8 @@ impl Statx { /// Retrieve gid (group ID), if it has been returned by kernel. pub fn gid(&self) -> Option { - if Mask::STATX_GID.bits() & self.inner.stx_mask > 0 { - Some(Gid::from_raw(self.inner.stx_uid)) + if Mask::STATX_GID.bits() & self.0.stx_mask > 0 { + Some(Gid::from_raw(self.0.stx_uid)) } else { None } @@ -165,8 +161,8 @@ impl Statx { /// Retrieve file access time, if it has been returned by kernel pub fn atime(&self) -> Option { - if Mask::STATX_ATIME.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_atime.into()) + if Mask::STATX_ATIME.bits() & self.0.stx_mask > 0 { + Some(self.0.stx_atime.into()) } else { None } @@ -174,8 +170,8 @@ impl Statx { /// Retrieve file modification time, if it has been returned by kernel pub fn mtime(&self) -> Option { - if Mask::STATX_MTIME.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_mtime.into()) + if Mask::STATX_MTIME.bits() & self.0.stx_mask > 0 { + Some(self.0.stx_mtime.into()) } else { None } @@ -183,8 +179,8 @@ impl Statx { /// Retrieve file attribute change time, if it has been returned by kernel pub fn ctime(&self) -> Option { - if Mask::STATX_CTIME.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_ctime.into()) + if Mask::STATX_CTIME.bits() & self.0.stx_mask > 0 { + Some(self.0.stx_ctime.into()) } else { None } @@ -192,8 +188,8 @@ impl Statx { /// Retrieve file birth (creation) time, if it has been returned by kernel pub fn btime(&self) -> Option { - if Mask::STATX_BTIME.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_btime.into()) + if Mask::STATX_BTIME.bits() & self.0.stx_mask > 0 { + Some(self.0.stx_btime.into()) } else { None } @@ -201,8 +197,8 @@ impl Statx { /// Retrieve inode number, if it has been returned by kernel pub fn ino(&self) -> Option { - if Mask::STATX_INO.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_ino) + if Mask::STATX_INO.bits() & self.0.stx_mask > 0 { + Some(self.0.stx_ino) } else { None } @@ -210,8 +206,8 @@ impl Statx { /// Retrieve file size, in bytes, if it has been returned by kernel pub fn size(&self) -> Option { - if Mask::STATX_SIZE.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_size) + if Mask::STATX_SIZE.bits() & self.0.stx_mask > 0 { + Some(self.0.stx_size) } else { None } @@ -219,8 +215,8 @@ impl Statx { /// Retrieve file size as a number of blocks, if it has been returned by kernel pub fn blocks(&self) -> Option { - if Mask::STATX_BLOCKS.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_blocks) + if Mask::STATX_BLOCKS.bits() & self.0.stx_mask > 0 { + Some(self.0.stx_blocks) } else { None } @@ -231,8 +227,8 @@ impl Statx { // I'm not sure what exact mask bit should be used to check presence of block size. // Actual Linux kernel seems return most of STATX_BASIC_STATS // in one go, regarless of which things were asked for. - if Mask::STATX_BASIC_STATS.bits() & self.inner.stx_mask == Mask::STATX_BASIC_STATS.bits() { - Some(self.inner.stx_blksize) + if Mask::STATX_BASIC_STATS.bits() & self.0.stx_mask == Mask::STATX_BASIC_STATS.bits() { + Some(self.0.stx_blksize) } else { None } @@ -240,8 +236,8 @@ impl Statx { /// Retrieve mount ID, if it has been returned by kernel pub fn mnt_id(&self) -> Option { - if Mask::STATX_MNT_ID.bits() & self.inner.stx_mask > 0 { - Some(self.inner.stx_mnt_id) + if Mask::STATX_MNT_ID.bits() & self.0.stx_mask > 0 { + Some(self.0.stx_mnt_id) } else { None } @@ -253,8 +249,8 @@ impl Statx { // I'm not sure what exact mask bit should be used to check presence of this information. // Actual Linux kernel seems return most of STATX_BASIC_STATS in one go, // regarless of which things were asked for. - if Mask::STATX_BASIC_STATS.bits() & self.inner.stx_mask == Mask::STATX_BASIC_STATS.bits() { - Some((self.inner.stx_dev_major, self.inner.stx_dev_minor)) + if Mask::STATX_BASIC_STATS.bits() & self.0.stx_mask == Mask::STATX_BASIC_STATS.bits() { + Some((self.0.stx_dev_major, self.0.stx_dev_minor)) } else { None } @@ -269,8 +265,8 @@ impl Statx { // I'm not sure what exact mask bit should be used to check presence of this information. // Actual Linux kernel seems return most of STATX_BASIC_STATS // in one go, regarless of which things were asked for. - if Mask::STATX_BASIC_STATS.bits() & self.inner.stx_mask == Mask::STATX_BASIC_STATS.bits() { - Some((self.inner.stx_rdev_major, self.inner.stx_rdev_minor)) + if Mask::STATX_BASIC_STATS.bits() & self.0.stx_mask == Mask::STATX_BASIC_STATS.bits() { + Some((self.0.stx_rdev_major, self.0.stx_rdev_minor)) } else { None } @@ -279,8 +275,8 @@ impl Statx { /// Determine if the file is compressed. None means kernel does not /// indicate this attrbiute is supported by the filesystem pub fn compressed(&self) -> Option { - if self.inner.stx_attributes_mask & libc::STATX_ATTR_COMPRESSED as u64 > 0 { - Some(self.inner.stx_attributes & libc::STATX_ATTR_COMPRESSED as u64 > 0) + if self.0.stx_attributes_mask & libc::STATX_ATTR_COMPRESSED as u64 > 0 { + Some(self.0.stx_attributes & libc::STATX_ATTR_COMPRESSED as u64 > 0) } else { None } @@ -289,8 +285,8 @@ impl Statx { /// Determine if the file is immutable. None means kernel does not indicate this /// attrbiute is supported by the filesystem pub fn immutable(&self) -> Option { - if self.inner.stx_attributes_mask & libc::STATX_ATTR_IMMUTABLE as u64 > 0 { - Some(self.inner.stx_attributes & libc::STATX_ATTR_IMMUTABLE as u64 > 0) + if self.0.stx_attributes_mask & libc::STATX_ATTR_IMMUTABLE as u64 > 0 { + Some(self.0.stx_attributes & libc::STATX_ATTR_IMMUTABLE as u64 > 0) } else { None } @@ -299,8 +295,8 @@ impl Statx { /// Determine if the file is append-only. None means kernel does not indicate /// this attrbiute is supported by the filesystem pub fn append_only(&self) -> Option { - if self.inner.stx_attributes_mask & libc::STATX_ATTR_APPEND as u64 > 0 { - Some(self.inner.stx_attributes & libc::STATX_ATTR_APPEND as u64 > 0) + if self.0.stx_attributes_mask & libc::STATX_ATTR_APPEND as u64 > 0 { + Some(self.0.stx_attributes & libc::STATX_ATTR_APPEND as u64 > 0) } else { None } @@ -309,8 +305,8 @@ impl Statx { /// Determine if the file is not a candidate for a backup. None means kernel /// does not indicate this attrbiute is supported by the filesystem pub fn nodump(&self) -> Option { - if self.inner.stx_attributes_mask & libc::STATX_ATTR_NODUMP as u64 > 0 { - Some(self.inner.stx_attributes & libc::STATX_ATTR_NODUMP as u64 > 0) + if self.0.stx_attributes_mask & libc::STATX_ATTR_NODUMP as u64 > 0 { + Some(self.0.stx_attributes & libc::STATX_ATTR_NODUMP as u64 > 0) } else { None } @@ -319,8 +315,8 @@ impl Statx { /// Determine if the file requires a key to be encrypted(?). /// None means kernel does not indicate this attrbiute is supported by the filesystem pub fn encrypted(&self) -> Option { - if self.inner.stx_attributes_mask & libc::STATX_ATTR_ENCRYPTED as u64 > 0 { - Some(self.inner.stx_attributes & libc::STATX_ATTR_ENCRYPTED as u64 > 0) + if self.0.stx_attributes_mask & libc::STATX_ATTR_ENCRYPTED as u64 > 0 { + Some(self.0.stx_attributes & libc::STATX_ATTR_ENCRYPTED as u64 > 0) } else { None } @@ -330,8 +326,8 @@ impl Statx { /// Determine if the file has fs-verify enabled. None means kernel does not /// indicate this attrbiute is supported by the filesystem pub fn verify_enabled(&self) -> Option { - if self.inner.stx_attributes_mask & libc::STATX_ATTR_VERITY as u64 > 0 { - Some(self.inner.stx_attributes & libc::STATX_ATTR_VERITY as u64 > 0) + if self.0.stx_attributes_mask & libc::STATX_ATTR_VERITY as u64 > 0 { + Some(self.0.stx_attributes & libc::STATX_ATTR_VERITY as u64 > 0) } else { None } @@ -340,8 +336,8 @@ impl Statx { /// Determine if the file is in CPU direct access state. None means kernel /// does not indicate this attrbiute is supported by the filesystem. pub fn dax(&self) -> Option { - if self.inner.stx_attributes_mask & libc::STATX_ATTR_DAX as u64 > 0 { - Some(self.inner.stx_attributes & libc::STATX_ATTR_DAX as u64 > 0) + if self.0.stx_attributes_mask & libc::STATX_ATTR_DAX as u64 > 0 { + Some(self.0.stx_attributes & libc::STATX_ATTR_DAX as u64 > 0) } else { None } From 913aea0952b7c94e98323faa843c2b1378f121b1 Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela Date: Mon, 7 Mar 2022 04:37:05 +0300 Subject: [PATCH 7/7] statx: fix typo in doccomment --- src/fcntl/statx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fcntl/statx.rs b/src/fcntl/statx.rs index 9379c927d8..8c3110a08e 100644 --- a/src/fcntl/statx.rs +++ b/src/fcntl/statx.rs @@ -68,7 +68,7 @@ libc_bitflags!( ); /// Attempt to retrieve stats of `pathname`. If `pathname` is relative, `dirfd` -/// is used as starting directory for lookups. If `dirfs` is None, current +/// is used as starting directory for lookups. If `dirfd` is None, current /// directory is used. /// `pathname` may be empty string. But instead of specifying empty string /// literal, you are adviced to use zero-terminated `CStr` to avoid extra allocation