From c6741d8603892ad23088d847e6ce71be7ebccb5a Mon Sep 17 00:00:00 2001 From: Gilbert Gilb's Date: Mon, 18 Nov 2024 00:52:46 +0100 Subject: [PATCH 1/4] fix: prevent overwriting hot repository on init (#353) This commit ensures that the hot repository won't get overwritten during init when the non-hot repository does not exist. --- **Motivation:** During my setup of rustic, I used a bad cold repository reference by mistake. This led rustic to corrupt my hot repository, which I thought it should have prevented. I guess there may be other places we'd want to detect and safeguard against hot/cold inconsistencies, but I've not encountered them and I'm not proficient enough with the codebase yet to go with a broader change confidently. **Old behavior:** ```console $ --repo ./cold --repo-hot ./hot --password 'rustic' init [INFO] using no config file, none of these exist: /home/myself/.config/rustic/rustic.toml, /etc/rustic/rustic.toml, ./rustic.toml [INFO] key 70fde4b5 successfully added. [INFO] repository 7e37c018 successfully created. [INFO] using cache at /home/myself/.cache/rustic/7e37c0182ecf9122ac29094aba7a2456e2f4fcd8901de5548ba8d1f2cfbf424e $ sha256sum hot/config af78a9385084d3734fe4bba280449266c0a151ab6390baf6b684122e09f76c2a hot/config $ rustic --repo ./cold2 --repo-hot ./hot --password 'rustic' init [INFO] using no config file, none of these exist: /home/myself/.config/rustic/rustic.toml, /etc/rustic/rustic.toml, ./rustic.toml [INFO] key 7455ca1b successfully added. [INFO] repository f6b5c80d successfully created. [INFO] using cache at /home/myself/.cache/rustic/f6b5c80d9aea1e279cb76f7f3855d563f277fa43884300738fe957a8051c54ff $ sha256sum hot/config 28a41dfbc7653c4f2aea443a94bfc5a665d120969bd38ccf663f6f1da505d7a0 hot/config ``` New behavior: ```console $ rustic --repo ./cold --repo-hot ./hot --password 'rustic' init [INFO] using no config file, none of these exist: /home/myself/.config/rustic/rustic.toml, /etc/rustic/rustic.toml, ./rustic.toml [INFO] key 60190cbb successfully added. [INFO] repository e5ce7241 successfully created. [INFO] using cache at /home/myself/.cache/rustic/e5ce7241a24254e7719865d60db0f0a939a5cdc21e1be9176856e5d385d70dfc $ rustic --repo ./cold2 --repo-hot ./hot --password 'rustic' init [INFO] using no config file, none of these exist: /home/myself/.config/rustic/rustic.toml, /etc/rustic/rustic.toml, ./rustic.toml error: Config file already exists. Aborting. ``` Co-authored-by: aawsome <37850842+aawsome@users.noreply.github.com> --- crates/core/src/repository.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/crates/core/src/repository.rs b/crates/core/src/repository.rs index a94f108b..d15d8f21 100644 --- a/crates/core/src/repository.rs +++ b/crates/core/src/repository.rs @@ -418,8 +418,25 @@ impl Repository { /// /// The id of the config file or `None` if no config file is found pub fn config_id(&self) -> RusticResult> { - let config_ids = self.be.list(FileType::Config)?; + self.config_id_with_backend(&self.be) + } + /// Returns the Id of the config file corresponding to a specific backend. + /// + /// # Errors + /// + /// * If listing the repository config file failed + /// * If there is more than one repository config file. + /// + /// # Arguments + /// + /// * `be` - The backend to use + /// + /// # Returns + /// + /// The id of the config file or `None` if no config file is found + fn config_id_with_backend(&self, be: &dyn WriteBackend) -> RusticResult> { + let config_ids = be.list(FileType::Config)?; match config_ids.len() { 1 => Ok(Some(ConfigId::from(config_ids[0]))), 0 => Ok(None), @@ -574,7 +591,12 @@ impl Repository { key_opts: &KeyOptions, config_opts: &ConfigOptions, ) -> RusticResult> { - if self.config_id()?.is_some() { + let config_exists = self.config_id_with_backend(&self.be)?.is_some(); + let hot_config_exists = match self.be_hot { + None => false, + Some(ref be) => self.config_id_with_backend(be)?.is_some(), + }; + if config_exists || hot_config_exists { return Err(RusticError::new( ErrorKind::Configuration, "Config file already exists for `{name}`. Please check the repository.", From 064ee26bf77e0b9bb86ef72a2525f40ec05c1cf6 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 18 Nov 2024 03:20:03 +0100 Subject: [PATCH 2/4] fix(ci): comment out unsupported cross-compilation targets for `aws-lc-sys` Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- .github/workflows/ci-heavy.yml | 33 +++++++++++++++++++++------------ build-dependencies.just | 5 +++++ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci-heavy.yml b/.github/workflows/ci-heavy.yml index 541ae94c..d3260452 100644 --- a/.github/workflows/ci-heavy.yml +++ b/.github/workflows/ci-heavy.yml @@ -174,11 +174,12 @@ jobs: target: x86_64-pc-windows-msvc architecture: x86_64 use-cross: false - - os: windows-latest - os-name: windows - target: x86_64-pc-windows-gnu - architecture: x86_64 - use-cross: false + # FIXME: `aws-lc-sys` doesn't cross compile + # - os: windows-latest + # os-name: windows + # target: x86_64-pc-windows-gnu + # architecture: x86_64 + # use-cross: false - os: macos-13 os-name: macos target: x86_64-apple-darwin @@ -209,16 +210,24 @@ jobs: target: i686-unknown-linux-gnu architecture: i686 use-cross: true - - os: ubuntu-latest - os-name: netbsd - target: x86_64-unknown-netbsd - architecture: x86_64 - use-cross: true + # Check because of Container images for rustic-rs - os: ubuntu-latest os-name: linux - target: armv7-unknown-linux-gnueabihf - architecture: armv7 + target: aarch64-unknown-linux-musl + architecture: arm64 use-cross: true + # FIXME: `aws-lc-sys` doesn't cross compile + # - os: ubuntu-latest + # os-name: netbsd + # target: x86_64-unknown-netbsd + # architecture: x86_64 + # use-cross: true + # FIXME: `aws-lc-sys` doesn't cross compile + # - os: ubuntu-latest + # os-name: linux + # target: armv7-unknown-linux-gnueabihf + # architecture: armv7 + # use-cross: true steps: - name: Checkout repository diff --git a/build-dependencies.just b/build-dependencies.just index 3701f839..eefb9548 100644 --- a/build-dependencies.just +++ b/build-dependencies.just @@ -4,3 +4,8 @@ install-default-x86_64-unknown-linux-musl: sudo apt-get update sudo apt-get install -y musl-tools + +# Install dependencies for the default feature on aarch64-unknown-linux-musl +install-default-aarch64-unknown-linux-musl: + sudo apt-get update + sudo apt-get install -y musl-tools From 20484839831bc40d57f5d28c37679e1402a5f7a3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 02:43:56 +0000 Subject: [PATCH 3/4] chore(deps): lock file maintenance (#357) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Update | Change | |---|---| | lockFileMaintenance | All locks refreshed | 🔧 This Pull Request updates lock files to use the latest dependency versions. --- ### Configuration 📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Never, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/rustic-rs/rustic_core). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: simonsan <14062932+simonsan@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a2bf122..e0e9370f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2381,9 +2381,9 @@ checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" [[package]] name = "mockall" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" dependencies = [ "cfg-if", "downcast", @@ -2395,9 +2395,9 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" dependencies = [ "cfg-if", "proc-macro2", From 749879f4d119c940d1c1515b39e1a261d9e341e2 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 18 Nov 2024 04:15:47 +0100 Subject: [PATCH 4/4] refactor(error): improve error messages and file handling (#334) - Create parent directory if it does not exist before opening the file for writing. Fixes https://github.com/rustic-rs/rustic/issues/1315 Fixes #310 --------- Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- crates/backend/src/local.rs | 46 +++++++++++++++++++++++++++++-- crates/core/src/commands/check.rs | 18 ++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/crates/backend/src/local.rs b/crates/backend/src/local.rs index 02cf6c5d..e20f27cc 100644 --- a/crates/backend/src/local.rs +++ b/crates/backend/src/local.rs @@ -169,6 +169,21 @@ impl LocalBackend { } Ok(()) } + + /// Returns the parent path of the given file type and id. + /// + /// # Arguments + /// + /// * `tpe` - The type of the file. + /// * `id` - The id of the file. + /// + /// # Returns + /// + /// The parent path of the file or `None` if the file does not have a parent. + fn parent_path(&self, tpe: FileType, id: &Id) -> Option { + let path = self.path(tpe, id); + path.parent().map(Path::to_path_buf) + } } impl ReadBackend for LocalBackend { @@ -355,13 +370,14 @@ impl ReadBackend for LocalBackend { length: u32, ) -> RusticResult { trace!("reading tpe: {tpe:?}, id: {id}, offset: {offset}, length: {length}"); - let mut file = File::open(self.path(tpe, id)).map_err(|err| { + let filename = self.path(tpe, id); + let mut file = File::open(filename.clone()).map_err(|err| { RusticError::with_source( ErrorKind::Backend, "Failed to open the file `{path}`. Please check the file and try again.", err, ) - .attach_context("path", self.path(tpe, id).to_string_lossy()) + .attach_context("path", filename.to_string_lossy()) })?; _ = file.seek(SeekFrom::Start(offset.into())).map_err(|err| { RusticError::with_source( @@ -459,6 +475,10 @@ impl WriteBackend for LocalBackend { /// * If the length of the file could not be set. /// * If the bytes could not be written to the file. /// * If the OS Metadata could not be synced to disk. + /// * If the file does not have a parent directory. + /// * If the parent directory could not be created. + /// * If the file cannot be opened, due to missing permissions. + /// * If the file cannot be written to, due to lack of space on the disk. fn write_bytes( &self, tpe: FileType, @@ -469,6 +489,28 @@ impl WriteBackend for LocalBackend { trace!("writing tpe: {:?}, id: {}", &tpe, &id); let filename = self.path(tpe, id); + let Some(parent) = self.parent_path(tpe, id) else { + return Err( + RusticError::new( + ErrorKind::Backend, + "The file `{path}` does not have a parent directory. This may be empty or a root path. Please check the file and try again.", + ) + .attach_context("path", filename.display().to_string()) + .ask_report() + ); + }; + + // create parent directory if it does not exist + fs::create_dir_all(parent.clone()).map_err(|err| { + RusticError::with_source( + ErrorKind::InputOutput, + "Failed to create directories `{path}`. Does the directory already exist? Please check the file and try again.", + err, + ) + .attach_context("path", parent.display().to_string()) + .ask_report() + })?; + let mut file = fs::OpenOptions::new() .create(true) .truncate(true) diff --git a/crates/core/src/commands/check.rs b/crates/core/src/commands/check.rs index ca92a12c..7d0668ef 100644 --- a/crates/core/src/commands/check.rs +++ b/crates/core/src/commands/check.rs @@ -306,10 +306,24 @@ pub(crate) fn check_repository( packs.into_par_iter().for_each(|pack| { let id = pack.id; - let data = be.read_full(FileType::Pack, &id).unwrap(); + let data = match be.read_full(FileType::Pack, &id) { + Ok(data) => data, + Err(err) => { + // FIXME: This needs different handling, now it prints a full display of RusticError + // Instead we should actually collect and return a list of errors on the happy path + // for `Check`, as this is a non-critical operation and we want to show all errors + // to the user. + error!("Error reading data for pack {id} : {err}"); + return; + } + }; match check_pack(be, pack, data, &p) { Ok(()) => {} - Err(err) => error!("Error reading pack {id} : {err}",), + // FIXME: This needs different handling, now it prints a full display of RusticError + // Instead we should actually collect and return a list of errors on the happy path + // for `Check`, as this is a non-critical operation and we want to show all errors + // to the user. + Err(err) => error!("Pack {id} is not valid: {err}",), } }); p.finish();