1use crate::{
2 aa_byte_stream::ArchiveFlags,
3 aa_field_key::{FieldKey, FieldKeySet},
4 ffi, util, CompressionError, Result,
5};
6use std::ffi::{c_void, CStr};
7use std::ptr::{null_mut, NonNull};
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
10pub enum HashFunction {
11 Crc32 = 1,
12 Sha1 = 2,
13 Sha256 = 3,
14 Sha384 = 4,
15 Sha512 = 5,
16}
17
18impl HashFunction {
19 pub const fn digest_len(self) -> usize {
20 match self {
21 Self::Crc32 => 4,
22 Self::Sha1 => 20,
23 Self::Sha256 => 32,
24 Self::Sha384 => 48,
25 Self::Sha512 => 64,
26 }
27 }
28
29 const fn from_raw(raw: u32) -> Option<Self> {
30 match raw {
31 1 => Some(Self::Crc32),
32 2 => Some(Self::Sha1),
33 3 => Some(Self::Sha256),
34 4 => Some(Self::Sha384),
35 5 => Some(Self::Sha512),
36 _ => None,
37 }
38 }
39
40 const fn as_raw(self) -> u32 {
41 self as u32
42 }
43}
44
45#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
46pub enum EntryType {
47 RegularFile = 'F' as isize,
48 Directory = 'D' as isize,
49 SymbolicLink = 'L' as isize,
50 Fifo = 'P' as isize,
51 CharacterDevice = 'C' as isize,
52 BlockDevice = 'B' as isize,
53 Socket = 'S' as isize,
54 Whiteout = 'W' as isize,
55 Door = 'R' as isize,
56 Port = 'T' as isize,
57 Metadata = 'M' as isize,
58}
59
60impl EntryType {
61 pub fn from_raw(raw: u32) -> Option<Self> {
62 match raw {
63 x if x == Self::RegularFile as u32 => Some(Self::RegularFile),
64 x if x == Self::Directory as u32 => Some(Self::Directory),
65 x if x == Self::SymbolicLink as u32 => Some(Self::SymbolicLink),
66 x if x == Self::Fifo as u32 => Some(Self::Fifo),
67 x if x == Self::CharacterDevice as u32 => Some(Self::CharacterDevice),
68 x if x == Self::BlockDevice as u32 => Some(Self::BlockDevice),
69 x if x == Self::Socket as u32 => Some(Self::Socket),
70 x if x == Self::Whiteout as u32 => Some(Self::Whiteout),
71 x if x == Self::Door as u32 => Some(Self::Door),
72 x if x == Self::Port as u32 => Some(Self::Port),
73 x if x == Self::Metadata as u32 => Some(Self::Metadata),
74 _ => None,
75 }
76 }
77}
78
79#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
80pub enum FieldType {
81 Flag = 0,
82 UInt = 1,
83 String = 2,
84 Hash = 3,
85 Timespec = 4,
86 Blob = 5,
87}
88
89impl FieldType {
90 fn from_raw(raw: i32) -> Option<Self> {
91 match raw {
92 0 => Some(Self::Flag),
93 1 => Some(Self::UInt),
94 2 => Some(Self::String),
95 3 => Some(Self::Hash),
96 4 => Some(Self::Timespec),
97 5 => Some(Self::Blob),
98 _ => None,
99 }
100 }
101}
102
103#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
104pub struct Timespec {
105 pub seconds: i64,
106 pub nanoseconds: i64,
107}
108
109#[derive(Clone, Debug, Eq, PartialEq)]
110pub struct HashValue {
111 pub function: HashFunction,
112 pub bytes: Vec<u8>,
113}
114
115#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
116pub struct BlobDescription {
117 pub size: u64,
118 pub offset: u64,
119}
120
121#[derive(Clone, Debug, Eq, PartialEq)]
122pub enum HeaderFieldValue {
123 Flag,
124 UInt(u64),
125 String(String),
126 Hash(HashValue),
127 Timespec(Timespec),
128 Blob(BlobDescription),
129}
130
131#[derive(Debug)]
132pub struct Header {
133 handle: NonNull<c_void>,
134}
135
136impl Header {
137 pub(crate) fn from_handle(handle: *mut c_void, operation: &'static str) -> Result<Self> {
138 Ok(Self {
139 handle: util::nonnull_handle(handle, operation)?,
140 })
141 }
142
143 pub fn new() -> Result<Self> {
144 let handle = unsafe { ffi::aa_header::compression_rs_aa_header_create() };
145 Self::from_handle(handle, "AAHeaderCreate")
146 }
147
148 pub fn from_encoded_data(data: &[u8]) -> Result<Self> {
149 let handle = unsafe {
150 ffi::aa_header::compression_rs_aa_header_create_with_encoded_data(
151 data.len(),
152 data.as_ptr(),
153 )
154 };
155 Self::from_handle(handle, "AAHeaderCreateWithEncodedData")
156 }
157
158 pub fn from_path(
159 key_set: &FieldKeySet,
160 dir: &str,
161 path: &str,
162 flags: ArchiveFlags,
163 ) -> Result<Self> {
164 let dir = util::cstring("dir", dir)?;
165 let path = util::cstring("path", path)?;
166 let handle = unsafe {
167 ffi::aa_header::compression_rs_aa_header_create_with_path(
168 key_set.as_ptr(),
169 dir.as_ptr(),
170 path.as_ptr(),
171 flags.bits(),
172 )
173 };
174 Self::from_handle(handle, "AAHeaderCreateWithPath")
175 }
176
177 pub(crate) fn as_ptr(&self) -> *mut c_void {
178 self.handle.as_ptr()
179 }
180
181 pub fn assign(&mut self, other: &Self) -> Result<()> {
182 let status = unsafe {
183 ffi::aa_header::compression_rs_aa_header_assign(self.as_ptr(), other.as_ptr())
184 };
185 util::status_result("AAHeaderAssign", status)
186 }
187
188 pub fn field_count(&self) -> u32 {
189 unsafe { ffi::aa_header::compression_rs_aa_header_get_field_count(self.as_ptr()) }
190 }
191
192 pub fn is_empty(&self) -> bool {
193 self.field_count() == 0
194 }
195
196 pub fn key_index(&self, key: FieldKey) -> Result<Option<u32>> {
197 match unsafe {
198 ffi::aa_header::compression_rs_aa_header_get_key_index(self.as_ptr(), key.raw())
199 } {
200 -1 => Ok(None),
201 value if value < -1 => Err(CompressionError::OperationFailed {
202 operation: "AAHeaderGetKeyIndex",
203 code: value,
204 }),
205 value => Ok(Some(value.unsigned_abs())),
206 }
207 }
208
209 pub fn field_type(&self, index: u32) -> Result<FieldType> {
210 let raw = unsafe {
211 ffi::aa_header::compression_rs_aa_header_get_field_type(self.as_ptr(), index)
212 };
213 if raw < 0 {
214 return Err(CompressionError::OperationFailed {
215 operation: "AAHeaderGetFieldType",
216 code: raw,
217 });
218 }
219 FieldType::from_raw(raw).ok_or(CompressionError::OperationFailed {
220 operation: "AAHeaderGetFieldType",
221 code: raw,
222 })
223 }
224
225 pub fn field_key(&self, index: u32) -> Result<FieldKey> {
226 if index >= self.field_count() {
227 return Err(CompressionError::OperationFailed {
228 operation: "AAHeaderGetFieldKey",
229 code: -1,
230 });
231 }
232
233 Ok(FieldKey::from_raw(unsafe {
234 ffi::aa_header::compression_rs_aa_header_get_field_key(self.as_ptr(), index)
235 }))
236 }
237
238 pub fn payload_size(&self) -> u64 {
239 unsafe { ffi::aa_header::compression_rs_aa_header_get_payload_size(self.as_ptr()) }
240 }
241
242 pub fn remove_field(&mut self, index: u32) -> Result<()> {
243 let status =
244 unsafe { ffi::aa_header::compression_rs_aa_header_remove_field(self.as_ptr(), index) };
245 util::status_result("AAHeaderRemoveField", status)
246 }
247
248 pub fn clear(&mut self) -> Result<()> {
249 let status = unsafe { ffi::aa_header::compression_rs_aa_header_clear(self.as_ptr()) };
250 util::status_result("AAHeaderClear", status)
251 }
252
253 pub fn set_field_flag(&mut self, index: u32, key: FieldKey) -> Result<()> {
254 let status = unsafe {
255 ffi::aa_header::compression_rs_aa_header_set_field_flag(self.as_ptr(), index, key.raw())
256 };
257 util::status_result("AAHeaderSetFieldFlag", status)
258 }
259
260 pub fn set_field_uint(&mut self, index: u32, key: FieldKey, value: u64) -> Result<()> {
261 let status = unsafe {
262 ffi::aa_header::compression_rs_aa_header_set_field_uint(
263 self.as_ptr(),
264 index,
265 key.raw(),
266 value,
267 )
268 };
269 util::status_result("AAHeaderSetFieldUInt", status)
270 }
271
272 pub fn set_field_string(&mut self, index: u32, key: FieldKey, value: &str) -> Result<()> {
273 let value = util::cstring("value", value)?;
274 let status = unsafe {
275 ffi::aa_header::compression_rs_aa_header_set_field_string(
276 self.as_ptr(),
277 index,
278 key.raw(),
279 value.as_ptr(),
280 value.as_bytes().len(),
281 )
282 };
283 util::status_result("AAHeaderSetFieldString", status)
284 }
285
286 pub fn set_field_hash(
287 &mut self,
288 index: u32,
289 key: FieldKey,
290 function: HashFunction,
291 value: &[u8],
292 ) -> Result<()> {
293 if value.len() != function.digest_len() {
294 return Err(CompressionError::InvalidHashLength {
295 expected: function.digest_len(),
296 actual: value.len(),
297 });
298 }
299 let status = unsafe {
300 ffi::aa_header::compression_rs_aa_header_set_field_hash(
301 self.as_ptr(),
302 index,
303 key.raw(),
304 function.as_raw(),
305 value.as_ptr(),
306 )
307 };
308 util::status_result("AAHeaderSetFieldHash", status)
309 }
310
311 pub fn set_field_timespec(&mut self, index: u32, key: FieldKey, value: Timespec) -> Result<()> {
312 let status = unsafe {
313 ffi::aa_header::compression_rs_aa_header_set_field_timespec(
314 self.as_ptr(),
315 index,
316 key.raw(),
317 value.seconds,
318 value.nanoseconds,
319 )
320 };
321 util::status_result("AAHeaderSetFieldTimespec", status)
322 }
323
324 pub fn set_field_blob(&mut self, index: u32, key: FieldKey, size: u64) -> Result<()> {
325 let status = unsafe {
326 ffi::aa_header::compression_rs_aa_header_set_field_blob(
327 self.as_ptr(),
328 index,
329 key.raw(),
330 size,
331 )
332 };
333 util::status_result("AAHeaderSetFieldBlob", status)
334 }
335
336 pub fn append_field_flag(&mut self, key: FieldKey) -> Result<()> {
337 self.set_field_flag(u32::MAX, key)
338 }
339
340 pub fn append_field_uint(&mut self, key: FieldKey, value: u64) -> Result<()> {
341 self.set_field_uint(u32::MAX, key, value)
342 }
343
344 pub fn append_field_string(&mut self, key: FieldKey, value: &str) -> Result<()> {
345 self.set_field_string(u32::MAX, key, value)
346 }
347
348 pub fn append_field_hash(
349 &mut self,
350 key: FieldKey,
351 function: HashFunction,
352 value: &[u8],
353 ) -> Result<()> {
354 self.set_field_hash(u32::MAX, key, function, value)
355 }
356
357 pub fn append_field_timespec(&mut self, key: FieldKey, value: Timespec) -> Result<()> {
358 self.set_field_timespec(u32::MAX, key, value)
359 }
360
361 pub fn append_field_blob(&mut self, key: FieldKey, size: u64) -> Result<()> {
362 self.set_field_blob(u32::MAX, key, size)
363 }
364
365 pub fn field_uint(&self, index: u32) -> Result<u64> {
366 let mut value = 0_u64;
367 let status = unsafe {
368 ffi::aa_header::compression_rs_aa_header_get_field_uint(
369 self.as_ptr(),
370 index,
371 &mut value,
372 )
373 };
374 util::status_result("AAHeaderGetFieldUInt", status)?;
375 Ok(value)
376 }
377
378 pub fn field_string(&self, index: u32) -> Result<String> {
379 let mut length = 0_usize;
380 let status = unsafe {
381 ffi::aa_header::compression_rs_aa_header_get_field_string(
382 self.as_ptr(),
383 index,
384 0,
385 null_mut(),
386 &mut length,
387 )
388 };
389 util::status_result("AAHeaderGetFieldString", status)?;
390
391 let mut buffer = vec![0_i8; length.saturating_add(1)];
392 let status = unsafe {
393 ffi::aa_header::compression_rs_aa_header_get_field_string(
394 self.as_ptr(),
395 index,
396 buffer.len(),
397 buffer.as_mut_ptr(),
398 &mut length,
399 )
400 };
401 util::status_result("AAHeaderGetFieldString", status)?;
402
403 let value = unsafe { CStr::from_ptr(buffer.as_ptr()) }
404 .to_str()
405 .map_err(|_| CompressionError::Utf8Error {
406 operation: "AAHeaderGetFieldString",
407 })?;
408 Ok(value.to_string())
409 }
410
411 pub fn field_hash(&self, index: u32) -> Result<HashValue> {
412 let mut function = 0_u32;
413 let mut bytes = vec![0_u8; HashFunction::Sha512.digest_len()];
414 let status = unsafe {
415 ffi::aa_header::compression_rs_aa_header_get_field_hash(
416 self.as_ptr(),
417 index,
418 bytes.len(),
419 &mut function,
420 bytes.as_mut_ptr(),
421 )
422 };
423 util::status_result("AAHeaderGetFieldHash", status)?;
424 let function =
425 HashFunction::from_raw(function).ok_or(CompressionError::OperationFailed {
426 operation: "AAHeaderGetFieldHash",
427 code: -1,
428 })?;
429 bytes.truncate(function.digest_len());
430 Ok(HashValue { function, bytes })
431 }
432
433 pub fn field_timespec(&self, index: u32) -> Result<Timespec> {
434 let mut seconds = 0_i64;
435 let mut nanoseconds = 0_i64;
436 let status = unsafe {
437 ffi::aa_header::compression_rs_aa_header_get_field_timespec(
438 self.as_ptr(),
439 index,
440 &mut seconds,
441 &mut nanoseconds,
442 )
443 };
444 util::status_result("AAHeaderGetFieldTimespec", status)?;
445 Ok(Timespec {
446 seconds,
447 nanoseconds,
448 })
449 }
450
451 pub fn field_blob(&self, index: u32) -> Result<BlobDescription> {
452 let mut size = 0_u64;
453 let mut offset = 0_u64;
454 let status = unsafe {
455 ffi::aa_header::compression_rs_aa_header_get_field_blob(
456 self.as_ptr(),
457 index,
458 &mut size,
459 &mut offset,
460 )
461 };
462 util::status_result("AAHeaderGetFieldBlob", status)?;
463 Ok(BlobDescription { size, offset })
464 }
465
466 pub fn field_value(&self, index: u32) -> Result<HeaderFieldValue> {
467 match self.field_type(index)? {
468 FieldType::Flag => Ok(HeaderFieldValue::Flag),
469 FieldType::UInt => self.field_uint(index).map(HeaderFieldValue::UInt),
470 FieldType::String => self.field_string(index).map(HeaderFieldValue::String),
471 FieldType::Hash => self.field_hash(index).map(HeaderFieldValue::Hash),
472 FieldType::Timespec => self.field_timespec(index).map(HeaderFieldValue::Timespec),
473 FieldType::Blob => self.field_blob(index).map(HeaderFieldValue::Blob),
474 }
475 }
476
477 pub fn uint_with_key(&self, key: FieldKey) -> Result<Option<u64>> {
478 self.key_index(key)?
479 .map(|index| self.field_uint(index))
480 .transpose()
481 }
482
483 pub fn string_with_key(&self, key: FieldKey) -> Result<Option<String>> {
484 self.key_index(key)?
485 .map(|index| self.field_string(index))
486 .transpose()
487 }
488
489 pub fn hash_with_key(&self, key: FieldKey) -> Result<Option<HashValue>> {
490 self.key_index(key)?
491 .map(|index| self.field_hash(index))
492 .transpose()
493 }
494
495 pub fn timespec_with_key(&self, key: FieldKey) -> Result<Option<Timespec>> {
496 self.key_index(key)?
497 .map(|index| self.field_timespec(index))
498 .transpose()
499 }
500
501 pub fn blob_with_key(&self, key: FieldKey) -> Result<Option<BlobDescription>> {
502 self.key_index(key)?
503 .map(|index| self.field_blob(index))
504 .transpose()
505 }
506
507 pub fn value_with_key(&self, key: FieldKey) -> Result<Option<HeaderFieldValue>> {
508 self.key_index(key)?
509 .map(|index| self.field_value(index))
510 .transpose()
511 }
512
513 pub fn entry_type(&self) -> Result<Option<EntryType>> {
514 Ok(self
515 .uint_with_key(FieldKey::TYP)?
516 .and_then(|raw| u32::try_from(raw).ok())
517 .and_then(EntryType::from_raw))
518 }
519
520 pub fn path(&self) -> Result<Option<String>> {
521 self.string_with_key(FieldKey::PAT)
522 }
523
524 pub fn encoded_size(&self) -> usize {
525 unsafe { ffi::aa_header::compression_rs_aa_header_get_encoded_size(self.as_ptr()) }
526 }
527
528 pub fn encoded_data(&self) -> Result<Vec<u8>> {
529 let size = self.encoded_size();
530 let mut data = vec![0_u8; size];
531 if size == 0 {
532 return Ok(data);
533 }
534 let copied = unsafe {
535 ffi::aa_header::compression_rs_aa_header_copy_encoded_data(
536 self.as_ptr(),
537 data.as_mut_ptr(),
538 )
539 };
540 if copied {
541 Ok(data)
542 } else {
543 Err(CompressionError::OperationFailed {
544 operation: "AAHeaderGetEncodedData",
545 code: -1,
546 })
547 }
548 }
549}
550
551impl Clone for Header {
552 fn clone(&self) -> Self {
553 let handle = unsafe { ffi::aa_header::compression_rs_aa_header_clone(self.as_ptr()) };
554 Self::from_handle(handle, "AAHeaderClone").expect("AAHeaderClone returned null")
555 }
556}
557
558impl Drop for Header {
559 fn drop(&mut self) {
560 unsafe { ffi::aa_header::compression_rs_aa_header_release(self.as_ptr()) };
561 }
562}