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;
7pub type AcePermSet = u64;
9pub type AceFlagSet = u64;
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
14#[repr(u32)]
15pub enum AceQualifierType {
16 User = 'U' as u32,
18 Group = 'G' as u32,
20 Sid = 'S' as u32,
22 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#[derive(Clone, Debug, Eq, PartialEq)]
44pub struct AccessControlEntry {
45 pub tag: AceTag,
47 pub perms: AcePermSet,
49 pub flags: AceFlagSet,
51 pub qualifier_type: AceQualifierType,
53 pub qualifier: Vec<u8>,
55}
56
57#[derive(Clone, Debug, Eq, PartialEq)]
59pub struct NamedBlobEntry {
60 pub key: String,
62 pub value: Vec<u8>,
64}
65
66#[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 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 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 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 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 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 pub fn is_empty(&self) -> bool {
169 self.entry_count() == 0
170 }
171
172 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 pub fn entries(&self) -> Result<Vec<AccessControlEntry>> {
230 (0..self.entry_count())
231 .map(|index| self.entry(index))
232 .collect()
233 }
234
235 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 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 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 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 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 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#[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 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 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 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 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 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 pub fn is_empty(&self) -> bool {
442 self.entry_count() == 0
443 }
444
445 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 pub fn entries(&self) -> Result<Vec<NamedBlobEntry>> {
495 (0..self.entry_count())
496 .map(|index| self.entry(index))
497 .collect()
498 }
499
500 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 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 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 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 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 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}