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
6 //! `PxarHeader`, followed by the item data.
7 use std
::cmp
::Ordering
;
9 use endian_trait
::Endian
;
10 use anyhow
::{bail, Error}
;
11 use siphasher
::sip
::SipHasher24
;
14 /// Header types identifying items stored in the archive
15 pub const PXAR_ENTRY
: u64 = 0x1396fabcea5bbb51;
16 pub const PXAR_FILENAME
: u64 = 0x6dbb6ebcb3161f0b;
17 pub const PXAR_SYMLINK
: u64 = 0x664a6fb6830e0d6c;
18 pub const PXAR_DEVICE
: u64 = 0xac3dace369dfe643;
19 pub const PXAR_XATTR
: u64 = 0xb8157091f80bc486;
20 pub const PXAR_ACL_USER
: u64 = 0x297dc88b2ef12faf;
21 pub const PXAR_ACL_GROUP
: u64 = 0x36f2acb56cb3dd0b;
22 pub const PXAR_ACL_GROUP_OBJ
: u64 = 0x23047110441f38f3;
23 pub const PXAR_ACL_DEFAULT
: u64 = 0xfe3eeda6823c8cd0;
24 pub const PXAR_ACL_DEFAULT_USER
: u64 = 0xbdf03df9bd010a91;
25 pub const PXAR_ACL_DEFAULT_GROUP
: u64 = 0xa0cb1168782d1f51;
26 pub const PXAR_FCAPS
: u64 = 0xf7267db0afed0629;
27 pub const PXAR_QUOTA_PROJID
: u64 = 0x161baf2d8772a72b;
29 /// Marks item as hardlink
30 /// compute_goodbye_hash(b"__PROXMOX_FORMAT_HARDLINK__");
31 pub const PXAR_FORMAT_HARDLINK
: u64 = 0x2c5e06f634f65b86;
32 /// Marks the beginning of the payload (actual content) of regular files
33 pub const PXAR_PAYLOAD
: u64 = 0x8b9e1d93d6dcffc9;
34 /// Marks item as entry of goodbye table
35 pub const PXAR_GOODBYE
: u64 = 0xdfd35c5e8327c403;
36 /// The end marker used in the GOODBYE object
37 pub const PXAR_GOODBYE_TAIL_MARKER
: u64 = 0x57446fa533702943;
39 #[derive(Debug, Endian)]
41 pub struct PxarHeader
{
42 /// The item type (see `PXAR_` constants).
44 /// The size of the item, including the size of `PxarHeader`.
50 pub struct PxarEntry
{
60 pub struct PxarDevice
{
67 pub struct PxarGoodbyeItem
{
68 /// SipHash24 of the directory item name. The last GOODBYE item
69 /// uses the special hash value `PXAR_GOODBYE_TAIL_MARKER`.
71 /// The offset from the start of the GOODBYE object to the start
72 /// of the matching directory item (point to a FILENAME). The last
73 /// GOODBYE item points to the start of the matching ENTRY
76 /// The overall size of the directory item. The last GOODBYE item
77 /// repeats the size of the GOODBYE item.
81 /// Helper function to extract file names from binary archive.
82 pub fn read_os_string(buffer
: &[u8]) -> std
::ffi
::OsString
{
83 let len
= buffer
.len();
85 use std
::os
::unix
::ffi
::OsStrExt
;
87 let name
= if len
> 0 && buffer
[len
- 1] == 0 {
88 std
::ffi
::OsStr
::from_bytes(&buffer
[0..len
- 1])
90 std
::ffi
::OsStr
::from_bytes(&buffer
)
98 pub struct PxarXAttr
{
103 impl Ord
for PxarXAttr
{
104 fn cmp(&self, other
: &PxarXAttr
) -> Ordering
{
105 self.name
.cmp(&other
.name
)
109 impl PartialOrd
for PxarXAttr
{
110 fn partial_cmp(&self, other
: &PxarXAttr
) -> Option
<Ordering
> {
111 Some(self.cmp(other
))
115 impl PartialEq
for PxarXAttr
{
116 fn eq(&self, other
: &PxarXAttr
) -> bool
{
117 self.name
== other
.name
123 pub struct PxarFCaps
{
127 #[derive(Debug, Endian, Eq)]
129 pub struct PxarACLUser
{
131 pub permissions
: u64,
132 //pub name: Vec<u64>, not impl for now
135 // TODO if also name is impl, sort by uid, then by name and last by permissions
136 impl Ord
for PxarACLUser
{
137 fn cmp(&self, other
: &PxarACLUser
) -> Ordering
{
138 match self.uid
.cmp(&other
.uid
) {
139 // uids are equal, entries ordered by permissions
140 Ordering
::Equal
=> self.permissions
.cmp(&other
.permissions
),
141 // uids are different, entries ordered by uid
142 uid_order
=> uid_order
,
147 impl PartialOrd
for PxarACLUser
{
148 fn partial_cmp(&self, other
: &PxarACLUser
) -> Option
<Ordering
> {
149 Some(self.cmp(other
))
153 impl PartialEq
for PxarACLUser
{
154 fn eq(&self, other
: &PxarACLUser
) -> bool
{
155 self.uid
== other
.uid
&& self.permissions
== other
.permissions
159 #[derive(Debug, Endian, Eq)]
161 pub struct PxarACLGroup
{
163 pub permissions
: u64,
164 //pub name: Vec<u64>, not impl for now
167 // TODO if also name is impl, sort by gid, then by name and last by permissions
168 impl Ord
for PxarACLGroup
{
169 fn cmp(&self, other
: &PxarACLGroup
) -> Ordering
{
170 match self.gid
.cmp(&other
.gid
) {
171 // gids are equal, entries are ordered by permissions
172 Ordering
::Equal
=> self.permissions
.cmp(&other
.permissions
),
173 // gids are different, entries ordered by gid
174 gid_ordering
=> gid_ordering
,
179 impl PartialOrd
for PxarACLGroup
{
180 fn partial_cmp(&self, other
: &PxarACLGroup
) -> Option
<Ordering
> {
181 Some(self.cmp(other
))
185 impl PartialEq
for PxarACLGroup
{
186 fn eq(&self, other
: &PxarACLGroup
) -> bool
{
187 self.gid
== other
.gid
&& self.permissions
== other
.permissions
191 #[derive(Debug, Endian)]
193 pub struct PxarACLGroupObj
{
194 pub permissions
: u64,
197 #[derive(Debug, Endian)]
199 pub struct PxarACLDefault
{
200 pub user_obj_permissions
: u64,
201 pub group_obj_permissions
: u64,
202 pub other_permissions
: u64,
203 pub mask_permissions
: u64,
206 pub(crate) struct PxarACL
{
207 pub users
: Vec
<PxarACLUser
>,
208 pub groups
: Vec
<PxarACLGroup
>,
209 pub group_obj
: Option
<PxarACLGroupObj
>,
210 pub default: Option
<PxarACLDefault
>,
213 pub const PXAR_ACL_PERMISSION_READ
: u64 = 4;
214 pub const PXAR_ACL_PERMISSION_WRITE
: u64 = 2;
215 pub const PXAR_ACL_PERMISSION_EXECUTE
: u64 = 1;
217 #[derive(Debug, Endian)]
219 pub struct PxarQuotaProjID
{
223 #[derive(Debug, Default)]
224 pub struct PxarAttributes
{
225 pub xattrs
: Vec
<PxarXAttr
>,
226 pub fcaps
: Option
<PxarFCaps
>,
227 pub quota_projid
: Option
<PxarQuotaProjID
>,
228 pub acl_user
: Vec
<PxarACLUser
>,
229 pub acl_group
: Vec
<PxarACLGroup
>,
230 pub acl_group_obj
: Option
<PxarACLGroupObj
>,
231 pub acl_default
: Option
<PxarACLDefault
>,
232 pub acl_default_user
: Vec
<PxarACLUser
>,
233 pub acl_default_group
: Vec
<PxarACLGroup
>,
236 /// Create SipHash values for goodby tables.
237 //pub fn compute_goodbye_hash(name: &std::ffi::CStr) -> u64 {
238 pub fn compute_goodbye_hash(name
: &[u8]) -> u64 {
239 use std
::hash
::Hasher
;
240 let mut hasher
= SipHasher24
::new_with_keys(0x8574442b0f1d84b3, 0x2736ed30d1c22ec1);
245 pub fn check_ca_header
<T
>(head
: &PxarHeader
, htype
: u64) -> Result
<(), Error
> {
246 if head
.htype
!= htype
{
248 "got wrong header type ({:016x} != {:016x})",
253 if head
.size
!= (std
::mem
::size_of
::<T
>() + std
::mem
::size_of
::<PxarHeader
>()) as u64 {
254 bail
!("got wrong header size for type {:016x}", htype
);
260 /// The format requires to build sorted directory lookup tables in
261 /// memory, so we restrict the number of allowed entries to limit
262 /// maximum memory usage.
263 pub const ENCODER_MAX_ENTRIES
: usize = 1024 * 1024;