├── Makefile ├── README.md ├── container.nix ├── datafiles.nix ├── default.nix ├── fusenar.c ├── fusenar_core.cpp ├── fusenar_core.h ├── hashes ├── inputs.nix ├── nar.cpp ├── nar.h ├── root.nix ├── termcolor.nix └── test.nix /Makefile: -------------------------------------------------------------------------------- 1 | CPPFLAGS=-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26 -g -std=c++11 -Wall -Werror 2 | CFLAGS=-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26 -g -Wall -Werror 3 | 4 | fusenar: fusenar.o nar.o fusenar_core.o 5 | g++ -g -o $@ $+ -lfuse -lnixstore 6 | 7 | install: fusenar 8 | mkdir -pv ${out}/bin/ 9 | cp -vi fusenar ${out}/bin/ 10 | 11 | %.o: %.c 12 | gcc ${CFLAGS} -o $@ -c $< 13 | %.o: %.cpp 14 | g++ ${CPPFLAGS} -o $@ -c $< 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | example usage: 2 | 3 | nix-build fusenar/root.nix -A guest 4 | ./result/bin/run-nixos-vm 5 | 6 | login as root/root 7 | 8 | start_container 9 | login as root/root again 10 | 11 | 12 | you are now inside a container with /nix/store mounted via fusenar 13 | 14 | if this can be finished, you will be able to p2p share your .nar files with other users, without the cost of storing the packed and unpacked form of every derivation 15 | -------------------------------------------------------------------------------- /container.nix: -------------------------------------------------------------------------------- 1 | { writeScriptBin, container_data, fusenar, systemd, tiny_container }: 2 | 3 | writeScriptBin "start_container" '' 4 | #!/bin/sh 5 | 6 | mkdir -pv /mnt/nix/store/ 7 | 8 | ${fusenar}/bin/fusenar -o allow_other -d -s -f /mnt/cache/ ${container_data} /mnt/nix/store/ > /mnt/fusenar.stdout 2>/mnt/fusenar.stderr & 9 | FUSEPID=$! 10 | 11 | sleep 5 12 | 13 | EXIT_ON_REBOOT=1 NOTIFY_SOCKET= ${systemd}/bin/systemd-nspawn -D /mnt/ -M fusenar ${tiny_container}/init 14 | '' 15 | -------------------------------------------------------------------------------- /datafiles.nix: -------------------------------------------------------------------------------- 1 | { stdenv, files, nix }: 2 | 3 | stdenv.mkDerivation { 4 | name = "data"; 5 | buildInputs = [ nix ]; 6 | unpackPhase = "true"; 7 | installPhase = '' 8 | mkdir $out 9 | export NIX_REMOTE=daemon 10 | for x in $(nix-store -qR --readonly-mode ${stdenv.lib.concatStringsSep " " files}); do nix-store --dump $x > $out/$(basename $x).nar;done 11 | ''; 12 | } 13 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { stdenv, fuse, nix, boost, termcolor }: 2 | 3 | stdenv.mkDerivation { 4 | name = "fusenar"; 5 | src = ./.; 6 | enableParallelBuilding = true; 7 | dontStrip = true; 8 | buildInputs = [ fuse nix boost termcolor ]; 9 | } 10 | -------------------------------------------------------------------------------- /fusenar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "fusenar_core.h" 8 | 9 | struct fuse_operations fusenar_oper = { 10 | .getattr = fusenar_getattr, 11 | .readlink = fusenar_readlink, 12 | .open = fusenar_open, 13 | .read = fusenar_read, 14 | .readdir = fusenar_readdir, 15 | .release = fusenar_release 16 | }; 17 | 18 | int main(int argc, char** argv) { 19 | if ((argc < 4) || (argv[argc-2][0] == '-') || (argv[argc-1][0] == '-')) { 20 | printf("%s \n",argv[0]); 21 | return -1; 22 | } 23 | 24 | struct fusenar_state fusenar_data; 25 | 26 | fusenar_data.rootdir = realpath(argv[argc-2], NULL); 27 | fusenar_data.cachedir = realpath(argv[argc-3], NULL); 28 | argv[argc-3] = argv[argc-1]; 29 | argv[argc-2] = NULL; 30 | argc--; 31 | argc--; 32 | 33 | int i; 34 | for (i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | #include "fusenar_core.h" 15 | #include "nar.h" 16 | 17 | using namespace std; 18 | using namespace termcolor; 19 | 20 | string find_nar(string prefix) { 21 | string output; 22 | struct stat statbuf2; 23 | struct fusenar_state* fusenar_data = (struct fusenar_state*)fuse_get_context()->private_data; 24 | memset(&statbuf2, 0, sizeof(struct stat)); 25 | 26 | //printf("root dir %s, prefix %s\n",fusenar_data->rootdir,prefix.c_str()); 27 | output = string(fusenar_data->rootdir) + "/" + prefix + ".nar"; 28 | //printf("final path %s\n",output.c_str()); 29 | if (stat(output.c_str(),&statbuf2) == 0) { 30 | return output; 31 | } else { 32 | return ""; 33 | } 34 | } 35 | 36 | int fusenar_readdir(const char* path_in, void* buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi) { 37 | int retstat = 0; 38 | struct fusenar_state* fusenar_data = (struct fusenar_state*)fuse_get_context()->private_data; 39 | string path_buffer1, path_buffer2; 40 | string path = path_in; 41 | 42 | printf("fusenar_readdir(path=\"%s\", fi=0x%p)\n",path_in,fi); 43 | 44 | if (path[0] != '/') { 45 | printf("error, path must begin with /\n"); 46 | return -ENOENT; 47 | } 48 | 49 | // generates a listing for the root folder 50 | if (path == "/") { 51 | DIR* dir = opendir(fusenar_data->rootdir); 52 | if (!dir) { 53 | printf("unable to open %s, %s\n",fusenar_data->rootdir,strerror(errno)); 54 | return -ENOENT; 55 | } 56 | filler(buf,".",NULL,0); 57 | filler(buf,"..",NULL,0); 58 | struct dirent* dp; 59 | while ( (dp = readdir(dir)) != NULL) { 60 | //printf("found %s\n",dp->d_name); 61 | string entry = dp->d_name; 62 | size_t pos = entry.find(".nar"); 63 | if ( (entry == ".") || (entry == "..")) { 64 | } else if ( entry.size() - 4 == pos) { 65 | //printf("its a nar file\n"); 66 | filler(buf,entry.substr(0,pos).c_str(),NULL,0); 67 | } 68 | } 69 | closedir(dir); 70 | } 71 | 72 | size_t firstslash = path.find("/",1); 73 | if (firstslash == string::npos) firstslash = path.size(); 74 | string name = path.substr(1,firstslash-1); 75 | if (name.size() > 32) { 76 | NarHandle* handle = open_nar_simple(name); 77 | assert(handle); 78 | 79 | string remainder = path.substr(firstslash); 80 | filler(buf,".",NULL,0); 81 | filler(buf,"..",NULL,0); 82 | handle->readdir(remainder, buf, filler); 83 | return 0; 84 | } 85 | if (path != "/") return -ENOENT; 86 | 87 | return retstat; 88 | } 89 | 90 | int fusenar_getattr(const char* path_in, struct stat* statbuf) { 91 | int res = 0; 92 | char path_buffer[PATH_MAX]; 93 | string path = path_in; 94 | struct fusenar_state* fusenar_data = (struct fusenar_state*)fuse_get_context()->private_data; 95 | 96 | memset(statbuf, 0, sizeof(struct stat)); 97 | 98 | if (path.size() > 32) { 99 | size_t firstslash = path.find("/",1); 100 | if (firstslash == string::npos) firstslash = path.size(); 101 | string name = path.substr(1,firstslash-1); 102 | NarHandle* handle = open_nar_simple(name); 103 | if (!handle) return -ENOENT; 104 | 105 | string remainder = path.substr(firstslash); 106 | return handle->getattr(remainder, statbuf); 107 | 108 | string narpath = find_nar(name); 109 | string cache_path = string(fusenar_data->cachedir) + "/" + name; 110 | string absolute_path = cache_path; 111 | if (path.size() > firstslash) { 112 | absolute_path = absolute_path + "/" + path.substr(firstslash); 113 | } 114 | //cout << green << "3checking cache for " << absolute_path << reset << endl; 115 | struct stat statbuf2; 116 | if (lstat(absolute_path.c_str(),&statbuf2) == 0) { 117 | statbuf->st_mode = statbuf2.st_mode; 118 | statbuf->st_size = statbuf2.st_size; 119 | return 0; 120 | } 121 | 122 | if (stat(cache_path.c_str(),&statbuf2) == 0) { 123 | // the nar has been unpacked, the file just doesnt exist 124 | return -ENOENT; 125 | } else { 126 | // the nar needs to be unpacked 127 | if (unpack_nar(narpath, cache_path) == 0) { 128 | // check again 129 | if (lstat(absolute_path.c_str(),&statbuf2) == 0) { 130 | statbuf->st_mode = statbuf2.st_mode; 131 | statbuf->st_size = statbuf2.st_size; 132 | return 0; 133 | } else return -ENOENT; 134 | } else return -EIO; 135 | } 136 | 137 | if (narpath.size() > 0) { 138 | statbuf->st_mode = S_IFDIR | 0755; 139 | statbuf->st_nlink = 2; 140 | return res; 141 | } else { 142 | printf("unable to stat %s, %s\n",path_buffer, strerror(errno)); 143 | } 144 | } 145 | 146 | if (path == "/") { 147 | statbuf->st_mode = S_IFDIR | 0755; 148 | statbuf->st_nlink = 2; 149 | } else { 150 | res = -ENOENT; 151 | } 152 | return res; 153 | } 154 | 155 | int fusenar_open(const char* path_in, struct fuse_file_info* fi) { 156 | int retstat = 0; 157 | string path = path_in; 158 | size_t firstslash = path.find("/",1); 159 | if (firstslash == string::npos) firstslash = path.size(); 160 | string name = path.substr(1,firstslash-1); 161 | NarHandle* handle = open_nar_simple(name); 162 | assert(handle); 163 | 164 | string remainder = path.substr(firstslash); 165 | FileHandle* fh; 166 | retstat = handle->open(remainder, &fh); 167 | if (retstat == 0) { 168 | fi->fh = (uint64_t)fh; 169 | return 0; 170 | } else return retstat; 171 | } 172 | 173 | int fusenar_read(const char* path, char* buf, size_t size, off_t offset, struct fuse_file_info *fi) { 174 | FileHandle* fh = (FileHandle*)fi->fh; 175 | 176 | return fh->pread(buf, size, offset); 177 | } 178 | 179 | int fusenar_release(const char* path, struct fuse_file_info *fi) { 180 | FileHandle* fh = (FileHandle*)fi->fh; 181 | 182 | delete fh; 183 | return 0; 184 | } 185 | 186 | int fusenar_readlink(const char* path_in, char* link, size_t size) { 187 | string path = path_in; 188 | size_t firstslash = path.find("/",1); 189 | if (firstslash == string::npos) firstslash = path.size(); 190 | string name = path.substr(1,firstslash-1); 191 | NarHandle* handle = open_nar_simple(name); 192 | assert(handle); 193 | 194 | string remainder = path.substr(firstslash); 195 | return handle->readlink(remainder, link, size); 196 | } 197 | -------------------------------------------------------------------------------- /fusenar_core.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | struct fusenar_state { 5 | char* rootdir; 6 | char* cachedir; 7 | }; 8 | 9 | int fusenar_readdir(const char* path, void* buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi); 10 | int fusenar_getattr(const char* path, struct stat* statbuf); 11 | int fusenar_open(const char* path_in, struct fuse_file_info* fi); 12 | int fusenar_read(const char* path, char* buf, size_t size, off_t offset, struct fuse_file_info *fi); 13 | int fusenar_readlink(const char* path, char* link, size_t size); 14 | int fusenar_release(const char* path, struct fuse_file_info *fi); 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | -------------------------------------------------------------------------------- /hashes: -------------------------------------------------------------------------------- 1 | 60b91f1875424d3b4322b0fdd0529d5d mount_point/vynqmi3mri5nk8325f9azwk2wfpqpibn-inputs-file 2 | 3d709e89c8ce201e3c928eb917989aef mount_point/20pdkab4pkcbicq7i3x2i9cbdhxjcy7g-inputs/dir/file2 3 | bbe02f946d5455d74616fc9777557c22 mount_point/20pdkab4pkcbicq7i3x2i9cbdhxjcy7g-inputs/file 4 | d380c00ea8e1d07ce1f1a86a310cd463 mount_point/20pdkab4pkcbicq7i3x2i9cbdhxjcy7g-inputs/script 5 | -------------------------------------------------------------------------------- /inputs.nix: -------------------------------------------------------------------------------- 1 | { stdenv }: 2 | 3 | stdenv.mkDerivation { 4 | name = "inputs"; 5 | unpackPhase = "true"; 6 | installPhase = '' 7 | mkdir -pv $out/dir/dir2/dir3/ 8 | echo file > $out/file 9 | echo file2 > $out/dir/file2 10 | ln -sv file $out/link 11 | #ln -sv /dev/null $link 12 | echo #!/bin/sh > $out/script 13 | echo 'echo hello world' >> $out/script 14 | chmod +x $out/script 15 | ''; 16 | outputHash = "08kh7flwc9r3ihgz288mhyvgl2nlnqjrb3dflx27mm8ffs1bkz06"; 17 | outputHashAlgo = "sha256"; 18 | outputHashMode = "recursive"; 19 | } 20 | -------------------------------------------------------------------------------- /nar.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace std; 14 | 15 | #include "nar.h" 16 | 17 | 18 | void NarIndexer::createDirectory(const nix::Path & path) { 19 | members.emplace(path, NarMember{NarMember::Type::tDirectory, false, 0, 0}); 20 | } 21 | void NarIndexer::createRegularFile(const nix::Path & path) { 22 | currentPath = path; 23 | isExec = false; 24 | } 25 | void NarIndexer::isExecutable() { 26 | isExec = true; 27 | } 28 | void NarIndexer::preallocateContents(unsigned long long size) { 29 | members.emplace(currentPath, NarMember{NarMember::Type::tRegular, isExec, source->position, size}); 30 | } 31 | void NarIndexer::receiveContents(unsigned char * data, unsigned int len) { 32 | if (!currentStart.empty()) { 33 | assert(len < 16 || currentStart == string((char *) data, 16)); 34 | currentStart.clear(); 35 | } 36 | } 37 | void NarIndexer::createSymlink(const nix::Path & path, const string & target) { 38 | members.emplace(path, NarMember{NarMember::Type::tSymlink, false, 0, target.size(), target}); 39 | } 40 | NarHandle::NarHandle(const string nar) : source(nar) { 41 | cout << "indexing nar file:" << nar << endl; 42 | if (source.open()) throw OpenNarFail(); 43 | indexer.source = &source; 44 | parseDump(indexer, source); 45 | } 46 | MMapSource::MMapSource(string file_in) : file(file_in) { 47 | address = 0; 48 | size = 0; 49 | } 50 | MMapSource::~MMapSource() { 51 | munmap(address, size); 52 | } 53 | bool MMapSource::open() { 54 | struct stat statbuf; 55 | int fd = ::open(file.c_str(), O_RDONLY); 56 | int err; 57 | if (fd < 0) { 58 | cerr << "unable to open " << file << endl; 59 | return true; 60 | } 61 | 62 | if (fstat(fd, &statbuf)) { 63 | err = errno; 64 | cerr << "unable to stat " << fd << " " << err << " " << strerror(err) << " " << file << endl; 65 | close(fd); 66 | return true; 67 | } 68 | size = statbuf.st_size; 69 | //cout << "file size is " << size << endl; 70 | position = 0; 71 | address = (uint8_t*) mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); 72 | if (address == MAP_FAILED) { 73 | close(fd); 74 | return true; 75 | } 76 | close(fd); 77 | return false; 78 | } 79 | size_t MMapSource::read(unsigned char* data, size_t len) { 80 | int available = std::min(size - position,len); 81 | memcpy(data, address + position, available); 82 | position += available; 83 | return available; 84 | } 85 | class NarExtractor : public nix::ParseSink { 86 | string root; 87 | FILE *currentFile; 88 | unsigned long long bytes_remaining; 89 | bool isExec; 90 | public: 91 | NarExtractor(string nar, string out) { 92 | root = out; 93 | //int fd = open(nar.c_str() ,O_RDONLY); 94 | //nix::FdSource handle(fd); 95 | MMapSource handle(nar); 96 | if (handle.open()) throw OpenNarFail(); 97 | parseDump(*this,handle); 98 | //close(fd); 99 | } 100 | void createDirectory(const nix::Path & path) override { 101 | string target = root + path; 102 | mkdir(target.c_str(),0700); 103 | } 104 | void createRegularFile(const nix::Path & path) override { 105 | string target = root + path; 106 | currentFile = fopen(target.c_str(), "w"); 107 | if (!currentFile) throw OpenFail(); 108 | //cout << "making " << target << endl; 109 | isExec = false; 110 | } 111 | void isExecutable() override { 112 | isExec = true; 113 | puts("it is executable"); 114 | } 115 | void preallocateContents(unsigned long long size) override { 116 | ftruncate(fileno(currentFile), size); 117 | //cout << "size set to " << size << endl; 118 | bytes_remaining = size; 119 | } 120 | void receiveContents(unsigned char * data, unsigned int len) override { 121 | //cout << len << " bytes added" << endl; 122 | fwrite(data,len,1,currentFile); 123 | bytes_remaining = bytes_remaining - len; 124 | if (bytes_remaining == 0) { 125 | if (isExec) { 126 | fchmod(fileno(currentFile), 0555); 127 | } else { 128 | fchmod(fileno(currentFile), 0444); 129 | } 130 | fclose(currentFile); 131 | } 132 | } 133 | void createSymlink(const nix::Path & path, const string & target) override { 134 | //printf("symlink %s -> %s\n",path.c_str(), target.c_str()); 135 | symlink(target.c_str(), (root + path).c_str()); 136 | } 137 | }; 138 | 139 | typedef map NarCache; 140 | typedef map::iterator NarCacheIt; 141 | NarCache open_nars; 142 | struct NarHandle* open_nar(const string path) { 143 | NarCacheIt it = open_nars.find(path); 144 | if (it != open_nars.end()) { 145 | return it->second; 146 | } 147 | NarHandle* handle = new NarHandle(path); 148 | open_nars[path] = handle; 149 | return handle; 150 | } 151 | struct NarHandle* open_nar_simple(const string path) { 152 | string narpath = find_nar(path); 153 | if (narpath.size() > 0) return open_nar(narpath); 154 | else return NULL; 155 | } 156 | int unpack_nar(string narpath, string outpath) { 157 | try { 158 | printf("unpacking %s to %s\n",narpath.c_str(),outpath.c_str()); 159 | NarExtractor handle(narpath,outpath); 160 | return 0; 161 | } catch (OpenFail of) { 162 | return -1; 163 | } 164 | } 165 | void NarHandle::readdir(string path, void* buf, fuse_fill_dir_t filler) { 166 | NarIndexer::Members::iterator it; 167 | set results; 168 | cout << "readdir '" << path << "'(" << path.size() << ")" << endl; 169 | 170 | size_t offset = max((int)path.size()+1,1); 171 | 172 | for (it = indexer.members.begin(); it != indexer.members.end(); ++it) { 173 | if (it->first.size() == 0) continue; 174 | 175 | if (it->first.substr(0,path.size()) != path) { 176 | cout << "skipping:" << it->first << endl; 177 | continue; 178 | } 179 | 180 | size_t pos = it->first.find("/",offset); 181 | if (pos == string::npos) pos = it->first.size(); 182 | else pos -= 1; 183 | 184 | if (offset > it->first.size()) continue; 185 | 186 | string fragment = it->first.substr(offset, (pos - offset)+1); 187 | 188 | results.emplace(fragment); 189 | 190 | cout << "pos:" << pos << " full:" << it->first << " fragment:" << fragment << endl; 191 | } 192 | set::iterator it2; 193 | for (it2 = results.begin(); it2 != results.end(); ++it2) { 194 | filler(buf, it2->c_str(), NULL, 0); 195 | } 196 | } 197 | int NarHandle::getattr(std::string path, struct stat *statbuf) { 198 | NarIndexer::Members::iterator it; 199 | it = indexer.members.find(path); 200 | if (it == indexer.members.end()) return -ENOENT; 201 | else { 202 | switch (it->second.type) { 203 | case NarMember::tRegular: 204 | if (it->second.isExecutable) { 205 | statbuf->st_mode = S_IFREG | 0555; 206 | } else { 207 | statbuf->st_mode = S_IFREG | 0444; 208 | } 209 | statbuf->st_size = it->second.size; 210 | break; 211 | case NarMember::tSymlink: 212 | statbuf->st_mode = S_IFLNK | 0777; 213 | statbuf->st_size = it->second.size; 214 | break; 215 | case NarMember::tDirectory: 216 | statbuf->st_mode = S_IFDIR | 0555; 217 | break; 218 | default: 219 | cout << "fault " << it->second.type << endl; 220 | return -ENOENT; 221 | } 222 | return 0; 223 | } 224 | for (it = indexer.members.begin(); it != indexer.members.end(); ++it) { 225 | cout << "'" << it->first << "' " << it->second.type << endl; 226 | } 227 | } 228 | int NarHandle::readlink(std::string path, char* link, size_t size) { 229 | NarIndexer::Members::iterator it; 230 | it = indexer.members.find(path); 231 | if (it == indexer.members.end()) return -ENOENT; 232 | else if (it->second.type != NarMember::tSymlink) return -EINVAL; 233 | else { 234 | int size2 = it->second.target.copy(link,size); 235 | link[size2] = 0; 236 | return 0; 237 | } 238 | } 239 | int NarHandle::open(std::string path, FileHandle** fh) { 240 | NarIndexer::Members::iterator it; 241 | it = indexer.members.find(path); 242 | if (it == indexer.members.end()) return -ENOENT; 243 | else if (it->second.type != NarMember::tRegular) return -EINVAL; 244 | else { 245 | *fh = new FileHandle(&source, it->second); 246 | return 0; 247 | } 248 | } 249 | FileHandle::FileHandle(MMapSource* source, NarMember file): source(source), file(file) { 250 | } 251 | int FileHandle::pread(char* buf, size_t size, off_t offset) { 252 | size_t available = min(size, file.size - offset); 253 | memcpy(buf, source->addr() + file.start + offset, available); 254 | return available; 255 | } 256 | -------------------------------------------------------------------------------- /nar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | class MMapSource : public nix::Source { 10 | public: 11 | MMapSource(std::string file); 12 | ~MMapSource(); 13 | virtual size_t read(unsigned char* data, size_t len); 14 | bool open(); 15 | uint8_t* addr() { return address; } 16 | 17 | size_t position; 18 | private: 19 | uint8_t* address; 20 | size_t size; 21 | std::string file; 22 | }; 23 | struct NarMember { 24 | enum Type { tMissing, tRegular, tSymlink, tDirectory }; 25 | 26 | Type type; 27 | 28 | bool isExecutable; 29 | 30 | size_t start, size; 31 | 32 | std::string target; 33 | }; 34 | class FileHandle { 35 | public: 36 | FileHandle(MMapSource* source, NarMember file); 37 | int pread(char* buf, size_t size, off_t offset); 38 | private: 39 | MMapSource* source; 40 | NarMember file; 41 | }; 42 | class NarIndexer : public nix::ParseSink { 43 | public: 44 | void createDirectory(const nix::Path & path) override; 45 | void createRegularFile(const nix::Path & path) override; 46 | void isExecutable() override; 47 | void preallocateContents(unsigned long long size) override; 48 | void receiveContents(unsigned char * data, unsigned int len) override; 49 | void createSymlink(const nix::Path & path, const std::string & target) override; 50 | 51 | typedef std::map Members; 52 | 53 | Members members; 54 | nix::Path currentPath; 55 | std::string currentStart; 56 | bool isExec; 57 | MMapSource* source; 58 | }; 59 | class NarHandle { 60 | NarIndexer indexer; 61 | MMapSource source; 62 | public: 63 | NarHandle(std::string narpath); 64 | void readdir(std::string path, void* buf, fuse_fill_dir_t filler); 65 | int getattr(std::string path, struct stat *statbuf); 66 | int readlink(std::string path, char* link, size_t size); 67 | int open(std::string path, FileHandle** fh); 68 | }; 69 | 70 | #ifdef __cplusplus 71 | extern "C" { 72 | #endif 73 | struct NarHandle* open_nar(const char* path); 74 | typedef void (*Filler)(const char* entry, int isDir, int isExec); 75 | void nar_list(const char* narpath, const char* dir, Filler filler); 76 | #ifdef __cplusplus 77 | } 78 | #endif 79 | int unpack_nar(std::string narpath, std::string outpath); 80 | struct NarHandle* open_nar(const std::string path); 81 | struct NarHandle* open_nar_simple(const std::string path); 82 | std::string find_nar(const std::string prefix); 83 | 84 | class OpenFail : public std::exception { 85 | public: 86 | const char* what() const noexcept { return "unable to open target file"; }; 87 | }; 88 | class OpenNarFail : public std::exception { 89 | public: 90 | const char* what() const noexcept { return "unable to open target file"; }; 91 | }; 92 | -------------------------------------------------------------------------------- /root.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import {}; 3 | callPackage = pkgs.newScope self; 4 | self = rec { 5 | termcolor = callPackage ./termcolor.nix {}; 6 | fusenar = callPackage ./default.nix {}; 7 | inputs = callPackage ./inputs.nix {}; 8 | data_files = callPackage ./datafiles.nix { files = [ inputs inputs.file tiny_container_bare.config.system.build.etc ]; }; 9 | do_test = callPackage ./test.nix {}; 10 | tiny_container_bare = (import { 11 | configuration = { ... }: 12 | { 13 | boot.isContainer = true; 14 | nix.readOnlyStore = false; 15 | users.users.root.password = "root"; 16 | networking.hostName = "fuse-container"; 17 | }; 18 | }); 19 | tiny_container = tiny_container_bare.config.system.build.toplevel; 20 | container_data = callPackage ./datafiles.nix { files = [ tiny_container ]; }; 21 | container = callPackage ./container.nix {}; 22 | 23 | guest = (import { 24 | configuration = { ... }: 25 | { 26 | imports = [ ]; 27 | environment.systemPackages = [ container ]; 28 | users.users.root.password = "root"; 29 | networking.hostName = "qemu-host"; 30 | }; 31 | }).config.system.build.vm; 32 | }; 33 | in self 34 | -------------------------------------------------------------------------------- /termcolor.nix: -------------------------------------------------------------------------------- 1 | { stdenv, cmake, fetchFromGitHub }: 2 | 3 | stdenv.mkDerivation { 4 | name = "termcolor"; 5 | src = fetchFromGitHub { 6 | owner = "ikalnitsky"; 7 | repo = "termcolor"; 8 | rev = "bfa523101efbdd48ce26e05244bd6f81f7cf1fae"; 9 | sha256 = "1x41q5klp90ijngaas6rmgimp3859qlv89fr93w00yzvlhvwm013"; 10 | }; 11 | buildInputs = [ cmake ]; 12 | } 13 | -------------------------------------------------------------------------------- /test.nix: -------------------------------------------------------------------------------- 1 | { stdenv, data_files, fusenar, strace, utillinux, valgrind }: 2 | 3 | stdenv.mkDerivation { 4 | name = "fusenar-test"; 5 | buildInputs = [ fusenar strace utillinux valgrind ]; 6 | unpackPhase = "true"; 7 | installPhase = '' 8 | function finish { 9 | umount -l mount_point 10 | kill $fusenarpid 11 | #ls -ltrh cache_dir 12 | exit 1 13 | } 14 | trap finish EXIT 15 | mkdir $out 16 | pwd 17 | mkdir cache_dir 18 | mkdir mount_point 19 | fusenar -s -f cache_dir ${data_files} mount_point & 20 | fusenarpid=$! 21 | sleep 2 22 | echo started fusenar pid $fusenarpid 23 | 24 | #echo listing 25 | # ls -ltrha mount_point/jkdplzhfbacw7fpa76v4mzh9q0ac4myc-inputs/ mount_point/1cxxc7fzlnqk7h6pi0rm3g84jp5ih019-inputs-file 26 | #echo listed 27 | # find mount_point -type f -print0 | xargs -0 md5sum > $out/hashes 28 | 29 | md5sum --check ${./hashes} 30 | 31 | ls -lh mount_point/nl3xpfypdsx36n99vgfa25f682gwqq67-etc/etc/systemd/ 32 | 33 | #stat mount_point/1cxxc7fzlnqk7h6pi0rm3g84jp5ih019-inputs-file 34 | 35 | kill $fusenarpid 36 | 37 | trap - EXIT 38 | ''; 39 | } 40 | --------------------------------------------------------------------------------