Merge "liblp: Add unit tests for flashing and readback."
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 672d401..f1b18dd 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -16,6 +16,7 @@
cc_library_static {
name: "libdm",
+ defaults: ["fs_mgr_defaults"],
recovery_available: true,
export_include_dirs: ["include"],
@@ -35,3 +36,16 @@
"liblog_headers",
],
}
+
+cc_test {
+ name: "libdm_test",
+ defaults: ["fs_mgr_defaults"],
+ static_libs: [
+ "libdm",
+ "libbase",
+ "liblog",
+ ],
+ srcs: [
+ "dm_test.cpp",
+ ]
+}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 5a2dfc6..8bd3b9d 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -114,6 +114,9 @@
io->data_size = ioctl_buffer.size();
io->data_start = sizeof(struct dm_ioctl);
io->target_count = static_cast<uint32_t>(table.num_targets());
+ if (table.readonly()) {
+ io->flags |= DM_READONLY_FLAG;
+ }
if (ioctl(fd_, DM_TABLE_LOAD, io)) {
PLOG(ERROR) << "DM_TABLE_LOAD failed";
return false;
@@ -129,7 +132,7 @@
// Reads all the available device mapper targets and their corresponding
// versions from the kernel and returns in a vector
-bool DeviceMapper::GetAvailableTargets(std::vector<DmTarget>* targets) {
+bool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets) {
targets->clear();
// calculate the space needed to read a maximum of kMaxPossibleDmTargets
@@ -178,7 +181,7 @@
struct dm_target_versions* vers =
reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
while (next && data_size) {
- targets->emplace_back((vers));
+ targets->emplace_back(vers);
if (vers->next == 0) {
break;
}
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
index 868286e..cb6f210 100644
--- a/fs_mgr/libdm/dm_table.cpp
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -22,7 +22,8 @@
namespace android {
namespace dm {
-bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& /* target */) {
+bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& target) {
+ targets_.push_back(std::move(target));
return true;
}
@@ -31,6 +32,14 @@
}
bool DmTable::valid() const {
+ if (targets_.empty()) {
+ LOG(ERROR) << "Device-mapper table must have at least one target.";
+ return "";
+ }
+ if (targets_[0]->start() != 0) {
+ LOG(ERROR) << "Device-mapper table must start at logical sector 0.";
+ return "";
+ }
return true;
}
@@ -38,14 +47,22 @@
return valid() ? num_sectors_ : 0;
}
-// Returns a string represnetation of the table that is ready to be passed
-// down to the kernel for loading
+// Returns a string representation of the table that is ready to be passed
+// down to the kernel for loading.
//
// Implementation must verify there are no gaps in the table, table starts
// with sector == 0, and iterate over each target to get its table
// serialized.
std::string DmTable::Serialize() const {
- return "";
+ if (!valid()) {
+ return "";
+ }
+
+ std::string table;
+ for (const auto& target : targets_) {
+ table += target->Serialize();
+ }
+ return table;
}
} // namespace dm
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index dbe4fed..5934416 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -19,6 +19,41 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <libdm/dm.h>
+
namespace android {
-namespace dm {} // namespace dm
+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();
+ strlcpy(spec->target_type, name().c_str(), sizeof(spec->target_type));
+ 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_);
+}
+
+} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
new file mode 100644
index 0000000..adbe820
--- /dev/null
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 <map>
+
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+
+using namespace std;
+using namespace android::dm;
+
+TEST(libdm, HasMinimumTargets) {
+ DeviceMapper& dm = DeviceMapper::Instance();
+ vector<DmTargetTypeInfo> targets;
+ ASSERT_TRUE(dm.GetAvailableTargets(&targets));
+
+ map<string, DmTargetTypeInfo> by_name;
+ for (const auto& target : targets) {
+ by_name[target.name()] = target;
+ }
+
+ auto iter = by_name.find("linear");
+ EXPECT_NE(iter, by_name.end());
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 8407774..8d23b2c 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -95,7 +95,7 @@
// Returns true if a list of available device mapper targets registered in the kernel was
// successfully read and stored in 'targets'. Returns 'false' otherwise.
- bool GetAvailableTargets(std::vector<DmTarget>* targets);
+ bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);
// Return 'true' if it can successfully read the list of device mapper block devices
// currently created. 'devices' will be empty if the kernel interactions
diff --git a/fs_mgr/libdm/include/libdm/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h
index 8a5c624..5c639be 100644
--- a/fs_mgr/libdm/include/libdm/dm_table.h
+++ b/fs_mgr/libdm/include/libdm/dm_table.h
@@ -30,7 +30,7 @@
class DmTable {
public:
- DmTable() : num_sectors_(0){};
+ DmTable() : num_sectors_(0), readonly_(false) {}
// Adds a target to the device mapper table for a range specified in the target object.
// The function will return 'true' if the target was successfully added and doesn't overlap with
@@ -59,6 +59,9 @@
// as part of the DM_TABLE_LOAD ioctl.
std::string Serialize() const;
+ void set_readonly(bool readonly) { readonly_ = readonly; }
+ bool readonly() const { return readonly_; }
+
~DmTable() = default;
private:
@@ -70,6 +73,9 @@
// Total size in terms of # of sectors, as calculated by looking at the last and the first
// target in 'target_'.
uint64_t num_sectors_;
+
+ // True if the device should be read-only; false otherwise.
+ bool readonly_;
};
} // namespace dm
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index e3058a8..31b6a70 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -27,50 +27,81 @@
namespace android {
namespace dm {
+class DmTargetTypeInfo {
+ public:
+ DmTargetTypeInfo() : major_(0), minor_(0), patch_(0) {}
+ DmTargetTypeInfo(const struct dm_target_versions* info)
+ : name_(info->name),
+ major_(info->version[0]),
+ minor_(info->version[1]),
+ patch_(info->version[2]) {}
+
+ const std::string& name() const { return name_; }
+ std::string version() const {
+ return std::to_string(major_) + "." + std::to_string(minor_) + "." + std::to_string(patch_);
+ }
+
+ private:
+ std::string name_;
+ uint32_t major_;
+ uint32_t minor_;
+ uint32_t patch_;
+};
+
class DmTarget {
public:
- DmTarget(const std::string& name, uint64_t start = 0, uint64_t length = 0)
- : name_(name), v0_(0), v1_(0), v2_(0), start_(start), length_(length){};
-
- // Creates a DmTarget object from dm_target_version as read from kernel
- // with DM_LIST_VERSION ioctl.
- DmTarget(const struct dm_target_versions* vers) : start_(0), length_(0) {
- CHECK(vers != nullptr) << "Can't create DmTarget with dm_target_versions set to nullptr";
- v0_ = vers->version[0];
- v1_ = vers->version[1];
- v2_ = vers->version[2];
- name_ = vers->name;
- }
+ DmTarget(uint64_t start, uint64_t length) : start_(start), length_(length) {}
virtual ~DmTarget() = default;
// Returns name of the target.
- const std::string& name() const { return name_; }
+ virtual std::string name() const = 0;
+
+ // Return the first logical sector represented by this target.
+ uint64_t start() const { return start_; }
// Returns size in number of sectors when this target is part of
// a DmTable, return 0 otherwise.
uint64_t size() const { return length_; }
- // Return string representation of the device mapper target version.
- std::string version() const {
- return std::to_string(v0_) + "." + std::to_string(v1_) + "." + std::to_string(v2_);
- }
-
// Function that converts this object to a string of arguments that can
// be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear)
// must implement this, for it to be used on a device.
- virtual std::string Serialize() const { return ""; }
+ std::string Serialize() const;
+
+ protected:
+ // Get the parameter string that is passed to the end of the dm_target_spec
+ // for this target type.
+ virtual std::string GetParameterString() const = 0;
private:
- // Name of the target.
- std::string name_;
- // Target version.
- uint32_t v0_, v1_, v2_;
// logical sector number start and total length (in terms of 512-byte sectors) represented
// by this target within a DmTable.
uint64_t start_, length_;
};
+class DmTargetZero final : public DmTarget {
+ public:
+ DmTargetZero(uint64_t start, uint64_t length) : DmTarget(start, length) {}
+
+ std::string name() const override { return "zero"; }
+ std::string GetParameterString() const override;
+};
+
+class DmTargetLinear final : public DmTarget {
+ public:
+ DmTargetLinear(uint64_t start, uint64_t length, const std::string& block_device,
+ uint64_t physical_sector)
+ : DmTarget(start, length), block_device_(block_device), physical_sector_(physical_sector) {}
+
+ std::string name() const override { return "linear"; }
+ std::string GetParameterString() const override;
+
+ private:
+ std::string block_device_;
+ uint64_t physical_sector_;
+};
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index d637dc6..c15173f 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -22,6 +22,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
#include <libdm/dm.h>
@@ -30,45 +31,131 @@
#include <ios>
#include <iostream>
#include <map>
+#include <sstream>
#include <string>
#include <vector>
using DeviceMapper = ::android::dm::DeviceMapper;
using DmTable = ::android::dm::DmTable;
using DmTarget = ::android::dm::DmTarget;
+using DmTargetLinear = ::android::dm::DmTargetLinear;
+using DmTargetZero = ::android::dm::DmTargetZero;
+using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
static int Usage(void) {
std::cerr << "usage: dmctl <command> [command options]" << std::endl;
std::cerr << "commands:" << std::endl;
- std::cerr << " create <dm-name> [<dm-target> [-lo <filename>] <dm-target-args>]" << std::endl;
+ std::cerr << " create <dm-name> [-ro] <targets...>" << std::endl;
std::cerr << " delete <dm-name>" << std::endl;
std::cerr << " list <devices | targets>" << std::endl;
std::cerr << " help" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << "Target syntax:" << std::endl;
+ std::cerr << " <target_type> <start_sector> <num_sectors> [target_data]" << std::endl;
return -EINVAL;
}
+class TargetParser final {
+ public:
+ TargetParser(int argc, char** argv) : arg_index_(0), argc_(argc), argv_(argv) {}
+
+ bool More() const { return arg_index_ < argc_; }
+ std::unique_ptr<DmTarget> Next() {
+ if (!HasArgs(3)) {
+ std::cerr << "Expected <target_type> <start_sector> <num_sectors>" << std::endl;
+ return nullptr;
+ }
+
+ std::string target_type = NextArg();
+ uint64_t start_sector, num_sectors;
+ if (!android::base::ParseUint(NextArg(), &start_sector)) {
+ std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
+ return nullptr;
+ }
+ if (!android::base::ParseUint(NextArg(), &num_sectors) || !num_sectors) {
+ std::cerr << "Expected non-zero sector count, got: " << PreviousArg() << std::endl;
+ return nullptr;
+ }
+
+ if (target_type == "zero") {
+ return std::make_unique<DmTargetZero>(start_sector, num_sectors);
+ } else if (target_type == "linear") {
+ if (!HasArgs(2)) {
+ std::cerr << "Expected \"linear\" <block_device> <sector>" << std::endl;
+ return nullptr;
+ }
+
+ std::string block_device = NextArg();
+ uint64_t physical_sector;
+ if (!android::base::ParseUint(NextArg(), &physical_sector)) {
+ std::cerr << "Expected sector, got: \"" << PreviousArg() << "\"" << std::endl;
+ return nullptr;
+ }
+ return std::make_unique<DmTargetLinear>(start_sector, num_sectors, block_device,
+ physical_sector);
+ } else {
+ std::cerr << "Unrecognized target type: " << target_type << std::endl;
+ return nullptr;
+ }
+ }
+
+ private:
+ bool HasArgs(int count) { return arg_index_ + count <= argc_; }
+ const char* NextArg() {
+ CHECK(arg_index_ < argc_);
+ return argv_[arg_index_++];
+ }
+ const char* PreviousArg() {
+ CHECK(arg_index_ >= 0);
+ return argv_[arg_index_ - 1];
+ }
+
+ private:
+ int arg_index_;
+ int argc_;
+ char** argv_;
+};
+
static int DmCreateCmdHandler(int argc, char** argv) {
if (argc < 1) {
- std::cerr << "Usage: dmctl create <name> [table-args]" << std::endl;
+ std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
+ return -EINVAL;
+ }
+ std::string name = argv[0];
+
+ // Parse extended options first.
+ DmTable table;
+ int arg_index = 1;
+ while (arg_index < argc && argv[arg_index][0] == '-') {
+ if (strcmp(argv[arg_index], "-ro") == 0) {
+ table.set_readonly(true);
+ } else {
+ std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
+ return -EINVAL;
+ }
+ arg_index++;
+ }
+
+ // Parse everything else as target information.
+ TargetParser parser(argc - arg_index, argv + arg_index);
+ while (parser.More()) {
+ std::unique_ptr<DmTarget> target = parser.Next();
+ if (!target || !table.AddTarget(std::move(target))) {
+ return -EINVAL;
+ }
+ }
+
+ if (table.num_targets() == 0) {
+ std::cerr << "Must define at least one target." << std::endl;
return -EINVAL;
}
- DmTable table;
-
- std::string name = argv[0];
DeviceMapper& dm = DeviceMapper::Instance();
if (!dm.CreateDevice(name, table)) {
std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
return -EIO;
}
-
- // if we also have target specified
- if (argc > 1) {
- // fall through for now. This will eventually create a DmTarget() based on the target name
- // passing it the table that is specified at the command line
- }
-
return 0;
}
@@ -89,7 +176,7 @@
}
static int DmListTargets(DeviceMapper& dm) {
- std::vector<DmTarget> targets;
+ std::vector<DmTargetTypeInfo> targets;
if (!dm.GetAvailableTargets(&targets)) {
std::cerr << "Failed to read available device mapper targets" << std::endl;
return -errno;
diff --git a/init/init.cpp b/init/init.cpp
index b494bcc..77c4fc4 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -352,21 +352,23 @@
}
static void export_kernel_boot_props() {
+ constexpr const char* UNSET = "";
struct {
const char *src_prop;
const char *dst_prop;
const char *default_value;
} prop_map[] = {
- { "ro.boot.serialno", "ro.serialno", "", },
+ { "ro.boot.serialno", "ro.serialno", UNSET, },
{ "ro.boot.mode", "ro.bootmode", "unknown", },
{ "ro.boot.baseband", "ro.baseband", "unknown", },
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
{ "ro.boot.hardware", "ro.hardware", "unknown", },
{ "ro.boot.revision", "ro.revision", "0", },
};
- for (size_t i = 0; i < arraysize(prop_map); i++) {
- std::string value = GetProperty(prop_map[i].src_prop, "");
- property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
+ for (const auto& prop : prop_map) {
+ std::string value = GetProperty(prop.src_prop, prop.default_value);
+ if (value != UNSET)
+ property_set(prop.dst_prop, value);
}
}
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 329ba85..6d78ed6 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -21,6 +21,7 @@
TEST(libc, __pstore_append) {
#ifdef __ANDROID__
+#ifndef NO_PSTORE
FILE* fp;
ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
static const char message[] = "libc.__pstore_append\n";
@@ -43,6 +44,9 @@
"Reboot, ensure string libc.__pstore_append is in "
"/sys/fs/pstore/pmsg-ramoops-0\n");
}
+#else /* NO_PSTORE */
+ GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif