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(sender: m_metadataMover, signal: &MetadataMover::movedWithoutData, context: this, slot: &FileWatch::indexNewFile);
36 connect(sender: m_metadataMover, signal: &MetadataMover::fileRemoved, context: this, slot: &FileWatch::fileRemoved);
37
38 m_pendingFileQueue = new PendingFileQueue(this);
39 connect(sender: m_pendingFileQueue, signal: &PendingFileQueue::indexNewFile, context: this, slot: &FileWatch::indexNewFile);
40 connect(sender: m_pendingFileQueue, signal: &PendingFileQueue::indexModifiedFile, context: this, slot: &FileWatch::indexModifiedFile);
41 connect(sender: m_pendingFileQueue, signal: &PendingFileQueue::indexXAttr, context: this, slot: &FileWatch::indexXAttr);
42 connect(sender: m_pendingFileQueue, signal: &PendingFileQueue::removeFileIndex, context: m_metadataMover, slot: &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(sender: m_dirWatch, signal: &KInotify::moved, context: this, slot: &FileWatch::slotFileMoved);
48 connect(sender: m_dirWatch, signal: &KInotify::deleted, context: this, slot: &FileWatch::slotFileDeleted);
49 connect(sender: m_dirWatch, signal: &KInotify::created, context: this, slot: &FileWatch::slotFileCreated);
50 connect(sender: m_dirWatch, signal: &KInotify::modified, context: this, slot: &FileWatch::slotFileModified);
51 connect(sender: m_dirWatch, signal: &KInotify::closedWrite, context: this, slot: &FileWatch::slotFileClosedAfterWrite);
52 connect(sender: m_dirWatch, signal: &KInotify::attributeChanged, context: this, slot: &FileWatch::slotAttributeChanged);
53 connect(sender: m_dirWatch, signal: &KInotify::watchUserLimitReached, context: this, slot: &FileWatch::slotInotifyWatchUserLimitReached);
54 connect(sender: m_dirWatch, signal: &KInotify::installedWatches, context: this, slot: &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
67 | KInotify::EventAttributeChange | KInotify::EventModify);
68
69 m_dirWatch->addWatch(path, modes: flags, flags: KInotify::FlagOnlyDir);
70 }
71}
72
73void FileWatch::slotFileMoved(const QString& urlFrom, const QString& urlTo)
74{
75 if (m_config->shouldBeIndexed(path: urlTo)) {
76 m_metadataMover->moveFileMetadata(from: urlFrom, to: urlTo);
77 } else {
78 QFileInfo src(urlFrom);
79 QString url = urlFrom;
80
81 if (src.isDir() && !url.endsWith(c: QLatin1Char('/'))) {
82 url.append(c: 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(c: QLatin1Char('/'))) {
97 url.append(c: 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(str: folder)) {
177 m_dirWatch->removeWatch(path: folder);
178 }
179 }
180 for (const QString& folder : m_excludedFolders) {
181 // Add no longer excluded folders
182 if (!excludedFolders.contains(str: folder)) {
183 watchFolder(path: folder);
184 }
185 }
186
187 for (const QString& folder : m_includedFolders) {
188 // Remove no longer included folders
189 if (!includedFolders.contains(str: folder)) {
190 m_dirWatch->removeWatch(path: folder);
191 }
192 }
193 for (const QString& folder : includedFolders) {
194 if (!m_includedFolders.contains(str: folder)) {
195 watchFolder(path: folder);
196 }
197 }
198
199 m_excludedFolders = excludedFolders;
200 m_includedFolders = includedFolders;
201 }
202}
203
204#include "moc_filewatch.cpp"
205

source code of baloo/src/file/filewatch.cpp