From c72cbcf173ce8df1f1d04a37f9aa5e261751a0cd Mon Sep 17 00:00:00 2001 From: Martin Kampas Date: Fri, 5 Apr 2013 12:50:47 +0200 Subject: [PATCH] Sandbox: allow to easy add world files --- libssu/sandbox.cpp | 161 ++++++++++++++++++++++++++++++++++++++++++++- libssu/sandbox_p.h | 9 +++ 2 files changed, 169 insertions(+), 1 deletion(-) diff --git a/libssu/sandbox.cpp b/libssu/sandbox.cpp index 47327a2..fa50222 100644 --- a/libssu/sandbox.cpp +++ b/libssu/sandbox.cpp @@ -24,10 +24,16 @@ class Sandbox::FileEngineHandler : public QAbstractFileEngineHandler { public: FileEngineHandler(const QString &sandboxPath); + void enableDirectoryOverlay(const QString &path); + bool isDirectoryOverlayEnabled(const QString &path) const; + + public: + // QAbstractFileEngineHandler QAbstractFileEngine *create(const QString &fileName) const; private: const QString m_sandboxPath; + QSet m_overlayEnabledDirectories; }; /** @@ -146,6 +152,149 @@ Sandbox::~Sandbox(){ s_instance = 0; } +bool Sandbox::isActive() const{ + return m_handler != 0; +} + +/** + * Copies selected files into sandbox. Existing files in sandbox are not overwriten. + * + * @c QDir::NoDotAndDotDot is always added into @a filters. + */ +void Sandbox::addWorldFiles(const QString &directory, QDir::Filters filters, + const QStringList &filterNames){ + Q_ASSERT(!directory.isEmpty()); + Q_ASSERT(directory.startsWith('/')); + Q_ASSERT(!directory.contains(':')); // separator in environment variable + Q_ASSERT(!directory.contains("/./") && !directory.endsWith("/.") + && !directory.contains("/../") && !directory.endsWith("/..") + && !directory.contains("//")); + Q_ASSERT_X(!(m_scopes & ChildProcesses), Q_FUNC_INFO, "Unimplemented case!"); + + if (!isActive()){ + qDebug("%s: Sandbox is not active", Q_FUNC_INFO); + return; + } + + const QString sandboxedDirectory = m_sandboxPath + directory; + + QFSFileEngine worldDirectoryEngine(directory); + QFSFileEngine sandboxDirectoryEngine(m_sandboxPath); + QFSFileEngine sandboxedDirectoryEngine(sandboxedDirectory); + + const QAbstractFileEngine::FileFlags worldDirectoryFlags = + worldDirectoryEngine.fileFlags( + QAbstractFileEngine::ExistsFlag | QAbstractFileEngine::DirectoryType); + + if (!(worldDirectoryFlags & QAbstractFileEngine::ExistsFlag)){ + qDebug("%s: Directory does not exist: '%s'", Q_FUNC_INFO, qPrintable(directory)); + return; + } + + if (!(worldDirectoryFlags & QAbstractFileEngine::DirectoryType)){ + qFatal("%s: Is not a directory: '%s'", Q_FUNC_INFO, qPrintable(directory)); + } + + const QAbstractFileEngine::FileFlags sandboxedDirectoryFlags = + sandboxedDirectoryEngine.fileFlags( + QAbstractFileEngine::ExistsFlag | QAbstractFileEngine::DirectoryType); + + if (!(sandboxedDirectoryFlags & QAbstractFileEngine::ExistsFlag)){ + if (!sandboxedDirectoryEngine.mkdir(sandboxedDirectory, true)){ + qFatal("%s: Failed to create sandbox directory '%s': %s", Q_FUNC_INFO, + qPrintable(sandboxedDirectory), qPrintable(sandboxedDirectoryEngine.errorString())); + } + } else if (!(sandboxedDirectoryFlags & QAbstractFileEngine::DirectoryType)){ + qFatal("%s: Failed to create sandbox directory '%s': Is not a directory", Q_FUNC_INFO, + qPrintable(sandboxedDirectory)); + } + + if (filters == QDir::NoFilter){ + filters = QDir::AllEntries; + } + + filters |= QDir::NoDotAndDotDot; + + foreach (const QString &entryName, worldDirectoryEngine.entryList(filters, filterNames)){ + + QFSFileEngine worldEntryEngine(directory + '/' + entryName); + const QAbstractFileEngine::FileFlags worldEntryFlags = worldEntryEngine.fileFlags( + QAbstractFileEngine::DirectoryType | QAbstractFileEngine::FileType); + + QFSFileEngine sandboxEntryEngine(sandboxedDirectory + '/' + entryName); + const QAbstractFileEngine::FileFlags sandboxEntryFlags = sandboxEntryEngine.fileFlags( + QAbstractFileEngine::ExistsFlag | QAbstractFileEngine::DirectoryType); + + if (worldEntryFlags & QAbstractFileEngine::DirectoryType){ + if (!(sandboxEntryFlags & QAbstractFileEngine::ExistsFlag)){ + if (!sandboxedDirectoryEngine.mkdir(entryName, false)){ + qFatal("%s: Failed to create overlay directory '%s/%s': %s", Q_FUNC_INFO, + qPrintable(sandboxedDirectory), qPrintable(entryName), + qPrintable(sandboxedDirectoryEngine.errorString())); + } + } else if (!(sandboxEntryFlags & QAbstractFileEngine::DirectoryType)){ + qFatal("%s: Failed to create sandboxed copy '%s/%s': Is not a directory", Q_FUNC_INFO, + qPrintable(sandboxedDirectory), qPrintable(entryName)); + } + } else if (worldEntryFlags & QAbstractFileEngine::FileType){ + if (!(sandboxEntryFlags & QAbstractFileEngine::ExistsFlag)){ + if (!copyFile(&worldEntryEngine, &sandboxEntryEngine)){ + return; + } + } else if (sandboxEntryFlags & QAbstractFileEngine::DirectoryType){ + qFatal("%s: Failed to create sandboxed copy '%s/%s': Is a directory", Q_FUNC_INFO, + qPrintable(sandboxedDirectory), qPrintable(entryName)); + } + } else{ + qFatal("%s: Failed to create sandboxed copy '%s/%s': " + "Can only copy regular files and directories", Q_FUNC_INFO, + qPrintable(sandboxedDirectory), qPrintable(entryName)); + } + } + + m_handler->enableDirectoryOverlay(directory); +} + +bool Sandbox::copyFile(QAbstractFileEngine *src, QAbstractFileEngine *dst){ + if (!src->open(QIODevice::ReadOnly)){ + qFatal("%s: Failed to create sandbox copy: '%s': Cannot open source file for reading", + Q_FUNC_INFO, qPrintable(src->fileName())); + return false; + } + + if (!dst->open(QIODevice::ReadWrite)){ + qFatal("%s: Failed to create sandbox copy: '%s': Cannot open file for writing", + Q_FUNC_INFO, qPrintable(dst->fileName())); + return false; + } + + char buf[4096]; + qint64 totalRead = 0; + while (!src->atEnd()){ + qint64 read = src->read(buf, sizeof(buf)); + if (read <= 0){ + break; + } + totalRead += read; + if (dst->write(buf, read) != read){ + qFatal("%s: Failed to create sandbox copy: '%s': Write error", Q_FUNC_INFO, + qPrintable(src->fileName())); + return false; + } + } + + if (totalRead != src->size()){ + qFatal("%s: Failed to create sandbox copy: '%s': Read/write error", Q_FUNC_INFO, + qPrintable(src->fileName())); + return false; + } + + src->close(); + dst->close(); + + return true; +} + /* * @class Sandbox::FileEngineHandler */ @@ -155,6 +304,14 @@ Sandbox::FileEngineHandler::FileEngineHandler(const QString &sandboxPath): Q_ASSERT(!sandboxPath.isEmpty()); } +void Sandbox::FileEngineHandler::enableDirectoryOverlay(const QString &path){ + m_overlayEnabledDirectories.insert(path); +} + +bool Sandbox::FileEngineHandler::isDirectoryOverlayEnabled(const QString &path) const{ + return m_overlayEnabledDirectories.contains(path); +} + QAbstractFileEngine *Sandbox::FileEngineHandler::create(const QString &fileName) const{ Q_ASSERT(!m_sandboxPath.isEmpty()); @@ -173,7 +330,9 @@ QAbstractFileEngine *Sandbox::FileEngineHandler::create(const QString &fileName) } if (flags & QAbstractFileEngine::DirectoryType){ - return 0; + if (!isDirectoryOverlayEnabled(fileName)){ + return 0; + } } return sandboxedFileEngine.take(); diff --git a/libssu/sandbox_p.h b/libssu/sandbox_p.h index b817f4d..ea84e19 100644 --- a/libssu/sandbox_p.h +++ b/libssu/sandbox_p.h @@ -8,6 +8,7 @@ #ifndef _SANDBOX_P_H #define _SANDBOX_P_H +#include #include class Sandbox { @@ -30,6 +31,14 @@ class Sandbox { Sandbox(const QString &sandboxPath, Usage usage, Scopes scopes); ~Sandbox(); + bool isActive() const; + + void addWorldFiles(const QString &directory, QDir::Filters filters = QDir::NoFilter, + const QStringList &filterNames = QStringList()); + + private: + bool copyFile(QAbstractFileEngine *src, QAbstractFileEngine *dst); + private: static Sandbox *s_instance; QString m_sandboxPath;