Skip to content

watch-store does not work with chroot stores #323

@sepointon

Description

@sepointon

A chroot store is located in a directory with a nonempty prefix before /nix/store (though the logical paths, which are actually embedded in the binaries, don't include that prefix). They can be enabled with e.g. NIX_CONFIG="store = /foo/bar"; note that the path passed is the prefix, and does not include /nix/store.

attic watch-store , however, doesn't know about this logical/physical path distinction. Firstly, it doesn't manage to watch the right directory, and then secondly it filters out the notifications of new entries in the directory due to not matching the pattern of /nix/store/*.

Here's an unsuitable patch that I used to hack around the problem:

diff --git a/client/src/command/watch_store.rs b/client/src/command/watch_store.rs
index 24eaf7a..dd208e2 100644
--- a/client/src/command/watch_store.rs
+++ b/client/src/command/watch_store.rs
@@ -1,3 +1,4 @@
+use std::ffi::OsString;
 use std::path::{Path, PathBuf};
 use std::sync::Arc;
 
@@ -38,6 +39,10 @@ pub struct WatchStore {
     /// Always send the upload info as part of the payload.
     #[clap(long, hide = true)]
     force_preamble: bool,
+
+    /// Prefix for store directory paths.
+    #[clap(long, default_value = "")]
+    root: OsString,
 }
 
 pub async fn run(opts: Opts) -> Result<()> {
@@ -49,7 +54,10 @@ pub async fn run(opts: Opts) -> Result<()> {
     let config = Config::load()?;
 
     let store = Arc::new(NixStore::connect()?);
-    let store_dir = store.store_dir().to_owned();
+    // Go through OsString, to avoid PathBuf::push's replacing-with-absolute-paths behaviour
+    let mut store_dir = sub.root.clone();
+    store_dir.push(store.store_dir());
+    let store_dir: PathBuf = store_dir.into();
 
     let (server_name, server, cache) = config.resolve_cache(&sub.cache)?;
     let mut api = ApiClient::from_server_config(server.clone())?;
@@ -108,7 +116,11 @@ pub async fn run(opts: Opts) -> Result<()> {
                         .iter()
                         .filter_map(|p| {
                             let base = strip_lock_file(p)?;
-                            store.parse_store_path(base).ok()
+                            let logical_relative = base.strip_prefix(&sub.root).ok()?;
+                            let mut logical: OsString = "/".into();
+                            logical.push(logical_relative);
+                            let logical: PathBuf = logical.into();
+                            store.parse_store_path(logical).ok()
                         })
                         .collect::<Vec<StorePath>>();

This is obviously not suitable for merging, because there surely must be some way of discovering the prefix from CppNix (which knows full well where the store is, since it can put things in there!), but I got lost trying to figure out how to do it and the ugly hack of explicitly passing it to Attic worked well enough in the moment. With that said, I think the changes to the actual logic (as opposed to the plumbing of configuration) are fine.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions