From 175ad6fb77fbb80a7372f52355c5c8da493063dc Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 29 Oct 2015 14:10:11 +0100 Subject: [PATCH] Reflect read-only permissions in filesystem #3244 --- src/libsync/filesystem.cpp | 32 +++++++++++++++++++++++++++++++ src/libsync/filesystem.h | 8 ++++++++ src/libsync/propagatedownload.cpp | 4 ++++ src/libsync/syncengine.cpp | 26 ++++++++++++++++++++----- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/libsync/filesystem.cpp b/src/libsync/filesystem.cpp index 5420fd58a..917992690 100644 --- a/src/libsync/filesystem.cpp +++ b/src/libsync/filesystem.cpp @@ -111,6 +111,38 @@ void FileSystem::setFileHidden(const QString& filename, bool hidden) #endif } +static QFile::Permissions getDefaultWritePermissions() +{ + QFile::Permissions result = QFile::WriteUser; +#ifndef Q_OS_WIN + __mode_t mask = umask(0); + umask(mask); + if (!(mask & S_IWGRP)) { + result |= QFile::WriteGroup; + } + if (!(mask & S_IWOTH)) { + result |= QFile::WriteOther; + } +#endif + return result; +} + +void FileSystem::setFileReadOnly(const QString& filename, bool readonly) +{ + QFile file(filename); + QFile::Permissions permissions = file.permissions(); + + QFile::Permissions allWritePermissions = + QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther | QFile::WriteOwner; + static QFile::Permissions defaultWritePermissions = getDefaultWritePermissions(); + + permissions &= ~allWritePermissions; + if (!readonly) { + permissions |= defaultWritePermissions; + } + file.setPermissions(permissions); +} + time_t FileSystem::getModTime(const QString &filename) { csync_vio_file_stat_t* stat = csync_vio_file_stat_new(); diff --git a/src/libsync/filesystem.h b/src/libsync/filesystem.h index 8a101cc34..e6a760a81 100644 --- a/src/libsync/filesystem.h +++ b/src/libsync/filesystem.h @@ -47,6 +47,14 @@ bool fileEquals(const QString &fn1, const QString &fn2); */ void OWNCLOUDSYNC_EXPORT setFileHidden(const QString& filename, bool hidden); +/** + * @brief Marks the file as read-only. + * + * On linux this either revokes all 'w' permissions or restores permissions + * according to the umask. + */ +void OWNCLOUDSYNC_EXPORT setFileReadOnly(const QString& filename, bool readonly); + /** convert a "normal" windows path into a path that can be 32k chars long. */ QString OWNCLOUDSYNC_EXPORT longWinPath( const QString& inpath ); diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp index 0de8a2139..a0fde8a6c 100644 --- a/src/libsync/propagatedownload.cpp +++ b/src/libsync/propagatedownload.cpp @@ -678,6 +678,10 @@ void PropagateDownloadFileQNAM::downloadFinished(const QByteArray& checksumType, } } + // Apply the remote permissions + FileSystem::setFileReadOnly(_tmpFile.fileName(), + !_item->_remotePerm.contains('W')); + QString error; _propagator->addTouchedFile(fn); // The fileChanged() check is done above to generate better error messages. diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 77c4cab2e..54ced86ce 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -22,6 +22,7 @@ #include "creds/abstractcredentials.h" #include "syncfilestatus.h" #include "csync_private.h" +#include "filesystem.h" #ifdef Q_OS_WIN #include @@ -459,16 +460,31 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote ) if (remote && item->_should_update_metadata && !item->_isDirectory && item->_instruction == CSYNC_INSTRUCTION_NONE) { // Update the database now already: New fileid or Etag or RemotePerm // Or for files that were detected as "resolved conflict". - // They should have been a conflict because they both were new, or both - // had their local mtime or remote etag modified, but the size and mtime - // is the same on the server. This typically happens when the database is removed. - // Nothing will be done for those files, but we still need to update the database. + + // In case of "resolved conflict": there should have been a conflict because they + // both were new, or both had their local mtime or remote etag modified, but the + // size and mtime is the same on the server. This typically happens when the + // database is removed. Nothing will be done for those files, but we still need + // to update the database. + + // This metadata update *could* be a propagation job of its own, but since it's + // quick to do and we don't want to create a potentially large number of + // mini-jobs later on, we just update metadata right now. + + QString filePath = _localPath + item->_file; // Even if the mtime is different on the server, we always want to keep the mtime from // the file system in the DB, this is to avoid spurious upload on the next sync item->_modtime = file->other.modtime; - _journal->updateFileRecordMetadata(SyncJournalFileRecord(*item, _localPath + item->_file)); + // If the 'W' remote permission changed, update the local filesystem + SyncJournalFileRecord prev = _journal->getFileRecord(item->_file); + if (prev._remotePerm.contains('W') != item->_remotePerm.contains('W')) { + const bool isReadOnly = !item->_remotePerm.contains('W'); + FileSystem::setFileReadOnly(filePath, isReadOnly); + } + + _journal->updateFileRecordMetadata(SyncJournalFileRecord(*item, filePath)); item->_should_update_metadata = false; } if (item->_isDirectory && file->should_update_metadata) {