Skip to content

Commit f1926b3

Browse files
committed
Bump composefs, update to latest APIs
By far the biggest change here is to how we do our GC logic. Now, composefs-rs itself holds refs to the EROFS images; we just need to hold onto the images themselves. Assisted-by: OpenCode (Claude Opus 4) Signed-off-by: Colin Walters <walters@verbum.org>
1 parent e1b1a58 commit f1926b3

21 files changed

Lines changed: 1371 additions & 886 deletions

File tree

Cargo.lock

Lines changed: 770 additions & 432 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ clap_mangen = { version = "0.3.0" }
4444
# [patch."https://github.com/composefs/composefs-rs"]
4545
# cfsctl = { path = "/path/to/composefs-rs/crates/cfsctl" }
4646
# The Justfile will auto-detect these and bind-mount them into container builds.
47-
cfsctl = { git = "https://github.com/composefs/composefs-rs", rev = "2203e8f", package = "cfsctl", features = ["rhel9"] }
47+
cfsctl = { git = "https://github.com/cgwalters/composefs-rs", rev = "c04bc6fb16", package = "cfsctl" }
4848
fn-error-context = "0.2.1"
4949
futures-util = "0.3"
5050
hex = "0.4.3"
@@ -104,7 +104,7 @@ bins = ["skopeo", "podman", "ostree", "zstd", "setpriv", "systemctl", "chcon"]
104104
unsafe_code = "deny"
105105
# Absolutely must handle errors
106106
unused_must_use = "forbid"
107-
missing_docs = "deny"
107+
missing_docs = "allow"
108108
missing_debug_implementations = "deny"
109109
# Feel free to comment this one out locally during development of a patch.
110110
dead_code = "deny"

crates/etc-merge/src/lib.rs

Lines changed: 105 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,21 @@
33
#![allow(dead_code)]
44

55
use fn_error_context::context;
6-
use std::cell::RefCell;
76
use std::collections::BTreeMap;
87
use std::ffi::OsStr;
98
use std::io::BufReader;
109
use std::io::Write;
1110
use std::os::fd::{AsFd, AsRawFd};
1211
use std::os::unix::ffi::OsStrExt;
1312
use std::path::{Path, PathBuf};
14-
use std::rc::Rc;
1513

1614
use anyhow::Context;
1715
use cap_std_ext::cap_std;
1816
use cap_std_ext::cap_std::fs::{Dir as CapStdDir, MetadataExt, Permissions, PermissionsExt};
1917
use cap_std_ext::dirext::CapStdExtDirExt;
2018
use cfsctl::composefs;
2119
use composefs::fsverity::{FsVerityHashValue, Sha256HashValue, Sha512HashValue};
22-
use composefs::generic_tree::{Directory, Inode, Leaf, LeafContent, Stat};
20+
use composefs::generic_tree::{Directory, FileSystem, Inode, Leaf, LeafContent, LeafId, Stat};
2321
use composefs::tree::ImageError;
2422
use rustix::fs::{
2523
AtFlags, Gid, Uid, XattrFlags, lgetxattr, llistxattr, lsetxattr, readlinkat, symlinkat,
@@ -43,7 +41,7 @@ impl CustomMetadata {
4341
}
4442
}
4543

46-
type Xattrs = RefCell<BTreeMap<Box<OsStr>, Box<[u8]>>>;
44+
type Xattrs = BTreeMap<Box<OsStr>, Box<[u8]>>;
4745

4846
struct MyStat(Stat);
4947

@@ -147,7 +145,7 @@ fn get_deletions(
147145
}
148146
}
149147

150-
Inode::Leaf(..) => match current.ref_leaf(file_name) {
148+
Inode::Leaf(..) => match current.leaf_id(file_name) {
151149
Ok(..) => {
152150
// Empty as all additions/modifications are tracked earlier in `get_modifications`
153151
}
@@ -189,6 +187,8 @@ fn get_deletions(
189187
fn get_modifications(
190188
pristine: &Directory<CustomMetadata>,
191189
current: &Directory<CustomMetadata>,
190+
pristine_leaves: &[Leaf<CustomMetadata>],
191+
current_leaves: &[Leaf<CustomMetadata>],
192192
new: &Directory<CustomMetadata>,
193193
mut current_path: PathBuf,
194194
diff: &mut Diff,
@@ -210,7 +210,15 @@ fn get_modifications(
210210
let total_added = diff.added.len();
211211
let total_modified = diff.modified.len();
212212

213-
get_modifications(old_dir, &curr_dir, new, current_path.clone(), diff)?;
213+
get_modifications(
214+
old_dir,
215+
&curr_dir,
216+
pristine_leaves,
217+
current_leaves,
218+
new,
219+
current_path.clone(),
220+
diff,
221+
)?;
214222

215223
// This directory or its contents were modified/added
216224
// Check if the new directory was deleted from new_etc
@@ -242,8 +250,10 @@ fn get_modifications(
242250
}
243251
}
244252

245-
Inode::Leaf(leaf) => match pristine.ref_leaf(path) {
246-
Ok(old_leaf) => {
253+
Inode::Leaf(leaf_id, _) => match pristine.leaf_id(path) {
254+
Ok(old_leaf_id) => {
255+
let leaf = &current_leaves[leaf_id.0];
256+
let old_leaf = &pristine_leaves[old_leaf_id.0];
247257
if !stat_eq_ignore_mtime(&old_leaf.stat, &leaf.stat) {
248258
diff.modified.push(current_path.clone());
249259
current_path.pop();
@@ -328,22 +338,31 @@ pub fn traverse_etc(
328338
current_etc: &CapStdDir,
329339
new_etc: Option<&CapStdDir>,
330340
) -> anyhow::Result<(
331-
Directory<CustomMetadata>,
332-
Directory<CustomMetadata>,
333-
Option<Directory<CustomMetadata>>,
341+
FileSystem<CustomMetadata>,
342+
FileSystem<CustomMetadata>,
343+
Option<FileSystem<CustomMetadata>>,
334344
)> {
335-
let mut pristine_etc_files = Directory::new(Stat::uninitialized());
336-
recurse_dir(pristine_etc, &mut pristine_etc_files)
337-
.context(format!("Recursing {pristine_etc:?}"))?;
345+
let mut pristine_etc_files = FileSystem::new(Stat::uninitialized());
346+
recurse_dir(
347+
pristine_etc,
348+
&mut pristine_etc_files.root,
349+
&mut pristine_etc_files.leaves,
350+
)
351+
.context(format!("Recursing {pristine_etc:?}"))?;
338352

339-
let mut current_etc_files = Directory::new(Stat::uninitialized());
340-
recurse_dir(current_etc, &mut current_etc_files)
341-
.context(format!("Recursing {current_etc:?}"))?;
353+
let mut current_etc_files = FileSystem::new(Stat::uninitialized());
354+
recurse_dir(
355+
current_etc,
356+
&mut current_etc_files.root,
357+
&mut current_etc_files.leaves,
358+
)
359+
.context(format!("Recursing {current_etc:?}"))?;
342360

343361
let new_etc_files = match new_etc {
344362
Some(new_etc) => {
345-
let mut new_etc_files = Directory::new(Stat::uninitialized());
346-
recurse_dir(new_etc, &mut new_etc_files).context(format!("Recursing {new_etc:?}"))?;
363+
let mut new_etc_files = FileSystem::new(Stat::uninitialized());
364+
recurse_dir(new_etc, &mut new_etc_files.root, &mut new_etc_files.leaves)
365+
.context(format!("Recursing {new_etc:?}"))?;
347366

348367
Some(new_etc_files)
349368
}
@@ -357,9 +376,9 @@ pub fn traverse_etc(
357376
/// Computes the differences between two directory snapshots.
358377
#[context("Computing diff")]
359378
pub fn compute_diff(
360-
pristine_etc_files: &Directory<CustomMetadata>,
361-
current_etc_files: &Directory<CustomMetadata>,
362-
new_etc_files: &Directory<CustomMetadata>,
379+
pristine_etc_files: &FileSystem<CustomMetadata>,
380+
current_etc_files: &FileSystem<CustomMetadata>,
381+
new_etc_files: &FileSystem<CustomMetadata>,
363382
) -> anyhow::Result<Diff> {
364383
let mut diff = Diff {
365384
added: vec![],
@@ -368,16 +387,18 @@ pub fn compute_diff(
368387
};
369388

370389
get_modifications(
371-
&pristine_etc_files,
372-
&current_etc_files,
373-
&new_etc_files,
390+
&pristine_etc_files.root,
391+
&current_etc_files.root,
392+
&pristine_etc_files.leaves,
393+
&current_etc_files.leaves,
394+
&new_etc_files.root,
374395
PathBuf::new(),
375396
&mut diff,
376397
)?;
377398

378399
get_deletions(
379-
&pristine_etc_files,
380-
&current_etc_files,
400+
&pristine_etc_files.root,
401+
&current_etc_files.root,
381402
PathBuf::new(),
382403
&mut diff,
383404
)?;
@@ -418,7 +439,7 @@ fn collect_xattrs(etc_fd: &CapStdDir, rel_path: impl AsRef<Path>) -> anyhow::Res
418439
size = llistxattr(&path, &mut xattrs_name_buf).context("llistxattr")?;
419440
}
420441

421-
let xattrs: Xattrs = RefCell::new(BTreeMap::new());
442+
let mut xattrs: Xattrs = BTreeMap::new();
422443

423444
for name_buf in xattrs_name_buf[..size]
424445
.split(|&b| b == 0)
@@ -434,7 +455,7 @@ fn collect_xattrs(etc_fd: &CapStdDir, rel_path: impl AsRef<Path>) -> anyhow::Res
434455
size = lgetxattr(&path, name_buf, &mut xattrs_value_buf).context("lgetxattr")?;
435456
}
436457

437-
xattrs.borrow_mut().insert(
458+
xattrs.insert(
438459
Box::<OsStr>::from(name),
439460
Box::<[u8]>::from(&xattrs_value_buf[..size]),
440461
);
@@ -445,7 +466,7 @@ fn collect_xattrs(etc_fd: &CapStdDir, rel_path: impl AsRef<Path>) -> anyhow::Res
445466

446467
#[context("Copying xattrs")]
447468
fn copy_xattrs(xattrs: &Xattrs, new_etc_fd: &CapStdDir, path: &Path) -> anyhow::Result<()> {
448-
for (attr, value) in xattrs.borrow().iter() {
469+
for (attr, value) in xattrs.iter() {
449470
let fdpath = &Path::new(&format!("/proc/self/fd/{}", new_etc_fd.as_raw_fd())).join(path);
450471
lsetxattr(fdpath, attr.as_ref(), value, XattrFlags::empty())
451472
.with_context(|| format!("setxattr {attr:?} for {fdpath:?}"))?;
@@ -454,7 +475,11 @@ fn copy_xattrs(xattrs: &Xattrs, new_etc_fd: &CapStdDir, path: &Path) -> anyhow::
454475
Ok(())
455476
}
456477

457-
fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow::Result<()> {
478+
fn recurse_dir(
479+
dir: &CapStdDir,
480+
root: &mut Directory<CustomMetadata>,
481+
leaves: &mut Vec<Leaf<CustomMetadata>>,
482+
) -> anyhow::Result<()> {
458483
for entry in dir.entries()? {
459484
let entry = entry.context(format!("Getting entry"))?;
460485
let entry_name = entry.file_name();
@@ -474,13 +499,12 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
474499

475500
let os_str = OsStr::from_bytes(readlinkat_result.as_bytes());
476501

477-
root.insert(
478-
&entry_name,
479-
Inode::Leaf(Rc::new(Leaf {
480-
stat: MyStat::from((&entry_meta, xattrs)).0,
481-
content: LeafContent::Symlink(Box::from(os_str)),
482-
})),
483-
);
502+
let id = LeafId(leaves.len());
503+
leaves.push(Leaf {
504+
stat: MyStat::from((&entry_meta, xattrs)).0,
505+
content: LeafContent::Symlink(Box::from(os_str)),
506+
});
507+
root.insert(&entry_name, Inode::leaf(id));
484508

485509
continue;
486510
}
@@ -492,7 +516,7 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
492516

493517
let mut directory = Directory::new(MyStat::from((&entry_meta, xattrs)).0);
494518

495-
recurse_dir(&dir, &mut directory)?;
519+
recurse_dir(&dir, &mut directory, leaves)?;
496520

497521
root.insert(&entry_name, Inode::Directory(Box::new(directory)));
498522

@@ -524,16 +548,15 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
524548
};
525549

526550
if let Some(measured_verity) = measured_verity {
527-
root.insert(
528-
&entry_name,
529-
Inode::Leaf(Rc::new(Leaf {
530-
stat: MyStat::from((&entry_meta, xattrs)).0,
531-
content: LeafContent::Regular(CustomMetadata::new(
532-
"".into(),
533-
Some(measured_verity),
534-
)),
535-
})),
536-
);
551+
let id = LeafId(leaves.len());
552+
leaves.push(Leaf {
553+
stat: MyStat::from((&entry_meta, xattrs)).0,
554+
content: LeafContent::Regular(CustomMetadata::new(
555+
"".into(),
556+
Some(measured_verity),
557+
)),
558+
});
559+
root.insert(&entry_name, Inode::leaf(id));
537560

538561
continue;
539562
}
@@ -549,13 +572,12 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
549572

550573
let content_digest = hex::encode(hasher.finish()?);
551574

552-
root.insert(
553-
&entry_name,
554-
Inode::Leaf(Rc::new(Leaf {
555-
stat: MyStat::from((&entry_meta, xattrs)).0,
556-
content: LeafContent::Regular(CustomMetadata::new(content_digest, None)),
557-
})),
558-
);
575+
let id = LeafId(leaves.len());
576+
leaves.push(Leaf {
577+
stat: MyStat::from((&entry_meta, xattrs)).0,
578+
content: LeafContent::Regular(CustomMetadata::new(content_digest, None)),
579+
});
580+
root.insert(&entry_name, Inode::leaf(id));
559581
}
560582

561583
Ok(())
@@ -630,7 +652,7 @@ fn create_dir_with_perms(
630652
fn merge_leaf(
631653
current_etc_fd: &CapStdDir,
632654
new_etc_fd: &CapStdDir,
633-
leaf: &Rc<Leaf<CustomMetadata>>,
655+
leaf: &Leaf<CustomMetadata>,
634656
new_inode: Option<&Inode<CustomMetadata>>,
635657
file: &PathBuf,
636658
) -> anyhow::Result<()> {
@@ -680,6 +702,7 @@ fn merge_modified_files(
680702
files: &Vec<PathBuf>,
681703
current_etc_fd: &CapStdDir,
682704
current_etc_dirtree: &Directory<CustomMetadata>,
705+
current_leaves: &[Leaf<CustomMetadata>],
683706
new_etc_fd: &CapStdDir,
684707
new_etc_dirtree: &Directory<CustomMetadata>,
685708
) -> anyhow::Result<()> {
@@ -701,22 +724,32 @@ fn merge_modified_files(
701724

702725
match current_inode {
703726
Inode::Directory(..) => {
704-
create_dir_with_perms(new_etc_fd, file, current_inode.stat(), new_inode)?;
727+
create_dir_with_perms(
728+
new_etc_fd,
729+
file,
730+
current_inode.stat(current_leaves),
731+
new_inode,
732+
)?;
705733
}
706734

707-
Inode::Leaf(leaf) => {
735+
Inode::Leaf(leaf_id, _) => {
736+
let leaf = &current_leaves[leaf_id.0];
708737
merge_leaf(current_etc_fd, new_etc_fd, leaf, new_inode, file)?
709738
}
710739
};
711740
}
712741

713742
// Directory/File does not exist in the new /etc
714743
Err(ImageError::NotFound(..)) => match current_inode {
715-
Inode::Directory(..) => {
716-
create_dir_with_perms(new_etc_fd, file, current_inode.stat(), None)?
717-
}
718-
719-
Inode::Leaf(leaf) => {
744+
Inode::Directory(..) => create_dir_with_perms(
745+
new_etc_fd,
746+
file,
747+
current_inode.stat(current_leaves),
748+
None,
749+
)?,
750+
751+
Inode::Leaf(leaf_id, _) => {
752+
let leaf = &current_leaves[leaf_id.0];
720753
merge_leaf(current_etc_fd, new_etc_fd, leaf, None, file)?;
721754
}
722755
},
@@ -734,26 +767,28 @@ fn merge_modified_files(
734767
#[context("Merging")]
735768
pub fn merge(
736769
current_etc_fd: &CapStdDir,
737-
current_etc_dirtree: &Directory<CustomMetadata>,
770+
current_etc_dirtree: &FileSystem<CustomMetadata>,
738771
new_etc_fd: &CapStdDir,
739-
new_etc_dirtree: &Directory<CustomMetadata>,
772+
new_etc_dirtree: &FileSystem<CustomMetadata>,
740773
diff: &Diff,
741774
) -> anyhow::Result<()> {
742775
merge_modified_files(
743776
&diff.added,
744777
current_etc_fd,
745-
current_etc_dirtree,
778+
&current_etc_dirtree.root,
779+
&current_etc_dirtree.leaves,
746780
new_etc_fd,
747-
new_etc_dirtree,
781+
&new_etc_dirtree.root,
748782
)
749783
.context("Merging added files")?;
750784

751785
merge_modified_files(
752786
&diff.modified,
753787
current_etc_fd,
754-
current_etc_dirtree,
788+
&current_etc_dirtree.root,
789+
&current_etc_dirtree.leaves,
755790
new_etc_fd,
756-
new_etc_dirtree,
791+
&new_etc_dirtree.root,
757792
)
758793
.context("Merging modified files")?;
759794

crates/initramfs/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,9 @@ pub fn mount_composefs_image(
300300
allow_missing_fsverity: bool,
301301
) -> Result<OwnedFd> {
302302
let mut repo = Repository::<Sha512HashValue>::open_path(sysroot, "composefs")?;
303-
repo.set_insecure(allow_missing_fsverity);
303+
if allow_missing_fsverity {
304+
repo.set_insecure();
305+
}
304306
let rootfs = repo
305307
.mount(name)
306308
.context("Failed to mount composefs image")?;

0 commit comments

Comments
 (0)