Created attachment 195436 [details] picture of archive contents vs. extraction result Ark silently omits empty directories when a directory tree is dragged from the Ark GUI and dropped into a folder in Dolphin. This is a regression: `25.12.3` works, while `26.04.2`, `26.04.3`, and `26.08.0` reproduce the problem. Normal full-archive extraction is not affected. The same archive extracts correctly using Ark's regular Extract/Extract All operation, `ark --batch`, `bsdtar`, or `tar`. # Steps To Reproduce 1. Create a small archive containing both an empty and a non-empty directory: ```bash rm -rf /tmp/ark-dnd-source /tmp/ark-dnd-target mkdir -p /tmp/ark-dnd-source/root/empty mkdir -p /tmp/ark-dnd-source/root/nonempty printf 'test\n' > /tmp/ark-dnd-source/root/nonempty/file.txt tar -C /tmp/ark-dnd-source -czf /tmp/ark-dnd-empty-dir.tar.gz root mkdir -p /tmp/ark-dnd-target ``` 2. Open `/tmp/ark-dnd-empty-dir.tar.gz` in Ark. 3. Drag the `root` directory from Ark's archive view and drop it into `/tmp/ark-dnd-target` in Dolphin. 4. Inspect the extracted tree: ```bash find /tmp/ark-dnd-target -print | sort ``` # Observed Result `root/nonempty/file.txt` is extracted, and its parent directories are implicitly recreated, but `root/empty` is missing. # Expected Result The complete selected tree is extracted, including `root/empty`. # Regression Range - Last tested working version: Ark/ark-libs `25.12.3` - First tested broken version: Ark/ark-libs `26.04.2` - Also broken: `26.04.3` and `26.08.0` - The regression is introduced by commit `189f1f4e411086d996d4e581c425a380d160b899` (included beginning with the `26.03.80` tag): <https://invent.kde.org/utilities/ark/-/commit/189f1f4e411086d996d4e581c425a380d160b899> # Technical Analysis Commit `189f1f4e411086d996d4e581c425a380d160b899` correctly normalizes libarchive directory entry names by removing trailing `/` characters before checking for existing destination files. However, partial extraction still builds `fullPaths` and `remainingFiles` with `ReadOnlyArchiveInterface::entryFullPaths()` using its default `WithTrailingSlash` format. During drag-and-drop extraction Ark supplies an explicit selected-entry list. A selected directory is therefore stored as `root/empty/`, while the corresponding libarchive entry is normalized to `root/empty`. The membership test fails and Ark skips the directory entry. Parent directories for regular files are recreated as a side effect, which hides the problem for every directory except genuinely empty ones. Full extraction passes an empty selected-entry list and uses the `extractAll` path, so it bypasses the mismatched membership test and works correctly. # Proposed Fix Normalize the two explicit selected-entry lists with the same representation used for libarchive entries: ```cpp const QStringList fullPaths = entryFullPaths(files, NoTrailingSlash); QStringList remainingFiles = entryFullPaths(files, NoTrailingSlash); ``` The attached patch also adds a regression test using the existing `empty_folders.tar.gz` fixture and drag-and-drop extraction options: ``` From 4cadf90043fbbe2c5bfc48355e2405b18f337811 Mon Sep 17 00:00:00 2001 From: GloriousEggroll <gloriouseggroll@gmail.com> Date: Mon, 24 Aug 2026 06:04:05 -0600 Subject: [PATCH] libarchive: Preserve empty directories during partial extraction Commit 189f1f4e411086d996d4e581c425a380d160b899 normalized libarchive directory entry names by removing trailing slashes. The explicit selected-entry lists retained their trailing slashes, so directory entries no longer matched during partial and drag-and-drop extraction. Non-empty parent directories were recreated while extracting files, but empty directories were silently omitted. Normalize both selected-entry lists to the same no-trailing-slash representation and add coverage for drag-and-drop extraction of empty directories. Fixes: 189f1f4e411086d996d4e581c425a380d160b899 --- autotests/kerfuffle/extracttest.cpp | 13 +++++++++++++ plugins/libarchive/libarchiveplugin.cpp | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/autotests/kerfuffle/extracttest.cpp b/autotests/kerfuffle/extracttest.cpp index 8f9774fd..10721326 100644 --- a/autotests/kerfuffle/extracttest.cpp +++ b/autotests/kerfuffle/extracttest.cpp @@ -181,6 +181,19 @@ void ExtractTest::testExtraction_data() archivePath = QFINDTESTDATA("data/empty_folders.tar.gz"); setupRow("tar with empty folders", archivePath, QList<Archive::Entry *>(), optionsPreservePaths, 5); + archivePath = QFINDTESTDATA("data/empty_folders.tar.gz"); + setupRow("extract selected empty folders from a tar.gz, drag-and-drop", + archivePath, + QList<Archive::Entry *>{ + new Archive::Entry(this, QStringLiteral("A/"), QString()), + new Archive::Entry(this, QStringLiteral("A/B/"), QString()), + new Archive::Entry(this, QStringLiteral("A/B/test.txt"), QString()), + new Archive::Entry(this, QStringLiteral("A/B/C/"), QString()), + new Archive::Entry(this, QStringLiteral("A/B1/"), QString()), + }, + dragAndDropOptions, + 5); + archivePath = QFINDTESTDATA("data/simplearchive.tar.bz2"); setupRow("extract selected entries from a bzip2-compressed tarball without path", archivePath, diff --git a/plugins/libarchive/libarchiveplugin.cpp b/plugins/libarchive/libarchiveplugin.cpp index d625ea51..26e59bd7 100644 --- a/plugins/libarchive/libarchiveplugin.cpp +++ b/plugins/libarchive/libarchiveplugin.cpp @@ -293,8 +293,8 @@ bool LibarchivePlugin::extractFiles(const QList<Archive::Entry *> &files, const QString fileBeingRenamed; // To avoid traversing the entire archive when extracting a limited set of // entries, we maintain a list of remaining entries and stop when it's empty. - const QStringList fullPaths = entryFullPaths(files); - QStringList remainingFiles = entryFullPaths(files); + const QStringList fullPaths = entryFullPaths(files, NoTrailingSlash); + QStringList remainingFiles = entryFullPaths(files, NoTrailingSlash); // Iterate through all entries in archive. while (!QThread::currentThread()->isInterruptionRequested() && (archive_read_next_header(m_archiveReader.data(), &entry) == ARCHIVE_OK)) { -- 2.55.0 ``` # System Information - OS: Nobara Linux 44 KDE Plasma Edition - Working Ark version currently installed: `25.12.3-1.fc44` - Broken Ark versions tested: `26.04.2-1.fc43`, `26.04.3-1.fc44`, `26.08.0-1.fc44` - libarchive: `3.8.7-1.fc44` - KDE Plasma: `6.7.4-1.fc44` - KDE Frameworks KArchive: `6.29.0-1.fc44` - Qt: `6.11.1-1.fc44` Extra: I changed the severity to major as it effects basic user experience and can have severe trickle-down effects. For example users manually installing custom proton versions for Steam will unknowingly end up with broken proton extractions and subsequently broken game launches. Specifically, the default_pfx folder in proton builds can break because it contains several empty directories that games/applications expect to exist in the prefix. Since proton copies default_pfx instead of using wineboot, those folders never get re-created:
Just ran across this too and can confirm this on 26.08.1. Full extraction (either via Dolphin's right-click -> extract, or Ark's extract menu) restores it, drag & drop does not.