5#include "qplatformdefs.h"
6#include "private/qabstractfileengine_p.h"
7#include "private/qfiledevice_p.h"
8#include "private/qfsfileengine_p.h"
34#define PATH_MAX FILENAME_MAX
39using namespace Qt::StringLiterals;
44 return (path.startsWith(
"\\\\"_L1)
45 && path.size() > 2 && path.at(2) != u'.');
49
50
51QString QFSFileEnginePrivate::longFileName(
const QString &path)
53 if (path.startsWith(
"\\\\.\\"_L1))
56 QString absPath = QFileSystemEngine::nativeAbsoluteFilePath(path);
57 QString prefix =
"\\\\?\\"_L1;
58 if (isUncPath(absPath)) {
59 prefix.append(
"UNC\\"_L1);
62 return prefix + absPath;
66
67
68bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode,
69 std::optional<QFile::Permissions> permissions)
74 DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
77 if (openMode & QIODevice::ReadOnly)
78 accessRights |= GENERIC_READ;
79 if (openMode & QIODevice::WriteOnly)
80 accessRights |= GENERIC_WRITE;
83 DWORD creationDisp = (openMode & QIODevice::NewOnly)
85 : openModeCanCreate(openMode)
89 QNativeFilePermissions nativePermissions(permissions,
false);
90 if (!nativePermissions.isOk())
93 fileHandle = CreateFile((
const wchar_t*)fileEntry.nativeFilePath().utf16(),
96 nativePermissions.securityAttributes(),
98 FILE_ATTRIBUTE_NORMAL,
102 if (fileHandle == INVALID_HANDLE_VALUE) {
103 q->setError(QFile::OpenError, qt_error_string());
108 if (openMode & QIODevice::Truncate)
115
116
117bool QFSFileEnginePrivate::nativeClose()
120 if (fh || fd != -1) {
128 if (cachedFd != -1) {
129 if (::_close(cachedFd) && !::CloseHandle(fileHandle)) {
130 q->setError(QFile::UnspecifiedError, qt_error_string());
135 fileHandle = INVALID_HANDLE_VALUE;
141 if ((fileHandle == INVALID_HANDLE_VALUE || !::CloseHandle(fileHandle))) {
142 q->setError(QFile::UnspecifiedError, qt_error_string());
145 fileHandle = INVALID_HANDLE_VALUE;
150
151
152bool QFSFileEnginePrivate::nativeFlush()
168
169
170
171bool QFSFileEnginePrivate::nativeSyncToDisk()
173 if (fh || fd != -1) {
177 return FlushFileBuffers(fileHandle);
181
182
183qint64 QFSFileEnginePrivate::nativeSize()
const
185 Q_Q(
const QFSFileEngine);
186 QFSFileEngine *thatQ =
const_cast<QFSFileEngine *>(q);
192 metaData.clearFlags(QFileSystemMetaData::SizeAttribute);
194 if (fileHandle != INVALID_HANDLE_VALUE && openMode != QIODevice::NotOpen )
195 filled = QFileSystemEngine::fillMetaData(fileHandle, metaData,
196 QFileSystemMetaData::SizeAttribute);
198 filled = doStat(QFileSystemMetaData::SizeAttribute);
201 thatQ->setError(QFile::UnspecifiedError, QSystemError::stdString());
204 return metaData.size();
208
209
210qint64 QFSFileEnginePrivate::nativePos()
const
212 Q_Q(
const QFSFileEngine);
213 QFSFileEngine *thatQ =
const_cast<QFSFileEngine *>(q);
215 if (fh || fd != -1) {
221 if (fileHandle == INVALID_HANDLE_VALUE)
224 LARGE_INTEGER currentFilePos;
225 LARGE_INTEGER offset;
227 if (!::SetFilePointerEx(fileHandle, offset, ¤tFilePos, FILE_CURRENT)) {
228 thatQ->setError(QFile::UnspecifiedError, qt_error_string());
232 return qint64(currentFilePos.QuadPart);
236
237
238bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
242 if (fh || fd != -1) {
244 return seekFdFh(pos);
247 LARGE_INTEGER currentFilePos;
248 LARGE_INTEGER offset;
249 offset.QuadPart = pos;
250 if (!::SetFilePointerEx(fileHandle, offset, ¤tFilePos, FILE_BEGIN)) {
251 q->setError(QFile::UnspecifiedError, qt_error_string());
259
260
261qint64 QFSFileEnginePrivate::nativeRead(
char *data, qint64 maxlen)
265 if (fh || fd != -1) {
267 if (fh && nativeIsSequential() && feof(fh)) {
268 q->setError(QFile::ReadError, QSystemError::stdString());
272 return readFdFh(data, maxlen);
276 if (fileHandle == INVALID_HANDLE_VALUE)
279 qint64 bytesToRead = maxlen;
283 static const qint64 maxBlockSize = 32 * 1024 * 1024;
285 qint64 totalRead = 0;
287 DWORD blockSize = DWORD(qMin(bytesToRead, maxBlockSize));
289 if (!ReadFile(fileHandle, data + totalRead, blockSize, &bytesRead, NULL)) {
290 if (totalRead == 0) {
292 q->setError(QFile::ReadError, qt_error_string());
299 totalRead += bytesRead;
300 bytesToRead -= bytesRead;
301 }
while (totalRead < maxlen);
306
307
308qint64 QFSFileEnginePrivate::nativeReadLine(
char *data, qint64 maxlen)
312 if (fh || fd != -1) {
314 return readLineFdFh(data, maxlen);
318 if (fileHandle == INVALID_HANDLE_VALUE)
322 return q->QAbstractFileEngine::readLine(data, maxlen);
326
327
328qint64 QFSFileEnginePrivate::nativeWrite(
const char *data, qint64 len)
332 if (fh || fd != -1) {
334 return writeFdFh(data, len);
338 if (fileHandle == INVALID_HANDLE_VALUE)
341 qint64 bytesToWrite = len;
345 qint64 totalWritten = 0;
347 const DWORD currentBlockSize = DWORD(qMin(bytesToWrite, qint64(32 * 1024 * 1024)));
349 if (!WriteFile(fileHandle, data + totalWritten, currentBlockSize, &bytesWritten, NULL)) {
350 if (totalWritten == 0) {
352 q->setError(QFile::WriteError, qt_error_string());
357 if (bytesWritten == 0)
359 totalWritten += bytesWritten;
360 bytesToWrite -= bytesWritten;
361 }
while (totalWritten < len);
362 return qint64(totalWritten);
366
367
368int QFSFileEnginePrivate::nativeHandle()
const
371 return fh ? QT_FILENO(fh) : fd;
376 if (openMode & QIODevice::Append)
378 if (!(openMode & QIODevice::WriteOnly))
380 cachedFd = _open_osfhandle((intptr_t) fileHandle, flags);
385
386
387bool QFSFileEnginePrivate::nativeIsSequential()
const
389 HANDLE handle = fileHandle;
391 handle = (HANDLE)_get_osfhandle(fh ? QT_FILENO(fh) : fd);
392 if (handle == INVALID_HANDLE_VALUE)
395 DWORD fileType = GetFileType(handle);
396 return (fileType == FILE_TYPE_CHAR)
397 || (fileType == FILE_TYPE_PIPE);
400bool QFSFileEnginePrivate::nativeRenameOverwrite(
const QFileSystemEntry &newEntry)
402 if (fileHandle == INVALID_HANDLE_VALUE)
404 const QString newFilePath = newEntry.nativeFilePath();
405 const size_t nameByteLength = newFilePath.length() *
sizeof(
wchar_t);
406 if (nameByteLength +
sizeof(
wchar_t) > std::numeric_limits<DWORD>::max())
409 constexpr size_t RenameInfoSize =
sizeof(FILE_RENAME_INFO);
410 const size_t renameDataSize = RenameInfoSize + nameByteLength +
sizeof(
wchar_t);
411 QVarLengthArray<
char> v(qsizetype(renameDataSize), 0);
413 auto *renameInfo = q20::construct_at(
reinterpret_cast<FILE_RENAME_INFO *>(v.data()));
414 auto renameInfoRAII = qScopeGuard([&] { std::destroy_at(renameInfo); });
415 renameInfo->ReplaceIfExists = TRUE;
416 renameInfo->RootDirectory =
nullptr;
417 renameInfo->FileNameLength = DWORD(nameByteLength);
418 memcpy(renameInfo->FileName, newFilePath.data(), nameByteLength);
420 bool res = SetFileInformationByHandle(fileHandle, FileRenameInfo, renameInfo,
421 DWORD(renameDataSize));
423 DWORD error = GetLastError();
424 q_func()->setError(QFile::RenameError, qt_error_string(
int(error)));
429QString QFSFileEngine::currentPath(
const QString &fileName)
433 if (fileName.length() >= 2 &&
434 fileName.at(0).isLetter() && fileName.at(1) == u':') {
435 int drv = fileName.toUpper().at(0).toLatin1() -
'A' + 1;
436 if (_getdrive() != drv) {
439 ret = QString::fromWCharArray(buf);
444 ret = QFileSystemEngine::currentPath().filePath();
446 if (ret.length() >= 2 && ret[1] == u':')
447 ret[0] = ret.at(0).toUpper();
454 DWORD fileSystemFlags;
455 const UINT driveType = GetDriveType(path);
456 return (driveType != DRIVE_REMOVABLE && driveType != DRIVE_CDROM)
457 || GetVolumeInformation(path,
nullptr, 0,
nullptr,
nullptr,
458 &fileSystemFlags,
nullptr, 0) == TRUE;
461QFileInfoList QFSFileEngine::drives()
464 const UINT oldErrorMode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
465 quint32 driveBits = (quint32) GetLogicalDrives() & 0x3ffffff;
466 wchar_t driveName[] = L"A:\\";
469 if ((driveBits & 1) && isDriveReady(driveName))
470 ret.append(QFileInfo(QString::fromWCharArray(driveName)));
472 driveBits = driveBits >> 1;
474 ::SetErrorMode(oldErrorMode);
478bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags)
const
480 if (!tried_stat || !metaData.hasFlags(flags)) {
484 if (fh && fileEntry.isEmpty())
485 localFd = QT_FILENO(fh);
487 QFileSystemEngine::fillMetaData(localFd, metaData, flags);
488 if (metaData.missingFlags(flags) && !fileEntry.isEmpty())
489 QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags));
492 return metaData.exists();
496bool QFSFileEngine::link(
const QString &newName)
499 bool ret = QFileSystemEngine::createLink(QFileSystemEntry(fileName(AbsoluteName)),
500 QFileSystemEntry(newName), error);
502 setError(QFile::RenameError, error.toString());
507
508
509QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type)
const
511 Q_D(
const QFSFileEngine);
516 QAbstractFileEngine::FileFlags ret;
518 if (type & FlagsMask)
519 ret |= LocalDiskFlag;
523 QFileSystemMetaData::MetaDataFlags queryFlags;
525 queryFlags |= QFileSystemMetaData::MetaDataFlags::fromInt(type.toInt())
526 & QFileSystemMetaData::Permissions;
529 if (type & TypesMask)
530 queryFlags |= QFileSystemMetaData::AliasType
531 | QFileSystemMetaData::LinkType
532 | QFileSystemMetaData::FileType
533 | QFileSystemMetaData::DirectoryType
534 | QFileSystemMetaData::BundleType;
536 if (type & FlagsMask)
537 queryFlags |= QFileSystemMetaData::HiddenAttribute
538 | QFileSystemMetaData::ExistsAttribute;
540 queryFlags |= QFileSystemMetaData::LinkType;
542 exists = d->doStat(queryFlags);
545 if (exists && (type & PermsMask))
546 ret |= FileFlags::fromInt(d->metaData.permissions().toInt());
548 if (type & TypesMask) {
549 if ((type & LinkType) && d->metaData.isLegacyLink())
551 if (d->metaData.isDirectory()) {
552 ret |= DirectoryType;
557 if (type & FlagsMask) {
558 if (d->metaData.exists()) {
562 if (d->fileEntry.isRoot())
564 else if (d->metaData.isHidden())
571QByteArray QFSFileEngine::id()
const
573 Q_D(
const QFSFileEngine);
574 HANDLE h = d->fileHandle;
575 if (h == INVALID_HANDLE_VALUE) {
577 if (d->fh && d->fileEntry.isEmpty())
578 localFd = QT_FILENO(d->fh);
580 h = HANDLE(_get_osfhandle(localFd));
582 if (h != INVALID_HANDLE_VALUE)
583 return QFileSystemEngine::id(h);
586 return QFileSystemEngine::id(d->fileEntry);
589QString QFSFileEngine::fileName(FileName file)
const
591 Q_D(
const QFSFileEngine);
594 return d->fileEntry.fileName();
596 return d->fileEntry.path();
598 case AbsolutePathName: {
599 QString ret = d->fileEntry.filePath();
600 if (isRelativePath()) {
601 ret = QDir::cleanPath(QDir::currentPath() + u'/' + ret);
602 }
else if (ret.startsWith(u'/')
605 || (ret.size() > 2 && ret.at(2) != u'/')
606 || ret.contains(QStringView(u"/../"))
607 || ret.contains(QStringView(u"/./"))
608 || ret.endsWith(QStringView(u"/.."))
609 || ret.endsWith(QStringView(u"/."))) {
610 ret = QDir::fromNativeSeparators(QFileSystemEngine::nativeAbsoluteFilePath(ret));
617 if (ret.at(0) != u'/') {
618 Q_ASSERT(ret.length() >= 2);
619 Q_ASSERT(ret.at(0).isLetter());
620 Q_ASSERT(ret.at(1) == u':');
623 ret[0] = ret.at(0).toUpper();
626 if (file == AbsolutePathName) {
627 int slash = ret.lastIndexOf(u'/');
630 if (ret.at(0) != u'/' && slash == 2)
632 return ret.left(slash > 0 ? slash : 1);
637 case CanonicalPathName: {
638 if (!(fileFlags(ExistsFlag) & ExistsFlag))
640 const QFileSystemEntry entry =
641 QFileSystemEngine::canonicalName(QFileSystemEntry(fileName(AbsoluteName)), d->metaData);
643 if (file == CanonicalPathName)
645 return entry.filePath();
647 case AbsoluteLinkTarget:
648 return QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData).filePath();
650 return QFileSystemEngine::getRawLinkPath(d->fileEntry, d->metaData).filePath();
654 return QFileSystemEngine::getJunctionTarget(d->fileEntry, d->metaData).filePath();
661 return d->fileEntry.filePath();
664bool QFSFileEngine::isRelativePath()
const
666 Q_D(
const QFSFileEngine);
668 return d->fileEntry.isRelative();
671uint QFSFileEngine::ownerId(FileOwner )
const
673 static const uint nobodyID = (uint) -2;
677QString QFSFileEngine::owner(FileOwner own)
const
679 Q_D(
const QFSFileEngine);
680 return QFileSystemEngine::owner(d->fileEntry, own);
683bool QFSFileEngine::setPermissions(uint perms)
689 d->metaData.clearFlags(QFileSystemMetaData::Permissions);
691 bool ret = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error);
693 setError(QFile::PermissionsError, error.toString());
697bool QFSFileEngine::setSize(qint64 size)
701 if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1 || d->fh) {
703 HANDLE fh = d->fileHandle;
704 if (fh == INVALID_HANDLE_VALUE) {
706 fh = (HANDLE)_get_osfhandle(QT_FILENO(d->fh));
708 fh = (HANDLE)_get_osfhandle(d->fd);
710 if (fh == INVALID_HANDLE_VALUE)
712 qint64 currentPos = pos();
714 if (seek(size) && SetEndOfFile(fh)) {
715 seek(qMin(currentPos, size));
723 if (!d->fileEntry.isEmpty()) {
725 QFile file(d->fileEntry.filePath());
726 if (file.open(QFile::ReadWrite)) {
727 bool ret = file.resize(size);
729 setError(QFile::ResizeError, file.errorString());
736bool QFSFileEngine::setFileTime(
const QDateTime &newDate, QFile::FileTime time)
740 if (d->openMode == QFile::NotOpen) {
741 setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
745 if (!newDate.isValid() || time == QFile::FileMetadataChangeTime) {
746 setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
750 HANDLE handle = d->fileHandle;
751 if (handle == INVALID_HANDLE_VALUE) {
753 handle =
reinterpret_cast<HANDLE>(::_get_osfhandle(QT_FILENO(d->fh)));
754 else if (d->fd != -1)
755 handle =
reinterpret_cast<HANDLE>(::_get_osfhandle(d->fd));
758 if (handle == INVALID_HANDLE_VALUE) {
759 setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
764 if (!QFileSystemEngine::setFileTime(handle, newDate, time, error)) {
765 setError(QFile::PermissionsError, error.toString());
769 d->metaData.clearFlags(QFileSystemMetaData::Times);
773uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
774 QFile::MemoryMapFlags flags)
778 if (openMode == QFile::NotOpen) {
779 q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
782 if (offset == 0 && size == 0) {
783 q->setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
789 if (flags & QFileDevice::MapPrivateOption) {
791 access = FILE_MAP_COPY;
793 q->setError(QFile::UnspecifiedError,
"MapPrivateOption unsupported");
796 }
else if (openMode & QIODevice::WriteOnly) {
797 access = FILE_MAP_WRITE;
798 }
else if (openMode & QIODevice::ReadOnly) {
799 access = FILE_MAP_READ;
802 if (mapHandle == NULL) {
804 HANDLE handle = fileHandle;
806 if (handle == INVALID_HANDLE_VALUE && fh)
807 handle = (HANDLE)::_get_osfhandle(QT_FILENO(fh));
809#ifdef Q_USE_DEPRECATED_MAP_API
812 handle = ::CreateFileForMapping((
const wchar_t*)fileEntry.nativeFilePath().utf16(),
813 GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0),
817 FILE_ATTRIBUTE_NORMAL,
822 handle = INVALID_HANDLE_VALUE;
825 if (handle == INVALID_HANDLE_VALUE) {
826 q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
831 DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY;
832 mapHandle = ::CreateFileMapping(handle, 0, protection, 0, 0, 0);
833 if (mapHandle == NULL) {
834 q->setError(QFile::PermissionsError, qt_error_string());
835#ifdef Q_USE_DEPRECATED_MAP_API
836 ::CloseHandle(handle);
842 DWORD offsetHi = offset >> 32;
843 DWORD offsetLo = offset & Q_UINT64_C(0xffffffff);
845 ::GetSystemInfo(&sysinfo);
846 DWORD mask = sysinfo.dwAllocationGranularity - 1;
847 DWORD extra = offset & mask;
852 LPVOID mapAddress = ::MapViewOfFile(mapHandle, access,
853 offsetHi, offsetLo, size + extra);
855 uchar *address = extra +
static_cast<uchar*>(mapAddress);
856 maps[address] = extra;
860 switch(GetLastError()) {
861 case ERROR_ACCESS_DENIED:
862 q->setError(QFile::PermissionsError, qt_error_string());
864 case ERROR_INVALID_PARAMETER:
867 q->setError(QFile::UnspecifiedError, qt_error_string());
870 ::CloseHandle(mapHandle);
875bool QFSFileEnginePrivate::unmap(uchar *ptr)
878 const auto it = std::as_const(maps).find(ptr);
879 if (it == maps.cend()) {
880 q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
883 uchar *start = ptr - *it;
884 if (!UnmapViewOfFile(start)) {
885 q->setError(QFile::PermissionsError, qt_error_string());
890 if (maps.isEmpty()) {
891 ::CloseHandle(mapHandle);
899
900
901QAbstractFileEngine::TriStateResult QFSFileEngine::cloneTo(QAbstractFileEngine *target)
906 return TriStateResult::NotSupported;
static bool isDriveReady(const wchar_t *path)
static bool isUncPath(const QString &path)