Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions crates/tmpfiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use rustix::path::Arg;
use thiserror::Error;

const TMPFILESD: &str = "usr/lib/tmpfiles.d";
const ETC_TMPFILESD: &str = "etc/tmpfiles.d";
/// The path to the file we use for generation
const BOOTC_GENERATED_PREFIX: &str = "bootc-autogenerated-var";

Expand Down Expand Up @@ -452,14 +453,16 @@ pub fn find_missing_tmpfiles_current_root() -> Result<TmpfilesResult> {
})
}

/// Read all tmpfiles.d entries in the target directory, and return a mapping
/// from (file path) => (single tmpfiles.d entry line)
fn read_tmpfiles(rootfs: &Dir) -> Result<(BTreeMap<PathBuf, String>, BootcTmpfilesGeneration)> {
let Some(tmpfiles_dir) = rootfs.open_dir_optional(TMPFILESD)? else {
/// Read all tmpfiles.d entries from a single directory
fn read_tmpfiles_from_dir(
rootfs: &Dir,
dir_path: &str,
generation: &mut BootcTmpfilesGeneration,
) -> Result<BTreeMap<PathBuf, String>> {
let Some(tmpfiles_dir) = rootfs.open_dir_optional(dir_path)? else {
return Ok(Default::default());
};
let mut result = BTreeMap::new();
let mut generation = BootcTmpfilesGeneration::default();
for entry in tmpfiles_dir.entries()? {
let entry = entry?;
let name = entry.file_name();
Expand All @@ -486,6 +489,25 @@ fn read_tmpfiles(rootfs: &Dir) -> Result<(BTreeMap<PathBuf, String>, BootcTmpfil
result.insert(path.to_owned(), line);
}
}
Ok(result)
}

/// Read all tmpfiles.d entries in the target directory, and return a mapping
/// from (file path) => (single tmpfiles.d entry line)
///
/// This function reads from both `/usr/lib/tmpfiles.d` and `/etc/tmpfiles.d`,
/// with `/etc` entries taking precedence (matching systemd's behavior).
fn read_tmpfiles(rootfs: &Dir) -> Result<(BTreeMap<PathBuf, String>, BootcTmpfilesGeneration)> {
let mut generation = BootcTmpfilesGeneration::default();

// Read from /usr/lib/tmpfiles.d first (system/package-provided)
let mut result = read_tmpfiles_from_dir(rootfs, TMPFILESD, &mut generation)?;

// Read from /etc/tmpfiles.d and merge (user-provided, takes precedence)
let etc_result = read_tmpfiles_from_dir(rootfs, ETC_TMPFILESD, &mut generation)?;
// /etc entries override /usr/lib entries for the same path
result.extend(etc_result);

Ok((result, generation))
}

Expand Down Expand Up @@ -569,10 +591,18 @@ mod tests {
"#},
)?;

// Also test /etc/tmpfiles.d (user-provided configs)
rootfs.create_dir_all(ETC_TMPFILESD)?;
rootfs.write(
Path::new(ETC_TMPFILESD).join("user.conf"),
"d /var/lib/user 0755 root root - -\n",
)?;

// Add test content.
rootfs.ensure_dir_with("var/lib/systemd", &db)?;
rootfs.ensure_dir_with("var/lib/private", &db)?;
rootfs.ensure_dir_with("var/lib/nfs", &db)?;
rootfs.ensure_dir_with("var/lib/user", &db)?;
let global_rwx = Permissions::from_mode(0o777);
rootfs.ensure_dir_with("var/lib/test/nested", &db).unwrap();
rootfs.set_permissions("var/lib/test", global_rwx.clone())?;
Expand Down