|
@@ -20,26 +20,20 @@ namespace Binman {
|
|
|
min_serial = new ManifestFile(latest_syml).read_header().serial;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
+ download_manifest(component, min_serial, null);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
+ private File download_manifest(CompositionComponent component, int64 min_serial = 0, FileProgressCallback? progress = null) throws IOError {
|
|
|
|
|
|
- private File download_manifest(CompositionComponent component, int64 min_serial = 0) throws IOError {
|
|
|
-
|
|
|
- var downloaded = false;
|
|
|
+ File result = null;
|
|
|
foreach (var uri in component.uris) {
|
|
|
- DataInputStream stream = null;
|
|
|
try {
|
|
|
+ // Peek at the header before downloading the whole thing
|
|
|
var remote_file = File.new_for_uri(uri);
|
|
|
- var decompressor = new ZlibDecompressor(ZlibCompressorFormat.GZIP);
|
|
|
- stream = new DataInputStream(new ConverterInputStream(remote_file.read(), decompressor));
|
|
|
-
|
|
|
- // Read header
|
|
|
- var header_line = stream.read_line();
|
|
|
- var header = ManifestHeader.get_mapper().materialise(new JsonElement.from_string(header_line).as<JsonObject>());
|
|
|
+ var remote_manifest = new ManifestFile(remote_file);
|
|
|
+ var header = remote_manifest.read_header();
|
|
|
|
|
|
if(header.serial < min_serial){
|
|
|
continue;
|
|
@@ -49,55 +43,35 @@ namespace Binman {
|
|
|
throw new ManifestError.KEY_MISMATCH("Key in manifest header does not match key in component");
|
|
|
}
|
|
|
|
|
|
- // Download and verify file
|
|
|
- FileIOStream tmp_stream;
|
|
|
- var download_tmp = File.new_tmp(null, out tmp_stream);
|
|
|
- var checksum = new Checksum(ChecksumType.SHA512);
|
|
|
- var compressor = new ZlibCompressor(ZlibCompressorFormat.GZIP, 9);
|
|
|
- var save_stream = new DataOutputStream(new ConverterOutputStream(tmp_stream.output_stream, compressor));
|
|
|
-
|
|
|
- var line = header_line;
|
|
|
- ManifestSignature signature = null;
|
|
|
- while (true) {
|
|
|
- save_stream.put_string(line);
|
|
|
-
|
|
|
- var obj = new JsonElement.from_string(line).as<JsonObject>();
|
|
|
- // Don't include signature object in checksum
|
|
|
- if(obj.has("signature")) {
|
|
|
- signature = ManifestSignature.get_mapper().materialise(obj);
|
|
|
- break;
|
|
|
- }
|
|
|
- checksum.update(line.data, line.data.length);
|
|
|
+ // Download the file
|
|
|
+ var manifest_tmp = File.new_build_filename(Environment.get_tmp_dir(), "binman-manifest-" + Uuid.string_random());
|
|
|
+ remote_file.copy(manifest_tmp, FileCopyFlags.NONE, null, progress);
|
|
|
+
|
|
|
+ // Verify the manifest
|
|
|
+ var local_manifest = new ManifestFile(manifest_tmp);
|
|
|
+ var local_manifest_header = local_manifest.read_header();
|
|
|
+ if(!local_manifest.verify_signature()) {
|
|
|
+ throw new ManifestError.INVALID_SIGNATURE(@"Invalid signature on manifest $(local_manifest_header.name)");
|
|
|
}
|
|
|
|
|
|
- // Calculate checksum
|
|
|
- var checksum_bytes = new uint8[ChecksumType.SHA512.get_length()];
|
|
|
- size_t size = checksum_bytes.length;
|
|
|
- checksum.get_digest(checksum_bytes, ref size);
|
|
|
-
|
|
|
- // Verify signature
|
|
|
- var signed_checksum = Sodium.Asymmetric.Signing.verify(signature.signature.to_array(), component.key.to_array());
|
|
|
- if(!new BinaryData.from_byte_array(signed_checksum).equals(new BinaryData.from_byte_array(checksum_bytes))) {
|
|
|
- throw new ManifestError.INVALID_SIGNATURE("Downloaded manifest has invalid signature");
|
|
|
- }
|
|
|
-
|
|
|
// Install new manifest
|
|
|
- var dest = File.new_build_filename("/var/binman/", component.name, )
|
|
|
+ var dest = File.new_build_filename("/var/binman/", component.name, local_manifest_header.serial);
|
|
|
+ manifest_tmp.move(dest, FileCopyFlags.TARGET_DEFAULT_MODIFIED_TIME | FileCopyFlags.TARGET_DEFAULT_PERMS);
|
|
|
+
|
|
|
+ // Update "latest" symlink
|
|
|
+ Applicator.posix_to_error(Posix.symlink(dest.get_path(), Path.build_filename("/var/binman/", component.name, "latest")));
|
|
|
+ result = dest;
|
|
|
|
|
|
}
|
|
|
catch(Error e) {
|
|
|
- warning(@"Download \"$(descriptor.remote_path)\" from remote \"$remote\" failed: $(e.message)");
|
|
|
- }
|
|
|
- finally {
|
|
|
- if(stream != null) {
|
|
|
- stream.close();
|
|
|
- }
|
|
|
+ warning(@"Download of manifest \"$(component.name)\" from $(uri) failed: $(e.message)");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if(!downloaded) {
|
|
|
+ if(result == null) {
|
|
|
throw new IOError.FAILED(@"Could not download manifest for component \"$(component.name)\", all remotes were tried.");
|
|
|
}
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
|