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