From 0eb73977efa184711647849791ebdb4da2cc65a8 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Nov 2025 22:11:43 -0500 Subject: [PATCH 1/3] Add zerocopy instance to raw bindings struct This will come in handy later, when we integrate harmonia. It is a very stable thing to do. --- Cargo.lock | 21 +++++++++++++++++++++ nix-bindings-store-sys/Cargo.toml | 1 + nix-bindings-store-sys/build.rs | 17 +++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 01fe2eb..f3d9634 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,6 +316,7 @@ dependencies = [ "bindgen", "nix-bindings-util-sys", "pkg-config", + "zerocopy", ] [[package]] @@ -611,3 +612,23 @@ name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/nix-bindings-store-sys/Cargo.toml b/nix-bindings-store-sys/Cargo.toml index 3bc2bb5..f9f5ee2 100644 --- a/nix-bindings-store-sys/Cargo.toml +++ b/nix-bindings-store-sys/Cargo.toml @@ -13,6 +13,7 @@ path = "src/lib.rs" [dependencies] nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } +zerocopy = { version = "0.8", features = ["derive"] } [build-dependencies] bindgen = "0.69" diff --git a/nix-bindings-store-sys/build.rs b/nix-bindings-store-sys/build.rs index bafdfae..8702756 100644 --- a/nix-bindings-store-sys/build.rs +++ b/nix-bindings-store-sys/build.rs @@ -9,6 +9,22 @@ impl bindgen::callbacks::ParseCallbacks for StripNixPrefix { } } +#[derive(Debug)] +struct AddZerocopyDerives {} +impl bindgen::callbacks::ParseCallbacks for AddZerocopyDerives { + fn add_derives(&self, info: &bindgen::callbacks::DeriveInfo<'_>) -> Vec { + if info.name == "store_path_hash_part" { + vec![ + "zerocopy::FromBytes".to_string(), + "zerocopy::IntoBytes".to_string(), + "zerocopy::Immutable".to_string(), + ] + } else { + vec![] + } + } +} + fn main() { println!("cargo:rerun-if-changed=include/nix-c-store.h"); println!("cargo:rustc-link-lib=nixstorec"); @@ -29,6 +45,7 @@ fn main() { .clang_args(args) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .parse_callbacks(Box::new(StripNixPrefix)) + .parse_callbacks(Box::new(AddZerocopyDerives {})) // Blocklist symbols from nix-bindings-util-sys .blocklist_file(".*nix_api_util\\.h") .generate() From 65abededcadec096e356ce3bf8c66bed9e7b1e5c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 Dec 2025 20:54:28 -0500 Subject: [PATCH 2/3] Add new `StorePath` bindings --- Cargo.lock | 8 +++++ nix-bindings-store/Cargo.toml | 2 ++ nix-bindings-store/src/path.rs | 62 +++++++++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index f3d9634..4577bc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,6 +132,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "home" version = "0.5.11" @@ -301,12 +307,14 @@ version = "0.2.1" dependencies = [ "anyhow", "ctor", + "hex-literal", "lazy_static", "nix-bindings-store-sys", "nix-bindings-util", "nix-bindings-util-sys", "pkg-config", "tempfile", + "zerocopy", ] [[package]] diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index c665a0e..c37d62d 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -17,9 +17,11 @@ nix-bindings-util = { path = "../nix-bindings-util", version = "0.2.1" } nix-bindings-util-sys = { path = "../nix-bindings-util-sys", version = "0.2.1" } nix-bindings-store-sys = { path = "../nix-bindings-store-sys", version = "0.2.1" } lazy_static = "1.4" +zerocopy = "0.8" [dev-dependencies] ctor = "0.2" +hex-literal = "0.4" tempfile = "3.10" [build-dependencies] diff --git a/nix-bindings-store/src/path.rs b/nix-bindings-store/src/path.rs index 51469af..e3be645 100644 --- a/nix-bindings-store/src/path.rs +++ b/nix-bindings-store/src/path.rs @@ -1,15 +1,21 @@ use std::ptr::NonNull; -use anyhow::Result; +use anyhow::{Context as _, Result}; use nix_bindings_store_sys as raw; +#[cfg(nix_at_least = "2.33")] +use nix_bindings_util::{check_call, context::Context}; use nix_bindings_util::{ result_string_init, string_return::{callback_get_result_string, callback_get_result_string_data}, }; +/// The size of a store path hash in bytes (20 bytes, decoded from nix32). +pub const STORE_PATH_HASH_SIZE: usize = 20; + pub struct StorePath { raw: NonNull, } + impl StorePath { /// Get the name of the store path. /// @@ -26,6 +32,43 @@ impl StorePath { } } + /// Get the hash part of the store path. + /// + /// This returns the decoded hash (not the nix32-encoded string). + #[cfg(nix_at_least = "2.33")] + pub fn hash(&self) -> Result<[u8; STORE_PATH_HASH_SIZE]> { + let mut result = [0u8; STORE_PATH_HASH_SIZE]; + let hash_part: &mut raw::store_path_hash_part = zerocopy::transmute_mut!(&mut result); + + let mut ctx = Context::new(); + + unsafe { + check_call!(raw::store_path_hash(&mut ctx, self.as_ptr(), hash_part))?; + } + Ok(result) + } + + /// Create a StorePath from hash and name components. + #[cfg(nix_at_least = "2.33")] + pub fn from_parts(hash: &[u8; STORE_PATH_HASH_SIZE], name: &str) -> Result { + let hash_part: &raw::store_path_hash_part = zerocopy::transmute_ref!(hash); + + let mut ctx = Context::new(); + + let out_path = unsafe { + check_call!(raw::store_create_from_parts( + &mut ctx, + hash_part, + name.as_ptr() as *const i8, + name.len() + ))? + }; + + NonNull::new(out_path) + .map(|ptr| unsafe { Self::new_raw(ptr) }) + .context("store_create_from_parts returned null") + } + /// This is a low level function that you shouldn't have to call unless you are developing the Nix bindings. /// /// Construct a new `StorePath` by first cloning the C store path. @@ -81,6 +124,9 @@ impl Drop for StorePath { #[cfg(test)] mod tests { + use super::*; + use hex_literal::hex; + #[test] #[cfg(nix_at_least = "2.26" /* get_storedir */)] fn store_path_name() { @@ -91,4 +137,18 @@ mod tests { let store_path = store.parse_store_path(store_path_string.as_str()).unwrap(); assert_eq!(store_path.name().unwrap(), "bash-interactive-5.2p26"); } + + #[test] + #[cfg(nix_at_least = "2.33")] + fn store_path_round_trip() { + let original_hash: [u8; STORE_PATH_HASH_SIZE] = + hex!("0123456789abcdef0011223344556677deadbeef"); + let original_name = "foo.drv"; + + let store_path = StorePath::from_parts(&original_hash, original_name).unwrap(); + + // Round trip gets back what we started with + assert_eq!(store_path.hash().unwrap(), original_hash); + assert_eq!(store_path.name().unwrap(), original_name); + } } From 6619b881b38842ea0d7dee7f9620321b6108d215 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 14 Jan 2026 21:47:02 +0100 Subject: [PATCH 3/3] Assert STORE_PATH_HASH_SIZE matches C struct `zerocopy` is safe enough, but it's good to be explicit about this. --- nix-bindings-store/src/path.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nix-bindings-store/src/path.rs b/nix-bindings-store/src/path.rs index e3be645..41f7f5b 100644 --- a/nix-bindings-store/src/path.rs +++ b/nix-bindings-store/src/path.rs @@ -12,6 +12,9 @@ use nix_bindings_util::{ /// The size of a store path hash in bytes (20 bytes, decoded from nix32). pub const STORE_PATH_HASH_SIZE: usize = 20; +#[cfg(nix_at_least = "2.33")] +const _: () = assert!(std::mem::size_of::() == STORE_PATH_HASH_SIZE); + pub struct StorePath { raw: NonNull, }