aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/local-store.cc
diff options
context:
space:
mode:
authorBen Radford <104896700+benradf@users.noreply.github.com>2023-06-20 10:34:09 +0100
committerGitHub <noreply@github.com>2023-06-20 11:34:09 +0200
commit6ae35534b7b6e10a26a0f2b2a0e37d7f7cfe47dd (patch)
tree2a56fe3241cc2845c806d573a4fcef7ccbac5bfb /src/libstore/local-store.cc
parent3910430b9d034c277650f4ff05a27008ede73c18 (diff)
Support opening local store with database on read-only filesystem (#8356)
Previously it was not possible to open a local store when its database is on a read-only filesystem. Obviously a store on a read-only filesystem cannot be modified, but it would still be useful to be able to query it. This change adds a new read-only setting to LocalStore. When set to true, Nix will skip operations that fail when the database is on a read-only filesystem (acquiring big-lock, schema migration, etc), and the store database will be opened in immutable mode. Co-authored-by: Ben Radford <benradf@users.noreply.github.com> Co-authored-by: cidkidnix <cidkidnix@protonmail.com> Co-authored-by: Dylan Green <67574902+cidkidnix@users.noreply.github.com> Co-authored-by: John Ericson <git@JohnEricson.me> Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Diffstat (limited to 'src/libstore/local-store.cc')
-rw-r--r--src/libstore/local-store.cc45
1 files changed, 36 insertions, 9 deletions
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 7fb312c37..e69460e6c 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -190,7 +190,11 @@ LocalStore::LocalStore(const Params & params)
/* Create missing state directories if they don't already exist. */
createDirs(realStoreDir);
- makeStoreWritable();
+ if (readOnly) {
+ experimentalFeatureSettings.require(Xp::ReadOnlyLocalStore);
+ } else {
+ makeStoreWritable();
+ }
createDirs(linksDir);
Path profilesDir = stateDir + "/profiles";
createDirs(profilesDir);
@@ -204,8 +208,10 @@ LocalStore::LocalStore(const Params & params)
for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) {
createDirs(perUserDir);
- if (chmod(perUserDir.c_str(), 0755) == -1)
- throw SysError("could not set permissions on '%s' to 755", perUserDir);
+ if (!readOnly) {
+ if (chmod(perUserDir.c_str(), 0755) == -1)
+ throw SysError("could not set permissions on '%s' to 755", perUserDir);
+ }
}
/* Optionally, create directories and set permissions for a
@@ -269,10 +275,12 @@ LocalStore::LocalStore(const Params & params)
/* Acquire the big fat lock in shared mode to make sure that no
schema upgrade is in progress. */
- Path globalLockPath = dbDir + "/big-lock";
- globalLock = openLockFile(globalLockPath.c_str(), true);
+ if (!readOnly) {
+ Path globalLockPath = dbDir + "/big-lock";
+ globalLock = openLockFile(globalLockPath.c_str(), true);
+ }
- if (!lockFile(globalLock.get(), ltRead, false)) {
+ if (!readOnly && !lockFile(globalLock.get(), ltRead, false)) {
printInfo("waiting for the big Nix store lock...");
lockFile(globalLock.get(), ltRead, true);
}
@@ -280,6 +288,14 @@ LocalStore::LocalStore(const Params & params)
/* Check the current database schema and if necessary do an
upgrade. */
int curSchema = getSchema();
+ if (readOnly && curSchema < nixSchemaVersion) {
+ debug("current schema version: %d", curSchema);
+ debug("supported schema version: %d", nixSchemaVersion);
+ throw Error(curSchema == 0 ?
+ "database does not exist, and cannot be created in read-only mode" :
+ "database schema needs migrating, but this cannot be done in read-only mode");
+ }
+
if (curSchema > nixSchemaVersion)
throw Error("current Nix store schema is version %1%, but I only support %2%",
curSchema, nixSchemaVersion);
@@ -344,7 +360,11 @@ LocalStore::LocalStore(const Params & params)
else openDB(*state, false);
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
- migrateCASchema(state->db, dbDir + "/ca-schema", globalLock);
+ if (!readOnly) {
+ migrateCASchema(state->db, dbDir + "/ca-schema", globalLock);
+ } else {
+ throw Error("need to migrate to content-addressed schema, but this cannot be done in read-only mode");
+ }
}
/* Prepare SQL statements. */
@@ -475,13 +495,20 @@ int LocalStore::getSchema()
void LocalStore::openDB(State & state, bool create)
{
- if (access(dbDir.c_str(), R_OK | W_OK))
+ if (create && readOnly) {
+ throw Error("cannot create database while in read-only mode");
+ }
+
+ if (access(dbDir.c_str(), R_OK | (readOnly ? 0 : W_OK)))
throw SysError("Nix database directory '%1%' is not writable", dbDir);
/* Open the Nix database. */
std::string dbPath = dbDir + "/db.sqlite";
auto & db(state.db);
- state.db = SQLite(dbPath, create);
+ auto openMode = readOnly ? SQLiteOpenMode::Immutable
+ : create ? SQLiteOpenMode::Normal
+ : SQLiteOpenMode::NoCreate;
+ state.db = SQLite(dbPath, openMode);
#ifdef __CYGWIN__
/* The cygwin version of sqlite3 has a patch which calls