Sfoglia il codice sorgente

Begin work on manifest updater

Billy Barrow 1 mese fa
parent
commit
a728038873
3 ha cambiato i file con 132 aggiunte e 4 eliminazioni
  1. 15 0
      src/lib/Applicator.vala
  2. 10 4
      src/lib/Manifest.vala
  3. 107 0
      src/lib/ManifestUpdater.vala

+ 15 - 0
src/lib/Applicator.vala

@@ -63,6 +63,21 @@ namespace Binman {
             foreach (var file in need_applied) {
                 apply_file(file);
             }
+
+            // Update manifest pointers
+            foreach (var manifest in manifests) {
+                var dir = manifest.file.get_parent();
+                var current_syml = Path.build_filename(dir.get_path(), "current");
+                var previous_syml = Path.build_filename(dir.get_path(), "previous");
+                
+                // Read "current" value
+                var buffer = new char[uint16.MAX];
+                buffer.length = (int)Posix.readlink(current_syml, buffer);
+                
+                // Set "current" and "previous" values
+                posix_to_error(Posix.symlink((string)buffer, previous_syml));
+                posix_to_error(Posix.symlink(Posix.realpath(manifest.file.get_path()), current_syml));
+            }
         }
 
         public void apply_file(FileDetails file) throws Error {

+ 10 - 4
src/lib/Manifest.vala

@@ -4,14 +4,15 @@ using InvercargillJson;
 namespace Binman {
 
     public errordomain ManifestError {
-        KEY_MISMATCH
+        KEY_MISMATCH,
+        INVALID_SIGNATURE
     }
 
     public class ManifestFile {
         public File file { get; private set; }
         
         public ManifestHeader read_header() throws Error {
-            return new JsonlInputStream(file.read())
+            return new JsonlInputStream(get_stream())
                 .as_property_groups()
                 .try_map_with<ManifestHeader>(ManifestHeader.get_mapper())
                 .first()
@@ -19,7 +20,7 @@ namespace Binman {
         }
 
         public Attempts<ManifestEntry> get_entries() throws Error {
-            return new JsonlInputStream(file.read())
+            return new JsonlInputStream(get_stream())
                 .as_property_groups()
                 .skip(1)
                 .until(o => o.has("signature"))
@@ -28,9 +29,14 @@ namespace Binman {
 
         //  public bool verify_signature() throws Error {
         //      var stream = new DataInputStream(file.read());
-        //      var checksum
+        //      var checksum = 
         //  }
 
+        private InputStream get_stream() throws Error {
+            var decompressor = new ZlibDecompressor(ZlibCompressorFormat.GZIP);
+            return new ConverterInputStream(file.read(), decompressor);
+        }
+
         public ManifestFile(File file) {
             this.file = file;
         }

+ 107 - 0
src/lib/ManifestUpdater.vala

@@ -0,0 +1,107 @@
+using Invercargill;
+using InvercargillJson;
+
+namespace Binman {
+
+    public class Updater {
+
+        public void update(Enumerable<CompositionComponent>? components = null) throws Error{
+            var comps = components ?? get_composition().to_series();
+
+            foreach (var component in comps) {
+                var folder = File.new_build_filename("/var/binman/", component.name);
+                if(!folder.query_exists()) {
+                    folder.make_directory_with_parents();
+                }
+
+                var min_serial = 0;
+                var latest_syml = File.new_build_filename(folder.get_path(), "latest");
+                if(latest_syml.query_exists()) {
+                    min_serial = new ManifestFile(latest_syml).read_header().serial;
+                }
+
+
+
+            }
+
+        }
+
+
+        private File download_manifest(CompositionComponent component, int64 min_serial = 0) throws IOError {
+
+            var downloaded = false;
+            foreach (var uri in component.uris) {
+                DataInputStream stream = null;
+                try {
+                    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>());
+                    
+                    if(header.serial < min_serial){
+                        continue;
+                    }
+
+                    if(header.key != component.key) {
+                        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);
+                    }
+
+                    // 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, )
+
+                }
+                catch(Error e) {
+                    warning(@"Download \"$(descriptor.remote_path)\" from remote \"$remote\" failed: $(e.message)");
+                }
+                finally {
+                    if(stream != null) {
+                        stream.close();
+                    }
+                }
+            }
+
+            if(!downloaded) {
+                throw new IOError.FAILED(@"Could not download manifest for component \"$(component.name)\", all remotes were tried.");
+            }
+        }
+
+
+    }
+
+
+}