From b9124a5c336fd231adaa548cf5be311731847848 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 19 Sep 2012 15:45:29 -0400 Subject: [PATCH] Support having /nix/store as a read-only bind mount It turns out that the immutable bit doesn't work all that well. A better way is to make the entire Nix store a read-only bind mount, i.e. by doing $ mount --bind /nix/store /nix/store $ mount -o remount,ro,bind /nix/store (This would typically done in an early boot script, before anything from /nix/store is used.) Since Nix needs to be able to write to the Nix store, it now detects if /nix/store is a read-only bind mount and then makes it writable in a private mount namespace. --- src/libstore/local-store.cc | 39 +++++++++++++++++++++++++++++++++++++ src/libstore/local-store.hh | 2 ++ 2 files changed, 41 insertions(+) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 085e662ed..bb755b821 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -20,6 +20,11 @@ #include #include +#if HAVE_UNSHARE +#include +#include +#endif + #include @@ -292,6 +297,8 @@ LocalStore::LocalStore(bool reserveSpace) } else openDB(false); + + makeStoreWritable(); } @@ -411,6 +418,38 @@ void LocalStore::openDB(bool create) } +/* To improve purity, users may want to make the Nix store a read-only + bind mount. So make the Nix store writable for this process. */ +void LocalStore::makeStoreWritable() +{ +#if HAVE_UNSHARE + if (getuid() != 0) return; + + if (!pathExists("/proc/self/mountinfo")) return; + + /* Check if /nix/store is a read-only bind mount. */ + bool found = false; + Strings mounts = tokenizeString(readFile("/proc/self/mountinfo", true), "\n"); + foreach (Strings::iterator, i, mounts) { + vector fields = tokenizeString >(*i, " "); + if (fields.at(3) == "/" || fields.at(4) != settings.nixStore) continue; + Strings options = tokenizeString(fields.at(5), ","); + if (std::find(options.begin(), options.end(), "ro") == options.end()) continue; + found = true; + break; + } + + if (!found) return; + + if (unshare(CLONE_NEWNS) == -1) + throw SysError("setting up a private mount namespace"); + + if (mount(0, settings.nixStore.c_str(), 0, MS_REMOUNT | MS_BIND, 0) == -1) + throw SysError(format("remounting %1% writable") % settings.nixStore); +#endif +} + + const time_t mtimeStore = 1; /* 1 second into the epoch */ diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index d2b13d6a9..8899873a7 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -228,6 +228,8 @@ private: void openDB(bool create); + void makeStoreWritable(); + unsigned long long queryValidPathId(const Path & path); unsigned long long addValidPath(const ValidPathInfo & info, bool checkOutputs = true);