Skip to content

Commit 36672b4

Browse files
tofayTom Fay
authored andcommitted
initial layering attempt
implement per-package layering a la https://grahamc.com/blog/nix-and-layered-docker-images/, using nix's existing popularity algorithm
1 parent 47443c4 commit 36672b4

5 files changed

Lines changed: 918 additions & 120 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ tests/**/out
33
out/
44
vendor/
55
tests/**/rpmoci.lock
6+
__pycache__

src/archive.rs

Lines changed: 2 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -13,110 +13,13 @@
1313
//! You should have received a copy of the GNU General Public License
1414
//! along with this program. If not, see <https://www.gnu.org/licenses/>.
1515
use anyhow::{Context, Result};
16-
use std::{
17-
collections::{hash_map::Entry, HashMap},
18-
io::Write,
19-
os::unix::{
20-
fs::MetadataExt,
21-
prelude::{FileTypeExt, OsStrExt},
22-
},
23-
path::{Path, PathBuf},
24-
};
25-
use walkdir::WalkDir;
16+
use std::{io::Write, os::unix::prelude::OsStrExt, path::Path};
2617

2718
// https://mgorny.pl/articles/portability-of-tar-features.html#id25
2819
const PAX_SCHILY_XATTR: &[u8; 13] = b"SCHILY.xattr.";
2920

30-
/// custom implementation of tar-rs's append_dir_all that:
31-
/// - works around https://github.com/alexcrichton/tar-rs/issues/102 so that security capabilities are preserved
32-
/// - emulates tar's `--clamp-mtime` option so that any file/dir/symlink mtimes are no later than a specific value
33-
/// - supports hardlinks
34-
pub(super) fn append_dir_all_with_xattrs(
35-
builder: &mut tar::Builder<impl Write>,
36-
src_path: impl AsRef<Path>,
37-
clamp_mtime: i64,
38-
) -> Result<()> {
39-
let src_path = src_path.as_ref();
40-
// Map (dev, inode) -> path for hardlinks
41-
let mut hardlinks: HashMap<(u64, u64), PathBuf> = HashMap::new();
42-
43-
for entry in WalkDir::new(src_path)
44-
.follow_links(false)
45-
.sort_by_file_name()
46-
.into_iter()
47-
{
48-
let entry = entry?;
49-
let meta = entry.metadata()?;
50-
// skip sockets as tar-rs errors when trying to archive them.
51-
// For comparison, umoci also errors, whereas docker skips them
52-
if meta.file_type().is_socket() {
53-
continue;
54-
}
55-
56-
let rel_path = pathdiff::diff_paths(entry.path(), src_path)
57-
.expect("walkdir returns path inside of search root");
58-
if rel_path == Path::new("") {
59-
continue;
60-
}
61-
62-
if entry.file_type().is_symlink() {
63-
if meta.mtime() > clamp_mtime {
64-
// Setting the mtime on a symlink is fiddly with tar-rs, so we use filetime to change
65-
// the mtime before adding the symlink to the tar archive
66-
let mtime = filetime::FileTime::from_unix_time(clamp_mtime, 0);
67-
filetime::set_symlink_file_times(entry.path(), mtime, mtime)?;
68-
}
69-
add_pax_extension_header(entry.path(), builder)?;
70-
builder.append_path_with_name(entry.path(), rel_path)?;
71-
} else if entry.file_type().is_file() || entry.file_type().is_dir() {
72-
add_pax_extension_header(entry.path(), builder)?;
73-
74-
// If this is a hardlink, add a link header instead of the file
75-
// if this isn't the first time we've seen this inode
76-
if meta.nlink() > 1 {
77-
match hardlinks.entry((meta.dev(), meta.ino())) {
78-
Entry::Occupied(e) => {
79-
// Add link header and continue to next entry
80-
let mut header = tar::Header::new_gnu();
81-
header.set_metadata(&meta);
82-
if meta.mtime() > clamp_mtime {
83-
header.set_mtime(clamp_mtime as u64);
84-
}
85-
header.set_entry_type(tar::EntryType::Link);
86-
header.set_cksum();
87-
builder.append_link(&mut header, &rel_path, e.get())?;
88-
continue;
89-
}
90-
Entry::Vacant(e) => {
91-
// This is the first time we've seen this inode
92-
e.insert(rel_path.clone());
93-
}
94-
}
95-
}
96-
97-
let mut header = tar::Header::new_gnu();
98-
header.set_size(meta.len());
99-
header.set_metadata(&meta);
100-
if meta.mtime() > clamp_mtime {
101-
header.set_mtime(clamp_mtime as u64);
102-
}
103-
if entry.file_type().is_file() {
104-
builder.append_data(
105-
&mut header,
106-
rel_path,
107-
&mut std::fs::File::open(entry.path())?,
108-
)?;
109-
} else {
110-
builder.append_data(&mut header, rel_path, &mut std::io::empty())?;
111-
};
112-
}
113-
}
114-
115-
Ok(())
116-
}
117-
11821
// Convert any extended attributes on the specified path to a tar PAX extension header, and add it to the tar archive
119-
fn add_pax_extension_header(
22+
pub(crate) fn add_pax_extension_header(
12023
path: impl AsRef<Path>,
12124
builder: &mut tar::Builder<impl Write>,
12225
) -> Result<(), anyhow::Error> {

0 commit comments

Comments
 (0)