aboutsummaryrefslogtreecommitdiff
path: root/launcher
diff options
context:
space:
mode:
authorRachel Powers <508861+Ryex@users.noreply.github.com>2023-02-09 02:02:40 -0700
committerRachel Powers <508861+Ryex@users.noreply.github.com>2023-03-20 14:56:32 -0700
commit397e7f036339b09569317300423261f2b37d6119 (patch)
treef4854cbe7d4737704f3f3580fd0e81bcd772047d /launcher
parentc5bbe42b57075a4b428d0be1c1ca9f51701a1a7c (diff)
downloadPrismLauncher-397e7f036339b09569317300423261f2b37d6119.tar.gz
PrismLauncher-397e7f036339b09569317300423261f2b37d6119.tar.bz2
PrismLauncher-397e7f036339b09569317300423261f2b37d6119.zip
feat(reflink): hook up relink / clone on the copy dialog
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
Diffstat (limited to 'launcher')
-rw-r--r--launcher/FileSystem.cpp26
-rw-r--r--launcher/FileSystem.h3
-rw-r--r--launcher/InstanceCopyPrefs.cpp10
-rw-r--r--launcher/InstanceCopyPrefs.h3
-rw-r--r--launcher/InstanceCopyTask.cpp11
-rw-r--r--launcher/InstanceCopyTask.h1
-rw-r--r--launcher/ui/dialogs/CopyInstanceDialog.cpp28
-rw-r--r--launcher/ui/dialogs/CopyInstanceDialog.h5
-rw-r--r--launcher/ui/dialogs/CopyInstanceDialog.ui43
9 files changed, 119 insertions, 11 deletions
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index 7b7fc80b..fd09842f 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -103,6 +103,7 @@ namespace fs = ghc::filesystem;
#include <fcntl.h> /* Definition of FICLONE* constants */
#include <sys/ioctl.h>
#include <errno.h>
+#include <unistd.h>
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
#include <sys/attr.h>
#include <sys/clonefile.h>
@@ -880,6 +881,9 @@ FilesystemInfo statFS(QString path)
info.name = storage_info.name();
info.rootPath = storage_info.rootPath();
+ qDebug() << "Pulling filesystem info for" << info.rootPath;
+ qDebug() << "Filesystem: " << fsTypeName << "detected" << getFilesystemTypeName(info.fsType);
+
return info;
}
@@ -995,33 +999,45 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
// https://man7.org/linux/man-pages/man2/ioctl_ficlone.2.html
int src_fd = open(src_path.c_str(), O_RDONLY);
- if(!src_fd) {
+ if(src_fd == -1) {
qWarning() << "Failed to open file:" << src_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast<std::errc>(errno));
return false;
}
- int dst_fd = open(dst_path.c_str(), O_WRONLY | O_TRUNC);
- if(!dst_fd) {
+ int dst_fd = open(dst_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ if(dst_fd == -1) {
qWarning() << "Failed to open file:" << dst_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast<std::errc>(errno));
+ close(src_fd);
return false;
}
// attempt to clone
- if(!ioctl(dst_fd, FICLONE, src_fd)){
+ if(ioctl(dst_fd, FICLONE, src_fd) == -1){
qWarning() << "Failed to clone file:" << src_path.c_str() << "to" << dst_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast<std::errc>(errno));
+ close(src_fd);
+ close(dst_fd);
return false;
}
+ if(close(src_fd)) {
+ qWarning() << "Failed to close file:" << src_path.c_str();
+ qDebug() << "Error:" << strerror(errno);
+ }
+ if(close(dst_fd)) {
+ qWarning() << "Failed to close file:" << dst_path.c_str();
+ qDebug() << "Error:" << strerror(errno);
+ }
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
// TODO: use clonefile
// clonefile(const char * src, const char * dst, int flags);
// https://www.manpagez.com/man/2/clonefile/
- if (!clonefile(src_path.c_str(), dst_path.c_str(), 0)) {
+ qDebug() << "attempting file clone via clonefile" << src << "to" << dst;
+ if (clonefile(src_path.c_str(), dst_path.c_str(), 0) == -1) {
qWarning() << "Failed to clone file:" << src_path.c_str() << "to" << dst_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast<std::errc>(errno));
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 531036dd..aa28de93 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -329,6 +329,7 @@ enum class FilesystemType {
HFS,
HFSPLUS,
HFSX,
+ FUSEBLK,
UNKNOWN
};
@@ -346,6 +347,7 @@ static const QMap<FilesystemType, QString> s_filesystem_type_names = {
{FilesystemType::HFS, QString("HFS")},
{FilesystemType::HFSPLUS, QString("HFSPLUS")},
{FilesystemType::HFSX, QString("HFSX")},
+ {FilesystemType::FUSEBLK, QString("FUSEBLK")},
{FilesystemType::UNKNOWN, QString("UNKNOWN")}
};
@@ -365,6 +367,7 @@ static const QMap<QString, FilesystemType> s_filesystem_type_names_inverse = {
{QString("HFSPLUS"), FilesystemType::HFSPLUS},
{QString("HFSX"), FilesystemType::HFSX},
{QString("HFS"), FilesystemType::HFS},
+ {QString("FUSEBLK"), FilesystemType::FUSEBLK},
{QString("UNKNOWN"), FilesystemType::UNKNOWN}
};
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index 59825ced..f2aa5e69 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -113,6 +113,11 @@ bool InstanceCopyPrefs::isDontLinkSavesEnabled() const
return dontLinkSaves;
}
+bool InstanceCopyPrefs::isUseCloneEnabled() const
+{
+ return useClone;
+}
+
// ======= Setters =======
void InstanceCopyPrefs::enableCopySaves(bool b)
{
@@ -173,3 +178,8 @@ void InstanceCopyPrefs::enableDontLinkSaves(bool b)
{
dontLinkSaves = b;
}
+
+void InstanceCopyPrefs::enableUseClone(bool b)
+{
+ useClone = b;
+} \ No newline at end of file
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 9fc9dcab..583fdd4c 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -23,6 +23,7 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isLinkRecursivelyEnabled() const;
[[nodiscard]] bool isUseHardLinksEnabled() const;
[[nodiscard]] bool isDontLinkSavesEnabled() const;
+ [[nodiscard]] bool isUseCloneEnabled() const;
// Setters
void enableCopySaves(bool b);
void enableKeepPlaytime(bool b);
@@ -36,6 +37,7 @@ struct InstanceCopyPrefs {
void enableLinkRecursively(bool b);
void enableUseHardLinks(bool b);
void enableDontLinkSaves(bool b);
+ void enableUseClone(bool b);
protected: // data
bool copySaves = true;
@@ -50,4 +52,5 @@ struct InstanceCopyPrefs {
bool linkRecursively = false;
bool useHardLinks = false;
bool dontLinkSaves = false;
+ bool useClone = false;
};
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 81502d89..b3ea54b0 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -17,6 +17,7 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
m_linkRecursively = prefs.isLinkRecursivelyEnabled();
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
+ m_useClone = prefs.isUseCloneEnabled();
if (!filters.isEmpty())
{
@@ -40,7 +41,12 @@ void InstanceCopyTask::executeTask()
};
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves]{
- if (m_useLinks) {
+ if (m_useClone) {
+ FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
+ folderClone.matcher(m_matcher.get());
+
+ return folderClone();
+ } else if (m_useLinks) {
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
folderLink.linkRecursively(m_linkRecursively).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
@@ -83,7 +89,8 @@ void InstanceCopyTask::executeTask()
}
#else
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
-#endif return false;
+#endif
+ return false;
}
if (m_copySaves) {
diff --git a/launcher/InstanceCopyTask.h b/launcher/InstanceCopyTask.h
index 3dce1662..aea9d99a 100644
--- a/launcher/InstanceCopyTask.h
+++ b/launcher/InstanceCopyTask.h
@@ -34,4 +34,5 @@ private:
bool m_useHardLinks = false;
bool m_copySaves = false;
bool m_linkRecursively = false;
+ bool m_useClone = false;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 55962c5a..c51bc067 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -46,6 +46,7 @@
#include "icons/IconList.h"
#include "BaseInstance.h"
#include "InstanceList.h"
+#include "FileSystem.h"
CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
:QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
@@ -85,11 +86,22 @@ 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->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
+
+ auto detectedOS = FS::statFS(m_original->instanceRoot()).fsType;
+ m_cloneSupported = FS::canCloneOnFS(detectedOS);
+
+ if (m_cloneSupported) {
+ ui->cloneSupportedLabel->setText(tr("Clone / Reflink is supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS)));
+ } else {
+ ui->cloneSupportedLabel->setText(tr("Clone / Reflink not supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS)));
+ }
+
+ updateUseCloneCheckbox();
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -152,6 +164,12 @@ void CopyInstanceDialog::updateSelectAllCheckbox()
ui->selectAllCheckbox->blockSignals(false);
}
+void CopyInstanceDialog::updateUseCloneCheckbox()
+{
+ ui->useCloneCheckbox->setEnabled(m_cloneSupported && !ui->linkFilesGroup->isChecked());
+ ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled());
+}
+
void CopyInstanceDialog::on_iconButton_clicked()
{
IconPickerDialog dlg(this);
@@ -230,6 +248,7 @@ void CopyInstanceDialog::on_copyScreenshotsCheckbox_stateChanged(int state)
void CopyInstanceDialog::on_linkFilesGroup_toggled(bool checked)
{
m_selectedOptions.enableLinkFiles(checked);
+ updateUseCloneCheckbox();
}
void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
@@ -254,3 +273,10 @@ void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
{
m_selectedOptions.enableDontLinkSaves(state == Qt::Checked);
}
+
+void CopyInstanceDialog::on_useCloneCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableUseClone(m_cloneSupported && (state == Qt::Checked));
+ ui->linkFilesGroup->setEnabled(!m_selectedOptions.isUseCloneEnabled());
+ updateUseCloneCheckbox();
+} \ No newline at end of file
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 2fc6f38a..859c643c 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -16,6 +16,7 @@
#pragma once
#include <QDialog>
+#include "BaseInstance.h"
#include "BaseVersion.h"
#include "InstanceCopyPrefs.h"
@@ -59,13 +60,17 @@ slots:
void on_recursiveLinkCheckbox_stateChanged(int state);
void on_hardLinksCheckbox_stateChanged(int state);
void on_dontLinkSavesCheckbox_stateChanged(int state);
+ void on_useCloneCheckbox_stateChanged(int state);
private:
void checkAllCheckboxes(const bool& b);
void updateSelectAllCheckbox();
+ void updateUseCloneCheckbox();
+
/* data */
Ui::CopyInstanceDialog *ui;
QString InstIconKey;
InstancePtr m_original;
InstanceCopyPrefs m_selectedOptions;
+ bool m_cloneSupported = false;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index 8df0d3db..ce8657f6 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>525</width>
- <height>581</height>
+ <width>531</width>
+ <height>640</height>
</rect>
</property>
<property name="windowTitle">
@@ -275,6 +275,44 @@
</layout>
</widget>
</item>
+ <item>
+ <widget class="QGroupBox" name="horizontalGroupBox">
+ <property name="title">
+ <string>Clone / Reflink (Copy On Write) Options</string>
+ </property>
+ <layout class="QHBoxLayout" name="useCloneLayout">
+ <item>
+ <widget class="QCheckBox" name="useCloneCheckbox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Use Clone / Reflink</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="cloneSupportedLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Clone / Reflink not supported on this filesystem</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="margin">
+ <number>4</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
</layout>
</item>
<item>
@@ -302,7 +340,6 @@
<tabstop>copyServersCheckbox</tabstop>
<tabstop>copyResPacksCheckbox</tabstop>
<tabstop>copyModsCheckbox</tabstop>
- <tabstop>linkFilesGroup</tabstop>
<tabstop>recursiveLinkCheckbox</tabstop>
<tabstop>hardLinksCheckbox</tabstop>
<tabstop>dontLinkSavesCheckbox</tabstop>