init/fs_mgr: prototype first-stage dm-linear support

This adds an API to fs_mgr for reading dm-linear tables out of device trees
and issuing device-mapper ioctls. The device tree code will be
implemented separately. The dm-linear structures in fs_mgr are organized
assuming we may want to pull them from sources other than DT (for
example, text files, binary blobs, or something hardcoded for testing).

File systems which are mounted from these logical partitions have
specific fstab requirements. The block device must be a partition name,
and if Verified Boot is used, that name must match the vbmeta partition name.
Second, the entry must have the "logical" fs_mgr flag. Example fstab
entry:

    vendor  /vendor  ext4  ro  wait,logical

Example fstab entry in device tree:

    vendor {
        compatible = "android,fstab";
        dev = "vendor";
        type = "ext4";
        mnt_flags = "ro";
        fs_mgr_flags = "wait,slotselect,avb,logical";
    };

Bug: 78914864
Test: N/A
Change-Id: I4d8878ea8858f26310119616cadc3ee0dd08566c
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index f23150d..05dba15 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -42,6 +42,7 @@
         "fs_mgr_verity.cpp",
         "fs_mgr_avb.cpp",
         "fs_mgr_avb_ops.cpp",
+        "fs_mgr_dm_linear.cpp",
     ],
     static_libs: [
         "libfec",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index e9040df..6e9ffba 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -794,6 +794,29 @@
     return true;
 }
 
+bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
+    // Logical partitions are specified with a named partition rather than a
+    // block device, so if the block device is a path, then it has already
+    // been updated.
+    if (rec->blk_device[0] == '/') {
+        return true;
+    }
+
+    android::base::unique_fd dm_fd(open("/dev/device-mapper", O_RDONLY));
+    if (dm_fd < 0) {
+        PLOG(ERROR) << "open /dev/device-mapper failed";
+        return false;
+    }
+    struct dm_ioctl io;
+    std::string device_name;
+    if (!fs_mgr_dm_get_device_name(&io, rec->blk_device, dm_fd, &device_name)) {
+        return false;
+    }
+    free(rec->blk_device);
+    rec->blk_device = strdup(device_name.c_str());
+    return true;
+}
+
 /* When multiple fstab records share the same mount_point, it will
  * try to mount each one in turn, and ignore any duplicates after a
  * first successful mount.
@@ -845,6 +868,13 @@
             }
         }
 
+        if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
+            if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+                LERROR << "Could not set up logical partition, skipping!";
+                continue;
+            }
+        }
+
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
             !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
             LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
@@ -1065,6 +1095,13 @@
             return FS_MGR_DOMNT_FAILED;
         }
 
+        if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
+            if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+                LERROR << "Could not set up logical partition, skipping!";
+                continue;
+            }
+        }
+
         /* First check the filesystem if requested */
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
             LERROR << "Skipping mounting '" << n_blk_device << "'";
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index cf6b497..5a9cb65 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -585,7 +585,13 @@
 
     // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
     // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
-    std::string partition_name(basename(fstab_entry->blk_device));
+    std::string partition_name;
+    if (fstab_entry->fs_mgr_flags & MF_LOGICAL) {
+        partition_name = fstab_entry->logical_partition_name;
+    } else {
+        partition_name = basename(fstab_entry->blk_device);
+    }
+
     if (fstab_entry->fs_mgr_flags & MF_SLOTSELECT) {
         auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix());
         if (ab_suffix != std::string::npos) {
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
new file mode 100644
index 0000000..b2f3a68
--- /dev/null
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "fs_mgr_dm_linear.h"
+
+#include <inttypes.h>
+#include <linux/dm-ioctl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <sstream>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_dm_ioctl.h"
+
+namespace android {
+namespace fs_mgr {
+
+std::string LogicalPartitionExtent::Serialize() const {
+    // Note: we need to include an explicit null-terminator.
+    std::string argv =
+        android::base::StringPrintf("%s %" PRIu64, block_device_.c_str(), first_sector_);
+    argv.push_back(0);
+
+    // The kernel expects each target to be aligned.
+    size_t spec_bytes = sizeof(struct dm_target_spec) + argv.size();
+    size_t padding = ((spec_bytes + 7) & ~7) - spec_bytes;
+    for (size_t i = 0; i < padding; i++) {
+        argv.push_back(0);
+    }
+
+    struct dm_target_spec spec;
+    spec.sector_start = logical_sector_;
+    spec.length = num_sectors_;
+    spec.status = 0;
+    strcpy(spec.target_type, "linear");
+    spec.next = sizeof(struct dm_target_spec) + argv.size();
+
+    return std::string((char*)&spec, sizeof(spec)) + argv;
+}
+
+static bool LoadDmTable(int dm_fd, const LogicalPartition& partition) {
+    // Combine all dm_target_spec buffers together.
+    std::string target_string;
+    for (const auto& extent : partition.extents) {
+        target_string += extent.Serialize();
+    }
+
+    // Allocate the ioctl buffer.
+    size_t buffer_size = sizeof(struct dm_ioctl) + target_string.size();
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(buffer_size);
+
+    // Initialize the ioctl buffer header, then copy our target specs in.
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+    fs_mgr_dm_ioctl_init(io, buffer_size, partition.name);
+    io->target_count = partition.extents.size();
+    if (partition.attributes & kPartitionReadonly) {
+        io->flags |= DM_READONLY_FLAG;
+    }
+    memcpy(io + 1, target_string.c_str(), target_string.size());
+
+    if (ioctl(dm_fd, DM_TABLE_LOAD, io)) {
+        PERROR << "Failed ioctl() on DM_TABLE_LOAD, partition " << partition.name;
+        return false;
+    }
+    return true;
+}
+
+static bool LoadTablesAndActivate(int dm_fd, const LogicalPartition& partition) {
+    if (!LoadDmTable(dm_fd, partition)) {
+        return false;
+    }
+
+    struct dm_ioctl io;
+    return fs_mgr_dm_resume_table(&io, partition.name, dm_fd);
+}
+
+static bool CreateDmDeviceForPartition(int dm_fd, const LogicalPartition& partition) {
+    struct dm_ioctl io;
+    if (!fs_mgr_dm_create_device(&io, partition.name, dm_fd)) {
+        return false;
+    }
+    if (!LoadTablesAndActivate(dm_fd, partition)) {
+        // Remove the device rather than leave it in an inactive state.
+        fs_mgr_dm_destroy_device(&io, partition.name, dm_fd);
+        return false;
+    }
+
+    LINFO << "Created device-mapper device: " << partition.name;
+    return true;
+}
+
+bool CreateLogicalPartitions(const LogicalPartitionTable& table) {
+    android::base::unique_fd dm_fd(open("/dev/device-mapper", O_RDWR));
+    if (dm_fd < 0) {
+        PLOG(ERROR) << "failed to open /dev/device-mapper";
+        return false;
+    }
+    for (const auto& partition : table.partitions) {
+        if (!CreateDmDeviceForPartition(dm_fd, partition)) {
+            LOG(ERROR) << "could not create dm-linear device for partition: " << partition.name;
+            return false;
+        }
+    }
+    return true;
+}
+
+std::unique_ptr<LogicalPartitionTable> LoadPartitionsFromDeviceTree() {
+    return nullptr;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 3fbef9f..472ab59 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -109,6 +109,7 @@
     {"logicalblk=", MF_LOGICALBLKSIZE},
     {"sysfs_path=", MF_SYSFS},
     {"defaults", 0},
+    {"logical", MF_LOGICAL},
     {0, 0},
 };
 
@@ -445,10 +446,6 @@
             LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
             return {};
         }
-        if (!StartsWith(value, "/dev")) {
-            LERROR << "dt_fstab: Invalid device node for partition " << dp->d_name;
-            return {};
-        }
         fstab_entry.push_back(value);
 
         std::string mount_point;
@@ -631,6 +628,10 @@
         fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
         fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
         fstab->recs[cnt].sysfs_path = flag_vals.sysfs_path;
+        if (fstab->recs[cnt].fs_mgr_flags & MF_LOGICAL) {
+            fstab->recs[cnt].logical_partition_name = strdup(fstab->recs[cnt].blk_device);
+        }
+
         cnt++;
     }
     /* If an A/B partition, modify block device to be the real block device */
@@ -797,6 +798,7 @@
     for (i = 0; i < fstab->num_entries; i++) {
         /* Free the pointers return by strdup(3) */
         free(fstab->recs[i].blk_device);
+        free(fstab->recs[i].logical_partition_name);
         free(fstab->recs[i].mount_point);
         free(fstab->recs[i].fs_type);
         free(fstab->recs[i].fs_options);
@@ -944,3 +946,7 @@
 {
     return fstab->fs_mgr_flags & MF_SYSFS;
 }
+
+int fs_mgr_is_logical(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & MF_LOGICAL;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index ade0cc4..afd7227 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -82,6 +82,7 @@
  *
  */
 
+// clang-format off
 #define MF_WAIT                  0x1
 #define MF_CHECK                 0x2
 #define MF_CRYPT                 0x4
@@ -111,6 +112,8 @@
 #define MF_AVB             0X2000000
 #define MF_KEYDIRECTORY    0X4000000
 #define MF_SYSFS           0X8000000
+#define MF_LOGICAL        0x10000000
+// clang-format on
 
 #define DM_BUF_SIZE 4096
 
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 0a113b4..3b01d0e 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -36,19 +36,30 @@
     std::string ab_suffix;
 
     for (n = 0; n < fstab->num_entries; n++) {
-        if (fstab->recs[n].fs_mgr_flags & MF_SLOTSELECT) {
-            char *tmp;
+        fstab_rec& record = fstab->recs[n];
+        if (record.fs_mgr_flags & MF_SLOTSELECT) {
             if (ab_suffix.empty()) {
                 ab_suffix = fs_mgr_get_slot_suffix();
                 // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
                 if (ab_suffix.empty()) return false;
             }
-            if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device, ab_suffix.c_str()) > 0) {
-                free(fstab->recs[n].blk_device);
-                fstab->recs[n].blk_device = tmp;
-            } else {
+
+            char* new_blk_device;
+            if (asprintf(&new_blk_device, "%s%s", record.blk_device, ab_suffix.c_str()) <= 0) {
                 return false;
             }
+            free(record.blk_device);
+            record.blk_device = new_blk_device;
+
+            char* new_partition_name;
+            if (record.logical_partition_name) {
+                if (asprintf(&new_partition_name, "%s%s", record.logical_partition_name,
+                             ab_suffix.c_str()) <= 0) {
+                    return false;
+                }
+                free(record.logical_partition_name);
+                record.logical_partition_name = new_partition_name;
+            }
         }
     }
     return true;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 653d8fa..72f019e 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -76,6 +76,7 @@
 bool fs_mgr_load_verity_state(int* mode);
 bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
 int fs_mgr_swapon_all(struct fstab *fstab);
+bool fs_mgr_update_logical_partition(struct fstab_rec* rec);
 
 int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
 
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
new file mode 100644
index 0000000..7f928e5
--- /dev/null
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CORE_FS_MGR_DM_LINEAR_H
+#define __CORE_FS_MGR_DM_LINEAR_H
+
+#include <stdint.h>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace fs_mgr {
+
+static const uint32_t kPartitionReadonly = 0x1;
+
+class LogicalPartitionExtent {
+  public:
+    LogicalPartitionExtent() : logical_sector_(0), first_sector_(0), num_sectors_(0) {}
+    LogicalPartitionExtent(uint64_t logical_sector, uint64_t first_sector, uint64_t num_sectors,
+                           const std::string& block_device)
+        : logical_sector_(logical_sector),
+          first_sector_(first_sector),
+          num_sectors_(num_sectors),
+          block_device_(block_device) {}
+
+    // Return a string containing the dm_target_spec buffer needed to use this
+    // extent in a device-mapper table.
+    std::string Serialize() const;
+
+    const std::string& block_device() const { return block_device_; }
+
+  private:
+    // Logical sector this extent represents in the presented block device.
+    // This is equal to the previous extent's logical sector plus the number
+    // of sectors in that extent. The first extent always starts at 0.
+    uint64_t logical_sector_;
+    // First 512-byte sector of this extent, on the source block device.
+    uint64_t first_sector_;
+    // Number of 512-byte sectors.
+    uint64_t num_sectors_;
+    // Target block device.
+    std::string block_device_;
+};
+
+struct LogicalPartition {
+    LogicalPartition() : attributes(0), num_sectors(0) {}
+
+    std::string name;
+    uint32_t attributes;
+    // Number of 512-byte sectors total.
+    uint64_t num_sectors;
+    // List of extents.
+    std::vector<LogicalPartitionExtent> extents;
+};
+
+struct LogicalPartitionTable {
+    // List of partitions in the partition table.
+    std::vector<LogicalPartition> partitions;
+};
+
+// Load a dm-linear table from the device tree if one is available; otherwise,
+// return null.
+std::unique_ptr<LogicalPartitionTable> LoadPartitionsFromDeviceTree();
+
+// Create device-mapper devices for the given partition table.
+//
+// On success, two devices nodes will be created for each partition, both
+// pointing to the same device:
+//   /dev/block/dm-<N> where N is a sequential ID assigned by device-mapper.
+//   /dev/block/dm-<name> where |name| is the partition name.
+//
+bool CreateLogicalPartitions(const LogicalPartitionTable& table);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // __CORE_FS_MGR_DM_LINEAR_H
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 8c585dd..04ccfc5 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -37,6 +37,7 @@
 
 struct fstab_rec {
     char* blk_device;
+    char* logical_partition_name;
     char* mount_point;
     char* fs_type;
     unsigned long flags;
@@ -84,6 +85,7 @@
 int fs_mgr_is_nofail(const struct fstab_rec* fstab);
 int fs_mgr_is_latemount(const struct fstab_rec* fstab);
 int fs_mgr_is_quota(const struct fstab_rec* fstab);
+int fs_mgr_is_logical(const struct fstab_rec* fstab);
 int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
 
 std::string fs_mgr_get_slot_suffix();
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 1b6e97e..a5ba647 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -33,11 +33,13 @@
 #include "devices.h"
 #include "fs_mgr.h"
 #include "fs_mgr_avb.h"
+#include "fs_mgr_dm_linear.h"
 #include "uevent.h"
 #include "uevent_listener.h"
 #include "util.h"
 
 using android::base::Timer;
+using android::fs_mgr::LogicalPartitionTable;
 
 namespace android {
 namespace init {
@@ -58,18 +60,21 @@
   protected:
     ListenerAction HandleBlockDevice(const std::string& name, const Uevent&);
     bool InitRequiredDevices();
-    bool InitVerityDevice(const std::string& verity_device);
+    bool InitMappedDevice(const std::string& verity_device);
+    bool CreateLogicalPartitions();
     bool MountPartitions();
+    bool GetBackingDmLinearDevices();
 
     virtual ListenerAction UeventCallback(const Uevent& uevent);
 
     // Pure virtual functions.
-    virtual bool GetRequiredDevices() = 0;
+    virtual bool GetDmVerityDevices() = 0;
     virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
 
     bool need_dm_verity_;
 
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
+    std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
     DeviceHandler device_handler_;
@@ -82,7 +87,7 @@
     ~FirstStageMountVBootV1() override = default;
 
   protected:
-    bool GetRequiredDevices() override;
+    bool GetDmVerityDevices() override;
     bool SetUpDmVerity(fstab_rec* fstab_rec) override;
 };
 
@@ -95,7 +100,7 @@
 
   protected:
     ListenerAction UeventCallback(const Uevent& uevent) override;
-    bool GetRequiredDevices() override;
+    bool GetDmVerityDevices() override;
     bool SetUpDmVerity(fstab_rec* fstab_rec) override;
     bool InitAvbHandle();
 
@@ -114,6 +119,17 @@
     return access("/sbin/recovery", F_OK) == 0;
 }
 
+static inline bool IsDmLinearEnabled() {
+    bool enabled = false;
+    import_kernel_cmdline(
+        false, [&enabled](const std::string& key, const std::string& value, bool in_qemu) {
+            if (key == "androidboot.lrap" && value == "1") {
+                enabled = true;
+            }
+        });
+    return enabled;
+}
+
 // Class Definitions
 // -----------------
 FirstStageMount::FirstStageMount()
@@ -127,6 +143,10 @@
     } else {
         LOG(INFO) << "Failed to read fstab from device tree";
     }
+
+    if (IsDmLinearEnabled()) {
+        dm_linear_table_ = android::fs_mgr::LoadPartitionsFromDeviceTree();
+    }
 }
 
 std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
@@ -138,7 +158,7 @@
 }
 
 bool FirstStageMount::DoFirstStageMount() {
-    if (mount_fstab_recs_.empty()) {
+    if (!dm_linear_table_ && mount_fstab_recs_.empty()) {
         // Nothing to mount.
         LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
         return true;
@@ -146,13 +166,30 @@
 
     if (!InitDevices()) return false;
 
+    if (!CreateLogicalPartitions()) return false;
+
     if (!MountPartitions()) return false;
 
     return true;
 }
 
 bool FirstStageMount::InitDevices() {
-    return GetRequiredDevices() && InitRequiredDevices();
+    return GetBackingDmLinearDevices() && GetDmVerityDevices() && InitRequiredDevices();
+}
+
+bool FirstStageMount::GetBackingDmLinearDevices() {
+    // Add any additional devices required for dm-linear mappings.
+    if (!dm_linear_table_) {
+        return true;
+    }
+
+    for (const auto& partition : dm_linear_table_->partitions) {
+        for (const auto& extent : partition.extents) {
+            const std::string& partition_name = android::base::Basename(extent.block_device());
+            required_devices_partition_names_.emplace(partition_name);
+        }
+    }
+    return true;
 }
 
 // Creates devices with uevent->partition_name matching one in the member variable
@@ -163,7 +200,7 @@
         return true;
     }
 
-    if (need_dm_verity_) {
+    if (dm_linear_table_ || need_dm_verity_) {
         const std::string dm_path = "/devices/virtual/misc/device-mapper";
         bool found = false;
         auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
@@ -210,6 +247,13 @@
     return true;
 }
 
+bool FirstStageMount::CreateLogicalPartitions() {
+    if (!dm_linear_table_) {
+        return true;
+    }
+    return android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get());
+}
+
 ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
     // Matches partition name to create device nodes.
     // Both required_devices_partition_names_ and uevent->partition_name have A/B
@@ -247,14 +291,14 @@
 }
 
 // Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-bool FirstStageMount::InitVerityDevice(const std::string& verity_device) {
-    const std::string device_name(basename(verity_device.c_str()));
+bool FirstStageMount::InitMappedDevice(const std::string& dm_device) {
+    const std::string device_name(basename(dm_device.c_str()));
     const std::string syspath = "/sys/block/" + device_name;
     bool found = false;
 
-    auto verity_callback = [&device_name, &verity_device, this, &found](const Uevent& uevent) {
+    auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
         if (uevent.device_name == device_name) {
-            LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
+            LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
             device_handler_.HandleDeviceEvent(uevent);
             found = true;
             return ListenerAction::kStop;
@@ -279,6 +323,14 @@
 
 bool FirstStageMount::MountPartitions() {
     for (auto fstab_rec : mount_fstab_recs_) {
+        if (fs_mgr_is_logical(fstab_rec)) {
+            if (!fs_mgr_update_logical_partition(fstab_rec)) {
+                return false;
+            }
+            if (!InitMappedDevice(fstab_rec->blk_device)) {
+                return false;
+            }
+        }
         if (!SetUpDmVerity(fstab_rec)) {
             PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
             return false;
@@ -291,7 +343,7 @@
     return true;
 }
 
-bool FirstStageMountVBootV1::GetRequiredDevices() {
+bool FirstStageMountVBootV1::GetDmVerityDevices() {
     std::string verity_loc_device;
     need_dm_verity_ = false;
 
@@ -344,7 +396,7 @@
                 // The exact block device name (fstab_rec->blk_device) is changed to
                 // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
                 // first stage.
-                return InitVerityDevice(fstab_rec->blk_device);
+                return InitMappedDevice(fstab_rec->blk_device);
             default:
                 return false;
         }
@@ -371,7 +423,7 @@
     }
 }
 
-bool FirstStageMountVBootV2::GetRequiredDevices() {
+bool FirstStageMountVBootV2::GetDmVerityDevices() {
     need_dm_verity_ = false;
 
     // fstab_rec->blk_device has A/B suffix.
@@ -444,7 +496,7 @@
                 // The exact block device name (fstab_rec->blk_device) is changed to
                 // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
                 // first stage.
-                return InitVerityDevice(fstab_rec->blk_device);
+                return InitMappedDevice(fstab_rec->blk_device);
             default:
                 return false;
         }