feat(symlinks&hardlinks): linkup copy dialog

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
Rachel Powers 2023-02-08 14:30:45 -08:00
parent 8ba51c7900
commit 5978882378
9 changed files with 96 additions and 26 deletions

View File

@ -420,7 +420,7 @@ void create_link::runPrivlaged(const QString& offset)
qint64 byteswritten = clientConnection->write(block); qint64 byteswritten = clientConnection->write(block);
bool bytesflushed = clientConnection->flush(); bool bytesflushed = clientConnection->flush();
qDebug() << "block flushed" << byteswritten << bytesflushed; qDebug() << "block flushed" << byteswritten << bytesflushed;
//clientConnection->disconnectFromServer();
}); });
qDebug() << "Listening on pipe" << serverName; qDebug() << "Listening on pipe" << serverName;
@ -437,7 +437,6 @@ void create_link::runPrivlaged(const QString& offset)
} }
void ExternalLinkFileProcess::runLinkFile() { void ExternalLinkFileProcess::runLinkFile() {
QString fileLinkExe = PathCombine(QCoreApplication::instance()->applicationDirPath(), BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink"); QString fileLinkExe = PathCombine(QCoreApplication::instance()->applicationDirPath(), BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink");
QString params = "-s " + m_server; QString params = "-s " + m_server;
@ -463,7 +462,7 @@ void ExternalLinkFileProcess::runLinkFile() {
ShExecInfo.lpFile = programNameWin; ShExecInfo.lpFile = programNameWin;
ShExecInfo.lpParameters = paramsWin; ShExecInfo.lpParameters = paramsWin;
ShExecInfo.lpDirectory = NULL; ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_NORMAL; ShExecInfo.nShow = SW_HIDE;
ShExecInfo.hInstApp = NULL; ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo); ShellExecuteEx(&ShExecInfo);

View File

@ -211,16 +211,21 @@ class create_link : public QObject {
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
int totalLinked() { return m_linked; }
void runPrivlaged() { runPrivlaged(QString()); } void runPrivlaged() { runPrivlaged(QString()); }
void runPrivlaged(const QString& offset); void runPrivlaged(const QString& offset);
int totalLinked() { return m_linked; } QList<LinkResult> getResults() { return m_path_results; }
signals: signals:
void fileLinked(const QString& srcName, const QString& dstName); void fileLinked(const QString& srcName, const QString& dstName);
void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value); void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value);
void finishedPrivlaged(bool gotResults);
void finished(); void finished();
void finishedPrivlaged(bool gotResults);
private: private:
bool operator()(const QString& offset, bool dryRun = false); bool operator()(const QString& offset, bool dryRun = false);

View File

@ -103,9 +103,9 @@ bool InstanceCopyPrefs::isUseHardLinksEnabled() const
return useHardLinks; return useHardLinks;
} }
bool InstanceCopyPrefs::isLinkWorldsEnabled() const bool InstanceCopyPrefs::isDontLinkSavesEnabled() const
{ {
return linkWorlds; return dontLinkSaves;
} }
// ======= Setters ======= // ======= Setters =======
@ -159,7 +159,7 @@ void InstanceCopyPrefs::enableUseHardLinks(bool b)
useHardLinks = b; useHardLinks = b;
} }
void InstanceCopyPrefs::enableLinkWorlds(bool b) void InstanceCopyPrefs::enableDontLinkSaves(bool b)
{ {
linkWorlds = b; dontLinkSaves = b;
} }

View File

@ -21,7 +21,7 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isCopyScreenshotsEnabled() const; [[nodiscard]] bool isCopyScreenshotsEnabled() const;
[[nodiscard]] bool isLinkFilesEnabled() const; [[nodiscard]] bool isLinkFilesEnabled() const;
[[nodiscard]] bool isUseHardLinksEnabled() const; [[nodiscard]] bool isUseHardLinksEnabled() const;
[[nodiscard]] bool isLinkWorldsEnabled() const; [[nodiscard]] bool isDontLinkSavesEnabled() const;
// Setters // Setters
void enableCopySaves(bool b); void enableCopySaves(bool b);
void enableKeepPlaytime(bool b); void enableKeepPlaytime(bool b);
@ -33,7 +33,7 @@ struct InstanceCopyPrefs {
void enableCopyScreenshots(bool b); void enableCopyScreenshots(bool b);
void enableLinkFiles(bool b); void enableLinkFiles(bool b);
void enableUseHardLinks(bool b); void enableUseHardLinks(bool b);
void enableLinkWorlds(bool b); void enableDontLinkSaves(bool b);
protected: // data protected: // data
bool copySaves = true; bool copySaves = true;
@ -46,5 +46,5 @@ struct InstanceCopyPrefs {
bool copyScreenshots = true; bool copyScreenshots = true;
bool linkFiles = false; bool linkFiles = false;
bool useHardLinks = false; bool useHardLinks = false;
bool linkWorlds = true; bool dontLinkSaves = false;
}; };

View File

@ -11,6 +11,11 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
m_keepPlaytime = prefs.isKeepPlaytimeEnabled(); m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
QString filters = prefs.getSelectedFiltersAsRegex(); QString filters = prefs.getSelectedFiltersAsRegex();
m_useLinks = prefs.isLinkFilesEnabled();
m_useHardLinks = prefs.isUseHardLinksEnabled();
m_copySaves = prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
if (!filters.isEmpty()) if (!filters.isEmpty())
{ {
// Set regex filter: // Set regex filter:
@ -25,11 +30,71 @@ void InstanceCopyTask::executeTask()
{ {
setStatus(tr("Copying instance %1").arg(m_origInstance->name())); setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]{ auto copySaves = [&](){
FS::copy savesCopy(FS::PathCombine(m_origInstance->instanceRoot(), "saves") , FS::PathCombine(m_stagingPath, "saves"));
savesCopy.followSymlinks(false);
return savesCopy();
};
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves]{
if (m_useLinks) {
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
folderLink.linkRecursively(true).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
bool there_were_errors = false;
if(!folderLink()){
#if defined Q_OS_WIN32
if (!m_useHardLinks) {
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
qDebug() << "atempting to run with privelage";
QEventLoop loop;
bool got_priv_results = false;
connect(&folderLink, &FS::create_link::finishedPrivlaged, this, [&](bool gotResults){
if (!gotResults) {
qDebug() << "Privlaged run exited without results!";
}
got_priv_results = gotResults;
loop.quit();
});
folderLink.runPrivlaged();
loop.exec(); // wait for the finished signal
for (auto result : folderLink.getResults()) {
if (result.err_value != 0) {
there_were_errors = true;
}
}
if (m_copySaves) {
there_were_errors |= !copySaves();
}
return got_priv_results && !there_were_errors;
} else {
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
}
#else
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
#endif return false;
}
if (m_copySaves) {
there_were_errors |= !copySaves();
}
return !there_were_errors;
} else {
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
folderCopy.followSymlinks(false).matcher(m_matcher.get()); folderCopy.followSymlinks(false).matcher(m_matcher.get());
return folderCopy(); return folderCopy();
}
}); });
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished); connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted); connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);

View File

@ -30,4 +30,7 @@ private:
QFutureWatcher<bool> m_copyFutureWatcher; QFutureWatcher<bool> m_copyFutureWatcher;
std::unique_ptr<IPathMatcher> m_matcher; std::unique_ptr<IPathMatcher> m_matcher;
bool m_keepPlaytime; bool m_keepPlaytime;
bool m_useLinks = false;
bool m_useHardLinks = false;
bool m_copySaves = true;
}; };

View File

@ -88,7 +88,7 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled()); ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled()); ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
ui->linkWorldsCheckbox->setChecked(m_selectedOptions.isLinkWorldsEnabled()); ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
} }
CopyInstanceDialog::~CopyInstanceDialog() CopyInstanceDialog::~CopyInstanceDialog()
@ -179,6 +179,7 @@ void CopyInstanceDialog::on_selectAllCheckbox_stateChanged(int state)
void CopyInstanceDialog::on_copySavesCheckbox_stateChanged(int state) void CopyInstanceDialog::on_copySavesCheckbox_stateChanged(int state)
{ {
m_selectedOptions.enableCopySaves(state == Qt::Checked); m_selectedOptions.enableCopySaves(state == Qt::Checked);
ui->dontLinkSavesCheckbox->setChecked((state == Qt::Checked) && ui->dontLinkSavesCheckbox->isChecked());
updateSelectAllCheckbox(); updateSelectAllCheckbox();
} }
@ -235,7 +236,7 @@ void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
m_selectedOptions.enableUseHardLinks(state == Qt::Checked); m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
} }
void CopyInstanceDialog::on_linkWorldsCheckbox_stateChanged(int state) void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
{ {
m_selectedOptions.enableLinkWorlds(state == Qt::Checked); m_selectedOptions.enableDontLinkSaves(state == Qt::Checked);
} }

View File

@ -57,7 +57,7 @@ slots:
void on_copyScreenshotsCheckbox_stateChanged(int state); void on_copyScreenshotsCheckbox_stateChanged(int state);
void on_linkFilesGroup_toggled(bool checked); void on_linkFilesGroup_toggled(bool checked);
void on_hardLinksCheckbox_stateChanged(int state); void on_hardLinksCheckbox_stateChanged(int state);
void on_linkWorldsCheckbox_stateChanged(int state); void on_dontLinkSavesCheckbox_stateChanged(int state);
private: private:
void checkAllCheckboxes(const bool& b); void checkAllCheckboxes(const bool& b);

View File

@ -240,17 +240,14 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="linkWorldsCheckbox"> <widget class="QCheckBox" name="dontLinkSavesCheckbox">
<property name="toolTip"> <property name="toolTip">
<string>World save data will be linked and thus shared between instances.</string> <string>If "copy saves" is selected world save data will be copied instead of linked and thus not shared between instances.</string>
</property> </property>
<property name="text"> <property name="text">
<string>Link worlds</string> <string>Don't link saves</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool>
</property>
<property name="tristate">
<bool>false</bool> <bool>false</bool>
</property> </property>
</widget> </widget>