Merge remote-tracking branch 'origin/master' into flakes

This commit is contained in:
Eelco Dolstra 2019-03-26 11:44:14 +01:00
commit b5565a7081
36 changed files with 470 additions and 180 deletions

3
.gitignore vendored
View file

@ -81,6 +81,9 @@ perl/Makefile.config
/tests/common.sh /tests/common.sh
/tests/dummy /tests/dummy
/tests/result* /tests/result*
/tests/restricted-innocent
/tests/shell
/tests/shell.drv
# /tests/lang/ # /tests/lang/
/tests/lang/*.out /tests/lang/*.out

View file

View file

@ -1,4 +1,6 @@
AR = @AR@
BDW_GC_LIBS = @BDW_GC_LIBS@ BDW_GC_LIBS = @BDW_GC_LIBS@
BUILD_SHARED_LIBS = @BUILD_SHARED_LIBS@
CC = @CC@ CC = @CC@
CFLAGS = @CFLAGS@ CFLAGS = @CFLAGS@
CXX = @CXX@ CXX = @CXX@

View file

@ -1,4 +1,4 @@
AC_INIT(nix, m4_esyscmd([bash -c "echo -n $(cat ./version)$VERSION_SUFFIX"])) AC_INIT(nix, m4_esyscmd([bash -c "echo -n $(cat ./.version)$VERSION_SUFFIX"]))
AC_CONFIG_SRCDIR(README.md) AC_CONFIG_SRCDIR(README.md)
AC_CONFIG_AUX_DIR(config) AC_CONFIG_AUX_DIR(config)
@ -64,6 +64,7 @@ AC_PROG_CXX
AC_PROG_CPP AC_PROG_CPP
AX_CXX_COMPILE_STDCXX_17 AX_CXX_COMPILE_STDCXX_17
AC_CHECK_TOOL([AR], [ar])
# Use 64-bit file system calls so that we can support files > 2 GiB. # Use 64-bit file system calls so that we can support files > 2 GiB.
AC_SYS_LARGEFILE AC_SYS_LARGEFILE
@ -267,6 +268,15 @@ AC_ARG_WITH(sandbox-shell, AC_HELP_STRING([--with-sandbox-shell=PATH],
sandbox_shell=$withval) sandbox_shell=$withval)
AC_SUBST(sandbox_shell) AC_SUBST(sandbox_shell)
AC_ARG_ENABLE(shared, AC_HELP_STRING([--enable-shared],
[Build shared libraries for Nix [default=yes]]),
shared=$enableval, shared=yes)
if test "$shared" = yes; then
AC_SUBST(BUILD_SHARED_LIBS, 1, [Whether to build shared libraries.])
else
AC_SUBST(BUILD_SHARED_LIBS, 0, [Whether to build shared libraries.])
fi
# Expand all variables in config.status. # Expand all variables in config.status.
test "$prefix" = NONE && prefix=$ac_default_prefix test "$prefix" = NONE && prefix=$ac_default_prefix

View file

@ -180,4 +180,8 @@ builders = @/etc/nix/machines
causes the list of machines in <filename>/etc/nix/machines</filename> causes the list of machines in <filename>/etc/nix/machines</filename>
to be included. (This is the default.)</para> to be included. (This is the default.)</para>
<para>If you want the builders to use caches, you likely want to set
the option <link linkend='conf-builders-use-substitutes'><literal>builders-use-substitutes</literal></link>
in your local <filename>nix.conf</filename>.</para>
</chapter> </chapter>

View file

@ -52,10 +52,15 @@ nixpkgs=/home/eelco/Dev/nixpkgs-branch:/etc/nixos</screen>
<envar>NIX_PATH</envar> to <envar>NIX_PATH</envar> to
<screen> <screen>
nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz</screen> nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-15.09.tar.gz</screen>
tells Nix to download the latest revision in the Nixpkgs/NixOS tells Nix to download the latest revision in the Nixpkgs/NixOS
14.12 channel.</para> 15.09 channel.</para>
<para>A following shorthand can be used to refer to the official channels:
<screen>nixpkgs=channel:nixos-15.09</screen>
</para>
<para>The search path can be extended using the <option <para>The search path can be extended using the <option
linkend="opt-I">-I</option> option, which takes precedence over linkend="opt-I">-I</option> option, which takes precedence over

View file

@ -1282,6 +1282,7 @@ ktorrent-2.2.1/NEWS
<cmdsynopsis> <cmdsynopsis>
<command>nix-store</command> <command>nix-store</command>
<arg choice='plain'><option>--dump-db</option></arg> <arg choice='plain'><option>--dump-db</option></arg>
<arg rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis> </cmdsynopsis>
</refsection> </refsection>
@ -1292,6 +1293,13 @@ Nix database to standard output. It can be loaded into an empty Nix
store using <option>--load-db</option>. This is useful for making store using <option>--load-db</option>. This is useful for making
backups and when migrating to different database schemas.</para> backups and when migrating to different database schemas.</para>
<para>By default, <option>--dump-db</option> will dump the entire Nix
database. When one or more store paths is passed, only the subset of
the Nix database for those store paths is dumped. As with
<option>--export</option>, the user is responsible for passing all the
store paths for a closure. See <option>--export</option> for an
example.</para>
</refsection> </refsection>
</refsection> </refsection>

View file

@ -23,6 +23,7 @@ available as <function>builtins.derivation</function>.</para>
<varlistentry xml:id='builtin-abort'> <varlistentry xml:id='builtin-abort'>
<term><function>abort</function> <replaceable>s</replaceable></term> <term><function>abort</function> <replaceable>s</replaceable></term>
<term><function>builtins.abort</function> <replaceable>s</replaceable></term>
<listitem><para>Abort Nix expression evaluation, print error <listitem><para>Abort Nix expression evaluation, print error
message <replaceable>s</replaceable>.</para></listitem> message <replaceable>s</replaceable>.</para></listitem>
@ -251,6 +252,8 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
<varlistentry xml:id='builtin-derivation'> <varlistentry xml:id='builtin-derivation'>
<term><function>derivation</function> <term><function>derivation</function>
<replaceable>attrs</replaceable></term> <replaceable>attrs</replaceable></term>
<term><function>builtins.derivation</function>
<replaceable>attrs</replaceable></term>
<listitem><para><function>derivation</function> is described in <listitem><para><function>derivation</function> is described in
<xref linkend='ssec-derivation' />.</para></listitem> <xref linkend='ssec-derivation' />.</para></listitem>
@ -260,6 +263,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
<varlistentry xml:id='builtin-dirOf'> <varlistentry xml:id='builtin-dirOf'>
<term><function>dirOf</function> <replaceable>s</replaceable></term> <term><function>dirOf</function> <replaceable>s</replaceable></term>
<term><function>builtins.dirOf</function> <replaceable>s</replaceable></term>
<listitem><para>Return the directory part of the string <listitem><para>Return the directory part of the string
<replaceable>s</replaceable>, that is, everything before the final <replaceable>s</replaceable>, that is, everything before the final
@ -318,6 +322,8 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
<varlistentry xml:id='builtin-fetchTarball'> <varlistentry xml:id='builtin-fetchTarball'>
<term><function>fetchTarball</function> <term><function>fetchTarball</function>
<replaceable>url</replaceable></term> <replaceable>url</replaceable></term>
<term><function>builtins.fetchTarball</function>
<replaceable>url</replaceable></term>
<listitem><para>Download the specified URL, unpack it and return <listitem><para>Download the specified URL, unpack it and return
the path of the unpacked tree. The file must be a tape archive the path of the unpacked tree. The file must be a tape archive
@ -693,8 +699,8 @@ builtins.genList (x: x * x) 5
<listitem><para>Return a base-16 representation of the <listitem><para>Return a base-16 representation of the
cryptographic hash of string <replaceable>s</replaceable>. The cryptographic hash of string <replaceable>s</replaceable>. The
hash algorithm specified by <replaceable>type</replaceable> must hash algorithm specified by <replaceable>type</replaceable> must
be one of <literal>"md5"</literal>, <literal>"sha1"</literal> or be one of <literal>"md5"</literal>, <literal>"sha1"</literal>,
<literal>"sha256"</literal>.</para></listitem> <literal>"sha256"</literal> or <literal>"sha512"</literal>.</para></listitem>
</varlistentry> </varlistentry>
@ -714,6 +720,8 @@ builtins.genList (x: x * x) 5
<varlistentry xml:id='builtin-import'> <varlistentry xml:id='builtin-import'>
<term><function>import</function> <term><function>import</function>
<replaceable>path</replaceable></term> <replaceable>path</replaceable></term>
<term><function>builtins.import</function>
<replaceable>path</replaceable></term>
<listitem><para>Load, parse and return the Nix expression in the <listitem><para>Load, parse and return the Nix expression in the
file <replaceable>path</replaceable>. If <replaceable>path file <replaceable>path</replaceable>. If <replaceable>path
@ -853,10 +861,20 @@ x: x + 456</programlisting>
</varlistentry> </varlistentry>
<varlistentry><term><function>builtins.isPath</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a path, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry xml:id='builtin-isNull'> <varlistentry xml:id='builtin-isNull'>
<term><function>isNull</function> <term><function>isNull</function>
<replaceable>e</replaceable></term> <replaceable>e</replaceable></term>
<term><function>builtins.isNull</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if <listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to <literal>null</literal>, <replaceable>e</replaceable> evaluates to <literal>null</literal>,
@ -925,6 +943,8 @@ builtins.listToAttrs
<varlistentry xml:id='builtin-map'> <varlistentry xml:id='builtin-map'>
<term><function>map</function> <term><function>map</function>
<replaceable>f</replaceable> <replaceable>list</replaceable></term> <replaceable>f</replaceable> <replaceable>list</replaceable></term>
<term><function>builtins.map</function>
<replaceable>f</replaceable> <replaceable>list</replaceable></term>
<listitem><para>Apply the function <replaceable>f</replaceable> to <listitem><para>Apply the function <replaceable>f</replaceable> to
each element in the list <replaceable>list</replaceable>. For each element in the list <replaceable>list</replaceable>. For
@ -1119,6 +1139,8 @@ Evaluates to <literal>[ "foo" ]</literal>.
<varlistentry xml:id='builtin-removeAttrs'> <varlistentry xml:id='builtin-removeAttrs'>
<term><function>removeAttrs</function> <term><function>removeAttrs</function>
<replaceable>set</replaceable> <replaceable>list</replaceable></term> <replaceable>set</replaceable> <replaceable>list</replaceable></term>
<term><function>builtins.removeAttrs</function>
<replaceable>set</replaceable> <replaceable>list</replaceable></term>
<listitem><para>Remove the attributes listed in <listitem><para>Remove the attributes listed in
<replaceable>list</replaceable> from <replaceable>list</replaceable> from
@ -1287,6 +1309,8 @@ builtins.substring 0 3 "nixos"
<varlistentry xml:id='builtin-throw'> <varlistentry xml:id='builtin-throw'>
<term><function>throw</function> <term><function>throw</function>
<replaceable>s</replaceable></term> <replaceable>s</replaceable></term>
<term><function>builtins.throw</function>
<replaceable>s</replaceable></term>
<listitem><para>Throw an error message <listitem><para>Throw an error message
<replaceable>s</replaceable>. This usually aborts Nix expression <replaceable>s</replaceable>. This usually aborts Nix expression
@ -1405,6 +1429,7 @@ in foo</programlisting>
<varlistentry xml:id='builtin-toString'> <varlistentry xml:id='builtin-toString'>
<term><function>toString</function> <replaceable>e</replaceable></term> <term><function>toString</function> <replaceable>e</replaceable></term>
<term><function>builtins.toString</function> <replaceable>e</replaceable></term>
<listitem><para>Convert the expression <listitem><para>Convert the expression
<replaceable>e</replaceable> to a string. <replaceable>e</replaceable> to a string.

View file

@ -89,7 +89,7 @@ the S3 URL:</para>
"Version": "2012-10-17", "Version": "2012-10-17",
"Statement": [ "Statement": [
{ {
"Sid": "AlowDirectReads", "Sid": "AllowDirectReads",
"Action": [ "Action": [
"s3:GetObject", "s3:GetObject",
"s3:GetBucketLocation" "s3:GetBucketLocation"

View file

@ -2,6 +2,11 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>EnvironmentVariables</key>
<dict>
<key>OBJC_DISABLE_INITIALIZE_FORK_SAFETY</key>
<string>YES</string>
</dict>
<key>Label</key> <key>Label</key>
<string>org.nixos.nix-daemon</string> <string>org.nixos.nix-daemon</string>
<key>KeepAlive</key> <key>KeepAlive</key>

View file

@ -125,7 +125,7 @@ define build-library
$(1)_PATH := $$(_d)/$$($(1)_NAME).a $(1)_PATH := $$(_d)/$$($(1)_NAME).a
$$($(1)_PATH): $$($(1)_OBJS) | $$(_d)/ $$($(1)_PATH): $$($(1)_OBJS) | $$(_d)/
$(trace-ar) ar crs $$@ $$? $(trace-ar) $(AR) crs $$@ $$?
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS) $(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS)

View file

@ -1,4 +1,4 @@
AC_INIT(nix-perl, m4_esyscmd([bash -c "echo -n $(cat ../version)$VERSION_SUFFIX"])) AC_INIT(nix-perl, m4_esyscmd([bash -c "echo -n $(cat ../.version)$VERSION_SUFFIX"]))
AC_CONFIG_SRCDIR(MANIFEST) AC_CONFIG_SRCDIR(MANIFEST)
AC_CONFIG_AUX_DIR(../config) AC_CONFIG_AUX_DIR(../config)

View file

@ -18,7 +18,7 @@ let
releaseTools.sourceTarball { releaseTools.sourceTarball {
name = "nix-tarball"; name = "nix-tarball";
version = builtins.readFile ./version; version = builtins.readFile ./.version;
versionSuffix = if officialRelease then "" else "pre${toString nix.revCount}_${nix.shortRev}"; versionSuffix = if officialRelease then "" else "pre${toString nix.revCount}_${nix.shortRev}";
src = nix; src = nix;
inherit officialRelease; inherit officialRelease;
@ -278,7 +278,6 @@ let
pkgs.runCommand "eval-nixos" { buildInputs = [ build.x86_64-linux ]; } pkgs.runCommand "eval-nixos" { buildInputs = [ build.x86_64-linux ]; }
'' ''
export NIX_STATE_DIR=$TMPDIR export NIX_STATE_DIR=$TMPDIR
nix-store --init
nix-instantiate ${nixpkgs}/nixos/release-combined.nix -A tested --dry-run \ nix-instantiate ${nixpkgs}/nixos/release-combined.nix -A tested --dry-run \
--arg nixpkgs '{ outPath = ${nixpkgs}; revCount = 123; shortRev = "abcdefgh"; }' --arg nixpkgs '{ outPath = ${nixpkgs}; revCount = 123; shortRev = "abcdefgh"; }'

View file

@ -674,9 +674,6 @@ $NIX_INSTALLED_NIX.
EOF EOF
fi fi
_sudo "to initialize the Nix Database" \
$NIX_INSTALLED_NIX/bin/nix-store --init
cat ./.reginfo \ cat ./.reginfo \
| _sudo "to load data for the first time in to the Nix Database" \ | _sudo "to load data for the first time in to the Nix Database" \
"$NIX_INSTALLED_NIX/bin/nix-store" --load-db "$NIX_INSTALLED_NIX/bin/nix-store" --load-db
@ -747,7 +744,6 @@ build-users-group = $NIX_BUILD_GROUP_NAME
max-jobs = $NIX_USER_COUNT max-jobs = $NIX_USER_COUNT
cores = 1 cores = 1
sandbox = false
EOF EOF
_sudo "to place the default nix daemon configuration (part 2)" \ _sudo "to place the default nix daemon configuration (part 2)" \
install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf

View file

@ -109,12 +109,6 @@ for i in $(cd "$self/store" >/dev/null && echo ./*); do
done done
echo "" >&2 echo "" >&2
echo "initialising Nix database..." >&2
if ! $nix/bin/nix-store --init; then
echo "$0: failed to initialize the Nix database" >&2
exit 1
fi
if ! "$nix/bin/nix-store" --load-db < "$self/.reginfo"; then if ! "$nix/bin/nix-store" --load-db < "$self/.reginfo"; then
echo "$0: unable to register valid paths" >&2 echo "$0: unable to register valid paths" >&2
exit 1 exit 1

View file

@ -75,7 +75,7 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ca-bundle.crt" export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ca-bundle.crt"
fi fi
if [ -n "${MANPATH}" ]; then if [ -n "${MANPATH-}" ]; then
export MANPATH="$NIX_LINK/share/man:$MANPATH" export MANPATH="$NIX_LINK/share/man:$MANPATH"
fi fi

View file

@ -38,6 +38,12 @@ static AutoCloseFD openSlotLock(const Machine & m, unsigned long long slot)
return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri), slot), true); return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri), slot), true);
} }
static bool allSupportedLocally(const std::set<std::string>& requiredFeatures) {
for (auto & feature : requiredFeatures)
if (!settings.systemFeatures.get().count(feature)) return false;
return true;
}
static int _main(int argc, char * * argv) static int _main(int argc, char * * argv)
{ {
{ {
@ -99,7 +105,8 @@ static int _main(int argc, char * * argv)
auto canBuildLocally = amWilling auto canBuildLocally = amWilling
&& ( neededSystem == settings.thisSystem && ( neededSystem == settings.thisSystem
|| settings.extraPlatforms.get().count(neededSystem) > 0); || settings.extraPlatforms.get().count(neededSystem) > 0)
&& allSupportedLocally(requiredFeatures);
/* Error ignored here, will be caught later */ /* Error ignored here, will be caught later */
mkdir(currentLoad.c_str(), 0777); mkdir(currentLoad.c_str(), 0777);

View file

@ -131,6 +131,16 @@ std::ostream & operator << (std::ostream & str, const Value & v)
} }
const Value *getPrimOp(const Value &v) {
const Value * primOp = &v;
while (primOp->type == tPrimOpApp) {
primOp = primOp->primOpApp.left;
}
assert(primOp->type == tPrimOp);
return primOp;
}
string showType(const Value & v) string showType(const Value & v)
{ {
switch (v.type) { switch (v.type) {
@ -145,8 +155,10 @@ string showType(const Value & v)
case tApp: return "a function application"; case tApp: return "a function application";
case tLambda: return "a function"; case tLambda: return "a function";
case tBlackhole: return "a black hole"; case tBlackhole: return "a black hole";
case tPrimOp: return "a built-in function"; case tPrimOp:
case tPrimOpApp: return "a partially applied built-in function"; return fmt("the built-in function '%s'", string(v.primOp->name));
case tPrimOpApp:
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
case tExternal: return v.external->showType(); case tExternal: return v.external->showType();
case tFloat: return "a float"; case tFloat: return "a float";
} }

View file

@ -327,6 +327,9 @@ private:
/* Return a string representing the type of the value `v'. */ /* Return a string representing the type of the value `v'. */
string showType(const Value & v); string showType(const Value & v);
/* Decode a context string !<name>!<path> into a pair <path,
name>. */
std::pair<string, string> decodeContext(const string & s);
/* If `path' refers to a directory, then append "/default.nix". */ /* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path); Path resolveExprPath(Path path);

View file

@ -315,6 +315,12 @@ static void prim_isBool(EvalState & state, const Pos & pos, Value * * args, Valu
mkBool(v, args[0]->type == tBool); mkBool(v, args[0]->type == tBool);
} }
/* Determine whether the argument is a path. */
static void prim_isPath(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceValue(*args[0]);
mkBool(v, args[0]->type == tPath);
}
struct CompareValues struct CompareValues
{ {
@ -687,21 +693,12 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
} }
} }
/* See prim_unsafeDiscardOutputDependency. */
else if (path.at(0) == '~')
drv.inputSrcs.insert(string(path, 1));
/* Handle derivation outputs of the form !<name>!<path>. */ /* Handle derivation outputs of the form !<name>!<path>. */
else if (path.at(0) == '!') { else if (path.at(0) == '!') {
std::pair<string, string> ctx = decodeContext(path); std::pair<string, string> ctx = decodeContext(path);
drv.inputDrvs[ctx.first].insert(ctx.second); drv.inputDrvs[ctx.first].insert(ctx.second);
} }
/* Handle derivation contexts returned by
builtins.storePath. */
else if (isDerivation(path))
drv.inputDrvs[path] = state.store->queryDerivationOutputNames(path);
/* Otherwise it's a source file. */ /* Otherwise it's a source file. */
else else
drv.inputSrcs.insert(path); drv.inputSrcs.insert(path);
@ -1004,13 +1001,8 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
PathSet refs; PathSet refs;
for (auto path : context) { for (auto path : context) {
if (path.at(0) == '=') path = string(path, 1); if (path.at(0) != '/')
if (isDerivation(path)) {
/* See prim_unsafeDiscardOutputDependency. */
if (path.at(0) != '~')
throw EvalError(format("in 'toFile': the file '%1%' cannot refer to derivation outputs, at %2%") % name % pos); throw EvalError(format("in 'toFile': the file '%1%' cannot refer to derivation outputs, at %2%") % name % pos);
path = string(path, 1);
}
refs.insert(path); refs.insert(path);
} }
@ -1794,41 +1786,6 @@ static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args
} }
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
string s = state.coerceToString(pos, *args[0], context);
mkString(v, s, PathSet());
}
static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
state.forceString(*args[0], context, pos);
mkBool(v, !context.empty());
}
/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
builder without causing the derivation to be built (for instance,
in the derivation that builds NARs in nix-push, when doing
source-only deployment). This primop marks the string context so
that builtins.derivation adds the path to drv.inputSrcs rather than
drv.inputDrvs. */
static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
string s = state.coerceToString(pos, *args[0], context);
PathSet context2;
for (auto & p : context)
context2.insert(p.at(0) == '=' ? "~" + string(p, 1) : p);
mkString(v, s, context2);
}
/* Return the cryptographic hash of a string in base-16. */ /* Return the cryptographic hash of a string in base-16. */
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
@ -2218,6 +2175,7 @@ void EvalState::createBaseEnv()
addPrimOp("__isInt", 1, prim_isInt); addPrimOp("__isInt", 1, prim_isInt);
addPrimOp("__isFloat", 1, prim_isFloat); addPrimOp("__isFloat", 1, prim_isFloat);
addPrimOp("__isBool", 1, prim_isBool); addPrimOp("__isBool", 1, prim_isBool);
addPrimOp("__isPath", 1, prim_isPath);
addPrimOp("__genericClosure", 1, prim_genericClosure); addPrimOp("__genericClosure", 1, prim_genericClosure);
addPrimOp("abort", 1, prim_abort); addPrimOp("abort", 1, prim_abort);
addPrimOp("__addErrorContext", 2, prim_addErrorContext); addPrimOp("__addErrorContext", 2, prim_addErrorContext);
@ -2299,9 +2257,6 @@ void EvalState::createBaseEnv()
addPrimOp("toString", 1, prim_toString); addPrimOp("toString", 1, prim_toString);
addPrimOp("__substring", 3, prim_substring); addPrimOp("__substring", 3, prim_substring);
addPrimOp("__stringLength", 1, prim_stringLength); addPrimOp("__stringLength", 1, prim_stringLength);
addPrimOp("__hasContext", 1, prim_hasContext);
addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
addPrimOp("__hashString", 2, prim_hashString); addPrimOp("__hashString", 2, prim_hashString);
addPrimOp("__match", 2, prim_match); addPrimOp("__match", 2, prim_match);
addPrimOp("__split", 2, prim_split); addPrimOp("__split", 2, prim_split);

View file

@ -0,0 +1,187 @@
#include "primops.hh"
#include "eval-inline.hh"
#include "derivations.hh"
namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
string s = state.coerceToString(pos, *args[0], context);
mkString(v, s, PathSet());
}
static RegisterPrimOp r1("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
state.forceString(*args[0], context, pos);
mkBool(v, !context.empty());
}
static RegisterPrimOp r2("__hasContext", 1, prim_hasContext);
/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
builder without causing the derivation to be built (for instance,
in the derivation that builds NARs in nix-push, when doing
source-only deployment). This primop marks the string context so
that builtins.derivation adds the path to drv.inputSrcs rather than
drv.inputDrvs. */
static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
string s = state.coerceToString(pos, *args[0], context);
PathSet context2;
for (auto & p : context)
context2.insert(p.at(0) == '=' ? string(p, 1) : p);
mkString(v, s, context2);
}
static RegisterPrimOp r3("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
/* Extract the context of a string as a structured Nix value.
The context is represented as an attribute set whose keys are the
paths in the context set and whose values are attribute sets with
the following keys:
path: True if the relevant path is in the context as a plain store
path (i.e. the kind of context you get when interpolating
a Nix path (e.g. ./.) into a string). False if missing.
allOutputs: True if the relevant path is a derivation and it is
in the context as a drv file with all of its outputs
(i.e. the kind of context you get when referencing
.drvPath of some derivation). False if missing.
outputs: If a non-empty list, the relevant path is a derivation
and the provided outputs are referenced in the context
(i.e. the kind of context you get when referencing
.outPath of some derivation). Empty list if missing.
Note that for a given path any combination of the above attributes
may be present.
*/
static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
struct ContextInfo {
bool path = false;
bool allOutputs = false;
Strings outputs;
};
PathSet context;
state.forceString(*args[0], context, pos);
auto contextInfos = std::map<Path, ContextInfo>();
for (const auto & p : context) {
Path drv;
string output;
const Path * path = &p;
if (p.at(0) == '=') {
drv = string(p, 1);
path = &drv;
} else if (p.at(0) == '!') {
std::pair<string, string> ctx = decodeContext(p);
drv = ctx.first;
output = ctx.second;
path = &drv;
}
auto isPath = drv.empty();
auto isAllOutputs = (!drv.empty()) && output.empty();
auto iter = contextInfos.find(*path);
if (iter == contextInfos.end()) {
contextInfos.emplace(*path, ContextInfo{isPath, isAllOutputs, output.empty() ? Strings{} : Strings{std::move(output)}});
} else {
if (isPath)
iter->second.path = true;
else if (isAllOutputs)
iter->second.allOutputs = true;
else
iter->second.outputs.emplace_back(std::move(output));
}
}
state.mkAttrs(v, contextInfos.size());
auto sPath = state.symbols.create("path");
auto sAllOutputs = state.symbols.create("allOutputs");
for (const auto & info : contextInfos) {
auto & infoVal = *state.allocAttr(v, state.symbols.create(info.first));
state.mkAttrs(infoVal, 3);
if (info.second.path)
mkBool(*state.allocAttr(infoVal, sPath), true);
if (info.second.allOutputs)
mkBool(*state.allocAttr(infoVal, sAllOutputs), true);
if (!info.second.outputs.empty()) {
auto & outputsVal = *state.allocAttr(infoVal, state.sOutputs);
state.mkList(outputsVal, info.second.outputs.size());
size_t i = 0;
for (const auto & output : info.second.outputs) {
mkString(*(outputsVal.listElems()[i++] = state.allocValue()), output);
}
}
infoVal.attrs->sort();
}
v.attrs->sort();
}
static RegisterPrimOp r4("__getContext", 1, prim_getContext);
/* Append the given context to a given string.
See the commentary above unsafeGetContext for details of the
context representation.
*/
static void prim_appendContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
auto orig = state.forceString(*args[0], context, pos);
state.forceAttrs(*args[1], pos);
auto sPath = state.symbols.create("path");
auto sAllOutputs = state.symbols.create("allOutputs");
for (auto & i : *args[1]->attrs) {
if (!state.store->isStorePath(i.name))
throw EvalError("Context key '%s' is not a store path, at %s", i.name, i.pos);
if (!settings.readOnlyMode)
state.store->ensurePath(i.name);
state.forceAttrs(*i.value, *i.pos);
auto iter = i.value->attrs->find(sPath);
if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, *iter->pos))
context.insert(i.name);
}
iter = i.value->attrs->find(sAllOutputs);
if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, *iter->pos)) {
if (!isDerivation(i.name)) {
throw EvalError("Tried to add all-outputs context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
}
context.insert("=" + string(i.name));
}
}
iter = i.value->attrs->find(state.sOutputs);
if (iter != i.value->attrs->end()) {
state.forceList(*iter->value, *iter->pos);
if (iter->value->listSize() && !isDerivation(i.name)) {
throw EvalError("Tried to add derivation output context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
}
for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos);
context.insert("!" + name + "!" + string(i.name));
}
}
}
mkString(v, orig, context);
}
static RegisterPrimOp r5("__appendContext", 2, prim_appendContext);
}

View file

@ -614,6 +614,22 @@ struct CurlDownloader : public Downloader
writeFull(wakeupPipe.writeSide.get(), " "); writeFull(wakeupPipe.writeSide.get(), " ");
} }
#ifdef ENABLE_S3
std::tuple<std::string, std::string, Store::Params> parseS3Uri(std::string uri)
{
auto [path, params] = splitUriAndParams(uri);
auto slash = path.find('/', 5); // 5 is the length of "s3://" prefix
if (slash == std::string::npos)
throw nix::Error("bad S3 URI '%s'", path);
std::string bucketName(path, 5, slash - 5);
std::string key(path, slash + 1);
return {bucketName, key, params};
}
#endif
void enqueueDownload(const DownloadRequest & request, void enqueueDownload(const DownloadRequest & request,
Callback<DownloadResult> callback) override Callback<DownloadResult> callback) override
{ {
@ -622,12 +638,15 @@ struct CurlDownloader : public Downloader
// FIXME: do this on a worker thread // FIXME: do this on a worker thread
try { try {
#ifdef ENABLE_S3 #ifdef ENABLE_S3
S3Helper s3Helper("", Aws::Region::US_EAST_1, "", ""); // FIXME: make configurable auto [bucketName, key, params] = parseS3Uri(request.uri);
auto slash = request.uri.find('/', 5);
if (slash == std::string::npos) std::string profile = get(params, "profile", "");
throw nix::Error("bad S3 URI '%s'", request.uri); std::string region = get(params, "region", Aws::Region::US_EAST_1);
std::string bucketName(request.uri, 5, slash - 5); std::string scheme = get(params, "scheme", "");
std::string key(request.uri, slash + 1); std::string endpoint = get(params, "endpoint", "");
S3Helper s3Helper(profile, region, scheme, endpoint);
// FIXME: implement ETag // FIXME: implement ETag
auto s3Res = s3Helper.getObject(bucketName, key); auto s3Res = s3Helper.getObject(bucketName, key);
DownloadResult res; DownloadResult res;

View file

@ -129,8 +129,8 @@ Path LocalFSStore::addPermRoot(const Path & _storePath,
check if the root is in a directory in or linked from the check if the root is in a directory in or linked from the
gcroots directory. */ gcroots directory. */
if (settings.checkRootReachability) { if (settings.checkRootReachability) {
Roots roots = findRoots(); Roots roots = findRoots(false);
if (roots.find(gcRoot) == roots.end()) if (roots[storePath].count(gcRoot) == 0)
printError( printError(
format( format(
"warning: '%1%' is not in a directory where the garbage collector looks for roots; " "warning: '%1%' is not in a directory where the garbage collector looks for roots; "
@ -197,10 +197,11 @@ void LocalStore::addTempRoot(const Path & path)
} }
std::set<std::pair<pid_t, Path>> LocalStore::readTempRoots(FDs & fds) static std::string censored = "{censored}";
{
std::set<std::pair<pid_t, Path>> tempRoots;
void LocalStore::findTempRoots(FDs & fds, Roots & tempRoots, bool censor)
{
/* Read the `temproots' directory for per-process temporary root /* Read the `temproots' directory for per-process temporary root
files. */ files. */
for (auto & i : readDirectory(tempRootsDir)) { for (auto & i : readDirectory(tempRootsDir)) {
@ -250,14 +251,12 @@ std::set<std::pair<pid_t, Path>> LocalStore::readTempRoots(FDs & fds)
Path root(contents, pos, end - pos); Path root(contents, pos, end - pos);
debug("got temporary root '%s'", root); debug("got temporary root '%s'", root);
assertStorePath(root); assertStorePath(root);
tempRoots.emplace(pid, root); tempRoots[root].emplace(censor ? censored : fmt("{temp:%d}", pid));
pos = end + 1; pos = end + 1;
} }
fds.push_back(fd); /* keep open */ fds.push_back(fd); /* keep open */
} }
return tempRoots;
} }
@ -266,7 +265,7 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
auto foundRoot = [&](const Path & path, const Path & target) { auto foundRoot = [&](const Path & path, const Path & target) {
Path storePath = toStorePath(target); Path storePath = toStorePath(target);
if (isStorePath(storePath) && isValidPath(storePath)) if (isStorePath(storePath) && isValidPath(storePath))
roots[path] = storePath; roots[storePath].emplace(path);
else else
printInfo(format("skipping invalid root from '%1%' to '%2%'") % path % storePath); printInfo(format("skipping invalid root from '%1%' to '%2%'") % path % storePath);
}; };
@ -306,7 +305,7 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
else if (type == DT_REG) { else if (type == DT_REG) {
Path storePath = storeDir + "/" + baseNameOf(path); Path storePath = storeDir + "/" + baseNameOf(path);
if (isStorePath(storePath) && isValidPath(storePath)) if (isStorePath(storePath) && isValidPath(storePath))
roots[path] = storePath; roots[storePath].emplace(path);
} }
} }
@ -321,10 +320,8 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
} }
Roots LocalStore::findRootsNoTemp() void LocalStore::findRootsNoTemp(Roots & roots, bool censor)
{ {
Roots roots;
/* Process direct roots in {gcroots,profiles}. */ /* Process direct roots in {gcroots,profiles}. */
findRoots(stateDir + "/" + gcRootsDir, DT_UNKNOWN, roots); findRoots(stateDir + "/" + gcRootsDir, DT_UNKNOWN, roots);
findRoots(stateDir + "/profiles", DT_UNKNOWN, roots); findRoots(stateDir + "/profiles", DT_UNKNOWN, roots);
@ -333,32 +330,22 @@ Roots LocalStore::findRootsNoTemp()
NIX_ROOT_FINDER environment variable. This is typically used NIX_ROOT_FINDER environment variable. This is typically used
to add running programs to the set of roots (to prevent them to add running programs to the set of roots (to prevent them
from being garbage collected). */ from being garbage collected). */
size_t n = 0; findRuntimeRoots(roots, censor);
for (auto & root : findRuntimeRoots())
roots[fmt("{memory:%d}", n++)] = root;
return roots;
} }
Roots LocalStore::findRoots() Roots LocalStore::findRoots(bool censor)
{ {
Roots roots = findRootsNoTemp(); Roots roots;
findRootsNoTemp(roots, censor);
FDs fds; FDs fds;
pid_t prev = -1; findTempRoots(fds, roots, censor);
size_t n = 0;
for (auto & root : readTempRoots(fds)) {
if (prev != root.first) n = 0;
prev = root.first;
roots[fmt("{temp:%d:%d}", root.first, n++)] = root.second;
}
return roots; return roots;
} }
static void readProcLink(const string & file, Roots & roots)
static void readProcLink(const string & file, StringSet & paths)
{ {
/* 64 is the starting buffer size gnu readlink uses... */ /* 64 is the starting buffer size gnu readlink uses... */
auto bufsiz = ssize_t{64}; auto bufsiz = ssize_t{64};
@ -377,8 +364,8 @@ try_again:
goto try_again; goto try_again;
} }
if (res > 0 && buf[0] == '/') if (res > 0 && buf[0] == '/')
paths.emplace(static_cast<char *>(buf), res); roots[std::string(static_cast<char *>(buf), res)]
return; .emplace(file);
} }
static string quoteRegexChars(const string & raw) static string quoteRegexChars(const string & raw)
@ -387,20 +374,20 @@ static string quoteRegexChars(const string & raw)
return std::regex_replace(raw, specialRegex, R"(\$&)"); return std::regex_replace(raw, specialRegex, R"(\$&)");
} }
static void readFileRoots(const char * path, StringSet & paths) static void readFileRoots(const char * path, Roots & roots)
{ {
try { try {
paths.emplace(readFile(path)); roots[readFile(path)].emplace(path);
} catch (SysError & e) { } catch (SysError & e) {
if (e.errNo != ENOENT && e.errNo != EACCES) if (e.errNo != ENOENT && e.errNo != EACCES)
throw; throw;
} }
} }
PathSet LocalStore::findRuntimeRoots() void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
{ {
PathSet roots; Roots unchecked;
StringSet paths;
auto procDir = AutoCloseDir{opendir("/proc")}; auto procDir = AutoCloseDir{opendir("/proc")};
if (procDir) { if (procDir) {
struct dirent * ent; struct dirent * ent;
@ -410,10 +397,10 @@ PathSet LocalStore::findRuntimeRoots()
while (errno = 0, ent = readdir(procDir.get())) { while (errno = 0, ent = readdir(procDir.get())) {
checkInterrupt(); checkInterrupt();
if (std::regex_match(ent->d_name, digitsRegex)) { if (std::regex_match(ent->d_name, digitsRegex)) {
readProcLink((format("/proc/%1%/exe") % ent->d_name).str(), paths); readProcLink(fmt("/proc/%s/exe" ,ent->d_name), unchecked);
readProcLink((format("/proc/%1%/cwd") % ent->d_name).str(), paths); readProcLink(fmt("/proc/%s/cwd", ent->d_name), unchecked);
auto fdStr = (format("/proc/%1%/fd") % ent->d_name).str(); auto fdStr = fmt("/proc/%s/fd", ent->d_name);
auto fdDir = AutoCloseDir(opendir(fdStr.c_str())); auto fdDir = AutoCloseDir(opendir(fdStr.c_str()));
if (!fdDir) { if (!fdDir) {
if (errno == ENOENT || errno == EACCES) if (errno == ENOENT || errno == EACCES)
@ -422,9 +409,8 @@ PathSet LocalStore::findRuntimeRoots()
} }
struct dirent * fd_ent; struct dirent * fd_ent;
while (errno = 0, fd_ent = readdir(fdDir.get())) { while (errno = 0, fd_ent = readdir(fdDir.get())) {
if (fd_ent->d_name[0] != '.') { if (fd_ent->d_name[0] != '.')
readProcLink((format("%1%/%2%") % fdStr % fd_ent->d_name).str(), paths); readProcLink(fmt("%s/%s", fdStr, fd_ent->d_name), unchecked);
}
} }
if (errno) { if (errno) {
if (errno == ESRCH) if (errno == ESRCH)
@ -434,18 +420,19 @@ PathSet LocalStore::findRuntimeRoots()
fdDir.reset(); fdDir.reset();
try { try {
auto mapLines = auto mapFile = fmt("/proc/%s/maps", ent->d_name);
tokenizeString<std::vector<string>>(readFile((format("/proc/%1%/maps") % ent->d_name).str(), true), "\n"); auto mapLines = tokenizeString<std::vector<string>>(readFile(mapFile, true), "\n");
for (const auto & line : mapLines) { for (const auto & line : mapLines) {
auto match = std::smatch{}; auto match = std::smatch{};
if (std::regex_match(line, match, mapRegex)) if (std::regex_match(line, match, mapRegex))
paths.emplace(match[1]); unchecked[match[1]].emplace(mapFile);
} }
auto envString = readFile((format("/proc/%1%/environ") % ent->d_name).str(), true); auto envFile = fmt("/proc/%s/environ", ent->d_name);
auto envString = readFile(envFile, true);
auto env_end = std::sregex_iterator{}; auto env_end = std::sregex_iterator{};
for (auto i = std::sregex_iterator{envString.begin(), envString.end(), storePathRegex}; i != env_end; ++i) for (auto i = std::sregex_iterator{envString.begin(), envString.end(), storePathRegex}; i != env_end; ++i)
paths.emplace(i->str()); unchecked[i->str()].emplace(envFile);
} catch (SysError & e) { } catch (SysError & e) {
if (errno == ENOENT || errno == EACCES || errno == ESRCH) if (errno == ENOENT || errno == EACCES || errno == ESRCH)
continue; continue;
@ -465,7 +452,7 @@ PathSet LocalStore::findRuntimeRoots()
for (const auto & line : lsofLines) { for (const auto & line : lsofLines) {
std::smatch match; std::smatch match;
if (std::regex_match(line, match, lsofRegex)) if (std::regex_match(line, match, lsofRegex))
paths.emplace(match[1]); unchecked[match[1]].emplace("{lsof}");
} }
} catch (ExecError & e) { } catch (ExecError & e) {
/* lsof not installed, lsof failed */ /* lsof not installed, lsof failed */
@ -473,21 +460,23 @@ PathSet LocalStore::findRuntimeRoots()
#endif #endif
#if defined(__linux__) #if defined(__linux__)
readFileRoots("/proc/sys/kernel/modprobe", paths); readFileRoots("/proc/sys/kernel/modprobe", unchecked);
readFileRoots("/proc/sys/kernel/fbsplash", paths); readFileRoots("/proc/sys/kernel/fbsplash", unchecked);
readFileRoots("/proc/sys/kernel/poweroff_cmd", paths); readFileRoots("/proc/sys/kernel/poweroff_cmd", unchecked);
#endif #endif
for (auto & i : paths) for (auto & [target, links] : unchecked) {
if (isInStore(i)) { if (isInStore(target)) {
Path path = toStorePath(i); Path path = toStorePath(target);
if (roots.find(path) == roots.end() && isStorePath(path) && isValidPath(path)) { if (isStorePath(path) && isValidPath(path)) {
debug(format("got additional root '%1%'") % path); debug(format("got additional root '%1%'") % path);
roots.insert(path); if (censor)
roots[path].insert(censored);
else
roots[path].insert(links.begin(), links.end());
}
} }
} }
return roots;
} }
@ -754,16 +743,20 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
/* Find the roots. Since we've grabbed the GC lock, the set of /* Find the roots. Since we've grabbed the GC lock, the set of
permanent roots cannot increase now. */ permanent roots cannot increase now. */
printError(format("finding garbage collector roots...")); printError(format("finding garbage collector roots..."));
Roots rootMap = options.ignoreLiveness ? Roots() : findRootsNoTemp(); Roots rootMap;
if (!options.ignoreLiveness)
findRootsNoTemp(rootMap, true);
for (auto & i : rootMap) state.roots.insert(i.second); for (auto & i : rootMap) state.roots.insert(i.first);
/* Read the temporary roots. This acquires read locks on all /* Read the temporary roots. This acquires read locks on all
per-process temporary root files. So after this point no paths per-process temporary root files. So after this point no paths
can be added to the set of temporary roots. */ can be added to the set of temporary roots. */
FDs fds; FDs fds;
for (auto & root : readTempRoots(fds)) Roots tempRoots;
state.tempRoots.insert(root.second); findTempRoots(fds, tempRoots, true);
for (auto & root : tempRoots)
state.tempRoots.insert(root.first);
state.roots.insert(state.tempRoots.begin(), state.tempRoots.end()); state.roots.insert(state.tempRoots.begin(), state.tempRoots.end());
/* After this point the set of roots or temporary roots cannot /* After this point the set of roots or temporary roots cannot

View file

@ -180,11 +180,11 @@ private:
typedef std::shared_ptr<AutoCloseFD> FDPtr; typedef std::shared_ptr<AutoCloseFD> FDPtr;
typedef list<FDPtr> FDs; typedef list<FDPtr> FDs;
std::set<std::pair<pid_t, Path>> readTempRoots(FDs & fds); void findTempRoots(FDs & fds, Roots & roots, bool censor);
public: public:
Roots findRoots() override; Roots findRoots(bool censor) override;
void collectGarbage(const GCOptions & options, GCResults & results) override; void collectGarbage(const GCOptions & options, GCResults & results) override;
@ -267,9 +267,9 @@ private:
void findRoots(const Path & path, unsigned char type, Roots & roots); void findRoots(const Path & path, unsigned char type, Roots & roots);
Roots findRootsNoTemp(); void findRootsNoTemp(Roots & roots, bool censor);
PathSet findRuntimeRoots(); void findRuntimeRoots(Roots & roots, bool censor);
void removeUnusedLinks(const GCState & state); void removeUnusedLinks(const GCState & state);

View file

@ -596,7 +596,7 @@ void RemoteStore::syncWithGC()
} }
Roots RemoteStore::findRoots() Roots RemoteStore::findRoots(bool censor)
{ {
auto conn(getConnection()); auto conn(getConnection());
conn->to << wopFindRoots; conn->to << wopFindRoots;
@ -606,7 +606,7 @@ Roots RemoteStore::findRoots()
while (count--) { while (count--) {
Path link = readString(conn->from); Path link = readString(conn->from);
Path target = readStorePath(*this, conn->from); Path target = readStorePath(*this, conn->from);
result[link] = target; result[target].emplace(link);
} }
return result; return result;
} }

View file

@ -82,7 +82,7 @@ public:
void syncWithGC() override; void syncWithGC() override;
Roots findRoots() override; Roots findRoots(bool censor) override;
void collectGarbage(const GCOptions & options, GCResults & results) override; void collectGarbage(const GCOptions & options, GCResults & results) override;

View file

@ -126,6 +126,7 @@ ref<Aws::Client::ClientConfiguration> S3Helper::makeConfig(const string & region
res->endpointOverride = endpoint; res->endpointOverride = endpoint;
} }
res->requestTimeoutMs = 600 * 1000; res->requestTimeoutMs = 600 * 1000;
res->connectTimeoutMs = 5 * 1000;
res->retryStrategy = std::make_shared<RetryStrategy>(); res->retryStrategy = std::make_shared<RetryStrategy>();
res->caFile = settings.caFile; res->caFile = settings.caFile;
return res; return res;

View file

@ -842,12 +842,11 @@ namespace nix {
RegisterStoreImplementation::Implementations * RegisterStoreImplementation::implementations = 0; RegisterStoreImplementation::Implementations * RegisterStoreImplementation::implementations = 0;
/* Split URI into protocol+hierarchy part and its parameter set. */
ref<Store> openStore(const std::string & uri_, std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri_)
const Store::Params & extraParams)
{ {
auto uri(uri_); auto uri(uri_);
Store::Params params(extraParams); Store::Params params;
auto q = uri.find('?'); auto q = uri.find('?');
if (q != std::string::npos) { if (q != std::string::npos) {
for (auto s : tokenizeString<Strings>(uri.substr(q + 1), "&")) { for (auto s : tokenizeString<Strings>(uri.substr(q + 1), "&")) {
@ -873,6 +872,15 @@ ref<Store> openStore(const std::string & uri_,
} }
uri = uri_.substr(0, q); uri = uri_.substr(0, q);
} }
return {uri, params};
}
ref<Store> openStore(const std::string & uri_,
const Store::Params & extraParams)
{
auto [uri, uriParams] = splitUriAndParams(uri_);
auto params = extraParams;
params.insert(uriParams.begin(), uriParams.end());
for (auto fun : *RegisterStoreImplementation::implementations) { for (auto fun : *RegisterStoreImplementation::implementations) {
auto store = fun(uri, params); auto store = fun(uri, params);

View file

@ -11,6 +11,8 @@
#include <atomic> #include <atomic>
#include <limits> #include <limits>
#include <map> #include <map>
#include <unordered_map>
#include <unordered_set>
#include <memory> #include <memory>
#include <string> #include <string>
@ -47,7 +49,7 @@ const size_t storePathHashLen = 32; // i.e. 160 bits
const uint32_t exportMagic = 0x4558494e; const uint32_t exportMagic = 0x4558494e;
typedef std::map<Path, Path> Roots; typedef std::unordered_map<Path, std::unordered_set<std::string>> Roots;
struct GCOptions struct GCOptions
@ -483,8 +485,10 @@ public:
/* Find the roots of the garbage collector. Each root is a pair /* Find the roots of the garbage collector. Each root is a pair
(link, storepath) where `link' is the path of the symlink (link, storepath) where `link' is the path of the symlink
outside of the Nix store that point to `storePath'. */ outside of the Nix store that point to `storePath'. If
virtual Roots findRoots() 'censor' is true, privacy-sensitive information about roots
found in /proc is censored. */
virtual Roots findRoots(bool censor)
{ unsupported("findRoots"); } { unsupported("findRoots"); }
/* Perform a garbage collection. */ /* Perform a garbage collection. */
@ -798,4 +802,8 @@ ValidPathInfo decodeValidPathInfo(std::istream & str,
for paths created by makeFixedOutputPath() / addToStore(). */ for paths created by makeFixedOutputPath() / addToStore(). */
std::string makeFixedOutputCA(bool recursive, const Hash & hash); std::string makeFixedOutputCA(bool recursive, const Hash & hash);
/* Split URI into protocol+hierarchy part and its parameter set. */
std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri);
} }

View file

@ -331,7 +331,7 @@ struct RestoreSink : ParseSink
filesystem doesn't support preallocation (e.g. on filesystem doesn't support preallocation (e.g. on
OpenSolaris). Since preallocation is just an OpenSolaris). Since preallocation is just an
optimisation, ignore it. */ optimisation, ignore it. */
if (errno && errno != EINVAL) if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS)
throw SysError(format("preallocating file of %1% bytes") % len); throw SysError(format("preallocating file of %1% bytes") % len);
} }
#endif #endif

View file

@ -475,11 +475,19 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopFindRoots: { case wopFindRoots: {
logger->startWork(); logger->startWork();
Roots roots = store->findRoots(); Roots roots = store->findRoots(!trusted);
logger->stopWork(); logger->stopWork();
to << roots.size();
size_t size = 0;
for (auto & i : roots) for (auto & i : roots)
to << i.first << i.second; size += i.second.size();
to << size;
for (auto & [target, links] : roots)
for (auto & link : links)
to << link << target;
break; break;
} }

View file

@ -427,10 +427,11 @@ static void opQuery(Strings opFlags, Strings opArgs)
maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise), maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise),
referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations); referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations);
} }
Roots roots = store->findRoots(); Roots roots = store->findRoots(false);
for (auto & i : roots) for (auto & [target, links] : roots)
if (referrers.find(i.second) != referrers.end()) if (referrers.find(target) != referrers.end())
cout << format("%1%\n") % i.first; for (auto & link : links)
cout << format("%1% -> %2%\n") % link % target;
break; break;
} }
@ -485,12 +486,17 @@ static void opReadLog(Strings opFlags, Strings opArgs)
static void opDumpDB(Strings opFlags, Strings opArgs) static void opDumpDB(Strings opFlags, Strings opArgs)
{ {
if (!opFlags.empty()) throw UsageError("unknown flag"); if (!opFlags.empty()) throw UsageError("unknown flag");
if (!opArgs.empty()) if (!opArgs.empty()) {
throw UsageError("no arguments expected"); for (auto & i : opArgs)
i = store->followLinksToStorePath(i);
for (auto & i : opArgs)
cout << store->makeValidityRegistration({i}, true, true);
} else {
PathSet validPaths = store->queryAllValidPaths(); PathSet validPaths = store->queryAllValidPaths();
for (auto & i : validPaths) for (auto & i : validPaths)
cout << store->makeValidityRegistration({i}, true, true); cout << store->makeValidityRegistration({i}, true, true);
} }
}
static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) static void registerValidity(bool reregister, bool hashGiven, bool canonicalise)
@ -585,9 +591,14 @@ static void opGC(Strings opFlags, Strings opArgs)
if (!opArgs.empty()) throw UsageError("no arguments expected"); if (!opArgs.empty()) throw UsageError("no arguments expected");
if (printRoots) { if (printRoots) {
Roots roots = store->findRoots(); Roots roots = store->findRoots(false);
for (auto & i : roots) std::set<std::pair<Path, Path>> roots2;
cout << i.first << " -> " << i.second << std::endl; // Transpose and sort the roots.
for (auto & [target, links] : roots)
for (auto & link : links)
roots2.emplace(link, target);
for (auto & [link, target] : roots2)
std::cout << link << " -> " << target << "\n";
} }
else { else {

View file

@ -7,7 +7,7 @@ outPath=$(nix-store -rvv "$drvPath")
rm -f "$NIX_STATE_DIR"/gcroots/foo rm -f "$NIX_STATE_DIR"/gcroots/foo
ln -sf $outPath "$NIX_STATE_DIR"/gcroots/foo ln -sf $outPath "$NIX_STATE_DIR"/gcroots/foo
[ "$(nix-store -q --roots $outPath)" = "$NIX_STATE_DIR"/gcroots/foo ] [ "$(nix-store -q --roots $outPath)" = "$NIX_STATE_DIR/gcroots/foo -> $outPath" ]
nix-store --gc --print-roots | grep $outPath nix-store --gc --print-roots | grep $outPath
nix-store --gc --print-live | grep $outPath nix-store --gc --print-live | grep $outPath

View file

@ -0,0 +1 @@
true

View file

@ -0,0 +1,24 @@
let
drv = derivation {
name = "fail";
builder = "/bin/false";
system = "x86_64-linux";
outputs = [ "out" "foo" ];
};
path = "${./eval-okay-context-introspection.nix}";
desired-context = {
"${builtins.unsafeDiscardStringContext path}" = {
path = true;
};
"${builtins.unsafeDiscardStringContext drv.drvPath}" = {
outputs = [ "foo" "out" ];
allOutputs = true;
};
};
legit-context = builtins.getContext "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}";
constructed-context = builtins.getContext (builtins.appendContext "" desired-context);
in legit-context == constructed-context

View file

@ -20,6 +20,8 @@ with builtins;
(isFloat (1 - 2.0)) (isFloat (1 - 2.0))
(isBool (true && false)) (isBool (true && false))
(isBool null) (isBool null)
(isPath /nix/store)
(isPath ./.)
(isAttrs { x = 123; }) (isAttrs { x = 123; })
(isAttrs null) (isAttrs null)
(typeOf (3 * 4)) (typeOf (3 * 4))