2014-02-18 14:52:38 +04:00
|
|
|
/*
|
|
|
|
* Copyright (C) by Olivier Goffart <ogoffart@owncloud.com>
|
|
|
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "propagatorjobs.h"
|
|
|
|
#include "owncloudpropagator_p.h"
|
2016-10-10 17:55:31 +03:00
|
|
|
#include "propagateremotemove.h"
|
2017-08-16 09:36:52 +03:00
|
|
|
#include "common/utility.h"
|
2014-02-18 14:52:38 +04:00
|
|
|
#include "syncjournaldb.h"
|
|
|
|
#include "syncjournalfilerecord.h"
|
2015-02-25 12:51:05 +03:00
|
|
|
#include "filesystem.h"
|
2014-02-18 14:52:38 +04:00
|
|
|
#include <qfile.h>
|
|
|
|
#include <qdir.h>
|
|
|
|
#include <qdiriterator.h>
|
|
|
|
#include <qtemporaryfile.h>
|
|
|
|
#include <qsavefile.h>
|
|
|
|
#include <QDateTime>
|
|
|
|
#include <qstack.h>
|
|
|
|
#include <QCoreApplication>
|
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
namespace OCC {
|
2014-02-18 14:52:38 +04:00
|
|
|
|
2017-05-09 15:24:11 +03:00
|
|
|
Q_LOGGING_CATEGORY(lcPropagateLocalRemove, "sync.propagator.localremove", QtInfoMsg)
|
|
|
|
Q_LOGGING_CATEGORY(lcPropagateLocalMkdir, "sync.propagator.localmkdir", QtInfoMsg)
|
|
|
|
Q_LOGGING_CATEGORY(lcPropagateLocalRename, "sync.propagator.localrename", QtInfoMsg)
|
|
|
|
|
2017-05-10 10:37:10 +03:00
|
|
|
QByteArray localFileIdFromFullId(const QByteArray &id)
|
|
|
|
{
|
|
|
|
return id.left(8);
|
|
|
|
}
|
|
|
|
|
2015-09-02 16:19:34 +03:00
|
|
|
/**
|
|
|
|
* Code inspired from Qt5's QDir::removeRecursively
|
|
|
|
* The code will update the database in case of error.
|
2015-10-05 06:20:09 +03:00
|
|
|
* If everything goes well (no error, returns true), the caller is responsible for removing the entries
|
2015-09-02 16:19:34 +03:00
|
|
|
* in the database. But in case of error, we need to remove the entries from the database of the files
|
|
|
|
* that were deleted.
|
|
|
|
*
|
2017-01-18 12:19:05 +03:00
|
|
|
* \a path is relative to propagator()->_localDir + _item->_file and should start with a slash
|
2015-09-02 16:19:34 +03:00
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
bool PropagateLocalRemove::removeRecursively(const QString &path)
|
2014-02-18 14:52:38 +04:00
|
|
|
{
|
|
|
|
bool success = true;
|
2017-01-17 16:29:12 +03:00
|
|
|
QString absolute = propagator()->_localDir + _item->_file + path;
|
2015-09-02 16:19:34 +03:00
|
|
|
QDirIterator di(absolute, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot);
|
|
|
|
|
|
|
|
QVector<QPair<QString, bool>> deleted;
|
|
|
|
|
2014-02-18 14:52:38 +04:00
|
|
|
while (di.hasNext()) {
|
|
|
|
di.next();
|
2017-05-17 11:55:42 +03:00
|
|
|
const QFileInfo &fi = di.fileInfo();
|
2014-02-18 14:52:38 +04:00
|
|
|
bool ok;
|
2015-02-12 13:02:56 +03:00
|
|
|
// The use of isSymLink here is okay:
|
|
|
|
// we never want to go into this branch for .lnk files
|
2015-09-02 16:19:34 +03:00
|
|
|
bool isDir = fi.isDir() && !fi.isSymLink();
|
|
|
|
if (isDir) {
|
|
|
|
ok = removeRecursively(path + QLatin1Char('/') + di.fileName()); // recursive
|
2014-10-29 14:23:48 +03:00
|
|
|
} else {
|
2016-01-05 13:58:18 +03:00
|
|
|
QString removeError;
|
|
|
|
ok = FileSystem::remove(di.filePath(), &removeError);
|
2014-10-29 14:23:48 +03:00
|
|
|
if (!ok) {
|
2017-05-17 11:55:42 +03:00
|
|
|
_error += PropagateLocalRemove::tr("Error removing '%1': %2;").arg(QDir::toNativeSeparators(di.filePath()), removeError) + " ";
|
2017-03-30 14:46:20 +03:00
|
|
|
qCWarning(lcPropagateLocalRemove) << "Error removing " << di.filePath() << ':' << removeError;
|
2014-10-29 14:23:48 +03:00
|
|
|
}
|
|
|
|
}
|
2015-09-02 16:19:34 +03:00
|
|
|
if (success && !ok) {
|
|
|
|
// We need to delete the entries from the database now from the deleted vector
|
2017-05-17 11:55:42 +03:00
|
|
|
foreach (const auto &it, deleted) {
|
2017-01-17 16:29:12 +03:00
|
|
|
propagator()->_journal->deleteFileRecord(_item->_originalFile + path + QLatin1Char('/') + it.first,
|
2017-05-17 11:55:42 +03:00
|
|
|
it.second);
|
2015-09-02 16:19:34 +03:00
|
|
|
}
|
2014-02-18 14:52:38 +04:00
|
|
|
success = false;
|
2015-09-02 16:19:34 +03:00
|
|
|
deleted.clear();
|
|
|
|
}
|
|
|
|
if (success) {
|
|
|
|
deleted.append(qMakePair(di.fileName(), isDir));
|
|
|
|
}
|
|
|
|
if (!success && ok) {
|
|
|
|
// This succeeded, so we need to delete it from the database now because the caller won't
|
2017-01-17 16:29:12 +03:00
|
|
|
propagator()->_journal->deleteFileRecord(_item->_originalFile + path + QLatin1Char('/') + di.fileName(),
|
2017-05-17 11:55:42 +03:00
|
|
|
isDir);
|
2015-09-02 16:19:34 +03:00
|
|
|
}
|
2014-02-18 14:52:38 +04:00
|
|
|
}
|
2014-10-29 14:23:48 +03:00
|
|
|
if (success) {
|
2015-09-02 16:19:34 +03:00
|
|
|
success = QDir().rmdir(absolute);
|
2014-10-29 14:23:48 +03:00
|
|
|
if (!success) {
|
2015-10-08 17:20:42 +03:00
|
|
|
_error += PropagateLocalRemove::tr("Could not remove folder '%1'")
|
2017-05-17 11:55:42 +03:00
|
|
|
.arg(QDir::toNativeSeparators(absolute))
|
|
|
|
+ " ";
|
2017-03-30 14:46:20 +03:00
|
|
|
qCWarning(lcPropagateLocalRemove) << "Error removing folder" << absolute;
|
2014-10-29 14:23:48 +03:00
|
|
|
}
|
|
|
|
}
|
2014-02-18 14:52:38 +04:00
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropagateLocalRemove::start()
|
|
|
|
{
|
2017-01-17 16:29:12 +03:00
|
|
|
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
2014-02-18 14:52:38 +04:00
|
|
|
return;
|
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
QString filename = propagator()->_localDir + _item->_file;
|
2015-11-23 21:33:49 +03:00
|
|
|
|
2017-05-09 15:24:11 +03:00
|
|
|
qCDebug(lcPropagateLocalRemove) << filename;
|
2015-11-23 21:33:49 +03:00
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
if (propagator()->localFileNameClash(_item->_file)) {
|
|
|
|
done(SyncFileItem::NormalError, tr("Could not remove %1 because of a local file name clash").arg(QDir::toNativeSeparators(filename)));
|
2014-05-26 20:17:18 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
if (_item->_isDirectory) {
|
2015-09-02 16:19:34 +03:00
|
|
|
if (QDir(filename).exists() && !removeRecursively(QString())) {
|
|
|
|
done(SyncFileItem::NormalError, _error);
|
2014-02-18 14:52:38 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
2016-01-05 13:58:18 +03:00
|
|
|
QString removeError;
|
|
|
|
if (FileSystem::fileExists(filename)
|
2017-05-17 11:55:42 +03:00
|
|
|
&& !FileSystem::remove(filename, &removeError)) {
|
2016-01-05 13:58:18 +03:00
|
|
|
done(SyncFileItem::NormalError, removeError);
|
2014-02-18 14:52:38 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2017-02-14 14:46:44 +03:00
|
|
|
propagator()->reportProgress(*_item, 0);
|
2017-01-17 16:29:12 +03:00
|
|
|
propagator()->_journal->deleteFileRecord(_item->_originalFile, _item->_isDirectory);
|
|
|
|
propagator()->_journal->commit("Local remove");
|
2014-02-18 14:52:38 +04:00
|
|
|
done(SyncFileItem::Success);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropagateLocalMkdir::start()
|
|
|
|
{
|
2017-01-17 16:29:12 +03:00
|
|
|
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
2014-02-18 14:52:38 +04:00
|
|
|
return;
|
|
|
|
|
2017-01-17 16:29:12 +03:00
|
|
|
QDir newDir(propagator()->getFilePath(_item->_file));
|
2014-05-22 12:16:33 +04:00
|
|
|
QString newDirStr = QDir::toNativeSeparators(newDir.path());
|
2015-12-22 13:26:07 +03:00
|
|
|
|
|
|
|
// When turning something that used to be a file into a directory
|
|
|
|
// we need to delete the file first.
|
|
|
|
QFileInfo fi(newDirStr);
|
|
|
|
if (_deleteExistingFile && fi.exists() && fi.isFile()) {
|
2016-01-05 13:58:18 +03:00
|
|
|
QString removeError;
|
|
|
|
if (!FileSystem::remove(newDirStr, &removeError)) {
|
2017-05-17 11:55:42 +03:00
|
|
|
done(SyncFileItem::NormalError,
|
|
|
|
tr("could not delete file %1, error: %2")
|
|
|
|
.arg(newDirStr, removeError));
|
2015-12-22 13:26:07 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
if (Utility::fsCasePreserving() && propagator()->localFileNameClash(_item->_file)) {
|
2017-05-11 16:36:47 +03:00
|
|
|
qCWarning(lcPropagateLocalMkdir) << "New folder to create locally already exists with different case:" << _item->_file;
|
2017-05-17 11:55:42 +03:00
|
|
|
done(SyncFileItem::NormalError, tr("Attention, possible case sensitivity clash with %1").arg(newDirStr));
|
2014-05-16 17:20:32 +04:00
|
|
|
return;
|
|
|
|
}
|
2017-01-17 16:29:12 +03:00
|
|
|
emit propagator()->touchedFile(newDirStr);
|
|
|
|
QDir localDir(propagator()->_localDir);
|
2015-04-15 16:19:11 +03:00
|
|
|
if (!localDir.mkpath(_item->_file)) {
|
2017-05-17 11:55:42 +03:00
|
|
|
done(SyncFileItem::NormalError, tr("could not create folder %1").arg(newDirStr));
|
2014-02-18 14:52:38 +04:00
|
|
|
return;
|
|
|
|
}
|
2015-06-17 14:56:37 +03:00
|
|
|
|
|
|
|
// Insert the directory into the database. The correct etag will be set later,
|
2015-07-09 16:05:37 +03:00
|
|
|
// once all contents have been propagated, because should_update_metadata is true.
|
2015-06-17 14:56:37 +03:00
|
|
|
// Adding an entry with a dummy etag to the database still makes sense here
|
|
|
|
// so the database is aware that this folder exists even if the sync is aborted
|
|
|
|
// before the correct etag is stored.
|
|
|
|
SyncJournalFileRecord record(*_item, newDirStr);
|
|
|
|
record._etag = "_invalid_";
|
2017-01-17 16:29:12 +03:00
|
|
|
if (!propagator()->_journal->setFileRecord(record)) {
|
2016-04-07 12:47:04 +03:00
|
|
|
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
|
|
|
|
return;
|
|
|
|
}
|
2017-01-17 16:29:12 +03:00
|
|
|
propagator()->_journal->commit("localMkdir");
|
2015-06-17 14:56:37 +03:00
|
|
|
|
2014-02-18 14:52:38 +04:00
|
|
|
done(SyncFileItem::Success);
|
|
|
|
}
|
|
|
|
|
2015-12-22 13:26:07 +03:00
|
|
|
void PropagateLocalMkdir::setDeleteExistingFile(bool enabled)
|
|
|
|
{
|
|
|
|
_deleteExistingFile = enabled;
|
|
|
|
}
|
|
|
|
|
2014-02-18 16:52:40 +04:00
|
|
|
void PropagateLocalRename::start()
|
|
|
|
{
|
2017-01-17 16:29:12 +03:00
|
|
|
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
2014-02-18 16:52:40 +04:00
|
|
|
return;
|
|
|
|
|
2017-01-17 16:29:12 +03:00
|
|
|
QString existingFile = propagator()->getFilePath(_item->_file);
|
|
|
|
QString targetFile = propagator()->getFilePath(_item->_renameTarget);
|
2014-11-07 13:41:21 +03:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
// if the file is a file underneath a moved dir, the _item->file is equal
|
|
|
|
// to _item->renameTarget and the file is not moved as a result.
|
|
|
|
if (_item->_file != _item->_renameTarget) {
|
2017-02-14 14:46:44 +03:00
|
|
|
propagator()->reportProgress(*_item, 0);
|
2017-05-09 15:24:11 +03:00
|
|
|
qCDebug(lcPropagateLocalRename) << "MOVE " << existingFile << " => " << targetFile;
|
2014-05-26 19:00:40 +04:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
if (QString::compare(_item->_file, _item->_renameTarget, Qt::CaseInsensitive) != 0
|
2017-05-17 11:55:42 +03:00
|
|
|
&& propagator()->localFileNameClash(_item->_renameTarget)) {
|
2014-10-17 18:11:25 +04:00
|
|
|
// Only use localFileNameClash for the destination if we know that the source was not
|
|
|
|
// the one conflicting (renaming A.txt -> a.txt is OK)
|
|
|
|
|
2014-10-17 17:58:48 +04:00
|
|
|
// Fixme: the file that is the reason for the clash could be named here,
|
|
|
|
// it would have to come out the localFileNameClash function
|
2017-05-17 11:54:57 +03:00
|
|
|
done(SyncFileItem::NormalError,
|
2017-05-17 11:55:42 +03:00
|
|
|
tr("File %1 can not be renamed to %2 because of a local file name clash")
|
|
|
|
.arg(QDir::toNativeSeparators(_item->_file))
|
|
|
|
.arg(QDir::toNativeSeparators(_item->_renameTarget)));
|
2014-10-17 17:58:48 +04:00
|
|
|
return;
|
|
|
|
}
|
2014-11-07 13:41:21 +03:00
|
|
|
|
2017-01-17 16:29:12 +03:00
|
|
|
emit propagator()->touchedFile(existingFile);
|
|
|
|
emit propagator()->touchedFile(targetFile);
|
2015-03-11 12:51:36 +03:00
|
|
|
QString renameError;
|
|
|
|
if (!FileSystem::rename(existingFile, targetFile, &renameError)) {
|
|
|
|
done(SyncFileItem::NormalError, renameError);
|
2014-05-26 17:01:26 +04:00
|
|
|
return;
|
2014-05-26 16:51:53 +04:00
|
|
|
}
|
2014-02-18 16:52:40 +04:00
|
|
|
}
|
|
|
|
|
2015-11-10 17:05:00 +03:00
|
|
|
SyncJournalFileRecord oldRecord =
|
2017-05-17 11:55:42 +03:00
|
|
|
propagator()->_journal->getFileRecord(_item->_originalFile);
|
2017-01-17 16:29:12 +03:00
|
|
|
propagator()->_journal->deleteFileRecord(_item->_originalFile);
|
2014-02-18 16:52:40 +04:00
|
|
|
|
|
|
|
// store the rename file name in the item.
|
2016-10-10 17:55:31 +03:00
|
|
|
const auto oldFile = _item->_file;
|
2015-04-15 16:19:11 +03:00
|
|
|
_item->_file = _item->_renameTarget;
|
2014-02-18 16:52:40 +04:00
|
|
|
|
2015-04-15 16:19:11 +03:00
|
|
|
SyncJournalFileRecord record(*_item, targetFile);
|
|
|
|
record._path = _item->_renameTarget;
|
2016-04-11 13:41:26 +03:00
|
|
|
if (oldRecord.isValid()) {
|
2017-06-14 13:14:46 +03:00
|
|
|
record._checksumHeader = oldRecord._checksumHeader;
|
2016-04-11 13:41:26 +03:00
|
|
|
}
|
2014-02-18 16:52:40 +04:00
|
|
|
|
2015-10-05 06:20:09 +03:00
|
|
|
if (!_item->_isDirectory) { // Directories are saved at the end
|
2017-01-17 16:29:12 +03:00
|
|
|
if (!propagator()->_journal->setFileRecord(record)) {
|
2016-04-07 12:47:04 +03:00
|
|
|
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
|
|
|
|
return;
|
|
|
|
}
|
2016-10-10 17:55:31 +03:00
|
|
|
} else {
|
2017-01-17 16:29:12 +03:00
|
|
|
if (!PropagateRemoteMove::adjustSelectiveSync(propagator()->_journal, oldFile, _item->_renameTarget)) {
|
2016-10-10 17:55:31 +03:00
|
|
|
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
|
|
|
|
return;
|
|
|
|
}
|
2014-02-18 16:52:40 +04:00
|
|
|
}
|
|
|
|
|
2017-01-17 16:29:12 +03:00
|
|
|
propagator()->_journal->commit("localRename");
|
2014-02-18 16:52:40 +04:00
|
|
|
|
|
|
|
done(SyncFileItem::Success);
|
|
|
|
}
|
2014-02-18 14:52:38 +04:00
|
|
|
}
|