1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qfilesystemengine_p.h"
41 #include "qoperatingsystemversion.h"
42 #include "qplatformdefs.h"
43 #include "qsysinfo.h"
44 #include "qscopeguard.h"
45 #include "private/qabstractfileengine_p.h"
46 #include "private/qfsfileengine_p.h"
47 #include <private/qsystemlibrary_p.h>
48 #include <qdebug.h>
49 
50 #include "qfile.h"
51 #include "qdir.h"
52 #include "qvarlengtharray.h"
53 #include "qdatetime.h"
54 #include "qt_windows.h"
55 #include "qvector.h"
56 
57 #include <sys/types.h>
58 #include <direct.h>
59 #include <winioctl.h>
60 #include <objbase.h>
61 #ifndef Q_OS_WINRT
62 #  include <shlobj.h>
63 #  include <shobjidl.h>
64 #  include <shellapi.h>
65 #  include <lm.h>
66 #  include <accctrl.h>
67 #endif
68 #include <initguid.h>
69 #include <ctype.h>
70 #include <limits.h>
71 #ifndef Q_OS_WINRT
72 #  define SECURITY_WIN32
73 #  include <security.h>
74 #else // !Q_OS_WINRT
75 #  include "qstandardpaths.h"
76 #  include "qthreadstorage.h"
77 #  include <wrl.h>
78 #  include <windows.foundation.h>
79 #  include <windows.storage.h>
80 #  include <Windows.ApplicationModel.h>
81 
82 using namespace Microsoft::WRL;
83 using namespace Microsoft::WRL::Wrappers;
84 using namespace ABI::Windows::Foundation;
85 using namespace ABI::Windows::Storage;
86 using namespace ABI::Windows::ApplicationModel;
87 #endif // Q_OS_WINRT
88 
89 #ifndef SPI_GETPLATFORMTYPE
90 #define SPI_GETPLATFORMTYPE 257
91 #endif
92 
93 #ifndef PATH_MAX
94 #define PATH_MAX FILENAME_MAX
95 #endif
96 
97 #ifndef _INTPTR_T_DEFINED
98 #ifdef  _WIN64
99 typedef __int64             intptr_t;
100 #else
101 #ifdef _W64
102 typedef _W64 int            intptr_t;
103 #else
104 typedef INT_PTR intptr_t;
105 #endif
106 #endif
107 #define _INTPTR_T_DEFINED
108 #endif
109 
110 #ifndef INVALID_FILE_ATTRIBUTES
111 #  define INVALID_FILE_ATTRIBUTES (DWORD (-1))
112 #endif
113 
114 #if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
115 typedef struct _REPARSE_DATA_BUFFER {
116     ULONG  ReparseTag;
117     USHORT ReparseDataLength;
118     USHORT Reserved;
119     union {
120         struct {
121             USHORT SubstituteNameOffset;
122             USHORT SubstituteNameLength;
123             USHORT PrintNameOffset;
124             USHORT PrintNameLength;
125             ULONG  Flags;
126             WCHAR  PathBuffer[1];
127         } SymbolicLinkReparseBuffer;
128         struct {
129             USHORT SubstituteNameOffset;
130             USHORT SubstituteNameLength;
131             USHORT PrintNameOffset;
132             USHORT PrintNameLength;
133             WCHAR  PathBuffer[1];
134         } MountPointReparseBuffer;
135         struct {
136             UCHAR  DataBuffer[1];
137         } GenericReparseBuffer;
138     };
139 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
140 #  define REPARSE_DATA_BUFFER_HEADER_SIZE  FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
141 #endif // !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
142 
143 #ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
144 #  define MAXIMUM_REPARSE_DATA_BUFFER_SIZE 16384
145 #endif
146 #ifndef IO_REPARSE_TAG_SYMLINK
147 #  define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
148 #endif
149 #ifndef FSCTL_GET_REPARSE_POINT
150 #  define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
151 #endif
152 
153 #if defined(Q_OS_WINRT) || defined(QT_BOOTSTRAPPED)
154 #  define QT_FEATURE_fslibs -1
155 #else
156 #  define QT_FEATURE_fslibs 1
157 #endif // Q_OS_WINRT
158 
159 #if QT_CONFIG(fslibs)
160 #include <aclapi.h>
161 #include <userenv.h>
162 static TRUSTEE_W currentUserTrusteeW;
163 static TRUSTEE_W worldTrusteeW;
164 static PSID currentUserSID = 0;
165 static PSID worldSID = 0;
166 static HANDLE currentUserImpersonatedToken = nullptr;
167 
168 QT_BEGIN_NAMESPACE
169 
170 namespace {
171 struct GlobalSid
172 {
173     GlobalSid();
174     ~GlobalSid();
175 };
176 
~GlobalSid()177 GlobalSid::~GlobalSid()
178 {
179     free(currentUserSID);
180     currentUserSID = 0;
181 
182     // worldSID was allocated with AllocateAndInitializeSid so it needs to be freed with FreeSid
183     if (worldSID) {
184         ::FreeSid(worldSID);
185         worldSID = 0;
186     }
187 
188     if (currentUserImpersonatedToken) {
189         ::CloseHandle(currentUserImpersonatedToken);
190         currentUserImpersonatedToken = nullptr;
191     }
192 }
193 
GlobalSid()194 GlobalSid::GlobalSid()
195 {
196     {
197         {
198             // Create TRUSTEE for current user
199             HANDLE hnd = ::GetCurrentProcess();
200             HANDLE token = 0;
201             if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) {
202                 DWORD retsize = 0;
203                 // GetTokenInformation requires a buffer big enough for the TOKEN_USER struct and
204                 // the SID struct. Since the SID struct can have variable number of subauthorities
205                 // tacked at the end, its size is variable. Obtain the required size by first
206                 // doing a dummy GetTokenInformation call.
207                 ::GetTokenInformation(token, TokenUser, 0, 0, &retsize);
208                 if (retsize) {
209                     void *tokenBuffer = malloc(retsize);
210                     Q_CHECK_PTR(tokenBuffer);
211                     if (::GetTokenInformation(token, TokenUser, tokenBuffer, retsize, &retsize)) {
212                         PSID tokenSid = reinterpret_cast<PTOKEN_USER>(tokenBuffer)->User.Sid;
213                         DWORD sidLen = ::GetLengthSid(tokenSid);
214                         currentUserSID = reinterpret_cast<PSID>(malloc(sidLen));
215                         Q_CHECK_PTR(currentUserSID);
216                         if (::CopySid(sidLen, currentUserSID, tokenSid))
217                             BuildTrusteeWithSid(&currentUserTrusteeW, currentUserSID);
218                     }
219                     free(tokenBuffer);
220                 }
221                 ::CloseHandle(token);
222             }
223 
224             token = nullptr;
225             if (::OpenProcessToken(hnd, TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | STANDARD_RIGHTS_READ, &token)) {
226                 ::DuplicateToken(token, SecurityImpersonation, &currentUserImpersonatedToken);
227                 ::CloseHandle(token);
228             }
229 
230             {
231                 // Create TRUSTEE for Everyone (World)
232                 SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY };
233                 if (AllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &worldSID))
234                     BuildTrusteeWithSid(&worldTrusteeW, worldSID);
235             }
236         }
237     }
238 }
239 
240 Q_GLOBAL_STATIC(GlobalSid, initGlobalSid)
241 
242 QT_END_NAMESPACE
243 
244 } // anonymous namespace
245 #endif // QT_CONFIG(fslibs)
246 
247 QT_BEGIN_NAMESPACE
248 
249 Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
250 
toFileTime(const QDateTime & date,FILETIME * fileTime)251 static inline bool toFileTime(const QDateTime &date, FILETIME *fileTime)
252 {
253     SYSTEMTIME sTime;
254     if (date.timeSpec() == Qt::LocalTime) {
255         SYSTEMTIME lTime;
256         const QDate d = date.date();
257         const QTime t = date.time();
258 
259         lTime.wYear = d.year();
260         lTime.wMonth = d.month();
261         lTime.wDay = d.day();
262         lTime.wHour = t.hour();
263         lTime.wMinute = t.minute();
264         lTime.wSecond = t.second();
265         lTime.wMilliseconds = t.msec();
266         lTime.wDayOfWeek = d.dayOfWeek() % 7;
267 
268         if (!::TzSpecificLocalTimeToSystemTime(0, &lTime, &sTime))
269             return false;
270     } else {
271         QDateTime utcDate = date.toUTC();
272         const QDate d = utcDate.date();
273         const QTime t = utcDate.time();
274 
275         sTime.wYear = d.year();
276         sTime.wMonth = d.month();
277         sTime.wDay = d.day();
278         sTime.wHour = t.hour();
279         sTime.wMinute = t.minute();
280         sTime.wSecond = t.second();
281         sTime.wMilliseconds = t.msec();
282         sTime.wDayOfWeek = d.dayOfWeek() % 7;
283     }
284 
285     return ::SystemTimeToFileTime(&sTime, fileTime);
286 }
287 
readSymLink(const QFileSystemEntry & link)288 static QString readSymLink(const QFileSystemEntry &link)
289 {
290     QString result;
291 #if !defined(Q_OS_WINRT)
292     HANDLE handle = CreateFile((wchar_t*)link.nativeFilePath().utf16(),
293                                FILE_READ_EA,
294                                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
295                                0,
296                                OPEN_EXISTING,
297                                FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
298                                0);
299     if (handle != INVALID_HANDLE_VALUE) {
300         DWORD bufsize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
301         REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)malloc(bufsize);
302         Q_CHECK_PTR(rdb);
303         DWORD retsize = 0;
304         if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, 0, 0, rdb, bufsize, &retsize, 0)) {
305             if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
306                 int length = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
307                 int offset = rdb->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
308                 const wchar_t* PathBuffer = &rdb->MountPointReparseBuffer.PathBuffer[offset];
309                 result = QString::fromWCharArray(PathBuffer, length);
310             } else if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
311                 int length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
312                 int offset = rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
313                 const wchar_t* PathBuffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[offset];
314                 result = QString::fromWCharArray(PathBuffer, length);
315             }
316             // cut-off "\\?\" and "\??\"
317             if (result.size() > 4
318                     && result.at(0) == QLatin1Char('\\')
319                     && result.at(2) == QLatin1Char('?')
320                     && result.at(3) == QLatin1Char('\\')) {
321                 result = result.mid(4);
322                 // cut off UNC in addition when the link points at a UNC share
323                 // in which case we need to prepend another backslash to get \\server\share
324                 if (result.leftRef(3) == QLatin1String("UNC")) {
325                     result.replace(0, 3, QLatin1Char('\\'));
326                 }
327             }
328         }
329         free(rdb);
330         CloseHandle(handle);
331 
332 #if QT_CONFIG(fslibs)
333         initGlobalSid();
334         QRegExp matchVolName(QLatin1String("^Volume\\{([a-z]|[0-9]|-)+\\}\\\\"), Qt::CaseInsensitive);
335         if (matchVolName.indexIn(result) == 0) {
336             DWORD len;
337             wchar_t buffer[MAX_PATH];
338             const QString volumeName = QLatin1String("\\\\?\\") + result.leftRef(matchVolName.matchedLength());
339             if (GetVolumePathNamesForVolumeName(reinterpret_cast<LPCWSTR>(volumeName.utf16()), buffer, MAX_PATH, &len) != 0)
340                 result.replace(0,matchVolName.matchedLength(), QString::fromWCharArray(buffer));
341         }
342 #endif // QT_CONFIG(fslibs)
343     }
344 #else
345     Q_UNUSED(link);
346 #endif // Q_OS_WINRT
347     return result;
348 }
349 
readLink(const QFileSystemEntry & link)350 static QString readLink(const QFileSystemEntry &link)
351 {
352 #if QT_CONFIG(fslibs)
353     QString ret;
354 
355     bool neededCoInit = false;
356     IShellLink *psl;                            // pointer to IShellLink i/f
357     WIN32_FIND_DATA wfd;
358     wchar_t szGotPath[MAX_PATH];
359 
360     // Get pointer to the IShellLink interface.
361     HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl);
362 
363     if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
364         neededCoInit = true;
365         CoInitialize(NULL);
366         hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
367                                     IID_IShellLink, (LPVOID *)&psl);
368     }
369     if (SUCCEEDED(hres)) {    // Get pointer to the IPersistFile interface.
370         IPersistFile *ppf;
371         hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
372         if (SUCCEEDED(hres))  {
373             hres = ppf->Load((LPOLESTR)link.nativeFilePath().utf16(), STGM_READ);
374             //The original path of the link is retrieved. If the file/folder
375             //was moved, the return value still have the old path.
376             if (SUCCEEDED(hres)) {
377                 if (psl->GetPath(szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR)
378                     ret = QString::fromWCharArray(szGotPath);
379             }
380             ppf->Release();
381         }
382         psl->Release();
383     }
384     if (neededCoInit)
385         CoUninitialize();
386 
387     return ret;
388 #else
389     Q_UNUSED(link);
390     return QString();
391 #endif // QT_CONFIG(fslibs)
392 }
393 
uncShareExists(const QString & server)394 static bool uncShareExists(const QString &server)
395 {
396     // This code assumes the UNC path is always like \\?\UNC\server...
397     const QVector<QStringRef> parts = server.splitRef(QLatin1Char('\\'), Qt::SkipEmptyParts);
398     if (parts.count() >= 3) {
399         QStringList shares;
400         if (QFileSystemEngine::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(2), &shares))
401             return parts.count() < 4 || shares.contains(parts.at(3).toString(), Qt::CaseInsensitive);
402     }
403     return false;
404 }
405 
getFindData(QString path,WIN32_FIND_DATA & findData)406 static inline bool getFindData(QString path, WIN32_FIND_DATA &findData)
407 {
408     // path should not end with a trailing slash
409     while (path.endsWith(QLatin1Char('\\')))
410         path.chop(1);
411 
412     // can't handle drives
413     if (!path.endsWith(QLatin1Char(':'))) {
414 #ifndef Q_OS_WINRT
415         HANDLE hFind = ::FindFirstFile((wchar_t*)path.utf16(), &findData);
416 #else
417         HANDLE hFind = ::FindFirstFileEx((const wchar_t*)path.utf16(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0);
418 #endif
419         if (hFind != INVALID_HANDLE_VALUE) {
420             ::FindClose(hFind);
421             return true;
422         }
423     }
424 
425     return false;
426 }
427 
428 #if defined(__IFileOperation_INTERFACE_DEFINED__)
429 class FileOperationProgressSink : public IFileOperationProgressSink
430 {
431 public:
FileOperationProgressSink()432     FileOperationProgressSink()
433     : ref(1)
434     {}
~FileOperationProgressSink()435     virtual ~FileOperationProgressSink() {}
436 
AddRef()437     ULONG STDMETHODCALLTYPE AddRef()
438     {
439         return ++ref;
440     }
Release()441     ULONG STDMETHODCALLTYPE Release()
442     {
443         if (--ref == 0) {
444             delete this;
445             return 0;
446         }
447         return ref;
448     }
QueryInterface(REFIID iid,void ** ppvObject)449     HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
450     {
451         if (!ppvObject)
452             return E_POINTER;
453 
454         *ppvObject = nullptr;
455 
456         if (iid == __uuidof(IUnknown)) {
457             *ppvObject = static_cast<IUnknown*>(this);
458         } else if (iid == __uuidof(IFileOperationProgressSink)) {
459             *ppvObject = static_cast<IFileOperationProgressSink*>(this);
460         }
461 
462         if (*ppvObject) {
463             AddRef();
464             return S_OK;
465         }
466 
467         return E_NOINTERFACE;
468     }
469 
StartOperations()470     HRESULT STDMETHODCALLTYPE StartOperations()
471     { return S_OK; }
FinishOperations(HRESULT)472     HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT)
473     { return S_OK; }
PreRenameItem(DWORD,IShellItem *,LPCWSTR)474     HRESULT STDMETHODCALLTYPE PreRenameItem(DWORD, IShellItem *, LPCWSTR)
475     { return S_OK; }
PostRenameItem(DWORD,IShellItem *,LPCWSTR,HRESULT,IShellItem *)476     HRESULT STDMETHODCALLTYPE PostRenameItem(DWORD, IShellItem *, LPCWSTR, HRESULT, IShellItem *)
477     { return S_OK; }
PreMoveItem(DWORD,IShellItem *,IShellItem *,LPCWSTR)478     HRESULT STDMETHODCALLTYPE PreMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR)
479     { return S_OK; }
PostMoveItem(DWORD,IShellItem *,IShellItem *,LPCWSTR,HRESULT,IShellItem *)480     HRESULT STDMETHODCALLTYPE PostMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT,
481                                            IShellItem *)
482     { return S_OK; }
PreCopyItem(DWORD,IShellItem *,IShellItem *,LPCWSTR)483     HRESULT STDMETHODCALLTYPE PreCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR )
484     { return S_OK; }
PostCopyItem(DWORD,IShellItem *,IShellItem *,LPCWSTR,HRESULT,IShellItem *)485     HRESULT STDMETHODCALLTYPE PostCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT,
486                                            IShellItem *)
487     { return S_OK; }
PreDeleteItem(DWORD dwFlags,IShellItem *)488     HRESULT STDMETHODCALLTYPE PreDeleteItem(DWORD dwFlags, IShellItem *)
489     {
490         // stop the operation if the file will be deleted rather than trashed
491         return (dwFlags & TSF_DELETE_RECYCLE_IF_POSSIBLE) ? S_OK : E_FAIL;
492     }
PostDeleteItem(DWORD,IShellItem *,HRESULT,IShellItem * psiNewlyCreated)493     HRESULT STDMETHODCALLTYPE PostDeleteItem(DWORD /* dwFlags */, IShellItem * /* psiItem */,
494                                              HRESULT /* hrDelete */, IShellItem *psiNewlyCreated)
495     {
496         if (psiNewlyCreated) {
497             wchar_t *pszName = nullptr;
498             psiNewlyCreated->GetDisplayName(SIGDN_FILESYSPATH, &pszName);
499             if (pszName) {
500                 targetPath = QString::fromWCharArray(pszName);
501                 CoTaskMemFree(pszName);
502             }
503         }
504         return S_OK;
505     }
PreNewItem(DWORD,IShellItem *,LPCWSTR)506     HRESULT STDMETHODCALLTYPE PreNewItem(DWORD, IShellItem *, LPCWSTR)
507     { return S_OK; }
PostNewItem(DWORD,IShellItem *,LPCWSTR,LPCWSTR,DWORD,HRESULT,IShellItem *)508     HRESULT STDMETHODCALLTYPE PostNewItem(DWORD, IShellItem *, LPCWSTR, LPCWSTR, DWORD, HRESULT,
509                                           IShellItem *)
510     { return S_OK; }
UpdateProgress(UINT,UINT)511     HRESULT STDMETHODCALLTYPE UpdateProgress(UINT,UINT)
512     { return S_OK; }
ResetTimer()513     HRESULT STDMETHODCALLTYPE ResetTimer()
514     { return S_OK; }
PauseTimer()515     HRESULT STDMETHODCALLTYPE PauseTimer()
516     { return S_OK; }
ResumeTimer()517     HRESULT STDMETHODCALLTYPE ResumeTimer()
518     { return S_OK; }
519 
520     QString targetPath;
521 private:
522     ULONG ref;
523 };
524 #endif
525 
uncListSharesOnServer(const QString & server,QStringList * list)526 bool QFileSystemEngine::uncListSharesOnServer(const QString &server, QStringList *list)
527 {
528     DWORD res = ERROR_NOT_SUPPORTED;
529 #ifndef Q_OS_WINRT
530     SHARE_INFO_1 *BufPtr, *p;
531     DWORD er = 0, tr = 0, resume = 0, i;
532     do {
533         res = NetShareEnum((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume);
534         if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) {
535             p = BufPtr;
536             for (i = 1; i <= er; ++i) {
537                 if (list && p->shi1_type == 0)
538                     list->append(QString::fromWCharArray(p->shi1_netname));
539                 p++;
540             }
541         }
542         NetApiBufferFree(BufPtr);
543     } while (res == ERROR_MORE_DATA);
544 #else
545     Q_UNUSED(server);
546     Q_UNUSED(list);
547 #endif
548     return res == ERROR_SUCCESS;
549 }
550 
clearWinStatData(QFileSystemMetaData & data)551 void QFileSystemEngine::clearWinStatData(QFileSystemMetaData &data)
552 {
553     data.size_ = 0;
554     data.fileAttribute_ =  0;
555     data.birthTime_ = FILETIME();
556     data.changeTime_ = FILETIME();
557     data.lastAccessTime_ = FILETIME();
558     data.lastWriteTime_ = FILETIME();
559 }
560 
561 //static
getLinkTarget(const QFileSystemEntry & link,QFileSystemMetaData & data)562 QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
563                                                   QFileSystemMetaData &data)
564 {
565     Q_CHECK_FILE_NAME(link, link);
566 
567     if (data.missingFlags(QFileSystemMetaData::LinkType))
568        QFileSystemEngine::fillMetaData(link, data, QFileSystemMetaData::LinkType);
569 
570     QString target;
571     if (data.isLnkFile())
572         target = readLink(link);
573     else if (data.isLink())
574         target = readSymLink(link);
575     QFileSystemEntry ret(target);
576     if (!target.isEmpty() && ret.isRelative()) {
577         target.prepend(absoluteName(link).path() + QLatin1Char('/'));
578         ret = QFileSystemEntry(QDir::cleanPath(target));
579     }
580     return ret;
581 }
582 
583 //static
canonicalName(const QFileSystemEntry & entry,QFileSystemMetaData & data)584 QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
585 {
586     Q_CHECK_FILE_NAME(entry, entry);
587 
588     if (data.missingFlags(QFileSystemMetaData::ExistsAttribute))
589        QFileSystemEngine::fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute);
590 
591     if (data.exists())
592         return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath()));
593     else
594         return QFileSystemEntry();
595 }
596 
597 //static
nativeAbsoluteFilePath(const QString & path)598 QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path)
599 {
600     Q_CHECK_FILE_NAME(path, QString());
601 
602     // can be //server or //server/share
603     QString absPath;
604     QVarLengthArray<wchar_t, MAX_PATH> buf(qMax(MAX_PATH, path.size() + 1));
605     wchar_t *fileName = 0;
606     DWORD retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
607     if (retLen > (DWORD)buf.size()) {
608         buf.resize(retLen);
609         retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
610     }
611     if (retLen != 0)
612         absPath = QString::fromWCharArray(buf.data(), retLen);
613 #  if defined(Q_OS_WINRT)
614     // Win32 returns eg C:/ as root directory with a trailing /.
615     // WinRT returns the sandbox root without /.
616     // Also C:/../.. returns C:/ on Win32, while for WinRT it steps outside the package
617     // and goes beyond package root. Hence force the engine to stay inside
618     // the package.
619     const QString rootPath = QDir::toNativeSeparators(QDir::rootPath());
620     if (absPath.size() < rootPath.size() && rootPath.startsWith(absPath))
621         absPath = rootPath;
622 #  endif // Q_OS_WINRT
623 
624     // This is really ugly, but GetFullPathName strips off whitespace at the end.
625     // If you for instance write ". " in the lineedit of QFileDialog,
626     // (which is an invalid filename) this function will strip the space off and viola,
627     // the file is later reported as existing. Therefore, we re-add the whitespace that
628     // was at the end of path in order to keep the filename invalid.
629     if (!path.isEmpty() && path.at(path.size() - 1) == QLatin1Char(' '))
630         absPath.append(QLatin1Char(' '));
631     return absPath;
632 }
633 
634 //static
absoluteName(const QFileSystemEntry & entry)635 QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
636 {
637     Q_CHECK_FILE_NAME(entry, entry);
638 
639     QString ret;
640 
641     if (!entry.isRelative()) {
642         if (entry.isAbsolute() && entry.isClean())
643             ret = entry.filePath();
644         else
645             ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(entry.filePath()));
646     } else {
647         ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + entry.filePath());
648     }
649 
650 #ifndef Q_OS_WINRT
651     // The path should be absolute at this point.
652     // From the docs :
653     // Absolute paths begin with the directory separator "/"
654     // (optionally preceded by a drive specification under Windows).
655     if (ret.at(0) != QLatin1Char('/')) {
656         Q_ASSERT(ret.length() >= 2);
657         Q_ASSERT(ret.at(0).isLetter());
658         Q_ASSERT(ret.at(1) == QLatin1Char(':'));
659 
660         // Force uppercase drive letters.
661         ret[0] = ret.at(0).toUpper();
662     }
663 #endif // !Q_OS_WINRT
664     return QFileSystemEntry(ret, QFileSystemEntry::FromInternalPath());
665 }
666 
667 #if defined(Q_CC_MINGW) && WINVER < 0x0602 //  Windows 8 onwards
668 
669 typedef struct _FILE_ID_INFO {
670     ULONGLONG VolumeSerialNumber;
671     FILE_ID_128 FileId;
672 } FILE_ID_INFO, *PFILE_ID_INFO;
673 
674 #endif // if defined (Q_CC_MINGW) && WINVER < 0x0602
675 
676 // File ID for Windows up to version 7 and FAT32 drives
fileId(HANDLE handle)677 static inline QByteArray fileId(HANDLE handle)
678 {
679 #ifndef Q_OS_WINRT
680     BY_HANDLE_FILE_INFORMATION info;
681     if (GetFileInformationByHandle(handle, &info)) {
682         char buffer[sizeof "01234567:0123456701234567"];
683         qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx",
684                   info.dwVolumeSerialNumber,
685                   info.nFileIndexHigh,
686                   info.nFileIndexLow);
687         return buffer;
688     }
689 #else // !Q_OS_WINRT
690     Q_UNUSED(handle);
691     Q_UNIMPLEMENTED();
692 #endif // Q_OS_WINRT
693     return QByteArray();
694 }
695 
696 // File ID for Windows starting from version 8.
fileIdWin8(HANDLE handle)697 QByteArray fileIdWin8(HANDLE handle)
698 {
699 #if !defined(QT_BOOTSTRAPPED) && !defined(QT_BUILD_QMAKE)
700     QByteArray result;
701     FILE_ID_INFO infoEx;
702     if (GetFileInformationByHandleEx(handle,
703                                      static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8
704                                      &infoEx, sizeof(FILE_ID_INFO))) {
705         result = QByteArray::number(infoEx.VolumeSerialNumber, 16);
706         result += ':';
707         // Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one.
708         result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId), int(sizeof(infoEx.FileId))).toHex();
709     } else {
710         result = fileId(handle); // GetFileInformationByHandleEx() is observed to fail for FAT32, QTBUG-74759
711     }
712     return result;
713 #else // !QT_BOOTSTRAPPED && !QT_BUILD_QMAKE
714     return fileId(handle);
715 #endif
716 }
717 
718 //static
id(const QFileSystemEntry & entry)719 QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
720 {
721     Q_CHECK_FILE_NAME(entry, QByteArray());
722 
723     QByteArray result;
724 
725 #ifndef Q_OS_WINRT
726     const HANDLE handle =
727         CreateFile((wchar_t*)entry.nativeFilePath().utf16(), 0,
728                    FILE_SHARE_READ, NULL, OPEN_EXISTING,
729                    FILE_FLAG_BACKUP_SEMANTICS, NULL);
730 #else // !Q_OS_WINRT
731     CREATEFILE2_EXTENDED_PARAMETERS params;
732     params.dwSize = sizeof(params);
733     params.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
734     params.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
735     params.dwSecurityQosFlags = SECURITY_ANONYMOUS;
736     params.lpSecurityAttributes = NULL;
737     params.hTemplateFile = NULL;
738     const HANDLE handle =
739         CreateFile2((const wchar_t*)entry.nativeFilePath().utf16(), 0,
740                     FILE_SHARE_READ, OPEN_EXISTING, &params);
741 #endif // Q_OS_WINRT
742     if (handle != INVALID_HANDLE_VALUE) {
743         result = id(handle);
744         CloseHandle(handle);
745     }
746     return result;
747 }
748 
749 //static
id(HANDLE fHandle)750 QByteArray QFileSystemEngine::id(HANDLE fHandle)
751 {
752     return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 ?
753                 fileIdWin8(HANDLE(fHandle)) : fileId(HANDLE(fHandle));
754 }
755 
756 //static
setFileTime(HANDLE fHandle,const QDateTime & newDate,QAbstractFileEngine::FileTime time,QSystemError & error)757 bool QFileSystemEngine::setFileTime(HANDLE fHandle, const QDateTime &newDate,
758                                     QAbstractFileEngine::FileTime time, QSystemError &error)
759 {
760     FILETIME fTime;
761     FILETIME *pLastWrite = NULL;
762     FILETIME *pLastAccess = NULL;
763     FILETIME *pCreationTime = NULL;
764 
765     switch (time) {
766     case QAbstractFileEngine::ModificationTime:
767         pLastWrite = &fTime;
768         break;
769 
770     case QAbstractFileEngine::AccessTime:
771         pLastAccess = &fTime;
772         break;
773 
774     case QAbstractFileEngine::BirthTime:
775         pCreationTime = &fTime;
776         break;
777 
778     default:
779         error = QSystemError(ERROR_INVALID_PARAMETER, QSystemError::NativeError);
780         return false;
781     }
782 
783     if (!toFileTime(newDate, &fTime))
784         return false;
785 
786     if (!::SetFileTime(fHandle, pCreationTime, pLastAccess, pLastWrite)) {
787         error = QSystemError(::GetLastError(), QSystemError::NativeError);
788         return false;
789     }
790     return true;
791 }
792 
owner(const QFileSystemEntry & entry,QAbstractFileEngine::FileOwner own)793 QString QFileSystemEngine::owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own)
794 {
795     QString name;
796 #if QT_CONFIG(fslibs)
797     extern int qt_ntfs_permission_lookup;
798     if (qt_ntfs_permission_lookup > 0) {
799         initGlobalSid();
800         {
801             PSID pOwner = 0;
802             PSECURITY_DESCRIPTOR pSD;
803             if (GetNamedSecurityInfo(reinterpret_cast<const wchar_t*>(entry.nativeFilePath().utf16()), SE_FILE_OBJECT,
804                                      own == QAbstractFileEngine::OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION,
805                                      own == QAbstractFileEngine::OwnerUser ? &pOwner : 0, own == QAbstractFileEngine::OwnerGroup ? &pOwner : 0,
806                                      0, 0, &pSD) == ERROR_SUCCESS) {
807                 DWORD lowner = 64;
808                 DWORD ldomain = 64;
809                 QVarLengthArray<wchar_t, 64> owner(lowner);
810                 QVarLengthArray<wchar_t, 64> domain(ldomain);
811                 SID_NAME_USE use = SidTypeUnknown;
812                 // First call, to determine size of the strings (with '\0').
813                 if (!LookupAccountSid(NULL, pOwner, (LPWSTR)owner.data(), &lowner,
814                                       domain.data(), &ldomain, &use)) {
815                     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
816                         if (lowner > (DWORD)owner.size())
817                             owner.resize(lowner);
818                         if (ldomain > (DWORD)domain.size())
819                             domain.resize(ldomain);
820                         // Second call, try on resized buf-s
821                         if (!LookupAccountSid(NULL, pOwner, owner.data(), &lowner,
822                                               domain.data(), &ldomain, &use)) {
823                             lowner = 0;
824                         }
825                     } else {
826                         lowner = 0;
827                     }
828                 }
829                 if (lowner != 0)
830                     name = QString::fromWCharArray(owner.data());
831                 LocalFree(pSD);
832             }
833         }
834     }
835 #else
836     Q_UNUSED(entry);
837     Q_UNUSED(own);
838 #endif
839     return name;
840 }
841 
842 //static
fillPermissions(const QFileSystemEntry & entry,QFileSystemMetaData & data,QFileSystemMetaData::MetaDataFlags what)843 bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSystemMetaData &data,
844                                         QFileSystemMetaData::MetaDataFlags what)
845 {
846 #if QT_CONFIG(fslibs)
847     if (qt_ntfs_permission_lookup > 0) {
848         initGlobalSid();
849         {
850             enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 };
851 
852             QString fname = entry.nativeFilePath();
853             PSID pOwner = 0;
854             PSID pGroup = 0;
855             PACL pDacl;
856             PSECURITY_DESCRIPTOR pSD;
857             DWORD res = GetNamedSecurityInfo(reinterpret_cast<const wchar_t*>(fname.utf16()), SE_FILE_OBJECT,
858                                              OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
859                                              &pOwner, &pGroup, &pDacl, 0, &pSD);
860             if(res == ERROR_SUCCESS) {
861                 ACCESS_MASK access_mask;
862                 TRUSTEE_W trustee;
863                 if (what & QFileSystemMetaData::UserPermissions) { // user
864                     // Using AccessCheck because GetEffectiveRightsFromAcl doesn't account for elevation
865                     if (currentUserImpersonatedToken) {
866                         GENERIC_MAPPING mapping = {FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS};
867                         PRIVILEGE_SET privileges;
868                         DWORD grantedAccess;
869                         BOOL result;
870 
871                         data.knownFlagsMask |= QFileSystemMetaData::UserPermissions;
872                         DWORD genericAccessRights = GENERIC_READ;
873                         ::MapGenericMask(&genericAccessRights, &mapping);
874 
875                         DWORD privilegesLength = sizeof(privileges);
876                         if (::AccessCheck(pSD, currentUserImpersonatedToken, genericAccessRights,
877                                           &mapping, &privileges, &privilegesLength, &grantedAccess, &result) && result) {
878                             data.entryFlags |= QFileSystemMetaData::UserReadPermission;
879                         }
880 
881                         privilegesLength = sizeof(privileges);
882                         genericAccessRights = GENERIC_WRITE;
883                         ::MapGenericMask(&genericAccessRights, &mapping);
884                         if (::AccessCheck(pSD, currentUserImpersonatedToken, genericAccessRights,
885                                           &mapping, &privileges, &privilegesLength, &grantedAccess, &result) && result) {
886                             data.entryFlags |= QFileSystemMetaData::UserWritePermission;
887                         }
888 
889                         privilegesLength = sizeof(privileges);
890                         genericAccessRights = GENERIC_EXECUTE;
891                         ::MapGenericMask(&genericAccessRights, &mapping);
892                         if (::AccessCheck(pSD, currentUserImpersonatedToken, genericAccessRights,
893                                           &mapping, &privileges, &privilegesLength, &grantedAccess, &result) && result) {
894                             data.entryFlags |= QFileSystemMetaData::UserExecutePermission;
895                         }
896                     } else { // fallback to GetEffectiveRightsFromAcl
897                         data.knownFlagsMask |= QFileSystemMetaData::UserPermissions;
898                         if (GetEffectiveRightsFromAclW(pDacl, &currentUserTrusteeW, &access_mask) != ERROR_SUCCESS)
899                             access_mask = ACCESS_MASK(-1);
900                         if (access_mask & ReadMask)
901                             data.entryFlags |= QFileSystemMetaData::UserReadPermission;
902                         if (access_mask & WriteMask)
903                             data.entryFlags|= QFileSystemMetaData::UserWritePermission;
904                         if (access_mask & ExecMask)
905                             data.entryFlags|= QFileSystemMetaData::UserExecutePermission;
906                     }
907                 }
908                 if (what & QFileSystemMetaData::OwnerPermissions) { // owner
909                     data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions;
910                     BuildTrusteeWithSid(&trustee, pOwner);
911                     if (GetEffectiveRightsFromAcl(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
912                         access_mask = (ACCESS_MASK)-1;
913                     if(access_mask & ReadMask)
914                         data.entryFlags |= QFileSystemMetaData::OwnerReadPermission;
915                     if(access_mask & WriteMask)
916                         data.entryFlags |= QFileSystemMetaData::OwnerWritePermission;
917                     if(access_mask & ExecMask)
918                         data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
919                 }
920                 if (what & QFileSystemMetaData::GroupPermissions) { // group
921                     data.knownFlagsMask |= QFileSystemMetaData::GroupPermissions;
922                     BuildTrusteeWithSid(&trustee, pGroup);
923                     if (GetEffectiveRightsFromAcl(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
924                         access_mask = (ACCESS_MASK)-1;
925                     if(access_mask & ReadMask)
926                         data.entryFlags |= QFileSystemMetaData::GroupReadPermission;
927                     if(access_mask & WriteMask)
928                         data.entryFlags |= QFileSystemMetaData::GroupWritePermission;
929                     if(access_mask & ExecMask)
930                         data.entryFlags |= QFileSystemMetaData::GroupExecutePermission;
931                 }
932                 if (what & QFileSystemMetaData::OtherPermissions) { // other (world)
933                     data.knownFlagsMask |= QFileSystemMetaData::OtherPermissions;
934                     if (GetEffectiveRightsFromAcl(pDacl, &worldTrusteeW, &access_mask) != ERROR_SUCCESS)
935                         access_mask = (ACCESS_MASK)-1; // ###
936                     if(access_mask & ReadMask)
937                         data.entryFlags |= QFileSystemMetaData::OtherReadPermission;
938                     if(access_mask & WriteMask)
939                         data.entryFlags |= QFileSystemMetaData::OtherWritePermission;
940                     if(access_mask & ExecMask)
941                         data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
942                 }
943                 LocalFree(pSD);
944             }
945         }
946     } else
947 #endif
948     {
949         //### what to do with permissions if we don't use NTFS
950         // for now just add all permissions and what about exe missions ??
951         // also qt_ntfs_permission_lookup is now not set by default ... should it ?
952         data.entryFlags |= QFileSystemMetaData::OwnerReadPermission
953                            | QFileSystemMetaData::GroupReadPermission
954                            | QFileSystemMetaData::OtherReadPermission;
955 
956         if (!(data.fileAttribute_ & FILE_ATTRIBUTE_READONLY)) {
957             data.entryFlags |= QFileSystemMetaData::OwnerWritePermission
958                    | QFileSystemMetaData::GroupWritePermission
959                    | QFileSystemMetaData::OtherWritePermission;
960         }
961 
962         QString fname = entry.filePath();
963         QString ext = fname.right(4).toLower();
964         if (data.isDirectory() ||
965             ext == QLatin1String(".exe") || ext == QLatin1String(".com") || ext == QLatin1String(".bat") ||
966             ext == QLatin1String(".pif") || ext == QLatin1String(".cmd")) {
967             data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission | QFileSystemMetaData::GroupExecutePermission
968                                | QFileSystemMetaData::OtherExecutePermission | QFileSystemMetaData::UserExecutePermission;
969         }
970         data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions | QFileSystemMetaData::GroupPermissions
971                                 | QFileSystemMetaData::OtherPermissions | QFileSystemMetaData::UserExecutePermission;
972         // calculate user permissions
973         if (what & QFileSystemMetaData::UserReadPermission) {
974             if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), R_OK) == 0)
975                 data.entryFlags |= QFileSystemMetaData::UserReadPermission;
976             data.knownFlagsMask |= QFileSystemMetaData::UserReadPermission;
977         }
978         if (what & QFileSystemMetaData::UserWritePermission) {
979             if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), W_OK) == 0)
980                 data.entryFlags |= QFileSystemMetaData::UserWritePermission;
981             data.knownFlagsMask |= QFileSystemMetaData::UserWritePermission;
982         }
983     }
984 
985     return data.hasFlags(what);
986 }
987 
tryDriveUNCFallback(const QFileSystemEntry & fname,QFileSystemMetaData & data)988 static bool tryDriveUNCFallback(const QFileSystemEntry &fname, QFileSystemMetaData &data)
989 {
990     bool entryExists = false;
991     DWORD fileAttrib = 0;
992 #if !defined(Q_OS_WINRT)
993     if (fname.isDriveRoot()) {
994         // a valid drive ??
995         const UINT oldErrorMode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
996         DWORD drivesBitmask = ::GetLogicalDrives();
997         ::SetErrorMode(oldErrorMode);
998         int drivebit = 1 << (fname.filePath().at(0).toUpper().unicode() - QLatin1Char('A').unicode());
999         if (drivesBitmask & drivebit) {
1000             fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM;
1001             entryExists = true;
1002         }
1003     } else {
1004 #endif
1005         const QString &path = fname.nativeFilePath();
1006         bool is_dir = false;
1007         if (path.startsWith(QLatin1String("\\\\?\\UNC"))) {
1008             // UNC - stat doesn't work for all cases (Windows bug)
1009             int s = path.indexOf(path.at(0),7);
1010             if (s > 0) {
1011                 // "\\?\UNC\server\..."
1012                 s = path.indexOf(path.at(0),s+1);
1013                 if (s > 0) {
1014                     // "\\?\UNC\server\share\..."
1015                     if (s == path.size() - 1) {
1016                         // "\\?\UNC\server\share\"
1017                         is_dir = true;
1018                     } else {
1019                         // "\\?\UNC\server\share\notfound"
1020                     }
1021                 } else {
1022                     // "\\?\UNC\server\share"
1023                     is_dir = true;
1024                 }
1025             } else {
1026                 // "\\?\UNC\server"
1027                 is_dir = true;
1028             }
1029         }
1030         if (is_dir && uncShareExists(path)) {
1031             // looks like a UNC dir, is a dir.
1032             fileAttrib = FILE_ATTRIBUTE_DIRECTORY;
1033             entryExists = true;
1034         }
1035 #if !defined(Q_OS_WINRT)
1036     }
1037 #endif
1038     if (entryExists)
1039         data.fillFromFileAttribute(fileAttrib);
1040     return entryExists;
1041 }
1042 
tryFindFallback(const QFileSystemEntry & fname,QFileSystemMetaData & data)1043 static bool tryFindFallback(const QFileSystemEntry &fname, QFileSystemMetaData &data)
1044 {
1045     bool filledData = false;
1046     // This assumes the last call to a Windows API failed.
1047     int errorCode = GetLastError();
1048     if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
1049         WIN32_FIND_DATA findData;
1050         if (getFindData(fname.nativeFilePath(), findData)
1051             && findData.dwFileAttributes != INVALID_FILE_ATTRIBUTES) {
1052             data.fillFromFindData(findData, true, fname.isDriveRoot());
1053             filledData = true;
1054         }
1055     }
1056     return filledData;
1057 }
1058 
1059 //static
fillMetaData(int fd,QFileSystemMetaData & data,QFileSystemMetaData::MetaDataFlags what)1060 bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data,
1061                                      QFileSystemMetaData::MetaDataFlags what)
1062 {
1063     auto fHandle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
1064     if (fHandle  != INVALID_HANDLE_VALUE) {
1065         return fillMetaData(fHandle, data, what);
1066     }
1067     return false;
1068 }
1069 
1070 //static
fillMetaData(HANDLE fHandle,QFileSystemMetaData & data,QFileSystemMetaData::MetaDataFlags what)1071 bool QFileSystemEngine::fillMetaData(HANDLE fHandle, QFileSystemMetaData &data,
1072                                      QFileSystemMetaData::MetaDataFlags what)
1073 {
1074     data.entryFlags &= ~what;
1075     clearWinStatData(data);
1076 #ifndef Q_OS_WINRT
1077     BY_HANDLE_FILE_INFORMATION fileInfo;
1078     UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1079     if (GetFileInformationByHandle(fHandle , &fileInfo)) {
1080         data.fillFromFindInfo(fileInfo);
1081     }
1082     SetErrorMode(oldmode);
1083 #else // !Q_OS_WINRT
1084     FILE_BASIC_INFO fileBasicInfo;
1085     if (GetFileInformationByHandleEx(fHandle, FileBasicInfo, &fileBasicInfo, sizeof(fileBasicInfo))) {
1086         data.fillFromFileAttribute(fileBasicInfo.FileAttributes);
1087         data.birthTime_.dwHighDateTime = fileBasicInfo.CreationTime.HighPart;
1088         data.birthTime_.dwLowDateTime = fileBasicInfo.CreationTime.LowPart;
1089         data.changeTime_.dwHighDateTime = fileBasicInfo.ChangeTime.HighPart;
1090         data.changeTime_.dwLowDateTime = fileBasicInfo.ChangeTime.LowPart;
1091         data.lastAccessTime_.dwHighDateTime = fileBasicInfo.LastAccessTime.HighPart;
1092         data.lastAccessTime_.dwLowDateTime = fileBasicInfo.LastAccessTime.LowPart;
1093         data.lastWriteTime_.dwHighDateTime = fileBasicInfo.LastWriteTime.HighPart;
1094         data.lastWriteTime_.dwLowDateTime = fileBasicInfo.LastWriteTime.LowPart;
1095         if (!(data.fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY)) {
1096             FILE_STANDARD_INFO fileStandardInfo;
1097             if (GetFileInformationByHandleEx(fHandle, FileStandardInfo, &fileStandardInfo, sizeof(fileStandardInfo)))
1098                 data.size_ = fileStandardInfo.EndOfFile.QuadPart;
1099         } else
1100             data.size_ = 0;
1101         data.knownFlagsMask |=  QFileSystemMetaData::Times | QFileSystemMetaData::SizeAttribute;
1102     }
1103 #endif // Q_OS_WINRT
1104     return data.hasFlags(what);
1105 }
1106 
1107 static bool isDirPath(const QString &dirPath, bool *existed);
1108 
1109 //static
fillMetaData(const QFileSystemEntry & entry,QFileSystemMetaData & data,QFileSystemMetaData::MetaDataFlags what)1110 bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
1111                                      QFileSystemMetaData::MetaDataFlags what)
1112 {
1113     Q_CHECK_FILE_NAME(entry, false);
1114     what |= QFileSystemMetaData::WinLnkType | QFileSystemMetaData::WinStatFlags;
1115     data.entryFlags &= ~what;
1116 
1117     QFileSystemEntry fname;
1118     data.knownFlagsMask |= QFileSystemMetaData::WinLnkType;
1119     // Check for ".lnk": Directories named ".lnk" should be skipped, corrupted
1120     // link files should still be detected as links.
1121     const QString origFilePath = entry.filePath();
1122     if (origFilePath.endsWith(QLatin1String(".lnk")) && !isDirPath(origFilePath, 0)) {
1123         data.entryFlags |= QFileSystemMetaData::WinLnkType;
1124         fname = QFileSystemEntry(readLink(entry));
1125     } else {
1126         fname = entry;
1127     }
1128 
1129     if (fname.isEmpty()) {
1130         data.knownFlagsMask |= what;
1131         clearWinStatData(data);
1132         return false;
1133     }
1134 
1135     if (what & QFileSystemMetaData::WinStatFlags) {
1136 #ifndef Q_OS_WINRT
1137         UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1138 #endif
1139         clearWinStatData(data);
1140         WIN32_FIND_DATA findData;
1141         // The memory structure for WIN32_FIND_DATA is same as WIN32_FILE_ATTRIBUTE_DATA
1142         // for all members used by fillFindData().
1143         bool ok = ::GetFileAttributesEx(reinterpret_cast<const wchar_t*>(fname.nativeFilePath().utf16()),
1144                                         GetFileExInfoStandard,
1145                                         reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA *>(&findData));
1146         if (ok) {
1147             data.fillFromFindData(findData, false, fname.isDriveRoot());
1148         } else {
1149             const DWORD lastError = GetLastError();
1150             if (lastError == ERROR_LOGON_FAILURE || lastError == ERROR_BAD_NETPATH // disconnected drive
1151                 || (!tryFindFallback(fname, data) && !tryDriveUNCFallback(fname, data))) {
1152                 data.clearFlags();
1153 #ifndef Q_OS_WINRT
1154                 SetErrorMode(oldmode);
1155 #endif
1156                 return false;
1157             }
1158         }
1159 #ifndef Q_OS_WINRT
1160         SetErrorMode(oldmode);
1161 #endif
1162     }
1163 
1164     if (what & QFileSystemMetaData::Permissions)
1165         fillPermissions(fname, data, what);
1166     if (what & QFileSystemMetaData::LinkType) {
1167         data.knownFlagsMask |= QFileSystemMetaData::LinkType;
1168         if (data.fileAttribute_ & FILE_ATTRIBUTE_REPARSE_POINT) {
1169             WIN32_FIND_DATA findData;
1170             if (getFindData(fname.nativeFilePath(), findData))
1171                 data.fillFromFindData(findData, true);
1172         }
1173     }
1174     data.knownFlagsMask |= what;
1175     return data.hasFlags(what);
1176 }
1177 
mkDir(const QString & path,DWORD * lastError=0)1178 static inline bool mkDir(const QString &path, DWORD *lastError = 0)
1179 {
1180     if (lastError)
1181         *lastError = 0;
1182     const QString longPath = QFSFileEnginePrivate::longFileName(path);
1183     const bool result = ::CreateDirectory((wchar_t*)longPath.utf16(), 0);
1184     if (lastError) // Capture lastError before any QString is freed since custom allocators might change it.
1185         *lastError = GetLastError();
1186     return result;
1187 }
1188 
rmDir(const QString & path)1189 static inline bool rmDir(const QString &path)
1190 {
1191     return ::RemoveDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16());
1192 }
1193 
isDirPath(const QString & dirPath,bool * existed)1194 static bool isDirPath(const QString &dirPath, bool *existed)
1195 {
1196     QString path = dirPath;
1197     if (path.length() == 2 && path.at(1) == QLatin1Char(':'))
1198         path += QLatin1Char('\\');
1199 
1200     const QString longPath = QFSFileEnginePrivate::longFileName(path);
1201 #ifndef Q_OS_WINRT
1202     DWORD fileAttrib = ::GetFileAttributes(reinterpret_cast<const wchar_t*>(longPath.utf16()));
1203 #else // Q_OS_WINRT
1204     DWORD fileAttrib = INVALID_FILE_ATTRIBUTES;
1205     WIN32_FILE_ATTRIBUTE_DATA data;
1206     if (::GetFileAttributesEx(reinterpret_cast<const wchar_t*>(longPath.utf16()),
1207                               GetFileExInfoStandard, &data)) {
1208         fileAttrib = data.dwFileAttributes;
1209     }
1210 #endif // Q_OS_WINRT
1211     if (fileAttrib == INVALID_FILE_ATTRIBUTES) {
1212         int errorCode = GetLastError();
1213         if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) {
1214             WIN32_FIND_DATA findData;
1215             if (getFindData(longPath, findData))
1216                 fileAttrib = findData.dwFileAttributes;
1217         }
1218     }
1219 
1220     if (existed)
1221         *existed = fileAttrib != INVALID_FILE_ATTRIBUTES;
1222 
1223     if (fileAttrib == INVALID_FILE_ATTRIBUTES)
1224         return false;
1225 
1226     return fileAttrib & FILE_ATTRIBUTE_DIRECTORY;
1227 }
1228 
1229 // NOTE: if \a shouldMkdirFirst is false, we assume the caller did try to mkdir
1230 // before calling this function.
createDirectoryWithParents(const QString & nativeName,bool shouldMkdirFirst=true)1231 static bool createDirectoryWithParents(const QString &nativeName, bool shouldMkdirFirst = true)
1232 {
1233     const auto isUNCRoot = [](const QString &nativeName) {
1234         return nativeName.startsWith(QLatin1String("\\\\")) && nativeName.count(QDir::separator()) <= 3;
1235     };
1236     const auto isDriveName = [](const QString &nativeName) {
1237         return nativeName.size() == 2 && nativeName.at(1) == QLatin1Char(':');
1238     };
1239     const auto isDir = [](const QString &nativeName) {
1240         bool exists = false;
1241         return isDirPath(nativeName, &exists) && exists;
1242     };
1243     // Do not try to mkdir a UNC root path or a drive letter.
1244     if (isUNCRoot(nativeName) || isDriveName(nativeName))
1245         return false;
1246 
1247     if (shouldMkdirFirst) {
1248         if (mkDir(nativeName))
1249             return true;
1250     }
1251 
1252     const int backSlash = nativeName.lastIndexOf(QDir::separator());
1253     if (backSlash < 1)
1254         return false;
1255 
1256     const QString parentNativeName = nativeName.left(backSlash);
1257     if (!createDirectoryWithParents(parentNativeName))
1258         return false;
1259 
1260     // try again
1261     if (mkDir(nativeName))
1262         return true;
1263     return isDir(nativeName);
1264 }
1265 
1266 //static
createDirectory(const QFileSystemEntry & entry,bool createParents)1267 bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
1268 {
1269     QString dirName = entry.filePath();
1270     Q_CHECK_FILE_NAME(dirName, false);
1271 
1272     dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
1273 
1274     // try to mkdir this directory
1275     DWORD lastError;
1276     if (mkDir(dirName, &lastError))
1277         return true;
1278     // mkpath should return true, if the directory already exists, mkdir false.
1279     if (!createParents)
1280         return false;
1281     if (lastError == ERROR_ALREADY_EXISTS)
1282         return isDirPath(dirName, nullptr);
1283 
1284     return createDirectoryWithParents(dirName, false);
1285 }
1286 
1287 //static
removeDirectory(const QFileSystemEntry & entry,bool removeEmptyParents)1288 bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
1289 {
1290     QString dirName = entry.filePath();
1291     Q_CHECK_FILE_NAME(dirName, false);
1292 
1293     if (removeEmptyParents) {
1294         dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
1295         for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
1296             const QStringRef chunkRef = dirName.leftRef(slash);
1297             if (chunkRef.length() == 2 && chunkRef.at(0).isLetter() && chunkRef.at(1) == QLatin1Char(':'))
1298                 break;
1299             const QString chunk = chunkRef.toString();
1300             if (!isDirPath(chunk, 0))
1301                 return false;
1302             if (!rmDir(chunk))
1303                 return oldslash != 0;
1304             slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
1305         }
1306         return true;
1307     }
1308     return rmDir(entry.filePath());
1309 }
1310 
1311 //static
rootPath()1312 QString QFileSystemEngine::rootPath()
1313 {
1314 #if defined(Q_OS_WINRT)
1315     // We specify the package root as root directory
1316     QString ret = QLatin1String("/");
1317     // Get package location
1318     ComPtr<IPackageStatics> statics;
1319     if (FAILED(GetActivationFactory(HStringReference(RuntimeClass_Windows_ApplicationModel_Package).Get(), &statics)))
1320         return ret;
1321     ComPtr<IPackage> package;
1322     if (FAILED(statics->get_Current(&package)))
1323         return ret;
1324     ComPtr<IStorageFolder> installedLocation;
1325     if (FAILED(package->get_InstalledLocation(&installedLocation)))
1326         return ret;
1327 
1328     ComPtr<IStorageItem> item;
1329     if (FAILED(installedLocation.As(&item)))
1330         return ret;
1331 
1332     HString finalWinPath;
1333     if (FAILED(item->get_Path(finalWinPath.GetAddressOf())))
1334         return ret;
1335 
1336     const QString qtWinPath = QDir::fromNativeSeparators(QString::fromWCharArray(finalWinPath.GetRawBuffer(nullptr)));
1337     ret = qtWinPath.endsWith(QLatin1Char('/')) ? qtWinPath : qtWinPath + QLatin1Char('/');
1338 #else
1339     QString ret = QString::fromLatin1(qgetenv("SystemDrive"));
1340     if (ret.isEmpty())
1341         ret = QLatin1String("c:");
1342     ret.append(QLatin1Char('/'));
1343 #endif
1344     return ret;
1345 }
1346 
1347 //static
homePath()1348 QString QFileSystemEngine::homePath()
1349 {
1350     QString ret;
1351 #if QT_CONFIG(fslibs)
1352     initGlobalSid();
1353     {
1354         HANDLE hnd = ::GetCurrentProcess();
1355         HANDLE token = 0;
1356         BOOL ok = ::OpenProcessToken(hnd, TOKEN_QUERY, &token);
1357         if (ok) {
1358             DWORD dwBufferSize = 0;
1359             // First call, to determine size of the strings (with '\0').
1360             ok = GetUserProfileDirectory(token, NULL, &dwBufferSize);
1361             if (!ok && dwBufferSize != 0) {        // We got the required buffer size
1362                 wchar_t *userDirectory = new wchar_t[dwBufferSize];
1363                 // Second call, now we can fill the allocated buffer.
1364                 ok = GetUserProfileDirectory(token, userDirectory, &dwBufferSize);
1365                 if (ok)
1366                     ret = QString::fromWCharArray(userDirectory);
1367                 delete [] userDirectory;
1368             }
1369             ::CloseHandle(token);
1370         }
1371     }
1372 #endif
1373     if (ret.isEmpty() || !QFile::exists(ret)) {
1374         ret = QString::fromLocal8Bit(qgetenv("USERPROFILE"));
1375         if (ret.isEmpty() || !QFile::exists(ret)) {
1376             ret = QString::fromLocal8Bit(qgetenv("HOMEDRIVE"))
1377                   + QString::fromLocal8Bit(qgetenv("HOMEPATH"));
1378             if (ret.isEmpty() || !QFile::exists(ret)) {
1379                 ret = QString::fromLocal8Bit(qgetenv("HOME"));
1380                 if (ret.isEmpty() || !QFile::exists(ret))
1381                     ret = rootPath();
1382             }
1383         }
1384     }
1385     return QDir::fromNativeSeparators(ret);
1386 }
1387 
tempPath()1388 QString QFileSystemEngine::tempPath()
1389 {
1390     QString ret;
1391 #ifndef Q_OS_WINRT
1392     wchar_t tempPath[MAX_PATH];
1393     const DWORD len = GetTempPath(MAX_PATH, tempPath);
1394     if (len) { // GetTempPath() can return short names, expand.
1395         wchar_t longTempPath[MAX_PATH];
1396         const DWORD longLen = GetLongPathName(tempPath, longTempPath, MAX_PATH);
1397         ret = longLen && longLen < MAX_PATH ?
1398               QString::fromWCharArray(longTempPath, longLen) :
1399               QString::fromWCharArray(tempPath, len);
1400     }
1401     if (!ret.isEmpty()) {
1402         while (ret.endsWith(QLatin1Char('\\')))
1403             ret.chop(1);
1404         ret = QDir::fromNativeSeparators(ret);
1405     }
1406 #else // !Q_OS_WINRT
1407     ComPtr<IApplicationDataStatics> applicationDataStatics;
1408     if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_ApplicationData).Get(), &applicationDataStatics)))
1409         return ret;
1410     ComPtr<IApplicationData> applicationData;
1411     if (FAILED(applicationDataStatics->get_Current(&applicationData)))
1412         return ret;
1413     ComPtr<IStorageFolder> tempFolder;
1414     if (FAILED(applicationData->get_TemporaryFolder(&tempFolder)))
1415         return ret;
1416     ComPtr<IStorageItem> tempFolderItem;
1417     if (FAILED(tempFolder.As(&tempFolderItem)))
1418         return ret;
1419     HString path;
1420     if (FAILED(tempFolderItem->get_Path(path.GetAddressOf())))
1421         return ret;
1422     ret = QDir::fromNativeSeparators(QString::fromWCharArray(path.GetRawBuffer(nullptr)));
1423 #endif // Q_OS_WINRT
1424     if (ret.isEmpty()) {
1425         ret = QLatin1String("C:/tmp");
1426     } else if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
1427         ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
1428     return ret;
1429 }
1430 
setCurrentPath(const QFileSystemEntry & entry)1431 bool QFileSystemEngine::setCurrentPath(const QFileSystemEntry &entry)
1432 {
1433     QFileSystemMetaData meta;
1434     fillMetaData(entry, meta, QFileSystemMetaData::ExistsAttribute | QFileSystemMetaData::DirectoryType);
1435     if(!(meta.exists() && meta.isDirectory()))
1436         return false;
1437 
1438     //TODO: this should really be using nativeFilePath(), but that returns a path in long format \\?\c:\foo
1439     //which causes many problems later on when it's returned through currentPath()
1440     return ::SetCurrentDirectory(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(entry.filePath()).utf16())) != 0;
1441 }
1442 
currentPath()1443 QFileSystemEntry QFileSystemEngine::currentPath()
1444 {
1445     QString ret;
1446     DWORD size = 0;
1447     wchar_t currentName[PATH_MAX];
1448     size = ::GetCurrentDirectory(PATH_MAX, currentName);
1449     if (size != 0) {
1450         if (size > PATH_MAX) {
1451             wchar_t *newCurrentName = new wchar_t[size];
1452             if (::GetCurrentDirectory(PATH_MAX, newCurrentName) != 0)
1453                 ret = QString::fromWCharArray(newCurrentName, size);
1454             delete [] newCurrentName;
1455         } else {
1456             ret = QString::fromWCharArray(currentName, size);
1457         }
1458     }
1459     if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
1460         ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
1461     return QFileSystemEntry(ret, QFileSystemEntry::FromNativePath());
1462 }
1463 
1464 //static
createLink(const QFileSystemEntry & source,const QFileSystemEntry & target,QSystemError & error)1465 bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
1466 {
1467     Q_ASSERT(false);
1468     Q_UNUSED(source)
1469     Q_UNUSED(target)
1470     Q_UNUSED(error)
1471 
1472     return false; // TODO implement; - code needs to be moved from qfsfileengine_win.cpp
1473 }
1474 
1475 //static
copyFile(const QFileSystemEntry & source,const QFileSystemEntry & target,QSystemError & error)1476 bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
1477 {
1478 #ifndef Q_OS_WINRT
1479     bool ret = ::CopyFile((wchar_t*)source.nativeFilePath().utf16(),
1480                           (wchar_t*)target.nativeFilePath().utf16(), true) != 0;
1481 #else // !Q_OS_WINRT
1482     COPYFILE2_EXTENDED_PARAMETERS copyParams = {
1483         sizeof(copyParams), COPY_FILE_FAIL_IF_EXISTS, NULL, NULL, NULL
1484     };
1485     HRESULT hres = ::CopyFile2((const wchar_t*)source.nativeFilePath().utf16(),
1486                            (const wchar_t*)target.nativeFilePath().utf16(), &copyParams);
1487     bool ret = SUCCEEDED(hres);
1488 #endif // Q_OS_WINRT
1489     if(!ret)
1490         error = QSystemError(::GetLastError(), QSystemError::NativeError);
1491     return ret;
1492 }
1493 
1494 //static
renameFile(const QFileSystemEntry & source,const QFileSystemEntry & target,QSystemError & error)1495 bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
1496 {
1497     Q_CHECK_FILE_NAME(source, false);
1498     Q_CHECK_FILE_NAME(target, false);
1499 
1500 #ifndef Q_OS_WINRT
1501     bool ret = ::MoveFile((wchar_t*)source.nativeFilePath().utf16(),
1502                           (wchar_t*)target.nativeFilePath().utf16()) != 0;
1503 #else // !Q_OS_WINRT
1504     bool ret = ::MoveFileEx((const wchar_t*)source.nativeFilePath().utf16(),
1505                             (const wchar_t*)target.nativeFilePath().utf16(), 0) != 0;
1506 #endif // Q_OS_WINRT
1507     if(!ret)
1508         error = QSystemError(::GetLastError(), QSystemError::NativeError);
1509     return ret;
1510 }
1511 
1512 //static
renameOverwriteFile(const QFileSystemEntry & source,const QFileSystemEntry & target,QSystemError & error)1513 bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
1514 {
1515     Q_CHECK_FILE_NAME(source, false);
1516     Q_CHECK_FILE_NAME(target, false);
1517 
1518     bool ret = ::MoveFileEx(reinterpret_cast<const wchar_t *>(source.nativeFilePath().utf16()),
1519                             reinterpret_cast<const wchar_t *>(target.nativeFilePath().utf16()),
1520                             MOVEFILE_REPLACE_EXISTING) != 0;
1521     if (!ret)
1522         error = QSystemError(::GetLastError(), QSystemError::NativeError);
1523     return ret;
1524 }
1525 
1526 //static
removeFile(const QFileSystemEntry & entry,QSystemError & error)1527 bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
1528 {
1529     Q_CHECK_FILE_NAME(entry, false);
1530 
1531     bool ret = ::DeleteFile((wchar_t*)entry.nativeFilePath().utf16()) != 0;
1532     if(!ret)
1533         error = QSystemError(::GetLastError(), QSystemError::NativeError);
1534     return ret;
1535 }
1536 
1537 /*
1538     If possible, we use the IFileOperation implementation, which allows us to determine
1539     the location of the object in the trash.
1540     If not (likely on mingw), we fall back to the old API, which won't allow us to know
1541     that.
1542 */
1543 //static
moveFileToTrash(const QFileSystemEntry & source,QFileSystemEntry & newLocation,QSystemError & error)1544 bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
1545                                         QFileSystemEntry &newLocation, QSystemError &error)
1546 {
1547 #ifndef Q_OS_WINRT
1548     // we need the "display name" of the file, so can't use nativeAbsoluteFilePath
1549     const QString sourcePath = QDir::toNativeSeparators(absoluteName(source).filePath());
1550 
1551     /*
1552         Windows 7 insists on showing confirmation dialogs and ignores the respective
1553         flags set on IFileOperation. Fall back to SHFileOperation, even if it doesn't
1554         give us the new location of the file.
1555     */
1556     if (QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7) {
1557 #  if defined(__IFileOperation_INTERFACE_DEFINED__)
1558         CoInitialize(NULL);
1559         IFileOperation *pfo = nullptr;
1560         IShellItem *deleteItem = nullptr;
1561         FileOperationProgressSink *sink = nullptr;
1562         HRESULT hres = E_FAIL;
1563 
1564         auto coUninitialize = qScopeGuard([&](){
1565             if (sink)
1566                 sink->Release();
1567             if (deleteItem)
1568                 deleteItem->Release();
1569             if (pfo)
1570                 pfo->Release();
1571             CoUninitialize();
1572             if (!SUCCEEDED(hres))
1573                 error = QSystemError(hres, QSystemError::NativeError);
1574         });
1575 
1576         hres = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pfo));
1577         if (!pfo)
1578             return false;
1579         pfo->SetOperationFlags(FOF_ALLOWUNDO | FOFX_RECYCLEONDELETE | FOF_NOCONFIRMATION
1580                             | FOF_SILENT | FOF_NOERRORUI);
1581         hres = SHCreateItemFromParsingName(reinterpret_cast<const wchar_t*>(sourcePath.utf16()),
1582                                         nullptr, IID_PPV_ARGS(&deleteItem));
1583         if (!deleteItem)
1584             return false;
1585         sink = new FileOperationProgressSink;
1586         hres = pfo->DeleteItem(deleteItem, static_cast<IFileOperationProgressSink*>(sink));
1587         if (!SUCCEEDED(hres))
1588             return false;
1589         hres = pfo->PerformOperations();
1590         if (!SUCCEEDED(hres))
1591             return false;
1592         newLocation = QFileSystemEntry(sink->targetPath);
1593 
1594 #  endif // no IFileOperation in SDK (mingw, likely) - fall back to SHFileOperation
1595     } else {
1596         // double null termination needed, so can't use QString::utf16
1597         QVarLengthArray<wchar_t, MAX_PATH + 1> winFile(sourcePath.length() + 2);
1598         sourcePath.toWCharArray(winFile.data());
1599         winFile[sourcePath.length()] = wchar_t{};
1600         winFile[sourcePath.length() + 1] = wchar_t{};
1601 
1602         SHFILEOPSTRUCTW operation;
1603         operation.hwnd = nullptr;
1604         operation.wFunc = FO_DELETE;
1605         operation.pFrom = winFile.constData();
1606         operation.pTo = nullptr;
1607         operation.fFlags = FOF_ALLOWUNDO | FOF_NO_UI;
1608         operation.fAnyOperationsAborted = FALSE;
1609         operation.hNameMappings = nullptr;
1610         operation.lpszProgressTitle = nullptr;
1611 
1612         int result = SHFileOperation(&operation);
1613         if (result != 0) {
1614             error = QSystemError(result, QSystemError::NativeError);
1615             return false;
1616         }
1617         /*
1618             This implementation doesn't let us know where the file ended up, even if
1619             we would specify FOF_WANTMAPPINGHANDLE | FOF_RENAMEONCOLLISION, as
1620             FOF_RENAMEONCOLLISION has no effect unless files are moved, copied, or renamed.
1621         */
1622         Q_UNUSED(newLocation);
1623     }
1624     return true;
1625 
1626 #else // Q_OS_WINRT
1627     Q_UNUSED(source);
1628     Q_UNUSED(newLocation);
1629     Q_UNUSED(error);
1630     return false;
1631 #endif
1632 }
1633 
1634 //static
setPermissions(const QFileSystemEntry & entry,QFile::Permissions permissions,QSystemError & error,QFileSystemMetaData * data)1635 bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error,
1636                                        QFileSystemMetaData *data)
1637 {
1638     Q_CHECK_FILE_NAME(entry, false);
1639 
1640     Q_UNUSED(data);
1641     int mode = 0;
1642 
1643     if (permissions & (QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther))
1644         mode |= _S_IREAD;
1645     if (permissions & (QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther))
1646         mode |= _S_IWRITE;
1647 
1648     if (mode == 0) // not supported
1649         return false;
1650 
1651     bool ret = ::_wchmod(reinterpret_cast<const wchar_t*>(entry.nativeFilePath().utf16()), mode) == 0;
1652     if(!ret)
1653         error = QSystemError(errno, QSystemError::StandardLibraryError);
1654     return ret;
1655 }
1656 
fileTimeToQDateTime(const FILETIME * time)1657 static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
1658 {
1659     if (time->dwHighDateTime == 0 && time->dwLowDateTime == 0)
1660         return QDateTime();
1661 
1662     SYSTEMTIME sTime;
1663     FileTimeToSystemTime(time, &sTime);
1664     return QDateTime(QDate(sTime.wYear, sTime.wMonth, sTime.wDay),
1665                      QTime(sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds),
1666                      Qt::UTC);
1667 }
1668 
birthTime() const1669 QDateTime QFileSystemMetaData::birthTime() const
1670 {
1671     return fileTimeToQDateTime(&birthTime_);
1672 }
metadataChangeTime() const1673 QDateTime QFileSystemMetaData::metadataChangeTime() const
1674 {
1675     return fileTimeToQDateTime(&changeTime_);
1676 }
modificationTime() const1677 QDateTime QFileSystemMetaData::modificationTime() const
1678 {
1679     return fileTimeToQDateTime(&lastWriteTime_);
1680 }
accessTime() const1681 QDateTime QFileSystemMetaData::accessTime() const
1682 {
1683     return fileTimeToQDateTime(&lastAccessTime_);
1684 }
1685 
1686 QT_END_NAMESPACE
1687