newly created folders will be read-only when needed

Close #6296

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
This commit is contained in:
Matthieu Gallien 2024-01-11 00:16:43 +01:00 committed by Matthieu Gallien
parent bbc976c920
commit 4844f326c1
9 changed files with 540 additions and 16 deletions

View File

@ -20,12 +20,13 @@
#include "config.h"
#include "csync/ocsynclib.h"
#include <QString>
#include <ctime>
#include <QFileInfo>
#include <QLoggingCategory>
#include <csync/ocsynclib.h>
#include <ctime>
class QFile;
@ -42,6 +43,10 @@ OCSYNC_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcFileSystem)
* @brief This file contains file system helper
*/
namespace FileSystem {
enum class FolderPermissions {
ReadOnly,
ReadWrite,
};
/**
* @brief Mark the file as hidden (only has effects on windows)

View File

@ -15,15 +15,20 @@
#include "filesystem.h"
#include "common/utility.h"
#include "csync.h"
#include "vio/csync_vio_local.h"
#include "std/c_time.h"
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDirIterator>
#include <QCoreApplication>
#include "csync.h"
#include "vio/csync_vio_local.h"
#include "std/c_time.h"
#ifdef Q_OS_WIN
#include <securitybaseapi.h>
#include <sddl.h>
#endif
namespace OCC {
@ -189,5 +194,173 @@ bool FileSystem::getInode(const QString &filename, quint64 *inode)
return false;
}
bool FileSystem::setFolderPermissions(const QString &path,
FileSystem::FolderPermissions permissions) noexcept
{
try {
switch (permissions) {
case OCC::FileSystem::FolderPermissions::ReadOnly:
std::filesystem::permissions(path.toStdWString(), std::filesystem::perms::owner_write | std::filesystem::perms::group_write | std::filesystem::perms::others_write, std::filesystem::perm_options::remove);
break;
case OCC::FileSystem::FolderPermissions::ReadWrite:
break;
}
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcFileSystem()) << "exception when modifying folder permissions" << e.what() << e.path1().c_str() << e.path2().c_str();
return false;
}
#ifdef Q_OS_WIN
SECURITY_INFORMATION info = DACL_SECURITY_INFORMATION;
std::unique_ptr<char[]> securityDescriptor;
auto neededLength = 0ul;
if (!GetFileSecurityW(path.toStdWString().c_str(), info, nullptr, 0, &neededLength)) {
const auto lastError = GetLastError();
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << lastError;
return false;
}
securityDescriptor.reset(new char[neededLength]);
if (!GetFileSecurityW(path.toStdWString().c_str(), info, securityDescriptor.get(), neededLength, &neededLength)) {
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << GetLastError();
return false;
}
}
int daclPresent = false, daclDefault = false;
PACL resultDacl = nullptr;
if (!GetSecurityDescriptorDacl(securityDescriptor.get(), &daclPresent, &resultDacl, &daclDefault)) {
qCWarning(lcFileSystem) << "error when calling GetSecurityDescriptorDacl" << path << GetLastError();
return false;
}
if (!daclPresent) {
qCWarning(lcFileSystem) << "error when calling DACL needed to set a folder read-only or read-write is missing" << path;
return false;
}
PSID sid = nullptr;
if (!ConvertStringSidToSidW(L"S-1-5-32-545", &sid))
{
qCWarning(lcFileSystem) << "error when calling ConvertStringSidToSidA" << path << GetLastError();
return false;
}
ACL_SIZE_INFORMATION aclSize;
if (!GetAclInformation(resultDacl, &aclSize, sizeof(aclSize), AclSizeInformation)) {
qCWarning(lcFileSystem) << "error when calling GetAclInformation" << path << GetLastError();
return false;
}
const auto newAclSize = aclSize.AclBytesInUse + sizeof(ACCESS_DENIED_ACE) + GetLengthSid(sid);
qCDebug(lcFileSystem) << "allocated a new DACL object of size" << newAclSize;
std::unique_ptr<ACL> newDacl{reinterpret_cast<PACL>(new char[newAclSize])};
if (!InitializeAcl(newDacl.get(), newAclSize, ACL_REVISION)) {
const auto lastError = GetLastError();
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
qCWarning(lcFileSystem) << "insufficient memory error when calling InitializeAcl" << path;
return false;
}
qCWarning(lcFileSystem) << "error when calling InitializeAcl" << path << lastError;
return false;
}
if (permissions == FileSystem::FolderPermissions::ReadOnly) {
qCInfo(lcFileSystem) << path << "will be read only";
if (!AddAccessDeniedAce(newDacl.get(), ACL_REVISION, FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | FILE_DELETE_CHILD, sid)) {
qCWarning(lcFileSystem) << "error when calling AddAccessDeniedAce << path" << GetLastError();
return false;
}
}
for (int i = 0; i < aclSize.AceCount; ++i) {
void *currentAce = nullptr;
if (!GetAce(resultDacl, i, &currentAce)) {
qCWarning(lcFileSystem) << "error when calling GetAce" << path << GetLastError();
return false;
}
const auto currentAceHeader = reinterpret_cast<PACE_HEADER>(currentAce);
if (permissions == FileSystem::FolderPermissions::ReadWrite) {
qCInfo(lcFileSystem) << path << "will be read write";
}
if (permissions == FileSystem::FolderPermissions::ReadWrite && (ACCESS_DENIED_ACE_TYPE == (currentAceHeader->AceType & ACCESS_DENIED_ACE_TYPE))) {
qCWarning(lcFileSystem) << "AceHeader" << path << currentAceHeader->AceFlags << currentAceHeader->AceSize << currentAceHeader->AceType;
continue;
}
if (!AddAce(newDacl.get(), ACL_REVISION, i + 1, currentAce, currentAceHeader->AceSize)) {
const auto lastError = GetLastError();
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
qCWarning(lcFileSystem) << "insufficient memory error when calling AddAce" << path;
return false;
}
if (lastError != ERROR_INVALID_PARAMETER) {
qCWarning(lcFileSystem) << "invalid parameter error when calling AddAce" << path << "ACL size" << newAclSize;
return false;
}
qCWarning(lcFileSystem) << "error when calling AddAce" << path << lastError << "acl index" << (i + 1);
return false;
}
}
SECURITY_DESCRIPTOR newSecurityDescriptor;
if (!InitializeSecurityDescriptor(&newSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)) {
qCWarning(lcFileSystem) << "error when calling InitializeSecurityDescriptor" << path << GetLastError();
return false;
}
if (!SetSecurityDescriptorDacl(&newSecurityDescriptor, true, newDacl.get(), false)) {
qCWarning(lcFileSystem) << "error when calling SetSecurityDescriptorDacl" << path << GetLastError();
return false;
}
if (!SetFileSecurityW(path.toStdWString().c_str(), info, &newSecurityDescriptor)) {
qCWarning(lcFileSystem) << "error when calling SetFileSecurityW" << path << GetLastError();
return false;
}
#endif
try {
switch (permissions) {
case OCC::FileSystem::FolderPermissions::ReadOnly:
break;
case OCC::FileSystem::FolderPermissions::ReadWrite:
std::filesystem::permissions(path.toStdWString(), std::filesystem::perms::owner_write, std::filesystem::perm_options::add);
break;
}
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcFileSystem()) << "exception when modifying folder permissions" << e.what() << e.path1().c_str() << e.path2().c_str();
return false;
}
return true;
}
bool FileSystem::isFolderReadOnly(const std::filesystem::path &path) noexcept
{
try {
const auto folderStatus = std::filesystem::status(path);
const auto folderPermissions = folderStatus.permissions();
return (folderPermissions & std::filesystem::perms::owner_write) != std::filesystem::perms::owner_write;
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcFileSystem()) << "exception when checking folder permissions" << e.what() << e.path1().c_str() << e.path2().c_str();
return false;
}
}
} // namespace OCC

View File

@ -16,13 +16,14 @@
#include "config.h"
#include "owncloudlib.h"
#include "common/filesystembase.h"
#include <QString>
#include <ctime>
#include <functional>
#include <owncloudlib.h>
// Chain in the base include and extend the namespace
#include "common/filesystembase.h"
#include <filesystem>
class QFile;
@ -96,6 +97,11 @@ namespace FileSystem {
bool OWNCLOUDSYNC_EXPORT removeRecursively(const QString &path,
const std::function<void(const QString &path, bool isDir)> &onDeleted = nullptr,
QStringList *errors = nullptr);
bool OWNCLOUDSYNC_EXPORT setFolderPermissions(const QString &path,
FileSystem::FolderPermissions permissions) noexcept;
bool OWNCLOUDSYNC_EXPORT isFolderReadOnly(const std::filesystem::path &path) noexcept;
}
/** @} */

View File

@ -1442,6 +1442,59 @@ void PropagateDirectory::slotSubJobsFinished(SyncFileItem::Status status)
if (_item->_instruction == CSYNC_INSTRUCTION_RENAME
|| _item->_instruction == CSYNC_INSTRUCTION_NEW
|| _item->_instruction == CSYNC_INSTRUCTION_UPDATE_METADATA) {
if (!_item->_remotePerm.isNull() &&
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddFile) &&
!_item->_remotePerm.hasPermission(RemotePermissions::CanRename) &&
!_item->_remotePerm.hasPermission(RemotePermissions::CanMove) &&
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories)) {
try {
if (QFileInfo::exists(propagator()->fullLocalPath(_item->_file))) {
FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_file), FileSystem::FolderPermissions::ReadOnly);
qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
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);
qCDebug(lcDirectory) << "new permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
}
if (!_item->_renameTarget.isEmpty() && QFileInfo::exists(propagator()->fullLocalPath(_item->_renameTarget))) {
FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_renameTarget), FileSystem::FolderPermissions::ReadOnly);
qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_renameTarget).toStdWString()).permissions());
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);
qCDebug(lcDirectory) << "new permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_renameTarget).toStdWString()).permissions());
}
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcDirectory) << "exception when checking parent folder access rights" << e.what() << e.path1().c_str() << e.path2().c_str();
_item->_status = SyncFileItem::NormalError;
_item->_errorString = tr("The folder %1 cannot be made read-only: %2").arg(_item->_file, e.what());
}
} else if (!_item->_remotePerm.isNull() &&
(_item->_remotePerm.hasPermission(RemotePermissions::CanAddFile) ||
!_item->_remotePerm.hasPermission(RemotePermissions::CanRename) ||
!_item->_remotePerm.hasPermission(RemotePermissions::CanMove) ||
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories))) {
try {
if (QFileInfo::exists(propagator()->fullLocalPath(_item->_file))) {
FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_file), FileSystem::FolderPermissions::ReadWrite);
qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
std::filesystem::permissions(propagator()->fullLocalPath(_item->_file).toStdWString(), std::filesystem::perms::owner_write, std::filesystem::perm_options::add);
qCDebug(lcDirectory) << "new permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_file).toStdWString()).permissions());
}
if (!_item->_renameTarget.isEmpty() && QFileInfo::exists(propagator()->fullLocalPath(_item->_renameTarget))) {
FileSystem::setFolderPermissions(propagator()->fullLocalPath(_item->_renameTarget), FileSystem::FolderPermissions::ReadWrite);
qCDebug(lcDirectory) << "old permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_renameTarget).toStdWString()).permissions());
std::filesystem::permissions(propagator()->fullLocalPath(_item->_renameTarget).toStdWString(), std::filesystem::perms::owner_write, std::filesystem::perm_options::add);
qCDebug(lcDirectory) << "new permissions" << static_cast<int>(std::filesystem::status(propagator()->fullLocalPath(_item->_renameTarget).toStdWString()).permissions());
}
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcDirectory) << "exception when checking parent folder access rights" << e.what() << e.path1().c_str() << e.path2().c_str();
_item->_status = SyncFileItem::NormalError;
_item->_errorString = tr("The folder %1 cannot be made read-only: %2").arg(e.path1().c_str(), e.what());
}
}
const auto result = propagator()->updateMetadata(*_item);
if (!result) {
status = _item->_status = SyncFileItem::FatalError;
@ -1454,7 +1507,7 @@ void PropagateDirectory::slotSubJobsFinished(SyncFileItem::Status status)
}
}
_state = Finished;
qCInfo(lcPropagator) << "PropagateDirectory::slotSubJobsFinished" << "emit finished" << status;
qCInfo(lcPropagator) << "PropagateDirectory::slotSubJobsFinished" << "emit finished" << status << _item->_file;
emit finished(status);
}

View File

@ -32,11 +32,8 @@
#include <QNetworkAccessManager>
#include <QFileInfo>
#include <QDir>
#include <cmath>
#ifdef Q_OS_UNIX
#include <unistd.h>
#endif
#include <cmath>
namespace OCC {
@ -672,8 +669,26 @@ void PropagateDownloadFile::startDownload()
// Can't open(Append) read-only files, make sure to make
// file writable if it exists.
if (_tmpFile.exists())
if (_tmpFile.exists()) {
FileSystem::setFileReadOnly(_tmpFile.fileName(), false);
}
try {
const auto newDirPath = std::filesystem::path{_tmpFile.fileName().toStdWString()};
Q_ASSERT(newDirPath.has_parent_path());
_parentPath = newDirPath.parent_path();
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcPropagateDownload) << "exception when checking parent folder access rights" << e.what() << e.path1().c_str() << e.path2().c_str();
}
if (FileSystem::isFolderReadOnly(_parentPath)) {
FileSystem::setFolderPermissions(QString::fromStdWString(_parentPath.wstring()), FileSystem::FolderPermissions::ReadWrite);
emit propagator()->touchedFile(QString::fromStdWString(_parentPath.wstring()));
_needParentFolderRestorePermissions = true;
}
if (!_tmpFile.open(QIODevice::Append | QIODevice::Unbuffered)) {
qCWarning(lcPropagateDownload) << "could not open temporary file" << _tmpFile.fileName();
done(SyncFileItem::NormalError, _tmpFile.errorString(), ErrorCategory::GenericError);
@ -1272,6 +1287,12 @@ void PropagateDownloadFile::downloadFinished()
return;
}
if (_needParentFolderRestorePermissions) {
FileSystem::setFolderPermissions(QString::fromStdWString(_parentPath.wstring()), FileSystem::FolderPermissions::ReadWrite);
emit propagator()->touchedFile(QString::fromStdWString(_parentPath.wstring()));
_needParentFolderRestorePermissions = false;
}
FileSystem::setFileHidden(filename, false);
// Maybe we downloaded a newer version of the file than we thought we would...

View File

@ -23,6 +23,8 @@
#include <QBuffer>
#include <QFile>
#include <filesystem>
namespace OCC {
class PropagateDownloadEncrypted;
@ -260,5 +262,8 @@ private:
QElapsedTimer _stopwatch;
PropagateDownloadEncrypted *_downloadEncryptedHelper = nullptr;
std::filesystem::path _parentPath;
bool _needParentFolderRestorePermissions = false;
};
}

View File

@ -32,6 +32,7 @@
#include <qstack.h>
#include <QCoreApplication>
#include <filesystem>
#include <ctime>
@ -181,6 +182,24 @@ void PropagateLocalMkdir::startLocalMkdir()
done(SyncFileItem::FileNameClash, tr("Folder %1 cannot be created because of a local file or folder name clash!").arg(newDirStr), ErrorCategory::GenericError);
return;
}
auto parentFolderPath = std::filesystem::path{};
auto parentNeedRollbackPermissions = false;
try {
const auto newDirPath = std::filesystem::path{newDirStr.toStdWString()};
Q_ASSERT(newDirPath.has_parent_path());
parentFolderPath = newDirPath.parent_path();
if (FileSystem::isFolderReadOnly(parentFolderPath)) {
FileSystem::setFolderPermissions(QString::fromStdWString(parentFolderPath.wstring()), FileSystem::FolderPermissions::ReadWrite);
parentNeedRollbackPermissions = true;
emit propagator()->touchedFile(QString::fromStdWString(parentFolderPath.wstring()));
}
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcPropagateLocalMkdir) << "exception when checking parent folder access rights" << e.what() << e.path1().c_str() << e.path2().c_str();
}
emit propagator()->touchedFile(newDirStr);
QDir localDir(propagator()->localPath());
if (!localDir.mkpath(_item->_file)) {
@ -188,6 +207,33 @@ void PropagateLocalMkdir::startLocalMkdir()
return;
}
if (!_item->_remotePerm.isNull() &&
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddFile) &&
!_item->_remotePerm.hasPermission(RemotePermissions::CanRename) &&
!_item->_remotePerm.hasPermission(RemotePermissions::CanMove) &&
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories)) {
try {
FileSystem::setFolderPermissions(newDirStr, FileSystem::FolderPermissions::ReadOnly);
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcPropagateLocalMkdir) << "exception when checking parent folder access rights" << e.what() << e.path1().c_str() << e.path2().c_str();
done(SyncFileItem::NormalError, tr("The folder %1 cannot be made read-only: %2").arg(_item->_file, e.what()), ErrorCategory::GenericError);
return;
}
}
try {
if (parentNeedRollbackPermissions) {
FileSystem::setFolderPermissions(QString::fromStdWString(parentFolderPath.wstring()), FileSystem::FolderPermissions::ReadOnly);
emit propagator()->touchedFile(QString::fromStdWString(parentFolderPath.wstring()));
}
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcPropagateLocalMkdir) << "exception when checking parent folder access rights" << e.what() << e.path1().c_str() << e.path2().c_str();
}
// Insert the directory into the database. The correct etag will be set later,
// once all contents have been propagated, because should_update_metadata is true.
// Adding an entry with a dummy etag to the database still makes sense here
@ -257,12 +303,71 @@ void PropagateLocalRename::start()
return;
}
auto targetParentFolderPath = std::filesystem::path{};
auto targetParentFolderWasReadOnly = false;
try {
const auto newDirPath = std::filesystem::path{targetFile.toStdWString()};
Q_ASSERT(newDirPath.has_parent_path());
targetParentFolderPath = newDirPath.parent_path();
if (FileSystem::isFolderReadOnly(targetParentFolderPath)) {
targetParentFolderWasReadOnly = true;
FileSystem::setFolderPermissions(QString::fromStdWString(targetParentFolderPath.wstring()), FileSystem::FolderPermissions::ReadWrite);
emit propagator()->touchedFile(QString::fromStdWString(targetParentFolderPath.wstring()));
}
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcPropagateLocalRename) << "exception when checking parent folder access rights" << e.what() << e.path1().c_str() << e.path2().c_str();
}
auto originParentFolderPath = std::filesystem::path{};
auto originParentFolderWasReadOnly = false;
try {
const auto newDirPath = std::filesystem::path{existingFile.toStdWString()};
Q_ASSERT(newDirPath.has_parent_path());
originParentFolderPath = newDirPath.parent_path();
if (FileSystem::isFolderReadOnly(originParentFolderPath)) {
originParentFolderWasReadOnly = true;
FileSystem::setFolderPermissions(QString::fromStdWString(originParentFolderPath.wstring()), FileSystem::FolderPermissions::ReadWrite);
emit propagator()->touchedFile(QString::fromStdWString(originParentFolderPath.wstring()));
}
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcPropagateLocalRename) << "exception when checking parent folder access rights" << e.what() << e.path1().c_str() << e.path2().c_str();
}
const auto restoreTargetPermissions = [this] (const auto &parentFolderPath) {
try {
FileSystem::setFolderPermissions(QString::fromStdWString(parentFolderPath.wstring()), FileSystem::FolderPermissions::ReadOnly);
emit propagator()->touchedFile(QString::fromStdWString(parentFolderPath.wstring()));
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcPropagateLocalRename) << "exception when checking parent folder access rights" << e.what() << e.path1().c_str() << e.path2().c_str();
}
};
emit propagator()->touchedFile(existingFile);
emit propagator()->touchedFile(targetFile);
if (QString renameError; !FileSystem::rename(existingFile, targetFile, &renameError)) {
if (targetParentFolderWasReadOnly) {
restoreTargetPermissions(targetParentFolderPath);
}
if (originParentFolderWasReadOnly) {
restoreTargetPermissions(originParentFolderPath);
}
done(SyncFileItem::NormalError, renameError, ErrorCategory::GenericError);
return;
}
if (targetParentFolderWasReadOnly) {
restoreTargetPermissions(targetParentFolderPath);
}
if (originParentFolderWasReadOnly) {
restoreTargetPermissions(originParentFolderPath);
}
}
SyncJournalFileRecord oldRecord;

View File

@ -12,6 +12,7 @@
#include <QtTest>
#include <filesystem>
#include <iostream>
using namespace OCC;
@ -590,6 +591,156 @@ private slots:
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
}
static void demo_perms(std::filesystem::perms p)
{
using std::filesystem::perms;
auto show = [=](char op, perms perm)
{
std::cout << (perms::none == (perm & p) ? '-' : op);
};
show('r', perms::owner_read);
show('w', perms::owner_write);
show('x', perms::owner_exec);
show('r', perms::group_read);
show('w', perms::group_write);
show('x', perms::group_exec);
show('r', perms::others_read);
show('w', perms::others_write);
show('x', perms::others_exec);
std::cout << std::endl;
}
void testReadOnlyFolderIsReallyReadOnly()
{
FakeFolder fakeFolder{FileInfo{}};
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("readOnlyFolder");
remote.find("readOnlyFolder")->permissions = RemotePermissions::fromServerString("M");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
const auto folderStatus = std::filesystem::status(static_cast<QString>(fakeFolder.localPath() + QStringLiteral("/readOnlyFolder")).toStdWString());
QVERIFY(folderStatus.permissions() & std::filesystem::perms::owner_read);
}
void testReadWriteFolderIsReallyReadWrite()
{
FakeFolder fakeFolder{FileInfo{}};
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("readWriteFolder");
remote.find("readWriteFolder")->permissions = RemotePermissions::fromServerString("WDNVRSM");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
const auto folderStatus = std::filesystem::status(static_cast<QString>(fakeFolder.localPath() + QStringLiteral("/readWriteFolder")).toStdWString());
QVERIFY(folderStatus.permissions() & std::filesystem::perms::owner_read);
QVERIFY(folderStatus.permissions() & std::filesystem::perms::owner_write);
}
void testChangePermissionsFolder()
{
FakeFolder fakeFolder{FileInfo{}};
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("testFolder");
remote.find("testFolder")->permissions = RemotePermissions::fromServerString("M");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
auto folderStatus = std::filesystem::status(static_cast<QString>(fakeFolder.localPath() + QStringLiteral("/testFolder")).toStdWString());
QVERIFY(folderStatus.permissions() & std::filesystem::perms::owner_read);
QVERIFY(!static_cast<bool>(folderStatus.permissions() & std::filesystem::perms::owner_write));
remote.find("testFolder")->permissions = RemotePermissions::fromServerString("WDNVRSM");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
folderStatus = std::filesystem::status(static_cast<QString>(fakeFolder.localPath() + QStringLiteral("/testFolder")).toStdWString());
QVERIFY(folderStatus.permissions() & std::filesystem::perms::owner_read);
QVERIFY(folderStatus.permissions() & std::filesystem::perms::owner_write);
remote.find("testFolder")->permissions = RemotePermissions::fromServerString("M");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
folderStatus = std::filesystem::status(static_cast<QString>(fakeFolder.localPath() + QStringLiteral("/testFolder")).toStdWString());
QVERIFY(folderStatus.permissions() & std::filesystem::perms::owner_read);
QVERIFY(!static_cast<bool>(folderStatus.permissions() & std::filesystem::perms::owner_write));
}
void testChangePermissionsForFolderHierarchy()
{
FakeFolder fakeFolder{FileInfo{}};
auto &remote = fakeFolder.remoteModifier();
remote.mkdir("testFolder");
remote.find("testFolder")->permissions = RemotePermissions::fromServerString("M");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
remote.mkdir("testFolder/subFolderReadWrite");
remote.mkdir("testFolder/subFolderReadOnly");
remote.find("testFolder/subFolderReadOnly")->permissions = RemotePermissions::fromServerString("m");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
auto testFolderStatus = std::filesystem::status(static_cast<QString>(fakeFolder.localPath() + QStringLiteral("/testFolder")).toStdWString());
QVERIFY(testFolderStatus.permissions() & std::filesystem::perms::owner_read);
QVERIFY(!static_cast<bool>(testFolderStatus.permissions() & std::filesystem::perms::owner_write));
auto subFolderReadWriteStatus = std::filesystem::status(static_cast<QString>(fakeFolder.localPath() + QStringLiteral("/testFolder/subFolderReadWrite")).toStdWString());
QVERIFY(subFolderReadWriteStatus.permissions() & std::filesystem::perms::owner_read);
QVERIFY(subFolderReadWriteStatus.permissions() & std::filesystem::perms::owner_write);
auto subFolderReadOnlyStatus = std::filesystem::status(static_cast<QString>(fakeFolder.localPath() + QStringLiteral("/testFolder/subFolderReadOnly")).toStdWString());
QVERIFY(subFolderReadOnlyStatus.permissions() & std::filesystem::perms::owner_read);
QVERIFY(!static_cast<bool>(subFolderReadOnlyStatus.permissions() & std::filesystem::perms::owner_write));
remote.find("testFolder/subFolderReadOnly")->permissions = RemotePermissions::fromServerString("WDNVRSm");
remote.find("testFolder/subFolderReadWrite")->permissions = RemotePermissions::fromServerString("m");
remote.mkdir("testFolder/newSubFolder");
remote.create("testFolder/testFile", 12, '9');
remote.create("testFolder/testReadOnlyFile", 13, '8');
remote.find("testFolder/testReadOnlyFile")->permissions = RemotePermissions::fromServerString("m");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
subFolderReadWriteStatus = std::filesystem::status(static_cast<QString>(fakeFolder.localPath() + QStringLiteral("/testFolder/subFolderReadWrite")).toStdWString());
QVERIFY(subFolderReadWriteStatus.permissions() & std::filesystem::perms::owner_read);
QVERIFY(!static_cast<bool>(subFolderReadWriteStatus.permissions() & std::filesystem::perms::owner_write));
subFolderReadOnlyStatus = std::filesystem::status(static_cast<QString>(fakeFolder.localPath() + QStringLiteral("/testFolder/subFolderReadOnly")).toStdWString());
QVERIFY(subFolderReadOnlyStatus.permissions() & std::filesystem::perms::owner_read);
QVERIFY(subFolderReadOnlyStatus.permissions() & std::filesystem::perms::owner_write);
remote.rename("testFolder/subFolderReadOnly", "testFolder/subFolderReadWriteNew");
remote.rename("testFolder/subFolderReadWrite", "testFolder/subFolderReadOnlyNew");
remote.rename("testFolder/testFile", "testFolder/testFileNew");
remote.rename("testFolder/testReadOnlyFile", "testFolder/testReadOnlyFileNew");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
testFolderStatus = std::filesystem::status(static_cast<QString>(fakeFolder.localPath() + QStringLiteral("/testFolder")).toStdWString());
QVERIFY(testFolderStatus.permissions() & std::filesystem::perms::owner_read);
QVERIFY(!static_cast<bool>(testFolderStatus.permissions() & std::filesystem::perms::owner_write));
}
};
QTEST_GUILESS_MAIN(TestPermissions)

View File

@ -95,6 +95,11 @@ private slots:
Logger::instance()->setLogDebug(true);
}
void init()
{
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
}
void testFileDownload() {
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
ItemCompletedSpy completeSpy(fakeFolder);
@ -845,12 +850,12 @@ private slots:
QVERIFY(fakeFolder.syncOnce());
QVERIFY(fakeFolder.currentRemoteState().find("C/tößt"));
QTextCodec::setCodecForLocale(utf8Locale);
}
catch (const std::filesystem::filesystem_error &e)
{
qCritical() << e.what() << e.path1().c_str() << e.path2().c_str() << e.code().message().c_str();
}
QTextCodec::setCodecForLocale(utf8Locale);
#endif
}