nix 路径类型何时进入 nix 存储区,何时不进入?

When does a nix path type make it into the nix store and when not?

我过去注意到 nix 中的 ./myfile.txt 路径类型似乎

我想知道到底是什么时候发生的。

这对于包含任何形式的秘密信息的文件尤其重要,因为 /nix/store 中的所有文件对于系统上的所有用户都是全球可读的。

(当使用 nixops 时,有一个特殊的 "keys" 功能用于此目的,请参阅手册中的 Managing keys 部分,但我认为这条路径何时以及如何仍然很重要-存储路径复制发生在 nix 本身。)

#nixos IRC 频道上的用户 clever 解释:

什么时候发生

当您在 ${} 字符串插值 中使用路径时, 会扩展到 /nix/store/...,例如 mystring = "cat ${./myfile.txt}.

当你使用toString函数时不会发生,例如toString ./myfile.txt 不会给你一个指向 /nix/store.

的路径

例如:

toString ./notes.txt == "/home/clever/apps/nixos-installer/installer-gui/notes.txt"
"${./notes.txt}"     == "/nix/store/55j24v9qwdarikv7kd3lc0pvxdr9r2y8-notes.txt"

它是如何发生的

55j24v9qwdarikv7kd3lc0pvxdr9r2y8 散列部分取自 ./path 引用的文件内容,因此它会随着文件更改而更改,依赖它的东西可以相应地重建。

文件复制到 /nix/store 发生在 nix-instantiate 时; nix 表达式的 evaluation 仍然是纯函数式的(在求值时没有复制发生),但是 instantiation ("building") 不是.

为了实现这一点,nix 中的每个字符串都有一个 "context" 来跟踪字符串所依赖的内容(实际上是它后面的 .drv 路径列表)。

例如,来自 GNU hello 包的字符串 "/nix/store/rkvwvi007k7w8lp4cc0n10yhlz5xjfmk-hello-2.10" 有一些不可见的状态,表示它取决于 hello 推导。如果该字符串最终作为 stdenv.mkDerivation 的输入,新的推导将 "magically" 取决于正在构建的 hello 包。

即使您通过 builtins.substring 弄乱了字符串,这仍然有效。请参阅 nix 的 this code,了解如何在第 1653 行中提取较长字符串的上下文,并在第 1657 行中将其用作子字符串的上下文。

您可以使用 builtins.unsafeDiscardStringContext.

摆脱字符串的依赖上下文

它发生在 nix 代码中的什么地方

${} 插值使用 coerceToString,它有一个 bool copyToStore 参数,默认为 true:

/* String coercion.  Converts strings, paths and derivations to a
   string.  If `coerceMore' is set, also converts nulls, integers,
   booleans and lists to a string.  If `copyToStore' is set,
   referenced paths are copied to the Nix store as a side effect. */
string coerceToString(const Pos & pos, Value & v, PathSet & context,
                      bool coerceMore = false, bool copyToStore = true);

实现了here, and the check for the interpolated thing being a ./path, and the copying to /nix/store, is happening just below:

if (v.type == tPath) {
    Path path(canonPath(v.path));
    return copyToStore ? copyPathToStore(context, path) : path;
}

toString 是用 prim_toString 实现的,它为 copyToStore 参数传递 false

/* Convert the argument to a string.  Paths are *not* copied to the
   store, so `toString /foo/bar' yields `"/foo/bar"', not
   `"/nix/store/whatever..."'. */
static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
    PathSet context;
    string s = state.coerceToString(pos, *args[0], context, true, false);
    mkString(v, s, context);
}