(Windows) Don't create a file mapping for each view that is mapped
[qt-netbsd.git] / src / corelib / io / qfsfileengine_win.cpp
blob151eabd7fa56f8ba661ef14ddf1256a0a4a74e0c
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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
42 #define _POSIX_
43 #include "qplatformdefs.h"
44 #include "qabstractfileengine.h"
45 #include "private/qfsfileengine_p.h"
46 #include <qdebug.h>
48 #include "qfile.h"
49 #include "qdir.h"
50 #include "private/qmutexpool_p.h"
51 #include "qvarlengtharray.h"
52 #include "qdatetime.h"
53 #include "qt_windows.h"
55 #if !defined(Q_OS_WINCE)
56 # include <sys/types.h>
57 # include <direct.h>
58 # include <winioctl.h>
59 #else
60 # include <types.h>
61 #endif
62 #include <objbase.h>
63 #include <shlobj.h>
64 #include <initguid.h>
65 #include <accctrl.h>
66 #include <ctype.h>
67 #include <limits.h>
68 #define SECURITY_WIN32
69 #ifdef Q_CC_MINGW
70 // A workaround for a certain version of MinGW, the define UNICODE_STRING.
71 #include <subauth.h>
72 #endif
73 #include <security.h>
75 #ifndef _INTPTR_T_DEFINED
76 #ifdef _WIN64
77 typedef __int64 intptr_t;
78 #else
79 #ifdef _W64
80 typedef _W64 int intptr_t;
81 #else
82 typedef INT_PTR intptr_t;
83 #endif
84 #endif
85 #define _INTPTR_T_DEFINED
86 #endif
88 #ifndef INVALID_FILE_ATTRIBUTES
89 # define INVALID_FILE_ATTRIBUTES (DWORD (-1))
90 #endif
92 #if !defined(Q_OS_WINCE)
93 # if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
94 typedef struct _REPARSE_DATA_BUFFER {
95 ULONG ReparseTag;
96 USHORT ReparseDataLength;
97 USHORT Reserved;
98 union {
99 struct {
100 USHORT SubstituteNameOffset;
101 USHORT SubstituteNameLength;
102 USHORT PrintNameOffset;
103 USHORT PrintNameLength;
104 ULONG Flags;
105 WCHAR PathBuffer[1];
106 } SymbolicLinkReparseBuffer;
107 struct {
108 USHORT SubstituteNameOffset;
109 USHORT SubstituteNameLength;
110 USHORT PrintNameOffset;
111 USHORT PrintNameLength;
112 WCHAR PathBuffer[1];
113 } MountPointReparseBuffer;
114 struct {
115 UCHAR DataBuffer[1];
116 } GenericReparseBuffer;
118 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
119 # define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
120 # endif // !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
122 # ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
123 # define MAXIMUM_REPARSE_DATA_BUFFER_SIZE 16384
124 # endif
125 # ifndef IO_REPARSE_TAG_SYMLINK
126 # define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
127 # endif
128 # ifndef FSCTL_GET_REPARSE_POINT
129 # define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
130 # endif
131 #endif // !defined(Q_OS_WINCE)
133 QT_BEGIN_NAMESPACE
135 static QString readLink(const QString &link);
137 Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
139 #if defined(Q_OS_WINCE)
140 static QString qfsPrivateCurrentDir = QLatin1String("");
141 // As none of the functions we try to resolve do exist on Windows CE
142 // we use QT_NO_LIBRARY to shorten everything up a little bit.
143 #define QT_NO_LIBRARY 1
144 #endif
146 #if !defined(QT_NO_LIBRARY)
147 QT_BEGIN_INCLUDE_NAMESPACE
148 typedef DWORD (WINAPI *PtrGetNamedSecurityInfoW)(LPWSTR, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID*, PSID*, PACL*, PACL*, PSECURITY_DESCRIPTOR*);
149 static PtrGetNamedSecurityInfoW ptrGetNamedSecurityInfoW = 0;
150 typedef BOOL (WINAPI *PtrLookupAccountSidW)(LPCWSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, PSID_NAME_USE);
151 static PtrLookupAccountSidW ptrLookupAccountSidW = 0;
152 typedef VOID (WINAPI *PtrBuildTrusteeWithSidW)(PTRUSTEE_W, PSID);
153 static PtrBuildTrusteeWithSidW ptrBuildTrusteeWithSidW = 0;
154 typedef DWORD (WINAPI *PtrGetEffectiveRightsFromAclW)(PACL, PTRUSTEE_W, OUT PACCESS_MASK);
155 static PtrGetEffectiveRightsFromAclW ptrGetEffectiveRightsFromAclW = 0;
156 static TRUSTEE_W currentUserTrusteeW;
157 static TRUSTEE_W worldTrusteeW;
159 typedef BOOL (WINAPI *PtrGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD);
160 static PtrGetUserProfileDirectoryW ptrGetUserProfileDirectoryW = 0;
161 QT_END_INCLUDE_NAMESPACE
164 void QFSFileEnginePrivate::resolveLibs()
166 static bool triedResolve = false;
167 if (!triedResolve) {
168 // need to resolve the security info functions
170 // protect initialization
171 #ifndef QT_NO_THREAD
172 QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
173 // check triedResolve again, since another thread may have already
174 // done the initialization
175 if (triedResolve) {
176 // another thread did initialize the security function pointers,
177 // so we shouldn't do it again.
178 return;
180 #endif
182 triedResolve = true;
183 #if !defined(Q_OS_WINCE)
184 HINSTANCE advapiHnd = LoadLibraryW(L"advapi32");
185 if (advapiHnd) {
186 ptrGetNamedSecurityInfoW = (PtrGetNamedSecurityInfoW)GetProcAddress(advapiHnd, "GetNamedSecurityInfoW");
187 ptrLookupAccountSidW = (PtrLookupAccountSidW)GetProcAddress(advapiHnd, "LookupAccountSidW");
188 ptrBuildTrusteeWithSidW = (PtrBuildTrusteeWithSidW)GetProcAddress(advapiHnd, "BuildTrusteeWithSidW");
189 ptrGetEffectiveRightsFromAclW = (PtrGetEffectiveRightsFromAclW)GetProcAddress(advapiHnd, "GetEffectiveRightsFromAclW");
191 if (ptrBuildTrusteeWithSidW) {
192 // Create TRUSTEE for current user
193 HANDLE hnd = ::GetCurrentProcess();
194 HANDLE token = 0;
195 if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) {
196 TOKEN_USER tu;
197 DWORD retsize;
198 if (::GetTokenInformation(token, TokenUser, &tu, sizeof(tu), &retsize))
199 ptrBuildTrusteeWithSidW(&currentUserTrusteeW, tu.User.Sid);
200 ::CloseHandle(token);
203 typedef BOOL (WINAPI *PtrAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*);
204 PtrAllocateAndInitializeSid ptrAllocateAndInitializeSid = (PtrAllocateAndInitializeSid)GetProcAddress(advapiHnd, "AllocateAndInitializeSid");
205 typedef PVOID (WINAPI *PtrFreeSid)(PSID);
206 PtrFreeSid ptrFreeSid = (PtrFreeSid)GetProcAddress(advapiHnd, "FreeSid");
207 if (ptrAllocateAndInitializeSid && ptrFreeSid) {
208 // Create TRUSTEE for Everyone (World)
209 SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY };
210 PSID pWorld = 0;
211 if (ptrAllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pWorld))
212 ptrBuildTrusteeWithSidW(&worldTrusteeW, pWorld);
213 ptrFreeSid(pWorld);
216 HINSTANCE userenvHnd = LoadLibraryW(L"userenv");
217 if (userenvHnd)
218 ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW");
219 #endif
222 #endif // QT_NO_LIBRARY
224 // UNC functions NT
225 typedef DWORD (WINAPI *PtrNetShareEnum)(LPWSTR, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD);
226 static PtrNetShareEnum ptrNetShareEnum = 0;
227 typedef DWORD (WINAPI *PtrNetApiBufferFree)(LPVOID);
228 static PtrNetApiBufferFree ptrNetApiBufferFree = 0;
229 typedef struct _SHARE_INFO_1 {
230 LPWSTR shi1_netname;
231 DWORD shi1_type;
232 LPWSTR shi1_remark;
233 } SHARE_INFO_1;
236 bool QFSFileEnginePrivate::resolveUNCLibs()
238 static bool triedResolve = false;
239 if (!triedResolve) {
240 #ifndef QT_NO_THREAD
241 QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
242 if (triedResolve) {
243 return ptrNetShareEnum && ptrNetApiBufferFree;
245 #endif
246 triedResolve = true;
247 #if !defined(Q_OS_WINCE)
248 HINSTANCE hLib = LoadLibraryW(L"Netapi32");
249 if (hLib) {
250 ptrNetShareEnum = (PtrNetShareEnum)GetProcAddress(hLib, "NetShareEnum");
251 if (ptrNetShareEnum)
252 ptrNetApiBufferFree = (PtrNetApiBufferFree)GetProcAddress(hLib, "NetApiBufferFree");
254 #endif
256 return ptrNetShareEnum && ptrNetApiBufferFree;
259 bool QFSFileEnginePrivate::uncListSharesOnServer(const QString &server, QStringList *list)
261 if (resolveUNCLibs()) {
262 SHARE_INFO_1 *BufPtr, *p;
263 DWORD res;
264 DWORD er = 0, tr = 0, resume = 0, i;
265 do {
266 res = ptrNetShareEnum((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume);
267 if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) {
268 p = BufPtr;
269 for (i = 1; i <= er; ++i) {
270 if (list && p->shi1_type == 0)
271 list->append(QString::fromWCharArray(p->shi1_netname));
272 p++;
275 ptrNetApiBufferFree(BufPtr);
276 } while (res == ERROR_MORE_DATA);
277 return res == ERROR_SUCCESS;
279 return false;
282 static bool isUncRoot(const QString &server)
284 QString localPath = QDir::toNativeSeparators(server);
285 if (!localPath.startsWith(QLatin1String("\\\\")))
286 return false;
288 int idx = localPath.indexOf(QLatin1Char('\\'), 2);
289 if (idx == -1 || idx + 1 == localPath.length())
290 return true;
292 localPath = localPath.right(localPath.length() - idx - 1).trimmed();
293 return localPath.isEmpty();
296 #if !defined(Q_OS_WINCE)
297 static inline bool isUncPath(const QString &path)
299 // Starts with \\, but not \\.
300 return (path.startsWith(QLatin1String("\\\\"))
301 && path.size() > 2 && path.at(2) != QLatin1Char('.'));
303 #endif
305 static inline bool isRelativePath(const QString &path)
307 // drive, e.g. "a:", or UNC root, e.q. "//"
308 return !(path.startsWith(QLatin1Char('/'))
309 || (path.length() >= 2
310 && ((path.at(0).isLetter() && path.at(1) == QLatin1Char(':'))
311 || (path.at(0) == QLatin1Char('/') && path.at(1) == QLatin1Char('/')))));
314 static QString fixIfRelativeUncPath(const QString &path)
316 if (isRelativePath(path)) {
317 QString currentPath = QDir::currentPath() + QLatin1Char('/');
318 if (currentPath.startsWith(QLatin1String("//")))
319 return QString(path).prepend(currentPath);
321 return path;
324 // can be //server or //server/share
325 static bool uncShareExists(const QString &server)
327 QStringList parts = server.split(QLatin1Char('\\'), QString::SkipEmptyParts);
328 if (parts.count()) {
329 QStringList shares;
330 if (QFSFileEnginePrivate::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(0), &shares))
331 return parts.count() >= 2 ? shares.contains(parts.at(1), Qt::CaseInsensitive) : true;
333 return false;
336 static inline bool isDriveRoot(const QString &path)
338 return (path.length() == 3
339 && path.at(0).isLetter() && path.at(1) == QLatin1Char(':')
340 && path.at(2) == QLatin1Char('/'));
343 static QString nativeAbsoluteFilePath(const QString &path)
345 QString absPath;
346 #if !defined(Q_OS_WINCE)
347 QVarLengthArray<wchar_t, MAX_PATH> buf(qMax(MAX_PATH, path.size() + 1));
348 wchar_t *fileName = 0;
349 DWORD retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
350 if (retLen > (DWORD)buf.size()) {
351 buf.resize(retLen);
352 retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
354 if (retLen != 0)
355 absPath = QString::fromWCharArray(buf.data(), retLen);
356 #else
357 if (path.startsWith(QLatin1Char('/')) || path.startsWith(QLatin1Char('\\')))
358 absPath = QDir::toNativeSeparators(path);
359 else
360 absPath = QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir + QLatin1Char('/') + path));
361 #endif
362 // This is really ugly, but GetFullPathName strips off whitespace at the end.
363 // If you for instance write ". " in the lineedit of QFileDialog,
364 // (which is an invalid filename) this function will strip the space off and viola,
365 // the file is later reported as existing. Therefore, we re-add the whitespace that
366 // was at the end of path in order to keep the filename invalid.
367 if (!path.isEmpty() && path.at(path.size() - 1) == QLatin1Char(' '))
368 absPath.append(QLatin1Char(' '));
369 return absPath;
373 \internal
375 QString QFSFileEnginePrivate::longFileName(const QString &path)
377 if (path.startsWith(QLatin1String("\\\\.\\")))
378 return path;
380 QString absPath = nativeAbsoluteFilePath(path);
381 #if !defined(Q_OS_WINCE)
382 QString prefix = QLatin1String("\\\\?\\");
383 if (isUncPath(absPath)) {
384 prefix.append(QLatin1String("UNC\\")); // "\\\\?\\UNC\\"
385 absPath.remove(0, 2);
387 return prefix + absPath;
388 #else
389 return absPath;
390 #endif
394 \internal
396 void QFSFileEnginePrivate::nativeInitFileName()
398 QString path = longFileName(QDir::toNativeSeparators(fixIfRelativeUncPath(filePath)));
399 nativeFilePath = QByteArray((const char *)path.utf16(), path.size() * 2 + 1);
403 \internal
405 bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
407 Q_Q(QFSFileEngine);
409 // All files are opened in share mode (both read and write).
410 DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
412 int accessRights = 0;
413 if (openMode & QIODevice::ReadOnly)
414 accessRights |= GENERIC_READ;
415 if (openMode & QIODevice::WriteOnly)
416 accessRights |= GENERIC_WRITE;
418 SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
420 // WriteOnly can create files, ReadOnly cannot.
421 DWORD creationDisp = (openMode & QIODevice::WriteOnly) ? OPEN_ALWAYS : OPEN_EXISTING;
423 // Create the file handle.
424 fileHandle = CreateFile((const wchar_t*)nativeFilePath.constData(),
425 accessRights,
426 shareMode,
427 &securityAtts,
428 creationDisp,
429 FILE_ATTRIBUTE_NORMAL,
430 NULL);
432 // Bail out on error.
433 if (fileHandle == INVALID_HANDLE_VALUE) {
434 q->setError(QFile::OpenError, qt_error_string());
435 return false;
438 // Truncate the file after successfully opening it if Truncate is passed.
439 if (openMode & QIODevice::Truncate)
440 q->setSize(0);
442 return true;
446 \internal
448 bool QFSFileEnginePrivate::nativeClose()
450 Q_Q(QFSFileEngine);
451 if (fh || fd != -1) {
452 // stdlib / stdio mode.
453 return closeFdFh();
456 // Windows native mode.
457 bool ok = true;
458 if ((fileHandle == INVALID_HANDLE_VALUE || !::CloseHandle(fileHandle))) {
459 q->setError(QFile::UnspecifiedError, qt_error_string());
460 ok = false;
462 fileHandle = INVALID_HANDLE_VALUE;
463 cachedFd = -1; // gets closed by CloseHandle above
465 return ok;
469 \internal
471 bool QFSFileEnginePrivate::nativeFlush()
473 if (fh) {
474 // Buffered stdlib mode.
475 return flushFh();
477 if (fd != -1) {
478 // Unbuffered stdio mode; always succeeds (no buffer).
479 return true;
482 // Windows native mode; flushing is
483 // unnecessary. FlushFileBuffers(), the equivalent of sync() or
484 // fsync() on Unix, does a low-level flush to the disk, and we
485 // don't expose an API for this.
486 return true;
490 \internal
492 qint64 QFSFileEnginePrivate::nativeSize() const
494 Q_Q(const QFSFileEngine);
495 QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
497 // ### Don't flush; for buffered files, we should get away with ftell.
498 thatQ->flush();
500 // Buffered stdlib mode.
501 if (fh) {
502 QT_OFF_T oldPos = QT_FTELL(fh);
503 QT_FSEEK(fh, 0, SEEK_END);
504 QT_OFF_T fileSize = QT_FTELL(fh);
505 QT_FSEEK(fh, oldPos, SEEK_SET);
506 return qint64(fileSize);
509 // Not-open mode, where the file name is known: We'll check the
510 // file system directly.
511 if (openMode == QIODevice::NotOpen && !nativeFilePath.isEmpty()) {
512 WIN32_FILE_ATTRIBUTE_DATA attribData;
513 bool ok = ::GetFileAttributesEx((const wchar_t*)nativeFilePath.constData(),
514 GetFileExInfoStandard, &attribData);
515 if (!ok) {
516 int errorCode = GetLastError();
517 if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
518 QByteArray path = nativeFilePath;
519 // path for the FindFirstFile should not end with a trailing slash
520 while (path.endsWith('\\'))
521 path.chop(1);
523 // FindFirstFile can not handle drives
524 if (!path.endsWith(':')) {
525 WIN32_FIND_DATA findData;
526 HANDLE hFind = ::FindFirstFile((const wchar_t*)path.constData(),
527 &findData);
528 if (hFind != INVALID_HANDLE_VALUE) {
529 ::FindClose(hFind);
530 ok = true;
531 attribData.nFileSizeHigh = findData.nFileSizeHigh;
532 attribData.nFileSizeLow = findData.nFileSizeLow;
537 if (ok) {
538 qint64 size = attribData.nFileSizeHigh;
539 size <<= 32;
540 size += attribData.nFileSizeLow;
541 return size;
543 thatQ->setError(QFile::UnspecifiedError, qt_error_string());
544 return 0;
547 // Unbuffed stdio mode.
548 if(fd != -1) {
549 #if !defined(Q_OS_WINCE)
550 HANDLE handle = (HANDLE)_get_osfhandle(fd);
551 if (handle != INVALID_HANDLE_VALUE) {
552 BY_HANDLE_FILE_INFORMATION fileInfo;
553 if (GetFileInformationByHandle(handle, &fileInfo)) {
554 qint64 size = fileInfo.nFileSizeHigh;
555 size <<= 32;
556 size += fileInfo.nFileSizeLow;
557 return size;
560 #endif
561 thatQ->setError(QFile::UnspecifiedError, qt_error_string());
562 return 0;
565 // Windows native mode.
566 if (fileHandle == INVALID_HANDLE_VALUE)
567 return 0;
569 BY_HANDLE_FILE_INFORMATION fileInfo;
570 if (!GetFileInformationByHandle(fileHandle, &fileInfo)) {
571 thatQ->setError(QFile::UnspecifiedError, qt_error_string());
572 return 0;
575 qint64 size = fileInfo.nFileSizeHigh;
576 size <<= 32;
577 size += fileInfo.nFileSizeLow;
578 return size;
582 \internal
584 qint64 QFSFileEnginePrivate::nativePos() const
586 Q_Q(const QFSFileEngine);
587 QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
589 if (fh || fd != -1) {
590 // stdlib / stido mode.
591 return posFdFh();
594 // Windows native mode.
595 if (fileHandle == INVALID_HANDLE_VALUE)
596 return 0;
598 #if !defined(Q_OS_WINCE)
599 LARGE_INTEGER currentFilePos;
600 LARGE_INTEGER offset;
601 offset.QuadPart = 0;
602 if (!::SetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_CURRENT)) {
603 thatQ->setError(QFile::UnspecifiedError, qt_error_string());
604 return 0;
607 return qint64(currentFilePos.QuadPart);
608 #else
609 LARGE_INTEGER filepos;
610 filepos.HighPart = 0;
611 DWORD newFilePointer = SetFilePointer(fileHandle, 0, &filepos.HighPart, FILE_CURRENT);
612 if (newFilePointer == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
613 thatQ->setError(QFile::UnspecifiedError, qt_error_string());
614 return 0;
617 filepos.LowPart = newFilePointer;
618 return filepos.QuadPart;
619 #endif
623 \internal
625 bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
627 Q_Q(QFSFileEngine);
629 if (fh || fd != -1) {
630 // stdlib / stdio mode.
631 return seekFdFh(pos);
634 #if !defined(Q_OS_WINCE)
635 LARGE_INTEGER currentFilePos;
636 LARGE_INTEGER offset;
637 offset.QuadPart = pos;
638 if (!::SetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_BEGIN)) {
639 q->setError(QFile::UnspecifiedError, qt_error_string());
640 return false;
643 return true;
644 #else
645 DWORD newFilePointer;
646 LARGE_INTEGER *li = reinterpret_cast<LARGE_INTEGER*>(&pos);
647 newFilePointer = SetFilePointer(fileHandle, li->LowPart, &li->HighPart, FILE_BEGIN);
648 if (newFilePointer == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
649 q->setError(QFile::PositionError, qt_error_string());
650 return false;
653 return true;
654 #endif
658 \internal
660 qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 maxlen)
662 Q_Q(QFSFileEngine);
664 if (fh || fd != -1) {
665 // stdio / stdlib mode.
666 if (fh && nativeIsSequential() && feof(fh)) {
667 q->setError(QFile::ReadError, qt_error_string(int(errno)));
668 return -1;
671 return readFdFh(data, maxlen);
674 // Windows native mode.
675 if (fileHandle == INVALID_HANDLE_VALUE)
676 return -1;
678 DWORD bytesToRead = DWORD(maxlen); // <- lossy
680 // Reading on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
681 // the chunks are too large, so we limit the block size to 32MB.
682 static const DWORD maxBlockSize = 32 * 1024 * 1024;
684 qint64 totalRead = 0;
685 do {
686 DWORD blockSize = qMin<DWORD>(bytesToRead, maxBlockSize);
687 DWORD bytesRead;
688 if (!ReadFile(fileHandle, data + totalRead, blockSize, &bytesRead, NULL)) {
689 if (totalRead == 0) {
690 // Note: only return failure if the first ReadFile fails.
691 q->setError(QFile::ReadError, qt_error_string());
692 return -1;
694 break;
696 if (bytesRead == 0)
697 break;
698 totalRead += bytesRead;
699 bytesToRead -= bytesRead;
700 } while (totalRead < maxlen);
701 return qint64(totalRead);
705 \internal
707 qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
709 Q_Q(QFSFileEngine);
711 if (fh || fd != -1) {
712 // stdio / stdlib mode.
713 return readLineFdFh(data, maxlen);
716 // Windows native mode.
717 if (fileHandle == INVALID_HANDLE_VALUE)
718 return -1;
720 // ### No equivalent in Win32?
721 return q->QAbstractFileEngine::readLine(data, maxlen);
725 \internal
727 qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
729 Q_Q(QFSFileEngine);
731 if (fh || fd != -1) {
732 // stdio / stdlib mode.
733 return writeFdFh(data, len);
736 // Windows native mode.
737 if (fileHandle == INVALID_HANDLE_VALUE)
738 return -1;
740 qint64 bytesToWrite = DWORD(len); // <- lossy
742 // Writing on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
743 // the chunks are too large, so we limit the block size to 32MB.
744 static const DWORD maxBlockSize = 32 * 1024 * 1024;
746 qint64 totalWritten = 0;
747 do {
748 DWORD blockSize = qMin<DWORD>(bytesToWrite, maxBlockSize);
749 DWORD bytesWritten;
750 if (!WriteFile(fileHandle, data + totalWritten, blockSize, &bytesWritten, NULL)) {
751 if (totalWritten == 0) {
752 // Note: Only return error if the first WriteFile failed.
753 q->setError(QFile::WriteError, qt_error_string());
754 return -1;
756 break;
758 if (bytesWritten == 0)
759 break;
760 totalWritten += bytesWritten;
761 bytesToWrite -= bytesWritten;
762 } while (totalWritten < len);
763 return qint64(totalWritten);
767 \internal
769 int QFSFileEnginePrivate::nativeHandle() const
771 if (fh || fd != -1)
772 return fh ? QT_FILENO(fh) : fd;
773 #ifndef Q_OS_WINCE
774 if (cachedFd != -1)
775 return cachedFd;
777 int flags = 0;
778 if (openMode & QIODevice::Append)
779 flags |= _O_APPEND;
780 if (!(openMode & QIODevice::WriteOnly))
781 flags |= _O_RDONLY;
782 cachedFd = _open_osfhandle((intptr_t) fileHandle, flags);
783 return cachedFd;
784 #else
785 return -1;
786 #endif
790 \internal
792 bool QFSFileEnginePrivate::nativeIsSequential() const
794 #if !defined(Q_OS_WINCE)
795 // stdlib / Windows native mode.
796 if (fh || fileHandle != INVALID_HANDLE_VALUE) {
797 if (fh == stdin || fh == stdout || fh == stderr)
798 return true;
800 HANDLE handle = fileHandle;
801 if (fileHandle == INVALID_HANDLE_VALUE) {
802 // Rare case: using QFile::open(FILE*) to open a pipe.
803 handle = (HANDLE)_get_osfhandle(QT_FILENO(fh));
804 return false;
807 DWORD fileType = GetFileType(handle);
808 return fileType == FILE_TYPE_PIPE;
811 // stdio mode.
812 if (fd != -1)
813 return isSequentialFdFh();
814 #endif
815 return false;
818 bool QFSFileEngine::remove()
820 Q_D(QFSFileEngine);
821 bool ret = ::DeleteFile((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16()) != 0;
822 if (!ret)
823 setError(QFile::RemoveError, qt_error_string());
824 return ret;
827 bool QFSFileEngine::copy(const QString &copyName)
829 Q_D(QFSFileEngine);
830 bool ret = ::CopyFile((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(),
831 (wchar_t*)QFSFileEnginePrivate::longFileName(copyName).utf16(), true) != 0;
832 if (!ret)
833 setError(QFile::CopyError, qt_error_string());
834 return ret;
837 bool QFSFileEngine::rename(const QString &newName)
839 Q_D(QFSFileEngine);
840 bool ret = ::MoveFile((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(),
841 (wchar_t*)QFSFileEnginePrivate::longFileName(newName).utf16()) != 0;
842 if (!ret)
843 setError(QFile::RenameError, qt_error_string());
844 return ret;
847 static inline bool mkDir(const QString &path)
849 #if defined(Q_OS_WINCE)
850 // Unfortunately CreateDirectory returns true for paths longer than
851 // 256, but does not create a directory. It starts to fail, when
852 // path length > MAX_PATH, which is 260 usually on CE.
853 // This only happens on a Windows Mobile device. Windows CE seems
854 // not to be affected by this.
855 static int platformId = 0;
856 if (platformId == 0) {
857 wchar_t platformString[64];
858 if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(platformString)/sizeof(*platformString),platformString,0)) {
859 if (0 == wcscmp(platformString, L"PocketPC") || 0 == wcscmp(platformString, L"Smartphone"))
860 platformId = 1;
861 else
862 platformId = 2;
865 if (platformId == 1 && QFSFileEnginePrivate::longFileName(path).size() > 256)
866 return false;
867 #endif
868 return ::CreateDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(), 0);
871 static inline bool rmDir(const QString &path)
873 return ::RemoveDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16());
876 static bool isDirPath(const QString &dirPath, bool *existed)
878 QString path = dirPath;
879 if (path.length() == 2 && path.at(1) == QLatin1Char(':'))
880 path += QLatin1Char('\\');
882 DWORD fileAttrib = ::GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16());
883 if (fileAttrib == INVALID_FILE_ATTRIBUTES) {
884 int errorCode = GetLastError();
885 if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
886 // path for the FindFirstFile should not end with a trailing slash
887 while (path.endsWith(QLatin1Char('\\')))
888 path.chop(1);
890 // FindFirstFile can not handle drives
891 if (!path.endsWith(QLatin1Char(':'))) {
892 WIN32_FIND_DATA findData;
893 HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(),
894 &findData);
895 if (hFind != INVALID_HANDLE_VALUE) {
896 ::FindClose(hFind);
897 fileAttrib = findData.dwFileAttributes;
903 if (existed)
904 *existed = fileAttrib != INVALID_FILE_ATTRIBUTES;
906 if (fileAttrib == INVALID_FILE_ATTRIBUTES)
907 return false;
909 return fileAttrib & FILE_ATTRIBUTE_DIRECTORY;
912 bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
914 QString dirName = name;
915 if (createParentDirectories) {
916 dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
917 // We spefically search for / so \ would break it..
918 int oldslash = -1;
919 if (dirName.startsWith(QLatin1String("\\\\"))) {
920 // Don't try to create the root path of a UNC path;
921 // CreateDirectory() will just return ERROR_INVALID_NAME.
922 for (int i = 0; i < dirName.size(); ++i) {
923 if (dirName.at(i) != QDir::separator()) {
924 oldslash = i;
925 break;
928 if (oldslash != -1)
929 oldslash = dirName.indexOf(QDir::separator(), oldslash);
931 for (int slash=0; slash != -1; oldslash = slash) {
932 slash = dirName.indexOf(QDir::separator(), oldslash+1);
933 if (slash == -1) {
934 if (oldslash == dirName.length())
935 break;
936 slash = dirName.length();
938 if (slash) {
939 QString chunk = dirName.left(slash);
940 bool existed = false;
941 if (!isDirPath(chunk, &existed)) {
942 if (!existed) {
943 if (!mkDir(chunk))
944 return false;
945 } else {
946 return false;
951 return true;
953 return mkDir(name);
956 bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
958 QString dirName = name;
959 if (recurseParentDirectories) {
960 dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
961 for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
962 QString chunk = dirName.left(slash);
963 if (chunk.length() == 2 && chunk.at(0).isLetter() && chunk.at(1) == QLatin1Char(':'))
964 break;
965 if (!isDirPath(chunk, 0))
966 return false;
967 if (!rmDir(chunk))
968 return oldslash != 0;
969 slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
971 return true;
973 return rmDir(name);
976 bool QFSFileEngine::caseSensitive() const
978 return false;
981 bool QFSFileEngine::setCurrentPath(const QString &path)
983 if (!QDir(path).exists())
984 return false;
986 #if !defined(Q_OS_WINCE)
987 return ::SetCurrentDirectory((wchar_t*)path.utf16()) != 0;
988 #else
989 qfsPrivateCurrentDir = QFSFileEnginePrivate::longFileName(path);
990 return true;
991 #endif
994 QString QFSFileEngine::currentPath(const QString &fileName)
996 #if !defined(Q_OS_WINCE)
997 QString ret;
998 //if filename is a drive: then get the pwd of that drive
999 if (fileName.length() >= 2 &&
1000 fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':')) {
1001 int drv = fileName.toUpper().at(0).toLatin1() - 'A' + 1;
1002 if (_getdrive() != drv) {
1003 wchar_t buf[PATH_MAX];
1004 ::_wgetdcwd(drv, buf, PATH_MAX);
1005 ret = QString::fromWCharArray(buf);
1008 if (ret.isEmpty()) {
1009 //just the pwd
1010 DWORD size = 0;
1011 wchar_t currentName[PATH_MAX];
1012 size = ::GetCurrentDirectory(PATH_MAX, currentName);
1013 if (size != 0) {
1014 if (size > PATH_MAX) {
1015 wchar_t *newCurrentName = new wchar_t[size];
1016 if (::GetCurrentDirectory(PATH_MAX, newCurrentName) != 0)
1017 ret = QString::fromWCharArray(newCurrentName);
1018 delete [] newCurrentName;
1019 } else {
1020 ret = QString::fromWCharArray(currentName);
1024 if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
1025 ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
1026 return QDir::fromNativeSeparators(ret);
1027 #else
1028 Q_UNUSED(fileName);
1029 if (qfsPrivateCurrentDir.isEmpty())
1030 qfsPrivateCurrentDir = QCoreApplication::applicationDirPath();
1032 return QDir::fromNativeSeparators(qfsPrivateCurrentDir);
1033 #endif
1036 QString QFSFileEngine::homePath()
1038 QString ret;
1039 #if !defined(QT_NO_LIBRARY)
1040 QFSFileEnginePrivate::resolveLibs();
1041 if (ptrGetUserProfileDirectoryW) {
1042 HANDLE hnd = ::GetCurrentProcess();
1043 HANDLE token = 0;
1044 BOOL ok = ::OpenProcessToken(hnd, TOKEN_QUERY, &token);
1045 if (ok) {
1046 DWORD dwBufferSize = 0;
1047 // First call, to determine size of the strings (with '\0').
1048 ok = ::ptrGetUserProfileDirectoryW(token, NULL, &dwBufferSize);
1049 if (!ok && dwBufferSize != 0) { // We got the required buffer size
1050 wchar_t *userDirectory = new wchar_t[dwBufferSize];
1051 // Second call, now we can fill the allocated buffer.
1052 ok = ::ptrGetUserProfileDirectoryW(token, userDirectory, &dwBufferSize);
1053 if (ok)
1054 ret = QString::fromWCharArray(userDirectory);
1056 delete [] userDirectory;
1058 ::CloseHandle(token);
1061 #endif
1062 if (ret.isEmpty() || !QFile::exists(ret)) {
1063 ret = QString::fromLocal8Bit(qgetenv("USERPROFILE").constData());
1064 if (ret.isEmpty() || !QFile::exists(ret)) {
1065 ret = QString::fromLocal8Bit(qgetenv("HOMEDRIVE").constData()) + QString::fromLocal8Bit(qgetenv("HOMEPATH").constData());
1066 if (ret.isEmpty() || !QFile::exists(ret)) {
1067 ret = QString::fromLocal8Bit(qgetenv("HOME").constData());
1068 if (ret.isEmpty() || !QFile::exists(ret)) {
1069 #if defined(Q_OS_WINCE)
1070 ret = QLatin1String("\\My Documents");
1071 if (!QFile::exists(ret))
1072 #endif
1073 ret = rootPath();
1078 return QDir::fromNativeSeparators(ret);
1081 QString QFSFileEngine::rootPath()
1083 #if defined(Q_OS_WINCE)
1084 QString ret = QLatin1String("/");
1085 #elif defined(Q_FS_FAT)
1086 QString ret = QString::fromLatin1(qgetenv("SystemDrive").constData());
1087 if (ret.isEmpty())
1088 ret = QLatin1String("c:");
1089 ret.append(QLatin1Char('/'));
1090 #elif defined(Q_OS_OS2EMX)
1091 char dir[4];
1092 _abspath(dir, QLatin1String("/"), _MAX_PATH);
1093 QString ret(dir);
1094 #endif
1095 return ret;
1098 QString QFSFileEngine::tempPath()
1100 QString ret;
1102 wchar_t tempPath[MAX_PATH];
1103 if (GetTempPath(MAX_PATH, tempPath))
1104 ret = QString::fromWCharArray(tempPath);
1105 if (!ret.isEmpty()) {
1106 while (ret.endsWith(QLatin1Char('\\')))
1107 ret.chop(1);
1108 ret = QDir::fromNativeSeparators(ret);
1111 if (ret.isEmpty()) {
1112 #if !defined(Q_OS_WINCE)
1113 ret = QLatin1String("c:/tmp");
1114 #else
1115 ret = QLatin1String("/Temp");
1116 #endif
1118 return ret;
1121 QFileInfoList QFSFileEngine::drives()
1123 QFileInfoList ret;
1124 #if !defined(Q_OS_WINCE)
1125 #if defined(Q_OS_WIN32)
1126 quint32 driveBits = (quint32) GetLogicalDrives() & 0x3ffffff;
1127 #elif defined(Q_OS_OS2EMX)
1128 quint32 driveBits, cur;
1129 if (DosQueryCurrentDisk(&cur, &driveBits) != NO_ERROR)
1130 exit(1);
1131 driveBits &= 0x3ffffff;
1132 #endif
1133 char driveName[] = "A:/";
1135 while (driveBits) {
1136 if (driveBits & 1)
1137 ret.append(QFileInfo(QLatin1String(driveName)));
1138 driveName[0]++;
1139 driveBits = driveBits >> 1;
1141 return ret;
1142 #else
1143 ret.append(QFileInfo(QLatin1String("/")));
1144 return ret;
1145 #endif
1148 bool QFSFileEnginePrivate::doStat() const
1150 if (!tried_stat) {
1151 tried_stat = true;
1152 could_stat = false;
1154 if (filePath.isEmpty())
1155 return could_stat;
1157 QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath;
1158 fname = fixIfRelativeUncPath(fname);
1160 UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1162 if (fd != -1) {
1163 #if !defined(Q_OS_WINCE)
1164 HANDLE fh = (HANDLE)_get_osfhandle(fd);
1165 if (fh != INVALID_HANDLE_VALUE) {
1166 BY_HANDLE_FILE_INFORMATION fileInfo;
1167 if (GetFileInformationByHandle(fh, &fileInfo)) {
1168 could_stat = true;
1169 fileAttrib = fileInfo.dwFileAttributes;
1172 #else
1173 DWORD tmpAttributes = GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(fname).utf16());
1174 if (tmpAttributes != -1) {
1175 fileAttrib = tmpAttributes;
1176 could_stat = true;
1178 #endif
1179 } else {
1180 fileAttrib = GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(fname).utf16());
1181 if (fileAttrib == INVALID_FILE_ATTRIBUTES) {
1182 int errorCode = GetLastError();
1183 if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
1184 QString path = QDir::toNativeSeparators(fname);
1185 // path for the FindFirstFile should not end with a trailing slash
1186 while (path.endsWith(QLatin1Char('\\')))
1187 path.chop(1);
1189 // FindFirstFile can not handle drives
1190 if (!path.endsWith(QLatin1Char(':'))) {
1191 WIN32_FIND_DATA findData;
1192 HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(),
1193 &findData);
1194 if (hFind != INVALID_HANDLE_VALUE) {
1195 ::FindClose(hFind);
1196 fileAttrib = findData.dwFileAttributes;
1201 could_stat = fileAttrib != INVALID_FILE_ATTRIBUTES;
1202 if (!could_stat) {
1203 #if !defined(Q_OS_WINCE)
1204 if (isDriveRoot(fname)) {
1205 // a valid drive ??
1206 DWORD drivesBitmask = ::GetLogicalDrives();
1207 int drivebit = 1 << (fname.at(0).toUpper().unicode() - QLatin1Char('A').unicode());
1208 if (drivesBitmask & drivebit) {
1209 fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM;
1210 could_stat = true;
1212 } else {
1213 #endif
1214 QString path = QDir::toNativeSeparators(fname);
1215 bool is_dir = false;
1216 if (path.startsWith(QLatin1String("\\\\"))) {
1217 // UNC - stat doesn't work for all cases (Windows bug)
1218 int s = path.indexOf(path.at(0),2);
1219 if (s > 0) {
1220 // "\\server\..."
1221 s = path.indexOf(path.at(0),s+1);
1222 if (s > 0) {
1223 // "\\server\share\..."
1224 if (s == path.size() - 1) {
1225 // "\\server\share\"
1226 is_dir = true;
1227 } else {
1228 // "\\server\share\notfound"
1230 } else {
1231 // "\\server\share"
1232 is_dir = true;
1234 } else {
1235 // "\\server"
1236 is_dir = true;
1239 if (is_dir && uncShareExists(path)) {
1240 // looks like a UNC dir, is a dir.
1241 fileAttrib = FILE_ATTRIBUTE_DIRECTORY;
1242 could_stat = true;
1244 #if !defined(Q_OS_WINCE)
1246 #endif
1250 SetErrorMode(oldmode);
1252 return could_stat;
1256 static QString readSymLink(const QString &link)
1258 QString result;
1259 #if !defined(Q_OS_WINCE)
1260 HANDLE handle = CreateFile((wchar_t*)QFSFileEnginePrivate::longFileName(link).utf16(),
1261 FILE_READ_EA,
1262 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1264 OPEN_EXISTING,
1265 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
1267 if (handle != INVALID_HANDLE_VALUE) {
1268 DWORD bufsize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
1269 REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)qMalloc(bufsize);
1270 DWORD retsize = 0;
1271 if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, 0, 0, rdb, bufsize, &retsize, 0)) {
1272 if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
1273 int length = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
1274 int offset = rdb->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
1275 const wchar_t* PathBuffer = &rdb->MountPointReparseBuffer.PathBuffer[offset];
1276 result = QString::fromWCharArray(PathBuffer, length);
1277 } else {
1278 int length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
1279 int offset = rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
1280 const wchar_t* PathBuffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[offset];
1281 result = QString::fromWCharArray(PathBuffer, length);
1283 // cut-off "//?/" and "/??/"
1284 if (result.size() > 4 && result.at(0) == QLatin1Char('\\') && result.at(2) == QLatin1Char('?') && result.at(3) == QLatin1Char('\\'))
1285 result = result.mid(4);
1287 qFree(rdb);
1288 CloseHandle(handle);
1290 #else
1291 Q_UNUSED(link);
1292 #endif // Q_OS_WINCE
1293 return result;
1296 static QString readLink(const QString &link)
1298 #if !defined(Q_OS_WINCE)
1299 #if !defined(QT_NO_LIBRARY) && !defined(Q_CC_MWERKS)
1300 QString ret;
1302 bool neededCoInit = false;
1303 IShellLink *psl; // pointer to IShellLink i/f
1304 WIN32_FIND_DATA wfd;
1305 wchar_t szGotPath[MAX_PATH];
1307 // Get pointer to the IShellLink interface.
1308 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl);
1310 if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
1311 neededCoInit = true;
1312 CoInitialize(NULL);
1313 hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1314 IID_IShellLink, (LPVOID *)&psl);
1316 if (SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface.
1317 IPersistFile *ppf;
1318 hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
1319 if (SUCCEEDED(hres)) {
1320 hres = ppf->Load((LPOLESTR)link.utf16(), STGM_READ);
1321 //The original path of the link is retrieved. If the file/folder
1322 //was moved, the return value still have the old path.
1323 if (SUCCEEDED(hres)) {
1324 if (psl->GetPath(szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR)
1325 ret = QString::fromWCharArray(szGotPath);
1327 ppf->Release();
1329 psl->Release();
1331 if (neededCoInit)
1332 CoUninitialize();
1334 return ret;
1335 #else
1336 Q_UNUSED(link);
1337 return QString();
1338 #endif // QT_NO_LIBRARY
1339 #else
1340 wchar_t target[MAX_PATH];
1341 QString result;
1342 if (SHGetShortcutTarget((wchar_t*)QFileInfo(link).absoluteFilePath().replace(QLatin1Char('/'),QLatin1Char('\\')).utf16(), target, MAX_PATH)) {
1343 result = QString::fromWCharArray(target);
1344 if (result.startsWith(QLatin1Char('"')))
1345 result.remove(0,1);
1346 if (result.endsWith(QLatin1Char('"')))
1347 result.remove(result.size()-1,1);
1349 return result;
1350 #endif // Q_OS_WINCE
1353 bool QFSFileEngine::link(const QString &newName)
1355 #if !defined(Q_OS_WINCE)
1356 #if !defined(QT_NO_LIBRARY) && !defined(Q_CC_MWERKS)
1357 bool ret = false;
1359 QString linkName = newName;
1360 //### assume that they add .lnk
1362 IShellLink *psl;
1363 bool neededCoInit = false;
1365 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
1367 if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
1368 neededCoInit = true;
1369 CoInitialize(NULL);
1370 hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
1373 if (SUCCEEDED(hres)) {
1374 hres = psl->SetPath((wchar_t *)fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\')).utf16());
1375 if (SUCCEEDED(hres)) {
1376 hres = psl->SetWorkingDirectory((wchar_t *)fileName(AbsolutePathName).replace(QLatin1Char('/'), QLatin1Char('\\')).utf16());
1377 if (SUCCEEDED(hres)) {
1378 IPersistFile *ppf;
1379 hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
1380 if (SUCCEEDED(hres)) {
1381 hres = ppf->Save((wchar_t*)linkName.utf16(), TRUE);
1382 if (SUCCEEDED(hres))
1383 ret = true;
1384 ppf->Release();
1388 psl->Release();
1390 if (!ret)
1391 setError(QFile::RenameError, qt_error_string());
1393 if (neededCoInit)
1394 CoUninitialize();
1396 return ret;
1397 #else
1398 Q_UNUSED(newName);
1399 return false;
1400 #endif // QT_NO_LIBRARY
1401 #else
1402 QString linkName = newName;
1403 if (!linkName.endsWith(QLatin1String(".lnk")))
1404 linkName += QLatin1String(".lnk");
1405 QString orgName = fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\'));
1406 // Need to append on our own
1407 orgName.prepend(QLatin1Char('"'));
1408 orgName.append(QLatin1Char('"'));
1409 bool ret = SUCCEEDED(SHCreateShortcut((wchar_t*)linkName.utf16(), (wchar_t*)orgName.utf16()));
1410 if (!ret)
1411 setError(QFile::RenameError, qt_error_string());
1412 return ret;
1413 #endif // Q_OS_WINCE
1417 \internal
1419 QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions() const
1421 QAbstractFileEngine::FileFlags ret = 0;
1423 #if !defined(QT_NO_LIBRARY)
1424 if((qt_ntfs_permission_lookup > 0) && ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_NT)) {
1425 PSID pOwner = 0;
1426 PSID pGroup = 0;
1427 PACL pDacl;
1428 PSECURITY_DESCRIPTOR pSD;
1429 ACCESS_MASK access_mask;
1431 enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 };
1432 resolveLibs();
1433 if(ptrGetNamedSecurityInfoW && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW) {
1435 QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath;
1436 DWORD res = ptrGetNamedSecurityInfoW((wchar_t*)fname.utf16(), SE_FILE_OBJECT,
1437 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
1438 &pOwner, &pGroup, &pDacl, 0, &pSD);
1440 if(res == ERROR_SUCCESS) {
1441 TRUSTEE_W trustee;
1442 { //user
1443 if(ptrGetEffectiveRightsFromAclW(pDacl, &currentUserTrusteeW, &access_mask) != ERROR_SUCCESS)
1444 access_mask = (ACCESS_MASK)-1;
1445 if(access_mask & ReadMask)
1446 ret |= QAbstractFileEngine::ReadUserPerm;
1447 if(access_mask & WriteMask)
1448 ret |= QAbstractFileEngine::WriteUserPerm;
1449 if(access_mask & ExecMask)
1450 ret |= QAbstractFileEngine::ExeUserPerm;
1452 { //owner
1453 ptrBuildTrusteeWithSidW(&trustee, pOwner);
1454 if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
1455 access_mask = (ACCESS_MASK)-1;
1456 if(access_mask & ReadMask)
1457 ret |= QAbstractFileEngine::ReadOwnerPerm;
1458 if(access_mask & WriteMask)
1459 ret |= QAbstractFileEngine::WriteOwnerPerm;
1460 if(access_mask & ExecMask)
1461 ret |= QAbstractFileEngine::ExeOwnerPerm;
1463 { //group
1464 ptrBuildTrusteeWithSidW(&trustee, pGroup);
1465 if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
1466 access_mask = (ACCESS_MASK)-1;
1467 if(access_mask & ReadMask)
1468 ret |= QAbstractFileEngine::ReadGroupPerm;
1469 if(access_mask & WriteMask)
1470 ret |= QAbstractFileEngine::WriteGroupPerm;
1471 if(access_mask & ExecMask)
1472 ret |= QAbstractFileEngine::ExeGroupPerm;
1474 { //other (world)
1475 if(ptrGetEffectiveRightsFromAclW(pDacl, &worldTrusteeW, &access_mask) != ERROR_SUCCESS)
1476 access_mask = (ACCESS_MASK)-1; // ###
1477 if(access_mask & ReadMask)
1478 ret |= QAbstractFileEngine::ReadOtherPerm;
1479 if(access_mask & WriteMask)
1480 ret |= QAbstractFileEngine::WriteOtherPerm;
1481 if(access_mask & ExecMask)
1482 ret |= QAbstractFileEngine::ExeOtherPerm;
1484 LocalFree(pSD);
1487 } else
1488 #endif
1490 //### what to do with permissions if we don't use NTFS
1491 // for now just add all permissions and what about exe missions ??
1492 // also qt_ntfs_permission_lookup is now not set by defualt ... should it ?
1493 ret |= QAbstractFileEngine::ReadOtherPerm | QAbstractFileEngine::ReadGroupPerm
1494 | QAbstractFileEngine::ReadOwnerPerm | QAbstractFileEngine::ReadUserPerm
1495 | QAbstractFileEngine::WriteUserPerm | QAbstractFileEngine::WriteOwnerPerm
1496 | QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm;
1498 if (doStat()) {
1499 if (ret & (QAbstractFileEngine::WriteOwnerPerm | QAbstractFileEngine::WriteUserPerm |
1500 QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm)) {
1501 if (fileAttrib & FILE_ATTRIBUTE_READONLY)
1502 ret &= ~(QAbstractFileEngine::WriteOwnerPerm | QAbstractFileEngine::WriteUserPerm |
1503 QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm);
1506 QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath;
1507 QString ext = fname.right(4).toLower();
1508 if (ext == QLatin1String(".exe") || ext == QLatin1String(".com") || ext == QLatin1String(".bat") ||
1509 ext == QLatin1String(".pif") || ext == QLatin1String(".cmd") || (fileAttrib & FILE_ATTRIBUTE_DIRECTORY))
1510 ret |= QAbstractFileEngine::ExeOwnerPerm | QAbstractFileEngine::ExeGroupPerm |
1511 QAbstractFileEngine::ExeOtherPerm | QAbstractFileEngine::ExeUserPerm;
1514 return ret;
1518 \internal
1520 bool QFSFileEnginePrivate::isSymlink() const
1522 #if !defined(Q_OS_WINCE)
1523 if (need_lstat) {
1524 need_lstat = false;
1525 is_link = false;
1527 if (fileAttrib & FILE_ATTRIBUTE_REPARSE_POINT) {
1528 QString path = QDir::toNativeSeparators(filePath);
1529 // path for the FindFirstFile should not end with a trailing slash
1530 while (path.endsWith(QLatin1Char('\\')))
1531 path.chop(1);
1533 WIN32_FIND_DATA findData;
1534 HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(),
1535 &findData);
1536 if (hFind != INVALID_HANDLE_VALUE) {
1537 ::FindClose(hFind);
1538 if ((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
1539 && (findData.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT
1540 || findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
1541 is_link = true;
1546 return is_link;
1547 #else
1548 return false;
1549 #endif // Q_OS_WINCE
1553 \reimp
1555 QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
1557 Q_D(const QFSFileEngine);
1558 QAbstractFileEngine::FileFlags ret = 0;
1559 // Force a stat, so that we're guaranteed to get up-to-date results
1560 if (type & Refresh) {
1561 d->tried_stat = 0;
1562 #if !defined(Q_OS_WINCE)
1563 d->need_lstat = 1;
1564 #endif
1567 if (type & PermsMask) {
1568 ret |= d->getPermissions();
1569 // ### Workaround pascals ### above. Since we always set all properties to true
1570 // we need to disable read and exec access if the file does not exists
1571 if (d->doStat())
1572 ret |= ExistsFlag;
1573 else
1574 ret &= 0x2222;
1576 if (type & TypesMask) {
1577 if (d->filePath.endsWith(QLatin1String(".lnk"))) {
1578 ret |= LinkType;
1579 QString l = readLink(d->filePath);
1580 if (!l.isEmpty()) {
1581 bool existed = false;
1582 if (isDirPath(l, &existed) && existed)
1583 ret |= DirectoryType;
1584 else if (existed)
1585 ret |= FileType;
1587 } else if (d->doStat()) {
1588 if ((type & LinkType) && d->isSymlink())
1589 ret |= LinkType;
1590 if (d->fileAttrib & FILE_ATTRIBUTE_DIRECTORY) {
1591 ret |= DirectoryType;
1592 } else {
1593 ret |= FileType;
1597 if (type & FlagsMask) {
1598 ret |= LocalDiskFlag;
1599 if (d->doStat()) {
1600 ret |= ExistsFlag;
1601 if (d->filePath == QLatin1String("/") || isDriveRoot(d->filePath) || isUncRoot(d->filePath)) {
1602 ret |= RootFlag;
1603 } else if (d->fileAttrib & FILE_ATTRIBUTE_HIDDEN) {
1604 QString baseName = fileName(BaseName);
1605 if (baseName != QLatin1String(".") && baseName != QLatin1String(".."))
1606 ret |= HiddenFlag;
1610 return ret;
1613 QString QFSFileEngine::fileName(FileName file) const
1615 Q_D(const QFSFileEngine);
1616 if (file == BaseName) {
1617 int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
1618 if (slash == -1) {
1619 int colon = d->filePath.lastIndexOf(QLatin1Char(':'));
1620 if (colon != -1)
1621 return d->filePath.mid(colon + 1);
1622 return d->filePath;
1624 return d->filePath.mid(slash + 1);
1625 } else if (file == PathName) {
1626 if (!d->filePath.size())
1627 return d->filePath;
1629 int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
1630 if (slash == -1) {
1631 if (d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':'))
1632 return d->filePath.left(2);
1633 return QString(QLatin1Char('.'));
1634 } else {
1635 if (!slash)
1636 return QString(QLatin1Char('/'));
1637 if (slash == 2 && d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':'))
1638 slash++;
1639 return d->filePath.left(slash);
1641 } else if (file == AbsoluteName || file == AbsolutePathName) {
1642 QString ret;
1644 if (!isRelativePath()) {
1645 #if !defined(Q_OS_WINCE)
1646 if ((d->filePath.size() > 2 && d->filePath.at(1) == QLatin1Char(':')
1647 && d->filePath.at(2) != QLatin1Char('/')) || // It's a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt
1648 d->filePath.startsWith(QLatin1Char('/')) // It's a absolute path to the current drive, so \a.txt -> Z:\a.txt
1650 ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(d->filePath));
1651 } else {
1652 ret = d->filePath;
1654 #else
1655 ret = d->filePath;
1656 #endif
1657 } else {
1658 ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + d->filePath);
1661 // The path should be absolute at this point.
1662 // From the docs :
1663 // Absolute paths begin with the directory separator "/"
1664 // (optionally preceded by a drive specification under Windows).
1665 if (ret.at(0) != QLatin1Char('/')) {
1666 Q_ASSERT(ret.length() >= 2);
1667 Q_ASSERT(ret.at(0).isLetter());
1668 Q_ASSERT(ret.at(1) == QLatin1Char(':'));
1670 // Force uppercase drive letters.
1671 ret[0] = ret.at(0).toUpper();
1674 if (file == AbsolutePathName) {
1675 int slash = ret.lastIndexOf(QLatin1Char('/'));
1676 if (slash < 0)
1677 return ret;
1678 else if (ret.at(0) != QLatin1Char('/') && slash == 2)
1679 return ret.left(3); // include the slash
1680 else
1681 return ret.left(slash > 0 ? slash : 1);
1683 return ret;
1684 } else if (file == CanonicalName || file == CanonicalPathName) {
1685 if (!(fileFlags(ExistsFlag) & ExistsFlag))
1686 return QString();
1688 QString ret = QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName));
1689 if (!ret.isEmpty() && file == CanonicalPathName) {
1690 int slash = ret.lastIndexOf(QLatin1Char('/'));
1691 if (slash == -1)
1692 ret = QDir::currentPath();
1693 else if (slash == 0)
1694 ret = QString(QLatin1Char('/'));
1695 ret = ret.left(slash);
1697 return ret;
1698 } else if (file == LinkName) {
1699 QString ret;
1700 if (d->filePath.endsWith(QLatin1String(".lnk")))
1701 ret = readLink(d->filePath);
1702 else if (d->doStat() && d->isSymlink())
1703 ret = readSymLink(d->filePath);
1704 return QDir::fromNativeSeparators(ret);
1705 } else if (file == BundleName) {
1706 return QString();
1708 return d->filePath;
1711 bool QFSFileEngine::isRelativePath() const
1713 Q_D(const QFSFileEngine);
1714 // drive, e.g. "a:", or UNC root, e.q. "//"
1715 return !(d->filePath.startsWith(QLatin1Char('/'))
1716 || (d->filePath.length() >= 2
1717 && ((d->filePath.at(0).isLetter() && d->filePath.at(1) == QLatin1Char(':'))
1718 || (d->filePath.at(0) == QLatin1Char('/') && d->filePath.at(1) == QLatin1Char('/')))));
1721 uint QFSFileEngine::ownerId(FileOwner /*own*/) const
1723 static const uint nobodyID = (uint) -2;
1724 return nobodyID;
1727 QString QFSFileEngine::owner(FileOwner own) const
1729 #if !defined(QT_NO_LIBRARY)
1730 Q_D(const QFSFileEngine);
1731 if((qt_ntfs_permission_lookup > 0) && ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_NT)) {
1732 PSID pOwner = 0;
1733 PSECURITY_DESCRIPTOR pSD;
1734 QString name;
1735 QFSFileEnginePrivate::resolveLibs();
1737 if(ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) {
1738 if(ptrGetNamedSecurityInfoW((wchar_t*)d->filePath.utf16(), SE_FILE_OBJECT,
1739 own == OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION,
1740 NULL, &pOwner, NULL, NULL, &pSD) == ERROR_SUCCESS) {
1741 DWORD lowner = 0, ldomain = 0;
1742 SID_NAME_USE use;
1743 // First call, to determine size of the strings (with '\0').
1744 ptrLookupAccountSidW(NULL, pOwner, NULL, &lowner, NULL, &ldomain, (SID_NAME_USE*)&use);
1745 wchar_t *owner = new wchar_t[lowner];
1746 wchar_t *domain = new wchar_t[ldomain];
1747 // Second call, size is without '\0'
1748 if(ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner, &lowner,
1749 (LPWSTR)domain, &ldomain, (SID_NAME_USE*)&use)) {
1750 name = QString::fromUtf16((ushort*)owner);
1752 LocalFree(pSD);
1753 delete [] owner;
1754 delete [] domain;
1757 return name;
1759 #else
1760 Q_UNUSED(own);
1761 #endif
1762 return QString();
1765 bool QFSFileEngine::setPermissions(uint perms)
1767 Q_D(QFSFileEngine);
1768 bool ret = false;
1769 int mode = 0;
1771 if (perms & QFile::ReadOwner || perms & QFile::ReadUser || perms & QFile::ReadGroup || perms & QFile::ReadOther)
1772 mode |= _S_IREAD;
1773 if (perms & QFile::WriteOwner || perms & QFile::WriteUser || perms & QFile::WriteGroup || perms & QFile::WriteOther)
1774 mode |= _S_IWRITE;
1776 if (mode == 0) // not supported
1777 return false;
1779 ret = ::_wchmod((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(), mode) == 0;
1780 if (!ret)
1781 setError(QFile::PermissionsError, qt_error_string(errno));
1782 return ret;
1785 bool QFSFileEngine::setSize(qint64 size)
1787 Q_D(QFSFileEngine);
1789 if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1) {
1790 // resize open file
1791 HANDLE fh = d->fileHandle;
1792 #if !defined(Q_OS_WINCE)
1793 if (fh == INVALID_HANDLE_VALUE)
1794 fh = (HANDLE)_get_osfhandle(d->fd);
1795 #endif
1796 if (fh == INVALID_HANDLE_VALUE)
1797 return false;
1798 qint64 currentPos = pos();
1800 if (seek(size) && SetEndOfFile(fh)) {
1801 seek(qMin(currentPos, size));
1802 return true;
1805 seek(currentPos);
1806 return false;
1809 if (!d->nativeFilePath.isEmpty()) {
1810 // resize file on disk
1811 QFile file(d->filePath);
1812 if (file.open(QFile::ReadWrite)) {
1813 bool ret = file.resize(size);
1814 if (!ret)
1815 setError(QFile::ResizeError, file.errorString());
1816 return ret;
1819 return false;
1823 static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
1825 QDateTime ret;
1827 #if defined(Q_OS_WINCE)
1828 SYSTEMTIME systime;
1829 FILETIME ftime;
1830 systime.wYear = 1970;
1831 systime.wMonth = 1;
1832 systime.wDay = 1;
1833 systime.wHour = 0;
1834 systime.wMinute = 0;
1835 systime.wSecond = 0;
1836 systime.wMilliseconds = 0;
1837 systime.wDayOfWeek = 4;
1838 SystemTimeToFileTime(&systime, &ftime);
1839 unsigned __int64 acttime = (unsigned __int64)time->dwHighDateTime << 32 | time->dwLowDateTime;
1840 FileTimeToSystemTime(time, &systime);
1841 unsigned __int64 time1970 = (unsigned __int64)ftime.dwHighDateTime << 32 | ftime.dwLowDateTime;
1842 unsigned __int64 difftime = acttime - time1970;
1843 difftime /= 10000000;
1844 ret.setTime_t((unsigned int)difftime);
1845 #else
1846 SYSTEMTIME sTime, lTime;
1847 FileTimeToSystemTime(time, &sTime);
1848 SystemTimeToTzSpecificLocalTime(0, &sTime, &lTime);
1849 ret.setDate(QDate(lTime.wYear, lTime.wMonth, lTime.wDay));
1850 ret.setTime(QTime(lTime.wHour, lTime.wMinute, lTime.wSecond, lTime.wMilliseconds));
1851 #endif
1853 return ret;
1856 QDateTime QFSFileEngine::fileTime(FileTime time) const
1858 Q_D(const QFSFileEngine);
1859 QDateTime ret;
1860 if (d->fd != -1) {
1861 #if !defined(Q_OS_WINCE)
1862 HANDLE fh = (HANDLE)_get_osfhandle(d->fd);
1863 if (fh != INVALID_HANDLE_VALUE) {
1864 FILETIME creationTime, lastAccessTime, lastWriteTime;
1865 if (GetFileTime(fh, &creationTime, &lastAccessTime, &lastWriteTime)) {
1866 if(time == CreationTime)
1867 ret = fileTimeToQDateTime(&creationTime);
1868 else if(time == ModificationTime)
1869 ret = fileTimeToQDateTime(&lastWriteTime);
1870 else if(time == AccessTime)
1871 ret = fileTimeToQDateTime(&lastAccessTime);
1874 #endif
1875 } else {
1876 WIN32_FILE_ATTRIBUTE_DATA attribData;
1877 bool ok = ::GetFileAttributesEx((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(), GetFileExInfoStandard, &attribData);
1878 if (!ok) {
1879 int errorCode = GetLastError();
1880 if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
1881 QString path = QDir::toNativeSeparators(d->filePath);
1882 // path for the FindFirstFile should not end with a trailing slash
1883 while (path.endsWith(QLatin1Char('\\')))
1884 path.chop(1);
1886 // FindFirstFile can not handle drives
1887 if (!path.endsWith(QLatin1Char(':'))) {
1888 WIN32_FIND_DATA findData;
1889 HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(),
1890 &findData);
1891 if (hFind != INVALID_HANDLE_VALUE) {
1892 ::FindClose(hFind);
1893 ok = true;
1894 attribData.ftCreationTime = findData.ftCreationTime;
1895 attribData.ftLastWriteTime = findData.ftLastWriteTime;
1896 attribData.ftLastAccessTime = findData.ftLastAccessTime;
1901 if (ok) {
1902 if(time == CreationTime)
1903 ret = fileTimeToQDateTime(&attribData.ftCreationTime);
1904 else if(time == ModificationTime)
1905 ret = fileTimeToQDateTime(&attribData.ftLastWriteTime);
1906 else if(time == AccessTime)
1907 ret = fileTimeToQDateTime(&attribData.ftLastAccessTime);
1910 return ret;
1913 uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
1914 QFile::MemoryMapFlags flags)
1916 Q_Q(QFSFileEngine);
1917 Q_UNUSED(flags);
1918 if (openMode == QFile::NotOpen) {
1919 q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
1920 return 0;
1922 if (offset == 0 && size == 0) {
1923 q->setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
1924 return 0;
1927 if (mapHandle == INVALID_HANDLE_VALUE) {
1928 // get handle to the file
1929 HANDLE handle = fileHandle;
1931 #ifndef Q_OS_WINCE
1932 if (handle == INVALID_HANDLE_VALUE && fh)
1933 handle = (HANDLE)::_get_osfhandle(QT_FILENO(fh));
1934 #endif
1936 #ifdef Q_USE_DEPRECATED_MAP_API
1937 nativeClose();
1938 // handle automatically closed by kernel with mapHandle (below).
1939 handle = ::CreateFileForMapping((const wchar_t*)nativeFilePath.constData(),
1940 GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0),
1942 NULL,
1943 OPEN_EXISTING,
1944 FILE_ATTRIBUTE_NORMAL,
1945 NULL);
1946 #endif
1948 if (handle == INVALID_HANDLE_VALUE) {
1949 q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
1950 return 0;
1953 // first create the file mapping handle
1954 DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY;
1955 mapHandle = ::CreateFileMapping(handle, 0, protection, 0, 0, 0);
1956 if (mapHandle == INVALID_HANDLE_VALUE) {
1957 q->setError(QFile::PermissionsError, qt_error_string());
1958 #ifdef Q_USE_DEPRECATED_MAP_API
1959 ::CloseHandle(handle);
1960 #endif
1961 return 0;
1965 // setup args to map
1966 DWORD access = 0;
1967 if (openMode & QIODevice::ReadOnly) access = FILE_MAP_READ;
1968 if (openMode & QIODevice::WriteOnly) access = FILE_MAP_WRITE;
1970 DWORD offsetHi = offset >> 32;
1971 DWORD offsetLo = offset & Q_UINT64_C(0xffffffff);
1972 SYSTEM_INFO sysinfo;
1973 ::GetSystemInfo(&sysinfo);
1974 DWORD mask = sysinfo.dwAllocationGranularity - 1;
1975 DWORD extra = offset & mask;
1976 if (extra)
1977 offsetLo &= ~mask;
1979 // attempt to create the map
1980 LPVOID mapAddress = ::MapViewOfFile(mapHandle, access,
1981 offsetHi, offsetLo, size + extra);
1982 if (mapAddress) {
1983 uchar *address = extra + static_cast<uchar*>(mapAddress);
1984 maps[address] = extra;
1985 return address;
1988 switch(GetLastError()) {
1989 case ERROR_ACCESS_DENIED:
1990 q->setError(QFile::PermissionsError, qt_error_string());
1991 break;
1992 case ERROR_INVALID_PARAMETER:
1993 // size are out of bounds
1994 default:
1995 q->setError(QFile::UnspecifiedError, qt_error_string());
1998 ::CloseHandle(mapHandle);
1999 return 0;
2002 bool QFSFileEnginePrivate::unmap(uchar *ptr)
2004 Q_Q(QFSFileEngine);
2005 if (!maps.contains(ptr)) {
2006 q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
2007 return false;
2009 uchar *start = ptr - maps[ptr];
2010 if (!UnmapViewOfFile(start)) {
2011 q->setError(QFile::PermissionsError, qt_error_string());
2012 return false;
2015 maps.remove(ptr);
2016 if (maps.isEmpty()) {
2017 ::CloseHandle(mapHandle);
2018 mapHandle = INVALID_HANDLE_VALUE;
2021 return true;
2024 QT_END_NAMESPACE