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