diff --git a/CMakeLists.txt b/CMakeLists.txt
index 34c071c3..29828b44 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,114 +1,109 @@
cmake_minimum_required (VERSION 3.0)
# KDE Application Version, managed by release script
set (KDE_APPLICATIONS_VERSION_MAJOR "20")
set (KDE_APPLICATIONS_VERSION_MINOR "03")
set (KDE_APPLICATIONS_VERSION_MICRO "70")
set (KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}")
project(ark VERSION ${KDE_APPLICATIONS_VERSION})
set(QT_MIN_VERSION 5.8.0)
set(KF5_MIN_VERSION 5.44.0)
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
include(GenerateExportHeader)
include(FeatureSummary)
include(ECMQtDeclareLoggingCategory)
include(ECMSetupVersion)
add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
ecm_setup_version(${KDE_APPLICATIONS_VERSION}
VARIABLE_PREFIX ARK
VERSION_HEADER "ark_version.h")
ecm_setup_version(PROJECT
VARIABLE_PREFIX KERFUFFLE)
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED
Concurrent
Core
Gui
Widgets)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Archive
Config
Crash
DBusAddons
DocTools
I18n
IconThemes
ItemModels
KIO
Service
Parts
Pty
WidgetsAddons)
find_package(Qt5Test ${QT_MIN_VERSION} CONFIG QUIET)
set_package_properties(Qt5Test PROPERTIES
PURPOSE "Required for tests"
TYPE OPTIONAL)
if(NOT Qt5Test_FOUND)
set(BUILD_TESTING OFF CACHE BOOL "Build the testing tree.")
endif()
-find_package(LibArchive 3.2.0 REQUIRED)
+find_package(LibArchive 3.3.3 REQUIRED)
set_package_properties(LibArchive PROPERTIES
URL "https://www.libarchive.org/"
DESCRIPTION "A library for dealing with a wide variety of archive file formats"
PURPOSE "Required for among others tar, tar.gz, tar.bz2 formats in Ark.")
find_package(LibZip 1.3.0)
set_package_properties(LibZip PROPERTIES
URL "https://nih.at/libzip/"
DESCRIPTION "A library for handling zip archives"
PURPOSE "Optional for zip archives.")
-find_package(SharedMimeInfo QUIET)
-set_package_properties(SharedMimeInfo PROPERTIES
- TYPE OPTIONAL
- PURPOSE "Required for archive formats without an official mimetype.")
-
option(WITH_TEST_COVERAGE "Build with test coverage support" OFF)
if (WITH_TEST_COVERAGE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
endif (WITH_TEST_COVERAGE)
set(SUPPORTED_ARK_MIMETYPES "")
add_definitions(-DTRANSLATION_DOMAIN="ark")
# Until kf5 5.56 kconfig had some header which used Q_FOREACH
# Q_FOREACH will be deprecated/removed in qt6. With QT_NO_FOREACH we prepare the migration to qt6.
if (KF5Config_VERSION VERSION_GREATER "5.56.0")
add_definitions(-DQT_NO_FOREACH)
endif()
add_subdirectory(plugins)
add_subdirectory(kerfuffle)
add_subdirectory(part)
add_subdirectory(app)
add_subdirectory(doc)
if(BUILD_TESTING)
add_subdirectory(autotests)
endif()
ki18n_install(po)
kdoctools_install(po)
if (NOT ECM_VERSION VERSION_LESS "5.59.0")
install(FILES ark.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR})
else()
install(FILES ark.categories DESTINATION ${KDE_INSTALL_CONFDIR})
endif()
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/kerfuffle/CMakeLists.txt b/kerfuffle/CMakeLists.txt
index ba13e8e9..154ab8d1 100644
--- a/kerfuffle/CMakeLists.txt
+++ b/kerfuffle/CMakeLists.txt
@@ -1,82 +1,77 @@
########### next target ###############
set(kerfuffle_SRCS
archiveformat.cpp
archive_kerfuffle.cpp
archiveinterface.cpp
extractionsettingspage.cpp
generalsettingspage.cpp
previewsettingspage.cpp
settingsdialog.cpp
settingspage.cpp
jobs.cpp
adddialog.cpp
compressionoptionswidget.cpp
createdialog.cpp
extractiondialog.cpp
propertiesdialog.cpp
queries.cpp
addtoarchive.cpp
cliinterface.cpp
cliproperties.cpp
mimetypes.cpp
plugin.cpp
pluginmanager.cpp
pluginsettingspage.cpp
archiveentry.cpp
options.cpp
)
kconfig_add_kcfg_files(kerfuffle_SRCS settings.kcfgc GENERATE_MOC)
ki18n_wrap_ui(kerfuffle_SRCS
createdialog.ui
extractiondialog.ui
extractionsettingspage.ui
generalsettingspage.ui
pluginsettingspage.ui
previewsettingspage.ui
propertiesdialog.ui
compressionoptionswidget.ui
)
ecm_qt_declare_logging_category(kerfuffle_SRCS
HEADER ark_debug.h
IDENTIFIER ARK
CATEGORY_NAME ark.kerfuffle)
add_library(kerfuffle SHARED ${kerfuffle_SRCS})
generate_export_header(kerfuffle BASE_NAME kerfuffle)
if (APPLE)
target_compile_definitions(kerfuffle PRIVATE -DDEPENDENCY_TOOL="otool")
target_compile_definitions(kerfuffle PRIVATE -DDEPENDENCY_TOOL_ARGS="-L")
else()
target_compile_definitions(kerfuffle PRIVATE -DDEPENDENCY_TOOL="ldd")
endif()
target_link_libraries(kerfuffle
PUBLIC
KF5::IconThemes
KF5::Pty
KF5::Service
KF5::I18n
KF5::WidgetsAddons
PRIVATE
Qt5::Concurrent
KF5::KIOCore
KF5::KIOWidgets
KF5::KIOFileWidgets
)
set_target_properties(kerfuffle PROPERTIES VERSION ${KERFUFFLE_VERSION_STRING} SOVERSION ${KERFUFFLE_SOVERSION})
install(TARGETS kerfuffle ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP)
install(FILES kerfufflePlugin.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR})
install(FILES ark.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR})
-
-install(FILES mime/kerfuffle.xml DESTINATION ${KDE_INSTALL_MIMEDIR})
-if(SharedMimeInfo_FOUND)
- update_xdg_mimetypes(${KDE_INSTALL_MIMEDIR})
-endif()
diff --git a/kerfuffle/mime/kerfuffle.xml b/kerfuffle/mime/kerfuffle.xml
deleted file mode 100644
index 931aa4d0..00000000
--- a/kerfuffle/mime/kerfuffle.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- Tar archive (zstd-compressed)
-
-
-
diff --git a/plugins/libarchive/CMakeLists.txt b/plugins/libarchive/CMakeLists.txt
index 4d22d718..97ed40a5 100644
--- a/plugins/libarchive/CMakeLists.txt
+++ b/plugins/libarchive/CMakeLists.txt
@@ -1,109 +1,93 @@
include_directories(${LibArchive_INCLUDE_DIRS})
-if(NOT LibArchive_VERSION VERSION_LESS "3.3.3")
- set(ENABLE_ZSTD_SUPPORT ON BOOL)
-endif()
-
########### next target ###############
set(SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES "application/x-tar;application/x-compressed-tar;application/x-bzip-compressed-tar;application/x-tarz;application/x-xz-compressed-tar;")
set(SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES "${SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES}application/x-lzma-compressed-tar;application/x-lzip-compressed-tar;application/x-tzo;application/x-lrzip-compressed-tar;application/x-lz4-compressed-tar;")
set(SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES "application/vnd.debian.binary-package;application/x-deb;application/x-cd-image;application/x-bcpio;application/x-cpio;application/x-cpio-compressed;application/x-sv4cpio;application/x-sv4crc;")
set(SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES "${SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES}application/x-rpm;application/x-source-rpm;application/vnd.ms-cab-compressed;application/x-xar;application/x-iso9660-appimage;application/x-archive;")
-
-if(ENABLE_ZSTD_SUPPORT)
- set(SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES "${SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES}application/x-zstd-compressed-tar;")
-endif()
+set(SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES "${SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES}application/x-zstd-compressed-tar;")
set(INSTALLED_LIBARCHIVE_PLUGINS "")
set(kerfuffle_libarchive_readonly_SRCS libarchiveplugin.cpp readonlylibarchiveplugin.cpp ark_debug.cpp)
set(kerfuffle_libarchive_readwrite_SRCS libarchiveplugin.cpp readwritelibarchiveplugin.cpp ark_debug.cpp)
set(kerfuffle_libarchive_SRCS ${kerfuffle_libarchive_readonly_SRCS} readwritelibarchiveplugin.cpp)
ecm_qt_declare_logging_category(kerfuffle_libarchive_SRCS
HEADER ark_debug.h
IDENTIFIER ARK
CATEGORY_NAME ark.libarchive)
# NOTE: the first double-quotes of the first mime and the last
# double-quotes of the last mime must NOT be escaped.
set(SUPPORTED_READONLY_MIMETYPES
"application/x-deb\",
\"application/x-cd-image\",
\"application/x-bcpio\",
\"application/x-cpio\",
\"application/x-cpio-compressed\",
\"application/x-sv4cpio\",
\"application/x-sv4crc\",
\"application/x-rpm\",
\"application/x-source-rpm\",
\"application/vnd.debian.binary-package\",
\"application/vnd.ms-cab-compressed\",
\"application/x-xar\",
\"application/x-iso9660-appimage\",
\"application/x-archive")
# NOTE: the first double-quotes of the first mime and the last
# double-quotes of the last mime must NOT be escaped.
set(SUPPORTED_READWRITE_MIMETYPES
"application/x-tar\",
\"application/x-compressed-tar\",
\"application/x-bzip-compressed-tar\",
\"application/x-tarz\",
\"application/x-xz-compressed-tar\",
\"application/x-lzma-compressed-tar\",
\"application/x-lzip-compressed-tar\",
\"application/x-tzo\",
\"application/x-lrzip-compressed-tar\",
- \"application/x-lz4-compressed-tar")
-
-if(ENABLE_ZSTD_SUPPORT)
- set(SUPPORTED_READWRITE_MIMETYPES
- "${SUPPORTED_READWRITE_MIMETYPES}\",
- \"application/x-zstd-compressed-tar")
-endif()
+ \"application/x-lz4-compressed-tar\",
+ \"application/x-zstd-compressed-tar")
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_libarchive_readonly.json.cmake
${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_libarchive_readonly.json)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_libarchive.json.cmake
${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_libarchive.json)
kerfuffle_add_plugin(kerfuffle_libarchive_readonly ${kerfuffle_libarchive_readonly_SRCS})
kerfuffle_add_plugin(kerfuffle_libarchive ${kerfuffle_libarchive_readwrite_SRCS})
-if(ENABLE_ZSTD_SUPPORT)
- target_compile_definitions(kerfuffle_libarchive PRIVATE -DHAVE_ZSTD_SUPPORT)
-endif()
-
target_link_libraries(kerfuffle_libarchive_readonly ${LibArchive_LIBRARIES})
target_link_libraries(kerfuffle_libarchive ${LibArchive_LIBRARIES})
set(INSTALLED_LIBARCHIVE_PLUGINS "${INSTALLED_LIBARCHIVE_PLUGINS}kerfuffle_libarchive_readonly;")
set(INSTALLED_LIBARCHIVE_PLUGINS "${INSTALLED_LIBARCHIVE_PLUGINS}kerfuffle_libarchive;")
set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES}${SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES}" PARENT_SCOPE)
set(INSTALLED_KERFUFFLE_PLUGINS "${INSTALLED_KERFUFFLE_PLUGINS}${INSTALLED_LIBARCHIVE_PLUGINS}" PARENT_SCOPE)
find_program(LRZIP lrzip)
if(LRZIP)
message(STATUS "Found lrzip executable: ${LRZIP}")
else()
message(WARNING "Could not find the lrzip executable. Ark requires lrzip to handle the tar.lrz archive format.")
endif()
find_program(LZOP lzop)
if(LZOP)
message(STATUS "Found lzop executable: ${LZOP}")
else()
message(WARNING "Could not find the lzop executable. Ark requires lzop to handle the tar.lzo archive format if libarchive >= 3.3 has been compiled without liblzo2 support.")
endif()
find_program(ZSTD zstd)
if(ZSTD)
message(STATUS "Found zstd executable: ${ZSTD}")
else()
message(WARNING "Could not find the zstd executable. Ark requires zstd to handle the tar.zst archive format if libarchive >= 3.3.3 has been compiled without libzstd support.")
endif()
diff --git a/plugins/libarchive/readwritelibarchiveplugin.cpp b/plugins/libarchive/readwritelibarchiveplugin.cpp
index fb821c84..37025947 100644
--- a/plugins/libarchive/readwritelibarchiveplugin.cpp
+++ b/plugins/libarchive/readwritelibarchiveplugin.cpp
@@ -1,558 +1,554 @@
/*
* Copyright (c) 2007 Henrique Pinto
* Copyright (c) 2008-2009 Harald Hvaal
* Copyright (c) 2010 Raphael Kubo da Costa
* Copyright (c) 2016 Vladyslav Batyrenko
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "readwritelibarchiveplugin.h"
#include "ark_debug.h"
#include
#include
#include
#include
#include
#include
K_PLUGIN_CLASS_WITH_JSON(ReadWriteLibarchivePlugin, "kerfuffle_libarchive.json")
ReadWriteLibarchivePlugin::ReadWriteLibarchivePlugin(QObject *parent, const QVariantList &args)
: LibarchivePlugin(parent, args)
{
qCDebug(ARK) << "Loaded libarchive read-write plugin";
}
ReadWriteLibarchivePlugin::~ReadWriteLibarchivePlugin()
{
}
bool ReadWriteLibarchivePlugin::addFiles(const QVector &files, const Archive::Entry *destination, const CompressionOptions &options, uint numberOfEntriesToAdd)
{
qCDebug(ARK) << "Adding" << files.size() << "entries with CompressionOptions" << options;
const bool creatingNewFile = !QFileInfo::exists(filename());
const uint totalCount = m_numberOfEntries + numberOfEntriesToAdd;
m_writtenFiles.clear();
if (!creatingNewFile && !initializeReader()) {
return false;
}
if (!initializeWriter(creatingNewFile, options)) {
return false;
}
// First write the new files.
qCDebug(ARK) << "Writing new entries";
uint addedEntries = 0;
// Recreate destination directory structure.
const QString destinationPath = (destination == nullptr)
? QString()
: destination->fullPath();
for (Archive::Entry *selectedFile : files) {
if (QThread::currentThread()->isInterruptionRequested()) {
break;
}
if (!writeFile(selectedFile->fullPath(), destinationPath)) {
finish(false);
return false;
}
addedEntries++;
emit progress(float(addedEntries)/float(totalCount));
// For directories, write all subfiles/folders.
const QString &fullPath = selectedFile->fullPath();
if (QFileInfo(fullPath).isDir()) {
QDirIterator it(fullPath,
QDir::AllEntries | QDir::Readable |
QDir::Hidden | QDir::NoDotAndDotDot,
QDirIterator::Subdirectories);
while (!QThread::currentThread()->isInterruptionRequested() && it.hasNext()) {
QString path = it.next();
if ((it.fileName() == QLatin1String("..")) ||
(it.fileName() == QLatin1Char('.'))) {
continue;
}
const bool isRealDir = it.fileInfo().isDir() && !it.fileInfo().isSymLink();
if (isRealDir) {
path.append(QLatin1Char('/'));
}
if (!writeFile(path, destinationPath)) {
finish(false);
return false;
}
addedEntries++;
emit progress(float(addedEntries)/float(totalCount));
}
}
}
qCDebug(ARK) << "Added" << addedEntries << "new entries to archive";
bool isSuccessful = true;
// If we have old archive entries.
if (!creatingNewFile) {
qCDebug(ARK) << "Copying any old entries";
m_filesPaths = m_writtenFiles;
isSuccessful = processOldEntries(addedEntries, Add, totalCount);
if (isSuccessful) {
qCDebug(ARK) << "Added" << addedEntries << "old entries to archive";
} else {
qCDebug(ARK) << "Adding entries failed";
}
}
finish(isSuccessful);
return isSuccessful;
}
bool ReadWriteLibarchivePlugin::moveFiles(const QVector &files, Archive::Entry *destination, const CompressionOptions &options)
{
Q_UNUSED(options);
qCDebug(ARK) << "Moving" << files.size() << "entries";
if (!initializeReader()) {
return false;
}
if (!initializeWriter()) {
return false;
}
// Copy old elements from previous archive to new archive.
uint movedEntries = 0;
m_filesPaths = entryFullPaths(files);
m_entriesWithoutChildren = entriesWithoutChildren(files).count();
m_destination = destination;
const bool isSuccessful = processOldEntries(movedEntries, Move, m_numberOfEntries);
if (isSuccessful) {
qCDebug(ARK) << "Moved" << movedEntries << "entries within archive";
} else {
qCDebug(ARK) << "Moving entries failed";
}
finish(isSuccessful);
return isSuccessful;
}
bool ReadWriteLibarchivePlugin::copyFiles(const QVector &files, Archive::Entry *destination, const CompressionOptions &options)
{
Q_UNUSED(options);
qCDebug(ARK) << "Copying" << files.size() << "entries";
if (!initializeReader()) {
return false;
}
if (!initializeWriter()) {
return false;
}
// Copy old elements from previous archive to new archive.
uint copiedEntries = 0;
m_filesPaths = entryFullPaths(files);
m_destination = destination;
const bool isSuccessful = processOldEntries(copiedEntries, Copy, m_numberOfEntries);
if (isSuccessful) {
qCDebug(ARK) << "Copied" << copiedEntries << "entries within archive";
} else {
qCDebug(ARK) << "Copying entries failed";
}
finish(isSuccessful);
return isSuccessful;
}
bool ReadWriteLibarchivePlugin::deleteFiles(const QVector &files)
{
qCDebug(ARK) << "Deleting" << files.size() << "entries";
if (!initializeReader()) {
return false;
}
if (!initializeWriter()) {
return false;
}
// Copy old elements from previous archive to new archive.
uint deletedEntries = 0;
m_filesPaths = entryFullPaths(files);
const bool isSuccessful = processOldEntries(deletedEntries, Delete, m_numberOfEntries);
if (isSuccessful) {
qCDebug(ARK) << "Removed" << deletedEntries << "entries from archive";
} else {
qCDebug(ARK) << "Removing entries failed";
}
finish(isSuccessful);
return isSuccessful;
}
bool ReadWriteLibarchivePlugin::initializeWriter(const bool creatingNewFile, const CompressionOptions &options)
{
m_tempFile.setFileName(filename());
if (!m_tempFile.open(QIODevice::WriteOnly | QIODevice::Unbuffered)) {
emit error(i18nc("@info", "Failed to create a temporary file for writing data."));
return false;
}
m_archiveWriter.reset(archive_write_new());
if (!(m_archiveWriter.data())) {
emit error(i18n("The archive writer could not be initialized."));
return false;
}
// pax_restricted is the libarchive default, let's go with that.
archive_write_set_format_pax_restricted(m_archiveWriter.data());
if (creatingNewFile) {
if (!initializeNewFileWriterFilters(options)) {
return false;
}
} else {
if (!initializeWriterFilters()) {
return false;
}
}
if (archive_write_open_fd(m_archiveWriter.data(), m_tempFile.handle()) != ARCHIVE_OK) {
emit error(i18nc("@info", "Could not open the archive for writing entries."));
return false;
}
return true;
}
bool ReadWriteLibarchivePlugin::initializeWriterFilters()
{
int ret;
bool requiresExecutable = false;
switch (archive_filter_code(m_archiveReader.data(), 0)) {
case ARCHIVE_FILTER_GZIP:
ret = archive_write_add_filter_gzip(m_archiveWriter.data());
break;
case ARCHIVE_FILTER_BZIP2:
ret = archive_write_add_filter_bzip2(m_archiveWriter.data());
break;
case ARCHIVE_FILTER_XZ:
ret = archive_write_add_filter_xz(m_archiveWriter.data());
break;
case ARCHIVE_FILTER_LZMA:
ret = archive_write_add_filter_lzma(m_archiveWriter.data());
break;
case ARCHIVE_FILTER_COMPRESS:
ret = archive_write_add_filter_compress(m_archiveWriter.data());
break;
case ARCHIVE_FILTER_LZIP:
ret = archive_write_add_filter_lzip(m_archiveWriter.data());
break;
case ARCHIVE_FILTER_LZOP:
ret = archive_write_add_filter_lzop(m_archiveWriter.data());
break;
case ARCHIVE_FILTER_LRZIP:
ret = archive_write_add_filter_lrzip(m_archiveWriter.data());
requiresExecutable = true;
break;
case ARCHIVE_FILTER_LZ4:
ret = archive_write_add_filter_lz4(m_archiveWriter.data());
break;
-#ifdef HAVE_ZSTD_SUPPORT
case ARCHIVE_FILTER_ZSTD:
ret = archive_write_add_filter_zstd(m_archiveWriter.data());
break;
-#endif
case ARCHIVE_FILTER_NONE:
ret = archive_write_add_filter_none(m_archiveWriter.data());
break;
default:
emit error(i18n("The compression type '%1' is not supported by Ark.",
QLatin1String(archive_filter_name(m_archiveReader.data(), 0))));
return false;
}
// Libarchive emits a warning for lrzip due to using external executable.
if ((requiresExecutable && ret != ARCHIVE_WARN) ||
(!requiresExecutable && ret != ARCHIVE_OK)) {
qCWarning(ARK) << "Failed to set compression method:" << archive_error_string(m_archiveWriter.data());
emit error(i18nc("@info", "Could not set the compression method."));
return false;
}
return true;
}
bool ReadWriteLibarchivePlugin::initializeNewFileWriterFilters(const CompressionOptions &options)
{
int ret;
bool requiresExecutable = false;
if (filename().right(2).toUpper() == QLatin1String("GZ")) {
qCDebug(ARK) << "Detected gzip compression for new file";
ret = archive_write_add_filter_gzip(m_archiveWriter.data());
} else if (filename().right(3).toUpper() == QLatin1String("BZ2")) {
qCDebug(ARK) << "Detected bzip2 compression for new file";
ret = archive_write_add_filter_bzip2(m_archiveWriter.data());
} else if (filename().right(2).toUpper() == QLatin1String("XZ")) {
qCDebug(ARK) << "Detected xz compression for new file";
ret = archive_write_add_filter_xz(m_archiveWriter.data());
} else if (filename().right(4).toUpper() == QLatin1String("LZMA")) {
qCDebug(ARK) << "Detected lzma compression for new file";
ret = archive_write_add_filter_lzma(m_archiveWriter.data());
} else if (filename().right(2).toUpper() == QLatin1String(".Z")) {
qCDebug(ARK) << "Detected compress (.Z) compression for new file";
ret = archive_write_add_filter_compress(m_archiveWriter.data());
} else if (filename().right(2).toUpper() == QLatin1String("LZ")) {
qCDebug(ARK) << "Detected lzip compression for new file";
ret = archive_write_add_filter_lzip(m_archiveWriter.data());
} else if (filename().right(3).toUpper() == QLatin1String("LZO")) {
qCDebug(ARK) << "Detected lzop compression for new file";
ret = archive_write_add_filter_lzop(m_archiveWriter.data());
} else if (filename().right(3).toUpper() == QLatin1String("LRZ")) {
qCDebug(ARK) << "Detected lrzip compression for new file";
ret = archive_write_add_filter_lrzip(m_archiveWriter.data());
requiresExecutable = true;
} else if (filename().right(3).toUpper() == QLatin1String("LZ4")) {
qCDebug(ARK) << "Detected lz4 compression for new file";
ret = archive_write_add_filter_lz4(m_archiveWriter.data());
-#ifdef HAVE_ZSTD_SUPPORT
} else if (filename().right(3).toUpper() == QLatin1String("ZST")) {
qCDebug(ARK) << "Detected zstd compression for new file";
ret = archive_write_add_filter_zstd(m_archiveWriter.data());
-#endif
} else if (filename().right(3).toUpper() == QLatin1String("TAR")) {
qCDebug(ARK) << "Detected no compression for new file (pure tar)";
ret = archive_write_add_filter_none(m_archiveWriter.data());
} else {
qCDebug(ARK) << "Falling back to gzip";
ret = archive_write_add_filter_gzip(m_archiveWriter.data());
}
// Libarchive emits a warning for lrzip due to using external executable.
if ((requiresExecutable && ret != ARCHIVE_WARN) ||
(!requiresExecutable && ret != ARCHIVE_OK)) {
qCWarning(ARK) << "Failed to set compression method:" << archive_error_string(m_archiveWriter.data());
emit error(i18nc("@info", "Could not set the compression method."));
return false;
}
// Set compression level if passed in CompressionOptions.
if (options.isCompressionLevelSet()) {
qCDebug(ARK) << "Using compression level:" << options.compressionLevel();
ret = archive_write_set_filter_option(m_archiveWriter.data(), nullptr, "compression-level", QString::number(options.compressionLevel()).toUtf8().constData());
if (ret != ARCHIVE_OK) {
qCWarning(ARK) << "Failed to set compression level" << archive_error_string(m_archiveWriter.data());
emit error(i18nc("@info", "Could not set the compression level."));
return false;
}
}
return true;
}
void ReadWriteLibarchivePlugin::finish(const bool isSuccessful)
{
if (!isSuccessful || QThread::currentThread()->isInterruptionRequested()) {
archive_write_fail(m_archiveWriter.data());
m_tempFile.cancelWriting();
} else {
// archive_write_close() needs to be called before calling QSaveFile::commit(),
// otherwise the latter will close() the file descriptor m_archiveWriter is still working on.
// TODO: We need to abstract this code better so that we only deal with one
// object that manages both QSaveFile and ArchiveWriter.
archive_write_close(m_archiveWriter.data());
m_tempFile.commit();
}
}
bool ReadWriteLibarchivePlugin::processOldEntries(uint &entriesCounter, OperationMode mode, uint totalCount)
{
const uint newEntries = entriesCounter;
entriesCounter = 0;
uint iteratedEntries = 0;
// Create a map that contains old path as key and new path as value.
QMap pathMap;
if (mode == Move || mode == Copy) {
m_filesPaths.sort();
QStringList resultList = entryPathsFromDestination(m_filesPaths, m_destination, m_entriesWithoutChildren);
const int listSize = m_filesPaths.count();
Q_ASSERT(listSize == resultList.count());
for (int i = 0; i < listSize; ++i) {
pathMap.insert(m_filesPaths.at(i), resultList.at(i));
}
}
struct archive_entry *entry;
while (!QThread::currentThread()->isInterruptionRequested() && archive_read_next_header(m_archiveReader.data(), &entry) == ARCHIVE_OK) {
const QString file = QFile::decodeName(archive_entry_pathname(entry));
if (mode == Move || mode == Copy) {
const QString newPathname = pathMap.value(file);
if (!newPathname.isEmpty()) {
if (mode == Copy) {
// Write the old entry.
if (!writeEntry(entry)) {
return false;
}
} else {
emit entryRemoved(file);
}
entriesCounter++;
iteratedEntries--;
// Change entry path.
archive_entry_set_pathname(entry, newPathname.toUtf8().constData());
emitEntryFromArchiveEntry(entry);
}
} else if (m_filesPaths.contains(file)) {
archive_read_data_skip(m_archiveReader.data());
switch (mode) {
case Delete:
entriesCounter++;
emit entryRemoved(file);
emit progress(float(newEntries + entriesCounter + iteratedEntries)/float(totalCount));
break;
case Add:
qCDebug(ARK) << file << "is already present in the new archive, skipping.";
// When overwriting entries, we need to decrement the counter manually,
// because entry was emitted.
m_numberOfEntries--;
break;
default:
qCDebug(ARK) << "Mode" << mode << "is not considered for processing old libarchive entries";
Q_ASSERT(false);
}
continue;
}
// Write old entries.
if (writeEntry(entry)) {
if (mode == Add) {
entriesCounter++;
} else if (mode == Move || mode == Copy) {
iteratedEntries++;
} else if (mode == Delete) {
iteratedEntries++;
}
} else {
return false;
}
emit progress(float(newEntries + entriesCounter + iteratedEntries)/float(totalCount));
}
return !QThread::currentThread()->isInterruptionRequested();
}
bool ReadWriteLibarchivePlugin::writeEntry(struct archive_entry *entry)
{
const int returnCode = archive_write_header(m_archiveWriter.data(), entry);
switch (returnCode) {
case ARCHIVE_OK:
// If the whole archive is extracted and the total filesize is
// available, we use partial progress.
copyData(QLatin1String(archive_entry_pathname(entry)), m_archiveReader.data(), m_archiveWriter.data(), false);
break;
case ARCHIVE_FAILED:
case ARCHIVE_FATAL:
qCCritical(ARK) << "archive_write_header() has returned" << returnCode
<< "with errno" << archive_errno(m_archiveWriter.data());
emit error(i18nc("@info", "Could not compress entry, operation aborted."));
return false;
default:
qCDebug(ARK) << "archive_writer_header() has returned" << returnCode
<< "which will be ignored.";
break;
}
return true;
}
// TODO: if we merge this with copyData(), we can pass more data
// such as an fd to archive_read_disk_entry_from_file()
bool ReadWriteLibarchivePlugin::writeFile(const QString &relativeName, const QString &destination)
{
const QString absoluteFilename = QFileInfo(relativeName).absoluteFilePath();
const QString destinationFilename = destination + relativeName;
// #253059: Even if we use archive_read_disk_entry_from_file,
// libarchive may have been compiled without HAVE_LSTAT,
// or something may have caused it to follow symlinks, in
// which case stat() will be called. To avoid this, we
// call lstat() ourselves.
struct stat st;
lstat(QFile::encodeName(absoluteFilename).constData(), &st); // krazy:exclude=syscalls
struct archive_entry *entry = archive_entry_new();
archive_entry_set_pathname(entry, QFile::encodeName(destinationFilename).constData());
archive_entry_copy_sourcepath(entry, QFile::encodeName(absoluteFilename).constData());
archive_read_disk_entry_from_file(m_archiveReadDisk.data(), entry, -1, &st);
const auto returnCode = archive_write_header(m_archiveWriter.data(), entry);
if (returnCode == ARCHIVE_OK) {
// If the whole archive is extracted and the total filesize is
// available, we use partial progress.
copyData(absoluteFilename, m_archiveWriter.data(), false);
} else {
qCCritical(ARK) << "Writing header failed with error code " << returnCode;
qCCritical(ARK) << "Error while writing..." << archive_error_string(m_archiveWriter.data()) << "(error no =" << archive_errno(m_archiveWriter.data()) << ')';
emit error(i18nc("@info Error in a message box",
"Could not compress entry."));
archive_entry_free(entry);
return false;
}
if (QThread::currentThread()->isInterruptionRequested()) {
archive_entry_free(entry);
return false;
}
m_writtenFiles.push_back(destinationFilename);
emitEntryFromArchiveEntry(entry);
archive_entry_free(entry);
return true;
}
#include "readwritelibarchiveplugin.moc"