Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qfsfileengine_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "qplatformdefs.h"
6#include "private/qabstractfileengine_p.h"
7#include "private/qfiledevice_p.h"
8#include "private/qfsfileengine_p.h"
10#include <qdebug.h>
11
12#include "qfile.h"
13#include "qdir.h"
15#include "qdatetime.h"
16#include "qt_windows.h"
17
18#include <sys/types.h>
19#include <direct.h>
20#include <winioctl.h>
21#include <objbase.h>
22#include <shlobj.h>
23#include <accctrl.h>
24#include <initguid.h>
25#include <ctype.h>
26#include <limits.h>
27#include <stdio.h>
28#define SECURITY_WIN32
29#include <security.h>
30
31#include <memory>
32
33#ifndef PATH_MAX
34#define PATH_MAX FILENAME_MAX
35#endif
36
38
39using namespace Qt::StringLiterals;
40
41static inline bool isUncPath(const QString &path)
42{
43 // Starts with \\, but not \\.
44 return (path.startsWith("\\\\"_L1)
45 && path.size() > 2 && path.at(2) != u'.');
46}
47
48/*!
49 \internal
50*/
51QString QFSFileEnginePrivate::longFileName(const QString &path)
52{
53 if (path.startsWith("\\\\.\\"_L1))
54 return path;
55
56 QString absPath = QFileSystemEngine::nativeAbsoluteFilePath(path);
57 QString prefix = "\\\\?\\"_L1;
58 if (isUncPath(absPath)) {
59 prefix.append("UNC\\"_L1); // "\\\\?\\UNC\\"
60 absPath.remove(0, 2);
61 }
62 return prefix + absPath;
63}
64
65/*
66 \internal
67*/
68bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode,
69 std::optional<QFile::Permissions> permissions)
70{
71 Q_Q(QFSFileEngine);
72
73 // All files are opened in share mode (both read and write).
74 DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
75
76 int accessRights = 0;
77 if (openMode & QIODevice::ReadOnly)
78 accessRights |= GENERIC_READ;
79 if (openMode & QIODevice::WriteOnly)
80 accessRights |= GENERIC_WRITE;
81
82 // WriteOnly can create files, ReadOnly cannot.
83 DWORD creationDisp = (openMode & QIODevice::NewOnly)
84 ? CREATE_NEW
85 : openModeCanCreate(openMode)
86 ? OPEN_ALWAYS
87 : OPEN_EXISTING;
88 // Create the file handle.
89 QNativeFilePermissions nativePermissions(permissions, false);
90 if (!nativePermissions.isOk())
91 return false;
92
93 fileHandle = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(),
94 accessRights,
95 shareMode,
96 nativePermissions.securityAttributes(),
97 creationDisp,
98 FILE_ATTRIBUTE_NORMAL,
99 NULL);
100
101 // Bail out on error.
102 if (fileHandle == INVALID_HANDLE_VALUE) {
103 q->setError(QFile::OpenError, qt_error_string());
104 return false;
105 }
106
107 // Truncate the file after successfully opening it if Truncate is passed.
108 if (openMode & QIODevice::Truncate)
109 q->setSize(0);
110
111 return true;
112}
113
114/*
115 \internal
116*/
117bool QFSFileEnginePrivate::nativeClose()
118{
119 Q_Q(QFSFileEngine);
120 if (fh || fd != -1) {
121 // stdlib / stdio mode.
122 return closeFdFh();
123 }
124
125 // Windows native mode.
126 bool ok = true;
127
128 if (cachedFd != -1) {
129 if (::_close(cachedFd) && !::CloseHandle(fileHandle)) {
130 q->setError(QFile::UnspecifiedError, qt_error_string());
131 ok = false;
132 }
133
134 // System handle is closed with associated file descriptor.
135 fileHandle = INVALID_HANDLE_VALUE;
136 cachedFd = -1;
137
138 return ok;
139 }
140
141 if ((fileHandle == INVALID_HANDLE_VALUE || !::CloseHandle(fileHandle))) {
142 q->setError(QFile::UnspecifiedError, qt_error_string());
143 ok = false;
144 }
145 fileHandle = INVALID_HANDLE_VALUE;
146 return ok;
147}
148
149/*
150 \internal
151*/
152bool QFSFileEnginePrivate::nativeFlush()
153{
154 if (fh) {
155 // Buffered stdlib mode.
156 return flushFh();
157 }
158 if (fd != -1) {
159 // Unbuffered stdio mode; always succeeds (no buffer).
160 return true;
161 }
162
163 // Windows native mode; flushing is unnecessary.
164 return true;
165}
166
167/*
168 \internal
169 \since 5.1
170*/
171bool QFSFileEnginePrivate::nativeSyncToDisk()
172{
173 if (fh || fd != -1) {
174 // stdlib / stdio mode. No API available.
175 return false;
176 }
177 return FlushFileBuffers(fileHandle);
178}
179
180/*
181 \internal
182*/
183qint64 QFSFileEnginePrivate::nativeSize() const
184{
185 Q_Q(const QFSFileEngine);
186 QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
187
188 // ### Don't flush; for buffered files, we should get away with ftell.
189 thatQ->flush();
190
191 // Always retrieve the current information
192 metaData.clearFlags(QFileSystemMetaData::SizeAttribute);
193 bool filled = false;
194 if (fileHandle != INVALID_HANDLE_VALUE && openMode != QIODevice::NotOpen )
195 filled = QFileSystemEngine::fillMetaData(fileHandle, metaData,
196 QFileSystemMetaData::SizeAttribute);
197 else
198 filled = doStat(QFileSystemMetaData::SizeAttribute);
199
200 if (!filled) {
201 thatQ->setError(QFile::UnspecifiedError, QSystemError::stdString());
202 return 0;
203 }
204 return metaData.size();
205}
206
207/*
208 \internal
209*/
210qint64 QFSFileEnginePrivate::nativePos() const
211{
212 Q_Q(const QFSFileEngine);
213 QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
214
215 if (fh || fd != -1) {
216 // stdlib / stido mode.
217 return posFdFh();
218 }
219
220 // Windows native mode.
221 if (fileHandle == INVALID_HANDLE_VALUE)
222 return 0;
223
224 LARGE_INTEGER currentFilePos;
225 LARGE_INTEGER offset;
226 offset.QuadPart = 0;
227 if (!::SetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_CURRENT)) {
228 thatQ->setError(QFile::UnspecifiedError, qt_error_string());
229 return 0;
230 }
231
232 return qint64(currentFilePos.QuadPart);
233}
234
235/*
236 \internal
237*/
238bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
239{
240 Q_Q(QFSFileEngine);
241
242 if (fh || fd != -1) {
243 // stdlib / stdio mode.
244 return seekFdFh(pos);
245 }
246
247 LARGE_INTEGER currentFilePos;
248 LARGE_INTEGER offset;
249 offset.QuadPart = pos;
250 if (!::SetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_BEGIN)) {
251 q->setError(QFile::UnspecifiedError, qt_error_string());
252 return false;
253 }
254
255 return true;
256}
257
258/*
259 \internal
260*/
261qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 maxlen)
262{
263 Q_Q(QFSFileEngine);
264
265 if (fh || fd != -1) {
266 // stdio / stdlib mode.
267 if (fh && nativeIsSequential() && feof(fh)) {
268 q->setError(QFile::ReadError, QSystemError::stdString());
269 return -1;
270 }
271
272 return readFdFh(data, maxlen);
273 }
274
275 // Windows native mode.
276 if (fileHandle == INVALID_HANDLE_VALUE)
277 return -1;
278
279 qint64 bytesToRead = maxlen;
280
281 // Reading on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
282 // the chunks are too large, so we limit the block size to 32MB.
283 static const qint64 maxBlockSize = 32 * 1024 * 1024;
284
285 qint64 totalRead = 0;
286 do {
287 DWORD blockSize = DWORD(qMin(bytesToRead, maxBlockSize));
288 DWORD bytesRead;
289 if (!ReadFile(fileHandle, data + totalRead, blockSize, &bytesRead, NULL)) {
290 if (totalRead == 0) {
291 // Note: only return failure if the first ReadFile fails.
292 q->setError(QFile::ReadError, qt_error_string());
293 return -1;
294 }
295 break;
296 }
297 if (bytesRead == 0)
298 break;
299 totalRead += bytesRead;
300 bytesToRead -= bytesRead;
301 } while (totalRead < maxlen);
302 return totalRead;
303}
304
305/*
306 \internal
307*/
308qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
309{
310 Q_Q(QFSFileEngine);
311
312 if (fh || fd != -1) {
313 // stdio / stdlib mode.
314 return readLineFdFh(data, maxlen);
315 }
316
317 // Windows native mode.
318 if (fileHandle == INVALID_HANDLE_VALUE)
319 return -1;
320
321 // ### No equivalent in Win32?
322 return q->QAbstractFileEngine::readLine(data, maxlen);
323}
324
325/*
326 \internal
327*/
328qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
329{
330 Q_Q(QFSFileEngine);
331
332 if (fh || fd != -1) {
333 // stdio / stdlib mode.
334 return writeFdFh(data, len);
335 }
336
337 // Windows native mode.
338 if (fileHandle == INVALID_HANDLE_VALUE)
339 return -1;
340
341 qint64 bytesToWrite = len;
342
343 // Writing on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
344 // the chunks are too large, so we limit the block size to 32MB.
345 qint64 totalWritten = 0;
346 do {
347 const DWORD currentBlockSize = DWORD(qMin(bytesToWrite, qint64(32 * 1024 * 1024)));
348 DWORD bytesWritten;
349 if (!WriteFile(fileHandle, data + totalWritten, currentBlockSize, &bytesWritten, NULL)) {
350 if (totalWritten == 0) {
351 // Note: Only return error if the first WriteFile failed.
352 q->setError(QFile::WriteError, qt_error_string());
353 return -1;
354 }
355 break;
356 }
357 if (bytesWritten == 0)
358 break;
359 totalWritten += bytesWritten;
360 bytesToWrite -= bytesWritten;
361 } while (totalWritten < len);
362 return qint64(totalWritten);
363}
364
365/*
366 \internal
367*/
368int QFSFileEnginePrivate::nativeHandle() const
369{
370 if (fh || fd != -1)
371 return fh ? QT_FILENO(fh) : fd;
372 if (cachedFd != -1)
373 return cachedFd;
374
375 int flags = 0;
376 if (openMode & QIODevice::Append)
377 flags |= _O_APPEND;
378 if (!(openMode & QIODevice::WriteOnly))
379 flags |= _O_RDONLY;
380 cachedFd = _open_osfhandle((intptr_t) fileHandle, flags);
381 return cachedFd;
382}
383
384/*
385 \internal
386*/
387bool QFSFileEnginePrivate::nativeIsSequential() const
388{
389 HANDLE handle = fileHandle;
390 if (fh || fd != -1)
391 handle = (HANDLE)_get_osfhandle(fh ? QT_FILENO(fh) : fd);
392 if (handle == INVALID_HANDLE_VALUE)
393 return false;
394
395 DWORD fileType = GetFileType(handle);
396 return (fileType == FILE_TYPE_CHAR)
397 || (fileType == FILE_TYPE_PIPE);
398}
399
400bool QFSFileEnginePrivate::nativeRenameOverwrite(const QFileSystemEntry &newEntry)
401{
402 if (fileHandle == INVALID_HANDLE_VALUE)
403 return false;
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())
407 return false;
408
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);
412
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);
419
420 bool res = SetFileInformationByHandle(fileHandle, FileRenameInfo, renameInfo,
421 DWORD(renameDataSize));
422 if (!res) {
423 DWORD error = GetLastError();
424 q_func()->setError(QFile::RenameError, qt_error_string(int(error)));
425 }
426 return res;
427}
428
429QString QFSFileEngine::currentPath(const QString &fileName)
430{
431 QString ret;
432 //if filename is a drive: then get the pwd of that drive
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) {
437 wchar_t buf[PATH_MAX];
438 ::_wgetdcwd(drv, buf, PATH_MAX);
439 ret = QString::fromWCharArray(buf);
440 }
441 }
442 if (ret.isEmpty()) {
443 //just the pwd
444 ret = QFileSystemEngine::currentPath().filePath();
445 }
446 if (ret.length() >= 2 && ret[1] == u':')
447 ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
448 return ret;
449}
450
451// cf QStorageInfo::isReady
452static inline bool isDriveReady(const wchar_t *path)
453{
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;
459}
460
461QFileInfoList QFSFileEngine::drives()
462{
463 QFileInfoList ret;
464 const UINT oldErrorMode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
465 quint32 driveBits = (quint32) GetLogicalDrives() & 0x3ffffff;
466 wchar_t driveName[] = L"A:\\";
467
468 while (driveBits) {
469 if ((driveBits & 1) && isDriveReady(driveName))
470 ret.append(QFileInfo(QString::fromWCharArray(driveName)));
471 driveName[0]++;
472 driveBits = driveBits >> 1;
473 }
474 ::SetErrorMode(oldErrorMode);
475 return ret;
476}
477
478bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const
479{
480 if (!tried_stat || !metaData.hasFlags(flags)) {
481 tried_stat = true;
482
483 int localFd = fd;
484 if (fh && fileEntry.isEmpty())
485 localFd = QT_FILENO(fh);
486 if (localFd != -1)
487 QFileSystemEngine::fillMetaData(localFd, metaData, flags);
488 if (metaData.missingFlags(flags) && !fileEntry.isEmpty())
489 QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags));
490 }
491
492 return metaData.exists();
493}
494
495// ### assume that they add .lnk to newName
496bool QFSFileEngine::link(const QString &newName)
497{
498 QSystemError error;
499 bool ret = QFileSystemEngine::createLink(QFileSystemEntry(fileName(AbsoluteName)),
500 QFileSystemEntry(newName), error);
501 if (!ret)
502 setError(QFile::RenameError, error.toString());
503 return ret;
504}
505
506/*!
507 \reimp
508*/
509QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
510{
511 Q_D(const QFSFileEngine);
512
513 if (type & Refresh)
514 d->metaData.clear();
515
516 QAbstractFileEngine::FileFlags ret;
517
518 if (type & FlagsMask)
519 ret |= LocalDiskFlag;
520
521 bool exists;
522 {
523 QFileSystemMetaData::MetaDataFlags queryFlags;
524
525 queryFlags |= QFileSystemMetaData::MetaDataFlags::fromInt(type.toInt())
526 & QFileSystemMetaData::Permissions;
527
528 // AliasType and BundleType are 0x0
529 if (type & TypesMask)
530 queryFlags |= QFileSystemMetaData::AliasType
531 | QFileSystemMetaData::LinkType
532 | QFileSystemMetaData::FileType
533 | QFileSystemMetaData::DirectoryType
534 | QFileSystemMetaData::BundleType;
535
536 if (type & FlagsMask)
537 queryFlags |= QFileSystemMetaData::HiddenAttribute
538 | QFileSystemMetaData::ExistsAttribute;
539
540 queryFlags |= QFileSystemMetaData::LinkType;
541
542 exists = d->doStat(queryFlags);
543 }
544
545 if (exists && (type & PermsMask))
546 ret |= FileFlags::fromInt(d->metaData.permissions().toInt());
547
548 if (type & TypesMask) {
549 if ((type & LinkType) && d->metaData.isLegacyLink())
550 ret |= LinkType;
551 if (d->metaData.isDirectory()) {
552 ret |= DirectoryType;
553 } else {
554 ret |= FileType;
555 }
556 }
557 if (type & FlagsMask) {
558 if (d->metaData.exists()) {
559 // if we succeeded in querying, then the file exists: a file on
560 // Windows cannot be deleted if we have an open handle to it
561 ret |= ExistsFlag;
562 if (d->fileEntry.isRoot())
563 ret |= RootFlag;
564 else if (d->metaData.isHidden())
565 ret |= HiddenFlag;
566 }
567 }
568 return ret;
569}
570
571QByteArray QFSFileEngine::id() const
572{
573 Q_D(const QFSFileEngine);
574 HANDLE h = d->fileHandle;
575 if (h == INVALID_HANDLE_VALUE) {
576 int localFd = d->fd;
577 if (d->fh && d->fileEntry.isEmpty())
578 localFd = QT_FILENO(d->fh);
579 if (localFd != -1)
580 h = HANDLE(_get_osfhandle(localFd));
581 }
582 if (h != INVALID_HANDLE_VALUE)
583 return QFileSystemEngine::id(h);
584
585 // file is not open, try by path
586 return QFileSystemEngine::id(d->fileEntry);
587}
588
589QString QFSFileEngine::fileName(FileName file) const
590{
591 Q_D(const QFSFileEngine);
592 switch (file) {
593 case BaseName:
594 return d->fileEntry.fileName();
595 case PathName:
596 return d->fileEntry.path();
597 case AbsoluteName:
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'/') // absolute path to the current drive, so \a.txt -> Z:\a.txt
603 || ret.size() == 2 // or a drive letter that needs to get a working dir appended
604 // or a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt
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));
611 }
612
613 // The path should be absolute at this point.
614 // From the docs :
615 // Absolute paths begin with the directory separator "/"
616 // (optionally preceded by a drive specification under Windows).
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':');
621
622 // Force uppercase drive letters.
623 ret[0] = ret.at(0).toUpper();
624 }
625
626 if (file == AbsolutePathName) {
627 int slash = ret.lastIndexOf(u'/');
628 if (slash < 0)
629 return ret;
630 if (ret.at(0) != u'/' && slash == 2)
631 return ret.left(3); // include the slash
632 return ret.left(slash > 0 ? slash : 1);
633 }
634 return ret;
635 }
636 case CanonicalName:
637 case CanonicalPathName: {
638 if (!(fileFlags(ExistsFlag) & ExistsFlag))
639 return QString();
640 const QFileSystemEntry entry =
641 QFileSystemEngine::canonicalName(QFileSystemEntry(fileName(AbsoluteName)), d->metaData);
642
643 if (file == CanonicalPathName)
644 return entry.path();
645 return entry.filePath();
646 }
647 case AbsoluteLinkTarget:
648 return QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData).filePath();
649 case RawLinkPath:
650 return QFileSystemEngine::getRawLinkPath(d->fileEntry, d->metaData).filePath();
651 case BundleName:
652 return QString();
653 case JunctionName:
654 return QFileSystemEngine::getJunctionTarget(d->fileEntry, d->metaData).filePath();
655 case DefaultName:
656 break;
657 case NFileNames:
658 Q_ASSERT(false);
659 break;
660 }
661 return d->fileEntry.filePath();
662}
663
664bool QFSFileEngine::isRelativePath() const
665{
666 Q_D(const QFSFileEngine);
667 // drive, e.g. "a:", or UNC root, e.q. "//"
668 return d->fileEntry.isRelative();
669}
670
671uint QFSFileEngine::ownerId(FileOwner /*own*/) const
672{
673 static const uint nobodyID = (uint) -2;
674 return nobodyID;
675}
676
677QString QFSFileEngine::owner(FileOwner own) const
678{
679 Q_D(const QFSFileEngine);
680 return QFileSystemEngine::owner(d->fileEntry, own);
681}
682
683bool QFSFileEngine::setPermissions(uint perms)
684{
685 Q_D(QFSFileEngine);
686 QSystemError error;
687
688 // clear cached state (if any)
689 d->metaData.clearFlags(QFileSystemMetaData::Permissions);
690
691 bool ret = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error);
692 if (!ret)
693 setError(QFile::PermissionsError, error.toString());
694 return ret;
695}
696
697bool QFSFileEngine::setSize(qint64 size)
698{
699 Q_D(QFSFileEngine);
700
701 if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1 || d->fh) {
702 // resize open file
703 HANDLE fh = d->fileHandle;
704 if (fh == INVALID_HANDLE_VALUE) {
705 if (d->fh)
706 fh = (HANDLE)_get_osfhandle(QT_FILENO(d->fh));
707 else
708 fh = (HANDLE)_get_osfhandle(d->fd);
709 }
710 if (fh == INVALID_HANDLE_VALUE)
711 return false;
712 qint64 currentPos = pos();
713
714 if (seek(size) && SetEndOfFile(fh)) {
715 seek(qMin(currentPos, size));
716 return true;
717 }
718
719 seek(currentPos);
720 return false;
721 }
722
723 if (!d->fileEntry.isEmpty()) {
724 // resize file on disk
725 QFile file(d->fileEntry.filePath());
726 if (file.open(QFile::ReadWrite)) {
727 bool ret = file.resize(size);
728 if (!ret)
729 setError(QFile::ResizeError, file.errorString());
730 return ret;
731 }
732 }
733 return false;
734}
735
736bool QFSFileEngine::setFileTime(const QDateTime &newDate, QFile::FileTime time)
737{
738 Q_D(QFSFileEngine);
739
740 if (d->openMode == QFile::NotOpen) {
741 setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
742 return false;
743 }
744
745 if (!newDate.isValid() || time == QFile::FileMetadataChangeTime) {
746 setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
747 return false;
748 }
749
750 HANDLE handle = d->fileHandle;
751 if (handle == INVALID_HANDLE_VALUE) {
752 if (d->fh)
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));
756 }
757
758 if (handle == INVALID_HANDLE_VALUE) {
759 setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
760 return false;
761 }
762
763 QSystemError error;
764 if (!QFileSystemEngine::setFileTime(handle, newDate, time, error)) {
765 setError(QFile::PermissionsError, error.toString());
766 return false;
767 }
768
769 d->metaData.clearFlags(QFileSystemMetaData::Times);
770 return true;
771}
772
773uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
774 QFile::MemoryMapFlags flags)
775{
776 Q_Q(QFSFileEngine);
777 Q_UNUSED(flags);
778 if (openMode == QFile::NotOpen) {
779 q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
780 return 0;
781 }
782 if (offset == 0 && size == 0) {
783 q->setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
784 return 0;
785 }
786
787 // check/setup args to map
788 DWORD access = 0;
789 if (flags & QFileDevice::MapPrivateOption) {
790#ifdef FILE_MAP_COPY
791 access = FILE_MAP_COPY;
792#else
793 q->setError(QFile::UnspecifiedError, "MapPrivateOption unsupported");
794 return 0;
795#endif
796 } else if (openMode & QIODevice::WriteOnly) {
797 access = FILE_MAP_WRITE;
798 } else if (openMode & QIODevice::ReadOnly) {
799 access = FILE_MAP_READ;
800 }
801
802 if (mapHandle == NULL) {
803 // get handle to the file
804 HANDLE handle = fileHandle;
805
806 if (handle == INVALID_HANDLE_VALUE && fh)
807 handle = (HANDLE)::_get_osfhandle(QT_FILENO(fh));
808
809#ifdef Q_USE_DEPRECATED_MAP_API
810 nativeClose();
811 // handle automatically closed by kernel with mapHandle (below).
812 handle = ::CreateFileForMapping((const wchar_t*)fileEntry.nativeFilePath().utf16(),
813 GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0),
814 0,
815 NULL,
816 OPEN_EXISTING,
817 FILE_ATTRIBUTE_NORMAL,
818 NULL);
819 // Since this is a special case, we check if the return value was NULL and if so
820 // we change it to INVALID_HANDLE_VALUE to follow the logic inside this function.
821 if (!handle)
822 handle = INVALID_HANDLE_VALUE;
823#endif
824
825 if (handle == INVALID_HANDLE_VALUE) {
826 q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
827 return 0;
828 }
829
830 // first create the file mapping handle
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);
837#endif
838 return 0;
839 }
840 }
841
842 DWORD offsetHi = offset >> 32;
843 DWORD offsetLo = offset & Q_UINT64_C(0xffffffff);
844 SYSTEM_INFO sysinfo;
845 ::GetSystemInfo(&sysinfo);
846 DWORD mask = sysinfo.dwAllocationGranularity - 1;
847 DWORD extra = offset & mask;
848 if (extra)
849 offsetLo &= ~mask;
850
851 // attempt to create the map
852 LPVOID mapAddress = ::MapViewOfFile(mapHandle, access,
853 offsetHi, offsetLo, size + extra);
854 if (mapAddress) {
855 uchar *address = extra + static_cast<uchar*>(mapAddress);
856 maps[address] = extra;
857 return address;
858 }
859
860 switch(GetLastError()) {
861 case ERROR_ACCESS_DENIED:
862 q->setError(QFile::PermissionsError, qt_error_string());
863 break;
864 case ERROR_INVALID_PARAMETER:
865 // size are out of bounds
866 default:
867 q->setError(QFile::UnspecifiedError, qt_error_string());
868 }
869
870 ::CloseHandle(mapHandle);
871 mapHandle = NULL;
872 return 0;
873}
874
875bool QFSFileEnginePrivate::unmap(uchar *ptr)
876{
877 Q_Q(QFSFileEngine);
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));
881 return false;
882 }
883 uchar *start = ptr - *it;
884 if (!UnmapViewOfFile(start)) {
885 q->setError(QFile::PermissionsError, qt_error_string());
886 return false;
887 }
888
889 maps.erase(it);
890 if (maps.isEmpty()) {
891 ::CloseHandle(mapHandle);
892 mapHandle = NULL;
893 }
894
895 return true;
896}
897
898/*!
899 \reimp
900*/
901QAbstractFileEngine::TriStateResult QFSFileEngine::cloneTo(QAbstractFileEngine *target)
902{
903 // There's some Windows Server 2016 API, but we won't
904 // bother with it.
905 Q_UNUSED(target);
906 return TriStateResult::NotSupported;
907}
908
909QT_END_NAMESPACE
#define PATH_MAX
static bool isDriveReady(const wchar_t *path)
static bool isUncPath(const QString &path)