blob: d2ab09adbdb3c0b73b9a70974fa1ed2e694c9977 [file] [log] [blame]
// 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/check.h>
#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