From 6d648daa16db911f5c71c2e20e05047619cea52c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 17 Dec 2021 16:19:32 -0500 Subject: [PATCH] tar/export: Add a repo/config with mode `bare-split-xattrs` Part of https://github.com/ostreedev/ostree/issues/2499 Basically, ostree core should have first-class support for reading and parsing this. The primary motivation is to support ostree-in-container better. The real main wrinkle here is xattrs, as usual. We can't rely on being able to read/write xattrs inside a container. For now, this will serve sufficient to *identify* this format, but it will pave the way for ostree core to support read/write. (Note that if we go to implement writes, a notable wrinkle is that garbage collection of the `xattrs` subdirectory will require another special handling pass) --- lib/src/tar/export.rs | 25 ++++++++++++++++++++++--- lib/tests/it/main.rs | 37 +++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/lib/src/tar/export.rs b/lib/src/tar/export.rs index e8ea73f2..4a21a14c 100644 --- a/lib/src/tar/export.rs +++ b/lib/src/tar/export.rs @@ -19,6 +19,13 @@ const SYSROOT: &str = "sysroot"; // This way the default ostree -> sysroot/ostree symlink works. const OSTREEDIR: &str = "sysroot/ostree"; +/// The base repository configuration that identifies this is a tar export. +// See https://github.com/ostreedev/ostree/issues/2499 +const REPO_CONFIG: &str = r#"[core] +repo_version=1 +mode=bare-split-xattrs +"#; + /// A decently large buffer, as used by e.g. coreutils `cat`. /// System calls are expensive. const BUF_CAPACITY: usize = 131072; @@ -94,8 +101,8 @@ impl<'a, W: std::io::Write> OstreeTarWriter<'a, W> { Ok(()) } - /// Write the initial directory structure. - fn write_initial_directories(&mut self) -> Result<()> { + /// Write the initial /sysroot/ostree/repo structure. + fn write_repo_structure(&mut self) -> Result<()> { if self.wrote_initdirs { return Ok(()); } @@ -124,6 +131,18 @@ impl<'a, W: std::io::Write> OstreeTarWriter<'a, W> { // The special `repo/xattrs` directory used only in our tar serialization. let path: Utf8PathBuf = format!("{}/repo/xattrs", OSTREEDIR).into(); self.append_default_dir(&path)?; + let mut h = tar::Header::new_gnu(); + h.set_entry_type(tar::EntryType::Regular); + h.set_uid(0); + h.set_gid(0); + h.set_mode(0o644); + h.set_size(REPO_CONFIG.as_bytes().len() as u64); + self.out.append_data( + &mut h, + &format!("{}/repo/config", OSTREEDIR), + std::io::Cursor::new(REPO_CONFIG), + )?; + Ok(()) } @@ -152,7 +171,7 @@ impl<'a, W: std::io::Write> OstreeTarWriter<'a, W> { self.append_dir(rootpath, metadata)?; // Now, we create sysroot/ and everything under it - self.write_initial_directories()?; + self.write_repo_structure()?; self.append(ostree::ObjectType::Commit, checksum, commit_v)?; if let Some(commitmeta) = self diff --git a/lib/tests/it/main.rs b/lib/tests/it/main.rs index 2e8eeee8..6041b552 100644 --- a/lib/tests/it/main.rs +++ b/lib/tests/it/main.rs @@ -253,22 +253,35 @@ fn test_tar_export_structure() -> Result<()> { assert_eq!(first.header().mode()?, libc::S_IFDIR | 0o755); let next = entries.next().unwrap().unwrap(); assert_eq!(next.path().unwrap().as_os_str(), "sysroot"); + + let expected = vec![ + ( + "sysroot/ostree/repo/config", + tar::EntryType::Regular, + 0o644, + ), + ("usr", tar::EntryType::Directory, libc::S_IFDIR | 0o755), + ]; + let mut entries = entries.map(|e| e.unwrap()); + // Verify we're injecting directories, fixes the absence of `/tmp` in our // images for example. - entries - .map(|e| e.unwrap()) - .find(|entry| { + for (path, expected_type, expected_mode) in expected { + let mut found = false; + while let Some(entry) = entries.next() { let header = entry.header(); - let path = entry.path().unwrap(); - if path.as_os_str() == "usr" { - assert_eq!(header.entry_type(), tar::EntryType::Directory); - assert_eq!(header.mode().unwrap(), libc::S_IFDIR | 0o755); - true - } else { - false + let entry_path = entry.path().unwrap(); + if path == entry_path.as_os_str() { + assert_eq!(header.entry_type(), expected_type); + assert_eq!(header.mode().unwrap(), expected_mode); + found = true; + break; } - }) - .unwrap(); + } + if !found { + panic!("Failed to find entry: {}", path); + } + } Ok(()) }