fs_mrg: clean up multi-type mount_all

Move the code that attempts to mount alternative fstab entries
into its own function.
Clarify return codes.
Suggest wipe via recovery in error messages.

Bug: 15747366
Change-Id: I3634477cd4d1d73f974f3e906c53285f0d9e0eac
Signed-off-by: JP Abgrall <jpa@google.com>
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index e9b4125..f01c562 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -245,31 +245,89 @@
     return strcmp(value, "1") ? 0 : 1;
 }
 
-void wipe_data_via_recovery()
+/*
+ * 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(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx)
 {
-    mkdir("/cache/recovery", 0700);
-    int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC, 0600);
-    if (fd >= 0) {
-        write(fd, "--wipe_data", strlen("--wipe_data") + 1);
-        close(fd);
-    } else {
-        ERROR("could not open /cache/recovery/command\n");
+    int i;
+    int mount_errno = 0;
+    int mounted = 0;
+
+    if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
+      errno = EINVAL;
+      if (end_idx) *end_idx = start_idx;
+      if (attempted_idx) *end_idx = start_idx;
+      return -1;
     }
-    property_set(ANDROID_RB_PROPERTY, "reboot,recovery");
+
+    /* 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) {
+                ERROR("%s(): skipping fstab dup mountpoint=%s rec[%d].fs_type=%s already mounted as %s.\n", __func__,
+                     fstab->recs[i].mount_point, i, fstab->recs[i].fs_type, fstab->recs[*attempted_idx].fs_type);
+                continue;
+            }
+
+            if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+                check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+                         fstab->recs[i].mount_point);
+            }
+            if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) {
+                *attempted_idx = i;
+                mounted = 1;
+                if (i != start_idx) {
+                    ERROR("%s(): Mounted %s on %s with fs_type=%s instead of %s\n", __func__,
+                         fstab->recs[i].blk_device, fstab->recs[i].mount_point, fstab->recs[i].fs_type,
+                         fstab->recs[start_idx].fs_type);
+                }
+            } else {
+                /* back up errno for crypto decisions */
+                mount_errno = errno;
+            }
+    }
+
+    /* Adjust i for the case where it was still withing the recs[] */
+    if (i < fstab->num_entries) --i;
+
+    *end_idx = i;
+    if (!mounted) {
+        *attempted_idx = start_idx;
+        errno = mount_errno;
+        return -1;
+    }
+    return 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(struct fstab *fstab)
 {
-    int i = 0, j = 0;
-    int encryptable = 0;
+    int i = 0;
+    int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
     int error_count = 0;
     int mret = -1;
     int mount_errno = 0;
-    const char *last_ok_mount_point = NULL;
+    int attempted_idx = -1;
 
     if (!fstab) {
         return -1;
@@ -299,55 +357,27 @@
                 continue;
             }
         }
-
-        /*
-         * 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 (last_ok_mount_point && !strcmp(last_ok_mount_point, fstab->recs[i].mount_point)) {
-            INFO("%s(): skipping fstab dup mountpoint=%s rec[%d].fs_type=%s already mounted.\n", __func__,
-                 last_ok_mount_point, i, fstab->recs[i].fs_type);
-            continue;
-        }
-        /* Hunt down an fstab entry for the same mount point that might succeed */
-        for (j = i;
-             /* We required that fstab entries for the same mountpoint be consecutive */
-             j < fstab->num_entries && !strcmp(fstab->recs[i].mount_point, fstab->recs[j].mount_point);
-             j++) {
-                if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
-                    check_fs(fstab->recs[j].blk_device, fstab->recs[j].fs_type,
-                             fstab->recs[j].mount_point);
-                }
-                mret = __mount(fstab->recs[j].blk_device, fstab->recs[j].mount_point, &fstab->recs[j]);
-                if (!mret) {
-                    last_ok_mount_point = fstab->recs[j].mount_point;
-                    if (i != j) {
-                        INFO("%s(): some alternate mount worked for mount_point=%s fs_type=%s instead of fs_type=%s\n", __func__,
-                             last_ok_mount_point, fstab->recs[j].fs_type, fstab->recs[i].fs_type);
-                        i = j;   /* We advance the recs index to the working entry */
-                    }
-                    break;
-                } else {
-                    /* back up errno for crypto decisions */
-                    mount_errno = errno;
-                }
-        }
+        int last_idx_inspected;
+        mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);
+        i = last_idx_inspected;
+        mount_errno = errno;
 
         /* Deal with encryptability. */
         if (!mret) {
             /* If this is encryptable, need to trigger encryption */
-            if ((fstab->recs[i].fs_mgr_flags & MF_FORCECRYPT)) {
-                if (umount(fstab->recs[i].mount_point) == 0) {
-                    if (!encryptable) {
-                        encryptable = 2;
+            if ((fstab->recs[attempted_idx].fs_mgr_flags & MF_FORCECRYPT)) {
+                if (umount(fstab->recs[attempted_idx].mount_point) == 0) {
+                    if (encryptable == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+                        ERROR("Will try to encrypt %s %s\n", fstab->recs[attempted_idx].mount_point,
+                              fstab->recs[attempted_idx].fs_type);
+                        encryptable = FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
                     } else {
                         ERROR("Only one encryptable/encrypted partition supported\n");
-                        encryptable = 1;
+                        encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
                     }
                 } else {
                     INFO("Could not umount %s - allow continue unencrypted\n",
-                         fstab->recs[i].mount_point);
+                         fstab->recs[attempted_idx].mount_point);
                     continue;
                 }
             }
@@ -358,27 +388,28 @@
         /* mount(2) returned an error, check if it's encryptable and deal with it */
         if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
             fs_mgr_is_encryptable(&fstab->recs[i])) {
-            if(partition_wiped(fstab->recs[i].blk_device) && fstab->recs[i].fs_mgr_flags & MF_FORCECRYPT) {
-                ERROR("Found an encryptable wiped partition with force encrypt. Formating via recovery.\n");
-                wipe_data_via_recovery();  /* This is queue up a reboot */
+            if (partition_wiped(fstab->recs[i].blk_device)) {
+                ERROR("%s(): Encryptable wiped partition %s. Recommend wiping via recovery. Fail for now.\n", __func__, fstab->recs[i].mount_point);
                 ++error_count;
                 continue;
             } else {
                 /* Need to mount a tmpfs at this mountpoint for now, and set
                  * properties that vold will query later for decrypting
                  */
-                if (fs_mgr_do_tmpfs_mount(fstab->recs[i].mount_point) < 0) {
+                ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__,
+                      fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+                      fstab->recs[attempted_idx].fs_type);
+                if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
                     ++error_count;
                     continue;
                 }
-                last_ok_mount_point = fstab->recs[i].mount_point;
             }
-            encryptable = 1;
+            encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
         } else {
             ERROR("Failed to mount an un-encryptable or wiped partition on"
                    "%s at %s options: %s error: %s\n",
-                   fstab->recs[i].blk_device, fstab->recs[i].mount_point,
-                   fstab->recs[i].fs_options, strerror(mount_errno));
+                   fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+                   fstab->recs[attempted_idx].fs_options, strerror(mount_errno));
             ++error_count;
             continue;
         }
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index b8bb5aa..ab3f828 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -53,6 +53,10 @@
 
 struct fstab *fs_mgr_read_fstab(const char *fstab_path);
 void fs_mgr_free_fstab(struct fstab *fstab);
+
+#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 2
+#define FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED 1
+#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 0
 int fs_mgr_mount_all(struct fstab *fstab);
 int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
                     char *tmp_mount_point);
diff --git a/init/builtins.c b/init/builtins.c
index 452119e..dd147f6 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -511,27 +511,26 @@
         if (child_ret == -1) {
             ERROR("fs_mgr_mount_all returned an error\n");
         }
-        exit(child_ret);
+        _exit(child_ret);
     } else {
         /* fork failed, return an error */
         return -1;
     }
 
-    /* ret is 2 if device needs encrypted, 1 if the device appears encrypted,
-     * 0 if not, and -1 on error */
-    if (ret == 2) {
+    if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
         property_set("ro.crypto.state", "unencrypted");
         property_set("vold.decrypt", "trigger_encryption");
-    } else if (ret == 1) {
+    } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
         property_set("ro.crypto.state", "encrypted");
         property_set("vold.decrypt", "trigger_default_encryption");
-    } else if (ret == 0) {
+    } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
         property_set("ro.crypto.state", "unencrypted");
         /* If fs_mgr determined this is an unencrypted device, then trigger
          * that action.
          */
         action_for_each_trigger("nonencrypted", action_add_queue_tail);
     }
+    /* else ... < 0: error */
 
     return ret;
 }