1
/****************************************************************************
2
**
3
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the QtCore module of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** No Commercial Usage
11
** This file contains pre-release code and may not be distributed.
12
** You may use this file in accordance with the terms and conditions
13
** contained in the Technology Preview License Agreement accompanying
14
** this package.
15
**
16
** GNU Lesser General Public License Usage
17
** Alternatively, this file may be used under the terms of the GNU Lesser
18
** General Public License version 2.1 as published by the Free Software
19
** Foundation and appearing in the file LICENSE.LGPL included in the
20
** packaging of this file.  Please review the following information to
21
** ensure the GNU Lesser General Public License version 2.1 requirements
22
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23
**
24
** In addition, as a special exception, Nokia gives you certain additional
25
** rights.  These rights are described in the Nokia Qt LGPL Exception
26
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27
**
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
30
**
31
**
32
**
33
**
34
**
35
**
36
**
37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
#include "qfsfileengine_p.h"
43
#include "qfsfileengine_iterator_p.h"
44
#include "qdatetime.h"
45
#include "qdiriterator.h"
46
#include "qset.h"
47
48
#ifndef QT_NO_FSFILEENGINE
49
50
#if !defined(Q_OS_WINCE)
51
#include <errno.h>
52
#endif
53
#include <stdio.h>
54
55
QT_BEGIN_NAMESPACE
56
57
#ifdef Q_OS_WIN
58
#  ifndef S_ISREG
59
#    define S_ISREG(x)   (((x) & S_IFMT) == S_IFREG)
60
#  endif
61
#  ifndef S_ISCHR
62
#    define S_ISCHR(x)   (((x) & S_IFMT) == S_IFCHR)
63
#  endif
64
#  ifndef S_ISFIFO
65
#    define S_ISFIFO(x) false
66
#  endif
67
#  ifndef S_ISSOCK
68
#    define S_ISSOCK(x) false
69
#  endif
70
#  ifndef INVALID_FILE_ATTRIBUTES
71
#    define INVALID_FILE_ATTRIBUTES (DWORD (-1))
72
#  endif
73
#endif
74
75
76
/*! \class QFSFileEngine
77
    \brief The QFSFileEngine class implements Qt's default file engine.
78
    \since 4.1
79
80
    This class is part of the file engine framework in Qt. If you only want to
81
    access files or directories, use QFile, QFileInfo or QDir instead.
82
83
    QFSFileEngine is the default file engine for accessing regular files. It
84
    is provided for convenience; by subclassing this class, you can alter its
85
    behavior slightly, without having to write a complete QAbstractFileEngine
86
    subclass. To install your custom file engine, you must also subclass
87
    QAbstractFileEngineHandler and create an instance of your handler.
88
89
    It can also be useful to create a QFSFileEngine object directly if you
90
    need to use the local file system inside QAbstractFileEngine::create(), in
91
    order to avoid recursion (as higher-level classes tend to call
92
    QAbstractFileEngine::create()).
93
*/
94
95
//**************** QFSFileEnginePrivate
96
QFSFileEnginePrivate::QFSFileEnginePrivate() : QAbstractFileEnginePrivate()
97
{
98
    init();
99
}
100
101
/*!
102
    \internal
103
*/
104
void QFSFileEnginePrivate::init()
105
{
106
    is_sequential = 0;
107
    tried_stat = 0;
108
#ifdef Q_OS_UNIX
109
    need_lstat = 1;
110
    is_link = 0;
111
#endif
112
    openMode = QIODevice::NotOpen;
113
    fd = -1;
114
    fh = 0;
115
    lastIOCommand = IOFlushCommand;
116
    lastFlushFailed = false;
117
    closeFileHandle = false;
118
#ifdef Q_OS_WIN
119
    fileAttrib = INVALID_FILE_ATTRIBUTES;
120
    fileHandle = INVALID_HANDLE_VALUE;
121
    cachedFd = -1;
122
#endif
123
#ifdef Q_USE_DEPRECATED_MAP_API
124
    fileMapHandle = INVALID_HANDLE_VALUE;
125
#endif
126
}
127
128
/*!
129
    \internal
130
131
    Returns the canonicalized form of \a path (i.e., with all symlinks
132
    resolved, and all redundant path elements removed.
133
*/
134
QString QFSFileEnginePrivate::canonicalized(const QString &path)
135
{
136
    if (path.isEmpty())
137
        return path;
138
139
    QFileInfo fi;
140
    const QChar slash(QLatin1Char('/'));
141
    QString tmpPath = path;
142
    int separatorPos = 0;
143
    QSet<QString> nonSymlinks;
144
    QSet<QString> known;
145
146
    known.insert(path);
147
    do {
148
#ifdef Q_OS_WIN
149
        // UNC, skip past the first two elements
150
        if (separatorPos == 0 && tmpPath.startsWith(QLatin1String("//")))
151
            separatorPos = tmpPath.indexOf(slash, 2);
152
        if (separatorPos != -1)
153
#endif
154
        separatorPos = tmpPath.indexOf(slash, separatorPos + 1);
155
        QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos);
156
        if (!nonSymlinks.contains(prefix)) {
157
            fi.setFile(prefix);
158
            if (fi.isSymLink()) {
159
                QString target = fi.symLinkTarget();
160
                if (separatorPos != -1) {
161
                    if (fi.isDir() && !target.endsWith(slash))
162
                        target.append(slash);
163
                    target.append(tmpPath.mid(separatorPos));
164
                }
165
                tmpPath = QDir::cleanPath(target);
166
                separatorPos = 0;
167
168
                if (known.contains(tmpPath))
169
                    return QString();
170
                known.insert(tmpPath);
171
            } else {
172
                nonSymlinks.insert(prefix);
173
            }
174
        }
175
    } while (separatorPos != -1);
176
177
    return QDir::cleanPath(tmpPath);
178
}
179
180
/*!
181
    Constructs a QFSFileEngine for the file name \a file.
182
*/
183
QFSFileEngine::QFSFileEngine(const QString &file) : QAbstractFileEngine(*new QFSFileEnginePrivate)
184
{
185
    Q_D(QFSFileEngine);
186
    d->filePath = QDir::fromNativeSeparators(file);
187
    d->nativeInitFileName();
188
}
189
190
/*!
191
    Constructs a QFSFileEngine.
192
*/
193
QFSFileEngine::QFSFileEngine() : QAbstractFileEngine(*new QFSFileEnginePrivate)
194
{
195
}
196
197
/*!
198
    \internal
199
*/
200
QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd)
201
    : QAbstractFileEngine(dd)
202
{
203
}
204
205
/*!
206
    Destructs the QFSFileEngine.
207
*/
208
QFSFileEngine::~QFSFileEngine()
209
{
210
    Q_D(QFSFileEngine);
211
    if (d->closeFileHandle) {
212
        if (d->fh) {
213
            int ret;
214
            do {
215
                ret = fclose(d->fh);
216
            } while (ret == EOF && errno == EINTR);
217
        } else if (d->fd != -1) {
218
            int ret;
219
            do {
220
                ret = QT_CLOSE(d->fd);
221
            } while (ret == -1 && errno == EINTR);
222
        }
223
    }
224
    QList<uchar*> keys = d->maps.keys();
225
    for (int i = 0; i < keys.count(); ++i)
226
        unmap(keys.at(i));
227
}
228
229
/*!
230
    \reimp
231
*/
232
void QFSFileEngine::setFileName(const QString &file)
233
{
234
    Q_D(QFSFileEngine);
235
    d->init();
236
    d->filePath = QDir::fromNativeSeparators(file);
237
    d->nativeInitFileName();
238
}
239
240
/*!
241
    \reimp
242
*/
243
bool QFSFileEngine::open(QIODevice::OpenMode openMode)
244
{
245
    Q_D(QFSFileEngine);
246
    if (d->filePath.isEmpty()) {
247
        qWarning("QFSFileEngine::open: No file name specified");
248
        setError(QFile::OpenError, QLatin1String("No file name specified"));
249
        return false;
250
    }
251
252
    // Append implies WriteOnly.
253
    if (openMode & QFile::Append)
254
        openMode |= QFile::WriteOnly;
255
256
    // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
257
    if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
258
        openMode |= QFile::Truncate;
259
260
    d->openMode = openMode;
261
    d->lastFlushFailed = false;
262
    d->tried_stat = 0;
263
    d->fh = 0;
264
    d->fd = -1;
265
266
    return d->nativeOpen(openMode);
267
}
268
269
/*!
270
    Opens the file handle \a fh in \a openMode mode. Returns true on
271
    success; otherwise returns false.
272
*/
273
bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh)
274
{
275
    Q_D(QFSFileEngine);
276
277
    // Append implies WriteOnly.
278
    if (openMode & QFile::Append)
279
        openMode |= QFile::WriteOnly;
280
281
    // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
282
    if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
283
        openMode |= QFile::Truncate;
284
285
    d->openMode = openMode;
286
    d->lastFlushFailed = false;
287
    d->closeFileHandle = false;
288
    d->nativeFilePath.clear();
289
    d->filePath.clear();
290
    d->tried_stat = 0;
291
    d->fd = -1;
292
293
    return d->openFh(openMode, fh);
294
}
295
296
/*!
297
    Opens the file handle \a fh using the open mode \a flags.
298
*/
299
bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh)
300
{
301
    Q_Q(QFSFileEngine);
302
    this->fh = fh;
303
    fd = -1;
304
305
    // Seek to the end when in Append mode.
306
    if (openMode & QIODevice::Append) {
307
        int ret;
308
        do {
309
            ret = QT_FSEEK(fh, 0, SEEK_END);
310
        } while (ret == -1 && errno == EINTR);
311
312
        if (ret == -1) {
313
            q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
314
                        qt_error_string(int(errno)));
315
316
            this->openMode = QIODevice::NotOpen;
317
            this->fh = 0;
318
319
            return false;
320
        }
321
    }
322
323
    return true;
324
}
325
326
/*!
327
    Opens the file descriptor \a fd in \a openMode mode. Returns true
328
    on success; otherwise returns false.
329
*/
330
bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd)
331
{
332
    Q_D(QFSFileEngine);
333
334
    // Append implies WriteOnly.
335
    if (openMode & QFile::Append)
336
        openMode |= QFile::WriteOnly;
337
338
    // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
339
    if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
340
        openMode |= QFile::Truncate;
341
342
    d->openMode = openMode;
343
    d->lastFlushFailed = false;
344
    d->closeFileHandle = false;
345
    d->nativeFilePath.clear();
346
    d->filePath.clear();
347
    d->fh = 0;
348
    d->fd = -1;
349
    d->tried_stat = 0;
350
351
    return d->openFd(openMode, fd);
352
}
353
354
355
/*!
356
    Opens the file descriptor \a fd to the file engine, using the open mode \a
357
    flags.
358
*/
359
bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd)
360
{
361
    Q_Q(QFSFileEngine);
362
    this->fd = fd;
363
    fh = 0;
364
365
    // Seek to the end when in Append mode.
366
    if (openMode & QFile::Append) {
367
        int ret;
368
        do {
369
            ret = QT_LSEEK(fd, 0, SEEK_END);
370
        } while (ret == -1 && errno == EINTR);
371
372
        if (ret == -1) {
373
            q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
374
                        qt_error_string(int(errno)));
375
376
            this->openMode = QIODevice::NotOpen;
377
            this->fd = -1;
378
379
            return false;
380
        }
381
    }
382
383
    return true;
384
}
385
386
/*!
387
    \reimp
388
*/
389
bool QFSFileEngine::close()
390
{
391
    Q_D(QFSFileEngine);
392
    d->openMode = QIODevice::NotOpen;
393
    return d->nativeClose();
394
}
395
396
/*!
397
    \internal
398
*/
399
bool QFSFileEnginePrivate::closeFdFh()
400
{
401
    Q_Q(QFSFileEngine);
402
    if (fd == -1 && !fh)
403
        return false;
404
405
    // Flush the file if it's buffered, and if the last flush didn't fail.
406
    bool flushed = !fh || (!lastFlushFailed && q->flush());
407
    bool closed = true;
408
    tried_stat = 0;
409
410
    // Close the file if we created the handle.
411
    if (closeFileHandle) {
412
        int ret;
413
        do {
414
            if (fh) {
415
                // Close buffered file.
416
                ret = fclose(fh) != 0 ? -1 : 0;
417
            } else {
418
                // Close unbuffered file.
419
                ret = QT_CLOSE(fd);
420
            }
421
        } while (ret == -1 && errno == EINTR);
422
423
        // We must reset these guys regardless; calling close again after a
424
        // failed close causes crashes on some systems.
425
        fh = 0;
426
        fd = -1;
427
        closed = (ret == 0);
428
    }
429
430
    // Report errors.
431
    if (!flushed || !closed) {
432
        if (flushed) {
433
            // If not flushed, we want the flush error to fall through.
434
            q->setError(QFile::UnspecifiedError, qt_error_string(errno));
435
        }
436
        return false;
437
    }
438
439
    return true;
440
}
441
442
/*!
443
    \reimp
444
*/
445
bool QFSFileEngine::flush()
446
{
447
    Q_D(QFSFileEngine);
448
    if ((d->openMode & QIODevice::WriteOnly) == 0) {
449
        // Nothing in the write buffers, so flush succeeds in doing
450
        // nothing.
451
        return true;
452
    }
453
    return d->nativeFlush();
454
}
455
456
/*!
457
    \internal
458
*/
459
bool QFSFileEnginePrivate::flushFh()
460
{
461
    Q_Q(QFSFileEngine);
462
463
    // Never try to flush again if the last flush failed. Otherwise you can
464
    // get crashes on some systems (AIX).
465
    if (lastFlushFailed)
466
        return false;
467
468
    int ret = fflush(fh);
469
470
    lastFlushFailed = (ret != 0);
471
    lastIOCommand = QFSFileEnginePrivate::IOFlushCommand;
472
473
    if (ret != 0) {
474
        q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError,
475
                    qt_error_string(errno));
476
        return false;
477
    }
478
    return true;
479
}
480
481
/*!
482
    \reimp
483
*/
484
qint64 QFSFileEngine::size() const
485
{
486
    Q_D(const QFSFileEngine);
487
    return d->nativeSize();
488
}
489
490
/*!
491
    \internal
492
*/
493
qint64 QFSFileEnginePrivate::sizeFdFh() const
494
{
495
    Q_Q(const QFSFileEngine);
496
    // ### Fix this function, it should not stat unless the file is closed.
497
    QT_STATBUF st;
498
    int ret = 0;
499
    const_cast<QFSFileEngine *>(q)->flush();
500
    if (fh && nativeFilePath.isEmpty()) {
501
        // Buffered stdlib mode.
502
        // ### This should really be an ftell
503
        ret = QT_FSTAT(QT_FILENO(fh), &st);
504
    } else if (fd == -1) {
505
        // Stateless stat.
506
        ret = QT_STAT(nativeFilePath.constData(), &st);
507
    } else {
508
        // Unbuffered stdio mode.
509
        ret = QT_FSTAT(fd, &st);
510
    }
511
    if (ret == -1)
512
        return 0;
513
    return st.st_size;
514
}
515
516
/*!
517
    \reimp
518
*/
519
qint64 QFSFileEngine::pos() const
520
{
521
    Q_D(const QFSFileEngine);
522
    return d->nativePos();
523
}
524
525
/*!
526
    \internal
527
*/
528
qint64 QFSFileEnginePrivate::posFdFh() const
529
{
530
    if (fh)
531
        return qint64(QT_FTELL(fh));
532
    return QT_LSEEK(fd, 0, SEEK_CUR);
533
}
534
535
/*!
536
    \reimp
537
*/
538
bool QFSFileEngine::seek(qint64 pos)
539
{
540
    Q_D(QFSFileEngine);
541
    return d->nativeSeek(pos);
542
}
543
544
/*!
545
    \internal
546
*/
547
bool QFSFileEnginePrivate::seekFdFh(qint64 pos)
548
{
549
    Q_Q(QFSFileEngine);
550
551
    // On Windows' stdlib implementation, the results of calling fread and
552
    // fwrite are undefined if not called either in sequence, or if preceded
553
    // with a call to fflush().
554
    if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush())
555
        return false;
556
557
    if (fh) {
558
        // Buffered stdlib mode.
559
        int ret;
560
        do {
561
            ret = QT_FSEEK(fh, QT_OFF_T(pos), SEEK_SET);
562
        } while (ret == -1 && errno == EINTR);
563
564
        if (ret == -1) {
565
            q->setError(QFile::ReadError, qt_error_string(int(errno)));
566
            return false;
567
        }
568
    } else {
569
        // Unbuffered stdio mode.
570
        if (QT_LSEEK(fd, pos, SEEK_SET) == -1) {
571
            qWarning("QFile::at: Cannot set file position %lld", pos);
572
            q->setError(QFile::PositionError, qt_error_string(errno));
573
            return false;
574
        }
575
    }
576
    return true;
577
}
578
579
/*!
580
    \reimp
581
*/
582
int QFSFileEngine::handle() const
583
{
584
    Q_D(const QFSFileEngine);
585
    return d->nativeHandle();
586
}
587
588
/*!
589
    \reimp
590
*/
591
qint64 QFSFileEngine::read(char *data, qint64 maxlen)
592
{
593
    Q_D(QFSFileEngine);
594
595
    // On Windows' stdlib implementation, the results of calling fread and
596
    // fwrite are undefined if not called either in sequence, or if preceded
597
    // with a call to fflush().
598
    if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) {
599
        flush();
600
        d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand;
601
    }
602
603
    return d->nativeRead(data, maxlen);
604
}
605
606
/*!
607
    \internal
608
*/
609
qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len)
610
{
611
    Q_Q(QFSFileEngine);
612
613
    // Buffered stdlib mode.
614
    if (fh) {
615
        qint64 readBytes = 0;
616
        qint64 read = 0;
617
        int retry = 0;
618
619
        // Read in blocks of 4k to avoid platform limitations (Windows
620
        // commonly bails out if you read or write too large blocks at once).
621
        qint64 bytesToRead;
622
        do {
623
            if (retry == 1)
624
                retry = 2;
625
626
            bytesToRead = qMin<qint64>(4096, len - read);
627
            do {
628
                readBytes = fread(data + read, 1, size_t(bytesToRead), fh);
629
            } while (readBytes == 0 && !feof(fh) && errno == EINTR);
630
631
            if (readBytes > 0) {
632
                read += readBytes;
633
            } else if (!retry && feof(fh)) {
634
                // Synchronize and try again (just once though).
635
                if (++retry == 1)
636
                    QT_FSEEK(fh, QT_FTELL(fh), SEEK_SET);
637
            }
638
        } while (retry == 1 || (readBytes == bytesToRead && read < len));
639
640
        // Return the number of bytes read, or if nothing was read, return -1
641
        // if an error occurred, or 0 if we detected EOF.
642
        if (read == 0) {
643
            q->setError(QFile::ReadError, qt_error_string(int(errno)));
644
            if (!feof(fh))
645
                read = -1;
646
        }
647
        return read;
648
    }
649
650
    // Unbuffered stdio mode.
651
    qint64 ret = 0;
652
    if (len) {
653
        int result;
654
        qint64 read = 0;
655
        errno = 0;
656
657
        // Read in blocks of 4k to avoid platform limitations (Windows
658
        // commonly bails out if you read or write too large blocks at once).
659
        do {
660
            qint64 bytesToRead = qMin<qint64>(4096, len - read);
661
            do {
662
                result = QT_READ(fd, data + read, int(bytesToRead));
663
            } while (result == -1 && errno == EINTR);
664
            if (result > 0)
665
                read += result;
666
        } while (result > 0 && read < len);
667
668
        // Return the number of bytes read, or if nothing was read, return -1
669
        // if an error occurred.
670
        if (read > 0) {
671
            ret += read;
672
        } else if (read == 0 && result < 0) {
673
            ret = -1;
674
            q->setError(QFile::ReadError, qt_error_string(errno));
675
        }
676
    }
677
    return ret;
678
}
679
680
/*!
681
    \reimp
682
*/
683
qint64 QFSFileEngine::readLine(char *data, qint64 maxlen)
684
{
685
    Q_D(QFSFileEngine);
686
687
    // On Windows' stdlib implementation, the results of calling fread and
688
    // fwrite are undefined if not called either in sequence, or if preceded
689
    // with a call to fflush().
690
    if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) {
691
        flush();
692
        d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand;
693
    }
694
695
    return d->nativeReadLine(data, maxlen);
696
}
697
698
/*!
699
    \internal
700
*/
701
qint64 QFSFileEnginePrivate::readLineFdFh(char *data, qint64 maxlen)
702
{
703
    Q_Q(QFSFileEngine);
704
    if (!fh)
705
        return q->QAbstractFileEngine::readLine(data, maxlen);
706
707
    QT_OFF_T oldPos = 0;
708
#ifdef Q_OS_WIN
709
    bool seq = q->isSequential();
710
    if (!seq)
711
#endif
712
        oldPos = QT_FTELL(fh);
713
714
    // QIODevice::readLine() passes maxlen - 1 to QFile::readLineData()
715
    // because it has made space for the '\0' at the end of data.  But fgets
716
    // does the same, so we'd get two '\0' at the end - passing maxlen + 1
717
    // solves this.
718
    if (!fgets(data, int(maxlen + 1), fh)) {
719
        if (!feof(fh))
720
            q->setError(QFile::ReadError, qt_error_string(int(errno)));
721
        return -1;              // error
722
    }
723
724
#ifdef Q_OS_WIN
725
    if (seq)
726
        return qstrlen(data);
727
#endif
728
729
    qint64 lineLength = QT_FTELL(fh) - oldPos;
730
    return lineLength > 0 ? lineLength : qstrlen(data);
731
}
732
733
/*!
734
    \reimp
735
*/
736
qint64 QFSFileEngine::write(const char *data, qint64 len)
737
{
738
    Q_D(QFSFileEngine);
739
740
    // On Windows' stdlib implementation, the results of calling fread and
741
    // fwrite are undefined if not called either in sequence, or if preceded
742
    // with a call to fflush().
743
    if (d->lastIOCommand != QFSFileEnginePrivate::IOWriteCommand) {
744
        flush();
745
        d->lastIOCommand = QFSFileEnginePrivate::IOWriteCommand;
746
    }
747
748
    return d->nativeWrite(data, len);
749
}
750
751
/*!
752
    \internal
753
*/
754
qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len)
755
{
756
    Q_Q(QFSFileEngine);
757
    qint64 result;
758
    qint64 written = 0;
759
760
    do {
761
        // Write blocks of 4k to avoid platform limitations (Windows commonly
762
        // bails out if you read or write too large blocks at once).
763
        qint64 bytesToWrite = qMin<qint64>(4096, len - written);
764
        if (fh) {
765
            do {
766
                // Buffered stdlib mode.
767
                result = qint64(fwrite(data + written, 1, size_t(bytesToWrite), fh));
768
            } while (result == 0 && errno == EINTR);
769
            if (bytesToWrite > 0 && result == 0)
770
                result = -1;
771
        } else {
772
            do {
773
                // Unbuffered stdio mode.
774
                result = QT_WRITE(fd, data + written, bytesToWrite);
775
            } while (result == -1 && errno == EINTR);
776
        }
777
        if (result > 0)
778
            written += qint64(result);
779
    } while (written < len && result > 0);
780
781
    // If we read anything, return that with success. Otherwise, set an error,
782
    // and return the last return value.
783
    if (result > 0)
784
        return written;
785
    q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, qt_error_string(errno));
786
    return result;
787
}
788
789
/*!
790
    \internal
791
*/
792
QAbstractFileEngine::Iterator *QFSFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames)
793
{
794
    return new QFSFileEngineIterator(filters, filterNames);
795
}
796
797
/*!
798
    \internal
799
*/
800
QAbstractFileEngine::Iterator *QFSFileEngine::endEntryList()
801
{
802
    return 0;
803
}
804
805
/*!
806
    \internal
807
*/
808
QStringList QFSFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
809
{
810
    return QAbstractFileEngine::entryList(filters, filterNames);
811
}
812
813
/*!
814
    \reimp
815
*/
816
bool QFSFileEngine::isSequential() const
817
{
818
    Q_D(const QFSFileEngine);
819
    if (d->is_sequential == 0)
820
        d->is_sequential = d->nativeIsSequential() ? 1 : 2;
821
    return d->is_sequential == 1;
822
}
823
824
/*!
825
    \internal
826
*/
827
bool QFSFileEnginePrivate::isSequentialFdFh() const
828
{
829
    if (!tried_stat)
830
        doStat();
831
    if (could_stat) {
832
#ifdef Q_OS_UNIX
833
        return (st.st_mode & S_IFMT) != S_IFREG;
834
        // ### WINDOWS!
835
#endif
836
    }
837
    return true;
838
}
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(d->fh);
848
849
    if (extension == MapExtension) {
850
        const MapExtensionOption *options = (MapExtensionOption*)(option);
851
        MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
852
        returnValue->address = d->map(options->offset, options->size, options->flags);
853
        return (returnValue->address != 0);
854
    }
855
    if (extension == UnMapExtension) {
856
        UnMapExtensionOption *options = (UnMapExtensionOption*)option;
857
        return d->unmap(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 true for Windows, false for Unix.
882
*/
883
884
/*! \fn bool QFSFileEngine::copy(const QString &copyName)
885
886
  For windows, copy the file to file \a copyName.
887
888
  Not implemented for Unix.
889
*/
890
891
/*! \fn QString QFSFileEngine::currentPath(const QString &fileName)
892
  For Unix, returns the current working directory for the file
893
  engine.
894
  
895
  For Windows, returns the canonicalized form of the current path used
896
  by the file engine for the drive specified by \a fileName.  On
897
  Windows, each drive has its own current directory, so a different
898
  path is returned for file names that include different drive names
899
  (e.g. A: or C:).
900
901
  \sa setCurrentPath()
902
*/
903
904
/*! \fn QFileInfoList QFSFileEngine::drives()
905
  For Windows, returns the list of drives in the file system as a list
906
  of QFileInfo objects. On unix, Mac OS X and Windows CE, only the
907
  root path is returned.  On Windows, this function returns all drives
908
  (A:\, C:\, D:\, etc.).
909
910
  For Unix, the list contains just the root path "/".
911
*/
912
913
/*! \fn QString QFSFileEngine::fileName(FileName file) const
914
  \reimp
915
*/
916
917
/*! \fn QDateTime QFSFileEngine::fileTime(FileTime time) const
918
  \reimp
919
*/
920
921
/*! \fn QString QFSFileEngine::homePath()
922
  Returns the home path of the current user.
923
924
  \sa rootPath()
925
*/
926
927
/*! \fn bool QFSFileEngine::isRelativePath() const
928
  \reimp
929
*/
930
931
/*! \fn bool QFSFileEngine::link(const QString &newName)
932
933
  Creates a link from the file currently specified by fileName() to
934
  \a newName. What a link is depends on the underlying filesystem
935
  (be it a shortcut on Windows or a symbolic link on Unix). Returns
936
  true if successful; otherwise returns false.
937
*/
938
939
/*! \fn bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
940
  \reimp
941
*/
942
943
/*! \fn uint QFSFileEngine::ownerId(FileOwner own) const
944
  In Unix, if stat() is successful, the \c uid is returned if
945
  \a own is the owner. Otherwise the \c gid is returned. If stat()
946
  is unsuccessful, -2 is reuturned.
947
948
  For Windows, -2 is always returned.
949
*/
950
951
/*! \fn QString QFSFileEngine::owner(FileOwner own) const
952
  \reimp
953
*/
954
955
/*! \fn bool QFSFileEngine::remove()
956
  \reimp
957
*/
958
959
/*! \fn bool QFSFileEngine::rename(const QString &newName)
960
  \reimp
961
*/
962
963
/*! \fn bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
964
  \reimp
965
*/
966
967
/*! \fn QString QFSFileEngine::rootPath()
968
  Returns the root path.
969
970
  \sa homePath()
971
*/
972
973
/*! \fn bool QFSFileEngine::setCurrentPath(const QString &path)
974
  Sets the current path (e.g., for QDir), to \a path. Returns true if the
975
  new path exists; otherwise this function does nothing, and returns false.
976
977
  \sa currentPath()
978
*/
979
980
/*! \fn bool QFSFileEngine::setPermissions(uint perms)
981
  \reimp
982
*/
983
984
/*! \fn bool QFSFileEngine::setSize(qint64 size)
985
  \reimp
986
*/
987
988
/*! \fn QString QFSFileEngine::tempPath()
989
  Returns the temporary path (i.e., a path in which it is safe
990
  to store temporary files).
991
*/
992
993
QT_END_NAMESPACE
994
995
#endif // QT_NO_FSFILEENGINE