1 //! Proxmox backup archive format handling.
3 //! This implements a reader and writer for the proxmox archive format (.pxar).
5 #![deny(unsafe_op_in_unsafe_fn)]
9 use std
::path
::{Path, PathBuf}
;
21 pub mod binary_tree_array
;
26 pub use format
::{mode, Stat}
;
28 /// File metadata found in pxar archives.
30 /// This includes the usual data you'd get from `stat()` as well as ACLs, extended attributes, file
31 /// capabilities and more.
32 #[derive(Clone, Debug, Default, PartialEq)]
33 #[cfg_attr(feature = "test-harness", derive(Eq))]
35 /// Data typically found in a `stat()` call.
38 /// Extended attributes.
39 pub xattrs
: Vec
<format
::XAttr
>,
44 /// File capabilities.
45 pub fcaps
: Option
<format
::FCaps
>,
48 pub quota_project_id
: Option
<format
::QuotaProjectId
>,
51 impl From
<Stat
> for Metadata
{
52 fn from(stat
: Stat
) -> Self {
60 impl From
<&std
::fs
::Metadata
> for Metadata
{
61 fn from(meta
: &std
::fs
::Metadata
) -> Metadata
{
62 // NOTE: fill the remaining metadata via feature flags?
63 Self::from(Stat
::from(meta
))
67 impl From
<std
::fs
::Metadata
> for Metadata
{
68 fn from(meta
: std
::fs
::Metadata
) -> Metadata
{
73 /// Convenience helpers.
75 /// Get the file type (`mode & mode::IFMT`).
77 pub fn file_type(&self) -> u64 {
81 /// Get the file mode bits (`mode & !mode::IFMT`).
83 pub fn file_mode(&self) -> u64 {
87 /// Check whether this is a directory.
89 pub fn is_dir(&self) -> bool
{
93 /// Check whether this is a symbolic link.
95 pub fn is_symlink(&self) -> bool
{
96 self.stat
.is_symlink()
99 /// Check whether this is a device node.
101 pub fn is_device(&self) -> bool
{
102 self.stat
.is_device()
105 /// Check whether this is a regular file.
107 pub fn is_regular_file(&self) -> bool
{
108 self.stat
.is_regular_file()
111 /// Check whether this is a named pipe (FIFO).
113 pub fn is_fifo(&self) -> bool
{
117 /// Check whether this is a named socket.
119 pub fn is_socket(&self) -> bool
{
120 self.stat
.is_socket()
123 /// Get the mtime as duration since the epoch. an `Ok` value is a positive duration, an `Err`
124 /// value is a negative duration.
125 pub fn mtime_as_duration(&self) -> format
::SignedDuration
{
126 self.stat
.mtime_as_duration()
129 /// A more convenient way to create metadata for a regular file.
130 pub fn file_builder(mode
: u64) -> MetadataBuilder
{
131 Self::builder(mode
::IFREG
| (mode
& !mode
::IFMT
))
134 /// A more convenient way to create metadata for a directory file.
135 pub fn dir_builder(mode
: u64) -> MetadataBuilder
{
136 Self::builder(mode
::IFDIR
| (mode
& !mode
::IFMT
))
139 /// A more convenient way to create generic metadata.
140 pub const fn builder(mode
: u64) -> MetadataBuilder
{
141 MetadataBuilder
::new(mode
)
144 #[cfg(all(test, target_os = "linux"))]
145 /// A more convenient way to create metadata starting from `libc::stat`.
146 pub fn builder_from_stat(stat
: &libc
::stat
) -> MetadataBuilder
{
147 MetadataBuilder
::new(0).fill_from_stat(stat
)
151 impl From
<MetadataBuilder
> for Metadata
{
152 fn from(builder
: MetadataBuilder
) -> Self {
157 /// A builder for the file [`Metadata`] stored in pxar archives.
158 pub struct MetadataBuilder
{
162 impl MetadataBuilder
{
163 /// Create a new [`MetadataBuilder`] given an initial type/mode bitset.
164 pub const fn new(type_and_mode
: u64) -> Self {
172 mtime
: format
::StatxTimestamp
::zero(),
180 default_users
: Vec
::new(),
181 default_groups
: Vec
::new(),
184 quota_project_id
: None
,
189 /// Build the [`Metadata`].
190 pub fn build(self) -> Metadata
{
194 /// Set the file mode (complete `stat.st_mode` with file type and permission bits).
195 pub const fn st_mode(mut self, mode
: u64) -> Self {
196 self.inner
.stat
.mode
= mode
;
200 /// Set the file type (`mode & mode::IFMT`).
201 pub const fn file_type(mut self, mode
: u64) -> Self {
202 self.inner
.stat
.mode
= (self.inner
.stat
.mode
& !mode
::IFMT
) | (mode
& mode
::IFMT
);
206 /// Set the file mode bits (`mode & !mode::IFMT`).
207 pub const fn file_mode(mut self, mode
: u64) -> Self {
208 self.inner
.stat
.mode
= (self.inner
.stat
.mode
& mode
::IFMT
) | (mode
& !mode
::IFMT
);
212 /// Set the modification time from a statx timespec value.
213 pub fn mtime_full(mut self, mtime
: format
::StatxTimestamp
) -> Self {
214 self.inner
.stat
.mtime
= mtime
;
218 /// Set the modification time from a duration since the epoch (`SystemTime::UNIX_EPOCH`).
219 pub fn mtime_unix(self, mtime
: std
::time
::Duration
) -> Self {
220 self.mtime_full(format
::StatxTimestamp
::from_duration_since_epoch(mtime
))
223 /// Set the modification time from a system time.
224 pub fn mtime(self, mtime
: std
::time
::SystemTime
) -> Self {
225 self.mtime_full(mtime
.into())
228 /// Set the ownership information.
229 pub const fn owner(self, uid
: u32, gid
: u32) -> Self {
230 self.uid(uid
).gid(gid
)
233 /// Set the owning user id.
234 pub const fn uid(mut self, uid
: u32) -> Self {
235 self.inner
.stat
.uid
= uid
;
239 /// Set the owning user id.
240 pub const fn gid(mut self, gid
: u32) -> Self {
241 self.inner
.stat
.gid
= gid
;
245 /// Add an extended attribute.
246 pub fn xattr
<N
: AsRef
<[u8]>, V
: AsRef
<[u8]>>(mut self, name
: N
, value
: V
) -> Self {
247 self.inner
.xattrs
.push(format
::XAttr
::new(name
, value
));
251 /// Add a user ACL entry.
252 pub fn acl_user(mut self, entry
: format
::acl
::User
) -> Self {
253 self.inner
.acl
.users
.push(entry
);
257 /// Add a group ACL entry.
258 pub fn acl_group(mut self, entry
: format
::acl
::Group
) -> Self {
259 self.inner
.acl
.groups
.push(entry
);
263 /// Add a user default-ACL entry.
264 pub fn default_acl_user(mut self, entry
: format
::acl
::User
) -> Self {
265 self.inner
.acl
.default_users
.push(entry
);
269 /// Add a group default-ACL entry.
270 pub fn default_acl_group(mut self, entry
: format
::acl
::Group
) -> Self {
271 self.inner
.acl
.default_groups
.push(entry
);
275 /// Set the default ACL entry for a directory.
276 pub const fn default_acl(mut self, entry
: Option
<format
::acl
::Default
>) -> Self {
277 self.inner
.acl
.default = entry
;
281 /// Set the quota project id.
282 pub fn quota_project_id(mut self, id
: Option
<u64>) -> Self {
283 self.inner
.quota_project_id
= id
.map(|projid
| format
::QuotaProjectId { projid }
);
287 /// Set the raw file capability data.
288 pub fn fcaps(mut self, fcaps
: Option
<Vec
<u8>>) -> Self {
289 self.inner
.fcaps
= fcaps
.map(|data
| format
::FCaps { data }
);
293 #[cfg(all(test, target_os = "linux"))]
294 /// Fill the metadata with information from `struct stat`.
295 pub fn fill_from_stat(self, stat
: &libc
::stat
) -> Self {
296 self.st_mode(u64::from(stat
.st_mode
))
297 .owner(stat
.st_uid
, stat
.st_gid
)
298 .mtime_full(format
::StatxTimestamp
::from_stat(
300 stat
.st_mtime_nsec
as u32,
305 /// ACL entries of a pxar archive.
307 /// This contains all the various ACL entry types supported by the pxar archive format.
308 #[derive(Clone, Debug, Default, PartialEq)]
309 #[cfg_attr(feature = "test-harness", derive(Eq))]
312 pub users
: Vec
<format
::acl
::User
>,
315 pub groups
: Vec
<format
::acl
::Group
>,
317 /// Group object ACL entry.
318 pub group_obj
: Option
<format
::acl
::GroupObject
>,
320 /// Default permissions.
321 pub default: Option
<format
::acl
::Default
>,
323 /// Default user permissions.
324 pub default_users
: Vec
<format
::acl
::User
>,
326 /// Default group permissions.
327 pub default_groups
: Vec
<format
::acl
::Group
>,
331 /// Shortcut to check if all fields of this [`Acl`] entry are empty.
332 pub fn is_empty(&self) -> bool
{
333 self.users
.is_empty()
334 && self.groups
.is_empty()
335 && self.group_obj
.is_none()
336 && self.default.is_none()
337 && self.default_users
.is_empty()
338 && self.default_groups
.is_empty()
342 /// Pxar archive entry kind.
344 /// Identifies whether the entry is a file, symlink, directory, etc.
345 #[derive(Clone, Debug)]
348 Symlink(format
::Symlink
),
350 /// Hard links, relative to the root of the current archive.
351 Hardlink(format
::Hardlink
),
354 Device(format
::Device
),
356 /// Named unix socket.
364 /// The file size in bytes.
367 /// The file's byte offset inside the archive, if available.
371 /// Directory entry. When iterating through an archive, the contents follow next.
374 /// End of a directory. This is for internal use to remember the goodbye-table of a directory
375 /// entry. Will not occur during normal iteration.
379 /// A pxar archive entry. This contains the current path, file metadata and entry type specific
381 #[derive(Clone, Debug)]
388 /// General accessors.
390 /// Clear everything except for the path.
391 fn clear_data(&mut self) {
392 self.metadata
= Metadata
::default();
393 self.kind
= EntryKind
::GoodbyeTable
;
396 fn internal_default() -> Self {
398 path
: PathBuf
::default(),
399 metadata
: Metadata
::default(),
400 kind
: EntryKind
::GoodbyeTable
,
404 fn take(&mut self) -> Self {
405 let this
= mem
::replace(self, Self::internal_default());
406 self.path
= this
.path
.clone();
410 /// Get the full path of this file within the current pxar directory structure.
412 pub fn path(&self) -> &Path
{
416 /// Convenience method to get just the file name portion of the current path.
418 pub fn file_name(&self) -> &OsStr
{
419 self.path
.file_name().unwrap_or_else(|| OsStr
::new(""))
422 /// Get the file metadata.
424 pub fn metadata(&self) -> &Metadata
{
428 /// Take out the metadata.
430 pub fn into_metadata(self) -> Metadata
{
434 /// Get the entry-type specific information.
435 pub fn kind(&self) -> &EntryKind
{
439 /// Get the value of the symbolic link if it is one.
440 pub fn get_symlink(&self) -> Option
<&OsStr
> {
442 EntryKind
::Symlink(link
) => Some(link
.as_ref()),
447 /// Get the value of the hard link if it is one.
448 pub fn get_hardlink(&self) -> Option
<&OsStr
> {
450 EntryKind
::Hardlink(link
) => Some(link
.as_ref()),
455 /// Get the value of the device node if it is one.
456 pub fn get_device(&self) -> Option
<format
::Device
> {
458 EntryKind
::Device(dev
) => Some(dev
.clone()),
464 /// Convenience helpers.
466 /// Check whether this is a directory.
467 pub fn is_dir(&self) -> bool
{
468 matches
!(self.kind
, EntryKind
::Directory
)
471 /// Check whether this is a symbolic link.
472 pub fn is_symlink(&self) -> bool
{
473 matches
!(self.kind
, EntryKind
::Symlink(_
))
476 /// Check whether this is a hard link.
477 pub fn is_hardlink(&self) -> bool
{
478 matches
!(self.kind
, EntryKind
::Hardlink(_
))
481 /// Check whether this is a device node.
482 pub fn is_device(&self) -> bool
{
483 matches
!(self.kind
, EntryKind
::Device(_
))
486 /// Check whether this is a regular file.
487 pub fn is_regular_file(&self) -> bool
{
488 matches
!(self.kind
, EntryKind
::File { .. }
)
491 /// Get the file size if this is a regular file, or `None`.
492 pub fn file_size(&self) -> Option
<u64> {
494 EntryKind
::File { size, .. }
=> Some(size
),