Warning: That file was not part of the compilation database. It may have many parsing errors.

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

Warning: That file was not part of the compilation database. It may have many parsing errors.