From b98e374e94e81b66be0dc009356a729e9b97dce8 Mon Sep 17 00:00:00 2001 From: Shane Kearns Date: Wed, 1 Dec 2010 16:38:59 +0000 Subject: QFile API: add API to specify if adopted file handles should be closed QFile behaviour has been to not close file handles adopted by open(FILE*, OpenMode) and open(int fd, OpenMode) functions. This is inconvenient for frameworks which want to return an opened QFile to the user. In this case it would be better to transfer ownership of the handle to the QFile. New overloads are added which take an additional parameter from the QFile::FileHandleFlags flags. Currently only one bit is used to provide the AutoCloseHandle option, but it is extensible. The AutoCloseHandle option tells the QFile backend that it should close the handle when close() is called, rather than the default behaviour which remains to flush the handle and leave it open. The DontCloseHandle option is the inverse of this, specifying the old behaviour of flushing but not closing the file handle. Symbian OS file handles are not compatible with int, they are an opaque data type which contains two integers. The first identifies to the kernel the file server session The second identifies to the file server the subsession object (the file) The reason for this is that it has a microkernel architecture, the kernel has no support for files - all files and file systems are handled by a user mode process called the "file server". So for symbian, a new QFile::open() overload is added for adopting a symbian RFile handle. The API mirrors the existing API for POSIX file handles, but takes an RFile reference rather than an integer file descriptor. Task-number: QT-2924 Reviewed-by: joao Reviewed-by: mread Reviewed-by: Oswald Buddenhagen --- src/corelib/io/qfile.cpp | 187 ++++++++++++++++++++++++++++++++-- src/corelib/io/qfile.h | 14 +++ src/corelib/io/qfile_p.h | 7 +- src/corelib/io/qfsfileengine.cpp | 14 ++- src/corelib/io/qfsfileengine.h | 8 ++ src/corelib/io/qfsfileengine_unix.cpp | 59 +++++++++++ 6 files changed, 277 insertions(+), 12 deletions(-) diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index 85e78a6..e3bc8da 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -103,7 +103,7 @@ QFilePrivate::~QFilePrivate() } bool -QFilePrivate::openExternalFile(int flags, int fd) +QFilePrivate::openExternalFile(int flags, int fd, QFile::FileHandleFlags handleFlags) { #ifdef QT_NO_FSFILEENGINE Q_UNUSED(flags); @@ -113,14 +113,13 @@ QFilePrivate::openExternalFile(int flags, int fd) delete fileEngine; fileEngine = 0; QFSFileEngine *fe = new QFSFileEngine; - fe->setFileName(fileName); fileEngine = fe; - return fe->open(QIODevice::OpenMode(flags), fd); + return fe->open(QIODevice::OpenMode(flags), fd, handleFlags); #endif } bool -QFilePrivate::openExternalFile(int flags, FILE *fh) +QFilePrivate::openExternalFile(int flags, FILE *fh, QFile::FileHandleFlags handleFlags) { #ifdef QT_NO_FSFILEENGINE Q_UNUSED(flags); @@ -130,12 +129,28 @@ QFilePrivate::openExternalFile(int flags, FILE *fh) delete fileEngine; fileEngine = 0; QFSFileEngine *fe = new QFSFileEngine; - fe->setFileName(fileName); fileEngine = fe; - return fe->open(QIODevice::OpenMode(flags), fh); + return fe->open(QIODevice::OpenMode(flags), fh, handleFlags); #endif } +#ifdef Q_OS_SYMBIAN +bool QFilePrivate::openExternalFile(int flags, const RFile &f, QFile::FileHandleFlags handleFlags) +{ +#ifdef QT_NO_FSFILEENGINE + Q_UNUSED(flags); + Q_UNUSED(fh); + return false; +#else + delete fileEngine; + fileEngine = 0; + QFSFileEngine *fe = new QFSFileEngine; + fileEngine = fe; + return fe->open(QIODevice::OpenMode(flags), f, handleFlags); +#endif +} +#endif + inline bool QFilePrivate::ensureFlushed() const { // This function ensures that the write buffer has been flushed (const @@ -341,6 +356,20 @@ QFilePrivate::setError(QFile::FileError err, int errNum) \snippet doc/src/snippets/ntfsp.cpp 1 */ +/*! + \enum QFile::FileHandleFlag + + This enum is used when opening a file to specify additional + options which only apply to files and not to a generic + QIODevice. + + \value AutoCloseHandle The file handle passed into open() should be + closed by close(), the default behaviour is that close just flushes + the file and the app is responsible for closing the file handle. When + opening a file by name, this flag is ignored as Qt always "owns" the + file handle and must close it. + */ + #ifdef QT3_SUPPORT /*! \typedef QFile::PermissionSpec @@ -1058,8 +1087,57 @@ bool QFile::open(OpenMode mode) \snippet doc/src/snippets/code/src_corelib_io_qfile.cpp 4 */ +// ### Qt5: merge this into new overload with a default parameter bool QFile::open(FILE *fh, OpenMode mode) { + return open(fh, mode, DontCloseHandle); +} + +/*! + \overload + + Opens the existing file handle \a fh in the given \a mode. + Returns true if successful; otherwise returns false. + + Example: + \snippet doc/src/snippets/code/src_corelib_io_qfile.cpp 3 + + When a QFile is opened using this function, behaviour of close() is + controlled by the AutoCloseHandle flag. + If AutoCloseHandle is specified, and this function succeeds, + then calling close() closes the adopted handle. + Otherwise, close() does not actually close the file, but only flushes it. + + \bold{Warning:} + \list 1 + \o If \a fh does not refer to a regular file, e.g., it is \c stdin, + \c stdout, or \c stderr, you may not be able to seek(). size() + returns \c 0 in those cases. See QIODevice::isSequential() for + more information. + \o Since this function opens the file without specifying the file name, + you cannot use this QFile with a QFileInfo. + \endlist + + \note For Windows CE you may not be able to call resize(). + + \sa close(), {qmake Variable Reference#CONFIG}{qmake Variable Reference} + + \bold{Note for the Windows Platform} + + \a fh must be opened in binary mode (i.e., the mode string must contain + 'b', as in "rb" or "wb") when accessing files and other random-access + devices. Qt will translate the end-of-line characters if you pass + QIODevice::Text to \a mode. Sequential devices, such as stdin and stdout, + are unaffected by this limitation. + + You need to enable support for console applications in order to use the + stdin, stdout and stderr streams at the console. To do this, add the + following declaration to your application's project file: + + \snippet doc/src/snippets/code/src_corelib_io_qfile.cpp 4 +*/ +bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags) +{ Q_D(QFile); if (isOpen()) { qWarning("QFile::open: File (%s) already open", qPrintable(fileName())); @@ -1072,7 +1150,7 @@ bool QFile::open(FILE *fh, OpenMode mode) qWarning("QFile::open: File access not specified"); return false; } - if(d->openExternalFile(mode, fh)) { + if (d->openExternalFile(mode, fh, handleFlags)) { QIODevice::open(mode); if (mode & Append) { seek(size()); @@ -1118,8 +1196,44 @@ bool QFile::open(FILE *fh, OpenMode mode) \sa close() */ +// ### Qt5: merge this into new overload with a default parameter bool QFile::open(int fd, OpenMode mode) { + return open(fd, mode, DontCloseHandle); +} + +/*! + \overload + + Opens the existing file descriptor \a fd in the given \a mode. + Returns true if successful; otherwise returns false. + + When a QFile is opened using this function, behaviour of close() is + controlled by the AutoCloseHandle flag. + If AutoCloseHandle is specified, and this function succeeds, + then calling close() closes the adopted handle. + Otherwise, close() does not actually close the file, but only flushes it. + + The QFile that is opened using this function is automatically set + to be in raw mode; this means that the file input/output functions + are slow. If you run into performance issues, you should try to + use one of the other open functions. + + \warning If \a fd is not a regular file, e.g, it is 0 (\c stdin), + 1 (\c stdout), or 2 (\c stderr), you may not be able to seek(). In + those cases, size() returns \c 0. See QIODevice::isSequential() + for more information. + + \warning For Windows CE you may not be able to call seek(), setSize(), + fileTime(). size() returns \c 0. + + \warning Since this function opens the file without specifying the file name, + you cannot use this QFile with a QFileInfo. + + \sa close() +*/ +bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags) +{ Q_D(QFile); if (isOpen()) { qWarning("QFile::open: File (%s) already open", qPrintable(fileName())); @@ -1132,7 +1246,7 @@ bool QFile::open(int fd, OpenMode mode) qWarning("QFile::open: File access not specified"); return false; } - if(d->openExternalFile(mode, fd)) { + if (d->openExternalFile(mode, fd, handleFlags)) { QIODevice::open(mode); if (mode & Append) { seek(size()); @@ -1146,6 +1260,63 @@ bool QFile::open(int fd, OpenMode mode) return false; } +#ifdef Q_OS_SYMBIAN +/*! + \overload + + Opens the existing file object \a f in the given \a mode. + Returns true if successful; otherwise returns false. + + When a QFile is opened using this function, behaviour of close() is + controlled by the AutoCloseHandle flag. + If AutoCloseHandle is specified, and this function succeeds, + then calling close() closes the adopted handle. + Otherwise, close() does not actually close the file, but only flushes it. + + \warning If the file handle is adopted from another process, + you may not be able to use this QFile with a QFileInfo. + + \sa close() +*/ +bool QFile::open(const RFile &f, OpenMode mode, FileHandleFlags handleFlags) +{ + Q_D(QFile); + if (isOpen()) { + qWarning("QFile::open: File (%s) already open", qPrintable(fileName())); + return false; + } + if (mode & Append) + mode |= WriteOnly; + unsetError(); + if ((mode & (ReadOnly | WriteOnly)) == 0) { + qWarning("QFile::open: File access not specified"); + return false; + } + if (d->openExternalFile(mode, f, handleFlags)) { + bool ok = QIODevice::open(mode); + if (ok) { + if (mode & Append) { + ok = seek(size()); + } else { + qint64 pos = 0; + TInt err; +#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + err = static_cast(f).Seek(ESeekCurrent, pos); +#else + TInt pos32 = 0; + err = f.Seek(ESeekCurrent, pos32); + pos = pos32; +#endif + ok = ok && (err == KErrNone); + ok = ok && seek(pos); + } + } + return ok; + } + return false; +} +#endif + /*! Returns the file handle of the file. diff --git a/src/corelib/io/qfile.h b/src/corelib/io/qfile.h index 212576c..fa9e5aa 100644 --- a/src/corelib/io/qfile.h +++ b/src/corelib/io/qfile.h @@ -45,6 +45,9 @@ #include #include #include +#ifdef Q_OS_SYMBIAN +#include +#endif #ifdef open #error qfile.h must be included before any header file that defines open @@ -97,6 +100,12 @@ public: }; Q_DECLARE_FLAGS(Permissions, Permission) + enum FileHandleFlag { + AutoCloseHandle = 0x0001, + DontCloseHandle = 0 + }; + Q_DECLARE_FLAGS(FileHandleFlags, FileHandleFlag) + QFile(); QFile(const QString &name); #ifndef QT_NO_QOBJECT @@ -145,6 +154,11 @@ public: bool open(OpenMode flags); bool open(FILE *f, OpenMode flags); bool open(int fd, OpenMode flags); +#ifdef Q_OS_SYMBIAN + bool open(const RFile &f, OpenMode flags, FileHandleFlags handleFlags = DontCloseHandle); +#endif + bool open(FILE *f, OpenMode ioFlags, FileHandleFlags handleFlags); + bool open(int fd, OpenMode ioFlags, FileHandleFlags handleFlags); virtual void close(); qint64 size() const; diff --git a/src/corelib/io/qfile_p.h b/src/corelib/io/qfile_p.h index cf76c09..085bbf0 100644 --- a/src/corelib/io/qfile_p.h +++ b/src/corelib/io/qfile_p.h @@ -67,8 +67,11 @@ protected: QFilePrivate(); ~QFilePrivate(); - bool openExternalFile(int flags, int fd); - bool openExternalFile(int flags, FILE *fh); + bool openExternalFile(int flags, int fd, QFile::FileHandleFlags handleFlags); + bool openExternalFile(int flags, FILE *fh, QFile::FileHandleFlags handleFlags); +#ifdef Q_OS_SYMBIAN + bool openExternalFile(int flags, const RFile& f, QFile::FileHandleFlags handleFlags); +#endif QString fileName; mutable QAbstractFileEngine *fileEngine; diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp index ae301f7..f1d3db5 100644 --- a/src/corelib/io/qfsfileengine.cpp +++ b/src/corelib/io/qfsfileengine.cpp @@ -230,6 +230,11 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode) */ bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh) { + return open(openMode, fh, QFile::DontCloseHandle); +} + +bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHandleFlags handleFlags) +{ Q_D(QFSFileEngine); // Append implies WriteOnly. @@ -242,7 +247,7 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh) d->openMode = openMode; d->lastFlushFailed = false; - d->closeFileHandle = false; + d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); d->fileEntry.clear(); d->tried_stat = 0; d->fd = -1; @@ -286,6 +291,11 @@ bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh) */ bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd) { + return open(openMode, fd, QFile::DontCloseHandle); +} + +bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd, QFile::FileHandleFlags handleFlags) +{ Q_D(QFSFileEngine); // Append implies WriteOnly. @@ -298,7 +308,7 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd) d->openMode = openMode; d->lastFlushFailed = false; - d->closeFileHandle = false; + d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); d->fileEntry.clear(); d->fh = 0; d->fd = -1; diff --git a/src/corelib/io/qfsfileengine.h b/src/corelib/io/qfsfileengine.h index 6b077ed..73c8c77 100644 --- a/src/corelib/io/qfsfileengine.h +++ b/src/corelib/io/qfsfileengine.h @@ -43,6 +43,9 @@ #define QFSFILEENGINE_H #include +#ifdef Q_OS_SYMBIAN +#include +#endif #ifndef QT_NO_FSFILEENGINE @@ -101,6 +104,11 @@ public: //FS only!! bool open(QIODevice::OpenMode flags, int fd); + bool open(QIODevice::OpenMode flags, int fd, QFile::FileHandleFlags handleFlags); + bool open(QIODevice::OpenMode flags, FILE *fh, QFile::FileHandleFlags handleFlags); +#ifdef Q_OS_SYMBIAN + bool open(QIODevice::OpenMode flags, const RFile &f, QFile::FileHandleFlags handleFlags); +#endif static bool setCurrentPath(const QString &path); static QString currentPath(const QString &path = QString()); static QString homePath(); diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp index 1e1b35b..d0350f0 100644 --- a/src/corelib/io/qfsfileengine_unix.cpp +++ b/src/corelib/io/qfsfileengine_unix.cpp @@ -251,6 +251,65 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) 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(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 -- cgit v0.12