Baloo

filewatch.cpp
1/*
2 This file is part of the KDE Project
3 SPDX-FileCopyrightText: 2007-2011 Sebastian Trueg <trueg@kde.org>
4 SPDX-FileCopyrightText: 2012-2014 Vishesh Handa <me@vhanda.in>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "filewatch.h"
10#include "metadatamover.h"
11#include "fileindexerconfig.h"
12#include "pendingfilequeue.h"
13#include "regexpcache.h"
14#include "database.h"
15#include "baloodebug.h"
16
17#include "kinotify.h"
18
19#include <QDateTime>
20#include <QFileInfo>
21#include <QDir>
22
23using namespace Baloo;
24
25FileWatch::FileWatch(Database* db, FileIndexerConfig* config, QObject* parent)
26 : QObject(parent)
27 , m_db(db)
28 , m_config(config)
29 , m_dirWatch(nullptr)
30{
31 Q_ASSERT(db);
32 Q_ASSERT(config);
33
34 m_metadataMover = new MetadataMover(m_db, this);
35 connect(m_metadataMover, &MetadataMover::movedWithoutData, this, &FileWatch::indexNewFile);
36 connect(m_metadataMover, &MetadataMover::fileRemoved, this, &FileWatch::fileRemoved);
37
38 m_pendingFileQueue = new PendingFileQueue(this);
39 connect(m_pendingFileQueue, &PendingFileQueue::indexNewFile, this, &FileWatch::indexNewFile);
40 connect(m_pendingFileQueue, &PendingFileQueue::indexModifiedFile, this, &FileWatch::indexModifiedFile);
41 connect(m_pendingFileQueue, &PendingFileQueue::indexXAttr, this, &FileWatch::indexXAttr);
42 connect(m_pendingFileQueue, &PendingFileQueue::removeFileIndex, m_metadataMover, &MetadataMover::removeFileMetadata);
43
44 // monitor the file system for changes (restricted by the inotify limit)
45 m_dirWatch = new KInotify(m_config, this);
46
47 connect(m_dirWatch, &KInotify::moved, this, &FileWatch::slotFileMoved);
48 connect(m_dirWatch, &KInotify::deleted, this, &FileWatch::slotFileDeleted);
49 connect(m_dirWatch, &KInotify::created, this, &FileWatch::slotFileCreated);
50 connect(m_dirWatch, &KInotify::modified, this, &FileWatch::slotFileModified);
51 connect(m_dirWatch, &KInotify::closedWrite, this, &FileWatch::slotFileClosedAfterWrite);
52 connect(m_dirWatch, &KInotify::attributeChanged, this, &FileWatch::slotAttributeChanged);
53 connect(m_dirWatch, &KInotify::watchUserLimitReached, this, &FileWatch::slotInotifyWatchUserLimitReached);
54 connect(m_dirWatch, &KInotify::installedWatches, this, &FileWatch::installedWatches);
55}
56
57FileWatch::~FileWatch()
58{
59}
60
61// FIXME: listen to Create for folders!
62void FileWatch::watchFolder(const QString& path)
63{
64 if (m_dirWatch && !m_dirWatch->watchingPath(path)) {
65 KInotify::WatchEvents flags(KInotify::EventMove | KInotify::EventDelete | KInotify::EventDeleteSelf
66 | KInotify::EventCloseWrite | KInotify::EventCreate
68
69 m_dirWatch->addWatch(path, flags, KInotify::FlagOnlyDir);
70 }
71}
72
73void FileWatch::slotFileMoved(const QString& urlFrom, const QString& urlTo)
74{
75 if (m_config->shouldBeIndexed(urlTo)) {
76 m_metadataMover->moveFileMetadata(urlFrom, urlTo);
77 } else {
78 QFileInfo src(urlFrom);
79 QString url = urlFrom;
80
81 if (src.isDir() && !url.endsWith(QLatin1Char('/'))) {
82 url.append(QLatin1Char('/'));
83 }
84
85 PendingFile file(url);
86 file.setDeleted();
87
88 m_pendingFileQueue->enqueue(file);
89 }
90}
91
92void FileWatch::slotFileDeleted(const QString& urlString, bool isDir)
93{
94 // Directories must always end with a trailing slash '/'
95 QString url = urlString;
96 if (isDir && !url.endsWith(QLatin1Char('/'))) {
97 url.append(QLatin1Char('/'));
98 }
99
100 PendingFile file(url);
101 file.setDeleted();
102
103 m_pendingFileQueue->enqueue(file);
104}
105
106void FileWatch::slotFileCreated(const QString& path, bool isDir)
107{
108 Q_UNUSED(isDir);
109 PendingFile file(path);
110 file.setCreated();
111
112 m_pendingFileQueue->enqueue(file);
113}
114
115void FileWatch::slotFileModified(const QString& path)
116{
117 PendingFile file(path);
118 file.setModified();
119
120 //qCDebug(BALOO) << "MOD" << path;
121 m_pendingFileQueue->enqueue(file);
122}
123
124void FileWatch::slotFileClosedAfterWrite(const QString& path)
125{
126 QDateTime current = QDateTime::currentDateTime();
127 QDateTime fileModification = QFileInfo(path).lastModified();
128 QDateTime dirModification = QFileInfo(QFileInfo(path).absoluteDir().absolutePath()).lastModified();
129
130 // If we have received a FileClosedAfterWrite event, then the file must have been
131 // closed within the last minute.
132 // This is being done cause many applications open the file under write mode, do not
133 // make any modifications and then close the file. This results in us getting
134 // the FileClosedAfterWrite event
135 if (fileModification.secsTo(current) <= 1000 * 60 || dirModification.secsTo(current) <= 1000 * 60) {
136 PendingFile file(path);
137 file.setClosedOnWrite();
138 //qCDebug(BALOO) << "CLOSE" << path;
139 m_pendingFileQueue->enqueue(file);
140 }
141}
142
143void FileWatch::slotAttributeChanged(const QString& path)
144{
145 PendingFile file(path);
146 file.setAttributeChanged();
147
148 m_pendingFileQueue->enqueue(file);
149}
150
151// This slot is connected to a signal emitted in KInotify when
152// inotify_add_watch fails with ENOSPC.
153void FileWatch::slotInotifyWatchUserLimitReached(const QString&)
154{
155 // If we got here, we hit the limit and are not allowed to raise it,
156 // so put something in the log.
157 qCWarning(BALOO) << "KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored.";
158 // we do it the brutal way for now hoping with new kernels and defaults this will never happen
159 // Delete the KInotify
160 // FIXME: Maybe we should be aborting?
161 if (m_dirWatch) {
162 m_dirWatch->deleteLater();
163 m_dirWatch = nullptr;
164 }
165 Q_EMIT installedWatches();
166}
167
168void FileWatch::updateIndexedFoldersWatches()
169{
170 if (m_dirWatch) {
171 const QStringList excludedFolders = m_config->excludeFolders();
172 const QStringList includedFolders = m_config->includeFolders();
173
174 for (const QString& folder : excludedFolders) {
175 // Remove watch for new excluded folders
176 if (!m_excludedFolders.contains(folder)) {
177 m_dirWatch->removeWatch(folder);
178 }
179 }
180 for (const QString& folder : m_excludedFolders) {
181 // Add no longer excluded folders
182 if (!excludedFolders.contains(folder)) {
183 watchFolder(folder);
184 }
185 }
186
187 for (const QString& folder : m_includedFolders) {
188 // Remove no longer included folders
189 if (!includedFolders.contains(folder)) {
190 m_dirWatch->removeWatch(folder);
191 }
192 }
193 for (const QString& folder : includedFolders) {
194 if (!m_includedFolders.contains(folder)) {
195 watchFolder(folder);
196 }
197 }
198
199 m_excludedFolders = excludedFolders;
200 m_includedFolders = includedFolders;
201 }
202}
203
204#include "moc_filewatch.cpp"
Active config class which emits signals if the config was changed, for example if the KCM saved the c...
A simple wrapper around inotify which only allows to add folders recursively.
Definition kinotify.h:25
void installedWatches()
This is emitted once watches have been installed in all the directories indicated by addWatch.
void created(const QString &file, bool isDir)
Emitted if a new file has been created in one of the watched folders (KInotify::EventCreate)
@ EventDelete
File/directory created in watched directory (compare inotify's IN_CREATE)
Definition kinotify.h:44
@ EventDeleteSelf
Watched file/directory was itself deleted (compare inotify's IN_DELETE_SELF)
Definition kinotify.h:45
@ EventCloseWrite
File opened for writing was closed (compare inotify's IN_CLOSE_WRITE)
Definition kinotify.h:41
@ EventModify
File was modified (compare inotify's IN_MODIFY)
Definition kinotify.h:46
@ EventAttributeChange
Metadata changed (permissions, timestamps, extended attributes, etc., compare inotify's IN_ATTRIB)
Definition kinotify.h:40
void modified(const QString &file)
Emitted if a watched file is modified (KInotify::EventModify)
void moved(const QString &oldName, const QString &newName)
Emitted if a file or folder has been moved or renamed.
void deleted(const QString &file, bool isDir)
Emitted if a watched file or folder has been deleted.
void watchUserLimitReached(const QString &path)
Emitted if during updating the internal watch structures (recursive watches) the inotify user watch l...
void closedWrite(const QString &file)
Emitted if FIXME (KInotify::EventCloseWrite)
@ FlagOnlyDir
Only watch the path if it is a directory (IN_ONLYDIR)
Definition kinotify.h:76
void attributeChanged(const QString &file)
Emitted if file attributes are changed (KInotify::EventAttributeChange)
Implements storage for docIds without any associated data Instantiated for:
Definition coding.cpp:11
QDateTime currentDateTime()
qint64 secsTo(const QDateTime &other) const const
Q_EMITQ_EMIT
QString & append(QChar ch)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:50:57 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.