diff --git a/src/engine/experimental/databasesanitizer.h b/src/engine/experimental/databasesanitizer.h --- a/src/engine/experimental/databasesanitizer.h +++ b/src/engine/experimental/databasesanitizer.h @@ -33,6 +33,16 @@ */ class BALOO_ENGINE_EXPORT DatabaseSanitizer { +public: + enum ItemAccessFilterFlags { + IgnoreNone = 0, + IgnoreAvailable = 1, + IgnoreUnavailable = 2, + IgnoreMounted = 8, + IgnoreUnmounted = 0x10, + }; + Q_DECLARE_FLAGS(ItemAccessFilters, ItemAccessFilterFlags) + public: DatabaseSanitizer(const Database& db, Transaction::TransactionType type); DatabaseSanitizer(Database* db, Transaction::TransactionType type); @@ -49,23 +59,26 @@ * \p urlFilter Filter result urls. Default is null = Print everything. */ void printList(const QVector& deviceIds, - const bool missingOnly, - const QSharedPointer& urlFilter + const ItemAccessFilters accessFilter = IgnoreNone, + const QSharedPointer& urlFilter = nullptr ); /** * Print info about known devices to stdout * * \p deviceIDs filter by device ids. Negative numbers list everything but... * with empty \p deviceIDs(default) everything is printed. * - * \p missingOnly Only inaccessible items are printed. + * \p accessFilter filter by accessibility. E.g IgnoreAvailable|IgnoreUnmounted + * prints only mounted devices with inaccessible files. */ - void printDevices(const QVector& deviceIds, const bool missingOnly = false); + void printDevices(const QVector& deviceIds, const ItemAccessFilters accessFilter = IgnoreNone); private: DatabaseSanitizer(const DatabaseSanitizer& rhs) = delete; DatabaseSanitizerImpl* m_pimpl; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(DatabaseSanitizer::ItemAccessFilters) + } #endif // BALOODATABASESANITIZER_H diff --git a/src/engine/experimental/databasesanitizer.cpp b/src/engine/experimental/databasesanitizer.cpp --- a/src/engine/experimental/databasesanitizer.cpp +++ b/src/engine/experimental/databasesanitizer.cpp @@ -60,21 +60,29 @@ cur++; } + /** + * Summary of createList() actions + */ + struct Summary { + quint64 total = 0; ///Count of all files + quint64 ignored = 0; ///Count of filtered out files + quint64 accessible = 0; ///Count of checked and accessible files + }; /** * Create a list of \a FileInfo items. * * \p deviceIDs filter by device ids. If the vector is empty no filtering is done * and every item is collected. * Positive numbers are including filters collecting only the mentioned device ids. * Negative numbers are excluding filters collecting everything but the mentioned device ids. * - * \p missingOnly Only inaccessible items are collected. + * \p accessFilter Flags to filter items by accessibility. * * \p urlFilter Filter result urls. Default is null = Collect everything. */ - QVector createList( + QPair, Summary> createList( const QVector& deviceIds, - const bool purging, + const DatabaseSanitizer::ItemAccessFilters accessFilter, const QSharedPointer& urlFilter ) const { @@ -86,9 +94,9 @@ const auto map = docUrlDb.toTestMap(); const auto keys = map.keys(); QVector result; - result.reserve(keys.count()); + uint max = map.size(); uint i = 0; - uint max = keys.count(); + result.reserve(max); QVector includeIds; QVector excludeIds; for (qint64 deviceId : deviceIds) { @@ -98,28 +106,42 @@ excludeIds.append(-deviceId); } } - + Summary summary; + summary.total = max; + summary.ignored = max; QTextStream err(stderr); - for (quint64 id: keys) { + + for (auto it = map.constBegin(), end = map.constEnd(); it != end; it++) { printProgress(err, i, max, 100); + const quint64 id = it.key(); + const quint32 deviceId = idToDeviceId(id); + if (!includeIds.isEmpty() && !includeIds.contains(deviceId)) { + continue; + } else if (excludeIds.contains(deviceId)) { + continue; + } else if (urlFilter && !urlFilter->match(it.value()).hasMatch()) { + continue; + } - const quint32* arr = reinterpret_cast(&id); - const auto url = docUrlDb.get(id); FileInfo info; - info.deviceId = arr[0]; - info.inode = arr[1]; - info.url = url; - info.accessible = !url.isEmpty() && QFileInfo::exists(url); - if ((!includeIds.isEmpty() && !includeIds.contains(info.deviceId)) - || (!excludeIds.isEmpty() && excludeIds.contains(info.deviceId)) - || (purging && info.accessible) - || (urlFilter && !urlFilter->match(info.url).hasMatch()) - ) { + info.deviceId = deviceId; + info.inode = idToInode(id); + info.url = QFile::decodeName(it.value()); + info.accessible = !info.url.isEmpty() && QFileInfo::exists(info.url); + + if (info.accessible && (accessFilter & DatabaseSanitizer::IgnoreAvailable)) { + continue; + } else if (!info.accessible && (accessFilter & DatabaseSanitizer::IgnoreUnavailable)) { continue; } + result.append(info); + summary.ignored--; + if (info.accessible) { + summary.accessible++; + } } - return result; + return {result, summary}; } QStorageInfo getStorageInfo(const quint32 id) { @@ -178,51 +200,58 @@ */ void DatabaseSanitizer::printList( const QVector& deviceIds, - const bool missingOnly, + const ItemAccessFilters accessFilter, const QSharedPointer& urlFilter) { - auto infos = m_pimpl->createList(deviceIds, missingOnly, urlFilter); + auto listResult = m_pimpl->createList(deviceIds, accessFilter, urlFilter); const auto sep = QLatin1Char(' '); QTextStream out(stdout); QTextStream err(stderr); - for (const auto& info: infos) { - if (!missingOnly) { - out << QStringLiteral("%1").arg(info.accessible ? "+" : "!") << sep; - } else if (!info.accessible) { - out << i18n("Missing:") << sep; - } else { - Q_ASSERT(false); - continue; - } - out << QStringLiteral("device: %1").arg(info.deviceId) + for (const auto& info: listResult.first) { + out << QStringLiteral("%1").arg(info.accessible ? "+" : "!") + << sep << QStringLiteral("device: %1").arg(info.deviceId) << sep << QStringLiteral("inode: %1").arg(info.inode) << sep << QStringLiteral("url: %1").arg(info.url) << endl; } - err << i18n("Found %1 matching items", infos.count()) << endl; + + const auto& summary = listResult.second; + if (accessFilter & IgnoreAvailable) { + err << i18n("Total: %1, Inaccessible: %2", + summary.total, + summary.total - (summary.ignored + summary.accessible)) << endl; + } else { + err << i18n("Total: %1, Ignored: %2, Accessible: %3, Inaccessible: %4", + summary.total, + summary.ignored, + summary.accessible, + summary.total - (summary.ignored + summary.accessible)) << endl; + } } -void DatabaseSanitizer::printDevices(const QVector& deviceIds, const bool missingOnly) +void DatabaseSanitizer::printDevices(const QVector& deviceIds, const ItemAccessFilters accessFilter) { - auto infos = m_pimpl->createList(deviceIds, false, nullptr); + auto infos = m_pimpl->createList(deviceIds, accessFilter, nullptr); QMap useCount; - for (const auto& info : infos) { + for (const auto& info : infos.first) { useCount[info.deviceId]++; } const auto sep = QLatin1Char(' '); QTextStream out(stdout); QTextStream err(stderr); int matchCount = 0; - for (auto it = useCount.cbegin(); it != useCount.cend(); it++) { auto id = it.key(); auto info = m_pimpl->getStorageInfo(id); auto mounted = info.isValid(); - - if (missingOnly && mounted) { + if (info.fileSystemType() == QLatin1String("tmpfs")) { + continue; + } else if (mounted && (accessFilter & IgnoreMounted)) { + continue; + } else if (!mounted && (accessFilter & IgnoreUnmounted)) { continue; } matchCount++; diff --git a/src/tools/experimental/baloodb/main.cpp b/src/tools/experimental/baloodb/main.cpp --- a/src/tools/experimental/baloodb/main.cpp +++ b/src/tools/experimental/baloodb/main.cpp @@ -61,6 +61,10 @@ QCommandLineOption{ QStringList{QStringLiteral("m"), QStringLiteral("missing-only")}, i18n("List only inaccessible entries.\nOnly applies to \"%1\"", QStringLiteral("list")) + }, + QCommandLineOption{ + QStringList{QStringLiteral("u"), QStringLiteral("mounted-only")}, + i18n("Act only on item on mounted devices") } }; @@ -102,7 +106,7 @@ QStringLiteral("devices"), i18n("List devices"), QStringList{}, - QStringList{QStringLiteral("missing-only")} + QStringList{QStringLiteral("missing-only"), QStringLiteral("mounted-only")} } }; @@ -205,7 +209,15 @@ for (const auto& dev : parser.values(QStringLiteral("device-id"))) { deviceIds.append(dev.toInt()); } - const bool missingOnly = parser.isSet(QStringLiteral("missing-only")); + const DatabaseSanitizer::ItemAccessFilters accessFilter = ( + parser.isSet(QStringLiteral("missing-only")) + ? DatabaseSanitizer::IgnoreAvailable + : DatabaseSanitizer::IgnoreNone + ) | ( + parser.isSet(QStringLiteral("mounted-only")) + ? DatabaseSanitizer::IgnoreUnmounted + : DatabaseSanitizer::IgnoreNone + ); const QString pattern = args.isEmpty() ? QString() : args.at(0); @@ -225,15 +237,15 @@ } DatabaseSanitizer san(db, Transaction::ReadOnly); err << i18n("Listing database contents...") << endl; - san.printList(deviceIds, missingOnly, urlFilter); + san.printList(deviceIds, accessFilter, urlFilter); } else if (command == QStringLiteral("devices")) { if (!db->open(Database::ReadOnlyDatabase)) { err << i18n("Baloo Index could not be opened") << endl; return 1; } DatabaseSanitizer san(db, Transaction::ReadOnly); err << i18n("Listing database contents...") << endl; - san.printDevices(deviceIds, missingOnly); + san.printDevices(deviceIds, accessFilter); } else if (command == QStringLiteral("clean")) { /* TODO: add prune command */