diff options
-rw-r--r-- | doc/troubleshooting.rst | 2 | ||||
-rw-r--r-- | mirall.desktop.in | 3 | ||||
-rw-r--r-- | src/gui/folderman.cpp | 3 | ||||
-rw-r--r-- | src/gui/lockwatcher.cpp | 10 | ||||
-rw-r--r-- | src/gui/lockwatcher.h | 6 | ||||
-rw-r--r-- | src/gui/settingsdialog.cpp | 5 | ||||
-rw-r--r-- | src/libsync/propagatedownload.cpp | 4 | ||||
-rw-r--r-- | src/libsync/propagateuploadng.cpp | 5 | ||||
-rw-r--r-- | src/libsync/propagateuploadv1.cpp | 3 | ||||
-rw-r--r-- | test/CMakeLists.txt | 2 | ||||
-rw-r--r-- | test/testlockedfiles.cpp | 163 | ||||
-rw-r--r-- | translations/client_es.ts | 4 |
12 files changed, 202 insertions, 8 deletions
diff --git a/doc/troubleshooting.rst b/doc/troubleshooting.rst index 1dc7b77ca..6ba165705 100644 --- a/doc/troubleshooting.rst +++ b/doc/troubleshooting.rst @@ -129,7 +129,7 @@ Logging to a Temporary Directory 1. Open the ownCloud Desktop Client. -2. Press F12 on your keyboard. +2. Press F12 or Ctrl-L on your keyboard. The Log Output window opens. diff --git a/mirall.desktop.in b/mirall.desktop.in index 4b3a30b51..d7d0cb456 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -95,6 +95,9 @@ MimeType=application/vnd.@APPLICATION_EXECUTABLE@; # Translations + + +# Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client Icon[oc]=@APPLICATION_EXECUTABLE@ Name[oc]=@APPLICATION_NAME@ sincronizacion del client diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index a006a277a..753d83156 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -852,7 +852,8 @@ void FolderMan::slotServerVersionChanged(Account *account) void FolderMan::slotWatchedFileUnlocked(const QString &path) { if (Folder *f = folderForPath(path)) { - f->scheduleThisFolderSoon(); + // Treat this equivalently to the file being reported by the file watcher + f->slotWatchedPathChanged(path); } } diff --git a/src/gui/lockwatcher.cpp b/src/gui/lockwatcher.cpp index cf76c097b..8e26cf2ce 100644 --- a/src/gui/lockwatcher.cpp +++ b/src/gui/lockwatcher.cpp @@ -38,6 +38,16 @@ void LockWatcher::addFile(const QString &path) _watchedPaths.insert(path); } +void LockWatcher::setCheckInterval(std::chrono::milliseconds interval) +{ + _timer.start(interval.count()); +} + +bool LockWatcher::contains(const QString &path) +{ + return _watchedPaths.contains(path); +} + void LockWatcher::checkFiles() { QSet<QString> unlocked; diff --git a/src/gui/lockwatcher.h b/src/gui/lockwatcher.h index ba2d8f8a7..ae8f2cd01 100644 --- a/src/gui/lockwatcher.h +++ b/src/gui/lockwatcher.h @@ -51,6 +51,12 @@ public: */ void addFile(const QString &path); + /** Adjusts the default interval for checking whether the lock is still present */ + void setCheckInterval(std::chrono::milliseconds interval); + + /** Whether the path is being watched for lock-changes */ + bool contains(const QString &path); + signals: /** Emitted when one of the watched files is no longer * being locked. */ diff --git a/src/gui/settingsdialog.cpp b/src/gui/settingsdialog.cpp index e1c36efd3..df30ba0d5 100644 --- a/src/gui/settingsdialog.cpp +++ b/src/gui/settingsdialog.cpp @@ -135,6 +135,11 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent) connect(showLogWindow, &QAction::triggered, gui, &ownCloudGui::slotToggleLogBrowser); addAction(showLogWindow); + QAction *showLogWindow2 = new QAction(this); + showLogWindow2->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); + connect(showLogWindow2, &QAction::triggered, gui, &ownCloudGui::slotToggleLogBrowser); + addAction(showLogWindow2); + customizeStyle(); cfg.restoreGeometry(this); diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp index 34f23ee13..d02a56628 100644 --- a/src/libsync/propagatedownload.cpp +++ b/src/libsync/propagatedownload.cpp @@ -850,7 +850,9 @@ namespace { // Anonymous namespace for the recall feature static void preserveGroupOwnership(const QString &fileName, const QFileInfo &fi) { #ifdef Q_OS_UNIX - chown(fileName.toLocal8Bit().constData(), -1, fi.groupId()); + if (chown(fileName.toLocal8Bit().constData(), -1, fi.groupId()) != 0) { + qCWarning(lcPropagateDownload) << "Unable to chown" << fileName << "to previous group owner" << strerror(errno); + } #else Q_UNUSED(fileName); Q_UNUSED(fi); diff --git a/src/libsync/propagateuploadng.cpp b/src/libsync/propagateuploadng.cpp index 85bdc0ae5..e141bcc86 100644 --- a/src/libsync/propagateuploadng.cpp +++ b/src/libsync/propagateuploadng.cpp @@ -37,6 +37,7 @@ extern "C" { #include <cmath> #include <cstring> +#include <memory> namespace OCC { @@ -541,7 +542,7 @@ void PropagateUploadFileNG::startNextChunk() _currentChunkOffset = _rangesToUpload.first().start; _currentChunkSize = qMin(propagator()->_chunkSize, _rangesToUpload.first().size); - auto device = std::make_unique<UploadDevice>(&propagator()->_bandwidthManager); + auto device = std::unique_ptr<UploadDevice>(new UploadDevice(&propagator()->_bandwidthManager)); const QString fileName = propagator()->getFilePath(_item->_file); if (!device->prepareAndOpen(fileName, _currentChunkOffset, _currentChunkSize)) { @@ -582,7 +583,7 @@ void PropagateUploadFileNG::slotZsyncGenerationFinished(const QString &generated << "Finished generation of:" << generatedFileName << "size:" << FileSystem::getSize(generatedFileName); - auto device = std::make_unique<UploadDevice>(&propagator()->_bandwidthManager); + auto device = std::unique_ptr<UploadDevice>(new UploadDevice(&propagator()->_bandwidthManager)); if (!device->prepareAndOpen(generatedFileName, 0, FileSystem::getSize(generatedFileName))) { qCWarning(lcPropagateUpload) << "Could not prepare generated file: " << generatedFileName << device->errorString(); diff --git a/src/libsync/propagateuploadv1.cpp b/src/libsync/propagateuploadv1.cpp index 68aab4172..c24e7b648 100644 --- a/src/libsync/propagateuploadv1.cpp +++ b/src/libsync/propagateuploadv1.cpp @@ -32,6 +32,7 @@ #include <QDir> #include <cmath> #include <cstring> +#include <memory> namespace OCC { @@ -90,7 +91,7 @@ void PropagateUploadFileV1::startNextChunk() QString path = _item->_file; - auto device = std::make_unique<UploadDevice>(&propagator()->_bandwidthManager); + auto device = std::unique_ptr<UploadDevice>(new UploadDevice(&propagator()->_bandwidthManager)); qint64 chunkStart = 0; qint64 currentChunkSize = fileSize; bool isFinalChunk = false; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1f078a665..1a26ace2e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -56,6 +56,8 @@ owncloud_add_test(LocalDiscovery "syncenginetestutils.h") owncloud_add_test(RemoteDiscovery "syncenginetestutils.h") owncloud_add_test(Permissions "syncenginetestutils.h") owncloud_add_test(SelectiveSync "syncenginetestutils.h") +owncloud_add_test(LockedFiles "syncenginetestutils.h;../src/gui/lockwatcher.cpp") + owncloud_add_test(FolderWatcher "${FolderWatcher_SRC}") if( UNIX AND NOT APPLE ) diff --git a/test/testlockedfiles.cpp b/test/testlockedfiles.cpp new file mode 100644 index 000000000..df1e8f8d0 --- /dev/null +++ b/test/testlockedfiles.cpp @@ -0,0 +1,163 @@ +/* + * This software is in the public domain, furnished "as is", without technical + * support, and with no warranty, express or implied, as to its usefulness for + * any purpose. + * + */ + +#include <QtTest> +#include "syncenginetestutils.h" +#include "lockwatcher.h" +#include <syncengine.h> +#include <localdiscoverytracker.h> + +using namespace OCC; + +#ifdef Q_OS_WIN +// pass combination of FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_DELETE +HANDLE makeHandle(const QString &file, int shareMode) +{ + const wchar_t *wuri = reinterpret_cast<const wchar_t *>(file.utf16()); + auto handle = CreateFileW( + wuri, + GENERIC_READ | GENERIC_WRITE, + shareMode, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (handle == INVALID_HANDLE_VALUE) { + qWarning() << GetLastError(); + } + return handle; +} +#endif + +class TestLockedFiles : public QObject +{ + Q_OBJECT + +private slots: + void testBasicLockFileWatcher() + { + int count = 0; + QString file; + + LockWatcher watcher; + watcher.setCheckInterval(std::chrono::milliseconds(50)); + connect(&watcher, &LockWatcher::fileUnlocked, &watcher, [&](const QString &f) { ++count; file = f; }); + + QString tmpFile; + { + QTemporaryFile tmp; + tmp.setAutoRemove(false); + tmp.open(); + tmpFile = tmp.fileName(); + } + QVERIFY(QFile::exists(tmpFile)); + + QVERIFY(!FileSystem::isFileLocked(tmpFile)); + watcher.addFile(tmpFile); + QVERIFY(watcher.contains(tmpFile)); + + QEventLoop loop; + QTimer::singleShot(120, &loop, [&] { loop.exit(); }); + loop.exec(); + + QCOMPARE(count, 1); + QCOMPARE(file, tmpFile); + QVERIFY(!watcher.contains(tmpFile)); + +#ifdef Q_OS_WIN + auto h = makeHandle(tmpFile, 0); + QVERIFY(FileSystem::isFileLocked(tmpFile)); + watcher.addFile(tmpFile); + + count = 0; + file.clear(); + QThread::msleep(120); + qApp->processEvents(); + + QCOMPARE(count, 0); + QVERIFY(file.isEmpty()); + QVERIFY(watcher.contains(tmpFile)); + + CloseHandle(h); + QVERIFY(!FileSystem::isFileLocked(tmpFile)); + + QThread::msleep(120); + qApp->processEvents(); + + QCOMPARE(count, 1); + QCOMPARE(file, tmpFile); + QVERIFY(!watcher.contains(tmpFile)); +#endif + QFile::remove(tmpFile); + } + +#ifdef Q_OS_WIN + void testLockedFilePropagation() + { + FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; + + QStringList seenLockedFiles; + connect(&fakeFolder.syncEngine(), &SyncEngine::seenLockedFile, &fakeFolder.syncEngine(), + [&](const QString &file) { seenLockedFiles.append(file); }); + + LocalDiscoveryTracker tracker; + connect(&fakeFolder.syncEngine(), &SyncEngine::itemCompleted, &tracker, &LocalDiscoveryTracker::slotItemCompleted); + connect(&fakeFolder.syncEngine(), &SyncEngine::finished, &tracker, &LocalDiscoveryTracker::slotSyncFinished); + auto hasLocalDiscoveryPath = [&](const QString &path) { + auto &paths = tracker.localDiscoveryPaths(); + return paths.find(path.toUtf8()) != paths.end(); + }; + + // + // Local change, attempted upload, but file is locked! + // + fakeFolder.localModifier().appendByte("A/a1"); + tracker.addTouchedPath("A/a1"); + auto h1 = makeHandle(fakeFolder.localPath() + "A/a1", 0); + + fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths()); + tracker.startSyncPartialDiscovery(); + QVERIFY(!fakeFolder.syncOnce()); + + QVERIFY(seenLockedFiles.contains(fakeFolder.localPath() + "A/a1")); + QVERIFY(seenLockedFiles.size() == 1); + QVERIFY(hasLocalDiscoveryPath("A/a1")); + + CloseHandle(h1); + + fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths()); + tracker.startSyncPartialDiscovery(); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + + seenLockedFiles.clear(); + QVERIFY(tracker.localDiscoveryPaths().empty()); + + // + // Remote change, attempted download, but file is locked! + // + fakeFolder.remoteModifier().appendByte("A/a1"); + auto h2 = makeHandle(fakeFolder.localPath() + "A/a1", 0); + + fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths()); + tracker.startSyncPartialDiscovery(); + QVERIFY(!fakeFolder.syncOnce()); + + QVERIFY(seenLockedFiles.contains(fakeFolder.localPath() + "A/a1")); + QVERIFY(seenLockedFiles.size() == 1); + + CloseHandle(h2); + + fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths()); + tracker.startSyncPartialDiscovery(); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + } +#endif +}; + +QTEST_GUILESS_MAIN(TestLockedFiles) +#include "testlockedfiles.moc" diff --git a/translations/client_es.ts b/translations/client_es.ts index 65677ea08..79ab99fb3 100644 --- a/translations/client_es.ts +++ b/translations/client_es.ts @@ -1375,12 +1375,12 @@ Si continua con la sincronización todos los archivos serán remplazados por su <message> <location filename="../src/gui/generalsettings.ui" line="80"/> <source>&Update Channel</source> - <translation type="unfinished"/> + <translation>&Actualizar canal</translation> </message> <message> <location filename="../src/gui/generalsettings.ui" line="186"/> <source>•</source> - <translation type="unfinished"/> + <translation>•</translation> </message> <message> <location filename="../src/gui/generalsettings.ui" line="236"/> |