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(¤tUserTrusteeW, (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, ¤tFilePos, 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, ¤tFilePos, 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 ©Name)
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, ¤tUserTrusteeW, &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