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

Support Windows .lnk files with VFS Cf API. #6629

Merged
merged 3 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 181 additions & 6 deletions src/common/filesystembase.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*

Check notice on line 1 in src/common/filesystembase.cpp

View workflow job for this annotation

GitHub Actions / build

Run clang-format on src/common/filesystembase.cpp

File src/common/filesystembase.cpp does not conform to Custom style guidelines. (lines 113)
* Copyright (C) by Daniel Molkentin <[email protected]>
*
* This library is free software; you can redistribute it and/or
Expand Down Expand Up @@ -52,8 +52,11 @@

void FileSystem::setFileHidden(const QString &filename, bool hidden)
{
if (filename.isEmpty()) {
return;
}
#ifdef _WIN32
QString fName = longWinPath(filename);
const QString fName = longWinPath(filename);
DWORD dwAttrs = 0;

dwAttrs = GetFileAttributesW((wchar_t *)fName.utf16());
Expand All @@ -71,6 +74,24 @@
#endif
}

bool FileSystem::isFileHidden(const QString &filename)
{
#ifdef _WIN32
if (isLnkFile(filename)) {
const QString fName = longWinPath(filename);
DWORD dwAttrs = 0;

dwAttrs = GetFileAttributesW((wchar_t *)fName.utf16());

if (dwAttrs == INVALID_FILE_ATTRIBUTES) {
return false;
}
return dwAttrs & FILE_ATTRIBUTE_HIDDEN;
}
#endif
return QFileInfo(filename).isHidden();
}

static QFile::Permissions getDefaultWritePermissions()
{
QFile::Permissions result = QFile::WriteUser;
Expand All @@ -89,11 +110,27 @@

void FileSystem::setFileReadOnly(const QString &filename, bool readonly)
{
#ifdef Q_OS_WIN
if (isLnkFile(filename)) {
if (!fileExists(filename)) {
return;
}
const auto permissions = filePermissionsWin(filename);

std::filesystem::perms allWritePermissions = std::filesystem::perms::_All_write;
static std::filesystem::perms defaultWritePermissions = std::filesystem::perms::others_write;

std::filesystem::permissions(filename.toStdString(), allWritePermissions, std::filesystem::perm_options::remove);

if (!readonly) {
std::filesystem::permissions(filename.toStdString(), defaultWritePermissions, std::filesystem::perm_options::add);
}
}
#endif
QFile file(filename);
QFile::Permissions permissions = file.permissions();

QFile::Permissions allWritePermissions =
QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther | QFile::WriteOwner;
QFile::Permissions allWritePermissions = QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther | QFile::WriteOwner;
static QFile::Permissions defaultWritePermissions = getDefaultWritePermissions();

permissions &= ~allWritePermissions;
Expand All @@ -116,6 +153,18 @@

bool FileSystem::setFileReadOnlyWeak(const QString &filename, bool readonly)
{
#ifdef Q_OS_WIN
if (isLnkFile(filename)) {
const auto permissions = filePermissionsWin(filename);

if (!readonly && static_cast<bool>((permissions & std::filesystem::perms::owner_write))) {
return false; // already writable enough
}

setFileReadOnly(filename, readonly);
return true;
}
#endif
QFile file(filename);
QFile::Permissions permissions = file.permissions();

Expand Down Expand Up @@ -193,7 +242,7 @@

#else //Q_OS_WIN
// You can not overwrite a read-only file on windows.
if (!QFileInfo(destinationFileName).isWritable()) {
if (!isWritable(destinationFileName)) {
setFileReadOnly(destinationFileName, false);
}

Expand Down Expand Up @@ -289,11 +338,24 @@
}

#ifdef Q_OS_WIN
std::filesystem::perms FileSystem::filePermissionsWin(const QString &filename)
{
return std::filesystem::status(filename.toStdString()).permissions();
}

void FileSystem::setFilePermissionsWin(const QString &filename, const std::filesystem::perms &perms)
{
if (!fileExists(filename)) {
return;
}
std::filesystem::permissions(filename.toStdString(), perms);
}

static bool fileExistsWin(const QString &filename)
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind = nullptr;
QString fName = FileSystem::longWinPath(filename);
const QString fName = FileSystem::longWinPath(filename);

hFind = FindFirstFileW((wchar_t *)fName.utf16(), &FindFileData);
if (hFind == INVALID_HANDLE_VALUE) {
Expand All @@ -302,6 +364,25 @@
FindClose(hFind);
return true;
}

static bool isDirWin(const QString &filename)
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind = nullptr;
const QString fName = FileSystem::longWinPath(filename);

hFind = FindFirstFileW((wchar_t *)fName.utf16(), &FindFileData);
if (hFind == INVALID_HANDLE_VALUE) {
return false;
}
FindClose(hFind);
return FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
}

static bool isFileWin(const QString &filename)
{
return !isDirWin(filename);
}
#endif

bool FileSystem::fileExists(const QString &filename, const QFileInfo &fileInfo)
Expand All @@ -323,6 +404,100 @@
return re;
}

bool FileSystem::isDir(const QString &filename, const QFileInfo &fileInfo)
{
#ifdef Q_OS_WIN
if (isLnkFile(filename)) {
// Use a native check.
return isDirWin(filename);
}
#endif
bool re = fileInfo.isDir();
// if the filename is different from the filename in fileInfo, the fileInfo is
// not valid. There needs to be one initialised here. Otherwise the incoming
// fileInfo is re-used.
if (fileInfo.filePath() != filename) {
QFileInfo myFI(filename);
re = myFI.isDir();
}
return re;
}

bool FileSystem::isFile(const QString &filename, const QFileInfo &fileInfo)
{
#ifdef Q_OS_WIN
if (isLnkFile(filename)) {
// Use a native check.
return isFileWin(filename);
}
#endif
bool re = fileInfo.isDir();
// if the filename is different from the filename in fileInfo, the fileInfo is
// not valid. There needs to be one initialised here. Otherwise the incoming
// fileInfo is re-used.
if (fileInfo.filePath() != filename) {
QFileInfo myFI(filename);
re = myFI.isFile();
}
return re;
}

bool FileSystem::isWritable(const QString &filename, const QFileInfo &fileInfo)
{
#ifdef Q_OS_WIN
if (isLnkFile(filename)) {
const auto permissions = filePermissionsWin(filename);
return static_cast<bool>((permissions & std::filesystem::perms::owner_write));
}
#endif
bool re = fileInfo.isWritable();
// if the filename is different from the filename in fileInfo, the fileInfo is
// not valid. There needs to be one initialised here. Otherwise the incoming
// fileInfo is re-used.
if (fileInfo.filePath() != filename) {
QFileInfo myFI(filename);
re = myFI.isWritable();
}
return re;
}

bool FileSystem::isReadable(const QString &filename, const QFileInfo &fileInfo)
{
#ifdef Q_OS_WIN
if (isLnkFile(filename)) {
const auto permissions = filePermissionsWin(filename);
return static_cast<bool>((permissions & std::filesystem::perms::owner_read));
}
#endif
bool re = fileInfo.isReadable();
// if the filename is different from the filename in fileInfo, the fileInfo is
// not valid. There needs to be one initialised here. Otherwise the incoming
// fileInfo is re-used.
if (fileInfo.filePath() != filename) {
QFileInfo myFI(filename);
re = myFI.isReadable();
}
return re;
}

bool FileSystem::isSymLink(const QString &filename, const QFileInfo &fileInfo)
{
#ifdef Q_OS_WIN
if (isLnkFile(filename)) {
return isJunction(filename);
}
#endif
bool re = fileInfo.isSymLink();
// if the filename is different from the filename in fileInfo, the fileInfo is
// not valid. There needs to be one initialised here. Otherwise the incoming
// fileInfo is re-used.
if (fileInfo.filePath() != filename) {
QFileInfo myFI(filename);
re = myFI.isSymLink();
}
return re;
}

#ifdef Q_OS_WIN
QString FileSystem::fileSystemForPath(const QString &path)
{
Expand Down Expand Up @@ -351,7 +526,7 @@
#ifdef Q_OS_WIN
// You cannot delete a read-only file on windows, but we want to
// allow that.
if (!QFileInfo(fileName).isWritable()) {
if (!isWritable(fileName)) {
setFileReadOnly(fileName, false);
}
#endif
Expand Down
37 changes: 37 additions & 0 deletions src/common/filesystembase.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*

Check notice on line 1 in src/common/filesystembase.h

View workflow job for this annotation

GitHub Actions / build

Run clang-format on src/common/filesystembase.h

File src/common/filesystembase.h does not conform to Custom style guidelines. (lines 104, 112)
* Copyright (C) by Olivier Goffart <[email protected]>
*
* This library is free software; you can redistribute it and/or
Expand All @@ -18,7 +18,7 @@

#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"

Expand All @@ -28,7 +28,11 @@

#include <ctime>

#if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
#include <filesystem>
#endif

class QFile;

Check warning on line 35 in src/common/filesystembase.h

View workflow job for this annotation

GitHub Actions / build

src/common/filesystembase.h:35:7 [cppcoreguidelines-avoid-non-const-global-variables]

variable 'QFile' is non-const and globally accessible, consider making it const

namespace OCC {

Expand All @@ -53,6 +57,8 @@
*/
void OCSYNC_EXPORT setFileHidden(const QString &filename, bool hidden);

bool OCSYNC_EXPORT isFileHidden(const QString &filename);

/**
* @brief Marks the file as read-only.
*
Expand Down Expand Up @@ -89,6 +95,34 @@
*/
bool OCSYNC_EXPORT fileExists(const QString &filename, const QFileInfo & = QFileInfo());

/**
* @brief Checks whether it is a dir.
*
* Use this over QFileInfo::isDir() and QFile::isDir() to avoid bugs with lnk
* files, see above.
*/
bool OCSYNC_EXPORT isDir(const QString &filename, const QFileInfo& = QFileInfo());

/**
* @brief Checks whether it is a file.
*
* Use this over QFileInfo::isDir() and QFile::isDir() to avoid bugs with lnk
* files, see above.
*/
bool OCSYNC_EXPORT isFile(const QString &filename, const QFileInfo& fileInfo = QFileInfo());

/**
* @brief Checks whether the file is writable.
*
* Use this over QFileInfo::isDir() and QFile::isDir() to avoid bugs with lnk
* files, see above.
*/
bool OCSYNC_EXPORT isWritable(const QString &filename, const QFileInfo &fileInfo = QFileInfo());

bool OCSYNC_EXPORT isReadable(const QString &filename, const QFileInfo &fileInfo = QFileInfo());

bool OCSYNC_EXPORT isSymLink(const QString &filename, const QFileInfo &fileInfo = QFileInfo());

/**
* @brief Rename the file \a originFileName to \a destinationFileName.
*
Expand Down Expand Up @@ -146,6 +180,9 @@
* the windows API functions work with the normal "unixoid" representation too.
*/
QString OCSYNC_EXPORT pathtoUNC(const QString &str);

std::filesystem::perms OCSYNC_EXPORT filePermissionsWin(const QString &filename);
void OCSYNC_EXPORT setFilePermissionsWin(const QString &filename, const std::filesystem::perms &perms);
#endif

/**
Expand Down
10 changes: 4 additions & 6 deletions src/csync/csync_exclude.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "config_csync.h"

Check failure on line 21 in src/csync/csync_exclude.cpp

View workflow job for this annotation

GitHub Actions / build

src/csync/csync_exclude.cpp:21:10 [clang-diagnostic-error]

'config_csync.h' file not found
#include <qglobal.h>

#ifndef _GNU_SOURCE
Expand All @@ -31,6 +31,7 @@
#include "csync_exclude.h"

#include "common/utility.h"
#include "common/filesystembase.h"
#include "../version.h"

#include <QString>
Expand Down Expand Up @@ -402,7 +403,7 @@
while (path.size() > basePath.size()) {
QFileInfo fi(path);
if (fi.fileName() != QStringLiteral(".sync-exclude.lst")
&& (fi.isHidden() || fi.fileName().startsWith(QLatin1Char('.')))) {
&& (FileSystem::isFileHidden(path) || fi.fileName().startsWith(QLatin1Char('.')))) {
return true;
}

Expand All @@ -411,9 +412,8 @@
}
}

QFileInfo fi(filePath);
ItemType type = ItemTypeFile;
if (fi.isDir()) {
if (OCC::FileSystem::isDir(filePath)) {
type = ItemTypeDirectory;
}

Expand All @@ -437,9 +437,7 @@
if (filetype == ItemTypeDirectory) {
const auto basePath = QString(_localPath + path + QLatin1Char('/'));
const QString absolutePath = basePath + QStringLiteral(".sync-exclude.lst");
QFileInfo excludeFileInfo(absolutePath);

if (excludeFileInfo.isReadable()) {
if (FileSystem::isReadable(absolutePath)) {
addExcludeFilePath(absolutePath);
reloadExcludeFiles();
} else {
Expand Down
5 changes: 3 additions & 2 deletions src/gui/caseclashfilenamedialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "account.h"
#include "folder.h"
#include "common/filesystembase.h"

#include <QPushButton>
#include <QDir>
Expand Down Expand Up @@ -180,12 +181,12 @@
while(it.hasNext()) {
const auto filePath = it.next();
qCDebug(lcCaseClashConflictFialog) << filePath;
QFileInfo fileInfo(filePath);

if(fileInfo.isDir()) {
if (FileSystem::isDir(filePath)) {
continue;
}

QFileInfo fileInfo(filePath);

Check warning on line 189 in src/gui/caseclashfilenamedialog.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/caseclashfilenamedialog.cpp:189:19 [cppcoreguidelines-init-variables]

variable 'fileInfo' is not initialized
const auto currentFileName = fileInfo.fileName();
if (currentFileName.compare(conflictFileName, Qt::CaseInsensitive) == 0 &&
currentFileName != conflictFileName) {
Expand Down
Loading
Loading