Skip to main content

compression/
aa_entry_blob.rs

1use crate::{aa_byte_stream::ArchiveFlags, ffi, util, CompressionError, Result};
2use std::ffi::{c_void, CStr};
3use std::ptr::{null, null_mut, NonNull};
4
5/// Wraps the AppleArchive ACL tag field type.
6pub type AceTag = u32;
7/// Wraps the AppleArchive ACL permission bitset type.
8pub type AcePermSet = u64;
9/// Wraps the AppleArchive ACL flag bitset type.
10pub type AceFlagSet = u64;
11
12/// Wraps AppleArchive ACL qualifier type identifiers.
13#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
14#[repr(u32)]
15pub enum AceQualifierType {
16    /// Wraps the `User` variant of `AceQualifierType`.
17    User = 'U' as u32,
18    /// Wraps the `Group` variant of `AceQualifierType`.
19    Group = 'G' as u32,
20    /// Wraps the `Sid` variant of `AceQualifierType`.
21    Sid = 'S' as u32,
22    /// Wraps the `Uuid` variant of `AceQualifierType`.
23    Uuid = 'I' as u32,
24}
25
26impl AceQualifierType {
27    const fn from_raw(raw: u32) -> Option<Self> {
28        match raw {
29            x if x == Self::User as u32 => Some(Self::User),
30            x if x == Self::Group as u32 => Some(Self::Group),
31            x if x == Self::Sid as u32 => Some(Self::Sid),
32            x if x == Self::Uuid as u32 => Some(Self::Uuid),
33            _ => None,
34        }
35    }
36
37    const fn as_raw(self) -> u32 {
38        self as u32
39    }
40}
41
42/// Wraps an ACL entry used by `AAEntryACLBlob`.
43#[derive(Clone, Debug, Eq, PartialEq)]
44pub struct AccessControlEntry {
45    /// Wraps the `tag` field of `AccessControlEntry`.
46    pub tag: AceTag,
47    /// Wraps the `perms` field of `AccessControlEntry`.
48    pub perms: AcePermSet,
49    /// Wraps the `flags` field of `AccessControlEntry`.
50    pub flags: AceFlagSet,
51    /// Wraps the `qualifier_type` field of `AccessControlEntry`.
52    pub qualifier_type: AceQualifierType,
53    /// Wraps the `qualifier` field of `AccessControlEntry`.
54    pub qualifier: Vec<u8>,
55}
56
57/// Wraps a named blob entry used by `AAEntryXATBlob` and `AEAAuthData`.
58#[derive(Clone, Debug, Eq, PartialEq)]
59pub struct NamedBlobEntry {
60    /// Wraps the `key` field of `NamedBlobEntry`.
61    pub key: String,
62    /// Wraps the `value` field of `NamedBlobEntry`.
63    pub value: Vec<u8>,
64}
65
66/// Wraps an `AAEntryACLBlob` handle.
67#[derive(Debug)]
68pub struct EntryAclBlob {
69    handle: NonNull<c_void>,
70}
71
72impl EntryAclBlob {
73    fn from_handle(handle: *mut c_void, operation: &'static str) -> Result<Self> {
74        Ok(Self {
75            handle: util::nonnull_handle(handle, operation)?,
76        })
77    }
78
79    pub(crate) fn clone_from_raw(raw: *mut c_void) -> Result<Self> {
80        let handle =
81            unsafe { ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_clone_from_raw(raw) };
82        Self::from_handle(handle, "AAEntryACLBlobClone")
83    }
84
85    pub(crate) fn sync_into_raw(raw: *mut c_void, value: &Self) -> Result<()> {
86        let status = unsafe { ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_clear_raw(raw) };
87        util::status_result("AAEntryACLBlobClear", status)?;
88        for entry in value.entries()? {
89            let qualifier_ptr = if entry.qualifier.is_empty() {
90                null()
91            } else {
92                entry.qualifier.as_ptr()
93            };
94            let status = unsafe {
95                ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_append_entry_raw(
96                    raw,
97                    entry.tag,
98                    entry.perms,
99                    entry.flags,
100                    entry.qualifier_type.as_raw(),
101                    qualifier_ptr,
102                    entry.qualifier.len(),
103                )
104            };
105            util::status_result("AAEntryACLBlobAppendEntry", status)?;
106        }
107        Ok(())
108    }
109
110    /// Wraps `AAEntryACLBlobCreate`.
111    pub fn new() -> Result<Self> {
112        let handle = unsafe { ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_create() };
113        Self::from_handle(handle, "AAEntryACLBlobCreate")
114    }
115
116    /// Wraps `AAEntryACLBlobCreateWithEncodedData`.
117    pub fn from_encoded_data(data: &[u8]) -> Result<Self> {
118        let handle = unsafe {
119            ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_create_with_encoded_data(
120                data.as_ptr(),
121                data.len(),
122            )
123        };
124        Self::from_handle(handle, "AAEntryACLBlobCreateWithEncodedData")
125    }
126
127    /// Wraps `AAEntryACLBlobCreateWithPath`.
128    pub fn from_path(dir: &str, path: &str, flags: ArchiveFlags) -> Result<Self> {
129        let dir = util::cstring("dir", dir)?;
130        let path = util::cstring("path", path)?;
131        let handle = unsafe {
132            ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_create_with_path(
133                dir.as_ptr(),
134                path.as_ptr(),
135                flags.bits(),
136            )
137        };
138        Self::from_handle(handle, "AAEntryACLBlobCreateWithPath")
139    }
140
141    pub(crate) fn as_ptr(&self) -> *mut c_void {
142        self.handle.as_ptr()
143    }
144
145    /// Wraps `AAEntryACLBlobApplyToPath`.
146    pub fn apply_to_path(&self, dir: &str, path: &str, flags: ArchiveFlags) -> Result<()> {
147        let dir = util::cstring("dir", dir)?;
148        let path = util::cstring("path", path)?;
149        let status = unsafe {
150            ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_apply_to_path(
151                self.as_ptr(),
152                dir.as_ptr(),
153                path.as_ptr(),
154                flags.bits(),
155            )
156        };
157        util::status_result("AAEntryACLBlobApplyToPath", status)
158    }
159
160    /// Wraps `AAEntryACLBlobGetEntry`.
161    pub fn entry_count(&self) -> u32 {
162        unsafe {
163            ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_get_entry_count(self.as_ptr())
164        }
165    }
166
167    /// Wraps `AAEntryACLBlobGetEntry`.
168    pub fn is_empty(&self) -> bool {
169        self.entry_count() == 0
170    }
171
172    /// Wraps `AAEntryACLBlobGetEntry`.
173    pub fn entry(&self, index: u32) -> Result<AccessControlEntry> {
174        let mut tag = 0_u32;
175        let mut perms = 0_u64;
176        let mut flags = 0_u64;
177        let mut qualifier_type = 0_u32;
178        let mut qualifier_size = 0_usize;
179        let status = unsafe {
180            ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_get_entry(
181                self.as_ptr(),
182                index,
183                &mut tag,
184                &mut perms,
185                &mut flags,
186                &mut qualifier_type,
187                0,
188                null_mut(),
189                &mut qualifier_size,
190            )
191        };
192        util::status_result("AAEntryACLBlobGetEntry", status)?;
193
194        let mut qualifier = vec![0_u8; qualifier_size];
195        let status = unsafe {
196            ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_get_entry(
197                self.as_ptr(),
198                index,
199                &mut tag,
200                &mut perms,
201                &mut flags,
202                &mut qualifier_type,
203                qualifier.len(),
204                if qualifier.is_empty() {
205                    null_mut()
206                } else {
207                    qualifier.as_mut_ptr()
208                },
209                &mut qualifier_size,
210            )
211        };
212        util::status_result("AAEntryACLBlobGetEntry", status)?;
213
214        Ok(AccessControlEntry {
215            tag,
216            perms,
217            flags,
218            qualifier_type: AceQualifierType::from_raw(qualifier_type).ok_or_else(|| {
219                CompressionError::OperationFailed {
220                    operation: "AAEntryACLBlobGetEntry",
221                    code: i32::try_from(qualifier_type).unwrap_or(i32::MAX),
222                }
223            })?,
224            qualifier,
225        })
226    }
227
228    /// Wraps `AAEntryACLBlobAppendEntry`.
229    pub fn entries(&self) -> Result<Vec<AccessControlEntry>> {
230        (0..self.entry_count())
231            .map(|index| self.entry(index))
232            .collect()
233    }
234
235    /// Wraps `AAEntryACLBlobAppendEntry`.
236    pub fn append_entry(&mut self, entry: &AccessControlEntry) -> Result<()> {
237        let qualifier_ptr = if entry.qualifier.is_empty() {
238            null()
239        } else {
240            entry.qualifier.as_ptr()
241        };
242        let status = unsafe {
243            ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_append_entry(
244                self.as_ptr(),
245                entry.tag,
246                entry.perms,
247                entry.flags,
248                entry.qualifier_type.as_raw(),
249                qualifier_ptr,
250                entry.qualifier.len(),
251            )
252        };
253        util::status_result("AAEntryACLBlobAppendEntry", status)
254    }
255
256    /// Wraps `AAEntryACLBlobSetEntry`.
257    pub fn set_entry(&mut self, index: u32, entry: &AccessControlEntry) -> Result<()> {
258        let qualifier_ptr = if entry.qualifier.is_empty() {
259            null()
260        } else {
261            entry.qualifier.as_ptr()
262        };
263        let status = unsafe {
264            ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_set_entry(
265                self.as_ptr(),
266                index,
267                entry.tag,
268                entry.perms,
269                entry.flags,
270                entry.qualifier_type.as_raw(),
271                qualifier_ptr,
272                entry.qualifier.len(),
273            )
274        };
275        util::status_result("AAEntryACLBlobSetEntry", status)
276    }
277
278    /// Wraps `AAEntryACLBlobClear`.
279    pub fn clear(&mut self) -> Result<()> {
280        let status =
281            unsafe { ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_clear(self.as_ptr()) };
282        util::status_result("AAEntryACLBlobClear", status)
283    }
284
285    /// Wraps `AAEntryACLBlobRemoveEntry`.
286    pub fn remove_entry(&mut self, index: u32) -> Result<()> {
287        let status = unsafe {
288            ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_remove_entry(self.as_ptr(), index)
289        };
290        util::status_result("AAEntryACLBlobRemoveEntry", status)
291    }
292
293    /// Wraps `AAEntryACLBlobGetEncodedData`.
294    pub fn encoded_size(&self) -> usize {
295        unsafe {
296            ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_get_encoded_size(self.as_ptr())
297        }
298    }
299
300    /// Wraps `AAEntryACLBlobGetEncodedData`.
301    pub fn encoded_data(&self) -> Result<Vec<u8>> {
302        let size = self.encoded_size();
303        let mut data = vec![0_u8; size];
304        if size == 0 {
305            return Ok(data);
306        }
307        let copied = unsafe {
308            ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_copy_encoded_data(
309                self.as_ptr(),
310                data.as_mut_ptr(),
311            )
312        };
313        if copied {
314            Ok(data)
315        } else {
316            Err(CompressionError::OperationFailed {
317                operation: "AAEntryACLBlobGetEncodedData",
318                code: -1,
319            })
320        }
321    }
322}
323
324impl Clone for EntryAclBlob {
325    fn clone(&self) -> Self {
326        Self::from_encoded_data(
327            &self
328                .encoded_data()
329                .expect("AAEntryACLBlobGetEncodedData returned invalid data"),
330        )
331        .expect("AAEntryACLBlobCreateWithEncodedData returned null")
332    }
333}
334
335impl Drop for EntryAclBlob {
336    fn drop(&mut self) {
337        unsafe { ffi::aa_entry_blob::compression_rs_aa_entry_acl_blob_release(self.as_ptr()) };
338    }
339}
340
341/// Wraps an `AAEntryXATBlob` handle.
342#[derive(Debug)]
343pub struct EntryXatBlob {
344    handle: NonNull<c_void>,
345}
346
347impl EntryXatBlob {
348    fn from_handle(handle: *mut c_void, operation: &'static str) -> Result<Self> {
349        Ok(Self {
350            handle: util::nonnull_handle(handle, operation)?,
351        })
352    }
353
354    pub(crate) fn clone_from_raw(raw: *mut c_void) -> Result<Self> {
355        let handle =
356            unsafe { ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_clone_from_raw(raw) };
357        Self::from_handle(handle, "AAEntryXATBlobClone")
358    }
359
360    pub(crate) fn sync_into_raw(raw: *mut c_void, value: &Self) -> Result<()> {
361        let status = unsafe { ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_clear_raw(raw) };
362        util::status_result("AAEntryXATBlobClear", status)?;
363        for entry in value.entries()? {
364            let key = util::cstring("key", &entry.key)?;
365            let data_ptr = if entry.value.is_empty() {
366                null()
367            } else {
368                entry.value.as_ptr()
369            };
370            let status = unsafe {
371                ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_append_entry_raw(
372                    raw,
373                    key.as_ptr(),
374                    data_ptr,
375                    entry.value.len(),
376                )
377            };
378            util::status_result("AAEntryXATBlobAppendEntry", status)?;
379        }
380        Ok(())
381    }
382
383    /// Wraps `AAEntryXATBlobCreate`.
384    pub fn new() -> Result<Self> {
385        let handle = unsafe { ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_create() };
386        Self::from_handle(handle, "AAEntryXATBlobCreate")
387    }
388
389    /// Wraps `AAEntryXATBlobCreateWithEncodedData`.
390    pub fn from_encoded_data(data: &[u8]) -> Result<Self> {
391        let handle = unsafe {
392            ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_create_with_encoded_data(
393                data.as_ptr(),
394                data.len(),
395            )
396        };
397        Self::from_handle(handle, "AAEntryXATBlobCreateWithEncodedData")
398    }
399
400    /// Wraps `AAEntryXATBlobCreateWithPath`.
401    pub fn from_path(dir: &str, path: &str, flags: ArchiveFlags) -> Result<Self> {
402        let dir = util::cstring("dir", dir)?;
403        let path = util::cstring("path", path)?;
404        let handle = unsafe {
405            ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_create_with_path(
406                dir.as_ptr(),
407                path.as_ptr(),
408                flags.bits(),
409            )
410        };
411        Self::from_handle(handle, "AAEntryXATBlobCreateWithPath")
412    }
413
414    pub(crate) fn as_ptr(&self) -> *mut c_void {
415        self.handle.as_ptr()
416    }
417
418    /// Wraps `AAEntryXATBlobApplyToPath`.
419    pub fn apply_to_path(&self, dir: &str, path: &str, flags: ArchiveFlags) -> Result<()> {
420        let dir = util::cstring("dir", dir)?;
421        let path = util::cstring("path", path)?;
422        let status = unsafe {
423            ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_apply_to_path(
424                self.as_ptr(),
425                dir.as_ptr(),
426                path.as_ptr(),
427                flags.bits(),
428            )
429        };
430        util::status_result("AAEntryXATBlobApplyToPath", status)
431    }
432
433    /// Wraps `AAEntryXATBlobGetEntry`.
434    pub fn entry_count(&self) -> u32 {
435        unsafe {
436            ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_get_entry_count(self.as_ptr())
437        }
438    }
439
440    /// Wraps `AAEntryXATBlobGetEntry`.
441    pub fn is_empty(&self) -> bool {
442        self.entry_count() == 0
443    }
444
445    /// Wraps `AAEntryXATBlobGetEntry`.
446    pub fn entry(&self, index: u32) -> Result<NamedBlobEntry> {
447        let mut key_length = 0_usize;
448        let mut data_size = 0_usize;
449        let status = unsafe {
450            ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_get_entry(
451                self.as_ptr(),
452                index,
453                0,
454                null_mut(),
455                &mut key_length,
456                0,
457                null_mut(),
458                &mut data_size,
459            )
460        };
461        util::status_result("AAEntryXATBlobGetEntry", status)?;
462
463        let mut key = vec![0_i8; key_length.saturating_add(1)];
464        let mut value = vec![0_u8; data_size];
465        let status = unsafe {
466            ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_get_entry(
467                self.as_ptr(),
468                index,
469                key.len(),
470                key.as_mut_ptr(),
471                &mut key_length,
472                value.len(),
473                if value.is_empty() {
474                    null_mut()
475                } else {
476                    value.as_mut_ptr()
477                },
478                &mut data_size,
479            )
480        };
481        util::status_result("AAEntryXATBlobGetEntry", status)?;
482
483        let key = unsafe { CStr::from_ptr(key.as_ptr()) }
484            .to_str()
485            .map_err(|_| CompressionError::Utf8Error {
486                operation: "AAEntryXATBlobGetEntry",
487            })?
488            .to_string();
489
490        Ok(NamedBlobEntry { key, value })
491    }
492
493    /// Wraps `AAEntryXATBlobAppendEntry`.
494    pub fn entries(&self) -> Result<Vec<NamedBlobEntry>> {
495        (0..self.entry_count())
496            .map(|index| self.entry(index))
497            .collect()
498    }
499
500    /// Wraps `AAEntryXATBlobAppendEntry`.
501    pub fn append_entry(&mut self, entry: &NamedBlobEntry) -> Result<()> {
502        let key = util::cstring("key", &entry.key)?;
503        let data_ptr = if entry.value.is_empty() {
504            null()
505        } else {
506            entry.value.as_ptr()
507        };
508        let status = unsafe {
509            ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_append_entry(
510                self.as_ptr(),
511                key.as_ptr(),
512                data_ptr,
513                entry.value.len(),
514            )
515        };
516        util::status_result("AAEntryXATBlobAppendEntry", status)
517    }
518
519    /// Wraps `AAEntryXATBlobSetEntry`.
520    pub fn set_entry(&mut self, index: u32, entry: &NamedBlobEntry) -> Result<()> {
521        let key = util::cstring("key", &entry.key)?;
522        let data_ptr = if entry.value.is_empty() {
523            null()
524        } else {
525            entry.value.as_ptr()
526        };
527        let status = unsafe {
528            ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_set_entry(
529                self.as_ptr(),
530                index,
531                key.as_ptr(),
532                data_ptr,
533                entry.value.len(),
534            )
535        };
536        util::status_result("AAEntryXATBlobSetEntry", status)
537    }
538
539    /// Wraps `AAEntryXATBlobClear`.
540    pub fn clear(&mut self) -> Result<()> {
541        let status =
542            unsafe { ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_clear(self.as_ptr()) };
543        util::status_result("AAEntryXATBlobClear", status)
544    }
545
546    /// Wraps `AAEntryXATBlobRemoveEntry`.
547    pub fn remove_entry(&mut self, index: u32) -> Result<()> {
548        let status = unsafe {
549            ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_remove_entry(self.as_ptr(), index)
550        };
551        util::status_result("AAEntryXATBlobRemoveEntry", status)
552    }
553
554    /// Wraps `AAEntryXATBlobGetEncodedData`.
555    pub fn encoded_size(&self) -> usize {
556        unsafe {
557            ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_get_encoded_size(self.as_ptr())
558        }
559    }
560
561    /// Wraps `AAEntryXATBlobGetEncodedData`.
562    pub fn encoded_data(&self) -> Result<Vec<u8>> {
563        let size = self.encoded_size();
564        let mut data = vec![0_u8; size];
565        if size == 0 {
566            return Ok(data);
567        }
568        let copied = unsafe {
569            ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_copy_encoded_data(
570                self.as_ptr(),
571                data.as_mut_ptr(),
572            )
573        };
574        if copied {
575            Ok(data)
576        } else {
577            Err(CompressionError::OperationFailed {
578                operation: "AAEntryXATBlobGetEncodedData",
579                code: -1,
580            })
581        }
582    }
583}
584
585impl Clone for EntryXatBlob {
586    fn clone(&self) -> Self {
587        Self::from_encoded_data(
588            &self
589                .encoded_data()
590                .expect("AAEntryXATBlobGetEncodedData returned invalid data"),
591        )
592        .expect("AAEntryXATBlobCreateWithEncodedData returned null")
593    }
594}
595
596impl Drop for EntryXatBlob {
597    fn drop(&mut self) {
598        unsafe { ffi::aa_entry_blob::compression_rs_aa_entry_xat_blob_release(self.as_ptr()) };
599    }
600}