2011-04-27 10:05:43 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2016-01-15 07:08:27 +00:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
|
|
|
** This file is part of the QtCore module of the Qt Toolkit.
|
|
|
|
**
|
2016-01-15 07:08:27 +00:00
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
2012-09-19 12:28:29 +00:00
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2015-01-28 08:44:43 +00:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
2016-01-15 07:08:27 +00:00
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2012-09-19 12:28:29 +00:00
|
|
|
**
|
2011-04-27 10:05:43 +00:00
|
|
|
** GNU Lesser General Public License Usage
|
2012-09-19 12:28:29 +00:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2016-01-15 07:08:27 +00:00
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU Lesser General Public License version 3 requirements
|
|
|
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
2016-01-15 07:08:27 +00:00
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 2.0 or (at your option) the GNU General
|
|
|
|
** Public license version 3 or any later version approved by the KDE Free
|
|
|
|
** Qt Foundation. The licenses are as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
|
|
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qfilesystemengine_p.h"
|
2016-09-09 18:21:29 +00:00
|
|
|
#include "qoperatingsystemversion.h"
|
2011-04-27 10:05:43 +00:00
|
|
|
#include "qplatformdefs.h"
|
2013-01-09 08:11:17 +00:00
|
|
|
#include "qsysinfo.h"
|
2012-02-17 19:09:17 +00:00
|
|
|
#include "private/qabstractfileengine_p.h"
|
2011-04-27 10:05:43 +00:00
|
|
|
#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"
|
2016-03-31 09:01:59 +00:00
|
|
|
#include "qvector.h"
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2016-03-23 09:25:46 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <direct.h>
|
|
|
|
#include <winioctl.h>
|
2011-04-27 10:05:43 +00:00
|
|
|
#include <objbase.h>
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
|
|
|
# include <shlobj.h>
|
|
|
|
# include <accctrl.h>
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
#include <initguid.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <limits.h>
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
|
|
|
# define SECURITY_WIN32
|
|
|
|
# include <security.h>
|
|
|
|
#else // !Q_OS_WINRT
|
2014-03-19 14:20:06 +00:00
|
|
|
# include "qstandardpaths.h"
|
|
|
|
# include "qthreadstorage.h"
|
2013-09-02 10:57:51 +00:00
|
|
|
# include <wrl.h>
|
|
|
|
# include <windows.foundation.h>
|
|
|
|
# include <windows.storage.h>
|
2013-11-01 15:16:15 +00:00
|
|
|
# include <Windows.ApplicationModel.h>
|
2013-09-02 10:57:51 +00:00
|
|
|
|
|
|
|
using namespace Microsoft::WRL;
|
|
|
|
using namespace Microsoft::WRL::Wrappers;
|
|
|
|
using namespace ABI::Windows::Foundation;
|
|
|
|
using namespace ABI::Windows::Storage;
|
2013-11-01 15:16:15 +00:00
|
|
|
using namespace ABI::Windows::ApplicationModel;
|
2013-09-02 10:57:51 +00:00
|
|
|
#endif // Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
#ifndef SPI_GETPLATFORMTYPE
|
|
|
|
#define SPI_GETPLATFORMTYPE 257
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef PATH_MAX
|
|
|
|
#define PATH_MAX FILENAME_MAX
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
2016-03-23 09:25:46 +00:00
|
|
|
#if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
|
2011-04-27 10:05:43 +00:00
|
|
|
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;
|
2016-03-23 09:25:46 +00:00
|
|
|
# define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
|
|
|
|
#endif // !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2016-03-23 09:25:46 +00:00
|
|
|
#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
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
|
|
|
|
|
2016-03-23 09:25:46 +00:00
|
|
|
#if defined(Q_OS_WINRT)
|
2016-12-19 09:34:32 +00:00
|
|
|
// As none of the functions we try to resolve do exist on WinRT we
|
|
|
|
// avoid library loading on WinRT in general to shorten everything
|
|
|
|
// up a little bit.
|
|
|
|
# define QT_FEATURE_fslibs -1
|
|
|
|
#else
|
|
|
|
# define QT_FEATURE_fslibs QT_FEATURE_library
|
2016-03-23 09:25:46 +00:00
|
|
|
#endif // Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2016-12-19 09:34:32 +00:00
|
|
|
#if QT_CONFIG(fslibs)
|
2011-04-27 10:05:43 +00:00
|
|
|
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;
|
2012-01-21 17:04:23 +00:00
|
|
|
typedef BOOL (WINAPI *PtrGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD);
|
|
|
|
static PtrGetUserProfileDirectoryW ptrGetUserProfileDirectoryW = 0;
|
|
|
|
QT_END_INCLUDE_NAMESPACE
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
static TRUSTEE_W currentUserTrusteeW;
|
|
|
|
static TRUSTEE_W worldTrusteeW;
|
2012-01-12 13:43:07 +00:00
|
|
|
static PSID currentUserSID = 0;
|
|
|
|
static PSID worldSID = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Deletes the allocated SIDs during global static cleanup
|
|
|
|
*/
|
|
|
|
class SidCleanup
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
~SidCleanup();
|
|
|
|
};
|
|
|
|
|
|
|
|
SidCleanup::~SidCleanup()
|
|
|
|
{
|
2012-01-21 14:09:51 +00:00
|
|
|
free(currentUserSID);
|
2012-01-12 13:43:07 +00:00
|
|
|
currentUserSID = 0;
|
|
|
|
|
|
|
|
// worldSID was allocated with AllocateAndInitializeSid so it needs to be freed with FreeSid
|
|
|
|
if (worldSID) {
|
|
|
|
::FreeSid(worldSID);
|
|
|
|
worldSID = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_GLOBAL_STATIC(SidCleanup, initSidCleanup)
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
static void 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;
|
|
|
|
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;
|
2012-01-12 13:43:07 +00:00
|
|
|
initSidCleanup();
|
2011-04-27 10:05:43 +00:00
|
|
|
if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) {
|
2012-01-12 13:43:07 +00:00
|
|
|
DWORD retsize = 0;
|
|
|
|
// GetTokenInformation requires a buffer big enough for the TOKEN_USER struct and
|
|
|
|
// the SID struct. Since the SID struct can have variable number of subauthorities
|
|
|
|
// tacked at the end, its size is variable. Obtain the required size by first
|
|
|
|
// doing a dummy GetTokenInformation call.
|
|
|
|
::GetTokenInformation(token, TokenUser, 0, 0, &retsize);
|
|
|
|
if (retsize) {
|
2012-01-21 14:09:51 +00:00
|
|
|
void *tokenBuffer = malloc(retsize);
|
2012-01-12 13:43:07 +00:00
|
|
|
if (::GetTokenInformation(token, TokenUser, tokenBuffer, retsize, &retsize)) {
|
|
|
|
PSID tokenSid = reinterpret_cast<PTOKEN_USER>(tokenBuffer)->User.Sid;
|
|
|
|
DWORD sidLen = ::GetLengthSid(tokenSid);
|
2012-01-21 14:09:51 +00:00
|
|
|
currentUserSID = reinterpret_cast<PSID>(malloc(sidLen));
|
2012-01-12 13:43:07 +00:00
|
|
|
if (::CopySid(sidLen, currentUserSID, tokenSid))
|
|
|
|
ptrBuildTrusteeWithSidW(¤tUserTrusteeW, currentUserSID);
|
|
|
|
}
|
2012-01-21 14:09:51 +00:00
|
|
|
free(tokenBuffer);
|
2012-01-12 13:43:07 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
::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");
|
2012-01-12 13:43:07 +00:00
|
|
|
if (ptrAllocateAndInitializeSid) {
|
2011-04-27 10:05:43 +00:00
|
|
|
// Create TRUSTEE for Everyone (World)
|
|
|
|
SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY };
|
2012-01-12 13:43:07 +00:00
|
|
|
if (ptrAllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &worldSID))
|
|
|
|
ptrBuildTrusteeWithSidW(&worldTrusteeW, worldSID);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
HINSTANCE userenvHnd = QSystemLibrary::load(L"userenv");
|
|
|
|
if (userenvHnd)
|
|
|
|
ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW");
|
|
|
|
}
|
|
|
|
}
|
2016-12-19 09:34:32 +00:00
|
|
|
#endif // QT_CONFIG(fslibs)
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
static bool resolveUNCLibs()
|
|
|
|
{
|
|
|
|
static bool triedResolve = false;
|
|
|
|
if (!triedResolve) {
|
|
|
|
#ifndef QT_NO_THREAD
|
|
|
|
QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
|
|
|
|
if (triedResolve) {
|
|
|
|
return ptrNetShareEnum && ptrNetApiBufferFree;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
triedResolve = true;
|
2016-03-23 09:25:46 +00:00
|
|
|
#if !defined(Q_OS_WINRT)
|
2011-04-27 10:05:43 +00:00
|
|
|
HINSTANCE hLib = QSystemLibrary::load(L"Netapi32");
|
|
|
|
if (hLib) {
|
|
|
|
ptrNetShareEnum = (PtrNetShareEnum)GetProcAddress(hLib, "NetShareEnum");
|
|
|
|
if (ptrNetShareEnum)
|
|
|
|
ptrNetApiBufferFree = (PtrNetApiBufferFree)GetProcAddress(hLib, "NetApiBufferFree");
|
|
|
|
}
|
2016-03-23 09:25:46 +00:00
|
|
|
#endif // !Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
return ptrNetShareEnum && ptrNetApiBufferFree;
|
|
|
|
}
|
|
|
|
|
|
|
|
static QString readSymLink(const QFileSystemEntry &link)
|
|
|
|
{
|
|
|
|
QString result;
|
2016-03-23 09:25:46 +00:00
|
|
|
#if !defined(Q_OS_WINRT)
|
2011-04-27 10:05:43 +00:00
|
|
|
HANDLE handle = CreateFile((wchar_t*)link.nativeFilePath().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;
|
2011-12-20 20:39:12 +00:00
|
|
|
REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)malloc(bufsize);
|
2011-04-27 10:05:43 +00:00
|
|
|
DWORD retsize = 0;
|
|
|
|
if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, 0, 0, rdb, bufsize, &retsize, 0)) {
|
|
|
|
if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
|
|
|
|
int length = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
|
|
|
|
int offset = rdb->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
|
|
|
|
const wchar_t* PathBuffer = &rdb->MountPointReparseBuffer.PathBuffer[offset];
|
|
|
|
result = QString::fromWCharArray(PathBuffer, length);
|
|
|
|
} else 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);
|
|
|
|
}
|
2011-12-20 20:39:12 +00:00
|
|
|
free(rdb);
|
2011-04-27 10:05:43 +00:00
|
|
|
CloseHandle(handle);
|
|
|
|
|
2016-12-19 09:34:32 +00:00
|
|
|
#if QT_CONFIG(fslibs)
|
2011-04-27 10:05:43 +00:00
|
|
|
resolveLibs();
|
2016-03-16 11:59:56 +00:00
|
|
|
QRegExp matchVolName(QLatin1String("^Volume\\{([a-z]|[0-9]|-)+\\}\\\\"), Qt::CaseInsensitive);
|
|
|
|
if (matchVolName.indexIn(result) == 0) {
|
|
|
|
DWORD len;
|
|
|
|
wchar_t buffer[MAX_PATH];
|
2016-08-15 14:30:44 +00:00
|
|
|
const QString volumeName = QLatin1String("\\\\?\\") + result.leftRef(matchVolName.matchedLength());
|
2016-03-16 11:59:56 +00:00
|
|
|
if (GetVolumePathNamesForVolumeName(reinterpret_cast<LPCWSTR>(volumeName.utf16()), buffer, MAX_PATH, &len) != 0)
|
|
|
|
result.replace(0,matchVolName.matchedLength(), QString::fromWCharArray(buffer));
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
2016-12-19 09:34:32 +00:00
|
|
|
#endif // QT_CONFIG(fslibs)
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
Q_UNUSED(link);
|
2016-03-23 09:25:46 +00:00
|
|
|
#endif // Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static QString readLink(const QFileSystemEntry &link)
|
|
|
|
{
|
2016-12-19 09:34:32 +00:00
|
|
|
#if QT_CONFIG(fslibs)
|
2011-04-27 10:05:43 +00:00
|
|
|
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.nativeFilePath().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();
|
2016-12-19 09:34:32 +00:00
|
|
|
#endif // QT_CONFIG(fslibs)
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool uncShareExists(const QString &server)
|
|
|
|
{
|
|
|
|
// This code assumes the UNC path is always like \\?\UNC\server...
|
2016-03-31 09:01:59 +00:00
|
|
|
const QVector<QStringRef> parts = server.splitRef(QLatin1Char('\\'), QString::SkipEmptyParts);
|
2011-04-27 10:05:43 +00:00
|
|
|
if (parts.count() >= 3) {
|
|
|
|
QStringList shares;
|
|
|
|
if (QFileSystemEngine::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(2), &shares))
|
2016-03-31 09:01:59 +00:00
|
|
|
return parts.count() < 4 || shares.contains(parts.at(3).toString(), Qt::CaseInsensitive);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool getFindData(QString path, WIN32_FIND_DATA &findData)
|
|
|
|
{
|
|
|
|
// path should not end with a trailing slash
|
|
|
|
while (path.endsWith(QLatin1Char('\\')))
|
|
|
|
path.chop(1);
|
|
|
|
|
|
|
|
// can't handle drives
|
|
|
|
if (!path.endsWith(QLatin1Char(':'))) {
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
HANDLE hFind = ::FindFirstFile((wchar_t*)path.utf16(), &findData);
|
2013-09-02 10:57:51 +00:00
|
|
|
#else
|
|
|
|
HANDLE hFind = ::FindFirstFileEx((const wchar_t*)path.utf16(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0);
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
if (hFind != INVALID_HANDLE_VALUE) {
|
|
|
|
::FindClose(hFind);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QFileSystemEngine::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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QFileSystemEngine::clearWinStatData(QFileSystemMetaData &data)
|
|
|
|
{
|
|
|
|
data.size_ = 0;
|
|
|
|
data.fileAttribute_ = 0;
|
|
|
|
data.creationTime_ = FILETIME();
|
|
|
|
data.lastAccessTime_ = FILETIME();
|
|
|
|
data.lastWriteTime_ = FILETIME();
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
|
|
|
|
QFileSystemMetaData &data)
|
|
|
|
{
|
|
|
|
if (data.missingFlags(QFileSystemMetaData::LinkType))
|
|
|
|
QFileSystemEngine::fillMetaData(link, data, QFileSystemMetaData::LinkType);
|
|
|
|
|
2017-08-26 19:54:33 +00:00
|
|
|
QString target;
|
2011-04-27 10:05:43 +00:00
|
|
|
if (data.isLnkFile())
|
2017-08-26 19:54:33 +00:00
|
|
|
target = readLink(link);
|
2011-04-27 10:05:43 +00:00
|
|
|
else if (data.isLink())
|
2017-08-26 19:54:33 +00:00
|
|
|
target = readSymLink(link);
|
|
|
|
QFileSystemEntry ret(target);
|
|
|
|
if (!target.isEmpty() && ret.isRelative()) {
|
|
|
|
target.prepend(absoluteName(link).path() + QLatin1Char('/'));
|
|
|
|
ret = QFileSystemEntry(QDir::cleanPath(target));
|
|
|
|
}
|
|
|
|
return ret;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
|
|
|
|
{
|
|
|
|
if (data.missingFlags(QFileSystemMetaData::ExistsAttribute))
|
|
|
|
QFileSystemEngine::fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute);
|
|
|
|
|
|
|
|
if (data.exists())
|
|
|
|
return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath()));
|
|
|
|
else
|
|
|
|
return QFileSystemEntry();
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path)
|
|
|
|
{
|
|
|
|
// can be //server or //server/share
|
|
|
|
QString absPath;
|
|
|
|
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);
|
2015-12-11 12:42:28 +00:00
|
|
|
# if defined(Q_OS_WINRT)
|
|
|
|
// Win32 returns eg C:/ as root directory with a trailing /.
|
|
|
|
// WinRT returns the sandbox root without /.
|
|
|
|
// Also C:/../.. returns C:/ on Win32, while for WinRT it steps outside the package
|
|
|
|
// and goes beyond package root. Hence force the engine to stay inside
|
|
|
|
// the package.
|
|
|
|
const QString rootPath = QDir::toNativeSeparators(QDir::rootPath());
|
|
|
|
if (absPath.size() < rootPath.size() && rootPath.startsWith(absPath))
|
|
|
|
absPath = rootPath;
|
|
|
|
# endif // Q_OS_WINRT
|
2016-11-22 13:54:02 +00:00
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
|
|
|
|
{
|
|
|
|
QString ret;
|
|
|
|
|
|
|
|
if (!entry.isRelative()) {
|
2016-03-23 09:25:46 +00:00
|
|
|
if (entry.isAbsolute() && entry.isClean())
|
2011-04-27 10:05:43 +00:00
|
|
|
ret = entry.filePath();
|
2016-03-23 09:25:46 +00:00
|
|
|
else
|
2011-04-27 10:05:43 +00:00
|
|
|
ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(entry.filePath()));
|
|
|
|
} else {
|
|
|
|
ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + entry.filePath());
|
|
|
|
}
|
|
|
|
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
// 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();
|
|
|
|
}
|
2013-09-02 10:57:51 +00:00
|
|
|
#endif // !Q_OS_WINRT
|
2011-08-26 09:01:48 +00:00
|
|
|
return QFileSystemEntry(ret, QFileSystemEntry::FromInternalPath());
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2016-03-16 11:59:56 +00:00
|
|
|
#if defined(Q_CC_MINGW) && WINVER < 0x0602 // Windows 8 onwards
|
2013-01-09 08:11:17 +00:00
|
|
|
|
|
|
|
typedef struct _FILE_ID_INFO {
|
|
|
|
ULONGLONG VolumeSerialNumber;
|
|
|
|
FILE_ID_128 FileId;
|
|
|
|
} FILE_ID_INFO, *PFILE_ID_INFO;
|
2016-03-16 11:59:56 +00:00
|
|
|
|
|
|
|
#endif // if defined (Q_CC_MINGW) && WINVER < 0x0602
|
2013-01-09 08:11:17 +00:00
|
|
|
|
|
|
|
// File ID for Windows up to version 7.
|
|
|
|
static inline QByteArray fileId(HANDLE handle)
|
|
|
|
{
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2013-01-09 08:11:17 +00:00
|
|
|
BY_HANDLE_FILE_INFORMATION info;
|
|
|
|
if (GetFileInformationByHandle(handle, &info)) {
|
2017-07-09 19:29:26 +00:00
|
|
|
char buffer[sizeof "01234567:0123456701234567"];
|
|
|
|
qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx",
|
|
|
|
info.dwVolumeSerialNumber,
|
|
|
|
info.nFileIndexHigh,
|
|
|
|
info.nFileIndexLow);
|
|
|
|
return buffer;
|
2013-01-09 08:11:17 +00:00
|
|
|
}
|
2013-09-02 10:57:51 +00:00
|
|
|
#else // !Q_OS_WINRT
|
|
|
|
Q_UNUSED(handle);
|
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
#endif // Q_OS_WINRT
|
2017-07-09 19:29:26 +00:00
|
|
|
return QByteArray();
|
2013-01-09 08:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// File ID for Windows starting from version 8.
|
|
|
|
QByteArray fileIdWin8(HANDLE handle)
|
|
|
|
{
|
2016-03-16 11:59:56 +00:00
|
|
|
#if !defined(QT_BOOTSTRAPPED) && !defined(QT_BUILD_QMAKE)
|
2013-09-02 10:57:51 +00:00
|
|
|
QByteArray result;
|
|
|
|
FILE_ID_INFO infoEx;
|
2016-03-16 11:59:56 +00:00
|
|
|
if (GetFileInformationByHandleEx(handle,
|
|
|
|
static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8
|
2013-09-02 10:57:51 +00:00
|
|
|
&infoEx, sizeof(FILE_ID_INFO))) {
|
|
|
|
result = QByteArray::number(infoEx.VolumeSerialNumber, 16);
|
|
|
|
result += ':';
|
2016-03-16 11:59:56 +00:00
|
|
|
// Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one.
|
|
|
|
result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId), int(sizeof(infoEx.FileId))).toHex();
|
2013-09-02 10:57:51 +00:00
|
|
|
}
|
2013-01-09 08:11:17 +00:00
|
|
|
return result;
|
2016-03-16 11:59:56 +00:00
|
|
|
#else // !QT_BOOTSTRAPPED && !QT_BUILD_QMAKE
|
|
|
|
return fileId(handle);
|
|
|
|
#endif
|
2013-01-09 08:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
|
|
|
|
{
|
|
|
|
QByteArray result;
|
2017-07-09 19:41:32 +00:00
|
|
|
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2017-07-09 19:41:32 +00:00
|
|
|
const HANDLE handle =
|
2017-07-05 21:41:11 +00:00
|
|
|
CreateFile((wchar_t*)entry.nativeFilePath().utf16(), 0,
|
2017-07-09 19:41:32 +00:00
|
|
|
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
|
|
|
FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
2013-09-02 10:57:51 +00:00
|
|
|
#else // !Q_OS_WINRT
|
2017-07-09 19:41:32 +00:00
|
|
|
CREATEFILE2_EXTENDED_PARAMETERS params;
|
|
|
|
params.dwSize = sizeof(params);
|
|
|
|
params.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
|
|
params.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
|
2017-10-19 12:32:09 +00:00
|
|
|
params.dwSecurityQosFlags = SECURITY_ANONYMOUS;
|
|
|
|
params.lpSecurityAttributes = NULL;
|
|
|
|
params.hTemplateFile = NULL;
|
2017-07-09 19:41:32 +00:00
|
|
|
const HANDLE handle =
|
2017-07-05 21:41:11 +00:00
|
|
|
CreateFile2((const wchar_t*)entry.nativeFilePath().utf16(), 0,
|
2017-07-09 19:41:32 +00:00
|
|
|
FILE_SHARE_READ, OPEN_EXISTING, ¶ms);
|
2013-09-02 10:57:51 +00:00
|
|
|
#endif // Q_OS_WINRT
|
2017-07-05 21:41:11 +00:00
|
|
|
if (handle != INVALID_HANDLE_VALUE) {
|
2017-06-29 19:55:54 +00:00
|
|
|
result = id(handle);
|
2013-01-09 08:11:17 +00:00
|
|
|
CloseHandle(handle);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-06-29 19:55:54 +00:00
|
|
|
//static
|
|
|
|
QByteArray QFileSystemEngine::id(HANDLE fHandle)
|
|
|
|
{
|
|
|
|
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 ?
|
|
|
|
fileIdWin8(HANDLE(fHandle)) : fileId(HANDLE(fHandle));
|
|
|
|
}
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
//static
|
|
|
|
QString QFileSystemEngine::owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own)
|
|
|
|
{
|
|
|
|
QString name;
|
2016-12-19 09:34:32 +00:00
|
|
|
#if QT_CONFIG(fslibs)
|
2011-04-27 10:05:43 +00:00
|
|
|
extern int qt_ntfs_permission_lookup;
|
|
|
|
if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) {
|
|
|
|
resolveLibs();
|
|
|
|
if (ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) {
|
|
|
|
PSID pOwner = 0;
|
|
|
|
PSECURITY_DESCRIPTOR pSD;
|
|
|
|
if (ptrGetNamedSecurityInfoW((wchar_t*)entry.nativeFilePath().utf16(), SE_FILE_OBJECT,
|
|
|
|
own == QAbstractFileEngine::OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION,
|
|
|
|
own == QAbstractFileEngine::OwnerUser ? &pOwner : 0, own == QAbstractFileEngine::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
|
2011-04-26 10:03:17 +00:00
|
|
|
Q_UNUSED(entry);
|
2011-04-27 10:05:43 +00:00
|
|
|
Q_UNUSED(own);
|
|
|
|
#endif
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSystemMetaData &data,
|
|
|
|
QFileSystemMetaData::MetaDataFlags what)
|
|
|
|
{
|
2016-12-19 09:34:32 +00:00
|
|
|
#if QT_CONFIG(fslibs)
|
2011-04-27 10:05:43 +00:00
|
|
|
if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) {
|
|
|
|
resolveLibs();
|
|
|
|
if(ptrGetNamedSecurityInfoW && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW) {
|
|
|
|
enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 };
|
|
|
|
|
2012-05-02 15:46:52 +00:00
|
|
|
QString fname = entry.nativeFilePath();
|
2011-04-27 10:05:43 +00:00
|
|
|
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 (what & QFileSystemMetaData::UserPermissions) { // user
|
|
|
|
data.knownFlagsMask |= QFileSystemMetaData::UserPermissions;
|
|
|
|
if(ptrGetEffectiveRightsFromAclW(pDacl, ¤tUserTrusteeW, &access_mask) != ERROR_SUCCESS)
|
|
|
|
access_mask = (ACCESS_MASK)-1;
|
|
|
|
if(access_mask & ReadMask)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::UserReadPermission;
|
|
|
|
if(access_mask & WriteMask)
|
|
|
|
data.entryFlags|= QFileSystemMetaData::UserWritePermission;
|
|
|
|
if(access_mask & ExecMask)
|
|
|
|
data.entryFlags|= QFileSystemMetaData::UserExecutePermission;
|
|
|
|
}
|
|
|
|
if (what & QFileSystemMetaData::OwnerPermissions) { // owner
|
|
|
|
data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions;
|
|
|
|
ptrBuildTrusteeWithSidW(&trustee, pOwner);
|
|
|
|
if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
|
|
|
|
access_mask = (ACCESS_MASK)-1;
|
|
|
|
if(access_mask & ReadMask)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::OwnerReadPermission;
|
|
|
|
if(access_mask & WriteMask)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::OwnerWritePermission;
|
|
|
|
if(access_mask & ExecMask)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
|
|
|
|
}
|
|
|
|
if (what & QFileSystemMetaData::GroupPermissions) { // group
|
|
|
|
data.knownFlagsMask |= QFileSystemMetaData::GroupPermissions;
|
|
|
|
ptrBuildTrusteeWithSidW(&trustee, pGroup);
|
|
|
|
if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
|
|
|
|
access_mask = (ACCESS_MASK)-1;
|
|
|
|
if(access_mask & ReadMask)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::GroupReadPermission;
|
|
|
|
if(access_mask & WriteMask)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::GroupWritePermission;
|
|
|
|
if(access_mask & ExecMask)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::GroupExecutePermission;
|
|
|
|
}
|
|
|
|
if (what & QFileSystemMetaData::OtherPermissions) { // other (world)
|
|
|
|
data.knownFlagsMask |= QFileSystemMetaData::OtherPermissions;
|
|
|
|
if(ptrGetEffectiveRightsFromAclW(pDacl, &worldTrusteeW, &access_mask) != ERROR_SUCCESS)
|
|
|
|
access_mask = (ACCESS_MASK)-1; // ###
|
|
|
|
if(access_mask & ReadMask)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::OtherReadPermission;
|
|
|
|
if(access_mask & WriteMask)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::OtherWritePermission;
|
|
|
|
if(access_mask & ExecMask)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
|
|
|
|
}
|
|
|
|
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 ?
|
|
|
|
data.entryFlags |= QFileSystemMetaData::OwnerReadPermission
|
|
|
|
| QFileSystemMetaData::GroupReadPermission
|
|
|
|
| QFileSystemMetaData::OtherReadPermission;
|
|
|
|
|
|
|
|
if (!(data.fileAttribute_ & FILE_ATTRIBUTE_READONLY)) {
|
|
|
|
data.entryFlags |= QFileSystemMetaData::OwnerWritePermission
|
|
|
|
| QFileSystemMetaData::GroupWritePermission
|
|
|
|
| QFileSystemMetaData::OtherWritePermission;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString fname = entry.filePath();
|
|
|
|
QString ext = fname.right(4).toLower();
|
|
|
|
if (data.isDirectory() ||
|
|
|
|
ext == QLatin1String(".exe") || ext == QLatin1String(".com") || ext == QLatin1String(".bat") ||
|
|
|
|
ext == QLatin1String(".pif") || ext == QLatin1String(".cmd")) {
|
|
|
|
data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission | QFileSystemMetaData::GroupExecutePermission
|
|
|
|
| QFileSystemMetaData::OtherExecutePermission | QFileSystemMetaData::UserExecutePermission;
|
|
|
|
}
|
|
|
|
data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions | QFileSystemMetaData::GroupPermissions
|
|
|
|
| QFileSystemMetaData::OtherPermissions | QFileSystemMetaData::UserExecutePermission;
|
|
|
|
// calculate user permissions
|
|
|
|
if (what & QFileSystemMetaData::UserReadPermission) {
|
|
|
|
if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), R_OK) == 0)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::UserReadPermission;
|
|
|
|
data.knownFlagsMask |= QFileSystemMetaData::UserReadPermission;
|
|
|
|
}
|
|
|
|
if (what & QFileSystemMetaData::UserWritePermission) {
|
|
|
|
if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), W_OK) == 0)
|
|
|
|
data.entryFlags |= QFileSystemMetaData::UserWritePermission;
|
2011-10-06 12:46:30 +00:00
|
|
|
data.knownFlagsMask |= QFileSystemMetaData::UserWritePermission;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.hasFlags(what);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool tryDriveUNCFallback(const QFileSystemEntry &fname, QFileSystemMetaData &data)
|
|
|
|
{
|
|
|
|
bool entryExists = false;
|
|
|
|
DWORD fileAttrib = 0;
|
2016-03-23 09:25:46 +00:00
|
|
|
#if !defined(Q_OS_WINRT)
|
2011-04-27 10:05:43 +00:00
|
|
|
if (fname.isDriveRoot()) {
|
|
|
|
// a valid drive ??
|
2016-05-12 06:46:13 +00:00
|
|
|
const UINT oldErrorMode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
2011-04-27 10:05:43 +00:00
|
|
|
DWORD drivesBitmask = ::GetLogicalDrives();
|
2016-05-12 06:46:13 +00:00
|
|
|
::SetErrorMode(oldErrorMode);
|
2011-04-27 10:05:43 +00:00
|
|
|
int drivebit = 1 << (fname.filePath().at(0).toUpper().unicode() - QLatin1Char('A').unicode());
|
|
|
|
if (drivesBitmask & drivebit) {
|
|
|
|
fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM;
|
|
|
|
entryExists = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
#endif
|
|
|
|
const QString &path = fname.nativeFilePath();
|
|
|
|
bool is_dir = false;
|
|
|
|
if (path.startsWith(QLatin1String("\\\\?\\UNC"))) {
|
|
|
|
// UNC - stat doesn't work for all cases (Windows bug)
|
|
|
|
int s = path.indexOf(path.at(0),7);
|
|
|
|
if (s > 0) {
|
|
|
|
// "\\?\UNC\server\..."
|
|
|
|
s = path.indexOf(path.at(0),s+1);
|
|
|
|
if (s > 0) {
|
|
|
|
// "\\?\UNC\server\share\..."
|
|
|
|
if (s == path.size() - 1) {
|
|
|
|
// "\\?\UNC\server\share\"
|
|
|
|
is_dir = true;
|
|
|
|
} else {
|
|
|
|
// "\\?\UNC\server\share\notfound"
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// "\\?\UNC\server\share"
|
|
|
|
is_dir = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// "\\?\UNC\server"
|
|
|
|
is_dir = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (is_dir && uncShareExists(path)) {
|
|
|
|
// looks like a UNC dir, is a dir.
|
|
|
|
fileAttrib = FILE_ATTRIBUTE_DIRECTORY;
|
|
|
|
entryExists = true;
|
|
|
|
}
|
2016-03-23 09:25:46 +00:00
|
|
|
#if !defined(Q_OS_WINRT)
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (entryExists)
|
|
|
|
data.fillFromFileAttribute(fileAttrib);
|
|
|
|
return entryExists;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool tryFindFallback(const QFileSystemEntry &fname, QFileSystemMetaData &data)
|
|
|
|
{
|
|
|
|
bool filledData = false;
|
|
|
|
// This assumes the last call to a Windows API failed.
|
|
|
|
int errorCode = GetLastError();
|
|
|
|
if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
|
|
|
|
WIN32_FIND_DATA findData;
|
2012-01-18 13:44:42 +00:00
|
|
|
if (getFindData(fname.nativeFilePath(), findData)
|
2011-04-27 10:05:43 +00:00
|
|
|
&& findData.dwFileAttributes != INVALID_FILE_ATTRIBUTES) {
|
2012-01-18 13:44:42 +00:00
|
|
|
data.fillFromFindData(findData, true, fname.isDriveRoot());
|
2011-04-27 10:05:43 +00:00
|
|
|
filledData = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return filledData;
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data,
|
|
|
|
QFileSystemMetaData::MetaDataFlags what)
|
|
|
|
{
|
|
|
|
HANDLE fHandle = (HANDLE)_get_osfhandle(fd);
|
|
|
|
if (fHandle != INVALID_HANDLE_VALUE) {
|
|
|
|
return fillMetaData(fHandle, data, what);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
bool QFileSystemEngine::fillMetaData(HANDLE fHandle, QFileSystemMetaData &data,
|
|
|
|
QFileSystemMetaData::MetaDataFlags what)
|
|
|
|
{
|
|
|
|
data.entryFlags &= ~what;
|
|
|
|
clearWinStatData(data);
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
BY_HANDLE_FILE_INFORMATION fileInfo;
|
|
|
|
UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
|
|
|
if (GetFileInformationByHandle(fHandle , &fileInfo)) {
|
|
|
|
data.fillFromFindInfo(fileInfo);
|
|
|
|
}
|
|
|
|
SetErrorMode(oldmode);
|
2013-09-02 10:57:51 +00:00
|
|
|
#else // !Q_OS_WINRT
|
2013-11-12 08:54:53 +00:00
|
|
|
FILE_BASIC_INFO fileBasicInfo;
|
|
|
|
if (GetFileInformationByHandleEx(fHandle, FileBasicInfo, &fileBasicInfo, sizeof(fileBasicInfo))) {
|
|
|
|
data.fillFromFileAttribute(fileBasicInfo.FileAttributes);
|
|
|
|
data.creationTime_.dwHighDateTime = fileBasicInfo.CreationTime.HighPart;
|
|
|
|
data.creationTime_.dwLowDateTime = fileBasicInfo.CreationTime.LowPart;
|
|
|
|
data.lastAccessTime_.dwHighDateTime = fileBasicInfo.LastAccessTime.HighPart;
|
|
|
|
data.lastAccessTime_.dwLowDateTime = fileBasicInfo.LastAccessTime.LowPart;
|
|
|
|
data.lastWriteTime_.dwHighDateTime = fileBasicInfo.LastWriteTime.HighPart;
|
|
|
|
data.lastWriteTime_.dwLowDateTime = fileBasicInfo.LastWriteTime.LowPart;
|
|
|
|
if (!(data.fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
|
|
FILE_STANDARD_INFO fileStandardInfo;
|
|
|
|
if (GetFileInformationByHandleEx(fHandle, FileStandardInfo, &fileStandardInfo, sizeof(fileStandardInfo)))
|
|
|
|
data.size_ = fileStandardInfo.EndOfFile.QuadPart;
|
|
|
|
} else
|
|
|
|
data.size_ = 0;
|
|
|
|
data.knownFlagsMask |= QFileSystemMetaData::Times | QFileSystemMetaData::SizeAttribute;
|
2013-09-02 10:57:51 +00:00
|
|
|
}
|
|
|
|
#endif // Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
return data.hasFlags(what);
|
|
|
|
}
|
|
|
|
|
2012-11-23 13:54:18 +00:00
|
|
|
static bool isDirPath(const QString &dirPath, bool *existed);
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
//static
|
|
|
|
bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
|
|
|
|
QFileSystemMetaData::MetaDataFlags what)
|
|
|
|
{
|
|
|
|
what |= QFileSystemMetaData::WinLnkType | QFileSystemMetaData::WinStatFlags;
|
|
|
|
data.entryFlags &= ~what;
|
|
|
|
|
|
|
|
QFileSystemEntry fname;
|
|
|
|
data.knownFlagsMask |= QFileSystemMetaData::WinLnkType;
|
2012-11-23 13:54:18 +00:00
|
|
|
// Check for ".lnk": Directories named ".lnk" should be skipped, corrupted
|
|
|
|
// link files should still be detected as links.
|
|
|
|
const QString origFilePath = entry.filePath();
|
|
|
|
if (origFilePath.endsWith(QLatin1String(".lnk")) && !isDirPath(origFilePath, 0)) {
|
2011-04-27 10:05:43 +00:00
|
|
|
data.entryFlags |= QFileSystemMetaData::WinLnkType;
|
|
|
|
fname = QFileSystemEntry(readLink(entry));
|
|
|
|
} else {
|
|
|
|
fname = entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fname.isEmpty()) {
|
|
|
|
data.knownFlagsMask |= what;
|
|
|
|
clearWinStatData(data);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (what & QFileSystemMetaData::WinStatFlags) {
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
2013-09-02 10:57:51 +00:00
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
clearWinStatData(data);
|
|
|
|
WIN32_FIND_DATA findData;
|
|
|
|
// The memory structure for WIN32_FIND_DATA is same as WIN32_FILE_ATTRIBUTE_DATA
|
|
|
|
// for all members used by fillFindData().
|
|
|
|
bool ok = ::GetFileAttributesEx((wchar_t*)fname.nativeFilePath().utf16(), GetFileExInfoStandard,
|
|
|
|
reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA *>(&findData));
|
|
|
|
if (ok) {
|
|
|
|
data.fillFromFindData(findData, false, fname.isDriveRoot());
|
|
|
|
} else {
|
|
|
|
if (!tryFindFallback(fname, data))
|
2012-06-05 12:50:13 +00:00
|
|
|
if (!tryDriveUNCFallback(fname, data)) {
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2012-06-05 12:50:13 +00:00
|
|
|
SetErrorMode(oldmode);
|
2013-09-02 10:57:51 +00:00
|
|
|
#endif
|
2012-06-05 12:50:13 +00:00
|
|
|
return false;
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
SetErrorMode(oldmode);
|
2013-09-02 10:57:51 +00:00
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (what & QFileSystemMetaData::Permissions)
|
|
|
|
fillPermissions(fname, data, what);
|
|
|
|
if ((what & QFileSystemMetaData::LinkType)
|
|
|
|
&& data.missingFlags(QFileSystemMetaData::LinkType)) {
|
|
|
|
data.knownFlagsMask |= QFileSystemMetaData::LinkType;
|
|
|
|
if (data.fileAttribute_ & FILE_ATTRIBUTE_REPARSE_POINT) {
|
|
|
|
WIN32_FIND_DATA findData;
|
2012-01-18 13:44:42 +00:00
|
|
|
if (getFindData(fname.nativeFilePath(), findData))
|
|
|
|
data.fillFromFindData(findData, true);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
data.knownFlagsMask |= what;
|
|
|
|
return data.hasFlags(what);
|
|
|
|
}
|
|
|
|
|
2013-10-17 09:13:01 +00:00
|
|
|
static inline bool mkDir(const QString &path, DWORD *lastError = 0)
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2013-10-17 09:13:01 +00:00
|
|
|
if (lastError)
|
|
|
|
*lastError = 0;
|
|
|
|
const QString longPath = QFSFileEnginePrivate::longFileName(path);
|
|
|
|
const bool result = ::CreateDirectory((wchar_t*)longPath.utf16(), 0);
|
|
|
|
if (lastError) // Capture lastError before any QString is freed since custom allocators might change it.
|
|
|
|
*lastError = GetLastError();
|
|
|
|
return result;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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('\\');
|
|
|
|
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
DWORD fileAttrib = ::GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16());
|
2013-09-02 10:57:51 +00:00
|
|
|
#else // Q_OS_WINRT
|
|
|
|
DWORD fileAttrib = INVALID_FILE_ATTRIBUTES;
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA data;
|
|
|
|
if (::GetFileAttributesEx((const wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(), GetFileExInfoStandard, &data))
|
|
|
|
fileAttrib = data.dwFileAttributes;
|
|
|
|
#endif // Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
if (fileAttrib == INVALID_FILE_ATTRIBUTES) {
|
|
|
|
int errorCode = GetLastError();
|
|
|
|
if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
|
|
|
|
WIN32_FIND_DATA findData;
|
|
|
|
if (getFindData(QFSFileEnginePrivate::longFileName(path), findData))
|
|
|
|
fileAttrib = findData.dwFileAttributes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (existed)
|
|
|
|
*existed = fileAttrib != INVALID_FILE_ATTRIBUTES;
|
|
|
|
|
|
|
|
if (fileAttrib == INVALID_FILE_ATTRIBUTES)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return fileAttrib & FILE_ATTRIBUTE_DIRECTORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
|
|
|
|
{
|
|
|
|
QString dirName = entry.filePath();
|
|
|
|
if (createParents) {
|
|
|
|
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);
|
2013-06-19 10:32:45 +00:00
|
|
|
} else if (dirName.size() > 2
|
|
|
|
&& dirName.at(1) == QLatin1Char(':')) {
|
|
|
|
// Don't try to call mkdir with just a drive letter
|
|
|
|
oldslash = 2;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
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) {
|
2013-10-17 09:13:01 +00:00
|
|
|
DWORD lastError;
|
2011-04-27 10:05:43 +00:00
|
|
|
QString chunk = dirName.left(slash);
|
2013-10-17 09:13:01 +00:00
|
|
|
if (!mkDir(chunk, &lastError)) {
|
2013-07-11 07:48:07 +00:00
|
|
|
if (lastError == ERROR_ALREADY_EXISTS || lastError == ERROR_ACCESS_DENIED) {
|
2013-06-11 14:58:49 +00:00
|
|
|
bool existed = false;
|
|
|
|
if (isDirPath(chunk, &existed) && existed)
|
|
|
|
continue;
|
2014-03-19 14:20:06 +00:00
|
|
|
#ifdef Q_OS_WINRT
|
|
|
|
static QThreadStorage<QString> dataLocation;
|
|
|
|
if (!dataLocation.hasLocalData())
|
|
|
|
dataLocation.setLocalData(QDir::toNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::DataLocation)));
|
|
|
|
static QThreadStorage<QString> tempLocation;
|
|
|
|
if (!tempLocation.hasLocalData())
|
|
|
|
tempLocation.setLocalData(QDir::toNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::TempLocation)));
|
|
|
|
// We try to create something outside the sandbox, which is forbidden
|
|
|
|
// However we could still try to pass into the sandbox
|
|
|
|
if (dataLocation.localData().startsWith(chunk) || tempLocation.localData().startsWith(chunk))
|
|
|
|
continue;
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
2013-06-11 14:58:49 +00:00
|
|
|
return false;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return mkDir(entry.filePath());
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
|
|
|
|
{
|
|
|
|
QString dirName = entry.filePath();
|
|
|
|
if (removeEmptyParents) {
|
|
|
|
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
|
|
|
|
for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
|
2016-03-31 09:01:59 +00:00
|
|
|
const QStringRef chunkRef = dirName.leftRef(slash);
|
|
|
|
if (chunkRef.length() == 2 && chunkRef.at(0).isLetter() && chunkRef.at(1) == QLatin1Char(':'))
|
2011-04-27 10:05:43 +00:00
|
|
|
break;
|
2016-03-31 09:01:59 +00:00
|
|
|
const QString chunk = chunkRef.toString();
|
2011-04-27 10:05:43 +00:00
|
|
|
if (!isDirPath(chunk, 0))
|
|
|
|
return false;
|
|
|
|
if (!rmDir(chunk))
|
|
|
|
return oldslash != 0;
|
|
|
|
slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return rmDir(entry.filePath());
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
QString QFileSystemEngine::rootPath()
|
|
|
|
{
|
2016-03-23 09:25:46 +00:00
|
|
|
#if defined(Q_OS_WINRT)
|
2013-11-01 15:16:15 +00:00
|
|
|
// We specify the package root as root directory
|
|
|
|
QString ret = QLatin1String("/");
|
|
|
|
// Get package location
|
|
|
|
ComPtr<IPackageStatics> statics;
|
|
|
|
if (FAILED(GetActivationFactory(HStringReference(RuntimeClass_Windows_ApplicationModel_Package).Get(), &statics)))
|
|
|
|
return ret;
|
|
|
|
ComPtr<IPackage> package;
|
|
|
|
if (FAILED(statics->get_Current(&package)))
|
|
|
|
return ret;
|
|
|
|
ComPtr<IStorageFolder> installedLocation;
|
|
|
|
if (FAILED(package->get_InstalledLocation(&installedLocation)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ComPtr<IStorageItem> item;
|
|
|
|
if (FAILED(installedLocation.As(&item)))
|
|
|
|
return ret;
|
|
|
|
|
2014-05-23 15:16:54 +00:00
|
|
|
HString finalWinPath;
|
|
|
|
if (FAILED(item->get_Path(finalWinPath.GetAddressOf())))
|
2013-11-01 15:16:15 +00:00
|
|
|
return ret;
|
|
|
|
|
2015-12-11 12:42:28 +00:00
|
|
|
const QString qtWinPath = QDir::fromNativeSeparators(QString::fromWCharArray(finalWinPath.GetRawBuffer(nullptr)));
|
|
|
|
ret = qtWinPath.endsWith(QLatin1Char('/')) ? qtWinPath : qtWinPath + QLatin1Char('/');
|
2011-10-21 07:55:40 +00:00
|
|
|
#else
|
2015-04-16 17:54:52 +00:00
|
|
|
QString ret = QString::fromLatin1(qgetenv("SystemDrive"));
|
2011-04-27 10:05:43 +00:00
|
|
|
if (ret.isEmpty())
|
|
|
|
ret = QLatin1String("c:");
|
|
|
|
ret.append(QLatin1Char('/'));
|
|
|
|
#endif
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
QString QFileSystemEngine::homePath()
|
|
|
|
{
|
|
|
|
QString ret;
|
2016-12-19 09:34:32 +00:00
|
|
|
#if QT_CONFIG(fslibs)
|
2011-04-27 10:05:43 +00:00
|
|
|
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)) {
|
2015-04-16 17:54:52 +00:00
|
|
|
ret = QString::fromLocal8Bit(qgetenv("USERPROFILE"));
|
2011-04-27 10:05:43 +00:00
|
|
|
if (ret.isEmpty() || !QFile::exists(ret)) {
|
2015-04-16 17:54:52 +00:00
|
|
|
ret = QString::fromLocal8Bit(qgetenv("HOMEDRIVE"))
|
|
|
|
+ QString::fromLocal8Bit(qgetenv("HOMEPATH"));
|
2011-04-27 10:05:43 +00:00
|
|
|
if (ret.isEmpty() || !QFile::exists(ret)) {
|
2015-04-16 17:54:52 +00:00
|
|
|
ret = QString::fromLocal8Bit(qgetenv("HOME"));
|
2016-03-23 09:25:46 +00:00
|
|
|
if (ret.isEmpty() || !QFile::exists(ret))
|
|
|
|
ret = rootPath();
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return QDir::fromNativeSeparators(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QFileSystemEngine::tempPath()
|
|
|
|
{
|
|
|
|
QString ret;
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
wchar_t tempPath[MAX_PATH];
|
2012-07-16 13:35:58 +00:00
|
|
|
const DWORD len = GetTempPath(MAX_PATH, tempPath);
|
|
|
|
if (len) { // GetTempPath() can return short names, expand.
|
|
|
|
wchar_t longTempPath[MAX_PATH];
|
|
|
|
const DWORD longLen = GetLongPathName(tempPath, longTempPath, MAX_PATH);
|
|
|
|
ret = longLen && longLen < MAX_PATH ?
|
|
|
|
QString::fromWCharArray(longTempPath, longLen) :
|
|
|
|
QString::fromWCharArray(tempPath, len);
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
if (!ret.isEmpty()) {
|
|
|
|
while (ret.endsWith(QLatin1Char('\\')))
|
|
|
|
ret.chop(1);
|
|
|
|
ret = QDir::fromNativeSeparators(ret);
|
|
|
|
}
|
2013-09-02 10:57:51 +00:00
|
|
|
#else // !Q_OS_WINRT
|
|
|
|
ComPtr<IApplicationDataStatics> applicationDataStatics;
|
|
|
|
if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_ApplicationData).Get(), &applicationDataStatics)))
|
|
|
|
return ret;
|
|
|
|
ComPtr<IApplicationData> applicationData;
|
|
|
|
if (FAILED(applicationDataStatics->get_Current(&applicationData)))
|
|
|
|
return ret;
|
|
|
|
ComPtr<IStorageFolder> tempFolder;
|
|
|
|
if (FAILED(applicationData->get_TemporaryFolder(&tempFolder)))
|
|
|
|
return ret;
|
|
|
|
ComPtr<IStorageItem> tempFolderItem;
|
|
|
|
if (FAILED(tempFolder.As(&tempFolderItem)))
|
|
|
|
return ret;
|
2014-05-23 15:16:54 +00:00
|
|
|
HString path;
|
|
|
|
if (FAILED(tempFolderItem->get_Path(path.GetAddressOf())))
|
2013-09-02 10:57:51 +00:00
|
|
|
return ret;
|
2014-05-23 15:16:54 +00:00
|
|
|
ret = QDir::fromNativeSeparators(QString::fromWCharArray(path.GetRawBuffer(nullptr)));
|
2013-09-02 10:57:51 +00:00
|
|
|
#endif // Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
if (ret.isEmpty()) {
|
2011-08-26 09:03:55 +00:00
|
|
|
ret = QLatin1String("C:/tmp");
|
|
|
|
} else if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
|
|
|
|
ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
|
2011-04-27 10:05:43 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QFileSystemEngine::setCurrentPath(const QFileSystemEntry &entry)
|
|
|
|
{
|
|
|
|
QFileSystemMetaData meta;
|
|
|
|
fillMetaData(entry, meta, QFileSystemMetaData::ExistsAttribute | QFileSystemMetaData::DirectoryType);
|
|
|
|
if(!(meta.exists() && meta.isDirectory()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
//TODO: this should really be using nativeFilePath(), but that returns a path in long format \\?\c:\foo
|
|
|
|
//which causes many problems later on when it's returned through currentPath()
|
|
|
|
return ::SetCurrentDirectory(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(entry.filePath()).utf16())) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
QFileSystemEntry QFileSystemEngine::currentPath()
|
|
|
|
{
|
|
|
|
QString ret;
|
|
|
|
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, size);
|
|
|
|
delete [] newCurrentName;
|
|
|
|
} else {
|
|
|
|
ret = QString::fromWCharArray(currentName, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
|
|
|
|
ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
|
|
|
|
return QFileSystemEntry(ret, QFileSystemEntry::FromNativePath());
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
|
|
|
|
{
|
|
|
|
Q_ASSERT(false);
|
|
|
|
Q_UNUSED(source)
|
|
|
|
Q_UNUSED(target)
|
|
|
|
Q_UNUSED(error)
|
|
|
|
|
|
|
|
return false; // TODO implement; - code needs to be moved from qfsfileengine_win.cpp
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
|
|
|
|
{
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
bool ret = ::CopyFile((wchar_t*)source.nativeFilePath().utf16(),
|
|
|
|
(wchar_t*)target.nativeFilePath().utf16(), true) != 0;
|
2013-09-02 10:57:51 +00:00
|
|
|
#else // !Q_OS_WINRT
|
|
|
|
COPYFILE2_EXTENDED_PARAMETERS copyParams = {
|
|
|
|
sizeof(copyParams), COPY_FILE_FAIL_IF_EXISTS, NULL, NULL, NULL
|
|
|
|
};
|
2016-04-27 15:40:56 +00:00
|
|
|
HRESULT hres = ::CopyFile2((const wchar_t*)source.nativeFilePath().utf16(),
|
|
|
|
(const wchar_t*)target.nativeFilePath().utf16(), ©Params);
|
|
|
|
bool ret = SUCCEEDED(hres);
|
2013-09-02 10:57:51 +00:00
|
|
|
#endif // Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
if(!ret)
|
|
|
|
error = QSystemError(::GetLastError(), QSystemError::NativeError);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
|
|
|
|
{
|
2013-09-02 10:57:51 +00:00
|
|
|
#ifndef Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
bool ret = ::MoveFile((wchar_t*)source.nativeFilePath().utf16(),
|
|
|
|
(wchar_t*)target.nativeFilePath().utf16()) != 0;
|
2013-09-02 10:57:51 +00:00
|
|
|
#else // !Q_OS_WINRT
|
|
|
|
bool ret = ::MoveFileEx((const wchar_t*)source.nativeFilePath().utf16(),
|
|
|
|
(const wchar_t*)target.nativeFilePath().utf16(), 0) != 0;
|
|
|
|
#endif // Q_OS_WINRT
|
2011-04-27 10:05:43 +00:00
|
|
|
if(!ret)
|
|
|
|
error = QSystemError(::GetLastError(), QSystemError::NativeError);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
|
|
|
|
{
|
|
|
|
bool ret = ::DeleteFile((wchar_t*)entry.nativeFilePath().utf16()) != 0;
|
|
|
|
if(!ret)
|
|
|
|
error = QSystemError(::GetLastError(), QSystemError::NativeError);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error,
|
|
|
|
QFileSystemMetaData *data)
|
|
|
|
{
|
|
|
|
Q_UNUSED(data);
|
|
|
|
int mode = 0;
|
|
|
|
|
2015-04-07 12:46:40 +00:00
|
|
|
if (permissions & (QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther))
|
2011-04-27 10:05:43 +00:00
|
|
|
mode |= _S_IREAD;
|
2015-04-07 12:46:40 +00:00
|
|
|
if (permissions & (QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther))
|
2011-04-27 10:05:43 +00:00
|
|
|
mode |= _S_IWRITE;
|
|
|
|
|
|
|
|
if (mode == 0) // not supported
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool ret = (::_wchmod((wchar_t*)entry.nativeFilePath().utf16(), mode) == 0);
|
|
|
|
if(!ret)
|
|
|
|
error = QSystemError(errno, QSystemError::StandardLibraryError);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
|
|
|
|
{
|
2016-12-14 10:18:50 +00:00
|
|
|
SYSTEMTIME sTime;
|
2011-04-27 10:05:43 +00:00
|
|
|
FileTimeToSystemTime(time, &sTime);
|
2016-12-14 10:18:50 +00:00
|
|
|
return QDateTime(QDate(sTime.wYear, sTime.wMonth, sTime.wDay),
|
|
|
|
QTime(sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds),
|
|
|
|
Qt::UTC);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QDateTime QFileSystemMetaData::creationTime() const
|
|
|
|
{
|
|
|
|
return fileTimeToQDateTime(&creationTime_);
|
|
|
|
}
|
|
|
|
QDateTime QFileSystemMetaData::modificationTime() const
|
|
|
|
{
|
|
|
|
return fileTimeToQDateTime(&lastWriteTime_);
|
|
|
|
}
|
|
|
|
QDateTime QFileSystemMetaData::accessTime() const
|
|
|
|
{
|
|
|
|
return fileTimeToQDateTime(&lastAccessTime_);
|
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|