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/*!
172 \macro QT_USE_NODISCARD_FILE_OPEN
173 \macro QT_NO_USE_NODISCARD_FILE_OPEN
174 \relates QFileDevice
175 \since 6.8
176
177 File-related I/O classes (such as QFile, QSaveFile, QTemporaryFile)
178 have an \c{open()} method to open the file they act upon. It is
179 important to check the return value of the call to \c{open()}
180 before proceeding with reading or writing data into the file.
181
182 For this reason, starting with Qt 6.8, some overloads of \c{open()}
183 have been marked with the \c{[[nodiscard]]} attribute. Since this
184 change may raise warnings in existing codebases, user code can
185 opt-in or opt-out from having the attribute applied by defining
186 certain macros:
187
188 \list
189 \li If the \c{QT_USE_NODISCARD_FILE_OPEN} macro is defined,
190 overloads of \c{open()} are marked as \c{[[nodiscard]]}.
191 \li If the \c{QT_NO_USE_NODISCARD_FILE_OPEN} is defined, the
192 overloads of \c{open()} are \e{not} marked as \c{[[nodiscard]]}.
193 \li If neither macro is defined, then the default up to and
194 including Qt 6.9 is not to have the attribute. Starting from Qt 6.10,
195 the attribute is automatically applied.
196 \li If both macros are defined, the program is ill-formed.
197 \endlist
198*/
199
200#ifdef QT_NO_QOBJECT
201QFileDevice::QFileDevice()
202 : QIODevice(*new QFileDevicePrivate)
203{
204}
205QFileDevice::QFileDevice(QFileDevicePrivate &dd)
206 : QIODevice(dd)
207{
208}
209#else
210/*!
211 \internal
212*/
213QFileDevice::QFileDevice()
214 : QIODevice(*new QFileDevicePrivate, nullptr)
215{
216}
217/*!
218 \internal
219*/
220QFileDevice::QFileDevice(QObject *parent)
221 : QIODevice(*new QFileDevicePrivate, parent)
222{
223}
224/*!
225 \internal
226*/
227QFileDevice::QFileDevice(QFileDevicePrivate &dd, QObject *parent)
228 : QIODevice(dd, parent)
229{
230}
231#endif
232
233/*!
234 Destroys the file device, closing it if necessary.
235*/
236QFileDevice::~QFileDevice()
237{
238 close();
239}
240
241/*!
242 Returns \c true if the file can only be manipulated sequentially;
243 otherwise returns \c false.
244
245 Most files support random-access, but some special files may not.
246
247 \sa QIODevice::isSequential()
248*/
249bool QFileDevice::isSequential() const
250{
251 Q_D(const QFileDevice);
252 return d->fileEngine && d->fileEngine->isSequential();
253}
254
255/*!
256 Returns the file handle of the file.
257
258 This is a small positive integer, suitable for use with C library
259 functions such as \c fdopen() and \c fcntl(). On systems that use file
260 descriptors for sockets (i.e. Unix systems, but not Windows) the handle
261 can be used with QSocketNotifier as well.
262
263 If the file is not open, or there is an error, handle() returns -1.
264
265 \sa QSocketNotifier
266*/
267int QFileDevice::handle() const
268{
269 Q_D(const QFileDevice);
270 if (!isOpen() || !d->fileEngine)
271 return -1;
272
273 return d->fileEngine->handle();
274}
275
276/*!
277 Returns the name of the file.
278 The default implementation in QFileDevice returns a null string.
279*/
280QString QFileDevice::fileName() const
281{
282 return QString();
283}
284
285/*!
286 Flushes any buffered data to the file. Returns \c true if successful;
287 otherwise returns \c false.
288*/
289bool QFileDevice::flush()
290{
291 Q_D(QFileDevice);
292 if (!d->fileEngine) {
293 qWarning(msg: "QFileDevice::flush: No file engine. Is IODevice open?");
294 return false;
295 }
296
297 if (!d->writeBuffer.isEmpty()) {
298 qint64 size = d->writeBuffer.nextDataBlockSize();
299 qint64 written = d->fileEngine->write(data: d->writeBuffer.readPointer(), len: size);
300 if (written > 0)
301 d->writeBuffer.free(bytes: written);
302 if (written != size) {
303 QFileDevice::FileError err = d->fileEngine->error();
304 if (err == QFileDevice::UnspecifiedError)
305 err = QFileDevice::WriteError;
306 d->setError(err, errStr: d->fileEngine->errorString());
307 return false;
308 }
309 }
310
311 if (!d->fileEngine->flush()) {
312 QFileDevice::FileError err = d->fileEngine->error();
313 if (err == QFileDevice::UnspecifiedError)
314 err = QFileDevice::WriteError;
315 d->setError(err, errStr: d->fileEngine->errorString());
316 return false;
317 }
318 return true;
319}
320
321/*!
322 Calls QFileDevice::flush() and closes the file. Errors from flush are ignored.
323
324 \sa QIODevice::close()
325*/
326void QFileDevice::close()
327{
328 Q_D(QFileDevice);
329 if (!isOpen())
330 return;
331 bool flushed = flush();
332 QIODevice::close();
333
334 // reset write buffer
335 d->lastWasWrite = false;
336 d->writeBuffer.clear();
337
338 // reset cached size
339 d->cachedSize = 0;
340
341 // If flush() succeeded but close() failed, copy its error condition;
342 // otherwise, keep the earlier flush() error.
343 if (d->fileEngine->close() && flushed)
344 unsetError();
345 else if (flushed)
346 d->setError(err: d->fileEngine->error(), errStr: d->fileEngine->errorString());
347}
348
349/*!
350 \reimp
351*/
352qint64 QFileDevice::pos() const
353{
354 return QIODevice::pos();
355}
356
357/*!
358 Returns \c true if the end of the file has been reached; otherwise returns
359 false.
360
361 For regular empty files on Unix (e.g. those in \c /proc), this function
362 returns \c true, since the file system reports that the size of such a file is
363 0. Therefore, you should not depend on atEnd() when reading data from such a
364 file, but rather call read() until no more data can be read.
365*/
366bool QFileDevice::atEnd() const
367{
368 Q_D(const QFileDevice);
369
370 // If there's buffered data left, we're not at the end.
371 if (!d->isBufferEmpty())
372 return false;
373
374 if (!isOpen())
375 return true;
376
377 if (!d->ensureFlushed())
378 return false;
379
380 // If the file engine knows best, say what it says.
381 if (d->fileEngine->supportsExtension(extension: QAbstractFileEngine::AtEndExtension)) {
382 // Check if the file engine supports AtEndExtension, and if it does,
383 // check if the file engine claims to be at the end.
384 return d->fileEngine->atEnd();
385 }
386
387 // if it looks like we are at the end, or if size is not cached,
388 // fall through to bytesAvailable() to make sure.
389 if (pos() < d->cachedSize)
390 return false;
391
392 // Fall back to checking how much is available (will stat files).
393 return bytesAvailable() == 0;
394}
395
396/*!
397 \fn bool QFileDevice::seek(qint64 pos)
398
399 For random-access devices, this function sets the current position
400 to \a pos, returning true on success, or false if an error occurred.
401 For sequential devices, the default behavior is to do nothing and
402 return false.
403
404 Seeking beyond the end of a file:
405 If the position is beyond the end of a file, then seek() will not
406 immediately extend the file. If a write is performed at this position,
407 then the file will be extended. The content of the file between the
408 previous end of file and the newly written data is UNDEFINED and
409 varies between platforms and file systems.
410*/
411bool QFileDevice::seek(qint64 off)
412{
413 Q_D(QFileDevice);
414 if (!isOpen()) {
415 qWarning(msg: "QFileDevice::seek: IODevice is not open");
416 return false;
417 }
418
419 if (!d->ensureFlushed())
420 return false;
421
422 if (!d->fileEngine->seek(pos: off) || !QIODevice::seek(pos: off)) {
423 QFileDevice::FileError err = d->fileEngine->error();
424 if (err == QFileDevice::UnspecifiedError)
425 err = QFileDevice::PositionError;
426 d->setError(err, errStr: d->fileEngine->errorString());
427 return false;
428 }
429 unsetError();
430 return true;
431}
432
433/*!
434 \reimp
435*/
436qint64 QFileDevice::readLineData(char *data, qint64 maxlen)
437{
438 Q_D(QFileDevice);
439 if (!d->ensureFlushed())
440 return -1;
441
442 qint64 read;
443 if (d->fileEngine->supportsExtension(extension: QAbstractFileEngine::FastReadLineExtension)) {
444 read = d->fileEngine->readLine(data, maxlen);
445 } else {
446 // Fall back to QIODevice's readLine implementation if the engine
447 // cannot do it faster.
448 read = QIODevice::readLineData(data, maxlen);
449 }
450
451 if (read < maxlen) {
452 // failed to read all requested, may be at the end of file, stop caching size so that it's rechecked
453 d->cachedSize = 0;
454 }
455
456 return read;
457}
458
459/*!
460 \reimp
461*/
462qint64 QFileDevice::readData(char *data, qint64 len)
463{
464 Q_D(QFileDevice);
465 if (!len)
466 return 0;
467 unsetError();
468 if (!d->ensureFlushed())
469 return -1;
470
471 const qint64 read = d->fileEngine->read(data, maxlen: len);
472 if (read < 0) {
473 QFileDevice::FileError err = d->fileEngine->error();
474 if (err == QFileDevice::UnspecifiedError)
475 err = QFileDevice::ReadError;
476 d->setError(err, errStr: d->fileEngine->errorString());
477 }
478
479 if (read < len) {
480 // failed to read all requested, may be at the end of file, stop caching size so that it's rechecked
481 d->cachedSize = 0;
482 }
483
484 return read;
485}
486
487/*!
488 \internal
489*/
490bool QFileDevicePrivate::putCharHelper(char c)
491{
492#ifdef QT_NO_QOBJECT
493 return QIODevicePrivate::putCharHelper(c);
494#else
495
496 // Cutoff for code that doesn't only touch the buffer.
497 qint64 writeBufferSize = writeBuffer.size();
498 if ((openMode & QIODevice::Unbuffered) || writeBufferSize + 1 >= writeBufferChunkSize
499#ifdef Q_OS_WIN
500 || ((openMode & QIODevice::Text) && c == '\n'
501 && writeBufferSize + 2 >= writeBufferChunkSize)
502#endif
503 ) {
504 return QIODevicePrivate::putCharHelper(c);
505 }
506
507 if (!(openMode & QIODevice::WriteOnly)) {
508 if (openMode == QIODevice::NotOpen)
509 qWarning(msg: "QIODevice::putChar: Closed device");
510 else
511 qWarning(msg: "QIODevice::putChar: ReadOnly device");
512 return false;
513 }
514
515 // Make sure the device is positioned correctly.
516 const bool sequential = isSequential();
517 if (pos != devicePos && !sequential && !q_func()->seek(off: pos))
518 return false;
519
520 lastWasWrite = true;
521
522 int len = 1;
523#ifdef Q_OS_WIN
524 if ((openMode & QIODevice::Text) && c == '\n') {
525 ++len;
526 *writeBuffer.reserve(1) = '\r';
527 }
528#endif
529
530 // Write to buffer.
531 *writeBuffer.reserve(bytes: 1) = c;
532
533 if (!sequential) {
534 pos += len;
535 devicePos += len;
536 if (!buffer.isEmpty())
537 buffer.skip(length: len);
538 }
539
540 return true;
541#endif
542}
543
544/*!
545 \reimp
546*/
547qint64 QFileDevice::writeData(const char *data, qint64 len)
548{
549 Q_D(QFileDevice);
550 unsetError();
551 d->lastWasWrite = true;
552 bool buffered = !(d->openMode & Unbuffered);
553
554 // Flush buffered data if this read will overflow.
555 if (buffered && (d->writeBuffer.size() + len) > d->writeBufferChunkSize) {
556 if (!flush())
557 return -1;
558 }
559
560 // Write directly to the engine if the block size is larger than
561 // the write buffer size.
562 if (!buffered || len > d->writeBufferChunkSize) {
563 const qint64 ret = d->fileEngine->write(data, len);
564 if (ret < 0) {
565 QFileDevice::FileError err = d->fileEngine->error();
566 if (err == QFileDevice::UnspecifiedError)
567 err = QFileDevice::WriteError;
568 d->setError(err, errStr: d->fileEngine->errorString());
569 }
570 return ret;
571 }
572
573 // Write to the buffer.
574 d->writeBuffer.append(data, size: len);
575 return len;
576}
577
578/*!
579 Returns the file error status.
580
581 The I/O device status returns an error code. For example, if open()
582 returns \c false, or a read/write operation returns -1, this function can
583 be called to find out the reason why the operation failed.
584
585 \sa unsetError()
586*/
587QFileDevice::FileError QFileDevice::error() const
588{
589 Q_D(const QFileDevice);
590 return d->error;
591}
592
593/*!
594 Sets the file's error to QFileDevice::NoError.
595
596 \sa error()
597*/
598void QFileDevice::unsetError()
599{
600 Q_D(QFileDevice);
601 d->setError(QFileDevice::NoError);
602}
603
604/*!
605 Returns the size of the file.
606
607 For regular empty files on Unix (e.g. those in \c /proc), this function
608 returns 0; the contents of such a file are generated on demand in response
609 to you calling read().
610*/
611qint64 QFileDevice::size() const
612{
613 Q_D(const QFileDevice);
614 if (!d->ensureFlushed())
615 return 0;
616 d->cachedSize = d->engine()->size();
617 return d->cachedSize;
618}
619
620/*!
621 Sets the file size (in bytes) \a sz. Returns \c true if the
622 resize succeeds; false otherwise. If \a sz is larger than the file
623 currently is, the new bytes will be set to 0; if \a sz is smaller, the
624 file is simply truncated.
625
626 \warning This function can fail if the file doesn't exist.
627
628 \sa size()
629*/
630bool QFileDevice::resize(qint64 sz)
631{
632 Q_D(QFileDevice);
633 if (!d->ensureFlushed())
634 return false;
635 d->engine();
636 if (isOpen() && d->fileEngine->pos() > sz)
637 seek(off: sz);
638 if (d->fileEngine->setSize(sz)) {
639 unsetError();
640 d->cachedSize = sz;
641 return true;
642 }
643 d->cachedSize = 0;
644 d->setError(err: QFile::ResizeError, errStr: d->fileEngine->errorString());
645 return false;
646}
647
648/*!
649 Returns the complete OR-ed together combination of
650 QFile::Permission for the file.
651
652 \sa setPermissions()
653*/
654QFile::Permissions QFileDevice::permissions() const
655{
656 Q_D(const QFileDevice);
657 QAbstractFileEngine::FileFlags perms = d->engine()->fileFlags(type: QAbstractFileEngine::PermsMask) & QAbstractFileEngine::PermsMask;
658 return QFile::Permissions::fromInt(i: perms.toInt()); //ewww
659}
660
661/*!
662 Sets the permissions for the file to the \a permissions specified.
663 Returns \c true if successful, or \c false if the permissions cannot be
664 modified.
665
666 \warning This function does not manipulate ACLs, which may limit its
667 effectiveness.
668
669 \sa permissions()
670*/
671bool QFileDevice::setPermissions(Permissions permissions)
672{
673 Q_D(QFileDevice);
674 if (d->engine()->setPermissions(permissions.toInt())) {
675 unsetError();
676 return true;
677 }
678 d->setError(err: QFile::PermissionsError, errStr: d->fileEngine->errorString());
679 return false;
680}
681
682/*!
683 \enum QFileDevice::MemoryMapFlag
684 \since 4.4
685
686 This enum describes special options that may be used by the map()
687 function.
688
689 \value NoOptions No options.
690 \value MapPrivateOption The mapped memory will be private, so any
691 modifications will not be visible to other processes and will not
692 be written to disk. Any such modifications will be lost when the
693 memory is unmapped. It is unspecified whether modifications made
694 to the file made after the mapping is created will be visible through
695 the mapped memory. This enum value was introduced in Qt 5.4.
696*/
697
698/*!
699 Maps \a size bytes of the file into memory starting at \a offset. A file
700 should be open for a map to succeed but the file does not need to stay
701 open after the memory has been mapped. When the QFile is destroyed
702 or a new file is opened with this object, any maps that have not been
703 unmapped will automatically be unmapped.
704
705 The mapping will have the same open mode as the file (read and/or write),
706 except when using MapPrivateOption, in which case it is always possible
707 to write to the mapped memory.
708
709 Any mapping options can be passed through \a flags.
710
711 Returns a pointer to the memory or \nullptr if there is an error.
712
713 \sa unmap()
714 */
715uchar *QFileDevice::map(qint64 offset, qint64 size, MemoryMapFlags flags)
716{
717 Q_D(QFileDevice);
718 if (d->engine()
719 && d->fileEngine->supportsExtension(extension: QAbstractFileEngine::MapExtension)) {
720 unsetError();
721 uchar *address = d->fileEngine->map(offset, size, flags);
722 if (address == nullptr)
723 d->setError(err: d->fileEngine->error(), errStr: d->fileEngine->errorString());
724 return address;
725 }
726 return nullptr;
727}
728
729/*!
730 Unmaps the memory \a address.
731
732 Returns \c true if the unmap succeeds; false otherwise.
733
734 \sa map()
735 */
736bool QFileDevice::unmap(uchar *address)
737{
738 Q_D(QFileDevice);
739 if (d->engine()
740 && d->fileEngine->supportsExtension(extension: QAbstractFileEngine::UnMapExtension)) {
741 unsetError();
742 bool success = d->fileEngine->unmap(ptr: address);
743 if (!success)
744 d->setError(err: d->fileEngine->error(), errStr: d->fileEngine->errorString());
745 return success;
746 }
747 d->setError(err: PermissionsError, errStr: tr(s: "No file engine available or engine does not support UnMapExtension"));
748 return false;
749}
750
751/*!
752 \enum QFileDevice::FileTime
753 \since 5.10
754
755 This enum is used by the fileTime() and setFileTime() functions.
756
757 \value FileAccessTime When the file was most recently accessed
758 (e.g. read or written to).
759 \value FileBirthTime When the file was created (may not be not
760 supported on UNIX).
761 \value FileMetadataChangeTime When the file's metadata was last changed.
762 \value FileModificationTime When the file was most recently modified.
763
764 \sa setFileTime(), fileTime(), QFileInfo::fileTime()
765*/
766
767/*!
768 \since 5.10
769 Returns the file time specified by \a time.
770 If the time cannot be determined return QDateTime() (an invalid
771 date time).
772
773 \sa setFileTime(), FileTime, QDateTime::isValid()
774*/
775QDateTime QFileDevice::fileTime(QFileDevice::FileTime time) const
776{
777 Q_D(const QFileDevice);
778
779 if (d->engine())
780 return d->engine()->fileTime(time);
781
782 return QDateTime();
783}
784
785/*!
786 \since 5.10
787 Sets the file time specified by \a fileTime to \a newDate, returning true
788 if successful; otherwise returns false.
789
790 \note The file must be open to use this function.
791
792 \sa fileTime(), FileTime
793*/
794bool QFileDevice::setFileTime(const QDateTime &newDate, QFileDevice::FileTime fileTime)
795{
796 Q_D(QFileDevice);
797
798 if (!d->engine()) {
799 d->setError(err: QFileDevice::UnspecifiedError, errStr: tr(s: "No file engine available"));
800 return false;
801 }
802
803 if (!d->fileEngine->setFileTime(newDate, time: fileTime)) {
804 d->setError(err: d->fileEngine->error(), errStr: d->fileEngine->errorString());
805 return false;
806 }
807
808 unsetError();
809 return true;
810}
811
812QT_END_NAMESPACE
813
814#ifndef QT_NO_QOBJECT
815#include "moc_qfiledevice.cpp"
816#endif
817

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