forked from proxmox-mirrors/proxmox
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]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
|
lazy_static = "1.4"
|
||||||
libc = "0.2.107"
|
libc = "0.2.107"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nix = "0.19.1"
|
nix = "0.19.1"
|
||||||
|
serde_json = "1.0"
|
||||||
zstd = { version = "0.6", features = [ "bindgen" ] }
|
zstd = { version = "0.6", features = [ "bindgen" ] }
|
||||||
|
|
||||||
proxmox = { path = "../proxmox", version = "0.15", default-features = false }
|
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+default-dev (= ${binary:Version}),
|
||||||
librust-proxmox-sys-0.1-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+default-dev (= ${binary:Version}),
|
||||||
librust-proxmox-sys-0.1.1-dev (= ${binary:Version}),
|
librust-proxmox-sys-0.1.2-dev (= ${binary:Version}),
|
||||||
librust-proxmox-sys-0.1.1+default-dev (= ${binary:Version})
|
librust-proxmox-sys-0.1.2+default-dev (= ${binary:Version})
|
||||||
Description: System tools (using nix) - Rust source code
|
Description: System tools (using nix) - Rust source code
|
||||||
This package contains the source for the Rust proxmox-sys crate, packaged by
|
This package contains the source for the Rust proxmox-sys crate, packaged by
|
||||||
debcargo for use with cargo and dh-cargo.
|
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> {
|
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));
|
let salt = format!("$5${}$", base64::encode_config(&salt, base64::CRYPT));
|
||||||
|
|
||||||
crypt(password.as_bytes(), salt.as_bytes())
|
crypt(password.as_bytes(), salt.as_bytes())
|
||||||
|
@ -150,7 +150,7 @@ macro_rules! other_error {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_io_result(self) -> io::Result<T> {
|
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::borrow::Borrow;
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||||
|
|
||||||
use nix::fcntl::OFlag;
|
use nix::fcntl::OFlag;
|
||||||
use nix::sys::stat::Mode;
|
use nix::sys::stat::Mode;
|
||||||
use nix::NixPath;
|
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
|
/// 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
|
/// `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
|
/// A reference to a raw file descriptor. (Strongly typed `&RawFd` which is not equivalent to an
|
||||||
/// `&i32`.
|
/// `&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::fs::File;
|
||||||
use std::io::{self, BufRead, BufReader, Write};
|
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::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -11,15 +8,16 @@ 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;
|
||||||
use nix::unistd::{self, Gid, Uid};
|
use nix::unistd;
|
||||||
use nix::NixPath;
|
use nix::NixPath;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use proxmox_lang::try_block;
|
use proxmox_lang::try_block;
|
||||||
|
|
||||||
use crate::sys::error::{SysError, SysResult};
|
use crate::error::{SysError, SysResult};
|
||||||
use crate::sys::timer;
|
use crate::linux::timer;
|
||||||
use crate::tools::fd::Fd;
|
|
||||||
|
use crate::fs::CreateOptions;
|
||||||
|
|
||||||
/// Read the entire contents of a file into a bytes vector
|
/// 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)
|
// /usr/include/linux/fs.h: #define BLKGETSIZE64 _IOR(0x12,114,size_t)
|
||||||
// return device size in bytes (u64 *arg)
|
// 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 crypt;
|
||||||
|
pub mod error;
|
||||||
|
pub mod fd;
|
||||||
|
pub mod fs;
|
||||||
|
pub mod linux;
|
||||||
pub mod logrotate;
|
pub mod logrotate;
|
||||||
|
pub mod macros;
|
||||||
|
pub mod mmap;
|
||||||
pub mod process_locker;
|
pub mod process_locker;
|
||||||
pub mod worker_task_context;
|
pub mod worker_task_context;
|
||||||
|
@ -9,6 +9,7 @@ pub mod pid;
|
|||||||
pub mod procfs;
|
pub mod procfs;
|
||||||
pub mod pty;
|
pub mod pty;
|
||||||
pub mod socket;
|
pub mod socket;
|
||||||
|
pub mod timer;
|
||||||
pub mod tty;
|
pub mod tty;
|
||||||
|
|
||||||
/// Get pseudo random data (/dev/urandom)
|
/// Get pseudo random data (/dev/urandom)
|
@ -13,9 +13,9 @@ use nix::NixPath;
|
|||||||
|
|
||||||
use proxmox_lang::c_str;
|
use proxmox_lang::c_str;
|
||||||
|
|
||||||
use crate::sys::error::{io_err_other, SysResult};
|
use crate::error::{io_err_other, SysResult};
|
||||||
use crate::sys::linux::procfs::{MountInfo, PidStat};
|
use crate::linux::procfs::{MountInfo, PidStat};
|
||||||
use crate::tools::fd::Fd;
|
use crate::fd::Fd;
|
||||||
use crate::{c_result, c_try};
|
use crate::{c_result, c_try};
|
||||||
|
|
||||||
/// asm-generic pidfd_open syscall number
|
/// asm-generic pidfd_open syscall number
|
@ -12,8 +12,7 @@ use anyhow::*;
|
|||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use nix::unistd::Pid;
|
use nix::unistd::Pid;
|
||||||
|
|
||||||
use crate::tools::fs::file_read_firstline;
|
use crate::fs::file_read_firstline;
|
||||||
use crate::tools::parse::hex_nibble;
|
|
||||||
|
|
||||||
pub mod mountinfo;
|
pub mod mountinfo;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
@ -570,6 +569,17 @@ pub fn read_proc_net_dev() -> Result<Vec<ProcFsNetDev>, Error> {
|
|||||||
Ok(result)
|
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> {
|
fn hexstr_to_ipv4addr<T: AsRef<[u8]>>(hex: T) -> Result<Ipv4Addr, Error> {
|
||||||
let hex = hex.as_ref();
|
let hex = hex.as_ref();
|
||||||
if hex.len() != 8 {
|
if hex.len() != 8 {
|
@ -11,7 +11,7 @@ use nix::sys::stat::Mode;
|
|||||||
use nix::unistd::{dup2, setsid};
|
use nix::unistd::{dup2, setsid};
|
||||||
use nix::{ioctl_write_int_bad, ioctl_write_ptr_bad, Result};
|
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_int_bad!(set_controlling_tty, libc::TIOCSCTTY);
|
||||||
ioctl_write_ptr_bad!(set_size, libc::TIOCSWINSZ, nix::pty::Winsize);
|
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 proxmox_lang::try_block;
|
||||||
|
|
||||||
use crate::c_try;
|
use crate::c_try;
|
||||||
use crate::sys::error::SysError;
|
use crate::error::SysError;
|
||||||
use crate::tools::fd::Fd;
|
use crate::fd::Fd;
|
||||||
|
|
||||||
/// Get the current size of the terminal (for stdout).
|
/// Get the current size of the terminal (for stdout).
|
||||||
/// # Safety
|
/// # Safety
|
@ -6,7 +6,7 @@ use std::io::Read;
|
|||||||
use anyhow::{bail, Error};
|
use anyhow::{bail, Error};
|
||||||
use nix::unistd;
|
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
|
/// Used for rotating log files and iterating over them
|
||||||
pub struct LogRotate {
|
pub struct LogRotate {
|
||||||
|
@ -7,7 +7,7 @@ use std::{io, mem, ptr};
|
|||||||
|
|
||||||
use nix::sys::mman;
|
use nix::sys::mman;
|
||||||
|
|
||||||
use crate::sys::error::{io_err_other, SysError};
|
use crate::error::{io_err_other, SysError};
|
||||||
|
|
||||||
pub struct Mmap<T> {
|
pub struct Mmap<T> {
|
||||||
data: *mut T,
|
data: *mut T,
|
@ -4,7 +4,6 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod serde_macros;
|
pub mod serde_macros;
|
||||||
|
|
||||||
pub mod sys;
|
|
||||||
pub mod tools;
|
pub mod tools;
|
||||||
|
|
||||||
/// An identity (nop) macro. Used by the `#[sortable]` proc macro.
|
/// 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 common_regex;
|
||||||
pub mod email;
|
pub mod email;
|
||||||
pub mod fd;
|
|
||||||
pub mod fs;
|
|
||||||
pub mod mmap;
|
|
||||||
pub mod parse;
|
|
||||||
pub mod serde;
|
pub mod serde;
|
||||||
pub mod systemd;
|
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