1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qplatformdefs.h"
5#include "qfiledevice.h"
6#include "qfiledevice_p.h"
7#include "qfsfileengine_p.h"
8
9#ifdef QT_NO_QOBJECT
10#define tr(X) QString::fromLatin1(X)
11#endif
12
13QT_BEGIN_NAMESPACE
14
15#ifndef QFILE_WRITEBUFFER_SIZE
16#define QFILE_WRITEBUFFER_SIZE 16384
17#endif
18
19QFileDevicePrivate::QFileDevicePrivate()
20 : cachedSize(0),
21 error(QFile::NoError), lastWasWrite(false)
22{
23 writeBufferChunkSize = QFILE_WRITEBUFFER_SIZE;
24}
25
26QFileDevicePrivate::~QFileDevicePrivate() = default;
27
28QAbstractFileEngine *QFileDevicePrivate::engine() const
29{
30 if (!fileEngine)
31 fileEngine = std::make_unique<QFSFileEngine>();
32 return fileEngine.get();
33}
34
35void QFileDevicePrivate::setError(QFileDevice::FileError err)
36{
37 error = err;
38 errorString.clear();
39}
40
41void QFileDevicePrivate::setError(QFileDevice::FileError err, const QString &errStr)
42{
43 error = err;
44 errorString = errStr;
45}
46
47void QFileDevicePrivate::setError(QFileDevice::FileError err, int errNum)
48{
49 error = err;
50 errorString = qt_error_string(errorCode: errNum);
51}
52
53/*!
54 \enum QFileDevice::FileError
55
56 This enum describes the errors that may be returned by the error()
57 function.
58
59 \value NoError No error occurred.
60 \value ReadError An error occurred when reading from the file.
61 \value WriteError An error occurred when writing to the file.
62 \value FatalError A fatal error occurred.
63 \value ResourceError Out of resources (e.g., too many open files, out of memory, etc.)
64 \value OpenError The file could not be opened.
65 \value AbortError The operation was aborted.
66 \value TimeOutError A timeout occurred.
67 \value UnspecifiedError An unspecified error occurred.
68 \value RemoveError The file could not be removed.
69 \value RenameError The file could not be renamed.
70 \value PositionError The position in the file could not be changed.
71 \value ResizeError The file could not be resized.
72 \value PermissionsError The file could not be accessed.
73 \value CopyError The file could not be copied.
74*/
75
76/*!
77 \enum QFileDevice::Permission
78
79 This enum is used by the permission() function to report the
80 permissions and ownership of a file. The values may be OR-ed
81 together to test multiple permissions and ownership values.
82
83 \value ReadOwner The file is readable by the owner of the file.
84 \value WriteOwner The file is writable by the owner of the file.
85 \value ExeOwner The file is executable by the owner of the file.
86 \value ReadUser The file is readable by the user.
87 \value WriteUser The file is writable by the user.
88 \value ExeUser The file is executable by the user.
89 \value ReadGroup The file is readable by the group.
90 \value WriteGroup The file is writable by the group.
91 \value ExeGroup The file is executable by the group.
92 \value ReadOther The file is readable by others.
93 \value WriteOther The file is writable by others.
94 \value ExeOther The file is executable by others.
95
96 \warning Because of differences in the platforms supported by Qt,
97 the semantics of ReadUser, WriteUser and ExeUser are
98 platform-dependent: On Unix, the rights of the owner of the file
99 are returned and on Windows the rights of the current user are
100 returned. This behavior might change in a future Qt version.
101
102 \note On NTFS file systems, ownership and permissions checking is
103 disabled by default for performance reasons. To enable it,
104 include the following line:
105
106 \snippet ntfsp.cpp 0
107
108 Permission checking is then turned on and off by incrementing and
109 decrementing \c qt_ntfs_permission_lookup by 1.
110
111 \snippet ntfsp.cpp 1
112
113 \note Since this is a non-atomic global variable, it is only safe
114 to increment or decrement \c qt_ntfs_permission_lookup before any
115 threads other than the main thread have started or after every thread
116 other than the main thread has ended.
117
118 \note From Qt 6.6 the variable \c qt_ntfs_permission_lookup is
119 deprecated. Please use the following alternatives.
120
121 The safe and easy way to manage permission checks is to use the RAII class
122 \c QNtfsPermissionCheckGuard.
123
124 \snippet ntfsp.cpp raii
125
126 If you need more fine-grained control, it is possible to manage the permission
127 with the following functions instead:
128
129 \snippet ntfsp.cpp free-funcs
130
131*/
132
133//************* QFileDevice
134
135/*!
136 \class QFileDevice
137 \inmodule QtCore
138 \since 5.0
139
140 \brief The QFileDevice class provides an interface for reading from and writing to open files.
141
142 \ingroup io
143
144 \reentrant
145
146 QFileDevice is the base class for I/O devices that can read and write text and binary files
147 and \l{The Qt Resource System}{resources}. QFile offers the main functionality,
148 QFileDevice serves as a base class for sharing functionality with other file devices such
149 as QSaveFile, by providing all the operations that can be done on files that have
150 been opened by QFile or QSaveFile.
151
152 \sa QFile, QSaveFile
153*/
154
155/*!
156 \enum QFileDevice::FileHandleFlag
157
158 This enum is used when opening a file to specify additional
159 options which only apply to files and not to a generic
160 QIODevice.
161
162 \value AutoCloseHandle The file handle passed into open() should be
163 closed by close(), the default behavior is that close just flushes
164 the file and the application is responsible for closing the file handle.
165 When opening a file by name, this flag is ignored as Qt always owns the
166 file handle and must close it.
167 \value DontCloseHandle If not explicitly closed, the underlying file
168 handle is left open when the QFile object is destroyed.
169 */
170
171#ifdef QT_NO_QOBJECT
172QFileDevice::QFileDevice()
173 : QIODevice(*new QFileDevicePrivate)
174{
175}
176QFileDevice::QFileDevice(QFileDevicePrivate &dd)
177 : QIODevice(dd)
178{
179}
180#else
181/*!
182 \internal
183*/
184QFileDevice::QFileDevice()
185 : QIODevice(*new QFileDevicePrivate, nullptr)
186{
187}
188/*!
189 \internal
190*/
191QFileDevice::QFileDevice(QObject *parent)
192 : QIODevice(*new QFileDevicePrivate, parent)
193{
194}
195/*!
196 \internal
197*/
198QFileDevice::QFileDevice(QFileDevicePrivate &dd, QObject *parent)
199 : QIODevice(dd, parent)
200{
201}
202#endif
203
204/*!
205 Destroys the file device, closing it if necessary.
206*/
207QFileDevice::~QFileDevice()
208{
209 close();
210}
211
212/*!
213 Returns \c true if the file can only be manipulated sequentially;
214 otherwise returns \c false.
215
216 Most files support random-access, but some special files may not.
217
218 \sa QIODevice::isSequential()
219*/
220bool QFileDevice::isSequential() const
221{
222 Q_D(const QFileDevice);
223 return d->fileEngine && d->fileEngine->isSequential();
224}
225
226/*!
227 Returns the file handle of the file.
228
229 This is a small positive integer, suitable for use with C library
230 functions such as \c fdopen() and \c fcntl(). On systems that use file
231 descriptors for sockets (i.e. Unix systems, but not Windows) the handle
232 can be used with QSocketNotifier as well.
233
234 If the file is not open, or there is an error, handle() returns -1.
235
236 \sa QSocketNotifier
237*/
238int QFileDevice::handle() const
239{
240 Q_D(const QFileDevice);
241 if (!isOpen() || !d->fileEngine)
242 return -1;
243
244 return d->fileEngine->handle();
245}
246
247/*!
248 Returns the name of the file.
249 The default implementation in QFileDevice returns a null string.
250*/
251QString QFileDevice::fileName() const
252{
253 return QString();
254}
255
256/*!
257 Flushes any buffered data to the file. Returns \c true if successful;
258 otherwise returns \c false.
259*/
260bool QFileDevice::flush()
261{
262 Q_D(QFileDevice);
263 if (!d->fileEngine) {
264 qWarning(msg: "QFileDevice::flush: No file engine. Is IODevice open?");
265 return false;
266 }
267
268 if (!d->writeBuffer.isEmpty()) {
269 qint64 size = d->writeBuffer.nextDataBlockSize();
270 qint64 written = d->fileEngine->write(data: d->writeBuffer.readPointer(), len: size);
271 if (written > 0)
272 d->writeBuffer.free(bytes: written);
273 if (written != size) {
274 QFileDevice::FileError err = d->fileEngine->error();
275 if (err == QFileDevice::UnspecifiedError)
276 err = QFileDevice::WriteError;
277 d->setError(err, errStr: d->fileEngine->errorString());
278 return false;
279 }
280 }
281
282 if (!d->fileEngine->flush()) {
283 QFileDevice::FileError err = d->fileEngine->error();
284 if (err == QFileDevice::UnspecifiedError)
285 err = QFileDevice::WriteError;
286 d->setError(err, errStr: d->fileEngine->errorString());
287 return false;
288 }
289 return true;
290}
291
292/*!
293 Calls QFileDevice::flush() and closes the file. Errors from flush are ignored.
294
295 \sa QIODevice::close()
296*/
297void QFileDevice::close()
298{
299 Q_D(QFileDevice);
300 if (!isOpen())
301 return;
302 bool flushed = flush();
303 QIODevice::close();
304
305 // reset write buffer
306 d->lastWasWrite = false;
307 d->writeBuffer.clear();
308
309 // reset cached size
310 d->cachedSize = 0;
311
312 // keep earlier error from flush
313 if (d->fileEngine->close() && flushed)
314 unsetError();
315 else if (flushed)
316 d->setError(err: d->fileEngine->error(), errStr: d->fileEngine->errorString());
317}
318
319/*!
320 \reimp
321*/
322qint64 QFileDevice::pos() const
323{
324 return QIODevice::pos();
325}
326
327/*!
328 Returns \c true if the end of the file has been reached; otherwise returns
329 false.
330
331 For regular empty files on Unix (e.g. those in \c /proc), this function
332 returns \c true, since the file system reports that the size of such a file is
333 0. Therefore, you should not depend on atEnd() when reading data from such a
334 file, but rather call read() until no more data can be read.
335*/
336bool QFileDevice::atEnd() const
337{
338 Q_D(const QFileDevice);
339
340 // If there's buffered data left, we're not at the end.
341 if (!d->isBufferEmpty())
342 return false;
343
344 if (!isOpen())
345 return true;
346
347 if (!d->ensureFlushed())
348 return false;
349
350 // If the file engine knows best, say what it says.
351 if (d->fileEngine->supportsExtension(extension: QAbstractFileEngine::AtEndExtension)) {
352 // Check if the file engine supports AtEndExtension, and if it does,
353 // check if the file engine claims to be at the end.
354 return d->fileEngine->atEnd();
355 }
356
357 // if it looks like we are at the end, or if size is not cached,
358 // fall through to bytesAvailable() to make sure.
359 if (pos() < d->cachedSize)
360 return false;
361
362 // Fall back to checking how much is available (will stat files).
363 return bytesAvailable() == 0;
364}
365
366/*!
367 \fn bool QFileDevice::seek(qint64 pos)
368
369 For random-access devices, this function sets the current position
370 to \a pos, returning true on success, or false if an error occurred.
371 For sequential devices, the default behavior is to do nothing and
372 return false.
373
374 Seeking beyond the end of a file:
375 If the position is beyond the end of a file, then seek() will not
376 immediately extend the file. If a write is performed at this position,
377 then the file will be extended. The content of the file between the
378 previous end of file and the newly written data is UNDEFINED and
379 varies between platforms and file systems.
380*/
381bool QFileDevice::seek(qint64 off)
382{
383 Q_D(QFileDevice);
384 if (!isOpen()) {
385 qWarning(msg: "QFileDevice::seek: IODevice is not open");
386 return false;
387 }
388
389 if (!d->ensureFlushed())
390 return false;
391
392 if (!d->fileEngine->seek(pos: off) || !QIODevice::seek(pos: off)) {
393 QFileDevice::FileError err = d->fileEngine->error();
394 if (err == QFileDevice::UnspecifiedError)
395 err = QFileDevice::PositionError;
396 d->setError(err, errStr: d->fileEngine->errorString());
397 return false;
398 }
399 unsetError();
400 return true;
401}
402
403/*!
404 \reimp
405*/
406qint64 QFileDevice::readLineData(char *data, qint64 maxlen)
407{
408 Q_D(QFileDevice);
409 if (!d->ensureFlushed())
410 return -1;
411
412 qint64 read;
413 if (d->fileEngine->supportsExtension(extension: QAbstractFileEngine::FastReadLineExtension)) {
414 read = d->fileEngine->readLine(data, maxlen);
415 } else {
416 // Fall back to QIODevice's readLine implementation if the engine
417 // cannot do it faster.
418 read = QIODevice::readLineData(data, maxlen);
419 }
420
421 if (read < maxlen) {
422 // failed to read all requested, may be at the end of file, stop caching size so that it's rechecked
423 d->cachedSize = 0;
424 }
425
426 return read;
427}
428
429/*!
430 \reimp
431*/
432qint64 QFileDevice::readData(char *data, qint64 len)
433{
434 Q_D(QFileDevice);
435 if (!len)
436 return 0;
437 unsetError();
438 if (!d->ensureFlushed())
439 return -1;
440
441 const qint64 read = d->fileEngine->read(data, maxlen: len);
442 if (read < 0) {
443 QFileDevice::FileError err = d->fileEngine->error();
444 if (err == QFileDevice::UnspecifiedError)
445 err = QFileDevice::ReadError;
446 d->setError(err, errStr: d->fileEngine->errorString());
447 }
448
449 if (read < len) {
450 // failed to read all requested, may be at the end of file, stop caching size so that it's rechecked
451 d->cachedSize = 0;
452 }
453
454 return read;
455}
456
457/*!
458 \internal
459*/
460bool QFileDevicePrivate::putCharHelper(char c)
461{
462#ifdef QT_NO_QOBJECT
463 return QIODevicePrivate::putCharHelper(c);
464#else
465
466 // Cutoff for code that doesn't only touch the buffer.
467 qint64 writeBufferSize = writeBuffer.size();
468 if ((openMode & QIODevice::Unbuffered) || writeBufferSize + 1 >= writeBufferChunkSize
469#ifdef Q_OS_WIN
470 || ((openMode & QIODevice::Text) && c == '\n'
471 && writeBufferSize + 2 >= writeBufferChunkSize)
472#endif
473 ) {
474 return QIODevicePrivate::putCharHelper(c);
475 }
476
477 if (!(openMode & QIODevice::WriteOnly)) {
478 if (openMode == QIODevice::NotOpen)
479 qWarning(msg: "QIODevice::putChar: Closed device");
480 else
481 qWarning(msg: "QIODevice::putChar: ReadOnly device");
482 return false;
483 }
484
485 // Make sure the device is positioned correctly.
486 const bool sequential = isSequential();
487 if (pos != devicePos && !sequential && !q_func()->seek(off: pos))
488 return false;
489
490 lastWasWrite = true;
491
492 int len = 1;
493#ifdef Q_OS_WIN
494 if ((openMode & QIODevice::Text) && c == '\n') {
495 ++len;
496 *writeBuffer.reserve(1) = '\r';
497 }
498#endif
499
500 // Write to buffer.
501 *writeBuffer.reserve(bytes: 1) = c;
502
503 if (!sequential) {
504 pos += len;
505 devicePos += len;
506 if (!buffer.isEmpty())
507 buffer.skip(length: len);
508 }
509
510 return true;
511#endif
512}
513
514/*!
515 \reimp
516*/
517qint64 QFileDevice::writeData(const char *data, qint64 len)
518{
519 Q_D(QFileDevice);
520 unsetError();
521 d->lastWasWrite = true;
522 bool buffered = !(d->openMode & Unbuffered);
523
524 // Flush buffered data if this read will overflow.
525 if (buffered && (d->writeBuffer.size() + len) > d->writeBufferChunkSize) {
526 if (!flush())
527 return -1;
528 }
529
530 // Write directly to the engine if the block size is larger than
531 // the write buffer size.
532 if (!buffered || len > d->writeBufferChunkSize) {
533 const qint64 ret = d->fileEngine->write(data, len);
534 if (ret < 0) {
535 QFileDevice::FileError err = d->fileEngine->error();
536 if (err == QFileDevice::UnspecifiedError)
537 err = QFileDevice::WriteError;
538 d->setError(err, errStr: d->fileEngine->errorString());
539 }
540 return ret;
541 }
542
543 // Write to the buffer.
544 d->writeBuffer.append(data, size: len);
545 return len;
546}
547
548/*!
549 Returns the file error status.
550
551 The I/O device status returns an error code. For example, if open()
552 returns \c false, or a read/write operation returns -1, this function can
553 be called to find out the reason why the operation failed.
554
555 \sa unsetError()
556*/
557QFileDevice::FileError QFileDevice::error() const
558{
559 Q_D(const QFileDevice);
560 return d->error;
561}
562
563/*!
564 Sets the file's error to QFileDevice::NoError.
565
566 \sa error()
567*/
568void QFileDevice::unsetError()
569{
570 Q_D(QFileDevice);
571 d->setError(QFileDevice::NoError);
572}
573
574/*!
575 Returns the size of the file.
576
577 For regular empty files on Unix (e.g. those in \c /proc), this function
578 returns 0; the contents of such a file are generated on demand in response
579 to you calling read().
580*/
581qint64 QFileDevice::size() const
582{
583 Q_D(const QFileDevice);
584 if (!d->ensureFlushed())
585 return 0;
586 d->cachedSize = d->engine()->size();
587 return d->cachedSize;
588}
589
590/*!
591 Sets the file size (in bytes) \a sz. Returns \c true if the
592 resize succeeds; false otherwise. If \a sz is larger than the file
593 currently is, the new bytes will be set to 0; if \a sz is smaller, the
594 file is simply truncated.
595
596 \warning This function can fail if the file doesn't exist.
597
598 \sa size()
599*/
600bool QFileDevice::resize(qint64 sz)
601{
602 Q_D(QFileDevice);
603 if (!d->ensureFlushed())
604 return false;
605 d->engine();
606 if (isOpen() && d->fileEngine->pos() > sz)
607 seek(off: sz);
608 if (d->fileEngine->setSize(sz)) {
609 unsetError();
610 d->cachedSize = sz;
611 return true;
612 }
613 d->cachedSize = 0;
614 d->setError(err: QFile::ResizeError, errStr: d->fileEngine->errorString());
615 return false;
616}
617
618/*!
619 Returns the complete OR-ed together combination of
620 QFile::Permission for the file.
621
622 \sa setPermissions()
623*/
624QFile::Permissions QFileDevice::permissions() const
625{
626 Q_D(const QFileDevice);
627 QAbstractFileEngine::FileFlags perms = d->engine()->fileFlags(type: QAbstractFileEngine::PermsMask) & QAbstractFileEngine::PermsMask;
628 return QFile::Permissions::fromInt(i: perms.toInt()); //ewww
629}
630
631/*!
632 Sets the permissions for the file to the \a permissions specified.
633 Returns \c true if successful, or \c false if the permissions cannot be
634 modified.
635
636 \warning This function does not manipulate ACLs, which may limit its
637 effectiveness.
638
639 \sa permissions()
640*/
641bool QFileDevice::setPermissions(Permissions permissions)
642{
643 Q_D(QFileDevice);
644 if (d->engine()->setPermissions(permissions.toInt())) {
645 unsetError();
646 return true;
647 }
648 d->setError(err: QFile::PermissionsError, errStr: d->fileEngine->errorString());
649 return false;
650}
651
652/*!
653 \enum QFileDevice::MemoryMapFlag
654 \since 4.4
655
656 This enum describes special options that may be used by the map()
657 function.
658
659 \value NoOptions No options.
660 \value MapPrivateOption The mapped memory will be private, so any
661 modifications will not be visible to other processes and will not
662 be written to disk. Any such modifications will be lost when the
663 memory is unmapped. It is unspecified whether modifications made
664 to the file made after the mapping is created will be visible through
665 the mapped memory. This enum value was introduced in Qt 5.4.
666*/
667
668/*!
669 Maps \a size bytes of the file into memory starting at \a offset. A file
670 should be open for a map to succeed but the file does not need to stay
671 open after the memory has been mapped. When the QFile is destroyed
672 or a new file is opened with this object, any maps that have not been
673 unmapped will automatically be unmapped.
674
675 The mapping will have the same open mode as the file (read and/or write),
676 except when using MapPrivateOption, in which case it is always possible
677 to write to the mapped memory.
678
679 Any mapping options can be passed through \a flags.
680
681 Returns a pointer to the memory or \nullptr if there is an error.
682
683 \sa unmap()
684 */
685uchar *QFileDevice::map(qint64 offset, qint64 size, MemoryMapFlags flags)
686{
687 Q_D(QFileDevice);
688 if (d->engine()
689 && d->fileEngine->supportsExtension(extension: QAbstractFileEngine::MapExtension)) {
690 unsetError();
691 uchar *address = d->fileEngine->map(offset, size, flags);
692 if (address == nullptr)
693 d->setError(err: d->fileEngine->error(), errStr: d->fileEngine->errorString());
694 return address;
695 }
696 return nullptr;
697}
698
699/*!
700 Unmaps the memory \a address.
701
702 Returns \c true if the unmap succeeds; false otherwise.
703
704 \sa map()
705 */
706bool QFileDevice::unmap(uchar *address)
707{
708 Q_D(QFileDevice);
709 if (d->engine()
710 && d->fileEngine->supportsExtension(extension: QAbstractFileEngine::UnMapExtension)) {
711 unsetError();
712 bool success = d->fileEngine->unmap(ptr: address);
713 if (!success)
714 d->setError(err: d->fileEngine->error(), errStr: d->fileEngine->errorString());
715 return success;
716 }
717 d->setError(err: PermissionsError, errStr: tr(s: "No file engine available or engine does not support UnMapExtension"));
718 return false;
719}
720
721/*!
722 \enum QFileDevice::FileTime
723 \since 5.10
724
725 This enum is used by the fileTime() and setFileTime() functions.
726
727 \value FileAccessTime When the file was most recently accessed
728 (e.g. read or written to).
729 \value FileBirthTime When the file was created (may not be not
730 supported on UNIX).
731 \value FileMetadataChangeTime When the file's metadata was last changed.
732 \value FileModificationTime When the file was most recently modified.
733
734 \sa setFileTime(), fileTime(), QFileInfo::fileTime()
735*/
736
737static inline QAbstractFileEngine::FileTime FileDeviceTimeToAbstractFileEngineTime(QFileDevice::FileTime time)
738{
739 static_assert(int(QFileDevice::FileAccessTime) == int(QAbstractFileEngine::AccessTime));
740 static_assert(int(QFileDevice::FileBirthTime) == int(QAbstractFileEngine::BirthTime));
741 static_assert(int(QFileDevice::FileMetadataChangeTime) == int(QAbstractFileEngine::MetadataChangeTime));
742 static_assert(int(QFileDevice::FileModificationTime) == int(QAbstractFileEngine::ModificationTime));
743 return QAbstractFileEngine::FileTime(time);
744}
745
746/*!
747 \since 5.10
748 Returns the file time specified by \a time.
749 If the time cannot be determined return QDateTime() (an invalid
750 date time).
751
752 \sa setFileTime(), FileTime, QDateTime::isValid()
753*/
754QDateTime QFileDevice::fileTime(QFileDevice::FileTime time) const
755{
756 Q_D(const QFileDevice);
757
758 if (d->engine())
759 return d->engine()->fileTime(time: FileDeviceTimeToAbstractFileEngineTime(time));
760
761 return QDateTime();
762}
763
764/*!
765 \since 5.10
766 Sets the file time specified by \a fileTime to \a newDate, returning true
767 if successful; otherwise returns false.
768
769 \note The file must be open to use this function.
770
771 \sa fileTime(), FileTime
772*/
773bool QFileDevice::setFileTime(const QDateTime &newDate, QFileDevice::FileTime fileTime)
774{
775 Q_D(QFileDevice);
776
777 if (!d->engine()) {
778 d->setError(err: QFileDevice::UnspecifiedError, errStr: tr(s: "No file engine available"));
779 return false;
780 }
781
782 if (!d->fileEngine->setFileTime(newDate, time: FileDeviceTimeToAbstractFileEngineTime(time: fileTime))) {
783 d->setError(err: d->fileEngine->error(), errStr: d->fileEngine->errorString());
784 return false;
785 }
786
787 unsetError();
788 return true;
789}
790
791QT_END_NAMESPACE
792
793#ifndef QT_NO_QOBJECT
794#include "moc_qfiledevice.cpp"
795#endif
796

source code of qtbase/src/corelib/io/qfiledevice.cpp