Baloo

filewatch.cpp
1 /*
2  This file is part of the KDE Project
3  SPDX-FileCopyrightText: 2007-2011 Sebastian Trueg <[email protected]>
4  SPDX-FileCopyrightText: 2012-2014 Vishesh Handa <[email protected]>
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 
23 using namespace Baloo;
24 
25 FileWatch::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 
57 FileWatch::~FileWatch()
58 {
59 }
60 
61 // FIXME: listen to Create for folders!
62 void FileWatch::watchFolder(const QString& path)
63 {
64  if (m_dirWatch && !m_dirWatch->watchingPath(path)) {
66  | KInotify::EventCloseWrite | KInotify::EventCreate
68 
69  m_dirWatch->addWatch(path, flags, KInotify::FlagOnlyDir);
70  }
71 }
72 
73 void 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 
92 void 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 
106 void 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 
115 void 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 
124 void FileWatch::slotFileClosedAfterWrite(const QString& path)
125 {
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 
143 void 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.
153 void 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 
168 void 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"
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
@ EventModify
File was modified (compare inotify's IN_MODIFY)
Definition: kinotify.h:46
QDateTime currentDateTime()
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
Represents a file which needs to be indexed.
Definition: pendingfile.h:19
void moved(const QString &oldName, const QString &newName)
Emitted if a file or folder has been moved or renamed.
@ EventDelete
File/directory created in watched directory (compare inotify's IN_CREATE)
Definition: kinotify.h:44
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
@ EventDeleteSelf
Watched file/directory was itself deleted (compare inotify's IN_DELETE_SELF)
Definition: kinotify.h:45
void deleted(const QString &file, bool isDir)
Emitted if a watched file or folder has been deleted.
qint64 secsTo(const QDateTime &other) const const
void created(const QString &file, bool isDir)
Emitted if a new file has been created in one of the watched folders (KInotify::EventCreate)
Implements storage for docIds without any associated data Instantiated for:
Definition: coding.cpp:11
@ EventCloseWrite
File opened for writing was closed (compare inotify's IN_CLOSE_WRITE)
Definition: kinotify.h:41
Active config class which emits signals if the config was changed, for example if the KCM saved the c...
void attributeChanged(const QString &file)
Emitted if file attributes are changed (KInotify::EventAttributeChange)
KSharedConfigPtr config()
A simple wrapper around inotify which only allows to add folders recursively.
Definition: kinotify.h:24
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
QDateTime lastModified() const const
void installedWatches()
This is emitted once watches have been installed in all the directories indicated by addWatch.
QString & append(QChar ch)
void modified(const QString &file)
Emitted if a watched file is modified (KInotify::EventModify)
@ EventAttributeChange
Metadata changed (permissions, timestamps, extended attributes, etc., compare inotify's IN_ATTRIB)
Definition: kinotify.h:40
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Nov 29 2023 03:56:26 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.