1 /*
2     SPDX-FileCopyrightText: 2009 Nokia Corporation and /or its subsidiary(-ies).
3     Contact: Qt Software Information (qt-info@nokia.com)
4 
5     This file is part of the QtCore module of the Qt Toolkit.
6 
7     $QT_BEGIN_LICENSE:LGPL$
8     Commercial Usage
9     Licensees holding valid Qt Commercial licenses may use this file in
10     accordance with the Qt Commercial License Agreement provided with the
11     Software or, alternatively, in accordance with the terms contained in
12     a written agreement between you and Nokia.
13 
14     GNU Lesser General Public License Usage
15     Alternatively, this file may be used under the terms of the GNU Lesser
16     General Public License version 2.1 as published by the Free Software
17     Foundation and appearing in the file LICENSE.LGPL included in the
18     packaging of this file.  Please review the following information to
19     ensure the GNU Lesser General Public License version 2.1 requirements
20     will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
21 
22     In addition, as a special exception, Nokia gives you certain
23     additional rights. These rights are described in the Nokia Qt LGPL
24     Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
25     package.
26 
27     GNU General Public License Usage
28     Alternatively, this file may be used under the terms of the GNU
29     General Public License version 3.0 as published by the Free Software
30     Foundation and appearing in the file LICENSE.GPL included in the
31     packaging of this file.  Please review the following information to
32     ensure the GNU General Public License version 3.0 requirements will be
33     met: https://www.gnu.org/licenses/gpl-3.0.html.
34 
35     If you are unsure which license is appropriate for your use, please
36     contact the sales department at qt-sales@nokia.com.
37     $QT_END_LICENSE$
38 
39 */
40 
41 #define _POSIX_
42 #include "qplatformdefs.h"
43 #include "qabstractfileengine.h"
44 #include "private/qfsfileengine_p.h"
45 #include <qdebug.h>
46 
47 #include "QtCore/QFile"
48 #include "QtCore/QDir"
49 #include "qtemporaryfile.h"
50 #ifndef QT_NO_REGEXP
51 # include "QtCore/QRegExp"
52 #endif
53 #include "private/qmutexpool_p.h"
54 #include "qvarlengtharray.h"
55 #include "qdatetime.h"
56 #include "qt_windows.h"
57 
58 #if !defined(Q_OS_WINCE)
59 #  include <sys/types.h>
60 #  include <direct.h>
61 #else
62 #  include <types.h>
63 #endif
64 #include <objbase.h>
65 #include <shlobj.h>
66 #include <initguid.h>
67 #include <accctrl.h>
68 #include <ctype.h>
69 #include <limits.h>
70 #define SECURITY_WIN32
71 #ifdef Q_CC_MINGW
72 // A workaround for a certain version of MinGW, the define UNICODE_STRING.
73 #include <subauth.h>
74 #endif
75 #include <security.h>
76 
77 #ifndef _INTPTR_T_DEFINED
78 #ifdef  _WIN64
79 typedef __int64             intptr_t;
80 #else
81 #ifdef _W64
82 typedef _W64 int            intptr_t;
83 #else
84 typedef INT_PTR intptr_t;
85 #endif
86 #endif
87 #define _INTPTR_T_DEFINED
88 #endif
89 
90 #ifndef INVALID_FILE_ATTRIBUTES
91 #  define INVALID_FILE_ATTRIBUTES (DWORD (-1))
92 #endif
93 
94 QT_BEGIN_NAMESPACE
95 
96 static QString readLink(const QString &link);
97 
98 LIBK3B_EXPORT int qt_ntfs_permission_lookup = 0;
99 
100 #if defined(Q_OS_WINCE)
101 static QString qfsPrivateCurrentDir = QLatin1String("");
102 // As none of the functions we try to resolve do exist on Windows CE
103 // we use QT_NO_LIBRARY to shorten everything up a little bit.
104 #define QT_NO_LIBRARY 1
105 #endif
106 
107 #if !defined(QT_NO_LIBRARY)
108 QT_BEGIN_INCLUDE_NAMESPACE
109 typedef DWORD (WINAPI *PtrGetNamedSecurityInfoW)(LPWSTR, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID*, PSID*, PACL*, PACL*, PSECURITY_DESCRIPTOR*);
110 static PtrGetNamedSecurityInfoW ptrGetNamedSecurityInfoW = 0;
111 typedef DECLSPEC_IMPORT BOOL (WINAPI *PtrLookupAccountSidW)(LPCWSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, PSID_NAME_USE);
112 static PtrLookupAccountSidW ptrLookupAccountSidW = 0;
113 typedef DECLSPEC_IMPORT BOOL (WINAPI *PtrAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*);
114 static PtrAllocateAndInitializeSid ptrAllocateAndInitializeSid = 0;
115 typedef VOID (WINAPI *PtrBuildTrusteeWithSidW)(PTRUSTEE_W, PSID);
116 static PtrBuildTrusteeWithSidW ptrBuildTrusteeWithSidW = 0;
117 typedef VOID (WINAPI *PtrBuildTrusteeWithNameW)(PTRUSTEE_W, unsigned short*);
118 static PtrBuildTrusteeWithNameW ptrBuildTrusteeWithNameW = 0;
119 typedef DWORD (WINAPI *PtrGetEffectiveRightsFromAclW)(PACL, PTRUSTEE_W, OUT PACCESS_MASK);
120 static PtrGetEffectiveRightsFromAclW ptrGetEffectiveRightsFromAclW = 0;
121 typedef DECLSPEC_IMPORT PVOID (WINAPI *PtrFreeSid)(PSID);
122 static PtrFreeSid ptrFreeSid = 0;
123 static TRUSTEE_W currentUserTrusteeW;
124 
125 typedef BOOL (WINAPI *PtrOpenProcessToken)(HANDLE, DWORD, PHANDLE );
126 static PtrOpenProcessToken ptrOpenProcessToken = 0;
127 typedef BOOL (WINAPI *PtrGetUserProfileDirectoryW)( HANDLE, LPWSTR, LPDWORD);
128 static PtrGetUserProfileDirectoryW ptrGetUserProfileDirectoryW = 0;
129 typedef BOOL (WINAPI *PtrSetFilePointerEx)(HANDLE, LARGE_INTEGER, PLARGE_INTEGER, DWORD);
130 static PtrSetFilePointerEx ptrSetFilePointerEx = 0;
131 QT_END_INCLUDE_NAMESPACE
132 
133 
resolveLibs()134 void QFSFileEnginePrivate::resolveLibs()
135 {
136     static bool triedResolve = false;
137     if(!triedResolve) {
138         // need to resolve the security info functions
139 
140         // protect initialization
141 #ifndef QT_NO_THREAD
142         QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
143         // check triedResolve again, since another thread may have already
144         // done the initialization
145         if(triedResolve) {
146             // another thread did initialize the security function pointers,
147             // so we shouldn't do it again.
148             return;
149         }
150 #endif
151 
152         triedResolve = true;
153 #if !defined(Q_OS_WINCE)
154         if(QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) {
155             HINSTANCE advapiHnd = LoadLibraryW(L"advapi32");
156             if (advapiHnd) {
157                 ptrGetNamedSecurityInfoW = (PtrGetNamedSecurityInfoW)GetProcAddress(advapiHnd, "GetNamedSecurityInfoW");
158                 ptrLookupAccountSidW = (PtrLookupAccountSidW)GetProcAddress(advapiHnd, "LookupAccountSidW");
159                 ptrAllocateAndInitializeSid = (PtrAllocateAndInitializeSid)GetProcAddress(advapiHnd, "AllocateAndInitializeSid");
160                 ptrBuildTrusteeWithSidW = (PtrBuildTrusteeWithSidW)GetProcAddress(advapiHnd, "BuildTrusteeWithSidW");
161                 ptrBuildTrusteeWithNameW = (PtrBuildTrusteeWithNameW)GetProcAddress(advapiHnd, "BuildTrusteeWithNameW");
162                 ptrGetEffectiveRightsFromAclW = (PtrGetEffectiveRightsFromAclW)GetProcAddress(advapiHnd, "GetEffectiveRightsFromAclW");
163                 ptrFreeSid = (PtrFreeSid)GetProcAddress(advapiHnd, "FreeSid");
164             }
165             if (ptrBuildTrusteeWithNameW) {
166                 HINSTANCE versionHnd = LoadLibraryW(L"version");
167                 if (versionHnd) {
168                     typedef DWORD (WINAPI *PtrGetFileVersionInfoSizeW)(LPWSTR lptstrFilename,LPDWORD lpdwHandle);
169                     PtrGetFileVersionInfoSizeW ptrGetFileVersionInfoSizeW = (PtrGetFileVersionInfoSizeW)GetProcAddress(versionHnd, "GetFileVersionInfoSizeW");
170                     typedef BOOL (WINAPI *PtrGetFileVersionInfoW)(LPWSTR lptstrFilename,DWORD dwHandle,DWORD dwLen,LPVOID lpData);
171                     PtrGetFileVersionInfoW ptrGetFileVersionInfoW = (PtrGetFileVersionInfoW)GetProcAddress(versionHnd, "GetFileVersionInfoW");
172                     typedef BOOL (WINAPI *PtrVerQueryValueW)(const LPVOID pBlock,LPWSTR lpSubBlock,LPVOID *lplpBuffer,PUINT puLen);
173                     PtrVerQueryValueW ptrVerQueryValueW = (PtrVerQueryValueW)GetProcAddress(versionHnd, "VerQueryValueW");
174                     if(ptrGetFileVersionInfoSizeW && ptrGetFileVersionInfoW && ptrVerQueryValueW) {
175                         DWORD fakeHandle;
176                         DWORD versionSize = ptrGetFileVersionInfoSizeW(L"secur32.dll", &fakeHandle);
177                         if(versionSize) {
178                             LPVOID versionData;
179                             versionData = malloc(versionSize);
180                             if(ptrGetFileVersionInfoW(L"secur32.dll", 0, versionSize, versionData)) {
181                                 UINT puLen;
182                                 VS_FIXEDFILEINFO *pLocalInfo;
183                                 if(ptrVerQueryValueW(versionData, L"\\", (void**)&pLocalInfo, &puLen)) {
184                                     WORD wVer1, wVer2, wVer3, wVer4;
185                                     wVer1 = HIWORD(pLocalInfo->dwFileVersionMS);
186                                     wVer2 = LOWORD(pLocalInfo->dwFileVersionMS);
187                                     wVer3 = HIWORD(pLocalInfo->dwFileVersionLS);
188                                     wVer4 = LOWORD(pLocalInfo->dwFileVersionLS);
189                                     // It will not work with secur32.dll version 5.0.2195.2862
190                                     if(!(wVer1 == 5 && wVer2 == 0 && wVer3 == 2195 && (wVer4 == 2862 || wVer4 == 4587))) {
191                                         HINSTANCE userHnd = LoadLibraryW(L"secur32");
192                                         if (userHnd) {
193                                             typedef BOOL (WINAPI *PtrGetUserNameExW)(EXTENDED_NAME_FORMAT nameFormat, ushort* lpBuffer, LPDWORD nSize);
194                                             PtrGetUserNameExW ptrGetUserNameExW = (PtrGetUserNameExW)GetProcAddress(userHnd, "GetUserNameExW");
195                                             if(ptrGetUserNameExW) {
196                                                 static TCHAR buffer[258];
197                                                 DWORD bufferSize = 257;
198                                                 ptrGetUserNameExW(NameSamCompatible, (ushort*)buffer, &bufferSize);
199                                                 ptrBuildTrusteeWithNameW(&currentUserTrusteeW, (ushort*)buffer);
200                                             }
201                                             FreeLibrary(userHnd);
202                                         }
203                                     }
204                                 }
205                             }
206                             free(versionData);
207                         }
208                     }
209                     FreeLibrary(versionHnd);
210                 }
211             }
212             ptrOpenProcessToken = (PtrOpenProcessToken)GetProcAddress(advapiHnd, "OpenProcessToken");
213 	        HINSTANCE userenvHnd = LoadLibraryW(L"userenv");
214             if (userenvHnd) {
215                 ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW");
216             }
217             HINSTANCE kernelHnd = LoadLibraryW(L"kernel32");
218             if (kernelHnd)
219                 ptrSetFilePointerEx = (PtrSetFilePointerEx)GetProcAddress(kernelHnd, "SetFilePointerEx");
220         }
221 #endif
222     }
223 }
224 #endif // QT_NO_LIBRARY
225 
226 // UNC functions NT
227 typedef DWORD (WINAPI *PtrNetShareEnum_NT)(LPWSTR, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD);
228 static PtrNetShareEnum_NT ptrNetShareEnum_NT = 0;
229 typedef DWORD (WINAPI *PtrNetApiBufferFree_NT)(LPVOID);
230 static PtrNetApiBufferFree_NT ptrNetApiBufferFree_NT = 0;
231 typedef struct _SHARE_INFO_1_NT {
232     LPWSTR shi1_netname;
233     DWORD shi1_type;
234     LPWSTR shi1_remark;
235 } SHARE_INFO_1_NT;
236 
237 
resolveUNCLibs_NT()238 bool QFSFileEnginePrivate::resolveUNCLibs_NT()
239 {
240     static bool triedResolve = false;
241     if (!triedResolve) {
242 #ifndef QT_NO_THREAD
243         QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
244         if (triedResolve) {
245             return ptrNetShareEnum_NT && ptrNetApiBufferFree_NT;
246         }
247 #endif
248         triedResolve = true;
249 #if !defined(Q_OS_WINCE)
250         HINSTANCE hLib = LoadLibraryW(L"Netapi32");
251         if (hLib) {
252             ptrNetShareEnum_NT = (PtrNetShareEnum_NT)GetProcAddress(hLib, "NetShareEnum");
253             if (ptrNetShareEnum_NT)
254                 ptrNetApiBufferFree_NT = (PtrNetApiBufferFree_NT)GetProcAddress(hLib, "NetApiBufferFree");
255         }
256 #endif
257     }
258     return ptrNetShareEnum_NT && ptrNetApiBufferFree_NT;
259 }
260 
261 // UNC functions 9x
262 typedef DWORD (WINAPI *PtrNetShareEnum_9x)(const char FAR *, short, char FAR *, unsigned short, unsigned short FAR *, unsigned short FAR *);
263 static PtrNetShareEnum_9x ptrNetShareEnum_9x = 0;
264 #ifdef LM20_NNLEN
265 # define LM20_NNLEN_9x LM20_NNLEN
266 #else
267 # define LM20_NNLEN_9x 12
268 #endif
269 typedef struct _SHARE_INFO_1_9x {
270   char shi1_netname[LM20_NNLEN_9x+1];
271   char shi1_pad1;
272   unsigned short shi1_type;
273   char FAR* shi1_remark;
274 } SHARE_INFO_1_9x;
275 
resolveUNCLibs_9x()276 bool QFSFileEnginePrivate::resolveUNCLibs_9x()
277 {
278     static bool triedResolve = false;
279     if (!triedResolve) {
280 #ifndef QT_NO_THREAD
281         QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
282         if (triedResolve) {
283             return ptrNetShareEnum_9x;
284         }
285 #endif
286         triedResolve = true;
287 #if !defined(Q_OS_WINCE)
288         HINSTANCE hLib = LoadLibraryA("Svrapi");
289         if (hLib)
290             ptrNetShareEnum_9x = (PtrNetShareEnum_9x)GetProcAddress(hLib, "NetShareEnum");
291 #endif
292     }
293     return ptrNetShareEnum_9x;
294 }
295 
uncListSharesOnServer(const QString & server,QStringList * list)296 bool QFSFileEnginePrivate::uncListSharesOnServer(const QString &server, QStringList *list)
297 {
298     if (resolveUNCLibs_NT()) {
299         SHARE_INFO_1_NT *BufPtr, *p;
300         DWORD res;
301         DWORD er=0,tr=0,resume=0, i;
302         do {
303             res = ptrNetShareEnum_NT((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume);
304             if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) {
305                 p=BufPtr;
306                 for (i = 1; i <= er; ++i) {
307                     if (list && p->shi1_type == 0)
308                         list->append(QString::fromUtf16((unsigned short *)p->shi1_netname));
309                     p++;
310                 }
311             }
312             ptrNetApiBufferFree_NT(BufPtr);
313         } while (res==ERROR_MORE_DATA);
314         return res == ERROR_SUCCESS;
315 
316     } else if (resolveUNCLibs_9x()) {
317         SHARE_INFO_1_9x *pBuf = 0;
318         short cbBuffer;
319         unsigned short nEntriesRead = 0;
320         unsigned short nTotalEntries = 0;
321         short numBuffs = 20;
322         DWORD nStatus = 0;
323         do {
324             cbBuffer = numBuffs * sizeof(SHARE_INFO_1_9x);
325             pBuf = (SHARE_INFO_1_9x *)malloc(cbBuffer);
326             if (pBuf) {
327                 nStatus = ptrNetShareEnum_9x(server.toLocal8Bit().constData(), 1, (char FAR *)pBuf, cbBuffer, &nEntriesRead, &nTotalEntries);
328                 if ((nStatus == ERROR_SUCCESS)) {
329                     for (int i = 0; i < nEntriesRead; ++i) {
330                         if (list && pBuf[i].shi1_type == 0)
331                             list->append(QString::fromLocal8Bit(pBuf[i].shi1_netname));
332                     }
333                     free(pBuf);
334                     break;
335                 }
336                 free(pBuf);
337                 numBuffs *=2;
338             }
339         } while (nStatus == ERROR_MORE_DATA);
340         return nStatus == ERROR_SUCCESS;
341     }
342     return false;
343 }
344 
isUncRoot(const QString & server)345 static bool isUncRoot(const QString &server)
346 {
347     QString localPath = QDir::toNativeSeparators(server);
348     QStringList parts = localPath.split(QLatin1Char('\\'), QString::SkipEmptyParts);
349     return localPath.startsWith(QLatin1String("\\\\")) && parts.count() <= 1;
350 }
351 
isUncPath(const QString & path)352 static bool isUncPath(const QString &path)
353 {
354     // Starts with // or \\, but not \\. or //.
355     return (path.startsWith(QLatin1String("//"))
356             || path.startsWith(QLatin1String("\\\\")))
357         && (path.size() > 2 && path.at(2) != QLatin1Char('.'));
358 }
359 
isRelativePath(const QString & path)360 static bool isRelativePath(const QString &path)
361 {
362     return !(path.startsWith(QLatin1Char('/'))
363            || (path.length() >= 2
364            && ((path.at(0).isLetter() && path.at(1) == QLatin1Char(':'))
365            || (path.at(0) == QLatin1Char('/') && path.at(1) == QLatin1Char('/'))))); // drive, e.g. a:
366 }
367 
fixIfRelativeUncPath(const QString & path)368 static QString fixIfRelativeUncPath(const QString &path)
369 {
370     if (isRelativePath(path)) {
371         QString currentPath = QDir::currentPath() + QLatin1Char('/');
372         if (currentPath.startsWith(QLatin1String("//")))
373             return QString(path).prepend(currentPath);
374     }
375     return path;
376 }
377 
378 // can be //server or //server/share
uncShareExists(const QString & server)379 static bool uncShareExists(const QString &server)
380 {
381     QStringList parts = server.split(QLatin1Char('\\'), QString::SkipEmptyParts);
382     if (parts.count()) {
383         QStringList shares;
384         if (QFSFileEnginePrivate::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(0), &shares)) {
385             if (parts.count() >= 2)
386                 return shares.contains(parts.at(1), Qt::CaseInsensitive);
387             else
388                 return true;
389         }
390     }
391     return false;
392 }
393 
394 #if !defined(Q_OS_WINCE)
395 // If you change this function, remember to also change the UNICODE version
nativeAbsoluteFilePathA(const QString & path)396 static QString nativeAbsoluteFilePathA(const QString &path)
397 {
398     QString ret;
399     QVarLengthArray<char, MAX_PATH> buf(MAX_PATH);
400     char *fileName = 0;
401     QByteArray ba = path.toLocal8Bit();
402     DWORD retLen = GetFullPathNameA(ba.constData(), buf.size(), buf.data(), &fileName);
403     if (retLen > (DWORD)buf.size()) {
404         buf.resize(retLen);
405         retLen = GetFullPathNameA(ba.constData(), buf.size(), buf.data(), &fileName);
406     }
407     if (retLen != 0)
408         ret = QString::fromLocal8Bit(buf.data(), retLen);
409     return ret;
410 }
411 #endif
412 
413 // If you change this function, remember to also change the NON-UNICODE version
nativeAbsoluteFilePathW(const QString & path)414 static QString nativeAbsoluteFilePathW(const QString &path)
415 {
416     QString ret;
417 #if !defined(Q_OS_WINCE)
418     QVarLengthArray<wchar_t, MAX_PATH> buf(MAX_PATH);
419     wchar_t *fileName = 0;
420     DWORD retLen = GetFullPathNameW((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
421     if (retLen > (DWORD)buf.size()) {
422         buf.resize(retLen);
423         retLen = GetFullPathNameW((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
424     }
425     if (retLen != 0)
426         ret = QString::fromUtf16((unsigned short *)buf.data(), retLen);
427 #else
428     if (path.startsWith(QLatin1String("/")) || path.startsWith(QLatin1String("\\")))
429         ret = QDir::toNativeSeparators(path);
430     else
431         ret = QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir + QLatin1Char('/') + path));
432 #endif
433     return ret;
434 }
435 
nativeAbsoluteFilePath(const QString & path)436 static QString nativeAbsoluteFilePath(const QString &path)
437 {
438     QString absPath = QT_WA_INLINE(nativeAbsoluteFilePathW(path), nativeAbsoluteFilePathA(path));
439     // This is really ugly, but GetFullPathName strips off whitespace at the end.
440     // If you for instance write ". " in the lineedit of QFileDialog,
441     // (which is an invalid filename) this function will strip the space off and viola,
442     // the file is later reported as existing. Therefore, we re-add the whitespace that
443     // was at the end of path in order to keep the filename invalid.
444     int i = path.size() - 1;
445     while (i >= 0 && path.at(i) == QLatin1Char(' ')) --i;
446     int extraws = path.size() - 1 - i;
447     if (extraws >= 0) {
448         while (extraws) {
449             absPath.append(QLatin1Char(' '));
450             --extraws;
451         }
452     }
453     return absPath;
454 }
455 
win95Name(const QString & path)456 QByteArray QFSFileEnginePrivate::win95Name(const QString &path)
457 {
458     QString ret(path);
459     if(path.length() > 1 && path[0] == QLatin1Char('/') && path[1] == QLatin1Char('/')) {
460         // Win95 cannot handle slash-slash needs slosh-slosh.
461         ret[0] = QLatin1Char('\\');
462         ret[1] = QLatin1Char('\\');
463         int n = ret.indexOf(QLatin1Char('/'));
464         if(n >= 0)
465             ret[n] = QLatin1Char('\\');
466     } else if(path.length() > 3 && path[2] == QLatin1Char('/') && path[3] == QLatin1Char('/')) {
467         ret[2] = QLatin1Char('\\');
468         ret.remove(3, 1);
469         int n = ret.indexOf(QLatin1Char('/'));
470         if(n >= 0)
471             ret[n] = QLatin1Char('\\');
472     }
473     return ret.toLocal8Bit();
474 }
475 
476 /*!
477     \internal
478 */
longFileName(const QString & path)479 QString QFSFileEnginePrivate::longFileName(const QString &path)
480 {
481     if (path.startsWith(QLatin1String("\\\\.\\")))
482         return path;
483 
484     QString absPath = nativeAbsoluteFilePath(path);
485 #if !defined(Q_OS_WINCE)
486     QString prefix = QLatin1String("\\\\?\\");
487     if (isUncPath(path)) {
488         prefix = QLatin1String("\\\\?\\UNC\\");
489         absPath.remove(0, 2);
490     }
491     return prefix + absPath;
492 #else
493     return absPath;
494 #endif
495 }
496 
497 /*
498     \internal
499 */
nativeInitFileName()500 void QFSFileEnginePrivate::nativeInitFileName()
501 {
502     QT_WA({
503         QString path = longFileName(QDir::toNativeSeparators(fixIfRelativeUncPath(filePath)));
504         nativeFilePath = QByteArray((const char *)path.utf16(), path.size() * 2 + 1);
505     }, {
506         QString path = fixIfRelativeUncPath(filePath);
507         nativeFilePath = win95Name(path).replace('/', '\\');
508     });
509 }
510 
511 /*
512     \internal
513 */
nativeOpen(QIODevice::OpenMode openMode)514 bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
515 {
516     Q_Q(QFSFileEngine);
517 
518     // All files are opened in share mode (both read and write).
519     DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
520 
521     int accessRights = 0;
522     if (openMode & QIODevice::ReadOnly)
523         accessRights |= GENERIC_READ;
524     if (openMode & QIODevice::WriteOnly)
525         accessRights |= GENERIC_WRITE;
526 
527     SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
528 
529     // WriteOnly can create files, ReadOnly cannot.
530     DWORD creationDisp = (openMode & QIODevice::WriteOnly)
531                          ? OPEN_ALWAYS : OPEN_EXISTING;
532 
533     // Create the file handle.
534     QT_WA({
535         fileHandle = CreateFileW((TCHAR *)nativeFilePath.constData(),
536                                  accessRights,
537                                  shareMode,
538                                  &securityAtts,
539                                  creationDisp,
540                                  FILE_ATTRIBUTE_NORMAL,
541                                  NULL);
542     }, {
543         fileHandle = CreateFileA(nativeFilePath.constData(),
544                                  accessRights,
545                                  shareMode,
546                                  &securityAtts,
547                                  creationDisp,
548                                  FILE_ATTRIBUTE_NORMAL,
549                                  NULL);
550     });
551 
552     // Bail out on error.
553     if (fileHandle == INVALID_HANDLE_VALUE) {
554         q->setError(QFile::OpenError, qt_error_string());
555         return false;
556     }
557 
558     // Truncate the file after successfully opening it if Truncate is passed.
559     if (openMode & QIODevice::Truncate)
560         q->setSize(0);
561 
562     return true;
563 }
564 
565 /*
566     \internal
567 */
nativeClose()568 bool QFSFileEnginePrivate::nativeClose()
569 {
570     Q_Q(QFSFileEngine);
571     if (fh || fd != -1) {
572         // stdlib / stdio mode.
573         return closeFdFh();
574     }
575 
576     // Windows native mode.
577     bool ok = true;
578     if ((fileHandle == INVALID_HANDLE_VALUE || !CloseHandle(fileHandle))
579 #ifdef Q_USE_DEPRECATED_MAP_API
580             && (fileMapHandle == INVALID_HANDLE_VALUE || !CloseHandle(fileMapHandle))
581 #endif
582         ) {
583         q->setError(QFile::UnspecifiedError, qt_error_string());
584         ok = false;
585     }
586     fileHandle = INVALID_HANDLE_VALUE;
587     cachedFd = -1;              // gets closed by CloseHandle above
588 
589     return ok;
590 }
591 
592 /*
593     \internal
594 */
nativeFlush()595 bool QFSFileEnginePrivate::nativeFlush()
596 {
597     if (fh) {
598         // Buffered stdlib mode.
599         return flushFh();
600     }
601     if (fd != -1) {
602         // Unbuffered stdio mode; always succeeds (no buffer).
603         return true;
604     }
605 
606     // Windows native mode; flushing is
607     // unnecessary. FlushFileBuffers(), the equivalent of sync() or
608     // fsync() on Unix, does a low-level flush to the disk, and we
609     // don't expose an API for this.
610     return true;
611 }
612 
613 /*
614     \internal
615 */
nativeSize() const616 qint64 QFSFileEnginePrivate::nativeSize() const
617 {
618     Q_Q(const QFSFileEngine);
619     QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
620 
621     // ### Don't flush; for buffered files, we should get away with ftell.
622     thatQ->flush();
623 
624     // Buffered stdlib mode.
625     if (fh) {
626         QT_OFF_T oldPos = QT_FTELL(fh);
627         QT_FSEEK(fh, 0, SEEK_END);
628         QT_OFF_T fileSize = QT_FTELL(fh);
629         QT_FSEEK(fh, oldPos, SEEK_SET);
630         return qint64(fileSize);
631     }
632 
633     // Not-open mode, where the file name is known: We'll check the
634     // file system directly.
635     if (openMode == QIODevice::NotOpen && !nativeFilePath.isEmpty()) {
636         bool ok = false;
637         WIN32_FILE_ATTRIBUTE_DATA attribData;
638         QT_WA({
639             ok = ::GetFileAttributesExW((TCHAR *)nativeFilePath.constData(),
640                                         GetFileExInfoStandard, &attribData);
641         } , {
642             ok = ::GetFileAttributesExA(nativeFilePath.constData(),
643                                         GetFileExInfoStandard, &attribData);
644         });
645         if (ok) {
646             qint64 size = attribData.nFileSizeHigh;
647             size <<= 32;
648             size += attribData.nFileSizeLow;
649             return size;
650         }
651         thatQ->setError(QFile::UnspecifiedError, qt_error_string());
652         return 0;
653     }
654 
655     // Unbuffed stdio mode.
656     if(fd != -1) {
657 #if !defined(Q_OS_WINCE)
658         HANDLE handle = (HANDLE)_get_osfhandle(fd);
659         if (handle != INVALID_HANDLE_VALUE) {
660             BY_HANDLE_FILE_INFORMATION fileInfo;
661             if (GetFileInformationByHandle(handle, &fileInfo)) {
662                 qint64 size = fileInfo.nFileSizeHigh;
663                 size <<= 32;
664                 size += fileInfo.nFileSizeLow;
665                 return size;
666             }
667         }
668 #endif
669         thatQ->setError(QFile::UnspecifiedError, qt_error_string());
670         return 0;
671     }
672 
673     // Windows native mode.
674     if (fileHandle == INVALID_HANDLE_VALUE)
675         return 0;
676 
677     BY_HANDLE_FILE_INFORMATION fileInfo;
678     if (!GetFileInformationByHandle(fileHandle, &fileInfo)) {
679         thatQ->setError(QFile::UnspecifiedError, qt_error_string());
680         return 0;
681     }
682 
683     qint64 size = fileInfo.nFileSizeHigh;
684     size <<= 32;
685     size += fileInfo.nFileSizeLow;
686     return size;
687 }
688 
689 /*
690     \internal
691 */
nativePos() const692 qint64 QFSFileEnginePrivate::nativePos() const
693 {
694     Q_Q(const QFSFileEngine);
695     QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
696 
697     if (fh || fd != -1) {
698         // stdlib / stido mode.
699         return posFdFh();
700     }
701 
702     // Windows native mode.
703     if (fileHandle == INVALID_HANDLE_VALUE)
704         return 0;
705 
706 #if !defined(QT_NO_LIBRARY)
707     QFSFileEnginePrivate::resolveLibs();
708     if (!ptrSetFilePointerEx) {
709 #endif
710         DWORD newFilePointer = SetFilePointer(fileHandle, 0, NULL, FILE_CURRENT);
711         if (newFilePointer == 0xFFFFFFFF) {
712             thatQ->setError(QFile::UnspecifiedError, qt_error_string());
713             return 0;
714         }
715 
716         // Note: returns <4GB; does not work with large files. This is the
717         // case for MOC, UIC, qmake and other bootstrapped tools, and for
718         // Win9x/ME.
719         return qint64(newFilePointer);
720 #if !defined(QT_NO_LIBRARY)
721     }
722 
723     // This approach supports large files.
724     LARGE_INTEGER currentFilePos;
725     LARGE_INTEGER offset;
726     offset.LowPart = 0;
727     offset.HighPart = 0;
728     if (!ptrSetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_CURRENT)) {
729         thatQ->setError(QFile::UnspecifiedError, qt_error_string());
730         return 0;
731     }
732 
733     return qint64(currentFilePos.QuadPart);
734 #endif
735 }
736 
737 /*
738     \internal
739 */
nativeSeek(qint64 pos)740 bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
741 {
742     Q_Q(const QFSFileEngine);
743     QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
744 
745     if (fh || fd != -1) {
746         // stdlib / stdio mode.
747         return seekFdFh(pos);
748     }
749 
750 #if !defined(QT_NO_LIBRARY)
751     QFSFileEnginePrivate::resolveLibs();
752     if (!ptrSetFilePointerEx) {
753 #endif
754         LONG seekToPos = LONG(pos); // <- lossy
755         DWORD newFilePointer = SetFilePointer(fileHandle, seekToPos, NULL, FILE_BEGIN);
756         if (newFilePointer == 0xFFFFFFFF) {
757             thatQ->setError(QFile::UnspecifiedError, qt_error_string());
758             return false;
759         }
760 
761         // Note: does not work with large files. This is the case for MOC,
762         // UIC, qmake and other bootstrapped tools, and for Win9x/ME.
763         return true;
764 #if !defined(QT_NO_LIBRARY)
765     }
766 
767     // This approach supports large files.
768     LARGE_INTEGER currentFilePos;
769     LARGE_INTEGER offset;
770     offset.LowPart = (unsigned int)(quint64(pos) & Q_UINT64_C(0xffffffff));
771     offset.HighPart = (unsigned int)((quint64(pos) >> 32) & Q_UINT64_C(0xffffffff));
772     if (ptrSetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_BEGIN) == 0) {
773         thatQ->setError(QFile::UnspecifiedError, qt_error_string());
774         return false;
775     }
776     return true;
777 #endif
778 }
779 
780 /*
781     \internal
782 */
nativeRead(char * data,qint64 maxlen)783 qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 maxlen)
784 {
785     Q_Q(QFSFileEngine);
786 
787     if (fh || fd != -1) {
788         // stdio / stdlib mode.
789         if (fh && nativeIsSequential() && feof(fh)) {
790             q->setError(QFile::ReadError, qt_error_string(int(errno)));
791             return -1;
792         }
793 
794         return readFdFh(data, maxlen);
795     }
796 
797     // Windows native mode.
798     if (fileHandle == INVALID_HANDLE_VALUE)
799         return -1;
800 
801     DWORD bytesToRead = DWORD(maxlen); // <- lossy
802 
803     // Reading on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
804     // the chunks are too large, so we limit the block size to 32MB.
805     static const DWORD maxBlockSize = 32 * 1024 * 1024;
806 
807     qint64 totalRead = 0;
808     do {
809         DWORD blockSize = qMin<DWORD>(bytesToRead, maxBlockSize);
810         DWORD bytesRead;
811         if (!ReadFile(fileHandle, data + totalRead, blockSize, &bytesRead, NULL)) {
812             if (totalRead == 0) {
813                 // Note: only return failure if the first ReadFile fails.
814                 q->setError(QFile::ReadError, qt_error_string());
815                 return -1;
816             }
817             break;
818         }
819         if (bytesRead == 0)
820             break;
821         totalRead += bytesRead;
822         bytesToRead -= bytesRead;
823     } while (totalRead < maxlen);
824     return qint64(totalRead);
825 }
826 
827 /*
828     \internal
829 */
nativeReadLine(char * data,qint64 maxlen)830 qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
831 {
832     Q_Q(QFSFileEngine);
833 
834     if (fh || fd != -1) {
835         // stdio / stdlib mode.
836         return readLineFdFh(data, maxlen);
837     }
838 
839     // Windows native mode.
840     if (fileHandle == INVALID_HANDLE_VALUE)
841         return -1;
842 
843     // ### No equivalent in Win32?
844     return q->QAbstractFileEngine::readLine(data, maxlen);
845 }
846 
847 /*
848     \internal
849 */
nativeWrite(const char * data,qint64 len)850 qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
851 {
852     Q_Q(QFSFileEngine);
853 
854     if (fh || fd != -1) {
855         // stdio / stdlib mode.
856         return writeFdFh(data, len);
857     }
858 
859     // Windows native mode.
860     if (fileHandle == INVALID_HANDLE_VALUE)
861         return -1;
862 
863     qint64 bytesToWrite = DWORD(len); // <- lossy
864 
865     // Writing on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
866     // the chunks are too large, so we limit the block size to 32MB.
867     static const DWORD maxBlockSize = 32 * 1024 * 1024;
868 
869     qint64 totalWritten = 0;
870     do {
871         DWORD blockSize = qMin<DWORD>(bytesToWrite, maxBlockSize);
872         DWORD bytesWritten;
873         if (!WriteFile(fileHandle, data + totalWritten, blockSize, &bytesWritten, NULL)) {
874             if (totalWritten == 0) {
875                 // Note: Only return error if the first WriteFile failed.
876                 q->setError(QFile::WriteError, qt_error_string());
877                 return -1;
878             }
879             break;
880         }
881         if (bytesWritten == 0)
882             break;
883         totalWritten += bytesWritten;
884         bytesToWrite -= bytesWritten;
885     } while (totalWritten < len);
886     return qint64(totalWritten);
887 }
888 
889 /*
890     \internal
891 */
nativeHandle() const892 int QFSFileEnginePrivate::nativeHandle() const
893 {
894     if (fh || fd != -1)
895         return fh ? QT_FILENO(fh) : fd;
896 #ifndef Q_OS_WINCE
897     if (cachedFd != -1)
898         return cachedFd;
899 
900     int flags = 0;
901     if (openMode & QIODevice::Append)
902         flags |= _O_APPEND;
903     if (!(openMode & QIODevice::WriteOnly))
904         flags |= _O_RDONLY;
905     cachedFd = _open_osfhandle((intptr_t) fileHandle, flags);
906     return cachedFd;
907 #else
908     return -1;
909 #endif
910 }
911 
912 /*
913     \internal
914 */
nativeIsSequential() const915 bool QFSFileEnginePrivate::nativeIsSequential() const
916 {
917 #if !defined(Q_OS_WINCE)
918     // stdlib / Windows native mode.
919     if (fh || fileHandle != INVALID_HANDLE_VALUE) {
920         if (fh == stdin || fh == stdout || fh == stderr)
921             return true;
922 
923         HANDLE handle = fileHandle;
924         if (fileHandle == INVALID_HANDLE_VALUE) {
925             // Rare case: using QFile::open(FILE*) to open a pipe.
926             handle = (HANDLE)_get_osfhandle(QT_FILENO(fh));
927             return false;
928         }
929 
930         DWORD fileType = GetFileType(handle);
931         return fileType == FILE_TYPE_PIPE;
932     }
933 
934     // stdio mode.
935     if (fd != -1)
936         return isSequentialFdFh();
937 #endif
938     return false;
939 }
940 
941 /*!
942     \reimp
943 */
remove()944 bool QFSFileEngine::remove()
945 {
946     Q_D(QFSFileEngine);
947     QT_WA({
948         return ::DeleteFileW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16()) != 0;
949     } , {
950         return ::DeleteFileA(QFSFileEnginePrivate::win95Name(d->filePath)) != 0;
951     });
952 }
953 
954 /*!
955     \reimp
956 */
copy(const QString & copyName)957 bool QFSFileEngine::copy(const QString &copyName)
958 {
959     Q_D(QFSFileEngine);
960     QT_WA({
961         return ::CopyFileW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(),
962                            (TCHAR*)QFSFileEnginePrivate::longFileName(copyName).utf16(), true) != 0;
963     } , {
964         return ::CopyFileA(QFSFileEnginePrivate::win95Name(d->filePath),
965                            QFSFileEnginePrivate::win95Name(copyName), true) != 0;
966     });
967 }
968 
969 /*!
970     \reimp
971 */
rename(const QString & newName)972 bool QFSFileEngine::rename(const QString &newName)
973 {
974     Q_D(QFSFileEngine);
975     QT_WA({
976         return ::MoveFileW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(),
977                            (TCHAR*)QFSFileEnginePrivate::longFileName(newName).utf16()) != 0;
978     } , {
979         return ::MoveFileA(QFSFileEnginePrivate::win95Name(d->filePath),
980                            QFSFileEnginePrivate::win95Name(newName)) != 0;
981     });
982 }
983 
mkDir(const QString & path)984 static inline bool mkDir(const QString &path)
985 {
986 #if defined(Q_OS_WINCE)
987     // Unfortunately CreateDirectory returns true for paths longer than
988     // 256, but does not create a directory. It starts to fail, when
989     // path length > MAX_PATH, which is 260 usually on CE.
990     // This only happens on a Windows Mobile device. Windows CE seems
991     // not to be affected by this.
992     static int platformId = 0;
993     if (platformId == 0) {
994         wchar_t platformString[64];
995         if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(platformString)/sizeof(*platformString),platformString,0)) {
996             if (0 == wcscmp(platformString, L"PocketPC") || 0 == wcscmp(platformString, L"Smartphone"))
997                 platformId = 1;
998             else
999                 platformId = 2;
1000         }
1001     }
1002     if (platformId == 1 && QFSFileEnginePrivate::longFileName(path).size() > 256)
1003         return false;
1004 #endif
1005     QT_WA({
1006         return ::CreateDirectoryW((TCHAR*)QFSFileEnginePrivate::longFileName(path).utf16(), 0);
1007     } , {
1008         return ::CreateDirectoryA(QFSFileEnginePrivate::win95Name(QFileInfo(path).absoluteFilePath()), 0);
1009     });
1010 }
1011 
1012 /*!
1013     \reimp
1014 */
rmDir(const QString & path)1015 static inline bool rmDir(const QString &path)
1016 {
1017     QT_WA({
1018         return ::RemoveDirectoryW((TCHAR*)QFSFileEnginePrivate::longFileName(path).utf16());
1019     } , {
1020         return ::RemoveDirectoryA(QFSFileEnginePrivate::win95Name(QFileInfo(path).absoluteFilePath()));
1021     });
1022 }
1023 
1024 /*!
1025     \reimp
1026 */
isDirPath(const QString & dirPath,bool * existed)1027 static inline bool isDirPath(const QString &dirPath, bool *existed)
1028 {
1029     QString path = dirPath;
1030     if (path.length() == 2 &&path.at(1) == QLatin1Char(':'))
1031         path += QLatin1Char('\\');
1032 
1033     DWORD fileAttrib = INVALID_FILE_ATTRIBUTES;
1034     QT_WA({
1035         fileAttrib = ::GetFileAttributesW((TCHAR*)QFSFileEnginePrivate::longFileName(path).utf16());
1036     } , {
1037         fileAttrib = ::GetFileAttributesA(QFSFileEnginePrivate::win95Name(QFileInfo(path).absoluteFilePath()));
1038     });
1039 
1040     if (existed)
1041         *existed = fileAttrib != INVALID_FILE_ATTRIBUTES;
1042 
1043     if (fileAttrib == INVALID_FILE_ATTRIBUTES)
1044         return false;
1045 
1046     return fileAttrib & FILE_ATTRIBUTE_DIRECTORY;
1047 }
1048 
1049 /*!
1050     \reimp
1051 */
mkdir(const QString & name,bool createParentDirectories) const1052 bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
1053 {
1054     QString dirName = name;
1055     if (createParentDirectories) {
1056         dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
1057         // We specifically search for / so \ would break it..
1058         int oldslash = -1;
1059         if (dirName.startsWith(QLatin1String("\\\\"))) {
1060             // Don't try to create the root path of a UNC path;
1061             // CreateDirectory() will just return ERROR_INVALID_NAME.
1062             for (int i = 0; i < dirName.size(); ++i) {
1063                 if (dirName.at(i) != QDir::separator()) {
1064                     oldslash = i;
1065                     break;
1066                 }
1067             }
1068             if (oldslash != -1)
1069                 oldslash = dirName.indexOf(QDir::separator(), oldslash);
1070         }
1071         for (int slash=0; slash != -1; oldslash = slash) {
1072             slash = dirName.indexOf(QDir::separator(), oldslash+1);
1073             if (slash == -1) {
1074                 if(oldslash == dirName.length())
1075                     break;
1076                 slash = dirName.length();
1077             }
1078             if (slash) {
1079                 QString chunk = dirName.left(slash);
1080                 bool existed = false;
1081                 if (!isDirPath(chunk, &existed) && !existed) {
1082                     if (!mkDir(chunk))
1083                         return false;
1084                 }
1085             }
1086         }
1087         return true;
1088     }
1089     return mkDir(name);
1090 }
1091 
1092 /*!
1093     \reimp
1094 */
rmdir(const QString & name,bool recurseParentDirectories) const1095 bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
1096 {
1097     QString dirName = name;
1098     if (recurseParentDirectories) {
1099         dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
1100         for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
1101             QString chunk = dirName.left(slash);
1102             if (chunk.length() == 2 && chunk.at(0).isLetter() && chunk.at(1) == QLatin1Char(':'))
1103                 break;
1104             if (!isDirPath(chunk, 0))
1105                 return false;
1106             if (!rmDir(chunk))
1107                 return oldslash != 0;
1108             slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
1109         }
1110         return true;
1111     }
1112     return rmDir(name);
1113 }
1114 
1115 /*!
1116     \reimp
1117 */
caseSensitive() const1118 bool QFSFileEngine::caseSensitive() const
1119 {
1120     return false;
1121 }
1122 
1123 /*!
1124     Sets the current path (e.g., for QDir), to \a path. Returns true if the
1125     new path exists; otherwise this function does nothing, and returns false.
1126 
1127     \sa currentPath()
1128 */
setCurrentPath(const QString & path)1129 bool QFSFileEngine::setCurrentPath(const QString &path)
1130 {
1131     if (!QDir(path).exists())
1132         return false;
1133 
1134 #if !defined(Q_OS_WINCE)
1135     int r;
1136     QT_WA({
1137         r = ::SetCurrentDirectoryW((WCHAR*)path.utf16());
1138     } , {
1139         r = ::SetCurrentDirectoryA(QFSFileEnginePrivate::win95Name(path));
1140     });
1141     return r != 0;
1142 #else
1143 	qfsPrivateCurrentDir = QFSFileEnginePrivate::longFileName(path);
1144 	return true;
1145 #endif
1146 }
1147 
1148 /*!
1149     Returns the canonicalized form of the current path used by the file
1150     engine for the drive specified by \a fileName.
1151 
1152     On Windows, each drive has its own current directory, so a different
1153     path is returned for file names that include different drive names
1154     (e.g. A: or C:).
1155 
1156     \sa setCurrentPath()
1157 */
currentPath(const QString & fileName)1158 QString QFSFileEngine::currentPath(const QString &fileName)
1159 {
1160 #if !defined(Q_OS_WINCE)
1161     QString ret;
1162     //if filename is a drive: then get the pwd of that drive
1163     if (fileName.length() >= 2 &&
1164         fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':')) {
1165         int drv = fileName.toUpper().at(0).toLatin1() - 'A' + 1;
1166         if (_getdrive() != drv) {
1167             QT_WA({
1168                 TCHAR buf[PATH_MAX];
1169                 ::_wgetdcwd(drv, buf, PATH_MAX);
1170                 ret.setUtf16((ushort*)buf, uint(::wcslen(buf)));
1171             }, {
1172                 char buf[PATH_MAX];
1173                 ::_getdcwd(drv, buf, PATH_MAX);
1174                 ret = QString::fromLatin1(buf);
1175             });
1176         }
1177     }
1178     if (ret.isEmpty()) {
1179         //just the pwd
1180         QT_WA({
1181             DWORD size = 0;
1182             WCHAR currentName[PATH_MAX];
1183             size = ::GetCurrentDirectoryW(PATH_MAX, currentName);
1184             if (size !=0) {
1185                 if (size > PATH_MAX) {
1186                     WCHAR * newCurrentName = new WCHAR[size];
1187                     if (::GetCurrentDirectoryW(PATH_MAX, newCurrentName) != 0)
1188                         ret = QString::fromUtf16((ushort*)newCurrentName);
1189                     delete [] newCurrentName;
1190                 } else {
1191                     ret = QString::fromUtf16((ushort*)currentName);
1192                 }
1193             }
1194         } , {
1195             DWORD size = 0;
1196             char currentName[PATH_MAX];
1197             size = ::GetCurrentDirectoryA(PATH_MAX, currentName);
1198             if (size !=0)
1199                 ret = QString::fromLocal8Bit(currentName);
1200         });
1201     }
1202     if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
1203         ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
1204     return QDir::fromNativeSeparators(ret);
1205 #else
1206 	Q_UNUSED(fileName);
1207 	if (qfsPrivateCurrentDir.isEmpty())
1208 		qfsPrivateCurrentDir = QCoreApplication::applicationDirPath();
1209 
1210     return QDir::fromNativeSeparators(qfsPrivateCurrentDir);
1211 #endif
1212 }
1213 
1214 /*!
1215     Returns the home path of the current user.
1216 
1217     \sa rootPath()
1218 */
homePath()1219 QString QFSFileEngine::homePath()
1220 {
1221     QString ret;
1222 #if !defined(QT_NO_LIBRARY)
1223     QT_WA (
1224     {
1225         QFSFileEnginePrivate::resolveLibs();
1226 		if (ptrOpenProcessToken && ptrGetUserProfileDirectoryW) {
1227 			HANDLE hnd = ::GetCurrentProcess();
1228 			HANDLE token = 0;
1229 			BOOL ok = ::ptrOpenProcessToken(hnd, TOKEN_QUERY, &token);
1230 			if (ok) {
1231 				DWORD dwBufferSize = 0;
1232 				// First call, to determine size of the strings (with '\0').
1233 				ok = ::ptrGetUserProfileDirectoryW(token, NULL, &dwBufferSize);
1234 				if (!ok && dwBufferSize != 0) {		// We got the required buffer size
1235 					wchar_t *userDirectory = new wchar_t[dwBufferSize];
1236 					// Second call, now we can fill the allocated buffer.
1237 					ok = ::ptrGetUserProfileDirectoryW(token, userDirectory, &dwBufferSize);
1238 					if (ok)
1239 						ret = QString::fromUtf16((ushort*)userDirectory);
1240 
1241 					delete [] userDirectory;
1242 				}
1243 				::CloseHandle(token);
1244 			}
1245 		}
1246     }
1247     ,
1248     {
1249         // GetUserProfileDirectory is only available from NT 4.0,
1250 		// so fall through for Win98 and friends version.
1251     })
1252 #endif
1253     if(ret.isEmpty() || !QFile::exists(ret)) {
1254         ret = QString::fromLocal8Bit(qgetenv("USERPROFILE").constData());
1255         if(ret.isEmpty() || !QFile::exists(ret)) {
1256             ret = QString::fromLocal8Bit(qgetenv("HOMEDRIVE").constData()) + QString::fromLocal8Bit(qgetenv("HOMEPATH").constData());
1257             if(ret.isEmpty() || !QFile::exists(ret)) {
1258                 ret = QString::fromLocal8Bit(qgetenv("HOME").constData());
1259                 if(ret.isEmpty() || !QFile::exists(ret)) {
1260 #if defined(Q_OS_WINCE)
1261                     ret = QString::fromLatin1("\\My Documents");
1262                     if (!QFile::exists(ret))
1263 #endif
1264                     ret = rootPath();
1265                 }
1266             }
1267         }
1268     }
1269     return QDir::fromNativeSeparators(ret);
1270 }
1271 
1272 /*!
1273     Returns the root path.
1274 
1275     \sa homePath()
1276 */
rootPath()1277 QString QFSFileEngine::rootPath()
1278 {
1279 #if defined(Q_OS_WINCE)
1280     QString ret = QString::fromLatin1("/");
1281 #elif defined(Q_FS_FAT)
1282     QString ret = QString::fromLatin1(qgetenv("SystemDrive").constData());
1283     if(ret.isEmpty())
1284         ret = QLatin1String("c:");
1285     ret += QLatin1String("/");
1286 #elif defined(Q_OS_OS2EMX)
1287     char dir[4];
1288     _abspath(dir, QLatin1String("/"), _MAX_PATH);
1289     QString ret(dir);
1290 #endif
1291     return ret;
1292 }
1293 
1294 /*!
1295     Returns the temporary path (i.e., a path in which it is safe to store
1296     temporary files).
1297 */
tempPath()1298 QString QFSFileEngine::tempPath()
1299 {
1300     QString ret;
1301     int success;
1302     QT_WA({
1303         wchar_t tempPath[MAX_PATH];
1304         success = GetTempPathW(MAX_PATH, tempPath);
1305         ret = QString::fromUtf16((ushort*)tempPath);
1306     } , {
1307         char tempPath[MAX_PATH];
1308         success = GetTempPathA(MAX_PATH, tempPath);
1309         ret = QString::fromLocal8Bit(tempPath);
1310     });
1311     if (ret.isEmpty() || !success) {
1312 #if !defined(Q_OS_WINCE)
1313         ret = QString::fromLatin1("c:/tmp");
1314 #else
1315         ret = QString::fromLatin1("\\Temp");
1316 #endif
1317     } else {
1318         ret = QDir::fromNativeSeparators(ret);
1319         while (ret.at(ret.length()-1) == QLatin1Char('/'))
1320             ret = ret.left(ret.length()-1);
1321     }
1322     return ret;
1323 }
1324 
1325 /*!
1326     Returns the list of drives in the file system as a list of QFileInfo
1327     objects. On unix, Mac OS X and Windows CE, only the root path is returned.
1328     On Windows, this function returns all drives (A:\, C:\, D:\, etc.).
1329 */
drives()1330 QFileInfoList QFSFileEngine::drives()
1331 {
1332     QFileInfoList ret;
1333 #if !defined(Q_OS_WINCE)
1334 #if defined(Q_OS_WIN32)
1335     quint32 driveBits = (quint32) GetLogicalDrives() & 0x3ffffff;
1336 #elif defined(Q_OS_OS2EMX)
1337     quint32 driveBits, cur;
1338     if(DosQueryCurrentDisk(&cur,&driveBits) != NO_ERROR)
1339 	exit(1);
1340     driveBits &= 0x3ffffff;
1341 #endif
1342     char driveName[4];
1343 
1344     qstrcpy(driveName, "A:/");
1345 
1346     while(driveBits) {
1347 	if(driveBits & 1)
1348 	    ret.append(QString::fromLatin1(driveName).toUpper());
1349 	driveName[0]++;
1350 	driveBits = driveBits >> 1;
1351     }
1352     return ret;
1353 #else
1354     ret.append(QString::fromLatin1("/").toUpper());
1355     return ret;
1356 #endif
1357 }
1358 
doStat() const1359 bool QFSFileEnginePrivate::doStat() const
1360 {
1361     if (!tried_stat) {
1362         tried_stat = true;
1363         could_stat = false;
1364 
1365         if (filePath.isEmpty())
1366             return could_stat;
1367         QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath;
1368         fname = fixIfRelativeUncPath(fname);
1369 
1370         UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1371 
1372         if (fd != -1) {
1373 #if !defined(Q_OS_WINCE)
1374             HANDLE fh = (HANDLE)_get_osfhandle(fd);
1375             if (fh != INVALID_HANDLE_VALUE) {
1376                 BY_HANDLE_FILE_INFORMATION fileInfo;
1377                 if (GetFileInformationByHandle(fh, &fileInfo)) {
1378                     could_stat = true;
1379                     fileAttrib = fileInfo.dwFileAttributes;
1380                 }
1381             }
1382 #else
1383             DWORD tmpAttributes = GetFileAttributesW((TCHAR*)QFSFileEnginePrivate::longFileName(fname).utf16());
1384             if (tmpAttributes != -1) {
1385                 fileAttrib = tmpAttributes;
1386                 could_stat = true;
1387             } else {
1388                 return false;
1389             }
1390 #endif
1391         } else {
1392             QT_WA({
1393                 fileAttrib = GetFileAttributesW((TCHAR*)QFSFileEnginePrivate::longFileName(fname).utf16());
1394             } , {
1395                 fileAttrib = GetFileAttributesA(QFSFileEnginePrivate::win95Name(QFileInfo(fname).absoluteFilePath()));
1396             });
1397             could_stat = fileAttrib != INVALID_FILE_ATTRIBUTES;
1398             if (!could_stat) {
1399 #if !defined(Q_OS_WINCE)
1400                 if (!fname.isEmpty() && fname.at(0).isLetter() && fname.mid(1, fname.length()) == QLatin1String(":/")) {
1401                     // an empty drive ??
1402                     DWORD drivesBitmask = ::GetLogicalDrives();
1403                     int drivebit = 1 << (fname.at(0).toUpper().unicode() - QLatin1Char('A').unicode());
1404                     if (drivesBitmask & drivebit) {
1405                         fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM;
1406                         could_stat = true;
1407                     }
1408                 } else {
1409 #endif
1410                     QString path = QDir::toNativeSeparators(fname);
1411                     bool is_dir = false;
1412                     if (path.startsWith(QLatin1String("\\\\"))) {
1413                         // UNC - stat doesn't work for all cases (Windows bug)
1414                         int s = path.indexOf(path.at(0),2);
1415                         if (s > 0) {
1416                             // "\\server\..."
1417                             s = path.indexOf(path.at(0),s+1);
1418                             if (s > 0) {
1419                                 // "\\server\share\..."
1420                                 if (s == path.size() - 1) {
1421                                     // "\\server\share\"
1422                                     is_dir = true;
1423                                 } else {
1424                                     // "\\server\share\notfound"
1425                                 }
1426                             } else {
1427                                 // "\\server\share"
1428                                 is_dir = true;
1429                             }
1430                         } else {
1431                             // "\\server"
1432                             is_dir = true;
1433                         }
1434                     }
1435                     if (is_dir && uncShareExists(path)) {
1436                         // looks like a UNC dir, is a dir.
1437                         fileAttrib = FILE_ATTRIBUTE_DIRECTORY;
1438                         could_stat = true;
1439                     }
1440 #if !defined(Q_OS_WINCE)
1441                 }
1442 #endif
1443             }
1444         }
1445         SetErrorMode(oldmode);
1446     }
1447     return could_stat;
1448 }
1449 
1450 
readLink(const QString & link)1451 static QString readLink(const QString &link)
1452 {
1453 #if !defined(Q_OS_WINCE)
1454 #if !defined(QT_NO_LIBRARY)
1455     QString ret;
1456     QT_WA({
1457         bool neededCoInit = false;
1458         IShellLink *psl;                            // pointer to IShellLink i/f
1459         HRESULT hres;
1460         WIN32_FIND_DATA wfd;
1461         TCHAR szGotPath[MAX_PATH];
1462         // Get pointer to the IShellLink interface.
1463         hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1464                                     IID_IShellLink, (LPVOID *)&psl);
1465 
1466         if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
1467             neededCoInit = true;
1468             CoInitialize(NULL);
1469             hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1470                                         IID_IShellLink, (LPVOID *)&psl);
1471         }
1472         if(SUCCEEDED(hres)) {    // Get pointer to the IPersistFile interface.
1473             IPersistFile *ppf;
1474             hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
1475             if(SUCCEEDED(hres))  {
1476                 hres = ppf->Load((LPOLESTR)link.utf16(), STGM_READ);
1477                 //The original path of the link is retrieved. If the file/folder
1478                 //was moved, the return value still have the old path.
1479                 if(SUCCEEDED(hres)) {
1480                     if (psl->GetPath(szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR)
1481                         ret = QString::fromUtf16((ushort*)szGotPath);
1482                 }
1483                 ppf->Release();
1484             }
1485             psl->Release();
1486         }
1487         if(neededCoInit)
1488             CoUninitialize();
1489     } , {
1490 	    bool neededCoInit = false;
1491         IShellLinkA *psl;                            // pointer to IShellLink i/f
1492         HRESULT hres;
1493         WIN32_FIND_DATAA wfd;
1494         char szGotPath[MAX_PATH];
1495         // Get pointer to the IShellLink interface.
1496 
1497         hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1498                                     IID_IShellLinkA, (LPVOID *)&psl);
1499 
1500         if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
1501             neededCoInit = true;
1502             CoInitialize(NULL);
1503             hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1504                                         IID_IShellLinkA, (LPVOID *)&psl);
1505         }
1506         if(SUCCEEDED(hres)) {    // Get pointer to the IPersistFile interface.
1507             IPersistFile *ppf;
1508             hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
1509             if(SUCCEEDED(hres))  {
1510                 hres = ppf->Load((LPOLESTR)QFileInfo(link).absoluteFilePath().utf16(), STGM_READ);
1511                 //The original path of the link is retrieved. If the file/folder
1512                 //was moved, the return value still have the old path.
1513                  if(SUCCEEDED(hres)) {
1514                     if (psl->GetPath((char*)szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR)
1515                         ret = QString::fromLocal8Bit(szGotPath);
1516                 }
1517                 ppf->Release();
1518             }
1519             psl->Release();
1520         }
1521         if(neededCoInit)
1522             CoUninitialize();
1523     });
1524     return ret;
1525 #else
1526     Q_UNUSED(link);
1527     return QString();
1528 #endif // QT_NO_LIBRARY
1529 #else
1530     wchar_t target[MAX_PATH];
1531     QString result;
1532     if (SHGetShortcutTarget((wchar_t*)QFileInfo(link).absoluteFilePath().replace(QLatin1Char('/'),QLatin1Char('\\')).utf16(), target, MAX_PATH)) {
1533         result = QString::fromUtf16(reinterpret_cast<const ushort *> (target));
1534         if (result.startsWith(QLatin1Char('"')))
1535             result.remove(0,1);
1536         if (result.endsWith(QLatin1Char('"')))
1537             result.remove(result.size()-1,1);
1538     }
1539     return result;
1540 #endif // Q_OS_WINCE
1541 }
1542 
1543 /*!
1544     \internal
1545 */
getLink() const1546 QString QFSFileEnginePrivate::getLink() const
1547 {
1548     return readLink(filePath);
1549 }
1550 
1551 /*!
1552     \reimp
1553 */
link(const QString & newName)1554 bool QFSFileEngine::link(const QString &newName)
1555 {
1556 #if !defined(Q_OS_WINCE)
1557 #if !defined(QT_NO_LIBRARY)
1558     bool ret = false;
1559 
1560     QString linkName = newName;
1561     //### assume that they add .lnk
1562 
1563     QT_WA({
1564         HRESULT hres;
1565         IShellLink *psl;
1566         bool neededCoInit = false;
1567 
1568         hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
1569         if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
1570                 neededCoInit = true;
1571                 CoInitialize(NULL);
1572                 hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
1573         }
1574         if (SUCCEEDED(hres)) {
1575             hres = psl->SetPath((wchar_t *)fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\')).utf16());
1576             if (SUCCEEDED(hres)) {
1577                 hres = psl->SetWorkingDirectory((wchar_t *)fileName(AbsolutePathName).replace(QLatin1Char('/'), QLatin1Char('\\')).utf16());
1578                 if (SUCCEEDED(hres)) {
1579                     IPersistFile *ppf;
1580                     hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
1581                     if (SUCCEEDED(hres)) {
1582                         hres = ppf->Save((TCHAR*)linkName.utf16(), TRUE);
1583                         if (SUCCEEDED(hres))
1584                              ret = true;
1585                         ppf->Release();
1586                     }
1587                 }
1588             }
1589             psl->Release();
1590         }
1591         if(neededCoInit)
1592                 CoUninitialize();
1593     } , {
1594         // the SetPath() call _sometimes_ changes the current path and when it does it sometimes
1595         // does not let us change it back unless we call currentPath() many times.
1596         QString cwd = currentPath();
1597         HRESULT hres;
1598         IShellLinkA *psl;
1599         bool neededCoInit = false;
1600 
1601         hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
1602         if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
1603             neededCoInit = true;
1604             CoInitialize(NULL);
1605             hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
1606         }
1607         if (SUCCEEDED(hres)) {
1608             currentPath();
1609             hres = psl->SetPath((char *)QString::fromLocal8Bit(QFSFileEnginePrivate::win95Name(fileName(AbsoluteName))).utf16());
1610             currentPath();
1611             if (SUCCEEDED(hres)) {
1612                 hres = psl->SetWorkingDirectory((char *)QString::fromLocal8Bit(QFSFileEnginePrivate::win95Name(fileName(AbsolutePathName))).utf16());
1613                 currentPath();
1614                 if (SUCCEEDED(hres)) {
1615                     IPersistFile *ppf;
1616                     hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
1617                     if (SUCCEEDED(hres)) {
1618                         currentPath();
1619                         hres = ppf->Save((LPCOLESTR)linkName.utf16(), TRUE);
1620                         currentPath();
1621                         if (SUCCEEDED(hres))
1622                             ret = true;
1623                         ppf->Release();
1624                     }
1625                 }
1626                 psl->Release();
1627             }
1628         }
1629         if(neededCoInit)
1630             CoUninitialize();
1631         setCurrentPath(cwd);
1632     });
1633     return ret;
1634 #else
1635     Q_UNUSED(newName);
1636     return false;
1637 #endif // QT_NO_LIBRARY
1638 #else
1639     QString linkName = newName;
1640     if (!linkName.endsWith(QLatin1String(".lnk")))
1641         linkName += QLatin1String(".lnk");
1642     QString orgName = fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\'));
1643     // Need to append on our own
1644     orgName.prepend(QLatin1Char('"'));
1645     orgName.append(QLatin1Char('"'));
1646     return SUCCEEDED(SHCreateShortcut((wchar_t*)linkName.utf16(), (wchar_t*)orgName.utf16()));
1647 #endif // Q_OS_WINCE
1648 }
1649 
1650 /*!
1651     \internal
1652 */
getPermissions() const1653 QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions() const
1654 {
1655     QAbstractFileEngine::FileFlags ret = 0;
1656 
1657 #if !defined(QT_NO_LIBRARY)
1658     if((qt_ntfs_permission_lookup > 0) && ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_NT)) {
1659 	PSID pOwner = 0;
1660 	PSID pGroup = 0;
1661 	PACL pDacl;
1662         PSECURITY_DESCRIPTOR pSD;
1663         ACCESS_MASK access_mask;
1664 
1665         enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 };
1666         resolveLibs();
1667         if(ptrGetNamedSecurityInfoW && ptrAllocateAndInitializeSid && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW && ptrFreeSid) {
1668 
1669             QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath;
1670             DWORD res = ptrGetNamedSecurityInfoW((wchar_t*)fname.utf16(), SE_FILE_OBJECT,
1671 						 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
1672 						 &pOwner, &pGroup, &pDacl, 0, &pSD);
1673 
1674             if(res == ERROR_SUCCESS) {
1675                 TRUSTEE_W trustee;
1676                 { //user
1677                     if(ptrGetEffectiveRightsFromAclW(pDacl, &currentUserTrusteeW, &access_mask) != ERROR_SUCCESS)
1678                         access_mask = (ACCESS_MASK)-1;
1679 		    if(access_mask & ReadMask)
1680 			ret |= QAbstractFileEngine::ReadUserPerm;
1681 		    if(access_mask & WriteMask)
1682 			ret |= QAbstractFileEngine::WriteUserPerm;
1683 		    if(access_mask & ExecMask)
1684 			ret |= QAbstractFileEngine::ExeUserPerm;
1685                 }
1686                 { //owner
1687                     ptrBuildTrusteeWithSidW(&trustee, pOwner);
1688                     if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
1689                         access_mask = (ACCESS_MASK)-1;
1690 		    if(access_mask & ReadMask)
1691 			ret |= QAbstractFileEngine::ReadOwnerPerm;
1692 		    if(access_mask & WriteMask)
1693 			ret |= QAbstractFileEngine::WriteOwnerPerm;
1694 		    if(access_mask & ExecMask)
1695 			ret |= QAbstractFileEngine::ExeOwnerPerm;
1696                 }
1697                 { //group
1698                     ptrBuildTrusteeWithSidW(&trustee, pGroup);
1699                     if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
1700                         access_mask = (ACCESS_MASK)-1;
1701 		    if(access_mask & ReadMask)
1702 			ret |= QAbstractFileEngine::ReadGroupPerm;
1703 		    if(access_mask & WriteMask)
1704 			ret |= QAbstractFileEngine::WriteGroupPerm;
1705 		    if(access_mask & ExecMask)
1706 			ret |= QAbstractFileEngine::ExeGroupPerm;
1707                 }
1708                 { //other (world)
1709                     // Create SID for Everyone (World)
1710                     SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY };
1711                     PSID pWorld = 0;
1712                     if(ptrAllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0,0,0,0,0,0,0, &pWorld)) {
1713                         ptrBuildTrusteeWithSidW(&trustee, pWorld);
1714                         if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
1715                             access_mask = (ACCESS_MASK)-1; // ###
1716 			if(access_mask & ReadMask)
1717 			    ret |= QAbstractFileEngine::ReadOtherPerm;
1718 			if(access_mask & WriteMask)
1719 			    ret |= QAbstractFileEngine::WriteOtherPerm;
1720 			if(access_mask & ExecMask)
1721 			    ret |= QAbstractFileEngine::ExeOtherPerm;
1722                     }
1723                     ptrFreeSid(pWorld);
1724                 }
1725                 LocalFree(pSD);
1726             }
1727         }
1728     } else
1729 #endif
1730            {
1731 	//### what to do with permissions if we don't use ntfs or are not on a NT system
1732 	// for now just add all permissions and what about exe missions ??
1733 	// also qt_ntfs_permission_lookup is now not set by default ... should it ?
1734     	ret |= QAbstractFileEngine::ReadOtherPerm | QAbstractFileEngine::ReadGroupPerm
1735 	    | QAbstractFileEngine::ReadOwnerPerm | QAbstractFileEngine::ReadUserPerm
1736 	    | QAbstractFileEngine::WriteUserPerm | QAbstractFileEngine::WriteOwnerPerm
1737 	    | QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm;
1738     }
1739 
1740     if (doStat()) {
1741         if (ret & (QAbstractFileEngine::WriteOwnerPerm | QAbstractFileEngine::WriteUserPerm |
1742             QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm)) {
1743             if (fileAttrib & FILE_ATTRIBUTE_READONLY)
1744                 ret &= ~(QAbstractFileEngine::WriteOwnerPerm | QAbstractFileEngine::WriteUserPerm |
1745                 QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm);
1746         }
1747 
1748         QString ext = filePath.right(4).toLower();
1749         if (ext == QLatin1String(".exe") || ext == QLatin1String(".com") || ext == QLatin1String(".bat") ||
1750             ext == QLatin1String(".pif") || ext == QLatin1String(".cmd") || (fileAttrib & FILE_ATTRIBUTE_DIRECTORY))
1751             ret |= QAbstractFileEngine::ExeOwnerPerm | QAbstractFileEngine::ExeGroupPerm |
1752             QAbstractFileEngine::ExeOtherPerm | QAbstractFileEngine::ExeUserPerm;
1753     }
1754     return ret;
1755 }
1756 
1757 /*!
1758     \reimp
1759 */
fileFlags(QAbstractFileEngine::FileFlags type) const1760 QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
1761 {
1762     Q_D(const QFSFileEngine);
1763     QAbstractFileEngine::FileFlags ret = 0;
1764     // Force a stat, so that we're guaranteed to get up-to-date results
1765     if (type & QAbstractFileEngine::FileFlag(QAbstractFileEngine::Refresh)) {
1766         d->tried_stat = 0;
1767     }
1768 
1769     if (type & PermsMask) {
1770         ret |= d->getPermissions();
1771         // ### Workaround pascals ### above. Since we always set all properties to true
1772         // we need to disable read and exec access if the file does not exists
1773         if (d->doStat())
1774             ret |= ExistsFlag;
1775         else
1776             ret &= 0x2222;
1777     }
1778     if (type & TypesMask) {
1779         if (d->filePath.endsWith(QLatin1String(".lnk"))) {
1780             ret |= LinkType;
1781             QString l = readLink(d->filePath);
1782             if (!l.isEmpty()) {
1783                 if (isDirPath(l, 0))
1784                     ret |= DirectoryType;
1785                 else
1786                     ret |= FileType;
1787             }
1788         } else if (d->doStat()) {
1789             if (d->fileAttrib & FILE_ATTRIBUTE_DIRECTORY) {
1790                 ret |= DirectoryType;
1791             } else {
1792                 ret |= FileType;
1793             }
1794         }
1795     }
1796     if (type & FlagsMask) {
1797         if(d->doStat()) {
1798             ret |= QAbstractFileEngine::FileFlags(ExistsFlag | LocalDiskFlag);
1799             if (d->fileAttrib & FILE_ATTRIBUTE_HIDDEN)
1800                 ret |= HiddenFlag;
1801             if (d->filePath == QLatin1String("/") || (d->filePath.at(0).isLetter() && d->filePath.mid(1,d->filePath.length()) == QLatin1String(":/"))
1802                 || isUncRoot(d->filePath)) {
1803                 ret |= RootFlag;
1804                 ret &= ~HiddenFlag;
1805             }
1806         }
1807     }
1808     return ret;
1809 }
1810 
1811 /*!
1812     \reimp
1813 */
fileName(FileName file) const1814 QString QFSFileEngine::fileName(FileName file) const
1815 {
1816     Q_D(const QFSFileEngine);
1817     if(file == BaseName) {
1818         int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
1819         if(slash == -1) {
1820             int colon = d->filePath.lastIndexOf(QLatin1Char(':'));
1821             if(colon != -1)
1822                 return d->filePath.mid(colon + 1);
1823             return d->filePath;
1824         }
1825         return d->filePath.mid(slash + 1);
1826     } else if(file == PathName) {
1827         if(!d->filePath.size())
1828             return d->filePath;
1829 
1830         int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
1831         if(slash == -1) {
1832             if(d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':'))
1833                 return d->filePath.left(2);
1834             return QString::fromLatin1(".");
1835         } else {
1836             if(!slash)
1837                 return QString::fromLatin1("/");
1838             if(slash == 2 && d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':'))
1839                 slash++;
1840             return d->filePath.left(slash);
1841         }
1842     } else if(file == AbsoluteName || file == AbsolutePathName) {
1843         QString ret;
1844 
1845         if (!isRelativePath()) {
1846 #if !defined(Q_OS_WINCE)
1847             if (d->filePath.size() > 2 && d->filePath.at(1) == QLatin1Char(':')
1848                 && d->filePath.at(2) != QLatin1Char('/') || // It's a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt
1849                 d->filePath.startsWith(QLatin1Char('/'))    // It's a absolute path to the current drive, so \a.txt -> Z:\a.txt
1850                 ) {
1851                 ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(d->filePath));
1852             } else {
1853                 ret = d->filePath;
1854             }
1855 #else
1856                 ret = d->filePath;
1857 #endif
1858         } else {
1859             ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + d->filePath);
1860         }
1861 
1862         // The path should be absolute at this point.
1863         // From the docs :
1864         // Absolute paths begin with the directory separator "/"
1865         // (optionally preceded by a drive specification under Windows).
1866         if (ret.at(0) != QLatin1Char('/')) {
1867             Q_ASSERT(ret.length() >= 2);
1868             Q_ASSERT(ret.at(0).isLetter());
1869             Q_ASSERT(ret.at(1) == QLatin1Char(':'));
1870 
1871             // Force uppercase drive letters.
1872             ret[0] = ret.at(0).toUpper();
1873         }
1874 
1875         if (file == AbsolutePathName) {
1876             int slash = ret.lastIndexOf(QLatin1Char('/'));
1877             if (slash < 0)
1878                 return ret;
1879             else if (ret.at(0) != QLatin1Char('/') && slash == 2)
1880                 return ret.left(3);      // include the slash
1881             else
1882                 return ret.left(slash > 0 ? slash : 1);
1883         }
1884         return ret;
1885     } else if(file == CanonicalName || file == CanonicalPathName) {
1886         if (!(fileFlags(ExistsFlag) & ExistsFlag))
1887             return QString();
1888 
1889         QString ret = QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName));
1890         if (!ret.isEmpty() && file == CanonicalPathName) {
1891             int slash = ret.lastIndexOf(QLatin1Char('/'));
1892             if (slash == -1)
1893                 ret = QDir::currentPath();
1894             else if (slash == 0)
1895                 ret = QLatin1String("/");
1896             ret = ret.left(slash);
1897         }
1898         return ret;
1899     } else if(file == LinkName) {
1900         return QDir::fromNativeSeparators(d->getLink());
1901     } else if(file == BundleName) {
1902         return QString();
1903     }
1904     return d->filePath;
1905 }
1906 
1907 /*!
1908     \reimp
1909 */
isRelativePath() const1910 bool QFSFileEngine::isRelativePath() const
1911 {
1912     Q_D(const QFSFileEngine);
1913     return !(d->filePath.startsWith(QLatin1Char('/'))
1914         || (d->filePath.length() >= 2
1915         && ((d->filePath.at(0).isLetter() && d->filePath.at(1) == QLatin1Char(':'))
1916         || (d->filePath.at(0) == QLatin1Char('/') && d->filePath.at(1) == QLatin1Char('/')))));                // drive, e.g. a:
1917 }
1918 
1919 /*!
1920     \reimp
1921 */
ownerId(FileOwner) const1922 uint QFSFileEngine::ownerId(FileOwner /*own*/) const
1923 {
1924     static const uint nobodyID = (uint) -2;
1925     return nobodyID;
1926 }
1927 
1928 /*!
1929     \reimp
1930 */
owner(FileOwner own) const1931 QString QFSFileEngine::owner(FileOwner own) const
1932 {
1933 #if !defined(QT_NO_LIBRARY)
1934     Q_D(const QFSFileEngine);
1935     if((qt_ntfs_permission_lookup > 0) && ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_NT)) {
1936 	PSID pOwner = 0;
1937 	PSECURITY_DESCRIPTOR pSD;
1938 	QString name;
1939 	QFSFileEnginePrivate::resolveLibs();
1940 
1941 	if(ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) {
1942 	    if(ptrGetNamedSecurityInfoW((wchar_t*)d->filePath.utf16(), SE_FILE_OBJECT,
1943 					 own == OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION,
1944 					 NULL, &pOwner, NULL, NULL, &pSD) == ERROR_SUCCESS) {
1945 		DWORD lowner = 0, ldomain = 0;
1946 		SID_NAME_USE use;
1947 		// First call, to determine size of the strings (with '\0').
1948 		ptrLookupAccountSidW(NULL, pOwner, NULL, &lowner, NULL, &ldomain, (SID_NAME_USE*)&use);
1949 		wchar_t *owner = new wchar_t[lowner];
1950 		wchar_t *domain = new wchar_t[ldomain];
1951 		// Second call, size is without '\0'
1952 		if(ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner, &lowner,
1953 					 (LPWSTR)domain, &ldomain, (SID_NAME_USE*)&use)) {
1954 		    name = QString::fromUtf16((ushort*)owner);
1955 		}
1956 		LocalFree(pSD);
1957 		delete [] owner;
1958 		delete [] domain;
1959 	    }
1960 	}
1961 	return name;
1962     }
1963 #else
1964     Q_UNUSED(own);
1965 #endif
1966     return QString(QLatin1String(""));
1967 }
1968 
1969 /*!
1970     \reimp
1971 */
setPermissions(uint perms)1972 bool QFSFileEngine::setPermissions(uint perms)
1973 {
1974     Q_D(QFSFileEngine);
1975     bool ret = false;
1976     int mode = 0;
1977 
1978     if (perms & QFile::ReadOwner || perms & QFile::ReadUser || perms & QFile::ReadGroup || perms & QFile::ReadOther)
1979         mode |= _S_IREAD;
1980     if (perms & QFile::WriteOwner || perms & QFile::WriteUser || perms & QFile::WriteGroup || perms & QFile::WriteOther)
1981         mode |= _S_IWRITE;
1982 
1983     if (mode == 0) // not supported
1984         return false;
1985 
1986 #if !defined(Q_OS_WINCE)
1987    QT_WA({
1988         ret = ::_wchmod((TCHAR*)d->filePath.utf16(), mode) == 0;
1989    } , {
1990         ret = ::_chmod(d->filePath.toLocal8Bit(), mode) == 0;
1991    });
1992 #else
1993     ret = ::_wchmod((TCHAR*)d->longFileName(d->filePath).utf16(), mode);
1994 #endif
1995    return ret;
1996 }
1997 
1998 /*!
1999     \reimp
2000 */
setSize(qint64 size)2001 bool QFSFileEngine::setSize(qint64 size)
2002 {
2003     Q_D(QFSFileEngine);
2004 
2005     if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1) {
2006         // resize open file
2007         HANDLE fh = d->fileHandle;
2008 #if !defined(Q_OS_WINCE)
2009         if (fh == INVALID_HANDLE_VALUE)
2010             fh = (HANDLE)_get_osfhandle(d->fd);
2011 #endif
2012         if (fh == INVALID_HANDLE_VALUE)
2013             return false;
2014         qint64 currentPos = pos();
2015 
2016         if (seek(size) && SetEndOfFile(fh)) {
2017             seek(qMin(currentPos, size));
2018             return true;
2019         }
2020 
2021         seek(currentPos);
2022         return false;
2023     }
2024 
2025     if (!d->nativeFilePath.isEmpty()) {
2026         // resize file on disk
2027         QFile file(d->filePath);
2028         if (file.open(QFile::ReadWrite)) {
2029             return file.resize(size);
2030         }
2031     }
2032     return false;
2033 }
2034 
2035 
fileTimeToQDateTime(const FILETIME * time)2036 static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
2037 {
2038     QDateTime ret;
2039     if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based || QSysInfo::WindowsVersion & QSysInfo::WV_CE_based) {
2040         // SystemTimeToTzSpecificLocalTime is not available on Win98/ME so we have to pull it off ourselves.
2041         SYSTEMTIME systime;
2042         FILETIME ftime;
2043         systime.wYear = 1970;
2044         systime.wMonth = 1;
2045         systime.wDay = 1;
2046         systime.wHour = 0;
2047         systime.wMinute = 0;
2048         systime.wSecond = 0;
2049         systime.wMilliseconds = 0;
2050         systime.wDayOfWeek = 4;
2051         SystemTimeToFileTime(&systime, &ftime);
2052         unsigned __int64 acttime = (unsigned __int64)time->dwHighDateTime << 32 | time->dwLowDateTime;
2053         FileTimeToSystemTime(time, &systime);
2054         unsigned __int64 time1970 = (unsigned __int64)ftime.dwHighDateTime << 32 | ftime.dwLowDateTime;
2055         unsigned __int64 difftime = acttime - time1970;
2056         difftime /= 10000000;
2057         ret.setTime_t((unsigned int)difftime);
2058     } else {
2059 #ifndef Q_OS_WINCE
2060         SYSTEMTIME sTime, lTime;
2061         FileTimeToSystemTime(time, &sTime);
2062         SystemTimeToTzSpecificLocalTime(0, &sTime, &lTime);
2063         ret.setDate(QDate(lTime.wYear, lTime.wMonth, lTime.wDay));
2064         ret.setTime(QTime(lTime.wHour, lTime.wMinute, lTime.wSecond, lTime.wMilliseconds));
2065 #endif
2066     }
2067     return ret;
2068 }
2069 
2070 /*!
2071     \reimp
2072 */
fileTime(FileTime time) const2073 QDateTime QFSFileEngine::fileTime(FileTime time) const
2074 {
2075     Q_D(const QFSFileEngine);
2076     QDateTime ret;
2077     if (d->fd != -1) {
2078 #if !defined(Q_OS_WINCE)
2079         HANDLE fh = (HANDLE)_get_osfhandle(d->fd);
2080         if (fh != INVALID_HANDLE_VALUE) {
2081             FILETIME creationTime, lastAccessTime, lastWriteTime;
2082             if (GetFileTime(fh, &creationTime, &lastAccessTime, &lastWriteTime)) {
2083                 if(time == CreationTime)
2084                     ret = fileTimeToQDateTime(&creationTime);
2085                 else if(time == ModificationTime)
2086                     ret = fileTimeToQDateTime(&lastWriteTime);
2087                 else if(time == AccessTime)
2088                     ret = fileTimeToQDateTime(&lastAccessTime);
2089             }
2090         }
2091 #endif
2092     } else {
2093         bool ok = false;
2094         WIN32_FILE_ATTRIBUTE_DATA attribData;
2095         QT_WA({
2096             ok = ::GetFileAttributesExW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(), GetFileExInfoStandard, &attribData);
2097         } , {
2098             ok = ::GetFileAttributesExA(QFSFileEnginePrivate::win95Name(QFileInfo(d->filePath).absoluteFilePath()), GetFileExInfoStandard, &attribData);
2099         });
2100         if (ok) {
2101             if(time == CreationTime)
2102                 ret = fileTimeToQDateTime(&attribData.ftCreationTime);
2103             else if(time == ModificationTime)
2104                 ret = fileTimeToQDateTime(&attribData.ftLastWriteTime);
2105             else if(time == AccessTime)
2106                 ret = fileTimeToQDateTime(&attribData.ftLastAccessTime);
2107         }
2108     }
2109     return ret;
2110 }
2111 
map(qint64 offset,qint64 size,QFile::MemoryMapFlags flags)2112 uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
2113                                  QFile::MemoryMapFlags flags)
2114 {
2115     Q_Q(QFSFileEngine);
2116     Q_UNUSED(flags);
2117     if (openMode == QFile::NotOpen) {
2118         q->setError(QFile::PermissionsError, qt_error_string());
2119 	return 0;
2120     }
2121     if (offset == 0 && size == 0) {
2122         q->setError(QFile::UnspecifiedError, qt_error_string());
2123 	return 0;
2124     }
2125 
2126 
2127     // get handle to the file
2128     HANDLE handle = fileHandle;
2129 #ifndef Q_OS_WINCE
2130     if (handle == INVALID_HANDLE_VALUE && fh)
2131         handle = (HANDLE)_get_osfhandle(QT_FILENO(fh));
2132 #else
2133     #ifdef Q_USE_DEPRECATED_MAP_API
2134     nativeClose();
2135     if (fileMapHandle == INVALID_HANDLE_VALUE) {
2136         fileMapHandle = CreateFileForMappingW((TCHAR *)nativeFilePath.constData(),
2137                 GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0),
2138                 0,
2139                 NULL,
2140                 OPEN_EXISTING,
2141                 FILE_ATTRIBUTE_NORMAL,
2142                 NULL);
2143     }
2144     handle = fileMapHandle;
2145     #endif
2146     if (handle == INVALID_HANDLE_VALUE && fh)
2147         return 0;
2148 #endif
2149 
2150     // first create the file mapping handle
2151     HANDLE mapHandle = 0;
2152     DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY;
2153     QT_WA({
2154     mapHandle = ::CreateFileMappingW(handle, 0, protection,
2155              0, 0, 0);
2156     },{
2157     mapHandle = ::CreateFileMappingA(handle, 0, protection,
2158              0, 0, 0);
2159     });
2160     if (mapHandle == NULL) {
2161         q->setError(QFile::PermissionsError, qt_error_string());
2162 #ifdef Q_USE_DEPRECATED_MAP_API
2163         mapHandleClose();
2164 #endif
2165 	return 0;
2166     }
2167 
2168     // setup args to map
2169     DWORD access = 0;
2170     if (openMode & QIODevice::ReadOnly) access = FILE_MAP_READ;
2171     if (openMode & QIODevice::WriteOnly) access = FILE_MAP_WRITE;
2172 
2173     DWORD offsetHi = offset >> 32;
2174     DWORD offsetLo = offset & Q_UINT64_C(0xffffffff);
2175     SYSTEM_INFO sysinfo;
2176     ::GetSystemInfo(&sysinfo);
2177     int mask = sysinfo.dwAllocationGranularity - 1;
2178     int extra = offset & mask;
2179     if (extra)
2180         offsetLo &= ~mask;
2181 
2182     // attempt to create the map
2183     LPVOID mapAddress = MapViewOfFile(mapHandle, access,
2184                                       offsetHi, offsetLo, size + extra);
2185     if (mapAddress) {
2186         uchar *address = extra + static_cast<uchar*>(mapAddress);
2187         maps[address] = QPair<int, HANDLE>(extra, mapHandle);
2188         return address;
2189     }
2190 
2191     switch(GetLastError()) {
2192     case ERROR_ACCESS_DENIED:
2193         q->setError(QFile::PermissionsError, qt_error_string());
2194 	break;
2195     case ERROR_INVALID_PARAMETER:
2196         // size are out of bounds
2197     default:
2198         q->setError(QFile::UnspecifiedError, qt_error_string());
2199     }
2200     CloseHandle(mapHandle);
2201 #ifdef Q_USE_DEPRECATED_MAP_API
2202     mapHandleClose();
2203 #endif
2204     return 0;
2205 }
2206 
unmap(uchar * ptr)2207 bool QFSFileEnginePrivate::unmap(uchar *ptr)
2208 {
2209     Q_Q(QFSFileEngine);
2210     if (!maps.contains(ptr)) {
2211         q->setError(QFile::PermissionsError, qt_error_string());
2212         return false;
2213     }
2214     uchar *start = ptr - maps[ptr].first;
2215     if (!UnmapViewOfFile(start)) {
2216         q->setError(QFile::PermissionsError, qt_error_string());
2217         return false;
2218     }
2219 
2220     if (!CloseHandle((HANDLE)maps[ptr].second)) {
2221         q->setError(QFile::UnspecifiedError, qt_error_string());
2222         return false;
2223     }
2224     maps.remove(ptr);
2225 
2226 #ifdef Q_USE_DEPRECATED_MAP_API
2227     mapHandleClose();
2228 #endif
2229     return true;
2230 }
2231 
2232 #ifdef Q_USE_DEPRECATED_MAP_API
mapHandleClose()2233 void QFSFileEnginePrivate::mapHandleClose()
2234 {
2235     if (maps.isEmpty()) {
2236         CloseHandle(fileMapHandle);
2237         fileMapHandle = INVALID_HANDLE_VALUE;
2238     }
2239 }
2240 #endif
2241 QT_END_NAMESPACE
2242