Meta: Add support for verifying downloaded files with their SHA-256 hash

This commit is contained in:
Timothy Flynn 2024-05-23 12:04:02 -04:00 committed by Tim Flynn
commit ec63f442f9
Notes: sideshowbarker 2024-07-18 00:34:07 +09:00
3 changed files with 50 additions and 10 deletions

View file

@ -200,6 +200,12 @@ function(invoke_generator name generator primary_source header implementation)
endfunction() endfunction()
function(download_file_multisource urls path) function(download_file_multisource urls path)
cmake_parse_arguments(DOWNLOAD "" "SHA256" "" ${ARGN})
if (NOT "${DOWNLOAD_SHA256}" STREQUAL "")
set(DOWNLOAD_SHA256 EXPECTED_HASH "SHA256=${DOWNLOAD_SHA256}")
endif()
if (NOT EXISTS "${path}") if (NOT EXISTS "${path}")
if (NOT ENABLE_NETWORK_DOWNLOADS) if (NOT ENABLE_NETWORK_DOWNLOADS)
message(FATAL_ERROR "${path} does not exist, and unable to download it") message(FATAL_ERROR "${path} does not exist, and unable to download it")
@ -209,7 +215,7 @@ function(download_file_multisource urls path)
foreach(url ${urls}) foreach(url ${urls})
message(STATUS "Downloading file ${file} from ${url}") message(STATUS "Downloading file ${file} from ${url}")
file(DOWNLOAD "${url}" "${path}" INACTIVITY_TIMEOUT 10 STATUS download_result) file(DOWNLOAD "${url}" "${path}" INACTIVITY_TIMEOUT 10 STATUS download_result ${DOWNLOAD_SHA256})
list(GET download_result 0 status_code) list(GET download_result 0 status_code)
list(GET download_result 1 error_message) list(GET download_result 1 error_message)
@ -228,8 +234,10 @@ function(download_file_multisource urls path)
endfunction() endfunction()
function(download_file url path) function(download_file url path)
cmake_parse_arguments(DOWNLOAD "" "SHA256" "" ${ARGN})
# If the timestamp doesn't match exactly, the Web Archive should redirect to the closest archived file automatically. # If the timestamp doesn't match exactly, the Web Archive should redirect to the closest archived file automatically.
download_file_multisource("${url};https://web.archive.org/web/99991231235959/${url}" "${path}") download_file_multisource("${url};https://web.archive.org/web/99991231235959/${url}" "${path}" SHA256 "${DOWNLOAD_SHA256}")
endfunction() endfunction()
function(extract_path dest_dir zip_path source_path dest_path) function(extract_path dest_dir zip_path source_path dest_path)

View file

@ -18,6 +18,8 @@
# cache [String] # cache [String]
# Directory to clear on version mismatch # Directory to clear on version mismatch
# #
# sha256 [String]
# Expected SHA-256 hash of the downloaded file
# #
# Example use: # Example use:
# #
@ -66,6 +68,12 @@ template("download_file") {
rebase_path(invoker.cache, root_build_dir), rebase_path(invoker.cache, root_build_dir),
] ]
} }
if (defined(invoker.sha256)) {
args += [
"-s",
invoker.sha256,
]
}
forward_variables_from(invoker, forward_variables_from(invoker,
[ [

View file

@ -8,6 +8,7 @@ It's intended to be used for files that are cached between runs.
""" """
import argparse import argparse
import hashlib
import os import os
import pathlib import pathlib
import shutil import shutil
@ -16,6 +17,20 @@ import tempfile
import urllib.request import urllib.request
def compute_sha256(path):
sha256 = hashlib.sha256()
with open(path, 'rb') as file:
while True:
data = file.read(256 << 10)
if not data:
break
sha256.update(data)
return sha256.hexdigest()
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
epilog=__doc__, epilog=__doc__,
@ -29,6 +44,8 @@ def main():
help='filesystem location to cache version') help='filesystem location to cache version')
parser.add_argument('-c', "--cache-path", required=False, parser.add_argument('-c', "--cache-path", required=False,
help='path for cached files to clear on version mismatch') help='path for cached files to clear on version mismatch')
parser.add_argument('-s', "--sha256", required=False,
help='expected SHA-256 hash of the downloaded file')
args = parser.parse_args() args = parser.parse_args()
version_from_file = '' version_from_file = ''
@ -41,23 +58,30 @@ def main():
return 0 return 0
# Fresh build or version mismatch, delete old cache # Fresh build or version mismatch, delete old cache
if (args.cache_path): if args.cache_path:
cache_path = pathlib.Path(args.cache_path) cache_path = pathlib.Path(args.cache_path)
shutil.rmtree(cache_path, ignore_errors=True) shutil.rmtree(cache_path, ignore_errors=True)
cache_path.mkdir(parents=True) cache_path.mkdir(parents=True)
print(f"Downloading version {args.version} of {args.output}...", end='') output_file = pathlib.Path(args.output)
print(f"Downloading file {output_file} from {args.url}")
with urllib.request.urlopen(args.url) as f: with urllib.request.urlopen(args.url) as f:
try: try:
with tempfile.NamedTemporaryFile(delete=False, with tempfile.NamedTemporaryFile(delete=False, dir=output_file.parent) as out:
dir=pathlib.Path(args.output).parent) as out:
out.write(f.read()) out.write(f.read())
os.rename(out.name, args.output) os.rename(out.name, output_file)
except IOError: except IOError:
os.unlink(out.name) os.unlink(out.name)
print("done") if args.sha256:
actual_sha256 = compute_sha256(output_file)
if args.sha256 != actual_sha256:
print(f"SHA-256 mismatch for downloaded file {output_file}")
print(f"Expected: {args.sha256}")
print(f"Actual: {actual_sha256}")
return 1
with open(version_file, 'w') as f: with open(version_file, 'w') as f:
f.write(args.version) f.write(args.version)