summaryrefslogtreecommitdiffstats
path: root/src/libutil
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/local.mk2
-rw-r--r--src/libutil/rust-ffi.cc11
-rw-r--r--src/libutil/rust-ffi.hh12
-rw-r--r--src/libutil/tarfile.cc146
-rw-r--r--src/libutil/tarfile.hh3
5 files changed, 122 insertions, 52 deletions
diff --git a/src/libutil/local.mk b/src/libutil/local.mk
index 35c1f6c13..16c1fa03f 100644
--- a/src/libutil/local.mk
+++ b/src/libutil/local.mk
@@ -6,6 +6,6 @@ libutil_DIR := $(d)
libutil_SOURCES := $(wildcard $(d)/*.cc)
-libutil_LDFLAGS = $(LIBLZMA_LIBS) -lbz2 -pthread $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(BOOST_LDFLAGS) -lboost_context
+libutil_LDFLAGS = $(LIBLZMA_LIBS) -lbz2 -pthread $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context
libutil_LIBS = libnixrust
diff --git a/src/libutil/rust-ffi.cc b/src/libutil/rust-ffi.cc
index 8b8b7b75d..6f36b3192 100644
--- a/src/libutil/rust-ffi.cc
+++ b/src/libutil/rust-ffi.cc
@@ -19,15 +19,4 @@ std::ostream & operator << (std::ostream & str, const String & s)
return str;
}
-size_t Source::sourceWrapper(void * _this, rust::Slice<uint8_t> data)
-{
- try {
- // FIXME: how to propagate exceptions?
- auto n = ((nix::Source *) _this)->read((unsigned char *) data.ptr, data.size);
- return n;
- } catch (...) {
- abort();
- }
-}
-
}
diff --git a/src/libutil/rust-ffi.hh b/src/libutil/rust-ffi.hh
index 3b51661c2..469a5fba3 100644
--- a/src/libutil/rust-ffi.hh
+++ b/src/libutil/rust-ffi.hh
@@ -131,18 +131,6 @@ struct String : Vec<char, ffi_String_drop>
std::ostream & operator << (std::ostream & str, const String & s);
-struct Source
-{
- size_t (*fun)(void * source_this, rust::Slice<uint8_t> data);
- nix::Source * _this;
-
- Source(nix::Source & _this)
- : fun(sourceWrapper), _this(&_this)
- {}
-
- static size_t sourceWrapper(void * _this, rust::Slice<uint8_t> data);
-};
-
/* C++ representation of Rust's Result<T, CppException>. */
template<typename T>
struct Result
diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc
index 1be0ba24c..68e918891 100644
--- a/src/libutil/tarfile.cc
+++ b/src/libutil/tarfile.cc
@@ -1,38 +1,132 @@
-#include "rust-ffi.hh"
-#include "compression.hh"
+#include <archive.h>
+#include <archive_entry.h>
-extern "C" {
- rust::Result<std::tuple<>> *
- unpack_tarfile(rust::Source source, rust::StringSlice dest_dir, rust::Result<std::tuple<>> & out);
-}
+#include "serialise.hh"
namespace nix {
+struct TarArchive {
+ struct archive *archive;
+ Source *source;
+ std::vector<unsigned char> buffer;
+
+ void check(int err, const char *reason = "Failed to extract archive (%s)") {
+ if (err == ARCHIVE_EOF)
+ throw EndOfFile("reached end of archive");
+ else if (err != ARCHIVE_OK)
+ throw Error(reason, archive_error_string(this->archive));
+ }
+
+ TarArchive(Source& source) : buffer(4096) {
+ this->archive = archive_read_new();
+ this->source = &source;
+
+ archive_read_support_filter_all(archive);
+ archive_read_support_format_all(archive);
+ check(archive_read_open(archive, (void *)this, TarArchive::callback_open, TarArchive::callback_read, TarArchive::callback_close), "Failed to open archive (%s)");
+ }
+
+ TarArchive(const Path &path) {
+ this->archive = archive_read_new();
+
+ archive_read_support_filter_all(archive);
+ archive_read_support_format_all(archive);
+ check(archive_read_open_filename(archive, path.c_str(), 16384), "Failed to open archive (%s)");
+ }
+
+ // disable copy constructor
+ TarArchive(const TarArchive&) = delete;
+
+ void close() {
+ check(archive_read_close(archive), "Failed to close archive (%s)");
+ }
+
+ ~TarArchive() {
+ if (this->archive) archive_read_free(this->archive);
+ }
+
+private:
+ static int callback_open(struct archive *, void *self) {
+ return ARCHIVE_OK;
+ }
+
+ static ssize_t callback_read(struct archive *archive, void *_self, const void **buffer) {
+ TarArchive *self = (TarArchive *)_self;
+ *buffer = self->buffer.data();
+
+ try {
+ return self->source->read(self->buffer.data(), 4096);
+ } catch (EndOfFile &) {
+ return 0;
+ } catch (std::exception &err) {
+ archive_set_error(archive, EIO, "Source threw exception: %s", err.what());
+
+ return -1;
+ }
+ }
+
+ static int callback_close(struct archive *, void *self) {
+ return ARCHIVE_OK;
+ }
+};
+
+struct PushD {
+ char * oldDir;
+
+ PushD(const std::string &newDir) {
+ oldDir = getcwd(0, 0);
+ if (!oldDir) throw SysError("getcwd");
+ int r = chdir(newDir.c_str());
+ if (r != 0) throw SysError("changing directory to tar output path");
+ }
+
+ ~PushD() {
+ int r = chdir(oldDir);
+ free(oldDir);
+ if (r != 0)
+ std::cerr << "warning: failed to change directory back after tar extraction";
+ /* can't throw out of a destructor */
+ }
+};
+
+static void extract_archive(TarArchive &archive, const Path & destDir) {
+ // need to chdir back *after* archive closing
+ PushD newDir(destDir);
+ struct archive_entry *entry;
+ int flags = ARCHIVE_EXTRACT_FFLAGS
+ | ARCHIVE_EXTRACT_PERM
+ | ARCHIVE_EXTRACT_SECURE_SYMLINKS
+ | ARCHIVE_EXTRACT_SECURE_NODOTDOT
+ | ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS;
+
+ for(;;) {
+ int r = archive_read_next_header(archive.archive, &entry);
+ if (r == ARCHIVE_EOF) break;
+ else if (r == ARCHIVE_WARN)
+ std::cerr << "warning: " << archive_error_string(archive.archive) << std::endl;
+ else
+ archive.check(r);
+
+ archive.check(archive_read_extract(archive.archive, entry, flags));
+ }
+
+ archive.close();
+}
+
void unpackTarfile(Source & source, const Path & destDir)
{
- rust::Source source2(source);
- rust::Result<std::tuple<>> res;
- unpack_tarfile(source2, destDir, res);
- res.unwrap();
+ auto archive = TarArchive(source);
+
+ createDirs(destDir);
+ extract_archive(archive, destDir);
}
-void unpackTarfile(const Path & tarFile, const Path & destDir,
- std::optional<std::string> baseName)
+void unpackTarfile(const Path & tarFile, const Path & destDir)
{
- if (!baseName) baseName = std::string(baseNameOf(tarFile));
-
- auto source = sinkToSource([&](Sink & sink) {
- // FIXME: look at first few bytes to determine compression type.
- auto decompressor =
- hasSuffix(*baseName, ".bz2") ? makeDecompressionSink("bzip2", sink) :
- hasSuffix(*baseName, ".gz") ? makeDecompressionSink("gzip", sink) :
- hasSuffix(*baseName, ".xz") ? makeDecompressionSink("xz", sink) :
- makeDecompressionSink("none", sink);
- readFile(tarFile, *decompressor);
- decompressor->finish();
- });
-
- unpackTarfile(*source, destDir);
+ auto archive = TarArchive(tarFile);
+
+ createDirs(destDir);
+ extract_archive(archive, destDir);
}
}
diff --git a/src/libutil/tarfile.hh b/src/libutil/tarfile.hh
index ce0911e2a..89a024f1d 100644
--- a/src/libutil/tarfile.hh
+++ b/src/libutil/tarfile.hh
@@ -4,7 +4,6 @@ namespace nix {
void unpackTarfile(Source & source, const Path & destDir);
-void unpackTarfile(const Path & tarFile, const Path & destDir,
- std::optional<std::string> baseName = {});
+void unpackTarfile(const Path & tarFile, const Path & destDir);
}