blob: a3f92ad026da73c0a693104a2f17d2f14e8ae460 [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
hscham1574c7e2020-10-13 12:40:01 +09008#include <base/json/json_reader.h>
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -07009#include <base/strings/string_number_conversions.h>
hscham1574c7e2020-10-13 12:40:01 +090010#include <base/values.h>
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070011
Xiaochu Liue61e1d62018-11-12 13:20:09 -080012#include "imageloader/manifest.h"
13
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070014namespace imageloader {
15
16namespace {
17// The current version of the manifest file.
18constexpr int kCurrentManifestVersion = 1;
19// The name of the version field in the manifest.
20constexpr char kManifestVersionField[] = "manifest-version";
21// The name of the component version field in the manifest.
22constexpr char kVersionField[] = "version";
23// The name of the field containing the image hash.
24constexpr char kImageHashField[] = "image-sha256-hash";
25// The name of the bool field indicating whether component is removable.
26constexpr char kIsRemovableField[] = "is-removable";
27// The name of the metadata field.
28constexpr char kMetadataField[] = "metadata";
29// The name of the field containing the table hash.
30constexpr char kTableHashField[] = "table-sha256-hash";
Colin Howesdc15ba22018-12-12 11:02:42 -080031// Optional manifest fields.
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070032constexpr char kFSType[] = "fs-type";
Colin Howesdc15ba22018-12-12 11:02:42 -080033constexpr char kId[] = "id";
Amin Hassani0badef12019-03-18 17:08:02 -070034constexpr char kPackage[] = "package";
Colin Howesdc15ba22018-12-12 11:02:42 -080035constexpr char kName[] = "name";
36constexpr char kImageType[] = "image-type";
37constexpr char kPreallocatedSize[] = "pre-allocated-size";
38constexpr char kSize[] = "size";
Jae Hoon Kim81d2b6d2020-01-06 16:09:32 -080039constexpr char kPreloadAllowed[] = "preload-allowed";
Jae Hoon Kim99ee2732020-06-25 15:59:35 -070040constexpr char kMountFileRequired[] = "mount-file-required";
Amin Hassani192c59a2020-04-13 15:11:04 -070041constexpr char kUsedBy[] = "used-by";
Amin Hassanib7c20d62020-04-23 23:14:53 -070042constexpr char kDescription[] = "description";
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070043
44bool GetSHA256FromString(const std::string& hash_str,
45 std::vector<uint8_t>* bytes) {
46 if (!base::HexStringToBytes(hash_str, bytes))
47 return false;
48 return bytes->size() == 32;
49}
50
51// Ensure the metadata entry is a dictionary mapping strings to strings and
52// parse it into |out_metadata| and return true if so.
hscham1574c7e2020-10-13 12:40:01 +090053bool ParseMetadata(const base::Value& metadata_dict,
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070054 std::map<std::string, std::string>* out_metadata) {
55 DCHECK(out_metadata);
56
hscham1574c7e2020-10-13 12:40:01 +090057 if (!metadata_dict.is_dict()) {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070058 return false;
hscham1574c7e2020-10-13 12:40:01 +090059 }
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070060
hscham1574c7e2020-10-13 12:40:01 +090061 for (const auto& item : metadata_dict.DictItems()) {
62 if (!item.second.is_string()) {
63 LOG(ERROR) << "Key \"" << item.first << "\" did not map to string value";
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070064 return false;
65 }
66
hscham1574c7e2020-10-13 12:40:01 +090067 (*out_metadata)[item.first] = item.second.GetString();
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070068 }
69
70 return true;
71}
72
73} // namespace
74
Ben Chan791bee22018-09-27 12:54:21 -070075Manifest::Manifest()
Xiaochu Liud9d853a2019-01-23 12:54:18 -080076 : manifest_version_(0),
77 fs_type_(FileSystem::kExt4),
Jae Hoon Kim9c012ac2020-02-03 17:37:49 -080078 preallocated_size_(0),
79 size_(0),
Xiaochu Liud9d853a2019-01-23 12:54:18 -080080 is_removable_(false) {}
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070081
82bool Manifest::ParseManifest(const std::string& manifest_raw) {
83 // Now deserialize the manifest json and read out the rest of the component.
hscham1574c7e2020-10-13 12:40:01 +090084 auto manifest_value = base::JSONReader::ReadAndReturnValueWithError(
85 manifest_raw, base::JSON_PARSE_RFC);
86 if (!manifest_value.value) {
87 LOG(ERROR) << "Could not parse the manifest file as JSON. Error "
88 << manifest_value.error_code << ": "
89 << manifest_value.error_message;
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070090 return false;
91 }
92
hscham1574c7e2020-10-13 12:40:01 +090093 if (!manifest_value.value->is_dict()) {
94 LOG(ERROR) << "Manifest file is not dictionary.";
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070095 return false;
96 }
hscham1574c7e2020-10-13 12:40:01 +090097 base::Value manifest_dict = std::move(*manifest_value.value);
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -070098
99 // This will have to be changed if the manifest version is bumped.
hscham1574c7e2020-10-13 12:40:01 +0900100 base::Optional<int> manifest_version =
101 manifest_dict.FindIntKey(kManifestVersionField);
102 if (!manifest_version.has_value()) {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700103 LOG(ERROR) << "Could not parse manifest version field from manifest.";
104 return false;
105 }
hscham1574c7e2020-10-13 12:40:01 +0900106 if (manifest_version != kCurrentManifestVersion) {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700107 LOG(ERROR) << "Unsupported version of the manifest.";
108 return false;
109 }
hscham1574c7e2020-10-13 12:40:01 +0900110 manifest_version_ = *manifest_version;
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700111
hscham1574c7e2020-10-13 12:40:01 +0900112 const std::string* image_hash_str =
113 manifest_dict.FindStringKey(kImageHashField);
114 if (!image_hash_str) {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700115 LOG(ERROR) << "Could not parse image hash from manifest.";
116 return false;
117 }
118
hscham1574c7e2020-10-13 12:40:01 +0900119 if (!GetSHA256FromString(*image_hash_str, &(image_sha256_))) {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700120 LOG(ERROR) << "Could not convert image hash to bytes.";
121 return false;
122 }
123
hscham1574c7e2020-10-13 12:40:01 +0900124 const std::string* table_hash_str =
125 manifest_dict.FindStringKey(kTableHashField);
126 if (table_hash_str == nullptr) {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700127 LOG(ERROR) << "Could not parse table hash from manifest.";
128 return false;
129 }
130
hscham1574c7e2020-10-13 12:40:01 +0900131 if (!GetSHA256FromString(*table_hash_str, &(table_sha256_))) {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700132 LOG(ERROR) << "Could not convert table hash to bytes.";
133 return false;
134 }
135
hscham1574c7e2020-10-13 12:40:01 +0900136 const std::string* version = manifest_dict.FindStringKey(kVersionField);
137 if (!version) {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700138 LOG(ERROR) << "Could not parse component version from manifest.";
139 return false;
140 }
hscham1574c7e2020-10-13 12:40:01 +0900141 version_ = *version;
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700142
143 // The fs_type field is optional, and squashfs by default.
hscham1574c7e2020-10-13 12:40:01 +0900144 const std::string* fs_type = manifest_dict.FindStringKey(kFSType);
145 if (fs_type) {
146 if (*fs_type == "ext4") {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700147 fs_type_ = FileSystem::kExt4;
hscham1574c7e2020-10-13 12:40:01 +0900148 } else if (*fs_type == "squashfs") {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700149 fs_type_ = FileSystem::kSquashFS;
150 } else {
hscham1574c7e2020-10-13 12:40:01 +0900151 LOG(ERROR) << "Unsupported file system type: " << *fs_type;
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700152 return false;
153 }
hscham1574c7e2020-10-13 12:40:01 +0900154 } else {
155 fs_type_ = FileSystem::kSquashFS;
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700156 }
157
hscham1574c7e2020-10-13 12:40:01 +0900158 base::Optional<bool> is_removable =
159 manifest_dict.FindBoolKey(kIsRemovableField);
160 // If |is-removable| field does not exist, by default it is false.
161 is_removable_ = is_removable.value_or(false);
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700162
hscham1574c7e2020-10-13 12:40:01 +0900163 base::Optional<bool> preload_allowed =
164 manifest_dict.FindBoolKey(kPreloadAllowed);
165 // If |preaload-allowed| field does not exist, by default it is false.
166 preload_allowed_ = preload_allowed.value_or(false);
Jae Hoon Kim81d2b6d2020-01-06 16:09:32 -0800167
hscham1574c7e2020-10-13 12:40:01 +0900168 base::Optional<bool> mount_file_required =
169 manifest_dict.FindBoolKey(kMountFileRequired);
170 // If 'mount-file-required' field does not exist, by default it is false.
171 mount_file_required_ = mount_file_required.value_or(false);
Jae Hoon Kim99ee2732020-06-25 15:59:35 -0700172
Colin Howesdc15ba22018-12-12 11:02:42 -0800173 // All of these fields are optional.
hscham1574c7e2020-10-13 12:40:01 +0900174 const std::string* id = manifest_dict.FindStringKey(kId);
175 if (id)
176 id_ = *id;
177 const std::string* package = manifest_dict.FindStringKey(kPackage);
178 if (package)
179 package_ = *package;
180 const std::string* name = manifest_dict.FindStringKey(kName);
181 if (name)
182 name_ = *name;
183 const std::string* image_type = manifest_dict.FindStringKey(kImageType);
184 if (image_type)
185 image_type_ = *image_type;
186 const std::string* used_by = manifest_dict.FindStringKey(kUsedBy);
187 if (used_by)
188 used_by_ = *used_by;
189 const std::string* description = manifest_dict.FindStringKey(kDescription);
190 if (description)
191 description_ = *description;
Jae Hoon Kim9c012ac2020-02-03 17:37:49 -0800192
hscham1574c7e2020-10-13 12:40:01 +0900193 const std::string* preallocated_size_str =
194 manifest_dict.FindStringKey(kPreallocatedSize);
195 if (preallocated_size_str) {
196 if (!base::StringToInt64(*preallocated_size_str, &preallocated_size_)) {
Jae Hoon Kim9c012ac2020-02-03 17:37:49 -0800197 LOG(ERROR) << "Manifest pre-allocated-size was malformed: "
hscham1574c7e2020-10-13 12:40:01 +0900198 << *preallocated_size_str;
Jae Hoon Kim9c012ac2020-02-03 17:37:49 -0800199 return false;
200 }
201 }
202
hscham1574c7e2020-10-13 12:40:01 +0900203 const std::string* size_str = manifest_dict.FindStringKey(kSize);
204 if (size_str) {
205 if (!base::StringToInt64(*size_str, &size_)) {
206 LOG(ERROR) << "Manifest size was malformed: " << *size_str;
Jae Hoon Kim9c012ac2020-02-03 17:37:49 -0800207 return false;
208 }
209 }
Colin Howesdc15ba22018-12-12 11:02:42 -0800210
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700211 // Copy out the metadata, if it's there.
hscham1574c7e2020-10-13 12:40:01 +0900212 const base::Value* metadata = manifest_dict.FindKey(kMetadataField);
213 if (metadata) {
214 if (!ParseMetadata(*metadata, &metadata_)) {
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700215 LOG(ERROR) << "Manifest metadata was malformed";
216 return false;
217 }
218 }
219
220 return true;
221}
222
Xiaochu Liucc7ff8c2018-07-23 11:54:02 -0700223} // namespace imageloader