1 //! *pxar* binary format definition
3 //! Please note the all values are stored in little endian ordering.
5 //! The Archive contains a list of items. Each item starts with a `Header`, followed by the
8 use std
::cmp
::Ordering
;
9 use std
::ffi
::{CStr, OsStr}
;
11 use std
::mem
::size_of
;
12 use std
::os
::unix
::ffi
::OsStrExt
;
15 use endian_trait
::Endian
;
16 use siphasher
::sip
::SipHasher24
;
20 /// While these constants correspond to `libc::S_` constants, we need these to be fixed for the
21 /// format itself, so we redefine them here.
23 /// Additionally this gets rid of a bunch of casts between u32 and u64.
25 /// You can usually find the values for these in `/usr/include/linux/stat.h`.
28 pub const IFMT
: u64 = 0o0170000;
30 pub const IFSOCK
: u64 = 0o0140000;
31 pub const IFLNK
: u64 = 0o0120000;
32 pub const IFREG
: u64 = 0o0100000;
33 pub const IFBLK
: u64 = 0o0060000;
34 pub const IFDIR
: u64 = 0o0040000;
35 pub const IFCHR
: u64 = 0o0020000;
36 pub const IFIFO
: u64 = 0o0010000;
38 pub const ISUID
: u64 = 0o0004000;
39 pub const ISGID
: u64 = 0o0002000;
40 pub const ISVTX
: u64 = 0o0001000;
43 pub const PXAR_ENTRY
: u64 = 0x1396fabcea5bbb51;
44 pub const PXAR_FILENAME
: u64 = 0x6dbb6ebcb3161f0b;
45 pub const PXAR_SYMLINK
: u64 = 0x664a6fb6830e0d6c;
46 pub const PXAR_DEVICE
: u64 = 0xac3dace369dfe643;
47 pub const PXAR_XATTR
: u64 = 0xb8157091f80bc486;
48 pub const PXAR_ACL_USER
: u64 = 0x297dc88b2ef12faf;
49 pub const PXAR_ACL_GROUP
: u64 = 0x36f2acb56cb3dd0b;
50 pub const PXAR_ACL_GROUP_OBJ
: u64 = 0x23047110441f38f3;
51 pub const PXAR_ACL_DEFAULT
: u64 = 0xfe3eeda6823c8cd0;
52 pub const PXAR_ACL_DEFAULT_USER
: u64 = 0xbdf03df9bd010a91;
53 pub const PXAR_ACL_DEFAULT_GROUP
: u64 = 0xa0cb1168782d1f51;
54 pub const PXAR_FCAPS
: u64 = 0xf7267db0afed0629;
55 pub const PXAR_QUOTA_PROJID
: u64 = 0x161baf2d8772a72b;
57 /// Marks item as hardlink
58 /// compute_goodbye_hash(b"__PROXMOX_FORMAT_HARDLINK__");
59 pub const PXAR_HARDLINK
: u64 = 0x2c5e06f634f65b86;
60 /// Marks the beginnig of the payload (actual content) of regular files
61 pub const PXAR_PAYLOAD
: u64 = 0x8b9e1d93d6dcffc9;
62 /// Marks item as entry of goodbye table
63 pub const PXAR_GOODBYE
: u64 = 0xdfd35c5e8327c403;
64 /// The end marker used in the GOODBYE object
65 pub const PXAR_GOODBYE_TAIL_MARKER
: u64 = 0x57446fa533702943;
67 #[derive(Debug, Endian)]
70 /// The item type (see `PXAR_` constants).
72 /// The size of the item, including the size of `Header`.
78 pub fn with_full_size(htype
: u64, full_size
: u64) -> Self {
79 Self { htype, full_size }
83 pub fn with_content_size(htype
: u64, content_size
: u64) -> Self {
84 Self::with_full_size(htype
, content_size
+ size_of
::<Header
>() as u64)
88 pub fn full_size(&self) -> u64 {
93 pub fn content_size(&self) -> u64 {
94 self.full_size() - (size_of
::<Self>() as u64)
98 #[derive(Clone, Debug, Default, Endian)]
108 /// Builder pattern methods.
110 pub const fn mode(self, mode
: u64) -> Self {
111 Self { mode, ..self }
114 pub const fn flags(self, flags
: u64) -> Self {
115 Self { flags, ..self }
118 pub const fn uid(self, uid
: u32) -> Self {
122 pub const fn gid(self, gid
: u32) -> Self {
126 pub const fn mtime(self, mtime
: u64) -> Self {
127 Self { mtime, ..self }
130 pub const fn set_dir(self) -> Self {
131 let mode
= self.mode
;
132 self.mode((mode
& !mode
::IFMT
) | mode
::IFDIR
)
135 pub const fn set_regular_file(self) -> Self {
136 let mode
= self.mode
;
137 self.mode((mode
& !mode
::IFMT
) | mode
::IFREG
)
140 pub const fn set_symlink(self) -> Self {
141 let mode
= self.mode
;
142 self.mode((mode
& !mode
::IFMT
) | mode
::IFLNK
)
145 pub const fn set_blockdev(self) -> Self {
146 let mode
= self.mode
;
147 self.mode((mode
& !mode
::IFMT
) | mode
::IFBLK
)
150 pub const fn set_chardev(self) -> Self {
151 let mode
= self.mode
;
152 self.mode((mode
& !mode
::IFMT
) | mode
::IFCHR
)
155 pub const fn set_fifo(self) -> Self {
156 let mode
= self.mode
;
157 self.mode((mode
& !mode
::IFMT
) | mode
::IFIFO
)
161 /// Convenience accessor methods.
163 /// Get the mtime as duration since the epoch.
164 pub fn mtime_as_duration(&self) -> std
::time
::Duration
{
165 std
::time
::Duration
::from_nanos(self.mtime
)
168 /// Get the file type portion of the mode bitfield.
169 pub fn get_file_bits(&self) -> u64 {
170 self.mode
& mode
::IFMT
173 /// Get the permission portion of the mode bitfield.
174 pub fn get_permission_bits(&self) -> u64 {
175 self.mode
& !mode
::IFMT
179 /// Convenience methods.
181 /// Get the file type (`mode & mode::IFMT`).
182 pub fn file_type(&self) -> u64 {
183 self.mode
& mode
::IFMT
186 /// Get the file mode bits (`mode & !mode::IFMT`).
187 pub fn file_mode(&self) -> u64 {
188 self.mode
& !mode
::IFMT
191 /// Check whether this is a directory.
192 pub fn is_dir(&self) -> bool
{
193 (self.mode
& mode
::IFMT
) == mode
::IFDIR
196 /// Check whether this is a symbolic link.
197 pub fn is_symlink(&self) -> bool
{
198 (self.mode
& mode
::IFMT
) == mode
::IFLNK
201 /// Check whether this is a device node.
202 pub fn is_device(&self) -> bool
{
203 let fmt
= self.mode
& mode
::IFMT
;
204 fmt
== mode
::IFCHR
|| fmt
== mode
::IFBLK
207 /// Check whether this is a block device node.
208 pub fn is_blockdev(&self) -> bool
{
209 let fmt
= self.mode
& mode
::IFMT
;
213 /// Check whether this is a character device node.
214 pub fn is_chardev(&self) -> bool
{
215 let fmt
= self.mode
& mode
::IFMT
;
219 /// Check whether this is a regular file.
220 pub fn is_regular_file(&self) -> bool
{
221 (self.mode
& mode
::IFMT
) == mode
::IFREG
224 /// Check whether this is a named pipe (FIFO).
225 pub fn is_fifo(&self) -> bool
{
226 (self.mode
& mode
::IFMT
) == mode
::IFIFO
229 /// Check whether this is a named socket.
230 pub fn is_socket(&self) -> bool
{
231 (self.mode
& mode
::IFMT
) == mode
::IFSOCK
235 impl From
<&std
::fs
::Metadata
> for Entry
{
236 fn from(meta
: &std
::fs
::Metadata
) -> Entry
{
238 use std
::os
::unix
::fs
::MetadataExt
;
240 let this
= Entry
::default();
246 .mode(meta
.mode() as u64)
247 .mtime(meta
.mtime() as u64);
249 let file_type
= meta
.file_type();
250 let mode
= this
.mode
;
251 let this
= if file_type
.is_dir() {
252 this
.mode(mode
| mode
::IFDIR
)
253 } else if file_type
.is_symlink() {
254 this
.mode(mode
| mode
::IFLNK
)
256 this
.mode(mode
| mode
::IFREG
)
263 #[derive(Clone, Debug)]
264 pub struct Filename
{
268 #[derive(Clone, Debug)]
274 pub fn as_os_str(&self) -> &OsStr
{
279 impl AsRef
<[u8]> for Symlink
{
280 fn as_ref(&self) -> &[u8] {
285 impl AsRef
<OsStr
> for Symlink
{
286 fn as_ref(&self) -> &OsStr
{
287 OsStr
::from_bytes(&self.data
[..self.data
.len().max(1) - 1])
291 #[derive(Clone, Debug)]
292 pub struct Hardlink
{
298 pub fn as_os_str(&self) -> &OsStr
{
303 impl AsRef
<[u8]> for Hardlink
{
304 fn as_ref(&self) -> &[u8] {
309 impl AsRef
<OsStr
> for Hardlink
{
310 fn as_ref(&self) -> &OsStr
{
311 OsStr
::from_bytes(&self.data
[..self.data
.len().max(1) - 1])
315 #[derive(Clone, Debug, Eq)]
318 pub(crate) data
: Vec
<u8>,
319 pub(crate) name_len
: usize,
323 pub fn new
<N
: AsRef
<[u8]>, V
: AsRef
<[u8]>>(name
: N
, value
: V
) -> Self {
324 let name
= name
.as_ref();
325 let value
= value
.as_ref();
326 let mut data
= Vec
::with_capacity(name
.len() + value
.len() + 1);
332 name_len
: name
.len(),
336 pub fn name(&self) -> &CStr
{
337 unsafe { CStr::from_bytes_with_nul_unchecked(&self.data[..self.name_len + 1]) }
340 pub fn value(&self) -> &[u8] {
341 &self.data
[(self.name_len
+ 1)..]
346 fn cmp(&self, other
: &XAttr
) -> Ordering
{
347 self.name().cmp(&other
.name())
351 impl PartialOrd
for XAttr
{
352 fn partial_cmp(&self, other
: &XAttr
) -> Option
<Ordering
> {
353 Some(self.cmp(other
))
357 impl PartialEq
for XAttr
{
358 fn eq(&self, other
: &XAttr
) -> bool
{
359 self.name() == other
.name()
363 #[derive(Clone, Debug, Endian)]
370 #[cfg(target_os = "linux")]
372 /// Get a `dev_t` value for this device.
374 pub fn to_dev_t(&self) -> u64 {
375 // see bits/sysmacros.h
376 ((self.major
& 0x0000_0fff) << 8) |
377 ((self.major
& 0xffff_f000) << 32) |
378 (self.minor
& 0x0000_00ff) |
379 ((self.minor
& 0xffff_ff00) << 12)
382 /// Get a `Device` from a `dev_t` value.
384 pub fn from_dev_t(dev
: u64) -> Self {
387 major
: (dev
>> 8) & 0x0000_0fff |
388 (dev
>> 32) & 0xffff_f000,
389 minor
: dev
& 0x0000_00ff |
390 (dev
>> 12) & 0xffff_ff00,
395 #[cfg(all(test, target_os = "linux"))]
397 fn test_linux_devices() {
398 let c_dev
= unsafe { ::libc::makedev(0xabcd_1234, 0xdcba_5678) }
;
399 let dev
= Device
::from_dev_t(c_dev
);
400 assert_eq
!(dev
.to_dev_t(), c_dev
);
403 #[derive(Clone, Debug)]
409 #[derive(Clone, Copy, Debug, Endian)]
411 pub struct QuotaProjectId
{
415 #[derive(Clone, Debug, Endian)]
417 pub struct GoodbyeItem
{
418 /// SipHash24 of the directory item name. The last GOODBYE item uses the special hash value
419 /// `PXAR_GOODBYE_TAIL_MARKER`.
422 /// The offset from the start of the GOODBYE object to the start of the matching directory item
423 /// (point to a FILENAME). The last GOODBYE item points to the start of the matching ENTRY
427 /// The overall size of the directory item. This includes the FILENAME header. In other words,
428 /// `goodbye_start - offset + size` points to the end of the directory.
430 /// The last GOODBYE item repeats the size of the GOODBYE item.
435 pub fn new(name
: &[u8], offset
: u64, size
: u64) -> Self {
436 let hash
= hash_filename(name
);
437 Self { hash, offset, size }
441 pub fn hash_filename(name
: &[u8]) -> u64 {
442 use std
::hash
::Hasher
;
443 let mut hasher
= SipHasher24
::new_with_keys(0x8574442b0f1d84b3, 0x2736ed30d1c22ec1);
448 pub fn path_is_legal_component(path
: &Path
) -> bool
{
449 let mut components
= path
.components();
450 match components
.next() {
451 Some(std
::path
::Component
::Normal(_
)) => (),
454 components
.next().is_none()
457 pub fn check_file_name(path
: &Path
) -> io
::Result
<()> {
458 if !path_is_legal_component(path
) {
459 io_bail
!("invalid file name in archive: {:?}", path
);