#include "../include/Archive.hpp" #include "../include/utils.hpp" #include #include #include using namespace std; #if OS_FAMILY == OS_FAMILY_POSIX #define archive_read_open_filename_native archive_read_open_filename #define archive_entry_pathname_native archive_entry_pathname #else #define archive_read_open_filename_native archive_read_open_filename_w #define archive_entry_pathname_native archive_entry_pathname_w #endif class FileHandler { DISABLE_COPY(FileHandler) public: FileHandler(FILE *_file) : file(_file) { } FileHandler(FileHandler &&other) { file = other.file; other.file = nullptr; } ~FileHandler() { fclose(file); } FILE *file; }; // libarchive code is modified version of: https://github.com/libarchive/libarchive/wiki/Examples#A_Complete_Extractor namespace sibs { static int copy_data(struct archive *ar, struct archive *aw) { int r; const void *buff; size_t size; int64_t offset; while(true) { r = archive_read_data_block(ar, &buff, &size, &offset); if (r == ARCHIVE_EOF) return ARCHIVE_OK; else if (r < ARCHIVE_OK) return r; r = archive_write_data_block(aw, buff, size, offset); if (r < ARCHIVE_OK) return r; } } Result Archive::extract(const _tinydir_char_t *source, const _tinydir_char_t *destination) { struct archive *a; struct archive *ext; struct archive_entry *entry; int flags; int r; /* Select which attributes we want to restore. */ flags = ARCHIVE_EXTRACT_TIME; flags |= ARCHIVE_EXTRACT_PERM; flags |= ARCHIVE_EXTRACT_ACL; flags |= ARCHIVE_EXTRACT_FFLAGS; FileString rootName; a = archive_read_new(); archive_read_support_format_all(a); archive_read_support_compression_all(a); ext = archive_write_disk_new(); archive_write_disk_set_options(ext, flags); archive_write_disk_set_standard_lookup(ext); if ((r = archive_read_open_filename_native(a, source, 10240))) { string errMsg = "Failed to extract archive: "; errMsg += toUtf8(source); return Result::Err(errMsg); } while(true) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; else if (r < ARCHIVE_OK) { string errMsg = "Failed to extract archive: "; errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(a); return Result::Err(errMsg); } else if (r < ARCHIVE_WARN) { string errMsg = "Failed to extract archive: "; errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(a); return Result::Err(errMsg); } const _tinydir_char_t* currentFile = archive_entry_pathname_native(entry); if(rootName.empty()) rootName = currentFile; FileString fullOutputPath = destination; fullOutputPath += (currentFile + (rootName.empty() ? 0 : rootName.size() - 1)); // TODO: Verify if this really works. Why doesn't libarchive have wide string version of archive_entry_set_pathname? string fullOutputPathUtf8 = toUtf8(fullOutputPath); archive_entry_set_pathname(entry, fullOutputPathUtf8.c_str()); r = archive_write_header(ext, entry); if (r < ARCHIVE_OK) { string errMsg = "Failed to extract archive: "; errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(ext); return Result::Err(errMsg); } else if (archive_entry_size(entry) > 0) { r = copy_data(a, ext); if (r < ARCHIVE_OK) { string errMsg = "Failed to extract archive: "; errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(ext); return Result::Err(errMsg); } else if (r < ARCHIVE_WARN) { string errMsg = "Failed to extract archive: "; errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(ext); return Result::Err(errMsg); } } r = archive_write_finish_entry(ext); if (r < ARCHIVE_OK) { string errMsg = "Failed to extract archive: "; errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(ext); return Result::Err(errMsg); } else if (r < ARCHIVE_WARN) { string errMsg = "Failed to extract archive: "; errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(ext); return Result::Err(errMsg); } } archive_read_close(a); archive_read_free(a); archive_write_close(ext); archive_write_free(ext); return Result::Ok(true); } }