summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qfsfileengine_unix.cpp
diff options
context:
space:
mode:
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