mirror of
https://github.com/NixOS/nix.git
synced 2024-11-21 22:32:26 +00:00
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:
parent
62e214fa6f
commit
a9cbd67f90
2
local.mk
2
local.mk
@ -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)))
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
207
src/nix/mount.cc
Normal 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>());
|
Loading…
Reference in New Issue
Block a user