blob: 28081c2ad48a943fa6f61f7e29c5c5f654c494d7 [file] [log] [blame]
// Copyright 2016 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 "imageloader_impl.h"
#include <sys/statvfs.h>
#include <sys/vfs.h>
#include </usr/include/linux/magic.h>
#include <memory>
#include <string>
#include <base/containers/adapters.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/important_file_writer.h>
#include <base/json/json_string_value_serializer.h>
#include <base/logging.h>
#include <base/values.h>
#include <base/version.h>
#include <chromeos/dbus/service_constants.h>
#include "component.h"
namespace imageloader {
namespace {
using imageloader::kBadResult;
// The name of the file containing the latest component version.
constexpr char kLatestVersionFile[] = "latest-version";
// The maximum size of the latest-version file.
constexpr int kMaximumLatestVersionSize = 4096;
// |mount_base_path| is the subfolder where all components are mounted.
// For example "/mnt/imageloader."
base::FilePath GetMountPoint(const base::FilePath& mount_base_path,
const std::string& component_name,
const std::string& component_version) {
return mount_base_path.Append(component_name).Append(component_version);
}
bool AssertComponentDirPerms(const base::FilePath& path) {
int mode;
if (!GetPosixFilePermissions(path, &mode)) return false;
return mode == kComponentDirPerms;
}
} // namespace {}
bool ImageLoaderImpl::LoadComponent(const std::string& name,
const std::string& mount_point_str,
HelperProcess* process) {
base::FilePath component_path;
if (!GetPathToCurrentComponentVersion(name, &component_path)) {
return false;
}
std::unique_ptr<Component> component =
Component::Create(component_path, config_.keys);
if (!component) {
LOG(ERROR) << "Failed to initialize component: " << name;
return false;
}
base::FilePath mount_point(mount_point_str);
return component->Mount(process, mount_point);
}
std::string ImageLoaderImpl::LoadComponent(const std::string& name,
HelperProcess* process) {
base::FilePath component_path;
if (!GetPathToCurrentComponentVersion(name, &component_path)) {
return kBadResult;
}
return LoadComponentAtPath(name, component_path, process);
}
std::string ImageLoaderImpl::LoadComponentAtPath(
const std::string& name, const base::FilePath& component_path,
HelperProcess* process) {
std::unique_ptr<Component> component =
Component::Create(component_path, config_.keys);
if (!component) {
LOG(ERROR) << "Failed to initialize component: " << name;
return kBadResult;
}
base::FilePath mount_point(
GetMountPoint(config_.mount_path, name, component->manifest().version));
return component->Mount(process, mount_point) ? mount_point.value()
: kBadResult;
}
bool ImageLoaderImpl::RemoveComponent(const std::string& name) {
base::FilePath component_root(GetComponentRoot(name));
base::FilePath component_path;
if (!GetPathToCurrentComponentVersion(name, &component_path)) {
LOG(ERROR) << "Failed to get current component version: " << name;
return false;
}
return RemoveComponentAtPath(name, component_root, component_path);
}
bool ImageLoaderImpl::RemoveComponentAtPath(
const std::string& name,
const base::FilePath& component_root,
const base::FilePath& component_path) {
// Check if component is removable.
std::unique_ptr<Component> component =
Component::Create(component_path, config_.keys);
if (!component) {
LOG(ERROR) << "Failed to initialize component: " << name;
return false;
}
if (!component->manifest().is_removable) {
LOG(ERROR) << "Component is not removable";
return false;
}
// Remove the component (all versions) and latest-version file.
if (!base::DeleteFile(component_root, /*recursive=*/true)) {
LOG(ERROR) << "Failed to delete component.";
return false;
}
return true;
}
bool ImageLoaderImpl::RegisterComponent(
const std::string& name, const std::string& version,
const std::string& component_folder_abs_path) {
base::FilePath components_dir(config_.storage_dir);
// If the directory is writable by others, do not trust the components.
if (!AssertComponentDirPerms(components_dir)) return false;
std::string old_version_hint;
base::FilePath version_hint_path(GetLatestVersionFilePath(name));
bool have_old_version = base::PathExists(version_hint_path);
if (have_old_version) {
if (!base::ReadFileToStringWithMaxSize(version_hint_path, &old_version_hint,
kMaximumLatestVersionSize)) {
return false;
}
// Check for version rollback.
base::Version current_version(old_version_hint);
base::Version new_version(version);
if (!new_version.IsValid()) {
return false;
}
if (current_version.IsValid() && new_version <= current_version) {
LOG(ERROR) << "Version [" << new_version << "] is not newer than ["
<< current_version << "] for component [" << name
<< "] and cannot be registered.";
return false;
}
}
// Check if this specific component already exists in the filesystem.
base::FilePath component_root(GetComponentRoot(name));
if (!base::PathExists(component_root)) {
if (mkdir(component_root.value().c_str(), kComponentDirPerms) != 0) {
PLOG(ERROR) << "Could not create component specific directory.";
return false;
}
}
std::unique_ptr<Component> component =
Component::Create(base::FilePath(component_folder_abs_path),
config_.keys);
if (!component) return false;
// Check that the reported version matches the component manifest version.
if (component->manifest().version != version) {
LOG(ERROR) << "Version in signed manifest does not match the reported "
"component version.";
return false;
}
// Take ownership of the component and verify it.
base::FilePath version_path(GetVersionPath(name, version));
// If |version_path| exists but was not the active version, ImageLoader
// probably crashed previously and could not cleanup.
if (base::PathExists(version_path)) {
base::DeleteFile(version_path, /*recursive=*/true);
}
if (mkdir(version_path.value().c_str(), kComponentDirPerms) != 0) {
PLOG(ERROR) << "Could not create directory for new component version.";
return false;
}
if (!component->CopyTo(version_path)) {
base::DeleteFile(version_path, /*recursive=*/true);
return false;
}
if (!base::ImportantFileWriter::WriteFileAtomically(version_hint_path,
version)) {
base::DeleteFile(version_path, /*recursive=*/true);
LOG(ERROR) << "Failed to update current version hint file.";
return false;
}
// Now delete the old component version, if there was one.
if (have_old_version) {
base::DeleteFile(GetVersionPath(name, old_version_hint),
/*recursive=*/true);
}
return true;
}
std::string ImageLoaderImpl::GetComponentVersion(const std::string& name) {
base::FilePath component_path;
if (!GetPathToCurrentComponentVersion(name, &component_path)) {
return kBadResult;
}
std::unique_ptr<Component> component =
Component::Create(component_path, config_.keys);
if (!component) return kBadResult;
return component->manifest().version;
}
bool ImageLoaderImpl::GetComponentMetadata(
const std::string& name,
std::map<std::string, std::string>* out_metadata) {
base::FilePath component_path;
if (!GetPathToCurrentComponentVersion(name, &component_path)) {
return false;
}
std::unique_ptr<Component> component =
Component::Create(component_path, config_.keys);
if (!component)
return false;
*out_metadata = component->manifest().metadata;
return true;
}
base::FilePath ImageLoaderImpl::GetLatestVersionFilePath(
const std::string& component_name) {
return config_.storage_dir.Append(component_name).Append(kLatestVersionFile);
}
base::FilePath ImageLoaderImpl::GetVersionPath(
const std::string& component_name, const std::string& version) {
return config_.storage_dir.Append(component_name).Append(version);
}
base::FilePath ImageLoaderImpl::GetComponentRoot(
const std::string& component_name) {
return config_.storage_dir.Append(component_name);
}
bool ImageLoaderImpl::GetPathToCurrentComponentVersion(
const std::string& component_name, base::FilePath* result) {
base::FilePath component_root(GetComponentRoot(component_name));
base::FilePath latest_version_path = GetLatestVersionFilePath(component_name);
// Check that the version file exists, otherwise the logging when
// ReadFileToString fails confuses the crash reporting. If the file doesn't
// exist, the component most likely isn't installed.
if (!base::PathExists(latest_version_path)) {
LOG(INFO) << "The latest-version file does not exist. Component "
<< component_name << " is probably not installed.";
return false;
}
std::string latest_version;
if (!base::ReadFileToStringWithMaxSize(latest_version_path, &latest_version,
kMaximumLatestVersionSize)) {
LOG(ERROR) << "Failed to read latest-version file.";
return false;
}
*result = component_root.Append(latest_version);
return true;
}
} // namespace imageloader