From b55d1f97ce8762711ff44f9b5695452cc8083c44 Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Tue, 14 Mar 2023 17:16:05 +0100 Subject: refactor(tvix/nix-compat): -derivation::Hash, +NixHash This stops using our own custom Hash structure, which was mostly only used because we had to parse the JSON representation somehow. Since cl/8217, there's a `NixHash` struct, which is better suited to hold this data. Converting the format requires a bit of serde labor though, but that only really matters when interacting with JSON representations (which we mostly don't). Change-Id: Idc5ee511e36e6726c71f66face8300a441b0bf4c Reviewed-on: https://cl.tvl.fyi/c/depot/+/8304 Autosubmit: flokli Tested-by: BuildkiteCI Reviewed-by: tazjin --- tvix/nix-compat/src/derivation/mod.rs | 67 ++++++++++++++++------------- tvix/nix-compat/src/derivation/output.rs | 38 +++++----------- tvix/nix-compat/src/derivation/tests/mod.rs | 19 +++++--- tvix/nix-compat/src/derivation/write.rs | 26 ++++++----- 4 files changed, 75 insertions(+), 75 deletions(-) (limited to 'tvix/nix-compat/src/derivation') diff --git a/tvix/nix-compat/src/derivation/mod.rs b/tvix/nix-compat/src/derivation/mod.rs index 2b549536b247..06b7ba02f4d8 100644 --- a/tvix/nix-compat/src/derivation/mod.rs +++ b/tvix/nix-compat/src/derivation/mod.rs @@ -1,5 +1,5 @@ use crate::{ - nixhash::NixHash, + nixhash::HashAlgo, store_path::{self, StorePath}, }; use serde::{Deserialize, Serialize}; @@ -17,8 +17,9 @@ mod write; mod tests; // Public API of the crate. +pub use crate::nixhash::{NixHash, NixHashWithMode}; pub use errors::{DerivationError, OutputError}; -pub use output::{Hash, Output}; +pub use output::Output; pub use utils::path_with_references; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] @@ -75,23 +76,22 @@ impl Derivation { buffer } - /// Returns the fixed output path and its hash - // (if the Derivation is fixed output), - /// or None if there is no fixed output. + /// Returns the fixed output path and its [NixHashWithMode] + /// (if the Derivation is fixed output), or None if there is no fixed output. /// This takes some shortcuts in case more than one output exists, as this /// can't be a valid fixed-output Derivation. - pub fn get_fixed_output(&self) -> Option<(&String, &Hash)> { + pub fn get_fixed_output(&self) -> Option<(&String, &NixHashWithMode)> { if self.outputs.len() != 1 { return None; } if let Some(out_output) = self.outputs.get("out") { - if let Some(out_output_hash) = &out_output.hash { + if let Some(out_output_hash) = &out_output.hash_with_mode { return Some((&out_output.path, out_output_hash)); } // There has to be a hash, otherwise it would not be FOD } - return None; + None } /// Returns the drv path of a Derivation struct. @@ -160,8 +160,6 @@ impl Derivation { /// `fixed:out:${algo}:${digest}:${fodPath}` string is hashed instead of /// the A-Term. /// - /// TODO: what's the representation of ${digest}? - /// /// If the derivation is not a fixed derivation, it's up to the caller of /// this function to provide a lookup function to lookup these calculation /// results of parent derivations at `fn_get_hash_derivation_modulo` (by @@ -175,8 +173,9 @@ impl Derivation { // Fixed-output derivations return a fixed hash Some((fixed_output_path, fixed_output_hash)) => { hasher.update(format!( - "fixed:out:{}:{}:{}", - &fixed_output_hash.algo, &fixed_output_hash.digest, fixed_output_path, + "fixed:out:{}:{}", + fixed_output_hash.to_nix_hash_string(), + fixed_output_path )); hasher.finalize() } @@ -196,7 +195,7 @@ impl Derivation { for (drv_path, output_names) in &self.input_derivations { replaced_input_derivations.insert( data_encoding::HEXLOWER - .encode(&fn_get_derivation_or_fod_hash(&drv_path).digest), + .encode(&fn_get_derivation_or_fod_hash(drv_path).digest), output_names.clone(), ); } @@ -255,7 +254,7 @@ impl Derivation { output_path_name.push_str(output_name); } - let s = &format!( + let fp = &format!( "output:{}:{}:{}:{}", output_name, derivation_or_fod_hash.to_nix_hash_string(), @@ -264,7 +263,7 @@ impl Derivation { ); let abs_store_path = - utils::build_store_path(false, s, &output_path_name)?.to_absolute_path(); + utils::build_store_path(false, fp, &output_path_name)?.to_absolute_path(); output.path = abs_store_path.clone(); self.environment @@ -277,33 +276,41 @@ impl Derivation { // footgun prevention mechanism. assert!(fixed_output_path.is_empty()); - let s = { - let mut s = String::new(); + let fp = { + let mut fp = String::new(); // Fixed-output derivation. - // There's two different hashing strategies in place, depending on the value of hash.algo. + // There's two different hashing strategies in place, + // depending on whether the hash is recursive AND sha256 or anything else. // This code is _weird_ but it is what Nix is doing. See: // https://github.com/NixOS/nix/blob/1385b2007804c8a0370f2a6555045a00e34b07c7/src/libstore/store-api.cc#L178-L196 - if fixed_output_hash.algo == "r:sha256" { - s.push_str(&format!( - "source:sha256:{}", - fixed_output_hash.digest, // lowerhex - )); + if let NixHashWithMode::Recursive(recursive_hash) = fixed_output_hash { + if recursive_hash.algo == HashAlgo::Sha256 { + fp.push_str(&format!("source:{}", recursive_hash.to_nix_hash_string())); + } else { + // This is similar to the FOD case, with an empty fixed_output_path. + fp.push_str(&format!( + "output:out:{}", + derivation_or_fod_hash.to_nix_hash_string(), + )); + } } else { - s.push_str("output:out:"); - // This is drv_replacement for FOD, with an empty fixed_output_path. - s.push_str(&derivation_or_fod_hash.to_nix_hash_string()); + // This is similar to the FOD case, with an empty fixed_output_path. + fp.push_str(&format!( + "output:out:{}", + derivation_or_fod_hash.to_nix_hash_string(), + )); } - s.push_str(&format!(":{}:{}", store_path::STORE_DIR, name)); - s + fp.push_str(&format!(":{}:{}", store_path::STORE_DIR, name)); + fp }; - let abs_store_path = utils::build_store_path(false, &s, name)?.to_absolute_path(); + let abs_store_path = utils::build_store_path(false, &fp, name)?.to_absolute_path(); self.outputs.insert( "out".to_string(), Output { path: abs_store_path.clone(), - hash: Some(fixed_output_hash.clone()), + hash_with_mode: Some(fixed_output_hash.clone()), }, ); self.environment.insert("out".to_string(), abs_store_path); diff --git a/tvix/nix-compat/src/derivation/output.rs b/tvix/nix-compat/src/derivation/output.rs index 39c0dbde5a23..4bfc7bf8014d 100644 --- a/tvix/nix-compat/src/derivation/output.rs +++ b/tvix/nix-compat/src/derivation/output.rs @@ -1,5 +1,6 @@ use crate::derivation::OutputError; -use crate::{nixbase32, store_path::StorePath}; +use crate::nixhash::{HashAlgo, NixHashWithMode}; +use crate::store_path::StorePath; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] @@ -7,42 +8,23 @@ pub struct Output { pub path: String, #[serde(flatten)] - pub hash: Option, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub struct Hash { - #[serde(rename = "hash")] - pub digest: String, - #[serde(rename = "hashAlgo")] - pub algo: String, + pub hash_with_mode: Option, } impl Output { pub fn is_fixed(&self) -> bool { - self.hash.is_some() + self.hash_with_mode.is_some() } pub fn validate(&self, validate_output_paths: bool) -> Result<(), OutputError> { - if let Some(hash) = &self.hash { - // try to decode digest - let result = nixbase32::decode(hash.digest.as_bytes()); - match result { - Err(e) => return Err(OutputError::InvalidHashEncoding(hash.digest.clone(), e)), - Ok(digest) => { - if hash.algo != "sha1" && hash.algo != "sha256" { - return Err(OutputError::InvalidHashAlgo(hash.algo.to_string())); - } - if (hash.algo == "sha1" && digest.len() != 20) - || (hash.algo == "sha256" && digest.len() != 32) - { - return Err(OutputError::InvalidDigestSizeForAlgo( - digest.len(), - hash.algo.to_string(), - )); + if let Some(hash) = &self.hash_with_mode { + match hash { + NixHashWithMode::Flat(h) | NixHashWithMode::Recursive(h) => { + if h.algo != HashAlgo::Sha1 || h.algo != HashAlgo::Sha256 { + return Err(OutputError::InvalidHashAlgo(h.algo.to_string())); } } - }; + } } if validate_output_paths { if let Err(e) = StorePath::from_absolute_path(&self.path) { diff --git a/tvix/nix-compat/src/derivation/tests/mod.rs b/tvix/nix-compat/src/derivation/tests/mod.rs index 7f94f5e690ce..d7b63a45ad9c 100644 --- a/tvix/nix-compat/src/derivation/tests/mod.rs +++ b/tvix/nix-compat/src/derivation/tests/mod.rs @@ -1,5 +1,6 @@ -use crate::derivation::output::{Hash, Output}; +use crate::derivation::output::Output; use crate::derivation::Derivation; +use crate::nixhash::NixHash; use crate::store_path::StorePath; use std::collections::BTreeSet; use std::fs::File; @@ -218,11 +219,15 @@ fn output_path_construction() { "out".to_string(), Output { path: "".to_string(), // will be calculated - hash: Some(Hash { - digest: "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba" - .to_string(), - algo: "r:sha256".to_string(), - }), + hash_with_mode: Some(crate::nixhash::NixHashWithMode::Recursive(NixHash { + digest: data_encoding::HEXLOWER + .decode( + "08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba" + .as_bytes(), + ) + .unwrap(), + algo: crate::nixhash::HashAlgo::Sha256, + })), }, ); @@ -271,7 +276,7 @@ fn output_path_construction() { "out".to_string(), Output { path: "".to_string(), // will be calculated - hash: None, + hash_with_mode: None, }, ); diff --git a/tvix/nix-compat/src/derivation/write.rs b/tvix/nix-compat/src/derivation/write.rs index 9423ef2e6af4..52166294e078 100644 --- a/tvix/nix-compat/src/derivation/write.rs +++ b/tvix/nix-compat/src/derivation/write.rs @@ -58,16 +58,22 @@ pub fn write_outputs( let mut elements: Vec<&str> = vec![output_name, &output.path]; - match &output.hash { - Some(hash) => { - elements.push(&hash.algo); - elements.push(&hash.digest); - } - None => { - elements.push(""); - elements.push(""); - } - } + let (e2, e3) = match &output.hash_with_mode { + Some(hash) => match hash { + crate::nixhash::NixHashWithMode::Flat(h) => ( + h.algo.to_string(), + data_encoding::HEXLOWER.encode(&h.digest), + ), + crate::nixhash::NixHashWithMode::Recursive(h) => ( + format!("r:{}", h.algo), + data_encoding::HEXLOWER.encode(&h.digest), + ), + }, + None => ("".to_string(), "".to_string()), + }; + + elements.push(&e2); + elements.push(&e3); write_array_elements( writer, -- cgit 1.4.1