/*
    SPDX-FileCopyrightText: 2009 Nokia Corporation and /or its subsidiary(-ies).
    Contact: Qt Software Information (qt-info@nokia.com)

    This file is part of the QtCore module of the Qt Toolkit.

    $QT_BEGIN_LICENSE:LGPL$
    Commercial Usage
    Licensees holding valid Qt Commercial licenses may use this file in
    accordance with the Qt Commercial License Agreement provided with the
    Software or, alternatively, in accordance with the terms contained in
    a written agreement between you and Nokia.

    GNU Lesser General Public License Usage
    Alternatively, this file may be used under the terms of the GNU Lesser
    General Public License version 2.1 as published by the Free Software
    Foundation and appearing in the file LICENSE.LGPL included in the
    packaging of this file.  Please review the following information to
    ensure the GNU Lesser General Public License version 2.1 requirements
    will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.

    In addition, as a special exception, Nokia gives you certain
    additional rights. These rights are described in the Nokia Qt LGPL
    Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    package.

    GNU General Public License Usage
    Alternatively, this file may be used under the terms of the GNU
    General Public License version 3.0 as published by the Free Software
    Foundation and appearing in the file LICENSE.GPL included in the
    packaging of this file.  Please review the following information to
    ensure the GNU General Public License version 3.0 requirements will be
    met: https://www.gnu.org/licenses/gpl-3.0.html.

    If you are unsure which license is appropriate for your use, please
    contact the sales department at qt-sales@nokia.com.
    $QT_END_LICENSE$

*/

#define _POSIX_
#include "qplatformdefs.h"
#include "qabstractfileengine.h"
#include "private/qfsfileengine_p.h"
#include <qdebug.h>

#include "QtCore/QFile"
#include "QtCore/QDir"
#include "qtemporaryfile.h"
#ifndef QT_NO_REGEXP
# include "QtCore/QRegExp"
#endif
#include "private/qmutexpool_p.h"
#include "qvarlengtharray.h"
#include "qdatetime.h"
#include "qt_windows.h"

#if !defined(Q_OS_WINCE)
#  include <sys/types.h>
#  include <direct.h>
#else
#  include <types.h>
#endif
#include <objbase.h>
#include <shlobj.h>
#include <initguid.h>
#include <accctrl.h>
#include <ctype.h>
#include <limits.h>
#define SECURITY_WIN32
#ifdef Q_CC_MINGW
// A workaround for a certain version of MinGW, the define UNICODE_STRING.
#include <subauth.h>
#endif
#include <security.h>

#ifndef _INTPTR_T_DEFINED
#ifdef  _WIN64
typedef __int64             intptr_t;
#else
#ifdef _W64
typedef _W64 int            intptr_t;
#else
typedef INT_PTR intptr_t;
#endif
#endif
#define _INTPTR_T_DEFINED
#endif

#ifndef INVALID_FILE_ATTRIBUTES
#  define INVALID_FILE_ATTRIBUTES (DWORD (-1))
#endif

QT_BEGIN_NAMESPACE

static QString readLink(const QString &link);

LIBK3B_EXPORT int qt_ntfs_permission_lookup = 0;

#if defined(Q_OS_WINCE)
static QString qfsPrivateCurrentDir = QLatin1String("");
// As none of the functions we try to resolve do exist on Windows CE
// we use QT_NO_LIBRARY to shorten everything up a little bit.
#define QT_NO_LIBRARY 1
#endif

#if !defined(QT_NO_LIBRARY)
QT_BEGIN_INCLUDE_NAMESPACE
typedef DWORD (WINAPI *PtrGetNamedSecurityInfoW)(LPWSTR, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID*, PSID*, PACL*, PACL*, PSECURITY_DESCRIPTOR*);
static PtrGetNamedSecurityInfoW ptrGetNamedSecurityInfoW = 0;
typedef DECLSPEC_IMPORT BOOL (WINAPI *PtrLookupAccountSidW)(LPCWSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, PSID_NAME_USE);
static PtrLookupAccountSidW ptrLookupAccountSidW = 0;
typedef DECLSPEC_IMPORT BOOL (WINAPI *PtrAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*);
static PtrAllocateAndInitializeSid ptrAllocateAndInitializeSid = 0;
typedef VOID (WINAPI *PtrBuildTrusteeWithSidW)(PTRUSTEE_W, PSID);
static PtrBuildTrusteeWithSidW ptrBuildTrusteeWithSidW = 0;
typedef VOID (WINAPI *PtrBuildTrusteeWithNameW)(PTRUSTEE_W, unsigned short*);
static PtrBuildTrusteeWithNameW ptrBuildTrusteeWithNameW = 0;
typedef DWORD (WINAPI *PtrGetEffectiveRightsFromAclW)(PACL, PTRUSTEE_W, OUT PACCESS_MASK);
static PtrGetEffectiveRightsFromAclW ptrGetEffectiveRightsFromAclW = 0;
typedef DECLSPEC_IMPORT PVOID (WINAPI *PtrFreeSid)(PSID);
static PtrFreeSid ptrFreeSid = 0;
static TRUSTEE_W currentUserTrusteeW;

typedef BOOL (WINAPI *PtrOpenProcessToken)(HANDLE, DWORD, PHANDLE );
static PtrOpenProcessToken ptrOpenProcessToken = 0;
typedef BOOL (WINAPI *PtrGetUserProfileDirectoryW)( HANDLE, LPWSTR, LPDWORD);
static PtrGetUserProfileDirectoryW ptrGetUserProfileDirectoryW = 0;
typedef BOOL (WINAPI *PtrSetFilePointerEx)(HANDLE, LARGE_INTEGER, PLARGE_INTEGER, DWORD);
static PtrSetFilePointerEx ptrSetFilePointerEx = 0;
QT_END_INCLUDE_NAMESPACE


void QFSFileEnginePrivate::resolveLibs()
{
    static bool triedResolve = false;
    if(!triedResolve) {
        // need to resolve the security info functions

        // protect initialization
#ifndef QT_NO_THREAD
        QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
        // check triedResolve again, since another thread may have already
        // done the initialization
        if(triedResolve) {
            // another thread did initialize the security function pointers,
            // so we shouldn't do it again.
            return;
        }
#endif

        triedResolve = true;
#if !defined(Q_OS_WINCE)
        if(QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) {
            HINSTANCE advapiHnd = LoadLibraryW(L"advapi32");
            if (advapiHnd) {
                ptrGetNamedSecurityInfoW = (PtrGetNamedSecurityInfoW)GetProcAddress(advapiHnd, "GetNamedSecurityInfoW");
                ptrLookupAccountSidW = (PtrLookupAccountSidW)GetProcAddress(advapiHnd, "LookupAccountSidW");
                ptrAllocateAndInitializeSid = (PtrAllocateAndInitializeSid)GetProcAddress(advapiHnd, "AllocateAndInitializeSid");
                ptrBuildTrusteeWithSidW = (PtrBuildTrusteeWithSidW)GetProcAddress(advapiHnd, "BuildTrusteeWithSidW");
                ptrBuildTrusteeWithNameW = (PtrBuildTrusteeWithNameW)GetProcAddress(advapiHnd, "BuildTrusteeWithNameW");
                ptrGetEffectiveRightsFromAclW = (PtrGetEffectiveRightsFromAclW)GetProcAddress(advapiHnd, "GetEffectiveRightsFromAclW");
                ptrFreeSid = (PtrFreeSid)GetProcAddress(advapiHnd, "FreeSid");
            }
            if (ptrBuildTrusteeWithNameW) {
                HINSTANCE versionHnd = LoadLibraryW(L"version");
                if (versionHnd) {
                    typedef DWORD (WINAPI *PtrGetFileVersionInfoSizeW)(LPWSTR lptstrFilename,LPDWORD lpdwHandle);
                    PtrGetFileVersionInfoSizeW ptrGetFileVersionInfoSizeW = (PtrGetFileVersionInfoSizeW)GetProcAddress(versionHnd, "GetFileVersionInfoSizeW");
                    typedef BOOL (WINAPI *PtrGetFileVersionInfoW)(LPWSTR lptstrFilename,DWORD dwHandle,DWORD dwLen,LPVOID lpData);
                    PtrGetFileVersionInfoW ptrGetFileVersionInfoW = (PtrGetFileVersionInfoW)GetProcAddress(versionHnd, "GetFileVersionInfoW");
                    typedef BOOL (WINAPI *PtrVerQueryValueW)(const LPVOID pBlock,LPWSTR lpSubBlock,LPVOID *lplpBuffer,PUINT puLen);
                    PtrVerQueryValueW ptrVerQueryValueW = (PtrVerQueryValueW)GetProcAddress(versionHnd, "VerQueryValueW");
                    if(ptrGetFileVersionInfoSizeW && ptrGetFileVersionInfoW && ptrVerQueryValueW) {
                        DWORD fakeHandle;
                        DWORD versionSize = ptrGetFileVersionInfoSizeW(L"secur32.dll", &fakeHandle);
                        if(versionSize) {
                            LPVOID versionData;
                            versionData = malloc(versionSize);
                            if(ptrGetFileVersionInfoW(L"secur32.dll", 0, versionSize, versionData)) {
                                UINT puLen;
                                VS_FIXEDFILEINFO *pLocalInfo;
                                if(ptrVerQueryValueW(versionData, L"\\", (void**)&pLocalInfo, &puLen)) {
                                    WORD wVer1, wVer2, wVer3, wVer4;
                                    wVer1 = HIWORD(pLocalInfo->dwFileVersionMS);
                                    wVer2 = LOWORD(pLocalInfo->dwFileVersionMS);
                                    wVer3 = HIWORD(pLocalInfo->dwFileVersionLS);
                                    wVer4 = LOWORD(pLocalInfo->dwFileVersionLS);
                                    // It will not work with secur32.dll version 5.0.2195.2862
                                    if(!(wVer1 == 5 && wVer2 == 0 && wVer3 == 2195 && (wVer4 == 2862 || wVer4 == 4587))) {
                                        HINSTANCE userHnd = LoadLibraryW(L"secur32");
                                        if (userHnd) {
                                            typedef BOOL (WINAPI *PtrGetUserNameExW)(EXTENDED_NAME_FORMAT nameFormat, ushort* lpBuffer, LPDWORD nSize);
                                            PtrGetUserNameExW ptrGetUserNameExW = (PtrGetUserNameExW)GetProcAddress(userHnd, "GetUserNameExW");
                                            if(ptrGetUserNameExW) {
                                                static TCHAR buffer[258];
                                                DWORD bufferSize = 257;
                                                ptrGetUserNameExW(NameSamCompatible, (ushort*)buffer, &bufferSize);
                                                ptrBuildTrusteeWithNameW(&currentUserTrusteeW, (ushort*)buffer);
                                            }
                                            FreeLibrary(userHnd);
                                        }
                                    }
                                }
                            }
                            free(versionData);
                        }
                    }
                    FreeLibrary(versionHnd);
                }
            }
            ptrOpenProcessToken = (PtrOpenProcessToken)GetProcAddress(advapiHnd, "OpenProcessToken");
	        HINSTANCE userenvHnd = LoadLibraryW(L"userenv");
            if (userenvHnd) {
                ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW");
            }
            HINSTANCE kernelHnd = LoadLibraryW(L"kernel32");
            if (kernelHnd)
                ptrSetFilePointerEx = (PtrSetFilePointerEx)GetProcAddress(kernelHnd, "SetFilePointerEx");
        }
#endif
    }
}
#endif // QT_NO_LIBRARY

// UNC functions NT
typedef DWORD (WINAPI *PtrNetShareEnum_NT)(LPWSTR, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD);
static PtrNetShareEnum_NT ptrNetShareEnum_NT = 0;
typedef DWORD (WINAPI *PtrNetApiBufferFree_NT)(LPVOID);
static PtrNetApiBufferFree_NT ptrNetApiBufferFree_NT = 0;
typedef struct _SHARE_INFO_1_NT {
    LPWSTR shi1_netname;
    DWORD shi1_type;
    LPWSTR shi1_remark;
} SHARE_INFO_1_NT;


bool QFSFileEnginePrivate::resolveUNCLibs_NT()
{
    static bool triedResolve = false;
    if (!triedResolve) {
#ifndef QT_NO_THREAD
        QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
        if (triedResolve) {
            return ptrNetShareEnum_NT && ptrNetApiBufferFree_NT;
        }
#endif
        triedResolve = true;
#if !defined(Q_OS_WINCE)
        HINSTANCE hLib = LoadLibraryW(L"Netapi32");
        if (hLib) {
            ptrNetShareEnum_NT = (PtrNetShareEnum_NT)GetProcAddress(hLib, "NetShareEnum");
            if (ptrNetShareEnum_NT)
                ptrNetApiBufferFree_NT = (PtrNetApiBufferFree_NT)GetProcAddress(hLib, "NetApiBufferFree");
        }
#endif
    }
    return ptrNetShareEnum_NT && ptrNetApiBufferFree_NT;
}

// UNC functions 9x
typedef DWORD (WINAPI *PtrNetShareEnum_9x)(const char FAR *, short, char FAR *, unsigned short, unsigned short FAR *, unsigned short FAR *);
static PtrNetShareEnum_9x ptrNetShareEnum_9x = 0;
#ifdef LM20_NNLEN
# define LM20_NNLEN_9x LM20_NNLEN
#else
# define LM20_NNLEN_9x 12
#endif
typedef struct _SHARE_INFO_1_9x {
  char shi1_netname[LM20_NNLEN_9x+1];
  char shi1_pad1;
  unsigned short shi1_type;
  char FAR* shi1_remark;
} SHARE_INFO_1_9x;

bool QFSFileEnginePrivate::resolveUNCLibs_9x()
{
    static bool triedResolve = false;
    if (!triedResolve) {
#ifndef QT_NO_THREAD
        QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
        if (triedResolve) {
            return ptrNetShareEnum_9x;
        }
#endif
        triedResolve = true;
#if !defined(Q_OS_WINCE)
        HINSTANCE hLib = LoadLibraryA("Svrapi");
        if (hLib)
            ptrNetShareEnum_9x = (PtrNetShareEnum_9x)GetProcAddress(hLib, "NetShareEnum");
#endif
    }
    return ptrNetShareEnum_9x;
}

bool QFSFileEnginePrivate::uncListSharesOnServer(const QString &server, QStringList *list)
{
    if (resolveUNCLibs_NT()) {
        SHARE_INFO_1_NT *BufPtr, *p;
        DWORD res;
        DWORD er=0,tr=0,resume=0, i;
        do {
            res = ptrNetShareEnum_NT((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume);
            if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) {
                p=BufPtr;
                for (i = 1; i <= er; ++i) {
                    if (list && p->shi1_type == 0)
                        list->append(QString::fromUtf16((unsigned short *)p->shi1_netname));
                    p++;
                }
            }
            ptrNetApiBufferFree_NT(BufPtr);
        } while (res==ERROR_MORE_DATA);
        return res == ERROR_SUCCESS;

    } else if (resolveUNCLibs_9x()) {
        SHARE_INFO_1_9x *pBuf = 0;
        short cbBuffer;
        unsigned short nEntriesRead = 0;
        unsigned short nTotalEntries = 0;
        short numBuffs = 20;
        DWORD nStatus = 0;
        do {
            cbBuffer = numBuffs * sizeof(SHARE_INFO_1_9x);
            pBuf = (SHARE_INFO_1_9x *)malloc(cbBuffer);
            if (pBuf) {
                nStatus = ptrNetShareEnum_9x(server.toLocal8Bit().constData(), 1, (char FAR *)pBuf, cbBuffer, &nEntriesRead, &nTotalEntries);
                if ((nStatus == ERROR_SUCCESS)) {
                    for (int i = 0; i < nEntriesRead; ++i) {
                        if (list && pBuf[i].shi1_type == 0)
                            list->append(QString::fromLocal8Bit(pBuf[i].shi1_netname));
                    }
                    free(pBuf);
                    break;
                }
                free(pBuf);
                numBuffs *=2;
            }
        } while (nStatus == ERROR_MORE_DATA);
        return nStatus == ERROR_SUCCESS;
    }
    return false;
}

static bool isUncRoot(const QString &server)
{
    QString localPath = QDir::toNativeSeparators(server);
    QStringList parts = localPath.split(QLatin1Char('\\'), QString::SkipEmptyParts);
    return localPath.startsWith(QLatin1String("\\\\")) && parts.count() <= 1;
}

static bool isUncPath(const QString &path)
{
    // Starts with // or \\, but not \\. or //.
    return (path.startsWith(QLatin1String("//"))
            || path.startsWith(QLatin1String("\\\\")))
        && (path.size() > 2 && path.at(2) != QLatin1Char('.'));
}

static bool isRelativePath(const QString &path)
{
    return !(path.startsWith(QLatin1Char('/'))
           || (path.length() >= 2
           && ((path.at(0).isLetter() && path.at(1) == QLatin1Char(':'))
           || (path.at(0) == QLatin1Char('/') && path.at(1) == QLatin1Char('/'))))); // drive, e.g. a:
}

static QString fixIfRelativeUncPath(const QString &path)
{
    if (isRelativePath(path)) {
        QString currentPath = QDir::currentPath() + QLatin1Char('/');
        if (currentPath.startsWith(QLatin1String("//")))
            return QString(path).prepend(currentPath);
    }
    return path;
}

// can be //server or //server/share
static bool uncShareExists(const QString &server)
{
    QStringList parts = server.split(QLatin1Char('\\'), QString::SkipEmptyParts);
    if (parts.count()) {
        QStringList shares;
        if (QFSFileEnginePrivate::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(0), &shares)) {
            if (parts.count() >= 2)
                return shares.contains(parts.at(1), Qt::CaseInsensitive);
            else
                return true;
        }
    }
    return false;
}

#if !defined(Q_OS_WINCE)
// If you change this function, remember to also change the UNICODE version
static QString nativeAbsoluteFilePathA(const QString &path)
{
    QString ret;
    QVarLengthArray<char, MAX_PATH> buf(MAX_PATH);
    char *fileName = 0;
    QByteArray ba = path.toLocal8Bit();
    DWORD retLen = GetFullPathNameA(ba.constData(), buf.size(), buf.data(), &fileName);
    if (retLen > (DWORD)buf.size()) {
        buf.resize(retLen);
        retLen = GetFullPathNameA(ba.constData(), buf.size(), buf.data(), &fileName);
    }
    if (retLen != 0)
        ret = QString::fromLocal8Bit(buf.data(), retLen);
    return ret;
}
#endif

// If you change this function, remember to also change the NON-UNICODE version
static QString nativeAbsoluteFilePathW(const QString &path)
{
    QString ret;
#if !defined(Q_OS_WINCE)
    QVarLengthArray<wchar_t, MAX_PATH> buf(MAX_PATH);
    wchar_t *fileName = 0;
    DWORD retLen = GetFullPathNameW((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
    if (retLen > (DWORD)buf.size()) {
        buf.resize(retLen);
        retLen = GetFullPathNameW((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
    }
    if (retLen != 0)
        ret = QString::fromUtf16((unsigned short *)buf.data(), retLen);
#else
    if (path.startsWith(QLatin1String("/")) || path.startsWith(QLatin1String("\\")))
        ret = QDir::toNativeSeparators(path);
    else
        ret = QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir + QLatin1Char('/') + path));
#endif
    return ret;
}

static QString nativeAbsoluteFilePath(const QString &path)
{
    QString absPath = QT_WA_INLINE(nativeAbsoluteFilePathW(path), nativeAbsoluteFilePathA(path));
    // This is really ugly, but GetFullPathName strips off whitespace at the end.
    // If you for instance write ". " in the lineedit of QFileDialog,
    // (which is an invalid filename) this function will strip the space off and viola,
    // the file is later reported as existing. Therefore, we re-add the whitespace that
    // was at the end of path in order to keep the filename invalid.
    int i = path.size() - 1;
    while (i >= 0 && path.at(i) == QLatin1Char(' ')) --i;
    int extraws = path.size() - 1 - i;
    if (extraws >= 0) {
        while (extraws) {
            absPath.append(QLatin1Char(' '));
            --extraws;
        }
    }
    return absPath;
}

QByteArray QFSFileEnginePrivate::win95Name(const QString &path)
{
    QString ret(path);
    if(path.length() > 1 && path[0] == QLatin1Char('/') && path[1] == QLatin1Char('/')) {
        // Win95 cannot handle slash-slash needs slosh-slosh.
        ret[0] = QLatin1Char('\\');
        ret[1] = QLatin1Char('\\');
        int n = ret.indexOf(QLatin1Char('/'));
        if(n >= 0)
            ret[n] = QLatin1Char('\\');
    } else if(path.length() > 3 && path[2] == QLatin1Char('/') && path[3] == QLatin1Char('/')) {
        ret[2] = QLatin1Char('\\');
        ret.remove(3, 1);
        int n = ret.indexOf(QLatin1Char('/'));
        if(n >= 0)
            ret[n] = QLatin1Char('\\');
    }
    return ret.toLocal8Bit();
}

/*!
    \internal
*/
QString QFSFileEnginePrivate::longFileName(const QString &path)
{
    if (path.startsWith(QLatin1String("\\\\.\\")))
        return path;

    QString absPath = nativeAbsoluteFilePath(path);
#if !defined(Q_OS_WINCE)
    QString prefix = QLatin1String("\\\\?\\");
    if (isUncPath(path)) {
        prefix = QLatin1String("\\\\?\\UNC\\");
        absPath.remove(0, 2);
    }
    return prefix + absPath;
#else
    return absPath;
#endif
}

/*
    \internal
*/
void QFSFileEnginePrivate::nativeInitFileName()
{
    QT_WA({
        QString path = longFileName(QDir::toNativeSeparators(fixIfRelativeUncPath(filePath)));
        nativeFilePath = QByteArray((const char *)path.utf16(), path.size() * 2 + 1);
    }, {
        QString path = fixIfRelativeUncPath(filePath);
        nativeFilePath = win95Name(path).replace('/', '\\');
    });
}

/*
    \internal
*/
bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
{
    Q_Q(QFSFileEngine);

    // All files are opened in share mode (both read and write).
    DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;

    int accessRights = 0;
    if (openMode & QIODevice::ReadOnly)
        accessRights |= GENERIC_READ;
    if (openMode & QIODevice::WriteOnly)
        accessRights |= GENERIC_WRITE;

    SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), nullptr, FALSE };

    // WriteOnly can create files, ReadOnly cannot.
    DWORD creationDisp = (openMode & QIODevice::WriteOnly)
                         ? OPEN_ALWAYS : OPEN_EXISTING;

    // Create the file handle.
    QT_WA({
        fileHandle = CreateFileW((TCHAR *)nativeFilePath.constData(),
                                 accessRights,
                                 shareMode,
                                 &securityAtts,
                                 creationDisp,
                                 FILE_ATTRIBUTE_NORMAL,
                                 nullptr);
    }, {
        fileHandle = CreateFileA(nativeFilePath.constData(),
                                 accessRights,
                                 shareMode,
                                 &securityAtts,
                                 creationDisp,
                                 FILE_ATTRIBUTE_NORMAL,
                                 nullptr);
    });

    // Bail out on error.
    if (fileHandle == INVALID_HANDLE_VALUE) {
        q->setError(QFile::OpenError, qt_error_string());
        return false;
    }

    // Truncate the file after successfully opening it if Truncate is passed.
    if (openMode & QIODevice::Truncate)
        q->setSize(0);

    return true;
}

/*
    \internal
*/
bool QFSFileEnginePrivate::nativeClose()
{
    Q_Q(QFSFileEngine);
    if (fh || fd != -1) {
        // stdlib / stdio mode.
        return closeFdFh();
    }

    // Windows native mode.
    bool ok = true;
    if ((fileHandle == INVALID_HANDLE_VALUE || !CloseHandle(fileHandle))
#ifdef Q_USE_DEPRECATED_MAP_API
            && (fileMapHandle == INVALID_HANDLE_VALUE || !CloseHandle(fileMapHandle))
#endif
        ) {
        q->setError(QFile::UnspecifiedError, qt_error_string());
        ok = false;
    }
    fileHandle = INVALID_HANDLE_VALUE;
    cachedFd = -1;              // gets closed by CloseHandle above

    return ok;
}

/*
    \internal
*/
bool QFSFileEnginePrivate::nativeFlush()
{
    if (fh) {
        // Buffered stdlib mode.
        return flushFh();
    }
    if (fd != -1) {
        // Unbuffered stdio mode; always succeeds (no buffer).
        return true;
    }

    // Windows native mode; flushing is
    // unnecessary. FlushFileBuffers(), the equivalent of sync() or
    // fsync() on Unix, does a low-level flush to the disk, and we
    // don't expose an API for this.
    return true;
}

/*
    \internal
*/
qint64 QFSFileEnginePrivate::nativeSize() const
{
    Q_Q(const QFSFileEngine);
    QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);

    // ### Don't flush; for buffered files, we should get away with ftell.
    thatQ->flush();

    // Buffered stdlib mode.
    if (fh) {
        QT_OFF_T oldPos = QT_FTELL(fh);
        QT_FSEEK(fh, 0, SEEK_END);
        QT_OFF_T fileSize = QT_FTELL(fh);
        QT_FSEEK(fh, oldPos, SEEK_SET);
        return qint64(fileSize);
    }

    // Not-open mode, where the file name is known: We'll check the
    // file system directly.
    if (openMode == QIODevice::NotOpen && !nativeFilePath.isEmpty()) {
        bool ok = false;
        WIN32_FILE_ATTRIBUTE_DATA attribData;
        QT_WA({
            ok = ::GetFileAttributesExW((TCHAR *)nativeFilePath.constData(),
                                        GetFileExInfoStandard, &attribData);
        } , {
            ok = ::GetFileAttributesExA(nativeFilePath.constData(),
                                        GetFileExInfoStandard, &attribData);
        });
        if (ok) {
            qint64 size = attribData.nFileSizeHigh;
            size <<= 32;
            size += attribData.nFileSizeLow;
            return size;
        }
        thatQ->setError(QFile::UnspecifiedError, qt_error_string());
        return 0;
    }

    // Unbuffed stdio mode.
    if(fd != -1) {
#if !defined(Q_OS_WINCE)
        HANDLE handle = (HANDLE)_get_osfhandle(fd);
        if (handle != INVALID_HANDLE_VALUE) {
            BY_HANDLE_FILE_INFORMATION fileInfo;
            if (GetFileInformationByHandle(handle, &fileInfo)) {
                qint64 size = fileInfo.nFileSizeHigh;
                size <<= 32;
                size += fileInfo.nFileSizeLow;
                return size;
            }
        }
#endif
        thatQ->setError(QFile::UnspecifiedError, qt_error_string());
        return 0;
    }

    // Windows native mode.
    if (fileHandle == INVALID_HANDLE_VALUE)
        return 0;

    BY_HANDLE_FILE_INFORMATION fileInfo;
    if (!GetFileInformationByHandle(fileHandle, &fileInfo)) {
        thatQ->setError(QFile::UnspecifiedError, qt_error_string());
        return 0;
    }

    qint64 size = fileInfo.nFileSizeHigh;
    size <<= 32;
    size += fileInfo.nFileSizeLow;
    return size;
}

/*
    \internal
*/
qint64 QFSFileEnginePrivate::nativePos() const
{
    Q_Q(const QFSFileEngine);
    QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);

    if (fh || fd != -1) {
        // stdlib / stido mode.
        return posFdFh();
    }

    // Windows native mode.
    if (fileHandle == INVALID_HANDLE_VALUE)
        return 0;

#if !defined(QT_NO_LIBRARY)
    QFSFileEnginePrivate::resolveLibs();
    if (!ptrSetFilePointerEx) {
#endif
        DWORD newFilePointer = SetFilePointer(fileHandle, 0, nullptr, FILE_CURRENT);
        if (newFilePointer == 0xFFFFFFFF) {
            thatQ->setError(QFile::UnspecifiedError, qt_error_string());
            return 0;
        }

        // Note: returns <4GB; does not work with large files. This is the
        // case for MOC, UIC, qmake and other bootstrapped tools, and for
        // Win9x/ME.
        return qint64(newFilePointer);
#if !defined(QT_NO_LIBRARY)
    }

    // This approach supports large files.
    LARGE_INTEGER currentFilePos;
    LARGE_INTEGER offset;
    offset.LowPart = 0;
    offset.HighPart = 0;
    if (!ptrSetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_CURRENT)) {
        thatQ->setError(QFile::UnspecifiedError, qt_error_string());
        return 0;
    }

    return qint64(currentFilePos.QuadPart);
#endif
}

/*
    \internal
*/
bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
{
    Q_Q(const QFSFileEngine);
    QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);

    if (fh || fd != -1) {
        // stdlib / stdio mode.
        return seekFdFh(pos);
    }

#if !defined(QT_NO_LIBRARY)
    QFSFileEnginePrivate::resolveLibs();
    if (!ptrSetFilePointerEx) {
#endif
        LONG seekToPos = LONG(pos); // <- lossy
        DWORD newFilePointer = SetFilePointer(fileHandle, seekToPos, nullptr, FILE_BEGIN);
        if (newFilePointer == 0xFFFFFFFF) {
            thatQ->setError(QFile::UnspecifiedError, qt_error_string());
            return false;
        }

        // Note: does not work with large files. This is the case for MOC,
        // UIC, qmake and other bootstrapped tools, and for Win9x/ME.
        return true;
#if !defined(QT_NO_LIBRARY)
    }

    // This approach supports large files.
    LARGE_INTEGER currentFilePos;
    LARGE_INTEGER offset;
    offset.LowPart = (unsigned int)(quint64(pos) & Q_UINT64_C(0xffffffff));
    offset.HighPart = (unsigned int)((quint64(pos) >> 32) & Q_UINT64_C(0xffffffff));
    if (ptrSetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_BEGIN) == 0) {
        thatQ->setError(QFile::UnspecifiedError, qt_error_string());
        return false;
    }
    return true;
#endif
}

/*
    \internal
*/
qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 maxlen)
{
    Q_Q(QFSFileEngine);

    if (fh || fd != -1) {
        // stdio / stdlib mode.
        if (fh && nativeIsSequential() && feof(fh)) {
            q->setError(QFile::ReadError, qt_error_string(int(errno)));
            return -1;
        }

        return readFdFh(data, maxlen);
    }

    // Windows native mode.
    if (fileHandle == INVALID_HANDLE_VALUE)
        return -1;

    DWORD bytesToRead = DWORD(maxlen); // <- lossy

    // Reading on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
    // the chunks are too large, so we limit the block size to 32MB.
    static const DWORD maxBlockSize = 32 * 1024 * 1024;

    qint64 totalRead = 0;
    do {
        DWORD blockSize = qMin<DWORD>(bytesToRead, maxBlockSize);
        DWORD bytesRead;
        if (!ReadFile(fileHandle, data + totalRead, blockSize, &bytesRead, nullptr)) {
            if (totalRead == 0) {
                // Note: only return failure if the first ReadFile fails.
                q->setError(QFile::ReadError, qt_error_string());
                return -1;
            }
            break;
        }
        if (bytesRead == 0)
            break;
        totalRead += bytesRead;
        bytesToRead -= bytesRead;
    } while (totalRead < maxlen);
    return qint64(totalRead);
}

/*
    \internal
*/
qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
{
    Q_Q(QFSFileEngine);

    if (fh || fd != -1) {
        // stdio / stdlib mode.
        return readLineFdFh(data, maxlen);
    }

    // Windows native mode.
    if (fileHandle == INVALID_HANDLE_VALUE)
        return -1;

    // ### No equivalent in Win32?
    return q->QAbstractFileEngine::readLine(data, maxlen);
}

/*
    \internal
*/
qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
{
    Q_Q(QFSFileEngine);

    if (fh || fd != -1) {
        // stdio / stdlib mode.
        return writeFdFh(data, len);
    }

    // Windows native mode.
    if (fileHandle == INVALID_HANDLE_VALUE)
        return -1;

    qint64 bytesToWrite = DWORD(len); // <- lossy

    // Writing on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
    // the chunks are too large, so we limit the block size to 32MB.
    static const DWORD maxBlockSize = 32 * 1024 * 1024;

    qint64 totalWritten = 0;
    do {
        DWORD blockSize = qMin<DWORD>(bytesToWrite, maxBlockSize);
        DWORD bytesWritten;
        if (!WriteFile(fileHandle, data + totalWritten, blockSize, &bytesWritten, nullptr)) {
            if (totalWritten == 0) {
                // Note: Only return error if the first WriteFile failed.
                q->setError(QFile::WriteError, qt_error_string());
                return -1;
            }
            break;
        }
        if (bytesWritten == 0)
            break;
        totalWritten += bytesWritten;
        bytesToWrite -= bytesWritten;
    } while (totalWritten < len);
    return qint64(totalWritten);
}

/*
    \internal
*/
int QFSFileEnginePrivate::nativeHandle() const
{
    if (fh || fd != -1)
        return fh ? QT_FILENO(fh) : fd;
#ifndef Q_OS_WINCE
    if (cachedFd != -1)
        return cachedFd;

    int flags = 0;
    if (openMode & QIODevice::Append)
        flags |= _O_APPEND;
    if (!(openMode & QIODevice::WriteOnly))
        flags |= _O_RDONLY;
    cachedFd = _open_osfhandle((intptr_t) fileHandle, flags);
    return cachedFd;
#else
    return -1;
#endif
}

/*
    \internal
*/
bool QFSFileEnginePrivate::nativeIsSequential() const
{
#if !defined(Q_OS_WINCE)
    // stdlib / Windows native mode.
    if (fh || fileHandle != INVALID_HANDLE_VALUE) {
        if (fh == stdin || fh == stdout || fh == stderr)
            return true;

        HANDLE handle = fileHandle;
        if (fileHandle == INVALID_HANDLE_VALUE) {
            // Rare case: using QFile::open(FILE*) to open a pipe.
            handle = (HANDLE)_get_osfhandle(QT_FILENO(fh));
            return false;
        }

        DWORD fileType = GetFileType(handle);
        return fileType == FILE_TYPE_PIPE;
    }

    // stdio mode.
    if (fd != -1)
        return isSequentialFdFh();
#endif
    return false;
}

/*!
    \reimp
*/
bool QFSFileEngine::remove()
{
    Q_D(QFSFileEngine);
    QT_WA({
        return ::DeleteFileW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16()) != 0;
    } , {
        return ::DeleteFileA(QFSFileEnginePrivate::win95Name(d->filePath)) != 0;
    });
}

/*!
    \reimp
*/
bool QFSFileEngine::copy(const QString &copyName)
{
    Q_D(QFSFileEngine);
    QT_WA({
        return ::CopyFileW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(),
                           (TCHAR*)QFSFileEnginePrivate::longFileName(copyName).utf16(), true) != 0;
    } , {
        return ::CopyFileA(QFSFileEnginePrivate::win95Name(d->filePath),
                           QFSFileEnginePrivate::win95Name(copyName), true) != 0;
    });
}

/*!
    \reimp
*/
bool QFSFileEngine::rename(const QString &newName)
{
    Q_D(QFSFileEngine);
    QT_WA({
        return ::MoveFileW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(),
                           (TCHAR*)QFSFileEnginePrivate::longFileName(newName).utf16()) != 0;
    } , {
        return ::MoveFileA(QFSFileEnginePrivate::win95Name(d->filePath),
                           QFSFileEnginePrivate::win95Name(newName)) != 0;
    });
}

static inline bool mkDir(const QString &path)
{
#if defined(Q_OS_WINCE)
    // Unfortunately CreateDirectory returns true for paths longer than
    // 256, but does not create a directory. It starts to fail, when
    // path length > MAX_PATH, which is 260 usually on CE.
    // This only happens on a Windows Mobile device. Windows CE seems
    // not to be affected by this.
    static int platformId = 0;
    if (platformId == 0) {
        wchar_t platformString[64];
        if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(platformString)/sizeof(*platformString),platformString,0)) {
            if (0 == wcscmp(platformString, L"PocketPC") || 0 == wcscmp(platformString, L"Smartphone"))
                platformId = 1;
            else
                platformId = 2;
        }
    }
    if (platformId == 1 && QFSFileEnginePrivate::longFileName(path).size() > 256)
        return false;
#endif
    QT_WA({
        return ::CreateDirectoryW((TCHAR*)QFSFileEnginePrivate::longFileName(path).utf16(), 0);
    } , {
        return ::CreateDirectoryA(QFSFileEnginePrivate::win95Name(QFileInfo(path).absoluteFilePath()), 0);
    });
}

/*!
    \reimp
*/
static inline bool rmDir(const QString &path)
{
    QT_WA({
        return ::RemoveDirectoryW((TCHAR*)QFSFileEnginePrivate::longFileName(path).utf16());
    } , {
        return ::RemoveDirectoryA(QFSFileEnginePrivate::win95Name(QFileInfo(path).absoluteFilePath()));
    });
}

/*!
    \reimp
*/
static inline bool isDirPath(const QString &dirPath, bool *existed)
{
    QString path = dirPath;
    if (path.length() == 2 &&path.at(1) == QLatin1Char(':'))
        path += QLatin1Char('\\');

    DWORD fileAttrib = INVALID_FILE_ATTRIBUTES;
    QT_WA({
        fileAttrib = ::GetFileAttributesW((TCHAR*)QFSFileEnginePrivate::longFileName(path).utf16());
    } , {
        fileAttrib = ::GetFileAttributesA(QFSFileEnginePrivate::win95Name(QFileInfo(path).absoluteFilePath()));
    });

    if (existed)
        *existed = fileAttrib != INVALID_FILE_ATTRIBUTES;

    if (fileAttrib == INVALID_FILE_ATTRIBUTES)
        return false;

    return fileAttrib & FILE_ATTRIBUTE_DIRECTORY;
}

/*!
    \reimp
*/
bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
{
    QString dirName = name;
    if (createParentDirectories) {
        dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
        // We specifically search for / so \ would break it..
        int oldslash = -1;
        if (dirName.startsWith(QLatin1String("\\\\"))) {
            // Don't try to create the root path of a UNC path;
            // CreateDirectory() will just return ERROR_INVALID_NAME.
            for (int i = 0; i < dirName.size(); ++i) {
                if (dirName.at(i) != QDir::separator()) {
                    oldslash = i;
                    break;
                }
            }
            if (oldslash != -1)
                oldslash = dirName.indexOf(QDir::separator(), oldslash);
        }
        for (int slash=0; slash != -1; oldslash = slash) {
            slash = dirName.indexOf(QDir::separator(), oldslash+1);
            if (slash == -1) {
                if(oldslash == dirName.length())
                    break;
                slash = dirName.length();
            }
            if (slash) {
                QString chunk = dirName.left(slash);
                bool existed = false;
                if (!isDirPath(chunk, &existed) && !existed) {
                    if (!mkDir(chunk))
                        return false;
                }
            }
        }
        return true;
    }
    return mkDir(name);
}

/*!
    \reimp
*/
bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
{
    QString dirName = name;
    if (recurseParentDirectories) {
        dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
        for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
            QString chunk = dirName.left(slash);
            if (chunk.length() == 2 && chunk.at(0).isLetter() && chunk.at(1) == QLatin1Char(':'))
                break;
            if (!isDirPath(chunk, 0))
                return false;
            if (!rmDir(chunk))
                return oldslash != 0;
            slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
        }
        return true;
    }
    return rmDir(name);
}

/*!
    \reimp
*/
bool QFSFileEngine::caseSensitive() const
{
    return false;
}

/*!
    Sets the current path (e.g., for QDir), to \a path. Returns true if the
    new path exists; otherwise this function does nothing, and returns false.

    \sa currentPath()
*/
bool QFSFileEngine::setCurrentPath(const QString &path)
{
    if (!QDir(path).exists())
        return false;

#if !defined(Q_OS_WINCE)
    int r;
    QT_WA({
        r = ::SetCurrentDirectoryW((WCHAR*)path.utf16());
    } , {
        r = ::SetCurrentDirectoryA(QFSFileEnginePrivate::win95Name(path));
    });
    return r != 0;
#else
	qfsPrivateCurrentDir = QFSFileEnginePrivate::longFileName(path);
	return true;
#endif
}

/*!
    Returns the canonicalized form of the current path used by the file
    engine for the drive specified by \a fileName.

    On Windows, each drive has its own current directory, so a different
    path is returned for file names that include different drive names
    (e.g. A: or C:).

    \sa setCurrentPath()
*/
QString QFSFileEngine::currentPath(const QString &fileName)
{
#if !defined(Q_OS_WINCE)
    QString ret;
    //if filename is a drive: then get the pwd of that drive
    if (fileName.length() >= 2 &&
        fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':')) {
        int drv = fileName.toUpper().at(0).toLatin1() - 'A' + 1;
        if (_getdrive() != drv) {
            QT_WA({
                TCHAR buf[PATH_MAX];
                ::_wgetdcwd(drv, buf, PATH_MAX);
                ret.setUtf16((ushort*)buf, uint(::wcslen(buf)));
            }, {
                char buf[PATH_MAX];
                ::_getdcwd(drv, buf, PATH_MAX);
                ret = QString::fromLatin1(buf);
            });
        }
    }
    if (ret.isEmpty()) {
        //just the pwd
        QT_WA({
            DWORD size = 0;
            WCHAR currentName[PATH_MAX];
            size = ::GetCurrentDirectoryW(PATH_MAX, currentName);
            if (size !=0) {
                if (size > PATH_MAX) {
                    WCHAR * newCurrentName = new WCHAR[size];
                    if (::GetCurrentDirectoryW(PATH_MAX, newCurrentName) != 0)
                        ret = QString::fromUtf16((ushort*)newCurrentName);
                    delete [] newCurrentName;
                } else {
                    ret = QString::fromUtf16((ushort*)currentName);
                }
            }
        } , {
            DWORD size = 0;
            char currentName[PATH_MAX];
            size = ::GetCurrentDirectoryA(PATH_MAX, currentName);
            if (size !=0)
                ret = QString::fromLocal8Bit(currentName);
        });
    }
    if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
        ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
    return QDir::fromNativeSeparators(ret);
#else
	Q_UNUSED(fileName);
	if (qfsPrivateCurrentDir.isEmpty())
		qfsPrivateCurrentDir = QCoreApplication::applicationDirPath();

    return QDir::fromNativeSeparators(qfsPrivateCurrentDir);
#endif
}

/*!
    Returns the home path of the current user.

    \sa rootPath()
*/
QString QFSFileEngine::homePath()
{
    QString ret;
#if !defined(QT_NO_LIBRARY)
    QT_WA (
    {
        QFSFileEnginePrivate::resolveLibs();
		if (ptrOpenProcessToken && ptrGetUserProfileDirectoryW) {
			HANDLE hnd = ::GetCurrentProcess();
			HANDLE token = 0;
			BOOL ok = ::ptrOpenProcessToken(hnd, TOKEN_QUERY, &token);
			if (ok) {
				DWORD dwBufferSize = 0;
				// First call, to determine size of the strings (with '\0').
				ok = ::ptrGetUserProfileDirectoryW(token, nullptr, &dwBufferSize);
				if (!ok && dwBufferSize != 0) {		// We got the required buffer size
					wchar_t *userDirectory = new wchar_t[dwBufferSize];
					// Second call, now we can fill the allocated buffer.
					ok = ::ptrGetUserProfileDirectoryW(token, userDirectory, &dwBufferSize);
					if (ok)
						ret = QString::fromUtf16((ushort*)userDirectory);

					delete [] userDirectory;
				}
				::CloseHandle(token);
			}
		}
    }
    ,
    {
        // GetUserProfileDirectory is only available from NT 4.0,
		// so fall through for Win98 and friends version.
    })
#endif
    if(ret.isEmpty() || !QFile::exists(ret)) {
        ret = QString::fromLocal8Bit(qgetenv("USERPROFILE").constData());
        if(ret.isEmpty() || !QFile::exists(ret)) {
            ret = QString::fromLocal8Bit(qgetenv("HOMEDRIVE").constData()) + QString::fromLocal8Bit(qgetenv("HOMEPATH").constData());
            if(ret.isEmpty() || !QFile::exists(ret)) {
                ret = QString::fromLocal8Bit(qgetenv("HOME").constData());
                if(ret.isEmpty() || !QFile::exists(ret)) {
#if defined(Q_OS_WINCE)
                    ret = QString::fromLatin1("\\My Documents");
                    if (!QFile::exists(ret))
#endif
                    ret = rootPath();
                }
            }
        }
    }
    return QDir::fromNativeSeparators(ret);
}

/*!
    Returns the root path.

    \sa homePath()
*/
QString QFSFileEngine::rootPath()
{
#if defined(Q_OS_WINCE)
    QString ret = QString::fromLatin1("/");
#elif defined(Q_FS_FAT)
    QString ret = QString::fromLatin1(qgetenv("SystemDrive").constData());
    if(ret.isEmpty())
        ret = QLatin1String("c:");
    ret += QLatin1String("/");
#elif defined(Q_OS_OS2EMX)
    char dir[4];
    _abspath(dir, QLatin1String("/"), _MAX_PATH);
    QString ret(dir);
#endif
    return ret;
}

/*!
    Returns the temporary path (i.e., a path in which it is safe to store
    temporary files).
*/
QString QFSFileEngine::tempPath()
{
    QString ret;
    int success;
    QT_WA({
        wchar_t tempPath[MAX_PATH];
        success = GetTempPathW(MAX_PATH, tempPath);
        ret = QString::fromUtf16((ushort*)tempPath);
    } , {
        char tempPath[MAX_PATH];
        success = GetTempPathA(MAX_PATH, tempPath);
        ret = QString::fromLocal8Bit(tempPath);
    });
    if (ret.isEmpty() || !success) {
#if !defined(Q_OS_WINCE)
        ret = QString::fromLatin1("c:/tmp");
#else
        ret = QString::fromLatin1("\\Temp");
#endif
    } else {
        ret = QDir::fromNativeSeparators(ret);
        while (ret.at(ret.length()-1) == QLatin1Char('/'))
            ret = ret.left(ret.length()-1);
    }
    return ret;
}

/*!
    Returns the list of drives in the file system as a list of QFileInfo
    objects. On unix, Mac OS X and Windows CE, only the root path is returned.
    On Windows, this function returns all drives (A:\, C:\, D:\, etc.).
*/
QFileInfoList QFSFileEngine::drives()
{
    QFileInfoList ret;
#if !defined(Q_OS_WINCE)
#if defined(Q_OS_WIN32)
    quint32 driveBits = (quint32) GetLogicalDrives() & 0x3ffffff;
#elif defined(Q_OS_OS2EMX)
    quint32 driveBits, cur;
    if(DosQueryCurrentDisk(&cur,&driveBits) != NO_ERROR)
	exit(1);
    driveBits &= 0x3ffffff;
#endif
    char driveName[4];

    qstrcpy(driveName, "A:/");

    while(driveBits) {
	if(driveBits & 1)
	    ret.append(QString::fromLatin1(driveName).toUpper());
	driveName[0]++;
	driveBits = driveBits >> 1;
    }
    return ret;
#else
    ret.append(QString::fromLatin1("/").toUpper());
    return ret;
#endif
}

bool QFSFileEnginePrivate::doStat() const
{
    if (!tried_stat) {
        tried_stat = true;
        could_stat = false;

        if (filePath.isEmpty())
            return could_stat;
        QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath;
        fname = fixIfRelativeUncPath(fname);

        UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);

        if (fd != -1) {
#if !defined(Q_OS_WINCE)
            HANDLE fh = (HANDLE)_get_osfhandle(fd);
            if (fh != INVALID_HANDLE_VALUE) {
                BY_HANDLE_FILE_INFORMATION fileInfo;
                if (GetFileInformationByHandle(fh, &fileInfo)) {
                    could_stat = true;
                    fileAttrib = fileInfo.dwFileAttributes;
                }
            }
#else
            DWORD tmpAttributes = GetFileAttributesW((TCHAR*)QFSFileEnginePrivate::longFileName(fname).utf16());
            if (tmpAttributes != -1) {
                fileAttrib = tmpAttributes;
                could_stat = true;
            } else {
                return false;
            }
#endif
        } else {
            QT_WA({
                fileAttrib = GetFileAttributesW((TCHAR*)QFSFileEnginePrivate::longFileName(fname).utf16());
            } , {
                fileAttrib = GetFileAttributesA(QFSFileEnginePrivate::win95Name(QFileInfo(fname).absoluteFilePath()));
            });
            could_stat = fileAttrib != INVALID_FILE_ATTRIBUTES;
            if (!could_stat) {
#if !defined(Q_OS_WINCE)
                if (!fname.isEmpty() && fname.at(0).isLetter() && fname.mid(1, fname.length()) == QLatin1String(":/")) {
                    // an empty drive ??
                    DWORD drivesBitmask = ::GetLogicalDrives();
                    int drivebit = 1 << (fname.at(0).toUpper().unicode() - QLatin1Char('A').unicode());
                    if (drivesBitmask & drivebit) {
                        fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM;
                        could_stat = true;
                    }
                } else {
#endif
                    QString path = QDir::toNativeSeparators(fname);
                    bool is_dir = false;
                    if (path.startsWith(QLatin1String("\\\\"))) {
                        // UNC - stat doesn't work for all cases (Windows bug)
                        int s = path.indexOf(path.at(0),2);
                        if (s > 0) {
                            // "\\server\..."
                            s = path.indexOf(path.at(0),s+1);
                            if (s > 0) {
                                // "\\server\share\..."
                                if (s == path.size() - 1) {
                                    // "\\server\share\"
                                    is_dir = true;
                                } else {
                                    // "\\server\share\notfound"
                                }
                            } else {
                                // "\\server\share"
                                is_dir = true;
                            }
                        } else {
                            // "\\server"
                            is_dir = true;
                        }
                    }
                    if (is_dir && uncShareExists(path)) {
                        // looks like a UNC dir, is a dir.
                        fileAttrib = FILE_ATTRIBUTE_DIRECTORY;
                        could_stat = true;
                    }
#if !defined(Q_OS_WINCE)
                }
#endif
            }
        }
        SetErrorMode(oldmode);
    }
    return could_stat;
}


static QString readLink(const QString &link)
{
#if !defined(Q_OS_WINCE)
#if !defined(QT_NO_LIBRARY)
    QString ret;
    QT_WA({
        bool neededCoInit = false;
        IShellLink *psl;                            // pointer to IShellLink i/f
        HRESULT hres;
        WIN32_FIND_DATA wfd;
        TCHAR szGotPath[MAX_PATH];
        // Get pointer to the IShellLink interface.
        hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
                                    IID_IShellLink, (LPVOID *)&psl);

        if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
            neededCoInit = true;
            CoInitialize(nullptr);
            hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
                                        IID_IShellLink, (LPVOID *)&psl);
        }
        if(SUCCEEDED(hres)) {    // Get pointer to the IPersistFile interface.
            IPersistFile *ppf;
            hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
            if(SUCCEEDED(hres))  {
                hres = ppf->Load((LPOLESTR)link.utf16(), STGM_READ);
                //The original path of the link is retrieved. If the file/folder
                //was moved, the return value still have the old path.
                if(SUCCEEDED(hres)) {
                    if (psl->GetPath(szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR)
                        ret = QString::fromUtf16((ushort*)szGotPath);
                }
                ppf->Release();
            }
            psl->Release();
        }
        if(neededCoInit)
            CoUninitialize();
    } , {
	    bool neededCoInit = false;
        IShellLinkA *psl;                            // pointer to IShellLink i/f
        HRESULT hres;
        WIN32_FIND_DATAA wfd;
        char szGotPath[MAX_PATH];
        // Get pointer to the IShellLink interface.

        hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
                                    IID_IShellLinkA, (LPVOID *)&psl);

        if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
            neededCoInit = true;
            CoInitialize(nullptr);
            hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
                                        IID_IShellLinkA, (LPVOID *)&psl);
        }
        if(SUCCEEDED(hres)) {    // Get pointer to the IPersistFile interface.
            IPersistFile *ppf;
            hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
            if(SUCCEEDED(hres))  {
                hres = ppf->Load((LPOLESTR)QFileInfo(link).absoluteFilePath().utf16(), STGM_READ);
                //The original path of the link is retrieved. If the file/folder
                //was moved, the return value still have the old path.
                 if(SUCCEEDED(hres)) {
                    if (psl->GetPath((char*)szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR)
                        ret = QString::fromLocal8Bit(szGotPath);
                }
                ppf->Release();
            }
            psl->Release();
        }
        if(neededCoInit)
            CoUninitialize();
    });
    return ret;
#else
    Q_UNUSED(link);
    return QString();
#endif // QT_NO_LIBRARY
#else
    wchar_t target[MAX_PATH];
    QString result;
    if (SHGetShortcutTarget((wchar_t*)QFileInfo(link).absoluteFilePath().replace(QLatin1Char('/'),QLatin1Char('\\')).utf16(), target, MAX_PATH)) {
        result = QString::fromUtf16(reinterpret_cast<const ushort *> (target));
        if (result.startsWith(QLatin1Char('"')))
            result.remove(0,1);
        if (result.endsWith(QLatin1Char('"')))
            result.remove(result.size()-1,1);
    }
    return result;
#endif // Q_OS_WINCE
}

/*!
    \internal
*/
QString QFSFileEnginePrivate::getLink() const
{
    return readLink(filePath);
}

/*!
    \reimp
*/
bool QFSFileEngine::link(const QString &newName)
{
#if !defined(Q_OS_WINCE)
#if !defined(QT_NO_LIBRARY)
    bool ret = false;

    QString linkName = newName;
    //### assume that they add .lnk

    QT_WA({
        HRESULT hres;
        IShellLink *psl;
        bool neededCoInit = false;

        hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
        if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
                neededCoInit = true;
                CoInitialize(nullptr);
                hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
        }
        if (SUCCEEDED(hres)) {
            hres = psl->SetPath((wchar_t *)fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\')).utf16());
            if (SUCCEEDED(hres)) {
                hres = psl->SetWorkingDirectory((wchar_t *)fileName(AbsolutePathName).replace(QLatin1Char('/'), QLatin1Char('\\')).utf16());
                if (SUCCEEDED(hres)) {
                    IPersistFile *ppf;
                    hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
                    if (SUCCEEDED(hres)) {
                        hres = ppf->Save((TCHAR*)linkName.utf16(), TRUE);
                        if (SUCCEEDED(hres))
                             ret = true;
                        ppf->Release();
                    }
                }
            }
            psl->Release();
        }
        if(neededCoInit)
                CoUninitialize();
    } , {
        // the SetPath() call _sometimes_ changes the current path and when it does it sometimes
        // does not let us change it back unless we call currentPath() many times.
        QString cwd = currentPath();
        HRESULT hres;
        IShellLinkA *psl;
        bool neededCoInit = false;

        hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
        if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
            neededCoInit = true;
            CoInitialize(nullptr);
            hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
        }
        if (SUCCEEDED(hres)) {
            currentPath();
            hres = psl->SetPath((char *)QString::fromLocal8Bit(QFSFileEnginePrivate::win95Name(fileName(AbsoluteName))).utf16());
            currentPath();
            if (SUCCEEDED(hres)) {
                hres = psl->SetWorkingDirectory((char *)QString::fromLocal8Bit(QFSFileEnginePrivate::win95Name(fileName(AbsolutePathName))).utf16());
                currentPath();
                if (SUCCEEDED(hres)) {
                    IPersistFile *ppf;
                    hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
                    if (SUCCEEDED(hres)) {
                        currentPath();
                        hres = ppf->Save((LPCOLESTR)linkName.utf16(), TRUE);
                        currentPath();
                        if (SUCCEEDED(hres))
                            ret = true;
                        ppf->Release();
                    }
                }
                psl->Release();
            }
        }
        if(neededCoInit)
            CoUninitialize();
        setCurrentPath(cwd);
    });
    return ret;
#else
    Q_UNUSED(newName);
    return false;
#endif // QT_NO_LIBRARY
#else
    QString linkName = newName;
    if (!linkName.endsWith(QLatin1String(".lnk")))
        linkName += QLatin1String(".lnk");
    QString orgName = fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\'));
    // Need to append on our own
    orgName.prepend(QLatin1Char('"'));
    orgName.append(QLatin1Char('"'));
    return SUCCEEDED(SHCreateShortcut((wchar_t*)linkName.utf16(), (wchar_t*)orgName.utf16()));
#endif // Q_OS_WINCE
}

/*!
    \internal
*/
QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions() const
{
    QAbstractFileEngine::FileFlags ret = 0;

#if !defined(QT_NO_LIBRARY)
    if((qt_ntfs_permission_lookup > 0) && ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_NT)) {
	PSID pOwner = 0;
	PSID pGroup = 0;
	PACL pDacl;
        PSECURITY_DESCRIPTOR pSD;
        ACCESS_MASK access_mask;

        enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 };
        resolveLibs();
        if(ptrGetNamedSecurityInfoW && ptrAllocateAndInitializeSid && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW && ptrFreeSid) {

            QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath;
            DWORD res = ptrGetNamedSecurityInfoW((wchar_t*)fname.utf16(), SE_FILE_OBJECT,
						 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
						 &pOwner, &pGroup, &pDacl, 0, &pSD);

            if(res == ERROR_SUCCESS) {
                TRUSTEE_W trustee;
                { //user
                    if(ptrGetEffectiveRightsFromAclW(pDacl, &currentUserTrusteeW, &access_mask) != ERROR_SUCCESS)
                        access_mask = (ACCESS_MASK)-1;
		    if(access_mask & ReadMask)
			ret |= QAbstractFileEngine::ReadUserPerm;
		    if(access_mask & WriteMask)
			ret |= QAbstractFileEngine::WriteUserPerm;
		    if(access_mask & ExecMask)
			ret |= QAbstractFileEngine::ExeUserPerm;
                }
                { //owner
                    ptrBuildTrusteeWithSidW(&trustee, pOwner);
                    if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
                        access_mask = (ACCESS_MASK)-1;
		    if(access_mask & ReadMask)
			ret |= QAbstractFileEngine::ReadOwnerPerm;
		    if(access_mask & WriteMask)
			ret |= QAbstractFileEngine::WriteOwnerPerm;
		    if(access_mask & ExecMask)
			ret |= QAbstractFileEngine::ExeOwnerPerm;
                }
                { //group
                    ptrBuildTrusteeWithSidW(&trustee, pGroup);
                    if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
                        access_mask = (ACCESS_MASK)-1;
		    if(access_mask & ReadMask)
			ret |= QAbstractFileEngine::ReadGroupPerm;
		    if(access_mask & WriteMask)
			ret |= QAbstractFileEngine::WriteGroupPerm;
		    if(access_mask & ExecMask)
			ret |= QAbstractFileEngine::ExeGroupPerm;
                }
                { //other (world)
                    // Create SID for Everyone (World)
                    SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY };
                    PSID pWorld = 0;
                    if(ptrAllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0,0,0,0,0,0,0, &pWorld)) {
                        ptrBuildTrusteeWithSidW(&trustee, pWorld);
                        if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
                            access_mask = (ACCESS_MASK)-1; // ###
			if(access_mask & ReadMask)
			    ret |= QAbstractFileEngine::ReadOtherPerm;
			if(access_mask & WriteMask)
			    ret |= QAbstractFileEngine::WriteOtherPerm;
			if(access_mask & ExecMask)
			    ret |= QAbstractFileEngine::ExeOtherPerm;
                    }
                    ptrFreeSid(pWorld);
                }
                LocalFree(pSD);
            }
        }
    } else
#endif
           {
	//### what to do with permissions if we don't use ntfs or are not on a NT system
	// for now just add all permissions and what about exe missions ??
	// also qt_ntfs_permission_lookup is now not set by default ... should it ?
    	ret |= QAbstractFileEngine::ReadOtherPerm | QAbstractFileEngine::ReadGroupPerm
	    | QAbstractFileEngine::ReadOwnerPerm | QAbstractFileEngine::ReadUserPerm
	    | QAbstractFileEngine::WriteUserPerm | QAbstractFileEngine::WriteOwnerPerm
	    | QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm;
    }

    if (doStat()) {
        if (ret & (QAbstractFileEngine::WriteOwnerPerm | QAbstractFileEngine::WriteUserPerm |
            QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm)) {
            if (fileAttrib & FILE_ATTRIBUTE_READONLY)
                ret &= ~(QAbstractFileEngine::WriteOwnerPerm | QAbstractFileEngine::WriteUserPerm |
                QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm);
        }

        QString ext = filePath.right(4).toLower();
        if (ext == QLatin1String(".exe") || ext == QLatin1String(".com") || ext == QLatin1String(".bat") ||
            ext == QLatin1String(".pif") || ext == QLatin1String(".cmd") || (fileAttrib & FILE_ATTRIBUTE_DIRECTORY))
            ret |= QAbstractFileEngine::ExeOwnerPerm | QAbstractFileEngine::ExeGroupPerm |
            QAbstractFileEngine::ExeOtherPerm | QAbstractFileEngine::ExeUserPerm;
    }
    return ret;
}

/*!
    \reimp
*/
QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
{
    Q_D(const QFSFileEngine);
    QAbstractFileEngine::FileFlags ret = 0;
    // Force a stat, so that we're guaranteed to get up-to-date results
    if (type & QAbstractFileEngine::FileFlag(QAbstractFileEngine::Refresh)) {
        d->tried_stat = 0;
    }

    if (type & PermsMask) {
        ret |= d->getPermissions();
        // ### Workaround pascals ### above. Since we always set all properties to true
        // we need to disable read and exec access if the file does not exists
        if (d->doStat())
            ret |= ExistsFlag;
        else
            ret &= 0x2222;
    }
    if (type & TypesMask) {
        if (d->filePath.endsWith(QLatin1String(".lnk"))) {
            ret |= LinkType;
            QString l = readLink(d->filePath);
            if (!l.isEmpty()) {
                if (isDirPath(l, 0))
                    ret |= DirectoryType;
                else
                    ret |= FileType;
            }
        } else if (d->doStat()) {
            if (d->fileAttrib & FILE_ATTRIBUTE_DIRECTORY) {
                ret |= DirectoryType;
            } else {
                ret |= FileType;
            }
        }
    }
    if (type & FlagsMask) {
        if(d->doStat()) {
            ret |= QAbstractFileEngine::FileFlags(ExistsFlag | LocalDiskFlag);
            if (d->fileAttrib & FILE_ATTRIBUTE_HIDDEN)
                ret |= HiddenFlag;
            if (d->filePath == QLatin1String("/") || (d->filePath.at(0).isLetter() && d->filePath.mid(1,d->filePath.length()) == QLatin1String(":/"))
                || isUncRoot(d->filePath)) {
                ret |= RootFlag;
                ret &= ~HiddenFlag;
            }
        }
    }
    return ret;
}

/*!
    \reimp
*/
QString QFSFileEngine::fileName(FileName file) const
{
    Q_D(const QFSFileEngine);
    if(file == BaseName) {
        int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
        if(slash == -1) {
            int colon = d->filePath.lastIndexOf(QLatin1Char(':'));
            if(colon != -1)
                return d->filePath.mid(colon + 1);
            return d->filePath;
        }
        return d->filePath.mid(slash + 1);
    } else if(file == PathName) {
        if(!d->filePath.size())
            return d->filePath;

        int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
        if(slash == -1) {
            if(d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':'))
                return d->filePath.left(2);
            return QString::fromLatin1(".");
        } else {
            if(!slash)
                return QString::fromLatin1("/");
            if(slash == 2 && d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':'))
                slash++;
            return d->filePath.left(slash);
        }
    } else if(file == AbsoluteName || file == AbsolutePathName) {
        QString ret;

        if (!isRelativePath()) {
#if !defined(Q_OS_WINCE)
            if (d->filePath.size() > 2 && d->filePath.at(1) == QLatin1Char(':')
                && d->filePath.at(2) != QLatin1Char('/') || // It's a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt
                d->filePath.startsWith(QLatin1Char('/'))    // It's a absolute path to the current drive, so \a.txt -> Z:\a.txt
                ) {
                ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(d->filePath));
            } else {
                ret = d->filePath;
            }
#else
                ret = d->filePath;
#endif
        } else {
            ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + d->filePath);
        }

        // The path should be absolute at this point.
        // From the docs :
        // Absolute paths begin with the directory separator "/"
        // (optionally preceded by a drive specification under Windows).
        if (ret.at(0) != QLatin1Char('/')) {
            Q_ASSERT(ret.length() >= 2);
            Q_ASSERT(ret.at(0).isLetter());
            Q_ASSERT(ret.at(1) == QLatin1Char(':'));

            // Force uppercase drive letters.
            ret[0] = ret.at(0).toUpper();
        }

        if (file == AbsolutePathName) {
            int slash = ret.lastIndexOf(QLatin1Char('/'));
            if (slash < 0)
                return ret;
            else if (ret.at(0) != QLatin1Char('/') && slash == 2)
                return ret.left(3);      // include the slash
            else
                return ret.left(slash > 0 ? slash : 1);
        }
        return ret;
    } else if(file == CanonicalName || file == CanonicalPathName) {
        if (!(fileFlags(ExistsFlag) & ExistsFlag))
            return QString();

        QString ret = QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName));
        if (!ret.isEmpty() && file == CanonicalPathName) {
            int slash = ret.lastIndexOf(QLatin1Char('/'));
            if (slash == -1)
                ret = QDir::currentPath();
            else if (slash == 0)
                ret = QLatin1String("/");
            ret = ret.left(slash);
        }
        return ret;
    } else if(file == LinkName) {
        return QDir::fromNativeSeparators(d->getLink());
    } else if(file == BundleName) {
        return QString();
    }
    return d->filePath;
}

/*!
    \reimp
*/
bool QFSFileEngine::isRelativePath() const
{
    Q_D(const QFSFileEngine);
    return !(d->filePath.startsWith(QLatin1Char('/'))
        || (d->filePath.length() >= 2
        && ((d->filePath.at(0).isLetter() && d->filePath.at(1) == QLatin1Char(':'))
        || (d->filePath.at(0) == QLatin1Char('/') && d->filePath.at(1) == QLatin1Char('/')))));                // drive, e.g. a:
}

/*!
    \reimp
*/
uint QFSFileEngine::ownerId(FileOwner /*own*/) const
{
    static const uint nobodyID = (uint) -2;
    return nobodyID;
}

/*!
    \reimp
*/
QString QFSFileEngine::owner(FileOwner own) const
{
#if !defined(QT_NO_LIBRARY)
    Q_D(const QFSFileEngine);
    if((qt_ntfs_permission_lookup > 0) && ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_NT)) {
	PSID pOwner = 0;
	PSECURITY_DESCRIPTOR pSD;
	QString name;
	QFSFileEnginePrivate::resolveLibs();

	if(ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) {
	    if(ptrGetNamedSecurityInfoW((wchar_t*)d->filePath.utf16(), SE_FILE_OBJECT,
					 own == OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION,
					 nullptr, &pOwner, nullptr, nullptr, &pSD) == ERROR_SUCCESS) {
		DWORD lowner = 0, ldomain = 0;
		SID_NAME_USE use;
		// First call, to determine size of the strings (with '\0').
		ptrLookupAccountSidW(nullptr, pOwner, nullptr, &lowner, nullptr, &ldomain, (SID_NAME_USE*)&use);
		wchar_t *owner = new wchar_t[lowner];
		wchar_t *domain = new wchar_t[ldomain];
		// Second call, size is without '\0'
		if(ptrLookupAccountSidW(nullptr, pOwner, (LPWSTR)owner, &lowner,
					 (LPWSTR)domain, &ldomain, (SID_NAME_USE*)&use)) {
		    name = QString::fromUtf16((ushort*)owner);
		}
		LocalFree(pSD);
		delete [] owner;
		delete [] domain;
	    }
	}
	return name;
    }
#else
    Q_UNUSED(own);
#endif
    return QString(QLatin1String(""));
}

/*!
    \reimp
*/
bool QFSFileEngine::setPermissions(uint perms)
{
    Q_D(QFSFileEngine);
    bool ret = false;
    int mode = 0;

    if (perms & QFile::ReadOwner || perms & QFile::ReadUser || perms & QFile::ReadGroup || perms & QFile::ReadOther)
        mode |= _S_IREAD;
    if (perms & QFile::WriteOwner || perms & QFile::WriteUser || perms & QFile::WriteGroup || perms & QFile::WriteOther)
        mode |= _S_IWRITE;

    if (mode == 0) // not supported
        return false;

#if !defined(Q_OS_WINCE)
   QT_WA({
        ret = ::_wchmod((TCHAR*)d->filePath.utf16(), mode) == 0;
   } , {
        ret = ::_chmod(d->filePath.toLocal8Bit(), mode) == 0;
   });
#else
    ret = ::_wchmod((TCHAR*)d->longFileName(d->filePath).utf16(), mode);
#endif
   return ret;
}

/*!
    \reimp
*/
bool QFSFileEngine::setSize(qint64 size)
{
    Q_D(QFSFileEngine);

    if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1) {
        // resize open file
        HANDLE fh = d->fileHandle;
#if !defined(Q_OS_WINCE)
        if (fh == INVALID_HANDLE_VALUE)
            fh = (HANDLE)_get_osfhandle(d->fd);
#endif
        if (fh == INVALID_HANDLE_VALUE)
            return false;
        qint64 currentPos = pos();

        if (seek(size) && SetEndOfFile(fh)) {
            seek(qMin(currentPos, size));
            return true;
        }

        seek(currentPos);
        return false;
    }

    if (!d->nativeFilePath.isEmpty()) {
        // resize file on disk
        QFile file(d->filePath);
        if (file.open(QFile::ReadWrite)) {
            return file.resize(size);
        }
    }
    return false;
}


static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
{
    QDateTime ret;
    if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based || QSysInfo::WindowsVersion & QSysInfo::WV_CE_based) {
        // SystemTimeToTzSpecificLocalTime is not available on Win98/ME so we have to pull it off ourselves.
        SYSTEMTIME systime;
        FILETIME ftime;
        systime.wYear = 1970;
        systime.wMonth = 1;
        systime.wDay = 1;
        systime.wHour = 0;
        systime.wMinute = 0;
        systime.wSecond = 0;
        systime.wMilliseconds = 0;
        systime.wDayOfWeek = 4;
        SystemTimeToFileTime(&systime, &ftime);
        unsigned __int64 acttime = (unsigned __int64)time->dwHighDateTime << 32 | time->dwLowDateTime;
        FileTimeToSystemTime(time, &systime);
        unsigned __int64 time1970 = (unsigned __int64)ftime.dwHighDateTime << 32 | ftime.dwLowDateTime;
        unsigned __int64 difftime = acttime - time1970;
        difftime /= 10000000;
        ret.setTime_t((unsigned int)difftime);
    } else {
#ifndef Q_OS_WINCE
        SYSTEMTIME sTime, lTime;
        FileTimeToSystemTime(time, &sTime);
        SystemTimeToTzSpecificLocalTime(0, &sTime, &lTime);
        ret.setDate(QDate(lTime.wYear, lTime.wMonth, lTime.wDay));
        ret.setTime(QTime(lTime.wHour, lTime.wMinute, lTime.wSecond, lTime.wMilliseconds));
#endif
    }
    return ret;
}

/*!
    \reimp
*/
QDateTime QFSFileEngine::fileTime(FileTime time) const
{
    Q_D(const QFSFileEngine);
    QDateTime ret;
    if (d->fd != -1) {
#if !defined(Q_OS_WINCE)
        HANDLE fh = (HANDLE)_get_osfhandle(d->fd);
        if (fh != INVALID_HANDLE_VALUE) {
            FILETIME creationTime, lastAccessTime, lastWriteTime;
            if (GetFileTime(fh, &creationTime, &lastAccessTime, &lastWriteTime)) {
                if(time == CreationTime)
                    ret = fileTimeToQDateTime(&creationTime);
                else if(time == ModificationTime)
                    ret = fileTimeToQDateTime(&lastWriteTime);
                else if(time == AccessTime)
                    ret = fileTimeToQDateTime(&lastAccessTime);
            }
        }
#endif
    } else {
        bool ok = false;
        WIN32_FILE_ATTRIBUTE_DATA attribData;
        QT_WA({
            ok = ::GetFileAttributesExW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(), GetFileExInfoStandard, &attribData);
        } , {
            ok = ::GetFileAttributesExA(QFSFileEnginePrivate::win95Name(QFileInfo(d->filePath).absoluteFilePath()), GetFileExInfoStandard, &attribData);
        });
        if (ok) {
            if(time == CreationTime)
                ret = fileTimeToQDateTime(&attribData.ftCreationTime);
            else if(time == ModificationTime)
                ret = fileTimeToQDateTime(&attribData.ftLastWriteTime);
            else if(time == AccessTime)
                ret = fileTimeToQDateTime(&attribData.ftLastAccessTime);
        }
    }
    return ret;
}

uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
                                 QFile::MemoryMapFlags flags)
{
    Q_Q(QFSFileEngine);
    Q_UNUSED(flags);
    if (openMode == QFile::NotOpen) {
        q->setError(QFile::PermissionsError, qt_error_string());
	return 0;
    }
    if (offset == 0 && size == 0) {
        q->setError(QFile::UnspecifiedError, qt_error_string());
	return 0;
    }


    // get handle to the file
    HANDLE handle = fileHandle;
#ifndef Q_OS_WINCE
    if (handle == INVALID_HANDLE_VALUE && fh)
        handle = (HANDLE)_get_osfhandle(QT_FILENO(fh));
#else
    #ifdef Q_USE_DEPRECATED_MAP_API
    nativeClose();
    if (fileMapHandle == INVALID_HANDLE_VALUE) {
        fileMapHandle = CreateFileForMappingW((TCHAR *)nativeFilePath.constData(),
                GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0),
                0,
                nullptr,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL,
                nullptr);
    }
    handle = fileMapHandle;
    #endif
    if (handle == INVALID_HANDLE_VALUE && fh)
        return 0;
#endif

    // first create the file mapping handle
    HANDLE mapHandle = 0;
    DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY;
    QT_WA({
    mapHandle = ::CreateFileMappingW(handle, 0, protection,
             0, 0, 0);
    },{
    mapHandle = ::CreateFileMappingA(handle, 0, protection,
             0, 0, 0);
    });
    if (mapHandle == nullptr) {
        q->setError(QFile::PermissionsError, qt_error_string());
#ifdef Q_USE_DEPRECATED_MAP_API
        mapHandleClose();
#endif
	return 0;
    }

    // setup args to map
    DWORD access = 0;
    if (openMode & QIODevice::ReadOnly) access = FILE_MAP_READ;
    if (openMode & QIODevice::WriteOnly) access = FILE_MAP_WRITE;

    DWORD offsetHi = offset >> 32;
    DWORD offsetLo = offset & Q_UINT64_C(0xffffffff);
    SYSTEM_INFO sysinfo;
    ::GetSystemInfo(&sysinfo);
    int mask = sysinfo.dwAllocationGranularity - 1;
    int extra = offset & mask;
    if (extra)
        offsetLo &= ~mask;

    // attempt to create the map
    LPVOID mapAddress = MapViewOfFile(mapHandle, access,
                                      offsetHi, offsetLo, size + extra);
    if (mapAddress) {
        uchar *address = extra + static_cast<uchar*>(mapAddress);
        maps[address] = QPair<int, HANDLE>(extra, mapHandle);
        return address;
    }

    switch(GetLastError()) {
    case ERROR_ACCESS_DENIED:
        q->setError(QFile::PermissionsError, qt_error_string());
	break;
    case ERROR_INVALID_PARAMETER:
        // size are out of bounds
    default:
        q->setError(QFile::UnspecifiedError, qt_error_string());
    }
    CloseHandle(mapHandle);
#ifdef Q_USE_DEPRECATED_MAP_API
    mapHandleClose();
#endif
    return 0;
}

bool QFSFileEnginePrivate::unmap(uchar *ptr)
{
    Q_Q(QFSFileEngine);
    if (!maps.contains(ptr)) {
        q->setError(QFile::PermissionsError, qt_error_string());
        return false;
    }
    uchar *start = ptr - maps[ptr].first;
    if (!UnmapViewOfFile(start)) {
        q->setError(QFile::PermissionsError, qt_error_string());
        return false;
    }

    if (!CloseHandle((HANDLE)maps[ptr].second)) {
        q->setError(QFile::UnspecifiedError, qt_error_string());
        return false;
    }
    maps.remove(ptr);

#ifdef Q_USE_DEPRECATED_MAP_API
    mapHandleClose();
#endif
    return true;
}

#ifdef Q_USE_DEPRECATED_MAP_API
void QFSFileEnginePrivate::mapHandleClose()
{
    if (maps.isEmpty()) {
        CloseHandle(fileMapHandle);
        fileMapHandle = INVALID_HANDLE_VALUE;
    }
}
#endif
QT_END_NAMESPACE
