299 lines
10 KiB
C++
299 lines
10 KiB
C++
|
/*
|
||
|
* Copyright (C) 2018 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#include "libdm/dm_target.h"
|
||
|
|
||
|
#include <inttypes.h>
|
||
|
#include <stdio.h>
|
||
|
#include <sys/types.h>
|
||
|
|
||
|
#include <android-base/logging.h>
|
||
|
#include <android-base/macros.h>
|
||
|
#include <android-base/parseint.h>
|
||
|
#include <android-base/strings.h>
|
||
|
|
||
|
#include <libdm/dm.h>
|
||
|
|
||
|
namespace android {
|
||
|
namespace dm {
|
||
|
|
||
|
std::string DmTarget::Serialize() const {
|
||
|
// Create a string containing a dm_target_spec, parameter data, and an
|
||
|
// explicit null terminator.
|
||
|
std::string data(sizeof(dm_target_spec), '\0');
|
||
|
data += GetParameterString();
|
||
|
data.push_back('\0');
|
||
|
|
||
|
// The kernel expects each target to be 8-byte aligned.
|
||
|
size_t padding = DM_ALIGN(data.size()) - data.size();
|
||
|
for (size_t i = 0; i < padding; i++) {
|
||
|
data.push_back('\0');
|
||
|
}
|
||
|
|
||
|
// Finally fill in the dm_target_spec.
|
||
|
struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);
|
||
|
spec->sector_start = start();
|
||
|
spec->length = size();
|
||
|
snprintf(spec->target_type, sizeof(spec->target_type), "%s", name().c_str());
|
||
|
spec->next = (uint32_t)data.size();
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
std::string DmTargetZero::GetParameterString() const {
|
||
|
// The zero target type has no additional parameters.
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
std::string DmTargetLinear::GetParameterString() const {
|
||
|
return block_device_ + " " + std::to_string(physical_sector_);
|
||
|
}
|
||
|
|
||
|
DmTargetVerity::DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
|
||
|
const std::string& block_device, const std::string& hash_device,
|
||
|
uint32_t data_block_size, uint32_t hash_block_size,
|
||
|
uint32_t num_data_blocks, uint32_t hash_start_block,
|
||
|
const std::string& hash_algorithm, const std::string& root_digest,
|
||
|
const std::string& salt)
|
||
|
: DmTarget(start, length), valid_(true) {
|
||
|
base_args_ = {
|
||
|
std::to_string(version),
|
||
|
block_device,
|
||
|
hash_device,
|
||
|
std::to_string(data_block_size),
|
||
|
std::to_string(hash_block_size),
|
||
|
std::to_string(num_data_blocks),
|
||
|
std::to_string(hash_start_block),
|
||
|
hash_algorithm,
|
||
|
root_digest,
|
||
|
salt,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
void DmTargetVerity::UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks,
|
||
|
uint32_t start) {
|
||
|
optional_args_.emplace_back("use_fec_from_device");
|
||
|
optional_args_.emplace_back(device);
|
||
|
optional_args_.emplace_back("fec_roots");
|
||
|
optional_args_.emplace_back(std::to_string(num_roots));
|
||
|
optional_args_.emplace_back("fec_blocks");
|
||
|
optional_args_.emplace_back(std::to_string(num_blocks));
|
||
|
optional_args_.emplace_back("fec_start");
|
||
|
optional_args_.emplace_back(std::to_string(start));
|
||
|
}
|
||
|
|
||
|
void DmTargetVerity::SetVerityMode(const std::string& mode) {
|
||
|
if (mode != "panic_on_corruption" &&
|
||
|
mode != "restart_on_corruption" &&
|
||
|
mode != "ignore_corruption") {
|
||
|
LOG(ERROR) << "Unknown verity mode: " << mode;
|
||
|
valid_ = false;
|
||
|
return;
|
||
|
}
|
||
|
optional_args_.emplace_back(mode);
|
||
|
}
|
||
|
|
||
|
void DmTargetVerity::IgnoreZeroBlocks() {
|
||
|
optional_args_.emplace_back("ignore_zero_blocks");
|
||
|
}
|
||
|
|
||
|
void DmTargetVerity::CheckAtMostOnce() {
|
||
|
optional_args_.emplace_back("check_at_most_once");
|
||
|
}
|
||
|
|
||
|
std::string DmTargetVerity::GetParameterString() const {
|
||
|
std::string base = android::base::Join(base_args_, " ");
|
||
|
if (optional_args_.empty()) {
|
||
|
return base;
|
||
|
}
|
||
|
std::string optional = android::base::Join(optional_args_, " ");
|
||
|
return base + " " + std::to_string(optional_args_.size()) + " " + optional;
|
||
|
}
|
||
|
|
||
|
std::string DmTargetAndroidVerity::GetParameterString() const {
|
||
|
return keyid_ + " " + block_device_;
|
||
|
}
|
||
|
|
||
|
std::string DmTargetBow::GetParameterString() const {
|
||
|
if (!block_size_) return target_string_;
|
||
|
return target_string_ + " 1 block_size:" + std::to_string(block_size_);
|
||
|
}
|
||
|
|
||
|
std::string DmTargetSnapshot::name() const {
|
||
|
if (mode_ == SnapshotStorageMode::Merge) {
|
||
|
return "snapshot-merge";
|
||
|
}
|
||
|
return "snapshot";
|
||
|
}
|
||
|
|
||
|
std::string DmTargetSnapshot::GetParameterString() const {
|
||
|
std::string mode;
|
||
|
switch (mode_) {
|
||
|
case SnapshotStorageMode::Persistent:
|
||
|
case SnapshotStorageMode::Merge:
|
||
|
// Note: "O" lets us query for overflow in the status message. This
|
||
|
// is only supported on kernels 4.4+. On earlier kernels, an overflow
|
||
|
// will be reported as "Invalid" in the status string.
|
||
|
mode = "P";
|
||
|
if (ReportsOverflow(name())) {
|
||
|
mode += "O";
|
||
|
}
|
||
|
break;
|
||
|
case SnapshotStorageMode::Transient:
|
||
|
mode = "N";
|
||
|
break;
|
||
|
default:
|
||
|
LOG(ERROR) << "DmTargetSnapshot unknown mode";
|
||
|
break;
|
||
|
}
|
||
|
return base_device_ + " " + cow_device_ + " " + mode + " " + std::to_string(chunk_size_);
|
||
|
}
|
||
|
|
||
|
// Computes the percentage of complition for snapshot status.
|
||
|
// @sectors_initial is the number of sectors_allocated stored right before
|
||
|
// starting the merge.
|
||
|
double DmTargetSnapshot::MergePercent(const DmTargetSnapshot::Status& status,
|
||
|
uint64_t sectors_initial) {
|
||
|
uint64_t s = status.sectors_allocated;
|
||
|
uint64_t t = status.total_sectors;
|
||
|
uint64_t m = status.metadata_sectors;
|
||
|
uint64_t i = sectors_initial == 0 ? t : sectors_initial;
|
||
|
|
||
|
if (t <= s || i <= s) {
|
||
|
return 0.0;
|
||
|
}
|
||
|
if (s == 0 || t == 0 || s <= m) {
|
||
|
return 100.0;
|
||
|
}
|
||
|
return 100.0 / (i - m) * (i - s);
|
||
|
}
|
||
|
|
||
|
bool DmTargetSnapshot::ReportsOverflow(const std::string& target_type) {
|
||
|
DeviceMapper& dm = DeviceMapper::Instance();
|
||
|
DmTargetTypeInfo info;
|
||
|
if (!dm.GetTargetByName(target_type, &info)) {
|
||
|
return false;
|
||
|
}
|
||
|
if (target_type == "snapshot") {
|
||
|
return info.IsAtLeast(1, 15, 0);
|
||
|
}
|
||
|
if (target_type == "snapshot-merge") {
|
||
|
return info.IsAtLeast(1, 4, 0);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool DmTargetSnapshot::ParseStatusText(const std::string& text, Status* status) {
|
||
|
// Try to parse the line as it should be
|
||
|
int args = sscanf(text.c_str(), "%" PRIu64 "/%" PRIu64 " %" PRIu64, &status->sectors_allocated,
|
||
|
&status->total_sectors, &status->metadata_sectors);
|
||
|
if (args == 3) {
|
||
|
return true;
|
||
|
}
|
||
|
auto sections = android::base::Split(text, " ");
|
||
|
if (sections.size() == 0) {
|
||
|
LOG(ERROR) << "could not parse empty status";
|
||
|
return false;
|
||
|
}
|
||
|
// Error codes are: "Invalid", "Overflow" and "Merge failed"
|
||
|
if (sections.size() == 1) {
|
||
|
if (text == "Invalid" || text == "Overflow") {
|
||
|
status->error = text;
|
||
|
return true;
|
||
|
}
|
||
|
} else if (sections.size() == 2 && text == "Merge failed") {
|
||
|
status->error = text;
|
||
|
return true;
|
||
|
}
|
||
|
LOG(ERROR) << "could not parse snapshot status: wrong format";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool DmTargetSnapshot::GetDevicesFromParams(const std::string& params, std::string* base_device,
|
||
|
std::string* cow_device) {
|
||
|
auto pieces = android::base::Split(params, " ");
|
||
|
if (pieces.size() < 2) {
|
||
|
LOG(ERROR) << "Parameter string is invalid: " << params;
|
||
|
return false;
|
||
|
}
|
||
|
*base_device = pieces[0];
|
||
|
*cow_device = pieces[1];
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
std::string DmTargetCrypt::GetParameterString() const {
|
||
|
std::vector<std::string> argv = {
|
||
|
cipher_,
|
||
|
key_,
|
||
|
std::to_string(iv_sector_offset_),
|
||
|
device_,
|
||
|
std::to_string(device_sector_),
|
||
|
};
|
||
|
|
||
|
std::vector<std::string> extra_argv;
|
||
|
if (allow_discards_) extra_argv.emplace_back("allow_discards");
|
||
|
if (allow_encrypt_override_) extra_argv.emplace_back("allow_encrypt_override");
|
||
|
if (iv_large_sectors_) extra_argv.emplace_back("iv_large_sectors");
|
||
|
if (sector_size_) extra_argv.emplace_back("sector_size:" + std::to_string(sector_size_));
|
||
|
|
||
|
if (!extra_argv.empty()) argv.emplace_back(std::to_string(extra_argv.size()));
|
||
|
|
||
|
argv.insert(argv.end(), extra_argv.begin(), extra_argv.end());
|
||
|
return android::base::Join(argv, " ");
|
||
|
}
|
||
|
|
||
|
bool DmTargetDefaultKey::Valid() const {
|
||
|
if (!use_legacy_options_format_ && !set_dun_) return false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
std::string DmTargetDefaultKey::GetParameterString() const {
|
||
|
std::vector<std::string> argv;
|
||
|
argv.emplace_back(cipher_);
|
||
|
argv.emplace_back(key_);
|
||
|
if (!use_legacy_options_format_) {
|
||
|
argv.emplace_back("0"); // iv_offset
|
||
|
}
|
||
|
argv.emplace_back(blockdev_);
|
||
|
argv.push_back(std::to_string(start_sector_));
|
||
|
std::vector<std::string> extra_argv;
|
||
|
if (use_legacy_options_format_) {
|
||
|
if (set_dun_) { // v2 always sets the DUN.
|
||
|
extra_argv.emplace_back("set_dun");
|
||
|
}
|
||
|
} else {
|
||
|
extra_argv.emplace_back("allow_discards");
|
||
|
extra_argv.emplace_back("sector_size:4096");
|
||
|
extra_argv.emplace_back("iv_large_sectors");
|
||
|
if (is_hw_wrapped_) extra_argv.emplace_back("wrappedkey_v0");
|
||
|
}
|
||
|
if (!extra_argv.empty()) {
|
||
|
argv.emplace_back(std::to_string(extra_argv.size()));
|
||
|
argv.insert(argv.end(), extra_argv.begin(), extra_argv.end());
|
||
|
}
|
||
|
return android::base::Join(argv, " ");
|
||
|
}
|
||
|
|
||
|
std::string DmTargetUser::GetParameterString() const {
|
||
|
std::vector<std::string> argv;
|
||
|
argv.push_back(std::to_string(start()));
|
||
|
argv.push_back(std::to_string(size()));
|
||
|
argv.push_back(control_device());
|
||
|
return android::base::Join(argv, " ");
|
||
|
}
|
||
|
|
||
|
} // namespace dm
|
||
|
} // namespace android
|