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