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}