fs_mgr: Create a C++ Fstab struct

Create a C++ Fstab struct with the intention to eventually deprecate
the legacy C 'fstab' struct.  This also contains functions for
converting between the two fstab variants to ease in the transition.

Bug: 62292478
Test: boot

Change-Id: I6fb51c6a99e65192708792719df01960cf6b432a
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 52eeb83..31b0944 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -38,21 +38,21 @@
 const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android");
 
 struct fs_mgr_flag_values {
-    char *key_loc;
-    char* key_dir;
-    char *verity_loc;
-    char *sysfs_path;
-    off64_t part_length;
-    char *label;
-    int partnum;
-    int swap_prio;
-    int max_comp_streams;
-    off64_t zram_size;
-    off64_t reserved_size;
-    int file_contents_mode;
-    int file_names_mode;
-    off64_t erase_blk_size;
-    off64_t logical_blk_size;
+    std::string key_loc;
+    std::string key_dir;
+    std::string verity_loc;
+    std::string sysfs_path;
+    off64_t part_length = 0;
+    std::string label;
+    int partnum = -1;
+    int swap_prio = -1;
+    int max_comp_streams = 0;
+    off64_t zram_size = 0;
+    off64_t reserved_size = 0;
+    int file_contents_mode = 0;
+    int file_names_mode = 0;
+    off64_t erase_blk_size = 0;
+    off64_t logical_blk_size = 0;
 };
 
 struct flag_list {
@@ -209,14 +209,6 @@
     char *p;
     char *savep;
 
-    /* initialize flag values.  If we find a relevant flag, we'll
-     * update the value */
-    if (flag_vals) {
-        memset(flag_vals, 0, sizeof(*flag_vals));
-        flag_vals->partnum = -1;
-        flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
-    }
-
     /* initialize fs_options to the null string */
     if (fs_options && (fs_options_len > 0)) {
         fs_options[0] = '\0';
@@ -242,22 +234,22 @@
                     /* The encryptable flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(arg);
+                    flag_vals->key_loc = arg;
                 } else if (flag == MF_VERIFY) {
                     /* If the verify flag is followed by an = and the
                      * location for the verity state,  get it and return it.
                      */
-                    flag_vals->verity_loc = strdup(arg);
+                    flag_vals->verity_loc = arg;
                 } else if (flag == MF_FORCECRYPT) {
                     /* The forceencrypt flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(arg);
+                    flag_vals->key_loc = arg;
                 } else if (flag == MF_FORCEFDEORFBE) {
                     /* The forcefdeorfbe flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(arg);
+                    flag_vals->key_loc = arg;
                     flag_vals->file_contents_mode = EM_AES_256_XTS;
                     flag_vals->file_names_mode = EM_AES_256_CTS;
                 } else if (flag == MF_FILEENCRYPTION) {
@@ -285,7 +277,7 @@
                     /* The metadata flag is followed by an = and the
                      * directory for the keys.  Get it and return it.
                      */
-                    flag_vals->key_dir = strdup(arg);
+                    flag_vals->key_dir = arg;
                 } else if (flag == MF_LENGTH) {
                     /* The length flag is followed by an = and the
                      * size of the partition.  Get it and return it.
@@ -302,8 +294,7 @@
                     auto label_end = strchr(label_start, ':');
 
                     if (label_end) {
-                        flag_vals->label = strndup(label_start,
-                                                   (int) (label_end - label_start));
+                        flag_vals->label = std::string(label_start, (int)(label_end - label_start));
                         auto part_start = label_end + 1;
                         if (!strcmp(part_start, "auto")) {
                             flag_vals->partnum = -1;
@@ -347,7 +338,7 @@
                         flag_vals->logical_blk_size = val;
                 } else if (flag == MF_SYSFS) {
                     /* The path to trigger device gc by idle-maint of vold. */
-                    flag_vals->sysfs_path = strdup(arg);
+                    flag_vals->sysfs_path = arg;
                 }
                 break;
             }
@@ -504,49 +495,17 @@
     return false;
 }
 
-static struct fstab* fs_mgr_read_fstab_file(FILE* fstab_file, bool proc_mounts) {
-    int cnt, entries;
+static bool fs_mgr_read_fstab_file(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
     ssize_t len;
     size_t alloc_len = 0;
     char *line = NULL;
     const char *delim = " \t";
     char *save_ptr, *p;
-    struct fstab *fstab = NULL;
+    Fstab fstab;
     struct fs_mgr_flag_values flag_vals;
 #define FS_OPTIONS_LEN 1024
     char tmp_fs_options[FS_OPTIONS_LEN];
 
-    entries = 0;
-    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
-        /* if the last character is a newline, shorten the string by 1 byte */
-        if (line[len - 1] == '\n') {
-            line[len - 1] = '\0';
-        }
-        /* Skip any leading whitespace */
-        p = line;
-        while (isspace(*p)) {
-            p++;
-        }
-        /* ignore comments or empty lines */
-        if (*p == '#' || *p == '\0')
-            continue;
-        entries++;
-    }
-
-    if (!entries) {
-        LERROR << "No entries found in fstab";
-        goto err;
-    }
-
-    /* Allocate and init the fstab structure */
-    fstab = static_cast<struct fstab *>(calloc(1, sizeof(struct fstab)));
-    fstab->num_entries = entries;
-    fstab->recs = static_cast<struct fstab_rec *>(
-        calloc(fstab->num_entries, sizeof(struct fstab_rec)));
-
-    fseek(fstab_file, 0, SEEK_SET);
-
-    cnt = 0;
     while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
         /* if the last character is a newline, shorten the string by 1 byte */
         if (line[len - 1] == '\n') {
@@ -562,46 +521,36 @@
         if (*p == '#' || *p == '\0')
             continue;
 
-        /* If a non-comment entry is greater than the size we allocated, give an
-         * error and quit.  This can happen in the unlikely case the file changes
-         * between the two reads.
-         */
-        if (cnt >= entries) {
-            LERROR << "Tried to process more entries than counted";
-            break;
-        }
+        FstabEntry entry;
 
         if (!(p = strtok_r(line, delim, &save_ptr))) {
             LERROR << "Error parsing mount source";
             goto err;
         }
-        fstab->recs[cnt].blk_device = strdup(p);
+        entry.blk_device = p;
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing mount_point";
             goto err;
         }
-        fstab->recs[cnt].mount_point = strdup(p);
+        entry.mount_point = p;
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing fs_type";
             goto err;
         }
-        fstab->recs[cnt].fs_type = strdup(p);
+        entry.fs_type = p;
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing mount_flags";
             goto err;
         }
         tmp_fs_options[0] = '\0';
-        fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
-                                       tmp_fs_options, FS_OPTIONS_LEN);
+        entry.flags = parse_flags(p, mount_flags, NULL, tmp_fs_options, FS_OPTIONS_LEN);
 
         /* fs_options are optional */
         if (tmp_fs_options[0]) {
-            fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
-        } else {
-            fstab->recs[cnt].fs_options = NULL;
+            entry.fs_options = tmp_fs_options;
         }
 
         // For /proc/mounts, ignore everything after mnt_freq and mnt_passno
@@ -611,78 +560,47 @@
             LERROR << "Error parsing fs_mgr_options";
             goto err;
         }
-        fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
-                                                    &flag_vals, NULL, 0);
-        fstab->recs[cnt].key_loc = flag_vals.key_loc;
-        fstab->recs[cnt].key_dir = flag_vals.key_dir;
-        fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
-        fstab->recs[cnt].length = flag_vals.part_length;
-        fstab->recs[cnt].label = flag_vals.label;
-        fstab->recs[cnt].partnum = flag_vals.partnum;
-        fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
-        fstab->recs[cnt].max_comp_streams = flag_vals.max_comp_streams;
-        fstab->recs[cnt].zram_size = flag_vals.zram_size;
-        fstab->recs[cnt].reserved_size = flag_vals.reserved_size;
-        fstab->recs[cnt].file_contents_mode = flag_vals.file_contents_mode;
-        fstab->recs[cnt].file_names_mode = flag_vals.file_names_mode;
-        fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
-        fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
-        fstab->recs[cnt].sysfs_path = flag_vals.sysfs_path;
-        if (fstab->recs[cnt].fs_mgr_flags & MF_LOGICAL) {
-            fstab->recs[cnt].logical_partition_name = strdup(fstab->recs[cnt].blk_device);
+        entry.fs_mgr_flags.val = parse_flags(p, fs_mgr_flags, &flag_vals, NULL, 0);
+
+        entry.key_loc = std::move(flag_vals.key_loc);
+        entry.key_dir = std::move(flag_vals.key_dir);
+        entry.verity_loc = std::move(flag_vals.verity_loc);
+        entry.length = flag_vals.part_length;
+        entry.label = std::move(flag_vals.label);
+        entry.partnum = flag_vals.partnum;
+        entry.swap_prio = flag_vals.swap_prio;
+        entry.max_comp_streams = flag_vals.max_comp_streams;
+        entry.zram_size = flag_vals.zram_size;
+        entry.reserved_size = flag_vals.reserved_size;
+        entry.file_contents_mode = flag_vals.file_contents_mode;
+        entry.file_names_mode = flag_vals.file_names_mode;
+        entry.erase_blk_size = flag_vals.erase_blk_size;
+        entry.logical_blk_size = flag_vals.logical_blk_size;
+        entry.sysfs_path = std::move(flag_vals.sysfs_path);
+        if (entry.fs_mgr_flags.logical) {
+            entry.logical_partition_name = entry.blk_device;
         }
 
-        cnt++;
+        fstab.emplace_back(std::move(entry));
     }
+
+    if (fstab.empty()) {
+        LERROR << "No entries found in fstab";
+        goto err;
+    }
+
     /* If an A/B partition, modify block device to be the real block device */
-    if (!fs_mgr_update_for_slotselect(fstab)) {
+    if (!fs_mgr_update_for_slotselect(&fstab)) {
         LERROR << "Error updating for slotselect";
         goto err;
     }
     free(line);
-    return fstab;
+    *fstab_out = std::move(fstab);
+    return true;
 
 err:
     free(line);
-    if (fstab)
-        fs_mgr_free_fstab(fstab);
-    return NULL;
-}
-
-/* merges fstab entries from both a and b, then returns the merged result.
- * note that the caller should only manage the return pointer without
- * doing further memory management for the two inputs, i.e. only need to
- * frees up memory of the return value without touching a and b. */
-static struct fstab *in_place_merge(struct fstab *a, struct fstab *b)
-{
-    if (!a && !b) return nullptr;
-    if (!a) return b;
-    if (!b) return a;
-
-    int total_entries = a->num_entries + b->num_entries;
-    a->recs = static_cast<struct fstab_rec *>(realloc(
-        a->recs, total_entries * (sizeof(struct fstab_rec))));
-    if (!a->recs) {
-        LERROR << __FUNCTION__ << "(): failed to allocate fstab recs";
-        // If realloc() fails the original block is left untouched;
-        // it is not freed or moved. So we have to free both a and b here.
-        fs_mgr_free_fstab(a);
-        fs_mgr_free_fstab(b);
-        return nullptr;
-    }
-
-    for (int i = a->num_entries, j = 0; i < total_entries; i++, j++) {
-        // Copy the structs by assignment.
-        a->recs[i] = b->recs[j];
-    }
-
-    // We can't call fs_mgr_free_fstab because a->recs still references the
-    // memory allocated by strdup.
-    free(b->recs);
-    free(b);
-
-    a->num_entries = total_entries;
-    return a;
+    return false;
 }
 
 /* Extracts <device>s from the by-name symlinks specified in a fstab:
@@ -695,11 +613,11 @@
  *   /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
  * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
  */
-static std::set<std::string> extract_boot_devices(const fstab& fstab) {
+static std::set<std::string> extract_boot_devices(const Fstab& fstab) {
     std::set<std::string> boot_devices;
 
-    for (int i = 0; i < fstab.num_entries; i++) {
-        std::string blk_device(fstab.recs[i].blk_device);
+    for (const auto& entry : fstab) {
+        std::string blk_device = entry.blk_device;
         // Skips blk_device that doesn't conform to the format.
         if (!android::base::StartsWith(blk_device, "/dev/block") ||
             android::base::StartsWith(blk_device, "/dev/block/by-name") ||
@@ -728,33 +646,36 @@
     return boot_devices;
 }
 
-struct fstab *fs_mgr_read_fstab(const char *fstab_path)
-{
-    struct fstab *fstab;
-
-    auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fstab_path, "re"), fclose};
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
+    auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
     if (!fstab_file) {
-        PERROR << __FUNCTION__<< "(): cannot open file: '" << fstab_path << "'";
+        PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
+        return false;
+    }
+
+    if (!fs_mgr_read_fstab_file(fstab_file.get(), path == "/proc/mounts", fstab)) {
+        LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
+        return false;
+    }
+
+    return true;
+}
+
+struct fstab* fs_mgr_read_fstab(const char* fstab_path) {
+    Fstab fstab;
+    if (!ReadFstabFromFile(fstab_path, &fstab)) {
         return nullptr;
     }
 
-    fstab = fs_mgr_read_fstab_file(fstab_file.get(), !strcmp("/proc/mounts", fstab_path));
-    if (!fstab) {
-        LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
-    }
-
-    return fstab;
+    return FstabToLegacyFstab(fstab);
 }
 
-/* Returns fstab entries parsed from the device tree if they
- * exist
- */
-struct fstab *fs_mgr_read_fstab_dt()
-{
+// Returns fstab entries parsed from the device tree if they exist
+bool ReadFstabFromDt(Fstab* fstab) {
     std::string fstab_buf = read_fstab_from_dt();
     if (fstab_buf.empty()) {
         LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
-        return nullptr;
+        return false;
     }
 
     std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
@@ -762,16 +683,25 @@
                  fstab_buf.length(), "r"), fclose);
     if (!fstab_file) {
         PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
+        return false;
+    }
+
+    if (!fs_mgr_read_fstab_file(fstab_file.get(), false, fstab)) {
+        LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:"
+               << std::endl << fstab_buf;
+        return false;
+    }
+
+    return true;
+}
+
+struct fstab* fs_mgr_read_fstab_dt() {
+    Fstab fstab;
+    if (!ReadFstabFromDt(&fstab)) {
         return nullptr;
     }
 
-    struct fstab* fstab = fs_mgr_read_fstab_file(fstab_file.get(), false);
-    if (!fstab) {
-        LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:"
-               << std::endl << fstab_buf;
-    }
-
-    return fstab;
+    return FstabToLegacyFstab(fstab);
 }
 
 /*
@@ -797,32 +727,42 @@
     return std::string();
 }
 
-/*
- * loads the fstab file and combines with fstab entries passed in from device tree.
- */
-struct fstab *fs_mgr_read_fstab_default()
-{
-    std::string default_fstab;
+// Loads the fstab file and combines with fstab entries passed in from device tree.
+bool ReadDefaultFstab(Fstab* fstab) {
+    Fstab dt_fstab;
+    ReadFstabFromDt(&dt_fstab);
 
+    *fstab = std::move(dt_fstab);
+
+    std::string default_fstab_path;
     // Use different fstab paths for normal boot and recovery boot, respectively
     if (access("/system/bin/recovery", F_OK) == 0) {
-        default_fstab = "/etc/recovery.fstab";
+        default_fstab_path = "/etc/recovery.fstab";
     } else {  // normal boot
-        default_fstab = get_fstab_path();
+        default_fstab_path = get_fstab_path();
     }
 
-    struct fstab* fstab = nullptr;
-    if (!default_fstab.empty()) {
-        fstab = fs_mgr_read_fstab(default_fstab.c_str());
+    Fstab default_fstab;
+    if (!default_fstab_path.empty()) {
+        ReadFstabFromFile(default_fstab_path, &default_fstab);
     } else {
         LINFO << __FUNCTION__ << "(): failed to find device default fstab";
     }
 
-    struct fstab* fstab_dt = fs_mgr_read_fstab_dt();
+    for (auto&& entry : default_fstab) {
+        fstab->emplace_back(std::move(entry));
+    }
 
-    // combines fstab entries passed in from device tree with
-    // the ones found from default_fstab file
-    return in_place_merge(fstab_dt, fstab);
+    return !fstab->empty();
+}
+
+struct fstab* fs_mgr_read_fstab_default() {
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return nullptr;
+    }
+
+    return FstabToLegacyFstab(fstab);
 }
 
 void fs_mgr_free_fstab(struct fstab *fstab)
@@ -908,11 +848,83 @@
     }
 
     // Fallback to extract boot devices from fstab.
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    if (fstab) return extract_boot_devices(*fstab);
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return {};
+    }
 
-    return {};
+    return extract_boot_devices(fstab);
+}
+
+FstabEntry FstabRecToFstabEntry(const fstab_rec* fstab_rec) {
+    FstabEntry entry;
+    entry.blk_device = fstab_rec->blk_device;
+    entry.logical_partition_name = fstab_rec->logical_partition_name;
+    entry.mount_point = fstab_rec->mount_point;
+    entry.fs_type = fstab_rec->fs_type;
+    entry.flags = fstab_rec->flags;
+    entry.fs_options = fstab_rec->fs_options;
+    entry.fs_mgr_flags.val = fstab_rec->fs_mgr_flags;
+    entry.key_loc = fstab_rec->key_loc;
+    entry.key_dir = fstab_rec->key_dir;
+    entry.verity_loc = fstab_rec->verity_loc;
+    entry.length = fstab_rec->length;
+    entry.label = fstab_rec->label;
+    entry.partnum = fstab_rec->partnum;
+    entry.swap_prio = fstab_rec->swap_prio;
+    entry.max_comp_streams = fstab_rec->max_comp_streams;
+    entry.zram_size = fstab_rec->zram_size;
+    entry.reserved_size = fstab_rec->reserved_size;
+    entry.file_contents_mode = fstab_rec->file_contents_mode;
+    entry.file_names_mode = fstab_rec->file_names_mode;
+    entry.erase_blk_size = fstab_rec->erase_blk_size;
+    entry.logical_blk_size = fstab_rec->logical_blk_size;
+    entry.sysfs_path = fstab_rec->sysfs_path;
+
+    return entry;
+}
+
+Fstab LegacyFstabToFstab(const struct fstab* legacy_fstab) {
+    Fstab fstab;
+    for (int i = 0; i < legacy_fstab->num_entries; i++) {
+        fstab.emplace_back(FstabRecToFstabEntry(&legacy_fstab->recs[i]));
+    }
+
+    return fstab;
+}
+
+fstab* FstabToLegacyFstab(const Fstab& fstab) {
+    struct fstab* legacy_fstab = static_cast<struct fstab*>(calloc(1, sizeof(struct fstab)));
+    legacy_fstab->num_entries = fstab.size();
+    legacy_fstab->recs =
+            static_cast<fstab_rec*>(calloc(legacy_fstab->num_entries, sizeof(fstab_rec)));
+
+    for (int i = 0; i < legacy_fstab->num_entries; i++) {
+        legacy_fstab->recs[i].blk_device = strdup(fstab[i].blk_device.c_str());
+        legacy_fstab->recs[i].logical_partition_name =
+                strdup(fstab[i].logical_partition_name.c_str());
+        legacy_fstab->recs[i].mount_point = strdup(fstab[i].mount_point.c_str());
+        legacy_fstab->recs[i].fs_type = strdup(fstab[i].fs_type.c_str());
+        legacy_fstab->recs[i].flags = fstab[i].flags;
+        legacy_fstab->recs[i].fs_options = strdup(fstab[i].fs_options.c_str());
+        legacy_fstab->recs[i].fs_mgr_flags = fstab[i].fs_mgr_flags.val;
+        legacy_fstab->recs[i].key_loc = strdup(fstab[i].key_loc.c_str());
+        legacy_fstab->recs[i].key_dir = strdup(fstab[i].key_dir.c_str());
+        legacy_fstab->recs[i].verity_loc = strdup(fstab[i].verity_loc.c_str());
+        legacy_fstab->recs[i].length = fstab[i].length;
+        legacy_fstab->recs[i].label = strdup(fstab[i].label.c_str());
+        legacy_fstab->recs[i].partnum = fstab[i].partnum;
+        legacy_fstab->recs[i].swap_prio = fstab[i].swap_prio;
+        legacy_fstab->recs[i].max_comp_streams = fstab[i].max_comp_streams;
+        legacy_fstab->recs[i].zram_size = fstab[i].zram_size;
+        legacy_fstab->recs[i].reserved_size = fstab[i].reserved_size;
+        legacy_fstab->recs[i].file_contents_mode = fstab[i].file_contents_mode;
+        legacy_fstab->recs[i].file_names_mode = fstab[i].file_names_mode;
+        legacy_fstab->recs[i].erase_blk_size = fstab[i].erase_blk_size;
+        legacy_fstab->recs[i].logical_blk_size = fstab[i].logical_blk_size;
+        legacy_fstab->recs[i].sysfs_path = strdup(fstab[i].sysfs_path.c_str());
+    }
+    return legacy_fstab;
 }
 
 int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 87c971a..711446c 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -22,6 +22,7 @@
 
 #include <android-base/logging.h>
 #include <fs_mgr.h>
+#include <fstab/fstab.h>
 
 #include "fs_mgr_priv_boot_config.h"
 
@@ -130,7 +131,7 @@
                           FileWaitMode wait_mode = FileWaitMode::Exists);
 
 int fs_mgr_set_blk_ro(const char* blockdev);
-bool fs_mgr_update_for_slotselect(fstab* fstab);
+bool fs_mgr_update_for_slotselect(Fstab* fstab);
 bool fs_mgr_is_device_unlocked();
 const std::string& get_android_dt_dir();
 bool is_dt_compatible();
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 3b01d0e..20d60ae 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -31,36 +31,23 @@
 }
 
 // Updates |fstab| for slot_suffix. Returns true on success, false on error.
-bool fs_mgr_update_for_slotselect(struct fstab *fstab) {
+bool fs_mgr_update_for_slotselect(Fstab* fstab) {
     int n;
     std::string ab_suffix;
 
-    for (n = 0; n < fstab->num_entries; n++) {
-        fstab_rec& record = fstab->recs[n];
-        if (record.fs_mgr_flags & MF_SLOTSELECT) {
-            if (ab_suffix.empty()) {
-                ab_suffix = fs_mgr_get_slot_suffix();
-                // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
-                if (ab_suffix.empty()) return false;
-            }
-
-            char* new_blk_device;
-            if (asprintf(&new_blk_device, "%s%s", record.blk_device, ab_suffix.c_str()) <= 0) {
-                return false;
-            }
-            free(record.blk_device);
-            record.blk_device = new_blk_device;
-
-            char* new_partition_name;
-            if (record.logical_partition_name) {
-                if (asprintf(&new_partition_name, "%s%s", record.logical_partition_name,
-                             ab_suffix.c_str()) <= 0) {
-                    return false;
-                }
-                free(record.logical_partition_name);
-                record.logical_partition_name = new_partition_name;
-            }
+    for (auto& entry : *fstab) {
+        if (!entry.fs_mgr_flags.slot_select) {
+            continue;
         }
+
+        if (ab_suffix.empty()) {
+            ab_suffix = fs_mgr_get_slot_suffix();
+            // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
+            if (ab_suffix.empty()) return false;
+        }
+
+        entry.blk_device = entry.blk_device + ab_suffix;
+        entry.logical_partition_name = entry.logical_partition_name + ab_suffix;
     }
     return true;
 }
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index da13899..80bdb87 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef __CORE_FS_TAB_H
-#define __CORE_FS_TAB_H
+#pragma once
 
 #include <linux/dm-ioctl.h>
 #include <stdbool.h>
@@ -25,6 +24,7 @@
 
 #include <set>
 #include <string>
+#include <vector>
 
 /*
  * The entries must be kept in the same order as they were seen in the fstab.
@@ -95,4 +95,82 @@
 std::string fs_mgr_get_slot_suffix();
 std::set<std::string> fs_mgr_get_boot_devices();
 
-#endif /* __CORE_FS_TAB_H */
+struct FstabEntry {
+    std::string blk_device;
+    std::string logical_partition_name;
+    std::string mount_point;
+    std::string fs_type;
+    unsigned long flags = 0;
+    std::string fs_options;
+    std::string key_loc;
+    std::string key_dir;
+    std::string verity_loc;
+    off64_t length = 0;
+    std::string label;
+    int partnum = -1;
+    int swap_prio = -1;
+    int max_comp_streams = 0;
+    off64_t zram_size = 0;
+    off64_t reserved_size = 0;
+    int file_contents_mode = 0;
+    int file_names_mode = 0;
+    off64_t erase_blk_size = 0;
+    off64_t logical_blk_size = 0;
+    std::string sysfs_path;
+
+    // TODO: Remove this union once fstab_rec is deprecated. It only serves as a
+    // convenient way to convert between fstab_rec::fs_mgr_flags and these bools.
+    union {
+        int val;
+        struct {
+            bool wait : 1;
+            bool check : 1;
+            bool crypt : 1;
+            bool nonremovable : 1;
+            bool vold_managed : 1;
+            bool length : 1;
+            bool recovery_only : 1;
+            bool swap_prio : 1;
+            bool zram_size : 1;
+            bool verify : 1;
+            bool force_crypt : 1;
+            bool no_emulated_sd : 1;  // No emulated sdcard daemon; sd card is the only external
+                                      // storage.
+            bool no_trim : 1;
+            bool file_encryption : 1;
+            bool formattable : 1;
+            bool slot_select : 1;
+            bool force_fde_or_fbe : 1;
+            bool late_mount : 1;
+            bool no_fail : 1;
+            bool verify_at_boot : 1;
+            bool max_comp_streams : 1;
+            bool reserved_size : 1;
+            bool quota : 1;
+            bool erase_blk_size : 1;
+            bool logical_blk_size : 1;
+            bool avb : 1;
+            bool key_directory : 1;
+            bool sysfs : 1;
+            bool logical : 1;
+            bool checkpoint_blk : 1;
+            bool checkpoint_fs : 1;
+        };
+    } fs_mgr_flags;
+
+    bool is_encryptable() const {
+        return fs_mgr_flags.crypt || fs_mgr_flags.force_crypt || fs_mgr_flags.force_fde_or_fbe;
+    }
+};
+
+// An Fstab is a collection of FstabEntry structs.
+using Fstab = std::vector<FstabEntry>;
+
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
+bool ReadFstabFromDt(Fstab* fstab);
+bool ReadDefaultFstab(Fstab* fstab);
+
+// Temporary conversion functions.
+FstabEntry FstabRecToFstabEntry(const fstab_rec* fstab_rec);
+Fstab LegacyFstabToFstab(const struct fstab* legacy_fstab);
+fstab* FstabToLegacyFstab(const Fstab& fstab);