From d6522376a23bc865dbd6d81458558c0266219acd Mon Sep 17 00:00:00 2001 From: DeathByDenim Date: Fri, 13 Mar 2015 15:20:27 -0400 Subject: [PATCH] Fix symlinking for Windows. --- bundle.cpp | 59 +---------------------- bundle.h | 5 +- patcher.cpp | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++-- patcher.h | 15 +++--- 4 files changed, 146 insertions(+), 69 deletions(-) diff --git a/bundle.cpp b/bundle.cpp index dc432a3..fb195a5 100644 --- a/bundle.cpp +++ b/bundle.cpp @@ -13,11 +13,6 @@ #include #include #include -#if !defined(_WIN32) -# include -# include -# include -#endif Bundle::Bundle(QString install_path, Patcher *patcher) : QObject(), mInstallPath(install_path), mPatcher(patcher), mNeedsDownloading(false), mBufferSize(10*1024) @@ -160,16 +155,7 @@ void Bundle::verifyFinished() if(mNeedsDownloading) emit downloadMe(); else - { -#ifdef _WIN32 - for(QMap::const_iterator symlink = mCopyLater.constBegin(); symlink != mCopyLater.constEnd(); ++symlink) - { - QFile from_file(symlink.key()); - from_file.copy(symlink.value()); - } -#endif emit finished(); - } } } @@ -314,14 +300,6 @@ void Bundle::downloadFinished() reply->deleteLater(); -#ifdef _WIN32 - for(QMap::const_iterator symlink = mCopyLater.constBegin(); symlink != mCopyLater.constEnd(); ++symlink) - { - QFile from_file(symlink.key()); - from_file.copy(symlink.value()); - } -#endif - emit finished(); } } @@ -402,41 +380,8 @@ void Bundle::nextFile() bool Bundle::createSymbolicLink(const QString& from, const QString& to) { - // This is the same file, so make a symbolic link, but first - // find the relative path. - QStringList path1 = from.split(QRegExp("[\\\\/]")); - QStringList path2 = to.split(QRegExp("[\\\\/]")); - int i; - for(i = 0; i < path2.count() - 1; i++) - { - if(path1[i] == path2[i]) - { - path1[i] = ""; - path2[i] = ""; - } - else - break; - } - for(; i < path2.count() - 1; i++) - { - path1.push_front(".."); - } - - for(int i = path1.count() - 1; i >= 0; i--) - { - if(path1[i].isEmpty()) - path1.removeAt(i); - } - -#if defined(_WIN32) - mCopyLater[mInstallPath + "/" + to] = path1.join("/"); -#else - int res = symlink(path1.join("/").toStdString().c_str(), (mInstallPath + "/" + to).toStdString().c_str()); - if(res != 0) - { - return false; - } -#endif + // Add to the list, which is retrieved by Patcher later to make the actual links. + mSymLinkLater[mInstallPath + "/" + to] = mInstallPath + "/" + from; return true; } diff --git a/bundle.h b/bundle.h index dc2dc18..4fe6944 100644 --- a/bundle.h +++ b/bundle.h @@ -28,6 +28,7 @@ class Bundle : public QObject QString checksum() {return mChecksum;} qint64 totalSize() {return mTotalSize;} void downloadAndExtract(QNetworkAccessManager* network_access_manager, QString download_url); + const QMap & symLinks() {return mSymLinkLater;} private: struct File @@ -58,9 +59,7 @@ class Bundle : public QObject QFile *mCurrentFile; bool mCurrentFileIsGzipped; int mFilesCurrentIndex; -#if defined(_WIN32) - QMap mCopyLater; -#endif + QMap mSymLinkLater; static bool verifySHA1(Bundle::File file_entry, bool* downloading, Patcher* patcher, QString install_path); void prepareZLib(); diff --git a/patcher.cpp b/patcher.cpp index a966d2c..5d0221d 100644 --- a/patcher.cpp +++ b/patcher.cpp @@ -9,6 +9,11 @@ #include #include #include +#if !defined(_WIN32) +# include +# include +# include +#endif bool error_occurred = false; @@ -124,20 +129,145 @@ void Patcher::bundleVerifyDone() } } +void Patcher::bundleError(QString error_string) +{ + emit stateChange("Error occurred"); + emit error(error_string); +} + void Patcher::bundleFinished() +{/* + for(QMap::const_iterator symlink = mCopyLater.constBegin(); symlink != mCopyLater.constEnd(); ++symlink) + { + QFile from_file(symlink.key()); + from_file.copy(symlink.value()); + } + + int res = symlink(path1.join("/").toStdString().c_str(), (mInstallPath + "/" + to).toStdString().c_str()); +if(res != 0) { +return false; +} +*/ + + Bundle *bundle = dynamic_cast(sender()); + if(bundle) + { + for(QMap::const_iterator link = bundle->symLinks().constBegin(); link != bundle->symLinks().constEnd(); ++link) + { + mSymLinkLater.insert(link.key(), link.value()); + } + } + mBundlesFinished++; if(mBundlesFinished == mNumBundles) { + processSymLinks(); + emit stateChange("Done"); emit done(); } } -void Patcher::bundleError(QString error_string) +void Patcher::processSymLinks() { - emit stateChange("Error occurred"); - emit error(error_string); + bool something_changed; + + while(mSymLinkLater.size() > 0) + { + something_changed = false; + for(QMap::const_iterator slink = mSymLinkLater.constBegin(); slink != mSymLinkLater.constEnd(); ++slink) + { + QFile source_file(slink.value()); + if(source_file.exists()) + { + QFile target_file(slink.key()); + if(target_file.exists()) + target_file.remove(); + +#ifdef _WIN32 + if(!from_file.copy(symlink.key())) + { + emit error(tr("Error copying duplicate file \"%1\" to \"%2\".\n%3").arg(symlink.value()).arg(symlink.value()).arg(from_file.errorString())); + return; + } +#else + // For proper symlinks, we need to find the relative path. + QStringList target_path = slink.key().split(QRegExp("[\\\\/]")); + QStringList source_path = slink.value().split(QRegExp("[\\\\/]")); + int i; + for(i = 0; i < target_path.count() - 1; i++) + { + if(source_path[i] == target_path[i]) + { + source_path[i] = ""; + target_path[i] = ""; + } + else + break; + } + for(; i < target_path.count() - 1; i++) + { + source_path.push_front(".."); + } + + for(int i = source_path.count() - 1; i >= 0; i--) + { + if(source_path[i].isEmpty()) + source_path.removeAt(i); + } + + int res = symlink(source_path.join("/").toStdString().c_str(), slink.key().toStdString().c_str()); + if(res != 0) + { + int error_code = errno; + emit error(tr("Error creating symlink from \"%1\" to \"%2\".\n%3").arg(source_file.fileName()).arg(source_path.join('/')).arg(strerror(error_code))); + return; + } +#endif + + mSymLinkLater.remove(slink.key()); + something_changed = true; + break; + } + } + + if(!something_changed) + { + emit error(tr("Error copying duplicate file. Source does not exist.")); + return; + } + } + + /* + // This is the same file, so make a symbolic link, but first + // find the relative path. + QStringList path1 = from.split(QRegExp("[\\\\/]")); + QStringList path2 = to.split(QRegExp("[\\\\/]")); + int i; + for(i = 0; i < path2.count() - 1; i++) + { + if(path1[i] == path2[i]) + { + path1[i] = ""; + path2[i] = ""; + } + else + break; + } + for(; i < path2.count() - 1; i++) + { + path1.push_front(".."); + } + + for(int i = path1.count() - 1; i >= 0; i--) + { + if(path1[i].isEmpty()) + path1.removeAt(i); + } + + int res = symlink(path1.join("/").toStdString().c_str(), (mInstallPath + "/" + to).toStdString().c_str()); +*/ } diff --git a/patcher.h b/patcher.h index 1555b69..6bc5b16 100644 --- a/patcher.h +++ b/patcher.h @@ -4,6 +4,7 @@ #include #include #include +#include class QNetworkAccessManager; @@ -16,7 +17,7 @@ class Patcher : public QObject ~Patcher(); void setInstallPath(QString install_path) {mInstallPath = install_path;} - void setDownloadUrl(QString url) {mDownloadUrl = url;} + void setDownloadUrl(QString url) {mDownloadUrl = url;} void giveJsonData(QByteArray data); QByteArray getFile(QString filename); @@ -33,8 +34,10 @@ class Patcher : public QObject int mNumBundles; int mBundlesVerified; int mBundlesFinished; + QMap mSymLinkLater; void startVerifying(); + void processSymLinks(); signals: void error(QString); @@ -43,11 +46,11 @@ class Patcher : public QObject void stateChange(QString state); private slots: - void bundleDownloadMe(); - void bundleDownloadProgress(qint64); - void bundleVerifyDone(); - void bundleFinished(); - void bundleError(QString error); + void bundleDownloadMe(); + void bundleDownloadProgress(qint64); + void bundleVerifyDone(); + void bundleFinished(); + void bundleError(QString error); public slots: void parseManifest();