blob: 82bd90382d41b0f6496a0c16dc4ebf9c635a325c [file] [log] [blame]
// Copyright 2021 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 <string>
#include <vector>
#include <base/files/file.h>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
#include <brillo/file_utils.h>
#include <brillo/process/process.h>
#include <brillo/userdb_utils.h>
#include "tpm2-simulator/constants.h"
#include "tpm2-simulator/tpm_nvchip_utils.h"
namespace {
constexpr char kNVChipPath[] = "NVChip";
constexpr size_t kNVChipSize = 1024 * 1024; // 1MB.
constexpr char kWoringDirectory[] = ".";
// The old NVChip size that need to migrate to new format.
constexpr size_t kOldNVChipSize = 16384;
struct NVChipMigrateData {
std::string chip_data;
};
bool Tune2Fs(const base::FilePath& file, const std::vector<std::string>& opts) {
brillo::ProcessImpl tune_process;
tune_process.AddArg("/sbin/tune2fs");
for (const auto& arg : opts)
tune_process.AddArg(arg);
tune_process.AddArg(file.value());
// Close unused file descriptors in child process.
tune_process.SetCloseUnusedFileDescriptors(true);
// Avoid polluting the parent process' stdout.
tune_process.RedirectOutput("/dev/null");
int rc = tune_process.Run();
if (rc != 0) {
LOG(ERROR) << "Can't tune ext4: " << file.value() << ", error: " << rc;
return false;
}
return true;
}
bool FormatExt4(const base::FilePath& file) {
brillo::ProcessImpl format_process;
format_process.AddArg("/sbin/mkfs.ext4");
format_process.AddArg(file.value());
// No need to emit output.
format_process.AddArg("-q");
// Close unused file descriptors in child process.
format_process.SetCloseUnusedFileDescriptors(true);
// Avoid polluting the parent process' stdout.
format_process.RedirectOutput("/dev/null");
int rc = format_process.Run();
if (rc != 0) {
LOG(ERROR) << "Can't format '" << file.value()
<< "' as ext4, exit status: " << rc;
return false;
}
// Tune the formatted filesystem:
// -c 0: Disable max mount count checking.
// -i 0: Disable filesystem checking.
return Tune2Fs(file, {"-c", "0", "-i", "0"});
}
bool MountLoopbackFile(const base::FilePath& file,
const base::FilePath& mount_point) {
brillo::ProcessImpl mount_process;
mount_process.AddArg("/bin/mount");
mount_process.AddArg("-o");
mount_process.AddArg("loop");
mount_process.AddArg(file.value());
mount_process.AddArg(mount_point.value());
// Close unused file descriptors in child process.
mount_process.SetCloseUnusedFileDescriptors(true);
// Avoid polluting the parent process' stdout.
mount_process.RedirectOutput("/dev/null");
int rc = mount_process.Run();
if (rc != 0) {
LOG(ERROR) << "Can't mount '" << file.value() << "' to '"
<< mount_point.value() << "', exit status: " << rc;
return false;
}
return true;
}
bool ChownDirectoryContents(const base::FilePath& dir, uid_t uid, gid_t gid) {
base::FileEnumerator ent_enum(dir, false, base::FileEnumerator::FILES);
for (base::FilePath path = ent_enum.Next(); !path.empty();
path = ent_enum.Next()) {
if (HANDLE_EINTR(chown(path.value().c_str(), uid, gid)) < 0) {
PLOG(ERROR) << "Failed to chown " << path.value();
return false;
}
}
return true;
}
bool PrepareMigrateNVChip(const base::FilePath& chip_path,
int64_t chip_size,
NVChipMigrateData* migrate_data) {
if (chip_size == kOldNVChipSize) {
if (!base::ReadFileToString(chip_path, &migrate_data->chip_data)) {
LOG(ERROR) << "Failed to read the NVChip.";
return false;
}
if (migrate_data->chip_data.size() != kOldNVChipSize) {
LOG(ERROR) << "Unknown NVChip size after read.";
return false;
}
if (!brillo::WriteStringToFile(chip_path, std::string(kNVChipSize, '\0'))) {
LOG(ERROR) << "Failed to create the NVChip.";
return false;
}
if (!FormatExt4(chip_path)) {
LOG(ERROR) << "Failed to format the NVChip to ext4.";
return false;
}
return true;
} else {
LOG(ERROR) << "Unknown NVChip size.";
return false;
}
}
bool MigrateNVChip(const base::FilePath& chip_path,
const base::FilePath& mount_point,
const NVChipMigrateData& migrate_data) {
base::FilePath new_chip_path = mount_point.Append(kNVChipPath);
if (!brillo::WriteStringToFile(new_chip_path, migrate_data.chip_data)) {
LOG(ERROR) << "Failed to create the NVChip.";
return false;
}
return true;
}
} // namespace
namespace tpm2_simulator {
bool MountAndEnterNVChip() {
base::FilePath chip_path(kNVChipPath);
base::FilePath mount_point(kNVChipMountPoint);
if (!base::PathExists(chip_path)) {
if (!brillo::WriteStringToFile(chip_path, std::string(kNVChipSize, '\0'))) {
LOG(ERROR) << "Failed to create the NVChip.";
return false;
}
if (!FormatExt4(chip_path)) {
LOG(ERROR) << "Failed to format the NVChip to ext4.";
return false;
}
}
int64_t chip_size = 0;
if (!base::GetFileSize(chip_path, &chip_size)) {
LOG(ERROR) << "Failed to get NVChip size.";
return false;
}
bool migrate_nvchip = chip_size != kNVChipSize;
NVChipMigrateData migrate_data;
if (migrate_nvchip) {
if (!PrepareMigrateNVChip(chip_path, chip_size, &migrate_data)) {
LOG(ERROR) << "Failed to prepare migrate NVChip.";
return false;
}
}
if (!base::PathExists(mount_point)) {
if (!base::CreateDirectory(mount_point)) {
LOG(ERROR) << "Failed to create the NVChip mount point.";
return false;
}
}
uid_t uid;
gid_t gid;
if (!brillo::userdb::GetUserInfo(kSimulatorUser, &uid, &gid)) {
LOG(ERROR) << "Failed to lookup the user name.";
return false;
}
if (HANDLE_EINTR(chown(chip_path.value().c_str(), uid, gid)) < 0) {
PLOG(ERROR) << "Failed to chown the NVChip.";
return false;
}
if (HANDLE_EINTR(chown(mount_point.value().c_str(), uid, gid)) < 0) {
PLOG(ERROR) << "Failed to chown the NVChip mount point.";
return false;
}
if (HANDLE_EINTR(unshare(CLONE_NEWNS)) < 0) {
PLOG(ERROR) << "Failed to unshare.";
return false;
}
if (!MountLoopbackFile(chip_path, mount_point)) {
LOG(ERROR) << "Failed to mount the NVChip.";
return false;
}
if (HANDLE_EINTR(chown(mount_point.value().c_str(), uid, gid)) < 0) {
PLOG(ERROR) << "Failed to chown the NVChip mount point after mount.";
return false;
}
if (migrate_nvchip) {
if (!MigrateNVChip(chip_path, mount_point, migrate_data)) {
LOG(ERROR) << "Failed to migrate NVChip.";
return false;
}
}
if (HANDLE_EINTR(chdir(mount_point.value().c_str())) < 0) {
PLOG(ERROR) << "Failed to enter the mount point.";
return false;
}
return true;
}
bool CorrectWorkingDirectoryFilesOwner() {
uid_t uid;
gid_t gid;
if (!brillo::userdb::GetUserInfo(kSimulatorUser, &uid, &gid)) {
LOG(ERROR) << "Failed to lookup the user name.";
return false;
}
if (!ChownDirectoryContents(base::FilePath(kWoringDirectory), uid, gid)) {
LOG(ERROR)
<< "Failed to chown the NVChip mount point contents after mount.";
return false;
}
return true;
}
} // namespace tpm2_simulator