/src/qtbase/src/corelib/io/qfsfileengine.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | Q_STATIC_ASSERT_X(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 | 1 | { |
116 | 1 | init(); |
117 | 1 | } |
118 | | |
119 | | /*! |
120 | | \internal |
121 | | */ |
122 | | void QFSFileEnginePrivate::init() |
123 | 1 | { |
124 | 1 | is_sequential = 0; |
125 | 1 | tried_stat = 0; |
126 | 1 | need_lstat = 1; |
127 | 1 | is_link = 0; |
128 | 1 | openMode = QIODevice::NotOpen; |
129 | 1 | fd = -1; |
130 | 1 | fh = nullptr; |
131 | 1 | lastIOCommand = IOFlushCommand; |
132 | 1 | lastFlushFailed = false; |
133 | 1 | 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 | 1 | } |
141 | | |
142 | | /*! |
143 | | Constructs a QFSFileEngine for the file name \a file. |
144 | | */ |
145 | | QFSFileEngine::QFSFileEngine(const QString &file) |
146 | | : QAbstractFileEngine(*new QFSFileEnginePrivate) |
147 | 1 | { |
148 | 1 | Q_D(QFSFileEngine); |
149 | 1 | d->fileEntry = QFileSystemEntry(file); |
150 | 1 | } |
151 | | |
152 | | /*! |
153 | | Constructs a QFSFileEngine. |
154 | | */ |
155 | | QFSFileEngine::QFSFileEngine() : QAbstractFileEngine(*new QFSFileEnginePrivate) |
156 | 0 | { |
157 | 0 | } |
158 | | |
159 | | /*! |
160 | | \internal |
161 | | */ |
162 | | QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd) |
163 | | : QAbstractFileEngine(dd) |
164 | 0 | { |
165 | 0 | } |
166 | | |
167 | | /*! |
168 | | \internal |
169 | | */ |
170 | | ProcessOpenModeResult processOpenModeFlags(QIODevice::OpenMode openMode) |
171 | 1 | { |
172 | 1 | ProcessOpenModeResult result; |
173 | 1 | result.ok = false; |
174 | 1 | if ((openMode & QFile::NewOnly) && (openMode & QFile::ExistingOnly)) { |
175 | 0 | qWarning("NewOnly and ExistingOnly are mutually exclusive"); |
176 | 0 | result.error = QLatin1String("NewOnly and ExistingOnly are mutually exclusive"); |
177 | 0 | return result; |
178 | 0 | } |
179 | | |
180 | 1 | if ((openMode & QFile::ExistingOnly) && !(openMode & (QFile::ReadOnly | QFile::WriteOnly))) { |
181 | 0 | qWarning("ExistingOnly must be specified alongside ReadOnly, WriteOnly, or ReadWrite"); |
182 | 0 | result.error = QLatin1String( |
183 | 0 | "ExistingOnly must be specified alongside ReadOnly, WriteOnly, or ReadWrite"); |
184 | 0 | return result; |
185 | 0 | } |
186 | | |
187 | | // Either Append or NewOnly implies WriteOnly |
188 | 1 | if (openMode & (QFile::Append | QFile::NewOnly)) |
189 | 0 | openMode |= QFile::WriteOnly; |
190 | | |
191 | | // WriteOnly implies Truncate when ReadOnly, Append, and NewOnly are not set. |
192 | 1 | if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append | QFile::NewOnly))) |
193 | 0 | openMode |= QFile::Truncate; |
194 | | |
195 | 1 | result.ok = true; |
196 | 1 | result.openMode = openMode; |
197 | 1 | return result; |
198 | 1 | } |
199 | | |
200 | | /*! |
201 | | Destructs the QFSFileEngine. |
202 | | */ |
203 | | QFSFileEngine::~QFSFileEngine() |
204 | 1 | { |
205 | 1 | Q_D(QFSFileEngine); |
206 | 1 | if (d->closeFileHandle) { |
207 | 0 | if (d->fh) { |
208 | 0 | fclose(d->fh); |
209 | 0 | } else if (d->fd != -1) { |
210 | 0 | QT_CLOSE(d->fd); |
211 | 0 | } |
212 | 0 | } |
213 | 1 | d->unmapAll(); |
214 | 1 | } |
215 | | |
216 | | /*! |
217 | | \reimp |
218 | | */ |
219 | | void QFSFileEngine::setFileName(const QString &file) |
220 | 0 | { |
221 | 0 | Q_D(QFSFileEngine); |
222 | 0 | d->init(); |
223 | 0 | d->fileEntry = QFileSystemEntry(file); |
224 | 0 | } |
225 | | |
226 | | /*! |
227 | | \reimp |
228 | | */ |
229 | | bool QFSFileEngine::open(QIODevice::OpenMode openMode) |
230 | 1 | { |
231 | 1 | Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open", |
232 | 1 | "QFSFileEngine no longer supports buffered mode; upper layer must buffer"); |
233 | | |
234 | 1 | Q_D(QFSFileEngine); |
235 | 1 | if (d->fileEntry.isEmpty()) { |
236 | 0 | qWarning("QFSFileEngine::open: No file name specified"); |
237 | 0 | setError(QFile::OpenError, QLatin1String("No file name specified")); |
238 | 0 | return false; |
239 | 0 | } |
240 | | |
241 | 1 | const ProcessOpenModeResult res = processOpenModeFlags(openMode); |
242 | 1 | if (!res.ok) { |
243 | 0 | setError(QFileDevice::OpenError, res.error); |
244 | 0 | return false; |
245 | 0 | } |
246 | | |
247 | 1 | d->openMode = res.openMode; |
248 | 1 | d->lastFlushFailed = false; |
249 | 1 | d->tried_stat = 0; |
250 | 1 | d->fh = nullptr; |
251 | 1 | d->fd = -1; |
252 | | |
253 | 1 | return d->nativeOpen(d->openMode); |
254 | 1 | } |
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 | 0 | { |
262 | 0 | return open(openMode, fh, QFile::DontCloseHandle); |
263 | 0 | } |
264 | | |
265 | | bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHandleFlags handleFlags) |
266 | 0 | { |
267 | 0 | Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open", |
268 | 0 | "QFSFileEngine no longer supports buffered mode; upper layer must buffer"); |
269 | |
|
270 | 0 | Q_D(QFSFileEngine); |
271 | |
|
272 | 0 | const ProcessOpenModeResult res = processOpenModeFlags(openMode); |
273 | 0 | if (!res.ok) { |
274 | 0 | setError(QFileDevice::OpenError, res.error); |
275 | 0 | return false; |
276 | 0 | } |
277 | | |
278 | 0 | d->openMode = res.openMode; |
279 | 0 | d->lastFlushFailed = false; |
280 | 0 | d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); |
281 | 0 | d->fileEntry.clear(); |
282 | 0 | d->tried_stat = 0; |
283 | 0 | d->fd = -1; |
284 | |
|
285 | 0 | return d->openFh(d->openMode, fh); |
286 | 0 | } |
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 | 0 | { |
293 | 0 | Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open", |
294 | 0 | "QFSFileEngine no longer supports buffered mode; upper layer must buffer"); |
295 | |
|
296 | 0 | Q_Q(QFSFileEngine); |
297 | 0 | this->fh = fh; |
298 | 0 | fd = -1; |
299 | | |
300 | | // Seek to the end when in Append mode. |
301 | 0 | if (openMode & QIODevice::Append) { |
302 | 0 | int ret; |
303 | 0 | do { |
304 | 0 | ret = QT_FSEEK(fh, 0, SEEK_END); |
305 | 0 | } while (ret != 0 && errno == EINTR); |
306 | |
|
307 | 0 | if (ret != 0) { |
308 | 0 | q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError, |
309 | 0 | QSystemError::stdString()); |
310 | |
|
311 | 0 | this->openMode = QIODevice::NotOpen; |
312 | 0 | this->fh = nullptr; |
313 | |
|
314 | 0 | return false; |
315 | 0 | } |
316 | 0 | } |
317 | | |
318 | 0 | return true; |
319 | 0 | } |
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 | 0 | { |
327 | 0 | return open(openMode, fd, QFile::DontCloseHandle); |
328 | 0 | } |
329 | | |
330 | | bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd, QFile::FileHandleFlags handleFlags) |
331 | 0 | { |
332 | 0 | Q_D(QFSFileEngine); |
333 | |
|
334 | 0 | const ProcessOpenModeResult res = processOpenModeFlags(openMode); |
335 | 0 | if (!res.ok) { |
336 | 0 | setError(QFileDevice::OpenError, res.error); |
337 | 0 | return false; |
338 | 0 | } |
339 | | |
340 | 0 | d->openMode = res.openMode; |
341 | 0 | d->lastFlushFailed = false; |
342 | 0 | d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); |
343 | 0 | d->fileEntry.clear(); |
344 | 0 | d->fh = nullptr; |
345 | 0 | d->fd = -1; |
346 | 0 | d->tried_stat = 0; |
347 | |
|
348 | 0 | return d->openFd(d->openMode, fd); |
349 | 0 | } |
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 | 0 | { |
358 | 0 | Q_Q(QFSFileEngine); |
359 | 0 | this->fd = fd; |
360 | 0 | fh = nullptr; |
361 | | |
362 | | // Seek to the end when in Append mode. |
363 | 0 | if (openMode & QFile::Append) { |
364 | 0 | int ret; |
365 | 0 | do { |
366 | 0 | ret = QT_LSEEK(fd, 0, SEEK_END); |
367 | 0 | } while (ret == -1 && errno == EINTR); |
368 | |
|
369 | 0 | if (ret == -1) { |
370 | 0 | q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError, |
371 | 0 | QSystemError::stdString()); |
372 | |
|
373 | 0 | this->openMode = QIODevice::NotOpen; |
374 | 0 | this->fd = -1; |
375 | |
|
376 | 0 | return false; |
377 | 0 | } |
378 | 0 | } |
379 | | |
380 | 0 | return true; |
381 | 0 | } |
382 | | |
383 | | /*! |
384 | | \reimp |
385 | | */ |
386 | | bool QFSFileEngine::close() |
387 | 0 | { |
388 | 0 | Q_D(QFSFileEngine); |
389 | 0 | d->openMode = QIODevice::NotOpen; |
390 | 0 | return d->nativeClose(); |
391 | 0 | } |
392 | | |
393 | | /*! |
394 | | \internal |
395 | | */ |
396 | | bool QFSFileEnginePrivate::closeFdFh() |
397 | 0 | { |
398 | 0 | Q_Q(QFSFileEngine); |
399 | 0 | if (fd == -1 && !fh) |
400 | 0 | return false; |
401 | | |
402 | | // Flush the file if it's buffered, and if the last flush didn't fail. |
403 | 0 | bool flushed = !fh || (!lastFlushFailed && q->flush()); |
404 | 0 | bool closed = true; |
405 | 0 | tried_stat = 0; |
406 | | |
407 | | // Close the file if we created the handle. |
408 | 0 | if (closeFileHandle) { |
409 | 0 | int ret; |
410 | |
|
411 | 0 | if (fh) { |
412 | | // Close buffered file. |
413 | 0 | ret = fclose(fh); |
414 | 0 | } else { |
415 | | // Close unbuffered file. |
416 | 0 | ret = QT_CLOSE(fd); |
417 | 0 | } |
418 | | |
419 | | // We must reset these guys regardless; calling close again after a |
420 | | // failed close causes crashes on some systems. |
421 | 0 | fh = nullptr; |
422 | 0 | fd = -1; |
423 | 0 | closed = (ret == 0); |
424 | 0 | } |
425 | | |
426 | | // Report errors. |
427 | 0 | if (!flushed || !closed) { |
428 | 0 | if (flushed) { |
429 | | // If not flushed, we want the flush error to fall through. |
430 | 0 | q->setError(QFile::UnspecifiedError, QSystemError::stdString()); |
431 | 0 | } |
432 | 0 | return false; |
433 | 0 | } |
434 | | |
435 | 0 | return true; |
436 | 0 | } |
437 | | |
438 | | /*! |
439 | | \reimp |
440 | | */ |
441 | | bool QFSFileEngine::flush() |
442 | 0 | { |
443 | 0 | Q_D(QFSFileEngine); |
444 | 0 | if ((d->openMode & QIODevice::WriteOnly) == 0) { |
445 | | // Nothing in the write buffers, so flush succeeds in doing |
446 | | // nothing. |
447 | 0 | return true; |
448 | 0 | } |
449 | 0 | return d->nativeFlush(); |
450 | 0 | } |
451 | | |
452 | | /*! |
453 | | \reimp |
454 | | */ |
455 | | bool QFSFileEngine::syncToDisk() |
456 | 0 | { |
457 | 0 | Q_D(QFSFileEngine); |
458 | 0 | if ((d->openMode & QIODevice::WriteOnly) == 0) |
459 | 0 | return true; |
460 | 0 | return d->nativeSyncToDisk(); |
461 | 0 | } |
462 | | |
463 | | /*! |
464 | | \internal |
465 | | */ |
466 | | bool QFSFileEnginePrivate::flushFh() |
467 | 0 | { |
468 | 0 | 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 | 0 | if (lastFlushFailed) |
473 | 0 | return false; |
474 | | |
475 | 0 | int ret = fflush(fh); |
476 | |
|
477 | 0 | lastFlushFailed = (ret != 0); |
478 | 0 | lastIOCommand = QFSFileEnginePrivate::IOFlushCommand; |
479 | |
|
480 | 0 | if (ret != 0) { |
481 | 0 | q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, |
482 | 0 | QSystemError::stdString()); |
483 | 0 | return false; |
484 | 0 | } |
485 | 0 | return true; |
486 | 0 | } |
487 | | |
488 | | /*! |
489 | | \reimp |
490 | | */ |
491 | | qint64 QFSFileEngine::size() const |
492 | 0 | { |
493 | 0 | Q_D(const QFSFileEngine); |
494 | 0 | return d->nativeSize(); |
495 | 0 | } |
496 | | |
497 | | /*! |
498 | | \internal |
499 | | */ |
500 | | void QFSFileEnginePrivate::unmapAll() |
501 | 1 | { |
502 | 1 | if (!maps.isEmpty()) { |
503 | 0 | const QList<uchar*> keys = maps.keys(); // Make a copy since unmap() modifies the map. |
504 | 0 | for (int i = 0; i < keys.count(); ++i) |
505 | 0 | unmap(keys.at(i)); |
506 | 0 | } |
507 | 1 | } |
508 | | |
509 | | #ifndef Q_OS_WIN |
510 | | /*! |
511 | | \internal |
512 | | */ |
513 | | qint64 QFSFileEnginePrivate::sizeFdFh() const |
514 | 0 | { |
515 | 0 | Q_Q(const QFSFileEngine); |
516 | 0 | const_cast<QFSFileEngine *>(q)->flush(); |
517 | |
|
518 | 0 | tried_stat = 0; |
519 | 0 | metaData.clearFlags(QFileSystemMetaData::SizeAttribute); |
520 | 0 | if (!doStat(QFileSystemMetaData::SizeAttribute)) |
521 | 0 | return 0; |
522 | 0 | return metaData.size(); |
523 | 0 | } |
524 | | #endif |
525 | | |
526 | | /*! |
527 | | \reimp |
528 | | */ |
529 | | qint64 QFSFileEngine::pos() const |
530 | 0 | { |
531 | 0 | Q_D(const QFSFileEngine); |
532 | 0 | return d->nativePos(); |
533 | 0 | } |
534 | | |
535 | | /*! |
536 | | \internal |
537 | | */ |
538 | | qint64 QFSFileEnginePrivate::posFdFh() const |
539 | 0 | { |
540 | 0 | if (fh) |
541 | 0 | return qint64(QT_FTELL(fh)); |
542 | 0 | return QT_LSEEK(fd, 0, SEEK_CUR); |
543 | 0 | } |
544 | | |
545 | | /*! |
546 | | \reimp |
547 | | */ |
548 | | bool QFSFileEngine::seek(qint64 pos) |
549 | 0 | { |
550 | 0 | Q_D(QFSFileEngine); |
551 | 0 | return d->nativeSeek(pos); |
552 | 0 | } |
553 | | |
554 | | /*! |
555 | | \reimp |
556 | | */ |
557 | | QDateTime QFSFileEngine::fileTime(FileTime time) const |
558 | 0 | { |
559 | 0 | Q_D(const QFSFileEngine); |
560 | |
|
561 | 0 | if (time == AccessTime) { |
562 | | // always refresh for the access time |
563 | 0 | d->metaData.clearFlags(QFileSystemMetaData::AccessTime); |
564 | 0 | } |
565 | |
|
566 | 0 | if (d->doStat(QFileSystemMetaData::Times)) |
567 | 0 | return d->metaData.fileTime(time); |
568 | | |
569 | 0 | return QDateTime(); |
570 | 0 | } |
571 | | |
572 | | |
573 | | /*! |
574 | | \internal |
575 | | */ |
576 | | bool QFSFileEnginePrivate::seekFdFh(qint64 pos) |
577 | 0 | { |
578 | 0 | 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 | 0 | if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush()) |
584 | 0 | return false; |
585 | | |
586 | 0 | if (pos < 0 || pos != qint64(QT_OFF_T(pos))) |
587 | 0 | return false; |
588 | | |
589 | 0 | if (fh) { |
590 | | // Buffered stdlib mode. |
591 | 0 | int ret; |
592 | 0 | do { |
593 | 0 | ret = QT_FSEEK(fh, QT_OFF_T(pos), SEEK_SET); |
594 | 0 | } while (ret != 0 && errno == EINTR); |
595 | |
|
596 | 0 | if (ret != 0) { |
597 | 0 | q->setError(QFile::ReadError, QSystemError::stdString()); |
598 | 0 | return false; |
599 | 0 | } |
600 | 0 | } else { |
601 | | // Unbuffered stdio mode. |
602 | 0 | if (QT_LSEEK(fd, QT_OFF_T(pos), SEEK_SET) == -1) { |
603 | 0 | qWarning("QFile::at: Cannot set file position %lld", pos); |
604 | 0 | q->setError(QFile::PositionError, QSystemError::stdString()); |
605 | 0 | return false; |
606 | 0 | } |
607 | 0 | } |
608 | 0 | return true; |
609 | 0 | } |
610 | | |
611 | | /*! |
612 | | \reimp |
613 | | */ |
614 | | int QFSFileEngine::handle() const |
615 | 0 | { |
616 | 0 | Q_D(const QFSFileEngine); |
617 | 0 | return d->nativeHandle(); |
618 | 0 | } |
619 | | |
620 | | /*! |
621 | | \reimp |
622 | | */ |
623 | | qint64 QFSFileEngine::read(char *data, qint64 maxlen) |
624 | 0 | { |
625 | 0 | 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 | 0 | if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) { |
631 | 0 | flush(); |
632 | 0 | d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand; |
633 | 0 | } |
634 | |
|
635 | 0 | return d->nativeRead(data, maxlen); |
636 | 0 | } |
637 | | |
638 | | /*! |
639 | | \internal |
640 | | */ |
641 | | qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len) |
642 | 0 | { |
643 | 0 | Q_Q(QFSFileEngine); |
644 | |
|
645 | 0 | if (len < 0 || len != qint64(size_t(len))) { |
646 | 0 | q->setError(QFile::ReadError, QSystemError::stdString(EINVAL)); |
647 | 0 | return -1; |
648 | 0 | } |
649 | | |
650 | 0 | qint64 readBytes = 0; |
651 | 0 | bool eof = false; |
652 | |
|
653 | 0 | if (fh) { |
654 | | // Buffered stdlib mode. |
655 | |
|
656 | 0 | size_t result; |
657 | 0 | bool retry = true; |
658 | 0 | do { |
659 | 0 | result = fread(data + readBytes, 1, size_t(len - readBytes), fh); |
660 | 0 | eof = feof(fh); |
661 | 0 | 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 | 0 | QT_FSEEK(fh, QT_FTELL(fh), SEEK_SET); // re-sync stream. |
666 | 0 | retry = false; |
667 | 0 | continue; |
668 | 0 | } |
669 | 0 | readBytes += result; |
670 | 0 | } while (!eof && (result == 0 ? errno == EINTR : readBytes < len)); |
671 | |
|
672 | 0 | } else if (fd != -1) { |
673 | | // Unbuffered stdio mode. |
674 | |
|
675 | 0 | SignedIOType result; |
676 | 0 | 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 | 0 | quint64 wantedBytes = quint64(len) - quint64(readBytes); |
681 | 0 | UnsignedIOType chunkSize = std::numeric_limits<SignedIOType>::max(); |
682 | 0 | if (chunkSize > wantedBytes) |
683 | 0 | chunkSize = wantedBytes; |
684 | 0 | result = QT_READ(fd, data + readBytes, chunkSize); |
685 | 0 | } while (result > 0 && (readBytes += result) < len); |
686 | |
|
687 | 0 | eof = !(result == -1); |
688 | 0 | } |
689 | | |
690 | 0 | if (!eof && readBytes == 0) { |
691 | 0 | readBytes = -1; |
692 | 0 | q->setError(QFile::ReadError, QSystemError::stdString()); |
693 | 0 | } |
694 | |
|
695 | 0 | return readBytes; |
696 | 0 | } |
697 | | |
698 | | /*! |
699 | | \reimp |
700 | | */ |
701 | | qint64 QFSFileEngine::readLine(char *data, qint64 maxlen) |
702 | 0 | { |
703 | 0 | 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 | 0 | if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) { |
709 | 0 | flush(); |
710 | 0 | d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand; |
711 | 0 | } |
712 | |
|
713 | 0 | return d->nativeReadLine(data, maxlen); |
714 | 0 | } |
715 | | |
716 | | /*! |
717 | | \internal |
718 | | */ |
719 | | qint64 QFSFileEnginePrivate::readLineFdFh(char *data, qint64 maxlen) |
720 | 0 | { |
721 | 0 | Q_Q(QFSFileEngine); |
722 | 0 | if (!fh) |
723 | 0 | return q->QAbstractFileEngine::readLine(data, maxlen); |
724 | | |
725 | 0 | QT_OFF_T oldPos = 0; |
726 | | #ifdef Q_OS_WIN |
727 | | bool seq = q->isSequential(); |
728 | | if (!seq) |
729 | | #endif |
730 | 0 | 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 | 0 | if (!fgets(data, int(maxlen + 1), fh)) { |
737 | 0 | if (!feof(fh)) |
738 | 0 | q->setError(QFile::ReadError, QSystemError::stdString()); |
739 | 0 | return -1; // error |
740 | 0 | } |
741 | | |
742 | | #ifdef Q_OS_WIN |
743 | | if (seq) |
744 | | return qstrlen(data); |
745 | | #endif |
746 | | |
747 | 0 | qint64 lineLength = QT_FTELL(fh) - oldPos; |
748 | 0 | return lineLength > 0 ? lineLength : qstrlen(data); |
749 | 0 | } |
750 | | |
751 | | /*! |
752 | | \reimp |
753 | | */ |
754 | | qint64 QFSFileEngine::write(const char *data, qint64 len) |
755 | 0 | { |
756 | 0 | Q_D(QFSFileEngine); |
757 | 0 | 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 | 0 | if (d->lastIOCommand != QFSFileEnginePrivate::IOWriteCommand) { |
763 | 0 | flush(); |
764 | 0 | d->lastIOCommand = QFSFileEnginePrivate::IOWriteCommand; |
765 | 0 | } |
766 | |
|
767 | 0 | return d->nativeWrite(data, len); |
768 | 0 | } |
769 | | |
770 | | /*! |
771 | | \internal |
772 | | */ |
773 | | qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len) |
774 | 0 | { |
775 | 0 | Q_Q(QFSFileEngine); |
776 | |
|
777 | 0 | if (len < 0 || len != qint64(size_t(len))) { |
778 | 0 | q->setError(QFile::WriteError, QSystemError::stdString(EINVAL)); |
779 | 0 | return -1; |
780 | 0 | } |
781 | | |
782 | 0 | qint64 writtenBytes = 0; |
783 | |
|
784 | 0 | if (len) { // avoid passing nullptr to fwrite() or QT_WRITE() (UB) |
785 | |
|
786 | 0 | if (fh) { |
787 | | // Buffered stdlib mode. |
788 | |
|
789 | 0 | size_t result; |
790 | 0 | do { |
791 | 0 | result = fwrite(data + writtenBytes, 1, size_t(len - writtenBytes), fh); |
792 | 0 | writtenBytes += result; |
793 | 0 | } while (result == 0 ? errno == EINTR : writtenBytes < len); |
794 | |
|
795 | 0 | } else if (fd != -1) { |
796 | | // Unbuffered stdio mode. |
797 | |
|
798 | 0 | SignedIOType result; |
799 | 0 | 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 | 0 | quint64 wantedBytes = quint64(len) - quint64(writtenBytes); |
804 | 0 | UnsignedIOType chunkSize = std::numeric_limits<SignedIOType>::max(); |
805 | 0 | if (chunkSize > wantedBytes) |
806 | 0 | chunkSize = wantedBytes; |
807 | 0 | result = QT_WRITE(fd, data + writtenBytes, chunkSize); |
808 | 0 | } while (result > 0 && (writtenBytes += result) < len); |
809 | 0 | } |
810 | |
|
811 | 0 | } |
812 | |
|
813 | 0 | if (len && writtenBytes == 0) { |
814 | 0 | writtenBytes = -1; |
815 | 0 | q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, QSystemError::stdString()); |
816 | 0 | } else { |
817 | | // reset the cached size, if any |
818 | 0 | metaData.clearFlags(QFileSystemMetaData::SizeAttribute); |
819 | 0 | } |
820 | |
|
821 | 0 | return writtenBytes; |
822 | 0 | } |
823 | | |
824 | | #ifndef QT_NO_FILESYSTEMITERATOR |
825 | | /*! |
826 | | \internal |
827 | | */ |
828 | | QAbstractFileEngine::Iterator *QFSFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) |
829 | 0 | { |
830 | 0 | return new QFSFileEngineIterator(filters, filterNames); |
831 | 0 | } |
832 | | |
833 | | /*! |
834 | | \internal |
835 | | */ |
836 | | QAbstractFileEngine::Iterator *QFSFileEngine::endEntryList() |
837 | 0 | { |
838 | 0 | return nullptr; |
839 | 0 | } |
840 | | #endif // QT_NO_FILESYSTEMITERATOR |
841 | | |
842 | | /*! |
843 | | \internal |
844 | | */ |
845 | | QStringList QFSFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const |
846 | 0 | { |
847 | 0 | return QAbstractFileEngine::entryList(filters, filterNames); |
848 | 0 | } |
849 | | |
850 | | /*! |
851 | | \reimp |
852 | | */ |
853 | | bool QFSFileEngine::isSequential() const |
854 | 0 | { |
855 | 0 | Q_D(const QFSFileEngine); |
856 | 0 | if (d->is_sequential == 0) |
857 | 0 | d->is_sequential = d->nativeIsSequential() ? 1 : 2; |
858 | 0 | return d->is_sequential == 1; |
859 | 0 | } |
860 | | |
861 | | /*! |
862 | | \internal |
863 | | */ |
864 | | #ifdef Q_OS_UNIX |
865 | | bool QFSFileEnginePrivate::isSequentialFdFh() const |
866 | 0 | { |
867 | 0 | if (doStat(QFileSystemMetaData::SequentialType)) |
868 | 0 | return metaData.isSequential(); |
869 | 0 | return true; |
870 | 0 | } |
871 | | #endif |
872 | | |
873 | | /*! |
874 | | \reimp |
875 | | */ |
876 | | bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) |
877 | 0 | { |
878 | 0 | Q_D(QFSFileEngine); |
879 | 0 | if (extension == AtEndExtension && d->fh && isSequential()) |
880 | 0 | return feof(d->fh); |
881 | | |
882 | 0 | if (extension == MapExtension) { |
883 | 0 | const MapExtensionOption *options = (const MapExtensionOption*)(option); |
884 | 0 | MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output); |
885 | 0 | returnValue->address = d->map(options->offset, options->size, options->flags); |
886 | 0 | return (returnValue->address != nullptr); |
887 | 0 | } |
888 | 0 | if (extension == UnMapExtension) { |
889 | 0 | const UnMapExtensionOption *options = (const UnMapExtensionOption*)option; |
890 | 0 | return d->unmap(options->address); |
891 | 0 | } |
892 | | |
893 | 0 | return false; |
894 | 0 | } |
895 | | |
896 | | /*! |
897 | | \reimp |
898 | | */ |
899 | | bool QFSFileEngine::supportsExtension(Extension extension) const |
900 | 0 | { |
901 | 0 | Q_D(const QFSFileEngine); |
902 | 0 | if (extension == AtEndExtension && d->fh && isSequential()) |
903 | 0 | return true; |
904 | 0 | if (extension == FastReadLineExtension && d->fh) |
905 | 0 | return true; |
906 | 0 | if (extension == FastReadLineExtension && d->fd != -1 && isSequential()) |
907 | 0 | return true; |
908 | 0 | if (extension == UnMapExtension || extension == MapExtension) |
909 | 0 | return true; |
910 | 0 | return false; |
911 | 0 | } |
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 | 0 | { |
953 | 0 | return QFileSystemEngine::homePath(); |
954 | 0 | } |
955 | | |
956 | | /*! |
957 | | Returns the root path. |
958 | | |
959 | | \sa homePath() |
960 | | */ |
961 | | QString QFSFileEngine::rootPath() |
962 | 0 | { |
963 | 0 | return QFileSystemEngine::rootPath(); |
964 | 0 | } |
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 | 0 | { |
972 | 0 | return QFileSystemEngine::tempPath(); |
973 | 0 | } |
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 | 0 | { |
1007 | 0 | Q_D(QFSFileEngine); |
1008 | 0 | QSystemError error; |
1009 | 0 | bool ret = QFileSystemEngine::copyFile(d->fileEntry, QFileSystemEntry(copyName), error); |
1010 | 0 | if (!ret) |
1011 | 0 | setError(QFile::CopyError, error.toString()); |
1012 | 0 | return ret; |
1013 | 0 | } |
1014 | | |
1015 | | /*! |
1016 | | \reimp |
1017 | | */ |
1018 | | bool QFSFileEngine::remove() |
1019 | 0 | { |
1020 | 0 | Q_D(QFSFileEngine); |
1021 | 0 | QSystemError error; |
1022 | 0 | bool ret = QFileSystemEngine::removeFile(d->fileEntry, error); |
1023 | 0 | d->metaData.clear(); |
1024 | 0 | if (!ret) |
1025 | 0 | setError(QFile::RemoveError, error.toString()); |
1026 | 0 | return ret; |
1027 | 0 | } |
1028 | | |
1029 | | /*! |
1030 | | \reimp |
1031 | | */ |
1032 | | bool QFSFileEngine::rename(const QString &newName) |
1033 | 0 | { |
1034 | 0 | Q_D(QFSFileEngine); |
1035 | 0 | QSystemError error; |
1036 | 0 | bool ret = QFileSystemEngine::renameFile(d->fileEntry, QFileSystemEntry(newName), error); |
1037 | 0 | if (!ret) |
1038 | 0 | setError(QFile::RenameError, error.toString()); |
1039 | 0 | return ret; |
1040 | 0 | } |
1041 | | /*! |
1042 | | \reimp |
1043 | | */ |
1044 | | bool QFSFileEngine::renameOverwrite(const QString &newName) |
1045 | 0 | { |
1046 | 0 | Q_D(QFSFileEngine); |
1047 | 0 | QSystemError error; |
1048 | 0 | bool ret = QFileSystemEngine::renameOverwriteFile(d->fileEntry, QFileSystemEntry(newName), error); |
1049 | 0 | if (!ret) |
1050 | 0 | setError(QFile::RenameError, error.toString()); |
1051 | 0 | return ret; |
1052 | 0 | } |
1053 | | |
1054 | | /*! |
1055 | | \reimp |
1056 | | */ |
1057 | | bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const |
1058 | 0 | { |
1059 | 0 | return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories); |
1060 | 0 | } |
1061 | | |
1062 | | /*! |
1063 | | \reimp |
1064 | | */ |
1065 | | bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const |
1066 | 0 | { |
1067 | 0 | return QFileSystemEngine::removeDirectory(QFileSystemEntry(name), recurseParentDirectories); |
1068 | 0 | } |
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 | 0 | { |
1079 | 0 | return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path)); |
1080 | 0 | } |
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 |