]>
Commit | Line | Data |
---|---|---|
98c54240 | 1 | use std::ffi::{OsStr, OsString}; |
6a584cfd CE |
2 | use std::os::unix::io::{AsRawFd, RawFd}; |
3 | use std::path::PathBuf; | |
4 | ||
c443f58b WB |
5 | use anyhow::{bail, format_err, Error}; |
6 | use nix::dir::Dir; | |
dac88033 | 7 | use nix::fcntl::OFlag; |
c443f58b | 8 | use nix::sys::stat::{mkdirat, Mode}; |
6a584cfd | 9 | |
c443f58b WB |
10 | use proxmox::sys::error::SysError; |
11 | use pxar::Metadata; | |
dac88033 | 12 | |
7eacdc76 | 13 | use crate::pxar::tools::{assert_single_path_component, perms_from_metadata}; |
6a584cfd | 14 | |
c443f58b WB |
15 | pub struct PxarDir { |
16 | file_name: OsString, | |
17 | metadata: Metadata, | |
18 | dir: Option<Dir>, | |
6a584cfd CE |
19 | } |
20 | ||
21 | impl 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 | ||
82 | pub struct PxarDirStack { | |
83 | dirs: Vec<PxarDir>, | |
84 | path: PathBuf, | |
85 | created: usize, | |
6a584cfd CE |
86 | } |
87 | ||
fe076c82 | 88 | impl 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 | } |