libfetchers: fallback to memory SQLite if fs IO fails

nix::fetchers::CacheImpl uses $XDG_CACHE_HOME, or its default based on
$HOME, to store its SQLite database. If the current process can't write
to that directory for whatever reason, though, any eval-time fetching
would fail just initializing the cache.

With this change, IO errors initializing the fetcher cache are logged
but ignored, and nix::fetchers::CacheImpl falls back to an in-memory¹
database instead.

Notably, this will fix any uses eval fetching while Lix itself is being
run in a derivation builder (such as during tests), as the derivation
builder does not set $XDG_CACHE_HOME, and sets $HOME to the non-existent
directory /homeless-shelter.

Before:

$ env -u XDG_CACHE_HOME HOME=/homeless-shelter nix -Lv eval --impure -E 'fetchTarball "https://git.lix.systems/lix-project/lix/archive/main.tar.gz"'
error:
       … while calling the 'fetchTarball' builtin
         at «string»:1:1:
            1| fetchTarball "https://git.lix.systems/lix-project/lix/archive/main.tar.gz"
             | ^

       error: creating directory '/homeless-shelter': Permission denied

After:

$ env -u XDG_CACHE_HOME HOME=/homeless-shelter nix -Lv eval --impure -E 'fetchTarball "https://git.lix.systems/lix-project/lix/archive/main.tar.gz"'
warning: ignoring error initializing Lix fetcher cache: error: creating directory '/homeless-shelter': Permission denied
"/nix/store/s9lxdnn0awp37n560bg4fgr497ah4hvw-source"

¹: https://www.sqlite.org/inmemorydb.html

Change-Id: I15c38c9baaf215fc6e192b8a4c70b9692a69bc22
This commit is contained in:
Qyriad 2024-05-21 09:30:25 -06:00
parent 20981461d4
commit 6881476232

View file

@ -34,7 +34,16 @@ struct CacheImpl : Cache
auto state(_state.lock()); auto state(_state.lock());
auto dbPath = getCacheDir() + "/nix/fetcher-cache-v1.sqlite"; auto dbPath = getCacheDir() + "/nix/fetcher-cache-v1.sqlite";
// It would be silly to fail fetcher operations if e.g. the user has no
// XDG_CACHE_HOME and their HOME directory doesn't exist.
// We'll warn the user if that happens, but fallback to an in-memory
// backend for the SQLite database.
try {
createDirs(dirOf(dbPath)); createDirs(dirOf(dbPath));
} catch (SysError const & ex) {
warn("ignoring error initializing Lix fetcher cache: %s", ex.what());
dbPath = ":memory:";
}
state->db = SQLite(dbPath); state->db = SQLite(dbPath);
state->db.isCache(); state->db.isCache();