1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtCore module of the Qt Toolkit.
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
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.
40 ****************************************************************************/
43 #include "qplatformdefs.h"
44 #include "qabstractfileengine.h"
45 #include "private/qfsfileengine_p.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>
58 # include <winioctl.h>
68 #define SECURITY_WIN32
70 // A workaround for a certain version of MinGW, the define UNICODE_STRING.
75 #ifndef _INTPTR_T_DEFINED
77 typedef __int64
intptr_t;
80 typedef _W64
int intptr_t;
82 typedef INT_PTR
intptr_t;
85 #define _INTPTR_T_DEFINED
88 #ifndef INVALID_FILE_ATTRIBUTES
89 # define INVALID_FILE_ATTRIBUTES (DWORD (-1))
92 #if !defined(Q_OS_WINCE)
93 # if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
94 typedef struct _REPARSE_DATA_BUFFER
{
96 USHORT ReparseDataLength
;
100 USHORT SubstituteNameOffset
;
101 USHORT SubstituteNameLength
;
102 USHORT PrintNameOffset
;
103 USHORT PrintNameLength
;
106 } SymbolicLinkReparseBuffer
;
108 USHORT SubstituteNameOffset
;
109 USHORT SubstituteNameLength
;
110 USHORT PrintNameOffset
;
111 USHORT PrintNameLength
;
113 } MountPointReparseBuffer
;
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
125 # ifndef IO_REPARSE_TAG_SYMLINK
126 # define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
128 # ifndef FSCTL_GET_REPARSE_POINT
129 # define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
131 #endif // !defined(Q_OS_WINCE)
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
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;
168 // need to resolve the security info functions
170 // protect initialization
172 QMutexLocker
locker(QMutexPool::globalInstanceGet(&triedResolve
));
173 // check triedResolve again, since another thread may have already
174 // done the initialization
176 // another thread did initialize the security function pointers,
177 // so we shouldn't do it again.
183 #if !defined(Q_OS_WINCE)
184 HINSTANCE advapiHnd
= LoadLibraryW(L
"advapi32");
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();
195 if (::OpenProcessToken(hnd
, TOKEN_QUERY
, &token
)) {
198 if (::GetTokenInformation(token
, TokenUser
, &tu
, sizeof(tu
), &retsize
))
199 ptrBuildTrusteeWithSidW(¤tUserTrusteeW
, 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
};
211 if (ptrAllocateAndInitializeSid(&worldAuth
, 1, SECURITY_WORLD_RID
, 0, 0, 0, 0, 0, 0, 0, &pWorld
))
212 ptrBuildTrusteeWithSidW(&worldTrusteeW
, pWorld
);
216 HINSTANCE userenvHnd
= LoadLibraryW(L
"userenv");
218 ptrGetUserProfileDirectoryW
= (PtrGetUserProfileDirectoryW
)GetProcAddress(userenvHnd
, "GetUserProfileDirectoryW");
222 #endif // QT_NO_LIBRARY
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
{
236 bool QFSFileEnginePrivate::resolveUNCLibs()
238 static bool triedResolve
= false;
241 QMutexLocker
locker(QMutexPool::globalInstanceGet(&triedResolve
));
243 return ptrNetShareEnum
&& ptrNetApiBufferFree
;
247 #if !defined(Q_OS_WINCE)
248 HINSTANCE hLib
= LoadLibraryW(L
"Netapi32");
250 ptrNetShareEnum
= (PtrNetShareEnum
)GetProcAddress(hLib
, "NetShareEnum");
252 ptrNetApiBufferFree
= (PtrNetApiBufferFree
)GetProcAddress(hLib
, "NetApiBufferFree");
256 return ptrNetShareEnum
&& ptrNetApiBufferFree
;
259 bool QFSFileEnginePrivate::uncListSharesOnServer(const QString
&server
, QStringList
*list
)
261 if (resolveUNCLibs()) {
262 SHARE_INFO_1
*BufPtr
, *p
;
264 DWORD er
= 0, tr
= 0, resume
= 0, i
;
266 res
= ptrNetShareEnum((wchar_t*)server
.utf16(), 1, (LPBYTE
*)&BufPtr
, DWORD(-1), &er
, &tr
, &resume
);
267 if (res
== ERROR_SUCCESS
|| res
== ERROR_MORE_DATA
) {
269 for (i
= 1; i
<= er
; ++i
) {
270 if (list
&& p
->shi1_type
== 0)
271 list
->append(QString::fromWCharArray(p
->shi1_netname
));
275 ptrNetApiBufferFree(BufPtr
);
276 } while (res
== ERROR_MORE_DATA
);
277 return res
== ERROR_SUCCESS
;
282 static bool isUncRoot(const QString
&server
)
284 QString localPath
= QDir::toNativeSeparators(server
);
285 if (!localPath
.startsWith(QLatin1String("\\\\")))
288 int idx
= localPath
.indexOf(QLatin1Char('\\'), 2);
289 if (idx
== -1 || idx
+ 1 == localPath
.length())
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('.'));
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
);
324 // can be //server or //server/share
325 static bool uncShareExists(const QString
&server
)
327 QStringList parts
= server
.split(QLatin1Char('\\'), QString::SkipEmptyParts
);
330 if (QFSFileEnginePrivate::uncListSharesOnServer(QLatin1String("\\\\") + parts
.at(0), &shares
))
331 return parts
.count() >= 2 ? shares
.contains(parts
.at(1), Qt::CaseInsensitive
) : true;
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
)
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()) {
352 retLen
= GetFullPathName((wchar_t*)path
.utf16(), buf
.size(), buf
.data(), &fileName
);
355 absPath
= QString::fromWCharArray(buf
.data(), retLen
);
357 if (path
.startsWith(QLatin1Char('/')) || path
.startsWith(QLatin1Char('\\')))
358 absPath
= QDir::toNativeSeparators(path
);
360 absPath
= QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir
+ QLatin1Char('/') + path
));
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(' '));
375 QString
QFSFileEnginePrivate::longFileName(const QString
&path
)
377 if (path
.startsWith(QLatin1String("\\\\.\\")))
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
;
396 void QFSFileEnginePrivate::nativeInitFileName()
398 QString path
= longFileName(QDir::toNativeSeparators(fixIfRelativeUncPath(filePath
)));
399 nativeFilePath
= QByteArray((const char *)path
.utf16(), path
.size() * 2 + 1);
405 bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode
)
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(),
429 FILE_ATTRIBUTE_NORMAL
,
432 // Bail out on error.
433 if (fileHandle
== INVALID_HANDLE_VALUE
) {
434 q
->setError(QFile::OpenError
, qt_error_string());
438 // Truncate the file after successfully opening it if Truncate is passed.
439 if (openMode
& QIODevice::Truncate
)
448 bool QFSFileEnginePrivate::nativeClose()
451 if (fh
|| fd
!= -1) {
452 // stdlib / stdio mode.
456 // Windows native mode.
458 if ((fileHandle
== INVALID_HANDLE_VALUE
|| !::CloseHandle(fileHandle
))) {
459 q
->setError(QFile::UnspecifiedError
, qt_error_string());
462 fileHandle
= INVALID_HANDLE_VALUE
;
463 cachedFd
= -1; // gets closed by CloseHandle above
471 bool QFSFileEnginePrivate::nativeFlush()
474 // Buffered stdlib mode.
478 // Unbuffered stdio mode; always succeeds (no buffer).
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.
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.
500 // Buffered stdlib mode.
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
);
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('\\'))
523 // FindFirstFile can not handle drives
524 if (!path
.endsWith(':')) {
525 WIN32_FIND_DATA findData
;
526 HANDLE hFind
= ::FindFirstFile((const wchar_t*)path
.constData(),
528 if (hFind
!= INVALID_HANDLE_VALUE
) {
531 attribData
.nFileSizeHigh
= findData
.nFileSizeHigh
;
532 attribData
.nFileSizeLow
= findData
.nFileSizeLow
;
538 qint64 size
= attribData
.nFileSizeHigh
;
540 size
+= attribData
.nFileSizeLow
;
543 thatQ
->setError(QFile::UnspecifiedError
, qt_error_string());
547 // Unbuffed stdio mode.
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
;
556 size
+= fileInfo
.nFileSizeLow
;
561 thatQ
->setError(QFile::UnspecifiedError
, qt_error_string());
565 // Windows native mode.
566 if (fileHandle
== INVALID_HANDLE_VALUE
)
569 BY_HANDLE_FILE_INFORMATION fileInfo
;
570 if (!GetFileInformationByHandle(fileHandle
, &fileInfo
)) {
571 thatQ
->setError(QFile::UnspecifiedError
, qt_error_string());
575 qint64 size
= fileInfo
.nFileSizeHigh
;
577 size
+= fileInfo
.nFileSizeLow
;
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.
594 // Windows native mode.
595 if (fileHandle
== INVALID_HANDLE_VALUE
)
598 #if !defined(Q_OS_WINCE)
599 LARGE_INTEGER currentFilePos
;
600 LARGE_INTEGER offset
;
602 if (!::SetFilePointerEx(fileHandle
, offset
, ¤tFilePos
, FILE_CURRENT
)) {
603 thatQ
->setError(QFile::UnspecifiedError
, qt_error_string());
607 return qint64(currentFilePos
.QuadPart
);
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());
617 filepos
.LowPart
= newFilePointer
;
618 return filepos
.QuadPart
;
625 bool QFSFileEnginePrivate::nativeSeek(qint64 pos
)
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
, ¤tFilePos
, FILE_BEGIN
)) {
639 q
->setError(QFile::UnspecifiedError
, qt_error_string());
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());
660 qint64
QFSFileEnginePrivate::nativeRead(char *data
, qint64 maxlen
)
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
)));
671 return readFdFh(data
, maxlen
);
674 // Windows native mode.
675 if (fileHandle
== INVALID_HANDLE_VALUE
)
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;
686 DWORD blockSize
= qMin
<DWORD
>(bytesToRead
, maxBlockSize
);
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());
698 totalRead
+= bytesRead
;
699 bytesToRead
-= bytesRead
;
700 } while (totalRead
< maxlen
);
701 return qint64(totalRead
);
707 qint64
QFSFileEnginePrivate::nativeReadLine(char *data
, qint64 maxlen
)
711 if (fh
|| fd
!= -1) {
712 // stdio / stdlib mode.
713 return readLineFdFh(data
, maxlen
);
716 // Windows native mode.
717 if (fileHandle
== INVALID_HANDLE_VALUE
)
720 // ### No equivalent in Win32?
721 return q
->QAbstractFileEngine::readLine(data
, maxlen
);
727 qint64
QFSFileEnginePrivate::nativeWrite(const char *data
, qint64 len
)
731 if (fh
|| fd
!= -1) {
732 // stdio / stdlib mode.
733 return writeFdFh(data
, len
);
736 // Windows native mode.
737 if (fileHandle
== INVALID_HANDLE_VALUE
)
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;
748 DWORD blockSize
= qMin
<DWORD
>(bytesToWrite
, maxBlockSize
);
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());
758 if (bytesWritten
== 0)
760 totalWritten
+= bytesWritten
;
761 bytesToWrite
-= bytesWritten
;
762 } while (totalWritten
< len
);
763 return qint64(totalWritten
);
769 int QFSFileEnginePrivate::nativeHandle() const
772 return fh
? QT_FILENO(fh
) : fd
;
778 if (openMode
& QIODevice::Append
)
780 if (!(openMode
& QIODevice::WriteOnly
))
782 cachedFd
= _open_osfhandle((intptr_t) fileHandle
, flags
);
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
)
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
));
807 DWORD fileType
= GetFileType(handle
);
808 return fileType
== FILE_TYPE_PIPE
;
813 return isSequentialFdFh();
818 bool QFSFileEngine::remove()
821 bool ret
= ::DeleteFile((wchar_t*)QFSFileEnginePrivate::longFileName(d
->filePath
).utf16()) != 0;
823 setError(QFile::RemoveError
, qt_error_string());
827 bool QFSFileEngine::copy(const QString
©Name
)
830 bool ret
= ::CopyFile((wchar_t*)QFSFileEnginePrivate::longFileName(d
->filePath
).utf16(),
831 (wchar_t*)QFSFileEnginePrivate::longFileName(copyName
).utf16(), true) != 0;
833 setError(QFile::CopyError
, qt_error_string());
837 bool QFSFileEngine::rename(const QString
&newName
)
840 bool ret
= ::MoveFile((wchar_t*)QFSFileEnginePrivate::longFileName(d
->filePath
).utf16(),
841 (wchar_t*)QFSFileEnginePrivate::longFileName(newName
).utf16()) != 0;
843 setError(QFile::RenameError
, qt_error_string());
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"))
865 if (platformId
== 1 && QFSFileEnginePrivate::longFileName(path
).size() > 256)
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('\\')))
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(),
895 if (hFind
!= INVALID_HANDLE_VALUE
) {
897 fileAttrib
= findData
.dwFileAttributes
;
904 *existed
= fileAttrib
!= INVALID_FILE_ATTRIBUTES
;
906 if (fileAttrib
== INVALID_FILE_ATTRIBUTES
)
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..
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()) {
929 oldslash
= dirName
.indexOf(QDir::separator(), oldslash
);
931 for (int slash
=0; slash
!= -1; oldslash
= slash
) {
932 slash
= dirName
.indexOf(QDir::separator(), oldslash
+1);
934 if (oldslash
== dirName
.length())
936 slash
= dirName
.length();
939 QString chunk
= dirName
.left(slash
);
940 bool existed
= false;
941 if (!isDirPath(chunk
, &existed
)) {
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(':'))
965 if (!isDirPath(chunk
, 0))
968 return oldslash
!= 0;
969 slash
= dirName
.lastIndexOf(QDir::separator(), oldslash
-1);
976 bool QFSFileEngine::caseSensitive() const
981 bool QFSFileEngine::setCurrentPath(const QString
&path
)
983 if (!QDir(path
).exists())
986 #if !defined(Q_OS_WINCE)
987 return ::SetCurrentDirectory((wchar_t*)path
.utf16()) != 0;
989 qfsPrivateCurrentDir
= QFSFileEnginePrivate::longFileName(path
);
994 QString
QFSFileEngine::currentPath(const QString
&fileName
)
996 #if !defined(Q_OS_WINCE)
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()) {
1011 wchar_t currentName
[PATH_MAX
];
1012 size
= ::GetCurrentDirectory(PATH_MAX
, currentName
);
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
;
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
);
1029 if (qfsPrivateCurrentDir
.isEmpty())
1030 qfsPrivateCurrentDir
= QCoreApplication::applicationDirPath();
1032 return QDir::fromNativeSeparators(qfsPrivateCurrentDir
);
1036 QString
QFSFileEngine::homePath()
1039 #if !defined(QT_NO_LIBRARY)
1040 QFSFileEnginePrivate::resolveLibs();
1041 if (ptrGetUserProfileDirectoryW
) {
1042 HANDLE hnd
= ::GetCurrentProcess();
1044 BOOL ok
= ::OpenProcessToken(hnd
, TOKEN_QUERY
, &token
);
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
);
1054 ret
= QString::fromWCharArray(userDirectory
);
1056 delete [] userDirectory
;
1058 ::CloseHandle(token
);
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
))
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());
1088 ret
= QLatin1String("c:");
1089 ret
.append(QLatin1Char('/'));
1090 #elif defined(Q_OS_OS2EMX)
1092 _abspath(dir
, QLatin1String("/"), _MAX_PATH
);
1098 QString
QFSFileEngine::tempPath()
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('\\')))
1108 ret
= QDir::fromNativeSeparators(ret
);
1111 if (ret
.isEmpty()) {
1112 #if !defined(Q_OS_WINCE)
1113 ret
= QLatin1String("c:/tmp");
1115 ret
= QLatin1String("/Temp");
1121 QFileInfoList
QFSFileEngine::drives()
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
)
1131 driveBits
&= 0x3ffffff;
1133 char driveName
[] = "A:/";
1137 ret
.append(QFileInfo(QLatin1String(driveName
)));
1139 driveBits
= driveBits
>> 1;
1143 ret
.append(QFileInfo(QLatin1String("/")));
1148 bool QFSFileEnginePrivate::doStat() const
1154 if (filePath
.isEmpty())
1157 QString fname
= filePath
.endsWith(QLatin1String(".lnk")) ? readLink(filePath
) : filePath
;
1158 fname
= fixIfRelativeUncPath(fname
);
1160 UINT oldmode
= SetErrorMode(SEM_FAILCRITICALERRORS
| SEM_NOOPENFILEERRORBOX
);
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
)) {
1169 fileAttrib
= fileInfo
.dwFileAttributes
;
1173 DWORD tmpAttributes
= GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(fname
).utf16());
1174 if (tmpAttributes
!= -1) {
1175 fileAttrib
= tmpAttributes
;
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('\\')))
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(),
1194 if (hFind
!= INVALID_HANDLE_VALUE
) {
1196 fileAttrib
= findData
.dwFileAttributes
;
1201 could_stat
= fileAttrib
!= INVALID_FILE_ATTRIBUTES
;
1203 #if !defined(Q_OS_WINCE)
1204 if (isDriveRoot(fname
)) {
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
;
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);
1221 s
= path
.indexOf(path
.at(0),s
+1);
1223 // "\\server\share\..."
1224 if (s
== path
.size() - 1) {
1225 // "\\server\share\"
1228 // "\\server\share\notfound"
1239 if (is_dir
&& uncShareExists(path
)) {
1240 // looks like a UNC dir, is a dir.
1241 fileAttrib
= FILE_ATTRIBUTE_DIRECTORY
;
1244 #if !defined(Q_OS_WINCE)
1250 SetErrorMode(oldmode
);
1256 static QString
readSymLink(const QString
&link
)
1259 #if !defined(Q_OS_WINCE)
1260 HANDLE handle
= CreateFile((wchar_t*)QFSFileEnginePrivate::longFileName(link
).utf16(),
1262 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
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
);
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
);
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);
1288 CloseHandle(handle
);
1292 #endif // Q_OS_WINCE
1296 static QString
readLink(const QString
&link
)
1298 #if !defined(Q_OS_WINCE)
1299 #if !defined(QT_NO_LIBRARY) && !defined(Q_CC_MWERKS)
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;
1313 hres
= CoCreateInstance(CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
,
1314 IID_IShellLink
, (LPVOID
*)&psl
);
1316 if (SUCCEEDED(hres
)) { // Get pointer to the IPersistFile interface.
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
);
1338 #endif // QT_NO_LIBRARY
1340 wchar_t target
[MAX_PATH
];
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('"')))
1346 if (result
.endsWith(QLatin1Char('"')))
1347 result
.remove(result
.size()-1,1);
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)
1359 QString linkName
= newName
;
1360 //### assume that they add .lnk
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;
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
)) {
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
))
1391 setError(QFile::RenameError
, qt_error_string());
1400 #endif // QT_NO_LIBRARY
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()));
1411 setError(QFile::RenameError
, qt_error_string());
1413 #endif // Q_OS_WINCE
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
)) {
1428 PSECURITY_DESCRIPTOR pSD
;
1429 ACCESS_MASK access_mask
;
1431 enum { ReadMask
= 0x00000001, WriteMask
= 0x00000002, ExecMask
= 0x00000020 };
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
) {
1443 if(ptrGetEffectiveRightsFromAclW(pDacl
, ¤tUserTrusteeW
, &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
;
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
;
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
;
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
;
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
;
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
;
1520 bool QFSFileEnginePrivate::isSymlink() const
1522 #if !defined(Q_OS_WINCE)
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('\\')))
1533 WIN32_FIND_DATA findData
;
1534 HANDLE hFind
= ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path
).utf16(),
1536 if (hFind
!= INVALID_HANDLE_VALUE
) {
1538 if ((findData
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)
1539 && (findData
.dwReserved0
== IO_REPARSE_TAG_MOUNT_POINT
1540 || findData
.dwReserved0
== IO_REPARSE_TAG_SYMLINK
)) {
1549 #endif // Q_OS_WINCE
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
) {
1562 #if !defined(Q_OS_WINCE)
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
1576 if (type
& TypesMask
) {
1577 if (d
->filePath
.endsWith(QLatin1String(".lnk"))) {
1579 QString l
= readLink(d
->filePath
);
1581 bool existed
= false;
1582 if (isDirPath(l
, &existed
) && existed
)
1583 ret
|= DirectoryType
;
1587 } else if (d
->doStat()) {
1588 if ((type
& LinkType
) && d
->isSymlink())
1590 if (d
->fileAttrib
& FILE_ATTRIBUTE_DIRECTORY
) {
1591 ret
|= DirectoryType
;
1597 if (type
& FlagsMask
) {
1598 ret
|= LocalDiskFlag
;
1601 if (d
->filePath
== QLatin1String("/") || isDriveRoot(d
->filePath
) || isUncRoot(d
->filePath
)) {
1603 } else if (d
->fileAttrib
& FILE_ATTRIBUTE_HIDDEN
) {
1604 QString baseName
= fileName(BaseName
);
1605 if (baseName
!= QLatin1String(".") && baseName
!= QLatin1String(".."))
1613 QString
QFSFileEngine::fileName(FileName file
) const
1615 Q_D(const QFSFileEngine
);
1616 if (file
== BaseName
) {
1617 int slash
= d
->filePath
.lastIndexOf(QLatin1Char('/'));
1619 int colon
= d
->filePath
.lastIndexOf(QLatin1Char(':'));
1621 return d
->filePath
.mid(colon
+ 1);
1624 return d
->filePath
.mid(slash
+ 1);
1625 } else if (file
== PathName
) {
1626 if (!d
->filePath
.size())
1629 int slash
= d
->filePath
.lastIndexOf(QLatin1Char('/'));
1631 if (d
->filePath
.length() >= 2 && d
->filePath
.at(1) == QLatin1Char(':'))
1632 return d
->filePath
.left(2);
1633 return QString(QLatin1Char('.'));
1636 return QString(QLatin1Char('/'));
1637 if (slash
== 2 && d
->filePath
.length() >= 2 && d
->filePath
.at(1) == QLatin1Char(':'))
1639 return d
->filePath
.left(slash
);
1641 } else if (file
== AbsoluteName
|| file
== AbsolutePathName
) {
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
));
1658 ret
= QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + d
->filePath
);
1661 // The path should be absolute at this point.
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('/'));
1678 else if (ret
.at(0) != QLatin1Char('/') && slash
== 2)
1679 return ret
.left(3); // include the slash
1681 return ret
.left(slash
> 0 ? slash
: 1);
1684 } else if (file
== CanonicalName
|| file
== CanonicalPathName
) {
1685 if (!(fileFlags(ExistsFlag
) & ExistsFlag
))
1688 QString ret
= QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName
));
1689 if (!ret
.isEmpty() && file
== CanonicalPathName
) {
1690 int slash
= ret
.lastIndexOf(QLatin1Char('/'));
1692 ret
= QDir::currentPath();
1693 else if (slash
== 0)
1694 ret
= QString(QLatin1Char('/'));
1695 ret
= ret
.left(slash
);
1698 } else if (file
== LinkName
) {
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
) {
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;
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
)) {
1733 PSECURITY_DESCRIPTOR pSD
;
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;
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
);
1765 bool QFSFileEngine::setPermissions(uint perms
)
1771 if (perms
& QFile::ReadOwner
|| perms
& QFile::ReadUser
|| perms
& QFile::ReadGroup
|| perms
& QFile::ReadOther
)
1773 if (perms
& QFile::WriteOwner
|| perms
& QFile::WriteUser
|| perms
& QFile::WriteGroup
|| perms
& QFile::WriteOther
)
1776 if (mode
== 0) // not supported
1779 ret
= ::_wchmod((wchar_t*)QFSFileEnginePrivate::longFileName(d
->filePath
).utf16(), mode
) == 0;
1781 setError(QFile::PermissionsError
, qt_error_string(errno
));
1785 bool QFSFileEngine::setSize(qint64 size
)
1789 if (d
->fileHandle
!= INVALID_HANDLE_VALUE
|| d
->fd
!= -1) {
1791 HANDLE fh
= d
->fileHandle
;
1792 #if !defined(Q_OS_WINCE)
1793 if (fh
== INVALID_HANDLE_VALUE
)
1794 fh
= (HANDLE
)_get_osfhandle(d
->fd
);
1796 if (fh
== INVALID_HANDLE_VALUE
)
1798 qint64 currentPos
= pos();
1800 if (seek(size
) && SetEndOfFile(fh
)) {
1801 seek(qMin(currentPos
, size
));
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
);
1815 setError(QFile::ResizeError
, file
.errorString());
1823 static inline QDateTime
fileTimeToQDateTime(const FILETIME
*time
)
1827 #if defined(Q_OS_WINCE)
1830 systime
.wYear
= 1970;
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
);
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
));
1856 QDateTime
QFSFileEngine::fileTime(FileTime time
) const
1858 Q_D(const QFSFileEngine
);
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
);
1876 WIN32_FILE_ATTRIBUTE_DATA attribData
;
1877 bool ok
= ::GetFileAttributesEx((wchar_t*)QFSFileEnginePrivate::longFileName(d
->filePath
).utf16(), GetFileExInfoStandard
, &attribData
);
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('\\')))
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(),
1891 if (hFind
!= INVALID_HANDLE_VALUE
) {
1894 attribData
.ftCreationTime
= findData
.ftCreationTime
;
1895 attribData
.ftLastWriteTime
= findData
.ftLastWriteTime
;
1896 attribData
.ftLastAccessTime
= findData
.ftLastAccessTime
;
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
);
1913 uchar
*QFSFileEnginePrivate::map(qint64 offset
, qint64 size
,
1914 QFile::MemoryMapFlags flags
)
1918 if (openMode
== QFile::NotOpen
) {
1919 q
->setError(QFile::PermissionsError
, qt_error_string(ERROR_ACCESS_DENIED
));
1922 if (offset
== 0 && size
== 0) {
1923 q
->setError(QFile::UnspecifiedError
, qt_error_string(ERROR_INVALID_PARAMETER
));
1927 if (mapHandle
== INVALID_HANDLE_VALUE
) {
1928 // get handle to the file
1929 HANDLE handle
= fileHandle
;
1932 if (handle
== INVALID_HANDLE_VALUE
&& fh
)
1933 handle
= (HANDLE
)::_get_osfhandle(QT_FILENO(fh
));
1936 #ifdef Q_USE_DEPRECATED_MAP_API
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),
1944 FILE_ATTRIBUTE_NORMAL
,
1948 if (handle
== INVALID_HANDLE_VALUE
) {
1949 q
->setError(QFile::PermissionsError
, qt_error_string(ERROR_ACCESS_DENIED
));
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
);
1965 // setup args to map
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
;
1979 // attempt to create the map
1980 LPVOID mapAddress
= ::MapViewOfFile(mapHandle
, access
,
1981 offsetHi
, offsetLo
, size
+ extra
);
1983 uchar
*address
= extra
+ static_cast<uchar
*>(mapAddress
);
1984 maps
[address
] = extra
;
1988 switch(GetLastError()) {
1989 case ERROR_ACCESS_DENIED
:
1990 q
->setError(QFile::PermissionsError
, qt_error_string());
1992 case ERROR_INVALID_PARAMETER
:
1993 // size are out of bounds
1995 q
->setError(QFile::UnspecifiedError
, qt_error_string());
1998 ::CloseHandle(mapHandle
);
2002 bool QFSFileEnginePrivate::unmap(uchar
*ptr
)
2005 if (!maps
.contains(ptr
)) {
2006 q
->setError(QFile::PermissionsError
, qt_error_string(ERROR_ACCESS_DENIED
));
2009 uchar
*start
= ptr
- maps
[ptr
];
2010 if (!UnmapViewOfFile(start
)) {
2011 q
->setError(QFile::PermissionsError
, qt_error_string());
2016 if (maps
.isEmpty()) {
2017 ::CloseHandle(mapHandle
);
2018 mapHandle
= INVALID_HANDLE_VALUE
;