Merge "liblp: Add unit tests."
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