diff options
author | Florian Klink <flokli@flokli.de> | 2022-12-29T20·37+0100 |
---|---|---|
committer | flokli <flokli@flokli.de> | 2023-01-03T13·03+0000 |
commit | 0b56d9f21bb5a857ee17ecc539c173926441a3fb (patch) | |
tree | ad7c5cb3f66f7a57b4e18c01df8a4b209d4cca0b /tvix/store/src/proto.rs | |
parent | ceb2c0ba895554c7cabb0ac20d3a80ea2aba1ab1 (diff) |
feat(src/proto): add PathInfo.validate() r/5570
This provides validation of PathInfo messages, and ensures the output hashes are properly parsed from the root node names. NixPath already has a more extensive test suite for various wrong NixPaths, so it's omitted from here. Change-Id: I5d69118df5816daabb521ddb19d178bddd1caacf Reviewed-on: https://cl.tvl.fyi/c/depot/+/7684 Reviewed-by: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
Diffstat (limited to 'tvix/store/src/proto.rs')
-rw-r--r-- | tvix/store/src/proto.rs | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/tvix/store/src/proto.rs b/tvix/store/src/proto.rs index 75e5c34ab137..f2af69ab0dbb 100644 --- a/tvix/store/src/proto.rs +++ b/tvix/store/src/proto.rs @@ -5,6 +5,8 @@ use thiserror::Error; use prost::Message; +use crate::nixpath::{NixPath, ParseNixPathError}; + tonic::include_proto!("tvix.store.v1"); #[cfg(feature = "reflection")] @@ -30,6 +32,27 @@ pub enum ValidateDirectoryError { InvalidDigestLen(usize), } +/// Errors that can occur during the validation of PathInfo messages. +#[derive(Debug, Error, PartialEq)] +pub enum ValidatePathInfoError { + /// No node present + #[error("No node present")] + NoNodePresent(), + + /// Invalid node name encountered. + #[error("{0} is an invalid node name: {1}")] + InvalidNodeName(String, ParseNixPathError), + + /// The digest the (root) node refers to has invalid length. + #[error("Invalid Digest length: {0}")] + InvalidDigestLen(usize), + + /// The number of references in the narinfo.reference_names field does not match + /// the number of references in the .references field. + #[error("Inconsistent Number of References: {0} (references) vs {0} (narinfo)")] + InconsistentNumberOfReferences(usize, usize), +} + /// Checks a Node name for validity as an intermediate node, and returns an /// error that's generated from the supplied constructor. /// @@ -51,6 +74,82 @@ fn validate_digest<E>(digest: &Vec<u8>, err: fn(usize) -> E) -> Result<(), E> { Ok(()) } +/// Parses a root node name. +/// +/// On success, this returns the parsed [NixPath]. +/// On error, it returns an error generated from the supplied constructor. +fn parse_node_name_root<E>( + name: &str, + err: fn(String, ParseNixPathError) -> E, +) -> Result<NixPath, E> { + match NixPath::from_string(name) { + Ok(np) => Ok(np), + Err(e) => Err(err(name.to_string(), e)), + } +} + +impl PathInfo { + /// validate performs some checks on the PathInfo struct, + /// Returning either a [NixPath] of the root node, or a + /// [ValidatePathInfoError]. + pub fn validate(&self) -> Result<NixPath, ValidatePathInfoError> { + // If there is a narinfo field populated, ensure the number of references there + // matches PathInfo.references count. + if let Some(narinfo) = &self.narinfo { + if narinfo.reference_names.len() != self.references.len() { + return Err(ValidatePathInfoError::InconsistentNumberOfReferences( + narinfo.reference_names.len(), + self.references.len(), + )); + } + } + // FUTUREWORK: parse references in reference_names. ensure they start + // with storeDir, and use the same digest as in self.references. + + // Ensure there is a (root) node present, and it properly parses to a NixPath. + let root_nix_path = match &self.node { + None => { + return Err(ValidatePathInfoError::NoNodePresent()); + } + Some(Node { node }) => match node { + None => { + return Err(ValidatePathInfoError::NoNodePresent()); + } + Some(node::Node::Directory(directory_node)) => { + // ensure the digest has the appropriate size. + validate_digest( + &directory_node.digest, + ValidatePathInfoError::InvalidDigestLen, + )?; + + // parse the name + parse_node_name_root( + &directory_node.name, + ValidatePathInfoError::InvalidNodeName, + )? + } + Some(node::Node::File(file_node)) => { + // ensure the digest has the appropriate size. + validate_digest(&file_node.digest, ValidatePathInfoError::InvalidDigestLen)?; + + // parse the name + parse_node_name_root(&file_node.name, ValidatePathInfoError::InvalidNodeName)? + } + Some(node::Node::Symlink(symlink_node)) => { + // parse the name + parse_node_name_root( + &symlink_node.name, + ValidatePathInfoError::InvalidNodeName, + )? + } + }, + }; + + // return the root nix path + Ok(root_nix_path) + } +} + /// Accepts a name, and a mutable reference to the previous name. /// If the passed name is larger than the previous one, the reference is updated. /// If it's not, an error is returned. |