Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qfilesystemengine_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6#include "qplatformdefs.h"
7#include "qsysinfo.h"
8#include "qscopeguard.h"
9#include "private/qabstractfileengine_p.h"
10#include "private/qfiledevice_p.h"
11#include "private/qfsfileengine_p.h"
12#include <private/qsystemlibrary_p.h>
13#include <qdebug.h>
14
15#include "qdir.h"
16#include "qdatetime.h"
17#include "qfile.h"
18#include "qvarlengtharray.h"
19#include "qt_windows.h"
20#if QT_CONFIG(regularexpression)
21#include "qregularexpression.h"
22#endif
23#include "qstring.h"
24
25#include <sys/types.h>
26#include <direct.h>
27#include <winioctl.h>
28#include <objbase.h>
29#include <shlobj.h>
30#include <shobjidl.h>
31#include <shellapi.h>
32#include <lm.h>
33#include <accctrl.h>
34#include <initguid.h>
35#include <ctype.h>
36#include <limits.h>
37#define SECURITY_WIN32
38#include <security.h>
39
40#include <QtCore/private/qfunctions_win_p.h>
41
42#ifndef SPI_GETPLATFORMTYPE
43#define SPI_GETPLATFORMTYPE 257
44#endif
45
46#ifndef PATH_MAX
47#define PATH_MAX FILENAME_MAX
48#endif
49
50#ifndef _INTPTR_T_DEFINED
51#ifdef _WIN64
52typedef __int64 intptr_t;
53#else
54#ifdef _W64
55typedef _W64 int intptr_t;
56#else
57typedef INT_PTR intptr_t;
58#endif
59#endif
60#define _INTPTR_T_DEFINED
61#endif
62
63#ifndef INVALID_FILE_ATTRIBUTES
64# define INVALID_FILE_ATTRIBUTES (DWORD (-1))
65#endif
66
67#if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
68typedef struct _REPARSE_DATA_BUFFER {
71 USHORT Reserved;
72 union {
73 struct {
78 ULONG Flags;
79 WCHAR PathBuffer[1];
81 struct {
84 USHORT PrintNameOffset;
85 USHORT PrintNameLength;
86 WCHAR PathBuffer[1];
88 struct {
89 UCHAR DataBuffer[1];
91 };
93# define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
94#endif // !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
95
96#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
97# define MAXIMUM_REPARSE_DATA_BUFFER_SIZE 16384
98#endif
99#ifndef IO_REPARSE_TAG_SYMLINK
100# define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
101#endif
102#ifndef FSCTL_GET_REPARSE_POINT
103# define FSCTL_GET_REPARSE_POINT \
104 CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
105#endif
106
107#if QT_CONFIG(fslibs)
108#include <aclapi.h>
109#include <authz.h>
110#include <userenv.h>
111static PSID currentUserSID = nullptr;
112static PSID currentGroupSID = nullptr;
113static PSID worldSID = nullptr;
114static HANDLE currentUserImpersonatedToken = nullptr;
115#endif // fslibs
116
118using namespace Qt::StringLiterals;
119
120#if QT_CONFIG(fslibs)
121namespace {
122struct GlobalSid
123{
124 GlobalSid();
125 ~GlobalSid();
126};
127
128GlobalSid::~GlobalSid()
129{
130 free(currentUserSID);
131 currentUserSID = nullptr;
132
133 free(currentGroupSID);
134 currentGroupSID = nullptr;
135
136 // worldSID was allocated with AllocateAndInitializeSid so it needs to be freed with FreeSid
137 if (worldSID) {
138 ::FreeSid(worldSID);
139 worldSID = nullptr;
140 }
141
142 if (currentUserImpersonatedToken) {
143 ::CloseHandle(currentUserImpersonatedToken);
144 currentUserImpersonatedToken = nullptr;
145 }
146}
147
148/*
149 Helper for GetTokenInformation that allocates chunk of memory to hold the requested information.
150
151 The memory size is determined by doing a dummy call first. The returned memory should be
152 freed by calling free().
153*/
154template<typename T>
155static T *getTokenInfo(HANDLE token, TOKEN_INFORMATION_CLASS infoClass)
156{
157 DWORD retsize = 0;
158 GetTokenInformation(token, infoClass, nullptr, 0, &retsize);
159 if (retsize) {
160 void *tokenBuffer = malloc(retsize);
161 if (::GetTokenInformation(token, infoClass, tokenBuffer, retsize, &retsize))
162 return reinterpret_cast<T *>(tokenBuffer);
163 else
164 free(tokenBuffer);
165 }
166 return nullptr;
167}
168
169/*
170 Takes a copy of the original SID and stores it into dstSid.
171 The copy can be destroyed using free().
172*/
173static void copySID(PSID &dstSid, PSID srcSid)
174{
175 DWORD sidLen = GetLengthSid(srcSid);
176 dstSid = reinterpret_cast<PSID>(malloc(sidLen));
177 Q_CHECK_PTR(dstSid);
178 CopySid(sidLen, dstSid, srcSid);
179}
180
181GlobalSid::GlobalSid()
182{
183 HANDLE hnd = ::GetCurrentProcess();
184 HANDLE token = nullptr;
185 if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) {
186 // Create SID for current user
187 if (auto info = getTokenInfo<TOKEN_USER>(token, TokenUser)) {
188 copySID(currentUserSID, info->User.Sid);
189 free(info);
190 }
191
192 // Create SID for the current user's primary group.
193 if (auto info = getTokenInfo<TOKEN_GROUPS>(token, TokenGroups)) {
194 copySID(currentGroupSID, info->Groups[0].Sid);
195 free(info);
196 }
197 ::CloseHandle(token);
198 }
199
200 token = nullptr;
201 if (::OpenProcessToken(hnd,
202 TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | STANDARD_RIGHTS_READ,
203 &token)) {
204 ::DuplicateToken(token, SecurityImpersonation, &currentUserImpersonatedToken);
205 ::CloseHandle(token);
206 }
207
208 // Create SID for Everyone (World)
209 SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY };
210 AllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &worldSID);
211}
212
213Q_GLOBAL_STATIC(GlobalSid, initGlobalSid)
214
215
221class QAuthzResourceManager
222{
223public:
224 QAuthzResourceManager();
225 ~QAuthzResourceManager();
226
227 bool isValid() const { return resourceManager != nullptr; }
228
229private:
230 friend class QAuthzClientContext;
231 Q_DISABLE_COPY_MOVE(QAuthzResourceManager)
232
233 AUTHZ_RESOURCE_MANAGER_HANDLE resourceManager;
234};
235
242class QAuthzClientContext
243{
244public:
245 // Tag to differentiate SID and TOKEN constructors. Those two types are pointers to void.
246 struct TokenTag
247 {
248 };
249
250 QAuthzClientContext(const QAuthzResourceManager &rm, PSID pSID);
251 QAuthzClientContext(const QAuthzResourceManager &rm, HANDLE tokenHandle, TokenTag);
252
253 ~QAuthzClientContext();
254
255 bool isValid() const { return context != nullptr; }
256
257 static constexpr ACCESS_MASK InvalidAccess = ~ACCESS_MASK(0);
258
259 ACCESS_MASK accessMask(PSECURITY_DESCRIPTOR pSD) const;
260
261private:
262 Q_DISABLE_COPY_MOVE(QAuthzClientContext)
263 AUTHZ_CLIENT_CONTEXT_HANDLE context = nullptr;
264};
265
266QAuthzResourceManager::QAuthzResourceManager()
267{
268 if (!AuthzInitializeResourceManager(AUTHZ_RM_FLAG_NO_AUDIT, nullptr, nullptr, nullptr, nullptr,
269 &resourceManager)) {
270 resourceManager = nullptr;
271 }
272}
273
274QAuthzResourceManager::~QAuthzResourceManager()
275{
276 if (resourceManager)
277 AuthzFreeResourceManager(resourceManager);
278}
279
287QAuthzClientContext::QAuthzClientContext(const QAuthzResourceManager &rm, PSID pSID)
288{
289 if (!rm.isValid())
290 return;
291
292 LUID unusedId = {};
293
294 if (!AuthzInitializeContextFromSid(AUTHZ_SKIP_TOKEN_GROUPS, pSID, rm.resourceManager, nullptr,
295 unusedId, nullptr, &context)) {
296 context = nullptr;
297 }
298}
299
305QAuthzClientContext::QAuthzClientContext(const QAuthzResourceManager &rm, HANDLE tokenHandle,
306 TokenTag)
307{
308 if (!rm.isValid())
309 return;
310
311 LUID unusedId = {};
312
313 if (!AuthzInitializeContextFromToken(0, tokenHandle, rm.resourceManager, nullptr, unusedId,
314 nullptr, &context)) {
315 context = nullptr;
316 }
317}
318
319QAuthzClientContext::~QAuthzClientContext()
320{
321 if (context)
322 AuthzFreeContext(context);
323}
324
332ACCESS_MASK QAuthzClientContext::accessMask(PSECURITY_DESCRIPTOR pSD) const
333{
334 if (!isValid())
335 return InvalidAccess;
336
337 AUTHZ_ACCESS_REQUEST accessRequest = {};
338 AUTHZ_ACCESS_REPLY accessReply = {};
339 ACCESS_MASK accessMask = 0;
340 DWORD error = 0;
341
342 accessRequest.DesiredAccess = MAXIMUM_ALLOWED;
343
344 accessReply.ResultListLength = 1;
345 accessReply.GrantedAccessMask = &accessMask;
346 accessReply.Error = &error;
347
348 if (!AuthzAccessCheck(0, context, &accessRequest, nullptr, pSD, nullptr, 0, &accessReply,
349 nullptr)
350 || error != 0) {
351 return InvalidAccess;
352 }
353
354 return accessMask;
355}
356
357enum NonSpecificPermission {
358 ReadPermission = 0x4,
359 WritePermission = 0x2,
360 ExePermission = 0x1,
361 AllPermissions = ReadPermission | WritePermission | ExePermission
362};
363Q_DECLARE_FLAGS(NonSpecificPermissions, NonSpecificPermission)
364Q_DECLARE_OPERATORS_FOR_FLAGS(NonSpecificPermissions)
365
366enum PermissionTag { OtherTag = 0, GroupTag = 4, UserTag = 8, OwnerTag = 12 };
367
368constexpr NonSpecificPermissions toNonSpecificPermissions(PermissionTag tag,
369 QFileDevice::Permissions permissions)
370{
371 return NonSpecificPermissions::fromInt((permissions.toInt() >> int(tag)) & 0x7);
372}
373
374[[maybe_unused]] // Not currently used; included to show how to do it (without bit-rotting).
375constexpr QFileDevice::Permissions toSpecificPermissions(PermissionTag tag,
376 NonSpecificPermissions permissions)
377{
378 return QFileDevice::Permissions::fromInt(permissions.toInt() << int(tag));
379}
380
381} // anonymous namespace
382#endif // QT_CONFIG(fslibs)
383
384#if QT_DEPRECATED_SINCE(6,6)
386#endif
387
389
392
400{
403 != 0;
404}
405
413{
416 == 1;
417}
418
426{
429 ;
430}
432
464QNativeFilePermissions::QNativeFilePermissions(std::optional<QFileDevice::Permissions> perms,
465 bool isDir)
466{
467#if QT_CONFIG(fslibs)
468 if (!perms) {
469 ok = true;
470 return;
471 }
472
473 initGlobalSid();
474
475 const auto permissions = *perms;
476
477 PACL acl = reinterpret_cast<PACL>(aclStorage);
478
479 if (!InitializeAcl(acl, sizeof(aclStorage), ACL_REVISION))
480 return;
481
482 struct Masks
483 {
484 ACCESS_MASK denyMask, allowMask;
485 };
486
487 auto makeMasks = [isDir](NonSpecificPermissions allowPermissions,
488 NonSpecificPermissions denyPermissions, bool owner) {
489 constexpr ACCESS_MASK AllowRead = FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA;
490 constexpr ACCESS_MASK DenyRead = FILE_READ_DATA | FILE_READ_EA;
491
492 constexpr ACCESS_MASK AllowWrite =
493 FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA;
494 constexpr ACCESS_MASK DenyWrite = AllowWrite | FILE_DELETE_CHILD;
495 constexpr ACCESS_MASK DenyWriteOwner =
496 FILE_WRITE_DATA | FILE_WRITE_EA | FILE_APPEND_DATA | FILE_DELETE_CHILD;
497
498 constexpr ACCESS_MASK AllowExe = FILE_EXECUTE;
499 constexpr ACCESS_MASK DenyExe = AllowExe;
500
501 constexpr ACCESS_MASK StdRightsOther =
502 STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | SYNCHRONIZE;
503 constexpr ACCESS_MASK StdRightsOwner =
504 STANDARD_RIGHTS_ALL | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE;
505
506 ACCESS_MASK allow = owner ? StdRightsOwner : StdRightsOther;
507 ACCESS_MASK deny = 0;
508
509 if (denyPermissions & ReadPermission)
510 deny |= DenyRead;
511
512 if (denyPermissions & WritePermission)
513 deny |= owner ? DenyWriteOwner : DenyWrite;
514
515 if (denyPermissions & ExePermission)
516 deny |= DenyExe;
517
518 if (allowPermissions & ReadPermission)
519 allow |= AllowRead;
520
521 if (allowPermissions & WritePermission)
522 allow |= AllowWrite;
523
524 if (allowPermissions & ExePermission)
525 allow |= AllowExe;
526
527 // Give the owner "full access" if all the permissions are allowed
528 if (owner && allowPermissions == AllPermissions)
529 allow |= FILE_DELETE_CHILD;
530
531 if (isDir
532 && (allowPermissions & (WritePermission | ExePermission))
533 == (WritePermission | ExePermission)) {
534 allow |= FILE_DELETE_CHILD;
535 }
536
537 return Masks { deny, allow };
538 };
539
540 auto userPermissions = toNonSpecificPermissions(OwnerTag, permissions)
541 | toNonSpecificPermissions(UserTag, permissions);
542 auto groupPermissions = toNonSpecificPermissions(GroupTag, permissions);
543 auto otherPermissions = toNonSpecificPermissions(OtherTag, permissions);
544
545 auto userMasks = makeMasks(userPermissions,
546 ~userPermissions & (groupPermissions | otherPermissions), true);
547 auto groupMasks = makeMasks(groupPermissions, ~groupPermissions & otherPermissions, false);
548 auto otherMasks = makeMasks(otherPermissions, {}, false);
549
550 const DWORD aceFlags = isDir ? OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE : 0;
551 const bool reorderGroupDeny = (groupMasks.denyMask & userMasks.allowMask) == 0;
552
553 const auto addDenyAce = [acl, aceFlags](const Masks &masks, PSID pSID) {
554 if (masks.denyMask)
555 return AddAccessDeniedAceEx(acl, ACL_REVISION, aceFlags, masks.denyMask, pSID);
556 return TRUE;
557 };
558
559 const auto addAllowAce = [acl, aceFlags](const Masks &masks, PSID pSID) {
560 if (masks.allowMask)
561 return AddAccessAllowedAceEx(acl, ACL_REVISION, aceFlags, masks.allowMask, pSID);
562 return TRUE;
563 };
564
565 if (!addDenyAce(userMasks, currentUserSID))
566 return;
567
568 if (reorderGroupDeny) {
569 if (!addDenyAce(groupMasks, currentGroupSID))
570 return;
571 }
572
573 if (!addAllowAce(userMasks, currentUserSID))
574 return;
575
576 if (!reorderGroupDeny) {
577 if (!addDenyAce(groupMasks, currentGroupSID))
578 return;
579 }
580
581 if (!addAllowAce(groupMasks, currentGroupSID))
582 return;
583
584 if (!addAllowAce(otherMasks, worldSID))
585 return;
586
587 if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
588 return;
589
590 if (!SetSecurityDescriptorOwner(&sd, currentUserSID, FALSE))
591 return;
592
593 if (!SetSecurityDescriptorGroup(&sd, currentGroupSID, FALSE))
594 return;
595
596 if (!SetSecurityDescriptorDacl(&sd, TRUE, acl, FALSE))
597 return;
598
599 sa.nLength = sizeof(sa);
600 sa.lpSecurityDescriptor = &sd;
601 sa.bInheritHandle = FALSE;
602
603 isNull = false;
604#else
605 Q_UNUSED(perms);
606 Q_UNUSED(isDir);
607#endif // QT_CONFIG(fslibs)
608 ok = true;
609}
610
619SECURITY_ATTRIBUTES *QNativeFilePermissions::securityAttributes()
620{
621 Q_ASSERT(ok);
622 return isNull ? nullptr : &sa;
623}
624
625static inline bool toFileTime(const QDateTime &date, FILETIME *fileTime)
626{
627 SYSTEMTIME sTime;
628 if (date.timeSpec() == Qt::LocalTime) {
629 SYSTEMTIME lTime;
630 const QDate d = date.date();
631 const QTime t = date.time();
632
633 lTime.wYear = d.year();
634 lTime.wMonth = d.month();
635 lTime.wDay = d.day();
636 lTime.wHour = t.hour();
637 lTime.wMinute = t.minute();
638 lTime.wSecond = t.second();
639 lTime.wMilliseconds = t.msec();
640 lTime.wDayOfWeek = d.dayOfWeek() % 7;
641
642 if (!::TzSpecificLocalTimeToSystemTime(nullptr, &lTime, &sTime))
643 return false;
644 } else {
645 QDateTime utcDate = date.toUTC();
646 const QDate d = utcDate.date();
647 const QTime t = utcDate.time();
648
649 sTime.wYear = d.year();
650 sTime.wMonth = d.month();
651 sTime.wDay = d.day();
652 sTime.wHour = t.hour();
653 sTime.wMinute = t.minute();
654 sTime.wSecond = t.second();
655 sTime.wMilliseconds = t.msec();
656 sTime.wDayOfWeek = d.dayOfWeek() % 7;
657 }
658
659 return ::SystemTimeToFileTime(&sTime, fileTime);
660}
661
663{
665 HANDLE handle = CreateFile((wchar_t *)link.nativeFilePath().utf16(), FILE_READ_EA,
666 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
667 OPEN_EXISTING,
668 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
669 if (handle != INVALID_HANDLE_VALUE) {
672 Q_CHECK_PTR(rdb);
673 DWORD retsize = 0;
674 if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, rdb, bufsize, &retsize,
675 nullptr)) {
676 if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
677 int length = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
678 int offset = rdb->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
679 const wchar_t* PathBuffer = &rdb->MountPointReparseBuffer.PathBuffer[offset];
681 } else if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
682 int length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
683 int offset = rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
684 const wchar_t* PathBuffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[offset];
686 }
687 // remove "\\?\", "\??\" or "\\?\UNC\"
688 result = QFileSystemEntry::removeUncOrLongPathPrefix(result);
689 }
690 free(rdb);
691 CloseHandle(handle);
692
693#if QT_CONFIG(fslibs) && QT_CONFIG(regularexpression)
694 initGlobalSid();
695 QRegularExpression matchVolumeRe("^Volume\\{([a-z]|[0-9]|-)+\\}\\\\"_L1,
697 auto matchVolume = matchVolumeRe.match(result);
698 if (matchVolume.hasMatch()) {
699 Q_ASSERT(matchVolume.capturedStart() == 0);
700 DWORD len;
701 wchar_t buffer[MAX_PATH];
702 const QString volumeName = "\\\\?\\"_L1 + matchVolume.captured();
703 if (GetVolumePathNamesForVolumeName(reinterpret_cast<LPCWSTR>(volumeName.utf16()),
705 != 0) {
706 result.replace(0, matchVolume.capturedLength(), QString::fromWCharArray(buffer));
707 }
708 }
709#endif // QT_CONFIG(fslibs)
710 }
711 return result;
712}
713
715{
716#if QT_CONFIG(fslibs)
717 QString ret;
718
719 IShellLink *psl; // pointer to IShellLink i/f
720 WIN32_FIND_DATA wfd;
721 wchar_t szGotPath[MAX_PATH];
722
723 QComHelper comHelper;
724
725 // Get pointer to the IShellLink interface.
726 HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
727 (LPVOID *)&psl);
728
729 if (SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface.
730 IPersistFile *ppf;
731 hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
732 if (SUCCEEDED(hres)) {
733 hres = ppf->Load((LPOLESTR)link.nativeFilePath().utf16(), STGM_READ);
734 //The original path of the link is retrieved. If the file/folder
735 //was moved, the return value still have the old path.
736 if (SUCCEEDED(hres)) {
737 if (psl->GetPath(szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR)
738 ret = QString::fromWCharArray(szGotPath);
739 }
740 ppf->Release();
741 }
742 psl->Release();
743 }
744
745 return ret;
746#else
747 Q_UNUSED(link);
748 return QString();
749#endif // QT_CONFIG(fslibs)
750}
751
752static bool uncShareExists(const QString &server)
753{
754 // This code assumes the UNC path is always like \\?\UNC\server...
755 const auto parts = QStringView{server}.split(u'\\', Qt::SkipEmptyParts);
756 if (parts.count() >= 3) {
757 QStringList shares;
758 if (QFileSystemEngine::uncListSharesOnServer("\\\\"_L1 + parts.at(2), &shares))
759 return parts.count() < 4
760 || shares.contains(parts.at(3).toString(), Qt::CaseInsensitive);
761 }
762 return false;
763}
764
765static inline bool getFindData(QString path, WIN32_FIND_DATA &findData)
766{
767 // path should not end with a trailing slash
768 while (path.endsWith(u'\\'))
769 path.chop(1);
770
771 // can't handle drives
772 if (!path.endsWith(u':')) {
773 HANDLE hFind = ::FindFirstFile((wchar_t*)path.utf16(), &findData);
774 if (hFind != INVALID_HANDLE_VALUE) {
775 ::FindClose(hFind);
776 return true;
777 }
778 }
779
780 return false;
781}
782
783class FileOperationProgressSink : public IFileOperationProgressSink
784{
785public:
787 : ref(1)
788 {}
790
791 ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; }
792 ULONG STDMETHODCALLTYPE Release() override
793 {
794 if (--ref == 0) {
795 delete this;
796 return 0;
797 }
798 return ref;
799 }
800 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override
801 {
802 if (!ppvObject)
803 return E_POINTER;
804
805 *ppvObject = nullptr;
806
807 if (iid == __uuidof(IUnknown)) {
808 *ppvObject = static_cast<IUnknown*>(this);
809 } else if (iid == __uuidof(IFileOperationProgressSink)) {
810 *ppvObject = static_cast<IFileOperationProgressSink*>(this);
811 }
812
813 if (*ppvObject) {
814 AddRef();
815 return S_OK;
816 }
817
818 return E_NOINTERFACE;
819 }
820
821 HRESULT STDMETHODCALLTYPE StartOperations() override { return S_OK; }
822 HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT) override { return S_OK; }
823 HRESULT STDMETHODCALLTYPE PreRenameItem(DWORD, IShellItem *, LPCWSTR) override { return S_OK; }
824 HRESULT STDMETHODCALLTYPE PostRenameItem(DWORD, IShellItem *, LPCWSTR, HRESULT,
825 IShellItem *) override
826 { return S_OK; }
827 HRESULT STDMETHODCALLTYPE PreMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR) override
828 { return S_OK; }
829 HRESULT STDMETHODCALLTYPE PostMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT,
830 IShellItem *) override
831 { return S_OK; }
832 HRESULT STDMETHODCALLTYPE PreCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR) override
833 { return S_OK; }
834 HRESULT STDMETHODCALLTYPE PostCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT,
835 IShellItem *) override
836 { return S_OK; }
837 HRESULT STDMETHODCALLTYPE PreDeleteItem(DWORD dwFlags, IShellItem *) override
838 {
839 // stop the operation if the file will be deleted rather than trashed
840 return (dwFlags & TSF_DELETE_RECYCLE_IF_POSSIBLE) ? S_OK : E_FAIL;
841 }
842 HRESULT STDMETHODCALLTYPE PostDeleteItem(DWORD /* dwFlags */, IShellItem * /* psiItem */,
843 HRESULT /* hrDelete */,
844 IShellItem *psiNewlyCreated) override
845 {
846 if (psiNewlyCreated) {
847 wchar_t *pszName = nullptr;
848 psiNewlyCreated->GetDisplayName(SIGDN_FILESYSPATH, &pszName);
849 if (pszName) {
851 CoTaskMemFree(pszName);
852 }
853 }
854 return S_OK;
855 }
856 HRESULT STDMETHODCALLTYPE PreNewItem(DWORD, IShellItem *, LPCWSTR) override { return S_OK; }
857 HRESULT STDMETHODCALLTYPE PostNewItem(DWORD, IShellItem *, LPCWSTR, LPCWSTR, DWORD, HRESULT,
858 IShellItem *) override
859 { return S_OK; }
860 HRESULT STDMETHODCALLTYPE UpdateProgress(UINT, UINT) override { return S_OK; }
861 HRESULT STDMETHODCALLTYPE ResetTimer() override { return S_OK; }
862 HRESULT STDMETHODCALLTYPE PauseTimer() override { return S_OK; }
863 HRESULT STDMETHODCALLTYPE ResumeTimer() override { return S_OK; }
864
866private:
867 ULONG ref;
868};
869
870bool QFileSystemEngine::uncListSharesOnServer(const QString &server, QStringList *list)
871{
872 DWORD res = ERROR_NOT_SUPPORTED;
873 SHARE_INFO_1 *BufPtr, *p;
874 DWORD er = 0, tr = 0, resume = 0, i;
875 do {
876 res = NetShareEnum((wchar_t *)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr,
877 &resume);
878 if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) {
879 p = BufPtr;
880 for (i = 1; i <= er; ++i) {
881 if (list && p->shi1_type == 0)
882 list->append(QString::fromWCharArray(p->shi1_netname));
883 p++;
884 }
885 }
886 NetApiBufferFree(BufPtr);
887 } while (res == ERROR_MORE_DATA);
888 return res == ERROR_SUCCESS;
889}
890
891void QFileSystemEngine::clearWinStatData(QFileSystemMetaData &data)
892{
893 data.size_ = 0;
894 data.fileAttribute_ = 0;
895 data.birthTime_ = FILETIME();
896 data.changeTime_ = FILETIME();
897 data.lastAccessTime_ = FILETIME();
898 data.lastWriteTime_ = FILETIME();
899}
900
901//static
904{
906 if (!ret.isEmpty() && ret.isRelative()) {
907 QString target = absoluteName(link).path() + u'/' + ret.filePath();
909 }
910 return ret;
911}
912
913//static
916{
917 Q_CHECK_FILE_NAME(link, link);
918
919 if (data.missingFlags(QFileSystemMetaData::LinkType))
921
923 if (data.isLnkFile())
924 target = readLink(link);
925 else if (data.isLink())
926 target = readSymLink(link);
927 return QFileSystemEntry(target);
928}
929
930//static
931QFileSystemEntry QFileSystemEngine::junctionTarget(const QFileSystemEntry &link,
933{
934 Q_CHECK_FILE_NAME(link, link);
935
936 if (data.missingFlags(QFileSystemMetaData::JunctionType))
938
940 if (data.isJunction())
941 target = readSymLink(link);
943 if (!target.isEmpty() && ret.isRelative()) {
944 target.prepend(absoluteName(link).path() + u'/');
946 }
947 return ret;
948}
949
950//static
953{
955
958
959 if (data.exists())
960 return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath()));
961 else
962 return QFileSystemEntry();
963}
964
965//static
966QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path)
967{
969
970 // can be //server or //server/share
971 QString absPath;
973 wchar_t *fileName = nullptr;
974 DWORD retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
975 if (retLen > (DWORD)buf.size()) {
976 buf.resize(retLen);
977 retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
978 }
979 if (retLen != 0)
980 absPath = QString::fromWCharArray(buf.data(), retLen);
981
982 // This is really ugly, but GetFullPathName strips off whitespace at the end.
983 // If you for instance write ". " in the lineedit of QFileDialog,
984 // (which is an invalid filename) this function will strip the space off and viola,
985 // the file is later reported as existing. Therefore, we re-add the whitespace that
986 // was at the end of path in order to keep the filename invalid.
987 if (!path.isEmpty() && path.at(path.size() - 1) == u' ')
988 absPath.append(u' ');
989 return absPath;
990}
991
992//static
994{
996
997 QString ret;
998
999 if (!entry.isRelative()) {
1000 if (entry.isAbsolute() && entry.isClean())
1001 ret = entry.filePath();
1002 else
1003 ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(entry.filePath()));
1004 } else {
1005 ret = QDir::cleanPath(QDir::currentPath() + u'/' + entry.filePath());
1006 }
1007
1008 // The path should be absolute at this point.
1009 // From the docs :
1010 // Absolute paths begin with the directory separator "/"
1011 // (optionally preceded by a drive specification under Windows).
1012 if (ret.at(0) != u'/') {
1013 Q_ASSERT(ret.length() >= 2);
1014 Q_ASSERT(ret.at(0).isLetter());
1015 Q_ASSERT(ret.at(1) == u':');
1016
1017 // Force uppercase drive letters.
1018 ret[0] = ret.at(0).toUpper();
1019 }
1021}
1022
1023// File ID for Windows up to version 7 and FAT32 drives
1025{
1026 BY_HANDLE_FILE_INFORMATION info;
1027 if (GetFileInformationByHandle(handle, &info)) {
1028 char buffer[sizeof "01234567:0123456701234567"];
1029 qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx",
1030 info.dwVolumeSerialNumber,
1031 info.nFileIndexHigh,
1032 info.nFileIndexLow);
1033 return buffer;
1034 }
1035 return QByteArray();
1036}
1037
1038// File ID for Windows starting from version 8.
1040{
1041#if !defined(QT_BOOTSTRAPPED)
1043 FILE_ID_INFO infoEx;
1044 if (GetFileInformationByHandleEx(
1045 handle,
1046 static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8
1047 &infoEx, sizeof(FILE_ID_INFO))) {
1048 result = QByteArray::number(infoEx.VolumeSerialNumber, 16);
1049 result += ':';
1050 // Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one.
1051 result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId),
1052 int(sizeof(infoEx.FileId)))
1053 .toHex();
1054 } else {
1055 // GetFileInformationByHandleEx() is observed to fail for FAT32, QTBUG-74759
1056 result = fileId(handle);
1057 }
1058 return result;
1059#else // !QT_BOOTSTRAPPED
1060 return fileId(handle);
1061#endif
1062}
1063
1064//static
1066{
1068
1070
1071 const HANDLE handle = CreateFile((wchar_t *)entry.nativeFilePath().utf16(), 0, FILE_SHARE_READ,
1072 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1073 if (handle != INVALID_HANDLE_VALUE) {
1074 result = id(handle);
1075 CloseHandle(handle);
1076 }
1077 return result;
1078}
1079
1080//static
1082{
1083 return fileIdWin8(HANDLE(fHandle));
1084}
1085
1086//static
1087bool QFileSystemEngine::setFileTime(HANDLE fHandle, const QDateTime &newDate,
1089{
1090 FILETIME fTime;
1091 FILETIME *pLastWrite = nullptr;
1092 FILETIME *pLastAccess = nullptr;
1093 FILETIME *pCreationTime = nullptr;
1094
1095 switch (time) {
1097 pLastWrite = &fTime;
1098 break;
1099
1101 pLastAccess = &fTime;
1102 break;
1103
1105 pCreationTime = &fTime;
1106 break;
1107
1108 default:
1109 error = QSystemError(ERROR_INVALID_PARAMETER, QSystemError::NativeError);
1110 return false;
1111 }
1112
1113 if (!toFileTime(newDate, &fTime))
1114 return false;
1115
1116 if (!::SetFileTime(fHandle, pCreationTime, pLastAccess, pLastWrite)) {
1117 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1118 return false;
1119 }
1120 return true;
1121}
1122
1123QString QFileSystemEngine::owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own)
1124{
1125 QString name;
1126#if QT_CONFIG(fslibs)
1128 initGlobalSid();
1129 {
1130 PSID pOwner = 0;
1131 PSECURITY_DESCRIPTOR pSD;
1132 if (GetNamedSecurityInfo(
1133 reinterpret_cast<const wchar_t *>(entry.nativeFilePath().utf16()),
1134 SE_FILE_OBJECT,
1135 own == QAbstractFileEngine::OwnerGroup ? GROUP_SECURITY_INFORMATION
1136 : OWNER_SECURITY_INFORMATION,
1137 own == QAbstractFileEngine::OwnerUser ? &pOwner : nullptr,
1138 own == QAbstractFileEngine::OwnerGroup ? &pOwner : nullptr, nullptr,
1139 nullptr, &pSD)
1140 == ERROR_SUCCESS) {
1141 DWORD lowner = 64;
1142 DWORD ldomain = 64;
1143 QVarLengthArray<wchar_t, 64> owner(lowner);
1144 QVarLengthArray<wchar_t, 64> domain(ldomain);
1145 SID_NAME_USE use = SidTypeUnknown;
1146 // First call, to determine size of the strings (with '\0').
1147 if (!LookupAccountSid(nullptr, pOwner, (LPWSTR)owner.data(), &lowner, domain.data(),
1148 &ldomain, &use)) {
1149 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
1150 if (lowner > (DWORD)owner.size())
1151 owner.resize(lowner);
1152 if (ldomain > (DWORD)domain.size())
1153 domain.resize(ldomain);
1154 // Second call, try on resized buf-s
1155 if (!LookupAccountSid(nullptr, pOwner, owner.data(), &lowner, domain.data(),
1156 &ldomain, &use)) {
1157 lowner = 0;
1158 }
1159 } else {
1160 lowner = 0;
1161 }
1162 }
1163 if (lowner != 0)
1164 name = QString::fromWCharArray(owner.data());
1165 LocalFree(pSD);
1166 }
1167 }
1168 }
1169#else
1170 Q_UNUSED(entry);
1171 Q_UNUSED(own);
1172#endif
1173 return name;
1174}
1175
1176//static
1177bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSystemMetaData &data,
1178 QFileSystemMetaData::MetaDataFlags what)
1179{
1180#if QT_CONFIG(fslibs)
1182 initGlobalSid();
1183
1184 QString fname = entry.nativeFilePath();
1185 PSID pOwner;
1186 PSID pGroup;
1187 PACL pDacl;
1188 PSECURITY_DESCRIPTOR pSD;
1189
1190 // pDacl is unused directly by the code below, but it is still needed here because
1191 // access checks below return incorrect results unless DACL_SECURITY_INFORMATION is
1192 // passed to this call.
1193 DWORD res = GetNamedSecurityInfo(
1194 reinterpret_cast<const wchar_t *>(fname.utf16()), SE_FILE_OBJECT,
1195 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
1196 &pOwner, &pGroup, &pDacl, nullptr, &pSD);
1197 if (res == ERROR_SUCCESS) {
1198 QAuthzResourceManager rm;
1199
1200 auto addPermissions = [&data](ACCESS_MASK accessMask,
1203 QFileSystemMetaData::MetaDataFlag executeFlags) {
1204 // Check for generic permissions and file-specific bits that most closely
1205 // represent POSIX permissions.
1206
1207 // Constants like FILE_GENERIC_{READ,WRITE,EXECUTE} cannot be used
1208 // here because they contain permission bits shared between all of them.
1209 if (accessMask & (GENERIC_READ | FILE_READ_DATA))
1210 data.entryFlags |= readFlags;
1211 if (accessMask & (GENERIC_WRITE | FILE_WRITE_DATA))
1212 data.entryFlags |= writeFlags;
1213 if (accessMask & (GENERIC_EXECUTE | FILE_EXECUTE))
1214 data.entryFlags |= executeFlags;
1215 };
1216
1217 if (what & QFileSystemMetaData::UserPermissions && currentUserImpersonatedToken) {
1219 QAuthzClientContext context(rm, currentUserImpersonatedToken,
1220 QAuthzClientContext::TokenTag {});
1221 addPermissions(context.accessMask(pSD),
1225 }
1226
1229 QAuthzClientContext context(rm, pOwner);
1230 addPermissions(context.accessMask(pSD),
1234 }
1235
1238 QAuthzClientContext context(rm, pGroup);
1239 addPermissions(context.accessMask(pSD),
1243 }
1244
1247 QAuthzClientContext context(rm, worldSID);
1248 addPermissions(context.accessMask(pSD),
1252 }
1253
1254 LocalFree(pSD);
1255 }
1256 } else
1257#endif
1258 {
1259 //### what to do with permissions if we don't use NTFS
1260 // for now just add all permissions and what about exe missions ??
1261 // also qt_ntfs_permission_lookup is now not set by default ... should it ?
1265
1266 if (!(data.fileAttribute_ & FILE_ATTRIBUTE_READONLY)) {
1270 }
1271
1272 QString fname = entry.filePath();
1273 QString ext = fname.right(4).toLower();
1274 if (data.isDirectory() || ext == ".exe"_L1 || ext == ".com"_L1
1275 || ext == ".bat"_L1 || ext == ".pif"_L1 || ext == ".cmd"_L1) {
1280 }
1284 // calculate user permissions
1286 if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), R_OK) == 0)
1289 }
1291 if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), W_OK) == 0)
1294 }
1295 }
1296
1297 return data.hasFlags(what);
1298}
1299
1301{
1302 bool entryExists = false;
1303 DWORD fileAttrib = 0;
1304 if (fname.isDriveRoot()) {
1305 // a valid drive ??
1306 const UINT oldErrorMode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1307 DWORD drivesBitmask = ::GetLogicalDrives();
1308 ::SetErrorMode(oldErrorMode);
1309 int drivebit =
1310 1 << (fname.filePath().at(0).toUpper().unicode() - u'A');
1311 if (drivesBitmask & drivebit) {
1312 fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM;
1313 entryExists = true;
1314 }
1315 } else {
1316 const QString &path = fname.nativeFilePath();
1317 bool is_dir = false;
1318 if (path.startsWith("\\\\?\\UNC"_L1)) {
1319 // UNC - stat doesn't work for all cases (Windows bug)
1320 int s = path.indexOf(path.at(0),7);
1321 if (s > 0) {
1322 // "\\?\UNC\server\..."
1323 s = path.indexOf(path.at(0),s+1);
1324 if (s > 0) {
1325 // "\\?\UNC\server\share\..."
1326 if (s == path.size() - 1) {
1327 // "\\?\UNC\server\share\"
1328 is_dir = true;
1329 } else {
1330 // "\\?\UNC\server\share\notfound"
1331 }
1332 } else {
1333 // "\\?\UNC\server\share"
1334 is_dir = true;
1335 }
1336 } else {
1337 // "\\?\UNC\server"
1338 is_dir = true;
1339 }
1340 }
1341 if (is_dir && uncShareExists(path)) {
1342 // looks like a UNC dir, is a dir.
1343 fileAttrib = FILE_ATTRIBUTE_DIRECTORY;
1344 entryExists = true;
1345 }
1346 }
1347 if (entryExists)
1348 data.fillFromFileAttribute(fileAttrib);
1349 return entryExists;
1350}
1351
1353{
1354 bool filledData = false;
1355 // This assumes the last call to a Windows API failed.
1356 int errorCode = GetLastError();
1357 if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
1358 WIN32_FIND_DATA findData;
1359 if (getFindData(fname.nativeFilePath(), findData)
1360 && findData.dwFileAttributes != INVALID_FILE_ATTRIBUTES) {
1361 data.fillFromFindData(findData, true, fname.isDriveRoot());
1362 filledData = true;
1363 }
1364 }
1365 return filledData;
1366}
1367
1368//static
1370 QFileSystemMetaData::MetaDataFlags what)
1371{
1372 auto fHandle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
1373 if (fHandle != INVALID_HANDLE_VALUE) {
1374 return fillMetaData(fHandle, data, what);
1375 }
1376 return false;
1377}
1378
1379//static
1381 QFileSystemMetaData::MetaDataFlags what)
1382{
1383 data.entryFlags &= ~what;
1384 clearWinStatData(data);
1385 BY_HANDLE_FILE_INFORMATION fileInfo;
1386 UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1387 if (GetFileInformationByHandle(fHandle , &fileInfo)) {
1388 data.fillFromFindInfo(fileInfo);
1389 }
1390 SetErrorMode(oldmode);
1391 return data.hasFlags(what);
1392}
1393
1394//static
1396 QFileSystemMetaData::MetaDataFlags what)
1397{
1398 Q_CHECK_FILE_NAME(entry, false);
1399 what |= QFileSystemMetaData::WinLnkType | QFileSystemMetaData::WinStatFlags;
1400 data.entryFlags &= ~what;
1401
1402 QFileSystemEntry fname;
1403 data.knownFlagsMask |= QFileSystemMetaData::WinLnkType;
1404 // Check for ".lnk": Directories named ".lnk" should be skipped, corrupted
1405 // link files should still be detected as links.
1406 const QString origFilePath = entry.filePath();
1407 if (origFilePath.endsWith(".lnk"_L1) && !isDirPath(origFilePath, nullptr)) {
1410 } else {
1411 fname = entry;
1412 }
1413
1414 if (fname.isEmpty()) {
1415 data.knownFlagsMask |= what;
1416 clearWinStatData(data);
1417 return false;
1418 }
1419
1420 if (what & QFileSystemMetaData::WinStatFlags) {
1421 UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1422 clearWinStatData(data);
1423 WIN32_FIND_DATA findData;
1424 // The memory structure for WIN32_FIND_DATA is same as WIN32_FILE_ATTRIBUTE_DATA
1425 // for all members used by fillFindData().
1426 bool ok = ::GetFileAttributesEx(
1427 reinterpret_cast<const wchar_t *>(fname.nativeFilePath().utf16()),
1428 GetFileExInfoStandard, reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA *>(&findData));
1429 if (ok) {
1430 data.fillFromFindData(findData, false, fname.isDriveRoot());
1431 } else {
1432 const DWORD lastError = GetLastError();
1433 // disconnected drive
1434 if (lastError == ERROR_LOGON_FAILURE || lastError == ERROR_BAD_NETPATH
1435 || (!tryFindFallback(fname, data) && !tryDriveUNCFallback(fname, data))) {
1436 data.clearFlags();
1437 SetErrorMode(oldmode);
1438 return false;
1439 }
1440 }
1441 SetErrorMode(oldmode);
1442 }
1443
1445 fillPermissions(fname, data, what);
1446 if (what & QFileSystemMetaData::LinkType) {
1447 data.knownFlagsMask |= QFileSystemMetaData::LinkType;
1448 if (data.fileAttribute_ & FILE_ATTRIBUTE_REPARSE_POINT) {
1449 WIN32_FIND_DATA findData;
1450 if (getFindData(fname.nativeFilePath(), findData))
1451 data.fillFromFindData(findData, true);
1452 }
1453 }
1454 data.knownFlagsMask |= what;
1455 return data.hasFlags(what);
1456}
1457
1458static inline bool mkDir(const QString &path, SECURITY_ATTRIBUTES *securityAttributes,
1459 DWORD *lastError = nullptr)
1460{
1461 if (lastError)
1462 *lastError = 0;
1463 const QString longPath = QFSFileEnginePrivate::longFileName(path);
1464 const bool result = ::CreateDirectory((wchar_t *)longPath.utf16(), securityAttributes);
1465 // Capture lastError before any QString is freed since custom allocators might change it.
1466 if (lastError)
1467 *lastError = GetLastError();
1468 return result;
1469}
1470
1471static inline bool rmDir(const QString &path)
1472{
1473 return ::RemoveDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16());
1474}
1475
1476//static
1477bool QFileSystemEngine::isDirPath(const QString &dirPath, bool *existed)
1478{
1479 QString path = dirPath;
1480 if (path.length() == 2 && path.at(1) == u':')
1481 path += u'\\';
1482
1483 const QString longPath = QFSFileEnginePrivate::longFileName(path);
1484 DWORD fileAttrib = ::GetFileAttributes(reinterpret_cast<const wchar_t*>(longPath.utf16()));
1485 if (fileAttrib == INVALID_FILE_ATTRIBUTES) {
1486 int errorCode = GetLastError();
1487 if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
1488 WIN32_FIND_DATA findData;
1489 if (getFindData(longPath, findData))
1490 fileAttrib = findData.dwFileAttributes;
1491 }
1492 }
1493
1494 if (existed)
1495 *existed = fileAttrib != INVALID_FILE_ATTRIBUTES;
1496
1497 if (fileAttrib == INVALID_FILE_ATTRIBUTES)
1498 return false;
1499
1500 return fileAttrib & FILE_ATTRIBUTE_DIRECTORY;
1501}
1502
1503// NOTE: if \a shouldMkdirFirst is false, we assume the caller did try to mkdir
1504// before calling this function.
1505static bool createDirectoryWithParents(const QString &nativeName,
1506 SECURITY_ATTRIBUTES *securityAttributes,
1507 bool shouldMkdirFirst = true)
1508{
1509 const auto isUNCRoot = [](const QString &nativeName) {
1510 return nativeName.startsWith("\\\\"_L1)
1511 && nativeName.count(QDir::separator()) <= 3;
1512 };
1513 const auto isDriveName = [](const QString &nativeName) {
1514 return nativeName.size() == 2 && nativeName.at(1) == u':';
1515 };
1516 const auto isDir = [](const QString &nativeName) {
1517 bool exists = false;
1518 return QFileSystemEngine::isDirPath(nativeName, &exists) && exists;
1519 };
1520 // Do not try to mkdir a UNC root path or a drive letter.
1521 if (isUNCRoot(nativeName) || isDriveName(nativeName))
1522 return false;
1523
1524 if (shouldMkdirFirst) {
1525 if (mkDir(nativeName, securityAttributes))
1526 return true;
1527 }
1528
1529 const int backSlash = nativeName.lastIndexOf(QDir::separator());
1530 if (backSlash < 1)
1531 return false;
1532
1533 const QString parentNativeName = nativeName.left(backSlash);
1534 if (!createDirectoryWithParents(parentNativeName, securityAttributes))
1535 return false;
1536
1537 // try again
1538 if (mkDir(nativeName, securityAttributes))
1539 return true;
1540 return isDir(nativeName);
1541}
1542
1543//static
1544bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents,
1545 std::optional<QFile::Permissions> permissions)
1546{
1547 QString dirName = entry.filePath();
1548 Q_CHECK_FILE_NAME(dirName, false);
1549
1550 dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
1551
1552 QNativeFilePermissions nativePermissions(permissions, true);
1553 if (!nativePermissions.isOk())
1554 return false;
1555
1556 auto securityAttributes = nativePermissions.securityAttributes();
1557
1558 // try to mkdir this directory
1559 DWORD lastError;
1560 if (mkDir(dirName, securityAttributes, &lastError))
1561 return true;
1562 // mkpath should return true, if the directory already exists, mkdir false.
1563 if (!createParents)
1564 return false;
1565 if (lastError == ERROR_ALREADY_EXISTS || lastError == ERROR_ACCESS_DENIED)
1566 return isDirPath(dirName, nullptr);
1567
1568 return createDirectoryWithParents(dirName, securityAttributes, false);
1569}
1570
1571//static
1572bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
1573{
1574 QString dirName = entry.filePath();
1575 Q_CHECK_FILE_NAME(dirName, false);
1576
1577 if (removeEmptyParents) {
1578 dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
1579 for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
1580 const auto chunkRef = QStringView{dirName}.left(slash);
1581 if (chunkRef.length() == 2 && chunkRef.at(0).isLetter()
1582 && chunkRef.at(1) == u':') {
1583 break;
1584 }
1585 const QString chunk = chunkRef.toString();
1586 if (!isDirPath(chunk, nullptr))
1587 return false;
1588 if (!rmDir(chunk))
1589 return oldslash != 0;
1590 slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
1591 }
1592 return true;
1593 }
1594 return rmDir(entry.filePath());
1595}
1596
1597//static
1599{
1600 QString ret = QString::fromLatin1(qgetenv("SystemDrive"));
1601 if (ret.isEmpty())
1602 ret = "c:"_L1;
1603 ret.append(u'/');
1604 return ret;
1605}
1606
1607//static
1609{
1610 QString ret;
1611#if QT_CONFIG(fslibs)
1612 initGlobalSid();
1613 {
1614 HANDLE hnd = ::GetCurrentProcess();
1615 HANDLE token = nullptr;
1616 BOOL ok = ::OpenProcessToken(hnd, TOKEN_QUERY, &token);
1617 if (ok) {
1618 DWORD dwBufferSize = 0;
1619 // First call, to determine size of the strings (with '\0').
1620 ok = GetUserProfileDirectory(token, nullptr, &dwBufferSize);
1621 if (!ok && dwBufferSize != 0) { // We got the required buffer size
1622 wchar_t *userDirectory = new wchar_t[dwBufferSize];
1623 // Second call, now we can fill the allocated buffer.
1624 ok = GetUserProfileDirectory(token, userDirectory, &dwBufferSize);
1625 if (ok)
1626 ret = QString::fromWCharArray(userDirectory);
1627 delete [] userDirectory;
1628 }
1629 ::CloseHandle(token);
1630 }
1631 }
1632#endif
1633 if (ret.isEmpty() || !QFile::exists(ret)) {
1634 ret = QString::fromLocal8Bit(qgetenv("USERPROFILE"));
1635 if (ret.isEmpty() || !QFile::exists(ret)) {
1636 ret = QString::fromLocal8Bit(qgetenv("HOMEDRIVE"))
1637 + QString::fromLocal8Bit(qgetenv("HOMEPATH"));
1638 if (ret.isEmpty() || !QFile::exists(ret)) {
1640 if (ret.isEmpty() || !QFile::exists(ret))
1641 ret = rootPath();
1642 }
1643 }
1644 }
1646}
1647
1649{
1650 QString ret;
1651 wchar_t tempPath[MAX_PATH];
1652 const DWORD len = GetTempPath(MAX_PATH, tempPath);
1653 if (len) { // GetTempPath() can return short names, expand.
1654 wchar_t longTempPath[MAX_PATH];
1655 const DWORD longLen = GetLongPathName(tempPath, longTempPath, MAX_PATH);
1656 ret = longLen && longLen < MAX_PATH ?
1657 QString::fromWCharArray(longTempPath, longLen) :
1658 QString::fromWCharArray(tempPath, len);
1659 }
1660 if (!ret.isEmpty()) {
1661 while (ret.endsWith(u'\\'))
1662 ret.chop(1);
1664 }
1665 if (ret.isEmpty()) {
1666 ret = "C:/tmp"_L1;
1667 } else if (ret.length() >= 2 && ret[1] == u':')
1668 ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
1669 return ret;
1670}
1671
1673{
1675 fillMetaData(entry, meta,
1677 if (!(meta.exists() && meta.isDirectory()))
1678 return false;
1679
1680 // TODO: this should really be using nativeFilePath(), but that returns a path
1681 // in long format \\?\c:\foo which causes many problems later on when it's
1682 // returned through currentPath()
1683 return ::SetCurrentDirectory(reinterpret_cast<const wchar_t *>(
1684 QDir::toNativeSeparators(entry.filePath()).utf16()))
1685 != 0;
1686}
1687
1689{
1691 DWORD size = GetCurrentDirectoryW(PATH_MAX, reinterpret_cast<wchar_t *>(ret.data()));
1692 if (size > PATH_MAX) {
1693 // try again after enlarging the buffer
1694 ret.resize(size);
1695 size = GetCurrentDirectoryW(size, reinterpret_cast<wchar_t *>(ret.data()));
1696
1697 // note: the current directory may have changed underneath us; if the
1698 // new one is even bigger, we may return a truncated string!
1699 }
1700 if (size >= 2 && ret.at(1) == u':')
1701 ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
1702 ret.resize(size);
1704}
1705
1706//static
1709{
1710 bool ret = false;
1711 QComHelper comHelper;
1712 IShellLink *psl = nullptr;
1713 HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
1714 reinterpret_cast<void **>(&psl));
1715
1716 if (SUCCEEDED(hres)) {
1717 const auto name = QDir::toNativeSeparators(source.filePath());
1718 const auto pathName = QDir::toNativeSeparators(source.path());
1719 if (SUCCEEDED(psl->SetPath(reinterpret_cast<const wchar_t *>(name.utf16())))
1720 && SUCCEEDED(psl->SetWorkingDirectory(
1721 reinterpret_cast<const wchar_t *>(pathName.utf16())))) {
1722 IPersistFile *ppf = nullptr;
1723 if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void **>(&ppf)))) {
1724 ret = SUCCEEDED(ppf->Save(
1725 reinterpret_cast<const wchar_t *>(target.filePath().utf16()), TRUE));
1726 ppf->Release();
1727 }
1728 }
1729 psl->Release();
1730 }
1731
1732 if (!ret)
1733 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1734
1735 return ret;
1736}
1737
1738//static
1741{
1742 bool ret = ::CopyFile((wchar_t*)source.nativeFilePath().utf16(),
1743 (wchar_t*)target.nativeFilePath().utf16(), true) != 0;
1744 if (!ret)
1745 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1746 return ret;
1747}
1748
1749//static
1752{
1753 Q_CHECK_FILE_NAME(source, false);
1754 Q_CHECK_FILE_NAME(target, false);
1755
1756 bool ret = ::MoveFile((wchar_t*)source.nativeFilePath().utf16(),
1757 (wchar_t*)target.nativeFilePath().utf16()) != 0;
1758 if (!ret)
1759 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1760 return ret;
1761}
1762
1763//static
1766{
1767 Q_CHECK_FILE_NAME(source, false);
1768 Q_CHECK_FILE_NAME(target, false);
1769
1770 bool ret = ::MoveFileEx(reinterpret_cast<const wchar_t *>(source.nativeFilePath().utf16()),
1771 reinterpret_cast<const wchar_t *>(target.nativeFilePath().utf16()),
1772 MOVEFILE_REPLACE_EXISTING) != 0;
1773 if (!ret)
1774 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1775 return ret;
1776}
1777
1778//static
1780{
1781 Q_CHECK_FILE_NAME(entry, false);
1782
1783 bool ret = ::DeleteFile((wchar_t*)entry.nativeFilePath().utf16()) != 0;
1784 if (!ret)
1785 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1786 return ret;
1787}
1788
1789/*
1790 If possible, we use the IFileOperation implementation, which allows us to determine
1791 the location of the object in the trash.
1792 If not (likely on mingw), we fall back to the old API, which won't allow us to know
1793 that.
1794*/
1795//static
1797 QFileSystemEntry &newLocation, QSystemError &error)
1798{
1799 // we need the "display name" of the file, so can't use nativeAbsoluteFilePath
1800 const QString sourcePath = QDir::toNativeSeparators(absoluteName(source).filePath());
1801
1802 QComHelper comHelper;
1803
1804 IFileOperation *pfo = nullptr;
1805 IShellItem *deleteItem = nullptr;
1807 HRESULT hres = E_FAIL;
1808
1809 auto coUninitialize = qScopeGuard([&](){
1810 if (sink)
1811 sink->Release();
1812 if (deleteItem)
1813 deleteItem->Release();
1814 if (pfo)
1815 pfo->Release();
1816 if (!SUCCEEDED(hres))
1818 });
1819
1820 hres = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pfo));
1821 if (!pfo)
1822 return false;
1823 pfo->SetOperationFlags(FOF_ALLOWUNDO | FOFX_RECYCLEONDELETE | FOF_NOCONFIRMATION
1824 | FOF_SILENT | FOF_NOERRORUI);
1825 hres = SHCreateItemFromParsingName(reinterpret_cast<const wchar_t*>(sourcePath.utf16()),
1826 nullptr, IID_PPV_ARGS(&deleteItem));
1827 if (!deleteItem)
1828 return false;
1830 hres = pfo->DeleteItem(deleteItem, static_cast<IFileOperationProgressSink*>(sink));
1831 if (!SUCCEEDED(hres))
1832 return false;
1833 hres = pfo->PerformOperations();
1834 if (!SUCCEEDED(hres))
1835 return false;
1836 newLocation = QFileSystemEntry(sink->targetPath);
1837
1838 return true;
1839}
1840
1841//static
1843 QFile::Permissions permissions, QSystemError &error,
1845{
1846 Q_CHECK_FILE_NAME(entry, false);
1847
1848 Q_UNUSED(data);
1849 int mode = 0;
1850
1852 mode |= _S_IREAD;
1853 if (permissions
1855 mode |= _S_IWRITE;
1856 }
1857
1858 if (mode == 0) // not supported
1859 return false;
1860
1861 bool ret =
1862 ::_wchmod(reinterpret_cast<const wchar_t *>(entry.nativeFilePath().utf16()), mode) == 0;
1863 if (!ret)
1865 return ret;
1866}
1867
1868static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
1869{
1870 if (time->dwHighDateTime == 0 && time->dwLowDateTime == 0)
1871 return QDateTime();
1872
1873 SYSTEMTIME sTime;
1874 FileTimeToSystemTime(time, &sTime);
1875 return QDateTime(QDate(sTime.wYear, sTime.wMonth, sTime.wDay),
1876 QTime(sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds),
1878}
1879
1881{
1882 return fileTimeToQDateTime(&birthTime_);
1883}
1885{
1886 return fileTimeToQDateTime(&changeTime_);
1887}
1889{
1890 return fileTimeToQDateTime(&lastWriteTime_);
1891}
1893{
1894 return fileTimeToQDateTime(&lastAccessTime_);
1895}
1896
HRESULT STDMETHODCALLTYPE PreRenameItem(DWORD, IShellItem *, LPCWSTR) override
HRESULT STDMETHODCALLTYPE PostDeleteItem(DWORD, IShellItem *, HRESULT, IShellItem *psiNewlyCreated) override
HRESULT STDMETHODCALLTYPE PreCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR) override
HRESULT STDMETHODCALLTYPE PostNewItem(DWORD, IShellItem *, LPCWSTR, LPCWSTR, DWORD, HRESULT, IShellItem *) override
HRESULT STDMETHODCALLTYPE UpdateProgress(UINT, UINT) override
HRESULT STDMETHODCALLTYPE PostCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT, IShellItem *) override
HRESULT STDMETHODCALLTYPE ResetTimer() override
HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT) override
HRESULT STDMETHODCALLTYPE PostMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT, IShellItem *) override
HRESULT STDMETHODCALLTYPE PauseTimer() override
HRESULT STDMETHODCALLTYPE PreDeleteItem(DWORD dwFlags, IShellItem *) override
HRESULT STDMETHODCALLTYPE PostRenameItem(DWORD, IShellItem *, LPCWSTR, HRESULT, IShellItem *) override
HRESULT STDMETHODCALLTYPE StartOperations() override
HRESULT STDMETHODCALLTYPE ResumeTimer() override
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override
HRESULT STDMETHODCALLTYPE PreNewItem(DWORD, IShellItem *, LPCWSTR) override
ULONG STDMETHODCALLTYPE AddRef() override
HRESULT STDMETHODCALLTYPE PreMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR) override
ULONG STDMETHODCALLTYPE Release() override
\inmodule QtCore \reentrant
FileOwner
\value OwnerUser The user who owns the file.
FileTime
These are used by the fileTime() function.
T fetchAndSubRelaxed(T valueToAdd) noexcept
T fetchAndAddRelaxed(T valueToAdd) noexcept
T loadRelaxed() const noexcept
\inmodule QtCore
Definition qbytearray.h:57
qsizetype indexOf(char c, qsizetype from=0) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
QByteArray toHex(char separator='\0') const
Returns a hex encoded copy of the byte array.
constexpr char16_t unicode() const noexcept
Returns the numeric Unicode value of the QChar.
Definition qchar.h:458
QChar toUpper() const noexcept
Returns the uppercase equivalent if the character is lowercase or titlecase; otherwise returns the ch...
Definition qchar.h:449
\inmodule QtCore\reentrant
Definition qdatetime.h:257
QTime time() const
Returns the time part of the datetime.
QDate date() const
Returns the date part of the datetime.
\inmodule QtCore \reentrant
Definition qdatetime.h:27
static QString fromNativeSeparators(const QString &pathName)
Definition qdir.cpp:962
static QChar separator()
Returns the native directory separator: "/" under Unix and "\\" under Windows.
Definition qdir.h:206
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2395
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
static QString currentPath()
Returns the absolute path of the application's current directory.
Definition qdir.cpp:2051
static bool setFileTime(const QFileSystemEntry &entry, const QDateTime &newDate, QAbstractFileEngine::FileTime whatTime, QSystemError &error)
static QFileSystemEntry getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
static QFileSystemEntry canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
static QByteArray id(const QFileSystemEntry &entry)
static bool setCurrentPath(const QFileSystemEntry &entry)
static bool moveFileToTrash(const QFileSystemEntry &source, QFileSystemEntry &newLocation, QSystemError &error)
static QFileSystemEntry getRawLinkPath(const QFileSystemEntry &link, QFileSystemMetaData &data)
static bool copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
static bool renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
static bool fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, QFileSystemMetaData::MetaDataFlags what)
static bool createDirectory(const QFileSystemEntry &entry, bool createParents, std::optional< QFile::Permissions > permissions=std::nullopt)
static bool createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
static bool setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data=nullptr)
static bool renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
static QFileSystemEntry absoluteName(const QFileSystemEntry &entry)
static bool removeFile(const QFileSystemEntry &entry, QSystemError &error)
static bool removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
static QFileSystemEntry currentPath()
Q_AUTOTEST_EXPORT NativePath nativeFilePath() const
Q_AUTOTEST_EXPORT bool isEmpty() const
Q_AUTOTEST_EXPORT QString path() const
Q_AUTOTEST_EXPORT QString filePath() const
QDateTime modificationTime() const
QDateTime metadataChangeTime() const
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore \reentrant
QRegularExpressionMatch match(const QString &subject, qsizetype offset=0, MatchType matchType=NormalMatch, MatchOptions matchOptions=NoMatchOption) const
Attempts to match the regular expression against the given subject string, starting at the position o...
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString right(qsizetype n) const
Returns a substring that contains the n rightmost characters of the string.
Definition qstring.cpp:5180
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:279
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6737
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5788
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
QString toLower() const &
Definition qstring.h:368
qsizetype count(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4732
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1164
QString & append(QChar c)
Definition qstring.cpp:3227
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
qsizetype length() const
Returns the number of characters in this string.
Definition qstring.h:187
\inmodule QtCore \reentrant
Definition qdatetime.h:189
static void writeFlags(QTextStream &stream, const Provider &provider)
Definition ctf.cpp:269
QDate date
[1]
Token token
Definition keywords.cpp:444
Combined button and popup list for selecting options.
bool isNull(const T &t)
@ LocalTime
void * HANDLE
@ CaseInsensitive
constexpr Initialization Uninitialized
@ SkipEmptyParts
Definition qnamespace.h:127
qAreNtfsPermissionChecksEnabled()
[raii]
Q_CORE_EXPORT int qt_ntfs_permission_lookup
[0]
Definition ntfsp.cpp:10
static void * context
#define Q_BASIC_ATOMIC_INITIALIZER(a)
Q_CORE_EXPORT int qsnprintf(char *str, size_t n, const char *fmt,...)
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_DEPRECATED
#define QT_WARNING_PUSH
AudioChannelLayoutTag tag
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage return DBusPendingCall DBusPendingCall return DBusPendingCall return dbus_int32_t return DBusServer * server
DBusConnection const char DBusError * error
#define Q_CHECK_FILE_NAME(name, result)
static bool createDirectoryWithParents(const QByteArray &nativeName, mode_t mode, bool shouldMkdirFirst=true)
#define FSCTL_GET_REPARSE_POINT
QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED bool qEnableNtfsPermissionChecks() noexcept
static bool toFileTime(const QDateTime &date, FILETIME *fileTime)
static QString readSymLink(const QFileSystemEntry &link)
static bool tryFindFallback(const QFileSystemEntry &fname, QFileSystemMetaData &data)
bool qDisableNtfsPermissionChecks() noexcept
static bool rmDir(const QString &path)
static QByteArray fileId(HANDLE handle)
#define INVALID_FILE_ATTRIBUTES
static bool mkDir(const QString &path, SECURITY_ATTRIBUTES *securityAttributes, DWORD *lastError=nullptr)
static bool tryDriveUNCFallback(const QFileSystemEntry &fname, QFileSystemMetaData &data)
QByteArray fileIdWin8(HANDLE handle)
#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE
static QString readLink(const QFileSystemEntry &link)
static bool createDirectoryWithParents(const QString &nativeName, SECURITY_ATTRIBUTES *securityAttributes, bool shouldMkdirFirst=true)
static QBasicAtomicInt qt_ntfs_permission_lookup_v2
static QDateTime fileTimeToQDateTime(const FILETIME *time)
static bool uncShareExists(const QString &server)
bool qAreNtfsPermissionChecksEnabled() noexcept
[raii]
#define IO_REPARSE_TAG_SYMLINK
INT_PTR intptr_t
static bool getFindData(QString path, WIN32_FIND_DATA &findData)
struct _REPARSE_DATA_BUFFER REPARSE_DATA_BUFFER
struct _REPARSE_DATA_BUFFER * PREPARSE_DATA_BUFFER
#define PATH_MAX
#define Q_DECLARE_FLAGS(Flags, Enum)
Definition qflags.h:174
#define Q_DECLARE_OPERATORS_FOR_FLAGS(Flags)
Definition qflags.h:194
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
return ret
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLuint64 GLenum void * handle
GLenum mode
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLenum GLuint id
[7]
GLenum GLuint buffer
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum target
GLsizei bufsize
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLuint64 GLenum GLint fd
GLint ref
GLuint name
GLsizei GLsizei GLchar * source
GLuint res
GLuint entry
GLenum GLsizei len
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLsizei GLenum GLboolean sink
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
#define MAX_PATH
#define QT_IF_DEPRECATED_SINCE(major, minor, whenTrue, whenFalse)
#define tr(X)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define Q_UNUSED(x)
long HRESULT
QList< int > list
[14]
future resume()
Q_CHECK_PTR(a=new int[80])
QFileInfo info(fileName)
[8]
QObject::connect nullptr
struct _REPARSE_DATA_BUFFER::@22::@26 GenericReparseBuffer
struct _REPARSE_DATA_BUFFER::@22::@24 SymbolicLinkReparseBuffer
struct _REPARSE_DATA_BUFFER::@22::@25 MountPointReparseBuffer