]> git.proxmox.com Git - proxmox-backup.git/blame - src/pxar/dir_stack.rs
require square brackets for ipv6 addresses
[proxmox-backup.git] / src / pxar / dir_stack.rs
CommitLineData
98c54240 1use std::ffi::{OsStr, OsString};
6a584cfd
CE
2use std::os::unix::io::{AsRawFd, RawFd};
3use std::path::PathBuf;
4
c443f58b
WB
5use anyhow::{bail, format_err, Error};
6use nix::dir::Dir;
dac88033 7use nix::fcntl::OFlag;
c443f58b 8use nix::sys::stat::{mkdirat, Mode};
6a584cfd 9
c443f58b
WB
10use proxmox::sys::error::SysError;
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
c443f58b
WB
38 fn create_dir(&mut self, parent: RawFd, allow_existing_dirs: bool) -> Result<RawFd, Error> {
39 match mkdirat(
40 parent,
41 self.file_name.as_os_str(),
42 perms_from_metadata(&self.metadata)?,
43 ) {
44 Ok(()) => (),
6a584cfd 45 Err(err) => {
c443f58b
WB
46 if !(allow_existing_dirs && err.already_exists()) {
47 return Err(err.into());
6a584cfd
CE
48 }
49 }
50 }
51
c443f58b
WB
52 self.open_dir(parent)
53 }
54
55 fn open_dir(&mut self, parent: RawFd) -> Result<RawFd, Error> {
56 let dir = Dir::openat(
dac88033 57 parent,
c443f58b 58 self.file_name.as_os_str(),
dac88033
CE
59 OFlag::O_DIRECTORY,
60 Mode::empty(),
61 )?;
6a584cfd 62
c443f58b
WB
63 let fd = dir.as_raw_fd();
64 self.dir = Some(dir);
65
66 Ok(fd)
6a584cfd 67 }
c443f58b
WB
68
69 pub fn try_as_raw_fd(&self) -> Option<RawFd> {
70 self.dir.as_ref().map(AsRawFd::as_raw_fd)
71 }
72
73 pub fn metadata(&self) -> &Metadata {
74 &self.metadata
75 }
98c54240
WB
76
77 pub fn file_name(&self) -> &OsStr {
78 &self.file_name
79 }
c443f58b
WB
80}
81
82pub struct PxarDirStack {
83 dirs: Vec<PxarDir>,
84 path: PathBuf,
85 created: usize,
6a584cfd
CE
86}
87
fe076c82 88impl PxarDirStack {
c443f58b 89 pub fn new(root: Dir, metadata: Metadata) -> Self {
6a584cfd 90 Self {
c443f58b
WB
91 dirs: vec![PxarDir::with_dir(root, metadata)],
92 path: PathBuf::from("/"),
93 created: 1, // the root directory exists
6a584cfd
CE
94 }
95 }
96
c443f58b
WB
97 pub fn is_empty(&self) -> bool {
98 self.dirs.is_empty()
6a584cfd
CE
99 }
100
c443f58b 101 pub fn push(&mut self, file_name: OsString, metadata: Metadata) -> Result<(), Error> {
7eacdc76 102 assert_single_path_component(&file_name)?;
c443f58b
WB
103 self.path.push(&file_name);
104 self.dirs.push(PxarDir::new(file_name, metadata));
105 Ok(())
6a584cfd
CE
106 }
107
c443f58b
WB
108 pub fn pop(&mut self) -> Result<Option<PxarDir>, Error> {
109 let out = self.dirs.pop();
110 if !self.path.pop() {
111 if self.path.as_os_str() == "/" {
112 // we just finished the root directory, make sure this can only happen once:
113 self.path = PathBuf::new();
114 } else {
115 bail!("lost track of path");
116 }
117 }
118 self.created = self.created.min(self.dirs.len());
119 Ok(out)
6a584cfd
CE
120 }
121
c443f58b
WB
122 pub fn last_dir_fd(&mut self, allow_existing_dirs: bool) -> Result<RawFd, Error> {
123 // should not be possible given the way we use it:
124 assert!(!self.dirs.is_empty(), "PxarDirStack underrun");
6a584cfd 125
c443f58b
WB
126 let mut fd = self.dirs[self.created - 1]
127 .try_as_raw_fd()
128 .ok_or_else(|| format_err!("lost track of directory file descriptors"))?;
129 while self.created < self.dirs.len() {
130 fd = self.dirs[self.created].create_dir(fd, allow_existing_dirs)?;
131 self.created += 1;
6a584cfd 132 }
c443f58b
WB
133
134 Ok(fd)
6a584cfd
CE
135 }
136
98c54240
WB
137 pub fn create_last_dir(&mut self, allow_existing_dirs: bool) -> Result<(), Error> {
138 let _: RawFd = self.last_dir_fd(allow_existing_dirs)?;
139 Ok(())
140 }
141
c443f58b
WB
142 pub fn root_dir_fd(&self) -> Result<RawFd, Error> {
143 // should not be possible given the way we use it:
144 assert!(!self.dirs.is_empty(), "PxarDirStack underrun");
6a584cfd 145
c443f58b
WB
146 self.dirs[0]
147 .try_as_raw_fd()
148 .ok_or_else(|| format_err!("lost track of directory file descriptors"))
6a584cfd
CE
149 }
150}