File indexing completed on 2024-04-14 03:49:45

0001 /*
0002     This file is part of the KDE Project
0003     SPDX-FileCopyrightText: 2007-2011 Sebastian Trueg <trueg@kde.org>
0004     SPDX-FileCopyrightText: 2012-2014 Vishesh Handa <me@vhanda.in>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "filewatch.h"
0010 #include "metadatamover.h"
0011 #include "fileindexerconfig.h"
0012 #include "pendingfilequeue.h"
0013 #include "regexpcache.h"
0014 #include "database.h"
0015 #include "baloodebug.h"
0016 
0017 #include "kinotify.h"
0018 
0019 #include <QDateTime>
0020 #include <QFileInfo>
0021 #include <QDir>
0022 
0023 using namespace Baloo;
0024 
0025 FileWatch::FileWatch(Database* db, FileIndexerConfig* config, QObject* parent)
0026     : QObject(parent)
0027     , m_db(db)
0028     , m_config(config)
0029     , m_dirWatch(nullptr)
0030 {
0031     Q_ASSERT(db);
0032     Q_ASSERT(config);
0033 
0034     m_metadataMover = new MetadataMover(m_db, this);
0035     connect(m_metadataMover, &MetadataMover::movedWithoutData, this, &FileWatch::indexNewFile);
0036     connect(m_metadataMover, &MetadataMover::fileRemoved, this, &FileWatch::fileRemoved);
0037 
0038     m_pendingFileQueue = new PendingFileQueue(this);
0039     connect(m_pendingFileQueue, &PendingFileQueue::indexNewFile, this, &FileWatch::indexNewFile);
0040     connect(m_pendingFileQueue, &PendingFileQueue::indexModifiedFile, this, &FileWatch::indexModifiedFile);
0041     connect(m_pendingFileQueue, &PendingFileQueue::indexXAttr, this, &FileWatch::indexXAttr);
0042     connect(m_pendingFileQueue, &PendingFileQueue::removeFileIndex, m_metadataMover, &MetadataMover::removeFileMetadata);
0043 
0044     // monitor the file system for changes (restricted by the inotify limit)
0045     m_dirWatch = new KInotify(m_config, this);
0046 
0047     connect(m_dirWatch, &KInotify::moved, this, &FileWatch::slotFileMoved);
0048     connect(m_dirWatch, &KInotify::deleted, this, &FileWatch::slotFileDeleted);
0049     connect(m_dirWatch, &KInotify::created, this, &FileWatch::slotFileCreated);
0050     connect(m_dirWatch, &KInotify::modified, this, &FileWatch::slotFileModified);
0051     connect(m_dirWatch, &KInotify::closedWrite, this, &FileWatch::slotFileClosedAfterWrite);
0052     connect(m_dirWatch, &KInotify::attributeChanged, this, &FileWatch::slotAttributeChanged);
0053     connect(m_dirWatch, &KInotify::watchUserLimitReached, this, &FileWatch::slotInotifyWatchUserLimitReached);
0054     connect(m_dirWatch, &KInotify::installedWatches, this, &FileWatch::installedWatches);
0055 }
0056 
0057 FileWatch::~FileWatch()
0058 {
0059 }
0060 
0061 // FIXME: listen to Create for folders!
0062 void FileWatch::watchFolder(const QString& path)
0063 {
0064     if (m_dirWatch && !m_dirWatch->watchingPath(path)) {
0065         KInotify::WatchEvents flags(KInotify::EventMove | KInotify::EventDelete | KInotify::EventDeleteSelf
0066                                     | KInotify::EventCloseWrite | KInotify::EventCreate
0067                                     | KInotify::EventAttributeChange | KInotify::EventModify);
0068 
0069         m_dirWatch->addWatch(path, flags, KInotify::FlagOnlyDir);
0070     }
0071 }
0072 
0073 void FileWatch::slotFileMoved(const QString& urlFrom, const QString& urlTo)
0074 {
0075     if (m_config->shouldBeIndexed(urlTo)) {
0076         m_metadataMover->moveFileMetadata(urlFrom, urlTo);
0077     } else {
0078         QFileInfo src(urlFrom);
0079         QString url = urlFrom;
0080 
0081         if (src.isDir() && !url.endsWith(QLatin1Char('/'))) {
0082             url.append(QLatin1Char('/'));
0083         }
0084 
0085         PendingFile file(url);
0086         file.setDeleted();
0087 
0088         m_pendingFileQueue->enqueue(file);
0089     }
0090 }
0091 
0092 void FileWatch::slotFileDeleted(const QString& urlString, bool isDir)
0093 {
0094     // Directories must always end with a trailing slash '/'
0095     QString url = urlString;
0096     if (isDir && !url.endsWith(QLatin1Char('/'))) {
0097         url.append(QLatin1Char('/'));
0098     }
0099 
0100     PendingFile file(url);
0101     file.setDeleted();
0102 
0103     m_pendingFileQueue->enqueue(file);
0104 }
0105 
0106 void FileWatch::slotFileCreated(const QString& path, bool isDir)
0107 {
0108     Q_UNUSED(isDir);
0109     PendingFile file(path);
0110     file.setCreated();
0111 
0112     m_pendingFileQueue->enqueue(file);
0113 }
0114 
0115 void FileWatch::slotFileModified(const QString& path)
0116 {
0117     PendingFile file(path);
0118     file.setModified();
0119 
0120     //qCDebug(BALOO) << "MOD" << path;
0121     m_pendingFileQueue->enqueue(file);
0122 }
0123 
0124 void FileWatch::slotFileClosedAfterWrite(const QString& path)
0125 {
0126     QDateTime current = QDateTime::currentDateTime();
0127     QDateTime fileModification = QFileInfo(path).lastModified();
0128     QDateTime dirModification = QFileInfo(QFileInfo(path).absoluteDir().absolutePath()).lastModified();
0129 
0130     // If we have received a FileClosedAfterWrite event, then the file must have been
0131     // closed within the last minute.
0132     // This is being done cause many applications open the file under write mode, do not
0133     // make any modifications and then close the file. This results in us getting
0134     // the FileClosedAfterWrite event
0135     if (fileModification.secsTo(current) <= 1000 * 60 || dirModification.secsTo(current) <= 1000 * 60) {
0136         PendingFile file(path);
0137         file.setClosedOnWrite();
0138         //qCDebug(BALOO) << "CLOSE" << path;
0139         m_pendingFileQueue->enqueue(file);
0140     }
0141 }
0142 
0143 void FileWatch::slotAttributeChanged(const QString& path)
0144 {
0145     PendingFile file(path);
0146     file.setAttributeChanged();
0147 
0148     m_pendingFileQueue->enqueue(file);
0149 }
0150 
0151 // This slot is connected to a signal emitted in KInotify when
0152 // inotify_add_watch fails with ENOSPC.
0153 void FileWatch::slotInotifyWatchUserLimitReached(const QString&)
0154 {
0155     // If we got here, we hit the limit and are not allowed to raise it,
0156     // so put something in the log.
0157     qCWarning(BALOO) << "KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.";
0158     // we do it the brutal way for now hoping with new kernels and defaults this will never happen
0159     // Delete the KInotify
0160     // FIXME: Maybe we should be aborting?
0161     if (m_dirWatch) {
0162         m_dirWatch->deleteLater();
0163         m_dirWatch = nullptr;
0164     }
0165     Q_EMIT installedWatches();
0166 }
0167 
0168 void FileWatch::updateIndexedFoldersWatches()
0169 {
0170     if (m_dirWatch) {
0171         const QStringList excludedFolders = m_config->excludeFolders();
0172         const QStringList includedFolders = m_config->includeFolders();
0173 
0174         for (const QString& folder : excludedFolders) {
0175             // Remove watch for new excluded folders
0176             if (!m_excludedFolders.contains(folder)) {
0177                 m_dirWatch->removeWatch(folder);
0178             }
0179         }
0180         for (const QString& folder : m_excludedFolders) {
0181             // Add no longer excluded folders
0182             if (!excludedFolders.contains(folder)) {
0183                 watchFolder(folder);
0184             }
0185         }
0186 
0187         for (const QString& folder : m_includedFolders) {
0188             // Remove no longer included folders
0189             if (!includedFolders.contains(folder)) {
0190                 m_dirWatch->removeWatch(folder);
0191             }
0192         }
0193         for (const QString& folder : includedFolders) {
0194             if (!m_includedFolders.contains(folder)) {
0195                 watchFolder(folder);
0196             }
0197         }
0198 
0199         m_excludedFolders = excludedFolders;
0200         m_includedFolders = includedFolders;
0201     }
0202 }
0203 
0204 #include "moc_filewatch.cpp"