struct HardlinkState {
names: Vec<HardlinkPath>,
source_ino: u64,
+ source_dev: u64,
mapped_ino: u32,
nlink: u32,
seen: u32,
}
-struct DevState {
- dev: u64,
- hls: Vec<HardlinkState>,
-}
-
struct ArchiveProperties {
// first inode number to use. @ArchiveState.ino increments from this.
initial_ino: u32,
}
struct ArchiveState {
- // 2d dev + inode vector serves two purposes:
- // - dev index provides reproducible major,minor values
- // - inode@dev provides hardlink state tracking
- ids: Vec<DevState>,
+ // dev + inode provides hardlink state tracking
+ hls: Vec<HardlinkState>,
// offset from the start of this archive
off: u64,
// next mapped inode number, used instead of source file inode numbers to
- // ensure reproducibility. XXX: should track inode per mapped dev?
+ // ensure reproducibility. Inode numbers all share the same dev (major=0
+ // minor=0) namespace.
ino: u32,
}
impl ArchiveState {
pub fn new(ino_start: u32) -> ArchiveState {
ArchiveState {
- ids: Vec::new(),
+ hls: Vec::new(),
off: 0,
ino: ino_start,
}
}
- // lookup or create DevState for @dev. Return @major/@minor based on index
- pub fn dev_seen(&mut self, dev: u64) -> Option<(u32, u32)> {
- let index: u64 = match self.ids.iter().position(|i| i.dev == dev) {
- Some(idx) => idx.try_into().ok()?,
- None => {
- self.ids.push(DevState {
- dev: dev,
- hls: Vec::new(),
- });
- (self.ids.len() - 1).try_into().ok()?
- }
- };
-
- let major: u32 = (index >> 32).try_into().unwrap();
- let minor: u32 = (index & u64::from(u32::MAX)).try_into().unwrap();
- Some((major, minor))
- }
-
// Check whether we've already seen this hardlink's dev/inode combination.
// If already seen, fill the existing mapped_ino.
// Return true if this entry has been deferred (seen != nlinks)
&mut self,
props: &ArchiveProperties,
mut writer: W,
- major: u32,
- minor: u32,
md: fs::Metadata,
inpath: &Path,
outpath: &Path,
mapped_nlink: &mut Option<u32>,
) -> std::io::Result<bool> {
assert!(md.nlink() > 1);
- let index = u64::from(major) << 32 | u64::from(minor);
- // reverse index->major/minor conversion that was just done
- let devstate: &mut DevState = &mut self.ids[index as usize];
- let (_index, hl) = match devstate
- .hls
+ let (_index, hl) = match self.hls
.iter_mut()
.enumerate()
- .find(|(_, hl)| hl.source_ino == md.ino())
+ .find(|(_, hl)| hl.source_ino == md.ino() && hl.source_dev == md.dev())
{
Some(hl) => hl,
None => {
- devstate.hls.push(HardlinkState {
+ self.hls.push(HardlinkState {
names: vec![HardlinkPath {
infile: inpath.to_path_buf(),
outfile: outpath.to_path_buf(),
}],
+ source_dev: md.dev(),
source_ino: md.ino(),
mapped_ino: self.ino,
nlink: md.nlink().try_into().unwrap(), // pre-checked
None => md.mtime().try_into().unwrap(),
},
filesize = 0,
- major = major,
- minor = minor,
+ major = 0,
+ minor = 0,
rmajor = 0,
rminor = 0,
namesize = fname.len() + 1,
// GNU cpio: if a name is given multiple times, exceeding nlink, then
// subsequent names continue to be packed (with a repeat data segment),
// using the same mapped inode.
- dout!("resetting hl at index {}", index);
+ dout!("resetting hl with dev {} ino {}", hl.source_dev, hl.source_ino);
hl.seen = 0;
hl.names.clear();
};
dout!("archiving {} with mode {:o}", outpath.display(), md.mode());
- let (major, minor) = match state.dev_seen(md.dev()) {
- Some((maj, min)) => (maj, min),
- None => return Err(io::Error::new(io::ErrorKind::Other, "failed to map dev")),
- };
-
if md.nlink() > u32::MAX as u64 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
let deferred = state.hardlink_seen(
&props,
&mut writer,
- major,
- minor,
md.clone(),
&inpath,
outpath,
},
mtime = mtime,
filesize = datalen,
- major = major,
- minor = minor,
+ major = 0,
+ minor = 0,
rmajor = rmajor,
rminor = rminor,
namesize = fname.len() + 1 + data_align_seek as usize,
mut writer: W,
) -> std::io::Result<()> {
let mut deferred_inpaths: Vec<PathBuf> = Vec::new();
- for id in state.ids.iter_mut() {
- for hl in id.hls.iter_mut() {
- if hl.seen == 0 || hl.seen == hl.nlink {
- dout!("HardlinkState complete with seen {}", hl.seen);
- continue;
- }
- dout!(
- "pending HardlinkState with seen {} != nlinks {}",
- hl.seen,
- hl.nlink
- );
+ for hl in state.hls.iter_mut() {
+ if hl.seen == 0 || hl.seen == hl.nlink {
+ dout!("HardlinkState complete with seen {}", hl.seen);
+ continue;
+ }
+ dout!(
+ "pending HardlinkState with seen {} != nlinks {}",
+ hl.seen,
+ hl.nlink
+ );
- while hl.names.len() > 0 {
- let path = hl.names.pop().unwrap();
- deferred_inpaths.push(path.infile);
- }
- // ensure that data segment gets added on archive_path recall
- hl.nlink = hl.seen;
- hl.seen = 0;
- // existing allocated inode should be used
+ while hl.names.len() > 0 {
+ let path = hl.names.pop().unwrap();
+ deferred_inpaths.push(path.infile);
}
+ // ensure that data segment gets added on archive_path recall
+ hl.nlink = hl.seen;
+ hl.seen = 0;
+ // existing allocated inode should be used
}
if deferred_inpaths.len() > 0 {