Skip to content

Commit

Permalink
tar/export: Add a repo/config with mode bare-split-xattrs
Browse files Browse the repository at this point in the history
Part of ostreedev/ostree#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)
  • Loading branch information
cgwalters committed Dec 18, 2021
1 parent 2d1805c commit 9afc67c
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 15 deletions.
28 changes: 25 additions & 3 deletions lib/src/tar/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,21 @@ use std::borrow::Cow;
use std::collections::HashSet;
use std::io::BufReader;

/// The repository mode generated by a tar export stream.
pub const BARE_SPLIT_XATTRS_MODE: &str = "bare-split-xattrs";

// This is both special in the tar stream *and* it's in the ostree commit.
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;
Expand Down Expand Up @@ -94,8 +104,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(());
}
Expand Down Expand Up @@ -124,6 +134,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(())
}

Expand Down Expand Up @@ -152,7 +174,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
Expand Down
4 changes: 4 additions & 0 deletions lib/src/tar/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ impl Importer {
.ok_or_else(|| anyhow!("Invalid non-utf8 path {:?}", orig_path))?;
// Ignore the regular non-object file hardlinks we inject
if let Ok(path) = path.strip_prefix(REPO_PREFIX) {
// Filter out the repo config file
if path.file_name() == Some("config") {
return Ok(None)
}
let path = path.into();
Ok(Some((e, path)))
} else {
Expand Down
33 changes: 21 additions & 12 deletions lib/tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,22 +253,31 @@ 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(())
}

Expand Down

0 comments on commit 9afc67c

Please sign in to comment.