Journal: Don't use a ._ path if it won't work #5633
authorChristian Kamm <mail@ckamm.de>
Fri, 16 Jun 2017 13:43:21 +0000 (15:43 +0200)
committerMarkus Goetz <markus@woboq.com>
Tue, 20 Jun 2017 11:35:34 +0000 (13:35 +0200)
When synchronizing a folder on a samba share, creating files that begin
with ._ is often forbidden. This prevented the client from creating
its ._sync_abcdef.db file.

Now, it'll check whether the preferred filename is creatable, and if
it isn't it'll use .sync_abcdef.db instead.

The disadvantage is that this alternative path won't be ignored by
older clients - that was the reason for the ._ prefix.

csync/src/csync_exclude.c
csync/tests/csync_tests/check_csync_exclude.c
src/cmd/cmd.cpp
src/gui/folder.cpp
src/gui/folderman.cpp
src/gui/folderwatcher_linux.cpp
src/libsync/syncjournaldb.cpp
src/libsync/syncjournaldb.h

index e125b33462539e14d532eeee0193189c67887ca6..88dd61e750183af7dfc9c6555789017a1fc9f34b 100644 (file)
@@ -235,6 +235,11 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
         match = CSYNC_FILE_SILENTLY_EXCLUDED;
         goto out;
     }
+    rc = csync_fnmatch(".sync_*.db*", bname, 0);
+    if (rc == 0) {
+        match = CSYNC_FILE_SILENTLY_EXCLUDED;
+        goto out;
+    }
     rc = csync_fnmatch(".csync_journal.db*", bname, 0);
     if (rc == 0) {
         match = CSYNC_FILE_SILENTLY_EXCLUDED;
index e7af73c67fa26b43f46790d2b11f410120c33231..bd4813c5ed01ac250b8bc088e45839f95d96e6e8 100644 (file)
@@ -157,7 +157,16 @@ static void check_csync_excluded(void **state)
     assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
     rc = csync_excluded_no_ctx(csync->excludes, "subdir/._sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE);
     assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
-    
+
+    rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE);
+    assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
+    rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db.ctmp", CSYNC_FTW_TYPE_FILE);
+    assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
+    rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db-shm", CSYNC_FTW_TYPE_FILE);
+    assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
+    rc = csync_excluded_no_ctx(csync->excludes, "subdir/.sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE);
+    assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
+
 
     /* pattern ]*.directory - ignore and remove */
     rc = csync_excluded_no_ctx(csync->excludes, "my.~directory", CSYNC_FTW_TYPE_FILE);
index 5421b23143ba8e8d46aa215d1bdb45f1ab2e5e29..152f24996222afbbe01c9409013f817116cf8b06 100644 (file)
@@ -469,7 +469,7 @@ restart_sync:
     }
 
     Cmd cmd;
-    QString dbPath = options.source_dir + SyncJournalDb::makeDbName(credentialFreeUrl, folder, user);
+    QString dbPath = options.source_dir + SyncJournalDb::makeDbName(options.source_dir, credentialFreeUrl, folder, user);
     SyncJournalDb db(dbPath);
 
     if (!selectiveSyncList.empty()) {
index 83ed5c531e0601aebad55f973de6b71f9aef8d92..7e2d12b1ca1810727b0a94508f8789b632f7b83b 100644 (file)
@@ -1003,7 +1003,7 @@ QString FolderDefinition::absoluteJournalPath() const
 
 QString FolderDefinition::defaultJournalPath(AccountPtr account)
 {
-    return SyncJournalDb::makeDbName(account->url(), targetPath, account->credentials()->user());
+    return SyncJournalDb::makeDbName(localPath, account->url(), targetPath, account->credentials()->user());
 }
 
 } // namespace OCC
index 1a230a28ca6a417da319a00a09d2028e1debf619..cb0fa90b532c69768408750c839efa7b4611bd63 100644 (file)
@@ -235,11 +235,22 @@ void FolderMan::setupFoldersHelper(QSettings &settings, AccountStatePtr account,
     foreach (const auto& folderAlias, settings.childGroups()) {
         FolderDefinition folderDefinition;
         if (FolderDefinition::load(settings, folderAlias, &folderDefinition)) {
+            auto defaultJournalPath = folderDefinition.defaultJournalPath(account->account());
+
             // Migration: Old settings don't have journalPath
             if (folderDefinition.journalPath.isEmpty()) {
-                folderDefinition.journalPath = folderDefinition.defaultJournalPath(account->account());
+                folderDefinition.journalPath = defaultJournalPath;
+            }
+
+            // Migration: ._ files sometimes don't work
+            // So if the configured journalPath is the default one ("._sync_*.db")
+            // but the current default doesn't have the underscore, switch to the
+            // new default. See SyncJournalDb::makeDbName().
+            if (folderDefinition.journalPath.startsWith("._sync_")
+                && defaultJournalPath.startsWith(".sync_")) {
+                folderDefinition.journalPath = defaultJournalPath;
             }
-            folderDefinition.defaultJournalPath(account->account());
+
             // Migration: If an old db is found, move it to the new name.
             if (backwardsCompatible) {
                 SyncJournalDb::maybeMigrateDb(folderDefinition.localPath, folderDefinition.absoluteJournalPath());
index c8024c00b4fc055b8fbda14404c6b852b41222c3..d647f659599fd23f8a9a4ec3a02fbff937ea7e91 100644 (file)
@@ -171,7 +171,8 @@ void FolderWatcherPrivate::slotReceivedNotification(int fd)
             // qDebug() << Q_FUNC_INFO << event->name;
             if (fileName.startsWith("._sync_") ||
                     fileName.startsWith(".csync_journal.db") ||
-                    fileName.startsWith(".owncloudsync.log")) {
+                    fileName.startsWith(".owncloudsync.log") ||
+                    fileName.startsWith(".sync_")) {
                 // qDebug() << "ignore journal";
             } else {
                 const QString p = _watches[event->wd] + '/' + fileName;
index 43ef9b37aaf2e1637809e64b690476f4ed6767a5..c6c4a9cd5bb1cdc34c7bd3cb286c1a88317a0af5 100644 (file)
@@ -17,6 +17,7 @@
 #include <QDebug>
 #include <QElapsedTimer>
 #include <QUrl>
+#include <QDir>
 
 #include "ownsql.h"
 
@@ -41,7 +42,8 @@ SyncJournalDb::SyncJournalDb(const QString& dbFilePath, QObject *parent) :
 
 }
 
-QString SyncJournalDb::makeDbName(const QUrl& remoteUrl,
+QString SyncJournalDb::makeDbName(const QString& localPath,
+                                  const QUrl& remoteUrl,
                                   const QString& remotePath,
                                   const QString& user)
 {
@@ -56,6 +58,42 @@ QString SyncJournalDb::makeDbName(const QUrl& remoteUrl,
     journalPath.append( ba.left(6).toHex() );
     journalPath.append(".db");
 
+    // If the journal doesn't exist and we can't create a file
+    // at that location, try again with a journal name that doesn't
+    // have the ._ prefix.
+    //
+    // The disadvantage of that filename is that it will only be ignored
+    // by client versions >2.3.2.
+    //
+    // See #5633: "._*" is often forbidden on samba shared folders.
+
+    // If it exists already, the path is clearly usable
+    QFile file(QDir(localPath).filePath(journalPath));
+    if (file.exists()) {
+        return journalPath;
+    }
+
+    // Try to create a file there
+    if (file.open(QIODevice::ReadWrite)) {
+        // Ok, all good.
+        file.close();
+        file.remove();
+        return journalPath;
+    }
+
+    // Can we create it if we drop the underscore?
+    QString alternateJournalPath = journalPath.mid(2).prepend(".");
+    QFile file2(QDir(localPath).filePath(alternateJournalPath));
+    if (file2.open(QIODevice::ReadWrite)) {
+        // The alternative worked, use it
+        qDebug() << "Using alternate database path" << alternateJournalPath;
+        file2.close();
+        file2.remove();
+        return alternateJournalPath;
+    }
+
+    // Neither worked, just keep the original and throw errors later
+    qDebug() << "Could not find a writable database path" << file.fileName();
     return journalPath;
 }
 
index 72db1c4631a75c8460ffc4ab3a7b505cc10e71e4..37df8c81eae4b976897881714b1eda0769423deb 100644 (file)
@@ -41,7 +41,8 @@ public:
     virtual ~SyncJournalDb();
 
     /// Create a journal path for a specific configuration
-    static QString makeDbName(const QUrl& remoteUrl,
+    static QString makeDbName(const QString& localPath,
+                              const QUrl& remoteUrl,
                               const QString& remotePath,
                               const QString& user);