]>
Commit | Line | Data |
---|---|---|
79e58a90 | 1 | use std::ffi::OsString; |
a63a869e | 2 | use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd}; |
79e58a90 | 3 | use std::path::{Path, PathBuf}; |
6a584cfd | 4 | |
54ef4f15 | 5 | use anyhow::{bail, Context, Error}; |
c443f58b | 6 | use nix::dir::Dir; |
dac88033 | 7 | use nix::fcntl::OFlag; |
c443f58b | 8 | use nix::sys::stat::{mkdirat, Mode}; |
6a584cfd | 9 | |
25877d05 | 10 | use proxmox_sys::error::SysError; |
c443f58b | 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 | |
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 | ||
83 | pub struct PxarDirStack { | |
84 | dirs: Vec<PxarDir>, | |
85 | path: PathBuf, | |
86 | created: usize, | |
6a584cfd CE |
87 | } |
88 | ||
fe076c82 | 89 | impl 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 | } |