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.hasFlags(QFileSystemMetaData::ExistsAttribute) && 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 = {};
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 we succeeded in querying, then the file exists: a file on
546 // Windows cannot be deleted if we have an open handle to it
547 if (!exists)
548 return ret;
549
550 if (exists && (type & PermsMask))
551 ret |= FileFlags::fromInt(d->metaData.permissions().toInt());
552
553 if (type & TypesMask) {
554 if ((type & LinkType) && d->metaData.isLegacyLink())
555 ret |= LinkType;
556 if (d->metaData.isDirectory()) {
557 ret |= DirectoryType;
558 } else {
559 ret |= FileType;
560 }
561 }
562 if (type & FlagsMask) {
563 if (d->metaData.exists()) {
564 ret |= ExistsFlag;
565 if (d->fileEntry.isRoot())
566 ret |= RootFlag;
567 else if (d->metaData.isHidden())
568 ret |= HiddenFlag;
569 }
570 }
571 return ret;
572}
573
574QByteArray QFSFileEngine::id() const
575{
576 Q_D(const QFSFileEngine);
577 HANDLE h = d->fileHandle;
578 if (h == INVALID_HANDLE_VALUE) {
579 int localFd = d->fd;
580 if (d->fh && d->fileEntry.isEmpty())
581 localFd = QT_FILENO(d->fh);
582 if (localFd != -1)
583 h = HANDLE(_get_osfhandle(localFd));
584 }
585 if (h != INVALID_HANDLE_VALUE)
586 return QFileSystemEngine::id(h);
587
588 // file is not open, try by path
589 return QFileSystemEngine::id(d->fileEntry);
590}
591
592QString QFSFileEngine::fileName(FileName file) const
593{
594 Q_D(const QFSFileEngine);
595 switch (file) {
596 case BaseName:
597 return d->fileEntry.fileName();
598 case PathName:
599 return d->fileEntry.path();
600 case AbsoluteName:
601 case AbsolutePathName: {
602 QString ret = d->fileEntry.filePath();
603 if (isRelativePath()) {
604 ret = QDir::cleanPath(QDir::currentPath() + u'/' + ret);
605 } else if (ret.startsWith(u'/') // absolute path to the current drive, so \a.txt -> Z:\a.txt
606 || ret.size() == 2 // or a drive letter that needs to get a working dir appended
607 // or a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt
608 || (ret.size() > 2 && ret.at(2) != u'/')
609 || ret.contains(QStringView(u"/../"))
610 || ret.contains(QStringView(u"/./"))
611 || ret.endsWith(QStringView(u"/.."))
612 || ret.endsWith(QStringView(u"/."))) {
613 ret = QDir::fromNativeSeparators(QFileSystemEngine::nativeAbsoluteFilePath(ret));
614 }
615
616 // The path should be absolute at this point.
617 // From the docs :
618 // Absolute paths begin with the directory separator "/"
619 // (optionally preceded by a drive specification under Windows).
620 if (ret.at(0) != u'/') {
621 Q_ASSERT(ret.length() >= 2);
622 Q_ASSERT(ret.at(0).isLetter());
623 Q_ASSERT(ret.at(1) == u':');
624
625 // Force uppercase drive letters.
626 ret[0] = ret.at(0).toUpper();
627 }
628
629 if (file == AbsolutePathName) {
630 int slash = ret.lastIndexOf(u'/');
631 if (slash < 0)
632 return ret;
633 if (ret.at(0) != u'/' && slash == 2)
634 return ret.left(3); // include the slash
635 return ret.left(slash > 0 ? slash : 1);
636 }
637 return ret;
638 }
639 case CanonicalName:
640 case CanonicalPathName: {
641 if (!(fileFlags(ExistsFlag) & ExistsFlag))
642 return QString();
643 const QFileSystemEntry entry =
644 QFileSystemEngine::canonicalName(QFileSystemEntry(fileName(AbsoluteName)), d->metaData);
645
646 if (file == CanonicalPathName)
647 return entry.path();
648 return entry.filePath();
649 }
650 case AbsoluteLinkTarget:
651 return QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData).filePath();
652 case RawLinkPath:
653 return QFileSystemEngine::getRawLinkPath(d->fileEntry, d->metaData).filePath();
654 case BundleName:
655 return QString();
656 case JunctionName:
657 return QFileSystemEngine::getJunctionTarget(d->fileEntry, d->metaData).filePath();
658 case DefaultName:
659 break;
660 case NFileNames:
661 Q_ASSERT(false);
662 break;
663 }
664 return d->fileEntry.filePath();
665}
666
667bool QFSFileEngine::isRelativePath() const
668{
669 Q_D(const QFSFileEngine);
670 // drive, e.g. "a:", or UNC root, e.q. "//"
671 return d->fileEntry.isRelative();
672}
673
674uint QFSFileEngine::ownerId(FileOwner /*own*/) const
675{
676 static const uint nobodyID = (uint) -2;
677 return nobodyID;
678}
679
680QString QFSFileEngine::owner(FileOwner own) const
681{
682 Q_D(const QFSFileEngine);
683 return QFileSystemEngine::owner(d->fileEntry, own);
684}
685
686bool QFSFileEngine::setPermissions(uint perms)
687{
688 Q_D(QFSFileEngine);
689 QSystemError error;
690
691 // clear cached state (if any)
692 d->metaData.clearFlags(QFileSystemMetaData::Permissions);
693
694 bool ret = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error);
695 if (!ret)
696 setError(QFile::PermissionsError, error.toString());
697 return ret;
698}
699
700bool QFSFileEngine::setSize(qint64 size)
701{
702 Q_D(QFSFileEngine);
703
704 if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1 || d->fh) {
705 // resize open file
706 HANDLE fh = d->fileHandle;
707 if (fh == INVALID_HANDLE_VALUE) {
708 if (d->fh)
709 fh = (HANDLE)_get_osfhandle(QT_FILENO(d->fh));
710 else
711 fh = (HANDLE)_get_osfhandle(d->fd);
712 }
713 if (fh == INVALID_HANDLE_VALUE)
714 return false;
715 qint64 currentPos = pos();
716
717 if (seek(size) && SetEndOfFile(fh)) {
718 seek(qMin(currentPos, size));
719 return true;
720 }
721
722 seek(currentPos);
723 return false;
724 }
725
726 if (!d->fileEntry.isEmpty()) {
727 // resize file on disk
728 QFile file(d->fileEntry.filePath());
729 if (file.open(QFile::ReadWrite)) {
730 bool ret = file.resize(size);
731 if (!ret)
732 setError(QFile::ResizeError, file.errorString());
733 return ret;
734 }
735 }
736 return false;
737}
738
739bool QFSFileEngine::setFileTime(const QDateTime &newDate, QFile::FileTime time)
740{
741 Q_D(QFSFileEngine);
742
743 if (d->openMode == QFile::NotOpen) {
744 setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
745 return false;
746 }
747
748 if (!newDate.isValid() || time == QFile::FileMetadataChangeTime) {
749 setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
750 return false;
751 }
752
753 HANDLE handle = d->fileHandle;
754 if (handle == INVALID_HANDLE_VALUE) {
755 if (d->fh)
756 handle = reinterpret_cast<HANDLE>(::_get_osfhandle(QT_FILENO(d->fh)));
757 else if (d->fd != -1)
758 handle = reinterpret_cast<HANDLE>(::_get_osfhandle(d->fd));
759 }
760
761 if (handle == INVALID_HANDLE_VALUE) {
762 setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
763 return false;
764 }
765
766 QSystemError error;
767 if (!QFileSystemEngine::setFileTime(handle, newDate, time, error)) {
768 setError(QFile::PermissionsError, error.toString());
769 return false;
770 }
771
772 d->metaData.clearFlags(QFileSystemMetaData::Times);
773 return true;
774}
775
776uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
777 QFile::MemoryMapFlags flags)
778{
779 Q_Q(QFSFileEngine);
780 Q_UNUSED(flags);
781 if (openMode == QFile::NotOpen) {
782 q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
783 return 0;
784 }
785 if (offset == 0 && size == 0) {
786 q->setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
787 return 0;
788 }
789
790 // check/setup args to map
791 DWORD access = 0;
792 if (flags & QFileDevice::MapPrivateOption) {
793#ifdef FILE_MAP_COPY
794 access = FILE_MAP_COPY;
795#else
796 q->setError(QFile::UnspecifiedError, "MapPrivateOption unsupported");
797 return 0;
798#endif
799 } else if (openMode & QIODevice::WriteOnly) {
800 access = FILE_MAP_WRITE;
801 } else if (openMode & QIODevice::ReadOnly) {
802 access = FILE_MAP_READ;
803 }
804
805 if (mapHandle == NULL) {
806 // get handle to the file
807 HANDLE handle = fileHandle;
808
809 if (handle == INVALID_HANDLE_VALUE && fh)
810 handle = (HANDLE)::_get_osfhandle(QT_FILENO(fh));
811
812#ifdef Q_USE_DEPRECATED_MAP_API
813 nativeClose();
814 // handle automatically closed by kernel with mapHandle (below).
815 handle = ::CreateFileForMapping((const wchar_t*)fileEntry.nativeFilePath().utf16(),
816 GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0),
817 0,
818 NULL,
819 OPEN_EXISTING,
820 FILE_ATTRIBUTE_NORMAL,
821 NULL);
822 // Since this is a special case, we check if the return value was NULL and if so
823 // we change it to INVALID_HANDLE_VALUE to follow the logic inside this function.
824 if (!handle)
825 handle = INVALID_HANDLE_VALUE;
826#endif
827
828 if (handle == INVALID_HANDLE_VALUE) {
829 q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
830 return 0;
831 }
832
833 // first create the file mapping handle
834 DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY;
835 mapHandle = ::CreateFileMapping(handle, 0, protection, 0, 0, 0);
836 if (mapHandle == NULL) {
837 q->setError(QFile::PermissionsError, qt_error_string());
838#ifdef Q_USE_DEPRECATED_MAP_API
839 ::CloseHandle(handle);
840#endif
841 return 0;
842 }
843 }
844
845 DWORD offsetHi = offset >> 32;
846 DWORD offsetLo = offset & Q_UINT64_C(0xffffffff);
847 SYSTEM_INFO sysinfo;
848 ::GetSystemInfo(&sysinfo);
849 DWORD mask = sysinfo.dwAllocationGranularity - 1;
850 DWORD extra = offset & mask;
851 if (extra)
852 offsetLo &= ~mask;
853
854 // attempt to create the map
855 LPVOID mapAddress = ::MapViewOfFile(mapHandle, access,
856 offsetHi, offsetLo, size + extra);
857 if (mapAddress) {
858 uchar *address = extra + static_cast<uchar*>(mapAddress);
859 maps[address] = extra;
860 return address;
861 }
862
863 switch(GetLastError()) {
864 case ERROR_ACCESS_DENIED:
865 q->setError(QFile::PermissionsError, qt_error_string());
866 break;
867 case ERROR_INVALID_PARAMETER:
868 // size are out of bounds
869 default:
870 q->setError(QFile::UnspecifiedError, qt_error_string());
871 }
872
873 ::CloseHandle(mapHandle);
874 mapHandle = NULL;
875 return 0;
876}
877
878bool QFSFileEnginePrivate::unmap(uchar *ptr)
879{
880 Q_Q(QFSFileEngine);
881 const auto it = std::as_const(maps).find(ptr);
882 if (it == maps.cend()) {
883 q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
884 return false;
885 }
886 uchar *start = ptr - *it;
887 if (!UnmapViewOfFile(start)) {
888 q->setError(QFile::PermissionsError, qt_error_string());
889 return false;
890 }
891
892 maps.erase(it);
893 if (maps.isEmpty()) {
894 ::CloseHandle(mapHandle);
895 mapHandle = NULL;
896 }
897
898 return true;
899}
900
901/*!
902 \reimp
903*/
904QAbstractFileEngine::TriStateResult QFSFileEngine::cloneTo(QAbstractFileEngine *target)
905{
906 // There's some Windows Server 2016 API, but we won't
907 // bother with it.
908 Q_UNUSED(target);
909 return TriStateResult::NotSupported;
910}
911
912QT_END_NAMESPACE
Combined button and popup list for selecting options.
#define PATH_MAX
static bool isDriveReady(const wchar_t *path)
static bool isUncPath(const QString &path)