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