Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/owncloud/client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Kamm <mail@ckamm.de>2018-10-12 12:03:10 +0300
committerckamm <mail@ckamm.de>2018-10-12 13:06:48 +0300
commit55cf407337a5db93511f1cb061165c53fa6dba6e (patch)
tree9cc826d82cf598c7923e2ba16a938d2b10dbade8 /src
parentd0815239807540c423c52282e57a5ed8ebfd41e2 (diff)
Folderwatcher: On linux, fix paths after dir renames #6808
If a folder was renamed A -> B, the folder watcher for the inode would be unaware and still report changes for A/foo. Now directory renames in the watched folders are tracked and paths are updated accordingly.
Diffstat (limited to 'src')
-rw-r--r--src/gui/folderwatcher_linux.cpp64
-rw-r--r--src/gui/folderwatcher_linux.h33
2 files changed, 81 insertions, 16 deletions
diff --git a/src/gui/folderwatcher_linux.cpp b/src/gui/folderwatcher_linux.cpp
index 5f6d580b1..d2fdb7d8d 100644
--- a/src/gui/folderwatcher_linux.cpp
+++ b/src/gui/folderwatcher_linux.cpp
@@ -31,6 +31,11 @@ FolderWatcherPrivate::FolderWatcherPrivate(FolderWatcher *p, const QString &path
, _parent(p)
, _folder(path)
{
+ _wipePotentialRenamesSoon = new QTimer(this);
+ _wipePotentialRenamesSoon->setInterval(1000);
+ _wipePotentialRenamesSoon->setSingleShot(true);
+ connect(_wipePotentialRenamesSoon, &QTimer::timeout, this, &FolderWatcherPrivate::wipePotentialRenames);
+
_fd = inotify_init();
if (_fd != -1) {
_socket.reset(new QSocketNotifier(_fd, QSocketNotifier::Read));
@@ -91,6 +96,17 @@ void FolderWatcherPrivate::inotifyRegisterPath(const QString &path)
}
}
+void FolderWatcherPrivate::applyDirectoryRename(const FolderWatcherPrivate::Rename &rename)
+{
+ QString fromSlash = rename.from + "/";
+ qCInfo(lcFolderWatcher) << "Applying rename from" << rename.from << "to" << rename.to;
+ for (auto &watch : _watches) {
+ if (watch == rename.from || watch.startsWith(fromSlash)) {
+ watch = rename.to + watch.mid(rename.from.size());
+ }
+ }
+}
+
void FolderWatcherPrivate::slotAddFolderRecursive(const QString &path)
{
int subdirs = 0;
@@ -126,6 +142,11 @@ void FolderWatcherPrivate::slotAddFolderRecursive(const QString &path)
}
}
+void FolderWatcherPrivate::wipePotentialRenames()
+{
+ _potentialRenames.clear();
+}
+
void FolderWatcherPrivate::slotReceivedNotification(int fd)
{
int len;
@@ -154,33 +175,44 @@ void FolderWatcherPrivate::slotReceivedNotification(int fd)
}
} while (false);
- // reset counter
- i = 0;
- // while there are enough events in the buffer
- while (i + sizeof(struct inotify_event) < static_cast<unsigned int>(len)) {
+ // iterate events in buffer
+ unsigned int ulen = len;
+ for (i = 0; i + sizeof(inotify_event) < ulen; i += sizeof(inotify_event) + (event ? event->len : 0)) {
// cast an inotify_event
event = (struct inotify_event *)&buffer[i];
if (event == NULL) {
qCDebug(lcFolderWatcher) << "NULL event";
- i += sizeof(struct inotify_event);
continue;
}
// Fire event for the path that was changed.
- if (event->len > 0 && event->wd > -1) {
- QByteArray fileName(event->name);
- if (fileName.startsWith("._sync_")
- || fileName.startsWith(".csync_journal.db")
- || fileName.startsWith(".owncloudsync.log")
- || fileName.startsWith(".sync_")) {
+ if (event->len == 0 || event->wd <= -1)
+ continue;
+ QByteArray fileName(event->name);
+ if (fileName.startsWith("._sync_")
+ || fileName.startsWith(".csync_journal.db")
+ || fileName.startsWith(".owncloudsync.log")
+ || fileName.startsWith(".sync_")) {
+ continue;
+ }
+ const QString p = _watches[event->wd] + '/' + fileName;
+ _parent->changeDetected(p);
+
+ // Collect events to form complete renames where possible
+ // and apply directory renames to the cached paths.
+ if ((event->mask & (IN_MOVED_TO | IN_MOVED_FROM)) && (event->mask & IN_ISDIR) && event->cookie > 0) {
+ auto &rename = _potentialRenames[event->cookie];
+ if (event->mask & IN_MOVED_TO)
+ rename.to = p;
+ if (event->mask & IN_MOVED_FROM)
+ rename.from = p;
+ if (!rename.from.isEmpty() && !rename.to.isEmpty()) {
+ applyDirectoryRename(rename);
+ _potentialRenames.remove(event->cookie);
} else {
- const QString p = _watches[event->wd] + '/' + fileName;
- _parent->changeDetected(p);
+ _wipePotentialRenamesSoon->start();
}
}
-
- // increment counter
- i += sizeof(struct inotify_event) + event->len;
}
}
diff --git a/src/gui/folderwatcher_linux.h b/src/gui/folderwatcher_linux.h
index 24d337777..e9361eb42 100644
--- a/src/gui/folderwatcher_linux.h
+++ b/src/gui/folderwatcher_linux.h
@@ -23,6 +23,8 @@
#include "folderwatcher.h"
+class QTimer;
+
namespace OCC {
/**
@@ -44,10 +46,22 @@ protected slots:
void slotReceivedNotification(int fd);
void slotAddFolderRecursive(const QString &path);
+ /// Remove all half-built renames. Called by timer when idle for a bit.
+ void wipePotentialRenames();
+
protected:
+ struct Rename
+ {
+ QString from;
+ QString to;
+ };
+
bool findFoldersBelow(const QDir &dir, QStringList &fullList);
void inotifyRegisterPath(const QString &path);
+ /// Adjusts the paths in _watches when directories are renamed.
+ void applyDirectoryRename(const Rename &rename);
+
private:
FolderWatcher *_parent;
@@ -55,6 +69,25 @@ private:
QHash<int, QString> _watches;
QScopedPointer<QSocketNotifier> _socket;
int _fd;
+
+ /** Maps inotify event cookie to rename data.
+ *
+ * For moves two independent inotify events will be seen and they
+ * can be matched via the event cookie. This field stores partial
+ * information as it is received. When both sides have arrived,
+ * directory moves can be processed with applyDirectoryRename().
+ *
+ * If we don't receive both sides (if something moves away from
+ * the watched folder tree, or into it from an unwatched location)
+ * the _wipePotentialRenamesSoon will eventually discard the
+ * incomplete data.
+ *
+ * These events can even be emitted by different watches if the
+ * directory parent folder changed.
+ */
+ QHash<quint32, Rename> _potentialRenames;
+
+ QTimer *_wipePotentialRenamesSoon;
};
}