2020-03-23 14:03:18 +00:00
|
|
|
use std::ffi::OsString;
|
2019-08-01 14:23:46 +00:00
|
|
|
use std::os::unix::io::{AsRawFd, RawFd};
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
use anyhow::{bail, format_err, Error};
|
|
|
|
use nix::dir::Dir;
|
2019-08-22 13:00:02 +00:00
|
|
|
use nix::fcntl::OFlag;
|
2020-03-23 14:03:18 +00:00
|
|
|
use nix::sys::stat::{mkdirat, Mode};
|
2019-08-01 14:23:46 +00:00
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
use proxmox::sys::error::SysError;
|
|
|
|
use pxar::Metadata;
|
2019-08-22 13:00:02 +00:00
|
|
|
|
2020-06-08 13:02:52 +00:00
|
|
|
use crate::pxar::tools::{assert_single_path_component, perms_from_metadata};
|
2019-08-01 14:23:46 +00:00
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
pub struct PxarDir {
|
|
|
|
file_name: OsString,
|
|
|
|
metadata: Metadata,
|
|
|
|
dir: Option<Dir>,
|
2019-08-01 14:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PxarDir {
|
2020-03-23 14:03:18 +00:00
|
|
|
pub fn new(file_name: OsString, metadata: Metadata) -> Self {
|
2019-08-01 14:23:46 +00:00
|
|
|
Self {
|
2020-03-23 14:03:18 +00:00
|
|
|
file_name,
|
|
|
|
metadata,
|
2019-08-01 14:23:46 +00:00
|
|
|
dir: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
pub fn with_dir(dir: Dir, metadata: Metadata) -> Self {
|
|
|
|
Self {
|
|
|
|
file_name: OsString::from("."),
|
|
|
|
metadata,
|
|
|
|
dir: Some(dir),
|
|
|
|
}
|
|
|
|
}
|
2019-08-01 14:23:46 +00:00
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
fn create_dir(&mut self, parent: RawFd, allow_existing_dirs: bool) -> Result<RawFd, Error> {
|
|
|
|
match mkdirat(
|
|
|
|
parent,
|
|
|
|
self.file_name.as_os_str(),
|
|
|
|
perms_from_metadata(&self.metadata)?,
|
|
|
|
) {
|
|
|
|
Ok(()) => (),
|
2019-08-01 14:23:46 +00:00
|
|
|
Err(err) => {
|
2020-03-23 14:03:18 +00:00
|
|
|
if !(allow_existing_dirs && err.already_exists()) {
|
|
|
|
return Err(err.into());
|
2019-08-01 14:23:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
self.open_dir(parent)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn open_dir(&mut self, parent: RawFd) -> Result<RawFd, Error> {
|
|
|
|
let dir = Dir::openat(
|
2019-08-22 13:00:02 +00:00
|
|
|
parent,
|
2020-03-23 14:03:18 +00:00
|
|
|
self.file_name.as_os_str(),
|
2019-08-22 13:00:02 +00:00
|
|
|
OFlag::O_DIRECTORY,
|
|
|
|
Mode::empty(),
|
|
|
|
)?;
|
2019-08-01 14:23:46 +00:00
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
let fd = dir.as_raw_fd();
|
|
|
|
self.dir = Some(dir);
|
|
|
|
|
|
|
|
Ok(fd)
|
2019-08-01 14:23:46 +00:00
|
|
|
}
|
2020-03-23 14:03:18 +00:00
|
|
|
|
|
|
|
pub fn try_as_raw_fd(&self) -> Option<RawFd> {
|
|
|
|
self.dir.as_ref().map(AsRawFd::as_raw_fd)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn metadata(&self) -> &Metadata {
|
|
|
|
&self.metadata
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PxarDirStack {
|
|
|
|
dirs: Vec<PxarDir>,
|
|
|
|
path: PathBuf,
|
|
|
|
created: usize,
|
2019-08-01 14:23:46 +00:00
|
|
|
}
|
|
|
|
|
2019-08-02 13:19:36 +00:00
|
|
|
impl PxarDirStack {
|
2020-03-23 14:03:18 +00:00
|
|
|
pub fn new(root: Dir, metadata: Metadata) -> Self {
|
2019-08-01 14:23:46 +00:00
|
|
|
Self {
|
2020-03-23 14:03:18 +00:00
|
|
|
dirs: vec![PxarDir::with_dir(root, metadata)],
|
|
|
|
path: PathBuf::from("/"),
|
|
|
|
created: 1, // the root directory exists
|
2019-08-01 14:23:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.dirs.is_empty()
|
2019-08-01 14:23:46 +00:00
|
|
|
}
|
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
pub fn push(&mut self, file_name: OsString, metadata: Metadata) -> Result<(), Error> {
|
2020-06-08 13:02:52 +00:00
|
|
|
assert_single_path_component(&file_name)?;
|
2020-03-23 14:03:18 +00:00
|
|
|
self.path.push(&file_name);
|
|
|
|
self.dirs.push(PxarDir::new(file_name, metadata));
|
|
|
|
Ok(())
|
2019-08-01 14:23:46 +00:00
|
|
|
}
|
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
pub fn pop(&mut self) -> Result<Option<PxarDir>, Error> {
|
|
|
|
let out = self.dirs.pop();
|
|
|
|
if !self.path.pop() {
|
|
|
|
if self.path.as_os_str() == "/" {
|
|
|
|
// we just finished the root directory, make sure this can only happen once:
|
|
|
|
self.path = PathBuf::new();
|
|
|
|
} else {
|
|
|
|
bail!("lost track of path");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.created = self.created.min(self.dirs.len());
|
|
|
|
Ok(out)
|
2019-08-01 14:23:46 +00:00
|
|
|
}
|
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
pub fn last_dir_fd(&mut self, allow_existing_dirs: bool) -> Result<RawFd, Error> {
|
|
|
|
// should not be possible given the way we use it:
|
|
|
|
assert!(!self.dirs.is_empty(), "PxarDirStack underrun");
|
2019-08-01 14:23:46 +00:00
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
let mut fd = self.dirs[self.created - 1]
|
|
|
|
.try_as_raw_fd()
|
|
|
|
.ok_or_else(|| format_err!("lost track of directory file descriptors"))?;
|
|
|
|
while self.created < self.dirs.len() {
|
|
|
|
fd = self.dirs[self.created].create_dir(fd, allow_existing_dirs)?;
|
|
|
|
self.created += 1;
|
2019-08-01 14:23:46 +00:00
|
|
|
}
|
2020-03-23 14:03:18 +00:00
|
|
|
|
|
|
|
Ok(fd)
|
2019-08-01 14:23:46 +00:00
|
|
|
}
|
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
pub fn root_dir_fd(&self) -> Result<RawFd, Error> {
|
|
|
|
// should not be possible given the way we use it:
|
|
|
|
assert!(!self.dirs.is_empty(), "PxarDirStack underrun");
|
2019-08-01 14:23:46 +00:00
|
|
|
|
2020-03-23 14:03:18 +00:00
|
|
|
self.dirs[0]
|
|
|
|
.try_as_raw_fd()
|
|
|
|
.ok_or_else(|| format_err!("lost track of directory file descriptors"))
|
2019-08-01 14:23:46 +00:00
|
|
|
}
|
|
|
|
}
|