about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/manual/command-ref/env-common.xml34
-rw-r--r--doc/manual/command-ref/nix-instantiate.xml7
-rw-r--r--doc/manual/command-ref/opt-common.xml11
-rw-r--r--doc/manual/expressions/advanced-attributes.xml21
-rw-r--r--doc/manual/release-notes/rl-18.xml14
-rw-r--r--release.nix4
-rwxr-xr-xscripts/build-remote.pl.in11
-rw-r--r--scripts/download-from-binary-cache.pl.in2
-rwxr-xr-xscripts/download-using-manifests.pl.in1
-rwxr-xr-xscripts/nix-build.in2
-rwxr-xr-xscripts/nix-channel.in3
-rwxr-xr-xscripts/nix-copy-closure.in1
-rwxr-xr-xscripts/nix-install-package.in3
-rwxr-xr-xscripts/nix-prefetch-url.in4
-rwxr-xr-xscripts/nix-pull.in3
-rwxr-xr-xscripts/nix-push.in3
-rw-r--r--src/libexpr/eval.cc2
-rw-r--r--src/libexpr/local.mk6
-rw-r--r--src/libstore/build.cc40
-rw-r--r--src/libstore/local-store.hh3
-rw-r--r--src/libstore/optimise-store.cc16
-rw-r--r--src/libstore/remote-store.cc7
-rw-r--r--src/libstore/remote-store.hh4
-rw-r--r--src/libstore/store-api.hh4
-rw-r--r--src/libstore/worker-protocol.hh1
-rw-r--r--src/nix-daemon/nix-daemon.cc7
-rw-r--r--src/nix-store/nix-store.cc20
-rw-r--r--tests/check-refs.nix12
-rw-r--r--tests/check-refs.sh6
-rw-r--r--tests/check-reqs.nix57
-rw-r--r--tests/check-reqs.sh13
-rw-r--r--tests/local.mk3
-rw-r--r--tests/remote-builds.nix5
33 files changed, 260 insertions, 70 deletions
diff --git a/doc/manual/command-ref/env-common.xml b/doc/manual/command-ref/env-common.xml
index c501d1c011c0..b52e450309f4 100644
--- a/doc/manual/command-ref/env-common.xml
+++ b/doc/manual/command-ref/env-common.xml
@@ -11,8 +11,8 @@
 
 <variablelist xml:id="env-common">
 
-  
-<varlistentry><term><envar>NIX_PATH</envar></term>
+
+<varlistentry xml:id="env-NIX_PATH"><term><envar>NIX_PATH</envar></term>
 
   <listitem>
 
@@ -28,7 +28,7 @@
     <filename>/home/eelco/Dev</filename> and
     <filename>/etc/nixos</filename>, in that order.  It is also
     possible to match paths against a prefix.  For example, the value
-    
+
     <screen>
 nixpkgs=/home/eelco/Dev/nixpkgs-branch:/etc/nixos</screen>
 
@@ -39,12 +39,12 @@ nixpkgs=/home/eelco/Dev/nixpkgs-branch:/etc/nixos</screen>
     <filename>/etc/nixos/nixpkgs/<replaceable>path</replaceable></filename>.
     </para>
 
-    <para>The search path can be extended using the
-    <option>-I</option> option, which takes precedence over
+    <para>The search path can be extended using the <option
+    linkend="opt-I">-I</option> option, which takes precedence over
     <envar>NIX_PATH</envar>.</para></listitem>
 
 </varlistentry>
-    
+
 
 <varlistentry><term><envar>NIX_IGNORE_SYMLINK_STORE</envar></term>
 
@@ -67,7 +67,7 @@ nixpkgs=/home/eelco/Dev/nixpkgs-branch:/etc/nixos</screen>
   you’re better off using <literal>bind</literal> mount points, e.g.,
 
   <screen>
-$ mkdir /nix   
+$ mkdir /nix
 $ mount -o bind /mnt/otherdisk/nix /nix</screen>
 
   Consult the <citerefentry><refentrytitle>mount</refentrytitle>
@@ -82,7 +82,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
 
   <listitem><para>Overrides the location of the Nix store (default
   <filename><replaceable>prefix</replaceable>/store</filename>).</para></listitem>
-  
+
 </varlistentry>
 
 
@@ -91,7 +91,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
   <listitem><para>Overrides the location of the Nix static data
   directory (default
   <filename><replaceable>prefix</replaceable>/share</filename>).</para></listitem>
-  
+
 </varlistentry>
 
 
@@ -99,7 +99,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
 
   <listitem><para>Overrides the location of the Nix log directory
   (default <filename><replaceable>prefix</replaceable>/log/nix</filename>).</para></listitem>
-  
+
 </varlistentry>
 
 
@@ -107,7 +107,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
 
   <listitem><para>Overrides the location of the Nix state directory
   (default <filename><replaceable>prefix</replaceable>/var/nix</filename>).</para></listitem>
-  
+
 </varlistentry>
 
 
@@ -116,7 +116,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
   <listitem><para>Overrides the location of the Nix database (default
   <filename><replaceable>$NIX_STATE_DIR</replaceable>/db</filename>, i.e.,
   <filename><replaceable>prefix</replaceable>/var/nix/db</filename>).</para></listitem>
-  
+
 </varlistentry>
 
 
@@ -125,7 +125,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
   <listitem><para>Overrides the location of the Nix configuration
   directory (default
   <filename><replaceable>prefix</replaceable>/etc/nix</filename>).</para></listitem>
-  
+
 </varlistentry>
 
 
@@ -135,7 +135,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
   files.  In particular, this includes temporary build directories;
   these can take up substantial amounts of disk space.  The default is
   <filename>/tmp</filename>.</para></listitem>
-  
+
 </varlistentry>
 
 
@@ -233,7 +233,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
       store derivation itself).</para></listitem>
 
     </varlistentry>
-  
+
     <varlistentry><term><filename>outputs</filename></term>
 
       <listitem><para>The set of store paths that are outputs of the
@@ -282,7 +282,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
 
 </varlistentry>
 
-    
+
 <varlistentry xml:id="envar-other-stores"><term><envar>NIX_OTHER_STORES</envar></term>
 
   <listitem><para>This variable contains the paths of remote Nix
@@ -333,7 +333,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
 
 </varlistentry>
 
-    
+
 </variablelist>
 
 
diff --git a/doc/manual/command-ref/nix-instantiate.xml b/doc/manual/command-ref/nix-instantiate.xml
index a4e45cf97af4..8649cd58cc60 100644
--- a/doc/manual/command-ref/nix-instantiate.xml
+++ b/doc/manual/command-ref/nix-instantiate.xml
@@ -104,9 +104,10 @@ input.</para>
   <varlistentry><term><option>--find-file</option></term>
 
     <listitem><para>Look up the given files in Nix’s search path (as
-    specified by the <envar>NIX_PATH</envar> environment variable).
-    If found, print the corresponding absolute paths on standard
-    output.  For instance, if <envar>NIX_PATH</envar> is
+    specified by the <envar linkend="env-NIX_PATH">NIX_PATH</envar>
+    environment variable).  If found, print the corresponding absolute
+    paths on standard output.  For instance, if
+    <envar>NIX_PATH</envar> is
     <literal>nixpkgs=/home/alice/nixpkgs</literal>, then
     <literal>nix-instantiate --find-file nixpkgs/default.nix</literal>
     will print
diff --git a/doc/manual/command-ref/opt-common.xml b/doc/manual/command-ref/opt-common.xml
index 3486c7e7d852..c7e8ae1ed05f 100644
--- a/doc/manual/command-ref/opt-common.xml
+++ b/doc/manual/command-ref/opt-common.xml
@@ -351,13 +351,14 @@
 </varlistentry>
 
 
-<varlistentry><term><option>-I</option> <replaceable>path</replaceable></term>
+<varlistentry xml:id="opt-I"><term><option>-I</option> <replaceable>path</replaceable></term>
 
   <listitem><para>Add a path to the Nix expression search path.  This
-  option may be given multiple times.  See the <envar>NIX_PATH</envar>
-  environment variable for information on the semantics of the Nix
-  search path.  Paths added through <option>-I</option> take
-  precedence over <envar>NIX_PATH</envar>.</para></listitem>
+  option may be given multiple times.  See the <envar
+  linkend="env-NIX_PATH">NIX_PATH</envar> environment variable for
+  information on the semantics of the Nix search path.  Paths added
+  through <option>-I</option> take precedence over
+  <envar>NIX_PATH</envar>.</para></listitem>
 
 </varlistentry>
 
diff --git a/doc/manual/expressions/advanced-attributes.xml b/doc/manual/expressions/advanced-attributes.xml
index 40a5a80acdb6..f8b84b98cb82 100644
--- a/doc/manual/expressions/advanced-attributes.xml
+++ b/doc/manual/expressions/advanced-attributes.xml
@@ -32,6 +32,25 @@ allowedReferences = [];
   </varlistentry>
 
 
+  <varlistentry><term><varname>allowedRequisites</varname></term>
+
+    <listitem><para>This attribute is similar to
+    <varname>allowedReferences</varname>, but it specifies the legal
+    requisites of the whole closure, so all the dependencies
+    recursively.  For example,
+
+<programlisting>
+allowedReferences = [ foobar ];
+</programlisting>
+
+    enforces that the output of a derivation cannot have any other
+    runtime dependency than <varname>foobar</varname>, and in addition
+    it enforces that <varname>foobar</varname> itself doesn't
+    introduce any other dependency itself.</para></listitem>
+
+  </varlistentry>
+
+
   <varlistentry><term><varname>exportReferencesGraph</varname></term>
 
     <listitem><para>This attribute allows builders access to the
@@ -240,4 +259,4 @@ impureEnvVars = [ "http_proxy" "https_proxy" <replaceable>...</replaceable> ];
 
 </variablelist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-18.xml b/doc/manual/release-notes/rl-18.xml
index 0fe3c2de761e..aace95210ff7 100644
--- a/doc/manual/release-notes/rl-18.xml
+++ b/doc/manual/release-notes/rl-18.xml
@@ -6,6 +6,16 @@
 
 <title>Release 1.8 (TBA)</title>
 
-<para>TODO</para>
+<itemizedlist>
 
-</chapter>
\ No newline at end of file
+  <listitem><para>Derivations can specify the new special attribute
+  <varname>allowedRequisites</varname>, which has a similar meaning to
+  <varname>allowedReferences</varname>. But instead of only enforcing
+  to explicitly specify the immediate references, it requires the
+  derivation to specify all the dependencies recursively (hence the
+  name, requisites) that are used by the resulting
+  output.</para></listitem>
+
+</itemizedlist>
+
+</chapter>
diff --git a/release.nix b/release.nix
index bc4c06a0b792..3e741660ccfe 100644
--- a/release.nix
+++ b/release.nix
@@ -214,11 +214,11 @@ let
     # System tests.
     tests.remote_builds = (import ./tests/remote-builds.nix rec {
       nix = build.x86_64-linux; system = "x86_64-linux";
-    }).test;
+    });
 
     tests.nix_copy_closure = (import ./tests/nix-copy-closure.nix rec {
       nix = build.x86_64-linux; system = "x86_64-linux";
-    }).test;
+    });
 
 
     # Aggregate job containing the release-critical jobs.
diff --git a/scripts/build-remote.pl.in b/scripts/build-remote.pl.in
index 2eb339675904..5f0c72b663b8 100755
--- a/scripts/build-remote.pl.in
+++ b/scripts/build-remote.pl.in
@@ -1,5 +1,6 @@
 #! @perl@ -w @perlFlags@
 
+use utf8;
 use Fcntl qw(:DEFAULT :flock);
 use English '-no_match_vars';
 use IO::Handle;
@@ -7,8 +8,14 @@ use Nix::Config;
 use Nix::SSH;
 use Nix::CopyClosure;
 use Nix::Store;
+use Encode;
 no warnings('once');
 
+STDERR->autoflush(1);
+binmode STDERR, ":encoding(utf8)";
+
+my $debug = defined $ENV{NIX_DEBUG_HOOK};
+
 
 # General operation:
 #
@@ -148,7 +155,7 @@ REQ: while (1) {
             }
         }
 
-        if (defined $ENV{NIX_DEBUG_HOOK}) {
+        if ($debug) {
             print STDERR "load on " . $_->{machine}->{hostName} . " = " . $_->{load} . "\n"
                 foreach @available;
         }
@@ -258,7 +265,7 @@ writeInt($maxSilentTime, $to);
 writeInt($buildTimeout, $to);
 my $res = readInt($from);
 if ($res != 0) {
-    my $msg = readString($from);
+    my $msg = decode("utf-8", readString($from));
     print STDERR "error: $msg on ‘$hostName’\n";
     exit $res;
 }
diff --git a/scripts/download-from-binary-cache.pl.in b/scripts/download-from-binary-cache.pl.in
index e09b051a4aa6..cdce8eb74a41 100644
--- a/scripts/download-from-binary-cache.pl.in
+++ b/scripts/download-from-binary-cache.pl.in
@@ -1,5 +1,6 @@
 #! @perl@ -w @perlFlags@
 
+use utf8;
 use DBI;
 use DBD::SQLite;
 use File::Basename;
@@ -12,6 +13,7 @@ use WWW::Curl::Easy;
 use WWW::Curl::Multi;
 use strict;
 
+binmode STDERR, ":encoding(utf8)";
 
 Nix::Config::readConfig;
 
diff --git a/scripts/download-using-manifests.pl.in b/scripts/download-using-manifests.pl.in
index e849a930e71e..591cd6b43a3a 100755
--- a/scripts/download-using-manifests.pl.in
+++ b/scripts/download-using-manifests.pl.in
@@ -9,6 +9,7 @@ use Nix::Utils;
 use POSIX qw(strftime);
 
 STDOUT->autoflush(1);
+binmode STDERR, ":encoding(utf8)";
 
 my $logFile = "$Nix::Config::logDir/downloads";
 
diff --git a/scripts/nix-build.in b/scripts/nix-build.in
index fb92a4909d4c..f8cf318ff07a 100755
--- a/scripts/nix-build.in
+++ b/scripts/nix-build.in
@@ -1,10 +1,12 @@
 #! @perl@ -w @perlFlags@
 
+use utf8;
 use strict;
 use Nix::Config;
 use Nix::Store;
 use Nix::Utils;
 
+binmode STDERR, ":encoding(utf8)";
 
 my $dryRun = 0;
 my $verbose = 0;
diff --git a/scripts/nix-channel.in b/scripts/nix-channel.in
index 407f27490410..e45b913382a7 100755
--- a/scripts/nix-channel.in
+++ b/scripts/nix-channel.in
@@ -1,11 +1,14 @@
 #! @perl@ -w @perlFlags@
 
+use utf8;
 use strict;
 use File::Basename;
 use File::Path qw(mkpath);
 use Nix::Config;
 use Nix::Manifest;
 
+binmode STDERR, ":encoding(utf8)";
+
 Nix::Config::readConfig;
 
 my $manifestDir = $Nix::Config::manifestDir;
diff --git a/scripts/nix-copy-closure.in b/scripts/nix-copy-closure.in
index 10c2a9171d67..f4d186256b70 100755
--- a/scripts/nix-copy-closure.in
+++ b/scripts/nix-copy-closure.in
@@ -6,6 +6,7 @@ use Nix::Store;
 use Nix::CopyClosure;
 use List::Util qw(sum);
 
+binmode STDERR, ":encoding(utf8)";
 
 if (scalar @ARGV < 1) {
     print STDERR <<EOF
diff --git a/scripts/nix-install-package.in b/scripts/nix-install-package.in
index 9340f1b729e6..23f6efbcdb87 100755
--- a/scripts/nix-install-package.in
+++ b/scripts/nix-install-package.in
@@ -1,9 +1,12 @@
 #! @perl@ -w @perlFlags@
 
+use utf8;
 use strict;
 use Nix::Config;
 use Nix::Utils;
 
+binmode STDERR, ":encoding(utf8)";
+
 
 # Parse the command line arguments.
 my @args = @ARGV;
diff --git a/scripts/nix-prefetch-url.in b/scripts/nix-prefetch-url.in
index 869d29c39909..6effbe208146 100755
--- a/scripts/nix-prefetch-url.in
+++ b/scripts/nix-prefetch-url.in
@@ -1,5 +1,6 @@
 #! @perl@ -w @perlFlags@
 
+use utf8;
 use strict;
 use File::Basename;
 use File::stat;
@@ -7,6 +8,9 @@ use Nix::Store;
 use Nix::Config;
 use Nix::Utils;
 
+binmode STDERR, ":encoding(utf8)";
+
+
 my $hashType = $ENV{'NIX_HASH_ALGO'} || "sha256"; # obsolete
 my $cacheDir = $ENV{'NIX_DOWNLOAD_CACHE'};
 
diff --git a/scripts/nix-pull.in b/scripts/nix-pull.in
index f9785d8e5d19..995b50935964 100755
--- a/scripts/nix-pull.in
+++ b/scripts/nix-pull.in
@@ -1,9 +1,12 @@
 #! @perl@ -w @perlFlags@
 
+use utf8;
 use strict;
 use Nix::Config;
 use Nix::Manifest;
 
+binmode STDERR, ":encoding(utf8)";
+
 my $manifestDir = $Nix::Config::manifestDir;
 
 
diff --git a/scripts/nix-push.in b/scripts/nix-push.in
index b0cb6d0da782..c6d187704bc7 100755
--- a/scripts/nix-push.in
+++ b/scripts/nix-push.in
@@ -1,5 +1,6 @@
 #! @perl@ -w @perlFlags@
 
+use utf8;
 use strict;
 use File::Basename;
 use File::Path qw(mkpath);
@@ -11,6 +12,8 @@ use Nix::Manifest;
 use Nix::Utils;
 use Nix::Crypto;
 
+binmode STDERR, ":encoding(utf8)";
+
 my $tmpDir = mkTempDir("nix-push");
 
 my $nixExpr = "$tmpDir/create-nars.nix";
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 5cb6fc3cfe7b..2af6a6787dfc 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -1330,7 +1330,7 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
 {
     string path = coerceToString(pos, v, context, false, false);
     if (path == "" || path[0] != '/')
-        throwEvalError("string ‘%1%’ doesn't represent an absolute path, at %1%", path, pos);
+        throwEvalError("string ‘%1%’ doesn't represent an absolute path, at %2%", path, pos);
     return path;
 }
 
diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk
index 75a0e185e369..620a03538bcd 100644
--- a/src/libexpr/local.mk
+++ b/src/libexpr/local.mk
@@ -15,16 +15,14 @@ libexpr_LDFLAGS = -ldl
 # because inline functions in libexpr's header files call libgc.
 libexpr_LDFLAGS_PROPAGATED = $(BDW_GC_LIBS)
 
+libexpr_ORDER_AFTER := $(d)/parser-tab.cc $(d)/parser-tab.hh $(d)/lexer-tab.cc $(d)/lexer-tab.hh
+
 $(d)/parser-tab.cc $(d)/parser-tab.hh: $(d)/parser.y
 	$(trace-gen) bison -v -o $(libexpr_DIR)/parser-tab.cc $< -d
 
 $(d)/lexer-tab.cc $(d)/lexer-tab.hh: $(d)/lexer.l
 	$(trace-gen) flex --outfile $(libexpr_DIR)/lexer-tab.cc --header-file=$(libexpr_DIR)/lexer-tab.hh $<
 
-$(d)/lexer-tab.o: $(d)/lexer-tab.hh $(d)/parser-tab.hh
-
-$(d)/parser-tab.o: $(d)/lexer-tab.hh $(d)/parser-tab.hh
-
 clean-files += $(d)/parser-tab.cc $(d)/parser-tab.hh $(d)/lexer-tab.cc $(d)/lexer-tab.hh
 
 dist-files += $(d)/parser-tab.cc $(d)/parser-tab.hh $(d)/lexer-tab.cc $(d)/lexer-tab.hh
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index c547a5cbfecf..0d290d7879b0 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -2318,16 +2318,36 @@ void DerivationGoal::registerOutputs()
                 debug(format("referenced input: ‘%1%’") % *i);
         }
 
-        /* If the derivation specifies an `allowedReferences'
-           attribute (containing a list of paths that the output may
-           refer to), check that all references are in that list.  !!!
-           allowedReferences should really be per-output. */
-        if (drv.env.find("allowedReferences") != drv.env.end()) {
-            PathSet allowed = parseReferenceSpecifiers(drv, get(drv.env, "allowedReferences"));
-            foreach (PathSet::iterator, i, references)
-                if (allowed.find(*i) == allowed.end())
-                    throw BuildError(format("output is not allowed to refer to path ‘%1%’") % *i);
-        }
+        /* Enforce `allowedReferences' and friends. */
+        auto checkRefs = [&](const string & attrName, bool allowed, bool recursive) {
+            if (drv.env.find(attrName) == drv.env.end()) return;
+
+            PathSet spec = parseReferenceSpecifiers(drv, get(drv.env, attrName));
+
+            PathSet used;
+            if (recursive) {
+                /* Our requisites are the union of the closures of our references. */
+                for (auto & i : references)
+                    /* Don't call computeFSClosure on ourselves. */
+                    if (actualPath != i)
+                        computeFSClosure(worker.store, i, used);
+            } else
+                used = references;
+
+            for (auto & i : used)
+                if (allowed) {
+                    if (spec.find(i) == spec.end())
+                        throw BuildError(format("output (‘%1%’) is not allowed to refer to path ‘%2%’") % actualPath % i);
+                } else {
+                    if (spec.find(i) != spec.end())
+                        throw BuildError(format("output (‘%1%’) is not allowed to refer to path ‘%2%’") % actualPath % i);
+                }
+        };
+
+        checkRefs("allowedReferences", true, false);
+        checkRefs("allowedRequisites", true, true);
+        checkRefs("disallowedReferences", false, false);
+        checkRefs("disallowedRequisites", false, true);
 
         worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
 
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index e58e6563f1cf..dccdba533a0c 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -167,6 +167,9 @@ public:
        files with the same contents. */
     void optimiseStore(OptimiseStats & stats);
 
+    /* Generic variant of the above method.  */
+    void optimiseStore();
+
     /* Optimise a single store path. */
     void optimisePath(const Path & path);
 
diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc
index 208d9688ed98..dd18d66fa008 100644
--- a/src/libstore/optimise-store.cc
+++ b/src/libstore/optimise-store.cc
@@ -225,6 +225,22 @@ void LocalStore::optimiseStore(OptimiseStats & stats)
     }
 }
 
+static string showBytes(unsigned long long bytes)
+{
+    return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str();
+}
+
+void LocalStore::optimiseStore()
+{
+    OptimiseStats stats;
+
+    optimiseStore(stats);
+
+    printMsg(lvlError,
+        format("%1% freed by hard-linking %2% files")
+        % showBytes(stats.bytesFreed)
+        % stats.filesLinked);
+}
 
 void LocalStore::optimisePath(const Path & path)
 {
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 8ef673783ec5..a0e9f22410f7 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -579,6 +579,13 @@ void RemoteStore::clearFailedPaths(const PathSet & paths)
     readInt(from);
 }
 
+void RemoteStore::optimiseStore()
+{
+    openConnection();
+    writeInt(wopOptimiseStore, to);
+    processStderr();
+    readInt(from);
+}
 
 void RemoteStore::processStderr(Sink * sink, Source * source)
 {
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index b0101476434b..98774c10b3dc 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -82,7 +82,9 @@ public:
     PathSet queryFailedPaths();
 
     void clearFailedPaths(const PathSet & paths);
-    
+
+    void optimiseStore();
+
 private:
     AutoCloseFD fdSocket;
     FdSink to;
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index b635fee2cf1a..3109f100ef93 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -250,6 +250,10 @@ public:
        `nix-store --register-validity'. */
     string makeValidityRegistration(const PathSet & paths,
         bool showDerivers, bool showHash);
+
+    /* Optimise the disk space usage of the Nix store by hard-linking files
+       with the same contents. */
+    virtual void optimiseStore() = 0;
 };
 
 
diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh
index c7d3a726ab4c..4b040b77ce63 100644
--- a/src/libstore/worker-protocol.hh
+++ b/src/libstore/worker-protocol.hh
@@ -42,6 +42,7 @@ typedef enum {
     wopQueryValidPaths = 31,
     wopQuerySubstitutablePaths = 32,
     wopQueryValidDerivers = 33,
+    wopOptimiseStore = 34
 } WorkerOp;
 
 
diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc
index ced356c34a3d..0f3235d76df8 100644
--- a/src/nix-daemon/nix-daemon.cc
+++ b/src/nix-daemon/nix-daemon.cc
@@ -508,6 +508,13 @@ static void performOp(bool trusted, unsigned int clientVersion,
         break;
     }
 
+    case wopOptimiseStore:
+	startWork();
+	store->optimiseStore();
+	stopWork();
+	writeInt(1, to);
+	break;
+
     default:
         throw Error(format("invalid operation %1%") % op);
     }
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index 8c3744824ce6..51839fac11ee 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -823,16 +823,6 @@ static void opRepairPath(Strings opFlags, Strings opArgs)
     }
 }
 
-
-static void showOptimiseStats(OptimiseStats & stats)
-{
-    printMsg(lvlError,
-        format("%1% freed by hard-linking %2% files")
-        % showBytes(stats.bytesFreed)
-        % stats.filesLinked);
-}
-
-
 /* Optimise the disk space usage of the Nix store by hard-linking
    files with the same contents. */
 static void opOptimise(Strings opFlags, Strings opArgs)
@@ -840,17 +830,9 @@ static void opOptimise(Strings opFlags, Strings opArgs)
     if (!opArgs.empty() || !opFlags.empty())
         throw UsageError("no arguments expected");
 
-    OptimiseStats stats;
-    try {
-        ensureLocalStore().optimiseStore(stats);
-    } catch (...) {
-        showOptimiseStats(stats);
-        throw;
-    }
-    showOptimiseStats(stats);
+    store->optimiseStore();
 }
 
-
 static void opQueryFailedPaths(Strings opFlags, Strings opArgs)
 {
     if (!opArgs.empty() || !opFlags.empty())
diff --git a/tests/check-refs.nix b/tests/check-refs.nix
index 63791fe16094..9d90b0920542 100644
--- a/tests/check-refs.nix
+++ b/tests/check-refs.nix
@@ -55,4 +55,16 @@ rec {
     inherit dep;
   };
 
+  test9 = makeTest 9 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
+    inherit dep;
+    disallowedReferences = [dep];
+  };
+
+  test10 = makeTest 10 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; echo $test5; ln -s $dep $out/link";
+    inherit dep test5;
+    disallowedReferences = [test5];
+  };
+
 }
diff --git a/tests/check-refs.sh b/tests/check-refs.sh
index 9e103664a3a7..34ee22cfc8f7 100644
--- a/tests/check-refs.sh
+++ b/tests/check-refs.sh
@@ -32,3 +32,9 @@ nix-build -o $RESULT check-refs.nix -A test7
 
 # test8 should fail (toFile depending on derivation output).
 (! nix-build -o $RESULT check-refs.nix -A test8)
+
+# test9 should fail (disallowed reference).
+(! nix-build -o $RESULT check-refs.nix -A test9)
+
+# test10 should succeed (no disallowed references).
+nix-build -o $RESULT check-refs.nix -A test10
diff --git a/tests/check-reqs.nix b/tests/check-reqs.nix
new file mode 100644
index 000000000000..41436cb48e08
--- /dev/null
+++ b/tests/check-reqs.nix
@@ -0,0 +1,57 @@
+with import ./config.nix;
+
+rec {
+  dep1 = mkDerivation {
+    name = "check-reqs-dep1";
+    builder = builtins.toFile "builder.sh" "mkdir $out; touch $out/file1";
+  };
+
+  dep2 = mkDerivation {
+    name = "check-reqs-dep2";
+    builder = builtins.toFile "builder.sh" "mkdir $out; touch $out/file2";
+  };
+
+  deps = mkDerivation {
+    name = "check-reqs-deps";
+    dep1 = dep1;
+    dep2 = dep2;
+    builder = builtins.toFile "builder.sh" ''
+      mkdir $out
+      ln -s $dep1/file1 $out/file1
+      ln -s $dep2/file2 $out/file2
+    '';
+  };
+
+  makeTest = nr: allowreqs: mkDerivation {
+    name = "check-reqs-" + toString nr;
+    inherit deps;
+    builder = builtins.toFile "builder.sh" ''
+      mkdir $out
+      ln -s $deps $out/depdir1
+    '';
+    allowedRequisites = allowreqs;
+  };
+
+  # When specifying all the requisites, the build succeeds.
+  test1 = makeTest 1 [ dep1 dep2 deps ];
+
+  # But missing anything it fails.
+  test2 = makeTest 2 [ dep2 deps ];
+  test3 = makeTest 3 [ dep1 deps ];
+  test4 = makeTest 4 [ deps ];
+  test5 = makeTest 5 [];
+
+  test6 = mkDerivation {
+    name = "check-reqs";
+    inherit deps;
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $deps $out/depdir1";
+    disallowedRequisites = [dep1];
+  };
+
+  test7 = mkDerivation {
+    name = "check-reqs";
+    inherit deps;
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $deps $out/depdir1";
+    disallowedRequisites = [test1];
+  };
+}
diff --git a/tests/check-reqs.sh b/tests/check-reqs.sh
new file mode 100644
index 000000000000..8b2454915a52
--- /dev/null
+++ b/tests/check-reqs.sh
@@ -0,0 +1,13 @@
+source common.sh
+
+RESULT=$TEST_ROOT/result
+
+nix-build -o $RESULT check-reqs.nix -A test1
+
+(! nix-build -o $RESULT check-reqs.nix -A test2)
+(! nix-build -o $RESULT check-reqs.nix -A test3)
+(! nix-build -o $RESULT check-reqs.nix -A test4)
+(! nix-build -o $RESULT check-reqs.nix -A test5)
+(! nix-build -o $RESULT check-reqs.nix -A test6)
+
+nix-build -o $RESULT check-reqs.nix -A test7
diff --git a/tests/local.mk b/tests/local.mk
index 65aa12637055..69a227495d94 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -10,7 +10,8 @@ nix_tests = \
   remote-store.sh export.sh export-graph.sh negative-caching.sh \
   binary-patching.sh timeout.sh secure-drv-outputs.sh nix-channel.sh \
   multiple-outputs.sh import-derivation.sh fetchurl.sh optimise-store.sh \
-  binary-cache.sh nix-profile.sh repair.sh dump-db.sh case-hack.sh
+  binary-cache.sh nix-profile.sh repair.sh dump-db.sh case-hack.sh \
+  check-reqs.sh
   # parallel.sh
 
 install-tests += $(foreach x, $(nix_tests), tests/$(x))
diff --git a/tests/remote-builds.nix b/tests/remote-builds.nix
index 81b81b87c115..5e2688d1adcf 100644
--- a/tests/remote-builds.nix
+++ b/tests/remote-builds.nix
@@ -60,6 +60,7 @@ in
           virtualisation.pathsInNixDB = [ config.system.build.extraUtils ];
           nix.package = nix;
           nix.binaryCaches = [ ];
+          programs.ssh.extraConfig = "ConnectTimeout 30";
         };
     };
 
@@ -69,14 +70,14 @@ in
 
       # Create an SSH key on the client.
       my $key = `${pkgs.openssh}/bin/ssh-keygen -t dsa -f key -N ""`;
-      $client->succeed("mkdir -m 700 /root/.ssh");
+      $client->succeed("mkdir -p -m 700 /root/.ssh");
       $client->copyFileFromHost("key", "/root/.ssh/id_dsa");
       $client->succeed("chmod 600 /root/.ssh/id_dsa");
 
       # Install the SSH key on the slaves.
       $client->waitForUnit("network.target");
       foreach my $slave ($slave1, $slave2) {
-          $slave->succeed("mkdir -m 700 /root/.ssh");
+          $slave->succeed("mkdir -p -m 700 /root/.ssh");
           $slave->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
           $slave->waitForUnit("sshd");
           $client->succeed("ssh -o StrictHostKeyChecking=no " . $slave->name() . " 'echo hello world'");