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
;
23 /// Reexport of `format::Entry`. Since this conveys mostly information found via the `stat` syscall
24 /// we mostly use this name for public interfaces.
26 pub use format
::Entry
as Stat
;
31 /// File metadata found in pxar archives.
33 /// This includes the usual data you'd get from `stat()` as well as ACLs, extended attributes, file
34 /// capabilities and more.
35 #[derive(Clone, Debug, Default)]
36 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
38 /// Data typically found in a `stat()` call.
41 /// Extended attributes.
42 pub xattrs
: Vec
<format
::XAttr
>,
47 /// File capabilities.
48 pub fcaps
: Option
<format
::FCaps
>,
51 pub quota_project_id
: Option
<format
::QuotaProjectId
>,
54 impl From
<Stat
> for Metadata
{
55 fn from(stat
: Stat
) -> Self {
63 impl From
<&std
::fs
::Metadata
> for Metadata
{
64 fn from(meta
: &std
::fs
::Metadata
) -> Metadata
{
65 // NOTE: fill the remaining metadata via feature flags?
66 Self::from(Stat
::from(meta
))
70 impl From
<std
::fs
::Metadata
> for Metadata
{
71 fn from(meta
: std
::fs
::Metadata
) -> Metadata
{
76 /// Convenience helpers.
78 /// Get the file type (`mode & mode::IFMT`).
80 pub fn file_type(&self) -> u64 {
84 /// Get the file mode bits (`mode & !mode::IFMT`).
86 pub fn file_mode(&self) -> u64 {
90 /// Check whether this is a directory.
92 pub fn is_dir(&self) -> bool
{
96 /// Check whether this is a symbolic link.
98 pub fn is_symlink(&self) -> bool
{
99 self.stat
.is_symlink()
102 /// Check whether this is a device node.
104 pub fn is_device(&self) -> bool
{
105 self.stat
.is_device()
108 /// Check whether this is a regular file.
110 pub fn is_regular_file(&self) -> bool
{
111 self.stat
.is_regular_file()
114 /// Check whether this is a named pipe (FIFO).
116 pub fn is_fifo(&self) -> bool
{
120 /// Check whether this is a named socket.
122 pub fn is_socket(&self) -> bool
{
123 self.stat
.is_socket()
126 /// Get the mtime as duration since the epoch. an `Ok` value is a positive duration, an `Err`
127 /// value is a negative duration.
128 pub fn mtime_as_duration(&self) -> format
::SignedDuration
{
129 self.stat
.mtime_as_duration()
132 /// A more convenient way to create metadata for a regular file.
133 pub fn file_builder(mode
: u64) -> MetadataBuilder
{
134 Self::builder(mode
::IFREG
| (mode
& !mode
::IFMT
))
137 /// A more convenient way to create metadata for a directory file.
138 pub fn dir_builder(mode
: u64) -> MetadataBuilder
{
139 Self::builder(mode
::IFDIR
| (mode
& !mode
::IFMT
))
142 /// A more convenient way to create generic metadata.
143 pub fn builder(mode
: u64) -> MetadataBuilder
{
144 MetadataBuilder
::new(mode
)
148 impl From
<MetadataBuilder
> for Metadata
{
149 fn from(builder
: MetadataBuilder
) -> Self {
154 pub struct MetadataBuilder
{
158 impl MetadataBuilder
{
159 pub const fn new(type_and_mode
: u64) -> Self {
167 mtime
: format
::StatxTimestamp
::zero(),
175 default_users
: Vec
::new(),
176 default_groups
: Vec
::new(),
179 quota_project_id
: None
,
184 pub fn build(self) -> Metadata
{
188 /// Set the file type (`mode & mode::IFMT`).
189 pub const fn file_type(mut self, mode
: u64) -> Self {
190 self.inner
.stat
.mode
= (self.inner
.stat
.mode
& !mode
::IFMT
) | (mode
& mode
::IFMT
);
194 /// Set the file mode bits (`mode & !mode::IFMT`).
195 pub const fn file_mode(mut self, mode
: u64) -> Self {
196 self.inner
.stat
.mode
= (self.inner
.stat
.mode
& mode
::IFMT
) | (mode
& !mode
::IFMT
);
200 /// Set the modification time from a statx timespec value.
201 pub fn mtime_full(mut self, mtime
: format
::StatxTimestamp
) -> Self {
202 self.inner
.stat
.mtime
= mtime
;
206 /// Set the modification time from a duration since the epoch (`SystemTime::UNIX_EPOCH`).
207 pub fn mtime_unix(self, mtime
: std
::time
::Duration
) -> Self {
208 self.mtime_full(format
::StatxTimestamp
::from_duration_since_epoch(mtime
))
211 /// Set the modification time from a system time.
212 pub fn mtime(self, mtime
: std
::time
::SystemTime
) -> Self {
213 self.mtime_full(mtime
.into())
216 /// Set the ownership information.
217 pub const fn owner(self, uid
: u32, gid
: u32) -> Self {
218 self.uid(uid
).gid(gid
)
221 /// Set the owning user id.
222 pub const fn uid(mut self, uid
: u32) -> Self {
223 self.inner
.stat
.uid
= uid
;
227 /// Set the owning user id.
228 pub const fn gid(mut self, gid
: u32) -> Self {
229 self.inner
.stat
.gid
= gid
;
233 /// Add an extended attribute.
234 pub fn xattr
<N
: AsRef
<[u8]>, V
: AsRef
<[u8]>>(mut self, name
: N
, value
: V
) -> Self {
235 self.inner
.xattrs
.push(format
::XAttr
::new(name
, value
));
239 /// Add a user ACL entry.
240 pub fn acl_user(mut self, entry
: format
::acl
::User
) -> Self {
241 self.inner
.acl
.users
.push(entry
);
245 /// Add a group ACL entry.
246 pub fn acl_group(mut self, entry
: format
::acl
::Group
) -> Self {
247 self.inner
.acl
.groups
.push(entry
);
251 /// Add a user default-ACL entry.
252 pub fn default_acl_user(mut self, entry
: format
::acl
::User
) -> Self {
253 self.inner
.acl
.default_users
.push(entry
);
257 /// Add a group default-ACL entry.
258 pub fn default_acl_group(mut self, entry
: format
::acl
::Group
) -> Self {
259 self.inner
.acl
.default_groups
.push(entry
);
263 /// Set the default ACL entry for a directory.
264 pub const fn default_acl(mut self, entry
: Option
<format
::acl
::Default
>) -> Self {
265 self.inner
.acl
.default = entry
;
269 /// Set the quota project id.
270 pub fn quota_project_id(mut self, id
: Option
<u64>) -> Self {
271 self.inner
.quota_project_id
= id
.map(|projid
| format
::QuotaProjectId { projid }
);
275 /// Set the raw file capability data.
276 pub fn fcaps(mut self, fcaps
: Option
<Vec
<u8>>) -> Self {
277 self.inner
.fcaps
= fcaps
.map(|data
| format
::FCaps { data }
);
282 /// ACL entries of a pxar archive.
284 /// This contains all the various ACL entry types supported by the pxar archive format.
285 #[derive(Clone, Debug, Default)]
286 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
289 pub users
: Vec
<format
::acl
::User
>,
292 pub groups
: Vec
<format
::acl
::Group
>,
294 /// Group object ACL entry.
295 pub group_obj
: Option
<format
::acl
::GroupObject
>,
297 /// Default permissions.
298 pub default: Option
<format
::acl
::Default
>,
300 /// Default user permissions.
301 pub default_users
: Vec
<format
::acl
::User
>,
303 /// Default group permissions.
304 pub default_groups
: Vec
<format
::acl
::Group
>,
308 pub fn is_empty(&self) -> bool
{
309 self.users
.is_empty()
310 && self.groups
.is_empty()
311 && self.group_obj
.is_none()
312 && self.default.is_none()
313 && self.default_users
.is_empty()
314 && self.default_groups
.is_empty()
318 /// Pxar archive entry kind.
320 /// Identifies whether the entry is a file, symlink, directory, etc.
321 #[derive(Clone, Debug)]
324 Symlink(format
::Symlink
),
326 /// Hard links, relative to the root of the current archive.
327 Hardlink(format
::Hardlink
),
330 Device(format
::Device
),
332 /// Named unix socket.
339 File { size: u64, offset: Option<u64> }
,
341 /// Directory entry. When iterating through an archive, the contents follow next.
344 /// End of a directory. This is for internal use to remember the goodbye-table of a directory
345 /// entry. Will not occur during normal iteration.
349 /// A pxar archive entry. This contains the current path, file metadata and entry type specific
351 #[derive(Clone, Debug)]
358 /// General accessors.
360 /// Clear everything except for the path.
361 fn clear_data(&mut self) {
362 self.metadata
= Metadata
::default();
363 self.kind
= EntryKind
::GoodbyeTable
;
366 fn internal_default() -> Self {
368 path
: PathBuf
::default(),
369 metadata
: Metadata
::default(),
370 kind
: EntryKind
::GoodbyeTable
,
374 fn take(&mut self) -> Self {
375 let this
= mem
::replace(self, Self::internal_default());
376 self.path
= this
.path
.clone();
380 /// Get the full path of this file within the current pxar directory structure.
382 pub fn path(&self) -> &Path
{
386 /// Convenience method to get just the file name portion of the current path.
388 pub fn file_name(&self) -> &OsStr
{
389 self.path
.file_name().unwrap_or_else(|| OsStr
::new(""))
392 /// Get the file metadata.
394 pub fn metadata(&self) -> &Metadata
{
398 /// Take out the metadata.
400 pub fn into_metadata(self) -> Metadata
{
404 /// Get the entry-type specific information.
405 pub fn kind(&self) -> &EntryKind
{
409 /// Get the value of the symbolic link if it is one.
410 pub fn get_symlink(&self) -> Option
<&OsStr
> {
412 EntryKind
::Symlink(link
) => Some(link
.as_ref()),
417 /// Get the value of the hard link if it is one.
418 pub fn get_hardlink(&self) -> Option
<&OsStr
> {
420 EntryKind
::Hardlink(link
) => Some(link
.as_ref()),
425 /// Get the value of the device node if it is one.
426 pub fn get_device(&self) -> Option
<format
::Device
> {
428 EntryKind
::Device(dev
) => Some(dev
.clone()),
434 /// Convenience helpers.
436 /// Check whether this is a directory.
437 pub fn is_dir(&self) -> bool
{
439 EntryKind
::Directory { .. }
=> true,
444 /// Check whether this is a symbolic link.
445 pub fn is_symlink(&self) -> bool
{
447 EntryKind
::Symlink(_
) => true,
452 /// Check whether this is a hard link.
453 pub fn is_hardlink(&self) -> bool
{
455 EntryKind
::Hardlink(_
) => true,
460 /// Check whether this is a device node.
461 pub fn is_device(&self) -> bool
{
463 EntryKind
::Device(_
) => true,
468 /// Check whether this is a regular file.
469 pub fn is_regular_file(&self) -> bool
{
471 EntryKind
::File { .. }
=> true,
476 /// Get the file size if this is a regular file, or `None`.
477 pub fn file_size(&self) -> Option
<u64> {
479 EntryKind
::File { size, .. }
=> Some(size
),