blob: 85485646ea9d8e1c21ffe52c3fc5d5fd47366a98 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "private_computing/private_computing_adaptor.h"
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#include <base/check.h>
#include <base/files/file_util.h>
#include <base/location.h>
#include <base/logging.h>
#include <base/strings/strcat.h>
#include <brillo/dbus/dbus_object.h>
#include <brillo/errors/error.h>
#include <dbus/private_computing/dbus-constants.h>
#include <google/protobuf/message_lite.h>
#include "private_computing/proto_bindings/private_computing_service.pb.h"
namespace private_computing {
namespace {
// Path to preserved file used to read the last ping dates after powerwash.
const char kPrivateComputingLastActiveDatesReadPath[] =
"/mnt/stateful_partition/unencrypted/preserve/last_active_dates";
// Path to file storing the last ping dates as serialized object
// private_computing_service::SaveStatusRequest.
const char kPrivateComputingLastActiveDatesWritePath[] =
"/var/lib/private_computing/last_active_dates";
// Serializes |proto| to a vector of bytes.
std::vector<uint8_t> SerializeProto(
const google::protobuf::MessageLite& proto) {
std::vector<uint8_t> proto_blob(proto.ByteSizeLong());
CHECK(proto.SerializeToArray(proto_blob.data(), proto_blob.size()));
return proto_blob;
}
// Parses a proto from an array of bytes |proto_blob|. Returns
// error message or empty string if no error.
std::string ParseProto(const base::Location& from_here,
google::protobuf::MessageLite* proto,
const std::vector<uint8_t>& proto_blob) {
if (!proto->ParseFromArray(proto_blob.data(), proto_blob.size())) {
const std::string error_message = "Failed to parse proto message.";
LOG(ERROR) << from_here.ToString() << " " << error_message;
return error_message;
}
return "";
}
std::optional<std::string> ReadActiveStatusFile(
const base::FilePath& filePath) {
base::File file(filePath, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid()) {
return std::nullopt;
}
base::File::Info file_info;
if (!file.GetInfo(&file_info) || file_info.size <= 0) {
return std::nullopt;
}
std::string result;
result.resize(file_info.size);
const int read_result =
file.Read(/* offset */ 0, result.data(), file_info.size);
if (read_result != file_info.size) {
return std::nullopt;
}
return result;
}
} // namespace
PrivateComputingAdaptor::PrivateComputingAdaptor(
std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object)
: org::chromium::PrivateComputingAdaptor(this),
dbus_object_(std::move(dbus_object)),
var_lib_dir_(kPrivateComputingLastActiveDatesWritePath),
preserve_dir_(kPrivateComputingLastActiveDatesReadPath) {}
void PrivateComputingAdaptor::RegisterAsync(
brillo::dbus_utils::AsyncEventSequencer::CompletionAction
completion_callback) {
RegisterWithDBusObject(dbus_object_.get());
dbus_object_->RegisterAsync(std::move(completion_callback));
}
std::vector<uint8_t> PrivateComputingAdaptor::SaveLastPingDatesStatus(
const std::vector<uint8_t>& request_blob) {
LOG(INFO) << "Save the last ping dates to file.";
SaveStatusRequest request;
std::string error_message = ParseProto(FROM_HERE, &request, request_blob);
SaveStatusResponse response;
if (!error_message.empty()) {
response.set_error_message(error_message);
return SerializeProto(response);
}
// Write serialized request to |kPrivateComputingLastActiveDatesWritePath|
base::File file(var_lib_dir_,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
if (!file.IsValid()) {
response.set_error_message(
"Failed to retrieve last_active_dates file descriptor.");
return SerializeProto(response);
}
std::string request_str = request.SerializeAsString();
const int write_count =
file.Write(0, request_str.c_str(), request_str.size());
if (write_count < 0 ||
static_cast<size_t>(write_count) < request_str.size()) {
response.set_error_message(
base::StrCat({"Failed to write data file ",
kPrivateComputingLastActiveDatesWritePath,
" write count=", std::to_string(write_count)}));
return SerializeProto(response);
}
LOG(INFO)
<< "Successfully saved last ping date to /var/lib/private_computing.";
return SerializeProto(response);
}
std::vector<uint8_t> PrivateComputingAdaptor::GetLastPingDatesStatus() {
// First try to read from `/var/lib/private_computing` folder.
// Because this file will be updated after every successfully ping.
// If this file exists, then it will be the latest value.
std::optional<std::string> result_string = ReadActiveStatusFile(var_lib_dir_);
GetStatusResponse response;
if (!result_string) {
LOG(ERROR) << "PSM: Cannot read from "
<< kPrivateComputingLastActiveDatesWritePath;
// Next try to read from preserved file if reading from var/lib failed,
// which means the device was powerwashed/recovery/new device. If the
// device was powerwashed, then the preserved file should be existed.
// If the device was recovery/new device, reading from preserved file
// should be failed as well.
result_string = ReadActiveStatusFile(preserve_dir_);
if (!result_string) {
LOG(ERROR) << "PSM: Cannot read from "
<< kPrivateComputingLastActiveDatesReadPath;
// Cannot read neither from /var/lib or preserved file, then return
// the response with error message.
response.set_error_message(
"PSM: Neither from /var/lib or the preserved file");
return SerializeProto(response);
}
}
LOG(INFO) << "PSM: Successfully read from /var/lib or /preserved file.";
// Successfully read file into |result_string|.
SaveStatusRequest request;
if (!request.ParseFromString(result_string.value())) {
response.set_error_message(
base::StrCat({"Failed to parse result string as a SaveStatusRequest ",
kPrivateComputingLastActiveDatesReadPath}));
return SerializeProto(response);
}
*response.mutable_active_status() = request.active_status();
return SerializeProto(response);
}
} // namespace private_computing