1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Copyright (C) 2016 Intel Corporation. |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the QtCore module of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:LGPL$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU Lesser General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
22 | ** packaging of this file. Please review the following information to |
23 | ** ensure the GNU Lesser General Public License version 3 requirements |
24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
25 | ** |
26 | ** GNU General Public License Usage |
27 | ** Alternatively, this file may be used under the terms of the GNU |
28 | ** General Public License version 2.0 or (at your option) the GNU General |
29 | ** Public license version 3 or any later version approved by the KDE Free |
30 | ** Qt Foundation. The licenses are as published by the Free Software |
31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
32 | ** included in the packaging of this file. Please review the following |
33 | ** information to ensure the GNU General Public License requirements will |
34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
35 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
36 | ** |
37 | ** $QT_END_LICENSE$ |
38 | ** |
39 | ****************************************************************************/ |
40 | |
41 | #include "qfsfileengine_p.h" |
42 | #include "qfsfileengine_iterator_p.h" |
43 | #include "qfilesystemengine_p.h" |
44 | #include "qdatetime.h" |
45 | #include "qdiriterator.h" |
46 | #include "qset.h" |
47 | #include <QtCore/qdebug.h> |
48 | |
49 | #ifndef QT_NO_FSFILEENGINE |
50 | |
51 | #include <errno.h> |
52 | #if defined(Q_OS_UNIX) |
53 | #include "private/qcore_unix_p.h" |
54 | #endif |
55 | #include <stdio.h> |
56 | #include <stdlib.h> |
57 | #if defined(Q_OS_MAC) |
58 | # include <private/qcore_mac_p.h> |
59 | #endif |
60 | |
61 | QT_BEGIN_NAMESPACE |
62 | |
63 | #ifdef Q_OS_WIN |
64 | # ifndef S_ISREG |
65 | # define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) |
66 | # endif |
67 | # ifndef S_ISCHR |
68 | # define S_ISCHR(x) (((x) & S_IFMT) == S_IFCHR) |
69 | # endif |
70 | # ifndef S_ISFIFO |
71 | # define S_ISFIFO(x) false |
72 | # endif |
73 | # ifndef S_ISSOCK |
74 | # define S_ISSOCK(x) false |
75 | # endif |
76 | # ifndef INVALID_FILE_ATTRIBUTES |
77 | # define INVALID_FILE_ATTRIBUTES (DWORD (-1)) |
78 | # endif |
79 | #endif |
80 | |
81 | #ifdef Q_OS_WIN |
82 | // on Windows, read() and write() use int and unsigned int |
83 | typedef int SignedIOType; |
84 | typedef unsigned int UnsignedIOType; |
85 | #else |
86 | typedef ssize_t SignedIOType; |
87 | typedef size_t UnsignedIOType; |
88 | static_assert(sizeof(SignedIOType) == sizeof(UnsignedIOType), |
89 | "Unsupported: read/write return a type with different size as the len parameter" ); |
90 | #endif |
91 | |
92 | /*! \class QFSFileEngine |
93 | \inmodule QtCore |
94 | \brief The QFSFileEngine class implements Qt's default file engine. |
95 | \since 4.1 |
96 | \internal |
97 | |
98 | This class is part of the file engine framework in Qt. If you only want to |
99 | access files or directories, use QFile, QFileInfo or QDir instead. |
100 | |
101 | QFSFileEngine is the default file engine for accessing regular files. It |
102 | is provided for convenience; by subclassing this class, you can alter its |
103 | behavior slightly, without having to write a complete QAbstractFileEngine |
104 | subclass. To install your custom file engine, you must also subclass |
105 | QAbstractFileEngineHandler and create an instance of your handler. |
106 | |
107 | It can also be useful to create a QFSFileEngine object directly if you |
108 | need to use the local file system inside QAbstractFileEngine::create(), in |
109 | order to avoid recursion (as higher-level classes tend to call |
110 | QAbstractFileEngine::create()). |
111 | */ |
112 | |
113 | //**************** QFSFileEnginePrivate |
114 | QFSFileEnginePrivate::QFSFileEnginePrivate() : QAbstractFileEnginePrivate() |
115 | { |
116 | init(); |
117 | } |
118 | |
119 | /*! |
120 | \internal |
121 | */ |
122 | void QFSFileEnginePrivate::init() |
123 | { |
124 | is_sequential = 0; |
125 | tried_stat = 0; |
126 | need_lstat = 1; |
127 | is_link = 0; |
128 | openMode = QIODevice::NotOpen; |
129 | fd = -1; |
130 | fh = nullptr; |
131 | lastIOCommand = IOFlushCommand; |
132 | lastFlushFailed = false; |
133 | closeFileHandle = false; |
134 | #ifdef Q_OS_WIN |
135 | fileAttrib = INVALID_FILE_ATTRIBUTES; |
136 | fileHandle = INVALID_HANDLE_VALUE; |
137 | mapHandle = NULL; |
138 | cachedFd = -1; |
139 | #endif |
140 | } |
141 | |
142 | /*! |
143 | Constructs a QFSFileEngine for the file name \a file. |
144 | */ |
145 | QFSFileEngine::QFSFileEngine(const QString &file) |
146 | : QAbstractFileEngine(*new QFSFileEnginePrivate) |
147 | { |
148 | Q_D(QFSFileEngine); |
149 | d->fileEntry = QFileSystemEntry(file); |
150 | } |
151 | |
152 | /*! |
153 | Constructs a QFSFileEngine. |
154 | */ |
155 | QFSFileEngine::QFSFileEngine() : QAbstractFileEngine(*new QFSFileEnginePrivate) |
156 | { |
157 | } |
158 | |
159 | /*! |
160 | \internal |
161 | */ |
162 | QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd) |
163 | : QAbstractFileEngine(dd) |
164 | { |
165 | } |
166 | |
167 | /*! |
168 | \internal |
169 | */ |
170 | ProcessOpenModeResult processOpenModeFlags(QIODevice::OpenMode openMode) |
171 | { |
172 | ProcessOpenModeResult result; |
173 | result.ok = false; |
174 | if ((openMode & QFile::NewOnly) && (openMode & QFile::ExistingOnly)) { |
175 | qWarning("NewOnly and ExistingOnly are mutually exclusive" ); |
176 | result.error = QLatin1String("NewOnly and ExistingOnly are mutually exclusive" ); |
177 | return result; |
178 | } |
179 | |
180 | if ((openMode & QFile::ExistingOnly) && !(openMode & (QFile::ReadOnly | QFile::WriteOnly))) { |
181 | qWarning("ExistingOnly must be specified alongside ReadOnly, WriteOnly, or ReadWrite" ); |
182 | result.error = QLatin1String( |
183 | "ExistingOnly must be specified alongside ReadOnly, WriteOnly, or ReadWrite" ); |
184 | return result; |
185 | } |
186 | |
187 | // Either Append or NewOnly implies WriteOnly |
188 | if (openMode & (QFile::Append | QFile::NewOnly)) |
189 | openMode |= QFile::WriteOnly; |
190 | |
191 | // WriteOnly implies Truncate when ReadOnly, Append, and NewOnly are not set. |
192 | if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append | QFile::NewOnly))) |
193 | openMode |= QFile::Truncate; |
194 | |
195 | result.ok = true; |
196 | result.openMode = openMode; |
197 | return result; |
198 | } |
199 | |
200 | /*! |
201 | Destructs the QFSFileEngine. |
202 | */ |
203 | QFSFileEngine::~QFSFileEngine() |
204 | { |
205 | Q_D(QFSFileEngine); |
206 | if (d->closeFileHandle) { |
207 | if (d->fh) { |
208 | fclose(d->fh); |
209 | } else if (d->fd != -1) { |
210 | QT_CLOSE(d->fd); |
211 | } |
212 | } |
213 | d->unmapAll(); |
214 | } |
215 | |
216 | /*! |
217 | \reimp |
218 | */ |
219 | void QFSFileEngine::setFileName(const QString &file) |
220 | { |
221 | Q_D(QFSFileEngine); |
222 | d->init(); |
223 | d->fileEntry = QFileSystemEntry(file); |
224 | } |
225 | |
226 | /*! |
227 | \reimp |
228 | */ |
229 | bool QFSFileEngine::open(QIODevice::OpenMode openMode) |
230 | { |
231 | Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open" , |
232 | "QFSFileEngine no longer supports buffered mode; upper layer must buffer" ); |
233 | |
234 | Q_D(QFSFileEngine); |
235 | if (d->fileEntry.isEmpty()) { |
236 | qWarning("QFSFileEngine::open: No file name specified" ); |
237 | setError(QFile::OpenError, QLatin1String("No file name specified" )); |
238 | return false; |
239 | } |
240 | |
241 | const ProcessOpenModeResult res = processOpenModeFlags(openMode); |
242 | if (!res.ok) { |
243 | setError(QFileDevice::OpenError, res.error); |
244 | return false; |
245 | } |
246 | |
247 | d->openMode = res.openMode; |
248 | d->lastFlushFailed = false; |
249 | d->tried_stat = 0; |
250 | d->fh = nullptr; |
251 | d->fd = -1; |
252 | |
253 | return d->nativeOpen(d->openMode); |
254 | } |
255 | |
256 | /*! |
257 | Opens the file handle \a fh in \a openMode mode. Returns \c true on |
258 | success; otherwise returns \c false. |
259 | */ |
260 | bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh) |
261 | { |
262 | return open(openMode, fh, QFile::DontCloseHandle); |
263 | } |
264 | |
265 | bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHandleFlags handleFlags) |
266 | { |
267 | Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open" , |
268 | "QFSFileEngine no longer supports buffered mode; upper layer must buffer" ); |
269 | |
270 | Q_D(QFSFileEngine); |
271 | |
272 | const ProcessOpenModeResult res = processOpenModeFlags(openMode); |
273 | if (!res.ok) { |
274 | setError(QFileDevice::OpenError, res.error); |
275 | return false; |
276 | } |
277 | |
278 | d->openMode = res.openMode; |
279 | d->lastFlushFailed = false; |
280 | d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); |
281 | d->fileEntry.clear(); |
282 | d->tried_stat = 0; |
283 | d->fd = -1; |
284 | |
285 | return d->openFh(d->openMode, fh); |
286 | } |
287 | |
288 | /*! |
289 | Opens the file handle \a fh using the open mode \a flags. |
290 | */ |
291 | bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh) |
292 | { |
293 | Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open" , |
294 | "QFSFileEngine no longer supports buffered mode; upper layer must buffer" ); |
295 | |
296 | Q_Q(QFSFileEngine); |
297 | this->fh = fh; |
298 | fd = -1; |
299 | |
300 | // Seek to the end when in Append mode. |
301 | if (openMode & QIODevice::Append) { |
302 | int ret; |
303 | do { |
304 | ret = QT_FSEEK(fh, 0, SEEK_END); |
305 | } while (ret != 0 && errno == EINTR); |
306 | |
307 | if (ret != 0) { |
308 | q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError, |
309 | QSystemError::stdString()); |
310 | |
311 | this->openMode = QIODevice::NotOpen; |
312 | this->fh = nullptr; |
313 | |
314 | return false; |
315 | } |
316 | } |
317 | |
318 | return true; |
319 | } |
320 | |
321 | /*! |
322 | Opens the file descriptor \a fd in \a openMode mode. Returns \c true |
323 | on success; otherwise returns \c false. |
324 | */ |
325 | bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd) |
326 | { |
327 | return open(openMode, fd, QFile::DontCloseHandle); |
328 | } |
329 | |
330 | bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd, QFile::FileHandleFlags handleFlags) |
331 | { |
332 | Q_D(QFSFileEngine); |
333 | |
334 | const ProcessOpenModeResult res = processOpenModeFlags(openMode); |
335 | if (!res.ok) { |
336 | setError(QFileDevice::OpenError, res.error); |
337 | return false; |
338 | } |
339 | |
340 | d->openMode = res.openMode; |
341 | d->lastFlushFailed = false; |
342 | d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); |
343 | d->fileEntry.clear(); |
344 | d->fh = nullptr; |
345 | d->fd = -1; |
346 | d->tried_stat = 0; |
347 | |
348 | return d->openFd(d->openMode, fd); |
349 | } |
350 | |
351 | |
352 | /*! |
353 | Opens the file descriptor \a fd to the file engine, using the open mode \a |
354 | flags. |
355 | */ |
356 | bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd) |
357 | { |
358 | Q_Q(QFSFileEngine); |
359 | this->fd = fd; |
360 | fh = nullptr; |
361 | |
362 | // Seek to the end when in Append mode. |
363 | if (openMode & QFile::Append) { |
364 | int ret; |
365 | do { |
366 | ret = QT_LSEEK(fd, 0, SEEK_END); |
367 | } while (ret == -1 && errno == EINTR); |
368 | |
369 | if (ret == -1) { |
370 | q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError, |
371 | QSystemError::stdString()); |
372 | |
373 | this->openMode = QIODevice::NotOpen; |
374 | this->fd = -1; |
375 | |
376 | return false; |
377 | } |
378 | } |
379 | |
380 | return true; |
381 | } |
382 | |
383 | /*! |
384 | \reimp |
385 | */ |
386 | bool QFSFileEngine::close() |
387 | { |
388 | Q_D(QFSFileEngine); |
389 | d->openMode = QIODevice::NotOpen; |
390 | return d->nativeClose(); |
391 | } |
392 | |
393 | /*! |
394 | \internal |
395 | */ |
396 | bool QFSFileEnginePrivate::closeFdFh() |
397 | { |
398 | Q_Q(QFSFileEngine); |
399 | if (fd == -1 && !fh) |
400 | return false; |
401 | |
402 | // Flush the file if it's buffered, and if the last flush didn't fail. |
403 | bool flushed = !fh || (!lastFlushFailed && q->flush()); |
404 | bool closed = true; |
405 | tried_stat = 0; |
406 | |
407 | // Close the file if we created the handle. |
408 | if (closeFileHandle) { |
409 | int ret; |
410 | |
411 | if (fh) { |
412 | // Close buffered file. |
413 | ret = fclose(fh); |
414 | } else { |
415 | // Close unbuffered file. |
416 | ret = QT_CLOSE(fd); |
417 | } |
418 | |
419 | // We must reset these guys regardless; calling close again after a |
420 | // failed close causes crashes on some systems. |
421 | fh = nullptr; |
422 | fd = -1; |
423 | closed = (ret == 0); |
424 | } |
425 | |
426 | // Report errors. |
427 | if (!flushed || !closed) { |
428 | if (flushed) { |
429 | // If not flushed, we want the flush error to fall through. |
430 | q->setError(QFile::UnspecifiedError, QSystemError::stdString()); |
431 | } |
432 | return false; |
433 | } |
434 | |
435 | return true; |
436 | } |
437 | |
438 | /*! |
439 | \reimp |
440 | */ |
441 | bool QFSFileEngine::flush() |
442 | { |
443 | Q_D(QFSFileEngine); |
444 | if ((d->openMode & QIODevice::WriteOnly) == 0) { |
445 | // Nothing in the write buffers, so flush succeeds in doing |
446 | // nothing. |
447 | return true; |
448 | } |
449 | return d->nativeFlush(); |
450 | } |
451 | |
452 | /*! |
453 | \reimp |
454 | */ |
455 | bool QFSFileEngine::syncToDisk() |
456 | { |
457 | Q_D(QFSFileEngine); |
458 | if ((d->openMode & QIODevice::WriteOnly) == 0) |
459 | return true; |
460 | return d->nativeSyncToDisk(); |
461 | } |
462 | |
463 | /*! |
464 | \internal |
465 | */ |
466 | bool QFSFileEnginePrivate::flushFh() |
467 | { |
468 | Q_Q(QFSFileEngine); |
469 | |
470 | // Never try to flush again if the last flush failed. Otherwise you can |
471 | // get crashes on some systems (AIX). |
472 | if (lastFlushFailed) |
473 | return false; |
474 | |
475 | int ret = fflush(fh); |
476 | |
477 | lastFlushFailed = (ret != 0); |
478 | lastIOCommand = QFSFileEnginePrivate::IOFlushCommand; |
479 | |
480 | if (ret != 0) { |
481 | q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, |
482 | QSystemError::stdString()); |
483 | return false; |
484 | } |
485 | return true; |
486 | } |
487 | |
488 | /*! |
489 | \reimp |
490 | */ |
491 | qint64 QFSFileEngine::size() const |
492 | { |
493 | Q_D(const QFSFileEngine); |
494 | return d->nativeSize(); |
495 | } |
496 | |
497 | /*! |
498 | \internal |
499 | */ |
500 | void QFSFileEnginePrivate::unmapAll() |
501 | { |
502 | if (!maps.isEmpty()) { |
503 | const QList<uchar*> keys = maps.keys(); // Make a copy since unmap() modifies the map. |
504 | for (int i = 0; i < keys.count(); ++i) |
505 | unmap(keys.at(i)); |
506 | } |
507 | } |
508 | |
509 | #ifndef Q_OS_WIN |
510 | /*! |
511 | \internal |
512 | */ |
513 | qint64 QFSFileEnginePrivate::sizeFdFh() const |
514 | { |
515 | Q_Q(const QFSFileEngine); |
516 | const_cast<QFSFileEngine *>(q)->flush(); |
517 | |
518 | tried_stat = 0; |
519 | metaData.clearFlags(QFileSystemMetaData::SizeAttribute); |
520 | if (!doStat(QFileSystemMetaData::SizeAttribute)) |
521 | return 0; |
522 | return metaData.size(); |
523 | } |
524 | #endif |
525 | |
526 | /*! |
527 | \reimp |
528 | */ |
529 | qint64 QFSFileEngine::pos() const |
530 | { |
531 | Q_D(const QFSFileEngine); |
532 | return d->nativePos(); |
533 | } |
534 | |
535 | /*! |
536 | \internal |
537 | */ |
538 | qint64 QFSFileEnginePrivate::posFdFh() const |
539 | { |
540 | if (fh) |
541 | return qint64(QT_FTELL(fh)); |
542 | return QT_LSEEK(fd, 0, SEEK_CUR); |
543 | } |
544 | |
545 | /*! |
546 | \reimp |
547 | */ |
548 | bool QFSFileEngine::seek(qint64 pos) |
549 | { |
550 | Q_D(QFSFileEngine); |
551 | return d->nativeSeek(pos); |
552 | } |
553 | |
554 | /*! |
555 | \reimp |
556 | */ |
557 | QDateTime QFSFileEngine::fileTime(FileTime time) const |
558 | { |
559 | Q_D(const QFSFileEngine); |
560 | |
561 | if (time == AccessTime) { |
562 | // always refresh for the access time |
563 | d->metaData.clearFlags(QFileSystemMetaData::AccessTime); |
564 | } |
565 | |
566 | if (d->doStat(QFileSystemMetaData::Times)) |
567 | return d->metaData.fileTime(time); |
568 | |
569 | return QDateTime(); |
570 | } |
571 | |
572 | |
573 | /*! |
574 | \internal |
575 | */ |
576 | bool QFSFileEnginePrivate::seekFdFh(qint64 pos) |
577 | { |
578 | Q_Q(QFSFileEngine); |
579 | |
580 | // On Windows' stdlib implementation, the results of calling fread and |
581 | // fwrite are undefined if not called either in sequence, or if preceded |
582 | // with a call to fflush(). |
583 | if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush()) |
584 | return false; |
585 | |
586 | if (pos < 0 || pos != qint64(QT_OFF_T(pos))) |
587 | return false; |
588 | |
589 | if (fh) { |
590 | // Buffered stdlib mode. |
591 | int ret; |
592 | do { |
593 | ret = QT_FSEEK(fh, QT_OFF_T(pos), SEEK_SET); |
594 | } while (ret != 0 && errno == EINTR); |
595 | |
596 | if (ret != 0) { |
597 | q->setError(QFile::ReadError, QSystemError::stdString()); |
598 | return false; |
599 | } |
600 | } else { |
601 | // Unbuffered stdio mode. |
602 | if (QT_LSEEK(fd, QT_OFF_T(pos), SEEK_SET) == -1) { |
603 | qWarning("QFile::at: Cannot set file position %lld" , pos); |
604 | q->setError(QFile::PositionError, QSystemError::stdString()); |
605 | return false; |
606 | } |
607 | } |
608 | return true; |
609 | } |
610 | |
611 | /*! |
612 | \reimp |
613 | */ |
614 | int QFSFileEngine::handle() const |
615 | { |
616 | Q_D(const QFSFileEngine); |
617 | return d->nativeHandle(); |
618 | } |
619 | |
620 | /*! |
621 | \reimp |
622 | */ |
623 | qint64 QFSFileEngine::read(char *data, qint64 maxlen) |
624 | { |
625 | Q_D(QFSFileEngine); |
626 | |
627 | // On Windows' stdlib implementation, the results of calling fread and |
628 | // fwrite are undefined if not called either in sequence, or if preceded |
629 | // with a call to fflush(). |
630 | if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) { |
631 | flush(); |
632 | d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand; |
633 | } |
634 | |
635 | return d->nativeRead(data, maxlen); |
636 | } |
637 | |
638 | /*! |
639 | \internal |
640 | */ |
641 | qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len) |
642 | { |
643 | Q_Q(QFSFileEngine); |
644 | |
645 | if (len < 0 || len != qint64(size_t(len))) { |
646 | q->setError(QFile::ReadError, QSystemError::stdString(EINVAL)); |
647 | return -1; |
648 | } |
649 | |
650 | qint64 readBytes = 0; |
651 | bool eof = false; |
652 | |
653 | if (fh) { |
654 | // Buffered stdlib mode. |
655 | |
656 | size_t result; |
657 | bool retry = true; |
658 | do { |
659 | result = fread(data + readBytes, 1, size_t(len - readBytes), fh); |
660 | eof = feof(fh); |
661 | if (retry && eof && result == 0) { |
662 | // On OS X, this is needed, e.g., if a file was written to |
663 | // through another stream since our last read. See test |
664 | // tst_QFile::appendAndRead |
665 | QT_FSEEK(fh, QT_FTELL(fh), SEEK_SET); // re-sync stream. |
666 | retry = false; |
667 | continue; |
668 | } |
669 | readBytes += result; |
670 | } while (!eof && (result == 0 ? errno == EINTR : readBytes < len)); |
671 | |
672 | } else if (fd != -1) { |
673 | // Unbuffered stdio mode. |
674 | |
675 | SignedIOType result; |
676 | do { |
677 | // calculate the chunk size |
678 | // on Windows or 32-bit no-largefile Unix, we'll need to read in chunks |
679 | // we limit to the size of the signed type, otherwise we could get a negative number as a result |
680 | quint64 wantedBytes = quint64(len) - quint64(readBytes); |
681 | UnsignedIOType chunkSize = std::numeric_limits<SignedIOType>::max(); |
682 | if (chunkSize > wantedBytes) |
683 | chunkSize = wantedBytes; |
684 | result = QT_READ(fd, data + readBytes, chunkSize); |
685 | } while (result > 0 && (readBytes += result) < len); |
686 | |
687 | eof = !(result == -1); |
688 | } |
689 | |
690 | if (!eof && readBytes == 0) { |
691 | readBytes = -1; |
692 | q->setError(QFile::ReadError, QSystemError::stdString()); |
693 | } |
694 | |
695 | return readBytes; |
696 | } |
697 | |
698 | /*! |
699 | \reimp |
700 | */ |
701 | qint64 QFSFileEngine::readLine(char *data, qint64 maxlen) |
702 | { |
703 | Q_D(QFSFileEngine); |
704 | |
705 | // On Windows' stdlib implementation, the results of calling fread and |
706 | // fwrite are undefined if not called either in sequence, or if preceded |
707 | // with a call to fflush(). |
708 | if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) { |
709 | flush(); |
710 | d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand; |
711 | } |
712 | |
713 | return d->nativeReadLine(data, maxlen); |
714 | } |
715 | |
716 | /*! |
717 | \internal |
718 | */ |
719 | qint64 QFSFileEnginePrivate::readLineFdFh(char *data, qint64 maxlen) |
720 | { |
721 | Q_Q(QFSFileEngine); |
722 | if (!fh) |
723 | return q->QAbstractFileEngine::readLine(data, maxlen); |
724 | |
725 | QT_OFF_T oldPos = 0; |
726 | #ifdef Q_OS_WIN |
727 | bool seq = q->isSequential(); |
728 | if (!seq) |
729 | #endif |
730 | oldPos = QT_FTELL(fh); |
731 | |
732 | // QIODevice::readLine() passes maxlen - 1 to QFile::readLineData() |
733 | // because it has made space for the '\0' at the end of data. But fgets |
734 | // does the same, so we'd get two '\0' at the end - passing maxlen + 1 |
735 | // solves this. |
736 | if (!fgets(data, int(maxlen + 1), fh)) { |
737 | if (!feof(fh)) |
738 | q->setError(QFile::ReadError, QSystemError::stdString()); |
739 | return -1; // error |
740 | } |
741 | |
742 | #ifdef Q_OS_WIN |
743 | if (seq) |
744 | return qstrlen(data); |
745 | #endif |
746 | |
747 | qint64 lineLength = QT_FTELL(fh) - oldPos; |
748 | return lineLength > 0 ? lineLength : qstrlen(data); |
749 | } |
750 | |
751 | /*! |
752 | \reimp |
753 | */ |
754 | qint64 QFSFileEngine::write(const char *data, qint64 len) |
755 | { |
756 | Q_D(QFSFileEngine); |
757 | d->metaData.clearFlags(QFileSystemMetaData::Times); |
758 | |
759 | // On Windows' stdlib implementation, the results of calling fread and |
760 | // fwrite are undefined if not called either in sequence, or if preceded |
761 | // with a call to fflush(). |
762 | if (d->lastIOCommand != QFSFileEnginePrivate::IOWriteCommand) { |
763 | flush(); |
764 | d->lastIOCommand = QFSFileEnginePrivate::IOWriteCommand; |
765 | } |
766 | |
767 | return d->nativeWrite(data, len); |
768 | } |
769 | |
770 | /*! |
771 | \internal |
772 | */ |
773 | qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len) |
774 | { |
775 | Q_Q(QFSFileEngine); |
776 | |
777 | if (len < 0 || len != qint64(size_t(len))) { |
778 | q->setError(QFile::WriteError, QSystemError::stdString(EINVAL)); |
779 | return -1; |
780 | } |
781 | |
782 | qint64 writtenBytes = 0; |
783 | |
784 | if (len) { // avoid passing nullptr to fwrite() or QT_WRITE() (UB) |
785 | |
786 | if (fh) { |
787 | // Buffered stdlib mode. |
788 | |
789 | size_t result; |
790 | do { |
791 | result = fwrite(data + writtenBytes, 1, size_t(len - writtenBytes), fh); |
792 | writtenBytes += result; |
793 | } while (result == 0 ? errno == EINTR : writtenBytes < len); |
794 | |
795 | } else if (fd != -1) { |
796 | // Unbuffered stdio mode. |
797 | |
798 | SignedIOType result; |
799 | do { |
800 | // calculate the chunk size |
801 | // on Windows or 32-bit no-largefile Unix, we'll need to read in chunks |
802 | // we limit to the size of the signed type, otherwise we could get a negative number as a result |
803 | quint64 wantedBytes = quint64(len) - quint64(writtenBytes); |
804 | UnsignedIOType chunkSize = std::numeric_limits<SignedIOType>::max(); |
805 | if (chunkSize > wantedBytes) |
806 | chunkSize = wantedBytes; |
807 | result = QT_WRITE(fd, data + writtenBytes, chunkSize); |
808 | } while (result > 0 && (writtenBytes += result) < len); |
809 | } |
810 | |
811 | } |
812 | |
813 | if (len && writtenBytes == 0) { |
814 | writtenBytes = -1; |
815 | q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, QSystemError::stdString()); |
816 | } else { |
817 | // reset the cached size, if any |
818 | metaData.clearFlags(QFileSystemMetaData::SizeAttribute); |
819 | } |
820 | |
821 | return writtenBytes; |
822 | } |
823 | |
824 | #ifndef QT_NO_FILESYSTEMITERATOR |
825 | /*! |
826 | \internal |
827 | */ |
828 | QAbstractFileEngine::Iterator *QFSFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) |
829 | { |
830 | return new QFSFileEngineIterator(filters, filterNames); |
831 | } |
832 | |
833 | /*! |
834 | \internal |
835 | */ |
836 | QAbstractFileEngine::Iterator *QFSFileEngine::endEntryList() |
837 | { |
838 | return nullptr; |
839 | } |
840 | #endif // QT_NO_FILESYSTEMITERATOR |
841 | |
842 | /*! |
843 | \internal |
844 | */ |
845 | QStringList QFSFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const |
846 | { |
847 | return QAbstractFileEngine::entryList(filters, filterNames); |
848 | } |
849 | |
850 | /*! |
851 | \reimp |
852 | */ |
853 | bool QFSFileEngine::isSequential() const |
854 | { |
855 | Q_D(const QFSFileEngine); |
856 | if (d->is_sequential == 0) |
857 | d->is_sequential = d->nativeIsSequential() ? 1 : 2; |
858 | return d->is_sequential == 1; |
859 | } |
860 | |
861 | /*! |
862 | \internal |
863 | */ |
864 | #ifdef Q_OS_UNIX |
865 | bool QFSFileEnginePrivate::isSequentialFdFh() const |
866 | { |
867 | if (doStat(QFileSystemMetaData::SequentialType)) |
868 | return metaData.isSequential(); |
869 | return true; |
870 | } |
871 | #endif |
872 | |
873 | /*! |
874 | \reimp |
875 | */ |
876 | bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) |
877 | { |
878 | Q_D(QFSFileEngine); |
879 | if (extension == AtEndExtension && d->fh && isSequential()) |
880 | return feof(d->fh); |
881 | |
882 | if (extension == MapExtension) { |
883 | const MapExtensionOption *options = (const MapExtensionOption*)(option); |
884 | MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output); |
885 | returnValue->address = d->map(options->offset, options->size, options->flags); |
886 | return (returnValue->address != nullptr); |
887 | } |
888 | if (extension == UnMapExtension) { |
889 | const UnMapExtensionOption *options = (const UnMapExtensionOption*)option; |
890 | return d->unmap(options->address); |
891 | } |
892 | |
893 | return false; |
894 | } |
895 | |
896 | /*! |
897 | \reimp |
898 | */ |
899 | bool QFSFileEngine::supportsExtension(Extension extension) const |
900 | { |
901 | Q_D(const QFSFileEngine); |
902 | if (extension == AtEndExtension && d->fh && isSequential()) |
903 | return true; |
904 | if (extension == FastReadLineExtension && d->fh) |
905 | return true; |
906 | if (extension == FastReadLineExtension && d->fd != -1 && isSequential()) |
907 | return true; |
908 | if (extension == UnMapExtension || extension == MapExtension) |
909 | return true; |
910 | return false; |
911 | } |
912 | |
913 | /*! \fn bool QFSFileEngine::caseSensitive() const |
914 | Returns \c false for Windows, true for Unix. |
915 | */ |
916 | |
917 | /*! \fn QString QFSFileEngine::currentPath(const QString &fileName) |
918 | For Unix, returns the current working directory for the file |
919 | engine. |
920 | |
921 | For Windows, returns the canonicalized form of the current path used |
922 | by the file engine for the drive specified by \a fileName. On |
923 | Windows, each drive has its own current directory, so a different |
924 | path is returned for file names that include different drive names |
925 | (e.g. A: or C:). |
926 | |
927 | \sa setCurrentPath() |
928 | */ |
929 | |
930 | /*! \fn QFileInfoList QFSFileEngine::drives() |
931 | For Windows, returns the list of drives in the file system as a list |
932 | of QFileInfo objects. On Unix, only the root path is returned. |
933 | On Windows, this function returns all drives (A:\, C:\, D:\, and so on). |
934 | |
935 | For Unix, the list contains just the root path "/". |
936 | */ |
937 | |
938 | /*! \fn QString QFSFileEngine::fileName(QAbstractFileEngine::FileName file) const |
939 | \reimp |
940 | */ |
941 | |
942 | /*! \fn bool QFSFileEngine::setFileTime(const QDateTime &newDate, QAbstractFileEngine::FileTime time) |
943 | \reimp |
944 | */ |
945 | |
946 | /*! |
947 | Returns the home path of the current user. |
948 | |
949 | \sa rootPath() |
950 | */ |
951 | QString QFSFileEngine::homePath() |
952 | { |
953 | return QFileSystemEngine::homePath(); |
954 | } |
955 | |
956 | /*! |
957 | Returns the root path. |
958 | |
959 | \sa homePath() |
960 | */ |
961 | QString QFSFileEngine::rootPath() |
962 | { |
963 | return QFileSystemEngine::rootPath(); |
964 | } |
965 | |
966 | /*! |
967 | Returns the temporary path (i.e., a path in which it is safe |
968 | to store temporary files). |
969 | */ |
970 | QString QFSFileEngine::tempPath() |
971 | { |
972 | return QFileSystemEngine::tempPath(); |
973 | } |
974 | |
975 | /*! \fn bool QFSFileEngine::isRelativePath() const |
976 | \reimp |
977 | */ |
978 | |
979 | /*! \fn bool QFSFileEngine::link(const QString &newName) |
980 | |
981 | Creates a link from the file currently specified by fileName() to |
982 | \a newName. What a link is depends on the underlying filesystem |
983 | (be it a shortcut on Windows or a symbolic link on Unix). Returns |
984 | true if successful; otherwise returns \c false. |
985 | */ |
986 | |
987 | |
988 | /*! \fn uint QFSFileEngine::ownerId(QAbstractFileEngine::FileOwner own) const |
989 | In Unix, if stat() is successful, the \c uid is returned if |
990 | \a own is the owner. Otherwise the \c gid is returned. If stat() |
991 | is unsuccessful, -2 is reuturned. |
992 | |
993 | For Windows, -2 is always returned. |
994 | */ |
995 | |
996 | /*! \fn QString QFSFileEngine::owner(QAbstractFileEngine::FileOwner own) const |
997 | \reimp |
998 | */ |
999 | |
1000 | /*! |
1001 | For Windows or Apple platforms, copy the file to file \a copyName. |
1002 | |
1003 | Not implemented for other Unix platforms. |
1004 | */ |
1005 | bool QFSFileEngine::copy(const QString ©Name) |
1006 | { |
1007 | Q_D(QFSFileEngine); |
1008 | QSystemError error; |
1009 | bool ret = QFileSystemEngine::copyFile(d->fileEntry, QFileSystemEntry(copyName), error); |
1010 | if (!ret) |
1011 | setError(QFile::CopyError, error.toString()); |
1012 | return ret; |
1013 | } |
1014 | |
1015 | /*! |
1016 | \reimp |
1017 | */ |
1018 | bool QFSFileEngine::remove() |
1019 | { |
1020 | Q_D(QFSFileEngine); |
1021 | QSystemError error; |
1022 | bool ret = QFileSystemEngine::removeFile(d->fileEntry, error); |
1023 | d->metaData.clear(); |
1024 | if (!ret) |
1025 | setError(QFile::RemoveError, error.toString()); |
1026 | return ret; |
1027 | } |
1028 | |
1029 | /*! |
1030 | \reimp |
1031 | */ |
1032 | bool QFSFileEngine::rename(const QString &newName) |
1033 | { |
1034 | Q_D(QFSFileEngine); |
1035 | QSystemError error; |
1036 | bool ret = QFileSystemEngine::renameFile(d->fileEntry, QFileSystemEntry(newName), error); |
1037 | if (!ret) |
1038 | setError(QFile::RenameError, error.toString()); |
1039 | return ret; |
1040 | } |
1041 | /*! |
1042 | \reimp |
1043 | */ |
1044 | bool QFSFileEngine::renameOverwrite(const QString &newName) |
1045 | { |
1046 | Q_D(QFSFileEngine); |
1047 | QSystemError error; |
1048 | bool ret = QFileSystemEngine::renameOverwriteFile(d->fileEntry, QFileSystemEntry(newName), error); |
1049 | if (!ret) |
1050 | setError(QFile::RenameError, error.toString()); |
1051 | return ret; |
1052 | } |
1053 | |
1054 | /*! |
1055 | \reimp |
1056 | */ |
1057 | bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const |
1058 | { |
1059 | return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories); |
1060 | } |
1061 | |
1062 | /*! |
1063 | \reimp |
1064 | */ |
1065 | bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const |
1066 | { |
1067 | return QFileSystemEngine::removeDirectory(QFileSystemEntry(name), recurseParentDirectories); |
1068 | } |
1069 | |
1070 | |
1071 | /*! |
1072 | Sets the current path (e.g., for QDir), to \a path. Returns \c true if the |
1073 | new path exists; otherwise this function does nothing, and returns \c false. |
1074 | |
1075 | \sa currentPath() |
1076 | */ |
1077 | bool QFSFileEngine::setCurrentPath(const QString &path) |
1078 | { |
1079 | return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path)); |
1080 | } |
1081 | |
1082 | /*! \fn bool QFSFileEngine::setPermissions(uint perms) |
1083 | \reimp |
1084 | */ |
1085 | |
1086 | /*! \fn bool QFSFileEngine::setSize(qint64 size) |
1087 | \reimp |
1088 | */ |
1089 | |
1090 | /*! \fn QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions(QAbstractFileEngine::FileFlags type) const |
1091 | \internal |
1092 | */ |
1093 | |
1094 | QT_END_NAMESPACE |
1095 | |
1096 | #endif // QT_NO_FSFILEENGINE |
1097 | |