Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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)
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
394{
395 return qt_ntfs_permission_lookup_v2.fetchAndAddRelaxed(1)
397 != 0;
398}
399
401{
402 return qt_ntfs_permission_lookup_v2.fetchAndSubRelaxed(1)
404 == 1;
405}
406
408{
409 return qt_ntfs_permission_lookup_v2.loadRelaxed()
411 ;
412}
414
446QNativeFilePermissions::QNativeFilePermissions(std::optional<QFileDevice::Permissions> perms,
447 bool isDir)
448{
449#if QT_CONFIG(fslibs)
450 if (!perms) {
451 ok = true;
452 return;
453 }
454
455 initGlobalSid();
456
457 const auto permissions = *perms;
458
459 PACL acl = reinterpret_cast<PACL>(aclStorage);
460
461 if (!InitializeAcl(acl, sizeof(aclStorage), ACL_REVISION))
462 return;
463
464 struct Masks
465 {
466 ACCESS_MASK denyMask, allowMask;
467 };
468
469 auto makeMasks = [isDir](NonSpecificPermissions allowPermissions,
470 NonSpecificPermissions denyPermissions, bool owner) {
471 constexpr ACCESS_MASK AllowRead = FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA;
472 constexpr ACCESS_MASK DenyRead = FILE_READ_DATA | FILE_READ_EA;
473
474 constexpr ACCESS_MASK AllowWrite =
475 FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA;
476 constexpr ACCESS_MASK DenyWrite = AllowWrite | FILE_DELETE_CHILD;
477 constexpr ACCESS_MASK DenyWriteOwner =
478 FILE_WRITE_DATA | FILE_WRITE_EA | FILE_APPEND_DATA | FILE_DELETE_CHILD;
479
480 constexpr ACCESS_MASK AllowExe = FILE_EXECUTE;
481 constexpr ACCESS_MASK DenyExe = AllowExe;
482
483 constexpr ACCESS_MASK StdRightsOther =
484 STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | SYNCHRONIZE;
485 constexpr ACCESS_MASK StdRightsOwner =
486 STANDARD_RIGHTS_ALL | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE;
487
488 ACCESS_MASK allow = owner ? StdRightsOwner : StdRightsOther;
489 ACCESS_MASK deny = 0;
490
491 if (denyPermissions & ReadPermission)
492 deny |= DenyRead;
493
494 if (denyPermissions & WritePermission)
495 deny |= owner ? DenyWriteOwner : DenyWrite;
496
497 if (denyPermissions & ExePermission)
498 deny |= DenyExe;
499
500 if (allowPermissions & ReadPermission)
501 allow |= AllowRead;
502
503 if (allowPermissions & WritePermission)
504 allow |= AllowWrite;
505
506 if (allowPermissions & ExePermission)
507 allow |= AllowExe;
508
509 // Give the owner "full access" if all the permissions are allowed
510 if (owner && allowPermissions == AllPermissions)
511 allow |= FILE_DELETE_CHILD;
512
513 if (isDir
514 && (allowPermissions & (WritePermission | ExePermission))
515 == (WritePermission | ExePermission)) {
516 allow |= FILE_DELETE_CHILD;
517 }
518
519 return Masks { deny, allow };
520 };
521
522 auto userPermissions = toNonSpecificPermissions(OwnerTag, permissions)
523 | toNonSpecificPermissions(UserTag, permissions);
524 auto groupPermissions = toNonSpecificPermissions(GroupTag, permissions);
525 auto otherPermissions = toNonSpecificPermissions(OtherTag, permissions);
526
527 auto userMasks = makeMasks(userPermissions,
528 ~userPermissions & (groupPermissions | otherPermissions), true);
529 auto groupMasks = makeMasks(groupPermissions, ~groupPermissions & otherPermissions, false);
530 auto otherMasks = makeMasks(otherPermissions, {}, false);
531
532 const DWORD aceFlags = isDir ? OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE : 0;
533 const bool reorderGroupDeny = (groupMasks.denyMask & userMasks.allowMask) == 0;
534
535 const auto addDenyAce = [acl, aceFlags](const Masks &masks, PSID pSID) {
536 if (masks.denyMask)
537 return AddAccessDeniedAceEx(acl, ACL_REVISION, aceFlags, masks.denyMask, pSID);
538 return TRUE;
539 };
540
541 const auto addAllowAce = [acl, aceFlags](const Masks &masks, PSID pSID) {
542 if (masks.allowMask)
543 return AddAccessAllowedAceEx(acl, ACL_REVISION, aceFlags, masks.allowMask, pSID);
544 return TRUE;
545 };
546
547 if (!addDenyAce(userMasks, currentUserSID))
548 return;
549
550 if (reorderGroupDeny) {
551 if (!addDenyAce(groupMasks, currentGroupSID))
552 return;
553 }
554
555 if (!addAllowAce(userMasks, currentUserSID))
556 return;
557
558 if (!reorderGroupDeny) {
559 if (!addDenyAce(groupMasks, currentGroupSID))
560 return;
561 }
562
563 if (!addAllowAce(groupMasks, currentGroupSID))
564 return;
565
566 if (!addAllowAce(otherMasks, worldSID))
567 return;
568
569 if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
570 return;
571
572 if (!SetSecurityDescriptorOwner(&sd, currentUserSID, FALSE))
573 return;
574
575 if (!SetSecurityDescriptorGroup(&sd, currentGroupSID, FALSE))
576 return;
577
578 if (!SetSecurityDescriptorDacl(&sd, TRUE, acl, FALSE))
579 return;
580
581 sa.nLength = sizeof(sa);
582 sa.lpSecurityDescriptor = &sd;
583 sa.bInheritHandle = FALSE;
584
585 isNull = false;
586#else
587 Q_UNUSED(perms);
588 Q_UNUSED(isDir);
589#endif // QT_CONFIG(fslibs)
590 ok = true;
591}
592
601SECURITY_ATTRIBUTES *QNativeFilePermissions::securityAttributes()
602{
603 Q_ASSERT(ok);
604 return isNull ? nullptr : &sa;
605}
606
607static inline bool toFileTime(const QDateTime &date, FILETIME *fileTime)
608{
609 SYSTEMTIME sTime;
610 if (date.timeSpec() == Qt::LocalTime) {
611 SYSTEMTIME lTime;
612 const QDate d = date.date();
613 const QTime t = date.time();
614
615 lTime.wYear = d.year();
616 lTime.wMonth = d.month();
617 lTime.wDay = d.day();
618 lTime.wHour = t.hour();
619 lTime.wMinute = t.minute();
620 lTime.wSecond = t.second();
621 lTime.wMilliseconds = t.msec();
622 lTime.wDayOfWeek = d.dayOfWeek() % 7;
623
624 if (!::TzSpecificLocalTimeToSystemTime(nullptr, &lTime, &sTime))
625 return false;
626 } else {
627 QDateTime utcDate = date.toUTC();
628 const QDate d = utcDate.date();
629 const QTime t = utcDate.time();
630
631 sTime.wYear = d.year();
632 sTime.wMonth = d.month();
633 sTime.wDay = d.day();
634 sTime.wHour = t.hour();
635 sTime.wMinute = t.minute();
636 sTime.wSecond = t.second();
637 sTime.wMilliseconds = t.msec();
638 sTime.wDayOfWeek = d.dayOfWeek() % 7;
639 }
640
641 return ::SystemTimeToFileTime(&sTime, fileTime);
642}
643
645{
647 HANDLE handle = CreateFile((wchar_t *)link.nativeFilePath().utf16(), FILE_READ_EA,
648 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
649 OPEN_EXISTING,
650 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
651 if (handle != INVALID_HANDLE_VALUE) {
654 Q_CHECK_PTR(rdb);
655 DWORD retsize = 0;
656 if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, rdb, bufsize, &retsize,
657 nullptr)) {
658 if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
659 int length = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
660 int offset = rdb->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
661 const wchar_t* PathBuffer = &rdb->MountPointReparseBuffer.PathBuffer[offset];
663 } else if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
664 int length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
665 int offset = rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
666 const wchar_t* PathBuffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[offset];
668 }
669 // remove "\\?\", "\??\" or "\\?\UNC\"
670 result = QFileSystemEntry::removeUncOrLongPathPrefix(result);
671 }
672 free(rdb);
673 CloseHandle(handle);
674
675#if QT_CONFIG(fslibs) && QT_CONFIG(regularexpression)
676 initGlobalSid();
677 QRegularExpression matchVolumeRe("^Volume\\{([a-z]|[0-9]|-)+\\}\\\\"_L1,
679 auto matchVolume = matchVolumeRe.match(result);
680 if (matchVolume.hasMatch()) {
681 Q_ASSERT(matchVolume.capturedStart() == 0);
682 DWORD len;
683 wchar_t buffer[MAX_PATH];
684 const QString volumeName = "\\\\?\\"_L1 + matchVolume.captured();
685 if (GetVolumePathNamesForVolumeName(reinterpret_cast<LPCWSTR>(volumeName.utf16()),
687 != 0) {
688 result.replace(0, matchVolume.capturedLength(), QString::fromWCharArray(buffer));
689 }
690 }
691#endif // QT_CONFIG(fslibs)
692 }
693 return result;
694}
695
697{
698#if QT_CONFIG(fslibs)
699 QString ret;
700
701 IShellLink *psl; // pointer to IShellLink i/f
702 WIN32_FIND_DATA wfd;
703 wchar_t szGotPath[MAX_PATH];
704
705 QComHelper comHelper;
706
707 // Get pointer to the IShellLink interface.
708 HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
709 (LPVOID *)&psl);
710
711 if (SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface.
712 IPersistFile *ppf;
713 hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
714 if (SUCCEEDED(hres)) {
715 hres = ppf->Load((LPOLESTR)link.nativeFilePath().utf16(), STGM_READ);
716 //The original path of the link is retrieved. If the file/folder
717 //was moved, the return value still have the old path.
718 if (SUCCEEDED(hres)) {
719 if (psl->GetPath(szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR)
720 ret = QString::fromWCharArray(szGotPath);
721 }
722 ppf->Release();
723 }
724 psl->Release();
725 }
726
727 return ret;
728#else
729 Q_UNUSED(link);
730 return QString();
731#endif // QT_CONFIG(fslibs)
732}
733
734static bool uncShareExists(const QString &server)
735{
736 // This code assumes the UNC path is always like \\?\UNC\server...
737 const auto parts = QStringView{server}.split(u'\\', Qt::SkipEmptyParts);
738 if (parts.count() >= 3) {
739 QStringList shares;
740 if (QFileSystemEngine::uncListSharesOnServer("\\\\"_L1 + parts.at(2), &shares))
741 return parts.count() < 4
742 || shares.contains(parts.at(3).toString(), Qt::CaseInsensitive);
743 }
744 return false;
745}
746
747static inline bool getFindData(QString path, WIN32_FIND_DATA &findData)
748{
749 // path should not end with a trailing slash
750 while (path.endsWith(u'\\'))
751 path.chop(1);
752
753 // can't handle drives
754 if (!path.endsWith(u':')) {
755 HANDLE hFind = ::FindFirstFile((wchar_t*)path.utf16(), &findData);
756 if (hFind != INVALID_HANDLE_VALUE) {
757 ::FindClose(hFind);
758 return true;
759 }
760 }
761
762 return false;
763}
764
765class FileOperationProgressSink : public IFileOperationProgressSink
766{
767public:
772
773 ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; }
774 ULONG STDMETHODCALLTYPE Release() override
775 {
776 if (--ref == 0) {
777 delete this;
778 return 0;
779 }
780 return ref;
781 }
782 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override
783 {
784 if (!ppvObject)
785 return E_POINTER;
786
787 *ppvObject = nullptr;
788
789 if (iid == __uuidof(IUnknown)) {
790 *ppvObject = static_cast<IUnknown*>(this);
791 } else if (iid == __uuidof(IFileOperationProgressSink)) {
792 *ppvObject = static_cast<IFileOperationProgressSink*>(this);
793 }
794
795 if (*ppvObject) {
796 AddRef();
797 return S_OK;
798 }
799
800 return E_NOINTERFACE;
801 }
802
803 HRESULT STDMETHODCALLTYPE StartOperations() override { return S_OK; }
804 HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT) override { return S_OK; }
805 HRESULT STDMETHODCALLTYPE PreRenameItem(DWORD, IShellItem *, LPCWSTR) override { return S_OK; }
806 HRESULT STDMETHODCALLTYPE PostRenameItem(DWORD, IShellItem *, LPCWSTR, HRESULT,
807 IShellItem *) override
808 { return S_OK; }
809 HRESULT STDMETHODCALLTYPE PreMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR) override
810 { return S_OK; }
811 HRESULT STDMETHODCALLTYPE PostMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT,
812 IShellItem *) override
813 { return S_OK; }
814 HRESULT STDMETHODCALLTYPE PreCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR) override
815 { return S_OK; }
816 HRESULT STDMETHODCALLTYPE PostCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT,
817 IShellItem *) override
818 { return S_OK; }
819 HRESULT STDMETHODCALLTYPE PreDeleteItem(DWORD dwFlags, IShellItem *) override
820 {
821 // stop the operation if the file will be deleted rather than trashed
822 return (dwFlags & TSF_DELETE_RECYCLE_IF_POSSIBLE) ? S_OK : E_FAIL;
823 }
824 HRESULT STDMETHODCALLTYPE PostDeleteItem(DWORD /* dwFlags */, IShellItem * /* psiItem */,
825 HRESULT hrDelete,
826 IShellItem *psiNewlyCreated) override
827 {
828 deleteResult = hrDelete;
829 if (psiNewlyCreated) {
830 wchar_t *pszName = nullptr;
831 psiNewlyCreated->GetDisplayName(SIGDN_FILESYSPATH, &pszName);
832 if (pszName) {
833 targetPath = QString::fromWCharArray(pszName);
834 CoTaskMemFree(pszName);
835 }
836 }
837 return S_OK;
838 }
839 HRESULT STDMETHODCALLTYPE PreNewItem(DWORD, IShellItem *, LPCWSTR) override { return S_OK; }
840 HRESULT STDMETHODCALLTYPE PostNewItem(DWORD, IShellItem *, LPCWSTR, LPCWSTR, DWORD, HRESULT,
841 IShellItem *) override
842 { return S_OK; }
843 HRESULT STDMETHODCALLTYPE UpdateProgress(UINT, UINT) override { return S_OK; }
844 HRESULT STDMETHODCALLTYPE ResetTimer() override { return S_OK; }
845 HRESULT STDMETHODCALLTYPE PauseTimer() override { return S_OK; }
846 HRESULT STDMETHODCALLTYPE ResumeTimer() override { return S_OK; }
847
849 HRESULT deleteResult = S_OK;
850private:
851 ULONG ref;
852};
853
854bool QFileSystemEngine::uncListSharesOnServer(const QString &server, QStringList *list)
855{
856 DWORD res = ERROR_NOT_SUPPORTED;
857 SHARE_INFO_1 *BufPtr, *p;
858 DWORD er = 0, tr = 0, resume = 0, i;
859 do {
860 res = NetShareEnum((wchar_t *)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr,
861 &resume);
862 if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) {
863 p = BufPtr;
864 for (i = 1; i <= er; ++i) {
865 if (list && p->shi1_type == 0)
866 list->append(QString::fromWCharArray(p->shi1_netname));
867 p++;
868 }
869 }
870 NetApiBufferFree(BufPtr);
871 } while (res == ERROR_MORE_DATA);
872 return res == ERROR_SUCCESS;
873}
874
875void QFileSystemEngine::clearWinStatData(QFileSystemMetaData &data)
876{
877 data.size_ = 0;
878 data.fileAttribute_ = 0;
879 data.birthTime_ = FILETIME();
880 data.changeTime_ = FILETIME();
881 data.lastAccessTime_ = FILETIME();
882 data.lastWriteTime_ = FILETIME();
883}
884
885//static
888{
889 QFileSystemEntry ret = getRawLinkPath(link, data);
890 if (!ret.isEmpty() && ret.isRelative()) {
891 QString target = absoluteName(link).path() + u'/' + ret.filePath();
893 }
894 return ret;
895}
896
897//static
900{
901 Q_CHECK_FILE_NAME(link, link);
902
903 if (data.missingFlags(QFileSystemMetaData::LinkType))
905
907 if (data.isLnkFile())
908 target = readLink(link);
909 else if (data.isLink())
910 target = readSymLink(link);
911 return QFileSystemEntry(target);
912}
913
914//static
915QFileSystemEntry QFileSystemEngine::junctionTarget(const QFileSystemEntry &link,
917{
918 Q_CHECK_FILE_NAME(link, link);
919
920 if (data.missingFlags(QFileSystemMetaData::JunctionType))
922
924 if (data.isJunction())
925 target = readSymLink(link);
927 if (!target.isEmpty() && ret.isRelative()) {
928 target.prepend(absoluteName(link).path() + u'/');
930 }
931 return ret;
932}
933
934//static
937{
939
942
943 if (data.exists())
944 return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath()));
945 else
946 return QFileSystemEntry();
947}
948
949//static
950QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path)
951{
953
954 // can be //server or //server/share
955 QString absPath;
956 QVarLengthArray<wchar_t, MAX_PATH> buf(qMax(MAX_PATH, path.size() + 1));
957 wchar_t *fileName = nullptr;
958 DWORD retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
959 if (retLen > (DWORD)buf.size()) {
960 buf.resize(retLen);
961 retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
962 }
963 if (retLen != 0)
964 absPath = QString::fromWCharArray(buf.data(), retLen);
965
966 // This is really ugly, but GetFullPathName strips off whitespace at the end.
967 // If you for instance write ". " in the lineedit of QFileDialog,
968 // (which is an invalid filename) this function will strip the space off and viola,
969 // the file is later reported as existing. Therefore, we re-add the whitespace that
970 // was at the end of path in order to keep the filename invalid.
971 if (!path.isEmpty() && path.at(path.size() - 1) == u' ')
972 absPath.append(u' ');
973 return absPath;
974}
975
976//static
978{
980
981 QString ret;
982
983 if (!entry.isRelative()) {
984 if (entry.isAbsolute() && entry.isClean())
985 ret = entry.filePath();
986 else
987 ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(entry.filePath()));
988 } else {
989 ret = QDir::cleanPath(QDir::currentPath() + u'/' + entry.filePath());
990 }
991
992 // The path should be absolute at this point.
993 // From the docs :
994 // Absolute paths begin with the directory separator "/"
995 // (optionally preceded by a drive specification under Windows).
996 if (ret.at(0) != u'/') {
997 Q_ASSERT(ret.length() >= 2);
998 Q_ASSERT(ret.at(0).isLetter());
999 Q_ASSERT(ret.at(1) == u':');
1000
1001 // Force uppercase drive letters.
1002 ret[0] = ret.at(0).toUpper();
1003 }
1005}
1006
1007// File ID for Windows up to version 7 and FAT32 drives
1009{
1010 BY_HANDLE_FILE_INFORMATION info;
1011 if (GetFileInformationByHandle(handle, &info)) {
1012 char buffer[sizeof "01234567:0123456701234567"];
1013 qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx",
1014 info.dwVolumeSerialNumber,
1015 info.nFileIndexHigh,
1016 info.nFileIndexLow);
1017 return buffer;
1018 }
1019 return QByteArray();
1020}
1021
1022// File ID for Windows starting from version 8.
1024{
1025#if !defined(QT_BOOTSTRAPPED)
1027 FILE_ID_INFO infoEx;
1028 if (GetFileInformationByHandleEx(
1029 handle,
1030 static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8
1031 &infoEx, sizeof(FILE_ID_INFO))) {
1032 result = QByteArray::number(infoEx.VolumeSerialNumber, 16);
1033 result += ':';
1034 // Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one.
1035 result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId),
1036 int(sizeof(infoEx.FileId)))
1037 .toHex();
1038 } else {
1039 // GetFileInformationByHandleEx() is observed to fail for FAT32, QTBUG-74759
1040 result = fileId(handle);
1041 }
1042 return result;
1043#else // !QT_BOOTSTRAPPED
1044 return fileId(handle);
1045#endif
1046}
1047
1048//static
1050{
1052
1054
1055 const HANDLE handle = CreateFile((wchar_t *)entry.nativeFilePath().utf16(), 0, FILE_SHARE_READ,
1056 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1057 if (handle != INVALID_HANDLE_VALUE) {
1058 result = id(handle);
1059 CloseHandle(handle);
1060 }
1061 return result;
1062}
1063
1064//static
1066{
1067 return fileIdWin8(HANDLE(fHandle));
1068}
1069
1070//static
1071bool QFileSystemEngine::setFileTime(HANDLE fHandle, const QDateTime &newDate,
1073{
1074 FILETIME fTime;
1075 FILETIME *pLastWrite = nullptr;
1076 FILETIME *pLastAccess = nullptr;
1077 FILETIME *pCreationTime = nullptr;
1078
1079 switch (time) {
1081 pLastWrite = &fTime;
1082 break;
1083
1085 pLastAccess = &fTime;
1086 break;
1087
1089 pCreationTime = &fTime;
1090 break;
1091
1092 default:
1093 error = QSystemError(ERROR_INVALID_PARAMETER, QSystemError::NativeError);
1094 return false;
1095 }
1096
1097 if (!toFileTime(newDate, &fTime))
1098 return false;
1099
1100 if (!::SetFileTime(fHandle, pCreationTime, pLastAccess, pLastWrite)) {
1101 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1102 return false;
1103 }
1104 return true;
1105}
1106
1107QString QFileSystemEngine::owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own)
1108{
1109 QString name;
1110#if QT_CONFIG(fslibs)
1112 initGlobalSid();
1113 {
1114 PSID pOwner = 0;
1115 PSECURITY_DESCRIPTOR pSD;
1116 if (GetNamedSecurityInfo(
1117 reinterpret_cast<const wchar_t *>(entry.nativeFilePath().utf16()),
1118 SE_FILE_OBJECT,
1119 own == QAbstractFileEngine::OwnerGroup ? GROUP_SECURITY_INFORMATION
1120 : OWNER_SECURITY_INFORMATION,
1121 own == QAbstractFileEngine::OwnerUser ? &pOwner : nullptr,
1122 own == QAbstractFileEngine::OwnerGroup ? &pOwner : nullptr, nullptr,
1123 nullptr, &pSD)
1124 == ERROR_SUCCESS) {
1125 DWORD lowner = 64;
1126 DWORD ldomain = 64;
1127 QVarLengthArray<wchar_t, 64> owner(lowner);
1128 QVarLengthArray<wchar_t, 64> domain(ldomain);
1129 SID_NAME_USE use = SidTypeUnknown;
1130 // First call, to determine size of the strings (with '\0').
1131 if (!LookupAccountSid(nullptr, pOwner, (LPWSTR)owner.data(), &lowner, domain.data(),
1132 &ldomain, &use)) {
1133 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
1134 if (lowner > (DWORD)owner.size())
1135 owner.resize(lowner);
1136 if (ldomain > (DWORD)domain.size())
1137 domain.resize(ldomain);
1138 // Second call, try on resized buf-s
1139 if (!LookupAccountSid(nullptr, pOwner, owner.data(), &lowner, domain.data(),
1140 &ldomain, &use)) {
1141 lowner = 0;
1142 }
1143 } else {
1144 lowner = 0;
1145 }
1146 }
1147 if (lowner != 0)
1148 name = QString::fromWCharArray(owner.data());
1149 LocalFree(pSD);
1150 }
1151 }
1152 }
1153#else
1154 Q_UNUSED(entry);
1155 Q_UNUSED(own);
1156#endif
1157 return name;
1158}
1159
1160//static
1161bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSystemMetaData &data,
1162 QFileSystemMetaData::MetaDataFlags what)
1163{
1164#if QT_CONFIG(fslibs)
1166 initGlobalSid();
1167
1168 QString fname = entry.nativeFilePath();
1169 PSID pOwner;
1170 PSID pGroup;
1171 PACL pDacl;
1172 PSECURITY_DESCRIPTOR pSD;
1173
1174 // pDacl is unused directly by the code below, but it is still needed here because
1175 // access checks below return incorrect results unless DACL_SECURITY_INFORMATION is
1176 // passed to this call.
1177 DWORD res = GetNamedSecurityInfo(
1178 reinterpret_cast<const wchar_t *>(fname.utf16()), SE_FILE_OBJECT,
1179 OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
1180 &pOwner, &pGroup, &pDacl, nullptr, &pSD);
1181 if (res == ERROR_SUCCESS) {
1182 QAuthzResourceManager rm;
1183
1184 auto addPermissions = [&data](ACCESS_MASK accessMask,
1187 QFileSystemMetaData::MetaDataFlag executeFlags) {
1188 // Check for generic permissions and file-specific bits that most closely
1189 // represent POSIX permissions.
1190
1191 // Constants like FILE_GENERIC_{READ,WRITE,EXECUTE} cannot be used
1192 // here because they contain permission bits shared between all of them.
1193 if (accessMask & (GENERIC_READ | FILE_READ_DATA))
1194 data.entryFlags |= readFlags;
1195 if (accessMask & (GENERIC_WRITE | FILE_WRITE_DATA))
1196 data.entryFlags |= writeFlags;
1197 if (accessMask & (GENERIC_EXECUTE | FILE_EXECUTE))
1198 data.entryFlags |= executeFlags;
1199 };
1200
1201 if (what & QFileSystemMetaData::UserPermissions && currentUserImpersonatedToken) {
1203 QAuthzClientContext context(rm, currentUserImpersonatedToken,
1204 QAuthzClientContext::TokenTag {});
1205 addPermissions(context.accessMask(pSD),
1209 }
1210
1213 QAuthzClientContext context(rm, pOwner);
1214 addPermissions(context.accessMask(pSD),
1218 }
1219
1222 QAuthzClientContext context(rm, pGroup);
1223 addPermissions(context.accessMask(pSD),
1227 }
1228
1231 QAuthzClientContext context(rm, worldSID);
1232 addPermissions(context.accessMask(pSD),
1236 }
1237
1238 LocalFree(pSD);
1239 }
1240 } else
1241#endif
1242 {
1243 //### what to do with permissions if we don't use NTFS
1244 // for now just add all permissions and what about exe missions ??
1245 // also qt_ntfs_permission_lookup is now not set by default ... should it ?
1249
1250 if (!(data.fileAttribute_ & FILE_ATTRIBUTE_READONLY)) {
1254 }
1255
1256 QString fname = entry.filePath();
1257 QString ext = fname.right(4).toLower();
1258 if (data.isDirectory() || ext == ".exe"_L1 || ext == ".com"_L1
1259 || ext == ".bat"_L1 || ext == ".pif"_L1 || ext == ".cmd"_L1) {
1264 }
1268 // calculate user permissions
1270 if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), R_OK) == 0)
1273 }
1275 if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), W_OK) == 0)
1278 }
1279 }
1280
1281 return data.hasFlags(what);
1282}
1283
1285{
1286 bool entryExists = false;
1287 DWORD fileAttrib = 0;
1288 if (fname.isDriveRoot()) {
1289 // a valid drive ??
1290 const UINT oldErrorMode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1291 DWORD drivesBitmask = ::GetLogicalDrives();
1292 ::SetErrorMode(oldErrorMode);
1293 int drivebit =
1294 1 << (fname.filePath().at(0).toUpper().unicode() - u'A');
1295 if (drivesBitmask & drivebit) {
1296 fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM;
1297 entryExists = true;
1298 }
1299 } else {
1300 const QString &path = fname.nativeFilePath();
1301 bool is_dir = false;
1302 if (path.startsWith("\\\\?\\UNC"_L1)) {
1303 // UNC - stat doesn't work for all cases (Windows bug)
1304 int s = path.indexOf(path.at(0),7);
1305 if (s > 0) {
1306 // "\\?\UNC\server\..."
1307 s = path.indexOf(path.at(0),s+1);
1308 if (s > 0) {
1309 // "\\?\UNC\server\share\..."
1310 if (s == path.size() - 1) {
1311 // "\\?\UNC\server\share\"
1312 is_dir = true;
1313 } else {
1314 // "\\?\UNC\server\share\notfound"
1315 }
1316 } else {
1317 // "\\?\UNC\server\share"
1318 is_dir = true;
1319 }
1320 } else {
1321 // "\\?\UNC\server"
1322 is_dir = true;
1323 }
1324 }
1325 if (is_dir && uncShareExists(path)) {
1326 // looks like a UNC dir, is a dir.
1327 fileAttrib = FILE_ATTRIBUTE_DIRECTORY;
1328 entryExists = true;
1329 }
1330 }
1331 if (entryExists)
1332 data.fillFromFileAttribute(fileAttrib);
1333 return entryExists;
1334}
1335
1337{
1338 bool filledData = false;
1339 // This assumes the last call to a Windows API failed.
1340 int errorCode = GetLastError();
1341 if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
1342 WIN32_FIND_DATA findData;
1343 if (getFindData(fname.nativeFilePath(), findData)
1344 && findData.dwFileAttributes != INVALID_FILE_ATTRIBUTES) {
1345 data.fillFromFindData(findData, true, fname.isDriveRoot());
1346 filledData = true;
1347 }
1348 }
1349 return filledData;
1350}
1351
1352//static
1354 QFileSystemMetaData::MetaDataFlags what)
1355{
1356 auto fHandle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
1357 if (fHandle != INVALID_HANDLE_VALUE) {
1358 return fillMetaData(fHandle, data, what);
1359 }
1360 return false;
1361}
1362
1363//static
1365 QFileSystemMetaData::MetaDataFlags what)
1366{
1367 data.entryFlags &= ~what;
1368 clearWinStatData(data);
1369 BY_HANDLE_FILE_INFORMATION fileInfo;
1370 UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1371 if (GetFileInformationByHandle(fHandle , &fileInfo)) {
1372 data.fillFromFindInfo(fileInfo);
1373 }
1374 SetErrorMode(oldmode);
1375 return data.hasFlags(what);
1376}
1377
1378//static
1380 QFileSystemMetaData::MetaDataFlags what)
1381{
1382 Q_CHECK_FILE_NAME(entry, false);
1383 what |= QFileSystemMetaData::WinLnkType | QFileSystemMetaData::WinStatFlags;
1384 data.entryFlags &= ~what;
1385
1386 QFileSystemEntry fname;
1387 data.knownFlagsMask |= QFileSystemMetaData::WinLnkType;
1388 // Check for ".lnk": Directories named ".lnk" should be skipped, corrupted
1389 // link files should still be detected as links.
1390 const QString origFilePath = entry.filePath();
1391 if (origFilePath.endsWith(".lnk"_L1) && !isDirPath(origFilePath, nullptr)) {
1394 } else {
1395 fname = entry;
1396 }
1397
1398 if (fname.isEmpty()) {
1399 data.knownFlagsMask |= what;
1400 clearWinStatData(data);
1401 return false;
1402 }
1403
1404 if (what & QFileSystemMetaData::WinStatFlags) {
1405 UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1406 clearWinStatData(data);
1407 WIN32_FIND_DATA findData;
1408 // The memory structure for WIN32_FIND_DATA is same as WIN32_FILE_ATTRIBUTE_DATA
1409 // for all members used by fillFindData().
1410 bool ok = ::GetFileAttributesEx(
1411 reinterpret_cast<const wchar_t *>(fname.nativeFilePath().utf16()),
1412 GetFileExInfoStandard, reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA *>(&findData));
1413 if (ok) {
1414 data.fillFromFindData(findData, false, fname.isDriveRoot());
1415 } else {
1416 const DWORD lastError = GetLastError();
1417 // disconnected drive
1418 if (lastError == ERROR_LOGON_FAILURE || lastError == ERROR_BAD_NETPATH
1419 || (!tryFindFallback(fname, data) && !tryDriveUNCFallback(fname, data))) {
1420 data.clearFlags();
1421 SetErrorMode(oldmode);
1422 return false;
1423 }
1424 }
1425 SetErrorMode(oldmode);
1426 }
1427
1429 fillPermissions(fname, data, what);
1430 if (what & QFileSystemMetaData::LinkType) {
1431 data.knownFlagsMask |= QFileSystemMetaData::LinkType;
1432 if (data.fileAttribute_ & FILE_ATTRIBUTE_REPARSE_POINT) {
1433 WIN32_FIND_DATA findData;
1434 if (getFindData(fname.nativeFilePath(), findData))
1435 data.fillFromFindData(findData, true);
1436 }
1437 }
1438 data.knownFlagsMask |= what;
1439 return data.hasFlags(what);
1440}
1441
1442static inline bool mkDir(const QString &path, SECURITY_ATTRIBUTES *securityAttributes,
1443 DWORD *lastError = nullptr)
1444{
1445 if (lastError)
1446 *lastError = 0;
1447 const QString longPath = QFSFileEnginePrivate::longFileName(path);
1448 const bool result = ::CreateDirectory((wchar_t *)longPath.utf16(), securityAttributes);
1449 // Capture lastError before any QString is freed since custom allocators might change it.
1450 if (lastError)
1451 *lastError = GetLastError();
1452 return result;
1453}
1454
1455static inline bool rmDir(const QString &path)
1456{
1457 return ::RemoveDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16());
1458}
1459
1460//static
1461bool QFileSystemEngine::isDirPath(const QString &dirPath, bool *existed)
1462{
1463 QString path = dirPath;
1464 if (path.length() == 2 && path.at(1) == u':')
1465 path += u'\\';
1466
1467 const QString longPath = QFSFileEnginePrivate::longFileName(path);
1468 DWORD fileAttrib = ::GetFileAttributes(reinterpret_cast<const wchar_t*>(longPath.utf16()));
1469 if (fileAttrib == INVALID_FILE_ATTRIBUTES) {
1470 int errorCode = GetLastError();
1471 if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
1472 WIN32_FIND_DATA findData;
1473 if (getFindData(longPath, findData))
1474 fileAttrib = findData.dwFileAttributes;
1475 }
1476 }
1477
1478 if (existed)
1479 *existed = fileAttrib != INVALID_FILE_ATTRIBUTES;
1480
1481 if (fileAttrib == INVALID_FILE_ATTRIBUTES)
1482 return false;
1483
1484 return fileAttrib & FILE_ATTRIBUTE_DIRECTORY;
1485}
1486
1487// NOTE: if \a shouldMkdirFirst is false, we assume the caller did try to mkdir
1488// before calling this function.
1489static bool createDirectoryWithParents(const QString &nativeName,
1490 SECURITY_ATTRIBUTES *securityAttributes,
1491 bool shouldMkdirFirst = true)
1492{
1493 const auto isUNCRoot = [](const QString &nativeName) {
1494 return nativeName.startsWith("\\\\"_L1)
1495 && nativeName.count(QDir::separator()) <= 3;
1496 };
1497 const auto isDriveName = [](const QString &nativeName) {
1498 return nativeName.size() == 2 && nativeName.at(1) == u':';
1499 };
1500 const auto isDir = [](const QString &nativeName) {
1501 bool exists = false;
1502 return QFileSystemEngine::isDirPath(nativeName, &exists) && exists;
1503 };
1504 // Do not try to mkdir a UNC root path or a drive letter.
1505 if (isUNCRoot(nativeName) || isDriveName(nativeName))
1506 return false;
1507
1508 if (shouldMkdirFirst) {
1509 if (mkDir(nativeName, securityAttributes))
1510 return true;
1511 }
1512
1513 const int backSlash = nativeName.lastIndexOf(QDir::separator());
1514 if (backSlash < 1)
1515 return false;
1516
1517 const QString parentNativeName = nativeName.left(backSlash);
1518 if (!createDirectoryWithParents(parentNativeName, securityAttributes))
1519 return false;
1520
1521 // try again
1522 if (mkDir(nativeName, securityAttributes))
1523 return true;
1524 return isDir(nativeName);
1525}
1526
1527//static
1528bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents,
1529 std::optional<QFile::Permissions> permissions)
1530{
1531 QString dirName = entry.filePath();
1532 Q_CHECK_FILE_NAME(dirName, false);
1533
1534 dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
1535
1536 QNativeFilePermissions nativePermissions(permissions, true);
1537 if (!nativePermissions.isOk())
1538 return false;
1539
1540 auto securityAttributes = nativePermissions.securityAttributes();
1541
1542 // try to mkdir this directory
1543 DWORD lastError;
1544 if (mkDir(dirName, securityAttributes, &lastError))
1545 return true;
1546 // mkpath should return true, if the directory already exists, mkdir false.
1547 if (!createParents)
1548 return false;
1549 if (lastError == ERROR_ALREADY_EXISTS || lastError == ERROR_ACCESS_DENIED)
1550 return isDirPath(dirName, nullptr);
1551
1552 return createDirectoryWithParents(dirName, securityAttributes, false);
1553}
1554
1555//static
1556bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
1557{
1558 QString dirName = entry.filePath();
1559 Q_CHECK_FILE_NAME(dirName, false);
1560
1561 if (removeEmptyParents) {
1562 dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
1563 for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
1564 const auto chunkRef = QStringView{dirName}.left(slash);
1565 if (chunkRef.length() == 2 && chunkRef.at(0).isLetter()
1566 && chunkRef.at(1) == u':') {
1567 break;
1568 }
1569 const QString chunk = chunkRef.toString();
1570 if (!isDirPath(chunk, nullptr))
1571 return false;
1572 if (!rmDir(chunk))
1573 return oldslash != 0;
1574 slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
1575 }
1576 return true;
1577 }
1578 return rmDir(entry.filePath());
1579}
1580
1581//static
1583{
1584 QString ret = QString::fromLatin1(qgetenv("SystemDrive"));
1585 if (ret.isEmpty())
1586 ret = "c:"_L1;
1587 ret.append(u'/');
1588 return ret;
1589}
1590
1591//static
1593{
1594 QString ret;
1595#if QT_CONFIG(fslibs)
1596 initGlobalSid();
1597 {
1598 HANDLE hnd = ::GetCurrentProcess();
1599 HANDLE token = nullptr;
1600 BOOL ok = ::OpenProcessToken(hnd, TOKEN_QUERY, &token);
1601 if (ok) {
1602 DWORD dwBufferSize = 0;
1603 // First call, to determine size of the strings (with '\0').
1604 ok = GetUserProfileDirectory(token, nullptr, &dwBufferSize);
1605 if (!ok && dwBufferSize != 0) { // We got the required buffer size
1606 wchar_t *userDirectory = new wchar_t[dwBufferSize];
1607 // Second call, now we can fill the allocated buffer.
1608 ok = GetUserProfileDirectory(token, userDirectory, &dwBufferSize);
1609 if (ok)
1610 ret = QString::fromWCharArray(userDirectory);
1611 delete [] userDirectory;
1612 }
1613 ::CloseHandle(token);
1614 }
1615 }
1616#endif
1617 if (ret.isEmpty() || !QFile::exists(ret)) {
1618 ret = QString::fromLocal8Bit(qgetenv("USERPROFILE"));
1619 if (ret.isEmpty() || !QFile::exists(ret)) {
1620 ret = QString::fromLocal8Bit(qgetenv("HOMEDRIVE"))
1621 + QString::fromLocal8Bit(qgetenv("HOMEPATH"));
1622 if (ret.isEmpty() || !QFile::exists(ret)) {
1624 if (ret.isEmpty() || !QFile::exists(ret))
1625 ret = rootPath();
1626 }
1627 }
1628 }
1630}
1631
1633{
1634 QString ret;
1635 wchar_t tempPath[MAX_PATH];
1636 const DWORD len = GetTempPath(MAX_PATH, tempPath);
1637 if (len) { // GetTempPath() can return short names, expand.
1638 wchar_t longTempPath[MAX_PATH];
1639 const DWORD longLen = GetLongPathName(tempPath, longTempPath, MAX_PATH);
1640 ret = longLen && longLen < MAX_PATH ?
1641 QString::fromWCharArray(longTempPath, longLen) :
1642 QString::fromWCharArray(tempPath, len);
1643 }
1644 if (!ret.isEmpty()) {
1645 while (ret.endsWith(u'\\'))
1646 ret.chop(1);
1648 }
1649 if (ret.isEmpty()) {
1650 ret = "C:/tmp"_L1;
1651 } else if (ret.length() >= 2 && ret[1] == u':')
1652 ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
1653 return ret;
1654}
1655
1657{
1659 fillMetaData(entry, meta,
1661 if (!(meta.exists() && meta.isDirectory()))
1662 return false;
1663
1664 // TODO: this should really be using nativeFilePath(), but that returns a path
1665 // in long format \\?\c:\foo which causes many problems later on when it's
1666 // returned through currentPath()
1667 return ::SetCurrentDirectory(reinterpret_cast<const wchar_t *>(
1668 QDir::toNativeSeparators(entry.filePath()).utf16()))
1669 != 0;
1670}
1671
1673{
1675 DWORD size = GetCurrentDirectoryW(PATH_MAX, reinterpret_cast<wchar_t *>(ret.data()));
1676 if (size > PATH_MAX) {
1677 // try again after enlarging the buffer
1678 ret.resize(size);
1679 size = GetCurrentDirectoryW(size, reinterpret_cast<wchar_t *>(ret.data()));
1680
1681 // note: the current directory may have changed underneath us; if the
1682 // new one is even bigger, we may return a truncated string!
1683 }
1684 if (size >= 2 && ret.at(1) == u':')
1685 ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
1686 ret.resize(size);
1688}
1689
1690//static
1693{
1694 bool ret = false;
1695 QComHelper comHelper;
1696 IShellLink *psl = nullptr;
1697 HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
1698 reinterpret_cast<void **>(&psl));
1699
1700 if (SUCCEEDED(hres)) {
1701 const auto name = QDir::toNativeSeparators(source.filePath());
1702 const auto pathName = QDir::toNativeSeparators(source.path());
1703 if (SUCCEEDED(psl->SetPath(reinterpret_cast<const wchar_t *>(name.utf16())))
1704 && SUCCEEDED(psl->SetWorkingDirectory(
1705 reinterpret_cast<const wchar_t *>(pathName.utf16())))) {
1706 IPersistFile *ppf = nullptr;
1707 if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void **>(&ppf)))) {
1708 ret = SUCCEEDED(ppf->Save(
1709 reinterpret_cast<const wchar_t *>(target.filePath().utf16()), TRUE));
1710 ppf->Release();
1711 }
1712 }
1713 psl->Release();
1714 }
1715
1716 if (!ret)
1717 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1718
1719 return ret;
1720}
1721
1722//static
1725{
1726 bool ret = ::CopyFile((wchar_t*)source.nativeFilePath().utf16(),
1727 (wchar_t*)target.nativeFilePath().utf16(), true) != 0;
1728 if (!ret)
1729 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1730 return ret;
1731}
1732
1733//static
1736{
1737 Q_CHECK_FILE_NAME(source, false);
1738 Q_CHECK_FILE_NAME(target, false);
1739
1740 bool ret = ::MoveFile((wchar_t*)source.nativeFilePath().utf16(),
1741 (wchar_t*)target.nativeFilePath().utf16()) != 0;
1742 if (!ret)
1743 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1744 return ret;
1745}
1746
1747//static
1750{
1751 Q_CHECK_FILE_NAME(source, false);
1752 Q_CHECK_FILE_NAME(target, false);
1753
1754 bool ret = ::MoveFileEx(reinterpret_cast<const wchar_t *>(source.nativeFilePath().utf16()),
1755 reinterpret_cast<const wchar_t *>(target.nativeFilePath().utf16()),
1756 MOVEFILE_REPLACE_EXISTING) != 0;
1757 if (!ret)
1758 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1759 return ret;
1760}
1761
1762//static
1764{
1765 Q_CHECK_FILE_NAME(entry, false);
1766
1767 bool ret = ::DeleteFile((wchar_t*)entry.nativeFilePath().utf16()) != 0;
1768 if (!ret)
1769 error = QSystemError(::GetLastError(), QSystemError::NativeError);
1770 return ret;
1771}
1772
1773/*
1774 If possible, we use the IFileOperation implementation, which allows us to determine
1775 the location of the object in the trash.
1776 If not (likely on mingw), we fall back to the old API, which won't allow us to know
1777 that.
1778*/
1779//static
1781 QFileSystemEntry &newLocation, QSystemError &error)
1782{
1783 // we need the "display name" of the file, so can't use nativeAbsoluteFilePath
1784 const QString sourcePath = QDir::toNativeSeparators(absoluteName(source).filePath());
1785
1786 QComHelper comHelper;
1787
1788 IFileOperation *pfo = nullptr;
1789 IShellItem *deleteItem = nullptr;
1791 HRESULT hres = E_FAIL;
1792
1793 auto coUninitialize = qScopeGuard([&](){
1794 if (sink)
1795 sink->Release();
1796 if (deleteItem)
1797 deleteItem->Release();
1798 if (pfo)
1799 pfo->Release();
1800 if (!SUCCEEDED(hres))
1802 });
1803
1804 hres = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pfo));
1805 if (!pfo)
1806 return false;
1807 pfo->SetOperationFlags(FOF_ALLOWUNDO | FOFX_RECYCLEONDELETE | FOF_NOCONFIRMATION
1808 | FOF_SILENT | FOF_NOERRORUI);
1809 hres = SHCreateItemFromParsingName(reinterpret_cast<const wchar_t*>(sourcePath.utf16()),
1810 nullptr, IID_PPV_ARGS(&deleteItem));
1811 if (!deleteItem)
1812 return false;
1814 hres = pfo->DeleteItem(deleteItem, static_cast<IFileOperationProgressSink*>(sink));
1815 if (!SUCCEEDED(hres))
1816 return false;
1817 hres = pfo->PerformOperations();
1818 if (!SUCCEEDED(hres))
1819 return false;
1820
1821 if (!SUCCEEDED(sink->deleteResult)) {
1823 return false;
1824 }
1825 newLocation = QFileSystemEntry(sink->targetPath);
1826 return true;
1827}
1828
1829//static
1831 QFile::Permissions permissions, QSystemError &error,
1833{
1834 Q_CHECK_FILE_NAME(entry, false);
1835
1836 Q_UNUSED(data);
1837 int mode = 0;
1838
1840 mode |= _S_IREAD;
1841 if (permissions
1843 mode |= _S_IWRITE;
1844 }
1845
1846 if (mode == 0) // not supported
1847 return false;
1848
1849 bool ret =
1850 ::_wchmod(reinterpret_cast<const wchar_t *>(entry.nativeFilePath().utf16()), mode) == 0;
1851 if (!ret)
1853 return ret;
1854}
1855
1856static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
1857{
1858 if (time->dwHighDateTime == 0 && time->dwLowDateTime == 0)
1859 return QDateTime();
1860
1861 SYSTEMTIME sTime;
1862 FileTimeToSystemTime(time, &sTime);
1863 return QDateTime(QDate(sTime.wYear, sTime.wMonth, sTime.wDay),
1864 QTime(sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds),
1866}
1867
1869{
1870 return fileTimeToQDateTime(&birthTime_);
1871}
1873{
1874 return fileTimeToQDateTime(&changeTime_);
1875}
1877{
1878 return fileTimeToQDateTime(&lastWriteTime_);
1879}
1881{
1882 return fileTimeToQDateTime(&lastAccessTime_);
1883}
1884
HRESULT STDMETHODCALLTYPE PreRenameItem(DWORD, IShellItem *, LPCWSTR) 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 PostDeleteItem(DWORD, IShellItem *, HRESULT hrDelete, IShellItem *psiNewlyCreated) 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.
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
\inmodule QtCore\reentrant
Definition qdatetime.h:283
\inmodule QtCore \reentrant
Definition qdatetime.h:29
int year() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
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:209
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2398
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:2054
@ FileModificationTime
Definition qfiledevice.h:61
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 setFileTime(const QFileSystemEntry &entry, const QDateTime &newDate, QFile::FileTime whatTime, QSystemError &error)
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
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:458
\inmodule QtCore \reentrant
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6340
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6995
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5949
QString right(qsizetype n) const &
Definition qstring.h:375
QString toLower() const &
Definition qstring.h:435
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
qsizetype length() const noexcept
Returns the number of characters in this string.
Definition qstring.h:191
\inmodule QtCore \reentrant
Definition qdatetime.h:215
int hour() const
Returns the hour part (0 to 23) of the time.
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:128
qAreNtfsPermissionChecksEnabled()
[raii]
Q_CORE_EXPORT int qt_ntfs_permission_lookup
[0]
Definition ntfsp.cpp:10
qEnableNtfsPermissionChecks()
qDisableNtfsPermissionChecks()
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
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define Q_CHECK_FILE_NAME(name, result)
static bool createDirectoryWithParents(const QByteArray &nativeName, mode_t mode, bool shouldMkdirFirst=true)
#define FSCTL_GET_REPARSE_POINT
static bool toFileTime(const QDateTime &date, FILETIME *fileTime)
static QString readSymLink(const QFileSystemEntry &link)
static bool tryFindFallback(const QFileSystemEntry &fname, QFileSystemMetaData &data)
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 QBasicAtomicInt qt_ntfs_permission_lookup_v2
static QDateTime fileTimeToQDateTime(const FILETIME *time)
static bool uncShareExists(const QString &server)
#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]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum target
GLsizei bufsize
GLenum GLuint GLintptr offset
GLuint64 GLenum GLint fd
GLint ref
GLuint name
GLsizei GLsizei GLchar * source
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
GLuint entry
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLsizei GLenum GLboolean sink
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLsizei len
#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])
QObject::connect nullptr
QHostInfo info
[0]
struct _REPARSE_DATA_BUFFER::@19::@21 SymbolicLinkReparseBuffer
struct _REPARSE_DATA_BUFFER::@19::@22 MountPointReparseBuffer
struct _REPARSE_DATA_BUFFER::@19::@23 GenericReparseBuffer