Expand Up
@@ -34,13 +34,28 @@
// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process
// Where to log updater output.
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 };
// The private key for UPDATE_PUB_KEY_TEST is in Tools/test-updater.py
const std::array<u8, 32 > UPDATE_PUB_KEY_TEST = {
0x0c , 0x5f , 0xdc , 0xd1 , 0x15 , 0x71 , 0xfb , 0x86 , 0x4f , 0x9e , 0x6d , 0xe6 , 0x65 , 0x39 , 0x43 , 0xe1 ,
0x9e , 0xe0 , 0x9b , 0x28 , 0xc9 , 0x1a , 0x60 , 0xb7 , 0x67 , 0x1c , 0xf3 , 0xf6 , 0xca , 0x1b , 0xdd , 0x1a };
// Where to log updater output.
static FILE* log_fp = stderr;
void LogToFile (const char * fmt, ...)
{
va_list args;
va_start (args, fmt);
vfprintf (log_fp, fmt, args);
fflush (log_fp);
va_end (args);
}
bool ProgressCallback (double total, double now, double , double )
{
Expand Down
Expand Up
@@ -120,7 +135,7 @@ std::optional<std::string> GzipInflate(const std::string& data)
if (ret != Z_STREAM_END)
{
fprintf (log_fp, " Could not read the data as gzip: error %d.\n " , ret);
LogToFile ( " Could not read the data as gzip: error %d.\n " , ret);
return {};
}
Expand Down
Expand Up
@@ -148,12 +163,13 @@ bool VerifySignature(const std::string& data, const std::string& b64_signature)
b64_signature.size ()) ||
sig_size != sizeof (signature))
{
fprintf (log_fp, " Invalid base64: %s\n " , b64_signature.c_str ());
LogToFile ( " Invalid base64: %s\n " , b64_signature.c_str ());
return false ;
}
const auto & pub_key = UI::IsTestMode () ? UPDATE_PUB_KEY_TEST : UPDATE_PUB_KEY;
return ed25519_verify (signature, reinterpret_cast <const u8*>(data.data ()), data.size (),
UPDATE_PUB_KEY .data ());
pub_key .data ());
}
void FlushLog ()
Expand All
@@ -166,22 +182,22 @@ void TodoList::Log() const
{
if (to_update.size ())
{
fprintf (log_fp, " Updating:\n " );
LogToFile ( " Updating:\n " );
for (const auto & op : to_update)
{
std::string old_desc =
op.old_hash ? HexEncode (op.old_hash ->data (), op.old_hash ->size ()) : " (new)" ;
fprintf (log_fp, " - %s: %s -> %s\n " , op.filename .c_str (), old_desc.c_str (),
HexEncode (op.new_hash .data (), op.new_hash .size ()).c_str ());
LogToFile ( " - %s: %s -> %s\n " , op.filename .c_str (), old_desc.c_str (),
HexEncode (op.new_hash .data (), op.new_hash .size ()).c_str ());
}
}
if (to_delete.size ())
{
fprintf (log_fp, " Deleting:\n " );
LogToFile ( " Deleting:\n " );
for (const auto & op : to_delete)
{
fprintf (log_fp, " - %s (%s)\n " , op.filename .c_str (),
HexEncode (op.old_hash .data (), op.old_hash .size ()).c_str ());
LogToFile ( " - %s (%s)\n " , op.filename .c_str (),
HexEncode (op.old_hash .data (), op.old_hash .size ()).c_str ());
}
}
}
Expand Down
Expand Up
@@ -215,7 +231,7 @@ bool DownloadContent(const std::vector<TodoList::DownloadOp>& to_download,
content_store_path.insert (2 , " /" );
std::string url = content_base_url + content_store_path;
fprintf (log_fp, " Downloading %s ...\n " , url.c_str ());
LogToFile ( " Downloading %s ...\n " , url.c_str ());
auto resp = req.Get (url);
if (!resp)
Expand All
@@ -234,14 +250,14 @@ bool DownloadContent(const std::vector<TodoList::DownloadOp>& to_download,
Manifest::Hash contents_hash = ComputeHash (decompressed);
if (contents_hash != download.hash )
{
fprintf (log_fp, " Wrong hash on downloaded content %s.\n " , url.c_str ());
LogToFile ( " Wrong hash on downloaded content %s.\n " , url.c_str ());
return false ;
}
const std::string out = temp_path + DIR_SEP + hash_filename;
if (!File::WriteStringToFile (out, decompressed))
{
fprintf (log_fp, " Could not write cache file %s.\n " , out.c_str ());
LogToFile ( " Could not write cache file %s.\n " , out.c_str ());
return false ;
}
}
Expand All
@@ -252,7 +268,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..." );
return Platform::VersionCheck (to_update, install_base_path, temp_dir, log_fp );
return Platform::VersionCheck (to_update, install_base_path, temp_dir);
}
TodoList ComputeActionsToDo (Manifest this_manifest, Manifest next_manifest)
Expand Down
Expand Up
@@ -310,10 +326,10 @@ void CleanUpTempDir(const std::string& temp_dir, const TodoList& todo)
bool BackupFile (const std::string& path)
{
std::string backup_path = path + " .bak" ;
fprintf (log_fp, " Backing up existing %s to .bak.\n " , path.c_str ());
LogToFile ( " Backing up existing %s to .bak.\n " , path.c_str ());
if (!File::Rename (path, backup_path))
{
fprintf (log_fp, " Cound not rename %s to %s for backup.\n " , path.c_str (), backup_path.c_str ());
LogToFile ( " Cound not rename %s to %s for backup.\n " , path.c_str (), backup_path.c_str ());
return false ;
}
return true ;
Expand All
@@ -328,15 +344,15 @@ bool DeleteObsoleteFiles(const std::vector<TodoList::DeleteOp>& to_delete,
if (!File::Exists (path))
{
fprintf (log_fp, " File %s is already missing.\n " , op.filename .c_str ());
LogToFile ( " File %s is already missing.\n " , op.filename .c_str ());
continue ;
}
else
{
std::string contents;
if (!File::ReadFileToString (path, contents))
{
fprintf (log_fp, " Could not read file planned for deletion: %s.\n " , op.filename .c_str ());
LogToFile ( " Could not read file planned for deletion: %s.\n " , op.filename .c_str ());
return false ;
}
Manifest::Hash contents_hash = ComputeHash (contents);
Expand Down
Expand Up
@@ -365,7 +381,7 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
std::string path = install_base_path + DIR_SEP + op.filename ;
if (!File::CreateFullPath (path))
{
fprintf (log_fp, " Could not create directory structure for %s.\n " , op.filename .c_str ());
LogToFile ( " Could not create directory structure for %s.\n " , op.filename .c_str ());
return false ;
}
Expand All
@@ -385,7 +401,7 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
if (S_ISLNK (file_stats.st_mode ))
{
fprintf (log_fp, " %s is symlink, skipping\n " , path.c_str ());
LogToFile ( " %s is symlink, skipping\n " , path.c_str ());
continue ;
}
Expand All
@@ -408,13 +424,13 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
std::string contents;
if (!File::ReadFileToString (path, contents))
{
fprintf (log_fp, " Could not read existing file %s.\n " , op.filename .c_str ());
LogToFile ( " Could not read existing file %s.\n " , op.filename .c_str ());
return false ;
}
Manifest::Hash contents_hash = ComputeHash (contents);
if (contents_hash == op.new_hash )
{
fprintf (log_fp, " File %s was already up to date. Partial update?\n " , op.filename .c_str ());
LogToFile ( " File %s was already up to date. Partial update?\n " , op.filename .c_str ());
continue ;
}
else if (!op.old_hash || contents_hash != *op.old_hash || is_self)
Expand All
@@ -426,8 +442,8 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
// Now we can safely move the new contents to the location.
std::string content_filename = HexEncode (op.new_hash .data (), op.new_hash .size ());
fprintf (log_fp, " Updating file %s from content %s...\n " , op.filename .c_str (),
content_filename.c_str ());
LogToFile ( " Updating file %s from content %s...\n " , op.filename .c_str (),
content_filename.c_str ());
#ifdef __APPLE__
// macOS caches the code signature of Mach-O executables when they're first loaded.
// Unfortunately, there is a quirk in the kernel with how it handles the cache: if the file is
Expand All
@@ -440,8 +456,7 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
const std::string temporary_file = temp_path + DIR_SEP + " temporary_file" ;
if (!File::CopyRegularFile (temp_path + DIR_SEP + content_filename, temporary_file))
{
fprintf (log_fp, " Could not copy %s to %s.\n " , content_filename.c_str (),
temporary_file.c_str ());
LogToFile (" Could not copy %s to %s.\n " , content_filename.c_str (), temporary_file.c_str ());
return false ;
}
Expand All
@@ -450,7 +465,7 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
if (!File::CopyRegularFile (temp_path + DIR_SEP + content_filename, path))
#endif
{
fprintf (log_fp, " Could not update file %s.\n " , op.filename .c_str ());
LogToFile ( " Could not update file %s.\n " , op.filename .c_str ());
return false ;
}
Expand All
@@ -465,32 +480,32 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
bool PerformUpdate (const TodoList& todo, const std::string& install_base_path,
const std::string& content_base_url, const std::string& temp_path)
{
fprintf (log_fp, " Starting download step...\n " );
LogToFile ( " Starting download step...\n " );
if (!DownloadContent (todo.to_download , content_base_url, temp_path))
return false ;
fprintf (log_fp, " Download step completed.\n " );
LogToFile ( " Download step completed.\n " );
fprintf (log_fp, " Starting platform version check step...\n " );
LogToFile ( " Starting platform version check step...\n " );
if (!PlatformVersionCheck (todo.to_update , install_base_path, temp_path))
return false ;
fprintf (log_fp, " Platform version check step completed.\n " );
LogToFile ( " Platform version check step completed.\n " );
fprintf (log_fp, " Starting update step...\n " );
LogToFile ( " Starting update step...\n " );
if (!UpdateFiles (todo.to_update , install_base_path, temp_path))
return false ;
fprintf (log_fp, " Update step completed.\n " );
LogToFile ( " Update step completed.\n " );
fprintf (log_fp, " Starting deletion step...\n " );
LogToFile ( " Starting deletion step...\n " );
if (!DeleteObsoleteFiles (todo.to_delete , install_base_path))
return false ;
fprintf (log_fp, " Deletion step completed.\n " );
LogToFile ( " Deletion step completed.\n " );
return true ;
}
void FatalError (const std::string& message)
{
fprintf (log_fp, " %s\n " , message.c_str ());
LogToFile ( " %s\n " , message.c_str ());
UI::SetVisible (true );
UI::Error (message);
Expand All
@@ -506,30 +521,28 @@ std::optional<Manifest> ParseManifest(const std::string& manifest)
size_t filename_end_pos = manifest.find (' \t ' , pos);
if (filename_end_pos == std::string::npos)
{
fprintf (log_fp, " Manifest entry %zu: could not find filename end.\n " , parsed.entries .size ());
LogToFile ( " Manifest entry %zu: could not find filename end.\n " , parsed.entries .size ());
return {};
}
size_t hash_end_pos = manifest.find (' \n ' , filename_end_pos);
if (hash_end_pos == std::string::npos)
{
fprintf (log_fp, " Manifest entry %zu: could not find hash end.\n " , parsed.entries .size ());
LogToFile ( " Manifest entry %zu: could not find hash end.\n " , parsed.entries .size ());
return {};
}
std::string filename = manifest.substr (pos, filename_end_pos - pos);
std::string hash = manifest.substr (filename_end_pos + 1 , hash_end_pos - filename_end_pos - 1 );
if (hash.size () != 32 )
{
fprintf (log_fp, " Manifest entry %zu: invalid hash: \" %s\" .\n " , parsed.entries .size (),
hash.c_str ());
LogToFile (" Manifest entry %zu: invalid hash: \" %s\" .\n " , parsed.entries .size (), hash.c_str ());
return {};
}
Manifest::Hash decoded_hash;
if (!HexDecode (hash, decoded_hash.data (), decoded_hash.size ()))
{
fprintf (log_fp, " Manifest entry %zu: invalid hash: \" %s\" .\n " , parsed.entries .size (),
hash.c_str ());
LogToFile (" Manifest entry %zu: invalid hash: \" %s\" .\n " , parsed.entries .size (), hash.c_str ());
return {};
}
Expand All
@@ -548,7 +561,7 @@ std::optional<Manifest> FetchAndParseManifest(const std::string& url)
Common::HttpRequest::Response resp = http.Get (url);
if (!resp)
{
fprintf (log_fp, " Manifest download failed.\n " );
LogToFile ( " Manifest download failed.\n " );
return {};
}
Expand All
@@ -562,7 +575,7 @@ std::optional<Manifest> FetchAndParseManifest(const std::string& url)
size_t boundary = decompressed.rfind (" \n\n " );
if (boundary == std::string::npos)
{
fprintf (log_fp, " No signature was found in manifest.\n " );
LogToFile ( " No signature was found in manifest.\n " );
return {};
}
Expand All
@@ -581,7 +594,7 @@ std::optional<Manifest> FetchAndParseManifest(const std::string& url)
}
if (!found_valid_signature)
{
fprintf (log_fp, " Could not verify signature of the manifest.\n " );
LogToFile ( " Could not verify signature of the manifest.\n " );
return {};
}
Expand Down
Expand Up
@@ -692,9 +705,9 @@ bool RunUpdater(std::vector<std::string> args)
atexit (FlushLog);
}
fprintf (log_fp, " Updating from: %s\n " , opts.this_manifest_url .c_str ());
fprintf (log_fp, " Updating to: %s\n " , opts.next_manifest_url .c_str ());
fprintf (log_fp, " Install path: %s\n " , opts.install_base_path .c_str ());
LogToFile ( " Updating from: %s\n " , opts.this_manifest_url .c_str ());
LogToFile ( " Updating to: %s\n " , opts.next_manifest_url .c_str ());
LogToFile ( " Install path: %s\n " , opts.install_base_path .c_str ());
if (!File::IsDirectory (opts.install_base_path ))
{
Expand All
@@ -704,13 +717,13 @@ bool RunUpdater(std::vector<std::string> args)
if (opts.parent_pid )
{
fprintf (log_fp, " Waiting for parent PID %d to complete...\n " , *opts.parent_pid );
LogToFile ( " Waiting for parent PID %d to complete...\n " , *opts.parent_pid );
auto pid = opts.parent_pid .value ();
UI::WaitForPID (static_cast <u32>(pid));
fprintf (log_fp, " Completed! Proceeding with update.\n " );
LogToFile ( " Completed! Proceeding with update.\n " );
}
UI::SetVisible (true );
Expand Down