| // 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_reader.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/values.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_dict, |
| std::map<std::string, std::string>* out_metadata) { |
| DCHECK(out_metadata); |
| |
| if (!metadata_dict.is_dict()) { |
| return false; |
| } |
| |
| for (const auto& item : metadata_dict.DictItems()) { |
| if (!item.second.is_string()) { |
| LOG(ERROR) << "Key \"" << item.first << "\" did not map to string value"; |
| return false; |
| } |
| |
| (*out_metadata)[item.first] = item.second.GetString(); |
| } |
| |
| 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. |
| auto manifest_value = base::JSONReader::ReadAndReturnValueWithError( |
| manifest_raw, base::JSON_PARSE_RFC); |
| if (!manifest_value.value) { |
| LOG(ERROR) << "Could not parse the manifest file as JSON. Error: " |
| << manifest_value.error_message; |
| return false; |
| } |
| |
| if (!manifest_value.value->is_dict()) { |
| LOG(ERROR) << "Manifest file is not dictionary."; |
| return false; |
| } |
| base::Value manifest_dict = std::move(*manifest_value.value); |
| |
| // This will have to be changed if the manifest version is bumped. |
| base::Optional<int> manifest_version = |
| manifest_dict.FindIntKey(kManifestVersionField); |
| if (!manifest_version.has_value()) { |
| LOG(ERROR) << "Could not parse manifest version field from manifest."; |
| return false; |
| } |
| if (manifest_version != kCurrentManifestVersion) { |
| LOG(ERROR) << "Unsupported version of the manifest."; |
| return false; |
| } |
| manifest_version_ = *manifest_version; |
| |
| const std::string* image_hash_str = |
| manifest_dict.FindStringKey(kImageHashField); |
| if (!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; |
| } |
| |
| const std::string* table_hash_str = |
| manifest_dict.FindStringKey(kTableHashField); |
| if (table_hash_str == nullptr) { |
| 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; |
| } |
| |
| const std::string* version = manifest_dict.FindStringKey(kVersionField); |
| if (!version) { |
| LOG(ERROR) << "Could not parse component version from manifest."; |
| return false; |
| } |
| version_ = *version; |
| |
| // The fs_type field is optional, and squashfs by default. |
| const std::string* fs_type = manifest_dict.FindStringKey(kFSType); |
| if (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; |
| } |
| } else { |
| fs_type_ = FileSystem::kSquashFS; |
| } |
| |
| base::Optional<bool> is_removable = |
| manifest_dict.FindBoolKey(kIsRemovableField); |
| // If |is-removable| field does not exist, by default it is false. |
| is_removable_ = is_removable.value_or(false); |
| |
| base::Optional<bool> preload_allowed = |
| manifest_dict.FindBoolKey(kPreloadAllowed); |
| // If |preaload-allowed| field does not exist, by default it is false. |
| preload_allowed_ = preload_allowed.value_or(false); |
| |
| base::Optional<bool> mount_file_required = |
| manifest_dict.FindBoolKey(kMountFileRequired); |
| // If 'mount-file-required' field does not exist, by default it is false. |
| mount_file_required_ = mount_file_required.value_or(false); |
| |
| // All of these fields are optional. |
| const std::string* id = manifest_dict.FindStringKey(kId); |
| if (id) |
| id_ = *id; |
| const std::string* package = manifest_dict.FindStringKey(kPackage); |
| if (package) |
| package_ = *package; |
| const std::string* name = manifest_dict.FindStringKey(kName); |
| if (name) |
| name_ = *name; |
| const std::string* image_type = manifest_dict.FindStringKey(kImageType); |
| if (image_type) |
| image_type_ = *image_type; |
| const std::string* used_by = manifest_dict.FindStringKey(kUsedBy); |
| if (used_by) |
| used_by_ = *used_by; |
| const std::string* description = manifest_dict.FindStringKey(kDescription); |
| if (description) |
| description_ = *description; |
| |
| const std::string* preallocated_size_str = |
| manifest_dict.FindStringKey(kPreallocatedSize); |
| if (preallocated_size_str) { |
| if (!base::StringToInt64(*preallocated_size_str, &preallocated_size_)) { |
| LOG(ERROR) << "Manifest pre-allocated-size was malformed: " |
| << *preallocated_size_str; |
| return false; |
| } |
| } |
| |
| const std::string* size_str = manifest_dict.FindStringKey(kSize); |
| if (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 = manifest_dict.FindKey(kMetadataField); |
| if (metadata) { |
| if (!ParseMetadata(*metadata, &metadata_)) { |
| LOG(ERROR) << "Manifest metadata was malformed"; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace imageloader |