| /**************************************************************************** |
| ** |
| ** 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$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qplatformdefs.h" |
| #include "private/qabstractfileengine_p.h" |
| #include "private/qfsfileengine_p.h" |
| #include "qfilesystemengine_p.h" |
| #include <qdebug.h> |
| |
| #include "qfile.h" |
| #include "qdir.h" |
| #include "qvarlengtharray.h" |
| #include "qdatetime.h" |
| #include "qt_windows.h" |
| |
| #include <sys/types.h> |
| #include <direct.h> |
| #include <winioctl.h> |
| #include <objbase.h> |
| #ifndef Q_OS_WINRT |
| # include <shlobj.h> |
| # include <accctrl.h> |
| #endif |
| #include <initguid.h> |
| #include <ctype.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #ifndef Q_OS_WINRT |
| # define SECURITY_WIN32 |
| # include <security.h> |
| #endif |
| |
| #ifndef PATH_MAX |
| #define PATH_MAX FILENAME_MAX |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| static inline bool isUncPath(const QString &path) |
| { |
| // Starts with \\, but not \\. |
| return (path.startsWith(QLatin1String("\\\\")) |
| && path.size() > 2 && path.at(2) != QLatin1Char('.')); |
| } |
| |
| /*! |
| \internal |
| */ |
| QString QFSFileEnginePrivate::longFileName(const QString &path) |
| { |
| if (path.startsWith(QLatin1String("\\\\.\\"))) |
| return path; |
| |
| QString absPath = QFileSystemEngine::nativeAbsoluteFilePath(path); |
| #if !defined(Q_OS_WINRT) |
| QString prefix = QLatin1String("\\\\?\\"); |
| if (isUncPath(absPath)) { |
| prefix.append(QLatin1String("UNC\\")); // "\\\\?\\UNC\\" |
| absPath.remove(0, 2); |
| } |
| return prefix + absPath; |
| #else |
| return absPath; |
| #endif |
| } |
| |
| /* |
| \internal |
| */ |
| bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) |
| { |
| Q_Q(QFSFileEngine); |
| |
| // All files are opened in share mode (both read and write). |
| DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; |
| |
| int accessRights = 0; |
| if (openMode & QIODevice::ReadOnly) |
| accessRights |= GENERIC_READ; |
| if (openMode & QIODevice::WriteOnly) |
| accessRights |= GENERIC_WRITE; |
| |
| // WriteOnly can create files, ReadOnly cannot. |
| DWORD creationDisp = (openMode & QIODevice::NewOnly) |
| ? CREATE_NEW |
| : openModeCanCreate(openMode) |
| ? OPEN_ALWAYS |
| : OPEN_EXISTING; |
| // Create the file handle. |
| #ifndef Q_OS_WINRT |
| SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE }; |
| fileHandle = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(), |
| accessRights, |
| shareMode, |
| &securityAtts, |
| creationDisp, |
| FILE_ATTRIBUTE_NORMAL, |
| NULL); |
| #else // !Q_OS_WINRT |
| fileHandle = CreateFile2((const wchar_t*)fileEntry.nativeFilePath().utf16(), |
| accessRights, |
| shareMode, |
| creationDisp, |
| NULL); |
| #endif // Q_OS_WINRT |
| |
| // Bail out on error. |
| if (fileHandle == INVALID_HANDLE_VALUE) { |
| q->setError(QFile::OpenError, qt_error_string()); |
| return false; |
| } |
| |
| // Truncate the file after successfully opening it if Truncate is passed. |
| if (openMode & QIODevice::Truncate) |
| q->setSize(0); |
| |
| return true; |
| } |
| |
| /* |
| \internal |
| */ |
| bool QFSFileEnginePrivate::nativeClose() |
| { |
| Q_Q(QFSFileEngine); |
| if (fh || fd != -1) { |
| // stdlib / stdio mode. |
| return closeFdFh(); |
| } |
| |
| // Windows native mode. |
| bool ok = true; |
| |
| if (cachedFd != -1) { |
| if (::_close(cachedFd) && !::CloseHandle(fileHandle)) { |
| q->setError(QFile::UnspecifiedError, qt_error_string()); |
| ok = false; |
| } |
| |
| // System handle is closed with associated file descriptor. |
| fileHandle = INVALID_HANDLE_VALUE; |
| cachedFd = -1; |
| |
| return ok; |
| } |
| |
| if ((fileHandle == INVALID_HANDLE_VALUE || !::CloseHandle(fileHandle))) { |
| q->setError(QFile::UnspecifiedError, qt_error_string()); |
| ok = false; |
| } |
| fileHandle = INVALID_HANDLE_VALUE; |
| return ok; |
| } |
| |
| /* |
| \internal |
| */ |
| bool QFSFileEnginePrivate::nativeFlush() |
| { |
| if (fh) { |
| // Buffered stdlib mode. |
| return flushFh(); |
| } |
| if (fd != -1) { |
| // Unbuffered stdio mode; always succeeds (no buffer). |
| return true; |
| } |
| |
| // Windows native mode; flushing is unnecessary. |
| return true; |
| } |
| |
| /* |
| \internal |
| \since 5.1 |
| */ |
| bool QFSFileEnginePrivate::nativeSyncToDisk() |
| { |
| if (fh || fd != -1) { |
| // stdlib / stdio mode. No API available. |
| return false; |
| } |
| return FlushFileBuffers(fileHandle); |
| } |
| |
| /* |
| \internal |
| */ |
| qint64 QFSFileEnginePrivate::nativeSize() const |
| { |
| Q_Q(const QFSFileEngine); |
| QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q); |
| |
| // ### Don't flush; for buffered files, we should get away with ftell. |
| thatQ->flush(); |
| |
| // Always retrive the current information |
| metaData.clearFlags(QFileSystemMetaData::SizeAttribute); |
| bool filled = false; |
| if (fileHandle != INVALID_HANDLE_VALUE && openMode != QIODevice::NotOpen ) |
| filled = QFileSystemEngine::fillMetaData(fileHandle, metaData, |
| QFileSystemMetaData::SizeAttribute); |
| else |
| filled = doStat(QFileSystemMetaData::SizeAttribute); |
| |
| if (!filled) { |
| thatQ->setError(QFile::UnspecifiedError, QSystemError::stdString()); |
| return 0; |
| } |
| return metaData.size(); |
| } |
| |
| /* |
| \internal |
| */ |
| qint64 QFSFileEnginePrivate::nativePos() const |
| { |
| Q_Q(const QFSFileEngine); |
| QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q); |
| |
| if (fh || fd != -1) { |
| // stdlib / stido mode. |
| return posFdFh(); |
| } |
| |
| // Windows native mode. |
| if (fileHandle == INVALID_HANDLE_VALUE) |
| return 0; |
| |
| LARGE_INTEGER currentFilePos; |
| LARGE_INTEGER offset; |
| offset.QuadPart = 0; |
| if (!::SetFilePointerEx(fileHandle, offset, ¤tFilePos, FILE_CURRENT)) { |
| thatQ->setError(QFile::UnspecifiedError, qt_error_string()); |
| return 0; |
| } |
| |
| return qint64(currentFilePos.QuadPart); |
| } |
| |
| /* |
| \internal |
| */ |
| bool QFSFileEnginePrivate::nativeSeek(qint64 pos) |
| { |
| Q_Q(QFSFileEngine); |
| |
| if (fh || fd != -1) { |
| // stdlib / stdio mode. |
| return seekFdFh(pos); |
| } |
| |
| LARGE_INTEGER currentFilePos; |
| LARGE_INTEGER offset; |
| offset.QuadPart = pos; |
| if (!::SetFilePointerEx(fileHandle, offset, ¤tFilePos, FILE_BEGIN)) { |
| q->setError(QFile::UnspecifiedError, qt_error_string()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* |
| \internal |
| */ |
| qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 maxlen) |
| { |
| Q_Q(QFSFileEngine); |
| |
| if (fh || fd != -1) { |
| // stdio / stdlib mode. |
| if (fh && nativeIsSequential() && feof(fh)) { |
| q->setError(QFile::ReadError, QSystemError::stdString()); |
| return -1; |
| } |
| |
| return readFdFh(data, maxlen); |
| } |
| |
| // Windows native mode. |
| if (fileHandle == INVALID_HANDLE_VALUE) |
| return -1; |
| |
| qint64 bytesToRead = maxlen; |
| |
| // Reading on Windows fails with ERROR_NO_SYSTEM_RESOURCES when |
| // the chunks are too large, so we limit the block size to 32MB. |
| static const qint64 maxBlockSize = 32 * 1024 * 1024; |
| |
| qint64 totalRead = 0; |
| do { |
| DWORD blockSize = DWORD(qMin(bytesToRead, maxBlockSize)); |
| DWORD bytesRead; |
| if (!ReadFile(fileHandle, data + totalRead, blockSize, &bytesRead, NULL)) { |
| if (totalRead == 0) { |
| // Note: only return failure if the first ReadFile fails. |
| q->setError(QFile::ReadError, qt_error_string()); |
| return -1; |
| } |
| break; |
| } |
| if (bytesRead == 0) |
| break; |
| totalRead += bytesRead; |
| bytesToRead -= bytesRead; |
| } while (totalRead < maxlen); |
| return totalRead; |
| } |
| |
| /* |
| \internal |
| */ |
| qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen) |
| { |
| Q_Q(QFSFileEngine); |
| |
| if (fh || fd != -1) { |
| // stdio / stdlib mode. |
| return readLineFdFh(data, maxlen); |
| } |
| |
| // Windows native mode. |
| if (fileHandle == INVALID_HANDLE_VALUE) |
| return -1; |
| |
| // ### No equivalent in Win32? |
| return q->QAbstractFileEngine::readLine(data, maxlen); |
| } |
| |
| /* |
| \internal |
| */ |
| qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len) |
| { |
| Q_Q(QFSFileEngine); |
| |
| if (fh || fd != -1) { |
| // stdio / stdlib mode. |
| return writeFdFh(data, len); |
| } |
| |
| // Windows native mode. |
| if (fileHandle == INVALID_HANDLE_VALUE) |
| return -1; |
| |
| qint64 bytesToWrite = len; |
| |
| // Writing on Windows fails with ERROR_NO_SYSTEM_RESOURCES when |
| // the chunks are too large, so we limit the block size to 32MB. |
| qint64 totalWritten = 0; |
| do { |
| const DWORD currentBlockSize = DWORD(qMin(bytesToWrite, qint64(32 * 1024 * 1024))); |
| DWORD bytesWritten; |
| if (!WriteFile(fileHandle, data + totalWritten, currentBlockSize, &bytesWritten, NULL)) { |
| if (totalWritten == 0) { |
| // Note: Only return error if the first WriteFile failed. |
| q->setError(QFile::WriteError, qt_error_string()); |
| return -1; |
| } |
| break; |
| } |
| if (bytesWritten == 0) |
| break; |
| totalWritten += bytesWritten; |
| bytesToWrite -= bytesWritten; |
| } while (totalWritten < len); |
| return qint64(totalWritten); |
| } |
| |
| /* |
| \internal |
| */ |
| int QFSFileEnginePrivate::nativeHandle() const |
| { |
| if (fh || fd != -1) |
| return fh ? QT_FILENO(fh) : fd; |
| if (cachedFd != -1) |
| return cachedFd; |
| |
| int flags = 0; |
| if (openMode & QIODevice::Append) |
| flags |= _O_APPEND; |
| if (!(openMode & QIODevice::WriteOnly)) |
| flags |= _O_RDONLY; |
| cachedFd = _open_osfhandle((intptr_t) fileHandle, flags); |
| return cachedFd; |
| } |
| |
| /* |
| \internal |
| */ |
| bool QFSFileEnginePrivate::nativeIsSequential() const |
| { |
| #if !defined(Q_OS_WINRT) |
| HANDLE handle = fileHandle; |
| if (fh || fd != -1) |
| handle = (HANDLE)_get_osfhandle(fh ? QT_FILENO(fh) : fd); |
| if (handle == INVALID_HANDLE_VALUE) |
| return false; |
| |
| DWORD fileType = GetFileType(handle); |
| return (fileType == FILE_TYPE_CHAR) |
| || (fileType == FILE_TYPE_PIPE); |
| #else |
| return false; |
| #endif |
| } |
| |
| bool QFSFileEngine::caseSensitive() const |
| { |
| return false; |
| } |
| |
| QString QFSFileEngine::currentPath(const QString &fileName) |
| { |
| #if !defined(Q_OS_WINRT) |
| 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(':')) { |
| int drv = fileName.toUpper().at(0).toLatin1() - 'A' + 1; |
| if (_getdrive() != drv) { |
| wchar_t buf[PATH_MAX]; |
| ::_wgetdcwd(drv, buf, PATH_MAX); |
| ret = QString::fromWCharArray(buf); |
| } |
| } |
| if (ret.isEmpty()) { |
| //just the pwd |
| ret = QFileSystemEngine::currentPath().filePath(); |
| } |
| if (ret.length() >= 2 && ret[1] == QLatin1Char(':')) |
| ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters. |
| return ret; |
| #else // !Q_OS_WINRT |
| Q_UNUSED(fileName); |
| return QFileSystemEngine::currentPath().filePath(); |
| #endif // Q_OS_WINRT |
| } |
| |
| #if !defined(Q_OS_WINRT) |
| // cf QStorageInfo::isReady |
| static inline bool isDriveReady(const wchar_t *path) |
| { |
| DWORD fileSystemFlags; |
| const UINT driveType = GetDriveType(path); |
| return (driveType != DRIVE_REMOVABLE && driveType != DRIVE_CDROM) |
| || GetVolumeInformation(path, nullptr, 0, nullptr, nullptr, |
| &fileSystemFlags, nullptr, 0) == TRUE; |
| } |
| #endif // !Q_OS_WINRT |
| |
| QFileInfoList QFSFileEngine::drives() |
| { |
| QFileInfoList ret; |
| #if !defined(Q_OS_WINRT) |
| const UINT oldErrorMode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); |
| quint32 driveBits = (quint32) GetLogicalDrives() & 0x3ffffff; |
| wchar_t driveName[] = L"A:\\"; |
| |
| while (driveBits) { |
| if ((driveBits & 1) && isDriveReady(driveName)) |
| ret.append(QFileInfo(QString::fromWCharArray(driveName))); |
| driveName[0]++; |
| driveBits = driveBits >> 1; |
| } |
| ::SetErrorMode(oldErrorMode); |
| return ret; |
| #else // !Q_OS_WINRT |
| ret.append(QFileInfo(QLatin1String("/"))); |
| return ret; |
| #endif // Q_OS_WINRT |
| } |
| |
| bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const |
| { |
| if (!tried_stat || !metaData.hasFlags(flags)) { |
| tried_stat = true; |
| |
| int localFd = fd; |
| if (fh && fileEntry.isEmpty()) |
| localFd = QT_FILENO(fh); |
| if (localFd != -1) |
| QFileSystemEngine::fillMetaData(localFd, metaData, flags); |
| if (metaData.missingFlags(flags) && !fileEntry.isEmpty()) |
| QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags)); |
| } |
| |
| return metaData.exists(); |
| } |
| |
| |
| bool QFSFileEngine::link(const QString &newName) |
| { |
| #if !defined(Q_OS_WINRT) |
| 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(); |
| } |
| if (!ret) |
| setError(QFile::RenameError, qt_error_string()); |
| |
| if (neededCoInit) |
| CoUninitialize(); |
| |
| return ret; |
| #else // !Q_OS_WINRT |
| Q_UNUSED(newName); |
| Q_UNIMPLEMENTED(); |
| return false; |
| #endif // Q_OS_WINRT |
| } |
| |
| /*! |
| \reimp |
| */ |
| QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const |
| { |
| Q_D(const QFSFileEngine); |
| |
| if (type & Refresh) |
| d->metaData.clear(); |
| |
| QAbstractFileEngine::FileFlags ret; |
| |
| if (type & FlagsMask) |
| ret |= LocalDiskFlag; |
| |
| bool exists; |
| { |
| QFileSystemMetaData::MetaDataFlags queryFlags; |
| |
| queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type)) |
| & QFileSystemMetaData::Permissions; |
| |
| // AliasType and BundleType are 0x0 |
| if (type & TypesMask) |
| queryFlags |= QFileSystemMetaData::AliasType |
| | QFileSystemMetaData::LinkType |
| | QFileSystemMetaData::FileType |
| | QFileSystemMetaData::DirectoryType |
| | QFileSystemMetaData::BundleType; |
| |
| if (type & FlagsMask) |
| queryFlags |= QFileSystemMetaData::HiddenAttribute |
| | QFileSystemMetaData::ExistsAttribute; |
| |
| queryFlags |= QFileSystemMetaData::LinkType; |
| |
| exists = d->doStat(queryFlags); |
| } |
| |
| if (exists && (type & PermsMask)) |
| ret |= FileFlags(uint(d->metaData.permissions())); |
| |
| if (type & TypesMask) { |
| if ((type & LinkType) && d->metaData.isLegacyLink()) |
| ret |= LinkType; |
| if (d->metaData.isDirectory()) { |
| ret |= DirectoryType; |
| } else { |
| ret |= FileType; |
| } |
| } |
| if (type & FlagsMask) { |
| if (d->metaData.exists()) { |
| // if we succeeded in querying, then the file exists: a file on |
| // Windows cannot be deleted if we have an open handle to it |
| ret |= ExistsFlag; |
| if (d->fileEntry.isRoot()) |
| ret |= RootFlag; |
| else if (d->metaData.isHidden()) |
| ret |= HiddenFlag; |
| } |
| } |
| return ret; |
| } |
| |
| QByteArray QFSFileEngine::id() const |
| { |
| Q_D(const QFSFileEngine); |
| HANDLE h = d->fileHandle; |
| if (h == INVALID_HANDLE_VALUE) { |
| int localFd = d->fd; |
| if (d->fh && d->fileEntry.isEmpty()) |
| localFd = QT_FILENO(d->fh); |
| if (localFd != -1) |
| h = HANDLE(_get_osfhandle(localFd)); |
| } |
| if (h != INVALID_HANDLE_VALUE) |
| return QFileSystemEngine::id(h); |
| |
| // file is not open, try by path |
| return QFileSystemEngine::id(d->fileEntry); |
| } |
| |
| QString QFSFileEngine::fileName(FileName file) const |
| { |
| Q_D(const QFSFileEngine); |
| if (file == BaseName) { |
| return d->fileEntry.fileName(); |
| } else if (file == 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()); |
| } |
| |
| // 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('/')) { |
| Q_ASSERT(ret.length() >= 2); |
| Q_ASSERT(ret.at(0).isLetter()); |
| Q_ASSERT(ret.at(1) == QLatin1Char(':')); |
| |
| // Force uppercase drive letters. |
| ret[0] = ret.at(0).toUpper(); |
| } |
| |
| if (file == AbsolutePathName) { |
| int slash = ret.lastIndexOf(QLatin1Char('/')); |
| if (slash < 0) |
| return ret; |
| if (ret.at(0) != QLatin1Char('/') && slash == 2) |
| return ret.left(3); // include the slash |
| return ret.left(slash > 0 ? slash : 1); |
| } |
| return ret; |
| } else if (file == CanonicalName || file == CanonicalPathName) { |
| if (!(fileFlags(ExistsFlag) & ExistsFlag)) |
| return QString(); |
| QFileSystemEntry entry(QFileSystemEngine::canonicalName(QFileSystemEntry(fileName(AbsoluteName)), d->metaData)); |
| |
| if (file == CanonicalPathName) |
| return entry.path(); |
| return entry.filePath(); |
| } else if (file == LinkName) { |
| return QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData).filePath(); |
| } else if (file == BundleName) { |
| return QString(); |
| } |
| return d->fileEntry.filePath(); |
| } |
| |
| bool QFSFileEngine::isRelativePath() const |
| { |
| Q_D(const QFSFileEngine); |
| // drive, e.g. "a:", or UNC root, e.q. "//" |
| return d->fileEntry.isRelative(); |
| } |
| |
| uint QFSFileEngine::ownerId(FileOwner /*own*/) const |
| { |
| static const uint nobodyID = (uint) -2; |
| return nobodyID; |
| } |
| |
| QString QFSFileEngine::owner(FileOwner own) const |
| { |
| Q_D(const QFSFileEngine); |
| return QFileSystemEngine::owner(d->fileEntry, own); |
| } |
| |
| bool QFSFileEngine::setPermissions(uint perms) |
| { |
| Q_D(QFSFileEngine); |
| QSystemError error; |
| bool ret = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error); |
| if (!ret) |
| setError(QFile::PermissionsError, error.toString()); |
| return ret; |
| } |
| |
| bool QFSFileEngine::setSize(qint64 size) |
| { |
| Q_D(QFSFileEngine); |
| |
| if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1 || d->fh) { |
| // resize open file |
| HANDLE fh = d->fileHandle; |
| if (fh == INVALID_HANDLE_VALUE) { |
| if (d->fh) |
| fh = (HANDLE)_get_osfhandle(QT_FILENO(d->fh)); |
| else |
| fh = (HANDLE)_get_osfhandle(d->fd); |
| } |
| if (fh == INVALID_HANDLE_VALUE) |
| return false; |
| qint64 currentPos = pos(); |
| |
| if (seek(size) && SetEndOfFile(fh)) { |
| seek(qMin(currentPos, size)); |
| return true; |
| } |
| |
| seek(currentPos); |
| return false; |
| } |
| |
| if (!d->fileEntry.isEmpty()) { |
| // resize file on disk |
| QFile file(d->fileEntry.filePath()); |
| if (file.open(QFile::ReadWrite)) { |
| bool ret = file.resize(size); |
| if (!ret) |
| setError(QFile::ResizeError, file.errorString()); |
| return ret; |
| } |
| } |
| return false; |
| } |
| |
| bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time) |
| { |
| Q_D(QFSFileEngine); |
| |
| if (d->openMode == QFile::NotOpen) { |
| setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); |
| return false; |
| } |
| |
| if (!newDate.isValid() || time == QAbstractFileEngine::MetadataChangeTime) { |
| setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER)); |
| return false; |
| } |
| |
| HANDLE handle = d->fileHandle; |
| if (handle == INVALID_HANDLE_VALUE) { |
| if (d->fh) |
| handle = reinterpret_cast<HANDLE>(::_get_osfhandle(QT_FILENO(d->fh))); |
| else if (d->fd != -1) |
| handle = reinterpret_cast<HANDLE>(::_get_osfhandle(d->fd)); |
| } |
| |
| if (handle == INVALID_HANDLE_VALUE) { |
| setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); |
| return false; |
| } |
| |
| QSystemError error; |
| if (!QFileSystemEngine::setFileTime(handle, newDate, time, error)) { |
| setError(QFile::PermissionsError, error.toString()); |
| return false; |
| } |
| |
| d->metaData.clearFlags(QFileSystemMetaData::Times); |
| return true; |
| } |
| |
| uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, |
| QFile::MemoryMapFlags flags) |
| { |
| Q_Q(QFSFileEngine); |
| Q_UNUSED(flags); |
| if (openMode == QFile::NotOpen) { |
| q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); |
| return 0; |
| } |
| if (offset == 0 && size == 0) { |
| q->setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER)); |
| return 0; |
| } |
| |
| // check/setup args to map |
| DWORD access = 0; |
| if (flags & QFileDevice::MapPrivateOption) { |
| #ifdef FILE_MAP_COPY |
| access = FILE_MAP_COPY; |
| #else |
| q->setError(QFile::UnspecifiedError, "MapPrivateOption unsupported"); |
| return 0; |
| #endif |
| } else if (openMode & QIODevice::WriteOnly) { |
| access = FILE_MAP_WRITE; |
| } else if (openMode & QIODevice::ReadOnly) { |
| access = FILE_MAP_READ; |
| } |
| |
| if (mapHandle == NULL) { |
| // get handle to the file |
| HANDLE handle = fileHandle; |
| |
| if (handle == INVALID_HANDLE_VALUE && fh) |
| handle = (HANDLE)::_get_osfhandle(QT_FILENO(fh)); |
| |
| #ifdef Q_USE_DEPRECATED_MAP_API |
| nativeClose(); |
| // handle automatically closed by kernel with mapHandle (below). |
| handle = ::CreateFileForMapping((const wchar_t*)fileEntry.nativeFilePath().utf16(), |
| GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0), |
| 0, |
| NULL, |
| OPEN_EXISTING, |
| FILE_ATTRIBUTE_NORMAL, |
| NULL); |
| // Since this is a special case, we check if the return value was NULL and if so |
| // we change it to INVALID_HANDLE_VALUE to follow the logic inside this function. |
| if(0 == handle) |
| handle = INVALID_HANDLE_VALUE; |
| #endif |
| |
| if (handle == INVALID_HANDLE_VALUE) { |
| q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); |
| return 0; |
| } |
| |
| // first create the file mapping handle |
| DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY; |
| #ifndef Q_OS_WINRT |
| mapHandle = ::CreateFileMapping(handle, 0, protection, 0, 0, 0); |
| #else |
| mapHandle = ::CreateFileMappingFromApp(handle, 0, protection, 0, 0); |
| #endif |
| if (mapHandle == NULL) { |
| q->setError(QFile::PermissionsError, qt_error_string()); |
| #ifdef Q_USE_DEPRECATED_MAP_API |
| ::CloseHandle(handle); |
| #endif |
| return 0; |
| } |
| } |
| |
| DWORD offsetHi = offset >> 32; |
| DWORD offsetLo = offset & Q_UINT64_C(0xffffffff); |
| SYSTEM_INFO sysinfo; |
| #ifndef Q_OS_WINRT |
| ::GetSystemInfo(&sysinfo); |
| #else |
| ::GetNativeSystemInfo(&sysinfo); |
| #endif |
| DWORD mask = sysinfo.dwAllocationGranularity - 1; |
| DWORD extra = offset & mask; |
| if (extra) |
| offsetLo &= ~mask; |
| |
| // attempt to create the map |
| #ifndef Q_OS_WINRT |
| LPVOID mapAddress = ::MapViewOfFile(mapHandle, access, |
| offsetHi, offsetLo, size + extra); |
| #else |
| LPVOID mapAddress = ::MapViewOfFileFromApp(mapHandle, access, |
| (ULONG64(offsetHi) << 32) + offsetLo, size + extra); |
| #endif |
| if (mapAddress) { |
| uchar *address = extra + static_cast<uchar*>(mapAddress); |
| maps[address] = extra; |
| return address; |
| } |
| |
| switch(GetLastError()) { |
| case ERROR_ACCESS_DENIED: |
| q->setError(QFile::PermissionsError, qt_error_string()); |
| break; |
| case ERROR_INVALID_PARAMETER: |
| // size are out of bounds |
| default: |
| q->setError(QFile::UnspecifiedError, qt_error_string()); |
| } |
| |
| ::CloseHandle(mapHandle); |
| mapHandle = NULL; |
| return 0; |
| } |
| |
| bool QFSFileEnginePrivate::unmap(uchar *ptr) |
| { |
| Q_Q(QFSFileEngine); |
| if (!maps.contains(ptr)) { |
| q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); |
| return false; |
| } |
| uchar *start = ptr - maps[ptr]; |
| if (!UnmapViewOfFile(start)) { |
| q->setError(QFile::PermissionsError, qt_error_string()); |
| return false; |
| } |
| |
| maps.remove(ptr); |
| if (maps.isEmpty()) { |
| ::CloseHandle(mapHandle); |
| mapHandle = NULL; |
| } |
| |
| return true; |
| } |
| |
| /*! |
| \reimp |
| */ |
| bool QFSFileEngine::cloneTo(QAbstractFileEngine *target) |
| { |
| // There's some Windows Server 2016 API, but we won't |
| // bother with it. |
| Q_UNUSED(target); |
| return false; |
| } |
| |
| QT_END_NAMESPACE |