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 | |
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(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 | |
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)) { |
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 | |
73 | void 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 | |
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(c: QLatin1Char('/'))) { |
97 | url.append(c: 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 | { |
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 | |
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(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 | |