diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2019-12-19 14:47:18 +0100 |
---|---|---|
committer | Eelco Dolstra <edolstra@gmail.com> | 2019-12-19 14:47:18 +0100 |
commit | ee235e764c6a2af1ff3ae24ce8ad9ae0e1928f39 (patch) | |
tree | 36929ecdd82a22d3eba38c31d4be11208f35e94a /src/libutil | |
parent | 9f7b4d068cc106e5d902dc6f52bf46d4a057fd00 (diff) | |
parent | f765e441237cb6679c33cb44372a5b30168c42c8 (diff) |
Merge branch 'libarchive' of https://github.com/yorickvP/nix
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/local.mk | 2 | ||||
-rw-r--r-- | src/libutil/rust-ffi.cc | 11 | ||||
-rw-r--r-- | src/libutil/rust-ffi.hh | 12 | ||||
-rw-r--r-- | src/libutil/tarfile.cc | 146 | ||||
-rw-r--r-- | src/libutil/tarfile.hh | 3 |
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); } |