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());
#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;
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;
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();
#else //Q_OS_WIN
// You can not overwrite a read-only file on windows.
- if (!QFileInfo(destinationFileName).isWritable()) {
+ if (!isWritable(destinationFileName)) {
setFileReadOnly(destinationFileName, false);
}
}
#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) {
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)
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)
{
#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
#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 {
*/
void OCSYNC_EXPORT setFileHidden(const QString &filename, bool hidden);
+ bool OCSYNC_EXPORT isFileHidden(const QString &filename);
+
/**
* @brief Marks the file as read-only.
*
*/
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.
*
* 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
/**
#include "csync_exclude.h"
#include "common/utility.h"
+#include "common/filesystembase.h"
#include "../version.h"
#include <QString>
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;
}
}
}
- QFileInfo fi(filePath);
ItemType type = ItemTypeFile;
- if (fi.isDir()) {
+ if (OCC::FileSystem::isDir(filePath)) {
type = ItemTypeDirectory;
}
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 {
#include "account.h"
#include "folder.h"
+#include "common/filesystembase.h"
#include <QPushButton>
#include <QDir>
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) {
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();
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);
return false;
}
- QFileInfo info(_localVersionFilename);
- if (!info.exists()) {
+ if (!FileSystem::fileExists(_localVersionFilename)) {
return false;
}
_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);
}
// 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;
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())
QString path = folder->cleanPath() + '/';
path += serverPath.mid(folder->remotePathTrailingSlash().length());
- if (QFile::exists(path)) {
+ if (FileSystem::fileExists(path)) {
re.append(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)) {
}
}
#else
- if (!selFile.isWritable()) {
+ if (!FileSystem::isWritable(path)) {
return FolderMan::tr("You have no permission to write to the selected folder!");
}
#endif
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
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;
}
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);
}
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);
}
// 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);
#include "tray/sortedactivitylistmodel.h"
#include "tray/syncstatussummary.h"
#include "tray/unifiedsearchresultslistmodel.h"
+#include "filesystem.h"
#ifdef WITH_LIBCLOUDPROVIDERS
#include "cloudproviders/cloudprovidermanager.h"
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);
// 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();
}
!fileData.folder->accountState() ||
!fileData.folder->accountState()->account() ||
!fileData.folder->accountState()->account()->capabilities().clientSideEncryptionAvailable() ||
- !fileInfo.isDir() ||
+ !FileSystem::isDir(fileInfo.absoluteFilePath()) ||
isE2eEncryptedPath) {
return;
}
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 {
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;
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"));
}
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);
}
// 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);
#include "notificationhandler.h"
#include "usermodel.h"
+#include "common/filesystembase.h"
#include "accountmanager.h"
#include "owncloudgui.h"
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;
}
#if !defined(QT_NO_DEBUG)
QFileInfo targetFileInfo(_targetFilePath);
Q_ASSERT(targetFileInfo.isAbsolute());
- Q_ASSERT(QFileInfo::exists(_conflictFilePath));
+ Q_ASSERT(FileSystem::fileExists(_conflictFilePath));
#endif
}
if (!localEntry.isValid() &&
item->_type == ItemTypeFile &&
opts._vfs->mode() != Vfs::Off &&
- !FileSystem::isLnkFile(item->_file) &&
_pinState != PinState::AlwaysLocal &&
!FileSystem::isExcludeFile(item->_file)) {
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;
}
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 {
#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;
bool OWNCLOUDSYNC_EXPORT fileChanged(const QString &fileName,
qint64 previousSize,
time_t previousMtime);
-
/**
* @brief Like !fileChanged() but with verbose logging if the file *did* change.
*/
if (!dBresult) {
return dBresult.error();
}
+
const auto result = vfs->convertToPlaceholder(fsPath, item, {}, updateType);
if (!result) {
return result.error();
!_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);
!_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);
void PropagateDownloadFile::deleteExistingFolder()
{
QString existingDir = propagator()->fullLocalPath(_item->_file);
- if (!QFileInfo(existingDir).isDir()) {
+ if (!FileSystem::isDir(existingDir)) {
return;
}
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
}
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) {
#include "account.h"
#include "common/syncjournalfilerecord.h"
#include "filesystem.h"
+#include "common/filesystembase.h"
#include "common/asserts.h"
#include <QFileInfo>
#include <QFile>
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;
}
}
- if (!QFileInfo::exists(targetFile)) {
+ if (!FileSystem::fileExists(targetFile)) {
propagator()->_journal->commit("Remote Rename");
done(SyncFileItem::Success, {}, ErrorCategory::NoError);
return;
#include "clientsideencryption.h"
#include "foldermetadata.h"
#include "encryptedfoldermetadatahandler.h"
+#include "filesystem.h"
#include "account.h"
#include <QFileInfo>
#include <QDir>
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
\ No newline at end of file
// 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)) {
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));
// 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;
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);
}
}
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);
}
}
_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();
}
// 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);
}
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) {
} 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);
#include "hydrationjob.h"
#include "syncfileitem.h"
#include "filesystem.h"
+#include "common/filesystembase.h"
#include "common/syncjournaldb.h"
#include "config.h"
// 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;
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)) {
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;
} 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) {
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);
: (*effectivePin == *basePinState) ? *effectivePin
: PinState::Inherited;
- if (info.isDir()) {
+ if (FileSystem::isDir(fullPath)) {
const auto dirState = HydratationAndPinStates {
pinResult,
{}
{
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 *)
QVERIFY(!localFileLocked.isWritable());
}
- void testLinkFileDoesNotConvertToPlaceholder()
+ void testLinkFileDownload()
{
- // inspired by GH issue #6041
FakeFolder fakeFolder{FileInfo{}};
auto vfs = setupVfs(fakeFolder);
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()