blob: 5332a799d8ef93ef4fbb32b3fb3ac5cfbd962c82 [file] [log] [blame]
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -07001// Copyright 2018 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -07005#include <memory>
6#include <utility>
7
8#include <base/json/json_string_value_serializer.h>
9#include <base/strings/string_number_conversions.h>
10
Xiaochu Liue61e1d62018-11-12 13:20:09 -080011#include "imageloader/manifest.h"
12
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070013namespace imageloader {
14
15namespace {
16// The current version of the manifest file.
17constexpr int kCurrentManifestVersion = 1;
18// The name of the version field in the manifest.
19constexpr char kManifestVersionField[] = "manifest-version";
20// The name of the component version field in the manifest.
21constexpr char kVersionField[] = "version";
22// The name of the field containing the image hash.
23constexpr char kImageHashField[] = "image-sha256-hash";
24// The name of the bool field indicating whether component is removable.
25constexpr char kIsRemovableField[] = "is-removable";
26// The name of the metadata field.
27constexpr char kMetadataField[] = "metadata";
28// The name of the field containing the table hash.
29constexpr char kTableHashField[] = "table-sha256-hash";
Colin Howesdc15ba22018-12-12 11:02:42 -080030// Optional manifest fields.
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070031constexpr char kFSType[] = "fs-type";
Colin Howesdc15ba22018-12-12 11:02:42 -080032constexpr char kId[] = "id";
Amin Hassani0badef12019-03-18 17:08:02 -070033constexpr char kPackage[] = "package";
Colin Howesdc15ba22018-12-12 11:02:42 -080034constexpr char kName[] = "name";
35constexpr char kImageType[] = "image-type";
36constexpr char kPreallocatedSize[] = "pre-allocated-size";
37constexpr char kSize[] = "size";
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070038
39bool GetSHA256FromString(const std::string& hash_str,
40 std::vector<uint8_t>* bytes) {
41 if (!base::HexStringToBytes(hash_str, bytes))
42 return false;
43 return bytes->size() == 32;
44}
45
46// Ensure the metadata entry is a dictionary mapping strings to strings and
47// parse it into |out_metadata| and return true if so.
48bool ParseMetadata(const base::Value* metadata_element,
49 std::map<std::string, std::string>* out_metadata) {
50 DCHECK(out_metadata);
51
52 const base::DictionaryValue* metadata_dict = nullptr;
53 if (!metadata_element->GetAsDictionary(&metadata_dict))
54 return false;
55
56 base::DictionaryValue::Iterator it(*metadata_dict);
57 for (; !it.IsAtEnd(); it.Advance()) {
58 std::string parsed_value;
59 if (!it.value().GetAsString(&parsed_value)) {
60 LOG(ERROR) << "Key \"" << it.key() << "\" did not map to string value";
61 return false;
62 }
63
64 (*out_metadata)[it.key()] = std::move(parsed_value);
65 }
66
67 return true;
68}
69
70} // namespace
71
Ben Chan791bee22018-09-27 12:54:21 -070072Manifest::Manifest()
Xiaochu Liud9d853a2019-01-23 12:54:18 -080073 : manifest_version_(0),
74 fs_type_(FileSystem::kExt4),
75 preallocated_size_(-1),
76 is_removable_(false) {}
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070077
78bool Manifest::ParseManifest(const std::string& manifest_raw) {
79 // Now deserialize the manifest json and read out the rest of the component.
80 int error_code;
81 std::string error_message;
82 JSONStringValueDeserializer deserializer(manifest_raw);
83 std::unique_ptr<base::Value> value =
84 deserializer.Deserialize(&error_code, &error_message);
85
86 if (!value) {
87 LOG(ERROR) << "Could not deserialize the manifest file. Error "
88 << error_code << ": " << error_message;
89 return false;
90 }
91
92 base::DictionaryValue* manifest_dict = nullptr;
93 if (!value->GetAsDictionary(&manifest_dict)) {
94 LOG(ERROR) << "Could not parse manifest file as JSON.";
95 return false;
96 }
97
98 // This will have to be changed if the manifest version is bumped.
99 int version;
100 if (!manifest_dict->GetInteger(kManifestVersionField, &version)) {
101 LOG(ERROR) << "Could not parse manifest version field from manifest.";
102 return false;
103 }
104 if (version != kCurrentManifestVersion) {
105 LOG(ERROR) << "Unsupported version of the manifest.";
106 return false;
107 }
108 manifest_version_ = version;
109
110 std::string image_hash_str;
111 if (!manifest_dict->GetString(kImageHashField, &image_hash_str)) {
112 LOG(ERROR) << "Could not parse image hash from manifest.";
113 return false;
114 }
115
116 if (!GetSHA256FromString(image_hash_str, &(image_sha256_))) {
117 LOG(ERROR) << "Could not convert image hash to bytes.";
118 return false;
119 }
120
121 std::string table_hash_str;
122 if (!manifest_dict->GetString(kTableHashField, &table_hash_str)) {
123 LOG(ERROR) << "Could not parse table hash from manifest.";
124 return false;
125 }
126
127 if (!GetSHA256FromString(table_hash_str, &(table_sha256_))) {
128 LOG(ERROR) << "Could not convert table hash to bytes.";
129 return false;
130 }
131
132 if (!manifest_dict->GetString(kVersionField, &(version_))) {
133 LOG(ERROR) << "Could not parse component version from manifest.";
134 return false;
135 }
136
137 // The fs_type field is optional, and squashfs by default.
138 fs_type_ = FileSystem::kSquashFS;
139 std::string fs_type;
140 if (manifest_dict->GetString(kFSType, &fs_type)) {
141 if (fs_type == "ext4") {
142 fs_type_ = FileSystem::kExt4;
143 } else if (fs_type == "squashfs") {
144 fs_type_ = FileSystem::kSquashFS;
145 } else {
146 LOG(ERROR) << "Unsupported file system type: " << fs_type;
147 return false;
148 }
149 }
150
Colin Howesdc15ba22018-12-12 11:02:42 -0800151 if (!manifest_dict->GetBoolean(kIsRemovableField, &is_removable_)) {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700152 // If is_removable field does not exist, by default it is false.
153 is_removable_ = false;
154 }
155
Colin Howesdc15ba22018-12-12 11:02:42 -0800156 // All of these fields are optional.
157 manifest_dict->GetString(kId, &id_);
Amin Hassani0badef12019-03-18 17:08:02 -0700158 manifest_dict->GetString(kPackage, &package_);
Colin Howesdc15ba22018-12-12 11:02:42 -0800159 manifest_dict->GetString(kName, &name_);
160 manifest_dict->GetString(kImageType, &image_type_);
161 // TODO(http://crbug.com/904539 comment #11): This can overflow, should get
162 // binary blob and convert to size_t.
163 manifest_dict->GetInteger(kPreallocatedSize, &preallocated_size_);
164 // TODO(http://crbug.com/904539 comment #11): This can overflow, should get
165 // binary blob and convert to size_t.
166 manifest_dict->GetInteger(kSize, &size_);
167
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700168 // Copy out the metadata, if it's there.
169 const base::Value* metadata = nullptr;
170 if (manifest_dict->Get(kMetadataField, &metadata)) {
Colin Howesdc15ba22018-12-12 11:02:42 -0800171 if (!ParseMetadata(metadata, &metadata_)) {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700172 LOG(ERROR) << "Manifest metadata was malformed";
173 return false;
174 }
175 }
176
177 return true;
178}
179
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700180} // namespace imageloader