feat: successful process elevation and comunication!
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
parent
32409a361b
commit
6d160a7b7e
@ -562,6 +562,13 @@ set(ATLAUNCHER_SOURCES
|
|||||||
set(LINKEXE_SOURCES
|
set(LINKEXE_SOURCES
|
||||||
filelink/FileLink.h
|
filelink/FileLink.h
|
||||||
filelink/FileLink.cpp
|
filelink/FileLink.cpp
|
||||||
|
FileSystem.h
|
||||||
|
FileSystem.cpp
|
||||||
|
Exception.h
|
||||||
|
StringUtils.h
|
||||||
|
StringUtils.cpp
|
||||||
|
DesktopServices.h
|
||||||
|
DesktopServices.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
######## Logging categories ########
|
######## Logging categories ########
|
||||||
@ -1126,8 +1133,6 @@ if(WIN32)
|
|||||||
Qt${QT_VERSION_MAJOR}::Xml
|
Qt${QT_VERSION_MAJOR}::Xml
|
||||||
Qt${QT_VERSION_MAJOR}::Network
|
Qt${QT_VERSION_MAJOR}::Network
|
||||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||||
Qt${QT_VERSION_MAJOR}::Gui
|
|
||||||
Qt${QT_VERSION_MAJOR}::Widgets
|
|
||||||
${Launcher_QT_LIBS}
|
${Launcher_QT_LIBS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "Application.h"
|
//#include "Application.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
|
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
|
||||||
|
@ -36,6 +36,8 @@
|
|||||||
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
@ -45,6 +47,7 @@
|
|||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QtNetwork>
|
||||||
|
|
||||||
#include "DesktopServices.h"
|
#include "DesktopServices.h"
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
@ -61,6 +64,11 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winnls.h>
|
#include <winnls.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
//for ShellExecute
|
||||||
|
#include <shlobj.h>
|
||||||
|
//#include <shlwapi.h>
|
||||||
|
#include <objbase.h>
|
||||||
|
#include <Shellapi.h>
|
||||||
#else
|
#else
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
#endif
|
#endif
|
||||||
@ -218,19 +226,29 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool create_link::operator()(const QString& offset, bool dryRun)
|
||||||
|
{
|
||||||
|
|
||||||
|
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 links a directory and it's contents from src to dest
|
||||||
* @param offset subdirectory form src to link to dest
|
* @param offset subdirectory form src to link to dest
|
||||||
* @return if there was an error during the attempt to link
|
* @return if there was an error during the attempt to link
|
||||||
*/
|
*/
|
||||||
bool create_link::operator()(const QString& offset, bool dryRun)
|
bool create_link::make_link(const QString& srcPath, const QString& dstPath, const QString& offset, bool dryRun)
|
||||||
{
|
{
|
||||||
m_linked = 0; // reset counter
|
m_linked = 0; // reset counter
|
||||||
|
|
||||||
auto src = PathCombine(m_src.absolutePath(), offset);
|
auto src = PathCombine(QDir(srcPath).absolutePath(), offset);
|
||||||
auto dst = PathCombine(m_dst.absolutePath(), offset);
|
auto dst = PathCombine(QDir(dstPath).absolutePath(), offset);
|
||||||
|
|
||||||
std::error_code err;
|
|
||||||
|
|
||||||
// you can't hard link a directory so make sure if we deal with a directory we do so recursively
|
// you can't hard link a directory so make sure if we deal with a directory we do so recursively
|
||||||
if (m_useHardLinks)
|
if (m_useHardLinks)
|
||||||
@ -248,26 +266,25 @@ bool create_link::operator()(const QString& offset, bool dryRun)
|
|||||||
if (m_useHardLinks) {
|
if (m_useHardLinks) {
|
||||||
if (m_debug)
|
if (m_debug)
|
||||||
qDebug() << "making hard link:" << src_path << "to" << dst_path;
|
qDebug() << "making hard link:" << src_path << "to" << dst_path;
|
||||||
fs::create_hard_link(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), err);
|
fs::create_hard_link(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), m_os_err);
|
||||||
} else if (fs::is_directory(StringUtils::toStdString(src_path))) {
|
} else if (fs::is_directory(StringUtils::toStdString(src_path))) {
|
||||||
if (m_debug)
|
if (m_debug)
|
||||||
qDebug() << "making directory_symlink:" << src_path << "to" << dst_path;
|
qDebug() << "making directory_symlink:" << src_path << "to" << dst_path;
|
||||||
fs::create_directory_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), err);
|
fs::create_directory_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), m_os_err);
|
||||||
} else {
|
} else {
|
||||||
if (m_debug)
|
if (m_debug)
|
||||||
qDebug() << "making symlink:" << src_path << "to" << dst_path;
|
qDebug() << "making symlink:" << src_path << "to" << dst_path;
|
||||||
fs::create_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), err);
|
fs::create_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), m_os_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (err) {
|
if (m_os_err) {
|
||||||
qWarning() << "Failed to link files:" << QString::fromStdString(err.message());
|
qWarning() << "Failed to link files:" << QString::fromStdString(m_os_err.message());
|
||||||
qDebug() << "Source file:" << src_path;
|
qDebug() << "Source file:" << src_path;
|
||||||
qDebug() << "Destination file:" << dst_path;
|
qDebug() << "Destination file:" << dst_path;
|
||||||
qDebug() << "Error catagory:" << err.category().name();
|
qDebug() << "Error catagory:" << m_os_err.category().name();
|
||||||
qDebug() << "Error code:" << err.value();
|
qDebug() << "Error code:" << m_os_err.value();
|
||||||
m_last_os_err = err.value();
|
emit linkFailed(src_path, dst_path, m_os_err);
|
||||||
emit linkFailed(src_path, dst_path, err);
|
|
||||||
} else {
|
} else {
|
||||||
m_linked++;
|
m_linked++;
|
||||||
emit fileLinked(relative_dst_path);
|
emit fileLinked(relative_dst_path);
|
||||||
@ -290,10 +307,103 @@ bool create_link::operator()(const QString& offset, bool dryRun)
|
|||||||
auto relative_path = src_dir.relativeFilePath(src_path);
|
auto relative_path = src_dir.relativeFilePath(src_path);
|
||||||
|
|
||||||
link_file(src_path, relative_path);
|
link_file(src_path, relative_path);
|
||||||
|
if (m_os_err) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err.value() == 0;
|
return m_os_err.value() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool create_link::runPrivlaged(const QString& offset)
|
||||||
|
{
|
||||||
|
|
||||||
|
QString serverName = BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink_server" + StringUtils::getRandomAlphaNumeric(8);
|
||||||
|
|
||||||
|
connect(&m_linkServer, &QLocalServer::newConnection, this, [&](){
|
||||||
|
|
||||||
|
qDebug() << "Client connected, sending out pairs";
|
||||||
|
// construct block of data to send
|
||||||
|
QByteArray block;
|
||||||
|
QDataStream out(&block, QIODevice::WriteOnly);
|
||||||
|
out.setVersion(QDataStream::Qt_5_15); // 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());
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLocalSocket *clientConnection = m_linkServer.nextPendingConnection();
|
||||||
|
connect(clientConnection, &QLocalSocket::disconnected,
|
||||||
|
clientConnection, &QLocalSocket::deleteLater);
|
||||||
|
|
||||||
|
qint64 byteswritten = clientConnection->write(block);
|
||||||
|
bool bytesflushed = clientConnection->flush();
|
||||||
|
qDebug() << "block flushed" << byteswritten << bytesflushed;
|
||||||
|
//clientConnection->disconnectFromServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
qDebug() << "Listening on pipe" << serverName;
|
||||||
|
if (!m_linkServer.listen(serverName)) {
|
||||||
|
qDebug() << "Unable to start local pipe server on" << serverName << ":" << m_linkServer.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalLinkFileProcess *linkFileProcess = new ExternalLinkFileProcess(serverName, this);
|
||||||
|
connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&](){
|
||||||
|
emit finishedPrivlaged();
|
||||||
|
});
|
||||||
|
connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater);
|
||||||
|
|
||||||
|
linkFileProcess->start();
|
||||||
|
|
||||||
|
// linkFileProcess->wait();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ExternalLinkFileProcess::runLinkFile() {
|
||||||
|
QString fileLinkExe = PathCombine(QCoreApplication::instance()->applicationDirPath(), BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink");
|
||||||
|
QString params = "-s " + m_server;
|
||||||
|
|
||||||
|
#if defined Q_OS_WIN32
|
||||||
|
SHELLEXECUTEINFO ShExecInfo;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
fileLinkExe = fileLinkExe + ".exe";
|
||||||
|
|
||||||
|
qDebug() << "Running: runas" << fileLinkExe << params;
|
||||||
|
|
||||||
|
LPCWSTR programNameWin = (const wchar_t*) fileLinkExe.utf16();
|
||||||
|
LPCWSTR paramsWin = (const wchar_t*) params.utf16();
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-shellexecuteinfoa
|
||||||
|
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
|
||||||
|
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||||
|
ShExecInfo.hwnd = NULL; // Optional. A handle to the owner window, used to display and position any UI that the system might produce while executing this function.
|
||||||
|
ShExecInfo.lpVerb = L"runas"; // elevate to admin, show UAC
|
||||||
|
ShExecInfo.lpFile = programNameWin;
|
||||||
|
ShExecInfo.lpParameters = paramsWin;
|
||||||
|
ShExecInfo.lpDirectory = NULL;
|
||||||
|
ShExecInfo.nShow = SW_NORMAL;
|
||||||
|
ShExecInfo.hInstApp = NULL;
|
||||||
|
|
||||||
|
ShellExecuteEx(&ShExecInfo);
|
||||||
|
|
||||||
|
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
|
||||||
|
CloseHandle(ShExecInfo.hProcess);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
qDebug() << "Process exited";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool move(const QString& source, const QString& dest)
|
bool move(const QString& source, const QString& dest)
|
||||||
|
@ -39,9 +39,13 @@
|
|||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
#include "pathmatcher/IPathMatcher.h"
|
#include "pathmatcher/IPathMatcher.h"
|
||||||
|
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFlags>
|
#include <QFlags>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QLocalServer>
|
||||||
|
|
||||||
namespace FS {
|
namespace FS {
|
||||||
|
|
||||||
@ -124,16 +128,45 @@ class copy : public QObject {
|
|||||||
int m_copied;
|
int m_copied;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LinkPair {
|
||||||
|
QString src;
|
||||||
|
QString dst;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExternalLinkFileProcess : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ExternalLinkFileProcess(QString server, QObject* parent = nullptr) : QThread(parent), m_server(server) {}
|
||||||
|
|
||||||
|
void run() override {
|
||||||
|
runLinkFile();
|
||||||
|
emit processExited();
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void processExited();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void runLinkFile();
|
||||||
|
|
||||||
|
QString m_server;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copies a directory and it's contents from src to dest
|
* @brief links (a file / a directory and it's contents) from src to dest
|
||||||
*/
|
*/
|
||||||
class create_link : public QObject {
|
class create_link : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
create_link(const QList<LinkPair> path_pairs, QObject* parent = nullptr) : QObject(parent)
|
||||||
|
{
|
||||||
|
m_path_pairs.append(path_pairs);
|
||||||
|
}
|
||||||
create_link(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
create_link(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
||||||
{
|
{
|
||||||
m_src.setPath(src);
|
LinkPair pair = {src, dst};
|
||||||
m_dst.setPath(dst);
|
m_path_pairs.append(pair);
|
||||||
}
|
}
|
||||||
create_link& useHardLinks(const bool useHard)
|
create_link& useHardLinks(const bool useHard)
|
||||||
{
|
{
|
||||||
@ -161,31 +194,39 @@ class create_link : public QObject {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getLastOSError() {
|
std::error_code getOSError() {
|
||||||
return m_last_os_err;
|
return m_os_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
|
|
||||||
|
bool runPrivlaged() { return runPrivlaged(QString()); }
|
||||||
|
bool runPrivlaged(const QString& offset);
|
||||||
|
|
||||||
int totalLinked() { return m_linked; }
|
int totalLinked() { return m_linked; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileLinked(const QString& relativeName);
|
void fileLinked(const QString& relativeName);
|
||||||
void linkFailed(const QString& srcName, const QString& dstName, std::error_code err);
|
void linkFailed(const QString& srcName, const QString& dstName, std::error_code err);
|
||||||
|
void finishedPrivlaged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool operator()(const QString& offset, bool dryRun = false);
|
bool operator()(const QString& offset, bool dryRun = false);
|
||||||
|
bool make_link(const QString& src_path, const QString& dst_path, const QString& offset, bool dryRun);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_useHardLinks = false;
|
bool m_useHardLinks = false;
|
||||||
const IPathMatcher* m_matcher = nullptr;
|
const IPathMatcher* m_matcher = nullptr;
|
||||||
bool m_whitelist = false;
|
bool m_whitelist = false;
|
||||||
bool m_recursive = true;
|
bool m_recursive = true;
|
||||||
QDir m_src;
|
|
||||||
QDir m_dst;
|
QList<LinkPair> m_path_pairs;
|
||||||
|
|
||||||
int m_linked;
|
int m_linked;
|
||||||
bool m_debug = false;
|
bool m_debug = false;
|
||||||
int m_last_os_err = 0;
|
std::error_code m_os_err;
|
||||||
|
|
||||||
|
QLocalServer m_linkServer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
/// If you're wondering where these came from exactly, then know you're not the only one =D
|
/// If you're wondering where these came from exactly, then know you're not the only one =D
|
||||||
|
|
||||||
/// TAKEN FROM Qt, because it doesn't expose it intelligently
|
/// TAKEN FROM Qt, because it doesn't expose it intelligently
|
||||||
@ -74,3 +76,16 @@ int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSe
|
|||||||
// The two strings are the same (02 == 2) so fall back to the normal sort
|
// The two strings are the same (02 == 2) so fall back to the normal sort
|
||||||
return QString::compare(s1, s2, cs);
|
return QString::compare(s1, s2, cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString StringUtils::getRandomAlphaNumeric(const int length)
|
||||||
|
{
|
||||||
|
const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
|
||||||
|
QString randomString;
|
||||||
|
for(int i=0; i < length; ++i)
|
||||||
|
{
|
||||||
|
int index = QRandomGenerator::global()->bounded(0, possibleCharacters.length());
|
||||||
|
QChar nextChar = possibleCharacters.at(index);
|
||||||
|
randomString.append(nextChar);
|
||||||
|
}
|
||||||
|
return randomString;
|
||||||
|
}
|
||||||
|
@ -29,4 +29,6 @@ inline QString fromStdString(string s)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs);
|
int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs);
|
||||||
|
|
||||||
|
QString getRandomAlphaNumeric(const int length);
|
||||||
} // namespace StringUtils
|
} // namespace StringUtils
|
||||||
|
@ -31,8 +31,6 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
|
||||||
#include <FileSystem.h>
|
|
||||||
#include <DesktopServices.h>
|
#include <DesktopServices.h>
|
||||||
|
|
||||||
#include <sys.h>
|
#include <sys.h>
|
||||||
@ -48,7 +46,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv)
|
FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this))
|
||||||
{
|
{
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
// attach the parent console
|
// attach the parent console
|
||||||
@ -81,18 +79,116 @@ FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv)
|
|||||||
|
|
||||||
// Commandline parsing
|
// Commandline parsing
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(QObject::tr("a batch MKLINK program for windows to be useed with prismlauncher"));
|
parser.setApplicationDescription(QObject::tr("a batch MKLINK program for windows to be used with prismlauncher"));
|
||||||
|
|
||||||
parser.addOptions({
|
parser.addOptions({
|
||||||
|
{{"s", "server"}, "Join the specified server on launch", "pipe name"}
|
||||||
});
|
});
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
|
|
||||||
parser.process(arguments());
|
parser.process(arguments());
|
||||||
|
|
||||||
|
QString serverToJoin = parser.value("server");
|
||||||
|
|
||||||
qDebug() << "link program launched";
|
qDebug() << "link program launched";
|
||||||
|
|
||||||
|
if (!serverToJoin.isEmpty()) {
|
||||||
|
qDebug() << "joining server" << serverToJoin;
|
||||||
|
joinServer(serverToJoin);
|
||||||
|
} else {
|
||||||
|
qDebug() << "no server to join";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileLinkApp::joinServer(QString server)
|
||||||
|
{
|
||||||
|
|
||||||
|
blockSize = 0;
|
||||||
|
|
||||||
|
in.setDevice(&socket);
|
||||||
|
in.setVersion(QDataStream::Qt_5_15);
|
||||||
|
|
||||||
|
connect(&socket, &QLocalSocket::connected, this, [&](){
|
||||||
|
qDebug() << "connected to server";
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
|
||||||
|
|
||||||
|
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.");
|
||||||
|
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.");
|
||||||
|
break;
|
||||||
|
case QLocalSocket::PeerClosedError:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qDebug() << tr("The following error occurred: %1.").arg(socket.errorString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&socket, &QLocalSocket::disconnected, this, [&](){
|
||||||
|
qDebug() << "dissconnected from server";
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.connectToServer(server);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileLinkApp::runLink()
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
//exit();
|
||||||
|
qDebug() << "done, should exit";
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileLinkApp::readPathPairs()
|
||||||
|
{
|
||||||
|
m_path_pairs.clear();
|
||||||
|
qDebug() << "Reading path pairs from server";
|
||||||
|
qDebug() << "bytes avalible" << socket.bytesAvailable();
|
||||||
|
if (blockSize == 0) {
|
||||||
|
// Relies on the fact that QDataStream serializes a quint32 into
|
||||||
|
// sizeof(quint32) bytes
|
||||||
|
if (socket.bytesAvailable() < (int)sizeof(quint32))
|
||||||
|
return;
|
||||||
|
qDebug() << "reading block size";
|
||||||
|
in >> blockSize;
|
||||||
|
}
|
||||||
|
qDebug() << "blocksize is" << blockSize;
|
||||||
|
qDebug() << "bytes avalible" << socket.bytesAvailable();
|
||||||
|
if (socket.bytesAvailable() < blockSize || in.atEnd())
|
||||||
|
return;
|
||||||
|
|
||||||
|
quint32 numPairs;
|
||||||
|
in >> numPairs;
|
||||||
|
qDebug() << "numPairs" << numPairs;
|
||||||
|
|
||||||
|
for(int i = 0; i < numPairs; i++) {
|
||||||
|
FS::LinkPair pair;
|
||||||
|
in >> pair.src;
|
||||||
|
in >> pair.dst;
|
||||||
|
qDebug() << "link" << pair.src << "to" << pair.dst;
|
||||||
|
m_path_pairs.append(pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
runLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,6 +32,11 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QLocalSocket>
|
||||||
|
|
||||||
|
#define PRISM_EXTERNAL_EXE
|
||||||
|
#include "FileSystem.h"
|
||||||
|
|
||||||
class FileLinkApp : public QCoreApplication
|
class FileLinkApp : public QCoreApplication
|
||||||
{
|
{
|
||||||
@ -43,7 +48,17 @@ public:
|
|||||||
virtual ~FileLinkApp();
|
virtual ~FileLinkApp();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void joinServer(QString server);
|
||||||
|
void readPathPairs();
|
||||||
|
void runLink();
|
||||||
|
|
||||||
QDateTime m_startTime;
|
QDateTime m_startTime;
|
||||||
|
QLocalSocket socket;
|
||||||
|
QDataStream in;
|
||||||
|
quint32 blockSize;
|
||||||
|
|
||||||
|
QList<FS::LinkPair> m_path_pairs;
|
||||||
|
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
// used on Windows to attach the standard IO streams
|
// used on Windows to attach the standard IO streams
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
#include <QTemporaryDir>
|
#include <QTemporaryDir>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#include <tasks/Task.h>
|
||||||
|
|
||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
#include <StringUtils.h>
|
#include <StringUtils.h>
|
||||||
|
|
||||||
@ -26,6 +28,66 @@ namespace fs = ghc::filesystem;
|
|||||||
|
|
||||||
#include <pathmatcher/RegexpMatcher.h>
|
#include <pathmatcher/RegexpMatcher.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class LinkTask : public Task {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class FileSystemTest;
|
||||||
|
|
||||||
|
LinkTask(QString src, QString dst)
|
||||||
|
{
|
||||||
|
m_lnk = new FS::create_link(src, dst, this);
|
||||||
|
m_lnk->debug(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void matcher(const IPathMatcher *filter)
|
||||||
|
{
|
||||||
|
m_lnk->matcher(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void linkRecursively(bool recursive)
|
||||||
|
{
|
||||||
|
m_lnk->linkRecursively(recursive);
|
||||||
|
m_linkRecursive = recursive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void whitelist(bool b)
|
||||||
|
{
|
||||||
|
m_lnk->whitelist(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void executeTask() override
|
||||||
|
{
|
||||||
|
if(!(*m_lnk)()){
|
||||||
|
#if defined Q_OS_WIN32
|
||||||
|
if (!m_useHard) {
|
||||||
|
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
||||||
|
|
||||||
|
qDebug() << "atempting to run with privelage";
|
||||||
|
connect(m_lnk, &FS::create_link::finishedPrivlaged, this, [&](){
|
||||||
|
emitSucceeded();
|
||||||
|
});
|
||||||
|
m_lnk->runPrivlaged();
|
||||||
|
} else {
|
||||||
|
qDebug() << "Link Failed!" << m_lnk->getOSError().value() << m_lnk->getOSError().message().c_str();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
qDebug() << "Link Failed!" << m_lnk->getOSError().value() << m_lnk->getOSError().message().c_str();
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
FS::create_link *m_lnk;
|
||||||
|
bool m_useHard = false;
|
||||||
|
bool m_linkRecursive = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class FileSystemTest : public QObject
|
class FileSystemTest : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -273,7 +335,7 @@ slots:
|
|||||||
void test_link()
|
void test_link()
|
||||||
{
|
{
|
||||||
QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder");
|
QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder");
|
||||||
auto f = [&folder]()
|
auto f = [&folder, this]()
|
||||||
{
|
{
|
||||||
QTemporaryDir tempDir;
|
QTemporaryDir tempDir;
|
||||||
tempDir.setAutoRemove(true);
|
tempDir.setAutoRemove(true);
|
||||||
@ -282,17 +344,17 @@ slots:
|
|||||||
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
|
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
|
||||||
qDebug() << tempDir.path();
|
qDebug() << tempDir.path();
|
||||||
qDebug() << target_dir.path();
|
qDebug() << target_dir.path();
|
||||||
FS::create_link lnk(folder, target_dir.path());
|
|
||||||
lnk.linkRecursively(false);
|
LinkTask lnk_tsk(folder, target_dir.path());
|
||||||
lnk.debug(true);
|
lnk_tsk.linkRecursively(false);
|
||||||
if(!lnk()){
|
QObject::connect(&lnk_tsk, &Task::finished, [&]{
|
||||||
#if defined Q_OS_WIN32
|
QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been.");
|
||||||
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
});
|
||||||
QVERIFY(lnk.getLastOSError() == 1314);
|
lnk_tsk.start();
|
||||||
return;
|
|
||||||
#endif
|
QVERIFY2(QTest::qWaitFor([&]() {
|
||||||
qDebug() << "Link Failed!" << lnk.getLastOSError();
|
return lnk_tsk.isFinished();
|
||||||
}
|
}, 100000), "Task didn't finish as it should.");
|
||||||
|
|
||||||
for(auto entry: target_dir.entryList())
|
for(auto entry: target_dir.entryList())
|
||||||
{
|
{
|
||||||
@ -337,7 +399,7 @@ slots:
|
|||||||
lnk.useHardLinks(true);
|
lnk.useHardLinks(true);
|
||||||
lnk.debug(true);
|
lnk.debug(true);
|
||||||
if(!lnk()){
|
if(!lnk()){
|
||||||
qDebug() << "Link Failed!" << lnk.getLastOSError();
|
qDebug() << "Link Failed!" << lnk.getOSError().value() << lnk.getOSError().message().c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto entry: target_dir.entryList())
|
for(auto entry: target_dir.entryList())
|
||||||
@ -385,18 +447,19 @@ slots:
|
|||||||
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
|
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
|
||||||
qDebug() << tempDir.path();
|
qDebug() << tempDir.path();
|
||||||
qDebug() << target_dir.path();
|
qDebug() << target_dir.path();
|
||||||
FS::create_link lnk(folder, target_dir.path());
|
|
||||||
lnk.matcher(new RegexpMatcher("[.]?mcmeta"));
|
LinkTask lnk_tsk(folder, target_dir.path());
|
||||||
lnk.linkRecursively(true);
|
lnk_tsk.matcher(new RegexpMatcher("[.]?mcmeta"));
|
||||||
lnk.debug(true);
|
lnk_tsk.linkRecursively(true);
|
||||||
if(!lnk()){
|
QObject::connect(&lnk_tsk, &Task::finished, [&]{
|
||||||
#if defined Q_OS_WIN32
|
QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been.");
|
||||||
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
});
|
||||||
QVERIFY(lnk.getLastOSError() == 1314);
|
lnk_tsk.start();
|
||||||
return;
|
|
||||||
#endif
|
QVERIFY2(QTest::qWaitFor([&]() {
|
||||||
qDebug() << "Link Failed!" << lnk.getLastOSError();
|
return lnk_tsk.isFinished();
|
||||||
}
|
}, 100000), "Task didn't finish as it should.");
|
||||||
|
|
||||||
|
|
||||||
for(auto entry: target_dir.entryList())
|
for(auto entry: target_dir.entryList())
|
||||||
{
|
{
|
||||||
@ -435,19 +498,19 @@ slots:
|
|||||||
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
|
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
|
||||||
qDebug() << tempDir.path();
|
qDebug() << tempDir.path();
|
||||||
qDebug() << target_dir.path();
|
qDebug() << target_dir.path();
|
||||||
FS::create_link lnk(folder, target_dir.path());
|
|
||||||
lnk.matcher(new RegexpMatcher("[.]?mcmeta"));
|
LinkTask lnk_tsk(folder, target_dir.path());
|
||||||
lnk.whitelist(true);
|
lnk_tsk.matcher(new RegexpMatcher("[.]?mcmeta"));
|
||||||
lnk.linkRecursively(true);
|
lnk_tsk.linkRecursively(true);
|
||||||
lnk.debug(true);
|
lnk_tsk.whitelist(true);
|
||||||
if(!lnk()){
|
QObject::connect(&lnk_tsk, &Task::finished, [&]{
|
||||||
#if defined Q_OS_WIN32
|
QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been.");
|
||||||
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
});
|
||||||
QVERIFY(lnk.getLastOSError() == 1314);
|
lnk_tsk.start();
|
||||||
return;
|
|
||||||
#endif
|
QVERIFY2(QTest::qWaitFor([&]() {
|
||||||
qDebug() << "Link Failed!" << lnk.getLastOSError();
|
return lnk_tsk.isFinished();
|
||||||
}
|
}, 100000), "Task didn't finish as it should.");
|
||||||
|
|
||||||
for(auto entry: target_dir.entryList())
|
for(auto entry: target_dir.entryList())
|
||||||
{
|
{
|
||||||
@ -486,17 +549,17 @@ slots:
|
|||||||
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
|
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
|
||||||
qDebug() << tempDir.path();
|
qDebug() << tempDir.path();
|
||||||
qDebug() << target_dir.path();
|
qDebug() << target_dir.path();
|
||||||
FS::create_link lnk(folder, target_dir.path());
|
|
||||||
lnk.linkRecursively(true);
|
LinkTask lnk_tsk(folder, target_dir.path());
|
||||||
lnk.debug(true);
|
lnk_tsk.linkRecursively(true);
|
||||||
if(!lnk()){
|
QObject::connect(&lnk_tsk, &Task::finished, [&]{
|
||||||
#if defined Q_OS_WIN32
|
QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been.");
|
||||||
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
});
|
||||||
QVERIFY(lnk.getLastOSError() == 1314);
|
lnk_tsk.start();
|
||||||
return;
|
|
||||||
#endif
|
QVERIFY2(QTest::qWaitFor([&]() {
|
||||||
qDebug() << "Link Failed!" << lnk.getLastOSError();
|
return lnk_tsk.isFinished();
|
||||||
}
|
}, 100000), "Task didn't finish as it should.");
|
||||||
|
|
||||||
auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden;
|
auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden;
|
||||||
|
|
||||||
@ -538,16 +601,16 @@ slots:
|
|||||||
QDir target_dir(FS::PathCombine(tempDir.path(), "pack.mcmeta"));
|
QDir target_dir(FS::PathCombine(tempDir.path(), "pack.mcmeta"));
|
||||||
qDebug() << tempDir.path();
|
qDebug() << tempDir.path();
|
||||||
qDebug() << target_dir.path();
|
qDebug() << target_dir.path();
|
||||||
FS::create_link lnk(file, target_dir.filePath("pack.mcmeta"));
|
|
||||||
lnk.debug(true);
|
LinkTask lnk_tsk(file, target_dir.filePath("pack.mcmeta"));
|
||||||
if(!lnk()){
|
QObject::connect(&lnk_tsk, &Task::finished, [&]{
|
||||||
#if defined Q_OS_WIN32
|
QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been.");
|
||||||
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
});
|
||||||
QVERIFY(lnk.getLastOSError() == 1314);
|
lnk_tsk.start();
|
||||||
return;
|
|
||||||
#endif
|
QVERIFY2(QTest::qWaitFor([&]() {
|
||||||
qDebug() << "Link Failed!" << lnk.getLastOSError();
|
return lnk_tsk.isFinished();
|
||||||
}
|
}, 100000), "Task didn't finish as it should.");
|
||||||
|
|
||||||
auto filter = QDir::Filter::Files;
|
auto filter = QDir::Filter::Files;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user