Qt: Improve updater error reporting
And swap from Qt file functions to our own.
This commit is contained in:
parent
57d3aa4850
commit
b28ca2b78a
|
@ -22,7 +22,7 @@
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QJsonArray>
|
#include <QtCore/QJsonArray>
|
||||||
#include <QtCore/QJsonDocument>
|
#include <QtCore/QJsonDocument>
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
|
@ -44,6 +44,8 @@ static constexpr u32 HTTP_POLL_INTERVAL = 10;
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
#include "common/cocoa_tools.h"
|
#include "common/cocoa_tools.h"
|
||||||
|
#else
|
||||||
|
#include <sys/stat.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Logic to detect whether we can use the auto updater.
|
// Logic to detect whether we can use the auto updater.
|
||||||
|
@ -602,8 +604,7 @@ static constexpr char UPDATER_ARCHIVE_NAME[] = "update.zip";
|
||||||
|
|
||||||
bool AutoUpdaterDialog::doesUpdaterNeedElevation(const std::string& application_dir) const
|
bool AutoUpdaterDialog::doesUpdaterNeedElevation(const std::string& application_dir) const
|
||||||
{
|
{
|
||||||
// Try to create a dummy text file in the PCSX2 updater directory. If it fails, we probably won't have write
|
// Try to create a dummy text file in the updater directory. If it fails, we probably won't have write permission.
|
||||||
// permission.
|
|
||||||
const std::string dummy_path = Path::Combine(application_dir, "update.txt");
|
const std::string dummy_path = Path::Combine(application_dir, "update.txt");
|
||||||
auto fp = FileSystem::OpenManagedCFile(dummy_path.c_str(), "wb");
|
auto fp = FileSystem::OpenManagedCFile(dummy_path.c_str(), "wb");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
|
@ -620,15 +621,16 @@ bool AutoUpdaterDialog::processUpdate(const std::vector<u8>& update_data)
|
||||||
const std::string update_zip_path = Path::Combine(EmuFolders::DataRoot, UPDATER_ARCHIVE_NAME);
|
const std::string update_zip_path = Path::Combine(EmuFolders::DataRoot, UPDATER_ARCHIVE_NAME);
|
||||||
const std::string updater_path = Path::Combine(EmuFolders::DataRoot, UPDATER_EXECUTABLE);
|
const std::string updater_path = Path::Combine(EmuFolders::DataRoot, UPDATER_EXECUTABLE);
|
||||||
|
|
||||||
if ((FileSystem::FileExists(update_zip_path.c_str()) && !FileSystem::DeleteFile(update_zip_path.c_str())))
|
Error error;
|
||||||
|
if ((FileSystem::FileExists(update_zip_path.c_str()) && !FileSystem::DeleteFile(update_zip_path.c_str(), &error)))
|
||||||
{
|
{
|
||||||
reportError("Removing existing update zip failed");
|
reportError(fmt::format("Removing existing update zip failed:\n{}", error.GetDescription()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FileSystem::WriteBinaryFile(update_zip_path.c_str(), update_data.data(), update_data.size()))
|
if (!FileSystem::WriteAtomicRenamedFile(update_zip_path.c_str(), update_data, &error))
|
||||||
{
|
{
|
||||||
reportError(fmt::format("Writing update zip to '{}' failed", update_zip_path));
|
reportError(fmt::format("Writing update zip to '{}' failed:\n{}", update_zip_path, error.GetDescription()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,7 +688,7 @@ bool AutoUpdaterDialog::extractUpdater(const std::string& zip_path, const std::s
|
||||||
|
|
||||||
if (std::fwrite(chunk, size, 1, fp.get()) != 1)
|
if (std::fwrite(chunk, size, 1, fp.get()) != 1)
|
||||||
{
|
{
|
||||||
Error::SetString(error, "Failed to write updater exe");
|
Error::SetErrno(error, "Failed to write updater exe: fwrite() failed: ", errno);
|
||||||
unzClose(zf);
|
unzClose(zf);
|
||||||
fp.reset();
|
fp.reset();
|
||||||
FileSystem::DeleteFile(destination_path.c_str());
|
FileSystem::DeleteFile(destination_path.c_str());
|
||||||
|
@ -742,9 +744,12 @@ void AutoUpdaterDialog::cleanupAfterUpdate()
|
||||||
if (!FileSystem::FileExists(updater_path.c_str()))
|
if (!FileSystem::FileExists(updater_path.c_str()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!FileSystem::DeleteFile(updater_path.c_str()))
|
Error error;
|
||||||
|
if (!FileSystem::DeleteFile(updater_path.c_str(), &error))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(nullptr, tr("Updater Error"), tr("Failed to remove updater exe after update."));
|
QMessageBox::critical(
|
||||||
|
nullptr, tr("Updater Error"),
|
||||||
|
tr("Failed to remove updater exe after update:\n%1").arg(QString::fromStdString(error.GetDescription())));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -784,24 +789,19 @@ bool AutoUpdaterDialog::processUpdate(const std::vector<u8>& update_data)
|
||||||
// We use the user data directory to temporarily store the update zip.
|
// We use the user data directory to temporarily store the update zip.
|
||||||
const std::string zip_path = Path::Combine(EmuFolders::DataRoot, "update.zip");
|
const std::string zip_path = Path::Combine(EmuFolders::DataRoot, "update.zip");
|
||||||
const std::string staging_directory = Path::Combine(EmuFolders::DataRoot, "UPDATE_STAGING");
|
const std::string staging_directory = Path::Combine(EmuFolders::DataRoot, "UPDATE_STAGING");
|
||||||
if (FileSystem::FileExists(zip_path.c_str()) && !FileSystem::DeleteFile(zip_path.c_str()))
|
Error error;
|
||||||
|
if (FileSystem::FileExists(zip_path.c_str()) && !FileSystem::DeleteFile(zip_path.c_str(), &error))
|
||||||
{
|
{
|
||||||
reportError("Failed to remove old update zip.");
|
reportError(fmt::format("Failed to remove old update zip:\n{}", error.GetDescription()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save update.
|
// Save update.
|
||||||
|
if (!FileSystem::WriteAtomicRenamedFile(zip_path.c_str(), update_data, &error))
|
||||||
{
|
{
|
||||||
QFile zip_file(QString::fromStdString(zip_path));
|
reportError(fmt::format("Writing update zip to '{}' failed:\n{}", zip_path, error.GetDescription()));
|
||||||
if (!zip_file.open(QIODevice::WriteOnly) ||
|
|
||||||
zip_file.write(reinterpret_cast<const char*>(update_data.data()), static_cast<qint64>(update_data.size())) !=
|
|
||||||
static_cast<qint64>(update_data.size()))
|
|
||||||
{
|
|
||||||
reportError(fmt::format("Writing update zip to '{}' failed", zip_path));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
zip_file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
INFO_LOG("Beginning update:\nUpdater path: {}\nZip path: {}\nStaging directory: {}\nOutput directory: {}",
|
INFO_LOG("Beginning update:\nUpdater path: {}\nZip path: {}\nStaging directory: {}\nOutput directory: {}",
|
||||||
updater_app, zip_path, staging_directory, bundle_path.value());
|
updater_app, zip_path, staging_directory, bundle_path.value());
|
||||||
|
@ -832,66 +832,105 @@ bool AutoUpdaterDialog::processUpdate(const std::vector<u8>& update_data)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString qappimage_path(QString::fromUtf8(appimage_path));
|
if (!FileSystem::FileExists(appimage_path))
|
||||||
if (!QFile::exists(qappimage_path))
|
|
||||||
{
|
{
|
||||||
reportError(fmt::format("Current AppImage does not exist: {}", appimage_path));
|
reportError(fmt::format("Current AppImage does not exist: {}", appimage_path));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString new_appimage_path(qappimage_path + QStringLiteral(".new"));
|
const std::string new_appimage_path = fmt::format("{}.new", appimage_path);
|
||||||
const QString backup_appimage_path(qappimage_path + QStringLiteral(".backup"));
|
const std::string backup_appimage_path = fmt::format("{}.backup", appimage_path);
|
||||||
INFO_LOG("APPIMAGE = {}", appimage_path);
|
INFO_LOG("APPIMAGE = {}", appimage_path);
|
||||||
INFO_LOG("Backup AppImage path = {}", backup_appimage_path.toStdString());
|
INFO_LOG("Backup AppImage path = {}", backup_appimage_path);
|
||||||
INFO_LOG("New AppImage path = {}", new_appimage_path.toStdString());
|
INFO_LOG("New AppImage path = {}", new_appimage_path);
|
||||||
|
|
||||||
// Remove old "new" appimage and existing backup appimage.
|
// Remove old "new" appimage and existing backup appimage.
|
||||||
if (QFile::exists(new_appimage_path) && !QFile::remove(new_appimage_path))
|
Error error;
|
||||||
|
if (FileSystem::FileExists(new_appimage_path.c_str()) && !FileSystem::DeleteFile(new_appimage_path.c_str(), &error))
|
||||||
{
|
{
|
||||||
reportError(fmt::format("Failed to remove old destination AppImage: {}", new_appimage_path.toStdString()));
|
reportError(
|
||||||
|
fmt::format("Failed to remove old destination AppImage: {}:\n{}", new_appimage_path, error.GetDescription()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (QFile::exists(backup_appimage_path) && !QFile::remove(backup_appimage_path))
|
if (FileSystem::FileExists(backup_appimage_path.c_str()) &&
|
||||||
|
!FileSystem::DeleteFile(backup_appimage_path.c_str(), &error))
|
||||||
{
|
{
|
||||||
reportError(fmt::format("Failed to remove old backup AppImage: {}", new_appimage_path.toStdString()));
|
reportError(
|
||||||
|
fmt::format("Failed to remove old backup AppImage: {}:\n{}", backup_appimage_path, error.GetDescription()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write "new" appimage.
|
// Write "new" appimage.
|
||||||
{
|
{
|
||||||
// We want to copy the permissions from the old appimage to the new one.
|
// We want to copy the permissions from the old appimage to the new one.
|
||||||
QFile old_file(qappimage_path);
|
static constexpr int permission_mask = S_IRWXU | S_IRWXG | S_IRWXO;
|
||||||
const QFileDevice::Permissions old_permissions = old_file.permissions();
|
struct stat old_stat;
|
||||||
QFile new_file(new_appimage_path);
|
if (!FileSystem::StatFile(appimage_path, &old_stat, &error))
|
||||||
if (!new_file.open(QIODevice::WriteOnly) ||
|
|
||||||
new_file.write(reinterpret_cast<const char*>(update_data.data()), static_cast<qint64>(update_data.size())) !=
|
|
||||||
static_cast<qint64>(update_data.size()) ||
|
|
||||||
!new_file.setPermissions(old_permissions))
|
|
||||||
{
|
{
|
||||||
QFile::remove(new_appimage_path);
|
reportError(fmt::format("Failed to get old AppImage {} permissions:\n{}", appimage_path, error.GetDescription()));
|
||||||
reportError(fmt::format("Failed to write new destination AppImage: {}", new_appimage_path.toStdString()));
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do this as a manual write here, rather than using WriteAtomicUpdatedFile(), because we want to write the file
|
||||||
|
// and set the permissions as one atomic operation.
|
||||||
|
FileSystem::ManagedCFilePtr fp = FileSystem::OpenManagedCFile(new_appimage_path.c_str(), "wb", &error);
|
||||||
|
bool success = static_cast<bool>(fp);
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
if (std::fwrite(update_data.data(), update_data.size(), 1, fp.get()) == 1 && std::fflush(fp.get()) == 0)
|
||||||
|
{
|
||||||
|
const int fd = fileno(fp.get());
|
||||||
|
if (fd >= 0)
|
||||||
|
{
|
||||||
|
if (fchmod(fd, old_stat.st_mode & permission_mask) != 0)
|
||||||
|
{
|
||||||
|
error.SetErrno("fchmod() failed: ", errno);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error.SetErrno("fileno() failed: ", errno);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error.SetErrno("fwrite() failed: ", errno);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp.reset();
|
||||||
|
if (!success)
|
||||||
|
FileSystem::DeleteFile(new_appimage_path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
reportError(
|
||||||
|
fmt::format("Failed to write new destination AppImage: {}:\n{}", new_appimage_path, error.GetDescription()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename "old" appimage.
|
// Rename "old" appimage.
|
||||||
if (!QFile::rename(qappimage_path, backup_appimage_path))
|
if (!FileSystem::RenamePath(appimage_path, backup_appimage_path.c_str(), &error))
|
||||||
{
|
{
|
||||||
reportError(fmt::format("Failed to rename old AppImage to {}", backup_appimage_path.toStdString()));
|
reportError(fmt::format("Failed to rename old AppImage to {}:\n{}", backup_appimage_path, error.GetDescription()));
|
||||||
QFile::remove(new_appimage_path);
|
FileSystem::DeleteFile(new_appimage_path.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename "new" appimage.
|
// Rename "new" appimage.
|
||||||
if (!QFile::rename(new_appimage_path, qappimage_path))
|
if (!FileSystem::RenamePath(new_appimage_path.c_str(), appimage_path, &error))
|
||||||
{
|
{
|
||||||
reportError(fmt::format("Failed to rename new AppImage to {}", qappimage_path.toStdString()));
|
reportError(fmt::format("Failed to rename new AppImage to {}:\n{}", appimage_path, error.GetDescription()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute new appimage.
|
// Execute new appimage.
|
||||||
QProcess* new_process = new QProcess();
|
QProcess* new_process = new QProcess();
|
||||||
new_process->setProgram(qappimage_path);
|
new_process->setProgram(QString::fromUtf8(appimage_path));
|
||||||
new_process->setArguments(QStringList{QStringLiteral("-updatecleanup")});
|
new_process->setArguments(QStringList{QStringLiteral("-updatecleanup")});
|
||||||
if (!new_process->startDetached())
|
if (!new_process->startDetached())
|
||||||
{
|
{
|
||||||
|
@ -910,16 +949,14 @@ void AutoUpdaterDialog::cleanupAfterUpdate()
|
||||||
if (!appimage_path)
|
if (!appimage_path)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const QString qappimage_path(QString::fromUtf8(appimage_path));
|
const std::string backup_appimage_path = fmt::format("{}.backup", appimage_path);
|
||||||
const QString backup_appimage_path(qappimage_path + QStringLiteral(".backup"));
|
if (!FileSystem::FileExists(backup_appimage_path.c_str()))
|
||||||
if (!QFile::exists(backup_appimage_path))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
INFO_LOG(QStringLiteral("Removing backup AppImage %1").arg(backup_appimage_path).toStdString().c_str());
|
Error error;
|
||||||
if (!QFile::remove(backup_appimage_path))
|
INFO_LOG("Removing backup AppImage: {}", backup_appimage_path);
|
||||||
{
|
if (!FileSystem::DeleteFile(backup_appimage_path.c_str(), &error))
|
||||||
ERROR_LOG(QStringLiteral("Failed to remove backup AppImage %1").arg(backup_appimage_path).toStdString().c_str());
|
ERROR_LOG("Failed to remove backup AppImage {}: {}", backup_appimage_path, error.GetDescription());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
Loading…
Reference in New Issue