diff options
Diffstat (limited to 'launcher')
| -rw-r--r-- | launcher/FileSystem.cpp | 221 | ||||
| -rw-r--r-- | launcher/FileSystem.h | 35 | ||||
| -rw-r--r-- | launcher/filelink/FileLink.cpp | 134 | ||||
| -rw-r--r-- | launcher/filelink/FileLink.h | 6 | 
4 files changed, 285 insertions, 111 deletions
| diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 9e51f932..c48a3bba 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -66,7 +66,6 @@  #include <string>  //for ShellExecute  #include <shlobj.h> -//#include <shlwapi.h>  #include <objbase.h>  #include <Shellapi.h>  #else @@ -228,94 +227,120 @@ bool copy::operator()(const QString& offset, bool dryRun)  bool create_link::operator()(const QString& offset, bool dryRun)   { +    m_linked = 0;  // reset counter +    m_path_results.clear(); +    m_links_to_make.clear(); + +    m_path_results.clear(); +     +    make_link_list(offset); +     +    if (!dryRun) +        return make_links(); -    for (auto pair : m_path_pairs) { -        if (!make_link(pair.src, pair.dst, offset, dryRun)) { -            return false; -        } -    }      return true;  }  /** - * @brief links a directory and it's contents from src to dest + * @brief make a list off all the  links ot make   * @param offset subdirectory form src to link to dest   * @return if there was an error during the attempt to link   */ -bool create_link::make_link(const QString& srcPath, const QString& dstPath, const QString& offset, bool dryRun) +void create_link::make_link_list( const QString& offset)  { -    m_linked = 0;  // reset counter +    for (auto pair : m_path_pairs) { +        const QString& srcPath = pair.src; +        const QString& dstPath = pair.dst; -    auto src = PathCombine(QDir(srcPath).absolutePath(), offset); -    auto dst = PathCombine(QDir(dstPath).absolutePath(), offset); +        auto src = PathCombine(QDir(srcPath).absolutePath(), offset); +        auto dst = PathCombine(QDir(dstPath).absolutePath(), offset); -    // you can't hard link a directory so make sure if we deal with a directory we do so recursively -    if (m_useHardLinks) -        m_recursive = true; +        // you can't hard link a directory so make sure if we deal with a directory we do so recursively +        if (m_useHardLinks) +            m_recursive = true; -    // Function that'll do the actual linking -    auto link_file = [&](QString src_path, QString relative_dst_path) { -        if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) -            return; +        // Function that'll do the actual linking +        auto link_file = [&](QString src_path, QString relative_dst_path) { +            if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) { +                qDebug() << "path" << relative_dst_path << "in black list or not in whitelist"; +                return; +            } +                 -        auto dst_path = PathCombine(dst, relative_dst_path); -        if (!dryRun) { -             -            ensureFilePathExists(dst_path); -            if (m_useHardLinks) { -                if (m_debug) -                    qDebug() << "making hard link:" << src_path << "to" << dst_path; -                fs::create_hard_link(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), m_os_err); -            } else if (fs::is_directory(StringUtils::toStdString(src_path))) { -                if (m_debug) -                    qDebug() << "making directory_symlink:" << src_path << "to" << dst_path; -                fs::create_directory_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), m_os_err); -            } else { -                if (m_debug) -                    qDebug() << "making symlink:" << src_path << "to" << dst_path; -                fs::create_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), m_os_err); +            auto dst_path = PathCombine(dst, relative_dst_path); +            LinkPair link = {src_path, dst_path}; +            m_links_to_make.append(link);  +        }; +         +        if ((!m_recursive) || !fs::is_directory(StringUtils::toStdString(src))) { +            if (m_debug) +                qDebug() << "linking single file or dir:" << src << "to" << dst; +            link_file(src, ""); +        } else { +            if (m_debug) +                qDebug() << "linking recursivly:" << src << "to" << dst; +            QDir src_dir(src); +            QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories); + +            while (source_it.hasNext()) { +                auto src_path = source_it.next(); +                auto relative_path = src_dir.relativeFilePath(src_path); + +                link_file(src_path, relative_path);              } -                     } +    }   +} + +bool create_link::make_links() +{ +    for (auto link : m_links_to_make) { + +        QString src_path = link.src; +        QString dst_path = link.dst; + +        ensureFilePathExists(dst_path); +        if (m_useHardLinks) { +            if (m_debug) +                qDebug() << "making hard link:" << src_path << "to" << dst_path; +            fs::create_hard_link(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), m_os_err); +        } else if (fs::is_directory(StringUtils::toStdString(src_path))) { +            if (m_debug) +                qDebug() << "making directory_symlink:" << src_path << "to" << dst_path; +            fs::create_directory_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), m_os_err); +        } else { +            if (m_debug) +                qDebug() << "making symlink:" << src_path << "to" << dst_path; +            fs::create_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), m_os_err); +        } +            +          if (m_os_err) {              qWarning() << "Failed to link files:" << QString::fromStdString(m_os_err.message());              qDebug() << "Source file:" << src_path;              qDebug() << "Destination file:" << dst_path;              qDebug() << "Error catagory:" << m_os_err.category().name();              qDebug() << "Error code:" << m_os_err.value(); -            emit linkFailed(src_path, dst_path,  m_os_err); +            emit linkFailed(src_path, dst_path, QString::fromStdString(m_os_err.message()), m_os_err.value());          } else {              m_linked++; -            emit fileLinked(relative_dst_path); -        } -         -    }; -     -    if ((!m_recursive) || !fs::is_directory(StringUtils::toStdString(src))) { -        if (m_debug) -            qDebug() << "linking single file or dir:" << src << "to" << dst; -        link_file(src, ""); -    } else { -        if (m_debug) -            qDebug() << "linking recursivly:" << src << "to" << dst; -        QDir src_dir(src); -        QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories); - -        while (source_it.hasNext()) { -            auto src_path = source_it.next(); -            auto relative_path = src_dir.relativeFilePath(src_path); - -            link_file(src_path, relative_path); -            if (m_os_err) return false; +            emit fileLinked(src_path, dst_path);          } +        if (m_os_err) return false;      } - -    return m_os_err.value() == 0; +    return true;  } -bool create_link::runPrivlaged(const QString& offset) +void create_link::runPrivlaged(const QString& offset)  { +    m_linked = 0;  // reset counter +    m_path_results.clear(); +    m_links_to_make.clear(); + +    bool gotResults = false; + +    make_link_list(offset);      QString serverName = BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink_server" + StringUtils::getRandomAlphaNumeric(8); @@ -325,25 +350,72 @@ bool create_link::runPrivlaged(const QString& offset)          // construct block of data to send          QByteArray block;          QDataStream out(&block, QIODevice::WriteOnly); -        out.setVersion(QDataStream::Qt_5_15); // choose correct version better? +        out.setVersion(QDataStream::Qt_5_0); // choose correct version better?          qint32 blocksize = quint32(sizeof(quint32)); -        for (auto pair : m_path_pairs) { -            blocksize += quint32(pair.src.size());  -            blocksize += quint32(pair.dst.size()); +        for (auto link : m_links_to_make) { +            blocksize += quint32(link.src.size());  +            blocksize += quint32(link.dst.size());          }          qDebug() << "About to write block of size:" << blocksize;          out << blocksize; -        out << quint32(m_path_pairs.length()); -        for (auto pair : m_path_pairs) { -            out << pair.src;    -            out << pair.dst; +        out << quint32(m_links_to_make.length()); +        for (auto link : m_links_to_make) { +            out << link.src;    +            out << link.dst;          }          QLocalSocket *clientConnection = m_linkServer.nextPendingConnection();          connect(clientConnection, &QLocalSocket::disconnected,                  clientConnection, &QLocalSocket::deleteLater); +         +        connect(clientConnection, &QLocalSocket::readyRead, this, [&, clientConnection](){ +            QDataStream in; +            quint32 blockSize = 0; +            in.setDevice(clientConnection); +            in.setVersion(QDataStream::Qt_5_0); +            qDebug() << "Reading path results from client"; +            qDebug() << "bytes avalible" << clientConnection->bytesAvailable(); + +            // Relies on the fact that QDataStream serializes a quint32 into +            // sizeof(quint32) bytes +            if (clientConnection->bytesAvailable() < (int)sizeof(quint32)) +                return; +            qDebug() << "reading block size"; +            in >> blockSize; + +            qDebug() << "blocksize is" << blockSize; +            qDebug() << "bytes avalible" << clientConnection->bytesAvailable(); +            if (clientConnection->bytesAvailable() < blockSize || in.atEnd()) +                return; +             +            quint32 numResults; +            in >> numResults; +            qDebug() << "numResults" << numResults; + +            for(int i = 0; i < numResults; i++) { +                FS::LinkResult result; +                in >> result.src; +                in >> result.dst; +                in >> result.err_msg; +                qint32 err_value; +                in >>  err_value; +                result.err_value = err_value; +                if (result.err_value) { +                    qDebug() << "privlaged link fail" << result.src << "to" << result.dst << "code" << result.err_value << result.err_msg; +                    emit linkFailed(result.src, result.dst, result.err_msg, result.err_value); +                } else { +                    qDebug() << "privlaged link success" << result.src << "to" << result.dst; +                    m_linked++; +                    emit fileLinked(result.src, result.dst); +                } +                m_path_results.append(result); +            } +            gotResults = true; +            qDebug() << "results recieved, closing connection"; +            clientConnection->close(); +        });          qint64 byteswritten = clientConnection->write(block);          bool bytesflushed = clientConnection->flush(); @@ -354,20 +426,15 @@ bool create_link::runPrivlaged(const QString& offset)      qDebug() << "Listening on pipe" << serverName;      if (!m_linkServer.listen(serverName)) {          qDebug() << "Unable to start local pipe server on" << serverName << ":" << m_linkServer.errorString(); -        return false; +        return;      } -    ExternalLinkFileProcess *linkFileProcess = new ExternalLinkFileProcess(serverName, this); -    connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&](){ -        emit finishedPrivlaged(); -    }); +    ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this); +    connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&]() { emit finishedPrivlaged(gotResults); });      connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater);      linkFileProcess->start(); -    // linkFileProcess->wait(); - -    return true;  } @@ -375,6 +442,8 @@ void ExternalLinkFileProcess::runLinkFile() {      QString fileLinkExe = PathCombine(QCoreApplication::instance()->applicationDirPath(),  BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink");      QString params = "-s " + m_server; +    params += " -H " + QVariant(m_useHardLinks).toString(); +  #if defined Q_OS_WIN32      SHELLEXECUTEINFO ShExecInfo;      HRESULT hr; diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index b15d1685..2e739298 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -133,13 +133,22 @@ struct LinkPair {      QString dst;  }; -class ExternalLinkFileProcess : public QThread -{ +struct LinkResult { +    QString src; +    QString dst; +    QString err_msg; +    int err_value; +}; + +class ExternalLinkFileProcess : public QThread {      Q_OBJECT     public: -    ExternalLinkFileProcess(QString server, QObject* parent = nullptr) : QThread(parent), m_server(server) {} +    ExternalLinkFileProcess(QString server, bool useHardLinks, QObject* parent = nullptr) +        : QThread(parent), m_server(server), m_useHardLinks(useHardLinks) +    {} -    void run() override { +    void run() override +    {          runLinkFile();          emit processExited();      } @@ -150,6 +159,8 @@ class ExternalLinkFileProcess : public QThread     private:      void runLinkFile(); +    bool m_useHardLinks = false; +      QString m_server;  }; @@ -200,19 +211,21 @@ class create_link : public QObject {      bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } -    bool runPrivlaged() { return runPrivlaged(QString()); } -    bool runPrivlaged(const QString& offset); +    void runPrivlaged() { runPrivlaged(QString()); } +    void runPrivlaged(const QString& offset);      int totalLinked() { return m_linked; }     signals: -    void fileLinked(const QString& relativeName); -    void linkFailed(const QString& srcName, const QString& dstName, std::error_code err); -    void finishedPrivlaged(); +    void fileLinked(const QString& srcName, const QString& dstName); +    void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value); +    void finishedPrivlaged(bool gotResults); +    void finished();     private:      bool operator()(const QString& offset, bool dryRun = false); -    bool make_link(const QString& src_path, const QString& dst_path, const QString& offset, bool dryRun); +    void make_link_list(const QString& offset); +    bool make_links();     private:      bool m_useHardLinks = false; @@ -221,6 +234,8 @@ class create_link : public QObject {      bool m_recursive = true;      QList<LinkPair> m_path_pairs; +    QList<LinkResult> m_path_results; +    QList<LinkPair> m_links_to_make;      int m_linked;      bool m_debug = false; diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index 78486507..a731ecdb 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -23,6 +23,8 @@  #include "FileLink.h"  #include "BuildConfig.h" +#include "StringUtils.h" +  #include <iostream> @@ -43,6 +45,24 @@  #include <stdio.h>  #endif +// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header + +#ifdef __APPLE__ +#include <Availability.h> // for deployment target to support pre-catalina targets without std::fs +#endif // __APPLE__ + +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include <filesystem> +namespace fs = std::filesystem; +#endif // MacOS min version check +#endif // Other OSes version check + +#ifndef GHC_USE_STD_FS +#include <ghc/filesystem.hpp> +namespace fs = ghc::filesystem; +#endif @@ -82,7 +102,8 @@ FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv),      parser.setApplicationDescription(QObject::tr("a batch MKLINK program for windows to be used with prismlauncher"));      parser.addOptions({ -        {{"s", "server"}, "Join the specified server on launch", "pipe name"} +        {{"s", "server"}, "Join the specified server on launch", "pipe name"}, +        {{"H", "hard"}, "use hard links insted of symbolic", "true/false"}      });      parser.addHelpOption();      parser.addVersionOption(); @@ -90,6 +111,7 @@ FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv),      parser.process(arguments());      QString serverToJoin = parser.value("server"); +    m_useHardLinks = QVariant(parser.value("hard")).toBool();      qDebug() << "link program launched"; @@ -109,7 +131,7 @@ void FileLinkApp::joinServer(QString server)      blockSize = 0;       in.setDevice(&socket); -    in.setVersion(QDataStream::Qt_5_15); +    in.setVersion(QDataStream::Qt_5_0);      connect(&socket, &QLocalSocket::connected, this, [&](){          qDebug() << "connected to server"; @@ -120,25 +142,27 @@ void FileLinkApp::joinServer(QString server)      connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError){          switch (socketError) {          case QLocalSocket::ServerNotFoundError: -            qDebug() << tr("The host was not found. Please make sure " -                            "that the server is running and that the " -                            "server name is correct."); +            qDebug() << ("The host was not found. Please make sure " +                        "that the server is running and that the " +                        "server name is correct.");              break;          case QLocalSocket::ConnectionRefusedError: -            qDebug() << tr("The connection was refused by the peer. " -                            "Make sure the server is running, " -                            "and check that the server name " -                            "is correct."); +            qDebug() << ("The connection was refused by the peer. " +                        "Make sure the server is running, " +                        "and check that the server name " +                        "is correct.");              break;          case QLocalSocket::PeerClosedError: +            qDebug() << ("The connection was closed by the peer. ");              break;          default: -            qDebug() << tr("The following error occurred: %1.").arg(socket.errorString()); +            qDebug() << "The following error occurred: " << socket.errorString();          }      });      connect(&socket, &QLocalSocket::disconnected, this, [&](){ -        qDebug() << "dissconnected from server"; +        qDebug() << "dissconnected from server, should exit"; +        exit();      });      socket.connectToServer(server); @@ -147,20 +171,82 @@ void FileLinkApp::joinServer(QString server)  }  void FileLinkApp::runLink() +{    + +    std::error_code os_err; + +    qDebug() << "creating links"; + +    for (auto link : m_links_to_make) { + +        QString src_path = link.src; +        QString dst_path = link.dst; + +        FS::ensureFilePathExists(dst_path); +        if (m_useHardLinks) { +            qDebug() << "making hard link:" << src_path << "to" << dst_path; +            fs::create_hard_link(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), os_err); +        } else if (fs::is_directory(StringUtils::toStdString(src_path))) { +            qDebug() << "making directory_symlink:" << src_path << "to" << dst_path; +            fs::create_directory_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), os_err); +        } else { +            qDebug() << "making symlink:" << src_path << "to" << dst_path; +            fs::create_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), os_err); +        }     + +        if (os_err) { +            qWarning() << "Failed to link files:" << QString::fromStdString(os_err.message()); +            qDebug() << "Source file:" << src_path; +            qDebug() << "Destination file:" << dst_path; +            qDebug() << "Error catagory:" << os_err.category().name(); +            qDebug() << "Error code:" << os_err.value(); + +            FS::LinkResult result = {src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value()}; +            m_path_results.append(result); +        } else { +            FS::LinkResult result = {src_path, dst_path}; +            m_path_results.append(result); +        } +    } + +    sendResults(); +    qDebug() << "done, should exit soon"; +     +} + +void FileLinkApp::sendResults()  { -    qDebug() << "creating link"; -    FS::create_link lnk(m_path_pairs); -    lnk.debug(true); -    if (!lnk()) { -        qDebug() << "Link Failed!" << lnk.getOSError().value() << lnk.getOSError().message().c_str(); +    // construct block of data to send +    QByteArray block; +    QDataStream out(&block, QIODevice::WriteOnly); +    out.setVersion(QDataStream::Qt_5_0); + +    qint32 blocksize = quint32(sizeof(quint32)); +    for (auto result : m_path_results) { +        blocksize += quint32(result.src.size()); +        blocksize += quint32(result.dst.size()); +        blocksize += quint32(result.err_msg.size()); +        blocksize += quint32(sizeof(quint32));      } -    //exit(); -    qDebug() << "done, should exit"; +    qDebug() << "About to write block of size:" << blocksize; +    out << blocksize; + +    out << quint32(m_path_results.length()); +    for (auto result : m_path_results) { +        out << result.src; +        out << result.dst; +        out << result.err_msg; +        out << quint32(result.err_value); +    } + +    qint64 byteswritten = socket.write(block); +    bool bytesflushed = socket.flush(); +    qDebug() << "block flushed" << byteswritten << bytesflushed;  }  void FileLinkApp::readPathPairs()  {    -    m_path_pairs.clear(); +    m_links_to_make.clear();      qDebug() << "Reading path pairs from server";      qDebug() << "bytes avalible" << socket.bytesAvailable();      if (blockSize == 0) { @@ -176,16 +262,16 @@ void FileLinkApp::readPathPairs()      if (socket.bytesAvailable() < blockSize || in.atEnd())          return; -    quint32 numPairs; -    in >> numPairs; -    qDebug() << "numPairs" << numPairs; +    quint32 numLinks; +    in >> numLinks; +    qDebug() << "numLinks" << numLinks; -    for(int i = 0; i < numPairs; i++) { +    for(int i = 0; i < numLinks; i++) {          FS::LinkPair pair;          in >> pair.src;          in >> pair.dst;          qDebug() << "link" << pair.src << "to" << pair.dst; -        m_path_pairs.append(pair); +        m_links_to_make.append(pair);      }      runLink(); diff --git a/launcher/filelink/FileLink.h b/launcher/filelink/FileLink.h index 5d0ba123..d146b8d9 100644 --- a/launcher/filelink/FileLink.h +++ b/launcher/filelink/FileLink.h @@ -52,13 +52,17 @@ private:      void joinServer(QString server);      void readPathPairs();      void runLink(); +    void sendResults(); + +    bool m_useHardLinks = false;      QDateTime m_startTime;      QLocalSocket socket;      QDataStream in;      quint32 blockSize; -    QList<FS::LinkPair> m_path_pairs; +    QList<FS::LinkPair> m_links_to_make; +    QList<FS::LinkResult> m_path_results;  #if defined Q_OS_WIN32      // used on Windows to attach the standard IO streams | 
