Skip to content

Commit 489725a

Browse files
committed
PropagateDownloadFile/FileSystem: Preserve permissions for symlinks
This commit will preserve the permissions set on the symlink if a new file is downloaded in the same place and the symlink is overwritten. That fixes a bug which caused the downloaded file to become unreadable if the symlink was broken and no permissions could be retrieved.
1 parent cfe27b5 commit 489725a

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

src/libsync/filesystem.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
#include "filesystem.h"
1616

17+
#include <filesystem>
18+
1719
#include "common/utility.h"
1820
#include <QFile>
1921
#include <QFileInfo>
@@ -140,6 +142,31 @@ qint64 FileSystem::getSize(const QString &filename)
140142
return info.size();
141143
}
142144

145+
QFile::Permissions FileSystem::getPermissions(const QString &filename)
146+
{
147+
using qtStdFilePermissionsMapping = std::pair<QFile::Permission, std::filesystem::perms>;
148+
constexpr std::array<qtStdFilePermissionsMapping, 9> qtStdFilePermissionsMappings = {
149+
std::make_pair(QFileDevice::ReadOwner, std::filesystem::perms::owner_read),
150+
{QFileDevice::WriteOwner, std::filesystem::perms::owner_write},
151+
{QFileDevice::ExeOwner, std::filesystem::perms::owner_exec},
152+
{QFileDevice::ReadGroup, std::filesystem::perms::group_read},
153+
{QFileDevice::WriteGroup, std::filesystem::perms::group_write},
154+
{QFileDevice::ExeGroup, std::filesystem::perms::group_exec},
155+
{QFileDevice::ReadOther, std::filesystem::perms::others_read},
156+
{QFileDevice::WriteOther, std::filesystem::perms::others_write},
157+
{QFileDevice::ExeOther, std::filesystem::perms::others_exec}};
158+
159+
auto fileStatus = std::filesystem::symlink_status(filename.toStdString());
160+
auto permissions = fileStatus.permissions();
161+
162+
QFile::Permissions resultPermissions;
163+
for (auto [qtPermissionFlag, stdPermissionFlag] : qtStdFilePermissionsMappings) {
164+
auto isPermissionSet = (permissions & stdPermissionFlag) != std::filesystem::perms::none;
165+
resultPermissions.setFlag(qtPermissionFlag, isPermissionSet);
166+
}
167+
return resultPermissions;
168+
}
169+
143170
// Code inspired from Qt5's QDir::removeRecursively
144171
bool FileSystem::removeRecursively(const QString &path, const std::function<void(const QString &path, bool isDir)> &onDeleted, QStringList *errors)
145172
{

src/libsync/filesystem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ namespace FileSystem {
6363
*/
6464
qint64 OWNCLOUDSYNC_EXPORT getSize(const QString &filename);
6565

66+
/**
67+
* @brief Get permissions for a file
68+
*
69+
* If the file is a symlink, the permissions for the symlink are returned.
70+
*/
71+
QFile::Permissions OWNCLOUDSYNC_EXPORT getPermissions(const QString &filename);
72+
6673
/**
6774
* @brief Retrieve a file inode with csync
6875
*/

src/libsync/propagatedownload.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,8 @@ namespace { // Anonymous namespace for the recall feature
11081108
static void preserveGroupOwnership(const QString &fileName, const QFileInfo &fi)
11091109
{
11101110
#ifdef Q_OS_UNIX
1111-
int chownErr = chown(fileName.toLocal8Bit().constData(), -1, fi.groupId());
1111+
int chownErr = fchownat(AT_FDCWD, fileName.toLocal8Bit().constData(), -1,
1112+
fi.groupId(), AT_SYMLINK_NOFOLLOW);
11121113
if (chownErr) {
11131114
// TODO: Consider further error handling!
11141115
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()
12201221
auto previousFileExists = FileSystem::fileExists(filename) && _item->_instruction != CSYNC_INSTRUCTION_CASE_CLASH_CONFLICT;
12211222
if (previousFileExists) {
12221223
// Preserve the existing file permissions.
1223-
const auto existingFile = QFileInfo{filename};
1224-
if (existingFile.permissions() != _tmpFile.permissions()) {
1225-
_tmpFile.setPermissions(existingFile.permissions());
1224+
auto previousPermissions = FileSystem::getPermissions(filename);
1225+
if (previousPermissions != FileSystem::getPermissions(_tmpFile.fileName())) {
1226+
_tmpFile.setPermissions(previousPermissions);
12261227
}
1227-
preserveGroupOwnership(_tmpFile.fileName(), existingFile);
1228+
preserveGroupOwnership(_tmpFile.fileName(), QFileInfo(filename));
12281229

12291230
// Make the file a hydrated placeholder if possible
12301231
const auto result = propagator()->syncOptions()._vfs->convertToPlaceholder(_tmpFile.fileName(), *_item, filename);

0 commit comments

Comments
 (0)