xref: /aosp_15_r20/external/crosvm/ext2/src/xattr.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2024 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker //! Provides utilites for extended attributes.
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use std::ffi::c_char;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::ffi::CString;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::os::unix::ffi::OsStrExt;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::path::Path;
11*bb4ee6a4SAndroid Build Coastguard Worker 
12*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::bail;
13*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context;
14*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Result;
15*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::AsBytes;
16*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::FromBytes;
17*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::FromZeroes;
18*bb4ee6a4SAndroid Build Coastguard Worker 
19*bb4ee6a4SAndroid Build Coastguard Worker use crate::inode::Inode;
20*bb4ee6a4SAndroid Build Coastguard Worker 
listxattr(path: &CString) -> Result<Vec<Vec<u8>>>21*bb4ee6a4SAndroid Build Coastguard Worker fn listxattr(path: &CString) -> Result<Vec<Vec<u8>>> {
22*bb4ee6a4SAndroid Build Coastguard Worker     // SAFETY: Passing valid pointers and values.
23*bb4ee6a4SAndroid Build Coastguard Worker     let size = unsafe { libc::llistxattr(path.as_ptr(), std::ptr::null_mut(), 0) };
24*bb4ee6a4SAndroid Build Coastguard Worker     if size < 0 {
25*bb4ee6a4SAndroid Build Coastguard Worker         bail!(
26*bb4ee6a4SAndroid Build Coastguard Worker             "failed to get xattr size: {}",
27*bb4ee6a4SAndroid Build Coastguard Worker             std::io::Error::last_os_error()
28*bb4ee6a4SAndroid Build Coastguard Worker         );
29*bb4ee6a4SAndroid Build Coastguard Worker     }
30*bb4ee6a4SAndroid Build Coastguard Worker 
31*bb4ee6a4SAndroid Build Coastguard Worker     if size == 0 {
32*bb4ee6a4SAndroid Build Coastguard Worker         // No extended attributes were set.
33*bb4ee6a4SAndroid Build Coastguard Worker         return Ok(vec![]);
34*bb4ee6a4SAndroid Build Coastguard Worker     }
35*bb4ee6a4SAndroid Build Coastguard Worker 
36*bb4ee6a4SAndroid Build Coastguard Worker     let mut buf = vec![0 as c_char; size as usize];
37*bb4ee6a4SAndroid Build Coastguard Worker 
38*bb4ee6a4SAndroid Build Coastguard Worker     // SAFETY: Passing valid pointers and values.
39*bb4ee6a4SAndroid Build Coastguard Worker     let size = unsafe { libc::llistxattr(path.as_ptr(), buf.as_mut_ptr(), buf.len()) };
40*bb4ee6a4SAndroid Build Coastguard Worker     if size < 0 {
41*bb4ee6a4SAndroid Build Coastguard Worker         bail!(
42*bb4ee6a4SAndroid Build Coastguard Worker             "failed to list of xattr: {}",
43*bb4ee6a4SAndroid Build Coastguard Worker             std::io::Error::last_os_error()
44*bb4ee6a4SAndroid Build Coastguard Worker         );
45*bb4ee6a4SAndroid Build Coastguard Worker     }
46*bb4ee6a4SAndroid Build Coastguard Worker 
47*bb4ee6a4SAndroid Build Coastguard Worker     buf.pop(); // Remove null terminator
48*bb4ee6a4SAndroid Build Coastguard Worker 
49*bb4ee6a4SAndroid Build Coastguard Worker     // While `c_char` is `i8` on x86_64, it's `u8` on ARM. So, disable the clippy for the cast.
50*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg_attr(
51*bb4ee6a4SAndroid Build Coastguard Worker         any(target_arch = "arm", target_arch = "aarch64"),
52*bb4ee6a4SAndroid Build Coastguard Worker         allow(clippy::unnecessary_cast)
53*bb4ee6a4SAndroid Build Coastguard Worker     )]
54*bb4ee6a4SAndroid Build Coastguard Worker     let keys = buf
55*bb4ee6a4SAndroid Build Coastguard Worker         .split(|c| *c == 0)
56*bb4ee6a4SAndroid Build Coastguard Worker         .map(|v| v.iter().map(|c| *c as u8).collect::<Vec<_>>())
57*bb4ee6a4SAndroid Build Coastguard Worker         .collect::<Vec<Vec<_>>>();
58*bb4ee6a4SAndroid Build Coastguard Worker 
59*bb4ee6a4SAndroid Build Coastguard Worker     Ok(keys)
60*bb4ee6a4SAndroid Build Coastguard Worker }
61*bb4ee6a4SAndroid Build Coastguard Worker 
lgetxattr(path: &CString, name: &CString) -> Result<Vec<u8>>62*bb4ee6a4SAndroid Build Coastguard Worker fn lgetxattr(path: &CString, name: &CString) -> Result<Vec<u8>> {
63*bb4ee6a4SAndroid Build Coastguard Worker     // SAFETY: passing valid pointers.
64*bb4ee6a4SAndroid Build Coastguard Worker     let size = unsafe { libc::lgetxattr(path.as_ptr(), name.as_ptr(), std::ptr::null_mut(), 0) };
65*bb4ee6a4SAndroid Build Coastguard Worker     if size < 0 {
66*bb4ee6a4SAndroid Build Coastguard Worker         bail!(
67*bb4ee6a4SAndroid Build Coastguard Worker             "failed to get xattr size for {:?}: {}",
68*bb4ee6a4SAndroid Build Coastguard Worker             name,
69*bb4ee6a4SAndroid Build Coastguard Worker             std::io::Error::last_os_error()
70*bb4ee6a4SAndroid Build Coastguard Worker         );
71*bb4ee6a4SAndroid Build Coastguard Worker     }
72*bb4ee6a4SAndroid Build Coastguard Worker     let mut buf = vec![0; size as usize];
73*bb4ee6a4SAndroid Build Coastguard Worker     // SAFETY: passing valid pointers and length.
74*bb4ee6a4SAndroid Build Coastguard Worker     let size = unsafe {
75*bb4ee6a4SAndroid Build Coastguard Worker         libc::lgetxattr(
76*bb4ee6a4SAndroid Build Coastguard Worker             path.as_ptr(),
77*bb4ee6a4SAndroid Build Coastguard Worker             name.as_ptr(),
78*bb4ee6a4SAndroid Build Coastguard Worker             buf.as_mut_ptr() as *mut libc::c_void,
79*bb4ee6a4SAndroid Build Coastguard Worker             buf.len(),
80*bb4ee6a4SAndroid Build Coastguard Worker         )
81*bb4ee6a4SAndroid Build Coastguard Worker     };
82*bb4ee6a4SAndroid Build Coastguard Worker     if size < 0 {
83*bb4ee6a4SAndroid Build Coastguard Worker         bail!(
84*bb4ee6a4SAndroid Build Coastguard Worker             "failed to get xattr for {:?}: {}",
85*bb4ee6a4SAndroid Build Coastguard Worker             name,
86*bb4ee6a4SAndroid Build Coastguard Worker             std::io::Error::last_os_error()
87*bb4ee6a4SAndroid Build Coastguard Worker         );
88*bb4ee6a4SAndroid Build Coastguard Worker     }
89*bb4ee6a4SAndroid Build Coastguard Worker 
90*bb4ee6a4SAndroid Build Coastguard Worker     Ok(buf)
91*bb4ee6a4SAndroid Build Coastguard Worker }
92*bb4ee6a4SAndroid Build Coastguard Worker 
93*bb4ee6a4SAndroid Build Coastguard Worker /// Retrieves the list of pairs of a name and a value of the extended attribute of the given `path`.
94*bb4ee6a4SAndroid Build Coastguard Worker /// If `path` is a symbolic link, it won't be followed and the value of the symlink itself is
95*bb4ee6a4SAndroid Build Coastguard Worker /// returned.
96*bb4ee6a4SAndroid Build Coastguard Worker /// The return values are byte arrays WITHOUT trailing NULL byte.
dump_xattrs(path: &Path) -> Result<Vec<(Vec<u8>, Vec<u8>)>>97*bb4ee6a4SAndroid Build Coastguard Worker pub fn dump_xattrs(path: &Path) -> Result<Vec<(Vec<u8>, Vec<u8>)>> {
98*bb4ee6a4SAndroid Build Coastguard Worker     let mut path_vec = path.as_os_str().as_bytes().to_vec();
99*bb4ee6a4SAndroid Build Coastguard Worker     path_vec.push(0);
100*bb4ee6a4SAndroid Build Coastguard Worker     let path_str = CString::from_vec_with_nul(path_vec)?;
101*bb4ee6a4SAndroid Build Coastguard Worker 
102*bb4ee6a4SAndroid Build Coastguard Worker     let keys = listxattr(&path_str).context("failed to listxattr")?;
103*bb4ee6a4SAndroid Build Coastguard Worker 
104*bb4ee6a4SAndroid Build Coastguard Worker     let mut kvs = vec![];
105*bb4ee6a4SAndroid Build Coastguard Worker     for key in keys {
106*bb4ee6a4SAndroid Build Coastguard Worker         let mut key_vec = key.to_vec();
107*bb4ee6a4SAndroid Build Coastguard Worker         key_vec.push(0);
108*bb4ee6a4SAndroid Build Coastguard Worker         let name = CString::from_vec_with_nul(key_vec)?;
109*bb4ee6a4SAndroid Build Coastguard Worker 
110*bb4ee6a4SAndroid Build Coastguard Worker         let buf = lgetxattr(&path_str, &name).context("failed to getxattr")?;
111*bb4ee6a4SAndroid Build Coastguard Worker         kvs.push((key.to_vec(), buf));
112*bb4ee6a4SAndroid Build Coastguard Worker     }
113*bb4ee6a4SAndroid Build Coastguard Worker 
114*bb4ee6a4SAndroid Build Coastguard Worker     Ok(kvs)
115*bb4ee6a4SAndroid Build Coastguard Worker }
116*bb4ee6a4SAndroid Build Coastguard Worker 
117*bb4ee6a4SAndroid Build Coastguard Worker /// Sets the extended attribute of the given `path` with the given `key` and `value`.
set_xattr(path: &Path, key: &str, value: &str) -> Result<()>118*bb4ee6a4SAndroid Build Coastguard Worker pub fn set_xattr(path: &Path, key: &str, value: &str) -> Result<()> {
119*bb4ee6a4SAndroid Build Coastguard Worker     let mut path_bytes = path
120*bb4ee6a4SAndroid Build Coastguard Worker         .as_os_str()
121*bb4ee6a4SAndroid Build Coastguard Worker         .as_bytes()
122*bb4ee6a4SAndroid Build Coastguard Worker         .iter()
123*bb4ee6a4SAndroid Build Coastguard Worker         .map(|i| *i as c_char)
124*bb4ee6a4SAndroid Build Coastguard Worker         .collect::<Vec<_>>();
125*bb4ee6a4SAndroid Build Coastguard Worker     path_bytes.push(0); // null terminator
126*bb4ee6a4SAndroid Build Coastguard Worker 
127*bb4ee6a4SAndroid Build Coastguard Worker     // While name must be a nul-terminated string, value is not, as it can be a binary data.
128*bb4ee6a4SAndroid Build Coastguard Worker     let mut key_vec = key.bytes().collect::<Vec<_>>();
129*bb4ee6a4SAndroid Build Coastguard Worker     key_vec.push(0);
130*bb4ee6a4SAndroid Build Coastguard Worker     let name = CString::from_vec_with_nul(key_vec)?;
131*bb4ee6a4SAndroid Build Coastguard Worker     let v = value.bytes().collect::<Vec<_>>();
132*bb4ee6a4SAndroid Build Coastguard Worker 
133*bb4ee6a4SAndroid Build Coastguard Worker     // SAFETY: `path_bytes` and `nam` are null-terminated byte arrays.
134*bb4ee6a4SAndroid Build Coastguard Worker     // `v` is valid data.
135*bb4ee6a4SAndroid Build Coastguard Worker     let size = unsafe {
136*bb4ee6a4SAndroid Build Coastguard Worker         libc::lsetxattr(
137*bb4ee6a4SAndroid Build Coastguard Worker             path_bytes.as_ptr(),
138*bb4ee6a4SAndroid Build Coastguard Worker             name.as_ptr(),
139*bb4ee6a4SAndroid Build Coastguard Worker             v.as_ptr() as *const libc::c_void,
140*bb4ee6a4SAndroid Build Coastguard Worker             v.len(),
141*bb4ee6a4SAndroid Build Coastguard Worker             0,
142*bb4ee6a4SAndroid Build Coastguard Worker         )
143*bb4ee6a4SAndroid Build Coastguard Worker     };
144*bb4ee6a4SAndroid Build Coastguard Worker     if size != 0 {
145*bb4ee6a4SAndroid Build Coastguard Worker         bail!(
146*bb4ee6a4SAndroid Build Coastguard Worker             "failed to set xattr for {:?}: {}",
147*bb4ee6a4SAndroid Build Coastguard Worker             path,
148*bb4ee6a4SAndroid Build Coastguard Worker             std::io::Error::last_os_error()
149*bb4ee6a4SAndroid Build Coastguard Worker         );
150*bb4ee6a4SAndroid Build Coastguard Worker     }
151*bb4ee6a4SAndroid Build Coastguard Worker     Ok(())
152*bb4ee6a4SAndroid Build Coastguard Worker }
153*bb4ee6a4SAndroid Build Coastguard Worker 
154*bb4ee6a4SAndroid Build Coastguard Worker #[repr(C)]
155*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Default, Debug, Copy, Clone, FromZeroes, FromBytes, AsBytes)]
156*bb4ee6a4SAndroid Build Coastguard Worker pub(crate) struct XattrEntry {
157*bb4ee6a4SAndroid Build Coastguard Worker     name_len: u8,
158*bb4ee6a4SAndroid Build Coastguard Worker     name_index: u8,
159*bb4ee6a4SAndroid Build Coastguard Worker     value_offs: u16,
160*bb4ee6a4SAndroid Build Coastguard Worker     value_inum: u32,
161*bb4ee6a4SAndroid Build Coastguard Worker     value_size: u32,
162*bb4ee6a4SAndroid Build Coastguard Worker     hash: u32,
163*bb4ee6a4SAndroid Build Coastguard Worker     // name[name_len] follows
164*bb4ee6a4SAndroid Build Coastguard Worker }
165*bb4ee6a4SAndroid Build Coastguard Worker 
166*bb4ee6a4SAndroid Build Coastguard Worker impl XattrEntry {
167*bb4ee6a4SAndroid Build Coastguard Worker     /// Creates a new `XattrEntry` instance with the name as a byte sequence that follows.
new_with_name<'a>( name: &'a [u8], value: &[u8], value_offs: u16, ) -> Result<(Self, &'a [u8])>168*bb4ee6a4SAndroid Build Coastguard Worker     pub(crate) fn new_with_name<'a>(
169*bb4ee6a4SAndroid Build Coastguard Worker         name: &'a [u8],
170*bb4ee6a4SAndroid Build Coastguard Worker         value: &[u8],
171*bb4ee6a4SAndroid Build Coastguard Worker         value_offs: u16,
172*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<(Self, &'a [u8])> {
173*bb4ee6a4SAndroid Build Coastguard Worker         let (name_index, key_str) = Self::split_key_prefix(name);
174*bb4ee6a4SAndroid Build Coastguard Worker         let name_len = key_str.len() as u8;
175*bb4ee6a4SAndroid Build Coastguard Worker         let value_size = value.len() as u32;
176*bb4ee6a4SAndroid Build Coastguard Worker         Ok((
177*bb4ee6a4SAndroid Build Coastguard Worker             XattrEntry {
178*bb4ee6a4SAndroid Build Coastguard Worker                 name_len,
179*bb4ee6a4SAndroid Build Coastguard Worker                 name_index,
180*bb4ee6a4SAndroid Build Coastguard Worker                 value_offs,
181*bb4ee6a4SAndroid Build Coastguard Worker                 value_inum: 0,
182*bb4ee6a4SAndroid Build Coastguard Worker                 value_size,
183*bb4ee6a4SAndroid Build Coastguard Worker                 hash: 0,
184*bb4ee6a4SAndroid Build Coastguard Worker             },
185*bb4ee6a4SAndroid Build Coastguard Worker             key_str,
186*bb4ee6a4SAndroid Build Coastguard Worker         ))
187*bb4ee6a4SAndroid Build Coastguard Worker     }
188*bb4ee6a4SAndroid Build Coastguard Worker 
189*bb4ee6a4SAndroid Build Coastguard Worker     /// Split the given xatrr key string into it's prefix's name index and the remaining part.
190*bb4ee6a4SAndroid Build Coastguard Worker     /// e.g. "user.foo" -> (1, "foo") because the key prefix "user." has index 1.
split_key_prefix(name: &[u8]) -> (u8, &[u8])191*bb4ee6a4SAndroid Build Coastguard Worker     fn split_key_prefix(name: &[u8]) -> (u8, &[u8]) {
192*bb4ee6a4SAndroid Build Coastguard Worker         // ref. https://docs.kernel.org/filesystems/ext4/dynamic.html#attribute-name-indices
193*bb4ee6a4SAndroid Build Coastguard Worker         for (name_index, key_prefix) in [
194*bb4ee6a4SAndroid Build Coastguard Worker             (1, "user."),
195*bb4ee6a4SAndroid Build Coastguard Worker             (2, "system.posix_acl_access"),
196*bb4ee6a4SAndroid Build Coastguard Worker             (3, "system.posix_acl_default"),
197*bb4ee6a4SAndroid Build Coastguard Worker             (4, "trusted."),
198*bb4ee6a4SAndroid Build Coastguard Worker             // 5 is skipped
199*bb4ee6a4SAndroid Build Coastguard Worker             (6, "security."),
200*bb4ee6a4SAndroid Build Coastguard Worker             (7, "system."),
201*bb4ee6a4SAndroid Build Coastguard Worker             (8, "system.richacl"),
202*bb4ee6a4SAndroid Build Coastguard Worker         ] {
203*bb4ee6a4SAndroid Build Coastguard Worker             let prefix_bytes = key_prefix.as_bytes();
204*bb4ee6a4SAndroid Build Coastguard Worker             if name.starts_with(prefix_bytes) {
205*bb4ee6a4SAndroid Build Coastguard Worker                 return (name_index, &name[prefix_bytes.len()..]);
206*bb4ee6a4SAndroid Build Coastguard Worker             }
207*bb4ee6a4SAndroid Build Coastguard Worker         }
208*bb4ee6a4SAndroid Build Coastguard Worker         (0, name)
209*bb4ee6a4SAndroid Build Coastguard Worker     }
210*bb4ee6a4SAndroid Build Coastguard Worker }
211*bb4ee6a4SAndroid Build Coastguard Worker 
212*bb4ee6a4SAndroid Build Coastguard Worker /// Xattr data written into Inode's inline xattr space.
213*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Default, Debug, PartialEq, Eq)]
214*bb4ee6a4SAndroid Build Coastguard Worker pub struct InlineXattrs {
215*bb4ee6a4SAndroid Build Coastguard Worker     pub entry_table: Vec<u8>,
216*bb4ee6a4SAndroid Build Coastguard Worker     pub values: Vec<u8>,
217*bb4ee6a4SAndroid Build Coastguard Worker }
218*bb4ee6a4SAndroid Build Coastguard Worker 
align<T: Clone + Default>(mut v: Vec<T>, alignment: usize) -> Vec<T>219*bb4ee6a4SAndroid Build Coastguard Worker fn align<T: Clone + Default>(mut v: Vec<T>, alignment: usize) -> Vec<T> {
220*bb4ee6a4SAndroid Build Coastguard Worker     let aligned = v.len().next_multiple_of(alignment);
221*bb4ee6a4SAndroid Build Coastguard Worker     v.extend(vec![T::default(); aligned - v.len()]);
222*bb4ee6a4SAndroid Build Coastguard Worker     v
223*bb4ee6a4SAndroid Build Coastguard Worker }
224*bb4ee6a4SAndroid Build Coastguard Worker 
225*bb4ee6a4SAndroid Build Coastguard Worker const XATTR_HEADER_MAGIC: u32 = 0xEA020000;
226*bb4ee6a4SAndroid Build Coastguard Worker 
227*bb4ee6a4SAndroid Build Coastguard Worker impl InlineXattrs {
228*bb4ee6a4SAndroid Build Coastguard Worker     // Creates `InlineXattrs` for the given path.
from_path(path: &Path) -> Result<Self>229*bb4ee6a4SAndroid Build Coastguard Worker     pub fn from_path(path: &Path) -> Result<Self> {
230*bb4ee6a4SAndroid Build Coastguard Worker         let v = dump_xattrs(path).with_context(|| format!("failed to get xattr for {:?}", path))?;
231*bb4ee6a4SAndroid Build Coastguard Worker 
232*bb4ee6a4SAndroid Build Coastguard Worker         // Assume all the data are in inode record.
233*bb4ee6a4SAndroid Build Coastguard Worker         let mut entry_table = vec![];
234*bb4ee6a4SAndroid Build Coastguard Worker         let mut values = vec![];
235*bb4ee6a4SAndroid Build Coastguard Worker         // Data layout of the inline Inode record is as follows.
236*bb4ee6a4SAndroid Build Coastguard Worker         //
237*bb4ee6a4SAndroid Build Coastguard Worker         // | Inode struct | header | extra region |
238*bb4ee6a4SAndroid Build Coastguard Worker         //  <--------- Inode record  ------------>
239*bb4ee6a4SAndroid Build Coastguard Worker         //
240*bb4ee6a4SAndroid Build Coastguard Worker         // The value `val_offset` below is an offset from the beginning of the extra region and used
241*bb4ee6a4SAndroid Build Coastguard Worker         // to indicate the place where the next xattr value will be written. While we place
242*bb4ee6a4SAndroid Build Coastguard Worker         // attribute entries from the beginning of the extra region, we place values from the end of
243*bb4ee6a4SAndroid Build Coastguard Worker         // the region. So the initial value of `val_offset` indicates the end of the extra
244*bb4ee6a4SAndroid Build Coastguard Worker         // region.
245*bb4ee6a4SAndroid Build Coastguard Worker         //
246*bb4ee6a4SAndroid Build Coastguard Worker         // See Table 5.1. at https://www.nongnu.org/ext2-doc/ext2.html#extended-attribute-layout for the more details on data layout.
247*bb4ee6a4SAndroid Build Coastguard Worker         // Although this table is for xattr in a separate block, data layout is same.
248*bb4ee6a4SAndroid Build Coastguard Worker         let mut val_offset = Inode::INODE_RECORD_SIZE
249*bb4ee6a4SAndroid Build Coastguard Worker             - std::mem::size_of::<Inode>()
250*bb4ee6a4SAndroid Build Coastguard Worker             - std::mem::size_of_val(&XATTR_HEADER_MAGIC);
251*bb4ee6a4SAndroid Build Coastguard Worker 
252*bb4ee6a4SAndroid Build Coastguard Worker         entry_table.extend(XATTR_HEADER_MAGIC.to_le_bytes());
253*bb4ee6a4SAndroid Build Coastguard Worker         for (name, value) in v {
254*bb4ee6a4SAndroid Build Coastguard Worker             let aligned_val_len = value.len().next_multiple_of(4);
255*bb4ee6a4SAndroid Build Coastguard Worker 
256*bb4ee6a4SAndroid Build Coastguard Worker             if entry_table.len()
257*bb4ee6a4SAndroid Build Coastguard Worker                 + values.len()
258*bb4ee6a4SAndroid Build Coastguard Worker                 + std::mem::size_of::<XattrEntry>()
259*bb4ee6a4SAndroid Build Coastguard Worker                 + aligned_val_len
260*bb4ee6a4SAndroid Build Coastguard Worker                 > Inode::XATTR_AREA_SIZE
261*bb4ee6a4SAndroid Build Coastguard Worker             {
262*bb4ee6a4SAndroid Build Coastguard Worker                 bail!("Xattr entry is too large");
263*bb4ee6a4SAndroid Build Coastguard Worker             }
264*bb4ee6a4SAndroid Build Coastguard Worker 
265*bb4ee6a4SAndroid Build Coastguard Worker             val_offset -= aligned_val_len;
266*bb4ee6a4SAndroid Build Coastguard Worker             let (entry, name) = XattrEntry::new_with_name(&name, &value, val_offset as u16)?;
267*bb4ee6a4SAndroid Build Coastguard Worker             entry_table.extend(entry.as_bytes());
268*bb4ee6a4SAndroid Build Coastguard Worker             entry_table.extend(name);
269*bb4ee6a4SAndroid Build Coastguard Worker             entry_table = align(entry_table, 4);
270*bb4ee6a4SAndroid Build Coastguard Worker             values.push(align(value, 4));
271*bb4ee6a4SAndroid Build Coastguard Worker         }
272*bb4ee6a4SAndroid Build Coastguard Worker         let values = values.iter().rev().flatten().copied().collect::<Vec<_>>();
273*bb4ee6a4SAndroid Build Coastguard Worker 
274*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Self {
275*bb4ee6a4SAndroid Build Coastguard Worker             entry_table,
276*bb4ee6a4SAndroid Build Coastguard Worker             values,
277*bb4ee6a4SAndroid Build Coastguard Worker         })
278*bb4ee6a4SAndroid Build Coastguard Worker     }
279*bb4ee6a4SAndroid Build Coastguard Worker }
280*bb4ee6a4SAndroid Build Coastguard Worker 
281*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
282*bb4ee6a4SAndroid Build Coastguard Worker pub(crate) mod tests {
283*bb4ee6a4SAndroid Build Coastguard Worker     use std::collections::BTreeMap;
284*bb4ee6a4SAndroid Build Coastguard Worker     use std::fs::File;
285*bb4ee6a4SAndroid Build Coastguard Worker 
286*bb4ee6a4SAndroid Build Coastguard Worker     use tempfile::tempdir;
287*bb4ee6a4SAndroid Build Coastguard Worker 
288*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
289*bb4ee6a4SAndroid Build Coastguard Worker 
to_char_array(s: &str) -> Vec<u8>290*bb4ee6a4SAndroid Build Coastguard Worker     fn to_char_array(s: &str) -> Vec<u8> {
291*bb4ee6a4SAndroid Build Coastguard Worker         s.bytes().collect()
292*bb4ee6a4SAndroid Build Coastguard Worker     }
293*bb4ee6a4SAndroid Build Coastguard Worker 
294*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_attr_name_index()295*bb4ee6a4SAndroid Build Coastguard Worker     fn test_attr_name_index() {
296*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
297*bb4ee6a4SAndroid Build Coastguard Worker             XattrEntry::split_key_prefix(b"user.foo"),
298*bb4ee6a4SAndroid Build Coastguard Worker             (1, "foo".as_bytes())
299*bb4ee6a4SAndroid Build Coastguard Worker         );
300*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
301*bb4ee6a4SAndroid Build Coastguard Worker             XattrEntry::split_key_prefix(b"trusted.bar"),
302*bb4ee6a4SAndroid Build Coastguard Worker             (4, "bar".as_bytes())
303*bb4ee6a4SAndroid Build Coastguard Worker         );
304*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
305*bb4ee6a4SAndroid Build Coastguard Worker             XattrEntry::split_key_prefix(b"security.abcdefgh"),
306*bb4ee6a4SAndroid Build Coastguard Worker             (6, "abcdefgh".as_bytes())
307*bb4ee6a4SAndroid Build Coastguard Worker         );
308*bb4ee6a4SAndroid Build Coastguard Worker 
309*bb4ee6a4SAndroid Build Coastguard Worker         // "system."-prefix
310*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
311*bb4ee6a4SAndroid Build Coastguard Worker             XattrEntry::split_key_prefix(b"system.posix_acl_access"),
312*bb4ee6a4SAndroid Build Coastguard Worker             (2, "".as_bytes())
313*bb4ee6a4SAndroid Build Coastguard Worker         );
314*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
315*bb4ee6a4SAndroid Build Coastguard Worker             XattrEntry::split_key_prefix(b"system.posix_acl_default"),
316*bb4ee6a4SAndroid Build Coastguard Worker             (3, "".as_bytes())
317*bb4ee6a4SAndroid Build Coastguard Worker         );
318*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
319*bb4ee6a4SAndroid Build Coastguard Worker             XattrEntry::split_key_prefix(b"system.abcdefgh"),
320*bb4ee6a4SAndroid Build Coastguard Worker             (7, "abcdefgh".as_bytes())
321*bb4ee6a4SAndroid Build Coastguard Worker         );
322*bb4ee6a4SAndroid Build Coastguard Worker 
323*bb4ee6a4SAndroid Build Coastguard Worker         // unmatched prefix
324*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
325*bb4ee6a4SAndroid Build Coastguard Worker             XattrEntry::split_key_prefix(b"invalid.foo"),
326*bb4ee6a4SAndroid Build Coastguard Worker             (0, "invalid.foo".as_bytes())
327*bb4ee6a4SAndroid Build Coastguard Worker         );
328*bb4ee6a4SAndroid Build Coastguard Worker     }
329*bb4ee6a4SAndroid Build Coastguard Worker 
330*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_get_xattr_empty()331*bb4ee6a4SAndroid Build Coastguard Worker     fn test_get_xattr_empty() {
332*bb4ee6a4SAndroid Build Coastguard Worker         let td = tempdir().unwrap();
333*bb4ee6a4SAndroid Build Coastguard Worker         let test_path = td.path().join("test.txt");
334*bb4ee6a4SAndroid Build Coastguard Worker 
335*bb4ee6a4SAndroid Build Coastguard Worker         // Don't set any extended attributes.
336*bb4ee6a4SAndroid Build Coastguard Worker         File::create(&test_path).unwrap();
337*bb4ee6a4SAndroid Build Coastguard Worker 
338*bb4ee6a4SAndroid Build Coastguard Worker         let kvs = dump_xattrs(&test_path).unwrap();
339*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(kvs.len(), 0);
340*bb4ee6a4SAndroid Build Coastguard Worker     }
341*bb4ee6a4SAndroid Build Coastguard Worker 
342*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_inline_xattr_from_path()343*bb4ee6a4SAndroid Build Coastguard Worker     fn test_inline_xattr_from_path() {
344*bb4ee6a4SAndroid Build Coastguard Worker         let td = tempdir().unwrap();
345*bb4ee6a4SAndroid Build Coastguard Worker         let test_path = td.path().join("test.txt");
346*bb4ee6a4SAndroid Build Coastguard Worker         File::create(&test_path).unwrap();
347*bb4ee6a4SAndroid Build Coastguard Worker 
348*bb4ee6a4SAndroid Build Coastguard Worker         let key = "key";
349*bb4ee6a4SAndroid Build Coastguard Worker         let xattr_key = &format!("user.{key}");
350*bb4ee6a4SAndroid Build Coastguard Worker         let value = "value";
351*bb4ee6a4SAndroid Build Coastguard Worker 
352*bb4ee6a4SAndroid Build Coastguard Worker         set_xattr(&test_path, xattr_key, value).unwrap();
353*bb4ee6a4SAndroid Build Coastguard Worker 
354*bb4ee6a4SAndroid Build Coastguard Worker         let xattrs = InlineXattrs::from_path(&test_path).unwrap();
355*bb4ee6a4SAndroid Build Coastguard Worker         let entry = XattrEntry {
356*bb4ee6a4SAndroid Build Coastguard Worker             name_len: key.len() as u8,
357*bb4ee6a4SAndroid Build Coastguard Worker             name_index: 1,
358*bb4ee6a4SAndroid Build Coastguard Worker             value_offs: (Inode::INODE_RECORD_SIZE
359*bb4ee6a4SAndroid Build Coastguard Worker                 - std::mem::size_of::<Inode>()
360*bb4ee6a4SAndroid Build Coastguard Worker                 - std::mem::size_of_val(&XATTR_HEADER_MAGIC)
361*bb4ee6a4SAndroid Build Coastguard Worker                 - value.len().next_multiple_of(4)) as u16,
362*bb4ee6a4SAndroid Build Coastguard Worker             value_size: value.len() as u32,
363*bb4ee6a4SAndroid Build Coastguard Worker             value_inum: 0,
364*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
365*bb4ee6a4SAndroid Build Coastguard Worker         };
366*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
367*bb4ee6a4SAndroid Build Coastguard Worker             xattrs.entry_table,
368*bb4ee6a4SAndroid Build Coastguard Worker             align(
369*bb4ee6a4SAndroid Build Coastguard Worker                 [
370*bb4ee6a4SAndroid Build Coastguard Worker                     XATTR_HEADER_MAGIC.to_le_bytes().to_vec(),
371*bb4ee6a4SAndroid Build Coastguard Worker                     entry.as_bytes().to_vec(),
372*bb4ee6a4SAndroid Build Coastguard Worker                     key.as_bytes().to_vec(),
373*bb4ee6a4SAndroid Build Coastguard Worker                 ]
374*bb4ee6a4SAndroid Build Coastguard Worker                 .concat(),
375*bb4ee6a4SAndroid Build Coastguard Worker                 4
376*bb4ee6a4SAndroid Build Coastguard Worker             ),
377*bb4ee6a4SAndroid Build Coastguard Worker         );
378*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(xattrs.values, align(value.as_bytes().to_vec(), 4),);
379*bb4ee6a4SAndroid Build Coastguard Worker     }
380*bb4ee6a4SAndroid Build Coastguard Worker 
381*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_too_many_values_for_inline_xattr()382*bb4ee6a4SAndroid Build Coastguard Worker     fn test_too_many_values_for_inline_xattr() {
383*bb4ee6a4SAndroid Build Coastguard Worker         let td = tempdir().unwrap();
384*bb4ee6a4SAndroid Build Coastguard Worker         let test_path = td.path().join("test.txt");
385*bb4ee6a4SAndroid Build Coastguard Worker         File::create(&test_path).unwrap();
386*bb4ee6a4SAndroid Build Coastguard Worker 
387*bb4ee6a4SAndroid Build Coastguard Worker         // Prepare 10 pairs of xattributes, which will not fit inline space.
388*bb4ee6a4SAndroid Build Coastguard Worker         let mut xattr_pairs = vec![];
389*bb4ee6a4SAndroid Build Coastguard Worker         for i in 0..10 {
390*bb4ee6a4SAndroid Build Coastguard Worker             xattr_pairs.push((format!("user.foo{i}"), "bar"));
391*bb4ee6a4SAndroid Build Coastguard Worker         }
392*bb4ee6a4SAndroid Build Coastguard Worker 
393*bb4ee6a4SAndroid Build Coastguard Worker         for (key, value) in &xattr_pairs {
394*bb4ee6a4SAndroid Build Coastguard Worker             set_xattr(&test_path, key, value).unwrap();
395*bb4ee6a4SAndroid Build Coastguard Worker         }
396*bb4ee6a4SAndroid Build Coastguard Worker 
397*bb4ee6a4SAndroid Build Coastguard Worker         // Must fail
398*bb4ee6a4SAndroid Build Coastguard Worker         InlineXattrs::from_path(&test_path).unwrap_err();
399*bb4ee6a4SAndroid Build Coastguard Worker     }
400*bb4ee6a4SAndroid Build Coastguard Worker 
401*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_get_xattr()402*bb4ee6a4SAndroid Build Coastguard Worker     fn test_get_xattr() {
403*bb4ee6a4SAndroid Build Coastguard Worker         let td = tempdir().unwrap();
404*bb4ee6a4SAndroid Build Coastguard Worker         let test_path = td.path().join("test.txt");
405*bb4ee6a4SAndroid Build Coastguard Worker         File::create(&test_path).unwrap();
406*bb4ee6a4SAndroid Build Coastguard Worker 
407*bb4ee6a4SAndroid Build Coastguard Worker         let xattr_pairs = vec![
408*bb4ee6a4SAndroid Build Coastguard Worker             ("user.foo", "bar"),
409*bb4ee6a4SAndroid Build Coastguard Worker             ("user.hash", "09f7e02f1290be211da707a266f153b3"),
410*bb4ee6a4SAndroid Build Coastguard Worker             ("user.empty", ""),
411*bb4ee6a4SAndroid Build Coastguard Worker         ];
412*bb4ee6a4SAndroid Build Coastguard Worker 
413*bb4ee6a4SAndroid Build Coastguard Worker         for (key, value) in &xattr_pairs {
414*bb4ee6a4SAndroid Build Coastguard Worker             set_xattr(&test_path, key, value).unwrap();
415*bb4ee6a4SAndroid Build Coastguard Worker         }
416*bb4ee6a4SAndroid Build Coastguard Worker 
417*bb4ee6a4SAndroid Build Coastguard Worker         let kvs = dump_xattrs(&test_path).unwrap();
418*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(kvs.len(), xattr_pairs.len());
419*bb4ee6a4SAndroid Build Coastguard Worker 
420*bb4ee6a4SAndroid Build Coastguard Worker         let xattr_map: BTreeMap<Vec<u8>, Vec<u8>> = kvs.into_iter().collect();
421*bb4ee6a4SAndroid Build Coastguard Worker 
422*bb4ee6a4SAndroid Build Coastguard Worker         for (orig_k, orig_v) in xattr_pairs {
423*bb4ee6a4SAndroid Build Coastguard Worker             let k = to_char_array(orig_k);
424*bb4ee6a4SAndroid Build Coastguard Worker             let v = to_char_array(orig_v);
425*bb4ee6a4SAndroid Build Coastguard Worker             let got = xattr_map.get(&k).unwrap();
426*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(&v, got);
427*bb4ee6a4SAndroid Build Coastguard Worker         }
428*bb4ee6a4SAndroid Build Coastguard Worker     }
429*bb4ee6a4SAndroid Build Coastguard Worker 
430*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_get_xattr_symlink()431*bb4ee6a4SAndroid Build Coastguard Worker     fn test_get_xattr_symlink() {
432*bb4ee6a4SAndroid Build Coastguard Worker         let td = tempdir().unwrap();
433*bb4ee6a4SAndroid Build Coastguard Worker 
434*bb4ee6a4SAndroid Build Coastguard Worker         // Set xattr on test.txt.
435*bb4ee6a4SAndroid Build Coastguard Worker         let test_path = td.path().join("test.txt");
436*bb4ee6a4SAndroid Build Coastguard Worker         File::create(&test_path).unwrap();
437*bb4ee6a4SAndroid Build Coastguard Worker         set_xattr(&test_path, "user.name", "user.test.txt").unwrap();
438*bb4ee6a4SAndroid Build Coastguard Worker 
439*bb4ee6a4SAndroid Build Coastguard Worker         // Create a symlink to test.txt.
440*bb4ee6a4SAndroid Build Coastguard Worker         let symlink_path = td.path().join("symlink");
441*bb4ee6a4SAndroid Build Coastguard Worker         std::os::unix::fs::symlink(&test_path, &symlink_path).unwrap();
442*bb4ee6a4SAndroid Build Coastguard Worker 
443*bb4ee6a4SAndroid Build Coastguard Worker         // dump_xattrs shouldn't follow a symlink.
444*bb4ee6a4SAndroid Build Coastguard Worker         let kvs = dump_xattrs(&symlink_path).unwrap();
445*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(kvs, vec![]);
446*bb4ee6a4SAndroid Build Coastguard Worker     }
447*bb4ee6a4SAndroid Build Coastguard Worker }
448