Coverage Report

Created: 2023-06-07 06:33

/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 &copyName)
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