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 /// A builder for the file [`Metadata`] stored in pxar archives.
156 pub struct MetadataBuilder
{
160 impl MetadataBuilder
{
161 /// Create a new [`MetadataBuilder`] given an initial type/mode bitset.
162 pub const fn new(type_and_mode
: u64) -> Self {
170 mtime
: format
::StatxTimestamp
::zero(),
178 default_users
: Vec
::new(),
179 default_groups
: Vec
::new(),
182 quota_project_id
: None
,
187 /// Build the [`Metadata`].
188 pub fn build(self) -> Metadata
{
192 /// Set the file mode (complete `stat.st_mode` with file type and permission bits).
193 pub const fn st_mode(mut self, mode
: u64) -> Self {
194 self.inner
.stat
.mode
= mode
;
198 /// Set the file type (`mode & mode::IFMT`).
199 pub const fn file_type(mut self, mode
: u64) -> Self {
200 self.inner
.stat
.mode
= (self.inner
.stat
.mode
& !mode
::IFMT
) | (mode
& mode
::IFMT
);
204 /// Set the file mode bits (`mode & !mode::IFMT`).
205 pub const fn file_mode(mut self, mode
: u64) -> Self {
206 self.inner
.stat
.mode
= (self.inner
.stat
.mode
& mode
::IFMT
) | (mode
& !mode
::IFMT
);
210 /// Set the modification time from a statx timespec value.
211 pub fn mtime_full(mut self, mtime
: format
::StatxTimestamp
) -> Self {
212 self.inner
.stat
.mtime
= mtime
;
216 /// Set the modification time from a duration since the epoch (`SystemTime::UNIX_EPOCH`).
217 pub fn mtime_unix(self, mtime
: std
::time
::Duration
) -> Self {
218 self.mtime_full(format
::StatxTimestamp
::from_duration_since_epoch(mtime
))
221 /// Set the modification time from a system time.
222 pub fn mtime(self, mtime
: std
::time
::SystemTime
) -> Self {
223 self.mtime_full(mtime
.into())
226 /// Set the ownership information.
227 pub const fn owner(self, uid
: u32, gid
: u32) -> Self {
228 self.uid(uid
).gid(gid
)
231 /// Set the owning user id.
232 pub const fn uid(mut self, uid
: u32) -> Self {
233 self.inner
.stat
.uid
= uid
;
237 /// Set the owning user id.
238 pub const fn gid(mut self, gid
: u32) -> Self {
239 self.inner
.stat
.gid
= gid
;
243 /// Add an extended attribute.
244 pub fn xattr
<N
: AsRef
<[u8]>, V
: AsRef
<[u8]>>(mut self, name
: N
, value
: V
) -> Self {
245 self.inner
.xattrs
.push(format
::XAttr
::new(name
, value
));
249 /// Add a user ACL entry.
250 pub fn acl_user(mut self, entry
: format
::acl
::User
) -> Self {
251 self.inner
.acl
.users
.push(entry
);
255 /// Add a group ACL entry.
256 pub fn acl_group(mut self, entry
: format
::acl
::Group
) -> Self {
257 self.inner
.acl
.groups
.push(entry
);
261 /// Add a user default-ACL entry.
262 pub fn default_acl_user(mut self, entry
: format
::acl
::User
) -> Self {
263 self.inner
.acl
.default_users
.push(entry
);
267 /// Add a group default-ACL entry.
268 pub fn default_acl_group(mut self, entry
: format
::acl
::Group
) -> Self {
269 self.inner
.acl
.default_groups
.push(entry
);
273 /// Set the default ACL entry for a directory.
274 pub const fn default_acl(mut self, entry
: Option
<format
::acl
::Default
>) -> Self {
275 self.inner
.acl
.default = entry
;
279 /// Set the quota project id.
280 pub fn quota_project_id(mut self, id
: Option
<u64>) -> Self {
281 self.inner
.quota_project_id
= id
.map(|projid
| format
::QuotaProjectId { projid }
);
285 /// Set the raw file capability data.
286 pub fn fcaps(mut self, fcaps
: Option
<Vec
<u8>>) -> Self {
287 self.inner
.fcaps
= fcaps
.map(|data
| format
::FCaps { data }
);
291 #[cfg(all(test, target_os = "linux"))]
292 /// Fill the metadata with information from `struct stat`.
293 pub fn fill_from_stat(self, stat
: &libc
::stat
) -> Self {
294 self.st_mode(u64::from(stat
.st_mode
))
295 .owner(stat
.st_uid
, stat
.st_gid
)
296 .mtime_full(format
::StatxTimestamp
::from_stat(
298 stat
.st_mtime_nsec
as u32,
303 /// ACL entries of a pxar archive.
305 /// This contains all the various ACL entry types supported by the pxar archive format.
306 #[derive(Clone, Debug, Default)]
307 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
310 pub users
: Vec
<format
::acl
::User
>,
313 pub groups
: Vec
<format
::acl
::Group
>,
315 /// Group object ACL entry.
316 pub group_obj
: Option
<format
::acl
::GroupObject
>,
318 /// Default permissions.
319 pub default: Option
<format
::acl
::Default
>,
321 /// Default user permissions.
322 pub default_users
: Vec
<format
::acl
::User
>,
324 /// Default group permissions.
325 pub default_groups
: Vec
<format
::acl
::Group
>,
329 /// Shortcut to check if all fields of this [`Acl`] entry are empty.
330 pub fn is_empty(&self) -> bool
{
331 self.users
.is_empty()
332 && self.groups
.is_empty()
333 && self.group_obj
.is_none()
334 && self.default.is_none()
335 && self.default_users
.is_empty()
336 && self.default_groups
.is_empty()
340 /// Pxar archive entry kind.
342 /// Identifies whether the entry is a file, symlink, directory, etc.
343 #[derive(Clone, Debug)]
346 Symlink(format
::Symlink
),
348 /// Hard links, relative to the root of the current archive.
349 Hardlink(format
::Hardlink
),
352 Device(format
::Device
),
354 /// Named unix socket.
362 /// The file size in bytes.
365 /// The file's byte offset inside the archive, if available.
369 /// Directory entry. When iterating through an archive, the contents follow next.
372 /// End of a directory. This is for internal use to remember the goodbye-table of a directory
373 /// entry. Will not occur during normal iteration.
377 /// A pxar archive entry. This contains the current path, file metadata and entry type specific
379 #[derive(Clone, Debug)]
386 /// General accessors.
388 /// Clear everything except for the path.
389 fn clear_data(&mut self) {
390 self.metadata
= Metadata
::default();
391 self.kind
= EntryKind
::GoodbyeTable
;
394 fn internal_default() -> Self {
396 path
: PathBuf
::default(),
397 metadata
: Metadata
::default(),
398 kind
: EntryKind
::GoodbyeTable
,
402 fn take(&mut self) -> Self {
403 let this
= mem
::replace(self, Self::internal_default());
404 self.path
= this
.path
.clone();
408 /// Get the full path of this file within the current pxar directory structure.
410 pub fn path(&self) -> &Path
{
414 /// Convenience method to get just the file name portion of the current path.
416 pub fn file_name(&self) -> &OsStr
{
417 self.path
.file_name().unwrap_or_else(|| OsStr
::new(""))
420 /// Get the file metadata.
422 pub fn metadata(&self) -> &Metadata
{
426 /// Take out the metadata.
428 pub fn into_metadata(self) -> Metadata
{
432 /// Get the entry-type specific information.
433 pub fn kind(&self) -> &EntryKind
{
437 /// Get the value of the symbolic link if it is one.
438 pub fn get_symlink(&self) -> Option
<&OsStr
> {
440 EntryKind
::Symlink(link
) => Some(link
.as_ref()),
445 /// Get the value of the hard link if it is one.
446 pub fn get_hardlink(&self) -> Option
<&OsStr
> {
448 EntryKind
::Hardlink(link
) => Some(link
.as_ref()),
453 /// Get the value of the device node if it is one.
454 pub fn get_device(&self) -> Option
<format
::Device
> {
456 EntryKind
::Device(dev
) => Some(dev
.clone()),
462 /// Convenience helpers.
464 /// Check whether this is a directory.
465 pub fn is_dir(&self) -> bool
{
466 matches
!(self.kind
, EntryKind
::Directory
)
469 /// Check whether this is a symbolic link.
470 pub fn is_symlink(&self) -> bool
{
471 matches
!(self.kind
, EntryKind
::Symlink(_
))
474 /// Check whether this is a hard link.
475 pub fn is_hardlink(&self) -> bool
{
476 matches
!(self.kind
, EntryKind
::Hardlink(_
))
479 /// Check whether this is a device node.
480 pub fn is_device(&self) -> bool
{
481 matches
!(self.kind
, EntryKind
::Device(_
))
484 /// Check whether this is a regular file.
485 pub fn is_regular_file(&self) -> bool
{
486 matches
!(self.kind
, EntryKind
::File { .. }
)
489 /// Get the file size if this is a regular file, or `None`.
490 pub fn file_size(&self) -> Option
<u64> {
492 EntryKind
::File { size, .. }
=> Some(size
),