]> git.proxmox.com Git - proxmox-backup.git/blame - pbs-client/src/pxar/dir_stack.rs
api: use if-let pattern for error-only handling
[proxmox-backup.git] / pbs-client / src / pxar / dir_stack.rs
CommitLineData
79e58a90 1use std::ffi::OsString;
a63a869e 2use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
79e58a90 3use std::path::{Path, PathBuf};
6a584cfd 4
54ef4f15 5use anyhow::{bail, Context, Error};
c443f58b 6use nix::dir::Dir;
dac88033 7use nix::fcntl::OFlag;
c443f58b 8use nix::sys::stat::{mkdirat, Mode};
6a584cfd 9
25877d05 10use proxmox_sys::error::SysError;
c443f58b 11use pxar::Metadata;
dac88033 12
7eacdc76 13use crate::pxar::tools::{assert_single_path_component, perms_from_metadata};
6a584cfd 14
c443f58b
WB
15pub struct PxarDir {
16 file_name: OsString,
17 metadata: Metadata,
18 dir: Option<Dir>,
6a584cfd
CE
19}
20
21impl PxarDir {
c443f58b 22 pub fn new(file_name: OsString, metadata: Metadata) -> Self {
6a584cfd 23 Self {
c443f58b
WB
24 file_name,
25 metadata,
6a584cfd
CE
26 dir: None,
27 }
28 }
29
c443f58b
WB
30 pub fn with_dir(dir: Dir, metadata: Metadata) -> Self {
31 Self {
32 file_name: OsString::from("."),
33 metadata,
34 dir: Some(dir),
35 }
36 }
6a584cfd 37
dd519bba
WB
38 fn create_dir(
39 &mut self,
40 parent: RawFd,
41 allow_existing_dirs: bool,
42 ) -> Result<BorrowedFd, Error> {
6aff2de5 43 if let Err(err) = mkdirat(
c443f58b
WB
44 parent,
45 self.file_name.as_os_str(),
46 perms_from_metadata(&self.metadata)?,
47 ) {
6aff2de5
MS
48 if !(allow_existing_dirs && err.already_exists()) {
49 return Err(err.into());
6a584cfd
CE
50 }
51 }
52
c443f58b
WB
53 self.open_dir(parent)
54 }
55
dd519bba 56 fn open_dir(&mut self, parent: RawFd) -> Result<BorrowedFd, Error> {
c443f58b 57 let dir = Dir::openat(
dac88033 58 parent,
c443f58b 59 self.file_name.as_os_str(),
dac88033
CE
60 OFlag::O_DIRECTORY,
61 Mode::empty(),
62 )?;
6a584cfd 63
a63a869e
WB
64 // FIXME: Once `nix` adds `AsFd` support use `.as_fd()` instead.
65 let fd = unsafe { BorrowedFd::borrow_raw(dir.as_raw_fd()) };
c443f58b
WB
66 self.dir = Some(dir);
67
68 Ok(fd)
6a584cfd 69 }
c443f58b 70
dd519bba 71 pub fn try_as_borrowed_fd(&self) -> Option<BorrowedFd> {
a63a869e
WB
72 // Once `nix` adds `AsFd` support use `.as_fd()` instead.
73 self.dir
74 .as_ref()
75 .map(|dir| unsafe { BorrowedFd::borrow_raw(dir.as_raw_fd()) })
c443f58b
WB
76 }
77
78 pub fn metadata(&self) -> &Metadata {
79 &self.metadata
80 }
81}
82
83pub struct PxarDirStack {
84 dirs: Vec<PxarDir>,
85 path: PathBuf,
86 created: usize,
6a584cfd
CE
87}
88
fe076c82 89impl PxarDirStack {
c443f58b 90 pub fn new(root: Dir, metadata: Metadata) -> Self {
6a584cfd 91 Self {
c443f58b
WB
92 dirs: vec![PxarDir::with_dir(root, metadata)],
93 path: PathBuf::from("/"),
94 created: 1, // the root directory exists
6a584cfd
CE
95 }
96 }
97
c443f58b
WB
98 pub fn is_empty(&self) -> bool {
99 self.dirs.is_empty()
6a584cfd
CE
100 }
101
c443f58b 102 pub fn push(&mut self, file_name: OsString, metadata: Metadata) -> Result<(), Error> {
7eacdc76 103 assert_single_path_component(&file_name)?;
c443f58b
WB
104 self.path.push(&file_name);
105 self.dirs.push(PxarDir::new(file_name, metadata));
106 Ok(())
6a584cfd
CE
107 }
108
c443f58b
WB
109 pub fn pop(&mut self) -> Result<Option<PxarDir>, Error> {
110 let out = self.dirs.pop();
111 if !self.path.pop() {
112 if self.path.as_os_str() == "/" {
113 // we just finished the root directory, make sure this can only happen once:
114 self.path = PathBuf::new();
115 } else {
116 bail!("lost track of path");
117 }
118 }
119 self.created = self.created.min(self.dirs.len());
120 Ok(out)
6a584cfd
CE
121 }
122
dd519bba 123 pub fn last_dir_fd(&mut self, allow_existing_dirs: bool) -> Result<BorrowedFd, Error> {
c443f58b
WB
124 // should not be possible given the way we use it:
125 assert!(!self.dirs.is_empty(), "PxarDirStack underrun");
6a584cfd 126
dd519bba 127 let dirs_len = self.dirs.len();
c443f58b 128 let mut fd = self.dirs[self.created - 1]
dd519bba 129 .try_as_borrowed_fd()
54ef4f15 130 .context("lost track of directory file descriptors")?
dd519bba
WB
131 .as_raw_fd();
132
133 while self.created < dirs_len {
134 fd = self.dirs[self.created]
135 .create_dir(fd, allow_existing_dirs)?
136 .as_raw_fd();
c443f58b 137 self.created += 1;
6a584cfd 138 }
c443f58b 139
dd519bba
WB
140 self.dirs[self.created - 1]
141 .try_as_borrowed_fd()
54ef4f15 142 .context("lost track of directory file descriptors")
6a584cfd
CE
143 }
144
98c54240 145 pub fn create_last_dir(&mut self, allow_existing_dirs: bool) -> Result<(), Error> {
dd519bba 146 let _: BorrowedFd = self.last_dir_fd(allow_existing_dirs)?;
98c54240
WB
147 Ok(())
148 }
149
dd519bba 150 pub fn root_dir_fd(&self) -> Result<BorrowedFd, Error> {
c443f58b
WB
151 // should not be possible given the way we use it:
152 assert!(!self.dirs.is_empty(), "PxarDirStack underrun");
6a584cfd 153
c443f58b 154 self.dirs[0]
dd519bba 155 .try_as_borrowed_fd()
54ef4f15 156 .context("lost track of directory file descriptors")
6a584cfd 157 }
79e58a90
WB
158
159 pub fn path(&self) -> &Path {
160 &self.path
161 }
6a584cfd 162}