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 "qplatformdefs.h" |
41 | #include "private/qabstractfileengine_p.h" |
42 | #include "private/qfsfileengine_p.h" |
43 | #include "qfilesystemengine_p.h" |
44 | #include <qdebug.h> |
45 | |
46 | #include "qfile.h" |
47 | #include "qdir.h" |
48 | #include "qvarlengtharray.h" |
49 | #include "qdatetime.h" |
50 | #include "qt_windows.h" |
51 | |
52 | #include <sys/types.h> |
53 | #include <direct.h> |
54 | #include <winioctl.h> |
55 | #include <objbase.h> |
56 | #ifndef Q_OS_WINRT |
57 | # include <shlobj.h> |
58 | # include <accctrl.h> |
59 | #endif |
60 | #include <initguid.h> |
61 | #include <ctype.h> |
62 | #include <limits.h> |
63 | #include <stdio.h> |
64 | #ifndef Q_OS_WINRT |
65 | # define SECURITY_WIN32 |
66 | # include <security.h> |
67 | #endif |
68 | |
69 | #ifndef PATH_MAX |
70 | #define PATH_MAX FILENAME_MAX |
71 | #endif |
72 | |
73 | QT_BEGIN_NAMESPACE |
74 | |
75 | static inline bool isUncPath(const QString &path) |
76 | { |
77 | // Starts with \\, but not \\. |
78 | return (path.startsWith(QLatin1String("\\\\")) |
79 | && path.size() > 2 && path.at(2) != QLatin1Char('.')); |
80 | } |
81 | |
82 | /*! |
83 | \internal |
84 | */ |
85 | QString QFSFileEnginePrivate::longFileName(const QString &path) |
86 | { |
87 | if (path.startsWith(QLatin1String("\\\\.\\"))) |
88 | return path; |
89 | |
90 | QString absPath = QFileSystemEngine::nativeAbsoluteFilePath(path); |
91 | #if !defined(Q_OS_WINRT) |
92 | QString prefix = QLatin1String("\\\\?\\"); |
93 | if (isUncPath(absPath)) { |
94 | prefix.append(QLatin1String("UNC\\")); // "\\\\?\\UNC\\" |
95 | absPath.remove(0, 2); |
96 | } |
97 | return prefix + absPath; |
98 | #else |
99 | return absPath; |
100 | #endif |
101 | } |
102 | |
103 | /* |
104 | \internal |
105 | */ |
106 | bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) |
107 | { |
108 | Q_Q(QFSFileEngine); |
109 | |
110 | // All files are opened in share mode (both read and write). |
111 | DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; |
112 | |
113 | int accessRights = 0; |
114 | if (openMode & QIODevice::ReadOnly) |
115 | accessRights |= GENERIC_READ; |
116 | if (openMode & QIODevice::WriteOnly) |
117 | accessRights |= GENERIC_WRITE; |
118 | |
119 | // WriteOnly can create files, ReadOnly cannot. |
120 | DWORD creationDisp = (openMode & QIODevice::NewOnly) |
121 | ? CREATE_NEW |
122 | : openModeCanCreate(openMode) |
123 | ? OPEN_ALWAYS |
124 | : OPEN_EXISTING; |
125 | // Create the file handle. |
126 | #ifndef Q_OS_WINRT |
127 | SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE }; |
128 | fileHandle = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(), |
129 | accessRights, |
130 | shareMode, |
131 | &securityAtts, |
132 | creationDisp, |
133 | FILE_ATTRIBUTE_NORMAL, |
134 | NULL); |
135 | #else // !Q_OS_WINRT |
136 | fileHandle = CreateFile2((const wchar_t*)fileEntry.nativeFilePath().utf16(), |
137 | accessRights, |
138 | shareMode, |
139 | creationDisp, |
140 | NULL); |
141 | #endif // Q_OS_WINRT |
142 | |
143 | // Bail out on error. |
144 | if (fileHandle == INVALID_HANDLE_VALUE) { |
145 | q->setError(QFile::OpenError, qt_error_string()); |
146 | return false; |
147 | } |
148 | |
149 | // Truncate the file after successfully opening it if Truncate is passed. |
150 | if (openMode & QIODevice::Truncate) |
151 | q->setSize(0); |
152 | |
153 | return true; |
154 | } |
155 | |
156 | /* |
157 | \internal |
158 | */ |
159 | bool QFSFileEnginePrivate::nativeClose() |
160 | { |
161 | Q_Q(QFSFileEngine); |
162 | if (fh || fd != -1) { |
163 | // stdlib / stdio mode. |
164 | return closeFdFh(); |
165 | } |
166 | |
167 | // Windows native mode. |
168 | bool ok = true; |
169 | |
170 | if (cachedFd != -1) { |
171 | if (::_close(cachedFd) && !::CloseHandle(fileHandle)) { |
172 | q->setError(QFile::UnspecifiedError, qt_error_string()); |
173 | ok = false; |
174 | } |
175 | |
176 | // System handle is closed with associated file descriptor. |
177 | fileHandle = INVALID_HANDLE_VALUE; |
178 | cachedFd = -1; |
179 | |
180 | return ok; |
181 | } |
182 | |
183 | if ((fileHandle == INVALID_HANDLE_VALUE || !::CloseHandle(fileHandle))) { |
184 | q->setError(QFile::UnspecifiedError, qt_error_string()); |
185 | ok = false; |
186 | } |
187 | fileHandle = INVALID_HANDLE_VALUE; |
188 | return ok; |
189 | } |
190 | |
191 | /* |
192 | \internal |
193 | */ |
194 | bool QFSFileEnginePrivate::nativeFlush() |
195 | { |
196 | if (fh) { |
197 | // Buffered stdlib mode. |
198 | return flushFh(); |
199 | } |
200 | if (fd != -1) { |
201 | // Unbuffered stdio mode; always succeeds (no buffer). |
202 | return true; |
203 | } |
204 | |
205 | // Windows native mode; flushing is unnecessary. |
206 | return true; |
207 | } |
208 | |
209 | /* |
210 | \internal |
211 | \since 5.1 |
212 | */ |
213 | bool QFSFileEnginePrivate::nativeSyncToDisk() |
214 | { |
215 | if (fh || fd != -1) { |
216 | // stdlib / stdio mode. No API available. |
217 | return false; |
218 | } |
219 | return FlushFileBuffers(fileHandle); |
220 | } |
221 | |
222 | /* |
223 | \internal |
224 | */ |
225 | qint64 QFSFileEnginePrivate::nativeSize() const |
226 | { |
227 | Q_Q(const QFSFileEngine); |
228 | QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q); |
229 | |
230 | // ### Don't flush; for buffered files, we should get away with ftell. |
231 | thatQ->flush(); |
232 | |
233 | // Always retrive the current information |
234 | metaData.clearFlags(QFileSystemMetaData::SizeAttribute); |
235 | bool filled = false; |
236 | if (fileHandle != INVALID_HANDLE_VALUE && openMode != QIODevice::NotOpen ) |
237 | filled = QFileSystemEngine::fillMetaData(fileHandle, metaData, |
238 | QFileSystemMetaData::SizeAttribute); |
239 | else |
240 | filled = doStat(QFileSystemMetaData::SizeAttribute); |
241 | |
242 | if (!filled) { |
243 | thatQ->setError(QFile::UnspecifiedError, QSystemError::stdString()); |
244 | return 0; |
245 | } |
246 | return metaData.size(); |
247 | } |
248 | |
249 | /* |
250 | \internal |
251 | */ |
252 | qint64 QFSFileEnginePrivate::nativePos() const |
253 | { |
254 | Q_Q(const QFSFileEngine); |
255 | QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q); |
256 | |
257 | if (fh || fd != -1) { |
258 | // stdlib / stido mode. |
259 | return posFdFh(); |
260 | } |
261 | |
262 | // Windows native mode. |
263 | if (fileHandle == INVALID_HANDLE_VALUE) |
264 | return 0; |
265 | |
266 | LARGE_INTEGER currentFilePos; |
267 | LARGE_INTEGER offset; |
268 | offset.QuadPart = 0; |
269 | if (!::SetFilePointerEx(fileHandle, offset, ¤tFilePos, FILE_CURRENT)) { |
270 | thatQ->setError(QFile::UnspecifiedError, qt_error_string()); |
271 | return 0; |
272 | } |
273 | |
274 | return qint64(currentFilePos.QuadPart); |
275 | } |
276 | |
277 | /* |
278 | \internal |
279 | */ |
280 | bool QFSFileEnginePrivate::nativeSeek(qint64 pos) |
281 | { |
282 | Q_Q(QFSFileEngine); |
283 | |
284 | if (fh || fd != -1) { |
285 | // stdlib / stdio mode. |
286 | return seekFdFh(pos); |
287 | } |
288 | |
289 | LARGE_INTEGER currentFilePos; |
290 | LARGE_INTEGER offset; |
291 | offset.QuadPart = pos; |
292 | if (!::SetFilePointerEx(fileHandle, offset, ¤tFilePos, FILE_BEGIN)) { |
293 | q->setError(QFile::UnspecifiedError, qt_error_string()); |
294 | return false; |
295 | } |
296 | |
297 | return true; |
298 | } |
299 | |
300 | /* |
301 | \internal |
302 | */ |
303 | qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 maxlen) |
304 | { |
305 | Q_Q(QFSFileEngine); |
306 | |
307 | if (fh || fd != -1) { |
308 | // stdio / stdlib mode. |
309 | if (fh && nativeIsSequential() && feof(fh)) { |
310 | q->setError(QFile::ReadError, QSystemError::stdString()); |
311 | return -1; |
312 | } |
313 | |
314 | return readFdFh(data, maxlen); |
315 | } |
316 | |
317 | // Windows native mode. |
318 | if (fileHandle == INVALID_HANDLE_VALUE) |
319 | return -1; |
320 | |
321 | qint64 bytesToRead = maxlen; |
322 | |
323 | // Reading on Windows fails with ERROR_NO_SYSTEM_RESOURCES when |
324 | // the chunks are too large, so we limit the block size to 32MB. |
325 | static const qint64 maxBlockSize = 32 * 1024 * 1024; |
326 | |
327 | qint64 totalRead = 0; |
328 | do { |
329 | DWORD blockSize = DWORD(qMin(bytesToRead, maxBlockSize)); |
330 | DWORD bytesRead; |
331 | if (!ReadFile(fileHandle, data + totalRead, blockSize, &bytesRead, NULL)) { |
332 | if (totalRead == 0) { |
333 | // Note: only return failure if the first ReadFile fails. |
334 | q->setError(QFile::ReadError, qt_error_string()); |
335 | return -1; |
336 | } |
337 | break; |
338 | } |
339 | if (bytesRead == 0) |
340 | break; |
341 | totalRead += bytesRead; |
342 | bytesToRead -= bytesRead; |
343 | } while (totalRead < maxlen); |
344 | return totalRead; |
345 | } |
346 | |
347 | /* |
348 | \internal |
349 | */ |
350 | qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen) |
351 | { |
352 | Q_Q(QFSFileEngine); |
353 | |
354 | if (fh || fd != -1) { |
355 | // stdio / stdlib mode. |
356 | return readLineFdFh(data, maxlen); |
357 | } |
358 | |
359 | // Windows native mode. |
360 | if (fileHandle == INVALID_HANDLE_VALUE) |
361 | return -1; |
362 | |
363 | // ### No equivalent in Win32? |
364 | return q->QAbstractFileEngine::readLine(data, maxlen); |
365 | } |
366 | |
367 | /* |
368 | \internal |
369 | */ |
370 | qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len) |
371 | { |
372 | Q_Q(QFSFileEngine); |
373 | |
374 | if (fh || fd != -1) { |
375 | // stdio / stdlib mode. |
376 | return writeFdFh(data, len); |
377 | } |
378 | |
379 | // Windows native mode. |
380 | if (fileHandle == INVALID_HANDLE_VALUE) |
381 | return -1; |
382 | |
383 | qint64 bytesToWrite = len; |
384 | |
385 | // Writing on Windows fails with ERROR_NO_SYSTEM_RESOURCES when |
386 | // the chunks are too large, so we limit the block size to 32MB. |
387 | qint64 totalWritten = 0; |
388 | do { |
389 | const DWORD currentBlockSize = DWORD(qMin(bytesToWrite, qint64(32 * 1024 * 1024))); |
390 | DWORD bytesWritten; |
391 | if (!WriteFile(fileHandle, data + totalWritten, currentBlockSize, &bytesWritten, NULL)) { |
392 | if (totalWritten == 0) { |
393 | // Note: Only return error if the first WriteFile failed. |
394 | q->setError(QFile::WriteError, qt_error_string()); |
395 | return -1; |
396 | } |
397 | break; |
398 | } |
399 | if (bytesWritten == 0) |
400 | break; |
401 | totalWritten += bytesWritten; |
402 | bytesToWrite -= bytesWritten; |
403 | } while (totalWritten < len); |
404 | return qint64(totalWritten); |
405 | } |
406 | |
407 | /* |
408 | \internal |
409 | */ |
410 | int QFSFileEnginePrivate::nativeHandle() const |
411 | { |
412 | if (fh || fd != -1) |
413 | return fh ? QT_FILENO(fh) : fd; |
414 | if (cachedFd != -1) |
415 | return cachedFd; |
416 | |
417 | int flags = 0; |
418 | if (openMode & QIODevice::Append) |
419 | flags |= _O_APPEND; |
420 | if (!(openMode & QIODevice::WriteOnly)) |
421 | flags |= _O_RDONLY; |
422 | cachedFd = _open_osfhandle((intptr_t) fileHandle, flags); |
423 | return cachedFd; |
424 | } |
425 | |
426 | /* |
427 | \internal |
428 | */ |
429 | bool QFSFileEnginePrivate::nativeIsSequential() const |
430 | { |
431 | #if !defined(Q_OS_WINRT) |
432 | HANDLE handle = fileHandle; |
433 | if (fh || fd != -1) |
434 | handle = (HANDLE)_get_osfhandle(fh ? QT_FILENO(fh) : fd); |
435 | if (handle == INVALID_HANDLE_VALUE) |
436 | return false; |
437 | |
438 | DWORD fileType = GetFileType(handle); |
439 | return (fileType == FILE_TYPE_CHAR) |
440 | || (fileType == FILE_TYPE_PIPE); |
441 | #else |
442 | return false; |
443 | #endif |
444 | } |
445 | |
446 | bool QFSFileEngine::remove() |
447 | { |
448 | Q_D(QFSFileEngine); |
449 | QSystemError error; |
450 | bool ret = QFileSystemEngine::removeFile(d->fileEntry, error); |
451 | if (!ret) |
452 | setError(QFile::RemoveError, error.toString()); |
453 | return ret; |
454 | } |
455 | |
456 | bool QFSFileEngine::copy(const QString ©Name) |
457 | { |
458 | Q_D(QFSFileEngine); |
459 | QSystemError error; |
460 | bool ret = QFileSystemEngine::copyFile(d->fileEntry, QFileSystemEntry(copyName), error); |
461 | if (!ret) |
462 | setError(QFile::CopyError, error.toString()); |
463 | return ret; |
464 | } |
465 | |
466 | bool QFSFileEngine::rename(const QString &newName) |
467 | { |
468 | Q_D(QFSFileEngine); |
469 | QSystemError error; |
470 | bool ret = QFileSystemEngine::renameFile(d->fileEntry, QFileSystemEntry(newName), error); |
471 | if (!ret) |
472 | setError(QFile::RenameError, error.toString()); |
473 | return ret; |
474 | } |
475 | |
476 | bool QFSFileEngine::renameOverwrite(const QString &newName) |
477 | { |
478 | Q_D(QFSFileEngine); |
479 | QSystemError error; |
480 | bool ret = QFileSystemEngine::renameOverwriteFile(d->fileEntry, QFileSystemEntry(newName), error); |
481 | if (!ret) |
482 | setError(QFile::RenameError, error.toString()); |
483 | return ret; |
484 | } |
485 | |
486 | bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const |
487 | { |
488 | return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories); |
489 | } |
490 | |
491 | bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const |
492 | { |
493 | return QFileSystemEngine::removeDirectory(QFileSystemEntry(name), recurseParentDirectories); |
494 | } |
495 | |
496 | bool QFSFileEngine::caseSensitive() const |
497 | { |
498 | return false; |
499 | } |
500 | |
501 | bool QFSFileEngine::setCurrentPath(const QString &path) |
502 | { |
503 | return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path)); |
504 | } |
505 | |
506 | QString QFSFileEngine::currentPath(const QString &fileName) |
507 | { |
508 | #if !defined(Q_OS_WINRT) |
509 | QString ret; |
510 | //if filename is a drive: then get the pwd of that drive |
511 | if (fileName.length() >= 2 && |
512 | fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':')) { |
513 | int drv = fileName.toUpper().at(0).toLatin1() - 'A' + 1; |
514 | if (_getdrive() != drv) { |
515 | wchar_t buf[PATH_MAX]; |
516 | ::_wgetdcwd(drv, buf, PATH_MAX); |
517 | ret = QString::fromWCharArray(buf); |
518 | } |
519 | } |
520 | if (ret.isEmpty()) { |
521 | //just the pwd |
522 | ret = QFileSystemEngine::currentPath().filePath(); |
523 | } |
524 | if (ret.length() >= 2 && ret[1] == QLatin1Char(':')) |
525 | ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters. |
526 | return ret; |
527 | #else // !Q_OS_WINRT |
528 | Q_UNUSED(fileName); |
529 | return QFileSystemEngine::currentPath().filePath(); |
530 | #endif // Q_OS_WINRT |
531 | } |
532 | |
533 | QString QFSFileEngine::homePath() |
534 | { |
535 | return QFileSystemEngine::homePath(); |
536 | } |
537 | |
538 | QString QFSFileEngine::rootPath() |
539 | { |
540 | return QFileSystemEngine::rootPath(); |
541 | } |
542 | |
543 | QString QFSFileEngine::tempPath() |
544 | { |
545 | return QFileSystemEngine::tempPath(); |
546 | } |
547 | |
548 | #if !defined(Q_OS_WINRT) |
549 | // cf QStorageInfo::isReady |
550 | static inline bool isDriveReady(const wchar_t *path) |
551 | { |
552 | DWORD fileSystemFlags; |
553 | const UINT driveType = GetDriveType(path); |
554 | return (driveType != DRIVE_REMOVABLE && driveType != DRIVE_CDROM) |
555 | || GetVolumeInformation(path, nullptr, 0, nullptr, nullptr, |
556 | &fileSystemFlags, nullptr, 0) == TRUE; |
557 | } |
558 | #endif // !Q_OS_WINRT |
559 | |
560 | QFileInfoList QFSFileEngine::drives() |
561 | { |
562 | QFileInfoList ret; |
563 | #if !defined(Q_OS_WINRT) |
564 | const UINT oldErrorMode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); |
565 | quint32 driveBits = (quint32) GetLogicalDrives() & 0x3ffffff; |
566 | wchar_t driveName[] = L"A:\\"; |
567 | |
568 | while (driveBits) { |
569 | if ((driveBits & 1) && isDriveReady(driveName)) |
570 | ret.append(QFileInfo(QString::fromWCharArray(driveName))); |
571 | driveName[0]++; |
572 | driveBits = driveBits >> 1; |
573 | } |
574 | ::SetErrorMode(oldErrorMode); |
575 | return ret; |
576 | #else // !Q_OS_WINRT |
577 | ret.append(QFileInfo(QLatin1String("/"))); |
578 | return ret; |
579 | #endif // Q_OS_WINRT |
580 | } |
581 | |
582 | bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const |
583 | { |
584 | if (!tried_stat || !metaData.hasFlags(flags)) { |
585 | tried_stat = true; |
586 | |
587 | int localFd = fd; |
588 | if (fh && fileEntry.isEmpty()) |
589 | localFd = QT_FILENO(fh); |
590 | if (localFd != -1) |
591 | QFileSystemEngine::fillMetaData(localFd, metaData, flags); |
592 | if (metaData.missingFlags(flags) && !fileEntry.isEmpty()) |
593 | QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags)); |
594 | } |
595 | |
596 | return metaData.exists(); |
597 | } |
598 | |
599 | |
600 | bool QFSFileEngine::link(const QString &newName) |
601 | { |
602 | #if !defined(Q_OS_WINRT) |
603 | bool ret = false; |
604 | |
605 | QString linkName = newName; |
606 | //### assume that they add .lnk |
607 | |
608 | IShellLink *psl; |
609 | bool neededCoInit = false; |
610 | |
611 | HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, |
612 | reinterpret_cast<void **>(&psl)); |
613 | |
614 | if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized |
615 | neededCoInit = true; |
616 | CoInitialize(nullptr); |
617 | hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, |
618 | reinterpret_cast<void **>(&psl)); |
619 | } |
620 | |
621 | if (SUCCEEDED(hres)) { |
622 | const QString nativeAbsoluteName = fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\')); |
623 | hres = psl->SetPath(reinterpret_cast<const wchar_t *>(nativeAbsoluteName.utf16())); |
624 | if (SUCCEEDED(hres)) { |
625 | const QString nativeAbsolutePathName = fileName(AbsolutePathName).replace(QLatin1Char('/'), QLatin1Char('\\')); |
626 | hres = psl->SetWorkingDirectory(reinterpret_cast<const wchar_t *>(nativeAbsolutePathName.utf16())); |
627 | if (SUCCEEDED(hres)) { |
628 | IPersistFile *ppf; |
629 | hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void **>(&ppf)); |
630 | if (SUCCEEDED(hres)) { |
631 | hres = ppf->Save(reinterpret_cast<const wchar_t *>(linkName.utf16()), TRUE); |
632 | if (SUCCEEDED(hres)) |
633 | ret = true; |
634 | ppf->Release(); |
635 | } |
636 | } |
637 | } |
638 | psl->Release(); |
639 | } |
640 | if (!ret) |
641 | setError(QFile::RenameError, qt_error_string()); |
642 | |
643 | if (neededCoInit) |
644 | CoUninitialize(); |
645 | |
646 | return ret; |
647 | #else // !Q_OS_WINRT |
648 | Q_UNUSED(newName); |
649 | Q_UNIMPLEMENTED(); |
650 | return false; |
651 | #endif // Q_OS_WINRT |
652 | } |
653 | |
654 | /*! |
655 | \reimp |
656 | */ |
657 | QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const |
658 | { |
659 | Q_D(const QFSFileEngine); |
660 | |
661 | if (type & Refresh) |
662 | d->metaData.clear(); |
663 | |
664 | QAbstractFileEngine::FileFlags ret = 0; |
665 | |
666 | if (type & FlagsMask) |
667 | ret |= LocalDiskFlag; |
668 | |
669 | bool exists; |
670 | { |
671 | QFileSystemMetaData::MetaDataFlags queryFlags = 0; |
672 | |
673 | queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type)) |
674 | & QFileSystemMetaData::Permissions; |
675 | |
676 | // AliasType and BundleType are 0x0 |
677 | if (type & TypesMask) |
678 | queryFlags |= QFileSystemMetaData::AliasType |
679 | | QFileSystemMetaData::LinkType |
680 | | QFileSystemMetaData::FileType |
681 | | QFileSystemMetaData::DirectoryType |
682 | | QFileSystemMetaData::BundleType; |
683 | |
684 | if (type & FlagsMask) |
685 | queryFlags |= QFileSystemMetaData::HiddenAttribute |
686 | | QFileSystemMetaData::ExistsAttribute; |
687 | |
688 | queryFlags |= QFileSystemMetaData::LinkType; |
689 | |
690 | exists = d->doStat(queryFlags); |
691 | } |
692 | |
693 | if (exists && (type & PermsMask)) |
694 | ret |= FileFlags(uint(d->metaData.permissions())); |
695 | |
696 | if (type & TypesMask) { |
697 | if ((type & LinkType) && d->metaData.isLegacyLink()) |
698 | ret |= LinkType; |
699 | if (d->metaData.isDirectory()) { |
700 | ret |= DirectoryType; |
701 | } else { |
702 | ret |= FileType; |
703 | } |
704 | } |
705 | if (type & FlagsMask) { |
706 | if (d->metaData.exists()) { |
707 | // if we succeeded in querying, then the file exists: a file on |
708 | // Windows cannot be deleted if we have an open handle to it |
709 | ret |= ExistsFlag; |
710 | if (d->fileEntry.isRoot()) |
711 | ret |= RootFlag; |
712 | else if (d->metaData.isHidden()) |
713 | ret |= HiddenFlag; |
714 | } |
715 | } |
716 | return ret; |
717 | } |
718 | |
719 | QByteArray QFSFileEngine::id() const |
720 | { |
721 | Q_D(const QFSFileEngine); |
722 | HANDLE h = d->fileHandle; |
723 | if (h == INVALID_HANDLE_VALUE) { |
724 | int localFd = d->fd; |
725 | if (d->fh && d->fileEntry.isEmpty()) |
726 | localFd = QT_FILENO(d->fh); |
727 | if (localFd != -1) |
728 | h = HANDLE(_get_osfhandle(localFd)); |
729 | } |
730 | if (h != INVALID_HANDLE_VALUE) |
731 | return QFileSystemEngine::id(h); |
732 | |
733 | // file is not open, try by path |
734 | return QFileSystemEngine::id(d->fileEntry); |
735 | } |
736 | |
737 | QString QFSFileEngine::fileName(FileName file) const |
738 | { |
739 | Q_D(const QFSFileEngine); |
740 | if (file == BaseName) { |
741 | return d->fileEntry.fileName(); |
742 | } else if (file == PathName) { |
743 | return d->fileEntry.path(); |
744 | } else if (file == AbsoluteName || file == AbsolutePathName) { |
745 | QString ret; |
746 | |
747 | if (!isRelativePath()) { |
748 | if (d->fileEntry.filePath().startsWith(QLatin1Char('/')) || // It's a absolute path to the current drive, so \a.txt -> Z:\a.txt |
749 | d->fileEntry.filePath().size() == 2 || // It's a drive letter that needs to get a working dir appended |
750 | (d->fileEntry.filePath().size() > 2 && d->fileEntry.filePath().at(2) != QLatin1Char('/')) || // It's a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt |
751 | d->fileEntry.filePath().contains(QLatin1String("/../")) || d->fileEntry.filePath().contains(QLatin1String( "/./")) || |
752 | d->fileEntry.filePath().endsWith(QLatin1String("/..")) || d->fileEntry.filePath().endsWith(QLatin1String( "/."))) |
753 | { |
754 | ret = QDir::fromNativeSeparators(QFileSystemEngine::nativeAbsoluteFilePath(d->fileEntry.filePath())); |
755 | } else { |
756 | ret = d->fileEntry.filePath(); |
757 | } |
758 | } else { |
759 | ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + d->fileEntry.filePath()); |
760 | } |
761 | |
762 | // The path should be absolute at this point. |
763 | // From the docs : |
764 | // Absolute paths begin with the directory separator "/" |
765 | // (optionally preceded by a drive specification under Windows). |
766 | if (ret.at(0) != QLatin1Char('/')) { |
767 | Q_ASSERT(ret.length() >= 2); |
768 | Q_ASSERT(ret.at(0).isLetter()); |
769 | Q_ASSERT(ret.at(1) == QLatin1Char(':')); |
770 | |
771 | // Force uppercase drive letters. |
772 | ret[0] = ret.at(0).toUpper(); |
773 | } |
774 | |
775 | if (file == AbsolutePathName) { |
776 | int slash = ret.lastIndexOf(QLatin1Char('/')); |
777 | if (slash < 0) |
778 | return ret; |
779 | if (ret.at(0) != QLatin1Char('/') && slash == 2) |
780 | return ret.left(3); // include the slash |
781 | return ret.left(slash > 0 ? slash : 1); |
782 | } |
783 | return ret; |
784 | } else if (file == CanonicalName || file == CanonicalPathName) { |
785 | if (!(fileFlags(ExistsFlag) & ExistsFlag)) |
786 | return QString(); |
787 | QFileSystemEntry entry(QFileSystemEngine::canonicalName(QFileSystemEntry(fileName(AbsoluteName)), d->metaData)); |
788 | |
789 | if (file == CanonicalPathName) |
790 | return entry.path(); |
791 | return entry.filePath(); |
792 | } else if (file == LinkName) { |
793 | return QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData).filePath(); |
794 | } else if (file == BundleName) { |
795 | return QString(); |
796 | } |
797 | return d->fileEntry.filePath(); |
798 | } |
799 | |
800 | bool QFSFileEngine::isRelativePath() const |
801 | { |
802 | Q_D(const QFSFileEngine); |
803 | // drive, e.g. "a:", or UNC root, e.q. "//" |
804 | return d->fileEntry.isRelative(); |
805 | } |
806 | |
807 | uint QFSFileEngine::ownerId(FileOwner /*own*/) const |
808 | { |
809 | static const uint nobodyID = (uint) -2; |
810 | return nobodyID; |
811 | } |
812 | |
813 | QString QFSFileEngine::owner(FileOwner own) const |
814 | { |
815 | Q_D(const QFSFileEngine); |
816 | return QFileSystemEngine::owner(d->fileEntry, own); |
817 | } |
818 | |
819 | bool QFSFileEngine::setPermissions(uint perms) |
820 | { |
821 | Q_D(QFSFileEngine); |
822 | QSystemError error; |
823 | bool ret = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error); |
824 | if (!ret) |
825 | setError(QFile::PermissionsError, error.toString()); |
826 | return ret; |
827 | } |
828 | |
829 | bool QFSFileEngine::setSize(qint64 size) |
830 | { |
831 | Q_D(QFSFileEngine); |
832 | |
833 | if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1 || d->fh) { |
834 | // resize open file |
835 | HANDLE fh = d->fileHandle; |
836 | if (fh == INVALID_HANDLE_VALUE) { |
837 | if (d->fh) |
838 | fh = (HANDLE)_get_osfhandle(QT_FILENO(d->fh)); |
839 | else |
840 | fh = (HANDLE)_get_osfhandle(d->fd); |
841 | } |
842 | if (fh == INVALID_HANDLE_VALUE) |
843 | return false; |
844 | qint64 currentPos = pos(); |
845 | |
846 | if (seek(size) && SetEndOfFile(fh)) { |
847 | seek(qMin(currentPos, size)); |
848 | return true; |
849 | } |
850 | |
851 | seek(currentPos); |
852 | return false; |
853 | } |
854 | |
855 | if (!d->fileEntry.isEmpty()) { |
856 | // resize file on disk |
857 | QFile file(d->fileEntry.filePath()); |
858 | if (file.open(QFile::ReadWrite)) { |
859 | bool ret = file.resize(size); |
860 | if (!ret) |
861 | setError(QFile::ResizeError, file.errorString()); |
862 | return ret; |
863 | } |
864 | } |
865 | return false; |
866 | } |
867 | |
868 | bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time) |
869 | { |
870 | Q_D(QFSFileEngine); |
871 | |
872 | if (d->openMode == QFile::NotOpen) { |
873 | setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); |
874 | return false; |
875 | } |
876 | |
877 | if (!newDate.isValid() || time == QAbstractFileEngine::MetadataChangeTime) { |
878 | setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER)); |
879 | return false; |
880 | } |
881 | |
882 | HANDLE handle = d->fileHandle; |
883 | if (handle == INVALID_HANDLE_VALUE) { |
884 | if (d->fh) |
885 | handle = reinterpret_cast<HANDLE>(::_get_osfhandle(QT_FILENO(d->fh))); |
886 | else if (d->fd != -1) |
887 | handle = reinterpret_cast<HANDLE>(::_get_osfhandle(d->fd)); |
888 | } |
889 | |
890 | if (handle == INVALID_HANDLE_VALUE) { |
891 | setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); |
892 | return false; |
893 | } |
894 | |
895 | QSystemError error; |
896 | if (!QFileSystemEngine::setFileTime(handle, newDate, time, error)) { |
897 | setError(QFile::PermissionsError, error.toString()); |
898 | return false; |
899 | } |
900 | |
901 | d->metaData.clearFlags(QFileSystemMetaData::Times); |
902 | return true; |
903 | } |
904 | |
905 | uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, |
906 | QFile::MemoryMapFlags flags) |
907 | { |
908 | Q_Q(QFSFileEngine); |
909 | Q_UNUSED(flags); |
910 | if (openMode == QFile::NotOpen) { |
911 | q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); |
912 | return 0; |
913 | } |
914 | if (offset == 0 && size == 0) { |
915 | q->setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER)); |
916 | return 0; |
917 | } |
918 | |
919 | // check/setup args to map |
920 | DWORD access = 0; |
921 | if (flags & QFileDevice::MapPrivateOption) { |
922 | #ifdef FILE_MAP_COPY |
923 | access = FILE_MAP_COPY; |
924 | #else |
925 | q->setError(QFile::UnspecifiedError, "MapPrivateOption unsupported"); |
926 | return 0; |
927 | #endif |
928 | } else if (openMode & QIODevice::WriteOnly) { |
929 | access = FILE_MAP_WRITE; |
930 | } else if (openMode & QIODevice::ReadOnly) { |
931 | access = FILE_MAP_READ; |
932 | } |
933 | |
934 | if (mapHandle == NULL) { |
935 | // get handle to the file |
936 | HANDLE handle = fileHandle; |
937 | |
938 | if (handle == INVALID_HANDLE_VALUE && fh) |
939 | handle = (HANDLE)::_get_osfhandle(QT_FILENO(fh)); |
940 | |
941 | #ifdef Q_USE_DEPRECATED_MAP_API |
942 | nativeClose(); |
943 | // handle automatically closed by kernel with mapHandle (below). |
944 | handle = ::CreateFileForMapping((const wchar_t*)fileEntry.nativeFilePath().utf16(), |
945 | GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0), |
946 | 0, |
947 | NULL, |
948 | OPEN_EXISTING, |
949 | FILE_ATTRIBUTE_NORMAL, |
950 | NULL); |
951 | // Since this is a special case, we check if the return value was NULL and if so |
952 | // we change it to INVALID_HANDLE_VALUE to follow the logic inside this function. |
953 | if(0 == handle) |
954 | handle = INVALID_HANDLE_VALUE; |
955 | #endif |
956 | |
957 | if (handle == INVALID_HANDLE_VALUE) { |
958 | q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); |
959 | return 0; |
960 | } |
961 | |
962 | // first create the file mapping handle |
963 | DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY; |
964 | #ifndef Q_OS_WINRT |
965 | mapHandle = ::CreateFileMapping(handle, 0, protection, 0, 0, 0); |
966 | #else |
967 | mapHandle = ::CreateFileMappingFromApp(handle, 0, protection, 0, 0); |
968 | #endif |
969 | if (mapHandle == NULL) { |
970 | q->setError(QFile::PermissionsError, qt_error_string()); |
971 | #ifdef Q_USE_DEPRECATED_MAP_API |
972 | ::CloseHandle(handle); |
973 | #endif |
974 | return 0; |
975 | } |
976 | } |
977 | |
978 | DWORD offsetHi = offset >> 32; |
979 | DWORD offsetLo = offset & Q_UINT64_C(0xffffffff); |
980 | SYSTEM_INFO sysinfo; |
981 | #ifndef Q_OS_WINRT |
982 | ::GetSystemInfo(&sysinfo); |
983 | #else |
984 | ::GetNativeSystemInfo(&sysinfo); |
985 | #endif |
986 | DWORD mask = sysinfo.dwAllocationGranularity - 1; |
987 | DWORD extra = offset & mask; |
988 | if (extra) |
989 | offsetLo &= ~mask; |
990 | |
991 | // attempt to create the map |
992 | #ifndef Q_OS_WINRT |
993 | LPVOID mapAddress = ::MapViewOfFile(mapHandle, access, |
994 | offsetHi, offsetLo, size + extra); |
995 | #else |
996 | LPVOID mapAddress = ::MapViewOfFileFromApp(mapHandle, access, |
997 | (ULONG64(offsetHi) << 32) + offsetLo, size + extra); |
998 | #endif |
999 | if (mapAddress) { |
1000 | uchar *address = extra + static_cast<uchar*>(mapAddress); |
1001 | maps[address] = extra; |
1002 | return address; |
1003 | } |
1004 | |
1005 | switch(GetLastError()) { |
1006 | case ERROR_ACCESS_DENIED: |
1007 | q->setError(QFile::PermissionsError, qt_error_string()); |
1008 | break; |
1009 | case ERROR_INVALID_PARAMETER: |
1010 | // size are out of bounds |
1011 | default: |
1012 | q->setError(QFile::UnspecifiedError, qt_error_string()); |
1013 | } |
1014 | |
1015 | ::CloseHandle(mapHandle); |
1016 | mapHandle = NULL; |
1017 | return 0; |
1018 | } |
1019 | |
1020 | bool QFSFileEnginePrivate::unmap(uchar *ptr) |
1021 | { |
1022 | Q_Q(QFSFileEngine); |
1023 | if (!maps.contains(ptr)) { |
1024 | q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); |
1025 | return false; |
1026 | } |
1027 | uchar *start = ptr - maps[ptr]; |
1028 | if (!UnmapViewOfFile(start)) { |
1029 | q->setError(QFile::PermissionsError, qt_error_string()); |
1030 | return false; |
1031 | } |
1032 | |
1033 | maps.remove(ptr); |
1034 | if (maps.isEmpty()) { |
1035 | ::CloseHandle(mapHandle); |
1036 | mapHandle = NULL; |
1037 | } |
1038 | |
1039 | return true; |
1040 | } |
1041 | |
1042 | /*! |
1043 | \reimp |
1044 | */ |
1045 | bool QFSFileEngine::cloneTo(QAbstractFileEngine *target) |
1046 | { |
1047 | // There's some Windows Server 2016 API, but we won't |
1048 | // bother with it. |
1049 | Q_UNUSED(target); |
1050 | return false; |
1051 | } |
1052 | |
1053 | QT_END_NAMESPACE |
1054 |
Warning: That file was not part of the compilation database. It may have many parsing errors.