From: Christian Kamm Date: Tue, 7 Mar 2017 10:47:38 +0000 (+0100) Subject: Windows/NTFS: Do not attempt to upload inaccessible files #5544 X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~790^2~9 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=13b2568358b595d630f6c9a39ece99c9e34e4cab;p=nextcloud-desktop.git Windows/NTFS: Do not attempt to upload inaccessible files #5544 It is possible to create files with filenames that differ only by case in NTFS, but most operations such as stat and open only target one of these by default. When that happens, we want to avoid uploading incorrect data and give up on the file. Typically this situation should never occurr during normal use of Windows. It can happen, however, when a NTFS partition is mounted in another OS. --- diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index c651c1caa..390ac7e85 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -601,6 +601,36 @@ bool OwncloudPropagator::localFileNameClash( const QString& relFile ) return re; } +bool OwncloudPropagator::hasCaseClashAccessibilityProblem(const QString &relfile) +{ +#ifdef Q_OS_WIN + bool result = false; + const QString file( _localDir + relfile ); + WIN32_FIND_DATA FindFileData; + HANDLE hFind; + + hFind = FindFirstFileW(reinterpret_cast(file.utf16()), &FindFileData); + if (hFind != INVALID_HANDLE_VALUE) { + QString firstFile = QString::fromWCharArray( FindFileData.cFileName ); + if (FindNextFile(hFind, &FindFileData)) { + QString secondFile = QString::fromWCharArray( FindFileData.cFileName ); + // This extra check shouldn't be necessary, but ensures that there + // are two different filenames that are identical when case is ignored. + if (firstFile != secondFile + && QString::compare(firstFile, secondFile, Qt::CaseInsensitive) == 0) { + result = true; + qDebug() << "Found two filepaths that only differ in case: " << firstFile << secondFile; + } + } + FindClose(hFind); + } + return result; +#else + Q_UNUSED(relfile); + return false; +#endif +} + QString OwncloudPropagator::getFilePath(const QString& tmp_file_name) const { return _localDir + tmp_file_name; diff --git a/src/libsync/owncloudpropagator.h b/src/libsync/owncloudpropagator.h index e16e2aee5..8fd4720c1 100644 --- a/src/libsync/owncloudpropagator.h +++ b/src/libsync/owncloudpropagator.h @@ -308,7 +308,23 @@ public: int hardMaximumActiveJob(); bool isInSharedDirectory(const QString& file); + + /** Check whether a download would clash with an existing file + * in filesystems that are only case-preserving. + */ bool localFileNameClash(const QString& relfile); + + /** Check whether a file is properly accessible for upload. + * + * It is possible to create files with filenames that differ + * only by case in NTFS, but most operations such as stat and + * open only target one of these by default. + * + * When that happens, we want to avoid uploading incorrect data + * and give up on the file. + */ + bool hasCaseClashAccessibilityProblem(const QString& relfile); + QString getFilePath(const QString& tmp_file_name) const; void abort() { diff --git a/src/libsync/propagateupload.cpp b/src/libsync/propagateupload.cpp index 0ad02c09a..56a46d4e4 100644 --- a/src/libsync/propagateupload.cpp +++ b/src/libsync/propagateupload.cpp @@ -199,6 +199,13 @@ void PropagateUploadFileCommon::start() return; } + // Check if the specific file can be accessed + if( propagator()->hasCaseClashAccessibilityProblem(_item->_file) ) { + done( SyncFileItem::NormalError, tr("File %1 cannot be uploaded because another file with the same name, differing only in case, exists") + .arg(QDir::toNativeSeparators(_item->_file)) ); + return; + } + propagator()->_activeJobList.append(this); if (!_deleteExisting) {