diff --git a/Cargo.lock b/Cargo.lock index 356d205..5f8d8cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,10 +28,35 @@ dependencies = [ "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", "serde_cbor 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.3.2" @@ -56,6 +81,14 @@ name = "cfg-if" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "failure" version = "0.1.5" @@ -76,6 +109,11 @@ dependencies = [ "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -86,6 +124,14 @@ name = "gcc" version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "getrandom" version = "0.1.11" @@ -111,6 +157,11 @@ name = "libc" version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ppv-lite86" version = "0.2.5" @@ -298,6 +349,17 @@ dependencies = [ "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "sha2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.15.44" @@ -352,6 +414,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.1.0" @@ -407,18 +474,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum backtrace 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "1371048253fa3bac6704bfd6bbfc922ee9bdcee8881330d40f308b81cc5adc55" "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" "checksum cc 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "8dae9c4b8fedcae85592ba623c4fd08cfdab3e3b72d6df780c6ead964a69bfff" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fc344b02d3868feb131e8b5fe2b9b0a1cc42942679af493061fc13b853243872" "checksum half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9353c2a89d550b58fa0061d8ed8d002a7d8cdf2494eb0e432859bd3a9e543836" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "175a40b9cf564ce9bf050654633dbf339978706b8ead1a907bb970b63185dd95" @@ -442,11 +516,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f" "checksum serde_cbor 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "318690c4f04ae6553665f3846c0614c9995bb1ea51a2f1c5c4b4ed338c248b49" "checksum serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "cb4dc18c61206b08dc98216c98faa0232f4337e1e1b8574551d5bad29ea1b425" +"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" diff --git a/Cargo.toml b/Cargo.toml index 6fbe2c2..c66f1b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ serde = { version = "1.0", features = ["derive"] } serde_cbor = "0.10" tempfile = "3.1" walkdir = "2.2" +sha2 = "0.8" diff --git a/src/index.rs b/src/index.rs index ceb497d..212eaae 100644 --- a/src/index.rs +++ b/src/index.rs @@ -8,6 +8,7 @@ use crate::repository_item::RepositoryItem; pub struct IndexItem { relative_path: String, original_source_path: String, + version: Box<[u8]>, } #[derive(Serialize, Deserialize)] @@ -56,7 +57,7 @@ impl Index { let original_source_path = Path::new(index_item.original_source_path.as_str()); let absolute_path = repository_path.join(relative_path); let absolute_path = absolute_path.as_path(); - RepositoryItem::from(original_source_path, absolute_path, relative_path) + RepositoryItem::from(original_source_path, absolute_path, relative_path, index_item.version) } } @@ -65,6 +66,7 @@ impl From for IndexItem { IndexItem { relative_path: i.relative_path().to_string_lossy().to_string(), original_source_path: i.original_source_path().to_string_lossy().to_string(), + version: i.version(), } } } diff --git a/src/lib.rs b/src/lib.rs index cbeb92d..8b827ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ pub mod backup; pub mod error; +pub mod repository; pub mod restore; pub mod source; mod index; -pub mod repository; mod repository_item; diff --git a/src/repository.rs b/src/repository.rs index abffe24..6667e07 100644 --- a/src/repository.rs +++ b/src/repository.rs @@ -4,7 +4,10 @@ use std::path::Path; use crate::error::BakareError; use crate::index::{Index, IndexIterator}; use crate::repository_item::RepositoryItem; +use sha2::Digest; +use sha2::Sha512; use std::fs::File; +use std::io::{BufReader, Read}; /// represents a place where backup is stored an can be restored from. /// right now only on-disk directory storage is supported @@ -66,20 +69,41 @@ impl<'a> Repository<'a> { if source_path.is_dir() { fs::create_dir(destination_path)?; } + if source_path.is_file() { println!("storing {} as {}", source_path.display(), destination_path.display()); fs::create_dir_all(destination_path.parent().unwrap())?; + let version = Repository::calculate_initial_version(source_path)?; fs::copy(source_path, destination_path)?; self.index.remember(RepositoryItem::from( source_path, destination_path, destination_path.strip_prefix(self.path)?, + Box::from(version), )); } Ok(()) } + fn calculate_initial_version(source_path: &Path) -> Result, BakareError> { + let source_file = File::open(source_path)?; + let mut reader = BufReader::new(source_file); + let mut buffer = Vec::with_capacity(1024); + let mut hasher = Sha512::new(); + + loop { + let count = reader.read(&mut buffer)?; + if count == 0 { + break; + } + hasher.input(&buffer); + } + + let version = hasher.result(); + Ok(Box::from(version.as_slice())) + } + pub fn item_by_source_path(&self, path: &Path) -> Result, BakareError> { println!( "trying to find {} in a repo [{}] of {} items", diff --git a/src/repository_item.rs b/src/repository_item.rs index 0894d57..2f1f8ac 100644 --- a/src/repository_item.rs +++ b/src/repository_item.rs @@ -1,4 +1,6 @@ use crate::error::BakareError; +use sha2::digest::DynDigest; +use sha2::{Digest, Sha512}; use std::fs; use std::path::Path; @@ -7,14 +9,16 @@ pub struct RepositoryItem { relative_path: Box, absolute_path: Box, original_source_path: Box, + version: Box<[u8]>, } impl RepositoryItem { - pub fn from(original_source_path: &Path, absolute_path: &Path, relative_path: &Path) -> Self { + pub fn from(original_source_path: &Path, absolute_path: &Path, relative_path: &Path, version: Box<[u8]>) -> Self { RepositoryItem { relative_path: Box::from(relative_path), absolute_path: Box::from(absolute_path), original_source_path: Box::from(original_source_path), + version, } } pub fn save(&self, save_to: &Path) -> Result<(), BakareError> { @@ -44,4 +48,8 @@ impl RepositoryItem { pub fn original_source_path(&self) -> &Path { &self.original_source_path } + + pub fn version(&self) -> Box<[u8]> { + self.version.clone() + } } diff --git a/tests/system_tests.rs b/tests/system_tests.rs index c77cbf3..c53390c 100644 --- a/tests/system_tests.rs +++ b/tests/system_tests.rs @@ -5,6 +5,7 @@ use bakare::source::TempSource; use bakare::error::BakareError; use bakare::repository::Repository; +use std::fs; use std::fs::File; use std::io::Read; use std::path::Path; @@ -12,7 +13,7 @@ use tempfile::tempdir; use walkdir::WalkDir; #[test] -fn restore_backed_up_files() -> Result<(), BakareError> { +fn restore_multiple_files() -> Result<(), BakareError> { let source = TempSource::new()?; source.write_text_to_file("first", "some contents")?; @@ -22,17 +23,44 @@ fn restore_backed_up_files() -> Result<(), BakareError> { assert_same_after_restore(source.path()) } +#[test] +fn restore_files_after_reopening_repository() -> Result<(), BakareError> { + let source = TempSource::new()?; + let repository_path = tempdir()?.into_path(); + let restore_target = tempdir()?.into_path(); + Repository::init(repository_path.as_path())?; + + let relative_path_text = "some file path"; + let original_contents = "some old contents"; + + { + let mut backup_repository = Repository::open(repository_path.as_path())?; + let mut backup_engine = backup::Engine::new(source.path(), &mut backup_repository); + source.write_text_to_file(relative_path_text, original_contents)?; + backup_engine.backup()?; + } + + { + let restore_repository = Repository::open(repository_path.as_path())?; + let restore_engine = restore::Engine::new(&restore_repository, &restore_target)?; + restore_engine.restore_all()?; + } + + let restored_file_path = restore_target.join(relative_path_text); + let contents = fs::read_to_string(restored_file_path)?; + + assert_eq!(contents, original_contents); + Ok(()) +} + #[test] fn restore_older_version_of_file() -> Result<(), BakareError> { let source = TempSource::new()?; let repository_path = tempdir()?.into_path(); - //let restore_repository = Repository::open(repository_path.as_path())?; + Repository::init(repository_path.as_path())?; let relative_path_text = "some path"; let file_path = source.file_path(relative_path_text); - let new_contents = "totally new contents"; - let restore_target = tempdir()?; - //let restore_engine = restore::Engine::new(&restore_repository, &restore_target.path())?; let old_contents = "some old contents"; { @@ -43,19 +71,23 @@ fn restore_older_version_of_file() -> Result<(), BakareError> { } let reading_repository = Repository::open(repository_path.as_path())?; - let file_id = reading_repository.item_by_source_path(&file_path)?; - assert!(file_id.is_some()); - //let file_id = file_id.unwrap(); - //let old_version = file_id.version(); + let item = reading_repository.item_by_source_path(&file_path)?; + assert!(item.is_some()); + let item = item.unwrap(); + let old_version = item.version(); { + let new_contents = "totally new contents"; let mut backup_repository = Repository::open(repository_path.as_path())?; let mut backup_engine = backup::Engine::new(source.path(), &mut backup_repository); source.write_text_to_file(relative_path_text, new_contents)?; backup_engine.backup()?; } - //restore_engine.restore_as_of_version(&file_id, old_version)?; + let restore_repository = Repository::open(repository_path.as_path())?; + let restore_target = tempdir()?; + let restore_engine = restore::Engine::new(&restore_repository, &restore_target.path())?; + //restore_engine.restore_as_of_version(&item, old_version)?; assert_target_file_contents(restore_target.path(), relative_path_text, old_contents) }