From 174af05400f6344a11f4aa2228244c954cbbca97 Mon Sep 17 00:00:00 2001 From: Ievgenii Meshcheriakov Date: Wed, 10 Nov 2021 16:52:07 +0100 Subject: QDir: Add support for setting directory permissions to mkdir() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: MÃ¥rten Nordheim --- src/corelib/io/qabstractfileengine.cpp | 11 +- src/corelib/io/qabstractfileengine_p.h | 5 +- src/corelib/io/qdir.cpp | 35 +++- src/corelib/io/qdir.h | 1 + src/corelib/io/qfiledevice_p.h | 34 +++- src/corelib/io/qfilesystemengine_p.h | 5 +- src/corelib/io/qfilesystemengine_unix.cpp | 17 +- src/corelib/io/qfilesystemengine_win.cpp | 306 +++++++++++++++++++++++++++--- src/corelib/io/qfsfileengine.cpp | 6 +- src/corelib/io/qfsfileengine_p.h | 5 +- 10 files changed, 380 insertions(+), 45 deletions(-) (limited to 'src/corelib/io') 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 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 + #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 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 -#ifdef Q_OS_UNIX +#if defined(Q_OS_UNIX) # include // for mode_t -# include // for mode_t constants +# include // for mode_t constants +#elif defined(Q_OS_WINDOWS) +# include +# include // for SECURITY_DESCRIPTOR +# include #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 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 +#include + 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 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 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 #include @@ -147,6 +148,7 @@ typedef struct _REPARSE_DATA_BUFFER { #include #include 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 +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(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(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(tokenBuffer)->User.Sid; - DWORD sidLen = ::GetLengthSid(tokenSid); - currentUserSID = reinterpret_cast(malloc(sidLen)); - Q_CHECK_PTR(currentUserSID); - ::CopySid(sidLen, currentUserSID, tokenSid); - } - free(tokenBuffer); + // Create SID for current user + if (auto info = getTokenInfo(token, TokenUser)) { + copySID(currentUserSID, info->User.Sid); + free(info); + } + + // Create SID for the current user's primary group. + if (auto info = getTokenInfo(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 perms, + bool isDir) +{ +#if QT_CONFIG(fslibs) + if (!perms) { + ok = true; + return; + } + + initGlobalSid(); + + const auto permissions = *perms; + + PACL acl = reinterpret_cast(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 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 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 #include +#include + #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 permissions) const override; bool rmdir(const QString &dirName, bool recurseParentDirectories) const override; bool setSize(qint64 size) override; bool caseSensitive() const override; -- cgit v1.2.3