diff --git a/lix/libutil/tarfile.cc b/lix/libutil/tarfile.cc index 9aa9f5680..9e38cf8aa 100644 --- a/lix/libutil/tarfile.cc +++ b/lix/libutil/tarfile.cc @@ -1,9 +1,11 @@ #include #include #include +#include #include "async-io.hh" #include "file-descriptor.hh" +#include "lix/libstore/temporary-dir.hh" #include "lix/libutil/c-calls.hh" #include "lix/libutil/charptr-cast.hh" #include "lix/libutil/file-system.hh" @@ -86,13 +88,57 @@ void TarArchive::close() check(archive_read_close(this->archive.get()), "Failed to close archive (%s)"); } +namespace { + +struct IndexEntry +{ + struct IndexDirectory + { + std::map entries; + }; + + struct IndexFile + { + uint64_t start; + uint64_t size; + }; + + std::variant innerData; + int mode; +}; + +/** Extract a TAR archive into an in-memory index with data stored on disk, + * because fundamentally all we have to do when importing archives to the store + * is to sort the entries and then send it into a NAR serializer. + * + * This deals with macOS performance problems by not materializing the files to + * disk in the first place. + * + * https://git.lix.systems/lix-project/lix/issues/1072 + */ +struct InMemoryIndex +{ + Path tempDir; + AutoDelete tempDirDeleter; + AutoCloseFD backingFile; + std::ofstream writer; + + IndexEntry topLevel; + + InMemoryIndex() : tempDir(createTempDir()), tempDirDeleter(tempDir, true) + { + backingFile = sys::open(tempDir + "/extracted", O_RDWR | O_TRUNC | O_CLOEXEC); + } +}; + +} + static void extract_archive(TarArchive & archive, const Path & destDir) { requireCString(destDir); - int flags = ARCHIVE_EXTRACT_TIME - | ARCHIVE_EXTRACT_SECURE_SYMLINKS - | ARCHIVE_EXTRACT_SECURE_NODOTDOT; + int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_SECURE_SYMLINKS + | ARCHIVE_EXTRACT_SECURE_NODOTDOT | ARCHIVE_EXTRACT_NO_HFS_COMPRESSION; for (;;) { struct archive_entry * entry;