diff --git a/src/libsync/filesystem.cpp b/src/libsync/filesystem.cpp index 042da06df18dc..357cee5b9d13d 100644 --- a/src/libsync/filesystem.cpp +++ b/src/libsync/filesystem.cpp @@ -14,6 +14,8 @@ #include "filesystem.h" +#include + #include "common/utility.h" #include #include @@ -140,6 +142,31 @@ qint64 FileSystem::getSize(const QString &filename) return info.size(); } +QFile::Permissions FileSystem::getPermissions(const QString &filename) +{ + using qtStdFilePermissionsMapping = std::pair; + constexpr std::array qtStdFilePermissionsMappings = { + std::make_pair(QFileDevice::ReadOwner, std::filesystem::perms::owner_read), + {QFileDevice::WriteOwner, std::filesystem::perms::owner_write}, + {QFileDevice::ExeOwner, std::filesystem::perms::owner_exec}, + {QFileDevice::ReadGroup, std::filesystem::perms::group_read}, + {QFileDevice::WriteGroup, std::filesystem::perms::group_write}, + {QFileDevice::ExeGroup, std::filesystem::perms::group_exec}, + {QFileDevice::ReadOther, std::filesystem::perms::others_read}, + {QFileDevice::WriteOther, std::filesystem::perms::others_write}, + {QFileDevice::ExeOther, std::filesystem::perms::others_exec}}; + + auto fileStatus = std::filesystem::symlink_status(filename.toStdString()); + auto permissions = fileStatus.permissions(); + + QFile::Permissions resultPermissions; + for (auto [qtPermissionFlag, stdPermissionFlag] : qtStdFilePermissionsMappings) { + auto isPermissionSet = (permissions & stdPermissionFlag) != std::filesystem::perms::none; + resultPermissions.setFlag(qtPermissionFlag, isPermissionSet); + } + return resultPermissions; +} + // Code inspired from Qt5's QDir::removeRecursively bool FileSystem::removeRecursively(const QString &path, const std::function &onDeleted, QStringList *errors) { diff --git a/src/libsync/filesystem.h b/src/libsync/filesystem.h index 602f74bb7f2bc..0cdaaac2dec96 100644 --- a/src/libsync/filesystem.h +++ b/src/libsync/filesystem.h @@ -63,6 +63,13 @@ namespace FileSystem { */ qint64 OWNCLOUDSYNC_EXPORT getSize(const QString &filename); + /** + * @brief Get permissions for a file + * + * If the file is a symlink, the permissions for the symlink are returned. + */ + QFile::Permissions OWNCLOUDSYNC_EXPORT getPermissions(const QString &filename); + /** * @brief Retrieve a file inode with csync */ diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp index c4bbd7e9f1ed0..6ad074eb71113 100644 --- a/src/libsync/propagatedownload.cpp +++ b/src/libsync/propagatedownload.cpp @@ -1108,7 +1108,8 @@ namespace { // Anonymous namespace for the recall feature static void preserveGroupOwnership(const QString &fileName, const QFileInfo &fi) { #ifdef Q_OS_UNIX - int chownErr = chown(fileName.toLocal8Bit().constData(), -1, fi.groupId()); + int chownErr = fchownat(AT_FDCWD, fileName.toLocal8Bit().constData(), -1, + fi.groupId(), AT_SYMLINK_NOFOLLOW); if (chownErr) { // TODO: Consider further error handling! qCWarning(lcPropagateDownload) << QString("preserveGroupOwnership: chown error %1: setting group %2 failed on file %3").arg(chownErr).arg(fi.groupId()).arg(fileName); @@ -1220,11 +1221,11 @@ void PropagateDownloadFile::downloadFinished() auto previousFileExists = FileSystem::fileExists(filename) && _item->_instruction != CSYNC_INSTRUCTION_CASE_CLASH_CONFLICT; if (previousFileExists) { // Preserve the existing file permissions. - const auto existingFile = QFileInfo{filename}; - if (existingFile.permissions() != _tmpFile.permissions()) { - _tmpFile.setPermissions(existingFile.permissions()); + auto previousPermissions = FileSystem::getPermissions(filename); + if (previousPermissions != FileSystem::getPermissions(_tmpFile.fileName())) { + _tmpFile.setPermissions(previousPermissions); } - preserveGroupOwnership(_tmpFile.fileName(), existingFile); + preserveGroupOwnership(_tmpFile.fileName(), QFileInfo(filename)); // Make the file a hydrated placeholder if possible const auto result = propagator()->syncOptions()._vfs->convertToPlaceholder(_tmpFile.fileName(), *_item, filename);