| // 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 <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/logging.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; |
| } |
| |
| Component component(component_path); |
| if (!component.Init(config_.key)) { |
| 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; |
| } |
| |
| Component component(component_path); |
| if (!component.Init(config_.key)) { |
| 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::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; |
| } |
| } |
| |
| base::FilePath component_path(component_folder_abs_path); |
| Component component(component_path); |
| if (!component.Init(config_.key)) 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; |
| } |
| |
| Component component(component_path); |
| if (!component.Init(config_.key)) return kBadResult; |
| |
| return component.manifest().version; |
| } |
| |
| 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)); |
| // Read the latest version file. |
| std::string latest_version; |
| if (!base::ReadFileToStringWithMaxSize( |
| GetLatestVersionFilePath(component_name), &latest_version, |
| kMaximumLatestVersionSize)) { |
| LOG(ERROR) << "Failed to read latest-version file."; |
| return false; |
| } |
| |
| *result = component_root.Append(latest_version); |
| return true; |
| } |
| |
| } // namespace imageloader |