diff --git a/pkgar/src/bin.rs b/pkgar/src/bin.rs index 2296350c94775ec543e6b81a16a1d999b9062f6c..2c2f2d585220b5fcaeb6bcaa405d35bb80573d5a 100644 --- a/pkgar/src/bin.rs +++ b/pkgar/src/bin.rs @@ -226,9 +226,8 @@ pub fn extract( let mut package = PackageFile::new(archive_path, &pkey)?; - let mut transaction = Transaction::new(base_dir); - transaction.install(&mut package)?; - transaction.commit()?; + Transaction::install(&mut package, base_dir)? + .commit()?; Ok(()) } @@ -242,9 +241,8 @@ pub fn remove( let mut package = PackageFile::new(archive_path, &pkey)?; - let mut transaction = Transaction::new(base_dir); - transaction.remove(&mut package)?; - transaction.commit()?; + Transaction::remove(&mut package, base_dir)? + .commit()?; Ok(()) } diff --git a/pkgar/src/transaction.rs b/pkgar/src/transaction.rs index b3b87789a8140053c9384186383dc417a87a361a..620ab02685fb6f1ecc9e24a4ed0ef7bef9bb2fa1 100644 --- a/pkgar/src/transaction.rs +++ b/pkgar/src/transaction.rs @@ -52,32 +52,28 @@ impl Action { pub struct Transaction { actions: Vec<Action>, - basedir: PathBuf, } impl Transaction { - pub fn new(basedir: impl AsRef<Path>) -> Transaction { - Transaction { - actions: Vec::new(), - basedir: basedir.as_ref().to_path_buf(), - } - } - - pub fn install<Pkg>(&mut self, src: &mut Pkg) -> Result<(), Error> + pub fn install<Pkg>( + src: &mut Pkg, + base_dir: impl AsRef<Path> + ) -> Result<Transaction, Error> where Pkg: PackageSrc<Err = Error> + PackageSrcExt, { let mut buf = vec![0; READ_WRITE_HASH_BUF_SIZE]; let entries = src.read_entries()?; - self.actions.reserve(entries.len()); + let mut actions = Vec::with_capacity(entries.len()); for entry in entries { let relative_path = entry.check_path() .map_err(|e| e.path(src.path()) )?; - let target_path = self.basedir.join(relative_path); + let target_path = base_dir.as_ref().join(relative_path); //HELP: Under what circumstances could this ever fail? - assert!(target_path.starts_with(&self.basedir), "target path was not in the base path"); + assert!(target_path.starts_with(&base_dir), + "target path was not in the base path"); let tmp_path = temp_path(&target_path, entry.blake3())?; @@ -131,61 +127,76 @@ impl Transaction { entry.verify(entry_data_hash, entry_data_size) .map_err(|e| e.path(src.path()))?; - self.actions.push(Action::Rename(tmp_path, target_path)) + actions.push(Action::Rename(tmp_path, target_path)) } - Ok(()) + Ok(Transaction { + actions, + }) } - pub fn replace<Pkg>(&mut self, old: &mut Pkg, new: &mut Pkg) -> Result<(), Error> + pub fn replace<Pkg>( + old: &mut Pkg, + new: &mut Pkg, + base_dir: impl AsRef<Path>, + ) -> Result<Transaction, Error> where Pkg: PackageSrc<Err = Error> + PackageSrcExt, { let old_entries = old.read_entries()?; let new_entries = new.read_entries()?; // All the files that are present in old but not in new - let mut removes = old_entries.iter() + let mut actions = old_entries.iter() .filter(|old_e| new_entries.iter() .find(|new_e| new_e.blake3() == old_e.blake3() ) .is_none()) .map(|e| { - let target_path = self.basedir + let target_path = base_dir.as_ref() .join(e.check_path()?); Ok(Action::Remove(target_path)) }) .collect::<Result<Vec<Action>, Error>>()?; - self.actions.append(&mut removes); //TODO: Don't force a re-read of all the entries for the new package - self.install(new) + let mut trans = Transaction::install(new, base_dir)?; + trans.actions.append(&mut actions); + Ok(trans) } - pub fn remove<Pkg>(&mut self, pkg: &mut Pkg) -> Result<(), Error> + pub fn remove<Pkg>( + pkg: &mut Pkg, + base_dir: impl AsRef<Path> + ) -> Result<Transaction, Error> where Pkg: PackageSrc<Err = Error>, { let mut buf = vec![0; READ_WRITE_HASH_BUF_SIZE]; let entries = pkg.read_entries()?; - self.actions.reserve(entries.len()); + let mut actions = Vec::with_capacity(entries.len()); for entry in entries { let relative_path = entry.check_path()?; - let target_path = self.basedir.join(relative_path); + let target_path = base_dir.as_ref() + .join(relative_path); // Under what circumstances could this ever fail? - assert!(target_path.starts_with(&self.basedir), "target path was not in the base path"); + assert!(target_path.starts_with(&base_dir), + "target path was not in the base path"); let candidate = File::open(&target_path) .map_err(|e| Error::from(e).path(&target_path) )?; + // Ensure that the deletion candidate on disk has not been modified copy_and_hash(candidate, io::sink(), &mut buf) .map_err(|e| Error::from(e) .reason(format!("Hashing file for entry: '{}'", relative_path.display())) .path(&target_path) )?; - self.actions.push(Action::Remove(target_path)); + actions.push(Action::Remove(target_path)); } - Ok(()) + Ok(Transaction { + actions + }) } pub fn commit(&mut self) -> Result<usize, Error> { diff --git a/pkgar/tests/transaction.rs b/pkgar/tests/transaction.rs index d1bebaffc7a71d721959bbfad8ca139aa66cb5c8..474edbeafb4dc2671a2789cbee1469a6238f0294 100644 --- a/pkgar/tests/transaction.rs +++ b/pkgar/tests/transaction.rs @@ -54,8 +54,7 @@ fn build_install_update_remove() -> Result<(), Box<dyn Error>> { let mut src_pkg = PackageFile::new(tmp.file("pkgar-src-1.pkgar"), &pkey_file.pkey)?; println!("Install archive"); - let mut install = Transaction::new(tmp.dir("installroot")); - install.install(&mut src_pkg)?; + let mut install = Transaction::install(&mut src_pkg, tmp.dir("installroot"))?; install.commit()?; println!("Modify build"); @@ -70,13 +69,11 @@ fn build_install_update_remove() -> Result<(), Box<dyn Error>> { let mut src2_pkg = PackageFile::new(tmp.file("pkgar-src-2.pkgar"), &pkey_file.pkey)?; println!("Upgrade archive"); - let mut update = Transaction::new(tmp.dir("installroot")); - update.replace(&mut src_pkg, &mut src2_pkg)?; + let mut update = Transaction::replace(&mut src_pkg, &mut src2_pkg, tmp.dir("installroot"))?; update.commit()?; println!("Uninstall archive"); - let mut remove = Transaction::new(tmp.dir("installroot")); - remove.remove(&mut src2_pkg)?; + let mut remove = Transaction::remove(&mut src2_pkg, tmp.dir("installroot"))?; remove.commit()?; assert_eq!(fs::read_dir(tmp.dir("installroot"))?.count(), 0);