blob: 7d22b51dd332ac2ec9bfe13e1ce6bbcc301eccf5 [file] [log] [blame] [edit]
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "vm_tools/concierge/service_common.h"
#include <optional>
#include <base/files/file.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include "vm_tools/common/naming.h"
namespace vm_tools::concierge {
std::optional<base::FilePath> GetFilePathFromName(
const VmId& vm_id,
StorageLocation storage_location,
const std::string& extension,
base::FilePath storage_dir) {
if (!IsValidOwnerId(vm_id.owner_id())) {
LOG(ERROR) << "Invalid cryptohome_id specified";
return std::nullopt;
}
// Encode the given disk name to ensure it only has valid characters.
std::string encoded_name = GetEncodedName(vm_id.name());
switch (storage_location) {
case STORAGE_CRYPTOHOME_ROOT: {
storage_dir = storage_dir.Append(kCrosvmDir);
break;
}
case STORAGE_CRYPTOHOME_PLUGINVM: {
storage_dir = storage_dir.Append(kPluginVmDir);
break;
}
default: {
LOG(ERROR) << "Unknown storage location type";
return std::nullopt;
}
}
storage_dir = storage_dir.Append(vm_id.owner_id());
if (!base::DirectoryExists(storage_dir)) {
LOG(ERROR) << "Missing VM storage directory " << storage_dir;
return std::nullopt;
}
if (base::IsLink(storage_dir)) {
LOG(ERROR) << "Invalid symlinked storage directory " << storage_dir;
return std::nullopt;
}
// Group rx permission needed for VM shader cache management by shadercached
if (!base::SetPosixFilePermissions(storage_dir, 0750)) {
LOG(WARNING) << "Failed to set directory permissions for " << storage_dir;
}
return storage_dir.Append(encoded_name).AddExtension(extension);
}
bool GetPluginDirectory(const base::FilePath& prefix,
const std::string& extension,
const std::string& vm_id,
bool create,
base::FilePath* path_out) {
std::string dirname = GetEncodedName(vm_id);
base::FilePath path = prefix.Append(dirname).AddExtension(extension);
if (create && !base::DirectoryExists(path)) {
base::File::Error dir_error;
if (!base::CreateDirectoryAndGetError(path, &dir_error)) {
LOG(ERROR) << "Failed to create plugin directory " << path.value() << ": "
<< base::File::ErrorToString(dir_error);
return false;
}
}
*path_out = path;
return true;
}
bool GetPluginIsoDirectory(const VmId& vm_id,
bool create,
base::FilePath* path_out) {
return GetPluginDirectory(base::FilePath(kCryptohomeRoot)
.Append(kPluginVmDir)
.Append(vm_id.owner_id()),
"iso", vm_id.name(), create, path_out);
}
// Valid owner/cryptohome ID is a hexadecimal string.
bool IsValidOwnerId(const std::string& owner_id) {
if (owner_id.empty())
return false;
return base::ContainsOnlyChars(owner_id, kValidCryptoHomeCharacters);
}
// Currently the only requirement for VM name to be non-empty because we
// encode them as base64 when creating on-disk representations.
bool IsValidVmName(const std::string& vm_name) {
return !vm_name.empty();
}
void SendDbusResponse(dbus::ExportedObject::ResponseSender response_sender,
dbus::MethodCall* method_call,
const google::protobuf::MessageLite& response) {
std::unique_ptr<dbus::Response> dbus_response(
dbus::Response::FromMethodCall(method_call));
dbus::MessageWriter writer(dbus_response.get());
writer.AppendProtoAsArrayOfBytes(response);
std::move(response_sender).Run(std::move(dbus_response));
}
std::optional<PflashMetadata> GetPflashMetadata(const VmId& vm_id,
base::FilePath storage_dir) {
std::optional<base::FilePath> pflash_installation_path_result =
GetFilePathFromName(vm_id, STORAGE_CRYPTOHOME_ROOT, kPflashImageExtension,
storage_dir);
if (!pflash_installation_path_result) {
return std::nullopt;
}
base::FilePath pflash_installation_path =
pflash_installation_path_result.value();
bool is_installed = base::PathExists(pflash_installation_path);
return PflashMetadata{.path = std::move(pflash_installation_path),
.is_installed = is_installed};
}
std::optional<base::FilePath> GetInstalledOrRequestPflashPath(
const VmId& vm_id, const base::FilePath& start_vm_request_pflash_path) {
bool is_pflash_sent_in_request =
base::PathExists(start_vm_request_pflash_path);
std::optional<PflashMetadata> pflash_metadata = GetPflashMetadata(vm_id);
if (!pflash_metadata) {
return std::nullopt;
}
// If a pflash file is installed then don't accept one sent in a start
// request.
if (pflash_metadata->is_installed && is_pflash_sent_in_request) {
return std::nullopt;
}
if (pflash_metadata->is_installed) {
return pflash_metadata->path;
}
// At this point we don't have an installed pflash file, if no pflash file is
// sent in the request then return an empty pflash location.
if (!is_pflash_sent_in_request) {
return base::FilePath();
}
return start_vm_request_pflash_path;
}
} // namespace vm_tools::concierge