Support Windows .lnk files with VFS Cf API.

Signed-off-by: alex-z <blackslayer4@gmail.com>
This commit is contained in:
alex-z 2024-04-10 21:54:37 +02:00 committed by Matthieu Gallien
parent d5c4c7e665
commit f70d1c1079
No known key found for this signature in database
GPG key ID: 7D0F74F05C22F553
27 changed files with 311 additions and 103 deletions

View file

@ -52,8 +52,11 @@ QString FileSystem::longWinPath(const QString &inpath)
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());
@ -71,6 +74,24 @@ void FileSystem::setFileHidden(const QString &filename, bool hidden)
#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;
@ -89,11 +110,27 @@ static QFile::Permissions getDefaultWritePermissions()
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;
@ -116,6 +153,18 @@ void FileSystem::setFolderMinimumPermissions(const QString &filename)
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();
@ -193,7 +242,7 @@ bool FileSystem::uncheckedRenameReplace(const QString &originFileName,
#else //Q_OS_WIN
// You can not overwrite a read-only file on windows.
if (!QFileInfo(destinationFileName).isWritable()) {
if (!isWritable(destinationFileName)) {
setFileReadOnly(destinationFileName, false);
}
@ -289,11 +338,24 @@ bool FileSystem::openAndSeekFileSharedRead(QFile *file, QString *errorOrNull, qi
}
#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) {
@ -302,6 +364,25 @@ static bool fileExistsWin(const QString &filename)
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)
@ -323,6 +404,100 @@ bool FileSystem::fileExists(const QString &filename, const QFileInfo &fileInfo)
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)
{
@ -351,7 +526,7 @@ bool FileSystem::remove(const QString &fileName, QString *errorString)
#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

View file

@ -28,6 +28,10 @@
#include <ctime>
#if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
#include <filesystem>
#endif
class QFile;
namespace OCC {
@ -53,6 +57,8 @@ namespace FileSystem {
*/
void OCSYNC_EXPORT setFileHidden(const QString &filename, bool hidden);
bool OCSYNC_EXPORT isFileHidden(const QString &filename);
/**
* @brief Marks the file as read-only.
*
@ -89,6 +95,34 @@ namespace FileSystem {
*/
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.
*
@ -146,6 +180,9 @@ namespace FileSystem {
* 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
/**

View file

@ -31,6 +31,7 @@
#include "csync_exclude.h"
#include "common/utility.h"
#include "common/filesystembase.h"
#include "../version.h"
#include <QString>
@ -402,7 +403,7 @@ bool ExcludedFiles::isExcluded(
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;
}
@ -411,9 +412,8 @@ bool ExcludedFiles::isExcluded(
}
}
QFileInfo fi(filePath);
ItemType type = ItemTypeFile;
if (fi.isDir()) {
if (OCC::FileSystem::isDir(filePath)) {
type = ItemTypeDirectory;
}
@ -437,9 +437,7 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const QString &path, Ite
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 {

View file

@ -19,6 +19,7 @@
#include "account.h"
#include "folder.h"
#include "common/filesystembase.h"
#include <QPushButton>
#include <QDir>
@ -180,12 +181,12 @@ QString CaseClashFilenameDialog::caseClashConflictFile(const QString &conflictFi
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);
const auto currentFileName = fileInfo.fileName();
if (currentFileName.compare(conflictFileName, Qt::CaseInsensitive) == 0 &&
currentFileName != conflictFileName) {

View file

@ -80,18 +80,19 @@ bool ConflictSolver::deleteLocalVersion()
return false;
}
QFileInfo info(_localVersionFilename);
if (!info.exists()) {
if (!FileSystem::fileExists(_localVersionFilename)) {
return false;
}
const auto message = info.isDir() ? tr("Do you want to delete the directory <i>%1</i> and all its contents permanently?").arg(info.dir().dirName())
QFileInfo info(_localVersionFilename);
const auto message = FileSystem::isDir(_localVersionFilename)
? tr("Do you want to delete the directory <i>%1</i> and all its contents permanently?").arg(info.dir().dirName())
: tr("Do you want to delete the file <i>%1</i> permanently?").arg(info.fileName());
const auto result = QMessageBox::question(_parentWidget, tr("Confirm deletion"), message, QMessageBox::Yes, QMessageBox::No);
if (result != QMessageBox::Yes)
return false;
if (info.isDir()) {
if (FileSystem::isDir(_localVersionFilename)) {
return FileSystem::removeRecursively(_localVersionFilename);
} else {
return QFile(_localVersionFilename).remove();
@ -118,7 +119,7 @@ bool ConflictSolver::renameLocalVersion()
const auto targetFilename = [=] {
uint i = 1;
auto result = renamePattern.arg(i);
while (QFileInfo::exists(result)) {
while (FileSystem::fileExists(result)) {
Q_ASSERT(i > 0);
i++;
result = renamePattern.arg(i);
@ -146,8 +147,7 @@ bool ConflictSolver::overwriteRemoteVersion()
return false;
}
QFileInfo info(_localVersionFilename);
if (!info.exists()) {
if (!FileSystem::fileExists(_localVersionFilename)) {
return false;
}

View file

@ -175,17 +175,17 @@ void Folder::checkLocalPath()
_canonicalLocalPath = Utility::trailingSlashPath(_canonicalLocalPath);
if (fi.isDir() && fi.isReadable()) {
if (FileSystem::isDir(_definition.localPath) && FileSystem::isReadable(_definition.localPath)) {
qCDebug(lcFolder) << "Checked local path ok";
} else {
// Check directory again
if (!FileSystem::fileExists(_definition.localPath, fi)) {
_syncResult.appendErrorString(tr("Local folder %1 does not exist.").arg(_definition.localPath));
_syncResult.setStatus(SyncResult::SetupError);
} else if (!fi.isDir()) {
} else if (!FileSystem::isDir(_definition.localPath)) {
_syncResult.appendErrorString(tr("%1 should be a folder but is not.").arg(_definition.localPath));
_syncResult.setStatus(SyncResult::SetupError);
} else if (!fi.isReadable()) {
} else if (!FileSystem::isReadable(_definition.localPath)) {
_syncResult.appendErrorString(tr("%1 is not readable.").arg(_definition.localPath));
_syncResult.setStatus(SyncResult::SetupError);
}
@ -1471,8 +1471,9 @@ void Folder::warnOnNewExcludedItem(const SyncJournalFileRecord &record, const QS
// Note: This assumes we're getting file watcher notifications
// for folders only on creation and deletion - if we got a notification
// on content change that would create spurious warnings.
QFileInfo fi(_canonicalLocalPath + path);
if (!fi.exists())
const auto fullPath = _canonicalLocalPath + path;
QFileInfo fi(fullPath);
if (!FileSystem::fileExists(fullPath))
return;
bool ok = false;
@ -1482,7 +1483,7 @@ void Folder::warnOnNewExcludedItem(const SyncJournalFileRecord &record, const QS
if (!blacklist.contains(path + "/"))
return;
const auto message = fi.isDir()
const auto message = FileSystem::isDir(fullPath)
? tr("The folder %1 was created but was excluded from synchronization previously. "
"Data inside it will not be synchronized.")
.arg(fi.filePath())

View file

@ -1320,7 +1320,7 @@ QStringList FolderMan::findFileInLocalFolders(const QString &relPath, const Acco
QString path = folder->cleanPath() + '/';
path += serverPath.mid(folder->remotePathTrailingSlash().length());
if (QFile::exists(path)) {
if (FileSystem::fileExists(path)) {
re.append(path);
}
}
@ -1754,19 +1754,19 @@ static QString checkPathValidityRecursive(const QString &path)
#endif
const QFileInfo selFile(path);
if (!selFile.exists()) {
if (!FileSystem::fileExists(path)) {
QString parentPath = selFile.dir().path();
if (parentPath != path)
return checkPathValidityRecursive(parentPath);
return FolderMan::tr("The selected path does not exist!");
}
if (!selFile.isDir()) {
if (!FileSystem::isDir(path)) {
return FolderMan::tr("The selected path is not a folder!");
}
#ifdef Q_OS_WIN
if (!selFile.isWritable()) {
if (!FileSystem::isWritable(path)) {
// isWritable() doesn't cover all NTFS permissions
// try creating and removing a test file, and make sure it is excluded from sync
if (!Utility::canCreateFileInPath(path)) {
@ -1774,7 +1774,7 @@ static QString checkPathValidityRecursive(const QString &path)
}
}
#else
if (!selFile.isWritable()) {
if (!FileSystem::isWritable(path)) {
return FolderMan::tr("You have no permission to write to the selected folder!");
}
#endif
@ -1787,7 +1787,7 @@ static QString checkPathValidityRecursive(const QString &path)
static QString canonicalPath(const QString &path)
{
QFileInfo selFile(path);
if (!selFile.exists()) {
if (!FileSystem::fileExists(path)) {
const auto parentPath = selFile.dir().path();
// It's possible for the parentPath to match the path
@ -1880,7 +1880,7 @@ QString FolderMan::findGoodPathForNewSyncFolder(const QString &basePath, const Q
int attempt = 1;
forever {
const auto isGood = FolderMan::instance()->checkPathValidityForNewFolder(folder, serverUrl).second.isEmpty() &&
(allowExisting == GoodPathStrategy::AllowOverrideExistingPath || !QFileInfo::exists(folder));
(allowExisting == GoodPathStrategy::AllowOverrideExistingPath || !FileSystem::fileExists(folder));
if (isGood) {
break;
}

View file

@ -84,7 +84,7 @@ void FolderWatcher::appendSubPaths(QDir dir, QStringList& subPaths) {
QString path = dir.path() + "/" + newSubPaths[i];
QFileInfo fileInfo(path);
subPaths.append(path);
if (fileInfo.isDir()) {
if (FileSystem::isDir(path)) {
QDir dir(path);
appendSubPaths(dir, subPaths);
}
@ -176,9 +176,8 @@ int FolderWatcher::lockChangeDebouncingTimout() const
void FolderWatcher::changeDetected(const QString &path)
{
QFileInfo fileInfo(path);
QStringList paths(path);
if (fileInfo.isDir()) {
if (FileSystem::isDir(path)) {
QDir dir(path);
appendSubPaths(dir, paths);
}

View file

@ -152,7 +152,7 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
// and new files in a folder, probably because of the folder's mtime
// changing. We don't need them.
const bool skip = curEntry->Action == FILE_ACTION_MODIFIED
&& QFileInfo(longfile).isDir();
&& FileSystem::isDir(longfile);
if (!skip) {
emit changed(longfile);

View file

@ -40,6 +40,7 @@
#include "tray/sortedactivitylistmodel.h"
#include "tray/syncstatussummary.h"
#include "tray/unifiedsearchresultslistmodel.h"
#include "filesystem.h"
#ifdef WITH_LIBCLOUDPROVIDERS
#include "cloudproviders/cloudprovidermanager.h"
@ -513,7 +514,7 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const ProgressInfo &
Folder *f = FolderMan::instance()->folder(folder);
if (f) {
QString fullPath = f->path() + '/' + progress._lastCompletedItem._file;
if (QFile(fullPath).exists()) {
if (FileSystem::fileExists(fullPath)) {
connect(action, &QAction::triggered, this, [this, fullPath] { this->slotOpenPath(fullPath); });
} else {
action->setEnabled(false);

View file

@ -1058,8 +1058,8 @@ void SocketApi::command_MOVE_ITEM(const QString &localFile, SocketListener *)
// If the parent doesn't accept new files, go to the root of the sync folder
QFileInfo fileInfo(localFile);
const auto parentRecord = parentDir.journalRecord();
if ((fileInfo.isFile() && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddFile))
|| (fileInfo.isDir() && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories))) {
if ((FileSystem::isFile(localFile) && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddFile))
|| (FileSystem::isDir(localFile) && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories))) {
defaultDirAndName = QFileInfo(defaultDirAndName).fileName();
}
@ -1234,7 +1234,7 @@ void SocketApi::sendEncryptFolderCommandMenuEntries(const QFileInfo &fileInfo,
!fileData.folder->accountState() ||
!fileData.folder->accountState()->account() ||
!fileData.folder->accountState()->account()->capabilities().clientSideEncryptionAvailable() ||
!fileInfo.isDir() ||
!FileSystem::isDir(fileInfo.absoluteFilePath()) ||
isE2eEncryptedPath) {
return;
}
@ -1262,7 +1262,7 @@ void SocketApi::sendLockFileCommandMenuEntries(const QFileInfo &fileInfo,
const FileData &fileData,
const OCC::SocketListener* const listener) const
{
if (!fileInfo.isDir() && syncFolder->accountState()->account()->capabilities().filesLockAvailable()) {
if (!FileSystem::isDir(fileInfo.absoluteFilePath()) && syncFolder->accountState()->account()->capabilities().filesLockAvailable()) {
if (syncFolder->accountState()->account()->fileLockStatus(syncFolder->journalDb(), fileData.folderRelativePath) == SyncFileItem::LockStatus::UnlockedItem) {
listener->sendMessage(QLatin1String("MENU_ITEM:LOCK_FILE::") + tr("Lock file"));
} else {
@ -1280,7 +1280,7 @@ void SocketApi::sendLockFileInfoMenuEntries(const QFileInfo &fileInfo,
const SyncJournalFileRecord &record) const
{
static constexpr auto SECONDS_PER_MINUTE = 60;
if (!fileInfo.isDir() && syncFolder->accountState()->account()->capabilities().filesLockAvailable() &&
if (!FileSystem::isDir(fileInfo.absoluteFilePath()) && syncFolder->accountState()->account()->capabilities().filesLockAvailable() &&
syncFolder->accountState()->account()->fileLockStatus(syncFolder->journalDb(), fileData.folderRelativePath) == SyncFileItem::LockStatus::LockedItem) {
listener->sendMessage(QLatin1String("MENU_ITEM:LOCKED_FILE_OWNER:d:") + tr("Locked by %1").arg(record._lockstate._lockOwnerDisplayName));
const auto lockExpirationTime = record._lockstate._lockTime + record._lockstate._lockTimeout;
@ -1381,7 +1381,7 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
const QFileInfo fileInfo(fileData.localPath);
sendLockFileInfoMenuEntries(fileInfo, syncFolder, fileData, listener, record);
if (!fileInfo.isDir()) {
if (!FileSystem::isDir(fileData.localPath)) {
listener->sendMessage(QLatin1String("MENU_ITEM:ACTIVITY") + flagString + tr("Activity"));
}

View file

@ -42,7 +42,7 @@ void SyncRunFileLog::start(const QString &folderPath)
QString filename = logpath + QLatin1String("/") + filenameSingle + QLatin1String("_sync.log");
int depthIndex = 2;
while(QFile::exists(filename)) {
while (FileSystem::fileExists(filename)) {
QFile file(filename);
file.open(QIODevice::ReadOnly| QIODevice::Text);
@ -65,9 +65,8 @@ void SyncRunFileLog::start(const QString &folderPath)
}
// When the file is too big, just rename it to an old name.
QFileInfo info(filename);
bool exists = info.exists();
if (exists && info.size() > logfileMaxSize) {
bool exists = FileSystem::fileExists(filename);
if (exists && FileSystem::getSize(filename) > logfileMaxSize) {
exists = false;
QString newFilename = filename + QLatin1String(".1");
QFile::remove(newFilename);

View file

@ -1,5 +1,6 @@
#include "notificationhandler.h"
#include "usermodel.h"
#include "common/filesystembase.h"
#include "accountmanager.h"
#include "owncloudgui.h"
@ -576,24 +577,24 @@ void User::slotProgressInfo(const QString &folder, const ProgressInfo &progress)
continue;
}
if (activity._syncFileItemStatus == SyncFileItem::Conflict && !QFileInfo::exists(f->path() + activity._file)) {
if (activity._syncFileItemStatus == SyncFileItem::Conflict && !FileSystem::fileExists(f->path() + activity._file)) {
_activityModel->removeActivityFromActivityList(activity);
continue;
}
if (activity._syncFileItemStatus == SyncFileItem::FileLocked && !QFileInfo::exists(f->path() + activity._file)) {
if (activity._syncFileItemStatus == SyncFileItem::FileLocked && !FileSystem::fileExists(f->path() + activity._file)) {
_activityModel->removeActivityFromActivityList(activity);
continue;
}
if (activity._syncFileItemStatus == SyncFileItem::FileIgnored && !QFileInfo::exists(f->path() + activity._file)) {
if (activity._syncFileItemStatus == SyncFileItem::FileIgnored && !FileSystem::fileExists(f->path() + activity._file)) {
_activityModel->removeActivityFromActivityList(activity);
continue;
}
if (!QFileInfo::exists(f->path() + activity._file)) {
if (!FileSystem::fileExists(f->path() + activity._file)) {
_activityModel->removeActivityFromActivityList(activity);
continue;
}

View file

@ -47,7 +47,7 @@ CaseClashConflictSolver::CaseClashConflictSolver(const QString &targetFilePath,
#if !defined(QT_NO_DEBUG)
QFileInfo targetFileInfo(_targetFilePath);
Q_ASSERT(targetFileInfo.isAbsolute());
Q_ASSERT(QFileInfo::exists(_conflictFilePath));
Q_ASSERT(FileSystem::fileExists(_conflictFilePath));
#endif
}

View file

@ -606,7 +606,6 @@ void ProcessDirectoryJob::postProcessServerNew(const SyncFileItemPtr &item,
if (!localEntry.isValid() &&
item->_type == ItemTypeFile &&
opts._vfs->mode() != Vfs::Off &&
!FileSystem::isLnkFile(item->_file) &&
_pinState != PinState::AlwaysLocal &&
!FileSystem::isExcludeFile(item->_file)) {
@ -2165,7 +2164,7 @@ bool ProcessDirectoryJob::isVfsWithSuffix() const
void ProcessDirectoryJob::computePinState(PinState parentState)
{
_pinState = parentState;
if (_queryLocal != ParentDontExist && QFileInfo::exists(_discoveryData->_localDir + _currentFolder._local)) {
if (_queryLocal != ParentDontExist && FileSystem::fileExists(_discoveryData->_localDir + _currentFolder._local)) {
if (auto state = _discoveryData->_syncOptions._vfs->pinState(_currentFolder._local)) // ouch! pin local or original?
_pinState = *state;
}

View file

@ -268,7 +268,7 @@ bool FileSystem::removeRecursively(const QString &path, const std::function<void
bool removeOk = false;
// The use of isSymLink here is okay:
// we never want to go into this branch for .lnk files
bool isDir = fi.isDir() && !fi.isSymLink() && !FileSystem::isJunction(fi.absoluteFilePath());
bool isDir = FileSystem::isDir(fi.absoluteFilePath()) && !FileSystem::isSymLink(fi.absoluteFilePath()) && !FileSystem::isJunction(fi.absoluteFilePath());
if (isDir) {
removeOk = removeRecursively(path + QLatin1Char('/') + di.fileName(), onDeleted, errors); // recursive
} else {

View file

@ -24,9 +24,7 @@
#include <ctime>
#include <functional>
#if !defined(Q_OS_MACOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
#include <filesystem>
#endif
#include <functional>
class QFile;
@ -96,7 +94,6 @@ namespace FileSystem {
bool OWNCLOUDSYNC_EXPORT fileChanged(const QString &fileName,
qint64 previousSize,
time_t previousMtime);
/**
* @brief Like !fileChanged() but with verbose logging if the file *did* change.
*/

View file

@ -1070,6 +1070,7 @@ Result<Vfs::ConvertToPlaceholderResult, QString> OwncloudPropagator::staticUpdat
if (!dBresult) {
return dBresult.error();
}
const auto result = vfs->convertToPlaceholder(fsPath, item, {}, updateType);
if (!result) {
return result.error();
@ -1460,13 +1461,13 @@ void PropagateDirectory::slotSubJobsFinished(SyncFileItem::Status status)
!_item->_remotePerm.hasPermission(RemotePermissions::CanMove) &&
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories)) {
try {
if (QFileInfo::exists(propagator()->fullLocalPath(_item->_file))) {
if (FileSystem::fileExists(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))) {
if (!_item->_renameTarget.isEmpty() && FileSystem::fileExists(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);
@ -1485,13 +1486,13 @@ void PropagateDirectory::slotSubJobsFinished(SyncFileItem::Status status)
!_item->_remotePerm.hasPermission(RemotePermissions::CanMove) ||
!_item->_remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories))) {
try {
if (QFileInfo::exists(propagator()->fullLocalPath(_item->_file))) {
if (FileSystem::fileExists(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))) {
if (!_item->_renameTarget.isEmpty() && FileSystem::fileExists(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);

View file

@ -1007,7 +1007,7 @@ void PropagateDownloadFile::checksumValidateFailedAbortDownload(const QString &e
void PropagateDownloadFile::deleteExistingFolder()
{
QString existingDir = propagator()->fullLocalPath(_item->_file);
if (!QFileInfo(existingDir).isDir()) {
if (!FileSystem::isDir(existingDir)) {
return;
}
@ -1204,9 +1204,17 @@ void PropagateDownloadFile::downloadFinished()
if (previousFileExists) {
// Preserve the existing file permissions.
const auto existingFile = QFileInfo{filename};
#ifdef Q_OS_WIN
const auto existingPermissions = FileSystem::filePermissionsWin(filename);
const auto tmpFilePermissions = FileSystem::filePermissionsWin(_tmpFile.fileName());
if (existingPermissions != tmpFilePermissions) {
FileSystem::setFilePermissionsWin(_tmpFile.fileName(), existingPermissions);
}
#else
if (existingFile.permissions() != _tmpFile.permissions()) {
_tmpFile.setPermissions(existingFile.permissions());
}
#endif
preserveGroupOwnership(_tmpFile.fileName(), existingFile);
// Make the file a hydrated placeholder if possible
@ -1228,7 +1236,7 @@ void PropagateDownloadFile::downloadFinished()
}
const auto isConflict = (_item->_instruction == CSYNC_INSTRUCTION_CONFLICT
&& (QFileInfo(filename).isDir() || !FileSystem::fileEquals(filename, _tmpFile.fileName()))) ||
&& (FileSystem::isDir(filename) || !FileSystem::fileEquals(filename, _tmpFile.fileName()))) ||
_item->_instruction == CSYNC_INSTRUCTION_CASE_CLASH_CONFLICT;
if (isConflict) {

View file

@ -18,6 +18,7 @@
#include "account.h"
#include "common/syncjournalfilerecord.h"
#include "filesystem.h"
#include "common/filesystembase.h"
#include "common/asserts.h"
#include <QFileInfo>
#include <QFile>
@ -253,7 +254,7 @@ void PropagateRemoteMove::finalize()
const auto targetFile = propagator()->fullLocalPath(_item->_renameTarget);
if (QFileInfo::exists(targetFile)) {
if (FileSystem::fileExists(targetFile)) {
// Delete old db data.
if (!propagator()->_journal->deleteFileRecord(_item->_originalFile)) {
qCWarning(lcPropagateRemoteMove) << "could not delete file from local DB" << _item->_originalFile;
@ -277,7 +278,7 @@ void PropagateRemoteMove::finalize()
}
}
if (!QFileInfo::exists(targetFile)) {
if (!FileSystem::fileExists(targetFile)) {
propagator()->_journal->commit("Remote Rename");
done(SyncFileItem::Success, {}, ErrorCategory::NoError);
return;

View file

@ -4,6 +4,7 @@
#include "clientsideencryption.h"
#include "foldermetadata.h"
#include "encryptedfoldermetadatahandler.h"
#include "filesystem.h"
#include "account.h"
#include <QFileInfo>
#include <QDir>
@ -182,7 +183,7 @@ void PropagateUploadEncrypted::slotUploadMetadataFinished(int statusCode, const
qCDebug(lcPropagateUploadEncrypted) << "Finalizing the upload part, now the actuall uploader will take over";
emit finalized(Utility::trailingSlashPath(outputInfo.path()) + outputInfo.fileName(),
Utility::trailingSlashPath(_remoteParentPath) + outputInfo.fileName(),
outputInfo.size());
FileSystem::getSize(_completeFileName));
}
} // namespace OCC

View file

@ -163,8 +163,7 @@ void PropagateLocalMkdir::startLocalMkdir()
// When turning something that used to be a file into a directory
// we need to delete the file first.
QFileInfo fi(newDirStr);
if (fi.exists() && fi.isFile()) {
if (FileSystem::fileExists(newDirStr) && FileSystem::isFile(newDirStr)) {
if (_deleteExistingFile) {
QString removeError;
if (!FileSystem::remove(newDirStr, &removeError)) {
@ -282,7 +281,7 @@ void PropagateLocalRename::start()
const auto existingFile = propagator()->fullLocalPath(previousNameInDb);
const auto targetFile = propagator()->fullLocalPath(_item->_renameTarget);
const auto fileAlreadyMoved = !QFileInfo::exists(propagator()->fullLocalPath(_item->_originalFile)) && QFileInfo::exists(existingFile);
const auto fileAlreadyMoved = !FileSystem::fileExists(propagator()->fullLocalPath(_item->_originalFile)) && FileSystem::fileExists(existingFile);
auto pinState = OCC::PinState::Unspecified;
if (!fileAlreadyMoved) {
auto pinStateResult = vfs->pinState(propagator()->adjustRenamedPath(_item->_file));
@ -294,7 +293,7 @@ void PropagateLocalRename::start()
// if the file is a file underneath a moved dir, the _item->file is equal
// to _item->renameTarget and the file is not moved as a result.
qCDebug(lcPropagateLocalRename) << _item->_file << _item->_renameTarget << _item->_originalFile << previousNameInDb << (fileAlreadyMoved ? "original file has already moved" : "original file is still there");
Q_ASSERT(QFileInfo::exists(propagator()->fullLocalPath(_item->_originalFile)) || QFileInfo::exists(existingFile));
Q_ASSERT(FileSystem::fileExists(propagator()->fullLocalPath(_item->_originalFile)) || FileSystem::fileExists(existingFile));
if (_item->_file != _item->_renameTarget) {
propagator()->reportProgress(*_item, 0);
qCDebug(lcPropagateLocalRename) << "MOVE " << existingFile << " => " << targetFile;

View file

@ -287,7 +287,7 @@ void SyncEngine::conflictRecordMaintenance()
const auto conflictRecordPaths = _journal->conflictRecordPaths();
for (const auto &path : conflictRecordPaths) {
auto fsPath = _propagator->fullLocalPath(QString::fromUtf8(path));
if (!QFileInfo::exists(fsPath)) {
if (!FileSystem::fileExists(fsPath)) {
_journal->deleteConflictRecord(path);
}
}
@ -326,7 +326,7 @@ void SyncEngine::caseClashConflictRecordMaintenance()
const auto conflictRecordPaths = _journal->caseClashConflictRecordPaths();
for (const auto &path : conflictRecordPaths) {
const auto fsPath = _propagator->fullLocalPath(QString::fromUtf8(path));
if (!QFileInfo::exists(fsPath)) {
if (!FileSystem::fileExists(fsPath)) {
_journal->deleteCaseClashConflictByPathRecord(path);
}
}
@ -640,7 +640,7 @@ void SyncEngine::startSync()
_discoveryPhase->_account = _account;
_discoveryPhase->_excludes = _excludedFiles.data();
const QString excludeFilePath = _localPath + QStringLiteral(".sync-exclude.lst");
if (QFile::exists(excludeFilePath)) {
if (FileSystem::fileExists(excludeFilePath)) {
_discoveryPhase->_excludes->addExcludeFilePath(excludeFilePath);
_discoveryPhase->_excludes->reloadExcludeFiles();
}
@ -1237,7 +1237,7 @@ void SyncEngine::wipeVirtualFiles(const QString &localPath, SyncJournalDb &journ
// If the local file is a dehydrated placeholder, wipe it too.
// Otherwise leave it to allow the next sync to have a new-new conflict.
QString localFile = localPath + rec._path;
if (QFile::exists(localFile) && vfs.isDehydratedPlaceholder(localFile)) {
if (FileSystem::fileExists(localFile) && vfs.isDehydratedPlaceholder(localFile)) {
qCDebug(lcEngine) << "Removing local dehydrated placeholder" << rec.path();
QFile::remove(localFile);
}

View file

@ -784,12 +784,11 @@ OCC::CfApiWrapper::FileHandle OCC::CfApiWrapper::handleForPath(const QString &pa
return {};
}
QFileInfo pathFileInfo(path);
if (!pathFileInfo.exists()) {
if (!FileSystem::fileExists(path)) {
return {};
}
if (pathFileInfo.isDir()) {
if (FileSystem::isDir(path)) {
HANDLE handle = nullptr;
const qint64 openResult = CfOpenFileWithOplock(path.toStdWString().data(), CF_OPEN_FILE_FLAG_NONE, &handle);
if (openResult == S_OK) {
@ -797,7 +796,7 @@ OCC::CfApiWrapper::FileHandle OCC::CfApiWrapper::handleForPath(const QString &pa
} else {
qCWarning(lcCfApiWrapper) << "Could not open handle for " << path << " result: " << QString::fromWCharArray(_com_error(openResult).ErrorMessage());
}
} else if (pathFileInfo.isFile()) {
} else if (FileSystem::isFile(path)) {
const auto longpath = OCC::FileSystem::longWinPath(path);
const auto handle = CreateFile(longpath.toStdWString().data(), 0, 0, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);

View file

@ -21,6 +21,7 @@
#include "hydrationjob.h"
#include "syncfileitem.h"
#include "filesystem.h"
#include "common/filesystembase.h"
#include "common/syncjournaldb.h"
#include "config.h"
@ -48,7 +49,7 @@ bool registerShellExtension()
// assume CFAPI_SHELL_EXTENSIONS_LIB_NAME is always in the same folder as the main executable
// assume CFAPI_SHELL_EXTENSIONS_LIB_NAME is always in the same folder as the main executable
const auto shellExtensionDllPath = QDir::toNativeSeparators(QString(QCoreApplication::applicationDirPath() + QStringLiteral("/") + CFAPI_SHELL_EXTENSIONS_LIB_NAME + QStringLiteral(".dll")));
if (!QFileInfo::exists(shellExtensionDllPath)) {
if (!OCC::FileSystem::fileExists(shellExtensionDllPath)) {
Q_ASSERT(false);
qCWarning(lcCfApi) << "Register CfAPI shell extensions failed. Dll does not exist in " << QCoreApplication::applicationDirPath();
return false;
@ -236,16 +237,6 @@ Result<void, QString> VfsCfApi::dehydratePlaceholder(const SyncFileItem &item)
Result<Vfs::ConvertToPlaceholderResult, QString> VfsCfApi::convertToPlaceholder(const QString &filename, const SyncFileItem &item, const QString &replacesFile, UpdateMetadataTypes updateType)
{
const auto localPath = QDir::toNativeSeparators(filename);
if (item._type != ItemTypeDirectory && OCC::FileSystem::isLnkFile(filename)) {
qCInfo(lcCfApi) << "File \"" << filename << "\" is a Windows shortcut. Not converting it to a placeholder.";
const auto pinState = pinStateLocal(localPath);
if (!pinState || *pinState != PinState::Excluded) {
setPinStateLocal(localPath, PinState::Excluded);
}
return Vfs::ConvertToPlaceholderResult::Ok;
}
const auto replacesPath = QDir::toNativeSeparators(replacesFile);
if (cfapi::findPlaceholderInfo(localPath)) {
@ -281,8 +272,6 @@ bool VfsCfApi::statTypeVirtualFile(csync_file_stat_t *stat, void *statData)
const auto hasReparsePoint = (ffd->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
const auto hasCloudTag = hasReparsePoint && (ffd->dwReserved0 & ~IO_REPARSE_TAG_CLOUD_MASK) == (IO_REPARSE_TAG_CLOUD & ~IO_REPARSE_TAG_CLOUD_MASK);
const auto isWindowsShortcut = !isDirectory && FileSystem::isLnkFile(stat->path);
const auto isExcludeFile = !isDirectory && FileSystem::isExcludeFile(stat->path);
stat->is_metadata_missing = !hasCloudTag;
@ -298,7 +287,7 @@ bool VfsCfApi::statTypeVirtualFile(csync_file_stat_t *stat, void *statData)
} else if (isSparseFile && isPinned) {
stat->type = ItemTypeVirtualFileDownload;
return true;
} else if (!isSparseFile && isUnpinned && !isWindowsShortcut && !isExcludeFile) {
} else if (!isSparseFile && isUnpinned && !isExcludeFile) {
stat->type = ItemTypeVirtualFileDehydration;
return true;
} else if (isSparseFile) {
@ -522,9 +511,10 @@ int VfsCfApi::finalizeHydrationJob(const QString &requestId)
VfsCfApi::HydratationAndPinStates VfsCfApi::computeRecursiveHydrationAndPinStates(const QString &folderPath, const Optional<PinState> &basePinState)
{
Q_ASSERT(!folderPath.endsWith('/'));
const auto fullPath = params().filesystemPath + folderPath;
QFileInfo info(params().filesystemPath + folderPath);
if (!info.exists()) {
if (!FileSystem::fileExists(fullPath)) {
return {};
}
const auto effectivePin = pinState(folderPath);
@ -533,7 +523,7 @@ VfsCfApi::HydratationAndPinStates VfsCfApi::computeRecursiveHydrationAndPinState
: (*effectivePin == *basePinState) ? *effectivePin
: PinState::Inherited;
if (info.isDir()) {
if (FileSystem::isDir(fullPath)) {
const auto dirState = HydratationAndPinStates {
pinResult,
{}

View file

@ -150,8 +150,7 @@ bool VfsSuffix::isDehydratedPlaceholder(const QString &filePath)
{
if (!filePath.endsWith(fileSuffix()))
return false;
QFileInfo fi(filePath);
return fi.exists() && fi.size() == 1;
return FileSystem::fileExists(filePath) && FileSystem::getSize(filePath) == 1;
}
bool VfsSuffix::statTypeVirtualFile(csync_file_stat_t *stat, void *)

View file

@ -1363,9 +1363,8 @@ private slots:
QVERIFY(!localFileLocked.isWritable());
}
void testLinkFileDoesNotConvertToPlaceholder()
void testLinkFileDownload()
{
// inspired by GH issue #6041
FakeFolder fakeFolder{FileInfo{}};
auto vfs = setupVfs(fakeFolder);
@ -1375,8 +1374,11 @@ private slots:
QVERIFY(fakeFolder.syncOnce());
ItemCompletedSpy completeSpy(fakeFolder);
QVERIFY(fakeFolder.syncOnce());
QVERIFY(!vfs->pinState("linkfile.lnk").isValid() || vfs->pinState("linkfile.lnk").get() == PinState::Excluded);
QVERIFY(vfs->pinState("linkfile.lnk").isValid());
QVERIFY(itemInstruction(completeSpy, "linkfile.lnk", CSYNC_INSTRUCTION_NONE));
triggerDownload(fakeFolder, "linkfile.lnk");
QVERIFY(fakeFolder.syncOnce());
QVERIFY(itemInstruction(completeSpy, "linkfile.lnk", CSYNC_INSTRUCTION_SYNC));
}
void testFolderDoesNotUpdatePlaceholderMetadata()