summaryrefslogtreecommitdiffstats
path: root/src/corelib/io
diff options
context:
space:
mode:
authorIevgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io>2021-11-10 16:52:07 +0100
committerIevgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io>2021-11-26 03:39:33 +0100
commit174af05400f6344a11f4aa2228244c954cbbca97 (patch)
treedf5edd252f5d0bd4b865c3ace2473fbbfb80d087 /src/corelib/io
parent8c9875893bbd7b1355e36d9501d8cb1f84cb6b4d (diff)
QDir: Add support for setting directory permissions to mkdir()
This patch adds an overload of the QDir::mkdir() method that accepts permissions. This allows setting of the directory permissions at the time of its creation. [ChangeLog][QtCore][QDir] Added QDir::mdkir() overload that accepts permissions argument. Task-number: QTBUG-79750 Change-Id: Ic9db723b94ff0d2da6e0b819ac2e5d1f9a4e2049 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src/corelib/io')
-rw-r--r--src/corelib/io/qabstractfileengine.cpp11
-rw-r--r--src/corelib/io/qabstractfileengine_p.h5
-rw-r--r--src/corelib/io/qdir.cpp35
-rw-r--r--src/corelib/io/qdir.h1
-rw-r--r--src/corelib/io/qfiledevice_p.h34
-rw-r--r--src/corelib/io/qfilesystemengine_p.h5
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp17
-rw-r--r--src/corelib/io/qfilesystemengine_win.cpp306
-rw-r--r--src/corelib/io/qfsfileengine.cpp6
-rw-r--r--src/corelib/io/qfsfileengine_p.h5
10 files changed, 380 insertions, 45 deletions
diff --git a/src/corelib/io/qabstractfileengine.cpp b/src/corelib/io/qabstractfileengine.cpp
index 9b2690ca15..063cee1071 100644
--- a/src/corelib/io/qabstractfileengine.cpp
+++ b/src/corelib/io/qabstractfileengine.cpp
@@ -534,19 +534,24 @@ bool QAbstractFileEngine::link(const QString &newName)
}
/*!
- Requests that the directory \a dirName be created. If
- \a createParentDirectories is true, then any sub-directories in \a dirName
+ Requests that the directory \a dirName be created with the specified \a permissions.
+ If \a createParentDirectories is true, then any sub-directories in \a dirName
that don't exist must be created. If \a createParentDirectories is false then
any sub-directories in \a dirName must already exist for the function to
succeed. If the operation succeeds return true; otherwise return
false.
+ If \a permissions is null then implementation-specific default permissions are
+ used.
+
\sa setFileName(), rmdir(), isRelativePath()
*/
-bool QAbstractFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const
+bool QAbstractFileEngine::mkdir(const QString &dirName, bool createParentDirectories,
+ std::optional<QFile::Permissions> permissions) const
{
Q_UNUSED(dirName);
Q_UNUSED(createParentDirectories);
+ Q_UNUSED(permissions);
return false;
}
diff --git a/src/corelib/io/qabstractfileengine_p.h b/src/corelib/io/qabstractfileengine_p.h
index 91b74bebd3..d8ea5c318f 100644
--- a/src/corelib/io/qabstractfileengine_p.h
+++ b/src/corelib/io/qabstractfileengine_p.h
@@ -55,6 +55,8 @@
#include "QtCore/qfile.h"
#include "QtCore/qdir.h"
+#include <optional>
+
#ifdef open
#error qabstractfileengine_p.h must be included before any header file that defines open
#endif
@@ -135,7 +137,8 @@ public:
virtual bool rename(const QString &newName);
virtual bool renameOverwrite(const QString &newName);
virtual bool link(const QString &newName);
- virtual bool mkdir(const QString &dirName, bool createParentDirectories) const;
+ virtual bool mkdir(const QString &dirName, bool createParentDirectories,
+ std::optional<QFile::Permissions> permissions = std::nullopt) const;
virtual bool rmdir(const QString &dirName, bool recurseParentDirectories) const;
virtual bool setSize(qint64 size);
virtual bool caseSensitive() const;
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp
index fb34aa7382..9d540b2581 100644
--- a/src/corelib/io/qdir.cpp
+++ b/src/corelib/io/qdir.cpp
@@ -1445,9 +1445,42 @@ QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filter
Returns \c true on success; otherwise returns \c false.
- If the directory already exists when this function is called, it will return false.
+ If the directory already exists when this function is called, it will return \c false.
+
+ The permissions of the created directory are set to \a{permissions}.
+
+ On POSIX systems the permissions are influenced by the value of \c umask.
+
+ On Windows the permissions are emulated using ACLs. These ACLs may be in non-canonical
+ order when the group is granted less permissions than others. Files and directories with
+ such permissions will generate warnings when the Security tab of the Properties dialog
+ is opened. Granting the group all permissions granted to others avoids such warnings.
\sa rmdir()
+
+ \since 6.3
+*/
+bool QDir::mkdir(const QString &dirName, QFile::Permissions permissions) const
+{
+ const QDirPrivate *d = d_ptr.constData();
+
+ if (dirName.isEmpty()) {
+ qWarning("QDir::mkdir: Empty or null file name");
+ return false;
+ }
+
+ QString fn = filePath(dirName);
+ if (!d->fileEngine)
+ return QFileSystemEngine::createDirectory(QFileSystemEntry(fn), false, permissions);
+ return d->fileEngine->mkdir(fn, false, permissions);
+}
+
+/*!
+ \overload
+ Creates a sub-directory called \a dirName with default permissions.
+
+ On POSIX systems the default is to grant all permissions allowed by \c umask.
+ On Windows, the new directory inherits its permissions from its parent directory.
*/
bool QDir::mkdir(const QString &dirName) const
{
diff --git a/src/corelib/io/qdir.h b/src/corelib/io/qdir.h
index 86ab33a2fc..38a4d8d961 100644
--- a/src/corelib/io/qdir.h
+++ b/src/corelib/io/qdir.h
@@ -196,6 +196,7 @@ public:
SortFlags sort = NoSort) const;
bool mkdir(const QString &dirName) const;
+ bool mkdir(const QString &dirName, QFile::Permissions permissions) const;
bool rmdir(const QString &dirName) const;
bool mkpath(const QString &dirPath) const;
bool rmpath(const QString &dirPath) const;
diff --git a/src/corelib/io/qfiledevice_p.h b/src/corelib/io/qfiledevice_p.h
index 9adba680d7..2b97773c2f 100644
--- a/src/corelib/io/qfiledevice_p.h
+++ b/src/corelib/io/qfiledevice_p.h
@@ -52,11 +52,16 @@
//
#include "private/qiodevice_p.h"
+#include "qfiledevice.h"
#include <memory>
-#ifdef Q_OS_UNIX
+#if defined(Q_OS_UNIX)
# include <sys/types.h> // for mode_t
-# include <sys/stat.h> // for mode_t constants
+# include <sys/stat.h> // for mode_t constants
+#elif defined(Q_OS_WINDOWS)
+# include <qt_windows.h>
+# include <winnt.h> // for SECURITY_DESCRIPTOR
+# include <optional>
#endif
QT_BEGIN_NAMESPACE
@@ -130,6 +135,31 @@ constexpr mode_t toMode_t(QFileDevice::Permissions permissions)
}
} // namespace QtPrivate
+#elif defined(Q_OS_WINDOWS)
+
+class QNativeFilePermissions
+{
+public:
+ QNativeFilePermissions(std::optional<QFileDevice::Permissions> perms, bool isDir);
+
+ SECURITY_ATTRIBUTES *securityAttributes();
+ bool isOk() const { return ok; }
+
+private:
+ bool ok = false;
+ bool isNull = true;
+
+ // At most 1 allow + 1 deny ACEs for user and group, 1 allow ACE for others
+ static constexpr auto MaxNumACEs = 5;
+
+ static constexpr auto MaxACLSize =
+ sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) + SECURITY_MAX_SID_SIZE) * MaxNumACEs;
+
+ SECURITY_DESCRIPTOR sd;
+ SECURITY_ATTRIBUTES sa;
+ alignas(DWORD) char aclStorage[MaxACLSize];
+};
+
#endif // Q_OS_UNIX
QT_END_NAMESPACE
diff --git a/src/corelib/io/qfilesystemengine_p.h b/src/corelib/io/qfilesystemengine_p.h
index 0b6f0fbd84..6fac1ae4be 100644
--- a/src/corelib/io/qfilesystemengine_p.h
+++ b/src/corelib/io/qfilesystemengine_p.h
@@ -56,6 +56,8 @@
#include "qfilesystemmetadata_p.h"
#include <QtCore/private/qsystemerror_p.h>
+#include <optional>
+
QT_BEGIN_NAMESPACE
#define Q_RETURN_ON_INVALID_FILENAME(message, result) \
@@ -151,7 +153,8 @@ public:
static QString rootPath();
static QString tempPath();
- static bool createDirectory(const QFileSystemEntry &entry, bool createParents);
+ static bool createDirectory(const QFileSystemEntry &entry, bool createParents,
+ std::optional<QFile::Permissions> permissions = std::nullopt);
static bool removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents);
static bool createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
index 8c2df84b72..6e8229daec 100644
--- a/src/corelib/io/qfilesystemengine_unix.cpp
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
@@ -1110,7 +1110,8 @@ bool QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaDat
// Note: if \a shouldMkdirFirst is false, we assume the caller did try to mkdir
// before calling this function.
-static bool createDirectoryWithParents(const QByteArray &nativeName, bool shouldMkdirFirst = true)
+static bool createDirectoryWithParents(const QByteArray &nativeName, mode_t mode,
+ bool shouldMkdirFirst = true)
{
// helper function to check if a given path is a directory, since mkdir can
// fail if the dir already exists (it may have been created by another
@@ -1120,7 +1121,7 @@ static bool createDirectoryWithParents(const QByteArray &nativeName, bool should
return QT_STAT(nativeName.constData(), &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR;
};
- if (shouldMkdirFirst && QT_MKDIR(nativeName, 0777) == 0)
+ if (shouldMkdirFirst && QT_MKDIR(nativeName, mode) == 0)
return true;
if (errno == EISDIR)
return true;
@@ -1135,17 +1136,18 @@ static bool createDirectoryWithParents(const QByteArray &nativeName, bool should
return false;
QByteArray parentNativeName = nativeName.left(slash);
- if (!createDirectoryWithParents(parentNativeName))
+ if (!createDirectoryWithParents(parentNativeName, mode))
return false;
// try again
- if (QT_MKDIR(nativeName, 0777) == 0)
+ if (QT_MKDIR(nativeName, mode) == 0)
return true;
return errno == EEXIST && 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);
@@ -1156,12 +1158,13 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
// try to mkdir this directory
QByteArray nativeName = QFile::encodeName(dirName);
- if (QT_MKDIR(nativeName, 0777) == 0)
+ mode_t mode = permissions ? QtPrivate::toMode_t(*permissions) : 0777;
+ if (QT_MKDIR(nativeName, mode) == 0)
return true;
if (!createParents)
return false;
- return createDirectoryWithParents(nativeName, false);
+ return createDirectoryWithParents(nativeName, mode, false);
}
//static
diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp
index 3c80f76d9e..60285cfea8 100644
--- a/src/corelib/io/qfilesystemengine_win.cpp
+++ b/src/corelib/io/qfilesystemengine_win.cpp
@@ -43,6 +43,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>
@@ -147,6 +148,7 @@ typedef struct _REPARSE_DATA_BUFFER {
#include <authz.h>
#include <userenv.h>
static PSID currentUserSID = nullptr;
+static PSID currentGroupSID = nullptr;
static PSID worldSID = nullptr;
static HANDLE currentUserImpersonatedToken = nullptr;
@@ -164,6 +166,9 @@ GlobalSid::~GlobalSid()
free(currentUserSID);
currentUserSID = nullptr;
+ free(currentGroupSID);
+ currentGroupSID = nullptr;
+
// worldSID was allocated with AllocateAndInitializeSid so it needs to be freed with FreeSid
if (worldSID) {
::FreeSid(worldSID);
@@ -176,29 +181,54 @@ 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)
+{
+ 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;
+}
+
+/*
+ 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);
+}
+
GlobalSid::GlobalSid()
{
- // Create TRUSTEE for current user
HANDLE hnd = ::GetCurrentProcess();
HANDLE token = nullptr;
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, nullptr, 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);
- ::CopySid(sidLen, currentUserSID, tokenSid);
- }
- free(tokenBuffer);
+ // 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);
}
@@ -360,6 +390,29 @@ ACCESS_MASK QAuthzClientContext::accessMask(PSECURITY_DESCRIPTOR pSD) const
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);
+}
+
+constexpr QFileDevice::Permissions toSpecificPermissions(PermissionTag tag,
+ NonSpecificPermissions permissions)
+{
+ return QFileDevice::Permissions::fromInt(permissions.toInt() << int(tag));
+}
+
QT_END_NAMESPACE
} // anonymous namespace
@@ -369,6 +422,195 @@ QT_BEGIN_NAMESPACE
Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
+/*!
+ \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 = [this, 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;
+#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 calles 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)
{
SYSTEMTIME sTime;
@@ -1206,12 +1448,13 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM
return data.hasFlags(what);
}
-static inline bool mkDir(const QString &path, DWORD *lastError = nullptr)
+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(), nullptr);
+ 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();
@@ -1252,7 +1495,9 @@ bool QFileSystemEngine::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("\\\\"))
@@ -1270,7 +1515,7 @@ static bool createDirectoryWithParents(const QString &nativeName, bool shouldMkd
return false;
if (shouldMkdirFirst) {
- if (mkDir(nativeName))
+ if (mkDir(nativeName, securityAttributes))
return true;
}
@@ -1279,26 +1524,33 @@ 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)
@@ -1306,7 +1558,7 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
if (lastError == ERROR_ALREADY_EXISTS || lastError == ERROR_ACCESS_DENIED)
return isDirPath(dirName, nullptr);
- return createDirectoryWithParents(dirName, false);
+ return createDirectoryWithParents(dirName, securityAttributes, false);
}
//static
diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp
index 394f21bd6d..6cd25de591 100644
--- a/src/corelib/io/qfsfileengine.cpp
+++ b/src/corelib/io/qfsfileengine.cpp
@@ -1057,9 +1057,11 @@ bool QFSFileEngine::renameOverwrite(const QString &newName)
/*!
\reimp
*/
-bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
+bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories,
+ std::optional<QFile::Permissions> permissions) const
{
- return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories);
+ return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories,
+ permissions);
}
/*!
diff --git a/src/corelib/io/qfsfileengine_p.h b/src/corelib/io/qfsfileengine_p.h
index f5236a0156..6cfa6ebbab 100644
--- a/src/corelib/io/qfsfileengine_p.h
+++ b/src/corelib/io/qfsfileengine_p.h
@@ -57,6 +57,8 @@
#include <QtCore/private/qfilesystemmetadata_p.h>
#include <qhash.h>
+#include <optional>
+
#ifndef QT_NO_FSFILEENGINE
QT_BEGIN_NAMESPACE
@@ -93,7 +95,8 @@ public:
bool rename(const QString &newName) override;
bool renameOverwrite(const QString &newName) override;
bool link(const QString &newName) override;
- bool mkdir(const QString &dirName, bool createParentDirectories) const override;
+ bool mkdir(const QString &dirName, bool createParentDirectories,
+ std::optional<QFile::Permissions> permissions) const override;
bool rmdir(const QString &dirName, bool recurseParentDirectories) const override;
bool setSize(qint64 size) override;
bool caseSensitive() const override;