]>
Commit | Line | Data |
---|---|---|
8968258b | 1 | //! *pxar* binary format definition |
4fa71e05 DM |
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 | |
5e50c606 | 6 | //! `PxarHeader`, followed by the item data. |
de2016d5 | 7 | use std::cmp::Ordering; |
b62b6cad | 8 | |
4ea22b68 | 9 | use endian_trait::Endian; |
f7d4e4b5 | 10 | use anyhow::{bail, Error}; |
0866748d DM |
11 | use siphasher::sip::SipHasher24; |
12 | ||
834a2f95 | 13 | |
5e50c606 | 14 | /// Header types identifying items stored in the archive |
4ea22b68 CE |
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; | |
5e50c606 CE |
28 | |
29 | /// Marks item as hardlink | |
30 | /// compute_goodbye_hash(b"__PROXMOX_FORMAT_HARDLINK__"); | |
4ea22b68 | 31 | pub const PXAR_FORMAT_HARDLINK: u64 = 0x2c5e06f634f65b86; |
add5861e | 32 | /// Marks the beginning of the payload (actual content) of regular files |
4ea22b68 | 33 | pub const PXAR_PAYLOAD: u64 = 0x8b9e1d93d6dcffc9; |
5e50c606 | 34 | /// Marks item as entry of goodbye table |
4ea22b68 | 35 | pub const PXAR_GOODBYE: u64 = 0xdfd35c5e8327c403; |
5e50c606 CE |
36 | /// The end marker used in the GOODBYE object |
37 | pub const PXAR_GOODBYE_TAIL_MARKER: u64 = 0x57446fa533702943; | |
4f6892eb | 38 | |
4ea22b68 | 39 | #[derive(Debug, Endian)] |
b62b6cad | 40 | #[repr(C)] |
5e50c606 | 41 | pub struct PxarHeader { |
5e50c606 | 42 | /// The item type (see `PXAR_` constants). |
3192ae96 | 43 | pub htype: u64, |
f92e8266 CE |
44 | /// The size of the item, including the size of `PxarHeader`. |
45 | pub size: u64, | |
b62b6cad DM |
46 | } |
47 | ||
8c1dfa6c | 48 | #[derive(Endian)] |
b62b6cad | 49 | #[repr(C)] |
5e50c606 | 50 | pub struct PxarEntry { |
3192ae96 DM |
51 | pub mode: u64, |
52 | pub flags: u64, | |
ead7546a CE |
53 | pub uid: u32, |
54 | pub gid: u32, | |
3192ae96 | 55 | pub mtime: u64, |
b62b6cad DM |
56 | } |
57 | ||
a7e37131 DM |
58 | #[derive(Endian)] |
59 | #[repr(C)] | |
5e50c606 | 60 | pub struct PxarDevice { |
a7e37131 DM |
61 | pub major: u64, |
62 | pub minor: u64, | |
63 | } | |
64 | ||
b6ebfb8d | 65 | #[derive(Endian)] |
b62b6cad | 66 | #[repr(C)] |
5e50c606 | 67 | pub struct PxarGoodbyeItem { |
f92e8266 CE |
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, | |
4fa71e05 DM |
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 | |
4b864ad4 | 74 | /// object. |
3192ae96 | 75 | pub offset: u64, |
4fa71e05 DM |
76 | /// The overall size of the directory item. The last GOODBYE item |
77 | /// repeats the size of the GOODBYE item. | |
3192ae96 | 78 | pub size: u64, |
b62b6cad DM |
79 | } |
80 | ||
0866748d DM |
81 | /// Helper function to extract file names from binary archive. |
82 | pub fn read_os_string(buffer: &[u8]) -> std::ffi::OsString { | |
b62b6cad DM |
83 | let len = buffer.len(); |
84 | ||
85 | use std::os::unix::ffi::OsStrExt; | |
86 | ||
4ea22b68 CE |
87 | let name = if len > 0 && buffer[len - 1] == 0 { |
88 | std::ffi::OsStr::from_bytes(&buffer[0..len - 1]) | |
b62b6cad DM |
89 | } else { |
90 | std::ffi::OsStr::from_bytes(&buffer) | |
91 | }; | |
92 | ||
93 | name.into() | |
94 | } | |
0866748d | 95 | |
de2016d5 CE |
96 | #[derive(Debug, Eq)] |
97 | #[repr(C)] | |
5e50c606 | 98 | pub struct PxarXAttr { |
de2016d5 CE |
99 | pub name: Vec<u8>, |
100 | pub value: Vec<u8>, | |
101 | } | |
102 | ||
5e50c606 CE |
103 | impl Ord for PxarXAttr { |
104 | fn cmp(&self, other: &PxarXAttr) -> Ordering { | |
de2016d5 CE |
105 | self.name.cmp(&other.name) |
106 | } | |
107 | } | |
108 | ||
5e50c606 CE |
109 | impl PartialOrd for PxarXAttr { |
110 | fn partial_cmp(&self, other: &PxarXAttr) -> Option<Ordering> { | |
de2016d5 CE |
111 | Some(self.cmp(other)) |
112 | } | |
113 | } | |
114 | ||
5e50c606 CE |
115 | impl PartialEq for PxarXAttr { |
116 | fn eq(&self, other: &PxarXAttr) -> bool { | |
de2016d5 CE |
117 | self.name == other.name |
118 | } | |
119 | } | |
120 | ||
121 | #[derive(Debug)] | |
122 | #[repr(C)] | |
5e50c606 | 123 | pub struct PxarFCaps { |
de2016d5 CE |
124 | pub data: Vec<u8>, |
125 | } | |
126 | ||
7cfaade7 CE |
127 | #[derive(Debug, Endian, Eq)] |
128 | #[repr(C)] | |
5e50c606 | 129 | pub struct PxarACLUser { |
7cfaade7 CE |
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 | |
5e50c606 CE |
136 | impl Ord for PxarACLUser { |
137 | fn cmp(&self, other: &PxarACLUser) -> Ordering { | |
7cfaade7 CE |
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 | ||
5e50c606 CE |
147 | impl PartialOrd for PxarACLUser { |
148 | fn partial_cmp(&self, other: &PxarACLUser) -> Option<Ordering> { | |
7cfaade7 CE |
149 | Some(self.cmp(other)) |
150 | } | |
151 | } | |
152 | ||
5e50c606 CE |
153 | impl PartialEq for PxarACLUser { |
154 | fn eq(&self, other: &PxarACLUser) -> bool { | |
7cfaade7 CE |
155 | self.uid == other.uid && self.permissions == other.permissions |
156 | } | |
157 | } | |
158 | ||
159 | #[derive(Debug, Endian, Eq)] | |
160 | #[repr(C)] | |
5e50c606 | 161 | pub struct PxarACLGroup { |
7cfaade7 CE |
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 | |
5e50c606 CE |
168 | impl Ord for PxarACLGroup { |
169 | fn cmp(&self, other: &PxarACLGroup) -> Ordering { | |
7cfaade7 CE |
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 | ||
5e50c606 CE |
179 | impl PartialOrd for PxarACLGroup { |
180 | fn partial_cmp(&self, other: &PxarACLGroup) -> Option<Ordering> { | |
7cfaade7 CE |
181 | Some(self.cmp(other)) |
182 | } | |
183 | } | |
184 | ||
5e50c606 CE |
185 | impl PartialEq for PxarACLGroup { |
186 | fn eq(&self, other: &PxarACLGroup) -> bool { | |
7cfaade7 CE |
187 | self.gid == other.gid && self.permissions == other.permissions |
188 | } | |
189 | } | |
190 | ||
191 | #[derive(Debug, Endian)] | |
192 | #[repr(C)] | |
5e50c606 | 193 | pub struct PxarACLGroupObj { |
7cfaade7 CE |
194 | pub permissions: u64, |
195 | } | |
196 | ||
197 | #[derive(Debug, Endian)] | |
198 | #[repr(C)] | |
5e50c606 | 199 | pub struct PxarACLDefault { |
7cfaade7 CE |
200 | pub user_obj_permissions: u64, |
201 | pub group_obj_permissions: u64, | |
202 | pub other_permissions: u64, | |
203 | pub mask_permissions: u64, | |
204 | } | |
205 | ||
4ea22b68 | 206 | pub(crate) struct PxarACL { |
5e50c606 CE |
207 | pub users: Vec<PxarACLUser>, |
208 | pub groups: Vec<PxarACLGroup>, | |
209 | pub group_obj: Option<PxarACLGroupObj>, | |
210 | pub default: Option<PxarACLDefault>, | |
7cfaade7 CE |
211 | } |
212 | ||
5e50c606 CE |
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; | |
7cfaade7 | 216 | |
7312ab9e CE |
217 | #[derive(Debug, Endian)] |
218 | #[repr(C)] | |
5e50c606 | 219 | pub struct PxarQuotaProjID { |
7312ab9e CE |
220 | pub projid: u64, |
221 | } | |
222 | ||
27e6e180 | 223 | #[derive(Debug, Default)] |
e60cfbcd | 224 | pub struct PxarAttributes { |
5e50c606 CE |
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>, | |
e60cfbcd CE |
234 | } |
235 | ||
0866748d DM |
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 { | |
0866748d DM |
239 | use std::hash::Hasher; |
240 | let mut hasher = SipHasher24::new_with_keys(0x8574442b0f1d84b3, 0x2736ed30d1c22ec1); | |
241 | hasher.write(name); | |
242 | hasher.finish() | |
243 | } | |
b6ebfb8d | 244 | |
5e50c606 | 245 | pub fn check_ca_header<T>(head: &PxarHeader, htype: u64) -> Result<(), Error> { |
b6ebfb8d | 246 | if head.htype != htype { |
4ea22b68 CE |
247 | bail!( |
248 | "got wrong header type ({:016x} != {:016x})", | |
249 | head.htype, | |
250 | htype | |
251 | ); | |
b6ebfb8d | 252 | } |
5e50c606 | 253 | if head.size != (std::mem::size_of::<T>() + std::mem::size_of::<PxarHeader>()) as u64 { |
b6ebfb8d DM |
254 | bail!("got wrong header size for type {:016x}", htype); |
255 | } | |
256 | ||
257 | Ok(()) | |
258 | } | |
6fc053ed CE |
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; |