Clear any immutable bits in the Nix store

Doing this once makes subsequent operations like garbage collecting
more efficient since we don't have to call makeMutable() first.
This commit is contained in:
Eelco Dolstra 2013-01-03 12:59:23 +01:00
parent 0a4e90395c
commit def5160b61
10 changed files with 76 additions and 89 deletions

View file

@ -8,10 +8,17 @@
<!--==================================================================--> <!--==================================================================-->
<section xml:id="ssec-relnotes-1.3"><title>Release 1.3 (January 2, 2013)</title> <section xml:id="ssec-relnotes-1.3"><title>Release 1.3 (January 3, 2013)</title>
<para>This is primarily a bug fix release. It has contributions from <para>This is primarily a bug fix release. When this version is first
Eelco Dolstra and Stuart Pernsteiner.</para> run on Linux, it removes any immutable bits from the Nix store and
increases the schema version of the Nix store. (The previous release
removed support for setting the immutable bit; this release clears any
remaining immutable bits to make certain operations more
efficient.)</para>
<para>This release has contributions from Eelco Dolstra and Stuart
Pernsteiner.</para>
</section> </section>

View file

@ -7,7 +7,6 @@
#include "local-store.hh" #include "local-store.hh"
#include "util.hh" #include "util.hh"
#include "archive.hh" #include "archive.hh"
#include "immutable.hh"
#include <map> #include <map>
#include <sstream> #include <sstream>
@ -1383,10 +1382,8 @@ void replaceValidPath(const Path & storePath, const Path tmpPath)
way first. We'd better not be interrupted here, because if way first. We'd better not be interrupted here, because if
we're repairing (say) Glibc, we end up with a broken system. */ we're repairing (say) Glibc, we end up with a broken system. */
Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % rand()).str(); Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % rand()).str();
if (pathExists(storePath)) { if (pathExists(storePath))
makeMutable(storePath);
rename(storePath.c_str(), oldPath.c_str()); rename(storePath.c_str(), oldPath.c_str());
}
if (rename(tmpPath.c_str(), storePath.c_str()) == -1) if (rename(tmpPath.c_str(), storePath.c_str()) == -1)
throw SysError(format("moving `%1%' to `%2%'") % tmpPath % storePath); throw SysError(format("moving `%1%' to `%2%'") % tmpPath % storePath);
if (pathExists(oldPath)) if (pathExists(oldPath))
@ -1911,10 +1908,6 @@ void DerivationGoal::startBuilder()
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
dirsInChroot[*i] = *i; dirsInChroot[*i] = *i;
else { else {
/* Creating a hard link to *i is impossible if its
immutable bit is set. So clear it first. */
makeMutable(*i);
Path p = chrootRootDir + *i; Path p = chrootRootDir + *i;
if (link(i->c_str(), p.c_str()) == -1) { if (link(i->c_str(), p.c_str()) == -1) {
/* Hard-linking fails if we exceed the maximum /* Hard-linking fails if we exceed the maximum

View file

@ -1,7 +1,6 @@
#include "globals.hh" #include "globals.hh"
#include "misc.hh" #include "misc.hh"
#include "local-store.hh" #include "local-store.hh"
#include "immutable.hh"
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
@ -456,7 +455,6 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
// if the path was not valid, need to determine the actual // if the path was not valid, need to determine the actual
// size. // size.
state.bytesInvalidated += size; state.bytesInvalidated += size;
makeMutable(path.c_str());
// Mac OS X cannot rename directories if they are read-only. // Mac OS X cannot rename directories if they are read-only.
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1) if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("making `%1%' writable") % path); throw SysError(format("making `%1%' writable") % path);

View file

@ -5,7 +5,6 @@
#include "pathlocks.hh" #include "pathlocks.hh"
#include "worker-protocol.hh" #include "worker-protocol.hh"
#include "derivations.hh" #include "derivations.hh"
#include "immutable.hh"
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
@ -25,6 +24,12 @@
#include <sys/mount.h> #include <sys/mount.h>
#endif #endif
#if HAVE_LINUX_FS_H
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <errno.h>
#endif
#include <sqlite3.h> #include <sqlite3.h>
@ -292,6 +297,7 @@ LocalStore::LocalStore(bool reserveSpace)
curSchema = getSchema(); curSchema = getSchema();
if (curSchema < 6) upgradeStore6(); if (curSchema < 6) upgradeStore6();
else if (curSchema < 7) upgradeStore7();
writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str()); writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
@ -1787,6 +1793,59 @@ void LocalStore::upgradeStore6()
} }
#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL)
static void makeMutable(const Path & path)
{
checkInterrupt();
struct stat st = lstat(path);
if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) return;
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
foreach (Strings::iterator, i, names)
makeMutable(path + "/" + *i);
}
/* The O_NOFOLLOW is important to prevent us from changing the
mutable bit on the target of a symlink (which would be a
security hole). */
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
if (errno == ELOOP) return; // it's a symlink
throw SysError(format("opening file `%1%'") % path);
}
unsigned int flags = 0, old;
/* Silently ignore errors getting/setting the immutable flag so
that we work correctly on filesystems that don't support it. */
if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return;
old = flags;
flags &= ~FS_IMMUTABLE_FL;
if (old == flags) return;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return;
}
/* Upgrade from schema 6 (Nix 0.15) to schema 7 (Nix >= 1.3). */
void LocalStore::upgradeStore7()
{
if (getuid() != 0) return;
printMsg(lvlError, "removing immutable bits from the Nix store (this may take a while)...");
makeMutable(settings.nixStore);
}
#else
void LocalStore::upgradeStore7()
{
}
#endif
void LocalStore::vacuumDB() void LocalStore::vacuumDB()
{ {
if (sqlite3_exec(db, "vacuum;", 0, 0, 0) != SQLITE_OK) if (sqlite3_exec(db, "vacuum;", 0, 0, 0) != SQLITE_OK)

View file

@ -17,8 +17,8 @@ namespace nix {
/* Nix store and database schema version. Version 1 (or 0) was Nix <= /* Nix store and database schema version. Version 1 (or 0) was Nix <=
0.7. Version 2 was Nix 0.8 and 0.9. Version 3 is Nix 0.10. 0.7. Version 2 was Nix 0.8 and 0.9. Version 3 is Nix 0.10.
Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.16. Version 6 is Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.16. Version 6 is
Nix 1.0. */ Nix 1.0. Version 7 is Nix 1.3. */
const int nixSchemaVersion = 6; const int nixSchemaVersion = 7;
extern string drvsLogDir; extern string drvsLogDir;
@ -265,6 +265,7 @@ private:
void updatePathInfo(const ValidPathInfo & info); void updatePathInfo(const ValidPathInfo & info);
void upgradeStore6(); void upgradeStore6();
void upgradeStore7();
PathSet queryValidPathsOld(); PathSet queryValidPathsOld();
ValidPathInfo queryPathInfoOld(const Path & path); ValidPathInfo queryPathInfoOld(const Path & path);

View file

@ -2,7 +2,6 @@
#include "util.hh" #include "util.hh"
#include "local-store.hh" #include "local-store.hh"
#include "immutable.hh"
#include "globals.hh" #include "globals.hh"
#include <sys/types.h> #include <sys/types.h>
@ -20,7 +19,6 @@ static void makeWritable(const Path & path)
struct stat st; struct stat st;
if (lstat(path.c_str(), &st)) if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path `%1%'") % path); 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) if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("changing writability of `%1%'") % path); throw SysError(format("changing writability of `%1%'") % path);
} }
@ -91,7 +89,6 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
if (!pathExists(linkPath)) { if (!pathExists(linkPath)) {
/* Nope, create a hard link in the links directory. */ /* Nope, create a hard link in the links directory. */
makeMutable(path);
if (link(path.c_str(), linkPath.c_str()) == 0) return; if (link(path.c_str(), linkPath.c_str()) == 0) return;
if (errno != EEXIST) if (errno != EEXIST)
throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path); throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path);
@ -123,12 +120,6 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
its timestamp back to 0. */ its timestamp back to 0. */
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : ""); MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
/* If linkPath is immutable, we can't create hard links to it,
so make it mutable first. We also have to make path mutable,
otherwise rename() will fail to delete it. */
makeMutable(path);
makeMutable(linkPath);
Path tempLink = (format("%1%/.tmp-link-%2%-%3%") Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
% settings.nixStore % getpid() % rand()).str(); % settings.nixStore % getpid() % rand()).str();

View file

@ -1,12 +1,12 @@
pkglib_LTLIBRARIES = libutil.la pkglib_LTLIBRARIES = libutil.la
libutil_la_SOURCES = util.cc hash.cc serialise.cc \ libutil_la_SOURCES = util.cc hash.cc serialise.cc \
archive.cc xml-writer.cc immutable.cc archive.cc xml-writer.cc
libutil_la_LIBADD = ../boost/format/libformat.la libutil_la_LIBADD = ../boost/format/libformat.la
pkginclude_HEADERS = util.hh hash.hh serialise.hh \ pkginclude_HEADERS = util.hh hash.hh serialise.hh \
archive.hh xml-writer.hh types.hh immutable.hh archive.hh xml-writer.hh types.hh
if !HAVE_OPENSSL if !HAVE_OPENSSL
libutil_la_SOURCES += \ libutil_la_SOURCES += \

View file

@ -1,49 +0,0 @@
#include "config.h"
#include "immutable.hh"
#include "util.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if HAVE_LINUX_FS_H
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <errno.h>
#endif
namespace nix {
void makeMutable(const Path & path)
{
#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL)
/* Don't even try if we're not root. One day we should support
the CAP_LINUX_IMMUTABLE capability. */
if (getuid() != 0) return;
/* The O_NOFOLLOW is important to prevent us from changing the
mutable bit on the target of a symlink (which would be a
security hole). */
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
if (errno == ELOOP) return; // it's a symlink
throw SysError(format("opening file `%1%'") % path);
}
unsigned int flags = 0, old;
/* Silently ignore errors getting/setting the immutable flag so
that we work correctly on filesystems that don't support it. */
if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return;
old = flags;
flags &= ~FS_IMMUTABLE_FL;
if (old == flags) return;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return;
#endif
}
}

View file

@ -1,10 +0,0 @@
#pragma once
#include <types.hh>
namespace nix {
/* Make the given path mutable. */
void makeMutable(const Path & path);
}

View file

@ -13,7 +13,6 @@
#include <limits.h> #include <limits.h>
#include "util.hh" #include "util.hh"
#include "immutable.hh"
extern char * * environ; extern char * * environ;
@ -305,8 +304,6 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
struct stat st = lstat(path); struct stat st = lstat(path);
if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path);
if (!S_ISDIR(st.st_mode) && st.st_nlink == 1) if (!S_ISDIR(st.st_mode) && st.st_nlink == 1)
bytesFreed += st.st_blocks * 512; bytesFreed += st.st_blocks * 512;