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}