f2fs_reader/
xattr.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4use 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, // XATTR_MAGIC
14    pub ref_count: u32,
15    _reserved: [u32; 4],
16}
17
18/// xattr have an attached 'index' that serves as a namespace/purpose.
19#[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    // Length in bytes of name.
41    name_len: u8,
42    // Length in bytes of value.
43    value_size: u16,
44    // Note: name follows, then value.
45    // Struct should be padded to u32. i.e. (padding_len + name_len + value_len) % 4 = 0
46}
47
48#[derive(Clone, Debug, Eq, PartialEq)]
49pub struct XattrEntry {
50    // Index dictates use. 1 -> user, 6 -> security, 9 -> encryption, 11 -> verity...
51    pub index: Index,
52    pub name: Box<[u8]>,
53    pub value: Box<[u8]>,
54}
55
56/// Returns all xattr for this inode as a set of key/value pairs.
57/// Requires another read to look up the xattr node.
58pub 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}