Skip to content

Commit d527926

Browse files
etc-merge: Handle xattrs
Signed-off-by: Johan-Liebert1 <[email protected]>
1 parent c226cf4 commit d527926

File tree

1 file changed

+79
-15
lines changed

1 file changed

+79
-15
lines changed

crates/etc-merge/src/lib.rs

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
#![allow(dead_code)]
44

55
use fn_error_context::context;
6-
use std::ffi::OsStr;
6+
use std::cell::RefCell;
7+
use std::collections::BTreeMap;
8+
use std::ffi::{OsStr, OsString};
79
use std::io::BufReader;
10+
use std::os::fd::{AsFd, AsRawFd};
811
use std::os::unix::ffi::OsStrExt;
9-
use std::path::PathBuf;
12+
use std::path::{Path, PathBuf};
1013
use std::rc::Rc;
1114

1215
use anyhow::Context;
@@ -16,7 +19,7 @@ use cap_std_ext::dirext::CapStdExtDirExt;
1619
use composefs::fsverity::{FsVerityHashValue, Sha256HashValue, Sha512HashValue};
1720
use composefs::generic_tree::{Directory, Inode, Leaf, LeafContent, Stat};
1821
use composefs::tree::ImageError;
19-
use rustix::fs::{AtFlags, Gid, Uid, readlinkat};
22+
use rustix::fs::{AtFlags, Gid, Uid, XattrFlags, getxattr, listxattr, lsetxattr, readlinkat};
2023

2124
#[derive(Debug)]
2225
struct CustomMetadata {
@@ -33,16 +36,18 @@ impl CustomMetadata {
3336
}
3437
}
3538

39+
type Xattrs = RefCell<BTreeMap<Box<OsStr>, Box<[u8]>>>;
40+
3641
struct MyStat(Stat);
3742

38-
impl From<&cap_std::fs::Metadata> for MyStat {
39-
fn from(value: &cap_std::fs::Metadata) -> Self {
43+
impl From<(&cap_std::fs::Metadata, Xattrs)> for MyStat {
44+
fn from(value: (&cap_std::fs::Metadata, Xattrs)) -> Self {
4045
Self(Stat {
41-
st_mode: value.mode(),
42-
st_uid: value.uid(),
43-
st_gid: value.gid(),
44-
st_mtim_sec: value.mtime(),
45-
xattrs: Default::default(),
46+
st_mode: value.0.mode(),
47+
st_uid: value.0.uid(),
48+
st_gid: value.0.gid(),
49+
st_mtim_sec: value.0.mtime(),
50+
xattrs: value.1,
4651
})
4752
}
4853
}
@@ -288,6 +293,58 @@ fn compute_diff(
288293
Ok(diff)
289294
}
290295

296+
#[context("Collecting xattrs")]
297+
fn collect_xattrs(etc_fd: &CapStdDir, rel_path: &OsString) -> anyhow::Result<Xattrs> {
298+
let link = format!("/proc/self/fd/{}", etc_fd.as_fd().as_raw_fd());
299+
let path = Path::new(&link).join(rel_path);
300+
301+
const DEFAULT_SIZE: usize = 128;
302+
303+
// Start with a guess for size
304+
let mut buf: Vec<u8> = vec![0; DEFAULT_SIZE];
305+
let size = listxattr(&path, &mut buf).context("listxattr")?;
306+
307+
if size > DEFAULT_SIZE {
308+
buf = vec![0; size];
309+
listxattr(&path, &mut buf).context("listxattr")?;
310+
}
311+
312+
let xattrs: Xattrs = RefCell::new(BTreeMap::new());
313+
314+
for name_buf in buf[..size]
315+
.split_inclusive(|&b| b == 0)
316+
.filter(|x| !x.is_empty())
317+
{
318+
let name = OsStr::from_bytes(name_buf);
319+
320+
let mut buf = vec![0; DEFAULT_SIZE];
321+
let size = getxattr(&path, name_buf, &mut buf).context("getxattr")?;
322+
323+
if size > DEFAULT_SIZE {
324+
buf = vec![0; size];
325+
getxattr(&path, name_buf, &mut buf).context("getxattr")?;
326+
}
327+
328+
xattrs
329+
.borrow_mut()
330+
.insert(Box::<OsStr>::from(name), Box::<[u8]>::from(&buf[..size]));
331+
}
332+
333+
Ok(xattrs)
334+
}
335+
336+
#[context("Copying xattrs")]
337+
fn copy_xattrs(xattrs: &Xattrs, new_etc_fd: &CapStdDir, file: &PathBuf) -> anyhow::Result<()> {
338+
for (attr, value) in xattrs.borrow().iter() {
339+
let path = Path::new(&format!("/proc/self/fd/{}", new_etc_fd.as_raw_fd())).join(file);
340+
341+
lsetxattr(path, attr.as_ref(), value, XattrFlags::empty())
342+
.context(format!("setxattr for {file:?}"))?;
343+
}
344+
345+
Ok(())
346+
}
347+
291348
fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow::Result<()> {
292349
for entry in dir.entries()? {
293350
let entry = entry.context(format!("Getting entry"))?;
@@ -298,12 +355,14 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
298355
.metadata()
299356
.context(format!("Getting metadata for {entry_name:?}"))?;
300357

358+
let xattrs = collect_xattrs(&dir, &entry_name)?;
359+
301360
if entry_type.is_dir() {
302361
let dir = dir
303362
.open_dir(&entry_name)
304363
.with_context(|| format!("Opening dir {entry_name:?} inside {dir:?}"))?;
305364

306-
let mut directory = Directory::new(MyStat::from(&entry_meta).0);
365+
let mut directory = Directory::new(MyStat::from((&entry_meta, xattrs)).0);
307366

308367
recurse_dir(&dir, &mut directory)?;
309368

@@ -328,7 +387,7 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
328387
root.insert(
329388
&entry_name,
330389
Inode::Leaf(Rc::new(Leaf {
331-
stat: MyStat::from(&entry_meta).0,
390+
stat: MyStat::from((&entry_meta, xattrs)).0,
332391
content: LeafContent::Symlink(Box::from(os_str)),
333392
})),
334393
);
@@ -357,7 +416,7 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
357416
root.insert(
358417
&entry_name,
359418
Inode::Leaf(Rc::new(Leaf {
360-
stat: MyStat::from(&entry_meta).0,
419+
stat: MyStat::from((&entry_meta, xattrs)).0,
361420
content: LeafContent::Regular(CustomMetadata::new(
362421
"".into(),
363422
Some(measured_verity),
@@ -382,7 +441,7 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
382441
root.insert(
383442
&entry_name,
384443
Inode::Leaf(Rc::new(Leaf {
385-
stat: MyStat::from(&entry_meta).0,
444+
stat: MyStat::from((&entry_meta, xattrs)).0,
386445
content: LeafContent::Regular(CustomMetadata::new(content_digest, None)),
387446
})),
388447
);
@@ -447,6 +506,8 @@ fn create_dir_with_perms(
447506
)
448507
.context(format!("chown {dir_name:?}"))?;
449508

509+
copy_xattrs(&stat.xattrs, new_etc_fd, dir_name)?;
510+
450511
Ok(())
451512
}
452513

@@ -481,6 +542,8 @@ fn handle_leaf(
481542
)
482543
.context(format!("chown {file:?}"))?;
483544

545+
copy_xattrs(&leaf.stat.xattrs, new_etc_fd, file)?;
546+
484547
"file"
485548
}
486549

@@ -507,6 +570,8 @@ fn handle_leaf(
507570
)
508571
.context(format!("chown {file:?}"))?;
509572

573+
copy_xattrs(&leaf.stat.xattrs, new_etc_fd, file)?;
574+
510575
"symlink"
511576
}
512577

@@ -864,7 +929,6 @@ mod tests {
864929

865930
let (pristine_etc_files, current_etc_files, new_etc_files) = traverse_etc(&p, &c, &n)?;
866931
let diff = compute_diff(&pristine_etc_files, &current_etc_files)?;
867-
println!("current_etc_files: {current_etc_files:#?}");
868932
merge(&c, &current_etc_files, &n, &new_etc_files, diff)?;
869933

870934
assert!(files_eq(&c, &n, "new_file.txt")?);

0 commit comments

Comments
 (0)