diff --git a/CHANGELOG.md b/CHANGELOG.md index b7c3df5f53..4f60b40b6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,10 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] - ReleaseDate ### Added +- Added `chflags`. + (#[1757](https://github.com/nix-rust/nix/pull/1757)) - Added `aio_writev` and `aio_readv`. (#[1713](https://github.com/nix-rust/nix/pull/1713)) - - impl `From` for `Uid` and `From` for `Gid` (#[1727](https://github.com/nix-rust/nix/pull/1727)) - impl From for std::net::SocketAddrV4 and diff --git a/src/sys/stat.rs b/src/sys/stat.rs index 5cf2deb75e..16bccfd31b 100644 --- a/src/sys/stat.rs +++ b/src/sys/stat.rs @@ -1,4 +1,13 @@ pub use libc::{dev_t, mode_t}; +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub use libc::c_uint; +#[cfg(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "freebsd", + target_os = "dragonfly" +))] +pub use libc::c_ulong; pub use libc::stat as FileStat; use crate::{Result, NixPath, errno::Errno}; @@ -43,6 +52,111 @@ libc_bitflags! { } } +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub type type_of_file_flag = c_uint; +#[cfg(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "freebsd", + target_os = "dragonfly" +))] +pub type type_of_file_flag = c_ulong; + +#[cfg(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "macos", + target_os = "ios" +))] +libc_bitflags! { + /// File flags. + #[cfg_attr(docsrs, doc(cfg(all())))] + pub struct FileFlag: type_of_file_flag { + /// The file may only be appended to. + SF_APPEND; + /// The file has been archived. + SF_ARCHIVED; + #[cfg(any(target_os = "dragonfly"))] + SF_CACHE; + /// The file may not be changed. + SF_IMMUTABLE; + /// Indicates a WAPBL journal file. + #[cfg(any(target_os = "netbsd"))] + SF_LOG; + /// Do not retain history for file + #[cfg(any(target_os = "dragonfly"))] + SF_NOHISTORY; + /// The file may not be renamed or deleted. + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + SF_NOUNLINK; + /// Mask of superuser changeable flags + SF_SETTABLE; + /// Snapshot is invalid. + #[cfg(any(target_os = "netbsd"))] + SF_SNAPINVAL; + /// The file is a snapshot file. + #[cfg(any(target_os = "netbsd", target_os = "freebsd"))] + SF_SNAPSHOT; + #[cfg(any(target_os = "dragonfly"))] + SF_XLINK; + /// The file may only be appended to. + UF_APPEND; + /// The file needs to be archived. + #[cfg(any(target_os = "freebsd"))] + UF_ARCHIVE; + #[cfg(any(target_os = "dragonfly"))] + UF_CACHE; + /// File is compressed at the file system level. + #[cfg(any(target_os = "macos", target_os = "ios"))] + UF_COMPRESSED; + /// The file may be hidden from directory listings at the application's + /// discretion. + #[cfg(any( + target_os = "freebsd", + target_os = "macos", + target_os = "ios", + ))] + UF_HIDDEN; + /// The file may not be changed. + UF_IMMUTABLE; + /// Do not dump the file. + UF_NODUMP; + #[cfg(any(target_os = "dragonfly"))] + UF_NOHISTORY; + /// The file may not be renamed or deleted. + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + UF_NOUNLINK; + /// The file is offline, or has the Windows and CIFS + /// `FILE_ATTRIBUTE_OFFLINE` attribute. + #[cfg(any(target_os = "freebsd"))] + UF_OFFLINE; + /// The directory is opaque when viewed through a union stack. + UF_OPAQUE; + /// The file is read only, and may not be written or appended. + #[cfg(any(target_os = "freebsd"))] + UF_READONLY; + /// The file contains a Windows reparse point. + #[cfg(any(target_os = "freebsd"))] + UF_REPARSE; + /// Mask of owner changeable flags. + UF_SETTABLE; + /// The file has the Windows `FILE_ATTRIBUTE_SPARSE_FILE` attribute. + #[cfg(any(target_os = "freebsd"))] + UF_SPARSE; + /// The file has the DOS, Windows and CIFS `FILE_ATTRIBUTE_SYSTEM` + /// attribute. + #[cfg(any(target_os = "freebsd"))] + UF_SYSTEM; + /// File renames and deletes are tracked. + #[cfg(any(target_os = "macos", target_os = "ios"))] + UF_TRACKED; + #[cfg(any(target_os = "dragonfly"))] + UF_XLINK; + } +} + /// Create a special or ordinary file, by pathname. pub fn mknod(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> { let res = path.with_nix_path(|cstr| unsafe { diff --git a/src/unistd.rs b/src/unistd.rs index 4c9b67ffc9..925ce9097d 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -2,12 +2,10 @@ use crate::errno::{self, Errno}; #[cfg(not(target_os = "redox"))] -#[cfg(feature = "fs")] -use crate::fcntl::{at_rawfd, AtFlags}; -#[cfg(feature = "fs")] -use crate::fcntl::{fcntl, FcntlArg::F_SETFD, FdFlag, OFlag}; -#[cfg(feature = "fs")] -use crate::sys::stat::Mode; +use crate::{ + fcntl::{at_rawfd, AtFlags, fcntl, FcntlArg::F_SETFD, FdFlag, OFlag}, + sys::stat::{FileFlag, Mode} +}; use crate::{Error, NixPath, Result}; #[cfg(not(target_os = "redox"))] use cfg_if::cfg_if; @@ -3275,3 +3273,26 @@ pub fn getpeereid(fd: RawFd) -> Result<(Uid, Gid)> { Errno::result(ret).map(|_| (Uid(uid), Gid(gid))) } } + +feature! { +#![all(feature = "fs")] + +/// Set the file flags. +/// +/// See also [chflags(2)](https://www.freebsd.org/cgi/man.cgi?query=chflags&sektion=2) +#[cfg(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "macos", + target_os = "ios" +))] +pub fn chflags(path: &P, flags: FileFlag) -> Result<()> { + let res = path.with_nix_path(|cstr| unsafe { + libc::chflags(cstr.as_ptr(), flags.bits()) + })?; + + Errno::result(res).map(drop) +} +} diff --git a/test/sys/mod.rs b/test/sys/mod.rs index ed4ad736fb..20312120a6 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -29,6 +29,7 @@ mod test_signalfd; mod test_socket; #[cfg(not(any(target_os = "redox")))] mod test_sockopt; +mod test_stat; #[cfg(any(target_os = "android", target_os = "linux"))] mod test_sysinfo; #[cfg(not(any( diff --git a/test/sys/test_stat.rs b/test/sys/test_stat.rs new file mode 100644 index 0000000000..141348ec50 --- /dev/null +++ b/test/sys/test_stat.rs @@ -0,0 +1,30 @@ +#[cfg(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "macos", + target_os = "ios", +))] +#[test] +fn test_chflags() { + use std::os::unix::io::AsRawFd; + use nix::{ + sys::stat::{FileFlag, fstat}, + unistd::chflags + }; + use tempfile::NamedTempFile; + + let f = NamedTempFile::new().unwrap(); + + let initial = FileFlag::from_bits_truncate(fstat(f.as_raw_fd()).unwrap().st_flags.into()); + // UF_OFFLINE is preserved by all FreeBSD file systems, but not interpreted + // in any way. + let commanded = initial ^ FileFlag::UF_OFFLINE; + + chflags(f.path(), commanded).unwrap(); + + let changed = FileFlag::from_bits_truncate(fstat(f.as_raw_fd()).unwrap().st_flags.into()); + + assert_eq!(commanded, changed); +}