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, ¤tFilePos, 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, ¤tFilePos, 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