Skip to content

Commit 1e378a9

Browse files
authoredNov 21, 2024··
Merge pull request #7532 from nextcloud/bugfix/narrowDownPermissionsDuringSync
Bugfix/narrow down permissions during sync
2 parents 728cff3 + 1417e8c commit 1e378a9

10 files changed

+39
-83
lines changed
 

‎src/csync/csync.h

+2
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ struct OCSYNC_EXPORT csync_file_stat_s {
217217
bool is_hidden BITFIELD(1); // Not saved in the DB, only used during discovery for local files.
218218
bool isE2eEncrypted BITFIELD(1);
219219
bool is_metadata_missing BITFIELD(1); // Indicates the file has missing metadata, f.ex. the file is not a placeholder in case of vfs.
220+
bool isPermissionsInvalid BITFIELD(1);
220221

221222
QByteArray path;
222223
QByteArray rename_path;
@@ -244,6 +245,7 @@ struct OCSYNC_EXPORT csync_file_stat_s {
244245
, is_hidden(false)
245246
, isE2eEncrypted(false)
246247
, is_metadata_missing(false)
248+
, isPermissionsInvalid(false)
247249
{ }
248250
};
249251

‎src/csync/vio/csync_vio_local_unix.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -170,5 +170,7 @@ static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf
170170
buf->inode = sb.st_ino;
171171
buf->modtime = sb.st_mtime;
172172
buf->size = sb.st_size;
173+
buf->isPermissionsInvalid = (sb.st_mode & S_IWOTH) == S_IWOTH;
174+
173175
return 0;
174176
}

‎src/libsync/discovery.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,10 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
10701070
if (_queryLocal != NormalQuery && _queryServer != NormalQuery)
10711071
recurse = false;
10721072

1073+
if (localEntry.isPermissionsInvalid) {
1074+
recurse = true;
1075+
}
1076+
10731077
if ((item->_direction == SyncFileItem::Down || item->_instruction == CSYNC_INSTRUCTION_CONFLICT || item->_instruction == CSYNC_INSTRUCTION_NEW || item->_instruction == CSYNC_INSTRUCTION_SYNC) &&
10741078
(item->_modtime <= 0 || item->_modtime >= 0xFFFFFFFF)) {
10751079
item->_instruction = CSYNC_INSTRUCTION_ERROR;
@@ -1097,6 +1101,13 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
10971101
}
10981102
}
10991103

1104+
if (localEntry.isPermissionsInvalid && item->_instruction == CSyncEnums::CSYNC_INSTRUCTION_NONE) {
1105+
item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
1106+
item->_direction = SyncFileItem::Down;
1107+
}
1108+
1109+
item->isPermissionsInvalid = localEntry.isPermissionsInvalid;
1110+
11001111
auto recurseQueryLocal = _queryLocal == ParentNotChanged ? ParentNotChanged : localEntry.isDirectory || item->_instruction == CSYNC_INSTRUCTION_RENAME ? NormalQuery : ParentDontExist;
11011112
processFileFinalize(item, path, recurse, recurseQueryLocal, recurseQueryServer);
11021113
};

‎src/libsync/discoveryphase.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ void DiscoverySingleLocalDirectoryJob::run() {
348348
i.isSymLink = dirent->type == ItemTypeSoftLink;
349349
i.isVirtualFile = dirent->type == ItemTypeVirtualFile || dirent->type == ItemTypeVirtualFileDownload;
350350
i.isMetadataMissing = dirent->is_metadata_missing;
351+
i.isPermissionsInvalid = dirent->isPermissionsInvalid;
351352
i.type = dirent->type;
352353
results.push_back(i);
353354
}

‎src/libsync/discoveryphase.h

+1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ struct LocalInfo
106106
bool isVirtualFile = false;
107107
bool isSymLink = false;
108108
bool isMetadataMissing = false;
109+
bool isPermissionsInvalid = false;
109110
[[nodiscard]] bool isValid() const { return !name.isNull(); }
110111
};
111112

‎src/libsync/filesystem.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ bool FileSystem::setFolderPermissions(const QString &path,
468468
case OCC::FileSystem::FolderPermissions::ReadOnly:
469469
break;
470470
case OCC::FileSystem::FolderPermissions::ReadWrite:
471+
std::filesystem::permissions(stdStrPath, std::filesystem::perms::others_write, std::filesystem::perm_options::remove);
471472
std::filesystem::permissions(stdStrPath, std::filesystem::perms::owner_write, std::filesystem::perm_options::add);
472473
break;
473474
}

‎src/libsync/owncloudpropagator.cpp

+12-23
Original file line numberDiff line numberDiff line change
@@ -1457,21 +1457,13 @@ void PropagateDirectory::slotSubJobsFinished(SyncFileItem::Status status)
14571457
#if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
14581458
if (!_item->_remotePerm.isNull() &&
14591459
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddFile) &&
1460-
!_item->_remotePerm.hasPermission(RemotePermissions::CanRename) &&
1461-
!_item->_remotePerm.hasPermission(RemotePermissions::CanMove) &&
14621460
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories)) {
14631461
try {
14641462
if (FileSystem::fileExists(propagator()->fullLocalPath(_item->_file))) {
14651463
FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_file), FileSystem::FolderPermissions::ReadOnly);
1466-
qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
1467-
std::filesystem::permissions(propagator()->fullLocalPath(_item->_file).toStdWString(), std::filesystem::perms::owner_write | std::filesystem::perms::group_write | std::filesystem::perms::others_write, std::filesystem::perm_options::remove);
1468-
qCDebug(lcDirectory) << "new permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
14691464
}
14701465
if (!_item->_renameTarget.isEmpty() && FileSystem::fileExists(propagator()->fullLocalPath(_item->_renameTarget))) {
14711466
FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_renameTarget), FileSystem::FolderPermissions::ReadOnly);
1472-
qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_renameTarget).toStdWString()).permissions());
1473-
std::filesystem::permissions(propagator()->fullLocalPath(_item->_renameTarget).toStdWString(), std::filesystem::perms::owner_write | std::filesystem::perms::group_write | std::filesystem::perms::others_write, std::filesystem::perm_options::remove);
1474-
qCDebug(lcDirectory) << "new permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_renameTarget).toStdWString()).permissions());
14751467
}
14761468
}
14771469
catch (const std::filesystem::filesystem_error &e)
@@ -1480,23 +1472,20 @@ void PropagateDirectory::slotSubJobsFinished(SyncFileItem::Status status)
14801472
_item->_status = SyncFileItem::NormalError;
14811473
_item->_errorString = tr("The folder %1 cannot be made read-only: %2").arg(_item->_file, e.what());
14821474
}
1483-
} else if (!_item->_remotePerm.isNull() &&
1484-
(_item->_remotePerm.hasPermission(RemotePermissions::CanAddFile) ||
1485-
!_item->_remotePerm.hasPermission(RemotePermissions::CanRename) ||
1486-
!_item->_remotePerm.hasPermission(RemotePermissions::CanMove) ||
1487-
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories))) {
1475+
} else {
14881476
try {
1489-
if (FileSystem::fileExists(propagator()->fullLocalPath(_item->_file))) {
1490-
FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_file), FileSystem::FolderPermissions::ReadWrite);
1491-
qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
1492-
std::filesystem::permissions(propagator()->fullLocalPath(_item->_file).toStdWString(), std::filesystem::perms::owner_write, std::filesystem::perm_options::add);
1493-
qCDebug(lcDirectory) << "new permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
1477+
const auto permissionsChangeHelper = [] (const auto fileName)
1478+
{
1479+
qCDebug(lcDirectory) << fileName << "permissions changed: old permissions" << static_cast<int>(std::filesystem::status(fileName.toStdWString()).permissions());
1480+
FileSystem::setFolderPermissions(fileName, FileSystem::FolderPermissions::ReadWrite);
1481+
qCDebug(lcDirectory) << fileName << "applied new permissions" << static_cast<int>(std::filesystem::status(fileName.toStdWString()).permissions());
1482+
};
1483+
1484+
if (const auto fileName = propagator()->fullLocalPath(_item->_file); FileSystem::fileExists(fileName)) {
1485+
permissionsChangeHelper(fileName);
14941486
}
1495-
if (!_item->_renameTarget.isEmpty() && FileSystem::fileExists(propagator()->fullLocalPath(_item->_renameTarget))) {
1496-
FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_renameTarget), FileSystem::FolderPermissions::ReadWrite);
1497-
qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_renameTarget).toStdWString()).permissions());
1498-
std::filesystem::permissions(propagator()->fullLocalPath(_item->_renameTarget).toStdWString(), std::filesystem::perms::owner_write, std::filesystem::perm_options::add);
1499-
qCDebug(lcDirectory) << "new permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_renameTarget).toStdWString()).permissions());
1487+
if (const auto fileName = propagator()->fullLocalPath(_item->_renameTarget); !_item->_renameTarget.isEmpty() && FileSystem::fileExists(fileName)) {
1488+
permissionsChangeHelper(fileName);
15001489
}
15011490
}
15021491
catch (const std::filesystem::filesystem_error &e)

‎src/libsync/syncengine.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,10 @@ void OCC::SyncEngine::slotItemDiscovered(const OCC::SyncFileItemPtr &item)
363363
const bool isReadOnly = !item->_remotePerm.isNull() && !item->_remotePerm.hasPermission(RemotePermissions::CanWrite);
364364
modificationHappened = FileSystem::setFileReadOnlyWeak(filePath, isReadOnly);
365365
}
366+
if (item->isPermissionsInvalid) {
367+
const auto isReadOnly = !item->_remotePerm.isNull() && !item->_remotePerm.hasPermission(RemotePermissions::CanWrite);
368+
FileSystem::setFileReadOnly(filePath, isReadOnly);
369+
}
366370

367371
modificationHappened |= item->_size != prev._fileSize;
368372

‎src/libsync/syncfileitem.h

+2
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,8 @@ class OWNCLOUDSYNC_EXPORT SyncFileItem
343343
bool _isLivePhoto = false;
344344
QString _livePhotoFile;
345345

346+
bool isPermissionsInvalid = false;
347+
346348
QString _discoveryResult;
347349
};
348350

‎test/testpermissions.cpp

+3-60
Original file line numberDiff line numberDiff line change
@@ -564,63 +564,6 @@ private slots:
564564
QVERIFY(cls.find("zallowed/sub2/file"));
565565
}
566566

567-
// Test for issue #7293
568-
void testAllowedMoveForbiddenDelete() {
569-
FakeFolder fakeFolder{FileInfo{}};
570-
571-
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders,
572-
[&](const QList<SyncFileItemPtr> &folders, const QString &localPath, std::function<void(bool)> callback) {
573-
for(const auto &oneFolder : folders) {
574-
FileSystem::removeRecursively(localPath + oneFolder->_file);
575-
}
576-
callback(false);
577-
});
578-
579-
// Some of this test depends on the order of discovery. With threading
580-
// that order becomes effectively random, but we want to make sure to test
581-
// all cases and thus disable threading.
582-
auto syncOpts = fakeFolder.syncEngine().syncOptions();
583-
syncOpts._parallelNetworkJobs = 1;
584-
fakeFolder.syncEngine().setSyncOptions(syncOpts);
585-
586-
auto &lm = fakeFolder.localModifier();
587-
auto &rm = fakeFolder.remoteModifier();
588-
rm.mkdir("changeonly");
589-
rm.mkdir("changeonly/sub1");
590-
rm.insert("changeonly/sub1/file1");
591-
rm.insert("changeonly/sub1/filetorname1a");
592-
rm.insert("changeonly/sub1/filetorname1z");
593-
rm.mkdir("changeonly/sub2");
594-
rm.insert("changeonly/sub2/file2");
595-
rm.insert("changeonly/sub2/filetorname2a");
596-
rm.insert("changeonly/sub2/filetorname2z");
597-
598-
setAllPerm(rm.find("changeonly"), RemotePermissions::fromServerString("NSV"));
599-
600-
QVERIFY(fakeFolder.syncOnce());
601-
602-
lm.rename("changeonly/sub1/filetorname1a", "changeonly/sub1/aaa1_renamed");
603-
lm.rename("changeonly/sub1/filetorname1z", "changeonly/sub1/zzz1_renamed");
604-
605-
lm.rename("changeonly/sub2/filetorname2a", "changeonly/sub2/aaa2_renamed");
606-
lm.rename("changeonly/sub2/filetorname2z", "changeonly/sub2/zzz2_renamed");
607-
608-
auto expectedState = fakeFolder.currentLocalState();
609-
610-
QVERIFY(fakeFolder.syncOnce());
611-
QCOMPARE(fakeFolder.currentLocalState(), expectedState);
612-
QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
613-
614-
lm.rename("changeonly/sub1", "changeonly/aaa");
615-
lm.rename("changeonly/sub2", "changeonly/zzz");
616-
617-
expectedState = fakeFolder.currentLocalState();
618-
619-
QVERIFY(fakeFolder.syncOnce());
620-
QCOMPARE(fakeFolder.currentLocalState(), expectedState);
621-
QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
622-
}
623-
624567
void testParentMoveNotAllowedChildrenRestored()
625568
{
626569
FakeFolder fakeFolder{FileInfo{}};
@@ -722,7 +665,7 @@ private slots:
722665

723666
remote.mkdir("readWriteFolder");
724667

725-
remote.find("readWriteFolder")->permissions = RemotePermissions::fromServerString("WDNVRSM");
668+
remote.find("readWriteFolder")->permissions = RemotePermissions::fromServerString("CKWDNVRSM");
726669

727670
QVERIFY(fakeFolder.syncOnce());
728671
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
@@ -757,7 +700,7 @@ private slots:
757700
QVERIFY(folderStatus.permissions() & std::filesystem::perms::owner_read);
758701
QVERIFY(!static_cast<bool>(folderStatus.permissions() & std::filesystem::perms::owner_write));
759702

760-
remote.find("testFolder")->permissions = RemotePermissions::fromServerString("WDNVRSM");
703+
remote.find("testFolder")->permissions = RemotePermissions::fromServerString("CKWDNVRSM");
761704

762705
QVERIFY(fakeFolder.syncOnce());
763706
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
@@ -815,7 +758,7 @@ private slots:
815758
QVERIFY(subFolderReadOnlyStatus.permissions() & std::filesystem::perms::owner_read);
816759
QVERIFY(!static_cast<bool>(subFolderReadOnlyStatus.permissions() & std::filesystem::perms::owner_write));
817760

818-
remote.find("testFolder/subFolderReadOnly")->permissions = RemotePermissions::fromServerString("WDNVRSm");
761+
remote.find("testFolder/subFolderReadOnly")->permissions = RemotePermissions::fromServerString("CKWDNVRSm");
819762
remote.find("testFolder/subFolderReadWrite")->permissions = RemotePermissions::fromServerString("m");
820763
remote.mkdir("testFolder/newSubFolder");
821764
remote.create("testFolder/testFile", 12, '9');

0 commit comments

Comments
 (0)
Please sign in to comment.