about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.dir-locals.el16
-rw-r--r--configure.ac1
-rw-r--r--corepkgs/buildenv.nix5
-rw-r--r--corepkgs/buildenv.pl2
-rw-r--r--doc/manual/command-ref/nix-collect-garbage.xml1
-rw-r--r--doc/manual/command-ref/nix-shell.xml20
-rw-r--r--doc/manual/command-ref/nix-store.xml27
-rw-r--r--doc/manual/expressions/builtins.xml18
-rw-r--r--doc/manual/installation/prerequisites-source.xml2
-rw-r--r--doc/manual/release-notes/release-notes.xml1
-rw-r--r--doc/manual/release-notes/rl-0.10.1.xml4
-rw-r--r--doc/manual/release-notes/rl-0.10.xml4
-rw-r--r--doc/manual/release-notes/rl-0.11.xml4
-rw-r--r--doc/manual/release-notes/rl-0.12.xml4
-rw-r--r--doc/manual/release-notes/rl-0.13.xml4
-rw-r--r--doc/manual/release-notes/rl-0.14.xml6
-rw-r--r--doc/manual/release-notes/rl-0.15.xml4
-rw-r--r--doc/manual/release-notes/rl-0.16.xml4
-rw-r--r--doc/manual/release-notes/rl-0.6.xml4
-rw-r--r--doc/manual/release-notes/rl-0.7.xml4
-rw-r--r--doc/manual/release-notes/rl-0.8.1.xml4
-rw-r--r--doc/manual/release-notes/rl-0.8.xml4
-rw-r--r--doc/manual/release-notes/rl-0.9.1.xml4
-rw-r--r--doc/manual/release-notes/rl-0.9.2.xml4
-rw-r--r--doc/manual/release-notes/rl-0.9.xml4
-rw-r--r--doc/manual/release-notes/rl-1.0.xml4
-rw-r--r--doc/manual/release-notes/rl-1.1.xml4
-rw-r--r--doc/manual/release-notes/rl-1.10.xml2
-rw-r--r--doc/manual/release-notes/rl-1.11.xml126
-rw-r--r--doc/manual/release-notes/rl-1.12.xml24
-rw-r--r--doc/manual/release-notes/rl-1.2.xml4
-rw-r--r--doc/manual/release-notes/rl-1.3.xml4
-rw-r--r--doc/manual/release-notes/rl-1.4.xml4
-rw-r--r--doc/manual/release-notes/rl-1.5.1.xml4
-rw-r--r--doc/manual/release-notes/rl-1.5.2.xml4
-rw-r--r--doc/manual/release-notes/rl-1.5.xml4
-rw-r--r--doc/manual/release-notes/rl-1.6.1.xml4
-rw-r--r--doc/manual/release-notes/rl-1.6.xml4
-rw-r--r--doc/manual/release-notes/rl-1.7.xml4
-rw-r--r--doc/manual/release-notes/rl-1.8.xml2
-rw-r--r--doc/manual/release-notes/rl-1.9.xml2
-rw-r--r--perl/lib/Nix/Store.xs103
-rw-r--r--release.nix29
-rw-r--r--scripts/download-from-binary-cache.pl.in2
-rwxr-xr-xscripts/download-using-manifests.pl.in3
-rw-r--r--scripts/install-nix-from-closure.sh2
-rwxr-xr-xscripts/nix-build.in4
-rw-r--r--src/libexpr/common-opts.cc2
-rw-r--r--src/libexpr/common-opts.hh2
-rw-r--r--src/libexpr/eval.cc13
-rw-r--r--src/libexpr/eval.hh12
-rw-r--r--src/libexpr/lexer.l24
-rw-r--r--src/libexpr/parser.y2
-rw-r--r--src/libexpr/primops.cc63
-rw-r--r--src/libmain/shared.cc11
-rw-r--r--src/libmain/shared.hh8
-rw-r--r--src/libstore/build.cc235
-rw-r--r--src/libstore/derivations.cc83
-rw-r--r--src/libstore/derivations.hh40
-rw-r--r--src/libstore/download.cc2
-rw-r--r--src/libstore/download.hh5
-rw-r--r--src/libstore/gc.cc49
-rw-r--r--src/libstore/local-store.cc16
-rw-r--r--src/libstore/local-store.hh12
-rw-r--r--src/libstore/misc.cc108
-rw-r--r--src/libstore/misc.hh40
-rw-r--r--src/libstore/pathlocks.cc6
-rw-r--r--src/libstore/profiles.cc4
-rw-r--r--src/libstore/profiles.hh4
-rw-r--r--src/libstore/remote-store.cc10
-rw-r--r--src/libstore/remote-store.hh2
-rw-r--r--src/libstore/store-api.cc29
-rw-r--r--src/libstore/store-api.hh67
-rw-r--r--src/libutil/compression.cc1
-rw-r--r--src/libutil/hash.cc12
-rw-r--r--src/libutil/hash.hh17
-rw-r--r--src/libutil/types.hh60
-rw-r--r--src/libutil/util.cc11
-rw-r--r--src/libutil/util.hh20
-rw-r--r--src/nix-collect-garbage/nix-collect-garbage.cc2
-rw-r--r--src/nix-daemon/nix-daemon.cc89
-rw-r--r--src/nix-env/nix-env.cc42
-rw-r--r--src/nix-env/user-env.cc12
-rw-r--r--src/nix-instantiate/nix-instantiate.cc7
-rw-r--r--src/nix-prefetch-url/nix-prefetch-url.cc4
-rw-r--r--src/nix-store/dotgraph.cc128
-rw-r--r--src/nix-store/dotgraph.hh4
-rw-r--r--src/nix-store/nix-store.cc68
-rw-r--r--src/nix-store/xmlgraph.cc34
-rw-r--r--src/nix-store/xmlgraph.hh4
-rw-r--r--tests/dump-db.sh1
-rw-r--r--tests/lang/parse-okay-regression-751.nix2
-rw-r--r--tests/repair.sh12
-rw-r--r--version2
94 files changed, 1170 insertions, 709 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
new file mode 100644
index 000000000000..2d1117f4bda6
--- /dev/null
+++ b/.dir-locals.el
@@ -0,0 +1,16 @@
+((c++-mode . (
+  (c-file-style . "k&r")
+  (c-basic-offset . 4)
+  (indent-tabs-mode . nil)
+  (tab-width . 4)
+  (show-trailing-whitespace . t)
+  (indicate-empty-lines . t)
+  (eval . (c-set-offset 'innamespace 0))
+  (eval . (c-set-offset 'defun-open 0))
+  (eval . (c-set-offset 'inline-open 0))
+  (eval . (c-set-offset 'arglist-intro '+))
+  (eval . (c-set-offset 'arglist-cont 0))
+  (eval . (c-set-offset 'arglist-cont-nonempty '+))
+  (eval . (c-set-offset 'substatement-open 0))
+  (eval . (c-set-offset 'access-label '-))
+  )))
diff --git a/configure.ac b/configure.ac
index df92950ff0cc..3a24053bb6a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -48,6 +48,7 @@ test "$localstatedir" = '${prefix}/var' && localstatedir=/nix/var
 
 
 # Solaris-specific stuff.
+AC_STRUCT_DIRENT_D_TYPE
 if test "$sys_name" = sunos; then
     # Solaris requires -lsocket -lnsl for network functions
     LIBS="-lsocket -lnsl $LIBS"
diff --git a/corepkgs/buildenv.nix b/corepkgs/buildenv.nix
index ab1ce13f2cf6..70981a752c3c 100644
--- a/corepkgs/buildenv.nix
+++ b/corepkgs/buildenv.nix
@@ -8,7 +8,7 @@ derivation {
   builder = perl;
   args = [ "-w" ./buildenv.pl ];
 
-  manifest = manifest;
+  inherit manifest;
 
   # !!! grmbl, need structured data for passing this in a clean way.
   derivations =
@@ -23,6 +23,9 @@ derivation {
   # network traffic, so don't do that.
   preferLocalBuild = true;
 
+  # Also don't bother substituting.
+  allowSubstitutes = false;
+
   __sandboxProfile = ''
     (allow sysctl-read)
     (allow file-read*
diff --git a/corepkgs/buildenv.pl b/corepkgs/buildenv.pl
index 264442104320..dacc53701a01 100644
--- a/corepkgs/buildenv.pl
+++ b/corepkgs/buildenv.pl
@@ -149,7 +149,7 @@ foreach my $pkg (@pkgs) {
 
 
 # Symlink to the packages that have been "propagated" by packages
-# installed by the user (i.e., package X declares that it want Y
+# installed by the user (i.e., package X declares that it wants Y
 # installed as well).  We do these later because they have a lower
 # priority in case of collisions.
 my $priorityCounter = 1000; # don't care about collisions
diff --git a/doc/manual/command-ref/nix-collect-garbage.xml b/doc/manual/command-ref/nix-collect-garbage.xml
index c88851299152..35a78c5b2015 100644
--- a/doc/manual/command-ref/nix-collect-garbage.xml
+++ b/doc/manual/command-ref/nix-collect-garbage.xml
@@ -28,6 +28,7 @@
       <arg choice='plain'><option>--print-dead</option></arg>
       <arg choice='plain'><option>--delete</option></arg>
     </group>
+    <arg><option>--max-freed</option> <replaceable>bytes</replaceable></arg>
     <arg><option>--dry-run</option></arg>
   </cmdsynopsis>
 </refsynopsisdiv>
diff --git a/doc/manual/command-ref/nix-shell.xml b/doc/manual/command-ref/nix-shell.xml
index 9e3e6d1882ba..6f00e28cacaa 100644
--- a/doc/manual/command-ref/nix-shell.xml
+++ b/doc/manual/command-ref/nix-shell.xml
@@ -176,6 +176,22 @@ also <xref linkend="sec-common-options" />.</phrase></para>
 </refsection>
 
 
+<refsection><title>Environment variables</title>
+
+<variablelist>
+
+  <varlistentry><term><envar>NIX_BUILD_SHELL</envar></term>
+    
+    <listitem><para>Shell used to start the interactive environment. 
+    Defaults to the <command>bash</command> found in <envar>PATH</envar>.</para></listitem>
+
+  </varlistentry>
+
+</variablelist>
+
+</refsection>
+
+
 <refsection><title>Examples</title>
 
 <para>To build the dependencies of the package Pan, and start an
@@ -252,8 +268,8 @@ dependencies in Nixpkgs.</para>
 <para>The lines starting with <literal>#! nix-shell</literal> specify
 <command>nix-shell</command> options (see above). Note that you cannot
 write <literal>#1 /usr/bin/env nix-shell -i ...</literal> because
-<command>/usr/bin/env</command> does not support passing options to
-the interpreter.</para>
+many operating systems only allow one argument in
+<literal>#!</literal> lines.</para>
 
 <para>For example, here is a Python script that depends on Python and
 the <literal>prettytable</literal> package:
diff --git a/doc/manual/command-ref/nix-store.xml b/doc/manual/command-ref/nix-store.xml
index e21d53d8b9f0..bf03c802b87d 100644
--- a/doc/manual/command-ref/nix-store.xml
+++ b/doc/manual/command-ref/nix-store.xml
@@ -194,6 +194,25 @@ printed.)</para>
 
   </varlistentry>
 
+  <varlistentry><term><option>--check</option></term>
+
+    <listitem><para>This option allows you to check whether a
+    derivation is deterministic. It rebuilds the specified derivation
+    and checks whether the result is bitwise-identical with the
+    existing outputs, printing an error if that’s not the case. The
+    outputs of the specified derivation must already exist. When used
+    with <option>-K</option>, if an output path is not identical to
+    the corresponding output from the previous build, the new output
+    path is left in
+    <filename>/nix/store/<replaceable>name</replaceable>-check.</filename></para>
+
+    <para>See also the <option>build-repeat</option> configuration
+    option, which repeats a derivation a number of times and prevents
+    its outputs from being registered as “valid” in the Nix store
+    unless they are identical.</para></listitem>
+
+  </varlistentry>
+
 </variablelist>
 
 </refsection>
@@ -212,6 +231,14 @@ $ nix-store -r $(nix-instantiate ./test.nix)
 This is essentially what <link
 linkend="sec-nix-build"><command>nix-build</command></link> does.</para>
 
+<para>To test whether a previously-built derivation is deterministic:
+
+<screen>
+$ nix-build -r '&lt;nixpkgs>' -A hello --check -K
+</screen>
+
+</para>
+
 </refsection>
 
 
diff --git a/doc/manual/expressions/builtins.xml b/doc/manual/expressions/builtins.xml
index 13cc2221e1b5..eae5f5a029bf 100644
--- a/doc/manual/expressions/builtins.xml
+++ b/doc/manual/expressions/builtins.xml
@@ -348,6 +348,24 @@ stdenv.mkDerivation {
   </varlistentry>
 
 
+  <varlistentry><term><function>builtins.functionArgs</function>
+    <replaceable>f</replaceable></term>
+
+    <listitem><para>
+    Return a set containing the names of the formal arguments expected
+    by the function <replaceable>f</replaceable>.
+    The value of each attribute is a Boolean denoting whether the corresponding
+    argument has a default value.  For instance,
+    <literal>functionArgs ({ x, y ? 123}: ...)  =  { x = false; y = true; }</literal>.
+    </para>
+
+    <para>"Formal argument" here refers to the attributes pattern-matched by
+    the function.  Plain lambdas are not included, e.g.
+    <literal>functionArgs (x: ...)  =  { }</literal>.
+    </para></listitem>
+  </varlistentry>
+
+
   <varlistentry><term><function>builtins.fromJSON</function> <replaceable>e</replaceable></term>
 
     <listitem><para>Convert a JSON string to a Nix
diff --git a/doc/manual/installation/prerequisites-source.xml b/doc/manual/installation/prerequisites-source.xml
index 01d52c74030a..49036d940bd4 100644
--- a/doc/manual/installation/prerequisites-source.xml
+++ b/doc/manual/installation/prerequisites-source.xml
@@ -36,7 +36,7 @@
   distribution does not provide them.</para></listitem>
 
   <listitem><para>The <link
-  xlink:href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/">Boehm
+  xlink:href="http://www.hboehm.info/gc/">Boehm
   garbage collector</link> to reduce the evaluator’s memory
   consumption (optional).  To enable it, install
   <literal>pkgconfig</literal> and the Boehm garbage collector, and
diff --git a/doc/manual/release-notes/release-notes.xml b/doc/manual/release-notes/release-notes.xml
index 0aa3e8717aa1..8c2deb394183 100644
--- a/doc/manual/release-notes/release-notes.xml
+++ b/doc/manual/release-notes/release-notes.xml
@@ -12,6 +12,7 @@
 </partintro>
 -->
 
+<xi:include href="rl-1.12.xml" />
 <xi:include href="rl-1.11.xml" />
 <xi:include href="rl-1.10.xml" />
 <xi:include href="rl-1.9.xml" />
diff --git a/doc/manual/release-notes/rl-0.10.1.xml b/doc/manual/release-notes/rl-0.10.1.xml
index 05cd2f654353..95829323d4fb 100644
--- a/doc/manual/release-notes/rl-0.10.1.xml
+++ b/doc/manual/release-notes/rl-0.10.1.xml
@@ -4,10 +4,10 @@
       version="5.0"
       xml:id="ch-relnotes-0.10.1">
 
-<title>Release 0.10.1 (October 11, 2006)</title>
+<title>Release 0.10.1 (2006-10-11)</title>
 
 <para>This release fixes two somewhat obscure bugs that occur when
 evaluating Nix expressions that are stored inside the Nix store
 (<literal>NIX-67</literal>).  These do not affect most users.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.10.xml b/doc/manual/release-notes/rl-0.10.xml
index 7815ae75b1b8..9afec4de94de 100644
--- a/doc/manual/release-notes/rl-0.10.xml
+++ b/doc/manual/release-notes/rl-0.10.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.10">
 
-<title>Release 0.10 (October 6, 2006)</title>
+<title>Release 0.10 (2006-10-06)</title>
 
 <note><para>This version of Nix uses Berkeley DB 4.4 instead of 4.3.
 The database is upgraded automatically, but you should be careful not
@@ -320,4 +320,4 @@ irreversible.</para></warning>
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.11.xml b/doc/manual/release-notes/rl-0.11.xml
index 9c5d8b8beb4b..7ad0ab5b71ad 100644
--- a/doc/manual/release-notes/rl-0.11.xml
+++ b/doc/manual/release-notes/rl-0.11.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-0.11">
 
-<title>Release 0.11 (December 31, 2007)</title>
+<title>Release 0.11 (2007-12-31)</title>
 
 <para>Nix 0.11 has many improvements over the previous stable release.
 The most important improvement is secure multi-user support.  It also
@@ -258,4 +258,4 @@ on Nix.  Here is an (incomplete) list:</para>
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.12.xml b/doc/manual/release-notes/rl-0.12.xml
index 1f04609b1654..fdba8c4d577f 100644
--- a/doc/manual/release-notes/rl-0.12.xml
+++ b/doc/manual/release-notes/rl-0.12.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-0.12">
 
-<title>Release 0.12 (November 20, 2008)</title>
+<title>Release 0.12 (2008-11-20)</title>
 
 <itemizedlist>
 
@@ -172,4 +172,4 @@ the following paths will be downloaded/copied (30.02 MiB):
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.13.xml b/doc/manual/release-notes/rl-0.13.xml
index 9cf144e4ea23..cce2e4a26b05 100644
--- a/doc/manual/release-notes/rl-0.13.xml
+++ b/doc/manual/release-notes/rl-0.13.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-0.13">
 
-<title>Release 0.13 (November 5, 2009)</title>
+<title>Release 0.13 (2009-11-05)</title>
 
 <para>This is primarily a bug fix release.  It has some new
 features:</para>
@@ -103,4 +103,4 @@ features:</para>
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.14.xml b/doc/manual/release-notes/rl-0.14.xml
index a671db3b8588..e5fe9da78e7e 100644
--- a/doc/manual/release-notes/rl-0.14.xml
+++ b/doc/manual/release-notes/rl-0.14.xml
@@ -2,7 +2,9 @@
       xmlns:xlink="http://www.w3.org/1999/xlink"
       xmlns:xi="http://www.w3.org/2001/XInclude"
       version="5.0"
-      xml:id="ssec-relnotes-0.14"><title>Release 0.14 (February 4, 2010)</title>
+      xml:id="ssec-relnotes-0.14">
+
+<title>Release 0.14 (2010-02-04)</title>
 
 <para>This release has the following improvements:</para>
 
@@ -41,4 +43,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.15.xml b/doc/manual/release-notes/rl-0.15.xml
index b76f674ba123..9f58a8efc5d4 100644
--- a/doc/manual/release-notes/rl-0.15.xml
+++ b/doc/manual/release-notes/rl-0.15.xml
@@ -4,11 +4,11 @@
       version="5.0"
       xml:id="ssec-relnotes-0.15">
 
-<title>Release 0.15 (March 17, 2010)</title>
+<title>Release 0.15 (2010-03-17)</title>
 
 <para>This is a bug-fix release.  Among other things, it fixes
 building on Mac OS X (Snow Leopard), and improves the contents of
 <filename>/etc/passwd</filename> and <filename>/etc/group</filename>
 in <literal>chroot</literal> builds.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.16.xml b/doc/manual/release-notes/rl-0.16.xml
index 5254e5f9d658..af1edc0ebbe0 100644
--- a/doc/manual/release-notes/rl-0.16.xml
+++ b/doc/manual/release-notes/rl-0.16.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-0.16">
 
-<title>Release 0.16 (August 17, 2010)</title>
+<title>Release 0.16 (2010-08-17)</title>
 
 <para>This release has the following improvements:</para>
 
@@ -52,4 +52,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.6.xml b/doc/manual/release-notes/rl-0.6.xml
index 83d9dc897c42..6dc6521d3c2a 100644
--- a/doc/manual/release-notes/rl-0.6.xml
+++ b/doc/manual/release-notes/rl-0.6.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.6">
 
-<title>Release 0.6 (November 14, 2004)</title>
+<title>Release 0.6 (2004-11-14)</title>
 
 <itemizedlist>
 
@@ -119,4 +119,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.7.xml b/doc/manual/release-notes/rl-0.7.xml
index fc8997fc1e36..6f95db4367db 100644
--- a/doc/manual/release-notes/rl-0.7.xml
+++ b/doc/manual/release-notes/rl-0.7.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.7">
 
-<title>Release 0.7 (January 12, 2005)</title>
+<title>Release 0.7 (2005-01-12)</title>
 
 <itemizedlist>
 
@@ -32,4 +32,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.8.1.xml b/doc/manual/release-notes/rl-0.8.1.xml
index b4a855b553a5..f7ffca0f8d59 100644
--- a/doc/manual/release-notes/rl-0.8.1.xml
+++ b/doc/manual/release-notes/rl-0.8.1.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.8.1">
 
-<title>Release 0.8.1 (April 13, 2005)</title>
+<title>Release 0.8.1 (2005-04-13)</title>
 
 <para>This is a bug fix release.</para>
 
@@ -18,4 +18,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.8.xml b/doc/manual/release-notes/rl-0.8.xml
index 970abb6e85e0..784b26c6b7d3 100644
--- a/doc/manual/release-notes/rl-0.8.xml
+++ b/doc/manual/release-notes/rl-0.8.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.8">
 
-<title>Release 0.8 (April 11, 2005)</title>
+<title>Release 0.8 (2005-04-11)</title>
 
 <para>NOTE: the hashing scheme in Nix 0.8 changed (as detailed below).
 As a result, <command>nix-pull</command> manifests and channels built
@@ -243,4 +243,4 @@ $ nix-env -f .../i686-linux.nix -i -E 'x: x.firefoxWrapper'</screen>
 
 </para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.9.1.xml b/doc/manual/release-notes/rl-0.9.1.xml
index a7d093aa8e02..85d11f416877 100644
--- a/doc/manual/release-notes/rl-0.9.1.xml
+++ b/doc/manual/release-notes/rl-0.9.1.xml
@@ -4,10 +4,10 @@
       version="5.0"
       xml:id="ch-relnotes-0.9.1">
 
-<title>Release 0.9.1 (September 20, 2005)</title>
+<title>Release 0.9.1 (2005-09-20)</title>
 
 <para>This bug fix release addresses a problem with the ATerm library
 when the <option>--with-aterm</option> flag in
 <command>configure</command> was <emphasis>not</emphasis> used.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.9.2.xml b/doc/manual/release-notes/rl-0.9.2.xml
index 33141c8e94ff..cb705e98ac25 100644
--- a/doc/manual/release-notes/rl-0.9.2.xml
+++ b/doc/manual/release-notes/rl-0.9.2.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.9.2">
 
-<title>Release 0.9.2 (September 21, 2005)</title>
+<title>Release 0.9.2 (2005-09-21)</title>
 
 <para>This bug fix release fixes two problems on Mac OS X:
 
@@ -25,4 +25,4 @@
 
 </para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-0.9.xml b/doc/manual/release-notes/rl-0.9.xml
index 07dd87cd9f61..fd1e633f78ea 100644
--- a/doc/manual/release-notes/rl-0.9.xml
+++ b/doc/manual/release-notes/rl-0.9.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ch-relnotes-0.9">
 
-<title>Release 0.9 (September 16, 2005)</title>
+<title>Release 0.9 (2005-09-16)</title>
 
 <para>NOTE: this version of Nix uses Berkeley DB 4.3 instead of 4.2.
 The database is upgraded automatically, but you should be careful not
@@ -95,4 +95,4 @@ svnService = derivation {
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.0.xml b/doc/manual/release-notes/rl-1.0.xml
index d000014eb311..ff11168d0932 100644
--- a/doc/manual/release-notes/rl-1.0.xml
+++ b/doc/manual/release-notes/rl-1.0.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.0">
 
-<title>Release 1.0 (May 11, 2012)</title>
+<title>Release 1.0 (2012-05-11)</title>
 
 <para>There have been numerous improvements and bug fixes since the
 previous release.  Here are the most significant:</para>
@@ -116,4 +116,4 @@ previous release.  Here are the most significant:</para>
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.1.xml b/doc/manual/release-notes/rl-1.1.xml
index 7ee076f193e9..2f26e7a24273 100644
--- a/doc/manual/release-notes/rl-1.1.xml
+++ b/doc/manual/release-notes/rl-1.1.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.1">
 
-<title>Release 1.1 (July 18, 2012)</title>
+<title>Release 1.1 (2012-07-18)</title>
 
 <para>This release has the following improvements:</para>
 
@@ -97,4 +97,4 @@
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.10.xml b/doc/manual/release-notes/rl-1.10.xml
index 7a90087487e9..689a95466343 100644
--- a/doc/manual/release-notes/rl-1.10.xml
+++ b/doc/manual/release-notes/rl-1.10.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.10">
 
-<title>Release 1.10 (September 3, 2015)</title>
+<title>Release 1.10 (2015-09-03)</title>
 
 <para>This is primarily a bug fix release. It also has a number of new
 features:</para>
diff --git a/doc/manual/release-notes/rl-1.11.xml b/doc/manual/release-notes/rl-1.11.xml
index fb6e4abfef52..efb03d61393f 100644
--- a/doc/manual/release-notes/rl-1.11.xml
+++ b/doc/manual/release-notes/rl-1.11.xml
@@ -4,12 +4,123 @@
       version="5.0"
       xml:id="ssec-relnotes-1.11">
 
-<title>Release 1.11 (TODO: date, 2015)</title>
+<title>Release 1.11 (2016-01-19)</title>
 
-<para>TODO: This is primarily a bug fix release. It also has a number of new
+<para>This is primarily a bug fix release. It also has a number of new
 features:</para>
 
 <itemizedlist>
+
+  <listitem>
+    <para><command>nix-prefetch-url</command> can now download URLs
+    specified in a Nix expression. For example,
+
+<screen>
+$ nix-prefetch-url -A hello.src
+</screen>
+
+    will prefetch the file specified by the
+    <function>fetchurl</function> call in the attribute
+    <literal>hello.src</literal> from the Nix expression in the
+    current directory, and print the cryptographic hash of the
+    resulting file on stdout. This differs from <literal>nix-build -A
+    hello.src</literal> in that it doesn't verify the hash, and is
+    thus useful when you’re updating a Nix expression.</para>
+
+    <para>You can also prefetch the result of functions that unpack a
+    tarball, such as <function>fetchFromGitHub</function>. For example:
+
+<screen>
+$ nix-prefetch-url --unpack https://github.com/NixOS/patchelf/archive/0.8.tar.gz
+</screen>
+
+    or from a Nix expression:
+
+<screen>
+$ nix-prefetch-url -A nix-repl.src
+</screen>
+
+    </para>
+
+  </listitem>
+
+  <listitem>
+    <para>The builtin function
+    <function>&lt;nix/fetchurl.nix></function> now supports
+    downloading and unpacking NARs. This removes the need to have
+    multiple downloads in the Nixpkgs stdenv bootstrap process (like a
+    separate busybox binary for Linux, or curl/mkdir/sh/bzip2 for
+    Darwin). Now all those files can be combined into a single NAR,
+    optionally compressed using <command>xz</command>.</para>
+  </listitem>
+
+  <listitem>
+    <para>Nix now supports SHA-512 hashes for verifying fixed-output
+    derivations, and in <function>builtins.hashString</function>.</para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The new flag <option>--option build-repeat
+      <replaceable>N</replaceable></option> will cause every build to
+      be executed <replaceable>N</replaceable>+1 times. If the build
+      output differs between any round, the build is rejected, and the
+      output paths are not registered as valid. This is primarily
+      useful to verify build determinism. (We already had a
+      <option>--check</option> option to repeat a previously succeeded
+      build. However, with <option>--check</option>, non-deterministic
+      builds are registered in the DB. Preventing that is useful for
+      Hydra to ensure that non-deterministic builds don't end up
+      getting published to the binary cache.)
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The options <option>--check</option> and <option>--option
+      build-repeat <replaceable>N</replaceable></option>, if they
+      detect a difference between two runs of the same derivation and
+      <option>-K</option> is given, will make the output of the other
+      run available under
+      <filename><replaceable>store-path</replaceable>-check</filename>. This
+      makes it easier to investigate the non-determinism using tools
+      like <command>diffoscope</command>, e.g.,
+
+<screen>
+$ nix-build pkgs/stdenv/linux -A stage1.pkgs.zlib --check -K
+error: derivation ‘/nix/store/l54i8wlw2265…-zlib-1.2.8.drv’ may not
+be deterministic: output ‘/nix/store/11a27shh6n2i…-zlib-1.2.8’
+differs from ‘/nix/store/11a27shh6n2i…-zlib-1.2.8-check’
+
+$ diffoscope /nix/store/11a27shh6n2i…-zlib-1.2.8 /nix/store/11a27shh6n2i…-zlib-1.2.8-check
+…
+├── lib/libz.a
+│   ├── metadata
+│   │ @@ -1,15 +1,15 @@
+│   │ -rw-r--r-- 30001/30000   3096 Jan 12 15:20 2016 adler32.o
+…
+│   │ +rw-r--r-- 30001/30000   3096 Jan 12 15:28 2016 adler32.o
+…
+</screen>
+
+    </para></listitem>
+
+  <listitem>
+    <para>Improved FreeBSD support.</para>
+  </listitem>
+
+  <listitem>
+    <para><command>nix-env -qa --xml --meta</command> now prints
+    license information.</para>
+  </listitem>
+
+  <listitem>
+    <para>The maximum number of parallel TCP connections that the
+    binary cache substituter will use has been decreased from 150 to
+    25. This should prevent upsetting some broken NAT routers, and
+    also improves performance.</para>
+  </listitem>
+
   <listitem>
     <para>The Nix language now supports floating point numbers. They are
     based on regular C++ <literal>float</literal> and compatible with
@@ -19,12 +130,19 @@ features:</para>
   </listitem>
   <listitem>
     <para>All "chroot"-containing strings got renamed to "sandbox".
-      In particular, some nix options got renamed, but the old names
+      In particular, some Nix options got renamed, but the old names
       are still accepted as lower-priority aliases.
     </para>
   </listitem>
+
 </itemizedlist>
 
-<para>This release has contributions from TODO.</para>
+<para>This release has contributions from Anders Claesson, Anthony
+Cowley, Bjørn Forsman, Brian McKenna, Danny Wilson, davidak, Eelco Dolstra,
+Fabian Schmitthenner, FrankHB, Ilya Novoselov, janus, Jim Garrison, John
+Ericson, Jude Taylor, Ludovic Courtès, Manuel Jacob, Mathnerd314,
+Pascal Wittmann, Peter Simons, Philip Potter, Preston Bennes, Rommel
+M. Martinez, Sander van der Burg, Shea Levy, Tim Cuthbertson, Tuomas
+Tynkkynen, Utku Demir and Vladimír Čunát.</para>
 
 </section>
diff --git a/doc/manual/release-notes/rl-1.12.xml b/doc/manual/release-notes/rl-1.12.xml
new file mode 100644
index 000000000000..d6864b3f55d1
--- /dev/null
+++ b/doc/manual/release-notes/rl-1.12.xml
@@ -0,0 +1,24 @@
+<section xmlns="http://docbook.org/ns/docbook"
+      xmlns:xlink="http://www.w3.org/1999/xlink"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      version="5.0"
+      xml:id="ssec-relnotes-1.12">
+
+<title>Release 1.12 (TBA)</title>
+
+<para>This release has the following new features:</para>
+
+<itemizedlist>
+
+  <listitem>
+    <para>It is no longer necessary to set the
+    <envar>NIX_REMOTE</envar> environment variable if you need to use
+    the Nix daemon. Nix will use the daemon automatically if you don’t
+    have write access to the Nix database.</para>
+  </listitem>
+
+</itemizedlist>
+
+<para>This release has contributions from TBD.</para>
+
+</section>
diff --git a/doc/manual/release-notes/rl-1.2.xml b/doc/manual/release-notes/rl-1.2.xml
index 6c05444ff76e..dc272c420ddb 100644
--- a/doc/manual/release-notes/rl-1.2.xml
+++ b/doc/manual/release-notes/rl-1.2.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.2">
 
-<title>Release 1.2 (December 6, 2012)</title>
+<title>Release 1.2 (2012-12-06)</title>
 
 <para>This release has the following improvements and changes:</para>
 
@@ -154,4 +154,4 @@ $ mount -o remount,ro,bind /nix/store
 <para>This release has contributions from Eelco Dolstra, Florian
 Friesdorf, Mats Erik Andersson and Shea Levy.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.3.xml b/doc/manual/release-notes/rl-1.3.xml
index 04169ed01b82..e2009ee3ba4b 100644
--- a/doc/manual/release-notes/rl-1.3.xml
+++ b/doc/manual/release-notes/rl-1.3.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.3">
 
-<title>Release 1.3 (January 4, 2013)</title>
+<title>Release 1.3 (2013-01-04)</title>
 
 <para>This is primarily a bug fix release.  When this version is first
 run on Linux, it removes any immutable bits from the Nix store and
@@ -16,4 +16,4 @@ efficient.)</para>
 <para>This release has contributions from Eelco Dolstra and Stuart
 Pernsteiner.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.4.xml b/doc/manual/release-notes/rl-1.4.xml
index d48e43f21c1b..aefb22f2b934 100644
--- a/doc/manual/release-notes/rl-1.4.xml
+++ b/doc/manual/release-notes/rl-1.4.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.4">
 
-<title>Release 1.4 (February 26, 2013)</title>
+<title>Release 1.4 (2013-02-26)</title>
 
 <para>This release fixes a security bug in multi-user operation.  It
 was possible for derivations to cause the mode of files outside of the
@@ -36,4 +36,4 @@ xlink:href="https://github.com/NixOS/nix/commit/5526a282b5b44e9296e61e07d7d2626a
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.5.1.xml b/doc/manual/release-notes/rl-1.5.1.xml
index 046960313ffd..035c8dbcbb16 100644
--- a/doc/manual/release-notes/rl-1.5.1.xml
+++ b/doc/manual/release-notes/rl-1.5.1.xml
@@ -4,9 +4,9 @@
       version="5.0"
       xml:id="ssec-relnotes-1.5.1">
 
-<title>Release 1.5.1 (February 28, 2013)</title>
+<title>Release 1.5.1 (2013-02-28)</title>
 
 <para>The bug fix to the bug fix had a bug itself, of course.  But
 this time it will work for sure!</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.5.2.xml b/doc/manual/release-notes/rl-1.5.2.xml
index d2f53bbdc0bd..7e81dd243284 100644
--- a/doc/manual/release-notes/rl-1.5.2.xml
+++ b/doc/manual/release-notes/rl-1.5.2.xml
@@ -4,9 +4,9 @@
       version="5.0"
       xml:id="ssec-relnotes-1.5.2">
 
-<title>Release 1.5.2 (May 13, 2013)</title>
+<title>Release 1.5.2 (2013-05-13)</title>
 
 <para>This is primarily a bug fix release.  It has contributions from
 Eelco Dolstra, Lluís Batlle i Rossell and Shea Levy.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.5.xml b/doc/manual/release-notes/rl-1.5.xml
index 84e0e9024946..8e279d7693e0 100644
--- a/doc/manual/release-notes/rl-1.5.xml
+++ b/doc/manual/release-notes/rl-1.5.xml
@@ -4,9 +4,9 @@
       version="5.0"
       xml:id="ssec-relnotes-1.5">
 
-<title>Release 1.5 (February 27, 2013)</title>
+<title>Release 1.5 (2013-02-27)</title>
 
 <para>This is a brown paper bag release to fix a regression introduced
 by the hard link security fix in 1.4.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.6.1.xml b/doc/manual/release-notes/rl-1.6.1.xml
index 1ca4dfebacdc..9ecc52734737 100644
--- a/doc/manual/release-notes/rl-1.6.1.xml
+++ b/doc/manual/release-notes/rl-1.6.1.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.6.1">
 
-<title>Release 1.6.1 (October 28, 2013)</title>
+<title>Release 1.6.1 (2013-10-28)</title>
 
 <para>This is primarily a bug fix release.  Changes of interest
 are:</para>
@@ -66,4 +66,4 @@ are:</para>
 
 </itemizedlist>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.6.xml b/doc/manual/release-notes/rl-1.6.xml
index a3d61faf2b73..580563420949 100644
--- a/doc/manual/release-notes/rl-1.6.xml
+++ b/doc/manual/release-notes/rl-1.6.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.6.0">
 
-<title>Release 1.6 (September 10, 2013)</title>
+<title>Release 1.6 (2013-09-10)</title>
 
 <para>In addition to the usual bug fixes, this release has several new
 features:</para>
@@ -124,4 +124,4 @@ in pkgs.bar
 Florian Friesdorf, Gergely Risko, Ivan Kozik, Ludovic Courtès and Shea
 Levy.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.7.xml b/doc/manual/release-notes/rl-1.7.xml
index a9863b99cba0..44ecaa78da5f 100644
--- a/doc/manual/release-notes/rl-1.7.xml
+++ b/doc/manual/release-notes/rl-1.7.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.7">
 
-<title>Release 1.7 (April 11, 2014)</title>
+<title>Release 1.7 (2014-04-11)</title>
 
 <para>In addition to the usual bug fixes, this release has the
 following new features:</para>
@@ -260,4 +260,4 @@ error: attribute `nixUnstabl' missing, at /etc/nixos/configurations/misc/eelco/m
 Eelco Dolstra, Ian-Woo Kim, Ludovic Courtès, Maxim Ivanov, Petr
 Rockai, Ricardo M. Correia and Shea Levy.</para>
 
-</section>
\ No newline at end of file
+</section>
diff --git a/doc/manual/release-notes/rl-1.8.xml b/doc/manual/release-notes/rl-1.8.xml
index b7acc1773baa..48caac2c6b60 100644
--- a/doc/manual/release-notes/rl-1.8.xml
+++ b/doc/manual/release-notes/rl-1.8.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.8">
 
-<title>Release 1.8 (December 14, 2014)</title>
+<title>Release 1.8 (2014-12-14)</title>
 
 <itemizedlist>
 
diff --git a/doc/manual/release-notes/rl-1.9.xml b/doc/manual/release-notes/rl-1.9.xml
index e0c79e751ed7..c8406bd2077c 100644
--- a/doc/manual/release-notes/rl-1.9.xml
+++ b/doc/manual/release-notes/rl-1.9.xml
@@ -4,7 +4,7 @@
       version="5.0"
       xml:id="ssec-relnotes-1.9">
 
-<title>Release 1.9 (June 12, 2015)</title>
+<title>Release 1.9 (2015-06-12)</title>
 
 <para>In addition to the usual bug fixes, this release has the
 following new features:</para>
diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs
index 07d81aa3ab05..5a1e8424f036 100644
--- a/perl/lib/Nix/Store.xs
+++ b/perl/lib/Nix/Store.xs
@@ -6,10 +6,10 @@
 #undef do_open
 #undef do_close
 
-#include <store-api.hh>
-#include <globals.hh>
-#include <misc.hh>
-#include <util.hh>
+#include "derivations.hh"
+#include "globals.hh"
+#include "store-api.hh"
+#include "util.hh"
 
 #if HAVE_SODIUM
 #include <sodium.h>
@@ -19,19 +19,21 @@
 using namespace nix;
 
 
-void doInit()
+static ref<Store> store()
 {
-    if (!store) {
+    static std::shared_ptr<Store> _store;
+    if (!_store) {
         try {
             settings.processEnvironment();
             settings.loadConfFile();
             settings.update();
             settings.lockCPU = false;
-            store = openStore();
+            _store = openStore();
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
     }
+    return ref<Store>(_store);
 }
 
 
@@ -45,7 +47,7 @@ PROTOTYPES: ENABLE
 
 void init()
     CODE:
-        doInit();
+        store();
 
 
 void setVerbosity(int level)
@@ -56,10 +58,9 @@ void setVerbosity(int level)
 int isValidPath(char * path)
     CODE:
         try {
-            doInit();
-            RETVAL = store->isValidPath(path);
+            RETVAL = store()->isValidPath(path);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
     OUTPUT:
         RETVAL
@@ -68,45 +69,41 @@ int isValidPath(char * path)
 SV * queryReferences(char * path)
     PPCODE:
         try {
-            doInit();
             PathSet paths;
-            store->queryReferences(path, paths);
+            store()->queryReferences(path, paths);
             for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
                 XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * queryPathHash(char * path)
     PPCODE:
         try {
-            doInit();
-            Hash hash = store->queryPathHash(path);
+            Hash hash = store()->queryPathHash(path);
             string s = "sha256:" + printHash32(hash);
             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * queryDeriver(char * path)
     PPCODE:
         try {
-            doInit();
-            Path deriver = store->queryDeriver(path);
+            Path deriver = store()->queryDeriver(path);
             if (deriver == "") XSRETURN_UNDEF;
             XPUSHs(sv_2mortal(newSVpv(deriver.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * queryPathInfo(char * path, int base32)
     PPCODE:
         try {
-            doInit();
-            ValidPathInfo info = store->queryPathInfo(path);
+            ValidPathInfo info = store()->queryPathInfo(path);
             if (info.deriver == "")
                 XPUSHs(&PL_sv_undef);
             else
@@ -120,56 +117,52 @@ SV * queryPathInfo(char * path, int base32)
                 av_push(arr, newSVpv(i->c_str(), 0));
             XPUSHs(sv_2mortal(newRV((SV *) arr)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * queryPathFromHashPart(char * hashPart)
     PPCODE:
         try {
-            doInit();
-            Path path = store->queryPathFromHashPart(hashPart);
+            Path path = store()->queryPathFromHashPart(hashPart);
             XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * computeFSClosure(int flipDirection, int includeOutputs, ...)
     PPCODE:
         try {
-            doInit();
             PathSet paths;
             for (int n = 2; n < items; ++n)
-                computeFSClosure(*store, SvPV_nolen(ST(n)), paths, flipDirection, includeOutputs);
+                store()->computeFSClosure(SvPV_nolen(ST(n)), paths, flipDirection, includeOutputs);
             for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
                 XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * topoSortPaths(...)
     PPCODE:
         try {
-            doInit();
             PathSet paths;
             for (int n = 0; n < items; ++n) paths.insert(SvPV_nolen(ST(n)));
-            Paths sorted = topoSortPaths(*store, paths);
+            Paths sorted = store()->topoSortPaths(paths);
             for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i)
                 XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * followLinksToStorePath(char * path)
     CODE:
         try {
-            doInit();
             RETVAL = newSVpv(followLinksToStorePath(path).c_str(), 0);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
     OUTPUT:
         RETVAL
@@ -178,24 +171,22 @@ SV * followLinksToStorePath(char * path)
 void exportPaths(int fd, int sign, ...)
     PPCODE:
         try {
-            doInit();
             Paths paths;
             for (int n = 2; n < items; ++n) paths.push_back(SvPV_nolen(ST(n)));
             FdSink sink(fd);
-            exportPaths(*store, paths, sign, sink);
+            store()->exportPaths(paths, sign, sink);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 void importPaths(int fd)
     PPCODE:
         try {
-            doInit();
             FdSource source(fd);
-            store->importPaths(false, source);
+            store()->importPaths(false, source);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -206,7 +197,7 @@ SV * hashPath(char * algo, int base32, char * path)
             string s = base32 ? printHash32(h) : printHash(h);
             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -217,7 +208,7 @@ SV * hashFile(char * algo, int base32, char * path)
             string s = base32 ? printHash32(h) : printHash(h);
             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -228,7 +219,7 @@ SV * hashString(char * algo, int base32, char * s)
             string s = base32 ? printHash32(h) : printHash(h);
             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -239,7 +230,7 @@ SV * convertHash(char * algo, char * s, int toBase32)
             string s = toBase32 ? printHash32(h) : printHash(h);
             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -260,7 +251,7 @@ SV * signString(SV * secretKey_, char * msg)
             throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
 #endif
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -283,7 +274,7 @@ int checkSignature(SV * publicKey_, SV * sig_, char * msg)
             throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
 #endif
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
     OUTPUT:
         RETVAL
@@ -292,24 +283,22 @@ int checkSignature(SV * publicKey_, SV * sig_, char * msg)
 SV * addToStore(char * srcPath, int recursive, char * algo)
     PPCODE:
         try {
-            doInit();
-            Path path = store->addToStore(baseNameOf(srcPath), srcPath, recursive, parseHashType(algo));
+            Path path = store()->addToStore(baseNameOf(srcPath), srcPath, recursive, parseHashType(algo));
             XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
 SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name)
     PPCODE:
         try {
-            doInit();
             HashType ht = parseHashType(algo);
             Path path = makeFixedOutputPath(recursive, ht,
                 parseHash16or32(ht, hash), name);
             XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
 
 
@@ -318,8 +307,7 @@ SV * derivationFromPath(char * drvPath)
         HV *hash;
     CODE:
         try {
-            doInit();
-            Derivation drv = derivationFromPath(*store, drvPath);
+            Derivation drv = store()->derivationFromPath(drvPath);
             hash = newHV();
 
             HV * outputs = newHV();
@@ -352,7 +340,7 @@ SV * derivationFromPath(char * drvPath)
 
             RETVAL = newRV_noinc((SV *)hash);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
     OUTPUT:
         RETVAL
@@ -361,8 +349,7 @@ SV * derivationFromPath(char * drvPath)
 void addTempRoot(char * storePath)
     PPCODE:
         try {
-            doInit();
-            store->addTempRoot(storePath);
+            store()->addTempRoot(storePath);
         } catch (Error & e) {
-            croak(e.what());
+            croak("%s", e.what());
         }
diff --git a/release.nix b/release.nix
index a6a745e27548..e27355c59365 100644
--- a/release.nix
+++ b/release.nix
@@ -1,4 +1,5 @@
 { nix ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
+, nixpkgs ? { outPath = <nixpkgs>; revCount = 1234; shortRev = "abcdef"; }
 , officialRelease ? false
 }:
 
@@ -76,7 +77,8 @@ let
 
     build = pkgs.lib.genAttrs systems (system:
 
-      with import <nixpkgs> { inherit system; };
+      # FIXME: temporarily use a different branch for the Darwin build.
+      with import (if system == "x86_64-darwin" then <nixpkgs-darwin> else <nixpkgs>) { inherit system; };
 
       releaseTools.nixBuild {
         name = "nix";
@@ -110,7 +112,8 @@ let
 
     binaryTarball = pkgs.lib.genAttrs systems (system:
 
-      with import <nixpkgs> { inherit system; };
+      # FIXME: temporarily use a different branch for the Darwin build.
+      with import (if system == "x86_64-darwin" then <nixpkgs-darwin> else <nixpkgs>) { inherit system; };
 
       let
         toplevel = builtins.getAttr system jobs.build;
@@ -232,6 +235,26 @@ let
           touch $out/nix-support/hydra-build-products
         ''); # */
 
+    tests.evalNixpkgs =
+      import <nixpkgs/pkgs/top-level/make-tarball.nix> {
+        inherit nixpkgs;
+        inherit pkgs;
+        nix = build.x86_64-linux;
+        officialRelease = false;
+      };
+
+    tests.evalNixOS =
+      pkgs.runCommand "eval-nixos" { buildInputs = [ build.x86_64-linux ]; }
+        ''
+          export NIX_DB_DIR=$TMPDIR
+          export NIX_STATE_DIR=$TMPDIR
+          nix-store --init
+
+          nix-instantiate ${nixpkgs}/nixos/release-combined.nix -A tested --dry-run
+
+          touch $out
+        '';
+
 
     # Aggregate job containing the release-critical jobs.
     release = pkgs.releaseTools.aggregate {
@@ -262,6 +285,8 @@ let
           tests.remoteBuilds
           tests.nix-copy-closure
           tests.binaryTarball
+          tests.evalNixpkgs
+          tests.evalNixOS
         ];
     };
 
diff --git a/scripts/download-from-binary-cache.pl.in b/scripts/download-from-binary-cache.pl.in
index 60f7f9aef3d2..a4f858610aca 100644
--- a/scripts/download-from-binary-cache.pl.in
+++ b/scripts/download-from-binary-cache.pl.in
@@ -566,7 +566,7 @@ sub downloadBinary {
         die if $requireSignedBinaryCaches && !defined $info->{signedBy};
         print STDERR "\n*** Downloading ‘$url’ ", ($requireSignedBinaryCaches ? "(signed by ‘$info->{signedBy}’) " : ""), "to ‘$storePath’...\n";
         checkURL $url;
-        if (system("$Nix::Config::curl --fail --location --insecure --connect-timeout $curlConnectTimeout -A '$userAgent' '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
+        if (system("$Nix::Config::curl --fail --location --connect-timeout $curlConnectTimeout -A '$userAgent' '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
             warn "download of ‘$url’ failed" . ($! ? ": $!" : "") . "\n";
             next;
         }
diff --git a/scripts/download-using-manifests.pl.in b/scripts/download-using-manifests.pl.in
index 591cd6b43a3a..ffc49f8fffde 100755
--- a/scripts/download-using-manifests.pl.in
+++ b/scripts/download-using-manifests.pl.in
@@ -17,8 +17,7 @@ my $logFile = "$Nix::Config::logDir/downloads";
 # estimating the expected download size.
 my $fast = 1;
 
-# ‘--insecure’ is fine because Nix verifies the hash of the result.
-my $curl = "$Nix::Config::curl --fail --location --insecure";
+my $curl = "$Nix::Config::curl --fail --location";
 
 
 # Open the manifest cache and update it if necessary.
diff --git a/scripts/install-nix-from-closure.sh b/scripts/install-nix-from-closure.sh
index 3efe7b38435e..465cc1013cc1 100644
--- a/scripts/install-nix-from-closure.sh
+++ b/scripts/install-nix-from-closure.sh
@@ -92,7 +92,7 @@ p=$NIX_LINK/etc/profile.d/nix.sh
 added=
 for i in .bash_profile .bash_login .profile; do
     fn="$HOME/$i"
-    if [ -e "$fn" ]; then
+    if [ -w "$fn" ]; then
         if ! grep -q "$p" "$fn"; then
             echo "modifying $fn..." >&2
             echo "if [ -e $p ]; then . $p; fi # added by Nix installer" >> $fn
diff --git a/scripts/nix-build.in b/scripts/nix-build.in
index ea099532b7cb..b93e5ab1390a 100755
--- a/scripts/nix-build.in
+++ b/scripts/nix-build.in
@@ -57,7 +57,7 @@ if ($runEnv && defined $ARGV[0] && $ARGV[0] !~ /nix-shell/) {
             while (<SCRIPT>) {
                 chomp;
                 if (/^\#\!\s*nix-shell (.*)$/) {
-                    push @ARGV, shellwords(/ /, $1);
+                    push @ARGV, shellwords($1);
                 }
             }
         }
@@ -270,7 +270,7 @@ foreach my $expr (@exprs) {
         my $tmp = $ENV{"TMPDIR"} // $ENV{"XDG_RUNTIME_DIR"} // "/tmp";
         if ($pure) {
             foreach my $name (keys %ENV) {
-                next if grep { $_ eq $name } ("HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER");
+                next if grep { $_ eq $name } ("HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER", "NIX_BUILD_SHELL");
                 delete $ENV{$name};
             }
             # NixOS hack: prevent /etc/bashrc from sourcing /etc/profile.
diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc
index 13760490d9c4..68ab4b5cdcbf 100644
--- a/src/libexpr/common-opts.cc
+++ b/src/libexpr/common-opts.cc
@@ -55,7 +55,7 @@ bool parseSearchPathArg(Strings::iterator & i,
 Path lookupFileArg(EvalState & state, string s)
 {
     if (isUri(s))
-        return downloadFileCached(s, true);
+        return downloadFileCached(state.store, s, true);
     else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
         Path p = s.substr(1, s.size() - 2);
         return state.findFile(p);
diff --git a/src/libexpr/common-opts.hh b/src/libexpr/common-opts.hh
index be0f40202430..cb2732d6fe7e 100644
--- a/src/libexpr/common-opts.hh
+++ b/src/libexpr/common-opts.hh
@@ -4,6 +4,8 @@
 
 namespace nix {
 
+class Store;
+
 /* Some common option parsing between nix-env and nix-instantiate. */
 bool parseAutoArgs(Strings::iterator & i,
     const Strings::iterator & argsEnd, std::map<string, string> & res);
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index b951daacaf0c..8ce2f3dfa6af 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -248,7 +248,7 @@ static Strings parseNixPath(const string & in)
 }
 
 
-EvalState::EvalState(const Strings & _searchPath)
+EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
     : sWith(symbols.create("<with>"))
     , sOutPath(symbols.create("outPath"))
     , sDrvPath(symbols.create("drvPath"))
@@ -265,6 +265,8 @@ EvalState::EvalState(const Strings & _searchPath)
     , sLine(symbols.create("line"))
     , sColumn(symbols.create("column"))
     , sFunctor(symbols.create("__functor"))
+    , sToString(symbols.create("__toString"))
+    , store(store)
     , baseEnv(allocEnv(128))
     , staticBaseEnv(false, 0)
 {
@@ -1431,7 +1433,14 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
     }
 
     if (v.type == tAttrs) {
-        Bindings::iterator i = v.attrs->find(sOutPath);
+        auto i = v.attrs->find(sToString);
+        if (i != v.attrs->end()) {
+            forceValue(*i->value, pos);
+            Value v1;
+            callFunction(*i->value, v, v1, pos);
+            return coerceToString(pos, v1, context, coerceMore, copyToStore);
+        }
+        i = v.attrs->find(sOutPath);
         if (i == v.attrs->end()) throwTypeError("cannot coerce a set to a string, at %1%", pos);
         return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
     }
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 13353a43f4e0..40e05712bab1 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -16,6 +16,7 @@
 namespace nix {
 
 
+class Store;
 class EvalState;
 
 
@@ -69,7 +70,7 @@ public:
 
     const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
         sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
-        sFile, sLine, sColumn, sFunctor;
+        sFile, sLine, sColumn, sFunctor, sToString;
     Symbol sDerivationNix;
 
     /* If set, force copying files to the Nix store even if they
@@ -82,6 +83,8 @@ public:
 
     Value vEmptySet;
 
+    const ref<Store> store;
+
 private:
     SrcToStore srcToStore;
 
@@ -97,7 +100,7 @@ private:
 
 public:
 
-    EvalState(const Strings & _searchPath);
+    EvalState(const Strings & _searchPath, ref<Store> store);
     ~EvalState();
 
     void addToSearchPath(const string & s, bool warn = false);
@@ -241,6 +244,8 @@ public:
     /* Print statistics. */
     void printStats();
 
+    void realiseContext(const PathSet & context);
+
 private:
 
     unsigned long nrEnvs = 0;
@@ -291,7 +296,4 @@ struct InvalidPathError : EvalError
 #endif
 };
 
-/* Realise all paths in `context' */
-void realiseContext(const PathSet & context);
-
 }
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 7a6e48215b34..701c01aff973 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -8,6 +8,7 @@
 
 %x STRING
 %x IND_STRING
+%x INSIDE_DOLLAR_CURLY
 
 
 %{
@@ -94,6 +95,8 @@ URI         [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~
 
 %%
 
+<INITIAL,INSIDE_DOLLAR_CURLY>{
+
 
 if          { return IF; }
 then        { return THEN; }
@@ -131,11 +134,15 @@ or          { return OR_KW; }
               return FLOAT;
             }
 
-\$\{        { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
-\{          { PUSH_STATE(INITIAL); return '{'; }
-\}          { POP_STATE(); return '}'; }
+\$\{        { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
+}
+
+\}                           { return '}'; }
+<INSIDE_DOLLAR_CURLY>\}      { POP_STATE(); return '}'; }
+\{                           { return '{'; }
+<INSIDE_DOLLAR_CURLY>\{      { PUSH_STATE(INSIDE_DOLLAR_CURLY); return '{'; }
 
-\"          { PUSH_STATE(STRING); return '"'; }
+<INITIAL,INSIDE_DOLLAR_CURLY>\"          { PUSH_STATE(STRING); return '"'; }
 <STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)*\$/\" |
 <STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)+ {
               /* It is impossible to match strings ending with '$' with one
@@ -144,11 +151,11 @@ or          { return OR_KW; }
               yylval->e = unescapeStr(data->symbols, yytext);
               return STR;
             }
-<STRING>\$\{  { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
+<STRING>\$\{  { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
 <STRING>\"  { POP_STATE(); return '"'; }
 <STRING>.   return yytext[0]; /* just in case: shouldn't be reached */
 
-\'\'(\ *\n)?     { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
+<INITIAL,INSIDE_DOLLAR_CURLY>\'\'(\ *\n)?     { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
 <IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
                    yylval->e = new ExprIndStr(yytext);
                    return IND_STR;
@@ -165,7 +172,7 @@ or          { return OR_KW; }
                    yylval->e = unescapeStr(data->symbols, yytext + 2);
                    return IND_STR;
                  }
-<IND_STRING>\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
+<IND_STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
 <IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
 <IND_STRING>\'   {
                    yylval->e = new ExprIndStr("'");
@@ -173,6 +180,8 @@ or          { return OR_KW; }
                  }
 <IND_STRING>.    return yytext[0]; /* just in case: shouldn't be reached */
 
+<INITIAL,INSIDE_DOLLAR_CURLY>{
+
 {PATH}      { yylval->path = strdup(yytext); return PATH; }
 {HPATH}     { yylval->path = strdup(yytext); return HPATH; }
 {SPATH}     { yylval->path = strdup(yytext); return SPATH; }
@@ -184,6 +193,7 @@ or          { return OR_KW; }
 
 .           return yytext[0];
 
+}
 
 %%
 
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 32c66c783328..f87aa261935b 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -606,7 +606,7 @@ void EvalState::addToSearchPath(const string & s, bool warn)
     }
 
     if (isUri(path))
-        path = downloadFileCached(path, true);
+        path = downloadFileCached(store, path, true);
 
     path = absPath(path);
     if (pathExists(path)) {
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index ebc5ae143143..3c899d769253 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -1,15 +1,15 @@
+#include "archive.hh"
+#include "derivations.hh"
+#include "download.hh"
+#include "eval-inline.hh"
 #include "eval.hh"
-#include "misc.hh"
 #include "globals.hh"
+#include "json-to-value.hh"
+#include "names.hh"
 #include "store-api.hh"
 #include "util.hh"
-#include "archive.hh"
-#include "value-to-xml.hh"
 #include "value-to-json.hh"
-#include "json-to-value.hh"
-#include "names.hh"
-#include "eval-inline.hh"
-#include "download.hh"
+#include "value-to-xml.hh"
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -43,7 +43,7 @@ std::pair<string, string> decodeContext(const string & s)
 InvalidPathError::InvalidPathError(const Path & path) :
     EvalError(format("path ‘%1%’ is not valid") % path), path(path) {}
 
-void realiseContext(const PathSet & context)
+void EvalState::realiseContext(const PathSet & context)
 {
     PathSet drvs;
     for (auto & i : context) {
@@ -52,16 +52,14 @@ void realiseContext(const PathSet & context)
         assert(isStorePath(ctx));
         if (!store->isValidPath(ctx))
             throw InvalidPathError(ctx);
-        if (!decoded.second.empty() && isDerivation(ctx))
+        if (!decoded.second.empty() && nix::isDerivation(ctx))
             drvs.insert(decoded.first + "!" + decoded.second);
     }
     if (!drvs.empty()) {
         /* For performance, prefetch all substitute info. */
         PathSet willBuild, willSubstitute, unknown;
         unsigned long long downloadSize, narSize;
-        queryMissing(*store, drvs,
-            willBuild, willSubstitute, unknown, downloadSize, narSize);
-
+        store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
         store->buildPaths(drvs);
     }
 }
@@ -75,7 +73,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
     Path path = state.coerceToPath(pos, *args[1], context);
 
     try {
-        realiseContext(context);
+        state.realiseContext(context);
     } catch (InvalidPathError & e) {
         throw EvalError(format("cannot import ‘%1%’, since path ‘%2%’ is not valid, at %3%")
             % path % e.path % pos);
@@ -83,7 +81,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
 
     path = state.checkSourcePath(path);
 
-    if (isStorePath(path) && store->isValidPath(path) && isDerivation(path)) {
+    if (isStorePath(path) && state.store->isValidPath(path) && isDerivation(path)) {
         Derivation drv = readDerivation(path);
         Value & w = *state.allocValue();
         state.mkAttrs(w, 3 + drv.outputs.size());
@@ -145,7 +143,7 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args
     Path path = state.coerceToPath(pos, *args[0], context);
 
     try {
-        realiseContext(context);
+        state.realiseContext(context);
     } catch (InvalidPathError & e) {
         throw EvalError(format("cannot import ‘%1%’, since path ‘%2%’ is not valid, at %3%")
             % path % e.path % pos);
@@ -259,7 +257,7 @@ struct CompareValues
         if (v1->type == tInt && v2->type == tFloat)
             return v1->integer < v2->fpoint;
         if (v1->type != v2->type)
-            throw EvalError("cannot compare values of different types");
+            throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
         switch (v1->type) {
             case tInt:
                 return v1->integer < v2->integer;
@@ -573,11 +571,12 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
            runs. */
         if (path.at(0) == '=') {
             /* !!! This doesn't work if readOnlyMode is set. */
-            PathSet refs; computeFSClosure(*store, string(path, 1), refs);
+            PathSet refs;
+            state.store->computeFSClosure(string(path, 1), refs);
             for (auto & j : refs) {
                 drv.inputSrcs.insert(j);
                 if (isDerivation(j))
-                    drv.inputDrvs[j] = store->queryDerivationOutputNames(j);
+                    drv.inputDrvs[j] = state.store->queryDerivationOutputNames(j);
             }
         }
 
@@ -594,7 +593,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
         /* Handle derivation contexts returned by
            ‘builtins.storePath’. */
         else if (isDerivation(path))
-            drv.inputDrvs[path] = store->queryDerivationOutputNames(path);
+            drv.inputDrvs[path] = state.store->queryDerivationOutputNames(path);
 
         /* Otherwise it's a source file. */
         else
@@ -643,7 +642,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
 
         /* Use the masked derivation expression to compute the output
            path. */
-        Hash h = hashDerivationModulo(*store, drv);
+        Hash h = hashDerivationModulo(*state.store, drv);
 
         for (auto & i : drv.outputs)
             if (i.second.path == "") {
@@ -654,7 +653,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
     }
 
     /* Write the resulting term into the Nix store directory. */
-    Path drvPath = writeDerivation(*store, drv, drvName, state.repair);
+    Path drvPath = writeDerivation(state.store, drv, drvName, state.repair);
 
     printMsg(lvlChatty, format("instantiated ‘%1%’ -> ‘%2%’")
         % drvName % drvPath);
@@ -662,7 +661,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
     /* Optimisation, but required in read-only mode! because in that
        case we don't actually write store derivations, so we can't
        read them later. */
-    drvHashes[drvPath] = hashDerivationModulo(*store, drv);
+    drvHashes[drvPath] = hashDerivationModulo(*state.store, drv);
 
     state.mkAttrs(v, 1 + drv.outputs.size());
     mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
@@ -708,7 +707,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V
         throw EvalError(format("path ‘%1%’ is not in the Nix store, at %2%") % path % pos);
     Path path2 = toStorePath(path);
     if (!settings.readOnlyMode)
-        store->ensurePath(path2);
+        state.store->ensurePath(path2);
     context.insert(path2);
     mkString(v, path, context);
 }
@@ -758,7 +757,7 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va
     PathSet context;
     Path path = state.coerceToPath(pos, *args[0], context);
     try {
-        realiseContext(context);
+        state.realiseContext(context);
     } catch (InvalidPathError & e) {
         throw EvalError(format("cannot read ‘%1%’, since path ‘%2%’ is not valid, at %3%")
             % path % e.path % pos);
@@ -799,7 +798,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
     string path = state.forceStringNoCtx(*args[1], pos);
 
     try {
-        realiseContext(context);
+        state.realiseContext(context);
     } catch (InvalidPathError & e) {
         throw EvalError(format("cannot find ‘%1%’, since path ‘%2%’ is not valid, at %3%")
             % path % e.path % pos);
@@ -814,7 +813,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
     PathSet ctx;
     Path path = state.coerceToPath(pos, *args[0], ctx);
     try {
-        realiseContext(ctx);
+        state.realiseContext(ctx);
     } catch (InvalidPathError & e) {
         throw EvalError(format("cannot read ‘%1%’, since path ‘%2%’ is not valid, at %3%")
             % path % e.path % pos);
@@ -892,13 +891,13 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
             if (path.at(0) != '~')
                 throw EvalError(format("in ‘toFile’: the file ‘%1%’ cannot refer to derivation outputs, at %2%") % name % pos);
             path = string(path, 1);
-	}
+        }
         refs.insert(path);
     }
 
     Path storePath = settings.readOnlyMode
         ? computeStorePathForText(name, contents, refs)
-        : store->addTextToStore(name, contents, refs, state.repair);
+        : state.store->addTextToStore(name, contents, refs, state.repair);
 
     /* Note: we don't need to add `context' to the context of the
        result, since `storePath' itself has references to the paths
@@ -964,7 +963,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
 
     Path dstPath = settings.readOnlyMode
         ? computeStorePathForPath(path, true, htSHA256, filter).first
-        : store->addToStore(baseNameOf(path), path, true, htSHA256, filter, state.repair);
+        : state.store->addToStore(baseNameOf(path), path, true, htSHA256, filter, state.repair);
 
     mkString(v, dstPath, singleton<PathSet>(dstPath));
 }
@@ -1167,7 +1166,7 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
 
 /* Return a set containing the names of the formal arguments expected
    by the function `f'.  The value of each attribute is a Boolean
-   denoting whether has a default value.  For instance,
+   denoting whether the corresponding argument has a default value.  For instance,
 
       functionArgs ({ x, y ? 123}: ...)
    => { x = false; y = true; }
@@ -1387,7 +1386,7 @@ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Val
 
     state.mkList(v, len);
 
-    for (unsigned int n = 0; n < len; ++n) {
+    for (unsigned int n = 0; n < (unsigned int) len; ++n) {
         Value * arg = state.allocValue();
         mkInt(*arg, n);
         mkApp(*(v.listElems()[n] = state.allocValue()), *args[0], *arg);
@@ -1704,7 +1703,7 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
     } else
         url = state.forceStringNoCtx(*args[0], pos);
 
-    Path res = downloadFileCached(url, unpack);
+    Path res = downloadFileCached(state.store, url, unpack);
     mkString(v, res, PathSet({res}));
 }
 
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index a6e8f352a0ab..88ed52497fb9 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -4,7 +4,6 @@
 #include "globals.hh"
 #include "store-api.hh"
 #include "util.hh"
-#include "misc.hh"
 
 #include <iostream>
 #include <cctype>
@@ -47,22 +46,22 @@ void printGCWarning()
 }
 
 
-void printMissing(StoreAPI & store, const PathSet & paths)
+void printMissing(ref<Store> store, const PathSet & paths)
 {
     unsigned long long downloadSize, narSize;
     PathSet willBuild, willSubstitute, unknown;
-    queryMissing(store, paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
-    printMissing(willBuild, willSubstitute, unknown, downloadSize, narSize);
+    store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
+    printMissing(store, willBuild, willSubstitute, unknown, downloadSize, narSize);
 }
 
 
-void printMissing(const PathSet & willBuild,
+void printMissing(ref<Store> store, const PathSet & willBuild,
     const PathSet & willSubstitute, const PathSet & unknown,
     unsigned long long downloadSize, unsigned long long narSize)
 {
     if (!willBuild.empty()) {
         printMsg(lvlInfo, format("these derivations will be built:"));
-        Paths sorted = topoSortPaths(*store, willBuild);
+        Paths sorted = store->topoSortPaths(willBuild);
         reverse(sorted.begin(), sorted.end());
         for (auto & i : sorted)
             printMsg(lvlInfo, format("  %1%") % i);
diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh
index 32183d6a6cb0..3f3f6f7232e0 100644
--- a/src/libmain/shared.hh
+++ b/src/libmain/shared.hh
@@ -19,8 +19,6 @@ public:
     Exit(int status) : status(status) { }
 };
 
-class StoreAPI;
-
 int handleExceptions(const string & programName, std::function<void()> fun);
 
 void initNix();
@@ -33,9 +31,11 @@ void printVersion(const string & programName);
 /* Ugh.  No better place to put this. */
 void printGCWarning();
 
-void printMissing(StoreAPI & store, const PathSet & paths);
+class Store;
+
+void printMissing(ref<Store> store, const PathSet & paths);
 
-void printMissing(const PathSet & willBuild,
+void printMissing(ref<Store> store, const PathSet & willBuild,
     const PathSet & willSubstitute, const PathSet & unknown,
     unsigned long long downloadSize, unsigned long long narSize);
 
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index e00a07d3b1a0..180a558dc2e9 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -2,7 +2,6 @@
 
 #include "references.hh"
 #include "pathlocks.hh"
-#include "misc.hh"
 #include "globals.hh"
 #include "local-store.hh"
 #include "util.hh"
@@ -621,11 +620,15 @@ HookInstance::HookInstance()
         if (dup2(builderOut.writeSide, 4) == -1)
             throw SysError("dupping builder's stdout/stderr");
 
-        execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(),
-            (format("%1%") % settings.maxSilentTime).str().c_str(),
-            (format("%1%") % settings.printBuildTrace).str().c_str(),
-            (format("%1%") % settings.buildTimeout).str().c_str(),
-            NULL);
+        Strings args = {
+            baseNameOf(buildHook),
+            settings.thisSystem,
+            (format("%1%") % settings.maxSilentTime).str(),
+            (format("%1%") % settings.printBuildTrace).str(),
+            (format("%1%") % settings.buildTimeout).str()
+        };
+
+        execv(buildHook.c_str(), stringsToCharPtrs(args).data());
 
         throw SysError(format("executing ‘%1%’") % buildHook);
     });
@@ -902,7 +905,7 @@ DerivationGoal::DerivationGoal(const Path & drvPath, const BasicDerivation & drv
 {
     this->drv = std::unique_ptr<BasicDerivation>(new BasicDerivation(drv));
     state = &DerivationGoal::haveDerivation;
-    name = (format("building of %1%") % showPaths(outputPaths(drv))).str();
+    name = (format("building of %1%") % showPaths(drv.outputPaths())).str();
     trace("created");
 
     /* Prevent the .chroot directory from being
@@ -1014,7 +1017,7 @@ void DerivationGoal::loadDerivation()
     assert(worker.store.isValidPath(drvPath));
 
     /* Get the derivation. */
-    drv = std::unique_ptr<BasicDerivation>(new Derivation(derivationFromPath(worker.store, drvPath)));
+    drv = std::unique_ptr<BasicDerivation>(new Derivation(worker.store.derivationFromPath(drvPath)));
 
     haveDerivation();
 }
@@ -1041,10 +1044,19 @@ void DerivationGoal::haveDerivation()
     for (auto & i : invalidOutputs)
         if (pathFailed(i)) return;
 
+    /* Reject doing a hash build of anything other than a fixed-output
+       derivation. */
+    if (buildMode == bmHash) {
+        if (drv->outputs.size() != 1 ||
+            drv->outputs.find("out") == drv->outputs.end() ||
+            drv->outputs["out"].hashAlgo == "")
+            throw Error(format("cannot do a hash build of non-fixed-output derivation ‘%1%’") % drvPath);
+    }
+
     /* We are first going to try to create the invalid output paths
        through substitutes.  If that doesn't work, we'll build
        them. */
-    if (settings.useSubstitutes && substitutesAllowed(*drv))
+    if (settings.useSubstitutes && drv->substitutesAllowed())
         for (auto & i : invalidOutputs)
             addWaitee(worker.makeSubstitutionGoal(i, buildMode == bmRepair));
 
@@ -1123,8 +1135,10 @@ void DerivationGoal::repairClosure()
 
     /* Get the output closure. */
     PathSet outputClosure;
-    for (auto & i : drv->outputs)
-        computeFSClosure(worker.store, i.second.path, outputClosure);
+    for (auto & i : drv->outputs) {
+        if (!wantOutput(i.first, wantedOutputs)) continue;
+        worker.store.computeFSClosure(i.second.path, outputClosure);
+    }
 
     /* Filter out our own outputs (which we have already checked). */
     for (auto & i : drv->outputs)
@@ -1134,11 +1148,11 @@ void DerivationGoal::repairClosure()
        derivation is responsible for which path in the output
        closure. */
     PathSet inputClosure;
-    if (useDerivation) computeFSClosure(worker.store, drvPath, inputClosure);
+    if (useDerivation) worker.store.computeFSClosure(drvPath, inputClosure);
     std::map<Path, Path> outputsToDrv;
     for (auto & i : inputClosure)
         if (isDerivation(i)) {
-            Derivation drv = derivationFromPath(worker.store, i);
+            Derivation drv = worker.store.derivationFromPath(i);
             for (auto & j : drv.outputs)
                 outputsToDrv[j.second.path] = i;
         }
@@ -1210,10 +1224,10 @@ void DerivationGoal::inputsRealised()
                `i' as input paths.  Only add the closures of output paths
                that are specified as inputs. */
             assert(worker.store.isValidPath(i.first));
-            Derivation inDrv = derivationFromPath(worker.store, i.first);
+            Derivation inDrv = worker.store.derivationFromPath(i.first);
             for (auto & j : i.second)
                 if (inDrv.outputs.find(j) != inDrv.outputs.end())
-                    computeFSClosure(worker.store, inDrv.outputs[j].path, inputPaths);
+                    worker.store.computeFSClosure(inDrv.outputs[j].path, inputPaths);
                 else
                     throw Error(
                         format("derivation ‘%1%’ requires non-existent output ‘%2%’ from input derivation ‘%3%’")
@@ -1222,7 +1236,7 @@ void DerivationGoal::inputsRealised()
 
     /* Second, the input sources. */
     for (auto & i : drv->inputSrcs)
-        computeFSClosure(worker.store, i, inputPaths);
+        worker.store.computeFSClosure(i, inputPaths);
 
     debug(format("added input paths %1%") % showPaths(inputPaths));
 
@@ -1245,46 +1259,6 @@ void DerivationGoal::inputsRealised()
 }
 
 
-static bool isBuiltin(const BasicDerivation & drv)
-{
-    return string(drv.builder, 0, 8) == "builtin:";
-}
-
-
-static bool canBuildLocally(const BasicDerivation & drv)
-{
-    return drv.platform == settings.thisSystem
-        || isBuiltin(drv)
-#if __linux__
-        || (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
-        || (drv.platform == "armv6l-linux" && settings.thisSystem == "armv7l-linux")
-#elif __FreeBSD__
-        || (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-freebsd")
-        || (drv.platform == "i686-linux" && settings.thisSystem == "i686-freebsd")
-#endif
-        ;
-}
-
-
-static string get(const StringPairs & map, const string & key, const string & def = "")
-{
-    StringPairs::const_iterator i = map.find(key);
-    return i == map.end() ? def : i->second;
-}
-
-
-bool willBuildLocally(const BasicDerivation & drv)
-{
-    return get(drv.env, "preferLocalBuild") == "1" && canBuildLocally(drv);
-}
-
-
-bool substitutesAllowed(const BasicDerivation & drv)
-{
-    return get(drv.env, "allowSubstitutes", "1") == "1";
-}
-
-
 void DerivationGoal::tryToBuild()
 {
     trace("trying to build");
@@ -1307,7 +1281,7 @@ void DerivationGoal::tryToBuild()
        can't acquire the lock, then continue; hopefully some other
        goal can start a build, and if not, the main loop will sleep a
        few seconds and then retry this goal. */
-    if (!outputLocks.lockPaths(outputPaths(*drv), "", false)) {
+    if (!outputLocks.lockPaths(drv->outputPaths(), "", false)) {
         worker.waitForAWhile(shared_from_this());
         return;
     }
@@ -1320,7 +1294,6 @@ void DerivationGoal::tryToBuild()
        now hold the locks on the output paths, no other process can
        build this derivation, so no further checks are necessary. */
     validPaths = checkPathValidity(true, buildMode == bmRepair);
-    assert(buildMode != bmCheck || validPaths.size() == drv->outputs.size());
     if (buildMode != bmCheck && validPaths.size() == drv->outputs.size()) {
         debug(format("skipping build of derivation ‘%1%’, someone beat us to it") % drvPath);
         outputLocks.setDeletion(true);
@@ -1328,7 +1301,7 @@ void DerivationGoal::tryToBuild()
         return;
     }
 
-    missingPaths = outputPaths(*drv);
+    missingPaths = drv->outputPaths();
     if (buildMode != bmCheck)
         for (auto & i : validPaths) missingPaths.erase(i);
 
@@ -1351,7 +1324,7 @@ void DerivationGoal::tryToBuild()
     /* Don't do a remote build if the derivation has the attribute
        `preferLocalBuild' set.  Also, check and repair modes are only
        supported for local builds. */
-    bool buildLocally = buildMode != bmNormal || willBuildLocally(*drv);
+    bool buildLocally = buildMode != bmNormal || drv->willBuildLocally();
 
     /* Is the build hook willing to accept this job? */
     if (!buildLocally) {
@@ -1647,7 +1620,7 @@ HookReply DerivationGoal::tryBuildHook()
        list it since the remote system *probably* already has it.) */
     PathSet allInputs;
     allInputs.insert(inputPaths.begin(), inputPaths.end());
-    computeFSClosure(worker.store, drvPath, allInputs);
+    worker.store.computeFSClosure(drvPath, allInputs);
 
     string s;
     for (auto & i : allInputs) { s += i; s += ' '; }
@@ -1702,7 +1675,7 @@ void DerivationGoal::startBuilder()
     startNest(nest, lvlInfo, f % showPaths(missingPaths) % curRound % nrRounds);
 
     /* Right platform? */
-    if (!canBuildLocally(*drv)) {
+    if (!drv->canBuildLocally()) {
         if (settings.printBuildTrace)
             printMsg(lvlError, format("@ unsupported-platform %1% %2%") % drvPath % drv->platform);
         throw Error(
@@ -1710,6 +1683,10 @@ void DerivationGoal::startBuilder()
             % drv->platform % settings.thisSystem % drvPath);
     }
 
+#if __APPLE__
+    additionalSandboxProfile = get(drv->env, "__sandboxProfile");
+#endif
+
     /* Are we doing a chroot build?  Note that fixed-output
        derivations are never done in a chroot, mainly so that
        functions like fetchurl (which needs a proper /etc/resolv.conf)
@@ -1723,7 +1700,13 @@ void DerivationGoal::startBuilder()
             throw Error("option ‘build-use-sandbox’ must be set to one of ‘true’, ‘false’ or ‘relaxed’");
         if (x == "true") {
             if (get(drv->env, "__noChroot") == "1")
-                throw Error(format("derivation ‘%1%’ has ‘__noChroot’ set, but that's not allowed when ‘build-use-sandbox’ is ‘true’") % drvPath);
+                throw Error(format("derivation ‘%1%’ has ‘__noChroot’ set, "
+                    "but that's not allowed when ‘build-use-sandbox’ is ‘true’") % drvPath);
+#if __APPLE__
+            if (additionalSandboxProfile != "")
+                throw Error(format("derivation ‘%1%’ specifies a sandbox profile, "
+                    "but this is only allowed when ‘build-use-sandbox’ is ‘relaxed’") % drvPath);
+#endif
             useChroot = true;
         }
         else if (x == "false")
@@ -1849,14 +1832,14 @@ void DerivationGoal::startBuilder()
            like passing all build-time dependencies of some path to a
            derivation that builds a NixOS DVD image. */
         PathSet paths, paths2;
-        computeFSClosure(worker.store, storePath, paths);
+        worker.store.computeFSClosure(storePath, paths);
         paths2 = paths;
 
         for (auto & j : paths2) {
             if (isDerivation(j)) {
-                Derivation drv = derivationFromPath(worker.store, j);
+                Derivation drv = worker.store.derivationFromPath(j);
                 for (auto & k : drv.outputs)
-                    computeFSClosure(worker.store, k.second.path, paths);
+                    worker.store.computeFSClosure(k.second.path, paths);
             }
         }
 
@@ -1920,13 +1903,10 @@ void DerivationGoal::startBuilder()
         PathSet closure;
         for (auto & i : dirsInChroot)
             if (isInStore(i.second))
-                computeFSClosure(worker.store, toStorePath(i.second), closure);
+                worker.store.computeFSClosure(toStorePath(i.second), closure);
         for (auto & i : closure)
             dirsInChroot[i] = i;
 
-#if __APPLE__
-        additionalSandboxProfile = get(drv->env, "__sandboxProfile");
-#endif
         string allowed = settings.get("allowed-impure-host-deps", string(DEFAULT_ALLOWED_IMPURE_PREFIXES));
         PathSet allowedPaths = tokenizeString<StringSet>(allowed);
 
@@ -1948,7 +1928,7 @@ void DerivationGoal::startBuilder()
                 }
             }
             if (!found)
-                throw Error(format("derivation '%1%' requested impure path ‘%2%’, but it was not in allowed-impure-host-deps (‘%3%’)") % drvPath % i % allowed);
+                throw Error(format("derivation ‘%1%’ requested impure path ‘%2%’, but it was not in allowed-impure-host-deps (‘%3%’)") % drvPath % i % allowed);
 
             dirsInChroot[i] = i;
         }
@@ -2170,7 +2150,7 @@ void DerivationGoal::startBuilder()
             size_t stackSize = 1 * 1024 * 1024;
             char * stack = (char *) mmap(0, stackSize,
                 PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
-            if (!stack) throw SysError("allocating stack");
+            if (stack == MAP_FAILED) throw SysError("allocating stack");
             int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
             if (!fixedOutput) flags |= CLONE_NEWNET;
             pid_t child = clone(childEntry, stack + stackSize, flags, this);
@@ -2191,7 +2171,7 @@ void DerivationGoal::startBuilder()
 #endif
     {
         ProcessOptions options;
-        options.allowVfork = !buildUser.enabled() && !isBuiltin(*drv);
+        options.allowVfork = !buildUser.enabled() && !drv->isBuiltin();
         pid = startProcess([&]() {
             runChild();
         }, options);
@@ -2445,7 +2425,7 @@ void DerivationGoal::runChild()
         const char *builder = "invalid";
 
         string sandboxProfile;
-        if (isBuiltin(*drv)) {
+        if (drv->isBuiltin()) {
             ;
 #if __APPLE__
         } else if (useChroot) {
@@ -2562,7 +2542,7 @@ void DerivationGoal::runChild()
         writeFull(STDERR_FILENO, string("\1\n"));
 
         /* Execute the program.  This should not return. */
-        if (isBuiltin(*drv)) {
+        if (drv->isBuiltin()) {
             try {
                 logType = ltFlat;
                 if (drv->builder == "builtin:fetchurl")
@@ -2625,6 +2605,8 @@ void DerivationGoal::registerOutputs()
        outputs to allow hard links between outputs. */
     InodesSeen inodesSeen;
 
+    Path checkSuffix = "-check";
+
     /* Check whether the output paths were created, and grep each
        output path to determine what other paths it references.  Also make all
        output paths read-only. */
@@ -2650,7 +2632,7 @@ void DerivationGoal::registerOutputs()
                 && redirectedBadOutputs.find(path) != redirectedBadOutputs.end()
                 && pathExists(redirected))
                 replaceValidPath(path, redirected);
-            if (buildMode == bmCheck)
+            if (buildMode == bmCheck && redirected != "")
                 actualPath = redirected;
         }
 
@@ -2713,12 +2695,29 @@ void DerivationGoal::registerOutputs()
                         format("output path ‘%1%’ should be a non-executable regular file") % path);
             }
 
-            /* Check the hash. */
+            /* Check the hash. In hash mode, move the path produced by
+               the derivation to its content-addressed location. */
             Hash h2 = recursive ? hashPath(ht, actualPath).first : hashFile(ht, actualPath);
-            if (h != h2)
-                throw BuildError(
-                    format("output path ‘%1%’ has %2% hash ‘%3%’ when ‘%4%’ was expected")
-                    % path % i.second.hashAlgo % printHash16or32(h2) % printHash16or32(h));
+            if (buildMode == bmHash) {
+                Path dest = makeFixedOutputPath(recursive, ht, h2, drv->env["name"]);
+                printMsg(lvlError, format("build produced path ‘%1%’ with %2% hash ‘%3%’")
+                    % dest % printHashType(ht) % printHash16or32(h2));
+                if (worker.store.isValidPath(dest))
+                    return;
+                if (actualPath != dest) {
+                    PathLocks outputLocks({dest});
+                    if (pathExists(dest))
+                        deletePath(dest);
+                    if (rename(actualPath.c_str(), dest.c_str()) == -1)
+                        throw SysError(format("moving ‘%1%’ to ‘%2%’") % actualPath % dest);
+                }
+                path = actualPath = dest;
+            } else {
+                if (h != h2)
+                    throw BuildError(
+                        format("output path ‘%1%’ has %2% hash ‘%3%’ when ‘%4%’ was expected")
+                        % path % i.second.hashAlgo % printHash16or32(h2) % printHash16or32(h));
+            }
         }
 
         /* Get rid of all weird permissions.  This also checks that
@@ -2734,9 +2733,20 @@ void DerivationGoal::registerOutputs()
         PathSet references = scanForReferences(actualPath, allPaths, hash);
 
         if (buildMode == bmCheck) {
+            if (!worker.store.isValidPath(path)) continue;
             ValidPathInfo info = worker.store.queryPathInfo(path);
-            if (hash.first != info.hash)
-                throw Error(format("derivation ‘%1%’ may not be deterministic: hash mismatch in output ‘%2%’") % drvPath % path);
+            if (hash.first != info.hash) {
+                if (settings.keepFailed) {
+                    Path dst = path + checkSuffix;
+                    if (pathExists(dst)) deletePath(dst);
+                    if (rename(actualPath.c_str(), dst.c_str()))
+                        throw SysError(format("renaming ‘%1%’ to ‘%2%’") % actualPath % dst);
+                    throw Error(format("derivation ‘%1%’ may not be deterministic: output ‘%2%’ differs from ‘%3%’")
+                        % drvPath % path % dst);
+                } else
+                    throw Error(format("derivation ‘%1%’ may not be deterministic: output ‘%2%’ differs")
+                        % drvPath % path);
+            }
             continue;
         }
 
@@ -2762,7 +2772,7 @@ void DerivationGoal::registerOutputs()
                 for (auto & i : references)
                     /* Don't call computeFSClosure on ourselves. */
                     if (actualPath != i)
-                        computeFSClosure(worker.store, i, used);
+                        worker.store.computeFSClosure(i, used);
             } else
                 used = references;
 
@@ -2781,9 +2791,11 @@ void DerivationGoal::registerOutputs()
         checkRefs("disallowedReferences", false, false);
         checkRefs("disallowedRequisites", false, true);
 
-        worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
+        if (curRound == nrRounds) {
+            worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
 
-        worker.store.markContentsGood(path);
+            worker.store.markContentsGood(path);
+        }
 
         ValidPathInfo info;
         info.path = path;
@@ -2796,10 +2808,37 @@ void DerivationGoal::registerOutputs()
 
     if (buildMode == bmCheck) return;
 
-    if (curRound > 1 && prevInfos != infos)
-        throw NotDeterministic(
-            format("result of ‘%1%’ differs from previous round; rejecting as non-deterministic")
-            % drvPath);
+    /* Compare the result with the previous round, and report which
+       path is different, if any.*/
+    if (curRound > 1 && prevInfos != infos) {
+        assert(prevInfos.size() == infos.size());
+        for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j)
+            if (!(*i == *j)) {
+                Path prev = i->path + checkSuffix;
+                if (pathExists(prev))
+                    throw NotDeterministic(
+                        format("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round")
+                        % i->path % drvPath % prev);
+                else
+                    throw NotDeterministic(
+                        format("output ‘%1%’ of ‘%2%’ differs from previous round")
+                        % i->path % drvPath);
+            }
+        assert(false); // shouldn't happen
+    }
+
+    if (settings.keepFailed) {
+        for (auto & i : drv->outputs) {
+            Path prev = i.second.path + checkSuffix;
+            if (pathExists(prev)) deletePath(prev);
+            if (curRound < nrRounds) {
+                Path dst = i.second.path + checkSuffix;
+                if (rename(i.second.path.c_str(), dst.c_str()))
+                    throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i.second.path % dst);
+            }
+        }
+
+    }
 
     if (curRound < nrRounds) {
         prevInfos = infos;
@@ -3122,6 +3161,7 @@ void SubstitutionGoal::tryNext()
         /* None left.  Terminate this goal and let someone else deal
            with it. */
         debug(format("path ‘%1%’ is required, but there is no substituter that can build it") % storePath);
+
         /* Hack: don't indicate failure if there were no substituters.
            In that case the calling derivation should just do a
            build. */
@@ -3721,7 +3761,7 @@ void Worker::waitForInput()
         }
     }
 
-    if (!waitingForAWhile.empty() && lastWokenUp + settings.pollInterval <= after) {
+    if (!waitingForAWhile.empty() && lastWokenUp + (time_t) settings.pollInterval <= after) {
         lastWokenUp = after;
         for (auto & i : waitingForAWhile) {
             GoalPtr goal = i.lock();
@@ -3817,8 +3857,17 @@ void LocalStore::repairPath(const Path & path)
 
     worker.run(goals);
 
-    if (goal->getExitCode() != Goal::ecSuccess)
-        throw Error(format("cannot repair path ‘%1%’") % path, worker.exitStatus());
+    if (goal->getExitCode() != Goal::ecSuccess) {
+        /* Since substituting the path didn't work, if we have a valid
+           deriver, then rebuild the deriver. */
+        Path deriver = queryDeriver(path);
+        if (deriver != "" && isValidPath(deriver)) {
+            goals.clear();
+            goals.insert(worker.makeDerivationGoal(deriver, StringSet(), bmRepair));
+            worker.run(goals);
+        } else
+            throw Error(format("cannot repair path ‘%1%’") % path, worker.exitStatus());
+    }
 }
 
 
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index 7959d5bfc3d5..d9b009d40322 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -2,7 +2,6 @@
 #include "store-api.hh"
 #include "globals.hh"
 #include "util.hh"
-#include "misc.hh"
 #include "worker-protocol.hh"
 
 
@@ -27,7 +26,49 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash
 }
 
 
-Path writeDerivation(StoreAPI & store,
+Path BasicDerivation::findOutput(const string & id) const
+{
+    auto i = outputs.find(id);
+    if (i == outputs.end())
+        throw Error(format("derivation has no output ‘%1%’") % id);
+    return i->second.path;
+}
+
+
+bool BasicDerivation::willBuildLocally() const
+{
+    return get(env, "preferLocalBuild") == "1" && canBuildLocally();
+}
+
+
+bool BasicDerivation::substitutesAllowed() const
+{
+    return get(env, "allowSubstitutes", "1") == "1";
+}
+
+
+bool BasicDerivation::isBuiltin() const
+{
+    return string(builder, 0, 8) == "builtin:";
+}
+
+
+bool BasicDerivation::canBuildLocally() const
+{
+    return platform == settings.thisSystem
+        || isBuiltin()
+#if __linux__
+        || (platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
+        || (platform == "armv6l-linux" && settings.thisSystem == "armv7l-linux")
+#elif __FreeBSD__
+        || (platform == "i686-linux" && settings.thisSystem == "x86_64-freebsd")
+        || (platform == "i686-linux" && settings.thisSystem == "i686-freebsd")
+#endif
+        ;
+}
+
+
+Path writeDerivation(ref<Store> store,
     const Derivation & drv, const string & name, bool repair)
 {
     PathSet references;
@@ -38,10 +79,10 @@ Path writeDerivation(StoreAPI & store,
        (that can be missing (of course) and should not necessarily be
        held during a garbage collection). */
     string suffix = name + drvExtension;
-    string contents = unparseDerivation(drv);
+    string contents = drv.unparse();
     return settings.readOnlyMode
         ? computeStorePathForText(suffix, contents, references)
-        : store.addTextToStore(suffix, contents, references, repair);
+        : store->addTextToStore(suffix, contents, references, repair);
 }
 
 
@@ -149,14 +190,14 @@ static void printStrings(string & res, ForwardIterator i, ForwardIterator j)
 }
 
 
-string unparseDerivation(const Derivation & drv)
+string Derivation::unparse() const
 {
     string s;
     s.reserve(65536);
     s += "Derive([";
 
     bool first = true;
-    for (auto & i : drv.outputs) {
+    for (auto & i : outputs) {
         if (first) first = false; else s += ',';
         s += '('; printString(s, i.first);
         s += ','; printString(s, i.second.path);
@@ -167,7 +208,7 @@ string unparseDerivation(const Derivation & drv)
 
     s += "],[";
     first = true;
-    for (auto & i : drv.inputDrvs) {
+    for (auto & i : inputDrvs) {
         if (first) first = false; else s += ',';
         s += '('; printString(s, i.first);
         s += ','; printStrings(s, i.second.begin(), i.second.end());
@@ -175,15 +216,15 @@ string unparseDerivation(const Derivation & drv)
     }
 
     s += "],";
-    printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end());
+    printStrings(s, inputSrcs.begin(), inputSrcs.end());
 
-    s += ','; printString(s, drv.platform);
-    s += ','; printString(s, drv.builder);
-    s += ','; printStrings(s, drv.args.begin(), drv.args.end());
+    s += ','; printString(s, platform);
+    s += ','; printString(s, builder);
+    s += ','; printStrings(s, args.begin(), args.end());
 
     s += ",[";
     first = true;
-    for (auto & i : drv.env) {
+    for (auto & i : env) {
         if (first) first = false; else s += ',';
         s += '('; printString(s, i.first);
         s += ','; printString(s, i.second);
@@ -202,11 +243,11 @@ bool isDerivation(const string & fileName)
 }
 
 
-bool isFixedOutputDrv(const Derivation & drv)
+bool BasicDerivation::isFixedOutput() const
 {
-    return drv.outputs.size() == 1 &&
-        drv.outputs.begin()->first == "out" &&
-        drv.outputs.begin()->second.hash != "";
+    return outputs.size() == 1 &&
+        outputs.begin()->first == "out" &&
+        outputs.begin()->second.hash != "";
 }
 
 
@@ -233,10 +274,10 @@ DrvHashes drvHashes;
    paths have been replaced by the result of a recursive call to this
    function, and that for fixed-output derivations we return a hash of
    its output path. */
-Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
+Hash hashDerivationModulo(Store & store, Derivation drv)
 {
     /* Return a fixed hash for fixed-output derivations. */
-    if (isFixedOutputDrv(drv)) {
+    if (drv.isFixedOutput()) {
         DerivationOutputs::const_iterator i = drv.outputs.begin();
         return hashString(htSHA256, "fixed:out:"
             + i->second.hashAlgo + ":"
@@ -259,7 +300,7 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
     }
     drv.inputDrvs = inputs2;
 
-    return hashString(htSHA256, unparseDerivation(drv));
+    return hashString(htSHA256, drv.unparse());
 }
 
 
@@ -286,10 +327,10 @@ bool wantOutput(const string & output, const std::set<string> & wanted)
 }
 
 
-PathSet outputPaths(const BasicDerivation & drv)
+PathSet BasicDerivation::outputPaths() const
 {
     PathSet paths;
-    for (auto & i : drv.outputs)
+    for (auto & i : outputs)
         paths.insert(i.second.path);
     return paths;
 }
diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh
index f0842045f86b..6f98869b0fe0 100644
--- a/src/libstore/derivations.hh
+++ b/src/libstore/derivations.hh
@@ -50,40 +50,56 @@ struct BasicDerivation
     StringPairs env;
 
     virtual ~BasicDerivation() { };
+
+    /* Return the path corresponding to the output identifier `id' in
+       the given derivation. */
+    Path findOutput(const string & id) const;
+
+    bool willBuildLocally() const;
+
+    bool substitutesAllowed() const;
+
+    bool isBuiltin() const;
+
+    bool canBuildLocally() const;
+
+    /* Return true iff this is a fixed-output derivation. */
+    bool isFixedOutput() const;
+
+    /* Return the output paths of a derivation. */
+    PathSet outputPaths() const;
+
 };
 
 struct Derivation : BasicDerivation
 {
     DerivationInputs inputDrvs; /* inputs that are sub-derivations */
+
+    /* Print a derivation. */
+    std::string unparse() const;
 };
 
 
-class StoreAPI;
+class Store;
 
 
 /* Write a derivation to the Nix store, and return its path. */
-Path writeDerivation(StoreAPI & store,
+Path writeDerivation(ref<Store> store,
     const Derivation & drv, const string & name, bool repair = false);
 
 /* Read a derivation from a file. */
 Derivation readDerivation(const Path & drvPath);
 
-/* Print a derivation. */
-string unparseDerivation(const Derivation & drv);
-
-/* Check whether a file name ends with the extensions for
+/* Check whether a file name ends with the extension for
    derivations. */
 bool isDerivation(const string & fileName);
 
-/* Return true iff this is a fixed-output derivation. */
-bool isFixedOutputDrv(const Derivation & drv);
-
-Hash hashDerivationModulo(StoreAPI & store, Derivation drv);
+Hash hashDerivationModulo(Store & store, Derivation drv);
 
 /* Memoisation of hashDerivationModulo(). */
 typedef std::map<Path, Hash> DrvHashes;
 
-extern DrvHashes drvHashes;
+extern DrvHashes drvHashes; // FIXME: global, not thread-safe
 
 /* Split a string specifying a derivation and a set of outputs
    (/nix/store/hash-foo!out1,out2,...) into the derivation path and
@@ -95,8 +111,6 @@ Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outpu
 
 bool wantOutput(const string & output, const std::set<string> & wanted);
 
-PathSet outputPaths(const BasicDerivation & drv);
-
 struct Source;
 struct Sink;
 
diff --git a/src/libstore/download.cc b/src/libstore/download.cc
index 822e9a8db867..01ce1ea2fd4c 100644
--- a/src/libstore/download.cc
+++ b/src/libstore/download.cc
@@ -188,7 +188,7 @@ DownloadResult downloadFile(string url, const DownloadOptions & options)
 }
 
 
-Path downloadFileCached(const string & url, bool unpack)
+Path downloadFileCached(ref<Store> store, const string & url, bool unpack)
 {
     Path cacheDir = getEnv("XDG_CACHE_HOME", getEnv("HOME", "") + "/.cache") + "/nix/tarballs";
     createDirs(cacheDir);
diff --git a/src/libstore/download.hh b/src/libstore/download.hh
index c1cb25b90c32..7aec8de73e48 100644
--- a/src/libstore/download.hh
+++ b/src/libstore/download.hh
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "types.hh"
+
 #include <string>
 
 namespace nix {
@@ -18,9 +19,11 @@ struct DownloadResult
     string data, etag;
 };
 
+class Store;
+
 DownloadResult downloadFile(string url, const DownloadOptions & options);
 
-Path downloadFileCached(const string & url, bool unpack);
+Path downloadFileCached(ref<Store> store, const string & url, bool unpack);
 
 MakeError(DownloadError, Error)
 
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 998a7516a13d..d19af1cefaf2 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -1,5 +1,5 @@
+#include "derivations.hh"
 #include "globals.hh"
-#include "misc.hh"
 #include "local-store.hh"
 
 #include <functional>
@@ -83,7 +83,7 @@ void LocalStore::addIndirectRoot(const Path & path)
 }
 
 
-Path addPermRoot(StoreAPI & store, const Path & _storePath,
+Path Store::addPermRoot(const Path & _storePath,
     const Path & _gcRoot, bool indirect, bool allowOutsideRootsDir)
 {
     Path storePath(canonPath(_storePath));
@@ -101,7 +101,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
         if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
             throw Error(format("cannot create symlink ‘%1%’; already exists") % gcRoot);
         makeSymlink(gcRoot, storePath);
-        store.addIndirectRoot(gcRoot);
+        addIndirectRoot(gcRoot);
     }
 
     else {
@@ -127,7 +127,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
        check if the root is in a directory in or linked from the
        gcroots directory. */
     if (settings.checkRootReachability) {
-        Roots roots = store.findRoots();
+        Roots roots = findRoots();
         if (roots.find(gcRoot) == roots.end())
             printMsg(lvlError,
                 format(
@@ -139,7 +139,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
     /* Grab the global GC root, causing us to block while a GC is in
        progress.  This prevents the set of permanent roots from
        increasing while a GC is in progress. */
-    store.syncWithGC();
+    syncWithGC();
 
     return gcRoot;
 }
@@ -260,19 +260,16 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
 }
 
 
-static void foundRoot(StoreAPI & store,
-    const Path & path, const Path & target, Roots & roots)
+void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
 {
-    Path storePath = toStorePath(target);
-    if (store.isValidPath(storePath))
-        roots[path] = storePath;
-    else
-        printMsg(lvlInfo, format("skipping invalid root from ‘%1%’ to ‘%2%’") % path % storePath);
-}
-
+    auto foundRoot = [&](const Path & path, const Path & target) {
+        Path storePath = toStorePath(target);
+        if (isValidPath(storePath))
+            roots[path] = storePath;
+        else
+            printMsg(lvlInfo, format("skipping invalid root from ‘%1%’ to ‘%2%’") % path % storePath);
+    };
 
-static void findRoots(StoreAPI & store, const Path & path, unsigned char type, Roots & roots)
-{
     try {
 
         if (type == DT_UNKNOWN)
@@ -280,13 +277,13 @@ static void findRoots(StoreAPI & store, const Path & path, unsigned char type, R
 
         if (type == DT_DIR) {
             for (auto & i : readDirectory(path))
-                findRoots(store, path + "/" + i.name, i.type, roots);
+                findRoots(path + "/" + i.name, i.type, roots);
         }
 
         else if (type == DT_LNK) {
             Path target = readLink(path);
             if (isInStore(target))
-                foundRoot(store, path, target, roots);
+                foundRoot(path, target);
 
             /* Handle indirect roots. */
             else {
@@ -300,14 +297,14 @@ static void findRoots(StoreAPI & store, const Path & path, unsigned char type, R
                     struct stat st2 = lstat(target);
                     if (!S_ISLNK(st2.st_mode)) return;
                     Path target2 = readLink(target);
-                    if (isInStore(target2)) foundRoot(store, target, target2, roots);
+                    if (isInStore(target2)) foundRoot(target, target2);
                 }
             }
         }
 
         else if (type == DT_REG) {
             Path storePath = settings.nixStore + "/" + baseNameOf(path);
-            if (store.isValidPath(storePath))
+            if (isValidPath(storePath))
                 roots[path] = storePath;
         }
 
@@ -328,16 +325,16 @@ Roots LocalStore::findRoots()
     Roots roots;
 
     /* Process direct roots in {gcroots,manifests,profiles}. */
-    nix::findRoots(*this, settings.nixStateDir + "/" + gcRootsDir, DT_UNKNOWN, roots);
+    findRoots(settings.nixStateDir + "/" + gcRootsDir, DT_UNKNOWN, roots);
     if (pathExists(settings.nixStateDir + "/manifests"))
-        nix::findRoots(*this, settings.nixStateDir + "/manifests", DT_UNKNOWN, roots);
-    nix::findRoots(*this, settings.nixStateDir + "/profiles", DT_UNKNOWN, roots);
+        findRoots(settings.nixStateDir + "/manifests", DT_UNKNOWN, roots);
+    findRoots(settings.nixStateDir + "/profiles", DT_UNKNOWN, roots);
 
     return roots;
 }
 
 
-static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
+void LocalStore::findRuntimeRoots(PathSet & roots)
 {
     Path rootFinder = getEnv("NIX_ROOT_FINDER",
         settings.nixLibexecDir + "/nix/find-runtime-roots.pl");
@@ -353,7 +350,7 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
     for (auto & i : paths)
         if (isInStore(i)) {
             Path path = toStorePath(i);
-            if (roots.find(path) == roots.end() && store.isValidPath(path)) {
+            if (roots.find(path) == roots.end() && isValidPath(path)) {
                 debug(format("got additional root ‘%1%’") % path);
                 roots.insert(path);
             }
@@ -628,7 +625,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
        to add running programs to the set of roots (to prevent them
        from being garbage collected). */
     if (!options.ignoreLiveness)
-        addAdditionalRoots(*this, state.roots);
+        findRuntimeRoots(state.roots);
 
     /* Read the temporary roots.  This acquires read locks on all
        per-process temporary root files.  So after this point no paths
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index d7cd0b088d98..13179459f1c1 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -397,9 +397,15 @@ int LocalStore::getSchema()
 }
 
 
+bool LocalStore::haveWriteAccess()
+{
+    return access(settings.nixDBPath.c_str(), R_OK | W_OK) == 0;
+}
+
+
 void LocalStore::openDB(bool create)
 {
-    if (access(settings.nixDBPath.c_str(), R_OK | W_OK))
+    if (!haveWriteAccess())
         throw SysError(format("Nix database directory ‘%1%’ is not writable") % settings.nixDBPath);
 
     /* Open the Nix database. */
@@ -602,10 +608,10 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
        users group); we check for this case below. */
     if (st.st_uid != geteuid()) {
 #if HAVE_LCHOWN
-        if (lchown(path.c_str(), geteuid(), (gid_t) -1) == -1)
+        if (lchown(path.c_str(), geteuid(), getegid()) == -1)
 #else
         if (!S_ISLNK(st.st_mode) &&
-            chown(path.c_str(), geteuid(), (gid_t) -1) == -1)
+            chown(path.c_str(), geteuid(), getegid()) == -1)
 #endif
             throw SysError(format("changing owner of ‘%1%’ to %2%")
                 % path % geteuid());
@@ -649,7 +655,7 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
     assert(isDerivation(drvName));
     drvName = string(drvName, 0, drvName.size() - drvExtension.size());
 
-    if (isFixedOutputDrv(drv)) {
+    if (drv.isFixedOutput()) {
         DerivationOutputs::const_iterator out = drv.outputs.find("out");
         if (out == drv.outputs.end())
             throw Error(format("derivation ‘%1%’ does not have an output named ‘out’") % drvPath);
@@ -1329,7 +1335,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
            error if a cycle is detected and roll back the
            transaction.  Cycles can only occur when a derivation
            has multiple outputs. */
-        topoSortPaths(*this, paths);
+        topoSortPaths(paths);
 
         txn.commit();
     } end_retry_sqlite;
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index ebdf19bf1359..b6d39d345c94 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -80,7 +80,7 @@ struct SQLiteStmt
 };
 
 
-class LocalStore : public StoreAPI
+class LocalStore : public Store
 {
 private:
     typedef std::map<Path, RunningSubstituter> RunningSubstituters;
@@ -253,6 +253,12 @@ private:
 
     int getSchema();
 
+public:
+
+    static bool haveWriteAccess();
+
+private:
+
     void openDB(bool create);
 
     void makeStoreWritable();
@@ -297,6 +303,10 @@ private:
 
     int openGCLock(LockType lockType);
 
+    void findRoots(const Path & path, unsigned char type, Roots & roots);
+
+    void findRuntimeRoots(PathSet & roots);
+
     void removeUnusedLinks(const GCState & state);
 
     void startSubstituter(const Path & substituter,
diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc
index 61a976c02f5c..12472f017ce4 100644
--- a/src/libstore/misc.cc
+++ b/src/libstore/misc.cc
@@ -1,21 +1,21 @@
-#include "misc.hh"
-#include "store-api.hh"
-#include "local-store.hh"
+#include "derivations.hh"
 #include "globals.hh"
+#include "local-store.hh"
+#include "store-api.hh"
 
 
 namespace nix {
 
 
-Derivation derivationFromPath(StoreAPI & store, const Path & drvPath)
+Derivation Store::derivationFromPath(const Path & drvPath)
 {
     assertStorePath(drvPath);
-    store.ensurePath(drvPath);
+    ensurePath(drvPath);
     return readDerivation(drvPath);
 }
 
 
-void computeFSClosure(StoreAPI & store, const Path & path,
+void Store::computeFSClosure(const Path & path,
     PathSet & paths, bool flipDirection, bool includeOutputs, bool includeDerivers)
 {
     if (paths.find(path) != paths.end()) return;
@@ -24,50 +24,42 @@ void computeFSClosure(StoreAPI & store, const Path & path,
     PathSet edges;
 
     if (flipDirection) {
-        store.queryReferrers(path, edges);
+        queryReferrers(path, edges);
 
         if (includeOutputs) {
-            PathSet derivers = store.queryValidDerivers(path);
+            PathSet derivers = queryValidDerivers(path);
             for (auto & i : derivers)
                 edges.insert(i);
         }
 
         if (includeDerivers && isDerivation(path)) {
-            PathSet outputs = store.queryDerivationOutputs(path);
+            PathSet outputs = queryDerivationOutputs(path);
             for (auto & i : outputs)
-                if (store.isValidPath(i) && store.queryDeriver(i) == path)
+                if (isValidPath(i) && queryDeriver(i) == path)
                     edges.insert(i);
         }
 
     } else {
-        store.queryReferences(path, edges);
+        queryReferences(path, edges);
 
         if (includeOutputs && isDerivation(path)) {
-            PathSet outputs = store.queryDerivationOutputs(path);
+            PathSet outputs = queryDerivationOutputs(path);
             for (auto & i : outputs)
-                if (store.isValidPath(i)) edges.insert(i);
+                if (isValidPath(i)) edges.insert(i);
         }
 
         if (includeDerivers) {
-            Path deriver = store.queryDeriver(path);
-            if (store.isValidPath(deriver)) edges.insert(deriver);
+            Path deriver = queryDeriver(path);
+            if (isValidPath(deriver)) edges.insert(deriver);
         }
     }
 
     for (auto & i : edges)
-        computeFSClosure(store, i, paths, flipDirection, includeOutputs, includeDerivers);
-}
-
-
-Path findOutput(const Derivation & drv, string id)
-{
-    for (auto & i : drv.outputs)
-        if (i.first == id) return i.second.path;
-    throw Error(format("derivation has no output ‘%1%’") % id);
+        computeFSClosure(i, paths, flipDirection, includeOutputs, includeDerivers);
 }
 
 
-void queryMissing(StoreAPI & store, const PathSet & targets,
+void Store::queryMissing(const PathSet & targets,
     PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
     unsigned long long & downloadSize, unsigned long long & narSize)
 {
@@ -105,27 +97,27 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
             DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i);
 
             if (isDerivation(i2.first)) {
-                if (!store.isValidPath(i2.first)) {
+                if (!isValidPath(i2.first)) {
                     // FIXME: we could try to substitute p.
                     unknown.insert(i);
                     continue;
                 }
-                Derivation drv = derivationFromPath(store, i2.first);
+                Derivation drv = derivationFromPath(i2.first);
 
                 PathSet invalid;
                 for (auto & j : drv.outputs)
                     if (wantOutput(j.first, i2.second)
-                        && !store.isValidPath(j.second.path))
+                        && !isValidPath(j.second.path))
                         invalid.insert(j.second.path);
                 if (invalid.empty()) continue;
 
                 todoDrv.insert(i);
-                if (settings.useSubstitutes && substitutesAllowed(drv))
+                if (settings.useSubstitutes && drv.substitutesAllowed())
                     query.insert(invalid.begin(), invalid.end());
             }
 
             else {
-                if (store.isValidPath(i)) continue;
+                if (isValidPath(i)) continue;
                 query.insert(i);
                 todoNonDrv.insert(i);
             }
@@ -134,20 +126,20 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
         todo.clear();
 
         SubstitutablePathInfos infos;
-        store.querySubstitutablePathInfos(query, infos);
+        querySubstitutablePathInfos(query, infos);
 
         for (auto & i : todoDrv) {
             DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i);
 
             // FIXME: cache this
-            Derivation drv = derivationFromPath(store, i2.first);
+            Derivation drv = derivationFromPath(i2.first);
 
             PathSet outputs;
             bool mustBuild = false;
-            if (settings.useSubstitutes && substitutesAllowed(drv)) {
+            if (settings.useSubstitutes && drv.substitutesAllowed()) {
                 for (auto & j : drv.outputs) {
                     if (!wantOutput(j.first, i2.second)) continue;
-                    if (!store.isValidPath(j.second.path)) {
+                    if (!isValidPath(j.second.path)) {
                         if (infos.find(j.second.path) == infos.end())
                             mustBuild = true;
                         else
@@ -181,38 +173,38 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
 }
 
 
-static void dfsVisit(StoreAPI & store, const PathSet & paths,
-    const Path & path, PathSet & visited, Paths & sorted,
-    PathSet & parents)
+Paths Store::topoSortPaths(const PathSet & paths)
 {
-    if (parents.find(path) != parents.end())
-        throw BuildError(format("cycle detected in the references of ‘%1%’") % path);
+    Paths sorted;
+    PathSet visited, parents;
 
-    if (visited.find(path) != visited.end()) return;
-    visited.insert(path);
-    parents.insert(path);
+    std::function<void(const Path & path)> dfsVisit;
 
-    PathSet references;
-    if (store.isValidPath(path))
-        store.queryReferences(path, references);
+    dfsVisit = [&](const Path & path) {
+        if (parents.find(path) != parents.end())
+            throw BuildError(format("cycle detected in the references of ‘%1%’") % path);
 
-    for (auto & i : references)
-        /* Don't traverse into paths that don't exist.  That can
-           happen due to substitutes for non-existent paths. */
-        if (i != path && paths.find(i) != paths.end())
-            dfsVisit(store, paths, i, visited, sorted, parents);
+        if (visited.find(path) != visited.end()) return;
+        visited.insert(path);
+        parents.insert(path);
 
-    sorted.push_front(path);
-    parents.erase(path);
-}
+        PathSet references;
+        if (isValidPath(path))
+            queryReferences(path, references);
 
+        for (auto & i : references)
+            /* Don't traverse into paths that don't exist.  That can
+               happen due to substitutes for non-existent paths. */
+            if (i != path && paths.find(i) != paths.end())
+                dfsVisit(i);
+
+        sorted.push_front(path);
+        parents.erase(path);
+    };
 
-Paths topoSortPaths(StoreAPI & store, const PathSet & paths)
-{
-    Paths sorted;
-    PathSet visited, parents;
     for (auto & i : paths)
-        dfsVisit(store, paths, i, visited, sorted, parents);
+        dfsVisit(i);
+
     return sorted;
 }
 
diff --git a/src/libstore/misc.hh b/src/libstore/misc.hh
deleted file mode 100644
index 495c528750fb..000000000000
--- a/src/libstore/misc.hh
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-
-#include "derivations.hh"
-
-
-namespace nix {
-
-
-/* Read a derivation, after ensuring its existence through
-   ensurePath(). */
-Derivation derivationFromPath(StoreAPI & store, const Path & drvPath);
-
-/* Place in `paths' the set of all store paths in the file system
-   closure of `storePath'; that is, all paths than can be directly or
-   indirectly reached from it.  `paths' is not cleared.  If
-   `flipDirection' is true, the set of paths that can reach
-   `storePath' is returned; that is, the closures under the
-   `referrers' relation instead of the `references' relation is
-   returned. */
-void computeFSClosure(StoreAPI & store, const Path & path,
-    PathSet & paths, bool flipDirection = false,
-    bool includeOutputs = false, bool includeDerivers = false);
-
-/* Return the path corresponding to the output identifier `id' in the
-   given derivation. */
-Path findOutput(const Derivation & drv, string id);
-
-/* Given a set of paths that are to be built, return the set of
-   derivations that will be built, and the set of output paths that
-   will be substituted. */
-void queryMissing(StoreAPI & store, const PathSet & targets,
-    PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
-    unsigned long long & downloadSize, unsigned long long & narSize);
-
-bool willBuildLocally(const BasicDerivation & drv);
-
-bool substitutesAllowed(const BasicDerivation & drv);
-
-
-}
diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc
index 1c87034f8e1a..eddf5bcbda65 100644
--- a/src/libstore/pathlocks.cc
+++ b/src/libstore/pathlocks.cc
@@ -161,7 +161,11 @@ bool PathLocks::lockPaths(const PathSet & _paths,
 
 PathLocks::~PathLocks()
 {
-    unlock();
+    try {
+        unlock();
+    } catch (...) {
+        ignoreException();
+    }
 }
 
 
diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc
index da3f7da9d19d..cc83a838eddc 100644
--- a/src/libstore/profiles.cc
+++ b/src/libstore/profiles.cc
@@ -74,7 +74,7 @@ static void makeName(const Path & profile, unsigned int num,
 }
 
 
-Path createGeneration(Path profile, Path outPath)
+Path createGeneration(ref<Store> store, Path profile, Path outPath)
 {
     /* The new generation number should be higher than old the
        previous ones. */
@@ -108,7 +108,7 @@ Path createGeneration(Path profile, Path outPath)
        user environment etc. we've just built. */
     Path generation;
     makeName(profile, num + 1, generation);
-    addPermRoot(*store, outPath, generation, false, true);
+    store->addPermRoot(outPath, generation, false, true);
 
     return generation;
 }
diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh
index e99bbf398a86..d758d94b603c 100644
--- a/src/libstore/profiles.hh
+++ b/src/libstore/profiles.hh
@@ -31,7 +31,9 @@ typedef list<Generation> Generations;
    profile, sorted by generation number. */
 Generations findGenerations(Path profile, int & curGen);
 
-Path createGeneration(Path profile, Path outPath);
+class Store;
+
+Path createGeneration(ref<Store> store, Path profile, Path outPath);
 
 void deleteGeneration(const Path & profile, unsigned int gen);
 
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 262e4650bfb5..679210d4cb16 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -50,14 +50,8 @@ void RemoteStore::openConnection(bool reserveSpace)
     if (initialised) return;
     initialised = true;
 
-    string remoteMode = getEnv("NIX_REMOTE");
-
-    if (remoteMode == "daemon")
-        /* Connect to a daemon that does the privileged work for
-           us. */
-        connectToDaemon();
-    else
-        throw Error(format("invalid setting for NIX_REMOTE, ‘%1%’") % remoteMode);
+    /* Connect to a daemon that does the privileged work for us. */
+    connectToDaemon();
 
     from.fd = fdSocket;
     to.fd = fdSocket;
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index b0787c072904..f15182285e8c 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -14,7 +14,7 @@ struct FdSink;
 struct FdSource;
 
 
-class RemoteStore : public StoreAPI
+class RemoteStore : public Store
 {
 public:
 
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index a73ebd824264..039d07e29b3e 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -220,7 +220,7 @@ Path computeStorePathForText(const string & name, const string & s,
 /* Return a string accepted by decodeValidPathInfo() that
    registers the specified paths as valid.  Note: it's the
    responsibility of the caller to provide a closure. */
-string StoreAPI::makeValidityRegistration(const PathSet & paths,
+string Store::makeValidityRegistration(const PathSet & paths,
     bool showDerivers, bool showHash)
 {
     string s = "";
@@ -284,12 +284,12 @@ string showPaths(const PathSet & paths)
 }
 
 
-void exportPaths(StoreAPI & store, const Paths & paths,
+void Store::exportPaths(const Paths & paths,
     bool sign, Sink & sink)
 {
     for (auto & i : paths) {
         sink << 1;
-        store.exportPath(i, sign, sink);
+        exportPath(i, sign, sink);
     }
     sink << 0;
 }
@@ -306,15 +306,24 @@ void exportPaths(StoreAPI & store, const Paths & paths,
 namespace nix {
 
 
-std::shared_ptr<StoreAPI> store;
+ref<Store> openStore(bool reserveSpace)
+{
+    enum { mDaemon, mLocal, mAuto } mode;
 
+    mode = getEnv("NIX_REMOTE") == "daemon" ? mDaemon : mAuto;
 
-std::shared_ptr<StoreAPI> openStore(bool reserveSpace)
-{
-    if (getEnv("NIX_REMOTE") == "")
-        return std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
-    else
-        return std::shared_ptr<StoreAPI>(new RemoteStore());
+    if (mode == mAuto) {
+        if (LocalStore::haveWriteAccess())
+            mode = mLocal;
+        else if (pathExists(settings.nixDaemonSocketFile))
+            mode = mDaemon;
+        else
+            mode = mLocal;
+    }
+
+    return mode == mDaemon
+        ? (ref<Store>) make_ref<RemoteStore>()
+        : (ref<Store>) make_ref<LocalStore>(reserveSpace);
 }
 
 
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 9cc5fd45b7c4..888ef3e2a083 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -103,7 +103,7 @@ struct ValidPathInfo
 typedef list<ValidPathInfo> ValidPathInfos;
 
 
-enum BuildMode { bmNormal, bmRepair, bmCheck };
+enum BuildMode { bmNormal, bmRepair, bmCheck, bmHash };
 
 
 struct BuildResult
@@ -132,13 +132,14 @@ struct BuildResult
 
 
 struct BasicDerivation;
+struct Derivation;
 
 
-class StoreAPI
+class Store
 {
 public:
 
-    virtual ~StoreAPI() { }
+    virtual ~Store() { }
 
     /* Check whether a path is valid. */
     virtual bool isValidPath(const Path & path) = 0;
@@ -214,6 +215,10 @@ public:
     virtual void exportPath(const Path & path, bool sign,
         Sink & sink) = 0;
 
+    /* Export multiple paths in the format expected by ‘nix-store
+       --import’. */
+    void exportPaths(const Paths & paths, bool sign, Sink & sink);
+
     /* Import a sequence of NAR dumps created by exportPaths() into
        the Nix store. */
     virtual Paths importPaths(bool requireSignature, Source & source) = 0;
@@ -250,6 +255,10 @@ public:
        `path' has disappeared. */
     virtual void addIndirectRoot(const Path & path) = 0;
 
+    /* Register a permanent GC root. */
+    Path addPermRoot(const Path & storePath,
+        const Path & gcRoot, bool indirect, bool allowOutsideRootsDir = false);
+
     /* Acquire the global GC lock, then immediately release it.  This
        function must be called after registering a new permanent root,
        but before exiting.  Otherwise, it is possible that a running
@@ -298,6 +307,35 @@ public:
     /* Check the integrity of the Nix store.  Returns true if errors
        remain. */
     virtual bool verifyStore(bool checkContents, bool repair) = 0;
+
+    /* Utility functions. */
+
+    /* Read a derivation, after ensuring its existence through
+       ensurePath(). */
+    Derivation derivationFromPath(const Path & drvPath);
+
+    /* Place in `paths' the set of all store paths in the file system
+       closure of `storePath'; that is, all paths than can be directly
+       or indirectly reached from it.  `paths' is not cleared.  If
+       `flipDirection' is true, the set of paths that can reach
+       `storePath' is returned; that is, the closures under the
+       `referrers' relation instead of the `references' relation is
+       returned. */
+    void computeFSClosure(const Path & path,
+        PathSet & paths, bool flipDirection = false,
+        bool includeOutputs = false, bool includeDerivers = false);
+
+    /* Given a set of paths that are to be built, return the set of
+       derivations that will be built, and the set of output paths
+       that will be substituted. */
+    void queryMissing(const PathSet & targets,
+        PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
+        unsigned long long & downloadSize, unsigned long long & narSize);
+
+    /* Sort a set of paths topologically under the references
+       relation.  If p refers to q, then p preceeds q in this list. */
+    Paths topoSortPaths(const PathSet & paths);
+
 };
 
 
@@ -372,24 +410,9 @@ Path computeStorePathForText(const string & name, const string & s,
 void removeTempRoots();
 
 
-/* Register a permanent GC root. */
-Path addPermRoot(StoreAPI & store, const Path & storePath,
-    const Path & gcRoot, bool indirect, bool allowOutsideRootsDir = false);
-
-
-/* Sort a set of paths topologically under the references relation.
-   If p refers to q, then p preceeds q in this list. */
-Paths topoSortPaths(StoreAPI & store, const PathSet & paths);
-
-
-/* For now, there is a single global store API object, but we'll
-   purify that in the future. */
-extern std::shared_ptr<StoreAPI> store;
-
-
 /* Factory method: open the Nix database, either through the local or
    remote implementation. */
-std::shared_ptr<StoreAPI> openStore(bool reserveSpace = true);
+ref<Store> openStore(bool reserveSpace = true);
 
 
 /* Display a set of paths in human-readable form (i.e., between quotes
@@ -401,12 +424,6 @@ ValidPathInfo decodeValidPathInfo(std::istream & str,
     bool hashGiven = false);
 
 
-/* Export multiple paths in the format expected by ‘nix-store
-   --import’. */
-void exportPaths(StoreAPI & store, const Paths & paths,
-    bool sign, Sink & sink);
-
-
 MakeError(SubstError, Error)
 MakeError(BuildError, Error) /* denotes a permanent build failure */
 
diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc
index 446fcb781564..fb4160669a29 100644
--- a/src/libutil/compression.cc
+++ b/src/libutil/compression.cc
@@ -2,6 +2,7 @@
 #include "types.hh"
 
 #include <lzma.h>
+#include <cstdio>
 
 namespace nix {
 
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index 2d97c5e6b6a7..64739300302b 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -96,19 +96,13 @@ Hash parseHash(HashType ht, const string & s)
 }
 
 
-unsigned int hashLength32(const Hash & hash)
-{
-    return (hash.hashSize * 8 - 1) / 5 + 1;
-}
-
-
 // omitted: E O U T
 const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";
 
 
 string printHash32(const Hash & hash)
 {
-    unsigned int len = hashLength32(hash);
+    size_t len = hash.base32Len();
 
     string s;
     s.reserve(len);
@@ -136,7 +130,7 @@ string printHash16or32(const Hash & hash)
 Hash parseHash32(HashType ht, const string & s)
 {
     Hash hash(ht);
-    unsigned int len = hashLength32(ht);
+    size_t len = hash.base32Len();
     assert(s.size() == len);
 
     for (unsigned int n = 0; n < len; ++n) {
@@ -163,7 +157,7 @@ Hash parseHash16or32(HashType ht, const string & s)
     if (s.size() == hash.hashSize * 2)
         /* hexadecimal representation */
         hash = parseHash(ht, s);
-    else if (s.size() == hashLength32(hash))
+    else if (s.size() == hash.base32Len())
         /* base-32 representation */
         hash = parseHash32(ht, s);
     else
diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh
index 841b4cb2936c..bac2ebf2dcfa 100644
--- a/src/libutil/hash.hh
+++ b/src/libutil/hash.hh
@@ -7,7 +7,7 @@
 namespace nix {
 
 
-typedef enum { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 } HashType;
+enum HashType : char { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 };
 
 
 const int md5HashSize = 16;
@@ -40,6 +40,18 @@ struct Hash
 
     /* For sorting. */
     bool operator < (const Hash & h) const;
+
+    /* Returns the length of a base-16 representation of this hash. */
+    size_t base16Len() const
+    {
+        return hashSize * 2;
+    }
+
+    /* Returns the length of a base-32 representation of this hash. */
+    size_t base32Len() const
+    {
+        return (hashSize * 8 - 1) / 5 + 1;
+    }
 };
 
 
@@ -49,9 +61,6 @@ string printHash(const Hash & hash);
 /* Parse a hexadecimal representation of a hash code. */
 Hash parseHash(HashType ht, const string & s);
 
-/* Returns the length of a base-32 hash representation. */
-unsigned int hashLength32(const Hash & hash);
-
 /* Convert a hash to a base-32 representation. */
 string printHash32(const Hash & hash);
 
diff --git a/src/libutil/types.hh b/src/libutil/types.hh
index 160884ee1ad7..0eae46c5fe93 100644
--- a/src/libutil/types.hh
+++ b/src/libutil/types.hh
@@ -5,6 +5,7 @@
 #include <string>
 #include <list>
 #include <set>
+#include <memory>
 
 #include <boost/format.hpp>
 
@@ -96,4 +97,63 @@ typedef enum {
 } Verbosity;
 
 
+/* A simple non-nullable reference-counted pointer. Actually a wrapper
+   around std::shared_ptr that prevents non-null constructions. */
+template<typename T>
+class ref
+{
+private:
+
+    std::shared_ptr<T> p;
+
+public:
+
+    ref<T>(const ref<T> & r)
+        : p(r.p)
+    { }
+
+    explicit ref<T>(const std::shared_ptr<T> & p)
+        : p(p)
+    {
+        if (!p)
+            throw std::invalid_argument("null pointer cast to ref");
+    }
+
+    T* operator ->() const
+    {
+        return &*p;
+    }
+
+    T& operator *() const
+    {
+        return *p;
+    }
+
+    operator std::shared_ptr<T> ()
+    {
+        return p;
+    }
+
+    template<typename T2>
+    operator ref<T2> ()
+    {
+        return ref<T2>((std::shared_ptr<T2>) p);
+    }
+
+private:
+
+    template<typename T2, typename... Args>
+    friend ref<T2>
+    make_ref(Args&&... args);
+
+};
+
+template<typename T, typename... Args>
+inline ref<T>
+make_ref(Args&&... args)
+{
+    auto p = std::make_shared<T>(std::forward<Args>(args)...);
+    return ref<T>(p);
+}
+
 }
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 75032bf90d0b..def0525abc18 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -150,7 +150,7 @@ Path dirOf(const Path & path)
 string baseNameOf(const Path & path)
 {
     if (path.empty())
-        return string("");
+        return "";
 
     Path::size_type last = path.length() - 1;
     if (path[last] == '/' && last > 0)
@@ -161,6 +161,7 @@ string baseNameOf(const Path & path)
         pos = 0;
     else
         pos += 1;
+
     return string(path, pos, last - pos + 1);
 }
 
@@ -232,7 +233,13 @@ DirEntries readDirectory(const Path & path)
         checkInterrupt();
         string name = dirent->d_name;
         if (name == "." || name == "..") continue;
-        entries.emplace_back(name, dirent->d_ino, dirent->d_type);
+        entries.emplace_back(name, dirent->d_ino,
+#ifdef HAVE_STRUCT_DIRENT_D_TYPE
+            dirent->d_type
+#else
+            DT_UNKNOWN
+#endif
+        );
     }
     if (errno) throw SysError(format("reading directory ‘%1%’") % path);
 
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 7ebfd7cee0e9..9eebb67fdf3a 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -8,9 +8,15 @@
 #include <unistd.h>
 #include <signal.h>
 #include <functional>
-
+#include <limits>
 #include <cstdio>
 
+#ifndef HAVE_STRUCT_DIRENT_D_TYPE
+#define DT_UNKNOWN 0
+#define DT_REG 1
+#define DT_LNK 2
+#define DT_DIR 3
+#endif
 
 namespace nix {
 
@@ -353,6 +359,8 @@ bool statusOk(int status);
 /* Parse a string into an integer. */
 template<class N> bool string2Int(const string & s, N & n)
 {
+    if (string(s, 0, 1) == "-" && !std::numeric_limits<N>::is_signed)
+        return false;
     std::istringstream str(s);
     str >> n;
     return str && str.get() == EOF;
@@ -413,4 +421,14 @@ string base64Encode(const string & s);
 string base64Decode(const string & s);
 
 
+/* Get a value for the specified key from an associate container, or a
+   default value if the key doesn't exist. */
+template <class T>
+string get(const T & map, const string & key, const string & def = "")
+{
+    auto i = map.find(key);
+    return i == map.end() ? def : i->second;
+}
+
+
 }
diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc
index 5a72cb712014..b9ccafb751c1 100644
--- a/src/nix-collect-garbage/nix-collect-garbage.cc
+++ b/src/nix-collect-garbage/nix-collect-garbage.cc
@@ -82,7 +82,7 @@ int main(int argc, char * * argv)
 
         // Run the actual garbage collector.
         if (!dryRun) {
-            store = openStore(false);
+            auto store = openStore(false);
             options.action = GCOptions::gcDeleteDead;
             GCResults results;
             PrintFreed freed(true, results);
diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc
index c5e11afa1553..aa20aa67daa8 100644
--- a/src/nix-daemon/nix-daemon.cc
+++ b/src/nix-daemon/nix-daemon.cc
@@ -149,7 +149,7 @@ struct SavingSourceAdapter : Source
 };
 
 
-static void performOp(bool trusted, unsigned int clientVersion,
+static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVersion,
     Source & from, Sink & to, unsigned int op)
 {
     switch (op) {
@@ -278,8 +278,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
 
         startWork();
         if (!savedRegular.regular) throw Error("regular file expected");
-        Path path = dynamic_cast<LocalStore *>(store.get())
-            ->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
+        Path path = store->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
         stopWork();
 
         to << path;
@@ -583,56 +582,56 @@ static void processConnection(bool trusted)
 #endif
 
         /* Open the store. */
-        store = std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
+        auto store = make_ref<LocalStore>(reserveSpace);
 
         stopWork();
         to.flush();
 
-    } catch (Error & e) {
-        stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
-        to.flush();
-        return;
-    }
+        /* Process client requests. */
+        unsigned int opCount = 0;
+
+        while (true) {
+            WorkerOp op;
+            try {
+                op = (WorkerOp) readInt(from);
+            } catch (Interrupted & e) {
+                break;
+            } catch (EndOfFile & e) {
+                break;
+            }
 
-    /* Process client requests. */
-    unsigned int opCount = 0;
+            opCount++;
+
+            try {
+                performOp(store, trusted, clientVersion, from, to, op);
+            } catch (Error & e) {
+                /* If we're not in a state where we can send replies, then
+                   something went wrong processing the input of the
+                   client.  This can happen especially if I/O errors occur
+                   during addTextToStore() / importPath().  If that
+                   happens, just send the error message and exit. */
+                bool errorAllowed = canSendStderr;
+                stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
+                if (!errorAllowed) throw;
+            } catch (std::bad_alloc & e) {
+                stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
+                throw;
+            }
 
-    while (true) {
-        WorkerOp op;
-        try {
-            op = (WorkerOp) readInt(from);
-        } catch (Interrupted & e) {
-            break;
-        } catch (EndOfFile & e) {
-            break;
-        }
+            to.flush();
 
-        opCount++;
+            assert(!canSendStderr);
+        };
 
-        try {
-            performOp(trusted, clientVersion, from, to, op);
-        } catch (Error & e) {
-            /* If we're not in a state where we can send replies, then
-               something went wrong processing the input of the
-               client.  This can happen especially if I/O errors occur
-               during addTextToStore() / importPath().  If that
-               happens, just send the error message and exit. */
-            bool errorAllowed = canSendStderr;
-            stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
-            if (!errorAllowed) throw;
-        } catch (std::bad_alloc & e) {
-            stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
-            throw;
-        }
+        canSendStderr = false;
+        _isInterrupted = false;
+        printMsg(lvlDebug, format("%1% operations") % opCount);
 
+    } catch (Error & e) {
+        stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
         to.flush();
-
-        assert(!canSendStderr);
-    };
-
-    canSendStderr = false;
-    _isInterrupted = false;
-    printMsg(lvlDebug, format("%1% operations") % opCount);
+        return;
+    }
 }
 
 
@@ -787,10 +786,6 @@ static void daemonLoop(char * * argv)
     while (1) {
 
         try {
-            /* Important: the server process *cannot* open the SQLite
-               database, because it doesn't like forks very much. */
-            assert(!store);
-
             /* Accept a connection. */
             struct sockaddr_un remoteAddr;
             socklen_t remoteAddrLen = sizeof(remoteAddr);
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index dcfb8f35324a..a9d1ed024dd3 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -1,17 +1,17 @@
-#include "profiles.hh"
-#include "names.hh"
-#include "globals.hh"
-#include "misc.hh"
-#include "shared.hh"
-#include "eval.hh"
-#include "get-drvs.hh"
 #include "attr-path.hh"
 #include "common-opts.hh"
-#include "xml-writer.hh"
+#include "derivations.hh"
+#include "eval.hh"
+#include "get-drvs.hh"
+#include "globals.hh"
+#include "names.hh"
+#include "profiles.hh"
+#include "shared.hh"
 #include "store-api.hh"
 #include "user-env.hh"
 #include "util.hh"
 #include "value-to-json.hh"
+#include "xml-writer.hh"
 
 #include <cerrno>
 #include <ctime>
@@ -223,8 +223,8 @@ static int comparePriorities(EvalState & state, DrvInfo & drv1, DrvInfo & drv2)
 static bool isPrebuilt(EvalState & state, DrvInfo & elem)
 {
     Path path = elem.queryOutPath();
-    if (store->isValidPath(path)) return true;
-    PathSet ps = store->querySubstitutablePaths(singleton<PathSet>(path));
+    if (state.store->isValidPath(path)) return true;
+    PathSet ps = state.store->querySubstitutablePaths(singleton<PathSet>(path));
     return ps.find(path) != ps.end();
 }
 
@@ -398,7 +398,7 @@ static void queryInstSources(EvalState & state,
 
                 if (isDerivation(path)) {
                     elem.setDrvPath(path);
-                    elem.setOutPath(findOutput(derivationFromPath(*store, path), "out"));
+                    elem.setOutPath(state.store->derivationFromPath(path).findOutput("out"));
                     if (name.size() >= drvExtension.size() &&
                         string(name, name.size() - drvExtension.size()) == drvExtension)
                         name = string(name, 0, name.size() - drvExtension.size());
@@ -445,7 +445,7 @@ static void printMissing(EvalState & state, DrvInfos & elems)
             targets.insert(i.queryOutPath());
     }
 
-    printMissing(*store, targets);
+    printMissing(state.store, targets);
 }
 
 
@@ -711,18 +711,18 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
 
     if (drv.queryDrvPath() != "") {
         PathSet paths = singleton<PathSet>(drv.queryDrvPath());
-        printMissing(*store, paths);
+        printMissing(globals.state->store, paths);
         if (globals.dryRun) return;
-        store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal);
+        globals.state->store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal);
     }
     else {
-        printMissing(*store, singleton<PathSet>(drv.queryOutPath()));
+        printMissing(globals.state->store, singleton<PathSet>(drv.queryOutPath()));
         if (globals.dryRun) return;
-        store->ensurePath(drv.queryOutPath());
+        globals.state->store->ensurePath(drv.queryOutPath());
     }
 
     debug(format("switching to new user environment"));
-    Path generation = createGeneration(globals.profile, drv.queryOutPath());
+    Path generation = createGeneration(globals.state->store, globals.profile, drv.queryOutPath());
     switchLink(globals.profile, generation);
 }
 
@@ -973,8 +973,8 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
                 printMsg(lvlTalkative, format("skipping derivation named ‘%1%’ which gives an assertion failure") % i.name);
                 i.setFailed();
             }
-        validPaths = store->queryValidPaths(paths);
-        substitutablePaths = store->querySubstitutablePaths(paths);
+        validPaths = globals.state->store->queryValidPaths(paths);
+        substitutablePaths = globals.state->store->querySubstitutablePaths(paths);
     }
 
 
@@ -1398,9 +1398,9 @@ int main(int argc, char * * argv)
 
         if (!op) throw UsageError("no operation specified");
 
-        store = openStore();
+        auto store = openStore();
 
-        globals.state = std::shared_ptr<EvalState>(new EvalState(searchPath));
+        globals.state = std::shared_ptr<EvalState>(new EvalState(searchPath, store));
         globals.state->repair = repair;
 
         if (file != "")
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc
index 9a20b94334f2..4e0e28c1158c 100644
--- a/src/nix-env/user-env.cc
+++ b/src/nix-env/user-env.cc
@@ -38,7 +38,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
             drvsToBuild.insert(i.queryDrvPath());
 
     debug(format("building user environment dependencies"));
-    store->buildPaths(drvsToBuild, state.repair ? bmRepair : bmNormal);
+    state.store->buildPaths(drvsToBuild, state.repair ? bmRepair : bmNormal);
 
     /* Construct the whole top level derivation. */
     PathSet references;
@@ -76,8 +76,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
 
             /* This is only necessary when installing store paths, e.g.,
                `nix-env -i /nix/store/abcd...-foo'. */
-            store->addTempRoot(j.second);
-            store->ensurePath(j.second);
+            state.store->addTempRoot(j.second);
+            state.store->ensurePath(j.second);
 
             references.insert(j.second);
         }
@@ -100,7 +100,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
     /* Also write a copy of the list of user environment elements to
        the store; we need it for future modifications of the
        environment. */
-    Path manifestFile = store->addTextToStore("env-manifest.nix",
+    Path manifestFile = state.store->addTextToStore("env-manifest.nix",
         (format("%1%") % manifest).str(), references);
 
     /* Get the environment builder expression. */
@@ -128,7 +128,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
 
     /* Realise the resulting store expression. */
     debug("building user environment");
-    store->buildPaths(singleton<PathSet>(topLevelDrv), state.repair ? bmRepair : bmNormal);
+    state.store->buildPaths(singleton<PathSet>(topLevelDrv), state.repair ? bmRepair : bmNormal);
 
     /* Switch the current user environment to the output path. */
     PathLocks lock;
@@ -141,7 +141,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
     }
 
     debug(format("switching to new user environment"));
-    Path generation = createGeneration(profile, topLevelOut);
+    Path generation = createGeneration(state.store, profile, topLevelOut);
     switchLink(profile, generation);
 
     return true;
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index 13a145a3b53e..81c1c8d5637c 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -9,7 +9,6 @@
 #include "util.hh"
 #include "store-api.hh"
 #include "common-opts.hh"
-#include "misc.hh"
 
 #include <map>
 #include <iostream>
@@ -80,7 +79,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
                 else {
                     Path rootName = gcRoot;
                     if (++rootNr > 1) rootName += "-" + std::to_string(rootNr);
-                    drvPath = addPermRoot(*store, drvPath, rootName, indirectRoot);
+                    drvPath = state.store->addPermRoot(drvPath, rootName, indirectRoot);
                 }
                 std::cout << format("%1%%2%\n") % drvPath % (outputName != "out" ? "!" + outputName : "");
             }
@@ -158,9 +157,9 @@ int main(int argc, char * * argv)
         if (evalOnly && !wantsReadWrite)
             settings.readOnlyMode = true;
 
-        store = openStore();
+        auto store = openStore();
 
-        EvalState state(searchPath);
+        EvalState state(searchPath, store);
         state.repair = repair;
 
         Bindings & autoArgs(*evalAutoArgs(state, autoArgs_));
diff --git a/src/nix-prefetch-url/nix-prefetch-url.cc b/src/nix-prefetch-url/nix-prefetch-url.cc
index 73a2845e07a5..c0c05a60bd78 100644
--- a/src/nix-prefetch-url/nix-prefetch-url.cc
+++ b/src/nix-prefetch-url/nix-prefetch-url.cc
@@ -91,8 +91,8 @@ int main(int argc, char * * argv)
         if (args.size() > 2)
             throw UsageError("too many arguments");
 
-        store = openStore();
-        EvalState state(searchPath);
+        auto store = openStore();
+        EvalState state(searchPath, store);
 
         Bindings & autoArgs(*evalAutoArgs(state, autoArgs_));
 
diff --git a/src/nix-store/dotgraph.cc b/src/nix-store/dotgraph.cc
index a333d7351010..8735cf9b667b 100644
--- a/src/nix-store/dotgraph.cc
+++ b/src/nix-store/dotgraph.cc
@@ -20,8 +20,8 @@ static string nextColour()
 {
     static int n = 0;
     static string colours[] =
-	{ "black", "red", "green", "blue"
-	, "magenta", "burlywood" };
+        { "black", "red", "green", "blue"
+        , "magenta", "burlywood" };
     return colours[n++ % (sizeof(colours) / sizeof(string))];
 }
 
@@ -29,7 +29,7 @@ static string nextColour()
 static string makeEdge(const string & src, const string & dst)
 {
     format f = format("%1% -> %2% [color = %3%];\n")
-	% dotQuote(src) % dotQuote(dst) % dotQuote(nextColour());
+        % dotQuote(src) % dotQuote(dst) % dotQuote(nextColour());
     return f.str();
 }
 
@@ -38,8 +38,8 @@ static string makeNode(const string & id, const string & label,
     const string & colour)
 {
     format f = format("%1% [label = %2%, shape = box, "
-	"style = filled, fillcolor = %3%];\n")
-	% dotQuote(id) % dotQuote(label) % dotQuote(colour);
+        "style = filled, fillcolor = %3%];\n")
+        % dotQuote(id) % dotQuote(label) % dotQuote(colour);
     return f.str();
 }
 
@@ -65,51 +65,51 @@ void printClosure(const Path & nePath, const StoreExpr & fs)
     PathSet doneSet;
 
     for (PathSet::iterator i = workList.begin(); i != workList.end(); ++i) {
-	cout << makeEdge(pathLabel(nePath, *i), nePath);
+        cout << makeEdge(pathLabel(nePath, *i), nePath);
     }
 
     while (!workList.empty()) {
-	Path path = *(workList.begin());
-	workList.erase(path);
-
-	if (doneSet.find(path) == doneSet.end()) {
-	    doneSet.insert(path);
-
-	    ClosureElems::const_iterator elem = fs.closure.elems.find(path);
-	    if (elem == fs.closure.elems.end())
-		throw Error(format("bad closure, missing path ‘%1%’") % path);
-
-	    for (StringSet::const_iterator i = elem->second.refs.begin();
-		 i != elem->second.refs.end(); ++i)
-	    {
-		workList.insert(*i);
-		cout << makeEdge(pathLabel(nePath, *i), pathLabel(nePath, path));
-	    }
-
-	    cout << makeNode(pathLabel(nePath, path), 
-		symbolicName(path), "#ff0000");
-	}
+        Path path = *(workList.begin());
+        workList.erase(path);
+
+        if (doneSet.find(path) == doneSet.end()) {
+            doneSet.insert(path);
+
+            ClosureElems::const_iterator elem = fs.closure.elems.find(path);
+            if (elem == fs.closure.elems.end())
+                throw Error(format("bad closure, missing path ‘%1%’") % path);
+
+            for (StringSet::const_iterator i = elem->second.refs.begin();
+                 i != elem->second.refs.end(); ++i)
+            {
+                workList.insert(*i);
+                cout << makeEdge(pathLabel(nePath, *i), pathLabel(nePath, path));
+            }
+
+            cout << makeNode(pathLabel(nePath, path),
+                symbolicName(path), "#ff0000");
+        }
     }
 }
 #endif
 
 
-void printDotGraph(const PathSet & roots)
+void printDotGraph(ref<Store> store, const PathSet & roots)
 {
     PathSet workList(roots);
     PathSet doneSet;
-            
+
     cout << "digraph G {\n";
 
     while (!workList.empty()) {
-	Path path = *(workList.begin());
-	workList.erase(path);
+        Path path = *(workList.begin());
+        workList.erase(path);
 
-	if (doneSet.find(path) != doneSet.end()) continue;
+        if (doneSet.find(path) != doneSet.end()) continue;
         doneSet.insert(path);
 
         cout << makeNode(path, symbolicName(path), "#ff0000");
-        
+
         PathSet references;
         store->queryReferences(path, references);
 
@@ -121,42 +121,42 @@ void printDotGraph(const PathSet & roots)
                 cout << makeEdge(*i, path);
             }
         }
-            
-
-#if 0        
-	    StoreExpr ne = storeExprFromPath(path);
-
-	    string label, colour;
-                    
-	    if (ne.type == StoreExpr::neDerivation) {
-		for (PathSet::iterator i = ne.derivation.inputs.begin();
-		     i != ne.derivation.inputs.end(); ++i)
-		{
-		    workList.insert(*i);
-		    cout << makeEdge(*i, path);
-		}
-
-		label = "derivation";
-		colour = "#00ff00";
-		for (StringPairs::iterator i = ne.derivation.env.begin();
-		     i != ne.derivation.env.end(); ++i)
-		    if (i->first == "name") label = i->second;
-	    }
-
-	    else if (ne.type == StoreExpr::neClosure) {
-		label = "<closure>";
-		colour = "#00ffff";
-		printClosure(path, ne);
-	    }
-
-	    else abort();
-
-	    cout << makeNode(path, label, colour);
+
+
+#if 0
+        StoreExpr ne = storeExprFromPath(path);
+
+        string label, colour;
+
+        if (ne.type == StoreExpr::neDerivation) {
+            for (PathSet::iterator i = ne.derivation.inputs.begin();
+                 i != ne.derivation.inputs.end(); ++i)
+            {
+                workList.insert(*i);
+                cout << makeEdge(*i, path);
+            }
+
+            label = "derivation";
+            colour = "#00ff00";
+            for (StringPairs::iterator i = ne.derivation.env.begin();
+                 i != ne.derivation.env.end(); ++i)
+                if (i->first == "name") label = i->second;
+        }
+
+        else if (ne.type == StoreExpr::neClosure) {
+            label = "<closure>";
+            colour = "#00ffff";
+            printClosure(path, ne);
+        }
+
+        else abort();
+
+        cout << makeNode(path, label, colour);
 #endif
     }
 
     cout << "}\n";
 }
 
- 
+
 }
diff --git a/src/nix-store/dotgraph.hh b/src/nix-store/dotgraph.hh
index 68410d84156d..e2b5fc72fbe1 100644
--- a/src/nix-store/dotgraph.hh
+++ b/src/nix-store/dotgraph.hh
@@ -4,6 +4,8 @@
 
 namespace nix {
 
-void printDotGraph(const PathSet & roots);
+class Store;
+
+void printDotGraph(ref<Store> store, const PathSet & roots);
 
 }
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index b49ebd09475c..2dfd9e7c7411 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -1,14 +1,14 @@
-#include "globals.hh"
-#include "misc.hh"
 #include "archive.hh"
-#include "shared.hh"
+#include "derivations.hh"
 #include "dotgraph.hh"
-#include "xmlgraph.hh"
+#include "globals.hh"
 #include "local-store.hh"
-#include "util.hh"
+#include "monitor-fd.hh"
 #include "serve-protocol.hh"
+#include "shared.hh"
+#include "util.hh"
 #include "worker-protocol.hh"
-#include "monitor-fd.hh"
+#include "xmlgraph.hh"
 
 #include <iostream>
 #include <algorithm>
@@ -37,13 +37,14 @@ static Path gcRoot;
 static int rootNr = 0;
 static bool indirectRoot = false;
 static bool noOutput = false;
+static std::shared_ptr<Store> store;
 
 
-LocalStore & ensureLocalStore()
+ref<LocalStore> ensureLocalStore()
 {
-    LocalStore * store2(dynamic_cast<LocalStore *>(store.get()));
+    auto store2 = std::dynamic_pointer_cast<LocalStore>(store);
     if (!store2) throw Error("you don't have sufficient rights to use this command");
-    return *store2;
+    return ref<LocalStore>(store2);
 }
 
 
@@ -65,7 +66,7 @@ static PathSet realisePath(Path path, bool build = true)
 
     if (isDerivation(p.first)) {
         if (build) store->buildPaths(singleton<PathSet>(path));
-        Derivation drv = derivationFromPath(*store, p.first);
+        Derivation drv = store->derivationFromPath(p.first);
         rootNr++;
 
         if (p.second.empty())
@@ -83,7 +84,7 @@ static PathSet realisePath(Path path, bool build = true)
                 Path rootName = gcRoot;
                 if (rootNr > 1) rootName += "-" + std::to_string(rootNr);
                 if (i->first != "out") rootName += "-" + i->first;
-                outPath = addPermRoot(*store, outPath, rootName, indirectRoot);
+                outPath = store->addPermRoot(outPath, rootName, indirectRoot);
             }
             outputs.insert(outPath);
         }
@@ -99,7 +100,7 @@ static PathSet realisePath(Path path, bool build = true)
             Path rootName = gcRoot;
             rootNr++;
             if (rootNr > 1) rootName += "-" + std::to_string(rootNr);
-            path = addPermRoot(*store, path, rootName, indirectRoot);
+            path = store->addPermRoot(path, rootName, indirectRoot);
         }
         return singleton<PathSet>(path);
     }
@@ -117,6 +118,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
         if (i == "--dry-run") dryRun = true;
         else if (i == "--repair") buildMode = bmRepair;
         else if (i == "--check") buildMode = bmCheck;
+        else if (i == "--hash") buildMode = bmHash;
         else if (i == "--ignore-unknown") ignoreUnknown = true;
         else throw UsageError(format("unknown flag ‘%1%’") % i);
 
@@ -128,7 +130,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
 
     unsigned long long downloadSize, narSize;
     PathSet willBuild, willSubstitute, unknown;
-    queryMissing(*store, PathSet(paths.begin(), paths.end()),
+    store->queryMissing(PathSet(paths.begin(), paths.end()),
         willBuild, willSubstitute, unknown, downloadSize, narSize);
 
     if (ignoreUnknown) {
@@ -140,7 +142,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
     }
 
     if (settings.get("print-missing", true))
-        printMissing(willBuild, willSubstitute, unknown, downloadSize, narSize);
+        printMissing(ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize);
 
     if (dryRun) return;
 
@@ -215,7 +217,7 @@ static PathSet maybeUseOutputs(const Path & storePath, bool useOutput, bool forc
 {
     if (forceRealise) realisePath(storePath);
     if (useOutput && isDerivation(storePath)) {
-        Derivation drv = derivationFromPath(*store, storePath);
+        Derivation drv = store->derivationFromPath(storePath);
         PathSet outputs;
         for (auto & i : drv.outputs)
             outputs.insert(i.second.path);
@@ -252,7 +254,7 @@ static void printTree(const Path & path,
        closure(B).  That is, if derivation A is an (possibly indirect)
        input of B, then A is printed first.  This has the effect of
        flattening the tree, preventing deeply nested structures.  */
-    Paths sorted = topoSortPaths(*store, references);
+    Paths sorted = store->topoSortPaths(references);
     reverse(sorted.begin(), sorted.end());
 
     for (auto i = sorted.begin(); i != sorted.end(); ++i) {
@@ -317,7 +319,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
             for (auto & i : opArgs) {
                 i = followLinksToStorePath(i);
                 if (forceRealise) realisePath(i);
-                Derivation drv = derivationFromPath(*store, i);
+                Derivation drv = store->derivationFromPath(i);
                 for (auto & j : drv.outputs)
                     cout << format("%1%\n") % j.second.path;
             }
@@ -332,13 +334,13 @@ static void opQuery(Strings opFlags, Strings opArgs)
             for (auto & i : opArgs) {
                 PathSet ps = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise);
                 for (auto & j : ps) {
-                    if (query == qRequisites) computeFSClosure(*store, j, paths, false, includeOutputs);
+                    if (query == qRequisites) store->computeFSClosure(j, paths, false, includeOutputs);
                     else if (query == qReferences) store->queryReferences(j, paths);
                     else if (query == qReferrers) store->queryReferrers(j, paths);
-                    else if (query == qReferrersClosure) computeFSClosure(*store, j, paths, true);
+                    else if (query == qReferrersClosure) store->computeFSClosure(j, paths, true);
                 }
             }
-            Paths sorted = topoSortPaths(*store, paths);
+            Paths sorted = store->topoSortPaths(paths);
             for (Paths::reverse_iterator i = sorted.rbegin();
                  i != sorted.rend(); ++i)
                 cout << format("%s\n") % *i;
@@ -356,7 +358,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
         case qBinding:
             for (auto & i : opArgs) {
                 Path path = useDeriver(followLinksToStorePath(i));
-                Derivation drv = derivationFromPath(*store, path);
+                Derivation drv = store->derivationFromPath(path);
                 StringPairs::iterator j = drv.env.find(bindingName);
                 if (j == drv.env.end())
                     throw Error(format("derivation ‘%1%’ has no environment binding named ‘%2%’")
@@ -393,7 +395,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
                 PathSet paths = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise);
                 roots.insert(paths.begin(), paths.end());
             }
-            printDotGraph(roots);
+            printDotGraph(ref<Store>(store), roots);
             break;
         }
 
@@ -403,7 +405,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
                 PathSet paths = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise);
                 roots.insert(paths.begin(), paths.end());
             }
-            printXmlGraph(roots);
+            printXmlGraph(ref<Store>(store), roots);
             break;
         }
 
@@ -418,7 +420,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
             for (auto & i : opArgs) {
                 PathSet paths = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise);
                 for (auto & j : paths)
-                    computeFSClosure(*store, j, referrers, true,
+                    store->computeFSClosure(j, referrers, true,
                         settings.gcKeepOutputs, settings.gcKeepDerivations);
             }
             Roots roots = store->findRoots();
@@ -449,7 +451,7 @@ static void opPrintEnv(Strings opFlags, Strings opArgs)
     if (opArgs.size() != 1) throw UsageError("‘--print-env’ requires one derivation store path");
 
     Path drvPath = opArgs.front();
-    Derivation drv = derivationFromPath(*store, drvPath);
+    Derivation drv = store->derivationFromPath(drvPath);
 
     /* Print each environment variable in the derivation in a format
        that can be sourced by the shell. */
@@ -572,7 +574,7 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise)
         }
     }
 
-    ensureLocalStore().registerValidPaths(infos);
+    ensureLocalStore()->registerValidPaths(infos);
 }
 
 
@@ -714,9 +716,9 @@ static void opExport(Strings opFlags, Strings opArgs)
         else throw UsageError(format("unknown flag ‘%1%’") % i);
 
     FdSink sink(STDOUT_FILENO);
-    Paths sorted = topoSortPaths(*store, PathSet(opArgs.begin(), opArgs.end()));
+    Paths sorted = store->topoSortPaths(PathSet(opArgs.begin(), opArgs.end()));
     reverse(sorted.begin(), sorted.end());
-    exportPaths(*store, sorted, sign, sink);
+    store->exportPaths(sorted, sign, sink);
 }
 
 
@@ -803,7 +805,7 @@ static void opRepairPath(Strings opFlags, Strings opArgs)
 
     for (auto & i : opArgs) {
         Path path = followLinksToStorePath(i);
-        ensureLocalStore().repairPath(path);
+        ensureLocalStore()->repairPath(path);
     }
 }
 
@@ -895,7 +897,7 @@ static void opServe(Strings opFlags, Strings opArgs)
                         if (!isDerivation(path)) paths2.insert(path);
                     unsigned long long downloadSize, narSize;
                     PathSet willBuild, willSubstitute, unknown;
-                    queryMissing(*store, PathSet(paths2.begin(), paths2.end()),
+                    store->queryMissing(PathSet(paths2.begin(), paths2.end()),
                         willBuild, willSubstitute, unknown, downloadSize, narSize);
                     /* FIXME: should use ensurePath(), but it only
                        does one path at a time. */
@@ -940,9 +942,9 @@ static void opServe(Strings opFlags, Strings opArgs)
 
             case cmdExportPaths: {
                 bool sign = readInt(in);
-                Paths sorted = topoSortPaths(*store, readStorePaths<PathSet>(in));
+                Paths sorted = store->topoSortPaths(readStorePaths<PathSet>(in));
                 reverse(sorted.begin(), sorted.end());
-                exportPaths(*store, sorted, sign, out);
+                store->exportPaths(sorted, sign, out);
                 break;
             }
 
@@ -987,7 +989,7 @@ static void opServe(Strings opFlags, Strings opArgs)
                 PathSet paths = readStorePaths<PathSet>(in);
                 PathSet closure;
                 for (auto & i : paths)
-                    computeFSClosure(*store, i, closure, false, includeOutputs);
+                    store->computeFSClosure(i, closure, false, includeOutputs);
                 out << closure;
                 break;
             }
diff --git a/src/nix-store/xmlgraph.cc b/src/nix-store/xmlgraph.cc
index 1b3ad3d28ad4..b6e1c1c4b873 100644
--- a/src/nix-store/xmlgraph.cc
+++ b/src/nix-store/xmlgraph.cc
@@ -33,34 +33,34 @@ static string makeNode(const string & id)
 }
 
 
-void printXmlGraph(const PathSet & roots)
+void printXmlGraph(ref<Store> store, const PathSet & roots)
 {
     PathSet workList(roots);
     PathSet doneSet;
 
     cout << "<?xml version='1.0' encoding='utf-8'?>\n"
-	 << "<nix>\n";
+         << "<nix>\n";
 
     while (!workList.empty()) {
-	Path path = *(workList.begin());
-	workList.erase(path);
+        Path path = *(workList.begin());
+        workList.erase(path);
 
-	if (doneSet.find(path) != doneSet.end()) continue;
-	doneSet.insert(path);
+        if (doneSet.find(path) != doneSet.end()) continue;
+        doneSet.insert(path);
 
-	cout << makeNode(path);
+        cout << makeNode(path);
 
-	PathSet references;
-	store->queryReferences(path, references);
+        PathSet references;
+        store->queryReferences(path, references);
 
-	for (PathSet::iterator i = references.begin();
-	     i != references.end(); ++i)
-	{
-	    if (*i != path) {
-		workList.insert(*i);
-		cout << makeEdge(*i, path);
-	    }
-	}
+        for (PathSet::iterator i = references.begin();
+             i != references.end(); ++i)
+        {
+            if (*i != path) {
+                workList.insert(*i);
+                cout << makeEdge(*i, path);
+            }
+        }
 
     }
 
diff --git a/src/nix-store/xmlgraph.hh b/src/nix-store/xmlgraph.hh
index c2216c5a4627..a6e7d4e2805a 100644
--- a/src/nix-store/xmlgraph.hh
+++ b/src/nix-store/xmlgraph.hh
@@ -4,6 +4,8 @@
 
 namespace nix {
 
-void printXmlGraph(const PathSet & roots);
+class Store;
+
+void printXmlGraph(ref<Store> store, const PathSet & roots);
 
 }
diff --git a/tests/dump-db.sh b/tests/dump-db.sh
index 234b7ac02680..57c8c401600d 100644
--- a/tests/dump-db.sh
+++ b/tests/dump-db.sh
@@ -9,6 +9,7 @@ deps="$(nix-store -qR $TEST_ROOT/result)"
 nix-store --dump-db > $TEST_ROOT/dump
 
 rm -rf $NIX_DB_DIR
+mkdir $NIX_DB_DIR
 
 nix-store --load-db < $TEST_ROOT/dump
 
diff --git a/tests/lang/parse-okay-regression-751.nix b/tests/lang/parse-okay-regression-751.nix
new file mode 100644
index 000000000000..05c78b3016d3
--- /dev/null
+++ b/tests/lang/parse-okay-regression-751.nix
@@ -0,0 +1,2 @@
+let const = a: "const"; in
+''${ const { x = "q"; }}''
diff --git a/tests/repair.sh b/tests/repair.sh
index ae82b649c6ac..92f2f8fe60a5 100644
--- a/tests/repair.sh
+++ b/tests/repair.sh
@@ -18,6 +18,18 @@ if nix-store --verify --check-contents -v; then
     exit 1
 fi
 
+# The path can be repaired by rebuilding the derivation.
+nix-store --verify --check-contents --repair
+
+nix-store --verify-path $path2
+
+# Re-corrupt and delete the deriver. Now --verify --repair should
+# not work.
+chmod u+w $path2
+touch $path2/bad
+
+nix-store --delete $(nix-store -qd $path2)
+
 if nix-store --verify --check-contents --repair; then
     echo "nix-store --verify --repair succeeded unexpectedly" >&2
     exit 1
diff --git a/version b/version
index b8162070734f..35d51f33b34f 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-1.11
\ No newline at end of file
+1.12
\ No newline at end of file