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(¤tUserTrusteeW, 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, ¤tUserImpersonatedToken);
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, ¶ms);
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, ¤tUserTrusteeW, &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(), ©Params);
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