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