From 93eb3d692c514036c12706ed66a77ef1a46d1091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=40pc2009?= <Пользователь@pc2009> Date: Sat, 4 Apr 2015 18:47:25 +0400 Subject: [PATCH] Backporting QlockFile. Successful build on Windows. --HG-- branch : develop --- src/app/app.pro | 3 + src/app/core/backport/qabstractfileengine.cpp | 1251 ++++++++++++++++ src/app/core/backport/qabstractfileengine_p.h | 251 ++++ .../{ => backport}/qcommandlineoption.cpp | 0 .../core/{ => backport}/qcommandlineoption.h | 0 .../{ => backport}/qcommandlineparser.cpp | 0 .../core/{ => backport}/qcommandlineparser.h | 0 src/app/core/backport/qcore_mac.cpp | 61 + src/app/core/backport/qcore_mac_p.h | 131 ++ src/app/core/backport/qcore_unix.cpp | 109 ++ src/app/core/backport/qcore_unix_p.h | 335 +++++ src/app/core/backport/qfileinfo_p.h | 164 ++ src/app/core/backport/qfilesystemengine.cpp | 403 +++++ src/app/core/backport/qfilesystemengine_p.h | 120 ++ .../core/backport/qfilesystemengine_unix.cpp | 756 ++++++++++ .../core/backport/qfilesystemengine_win.cpp | 1324 +++++++++++++++++ src/app/core/backport/qfilesystementry.cpp | 388 +++++ src/app/core/backport/qfilesystementry_p.h | 106 ++ src/app/core/backport/qfilesystemiterator_p.h | 94 ++ .../backport/qfilesystemiterator_unix.cpp | 112 ++ .../core/backport/qfilesystemiterator_win.cpp | 129 ++ src/app/core/backport/qfilesystemmetadata_p.h | 341 +++++ src/app/core/backport/qfsfileengine.cpp | 942 ++++++++++++ .../core/backport/qfsfileengine_iterator.cpp | 85 ++ .../core/backport/qfsfileengine_iterator_p.h | 70 + src/app/core/backport/qfsfileengine_p.h | 218 +++ src/app/core/backport/qfsfileengine_unix.cpp | 749 ++++++++++ src/app/core/backport/qfsfileengine_win.cpp | 1053 +++++++++++++ src/app/core/{ => backport}/qlockfile.cpp | 4 +- src/app/core/{ => backport}/qlockfile.h | 0 src/app/core/{ => backport}/qlockfile_p.h | 0 src/app/core/backport/qlockfile_unix.cpp | 193 +++ src/app/core/backport/qlockfile_win.cpp | 116 ++ src/app/core/backport/qmutexpool.cpp | 133 ++ src/app/core/backport/qmutexpool_p.h | 70 + src/app/core/backport/qresource.h | 77 + src/app/core/backport/qresource_p.h | 98 ++ src/app/core/backport/qsystemerror.cpp | 154 ++ src/app/core/backport/qsystemerror_p.h | 85 ++ src/app/core/backport/qsystemlibrary_p.h | 90 ++ src/app/core/core.pri | 66 +- src/app/core/vapplication.cpp | 2 +- src/app/main.cpp | 2 +- src/app/mainwindow.cpp | 2 +- 44 files changed, 10275 insertions(+), 12 deletions(-) create mode 100644 src/app/core/backport/qabstractfileengine.cpp create mode 100644 src/app/core/backport/qabstractfileengine_p.h rename src/app/core/{ => backport}/qcommandlineoption.cpp (100%) rename src/app/core/{ => backport}/qcommandlineoption.h (100%) rename src/app/core/{ => backport}/qcommandlineparser.cpp (100%) rename src/app/core/{ => backport}/qcommandlineparser.h (100%) create mode 100644 src/app/core/backport/qcore_mac.cpp create mode 100644 src/app/core/backport/qcore_mac_p.h create mode 100644 src/app/core/backport/qcore_unix.cpp create mode 100644 src/app/core/backport/qcore_unix_p.h create mode 100644 src/app/core/backport/qfileinfo_p.h create mode 100644 src/app/core/backport/qfilesystemengine.cpp create mode 100644 src/app/core/backport/qfilesystemengine_p.h create mode 100644 src/app/core/backport/qfilesystemengine_unix.cpp create mode 100644 src/app/core/backport/qfilesystemengine_win.cpp create mode 100644 src/app/core/backport/qfilesystementry.cpp create mode 100644 src/app/core/backport/qfilesystementry_p.h create mode 100644 src/app/core/backport/qfilesystemiterator_p.h create mode 100644 src/app/core/backport/qfilesystemiterator_unix.cpp create mode 100644 src/app/core/backport/qfilesystemiterator_win.cpp create mode 100644 src/app/core/backport/qfilesystemmetadata_p.h create mode 100644 src/app/core/backport/qfsfileengine.cpp create mode 100644 src/app/core/backport/qfsfileengine_iterator.cpp create mode 100644 src/app/core/backport/qfsfileengine_iterator_p.h create mode 100644 src/app/core/backport/qfsfileengine_p.h create mode 100644 src/app/core/backport/qfsfileengine_unix.cpp create mode 100644 src/app/core/backport/qfsfileengine_win.cpp rename src/app/core/{ => backport}/qlockfile.cpp (98%) rename src/app/core/{ => backport}/qlockfile.h (100%) rename src/app/core/{ => backport}/qlockfile_p.h (100%) create mode 100644 src/app/core/backport/qlockfile_unix.cpp create mode 100644 src/app/core/backport/qlockfile_win.cpp create mode 100644 src/app/core/backport/qmutexpool.cpp create mode 100644 src/app/core/backport/qmutexpool_p.h create mode 100644 src/app/core/backport/qresource.h create mode 100644 src/app/core/backport/qresource_p.h create mode 100644 src/app/core/backport/qsystemerror.cpp create mode 100644 src/app/core/backport/qsystemerror_p.h create mode 100644 src/app/core/backport/qsystemlibrary_p.h diff --git a/src/app/app.pro b/src/app/app.pro index 6434b95be..cbb78350a 100644 --- a/src/app/app.pro +++ b/src/app/app.pro @@ -430,6 +430,9 @@ DEPENDPATH += $$PWD/../libs/vlayout win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../libs/vlayout/$${DESTDIR}/vlayout.lib else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../libs/vlayout/$${DESTDIR}/libvlayout.a +# For build qt backport code +win32: LIBS+= libole32 libuuid + # Strip after you link all libaries. CONFIG(release, debug|release){ diff --git a/src/app/core/backport/qabstractfileengine.cpp b/src/app/core/backport/qabstractfileengine.cpp new file mode 100644 index 000000000..ec408c5c2 --- /dev/null +++ b/src/app/core/backport/qabstractfileengine.cpp @@ -0,0 +1,1251 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qabstractfileengine_p.h" +#include "qfsfileengine_p.h" +#ifdef QT_BUILD_CORE_LIB +#include "qresource_p.h" +#endif +#include +#include +#include +// built-in handlers +#include +#include + +#include "qfilesystementry_p.h" +#include "qfilesystemmetadata_p.h" +#include "qfilesystemengine_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +/*! + \class QAbstractFileEngineHandler + \inmodule QtCore + \reentrant + \internal + + \brief The QAbstractFileEngineHandler class provides a way to register + custom file engines with your application. + + \ingroup io + \since 4.1 + + QAbstractFileEngineHandler is a factory for creating QAbstractFileEngine + objects (file engines), which are used internally by QFile, QFileInfo, and + QDir when working with files and directories. + + When you open a file, Qt chooses a suitable file engine by passing the + file name from QFile or QDir through an internal list of registered file + engine handlers. The first handler to recognize the file name is used to + create the engine. Qt provides internal file engines for working with + regular files and resources, but you can also register your own + QAbstractFileEngine subclasses. + + To install an application-specific file engine, you subclass + QAbstractFileEngineHandler and reimplement create(). When you instantiate + the handler (e.g. by creating an instance on the stack or on the heap), it + will automatically register with Qt. (The latest registered handler takes + precedence over existing handlers.) + + For example: + + \snippet code/src_corelib_io_qabstractfileengine.cpp 0 + + When the handler is destroyed, it is automatically removed from Qt. + + The most common approach to registering a handler is to create an instance + as part of the start-up phase of your application. It is also possible to + limit the scope of the file engine handler to a particular area of + interest (e.g. a special file dialog that needs a custom file engine). By + creating the handler inside a local scope, you can precisely control the + area in which your engine will be applied without disturbing file + operations in other parts of your application. + + \sa QAbstractFileEngine, QAbstractFileEngine::create() +*/ + +static bool qt_file_engine_handlers_in_use = false; + +/* + All application-wide handlers are stored in this list. The mutex must be + acquired to ensure thread safety. + */ +Q_GLOBAL_STATIC_WITH_ARGS(QReadWriteLock, fileEngineHandlerMutex, (QReadWriteLock::Recursive)) + +static bool qt_abstractfileenginehandlerlist_shutDown = false; +class QAbstractFileEngineHandlerList : public QList +{ +public: + ~QAbstractFileEngineHandlerList() + { + QWriteLocker locker(fileEngineHandlerMutex()); + qt_abstractfileenginehandlerlist_shutDown = true; + } +}; +Q_GLOBAL_STATIC(QAbstractFileEngineHandlerList, fileEngineHandlers) + +/*! + Constructs a file handler and registers it with Qt. Once created this + handler's create() function will be called (along with all the other + handlers) for any paths used. The most recently created handler that + recognizes the given path (i.e. that returns a QAbstractFileEngine) is + used for the new path. + + \sa create() + */ +QAbstractFileEngineHandler::QAbstractFileEngineHandler() +{ + QWriteLocker locker(fileEngineHandlerMutex()); + qt_file_engine_handlers_in_use = true; + fileEngineHandlers()->prepend(this); +} + +/*! + Destroys the file handler. This will automatically unregister the handler + from Qt. + */ +QAbstractFileEngineHandler::~QAbstractFileEngineHandler() +{ + QWriteLocker locker(fileEngineHandlerMutex()); + // Remove this handler from the handler list only if the list is valid. + if (!qt_abstractfileenginehandlerlist_shutDown) { + QAbstractFileEngineHandlerList *handlers = fileEngineHandlers(); + handlers->removeOne(this); + if (handlers->isEmpty()) + qt_file_engine_handlers_in_use = false; + } +} + +/* + \internal + + Handles calls to custom file engine handlers. +*/ +QAbstractFileEngine *qt_custom_file_engine_handler_create(const QString &path) +{ + QAbstractFileEngine *engine = 0; + + if (qt_file_engine_handlers_in_use) { + QReadLocker locker(fileEngineHandlerMutex()); + + // check for registered handlers that can load the file + QAbstractFileEngineHandlerList *handlers = fileEngineHandlers(); + for (int i = 0; i < handlers->size(); i++) { + if ((engine = handlers->at(i)->create(path))) + break; + } + } + + return engine; +} + +/*! + \fn QAbstractFileEngine *QAbstractFileEngineHandler::create(const QString &fileName) const + + Creates a file engine for file \a fileName. Returns 0 if this + file handler cannot handle \a fileName. + + Example: + + \snippet code/src_corelib_io_qabstractfileengine.cpp 1 + + \sa QAbstractFileEngine::create() +*/ + +/*! + Creates and returns a QAbstractFileEngine suitable for processing \a + fileName. + + You should not need to call this function; use QFile, QFileInfo or + QDir directly instead. + + If you reimplemnt this function, it should only return file + engines that knows how to handle \a fileName; otherwise, it should + return 0. + + \sa QAbstractFileEngineHandler +*/ +QAbstractFileEngine *QAbstractFileEngine::create(const QString &fileName) +{ + QFileSystemEntry entry(fileName); + QFileSystemMetaData metaData; + QAbstractFileEngine *engine = QFileSystemEngine::resolveEntryAndCreateLegacyEngine(entry, metaData); + +#ifndef QT_NO_FSFILEENGINE + if (!engine) + // fall back to regular file engine + return new QFSFileEngine(entry.filePath()); +#endif + + return engine; +} + +/*! + \class QAbstractFileEngine + \inmodule QtCore + \reentrant + \internal + + \brief The QAbstractFileEngine class provides an abstraction for accessing + the filesystem. + + \ingroup io + \since 4.1 + + The QDir, QFile, and QFileInfo classes all make use of a + QAbstractFileEngine internally. If you create your own QAbstractFileEngine + subclass (and register it with Qt by creating a QAbstractFileEngineHandler + subclass), your file engine will be used when the path is one that your + file engine handles. + + A QAbstractFileEngine refers to one file or one directory. If the referent + is a file, the setFileName(), rename(), and remove() functions are + applicable. If the referent is a directory the mkdir(), rmdir(), and + entryList() functions are applicable. In all cases the caseSensitive(), + isRelativePath(), fileFlags(), ownerId(), owner(), and fileTime() + functions are applicable. + + A QAbstractFileEngine subclass can be created to do synchronous network I/O + based file system operations, local file system operations, or to operate + as a resource system to access file based resources. + + \sa QAbstractFileEngineHandler +*/ + +/*! + \enum QAbstractFileEngine::FileName + + These values are used to request a file name in a particular + format. + + \value DefaultName The same filename that was passed to the + QAbstractFileEngine. + \value BaseName The name of the file excluding the path. + \value PathName The path to the file excluding the base name. + \value AbsoluteName The absolute path to the file (including + the base name). + \value AbsolutePathName The absolute path to the file (excluding + the base name). + \value LinkName The full file name of the file that this file is a + link to. (This will be empty if this file is not a link.) + \value CanonicalName Often very similar to LinkName. Will return the true path to the file. + \value CanonicalPathName Same as CanonicalName, excluding the base name. + \value BundleName Returns the name of the bundle implies BundleType is set. + + \omitvalue NFileNames + + \sa fileName(), setFileName() +*/ + +/*! + \enum QAbstractFileEngine::FileFlag + + The permissions and types of a file, suitable for OR'ing together. + + \value ReadOwnerPerm The owner of the file has permission to read + it. + \value WriteOwnerPerm The owner of the file has permission to + write to it. + \value ExeOwnerPerm The owner of the file has permission to + execute it. + \value ReadUserPerm The current user has permission to read the + file. + \value WriteUserPerm The current user has permission to write to + the file. + \value ExeUserPerm The current user has permission to execute the + file. + \value ReadGroupPerm Members of the current user's group have + permission to read the file. + \value WriteGroupPerm Members of the current user's group have + permission to write to the file. + \value ExeGroupPerm Members of the current user's group have + permission to execute the file. + \value ReadOtherPerm All users have permission to read the file. + \value WriteOtherPerm All users have permission to write to the + file. + \value ExeOtherPerm All users have permission to execute the file. + + \value LinkType The file is a link to another file (or link) in + the file system (i.e. not a file or directory). + \value FileType The file is a regular file to the file system + (i.e. not a link or directory) + \value BundleType The file is a Mac OS X bundle implies DirectoryType + \value DirectoryType The file is a directory in the file system + (i.e. not a link or file). + + \value HiddenFlag The file is hidden. + \value ExistsFlag The file actually exists in the file system. + \value RootFlag The file or the file pointed to is the root of the filesystem. + \value LocalDiskFlag The file resides on the local disk and can be passed to standard file functions. + \value Refresh Passing this flag will force the file engine to refresh all flags. + + \omitvalue PermsMask + \omitvalue TypesMask + \omitvalue FlagsMask + \omitvalue FileInfoAll + + \sa fileFlags(), setFileName() +*/ + +/*! + \enum QAbstractFileEngine::FileTime + + These are used by the fileTime() function. + + \value CreationTime When the file was created. + \value ModificationTime When the file was most recently modified. + \value AccessTime When the file was most recently accessed (e.g. + read or written to). + + \sa setFileName() +*/ + +/*! + \enum QAbstractFileEngine::FileOwner + + \value OwnerUser The user who owns the file. + \value OwnerGroup The group who owns the file. + + \sa owner(), ownerId(), setFileName() +*/ + +/*! + Constructs a new QAbstractFileEngine that does not refer to any file or directory. + + \sa setFileName() + */ +QAbstractFileEngine::QAbstractFileEngine() : d_ptr(new QAbstractFileEnginePrivate) +{ + d_ptr->q_ptr = this; +} + +/*! + \internal + + Constructs a QAbstractFileEngine. + */ +QAbstractFileEngine::QAbstractFileEngine(QAbstractFileEnginePrivate &dd) : d_ptr(&dd) +{ + d_ptr->q_ptr = this; +} + +/*! + Destroys the QAbstractFileEngine. + */ +QAbstractFileEngine::~QAbstractFileEngine() +{ +} + +/*! + \fn bool QAbstractFileEngine::open(QIODevice::OpenMode mode) + + Opens the file in the specified \a mode. Returns \c true if the file + was successfully opened; otherwise returns \c false. + + The \a mode is an OR combination of QIODevice::OpenMode and + QIODevice::HandlingMode values. +*/ +bool QAbstractFileEngine::open(QIODevice::OpenMode openMode) +{ + Q_UNUSED(openMode); + return false; +} + +/*! + Closes the file, returning true if successful; otherwise returns \c false. + + The default implementation always returns \c false. +*/ +bool QAbstractFileEngine::close() +{ + return false; +} + +/*! + \since 5.1 + + Flushes and syncs the file to disk. + + Returns \c true if successful; otherwise returns \c false. + The default implementation always returns \c false. +*/ +bool QAbstractFileEngine::syncToDisk() +{ + return false; +} + +/*! + Flushes the open file, returning true if successful; otherwise returns + false. + + The default implementation always returns \c false. +*/ +bool QAbstractFileEngine::flush() +{ + return false; +} + +/*! + Returns the size of the file. +*/ +qint64 QAbstractFileEngine::size() const +{ + return 0; +} + +/*! + Returns the current file position. + + This is the position of the data read/write head of the file. +*/ +qint64 QAbstractFileEngine::pos() const +{ + return 0; +} + +/*! + \fn bool QAbstractFileEngine::seek(qint64 offset) + + Sets the file position to the given \a offset. Returns \c true if + the position was successfully set; otherwise returns \c false. + + The offset is from the beginning of the file, unless the + file is sequential. + + \sa isSequential() +*/ +bool QAbstractFileEngine::seek(qint64 pos) +{ + Q_UNUSED(pos); + return false; +} + +/*! + Returns \c true if the file is a sequential access device; returns + false if the file is a direct access device. + + Operations involving size() and seek(int) are not valid on + sequential devices. +*/ +bool QAbstractFileEngine::isSequential() const +{ + return false; +} + +/*! + Requests that the file is deleted from the file system. If the + operation succeeds return true; otherwise return false. + + This virtual function must be reimplemented by all subclasses. + + \sa setFileName(), rmdir() + */ +bool QAbstractFileEngine::remove() +{ + return false; +} + +/*! + Copies the contents of this file to a file with the name \a newName. + Returns \c true on success; otherwise, false is returned. +*/ +bool QAbstractFileEngine::copy(const QString &newName) +{ + Q_UNUSED(newName); + return false; +} + +/*! + Requests that the file be renamed to \a newName in the file + system. If the operation succeeds return true; otherwise return + false. + + This virtual function must be reimplemented by all subclasses. + + \sa setFileName() + */ +bool QAbstractFileEngine::rename(const QString &newName) +{ + Q_UNUSED(newName); + return false; +} + +/*! + \since 5.1 + + Requests that the file be renamed to \a newName in the file + system. If the new name already exists, it must be overwritten. + If the operation succeeds, returns \c true; otherwise returns + false. + + This virtual function must be reimplemented by all subclasses. + + \sa setFileName() + */ +bool QAbstractFileEngine::renameOverwrite(const QString &newName) +{ + Q_UNUSED(newName); + return false; +} + +/*! + Creates a link from the file currently specified by fileName() to + \a newName. What a link is depends on the underlying filesystem + (be it a shortcut on Windows or a symbolic link on Unix). Returns + true if successful; otherwise returns \c false. +*/ +bool QAbstractFileEngine::link(const QString &newName) +{ + Q_UNUSED(newName); + return false; +} + +/*! + Requests that the directory \a dirName be created. If + \a createParentDirectories is true, then any sub-directories in \a dirName + that don't exist must be created. If \a createParentDirectories is false then + any sub-directories in \a dirName must already exist for the function to + succeed. If the operation succeeds return true; otherwise return + false. + + This virtual function must be reimplemented by all subclasses. + + \sa setFileName(), rmdir(), isRelativePath() + */ +bool QAbstractFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const +{ + Q_UNUSED(dirName); + Q_UNUSED(createParentDirectories); + return false; +} + +/*! + Requests that the directory \a dirName is deleted from the file + system. When \a recurseParentDirectories is true, then any empty + parent-directories in \a dirName must also be deleted. If + \a recurseParentDirectories is false, only the \a dirName leaf-node + should be deleted. In most file systems a directory cannot be deleted + using this function if it is non-empty. If the operation succeeds + return true; otherwise return false. + + This virtual function must be reimplemented by all subclasses. + + \sa setFileName(), remove(), mkdir(), isRelativePath() + */ +bool QAbstractFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const +{ + Q_UNUSED(dirName); + Q_UNUSED(recurseParentDirectories); + return false; +} + +/*! + Requests that the file be set to size \a size. If \a size is larger + than the current file then it is filled with 0's, if smaller it is + simply truncated. If the operations succceeds return true; otherwise + return false; + + This virtual function must be reimplemented by all subclasses. + + \sa size() +*/ +bool QAbstractFileEngine::setSize(qint64 size) +{ + Q_UNUSED(size); + return false; +} + +/*! + Should return true if the underlying file system is case-sensitive; + otherwise return false. + + This virtual function must be reimplemented by all subclasses. + */ +bool QAbstractFileEngine::caseSensitive() const +{ + return false; +} + +/*! + Return true if the file referred to by this file engine has a + relative path; otherwise return false. + + This virtual function must be reimplemented by all subclasses. + + \sa setFileName() + */ +bool QAbstractFileEngine::isRelativePath() const +{ + return false; +} + +/*! + Requests that a list of all the files matching the \a filters + list based on the \a filterNames in the file engine's directory + are returned. + + Should return an empty list if the file engine refers to a file + rather than a directory, or if the directory is unreadable or does + not exist or if nothing matches the specifications. + + This virtual function must be reimplemented by all subclasses. + + \sa setFileName() + */ +QStringList QAbstractFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const +{ + QStringList ret; + QDirIterator it(fileName(), filterNames, filters); + while (it.hasNext()) { + it.next(); + ret << it.fileName(); + } + return ret; +} + +/*! + This function should return the set of OR'd flags that are true + for the file engine's file, and that are in the \a type's OR'd + members. + + In your reimplementation you can use the \a type argument as an + optimization hint and only return the OR'd set of members that are + true and that match those in \a type; in other words you can + ignore any members not mentioned in \a type, thus avoiding some + potentially expensive lookups or system calls. + + This virtual function must be reimplemented by all subclasses. + + \sa setFileName() +*/ +QAbstractFileEngine::FileFlags QAbstractFileEngine::fileFlags(FileFlags type) const +{ + Q_UNUSED(type); + return 0; +} + +/*! + Requests that the file's permissions be set to \a perms. The argument + perms will be set to the OR-ed together combination of + QAbstractFileEngine::FileInfo, with only the QAbstractFileEngine::PermsMask being + honored. If the operations succceeds return true; otherwise return + false; + + This virtual function must be reimplemented by all subclasses. + + \sa size() +*/ +bool QAbstractFileEngine::setPermissions(uint perms) +{ + Q_UNUSED(perms); + return false; +} + +/*! + Return the file engine's current file name in the format + specified by \a file. + + If you don't handle some \c FileName possibilities, return the + file name set in setFileName() when an unhandled format is + requested. + + This virtual function must be reimplemented by all subclasses. + + \sa setFileName(), FileName + */ +QString QAbstractFileEngine::fileName(FileName file) const +{ + Q_UNUSED(file); + return QString(); +} + +/*! + If \a owner is \c OwnerUser return the ID of the user who owns + the file. If \a owner is \c OwnerGroup return the ID of the group + that own the file. If you can't determine the owner return -2. + + This virtual function must be reimplemented by all subclasses. + + \sa owner(), setFileName(), FileOwner + */ +uint QAbstractFileEngine::ownerId(FileOwner owner) const +{ + Q_UNUSED(owner); + return 0; +} + +/*! + If \a owner is \c OwnerUser return the name of the user who owns + the file. If \a owner is \c OwnerGroup return the name of the group + that own the file. If you can't determine the owner return + QString(). + + This virtual function must be reimplemented by all subclasses. + + \sa ownerId(), setFileName(), FileOwner + */ +QString QAbstractFileEngine::owner(FileOwner owner) const +{ + Q_UNUSED(owner); + return QString(); +} + +/*! + If \a time is \c CreationTime, return when the file was created. + If \a time is \c ModificationTime, return when the file was most + recently modified. If \a time is \c AccessTime, return when the + file was most recently accessed (e.g. read or written). + If the time cannot be determined return QDateTime() (an invalid + date time). + + This virtual function must be reimplemented by all subclasses. + + \sa setFileName(), QDateTime, QDateTime::isValid(), FileTime + */ +QDateTime QAbstractFileEngine::fileTime(FileTime time) const +{ + Q_UNUSED(time); + return QDateTime(); +} + +/*! + Sets the file engine's file name to \a file. This file name is the + file that the rest of the virtual functions will operate on. + + This virtual function must be reimplemented by all subclasses. + + \sa rename() + */ +void QAbstractFileEngine::setFileName(const QString &file) +{ + Q_UNUSED(file); +} + +/*! + Returns the native file handle for this file engine. This handle must be + used with care; its value and type are platform specific, and using it + will most likely lead to non-portable code. +*/ +int QAbstractFileEngine::handle() const +{ + return -1; +} + +/*! + \since 4.3 + + Returns \c true if the current position is at the end of the file; otherwise, + returns \c false. + + This function bases its behavior on calling extension() with + AtEndExtension. If the engine does not support this extension, false is + returned. + + \sa extension(), supportsExtension(), QFile::atEnd() +*/ +bool QAbstractFileEngine::atEnd() const +{ + return const_cast(this)->extension(AtEndExtension); +} + +/*! + \since 4.4 + + Maps \a size bytes of the file into memory starting at \a offset. + Returns a pointer to the memory if successful; otherwise returns \c false + if, for example, an error occurs. + + This function bases its behavior on calling extension() with + MapExtensionOption. If the engine does not support this extension, 0 is + returned. + + \a flags is currently not used, but could be used in the future. + + \sa unmap(), supportsExtension() + */ + +uchar *QAbstractFileEngine::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags) +{ + MapExtensionOption option; + option.offset = offset; + option.size = size; + option.flags = flags; + MapExtensionReturn r; + if (!extension(MapExtension, &option, &r)) + return 0; + return r.address; +} + +/*! + \since 4.4 + + Unmaps the memory \a address. Returns \c true if the unmap succeeds; otherwise + returns \c false. + + This function bases its behavior on calling extension() with + UnMapExtensionOption. If the engine does not support this extension, false is + returned. + + \sa map(), supportsExtension() + */ +bool QAbstractFileEngine::unmap(uchar *address) +{ + UnMapExtensionOption options; + options.address = address; + return extension(UnMapExtension, &options); +} + +/*! + \since 4.3 + \class QAbstractFileEngineIterator + \inmodule QtCore + \brief The QAbstractFileEngineIterator class provides an iterator + interface for custom file engines. + \internal + + If all you want is to iterate over entries in a directory, see + QDirIterator instead. This class is only for custom file engine authors. + + QAbstractFileEngineIterator is a unidirectional single-use virtual + iterator that plugs into QDirIterator, providing transparent proxy + iteration for custom file engines. + + You can subclass QAbstractFileEngineIterator to provide an iterator when + writing your own file engine. To plug the iterator into your file system, + you simply return an instance of this subclass from a reimplementation of + QAbstractFileEngine::beginEntryList(). + + Example: + + \snippet code/src_corelib_io_qabstractfileengine.cpp 2 + + QAbstractFileEngineIterator is associated with a path, name filters, and + entry filters. The path is the directory that the iterator lists entries + in. The name filters and entry filters are provided for file engines that + can optimize directory listing at the iterator level (e.g., network file + systems that need to minimize network traffic), but they can also be + ignored by the iterator subclass; QAbstractFileEngineIterator already + provides the required filtering logics in the matchesFilters() function. + You can call dirName() to get the directory name, nameFilters() to get a + stringlist of name filters, and filters() to get the entry filters. + + The pure virtual function hasNext() returns \c true if the current directory + has at least one more entry (i.e., the directory name is valid and + accessible, and we have not reached the end of the entry list), and false + otherwise. Reimplement next() to seek to the next entry. + + The pure virtual function currentFileName() returns the name of the + current entry without advancing the iterator. The currentFilePath() + function is provided for convenience; it returns the full path of the + current entry. + + Here is an example of how to implement an iterator that returns each of + three fixed entries in sequence. + + \snippet code/src_corelib_io_qabstractfileengine.cpp 3 + + Note: QAbstractFileEngineIterator does not deal with QDir::IteratorFlags; + it simply returns entries for a single directory. + + \sa QDirIterator +*/ + +/*! + \enum QAbstractFileEngineIterator::EntryInfoType + \internal + + This enum describes the different types of information that can be + requested through the QAbstractFileEngineIterator::entryInfo() function. +*/ + +/*! + \typedef QAbstractFileEngine::Iterator + \since 4.3 + \relates QAbstractFileEngine + + Synonym for QAbstractFileEngineIterator. +*/ + +class QAbstractFileEngineIteratorPrivate +{ +public: + QString path; + QDir::Filters filters; + QStringList nameFilters; + QFileInfo fileInfo; +}; + +/*! + Constructs a QAbstractFileEngineIterator, using the entry filters \a + filters, and wildcard name filters \a nameFilters. +*/ +QAbstractFileEngineIterator::QAbstractFileEngineIterator(QDir::Filters filters, + const QStringList &nameFilters) + : d(new QAbstractFileEngineIteratorPrivate) +{ + d->nameFilters = nameFilters; + d->filters = filters; +} + +/*! + Destroys the QAbstractFileEngineIterator. + + \sa QDirIterator +*/ +QAbstractFileEngineIterator::~QAbstractFileEngineIterator() +{ +} + +/*! + Returns the path for this iterator. QDirIterator is responsible for + assigning this path; it cannot change during the iterator's lifetime. + + \sa nameFilters(), filters() +*/ +QString QAbstractFileEngineIterator::path() const +{ + return d->path; +} + +/*! + \internal + + Sets the iterator path to \a path. This function is called from within + QDirIterator. +*/ +void QAbstractFileEngineIterator::setPath(const QString &path) +{ + d->path = path; +} + +/*! + Returns the name filters for this iterator. + + \sa QDir::nameFilters(), filters(), path() +*/ +QStringList QAbstractFileEngineIterator::nameFilters() const +{ + return d->nameFilters; +} + +/*! + Returns the entry filters for this iterator. + + \sa QDir::filter(), nameFilters(), path() +*/ +QDir::Filters QAbstractFileEngineIterator::filters() const +{ + return d->filters; +} + +/*! + \fn QString QAbstractFileEngineIterator::currentFileName() const = 0 + + This pure virtual function returns the name of the current directory + entry, excluding the path. + + \sa currentFilePath() +*/ + +/*! + Returns the path to the current directory entry. It's the same as + prepending path() to the return value of currentFileName(). + + \sa currentFileName() +*/ +QString QAbstractFileEngineIterator::currentFilePath() const +{ + QString name = currentFileName(); + if (!name.isNull()) { + QString tmp = path(); + if (!tmp.isEmpty()) { + if (!tmp.endsWith(QLatin1Char('/'))) + tmp.append(QLatin1Char('/')); + name.prepend(tmp); + } + } + return name; +} + +/*! + The virtual function returns a QFileInfo for the current directory + entry. This function is provided for convenience. It can also be slightly + faster than creating a QFileInfo object yourself, as the object returned + by this function might contain cached information that QFileInfo otherwise + would have to access through the file engine. + + \sa currentFileName() +*/ +QFileInfo QAbstractFileEngineIterator::currentFileInfo() const +{ + QString path = currentFilePath(); + if (d->fileInfo.filePath() != path) + d->fileInfo.setFile(path); + + // return a shallow copy + return d->fileInfo; +} + +/*! + \internal + + Returns the entry info \a type for this iterator's current directory entry + as a QVariant. If \a type is undefined for this entry, a null QVariant is + returned. + + \sa QAbstractFileEngine::beginEntryList(), QDir::beginEntryList() +*/ +QVariant QAbstractFileEngineIterator::entryInfo(EntryInfoType type) const +{ + Q_UNUSED(type) + return QVariant(); +} + +/*! + \fn virtual QString QAbstractFileEngineIterator::next() = 0 + + This pure virtual function advances the iterator to the next directory + entry, and returns the file path to the current entry. + + This function can optionally make use of nameFilters() and filters() to + optimize its performance. + + Reimplement this function in a subclass to advance the iterator. + + \sa QDirIterator::next() +*/ + +/*! + \fn virtual bool QAbstractFileEngineIterator::hasNext() const = 0 + + This pure virtual function returns \c true if there is at least one more + entry in the current directory (i.e., the iterator path is valid and + accessible, and the iterator has not reached the end of the entry list). + + \sa QDirIterator::hasNext() +*/ + +/*! + Returns an instance of a QAbstractFileEngineIterator using \a filters for + entry filtering and \a filterNames for name filtering. This function is + called by QDirIterator to initiate directory iteration. + + QDirIterator takes ownership of the returned instance, and deletes it when + it's done. + + \sa QDirIterator +*/ +QAbstractFileEngine::Iterator *QAbstractFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) +{ + Q_UNUSED(filters); + Q_UNUSED(filterNames); + return 0; +} + +/*! + \internal +*/ +QAbstractFileEngine::Iterator *QAbstractFileEngine::endEntryList() +{ + return 0; +} + +/*! + Reads a number of characters from the file into \a data. At most + \a maxlen characters will be read. + + Returns -1 if a fatal error occurs, or 0 if there are no bytes to + read. +*/ +qint64 QAbstractFileEngine::read(char *data, qint64 maxlen) +{ + Q_UNUSED(data); + Q_UNUSED(maxlen); + return -1; +} + +/*! + Writes \a len bytes from \a data to the file. Returns the number + of characters written on success; otherwise returns -1. +*/ +qint64 QAbstractFileEngine::write(const char *data, qint64 len) +{ + Q_UNUSED(data); + Q_UNUSED(len); + return -1; +} + +/*! + This function reads one line, terminated by a '\\n' character, from the + file info \a data. At most \a maxlen characters will be read. The + end-of-line character is included. +*/ +qint64 QAbstractFileEngine::readLine(char *data, qint64 maxlen) +{ + qint64 readSoFar = 0; + while (readSoFar < maxlen) { + char c; + qint64 readResult = read(&c, 1); + if (readResult <= 0) + return (readSoFar > 0) ? readSoFar : -1; + ++readSoFar; + *data++ = c; + if (c == '\n') + return readSoFar; + } + return readSoFar; +} + +/*! + \enum QAbstractFileEngine::Extension + \since 4.3 + + This enum describes the types of extensions that the file engine can + support. Before using these extensions, you must verify that the extension + is supported (i.e., call supportsExtension()). + + \value AtEndExtension Whether the current file position is at the end of + the file or not. This extension allows file engines that implement local + buffering to report end-of-file status without having to check the size of + the file. It is also useful for sequential files, where the size of the + file cannot be used to determine whether or not you have reached the end. + This extension returns \c true if the file is at the end; otherwise it returns + false. The input and output arguments to extension() are ignored. + + \value FastReadLineExtension Whether the file engine provides a + fast implementation for readLine() or not. If readLine() remains + unimplemented in the file engine, QAbstractFileEngine will provide + an implementation based on calling read() repeatedly. If + supportsExtension() returns \c false for this extension, however, + QIODevice can provide a faster implementation by making use of its + internal buffer. For engines that already provide a fast readLine() + implementation, returning false for this extension can avoid + unnnecessary double-buffering in QIODevice. + + \value MapExtension Whether the file engine provides the ability to map + a file to memory. + + \value UnMapExtension Whether the file engine provides the ability to + unmap memory that was previously mapped. +*/ + +/*! + \class QAbstractFileEngine::ExtensionOption + \inmodule QtCore + \since 4.3 + \brief provides an extended input argument to QAbstractFileEngine's + extension support. + + \sa QAbstractFileEngine::extension() +*/ + +/*! + \class QAbstractFileEngine::ExtensionReturn + \inmodule QtCore + \since 4.3 + \brief provides an extended output argument to QAbstractFileEngine's + extension support. + + \sa QAbstractFileEngine::extension() +*/ + +/*! + \since 4.3 + + This virtual function can be reimplemented in a QAbstractFileEngine + subclass to provide support for extensions. The \a option argument is + provided as input to the extension, and this function can store output + results in \a output. + + The behavior of this function is determined by \a extension; see the + Extension documentation for details. + + You can call supportsExtension() to check if an extension is supported by + the file engine. + + By default, no extensions are supported, and this function returns \c false. + + \sa supportsExtension(), Extension +*/ +bool QAbstractFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) +{ + Q_UNUSED(extension); + Q_UNUSED(option); + Q_UNUSED(output); + return false; +} + +/*! + \since 4.3 + + This virtual function returns \c true if the file engine supports \a + extension; otherwise, false is returned. By default, no extensions are + supported. + + \sa extension() +*/ +bool QAbstractFileEngine::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + Returns the QFile::FileError that resulted from the last failed + operation. If QFile::UnspecifiedError is returned, QFile will + use its own idea of the error status. + + \sa QFile::FileError, errorString() + */ +QFile::FileError QAbstractFileEngine::error() const +{ + Q_D(const QAbstractFileEngine); + return d->fileError; +} + +/*! + Returns the human-readable message appropriate to the current error + reported by error(). If no suitable string is available, an + empty string is returned. + + \sa error() + */ +QString QAbstractFileEngine::errorString() const +{ + Q_D(const QAbstractFileEngine); + return d->errorString; +} + +/*! + Sets the error type to \a error, and the error string to \a errorString. + Call this function to set the error values returned by the higher-level + classes. + + \sa QFile::error(), QIODevice::errorString(), QIODevice::setErrorString() +*/ +void QAbstractFileEngine::setError(QFile::FileError error, const QString &errorString) +{ + Q_D(QAbstractFileEngine); + d->fileError = error; + d->errorString = errorString; +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qabstractfileengine_p.h b/src/app/core/backport/qabstractfileengine_p.h new file mode 100644 index 000000000..d393a70d6 --- /dev/null +++ b/src/app/core/backport/qabstractfileengine_p.h @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTFILEENGINE_P_H +#define QABSTRACTFILEENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifdef open +#error qabstractfileengine_p.h must be included before any header file that defines open +#endif + +class QVariant; +class QAbstractFileEngineIterator; +class QAbstractFileEnginePrivate; + +class QAbstractFileEngine +{ +public: + enum FileFlag { + //perms (overlaps the QFile::Permission) + ReadOwnerPerm = 0x4000, WriteOwnerPerm = 0x2000, ExeOwnerPerm = 0x1000, + ReadUserPerm = 0x0400, WriteUserPerm = 0x0200, ExeUserPerm = 0x0100, + ReadGroupPerm = 0x0040, WriteGroupPerm = 0x0020, ExeGroupPerm = 0x0010, + ReadOtherPerm = 0x0004, WriteOtherPerm = 0x0002, ExeOtherPerm = 0x0001, + + //types + LinkType = 0x10000, + FileType = 0x20000, + DirectoryType = 0x40000, + BundleType = 0x80000, + + //flags + HiddenFlag = 0x0100000, + LocalDiskFlag = 0x0200000, + ExistsFlag = 0x0400000, + RootFlag = 0x0800000, + Refresh = 0x1000000, + + //masks + PermsMask = 0x0000FFFF, + TypesMask = 0x000F0000, + FlagsMask = 0x0FF00000, + FileInfoAll = FlagsMask | PermsMask | TypesMask + }; + Q_DECLARE_FLAGS(FileFlags, FileFlag) + + enum FileName { + DefaultName, + BaseName, + PathName, + AbsoluteName, + AbsolutePathName, + LinkName, + CanonicalName, + CanonicalPathName, + BundleName, + NFileNames = 9 + }; + enum FileOwner { + OwnerUser, + OwnerGroup + }; + enum FileTime { + CreationTime, + ModificationTime, + AccessTime + }; + + virtual ~QAbstractFileEngine(); + + virtual bool open(QIODevice::OpenMode openMode); + virtual bool close(); + virtual bool flush(); + virtual bool syncToDisk(); + virtual qint64 size() const; + virtual qint64 pos() const; + virtual bool seek(qint64 pos); + virtual bool isSequential() const; + virtual bool remove(); + virtual bool copy(const QString &newName); + virtual bool rename(const QString &newName); + virtual bool renameOverwrite(const QString &newName); + virtual bool link(const QString &newName); + virtual bool mkdir(const QString &dirName, bool createParentDirectories) const; + virtual bool rmdir(const QString &dirName, bool recurseParentDirectories) const; + virtual bool setSize(qint64 size); + virtual bool caseSensitive() const; + virtual bool isRelativePath() const; + virtual QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const; + virtual FileFlags fileFlags(FileFlags type=FileInfoAll) const; + virtual bool setPermissions(uint perms); + virtual QString fileName(FileName file=DefaultName) const; + virtual uint ownerId(FileOwner) const; + virtual QString owner(FileOwner) const; + virtual QDateTime fileTime(FileTime time) const; + virtual void setFileName(const QString &file); + virtual int handle() const; + bool atEnd() const; + uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags); + bool unmap(uchar *ptr); + + typedef QAbstractFileEngineIterator Iterator; + virtual Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames); + virtual Iterator *endEntryList(); + + virtual qint64 read(char *data, qint64 maxlen); + virtual qint64 readLine(char *data, qint64 maxlen); + virtual qint64 write(const char *data, qint64 len); + + QFile::FileError error() const; + QString errorString() const; + + enum Extension { + AtEndExtension, + FastReadLineExtension, + MapExtension, + UnMapExtension + }; + class ExtensionOption + {}; + class ExtensionReturn + {}; + + class MapExtensionOption : public ExtensionOption { + public: + qint64 offset; + qint64 size; + QFile::MemoryMapFlags flags; + }; + class MapExtensionReturn : public ExtensionReturn { + public: + uchar *address; + }; + + class UnMapExtensionOption : public ExtensionOption { + public: + uchar *address; + }; + + virtual bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0); + virtual bool supportsExtension(Extension extension) const; + + // Factory + static QAbstractFileEngine *create(const QString &fileName); + +protected: + void setError(QFile::FileError error, const QString &str); + + QAbstractFileEngine(); + QAbstractFileEngine(QAbstractFileEnginePrivate &); + + QScopedPointer d_ptr; +private: + Q_DECLARE_PRIVATE(QAbstractFileEngine) + Q_DISABLE_COPY(QAbstractFileEngine) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractFileEngine::FileFlags) + +class QAbstractFileEngineHandler +{ +public: + QAbstractFileEngineHandler(); + virtual ~QAbstractFileEngineHandler(); + virtual QAbstractFileEngine *create(const QString &fileName) const = 0; +}; + +class QAbstractFileEngineIteratorPrivate; +class QAbstractFileEngineIterator +{ +public: + QAbstractFileEngineIterator(QDir::Filters filters, const QStringList &nameFilters); + virtual ~QAbstractFileEngineIterator(); + + virtual QString next() = 0; + virtual bool hasNext() const = 0; + + QString path() const; + QStringList nameFilters() const; + QDir::Filters filters() const; + + virtual QString currentFileName() const = 0; + virtual QFileInfo currentFileInfo() const; + QString currentFilePath() const; + +protected: + enum EntryInfoType { + }; + virtual QVariant entryInfo(EntryInfoType type) const; + +private: + Q_DISABLE_COPY(QAbstractFileEngineIterator) + friend class QDirIterator; + friend class QDirIteratorPrivate; + void setPath(const QString &path); + QScopedPointer d; +}; + +class QAbstractFileEnginePrivate +{ +public: + inline QAbstractFileEnginePrivate() + : fileError(QFile::UnspecifiedError) + { + } + inline virtual ~QAbstractFileEnginePrivate() { } + + QFile::FileError fileError; + QString errorString; + + QAbstractFileEngine *q_ptr; + Q_DECLARE_PUBLIC(QAbstractFileEngine) +}; + +QAbstractFileEngine *qt_custom_file_engine_handler_create(const QString &path); + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QABSTRACTFILEENGINE_P_H diff --git a/src/app/core/qcommandlineoption.cpp b/src/app/core/backport/qcommandlineoption.cpp similarity index 100% rename from src/app/core/qcommandlineoption.cpp rename to src/app/core/backport/qcommandlineoption.cpp diff --git a/src/app/core/qcommandlineoption.h b/src/app/core/backport/qcommandlineoption.h similarity index 100% rename from src/app/core/qcommandlineoption.h rename to src/app/core/backport/qcommandlineoption.h diff --git a/src/app/core/qcommandlineparser.cpp b/src/app/core/backport/qcommandlineparser.cpp similarity index 100% rename from src/app/core/qcommandlineparser.cpp rename to src/app/core/backport/qcommandlineparser.cpp diff --git a/src/app/core/qcommandlineparser.h b/src/app/core/backport/qcommandlineparser.h similarity index 100% rename from src/app/core/qcommandlineparser.h rename to src/app/core/backport/qcommandlineparser.h diff --git a/src/app/core/backport/qcore_mac.cpp b/src/app/core/backport/qcore_mac.cpp new file mode 100644 index 000000000..52f86500a --- /dev/null +++ b/src/app/core/backport/qcore_mac.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcore_mac_p.h" +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +QString QCFString::toQString(CFStringRef str) +{ + if(!str) + return QString(); + CFIndex length = CFStringGetLength(str); + const UniChar *chars = CFStringGetCharactersPtr(str); + if (chars) + return QString(reinterpret_cast(chars), length); + + QVarLengthArray buffer(length); + CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data()); + return QString(reinterpret_cast(buffer.constData()), length); +} + +QCFString::operator QString() const +{ + if (string.isEmpty() && type) + const_cast(this)->string = toQString(type); + return string; +} + +CFStringRef QCFString::toCFStringRef(const QString &string) +{ + return CFStringCreateWithCharacters(0, reinterpret_cast(string.unicode()), + string.length()); +} + +QCFString::operator CFStringRef() const +{ + if (!type) + const_cast(this)->type = toCFStringRef(string); + return type; +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qcore_mac_p.h b/src/app/core/backport/qcore_mac_p.h new file mode 100644 index 000000000..b5a43124c --- /dev/null +++ b/src/app/core/backport/qcore_mac_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCORE_MAC_P_H +#define QCORE_MAC_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef __IMAGECAPTURE__ +# define __IMAGECAPTURE__ +#endif + +#if defined(QT_BOOTSTRAPPED) +#include +#else +#include +#endif + +#include + +#ifdef Q_OS_MACX +#include +#endif + +#ifdef __OBJC__ +#include +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#include + +#if defined( __OBJC__) && defined(QT_NAMESPACE) +#define QT_NAMESPACE_ALIAS_OBJC_CLASS(__KLASS__) @compatibility_alias __KLASS__ QT_MANGLE_NAMESPACE(__KLASS__) +#else +#define QT_NAMESPACE_ALIAS_OBJC_CLASS(__KLASS__) +#endif + +/* + Helper class that automates refernce counting for CFtypes. + After constructing the QCFType object, it can be copied like a + value-based type. + + Note that you must own the object you are wrapping. + This is typically the case if you get the object from a Core + Foundation function with the word "Create" or "Copy" in it. If + you got the object from a "Get" function, either retain it or use + constructFromGet(). One exception to this rule is the + HIThemeGet*Shape functions, which in reality are "Copy" functions. +*/ +template +class QCFType +{ +public: + inline QCFType(const T &t = 0) : type(t) {} + inline QCFType(const QCFType &helper) : type(helper.type) { if (type) CFRetain(type); } + inline ~QCFType() { if (type) CFRelease(type); } + inline operator T() { return type; } + inline QCFType operator =(const QCFType &helper) + { + if (helper.type) + CFRetain(helper.type); + CFTypeRef type2 = type; + type = helper.type; + if (type2) + CFRelease(type2); + return *this; + } + inline T *operator&() { return &type; } + template X as() const { return reinterpret_cast(type); } + static QCFType constructFromGet(const T &t) + { + CFRetain(t); + return QCFType(t); + } +protected: + T type; +}; + +class Q_CORE_EXPORT QCFString : public QCFType +{ +public: + inline QCFString(const QString &str) : QCFType(0), string(str) {} + inline QCFString(const CFStringRef cfstr = 0) : QCFType(cfstr) {} + inline QCFString(const QCFType &other) : QCFType(other) {} + operator QString() const; + operator CFStringRef() const; + static QString toQString(CFStringRef cfstr); + static CFStringRef toCFStringRef(const QString &str); +#ifdef __OBJC__ + static QString toQString(const NSString *nsstr); + static NSString *toNSString(const QString &string); +#endif + +private: + QString string; +}; + +#ifdef Q_OS_IOS +QSysInfo::MacVersion qt_ios_version(); +#endif + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QCORE_MAC_P_H diff --git a/src/app/core/backport/qcore_unix.cpp b/src/app/core/backport/qcore_unix.cpp new file mode 100644 index 000000000..2fed9b277 --- /dev/null +++ b/src/app/core/backport/qcore_unix.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcore_unix_p.h" +#include "qelapsedtimer.h" + +#ifdef Q_OS_NACL +#elif !defined (Q_OS_VXWORKS) +# if !defined(Q_OS_HPUX) || defined(__ia64) +# include +# endif +# include +#else +# include +#endif + +#include + +#ifdef Q_OS_MAC +#include +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +static inline bool time_update(struct timespec *tv, const struct timespec &start, + const struct timespec &timeout) +{ + // clock source is (hopefully) monotonic, so we can recalculate how much timeout is left; + // if it isn't monotonic, we'll simply hope that it hasn't jumped, because we have no alternative + struct timespec now = qt_gettime(); + *tv = timeout + start - now; + return tv->tv_sec >= 0; +} + +int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, + const struct timespec *orig_timeout) +{ + if (!orig_timeout) { + // no timeout -> block forever + int ret; + EINTR_LOOP(ret, select(nfds, fdread, fdwrite, fdexcept, 0)); + return ret; + } + + timespec start = qt_gettime(); + timespec timeout = *orig_timeout; + + // loop and recalculate the timeout as needed + int ret; + forever { +#ifndef Q_OS_QNX + ret = ::pselect(nfds, fdread, fdwrite, fdexcept, &timeout, 0); +#else + timeval timeoutVal; + timeoutVal.tv_sec = timeout.tv_sec; + timeoutVal.tv_usec = timeout.tv_nsec / 1000; + ret = ::select(nfds, fdread, fdwrite, fdexcept, &timeoutVal); +#endif + if (ret != -1 || errno != EINTR) + return ret; + + // recalculate the timeout + if (!time_update(&timeout, start, *orig_timeout)) { + // timeout during update + // or clock reset, fake timeout error + return 0; + } + } +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qcore_unix_p.h b/src/app/core/backport/qcore_unix_p.h new file mode 100644 index 000000000..9b1a2721a --- /dev/null +++ b/src/app/core/backport/qcore_unix_p.h @@ -0,0 +1,335 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCORE_UNIX_P_H +#define QCORE_UNIX_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt code on Unix. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qplatformdefs.h" +#include "qatomic.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifndef Q_OS_UNIX +# error "qcore_unix_p.h included on a non-Unix system" +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#if defined(Q_OS_VXWORKS) +# include +#endif + +struct sockaddr; + +#if defined(Q_OS_LINUX) && defined(O_CLOEXEC) +# define QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC 1 + +namespace QtLibcSupplement { + inline int accept4(int, sockaddr *, QT_SOCKLEN_T *, int) + { errno = ENOSYS; return -1; } + inline int dup3(int, int, int) + { errno = ENOSYS; return -1; } + inline int pipe2(int [], int ) + { errno = ENOSYS; return -1; } +} + +using namespace QT_PREPEND_NAMESPACE(QtLibcSupplement); + +#else +# define QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC 0 +#endif + +#define EINTR_LOOP(var, cmd) \ + do { \ + var = cmd; \ + } while (var == -1 && errno == EINTR) + + + +// Internal operator functions for timespecs +inline timespec &normalizedTimespec(timespec &t) +{ + while (t.tv_nsec >= 1000000000) { + ++t.tv_sec; + t.tv_nsec -= 1000000000; + } + while (t.tv_nsec < 0) { + --t.tv_sec; + t.tv_nsec += 1000000000; + } + return t; +} +inline bool operator<(const timespec &t1, const timespec &t2) +{ return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec); } +inline bool operator==(const timespec &t1, const timespec &t2) +{ return t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec; } +inline timespec &operator+=(timespec &t1, const timespec &t2) +{ + t1.tv_sec += t2.tv_sec; + t1.tv_nsec += t2.tv_nsec; + return normalizedTimespec(t1); +} +inline timespec operator+(const timespec &t1, const timespec &t2) +{ + timespec tmp; + tmp.tv_sec = t1.tv_sec + t2.tv_sec; + tmp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + return normalizedTimespec(tmp); +} +inline timespec operator-(const timespec &t1, const timespec &t2) +{ + timespec tmp; + tmp.tv_sec = t1.tv_sec - (t2.tv_sec - 1); + tmp.tv_nsec = t1.tv_nsec - (t2.tv_nsec + 1000000000); + return normalizedTimespec(tmp); +} +inline timespec operator*(const timespec &t1, int mul) +{ + timespec tmp; + tmp.tv_sec = t1.tv_sec * mul; + tmp.tv_nsec = t1.tv_nsec * mul; + return normalizedTimespec(tmp); +} + +inline void qt_ignore_sigpipe() +{ + // Set to ignore SIGPIPE once only. + static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0); + if (!atom.load()) { + // More than one thread could turn off SIGPIPE at the same time + // But that's acceptable because they all would be doing the same + // action + struct sigaction noaction; + memset(&noaction, 0, sizeof(noaction)); + noaction.sa_handler = SIG_IGN; + ::sigaction(SIGPIPE, &noaction, 0); + atom.store(1); + } +} + +// don't call QT_OPEN or ::open +// call qt_safe_open +static inline int qt_safe_open(const char *pathname, int flags, mode_t mode = 0777) +{ +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; +#endif + int fd; + EINTR_LOOP(fd, QT_OPEN(pathname, flags, mode)); + + // unknown flags are ignored, so we have no way of verifying if + // O_CLOEXEC was accepted + if (fd != -1) + ::fcntl(fd, F_SETFD, FD_CLOEXEC); + return fd; +} +#undef QT_OPEN +#define QT_OPEN qt_safe_open + +#ifndef Q_OS_VXWORKS // no POSIX pipes in VxWorks +// don't call ::pipe +// call qt_safe_pipe +static inline int qt_safe_pipe(int pipefd[2], int flags = 0) +{ +#ifdef O_CLOEXEC + Q_ASSERT((flags & ~(O_CLOEXEC | O_NONBLOCK)) == 0); +#else + Q_ASSERT((flags & ~O_NONBLOCK) == 0); +#endif + + int ret; +#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(O_CLOEXEC) + // use pipe2 + flags |= O_CLOEXEC; + ret = ::pipe2(pipefd, flags); // pipe2 is Linux-specific and is documented not to return EINTR + if (ret == 0 || errno != ENOSYS) + return ret; +#endif + + ret = ::pipe(pipefd); + if (ret == -1) + return -1; + + ::fcntl(pipefd[0], F_SETFD, FD_CLOEXEC); + ::fcntl(pipefd[1], F_SETFD, FD_CLOEXEC); + + // set non-block too? + if (flags & O_NONBLOCK) { + ::fcntl(pipefd[0], F_SETFL, ::fcntl(pipefd[0], F_GETFL) | O_NONBLOCK); + ::fcntl(pipefd[1], F_SETFL, ::fcntl(pipefd[1], F_GETFL) | O_NONBLOCK); + } + + return 0; +} + +#endif // Q_OS_VXWORKS + +// don't call dup or fcntl(F_DUPFD) +static inline int qt_safe_dup(int oldfd, int atleast = 0, int flags = FD_CLOEXEC) +{ + Q_ASSERT(flags == FD_CLOEXEC || flags == 0); + + int ret; +#ifdef F_DUPFD_CLOEXEC + // use this fcntl + if (flags & FD_CLOEXEC) { + ret = ::fcntl(oldfd, F_DUPFD_CLOEXEC, atleast); + if (ret != -1 || errno != EINVAL) + return ret; + } +#endif + + // use F_DUPFD + ret = ::fcntl(oldfd, F_DUPFD, atleast); + + if (flags && ret != -1) + ::fcntl(ret, F_SETFD, flags); + return ret; +} + +// don't call dup2 +// call qt_safe_dup2 +static inline int qt_safe_dup2(int oldfd, int newfd, int flags = FD_CLOEXEC) +{ + Q_ASSERT(flags == FD_CLOEXEC || flags == 0); + + int ret; +#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(O_CLOEXEC) + // use dup3 + if (flags & FD_CLOEXEC) { + EINTR_LOOP(ret, ::dup3(oldfd, newfd, O_CLOEXEC)); + if (ret == 0 || errno != ENOSYS) + return ret; + } +#endif + EINTR_LOOP(ret, ::dup2(oldfd, newfd)); + if (ret == -1) + return -1; + + if (flags) + ::fcntl(newfd, F_SETFD, flags); + return 0; +} + +static inline qint64 qt_safe_read(int fd, void *data, qint64 maxlen) +{ + qint64 ret = 0; + EINTR_LOOP(ret, QT_READ(fd, data, maxlen)); + return ret; +} +#undef QT_READ +#define QT_READ qt_safe_read + +static inline qint64 qt_safe_write(int fd, const void *data, qint64 len) +{ + qint64 ret = 0; + EINTR_LOOP(ret, QT_WRITE(fd, data, len)); + return ret; +} +#undef QT_WRITE +#define QT_WRITE qt_safe_write + +static inline qint64 qt_safe_write_nosignal(int fd, const void *data, qint64 len) +{ + qt_ignore_sigpipe(); + return qt_safe_write(fd, data, len); +} + +static inline int qt_safe_close(int fd) +{ + int ret; + EINTR_LOOP(ret, QT_CLOSE(fd)); + return ret; +} +#undef QT_CLOSE +#define QT_CLOSE qt_safe_close + +// - VxWorks doesn't have processes +#if !defined(Q_OS_VXWORKS) +static inline int qt_safe_execve(const char *filename, char *const argv[], + char *const envp[]) +{ + int ret; + EINTR_LOOP(ret, ::execve(filename, argv, envp)); + return ret; +} + +static inline int qt_safe_execv(const char *path, char *const argv[]) +{ + int ret; + EINTR_LOOP(ret, ::execv(path, argv)); + return ret; +} + +static inline int qt_safe_execvp(const char *file, char *const argv[]) +{ + int ret; + EINTR_LOOP(ret, ::execvp(file, argv)); + return ret; +} + +static inline pid_t qt_safe_waitpid(pid_t pid, int *status, int options) +{ + int ret; + EINTR_LOOP(ret, ::waitpid(pid, status, options)); + return ret; +} +#endif // Q_OS_VXWORKS + +#if !defined(_POSIX_MONOTONIC_CLOCK) +# define _POSIX_MONOTONIC_CLOCK -1 +#endif + +// in qelapsedtimer_mac.cpp or qtimestamp_unix.cpp +timespec qt_gettime() Q_DECL_NOTHROW; +void qt_nanosleep(timespec amount); + +int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, const struct timespec *tv); + +// according to X/OPEN we have to define semun ourselves +// we use prefix as on some systems sem.h will have it +struct semid_ds; +union qt_semun { + int val; /* value for SETVAL */ + struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ + unsigned short *array; /* array for GETALL, SETALL */ +}; + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif diff --git a/src/app/core/backport/qfileinfo_p.h b/src/app/core/backport/qfileinfo_p.h new file mode 100644 index 000000000..8339c102a --- /dev/null +++ b/src/app/core/backport/qfileinfo_p.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILEINFO_P_H +#define QFILEINFO_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include "qatomic.h" +#include +#include "qfilesystemengine_p.h" +#include + +#include "qabstractfileengine_p.h" +#include "qfilesystementry_p.h" +#include "qfilesystemmetadata_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +class QFileInfoPrivate : public QSharedData +{ +public: + enum { CachedFileFlags=0x01, CachedLinkTypeFlag=0x02, CachedBundleTypeFlag=0x04, + CachedMTime=0x10, CachedCTime=0x20, CachedATime=0x40, + CachedSize =0x08, CachedPerms=0x80 }; + + inline QFileInfoPrivate() + : QSharedData(), fileEngine(0), + cachedFlags(0), + isDefaultConstructed(true), + cache_enabled(true), fileFlags(0), fileSize(0) + {} + inline QFileInfoPrivate(const QFileInfoPrivate ©) + : QSharedData(copy), + fileEntry(copy.fileEntry), + metaData(copy.metaData), + fileEngine(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(fileEntry, metaData)), + cachedFlags(0), +#ifndef QT_NO_FSFILEENGINE + isDefaultConstructed(false), +#else + isDefaultConstructed(!fileEngine), +#endif + cache_enabled(copy.cache_enabled), fileFlags(0), fileSize(0) + {} + inline QFileInfoPrivate(const QString &file) + : fileEntry(QDir::fromNativeSeparators(file)), + fileEngine(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(fileEntry, metaData)), + cachedFlags(0), +#ifndef QT_NO_FSFILEENGINE + isDefaultConstructed(false), +#else + isDefaultConstructed(!fileEngine), +#endif + cache_enabled(true), fileFlags(0), fileSize(0) + { + } + + inline QFileInfoPrivate(const QFileSystemEntry &file, const QFileSystemMetaData &data) + : QSharedData(), + fileEntry(file), + metaData(data), + fileEngine(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(fileEntry, metaData)), + cachedFlags(0), + isDefaultConstructed(false), + cache_enabled(true), fileFlags(0), fileSize(0) + { + //If the file engine is not null, this maybe a "mount point" for a custom file engine + //in which case we can't trust the metadata + if (fileEngine) + metaData = QFileSystemMetaData(); + } + + inline QFileInfoPrivate(const QFileSystemEntry &file, const QFileSystemMetaData &data, QAbstractFileEngine *engine) + : fileEntry(file), + metaData(data), + fileEngine(engine), + cachedFlags(0), +#ifndef QT_NO_FSFILEENGINE + isDefaultConstructed(false), +#else + isDefaultConstructed(!fileEngine), +#endif + cache_enabled(true), fileFlags(0), fileSize(0) + { + } + + inline void clearFlags() const { + fileFlags = 0; + cachedFlags = 0; + if (fileEngine) + (void)fileEngine->fileFlags(QAbstractFileEngine::Refresh); + } + inline void clear() { + metaData.clear(); + clearFlags(); + for (int i = QAbstractFileEngine::NFileNames - 1 ; i >= 0 ; --i) + fileNames[i].clear(); + fileOwners[1].clear(); + fileOwners[0].clear(); + } + + uint getFileFlags(QAbstractFileEngine::FileFlags) const; + QDateTime &getFileTime(QAbstractFileEngine::FileTime) const; + QString getFileName(QAbstractFileEngine::FileName) const; + QString getFileOwner(QAbstractFileEngine::FileOwner own) const; + + QFileSystemEntry fileEntry; + mutable QFileSystemMetaData metaData; + + QScopedPointer const fileEngine; + + mutable QString fileNames[QAbstractFileEngine::NFileNames]; + mutable QString fileOwners[2]; + + mutable uint cachedFlags : 30; + bool const isDefaultConstructed : 1; // QFileInfo is a default constructed instance + bool cache_enabled : 1; + mutable uint fileFlags; + mutable qint64 fileSize; + // ### Qt6: FIXME: This vector is essentially a plain array + // mutable QDateTime fileTimes[3], but the array is slower + // to initialize than the QVector as QDateTime has a pimpl. + // In Qt 6, QDateTime should inline its data members, + // and this here can be an array again. + mutable QVector fileTimes; + inline bool getCachedFlag(uint c) const + { return cache_enabled ? (cachedFlags & c) : 0; } + inline void setCachedFlag(uint c) const + { if (cache_enabled) cachedFlags |= c; } + +}; + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QFILEINFO_P_H diff --git a/src/app/core/backport/qfilesystemengine.cpp b/src/app/core/backport/qfilesystemengine.cpp new file mode 100644 index 000000000..f99668239 --- /dev/null +++ b/src/app/core/backport/qfilesystemengine.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilesystemengine_p.h" +#include +#include +#include +#include "qabstractfileengine_p.h" +#ifdef QT_BUILD_CORE_LIB +#include "qresource_p.h" +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +/*! + \internal + + Returns the canonicalized form of \a path (i.e., with all symlinks + resolved, and all redundant path elements removed. +*/ +QString QFileSystemEngine::slowCanonicalized(const QString &path) +{ + if (path.isEmpty()) + return path; + + QFileInfo fi; + const QChar slash(QLatin1Char('/')); + QString tmpPath = path; + int separatorPos = 0; + QSet nonSymlinks; + QSet known; + + known.insert(path); + do { +#ifdef Q_OS_WIN + if (separatorPos == 0) { + if (tmpPath.size() >= 2 && tmpPath.at(0) == slash && tmpPath.at(1) == slash) { + // UNC, skip past the first two elements + separatorPos = tmpPath.indexOf(slash, 2); + } else if (tmpPath.size() >= 3 && tmpPath.at(1) == QLatin1Char(':') && tmpPath.at(2) == slash) { + // volume root, skip since it can not be a symlink + separatorPos = 2; + } + } + if (separatorPos != -1) +#endif + separatorPos = tmpPath.indexOf(slash, separatorPos + 1); + QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos); + if (!nonSymlinks.contains(prefix)) { + fi.setFile(prefix); + if (fi.isSymLink()) { + QString target = fi.symLinkTarget(); + if(QFileInfo(target).isRelative()) + target = fi.absolutePath() + slash + target; + if (separatorPos != -1) { + if (fi.isDir() && !target.endsWith(slash)) + target.append(slash); + target.append(tmpPath.mid(separatorPos)); + } + tmpPath = QDir::cleanPath(target); + separatorPos = 0; + + if (known.contains(tmpPath)) + return QString(); + known.insert(tmpPath); + } else { + nonSymlinks.insert(prefix); + } + } + } while (separatorPos != -1); + + return QDir::cleanPath(tmpPath); +} + +static inline bool _q_checkEntry(QFileSystemEntry &entry, QFileSystemMetaData &data, bool resolvingEntry) +{ + if (resolvingEntry) { + if (!QFileSystemEngine::fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute) + || !data.exists()) { + data.clear(); + return false; + } + } + + return true; +} + +static inline bool _q_checkEntry(QAbstractFileEngine *&engine, bool resolvingEntry) +{ + if (resolvingEntry) { + if (!(engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::ExistsFlag)) { + delete engine; + engine = 0; + return false; + } + } + + return true; +} + +static bool _q_resolveEntryAndCreateLegacyEngine_recursive(QFileSystemEntry &entry, QFileSystemMetaData &data, + QAbstractFileEngine *&engine, bool resolvingEntry = false) +{ + QString const &filePath = entry.filePath(); + if ((engine = qt_custom_file_engine_handler_create(filePath))) + return _q_checkEntry(engine, resolvingEntry); + +#if defined(QT_BUILD_CORE_LIB) + for (int prefixSeparator = 0; prefixSeparator < filePath.size(); ++prefixSeparator) { + QChar const ch = filePath[prefixSeparator]; + if (ch == QLatin1Char('/')) + break; + + if (ch == QLatin1Char(':')) { + if (prefixSeparator == 0) { + engine = new QResourceFileEngine(filePath); + return _q_checkEntry(engine, resolvingEntry); + } + + if (prefixSeparator == 1) + break; + + const QStringList &paths = QDir::searchPaths(filePath.left(prefixSeparator)); + for (int i = 0; i < paths.count(); i++) { + entry = QFileSystemEntry(QDir::cleanPath(paths.at(i) % QLatin1Char('/') % filePath.mid(prefixSeparator + 1))); + // Recurse! + if (_q_resolveEntryAndCreateLegacyEngine_recursive(entry, data, engine, true)) + return true; + } + + // entry may have been clobbered at this point. + return false; + } + + // There's no need to fully validate the prefix here. Consulting the + // unicode tables could be expensive and validation is already + // performed in QDir::setSearchPaths. + // + // if (!ch.isLetterOrNumber()) + // break; + } +#endif // defined(QT_BUILD_CORE_LIB) + + return _q_checkEntry(entry, data, resolvingEntry); +} + +/*! + \internal + + Resolves the \a entry (see QDir::searchPaths) and returns an engine for + it, but never a QFSFileEngine. + + Returns a file engine that can be used to access the entry. Returns 0 if + QFileSystemEngine API should be used to query and interact with the file + system object. +*/ +QAbstractFileEngine *QFileSystemEngine::resolveEntryAndCreateLegacyEngine( + QFileSystemEntry &entry, QFileSystemMetaData &data) { + QFileSystemEntry copy = entry; + QAbstractFileEngine *engine = 0; + + if (_q_resolveEntryAndCreateLegacyEngine_recursive(copy, data, engine)) + // Reset entry to resolved copy. + entry = copy; + else + data.clear(); + + return engine; +} + +//these unix functions are in this file, because they are shared by symbian port +//for open C file handles. +#ifdef Q_OS_UNIX +//static +bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data) +{ + data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags; + data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags; + + QT_STATBUF statBuffer; + if (QT_FSTAT(fd, &statBuffer) == 0) { + data.fillFromStatBuf(statBuffer); + return true; + } + + return false; +} + +#if defined(Q_OS_QNX) +static void fillStat64fromStat32(struct stat64 *statBuf64, const struct stat &statBuf32) +{ + statBuf64->st_mode = statBuf32.st_mode; + statBuf64->st_size = statBuf32.st_size; + statBuf64->st_ctime = statBuf32.st_ctime; + statBuf64->st_mtime = statBuf32.st_mtime; + statBuf64->st_atime = statBuf32.st_atime; + statBuf64->st_uid = statBuf32.st_uid; + statBuf64->st_gid = statBuf32.st_gid; +} +#endif + +void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer) +{ + // Permissions + if (statBuffer.st_mode & S_IRUSR) + entryFlags |= QFileSystemMetaData::OwnerReadPermission; + if (statBuffer.st_mode & S_IWUSR) + entryFlags |= QFileSystemMetaData::OwnerWritePermission; + if (statBuffer.st_mode & S_IXUSR) + entryFlags |= QFileSystemMetaData::OwnerExecutePermission; + + if (statBuffer.st_mode & S_IRGRP) + entryFlags |= QFileSystemMetaData::GroupReadPermission; + if (statBuffer.st_mode & S_IWGRP) + entryFlags |= QFileSystemMetaData::GroupWritePermission; + if (statBuffer.st_mode & S_IXGRP) + entryFlags |= QFileSystemMetaData::GroupExecutePermission; + + if (statBuffer.st_mode & S_IROTH) + entryFlags |= QFileSystemMetaData::OtherReadPermission; + if (statBuffer.st_mode & S_IWOTH) + entryFlags |= QFileSystemMetaData::OtherWritePermission; + if (statBuffer.st_mode & S_IXOTH) + entryFlags |= QFileSystemMetaData::OtherExecutePermission; + + // Type + if ((statBuffer.st_mode & S_IFMT) == S_IFREG) + entryFlags |= QFileSystemMetaData::FileType; + else if ((statBuffer.st_mode & S_IFMT) == S_IFDIR) + entryFlags |= QFileSystemMetaData::DirectoryType; + else + entryFlags |= QFileSystemMetaData::SequentialType; + + // Attributes + entryFlags |= QFileSystemMetaData::ExistsAttribute; + size_ = statBuffer.st_size; +#if defined(Q_OS_MACX) + if (statBuffer.st_flags & UF_HIDDEN) { + entryFlags |= QFileSystemMetaData::HiddenAttribute; + knownFlagsMask |= QFileSystemMetaData::HiddenAttribute; + } +#endif + + // Times + creationTime_ = statBuffer.st_ctime ? statBuffer.st_ctime : statBuffer.st_mtime; + modificationTime_ = statBuffer.st_mtime; + accessTime_ = statBuffer.st_atime; + userId_ = statBuffer.st_uid; + groupId_ = statBuffer.st_gid; +} + +void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry) +{ +#if defined(Q_OS_QNX) + knownFlagsMask = 0; + entryFlags = 0; + for (dirent_extra *extra = _DEXTRA_FIRST(&entry); _DEXTRA_VALID(extra, &entry); + extra = _DEXTRA_NEXT(extra)) { + if (extra->d_type == _DTYPE_STAT || extra->d_type == _DTYPE_LSTAT) { + + const struct dirent_extra_stat * const extra_stat = + reinterpret_cast(extra); + + // Remember whether this was a link or not, this saves an lstat() call later. + if (extra->d_type == _DTYPE_LSTAT) { + knownFlagsMask |= QFileSystemMetaData::LinkType; + if (S_ISLNK(extra_stat->d_stat.st_mode)) + entryFlags |= QFileSystemMetaData::LinkType; + } + + // For symlinks, the extra type _DTYPE_LSTAT doesn't work for filling out the meta data, + // as we need the stat() information there, not the lstat() information. + // In this case, don't use the extra information. + // Unfortunately, readdir() never seems to return extra info of type _DTYPE_STAT, so for + // symlinks, we always incur the cost of an extra stat() call later. + if (S_ISLNK(extra_stat->d_stat.st_mode) && extra->d_type == _DTYPE_LSTAT) + continue; + +#if defined(QT_USE_XOPEN_LFS_EXTENSIONS) && defined(QT_LARGEFILE_SUPPORT) + // Even with large file support, d_stat is always of type struct stat, not struct stat64, + // so it needs to be converted + struct stat64 statBuf; + fillStat64fromStat32(&statBuf, extra_stat->d_stat); + fillFromStatBuf(statBuf); +#else + fillFromStatBuf(extra_stat->d_stat); +#endif + knownFlagsMask |= QFileSystemMetaData::PosixStatFlags; + if (!S_ISLNK(extra_stat->d_stat.st_mode)) { + knownFlagsMask |= QFileSystemMetaData::ExistsAttribute; + entryFlags |= QFileSystemMetaData::ExistsAttribute; + } + } + } +#elif defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4) + // BSD4 includes Mac OS X + + // ### This will clear all entry flags and knownFlagsMask + switch (entry.d_type) + { + case DT_DIR: + knownFlagsMask = QFileSystemMetaData::LinkType + | QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::SequentialType + | QFileSystemMetaData::ExistsAttribute; + + entryFlags = QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::ExistsAttribute; + + break; + + case DT_BLK: + case DT_CHR: + case DT_FIFO: + case DT_SOCK: + // ### System attribute + knownFlagsMask = QFileSystemMetaData::LinkType + | QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::BundleType + | QFileSystemMetaData::AliasType + | QFileSystemMetaData::SequentialType + | QFileSystemMetaData::ExistsAttribute; + + entryFlags = QFileSystemMetaData::SequentialType + | QFileSystemMetaData::ExistsAttribute; + + break; + + case DT_LNK: + knownFlagsMask = QFileSystemMetaData::LinkType; + entryFlags = QFileSystemMetaData::LinkType; + break; + + case DT_REG: + knownFlagsMask = QFileSystemMetaData::LinkType + | QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::BundleType + | QFileSystemMetaData::SequentialType + | QFileSystemMetaData::ExistsAttribute; + + entryFlags = QFileSystemMetaData::FileType + | QFileSystemMetaData::ExistsAttribute; + + break; + + case DT_UNKNOWN: + default: + clear(); + } +#else + Q_UNUSED(entry) +#endif +} + +#endif + +//static +QString QFileSystemEngine::resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData) +{ +#if defined(Q_OS_WIN) + Q_UNUSED(metaData); + return QFileSystemEngine::owner(entry, QAbstractFileEngine::OwnerUser); +#else //(Q_OS_UNIX) + if (!metaData.hasFlags(QFileSystemMetaData::UserId)) + QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::UserId); + return resolveUserName(metaData.userId()); +#endif +} + +//static +QString QFileSystemEngine::resolveGroupName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData) +{ +#if defined(Q_OS_WIN) + Q_UNUSED(metaData); + return QFileSystemEngine::owner(entry, QAbstractFileEngine::OwnerGroup); +#else //(Q_OS_UNIX) + if (!metaData.hasFlags(QFileSystemMetaData::GroupId)) + QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::GroupId); + return resolveGroupName(metaData.groupId()); +#endif +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qfilesystemengine_p.h b/src/app/core/backport/qfilesystemengine_p.h new file mode 100644 index 000000000..69a10d613 --- /dev/null +++ b/src/app/core/backport/qfilesystemengine_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMENGINE_P_H +#define QFILESYSTEMENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include "qfilesystementry_p.h" +#include "qfilesystemmetadata_p.h" +#include "qsystemerror_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +class QFileSystemEngine +{ +public: + static bool isCaseSensitive() + { +#ifndef Q_OS_WIN + return true; +#else + return false; +#endif + } + + static QFileSystemEntry getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data); + static QFileSystemEntry canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data); + static QFileSystemEntry absoluteName(const QFileSystemEntry &entry); + static QByteArray id(const QFileSystemEntry &entry); + static QString resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &data); + static QString resolveGroupName(const QFileSystemEntry &entry, QFileSystemMetaData &data); + +#if defined(Q_OS_UNIX) + static QString resolveUserName(uint userId); + static QString resolveGroupName(uint groupId); +#endif + +#if defined(Q_OS_MACX) + static QString bundleName(const QFileSystemEntry &entry); +#else + static QString bundleName(const QFileSystemEntry &entry) { Q_UNUSED(entry) return QString(); } +#endif + + static bool fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what); +#if defined(Q_OS_UNIX) + static bool fillMetaData(int fd, QFileSystemMetaData &data); // what = PosixStatFlags +#endif +#if defined(Q_OS_WIN) + + static bool uncListSharesOnServer(const QString &server, QStringList *list); //Used also by QFSFileEngineIterator::hasNext() + static bool fillMetaData(int fd, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what); + static bool fillMetaData(HANDLE fHandle, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what); + static bool fillPermissions(const QFileSystemEntry &entry, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what); + static QString owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own); + static QString nativeAbsoluteFilePath(const QString &path); +#endif + //homePath, rootPath and tempPath shall return clean paths + static QString homePath(); + static QString rootPath(); + static QString tempPath(); + + static bool createDirectory(const QFileSystemEntry &entry, bool createParents); + static bool removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents); + + static bool createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error); + + static bool copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error); + static bool renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error); + static bool removeFile(const QFileSystemEntry &entry, QSystemError &error); + + static bool setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, + QFileSystemMetaData *data = 0); + + static bool setCurrentPath(const QFileSystemEntry &entry); + static QFileSystemEntry currentPath(); + + static QAbstractFileEngine *resolveEntryAndCreateLegacyEngine(QFileSystemEntry &entry, + QFileSystemMetaData &data); +private: + static QString slowCanonicalized(const QString &path); +#if defined(Q_OS_WIN) + static void clearWinStatData(QFileSystemMetaData &data); +#endif +}; + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // include guard diff --git a/src/app/core/backport/qfilesystemengine_unix.cpp b/src/app/core/backport/qfilesystemengine_unix.cpp new file mode 100644 index 000000000..0fe357037 --- /dev/null +++ b/src/app/core/backport/qfilesystemengine_unix.cpp @@ -0,0 +1,756 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Samuel Gaist > +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qfilesystemengine_p.h" +#include + +#include + +#include // for realpath() +#include +#include +#include +#include +#include + +#if defined(Q_OS_MAC) +# include "qcore_mac_p.h" +# include +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#if defined(Q_OS_MACX) +static inline bool _q_isMacHidden(const char *nativePath) +{ + OSErr err; + + FSRef fsRef; + err = FSPathMakeRefWithOptions(reinterpret_cast(nativePath), + kFSPathMakeRefDoNotFollowLeafSymlink, &fsRef, 0); + if (err != noErr) + return false; + + FSCatalogInfo catInfo; + err = FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL); + if (err != noErr) + return false; + + FileInfo * const fileInfo = reinterpret_cast(&catInfo.finderInfo); + return (fileInfo->finderFlags & kIsInvisible); +} + +static bool isPackage(const QFileSystemMetaData &data, const QFileSystemEntry &entry) +{ + if (!data.isDirectory()) + return false; + + QFileInfo info(entry.filePath()); + QString suffix = info.suffix(); + + // First step: is the extenstion known ? + if (suffix == QLatin1String("app") + || suffix == QLatin1String("debug") + || suffix == QLatin1String("profile") + || suffix == QLatin1String("bundle") + || suffix == QLatin1String("pkg")) { + return true; + } + + // Second step: check if an application knows the package type + const QByteArray &native = entry.nativeFilePath(); + const char *nativeFilePath = native.constData(); + int nativeFilePathLength = native.size(); + + QCFType path = CFStringCreateWithBytes(0, + reinterpret_cast(nativeFilePath), + nativeFilePathLength, + kCFStringEncodingUTF8, + false); + + QCFType url = CFURLCreateWithFileSystemPath(0, path, kCFURLPOSIXPathStyle, true); + + UInt32 type, creator; + // Well created packages have the PkgInfo file + if (CFBundleGetPackageInfoInDirectory(url, &type, &creator)) + return true; + + // Find if an application other than Finder claims to know how to handle the package + if (suffix.length() > 0) { + QCFType application; + LSGetApplicationForURL(url, + kLSRolesEditor|kLSRolesViewer|kLSRolesViewer, + NULL, + &application); + + if (application) { + CFStringRef path = CFURLGetString(application); + QString applicationPath = QCFString::toQString(path); + if (applicationPath != QLatin1String("file://localhost/System/Library/CoreServices/Finder.app/")) + return true; + } + } + + // Third step: check if the directory has the package bit set + FSRef packageRef; + FSPathMakeRef((UInt8 *)nativeFilePath, &packageRef, NULL); + + FSCatalogInfo catalogInfo; + FSGetCatalogInfo(&packageRef, + kFSCatInfoFinderInfo, + &catalogInfo, + NULL, + NULL, + NULL); + + FolderInfo *folderInfo = reinterpret_cast(catalogInfo.finderInfo); + return folderInfo->finderFlags & kHasBundle; +} + +#else +static inline bool _q_isMacHidden(const char *nativePath) +{ + Q_UNUSED(nativePath); + // no-op + return false; +} +#endif + +//static +QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data) +{ +#if defined(__GLIBC__) && !defined(PATH_MAX) +#define PATH_CHUNK_SIZE 256 + char *s = 0; + int len = -1; + int size = PATH_CHUNK_SIZE; + + while (1) { + s = (char *) ::realloc(s, size); + Q_CHECK_PTR(s); + len = ::readlink(link.nativeFilePath().constData(), s, size); + if (len < 0) { + ::free(s); + break; + } + if (len < size) { + break; + } + size *= 2; + } +#else + char s[PATH_MAX+1]; + int len = readlink(link.nativeFilePath().constData(), s, PATH_MAX); +#endif + if (len > 0) { + QString ret; + if (!data.hasFlags(QFileSystemMetaData::DirectoryType)) + fillMetaData(link, data, QFileSystemMetaData::DirectoryType); + if (data.isDirectory() && s[0] != '/') { + QDir parent(link.filePath()); + parent.cdUp(); + ret = parent.path(); + if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/'))) + ret += QLatin1Char('/'); + } + s[len] = '\0'; + ret += QFile::decodeName(QByteArray(s)); +#if defined(__GLIBC__) && !defined(PATH_MAX) + ::free(s); +#endif + + if (!ret.startsWith(QLatin1Char('/'))) { + if (link.filePath().startsWith(QLatin1Char('/'))) { + ret.prepend(link.filePath().left(link.filePath().lastIndexOf(QLatin1Char('/'))) + + QLatin1Char('/')); + } else { + ret.prepend(QDir::currentPath() + QLatin1Char('/')); + } + } + ret = QDir::cleanPath(ret); + if (ret.size() > 1 && ret.endsWith(QLatin1Char('/'))) + ret.chop(1); + return QFileSystemEntry(ret); + } +#if defined(Q_OS_MACX) + { + FSRef fref; + if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(link.filePath())).data(), &fref, 0) == noErr) { + // TODO get the meta data info from the QFileSystemMetaData object + Boolean isAlias, isFolder; + if (FSResolveAliasFile(&fref, true, &isFolder, &isAlias) == noErr && isAlias) { + AliasHandle alias; + if (FSNewAlias(0, &fref, &alias) == noErr && alias) { + QCFString cfstr; + if (FSCopyAliasInfo(alias, 0, 0, &cfstr, 0, 0) == noErr) + return QFileSystemEntry(QCFString::toQString(cfstr)); + } + } + } + } +#endif + return QFileSystemEntry(); +} + +//static +QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data) +{ + if (entry.isEmpty() || entry.isRoot()) + return entry; + +#if !defined(Q_OS_MAC) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && _POSIX_VERSION < 200809L + // realpath(X,0) is not supported + Q_UNUSED(data); + return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath())); +#else + char *ret = 0; +# if defined(Q_OS_MACX) + // When using -mmacosx-version-min=10.4, we get the legacy realpath implementation, + // which does not work properly with the realpath(X,0) form. See QTBUG-28282. + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) { + ret = (char*)malloc(PATH_MAX + 1); + if (ret && realpath(entry.nativeFilePath().constData(), (char*)ret) == 0) { + const int savedErrno = errno; // errno is checked below, and free() might change it + free(ret); + errno = savedErrno; + ret = 0; + } + } else { + // on 10.5 we can use FSRef to resolve the file path. + QString path = QDir::cleanPath(entry.filePath()); + FSRef fsref; + if (FSPathMakeRef((const UInt8 *)path.toUtf8().data(), &fsref, 0) == noErr) { + CFURLRef urlref = CFURLCreateFromFSRef(NULL, &fsref); + CFStringRef canonicalPath = CFURLCopyFileSystemPath(urlref, kCFURLPOSIXPathStyle); + QString ret = QCFString::toQString(canonicalPath); + CFRelease(canonicalPath); + CFRelease(urlref); + return QFileSystemEntry(ret); + } + } +# else +# if _POSIX_VERSION >= 200801L + ret = realpath(entry.nativeFilePath().constData(), (char*)0); +# else + ret = (char*)malloc(PATH_MAX + 1); + if (realpath(entry.nativeFilePath().constData(), (char*)ret) == 0) { + const int savedErrno = errno; // errno is checked below, and free() might change it + free(ret); + errno = savedErrno; + ret = 0; + } +# endif +# endif + if (ret) { + data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute; + data.entryFlags |= QFileSystemMetaData::ExistsAttribute; + QString canonicalPath = QDir::cleanPath(QString::fromLocal8Bit(ret)); + free(ret); + return QFileSystemEntry(canonicalPath); + } else if (errno == ENOENT) { // file doesn't exist + data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute; + data.entryFlags &= ~(QFileSystemMetaData::ExistsAttribute); + return QFileSystemEntry(); + } + return entry; +#endif +} + +//static +QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) +{ + if (entry.isAbsolute() && entry.isClean()) + return entry; + + QByteArray orig = entry.nativeFilePath(); + QByteArray result; + if (orig.isEmpty() || !orig.startsWith('/')) { + QFileSystemEntry cur(currentPath()); + result = cur.nativeFilePath(); + } + if (!orig.isEmpty() && !(orig.length() == 1 && orig[0] == '.')) { + if (!result.isEmpty() && !result.endsWith('/')) + result.append('/'); + result.append(orig); + } + + if (result.length() == 1 && result[0] == '/') + return QFileSystemEntry(result, QFileSystemEntry::FromNativePath()); + const bool isDir = result.endsWith('/'); + + /* as long as QDir::cleanPath() operates on a QString we have to convert to a string here. + * ideally we never convert to a string since that loses information. Please fix after + * we get a QByteArray version of QDir::cleanPath() + */ + QFileSystemEntry resultingEntry(result, QFileSystemEntry::FromNativePath()); + QString stringVersion = QDir::cleanPath(resultingEntry.filePath()); + if (isDir) + stringVersion.append(QLatin1Char('/')); + return QFileSystemEntry(stringVersion); +} + +//static +QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry) +{ + struct stat statResult; + if (stat(entry.nativeFilePath().constData(), &statResult)) { + qErrnoWarning("stat() failed for '%s'", entry.nativeFilePath().constData()); + return QByteArray(); + } + QByteArray result = QByteArray::number(quint64(statResult.st_dev), 16); + result += ':'; + result += QByteArray::number(quint64(statResult.st_ino)); + return result; +} + +//static +QString QFileSystemEngine::resolveUserName(uint userId) +{ +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) + int size_max = sysconf(_SC_GETPW_R_SIZE_MAX); + if (size_max == -1) + size_max = 1024; + QVarLengthArray buf(size_max); +#endif + + struct passwd *pw = 0; +#if !defined(Q_OS_INTEGRITY) +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS) + struct passwd entry; + getpwuid_r(userId, &entry, buf.data(), buf.size(), &pw); +#else + pw = getpwuid(userId); +#endif +#endif + if (pw) + return QFile::decodeName(QByteArray(pw->pw_name)); + return QString(); +} + +//static +QString QFileSystemEngine::resolveGroupName(uint groupId) +{ +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) + int size_max = sysconf(_SC_GETPW_R_SIZE_MAX); + if (size_max == -1) + size_max = 1024; + QVarLengthArray buf(size_max); +#endif + + struct group *gr = 0; +#if !defined(Q_OS_INTEGRITY) +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS) + size_max = sysconf(_SC_GETGR_R_SIZE_MAX); + if (size_max == -1) + size_max = 1024; + buf.resize(size_max); + struct group entry; + // Some large systems have more members than the POSIX max size + // Loop over by doubling the buffer size (upper limit 250k) + for (unsigned size = size_max; size < 256000; size += size) + { + buf.resize(size); + // ERANGE indicates that the buffer was too small + if (!getgrgid_r(groupId, &entry, buf.data(), buf.size(), &gr) + || errno != ERANGE) + break; + } +#else + gr = getgrgid(groupId); +#endif +#endif + if (gr) + return QFile::decodeName(QByteArray(gr->gr_name)); + return QString(); +} + +#if defined(Q_OS_MACX) +//static +QString QFileSystemEngine::bundleName(const QFileSystemEntry &entry) +{ + QCFType url = CFURLCreateWithFileSystemPath(0, QCFString(entry.filePath()), + kCFURLPOSIXPathStyle, true); + if (QCFType dict = CFBundleCopyInfoDictionaryForURL(url)) { + if (CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) { + if (CFGetTypeID(name) == CFStringGetTypeID()) + return QCFString::toQString((CFStringRef)name); + } + } + return QString(); +} +#endif + +//static +bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what) +{ +#if defined(Q_OS_MACX) + if (what & QFileSystemMetaData::BundleType) { + if (!data.hasFlags(QFileSystemMetaData::DirectoryType)) + what |= QFileSystemMetaData::DirectoryType; + } + if (what & QFileSystemMetaData::HiddenAttribute) { + // Mac OS >= 10.5: st_flags & UF_HIDDEN + what |= QFileSystemMetaData::PosixStatFlags; + } +#endif // defined(Q_OS_MACX) + + if (what & QFileSystemMetaData::PosixStatFlags) + what |= QFileSystemMetaData::PosixStatFlags; + + if (what & QFileSystemMetaData::ExistsAttribute) { + // FIXME: Would other queries being performed provide this bit? + what |= QFileSystemMetaData::PosixStatFlags; + } + + data.entryFlags &= ~what; + + const char * nativeFilePath; + int nativeFilePathLength; + { + const QByteArray &path = entry.nativeFilePath(); + nativeFilePath = path.constData(); + nativeFilePathLength = path.size(); + Q_UNUSED(nativeFilePathLength); + } + + bool entryExists = true; // innocent until proven otherwise + + QT_STATBUF statBuffer; + bool statBufferValid = false; + if (what & QFileSystemMetaData::LinkType) { + if (QT_LSTAT(nativeFilePath, &statBuffer) == 0) { + if (S_ISLNK(statBuffer.st_mode)) { + data.entryFlags |= QFileSystemMetaData::LinkType; + } else { + statBufferValid = true; + data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags; + } + } else { + entryExists = false; + } + + data.knownFlagsMask |= QFileSystemMetaData::LinkType; + } + + if (statBufferValid || (what & QFileSystemMetaData::PosixStatFlags)) { + if (entryExists && !statBufferValid) + statBufferValid = (QT_STAT(nativeFilePath, &statBuffer) == 0); + + if (statBufferValid) + data.fillFromStatBuf(statBuffer); + else { + entryExists = false; + data.creationTime_ = 0; + data.modificationTime_ = 0; + data.accessTime_ = 0; + data.size_ = 0; + data.userId_ = (uint) -2; + data.groupId_ = (uint) -2; + } + + // reset the mask + data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags + | QFileSystemMetaData::ExistsAttribute; + } + +#if defined(Q_OS_MACX) + if (what & QFileSystemMetaData::AliasType) + { + if (entryExists) { + FSRef fref; + if (FSPathMakeRef((const UInt8 *)nativeFilePath, &fref, NULL) == noErr) { + Boolean isAlias, isFolder; + if (FSIsAliasFile(&fref, &isAlias, &isFolder) == noErr) { + if (isAlias) + data.entryFlags |= QFileSystemMetaData::AliasType; + } + } + } + data.knownFlagsMask |= QFileSystemMetaData::AliasType; + } +#endif + + if (what & QFileSystemMetaData::UserPermissions) { + // calculate user permissions + + if (entryExists) { + if (what & QFileSystemMetaData::UserReadPermission) { + if (QT_ACCESS(nativeFilePath, R_OK) == 0) + data.entryFlags |= QFileSystemMetaData::UserReadPermission; + } + if (what & QFileSystemMetaData::UserWritePermission) { + if (QT_ACCESS(nativeFilePath, W_OK) == 0) + data.entryFlags |= QFileSystemMetaData::UserWritePermission; + } + if (what & QFileSystemMetaData::UserExecutePermission) { + if (QT_ACCESS(nativeFilePath, X_OK) == 0) + data.entryFlags |= QFileSystemMetaData::UserExecutePermission; + } + } + data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions); + } + + if (what & QFileSystemMetaData::HiddenAttribute + && !data.isHidden()) { + QString fileName = entry.fileName(); + if ((fileName.size() > 0 && fileName.at(0) == QLatin1Char('.')) + || (entryExists && _q_isMacHidden(nativeFilePath))) + data.entryFlags |= QFileSystemMetaData::HiddenAttribute; + data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute; + } + +#if defined(Q_OS_MACX) + if (what & QFileSystemMetaData::BundleType) { + if (entryExists && isPackage(data, entry)) + data.entryFlags |= QFileSystemMetaData::BundleType; + + data.knownFlagsMask |= QFileSystemMetaData::BundleType; + } +#endif + if (!entryExists) { + data.clearFlags(what); + return false; + } + return data.hasFlags(what); +} + +//static +bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents) +{ + QString dirName = entry.filePath(); + if (createParents) { + dirName = QDir::cleanPath(dirName); + for (int oldslash = -1, slash=0; slash != -1; oldslash = slash) { + slash = dirName.indexOf(QDir::separator(), oldslash+1); + if (slash == -1) { + if (oldslash == dirName.length()) + break; + slash = dirName.length(); + } + if (slash) { + const QByteArray chunk = QFile::encodeName(dirName.left(slash)); + if (QT_MKDIR(chunk.constData(), 0777) != 0) { + if (errno == EEXIST +#if defined(Q_OS_QNX) + // On QNX the QNet (VFS paths of other hosts mounted under a directory + // such as /net) mountpoint returns ENOENT, despite existing. stat() + // on the QNet mountpoint returns successfully and reports S_IFDIR. + || errno == ENOENT +#endif + ) { + QT_STATBUF st; + if (QT_STAT(chunk.constData(), &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR) + continue; + } + return false; + } + } + } + return true; + } +#if defined(Q_OS_DARWIN) // Mac X doesn't support trailing /'s + if (dirName.endsWith(QLatin1Char('/'))) + dirName.chop(1); +#endif + return (QT_MKDIR(QFile::encodeName(dirName).constData(), 0777) == 0); +} + +//static +bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents) +{ + if (removeEmptyParents) { + QString dirName = QDir::cleanPath(entry.filePath()); + for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) { + const QByteArray chunk = QFile::encodeName(dirName.left(slash)); + QT_STATBUF st; + if (QT_STAT(chunk.constData(), &st) != -1) { + if ((st.st_mode & S_IFMT) != S_IFDIR) + return false; + if (::rmdir(chunk.constData()) != 0) + return oldslash != 0; + } else { + return false; + } + slash = dirName.lastIndexOf(QDir::separator(), oldslash-1); + } + return true; + } + return rmdir(QFile::encodeName(entry.filePath()).constData()) == 0; +} + +//static +bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0) + return true; + error = QSystemError(errno, QSystemError::StandardLibraryError); + return false; +} + +//static +bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + Q_UNUSED(source); + Q_UNUSED(target); + error = QSystemError(ENOSYS, QSystemError::StandardLibraryError); //Function not implemented + return false; +} + +//static +bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0) + return true; + error = QSystemError(errno, QSystemError::StandardLibraryError); + return false; +} + +//static +bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error) +{ + if (unlink(entry.nativeFilePath().constData()) == 0) + return true; + error = QSystemError(errno, QSystemError::StandardLibraryError); + return false; + +} + +//static +bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data) +{ + mode_t mode = 0; + if (permissions & QFile::ReadOwner) + mode |= S_IRUSR; + if (permissions & QFile::WriteOwner) + mode |= S_IWUSR; + if (permissions & QFile::ExeOwner) + mode |= S_IXUSR; + if (permissions & QFile::ReadUser) + mode |= S_IRUSR; + if (permissions & QFile::WriteUser) + mode |= S_IWUSR; + if (permissions & QFile::ExeUser) + mode |= S_IXUSR; + if (permissions & QFile::ReadGroup) + mode |= S_IRGRP; + if (permissions & QFile::WriteGroup) + mode |= S_IWGRP; + if (permissions & QFile::ExeGroup) + mode |= S_IXGRP; + if (permissions & QFile::ReadOther) + mode |= S_IROTH; + if (permissions & QFile::WriteOther) + mode |= S_IWOTH; + if (permissions & QFile::ExeOther) + mode |= S_IXOTH; + + bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0; + if (success && data) { + data->entryFlags &= ~QFileSystemMetaData::Permissions; + data->entryFlags |= QFileSystemMetaData::MetaDataFlag(uint(permissions)); + data->knownFlagsMask |= QFileSystemMetaData::Permissions; + } + if (!success) + error = QSystemError(errno, QSystemError::StandardLibraryError); + return success; +} + +QString QFileSystemEngine::homePath() +{ + QString home = QFile::decodeName(qgetenv("HOME")); + if (home.isEmpty()) + home = rootPath(); + return QDir::cleanPath(home); +} + +QString QFileSystemEngine::rootPath() +{ + return QLatin1String("/"); +} + +QString QFileSystemEngine::tempPath() +{ +#ifdef QT_UNIX_TEMP_PATH_OVERRIDE + return QLatin1String(QT_UNIX_TEMP_PATH_OVERRIDE); +#elif defined(Q_OS_BLACKBERRY) + QString temp = QFile::decodeName(qgetenv("TEMP")); + if (temp.isEmpty()) + temp = QFile::decodeName(qgetenv("TMPDIR")); + + if (temp.isEmpty()) { + qWarning("Neither the TEMP nor the TMPDIR environment variable is set, falling back to /tmp."); + temp = QLatin1String("/tmp/"); + } + return QDir::cleanPath(temp); +#else + QString temp = QFile::decodeName(qgetenv("TMPDIR")); + if (temp.isEmpty()) + temp = QLatin1String("/tmp/"); + return QDir::cleanPath(temp); +#endif +} + +bool QFileSystemEngine::setCurrentPath(const QFileSystemEntry &path) +{ + int r; + r = QT_CHDIR(path.nativeFilePath().constData()); + return r >= 0; +} + +QFileSystemEntry QFileSystemEngine::currentPath() +{ + QFileSystemEntry result; + QT_STATBUF st; + if (QT_STAT(".", &st) == 0) { +#if defined(__GLIBC__) && !defined(PATH_MAX) + char *currentName = ::get_current_dir_name(); + if (currentName) { + result = QFileSystemEntry(QByteArray(currentName), QFileSystemEntry::FromNativePath()); + ::free(currentName); + } +#else + char currentName[PATH_MAX+1]; + if (::getcwd(currentName, PATH_MAX)) { +#if defined(Q_OS_VXWORKS) && defined(VXWORKS_VXSIM) + QByteArray dir(currentName); + if (dir.indexOf(':') < dir.indexOf('/')) + dir.remove(0, dir.indexOf(':')+1); + + qstrncpy(currentName, dir.constData(), PATH_MAX); +#endif + result = QFileSystemEntry(QByteArray(currentName), QFileSystemEntry::FromNativePath()); + } +# if defined(QT_DEBUG) + if (result.isEmpty()) + qWarning("QFileSystemEngine::currentPath: getcwd() failed"); +# endif +#endif + } else { +# if defined(QT_DEBUG) + qWarning("QFileSystemEngine::currentPath: stat(\".\") failed"); +# endif + } + return result; +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qfilesystemengine_win.cpp b/src/app/core/backport/qfilesystemengine_win.cpp new file mode 100644 index 000000000..d401be514 --- /dev/null +++ b/src/app/core/backport/qfilesystemengine_win.cpp @@ -0,0 +1,1324 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilesystemengine_p.h" + +#include "qplatformdefs.h" +#include +#include "qabstractfileengine_p.h" +#include "qfsfileengine_p.h" +#include "qsystemlibrary_p.h" +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#include +#include +#include "qmutexpool_p.h" +#include +#include +#include "qt_windows.h" + +#if !defined(Q_OS_WINCE) +# include +# include +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include +#define SECURITY_WIN32 +#include + +#ifndef SPI_GETPLATFORMTYPE +#define SPI_GETPLATFORMTYPE 257 +#endif + +#ifndef PATH_MAX +#define PATH_MAX FILENAME_MAX +#endif + +#ifndef _INTPTR_T_DEFINED +#ifdef _WIN64 +typedef __int64 intptr_t; +#else +#ifdef _W64 +typedef _W64 int intptr_t; +#else +typedef INT_PTR intptr_t; +#endif +#endif +#define _INTPTR_T_DEFINED +#endif + +#ifndef INVALID_FILE_ATTRIBUTES +# define INVALID_FILE_ATTRIBUTES (DWORD (-1)) +#endif + +#if !defined(Q_OS_WINCE) +# if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE) +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; +# define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) +# endif // !defined(REPARSE_DATA_BUFFER_HEADER_SIZE) + +# ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE +# define MAXIMUM_REPARSE_DATA_BUFFER_SIZE 16384 +# endif +# ifndef IO_REPARSE_TAG_SYMLINK +# define IO_REPARSE_TAG_SYMLINK (0xA000000CL) +# endif +# ifndef FSCTL_GET_REPARSE_POINT +# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) +# endif +#endif // !defined(Q_OS_WINCE) + + +int qt_ntfs_permission_lookup = 0; + +#if defined(Q_OS_WINCE) +static QString qfsPrivateCurrentDir = QLatin1String(""); +// As none of the functions we try to resolve do exist on Windows CE +// we use QT_NO_LIBRARY to shorten everything up a little bit. +#ifndef QT_NO_LIBRARY +#define QT_NO_LIBRARY 1 +#endif +#endif + +#if !defined(QT_NO_LIBRARY) +typedef DWORD (WINAPI *PtrGetNamedSecurityInfoW)(LPWSTR, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID*, PSID*, PACL*, PACL*, PSECURITY_DESCRIPTOR*); +static PtrGetNamedSecurityInfoW ptrGetNamedSecurityInfoW = 0; +typedef BOOL (WINAPI *PtrLookupAccountSidW)(LPCWSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, PSID_NAME_USE); +static PtrLookupAccountSidW ptrLookupAccountSidW = 0; +typedef VOID (WINAPI *PtrBuildTrusteeWithSidW)(PTRUSTEE_W, PSID); +static PtrBuildTrusteeWithSidW ptrBuildTrusteeWithSidW = 0; +typedef DWORD (WINAPI *PtrGetEffectiveRightsFromAclW)(PACL, PTRUSTEE_W, OUT PACCESS_MASK); +static PtrGetEffectiveRightsFromAclW ptrGetEffectiveRightsFromAclW = 0; +typedef BOOL (WINAPI *PtrGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD); +static PtrGetUserProfileDirectoryW ptrGetUserProfileDirectoryW = 0; +typedef BOOL (WINAPI *PtrGetVolumePathNamesForVolumeNameW)(LPCWSTR,LPWSTR,DWORD,PDWORD); +static PtrGetVolumePathNamesForVolumeNameW ptrGetVolumePathNamesForVolumeNameW = 0; + +static TRUSTEE_W currentUserTrusteeW; +static TRUSTEE_W worldTrusteeW; +static PSID currentUserSID = 0; +static PSID worldSID = 0; + +/* + Deletes the allocated SIDs during global static cleanup +*/ +class SidCleanup +{ +public: + ~SidCleanup(); +}; + +SidCleanup::~SidCleanup() +{ + free(currentUserSID); + currentUserSID = 0; + + // worldSID was allocated with AllocateAndInitializeSid so it needs to be freed with FreeSid + if (worldSID) { + ::FreeSid(worldSID); + worldSID = 0; + } +} + +Q_GLOBAL_STATIC(SidCleanup, initSidCleanup) + +static void resolveLibs() +{ + static bool triedResolve = false; + if (!triedResolve) { + // need to resolve the security info functions + + // protect initialization +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve)); + // check triedResolve again, since another thread may have already + // done the initialization + if (triedResolve) { + // another thread did initialize the security function pointers, + // so we shouldn't do it again. + return; + } +#endif + + triedResolve = true; +#if !defined(Q_OS_WINCE) + HINSTANCE advapiHnd = QSystemLibrary::load(L"advapi32"); + if (advapiHnd) { + ptrGetNamedSecurityInfoW = (PtrGetNamedSecurityInfoW)GetProcAddress(advapiHnd, "GetNamedSecurityInfoW"); + ptrLookupAccountSidW = (PtrLookupAccountSidW)GetProcAddress(advapiHnd, "LookupAccountSidW"); + ptrBuildTrusteeWithSidW = (PtrBuildTrusteeWithSidW)GetProcAddress(advapiHnd, "BuildTrusteeWithSidW"); + ptrGetEffectiveRightsFromAclW = (PtrGetEffectiveRightsFromAclW)GetProcAddress(advapiHnd, "GetEffectiveRightsFromAclW"); + } + if (ptrBuildTrusteeWithSidW) { + // Create TRUSTEE for current user + HANDLE hnd = ::GetCurrentProcess(); + HANDLE token = 0; + initSidCleanup(); + if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) { + DWORD retsize = 0; + // GetTokenInformation requires a buffer big enough for the TOKEN_USER struct and + // the SID struct. Since the SID struct can have variable number of subauthorities + // tacked at the end, its size is variable. Obtain the required size by first + // doing a dummy GetTokenInformation call. + ::GetTokenInformation(token, TokenUser, 0, 0, &retsize); + if (retsize) { + void *tokenBuffer = malloc(retsize); + if (::GetTokenInformation(token, TokenUser, tokenBuffer, retsize, &retsize)) { + PSID tokenSid = reinterpret_cast(tokenBuffer)->User.Sid; + DWORD sidLen = ::GetLengthSid(tokenSid); + currentUserSID = reinterpret_cast(malloc(sidLen)); + if (::CopySid(sidLen, currentUserSID, tokenSid)) + ptrBuildTrusteeWithSidW(¤tUserTrusteeW, currentUserSID); + } + free(tokenBuffer); + } + ::CloseHandle(token); + } + + typedef BOOL (WINAPI *PtrAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*); + PtrAllocateAndInitializeSid ptrAllocateAndInitializeSid = (PtrAllocateAndInitializeSid)GetProcAddress(advapiHnd, "AllocateAndInitializeSid"); + if (ptrAllocateAndInitializeSid) { + // Create TRUSTEE for Everyone (World) + SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY }; + if (ptrAllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &worldSID)) + ptrBuildTrusteeWithSidW(&worldTrusteeW, worldSID); + } + } + HINSTANCE userenvHnd = QSystemLibrary::load(L"userenv"); + if (userenvHnd) + ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW"); + HINSTANCE kernel32 = LoadLibrary(L"kernel32"); + if(kernel32) + ptrGetVolumePathNamesForVolumeNameW = (PtrGetVolumePathNamesForVolumeNameW)GetProcAddress(kernel32, "GetVolumePathNamesForVolumeNameW"); +#endif + } +} +#endif // QT_NO_LIBRARY + +typedef DWORD (WINAPI *PtrNetShareEnum)(LPWSTR, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD); +static PtrNetShareEnum ptrNetShareEnum = 0; +typedef DWORD (WINAPI *PtrNetApiBufferFree)(LPVOID); +static PtrNetApiBufferFree ptrNetApiBufferFree = 0; +typedef struct _SHARE_INFO_1 { + LPWSTR shi1_netname; + DWORD shi1_type; + LPWSTR shi1_remark; +} SHARE_INFO_1; + + +static bool resolveUNCLibs() +{ + static bool triedResolve = false; + if (!triedResolve) { +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve)); + if (triedResolve) { + return ptrNetShareEnum && ptrNetApiBufferFree; + } +#endif + triedResolve = true; +#if !defined(Q_OS_WINCE) + HINSTANCE hLib = QSystemLibrary::load(L"Netapi32"); + if (hLib) { + ptrNetShareEnum = (PtrNetShareEnum)GetProcAddress(hLib, "NetShareEnum"); + if (ptrNetShareEnum) + ptrNetApiBufferFree = (PtrNetApiBufferFree)GetProcAddress(hLib, "NetApiBufferFree"); + } +#endif + } + return ptrNetShareEnum && ptrNetApiBufferFree; +} + +static QString readSymLink(const QFileSystemEntry &link) +{ + QString result; +#if !defined(Q_OS_WINCE) + HANDLE handle = CreateFile((wchar_t*)link.nativeFilePath().utf16(), + FILE_READ_EA, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + 0); + if (handle != INVALID_HANDLE_VALUE) { + DWORD bufsize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; + REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)malloc(bufsize); + DWORD retsize = 0; + if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, 0, 0, rdb, bufsize, &retsize, 0)) { + if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { + int length = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t); + int offset = rdb->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t); + const wchar_t* PathBuffer = &rdb->MountPointReparseBuffer.PathBuffer[offset]; + result = QString::fromWCharArray(PathBuffer, length); + } else if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + int length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t); + int offset = rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t); + const wchar_t* PathBuffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[offset]; + result = QString::fromWCharArray(PathBuffer, length); + } + // cut-off "//?/" and "/??/" + if (result.size() > 4 && result.at(0) == QLatin1Char('\\') && result.at(2) == QLatin1Char('?') && result.at(3) == QLatin1Char('\\')) + result = result.mid(4); + } + free(rdb); + CloseHandle(handle); + +#if !defined(QT_NO_LIBRARY) + resolveLibs(); + if (ptrGetVolumePathNamesForVolumeNameW) { + QRegExp matchVolName(QLatin1String("^Volume\\{([a-z]|[0-9]|-)+\\}\\\\"), Qt::CaseInsensitive); + if(matchVolName.indexIn(result) == 0) { + DWORD len; + wchar_t buffer[MAX_PATH]; + QString volumeName = result.mid(0, matchVolName.matchedLength()).prepend(QLatin1String("\\\\?\\")); + if(ptrGetVolumePathNamesForVolumeNameW((wchar_t*)volumeName.utf16(), buffer, MAX_PATH, &len) != 0) + result.replace(0,matchVolName.matchedLength(), QString::fromWCharArray(buffer)); + } + } +#endif + } +#else + Q_UNUSED(link); +#endif // Q_OS_WINCE + return result; +} + +static QString readLink(const QFileSystemEntry &link) +{ +#if !defined(Q_OS_WINCE) +#if !defined(QT_NO_LIBRARY) + QString ret; + + bool neededCoInit = false; + IShellLink *psl; // pointer to IShellLink i/f + WIN32_FIND_DATA wfd; + wchar_t szGotPath[MAX_PATH]; + + // Get pointer to the IShellLink interface. + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl); + + if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized + neededCoInit = true; + CoInitialize(NULL); + hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + IID_IShellLink, (LPVOID *)&psl); + } + if (SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface. + IPersistFile *ppf; + hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf); + if (SUCCEEDED(hres)) { + hres = ppf->Load((LPOLESTR)link.nativeFilePath().utf16(), STGM_READ); + //The original path of the link is retrieved. If the file/folder + //was moved, the return value still have the old path. + if (SUCCEEDED(hres)) { + if (psl->GetPath(szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR) + ret = QString::fromWCharArray(szGotPath); + } + ppf->Release(); + } + psl->Release(); + } + if (neededCoInit) + CoUninitialize(); + + return ret; +#else + Q_UNUSED(link); + return QString(); +#endif // QT_NO_LIBRARY +#else + wchar_t target[MAX_PATH]; + QString result; + if (SHGetShortcutTarget((wchar_t*)QFileInfo(link.filePath()).absoluteFilePath().replace(QLatin1Char('/'),QLatin1Char('\\')).utf16(), target, MAX_PATH)) { + result = QString::fromWCharArray(target); + if (result.startsWith(QLatin1Char('"'))) + result.remove(0,1); + if (result.endsWith(QLatin1Char('"'))) + result.remove(result.size()-1,1); + } + return result; +#endif // Q_OS_WINCE +} + +static bool uncShareExists(const QString &server) +{ + // This code assumes the UNC path is always like \\?\UNC\server... + QStringList parts = server.split(QLatin1Char('\\'), QString::SkipEmptyParts); + if (parts.count() >= 3) { + QStringList shares; + if (QFileSystemEngine::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(2), &shares)) + return parts.count() >= 4 ? shares.contains(parts.at(3), Qt::CaseInsensitive) : true; + } + return false; +} + +static inline bool getFindData(QString path, WIN32_FIND_DATA &findData) +{ + // path should not end with a trailing slash + while (path.endsWith(QLatin1Char('\\'))) + path.chop(1); + + // can't handle drives + if (!path.endsWith(QLatin1Char(':'))) { + HANDLE hFind = ::FindFirstFile((wchar_t*)path.utf16(), &findData); + if (hFind != INVALID_HANDLE_VALUE) { + ::FindClose(hFind); + return true; + } + } + + return false; +} + +bool QFileSystemEngine::uncListSharesOnServer(const QString &server, QStringList *list) +{ + if (resolveUNCLibs()) { + SHARE_INFO_1 *BufPtr, *p; + DWORD res; + DWORD er = 0, tr = 0, resume = 0, i; + do { + res = ptrNetShareEnum((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume); + if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) { + p = BufPtr; + for (i = 1; i <= er; ++i) { + if (list && p->shi1_type == 0) + list->append(QString::fromWCharArray(p->shi1_netname)); + p++; + } + } + ptrNetApiBufferFree(BufPtr); + } while (res == ERROR_MORE_DATA); + return res == ERROR_SUCCESS; + } + return false; +} + +void QFileSystemEngine::clearWinStatData(QFileSystemMetaData &data) +{ + data.size_ = 0; + data.fileAttribute_ = 0; + data.creationTime_ = FILETIME(); + data.lastAccessTime_ = FILETIME(); + data.lastWriteTime_ = FILETIME(); +} + +//static +QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, + QFileSystemMetaData &data) +{ + if (data.missingFlags(QFileSystemMetaData::LinkType)) + QFileSystemEngine::fillMetaData(link, data, QFileSystemMetaData::LinkType); + + QString ret; + if (data.isLnkFile()) + ret = readLink(link); + else if (data.isLink()) + ret = readSymLink(link); + return QFileSystemEntry(ret); +} + +//static +QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data) +{ + if (data.missingFlags(QFileSystemMetaData::ExistsAttribute)) + QFileSystemEngine::fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute); + + if (data.exists()) + return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath())); + else + return QFileSystemEntry(); +} + +//static +QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path) +{ + // can be //server or //server/share + QString absPath; +#if !defined(Q_OS_WINCE) + QVarLengthArray buf(qMax(MAX_PATH, path.size() + 1)); + wchar_t *fileName = 0; + DWORD retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName); + if (retLen > (DWORD)buf.size()) { + buf.resize(retLen); + retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName); + } + if (retLen != 0) + absPath = QString::fromWCharArray(buf.data(), retLen); +#else + if (path.startsWith(QLatin1Char('/')) || path.startsWith(QLatin1Char('\\'))) + absPath = QDir::toNativeSeparators(path); + else + absPath = QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir + QLatin1Char('/') + path)); +#endif + // This is really ugly, but GetFullPathName strips off whitespace at the end. + // If you for instance write ". " in the lineedit of QFileDialog, + // (which is an invalid filename) this function will strip the space off and viola, + // the file is later reported as existing. Therefore, we re-add the whitespace that + // was at the end of path in order to keep the filename invalid. + if (!path.isEmpty() && path.at(path.size() - 1) == QLatin1Char(' ')) + absPath.append(QLatin1Char(' ')); + return absPath; +} + +//static +QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) +{ + QString ret; + + if (!entry.isRelative()) { +#if !defined(Q_OS_WINCE) + if (entry.isAbsolute() && entry.isClean()) { + ret = entry.filePath(); + } else { + ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(entry.filePath())); + } +#else + ret = entry.filePath(); +#endif + } else { + ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + entry.filePath()); + } + + // The path should be absolute at this point. + // From the docs : + // Absolute paths begin with the directory separator "/" + // (optionally preceded by a drive specification under Windows). + if (ret.at(0) != QLatin1Char('/')) { + Q_ASSERT(ret.length() >= 2); + Q_ASSERT(ret.at(0).isLetter()); + Q_ASSERT(ret.at(1) == QLatin1Char(':')); + + // Force uppercase drive letters. + ret[0] = ret.at(0).toUpper(); + } + return QFileSystemEntry(ret, QFileSystemEntry::FromInternalPath()); +} + +#ifndef Q_OS_WINCE + +// FILE_INFO_BY_HANDLE_CLASS has been extended by FileIdInfo = 18 as of VS2012. +typedef enum { Q_FileIdInfo = 18 } Q_FILE_INFO_BY_HANDLE_CLASS; + +# if defined(Q_CC_MINGW) || (defined(Q_CC_MSVC) && _MSC_VER < 1700) + +// MinGW-64 defines FILE_ID_128 as of gcc-4.8.1 along with FILE_SUPPORTS_INTEGRITY_STREAMS +# if !(defined(Q_CC_MINGW) && defined(FILE_SUPPORTS_INTEGRITY_STREAMS)) +typedef struct _FILE_ID_128 { + BYTE Identifier[16]; +} FILE_ID_128, *PFILE_ID_128; +# endif // !(Q_CC_MINGW && FILE_SUPPORTS_INTEGRITY_STREAMS) + +typedef struct _FILE_ID_INFO { + ULONGLONG VolumeSerialNumber; + FILE_ID_128 FileId; +} FILE_ID_INFO, *PFILE_ID_INFO; +# endif // if defined (Q_CC_MINGW) || (defined(Q_CC_MSVC) && _MSC_VER < 1700)) + +// File ID for Windows up to version 7. +static inline QByteArray fileId(HANDLE handle) +{ + QByteArray result; + BY_HANDLE_FILE_INFORMATION info; + if (GetFileInformationByHandle(handle, &info)) { + result = QByteArray::number(uint(info.nFileIndexLow), 16); + result += ':'; + result += QByteArray::number(uint(info.nFileIndexHigh), 16); + } + return result; +} + +// File ID for Windows starting from version 8. +QByteArray fileIdWin8(HANDLE handle) +{ + typedef BOOL (WINAPI* GetFileInformationByHandleExType)(HANDLE, Q_FILE_INFO_BY_HANDLE_CLASS, void *, DWORD); + + // Dynamically resolve GetFileInformationByHandleEx (Vista onwards). + static GetFileInformationByHandleExType getFileInformationByHandleEx = 0; + if (!getFileInformationByHandleEx) { + QSystemLibrary library(QLatin1String("kernel32")); + getFileInformationByHandleEx = (GetFileInformationByHandleExType)library.resolve("GetFileInformationByHandleEx"); + } + QByteArray result; + if (getFileInformationByHandleEx) { + FILE_ID_INFO infoEx; + if (getFileInformationByHandleEx(handle, Q_FileIdInfo, + &infoEx, sizeof(FILE_ID_INFO))) { + result = QByteArray::number(infoEx.VolumeSerialNumber, 16); + result += ':'; + // Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one. + result += QByteArray((char *)&infoEx.FileId, sizeof(infoEx.FileId)).toHex(); + } + } + return result; +} +#endif // !Q_OS_WINCE + +//static +QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry) +{ +#ifndef Q_OS_WINCE + QByteArray result; + const HANDLE handle = + CreateFile((wchar_t*)entry.nativeFilePath().utf16(), GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle) { + result = QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS8 ? + fileIdWin8(handle) : fileId(handle); + CloseHandle(handle); + } + return result; +#else // !Q_OS_WINCE + return entry.nativeFilePath().toLower().toLatin1(); +#endif +} + +//static +QString QFileSystemEngine::owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own) +{ + QString name; +#if !defined(QT_NO_LIBRARY) + extern int qt_ntfs_permission_lookup; + if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) { + resolveLibs(); + if (ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) { + PSID pOwner = 0; + PSECURITY_DESCRIPTOR pSD; + if (ptrGetNamedSecurityInfoW((wchar_t*)entry.nativeFilePath().utf16(), SE_FILE_OBJECT, + own == QAbstractFileEngine::OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION, + own == QAbstractFileEngine::OwnerUser ? &pOwner : 0, own == QAbstractFileEngine::OwnerGroup ? &pOwner : 0, + 0, 0, &pSD) == ERROR_SUCCESS) { + DWORD lowner = 64; + DWORD ldomain = 64; + QVarLengthArray owner(lowner); + QVarLengthArray domain(ldomain); + SID_NAME_USE use = SidTypeUnknown; + // First call, to determine size of the strings (with '\0'). + if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner, + (LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + if (lowner > (DWORD)owner.size()) + owner.resize(lowner); + if (ldomain > (DWORD)domain.size()) + domain.resize(ldomain); + // Second call, try on resized buf-s + if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner, + (LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) { + lowner = 0; + } + } else { + lowner = 0; + } + } + if (lowner != 0) + name = QString::fromWCharArray(owner.data()); + LocalFree(pSD); + } + } + } +#else + Q_UNUSED(entry); + Q_UNUSED(own); +#endif + return name; +} + +//static +bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what) +{ +#if !defined(QT_NO_LIBRARY) + if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) { + resolveLibs(); + if(ptrGetNamedSecurityInfoW && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW) { + enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 }; + + QString fname = entry.nativeFilePath(); + PSID pOwner = 0; + PSID pGroup = 0; + PACL pDacl; + PSECURITY_DESCRIPTOR pSD; + DWORD res = ptrGetNamedSecurityInfoW((wchar_t*)fname.utf16(), SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + &pOwner, &pGroup, &pDacl, 0, &pSD); + if(res == ERROR_SUCCESS) { + ACCESS_MASK access_mask; + TRUSTEE_W trustee; + if (what & QFileSystemMetaData::UserPermissions) { // user + data.knownFlagsMask |= QFileSystemMetaData::UserPermissions; + if(ptrGetEffectiveRightsFromAclW(pDacl, ¤tUserTrusteeW, &access_mask) != ERROR_SUCCESS) + access_mask = (ACCESS_MASK)-1; + if(access_mask & ReadMask) + data.entryFlags |= QFileSystemMetaData::UserReadPermission; + if(access_mask & WriteMask) + data.entryFlags|= QFileSystemMetaData::UserWritePermission; + if(access_mask & ExecMask) + data.entryFlags|= QFileSystemMetaData::UserExecutePermission; + } + if (what & QFileSystemMetaData::OwnerPermissions) { // owner + data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions; + ptrBuildTrusteeWithSidW(&trustee, pOwner); + if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS) + access_mask = (ACCESS_MASK)-1; + if(access_mask & ReadMask) + data.entryFlags |= QFileSystemMetaData::OwnerReadPermission; + if(access_mask & WriteMask) + data.entryFlags |= QFileSystemMetaData::OwnerWritePermission; + if(access_mask & ExecMask) + data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission; + } + if (what & QFileSystemMetaData::GroupPermissions) { // group + data.knownFlagsMask |= QFileSystemMetaData::GroupPermissions; + ptrBuildTrusteeWithSidW(&trustee, pGroup); + if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS) + access_mask = (ACCESS_MASK)-1; + if(access_mask & ReadMask) + data.entryFlags |= QFileSystemMetaData::GroupReadPermission; + if(access_mask & WriteMask) + data.entryFlags |= QFileSystemMetaData::GroupWritePermission; + if(access_mask & ExecMask) + data.entryFlags |= QFileSystemMetaData::GroupExecutePermission; + } + if (what & QFileSystemMetaData::OtherPermissions) { // other (world) + data.knownFlagsMask |= QFileSystemMetaData::OtherPermissions; + if(ptrGetEffectiveRightsFromAclW(pDacl, &worldTrusteeW, &access_mask) != ERROR_SUCCESS) + access_mask = (ACCESS_MASK)-1; // ### + if(access_mask & ReadMask) + data.entryFlags |= QFileSystemMetaData::OtherReadPermission; + if(access_mask & WriteMask) + data.entryFlags |= QFileSystemMetaData::OtherWritePermission; + if(access_mask & ExecMask) + data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission; + } + LocalFree(pSD); + } + } + } else +#endif + { + //### what to do with permissions if we don't use NTFS + // for now just add all permissions and what about exe missions ?? + // also qt_ntfs_permission_lookup is now not set by default ... should it ? + data.entryFlags |= QFileSystemMetaData::OwnerReadPermission + | QFileSystemMetaData::GroupReadPermission + | QFileSystemMetaData::OtherReadPermission; + + if (!(data.fileAttribute_ & FILE_ATTRIBUTE_READONLY)) { + data.entryFlags |= QFileSystemMetaData::OwnerWritePermission + | QFileSystemMetaData::GroupWritePermission + | QFileSystemMetaData::OtherWritePermission; + } + + QString fname = entry.filePath(); + QString ext = fname.right(4).toLower(); + if (data.isDirectory() || + ext == QLatin1String(".exe") || ext == QLatin1String(".com") || ext == QLatin1String(".bat") || + ext == QLatin1String(".pif") || ext == QLatin1String(".cmd")) { + data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission | QFileSystemMetaData::GroupExecutePermission + | QFileSystemMetaData::OtherExecutePermission | QFileSystemMetaData::UserExecutePermission; + } + data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions | QFileSystemMetaData::GroupPermissions + | QFileSystemMetaData::OtherPermissions | QFileSystemMetaData::UserExecutePermission; + // calculate user permissions + if (what & QFileSystemMetaData::UserReadPermission) { + if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), R_OK) == 0) + data.entryFlags |= QFileSystemMetaData::UserReadPermission; + data.knownFlagsMask |= QFileSystemMetaData::UserReadPermission; + } + if (what & QFileSystemMetaData::UserWritePermission) { + if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), W_OK) == 0) + data.entryFlags |= QFileSystemMetaData::UserWritePermission; + data.knownFlagsMask |= QFileSystemMetaData::UserWritePermission; + } + } + + return data.hasFlags(what); +} + +static bool tryDriveUNCFallback(const QFileSystemEntry &fname, QFileSystemMetaData &data) +{ + bool entryExists = false; + DWORD fileAttrib = 0; +#if !defined(Q_OS_WINCE) + if (fname.isDriveRoot()) { + // a valid drive ?? + DWORD drivesBitmask = ::GetLogicalDrives(); + int drivebit = 1 << (fname.filePath().at(0).toUpper().unicode() - QLatin1Char('A').unicode()); + if (drivesBitmask & drivebit) { + fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM; + entryExists = true; + } + } else { +#endif + const QString &path = fname.nativeFilePath(); + bool is_dir = false; + if (path.startsWith(QLatin1String("\\\\?\\UNC"))) { + // UNC - stat doesn't work for all cases (Windows bug) + int s = path.indexOf(path.at(0),7); + if (s > 0) { + // "\\?\UNC\server\..." + s = path.indexOf(path.at(0),s+1); + if (s > 0) { + // "\\?\UNC\server\share\..." + if (s == path.size() - 1) { + // "\\?\UNC\server\share\" + is_dir = true; + } else { + // "\\?\UNC\server\share\notfound" + } + } else { + // "\\?\UNC\server\share" + is_dir = true; + } + } else { + // "\\?\UNC\server" + is_dir = true; + } + } + if (is_dir && uncShareExists(path)) { + // looks like a UNC dir, is a dir. + fileAttrib = FILE_ATTRIBUTE_DIRECTORY; + entryExists = true; + } +#if !defined(Q_OS_WINCE) + } +#endif + if (entryExists) + data.fillFromFileAttribute(fileAttrib); + return entryExists; +} + +static bool tryFindFallback(const QFileSystemEntry &fname, QFileSystemMetaData &data) +{ + bool filledData = false; + // This assumes the last call to a Windows API failed. + int errorCode = GetLastError(); + if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) { + WIN32_FIND_DATA findData; + if (getFindData(fname.nativeFilePath(), findData) + && findData.dwFileAttributes != INVALID_FILE_ATTRIBUTES) { + data.fillFromFindData(findData, true, fname.isDriveRoot()); + filledData = true; + } + } + return filledData; +} + +#if !defined(Q_OS_WINCE) +//static +bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what) +{ + HANDLE fHandle = (HANDLE)_get_osfhandle(fd); + if (fHandle != INVALID_HANDLE_VALUE) { + return fillMetaData(fHandle, data, what); + } + return false; +} +#endif + +//static +bool QFileSystemEngine::fillMetaData(HANDLE fHandle, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what) +{ + data.entryFlags &= ~what; + clearWinStatData(data); + BY_HANDLE_FILE_INFORMATION fileInfo; + UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); + if (GetFileInformationByHandle(fHandle , &fileInfo)) { + data.fillFromFindInfo(fileInfo); + } + SetErrorMode(oldmode); + return data.hasFlags(what); +} + +static bool isDirPath(const QString &dirPath, bool *existed); + +//static +bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what) +{ + what |= QFileSystemMetaData::WinLnkType | QFileSystemMetaData::WinStatFlags; + data.entryFlags &= ~what; + + QFileSystemEntry fname; + data.knownFlagsMask |= QFileSystemMetaData::WinLnkType; + // Check for ".lnk": Directories named ".lnk" should be skipped, corrupted + // link files should still be detected as links. + const QString origFilePath = entry.filePath(); + if (origFilePath.endsWith(QLatin1String(".lnk")) && !isDirPath(origFilePath, 0)) { + data.entryFlags |= QFileSystemMetaData::WinLnkType; + fname = QFileSystemEntry(readLink(entry)); + } else { + fname = entry; + } + + if (fname.isEmpty()) { + data.knownFlagsMask |= what; + clearWinStatData(data); + return false; + } + + if (what & QFileSystemMetaData::WinStatFlags) { + UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); + clearWinStatData(data); + WIN32_FIND_DATA findData; + // The memory structure for WIN32_FIND_DATA is same as WIN32_FILE_ATTRIBUTE_DATA + // for all members used by fillFindData(). + bool ok = ::GetFileAttributesEx((wchar_t*)fname.nativeFilePath().utf16(), GetFileExInfoStandard, + reinterpret_cast(&findData)); + if (ok) { + data.fillFromFindData(findData, false, fname.isDriveRoot()); + } else { + if (!tryFindFallback(fname, data)) + if (!tryDriveUNCFallback(fname, data)) { + SetErrorMode(oldmode); + return false; + } + } + SetErrorMode(oldmode); + } + + if (what & QFileSystemMetaData::Permissions) + fillPermissions(fname, data, what); + if ((what & QFileSystemMetaData::LinkType) + && data.missingFlags(QFileSystemMetaData::LinkType)) { + data.knownFlagsMask |= QFileSystemMetaData::LinkType; + if (data.fileAttribute_ & FILE_ATTRIBUTE_REPARSE_POINT) { + WIN32_FIND_DATA findData; + if (getFindData(fname.nativeFilePath(), findData)) + data.fillFromFindData(findData, true); + } + } + data.knownFlagsMask |= what; + return data.hasFlags(what); +} + +static inline bool mkDir(const QString &path, DWORD *lastError = 0) +{ + if (lastError) + *lastError = 0; +#if defined(Q_OS_WINCE) + // Unfortunately CreateDirectory returns true for paths longer than + // 256, but does not create a directory. It starts to fail, when + // path length > MAX_PATH, which is 260 usually on CE. + // This only happens on a Windows Mobile device. Windows CE seems + // not to be affected by this. + static int platformId = 0; + if (platformId == 0) { + wchar_t platformString[64]; + if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(platformString)/sizeof(*platformString),platformString,0)) { + if (0 == wcscmp(platformString, L"PocketPC") || 0 == wcscmp(platformString, L"Smartphone")) + platformId = 1; + else + platformId = 2; + } + } + if (platformId == 1 && QFSFileEnginePrivate::longFileName(path).size() > 256) + return false; +#endif + const QString longPath = QFSFileEnginePrivate::longFileName(path); + const bool result = ::CreateDirectory((wchar_t*)longPath.utf16(), 0); + if (lastError) // Capture lastError before any QString is freed since custom allocators might change it. + *lastError = GetLastError(); + return result; +} + +static inline bool rmDir(const QString &path) +{ + return ::RemoveDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16()); +} + +static bool isDirPath(const QString &dirPath, bool *existed) +{ + QString path = dirPath; + if (path.length() == 2 && path.at(1) == QLatin1Char(':')) + path += QLatin1Char('\\'); + + DWORD fileAttrib = ::GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16()); + if (fileAttrib == INVALID_FILE_ATTRIBUTES) { + int errorCode = GetLastError(); + if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) { + WIN32_FIND_DATA findData; + if (getFindData(QFSFileEnginePrivate::longFileName(path), findData)) + fileAttrib = findData.dwFileAttributes; + } + } + + if (existed) + *existed = fileAttrib != INVALID_FILE_ATTRIBUTES; + + if (fileAttrib == INVALID_FILE_ATTRIBUTES) + return false; + + return fileAttrib & FILE_ATTRIBUTE_DIRECTORY; +} + +//static +bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents) +{ + QString dirName = entry.filePath(); + if (createParents) { + dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName)); + // We spefically search for / so \ would break it.. + int oldslash = -1; + if (dirName.startsWith(QLatin1String("\\\\"))) { + // Don't try to create the root path of a UNC path; + // CreateDirectory() will just return ERROR_INVALID_NAME. + for (int i = 0; i < dirName.size(); ++i) { + if (dirName.at(i) != QDir::separator()) { + oldslash = i; + break; + } + } + if (oldslash != -1) + oldslash = dirName.indexOf(QDir::separator(), oldslash); + } else if (dirName.size() > 2 + && dirName.at(1) == QLatin1Char(':')) { + // Don't try to call mkdir with just a drive letter + oldslash = 2; + } + for (int slash=0; slash != -1; oldslash = slash) { + slash = dirName.indexOf(QDir::separator(), oldslash+1); + if (slash == -1) { + if (oldslash == dirName.length()) + break; + slash = dirName.length(); + } + if (slash) { + DWORD lastError; + QString chunk = dirName.left(slash); + if (!mkDir(chunk, &lastError)) { + if (lastError == ERROR_ALREADY_EXISTS || lastError == ERROR_ACCESS_DENIED) { + bool existed = false; + if (isDirPath(chunk, &existed) && existed) + continue; + } + return false; + } + } + } + return true; + } + return mkDir(entry.filePath()); +} + +//static +bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents) +{ + QString dirName = entry.filePath(); + if (removeEmptyParents) { + dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName)); + for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) { + QString chunk = dirName.left(slash); + if (chunk.length() == 2 && chunk.at(0).isLetter() && chunk.at(1) == QLatin1Char(':')) + break; + if (!isDirPath(chunk, 0)) + return false; + if (!rmDir(chunk)) + return oldslash != 0; + slash = dirName.lastIndexOf(QDir::separator(), oldslash-1); + } + return true; + } + return rmDir(entry.filePath()); +} + +//static +QString QFileSystemEngine::rootPath() +{ +#if defined(Q_OS_WINCE) + QString ret = QLatin1String("/"); +#else + QString ret = QString::fromLatin1(qgetenv("SystemDrive").constData()); + if (ret.isEmpty()) + ret = QLatin1String("c:"); + ret.append(QLatin1Char('/')); +#endif + return ret; +} + +//static +QString QFileSystemEngine::homePath() +{ + QString ret; +#if !defined(QT_NO_LIBRARY) + resolveLibs(); + if (ptrGetUserProfileDirectoryW) { + HANDLE hnd = ::GetCurrentProcess(); + HANDLE token = 0; + BOOL ok = ::OpenProcessToken(hnd, TOKEN_QUERY, &token); + if (ok) { + DWORD dwBufferSize = 0; + // First call, to determine size of the strings (with '\0'). + ok = ptrGetUserProfileDirectoryW(token, NULL, &dwBufferSize); + if (!ok && dwBufferSize != 0) { // We got the required buffer size + wchar_t *userDirectory = new wchar_t[dwBufferSize]; + // Second call, now we can fill the allocated buffer. + ok = ptrGetUserProfileDirectoryW(token, userDirectory, &dwBufferSize); + if (ok) + ret = QString::fromWCharArray(userDirectory); + delete [] userDirectory; + } + ::CloseHandle(token); + } + } +#endif + if (ret.isEmpty() || !QFile::exists(ret)) { + ret = QString::fromLocal8Bit(qgetenv("USERPROFILE").constData()); + if (ret.isEmpty() || !QFile::exists(ret)) { + ret = QString::fromLocal8Bit(qgetenv("HOMEDRIVE").constData()) + + QString::fromLocal8Bit(qgetenv("HOMEPATH").constData()); + if (ret.isEmpty() || !QFile::exists(ret)) { + ret = QString::fromLocal8Bit(qgetenv("HOME").constData()); + if (ret.isEmpty() || !QFile::exists(ret)) { +#if defined(Q_OS_WINCE) + ret = QLatin1String("\\My Documents"); + if (!QFile::exists(ret)) +#endif + ret = rootPath(); + } + } + } + } + return QDir::fromNativeSeparators(ret); +} + +QString QFileSystemEngine::tempPath() +{ + QString ret; + wchar_t tempPath[MAX_PATH]; + const DWORD len = GetTempPath(MAX_PATH, tempPath); +#ifdef Q_OS_WINCE + if (len) + ret = QString::fromWCharArray(tempPath, len); +#else + if (len) { // GetTempPath() can return short names, expand. + wchar_t longTempPath[MAX_PATH]; + const DWORD longLen = GetLongPathName(tempPath, longTempPath, MAX_PATH); + ret = longLen && longLen < MAX_PATH ? + QString::fromWCharArray(longTempPath, longLen) : + QString::fromWCharArray(tempPath, len); + } +#endif + if (!ret.isEmpty()) { + while (ret.endsWith(QLatin1Char('\\'))) + ret.chop(1); + ret = QDir::fromNativeSeparators(ret); + } + if (ret.isEmpty()) { +#if !defined(Q_OS_WINCE) + ret = QLatin1String("C:/tmp"); +#else + ret = QLatin1String("/Temp"); +#endif + } else if (ret.length() >= 2 && ret[1] == QLatin1Char(':')) + ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters. + return ret; +} + +bool QFileSystemEngine::setCurrentPath(const QFileSystemEntry &entry) +{ + QFileSystemMetaData meta; + fillMetaData(entry, meta, QFileSystemMetaData::ExistsAttribute | QFileSystemMetaData::DirectoryType); + if(!(meta.exists() && meta.isDirectory())) + return false; + +#if !defined(Q_OS_WINCE) + //TODO: this should really be using nativeFilePath(), but that returns a path in long format \\?\c:\foo + //which causes many problems later on when it's returned through currentPath() + return ::SetCurrentDirectory(reinterpret_cast(QDir::toNativeSeparators(entry.filePath()).utf16())) != 0; +#else + qfsPrivateCurrentDir = entry.filePath(); + return true; +#endif +} + +QFileSystemEntry QFileSystemEngine::currentPath() +{ + QString ret; +#if !defined(Q_OS_WINCE) + DWORD size = 0; + wchar_t currentName[PATH_MAX]; + size = ::GetCurrentDirectory(PATH_MAX, currentName); + if (size != 0) { + if (size > PATH_MAX) { + wchar_t *newCurrentName = new wchar_t[size]; + if (::GetCurrentDirectory(PATH_MAX, newCurrentName) != 0) + ret = QString::fromWCharArray(newCurrentName, size); + delete [] newCurrentName; + } else { + ret = QString::fromWCharArray(currentName, size); + } + } + if (ret.length() >= 2 && ret[1] == QLatin1Char(':')) + ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters. +#else + //TODO - a race condition exists when using currentPath / setCurrentPath from multiple threads + if (qfsPrivateCurrentDir.isEmpty()) + qfsPrivateCurrentDir = QCoreApplication::applicationDirPath(); + + ret = qfsPrivateCurrentDir; +#endif + return QFileSystemEntry(ret, QFileSystemEntry::FromNativePath()); +} + +//static +bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + Q_ASSERT(false); + Q_UNUSED(source) + Q_UNUSED(target) + Q_UNUSED(error) + + return false; // TODO implement; - code needs to be moved from qfsfileengine_win.cpp +} + +//static +bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + bool ret = ::CopyFile((wchar_t*)source.nativeFilePath().utf16(), + (wchar_t*)target.nativeFilePath().utf16(), true) != 0; + if(!ret) + error = QSystemError(::GetLastError(), QSystemError::NativeError); + return ret; +} + +//static +bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + bool ret = ::MoveFile((wchar_t*)source.nativeFilePath().utf16(), + (wchar_t*)target.nativeFilePath().utf16()) != 0; + if(!ret) + error = QSystemError(::GetLastError(), QSystemError::NativeError); + return ret; +} + +//static +bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error) +{ + bool ret = ::DeleteFile((wchar_t*)entry.nativeFilePath().utf16()) != 0; + if(!ret) + error = QSystemError(::GetLastError(), QSystemError::NativeError); + return ret; +} + +//static +bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, + QFileSystemMetaData *data) +{ + Q_UNUSED(data); + int mode = 0; + + if (permissions & QFile::ReadOwner || permissions & QFile::ReadUser + || permissions & QFile::ReadGroup || permissions & QFile::ReadOther) + mode |= _S_IREAD; + if (permissions & QFile::WriteOwner || permissions & QFile::WriteUser + || permissions & QFile::WriteGroup || permissions & QFile::WriteOther) + mode |= _S_IWRITE; + + if (mode == 0) // not supported + return false; + + bool ret = (::_wchmod((wchar_t*)entry.nativeFilePath().utf16(), mode) == 0); + if(!ret) + error = QSystemError(errno, QSystemError::StandardLibraryError); + return ret; +} + +static inline QDateTime fileTimeToQDateTime(const FILETIME *time) +{ + QDateTime ret; + +#if defined(Q_OS_WINCE) + SYSTEMTIME systime; + FILETIME ftime; + systime.wYear = 1970; + systime.wMonth = 1; + systime.wDay = 1; + systime.wHour = 0; + systime.wMinute = 0; + systime.wSecond = 0; + systime.wMilliseconds = 0; + systime.wDayOfWeek = 4; + SystemTimeToFileTime(&systime, &ftime); + unsigned __int64 acttime = (unsigned __int64)time->dwHighDateTime << 32 | time->dwLowDateTime; + FileTimeToSystemTime(time, &systime); + unsigned __int64 time1970 = (unsigned __int64)ftime.dwHighDateTime << 32 | ftime.dwLowDateTime; + unsigned __int64 difftime = acttime - time1970; + difftime /= 10000000; + ret.setTime_t((unsigned int)difftime); +#else + SYSTEMTIME sTime, lTime; + FileTimeToSystemTime(time, &sTime); + SystemTimeToTzSpecificLocalTime(0, &sTime, &lTime); + ret.setDate(QDate(lTime.wYear, lTime.wMonth, lTime.wDay)); + ret.setTime(QTime(lTime.wHour, lTime.wMinute, lTime.wSecond, lTime.wMilliseconds)); +#endif + + return ret; +} + +QDateTime QFileSystemMetaData::creationTime() const +{ + return fileTimeToQDateTime(&creationTime_); +} +QDateTime QFileSystemMetaData::modificationTime() const +{ + return fileTimeToQDateTime(&lastWriteTime_); +} +QDateTime QFileSystemMetaData::accessTime() const +{ + return fileTimeToQDateTime(&lastAccessTime_); +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qfilesystementry.cpp b/src/app/core/backport/qfilesystementry.cpp new file mode 100644 index 000000000..d39a44f38 --- /dev/null +++ b/src/app/core/backport/qfilesystementry.cpp @@ -0,0 +1,388 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilesystementry_p.h" + +#include +#include +#include "qfsfileengine_p.h" +#ifdef Q_OS_WIN +#include +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifdef Q_OS_WIN +static bool isUncRoot(const QString &server) +{ + QString localPath = QDir::toNativeSeparators(server); + if (!localPath.startsWith(QLatin1String("\\\\"))) + return false; + + int idx = localPath.indexOf(QLatin1Char('\\'), 2); + if (idx == -1 || idx + 1 == localPath.length()) + return true; + + localPath = localPath.right(localPath.length() - idx - 1).trimmed(); + return localPath.isEmpty(); +} + +static inline QString fixIfRelativeUncPath(const QString &path) +{ + QString currentPath = QDir::currentPath(); + if (currentPath.startsWith(QLatin1String("//"))) + return currentPath % QChar(QLatin1Char('/')) % path; + return path; +} +#endif + +QFileSystemEntry::QFileSystemEntry() + : m_lastSeparator(-1), + m_firstDotInFileName(-1), + m_lastDotInFileName(-1) +{ +} + +/*! + \internal + Use this constructor when the path is supplied by user code, as it may contain a mix + of '/' and the native separator. + */ +QFileSystemEntry::QFileSystemEntry(const QString &filePath) + : m_filePath(QDir::fromNativeSeparators(filePath)), + m_lastSeparator(-2), + m_firstDotInFileName(-2), + m_lastDotInFileName(0) +{ +} + +/*! + \internal + Use this constructor when the path is guaranteed to be in internal format, i.e. all + directory separators are '/' and not the native separator. + */ +QFileSystemEntry::QFileSystemEntry(const QString &filePath, FromInternalPath /* dummy */) + : m_filePath(filePath), + m_lastSeparator(-2), + m_firstDotInFileName(-2), + m_lastDotInFileName(0) +{ +} + +/*! + \internal + Use this constructor when the path comes from a native API + */ +QFileSystemEntry::QFileSystemEntry(const NativePath &nativeFilePath, FromNativePath /* dummy */) + : m_nativeFilePath(nativeFilePath), + m_lastSeparator(-2), + m_firstDotInFileName(-2), + m_lastDotInFileName(0) +{ +} + +QFileSystemEntry::QFileSystemEntry(const QString &filePath, const NativePath &nativeFilePath) + : m_filePath(QDir::fromNativeSeparators(filePath)), + m_nativeFilePath(nativeFilePath), + m_lastSeparator(-2), + m_firstDotInFileName(-2), + m_lastDotInFileName(0) +{ +} + +QString QFileSystemEntry::filePath() const +{ + resolveFilePath(); + return m_filePath; +} + +QFileSystemEntry::NativePath QFileSystemEntry::nativeFilePath() const +{ + resolveNativeFilePath(); + return m_nativeFilePath; +} + +void QFileSystemEntry::resolveFilePath() const +{ + if (m_filePath.isEmpty() && !m_nativeFilePath.isEmpty()) { +#if defined(QFILESYSTEMENTRY_NATIVE_PATH_IS_UTF16) + m_filePath = QDir::fromNativeSeparators(m_nativeFilePath); +#ifdef Q_OS_WIN + if (m_filePath.startsWith(QLatin1String("//?/UNC/"))) + m_filePath = m_filePath.remove(2,6); + if (m_filePath.startsWith(QLatin1String("//?/"))) + m_filePath = m_filePath.remove(0,4); +#endif +#else + m_filePath = QDir::fromNativeSeparators(QFile::decodeName(m_nativeFilePath)); +#endif + } +} + +void QFileSystemEntry::resolveNativeFilePath() const +{ + if (!m_filePath.isEmpty() && m_nativeFilePath.isEmpty()) { +#ifdef Q_OS_WIN + QString filePath = m_filePath; + if (isRelative()) + filePath = fixIfRelativeUncPath(m_filePath); + m_nativeFilePath = QFSFileEnginePrivate::longFileName(QDir::toNativeSeparators(filePath)); +#elif defined(QFILESYSTEMENTRY_NATIVE_PATH_IS_UTF16) + m_nativeFilePath = QDir::toNativeSeparators(m_filePath); +#else + m_nativeFilePath = QFile::encodeName(QDir::toNativeSeparators(m_filePath)); +#endif + } +} + +QString QFileSystemEntry::fileName() const +{ + findLastSeparator(); +#if defined(Q_OS_WIN) + if (m_lastSeparator == -1 && m_filePath.length() >= 2 && m_filePath.at(1) == QLatin1Char(':')) + return m_filePath.mid(2); +#endif + return m_filePath.mid(m_lastSeparator + 1); +} + +QString QFileSystemEntry::path() const +{ + findLastSeparator(); + if (m_lastSeparator == -1) { +#if defined(Q_OS_WIN) + if (m_filePath.length() >= 2 && m_filePath.at(1) == QLatin1Char(':')) + return QFSFileEngine::currentPath(m_filePath.left(2)); +#endif + return QString(QLatin1Char('.')); + } + if (m_lastSeparator == 0) + return QString(QLatin1Char('/')); +#if defined(Q_OS_WIN) + if (m_lastSeparator == 2 && m_filePath.at(1) == QLatin1Char(':')) + return m_filePath.left(m_lastSeparator + 1); +#endif + return m_filePath.left(m_lastSeparator); +} + +QString QFileSystemEntry::baseName() const +{ + findFileNameSeparators(); + int length = -1; + if (m_firstDotInFileName >= 0) { + length = m_firstDotInFileName; + if (m_lastSeparator != -1) // avoid off by one + length--; + } +#if defined(Q_OS_WIN) + if (m_lastSeparator == -1 && m_filePath.length() >= 2 && m_filePath.at(1) == QLatin1Char(':')) + return m_filePath.mid(2, length - 2); +#endif + return m_filePath.mid(m_lastSeparator + 1, length); +} + +QString QFileSystemEntry::completeBaseName() const +{ + findFileNameSeparators(); + int length = -1; + if (m_firstDotInFileName >= 0) { + length = m_firstDotInFileName + m_lastDotInFileName; + if (m_lastSeparator != -1) // avoid off by one + length--; + } +#if defined(Q_OS_WIN) + if (m_lastSeparator == -1 && m_filePath.length() >= 2 && m_filePath.at(1) == QLatin1Char(':')) + return m_filePath.mid(2, length - 2); +#endif + return m_filePath.mid(m_lastSeparator + 1, length); +} + +QString QFileSystemEntry::suffix() const +{ + findFileNameSeparators(); + + if (m_lastDotInFileName == -1) + return QString(); + + return m_filePath.mid(qMax((qint16)0, m_lastSeparator) + m_firstDotInFileName + m_lastDotInFileName + 1); +} + +QString QFileSystemEntry::completeSuffix() const +{ + findFileNameSeparators(); + if (m_firstDotInFileName == -1) + return QString(); + + return m_filePath.mid(qMax((qint16)0, m_lastSeparator) + m_firstDotInFileName + 1); +} + +#if defined(Q_OS_WIN) +bool QFileSystemEntry::isRelative() const +{ + resolveFilePath(); + return (m_filePath.isEmpty() || (!m_filePath.isEmpty() && (m_filePath[0].unicode() != '/') + && (!(m_filePath.length() >= 2 && m_filePath[1].unicode() == ':')))); +} + +bool QFileSystemEntry::isAbsolute() const +{ + resolveFilePath(); + return (!m_filePath.isEmpty() && ((m_filePath.length() >= 3 + && (m_filePath[0].isLetter() && m_filePath[1].unicode() == ':' && m_filePath[2].unicode() == '/')) + || (m_filePath.length() >= 2 && (m_filePath.at(0) == QLatin1Char('/') && m_filePath.at(1) == QLatin1Char('/'))) + )); +} +#else +bool QFileSystemEntry::isRelative() const +{ + return !isAbsolute(); +} + +bool QFileSystemEntry::isAbsolute() const +{ + resolveFilePath(); + return (!m_filePath.isEmpty() && (m_filePath[0].unicode() == '/')); +} +#endif + +#if defined(Q_OS_WIN) +bool QFileSystemEntry::isDriveRoot() const +{ + resolveFilePath(); + return (m_filePath.length() == 3 + && m_filePath.at(0).isLetter() && m_filePath.at(1) == QLatin1Char(':') + && m_filePath.at(2) == QLatin1Char('/')); +} +#endif + +bool QFileSystemEntry::isRoot() const +{ + resolveFilePath(); + if (m_filePath == QLatin1String("/") +#if defined(Q_OS_WIN) + || isDriveRoot() + || isUncRoot(m_filePath) +#endif + ) + return true; + + return false; +} + +bool QFileSystemEntry::isEmpty() const +{ + return m_filePath.isEmpty() && m_nativeFilePath.isEmpty(); +} + +// private methods + +void QFileSystemEntry::findLastSeparator() const +{ + if (m_lastSeparator == -2) { + resolveFilePath(); + m_lastSeparator = -1; + for (int i = m_filePath.size() - 1; i >= 0; --i) { + if (m_filePath[i].unicode() == '/') { + m_lastSeparator = i; + break; + } + } + } +} + +void QFileSystemEntry::findFileNameSeparators() const +{ + if (m_firstDotInFileName == -2) { + resolveFilePath(); + int firstDotInFileName = -1; + int lastDotInFileName = -1; + int lastSeparator = m_lastSeparator; + + int stop; + if (lastSeparator < 0) { + lastSeparator = -1; + stop = 0; + } else { + stop = lastSeparator; + } + + int i = m_filePath.size() - 1; + for (; i >= stop; --i) { + if (m_filePath[i].unicode() == '.') { + firstDotInFileName = lastDotInFileName = i; + break; + } else if (m_filePath[i].unicode() == '/') { + lastSeparator = i; + break; + } + } + + if (lastSeparator != i) { + for (--i; i >= stop; --i) { + if (m_filePath[i].unicode() == '.') + firstDotInFileName = i; + else if (m_filePath[i].unicode() == '/') { + lastSeparator = i; + break; + } + } + } + m_lastSeparator = lastSeparator; + m_firstDotInFileName = firstDotInFileName == -1 ? -1 : firstDotInFileName - qMax(0, lastSeparator); + if (lastDotInFileName == -1) + m_lastDotInFileName = -1; + else if (firstDotInFileName == lastDotInFileName) + m_lastDotInFileName = 0; + else + m_lastDotInFileName = lastDotInFileName - firstDotInFileName; + } +} + +bool QFileSystemEntry::isClean() const +{ + resolveFilePath(); + int dots = 0; + bool dotok = true; // checking for ".." or "." starts to relative paths + bool slashok = true; + for (QString::const_iterator iter = m_filePath.constBegin(); iter != m_filePath.constEnd(); iter++) { + if (*iter == QLatin1Char('/')) { + if (dots == 1 || dots == 2) + return false; // path contains "./" or "../" + if (!slashok) + return false; // path contains "//" + dots = 0; + dotok = true; + slashok = false; + } else if (dotok) { + slashok = true; + if (*iter == QLatin1Char('.')) { + dots++; + if (dots > 2) + dotok = false; + } else { + //path element contains a character other than '.', it's clean + dots = 0; + dotok = false; + } + } + } + return (dots != 1 && dots != 2); // clean if path doesn't end in . or .. +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qfilesystementry_p.h b/src/app/core/backport/qfilesystementry_p.h new file mode 100644 index 000000000..c1103fd05 --- /dev/null +++ b/src/app/core/backport/qfilesystementry_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMENTRY_P_H +#define QFILESYSTEMENTRY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#if defined(Q_OS_WIN) +#define QFILESYSTEMENTRY_NATIVE_PATH_IS_UTF16 +#endif + +class QFileSystemEntry +{ +public: + +#ifndef QFILESYSTEMENTRY_NATIVE_PATH_IS_UTF16 + typedef QByteArray NativePath; +#else + typedef QString NativePath; +#endif + struct FromNativePath{}; + struct FromInternalPath{}; + + QFileSystemEntry(); + explicit QFileSystemEntry(const QString &filePath); + + QFileSystemEntry(const QString &filePath, FromInternalPath dummy); + QFileSystemEntry(const NativePath &nativeFilePath, FromNativePath dummy); + QFileSystemEntry(const QString &filePath, const NativePath &nativeFilePath); + + QString filePath() const; + QString fileName() const; + QString path() const; + NativePath nativeFilePath() const; + QString baseName() const; + QString completeBaseName() const; + QString suffix() const; + QString completeSuffix() const; + bool isAbsolute() const; + bool isRelative() const; + bool isClean() const; + +#if defined(Q_OS_WIN) + bool isDriveRoot() const; +#endif + bool isRoot() const; + + bool isEmpty() const; + void clear() + { + *this = QFileSystemEntry(); + } + +private: + // creates the QString version out of the bytearray version + void resolveFilePath() const; + // creates the bytearray version out of the QString version + void resolveNativeFilePath() const; + // resolves the separator + void findLastSeparator() const; + // resolves the dots and the separator + void findFileNameSeparators() const; + + mutable QString m_filePath; // always has slashes as separator + mutable NativePath m_nativeFilePath; // native encoding and separators + + mutable qint16 m_lastSeparator; // index in m_filePath of last separator + mutable qint16 m_firstDotInFileName; // index after m_filePath for first dot (.) + mutable qint16 m_lastDotInFileName; // index after m_firstDotInFileName for last dot (.) +}; + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // include guard diff --git a/src/app/core/backport/qfilesystemiterator_p.h b/src/app/core/backport/qfilesystemiterator_p.h new file mode 100644 index 000000000..0a915f6af --- /dev/null +++ b/src/app/core/backport/qfilesystemiterator_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMITERATOR_P_H +#define QFILESYSTEMITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifndef QT_NO_FILESYSTEMITERATOR + +#include +#include +#include + +#include "qfilesystementry_p.h" +#include "qfilesystemmetadata_p.h" + +// Platform-specific headers +#if !defined(Q_OS_WIN) +#include +#endif + +class QFileSystemIterator +{ +public: + QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters, + const QStringList &nameFilters, QDirIterator::IteratorFlags flags + = QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); + ~QFileSystemIterator(); + + bool advance(QFileSystemEntry &fileEntry, QFileSystemMetaData &metaData); + +private: + QFileSystemEntry::NativePath nativePath; + + // Platform-specific data +#if defined(Q_OS_WIN) + QString dirPath; + HANDLE findFileHandle; + QStringList uncShares; + bool uncFallback; + int uncShareIndex; + bool onlyDirs; +#else + QT_DIR *dir; + QT_DIRENT *dirEntry; +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) + // for readdir_r + QScopedPointer mt_file; +#if defined(Q_OS_QNX) && defined(__EXT_QNX__READDIR_R) + // for _readdir_r + size_t direntSize; +#endif +#endif + int lastError; +#endif + + Q_DISABLE_COPY(QFileSystemIterator) +}; + +#endif // QT_NO_FILESYSTEMITERATOR + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // include guard diff --git a/src/app/core/backport/qfilesystemiterator_unix.cpp b/src/app/core/backport/qfilesystemiterator_unix.cpp new file mode 100644 index 000000000..f4218ccdf --- /dev/null +++ b/src/app/core/backport/qfilesystemiterator_unix.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qfilesystemiterator_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifndef QT_NO_FILESYSTEMITERATOR + +#include +#include + +QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters, + const QStringList &nameFilters, QDirIterator::IteratorFlags flags) + : nativePath(entry.nativeFilePath()) + , dir(0) + , dirEntry(0) +#if defined(Q_OS_QNX) && defined(__EXT_QNX__READDIR_R) + , direntSize(0) +#endif + , lastError(0) +{ + Q_UNUSED(filters) + Q_UNUSED(nameFilters) + Q_UNUSED(flags) + + if ((dir = QT_OPENDIR(nativePath.constData())) == 0) { + lastError = errno; + } else { + + if (!nativePath.endsWith('/')) + nativePath.append('/'); + +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) + // ### Race condition; we should use fpathconf and dirfd(). + size_t maxPathName = ::pathconf(nativePath.constData(), _PC_NAME_MAX); + if (maxPathName == size_t(-1)) + maxPathName = FILENAME_MAX; + maxPathName += sizeof(QT_DIRENT) + 1; + + QT_DIRENT *p = reinterpret_cast(::malloc(maxPathName)); + Q_CHECK_PTR(p); + + mt_file.reset(p); +#if defined(Q_OS_QNX) && defined(__EXT_QNX__READDIR_R) + direntSize = maxPathName; + + // Include extra stat information in the readdir() call (d_stat member of dirent_extra_stat). + // This is used in QFileSystemMetaData::fillFromDirEnt() to avoid extra stat() calls when iterating + // over directories + if (dircntl(dir, D_SETFLAG, D_FLAG_STAT) == -1) + lastError = errno; +#endif +#endif + } +} + +QFileSystemIterator::~QFileSystemIterator() +{ + if (dir) + QT_CLOSEDIR(dir); +} + +bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaData &metaData) +{ + if (!dir) + return false; + +#if defined(Q_OS_QNX) && defined(__EXT_QNX__READDIR_R) + lastError = _readdir_r(dir, mt_file.data(), &dirEntry, direntSize); + if (lastError) + return false; +#elif defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) + lastError = QT_READDIR_R(dir, mt_file.data(), &dirEntry); + if (lastError) + return false; +#else + // ### add local lock to prevent breaking reentrancy + dirEntry = QT_READDIR(dir); +#endif // _POSIX_THREAD_SAFE_FUNCTIONS + + if (dirEntry) { + fileEntry = QFileSystemEntry(nativePath + QByteArray(dirEntry->d_name), QFileSystemEntry::FromNativePath()); + metaData.fillFromDirEnt(*dirEntry); + return true; + } + + lastError = errno; + return false; +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QT_NO_FILESYSTEMITERATOR diff --git a/src/app/core/backport/qfilesystemiterator_win.cpp b/src/app/core/backport/qfilesystemiterator_win.cpp new file mode 100644 index 000000000..ca1585b52 --- /dev/null +++ b/src/app/core/backport/qfilesystemiterator_win.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if _WIN32_WINNT < 0x0500 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#include "qfilesystemiterator_p.h" +#include "qfilesystemengine_p.h" +#include "qplatformdefs.h" + +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +bool done = true; + +QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters, + const QStringList &nameFilters, QDirIterator::IteratorFlags flags) + : nativePath(entry.nativeFilePath()) + , dirPath(entry.filePath()) + , findFileHandle(INVALID_HANDLE_VALUE) + , uncFallback(false) + , uncShareIndex(0) + , onlyDirs(false) +{ + Q_UNUSED(nameFilters) + Q_UNUSED(flags) + if (nativePath.endsWith(QLatin1String(".lnk"))) { + QFileSystemMetaData metaData; + QFileSystemEntry link = QFileSystemEngine::getLinkTarget(entry, metaData); + nativePath = link.nativeFilePath(); + } + if (!nativePath.endsWith(QLatin1Char('\\'))) + nativePath.append(QLatin1Char('\\')); + nativePath.append(QLatin1Char('*')); + if (!dirPath.endsWith(QLatin1Char('/'))) + dirPath.append(QLatin1Char('/')); + if ((filters & (QDir::Dirs|QDir::Drives)) && (!(filters & (QDir::Files)))) + onlyDirs = true; +} + +QFileSystemIterator::~QFileSystemIterator() +{ + if (findFileHandle != INVALID_HANDLE_VALUE) + FindClose(findFileHandle); +} + +bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaData &metaData) +{ + bool haveData = false; + WIN32_FIND_DATA findData; + + if (findFileHandle == INVALID_HANDLE_VALUE && !uncFallback) { + haveData = true; + int infoLevel = 0 ; // FindExInfoStandard; + DWORD dwAdditionalFlags = 0; +#ifndef Q_OS_WINCE + if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) { + dwAdditionalFlags = 2; // FIND_FIRST_EX_LARGE_FETCH + infoLevel = 1 ; // FindExInfoBasic; + } +#endif + int searchOps = 0; // FindExSearchNameMatch + if (onlyDirs) + searchOps = 1 ; // FindExSearchLimitToDirectories + findFileHandle = FindFirstFileEx((const wchar_t *)nativePath.utf16(), FINDEX_INFO_LEVELS(infoLevel), &findData, + FINDEX_SEARCH_OPS(searchOps), 0, dwAdditionalFlags); + if (findFileHandle == INVALID_HANDLE_VALUE) { + if (nativePath.startsWith(QLatin1String("\\\\?\\UNC\\"))) { + QStringList parts = nativePath.split(QLatin1Char('\\'), QString::SkipEmptyParts); + if (parts.count() == 4 && QFileSystemEngine::uncListSharesOnServer( + QLatin1String("\\\\") + parts.at(2), &uncShares)) { + if (uncShares.isEmpty()) + return false; // No shares found in the server + else + uncFallback = true; + } + } + } + } + if (findFileHandle == INVALID_HANDLE_VALUE && !uncFallback) + return false; + // Retrieve the new file information. + if (!haveData) { + if (uncFallback) { + if (++uncShareIndex >= uncShares.count()) + return false; + } else { + if (!FindNextFile(findFileHandle, &findData)) + return false; + } + } + // Create the new file system entry & meta data. + if (uncFallback) { + fileEntry = QFileSystemEntry(dirPath + uncShares.at(uncShareIndex)); + metaData.fillFromFileAttribute(FILE_ATTRIBUTE_DIRECTORY); + return true; + } else { + QString fileName = QString::fromWCharArray(findData.cFileName); + fileEntry = QFileSystemEntry(dirPath + fileName); + metaData = QFileSystemMetaData(); + if (!fileName.endsWith(QLatin1String(".lnk"))) { + metaData.fillFromFindData(findData, true); + } + return true; + } + return false; +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qfilesystemmetadata_p.h b/src/app/core/backport/qfilesystemmetadata_p.h new file mode 100644 index 000000000..9aa896dec --- /dev/null +++ b/src/app/core/backport/qfilesystemmetadata_p.h @@ -0,0 +1,341 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMMETADATA_P_H +#define QFILESYSTEMMETADATA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qplatformdefs.h" +#include +#include +#include "qabstractfileengine_p.h" + +// Platform-specific includes +#ifdef Q_OS_WIN +# include +# ifndef IO_REPARSE_TAG_SYMLINK +# define IO_REPARSE_TAG_SYMLINK (0xA000000CL) +# endif +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +class QFileSystemEngine; + +class QFileSystemMetaData +{ +public: + QFileSystemMetaData() + : knownFlagsMask(0) + { + } + + enum MetaDataFlag { + // Permissions, overlaps with QFile::Permissions + OtherReadPermission = 0x00000004, OtherWritePermission = 0x00000002, OtherExecutePermission = 0x00000001, + GroupReadPermission = 0x00000040, GroupWritePermission = 0x00000020, GroupExecutePermission = 0x00000010, + UserReadPermission = 0x00000400, UserWritePermission = 0x00000200, UserExecutePermission = 0x00000100, + OwnerReadPermission = 0x00004000, OwnerWritePermission = 0x00002000, OwnerExecutePermission = 0x00001000, + + OtherPermissions = OtherReadPermission | OtherWritePermission | OtherExecutePermission, + GroupPermissions = GroupReadPermission | GroupWritePermission | GroupExecutePermission, + UserPermissions = UserReadPermission | UserWritePermission | UserExecutePermission, + OwnerPermissions = OwnerReadPermission | OwnerWritePermission | OwnerExecutePermission, + + ReadPermissions = OtherReadPermission | GroupReadPermission | UserReadPermission | OwnerReadPermission, + WritePermissions = OtherWritePermission | GroupWritePermission | UserWritePermission | OwnerWritePermission, + ExecutePermissions = OtherExecutePermission | GroupExecutePermission | UserExecutePermission | OwnerExecutePermission, + + Permissions = OtherPermissions | GroupPermissions | UserPermissions | OwnerPermissions, + + // Type + LinkType = 0x00010000, + FileType = 0x00020000, + DirectoryType = 0x00040000, +#if defined(Q_OS_MACX) + BundleType = 0x00080000, + AliasType = 0x08000000, +#else + BundleType = 0x0, + AliasType = 0x0, +#endif +#if defined(Q_OS_WIN) + WinLnkType = 0x08000000, // Note: Uses the same position for AliasType on Mac +#else + WinLnkType = 0x0, +#endif + SequentialType = 0x00800000, // Note: overlaps with QAbstractFileEngine::RootFlag + + LegacyLinkType = LinkType | AliasType | WinLnkType, + + Type = LinkType | FileType | DirectoryType | BundleType | SequentialType | AliasType, + + // Attributes + HiddenAttribute = 0x00100000, + SizeAttribute = 0x00200000, // Note: overlaps with QAbstractFileEngine::LocalDiskFlag + ExistsAttribute = 0x00400000, + + Attributes = HiddenAttribute | SizeAttribute | ExistsAttribute, + + // Times + CreationTime = 0x01000000, // Note: overlaps with QAbstractFileEngine::Refresh + ModificationTime = 0x02000000, + AccessTime = 0x04000000, + + Times = CreationTime | ModificationTime | AccessTime, + + // Owner IDs + UserId = 0x10000000, + GroupId = 0x20000000, + + OwnerIds = UserId | GroupId, + + PosixStatFlags = QFileSystemMetaData::OtherPermissions + | QFileSystemMetaData::GroupPermissions + | QFileSystemMetaData::OwnerPermissions + | QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::SequentialType + | QFileSystemMetaData::SizeAttribute + | QFileSystemMetaData::Times + | QFileSystemMetaData::OwnerIds, + +#if defined(Q_OS_WIN) + WinStatFlags = QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::HiddenAttribute + | QFileSystemMetaData::ExistsAttribute + | QFileSystemMetaData::SizeAttribute + | QFileSystemMetaData::Times, +#endif + + AllMetaDataFlags = 0xFFFFFFFF + + }; + Q_DECLARE_FLAGS(MetaDataFlags, MetaDataFlag) + + bool hasFlags(MetaDataFlags flags) const + { + return ((knownFlagsMask & flags) == flags); + } + + MetaDataFlags missingFlags(MetaDataFlags flags) + { + return flags & ~knownFlagsMask; + } + + void clear() + { + knownFlagsMask = 0; + } + + void clearFlags(MetaDataFlags flags = AllMetaDataFlags) + { + knownFlagsMask &= ~flags; + } + + bool exists() const { return (entryFlags & ExistsAttribute); } + + bool isLink() const { return (entryFlags & LinkType); } + bool isFile() const { return (entryFlags & FileType); } + bool isDirectory() const { return (entryFlags & DirectoryType); } + bool isBundle() const; + bool isAlias() const; + bool isLegacyLink() const { return (entryFlags & LegacyLinkType); } + bool isSequential() const { return (entryFlags & SequentialType); } + bool isHidden() const { return (entryFlags & HiddenAttribute); } +#if defined(Q_OS_WIN) + bool isLnkFile() const { return (entryFlags & WinLnkType); } +#else + bool isLnkFile() const { return false; } +#endif + + qint64 size() const { return size_; } + + QFile::Permissions permissions() const { return QFile::Permissions(Permissions & entryFlags); } + + QDateTime creationTime() const; + QDateTime modificationTime() const; + QDateTime accessTime() const; + + QDateTime fileTime(QAbstractFileEngine::FileTime time) const; + uint userId() const; + uint groupId() const; + uint ownerId(QAbstractFileEngine::FileOwner owner) const; + +#ifdef Q_OS_UNIX + void fillFromStatBuf(const QT_STATBUF &statBuffer); + void fillFromDirEnt(const QT_DIRENT &statBuffer); +#endif + +#if defined(Q_OS_WIN) + inline void fillFromFileAttribute(DWORD fileAttribute, bool isDriveRoot = false); + inline void fillFromFindData(WIN32_FIND_DATA &findData, bool setLinkType = false, bool isDriveRoot = false); + inline void fillFromFindInfo(BY_HANDLE_FILE_INFORMATION &fileInfo); +#endif +private: + friend class QFileSystemEngine; + + MetaDataFlags knownFlagsMask; + MetaDataFlags entryFlags; + + qint64 size_; + + // Platform-specific data goes here: +#if defined(Q_OS_WIN) + DWORD fileAttribute_; + FILETIME creationTime_; + FILETIME lastAccessTime_; + FILETIME lastWriteTime_; +#else + time_t creationTime_; + time_t modificationTime_; + time_t accessTime_; + + uint userId_; + uint groupId_; +#endif + +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QFileSystemMetaData::MetaDataFlags) + +#if defined(Q_OS_MACX) +inline bool QFileSystemMetaData::isBundle() const { return (entryFlags & BundleType); } +inline bool QFileSystemMetaData::isAlias() const { return (entryFlags & AliasType); } +#else +inline bool QFileSystemMetaData::isBundle() const { return false; } +inline bool QFileSystemMetaData::isAlias() const { return false; } +#endif + +#if defined(Q_OS_UNIX) || defined (Q_OS_WIN) +inline QDateTime QFileSystemMetaData::fileTime(QAbstractFileEngine::FileTime time) const +{ + switch (time) { + case QAbstractFileEngine::ModificationTime: + return modificationTime(); + + case QAbstractFileEngine::AccessTime: + return accessTime(); + + case QAbstractFileEngine::CreationTime: + return creationTime(); + } + + return QDateTime(); +} +#endif + +#if defined(Q_OS_UNIX) +inline QDateTime QFileSystemMetaData::creationTime() const { return QDateTime::fromTime_t(creationTime_); } +inline QDateTime QFileSystemMetaData::modificationTime() const { return QDateTime::fromTime_t(modificationTime_); } +inline QDateTime QFileSystemMetaData::accessTime() const { return QDateTime::fromTime_t(accessTime_); } + +inline uint QFileSystemMetaData::userId() const { return userId_; } +inline uint QFileSystemMetaData::groupId() const { return groupId_; } + +inline uint QFileSystemMetaData::ownerId(QAbstractFileEngine::FileOwner owner) const +{ + if (owner == QAbstractFileEngine::OwnerUser) + return userId(); + else + return groupId(); +} +#endif + +#if defined(Q_OS_WIN) +inline uint QFileSystemMetaData::userId() const { return (uint) -2; } +inline uint QFileSystemMetaData::groupId() const { return (uint) -2; } +inline uint QFileSystemMetaData::ownerId(QAbstractFileEngine::FileOwner owner) const +{ + if (owner == QAbstractFileEngine::OwnerUser) + return userId(); + else + return groupId(); +} + +inline void QFileSystemMetaData::fillFromFileAttribute(DWORD fileAttribute,bool isDriveRoot) +{ + fileAttribute_ = fileAttribute; + // Ignore the hidden attribute for drives. + if (!isDriveRoot && (fileAttribute_ & FILE_ATTRIBUTE_HIDDEN)) + entryFlags |= HiddenAttribute; + entryFlags |= ((fileAttribute & FILE_ATTRIBUTE_DIRECTORY) ? DirectoryType: FileType); + entryFlags |= ExistsAttribute; + knownFlagsMask |= FileType | DirectoryType | HiddenAttribute | ExistsAttribute; +} + +inline void QFileSystemMetaData::fillFromFindData(WIN32_FIND_DATA &findData, bool setLinkType, bool isDriveRoot) +{ + fillFromFileAttribute(findData.dwFileAttributes, isDriveRoot); + creationTime_ = findData.ftCreationTime; + lastAccessTime_ = findData.ftLastAccessTime; + lastWriteTime_ = findData.ftLastWriteTime; + if (fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY) { + size_ = 0; + } else { + size_ = findData.nFileSizeHigh; + size_ <<= 32; + size_ += findData.nFileSizeLow; + } + knownFlagsMask |= Times | SizeAttribute; + if (setLinkType) { + knownFlagsMask |= LinkType; + entryFlags &= ~LinkType; +#if !defined(Q_OS_WINCE) + if ((fileAttribute_ & FILE_ATTRIBUTE_REPARSE_POINT) + && (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) { + entryFlags |= LinkType; + } +#endif + + } +} + +inline void QFileSystemMetaData::fillFromFindInfo(BY_HANDLE_FILE_INFORMATION &fileInfo) +{ + fillFromFileAttribute(fileInfo.dwFileAttributes); + creationTime_ = fileInfo.ftCreationTime; + lastAccessTime_ = fileInfo.ftLastAccessTime; + lastWriteTime_ = fileInfo.ftLastWriteTime; + if (fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY) { + size_ = 0; + } else { + size_ = fileInfo.nFileSizeHigh; + size_ <<= 32; + size_ += fileInfo.nFileSizeLow; + } + knownFlagsMask |= Times | SizeAttribute; +} +#endif + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // include guard diff --git a/src/app/core/backport/qfsfileengine.cpp b/src/app/core/backport/qfsfileengine.cpp new file mode 100644 index 000000000..9dd4deda3 --- /dev/null +++ b/src/app/core/backport/qfsfileengine.cpp @@ -0,0 +1,942 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfsfileengine_p.h" +#include "qfsfileengine_iterator_p.h" +#include "qfilesystemengine_p.h" +#include +#include +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifndef QT_NO_FSFILEENGINE + +#if !defined(Q_OS_WINCE) +#include +#endif +#if defined(Q_OS_UNIX) +#include "qcore_unix_p.h" +#endif +#include +#include +#if defined(Q_OS_MAC) +# include "qcore_mac_p.h" +#endif + +#ifdef Q_OS_WIN +# ifndef S_ISREG +# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) +# endif +# ifndef S_ISCHR +# define S_ISCHR(x) (((x) & S_IFMT) == S_IFCHR) +# endif +# ifndef S_ISFIFO +# define S_ISFIFO(x) false +# endif +# ifndef S_ISSOCK +# define S_ISSOCK(x) false +# endif +# ifndef INVALID_FILE_ATTRIBUTES +# define INVALID_FILE_ATTRIBUTES (DWORD (-1)) +# endif +#endif + +/*! \class QFSFileEngine + \inmodule QtCore + \brief The QFSFileEngine class implements Qt's default file engine. + \since 4.1 + \internal + + This class is part of the file engine framework in Qt. If you only want to + access files or directories, use QFile, QFileInfo or QDir instead. + + QFSFileEngine is the default file engine for accessing regular files. It + is provided for convenience; by subclassing this class, you can alter its + behavior slightly, without having to write a complete QAbstractFileEngine + subclass. To install your custom file engine, you must also subclass + QAbstractFileEngineHandler and create an instance of your handler. + + It can also be useful to create a QFSFileEngine object directly if you + need to use the local file system inside QAbstractFileEngine::create(), in + order to avoid recursion (as higher-level classes tend to call + QAbstractFileEngine::create()). +*/ + +//**************** QFSFileEnginePrivate +QFSFileEnginePrivate::QFSFileEnginePrivate() : QAbstractFileEnginePrivate() +{ + init(); +} + +/*! + \internal +*/ +void QFSFileEnginePrivate::init() +{ + is_sequential = 0; + tried_stat = 0; +#if !defined(Q_OS_WINCE) + need_lstat = 1; + is_link = 0; +#endif + openMode = QIODevice::NotOpen; + fd = -1; + fh = 0; + lastIOCommand = IOFlushCommand; + lastFlushFailed = false; + closeFileHandle = false; +#ifdef Q_OS_WIN + fileAttrib = INVALID_FILE_ATTRIBUTES; + fileHandle = INVALID_HANDLE_VALUE; + mapHandle = NULL; +#ifndef Q_OS_WINCE + cachedFd = -1; +#endif +#endif +} + +/*! + Constructs a QFSFileEngine for the file name \a file. +*/ +QFSFileEngine::QFSFileEngine(const QString &file) + : QAbstractFileEngine(*new QFSFileEnginePrivate) +{ + Q_D(QFSFileEngine); + d->fileEntry = QFileSystemEntry(file); +} + +/*! + Constructs a QFSFileEngine. +*/ +QFSFileEngine::QFSFileEngine() : QAbstractFileEngine(*new QFSFileEnginePrivate) +{ +} + +/*! + \internal +*/ +QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd) + : QAbstractFileEngine(dd) +{ +} + +/*! + Destructs the QFSFileEngine. +*/ +QFSFileEngine::~QFSFileEngine() +{ + Q_D(QFSFileEngine); + if (d->closeFileHandle) { + if (d->fh) { + int ret; + do { + ret = fclose(d->fh); + } while (ret == EOF && errno == EINTR); + } else if (d->fd != -1) { + int ret; + do { + ret = QT_CLOSE(d->fd); + } while (ret == -1 && errno == EINTR); + } + } + QList keys = d->maps.keys(); + for (int i = 0; i < keys.count(); ++i) + unmap(keys.at(i)); +} + +/*! + \reimp +*/ +void QFSFileEngine::setFileName(const QString &file) +{ + Q_D(QFSFileEngine); + d->init(); + d->fileEntry = QFileSystemEntry(file); +} + +/*! + \reimp +*/ +bool QFSFileEngine::open(QIODevice::OpenMode openMode) +{ + Q_D(QFSFileEngine); + if (d->fileEntry.isEmpty()) { + qWarning("QFSFileEngine::open: No file name specified"); + setError(QFile::OpenError, QLatin1String("No file name specified")); + return false; + } + + // 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->tried_stat = 0; + d->fh = 0; + d->fd = -1; + + return d->nativeOpen(openMode); +} + +/*! + Opens the file handle \a fh in \a openMode mode. Returns \c true on + success; otherwise returns \c false. +*/ +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. + 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->tried_stat = 0; + d->fd = -1; + + return d->openFh(openMode, fh); +} + +/*! + Opens the file handle \a fh using the open mode \a flags. +*/ +bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh) +{ + Q_Q(QFSFileEngine); + this->fh = fh; + fd = -1; + + // Seek to the end when in Append mode. + if (openMode & QIODevice::Append) { + int ret; + do { + ret = QT_FSEEK(fh, 0, SEEK_END); + } while (ret != 0 && errno == EINTR); + + if (ret != 0) { + q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError, + qt_error_string(int(errno))); + + this->openMode = QIODevice::NotOpen; + this->fh = 0; + + return false; + } + } + + return true; +} + +/*! + Opens the file descriptor \a fd in \a openMode mode. Returns \c true + on success; otherwise returns \c false. +*/ +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. + 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; + + return d->openFd(openMode, fd); +} + + +/*! + Opens the file descriptor \a fd to the file engine, using the open mode \a + flags. +*/ +bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd) +{ + Q_Q(QFSFileEngine); + this->fd = fd; + fh = 0; + + // Seek to the end when in Append mode. + if (openMode & 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))); + + this->openMode = QIODevice::NotOpen; + this->fd = -1; + + return false; + } + } + + return true; +} + +/*! + \reimp +*/ +bool QFSFileEngine::close() +{ + Q_D(QFSFileEngine); + d->openMode = QIODevice::NotOpen; + return d->nativeClose(); +} + +/*! + \internal +*/ +bool QFSFileEnginePrivate::closeFdFh() +{ + Q_Q(QFSFileEngine); + if (fd == -1 && !fh) + return false; + + // Flush the file if it's buffered, and if the last flush didn't fail. + bool flushed = !fh || (!lastFlushFailed && q->flush()); + bool closed = true; + tried_stat = 0; + + // Close the file if we created the handle. + if (closeFileHandle) { + int ret; + do { + if (fh) { + // Close buffered file. + ret = fclose(fh) != 0 ? -1 : 0; + } else { + // Close unbuffered file. + ret = QT_CLOSE(fd); + } + } while (ret == -1 && errno == EINTR); + + // We must reset these guys regardless; calling close again after a + // failed close causes crashes on some systems. + fh = 0; + fd = -1; + closed = (ret == 0); + } + + // Report errors. + if (!flushed || !closed) { + if (flushed) { + // If not flushed, we want the flush error to fall through. + q->setError(QFile::UnspecifiedError, qt_error_string(errno)); + } + return false; + } + + return true; +} + +/*! + \reimp +*/ +bool QFSFileEngine::flush() +{ + Q_D(QFSFileEngine); + if ((d->openMode & QIODevice::WriteOnly) == 0) { + // Nothing in the write buffers, so flush succeeds in doing + // nothing. + return true; + } + return d->nativeFlush(); +} + +/*! + \reimp +*/ +bool QFSFileEngine::syncToDisk() +{ + Q_D(QFSFileEngine); + if ((d->openMode & QIODevice::WriteOnly) == 0) + return true; + return d->nativeSyncToDisk(); +} + +/*! + \internal +*/ +bool QFSFileEnginePrivate::flushFh() +{ + Q_Q(QFSFileEngine); + + // Never try to flush again if the last flush failed. Otherwise you can + // get crashes on some systems (AIX). + if (lastFlushFailed) + return false; + + int ret = fflush(fh); + + lastFlushFailed = (ret != 0); + lastIOCommand = QFSFileEnginePrivate::IOFlushCommand; + + if (ret != 0) { + q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, + qt_error_string(errno)); + return false; + } + return true; +} + +/*! + \reimp +*/ +qint64 QFSFileEngine::size() const +{ + Q_D(const QFSFileEngine); + return d->nativeSize(); +} + +#ifndef Q_OS_WIN +/*! + \internal +*/ +qint64 QFSFileEnginePrivate::sizeFdFh() const +{ + Q_Q(const QFSFileEngine); + const_cast(q)->flush(); + + tried_stat = 0; + metaData.clearFlags(QFileSystemMetaData::SizeAttribute); + if (!doStat(QFileSystemMetaData::SizeAttribute)) + return 0; + return metaData.size(); +} +#endif + +/*! + \reimp +*/ +qint64 QFSFileEngine::pos() const +{ + Q_D(const QFSFileEngine); + return d->nativePos(); +} + +/*! + \internal +*/ +qint64 QFSFileEnginePrivate::posFdFh() const +{ + if (fh) + return qint64(QT_FTELL(fh)); + return QT_LSEEK(fd, 0, SEEK_CUR); +} + +/*! + \reimp +*/ +bool QFSFileEngine::seek(qint64 pos) +{ + Q_D(QFSFileEngine); + return d->nativeSeek(pos); +} + +/*! + \internal +*/ +bool QFSFileEnginePrivate::seekFdFh(qint64 pos) +{ + Q_Q(QFSFileEngine); + + // On Windows' stdlib implementation, the results of calling fread and + // fwrite are undefined if not called either in sequence, or if preceded + // with a call to fflush(). + if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush()) + return false; + + if (pos < 0 || pos != qint64(QT_OFF_T(pos))) + return false; + + if (fh) { + // Buffered stdlib mode. + int ret; + do { + ret = QT_FSEEK(fh, QT_OFF_T(pos), SEEK_SET); + } while (ret != 0 && errno == EINTR); + + if (ret != 0) { + q->setError(QFile::ReadError, qt_error_string(int(errno))); + return false; + } + } else { + // Unbuffered stdio mode. + if (QT_LSEEK(fd, QT_OFF_T(pos), SEEK_SET) == -1) { + qWarning() << "QFile::at: Cannot set file position" << pos; + q->setError(QFile::PositionError, qt_error_string(errno)); + return false; + } + } + return true; +} + +/*! + \reimp +*/ +int QFSFileEngine::handle() const +{ + Q_D(const QFSFileEngine); + return d->nativeHandle(); +} + +/*! + \reimp +*/ +qint64 QFSFileEngine::read(char *data, qint64 maxlen) +{ + Q_D(QFSFileEngine); + + // On Windows' stdlib implementation, the results of calling fread and + // fwrite are undefined if not called either in sequence, or if preceded + // with a call to fflush(). + if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) { + flush(); + d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand; + } + + return d->nativeRead(data, maxlen); +} + +/*! + \internal +*/ +qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len) +{ + Q_Q(QFSFileEngine); + + if (len < 0 || len != qint64(size_t(len))) { + q->setError(QFile::ReadError, qt_error_string(EINVAL)); + return -1; + } + + qint64 readBytes = 0; + bool eof = false; + + if (fh) { + // Buffered stdlib mode. + + size_t result; + bool retry = true; + do { + result = fread(data + readBytes, 1, size_t(len - readBytes), fh); + eof = feof(fh); + if (retry && eof && result == 0) { + // On Mac OS, this is needed, e.g., if a file was written to + // through another stream since our last read. See test + // tst_QFile::appendAndRead + QT_FSEEK(fh, QT_FTELL(fh), SEEK_SET); // re-sync stream. + retry = false; + continue; + } + readBytes += result; + } while (!eof && (result == 0 ? errno == EINTR : readBytes < len)); + + } else if (fd != -1) { + // Unbuffered stdio mode. + +#ifdef Q_OS_WIN + int result; +#else + ssize_t result; +#endif + do { + result = QT_READ(fd, data + readBytes, size_t(len - readBytes)); + } while ((result == -1 && errno == EINTR) + || (result > 0 && (readBytes += result) < len)); + + eof = !(result == -1); + } + + if (!eof && readBytes == 0) { + readBytes = -1; + q->setError(QFile::ReadError, qt_error_string(errno)); + } + + return readBytes; +} + +/*! + \reimp +*/ +qint64 QFSFileEngine::readLine(char *data, qint64 maxlen) +{ + Q_D(QFSFileEngine); + + // On Windows' stdlib implementation, the results of calling fread and + // fwrite are undefined if not called either in sequence, or if preceded + // with a call to fflush(). + if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) { + flush(); + d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand; + } + + return d->nativeReadLine(data, maxlen); +} + +/*! + \internal +*/ +qint64 QFSFileEnginePrivate::readLineFdFh(char *data, qint64 maxlen) +{ + Q_Q(QFSFileEngine); + if (!fh) + return q->QAbstractFileEngine::readLine(data, maxlen); + + QT_OFF_T oldPos = 0; +#ifdef Q_OS_WIN + bool seq = q->isSequential(); + if (!seq) +#endif + oldPos = QT_FTELL(fh); + + // QIODevice::readLine() passes maxlen - 1 to QFile::readLineData() + // because it has made space for the '\0' at the end of data. But fgets + // does the same, so we'd get two '\0' at the end - passing maxlen + 1 + // solves this. + if (!fgets(data, int(maxlen + 1), fh)) { + if (!feof(fh)) + q->setError(QFile::ReadError, qt_error_string(int(errno))); + return -1; // error + } + +#ifdef Q_OS_WIN + if (seq) + return qstrlen(data); +#endif + + qint64 lineLength = QT_FTELL(fh) - oldPos; + return lineLength > 0 ? lineLength : qstrlen(data); +} + +/*! + \reimp +*/ +qint64 QFSFileEngine::write(const char *data, qint64 len) +{ + Q_D(QFSFileEngine); + + // On Windows' stdlib implementation, the results of calling fread and + // fwrite are undefined if not called either in sequence, or if preceded + // with a call to fflush(). + if (d->lastIOCommand != QFSFileEnginePrivate::IOWriteCommand) { + flush(); + d->lastIOCommand = QFSFileEnginePrivate::IOWriteCommand; + } + + return d->nativeWrite(data, len); +} + +/*! + \internal +*/ +qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len) +{ + Q_Q(QFSFileEngine); + + if (len < 0 || len != qint64(size_t(len))) { + q->setError(QFile::WriteError, qt_error_string(EINVAL)); + return -1; + } + + qint64 writtenBytes = 0; + + if (fh) { + // Buffered stdlib mode. + + size_t result; + do { + result = fwrite(data + writtenBytes, 1, size_t(len - writtenBytes), fh); + writtenBytes += result; + } while (result == 0 ? errno == EINTR : writtenBytes < len); + + } else if (fd != -1) { + // Unbuffered stdio mode. + +#ifdef Q_OS_WIN + int result; +#else + ssize_t result; +#endif + do { + result = QT_WRITE(fd, data + writtenBytes, size_t(len - writtenBytes)); + } while ((result == -1 && errno == EINTR) + || (result > 0 && (writtenBytes += result) < len)); + } + + if (len && writtenBytes == 0) { + writtenBytes = -1; + q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, qt_error_string(errno)); + } + + return writtenBytes; +} + +#ifndef QT_NO_FILESYSTEMITERATOR +/*! + \internal +*/ +QAbstractFileEngine::Iterator *QFSFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) +{ + return new QFSFileEngineIterator(filters, filterNames); +} + +/*! + \internal +*/ +QAbstractFileEngine::Iterator *QFSFileEngine::endEntryList() +{ + return 0; +} +#endif // QT_NO_FILESYSTEMITERATOR + +/*! + \internal +*/ +QStringList QFSFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const +{ + return QAbstractFileEngine::entryList(filters, filterNames); +} + +/*! + \reimp +*/ +bool QFSFileEngine::isSequential() const +{ + Q_D(const QFSFileEngine); + if (d->is_sequential == 0) + d->is_sequential = d->nativeIsSequential() ? 1 : 2; + return d->is_sequential == 1; +} + +/*! + \internal +*/ +#ifdef Q_OS_UNIX +bool QFSFileEnginePrivate::isSequentialFdFh() const +{ + if (doStat(QFileSystemMetaData::SequentialType)) + return metaData.isSequential(); + return true; +} +#endif + +/*! + \reimp +*/ +bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) +{ + Q_D(QFSFileEngine); + if (extension == AtEndExtension && d->fh && isSequential()) + return feof(d->fh); + + if (extension == MapExtension) { + const MapExtensionOption *options = (MapExtensionOption*)(option); + MapExtensionReturn *returnValue = static_cast(output); + returnValue->address = d->map(options->offset, options->size, options->flags); + return (returnValue->address != 0); + } + if (extension == UnMapExtension) { + UnMapExtensionOption *options = (UnMapExtensionOption*)option; + return d->unmap(options->address); + } + + return false; +} + +/*! + \reimp +*/ +bool QFSFileEngine::supportsExtension(Extension extension) const +{ + Q_D(const QFSFileEngine); + if (extension == AtEndExtension && d->fh && isSequential()) + return true; + if (extension == FastReadLineExtension && d->fh) + return true; + if (extension == FastReadLineExtension && d->fd != -1 && isSequential()) + return true; + if (extension == UnMapExtension || extension == MapExtension) + return true; + return false; +} + +/*! \fn bool QFSFileEngine::caseSensitive() const + Returns \c true for Windows, false for Unix. +*/ + +/*! \fn bool QFSFileEngine::copy(const QString ©Name) + + For windows, copy the file to file \a copyName. + + Not implemented for Unix. +*/ + +/*! \fn QString QFSFileEngine::currentPath(const QString &fileName) + For Unix, returns the current working directory for the file + engine. + + For Windows, returns the canonicalized form of the current path used + by the file engine for the drive specified by \a fileName. On + Windows, each drive has its own current directory, so a different + path is returned for file names that include different drive names + (e.g. A: or C:). + + \sa setCurrentPath() +*/ + +/*! \fn QFileInfoList QFSFileEngine::drives() + For Windows, returns the list of drives in the file system as a list + of QFileInfo objects. On unix, Mac OS X and Windows CE, only the + root path is returned. On Windows, this function returns all drives + (A:\, C:\, D:\, etc.). + + For Unix, the list contains just the root path "/". +*/ + +/*! \fn QString QFSFileEngine::fileName(FileName file) const + \reimp +*/ + +/*! \fn QDateTime QFSFileEngine::fileTime(FileTime time) const + \reimp +*/ + +/*! \fn QString QFSFileEngine::homePath() + Returns the home path of the current user. + + \sa rootPath() +*/ + +/*! \fn bool QFSFileEngine::isRelativePath() const + \reimp +*/ + +/*! \fn bool QFSFileEngine::link(const QString &newName) + + Creates a link from the file currently specified by fileName() to + \a newName. What a link is depends on the underlying filesystem + (be it a shortcut on Windows or a symbolic link on Unix). Returns + true if successful; otherwise returns \c false. +*/ + +/*! \fn bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const + \reimp +*/ + +/*! \fn uint QFSFileEngine::ownerId(FileOwner own) const + In Unix, if stat() is successful, the \c uid is returned if + \a own is the owner. Otherwise the \c gid is returned. If stat() + is unsuccessful, -2 is reuturned. + + For Windows, -2 is always returned. +*/ + +/*! \fn QString QFSFileEngine::owner(FileOwner own) const + \reimp +*/ + +/*! \fn bool QFSFileEngine::remove() + \reimp +*/ + +/*! \fn bool QFSFileEngine::rename(const QString &newName) + \reimp +*/ + + +/*! \fn bool QFSFileEngine::renameOverwrite(const QString &newName) + \reimp +*/ + +/*! \fn bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const + \reimp +*/ + +/*! \fn QString QFSFileEngine::rootPath() + Returns the root path. + + \sa homePath() +*/ + +/*! \fn bool QFSFileEngine::setCurrentPath(const QString &path) + Sets the current path (e.g., for QDir), to \a path. Returns \c true if the + new path exists; otherwise this function does nothing, and returns \c false. + + \sa currentPath() +*/ + +/*! \fn bool QFSFileEngine::setPermissions(uint perms) + \reimp +*/ + +/*! \fn bool QFSFileEngine::setSize(qint64 size) + \reimp +*/ + +/*! \fn QString QFSFileEngine::tempPath() + Returns the temporary path (i.e., a path in which it is safe + to store temporary files). +*/ + +/*! \fn QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions(QAbstractFileEngine::FileFlags type) const + \internal +*/ + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QT_NO_FSFILEENGINE diff --git a/src/app/core/backport/qfsfileengine_iterator.cpp b/src/app/core/backport/qfsfileengine_iterator.cpp new file mode 100644 index 000000000..af27a3717 --- /dev/null +++ b/src/app/core/backport/qfsfileengine_iterator.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfsfileengine_iterator_p.h" +#include "qfileinfo_p.h" +#include + +#ifndef QT_NO_FILESYSTEMITERATOR + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +QFSFileEngineIterator::QFSFileEngineIterator(QDir::Filters filters, const QStringList &filterNames) + : QAbstractFileEngineIterator(filters, filterNames) + , done(false) +{ +} + +QFSFileEngineIterator::~QFSFileEngineIterator() +{ +} + +bool QFSFileEngineIterator::hasNext() const +{ + if (!done && !nativeIterator) { + nativeIterator.reset(new QFileSystemIterator(QFileSystemEntry(path()), + filters(), nameFilters())); + advance(); + } + + return !done; +} + +QString QFSFileEngineIterator::next() +{ + if (!hasNext()) + return QString(); + + advance(); + return currentFilePath(); +} + +void QFSFileEngineIterator::advance() const +{ + currentInfo = nextInfo; + + QFileSystemEntry entry; + QFileSystemMetaData data; + if (nativeIterator->advance(entry, data)) { + nextInfo = QFileInfo(new QFileInfoPrivate(entry, data)); + } else { + done = true; + nativeIterator.reset(); + } +} + +QString QFSFileEngineIterator::currentFileName() const +{ + return currentInfo.fileName(); +} + +QFileInfo QFSFileEngineIterator::currentFileInfo() const +{ + return currentInfo; +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QT_NO_FILESYSTEMITERATOR diff --git a/src/app/core/backport/qfsfileengine_iterator_p.h b/src/app/core/backport/qfsfileengine_iterator_p.h new file mode 100644 index 000000000..f78b0a193 --- /dev/null +++ b/src/app/core/backport/qfsfileengine_iterator_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFSFILEENGINE_ITERATOR_P_H +#define QFSFILEENGINE_ITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qabstractfileengine_p.h" +#include "qfilesystemiterator_p.h" +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifndef QT_NO_FILESYSTEMITERATOR + +class QFSFileEngineIteratorPrivate; +class QFSFileEngineIteratorPlatformSpecificData; + +class QFSFileEngineIterator : public QAbstractFileEngineIterator +{ +public: + QFSFileEngineIterator(QDir::Filters filters, const QStringList &filterNames); + ~QFSFileEngineIterator(); + + QString next(); + bool hasNext() const; + + QString currentFileName() const; + QFileInfo currentFileInfo() const; + +private: + void advance() const; + mutable QScopedPointer nativeIterator; + mutable QFileInfo currentInfo; + mutable QFileInfo nextInfo; + mutable bool done; +}; + +#endif // QT_NO_FILESYSTEMITERATOR + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QFSFILEENGINE_ITERATOR_P_H diff --git a/src/app/core/backport/qfsfileengine_p.h b/src/app/core/backport/qfsfileengine_p.h new file mode 100644 index 000000000..07a7e5d14 --- /dev/null +++ b/src/app/core/backport/qfsfileengine_p.h @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFSFILEENGINE_P_H +#define QFSFILEENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qplatformdefs.h" +#include "qabstractfileengine_p.h" +#include "qfilesystementry_p.h" +#include "qfilesystemmetadata_p.h" +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifndef QT_NO_FSFILEENGINE + +#if defined(Q_OS_WINCE_STD) && _WIN32_WCE < 0x600 +#define Q_USE_DEPRECATED_MAP_API 1 +#endif + +class QFSFileEnginePrivate; + +class QFSFileEngine : public QAbstractFileEngine +{ + Q_DECLARE_PRIVATE(QFSFileEngine) +public: + QFSFileEngine(); + explicit QFSFileEngine(const QString &file); + ~QFSFileEngine(); + + bool open(QIODevice::OpenMode openMode); + bool open(QIODevice::OpenMode flags, FILE *fh); + bool close(); + bool flush(); + bool syncToDisk(); + qint64 size() const; + qint64 pos() const; + bool seek(qint64); + bool isSequential() const; + bool remove(); + bool copy(const QString &newName); + bool rename(const QString &newName); + bool renameOverwrite(const QString &newName); + bool link(const QString &newName); + bool mkdir(const QString &dirName, bool createParentDirectories) const; + bool rmdir(const QString &dirName, bool recurseParentDirectories) const; + bool setSize(qint64 size); + bool caseSensitive() const; + bool isRelativePath() const; + QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const; + FileFlags fileFlags(FileFlags type) const; + bool setPermissions(uint perms); + QString fileName(FileName file) const; + uint ownerId(FileOwner) const; + QString owner(FileOwner) const; + QDateTime fileTime(FileTime time) const; + void setFileName(const QString &file); + int handle() const; + +#ifndef QT_NO_FILESYSTEMITERATOR + Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames); + Iterator *endEntryList(); +#endif + + qint64 read(char *data, qint64 maxlen); + qint64 readLine(char *data, qint64 maxlen); + qint64 write(const char *data, qint64 len); + + bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0); + bool supportsExtension(Extension extension) const; + + //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); + static bool setCurrentPath(const QString &path); + static QString currentPath(const QString &path = QString()); + static QString homePath(); + static QString rootPath(); + static QString tempPath(); + static QFileInfoList drives(); + +protected: + QFSFileEngine(QFSFileEnginePrivate &dd); +}; + +class QFSFileEnginePrivate : public QAbstractFileEnginePrivate +{ + Q_DECLARE_PUBLIC(QFSFileEngine) + +public: +#ifdef Q_OS_WIN + static QString longFileName(const QString &path); +#endif + + QFileSystemEntry fileEntry; + QIODevice::OpenMode openMode; + + bool nativeOpen(QIODevice::OpenMode openMode); + bool openFh(QIODevice::OpenMode flags, FILE *fh); + bool openFd(QIODevice::OpenMode flags, int fd); + bool nativeClose(); + bool closeFdFh(); + bool nativeFlush(); + bool nativeSyncToDisk(); + bool flushFh(); + qint64 nativeSize() const; +#ifndef Q_OS_WIN + qint64 sizeFdFh() const; +#endif + qint64 nativePos() const; + qint64 posFdFh() const; + bool nativeSeek(qint64); + bool seekFdFh(qint64); + qint64 nativeRead(char *data, qint64 maxlen); + qint64 readFdFh(char *data, qint64 maxlen); + qint64 nativeReadLine(char *data, qint64 maxlen); + qint64 readLineFdFh(char *data, qint64 maxlen); + qint64 nativeWrite(const char *data, qint64 len); + qint64 writeFdFh(const char *data, qint64 len); + int nativeHandle() const; + bool nativeIsSequential() const; +#ifndef Q_OS_WIN + bool isSequentialFdFh() const; +#endif + + uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags); + bool unmap(uchar *ptr); + + mutable QFileSystemMetaData metaData; + + FILE *fh; + +#ifdef Q_OS_WIN + HANDLE fileHandle; + HANDLE mapHandle; + QHash maps; + +#ifndef Q_OS_WINCE + mutable int cachedFd; +#endif + + mutable DWORD fileAttrib; +#else + QHash > maps; +#endif + int fd; + + enum LastIOCommand + { + IOFlushCommand, + IOReadCommand, + IOWriteCommand + }; + LastIOCommand lastIOCommand; + bool lastFlushFailed; + bool closeFileHandle; + + mutable uint is_sequential : 2; + mutable uint could_stat : 1; + mutable uint tried_stat : 1; +#if !defined(Q_OS_WINCE) + mutable uint need_lstat : 1; + mutable uint is_link : 1; +#endif + +#if defined(Q_OS_WIN) + bool doStat(QFileSystemMetaData::MetaDataFlags flags) const; +#else + bool doStat(QFileSystemMetaData::MetaDataFlags flags = QFileSystemMetaData::PosixStatFlags) const; +#endif + bool isSymlink() const; + +#if defined(Q_OS_WIN32) + int sysOpen(const QString &, int flags); +#endif + +protected: + QFSFileEnginePrivate(); + + void init(); + + QAbstractFileEngine::FileFlags getPermissions(QAbstractFileEngine::FileFlags type) const; +}; + +#endif // QT_NO_FSFILEENGINE + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QFSFILEENGINE_P_H diff --git a/src/app/core/backport/qfsfileengine_unix.cpp b/src/app/core/backport/qfsfileengine_unix.cpp new file mode 100644 index 000000000..fc32cec40 --- /dev/null +++ b/src/app/core/backport/qfsfileengine_unix.cpp @@ -0,0 +1,749 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qabstractfileengine_p.h" +#include "qfsfileengine_p.h" +#include "qcore_unix_p.h" +#include "qfilesystementry_p.h" +#include "qfilesystemengine_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifndef QT_NO_FSFILEENGINE + +#include +#include +#include +#include + +#include +#include +#include +#include +#if !defined(QWS) && defined(Q_OS_MAC) +# include "qcore_mac_p.h" +#endif + +/*! + \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; +} + +/*! + \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; +} + +/*! + \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; +} + +/*! + \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; +} + +/*! + \internal +*/ +bool QFSFileEnginePrivate::nativeClose() +{ + return closeFdFh(); +} + +/*! + \internal + +*/ +bool QFSFileEnginePrivate::nativeFlush() +{ + return fh ? flushFh() : fd != -1; +} + +/*! + \internal + \since 5.1 +*/ +bool QFSFileEnginePrivate::nativeSyncToDisk() +{ + Q_Q(QFSFileEngine); +#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 + const int ret = fdatasync(nativeHandle()); +#else + const int ret = fsync(nativeHandle()); +#endif + if (ret != 0) + q->setError(QFile::WriteError, qt_error_string(errno)); + return ret == 0; +} + +/*! + \internal +*/ +qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 len) +{ + Q_Q(QFSFileEngine); + + 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) +{ + return writeFdFh(data, len); +} + +/*! + \internal +*/ +qint64 QFSFileEnginePrivate::nativePos() const +{ + return posFdFh(); +} + +/*! + \internal +*/ +bool QFSFileEnginePrivate::nativeSeek(qint64 pos) +{ + return seekFdFh(pos); +} + +/*! + \internal +*/ +int QFSFileEnginePrivate::nativeHandle() const +{ + return fh ? fileno(fh) : fd; +} + +/*! + \internal +*/ +bool QFSFileEnginePrivate::nativeIsSequential() const +{ + 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::renameOverwrite(const QString &newName) +{ + // On Unix, rename() overwrites. + return rename(newName); +} + +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 +{ + 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 +{ + return true; +} + +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; + ret.append(QFileInfo(rootPath())); + 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); + return d->fileEntry.filePath().length() ? d->fileEntry.filePath()[0] != QLatin1Char('/') : true; +} + +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 +{ + if (own == OwnerUser) + return QFileSystemEngine::resolveUserName(ownerId(own)); + return QFileSystemEngine::resolveGroupName(ownerId(own)); +} + +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; +} + +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; +} + +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)); + + void *mapAddress = QT_MMAP((void*)0, realSize, + access, MAP_SHARED, nativeHandle(), realOffset); + if (MAP_FAILED != mapAddress) { + uchar *address = extra + static_cast(mapAddress); + maps[address] = QPair(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 +} + +#endif // QT_NO_FSFILEENGINE + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qfsfileengine_win.cpp b/src/app/core/backport/qfsfileengine_win.cpp new file mode 100644 index 000000000..24a4827ab --- /dev/null +++ b/src/app/core/backport/qfsfileengine_win.cpp @@ -0,0 +1,1053 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qabstractfileengine_p.h" +#include "qfsfileengine_p.h" +#include "qfilesystemengine_p.h" +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#include +#include +#include "qmutexpool_p.h" +#include +#include +#include "qt_windows.h" + +#if !defined(Q_OS_WINCE) +# include +# include +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#define SECURITY_WIN32 +#include + +#ifndef PATH_MAX +#define PATH_MAX FILENAME_MAX +#endif + +#if !defined(Q_OS_WINCE) +static inline bool isUncPath(const QString &path) +{ + // Starts with \\, but not \\. + return (path.startsWith(QLatin1String("\\\\")) + && path.size() > 2 && path.at(2) != QLatin1Char('.')); +} +#endif + +/*! + \internal +*/ +QString QFSFileEnginePrivate::longFileName(const QString &path) +{ + if (path.startsWith(QLatin1String("\\\\.\\"))) + return path; + + QString absPath = QFileSystemEngine::nativeAbsoluteFilePath(path); +#if !defined(Q_OS_WINCE) + QString prefix = QLatin1String("\\\\?\\"); + if (isUncPath(absPath)) { + prefix.append(QLatin1String("UNC\\")); // "\\\\?\\UNC\\" + absPath.remove(0, 2); + } + return prefix + absPath; +#else + return absPath; +#endif +} + +/* + \internal +*/ +bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) +{ + Q_Q(QFSFileEngine); + + // All files are opened in share mode (both read and write). + DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + + int accessRights = 0; + if (openMode & QIODevice::ReadOnly) + accessRights |= GENERIC_READ; + if (openMode & QIODevice::WriteOnly) + accessRights |= GENERIC_WRITE; + + SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE }; + + // WriteOnly can create files, ReadOnly cannot. + DWORD creationDisp = (openMode & QIODevice::WriteOnly) ? OPEN_ALWAYS : OPEN_EXISTING; + // Create the file handle. + fileHandle = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(), + accessRights, + shareMode, + &securityAtts, + creationDisp, + FILE_ATTRIBUTE_NORMAL, + NULL); + + // Bail out on error. + if (fileHandle == INVALID_HANDLE_VALUE) { + q->setError(QFile::OpenError, qt_error_string()); + return false; + } + + // Truncate the file after successfully opening it if Truncate is passed. + if (openMode & QIODevice::Truncate) + q->setSize(0); + + return true; +} + +/* + \internal +*/ +bool QFSFileEnginePrivate::nativeClose() +{ + Q_Q(QFSFileEngine); + if (fh || fd != -1) { + // stdlib / stdio mode. + return closeFdFh(); + } + + // Windows native mode. + bool ok = true; + +#ifndef Q_OS_WINCE + if (cachedFd != -1) { + if (::_close(cachedFd) && !::CloseHandle(fileHandle)) { + q->setError(QFile::UnspecifiedError, qt_error_string()); + ok = false; + } + + // System handle is closed with associated file descriptor. + fileHandle = INVALID_HANDLE_VALUE; + cachedFd = -1; + + return ok; + } +#endif + + if ((fileHandle == INVALID_HANDLE_VALUE || !::CloseHandle(fileHandle))) { + q->setError(QFile::UnspecifiedError, qt_error_string()); + ok = false; + } + fileHandle = INVALID_HANDLE_VALUE; + return ok; +} + +/* + \internal +*/ +bool QFSFileEnginePrivate::nativeFlush() +{ + if (fh) { + // Buffered stdlib mode. + return flushFh(); + } + if (fd != -1) { + // Unbuffered stdio mode; always succeeds (no buffer). + return true; + } + + // Windows native mode; flushing is unnecessary. + return true; +} + +/* + \internal + \since 5.1 +*/ +bool QFSFileEnginePrivate::nativeSyncToDisk() +{ + if (fh || fd != -1) { + // stdlib / stdio mode. No API available. + return false; + } + return FlushFileBuffers(fileHandle); +} + +/* + \internal +*/ +qint64 QFSFileEnginePrivate::nativeSize() const +{ + Q_Q(const QFSFileEngine); + QFSFileEngine *thatQ = const_cast(q); + + // ### Don't flush; for buffered files, we should get away with ftell. + thatQ->flush(); + + // Always retrive the current information + metaData.clearFlags(QFileSystemMetaData::SizeAttribute); +#if defined(Q_OS_WINCE) + // Buffered stdlib mode. + if (fh) { + QT_OFF_T oldPos = QT_FTELL(fh); + QT_FSEEK(fh, 0, SEEK_END); + qint64 fileSize = (qint64)QT_FTELL(fh); + QT_FSEEK(fh, oldPos, SEEK_SET); + if (fileSize == -1) { + fileSize = 0; + thatQ->setError(QFile::UnspecifiedError, qt_error_string(errno)); + } + return fileSize; + } + if (fd != -1) { + thatQ->setError(QFile::UnspecifiedError, QLatin1String("Not implemented!")); + return 0; + } +#endif + bool filled = false; + if (fileHandle != INVALID_HANDLE_VALUE && openMode != QIODevice::NotOpen ) + filled = QFileSystemEngine::fillMetaData(fileHandle, metaData, + QFileSystemMetaData::SizeAttribute); + else + filled = doStat(QFileSystemMetaData::SizeAttribute); + + if (!filled) { + thatQ->setError(QFile::UnspecifiedError, qt_error_string(errno)); + return 0; + } + return metaData.size(); +} + +/* + \internal +*/ +qint64 QFSFileEnginePrivate::nativePos() const +{ + Q_Q(const QFSFileEngine); + QFSFileEngine *thatQ = const_cast(q); + + if (fh || fd != -1) { + // stdlib / stido mode. + return posFdFh(); + } + + // Windows native mode. + if (fileHandle == INVALID_HANDLE_VALUE) + return 0; + +#if !defined(Q_OS_WINCE) + LARGE_INTEGER currentFilePos; + LARGE_INTEGER offset; + offset.QuadPart = 0; + if (!::SetFilePointerEx(fileHandle, offset, ¤tFilePos, FILE_CURRENT)) { + thatQ->setError(QFile::UnspecifiedError, qt_error_string()); + return 0; + } + + return qint64(currentFilePos.QuadPart); +#else + LARGE_INTEGER filepos; + filepos.HighPart = 0; + DWORD newFilePointer = SetFilePointer(fileHandle, 0, &filepos.HighPart, FILE_CURRENT); + if (newFilePointer == 0xFFFFFFFF && GetLastError() != NO_ERROR) { + thatQ->setError(QFile::UnspecifiedError, qt_error_string()); + return 0; + } + + filepos.LowPart = newFilePointer; + return filepos.QuadPart; +#endif +} + +/* + \internal +*/ +bool QFSFileEnginePrivate::nativeSeek(qint64 pos) +{ + Q_Q(QFSFileEngine); + + if (fh || fd != -1) { + // stdlib / stdio mode. + return seekFdFh(pos); + } + +#if !defined(Q_OS_WINCE) + LARGE_INTEGER currentFilePos; + LARGE_INTEGER offset; + offset.QuadPart = pos; + if (!::SetFilePointerEx(fileHandle, offset, ¤tFilePos, FILE_BEGIN)) { + q->setError(QFile::UnspecifiedError, qt_error_string()); + return false; + } + + return true; +#else + DWORD newFilePointer; + LARGE_INTEGER *li = reinterpret_cast(&pos); + newFilePointer = SetFilePointer(fileHandle, li->LowPart, &li->HighPart, FILE_BEGIN); + if (newFilePointer == 0xFFFFFFFF && GetLastError() != NO_ERROR) { + q->setError(QFile::PositionError, qt_error_string()); + return false; + } + + return true; +#endif +} + +/* + \internal +*/ +qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 maxlen) +{ + Q_Q(QFSFileEngine); + + if (fh || fd != -1) { + // stdio / stdlib mode. + if (fh && nativeIsSequential() && feof(fh)) { + q->setError(QFile::ReadError, qt_error_string(int(errno))); + return -1; + } + + return readFdFh(data, maxlen); + } + + // Windows native mode. + if (fileHandle == INVALID_HANDLE_VALUE) + return -1; + + DWORD bytesToRead = DWORD(maxlen); // <- lossy + + // Reading on Windows fails with ERROR_NO_SYSTEM_RESOURCES when + // the chunks are too large, so we limit the block size to 32MB. + static const DWORD maxBlockSize = 32 * 1024 * 1024; + + qint64 totalRead = 0; + do { + DWORD blockSize = qMin(bytesToRead, maxBlockSize); + DWORD bytesRead; + if (!ReadFile(fileHandle, data + totalRead, blockSize, &bytesRead, NULL)) { + if (totalRead == 0) { + // Note: only return failure if the first ReadFile fails. + q->setError(QFile::ReadError, qt_error_string()); + return -1; + } + break; + } + if (bytesRead == 0) + break; + totalRead += bytesRead; + bytesToRead -= bytesRead; + } while (totalRead < maxlen); + return qint64(totalRead); +} + +/* + \internal +*/ +qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen) +{ + Q_Q(QFSFileEngine); + + if (fh || fd != -1) { + // stdio / stdlib mode. + return readLineFdFh(data, maxlen); + } + + // Windows native mode. + if (fileHandle == INVALID_HANDLE_VALUE) + return -1; + + // ### No equivalent in Win32? + return q->QAbstractFileEngine::readLine(data, maxlen); +} + +/* + \internal +*/ +qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len) +{ + Q_Q(QFSFileEngine); + + if (fh || fd != -1) { + // stdio / stdlib mode. + return writeFdFh(data, len); + } + + // Windows native mode. + if (fileHandle == INVALID_HANDLE_VALUE) + return -1; + + qint64 bytesToWrite = DWORD(len); // <- lossy + + // Writing on Windows fails with ERROR_NO_SYSTEM_RESOURCES when + // the chunks are too large, so we limit the block size to 32MB. + static const DWORD maxBlockSize = 32 * 1024 * 1024; + + qint64 totalWritten = 0; + do { + DWORD blockSize = qMin(bytesToWrite, maxBlockSize); + DWORD bytesWritten; + if (!WriteFile(fileHandle, data + totalWritten, blockSize, &bytesWritten, NULL)) { + if (totalWritten == 0) { + // Note: Only return error if the first WriteFile failed. + q->setError(QFile::WriteError, qt_error_string()); + return -1; + } + break; + } + if (bytesWritten == 0) + break; + totalWritten += bytesWritten; + bytesToWrite -= bytesWritten; + } while (totalWritten < len); + return qint64(totalWritten); +} + +/* + \internal +*/ +int QFSFileEnginePrivate::nativeHandle() const +{ + if (fh || fd != -1) + return fh ? QT_FILENO(fh) : fd; +#ifndef Q_OS_WINCE + if (cachedFd != -1) + return cachedFd; + + int flags = 0; + if (openMode & QIODevice::Append) + flags |= _O_APPEND; + if (!(openMode & QIODevice::WriteOnly)) + flags |= _O_RDONLY; + cachedFd = _open_osfhandle((intptr_t) fileHandle, flags); + return cachedFd; +#else + return -1; +#endif +} + +/* + \internal +*/ +bool QFSFileEnginePrivate::nativeIsSequential() const +{ +#if !defined(Q_OS_WINCE) + HANDLE handle = fileHandle; + if (fh || fd != -1) + handle = (HANDLE)_get_osfhandle(fh ? QT_FILENO(fh) : fd); + if (handle == INVALID_HANDLE_VALUE) + return false; + + DWORD fileType = GetFileType(handle); + return (fileType == FILE_TYPE_CHAR) + || (fileType == FILE_TYPE_PIPE); +#else + return false; +#endif +} + +bool QFSFileEngine::remove() +{ + Q_D(QFSFileEngine); + QSystemError error; + bool ret = QFileSystemEngine::removeFile(d->fileEntry, error); + if (!ret) + setError(QFile::RemoveError, error.toString()); + return ret; +} + +bool QFSFileEngine::copy(const QString ©Name) +{ + Q_D(QFSFileEngine); + QSystemError error; + bool ret = QFileSystemEngine::copyFile(d->fileEntry, QFileSystemEntry(copyName), 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::renameOverwrite(const QString &newName) +{ + Q_D(QFSFileEngine); +#if defined(Q_OS_WINCE) + // Windows Embedded Compact 7 does not have MoveFileEx, simulate it with the following sequence: + // 1. DeleteAndRenameFile (Should work on RAM FS when both files exist) + // 2. DeleteFile/MoveFile (Should work on all file systems) + // + // DeleteFile/MoveFile fallback implementation violates atomicity, but it is more acceptable than + // alternative CopyFile/DeleteFile sequence for the following reasons: + // + // 1. DeleteFile/MoveFile is way faster than CopyFile/DeleteFile and thus more atomic. + // 2. Given the intended use case of this function in QSaveFile, DeleteFile/MoveFile sequence will + // delete the old content, but leave a file "filename.ext.XXXXXX" in the same directory if MoveFile fails. + // With CopyFile/DeleteFile sequence, it can happen that new data is partially copied to target file + // (because CopyFile is not atomic either), thus leaving *some* content to target file. + // This makes the need for application level recovery harder to detect than in DeleteFile/MoveFile + // sequence where target file simply does not exist. + // + bool ret = ::DeleteAndRenameFile((wchar_t*)QFileSystemEntry(newName).nativeFilePath().utf16(), + (wchar_t*)d->fileEntry.nativeFilePath().utf16()) != 0; + if (!ret) { + ret = ::DeleteFile((wchar_t*)d->fileEntry.nativeFilePath().utf16()) != 0; + if (ret) + ret = ::MoveFile((wchar_t*)d->fileEntry.nativeFilePath().utf16(), + (wchar_t*)QFileSystemEntry(newName).nativeFilePath().utf16()) != 0; + } +#else + bool ret = ::MoveFileEx((wchar_t*)d->fileEntry.nativeFilePath().utf16(), + (wchar_t*)QFileSystemEntry(newName).nativeFilePath().utf16(), + MOVEFILE_REPLACE_EXISTING) != 0; +#endif + if (!ret) + setError(QFile::RenameError, QSystemError(::GetLastError(), QSystemError::NativeError).toString()); + return ret; +} + +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 +{ + return false; +} + +bool QFSFileEngine::setCurrentPath(const QString &path) +{ + return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path)); +} + +QString QFSFileEngine::currentPath(const QString &fileName) +{ +#if !defined(Q_OS_WINCE) + QString ret; + //if filename is a drive: then get the pwd of that drive + if (fileName.length() >= 2 && + fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':')) { + int drv = fileName.toUpper().at(0).toLatin1() - 'A' + 1; + if (_getdrive() != drv) { + wchar_t buf[PATH_MAX]; + ::_wgetdcwd(drv, buf, PATH_MAX); + ret = QString::fromWCharArray(buf); + } + } + if (ret.isEmpty()) { + //just the pwd + ret = QFileSystemEngine::currentPath().filePath(); + } + if (ret.length() >= 2 && ret[1] == QLatin1Char(':')) + ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters. + return ret; +#else + Q_UNUSED(fileName); + return QFileSystemEngine::currentPath().filePath(); +#endif +} + +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_WINCE) +#if defined(Q_OS_WIN32) + quint32 driveBits = (quint32) GetLogicalDrives() & 0x3ffffff; +#endif + char driveName[] = "A:/"; + + while (driveBits) { + if (driveBits & 1) + ret.append(QFileInfo(QLatin1String(driveName))); + driveName[0]++; + driveBits = driveBits >> 1; + } + return ret; +#else + ret.append(QFileInfo(QLatin1String("/"))); + return ret; +#endif +} + +bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const +{ + if (!tried_stat || !metaData.hasFlags(flags)) { + tried_stat = true; + +#if !defined(Q_OS_WINCE) + int localFd = fd; + if (fh && fileEntry.isEmpty()) + localFd = QT_FILENO(fh); + if (localFd != -1) + QFileSystemEngine::fillMetaData(localFd, metaData, flags); +#endif + if (metaData.missingFlags(flags) && !fileEntry.isEmpty()) + QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags)); + } + + return metaData.exists(); +} + + +bool QFSFileEngine::link(const QString &newName) +{ +#if !defined(Q_OS_WINCE) +#if !defined(QT_NO_LIBRARY) + bool ret = false; + + QString linkName = newName; + //### assume that they add .lnk + + IShellLink *psl; + bool neededCoInit = false; + + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl); + + if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized + neededCoInit = true; + CoInitialize(NULL); + hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl); + } + + if (SUCCEEDED(hres)) { + hres = psl->SetPath((wchar_t *)fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\')).utf16()); + if (SUCCEEDED(hres)) { + hres = psl->SetWorkingDirectory((wchar_t *)fileName(AbsolutePathName).replace(QLatin1Char('/'), QLatin1Char('\\')).utf16()); + if (SUCCEEDED(hres)) { + IPersistFile *ppf; + hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf); + if (SUCCEEDED(hres)) { + hres = ppf->Save((wchar_t*)linkName.utf16(), TRUE); + if (SUCCEEDED(hres)) + ret = true; + ppf->Release(); + } + } + } + psl->Release(); + } + if (!ret) + setError(QFile::RenameError, qt_error_string()); + + if (neededCoInit) + CoUninitialize(); + + return ret; +#else + Q_UNUSED(newName); + return false; +#endif // QT_NO_LIBRARY +#else + QString linkName = newName; + linkName.replace(QLatin1Char('/'), QLatin1Char('\\')); + if (!linkName.endsWith(QLatin1String(".lnk"))) + linkName += QLatin1String(".lnk"); + QString orgName = fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\')); + // Need to append on our own + orgName.prepend(QLatin1Char('"')); + orgName.append(QLatin1Char('"')); + bool ret = SUCCEEDED(SHCreateShortcut((wchar_t*)linkName.utf16(), (wchar_t*)orgName.utf16())); + if (!ret) + setError(QFile::RenameError, qt_error_string()); + return ret; +#endif // Q_OS_WINCE +} + +/*! + \reimp +*/ +QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::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; + + // AliasType and BundleType are 0x0 + 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 && (type & PermsMask)) + ret |= FileFlags(uint(d->metaData.permissions())); + + if (type & TypesMask) { + if ((type & LinkType) && d->metaData.isLegacyLink()) + ret |= LinkType; + if (d->metaData.isDirectory()) { + ret |= DirectoryType; + } else { + ret |= FileType; + } + } + if (type & FlagsMask) { + if (d->metaData.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 == BaseName) { + return d->fileEntry.fileName(); + } else if (file == PathName) { + return d->fileEntry.path(); + } else if (file == AbsoluteName || file == AbsolutePathName) { + QString ret; + + if (!isRelativePath()) { +#if !defined(Q_OS_WINCE) + if (d->fileEntry.filePath().startsWith(QLatin1Char('/')) || // It's a absolute path to the current drive, so \a.txt -> Z:\a.txt + d->fileEntry.filePath().size() == 2 || // It's a drive letter that needs to get a working dir appended + (d->fileEntry.filePath().size() > 2 && d->fileEntry.filePath().at(2) != QLatin1Char('/')) || // It's a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt + d->fileEntry.filePath().contains(QLatin1String("/../")) || d->fileEntry.filePath().contains(QLatin1String("/./")) || + d->fileEntry.filePath().endsWith(QLatin1String("/..")) || d->fileEntry.filePath().endsWith(QLatin1String("/."))) + { + ret = QDir::fromNativeSeparators(QFileSystemEngine::nativeAbsoluteFilePath(d->fileEntry.filePath())); + } else +#endif + { + ret = d->fileEntry.filePath(); + } + } else { + ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + d->fileEntry.filePath()); + } + + // The path should be absolute at this point. + // From the docs : + // Absolute paths begin with the directory separator "/" + // (optionally preceded by a drive specification under Windows). + if (ret.at(0) != QLatin1Char('/')) { + Q_ASSERT(ret.length() >= 2); + Q_ASSERT(ret.at(0).isLetter()); + Q_ASSERT(ret.at(1) == QLatin1Char(':')); + + // Force uppercase drive letters. + ret[0] = ret.at(0).toUpper(); + } + + if (file == AbsolutePathName) { + int slash = ret.lastIndexOf(QLatin1Char('/')); + if (slash < 0) + return ret; + else if (ret.at(0) != QLatin1Char('/') && slash == 2) + return ret.left(3); // include the slash + else + return ret.left(slash > 0 ? slash : 1); + } + return ret; + } else if (file == CanonicalName || file == CanonicalPathName) { + if (!(fileFlags(ExistsFlag) & ExistsFlag)) + return QString(); + QFileSystemEntry entry(QFileSystemEngine::canonicalName(QFileSystemEntry(fileName(AbsoluteName)), d->metaData)); + + if (file == CanonicalPathName) + return entry.path(); + return entry.filePath(); + } else if (file == LinkName) { + return QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData).filePath(); + } else if (file == BundleName) { + return QString(); + } + return d->fileEntry.filePath(); +} + +bool QFSFileEngine::isRelativePath() const +{ + Q_D(const QFSFileEngine); + // drive, e.g. "a:", or UNC root, e.q. "//" + return d->fileEntry.isRelative(); +} + +uint QFSFileEngine::ownerId(FileOwner /*own*/) const +{ + static const uint nobodyID = (uint) -2; + return nobodyID; +} + +QString QFSFileEngine::owner(FileOwner own) const +{ + Q_D(const QFSFileEngine); + return QFileSystemEngine::owner(d->fileEntry, own); +} + +bool QFSFileEngine::setPermissions(uint perms) +{ + Q_D(QFSFileEngine); + QSystemError error; + bool ret = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error); + if (!ret) + setError(QFile::PermissionsError, error.toString()); + return ret; +} + +bool QFSFileEngine::setSize(qint64 size) +{ + Q_D(QFSFileEngine); + + if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1 || d->fh) { + // resize open file + HANDLE fh = d->fileHandle; +#if !defined(Q_OS_WINCE) + if (fh == INVALID_HANDLE_VALUE) { + if (d->fh) + fh = (HANDLE)_get_osfhandle(QT_FILENO(d->fh)); + else + fh = (HANDLE)_get_osfhandle(d->fd); + } +#endif + if (fh == INVALID_HANDLE_VALUE) + return false; + qint64 currentPos = pos(); + + if (seek(size) && SetEndOfFile(fh)) { + seek(qMin(currentPos, size)); + return true; + } + + seek(currentPos); + return false; + } + + if (!d->fileEntry.isEmpty()) { + // resize file on disk + QFile file(d->fileEntry.filePath()); + if (file.open(QFile::ReadWrite)) { + bool ret = file.resize(size); + if (!ret) + setError(QFile::ResizeError, file.errorString()); + return ret; + } + } + return false; +} + + +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 == QFile::NotOpen) { + q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); + return 0; + } + if (offset == 0 && size == 0) { + q->setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER)); + return 0; + } + + if (mapHandle == NULL) { + // get handle to the file + HANDLE handle = fileHandle; + +#ifndef Q_OS_WINCE + if (handle == INVALID_HANDLE_VALUE && fh) + handle = (HANDLE)::_get_osfhandle(QT_FILENO(fh)); +#endif + +#ifdef Q_USE_DEPRECATED_MAP_API + nativeClose(); + // handle automatically closed by kernel with mapHandle (below). + handle = ::CreateFileForMapping((const wchar_t*)fileEntry.nativeFilePath().utf16(), + GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0), + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + // Since this is a special case, we check if the return value was NULL and if so + // we change it to INVALID_HANDLE_VALUE to follow the logic inside this function. + if(0 == handle) + handle = INVALID_HANDLE_VALUE; +#endif + + if (handle == INVALID_HANDLE_VALUE) { + q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); + return 0; + } + + // first create the file mapping handle + DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY; + mapHandle = ::CreateFileMapping(handle, 0, protection, 0, 0, 0); + if (mapHandle == NULL) { + q->setError(QFile::PermissionsError, qt_error_string()); +#ifdef Q_USE_DEPRECATED_MAP_API + ::CloseHandle(handle); +#endif + return 0; + } + } + + // setup args to map + DWORD access = 0; + if (openMode & QIODevice::ReadOnly) access = FILE_MAP_READ; + if (openMode & QIODevice::WriteOnly) access = FILE_MAP_WRITE; + + DWORD offsetHi = offset >> 32; + DWORD offsetLo = offset & Q_UINT64_C(0xffffffff); + SYSTEM_INFO sysinfo; + ::GetSystemInfo(&sysinfo); + DWORD mask = sysinfo.dwAllocationGranularity - 1; + DWORD extra = offset & mask; + if (extra) + offsetLo &= ~mask; + + // attempt to create the map + LPVOID mapAddress = ::MapViewOfFile(mapHandle, access, + offsetHi, offsetLo, size + extra); + if (mapAddress) { + uchar *address = extra + static_cast(mapAddress); + maps[address] = extra; + return address; + } + + switch(GetLastError()) { + case ERROR_ACCESS_DENIED: + q->setError(QFile::PermissionsError, qt_error_string()); + break; + case ERROR_INVALID_PARAMETER: + // size are out of bounds + default: + q->setError(QFile::UnspecifiedError, qt_error_string()); + } + + ::CloseHandle(mapHandle); + mapHandle = NULL; + return 0; +} + +bool QFSFileEnginePrivate::unmap(uchar *ptr) +{ + Q_Q(QFSFileEngine); + if (!maps.contains(ptr)) { + q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); + return false; + } + uchar *start = ptr - maps[ptr]; + if (!UnmapViewOfFile(start)) { + q->setError(QFile::PermissionsError, qt_error_string()); + return false; + } + + maps.remove(ptr); + if (maps.isEmpty()) { + ::CloseHandle(mapHandle); + mapHandle = NULL; + } + + return true; +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/qlockfile.cpp b/src/app/core/backport/qlockfile.cpp similarity index 98% rename from src/app/core/qlockfile.cpp rename to src/app/core/backport/qlockfile.cpp index f828e835b..9e46c0566 100644 --- a/src/app/core/qlockfile.cpp +++ b/src/app/core/backport/qlockfile.cpp @@ -210,7 +210,7 @@ bool QLockFile::tryLock(int timeout) if (!d->isLocked && d->isApparentlyStale()) { // Stale lock from another thread/process // Ensure two processes don't remove it at the same time - QLockFile rmlock(d->fileName + QLatin1String(".rmlock")); + QLockFile rmlock(d->fileName + QStringLiteral(".rmlock")); if (rmlock.tryLock()) { if (d->isApparentlyStale() && d->removeStaleLock()) continue; @@ -220,7 +220,7 @@ bool QLockFile::tryLock(int timeout) } if (timeout == 0 || (timeout > 0 && timer.hasExpired(timeout))) return false; - QLockFileThread::msleep(sleepTime); + QThread::msleep(sleepTime); if (sleepTime < 5 * 1000) sleepTime *= 2; } diff --git a/src/app/core/qlockfile.h b/src/app/core/backport/qlockfile.h similarity index 100% rename from src/app/core/qlockfile.h rename to src/app/core/backport/qlockfile.h diff --git a/src/app/core/qlockfile_p.h b/src/app/core/backport/qlockfile_p.h similarity index 100% rename from src/app/core/qlockfile_p.h rename to src/app/core/backport/qlockfile_p.h diff --git a/src/app/core/backport/qlockfile_unix.cpp b/src/app/core/backport/qlockfile_unix.cpp new file mode 100644 index 000000000..080aff9fb --- /dev/null +++ b/src/app/core/backport/qlockfile_unix.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2013 David Faure +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlockfile_p.h" + +#include +#include +#include +#include +#include + +#include "qcore_unix_p.h" // qt_safe_open +#include "qabstractfileengine_p.h" +//#include "qtemporaryfile_p.h" + +#include // flock +#include // kill +#include // kill +#include // gethostname + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +static QByteArray localHostName() // from QHostInfo::localHostName(), modified to return a QByteArray +{ + QByteArray hostName(512, Qt::Uninitialized); + if (gethostname(hostName.data(), hostName.size()) == -1) + return QByteArray(); + hostName.truncate(strlen(hostName.data())); + return hostName; +} + +// ### merge into qt_safe_write? +static qint64 qt_write_loop(int fd, const char *data, qint64 len) +{ + qint64 pos = 0; + while (pos < len) { + const qint64 ret = qt_safe_write(fd, data + pos, len - pos); + if (ret == -1) // e.g. partition full + return pos; + pos += ret; + } + return pos; +} + +int QLockFilePrivate::checkFcntlWorksAfterFlock() +{ +#ifndef QT_NO_TEMPORARYFILE + QTemporaryFile file; + if (!file.open()) + return 0; + const int fd = file.d_func()->engine()->handle(); +#if defined(LOCK_EX) && defined(LOCK_NB) + if (flock(fd, LOCK_EX | LOCK_NB) == -1) // other threads, and other processes on a local fs + return 0; +#endif + struct flock flockData; + flockData.l_type = F_WRLCK; + flockData.l_whence = SEEK_SET; + flockData.l_start = 0; + flockData.l_len = 0; // 0 = entire file + flockData.l_pid = getpid(); + if (fcntl(fd, F_SETLK, &flockData) == -1) // for networked filesystems + return 0; + return 1; +#else + return 0; +#endif +} + +static QBasicAtomicInt fcntlOK = Q_BASIC_ATOMIC_INITIALIZER(-1); + +/*! + \internal + Checks that the OS isn't using POSIX locks to emulate flock(). + Mac OS X is one of those. +*/ +static bool fcntlWorksAfterFlock() +{ + int value = fcntlOK.load(); + if (Q_UNLIKELY(value == -1)) { + value = QLockFilePrivate::checkFcntlWorksAfterFlock(); + fcntlOK.store(value); + } + return value == 1; +} + +static bool setNativeLocks(int fd) +{ +#if defined(LOCK_EX) && defined(LOCK_NB) + if (flock(fd, LOCK_EX | LOCK_NB) == -1) // other threads, and other processes on a local fs + return false; +#endif + struct flock flockData; + flockData.l_type = F_WRLCK; + flockData.l_whence = SEEK_SET; + flockData.l_start = 0; + flockData.l_len = 0; // 0 = entire file + flockData.l_pid = getpid(); + if (fcntlWorksAfterFlock() && fcntl(fd, F_SETLK, &flockData) == -1) // for networked filesystems + return false; + return true; +} + +QLockFile::LockError QLockFilePrivate::tryLock_sys() +{ + // Assemble data, to write in a single call to write + // (otherwise we'd have to check every write call) + // Use operator% from the fast builder to avoid multiple memory allocations. + QByteArray fileData = QByteArray::number(QCoreApplication::applicationPid()) % '\n' + % qAppName().toUtf8() % '\n' + % localHostName() % '\n'; + + const QByteArray lockFileName = QFile::encodeName(fileName); + const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY | O_CREAT | O_EXCL, 0644); + if (fd < 0) { + switch (errno) { + case EEXIST: + return QLockFile::LockFailedError; + case EACCES: + case EROFS: + return QLockFile::PermissionError; + default: + return QLockFile::UnknownError; + } + } + // Ensure nobody else can delete the file while we have it + if (!setNativeLocks(fd)) + qWarning() << "setNativeLocks failed:" << strerror(errno); + + // We hold the lock, continue. + fileHandle = fd; + + QLockFile::LockError error = QLockFile::NoError; + if (qt_write_loop(fd, fileData.constData(), fileData.size()) < fileData.size()) + error = QLockFile::UnknownError; // partition full + return error; +} + +bool QLockFilePrivate::removeStaleLock() +{ + const QByteArray lockFileName = QFile::encodeName(fileName); + const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY, 0644); + if (fd < 0) // gone already? + return false; + bool success = setNativeLocks(fd) && (::unlink(lockFileName) == 0); + close(fd); + return success; +} + +bool QLockFilePrivate::isApparentlyStale() const +{ + qint64 pid; + QString hostname, appname; + if (!getLockInfo(&pid, &hostname, &appname)) + return false; + if (hostname == QString::fromLocal8Bit(localHostName())) { + if (::kill(pid, 0) == -1 && errno == ESRCH) + return true; // PID doesn't exist anymore + } + const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime()); + return staleLockTime > 0 && age > staleLockTime; +} + +void QLockFile::unlock() +{ + Q_D(QLockFile); + if (!d->isLocked) + return; + close(d->fileHandle); + d->fileHandle = -1; + QFile::remove(d->fileName); + d->lockError = QLockFile::NoError; + d->isLocked = false; +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qlockfile_win.cpp b/src/app/core/backport/qlockfile_win.cpp new file mode 100644 index 000000000..87d9b1f5d --- /dev/null +++ b/src/app/core/backport/qlockfile_win.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2013 David Faure +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlockfile_p.h" +#include "qfilesystementry_p.h" +#include + +#include +#include +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +QLockFile::LockError QLockFilePrivate::tryLock_sys() +{ + SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE }; + const QFileSystemEntry fileEntry(fileName); + // When writing, allow others to read. + // When reading, QFile will allow others to read and write, all good. + // Adding FILE_SHARE_DELETE would allow forceful deletion of stale files, + // but Windows doesn't allow recreating it while this handle is open anyway, + // so this would only create confusion (can't lock, but no lock file to read from). + const DWORD dwShareMode = FILE_SHARE_READ; + HANDLE fh = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(), + GENERIC_WRITE, + dwShareMode, + &securityAtts, + CREATE_NEW, // error if already exists + FILE_ATTRIBUTE_NORMAL, + NULL); + if (fh == INVALID_HANDLE_VALUE) { + const DWORD lastError = GetLastError(); + switch (lastError) { + case ERROR_SHARING_VIOLATION: + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + case ERROR_ACCESS_DENIED: // readonly file, or file still in use by another process. Assume the latter, since we don't create it readonly. + return QLockFile::LockFailedError; + default: + qWarning() << "Got unexpected locking error" << lastError; + return QLockFile::UnknownError; + } + } + + // We hold the lock, continue. + fileHandle = fh; + // Assemble data, to write in a single call to write + // (otherwise we'd have to check every write call) + QByteArray fileData; + fileData += QByteArray::number(QCoreApplication::applicationPid()); + fileData += '\n'; + fileData += qAppName().toUtf8(); + fileData += '\n'; + //fileData += localHostname(); // gethostname requires winsock init, see QHostInfo... + fileData += '\n'; + DWORD bytesWritten = 0; + QLockFile::LockError error = QLockFile::NoError; + if (!WriteFile(fh, fileData.constData(), fileData.size(), &bytesWritten, NULL) || !FlushFileBuffers(fh)) + error = QLockFile::UnknownError; // partition full + return error; +} + +bool QLockFilePrivate::removeStaleLock() +{ + // QFile::remove fails on Windows if the other process is still using the file, so it's not stale. + return QFile::remove(fileName); +} + +bool QLockFilePrivate::isApparentlyStale() const +{ + qint64 pid; + QString hostname, appname; + if (!getLockInfo(&pid, &hostname, &appname)) + return false; + + HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (!procHandle) + return true; + // We got a handle but check if process is still alive + DWORD dwR = ::WaitForSingleObject(procHandle, 0); + ::CloseHandle(procHandle); + if (dwR == WAIT_TIMEOUT) + return true; + const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime()); + return staleLockTime > 0 && age > staleLockTime; +} + +void QLockFile::unlock() +{ + Q_D(QLockFile); + if (!d->isLocked) + return; + CloseHandle(d->fileHandle); + QFile::remove(d->fileName); + d->lockError = QLockFile::NoError; + d->isLocked = false; +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qmutexpool.cpp b/src/app/core/backport/qmutexpool.cpp new file mode 100644 index 000000000..df86cda58 --- /dev/null +++ b/src/app/core/backport/qmutexpool.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qatomic.h" +#include "qmutexpool_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifndef QT_NO_THREAD + +Q_GLOBAL_STATIC_WITH_ARGS(QMutexPool, globalMutexPool, (QMutex::Recursive)) + +/*! + \class QMutexPool + \inmodule QtCore + \brief The QMutexPool class provides a pool of QMutex objects. + + \internal + + \ingroup thread + + QMutexPool is a convenience class that provides access to a fixed + number of QMutex objects. + + Typical use of a QMutexPool is in situations where it is not + possible or feasible to use one QMutex for every protected object. + The mutex pool will return a mutex based on the address of the + object that needs protection. + + For example, consider this simple class: + + \snippet code/src_corelib_thread_qmutexpool.cpp 0 + + Adding a QMutex member to the Number class does not make sense, + because it is so small. However, in order to ensure that access to + each Number is protected, you need to use a mutex. In this case, a + QMutexPool would be ideal. + + Code to calculate the square of a number would then look something + like this: + + \snippet code/src_corelib_thread_qmutexpool.cpp 1 + + This function will safely calculate the square of a number, since + it uses a mutex from a QMutexPool. The mutex is locked and + unlocked automatically by the QMutexLocker class. See the + QMutexLocker documentation for more details. +*/ + +/*! + Constructs a QMutexPool, reserving space for \a size QMutexes. All + mutexes in the pool are created with \a recursionMode. By default, + all mutexes are non-recursive. + + The QMutexes are created when needed, and deleted when the + QMutexPool is destructed. +*/ +QMutexPool::QMutexPool(QMutex::RecursionMode recursionMode, int size) + : mutexes(size), recursionMode(recursionMode) +{ + for (int index = 0; index < mutexes.count(); ++index) { + mutexes[index].store(0); + } +} + +/*! + Destructs a QMutexPool. All QMutexes that were created by the pool + are deleted. +*/ +QMutexPool::~QMutexPool() +{ + for (int index = 0; index < mutexes.count(); ++index) + delete mutexes[index].load(); +} + +/*! + Returns the global QMutexPool instance. +*/ +QMutexPool *QMutexPool::instance() +{ + return globalMutexPool(); +} + +/*! + \fn QMutexPool::get(const void *address) + Returns a QMutex from the pool. QMutexPool uses the value \a address + to determine which mutex is returned from the pool. +*/ + +/*! + \internal + create the mutex for the given index + */ +QMutex *QMutexPool::createMutex(int index) +{ + // mutex not created, create one + QMutex *newMutex = new QMutex(recursionMode); + if (!mutexes[index].testAndSetRelease(0, newMutex)) + delete newMutex; + return mutexes[index].load(); +} + +/*! + Returns a QMutex from the global mutex pool. +*/ +QMutex *QMutexPool::globalInstanceGet(const void *address) +{ + QMutexPool * const globalInstance = globalMutexPool(); + if (globalInstance == 0) + return 0; + return globalInstance->get(address); +} + +#endif // QT_NO_THREAD + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qmutexpool_p.h b/src/app/core/backport/qmutexpool_p.h new file mode 100644 index 000000000..72f56e433 --- /dev/null +++ b/src/app/core/backport/qmutexpool_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMUTEXPOOL_P_H +#define QMUTEXPOOL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QSettings. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qatomic.h" +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifndef QT_NO_THREAD + +class QMutexPool +{ +public: + explicit QMutexPool(QMutex::RecursionMode recursionMode = QMutex::NonRecursive, int size = 131); + ~QMutexPool(); + + inline QMutex *get(const void *address) { + int index = uint(quintptr(address)) % mutexes.count(); + QMutex *m = mutexes[index].load(); + if (m) + return m; + else + return createMutex(index); + } + static QMutexPool *instance(); + static QMutex *globalInstanceGet(const void *address); + +private: + QMutex *createMutex(int index); + QVarLengthArray, 131> mutexes; + QMutex::RecursionMode recursionMode; +}; + +#endif // QT_NO_THREAD + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QMUTEXPOOL_P_H diff --git a/src/app/core/backport/qresource.h b/src/app/core/backport/qresource.h new file mode 100644 index 000000000..128f86cbe --- /dev/null +++ b/src/app/core/backport/qresource.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRESOURCE_H +#define QRESOURCE_H + +#include +#include +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +class QResourcePrivate; + +class QResource +{ +public: + QResource(const QString &file=QString(), const QLocale &locale=QLocale()); + ~QResource(); + + void setFileName(const QString &file); + QString fileName() const; + QString absoluteFilePath() const; + + void setLocale(const QLocale &locale); + QLocale locale() const; + + bool isValid() const; + + bool isCompressed() const; + qint64 size() const; + const uchar *data() const; + + static void addSearchPath(const QString &path); + static QStringList searchPaths(); + + static bool registerResource(const QString &rccFilename, const QString &resourceRoot=QString()); + static bool unregisterResource(const QString &rccFilename, const QString &resourceRoot=QString()); + + static bool registerResource(const uchar *rccData, const QString &resourceRoot=QString()); + static bool unregisterResource(const uchar *rccData, const QString &resourceRoot=QString()); + +protected: + friend class QResourceFileEngine; + friend class QResourceFileEngineIterator; + bool isDir() const; + inline bool isFile() const { return !isDir(); } + QStringList children() const; + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(QResource) +}; + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QRESOURCE_H diff --git a/src/app/core/backport/qresource_p.h b/src/app/core/backport/qresource_p.h new file mode 100644 index 000000000..4bc17b83d --- /dev/null +++ b/src/app/core/backport/qresource_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRESOURCE_P_H +#define QRESOURCE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qabstractfileengine_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +class QResourceFileEnginePrivate; +class QResourceFileEngine : public QAbstractFileEngine +{ +private: + Q_DECLARE_PRIVATE(QResourceFileEngine) +public: + explicit QResourceFileEngine(const QString &path); + ~QResourceFileEngine(); + + virtual void setFileName(const QString &file); + + virtual bool open(QIODevice::OpenMode flags) ; + virtual bool close(); + virtual bool flush(); + virtual qint64 size() const; + virtual qint64 pos() const; + virtual bool atEnd() const; + virtual bool seek(qint64); + virtual qint64 read(char *data, qint64 maxlen); + virtual qint64 write(const char *data, qint64 len); + + virtual bool remove(); + virtual bool copy(const QString &newName); + virtual bool rename(const QString &newName); + virtual bool link(const QString &newName); + + virtual bool isSequential() const; + + virtual bool isRelativePath() const; + + virtual bool mkdir(const QString &dirName, bool createParentDirectories) const; + virtual bool rmdir(const QString &dirName, bool recurseParentDirectories) const; + + virtual bool setSize(qint64 size); + + virtual QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const; + + virtual bool caseSensitive() const; + + virtual FileFlags fileFlags(FileFlags type) const; + + virtual bool setPermissions(uint perms); + + virtual QString fileName(QAbstractFileEngine::FileName file) const; + + virtual uint ownerId(FileOwner) const; + virtual QString owner(FileOwner) const; + + virtual QDateTime fileTime(FileTime time) const; + + virtual Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames); + virtual Iterator *endEntryList(); + + bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0); + bool supportsExtension(Extension extension) const; +}; + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QRESOURCE_P_H diff --git a/src/app/core/backport/qsystemerror.cpp b/src/app/core/backport/qsystemerror.cpp new file mode 100644 index 000000000..49ecbd41b --- /dev/null +++ b/src/app/core/backport/qsystemerror.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qsystemerror_p.h" +#if !defined(Q_OS_WINCE) +# include +# if defined(Q_CC_MSVC) +# include +# endif +#else +# if (_WIN32_WCE >= 0x700) +# include +# endif +#endif +#ifdef Q_OS_WIN +# include +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#if !defined(Q_OS_WIN) && !defined(QT_NO_THREAD) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_QNX) && \ + defined(_POSIX_THREAD_SAFE_FUNCTIONS) && _POSIX_VERSION >= 200112L +namespace { + // There are two incompatible versions of strerror_r: + // a) the XSI/POSIX.1 version, which returns an int, + // indicating success or not + // b) the GNU version, which returns a char*, which may or may not + // be the beginning of the buffer we used + // The GNU libc manpage for strerror_r says you should use the XSI + // version in portable code. However, it's impossible to do that if + // _GNU_SOURCE is defined so we use C++ overloading to decide what to do + // depending on the return type + static inline QString fromstrerror_helper(int, const QByteArray &buf) + { + return QString::fromLocal8Bit(buf); + } + static inline QString fromstrerror_helper(const char *str, const QByteArray &) + { + return QString::fromLocal8Bit(str); + } +} +#endif + +#ifdef Q_OS_WIN +static QString windowsErrorString(int errorCode) +{ + QString ret; +#ifndef Q_OS_WINRT + wchar_t *string = 0; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&string, + 0, + NULL); + ret = QString::fromWCharArray(string); + LocalFree((HLOCAL)string); +#else + wchar_t errorString[1024]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&errorString, + sizeof(errorString)/sizeof(wchar_t), + NULL); + ret = QString::fromWCharArray(errorString); +#endif // Q_OS_WINRT + + if (ret.isEmpty() && errorCode == ERROR_MOD_NOT_FOUND) + ret = QString::fromLatin1("The specified module could not be found."); + return ret; +} +#endif + +static QString standardLibraryErrorString(int errorCode) +{ + const char *s = 0; + QString ret; + switch (errorCode) { + case 0: + break; + case EACCES: + s = QT_TRANSLATE_NOOP("QIODevice", "Permission denied"); + break; + case EMFILE: + s = QT_TRANSLATE_NOOP("QIODevice", "Too many open files"); + break; + case ENOENT: + s = QT_TRANSLATE_NOOP("QIODevice", "No such file or directory"); + break; + case ENOSPC: + s = QT_TRANSLATE_NOOP("QIODevice", "No space left on device"); + break; + default: { + #ifdef Q_OS_WINCE + ret = windowsErrorString(errorCode); + #else + #if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && _POSIX_VERSION >= 200112L && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_QNX) + QByteArray buf(1024, '\0'); + ret = fromstrerror_helper(strerror_r(errorCode, buf.data(), buf.size()), buf); + #else + ret = QString::fromLocal8Bit(strerror(errorCode)); + #endif + #endif + break; } + } + if (s) { + // ######## this breaks moc build currently + // ret = QCoreApplication::translate("QIODevice", s); + ret = QString::fromLatin1(s); + } + return ret.trimmed(); +} + +QString QSystemError::toString() +{ + switch(errorScope) { + case NativeError: +#if defined (Q_OS_WIN) + return windowsErrorString(errorCode); +#else + //unix: fall through as native and standard library are the same +#endif + case StandardLibraryError: + return standardLibraryErrorString(errorCode); + default: + qWarning("invalid error scope"); + //fall through + case NoError: + return QLatin1String("No error"); + } +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) diff --git a/src/app/core/backport/qsystemerror_p.h b/src/app/core/backport/qsystemerror_p.h new file mode 100644 index 000000000..eae76df6b --- /dev/null +++ b/src/app/core/backport/qsystemerror_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSYSTEMERROR_P_H +#define QSYSTEMERROR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +class QSystemError +{ +public: + enum ErrorScope + { + NoError, + StandardLibraryError, + NativeError + }; + + inline QSystemError(int error, ErrorScope scope); + inline QSystemError(); + + QString toString(); + inline ErrorScope scope(); + inline int error(); + + //data members + int errorCode; + ErrorScope errorScope; +}; + +QSystemError::QSystemError(int error, QSystemError::ErrorScope scope) +: errorCode(error), errorScope(scope) +{ + +} + +QSystemError::QSystemError() +: errorCode(0), errorScope(NoError) +{ + +} + +QSystemError::ErrorScope QSystemError::scope() +{ + return errorScope; +} + +int QSystemError::error() +{ + return errorCode; +} + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif // QSYSTEMERROR_P_H diff --git a/src/app/core/backport/qsystemlibrary_p.h b/src/app/core/backport/qsystemlibrary_p.h new file mode 100644 index 000000000..e11d61545 --- /dev/null +++ b/src/app/core/backport/qsystemlibrary_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** $QT_BEGIN_LICENSE:GPL$ +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSYSTEMLIBRARY_P_H +#define QSYSTEMLIBRARY_P_H + +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#ifdef Q_OS_WIN +# include +# include + +class QSystemLibrary +{ +public: + explicit QSystemLibrary(const QString &libraryName) + { + m_libraryName = libraryName; + m_handle = 0; + m_didLoad = false; + } + + explicit QSystemLibrary(const wchar_t *libraryName) + { + m_libraryName = QString::fromWCharArray(libraryName); + m_handle = 0; + m_didLoad = false; + } + + bool load(bool onlySystemDirectory = true) + { + m_handle = load((const wchar_t *)m_libraryName.utf16(), onlySystemDirectory); + m_didLoad = true; + return (m_handle != 0); + } + + bool isLoaded() + { + return (m_handle != 0); + } + + QFunctionPointer resolve(const char *symbol) + { + if (!m_didLoad) + load(); + if (!m_handle) + return 0; +#ifdef Q_OS_WINCE + return QFunctionPointer(GetProcAddress(m_handle, (const wchar_t*)QString::fromLatin1(symbol).utf16())); +#else + return QFunctionPointer(GetProcAddress(m_handle, symbol)); +#endif + } + + static QFunctionPointer resolve(const QString &libraryName, const char *symbol) + { + return QSystemLibrary(libraryName).resolve(symbol); + } + + static Q_CORE_EXPORT HINSTANCE load(const wchar_t *lpFileName, bool onlySystemDirectory = true); +private: + HINSTANCE m_handle; + QString m_libraryName; + bool m_didLoad; +}; + +#endif //Q_OS_WIN + +#endif //QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + +#endif //QSYSTEMLIBRARY_P_H diff --git a/src/app/core/core.pri b/src/app/core/core.pri index 90169c4de..7952cf82a 100644 --- a/src/app/core/core.pri +++ b/src/app/core/core.pri @@ -5,15 +5,67 @@ HEADERS += \ $$PWD/vapplication.h \ $$PWD/undoevent.h \ $$PWD/vsettings.h \ - $$PWD/qcommandlineoption.h \ - $$PWD/qcommandlineparser.h \ - $$PWD/qlockfile.h \ - $$PWD/qlockfile_p.h + $$PWD/backport/qcommandlineoption.h \ + $$PWD/backport/qcommandlineparser.h \ + $$PWD/backport/qlockfile.h \ + $$PWD/backport/qlockfile_p.h \ + $$PWD/backport/qfilesystementry_p.h \ + $$PWD/backport/qfsfileengine_p.h \ + $$PWD/backport/qabstractfileengine_p.h \ + $$PWD/backport/qresource_p.h \ + $$PWD/backport/qresource.h \ + $$PWD/backport/qfilesystemmetadata_p.h \ + $$PWD/backport/qfilesystemengine_p.h \ + $$PWD/backport/qsystemerror_p.h \ + $$PWD/backport/qfsfileengine_iterator_p.h \ + $$PWD/backport/qfilesystemiterator_p.h \ + $$PWD/backport/qfileinfo_p.h \ + $$PWD/backport/qmutexpool_p.h \ + $$PWD/backport/qsystemlibrary_p.h \ + +unix:!macx { + HEADERS += \ + $$PWD/backport/qcore_unix_p.h +} + +unix:macx { + HEADERS += \ + $$PWD/backport/qcore_mac_p.h +} SOURCES += \ $$PWD/vapplication.cpp \ $$PWD/undoevent.cpp \ $$PWD/vsettings.cpp \ - $$PWD/qcommandlineoption.cpp \ - $$PWD/qcommandlineparser.cpp \ - $$PWD/qlockfile.cpp + $$PWD/backport/qcommandlineoption.cpp \ + $$PWD/backport/qcommandlineparser.cpp \ + $$PWD/backport/qlockfile.cpp \ + $$PWD/backport/qfilesystementry.cpp \ + $$PWD/backport/qfsfileengine.cpp \ + $$PWD/backport/qabstractfileengine.cpp \ + $$PWD/backport/qfilesystemengine.cpp \ + $$PWD/backport/qsystemerror.cpp \ + $$PWD/backport/qfsfileengine_iterator.cpp \ + $$PWD/backport/qmutexpool.cpp + +unix:!macx { + SOURCES += \ + $$PWD/backport/qlockfile_unix.cpp \ + $$PWD/backport/qfilesystemiterator_unix.cpp \ + $$PWD/backport/qfsfileengine_unix.cpp \ + $$PWD/backport/qfilesystemengine_unix.cpp \ + $$PWD/backport/qcore_unix.cpp +} + +unix:macx { + SOURCES += \ + $$PWD/backport/qcore_mac.cpp +} + +win32 { + SOURCES += \ + $$PWD/backport/qlockfile_win.cpp \ + $$PWD/backport/qfilesystemiterator_win.cpp \ + $$PWD/backport/qfsfileengine_win.cpp \ + $$PWD/backport/qfilesystemengine_win.cpp +} diff --git a/src/app/core/vapplication.cpp b/src/app/core/vapplication.cpp index b8849fd1f..6df89241e 100644 --- a/src/app/core/vapplication.cpp +++ b/src/app/core/vapplication.cpp @@ -50,7 +50,7 @@ #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) -# include "qlockfile.h" +# include "backport/qlockfile.h" #else # include #endif diff --git a/src/app/main.cpp b/src/app/main.cpp index 83e2e85f8..1bf5555fb 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -36,7 +36,7 @@ #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) -# include "../core/qcommandlineparser.h" +# include "../core/backport/qcommandlineparser.h" #else # include #endif diff --git a/src/app/mainwindow.cpp b/src/app/mainwindow.cpp index 8ba02bc51..64043c548 100644 --- a/src/app/mainwindow.cpp +++ b/src/app/mainwindow.cpp @@ -62,7 +62,7 @@ #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) -# include "core/qlockfile.h" +# include "core/backport/qlockfile.h" #else # include #endif