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 @@ -24,7 +24,7 @@ #include "transaction.h" -namespace Baloo +namespace Baloo { class DatabaseSanitizerImpl; /** @@ -37,31 +37,31 @@ DatabaseSanitizer(const Database& db, Transaction::TransactionType type); DatabaseSanitizer(Database* db, Transaction::TransactionType type); ~DatabaseSanitizer(); - - /** + + /** * Print database content to stdout - * + * * \p deviceIDs filter by device ids. Negative numbers list everything but... * with empty \p deviceIDs(default) everything is printed. - * + * * \p missingOnly Simulate purging operation. Only inaccessible items are printed. - * + * * \p urlFilter Filter result urls. Default is null = Print everything. */ - void printList(const QVector& deviceIds, - const bool missingOnly, + void printList(const QVector& deviceIds, + const bool missingOnly, const QSharedPointer& urlFilter ); - /** + /** * 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. */ void printDevices(const QVector& deviceIds, const bool missingOnly = false); - + private: DatabaseSanitizer(const DatabaseSanitizer& rhs) = delete; DatabaseSanitizerImpl* m_pimpl; 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 @@ -21,14 +21,18 @@ #include "databasesanitizer.h" #include "documenturldb.h" -#include "baloodebug.h" +#include "fsutils.h" +#include "idutils.h" + +#include #include #include +#include namespace Baloo { - + class DatabaseSanitizerImpl { public: DatabaseSanitizerImpl(const Database& db, Transaction::TransactionType type) @@ -41,43 +45,43 @@ * \brief Basic info about database items */ struct FileInfo { - quint32 deviceId = 0; + quint32 deviceId = 0; quint32 inode = 0; QString url = QString(); bool accessible = true; }; - + void printProgress(QTextStream& out, uint& cur, const uint max, const uint step) const { if (cur % step == 0) { out << QStringLiteral("%1%2\r").arg(100 * cur / max, 6).arg("%", -16); out.flush(); } cur++; } - + /** * 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 urlFilter Filter result urls. Default is null = Collect everything. */ QVector createList( - const QVector& deviceIds, - const bool purging, + const QVector& deviceIds, + const bool purging, const QSharedPointer& urlFilter ) const { Q_ASSERT(m_transaction); - const auto docUrlDb = DocumentUrlDB(m_transaction->m_dbis.idTreeDbi, - m_transaction->m_dbis.idFilenameDbi, + const auto docUrlDb = DocumentUrlDB(m_transaction->m_dbis.idTreeDbi, + m_transaction->m_dbis.idFilenameDbi, m_transaction->m_txn); const auto map = docUrlDb.toTestMap(); const auto keys = map.keys(); @@ -94,11 +98,11 @@ excludeIds.append(-deviceId); } } - + QTextStream err(stderr); for (quint64 id: keys) { printProgress(err, i, max, 100); - + const quint32* arr = reinterpret_cast(&id); const auto url = docUrlDb.get(id); FileInfo info; @@ -117,17 +121,54 @@ } return result; } - - QMultiHash createDeviceList(const QVector& deviceIds) + + struct DeviceInfo { + quint32 id = 0; + int items = 0; + FSUtils::DeviceInfo fsInfo = {}; + bool mounted = false; + }; + + int fillInDeviceInfo(const quint32 id, DeviceInfo& deviceInfo) { + static QMap deviceInfos = []() { + QMap result; + const auto devices = FSUtils::attachedDevices(); + for (const auto& dev : devices) { + const QByteArray filePath = QFile::encodeName(dev.mountpoint); + const auto fsinfo = filePathToStat(filePath); + const quint32 id = static_cast(fsinfo.st_dev); + DeviceInfo devInfo; + devInfo.id = id; + devInfo.fsInfo = dev; + devInfo.mounted = true && (dev.filesystem != QLatin1String("tmpfs")); + qDebug() << "filesystem" << dev.filesystem; + result[id] = devInfo; + } + return result; + }(); + + deviceInfo.id = id; + if (deviceInfos.count(id) == 1) { + const DeviceInfo devInf = deviceInfos[deviceInfo.id]; + deviceInfo.fsInfo = devInf.fsInfo ; + deviceInfo.mounted = devInf.mounted; + return 0; + } + return 1; + } + + QMap createDeviceList(const QVector& infos) { - auto infos = createList(deviceIds, false, nullptr); - QMultiHash usedDevices; - for (const auto& info: infos) { - usedDevices.insert(info.deviceId, info); + QMap usedDevices; + for (const auto& info : infos) { + usedDevices[info.deviceId].items++; + } + for (auto it = usedDevices.begin(), end = usedDevices.end(); it != end; it++) { + fillInDeviceInfo(it.key(), it.value()); } return usedDevices; } - + private: Transaction* m_transaction; }; @@ -154,19 +195,19 @@ /** * Create a list of \a FileInfo items and print it to stdout. -* +* * \p deviceIDs filter by device ids. If the vector is empty no filtering is done * and everything is printed. * Positive numbers are including filters printing only the mentioned device ids. * Negative numbers are excluding filters printing everything but the mentioned device ids. -* +* * \p missingOnly Simulate purging operation. Only inaccessible items are printed. -* +* * \p urlFilter Filter result urls. Default is null = Print everything. */ void DatabaseSanitizer::printList( - const QVector& deviceIds, - const bool missingOnly, + const QVector& deviceIds, + const bool missingOnly, const QSharedPointer& urlFilter) { auto infos = m_pimpl->createList(deviceIds, missingOnly, urlFilter); @@ -188,27 +229,44 @@ << endl; } err << i18n("Found %1 matching items", infos.count()) << endl; - + } void DatabaseSanitizer::printDevices(const QVector& deviceIds, const bool missingOnly) { - Q_UNUSED(missingOnly) - /* - * TODO: Implement missingOnly filter. Checking for file existence - * will not work. We need to read /etc/mtab or so. - */ - auto usedDevices = m_pimpl->createDeviceList(deviceIds); - + auto infos = m_pimpl->createList(deviceIds, false, nullptr); + auto usedDevices = m_pimpl->createDeviceList(infos); + const auto sep = QLatin1Char(' '); QTextStream out(stdout); QTextStream err(stderr); - - for (const auto& dev: usedDevices.uniqueKeys()) { - out << "Device:" << dev - << sep << usedDevices.values(dev).count() << sep << "items" - << endl; + int matchCount = 0; + for (const auto& dev : usedDevices) { + if (missingOnly && dev.mounted) { + continue; + } + matchCount++; + // TODO coloring would be nice, but "...|grep '^!'" does not work with it. + // out << QStringLiteral("%1").arg(dev.mounted ? "+" : "\033[1;31m!") + // Can be done, see: https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/global/qlogging.cpp#n263 + out << QStringLiteral("%1").arg(dev.mounted ? "+" : "!") + << sep << QStringLiteral("device:%1").arg(dev.id) + << sep << QStringLiteral("[%1:%2]") + .arg(major(dev.id), 4, 16, QLatin1Char('0')) + .arg(minor(dev.id), 4, 16, QLatin1Char('0')) + << sep << QStringLiteral("indexed-items:%1").arg(dev.items); + + if (dev.mounted) { + out + << sep << QStringLiteral("fstype:%1").arg(dev.fsInfo.filesystem) + << sep << QStringLiteral("fsname:%1").arg(dev.fsInfo.name) + << sep << QStringLiteral("mount:%1").arg(dev.fsInfo.mountpoint) + ; + } + // TODO: see above + // out << QStringLiteral("\033[0m") << endl; + out << endl; } - - err << i18n("Found %1 matching items", usedDevices.count()) << endl; + + err << i18n("Found %1 matching in %2 devices", matchCount, usedDevices.size()) << endl; } diff --git a/src/engine/fsutils.h b/src/engine/fsutils.h --- a/src/engine/fsutils.h +++ b/src/engine/fsutils.h @@ -25,6 +25,7 @@ #define BALOO_ENGINE_FSUTILS_H #include +#include namespace Baloo { namespace FSUtils { @@ -45,6 +46,16 @@ */ void disableCoW(const QString &path); +struct DeviceInfo { + quint64 id = 0; + QString filesystem; + QString name; + QString mountpoint; + QStringList options; +}; + +const QVector attachedDevices(); + } } diff --git a/src/engine/fsutils.cpp b/src/engine/fsutils.cpp --- a/src/engine/fsutils.cpp +++ b/src/engine/fsutils.cpp @@ -107,3 +107,31 @@ close(fd); #endif } + +const QVector FSUtils::attachedDevices() +{ + QVector result; +#ifndef Q_OS_LINUX + return result; +#else + FILE *mtab = setmntent("/etc/mtab", "r"); + if (!mtab) { + return result; + } + while (mntent *mnt = getmntent(mtab)) { + if (qstrcmp(mnt->mnt_type, MNTTYPE_IGNORE) == 0) { + continue; + } + DeviceInfo info; + info.mountpoint = QString::fromLocal8Bit(mnt->mnt_dir); + info.filesystem = QString::fromLocal8Bit(mnt->mnt_type); + info.name = QString::fromLocal8Bit(mnt->mnt_fsname); + info.options = QString::fromLocal8Bit(mnt->mnt_opts).split(QLatin1Char(',')); + result.append(info); + } + + endmntent(mtab); + + return result; +#endif +} 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 @@ -233,7 +233,7 @@ } DatabaseSanitizer san(db, Transaction::ReadOnly); err << i18n("Listing database contents...") << endl; - san.printDevices(deviceIds); + san.printDevices(deviceIds, missingOnly); } else if (command == QStringLiteral("clean")) { /* TODO: add prune command */