blob: 38c74093f5b0b5771a7f57544b402f6d147c3911 [file] [log] [blame]
// Copyright (c) 2012 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 "installer/chromeos_legacy.h"
#include <stdio.h>
#include <unistd.h>
#include <string>
#include <vector>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include "installer/inst_util.h"
using std::string;
using std::vector;
bool UpdateLegacyKernel(const InstallConfig& install_config) {
const base::FilePath root_mount(install_config.root.mount());
const base::FilePath boot_mount(install_config.boot.mount());
const base::FilePath kernel_from = root_mount.Append("boot/vmlinuz");
const base::FilePath kernel_to =
boot_mount.Append("syslinux").Append("vmlinuz." + install_config.slot);
return base::CopyFile(kernel_from, kernel_to);
}
string ExplandVerityArguments(const string& kernel_config,
const string& root_uuid) {
string kernel_config_dm = ExtractKernelArg(kernel_config, "dm");
// The verity config from the kernel contains short hand symbols for
// partition names that we have to expand to specific UUIDs.
// %U+1 -> XXX-YYY-ZZZ
ReplaceAll(&kernel_config_dm, "%U+1", root_uuid);
// PARTUUID=%U/PARTNROFF=1 -> PARTUUID=XXX-YYY-ZZZ
ReplaceAll(&kernel_config_dm, "%U/PARTNROFF=1", root_uuid);
return kernel_config_dm;
}
bool RunLegacyPostInstall(const InstallConfig& install_config) {
const base::FilePath root_mount(install_config.root.mount());
const base::FilePath root_syslinux = root_mount.Append("boot/syslinux");
const base::FilePath boot_mount(install_config.boot.mount());
const base::FilePath boot_syslinux = boot_mount.Append("syslinux");
LOG(INFO) << "Running LegacyPostInstall.";
if (RunCommand({"cp", "-nR", root_syslinux.value(), boot_mount.value()}) !=
0) {
return false;
}
if (!UpdateLegacyKernel(install_config))
return false;
string kernel_config = DumpKernelConfig(install_config.kernel.device());
string kernel_config_root = ExtractKernelArg(kernel_config, "root");
// Prepare the new default.cfg
string verity_enabled =
(IsReadonly(kernel_config_root) ? "chromeos-vhd" : "chromeos-hd");
string default_syslinux_cfg = base::StringPrintf(
"DEFAULT %s.%s\n", verity_enabled.c_str(), install_config.slot.c_str());
const base::FilePath syslinux_cfg = boot_syslinux.Append("default.cfg");
if (!base::WriteFile(syslinux_cfg, default_syslinux_cfg))
return false;
// Prepare the new root.A/B.cfg
const base::FilePath old_root_cfg_file =
root_syslinux.Append("root." + install_config.slot + ".cfg");
const base::FilePath new_root_cfg_file =
boot_syslinux.Append(old_root_cfg_file.BaseName());
// Copy over the unmodified version for this release...
if (!base::CopyFile(old_root_cfg_file, new_root_cfg_file))
return false;
// Insert the proper root device for non-verity boots
const string root_opt = "PARTUUID=" + install_config.root.uuid();
if (!ReplaceInFile("HDROOT" + install_config.slot, root_opt,
new_root_cfg_file))
return false;
string kernel_config_dm =
ExplandVerityArguments(kernel_config, install_config.root.uuid());
if (kernel_config_dm.empty()) {
LOG(ERROR) << "Failed to extract Verity arguments.";
return false;
}
// Insert the proper verity options for verity boots
if (!ReplaceInFile("DMTABLE" + install_config.slot, kernel_config_dm,
new_root_cfg_file))
return false;
return true;
}
// Copy a file from the root partition to the boot partition.
bool CopyBootFile(const InstallConfig& install_config,
const std::string& src,
const std::string& dst) {
bool result = true;
const base::FilePath root_mount(install_config.root.mount());
const base::FilePath boot_mount(install_config.boot.mount());
const base::FilePath src_path = root_mount.Append(src);
const base::FilePath dst_path = boot_mount.Append(dst);
// If the source file file exists, copy it into place, else do nothing.
if (base::PathExists(src_path)) {
LOG(INFO) << "Copying " << src_path << " to " << dst_path;
result = base::CopyFile(src_path, dst_path);
} else {
LOG(INFO) << "Not present to install: " << src_path;
}
return result;
}
bool RunLegacyUBootPostInstall(const InstallConfig& install_config) {
bool result = true;
LOG(INFO) << "Running LegacyUBootPostInstall.";
result &= CopyBootFile(install_config,
"boot/boot-" + install_config.slot + ".scr.uimg",
"u-boot/boot.scr.uimg");
result &= CopyBootFile(
install_config, "boot/uEnv." + install_config.slot + ".txt", "uEnv.txt");
result &= CopyBootFile(install_config, "boot/MLO", "MLO");
result &= CopyBootFile(install_config, "boot/u-boot.img", "u-boot.img");
return result;
}
bool UpdateEfiBootloaders(const InstallConfig& install_config) {
bool result = true;
const base::FilePath src_dir =
base::FilePath(install_config.root.mount()).Append("boot/efi/boot");
const base::FilePath dest_dir =
base::FilePath(install_config.boot.mount()).Append("efi/boot");
base::FileEnumerator file_enum(src_dir, false, base::FileEnumerator::FILES,
"*.efi");
for (auto src = file_enum.Next(); !src.empty(); src = file_enum.Next()) {
const base::FilePath dest = dest_dir.Append(src.BaseName());
if (!base::CopyFile(src, dest))
result = false;
}
return result;
}
bool RunEfiPostInstall(const InstallConfig& install_config) {
LOG(INFO) << "Running EfiPostInstall.";
// Update the kernel we are about to use.
if (!UpdateLegacyKernel(install_config))
return false;
if (!UpdateEfiBootloaders(install_config))
return false;
// Of the form: PARTUUID=XXX-YYY-ZZZ
string kernel_config = DumpKernelConfig(install_config.kernel.device());
string root_uuid = install_config.root.uuid();
string kernel_config_dm = ExplandVerityArguments(kernel_config, root_uuid);
base::FilePath grub_path =
base::FilePath(install_config.boot.mount()).Append("efi/boot/grub.cfg");
// Read in the grub.cfg to be updated.
string grub_src;
if (!base::ReadFileToString(grub_path, &grub_src)) {
PLOG(ERROR) << "Unable to read grub template file: " << grub_path.value();
return false;
}
string output;
if (!EfiGrubUpdate(grub_src, install_config.slot, root_uuid, kernel_config_dm,
&output)) {
return false;
}
// Write out the new grub.cfg.
if (!base::WriteFile(grub_path, output)) {
PLOG(ERROR) << "Unable to write boot menu file: " << grub_path;
return false;
}
// We finished.
return true;
}
bool EfiGrubUpdate(const string& input,
const string& slot,
const string& root_uuid,
const string& verity_args,
string* output) {
// Split the file contents into lines.
vector<string> file_lines = base::SplitString(
input, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
// Search pattern for lines are related to our slot.
string kernel_pattern = "/syslinux/vmlinuz." + slot;
for (vector<string>::iterator line = file_lines.begin();
line < file_lines.end(); line++) {
if (line->find(kernel_pattern) != string::npos) {
if (ExtractKernelArg(*line, "dm").empty()) {
// If it's an unverified boot line, just set the root partition to boot.
if (!SetKernelArg("root", "PARTUUID=" + root_uuid, &(*line))) {
LOG(ERROR) << "Unable to update unverified root flag in " << *line;
return false;
}
} else {
if (!SetKernelArg("dm", verity_args, &(*line))) {
LOG(INFO) << "Unable to update verified dm flag.";
return false;
}
}
}
}
// Join the lines back into file contents.
*output = base::JoinString(file_lines, "\n");
// Other EFI post-install actions, if any, go here.
return true;
}