File indexing completed on 2024-12-22 04:35:00
0001 /* 0002 SPDX-FileCopyrightText: 2009 Nokia Corporation and /or its subsidiary(-ies). 0003 Contact: Qt Software Information (qt-info@nokia.com) 0004 0005 This file is part of the QtCore module of the Qt Toolkit. 0006 0007 $QT_BEGIN_LICENSE:LGPL$ 0008 Commercial Usage 0009 Licensees holding valid Qt Commercial licenses may use this file in 0010 accordance with the Qt Commercial License Agreement provided with the 0011 Software or, alternatively, in accordance with the terms contained in 0012 a written agreement between you and Nokia. 0013 0014 GNU Lesser General Public License Usage 0015 Alternatively, this file may be used under the terms of the GNU Lesser 0016 General Public License version 2.1 as published by the Free Software 0017 Foundation and appearing in the file LICENSE.LGPL included in the 0018 packaging of this file. Please review the following information to 0019 ensure the GNU Lesser General Public License version 2.1 requirements 0020 will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 0021 0022 In addition, as a special exception, Nokia gives you certain 0023 additional rights. These rights are described in the Nokia Qt LGPL 0024 Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this 0025 package. 0026 0027 GNU General Public License Usage 0028 Alternatively, this file may be used under the terms of the GNU 0029 General Public License version 3.0 as published by the Free Software 0030 Foundation and appearing in the file LICENSE.GPL included in the 0031 packaging of this file. Please review the following information to 0032 ensure the GNU General Public License version 3.0 requirements will be 0033 met: https://www.gnu.org/licenses/gpl-3.0.html. 0034 0035 If you are unsure which license is appropriate for your use, please 0036 contact the sales department at qt-sales@nokia.com. 0037 $QT_END_LICENSE$ 0038 0039 */ 0040 0041 #include "qfsfileengine_p.h" 0042 #include "qfsfileengine_iterator_p.h" 0043 #include "qdatetime.h" 0044 #include "qdiriterator.h" 0045 #include "qset.h" 0046 0047 #ifndef QT_NO_FSFILEENGINE 0048 0049 #if !defined(Q_OS_WINCE) 0050 #include <errno.h> 0051 #endif 0052 #include <stdio.h> 0053 0054 QT_BEGIN_NAMESPACE 0055 0056 #ifdef Q_OS_WIN 0057 # ifndef S_ISREG 0058 # define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) 0059 # endif 0060 # ifndef S_ISCHR 0061 # define S_ISCHR(x) (((x) & S_IFMT) == S_IFCHR) 0062 # endif 0063 # ifndef S_ISFIFO 0064 # define S_ISFIFO(x) false 0065 # endif 0066 # ifndef S_ISSOCK 0067 # define S_ISSOCK(x) false 0068 # endif 0069 # ifndef INVALID_FILE_ATTRIBUTES 0070 # define INVALID_FILE_ATTRIBUTES (DWORD (-1)) 0071 # endif 0072 #endif 0073 0074 0075 /*! \class QFSFileEngine 0076 \brief The QFSFileEngine class implements Qt's default file engine. 0077 \since 4.1 0078 0079 This class is part of the file engine framework in Qt. If you only want to 0080 access files or directories, use QFile, QFileInfo or QDir instead. 0081 0082 QFSFileEngine is the default file engine for accessing regular files. It 0083 is provided for convenience; by subclassing this class, you can alter its 0084 behavior slightly, without having to write a complete QAbstractFileEngine 0085 subclass. To install your custom file engine, you must also subclass 0086 QAbstractFileEngineHandler and create an instance of your handler. 0087 0088 It can also be useful to create a QFSFileEngine object directly if you 0089 need to use the local file system inside QAbstractFileEngine::create(), in 0090 order to avoid recursion (as higher-level classes tend to call 0091 QAbstractFileEngine::create()). 0092 */ 0093 0094 //**************** QFSFileEnginePrivate 0095 QFSFileEnginePrivate::QFSFileEnginePrivate() : QAbstractFileEnginePrivate() 0096 { 0097 init(); 0098 } 0099 0100 /*! 0101 \internal 0102 */ 0103 void QFSFileEnginePrivate::init() 0104 { 0105 is_sequential = 0; 0106 tried_stat = 0; 0107 #ifdef Q_OS_UNIX 0108 need_lstat = 1; 0109 is_link = 0; 0110 #endif 0111 openMode = QIODevice::NotOpen; 0112 fd = -1; 0113 fh = 0; 0114 lastIOCommand = IOFlushCommand; 0115 lastFlushFailed = false; 0116 closeFileHandle = false; 0117 #ifdef Q_OS_WIN 0118 fileAttrib = INVALID_FILE_ATTRIBUTES; 0119 fileHandle = INVALID_HANDLE_VALUE; 0120 cachedFd = -1; 0121 #endif 0122 #ifdef Q_USE_DEPRECATED_MAP_API 0123 fileMapHandle = INVALID_HANDLE_VALUE; 0124 #endif 0125 } 0126 0127 /*! 0128 \internal 0129 0130 Returns the canonicalized form of \a path (i.e., with all symlinks 0131 resolved, and all redundant path elements removed. 0132 */ 0133 QString QFSFileEnginePrivate::canonicalized(const QString &path) 0134 { 0135 if (path.isEmpty()) 0136 return path; 0137 0138 QFileInfo fi; 0139 const QChar slash(QLatin1Char('/')); 0140 QString tmpPath = path; 0141 int separatorPos = 0; 0142 QSet<QString> nonSymlinks; 0143 QSet<QString> known; 0144 0145 known.insert(path); 0146 do { 0147 #ifdef Q_OS_WIN 0148 // UNC, skip past the first two elements 0149 if (separatorPos == 0 && tmpPath.startsWith(QLatin1String("//"))) 0150 separatorPos = tmpPath.indexOf(slash, 2); 0151 if (separatorPos != -1) 0152 #endif 0153 separatorPos = tmpPath.indexOf(slash, separatorPos + 1); 0154 QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos); 0155 if (!nonSymlinks.contains(prefix)) { 0156 fi.setFile(prefix); 0157 if (fi.isSymLink()) { 0158 QString target = fi.symLinkTarget(); 0159 if (separatorPos != -1) { 0160 if (fi.isDir() && !target.endsWith(slash)) 0161 target.append(slash); 0162 target.append(tmpPath.mid(separatorPos)); 0163 } 0164 tmpPath = QDir::cleanPath(target); 0165 separatorPos = 0; 0166 0167 if (known.contains(tmpPath)) 0168 return QString(); 0169 known.insert(tmpPath); 0170 } else { 0171 nonSymlinks.insert(prefix); 0172 } 0173 } 0174 } while (separatorPos != -1); 0175 0176 return QDir::cleanPath(tmpPath); 0177 } 0178 0179 /*! 0180 Constructs a QFSFileEngine for the file name \a file. 0181 */ 0182 QFSFileEngine::QFSFileEngine(const QString &file) : QAbstractFileEngine(*new QFSFileEnginePrivate) 0183 { 0184 Q_D(QFSFileEngine); 0185 d->filePath = QDir::fromNativeSeparators(file); 0186 d->nativeInitFileName(); 0187 } 0188 0189 /*! 0190 Constructs a QFSFileEngine. 0191 */ 0192 QFSFileEngine::QFSFileEngine() : QAbstractFileEngine(*new QFSFileEnginePrivate) 0193 { 0194 } 0195 0196 /*! 0197 \internal 0198 */ 0199 QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd) 0200 : QAbstractFileEngine(dd) 0201 { 0202 } 0203 0204 /*! 0205 Destructs the QFSFileEngine. 0206 */ 0207 QFSFileEngine::~QFSFileEngine() 0208 { 0209 Q_D(QFSFileEngine); 0210 if (d->closeFileHandle) { 0211 if (d->fh) { 0212 int ret; 0213 do { 0214 ret = fclose(d->fh); 0215 } while (ret == EOF && errno == EINTR); 0216 } else if (d->fd != -1) { 0217 int ret; 0218 do { 0219 ret = QT_CLOSE(d->fd); 0220 } while (ret == -1 && errno == EINTR); 0221 } 0222 } 0223 QList<uchar*> keys = d->maps.keys(); 0224 for (int i = 0; i < keys.count(); ++i) 0225 unmap(keys.at(i)); 0226 } 0227 0228 /*! 0229 \reimp 0230 */ 0231 void QFSFileEngine::setFileName(const QString &file) 0232 { 0233 Q_D(QFSFileEngine); 0234 d->init(); 0235 d->filePath = QDir::fromNativeSeparators(file); 0236 d->nativeInitFileName(); 0237 } 0238 0239 /*! 0240 \reimp 0241 */ 0242 bool QFSFileEngine::open(QIODevice::OpenMode openMode) 0243 { 0244 Q_D(QFSFileEngine); 0245 if (d->filePath.isEmpty()) { 0246 qWarning("QFSFileEngine::open: No file name specified"); 0247 setError(QFile::OpenError, QLatin1String("No file name specified")); 0248 return false; 0249 } 0250 0251 // Append implies WriteOnly. 0252 if (openMode & QFile::Append) 0253 openMode |= QFile::WriteOnly; 0254 0255 // WriteOnly implies Truncate if neither ReadOnly nor Append are sent. 0256 if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append))) 0257 openMode |= QFile::Truncate; 0258 0259 d->openMode = openMode; 0260 d->lastFlushFailed = false; 0261 d->tried_stat = 0; 0262 d->fh = 0; 0263 d->fd = -1; 0264 0265 return d->nativeOpen(openMode); 0266 } 0267 0268 /*! 0269 Opens the file handle \a fh in \a openMode mode. Returns true on 0270 success; otherwise returns false. 0271 */ 0272 bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh) 0273 { 0274 Q_D(QFSFileEngine); 0275 0276 // Append implies WriteOnly. 0277 if (openMode & QFile::Append) 0278 openMode |= QFile::WriteOnly; 0279 0280 // WriteOnly implies Truncate if neither ReadOnly nor Append are sent. 0281 if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append))) 0282 openMode |= QFile::Truncate; 0283 0284 d->openMode = openMode; 0285 d->lastFlushFailed = false; 0286 d->closeFileHandle = false; 0287 d->nativeFilePath.clear(); 0288 d->filePath.clear(); 0289 d->tried_stat = 0; 0290 d->fd = -1; 0291 0292 return d->openFh(openMode, fh); 0293 } 0294 0295 /*! 0296 Opens the file handle \a fh using the open mode \a flags. 0297 */ 0298 bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh) 0299 { 0300 Q_Q(QFSFileEngine); 0301 this->fh = fh; 0302 fd = -1; 0303 0304 // Seek to the end when in Append mode. 0305 if (openMode & QIODevice::Append) { 0306 int ret; 0307 do { 0308 ret = QT_FSEEK(fh, 0, SEEK_END); 0309 } while (ret == -1 && errno == EINTR); 0310 0311 if (ret == -1) { 0312 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError, 0313 qt_error_string(int(errno))); 0314 return false; 0315 } 0316 } 0317 0318 return true; 0319 } 0320 0321 /*! 0322 Opens the file descriptor \a fd in \a openMode mode. Returns true 0323 on success; otherwise returns false. 0324 */ 0325 bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd) 0326 { 0327 Q_D(QFSFileEngine); 0328 0329 // Append implies WriteOnly. 0330 if (openMode & QFile::Append) 0331 openMode |= QFile::WriteOnly; 0332 0333 // WriteOnly implies Truncate if neither ReadOnly nor Append are sent. 0334 if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append))) 0335 openMode |= QFile::Truncate; 0336 0337 d->lastFlushFailed = false; 0338 d->closeFileHandle = false; 0339 d->nativeFilePath.clear(); 0340 d->filePath.clear(); 0341 d->fh = 0; 0342 d->fd = -1; 0343 d->tried_stat = 0; 0344 0345 return d->openFd(openMode, fd); 0346 } 0347 0348 0349 /*! 0350 Opens the file descriptor \a fd to the file engine, using the open mode \a 0351 flags. 0352 */ 0353 bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd) 0354 { 0355 Q_Q(QFSFileEngine); 0356 this->fd = fd; 0357 fh = 0; 0358 0359 // Seek to the end when in Append mode. 0360 if (openMode & QFile::Append) { 0361 int ret; 0362 do { 0363 ret = QT_LSEEK(fd, 0, SEEK_END); 0364 } while (ret == -1 && errno == EINTR); 0365 0366 if (ret == -1) { 0367 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError, 0368 qt_error_string(int(errno))); 0369 return false; 0370 } 0371 } 0372 0373 return true; 0374 } 0375 0376 /*! 0377 \reimp 0378 */ 0379 bool QFSFileEngine::close() 0380 { 0381 Q_D(QFSFileEngine); 0382 d->openMode = QIODevice::NotOpen; 0383 return d->nativeClose(); 0384 } 0385 0386 /*! 0387 \internal 0388 */ 0389 bool QFSFileEnginePrivate::closeFdFh() 0390 { 0391 Q_Q(QFSFileEngine); 0392 if (fd == -1 && !fh) 0393 return false; 0394 0395 // Flush the file if it's buffered, and if the last flush didn't fail. 0396 bool flushed = !fh || (!lastFlushFailed && q->flush()); 0397 bool closed = true; 0398 tried_stat = 0; 0399 0400 // Close the file if we created the handle. 0401 if (closeFileHandle) { 0402 int ret; 0403 do { 0404 if (fh) { 0405 // Close buffered file. 0406 ret = fclose(fh) != 0 ? -1 : 0; 0407 } else { 0408 // Close unbuffered file. 0409 ret = QT_CLOSE(fd); 0410 } 0411 } while (ret == -1 && errno == EINTR); 0412 0413 // We must reset these guys regardless; calling close again after a 0414 // failed close causes crashes on some systems. 0415 fh = 0; 0416 fd = -1; 0417 closed = (ret == 0); 0418 } 0419 0420 // Report errors. 0421 if (!flushed || !closed) { 0422 if (flushed) { 0423 // If not flushed, we want the flush error to fall through. 0424 q->setError(QFile::UnspecifiedError, qt_error_string(errno)); 0425 } 0426 return false; 0427 } 0428 0429 return true; 0430 } 0431 0432 /*! 0433 \reimp 0434 */ 0435 bool QFSFileEngine::flush() 0436 { 0437 Q_D(QFSFileEngine); 0438 if ((d->openMode & QIODevice::WriteOnly) == 0) { 0439 // Nothing in the write buffers, so flush succeeds in doing 0440 // nothing. 0441 return true; 0442 } 0443 return d->nativeFlush(); 0444 } 0445 0446 /*! 0447 \internal 0448 */ 0449 bool QFSFileEnginePrivate::flushFh() 0450 { 0451 Q_Q(QFSFileEngine); 0452 0453 // Never try to flush again if the last flush failed. Otherwise you can 0454 // get crashes on some systems (AIX). 0455 if (lastFlushFailed) 0456 return false; 0457 0458 int ret = fflush(fh); 0459 0460 lastFlushFailed = (ret != 0); 0461 lastIOCommand = QFSFileEnginePrivate::IOFlushCommand; 0462 0463 if (ret != 0) { 0464 q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, 0465 qt_error_string(errno)); 0466 return false; 0467 } 0468 return true; 0469 } 0470 0471 /*! 0472 \reimp 0473 */ 0474 qint64 QFSFileEngine::size() const 0475 { 0476 Q_D(const QFSFileEngine); 0477 return d->nativeSize(); 0478 } 0479 0480 /*! 0481 \internal 0482 */ 0483 qint64 QFSFileEnginePrivate::sizeFdFh() const 0484 { 0485 Q_Q(const QFSFileEngine); 0486 // ### Fix this function, it should not stat unless the file is closed. 0487 QT_STATBUF st; 0488 int ret = 0; 0489 const_cast<QFSFileEngine *>(q)->flush(); 0490 if (fh && nativeFilePath.isEmpty()) { 0491 // Buffered stdlib mode. 0492 // ### This should really be an ftell 0493 ret = QT_FSTAT(QT_FILENO(fh), &st); 0494 } else if (fd == -1) { 0495 // Stateless stat. 0496 ret = QT_STAT(nativeFilePath.constData(), &st); 0497 } else { 0498 // Unbuffered stdio mode. 0499 ret = QT_FSTAT(fd, &st); 0500 } 0501 if (ret == -1) 0502 return 0; 0503 return st.st_size; 0504 } 0505 0506 /*! 0507 \reimp 0508 */ 0509 qint64 QFSFileEngine::pos() const 0510 { 0511 Q_D(const QFSFileEngine); 0512 return d->nativePos(); 0513 } 0514 0515 /*! 0516 \internal 0517 */ 0518 qint64 QFSFileEnginePrivate::posFdFh() const 0519 { 0520 if (fh) 0521 return qint64(QT_FTELL(fh)); 0522 return QT_LSEEK(fd, 0, SEEK_CUR); 0523 } 0524 0525 /*! 0526 \reimp 0527 */ 0528 bool QFSFileEngine::seek(qint64 pos) 0529 { 0530 Q_D(QFSFileEngine); 0531 return d->nativeSeek(pos); 0532 } 0533 0534 /*! 0535 \internal 0536 */ 0537 bool QFSFileEnginePrivate::seekFdFh(qint64 pos) 0538 { 0539 Q_Q(QFSFileEngine); 0540 0541 // On Windows' stdlib implementation, the results of calling fread and 0542 // fwrite are undefined if not called either in sequence, or if preceded 0543 // with a call to fflush(). 0544 if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush()) 0545 return false; 0546 0547 if (fh) { 0548 // Buffered stdlib mode. 0549 int ret; 0550 do { 0551 ret = QT_FSEEK(fh, QT_OFF_T(pos), SEEK_SET); 0552 } while (ret == -1 && errno == EINTR); 0553 0554 if (ret == -1) { 0555 q->setError(QFile::ReadError, qt_error_string(int(errno))); 0556 return false; 0557 } 0558 } else { 0559 // Unbuffered stdio mode. 0560 if (QT_LSEEK(fd, pos, SEEK_SET) == -1) { 0561 qWarning("QFile::at: Cannot set file position %lld", pos); 0562 q->setError(QFile::PositionError, qt_error_string(errno)); 0563 return false; 0564 } 0565 } 0566 return true; 0567 } 0568 0569 /*! 0570 \reimp 0571 */ 0572 int QFSFileEngine::handle() const 0573 { 0574 Q_D(const QFSFileEngine); 0575 return d->nativeHandle(); 0576 } 0577 0578 /*! 0579 \reimp 0580 */ 0581 qint64 QFSFileEngine::read(char *data, qint64 maxlen) 0582 { 0583 Q_D(QFSFileEngine); 0584 0585 // On Windows' stdlib implementation, the results of calling fread and 0586 // fwrite are undefined if not called either in sequence, or if preceded 0587 // with a call to fflush(). 0588 if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) { 0589 flush(); 0590 d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand; 0591 } 0592 0593 return d->nativeRead(data, maxlen); 0594 } 0595 0596 /*! 0597 \internal 0598 */ 0599 qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len) 0600 { 0601 Q_Q(QFSFileEngine); 0602 0603 // Buffered stdlib mode. 0604 if (fh) { 0605 qint64 readBytes = 0; 0606 qint64 read = 0; 0607 int retry = 0; 0608 0609 // Read in blocks of 4k to avoid platform limitations (Windows 0610 // commonly bails out if you read or write too large blocks at once). 0611 qint64 bytesToRead; 0612 do { 0613 if (retry == 1) 0614 retry = 2; 0615 0616 bytesToRead = qMin<qint64>(4096, len - read); 0617 do { 0618 readBytes = fread(data + read, 1, size_t(bytesToRead), fh); 0619 } while (readBytes == 0 && !feof(fh) && errno == EINTR); 0620 0621 if (readBytes > 0) { 0622 read += readBytes; 0623 } else if (!retry && feof(fh)) { 0624 // Synchronize and try again (just once though). 0625 if (++retry == 1) 0626 QT_FSEEK(fh, QT_FTELL(fh), SEEK_SET); 0627 } 0628 } while (retry == 1 || (readBytes == bytesToRead && read < len)); 0629 0630 // Return the number of bytes read, or if nothing was read, return -1 0631 // if an error occurred, or 0 if we detected EOF. 0632 if (read == 0) { 0633 q->setError(QFile::ReadError, qt_error_string(int(errno))); 0634 if (!feof(fh)) 0635 read = -1; 0636 } 0637 return read; 0638 } 0639 0640 // Unbuffered stdio mode. 0641 qint64 ret = 0; 0642 if (len) { 0643 int result; 0644 qint64 read = 0; 0645 errno = 0; 0646 0647 // Read in blocks of 4k to avoid platform limitations (Windows 0648 // commonly bails out if you read or write too large blocks at once). 0649 do { 0650 qint64 bytesToRead = qMin<qint64>(4096, len - read); 0651 do { 0652 result = QT_READ(fd, data + read, int(bytesToRead)); 0653 } while (result == -1 && errno == EINTR); 0654 if (result > 0) 0655 read += result; 0656 } while (result > 0 && read < len); 0657 0658 // Return the number of bytes read, or if nothing was read, return -1 0659 // if an error occurred. 0660 if (read > 0) { 0661 ret += read; 0662 } else if (read == 0 && result < 0) { 0663 ret = -1; 0664 q->setError(QFile::ReadError, qt_error_string(errno)); 0665 } 0666 } 0667 return ret; 0668 } 0669 0670 /*! 0671 \reimp 0672 */ 0673 qint64 QFSFileEngine::readLine(char *data, qint64 maxlen) 0674 { 0675 Q_D(QFSFileEngine); 0676 0677 // On Windows' stdlib implementation, the results of calling fread and 0678 // fwrite are undefined if not called either in sequence, or if preceded 0679 // with a call to fflush(). 0680 if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) { 0681 flush(); 0682 d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand; 0683 } 0684 0685 return d->nativeReadLine(data, maxlen); 0686 } 0687 0688 /*! 0689 \internal 0690 */ 0691 qint64 QFSFileEnginePrivate::readLineFdFh(char *data, qint64 maxlen) 0692 { 0693 Q_Q(QFSFileEngine); 0694 if (!fh) 0695 return q->QAbstractFileEngine::readLine(data, maxlen); 0696 0697 QT_OFF_T oldPos = 0; 0698 #ifdef Q_OS_WIN 0699 bool seq = q->isSequential(); 0700 if (!seq) 0701 #endif 0702 oldPos = QT_FTELL(fh); 0703 0704 // QIODevice::readLine() passes maxlen - 1 to QFile::readLineData() 0705 // because it has made space for the '\0' at the end of data. But fgets 0706 // does the same, so we'd get two '\0' at the end - passing maxlen + 1 0707 // solves this. 0708 if (!fgets(data, int(maxlen + 1), fh)) { 0709 if (!feof(fh)) 0710 q->setError(QFile::ReadError, qt_error_string(int(errno))); 0711 return -1; // error 0712 } 0713 0714 #ifdef Q_OS_WIN 0715 if (seq) 0716 return qstrlen(data); 0717 #endif 0718 0719 qint64 lineLength = QT_FTELL(fh) - oldPos; 0720 return lineLength > 0 ? lineLength : qstrlen(data); 0721 } 0722 0723 /*! 0724 \reimp 0725 */ 0726 qint64 QFSFileEngine::write(const char *data, qint64 len) 0727 { 0728 Q_D(QFSFileEngine); 0729 0730 // On Windows' stdlib implementation, the results of calling fread and 0731 // fwrite are undefined if not called either in sequence, or if preceded 0732 // with a call to fflush(). 0733 if (d->lastIOCommand != QFSFileEnginePrivate::IOWriteCommand) { 0734 flush(); 0735 d->lastIOCommand = QFSFileEnginePrivate::IOWriteCommand; 0736 } 0737 0738 return d->nativeWrite(data, len); 0739 } 0740 0741 /*! 0742 \internal 0743 */ 0744 qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len) 0745 { 0746 Q_Q(QFSFileEngine); 0747 qint64 result; 0748 qint64 written = 0; 0749 0750 do { 0751 // Write blocks of 4k to avoid platform limitations (Windows commonly 0752 // bails out if you read or write too large blocks at once). 0753 qint64 bytesToWrite = qMin<qint64>(4096, len - written); 0754 if (fh) { 0755 do { 0756 // Buffered stdlib mode. 0757 result = qint64(fwrite(data + written, 1, size_t(bytesToWrite), fh)); 0758 } while (result == 0 && errno == EINTR); 0759 if (bytesToWrite > 0 && result == 0) 0760 result = -1; 0761 } else { 0762 do { 0763 // Unbuffered stdio mode. 0764 result = QT_WRITE(fd, data + written, bytesToWrite); 0765 } while (result == -1 && errno == EINTR); 0766 } 0767 if (result > 0) 0768 written += qint64(result); 0769 } while (written < len && result > 0); 0770 0771 // If we read anything, return that with success. Otherwise, set an error, 0772 // and return the last return value. 0773 if (result > 0) 0774 return written; 0775 q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, qt_error_string(errno)); 0776 return result; 0777 } 0778 0779 /*! 0780 \internal 0781 */ 0782 QAbstractFileEngine::Iterator *QFSFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) 0783 { 0784 return new QFSFileEngineIterator(filters, filterNames); 0785 } 0786 0787 /*! 0788 \internal 0789 */ 0790 QAbstractFileEngine::Iterator *QFSFileEngine::endEntryList() 0791 { 0792 return 0; 0793 } 0794 0795 /*! 0796 \internal 0797 */ 0798 QStringList QFSFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const 0799 { 0800 return QAbstractFileEngine::entryList(filters, filterNames); 0801 } 0802 0803 /*! 0804 \reimp 0805 */ 0806 bool QFSFileEngine::isSequential() const 0807 { 0808 Q_D(const QFSFileEngine); 0809 if (d->is_sequential == 0) 0810 d->is_sequential = d->nativeIsSequential() ? 1 : 2; 0811 return d->is_sequential == 1; 0812 } 0813 0814 /*! 0815 \internal 0816 */ 0817 bool QFSFileEnginePrivate::isSequentialFdFh() const 0818 { 0819 if (!tried_stat) 0820 doStat(); 0821 if (could_stat) { 0822 #ifdef Q_OS_UNIX 0823 return (st.st_mode & S_IFMT) != S_IFREG; 0824 // ### WINDOWS! 0825 #endif 0826 } 0827 return true; 0828 } 0829 0830 /*! 0831 \reimp 0832 */ 0833 bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) 0834 { 0835 Q_D(QFSFileEngine); 0836 if (extension == AtEndExtension && d->fh && isSequential()) 0837 return feof(d->fh); 0838 0839 if (extension == MapExtension) { 0840 const MapExtensionOption *options = (MapExtensionOption*)(option); 0841 MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output); 0842 returnValue->address = d->map(options->offset, options->size, options->flags); 0843 return (returnValue->address != 0); 0844 } 0845 if (extension == UnMapExtension) { 0846 UnMapExtensionOption *options = (UnMapExtensionOption*)option; 0847 return d->unmap(options->address); 0848 } 0849 0850 return false; 0851 } 0852 0853 /*! 0854 \reimp 0855 */ 0856 bool QFSFileEngine::supportsExtension(Extension extension) const 0857 { 0858 Q_D(const QFSFileEngine); 0859 if (extension == AtEndExtension && d->fh && isSequential()) 0860 return true; 0861 if (extension == FastReadLineExtension && d->fh) 0862 return true; 0863 if (extension == FastReadLineExtension && d->fd != -1 && isSequential()) 0864 return true; 0865 if (extension == UnMapExtension || extension == MapExtension) 0866 return true; 0867 return false; 0868 } 0869 0870 QT_END_NAMESPACE 0871 0872 #endif // QT_NO_FSFILEENGINE