daemon: Ensure store is writable even as non-root.

If the store is read only, return an error early.
This is bit of a compromise. Not all operations of the daemon need the store
as writable. For example, if hello package is built already `guix build hello`
could previously succeed even if store is RO.

* nix/libstore/local-store.cc
(makeStoreWritable): Rename to ensureStoreWritable.
(ensureStoreWritable): As non-root, check that the store is writable and if
not, throw an error.
(LocalStore::LocalStore): Use it.

* nix/libstore/local-store.hh: Rename makeStoreWritable to ensureStoreWritable.

Change-Id: I94783ba7e32d57bfa77e37e84b6ac316f95e31e2
Signed-off-by: Rutherther <rutherther@ditigal.xyz>
This commit is contained in:
Rutherther 2025-12-21 13:25:59 +01:00
parent 09eda1627e
commit 2a0ac4cba5
No known key found for this signature in database
GPG key ID: 0322798269E471C3
2 changed files with 14 additions and 6 deletions

View file

@ -67,7 +67,7 @@ LocalStore::LocalStore(bool reserveSpace)
/* Create missing state directories if they don't already exist. */
createDirs(settings.nixStore);
makeStoreWritable();
ensureStoreWritable();
createDirs(linksDir = settings.nixStore + "/.links");
Path profilesDir = settings.nixStateDir + "/profiles";
createDirs(profilesDir);
@ -300,18 +300,26 @@ void LocalStore::openDB(bool create)
}
/* To improve purity, users may want to make the store a read-only
bind mount. So make the store writable for this process. */
void LocalStore::makeStoreWritable()
/* To improve purity, users may want to make the store a read-only bind mount.
So make the store writable for this process. In case the store is read-only
and cannot be made writable, throw an error. */
void LocalStore::ensureStoreWritable()
{
#if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_REMOUNT)
if (getuid() != 0) return;
/* Check if /nix/store is on a read-only mount. */
struct statvfs stat;
if (statvfs(settings.nixStore.c_str(), &stat) != 0)
throw SysError("getting info about the store mount point");
if (stat.f_flag & ST_RDONLY) {
if (getuid() != 0) {
throw Error(
std::format(
"'{}' is read-only; make sure to mount it read-write "
"for proper guix-daemon operation",
settings.nixStore));
}
if (unshare(CLONE_NEWNS) == -1)
throw SysError("setting up a private mount namespace");

View file

@ -212,7 +212,7 @@ private:
void openDB(bool create);
void makeStoreWritable();
void ensureStoreWritable();
uint64_t queryValidPathId(const Path & path);