about summary refs log blame commit diff
path: root/tvix/nix-compat/src/derivation/utils.rs
blob: cbb5d02a21800d834d228fa8c3194cce7268c689 (plain) (tree)
1
2
3

                                       
                            


































































                                                                                                   

                                                                                    
                        

                               





                                     
use crate::derivation::DerivationError;
use crate::nixbase32;
use crate::nixhash::NixHash;
use crate::store_path::{self, StorePath};
use sha2::{Digest, Sha256};

/// compress_hash takes an arbitrarily long sequence of bytes (usually
/// a hash digest), and returns a sequence of bytes of length
/// output_size.
///
/// It's calculated by rotating through the bytes in the output buffer
/// (zero- initialized), and XOR'ing with each byte of the passed
/// input. It consumes 1 byte at a time, and XOR's it with the current
/// value in the output buffer.
///
/// This mimics equivalent functionality in C++ Nix.
fn compress_hash(input: &[u8], output_size: usize) -> Vec<u8> {
    let mut output: Vec<u8> = vec![0; output_size];

    for (ii, ch) in input.iter().enumerate() {
        output[ii % output_size] ^= ch;
    }

    output
}

/// This returns a store path, either of a derivation or a regular output.
/// The string is hashed with sha256, its digest is compressed to 20 bytes, and
/// nixbase32-encoded (32 characters)
pub(super) fn build_store_path(
    is_derivation: bool,
    fingerprint: &str,
    name: &str,
) -> Result<StorePath, DerivationError> {
    let digest = {
        let mut hasher = Sha256::new();
        hasher.update(fingerprint);
        hasher.finalize()
    };
    let compressed = compress_hash(&digest, 20);
    if is_derivation {
        StorePath::from_string(format!("{}-{}.drv", nixbase32::encode(&compressed), name).as_str())
    } else {
        StorePath::from_string(format!("{}-{}", nixbase32::encode(&compressed), name,).as_str())
    }
    .map_err(|_e| DerivationError::InvalidOutputName(name.to_string()))
    // Constructing the StorePath can only fail if the passed output name was
    // invalid, so map errors to a [DerivationError::InvalidOutputName].
}

/// Build a store path for a literal text file in the store that may
/// contain references.
pub fn path_with_references<S: AsRef<str>, I: IntoIterator<Item = S>, C: AsRef<[u8]>>(
    name: &str,
    content: C,
    references: I,
) -> Result<StorePath, DerivationError> {
    let mut s = String::from("text");

    for reference in references {
        s.push(':');
        s.push_str(reference.as_ref());
    }

    let content_digest = {
        let mut hasher = Sha256::new();
        hasher.update(content);
        hasher.finalize()
    };

    let h = NixHash::new(crate::nixhash::HashAlgo::Sha256, content_digest.to_vec());

    s.push_str(&format!(
        ":{}:{}:{}",
        h.to_nix_hash_string(),
        store_path::STORE_DIR,
        name
    ));

    build_store_path(false, &s, name)
}