mirror of
https://git.proxmox.com/git/proxmox
synced 2025-06-06 09:42:42 +00:00
sys: add helpers to check file and directory permissions
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
This commit is contained in:
parent
37c9dbf1eb
commit
57723e98fd
@ -1,8 +1,9 @@
|
|||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
use std::os::fd::FromRawFd;
|
||||||
use std::os::unix::io::{AsRawFd, OwnedFd};
|
use std::os::unix::io::{AsRawFd, OwnedFd};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::{bail, Error};
|
use anyhow::{bail, format_err, Error};
|
||||||
use nix::errno::Errno;
|
use nix::errno::Errno;
|
||||||
use nix::fcntl::OFlag;
|
use nix::fcntl::OFlag;
|
||||||
use nix::sys::stat;
|
use nix::sys::stat;
|
||||||
@ -27,6 +28,51 @@ pub fn create_dir<P: AsRef<Path>>(path: P, options: CreateOptions) -> Result<(),
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensure a directory exists.
|
||||||
|
///
|
||||||
|
/// Like [create_dir], but does not fail if the directory already exists.
|
||||||
|
///
|
||||||
|
/// Directory permissions are verified and raise an error if enforce_permissions is set.
|
||||||
|
pub fn ensure_dir_exists<P: AsRef<Path>>(
|
||||||
|
path: P,
|
||||||
|
options: &CreateOptions,
|
||||||
|
enforce_permissions: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let uid = options.owner;
|
||||||
|
let gid = options.group;
|
||||||
|
|
||||||
|
let mode: stat::Mode = options
|
||||||
|
.perm
|
||||||
|
.unwrap_or(stat::Mode::from_bits_truncate(0o770));
|
||||||
|
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
match nix::unistd::mkdir(path, mode) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(nix::errno::Errno::EEXIST) => {
|
||||||
|
if enforce_permissions {
|
||||||
|
return options.check(path);
|
||||||
|
} else {
|
||||||
|
if let Err(err) = options.check(path) {
|
||||||
|
log::error!("{err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => bail!("unable to create directory {path:?} - {err}",),
|
||||||
|
}
|
||||||
|
|
||||||
|
let fd = nix::fcntl::open(path, OFlag::O_DIRECTORY, stat::Mode::empty())
|
||||||
|
.map(|fd| unsafe { OwnedFd::from_raw_fd(fd) })
|
||||||
|
.map_err(|err| format_err!("unable to open created directory {path:?} - {err}"))?;
|
||||||
|
// umask defaults to 022 so make sure the mode is fully honowed:
|
||||||
|
nix::sys::stat::fchmod(fd.as_raw_fd(), mode)
|
||||||
|
.map_err(|err| format_err!("unable to set mode for directory {path:?} - {err}"))?;
|
||||||
|
nix::unistd::fchown(fd.as_raw_fd(), uid, gid)
|
||||||
|
.map_err(|err| format_err!("unable to set ownership directory {path:?} - {err}"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Recursively create a path with separately defined metadata for intermediate directories and the
|
/// Recursively create a path with separately defined metadata for intermediate directories and the
|
||||||
/// final component in the path.
|
/// final component in the path.
|
||||||
///
|
///
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! File system related utilities
|
//! File system related utilities
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::{bail, Error};
|
use anyhow::{bail, Context, Error};
|
||||||
|
|
||||||
use nix::sys::stat;
|
use nix::sys::stat;
|
||||||
use nix::unistd::{Gid, Uid};
|
use nix::unistd::{Gid, Uid};
|
||||||
@ -84,6 +84,44 @@ impl CreateOptions {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check file/directory permissions.
|
||||||
|
///
|
||||||
|
/// Make sure that the file or dir is owned by uid/gid and has the correct mode.
|
||||||
|
pub fn check<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
let nix::sys::stat::FileStat {
|
||||||
|
st_uid,
|
||||||
|
st_gid,
|
||||||
|
st_mode,
|
||||||
|
..
|
||||||
|
} = nix::sys::stat::stat(path).with_context(|| format!("failed to stat {path:?}"))?;
|
||||||
|
|
||||||
|
if let Some(uid) = self.owner {
|
||||||
|
let uid = uid.as_raw();
|
||||||
|
if st_uid != uid {
|
||||||
|
bail!("bad owner on {path:?} ({st_uid} != {uid})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(group) = self.group {
|
||||||
|
let gid = group.as_raw();
|
||||||
|
if st_gid != gid {
|
||||||
|
bail!("bad group on {path:?} ({st_gid} != {gid})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mode) = self.perm {
|
||||||
|
let mode = mode.bits();
|
||||||
|
let perms = st_mode & !nix::sys::stat::SFlag::S_IFMT.bits();
|
||||||
|
if perms != mode {
|
||||||
|
bail!("bad permissions on {path:?} (0o{perms:o} != 0o{mode:o})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Convenience shortcut around having to import `Gid` from nix.
|
/// Convenience shortcut around having to import `Gid` from nix.
|
||||||
pub const fn group_root(self) -> Self {
|
pub const fn group_root(self) -> Self {
|
||||||
self.group(Gid::from_raw(0))
|
self.group(Gid::from_raw(0))
|
||||||
|
Loading…
Reference in New Issue
Block a user