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