summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qfsfileengine_unix.cpp
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /src/corelib/io/qfsfileengine_unix.cpp
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/corelib/io/qfsfileengine_unix.cpp')
-rw-r--r--src/corelib/io/qfsfileengine_unix.cpp1108
1 files changed, 1108 insertions, 0 deletions
diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp
new file mode 100644
index 0000000000..6c03b32a83
--- /dev/null
+++ b/src/corelib/io/qfsfileengine_unix.cpp
@@ -0,0 +1,1108 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qabstractfileengine.h"
+#include "private/qfsfileengine_p.h"
+#include "private/qcore_unix_p.h"
+#include "qfilesystementry_p.h"
+#include "qfilesystemengine_p.h"
+
+#ifndef QT_NO_FSFILEENGINE
+
+#include "qfile.h"
+#include "qdir.h"
+#include "qdatetime.h"
+#include "qvarlengtharray.h"
+
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <limits.h>
+#if defined(Q_OS_SYMBIAN)
+# include <sys/syslimits.h>
+# include <f32file.h>
+# include <pathinfo.h>
+# include "private/qcore_symbian_p.h"
+#endif
+#include <errno.h>
+#if !defined(QWS) && defined(Q_OS_MAC)
+# include <private/qcore_mac_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_OS_SYMBIAN)
+/*!
+ \internal
+
+ Returns true if supplied path is a relative path
+*/
+static bool isRelativePathSymbian(const QString& fileName)
+{
+ return !(fileName.startsWith(QLatin1Char('/'))
+ || (fileName.length() >= 2
+ && ((fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':'))
+ || (fileName.at(0) == QLatin1Char('/') && fileName.at(1) == QLatin1Char('/')))));
+}
+
+#endif
+
+#ifndef Q_OS_SYMBIAN
+/*!
+ \internal
+
+ Returns the stdlib open string corresponding to a QIODevice::OpenMode.
+*/
+static inline QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QFileSystemEntry &fileEntry,
+ QFileSystemMetaData &metaData)
+{
+ QByteArray mode;
+ if ((flags & QIODevice::ReadOnly) && !(flags & QIODevice::Truncate)) {
+ mode = "rb";
+ if (flags & QIODevice::WriteOnly) {
+ metaData.clearFlags(QFileSystemMetaData::FileType);
+ if (!fileEntry.isEmpty()
+ && QFileSystemEngine::fillMetaData(fileEntry, metaData, QFileSystemMetaData::FileType)
+ && metaData.isFile()) {
+ mode += '+';
+ } else {
+ mode = "wb+";
+ }
+ }
+ } else if (flags & QIODevice::WriteOnly) {
+ mode = "wb";
+ if (flags & QIODevice::ReadOnly)
+ mode += '+';
+ }
+ if (flags & QIODevice::Append) {
+ mode = "ab";
+ if (flags & QIODevice::ReadOnly)
+ mode += '+';
+ }
+
+#if defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0207
+ // must be glibc >= 2.7
+ mode += 'e';
+#endif
+
+ return mode;
+}
+#endif
+
+/*!
+ \internal
+
+ Returns the stdio open flags corresponding to a QIODevice::OpenMode.
+*/
+static inline int openModeToOpenFlags(QIODevice::OpenMode mode)
+{
+ int oflags = QT_OPEN_RDONLY;
+#ifdef QT_LARGEFILE_SUPPORT
+ oflags |= QT_OPEN_LARGEFILE;
+#endif
+
+ if ((mode & QFile::ReadWrite) == QFile::ReadWrite) {
+ oflags = QT_OPEN_RDWR | QT_OPEN_CREAT;
+ } else if (mode & QFile::WriteOnly) {
+ oflags = QT_OPEN_WRONLY | QT_OPEN_CREAT;
+ }
+
+ if (mode & QFile::Append) {
+ oflags |= QT_OPEN_APPEND;
+ } else if (mode & QFile::WriteOnly) {
+ if ((mode & QFile::Truncate) || !(mode & QFile::ReadOnly))
+ oflags |= QT_OPEN_TRUNC;
+ }
+
+ return oflags;
+}
+
+#ifndef Q_OS_SYMBIAN
+/*!
+ \internal
+
+ Sets the file descriptor to close on exec. That is, the file
+ descriptor is not inherited by child processes.
+*/
+static inline bool setCloseOnExec(int fd)
+{
+ return fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) != -1;
+}
+#endif
+
+#ifdef Q_OS_SYMBIAN
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
+{
+ Q_Q(QFSFileEngine);
+
+ fh = 0;
+ fd = -1;
+
+ QString fn(QFileSystemEngine::absoluteName(fileEntry).nativeFilePath());
+ RFs& fs = qt_s60GetRFs();
+
+ TUint symbianMode = 0;
+
+ if(openMode & QIODevice::ReadOnly)
+ symbianMode |= EFileRead;
+ if(openMode & QIODevice::WriteOnly)
+ symbianMode |= EFileWrite;
+ if(openMode & QIODevice::Text)
+ symbianMode |= EFileStreamText;
+
+ // pre Symbian 9.4, file I/O is always unbuffered, and the enum values don't exist
+ if(QSysInfo::symbianVersion() >= QSysInfo::SV_9_4) {
+ if (openMode & QFile::Unbuffered) {
+ if (openMode & QIODevice::WriteOnly)
+ symbianMode |= 0x00001000; //EFileWriteDirectIO;
+ // ### Unbuffered read is not used, because it prevents file open in /resource
+ // ### and has no obvious benefits
+ } else {
+ if (openMode & QIODevice::WriteOnly)
+ symbianMode |= 0x00000800; //EFileWriteBuffered;
+ // use implementation defaults for read buffering
+ }
+ }
+
+ // Until Qt supports file sharing, we can't support EFileShareReadersOrWriters safely,
+ // but Qt does this on other platforms and autotests rely on it.
+ // The reason is that Unix locks are only advisory - the application needs to test the
+ // lock after opening the file. Symbian and Windows locks are mandatory - opening a
+ // locked file will fail.
+ symbianMode |= EFileShareReadersOrWriters;
+
+ TInt r;
+ //note QIODevice::Truncate only has meaning for read/write access
+ //write-only files are always truncated unless append is specified
+ //reference openModeToOpenFlags in qfsfileengine_unix.cpp
+ if ((openMode & QIODevice::Truncate) || (!(openMode & QIODevice::ReadOnly) && !(openMode & QIODevice::Append))) {
+ r = symbianFile.Replace(fs, qt_QString2TPtrC(fn), symbianMode);
+ } else {
+ r = symbianFile.Open(fs, qt_QString2TPtrC(fn), symbianMode);
+ if (r == KErrNotFound && (openMode & QIODevice::WriteOnly)) {
+ r = symbianFile.Create(fs, qt_QString2TPtrC(fn), symbianMode);
+ }
+ }
+
+ if (r == KErrNone) {
+#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
+ TInt64 size;
+#else
+ TInt size;
+#endif
+ r = symbianFile.Size(size);
+ if (r==KErrNone) {
+ if (openMode & QIODevice::Append)
+ symbianFilePos = size;
+ else
+ symbianFilePos = 0;
+ //TODO: port this (QFileSystemMetaData in open?)
+ //cachedSize = size;
+ }
+ }
+
+ if (r != KErrNone) {
+ q->setError(QFile::OpenError, QSystemError(r, QSystemError::NativeError).toString());
+ symbianFile.Close();
+ return false;
+ }
+
+ closeFileHandle = true;
+ return true;
+}
+
+bool QFSFileEngine::open(QIODevice::OpenMode openMode, const RFile &file, QFile::FileHandleFlags handleFlags)
+{
+ Q_D(QFSFileEngine);
+
+ // Append implies WriteOnly.
+ if (openMode & QFile::Append)
+ openMode |= QFile::WriteOnly;
+
+ // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
+ if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
+ openMode |= QFile::Truncate;
+
+ d->openMode = openMode;
+ d->lastFlushFailed = false;
+ d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle);
+ d->fileEntry.clear();
+ d->fh = 0;
+ d->fd = -1;
+ d->tried_stat = 0;
+
+#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
+ //RFile64 adds only functions to RFile, no data members
+ d->symbianFile = static_cast<const RFile64&>(file);
+#else
+ d->symbianFile = file;
+#endif
+ TInt ret;
+ d->symbianFilePos = 0;
+ if (openMode & QFile::Append) {
+ // Seek to the end when in Append mode.
+ ret = d->symbianFile.Size(d->symbianFilePos);
+ } else {
+ // Seek to current otherwise
+ ret = d->symbianFile.Seek(ESeekCurrent, d->symbianFilePos);
+ }
+
+ if (ret != KErrNone) {
+ setError(QFile::OpenError, QSystemError(ret, QSystemError::NativeError).toString());
+
+ d->openMode = QIODevice::NotOpen;
+#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
+ d->symbianFile = RFile64();
+#else
+ d->symbianFile = RFile();
+#endif
+ return false;
+ }
+
+ // Extract filename (best effort)
+ TFileName fn;
+ TInt err = d->symbianFile.FullName(fn);
+ if (err == KErrNone)
+ d->fileEntry = QFileSystemEntry(qt_TDesC2QString(fn), QFileSystemEntry::FromNativePath());
+ else
+ d->fileEntry.clear();
+
+ return true;
+}
+#else
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
+{
+ Q_Q(QFSFileEngine);
+
+ if (openMode & QIODevice::Unbuffered) {
+ int flags = openModeToOpenFlags(openMode);
+
+ // Try to open the file in unbuffered mode.
+ do {
+ fd = QT_OPEN(fileEntry.nativeFilePath().constData(), flags, 0666);
+ } while (fd == -1 && errno == EINTR);
+
+ // On failure, return and report the error.
+ if (fd == -1) {
+ q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
+ qt_error_string(errno));
+ return false;
+ }
+
+ if (!(openMode & QIODevice::WriteOnly)) {
+ // we don't need this check if we tried to open for writing because then
+ // we had received EISDIR anyway.
+ if (QFileSystemEngine::fillMetaData(fd, metaData)
+ && metaData.isDirectory()) {
+ q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
+ QT_CLOSE(fd);
+ return false;
+ }
+ }
+
+ // Seek to the end when in Append mode.
+ if (flags & QFile::Append) {
+ int ret;
+ do {
+ ret = QT_LSEEK(fd, 0, SEEK_END);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1) {
+ q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
+ qt_error_string(int(errno)));
+ return false;
+ }
+ }
+
+ fh = 0;
+ } else {
+ QByteArray fopenMode = openModeToFopenMode(openMode, fileEntry, metaData);
+
+ // Try to open the file in buffered mode.
+ do {
+ fh = QT_FOPEN(fileEntry.nativeFilePath().constData(), fopenMode.constData());
+ } while (!fh && errno == EINTR);
+
+ // On failure, return and report the error.
+ if (!fh) {
+ q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
+ qt_error_string(int(errno)));
+ return false;
+ }
+
+ if (!(openMode & QIODevice::WriteOnly)) {
+ // we don't need this check if we tried to open for writing because then
+ // we had received EISDIR anyway.
+ if (QFileSystemEngine::fillMetaData(QT_FILENO(fh), metaData)
+ && metaData.isDirectory()) {
+ q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
+ fclose(fh);
+ return false;
+ }
+ }
+
+ setCloseOnExec(fileno(fh)); // ignore failure
+
+ // Seek to the end when in Append mode.
+ if (openMode & QIODevice::Append) {
+ int ret;
+ do {
+ ret = QT_FSEEK(fh, 0, SEEK_END);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1) {
+ q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
+ qt_error_string(int(errno)));
+ return false;
+ }
+ }
+
+ fd = -1;
+ }
+
+ closeFileHandle = true;
+ return true;
+}
+#endif
+
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeClose()
+{
+ return closeFdFh();
+}
+
+/*!
+ \internal
+
+*/
+bool QFSFileEnginePrivate::nativeFlush()
+{
+#ifdef Q_OS_SYMBIAN
+ if (symbianFile.SubSessionHandle())
+ return (KErrNone == symbianFile.Flush());
+#endif
+ return fh ? flushFh() : fd != -1;
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 len)
+{
+ Q_Q(QFSFileEngine);
+
+#ifdef Q_OS_SYMBIAN
+ if (symbianFile.SubSessionHandle()) {
+ if(len > KMaxTInt) {
+ //this check is more likely to catch a corrupt length, since it isn't possible to allocate 2GB buffers (yet..)
+ q->setError(QFile::ReadError, QLatin1String("Maximum 2GB in single read on this platform"));
+ return -1;
+ }
+ TPtr8 ptr(reinterpret_cast<TUint8*>(data), static_cast<TInt>(len));
+ TInt r = symbianFile.Read(symbianFilePos, ptr);
+ if (r != KErrNone)
+ {
+ q->setError(QFile::ReadError, QSystemError(r, QSystemError::NativeError).toString());
+ return -1;
+ }
+ symbianFilePos += ptr.Length();
+ return qint64(ptr.Length());
+ }
+#endif
+ if (fh && nativeIsSequential()) {
+ size_t readBytes = 0;
+ int oldFlags = fcntl(QT_FILENO(fh), F_GETFL);
+ for (int i = 0; i < 2; ++i) {
+ // Unix: Make the underlying file descriptor non-blocking
+ if ((oldFlags & O_NONBLOCK) == 0)
+ fcntl(QT_FILENO(fh), F_SETFL, oldFlags | O_NONBLOCK);
+
+ // Cross platform stdlib read
+ size_t read = 0;
+ do {
+ read = fread(data + readBytes, 1, size_t(len - readBytes), fh);
+ } while (read == 0 && !feof(fh) && errno == EINTR);
+ if (read > 0) {
+ readBytes += read;
+ break;
+ } else {
+ if (readBytes)
+ break;
+ readBytes = read;
+ }
+
+ // Unix: Restore the blocking state of the underlying socket
+ if ((oldFlags & O_NONBLOCK) == 0) {
+ fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
+ if (readBytes == 0) {
+ int readByte = 0;
+ do {
+ readByte = fgetc(fh);
+ } while (readByte == -1 && errno == EINTR);
+ if (readByte != -1) {
+ *data = uchar(readByte);
+ readBytes += 1;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ // Unix: Restore the blocking state of the underlying socket
+ if ((oldFlags & O_NONBLOCK) == 0) {
+ fcntl(QT_FILENO(fh), F_SETFL, oldFlags);
+ }
+ if (readBytes == 0 && !feof(fh)) {
+ // if we didn't read anything and we're not at EOF, it must be an error
+ q->setError(QFile::ReadError, qt_error_string(int(errno)));
+ return -1;
+ }
+ return readBytes;
+ }
+
+ return readFdFh(data, len);
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
+{
+ return readLineFdFh(data, maxlen);
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
+{
+#ifdef Q_OS_SYMBIAN
+ Q_Q(QFSFileEngine);
+ if (symbianFile.SubSessionHandle()) {
+ if(len > KMaxTInt) {
+ //this check is more likely to catch a corrupt length, since it isn't possible to allocate 2GB buffers (yet..)
+ q->setError(QFile::WriteError, QLatin1String("Maximum 2GB in single write on this platform"));
+ return -1;
+ }
+ const TPtrC8 ptr(reinterpret_cast<const TUint8*>(data), static_cast<TInt>(len));
+#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
+ TInt64 eofpos = 0;
+#else
+ TInt eofpos = 0;
+#endif
+ //The end of file position is not cached because QFile is read/write sharable, therefore another
+ //process may have altered the file size.
+ TInt r = symbianFile.Seek(ESeekEnd, eofpos);
+ if (r == KErrNone && symbianFilePos > eofpos) {
+ //seek position is beyond end of file so file needs to be extended before write.
+ //note that SetSize does not zero-initialise (c.f. posix lseek)
+ r = symbianFile.SetSize(symbianFilePos);
+ }
+ if (r == KErrNone) {
+ //write to specific position in the file (i.e. use our own cursor rather than calling seek)
+ r = symbianFile.Write(symbianFilePos, ptr);
+ }
+ if (r != KErrNone) {
+ q->setError(QFile::WriteError, QSystemError(r, QSystemError::NativeError).toString());
+ return -1;
+ }
+ symbianFilePos += len;
+ return len;
+ }
+#endif
+ return writeFdFh(data, len);
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativePos() const
+{
+#ifdef Q_OS_SYMBIAN
+ const Q_Q(QFSFileEngine);
+ if (symbianFile.SubSessionHandle()) {
+ return symbianFilePos;
+ }
+#endif
+ return posFdFh();
+}
+
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
+{
+#ifdef Q_OS_SYMBIAN
+ Q_Q(QFSFileEngine);
+ if (symbianFile.SubSessionHandle()) {
+#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
+ if(pos > KMaxTInt) {
+ q->setError(QFile::PositionError, QLatin1String("Maximum 2GB file position on this platform"));
+ return false;
+ }
+#endif
+ symbianFilePos = pos;
+ return true;
+ }
+#endif
+ return seekFdFh(pos);
+}
+
+/*!
+ \internal
+*/
+int QFSFileEnginePrivate::nativeHandle() const
+{
+ return fh ? fileno(fh) : fd;
+}
+
+#ifdef Q_OS_SYMBIAN
+int QFSFileEnginePrivate::getMapHandle()
+{
+ if (symbianFile.SubSessionHandle()) {
+ // Symbian file handle can't be used for open C mmap() so open the file with open C as well.
+ if (fileHandleForMaps < 0) {
+ int flags = openModeToOpenFlags(openMode);
+ flags &= ~(O_CREAT | O_TRUNC);
+ fileHandleForMaps = ::wopen((wchar_t*)(fileEntry.nativeFilePath().utf16()), flags, 0666);
+ }
+ return fileHandleForMaps;
+ }
+ return nativeHandle();
+}
+#endif
+
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeIsSequential() const
+{
+#ifdef Q_OS_SYMBIAN
+ if (symbianFile.SubSessionHandle())
+ return false;
+#endif
+ return isSequentialFdFh();
+}
+
+bool QFSFileEngine::remove()
+{
+ Q_D(QFSFileEngine);
+ QSystemError error;
+ bool ret = QFileSystemEngine::removeFile(d->fileEntry, error);
+ d->metaData.clear();
+ if (!ret) {
+ setError(QFile::RemoveError, error.toString());
+ }
+ return ret;
+}
+
+bool QFSFileEngine::copy(const QString &newName)
+{
+ Q_D(QFSFileEngine);
+ QSystemError error;
+ bool ret = QFileSystemEngine::copyFile(d->fileEntry, QFileSystemEntry(newName), error);
+ if (!ret) {
+ setError(QFile::CopyError, error.toString());
+ }
+ return ret;
+}
+
+bool QFSFileEngine::rename(const QString &newName)
+{
+ Q_D(QFSFileEngine);
+ QSystemError error;
+ bool ret = QFileSystemEngine::renameFile(d->fileEntry, QFileSystemEntry(newName), error);
+
+ if (!ret) {
+ setError(QFile::RenameError, error.toString());
+ }
+
+ return ret;
+}
+
+bool QFSFileEngine::link(const QString &newName)
+{
+ Q_D(QFSFileEngine);
+ QSystemError error;
+ bool ret = QFileSystemEngine::createLink(d->fileEntry, QFileSystemEntry(newName), error);
+ if (!ret) {
+ setError(QFile::RenameError, error.toString());
+ }
+ return ret;
+}
+
+qint64 QFSFileEnginePrivate::nativeSize() const
+{
+#ifdef Q_OS_SYMBIAN
+ const Q_Q(QFSFileEngine);
+ if (symbianFile.SubSessionHandle()) {
+#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
+ qint64 size;
+#else
+ TInt size;
+#endif
+ TInt err = symbianFile.Size(size);
+ if(err != KErrNone) {
+ const_cast<QFSFileEngine*>(q)->setError(QFile::PositionError, QSystemError(err, QSystemError::NativeError).toString());
+ return 0;
+ }
+ return size;
+ }
+#endif
+ return sizeFdFh();
+}
+
+bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
+{
+ return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories);
+}
+
+bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
+{
+ return QFileSystemEngine::removeDirectory(QFileSystemEntry(name), recurseParentDirectories);
+}
+
+bool QFSFileEngine::caseSensitive() const
+{
+#if defined(Q_OS_SYMBIAN)
+ return false;
+#else
+ return true;
+#endif
+}
+
+bool QFSFileEngine::setCurrentPath(const QString &path)
+{
+ return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path));
+}
+
+QString QFSFileEngine::currentPath(const QString &)
+{
+ return QFileSystemEngine::currentPath().filePath();
+}
+
+QString QFSFileEngine::homePath()
+{
+ return QFileSystemEngine::homePath();
+}
+
+QString QFSFileEngine::rootPath()
+{
+ return QFileSystemEngine::rootPath();
+}
+
+QString QFSFileEngine::tempPath()
+{
+ return QFileSystemEngine::tempPath();
+}
+
+QFileInfoList QFSFileEngine::drives()
+{
+ QFileInfoList ret;
+#if defined(Q_OS_SYMBIAN)
+ TDriveList driveList;
+ RFs rfs = qt_s60GetRFs();
+ TInt err = rfs.DriveList(driveList);
+ if (err == KErrNone) {
+ char driveName[] = "A:/";
+
+ for (char i = 0; i < KMaxDrives; i++) {
+ if (driveList[i]) {
+ driveName[0] = 'A' + i;
+ ret.append(QFileInfo(QLatin1String(driveName)));
+ }
+ }
+ } else {
+ qWarning("QFSFileEngine::drives: Getting drives failed");
+ }
+#else
+ ret.append(QFileInfo(rootPath()));
+#endif
+ return ret;
+}
+
+bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const
+{
+ if (!tried_stat || !metaData.hasFlags(flags)) {
+ tried_stat = 1;
+
+ int localFd = fd;
+ if (fh && fileEntry.isEmpty())
+ localFd = QT_FILENO(fh);
+ if (localFd != -1)
+ QFileSystemEngine::fillMetaData(localFd, metaData);
+
+ if (metaData.missingFlags(flags) && !fileEntry.isEmpty())
+ QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags));
+ }
+
+ return metaData.exists();
+}
+
+bool QFSFileEnginePrivate::isSymlink() const
+{
+ if (!metaData.hasFlags(QFileSystemMetaData::LinkType))
+ QFileSystemEngine::fillMetaData(fileEntry, metaData, QFileSystemMetaData::LinkType);
+
+ return metaData.isLink();
+}
+
+/*!
+ \reimp
+*/
+QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
+{
+ Q_D(const QFSFileEngine);
+
+ if (type & Refresh)
+ d->metaData.clear();
+
+ QAbstractFileEngine::FileFlags ret = 0;
+
+ if (type & FlagsMask)
+ ret |= LocalDiskFlag;
+
+ bool exists;
+ {
+ QFileSystemMetaData::MetaDataFlags queryFlags = 0;
+
+ queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type))
+ & QFileSystemMetaData::Permissions;
+
+ if (type & TypesMask)
+ queryFlags |= QFileSystemMetaData::AliasType
+ | QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::BundleType;
+
+ if (type & FlagsMask)
+ queryFlags |= QFileSystemMetaData::HiddenAttribute
+ | QFileSystemMetaData::ExistsAttribute;
+
+ queryFlags |= QFileSystemMetaData::LinkType;
+
+ exists = d->doStat(queryFlags);
+ }
+
+ if (!exists && !d->metaData.isLink())
+ return ret;
+
+ if (exists && (type & PermsMask))
+ ret |= FileFlags(uint(d->metaData.permissions()));
+
+ if (type & TypesMask) {
+ if (d->metaData.isAlias()) {
+ ret |= LinkType;
+ } else {
+ if ((type & LinkType) && d->metaData.isLink())
+ ret |= LinkType;
+ if (exists) {
+ if (d->metaData.isFile()) {
+ ret |= FileType;
+ } else if (d->metaData.isDirectory()) {
+ ret |= DirectoryType;
+ if ((type & BundleType) && d->metaData.isBundle())
+ ret |= BundleType;
+ }
+ }
+ }
+ }
+
+ if (type & FlagsMask) {
+ if (exists)
+ ret |= ExistsFlag;
+ if (d->fileEntry.isRoot())
+ ret |= RootFlag;
+ else if (d->metaData.isHidden())
+ ret |= HiddenFlag;
+ }
+
+ return ret;
+}
+
+QString QFSFileEngine::fileName(FileName file) const
+{
+ Q_D(const QFSFileEngine);
+ if (file == BundleName) {
+ return QFileSystemEngine::bundleName(d->fileEntry);
+ } else if (file == BaseName) {
+ return d->fileEntry.fileName();
+ } else if (file == PathName) {
+ return d->fileEntry.path();
+ } else if (file == AbsoluteName || file == AbsolutePathName) {
+ QFileSystemEntry entry(QFileSystemEngine::absoluteName(d->fileEntry));
+ if (file == AbsolutePathName) {
+ return entry.path();
+ }
+ return entry.filePath();
+ } else if (file == CanonicalName || file == CanonicalPathName) {
+ QFileSystemEntry entry(QFileSystemEngine::canonicalName(d->fileEntry, d->metaData));
+ if (file == CanonicalPathName)
+ return entry.path();
+ return entry.filePath();
+ } else if (file == LinkName) {
+ if (d->isSymlink()) {
+ QFileSystemEntry entry = QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData);
+ return entry.filePath();
+ }
+ return QString();
+ }
+ return d->fileEntry.filePath();
+}
+
+bool QFSFileEngine::isRelativePath() const
+{
+ Q_D(const QFSFileEngine);
+#if defined(Q_OS_SYMBIAN)
+ return isRelativePathSymbian(d->fileEntry.filePath());
+#else
+ return d->fileEntry.filePath().length() ? d->fileEntry.filePath()[0] != QLatin1Char('/') : true;
+#endif
+}
+
+uint QFSFileEngine::ownerId(FileOwner own) const
+{
+ Q_D(const QFSFileEngine);
+ static const uint nobodyID = (uint) -2;
+
+ if (d->doStat(QFileSystemMetaData::OwnerIds))
+ return d->metaData.ownerId(own);
+
+ return nobodyID;
+}
+
+QString QFSFileEngine::owner(FileOwner own) const
+{
+#ifndef Q_OS_SYMBIAN
+ if (own == OwnerUser)
+ return QFileSystemEngine::resolveUserName(ownerId(own));
+ return QFileSystemEngine::resolveGroupName(ownerId(own));
+#else
+ return QString();
+#endif
+}
+
+bool QFSFileEngine::setPermissions(uint perms)
+{
+ Q_D(QFSFileEngine);
+ QSystemError error;
+ if (!QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error, 0)) {
+ setError(QFile::PermissionsError, error.toString());
+ return false;
+ }
+ return true;
+}
+
+#ifdef Q_OS_SYMBIAN
+bool QFSFileEngine::setSize(qint64 size)
+{
+ Q_D(QFSFileEngine);
+ bool ret = false;
+ TInt err = KErrNone;
+ if (d->symbianFile.SubSessionHandle()) {
+ TInt err = d->symbianFile.SetSize(size);
+ ret = (err == KErrNone);
+ if (ret && d->symbianFilePos > size)
+ d->symbianFilePos = size;
+ }
+ else if (d->fd != -1)
+ ret = QT_FTRUNCATE(d->fd, size) == 0;
+ else if (d->fh)
+ ret = QT_FTRUNCATE(QT_FILENO(d->fh), size) == 0;
+ else {
+ RFile tmp;
+ QString symbianFilename(d->fileEntry.nativeFilePath());
+ err = tmp.Open(qt_s60GetRFs(), qt_QString2TPtrC(symbianFilename), EFileWrite);
+ if (err == KErrNone)
+ {
+ err = tmp.SetSize(size);
+ tmp.Close();
+ }
+ ret = (err == KErrNone);
+ }
+ if (!ret) {
+ QSystemError error;
+ if (err)
+ error = QSystemError(err, QSystemError::NativeError);
+ else
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ setError(QFile::ResizeError, error.toString());
+ }
+ return ret;
+}
+#else
+bool QFSFileEngine::setSize(qint64 size)
+{
+ Q_D(QFSFileEngine);
+ bool ret = false;
+ if (d->fd != -1)
+ ret = QT_FTRUNCATE(d->fd, size) == 0;
+ else if (d->fh)
+ ret = QT_FTRUNCATE(QT_FILENO(d->fh), size) == 0;
+ else
+ ret = QT_TRUNCATE(d->fileEntry.nativeFilePath().constData(), size) == 0;
+ if (!ret)
+ setError(QFile::ResizeError, qt_error_string(errno));
+ return ret;
+}
+#endif
+
+QDateTime QFSFileEngine::fileTime(FileTime time) const
+{
+ Q_D(const QFSFileEngine);
+
+ if (d->doStat(QFileSystemMetaData::Times))
+ return d->metaData.fileTime(time);
+
+ return QDateTime();
+}
+
+uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
+{
+ Q_Q(QFSFileEngine);
+ Q_UNUSED(flags);
+ if (openMode == QIODevice::NotOpen) {
+ q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
+ return 0;
+ }
+
+ if (offset < 0 || offset != qint64(QT_OFF_T(offset))
+ || size < 0 || quint64(size) > quint64(size_t(-1))) {
+ q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
+ return 0;
+ }
+
+ // If we know the mapping will extend beyond EOF, fail early to avoid
+ // undefined behavior. Otherwise, let mmap have its say.
+ if (doStat(QFileSystemMetaData::SizeAttribute)
+ && (QT_OFF_T(size) > metaData.size() - QT_OFF_T(offset)))
+ qWarning("QFSFileEngine::map: Mapping a file beyond its size is not portable");
+
+ int access = 0;
+ if (openMode & QIODevice::ReadOnly) access |= PROT_READ;
+ if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE;
+
+#if defined(Q_OS_INTEGRITY)
+ int pageSize = sysconf(_SC_PAGESIZE);
+#else
+ int pageSize = getpagesize();
+#endif
+ int extra = offset % pageSize;
+
+ if (quint64(size + extra) > quint64((size_t)-1)) {
+ q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
+ return 0;
+ }
+
+ size_t realSize = (size_t)size + extra;
+ QT_OFF_T realOffset = QT_OFF_T(offset);
+ realOffset &= ~(QT_OFF_T(pageSize - 1));
+
+#ifdef Q_OS_SYMBIAN
+ void *mapAddress;
+ TRAPD(err, mapAddress = QT_MMAP((void*)0, realSize,
+ access, MAP_SHARED, getMapHandle(), realOffset));
+ if (err != KErrNone) {
+ qWarning("OpenC bug: leave from mmap %d", err);
+ mapAddress = MAP_FAILED;
+ errno = EINVAL;
+ }
+#else
+ void *mapAddress = QT_MMAP((void*)0, realSize,
+ access, MAP_SHARED, nativeHandle(), realOffset);
+#endif
+ if (MAP_FAILED != mapAddress) {
+ uchar *address = extra + static_cast<uchar*>(mapAddress);
+ maps[address] = QPair<int,size_t>(extra, realSize);
+ return address;
+ }
+
+ switch(errno) {
+ case EBADF:
+ q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
+ break;
+ case ENFILE:
+ case ENOMEM:
+ q->setError(QFile::ResourceError, qt_error_string(int(errno)));
+ break;
+ case EINVAL:
+ // size are out of bounds
+ default:
+ q->setError(QFile::UnspecifiedError, qt_error_string(int(errno)));
+ break;
+ }
+ return 0;
+}
+
+bool QFSFileEnginePrivate::unmap(uchar *ptr)
+{
+#if !defined(Q_OS_INTEGRITY)
+ Q_Q(QFSFileEngine);
+ if (!maps.contains(ptr)) {
+ q->setError(QFile::PermissionsError, qt_error_string(EACCES));
+ return false;
+ }
+
+ uchar *start = ptr - maps[ptr].first;
+ size_t len = maps[ptr].second;
+ if (-1 == munmap(start, len)) {
+ q->setError(QFile::UnspecifiedError, qt_error_string(errno));
+ return false;
+ }
+ maps.remove(ptr);
+ return true;
+#else
+ return false;
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FSFILEENGINE