Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #11220 from shuffle2/macversion
MacUpdater: check os version
  • Loading branch information
JMC47 committed Oct 30, 2022
2 parents 2f80928 + 089886a commit 969309c
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 186 deletions.
1 change: 0 additions & 1 deletion Source/Core/Common/build_info.txt.in
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions Source/Core/MacUpdater/Info.plist.in
Expand Up @@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>${DOLPHIN_WC_DESCRIBE}</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>${DOLPHIN_VERSION_MAJOR}.${DOLPHIN_VERSION_MINOR}</string>
<key>LSMinimumSystemVersion</key>
<string>10.9</string>
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
<key>NSHumanReadableCopyright</key>
<string>Licensed under GPL version 2 or later (GPLv2+)</string>
<key>NSMainStoryboardFile</key>
Expand Down
63 changes: 50 additions & 13 deletions Source/Core/MacUpdater/MacUI.mm
Expand Up @@ -5,6 +5,7 @@

#include "UpdaterCommon/Platform.h"
#include "UpdaterCommon/UI.h"
#include "UpdaterCommon/UpdaterCommon.h"

#include <Cocoa/Cocoa.h>
#include <unistd.h>
Expand Down Expand Up @@ -138,20 +139,56 @@ void run_on_main(std::function<void()> fnc)
{
}

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;
}
89 changes: 4 additions & 85 deletions Source/Core/UpdaterCommon/Platform.h
Expand Up @@ -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
70 changes: 2 additions & 68 deletions Source/Core/UpdaterCommon/UpdaterCommon.cpp
Expand Up @@ -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));
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -732,7 +667,6 @@ std::optional<Options> ParseCommandLine(std::vector<std::string>& args)

return opts;
}
}; // namespace

bool RunUpdater(std::vector<std::string> args)
{
Expand Down
37 changes: 37 additions & 0 deletions Source/Core/UpdaterCommon/UpdaterCommon.h
Expand Up @@ -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);

0 comments on commit 969309c

Please sign in to comment.