]> git.proxmox.com Git - proxmox-backup.git/blob - pbs-client/src/pxar/dir_stack.rs
update to proxmox-sys 0.2 crate
[proxmox-backup.git] / pbs-client / src / pxar / dir_stack.rs
1 use std::ffi::OsString;
2 use std::os::unix::io::{AsRawFd, RawFd};
3 use std::path::{Path, PathBuf};
4
5 use anyhow::{bail, format_err, Error};
6 use nix::dir::Dir;
7 use nix::fcntl::OFlag;
8 use nix::sys::stat::{mkdirat, Mode};
9
10 use proxmox_sys::error::SysError;
11 use proxmox_sys::fd::BorrowedFd;
12 use pxar::Metadata;
13
14 use crate::pxar::tools::{assert_single_path_component, perms_from_metadata};
15
16 pub struct PxarDir {
17 file_name: OsString,
18 metadata: Metadata,
19 dir: Option<Dir>,
20 }
21
22 impl PxarDir {
23 pub fn new(file_name: OsString, metadata: Metadata) -> Self {
24 Self {
25 file_name,
26 metadata,
27 dir: None,
28 }
29 }
30
31 pub fn with_dir(dir: Dir, metadata: Metadata) -> Self {
32 Self {
33 file_name: OsString::from("."),
34 metadata,
35 dir: Some(dir),
36 }
37 }
38
39 fn create_dir(
40 &mut self,
41 parent: RawFd,
42 allow_existing_dirs: bool,
43 ) -> Result<BorrowedFd, Error> {
44 match mkdirat(
45 parent,
46 self.file_name.as_os_str(),
47 perms_from_metadata(&self.metadata)?,
48 ) {
49 Ok(()) => (),
50 Err(err) => {
51 if !(allow_existing_dirs && err.already_exists()) {
52 return Err(err.into());
53 }
54 }
55 }
56
57 self.open_dir(parent)
58 }
59
60 fn open_dir(&mut self, parent: RawFd) -> Result<BorrowedFd, Error> {
61 let dir = Dir::openat(
62 parent,
63 self.file_name.as_os_str(),
64 OFlag::O_DIRECTORY,
65 Mode::empty(),
66 )?;
67
68 let fd = BorrowedFd::new(&dir);
69 self.dir = Some(dir);
70
71 Ok(fd)
72 }
73
74 pub fn try_as_borrowed_fd(&self) -> Option<BorrowedFd> {
75 self.dir.as_ref().map(BorrowedFd::new)
76 }
77
78 pub fn metadata(&self) -> &Metadata {
79 &self.metadata
80 }
81 }
82
83 pub struct PxarDirStack {
84 dirs: Vec<PxarDir>,
85 path: PathBuf,
86 created: usize,
87 }
88
89 impl PxarDirStack {
90 pub fn new(root: Dir, metadata: Metadata) -> Self {
91 Self {
92 dirs: vec![PxarDir::with_dir(root, metadata)],
93 path: PathBuf::from("/"),
94 created: 1, // the root directory exists
95 }
96 }
97
98 pub fn is_empty(&self) -> bool {
99 self.dirs.is_empty()
100 }
101
102 pub fn push(&mut self, file_name: OsString, metadata: Metadata) -> Result<(), Error> {
103 assert_single_path_component(&file_name)?;
104 self.path.push(&file_name);
105 self.dirs.push(PxarDir::new(file_name, metadata));
106 Ok(())
107 }
108
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)
121 }
122
123 pub fn last_dir_fd(&mut self, allow_existing_dirs: bool) -> Result<BorrowedFd, Error> {
124 // should not be possible given the way we use it:
125 assert!(!self.dirs.is_empty(), "PxarDirStack underrun");
126
127 let dirs_len = self.dirs.len();
128 let mut fd = self.dirs[self.created - 1]
129 .try_as_borrowed_fd()
130 .ok_or_else(|| format_err!("lost track of directory file descriptors"))?
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();
137 self.created += 1;
138 }
139
140 self.dirs[self.created - 1]
141 .try_as_borrowed_fd()
142 .ok_or_else(|| format_err!("lost track of directory file descriptors"))
143 }
144
145 pub fn create_last_dir(&mut self, allow_existing_dirs: bool) -> Result<(), Error> {
146 let _: BorrowedFd = self.last_dir_fd(allow_existing_dirs)?;
147 Ok(())
148 }
149
150 pub fn root_dir_fd(&self) -> Result<BorrowedFd, Error> {
151 // should not be possible given the way we use it:
152 assert!(!self.dirs.is_empty(), "PxarDirStack underrun");
153
154 self.dirs[0]
155 .try_as_borrowed_fd()
156 .ok_or_else(|| format_err!("lost track of directory file descriptors"))
157 }
158
159 pub fn path(&self) -> &Path {
160 &self.path
161 }
162 }