archive_sys refactoring: ArchiveEntryView
All checks were successful
continuous-integration/drone/push Build is passing

Entries in libarchive archives are forward iterate only. Entries are owned by
the current archive. Reading the next header can hence invalidate the current
entry.

ArchiveEntrySys was replaced by ArchiveEntryView. ArchiveEntryView now only
guarantees a temporary view of the currently active archive entry.
This commit is contained in:
Armin Friedl 2020-02-22 20:13:32 +01:00
parent 351849e03f
commit 8249a2f017
Signed by: armin
GPG key ID: 48C726EEE7FBCBC8
4 changed files with 72 additions and 78 deletions

View file

@ -18,7 +18,7 @@ namespace logger = spdlog;
namespace xwim {
static void _spec_is_root_filename(ArchiveSpec* spec,
ArchiveEntrySys& entry,
ArchiveEntryView entry,
std::filesystem::path* filepath) {
auto entry_path = entry.path();
auto norm_stem = filepath->filename();
@ -37,7 +37,7 @@ static void _spec_is_root_filename(ArchiveSpec* spec,
logger::debug("\t-> Archive stem: {}", norm_stem.string());
}
static void _spec_is_root_dir(ArchiveSpec* spec, ArchiveEntrySys& entry) {
static void _spec_is_root_dir(ArchiveSpec* spec, ArchiveEntryView entry) {
if (entry.is_directory()) {
logger::debug("Archive root is directory");
spec->is_root_dir = true;
@ -49,16 +49,15 @@ static void _spec_is_root_dir(ArchiveSpec* spec, ArchiveEntrySys& entry) {
}
static void _spec_has_single_root(ArchiveSpec* spec,
ArchiveEntrySys& first_entry,
ArchiveEntryView first_entry,
ArchiveReaderSys& archive_reader) {
std::filesystem::path first_entry_root = *(first_entry.path().begin());
logger::trace("Testing roots");
spec->has_single_root = true;
while (true) {
ArchiveEntrySys& entry = archive_reader.next();
if(entry.is_empty()) break;
while (archive_reader.advance()) {
ArchiveEntryView entry = archive_reader.cur();
auto next_entry = entry.path();
logger::trace("Path: {}, Root: {}", next_entry.string(),
@ -92,13 +91,13 @@ ArchiveSpec Archive::check() {
ArchiveSpec archive_spec;
ArchiveEntrySys& first_entry = archive_reader.next();
if (first_entry.is_empty()) { // archive is empty
if (!archive_reader.advance()) { // can't advance even once, archive is empty
logger::debug("Archive is empty");
return {false, false, false};
}
ArchiveEntryView first_entry = archive_reader.cur();
logger::trace("Found archive entry {}", first_entry.path_name());
_spec_is_root_filename(&archive_spec, first_entry, &this->path);
@ -109,6 +108,9 @@ ArchiveSpec Archive::check() {
}
void Archive::extract(ExtractSpec extract_spec) {
std::filesystem::path abs_path = std::filesystem::absolute(this->path);
ArchiveReaderSys reader{abs_path};
ArchiveExtractorSys extractor;
if(extract_spec.make_dir) {
@ -118,7 +120,6 @@ void Archive::extract(ExtractSpec extract_spec) {
extractor = ArchiveExtractorSys{};
}
ArchiveReaderSys reader{this->path};
extractor.extract_all(reader);
}

View file

@ -8,23 +8,27 @@ namespace logger = spdlog;
#include <filesystem>
#include <memory>
bool xwim::ArchiveEntrySys::is_empty() {
return (this->ae.get() == nullptr);
bool xwim::ArchiveEntryView::is_empty() {
return (this->ae == nullptr);
}
std::string xwim::ArchiveEntrySys::path_name() {
return archive_entry_pathname(this->ae.get());
std::string xwim::ArchiveEntryView::path_name() {
if (!this->ae) throw ArchiveSysException{"Access to invalid archive entry"};
return archive_entry_pathname(this->ae);
}
std::filesystem::path xwim::ArchiveEntrySys::path() {
std::filesystem::path xwim::ArchiveEntryView::path() {
if (!this->ae) throw ArchiveSysException{"Access to invalid archive entry"};
return std::filesystem::path{this->path_name()};
}
mode_t xwim::ArchiveEntrySys::file_type() {
return archive_entry_filetype(this->ae.get());
mode_t xwim::ArchiveEntryView::file_type() {
if (!this->ae) throw ArchiveSysException{"Access to invalid archive entry"};
return archive_entry_filetype(this->ae);
}
bool xwim::ArchiveEntrySys::is_directory() {
bool xwim::ArchiveEntryView::is_directory() {
return S_ISDIR(this->file_type());
}
@ -48,24 +52,20 @@ xwim::ArchiveReaderSys::~ArchiveReaderSys() {
archive_free(this->ar);
}
xwim::ArchiveEntrySys& xwim::ArchiveReaderSys::next() {
bool xwim::ArchiveReaderSys::advance() {
int r; // libarchive error handling
logger::trace("Listing next archive entry");
archive_entry* ae;
r = archive_read_next_header(this->ar, &ae);
if (r != ARCHIVE_OK)
throw(ArchiveSysException{"Could not list archive", this->ar});
logger::trace("Advancing reader to next archive entry");
this->cur_entry = xwim::ArchiveEntrySys { ae };
r = archive_read_next_header(this->ar, &this->ae);
if (r == ARCHIVE_EOF) { this->ae = nullptr; return false; }
if (r != ARCHIVE_OK) throw(ArchiveSysException{"Could not list archive", this->ar});
logger::trace("Got archive header");
return this->cur_entry;
logger::trace("Got entry {}", archive_entry_pathname(ae));
return true;
}
xwim::ArchiveEntrySys& xwim::ArchiveReaderSys::cur() {
return this->cur_entry;
const xwim::ArchiveEntryView xwim::ArchiveReaderSys::cur() {
return ArchiveEntryView{this->ae};
}
xwim::ArchiveExtractorSys::ArchiveExtractorSys(std::filesystem::path& root) {
@ -82,12 +82,24 @@ xwim::ArchiveExtractorSys::ArchiveExtractorSys() {
}
void xwim::ArchiveExtractorSys::extract_all(xwim::ArchiveReaderSys& reader) {
for(;;) {
ArchiveEntrySys& entry = reader.next();
while(reader.advance()) {
this->extract_entry(reader);
}
}
if(entry.is_empty()) break;
// forward declared
static int copy_data(struct archive* ar, struct archive* aw);
this->extract(reader, entry);
void xwim:: ArchiveExtractorSys::extract_entry(xwim::ArchiveReaderSys& reader) {
int r;
r = archive_write_header(this->writer, reader.ae);
if (r != ARCHIVE_OK) {
throw(ArchiveSysException("Could not extract entry", reader.ar));
}
r = copy_data(reader.ar, this->writer);
if (r != ARCHIVE_OK) {
throw(ArchiveSysException("Could not extract entry", reader.ar));
}
}
@ -110,16 +122,3 @@ static int copy_data(struct archive* ar, struct archive* aw) {
}
}
}
void xwim::ArchiveExtractorSys::extract(xwim::ArchiveReaderSys& reader, xwim::ArchiveEntrySys& entry) {
int r;
r = archive_write_header(this->writer, entry.ae.get());
if (r != ARCHIVE_OK) {
throw(ArchiveSysException("Could not extract entry", reader.ar));
}
r = copy_data(reader.ar, this->writer);
if (r != ARCHIVE_OK) {
throw(ArchiveSysException("Could not extract entry", reader.ar));
}
}

View file

@ -7,22 +7,13 @@
namespace xwim {
class ArchiveEntrySys {
class ArchiveEntryView {
private:
std::function<void (archive_entry*)> ae_deleter = [](archive_entry* ae) { archive_entry_free(ae); };
std::unique_ptr<archive_entry, decltype(ae_deleter)> ae;
friend class ArchiveExtractorSys;
archive_entry* ae;
public:
ArchiveEntrySys(std::unique_ptr<archive_entry> entry)
: ae{ std::unique_ptr<archive_entry, decltype(ae_deleter)>{entry.release(), ae_deleter} }
{}
ArchiveEntrySys()
: ae{ std::unique_ptr<archive_entry, decltype(ae_deleter)>{nullptr, ae_deleter} }
{}
ArchiveEntrySys(archive_entry* entry)
: ae{ std::unique_ptr<archive_entry, decltype(ae_deleter)>{entry, ae_deleter} }
{}
ArchiveEntryView() = default;
ArchiveEntryView(archive_entry* entry) : ae{entry} {}
bool is_empty();
std::string path_name();
@ -39,24 +30,31 @@ class ArchiveEntrySys {
class ArchiveReaderSys {
private:
archive* ar;
ArchiveEntrySys cur_entry;
archive_entry* ae;
friend class ArchiveExtractorSys;
public:
ArchiveReaderSys(std::filesystem::path& path);
~ArchiveReaderSys();
/** Returns the next archive entry
/** Advances the internal entry pointer
*
* @return archive_entry or nullptr if end of archive reached
* @return true if the pointer advanced to the next entry
* false if the end of the archive was reached
*/
ArchiveEntrySys& next();
bool advance();
/** Returns the current archive entry
/** Returns a non-owning view of the current entry
*
* @return archive_entry or nullptr if current entry at end of archive
* ArchiveEntryView is a non-owning view of the currently
* active entry in this reader. A retrieved archive entry
* may not be used after another call to advance in the
* same reader.
*
* @return a view to the archive entry this reader currently
* points to
*/
ArchiveEntrySys& cur();
const ArchiveEntryView cur();
};
/** A extractor for archive files
@ -64,17 +62,15 @@ class ArchiveReaderSys {
* Shim for `libarchive`.
*/
class ArchiveExtractorSys {
private:
private:
archive* writer;
public:
public:
ArchiveExtractorSys(std::filesystem::path& root);
ArchiveExtractorSys();
void extract(ArchiveReaderSys& reader, ArchiveEntrySys& entry);
void extract_all(ArchiveReaderSys& reader);
void extract_header(ArchiveEntrySys& entry);
void extract_data();
void extract_entry(ArchiveReaderSys& reader);
};
class ArchiveSysException : public std::exception {
@ -89,9 +85,7 @@ class ArchiveSysException : public std::exception {
_what = fmt::format("{}", what);
}
}
ArchiveSysException(std::string what) {
_what = fmt::format("{}", what);
}
ArchiveSysException(std::string what) { _what = fmt::format("{}", what); }
virtual const char* what() const noexcept { return this->_what.c_str(); }
};

View file

@ -5,13 +5,13 @@ namespace logger = spdlog;
#include <iostream>
#include <ostream>
#include <string>
#include <list>
#include "archive.hpp"
#include "spec.hpp"
int main(int argc, char** argv) {
logger::set_level(logger::level::trace);
logger::flush_on(logger::level::trace);
try {
std::filesystem::path filepath{argv[1]};