summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorShawn Hoffman <godisgovernment@gmail.com>2022-10-27 13:24:36 -0700
committerShawn Hoffman <godisgovernment@gmail.com>2022-10-30 12:04:57 -0700
commit089886a6f869992f7566ee9543e136e136b168aa (patch)
tree85a80d8e46df5707cc17db53e99122a435f4856d /Source/Core
parent68875dc06b8c9aa72cd2bc87d3b2d4eda73e020c (diff)
MacUpdater: check os version
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Common/build_info.txt.in1
-rw-r--r--Source/Core/MacUpdater/MacUI.mm63
-rw-r--r--Source/Core/UpdaterCommon/Platform.h89
-rw-r--r--Source/Core/UpdaterCommon/UpdaterCommon.cpp70
-rw-r--r--Source/Core/UpdaterCommon/UpdaterCommon.h37
-rw-r--r--Source/Core/WinUpdater/Platform.cpp169
6 files changed, 246 insertions, 183 deletions
diff --git a/Source/Core/Common/build_info.txt.in b/Source/Core/Common/build_info.txt.in
index 3ae938cfc5..5d658d95e6 100644
--- a/Source/Core/Common/build_info.txt.in
+++ b/Source/Core/Common/build_info.txt.in
@@ -2,7 +2,6 @@
// Updater will fail the update if the user does not meet this requirement.
OSMinimumVersionWin10=10.0.15063.0
OSMinimumVersionWin11=10.0.22000.0
-OSMinimumVersionMacOS=10.14
// This is the runtime which was compiled against - providing a way for Updater to detect if update
// is needed before executing this binary. Note that, annoyingly, the version in environment
diff --git a/Source/Core/MacUpdater/MacUI.mm b/Source/Core/MacUpdater/MacUI.mm
index 0b2699b1dd..388f5a1da4 100644
--- a/Source/Core/MacUpdater/MacUI.mm
+++ b/Source/Core/MacUpdater/MacUI.mm
@@ -5,6 +5,7 @@
#include "UpdaterCommon/Platform.h"
#include "UpdaterCommon/UI.h"
+#include "UpdaterCommon/UpdaterCommon.h"
#include <Cocoa/Cocoa.h>
#include <unistd.h>
@@ -138,20 +139,56 @@ void UI::Init()
{
}
-Platform::BuildInfo::BuildInfo(const std::string& content)
+bool Platform::VersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
+ const std::string& install_base_path, const std::string& temp_dir,
+ FILE* log_fp)
{
- map = {{"OSMinimumVersionMacOS", ""}};
- Parse(content);
-}
+ const auto op_it = std::find_if(to_update.cbegin(), to_update.cend(), [&](const auto& op) {
+ return op.filename == "Dolphin.app/Contents/Info.plist";
+ });
+ if (op_it == to_update.cend())
+ return true;
-bool Platform::VersionCheck(const BuildInfo& this_build_info, const BuildInfo& next_build_info)
-{
- // TODO implement OS Minimum Version check
- // It should go something like this:
- // auto target_version = next_build_info.GetVersion("OSMinimumVersionMacOS");
- // if (!target_version.has_value() || current_version >= target_version)
- // return true;
- // show error
- // return false;
+ const auto op = *op_it;
+ std::string plist_path = temp_dir + "/" + HexEncode(op.new_hash.data(), op.new_hash.size());
+
+ NSData* data = [NSData dataWithContentsOfFile:[NSString stringWithCString:plist_path.c_str()]];
+ if (!data)
+ {
+ fprintf(log_fp, "Failed to read %s, skipping platform version check.\n", plist_path.c_str());
+ return true;
+ }
+
+ NSError* error = nil;
+ NSDictionary* info_dict =
+ [NSPropertyListSerialization propertyListWithData:data
+ options:NSPropertyListImmutable
+ format:nil
+ error:&error];
+ if (error)
+ {
+ fprintf(log_fp, "Failed to parse %s, skipping platform version check.\n", plist_path.c_str());
+ return true;
+ }
+ NSString* min_version_str = info_dict[@"LSMinimumSystemVersion"];
+ if (!min_version_str)
+ {
+ fprintf(log_fp, "LSMinimumSystemVersion key missing, skipping platform version check.\n");
+ return true;
+ }
+
+ NSArray* components = [min_version_str componentsSeparatedByString:@"."];
+ NSOperatingSystemVersion next_version{
+ [components[0] integerValue], [components[1] integerValue], [components[2] integerValue]};
+
+ fprintf(log_fp, "Platform version check: next_version=%ld.%ld.%ld\n",
+ (long)next_version.majorVersion, (long)next_version.minorVersion,
+ (long)next_version.patchVersion);
+
+ if (![[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:next_version])
+ {
+ UI::Error("Please update macOS in order to update Dolphin.");
+ return false;
+ }
return true;
}
diff --git a/Source/Core/UpdaterCommon/Platform.h b/Source/Core/UpdaterCommon/Platform.h
index 9489ba9947..546ceff98b 100644
--- a/Source/Core/UpdaterCommon/Platform.h
+++ b/Source/Core/UpdaterCommon/Platform.h
@@ -10,91 +10,10 @@
#include "Common/CommonTypes.h"
#include "Common/StringUtil.h"
-namespace Platform
-{
-struct BuildVersion
-{
- u32 major{};
- u32 minor{};
- u32 build{};
- auto operator<=>(BuildVersion const& rhs) const = default;
- static std::optional<BuildVersion> from_string(const std::string& str)
- {
- auto components = SplitString(str, '.');
- // Allow variable number of components (truncating after "build"), but not
- // empty.
- if (components.size() == 0)
- return {};
- BuildVersion version;
- if (!TryParse(components[0], &version.major, 10))
- return {};
- if (components.size() > 1 && !TryParse(components[1], &version.minor, 10))
- return {};
- if (components.size() > 2 && !TryParse(components[2], &version.build, 10))
- return {};
- return version;
- }
-};
-
-enum class VersionCheckStatus
-{
- NothingToDo,
- UpdateOptional,
- UpdateRequired,
-};
-
-struct VersionCheckResult
-{
- VersionCheckStatus status{VersionCheckStatus::NothingToDo};
- std::optional<BuildVersion> current_version{};
- std::optional<BuildVersion> target_version{};
-};
+#include "UpdaterCommon/UpdaterCommon.h"
-class BuildInfo
+namespace Platform
{
- using Map = std::map<std::string, std::string>;
-
-public:
- BuildInfo() = default;
- BuildInfo(const std::string& content);
-
- std::optional<std::string> GetString(const std::string& name) const
- {
- auto it = map.find(name);
- if (it == map.end() || it->second.size() == 0)
- return {};
- return it->second;
- }
-
- std::optional<BuildVersion> GetVersion(const std::string& name) const
- {
- auto str = GetString(name);
- if (!str.has_value())
- return {};
- return BuildVersion::from_string(str.value());
- }
-
-private:
- void Parse(const std::string& content)
- {
- std::stringstream content_stream(content);
- std::string line;
- while (std::getline(content_stream, line))
- {
- if (line.starts_with("//"))
- continue;
- const size_t equals_index = line.find('=');
- if (equals_index == line.npos)
- continue;
- auto key = line.substr(0, equals_index);
- auto key_it = map.find(key);
- if (key_it == map.end())
- continue;
- key_it->second = line.substr(equals_index + 1);
- }
- }
- Map map;
-};
-
-bool VersionCheck(const BuildInfo& this_build_info, const BuildInfo& next_build_info);
+bool VersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
+ const std::string& install_base_path, const std::string& temp_dir, FILE* log_fp);
} // namespace Platform
diff --git a/Source/Core/UpdaterCommon/UpdaterCommon.cpp b/Source/Core/UpdaterCommon/UpdaterCommon.cpp
index e307b0610e..6fdb9630a0 100644
--- a/Source/Core/UpdaterCommon/UpdaterCommon.cpp
+++ b/Source/Core/UpdaterCommon/UpdaterCommon.cpp
@@ -34,51 +34,14 @@
// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process
-namespace
-{
// Where to log updater output.
-FILE* log_fp = stderr;
+static FILE* log_fp = stderr;
// Public key used to verify update manifests.
const std::array<u8, 32> UPDATE_PUB_KEY = {
0x2a, 0xb3, 0xd1, 0xdc, 0x6e, 0xf5, 0x07, 0xf6, 0xa0, 0x6c, 0x7c, 0x54, 0xdf, 0x54, 0xf4, 0x42,
0x80, 0xa6, 0x28, 0x8b, 0x6d, 0x70, 0x14, 0xb5, 0x4c, 0x34, 0x95, 0x20, 0x4d, 0xd4, 0xd3, 0x5d};
-struct Manifest
-{
- using Filename = std::string;
- using Hash = std::array<u8, 16>;
- std::map<Filename, Hash> entries;
-};
-
-// Represent the operations to be performed by the updater.
-struct TodoList
-{
- struct DownloadOp
- {
- Manifest::Filename filename;
- Manifest::Hash hash{};
- };
- std::vector<DownloadOp> to_download;
-
- struct UpdateOp
- {
- Manifest::Filename filename;
- std::optional<Manifest::Hash> old_hash;
- Manifest::Hash new_hash{};
- };
- std::vector<UpdateOp> to_update;
-
- struct DeleteOp
- {
- Manifest::Filename filename;
- Manifest::Hash old_hash{};
- };
- std::vector<DeleteOp> to_delete;
-
- void Log() const;
-};
-
bool ProgressCallback(double total, double now, double, double)
{
UI::SetCurrentProgress(static_cast<int>(now), static_cast<int>(total));
@@ -289,35 +252,7 @@ bool PlatformVersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
const std::string& install_base_path, const std::string& temp_dir)
{
UI::SetDescription("Checking platform...");
-
- const auto op_it = std::find_if(to_update.cbegin(), to_update.cend(),
- [&](const auto& op) { return op.filename == "build_info.txt"; });
- if (op_it == to_update.cend())
- return true;
-
- const auto op = *op_it;
- std::string build_info_path =
- temp_dir + DIR_SEP + HexEncode(op.new_hash.data(), op.new_hash.size());
- std::string build_info_content;
- if (!File::ReadFileToString(build_info_path, build_info_content) ||
- op.new_hash != ComputeHash(build_info_content))
- {
- fprintf(log_fp, "Failed to read %s\n.", build_info_path.c_str());
- return false;
- }
- auto next_build_info = Platform::BuildInfo(build_info_content);
-
- build_info_path = install_base_path + DIR_SEP + "build_info.txt";
- auto this_build_info = Platform::BuildInfo();
- if (File::ReadFileToString(build_info_path, build_info_content))
- {
- if (op.old_hash != ComputeHash(build_info_content))
- fprintf(log_fp, "Using modified existing BuildInfo %s.\n", build_info_path.c_str());
- this_build_info = Platform::BuildInfo(build_info_content);
- }
-
- // The existing BuildInfo may have been modified. Be careful not to overly trust its contents!
- return Platform::VersionCheck(this_build_info, next_build_info);
+ return Platform::VersionCheck(to_update, install_base_path, temp_dir, log_fp);
}
TodoList ComputeActionsToDo(Manifest this_manifest, Manifest next_manifest)
@@ -732,7 +667,6 @@ std::optional<Options> ParseCommandLine(std::vector<std::string>& args)
return opts;
}
-}; // namespace
bool RunUpdater(std::vector<std::string> args)
{
diff --git a/Source/Core/UpdaterCommon/UpdaterCommon.h b/Source/Core/UpdaterCommon/UpdaterCommon.h
index 178ceffe28..679a339f90 100644
--- a/Source/Core/UpdaterCommon/UpdaterCommon.h
+++ b/Source/Core/UpdaterCommon/UpdaterCommon.h
@@ -14,4 +14,41 @@
// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process
+struct Manifest
+{
+ using Filename = std::string;
+ using Hash = std::array<u8, 16>;
+ std::map<Filename, Hash> entries;
+};
+
+// Represent the operations to be performed by the updater.
+struct TodoList
+{
+ struct DownloadOp
+ {
+ Manifest::Filename filename;
+ Manifest::Hash hash{};
+ };
+ std::vector<DownloadOp> to_download;
+
+ struct UpdateOp
+ {
+ Manifest::Filename filename;
+ std::optional<Manifest::Hash> old_hash;
+ Manifest::Hash new_hash{};
+ };
+ std::vector<UpdateOp> to_update;
+
+ struct DeleteOp
+ {
+ Manifest::Filename filename;
+ Manifest::Hash old_hash{};
+ };
+ std::vector<DeleteOp> to_delete;
+
+ void Log() const;
+};
+
+std::string HexEncode(const u8* buffer, size_t size);
+Manifest::Hash ComputeHash(const std::string& contents);
bool RunUpdater(std::vector<std::string> args);
diff --git a/Source/Core/WinUpdater/Platform.cpp b/Source/Core/WinUpdater/Platform.cpp
index 8c79479560..565d53c413 100644
--- a/Source/Core/WinUpdater/Platform.cpp
+++ b/Source/Core/WinUpdater/Platform.cpp
@@ -4,6 +4,7 @@
#include <map>
#include <optional>
+#include "Common/CommonPaths.h"
#include "Common/FileUtil.h"
#include "Common/HttpRequest.h"
#include "Common/IOFile.h"
@@ -12,17 +13,106 @@
#include "UpdaterCommon/Platform.h"
#include "UpdaterCommon/UI.h"
+#include "UpdaterCommon/UpdaterCommon.h"
namespace Platform
{
-BuildInfo::BuildInfo(const std::string& content)
+struct BuildVersion
{
- map = {{"OSMinimumVersionWin10", ""},
- {"OSMinimumVersionWin11", ""},
- {"VCToolsVersion", ""},
- {"VCToolsUpdateURL", ""}};
- Parse(content);
-}
+ u32 major{};
+ u32 minor{};
+ u32 build{};
+ auto operator<=>(BuildVersion const& rhs) const = default;
+ static std::optional<BuildVersion> from_string(const std::string& str)
+ {
+ auto components = SplitString(str, '.');
+ // Allow variable number of components (truncating after "build"), but not
+ // empty.
+ if (components.size() == 0)
+ return {};
+ BuildVersion version;
+ if (!TryParse(components[0], &version.major, 10))
+ return {};
+ if (components.size() > 1 && !TryParse(components[1], &version.minor, 10))
+ return {};
+ if (components.size() > 2 && !TryParse(components[2], &version.build, 10))
+ return {};
+ return version;
+ }
+};
+
+enum class VersionCheckStatus
+{
+ NothingToDo,
+ UpdateOptional,
+ UpdateRequired,
+};
+
+struct VersionCheckResult
+{
+ VersionCheckStatus status{VersionCheckStatus::NothingToDo};
+ std::optional<BuildVersion> current_version{};
+ std::optional<BuildVersion> target_version{};
+};
+
+class BuildInfo
+{
+ using Map = std::map<std::string, std::string>;
+
+public:
+ BuildInfo() = default;
+ BuildInfo(const std::string& content)
+ {
+ map = {{"OSMinimumVersionWin10", ""},
+ {"OSMinimumVersionWin11", ""},
+ {"VCToolsVersion", ""},
+ {"VCToolsUpdateURL", ""}};
+ Parse(content);
+ }
+
+ std::optional<std::string> GetString(const std::string& name) const
+ {
+ auto it = map.find(name);
+ if (it == map.end() || it->second.size() == 0)
+ return {};
+ return it->second;
+ }
+
+ std::optional<BuildVersion> GetVersion(const std::string& name) const
+ {
+ auto str = GetString(name);
+ if (!str.has_value())
+ return {};
+ return BuildVersion::from_string(str.value());
+ }
+
+private:
+ void Parse(const std::string& content)
+ {
+ std::stringstream content_stream(content);
+ std::string line;
+ while (std::getline(content_stream, line))
+ {
+ if (line.starts_with("//"))
+ continue;
+ const size_t equals_index = line.find('=');
+ if (equals_index == line.npos)
+ continue;
+ auto key = line.substr(0, equals_index);
+ auto key_it = map.find(key);
+ if (key_it == map.end())
+ continue;
+ key_it->second = line.substr(equals_index + 1);
+ }
+ }
+ Map map;
+};
+
+struct BuildInfos
+{
+ BuildInfo current;
+ BuildInfo next;
+};
// This default value should be kept in sync with the value of VCToolsUpdateURL in
// build_info.txt.in
@@ -63,14 +153,13 @@ static std::optional<BuildVersion> GetInstalledVCRuntimeVersion()
return version;
}
-static VersionCheckResult VCRuntimeVersionCheck(const BuildInfo& this_build_info,
- const BuildInfo& next_build_info)
+static VersionCheckResult VCRuntimeVersionCheck(const BuildInfos& build_infos)
{
VersionCheckResult result;
result.current_version = GetInstalledVCRuntimeVersion();
- result.target_version = next_build_info.GetVersion("VCToolsVersion");
+ result.target_version = build_infos.next.GetVersion("VCToolsVersion");
- auto existing_version = this_build_info.GetVersion("VCToolsVersion");
+ auto existing_version = build_infos.current.GetVersion("VCToolsVersion");
if (!result.target_version.has_value())
result.status = VersionCheckStatus::UpdateOptional;
@@ -155,10 +244,45 @@ static VersionCheckResult OSVersionCheck(const BuildInfo& build_info)
return result;
}
-bool VersionCheck(const BuildInfo& this_build_info, const BuildInfo& next_build_info)
+std::optional<BuildInfos> InitBuildInfos(const std::vector<TodoList::UpdateOp>& to_update,
+ const std::string& install_base_path,
+ const std::string& temp_dir, FILE* log_fp)
{
+ const auto op_it = std::find_if(to_update.cbegin(), to_update.cend(),
+ [&](const auto& op) { return op.filename == "build_info.txt"; });
+ if (op_it == to_update.cend())
+ return {};
+
+ const auto op = *op_it;
+ std::string build_info_path =
+ temp_dir + DIR_SEP + HexEncode(op.new_hash.data(), op.new_hash.size());
+ std::string build_info_content;
+ if (!File::ReadFileToString(build_info_path, build_info_content) ||
+ op.new_hash != ComputeHash(build_info_content))
+ {
+ fprintf(log_fp, "Failed to read %s\n.", build_info_path.c_str());
+ return {};
+ }
+ BuildInfos build_infos;
+ build_infos.next = Platform::BuildInfo(build_info_content);
+
+ build_info_path = install_base_path + DIR_SEP + "build_info.txt";
+ build_infos.current = Platform::BuildInfo();
+ if (File::ReadFileToString(build_info_path, build_info_content))
+ {
+ if (op.old_hash != ComputeHash(build_info_content))
+ fprintf(log_fp, "Using modified existing BuildInfo %s.\n", build_info_path.c_str());
+ build_infos.current = Platform::BuildInfo(build_info_content);
+ }
+ return build_infos;
+}
+
+bool CheckBuildInfo(const BuildInfos& build_infos)
+{
+ // The existing BuildInfo may have been modified. Be careful not to overly trust its contents!
+
// If the binary requires more recent OS, inform the user.
- auto os_check = OSVersionCheck(next_build_info);
+ auto os_check = OSVersionCheck(build_infos.next);
if (os_check.status == VersionCheckStatus::UpdateRequired)
{
UI::Error("Please update Windows in order to update Dolphin.");
@@ -167,13 +291,13 @@ bool VersionCheck(const BuildInfo& this_build_info, const BuildInfo& next_build_
// Check if application being launched needs more recent version of VC Redist. If so, download
// latest updater and execute it.
- auto vc_check = VCRuntimeVersionCheck(this_build_info, next_build_info);
+ auto vc_check = VCRuntimeVersionCheck(build_infos);
if (vc_check.status != VersionCheckStatus::NothingToDo)
{
// Don't bother checking status of the install itself, just check if we actually see the new
// version.
- VCRuntimeUpdate(next_build_info);
- vc_check = VCRuntimeVersionCheck(this_build_info, next_build_info);
+ VCRuntimeUpdate(build_infos.next);
+ vc_check = VCRuntimeVersionCheck(build_infos);
if (vc_check.status == VersionCheckStatus::UpdateRequired)
{
// The update is required and the install failed for some reason.
@@ -184,4 +308,17 @@ bool VersionCheck(const BuildInfo& this_build_info, const BuildInfo& next_build_
return true;
}
+
+bool VersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
+ const std::string& install_base_path, const std::string& temp_dir, FILE* log_fp)
+{
+ auto build_infos = InitBuildInfos(to_update, install_base_path, temp_dir, log_fp);
+ // If there's no build info, it means the check should be skipped.
+ if (!build_infos.has_value())
+ {
+ return true;
+ }
+ return CheckBuildInfo(build_infos.value());
+}
+
} // namespace Platform