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