]>
Commit | Line | Data |
---|---|---|
c443f58b | 1 | use std::ffi::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 | |
c443f58b | 13 | use crate::pxar::tools::{assert_relative_path, 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 | } | |
76 | } | |
77 | ||
78 | pub struct PxarDirStack { | |
79 | dirs: Vec<PxarDir>, | |
80 | path: PathBuf, | |
81 | created: usize, | |
6a584cfd CE |
82 | } |
83 | ||
fe076c82 | 84 | impl PxarDirStack { |
c443f58b | 85 | pub fn new(root: Dir, metadata: Metadata) -> Self { |
6a584cfd | 86 | Self { |
c443f58b WB |
87 | dirs: vec![PxarDir::with_dir(root, metadata)], |
88 | path: PathBuf::from("/"), | |
89 | created: 1, // the root directory exists | |
6a584cfd CE |
90 | } |
91 | } | |
92 | ||
c443f58b WB |
93 | pub fn is_empty(&self) -> bool { |
94 | self.dirs.is_empty() | |
6a584cfd CE |
95 | } |
96 | ||
c443f58b WB |
97 | pub fn push(&mut self, file_name: OsString, metadata: Metadata) -> Result<(), Error> { |
98 | assert_relative_path(&file_name)?; | |
99 | self.path.push(&file_name); | |
100 | self.dirs.push(PxarDir::new(file_name, metadata)); | |
101 | Ok(()) | |
6a584cfd CE |
102 | } |
103 | ||
c443f58b WB |
104 | pub fn pop(&mut self) -> Result<Option<PxarDir>, Error> { |
105 | let out = self.dirs.pop(); | |
106 | if !self.path.pop() { | |
107 | if self.path.as_os_str() == "/" { | |
108 | // we just finished the root directory, make sure this can only happen once: | |
109 | self.path = PathBuf::new(); | |
110 | } else { | |
111 | bail!("lost track of path"); | |
112 | } | |
113 | } | |
114 | self.created = self.created.min(self.dirs.len()); | |
115 | Ok(out) | |
6a584cfd CE |
116 | } |
117 | ||
c443f58b WB |
118 | pub fn last_dir_fd(&mut self, allow_existing_dirs: bool) -> Result<RawFd, Error> { |
119 | // should not be possible given the way we use it: | |
120 | assert!(!self.dirs.is_empty(), "PxarDirStack underrun"); | |
6a584cfd | 121 | |
c443f58b WB |
122 | let mut fd = self.dirs[self.created - 1] |
123 | .try_as_raw_fd() | |
124 | .ok_or_else(|| format_err!("lost track of directory file descriptors"))?; | |
125 | while self.created < self.dirs.len() { | |
126 | fd = self.dirs[self.created].create_dir(fd, allow_existing_dirs)?; | |
127 | self.created += 1; | |
6a584cfd | 128 | } |
c443f58b WB |
129 | |
130 | Ok(fd) | |
6a584cfd CE |
131 | } |
132 | ||
c443f58b WB |
133 | pub fn root_dir_fd(&self) -> Result<RawFd, Error> { |
134 | // should not be possible given the way we use it: | |
135 | assert!(!self.dirs.is_empty(), "PxarDirStack underrun"); | |
6a584cfd | 136 | |
c443f58b WB |
137 | self.dirs[0] |
138 | .try_as_raw_fd() | |
139 | .ok_or_else(|| format_err!("lost track of directory file descriptors")) | |
6a584cfd CE |
140 | } |
141 | } |