[system/kio-snapshot] /: add support for external volumes to snapshot worker
Bharadwaj Raju <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 87ddea8d20b714e0245141e8db2984fb67506721 by Bharadwaj Raju.
Committed on 17/07/2026 at 12:06.
Pushed by bharadwaj-raju into branch 'master'.
add support for external volumes to snapshot worker
M +21 -6 contextmenu/snapshotfileitemaction.cpp
M +47 -11 kioworker/snapshot.cpp
https://invent.kde.org/system/kio-snapshot/-/commit/87ddea8d20b714e0245141e8db2984fb67506721
diff --git a/contextmenu/snapshotfileitemaction.cpp b/contextmenu/snapshotfileitemaction.cpp
index c834638..0665c96 100644
--- a/contextmenu/snapshotfileitemaction.cpp
+++ b/contextmenu/snapshotfileitemaction.cpp
@@ -14,6 +14,7 @@
#include <Solid/Device>
#include <Solid/StorageAccess>
+#include <Solid/StorageVolume>
#include <KFileItem>
#include <KLocalizedString>
@@ -51,22 +52,36 @@ QList<QAction *> SnapshotFileItemAction::actions(const KFileItemListProperties &
if (item.isDir()) {
QString localPath = itemUrl.toLocalFile();
- auto fsRoot = Solid::Device::storageAccessFromPath(localPath).as<Solid::StorageAccess>();
- if (!fsRoot) {
+ auto fsDevice = Solid::Device::storageAccessFromPath(localPath);
+ auto fsAccess = fsDevice.as<Solid::StorageAccess>();
+ if (!fsAccess) {
qCCritical(SNAPSHOT_FILEITEMACTION()) << "could not determine fs root path for" << localPath;
return actions;
}
- QString fsRootPath = fsRoot->filePath();
+ QString fsRootPath = fsAccess->filePath();
+ auto fsVolume = fsDevice.as<Solid::StorageVolume>();
+ if (!fsVolume) {
+ qCCritical(SNAPSHOT_FILEITEMACTION()) << "could not determine fs storage volume for" << localPath;
+ return actions;
+ }
+ QString fsUuid = fsVolume->uuid();
+
if (!BtrfsSnapshots::getSnapshotsForSubvolume(itemUrl.toLocalFile(), fsRootPath).empty()) {
- auto subvolumeIdOpt = BtrfsSnapshots::getSubvolumeForPath(itemUrl.toLocalFile());
+ auto subvolumeIdOpt = BtrfsSnapshots::getSubvolumeForPath(itemUrl.toLocalFile(), fsRootPath);
if (!subvolumeIdOpt.has_value()) {
qCCritical(SNAPSHOT_FILEITEMACTION()) << "found snapshots for dir" << itemUrl.toLocalFile() << "but it did not have a subvolume id";
return actions;
}
auto subvolumeId = subvolumeIdOpt.value();
QAction *action = new QAction(QIcon::fromTheme("view-history"_L1), i18nc("@action:inmenu", "Browse snapshots…"), parentWidget);
- connect(action, &QAction::triggered, this, [this, subvolumeId]() {
- KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl("snapshot:///%1"_L1.arg(QString::number(subvolumeId))), "inode/directory"_L1, this);
+ connect(action, &QAction::triggered, this, [this, subvolumeId, fsRootPath, fsUuid]() {
+ QUrl targetUrl;
+ targetUrl.setScheme("snapshot"_L1);
+ if (fsRootPath != "/"_L1) {
+ targetUrl.setHost(fsUuid);
+ }
+ targetUrl.setPath("/"_L1 + QString::number(subvolumeId));
+ KIO::OpenUrlJob *job = new KIO::OpenUrlJob(targetUrl, "inode/directory"_L1, this);
job->start();
});
actions << action;
diff --git a/kioworker/snapshot.cpp b/kioworker/snapshot.cpp
index c7aa8c5..f2d0571 100644
--- a/kioworker/snapshot.cpp
+++ b/kioworker/snapshot.cpp
@@ -13,6 +13,10 @@
#include <KIO/Global>
#include <KIO/UDSEntry>
+#include <Solid/Device>
+#include <Solid/StorageAccess>
+#include <Solid/StorageVolume>
+
#include <KLocalizedString>
#include <QCoreApplication>
@@ -20,6 +24,7 @@
#include <QDir>
#include <QLocale>
#include <QUrl>
+#include <QUuid>
using namespace Qt::StringLiterals;
@@ -46,12 +51,39 @@ extern "C" int Q_DECL_EXPORT kdemain(int argc, char **argv)
class SnapshotUrl : public QUrl
{
+private:
+ bool hasUuid() const
+ {
+ QString firstSegment = path().section('/'_L1, 0, 0, QString::SectionFlag::SectionSkipEmpty);
+ return firstSegment.startsWith("uuid="_L1);
+ }
+
public:
SnapshotUrl(const QUrl &url)
: QUrl(url)
{
}
+ QString fsRoot() const
+ {
+ if (host().isEmpty()) {
+ return "/"_L1;
+ }
+ QUuid uuid(host());
+ if (!uuid.isNull()) {
+ const auto deviceList =
+ Solid::Device::listFromQuery("StorageVolume.uuid == '%1'"_L1.arg(uuid.toString(QUuid::StringFormat::WithoutBraces).toLower()));
+ if (!deviceList.isEmpty()) {
+ auto device = deviceList.first();
+ auto storageAccess = device.as<Solid::StorageAccess>();
+ if (storageAccess) {
+ return storageAccess->filePath();
+ }
+ }
+ }
+ return "/"_L1;
+ }
+
std::optional<qulonglong> subvolumeId() const
{
bool ok;
@@ -91,7 +123,7 @@ bool SnapshotProtocol::rewriteUrl(const QUrl &url, QUrl &newUrl)
{
const SnapshotUrl snapshotUrl(url);
auto snapshotId = snapshotUrl.snapshotId();
- auto snapshotPathOpt = BtrfsSnapshots::getPathForSubvolume(snapshotId.value());
+ auto snapshotPathOpt = BtrfsSnapshots::getPathForSubvolume(snapshotId.value(), snapshotUrl.fsRoot());
if (!snapshotPathOpt.has_value()) {
warning(i18nc("@info warning", "Could not open snapshot"));
return false;
@@ -109,17 +141,21 @@ KIO::WorkerResult SnapshotProtocol::listDir(const QUrl &url)
qCDebug(KIO_SNAPSHOT) << "url" << url << "url.host" << url.host() << "subvolume" << snapshotUrl.subvolumeId() << "snapshotId" << snapshotUrl.snapshotId()
<< "actualPath" << snapshotUrl.actualPath();
+ const QString fsRoot = snapshotUrl.fsRoot();
+
if (!snapshotUrl.subvolumeId().has_value()) {
KIO::UDSEntryList udsList;
- for (const auto [id, path] : BtrfsSnapshots::getNonSnapshotSubvolumes().asKeyValueRange()) {
- if (!BtrfsSnapshots::getSnapshotsForSubvolume(path).empty()) {
+ for (const auto [id, path] : BtrfsSnapshots::getNonSnapshotSubvolumes(fsRoot).asKeyValueRange()) {
+ if (!BtrfsSnapshots::getSnapshotsForSubvolume(path, fsRoot).empty()) {
KIO::UDSEntry entry;
entry.fastInsert(KIO::UDSEntry::UDS_NAME, "subvolume%1"_L1.arg(QString::number(id)));
entry.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME,
i18nc("@title denoting a listing of snapshots for a directory; %1 is the path to the directory", "Snapshots for %1", path));
entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, "view-history"_L1);
entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, QT_STAT_DIR);
- entry.fastInsert(KIO::UDSEntry::UDS_URL, "snapshot:///%1"_L1.arg(QString::number(id)));
+ QUrl targetUrl = url;
+ targetUrl.setPath("/"_L1 + QString::number(id));
+ entry.fastInsert(KIO::UDSEntry::UDS_URL, targetUrl.toString(QUrl::FullyEncoded));
udsList << entry;
}
}
@@ -131,12 +167,12 @@ KIO::WorkerResult SnapshotProtocol::listDir(const QUrl &url)
return KIO::ForwardingWorkerBase::listDir(url);
}
- auto subvolumePathOpt = BtrfsSnapshots::getPathForSubvolume(snapshotUrl.subvolumeId().value());
+ auto subvolumePathOpt = BtrfsSnapshots::getPathForSubvolume(snapshotUrl.subvolumeId().value(), fsRoot);
if (!subvolumePathOpt.has_value()) {
return KIO::WorkerResult::fail(KIO::ERR_ACCESS_DENIED);
}
- const QList<BtrfsSnapshots::SubvolumeSnapshot> snapshots = BtrfsSnapshots::getSnapshotsForSubvolume(subvolumePathOpt.value());
+ const QList<BtrfsSnapshots::SubvolumeSnapshot> snapshots = BtrfsSnapshots::getSnapshotsForSubvolume(subvolumePathOpt.value(), fsRoot);
KIO::UDSEntryList udsList;
for (const auto &snapshot : snapshots) {
@@ -152,8 +188,7 @@ KIO::WorkerResult SnapshotProtocol::listDir(const QUrl &url)
entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, snapshot.snapshotted.toSecsSinceEpoch());
entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, QT_STAT_DIR);
entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory"_L1);
- QUrl targetUrl;
- targetUrl.setScheme("snapshot"_L1);
+ QUrl targetUrl = url;
targetUrl.setPath(QDir::cleanPath("/%1/%2/"_L1.arg(QString::number(snapshotUrl.subvolumeId().value())).arg(QString::number(snapshot.subvolumeId))));
entry.fastInsert(KIO::UDSEntry::UDS_TARGET_URL, targetUrl.toString(QUrl::FullyEncoded));
qCDebug(KIO_SNAPSHOT) << entry;
@@ -190,6 +225,7 @@ KIO::WorkerResult SnapshotProtocol::stat(const QUrl &url)
}
qulonglong subvolumeId = snapshotUrl.subvolumeId().value();
+ const QString fsRoot = snapshotUrl.fsRoot();
if (snapshotUrl.snapshotId().has_value() && !snapshotUrl.actualPath().isEmpty()) {
qCDebug(KIO_SNAPSHOT()) << "forwarding stat...";
@@ -203,11 +239,11 @@ KIO::WorkerResult SnapshotProtocol::stat(const QUrl &url)
if (snapshotInfoMap.contains(snapshotId)) {
snapshotInfo = snapshotInfoMap[snapshotId];
} else {
- auto snapshotPathOpt = BtrfsSnapshots::getPathForSubvolume(subvolumeId);
+ auto snapshotPathOpt = BtrfsSnapshots::getPathForSubvolume(subvolumeId, fsRoot);
if (!snapshotPathOpt.has_value()) {
return KIO::WorkerResult::fail(KIO::ERR_ACCESS_DENIED);
}
- const auto snapshotQuery = BtrfsSnapshots::getSnapshotsForSubvolume(snapshotPathOpt.value());
+ const auto snapshotQuery = BtrfsSnapshots::getSnapshotsForSubvolume(snapshotPathOpt.value(), fsRoot);
for (const auto &snapshot : snapshotQuery) {
snapshotInfoMap[snapshot.subvolumeId] = snapshot;
if (snapshot.subvolumeId == snapshotId) {
@@ -234,7 +270,7 @@ KIO::WorkerResult SnapshotProtocol::stat(const QUrl &url)
return KIO::WorkerResult::pass();
}
- auto snapshotPathOpt = BtrfsSnapshots::getPathForSubvolume(subvolumeId);
+ auto snapshotPathOpt = BtrfsSnapshots::getPathForSubvolume(subvolumeId, fsRoot);
if (!snapshotPathOpt.has_value()) {
return KIO::WorkerResult::fail(KIO::ERR_ACCESS_DENIED);
}