1 //! Proxmox backup archive format handling.
3 //! This implements a reader and writer for the proxmox archive format (.pxar).
7 use std
::path
::{Path, PathBuf}
;
19 pub mod binary_tree_array
;
24 pub use format
::{mode, Stat}
;
26 /// File metadata found in pxar archives.
28 /// This includes the usual data you'd get from `stat()` as well as ACLs, extended attributes, file
29 /// capabilities and more.
30 #[derive(Clone, Debug, Default)]
31 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
33 /// Data typically found in a `stat()` call.
36 /// Extended attributes.
37 pub xattrs
: Vec
<format
::XAttr
>,
42 /// File capabilities.
43 pub fcaps
: Option
<format
::FCaps
>,
46 pub quota_project_id
: Option
<format
::QuotaProjectId
>,
49 impl From
<Stat
> for Metadata
{
50 fn from(stat
: Stat
) -> Self {
58 impl From
<&std
::fs
::Metadata
> for Metadata
{
59 fn from(meta
: &std
::fs
::Metadata
) -> Metadata
{
60 // NOTE: fill the remaining metadata via feature flags?
61 Self::from(Stat
::from(meta
))
65 impl From
<std
::fs
::Metadata
> for Metadata
{
66 fn from(meta
: std
::fs
::Metadata
) -> Metadata
{
71 /// Convenience helpers.
73 /// Get the file type (`mode & mode::IFMT`).
75 pub fn file_type(&self) -> u64 {
79 /// Get the file mode bits (`mode & !mode::IFMT`).
81 pub fn file_mode(&self) -> u64 {
85 /// Check whether this is a directory.
87 pub fn is_dir(&self) -> bool
{
91 /// Check whether this is a symbolic link.
93 pub fn is_symlink(&self) -> bool
{
94 self.stat
.is_symlink()
97 /// Check whether this is a device node.
99 pub fn is_device(&self) -> bool
{
100 self.stat
.is_device()
103 /// Check whether this is a regular file.
105 pub fn is_regular_file(&self) -> bool
{
106 self.stat
.is_regular_file()
109 /// Check whether this is a named pipe (FIFO).
111 pub fn is_fifo(&self) -> bool
{
115 /// Check whether this is a named socket.
117 pub fn is_socket(&self) -> bool
{
118 self.stat
.is_socket()
121 /// Get the mtime as duration since the epoch. an `Ok` value is a positive duration, an `Err`
122 /// value is a negative duration.
123 pub fn mtime_as_duration(&self) -> format
::SignedDuration
{
124 self.stat
.mtime_as_duration()
127 /// A more convenient way to create metadata for a regular file.
128 pub fn file_builder(mode
: u64) -> MetadataBuilder
{
129 Self::builder(mode
::IFREG
| (mode
& !mode
::IFMT
))
132 /// A more convenient way to create metadata for a directory file.
133 pub fn dir_builder(mode
: u64) -> MetadataBuilder
{
134 Self::builder(mode
::IFDIR
| (mode
& !mode
::IFMT
))
137 /// A more convenient way to create generic metadata.
138 pub const fn builder(mode
: u64) -> MetadataBuilder
{
139 MetadataBuilder
::new(mode
)
142 #[cfg(all(test, target_os = "linux"))]
143 /// A more convenient way to create metadata starting from `libc::stat`.
144 pub fn builder_from_stat(stat
: &libc
::stat
) -> MetadataBuilder
{
145 MetadataBuilder
::new(0).fill_from_stat(stat
)
149 impl From
<MetadataBuilder
> for Metadata
{
150 fn from(builder
: MetadataBuilder
) -> Self {
155 pub struct MetadataBuilder
{
159 impl MetadataBuilder
{
160 pub const fn new(type_and_mode
: u64) -> Self {
168 mtime
: format
::StatxTimestamp
::zero(),
176 default_users
: Vec
::new(),
177 default_groups
: Vec
::new(),
180 quota_project_id
: None
,
185 pub fn build(self) -> Metadata
{
189 /// Set the file mode (complete `stat.st_mode` with file type and permission bits).
190 pub const fn st_mode(mut self, mode
: u64) -> Self {
191 self.inner
.stat
.mode
= mode
;
195 /// Set the file type (`mode & mode::IFMT`).
196 pub const fn file_type(mut self, mode
: u64) -> Self {
197 self.inner
.stat
.mode
= (self.inner
.stat
.mode
& !mode
::IFMT
) | (mode
& mode
::IFMT
);
201 /// Set the file mode bits (`mode & !mode::IFMT`).
202 pub const fn file_mode(mut self, mode
: u64) -> Self {
203 self.inner
.stat
.mode
= (self.inner
.stat
.mode
& mode
::IFMT
) | (mode
& !mode
::IFMT
);
207 /// Set the modification time from a statx timespec value.
208 pub fn mtime_full(mut self, mtime
: format
::StatxTimestamp
) -> Self {
209 self.inner
.stat
.mtime
= mtime
;
213 /// Set the modification time from a duration since the epoch (`SystemTime::UNIX_EPOCH`).
214 pub fn mtime_unix(self, mtime
: std
::time
::Duration
) -> Self {
215 self.mtime_full(format
::StatxTimestamp
::from_duration_since_epoch(mtime
))
218 /// Set the modification time from a system time.
219 pub fn mtime(self, mtime
: std
::time
::SystemTime
) -> Self {
220 self.mtime_full(mtime
.into())
223 /// Set the ownership information.
224 pub const fn owner(self, uid
: u32, gid
: u32) -> Self {
225 self.uid(uid
).gid(gid
)
228 /// Set the owning user id.
229 pub const fn uid(mut self, uid
: u32) -> Self {
230 self.inner
.stat
.uid
= uid
;
234 /// Set the owning user id.
235 pub const fn gid(mut self, gid
: u32) -> Self {
236 self.inner
.stat
.gid
= gid
;
240 /// Add an extended attribute.
241 pub fn xattr
<N
: AsRef
<[u8]>, V
: AsRef
<[u8]>>(mut self, name
: N
, value
: V
) -> Self {
242 self.inner
.xattrs
.push(format
::XAttr
::new(name
, value
));
246 /// Add a user ACL entry.
247 pub fn acl_user(mut self, entry
: format
::acl
::User
) -> Self {
248 self.inner
.acl
.users
.push(entry
);
252 /// Add a group ACL entry.
253 pub fn acl_group(mut self, entry
: format
::acl
::Group
) -> Self {
254 self.inner
.acl
.groups
.push(entry
);
258 /// Add a user default-ACL entry.
259 pub fn default_acl_user(mut self, entry
: format
::acl
::User
) -> Self {
260 self.inner
.acl
.default_users
.push(entry
);
264 /// Add a group default-ACL entry.
265 pub fn default_acl_group(mut self, entry
: format
::acl
::Group
) -> Self {
266 self.inner
.acl
.default_groups
.push(entry
);
270 /// Set the default ACL entry for a directory.
271 pub const fn default_acl(mut self, entry
: Option
<format
::acl
::Default
>) -> Self {
272 self.inner
.acl
.default = entry
;
276 /// Set the quota project id.
277 pub fn quota_project_id(mut self, id
: Option
<u64>) -> Self {
278 self.inner
.quota_project_id
= id
.map(|projid
| format
::QuotaProjectId { projid }
);
282 /// Set the raw file capability data.
283 pub fn fcaps(mut self, fcaps
: Option
<Vec
<u8>>) -> Self {
284 self.inner
.fcaps
= fcaps
.map(|data
| format
::FCaps { data }
);
288 #[cfg(all(test, target_os = "linux"))]
289 /// Fill the metadata with information from `struct stat`.
290 pub fn fill_from_stat(self, stat
: &libc
::stat
) -> Self {
291 self.st_mode(u64::from(stat
.st_mode
))
292 .owner(stat
.st_uid
, stat
.st_gid
)
293 .mtime_full(format
::StatxTimestamp
::from_stat(
295 stat
.st_mtime_nsec
as u32,
300 /// ACL entries of a pxar archive.
302 /// This contains all the various ACL entry types supported by the pxar archive format.
303 #[derive(Clone, Debug, Default)]
304 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
307 pub users
: Vec
<format
::acl
::User
>,
310 pub groups
: Vec
<format
::acl
::Group
>,
312 /// Group object ACL entry.
313 pub group_obj
: Option
<format
::acl
::GroupObject
>,
315 /// Default permissions.
316 pub default: Option
<format
::acl
::Default
>,
318 /// Default user permissions.
319 pub default_users
: Vec
<format
::acl
::User
>,
321 /// Default group permissions.
322 pub default_groups
: Vec
<format
::acl
::Group
>,
326 pub fn is_empty(&self) -> bool
{
327 self.users
.is_empty()
328 && self.groups
.is_empty()
329 && self.group_obj
.is_none()
330 && self.default.is_none()
331 && self.default_users
.is_empty()
332 && self.default_groups
.is_empty()
336 /// Pxar archive entry kind.
338 /// Identifies whether the entry is a file, symlink, directory, etc.
339 #[derive(Clone, Debug)]
342 Symlink(format
::Symlink
),
344 /// Hard links, relative to the root of the current archive.
345 Hardlink(format
::Hardlink
),
348 Device(format
::Device
),
350 /// Named unix socket.
357 File { size: u64, offset: Option<u64> }
,
359 /// Directory entry. When iterating through an archive, the contents follow next.
362 /// End of a directory. This is for internal use to remember the goodbye-table of a directory
363 /// entry. Will not occur during normal iteration.
367 /// A pxar archive entry. This contains the current path, file metadata and entry type specific
369 #[derive(Clone, Debug)]
376 /// General accessors.
378 /// Clear everything except for the path.
379 fn clear_data(&mut self) {
380 self.metadata
= Metadata
::default();
381 self.kind
= EntryKind
::GoodbyeTable
;
384 fn internal_default() -> Self {
386 path
: PathBuf
::default(),
387 metadata
: Metadata
::default(),
388 kind
: EntryKind
::GoodbyeTable
,
392 fn take(&mut self) -> Self {
393 let this
= mem
::replace(self, Self::internal_default());
394 self.path
= this
.path
.clone();
398 /// Get the full path of this file within the current pxar directory structure.
400 pub fn path(&self) -> &Path
{
404 /// Convenience method to get just the file name portion of the current path.
406 pub fn file_name(&self) -> &OsStr
{
407 self.path
.file_name().unwrap_or_else(|| OsStr
::new(""))
410 /// Get the file metadata.
412 pub fn metadata(&self) -> &Metadata
{
416 /// Take out the metadata.
418 pub fn into_metadata(self) -> Metadata
{
422 /// Get the entry-type specific information.
423 pub fn kind(&self) -> &EntryKind
{
427 /// Get the value of the symbolic link if it is one.
428 pub fn get_symlink(&self) -> Option
<&OsStr
> {
430 EntryKind
::Symlink(link
) => Some(link
.as_ref()),
435 /// Get the value of the hard link if it is one.
436 pub fn get_hardlink(&self) -> Option
<&OsStr
> {
438 EntryKind
::Hardlink(link
) => Some(link
.as_ref()),
443 /// Get the value of the device node if it is one.
444 pub fn get_device(&self) -> Option
<format
::Device
> {
446 EntryKind
::Device(dev
) => Some(dev
.clone()),
452 /// Convenience helpers.
454 /// Check whether this is a directory.
455 pub fn is_dir(&self) -> bool
{
457 EntryKind
::Directory { .. }
=> true,
462 /// Check whether this is a symbolic link.
463 pub fn is_symlink(&self) -> bool
{
465 EntryKind
::Symlink(_
) => true,
470 /// Check whether this is a hard link.
471 pub fn is_hardlink(&self) -> bool
{
473 EntryKind
::Hardlink(_
) => true,
478 /// Check whether this is a device node.
479 pub fn is_device(&self) -> bool
{
481 EntryKind
::Device(_
) => true,
486 /// Check whether this is a regular file.
487 pub fn is_regular_file(&self) -> bool
{
489 EntryKind
::File { .. }
=> true,
494 /// Get the file size if this is a regular file, or `None`.
495 pub fn file_size(&self) -> Option
<u64> {
497 EntryKind
::File { size, .. }
=> Some(size
),