about summary refs log tree commit diff
path: root/src/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/local-store.cc7
-rw-r--r--src/libstore/optimise-store.cc26
2 files changed, 31 insertions, 2 deletions
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index a30839643c4b..21b1bdceae2f 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -5,6 +5,7 @@
 #include "pathlocks.hh"
 #include "worker-protocol.hh"
 #include "derivations.hh"
+#include "immutable.hh"
     
 #include <iostream>
 #include <algorithm>
@@ -405,6 +406,10 @@ void canonicalisePathMetaData(const Path & path, bool recurse)
     if (lstat(path.c_str(), &st))
 	throw SysError(format("getting attributes of path `%1%'") % path);
 
+    /* Really make sure that the path is of a supported type.  This
+       has already been checked in dumpPath(). */
+    assert(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode));
+
     /* Change ownership to the current uid.  If it's a symlink, use
        lchown if available, otherwise don't bother.  Wrong ownership
        of a symlink doesn't matter, since the owning user can't change
@@ -451,6 +456,8 @@ void canonicalisePathMetaData(const Path & path, bool recurse)
 	foreach (Strings::iterator, i, names)
 	    canonicalisePathMetaData(path + "/" + *i, true);
     }
+
+    makeImmutable(path);
 }
 
 
diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc
index 89be6ac6529a..2ca98f46ddf4 100644
--- a/src/libstore/optimise-store.cc
+++ b/src/libstore/optimise-store.cc
@@ -1,5 +1,6 @@
 #include "util.hh"
 #include "local-store.hh"
+#include "immutable.hh"
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -19,6 +20,7 @@ static void makeWritable(const Path & path)
     struct stat st;
     if (lstat(path.c_str(), &st))
 	throw SysError(format("getting attributes of path `%1%'") % path);
+    if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path);
     if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
         throw SysError(format("changing writability of `%1%'") % path);
 }
@@ -31,6 +33,8 @@ struct MakeReadOnly
     ~MakeReadOnly()
     {
         try {
+            /* This will make the path read-only (and restore the
+               immutable bit on platforms that support it). */
             if (path != "") canonicalisePathMetaData(path, false);
         } catch (...) {
             ignoreException();
@@ -39,6 +43,14 @@ struct MakeReadOnly
 };
 
 
+struct MakeImmutable
+{
+    Path path;
+    MakeImmutable(const Path & path) : path(path) { }
+    ~MakeImmutable() { makeImmutable(path); }
+};
+
+
 static void hashAndLink(bool dryRun, HashToPath & hashToPath,
     OptimiseStats & stats, const Path & path)
 {
@@ -96,14 +108,24 @@ static void hashAndLink(bool dryRun, HashToPath & hashToPath,
 
             /* Make the containing directory writable, but only if
                it's not the store itself (we don't want or need to
-               mess with  its permissions). */
+               mess with its permissions). */
             bool mustToggle = !isStorePath(path);
             if (mustToggle) makeWritable(dirOf(path));
             
             /* When we're done, make the directory read-only again and
                reset its timestamp back to 0. */
             MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
-        
+
+            /* If ‘prevPath’ is immutable, we can't create hard links
+               to it, so make it mutable first (and make it immutable
+               again when we're done).  We also have to make ‘path’
+               mutable, otherwise rename() will fail to delete it. */
+            makeMutable(prevPath.first);
+            MakeImmutable mk1(prevPath.first);
+            
+            makeMutable(path);
+            MakeImmutable mk2(path);
+
             if (link(prevPath.first.c_str(), tempLink.c_str()) == -1) {
                 if (errno == EMLINK) {
                     /* Too many links to the same file (>= 32000 on