diff options
Diffstat (limited to 'src/corelib/io/qfsfileengine_win.cpp')
-rw-r--r-- | src/corelib/io/qfsfileengine_win.cpp | 231 |
1 files changed, 106 insertions, 125 deletions
diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp index 84ba42f55a..4ac305f49b 100644 --- a/src/corelib/io/qfsfileengine_win.cpp +++ b/src/corelib/io/qfsfileengine_win.cpp @@ -1,44 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qplatformdefs.h" #include "private/qabstractfileengine_p.h" +#include "private/qfiledevice_p.h" #include "private/qfsfileengine_p.h" #include "qfilesystemengine_p.h" #include <qdebug.h> @@ -62,17 +27,21 @@ #define SECURITY_WIN32 #include <security.h> +#include <memory> + #ifndef PATH_MAX #define PATH_MAX FILENAME_MAX #endif QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + static inline bool isUncPath(const QString &path) { // Starts with \\, but not \\. - return (path.startsWith(QLatin1String("\\\\")) - && path.size() > 2 && path.at(2) != QLatin1Char('.')); + return (path.startsWith("\\\\"_L1) + && path.size() > 2 && path.at(2) != u'.'); } /*! @@ -80,13 +49,13 @@ static inline bool isUncPath(const QString &path) */ QString QFSFileEnginePrivate::longFileName(const QString &path) { - if (path.startsWith(QLatin1String("\\\\.\\"))) + if (path.startsWith("\\\\.\\"_L1)) return path; QString absPath = QFileSystemEngine::nativeAbsoluteFilePath(path); - QString prefix = QLatin1String("\\\\?\\"); + QString prefix = "\\\\?\\"_L1; if (isUncPath(absPath)) { - prefix.append(QLatin1String("UNC\\")); // "\\\\?\\UNC\\" + prefix.append("UNC\\"_L1); // "\\\\?\\UNC\\" absPath.remove(0, 2); } return prefix + absPath; @@ -95,7 +64,8 @@ QString QFSFileEnginePrivate::longFileName(const QString &path) /* \internal */ -bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) +bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode, + std::optional<QFile::Permissions> permissions) { Q_Q(QFSFileEngine); @@ -115,11 +85,14 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) ? OPEN_ALWAYS : OPEN_EXISTING; // Create the file handle. - SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE }; + QNativeFilePermissions nativePermissions(permissions, false); + if (!nativePermissions.isOk()) + return false; + fileHandle = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(), accessRights, shareMode, - &securityAtts, + nativePermissions.securityAttributes(), creationDisp, FILE_ATTRIBUTE_NORMAL, NULL); @@ -214,7 +187,7 @@ qint64 QFSFileEnginePrivate::nativeSize() const // ### Don't flush; for buffered files, we should get away with ftell. thatQ->flush(); - // Always retrive the current information + // Always retrieve the current information metaData.clearFlags(QFileSystemMetaData::SizeAttribute); bool filled = false; if (fileHandle != INVALID_HANDLE_VALUE && openMode != QIODevice::NotOpen ) @@ -423,6 +396,35 @@ bool QFSFileEnginePrivate::nativeIsSequential() const || (fileType == FILE_TYPE_PIPE); } +bool QFSFileEnginePrivate::nativeRenameOverwrite(const QFileSystemEntry &newEntry) +{ + if (fileHandle == INVALID_HANDLE_VALUE) + return false; + const QString newFilePath = newEntry.nativeFilePath(); + const size_t nameByteLength = newFilePath.length() * sizeof(wchar_t); + if (nameByteLength + sizeof(wchar_t) > std::numeric_limits<DWORD>::max()) + return false; + + constexpr size_t RenameInfoSize = sizeof(FILE_RENAME_INFO); + const size_t renameDataSize = RenameInfoSize + nameByteLength + sizeof(wchar_t); + QVarLengthArray<char> v(qsizetype(renameDataSize), 0); + + auto *renameInfo = q20::construct_at(reinterpret_cast<FILE_RENAME_INFO *>(v.data())); + auto renameInfoRAII = qScopeGuard([&] { std::destroy_at(renameInfo); }); + renameInfo->ReplaceIfExists = TRUE; + renameInfo->RootDirectory = nullptr; + renameInfo->FileNameLength = DWORD(nameByteLength); + memcpy(renameInfo->FileName, newFilePath.data(), nameByteLength); + + bool res = SetFileInformationByHandle(fileHandle, FileRenameInfo, renameInfo, + DWORD(renameDataSize)); + if (!res) { + DWORD error = GetLastError(); + q_func()->setError(QFile::RenameError, qt_error_string(int(error))); + } + return res; +} + bool QFSFileEngine::caseSensitive() const { return false; @@ -433,7 +435,7 @@ QString QFSFileEngine::currentPath(const QString &fileName) QString ret; //if filename is a drive: then get the pwd of that drive if (fileName.length() >= 2 && - fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':')) { + fileName.at(0).isLetter() && fileName.at(1) == u':') { int drv = fileName.toUpper().at(0).toLatin1() - 'A' + 1; if (_getdrive() != drv) { wchar_t buf[PATH_MAX]; @@ -445,7 +447,7 @@ QString QFSFileEngine::currentPath(const QString &fileName) //just the pwd ret = QFileSystemEngine::currentPath().filePath(); } - if (ret.length() >= 2 && ret[1] == QLatin1Char(':')) + if (ret.length() >= 2 && ret[1] == u':') ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters. return ret; } @@ -494,52 +496,14 @@ bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) cons return metaData.exists(); } - +// ### assume that they add .lnk to newName bool QFSFileEngine::link(const QString &newName) { - bool ret = false; - - QString linkName = newName; - //### assume that they add .lnk - - IShellLink *psl; - bool neededCoInit = false; - - HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, - reinterpret_cast<void **>(&psl)); - - if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized - neededCoInit = true; - CoInitialize(nullptr); - hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, - reinterpret_cast<void **>(&psl)); - } - - if (SUCCEEDED(hres)) { - const QString nativeAbsoluteName = fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\')); - hres = psl->SetPath(reinterpret_cast<const wchar_t *>(nativeAbsoluteName.utf16())); - if (SUCCEEDED(hres)) { - const QString nativeAbsolutePathName = fileName(AbsolutePathName).replace(QLatin1Char('/'), QLatin1Char('\\')); - hres = psl->SetWorkingDirectory(reinterpret_cast<const wchar_t *>(nativeAbsolutePathName.utf16())); - if (SUCCEEDED(hres)) { - IPersistFile *ppf; - hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void **>(&ppf)); - if (SUCCEEDED(hres)) { - hres = ppf->Save(reinterpret_cast<const wchar_t *>(linkName.utf16()), TRUE); - if (SUCCEEDED(hres)) - ret = true; - ppf->Release(); - } - } - } - psl->Release(); - } + QSystemError error; + bool ret = QFileSystemEngine::createLink(QFileSystemEntry(fileName(AbsoluteName)), + QFileSystemEntry(newName), error); if (!ret) - setError(QFile::RenameError, qt_error_string()); - - if (neededCoInit) - CoUninitialize(); - + setError(QFile::RenameError, error.toString()); return ret; } @@ -562,7 +526,7 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::Fil { QFileSystemMetaData::MetaDataFlags queryFlags; - queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type)) + queryFlags |= QFileSystemMetaData::MetaDataFlags::fromInt(type.toInt()) & QFileSystemMetaData::Permissions; // AliasType and BundleType are 0x0 @@ -583,7 +547,7 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::Fil } if (exists && (type & PermsMask)) - ret |= FileFlags(uint(d->metaData.permissions())); + ret |= FileFlags::fromInt(d->metaData.permissions().toInt()); if (type & TypesMask) { if ((type & LinkType) && d->metaData.isLegacyLink()) @@ -629,62 +593,74 @@ QByteArray QFSFileEngine::id() const QString QFSFileEngine::fileName(FileName file) const { Q_D(const QFSFileEngine); - if (file == BaseName) { + switch (file) { + case BaseName: return d->fileEntry.fileName(); - } else if (file == PathName) { + case PathName: return d->fileEntry.path(); - } else if (file == AbsoluteName || file == AbsolutePathName) { - QString ret; - - if (!isRelativePath()) { - if (d->fileEntry.filePath().startsWith(QLatin1Char('/')) || // It's a absolute path to the current drive, so \a.txt -> Z:\a.txt - d->fileEntry.filePath().size() == 2 || // It's a drive letter that needs to get a working dir appended - (d->fileEntry.filePath().size() > 2 && d->fileEntry.filePath().at(2) != QLatin1Char('/')) || // It's a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt - d->fileEntry.filePath().contains(QLatin1String("/../")) || d->fileEntry.filePath().contains(QLatin1String("/./")) || - d->fileEntry.filePath().endsWith(QLatin1String("/..")) || d->fileEntry.filePath().endsWith(QLatin1String("/."))) - { - ret = QDir::fromNativeSeparators(QFileSystemEngine::nativeAbsoluteFilePath(d->fileEntry.filePath())); - } else { - ret = d->fileEntry.filePath(); - } - } else { - ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + d->fileEntry.filePath()); + case AbsoluteName: + case AbsolutePathName: { + QString ret = d->fileEntry.filePath(); + if (isRelativePath()) { + ret = QDir::cleanPath(QDir::currentPath() + u'/' + ret); + } else if (ret.startsWith(u'/') // absolute path to the current drive, so \a.txt -> Z:\a.txt + || ret.size() == 2 // or a drive letter that needs to get a working dir appended + // or a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt + || (ret.size() > 2 && ret.at(2) != u'/') + || ret.contains(QStringView(u"/../")) + || ret.contains(QStringView(u"/./")) + || ret.endsWith(QStringView(u"/..")) + || ret.endsWith(QStringView(u"/."))) { + ret = QDir::fromNativeSeparators(QFileSystemEngine::nativeAbsoluteFilePath(ret)); } // The path should be absolute at this point. // From the docs : // Absolute paths begin with the directory separator "/" // (optionally preceded by a drive specification under Windows). - if (ret.at(0) != QLatin1Char('/')) { + if (ret.at(0) != u'/') { Q_ASSERT(ret.length() >= 2); Q_ASSERT(ret.at(0).isLetter()); - Q_ASSERT(ret.at(1) == QLatin1Char(':')); + Q_ASSERT(ret.at(1) == u':'); // Force uppercase drive letters. ret[0] = ret.at(0).toUpper(); } if (file == AbsolutePathName) { - int slash = ret.lastIndexOf(QLatin1Char('/')); + int slash = ret.lastIndexOf(u'/'); if (slash < 0) return ret; - if (ret.at(0) != QLatin1Char('/') && slash == 2) + if (ret.at(0) != u'/' && slash == 2) return ret.left(3); // include the slash return ret.left(slash > 0 ? slash : 1); } return ret; - } else if (file == CanonicalName || file == CanonicalPathName) { + } + case CanonicalName: + case CanonicalPathName: { if (!(fileFlags(ExistsFlag) & ExistsFlag)) return QString(); - QFileSystemEntry entry(QFileSystemEngine::canonicalName(QFileSystemEntry(fileName(AbsoluteName)), d->metaData)); + const QFileSystemEntry entry = + QFileSystemEngine::canonicalName(QFileSystemEntry(fileName(AbsoluteName)), d->metaData); if (file == CanonicalPathName) return entry.path(); return entry.filePath(); - } else if (file == LinkName) { + } + case AbsoluteLinkTarget: return QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData).filePath(); - } else if (file == BundleName) { + case RawLinkPath: + return QFileSystemEngine::getRawLinkPath(d->fileEntry, d->metaData).filePath(); + case BundleName: return QString(); + case JunctionName: + return QFileSystemEngine::getJunctionTarget(d->fileEntry, d->metaData).filePath(); + case DefaultName: + break; + case NFileNames: + Q_ASSERT(false); + break; } return d->fileEntry.filePath(); } @@ -712,6 +688,10 @@ bool QFSFileEngine::setPermissions(uint perms) { Q_D(QFSFileEngine); QSystemError error; + + // clear cached state (if any) + d->metaData.clearFlags(QFileSystemMetaData::Permissions); + bool ret = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error); if (!ret) setError(QFile::PermissionsError, error.toString()); @@ -757,7 +737,7 @@ bool QFSFileEngine::setSize(qint64 size) return false; } -bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time) +bool QFSFileEngine::setFileTime(const QDateTime &newDate, QFile::FileTime time) { Q_D(QFSFileEngine); @@ -766,7 +746,7 @@ bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time) return false; } - if (!newDate.isValid() || time == QAbstractFileEngine::MetadataChangeTime) { + if (!newDate.isValid() || time == QFile::FileMetadataChangeTime) { setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER)); return false; } @@ -899,17 +879,18 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, bool QFSFileEnginePrivate::unmap(uchar *ptr) { Q_Q(QFSFileEngine); - if (!maps.contains(ptr)) { + const auto it = std::as_const(maps).find(ptr); + if (it == maps.cend()) { q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); return false; } - uchar *start = ptr - maps[ptr]; + uchar *start = ptr - *it; if (!UnmapViewOfFile(start)) { q->setError(QFile::PermissionsError, qt_error_string()); return false; } - maps.remove(ptr); + maps.erase(it); if (maps.isEmpty()) { ::CloseHandle(mapHandle); mapHandle = NULL; |