Add "nix mount-store" command

This mounts an arbitrary Nix store on the specified mount point.

Typical usage:

  $ /nix/store/d0am5d8gwh2kfdcgyxh4y684mb5b2v54-blender-2.79/bin/blender --version
  bash: /nix/store/d0am5d8gwh2kfdcgyxh4y684mb5b2v54-blender-2.79/bin/blender: No such file or directory

  $ nix mount-store /tmp/mp --store https://cache.nixos.org?local-nar-cache=/tmp/nars

  $ unshare -m -r

  $ mount -o bind /tmp/mp /nix/store

  $ /nix/store/d0am5d8gwh2kfdcgyxh4y684mb5b2v54-blender-2.79/bin/blender --version
  [after a lot of downloading...]
  Blender 2.79 (sub 0)

One application is to replace the current remote store file access in
hydra-server implemented via "nix {cat,ls}-store", which doesn't work
all that well (e.g. it doesn't resolve symlinks properly).

Another application would be on-demand fetching of build inputs on
Hydra build slaves (to speed up builds that don't access their entire
closure). However, that will require a lot more machinery.
This commit is contained in:
Eelco Dolstra 2017-12-22 00:12:19 +01:00
parent 62e214fa6f
commit a9cbd67f90
5 changed files with 212 additions and 3 deletions

View File

@ -7,7 +7,7 @@ dist-files += configure config.h.in nix.spec perl/configure
clean-files += Makefile.config
GLOBAL_CXXFLAGS += -I . -I src -I src/libutil -I src/libstore -I src/libmain -I src/libexpr
GLOBAL_CXXFLAGS += -I . -I src -I src/libutil -I src/libstore -I src/libmain -I src/libexpr -D_FILE_OFFSET_BITS=64
$(foreach i, config.h $(call rwildcard, src/lib*, *.hh), \
$(eval $(call install-file-in, $(i), $(includedir)/nix, 0644)))

View File

@ -79,7 +79,7 @@ let
git
mercurial
]
++ lib.optional stdenv.isLinux libseccomp
++ lib.optionals stdenv.isLinux [ libseccomp fuse ]
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
++ lib.optional (stdenv.isLinux || stdenv.isDarwin)
(aws-sdk-cpp.override {

View File

@ -27,7 +27,7 @@ with import ./release-common.nix { inherit pkgs; };
git
mercurial
]
++ lib.optional stdenv.isLinux libseccomp;
++ lib.optionals stdenv.isLinux [ libseccomp fuse ];
inherit configureFlags;

View File

@ -6,4 +6,6 @@ nix_SOURCES := $(wildcard $(d)/*.cc) $(wildcard src/linenoise/*.cpp)
nix_LIBS = libexpr libmain libstore libutil libformat
nix_LDFLAGS = -lfuse
$(eval $(call install-symlink, nix, $(bindir)/nix-hash))

207
src/nix/mount.cc Normal file
View File

@ -0,0 +1,207 @@
#include "command.hh"
#include "store-api.hh"
#include "fs-accessor.hh"
#include "nar-accessor.hh"
#define FUSE_USE_VERSION 30
#include <fuse.h>
#include <cstring>
using namespace nix;
std::shared_ptr<Store> store;
std::shared_ptr<FSAccessor> accessor;
static int op_getattr(const char * path_, struct stat * stbuf)
{
try {
Path path(path_);
memset(stbuf, 0, sizeof(struct stat));
stbuf->st_uid = 0;
stbuf->st_gid = 0;
stbuf->st_nlink = 1;
if (path == "/") {
stbuf->st_mode = S_IFDIR | 0111;
} else {
auto st = accessor->stat(store->storeDir + path);
switch (st.type) {
case FSAccessor::tRegular:
stbuf->st_mode = S_IFREG | (st.isExecutable ? 0555 : 0444);
stbuf->st_size = st.fileSize;
break;
case FSAccessor::tSymlink:
stbuf->st_mode = S_IFLNK | 0777;
break;
case FSAccessor::tDirectory:
stbuf->st_mode = S_IFDIR | 0555;
break;
default:
return -ENOENT;
}
}
return 0;
} catch (...) {
ignoreException();
return -EIO;
}
}
static int op_readdir(const char * path_, void * buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info * fi)
{
try {
Path path(path_);
if (path == "/")
/* FIXME: could use queryAllValidPaths(), but it will be
superslow for binary caches, and won't include name
parts. */
return 0;
auto st = accessor->stat(store->storeDir + path);
if (st.type == FSAccessor::tMissing) return -ENOENT;
if (st.type != FSAccessor::tDirectory) return -ENOTDIR;
for (auto & entry : accessor->readDirectory(store->storeDir + path))
filler(buf, entry.c_str(), nullptr, 0);
return 0;
} catch (...) {
ignoreException();
return -EIO;
}
}
static int op_open(const char * path_, struct fuse_file_info * fi)
{
try {
Path path(path_);
auto st = accessor->stat(store->storeDir + path);
if (st.type == FSAccessor::tMissing) return -ENOENT;
if (st.type == FSAccessor::tDirectory) return -EISDIR;
if (st.type != FSAccessor::tRegular) return -EINVAL;
return 0;
} catch (...) {
ignoreException();
return -EIO;
}
}
static int op_read(const char * path_, char * buf, size_t size, off_t offset,
struct fuse_file_info * fi)
{
try {
Path path(path_);
// FIXME: absolutely need to cache this and/or provide random
// access.
auto s = accessor->readFile(store->storeDir + path);
if (offset >= (off_t) s.size()) return 0;
if (offset + size > s.size())
size = s.size() - offset;
memcpy(buf, s.data() + offset, size);
return size;
} catch (...) {
ignoreException();
return -EIO;
}
}
static int op_readlink(const char * path_, char * buf, size_t size)
{
try {
Path path(path_);
auto st = accessor->stat(store->storeDir + path);
if (st.type == FSAccessor::tMissing) return -ENOENT;
if (st.type != FSAccessor::tSymlink) return -EINVAL;
auto s = accessor->readLink(store->storeDir + path);
if (s.size() >= size) return ENAMETOOLONG; // FIXME
strncpy(buf, s.c_str(), size);
return 0;
} catch (...) {
ignoreException();
return -EIO;
}
}
struct CmdMountStore : StoreCommand
{
Path mountPoint;
CmdMountStore()
{
expectArg("mount-point", &mountPoint);
}
std::string name() override
{
return "mount-store";
}
std::string description() override
{
return "mount a Nix store as a FUSE file system";
}
void run(ref<Store> store) override
{
::store = store;
accessor = store->getFSAccessor();
Strings fuseArgs = { "nix", mountPoint, "-o", "debug" };
auto fuseArgs2 = stringsToCharPtrs(fuseArgs);
struct fuse * fuse;
char * mountpoint;
int multithreaded;
fuse_operations oper;
memset(&oper, 0, sizeof(oper));
oper.getattr = op_getattr;
oper.readdir = op_readdir;
oper.open = op_open;
oper.read = op_read;
oper.readlink = op_readlink;
fuse = fuse_setup(fuseArgs2.size() - 1, fuseArgs2.data(),
&oper, sizeof(oper),
&mountpoint, &multithreaded, nullptr);
if (!fuse) throw Error("FUSE setup failed");
if (multithreaded)
fuse_loop_mt(fuse);
else
fuse_loop(fuse);
fuse_teardown(fuse, mountpoint);
}
};
static RegisterCommand r(make_ref<CmdMountStore>());