Skip to content

Commit

Permalink
Conflict handling I.
Browse files Browse the repository at this point in the history
Signed-off-by: alex-z <[email protected]>
  • Loading branch information
allexzander committed May 14, 2024
1 parent cb19984 commit b9f6151
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 21 deletions.
99 changes: 93 additions & 6 deletions src/libsync/bulkpropagatordownloadjob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,69 @@ BulkPropagatorDownloadJob::BulkPropagatorDownloadJob(OwncloudPropagator *propaga
}
}

namespace
{
static QString makeRecallFileName(const QString &fn)
{
QString recallFileName(fn);
// Add _recall-XXXX before the extension.
int dotLocation = recallFileName.lastIndexOf('.');
// If no extension, add it at the end (take care of cases like foo/.hidden or foo.bar/file)
if (dotLocation <= recallFileName.lastIndexOf('/') + 1) {
dotLocation = recallFileName.size();
}

QString timeString = QDateTime::currentDateTimeUtc().toString("yyyyMMdd-hhmmss");
recallFileName.insert(dotLocation, "_.sys.admin#recall#-" + timeString);

return recallFileName;
}

void handleRecallFile(const QString &filePath, const QString &folderPath, SyncJournalDb &journal)
{
qCDebug(lcBulkPropagatorDownloadJob) << "handleRecallFile: " << filePath;

FileSystem::setFileHidden(filePath, true);

QFile file(filePath);
if (!file.open(QIODevice::ReadOnly)) {
qCWarning(lcBulkPropagatorDownloadJob) << "Could not open recall file" << file.errorString();
return;
}
QFileInfo existingFile(filePath);
QDir baseDir = existingFile.dir();

while (!file.atEnd()) {
QByteArray line = file.readLine();
line.chop(1); // remove trailing \n

QString recalledFile = QDir::cleanPath(baseDir.filePath(line));
if (!recalledFile.startsWith(folderPath) || !recalledFile.startsWith(baseDir.path())) {
qCWarning(lcBulkPropagatorDownloadJob) << "Ignoring recall of " << recalledFile;
continue;
}

// Path of the recalled file in the local folder
QString localRecalledFile = recalledFile.mid(folderPath.size());

SyncJournalFileRecord record;
if (!journal.getFileRecord(localRecalledFile, &record) || !record.isValid()) {
qCWarning(lcBulkPropagatorDownloadJob) << "No db entry for recall of" << localRecalledFile;
continue;
}

qCInfo(lcBulkPropagatorDownloadJob) << "Recalling" << localRecalledFile << "Checksum:" << record._checksumHeader;

QString targetPath = makeRecallFileName(recalledFile);

qCDebug(lcBulkPropagatorDownloadJob) << "Copy recall file: " << recalledFile << " -> " << targetPath;
// Remove the target first, QFile::copy will not overwrite it.
FileSystem::remove(targetPath);
QFile::copy(recalledFile, targetPath);
}
}
}

void BulkPropagatorDownloadJob::addDownloadItem(const SyncFileItemPtr &item)
{
Q_ASSERT(item->isDirectory() || item->_type == ItemTypeVirtualFileDehydration || item->_type == ItemTypeVirtualFile);
Expand Down Expand Up @@ -83,6 +146,13 @@ void BulkPropagatorDownloadJob::startAfterIsEncryptedIsChecked(const SyncFileIte
Q_ASSERT(vfs && vfs->mode() == Vfs::WindowsCfApi);
Q_ASSERT(item->_type == ItemTypeVirtualFileDehydration || item->_type == ItemTypeVirtualFile);

if (propagator()->localFileNameClash(item->_file)) {
if (!updateMetadata(item, true)) {
return;
}
return;
}

// For virtual files just dehydrate or create the placeholder and be done
if (item->_type == ItemTypeVirtualFileDehydration) {
const auto fsPath = propagator()->fullLocalPath(item->_file);
Expand All @@ -106,10 +176,6 @@ void BulkPropagatorDownloadJob::startAfterIsEncryptedIsChecked(const SyncFileIte
}
} else if (item->_type == ItemTypeVirtualFile) {
qCDebug(lcBulkPropagatorDownloadJob) << "creating virtual file" << item->_file;
if (propagator()->localFileNameClash(item->_file)) {
abortWithError(item, SyncFileItem::FileNameClash, tr("File %1 can not be downloaded because of a local file name clash!").arg(QDir::toNativeSeparators(item->_file)));
return;
}
const auto r = vfs->createPlaceholder(*item);
if (!r) {
qCCritical(lcBulkPropagatorDownloadJob) << "Could not create a placholder for a file" << QDir::toNativeSeparators(item->_file) << ":" << r.error();
Expand All @@ -123,7 +189,7 @@ void BulkPropagatorDownloadJob::startAfterIsEncryptedIsChecked(const SyncFileIte
return;
}

if (!updateMetadata(item)) {
if (!updateMetadata(item, false)) {
return;
}

Expand Down Expand Up @@ -191,7 +257,7 @@ void BulkPropagatorDownloadJob::start(const SyncFileItemPtr &item)
}
}

bool BulkPropagatorDownloadJob::updateMetadata(const SyncFileItemPtr &item)
bool BulkPropagatorDownloadJob::updateMetadata(const SyncFileItemPtr &item, bool isConflict)
{
const auto fn = propagator()->fullLocalPath(item->_file);
const auto result = propagator()->updateMetadata(*item);
Expand All @@ -203,6 +269,18 @@ bool BulkPropagatorDownloadJob::updateMetadata(const SyncFileItemPtr &item)
return false;
}

propagator()->_journal->commit("download file start2");

if (isConflict) {
continueWithError(item, SyncFileItem::Conflict, {});
}

// handle the special recall file
if (!item->_remotePerm.hasPermission(RemotePermissions::IsShared)
&& (item->_file == QLatin1String(".sys.admin#recall#") || item->_file.endsWith(QLatin1String("/.sys.admin#recall#")))) {
handleRecallFile(fn, propagator()->localPath(), *propagator()->_journal);
}

const auto isLockOwnedByCurrentUser = item->_lockOwnerId == propagator()->account()->davUser();

const auto isUserLockOwnedByCurrentUser = (item->_lockOwnerType == SyncFileItem::LockOwnerType::UserLock && isLockOwnedByCurrentUser);
Expand Down Expand Up @@ -231,9 +309,18 @@ void BulkPropagatorDownloadJob::abortWithError(SyncFileItemPtr item, SyncFileIte
abort(AbortType::Synchronous);
if (item) {
item->_errorString = error;
item->_status = status;
emit propagator()->itemCompleted(item, ErrorCategory::GenericError);
}
done(status);
}

void BulkPropagatorDownloadJob::continueWithError(SyncFileItemPtr item, SyncFileItem::Status status, const QString &error)
{
qCInfo(lcBulkPropagatorDownloadJob) << "item finished with status" << status << error;
item->_errorString = error;
item->_status = status;
emit propagator()->itemCompleted(item, ErrorCategory::NoError);
}

}
7 changes: 3 additions & 4 deletions src/libsync/bulkpropagatordownloadjob.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,11 @@ private slots:

void done( const SyncFileItem::Status status);

void abortWithError(SyncFileItemPtr item,
SyncFileItem::Status status,
const QString &error);
void abortWithError(SyncFileItemPtr item, SyncFileItem::Status status, const QString &error);
void continueWithError(SyncFileItemPtr item, SyncFileItem::Status status, const QString &error);

private:
bool updateMetadata(const SyncFileItemPtr &item);
bool updateMetadata(const SyncFileItemPtr &item, bool isConflict);
void checkPropagationIsDone();

std::vector<SyncFileItemPtr> _filesToDownload;
Expand Down
12 changes: 1 addition & 11 deletions src/libsync/owncloudpropagator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,17 +689,7 @@ void OwncloudPropagator::startFilePropagation(const SyncFileItemPtr &item,
}
removedDirectory = item->_file + "/";
} else {
const auto isVfsCfApi = syncOptions()._vfs && syncOptions()._vfs->mode() == Vfs::WindowsCfApi;
const auto isDownload = item->_direction == SyncFileItem::Down &&
(item->_instruction == CSYNC_INSTRUCTION_NEW || item->_instruction == CSYNC_INSTRUCTION_SYNC);
const auto isVirtualFile = item->_type == ItemTypeVirtualFile || item->_type == ItemTypeVirtualFileDehydration;
const auto shouldAddBulkPropagateDownloadItem = isDownload && isVirtualFile && isVfsCfApi && !directories.isEmpty();

if (shouldAddBulkPropagateDownloadItem) {
addBulkPropagateDownloadItem(item, directories);
} else {
directories.top().second->appendTask(item);
}
directories.top().second->appendTask(item);
}

if (item->_instruction == CSYNC_INSTRUCTION_CONFLICT) {
Expand Down

0 comments on commit b9f6151

Please sign in to comment.