diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp index 5ecc5d8f06d..6c24bfbe1c5 100644 --- a/src/common/syncjournaldb.cpp +++ b/src/common/syncjournaldb.cpp @@ -824,11 +824,11 @@ QVector SyncJournalDb::tableColumns(const QByteArray &table) qint64 SyncJournalDb::getPHash(const QByteArray &file) { - qint64 h; - int len = file.length(); - - h = c_jhash64((uint8_t *)file.data(), len, 0); - return h; + QByteArray normalizedFile = file; + if (Utility::isMac()) { + normalizedFile = QString::fromUtf8(file).normalized(QString::NormalizationForm_C).toUtf8(); + } + return c_jhash64((uint8_t *)normalizedFile.data(), normalizedFile.length(), 0); } Result SyncJournalDb::setFileRecord(const SyncJournalFileRecord &_record) diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index bd01acbfcde..2b5ebcc0110 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -545,6 +545,9 @@ Folder *FolderMan::addFolderInternal( Folder *FolderMan::folderForPath(const QString &path, QString *relativePath) { QString absolutePath = QDir::cleanPath(path) + QLatin1Char('/'); + if (!Utility::isLinux()) { + absolutePath = absolutePath.normalized(QString::NormalizationForm_C); + } for (auto *folder : std::as_const(_folders)) { const QString folderPath = folder->cleanPath() + QLatin1Char('/'); diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 293b82a6c82..61a22c35df1 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -80,20 +80,24 @@ void ProcessDirectoryJob::process() RemoteInfo serverEntry; LocalInfo localEntry; }; + + // The key for the `entries` mapping is the NFC form of the filename. This is done to prevent + // NFC->NFD or NFD->NFC conversions done by `QFile` and/or the underlying local filesystem. std::map entries; for (auto &e : _serverNormalQueryEntries) { - entries[e.name].serverEntry = std::move(e); + // Keep the name on the server as-is, but use the NFC form as the key + entries[e.name.normalized(QString::NormalizationForm_C)].serverEntry = std::move(e); } _serverNormalQueryEntries.clear(); // fetch all the name from the DB - auto pathU8 = _currentFolder._original.toUtf8(); + auto pathU8 = Utility::isMac() ? _currentFolder._original.normalized(QString::NormalizationForm_C).toUtf8() : _currentFolder._original.toUtf8(); // XXX if (!_discoveryData->_statedb->listFilesInPath(pathU8, [&](const SyncJournalFileRecord &rec) { auto name = pathU8.isEmpty() ? QString::fromUtf8(rec._path) : QString::fromUtf8(rec._path.constData() + (pathU8.size() + 1)); if (rec.isVirtualFile() && isVfsWithSuffix()) { name = chopVirtualFileSuffix(name); } - auto &dbEntry = entries[name].dbEntry; + auto &dbEntry = entries[name.normalized(QString::NormalizationForm_C)].dbEntry; dbEntry = rec; setupDbPinStateActions(dbEntry); })) { @@ -102,6 +106,7 @@ void ProcessDirectoryJob::process() } for (auto &e : _localNormalQueryEntries) { + e.name = e.name.normalized(QString::NormalizationForm_C); entries[e.name].localEntry = e; } if (isVfsWithSuffix()) { @@ -140,8 +145,16 @@ void ProcessDirectoryJob::process() for (const auto &f : entries) { const auto &e = f.second; - PathTuple path; - path = _currentFolder.addName(e.nameOverride.isEmpty() ? f.first : e.nameOverride); + QString name; + if (!e.nameOverride.isEmpty()) { + name = e.nameOverride; + } else if (!e.serverEntry.name.isEmpty()) { + // IF there is a name on the server, take that, as it hasn't been normalized. + name = e.serverEntry.name; + } else { + name = f.first; + } + PathTuple path = _currentFolder.addName(name); if (isVfsWithSuffix()) { // Without suffix vfs the paths would be good. But since the dbEntry and localEntry