| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <utility> |
| |
| #include <base/json/json_string_value_serializer.h> |
| #include <base/strings/string_number_conversions.h> |
| |
| #include "imageloader/manifest.h" |
| |
| namespace imageloader { |
| |
| namespace { |
| // The current version of the manifest file. |
| constexpr int kCurrentManifestVersion = 1; |
| // The name of the version field in the manifest. |
| constexpr char kManifestVersionField[] = "manifest-version"; |
| // The name of the component version field in the manifest. |
| constexpr char kVersionField[] = "version"; |
| // The name of the field containing the image hash. |
| constexpr char kImageHashField[] = "image-sha256-hash"; |
| // The name of the bool field indicating whether component is removable. |
| constexpr char kIsRemovableField[] = "is-removable"; |
| // The name of the metadata field. |
| constexpr char kMetadataField[] = "metadata"; |
| // The name of the field containing the table hash. |
| constexpr char kTableHashField[] = "table-sha256-hash"; |
| // Optional manifest fields. |
| constexpr char kFSType[] = "fs-type"; |
| constexpr char kId[] = "id"; |
| constexpr char kPackage[] = "package"; |
| constexpr char kName[] = "name"; |
| constexpr char kImageType[] = "image-type"; |
| constexpr char kPreallocatedSize[] = "pre-allocated-size"; |
| constexpr char kSize[] = "size"; |
| constexpr char kPreloadAllowed[] = "preload-allowed"; |
| constexpr char kMountFileRequired[] = "mount-file-required"; |
| constexpr char kUsedBy[] = "used-by"; |
| constexpr char kDescription[] = "description"; |
| |
| bool GetSHA256FromString(const std::string& hash_str, |
| std::vector<uint8_t>* bytes) { |
| if (!base::HexStringToBytes(hash_str, bytes)) |
| return false; |
| return bytes->size() == 32; |
| } |
| |
| // Ensure the metadata entry is a dictionary mapping strings to strings and |
| // parse it into |out_metadata| and return true if so. |
| bool ParseMetadata(const base::Value* metadata_element, |
| std::map<std::string, std::string>* out_metadata) { |
| DCHECK(out_metadata); |
| |
| const base::DictionaryValue* metadata_dict = nullptr; |
| if (!metadata_element->GetAsDictionary(&metadata_dict)) |
| return false; |
| |
| base::DictionaryValue::Iterator it(*metadata_dict); |
| for (; !it.IsAtEnd(); it.Advance()) { |
| std::string parsed_value; |
| if (!it.value().GetAsString(&parsed_value)) { |
| LOG(ERROR) << "Key \"" << it.key() << "\" did not map to string value"; |
| return false; |
| } |
| |
| (*out_metadata)[it.key()] = std::move(parsed_value); |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| Manifest::Manifest() |
| : manifest_version_(0), |
| fs_type_(FileSystem::kExt4), |
| preallocated_size_(0), |
| size_(0), |
| is_removable_(false) {} |
| |
| bool Manifest::ParseManifest(const std::string& manifest_raw) { |
| // Now deserialize the manifest json and read out the rest of the component. |
| int error_code; |
| std::string error_message; |
| JSONStringValueDeserializer deserializer(manifest_raw); |
| std::unique_ptr<base::Value> value = |
| deserializer.Deserialize(&error_code, &error_message); |
| |
| if (!value) { |
| LOG(ERROR) << "Could not deserialize the manifest file. Error " |
| << error_code << ": " << error_message; |
| return false; |
| } |
| |
| base::DictionaryValue* manifest_dict = nullptr; |
| if (!value->GetAsDictionary(&manifest_dict)) { |
| LOG(ERROR) << "Could not parse manifest file as JSON."; |
| return false; |
| } |
| |
| // This will have to be changed if the manifest version is bumped. |
| int version; |
| if (!manifest_dict->GetInteger(kManifestVersionField, &version)) { |
| LOG(ERROR) << "Could not parse manifest version field from manifest."; |
| return false; |
| } |
| if (version != kCurrentManifestVersion) { |
| LOG(ERROR) << "Unsupported version of the manifest."; |
| return false; |
| } |
| manifest_version_ = version; |
| |
| std::string image_hash_str; |
| if (!manifest_dict->GetString(kImageHashField, &image_hash_str)) { |
| LOG(ERROR) << "Could not parse image hash from manifest."; |
| return false; |
| } |
| |
| if (!GetSHA256FromString(image_hash_str, &(image_sha256_))) { |
| LOG(ERROR) << "Could not convert image hash to bytes."; |
| return false; |
| } |
| |
| std::string table_hash_str; |
| if (!manifest_dict->GetString(kTableHashField, &table_hash_str)) { |
| LOG(ERROR) << "Could not parse table hash from manifest."; |
| return false; |
| } |
| |
| if (!GetSHA256FromString(table_hash_str, &(table_sha256_))) { |
| LOG(ERROR) << "Could not convert table hash to bytes."; |
| return false; |
| } |
| |
| if (!manifest_dict->GetString(kVersionField, &(version_))) { |
| LOG(ERROR) << "Could not parse component version from manifest."; |
| return false; |
| } |
| |
| // The fs_type field is optional, and squashfs by default. |
| fs_type_ = FileSystem::kSquashFS; |
| std::string fs_type; |
| if (manifest_dict->GetString(kFSType, &fs_type)) { |
| if (fs_type == "ext4") { |
| fs_type_ = FileSystem::kExt4; |
| } else if (fs_type == "squashfs") { |
| fs_type_ = FileSystem::kSquashFS; |
| } else { |
| LOG(ERROR) << "Unsupported file system type: " << fs_type; |
| return false; |
| } |
| } |
| |
| if (!manifest_dict->GetBoolean(kIsRemovableField, &is_removable_)) { |
| // If |is-removable| field does not exist, by default it is false. |
| is_removable_ = false; |
| } |
| |
| if (!manifest_dict->GetBoolean(kPreloadAllowed, &preload_allowed_)) { |
| // If |preaload-allowed| field does not exist, by default it is false. |
| preload_allowed_ = false; |
| } |
| |
| if (!manifest_dict->GetBoolean(kMountFileRequired, &mount_file_required_)) { |
| // If 'mount-file-required' field does not exist, by default it is false. |
| mount_file_required_ = false; |
| } |
| |
| // All of these fields are optional. |
| manifest_dict->GetString(kId, &id_); |
| manifest_dict->GetString(kPackage, &package_); |
| manifest_dict->GetString(kName, &name_); |
| manifest_dict->GetString(kImageType, &image_type_); |
| manifest_dict->GetString(kUsedBy, &used_by_); |
| manifest_dict->GetString(kDescription, &description_); |
| |
| std::string preallocated_size_str; |
| if (manifest_dict->GetString(kPreallocatedSize, &preallocated_size_str)) { |
| if (!base::StringToInt64(preallocated_size_str, &preallocated_size_)) { |
| LOG(ERROR) << "Manifest pre-allocated-size was malformed: " |
| << preallocated_size_str; |
| return false; |
| } |
| } |
| |
| std::string size_str; |
| if (manifest_dict->GetString(kSize, &size_str)) { |
| if (!base::StringToInt64(size_str, &size_)) { |
| LOG(ERROR) << "Manifest size was malformed: " << size_str; |
| return false; |
| } |
| } |
| |
| // Copy out the metadata, if it's there. |
| const base::Value* metadata = nullptr; |
| if (manifest_dict->Get(kMetadataField, &metadata)) { |
| if (!ParseMetadata(metadata, &metadata_)) { |
| LOG(ERROR) << "Manifest metadata was malformed"; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace imageloader |