Skip to content

Commit f92bf57

Browse files
Johan-Liebert1cgwalters
authored andcommitted
etc-merge: Create directory in new_etc if deleted
If a directory is modified/added in the current etc, but deleted in the new etc, we'd want it in the new etc. This case prior to this commit resulted in a panic as we were not taking it into account Fixes: #1924 Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
1 parent ebbd348 commit f92bf57

3 files changed

Lines changed: 67 additions & 27 deletions

File tree

crates/etc-merge/src/lib.rs

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ fn get_deletions(
188188
fn get_modifications(
189189
pristine: &Directory<CustomMetadata>,
190190
current: &Directory<CustomMetadata>,
191+
new: &Directory<CustomMetadata>,
191192
mut current_path: PathBuf,
192193
diff: &mut Diff,
193194
) -> anyhow::Result<()> {
@@ -205,7 +206,21 @@ fn get_modifications(
205206
diff.modified.push(current_path.clone());
206207
}
207208

208-
get_modifications(old_dir, &curr_dir, current_path.clone(), diff)?
209+
let total_added = diff.added.len();
210+
let total_modified = diff.modified.len();
211+
212+
get_modifications(old_dir, &curr_dir, new, current_path.clone(), diff)?;
213+
214+
// This directory or its contents were modified/added
215+
// Check if the new directory was deleted from new_etc
216+
// If it was, we want to add the directory back
217+
if new.get_directory_opt(&current_path.as_os_str())?.is_none() {
218+
if diff.added.len() != total_added {
219+
diff.added.insert(total_added, current_path.clone());
220+
} else if diff.modified.len() != total_modified {
221+
diff.modified.insert(total_modified, current_path.clone());
222+
}
223+
}
209224
}
210225

211226
Err(ImageError::NotFound(..)) => {
@@ -343,6 +358,7 @@ pub fn traverse_etc(
343358
pub fn compute_diff(
344359
pristine_etc_files: &Directory<CustomMetadata>,
345360
current_etc_files: &Directory<CustomMetadata>,
361+
new_etc_files: &Directory<CustomMetadata>,
346362
) -> anyhow::Result<Diff> {
347363
let mut diff = Diff {
348364
added: vec![],
@@ -353,6 +369,7 @@ pub fn compute_diff(
353369
get_modifications(
354370
&pristine_etc_files,
355371
&current_etc_files,
372+
&new_etc_files,
356373
PathBuf::new(),
357374
&mut diff,
358375
)?;
@@ -641,7 +658,7 @@ fn merge_leaf(
641658
} else {
642659
current_etc_fd
643660
.copy(&file, new_etc_fd, &file)
644-
.context(format!("Copying file {file:?}"))?;
661+
.with_context(|| format!("Copying file {file:?}"))?;
645662
};
646663

647664
rustix::fs::chownat(
@@ -719,7 +736,7 @@ pub fn merge(
719736
current_etc_dirtree: &Directory<CustomMetadata>,
720737
new_etc_fd: &CapStdDir,
721738
new_etc_dirtree: &Directory<CustomMetadata>,
722-
diff: Diff,
739+
diff: &Diff,
723740
) -> anyhow::Result<()> {
724741
merge_modified_files(
725742
&diff.added,
@@ -739,7 +756,7 @@ pub fn merge(
739756
)
740757
.context("Merging modified files")?;
741758

742-
for removed in diff.removed {
759+
for removed in &diff.removed {
743760
let stat = new_etc_fd.metadata_optional(&removed)?;
744761

745762
let Some(stat) = stat else {
@@ -779,7 +796,7 @@ mod tests {
779796
];
780797

781798
#[test]
782-
fn test_etc_diff() -> anyhow::Result<()> {
799+
fn test_etc_diff_plus_merge() -> anyhow::Result<()> {
783800
let tempdir = cap_std_ext::cap_tempfile::tempdir(cap_std::ambient_authority())?;
784801

785802
tempdir.create_dir("pristine_etc")?;
@@ -822,17 +839,28 @@ mod tests {
822839
c.remove_file(deleted_files[0])?;
823840
c.remove_file(deleted_files[1])?;
824841

825-
let (pristine_etc_files, current_etc_files, _) = traverse_etc(&p, &c, Some(&n))?;
826-
let res = compute_diff(&pristine_etc_files, &current_etc_files)?;
842+
let (pristine_etc_files, current_etc_files, new_etc_files) =
843+
traverse_etc(&p, &c, Some(&n))?;
827844

828-
// Test added files
829-
assert_eq!(res.added.len(), new_files.len());
830-
assert!(res.added.iter().all(|file| {
831-
new_files
832-
.iter()
833-
.find(|x| PathBuf::from(*x) == *file)
834-
.is_some()
835-
}));
845+
let res = compute_diff(
846+
&pristine_etc_files,
847+
&current_etc_files,
848+
new_etc_files.as_ref().unwrap(),
849+
)?;
850+
851+
merge(
852+
&c,
853+
&current_etc_files,
854+
&n,
855+
new_etc_files.as_ref().unwrap(),
856+
&res,
857+
)
858+
.expect("Merge failed");
859+
860+
let added_dirs = ["a", "a/b", "a/b/c"];
861+
862+
// 3 for the files, and 3 for the directories
863+
assert_eq!(res.added.len(), new_files.len() + added_dirs.len());
836864

837865
// Test modified files
838866
let all_modified_files = overwritten_files
@@ -1008,8 +1036,12 @@ mod tests {
10081036

10091037
let (pristine_etc_files, current_etc_files, new_etc_files) =
10101038
traverse_etc(&p, &c, Some(&n))?;
1011-
let diff = compute_diff(&pristine_etc_files, &current_etc_files)?;
1012-
merge(&c, &current_etc_files, &n, &new_etc_files.unwrap(), diff)?;
1039+
let diff = compute_diff(
1040+
&pristine_etc_files,
1041+
&current_etc_files,
1042+
&new_etc_files.as_ref().unwrap(),
1043+
)?;
1044+
merge(&c, &current_etc_files, &n, &new_etc_files.unwrap(), &diff)?;
10131045

10141046
assert!(files_eq(&c, &n, "new_file.txt")?);
10151047
assert!(files_eq(&c, &n, "a/new_file.txt")?);
@@ -1081,9 +1113,13 @@ mod tests {
10811113

10821114
let (pristine_etc_files, current_etc_files, new_etc_files) =
10831115
traverse_etc(&p, &c, Some(&n))?;
1084-
let diff = compute_diff(&pristine_etc_files, &current_etc_files)?;
1116+
let diff = compute_diff(
1117+
&pristine_etc_files,
1118+
&current_etc_files,
1119+
&new_etc_files.as_ref().unwrap(),
1120+
)?;
10851121

1086-
let merge_res = merge(&c, &current_etc_files, &n, &new_etc_files.unwrap(), diff);
1122+
let merge_res = merge(&c, &current_etc_files, &n, &new_etc_files.unwrap(), &diff);
10871123

10881124
assert!(merge_res.is_err());
10891125
assert_eq!(

crates/lib/src/bootc_composefs/finalize.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use bootc_initramfs_setup::mount_composefs_image;
1111
use bootc_mount::tempmount::TempMount;
1212
use cap_std_ext::cap_std::{ambient_authority, fs::Dir};
1313
use cap_std_ext::dirext::CapStdExtDirExt;
14+
use composefs::generic_tree::Directory;
1415
use etc_merge::{compute_diff, merge, print_diff, traverse_etc};
1516
use rustix::fs::{fsync, renameat};
1617
use rustix::path::Arg;
@@ -32,7 +33,7 @@ pub(crate) async fn get_etc_diff(storage: &Storage, booted_cfs: &BootedComposefs
3233
let current_etc = Dir::open_ambient_dir("/etc", ambient_authority())?;
3334

3435
let (pristine_files, current_files, _) = traverse_etc(&pristine_etc, &current_etc, None)?;
35-
let diff = compute_diff(&pristine_files, &current_files)?;
36+
let diff = compute_diff(&pristine_files, &current_files, &Directory::default())?;
3637

3738
print_diff(&diff, &mut std::io::stdout());
3839

@@ -76,10 +77,11 @@ pub(crate) async fn composefs_backend_finalize(
7677
let (pristine_files, current_files, new_files) =
7778
traverse_etc(&pristine_etc, &current_etc, Some(&new_etc))?;
7879

79-
let new_files = new_files.ok_or(anyhow::anyhow!("Failed to get dirtree for new etc"))?;
80+
let new_files =
81+
new_files.ok_or_else(|| anyhow::anyhow!("Failed to get dirtree for new etc"))?;
8082

81-
let diff = compute_diff(&pristine_files, &current_files)?;
82-
merge(&current_etc, &current_files, &new_etc, &new_files, diff)?;
83+
let diff = compute_diff(&pristine_files, &current_files, &new_files)?;
84+
merge(&current_etc, &current_files, &new_etc, &new_files, &diff)?;
8385

8486
// Unmount EROFS
8587
drop(erofs_tmp_mnt);

crates/lib/src/cli.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,13 +1823,15 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
18231823
let (p, c, n) =
18241824
etc_merge::traverse_etc(&pristine_etc, &current_etc, Some(&new_etc))?;
18251825

1826-
let diff = compute_diff(&p, &c)?;
1826+
let n = n
1827+
.as_ref()
1828+
.ok_or_else(|| anyhow::anyhow!("Failed to get new directory tree"))?;
1829+
1830+
let diff = compute_diff(&p, &c, &n)?;
18271831
print_diff(&diff, &mut std::io::stdout());
18281832

18291833
if merge {
1830-
let n =
1831-
n.ok_or_else(|| anyhow::anyhow!("Failed to get dirtree for new etc"))?;
1832-
etc_merge::merge(&current_etc, &c, &new_etc, &n, diff)?;
1834+
etc_merge::merge(&current_etc, &c, &new_etc, &n, &diff)?;
18331835
}
18341836

18351837
Ok(())

0 commit comments

Comments
 (0)