Add chown functions to std::os::unix::fs to change the owner and group of files

This is a straightforward wrapper that uses the existing helpers for C
string handling and errno handling.

Having this available is convenient for UNIX utility programs written in
Rust, and avoids having to call unsafe functions like `libc::chown`
directly and handle errors manually, in a program that may otherwise be
entirely safe code.

In addition, these functions provide a more Rustic interface by
accepting appropriate traits and using `None` rather than `-1`.
This commit is contained in:
Josh Triplett 2021-09-14 18:29:38 -07:00
parent c3c0f80d60
commit 4840f67fcb
2 changed files with 87 additions and 0 deletions

View file

@ -5,6 +5,7 @@
use super::platform::fs::MetadataExt as _;
use crate::fs::{self, OpenOptions, Permissions};
use crate::io;
use crate::os::unix::io::{AsFd, AsRawFd};
use crate::path::Path;
use crate::sys;
use crate::sys_common::{AsInner, AsInnerMut, FromInner};
@ -924,6 +925,75 @@ impl DirBuilderExt for fs::DirBuilder {
}
}
/// Change the owner and group of the specified path.
///
/// Specifying either the uid or gid as `None` will leave it unchanged.
///
/// Changing the owner typically requires privileges, such as root or a specific capability.
/// Changing the group typically requires either being the owner and a member of the group, or
/// having privileges.
///
/// If called on a symbolic link, this will change the owner and group of the link target. To
/// change the owner and group of the link itself, see [`lchown`].
///
/// # Examples
///
/// ```no_run
/// #![feature(unix_chown)]
/// use std::os::unix::fs;
///
/// fn main() -> std::io::Result<()> {
/// fs::chown("/sandbox", Some(0), Some(0))?;
/// Ok(())
/// }
/// ```
#[unstable(feature = "unix_chown", issue = "none")]
pub fn chown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> {
sys::fs::chown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX))
}
/// Change the owner and group of the file referenced by the specified open file descriptor.
///
/// For semantics and required privileges, see [`chown`].
///
/// # Examples
///
/// ```no_run
/// #![feature(unix_chown)]
/// use std::os::unix::fs;
///
/// fn main() -> std::io::Result<()> {
/// let f = std::fs::File::open("/file")?;
/// fs::fchown(f, Some(0), Some(0))?;
/// Ok(())
/// }
/// ```
#[unstable(feature = "unix_chown", issue = "none")]
pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> {
sys::fs::fchown(fd.as_fd().as_raw_fd(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX))
}
/// Change the owner and group of the specified path, without dereferencing symbolic links.
///
/// Identical to [`chown`], except that if called on a symbolic link, this will change the owner
/// and group of the link itself rather than the owner and group of the link target.
///
/// # Examples
///
/// ```no_run
/// #![feature(unix_chown)]
/// use std::os::unix::fs;
///
/// fn main() -> std::io::Result<()> {
/// fs::lchown("/symlink", Some(0), Some(0))?;
/// Ok(())
/// }
/// ```
#[unstable(feature = "unix_chown", issue = "none")]
pub fn lchown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> {
sys::fs::lchown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX))
}
/// Change the root directory of the current process to the specified path.
///
/// This typically requires privileges, such as root or a specific capability.

View file

@ -1416,6 +1416,23 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
Ok(bytes_copied as u64)
}
pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
let path = cstr(path)?;
cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
Ok(())
}
pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?;
Ok(())
}
pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
let path = cstr(path)?;
cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
Ok(())
}
#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
pub fn chroot(dir: &Path) -> io::Result<()> {
let dir = cstr(dir)?;