1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qplatformdefs.h"
41 #include "private/qabstractfileengine_p.h"
42 #include "private/qfsfileengine_p.h"
43 #include "qfilesystemengine_p.h"
44 #include <qdebug.h>
45 
46 #include "qfile.h"
47 #include "qdir.h"
48 #include "qvarlengtharray.h"
49 #include "qdatetime.h"
50 #include "qt_windows.h"
51 
52 #include <sys/types.h>
53 #include <direct.h>
54 #include <winioctl.h>
55 #include <objbase.h>
56 #ifndef Q_OS_WINRT
57 #  include <shlobj.h>
58 #  include <accctrl.h>
59 #endif
60 #include <initguid.h>
61 #include <ctype.h>
62 #include <limits.h>
63 #include <stdio.h>
64 #ifndef Q_OS_WINRT
65 #  define SECURITY_WIN32
66 #  include <security.h>
67 #endif
68 
69 #ifndef PATH_MAX
70 #define PATH_MAX FILENAME_MAX
71 #endif
72 
73 QT_BEGIN_NAMESPACE
74 
isUncPath(const QString & path)75 static inline bool isUncPath(const QString &path)
76 {
77     // Starts with \\, but not \\.
78     return (path.startsWith(QLatin1String("\\\\"))
79             && path.size() > 2 && path.at(2) != QLatin1Char('.'));
80 }
81 
82 /*!
83     \internal
84 */
longFileName(const QString & path)85 QString QFSFileEnginePrivate::longFileName(const QString &path)
86 {
87     if (path.startsWith(QLatin1String("\\\\.\\")))
88         return path;
89 
90     QString absPath = QFileSystemEngine::nativeAbsoluteFilePath(path);
91 #if !defined(Q_OS_WINRT)
92     QString prefix = QLatin1String("\\\\?\\");
93     if (isUncPath(absPath)) {
94         prefix.append(QLatin1String("UNC\\")); // "\\\\?\\UNC\\"
95         absPath.remove(0, 2);
96     }
97     return prefix + absPath;
98 #else
99     return absPath;
100 #endif
101 }
102 
103 /*
104     \internal
105 */
nativeOpen(QIODevice::OpenMode openMode)106 bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
107 {
108     Q_Q(QFSFileEngine);
109 
110     // All files are opened in share mode (both read and write).
111     DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
112 
113     int accessRights = 0;
114     if (openMode & QIODevice::ReadOnly)
115         accessRights |= GENERIC_READ;
116     if (openMode & QIODevice::WriteOnly)
117         accessRights |= GENERIC_WRITE;
118 
119     // WriteOnly can create files, ReadOnly cannot.
120     DWORD creationDisp = (openMode & QIODevice::NewOnly)
121                             ? CREATE_NEW
122                             : openModeCanCreate(openMode)
123                                 ? OPEN_ALWAYS
124                                 : OPEN_EXISTING;
125     // Create the file handle.
126 #ifndef Q_OS_WINRT
127     SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
128     fileHandle = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(),
129                             accessRights,
130                             shareMode,
131                             &securityAtts,
132                             creationDisp,
133                             FILE_ATTRIBUTE_NORMAL,
134                             NULL);
135 #else // !Q_OS_WINRT
136     fileHandle = CreateFile2((const wchar_t*)fileEntry.nativeFilePath().utf16(),
137                              accessRights,
138                              shareMode,
139                              creationDisp,
140                              NULL);
141 #endif // Q_OS_WINRT
142 
143     // Bail out on error.
144     if (fileHandle == INVALID_HANDLE_VALUE) {
145         q->setError(QFile::OpenError, qt_error_string());
146         return false;
147     }
148 
149     // Truncate the file after successfully opening it if Truncate is passed.
150     if (openMode & QIODevice::Truncate)
151         q->setSize(0);
152 
153     return true;
154 }
155 
156 /*
157     \internal
158 */
nativeClose()159 bool QFSFileEnginePrivate::nativeClose()
160 {
161     Q_Q(QFSFileEngine);
162     if (fh || fd != -1) {
163         // stdlib / stdio mode.
164         return closeFdFh();
165     }
166 
167     // Windows native mode.
168     bool ok = true;
169 
170     if (cachedFd != -1) {
171         if (::_close(cachedFd) && !::CloseHandle(fileHandle)) {
172             q->setError(QFile::UnspecifiedError, qt_error_string());
173             ok = false;
174         }
175 
176         // System handle is closed with associated file descriptor.
177         fileHandle = INVALID_HANDLE_VALUE;
178         cachedFd = -1;
179 
180         return ok;
181     }
182 
183     if ((fileHandle == INVALID_HANDLE_VALUE || !::CloseHandle(fileHandle))) {
184         q->setError(QFile::UnspecifiedError, qt_error_string());
185         ok = false;
186     }
187     fileHandle = INVALID_HANDLE_VALUE;
188     return ok;
189 }
190 
191 /*
192     \internal
193 */
nativeFlush()194 bool QFSFileEnginePrivate::nativeFlush()
195 {
196     if (fh) {
197         // Buffered stdlib mode.
198         return flushFh();
199     }
200     if (fd != -1) {
201         // Unbuffered stdio mode; always succeeds (no buffer).
202         return true;
203     }
204 
205     // Windows native mode; flushing is unnecessary.
206     return true;
207 }
208 
209 /*
210     \internal
211     \since 5.1
212 */
nativeSyncToDisk()213 bool QFSFileEnginePrivate::nativeSyncToDisk()
214 {
215     if (fh || fd != -1) {
216         // stdlib / stdio mode. No API available.
217         return false;
218     }
219     return FlushFileBuffers(fileHandle);
220 }
221 
222 /*
223     \internal
224 */
nativeSize() const225 qint64 QFSFileEnginePrivate::nativeSize() const
226 {
227     Q_Q(const QFSFileEngine);
228     QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
229 
230     // ### Don't flush; for buffered files, we should get away with ftell.
231     thatQ->flush();
232 
233     // Always retrive the current information
234     metaData.clearFlags(QFileSystemMetaData::SizeAttribute);
235     bool filled = false;
236     if (fileHandle != INVALID_HANDLE_VALUE && openMode != QIODevice::NotOpen )
237         filled = QFileSystemEngine::fillMetaData(fileHandle, metaData,
238                                                  QFileSystemMetaData::SizeAttribute);
239     else
240         filled = doStat(QFileSystemMetaData::SizeAttribute);
241 
242     if (!filled) {
243         thatQ->setError(QFile::UnspecifiedError, QSystemError::stdString());
244         return 0;
245     }
246     return metaData.size();
247 }
248 
249 /*
250     \internal
251 */
nativePos() const252 qint64 QFSFileEnginePrivate::nativePos() const
253 {
254     Q_Q(const QFSFileEngine);
255     QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
256 
257     if (fh || fd != -1) {
258         // stdlib / stido mode.
259         return posFdFh();
260     }
261 
262     // Windows native mode.
263     if (fileHandle == INVALID_HANDLE_VALUE)
264         return 0;
265 
266     LARGE_INTEGER currentFilePos;
267     LARGE_INTEGER offset;
268     offset.QuadPart = 0;
269     if (!::SetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_CURRENT)) {
270         thatQ->setError(QFile::UnspecifiedError, qt_error_string());
271         return 0;
272     }
273 
274     return qint64(currentFilePos.QuadPart);
275 }
276 
277 /*
278     \internal
279 */
nativeSeek(qint64 pos)280 bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
281 {
282     Q_Q(QFSFileEngine);
283 
284     if (fh || fd != -1) {
285         // stdlib / stdio mode.
286         return seekFdFh(pos);
287     }
288 
289     LARGE_INTEGER currentFilePos;
290     LARGE_INTEGER offset;
291     offset.QuadPart = pos;
292     if (!::SetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_BEGIN)) {
293         q->setError(QFile::UnspecifiedError, qt_error_string());
294         return false;
295     }
296 
297     return true;
298 }
299 
300 /*
301     \internal
302 */
nativeRead(char * data,qint64 maxlen)303 qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 maxlen)
304 {
305     Q_Q(QFSFileEngine);
306 
307     if (fh || fd != -1) {
308         // stdio / stdlib mode.
309         if (fh && nativeIsSequential() && feof(fh)) {
310             q->setError(QFile::ReadError, QSystemError::stdString());
311             return -1;
312         }
313 
314         return readFdFh(data, maxlen);
315     }
316 
317     // Windows native mode.
318     if (fileHandle == INVALID_HANDLE_VALUE)
319         return -1;
320 
321     qint64 bytesToRead = maxlen;
322 
323     // Reading on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
324     // the chunks are too large, so we limit the block size to 32MB.
325     static const qint64 maxBlockSize = 32 * 1024 * 1024;
326 
327     qint64 totalRead = 0;
328     do {
329         DWORD blockSize = DWORD(qMin(bytesToRead, maxBlockSize));
330         DWORD bytesRead;
331         if (!ReadFile(fileHandle, data + totalRead, blockSize, &bytesRead, NULL)) {
332             if (totalRead == 0) {
333                 // Note: only return failure if the first ReadFile fails.
334                 q->setError(QFile::ReadError, qt_error_string());
335                 return -1;
336             }
337             break;
338         }
339         if (bytesRead == 0)
340             break;
341         totalRead += bytesRead;
342         bytesToRead -= bytesRead;
343     } while (totalRead < maxlen);
344     return totalRead;
345 }
346 
347 /*
348     \internal
349 */
nativeReadLine(char * data,qint64 maxlen)350 qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
351 {
352     Q_Q(QFSFileEngine);
353 
354     if (fh || fd != -1) {
355         // stdio / stdlib mode.
356         return readLineFdFh(data, maxlen);
357     }
358 
359     // Windows native mode.
360     if (fileHandle == INVALID_HANDLE_VALUE)
361         return -1;
362 
363     // ### No equivalent in Win32?
364     return q->QAbstractFileEngine::readLine(data, maxlen);
365 }
366 
367 /*
368     \internal
369 */
nativeWrite(const char * data,qint64 len)370 qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
371 {
372     Q_Q(QFSFileEngine);
373 
374     if (fh || fd != -1) {
375         // stdio / stdlib mode.
376         return writeFdFh(data, len);
377     }
378 
379     // Windows native mode.
380     if (fileHandle == INVALID_HANDLE_VALUE)
381         return -1;
382 
383     qint64 bytesToWrite = len;
384 
385     // Writing on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
386     // the chunks are too large, so we limit the block size to 32MB.
387     qint64 totalWritten = 0;
388     do {
389         const DWORD currentBlockSize = DWORD(qMin(bytesToWrite, qint64(32 * 1024 * 1024)));
390         DWORD bytesWritten;
391         if (!WriteFile(fileHandle, data + totalWritten, currentBlockSize, &bytesWritten, NULL)) {
392             if (totalWritten == 0) {
393                 // Note: Only return error if the first WriteFile failed.
394                 q->setError(QFile::WriteError, qt_error_string());
395                 return -1;
396             }
397             break;
398         }
399         if (bytesWritten == 0)
400             break;
401         totalWritten += bytesWritten;
402         bytesToWrite -= bytesWritten;
403     } while (totalWritten < len);
404     return qint64(totalWritten);
405 }
406 
407 /*
408     \internal
409 */
nativeHandle() const410 int QFSFileEnginePrivate::nativeHandle() const
411 {
412     if (fh || fd != -1)
413         return fh ? QT_FILENO(fh) : fd;
414     if (cachedFd != -1)
415         return cachedFd;
416 
417     int flags = 0;
418     if (openMode & QIODevice::Append)
419         flags |= _O_APPEND;
420     if (!(openMode & QIODevice::WriteOnly))
421         flags |= _O_RDONLY;
422     cachedFd = _open_osfhandle((intptr_t) fileHandle, flags);
423     return cachedFd;
424 }
425 
426 /*
427     \internal
428 */
nativeIsSequential() const429 bool QFSFileEnginePrivate::nativeIsSequential() const
430 {
431 #if !defined(Q_OS_WINRT)
432     HANDLE handle = fileHandle;
433     if (fh || fd != -1)
434         handle = (HANDLE)_get_osfhandle(fh ? QT_FILENO(fh) : fd);
435     if (handle == INVALID_HANDLE_VALUE)
436         return false;
437 
438     DWORD fileType = GetFileType(handle);
439     return (fileType == FILE_TYPE_CHAR)
440             || (fileType == FILE_TYPE_PIPE);
441 #else
442     return false;
443 #endif
444 }
445 
caseSensitive() const446 bool QFSFileEngine::caseSensitive() const
447 {
448     return false;
449 }
450 
currentPath(const QString & fileName)451 QString QFSFileEngine::currentPath(const QString &fileName)
452 {
453 #if !defined(Q_OS_WINRT)
454     QString ret;
455     //if filename is a drive: then get the pwd of that drive
456     if (fileName.length() >= 2 &&
457         fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':')) {
458         int drv = fileName.toUpper().at(0).toLatin1() - 'A' + 1;
459         if (_getdrive() != drv) {
460             wchar_t buf[PATH_MAX];
461             ::_wgetdcwd(drv, buf, PATH_MAX);
462             ret = QString::fromWCharArray(buf);
463         }
464     }
465     if (ret.isEmpty()) {
466         //just the pwd
467         ret = QFileSystemEngine::currentPath().filePath();
468     }
469     if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
470         ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
471     return ret;
472 #else // !Q_OS_WINRT
473     Q_UNUSED(fileName);
474     return QFileSystemEngine::currentPath().filePath();
475 #endif // Q_OS_WINRT
476 }
477 
478 #if !defined(Q_OS_WINRT)
479 // cf QStorageInfo::isReady
isDriveReady(const wchar_t * path)480 static inline bool isDriveReady(const wchar_t *path)
481 {
482     DWORD fileSystemFlags;
483     const UINT driveType = GetDriveType(path);
484     return (driveType != DRIVE_REMOVABLE && driveType != DRIVE_CDROM)
485         || GetVolumeInformation(path, nullptr, 0, nullptr, nullptr,
486                                 &fileSystemFlags, nullptr, 0) == TRUE;
487 }
488 #endif // !Q_OS_WINRT
489 
drives()490 QFileInfoList QFSFileEngine::drives()
491 {
492     QFileInfoList ret;
493 #if !defined(Q_OS_WINRT)
494     const UINT oldErrorMode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
495     quint32 driveBits = (quint32) GetLogicalDrives() & 0x3ffffff;
496     wchar_t driveName[] = L"A:\\";
497 
498     while (driveBits) {
499         if ((driveBits & 1) && isDriveReady(driveName))
500             ret.append(QFileInfo(QString::fromWCharArray(driveName)));
501         driveName[0]++;
502         driveBits = driveBits >> 1;
503     }
504     ::SetErrorMode(oldErrorMode);
505     return ret;
506 #else // !Q_OS_WINRT
507     ret.append(QFileInfo(QLatin1String("/")));
508     return ret;
509 #endif // Q_OS_WINRT
510 }
511 
doStat(QFileSystemMetaData::MetaDataFlags flags) const512 bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const
513 {
514     if (!tried_stat || !metaData.hasFlags(flags)) {
515         tried_stat = true;
516 
517         int localFd = fd;
518         if (fh && fileEntry.isEmpty())
519             localFd = QT_FILENO(fh);
520         if (localFd != -1)
521             QFileSystemEngine::fillMetaData(localFd, metaData, flags);
522         if (metaData.missingFlags(flags) && !fileEntry.isEmpty())
523             QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags));
524     }
525 
526     return metaData.exists();
527 }
528 
529 
link(const QString & newName)530 bool QFSFileEngine::link(const QString &newName)
531 {
532 #if !defined(Q_OS_WINRT)
533     bool ret = false;
534 
535     QString linkName = newName;
536     //### assume that they add .lnk
537 
538     IShellLink *psl;
539     bool neededCoInit = false;
540 
541     HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
542                                     reinterpret_cast<void **>(&psl));
543 
544     if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
545         neededCoInit = true;
546         CoInitialize(nullptr);
547         hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
548                                 reinterpret_cast<void **>(&psl));
549     }
550 
551     if (SUCCEEDED(hres)) {
552         const QString nativeAbsoluteName = fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\'));
553         hres = psl->SetPath(reinterpret_cast<const wchar_t *>(nativeAbsoluteName.utf16()));
554         if (SUCCEEDED(hres)) {
555             const QString nativeAbsolutePathName = fileName(AbsolutePathName).replace(QLatin1Char('/'), QLatin1Char('\\'));
556             hres = psl->SetWorkingDirectory(reinterpret_cast<const wchar_t *>(nativeAbsolutePathName.utf16()));
557             if (SUCCEEDED(hres)) {
558                 IPersistFile *ppf;
559                 hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void **>(&ppf));
560                 if (SUCCEEDED(hres)) {
561                     hres = ppf->Save(reinterpret_cast<const wchar_t *>(linkName.utf16()), TRUE);
562                     if (SUCCEEDED(hres))
563                          ret = true;
564                     ppf->Release();
565                 }
566             }
567         }
568         psl->Release();
569     }
570     if (!ret)
571         setError(QFile::RenameError, qt_error_string());
572 
573     if (neededCoInit)
574         CoUninitialize();
575 
576     return ret;
577 #else // !Q_OS_WINRT
578     Q_UNUSED(newName);
579     Q_UNIMPLEMENTED();
580     return false;
581 #endif // Q_OS_WINRT
582 }
583 
584 /*!
585     \reimp
586 */
fileFlags(QAbstractFileEngine::FileFlags type) const587 QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
588 {
589     Q_D(const QFSFileEngine);
590 
591     if (type & Refresh)
592         d->metaData.clear();
593 
594     QAbstractFileEngine::FileFlags ret;
595 
596     if (type & FlagsMask)
597         ret |= LocalDiskFlag;
598 
599     bool exists;
600     {
601         QFileSystemMetaData::MetaDataFlags queryFlags;
602 
603         queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type))
604                 & QFileSystemMetaData::Permissions;
605 
606         // AliasType and BundleType are 0x0
607         if (type & TypesMask)
608             queryFlags |= QFileSystemMetaData::AliasType
609                     | QFileSystemMetaData::LinkType
610                     | QFileSystemMetaData::FileType
611                     | QFileSystemMetaData::DirectoryType
612                     | QFileSystemMetaData::BundleType;
613 
614         if (type & FlagsMask)
615             queryFlags |= QFileSystemMetaData::HiddenAttribute
616                     | QFileSystemMetaData::ExistsAttribute;
617 
618         queryFlags |= QFileSystemMetaData::LinkType;
619 
620         exists = d->doStat(queryFlags);
621     }
622 
623     if (exists && (type & PermsMask))
624         ret |= FileFlags(uint(d->metaData.permissions()));
625 
626     if (type & TypesMask) {
627         if ((type & LinkType) && d->metaData.isLegacyLink())
628             ret |= LinkType;
629         if (d->metaData.isDirectory()) {
630             ret |= DirectoryType;
631         } else {
632             ret |= FileType;
633         }
634     }
635     if (type & FlagsMask) {
636         if (d->metaData.exists()) {
637             // if we succeeded in querying, then the file exists: a file on
638             // Windows cannot be deleted if we have an open handle to it
639             ret |= ExistsFlag;
640             if (d->fileEntry.isRoot())
641                 ret |= RootFlag;
642             else if (d->metaData.isHidden())
643                 ret |= HiddenFlag;
644         }
645     }
646     return ret;
647 }
648 
id() const649 QByteArray QFSFileEngine::id() const
650 {
651     Q_D(const QFSFileEngine);
652     HANDLE h = d->fileHandle;
653     if (h == INVALID_HANDLE_VALUE) {
654         int localFd = d->fd;
655         if (d->fh && d->fileEntry.isEmpty())
656             localFd = QT_FILENO(d->fh);
657         if (localFd != -1)
658             h = HANDLE(_get_osfhandle(localFd));
659     }
660     if (h != INVALID_HANDLE_VALUE)
661         return QFileSystemEngine::id(h);
662 
663     // file is not open, try by path
664     return QFileSystemEngine::id(d->fileEntry);
665 }
666 
fileName(FileName file) const667 QString QFSFileEngine::fileName(FileName file) const
668 {
669     Q_D(const QFSFileEngine);
670     if (file == BaseName) {
671         return d->fileEntry.fileName();
672     } else if (file == PathName) {
673         return d->fileEntry.path();
674     } else if (file == AbsoluteName || file == AbsolutePathName) {
675         QString ret;
676 
677         if (!isRelativePath()) {
678             if (d->fileEntry.filePath().startsWith(QLatin1Char('/')) || // It's a absolute path to the current drive, so \a.txt -> Z:\a.txt
679                 d->fileEntry.filePath().size() == 2 ||                  // It's a drive letter that needs to get a working dir appended
680                 (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
681                 d->fileEntry.filePath().contains(QLatin1String("/../")) || d->fileEntry.filePath().contains(QLatin1String("/./")) ||
682                 d->fileEntry.filePath().endsWith(QLatin1String("/..")) || d->fileEntry.filePath().endsWith(QLatin1String("/.")))
683             {
684                 ret = QDir::fromNativeSeparators(QFileSystemEngine::nativeAbsoluteFilePath(d->fileEntry.filePath()));
685             } else {
686                 ret = d->fileEntry.filePath();
687             }
688         } else {
689             ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + d->fileEntry.filePath());
690         }
691 
692         // The path should be absolute at this point.
693         // From the docs :
694         // Absolute paths begin with the directory separator "/"
695         // (optionally preceded by a drive specification under Windows).
696         if (ret.at(0) != QLatin1Char('/')) {
697             Q_ASSERT(ret.length() >= 2);
698             Q_ASSERT(ret.at(0).isLetter());
699             Q_ASSERT(ret.at(1) == QLatin1Char(':'));
700 
701             // Force uppercase drive letters.
702             ret[0] = ret.at(0).toUpper();
703         }
704 
705         if (file == AbsolutePathName) {
706             int slash = ret.lastIndexOf(QLatin1Char('/'));
707             if (slash < 0)
708                 return ret;
709             if (ret.at(0) != QLatin1Char('/') && slash == 2)
710                 return ret.left(3);      // include the slash
711             return ret.left(slash > 0 ? slash : 1);
712         }
713         return ret;
714     } else if (file == CanonicalName || file == CanonicalPathName) {
715         if (!(fileFlags(ExistsFlag) & ExistsFlag))
716             return QString();
717         QFileSystemEntry entry(QFileSystemEngine::canonicalName(QFileSystemEntry(fileName(AbsoluteName)), d->metaData));
718 
719         if (file == CanonicalPathName)
720             return entry.path();
721         return entry.filePath();
722     } else if (file == LinkName) {
723         return QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData).filePath();
724     } else if (file == BundleName) {
725         return QString();
726     }
727     return d->fileEntry.filePath();
728 }
729 
isRelativePath() const730 bool QFSFileEngine::isRelativePath() const
731 {
732     Q_D(const QFSFileEngine);
733     // drive, e.g. "a:", or UNC root, e.q. "//"
734     return d->fileEntry.isRelative();
735 }
736 
ownerId(FileOwner) const737 uint QFSFileEngine::ownerId(FileOwner /*own*/) const
738 {
739     static const uint nobodyID = (uint) -2;
740     return nobodyID;
741 }
742 
owner(FileOwner own) const743 QString QFSFileEngine::owner(FileOwner own) const
744 {
745     Q_D(const QFSFileEngine);
746     return QFileSystemEngine::owner(d->fileEntry, own);
747 }
748 
setPermissions(uint perms)749 bool QFSFileEngine::setPermissions(uint perms)
750 {
751     Q_D(QFSFileEngine);
752     QSystemError error;
753     bool ret = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error);
754     if (!ret)
755         setError(QFile::PermissionsError, error.toString());
756     return ret;
757 }
758 
setSize(qint64 size)759 bool QFSFileEngine::setSize(qint64 size)
760 {
761     Q_D(QFSFileEngine);
762 
763     if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1 || d->fh) {
764         // resize open file
765         HANDLE fh = d->fileHandle;
766         if (fh == INVALID_HANDLE_VALUE) {
767             if (d->fh)
768                 fh = (HANDLE)_get_osfhandle(QT_FILENO(d->fh));
769             else
770                 fh = (HANDLE)_get_osfhandle(d->fd);
771         }
772         if (fh == INVALID_HANDLE_VALUE)
773             return false;
774         qint64 currentPos = pos();
775 
776         if (seek(size) && SetEndOfFile(fh)) {
777             seek(qMin(currentPos, size));
778             return true;
779         }
780 
781         seek(currentPos);
782         return false;
783     }
784 
785     if (!d->fileEntry.isEmpty()) {
786         // resize file on disk
787         QFile file(d->fileEntry.filePath());
788         if (file.open(QFile::ReadWrite)) {
789             bool ret = file.resize(size);
790             if (!ret)
791                 setError(QFile::ResizeError, file.errorString());
792             return ret;
793         }
794     }
795     return false;
796 }
797 
setFileTime(const QDateTime & newDate,FileTime time)798 bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time)
799 {
800     Q_D(QFSFileEngine);
801 
802     if (d->openMode == QFile::NotOpen) {
803         setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
804         return false;
805     }
806 
807     if (!newDate.isValid() || time == QAbstractFileEngine::MetadataChangeTime) {
808         setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
809         return false;
810     }
811 
812     HANDLE handle = d->fileHandle;
813     if (handle == INVALID_HANDLE_VALUE) {
814         if (d->fh)
815             handle = reinterpret_cast<HANDLE>(::_get_osfhandle(QT_FILENO(d->fh)));
816         else if (d->fd != -1)
817             handle = reinterpret_cast<HANDLE>(::_get_osfhandle(d->fd));
818     }
819 
820     if (handle == INVALID_HANDLE_VALUE) {
821         setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
822         return false;
823     }
824 
825     QSystemError error;
826     if (!QFileSystemEngine::setFileTime(handle, newDate, time, error)) {
827         setError(QFile::PermissionsError, error.toString());
828         return false;
829     }
830 
831     d->metaData.clearFlags(QFileSystemMetaData::Times);
832     return true;
833 }
834 
map(qint64 offset,qint64 size,QFile::MemoryMapFlags flags)835 uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
836                                  QFile::MemoryMapFlags flags)
837 {
838     Q_Q(QFSFileEngine);
839     Q_UNUSED(flags);
840     if (openMode == QFile::NotOpen) {
841         q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
842         return 0;
843     }
844     if (offset == 0 && size == 0) {
845         q->setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
846         return 0;
847     }
848 
849     // check/setup args to map
850     DWORD access = 0;
851     if (flags & QFileDevice::MapPrivateOption) {
852 #ifdef FILE_MAP_COPY
853         access = FILE_MAP_COPY;
854 #else
855         q->setError(QFile::UnspecifiedError, "MapPrivateOption unsupported");
856         return 0;
857 #endif
858     } else if (openMode & QIODevice::WriteOnly) {
859         access = FILE_MAP_WRITE;
860     } else if (openMode & QIODevice::ReadOnly) {
861         access = FILE_MAP_READ;
862     }
863 
864     if (mapHandle == NULL) {
865         // get handle to the file
866         HANDLE handle = fileHandle;
867 
868         if (handle == INVALID_HANDLE_VALUE && fh)
869             handle = (HANDLE)::_get_osfhandle(QT_FILENO(fh));
870 
871 #ifdef Q_USE_DEPRECATED_MAP_API
872         nativeClose();
873         // handle automatically closed by kernel with mapHandle (below).
874         handle = ::CreateFileForMapping((const wchar_t*)fileEntry.nativeFilePath().utf16(),
875                 GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0),
876                 0,
877                 NULL,
878                 OPEN_EXISTING,
879                 FILE_ATTRIBUTE_NORMAL,
880                 NULL);
881         // Since this is a special case, we check if the return value was NULL and if so
882         // we change it to INVALID_HANDLE_VALUE to follow the logic inside this function.
883         if(0 == handle)
884             handle = INVALID_HANDLE_VALUE;
885 #endif
886 
887         if (handle == INVALID_HANDLE_VALUE) {
888             q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
889             return 0;
890         }
891 
892         // first create the file mapping handle
893         DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY;
894 #ifndef Q_OS_WINRT
895         mapHandle = ::CreateFileMapping(handle, 0, protection, 0, 0, 0);
896 #else
897         mapHandle = ::CreateFileMappingFromApp(handle, 0, protection, 0, 0);
898 #endif
899         if (mapHandle == NULL) {
900             q->setError(QFile::PermissionsError, qt_error_string());
901 #ifdef Q_USE_DEPRECATED_MAP_API
902             ::CloseHandle(handle);
903 #endif
904             return 0;
905         }
906     }
907 
908     DWORD offsetHi = offset >> 32;
909     DWORD offsetLo = offset & Q_UINT64_C(0xffffffff);
910     SYSTEM_INFO sysinfo;
911 #ifndef Q_OS_WINRT
912     ::GetSystemInfo(&sysinfo);
913 #else
914     ::GetNativeSystemInfo(&sysinfo);
915 #endif
916     DWORD mask = sysinfo.dwAllocationGranularity - 1;
917     DWORD extra = offset & mask;
918     if (extra)
919         offsetLo &= ~mask;
920 
921     // attempt to create the map
922 #ifndef Q_OS_WINRT
923     LPVOID mapAddress = ::MapViewOfFile(mapHandle, access,
924                                       offsetHi, offsetLo, size + extra);
925 #else
926     LPVOID mapAddress = ::MapViewOfFileFromApp(mapHandle, access,
927                                                (ULONG64(offsetHi) << 32) + offsetLo, size + extra);
928 #endif
929     if (mapAddress) {
930         uchar *address = extra + static_cast<uchar*>(mapAddress);
931         maps[address] = extra;
932         return address;
933     }
934 
935     switch(GetLastError()) {
936     case ERROR_ACCESS_DENIED:
937         q->setError(QFile::PermissionsError, qt_error_string());
938         break;
939     case ERROR_INVALID_PARAMETER:
940         // size are out of bounds
941     default:
942         q->setError(QFile::UnspecifiedError, qt_error_string());
943     }
944 
945     ::CloseHandle(mapHandle);
946     mapHandle = NULL;
947     return 0;
948 }
949 
unmap(uchar * ptr)950 bool QFSFileEnginePrivate::unmap(uchar *ptr)
951 {
952     Q_Q(QFSFileEngine);
953     if (!maps.contains(ptr)) {
954         q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
955         return false;
956     }
957     uchar *start = ptr - maps[ptr];
958     if (!UnmapViewOfFile(start)) {
959         q->setError(QFile::PermissionsError, qt_error_string());
960         return false;
961     }
962 
963     maps.remove(ptr);
964     if (maps.isEmpty()) {
965         ::CloseHandle(mapHandle);
966         mapHandle = NULL;
967     }
968 
969     return true;
970 }
971 
972 /*!
973     \reimp
974 */
cloneTo(QAbstractFileEngine * target)975 bool QFSFileEngine::cloneTo(QAbstractFileEngine *target)
976 {
977     // There's some Windows Server 2016 API, but we won't
978     // bother with it.
979     Q_UNUSED(target);
980     return false;
981 }
982 
983 QT_END_NAMESPACE
984