-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
4 changed files
with
210 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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>()); |