blob: d6188ee923c21113e0871f7e7ffa8638e6eec2e1 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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:
** http://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.1, 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:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#define _POSIX_
#include "qplatformdefs.h"
#include "qabstractfileengine.h"
#include "private/qfsfileengine_p.h"
#include <private/qsystemlibrary_p.h>
#include <qdebug.h>
#include "qfile.h"
#include "qdir.h"
#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>
# include <winioctl.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
#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
#if !defined(Q_OS_WINCE)
# if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union {
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct {
UCHAR DataBuffer[1];
} GenericReparseBuffer;
};
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
# define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
# endif // !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
# ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
# define MAXIMUM_REPARSE_DATA_BUFFER_SIZE 16384
# endif
# ifndef IO_REPARSE_TAG_SYMLINK
# define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
# endif
# ifndef FSCTL_GET_REPARSE_POINT
# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
# endif
#endif // !defined(Q_OS_WINCE)
QT_BEGIN_NAMESPACE
static QString readLink(const QString &link);
Q_CORE_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 BOOL (WINAPI *PtrLookupAccountSidW)(LPCWSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, PSID_NAME_USE);
static PtrLookupAccountSidW ptrLookupAccountSidW = 0;
typedef VOID (WINAPI *PtrBuildTrusteeWithSidW)(PTRUSTEE_W, PSID);
static PtrBuildTrusteeWithSidW ptrBuildTrusteeWithSidW = 0;
typedef DWORD (WINAPI *PtrGetEffectiveRightsFromAclW)(PACL, PTRUSTEE_W, OUT PACCESS_MASK);
static PtrGetEffectiveRightsFromAclW ptrGetEffectiveRightsFromAclW = 0;
static TRUSTEE_W currentUserTrusteeW;
static TRUSTEE_W worldTrusteeW;
typedef BOOL (WINAPI *PtrGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD);
static PtrGetUserProfileDirectoryW ptrGetUserProfileDirectoryW = 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)
HINSTANCE advapiHnd = QSystemLibrary::load(L"advapi32");
if (advapiHnd) {
ptrGetNamedSecurityInfoW = (PtrGetNamedSecurityInfoW)GetProcAddress(advapiHnd, "GetNamedSecurityInfoW");
ptrLookupAccountSidW = (PtrLookupAccountSidW)GetProcAddress(advapiHnd, "LookupAccountSidW");
ptrBuildTrusteeWithSidW = (PtrBuildTrusteeWithSidW)GetProcAddress(advapiHnd, "BuildTrusteeWithSidW");
ptrGetEffectiveRightsFromAclW = (PtrGetEffectiveRightsFromAclW)GetProcAddress(advapiHnd, "GetEffectiveRightsFromAclW");
}
if (ptrBuildTrusteeWithSidW) {
// Create TRUSTEE for current user
HANDLE hnd = ::GetCurrentProcess();
HANDLE token = 0;
if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) {
TOKEN_USER tu;
DWORD retsize;
if (::GetTokenInformation(token, TokenUser, &tu, sizeof(tu), &retsize))
ptrBuildTrusteeWithSidW(&currentUserTrusteeW, tu.User.Sid);
::CloseHandle(token);
}
typedef BOOL (WINAPI *PtrAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*);
PtrAllocateAndInitializeSid ptrAllocateAndInitializeSid = (PtrAllocateAndInitializeSid)GetProcAddress(advapiHnd, "AllocateAndInitializeSid");
typedef PVOID (WINAPI *PtrFreeSid)(PSID);
PtrFreeSid ptrFreeSid = (PtrFreeSid)GetProcAddress(advapiHnd, "FreeSid");
if (ptrAllocateAndInitializeSid && ptrFreeSid) {
// Create TRUSTEE 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(&worldTrusteeW, pWorld);
ptrFreeSid(pWorld);
}
}
HINSTANCE userenvHnd = QSystemLibrary::load(L"userenv");
if (userenvHnd)
ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW");
#endif
}
}
#endif // QT_NO_LIBRARY
typedef DWORD (WINAPI *PtrNetShareEnum)(LPWSTR, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD);
static PtrNetShareEnum ptrNetShareEnum = 0;
typedef DWORD (WINAPI *PtrNetApiBufferFree)(LPVOID);
static PtrNetApiBufferFree ptrNetApiBufferFree = 0;
typedef struct _SHARE_INFO_1 {
LPWSTR shi1_netname;
DWORD shi1_type;
LPWSTR shi1_remark;
} SHARE_INFO_1;
bool QFSFileEnginePrivate::resolveUNCLibs()
{
static bool triedResolve = false;
if (!triedResolve) {
#ifndef QT_NO_THREAD
QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
if (triedResolve) {
return ptrNetShareEnum && ptrNetApiBufferFree;
}
#endif
triedResolve = true;
#if !defined(Q_OS_WINCE)
HINSTANCE hLib = QSystemLibrary::load(L"Netapi32");
if (hLib) {
ptrNetShareEnum = (PtrNetShareEnum)GetProcAddress(hLib, "NetShareEnum");
if (ptrNetShareEnum)
ptrNetApiBufferFree = (PtrNetApiBufferFree)GetProcAddress(hLib, "NetApiBufferFree");
}
#endif
}
return ptrNetShareEnum && ptrNetApiBufferFree;
}
bool QFSFileEnginePrivate::uncListSharesOnServer(const QString &server, QStringList *list)
{
if (resolveUNCLibs()) {
SHARE_INFO_1 *BufPtr, *p;
DWORD res;
DWORD er = 0, tr = 0, resume = 0, i;
do {
res = ptrNetShareEnum((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::fromWCharArray(p->shi1_netname));
p++;
}
}
ptrNetApiBufferFree(BufPtr);
} while (res == ERROR_MORE_DATA);
return res == ERROR_SUCCESS;
}
return false;
}
static bool isUncRoot(const QString &server)
{
QString localPath = QDir::toNativeSeparators(server);
if (!localPath.startsWith(QLatin1String("\\\\")))
return false;
int idx = localPath.indexOf(QLatin1Char('\\'), 2);
if (idx == -1 || idx + 1 == localPath.length())
return true;
localPath = localPath.right(localPath.length() - idx - 1).trimmed();
return localPath.isEmpty();
}
#if !defined(Q_OS_WINCE)
static inline bool isUncPath(const QString &path)
{
// Starts with \\, but not \\.
return (path.startsWith(QLatin1String("\\\\"))
&& path.size() > 2 && path.at(2) != QLatin1Char('.'));
}
#endif
static inline bool isRelativePath(const QString &path)
{
// drive, e.g. "a:", or UNC root, e.q. "//"
return !(path.startsWith(QLatin1Char('/'))
|| (path.length() >= 2
&& ((path.at(0).isLetter() && path.at(1) == QLatin1Char(':'))
|| (path.at(0) == QLatin1Char('/') && path.at(1) == QLatin1Char('/')))));
}
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))
return parts.count() >= 2 ? shares.contains(parts.at(1), Qt::CaseInsensitive) : true;
}
return false;
}
static inline bool isDriveRoot(const QString &path)
{
return (path.length() == 3
&& path.at(0).isLetter() && path.at(1) == QLatin1Char(':')
&& path.at(2) == QLatin1Char('/'));
}
static QString nativeAbsoluteFilePath(const QString &path)
{
QString absPath;
#if !defined(Q_OS_WINCE)
QVarLengthArray<wchar_t, MAX_PATH> buf(qMax(MAX_PATH, path.size() + 1));
wchar_t *fileName = 0;
DWORD retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
if (retLen > (DWORD)buf.size()) {
buf.resize(retLen);
retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
}
if (retLen != 0)
absPath = QString::fromWCharArray(buf.data(), retLen);
#else
if (path.startsWith(QLatin1Char('/')) || path.startsWith(QLatin1Char('\\')))
absPath = QDir::toNativeSeparators(path);
else
absPath = QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir + QLatin1Char('/') + path));
#endif
// 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.
if (!path.isEmpty() && path.at(path.size() - 1) == QLatin1Char(' '))
absPath.append(QLatin1Char(' '));
return absPath;
}
/*!
\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(absPath)) {
prefix.append(QLatin1String("UNC\\")); // "\\\\?\\UNC\\"
absPath.remove(0, 2);
}
return prefix + absPath;
#else
return absPath;
#endif
}
/*
\internal
*/
void QFSFileEnginePrivate::nativeInitFileName()
{
QString path = longFileName(QDir::toNativeSeparators(fixIfRelativeUncPath(filePath)));
nativeFilePath = QByteArray((const char *)path.utf16(), path.size() * 2 + 1);
}
/*
\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), NULL, FALSE };
// WriteOnly can create files, ReadOnly cannot.
DWORD creationDisp = (openMode & QIODevice::WriteOnly) ? OPEN_ALWAYS : OPEN_EXISTING;
// Create the file handle.
fileHandle = CreateFile((const wchar_t*)nativeFilePath.constData(),
accessRights,
shareMode,
&securityAtts,
creationDisp,
FILE_ATTRIBUTE_NORMAL,
NULL);
// 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;
#ifndef Q_OS_WINCE
if (cachedFd != -1) {
if (::_close(cachedFd) && !::CloseHandle(fileHandle)) {
q->setError(QFile::UnspecifiedError, qt_error_string());
ok = false;
}
// System handle is closed with associated file descriptor.
fileHandle = INVALID_HANDLE_VALUE;
cachedFd = -1;
return ok;
}
#endif
if ((fileHandle == INVALID_HANDLE_VALUE || !::CloseHandle(fileHandle))) {
q->setError(QFile::UnspecifiedError, qt_error_string());
ok = false;
}
fileHandle = INVALID_HANDLE_VALUE;
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();
#if !defined(Q_OS_WINCE)
// stdlib/stdio mode.
if (fh || fd != -1) {
qint64 fileSize = _filelengthi64(fh ? QT_FILENO(fh) : fd);
if (fileSize == -1) {
fileSize = 0;
thatQ->setError(QFile::UnspecifiedError, qt_error_string(errno));
}
return fileSize;
}
#else // Q_OS_WINCE
// Buffered stdlib mode.
if (fh) {
QT_OFF_T oldPos = QT_FTELL(fh);
QT_FSEEK(fh, 0, SEEK_END);
qint64 fileSize = (qint64)QT_FTELL(fh);
QT_FSEEK(fh, oldPos, SEEK_SET);
if (fileSize == -1) {
fileSize = 0;
thatQ->setError(QFile::UnspecifiedError, qt_error_string(errno));
}
return fileSize;
}
#endif
// Not-open mode, where the file name is known: We'll check the
// file system directly.
if (openMode == QIODevice::NotOpen && !nativeFilePath.isEmpty()) {
WIN32_FILE_ATTRIBUTE_DATA attribData;
bool ok = ::GetFileAttributesEx((const wchar_t*)nativeFilePath.constData(),
GetFileExInfoStandard, &attribData);
if (!ok) {
int errorCode = GetLastError();
if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
QByteArray path = nativeFilePath;
// path for the FindFirstFile should not end with a trailing slash
while (!path.isEmpty() && reinterpret_cast<const wchar_t *>(
path.constData() + path.length())[-1] == '\\')
path.chop(2);
// FindFirstFile can not handle drives
if (!path.isEmpty() && reinterpret_cast<const wchar_t *>(
path.constData() + path.length())[-1] != ':') {
WIN32_FIND_DATA findData;
HANDLE hFind = ::FindFirstFile((const wchar_t*)path.constData(),
&findData);
if (hFind != INVALID_HANDLE_VALUE) {
::FindClose(hFind);
ok = true;
attribData.nFileSizeHigh = findData.nFileSizeHigh;
attribData.nFileSizeLow = findData.nFileSizeLow;
}
}
}
}
if (ok) {
qint64 size = attribData.nFileSizeHigh;
size <<= 32;
size += attribData.nFileSizeLow;
return size;
}
thatQ->setError(QFile::UnspecifiedError, qt_error_string());
return 0;
}
#if defined(Q_OS_WINCE)
// Unbuffed stdio mode
if (fd != -1) {
thatQ->setError(QFile::UnspecifiedError, QLatin1String("Not implemented!"));
return 0;
}
#endif
// 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(Q_OS_WINCE)
LARGE_INTEGER currentFilePos;
LARGE_INTEGER offset;
offset.QuadPart = 0;
if (!::SetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_CURRENT)) {
thatQ->setError(QFile::UnspecifiedError, qt_error_string());
return 0;
}
return qint64(currentFilePos.QuadPart);
#else
LARGE_INTEGER filepos;
filepos.HighPart = 0;
DWORD newFilePointer = SetFilePointer(fileHandle, 0, &filepos.HighPart, FILE_CURRENT);
if (newFilePointer == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
thatQ->setError(QFile::UnspecifiedError, qt_error_string());
return 0;
}
filepos.LowPart = newFilePointer;
return filepos.QuadPart;
#endif
}
/*
\internal
*/
bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
{
Q_Q(QFSFileEngine);
if (fh || fd != -1) {
// stdlib / stdio mode.
return seekFdFh(pos);
}
#if !defined(Q_OS_WINCE)
LARGE_INTEGER currentFilePos;
LARGE_INTEGER offset;
offset.QuadPart = pos;
if (!::SetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_BEGIN)) {
q->setError(QFile::UnspecifiedError, qt_error_string());
return false;
}
return true;
#else
DWORD newFilePointer;
LARGE_INTEGER *li = reinterpret_cast<LARGE_INTEGER*>(&pos);
newFilePointer = SetFilePointer(fileHandle, li->LowPart, &li->HighPart, FILE_BEGIN);
if (newFilePointer == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
q->setError(QFile::PositionError, 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, NULL)) {
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, NULL)) {
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)
HANDLE handle = fileHandle;
if (fh || fd != -1)
handle = (HANDLE)_get_osfhandle(fh ? QT_FILENO(fh) : fd);
if (handle == INVALID_HANDLE_VALUE)
return false;
DWORD fileType = GetFileType(handle);
return (fileType == FILE_TYPE_CHAR)
|| (fileType == FILE_TYPE_PIPE);
#else
return false;
#endif
}
bool QFSFileEngine::remove()
{
Q_D(QFSFileEngine);
bool ret = ::DeleteFile((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16()) != 0;
if (!ret)
setError(QFile::RemoveError, qt_error_string());
return ret;
}
bool QFSFileEngine::copy(const QString &copyName)
{
Q_D(QFSFileEngine);
bool ret = ::CopyFile((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(),
(wchar_t*)QFSFileEnginePrivate::longFileName(copyName).utf16(), true) != 0;
if (!ret)
setError(QFile::CopyError, qt_error_string());
return ret;
}
bool QFSFileEngine::rename(const QString &newName)
{
Q_D(QFSFileEngine);
bool ret = ::MoveFile((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(),
(wchar_t*)QFSFileEnginePrivate::longFileName(newName).utf16()) != 0;
if (!ret)
setError(QFile::RenameError, qt_error_string());
return ret;
}
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
return ::CreateDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(), 0);
}
static inline bool rmDir(const QString &path)
{
return ::RemoveDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16());
}
static bool isDirPath(const QString &dirPath, bool *existed)
{
QString path = dirPath;
if (path.length() == 2 && path.at(1) == QLatin1Char(':'))
path += QLatin1Char('\\');
DWORD fileAttrib = ::GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16());
if (fileAttrib == INVALID_FILE_ATTRIBUTES) {
int errorCode = GetLastError();
if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
// path for the FindFirstFile should not end with a trailing slash
while (path.endsWith(QLatin1Char('\\')))
path.chop(1);
// FindFirstFile can not handle drives
if (!path.endsWith(QLatin1Char(':'))) {
WIN32_FIND_DATA findData;
HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(),
&findData);
if (hFind != INVALID_HANDLE_VALUE) {
::FindClose(hFind);
fileAttrib = findData.dwFileAttributes;
}
}
}
}
if (existed)
*existed = fileAttrib != INVALID_FILE_ATTRIBUTES;
if (fileAttrib == INVALID_FILE_ATTRIBUTES)
return false;
return fileAttrib & FILE_ATTRIBUTE_DIRECTORY;
}
bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
{
QString dirName = name;
if (createParentDirectories) {
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
// We spefically 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)) {
if (!existed) {
if (!mkDir(chunk))
return false;
} else {
return false;
}
}
}
}
return true;
}
return mkDir(name);
}
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);
}
bool QFSFileEngine::caseSensitive() const
{
return false;
}
bool QFSFileEngine::setCurrentPath(const QString &path)
{
if (!QDir(path).exists())
return false;
#if !defined(Q_OS_WINCE)
return ::SetCurrentDirectory((wchar_t*)path.utf16()) != 0;
#else
qfsPrivateCurrentDir = QFSFileEnginePrivate::longFileName(path);
return true;
#endif
}
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) {
wchar_t buf[PATH_MAX];
::_wgetdcwd(drv, buf, PATH_MAX);
ret = QString::fromWCharArray(buf);
}
}
if (ret.isEmpty()) {
//just the pwd
DWORD size = 0;
wchar_t currentName[PATH_MAX];
size = ::GetCurrentDirectory(PATH_MAX, currentName);
if (size != 0) {
if (size > PATH_MAX) {
wchar_t *newCurrentName = new wchar_t[size];
if (::GetCurrentDirectory(PATH_MAX, newCurrentName) != 0)
ret = QString::fromWCharArray(newCurrentName);
delete [] newCurrentName;
} else {
ret = QString::fromWCharArray(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
}
QString QFSFileEngine::homePath()
{
QString ret;
#if !defined(QT_NO_LIBRARY)
QFSFileEnginePrivate::resolveLibs();
if (ptrGetUserProfileDirectoryW) {
HANDLE hnd = ::GetCurrentProcess();
HANDLE token = 0;
BOOL ok = ::OpenProcessToken(hnd, TOKEN_QUERY, &token);
if (ok) {
DWORD dwBufferSize = 0;
// First call, to determine size of the strings (with '\0').
ok = ptrGetUserProfileDirectoryW(token, NULL, &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::fromWCharArray(userDirectory);
delete [] userDirectory;
}
::CloseHandle(token);
}
}
#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 = QLatin1String("\\My Documents");
if (!QFile::exists(ret))
#endif
ret = rootPath();
}
}
}
}
return QDir::fromNativeSeparators(ret);
}
QString QFSFileEngine::rootPath()
{
#if defined(Q_OS_WINCE)
QString ret = QLatin1String("/");
#elif defined(Q_FS_FAT)
QString ret = QString::fromLatin1(qgetenv("SystemDrive").constData());
if (ret.isEmpty())
ret = QLatin1String("c:");
ret.append(QLatin1Char('/'));
#elif defined(Q_OS_OS2EMX)
char dir[4];
_abspath(dir, QLatin1String("/"), _MAX_PATH);
QString ret(dir);
#endif
return ret;
}
QString QFSFileEngine::tempPath()
{
QString ret;
{
wchar_t tempPath[MAX_PATH];
if (GetTempPath(MAX_PATH, tempPath))
ret = QString::fromWCharArray(tempPath);
if (!ret.isEmpty()) {
while (ret.endsWith(QLatin1Char('\\')))
ret.chop(1);
ret = QDir::fromNativeSeparators(ret);
}
}
if (ret.isEmpty()) {
#if !defined(Q_OS_WINCE)
ret = QLatin1String("c:/tmp");
#else
ret = QLatin1String("/Temp");
#endif
}
return ret;
}
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[] = "A:/";
while (driveBits) {
if (driveBits & 1)
ret.append(QFileInfo(QLatin1String(driveName)));
driveName[0]++;
driveBits = driveBits >> 1;
}
return ret;
#else
ret.append(QFileInfo(QLatin1String("/")));
return ret;
#endif
}
bool QFSFileEnginePrivate::doStat() const
{
if (!tried_stat) {
tried_stat = true;
could_stat = false;
if (filePath.isEmpty())
return could_stat;
QString fname;
if(filePath.endsWith(QLatin1String(".lnk"))) {
fname = readLink(filePath);
if(fname.isEmpty())
return could_stat;
}
else
fname = 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 = GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(fname).utf16());
if (tmpAttributes != -1) {
fileAttrib = tmpAttributes;
could_stat = true;
}
#endif
} else {
fileAttrib = GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(fname).utf16());
if (fileAttrib == INVALID_FILE_ATTRIBUTES) {
int errorCode = GetLastError();
if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
QString path = QDir::toNativeSeparators(fname);
// path for the FindFirstFile should not end with a trailing slash
while (path.endsWith(QLatin1Char('\\')))
path.chop(1);
// FindFirstFile can not handle drives
if (!path.endsWith(QLatin1Char(':'))) {
WIN32_FIND_DATA findData;
HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(),
&findData);
if (hFind != INVALID_HANDLE_VALUE) {
::FindClose(hFind);
fileAttrib = findData.dwFileAttributes;
}
}
}
}
could_stat = fileAttrib != INVALID_FILE_ATTRIBUTES;
if (!could_stat) {
#if !defined(Q_OS_WINCE)
if (isDriveRoot(fname)) {
// a valid 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 readSymLink(const QString &link)
{
QString result;
#if !defined(Q_OS_WINCE)
HANDLE handle = CreateFile((wchar_t*)QFSFileEnginePrivate::longFileName(link).utf16(),
FILE_READ_EA,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
0);
if (handle != INVALID_HANDLE_VALUE) {
DWORD bufsize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)qMalloc(bufsize);
DWORD retsize = 0;
if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, 0, 0, rdb, bufsize, &retsize, 0)) {
if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
int length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
int offset = rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
const wchar_t* PathBuffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[offset];
result = QString::fromWCharArray(PathBuffer, length);
}
// cut-off "//?/" and "/??/"
if (result.size() > 4 && result.at(0) == QLatin1Char('\\') && result.at(2) == QLatin1Char('?') && result.at(3) == QLatin1Char('\\'))
result = result.mid(4);
}
qFree(rdb);
CloseHandle(handle);
}
#else
Q_UNUSED(link);
#endif // Q_OS_WINCE
return result;
}
static QString readLink(const QString &link)
{
#if !defined(Q_OS_WINCE)
#if !defined(QT_NO_LIBRARY) && !defined(Q_CC_MWERKS)
QString ret;
bool neededCoInit = false;
IShellLink *psl; // pointer to IShellLink i/f
WIN32_FIND_DATA wfd;
wchar_t szGotPath[MAX_PATH];
// Get pointer to the IShellLink interface.
HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl);
if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
neededCoInit = true;
CoInitialize(NULL);
hres = CoCreateInstance(CLSID_ShellLink, NULL, 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::fromWCharArray(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::fromWCharArray(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
}
bool QFSFileEngine::link(const QString &newName)
{
#if !defined(Q_OS_WINCE)
#if !defined(QT_NO_LIBRARY) && !defined(Q_CC_MWERKS)
bool ret = false;
QString linkName = newName;
//### assume that they add .lnk
IShellLink *psl;
bool neededCoInit = false;
HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
neededCoInit = true;
CoInitialize(NULL);
hres = CoCreateInstance(CLSID_ShellLink, NULL, 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((wchar_t*)linkName.utf16(), TRUE);
if (SUCCEEDED(hres))
ret = true;
ppf->Release();
}
}
}
psl->Release();
}
if (!ret)
setError(QFile::RenameError, qt_error_string());
if (neededCoInit)
CoUninitialize();
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('"'));
bool ret = SUCCEEDED(SHCreateShortcut((wchar_t*)linkName.utf16(), (wchar_t*)orgName.utf16()));
if (!ret)
setError(QFile::RenameError, qt_error_string());
return ret;
#endif // Q_OS_WINCE
}
QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions(QAbstractFileEngine::FileFlags type) const
{
QAbstractFileEngine::FileFlags ret = 0;
#if !defined(QT_NO_LIBRARY)
if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) {
resolveLibs();
if(ptrGetNamedSecurityInfoW && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW) {
enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 };
QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath;
PSID pOwner = 0;
PSID pGroup = 0;
PACL pDacl;
PSECURITY_DESCRIPTOR pSD;
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) {
ACCESS_MASK access_mask;
TRUSTEE_W trustee;
if (type & 0x0700) { // 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;
}
if (type & 0x7000) { // 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;
}
if (type & 0x0070) { // 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;
}
if (type & 0x0007) { // other (world)
if(ptrGetEffectiveRightsFromAclW(pDacl, &worldTrusteeW, &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;
}
LocalFree(pSD);
}
}
} else
#endif
{
//### what to do with permissions if we don't use NTFS
// 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::ReadOwnerPerm | QAbstractFileEngine::ReadGroupPerm
| QAbstractFileEngine::ReadOtherPerm;
if (!(fileAttrib & FILE_ATTRIBUTE_READONLY)) {
ret |= QAbstractFileEngine::WriteOwnerPerm | QAbstractFileEngine::WriteGroupPerm
| QAbstractFileEngine::WriteOtherPerm;
}
QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath;
QString ext = fname.right(4).toLower();
if ((fileAttrib & FILE_ATTRIBUTE_DIRECTORY) ||
ext == QLatin1String(".exe") || ext == QLatin1String(".com") || ext == QLatin1String(".bat") ||
ext == QLatin1String(".pif") || ext == QLatin1String(".cmd")) {
ret |= QAbstractFileEngine::ExeOwnerPerm | QAbstractFileEngine::ExeGroupPerm
| QAbstractFileEngine::ExeOtherPerm | QAbstractFileEngine::ExeUserPerm;
}
// calculate user permissions
if (type & QAbstractFileEngine::ReadUserPerm) {
if (::_waccess((wchar_t*)longFileName(fname).utf16(), R_OK) == 0)
ret |= QAbstractFileEngine::ReadUserPerm;
}
if (type & QAbstractFileEngine::WriteUserPerm) {
if (::_waccess((wchar_t*)longFileName(fname).utf16(), W_OK) == 0)
ret |= QAbstractFileEngine::WriteUserPerm;
}
}
return ret;
}
/*!
\internal
*/
bool QFSFileEnginePrivate::isSymlink() const
{
#if !defined(Q_OS_WINCE)
if (need_lstat) {
need_lstat = false;
is_link = false;
if (fileAttrib & FILE_ATTRIBUTE_REPARSE_POINT) {
QString path = QDir::toNativeSeparators(filePath);
// path for the FindFirstFile should not end with a trailing slash
while (path.endsWith(QLatin1Char('\\')))
path.chop(1);
WIN32_FIND_DATA findData;
HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(),
&findData);
if (hFind != INVALID_HANDLE_VALUE) {
::FindClose(hFind);
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
&& findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
is_link = true;
}
}
}
}
return is_link;
#else
return false;
#endif // Q_OS_WINCE
}
/*!
\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 & Refresh) {
d->tried_stat = 0;
#if !defined(Q_OS_WINCE)
d->need_lstat = 1;
#endif
}
if (type & PermsMask) {
if (d->doStat()) {
ret |= ExistsFlag;
ret |= d->getPermissions(type);
}
}
if (type & TypesMask) {
if (d->filePath.endsWith(QLatin1String(".lnk"))) {
ret |= LinkType;
QString l = readLink(d->filePath);
if (!l.isEmpty()) {
bool existed = false;
if (isDirPath(l, &existed) && existed)
ret |= DirectoryType;
else if (existed)
ret |= FileType;
}
} else if (d->doStat()) {
if ((type & LinkType) && d->isSymlink())
ret |= LinkType;
if (d->fileAttrib & FILE_ATTRIBUTE_DIRECTORY) {
ret |= DirectoryType;
} else {
ret |= FileType;
}
}
}
if (type & FlagsMask) {
ret |= LocalDiskFlag;
if (d->doStat()) {
ret |= ExistsFlag;
if (d->filePath == QLatin1String("/") || isDriveRoot(d->filePath) || isUncRoot(d->filePath))
ret |= RootFlag;
else if (d->fileAttrib & FILE_ATTRIBUTE_HIDDEN)
ret |= HiddenFlag;
}
}
return ret;
}
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(QLatin1Char('.'));
} else {
if (!slash)
return QString(QLatin1Char('/'));
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 = QString(QLatin1Char('/'));
ret = ret.left(slash);
}
return ret;
} else if (file == LinkName) {
QString ret;
if (d->filePath.endsWith(QLatin1String(".lnk")))
ret = readLink(d->filePath);
else if (d->doStat() && d->isSymlink())
ret = readSymLink(d->filePath);
return QDir::fromNativeSeparators(ret);
} else if (file == BundleName) {
return QString();
}
return d->filePath;
}
bool QFSFileEngine::isRelativePath() const
{
Q_D(const QFSFileEngine);
// drive, e.g. "a:", or UNC root, e.q. "//"
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('/')))));
}
uint QFSFileEngine::ownerId(FileOwner /*own*/) const
{
static const uint nobodyID = (uint) -2;
return nobodyID;
}
QString QFSFileEngine::owner(FileOwner own) const
{
QString name;
#if !defined(QT_NO_LIBRARY)
Q_D(const QFSFileEngine);
if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) {
QFSFileEnginePrivate::resolveLibs();
if (ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) {
PSID pOwner = 0;
PSECURITY_DESCRIPTOR pSD;
if (ptrGetNamedSecurityInfoW((wchar_t*)d->filePath.utf16(), SE_FILE_OBJECT,
own == OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION,
own == OwnerUser ? &pOwner : 0, own == OwnerGroup ? &pOwner : 0,
0, 0, &pSD) == ERROR_SUCCESS) {
DWORD lowner = 64;
DWORD ldomain = 64;
QVarLengthArray<wchar_t, 64> owner(lowner);
QVarLengthArray<wchar_t, 64> domain(ldomain);
SID_NAME_USE use = SidTypeUnknown;
// First call, to determine size of the strings (with '\0').
if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner,
(LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
if (lowner > (DWORD)owner.size())
owner.resize(lowner);
if (ldomain > (DWORD)domain.size())
domain.resize(ldomain);
// Second call, try on resized buf-s
if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner,
(LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) {
lowner = 0;
}
} else {
lowner = 0;
}
}
if (lowner != 0)
name = QString::fromWCharArray(owner.data());
LocalFree(pSD);
}
}
}
#else
Q_UNUSED(own);
#endif
return name;
}
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;
ret = ::_wchmod((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(), mode) == 0;
if (!ret)
setError(QFile::PermissionsError, qt_error_string(errno));
return ret;
}
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)) {
bool ret = file.resize(size);
if (!ret)
setError(QFile::ResizeError, file.errorString());
return ret;
}
}
return false;
}
static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
{
QDateTime ret;
#if defined(Q_OS_WINCE)
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
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;
}
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 {
WIN32_FILE_ATTRIBUTE_DATA attribData;
bool ok = ::GetFileAttributesEx((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(), GetFileExInfoStandard, &attribData);
if (!ok) {
int errorCode = GetLastError();
if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
QString path = QDir::toNativeSeparators(d->filePath);
// path for the FindFirstFile should not end with a trailing slash
while (path.endsWith(QLatin1Char('\\')))
path.chop(1);
// FindFirstFile can not handle drives
if (!path.endsWith(QLatin1Char(':'))) {
WIN32_FIND_DATA findData;
HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(),
&findData);
if (hFind != INVALID_HANDLE_VALUE) {
::FindClose(hFind);
ok = true;
attribData.ftCreationTime = findData.ftCreationTime;
attribData.ftLastWriteTime = findData.ftLastWriteTime;
attribData.ftLastAccessTime = findData.ftLastAccessTime;
}
}
}
}
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(ERROR_ACCESS_DENIED));
return 0;
}
if (offset == 0 && size == 0) {
q->setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
return 0;
}
if (mapHandle == INVALID_HANDLE_VALUE) {
// 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));
#endif
#ifdef Q_USE_DEPRECATED_MAP_API
nativeClose();
// handle automatically closed by kernel with mapHandle (below).
handle = ::CreateFileForMapping((const wchar_t*)nativeFilePath.constData(),
GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0),
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
// Since this is a special case, we check if the return value was NULL and if so
// we change it to INVALID_HANDLE_VALUE to follow the logic inside this function.
if(0 == handle)
handle = INVALID_HANDLE_VALUE;
#endif
if (handle == INVALID_HANDLE_VALUE) {
q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
return 0;
}
// first create the file mapping handle
DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY;
mapHandle = ::CreateFileMapping(handle, 0, protection, 0, 0, 0);
if (mapHandle == INVALID_HANDLE_VALUE) {
q->setError(QFile::PermissionsError, qt_error_string());
#ifdef Q_USE_DEPRECATED_MAP_API
::CloseHandle(handle);
#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);
DWORD mask = sysinfo.dwAllocationGranularity - 1;
DWORD 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] = extra;
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);
return 0;
}
bool QFSFileEnginePrivate::unmap(uchar *ptr)
{
Q_Q(QFSFileEngine);
if (!maps.contains(ptr)) {
q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
return false;
}
uchar *start = ptr - maps[ptr];
if (!UnmapViewOfFile(start)) {
q->setError(QFile::PermissionsError, qt_error_string());
return false;
}
maps.remove(ptr);
if (maps.isEmpty()) {
::CloseHandle(mapHandle);
mapHandle = INVALID_HANDLE_VALUE;
}
return true;
}
QT_END_NAMESPACE