1use anyhow::{anyhow, ensure, Error};
5use enumn::N;
6use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref, Unaligned};
7
8pub const XATTR_MAGIC: u32 = 0xF2F52011;
9
10#[repr(C, packed)]
11#[derive(Copy, Clone, Debug, Immutable, KnownLayout, FromBytes, IntoBytes, Unaligned)]
12pub struct XattrHeader {
13 pub magic: u32, pub ref_count: u32,
15 _reserved: [u32; 4],
16}
17
18#[derive(Copy, Clone, Debug, Eq, PartialEq, N)]
20#[repr(u8)]
21pub enum Index {
22 Unused0 = 0,
23 User = 1,
24 PosixAclAccess = 2,
25 PosixAclDefault = 3,
26 Trusted = 4,
27 Lustre = 5,
28 Security = 6,
29 IndexAdvise = 7,
30 Unused8 = 8,
31 Encryption = 9,
32 Unused10 = 10,
33 Verity = 11,
34}
35
36#[repr(C, packed)]
37#[derive(Copy, Clone, Debug, Immutable, KnownLayout, FromBytes, IntoBytes, Unaligned)]
38struct RawXattrEntry {
39 name_index: u8,
40 name_len: u8,
42 value_size: u16,
44 }
47
48#[derive(Clone, Debug, Eq, PartialEq)]
49pub struct XattrEntry {
50 pub index: Index,
52 pub name: Box<[u8]>,
53 pub value: Box<[u8]>,
54}
55
56pub fn decode_xattr(raw_data: &[u8]) -> Result<Vec<XattrEntry>, Error> {
59 if raw_data.is_empty() {
60 return Ok(vec![]);
61 }
62 let (header, mut rest): (Ref<_, XattrHeader>, _) =
63 Ref::from_prefix(raw_data).map_err(|_| anyhow!("xattr too small"))?;
64 if header.magic != XATTR_MAGIC {
65 return Ok(vec![]);
66 }
67
68 let mut entries = Vec::new();
69 while rest.len() >= std::mem::size_of::<RawXattrEntry>() {
70 let (entry, remainder): (Ref<_, RawXattrEntry>, _) = Ref::from_prefix(rest).unwrap();
71 rest = remainder;
72 if entry.name_index > 0 {
73 let index =
74 Index::n(entry.name_index).ok_or_else(|| anyhow!("Unexpected xattr index"))?;
75 ensure!(
76 (entry.name_len as usize + entry.value_size as usize) <= rest.len(),
77 "invalid name_len/value_size in xattr"
78 );
79 let padding_len = 4 - (entry.name_len as usize + entry.value_size as usize) % 4;
80
81 let name = rest[..entry.name_len as usize].to_owned().into_boxed_slice();
82 rest = &rest[entry.name_len as usize..];
83 let value = rest[..entry.value_size as usize].to_owned().into_boxed_slice();
84 rest = &rest[entry.value_size as usize + padding_len..];
85 entries.push(XattrEntry { index, name, value });
86 } else {
87 break;
88 }
89 }
90 Ok(entries)
91}