Skip to content

Commit 0018400

Browse files
cgwaltersjeckersb
authored andcommitted
container-export: Handle metacopy=off overlay setups
This fixes a bug in `container export` on overlayfs setups with `metacopy=off` (a common setup). In that scenario we will see a single mountpoint, but different device+inode. Change hardlink tracking to account for device+inode. Assisted-by: OpenCode (Claude Opus 4) Signed-off-by: Colin Walters <walters@verbum.org>
1 parent 222ecc0 commit 0018400

File tree

1 file changed

+11
-30
lines changed

1 file changed

+11
-30
lines changed

crates/lib/src/container_export.rs

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -134,17 +134,12 @@ fn export_filesystem_walk<W: Write>(
134134
root_dir: &cap_std_ext::cap_std::fs::Dir,
135135
sepolicy: Option<&ostree::SePolicy>,
136136
) -> Result<()> {
137-
use cap_std_ext::cap_primitives::fs::MetadataExt;
138137
use std::path::Path;
139138

140-
// Get the device number of the root directory - we should never see a different device
141-
// since we use noxdev() which prevents crossing mount points
142-
let root_meta = root_dir.dir_metadata()?;
143-
let expected_dev = root_meta.dev();
144-
145-
// Track hardlinks: maps inode -> first path seen
146-
// We only track inode since all files must be on the same device
147-
let mut hardlinks: HashMap<u64, std::path::PathBuf> = HashMap::new();
139+
// Track hardlinks: maps (dev, inode) -> first path seen.
140+
// We key on (dev, ino) because overlay filesystems may present
141+
// different device numbers for directories vs regular files.
142+
let mut hardlinks: HashMap<(u64, u64), std::path::PathBuf> = HashMap::new();
148143

149144
// The target mount shouldn't have submounts, but just in case we use noxdev
150145
let walk_config = WalkConfiguration::default()
@@ -192,7 +187,6 @@ fn export_filesystem_walk<W: Write>(
192187
path,
193188
relative_path,
194189
sepolicy,
195-
expected_dev,
196190
&mut hardlinks,
197191
)
198192
.map_err(std::io::Error::other)?;
@@ -250,34 +244,21 @@ fn add_file_to_tar_from_walk<W: Write>(
250244
absolute_path: &std::path::Path,
251245
relative_path: &std::path::Path,
252246
sepolicy: Option<&ostree::SePolicy>,
253-
expected_dev: u64,
254-
hardlinks: &mut HashMap<u64, std::path::PathBuf>,
247+
hardlinks: &mut HashMap<(u64, u64), std::path::PathBuf>,
255248
) -> Result<()> {
256249
use cap_std_ext::cap_primitives::fs::{MetadataExt, PermissionsExt};
257250
use std::path::Path;
258251

259252
let filename_path = Path::new(filename);
260253
let metadata = dir.metadata(filename_path)?;
261254

262-
// Skip files on different devices (e.g., bind mounts in containers like /etc/hosts).
263-
// The noxdev() option prevents descending into directories on different devices,
264-
// but individual files can still be bind-mounted from other filesystems.
265-
let dev = metadata.dev();
266-
if dev != expected_dev {
267-
tracing::debug!(
268-
"Skipping file on different device: {} (expected dev {}, found {})",
269-
relative_path.display(),
270-
expected_dev,
271-
dev
272-
);
273-
return Ok(());
274-
}
275-
276-
// Check for hardlinks: if nlink > 1, this file may have other links
255+
// Check for hardlinks: if nlink > 1, this file may have other links.
256+
// We key on (dev, ino) because overlay filesystems may present
257+
// different device numbers for directories vs regular files.
277258
let nlink = metadata.nlink();
278259
if nlink > 1 {
279-
let ino = metadata.ino();
280-
if let Some(first_path) = hardlinks.get(&ino) {
260+
let key = (metadata.dev(), metadata.ino());
261+
if let Some(first_path) = hardlinks.get(&key) {
281262
// This is a hardlink to a file we've already written
282263
let mut header = tar_header_from_meta(tar::EntryType::Link, 0, &metadata);
283264

@@ -293,7 +274,7 @@ fn add_file_to_tar_from_walk<W: Write>(
293274
return Ok(());
294275
} else {
295276
// First time seeing this inode, record it
296-
hardlinks.insert(ino, relative_path.to_path_buf());
277+
hardlinks.insert(key, relative_path.to_path_buf());
297278
}
298279
}
299280

0 commit comments

Comments
 (0)