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/Cargo.lock b/Cargo.lock index 0b179d0e..9145600f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2361,9 +2361,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", @@ -2375,9 +2375,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", 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 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(); 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.",