Merge "Ignore null-pointer-arithmetic warning from dlmalloc include"
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index b72ed16..1a92317 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -245,9 +245,8 @@
     // If we can use overlayfs, lets get it in place first
     // before we struggle with determining deduplication operations.
     if (!verity_enabled && fs_mgr_overlayfs_setup()) {
-        std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                                   fs_mgr_free_fstab);
-        if (fs_mgr_overlayfs_mount_all(fstab.get())) {
+        Fstab fstab;
+        if (ReadDefaultFstab(&fstab) && fs_mgr_overlayfs_mount_all(&fstab)) {
             WriteFdExactly(fd.get(), "overlayfs mounted\n");
         }
     }
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 01097ac..8805fc1 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -273,7 +273,7 @@
     }
 
     std::vector<std::string> joined_env;
-    for (auto it : env) {
+    for (const auto& it : env) {
         const char* key = it.first.c_str();
         const char* value = it.second.c_str();
         joined_env.push_back(android::base::StringPrintf("%s=%s", key, value));
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 479a06a..ef34771 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -281,7 +281,7 @@
     std::vector<std::string> vars;
     EXPECT_EQ(fb->GetVarAll(&vars), SUCCESS) << "getvar:all failed";
     EXPECT_GT(vars.size(), 0) << "getvar:all did not respond with any INFO responses";
-    for (const auto s : vars) {
+    for (const auto& s : vars) {
         EXPECT_LE(s.size(), FB_RESPONSE_SZ - 4)
                 << "getvar:all included an INFO response: 'INFO" + s << "' which is too long";
     }
@@ -316,7 +316,7 @@
     EXPECT_GT(parts.size(), 0)
             << "getvar:all did not report any partition-size: through INFO responses";
     std::set<std::string> allowed{"ext4", "f2fs", "raw"};
-    for (const auto p : parts) {
+    for (const auto& p : parts) {
         EXPECT_GE(std::get<1>(p), 0);
         std::string part(std::get<0>(p));
         std::set<std::string> allowed{"ext4", "f2fs", "raw"};
@@ -355,7 +355,7 @@
     if (num_slots > 0) {
         EXPECT_EQ(fb->GetVar("current-slot", &var), SUCCESS) << "getvar:current-slot failed";
 
-        for (const auto p : parts) {
+        for (const auto& p : parts) {
             std::string part(std::get<0>(p));
             std::regex reg("([[:graph:]]*)_([[:lower:]])");
             std::smatch sm;
@@ -378,7 +378,7 @@
             }
         }
         // Ensure each partition has the correct slot suffix
-        for (const auto iter : part_slots) {
+        for (const auto& iter : part_slots) {
             const std::set<char>& char_set = iter.second;
             std::string chars;
             for (char c : char_set) {
@@ -572,7 +572,7 @@
     std::vector<std::tuple<std::string, uint64_t>> parts;
     EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in locked mode";
     std::string resp;
-    for (const auto tup : parts) {
+    for (const auto& tup : parts) {
         EXPECT_EQ(fb->Flash(std::get<0>(tup), &resp), DEVICE_FAIL)
                 << "Device did not respond with FAIL when trying to flash '" << std::get<0>(tup)
                 << "' in locked mode";
@@ -585,7 +585,7 @@
     std::vector<std::tuple<std::string, uint64_t>> parts;
     EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
     std::string resp;
-    for (const auto tup : parts) {
+    for (const auto& tup : parts) {
         EXPECT_EQ(fb->Erase(std::get<0>(tup), &resp), DEVICE_FAIL)
                 << "Device did not respond with FAIL when trying to erase '" << std::get<0>(tup)
                 << "' in locked mode";
@@ -601,7 +601,7 @@
     EXPECT_EQ(fb->GetVar("slot-count", &resp), SUCCESS) << "getvar:slot-count failed";
     int32_t num_slots = strtol(resp.c_str(), nullptr, 10);
 
-    for (const auto tup : parts) {
+    for (const auto& tup : parts) {
         std::string part(std::get<0>(tup));
         std::regex reg("([[:graph:]]*)_([[:lower:]])");
         std::smatch sm;
@@ -1554,12 +1554,12 @@
 
 void GenerateXmlTests(const extension::Configuration& config) {
     // Build the getvar tests
-    for (const auto it : config.getvars) {
+    for (const auto& it : config.getvars) {
         GETVAR_XML_TESTS.push_back(std::make_pair(it.first, it.second));
     }
 
     // Build the partition tests, to interface with gtest we need to do it this way
-    for (const auto it : config.partitions) {
+    for (const auto& it : config.partitions) {
         const auto tup = std::make_tuple(it.first, it.second);
         PARTITION_XML_TESTS.push_back(tup);  // All partitions
 
@@ -1581,7 +1581,7 @@
 
     // Build the packed tests, only useful if we have a hash
     if (!config.checksum.empty()) {
-        for (const auto it : config.packed) {
+        for (const auto& it : config.packed) {
             for (const auto& test : it.second.tests) {
                 const auto tup = std::make_tuple(it.first, test);
                 if (test.expect == extension::OKAY) {  // only testing the success case
@@ -1608,7 +1608,7 @@
     }
 
     // Build oem tests
-    for (const auto it : config.oem) {
+    for (const auto& it : config.oem) {
         auto oem_cmd = it.second;
         for (const auto& t : oem_cmd.tests) {
             OEM_XML_TESTS.push_back(std::make_tuple(it.first, oem_cmd.restricted, t));
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index d1eabab..8757689 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -336,10 +336,10 @@
 }
 
 // Enable/disable quota support on the filesystem if needed.
-static void tune_quota(const std::string& blk_device, const struct fstab_rec* rec,
+static void tune_quota(const std::string& blk_device, const FstabEntry& entry,
                        const struct ext4_super_block* sb, int* fs_stat) {
     bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
-    bool want_quota = fs_mgr_is_quota(rec) != 0;
+    bool want_quota = entry.fs_mgr_flags.quota;
 
     if (has_quota == want_quota) {
         return;
@@ -372,16 +372,16 @@
 }
 
 // Set the number of reserved filesystem blocks if needed.
-static void tune_reserved_size(const std::string& blk_device, const struct fstab_rec* rec,
+static void tune_reserved_size(const std::string& blk_device, const FstabEntry& entry,
                                const struct ext4_super_block* sb, int* fs_stat) {
-    if (!(rec->fs_mgr_flags & MF_RESERVEDSIZE)) {
+    if (!entry.fs_mgr_flags.reserved_size) {
         return;
     }
 
     // The size to reserve is given in the fstab, but we won't reserve more
     // than 2% of the filesystem.
     const uint64_t max_reserved_blocks = ext4_blocks_count(sb) * 0.02;
-    uint64_t reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(sb);
+    uint64_t reserved_blocks = entry.reserved_size / EXT4_BLOCK_SIZE(sb);
 
     if (reserved_blocks > max_reserved_blocks) {
         LWARNING << "Reserved blocks " << reserved_blocks << " is too large; "
@@ -414,10 +414,10 @@
 }
 
 // Enable file-based encryption if needed.
-static void tune_encrypt(const std::string& blk_device, const struct fstab_rec* rec,
+static void tune_encrypt(const std::string& blk_device, const FstabEntry& entry,
                          const struct ext4_super_block* sb, int* fs_stat) {
     bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0;
-    bool want_encrypt = fs_mgr_is_file_encrypted(rec) != 0;
+    bool want_encrypt = entry.fs_mgr_flags.file_encryption;
 
     if (has_encrypt || !want_encrypt) {
         return;
@@ -478,10 +478,10 @@
 // If needed, we'll also enable (or disable) filesystem features as specified by
 // the fstab record.
 //
-static int prepare_fs_for_mount(const std::string& blk_device, const struct fstab_rec* rec) {
+static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry& entry) {
     int fs_stat = 0;
 
-    if (is_extfs(rec->fs_type)) {
+    if (is_extfs(entry.fs_type)) {
         struct ext4_super_block sb;
 
         if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
@@ -494,27 +494,28 @@
             }
 
             // Note: quotas should be enabled before running fsck.
-            tune_quota(blk_device, rec, &sb, &fs_stat);
+            tune_quota(blk_device, entry, &sb, &fs_stat);
         } else {
             return fs_stat;
         }
-    } else if (is_f2fs(rec->fs_type)) {
+    } else if (is_f2fs(entry.fs_type)) {
         if (!read_f2fs_superblock(blk_device, &fs_stat)) {
             return fs_stat;
         }
     }
 
-    if ((rec->fs_mgr_flags & MF_CHECK) ||
+    if (entry.fs_mgr_flags.check ||
         (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
-        check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat);
+        check_fs(blk_device, entry.fs_type, entry.mount_point, &fs_stat);
     }
 
-    if (is_extfs(rec->fs_type) && (rec->fs_mgr_flags & (MF_RESERVEDSIZE | MF_FILEENCRYPTION))) {
+    if (is_extfs(entry.fs_type) &&
+        (entry.fs_mgr_flags.reserved_size || entry.fs_mgr_flags.file_encryption)) {
         struct ext4_super_block sb;
 
         if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
-            tune_reserved_size(blk_device, rec, &sb, &fs_stat);
-            tune_encrypt(blk_device, rec, &sb, &fs_stat);
+            tune_reserved_size(blk_device, entry, &sb, &fs_stat);
+            tune_encrypt(blk_device, entry, &sb, &fs_stat);
         }
     }
 
@@ -545,8 +546,7 @@
 // __mount(): wrapper around the mount() system call which also
 // sets the underlying block device to read-only if the mount is read-only.
 // See "man 2 mount" for return values.
-static int __mount(const std::string& source, const std::string& target,
-                   const struct fstab_rec* rec) {
+static int __mount(const std::string& source, const std::string& target, const FstabEntry& entry) {
     // We need this because sometimes we have legacy symlinks that are
     // lingering around and need cleaning up.
     struct stat info;
@@ -555,8 +555,9 @@
     }
     mkdir(target.c_str(), 0755);
     errno = 0;
-    unsigned long mountflags = rec->flags;
-    int ret = mount(source.c_str(), target.c_str(), rec->fs_type, mountflags, rec->fs_options);
+    unsigned long mountflags = entry.flags;
+    int ret = mount(source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
+                    entry.fs_options.c_str());
     int save_errno = errno;
     const char* target_missing = "";
     const char* source_missing = "";
@@ -569,7 +570,7 @@
         errno = save_errno;
     }
     PINFO << __FUNCTION__ << "(source=" << source << source_missing << ",target=" << target
-          << target_missing << ",type=" << rec->fs_type << ")=" << ret;
+          << target_missing << ",type=" << entry.fs_type << ")=" << ret;
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
@@ -605,106 +606,88 @@
     return true;
 }
 
-/*
- * Tries to mount any of the consecutive fstab entries that match
- * the mountpoint of the one given by fstab->recs[start_idx].
- *
- * end_idx: On return, will be the last rec that was looked at.
- * attempted_idx: On return, will indicate which fstab rec
- *     succeeded. In case of failure, it will be the start_idx.
- * Returns
- *   -1 on failure with errno set to match the 1st mount failure.
- *   0 on success.
- */
-static int mount_with_alternatives(fstab* fstab, int start_idx, int* end_idx, int* attempted_idx) {
+// Tries to mount any of the consecutive fstab entries that match
+// the mountpoint of the one given by fstab[start_idx].
+//
+// end_idx: On return, will be the last entry that was looked at.
+// attempted_idx: On return, will indicate which fstab entry
+//     succeeded. In case of failure, it will be the start_idx.
+// Sets errno to match the 1st mount failure on failure.
+static bool mount_with_alternatives(const Fstab& fstab, int start_idx, int* end_idx,
+                                    int* attempted_idx) {
     int i;
     int mount_errno = 0;
-    int mounted = 0;
+    bool mounted = false;
 
-    if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
-      errno = EINVAL;
-      if (end_idx) *end_idx = start_idx;
-      if (attempted_idx) *attempted_idx = start_idx;
-      return -1;
-    }
-
-    /* Hunt down an fstab entry for the same mount point that might succeed */
+    // Hunt down an fstab entry for the same mount point that might succeed.
     for (i = start_idx;
-         /* We required that fstab entries for the same mountpoint be consecutive */
-         i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point);
-         i++) {
-            /*
-             * Don't try to mount/encrypt the same mount point again.
-             * Deal with alternate entries for the same point which are required to be all following
-             * each other.
-             */
-            if (mounted) {
-                LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint="
-                       << fstab->recs[i].mount_point << " rec[" << i
-                       << "].fs_type=" << fstab->recs[i].fs_type
-                       << " already mounted as "
-                       << fstab->recs[*attempted_idx].fs_type;
-                continue;
-            }
+         // We required that fstab entries for the same mountpoint be consecutive.
+         i < fstab.size() && fstab[start_idx].mount_point == fstab[i].mount_point; i++) {
+        // Don't try to mount/encrypt the same mount point again.
+        // Deal with alternate entries for the same point which are required to be all following
+        // each other.
+        if (mounted) {
+            LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint=" << fstab[i].mount_point
+                   << " rec[" << i << "].fs_type=" << fstab[i].fs_type << " already mounted as "
+                   << fstab[*attempted_idx].fs_type;
+            continue;
+        }
 
-            int fs_stat = prepare_fs_for_mount(fstab->recs[i].blk_device, &fstab->recs[i]);
-            if (fs_stat & FS_STAT_INVALID_MAGIC) {
-                LERROR << __FUNCTION__ << "(): skipping mount due to invalid magic, mountpoint="
-                       << fstab->recs[i].mount_point
-                       << " blk_dev=" << realpath(fstab->recs[i].blk_device) << " rec[" << i
-                       << "].fs_type=" << fstab->recs[i].fs_type;
-                mount_errno = EINVAL;  // continue bootup for FDE
-                continue;
-            }
+        int fs_stat = prepare_fs_for_mount(fstab[i].blk_device, fstab[i]);
+        if (fs_stat & FS_STAT_INVALID_MAGIC) {
+            LERROR << __FUNCTION__
+                   << "(): skipping mount due to invalid magic, mountpoint=" << fstab[i].mount_point
+                   << " blk_dev=" << realpath(fstab[i].blk_device) << " rec[" << i
+                   << "].fs_type=" << fstab[i].fs_type;
+            mount_errno = EINVAL;  // continue bootup for FDE
+            continue;
+        }
 
-            int retry_count = 2;
-            while (retry_count-- > 0) {
-                if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point,
-                             &fstab->recs[i])) {
-                    *attempted_idx = i;
-                    mounted = 1;
-                    if (i != start_idx) {
-                        LERROR << __FUNCTION__ << "(): Mounted " << fstab->recs[i].blk_device
-                               << " on " << fstab->recs[i].mount_point
-                               << " with fs_type=" << fstab->recs[i].fs_type << " instead of "
-                               << fstab->recs[start_idx].fs_type;
-                    }
-                    fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
-                    mount_errno = 0;
-                    break;
-                } else {
-                    if (retry_count <= 0) break;  // run check_fs only once
-                    fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
-                    /* back up the first errno for crypto decisions */
-                    if (mount_errno == 0) {
-                        mount_errno = errno;
-                    }
-                    // retry after fsck
-                    check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
-                             fstab->recs[i].mount_point, &fs_stat);
+        int retry_count = 2;
+        while (retry_count-- > 0) {
+            if (!__mount(fstab[i].blk_device, fstab[i].mount_point, fstab[i])) {
+                *attempted_idx = i;
+                mounted = true;
+                if (i != start_idx) {
+                    LERROR << __FUNCTION__ << "(): Mounted " << fstab[i].blk_device << " on "
+                           << fstab[i].mount_point << " with fs_type=" << fstab[i].fs_type
+                           << " instead of " << fstab[start_idx].fs_type;
                 }
+                fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+                mount_errno = 0;
+                break;
+            } else {
+                if (retry_count <= 0) break;  // run check_fs only once
+                fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
+                // back up the first errno for crypto decisions.
+                if (mount_errno == 0) {
+                    mount_errno = errno;
+                }
+                // retry after fsck
+                check_fs(fstab[i].blk_device, fstab[i].fs_type, fstab[i].mount_point, &fs_stat);
             }
-            log_fs_stat(fstab->recs[i].blk_device, fs_stat);
+        }
+        log_fs_stat(fstab[i].blk_device, fs_stat);
     }
 
     /* Adjust i for the case where it was still withing the recs[] */
-    if (i < fstab->num_entries) --i;
+    if (i < fstab.size()) --i;
 
     *end_idx = i;
     if (!mounted) {
         *attempted_idx = start_idx;
         errno = mount_errno;
-        return -1;
+        return false;
     }
-    return 0;
+    return true;
 }
 
-static bool TranslateExtLabels(fstab_rec* rec) {
-    if (!StartsWith(rec->blk_device, "LABEL=")) {
+static bool TranslateExtLabels(FstabEntry* entry) {
+    if (!StartsWith(entry->blk_device, "LABEL=")) {
         return true;
     }
 
-    std::string label = rec->blk_device + 6;
+    std::string label = entry->blk_device.substr(6);
     if (label.size() > 16) {
         LERROR << "FS label is longer than allowed by filesystem";
         return false;
@@ -744,10 +727,9 @@
         if (label == super_block.s_volume_name) {
             std::string new_blk_device = "/dev/block/"s + ent->d_name;
 
-            LINFO << "resolved label " << rec->blk_device << " to " << new_blk_device;
+            LINFO << "resolved label " << entry->blk_device << " to " << new_blk_device;
 
-            free(rec->blk_device);
-            rec->blk_device = strdup(new_blk_device.c_str());
+            entry->blk_device = new_blk_device;
             return true;
         }
     }
@@ -755,54 +737,49 @@
     return false;
 }
 
-static bool needs_block_encryption(const struct fstab_rec* rec)
-{
-    if (android::base::GetBoolProperty("ro.vold.forceencryption", false) &&
-        fs_mgr_is_encryptable(rec))
+static bool needs_block_encryption(const FstabEntry& entry) {
+    if (android::base::GetBoolProperty("ro.vold.forceencryption", false) && entry.is_encryptable())
         return true;
-    if (rec->fs_mgr_flags & MF_FORCECRYPT) return true;
-    if (rec->fs_mgr_flags & MF_CRYPT) {
+    if (entry.fs_mgr_flags.force_crypt) return true;
+    if (entry.fs_mgr_flags.crypt) {
         // Check for existence of convert_fde breadcrumb file.
-        auto convert_fde_name = rec->mount_point + "/misc/vold/convert_fde"s;
+        auto convert_fde_name = entry.mount_point + "/misc/vold/convert_fde";
         if (access(convert_fde_name.c_str(), F_OK) == 0) return true;
     }
-    if (rec->fs_mgr_flags & MF_FORCEFDEORFBE) {
+    if (entry.fs_mgr_flags.force_fde_or_fbe) {
         // Check for absence of convert_fbe breadcrumb file.
-        auto convert_fbe_name = rec->mount_point + "/convert_fbe"s;
+        auto convert_fbe_name = entry.mount_point + "/convert_fbe";
         if (access(convert_fbe_name.c_str(), F_OK) != 0) return true;
     }
     return false;
 }
 
-static bool should_use_metadata_encryption(const struct fstab_rec* rec) {
-    if (!(rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE))) return false;
-    if (!(rec->fs_mgr_flags & MF_KEYDIRECTORY)) return false;
-    return true;
+static bool should_use_metadata_encryption(const FstabEntry& entry) {
+    return entry.fs_mgr_flags.key_directory &&
+           (entry.fs_mgr_flags.file_encryption || entry.fs_mgr_flags.force_fde_or_fbe);
 }
 
 // Check to see if a mountable volume has encryption requirements
-static int handle_encryptable(const struct fstab_rec* rec)
-{
-    /* If this is block encryptable, need to trigger encryption */
-    if (needs_block_encryption(rec)) {
-        if (umount(rec->mount_point) == 0) {
+static int handle_encryptable(const FstabEntry& entry) {
+    // If this is block encryptable, need to trigger encryption.
+    if (needs_block_encryption(entry)) {
+        if (umount(entry.mount_point.c_str()) == 0) {
             return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
         } else {
-            PWARNING << "Could not umount " << rec->mount_point
-                     << " - allow continue unencrypted";
+            PWARNING << "Could not umount " << entry.mount_point << " - allow continue unencrypted";
             return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
         }
-    } else if (should_use_metadata_encryption(rec)) {
-        if (umount(rec->mount_point) == 0) {
+    } else if (should_use_metadata_encryption(entry)) {
+        if (umount(entry.mount_point.c_str()) == 0) {
             return FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION;
         } else {
-            PERROR << "Could not umount " << rec->mount_point << " - fail since can't encrypt";
+            PERROR << "Could not umount " << entry.mount_point << " - fail since can't encrypt";
             return FS_MGR_MNTALL_FAIL;
         }
-    } else if (rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE)) {
-        LINFO << rec->mount_point << " is file encrypted";
+    } else if (entry.fs_mgr_flags.file_encryption || entry.fs_mgr_flags.force_fde_or_fbe) {
+        LINFO << entry.mount_point << " is file encrypted";
         return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;
-    } else if (fs_mgr_is_encryptable(rec)) {
+    } else if (entry.is_encryptable()) {
         return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
     } else {
         return FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
@@ -843,21 +820,34 @@
     return true;
 }
 
-bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
+bool fs_mgr_update_logical_partition(FstabEntry* entry) {
     // 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] == '/') {
+    if (entry->blk_device[0] == '/') {
         return true;
     }
 
     DeviceMapper& dm = DeviceMapper::Instance();
     std::string device_name;
-    if (!dm.GetDmDevicePathByName(rec->blk_device, &device_name)) {
+    if (!dm.GetDmDevicePathByName(entry->blk_device, &device_name)) {
         return false;
     }
+
+    entry->blk_device = device_name;
+    return true;
+}
+
+bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
+    auto entry = FstabRecToFstabEntry(rec);
+
+    if (!fs_mgr_update_logical_partition(&entry)) {
+        return false;
+    }
+
     free(rec->blk_device);
-    rec->blk_device = strdup(device_name.c_str());
+    rec->blk_device = strdup(entry.blk_device.c_str());
+
     return true;
 }
 
@@ -865,13 +855,13 @@
   public:
     CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
 
-    bool Update(struct fstab_rec* rec) {
-        if (!fs_mgr_is_checkpoint(rec)) {
+    bool Update(FstabEntry* entry) {
+        if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
             return true;
         }
 
-        if (fs_mgr_is_checkpoint_blk(rec)) {
-            call_vdc({"checkpoint", "restoreCheckpoint", rec->blk_device});
+        if (entry->fs_mgr_flags.checkpoint_blk) {
+            call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device});
         }
 
         if (needs_checkpoint_ == UNKNOWN &&
@@ -884,7 +874,7 @@
             return true;
         }
 
-        if (!UpdateCheckpointPartition(rec)) {
+        if (!UpdateCheckpointPartition(entry)) {
             LERROR << "Could not set up checkpoint partition, skipping!";
             return false;
         }
@@ -892,18 +882,17 @@
         return true;
     }
 
-    bool Revert(struct fstab_rec* rec) {
-        if (!fs_mgr_is_checkpoint(rec)) {
+    bool Revert(FstabEntry* entry) {
+        if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
             return true;
         }
 
-        if (device_map_.find(rec->blk_device) == device_map_.end()) {
+        if (device_map_.find(entry->blk_device) == device_map_.end()) {
             return true;
         }
 
-        std::string bow_device = rec->blk_device;
-        free(rec->blk_device);
-        rec->blk_device = strdup(device_map_[bow_device].c_str());
+        std::string bow_device = entry->blk_device;
+        entry->blk_device = device_map_[bow_device];
         device_map_.erase(bow_device);
 
         DeviceMapper& dm = DeviceMapper::Instance();
@@ -915,21 +904,17 @@
     }
 
   private:
-    bool UpdateCheckpointPartition(struct fstab_rec* rec) {
-        if (fs_mgr_is_checkpoint_fs(rec)) {
-            if (is_f2fs(rec->fs_type)) {
-                std::string opts(rec->fs_options);
-
-                opts += ",checkpoint=disable";
-                free(rec->fs_options);
-                rec->fs_options = strdup(opts.c_str());
+    bool UpdateCheckpointPartition(FstabEntry* entry) {
+        if (entry->fs_mgr_flags.checkpoint_fs) {
+            if (is_f2fs(entry->fs_type)) {
+                entry->fs_options += ",checkpoint=disable";
             } else {
-                LERROR << rec->fs_type << " does not implement checkpoints.";
+                LERROR << entry->fs_type << " does not implement checkpoints.";
             }
-        } else if (fs_mgr_is_checkpoint_blk(rec)) {
-            unique_fd fd(TEMP_FAILURE_RETRY(open(rec->blk_device, O_RDONLY | O_CLOEXEC)));
+        } else if (entry->fs_mgr_flags.checkpoint_blk) {
+            unique_fd fd(TEMP_FAILURE_RETRY(open(entry->blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
             if (fd < 0) {
-                PERROR << "Cannot open device " << rec->blk_device;
+                PERROR << "Cannot open device " << entry->blk_device;
                 return false;
             }
 
@@ -941,7 +926,7 @@
 
             android::dm::DmTable table;
             if (!table.AddTarget(
-                        std::make_unique<android::dm::DmTargetBow>(0, size, rec->blk_device))) {
+                        std::make_unique<android::dm::DmTargetBow>(0, size, entry->blk_device))) {
                 LERROR << "Failed to add bow target";
                 return false;
             }
@@ -958,9 +943,8 @@
                 return false;
             }
 
-            device_map_[name] = rec->blk_device;
-            free(rec->blk_device);
-            rec->blk_device = strdup(name.c_str());
+            device_map_[name] = entry->blk_device;
+            entry->blk_device = name;
         }
         return true;
     }
@@ -970,76 +954,70 @@
     std::map<std::string, std::string> device_map_;
 };
 
-/* 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.
- * Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
- */
-int fs_mgr_mount_all(fstab* fstab, int mount_mode) {
-    int i = 0;
+// 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.
+// Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
+int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
     int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
     int error_count = 0;
-    int mret = -1;
-    int mount_errno = 0;
-    int attempted_idx = -1;
     CheckpointManager checkpoint_manager;
     AvbUniquePtr avb_handle(nullptr);
 
-    if (!fstab) {
+    if (fstab->empty()) {
         return FS_MGR_MNTALL_FAIL;
     }
 
-    for (i = 0; i < fstab->num_entries; i++) {
-        /* Don't mount entries that are managed by vold or not for the mount mode*/
-        if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) ||
-            ((mount_mode == MOUNT_MODE_LATE) && !fs_mgr_is_latemount(&fstab->recs[i])) ||
-            ((mount_mode == MOUNT_MODE_EARLY) && fs_mgr_is_latemount(&fstab->recs[i])) ||
-            fs_mgr_is_first_stage_mount(&fstab->recs[i])) {
+    for (size_t i = 0; i < fstab->size(); i++) {
+        auto& current_entry = (*fstab)[i];
+
+        // Don't mount entries that are managed by vold or not for the mount mode.
+        if (current_entry.fs_mgr_flags.vold_managed || current_entry.fs_mgr_flags.recovery_only ||
+            current_entry.fs_mgr_flags.first_stage_mount ||
+            ((mount_mode == MOUNT_MODE_LATE) && !current_entry.fs_mgr_flags.late_mount) ||
+            ((mount_mode == MOUNT_MODE_EARLY) && current_entry.fs_mgr_flags.late_mount)) {
             continue;
         }
 
-        /* Skip swap and raw partition entries such as boot, recovery, etc */
-        if (!strcmp(fstab->recs[i].fs_type, "swap") ||
-            !strcmp(fstab->recs[i].fs_type, "emmc") ||
-            !strcmp(fstab->recs[i].fs_type, "mtd")) {
+        // Skip swap and raw partition entries such as boot, recovery, etc.
+        if (current_entry.fs_type == "swap" || current_entry.fs_type == "emmc" ||
+            current_entry.fs_type == "mtd") {
             continue;
         }
 
-        /* Skip mounting the root partition, as it will already have been mounted */
-        if (!strcmp(fstab->recs[i].mount_point, "/") ||
-            !strcmp(fstab->recs[i].mount_point, "/system")) {
-            if ((fstab->recs[i].flags & MS_RDONLY) != 0) {
-                fs_mgr_set_blk_ro(fstab->recs[i].blk_device);
+        // Skip mounting the root partition, as it will already have been mounted.
+        if (current_entry.mount_point == "/" || current_entry.mount_point == "/system") {
+            if ((current_entry.flags & MS_RDONLY) != 0) {
+                fs_mgr_set_blk_ro(current_entry.blk_device);
             }
             continue;
         }
 
-        /* Translate LABEL= file system labels into block devices */
-        if (is_extfs(fstab->recs[i].fs_type)) {
-            if (!TranslateExtLabels(&fstab->recs[i])) {
+        // Translate LABEL= file system labels into block devices.
+        if (is_extfs(current_entry.fs_type)) {
+            if (!TranslateExtLabels(&current_entry)) {
                 LERROR << "Could not translate label to block device";
                 continue;
             }
         }
 
-        if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
-            if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+        if (current_entry.fs_mgr_flags.logical) {
+            if (!fs_mgr_update_logical_partition(&current_entry)) {
                 LERROR << "Could not set up logical partition, skipping!";
                 continue;
             }
         }
 
-        if (!checkpoint_manager.Update(&fstab->recs[i])) {
+        if (!checkpoint_manager.Update(&current_entry)) {
             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";
+        if (current_entry.fs_mgr_flags.wait &&
+            !fs_mgr_wait_for_file(current_entry.blk_device, 20s)) {
+            LERROR << "Skipping '" << current_entry.blk_device << "' during mount_all";
             continue;
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
+        if (current_entry.fs_mgr_flags.avb) {
             if (!avb_handle) {
                 avb_handle = AvbHandle::Open();
                 if (!avb_handle) {
@@ -1047,15 +1025,15 @@
                     return FS_MGR_MNTALL_FAIL;
                 }
             }
-            if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
+            if (avb_handle->SetUpAvbHashtree(&current_entry, true /* wait_for_verity_dev */) ==
                 AvbHashtreeResult::kFail) {
-                LERROR << "Failed to set up AVB on partition: "
-                       << fstab->recs[i].mount_point << ", skipping!";
-                /* Skips mounting the device. */
+                LERROR << "Failed to set up AVB on partition: " << current_entry.mount_point
+                       << ", skipping!";
+                // Skips mounting the device.
                 continue;
             }
-        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
-            int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
+        } else if ((current_entry.fs_mgr_flags.verify)) {
+            int rc = fs_mgr_setup_verity(&current_entry, true);
             if (__android_log_is_debuggable() &&
                     (rc == FS_MGR_SETUP_VERITY_DISABLED ||
                      rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
@@ -1068,17 +1046,19 @@
 
         int last_idx_inspected;
         int top_idx = i;
+        int attempted_idx = -1;
 
-        mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);
+        bool mret = mount_with_alternatives(*fstab, i, &last_idx_inspected, &attempted_idx);
+        auto& attempted_entry = (*fstab)[attempted_idx];
         i = last_idx_inspected;
-        mount_errno = errno;
+        int mount_errno = errno;
 
-        /* Deal with encryptability. */
-        if (!mret) {
-            int status = handle_encryptable(&fstab->recs[attempted_idx]);
+        // Handle success and deal with encryptability.
+        if (mret) {
+            int status = handle_encryptable(attempted_entry);
 
             if (status == FS_MGR_MNTALL_FAIL) {
-                /* Fatal error - no point continuing */
+                // Fatal error - no point continuing.
                 return status;
             }
 
@@ -1089,50 +1069,45 @@
                 }
                 encryptable = status;
                 if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
-                    if (!call_vdc(
-                            {"cryptfs", "encryptFstab", fstab->recs[attempted_idx].mount_point})) {
+                    if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.mount_point})) {
                         LERROR << "Encryption failed";
                         return FS_MGR_MNTALL_FAIL;
                     }
                 }
             }
 
-            /* Success!  Go get the next one */
+            // Success!  Go get the next one.
             continue;
         }
 
-        bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
+        // Mounting failed, understand why and retry.
+        bool wiped = partition_wiped(current_entry.blk_device.c_str());
         bool crypt_footer = false;
-        if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
-            fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) {
-            /* top_idx and attempted_idx point at the same partition, but sometimes
-             * at two different lines in the fstab.  Use the top one for formatting
-             * as that is the preferred one.
-             */
-            LERROR << __FUNCTION__ << "(): " << realpath(fstab->recs[top_idx].blk_device)
-                   << " is wiped and " << fstab->recs[top_idx].mount_point << " "
-                   << fstab->recs[top_idx].fs_type << " is formattable. Format it.";
+        if (mount_errno != EBUSY && mount_errno != EACCES &&
+            current_entry.fs_mgr_flags.formattable && wiped) {
+            // current_entry and attempted_entry point at the same partition, but sometimes
+            // at two different lines in the fstab.  Use current_entry for formatting
+            // as that is the preferred one.
+            LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
+                   << " is wiped and " << current_entry.mount_point << " " << current_entry.fs_type
+                   << " is formattable. Format it.";
 
-            checkpoint_manager.Revert(&fstab->recs[top_idx]);
+            checkpoint_manager.Revert(&current_entry);
 
-            if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
-                strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
+            if (current_entry.is_encryptable() && current_entry.key_loc != KEY_IN_FOOTER) {
                 unique_fd fd(TEMP_FAILURE_RETRY(
-                        open(fstab->recs[top_idx].key_loc, O_WRONLY | O_CLOEXEC)));
+                        open(current_entry.key_loc.c_str(), O_WRONLY | O_CLOEXEC)));
                 if (fd >= 0) {
-                    LINFO << __FUNCTION__ << "(): also wipe "
-                          << fstab->recs[top_idx].key_loc;
+                    LINFO << __FUNCTION__ << "(): also wipe " << current_entry.key_loc;
                     wipe_block_device(fd, get_file_size(fd));
                 } else {
-                    PERROR << __FUNCTION__ << "(): "
-                           << fstab->recs[top_idx].key_loc << " wouldn't open";
+                    PERROR << __FUNCTION__ << "(): " << current_entry.key_loc << " wouldn't open";
                 }
-            } else if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
-                !strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
+            } else if (current_entry.is_encryptable() && current_entry.key_loc == KEY_IN_FOOTER) {
                 crypt_footer = true;
             }
-            if (fs_mgr_do_format(&fstab->recs[top_idx], crypt_footer) == 0) {
-                /* Let's replay the mount actions. */
+            if (fs_mgr_do_format(current_entry, crypt_footer) == 0) {
+                // Let's replay the mount actions.
                 i = top_idx - 1;
                 continue;
             } else {
@@ -1143,35 +1118,29 @@
             }
         }
 
-        /* mount(2) returned an error, handle the encryptable/formattable case */
-        if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
-            fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
+        // mount(2) returned an error, handle the encryptable/formattable case.
+        if (mount_errno != EBUSY && mount_errno != EACCES && attempted_entry.is_encryptable()) {
             if (wiped) {
-                LERROR << __FUNCTION__ << "(): "
-                       << fstab->recs[attempted_idx].blk_device
-                       << " is wiped and "
-                       << fstab->recs[attempted_idx].mount_point << " "
-                       << fstab->recs[attempted_idx].fs_type
+                LERROR << __FUNCTION__ << "(): " << attempted_entry.blk_device << " is wiped and "
+                       << attempted_entry.mount_point << " " << attempted_entry.fs_type
                        << " is encryptable. Suggest recovery...";
                 encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
                 continue;
             } else {
-                /* Need to mount a tmpfs at this mountpoint for now, and set
-                 * properties that vold will query later for decrypting
-                 */
+                // Need to mount a tmpfs at this mountpoint for now, and set
+                // properties that vold will query later for decrypting
                 LERROR << __FUNCTION__ << "(): possibly an encryptable blkdev "
-                       << fstab->recs[attempted_idx].blk_device
-                       << " for mount " << fstab->recs[attempted_idx].mount_point
-                       << " type " << fstab->recs[attempted_idx].fs_type;
-                if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
+                       << attempted_entry.blk_device << " for mount " << attempted_entry.mount_point
+                       << " type " << attempted_entry.fs_type;
+                if (fs_mgr_do_tmpfs_mount(attempted_entry.mount_point.c_str()) < 0) {
                     ++error_count;
                     continue;
                 }
             }
             encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
-        } else if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
-                   should_use_metadata_encryption(&fstab->recs[attempted_idx])) {
-            if (!call_vdc({"cryptfs", "mountFstab", fstab->recs[attempted_idx].mount_point})) {
+        } else if (mount_errno != EBUSY && mount_errno != EACCES &&
+                   should_use_metadata_encryption(attempted_entry)) {
+            if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.mount_point})) {
                 ++error_count;
             }
             encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
@@ -1179,18 +1148,18 @@
         } else {
             // fs_options might be null so we cannot use PERROR << directly.
             // Use StringPrintf to output "(null)" instead.
-            if (fs_mgr_is_nofail(&fstab->recs[attempted_idx])) {
+            if (attempted_entry.fs_mgr_flags.no_fail) {
                 PERROR << android::base::StringPrintf(
-                    "Ignoring failure to mount an un-encryptable or wiped "
-                    "partition on %s at %s options: %s",
-                    fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
-                    fstab->recs[attempted_idx].fs_options);
+                        "Ignoring failure to mount an un-encryptable or wiped "
+                        "partition on %s at %s options: %s",
+                        attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
+                        attempted_entry.fs_options.c_str());
             } else {
                 PERROR << android::base::StringPrintf(
-                    "Failed to mount an un-encryptable or wiped partition "
-                    "on %s at %s options: %s",
-                    fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
-                    fstab->recs[attempted_idx].fs_options);
+                        "Failed to mount an un-encryptable or wiped partition "
+                        "on %s at %s options: %s",
+                        attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
+                        attempted_entry.fs_options.c_str());
                 ++error_count;
             }
             continue;
@@ -1208,20 +1177,13 @@
     }
 }
 
-/* wrapper to __mount() and expects a fully prepared fstab_rec,
- * unlike fs_mgr_do_mount which does more things with avb / verity
- * etc.
- */
-int fs_mgr_do_mount_one(struct fstab_rec *rec)
-{
-    if (!rec) {
-        return FS_MGR_DOMNT_FAILED;
-    }
-
+// wrapper to __mount() and expects a fully prepared fstab_rec,
+// unlike fs_mgr_do_mount which does more things with avb / verity etc.
+int fs_mgr_do_mount_one(const FstabEntry& entry) {
     // Run fsck if needed
-    prepare_fs_for_mount(rec->blk_device, rec);
+    prepare_fs_for_mount(entry.blk_device, entry);
 
-    int ret = __mount(rec->blk_device, rec->mount_point, rec);
+    int ret = __mount(entry.blk_device, entry.mount_point, entry);
     if (ret) {
       ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
     }
@@ -1229,17 +1191,26 @@
     return ret;
 }
 
-/* If tmp_mount_point is non-null, mount the filesystem there.  This is for the
- * tmp mount we do to check the user password
- * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
- * in turn, and stop on 1st success, or no more match.
- */
-static int fs_mgr_do_mount_helper(fstab* fstab, const char* n_name, char* n_blk_device,
-                                  char* tmp_mount_point, int needs_checkpoint) {
-    int i = 0;
+int fs_mgr_do_mount_one(struct fstab_rec* rec) {
+    if (!rec) {
+        return FS_MGR_DOMNT_FAILED;
+    }
+
+    auto entry = FstabRecToFstabEntry(rec);
+
+    return fs_mgr_do_mount_one(entry);
+}
+
+// If tmp_mount_point is non-null, mount the filesystem there.  This is for the
+// tmp mount we do to check the user password
+// If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
+// in turn, and stop on 1st success, or no more match.
+static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
+                                  const std::string& n_blk_device, const char* tmp_mount_point,
+                                  int needs_checkpoint) {
     int mount_errors = 0;
     int first_mount_errno = 0;
-    char* mount_point;
+    std::string mount_point;
     CheckpointManager checkpoint_manager(needs_checkpoint);
     AvbUniquePtr avb_handle(nullptr);
 
@@ -1247,42 +1218,41 @@
         return FS_MGR_DOMNT_FAILED;
     }
 
-    for (i = 0; i < fstab->num_entries; i++) {
-        if (!fs_match(fstab->recs[i].mount_point, n_name)) {
+    for (auto& fstab_entry : *fstab) {
+        if (!fs_match(fstab_entry.mount_point, n_name)) {
             continue;
         }
 
-        /* We found our match */
-        /* If this swap or a raw partition, report an error */
-        if (!strcmp(fstab->recs[i].fs_type, "swap") ||
-            !strcmp(fstab->recs[i].fs_type, "emmc") ||
-            !strcmp(fstab->recs[i].fs_type, "mtd")) {
-            LERROR << "Cannot mount filesystem of type "
-                   << fstab->recs[i].fs_type << " on " << n_blk_device;
+        // We found our match.
+        // If this swap or a raw partition, report an error.
+        if (fstab_entry.fs_type == "swap" || fstab_entry.fs_type == "emmc" ||
+            fstab_entry.fs_type == "mtd") {
+            LERROR << "Cannot mount filesystem of type " << fstab_entry.fs_type << " on "
+                   << n_blk_device;
             return FS_MGR_DOMNT_FAILED;
         }
 
-        if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
-            if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+        if (fstab_entry.fs_mgr_flags.logical) {
+            if (!fs_mgr_update_logical_partition(&fstab_entry)) {
                 LERROR << "Could not set up logical partition, skipping!";
                 continue;
             }
         }
 
-        if (!checkpoint_manager.Update(&fstab->recs[i])) {
+        if (!checkpoint_manager.Update(&fstab_entry)) {
             LERROR << "Could not set up checkpoint 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)) {
+        // First check the filesystem if requested.
+        if (fstab_entry.fs_mgr_flags.wait && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
             LERROR << "Skipping mounting '" << n_blk_device << "'";
             continue;
         }
 
-        int fs_stat = prepare_fs_for_mount(n_blk_device, &fstab->recs[i]);
+        int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry);
 
-        if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
+        if (fstab_entry.fs_mgr_flags.avb) {
             if (!avb_handle) {
                 avb_handle = AvbHandle::Open();
                 if (!avb_handle) {
@@ -1290,15 +1260,15 @@
                     return FS_MGR_DOMNT_FAILED;
                 }
             }
-            if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
+            if (avb_handle->SetUpAvbHashtree(&fstab_entry, true /* wait_for_verity_dev */) ==
                 AvbHashtreeResult::kFail) {
-                LERROR << "Failed to set up AVB on partition: "
-                       << fstab->recs[i].mount_point << ", skipping!";
-                /* Skips mounting the device. */
+                LERROR << "Failed to set up AVB on partition: " << fstab_entry.mount_point
+                       << ", skipping!";
+                // Skips mounting the device.
                 continue;
             }
-        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
-            int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
+        } else if (fstab_entry.fs_mgr_flags.verify) {
+            int rc = fs_mgr_setup_verity(&fstab_entry, true);
             if (__android_log_is_debuggable() &&
                     (rc == FS_MGR_SETUP_VERITY_DISABLED ||
                      rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
@@ -1309,15 +1279,15 @@
             }
         }
 
-        /* Now mount it where requested */
+        // Now mount it where requested */
         if (tmp_mount_point) {
             mount_point = tmp_mount_point;
         } else {
-            mount_point = fstab->recs[i].mount_point;
+            mount_point = fstab_entry.mount_point;
         }
         int retry_count = 2;
         while (retry_count-- > 0) {
-            if (!__mount(n_blk_device, mount_point, &fstab->recs[i])) {
+            if (!__mount(n_blk_device, mount_point, fstab_entry)) {
                 fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
                 return FS_MGR_DOMNT_SUCCESS;
             } else {
@@ -1326,10 +1296,10 @@
                 mount_errors++;
                 fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
                 // try again after fsck
-                check_fs(n_blk_device, fstab->recs[i].fs_type, fstab->recs[i].mount_point, &fs_stat);
+                check_fs(n_blk_device, fstab_entry.fs_type, fstab_entry.mount_point, &fs_stat);
             }
         }
-        log_fs_stat(fstab->recs[i].blk_device, fs_stat);
+        log_fs_stat(fstab_entry.blk_device, fs_stat);
     }
 
     // Reach here means the mount attempt fails.
@@ -1337,19 +1307,22 @@
         PERROR << "Cannot mount filesystem on " << n_blk_device << " at " << mount_point;
         if (first_mount_errno == EBUSY) return FS_MGR_DOMNT_BUSY;
     } else {
-        /* We didn't find a match, say so and return an error */
+        // We didn't find a match, say so and return an error.
         LERROR << "Cannot find mount point " << n_name << " in fstab";
     }
     return FS_MGR_DOMNT_FAILED;
 }
 
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
-    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1);
+    auto new_fstab = LegacyFstabToFstab(fstab);
+    return fs_mgr_do_mount_helper(&new_fstab, n_name, n_blk_device, tmp_mount_point, -1);
 }
 
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
                     bool needs_checkpoint) {
-    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint);
+    auto new_fstab = LegacyFstabToFstab(fstab);
+    return fs_mgr_do_mount_helper(&new_fstab, n_name, n_blk_device, tmp_mount_point,
+                                  needs_checkpoint);
 }
 
 /*
@@ -1492,23 +1465,22 @@
      * logging mode, in which case return that */
     *mode = VERITY_MODE_DEFAULT;
 
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    if (!fstab) {
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
         LERROR << "Failed to read default fstab";
         return false;
     }
 
-    for (int i = 0; i < fstab->num_entries; i++) {
-        if (fs_mgr_is_avb(&fstab->recs[i])) {
+    for (const auto& entry : fstab) {
+        if (entry.fs_mgr_flags.avb) {
             *mode = VERITY_MODE_RESTART;  // avb only supports restart mode.
             break;
-        } else if (!fs_mgr_is_verified(&fstab->recs[i])) {
+        } else if (!entry.fs_mgr_flags.verify) {
             continue;
         }
 
         int current;
-        if (load_verity_state(&fstab->recs[i], &current) < 0) {
+        if (load_verity_state(entry, &current) < 0) {
             continue;
         }
         if (current != VERITY_MODE_DEFAULT) {
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 737deca..1a0e7ab 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -120,20 +120,21 @@
                                    LOG_KLOG, true, nullptr, nullptr, 0);
 }
 
-int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
-{
-    int rc = -EINVAL;
+int fs_mgr_do_format(const FstabEntry& entry, bool crypt_footer) {
+    LERROR << __FUNCTION__ << ": Format " << entry.blk_device << " as '" << entry.fs_type << "'";
 
-    LERROR << __FUNCTION__ << ": Format " << fstab->blk_device
-           << " as '" << fstab->fs_type << "'";
-
-    if (!strncmp(fstab->fs_type, "f2fs", 4)) {
-        rc = format_f2fs(fstab->blk_device, fstab->length, crypt_footer);
-    } else if (!strncmp(fstab->fs_type, "ext4", 4)) {
-        rc = format_ext4(fstab->blk_device, fstab->mount_point, crypt_footer);
+    if (entry.fs_type == "f2fs") {
+        return format_f2fs(entry.blk_device, entry.length, crypt_footer);
+    } else if (entry.fs_type == "ext4") {
+        return format_ext4(entry.blk_device, entry.mount_point, crypt_footer);
     } else {
-        LERROR << "File system type '" << fstab->fs_type << "' is not supported";
+        LERROR << "File system type '" << entry.fs_type << "' is not supported";
+        return -EINVAL;
     }
+}
 
-    return rc;
+int fs_mgr_do_format(struct fstab_rec* rec, bool crypt_footer) {
+    auto entry = FstabRecToFstabEntry(rec);
+
+    return fs_mgr_do_format(entry, crypt_footer);
 }
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index d2d8dc1..ef043f9 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -70,19 +70,11 @@
 
 #if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
 
-bool fs_mgr_overlayfs_mount_all(fstab*) {
+bool fs_mgr_overlayfs_mount_all(Fstab*) {
     return false;
 }
 
-bool fs_mgr_overlayfs_mount_all(const std::vector<fstab_rec*>&) {
-    return false;
-}
-
-std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab*) {
-    return {};
-}
-
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const std::vector<fstab_rec*>&) {
+std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab*) {
     return {};
 }
 
@@ -131,28 +123,28 @@
 
 // At less than 1% free space return value of false,
 // means we will try to wrap with overlayfs.
-bool fs_mgr_filesystem_has_space(const char* mount_point) {
+bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
     // If we have access issues to find out space remaining, return true
     // to prevent us trying to override with overlayfs.
     struct statvfs vst;
-    if (statvfs(mount_point, &vst)) return true;
+    if (statvfs(mount_point.c_str(), &vst)) return true;
 
     static constexpr int kPercentThreshold = 1;  // 1%
 
     return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
 }
 
-bool fs_mgr_overlayfs_enabled(struct fstab_rec* fsrec) {
+bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {
     // readonly filesystem, can not be mount -o remount,rw
     // if squashfs or if free space is (near) zero making such a remount
     // virtually useless, or if there are shared blocks that prevent remount,rw
-    if (("squashfs"s == fsrec->fs_type) || !fs_mgr_filesystem_has_space(fsrec->mount_point)) {
+    if ("squashfs" == entry->fs_type || !fs_mgr_filesystem_has_space(entry->mount_point)) {
         return true;
     }
-    if (fs_mgr_is_logical(fsrec)) {
-        fs_mgr_update_logical_partition(fsrec);
+    if (entry->fs_mgr_flags.logical) {
+        fs_mgr_update_logical_partition(entry);
     }
-    return fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device);
+    return fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
 }
 
 bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
@@ -250,22 +242,16 @@
 }
 
 bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
-                                                               fs_mgr_free_fstab);
-    if (!fstab) return false;
+    Fstab fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+        return false;
+    }
     const auto lowerdir = kLowerdirOption + mount_point;
-    for (auto i = 0; i < fstab->num_entries; ++i) {
-        const auto fsrec = &fstab->recs[i];
-        const auto fs_type = fsrec->fs_type;
-        if (!fs_type) continue;
-        if (overlay_only && ("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
-        auto fsrec_mount_point = fsrec->mount_point;
-        if (!fsrec_mount_point) continue;
-        if (mount_point != fsrec_mount_point) continue;
+    for (const auto& entry : fstab) {
+        if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
+        if (mount_point != entry.mount_point) continue;
         if (!overlay_only) return true;
-        const auto fs_options = fsrec->fs_options;
-        if (!fs_options) continue;
-        const auto options = android::base::Split(fs_options, ",");
+        const auto options = android::base::Split(entry.fs_options, ",");
         for (const auto& opt : options) {
             if (opt == lowerdir) {
                 return true;
@@ -282,28 +268,20 @@
     return ret;
 }
 
-bool fs_mgr_wants_overlayfs(fstab_rec* fsrec) {
-    if (!fsrec) return false;
-
-    auto fsrec_mount_point = fsrec->mount_point;
-    if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
-    if (!fsrec->blk_device) return false;
-
-    if (!fsrec->fs_type) return false;
-
+bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
     // Don't check entries that are managed by vold.
-    if (fsrec->fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) return false;
+    if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
 
     // Only concerned with readonly partitions.
-    if (!(fsrec->flags & MS_RDONLY)) return false;
+    if (!(entry->flags & MS_RDONLY)) return false;
 
     // If unbindable, do not allow overlayfs as this could expose us to
     // security issues.  On Android, this could also be used to turn off
     // the ability to overlay an otherwise acceptable filesystem since
     // /system and /vendor are never bound(sic) to.
-    if (fsrec->flags & MS_UNBINDABLE) return false;
+    if (entry->flags & MS_UNBINDABLE) return false;
 
-    if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
+    if (!fs_mgr_overlayfs_enabled(entry)) return false;
 
     return true;
 }
@@ -391,11 +369,11 @@
     return kPhysicalDevice + fs_mgr_get_super_partition_name(slot_number);
 }
 
-bool fs_mgr_overlayfs_has_logical(const fstab* fstab) {
-    if (!fstab) return false;
-    for (auto i = 0; i < fstab->num_entries; i++) {
-        const auto fsrec = &fstab->recs[i];
-        if (fs_mgr_is_logical(fsrec)) return true;
+bool fs_mgr_overlayfs_has_logical(const Fstab& fstab) {
+    for (const auto& entry : fstab) {
+        if (entry.fs_mgr_flags.logical) {
+            return true;
+        }
     }
     return false;
 }
@@ -521,7 +499,7 @@
     // hijack __mount() report format to help triage
     auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
     const auto opt_list = android::base::Split(options, ",");
-    for (const auto opt : opt_list) {
+    for (const auto& opt : opt_list) {
         if (android::base::StartsWith(opt, kUpperdirOption)) {
             report = report + "," + opt;
             break;
@@ -540,15 +518,12 @@
     }
 }
 
-std::vector<std::string> fs_mgr_candidate_list(fstab* fstab, const char* mount_point = nullptr) {
+std::vector<std::string> fs_mgr_candidate_list(Fstab* fstab, const char* mount_point = nullptr) {
     std::vector<std::string> mounts;
-    if (!fstab) return mounts;
-
     auto verity = fs_mgr_overlayfs_verity_enabled_list();
-    for (auto i = 0; i < fstab->num_entries; i++) {
-        const auto fsrec = &fstab->recs[i];
-        if (!fs_mgr_wants_overlayfs(fsrec)) continue;
-        std::string new_mount_point(fs_mgr_mount_point(fsrec->mount_point));
+    for (auto& entry : *fstab) {
+        if (!fs_mgr_wants_overlayfs(&entry)) continue;
+        std::string new_mount_point(fs_mgr_mount_point(entry.mount_point.c_str()));
         if (mount_point && (new_mount_point != mount_point)) continue;
         if (std::find(verity.begin(), verity.end(), android::base::Basename(new_mount_point)) !=
             verity.end()) {
@@ -580,10 +555,9 @@
     if (std::find(verity.begin(), verity.end(), "system") != verity.end()) return mounts;
 
     // confirm that fstab is missing system
-    if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/")) {
-        return mounts;
-    }
-    if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/system")) {
+    if (std::find_if(fstab->begin(), fstab->end(), [](const auto& entry) {
+            return entry.mount_point == "/" || entry.mount_point == "/system ";
+        }) != fstab->end()) {
         return mounts;
     }
 
@@ -605,26 +579,20 @@
         PERROR << "create " << kScratchMountPoint;
     }
 
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> local_fstab(
-            static_cast<fstab*>(calloc(1, sizeof(fstab))), fs_mgr_free_fstab);
-    auto fsrec = static_cast<fstab_rec*>(calloc(1, sizeof(fstab_rec)));
-    local_fstab->num_entries = 1;
-    local_fstab->recs = fsrec;
-    fsrec->blk_device = strdup(device_path.c_str());
-    fsrec->mount_point = strdup(kScratchMountPoint.c_str());
-    fsrec->fs_type = strdup(mnt_type.c_str());
-    fsrec->flags = MS_RELATIME;
-    fsrec->fs_options = strdup("");
+    FstabEntry entry;
+    entry.blk_device = device_path;
+    entry.mount_point = kScratchMountPoint;
+    entry.fs_type = mnt_type;
+    entry.flags = MS_RELATIME;
     auto save_errno = errno;
-    auto mounted = fs_mgr_do_mount_one(fsrec) == 0;
+    auto mounted = fs_mgr_do_mount_one(entry) == 0;
     if (!mounted) {
-        free(fsrec->fs_type);
         if (mnt_type == "f2fs") {
-            fsrec->fs_type = strdup("ext4");
+            entry.fs_type = "ext4";
         } else {
-            fsrec->fs_type = strdup("f2fs");
+            entry.fs_type = "f2fs";
         }
-        mounted = fs_mgr_do_mount_one(fsrec) == 0;
+        mounted = fs_mgr_do_mount_one(entry) == 0;
         if (!mounted) save_errno = errno;
     }
     setfscreatecon(nullptr);
@@ -681,7 +649,7 @@
     return true;
 }
 
-bool fs_mgr_overlayfs_create_scratch(const fstab* fstab, std::string* scratch_device,
+bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
                                      bool* partition_exists, bool* change) {
     *scratch_device = fs_mgr_overlayfs_scratch_device();
     *partition_exists = fs_mgr_rw_access(*scratch_device);
@@ -765,7 +733,7 @@
 }
 
 // Create and mount kScratchMountPoint storage if we have logical partitions
-bool fs_mgr_overlayfs_setup_scratch(const fstab* fstab, bool* change) {
+bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab, bool* change) {
     if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
 
     std::string scratch_device;
@@ -805,20 +773,20 @@
     return builder->FindPartition(android::base::Basename(kScratchMountPoint)) != nullptr;
 }
 
-bool fs_mgr_overlayfs_invalid(const fstab* fstab) {
+bool fs_mgr_overlayfs_invalid() {
     if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
 
     // in recovery or fastbootd mode, not allowed!
     if (fs_mgr_access("/system/bin/recovery")) return true;
 
-    return !fstab;
+    return false;
 }
 
 }  // namespace
 
-bool fs_mgr_overlayfs_mount_all(fstab* fstab) {
+bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
     auto ret = false;
-    if (fs_mgr_overlayfs_invalid(fstab)) return ret;
+    if (fs_mgr_overlayfs_invalid()) return ret;
 
     auto scratch_can_be_mounted = true;
     for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
@@ -839,17 +807,12 @@
     return ret;
 }
 
-bool fs_mgr_overlayfs_mount_all(const std::vector<fstab_rec*>& fsrecs) {
-    std::vector<fstab_rec> recs;
-    for (const auto& rec : fsrecs) recs.push_back(*rec);
-    fstab fstab = {static_cast<int>(fsrecs.size()), &recs[0]};
-    return fs_mgr_overlayfs_mount_all(&fstab);
-}
+std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab* fstab) {
+    if (fs_mgr_overlayfs_invalid()) return {};
 
-std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab) {
-    if (fs_mgr_overlayfs_invalid(fstab)) return {};
-
-    if (fs_mgr_get_entry_for_mount_point(fstab, kScratchMountPoint)) {
+    if (std::find_if(fstab->begin(), fstab->end(), [](const auto& entry) {
+            return entry.mount_point == kScratchMountPoint;
+        }) != fstab->end()) {
         return {};
     }
 
@@ -862,13 +825,6 @@
     return {};
 }
 
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const std::vector<fstab_rec*>& fsrecs) {
-    std::vector<fstab_rec> recs;
-    for (const auto& rec : fsrecs) recs.push_back(*rec);
-    fstab fstab = {static_cast<int>(fsrecs.size()), &recs[0]};
-    return fs_mgr_overlayfs_required_devices(&fstab);
-}
-
 // Returns false if setup not permitted, errno set to last error.
 // If something is altered, set *change.
 bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change) {
@@ -881,10 +837,11 @@
         return ret;
     }
 
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    if (!fstab) return ret;
-    auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(mount_point));
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return false;
+    }
+    auto mounts = fs_mgr_candidate_list(&fstab, fs_mgr_mount_point(mount_point));
     if (mounts.empty()) return ret;
 
     std::string dir;
@@ -892,12 +849,16 @@
         if (backing && backing[0] && (overlay_mount_point != backing)) continue;
         if (overlay_mount_point == kScratchMountPoint) {
             if (!fs_mgr_rw_access(fs_mgr_overlayfs_super_device(fs_mgr_overlayfs_slot_number())) ||
-                !fs_mgr_overlayfs_has_logical(fstab.get())) {
+                !fs_mgr_overlayfs_has_logical(fstab)) {
                 continue;
             }
-            if (!fs_mgr_overlayfs_setup_scratch(fstab.get(), change)) continue;
+            if (!fs_mgr_overlayfs_setup_scratch(fstab, change)) continue;
         } else {
-            if (!fs_mgr_get_entry_for_mount_point(fstab.get(), overlay_mount_point)) continue;
+            if (std::find_if(fstab.begin(), fstab.end(), [&overlay_mount_point](const auto& entry) {
+                    return entry.mount_point == overlay_mount_point;
+                }) == fstab.end()) {
+                continue;
+            }
         }
         dir = overlay_mount_point;
         break;
@@ -959,10 +920,12 @@
 
 bool fs_mgr_overlayfs_is_setup() {
     if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    if (fs_mgr_overlayfs_invalid(fstab.get())) return false;
-    for (const auto& mount_point : fs_mgr_candidate_list(fstab.get())) {
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return false;
+    }
+    if (fs_mgr_overlayfs_invalid()) return false;
+    for (const auto& mount_point : fs_mgr_candidate_list(&fstab)) {
         if (fs_mgr_overlayfs_already_mounted(mount_point)) return true;
     }
     return false;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index faef34b..072da97 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -137,6 +137,6 @@
 bool fs_mgr_is_device_unlocked();
 const std::string& get_android_dt_dir();
 bool is_dt_compatible();
-int load_verity_state(fstab_rec* fstab, int* mode);
+int load_verity_state(const FstabEntry& entry, int* mode);
 
 #endif /* __CORE_FS_MGR_PRIV_H */
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 2727a6d..9adf8cc 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -536,8 +536,7 @@
     return 0;
 }
 
-static int compare_last_signature(struct fstab_rec *fstab, int *match)
-{
+static int compare_last_signature(const FstabEntry& entry, int* match) {
     char tag[METADATA_TAG_MAX_LENGTH + 1];
     int fd = -1;
     int rc = -1;
@@ -549,42 +548,40 @@
 
     *match = 1;
 
-    if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
-            FEC_DEFAULT_ROOTS) == -1) {
-        PERROR << "Failed to open '" << fstab->blk_device << "'";
+    if (fec_open(&f, entry.blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) ==
+        -1) {
+        PERROR << "Failed to open '" << entry.blk_device << "'";
         return rc;
     }
 
     // read verity metadata
     if (fec_verity_get_metadata(f, &verity) == -1) {
-        PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
+        PERROR << "Failed to get verity metadata '" << entry.blk_device << "'";
         goto out;
     }
 
     SHA256(verity.signature, sizeof(verity.signature), curr);
 
-    if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
-            basename(fstab->mount_point)) >= (int)sizeof(tag)) {
-        LERROR << "Metadata tag name too long for " << fstab->mount_point;
+    if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s", basename(entry.mount_point.c_str())) >=
+        (int)sizeof(tag)) {
+        LERROR << "Metadata tag name too long for " << entry.mount_point;
         goto out;
     }
 
-    if (metadata_find(fstab->verity_loc, tag, SHA256_DIGEST_LENGTH,
-            &offset) < 0) {
+    if (metadata_find(entry.verity_loc.c_str(), tag, SHA256_DIGEST_LENGTH, &offset) < 0) {
         goto out;
     }
 
-    fd = TEMP_FAILURE_RETRY(open(fstab->verity_loc, O_RDWR | O_SYNC | O_CLOEXEC));
+    fd = TEMP_FAILURE_RETRY(open(entry.verity_loc.c_str(), O_RDWR | O_SYNC | O_CLOEXEC));
 
     if (fd == -1) {
-        PERROR << "Failed to open " << fstab->verity_loc;
+        PERROR << "Failed to open " << entry.verity_loc;
         goto out;
     }
 
-    if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev),
-            offset)) != sizeof(prev)) {
-        PERROR << "Failed to read " << sizeof(prev) << " bytes from "
-               << fstab->verity_loc << " offset " << offset;
+    if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev), offset)) != sizeof(prev)) {
+        PERROR << "Failed to read " << sizeof(prev) << " bytes from " << entry.verity_loc
+               << " offset " << offset;
         goto out;
     }
 
@@ -594,8 +591,8 @@
         /* update current signature hash */
         if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
                 offset)) != sizeof(curr)) {
-            PERROR << "Failed to write " << sizeof(curr) << " bytes to "
-                   << fstab->verity_loc << " offset " << offset;
+            PERROR << "Failed to write " << sizeof(curr) << " bytes to " << entry.verity_loc
+                   << " offset " << offset;
             goto out;
         }
     }
@@ -607,28 +604,23 @@
     return rc;
 }
 
-static int get_verity_state_offset(struct fstab_rec *fstab, off64_t *offset)
-{
+static int get_verity_state_offset(const FstabEntry& entry, off64_t* offset) {
     char tag[METADATA_TAG_MAX_LENGTH + 1];
 
-    if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s",
-            basename(fstab->mount_point)) >= (int)sizeof(tag)) {
-        LERROR << "Metadata tag name too long for " << fstab->mount_point;
+    if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s", basename(entry.mount_point.c_str())) >=
+        (int)sizeof(tag)) {
+        LERROR << "Metadata tag name too long for " << entry.mount_point;
         return -1;
     }
 
-    return metadata_find(fstab->verity_loc, tag, sizeof(struct verity_state),
-                offset);
+    return metadata_find(entry.verity_loc.c_str(), tag, sizeof(struct verity_state), offset);
 }
 
-int load_verity_state(struct fstab_rec* fstab, int* mode) {
-    int match = 0;
-    off64_t offset = 0;
-
-    /* unless otherwise specified, use EIO mode */
+int load_verity_state(const FstabEntry& entry, int* mode) {
+    // unless otherwise specified, use EIO mode.
     *mode = VERITY_MODE_EIO;
 
-    /* use the kernel parameter if set */
+    // use the kernel parameter if set.
     std::string veritymode;
     if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
         if (veritymode == "enforcing") {
@@ -637,7 +629,8 @@
         return 0;
     }
 
-    if (get_verity_state_offset(fstab, &offset) < 0) {
+    off64_t offset = 0;
+    if (get_verity_state_offset(entry, &offset) < 0) {
         /* fall back to stateless behavior */
         return 0;
     }
@@ -645,16 +638,17 @@
     if (was_verity_restart()) {
         /* device was restarted after dm-verity detected a corrupted
          * block, so use EIO mode */
-        return write_verity_state(fstab->verity_loc, offset, *mode);
+        return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
     }
 
-    if (!compare_last_signature(fstab, &match) && !match) {
+    int match = 0;
+    if (!compare_last_signature(entry, &match) && !match) {
         /* partition has been reflashed, reset dm-verity state */
         *mode = VERITY_MODE_DEFAULT;
-        return write_verity_state(fstab->verity_loc, offset, *mode);
+        return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
     }
 
-    return read_verity_state(fstab->verity_loc, offset, mode);
+    return read_verity_state(entry.verity_loc.c_str(), offset, mode);
 }
 
 // Update the verity table using the actual block device path.
@@ -716,8 +710,7 @@
 // prepares the verity enabled (MF_VERIFY / MF_VERIFYATBOOT) fstab record for
 // mount. The 'wait_for_verity_dev' parameter makes this function wait for the
 // verity device to get created before return
-int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev)
-{
+int fs_mgr_setup_verity(FstabEntry* entry, bool wait_for_verity_dev) {
     int retval = FS_MGR_SETUP_VERITY_FAIL;
     int fd = -1;
     std::string verity_blk_name;
@@ -725,20 +718,20 @@
     struct fec_verity_metadata verity;
     struct verity_table_params params = { .table = NULL };
 
-    const std::string mount_point(basename(fstab->mount_point));
+    const std::string mount_point(basename(entry->mount_point.c_str()));
     bool verified_at_boot = false;
 
     android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
 
-    if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
-            FEC_DEFAULT_ROOTS) < 0) {
-        PERROR << "Failed to open '" << fstab->blk_device << "'";
+    if (fec_open(&f, entry->blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) <
+        0) {
+        PERROR << "Failed to open '" << entry->blk_device << "'";
         return retval;
     }
 
     // read verity metadata
     if (fec_verity_get_metadata(f, &verity) < 0) {
-        PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
+        PERROR << "Failed to get verity metadata '" << entry->blk_device << "'";
         // Allow verity disabled when the device is unlocked without metadata
         if (fs_mgr_is_device_unlocked()) {
             retval = FS_MGR_SETUP_VERITY_SKIPPED;
@@ -760,9 +753,9 @@
         params.ecc.valid = false;
     }
 
-    params.ecc_dev = fstab->blk_device;
+    params.ecc_dev = entry->blk_device.c_str();
 
-    if (load_verity_state(fstab, &params.mode) < 0) {
+    if (load_verity_state(*entry, &params.mode) < 0) {
         /* if accessing or updating the state failed, switch to the default
          * safe mode. This makes sure the device won't end up in an endless
          * restart loop, and no corrupted data will be exposed to userspace
@@ -803,8 +796,8 @@
           << " (mode " << params.mode << ")";
 
     // Update the verity params using the actual block device path
-    update_verity_table_blk_device(fstab->blk_device, &params.table,
-                                   fstab->fs_mgr_flags & MF_SLOTSELECT);
+    update_verity_table_blk_device(entry->blk_device, &params.table,
+                                   entry->fs_mgr_flags.slot_select);
 
     // load the verity mapping table
     if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
@@ -848,31 +841,29 @@
     }
 
     // mark the underlying block device as read-only
-    fs_mgr_set_blk_ro(fstab->blk_device);
+    fs_mgr_set_blk_ro(entry->blk_device);
 
     // Verify the entire partition in one go
     // If there is an error, allow it to mount as a normal verity partition.
-    if (fstab->fs_mgr_flags & MF_VERIFYATBOOT) {
-        LINFO << "Verifying partition " << fstab->blk_device << " at boot";
+    if (entry->fs_mgr_flags.verify_at_boot) {
+        LINFO << "Verifying partition " << entry->blk_device << " at boot";
         int err = read_partition(verity_blk_name.c_str(), verity.data_size);
         if (!err) {
-            LINFO << "Verified verity partition "
-                  << fstab->blk_device << " at boot";
+            LINFO << "Verified verity partition " << entry->blk_device << " at boot";
             verified_at_boot = true;
         }
     }
 
     // assign the new verity block device as the block device
     if (!verified_at_boot) {
-        free(fstab->blk_device);
-        fstab->blk_device = strdup(verity_blk_name.c_str());
+        entry->blk_device = verity_blk_name;
     } else if (!dm.DeleteDevice(mount_point)) {
         LERROR << "Failed to remove verity device " << mount_point.c_str();
         goto out;
     }
 
     // make sure we've set everything up properly
-    if (wait_for_verity_dev && !fs_mgr_wait_for_file(fstab->blk_device, 1s)) {
+    if (wait_for_verity_dev && !fs_mgr_wait_for_file(entry->blk_device, 1s)) {
         goto out;
     }
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index c1dc731..814ba46 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -59,7 +59,8 @@
 #define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 1
 #define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
 #define FS_MGR_MNTALL_FAIL (-1)
-int fs_mgr_mount_all(fstab* fstab, int mount_mode);
+// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
+int fs_mgr_mount_all(Fstab* fstab, int mount_mode);
 
 #define FS_MGR_DOMNT_FAILED (-1)
 #define FS_MGR_DOMNT_BUSY (-2)
@@ -68,6 +69,7 @@
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point);
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
                     bool need_cp);
+int fs_mgr_do_mount_one(const FstabEntry& entry);
 int fs_mgr_do_mount_one(fstab_rec* rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
 fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab);
@@ -76,15 +78,17 @@
 bool fs_mgr_update_verity_state(
         std::function<void(const std::string& mount_point, int mode)> callback);
 bool fs_mgr_swapon_all(const Fstab& fstab);
+bool fs_mgr_update_logical_partition(FstabEntry* entry);
 bool fs_mgr_update_logical_partition(struct fstab_rec* rec);
 
-int fs_mgr_do_format(fstab_rec* fstab, bool reserve_footer);
+int fs_mgr_do_format(const FstabEntry& entry, bool reserve_footer);
+int fs_mgr_do_format(fstab_rec* rec, bool reserve_footer);
 
 #define FS_MGR_SETUP_VERITY_SKIPPED  (-3)
 #define FS_MGR_SETUP_VERITY_DISABLED (-2)
 #define FS_MGR_SETUP_VERITY_FAIL (-1)
 #define FS_MGR_SETUP_VERITY_SUCCESS 0
-int fs_mgr_setup_verity(fstab_rec* fstab, bool wait_for_verity_dev);
+int fs_mgr_setup_verity(FstabEntry* fstab, bool wait_for_verity_dev);
 
 // Return the name of the super partition if it exists. If a slot number is
 // specified, the super partition for the corresponding metadata slot will be
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 0dd9121..4bf2238 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -21,10 +21,8 @@
 #include <string>
 #include <vector>
 
-bool fs_mgr_overlayfs_mount_all(fstab* fstab);
-bool fs_mgr_overlayfs_mount_all(const std::vector<fstab_rec*>& fstab);
-std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab);
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const std::vector<fstab_rec*>& fstab);
+bool fs_mgr_overlayfs_mount_all(Fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab* fstab);
 bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
                             bool* change = nullptr);
 bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 4706028..26a1e5c 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -156,6 +156,7 @@
             bool logical : 1;
             bool checkpoint_blk : 1;
             bool checkpoint_fs : 1;
+            bool first_stage_mount : 1;
         };
     } fs_mgr_flags;
 
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index c4accad..89c755e 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -265,7 +265,7 @@
     return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
 }
 
-static bool hashtree_dm_verity_setup(struct fstab_rec* fstab_entry,
+static bool hashtree_dm_verity_setup(FstabEntry* fstab_entry,
                                      const AvbHashtreeDescriptor& hashtree_desc,
                                      const std::string& salt, const std::string& root_digest,
                                      bool wait_for_verity_dev) {
@@ -278,7 +278,7 @@
     }
     table.set_readonly(true);
 
-    const std::string mount_point(basename(fstab_entry->mount_point));
+    const std::string mount_point(basename(fstab_entry->mount_point.c_str()));
     android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
     if (!dm.CreateDevice(mount_point, table)) {
         LERROR << "Couldn't create verity device!";
@@ -295,8 +295,7 @@
     fs_mgr_set_blk_ro(fstab_entry->blk_device);
 
     // Updates fstab_rec->blk_device to verity device name.
-    free(fstab_entry->blk_device);
-    fstab_entry->blk_device = strdup(dev_path.c_str());
+    fstab_entry->blk_device = dev_path;
 
     // Makes sure we've set everything up properly.
     if (wait_for_verity_dev && !fs_mgr_wait_for_file(dev_path, 1s)) {
@@ -449,8 +448,7 @@
     return avb_handle;
 }
 
-AvbHashtreeResult AvbHandle::SetUpAvbHashtree(struct fstab_rec* fstab_entry,
-                                              bool wait_for_verity_dev) {
+AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {
     if (!fstab_entry || status_ == kAvbHandleUninitialized || !avb_slot_data_ ||
         avb_slot_data_->num_vbmeta_images < 1) {
         return AvbHashtreeResult::kFail;
@@ -464,13 +462,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;
-    if (fstab_entry->fs_mgr_flags & MF_LOGICAL) {
+    if (fstab_entry->fs_mgr_flags.logical) {
         partition_name = fstab_entry->logical_partition_name;
     } else {
-        partition_name = basename(fstab_entry->blk_device);
+        partition_name = basename(fstab_entry->blk_device.c_str());
     }
 
-    if (fstab_entry->fs_mgr_flags & MF_SLOTSELECT) {
+    if (fstab_entry->fs_mgr_flags.slot_select) {
         auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix());
         if (ab_suffix != std::string::npos) {
             partition_name.erase(ab_suffix);
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index 9adab3d..08bdbdc 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -85,7 +85,7 @@
     //     failed to get the HASHTREE descriptor, runtime error when set up
     //     device-mapper, etc.
     //   - kDisabled: hashtree is disabled.
-    AvbHashtreeResult SetUpAvbHashtree(fstab_rec* fstab_entry, bool wait_for_verity_dev);
+    AvbHashtreeResult SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev);
 
     const std::string& avb_version() const { return avb_version_; }
 
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 2bebe76..7fd4e27 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -473,9 +473,10 @@
         // Only needed if someone explicitly changes the default log level in their init.rc.
         android::base::ScopedLogSeverity info(android::base::INFO);
 
-        struct fstab* fstab = fs_mgr_read_fstab(fstabfile);
-        int child_ret = fs_mgr_mount_all(fstab, mount_mode);
-        fs_mgr_free_fstab(fstab);
+        Fstab fstab;
+        ReadFstabFromFile(fstabfile, &fstab);
+
+        int child_ret = fs_mgr_mount_all(&fstab, mount_mode);
         if (child_ret == -1) {
             PLOG(ERROR) << "fs_mgr_mount_all returned an error";
         }
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 6ae1123..f5b291e 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -70,7 +70,7 @@
     bool InitRequiredDevices();
     bool InitMappedDevice(const std::string& verity_device);
     bool CreateLogicalPartitions();
-    bool MountPartition(fstab_rec* fstab_rec);
+    bool MountPartition(FstabEntry* fstab_entry);
     bool MountPartitions();
     bool IsDmLinearEnabled();
     bool GetDmLinearMetadataDevice();
@@ -80,13 +80,12 @@
 
     // Pure virtual functions.
     virtual bool GetDmVerityDevices() = 0;
-    virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
+    virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
 
     bool need_dm_verity_;
 
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab_;
+    Fstab fstab_;
     std::string lp_metadata_partition_;
-    std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
     std::string super_partition_name_;
     std::unique_ptr<DeviceHandler> device_handler_;
@@ -100,7 +99,7 @@
 
   protected:
     bool GetDmVerityDevices() override;
-    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+    bool SetUpDmVerity(FstabEntry* fstab_entry) override;
 };
 
 class FirstStageMountVBootV2 : public FirstStageMount {
@@ -112,7 +111,7 @@
 
   protected:
     bool GetDmVerityDevices() override;
-    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+    bool SetUpDmVerity(FstabEntry* fstab_entry) override;
     bool InitAvbHandle();
 
     std::string device_tree_vbmeta_parts_;
@@ -131,29 +130,16 @@
 
 // Class Definitions
 // -----------------
-FirstStageMount::FirstStageMount()
-    : need_dm_verity_(false),
-      fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab),
-      uevent_listener_(16 * 1024 * 1024) {
-    // Stores fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
-    // for easier manipulation later, e.g., range-base for loop.
-    if (fstab_) {
-        // DT Fstab predated having a first_stage_mount fs_mgr flag, so if it exists, we use it.
-        for (int i = 0; i < fstab_->num_entries; i++) {
-            mount_fstab_recs_.push_back(&fstab_->recs[i]);
-        }
-    } else {
-        // Fstab found in first stage ramdisk, which should be a copy of the normal fstab.
-        // Mounts intended for first stage are explicitly flagged as such.
-        fstab_.reset(fs_mgr_read_fstab_default());
-        if (fstab_) {
-            for (int i = 0; i < fstab_->num_entries; i++) {
-                if (fs_mgr_is_first_stage_mount(&fstab_->recs[i])) {
-                    mount_fstab_recs_.push_back(&fstab_->recs[i]);
-                }
-            }
+FirstStageMount::FirstStageMount() : need_dm_verity_(false), uevent_listener_(16 * 1024 * 1024) {
+    if (!ReadFstabFromDt(&fstab_)) {
+        if (ReadDefaultFstab(&fstab_)) {
+            fstab_.erase(std::remove_if(fstab_.begin(), fstab_.end(),
+                                        [](const auto& entry) {
+                                            return !entry.fs_mgr_flags.first_stage_mount;
+                                        }),
+                         fstab_.end());
         } else {
-            LOG(INFO) << "Failed to read fstab from device tree";
+            LOG(INFO) << "Failed to fstab for first stage mount";
         }
     }
 
@@ -174,7 +160,7 @@
 }
 
 bool FirstStageMount::DoFirstStageMount() {
-    if (!IsDmLinearEnabled() && mount_fstab_recs_.empty()) {
+    if (!IsDmLinearEnabled() && fstab_.empty()) {
         // Nothing to mount.
         LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
         return true;
@@ -194,8 +180,8 @@
 }
 
 bool FirstStageMount::IsDmLinearEnabled() {
-    for (auto fstab_rec : mount_fstab_recs_) {
-        if (fs_mgr_is_logical(fstab_rec)) return true;
+    for (const auto& entry : fstab_) {
+        if (entry.fs_mgr_flags.logical) return true;
     }
     return false;
 }
@@ -381,21 +367,21 @@
     return true;
 }
 
-bool FirstStageMount::MountPartition(fstab_rec* fstab_rec) {
-    if (fs_mgr_is_logical(fstab_rec)) {
-        if (!fs_mgr_update_logical_partition(fstab_rec)) {
+bool FirstStageMount::MountPartition(FstabEntry* fstab_entry) {
+    if (fstab_entry->fs_mgr_flags.logical) {
+        if (!fs_mgr_update_logical_partition(fstab_entry)) {
             return false;
         }
-        if (!InitMappedDevice(fstab_rec->blk_device)) {
+        if (!InitMappedDevice(fstab_entry->blk_device)) {
             return false;
         }
     }
-    if (!SetUpDmVerity(fstab_rec)) {
-        PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
+    if (!SetUpDmVerity(fstab_entry)) {
+        PLOG(ERROR) << "Failed to setup verity for '" << fstab_entry->mount_point << "'";
         return false;
     }
-    if (fs_mgr_do_mount_one(fstab_rec)) {
-        PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
+    if (fs_mgr_do_mount_one(*fstab_entry)) {
+        PLOG(ERROR) << "Failed to mount '" << fstab_entry->mount_point << "'";
         return false;
     }
     return true;
@@ -405,28 +391,28 @@
     // If system is in the fstab then we're not a system-as-root device, and in
     // this case, we mount system first then pivot to it.  From that point on,
     // we are effectively identical to a system-as-root device.
-    auto system_partition =
-            std::find_if(mount_fstab_recs_.begin(), mount_fstab_recs_.end(),
-                         [](const auto& rec) { return rec->mount_point == "/system"s; });
+    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/system";
+    });
 
-    if (system_partition != mount_fstab_recs_.end()) {
-        if (!MountPartition(*system_partition)) {
+    if (system_partition != fstab_.end()) {
+        if (!MountPartition(&(*system_partition))) {
             return false;
         }
 
-        SwitchRoot((*system_partition)->mount_point);
+        SwitchRoot((*system_partition).mount_point);
 
-        mount_fstab_recs_.erase(system_partition);
+        fstab_.erase(system_partition);
     }
 
-    for (auto fstab_rec : mount_fstab_recs_) {
-        if (!MountPartition(fstab_rec) && !fs_mgr_is_nofail(fstab_rec)) {
+    for (auto& fstab_entry : fstab_) {
+        if (!MountPartition(&fstab_entry) && !fstab_entry.fs_mgr_flags.no_fail) {
             return false;
         }
     }
 
     // heads up for instantiating required device(s) for overlayfs logic
-    const auto devices = fs_mgr_overlayfs_required_devices(mount_fstab_recs_);
+    const auto devices = fs_mgr_overlayfs_required_devices(&fstab_);
     for (auto const& device : devices) {
         if (android::base::StartsWith(device, "/dev/block/by-name/")) {
             required_devices_partition_names_.emplace(basename(device.c_str()));
@@ -438,7 +424,7 @@
         }
     }
 
-    fs_mgr_overlayfs_mount_all(mount_fstab_recs_);
+    fs_mgr_overlayfs_mount_all(&fstab_);
 
     return true;
 }
@@ -447,25 +433,25 @@
     std::string verity_loc_device;
     need_dm_verity_ = false;
 
-    for (auto fstab_rec : mount_fstab_recs_) {
+    for (const auto& fstab_entry : fstab_) {
         // Don't allow verifyatboot in the first stage.
-        if (fs_mgr_is_verifyatboot(fstab_rec)) {
+        if (fstab_entry.fs_mgr_flags.verify_at_boot) {
             LOG(ERROR) << "Partitions can't be verified at boot";
             return false;
         }
         // Checks for verified partitions.
-        if (fs_mgr_is_verified(fstab_rec)) {
+        if (fstab_entry.fs_mgr_flags.verify) {
             need_dm_verity_ = true;
         }
         // Checks if verity metadata is on a separate partition. Note that it is
         // not partition specific, so there must be only one additional partition
         // that carries verity state.
-        if (fstab_rec->verity_loc) {
+        if (!fstab_entry.verity_loc.empty()) {
             if (verity_loc_device.empty()) {
-                verity_loc_device = fstab_rec->verity_loc;
-            } else if (verity_loc_device != fstab_rec->verity_loc) {
+                verity_loc_device = fstab_entry.verity_loc;
+            } else if (verity_loc_device != fstab_entry.verity_loc) {
                 LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
-                           << fstab_rec->verity_loc;
+                           << fstab_entry.verity_loc;
                 return false;
             }
         }
@@ -473,9 +459,9 @@
 
     // Includes the partition names of fstab records and verity_loc_device (if any).
     // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
-    for (auto fstab_rec : mount_fstab_recs_) {
-        if (!fs_mgr_is_logical(fstab_rec)) {
-            required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+    for (const auto& fstab_entry : fstab_) {
+        if (!fstab_entry.fs_mgr_flags.logical) {
+            required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
         }
     }
 
@@ -486,19 +472,19 @@
     return true;
 }
 
-bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
-    if (fs_mgr_is_verified(fstab_rec)) {
-        int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
+bool FirstStageMountVBootV1::SetUpDmVerity(FstabEntry* fstab_entry) {
+    if (fstab_entry->fs_mgr_flags.verify) {
+        int ret = fs_mgr_setup_verity(fstab_entry, false /* wait_for_verity_dev */);
         switch (ret) {
             case FS_MGR_SETUP_VERITY_SKIPPED:
             case FS_MGR_SETUP_VERITY_DISABLED:
-                LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'";
+                LOG(INFO) << "Verity disabled/skipped for '" << fstab_entry->mount_point << "'";
                 return true;
             case FS_MGR_SETUP_VERITY_SUCCESS:
                 // 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 InitMappedDevice(fstab_rec->blk_device);
+                return InitMappedDevice(fstab_entry->blk_device);
             default:
                 return false;
         }
@@ -531,15 +517,15 @@
     std::set<std::string> logical_partitions;
 
     // fstab_rec->blk_device has A/B suffix.
-    for (auto fstab_rec : mount_fstab_recs_) {
-        if (fs_mgr_is_avb(fstab_rec)) {
+    for (const auto& fstab_entry : fstab_) {
+        if (fstab_entry.fs_mgr_flags.avb) {
             need_dm_verity_ = true;
         }
-        if (fs_mgr_is_logical(fstab_rec)) {
+        if (fstab_entry.fs_mgr_flags.logical) {
             // Don't try to find logical partitions via uevent regeneration.
-            logical_partitions.emplace(basename(fstab_rec->blk_device));
+            logical_partitions.emplace(basename(fstab_entry.blk_device.c_str()));
         } else {
-            required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+            required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
         }
     }
 
@@ -569,11 +555,11 @@
     return true;
 }
 
-bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
-    if (fs_mgr_is_avb(fstab_rec)) {
+bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) {
+    if (fstab_entry->fs_mgr_flags.avb) {
         if (!InitAvbHandle()) return false;
         AvbHashtreeResult hashtree_result =
-                avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
+                avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
         switch (hashtree_result) {
             case AvbHashtreeResult::kDisabled:
                 return true;  // Returns true to mount the partition.
@@ -581,7 +567,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 InitMappedDevice(fstab_rec->blk_device);
+                return InitMappedDevice(fstab_entry->blk_device);
             default:
                 return false;
         }
diff --git a/libcutils/include/cutils/partition_utils.h b/libcutils/include/cutils/partition_utils.h
index 7518559..8bc9b48 100644
--- a/libcutils/include/cutils/partition_utils.h
+++ b/libcutils/include/cutils/partition_utils.h
@@ -21,7 +21,7 @@
 
 __BEGIN_DECLS
 
-int partition_wiped(char *source);
+int partition_wiped(const char* source);
 
 __END_DECLS
 
diff --git a/libcutils/partition_utils.cpp b/libcutils/partition_utils.cpp
index 2211ff6..b840559 100644
--- a/libcutils/partition_utils.cpp
+++ b/libcutils/partition_utils.cpp
@@ -39,8 +39,7 @@
     return ret;
 }
 
-int partition_wiped(char *source)
-{
+int partition_wiped(const char* source) {
     uint8_t buf[4096];
     int fd, ret;
 
@@ -67,4 +66,3 @@
 
     return 0;
 }
-
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 0fa1638..4ba8a4b 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -260,7 +260,7 @@
   }
 
   // Log any of the expression data.
-  for (const auto line : expression_lines) {
+  for (const auto& line : expression_lines) {
     log(indent + 1, "%s", line.c_str());
   }
   return true;
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 640992f..c8a8ab5 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -224,7 +224,7 @@
     sp_map_start = map_info->start;
   }
 
-  for (auto frame : unwinder.frames()) {
+  for (const auto& frame : unwinder.frames()) {
     map_info = maps.Find(frame.sp);
     if (map_info != nullptr && sp_map_start != map_info->start) {
       stacks.emplace_back(std::make_pair(frame.sp, map_info->end));
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 427dace..969f26b 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -610,7 +610,7 @@
 
 std::string llkFormat(const std::unordered_set<std::string>& blacklist) {
     std::string ret;
-    for (auto entry : blacklist) {
+    for (const auto& entry : blacklist) {
         if (ret.size()) {
             ret += ",";
         }
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index d3e80c9..264c612 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -28,7 +28,7 @@
 dir.postinstall = /postinstall
 
 [system]
-additional.namespaces = sphal,vndk,rs
+additional.namespaces = runtime,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -105,6 +105,28 @@
 namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.asan.permitted.paths += /mnt/expand
 
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs  = libc.so:libdl.so:libm.so
+namespace.default.link.runtime.shared_libs += libart.so:libartd.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
 ###############################################################################
 # "sphal" namespace
 #
@@ -139,8 +161,12 @@
 
 # Once in this namespace, access to libraries in /system/lib is restricted. Only
 # libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+namespace.sphal.links = runtime,default,vndk,rs
 
+namespace.sphal.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+# LLNDK_LIBRARIES includes the runtime libs above, but the order here ensures
+# that they are loaded from the runtime namespace.
 namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
@@ -187,9 +213,11 @@
 namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
 namespace.rs.asan.permitted.paths += /data
 
-namespace.rs.links = default,vndk
+namespace.rs.links = runtime,default,vndk
 
-namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
+namespace.rs.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 # Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
 # namespace because RS framework libs are using them.
@@ -235,10 +263,13 @@
 namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
 namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%/hw
 
-# The "vndk" namespace links to "default" namespace for LLNDK libs and links to
-# "sphal" namespace for vendor libs.  The ordering matters.  The "default"
-# namespace has higher priority than the "sphal" namespace.
-namespace.vndk.links = default,sphal
+# The "vndk" namespace links to "runtime" for Bionic libs, "default" namespace
+# for LLNDK libs, and links to "sphal" namespace for vendor libs. The ordering
+# matters. The "default" namespace has higher priority than the "sphal"
+# namespace.
+namespace.vndk.links = runtime,default,sphal
+
+namespace.vndk.link.runtime.shared_libs = libc.so:libdl.so:libm.so
 
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
@@ -249,6 +280,7 @@
 # Allow VNDK-SP extensions to use vendor libraries
 namespace.vndk.link.sphal.allow_all_shared_libs = true
 
+
 ###############################################################################
 # Namespace config for vendor processes. In O, no restriction is enforced for
 # them. However, in O-MR1, access to /system/${LIB} will not be allowed to
@@ -256,7 +288,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-additional.namespaces = system,vndk
+additional.namespaces = runtime,system,vndk
 
 ###############################################################################
 # "default" namespace
@@ -287,12 +319,24 @@
 namespace.default.asan.permitted.paths += /data/asan/vendor
 namespace.default.asan.permitted.paths +=           /vendor
 
-namespace.default.links = system,vndk
+namespace.default.links = runtime,system,vndk
+namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
 namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
 namespace.default.link.vndk.shared_libs  = %VNDK_SAMEPROCESS_LIBRARIES%
 namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
 
 ###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
 # "vndk" namespace
 #
 # This namespace is where VNDK and VNDK-SP libraries are loaded for
@@ -323,7 +367,10 @@
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the system namespace. This is possible since their ABI is stable across
 # Android releases.
-namespace.vndk.links = system,default
+namespace.vndk.links = runtime,system,default
+
+namespace.vndk.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
 namespace.vndk.link.system.shared_libs  = %LLNDK_LIBRARIES%
 namespace.vndk.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
@@ -348,16 +395,36 @@
 namespace.system.asan.search.paths += /data/asan/product_services/${LIB}
 namespace.system.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
+namespace.system.links = runtime
+namespace.system.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+
 ###############################################################################
 # Namespace config for binaries under /postinstall.
-# Only one default namespace is defined and it has no directories other than
-# /system/lib in the search paths. This is because linker calls realpath on the
-# search paths and this causes selinux denial if the paths (/vendor, /odm) are
-# not allowed to the poinstall binaries. There is no reason to allow the
-# binaries to access the paths.
+# Only default and runtime namespaces are defined and default has no directories
+# other than /system/lib in the search paths. This is because linker calls
+# realpath on the search paths and this causes selinux denial if the paths
+# (/vendor, /odm) are not allowed to the postinstall binaries. There is no
+# reason to allow the binaries to access the paths.
 ###############################################################################
 [postinstall]
+additional.namespaces = runtime
+
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
 namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
+namespace.runtime.link.default.allow_all_shared_libs = true
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 7e354ac..7ca45ff 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -28,7 +28,7 @@
 dir.postinstall = /postinstall
 
 [system]
-additional.namespaces = sphal,vndk,rs
+additional.namespaces = runtime,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -55,6 +55,27 @@
 namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
 namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs  = libc.so:libdl.so:libm.so
+namespace.default.link.runtime.shared_libs += libart.so:libartd.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
 ###############################################################################
 # "sphal" namespace
 #
@@ -89,8 +110,12 @@
 
 # Once in this namespace, access to libraries in /system/lib is restricted. Only
 # libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+namespace.sphal.links = runtime,default,vndk,rs
 
+namespace.sphal.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+# LLNDK_LIBRARIES includes the runtime libs above, but the order here ensures
+# that they are loaded from the runtime namespace.
 namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
@@ -137,9 +162,11 @@
 namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
 namespace.rs.asan.permitted.paths += /data
 
-namespace.rs.links = default,vndk
+namespace.rs.links = runtime,default,vndk
 
-namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
+namespace.rs.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 # Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
 # namespace because RS framework libs are using them.
@@ -188,10 +215,14 @@
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
 # Android releases.
-namespace.vndk.links = default
+namespace.vndk.links = runtime,default
+
+namespace.vndk.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
 namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
+
 ###############################################################################
 # Namespace config for vendor processes. In O, no restriction is enforced for
 # them. However, in O-MR1, access to /system/${LIB} will not be allowed to
@@ -199,6 +230,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
+additional.namespaces = runtime
 namespace.default.isolated = false
 
 namespace.default.search.paths  = /odm/${LIB}
@@ -208,7 +240,7 @@
 namespace.default.search.paths += /vendor/${LIB}/vndk
 namespace.default.search.paths += /vendor/${LIB}/vndk-sp
 
-# Access to system libraries are allowed
+# Access to system libraries is allowed
 namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}
@@ -238,16 +270,47 @@
 namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
 namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+
 ###############################################################################
 # Namespace config for binaries under /postinstall.
-# Only one default namespace is defined and it has no directories other than
-# /system/lib in the search paths. This is because linker calls realpath on the
-# search paths and this causes selinux denial if the paths (/vendor, /odm) are
-# not allowed to the poinstall binaries. There is no reason to allow the
-# binaries to access the paths.
+# Only default and runtime namespaces are defined and default has no directories
+# other than /system/lib in the search paths. This is because linker calls
+# realpath on the search paths and this causes selinux denial if the paths
+# (/vendor, /odm) are not allowed to the postinstall binaries. There is no
+# reason to allow the binaries to access the paths.
 ###############################################################################
 [postinstall]
+additional.namespaces = runtime
+
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
 namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
+namespace.runtime.link.default.allow_all_shared_libs = true