proxmox-sys: imported proxmox tools/sys
And split some files into smaller parts. Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
This commit is contained in:
parent
4b1cb9f9b3
commit
32b69176dd
@ -11,9 +11,13 @@ exclude = [ "debian" ]
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
base64 = "0.13"
|
||||
lazy_static = "1.4"
|
||||
libc = "0.2.107"
|
||||
log = "0.4"
|
||||
nix = "0.19.1"
|
||||
serde_json = "1.0"
|
||||
zstd = { version = "0.6", features = [ "bindgen" ] }
|
||||
|
||||
proxmox = { path = "../proxmox", version = "0.15", default-features = false }
|
||||
proxmox-io = { path = "../proxmox-io", version = "1.0.0" }
|
||||
proxmox-lang = { path = "../proxmox-lang", version = "1.0.0" }
|
||||
|
@ -41,8 +41,8 @@ Provides:
|
||||
librust-proxmox-sys-0+default-dev (= ${binary:Version}),
|
||||
librust-proxmox-sys-0.1-dev (= ${binary:Version}),
|
||||
librust-proxmox-sys-0.1+default-dev (= ${binary:Version}),
|
||||
librust-proxmox-sys-0.1.1-dev (= ${binary:Version}),
|
||||
librust-proxmox-sys-0.1.1+default-dev (= ${binary:Version})
|
||||
librust-proxmox-sys-0.1.2-dev (= ${binary:Version}),
|
||||
librust-proxmox-sys-0.1.2+default-dev (= ${binary:Version})
|
||||
Description: System tools (using nix) - Rust source code
|
||||
This package contains the source for the Rust proxmox-sys crate, packaged by
|
||||
debcargo for use with cargo and dh-cargo.
|
||||
|
@ -53,7 +53,7 @@ pub fn crypt(password: &[u8], salt: &[u8]) -> Result<String, Error> {
|
||||
|
||||
pub fn encrypt_pw(password: &str) -> Result<String, Error> {
|
||||
|
||||
let salt = proxmox::sys::linux::random_data(8)?;
|
||||
let salt = crate::linux::random_data(8)?;
|
||||
let salt = format!("$5${}$", base64::encode_config(&salt, base64::CRYPT));
|
||||
|
||||
crypt(password.as_bytes(), salt.as_bytes())
|
||||
|
@ -150,7 +150,7 @@ macro_rules! other_error {
|
||||
|
||||
#[inline]
|
||||
fn into_io_result(self) -> io::Result<T> {
|
||||
self.map_err($crate::sys::error::io_err_other)
|
||||
self.map_err($crate::error::io_err_other)
|
||||
}
|
||||
}
|
||||
};
|
37
proxmox-sys/src/fd/borrowed_fd.rs
Normal file
37
proxmox-sys/src/fd/borrowed_fd.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
/// A borrowed file raw descriptor. (A `RawFd` with an attached lifetime).
|
||||
///
|
||||
/// For when using `&FdRef` is not an option.
|
||||
///
|
||||
/// This specifically does not implement `IntoRawFd` or `FromRawFd`, since those would drop life
|
||||
/// times.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct BorrowedFd<'a> {
|
||||
fd: RawFd,
|
||||
_borrow: PhantomData<&'a RawFd>,
|
||||
}
|
||||
|
||||
impl<'a> BorrowedFd<'a> {
|
||||
#[inline]
|
||||
pub fn new<T: ?Sized + AsRawFd>(fd: &T) -> Self {
|
||||
Self {
|
||||
fd: fd.as_raw_fd(),
|
||||
_borrow: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for BorrowedFd<'_> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + AsRawFd> From<&'a T> for BorrowedFd<'a> {
|
||||
#[inline]
|
||||
fn from(fd: &'a T) -> Self {
|
||||
Self::new(fd)
|
||||
}
|
||||
}
|
@ -1,13 +1,10 @@
|
||||
//! Raw file descriptor related structures.
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
|
||||
use nix::fcntl::OFlag;
|
||||
use nix::sys::stat::Mode;
|
||||
use nix::NixPath;
|
||||
use nix::fcntl::{fcntl, FdFlag, F_GETFD, F_SETFD};
|
||||
|
||||
/// Guard a raw file descriptor with a drop handler. This is mostly useful when access to an owned
|
||||
/// `RawFd` is required without the corresponding handler object (such as when only the file
|
||||
@ -92,56 +89,6 @@ impl std::ops::Deref for Fd {
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw file descriptor by number. Thin wrapper to provide `AsRawFd` which a simple `RawFd` does
|
||||
/// not since it's just an `i32`.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct RawFdNum(RawFd);
|
||||
|
||||
impl RawFdNum {
|
||||
/// Borrow this file descriptor as an `&FdRef`.
|
||||
pub fn as_fd_ref(&self) -> &FdRef {
|
||||
unsafe { &*(&self.0 as *const RawFd as *const FdRef) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for RawFdNum {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawFd for RawFdNum {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Self {
|
||||
Self(fd)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for RawFdNum {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<FdRef> for RawFdNum {
|
||||
fn as_ref(&self) -> &FdRef {
|
||||
self.as_fd_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<FdRef> for RawFdNum {
|
||||
fn borrow(&self) -> &FdRef {
|
||||
self.as_fd_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for RawFdNum {
|
||||
type Target = FdRef;
|
||||
|
||||
fn deref(&self) -> &FdRef {
|
||||
self.as_fd_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a raw file descriptor. (Strongly typed `&RawFd` which is not equivalent to an
|
||||
/// `&i32`.
|
||||
///
|
||||
@ -161,45 +108,3 @@ impl AsRawFd for FdRef {
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed file raw descriptor. (A `RawFd` with an attached lifetime).
|
||||
///
|
||||
/// For when using `&FdRef` is not an option.
|
||||
///
|
||||
/// This specifically does not implement `IntoRawFd` or `FromRawFd`, since those would drop life
|
||||
/// times.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct BorrowedFd<'a> {
|
||||
fd: RawFd,
|
||||
_borrow: PhantomData<&'a RawFd>,
|
||||
}
|
||||
|
||||
impl<'a> BorrowedFd<'a> {
|
||||
#[inline]
|
||||
pub fn new<T: ?Sized + AsRawFd>(fd: &T) -> Self {
|
||||
Self {
|
||||
fd: fd.as_raw_fd(),
|
||||
_borrow: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for BorrowedFd<'_> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + AsRawFd> From<&'a T> for BorrowedFd<'a> {
|
||||
#[inline]
|
||||
fn from(fd: &'a T) -> Self {
|
||||
Self::new(fd)
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the `O_CLOEXEC` flag of an existing file descriptor.
|
||||
pub fn fd_change_cloexec(fd: RawFd, on: bool) -> Result<(), anyhow::Error> {
|
||||
let mut flags = unsafe { FdFlag::from_bits_unchecked(fcntl(fd, F_GETFD)?) };
|
||||
flags.set(FdFlag::FD_CLOEXEC, on);
|
||||
fcntl(fd, F_SETFD(flags))?;
|
||||
Ok(())
|
||||
}
|
22
proxmox-sys/src/fd/mod.rs
Normal file
22
proxmox-sys/src/fd/mod.rs
Normal file
@ -0,0 +1,22 @@
|
||||
//! Raw file descriptor related structures.
|
||||
|
||||
mod fd;
|
||||
pub use fd::*;
|
||||
|
||||
mod raw_fd_num;
|
||||
pub use raw_fd_num::*;
|
||||
|
||||
mod borrowed_fd;
|
||||
pub use borrowed_fd::*;
|
||||
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use nix::fcntl::{fcntl, FdFlag, F_GETFD, F_SETFD};
|
||||
|
||||
/// Change the `O_CLOEXEC` flag of an existing file descriptor.
|
||||
pub fn fd_change_cloexec(fd: RawFd, on: bool) -> Result<(), anyhow::Error> {
|
||||
let mut flags = unsafe { FdFlag::from_bits_unchecked(fcntl(fd, F_GETFD)?) };
|
||||
flags.set(FdFlag::FD_CLOEXEC, on);
|
||||
fcntl(fd, F_SETFD(flags))?;
|
||||
Ok(())
|
||||
}
|
55
proxmox-sys/src/fd/raw_fd_num.rs
Normal file
55
proxmox-sys/src/fd/raw_fd_num.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
|
||||
use super::FdRef;
|
||||
|
||||
/// Raw file descriptor by number. Thin wrapper to provide `AsRawFd` which a simple `RawFd` does
|
||||
/// not since it's just an `i32`.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct RawFdNum(RawFd);
|
||||
|
||||
impl RawFdNum {
|
||||
/// Borrow this file descriptor as an `&FdRef`.
|
||||
pub fn as_fd_ref(&self) -> &FdRef {
|
||||
unsafe { &*(&self.0 as *const RawFd as *const FdRef) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for RawFdNum {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawFd for RawFdNum {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Self {
|
||||
Self(fd)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for RawFdNum {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<FdRef> for RawFdNum {
|
||||
fn as_ref(&self) -> &FdRef {
|
||||
self.as_fd_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<FdRef> for RawFdNum {
|
||||
fn borrow(&self) -> &FdRef {
|
||||
self.as_fd_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for RawFdNum {
|
||||
type Target = FdRef;
|
||||
|
||||
fn deref(&self) -> &FdRef {
|
||||
self.as_fd_ref()
|
||||
}
|
||||
}
|
||||
|
168
proxmox-sys/src/fs/dir.rs
Normal file
168
proxmox-sys/src/fs/dir.rs
Normal file
@ -0,0 +1,168 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{bail, Error};
|
||||
use nix::errno::Errno;
|
||||
use nix::fcntl::OFlag;
|
||||
use nix::sys::stat;
|
||||
use nix::unistd;
|
||||
|
||||
use crate::fd::Fd;
|
||||
use crate::fs::{fchown, CreateOptions};
|
||||
|
||||
/// Creates directory at the provided path with specified ownership.
|
||||
///
|
||||
/// Errors if the directory already exists.
|
||||
pub fn create_dir<P: AsRef<Path>>(path: P, options: CreateOptions) -> Result<(), nix::Error> {
|
||||
// clippy bug?: from_bits_truncate is actually a const fn...
|
||||
#[allow(clippy::or_fun_call)]
|
||||
let mode: stat::Mode = options
|
||||
.perm
|
||||
.unwrap_or(stat::Mode::from_bits_truncate(0o770));
|
||||
|
||||
let path = path.as_ref();
|
||||
nix::unistd::mkdir(path, mode)?;
|
||||
unistd::chown(path, options.owner, options.group)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Recursively create a path with separately defined metadata for intermediate directories and the
|
||||
/// final component in the path.
|
||||
///
|
||||
/// Returns `true` if the final directory was created. Otherwise `false` is returned and no changes
|
||||
/// to the directory's metadata have been performed.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use nix::sys::stat::Mode;
|
||||
/// # use nix::unistd::{Gid, Uid};
|
||||
/// # use proxmox::tools::fs::{create_path, CreateOptions};
|
||||
/// # fn code() -> Result<(), anyhow::Error> {
|
||||
/// create_path(
|
||||
/// "/var/lib/mytool/wwwdata",
|
||||
/// None,
|
||||
/// Some(CreateOptions::new()
|
||||
/// .perm(Mode::from_bits(0o777).unwrap())
|
||||
/// .owner(Uid::from_raw(33))
|
||||
/// ),
|
||||
/// )?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn create_path<P: AsRef<Path>>(
|
||||
path: P,
|
||||
intermediate_opts: Option<CreateOptions>,
|
||||
final_opts: Option<CreateOptions>,
|
||||
) -> Result<bool, Error> {
|
||||
create_path_do(path.as_ref(), intermediate_opts, final_opts)
|
||||
}
|
||||
|
||||
fn create_path_do(
|
||||
path: &Path,
|
||||
intermediate_opts: Option<CreateOptions>,
|
||||
final_opts: Option<CreateOptions>,
|
||||
) -> Result<bool, Error> {
|
||||
use std::path::Component;
|
||||
|
||||
let mut iter = path.components().peekable();
|
||||
let at: Fd = match iter.peek() {
|
||||
Some(Component::Prefix(_)) => bail!("illegal prefix path component encountered"),
|
||||
Some(Component::RootDir) => {
|
||||
let _ = iter.next();
|
||||
Fd::open(
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(b"/\0") },
|
||||
OFlag::O_DIRECTORY,
|
||||
stat::Mode::empty(),
|
||||
)?
|
||||
}
|
||||
Some(Component::CurDir) => {
|
||||
let _ = iter.next();
|
||||
Fd::cwd()
|
||||
}
|
||||
Some(Component::ParentDir) => {
|
||||
let _ = iter.next();
|
||||
Fd::open(
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(b"..\0") },
|
||||
OFlag::O_DIRECTORY,
|
||||
stat::Mode::empty(),
|
||||
)?
|
||||
}
|
||||
Some(Component::Normal(_)) => {
|
||||
// simply do not advance the iterator, heavy lifting happens in create_path_at_do()
|
||||
Fd::cwd()
|
||||
}
|
||||
None => bail!("create_path on empty path?"),
|
||||
};
|
||||
|
||||
create_path_at_do(at, iter, intermediate_opts, final_opts)
|
||||
}
|
||||
|
||||
fn create_path_at_do(
|
||||
mut at: Fd,
|
||||
mut iter: std::iter::Peekable<std::path::Components>,
|
||||
intermediate_opts: Option<CreateOptions>,
|
||||
final_opts: Option<CreateOptions>,
|
||||
) -> Result<bool, Error> {
|
||||
let mut created = false;
|
||||
loop {
|
||||
use std::path::Component;
|
||||
|
||||
match iter.next() {
|
||||
None => return Ok(created),
|
||||
|
||||
Some(Component::ParentDir) => {
|
||||
at = Fd::openat(
|
||||
&at,
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(b"..\0") },
|
||||
OFlag::O_DIRECTORY,
|
||||
stat::Mode::empty(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Some(Component::Normal(path)) => {
|
||||
let opts = if iter.peek().is_some() {
|
||||
intermediate_opts.as_ref()
|
||||
} else {
|
||||
final_opts.as_ref()
|
||||
};
|
||||
|
||||
// clippy bug?: from_bits_truncate is actually a const fn...
|
||||
#[allow(clippy::or_fun_call)]
|
||||
let mode = opts
|
||||
.and_then(|o| o.perm)
|
||||
.unwrap_or(stat::Mode::from_bits_truncate(0o755));
|
||||
|
||||
created = match stat::mkdirat(at.as_raw_fd(), path, mode) {
|
||||
Err(nix::Error::Sys(Errno::EEXIST)) => false,
|
||||
Err(e) => return Err(e.into()),
|
||||
Ok(_) => true,
|
||||
};
|
||||
at = Fd::openat(&at, path, OFlag::O_DIRECTORY, stat::Mode::empty())?;
|
||||
|
||||
if let (true, Some(opts)) = (created, opts) {
|
||||
if opts.owner.is_some() || opts.group.is_some() {
|
||||
fchown(at.as_raw_fd(), opts.owner, opts.group)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impossible according to the docs:
|
||||
Some(_) => bail!("encountered unexpected special path component"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_path() {
|
||||
create_path(
|
||||
"testdir/testsub/testsub2/testfinal",
|
||||
Some(CreateOptions::new().perm(stat::Mode::from_bits_truncate(0o755))),
|
||||
Some(
|
||||
CreateOptions::new()
|
||||
.owner(Uid::effective())
|
||||
.group(Gid::effective()),
|
||||
),
|
||||
)
|
||||
.expect("expected create_path to work");
|
||||
}
|
@ -1,9 +1,6 @@
|
||||
//! File related utilities such as `replace_file`.
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead, BufReader, Write};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
@ -11,15 +8,16 @@ use anyhow::{bail, format_err, Error};
|
||||
use nix::errno::Errno;
|
||||
use nix::fcntl::OFlag;
|
||||
use nix::sys::stat;
|
||||
use nix::unistd::{self, Gid, Uid};
|
||||
use nix::unistd;
|
||||
use nix::NixPath;
|
||||
use serde_json::Value;
|
||||
|
||||
use proxmox_lang::try_block;
|
||||
|
||||
use crate::sys::error::{SysError, SysResult};
|
||||
use crate::sys::timer;
|
||||
use crate::tools::fd::Fd;
|
||||
use crate::error::{SysError, SysResult};
|
||||
use crate::linux::timer;
|
||||
|
||||
use crate::fs::CreateOptions;
|
||||
|
||||
/// Read the entire contents of a file into a bytes vector
|
||||
///
|
||||
@ -318,248 +316,6 @@ pub fn atomic_open_or_create_file<P: AsRef<Path>>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Change ownership of an open file handle
|
||||
pub fn fchown(fd: RawFd, owner: Option<Uid>, group: Option<Gid>) -> Result<(), Error> {
|
||||
// According to the POSIX specification, -1 is used to indicate that owner and group
|
||||
// are not to be changed. Since uid_t and gid_t are unsigned types, we have to wrap
|
||||
// around to get -1 (copied fron nix crate).
|
||||
let uid = owner.map(Into::into).unwrap_or(!(0 as libc::uid_t));
|
||||
let gid = group.map(Into::into).unwrap_or(!(0 as libc::gid_t));
|
||||
|
||||
let res = unsafe { libc::fchown(fd, uid, gid) };
|
||||
Errno::result(res)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// FIXME: Consider using derive-builder!
|
||||
#[derive(Clone, Default)]
|
||||
pub struct CreateOptions {
|
||||
perm: Option<stat::Mode>,
|
||||
owner: Option<Uid>,
|
||||
group: Option<Gid>,
|
||||
}
|
||||
|
||||
impl CreateOptions {
|
||||
// contrary to Default::default() this is const
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
perm: None,
|
||||
owner: None,
|
||||
group: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn perm(mut self, perm: stat::Mode) -> Self {
|
||||
self.perm = Some(perm);
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn owner(mut self, owner: Uid) -> Self {
|
||||
self.owner = Some(owner);
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn group(mut self, group: Gid) -> Self {
|
||||
self.group = Some(group);
|
||||
self
|
||||
}
|
||||
|
||||
/// Convenience shortcut around having to import `Uid` from nix.
|
||||
pub const fn owner_root(self) -> Self {
|
||||
self.owner(nix::unistd::ROOT)
|
||||
}
|
||||
|
||||
pub fn apply_to(&self, file: &mut File, path: &Path) -> Result<(), Error> {
|
||||
|
||||
// clippy bug?: from_bits_truncate is actually a const fn...
|
||||
#[allow(clippy::or_fun_call)]
|
||||
let mode: stat::Mode = self.perm
|
||||
.unwrap_or(stat::Mode::from_bits_truncate(0o644));
|
||||
|
||||
if let Err(err) = stat::fchmod(file.as_raw_fd(), mode) {
|
||||
bail!("fchmod {:?} failed: {}", path, err);
|
||||
}
|
||||
|
||||
if self.owner.is_some() || self.group.is_some() {
|
||||
if let Err(err) = fchown(file.as_raw_fd(), self.owner, self.group) {
|
||||
bail!("fchown {:?} failed: {}", path, err);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: once 'nix' has `const fn` constructors for Uid and Gid we can enable these:
|
||||
|
||||
/*
|
||||
/// Convenience shortcut around having to import `Gid` from nix.
|
||||
pub const fn group_root(self) -> Self {
|
||||
// nix hasn't constified these yet, but it's just an alias to gid_t:
|
||||
self.group(Gid::from_raw(0))
|
||||
}
|
||||
|
||||
/// Convenience shortcut to set both owner and group to 0.
|
||||
pub const fn root_only(self) -> Self {
|
||||
self.owner_root().group_root()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/// Creates directory at the provided path with specified ownership.
|
||||
///
|
||||
/// Errors if the directory already exists.
|
||||
pub fn create_dir<P: AsRef<Path>>(path: P, options: CreateOptions) -> Result<(), nix::Error> {
|
||||
// clippy bug?: from_bits_truncate is actually a const fn...
|
||||
#[allow(clippy::or_fun_call)]
|
||||
let mode: stat::Mode = options
|
||||
.perm
|
||||
.unwrap_or(stat::Mode::from_bits_truncate(0o770));
|
||||
|
||||
let path = path.as_ref();
|
||||
nix::unistd::mkdir(path, mode)?;
|
||||
unistd::chown(path, options.owner, options.group)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Recursively create a path with separately defined metadata for intermediate directories and the
|
||||
/// final component in the path.
|
||||
///
|
||||
/// Returns `true` if the final directory was created. Otherwise `false` is returned and no changes
|
||||
/// to the directory's metadata have been performed.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use nix::sys::stat::Mode;
|
||||
/// # use nix::unistd::{Gid, Uid};
|
||||
/// # use proxmox::tools::fs::{create_path, CreateOptions};
|
||||
/// # fn code() -> Result<(), anyhow::Error> {
|
||||
/// create_path(
|
||||
/// "/var/lib/mytool/wwwdata",
|
||||
/// None,
|
||||
/// Some(CreateOptions::new()
|
||||
/// .perm(Mode::from_bits(0o777).unwrap())
|
||||
/// .owner(Uid::from_raw(33))
|
||||
/// ),
|
||||
/// )?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn create_path<P: AsRef<Path>>(
|
||||
path: P,
|
||||
intermediate_opts: Option<CreateOptions>,
|
||||
final_opts: Option<CreateOptions>,
|
||||
) -> Result<bool, Error> {
|
||||
create_path_do(path.as_ref(), intermediate_opts, final_opts)
|
||||
}
|
||||
|
||||
fn create_path_do(
|
||||
path: &Path,
|
||||
intermediate_opts: Option<CreateOptions>,
|
||||
final_opts: Option<CreateOptions>,
|
||||
) -> Result<bool, Error> {
|
||||
use std::path::Component;
|
||||
|
||||
let mut iter = path.components().peekable();
|
||||
let at: Fd = match iter.peek() {
|
||||
Some(Component::Prefix(_)) => bail!("illegal prefix path component encountered"),
|
||||
Some(Component::RootDir) => {
|
||||
let _ = iter.next();
|
||||
Fd::open(
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(b"/\0") },
|
||||
OFlag::O_DIRECTORY,
|
||||
stat::Mode::empty(),
|
||||
)?
|
||||
}
|
||||
Some(Component::CurDir) => {
|
||||
let _ = iter.next();
|
||||
Fd::cwd()
|
||||
}
|
||||
Some(Component::ParentDir) => {
|
||||
let _ = iter.next();
|
||||
Fd::open(
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(b"..\0") },
|
||||
OFlag::O_DIRECTORY,
|
||||
stat::Mode::empty(),
|
||||
)?
|
||||
}
|
||||
Some(Component::Normal(_)) => {
|
||||
// simply do not advance the iterator, heavy lifting happens in create_path_at_do()
|
||||
Fd::cwd()
|
||||
}
|
||||
None => bail!("create_path on empty path?"),
|
||||
};
|
||||
|
||||
create_path_at_do(at, iter, intermediate_opts, final_opts)
|
||||
}
|
||||
|
||||
fn create_path_at_do(
|
||||
mut at: Fd,
|
||||
mut iter: std::iter::Peekable<std::path::Components>,
|
||||
intermediate_opts: Option<CreateOptions>,
|
||||
final_opts: Option<CreateOptions>,
|
||||
) -> Result<bool, Error> {
|
||||
let mut created = false;
|
||||
loop {
|
||||
use std::path::Component;
|
||||
|
||||
match iter.next() {
|
||||
None => return Ok(created),
|
||||
|
||||
Some(Component::ParentDir) => {
|
||||
at = Fd::openat(
|
||||
&at,
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(b"..\0") },
|
||||
OFlag::O_DIRECTORY,
|
||||
stat::Mode::empty(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Some(Component::Normal(path)) => {
|
||||
let opts = if iter.peek().is_some() {
|
||||
intermediate_opts.as_ref()
|
||||
} else {
|
||||
final_opts.as_ref()
|
||||
};
|
||||
|
||||
// clippy bug?: from_bits_truncate is actually a const fn...
|
||||
#[allow(clippy::or_fun_call)]
|
||||
let mode = opts
|
||||
.and_then(|o| o.perm)
|
||||
.unwrap_or(stat::Mode::from_bits_truncate(0o755));
|
||||
|
||||
created = match stat::mkdirat(at.as_raw_fd(), path, mode) {
|
||||
Err(nix::Error::Sys(Errno::EEXIST)) => false,
|
||||
Err(e) => return Err(e.into()),
|
||||
Ok(_) => true,
|
||||
};
|
||||
at = Fd::openat(&at, path, OFlag::O_DIRECTORY, stat::Mode::empty())?;
|
||||
|
||||
if let (true, Some(opts)) = (created, opts) {
|
||||
if opts.owner.is_some() || opts.group.is_some() {
|
||||
fchown(at.as_raw_fd(), opts.owner, opts.group)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impossible according to the docs:
|
||||
Some(_) => bail!("encountered unexpected special path component"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_path() {
|
||||
create_path(
|
||||
"testdir/testsub/testsub2/testfinal",
|
||||
Some(CreateOptions::new().perm(stat::Mode::from_bits_truncate(0o755))),
|
||||
Some(
|
||||
CreateOptions::new()
|
||||
.owner(Uid::effective())
|
||||
.group(Gid::effective()),
|
||||
),
|
||||
)
|
||||
.expect("expected create_path to work");
|
||||
}
|
||||
|
||||
// /usr/include/linux/fs.h: #define BLKGETSIZE64 _IOR(0x12,114,size_t)
|
||||
// return device size in bytes (u64 *arg)
|
104
proxmox-sys/src/fs/mod.rs
Normal file
104
proxmox-sys/src/fs/mod.rs
Normal file
@ -0,0 +1,104 @@
|
||||
//! File system related utilities
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{bail, Error};
|
||||
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use nix::unistd::{Gid, Uid};
|
||||
use nix::sys::stat;
|
||||
use nix::errno::Errno;
|
||||
|
||||
mod file;
|
||||
pub use file::*;
|
||||
|
||||
mod dir;
|
||||
pub use dir::*;
|
||||
|
||||
/// Change ownership of an open file handle
|
||||
pub fn fchown(fd: RawFd, owner: Option<Uid>, group: Option<Gid>) -> Result<(), Error> {
|
||||
// According to the POSIX specification, -1 is used to indicate that owner and group
|
||||
// are not to be changed. Since uid_t and gid_t are unsigned types, we have to wrap
|
||||
// around to get -1 (copied fron nix crate).
|
||||
let uid = owner.map(Into::into).unwrap_or(!(0 as libc::uid_t));
|
||||
let gid = group.map(Into::into).unwrap_or(!(0 as libc::gid_t));
|
||||
|
||||
let res = unsafe { libc::fchown(fd, uid, gid) };
|
||||
Errno::result(res)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// FIXME: Consider using derive-builder!
|
||||
#[derive(Clone, Default)]
|
||||
pub struct CreateOptions {
|
||||
perm: Option<stat::Mode>,
|
||||
owner: Option<Uid>,
|
||||
group: Option<Gid>,
|
||||
}
|
||||
|
||||
impl CreateOptions {
|
||||
// contrary to Default::default() this is const
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
perm: None,
|
||||
owner: None,
|
||||
group: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn perm(mut self, perm: stat::Mode) -> Self {
|
||||
self.perm = Some(perm);
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn owner(mut self, owner: Uid) -> Self {
|
||||
self.owner = Some(owner);
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn group(mut self, group: Gid) -> Self {
|
||||
self.group = Some(group);
|
||||
self
|
||||
}
|
||||
|
||||
/// Convenience shortcut around having to import `Uid` from nix.
|
||||
pub const fn owner_root(self) -> Self {
|
||||
self.owner(nix::unistd::ROOT)
|
||||
}
|
||||
|
||||
pub fn apply_to(&self, file: &mut File, path: &Path) -> Result<(), Error> {
|
||||
|
||||
// clippy bug?: from_bits_truncate is actually a const fn...
|
||||
#[allow(clippy::or_fun_call)]
|
||||
let mode: stat::Mode = self.perm
|
||||
.unwrap_or(stat::Mode::from_bits_truncate(0o644));
|
||||
|
||||
if let Err(err) = stat::fchmod(file.as_raw_fd(), mode) {
|
||||
bail!("fchmod {:?} failed: {}", path, err);
|
||||
}
|
||||
|
||||
if self.owner.is_some() || self.group.is_some() {
|
||||
if let Err(err) = fchown(file.as_raw_fd(), self.owner, self.group) {
|
||||
bail!("fchown {:?} failed: {}", path, err);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: once 'nix' has `const fn` constructors for Uid and Gid we can enable these:
|
||||
|
||||
/*
|
||||
/// Convenience shortcut around having to import `Gid` from nix.
|
||||
pub const fn group_root(self) -> Self {
|
||||
// nix hasn't constified these yet, but it's just an alias to gid_t:
|
||||
self.group(Gid::from_raw(0))
|
||||
}
|
||||
|
||||
/// Convenience shortcut to set both owner and group to 0.
|
||||
pub const fn root_only(self) -> Self {
|
||||
self.owner_root().group_root()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
pub mod crypt;
|
||||
pub mod error;
|
||||
pub mod fd;
|
||||
pub mod fs;
|
||||
pub mod linux;
|
||||
pub mod logrotate;
|
||||
pub mod macros;
|
||||
pub mod mmap;
|
||||
pub mod process_locker;
|
||||
pub mod worker_task_context;
|
||||
|
@ -9,6 +9,7 @@ pub mod pid;
|
||||
pub mod procfs;
|
||||
pub mod pty;
|
||||
pub mod socket;
|
||||
pub mod timer;
|
||||
pub mod tty;
|
||||
|
||||
/// Get pseudo random data (/dev/urandom)
|
@ -13,9 +13,9 @@ use nix::NixPath;
|
||||
|
||||
use proxmox_lang::c_str;
|
||||
|
||||
use crate::sys::error::{io_err_other, SysResult};
|
||||
use crate::sys::linux::procfs::{MountInfo, PidStat};
|
||||
use crate::tools::fd::Fd;
|
||||
use crate::error::{io_err_other, SysResult};
|
||||
use crate::linux::procfs::{MountInfo, PidStat};
|
||||
use crate::fd::Fd;
|
||||
use crate::{c_result, c_try};
|
||||
|
||||
/// asm-generic pidfd_open syscall number
|
@ -12,8 +12,7 @@ use anyhow::*;
|
||||
use lazy_static::lazy_static;
|
||||
use nix::unistd::Pid;
|
||||
|
||||
use crate::tools::fs::file_read_firstline;
|
||||
use crate::tools::parse::hex_nibble;
|
||||
use crate::fs::file_read_firstline;
|
||||
|
||||
pub mod mountinfo;
|
||||
#[doc(inline)]
|
||||
@ -570,6 +569,17 @@ pub fn read_proc_net_dev() -> Result<Vec<ProcFsNetDev>, Error> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// Parse a hexadecimal digit into a byte.
|
||||
#[inline]
|
||||
fn hex_nibble(c: u8) -> Result<u8, Error> {
|
||||
Ok(match c {
|
||||
b'0'..=b'9' => c - b'0',
|
||||
b'a'..=b'f' => c - b'a' + 0xa,
|
||||
b'A'..=b'F' => c - b'A' + 0xa,
|
||||
_ => bail!("not a hex digit: {}", c as char),
|
||||
})
|
||||
}
|
||||
|
||||
fn hexstr_to_ipv4addr<T: AsRef<[u8]>>(hex: T) -> Result<Ipv4Addr, Error> {
|
||||
let hex = hex.as_ref();
|
||||
if hex.len() != 8 {
|
@ -11,7 +11,7 @@ use nix::sys::stat::Mode;
|
||||
use nix::unistd::{dup2, setsid};
|
||||
use nix::{ioctl_write_int_bad, ioctl_write_ptr_bad, Result};
|
||||
|
||||
use crate::tools::fd::Fd;
|
||||
use crate::fd::Fd;
|
||||
|
||||
ioctl_write_int_bad!(set_controlling_tty, libc::TIOCSCTTY);
|
||||
ioctl_write_ptr_bad!(set_size, libc::TIOCSWINSZ, nix::pty::Winsize);
|
@ -9,8 +9,8 @@ use nix::sys::stat::Mode;
|
||||
use proxmox_lang::try_block;
|
||||
|
||||
use crate::c_try;
|
||||
use crate::sys::error::SysError;
|
||||
use crate::tools::fd::Fd;
|
||||
use crate::error::SysError;
|
||||
use crate::fd::Fd;
|
||||
|
||||
/// Get the current size of the terminal (for stdout).
|
||||
/// # Safety
|
@ -6,7 +6,7 @@ use std::io::Read;
|
||||
use anyhow::{bail, Error};
|
||||
use nix::unistd;
|
||||
|
||||
use proxmox::tools::fs::{CreateOptions, make_tmp_file};
|
||||
use crate::fs::{CreateOptions, make_tmp_file};
|
||||
|
||||
/// Used for rotating log files and iterating over them
|
||||
pub struct LogRotate {
|
||||
|
@ -7,7 +7,7 @@ use std::{io, mem, ptr};
|
||||
|
||||
use nix::sys::mman;
|
||||
|
||||
use crate::sys::error::{io_err_other, SysError};
|
||||
use crate::error::{io_err_other, SysError};
|
||||
|
||||
pub struct Mmap<T> {
|
||||
data: *mut T,
|
@ -4,7 +4,6 @@
|
||||
#[macro_use]
|
||||
pub mod serde_macros;
|
||||
|
||||
pub mod sys;
|
||||
pub mod tools;
|
||||
|
||||
/// An identity (nop) macro. Used by the `#[sortable]` proc macro.
|
||||
|
@ -1,7 +0,0 @@
|
||||
//! This is a system utility crate used by all our rust projects.
|
||||
|
||||
pub mod macros;
|
||||
|
||||
pub mod error;
|
||||
pub mod linux;
|
||||
pub mod timer;
|
@ -9,10 +9,6 @@ use proxmox_io::vec;
|
||||
|
||||
pub mod common_regex;
|
||||
pub mod email;
|
||||
pub mod fd;
|
||||
pub mod fs;
|
||||
pub mod mmap;
|
||||
pub mod parse;
|
||||
pub mod serde;
|
||||
pub mod systemd;
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
//! Some parsing utilities.
|
||||
|
||||
use anyhow::{bail, Error};
|
||||
|
||||
/// Parse a hexadecimal digit into a byte.
|
||||
#[inline]
|
||||
pub fn hex_nibble(c: u8) -> Result<u8, Error> {
|
||||
Ok(match c {
|
||||
b'0'..=b'9' => c - b'0',
|
||||
b'a'..=b'f' => c - b'a' + 0xa,
|
||||
b'A'..=b'F' => c - b'A' + 0xa,
|
||||
_ => bail!("not a hex digit: {}", c as char),
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user