1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-21 17:59:36 +01:00
nix/src/libstore/remote-fs-accessor.cc
John Ericson eb643d034f Store::getFSAccessor: Do not include the store dir
Rather than "mounting" the store inside an empty virtual filesystem,
just return the store as a virtual filesystem. This is more modular.

(FWIW, it also supports two long term hopes of mind:

1. More capability-based Nix language mode. I dream of a "super pure
   eval" where you can only use relative path literals (See #8738), and
   any `fetchTree`-fetched stuff + the store are all disjoint (none is
   mounted in another) file systems.

2. Windows, where the store dir may include drive letters, etc., and is
   thus unsuitable to be the prefix of any `CanonPath`s.

)

Co-authored-by: Eelco Dolstra <edolstra@gmail.com>
2025-04-09 17:34:18 -04:00

132 lines
3.9 KiB
C++

#include <nlohmann/json.hpp>
#include "nix/store/remote-fs-accessor.hh"
#include "nix/store/nar-accessor.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
namespace nix {
RemoteFSAccessor::RemoteFSAccessor(ref<Store> store, bool requireValidPath, const Path & cacheDir)
: store(store)
, requireValidPath(requireValidPath)
, cacheDir(cacheDir)
{
if (cacheDir != "")
createDirs(cacheDir);
}
Path RemoteFSAccessor::makeCacheFile(std::string_view hashPart, const std::string & ext)
{
assert(cacheDir != "");
return fmt("%s/%s.%s", cacheDir, hashPart, ext);
}
ref<SourceAccessor> RemoteFSAccessor::addToCache(std::string_view hashPart, std::string && nar)
{
if (cacheDir != "") {
try {
/* FIXME: do this asynchronously. */
writeFile(makeCacheFile(hashPart, "nar"), nar);
} catch (...) {
ignoreExceptionExceptInterrupt();
}
}
auto narAccessor = makeNarAccessor(std::move(nar));
nars.emplace(hashPart, narAccessor);
if (cacheDir != "") {
try {
nlohmann::json j = listNar(narAccessor, CanonPath::root, true);
writeFile(makeCacheFile(hashPart, "ls"), j.dump());
} catch (...) {
ignoreExceptionExceptInterrupt();
}
}
return narAccessor;
}
std::pair<ref<SourceAccessor>, CanonPath> RemoteFSAccessor::fetch(const CanonPath & path)
{
auto [storePath, restPath_] = store->toStorePath(store->storeDir + path.abs());
auto restPath = CanonPath(restPath_);
if (requireValidPath && !store->isValidPath(storePath))
throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
auto i = nars.find(std::string(storePath.hashPart()));
if (i != nars.end()) return {i->second, restPath};
std::string listing;
Path cacheFile;
if (cacheDir != "" && nix::pathExists(cacheFile = makeCacheFile(storePath.hashPart(), "nar"))) {
try {
listing = nix::readFile(makeCacheFile(storePath.hashPart(), "ls"));
auto narAccessor = makeLazyNarAccessor(listing,
[cacheFile](uint64_t offset, uint64_t length) {
AutoCloseFD fd = toDescriptor(open(cacheFile.c_str(), O_RDONLY
#ifndef _WIN32
| O_CLOEXEC
#endif
));
if (!fd)
throw SysError("opening NAR cache file '%s'", cacheFile);
if (lseek(fromDescriptorReadOnly(fd.get()), offset, SEEK_SET) != (off_t) offset)
throw SysError("seeking in '%s'", cacheFile);
std::string buf(length, 0);
readFull(fd.get(), buf.data(), length);
return buf;
});
nars.emplace(storePath.hashPart(), narAccessor);
return {narAccessor, restPath};
} catch (SystemError &) { }
try {
auto narAccessor = makeNarAccessor(nix::readFile(cacheFile));
nars.emplace(storePath.hashPart(), narAccessor);
return {narAccessor, restPath};
} catch (SystemError &) { }
}
StringSink sink;
store->narFromPath(storePath, sink);
return {addToCache(storePath.hashPart(), std::move(sink.s)), restPath};
}
std::optional<SourceAccessor::Stat> RemoteFSAccessor::maybeLstat(const CanonPath & path)
{
auto res = fetch(path);
return res.first->maybeLstat(res.second);
}
SourceAccessor::DirEntries RemoteFSAccessor::readDirectory(const CanonPath & path)
{
auto res = fetch(path);
return res.first->readDirectory(res.second);
}
std::string RemoteFSAccessor::readFile(const CanonPath & path)
{
auto res = fetch(path);
return res.first->readFile(res.second);
}
std::string RemoteFSAccessor::readLink(const CanonPath & path)
{
auto res = fetch(path);
return res.first->readLink(res.second);
}
}