source: trunk/src/corelib/io/qfsfileengine.cpp

Last change on this file was 1113, checked in by Dmitry A. Kuminov, 12 years ago

corelib: Make QDir::canonicalPath() and QFileInfo::canonicalFilePath() actually follow symlinks.

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