fix(filesystem): avoid multiple directory separators when joining paths

On Windows using paths prefixed with `\\?\` to allow using long file
names require the rest of the path to be valid, a path like
`\\?\C:\Users\\jyrki` will fail in different ways...

This helps with making setting/removing permission ACLs more reliable.

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
This commit is contained in:
Jyrki Gadinger 2025-07-17 10:39:10 +02:00
parent 7d51761a7a
commit 679dcbe6dd
3 changed files with 31 additions and 2 deletions

View File

@ -323,6 +323,25 @@ bool FileSystem::openAndSeekFileSharedRead(QFile *file, QString *errorOrNull, qi
#endif
}
QString FileSystem::joinPath(const QString& path, const QString& file)
{
if (path.isEmpty()) {
qCWarning(lcFileSystem).nospace() << "joinPath called with an empty path; returning file=" << file;
return QDir::toNativeSeparators(file);
}
if (file.isEmpty()) {
qCWarning(lcFileSystem).nospace() << "joinPath called with an empty file; returning path=" << path;
return QDir::toNativeSeparators(path);
}
if (const auto lastChar = path[path.size() - 1]; lastChar == QLatin1Char{'/'} || lastChar == QLatin1Char{'\\'}) {
return QDir::toNativeSeparators(path + file);
}
return QDir::toNativeSeparators(path + QDir::separator() + file);
}
#ifdef Q_OS_WIN
std::filesystem::perms FileSystem::filePermissionsWinSymlinkSafe(const QString &filename)
{
@ -804,7 +823,7 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
const auto currentFolder = safePathFileInfo.dir();
const auto childFiles = currentFolder.entryList(QDir::Filter::Files);
for (const auto &oneEntry : childFiles) {
const auto childFile = QDir::toNativeSeparators(path + QDir::separator() + oneEntry);
const auto childFile = joinPath(path, oneEntry);
const auto &childFileStdWString = childFile.toStdWString();
const auto attributes = GetFileAttributes(childFileStdWString.c_str());

View File

@ -142,6 +142,16 @@ namespace FileSystem {
*/
bool OCSYNC_EXPORT openAndSeekFileSharedRead(QFile *file, QString *error, qint64 seek);
/**
* Returns `path + "/" + file` with native directory separators.
*
* If `path` ends in a directory separator this method will not insert another one in-between.
*
* In the case one of the parameters is empty, the other parameter will be returned with native
* directory separators and a warning is logged.
*/
QString OCSYNC_EXPORT joinPath(const QString &path, const QString &file);
#ifdef Q_OS_WIN
/**
* Returns the file system used at the given path.

View File

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