Skip to content

Commit 4a6bdfb

Browse files
authored
Merge pull request #9642 from nextcloud/backport/9637/stable-4.0
[stable-4.0] Set folders writable if remote perms require it, recurse into subdirs if etag and remoteperms differ
2 parents 899f395 + 0407804 commit 4a6bdfb

File tree

5 files changed

+76
-8
lines changed

5 files changed

+76
-8
lines changed

src/common/remotepermissions.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ class OCSYNC_EXPORT RemotePermissions
7979
{
8080
return _value & (1 << static_cast<int>(p));
8181
}
82+
83+
/// Returns true if the local folder needs to be read/writeable for the current remote permissions set
84+
[[nodiscard]] bool hasPermissionsForReadWrite() const
85+
{
86+
return hasPermission(CanWrite) ||
87+
hasPermission(CanDelete) ||
88+
hasPermission(CanRename) ||
89+
hasPermission(CanMove) ||
90+
hasPermission(CanAddFile) ||
91+
hasPermission(CanAddSubDirectories);
92+
}
93+
8294
void setPermission(Permissions p)
8395
{
8496
_value |= (1 << static_cast<int>(p)) | notNullMask;

src/libsync/discovery.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,7 +1070,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it
10701070
_discoveryData->findAndCancelDeletedJob(originalPath);
10711071

10721072
postProcessRename(path);
1073-
if (item->isDirectory() && serverEntry.isValid() && dbEntry.isValid() && serverEntry.etag == dbEntry._etag && serverEntry.remotePerm != dbEntry._remotePerm) {
1073+
if (item->isDirectory() && serverEntry.isValid() && dbEntry.isValid() && serverEntry.etag == dbEntry._etag && serverEntry.remotePerm == dbEntry._remotePerm) {
10741074
_queryServer = ParentNotChanged;
10751075
}
10761076
processFileFinalize(item, path, item->isDirectory(), item->_instruction == CSYNC_INSTRUCTION_RENAME ? NormalQuery : ParentDontExist, _queryServer);
@@ -1260,7 +1260,7 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
12601260
item->isPermissionsInvalid = localEntry.isPermissionsInvalid;
12611261

12621262
auto recurseQueryLocal = _queryLocal == ParentNotChanged ? ParentNotChanged : localEntry.isDirectory || item->_instruction == CSYNC_INSTRUCTION_RENAME ? NormalQuery : ParentDontExist;
1263-
if (item->isDirectory() && serverEntry.isValid() && dbEntry.isValid() && serverEntry.etag == dbEntry._etag && serverEntry.remotePerm != dbEntry._remotePerm) {
1263+
if (item->isDirectory() && serverEntry.isValid() && dbEntry.isValid() && serverEntry.etag == dbEntry._etag && serverEntry.remotePerm == dbEntry._remotePerm) {
12641264
recurseQueryServer = ParentNotChanged;
12651265
}
12661266
processFileFinalize(item, path, recurse, recurseQueryLocal, recurseQueryServer);
@@ -1699,7 +1699,7 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
16991699
processRename(path);
17001700
recurseQueryServer = etag.get() == base._etag ? ParentNotChanged : NormalQuery;
17011701
}
1702-
if (item->isDirectory() && serverEntry.isValid() && dbEntry.isValid() && serverEntry.etag == dbEntry._etag && serverEntry.remotePerm != dbEntry._remotePerm) {
1702+
if (item->isDirectory() && serverEntry.isValid() && dbEntry.isValid() && serverEntry.etag == dbEntry._etag && serverEntry.remotePerm == dbEntry._remotePerm) {
17031703
recurseQueryServer = ParentNotChanged;
17041704
}
17051705
processFileFinalize(item, path, item->isDirectory(), NormalQuery, recurseQueryServer);

src/libsync/owncloudpropagator.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,8 +1506,7 @@ void PropagateDirectory::slotSubJobsFinished(SyncFileItem::Status status)
15061506
|| _item->_instruction == CSYNC_INSTRUCTION_UPDATE_METADATA) {
15071507

15081508
if (!_item->_remotePerm.isNull() &&
1509-
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddFile) &&
1510-
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories)) {
1509+
!_item->_remotePerm.hasPermissionsForReadWrite()) {
15111510
try {
15121511
if (const auto fileName = propagator()->fullLocalPath(_item->_file); FileSystem::fileExists(fileName)) {
15131512
FileSystem::setFolderPermissions(fileName, FileSystem::FolderPermissions::ReadOnly);

src/libsync/propagatorjobs.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,7 @@ void PropagateLocalMkdir::startLocalMkdir()
266266
}
267267

268268
if (!_item->_remotePerm.isNull() &&
269-
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddFile) &&
270-
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories)) {
269+
!_item->_remotePerm.hasPermissionsForReadWrite()) {
271270
try {
272271
FileSystem::setFolderPermissions(newDirStr, FileSystem::FolderPermissions::ReadOnly);
273272
}

test/testpermissions.cpp

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <iostream>
1919

2020
using namespace OCC;
21+
using namespace Qt::StringLiterals;
2122

2223
static void applyPermissionsFromName(FileInfo &info) {
2324
static QRegularExpression rx("_PERM_([^_]*)_[^/]*$");
@@ -969,7 +970,64 @@ private slots:
969970
fakeFolder.remoteModifier().insert("groupFolder/simpleChildFolder/otherFile", 12);
970971

971972
QVERIFY(fakeFolder.syncOnce());
972-
QCOMPARE(propfindCounter, 8);
973+
QCOMPARE(propfindCounter, 10);
974+
}
975+
976+
void testFolderReadonlyWhenRemotePermissionsWithoutEtagChanged_data()
977+
{
978+
QTest::addColumn<bool>("isReadOnly");
979+
QTest::newRow("G") << true;
980+
QTest::newRow("GS") << true;
981+
QTest::newRow("GR") << true;
982+
QTest::newRow("GRS") << true;
983+
QTest::newRow("GRM") << true;
984+
QTest::newRow("GRMS") << true;
985+
QTest::newRow("GW") << false;
986+
QTest::newRow("GWS") << false;
987+
QTest::newRow("GD") << false;
988+
QTest::newRow("GDS") << false;
989+
QTest::newRow("GN") << false;
990+
QTest::newRow("GV") << false;
991+
QTest::newRow("GC") << false;
992+
QTest::newRow("GK") << false;
993+
QTest::newRow("GWD") << false;
994+
QTest::newRow("GWDS") << false;
995+
QTest::newRow("GDNCV") << false;
996+
QTest::newRow("GDNCVS") << false;
997+
QTest::newRow("GDNVCKR") << false;
998+
QTest::newRow("GDNVCKRS") << false;
999+
}
1000+
1001+
void testFolderReadonlyWhenRemotePermissionsWithoutEtagChanged()
1002+
{
1003+
const auto remotePerm = QString::fromLocal8Bit(QTest::currentDataTag());
1004+
QFETCH(bool, isReadOnly);
1005+
auto remotePermChange = isReadOnly ? (remotePerm + "W") : u"G"_s;
1006+
1007+
FakeFolder fakeFolder{FileInfo{}};
1008+
1009+
const auto isFolderReadOnly = [&fakeFolder](const QString &relativePath) -> bool {
1010+
return FileSystem::isFolderReadOnly(std::filesystem::path{FileSystem::joinPath(fakeFolder.localPath(), relativePath).toStdWString()});
1011+
};
1012+
1013+
fakeFolder.remoteModifier().mkdir("root");
1014+
fakeFolder.remoteModifier().mkdir("root/subdir");
1015+
fakeFolder.remoteModifier().mkdir("root/subdir/subsubdir");
1016+
auto rootFolder = fakeFolder.remoteModifier().find("root");
1017+
1018+
qInfo() << "initial sync with" << remotePerm;
1019+
setAllPerm(rootFolder, RemotePermissions::fromServerString(remotePerm));
1020+
QVERIFY(fakeFolder.syncOnce());
1021+
QCOMPARE_EQ(isFolderReadOnly(u"root"_s), isReadOnly);
1022+
QCOMPARE_EQ(isFolderReadOnly(u"root/subdir"_s), isReadOnly);
1023+
QCOMPARE_EQ(isFolderReadOnly(u"root/subdir/subsubdir"_s), isReadOnly);
1024+
1025+
qInfo() << "remote update with" << remotePermChange;
1026+
setAllPerm(rootFolder, RemotePermissions::fromServerString(remotePermChange));
1027+
QVERIFY(fakeFolder.syncOnce());
1028+
QCOMPARE_EQ(isFolderReadOnly(u"root"_s), !isReadOnly);
1029+
QCOMPARE_EQ(isFolderReadOnly(u"root/subdir"_s), !isReadOnly);
1030+
QCOMPARE_EQ(isFolderReadOnly(u"root/subdir/subsubdir"_s), !isReadOnly);
9731031
}
9741032
};
9751033

0 commit comments

Comments
 (0)