]> git.proxmox.com Git - proxmox-backup.git/blob - src/pxar/format_definition.rs
206d3beba2a1f925ab48548f4ee58e5ad97516b4
[proxmox-backup.git] / src / pxar / format_definition.rs
1 //! *pxar* binary format definition
2 //!
3 //! Please note the all values are stored in little endian ordering.
4 //!
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;
8
9 use endian_trait::Endian;
10 use anyhow::{bail, Error};
11 use siphasher::sip::SipHasher24;
12
13
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;
28
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;
38
39 #[derive(Debug, Endian)]
40 #[repr(C)]
41 pub struct PxarHeader {
42 /// The item type (see `PXAR_` constants).
43 pub htype: u64,
44 /// The size of the item, including the size of `PxarHeader`.
45 pub size: u64,
46 }
47
48 #[derive(Endian)]
49 #[repr(C)]
50 pub struct PxarEntry {
51 pub mode: u64,
52 pub flags: u64,
53 pub uid: u32,
54 pub gid: u32,
55 pub mtime: u64,
56 }
57
58 #[derive(Endian)]
59 #[repr(C)]
60 pub struct PxarDevice {
61 pub major: u64,
62 pub minor: u64,
63 }
64
65 #[derive(Endian)]
66 #[repr(C)]
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`.
70 pub hash: u64,
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
74 /// object.
75 pub offset: u64,
76 /// The overall size of the directory item. The last GOODBYE item
77 /// repeats the size of the GOODBYE item.
78 pub size: u64,
79 }
80
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();
84
85 use std::os::unix::ffi::OsStrExt;
86
87 let name = if len > 0 && buffer[len - 1] == 0 {
88 std::ffi::OsStr::from_bytes(&buffer[0..len - 1])
89 } else {
90 std::ffi::OsStr::from_bytes(&buffer)
91 };
92
93 name.into()
94 }
95
96 #[derive(Debug, Eq)]
97 #[repr(C)]
98 pub struct PxarXAttr {
99 pub name: Vec<u8>,
100 pub value: Vec<u8>,
101 }
102
103 impl Ord for PxarXAttr {
104 fn cmp(&self, other: &PxarXAttr) -> Ordering {
105 self.name.cmp(&other.name)
106 }
107 }
108
109 impl PartialOrd for PxarXAttr {
110 fn partial_cmp(&self, other: &PxarXAttr) -> Option<Ordering> {
111 Some(self.cmp(other))
112 }
113 }
114
115 impl PartialEq for PxarXAttr {
116 fn eq(&self, other: &PxarXAttr) -> bool {
117 self.name == other.name
118 }
119 }
120
121 #[derive(Debug)]
122 #[repr(C)]
123 pub struct PxarFCaps {
124 pub data: Vec<u8>,
125 }
126
127 #[derive(Debug, Endian, Eq)]
128 #[repr(C)]
129 pub struct PxarACLUser {
130 pub uid: u64,
131 pub permissions: u64,
132 //pub name: Vec<u64>, not impl for now
133 }
134
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,
143 }
144 }
145 }
146
147 impl PartialOrd for PxarACLUser {
148 fn partial_cmp(&self, other: &PxarACLUser) -> Option<Ordering> {
149 Some(self.cmp(other))
150 }
151 }
152
153 impl PartialEq for PxarACLUser {
154 fn eq(&self, other: &PxarACLUser) -> bool {
155 self.uid == other.uid && self.permissions == other.permissions
156 }
157 }
158
159 #[derive(Debug, Endian, Eq)]
160 #[repr(C)]
161 pub struct PxarACLGroup {
162 pub gid: u64,
163 pub permissions: u64,
164 //pub name: Vec<u64>, not impl for now
165 }
166
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,
175 }
176 }
177 }
178
179 impl PartialOrd for PxarACLGroup {
180 fn partial_cmp(&self, other: &PxarACLGroup) -> Option<Ordering> {
181 Some(self.cmp(other))
182 }
183 }
184
185 impl PartialEq for PxarACLGroup {
186 fn eq(&self, other: &PxarACLGroup) -> bool {
187 self.gid == other.gid && self.permissions == other.permissions
188 }
189 }
190
191 #[derive(Debug, Endian)]
192 #[repr(C)]
193 pub struct PxarACLGroupObj {
194 pub permissions: u64,
195 }
196
197 #[derive(Debug, Endian)]
198 #[repr(C)]
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,
204 }
205
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>,
211 }
212
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;
216
217 #[derive(Debug, Endian)]
218 #[repr(C)]
219 pub struct PxarQuotaProjID {
220 pub projid: u64,
221 }
222
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>,
234 }
235
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);
241 hasher.write(name);
242 hasher.finish()
243 }
244
245 pub fn check_ca_header<T>(head: &PxarHeader, htype: u64) -> Result<(), Error> {
246 if head.htype != htype {
247 bail!(
248 "got wrong header type ({:016x} != {:016x})",
249 head.htype,
250 htype
251 );
252 }
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);
255 }
256
257 Ok(())
258 }
259
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;