Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

newly created folders will be read-only when needed #6343

Merged
merged 10 commits into from
Mar 12, 2024
16 changes: 8 additions & 8 deletions .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: qt-5.15

steps:
- name: cmake
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
Expand All @@ -13,7 +13,7 @@ steps:
- cmake -G Ninja -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_BUILD_TYPE=Debug -DQUICK_COMPILER=ON -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DADD_E2E_TESTS=ON -DECM_ENABLE_SANITIZERS=address -DCMAKE_CXX_FLAGS=-Werror -DOPENSSL_ROOT_DIR=/usr/local/lib64 ../src

- name: compile
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
Expand All @@ -22,7 +22,7 @@ steps:
- ninja

- name: test
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
Expand Down Expand Up @@ -74,23 +74,23 @@ name: qt-5.15-clang

steps:
- name: cmake
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
commands:
- cd /drone/build
- cmake -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_C_COMPILER=clang-14 -DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_BUILD_TYPE=Debug -DQUICK_COMPILER=ON -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DADD_E2E_TESTS=ON -DECM_ENABLE_SANITIZERS=address -DCMAKE_CXX_FLAGS=-Werror -DOPENSSL_ROOT_DIR=/usr/local/lib64 ../src
- name: compile
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
commands:
- cd /drone/build
- ninja
- name: test
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
Expand Down Expand Up @@ -142,7 +142,7 @@ name: AppImage

steps:
- name: build
image: ghcr.io/nextcloud/continuous-integration-client-appimage:client-appimage-9
image: ghcr.io/nextcloud/continuous-integration-client-appimage:client-appimage-10
environment:
CI_UPLOAD_GIT_TOKEN:
from_secret: CI_UPLOAD_GIT_TOKEN
Expand Down Expand Up @@ -196,6 +196,6 @@ trigger:
- push
---
kind: signature
hmac: d72110d7f9cba086ca21f9f4f4032ae87f3d9555ab4c5f55d3aeb3df99cab540
hmac: a8fd97516ee53ca8c938ad413e030dc7df483f116c4b19b5811e359960b7b44d

...
2 changes: 1 addition & 1 deletion .github/workflows/linux-clang-compile-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
build:
name: Linux Clang compilation and tests
runs-on: ubuntu-22.04
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
steps:
- uses: actions/checkout@v4
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linux-gcc-compile-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
build:
name: Linux GCC compilation and tests
runs-on: ubuntu-22.04
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
steps:
- uses: actions/checkout@v4
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
build:
name: SonarCloud analysis
runs-on: ubuntu-22.04
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
env:
SONAR_SERVER_URL: "https://sonarcloud.io"
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed
Expand Down
14 changes: 8 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 17)
cmake_policy(SET CMP0071 NEW) # Enable use of QtQuick compiler/generated code

find_program(CLANG_TIDY_EXE NAMES "clang-tidy")
if (CLANG_TIDY_EXE)
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} -checks=-*,modernize-use-auto,modernize-use-using,modernize-use-nodiscard,modernize-use-nullptr,modernize-use-override,cppcoreguidelines-pro-type-static-cast-downcast,modernize-use-default-member-init,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-init-variables)
endif()

project(client)

if(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum OSX deployment version")
endif()

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED 17)

include(FeatureSummary)

find_program(CLANG_TIDY_EXE NAMES "clang-tidy")
if (CLANG_TIDY_EXE)
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} -checks=-*,modernize-use-auto,modernize-use-using,modernize-use-nodiscard,modernize-use-nullptr,modernize-use-override,cppcoreguidelines-pro-type-static-cast-downcast,modernize-use-default-member-init,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-init-variables)
endif()

set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES)

set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
Expand Down
2 changes: 1 addition & 1 deletion admin/linux/build-appimage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ chmod a+x linuxdeployqt.AppImage
rm ./linuxdeployqt.AppImage
cp -r ./squashfs-root ./linuxdeployqt-squashfs-root
unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/lib/x86_64-linux-gnu
export LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib/x86_64-linux-gnu
./squashfs-root/AppRun ${DESKTOP_FILE} -bundle-non-qt-libs -qmldir=${DESKTOP_CLIENT_ROOT}/src/gui

# Set origin
Expand Down
9 changes: 7 additions & 2 deletions src/common/filesystembase.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@

#pragma once

#include "config.h"

Check failure on line 21 in src/common/filesystembase.h

View workflow job for this annotation

GitHub Actions / build

src/common/filesystembase.h:21:10 [clang-diagnostic-error]

'config.h' file not found

#include "csync/ocsynclib.h"

#include <QString>
#include <ctime>
#include <QFileInfo>
#include <QLoggingCategory>

#include <csync/ocsynclib.h>
#include <ctime>

class QFile;

Expand All @@ -42,6 +43,10 @@
* @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)
Expand Down
5 changes: 5 additions & 0 deletions src/libsync/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ target_link_libraries(nextcloudsync
KF5::Archive
)

target_compile_features(nextcloudsync
PRIVATE
cxx_std_17
)

find_package(Qt5 REQUIRED COMPONENTS Gui Widgets Svg)
target_link_libraries(nextcloudsync PUBLIC Qt5::Gui Qt5::Widgets Qt5::Svg)

Expand Down
179 changes: 176 additions & 3 deletions src/libsync/filesystem.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*

Check notice on line 1 in src/libsync/filesystem.cpp

View workflow job for this annotation

GitHub Actions / build

Run clang-format on src/libsync/filesystem.cpp

File src/libsync/filesystem.cpp does not conform to Custom style guidelines. (lines 29, 197, 203, 208, 209, 247, 276, 341, 342, 357, 358, 363)
* Copyright (C) by Daniel Molkentin <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
Expand All @@ -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 {

Expand Down Expand Up @@ -189,5 +194,173 @@
return false;
}

bool FileSystem::setFolderPermissions(const QString &path,

Check warning on line 197 in src/libsync/filesystem.cpp

View workflow job for this annotation

GitHub Actions / build

src/libsync/filesystem.cpp:197:18 [modernize-use-trailing-return-type]

use a trailing return type for this function

Check warning on line 197 in src/libsync/filesystem.cpp

View workflow job for this annotation

GitHub Actions / build

src/libsync/filesystem.cpp:197:39 [bugprone-easily-swappable-parameters]

2 adjacent parameters of 'setFolderPermissions' of similar type are easily swapped by mistake
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]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mgallien you could've used std::vector<char>::resize()


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);
mgallien marked this conversation as resolved.
Show resolved Hide resolved

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;
}
}

mgallien marked this conversation as resolved.
Show resolved Hide resolved
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

Check warning on line 351 in src/libsync/filesystem.cpp

View workflow job for this annotation

GitHub Actions / build

src/libsync/filesystem.cpp:351:18 [modernize-use-trailing-return-type]

use a trailing return type for this function
{
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
Loading
Loading