From f794e49bb6eadd70c52683e60a700a1d7e9cd17b Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Mon, 6 Feb 2023 23:05:06 -0800
Subject: we want to make links!
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/CMakeLists.txt | 52 ++++++++
launcher/FileSystem.cpp | 87 ++++++++++++-
launcher/FileSystem.h | 68 +++++++++-
launcher/InstanceCopyPrefs.cpp | 30 +++++
launcher/InstanceCopyPrefs.h | 9 ++
launcher/filelink/FileLink.cpp | 119 +++++++++++++++++
launcher/filelink/FileLink.h | 52 ++++++++
launcher/filelink/filelink.exe.manifest | 28 ++++
launcher/filelink/main.cpp | 31 +++++
launcher/ui/dialogs/CopyInstanceDialog.cpp | 19 +++
launcher/ui/dialogs/CopyInstanceDialog.h | 3 +
launcher/ui/dialogs/CopyInstanceDialog.ui | 200 +++++++++++++++++++----------
12 files changed, 627 insertions(+), 71 deletions(-)
create mode 100644 launcher/filelink/FileLink.cpp
create mode 100644 launcher/filelink/FileLink.h
create mode 100644 launcher/filelink/filelink.exe.manifest
create mode 100644 launcher/filelink/main.cpp
(limited to 'launcher')
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 074570e3..2216aa1b 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -559,6 +559,11 @@ set(ATLAUNCHER_SOURCES
modplatform/atlauncher/ATLShareCode.h
)
+set(LINKDAEMON_SOURCES
+ filelink/FileLink.h
+ filelink/FileLink.cpp
+)
+
######## Logging categories ########
ecm_qt_declare_logging_category(CORE_SOURCES
@@ -1107,6 +1112,53 @@ install(TARGETS ${Launcher_Name}
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
)
+if(WIN32)
+ add_library(filelink_logic STATIC ${LINKDAEMON_SOURCES})
+ target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+ target_link_libraries(filelink_logic
+ systeminfo
+ BuildConfig
+ Qt${QT_VERSION_MAJOR}::Widgets
+ ghcFilesystem::ghc_filesystem
+ )
+ target_link_libraries(filelink_logic
+ Qt${QT_VERSION_MAJOR}::Core
+ Qt${QT_VERSION_MAJOR}::Xml
+ Qt${QT_VERSION_MAJOR}::Network
+ Qt${QT_VERSION_MAJOR}::Concurrent
+ Qt${QT_VERSION_MAJOR}::Gui
+ Qt${QT_VERSION_MAJOR}::Widgets
+ ${Launcher_QT_LIBS}
+ )
+
+ add_executable("${Launcher_Name}_filelink" WIN32 filelink/main.cpp)
+
+ target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)
+
+ target_link_libraries("${Launcher_Name}_filelink" filelink_logic)
+
+ if(DEFINED Launcher_APP_BINARY_NAME)
+ set_target_properties("${Launcher_Name}_filelink" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_filelink")
+ endif()
+ if(DEFINED Launcher_BINARY_RPATH)
+ SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}")
+ endif()
+
+ if(CMAKE_GENERATOR MATCHES "Visual Studio")
+ SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES LINK_FLAGS "/level='requireAdministrator' /uiAccess='false' /SUBSYSTEM:CONSOLE")
+ else()
+ SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES LINK_FLAGS "/MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\" /SUBSYSTEM:CONSOLE")
+ endif()
+
+
+ install(TARGETS "${Launcher_Name}_filelink"
+ BUNDLE DESTINATION "." COMPONENT Runtime
+ LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
+ RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
+ FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
+ )
+endif()
+
if (UNIX AND APPLE)
# Add Sparkle updater
# It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index aee5245d..ec4af98c 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -152,9 +152,11 @@ bool ensureFolderPathExists(QString foldernamepath)
return success;
}
-/// @brief Copies a directory and it's contents from src to dest
-/// @param offset subdirectory form src to copy to dest
-/// @return if there was an error during the filecopy
+/**
+ * @brief Copies a directory and it's contents from src to dest
+ * @param offset subdirectory form src to copy to dest
+ * @return if there was an error during the filecopy
+ */
bool copy::operator()(const QString& offset, bool dryRun)
{
using copy_opts = fs::copy_options;
@@ -215,6 +217,85 @@ bool copy::operator()(const QString& offset, bool dryRun)
return err.value() == 0;
}
+
+/**
+ * @brief links a directory and it's contents from src to dest
+ * @param offset subdirectory form src to link to dest
+ * @return if there was an error during the attempt to link
+ */
+bool create_link::operator()(const QString& offset, bool dryRun)
+{
+ m_linked = 0; // reset counter
+
+ auto src = PathCombine(m_src.absolutePath(), offset);
+ auto dst = PathCombine(m_dst.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
+ 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;
+
+ 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), 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), err);
+ } else {
+ if (m_debug)
+ qDebug() << "making symlink:" << src_path << "to" << dst_path;
+ fs::create_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), err);
+ }
+
+ }
+ if (err) {
+ qWarning() << "Failed to link files:" << QString::fromStdString(err.message());
+ qDebug() << "Source file:" << src_path;
+ qDebug() << "Destination file:" << dst_path;
+ qDebug() << "Error catagory:" << err.category().name();
+ qDebug() << "Error code:" << err.value();
+ m_last_os_err = err.value();
+ emit linkFailed(src_path, dst_path, err);
+ } 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);
+ }
+ }
+
+ return err.value() == 0;
+}
+
bool move(const QString& source, const QString& dest)
{
std::error_code err;
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index f083f3c7..98f55f96 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -77,7 +77,9 @@ bool ensureFilePathExists(QString filenamepath);
*/
bool ensureFolderPathExists(QString filenamepath);
-/// @brief Copies a directory and it's contents from src to dest
+/**
+ * @brief Copies a directory and it's contents from src to dest
+ */
class copy : public QObject {
Q_OBJECT
public:
@@ -122,6 +124,70 @@ class copy : public QObject {
int m_copied;
};
+/**
+ * @brief Copies a directory and it's contents from src to dest
+ */
+class create_link : public QObject {
+ Q_OBJECT
+ public:
+ create_link(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
+ {
+ m_src.setPath(src);
+ m_dst.setPath(dst);
+ }
+ create_link& useHardLinks(const bool useHard)
+ {
+ m_useHardLinks = useHard;
+ return *this;
+ }
+ create_link& matcher(const IPathMatcher* filter)
+ {
+ m_matcher = filter;
+ return *this;
+ }
+ create_link& whitelist(bool whitelist)
+ {
+ m_whitelist = whitelist;
+ return *this;
+ }
+ create_link& linkRecursively(bool recursive)
+ {
+ m_recursive = recursive;
+ return *this;
+ }
+ create_link& debug(bool d)
+ {
+ m_debug = d;
+ return *this;
+ }
+
+ int getLastOSError() {
+ return m_last_os_err;
+ }
+
+ bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
+
+ int totalLinked() { return m_linked; }
+
+ signals:
+ void fileLinked(const QString& relativeName);
+ void linkFailed(const QString& srcName, const QString& dstName, std::error_code err);
+
+ private:
+ bool operator()(const QString& offset, bool dryRun = false);
+
+ private:
+ bool m_useHardLinks = false;
+ const IPathMatcher* m_matcher = nullptr;
+ bool m_whitelist = false;
+ bool m_recursive = true;
+ QDir m_src;
+ QDir m_dst;
+ int m_linked;
+ bool m_debug = false;
+ int m_last_os_err = 0;
+};
+
/**
* @brief moves a file by renaming it
* @param source source file path
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index 7b93a516..18a6d704 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -93,6 +93,21 @@ bool InstanceCopyPrefs::isCopyScreenshotsEnabled() const
return copyScreenshots;
}
+bool InstanceCopyPrefs::isLinkFilesEnabled() const
+{
+ return linkFiles;
+}
+
+bool InstanceCopyPrefs::isUseHardLinksEnabled() const
+{
+ return useHardLinks;
+}
+
+bool InstanceCopyPrefs::isLinkWorldsEnabled() const
+{
+ return linkWorlds;
+}
+
// ======= Setters =======
void InstanceCopyPrefs::enableCopySaves(bool b)
{
@@ -133,3 +148,18 @@ void InstanceCopyPrefs::enableCopyScreenshots(bool b)
{
copyScreenshots = b;
}
+
+void InstanceCopyPrefs::enableLinkFiles(bool b)
+{
+ linkFiles = b;
+}
+
+void InstanceCopyPrefs::enableUseHardLinks(bool b)
+{
+ useHardLinks = b;
+}
+
+void InstanceCopyPrefs::enableLinkWorlds(bool b)
+{
+ linkWorlds = b;
+}
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 6988b2df..25c0f3fc 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -19,6 +19,9 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isCopyServersEnabled() const;
[[nodiscard]] bool isCopyModsEnabled() const;
[[nodiscard]] bool isCopyScreenshotsEnabled() const;
+ [[nodiscard]] bool isLinkFilesEnabled() const;
+ [[nodiscard]] bool isUseHardLinksEnabled() const;
+ [[nodiscard]] bool isLinkWorldsEnabled() const;
// Setters
void enableCopySaves(bool b);
void enableKeepPlaytime(bool b);
@@ -28,6 +31,9 @@ struct InstanceCopyPrefs {
void enableCopyServers(bool b);
void enableCopyMods(bool b);
void enableCopyScreenshots(bool b);
+ void enableLinkFiles(bool b);
+ void enableUseHardLinks(bool b);
+ void enableLinkWorlds(bool b);
protected: // data
bool copySaves = true;
@@ -38,4 +44,7 @@ struct InstanceCopyPrefs {
bool copyServers = true;
bool copyMods = true;
bool copyScreenshots = true;
+ bool linkFiles = false;
+ bool useHardLinks = false;
+ bool linkWorlds = true;
};
diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp
new file mode 100644
index 00000000..9b5589ab
--- /dev/null
+++ b/launcher/filelink/FileLink.cpp
@@ -0,0 +1,119 @@
+// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "FileLink.h"
+#include "BuildConfig.h"
+
+
+#include
+
+#include
+#include
+
+#include
+
+
+#include
+#include
+
+#include
+
+#if defined Q_OS_WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include
+#include
+#endif
+
+
+
+
+FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv)
+{
+#if defined Q_OS_WIN32
+ // attach the parent console
+ if(AttachConsole(ATTACH_PARENT_PROCESS))
+ {
+ // if attach succeeds, reopen and sync all the i/o
+ if(freopen("CON", "w", stdout))
+ {
+ std::cout.sync_with_stdio();
+ }
+ if(freopen("CON", "w", stderr))
+ {
+ std::cerr.sync_with_stdio();
+ }
+ if(freopen("CON", "r", stdin))
+ {
+ std::cin.sync_with_stdio();
+ }
+ auto out = GetStdHandle (STD_OUTPUT_HANDLE);
+ DWORD written;
+ const char * endline = "\n";
+ WriteConsole(out, endline, strlen(endline), &written, NULL);
+ consoleAttached = true;
+ }
+#endif
+ setOrganizationName(BuildConfig.LAUNCHER_NAME);
+ setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
+ setApplicationName(BuildConfig.LAUNCHER_NAME + "FileLink");
+ setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT);
+
+ // Commandline parsing
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QObject::tr("a batch MKLINK program for windows to be useed with prismlauncher"));
+
+ parser.addOptions({
+
+ });
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ parser.process(arguments());
+
+ qDebug() << "link program launched";
+
+}
+
+
+FileLinkApp::~FileLinkApp()
+{
+ qDebug() << "link program shutting down";
+ // Shut down logger by setting the logger function to nothing
+ qInstallMessageHandler(nullptr);
+
+#if defined Q_OS_WIN32
+ // Detach from Windows console
+ if(consoleAttached)
+ {
+ fclose(stdout);
+ fclose(stdin);
+ fclose(stderr);
+ FreeConsole();
+ }
+#endif
+}
+
+
+
+
diff --git a/launcher/filelink/FileLink.h b/launcher/filelink/FileLink.h
new file mode 100644
index 00000000..253d1394
--- /dev/null
+++ b/launcher/filelink/FileLink.h
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#pragma once
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class FileLinkApp : public QCoreApplication
+{
+ // friends for the purpose of limiting access to deprecated stuff
+ Q_OBJECT
+public:
+
+ FileLinkApp(int &argc, char **argv);
+ virtual ~FileLinkApp();
+
+private:
+ QDateTime m_startTime;
+
+#if defined Q_OS_WIN32
+ // used on Windows to attach the standard IO streams
+ bool consoleAttached = false;
+#endif
+};
diff --git a/launcher/filelink/filelink.exe.manifest b/launcher/filelink/filelink.exe.manifest
new file mode 100644
index 00000000..a4e16264
--- /dev/null
+++ b/launcher/filelink/filelink.exe.manifest
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/launcher/filelink/main.cpp b/launcher/filelink/main.cpp
new file mode 100644
index 00000000..7f06795e
--- /dev/null
+++ b/launcher/filelink/main.cpp
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "FileLink.h"
+
+int main(int argc, char *argv[])
+{
+
+ FileLinkApp ldh(argc, argv);
+
+ return ldh.exec();
+}
\ No newline at end of file
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 3f5122f6..981352ae 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -85,6 +85,10 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->copyServersCheckbox->setChecked(m_selectedOptions.isCopyServersEnabled());
ui->copyModsCheckbox->setChecked(m_selectedOptions.isCopyModsEnabled());
ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());
+
+ ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
+ ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
+ ui->linkWorldsCheckbox->setChecked(m_selectedOptions.isLinkWorldsEnabled());
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -220,3 +224,18 @@ void CopyInstanceDialog::on_copyScreenshotsCheckbox_stateChanged(int state)
m_selectedOptions.enableCopyScreenshots(state == Qt::Checked);
updateSelectAllCheckbox();
}
+
+void CopyInstanceDialog::on_linkFilesGroup_toggled(bool checked)
+{
+ m_selectedOptions.enableLinkFiles(checked);
+}
+
+void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
+}
+
+void CopyInstanceDialog::on_linkWorldsCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableLinkWorlds(state == Qt::Checked);
+}
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 884501d1..a80faab9 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -55,6 +55,9 @@ slots:
void on_copyServersCheckbox_stateChanged(int state);
void on_copyModsCheckbox_stateChanged(int state);
void on_copyScreenshotsCheckbox_stateChanged(int state);
+ void on_linkFilesGroup_toggled(bool checked);
+ void on_hardLinksCheckbox_stateChanged(int state);
+ void on_linkWorldsCheckbox_stateChanged(int state);
private:
void checkAllCheckboxes(const bool& b);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index b7828fe3..e41ad526 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
0
0
- 341
- 399
+ 525
+ 581
@@ -136,70 +136,126 @@
-
-
-
-
-
-
- Disabling this will still keep the mod loader (ex: Fabric, Quilt, etc.) but erase the mods folder and their configs.
-
-
- Copy mods
-
-
-
- -
-
+
+
+ Instance copy options
+
+
+
-
+
+
+ Disabling this will still keep the mod loader (ex: Fabric, Quilt, etc.) but erase the mods folder and their configs.
+
+
+ Copy mods
+
+
+
+ -
+
+
+ Copy the in-game options like FOV, max framerate, etc.
+
+
+ Copy game options
+
+
+
+ -
+
+
+ Copy saves
+
+
+
+ -
+
+
+ Copy shader packs
+
+
+
+ -
+
+
+ Copy servers
+
+
+
+ -
+
+
+ true
+
+
+ Copy resource packs
+
+
+
+ -
+
+
+ Keep play time
+
+
+
+ -
+
+
+ Copy screenshots
+
+
+
+
+
+
+ -
+
+
-
+
- Copy the in-game options like FOV, max framerate, etc.
+ Use symbolic links instead of copying files.
-
- Copy game options
+
+ Link files instead of copying them
-
-
- -
-
-
- Copy saves
-
-
-
- -
-
-
- Copy shader packs
-
-
-
- -
-
-
- Copy servers
+
+ false
-
-
- -
-
-
+
true
-
- Copy resource packs
-
-
-
- -
-
-
- Keep play time
-
-
-
- -
-
-
- Copy screenshots
+
+ false
+
+
-
+
+
+ Use hard links instead of symbolic links
+
+
+ Use hard links
+
+
+
+ -
+
+
+ World save data will be linked and thus shared between instances.
+
+
+ Link worlds
+
+
+ true
+
+
+ false
+
+
+
+
@@ -210,7 +266,7 @@
Qt::Horizontal
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+ QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok
@@ -220,10 +276,20 @@
iconButton
instNameTextBox
groupBox
+ selectAllCheckbox
+ keepPlaytimeCheckbox
+ copyScreenshotsCheckbox
+ copySavesCheckbox
+ copyShaderPacksCheckbox
+ copyGameOptionsCheckbox
+ copyServersCheckbox
+ copyResPacksCheckbox
+ copyModsCheckbox
+ linkFilesGroup
+ hardLinksCheckbox
+ linkWorldsCheckbox
-
-
-
+
buttonBox
@@ -232,8 +298,8 @@
accept()
- 254
- 316
+ 263
+ 571
157
@@ -248,8 +314,8 @@
reject()
- 322
- 316
+ 331
+ 571
286
--
cgit
From 32409a361b797342d625bfc6d0726cc330ced760 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Tue, 7 Feb 2023 03:31:46 -0700
Subject: fix CMakeLits.txt for non MSVC windows builds
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/CMakeLists.txt | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
(limited to 'launcher')
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 2216aa1b..18d4ce0b 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -559,7 +559,7 @@ set(ATLAUNCHER_SOURCES
modplatform/atlauncher/ATLShareCode.h
)
-set(LINKDAEMON_SOURCES
+set(LINKEXE_SOURCES
filelink/FileLink.h
filelink/FileLink.cpp
)
@@ -1113,7 +1113,7 @@ install(TARGETS ${Launcher_Name}
)
if(WIN32)
- add_library(filelink_logic STATIC ${LINKDAEMON_SOURCES})
+ add_library(filelink_logic STATIC ${LINKEXE_SOURCES})
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(filelink_logic
systeminfo
@@ -1144,10 +1144,11 @@ if(WIN32)
SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}")
endif()
+ # may be unnessacery with manifest
if(CMAKE_GENERATOR MATCHES "Visual Studio")
SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES LINK_FLAGS "/level='requireAdministrator' /uiAccess='false' /SUBSYSTEM:CONSOLE")
- else()
- SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES LINK_FLAGS "/MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\" /SUBSYSTEM:CONSOLE")
+ # else() # link arg /MANIFESTUAC only works with MSVC
+ # SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES LINK_FLAGS "/MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\" /SUBSYSTEM:CONSOLE")
endif()
--
cgit
From 6d160a7b7e31034c7a657f30003562c20f9b9c21 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Wed, 8 Feb 2023 00:35:03 -0800
Subject: feat: successful process elevation and comunication!
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/CMakeLists.txt | 9 ++-
launcher/DesktopServices.cpp | 2 +-
launcher/FileSystem.cpp | 140 ++++++++++++++++++++++++++++++++++++-----
launcher/FileSystem.h | 57 ++++++++++++++---
launcher/StringUtils.cpp | 15 +++++
launcher/StringUtils.h | 2 +
launcher/filelink/FileLink.cpp | 106 +++++++++++++++++++++++++++++--
launcher/filelink/FileLink.h | 15 +++++
8 files changed, 315 insertions(+), 31 deletions(-)
(limited to 'launcher')
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 18d4ce0b..dd62893c 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -562,6 +562,13 @@ set(ATLAUNCHER_SOURCES
set(LINKEXE_SOURCES
filelink/FileLink.h
filelink/FileLink.cpp
+ FileSystem.h
+ FileSystem.cpp
+ Exception.h
+ StringUtils.h
+ StringUtils.cpp
+ DesktopServices.h
+ DesktopServices.cpp
)
######## Logging categories ########
@@ -1126,8 +1133,6 @@ if(WIN32)
Qt${QT_VERSION_MAJOR}::Xml
Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Concurrent
- Qt${QT_VERSION_MAJOR}::Gui
- Qt${QT_VERSION_MAJOR}::Widgets
${Launcher_QT_LIBS}
)
diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp
index 302eaf96..69770e99 100644
--- a/launcher/DesktopServices.cpp
+++ b/launcher/DesktopServices.cpp
@@ -37,7 +37,7 @@
#include
#include
#include
-#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.
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index ec4af98c..9e51f932 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -36,6 +36,8 @@
#include "FileSystem.h"
+#include "BuildConfig.h"
+
#include
#include
#include
@@ -45,6 +47,7 @@
#include
#include
#include
+#include
#include "DesktopServices.h"
#include "StringUtils.h"
@@ -61,6 +64,11 @@
#include
#include
#include
+//for ShellExecute
+#include
+//#include
+#include
+#include
#else
#include
#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
* @param offset subdirectory form src to link to dest
* @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
- auto src = PathCombine(m_src.absolutePath(), offset);
- auto dst = PathCombine(m_dst.absolutePath(), offset);
-
- std::error_code err;
+ 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)
@@ -248,26 +266,25 @@ bool create_link::operator()(const QString& offset, bool dryRun)
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), 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))) {
if (m_debug)
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 {
if (m_debug)
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) {
- qWarning() << "Failed to link files:" << QString::fromStdString(err.message());
+ 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:" << err.category().name();
- qDebug() << "Error code:" << err.value();
- m_last_os_err = err.value();
- emit linkFailed(src_path, dst_path, err);
+ qDebug() << "Error catagory:" << m_os_err.category().name();
+ qDebug() << "Error code:" << m_os_err.value();
+ emit linkFailed(src_path, dst_path, m_os_err);
} else {
m_linked++;
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);
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)
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 98f55f96..b15d1685 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -39,9 +39,13 @@
#include "Exception.h"
#include "pathmatcher/IPathMatcher.h"
+#include
+
#include
#include
#include
+#include
+#include
namespace FS {
@@ -124,16 +128,45 @@ class copy : public QObject {
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 {
Q_OBJECT
public:
+ create_link(const QList 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)
{
- m_src.setPath(src);
- m_dst.setPath(dst);
+ LinkPair pair = {src, dst};
+ m_path_pairs.append(pair);
}
create_link& useHardLinks(const bool useHard)
{
@@ -161,31 +194,39 @@ class create_link : public QObject {
return *this;
}
- int getLastOSError() {
- return m_last_os_err;
+ std::error_code getOSError() {
+ return m_os_err;
}
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
+ bool runPrivlaged() { return runPrivlaged(QString()); }
+ bool 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();
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);
private:
bool m_useHardLinks = false;
const IPathMatcher* m_matcher = nullptr;
bool m_whitelist = false;
bool m_recursive = true;
- QDir m_src;
- QDir m_dst;
+
+ QList m_path_pairs;
+
int m_linked;
bool m_debug = false;
- int m_last_os_err = 0;
+ std::error_code m_os_err;
+
+ QLocalServer m_linkServer;
};
/**
diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp
index 0f3c3669..93a44d4c 100644
--- a/launcher/StringUtils.cpp
+++ b/launcher/StringUtils.cpp
@@ -1,5 +1,7 @@
#include "StringUtils.h"
+#include
+
/// 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
@@ -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
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;
+}
diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h
index 1799605b..1ba19555 100644
--- a/launcher/StringUtils.h
+++ b/launcher/StringUtils.h
@@ -29,4 +29,6 @@ inline QString fromStdString(string s)
#endif
int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs);
+
+QString getRandomAlphaNumeric(const int length);
} // namespace StringUtils
diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp
index 9b5589ab..78486507 100644
--- a/launcher/filelink/FileLink.cpp
+++ b/launcher/filelink/FileLink.cpp
@@ -31,8 +31,6 @@
#include
-
-#include
#include
#include
@@ -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
// attach the parent console
@@ -81,18 +79,116 @@ FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv)
// Commandline parsing
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({
-
+ {{"s", "server"}, "Join the specified server on launch", "pipe name"}
});
parser.addHelpOption();
parser.addVersionOption();
parser.process(arguments());
+ QString serverToJoin = parser.value("server");
+
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();
}
diff --git a/launcher/filelink/FileLink.h b/launcher/filelink/FileLink.h
index 253d1394..5d0ba123 100644
--- a/launcher/filelink/FileLink.h
+++ b/launcher/filelink/FileLink.h
@@ -32,6 +32,11 @@
#include
#include
#include
+#include
+#include
+
+#define PRISM_EXTERNAL_EXE
+#include "FileSystem.h"
class FileLinkApp : public QCoreApplication
{
@@ -43,7 +48,17 @@ public:
virtual ~FileLinkApp();
private:
+
+ void joinServer(QString server);
+ void readPathPairs();
+ void runLink();
+
QDateTime m_startTime;
+ QLocalSocket socket;
+ QDataStream in;
+ quint32 blockSize;
+
+ QList m_path_pairs;
#if defined Q_OS_WIN32
// used on Windows to attach the standard IO streams
--
cgit
From 8ba51c790098ec9ebe3d2ef686f823b61c8a3645 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Wed, 8 Feb 2023 12:36:15 -0800
Subject: refactor: make complete list of links to make and send that.
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 221 +++++++++++++++++++++++++++--------------
launcher/FileSystem.h | 35 +++++--
launcher/filelink/FileLink.cpp | 134 ++++++++++++++++++++-----
launcher/filelink/FileLink.h | 6 +-
4 files changed, 285 insertions(+), 111 deletions(-)
(limited to 'launcher')
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
//for ShellExecute
#include
-//#include
#include
#include
#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
+ {