summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qfilesystemengine_win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io/qfilesystemengine_win.cpp')
-rw-r--r--src/corelib/io/qfilesystemengine_win.cpp1073
1 files changed, 721 insertions, 352 deletions
diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp
index 0d3dd2e0b2..3ec32e31a1 100644
--- a/src/corelib/io/qfilesystemengine_win.cpp
+++ b/src/corelib/io/qfilesystemengine_win.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** 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
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** 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.
-**
-** 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.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qfilesystemengine_p.h"
#include "qoperatingsystemversion.h"
@@ -43,6 +7,7 @@
#include "qsysinfo.h"
#include "qscopeguard.h"
#include "private/qabstractfileengine_p.h"
+#include "private/qfiledevice_p.h"
#include "private/qfsfileengine_p.h"
#include <private/qsystemlibrary_p.h>
#include <qdebug.h>
@@ -55,6 +20,7 @@
#if QT_CONFIG(regularexpression)
#include "qregularexpression.h"
#endif
+#include "qstring.h"
#include <sys/types.h>
#include <direct.h>
@@ -71,6 +37,8 @@
#define SECURITY_WIN32
#include <security.h>
+#include <QtCore/private/qfunctions_win_p.h>
+
#ifndef SPI_GETPLATFORMTYPE
#define SPI_GETPLATFORMTYPE 257
#endif
@@ -132,26 +100,24 @@ typedef struct _REPARSE_DATA_BUFFER {
# 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)
+# define FSCTL_GET_REPARSE_POINT \
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif
-#if defined(QT_BOOTSTRAPPED)
-# define QT_FEATURE_fslibs -1
-#else
-# define QT_FEATURE_fslibs 1
-#endif // QT_BOOTSTRAPPED
-
#if QT_CONFIG(fslibs)
#include <aclapi.h>
+#include <authz.h>
#include <userenv.h>
-static TRUSTEE_W currentUserTrusteeW;
-static TRUSTEE_W worldTrusteeW;
-static PSID currentUserSID = 0;
-static PSID worldSID = 0;
+static PSID currentUserSID = nullptr;
+static PSID currentGroupSID = nullptr;
+static PSID worldSID = nullptr;
static HANDLE currentUserImpersonatedToken = nullptr;
+#endif // fslibs
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+#if QT_CONFIG(fslibs)
namespace {
struct GlobalSid
{
@@ -162,12 +128,15 @@ struct GlobalSid
GlobalSid::~GlobalSid()
{
free(currentUserSID);
- currentUserSID = 0;
+ currentUserSID = nullptr;
+
+ free(currentGroupSID);
+ currentGroupSID = nullptr;
// worldSID was allocated with AllocateAndInitializeSid so it needs to be freed with FreeSid
if (worldSID) {
::FreeSid(worldSID);
- worldSID = 0;
+ worldSID = nullptr;
}
if (currentUserImpersonatedToken) {
@@ -176,62 +145,464 @@ GlobalSid::~GlobalSid()
}
}
-GlobalSid::GlobalSid()
+/*
+ Helper for GetTokenInformation that allocates chunk of memory to hold the requested information.
+
+ The memory size is determined by doing a dummy call first. The returned memory should be
+ freed by calling free().
+*/
+template<typename T>
+static T *getTokenInfo(HANDLE token, TOKEN_INFORMATION_CLASS infoClass)
{
- {
- {
- // Create TRUSTEE for current user
- HANDLE hnd = ::GetCurrentProcess();
- HANDLE token = 0;
- if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) {
- 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) {
- void *tokenBuffer = malloc(retsize);
- Q_CHECK_PTR(tokenBuffer);
- if (::GetTokenInformation(token, TokenUser, tokenBuffer, retsize, &retsize)) {
- PSID tokenSid = reinterpret_cast<PTOKEN_USER>(tokenBuffer)->User.Sid;
- DWORD sidLen = ::GetLengthSid(tokenSid);
- currentUserSID = reinterpret_cast<PSID>(malloc(sidLen));
- Q_CHECK_PTR(currentUserSID);
- if (::CopySid(sidLen, currentUserSID, tokenSid))
- BuildTrusteeWithSid(&currentUserTrusteeW, currentUserSID);
- }
- free(tokenBuffer);
- }
- ::CloseHandle(token);
- }
+ DWORD retsize = 0;
+ GetTokenInformation(token, infoClass, nullptr, 0, &retsize);
+ if (retsize) {
+ void *tokenBuffer = malloc(retsize);
+ if (::GetTokenInformation(token, infoClass, tokenBuffer, retsize, &retsize))
+ return reinterpret_cast<T *>(tokenBuffer);
+ else
+ free(tokenBuffer);
+ }
+ return nullptr;
+}
- token = nullptr;
- if (::OpenProcessToken(hnd, TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | STANDARD_RIGHTS_READ, &token)) {
- ::DuplicateToken(token, SecurityImpersonation, &currentUserImpersonatedToken);
- ::CloseHandle(token);
- }
+/*
+ Takes a copy of the original SID and stores it into dstSid.
+ The copy can be destroyed using free().
+*/
+static void copySID(PSID &dstSid, PSID srcSid)
+{
+ DWORD sidLen = GetLengthSid(srcSid);
+ dstSid = reinterpret_cast<PSID>(malloc(sidLen));
+ Q_CHECK_PTR(dstSid);
+ CopySid(sidLen, dstSid, srcSid);
+}
- {
- // Create TRUSTEE for Everyone (World)
- SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY };
- if (AllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &worldSID))
- BuildTrusteeWithSid(&worldTrusteeW, worldSID);
- }
+GlobalSid::GlobalSid()
+{
+ HANDLE hnd = ::GetCurrentProcess();
+ HANDLE token = nullptr;
+ if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) {
+ // Create SID for current user
+ if (auto info = getTokenInfo<TOKEN_USER>(token, TokenUser)) {
+ copySID(currentUserSID, info->User.Sid);
+ free(info);
}
+
+ // Create SID for the current user's primary group.
+ if (auto info = getTokenInfo<TOKEN_GROUPS>(token, TokenGroups)) {
+ copySID(currentGroupSID, info->Groups[0].Sid);
+ free(info);
+ }
+ ::CloseHandle(token);
+ }
+
+ token = nullptr;
+ if (::OpenProcessToken(hnd,
+ TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | STANDARD_RIGHTS_READ,
+ &token)) {
+ ::DuplicateToken(token, SecurityImpersonation, &currentUserImpersonatedToken);
+ ::CloseHandle(token);
}
+
+ // Create SID for Everyone (World)
+ SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY };
+ AllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &worldSID);
}
Q_GLOBAL_STATIC(GlobalSid, initGlobalSid)
-QT_END_NAMESPACE
+/*!
+ \class QAuthzResourceManager
+ \internal
+
+ RAII wrapper around Windows Authz resource manager.
+*/
+class QAuthzResourceManager
+{
+public:
+ QAuthzResourceManager();
+ ~QAuthzResourceManager();
+
+ bool isValid() const { return resourceManager != nullptr; }
+
+private:
+ friend class QAuthzClientContext;
+ Q_DISABLE_COPY_MOVE(QAuthzResourceManager)
+
+ AUTHZ_RESOURCE_MANAGER_HANDLE resourceManager;
+};
+
+/*!
+ \class QAuthzClientContext
+ \internal
+
+ RAII wrapper around Windows Authz client context.
+*/
+class QAuthzClientContext
+{
+public:
+ // Tag to differentiate SID and TOKEN constructors. Those two types are pointers to void.
+ struct TokenTag
+ {
+ };
+
+ QAuthzClientContext(const QAuthzResourceManager &rm, PSID pSID);
+ QAuthzClientContext(const QAuthzResourceManager &rm, HANDLE tokenHandle, TokenTag);
+
+ ~QAuthzClientContext();
+
+ bool isValid() const { return context != nullptr; }
+
+ static constexpr ACCESS_MASK InvalidAccess = ~ACCESS_MASK(0);
+
+ ACCESS_MASK accessMask(PSECURITY_DESCRIPTOR pSD) const;
+
+private:
+ Q_DISABLE_COPY_MOVE(QAuthzClientContext)
+ AUTHZ_CLIENT_CONTEXT_HANDLE context = nullptr;
+};
+
+QAuthzResourceManager::QAuthzResourceManager()
+{
+ if (!AuthzInitializeResourceManager(AUTHZ_RM_FLAG_NO_AUDIT, nullptr, nullptr, nullptr, nullptr,
+ &resourceManager)) {
+ resourceManager = nullptr;
+ }
+}
+
+QAuthzResourceManager::~QAuthzResourceManager()
+{
+ if (resourceManager)
+ AuthzFreeResourceManager(resourceManager);
+}
+
+/*!
+ \internal
+
+ Create an Authz client context from a security identifier.
+
+ The created context will not include any group information associated with \a pSID.
+*/
+QAuthzClientContext::QAuthzClientContext(const QAuthzResourceManager &rm, PSID pSID)
+{
+ if (!rm.isValid())
+ return;
+
+ LUID unusedId = {};
+
+ if (!AuthzInitializeContextFromSid(AUTHZ_SKIP_TOKEN_GROUPS, pSID, rm.resourceManager, nullptr,
+ unusedId, nullptr, &context)) {
+ context = nullptr;
+ }
+}
+
+/*!
+ \internal
+
+ Create an Authz client context from a token handle.
+*/
+QAuthzClientContext::QAuthzClientContext(const QAuthzResourceManager &rm, HANDLE tokenHandle,
+ TokenTag)
+{
+ if (!rm.isValid())
+ return;
+
+ LUID unusedId = {};
+
+ if (!AuthzInitializeContextFromToken(0, tokenHandle, rm.resourceManager, nullptr, unusedId,
+ nullptr, &context)) {
+ context = nullptr;
+ }
+}
+
+QAuthzClientContext::~QAuthzClientContext()
+{
+ if (context)
+ AuthzFreeContext(context);
+}
+
+/*!
+ \internal
+
+ Returns permissions that are granted to this client by \a pSD.
+
+ Returns \c InvalidAccess in case of an error.
+*/
+ACCESS_MASK QAuthzClientContext::accessMask(PSECURITY_DESCRIPTOR pSD) const
+{
+ if (!isValid())
+ return InvalidAccess;
+
+ AUTHZ_ACCESS_REQUEST accessRequest = {};
+ AUTHZ_ACCESS_REPLY accessReply = {};
+ ACCESS_MASK accessMask = 0;
+ DWORD error = 0;
+
+ accessRequest.DesiredAccess = MAXIMUM_ALLOWED;
+
+ accessReply.ResultListLength = 1;
+ accessReply.GrantedAccessMask = &accessMask;
+ accessReply.Error = &error;
+
+ if (!AuthzAccessCheck(0, context, &accessRequest, nullptr, pSD, nullptr, 0, &accessReply,
+ nullptr)
+ || error != 0) {
+ return InvalidAccess;
+ }
+
+ return accessMask;
+}
+
+enum NonSpecificPermission {
+ ReadPermission = 0x4,
+ WritePermission = 0x2,
+ ExePermission = 0x1,
+ AllPermissions = ReadPermission | WritePermission | ExePermission
+};
+Q_DECLARE_FLAGS(NonSpecificPermissions, NonSpecificPermission)
+Q_DECLARE_OPERATORS_FOR_FLAGS(NonSpecificPermissions)
+
+enum PermissionTag { OtherTag = 0, GroupTag = 4, UserTag = 8, OwnerTag = 12 };
+
+constexpr NonSpecificPermissions toNonSpecificPermissions(PermissionTag tag,
+ QFileDevice::Permissions permissions)
+{
+ return NonSpecificPermissions::fromInt((permissions.toInt() >> int(tag)) & 0x7);
+}
+
+[[maybe_unused]] // Not currently used; included to show how to do it (without bit-rotting).
+constexpr QFileDevice::Permissions toSpecificPermissions(PermissionTag tag,
+ NonSpecificPermissions permissions)
+{
+ return QFileDevice::Permissions::fromInt(permissions.toInt() << int(tag));
+}
} // anonymous namespace
#endif // QT_CONFIG(fslibs)
-QT_BEGIN_NAMESPACE
+#if QT_DEPRECATED_SINCE(6,6)
+int qt_ntfs_permission_lookup = 0;
+#endif
+
+static QBasicAtomicInt qt_ntfs_permission_lookup_v2 = Q_BASIC_ATOMIC_INITIALIZER(0);
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+
+bool qEnableNtfsPermissionChecks() noexcept
+{
+ return qt_ntfs_permission_lookup_v2.fetchAndAddRelaxed(1)
+QT_IF_DEPRECATED_SINCE(6, 6, /*nothing*/, + qt_ntfs_permission_lookup)
+ != 0;
+}
-Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
+bool qDisableNtfsPermissionChecks() noexcept
+{
+ return qt_ntfs_permission_lookup_v2.fetchAndSubRelaxed(1)
+QT_IF_DEPRECATED_SINCE(6, 6, /*nothing*/, + qt_ntfs_permission_lookup)
+ == 1;
+}
+
+bool qAreNtfsPermissionChecksEnabled() noexcept
+{
+ return qt_ntfs_permission_lookup_v2.loadRelaxed()
+QT_IF_DEPRECATED_SINCE(6, 6, /*nothing*/, + qt_ntfs_permission_lookup)
+ ;
+}
+QT_WARNING_POP
+
+/*!
+ \class QNativeFilePermissions
+ \internal
+
+ This class can be used to produce a security descriptor that contains ACL that produces
+ result similar to what is expected for POSIX permission corresponding to the supplied
+ \c QFileDevice::Permissions value. When supplied optional value is empty, a null
+ security descriptor is produced. Files or directories with such null security descriptor
+ will inherit ACLs from parent directories. Otherwise an ACL is generated and applied to
+ the security descriptor. The created ACL has permission bits set similar to what Cygwin
+ does. Unlike Cygwin, this code tries to reorder the access control entries (ACE) inside
+ the ACL to match the canonical ordering (deny ACEs followed by allow ACEs) if possible.
+
+ The default ordering of ACEs is as follows:
+
+ * User deny ACE, only lists permission that may be granted by the subsequent Group and
+ Other allow ACEs.
+ * User allow ACE.
+ * Group deny ACE, only lists permissions that may be granted by the subsequent Other
+ allow ACE.
+ * Group allow ACE.
+ * Other allow ACE.
+
+ Any ACEs that would have zero mask are skipped. Group deny ACE may be moved to before
+ User allow ACE if these 2 ACEs don't have any common mask bits set. This allows use of
+ canonical ordering in more cases. ACLs for permissions with group having less permissions
+ than both user and others (ex.: 0757) are still in noncanonical order. Files with
+ noncanonical ACLs generate warnings when one tries to edit permissions with Windows GUI,
+ and don't work correctly with API like GetEffectiveRightsFromAcl(), but otherwise access
+ checks work fine and such ACLs can still be edited with the "Advanced" GUI.
+*/
+QNativeFilePermissions::QNativeFilePermissions(std::optional<QFileDevice::Permissions> perms,
+ bool isDir)
+{
+#if QT_CONFIG(fslibs)
+ if (!perms) {
+ ok = true;
+ return;
+ }
+
+ initGlobalSid();
+
+ const auto permissions = *perms;
+
+ PACL acl = reinterpret_cast<PACL>(aclStorage);
+
+ if (!InitializeAcl(acl, sizeof(aclStorage), ACL_REVISION))
+ return;
+
+ struct Masks
+ {
+ ACCESS_MASK denyMask, allowMask;
+ };
+
+ auto makeMasks = [isDir](NonSpecificPermissions allowPermissions,
+ NonSpecificPermissions denyPermissions, bool owner) {
+ constexpr ACCESS_MASK AllowRead = FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA;
+ constexpr ACCESS_MASK DenyRead = FILE_READ_DATA | FILE_READ_EA;
+
+ constexpr ACCESS_MASK AllowWrite =
+ FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA;
+ constexpr ACCESS_MASK DenyWrite = AllowWrite | FILE_DELETE_CHILD;
+ constexpr ACCESS_MASK DenyWriteOwner =
+ FILE_WRITE_DATA | FILE_WRITE_EA | FILE_APPEND_DATA | FILE_DELETE_CHILD;
+
+ constexpr ACCESS_MASK AllowExe = FILE_EXECUTE;
+ constexpr ACCESS_MASK DenyExe = AllowExe;
+
+ constexpr ACCESS_MASK StdRightsOther =
+ STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | SYNCHRONIZE;
+ constexpr ACCESS_MASK StdRightsOwner =
+ STANDARD_RIGHTS_ALL | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE;
+
+ ACCESS_MASK allow = owner ? StdRightsOwner : StdRightsOther;
+ ACCESS_MASK deny = 0;
+
+ if (denyPermissions & ReadPermission)
+ deny |= DenyRead;
+
+ if (denyPermissions & WritePermission)
+ deny |= owner ? DenyWriteOwner : DenyWrite;
+
+ if (denyPermissions & ExePermission)
+ deny |= DenyExe;
+
+ if (allowPermissions & ReadPermission)
+ allow |= AllowRead;
+
+ if (allowPermissions & WritePermission)
+ allow |= AllowWrite;
+
+ if (allowPermissions & ExePermission)
+ allow |= AllowExe;
+
+ // Give the owner "full access" if all the permissions are allowed
+ if (owner && allowPermissions == AllPermissions)
+ allow |= FILE_DELETE_CHILD;
+
+ if (isDir
+ && (allowPermissions & (WritePermission | ExePermission))
+ == (WritePermission | ExePermission)) {
+ allow |= FILE_DELETE_CHILD;
+ }
+
+ return Masks { deny, allow };
+ };
+
+ auto userPermissions = toNonSpecificPermissions(OwnerTag, permissions)
+ | toNonSpecificPermissions(UserTag, permissions);
+ auto groupPermissions = toNonSpecificPermissions(GroupTag, permissions);
+ auto otherPermissions = toNonSpecificPermissions(OtherTag, permissions);
+
+ auto userMasks = makeMasks(userPermissions,
+ ~userPermissions & (groupPermissions | otherPermissions), true);
+ auto groupMasks = makeMasks(groupPermissions, ~groupPermissions & otherPermissions, false);
+ auto otherMasks = makeMasks(otherPermissions, {}, false);
+
+ const DWORD aceFlags = isDir ? OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE : 0;
+ const bool reorderGroupDeny = (groupMasks.denyMask & userMasks.allowMask) == 0;
+
+ const auto addDenyAce = [acl, aceFlags](const Masks &masks, PSID pSID) {
+ if (masks.denyMask)
+ return AddAccessDeniedAceEx(acl, ACL_REVISION, aceFlags, masks.denyMask, pSID);
+ return TRUE;
+ };
+
+ const auto addAllowAce = [acl, aceFlags](const Masks &masks, PSID pSID) {
+ if (masks.allowMask)
+ return AddAccessAllowedAceEx(acl, ACL_REVISION, aceFlags, masks.allowMask, pSID);
+ return TRUE;
+ };
+
+ if (!addDenyAce(userMasks, currentUserSID))
+ return;
+
+ if (reorderGroupDeny) {
+ if (!addDenyAce(groupMasks, currentGroupSID))
+ return;
+ }
+
+ if (!addAllowAce(userMasks, currentUserSID))
+ return;
+
+ if (!reorderGroupDeny) {
+ if (!addDenyAce(groupMasks, currentGroupSID))
+ return;
+ }
+
+ if (!addAllowAce(groupMasks, currentGroupSID))
+ return;
+
+ if (!addAllowAce(otherMasks, worldSID))
+ return;
+
+ if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
+ return;
+
+ if (!SetSecurityDescriptorOwner(&sd, currentUserSID, FALSE))
+ return;
+
+ if (!SetSecurityDescriptorGroup(&sd, currentGroupSID, FALSE))
+ return;
+
+ if (!SetSecurityDescriptorDacl(&sd, TRUE, acl, FALSE))
+ return;
+
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = &sd;
+ sa.bInheritHandle = FALSE;
+
+ isNull = false;
+#else
+ Q_UNUSED(perms);
+ Q_UNUSED(isDir);
+#endif // QT_CONFIG(fslibs)
+ ok = true;
+}
+
+/*!
+ \internal
+ Return pointer to a \c SECURITY_ATTRIBUTES object describing the permissions.
+
+ The returned pointer many be null if default permissions were requested or
+ during bootstrap. The callers must call \c isOk() to check if the object
+ was successfully constructed before using this method.
+*/
+SECURITY_ATTRIBUTES *QNativeFilePermissions::securityAttributes()
+{
+ Q_ASSERT(ok);
+ return isNull ? nullptr : &sa;
+}
static inline bool toFileTime(const QDateTime &date, FILETIME *fileTime)
{
@@ -250,7 +621,7 @@ static inline bool toFileTime(const QDateTime &date, FILETIME *fileTime)
lTime.wMilliseconds = t.msec();
lTime.wDayOfWeek = d.dayOfWeek() % 7;
- if (!::TzSpecificLocalTimeToSystemTime(0, &lTime, &sTime))
+ if (!::TzSpecificLocalTimeToSystemTime(nullptr, &lTime, &sTime))
return false;
} else {
QDateTime utcDate = date.toUTC();
@@ -273,19 +644,17 @@ static inline bool toFileTime(const QDateTime &date, FILETIME *fileTime)
static QString readSymLink(const QFileSystemEntry &link)
{
QString result;
- HANDLE handle = CreateFile((wchar_t*)link.nativeFilePath().utf16(),
- FILE_READ_EA,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- 0,
+ HANDLE handle = CreateFile((wchar_t *)link.nativeFilePath().utf16(), FILE_READ_EA,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
- 0);
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
if (handle != INVALID_HANDLE_VALUE) {
DWORD bufsize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)malloc(bufsize);
Q_CHECK_PTR(rdb);
DWORD retsize = 0;
- if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, 0, 0, rdb, bufsize, &retsize, 0)) {
+ if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, rdb, bufsize, &retsize,
+ nullptr)) {
if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
int length = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
int offset = rdb->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
@@ -305,15 +674,19 @@ static QString readSymLink(const QFileSystemEntry &link)
#if QT_CONFIG(fslibs) && QT_CONFIG(regularexpression)
initGlobalSid();
- QRegularExpression matchVolumeRe(QLatin1String("^Volume\\{([a-z]|[0-9]|-)+\\}\\\\"), QRegularExpression::CaseInsensitiveOption);
+ QRegularExpression matchVolumeRe("^Volume\\{([a-z]|[0-9]|-)+\\}\\\\"_L1,
+ QRegularExpression::CaseInsensitiveOption);
auto matchVolume = matchVolumeRe.match(result);
if (matchVolume.hasMatch()) {
Q_ASSERT(matchVolume.capturedStart() == 0);
DWORD len;
wchar_t buffer[MAX_PATH];
- const QString volumeName = QLatin1String("\\\\?\\") + matchVolume.captured();
- if (GetVolumePathNamesForVolumeName(reinterpret_cast<LPCWSTR>(volumeName.utf16()), buffer, MAX_PATH, &len) != 0)
+ const QString volumeName = "\\\\?\\"_L1 + matchVolume.captured();
+ if (GetVolumePathNamesForVolumeName(reinterpret_cast<LPCWSTR>(volumeName.utf16()),
+ buffer, MAX_PATH, &len)
+ != 0) {
result.replace(0, matchVolume.capturedLength(), QString::fromWCharArray(buffer));
+ }
}
#endif // QT_CONFIG(fslibs)
}
@@ -325,20 +698,16 @@ static QString readLink(const QFileSystemEntry &link)
#if QT_CONFIG(fslibs)
QString ret;
- bool neededCoInit = false;
IShellLink *psl; // pointer to IShellLink i/f
WIN32_FIND_DATA wfd;
wchar_t szGotPath[MAX_PATH];
+ QComHelper comHelper;
+
// Get pointer to the IShellLink interface.
- HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl);
+ HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, 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);
@@ -354,8 +723,6 @@ static QString readLink(const QFileSystemEntry &link)
}
psl->Release();
}
- if (neededCoInit)
- CoUninitialize();
return ret;
#else
@@ -367,11 +734,12 @@ static QString readLink(const QFileSystemEntry &link)
static bool uncShareExists(const QString &server)
{
// This code assumes the UNC path is always like \\?\UNC\server...
- const auto parts = QStringView{server}.split(QLatin1Char('\\'), Qt::SkipEmptyParts);
+ const auto parts = QStringView{server}.split(u'\\', Qt::SkipEmptyParts);
if (parts.count() >= 3) {
QStringList shares;
- if (QFileSystemEngine::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(2), &shares))
- return parts.count() < 4 || shares.contains(parts.at(3).toString(), Qt::CaseInsensitive);
+ if (QFileSystemEngine::uncListSharesOnServer("\\\\"_L1 + parts.at(2), &shares))
+ return parts.count() < 4
+ || shares.contains(parts.at(3).toString(), Qt::CaseInsensitive);
}
return false;
}
@@ -379,11 +747,11 @@ static bool uncShareExists(const QString &server)
static inline bool getFindData(QString path, WIN32_FIND_DATA &findData)
{
// path should not end with a trailing slash
- while (path.endsWith(QLatin1Char('\\')))
+ while (path.endsWith(u'\\'))
path.chop(1);
// can't handle drives
- if (!path.endsWith(QLatin1Char(':'))) {
+ if (!path.endsWith(u':')) {
HANDLE hFind = ::FindFirstFile((wchar_t*)path.utf16(), &findData);
if (hFind != INVALID_HANDLE_VALUE) {
::FindClose(hFind);
@@ -394,7 +762,6 @@ static inline bool getFindData(QString path, WIN32_FIND_DATA &findData)
return false;
}
-#if defined(__IFileOperation_INTERFACE_DEFINED__)
class FileOperationProgressSink : public IFileOperationProgressSink
{
public:
@@ -455,9 +822,10 @@ public:
return (dwFlags & TSF_DELETE_RECYCLE_IF_POSSIBLE) ? S_OK : E_FAIL;
}
HRESULT STDMETHODCALLTYPE PostDeleteItem(DWORD /* dwFlags */, IShellItem * /* psiItem */,
- HRESULT /* hrDelete */,
+ HRESULT hrDelete,
IShellItem *psiNewlyCreated) override
{
+ deleteResult = hrDelete;
if (psiNewlyCreated) {
wchar_t *pszName = nullptr;
psiNewlyCreated->GetDisplayName(SIGDN_FILESYSPATH, &pszName);
@@ -478,10 +846,10 @@ public:
HRESULT STDMETHODCALLTYPE ResumeTimer() override { return S_OK; }
QString targetPath;
+ HRESULT deleteResult = S_OK;
private:
ULONG ref;
};
-#endif
bool QFileSystemEngine::uncListSharesOnServer(const QString &server, QStringList *list)
{
@@ -489,7 +857,8 @@ bool QFileSystemEngine::uncListSharesOnServer(const QString &server, QStringList
SHARE_INFO_1 *BufPtr, *p;
DWORD er = 0, tr = 0, resume = 0, i;
do {
- res = NetShareEnum((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume);
+ res = NetShareEnum((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) {
@@ -517,6 +886,18 @@ void QFileSystemEngine::clearWinStatData(QFileSystemMetaData &data)
QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
QFileSystemMetaData &data)
{
+ QFileSystemEntry ret = getRawLinkPath(link, data);
+ if (!ret.isEmpty() && ret.isRelative()) {
+ QString target = absoluteName(link).path() + u'/' + ret.filePath();
+ ret = QFileSystemEntry(QDir::cleanPath(target));
+ }
+ return ret;
+}
+
+//static
+QFileSystemEntry QFileSystemEngine::getRawLinkPath(const QFileSystemEntry &link,
+ QFileSystemMetaData &data)
+{
Q_CHECK_FILE_NAME(link, link);
if (data.missingFlags(QFileSystemMetaData::LinkType))
@@ -527,12 +908,7 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
target = readLink(link);
else if (data.isLink())
target = readSymLink(link);
- QFileSystemEntry ret(target);
- if (!target.isEmpty() && ret.isRelative()) {
- target.prepend(absoluteName(link).path() + QLatin1Char('/'));
- ret = QFileSystemEntry(QDir::cleanPath(target));
- }
- return ret;
+ return QFileSystemEntry(target);
}
//static
@@ -549,14 +925,15 @@ QFileSystemEntry QFileSystemEngine::junctionTarget(const QFileSystemEntry &link,
target = readSymLink(link);
QFileSystemEntry ret(target);
if (!target.isEmpty() && ret.isRelative()) {
- target.prepend(absoluteName(link).path() + QLatin1Char('/'));
+ target.prepend(absoluteName(link).path() + u'/');
ret = QFileSystemEntry(QDir::cleanPath(target));
}
return ret;
}
//static
-QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
+QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry,
+ QFileSystemMetaData &data)
{
Q_CHECK_FILE_NAME(entry, entry);
@@ -591,8 +968,8 @@ QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path)
// (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(' '));
+ if (!path.isEmpty() && path.at(path.size() - 1) == u' ')
+ absPath.append(u' ');
return absPath;
}
@@ -609,17 +986,17 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
else
ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(entry.filePath()));
} else {
- ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + entry.filePath());
+ ret = QDir::cleanPath(QDir::currentPath() + u'/' + entry.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('/')) {
+ if (ret.at(0) != u'/') {
Q_ASSERT(ret.length() >= 2);
Q_ASSERT(ret.at(0).isLetter());
- Q_ASSERT(ret.at(1) == QLatin1Char(':'));
+ Q_ASSERT(ret.at(1) == u':');
// Force uppercase drive letters.
ret[0] = ret.at(0).toUpper();
@@ -627,15 +1004,6 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
return QFileSystemEntry(ret, QFileSystemEntry::FromInternalPath());
}
-#if defined(Q_CC_MINGW) && WINVER < 0x0602 && _WIN32_WINNT < _WIN32_WINNT_WIN8 // Windows 8 onwards
-
-typedef struct _FILE_ID_INFO {
- ULONGLONG VolumeSerialNumber;
- FILE_ID_128 FileId;
-} FILE_ID_INFO, *PFILE_ID_INFO;
-
-#endif // if defined(Q_CC_MINGW) && WINVER < 0x0602 && _WIN32_WINNT < _WIN32_WINNT_WIN8
-
// File ID for Windows up to version 7 and FAT32 drives
static inline QByteArray fileId(HANDLE handle)
{
@@ -657,15 +1025,19 @@ QByteArray fileIdWin8(HANDLE handle)
#if !defined(QT_BOOTSTRAPPED)
QByteArray result;
FILE_ID_INFO infoEx;
- if (GetFileInformationByHandleEx(handle,
- static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8
- &infoEx, sizeof(FILE_ID_INFO))) {
+ if (GetFileInformationByHandleEx(
+ handle,
+ static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8
+ &infoEx, sizeof(FILE_ID_INFO))) {
result = QByteArray::number(infoEx.VolumeSerialNumber, 16);
result += ':';
// 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();
+ result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId),
+ int(sizeof(infoEx.FileId)))
+ .toHex();
} else {
- result = fileId(handle); // GetFileInformationByHandleEx() is observed to fail for FAT32, QTBUG-74759
+ // GetFileInformationByHandleEx() is observed to fail for FAT32, QTBUG-74759
+ result = fileId(handle);
}
return result;
#else // !QT_BOOTSTRAPPED
@@ -680,10 +1052,8 @@ QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
QByteArray result;
- const HANDLE handle =
- CreateFile((wchar_t*)entry.nativeFilePath().utf16(), 0,
- FILE_SHARE_READ, NULL, OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ const HANDLE handle = CreateFile((wchar_t *)entry.nativeFilePath().utf16(), 0, FILE_SHARE_READ,
+ nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (handle != INVALID_HANDLE_VALUE) {
result = id(handle);
CloseHandle(handle);
@@ -699,23 +1069,23 @@ QByteArray QFileSystemEngine::id(HANDLE fHandle)
//static
bool QFileSystemEngine::setFileTime(HANDLE fHandle, const QDateTime &newDate,
- QAbstractFileEngine::FileTime time, QSystemError &error)
+ QFile::FileTime time, QSystemError &error)
{
FILETIME fTime;
- FILETIME *pLastWrite = NULL;
- FILETIME *pLastAccess = NULL;
- FILETIME *pCreationTime = NULL;
+ FILETIME *pLastWrite = nullptr;
+ FILETIME *pLastAccess = nullptr;
+ FILETIME *pCreationTime = nullptr;
switch (time) {
- case QAbstractFileEngine::ModificationTime:
+ case QFile::FileModificationTime:
pLastWrite = &fTime;
break;
- case QAbstractFileEngine::AccessTime:
+ case QFile::FileAccessTime:
pLastAccess = &fTime;
break;
- case QAbstractFileEngine::BirthTime:
+ case QFile::FileBirthTime:
pCreationTime = &fTime;
break;
@@ -738,32 +1108,36 @@ QString QFileSystemEngine::owner(const QFileSystemEntry &entry, QAbstractFileEng
{
QString name;
#if QT_CONFIG(fslibs)
- extern int qt_ntfs_permission_lookup;
- if (qt_ntfs_permission_lookup > 0) {
+ if (qAreNtfsPermissionChecksEnabled()) {
initGlobalSid();
{
PSID pOwner = 0;
PSECURITY_DESCRIPTOR pSD;
- if (GetNamedSecurityInfo(reinterpret_cast<const 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) {
+ if (GetNamedSecurityInfo(
+ reinterpret_cast<const wchar_t *>(entry.nativeFilePath().utf16()),
+ SE_FILE_OBJECT,
+ own == QAbstractFileEngine::OwnerGroup ? GROUP_SECURITY_INFORMATION
+ : OWNER_SECURITY_INFORMATION,
+ own == QAbstractFileEngine::OwnerUser ? &pOwner : nullptr,
+ own == QAbstractFileEngine::OwnerGroup ? &pOwner : nullptr, nullptr,
+ nullptr, &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 (!LookupAccountSid(NULL, pOwner, (LPWSTR)owner.data(), &lowner,
- domain.data(), &ldomain, &use)) {
+ if (!LookupAccountSid(nullptr, pOwner, (LPWSTR)owner.data(), &lowner, domain.data(),
+ &ldomain, &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 (!LookupAccountSid(NULL, pOwner, owner.data(), &lowner,
- domain.data(), &ldomain, &use)) {
+ if (!LookupAccountSid(nullptr, pOwner, owner.data(), &lowner, domain.data(),
+ &ldomain, &use)) {
lowner = 0;
}
} else {
@@ -788,104 +1162,80 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
QFileSystemMetaData::MetaDataFlags what)
{
#if QT_CONFIG(fslibs)
- if (qt_ntfs_permission_lookup > 0) {
+ if (qAreNtfsPermissionChecksEnabled()) {
initGlobalSid();
- {
- enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 };
- QString fname = entry.nativeFilePath();
- PSID pOwner = 0;
- PSID pGroup = 0;
- PACL pDacl;
- PSECURITY_DESCRIPTOR pSD;
- DWORD res = GetNamedSecurityInfo(reinterpret_cast<const 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
- // Using AccessCheck because GetEffectiveRightsFromAcl doesn't account for elevation
- if (currentUserImpersonatedToken) {
- GENERIC_MAPPING mapping = {FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS};
- PRIVILEGE_SET privileges;
- DWORD grantedAccess;
- BOOL result;
-
- data.knownFlagsMask |= QFileSystemMetaData::UserPermissions;
- DWORD genericAccessRights = GENERIC_READ;
- ::MapGenericMask(&genericAccessRights, &mapping);
-
- DWORD privilegesLength = sizeof(privileges);
- if (::AccessCheck(pSD, currentUserImpersonatedToken, genericAccessRights,
- &mapping, &privileges, &privilegesLength, &grantedAccess, &result) && result) {
- data.entryFlags |= QFileSystemMetaData::UserReadPermission;
- }
+ QString fname = entry.nativeFilePath();
+ PSID pOwner;
+ PSID pGroup;
+ PACL pDacl;
+ PSECURITY_DESCRIPTOR pSD;
+
+ // pDacl is unused directly by the code below, but it is still needed here because
+ // access checks below return incorrect results unless DACL_SECURITY_INFORMATION is
+ // passed to this call.
+ DWORD res = GetNamedSecurityInfo(
+ reinterpret_cast<const wchar_t *>(fname.utf16()), SE_FILE_OBJECT,
+ OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
+ &pOwner, &pGroup, &pDacl, nullptr, &pSD);
+ if (res == ERROR_SUCCESS) {
+ QAuthzResourceManager rm;
+
+ auto addPermissions = [&data](ACCESS_MASK accessMask,
+ QFileSystemMetaData::MetaDataFlag readFlags,
+ QFileSystemMetaData::MetaDataFlag writeFlags,
+ QFileSystemMetaData::MetaDataFlag executeFlags) {
+ // Check for generic permissions and file-specific bits that most closely
+ // represent POSIX permissions.
+
+ // Constants like FILE_GENERIC_{READ,WRITE,EXECUTE} cannot be used
+ // here because they contain permission bits shared between all of them.
+ if (accessMask & (GENERIC_READ | FILE_READ_DATA))
+ data.entryFlags |= readFlags;
+ if (accessMask & (GENERIC_WRITE | FILE_WRITE_DATA))
+ data.entryFlags |= writeFlags;
+ if (accessMask & (GENERIC_EXECUTE | FILE_EXECUTE))
+ data.entryFlags |= executeFlags;
+ };
+
+ if (what & QFileSystemMetaData::UserPermissions && currentUserImpersonatedToken) {
+ data.knownFlagsMask |= QFileSystemMetaData::UserPermissions;
+ QAuthzClientContext context(rm, currentUserImpersonatedToken,
+ QAuthzClientContext::TokenTag {});
+ addPermissions(context.accessMask(pSD),
+ QFileSystemMetaData::UserReadPermission,
+ QFileSystemMetaData::UserWritePermission,
+ QFileSystemMetaData::UserExecutePermission);
+ }
- privilegesLength = sizeof(privileges);
- genericAccessRights = GENERIC_WRITE;
- ::MapGenericMask(&genericAccessRights, &mapping);
- if (::AccessCheck(pSD, currentUserImpersonatedToken, genericAccessRights,
- &mapping, &privileges, &privilegesLength, &grantedAccess, &result) && result) {
- data.entryFlags |= QFileSystemMetaData::UserWritePermission;
- }
+ if (what & QFileSystemMetaData::OwnerPermissions) {
+ data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions;
+ QAuthzClientContext context(rm, pOwner);
+ addPermissions(context.accessMask(pSD),
+ QFileSystemMetaData::OwnerReadPermission,
+ QFileSystemMetaData::OwnerWritePermission,
+ QFileSystemMetaData::OwnerExecutePermission);
+ }
- privilegesLength = sizeof(privileges);
- genericAccessRights = GENERIC_EXECUTE;
- ::MapGenericMask(&genericAccessRights, &mapping);
- if (::AccessCheck(pSD, currentUserImpersonatedToken, genericAccessRights,
- &mapping, &privileges, &privilegesLength, &grantedAccess, &result) && result) {
- data.entryFlags |= QFileSystemMetaData::UserExecutePermission;
- }
- } else { // fallback to GetEffectiveRightsFromAcl
- data.knownFlagsMask |= QFileSystemMetaData::UserPermissions;
- if (GetEffectiveRightsFromAclW(pDacl, &currentUserTrusteeW, &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;
- BuildTrusteeWithSid(&trustee, pOwner);
- if (GetEffectiveRightsFromAcl(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;
- BuildTrusteeWithSid(&trustee, pGroup);
- if (GetEffectiveRightsFromAcl(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 (GetEffectiveRightsFromAcl(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);
+ if (what & QFileSystemMetaData::GroupPermissions) {
+ data.knownFlagsMask |= QFileSystemMetaData::GroupPermissions;
+ QAuthzClientContext context(rm, pGroup);
+ addPermissions(context.accessMask(pSD),
+ QFileSystemMetaData::GroupReadPermission,
+ QFileSystemMetaData::GroupWritePermission,
+ QFileSystemMetaData::GroupExecutePermission);
+ }
+
+ if (what & QFileSystemMetaData::OtherPermissions) {
+ data.knownFlagsMask |= QFileSystemMetaData::OtherPermissions;
+ QAuthzClientContext context(rm, worldSID);
+ addPermissions(context.accessMask(pSD),
+ QFileSystemMetaData::OtherReadPermission,
+ QFileSystemMetaData::OtherWritePermission,
+ QFileSystemMetaData::OtherExecutePermission);
}
+
+ LocalFree(pSD);
}
} else
#endif
@@ -905,14 +1255,16 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
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;
+ if (data.isDirectory() || ext == ".exe"_L1 || ext == ".com"_L1
+ || ext == ".bat"_L1 || ext == ".pif"_L1 || ext == ".cmd"_L1) {
+ data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission
+ | QFileSystemMetaData::GroupExecutePermission
+ | QFileSystemMetaData::OtherExecutePermission
+ | QFileSystemMetaData::UserExecutePermission;
}
- data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions | QFileSystemMetaData::GroupPermissions
- | QFileSystemMetaData::OtherPermissions | 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)
@@ -938,7 +1290,8 @@ static bool tryDriveUNCFallback(const QFileSystemEntry &fname, QFileSystemMetaDa
const UINT oldErrorMode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
DWORD drivesBitmask = ::GetLogicalDrives();
::SetErrorMode(oldErrorMode);
- int drivebit = 1 << (fname.filePath().at(0).toUpper().unicode() - QLatin1Char('A').unicode());
+ int drivebit =
+ 1 << (fname.filePath().at(0).toUpper().unicode() - u'A');
if (drivesBitmask & drivebit) {
fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM;
entryExists = true;
@@ -946,7 +1299,7 @@ static bool tryDriveUNCFallback(const QFileSystemEntry &fname, QFileSystemMetaDa
} else {
const QString &path = fname.nativeFilePath();
bool is_dir = false;
- if (path.startsWith(QLatin1String("\\\\?\\UNC"))) {
+ if (path.startsWith("\\\\?\\UNC"_L1)) {
// UNC - stat doesn't work for all cases (Windows bug)
int s = path.indexOf(path.at(0),7);
if (s > 0) {
@@ -1022,8 +1375,6 @@ bool QFileSystemEngine::fillMetaData(HANDLE fHandle, QFileSystemMetaData &data,
return data.hasFlags(what);
}
-static bool isDirPath(const QString &dirPath, bool *existed);
-
//static
bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what)
@@ -1037,7 +1388,7 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM
// 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)) {
+ if (origFilePath.endsWith(".lnk"_L1) && !isDirPath(origFilePath, nullptr)) {
data.entryFlags |= QFileSystemMetaData::WinLnkType;
fname = QFileSystemEntry(readLink(entry));
} else {
@@ -1056,14 +1407,15 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM
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(reinterpret_cast<const wchar_t*>(fname.nativeFilePath().utf16()),
- GetFileExInfoStandard,
- reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA *>(&findData));
+ bool ok = ::GetFileAttributesEx(
+ reinterpret_cast<const wchar_t *>(fname.nativeFilePath().utf16()),
+ GetFileExInfoStandard, reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA *>(&findData));
if (ok) {
data.fillFromFindData(findData, false, fname.isDriveRoot());
} else {
const DWORD lastError = GetLastError();
- if (lastError == ERROR_LOGON_FAILURE || lastError == ERROR_BAD_NETPATH // disconnected drive
+ // disconnected drive
+ if (lastError == ERROR_LOGON_FAILURE || lastError == ERROR_BAD_NETPATH
|| (!tryFindFallback(fname, data) && !tryDriveUNCFallback(fname, data))) {
data.clearFlags();
SetErrorMode(oldmode);
@@ -1087,13 +1439,15 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM
return data.hasFlags(what);
}
-static inline bool mkDir(const QString &path, DWORD *lastError = 0)
+static inline bool mkDir(const QString &path, SECURITY_ATTRIBUTES *securityAttributes,
+ DWORD *lastError = nullptr)
{
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.
+ const bool result = ::CreateDirectory((wchar_t *)longPath.utf16(), securityAttributes);
+ // Capture lastError before any QString is freed since custom allocators might change it.
+ if (lastError)
*lastError = GetLastError();
return result;
}
@@ -1103,11 +1457,12 @@ static inline bool rmDir(const QString &path)
return ::RemoveDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16());
}
-static bool isDirPath(const QString &dirPath, bool *existed)
+//static
+bool QFileSystemEngine::isDirPath(const QString &dirPath, bool *existed)
{
QString path = dirPath;
- if (path.length() == 2 && path.at(1) == QLatin1Char(':'))
- path += QLatin1Char('\\');
+ if (path.length() == 2 && path.at(1) == u':')
+ path += u'\\';
const QString longPath = QFSFileEnginePrivate::longFileName(path);
DWORD fileAttrib = ::GetFileAttributes(reinterpret_cast<const wchar_t*>(longPath.utf16()));
@@ -1131,24 +1486,27 @@ static bool isDirPath(const QString &dirPath, bool *existed)
// NOTE: if \a shouldMkdirFirst is false, we assume the caller did try to mkdir
// before calling this function.
-static bool createDirectoryWithParents(const QString &nativeName, bool shouldMkdirFirst = true)
+static bool createDirectoryWithParents(const QString &nativeName,
+ SECURITY_ATTRIBUTES *securityAttributes,
+ bool shouldMkdirFirst = true)
{
const auto isUNCRoot = [](const QString &nativeName) {
- return nativeName.startsWith(QLatin1String("\\\\")) && nativeName.count(QDir::separator()) <= 3;
+ return nativeName.startsWith("\\\\"_L1)
+ && nativeName.count(QDir::separator()) <= 3;
};
const auto isDriveName = [](const QString &nativeName) {
- return nativeName.size() == 2 && nativeName.at(1) == QLatin1Char(':');
+ return nativeName.size() == 2 && nativeName.at(1) == u':';
};
const auto isDir = [](const QString &nativeName) {
bool exists = false;
- return isDirPath(nativeName, &exists) && exists;
+ return QFileSystemEngine::isDirPath(nativeName, &exists) && exists;
};
// Do not try to mkdir a UNC root path or a drive letter.
if (isUNCRoot(nativeName) || isDriveName(nativeName))
return false;
if (shouldMkdirFirst) {
- if (mkDir(nativeName))
+ if (mkDir(nativeName, securityAttributes))
return true;
}
@@ -1157,34 +1515,41 @@ static bool createDirectoryWithParents(const QString &nativeName, bool shouldMkd
return false;
const QString parentNativeName = nativeName.left(backSlash);
- if (!createDirectoryWithParents(parentNativeName))
+ if (!createDirectoryWithParents(parentNativeName, securityAttributes))
return false;
// try again
- if (mkDir(nativeName))
+ if (mkDir(nativeName, securityAttributes))
return true;
return isDir(nativeName);
}
//static
-bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
+bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents,
+ std::optional<QFile::Permissions> permissions)
{
QString dirName = entry.filePath();
Q_CHECK_FILE_NAME(dirName, false);
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
+ QNativeFilePermissions nativePermissions(permissions, true);
+ if (!nativePermissions.isOk())
+ return false;
+
+ auto securityAttributes = nativePermissions.securityAttributes();
+
// try to mkdir this directory
DWORD lastError;
- if (mkDir(dirName, &lastError))
+ if (mkDir(dirName, securityAttributes, &lastError))
return true;
// mkpath should return true, if the directory already exists, mkdir false.
if (!createParents)
return false;
- if (lastError == ERROR_ALREADY_EXISTS)
+ if (lastError == ERROR_ALREADY_EXISTS || lastError == ERROR_ACCESS_DENIED)
return isDirPath(dirName, nullptr);
- return createDirectoryWithParents(dirName, false);
+ return createDirectoryWithParents(dirName, securityAttributes, false);
}
//static
@@ -1197,10 +1562,12 @@ bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool remo
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
const auto chunkRef = QStringView{dirName}.left(slash);
- if (chunkRef.length() == 2 && chunkRef.at(0).isLetter() && chunkRef.at(1) == QLatin1Char(':'))
+ if (chunkRef.length() == 2 && chunkRef.at(0).isLetter()
+ && chunkRef.at(1) == u':') {
break;
+ }
const QString chunk = chunkRef.toString();
- if (!isDirPath(chunk, 0))
+ if (!isDirPath(chunk, nullptr))
return false;
if (!rmDir(chunk))
return oldslash != 0;
@@ -1216,8 +1583,8 @@ QString QFileSystemEngine::rootPath()
{
QString ret = QString::fromLatin1(qgetenv("SystemDrive"));
if (ret.isEmpty())
- ret = QLatin1String("c:");
- ret.append(QLatin1Char('/'));
+ ret = "c:"_L1;
+ ret.append(u'/');
return ret;
}
@@ -1229,12 +1596,12 @@ QString QFileSystemEngine::homePath()
initGlobalSid();
{
HANDLE hnd = ::GetCurrentProcess();
- HANDLE token = 0;
+ HANDLE token = nullptr;
BOOL ok = ::OpenProcessToken(hnd, TOKEN_QUERY, &token);
if (ok) {
DWORD dwBufferSize = 0;
// First call, to determine size of the strings (with '\0').
- ok = GetUserProfileDirectory(token, NULL, &dwBufferSize);
+ ok = GetUserProfileDirectory(token, nullptr, &dwBufferSize);
if (!ok && dwBufferSize != 0) { // We got the required buffer size
wchar_t *userDirectory = new wchar_t[dwBufferSize];
// Second call, now we can fill the allocated buffer.
@@ -1275,13 +1642,13 @@ QString QFileSystemEngine::tempPath()
QString::fromWCharArray(tempPath, len);
}
if (!ret.isEmpty()) {
- while (ret.endsWith(QLatin1Char('\\')))
+ while (ret.endsWith(u'\\'))
ret.chop(1);
ret = QDir::fromNativeSeparators(ret);
}
if (ret.isEmpty()) {
- ret = QLatin1String("C:/tmp");
- } else if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
+ ret = "C:/tmp"_L1;
+ } else if (ret.length() >= 2 && ret[1] == u':')
ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
return ret;
}
@@ -1289,60 +1656,57 @@ QString QFileSystemEngine::tempPath()
bool QFileSystemEngine::setCurrentPath(const QFileSystemEntry &entry)
{
QFileSystemMetaData meta;
- fillMetaData(entry, meta, QFileSystemMetaData::ExistsAttribute | QFileSystemMetaData::DirectoryType);
+ 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;
+ // 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);
- }
+ QString ret(PATH_MAX, Qt::Uninitialized);
+ DWORD size = GetCurrentDirectoryW(PATH_MAX, reinterpret_cast<wchar_t *>(ret.data()));
+ if (size > PATH_MAX) {
+ // try again after enlarging the buffer
+ ret.resize(size);
+ size = GetCurrentDirectoryW(size, reinterpret_cast<wchar_t *>(ret.data()));
+
+ // note: the current directory may have changed underneath us; if the
+ // new one is even bigger, we may return a truncated string!
}
- if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
+ if (size >= 2 && ret.at(1) == u':')
ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
- return QFileSystemEntry(ret, QFileSystemEntry::FromNativePath());
+ ret.resize(size);
+ return QFileSystemEntry(std::move(ret), QFileSystemEntry::FromNativePath());
}
//static
-bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
+bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target,
+ QSystemError &error)
{
bool ret = false;
+ QComHelper comHelper;
IShellLink *psl = nullptr;
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
reinterpret_cast<void **>(&psl));
- bool neededCoInit = false;
- if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
- neededCoInit = true;
- CoInitialize(nullptr);
- hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
- reinterpret_cast<void **>(&psl));
- }
-
if (SUCCEEDED(hres)) {
const auto name = QDir::toNativeSeparators(source.filePath());
const auto pathName = QDir::toNativeSeparators(source.path());
if (SUCCEEDED(psl->SetPath(reinterpret_cast<const wchar_t *>(name.utf16())))
- && SUCCEEDED(psl->SetWorkingDirectory(reinterpret_cast<const wchar_t *>(pathName.utf16())))) {
+ && SUCCEEDED(psl->SetWorkingDirectory(
+ reinterpret_cast<const wchar_t *>(pathName.utf16())))) {
IPersistFile *ppf = nullptr;
if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void **>(&ppf)))) {
- ret = SUCCEEDED(ppf->Save(reinterpret_cast<const wchar_t *>(target.filePath().utf16()), TRUE));
+ ret = SUCCEEDED(ppf->Save(
+ reinterpret_cast<const wchar_t *>(target.filePath().utf16()), TRUE));
ppf->Release();
}
}
@@ -1352,14 +1716,12 @@ bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSy
if (!ret)
error = QSystemError(::GetLastError(), QSystemError::NativeError);
- if (neededCoInit)
- CoUninitialize();
-
return ret;
}
//static
-bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
+bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target,
+ QSystemError &error)
{
bool ret = ::CopyFile((wchar_t*)source.nativeFilePath().utf16(),
(wchar_t*)target.nativeFilePath().utf16(), true) != 0;
@@ -1369,7 +1731,8 @@ bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSyst
}
//static
-bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
+bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target,
+ QSystemError &error)
{
Q_CHECK_FILE_NAME(source, false);
Q_CHECK_FILE_NAME(target, false);
@@ -1382,7 +1745,8 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
}
//static
-bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
+bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source,
+ const QFileSystemEntry &target, QSystemError &error)
{
Q_CHECK_FILE_NAME(source, false);
Q_CHECK_FILE_NAME(target, false);
@@ -1419,8 +1783,8 @@ bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
// we need the "display name" of the file, so can't use nativeAbsoluteFilePath
const QString sourcePath = QDir::toNativeSeparators(absoluteName(source).filePath());
-# if defined(__IFileOperation_INTERFACE_DEFINED__)
- CoInitialize(NULL);
+ QComHelper comHelper;
+
IFileOperation *pfo = nullptr;
IShellItem *deleteItem = nullptr;
FileOperationProgressSink *sink = nullptr;
@@ -1433,7 +1797,6 @@ bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
deleteItem->Release();
if (pfo)
pfo->Release();
- CoUninitialize();
if (!SUCCEEDED(hres))
error = QSystemError(hres, QSystemError::NativeError);
});
@@ -1454,15 +1817,18 @@ bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
hres = pfo->PerformOperations();
if (!SUCCEEDED(hres))
return false;
- newLocation = QFileSystemEntry(sink->targetPath);
-# endif // no IFileOperation in SDK (mingw, likely) - fall back to SHFileOperation
+ if (!SUCCEEDED(sink->deleteResult)) {
+ error = QSystemError(sink->deleteResult, QSystemError::NativeError);
+ return false;
+ }
+ newLocation = QFileSystemEntry(sink->targetPath);
return true;
-
}
//static
-bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error,
+bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry,
+ QFile::Permissions permissions, QSystemError &error,
QFileSystemMetaData *data)
{
Q_CHECK_FILE_NAME(entry, false);
@@ -1472,13 +1838,16 @@ bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Per
if (permissions & (QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther))
mode |= _S_IREAD;
- if (permissions & (QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther))
+ if (permissions
+ & (QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther)) {
mode |= _S_IWRITE;
+ }
if (mode == 0) // not supported
return false;
- bool ret = ::_wchmod(reinterpret_cast<const wchar_t*>(entry.nativeFilePath().utf16()), mode) == 0;
+ bool ret =
+ ::_wchmod(reinterpret_cast<const wchar_t *>(entry.nativeFilePath().utf16()), mode) == 0;
if (!ret)
error = QSystemError(errno, QSystemError::StandardLibraryError);
return ret;
@@ -1493,7 +1862,7 @@ static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
FileTimeToSystemTime(time, &sTime);
return QDateTime(QDate(sTime.wYear, sTime.wMonth, sTime.wDay),
QTime(sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds),
- Qt::UTC);
+ QTimeZone::UTC);
}
QDateTime QFileSystemMetaData::birthTime() const