After a build has completed, Nix sometimes needs to rewrite hashes in the result, e.g.,:
rewriting hashes in '/nix/store/2sp9hma57rj2f0drhyikab993jkqa3px-bionix-query-tmb-csv'; cross fingers
This is currently done in ram, which causes issues when using BioNix as some of these outputs are too large to fit in ram, such as BAM files. I patched Nix to instead do the hash rewriting on disk via a memory map:
diff --git a/src/libstore/build.cc b/src/libstore/build.cc index dd932cee9..c1e9f8ac3 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -3705,18 +3705,45 @@ void DerivationGoal::registerOutputs() .hint = hintfmt("rewriting hashes in '%1%'; cross fingers", path) }); + + /* Canonicalise first. This ensures that the path we're rewriting doesn't contain a hard link to /etc/shadow or something like that. */ canonicalisePathMetaData(actualPath, buildUser ? buildUser->getUID() : -1, inodesSeen); - /* FIXME: this is in-memory. */ - StringSink sink; - dumpPath(actualPath, sink); - deletePath(actualPath); - sink.s = make_ref<std::string>(rewriteStrings(*sink.s, outputRewrites)); - StringSource source(*sink.s); - restorePath(actualPath, source); + AutoDelete tmpDir(createTempDir(), true); + Path tmpFile = (Path) tmpDir + "/rewrite"; + { + AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0600); + if(!fd) throw SysError("creating temporary file '%s'", tmpFile); + FdSink sink(fd.get()); + dumpPath(actualPath, sink); + deletePath(actualPath); + } + + { + AutoCloseFD fd = open(tmpFile.c_str(), O_RDWR); + if(!fd) throw SysError("Opening temporary file '%s'", tmpFile); + struct stat stat_buf; + if(fstat(fd.get(), &stat_buf) == -1) throw SysError("fstat: '%s'", tmpFile); + void *ptr = mmap(NULL, stat_buf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0); + if(!ptr) throw SysError("mmap: '%s'", tmpFile); + for(auto & i : outputRewrites) { + if(i.first == i.second) continue; + void *j; + while((j = memmem(ptr, stat_buf.st_size, i.first.c_str(), i.first.size()))) + memcpy(j, i.second.c_str(), i.first.size()); + } + munmap(ptr, stat_buf.st_size); + } + + { + AutoCloseFD fd = open(tmpFile.c_str(), O_RDONLY); + if(!fd) throw SysError("Opening temporary file '%s'", tmpFile); + FdSource source(fd.get()); + restorePath(actualPath, source); + } rewritten = true; }