feat(tvix/store): add as_narinfo() for PathInfo

This allows seeing a PathInfo as a nix_compat::narinfo::NarInfo<'_>.

It doesn't allocate any new data, but the NarInfo<'_> view allows us to
access things like signature verification, or rendering out
(alternations of this) as strings.

Change-Id: Id0d8d7feeb626ee02c3d8a4932f24ace77022619
Reviewed-on: https://cl.tvl.fyi/c/depot/+/10108
Reviewed-by: edef <edef@edef.eu>
Autosubmit: flokli <flokli@flokli.de>
Tested-by: BuildkiteCI
This commit is contained in:
Florian Klink 2023-11-22 21:19:18 +02:00 committed by flokli
parent 639cca3e22
commit 6b685ec4a5
2 changed files with 106 additions and 2 deletions

View file

@ -3,8 +3,9 @@ use bytes::Bytes;
use data_encoding::BASE64;
// https://github.com/hyperium/tonic/issues/1056
use nix_compat::{
narinfo::Flags,
nixhash::{CAHash, NixHash},
store_path,
store_path::{self, StorePathRef},
};
use thiserror::Error;
use tvix_castore::proto::{self as castorepb, NamedNode, ValidateNodeError};
@ -174,6 +175,67 @@ impl PathInfo {
// return the root nix path
Ok(root_nix_path)
}
/// With self and a given StorePathRef, this reconstructs a
/// [nix_compat::narinfo::NarInfo<'_>].
/// It can be used to validate Signatures, or get back a (sparse) NarInfo
/// struct to prepare writing it out.
///
/// This doesn't allocate any new data.
///
/// Keep in mind this is not able to reconstruct all data present in the
/// NarInfo<'_>, as some of it is not stored at all:
/// - the `system`, `file_hash` and `file_size` fields are set to `None`.
/// - the URL is set to an empty string.
/// - Compression is set to "none"
///
/// If you want to render it out to a string and be able to parse it back
/// in, at least URL *must* be set again.
pub fn as_narinfo<'a>(
&'a self,
store_path: store_path::StorePathRef<'a>,
) -> Option<nix_compat::narinfo::NarInfo<'_>> {
let narinfo = &self.narinfo.as_ref()?;
Some(nix_compat::narinfo::NarInfo {
flags: Flags::empty(),
store_path,
nar_hash: narinfo.nar_sha256.to_vec().try_into().unwrap(),
nar_size: narinfo.nar_size,
references: narinfo
.reference_names
.iter()
.map(|ref_name| {
// This shouldn't pass validation
StorePathRef::from_bytes(ref_name.as_bytes()).expect("invalid reference")
})
.collect(),
signatures: narinfo
.signatures
.iter()
.map(|sig| {
nix_compat::narinfo::Signature::new(
&sig.name,
// This shouldn't pass validation
sig.data[..].try_into().expect("invalid signature len"),
)
})
.collect(),
ca: narinfo
.ca
.as_ref()
.map(|ca| ca.try_into().expect("invalid ca")),
system: None,
deriver: narinfo.deriver.as_ref().map(|deriver| {
StorePathRef::from_name_and_digest(&deriver.name, &deriver.digest)
.expect("invalid deriver")
}),
url: "",
compression: Some("none"),
file_hash: None,
file_size: None,
})
}
}
/// Errors that can occur when converting from a [&nar_info::Ca] to a (stricter)

View file

@ -3,7 +3,7 @@ use crate::tests::fixtures::*;
use bytes::Bytes;
use data_encoding::BASE64;
use nix_compat::nixbase32;
use nix_compat::store_path::{self, StorePath};
use nix_compat::store_path::{self, StorePath, StorePathRef};
use std::str::FromStr;
use test_case::test_case;
use tvix_castore::proto as castorepb;
@ -404,3 +404,45 @@ CA: fixed:sha256:086vqwk2wl8zfs47sq2xpjc9k066ilmb8z6dn0q6ymwjzlm196cd"#
(&narinfo_parsed).into()
);
}
/// Exercise .as_narinfo() on a PathInfo and ensure important fields are preserved..
#[test]
fn as_narinfo() {
let narinfo_parsed = nix_compat::narinfo::NarInfo::parse(
r#"StorePath: /nix/store/pa10z4ngm0g83kx9mssrqzz30s84vq7k-hello-2.12.1.tar.gz
URL: nar/1zjrhzhaizsrlsvdkqfl073vivmxcqnzkff4s50i0cdf541ary1r.nar.xz
Compression: xz
FileHash: sha256:1zjrhzhaizsrlsvdkqfl073vivmxcqnzkff4s50i0cdf541ary1r
FileSize: 1033524
NarHash: sha256:1lvqpbk2k1sb39z8jfxixf7p7v8sj4z6mmpa44nnmff3w1y6h8lh
NarSize: 1033416
References:
Deriver: dyivpmlaq2km6c11i0s6bi6mbsx0ylqf-hello-2.12.1.tar.gz.drv
Sig: cache.nixos.org-1:ywnIG629nQZQhEr6/HLDrLT/mUEp5J1LC6NmWSlJRWL/nM7oGItJQUYWGLvYGhSQvHrhIuvMpjNmBNh/WWqCDg==
CA: fixed:sha256:086vqwk2wl8zfs47sq2xpjc9k066ilmb8z6dn0q6ymwjzlm196cd"#
).expect("must parse");
let path_info: PathInfo = (&narinfo_parsed).into();
let mut narinfo_returned = path_info
.as_narinfo(
StorePathRef::from_bytes(b"pa10z4ngm0g83kx9mssrqzz30s84vq7k-hello-2.12.1.tar.gz")
.expect("invalid storepath"),
)
.expect("must be some");
narinfo_returned.url = "some.nar";
assert_eq!(
r#"StorePath: /nix/store/pa10z4ngm0g83kx9mssrqzz30s84vq7k-hello-2.12.1.tar.gz
URL: some.nar
Compression: none
NarHash: sha256:1lvqpbk2k1sb39z8jfxixf7p7v8sj4z6mmpa44nnmff3w1y6h8lh
NarSize: 1033416
References:
Deriver: dyivpmlaq2km6c11i0s6bi6mbsx0ylqf-hello-2.12.1.tar.gz.drv
Sig: cache.nixos.org-1:ywnIG629nQZQhEr6/HLDrLT/mUEp5J1LC6NmWSlJRWL/nM7oGItJQUYWGLvYGhSQvHrhIuvMpjNmBNh/WWqCDg==
CA: fixed:sha256:086vqwk2wl8zfs47sq2xpjc9k066ilmb8z6dn0q6ymwjzlm196cd
"#,
narinfo_returned.to_string(),
);
}