Add fs_mgr option to enable/disable quotas.

To support upcoming disk usage calculation optimizations, this change
adds a new 'quota' mount flag.  As part of mounting an ext4 device,
we now enable/disable the quota feature using tune2fs to match the
requested value in the fstab.

When changing the quota status, we force a fsck pass on the device
before actually mounting it to prime the quota data structures which
are stored in hidden inodes.

Changing quota state and priming the data structures needs to happen
before we actually mount the device, so fs_mgr is the best place to
place this logic.

Test: builds, boots, enables and disables quota
Bug: 27948817
Change-Id: I7ccbf97cbc4a679bdd7a31a77be4b99aa9a88e66
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index c9876fd..6e6d69f 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -213,6 +213,73 @@
             le32_to_cpu(es->s_r_blocks_count_lo);
 }
 
+static int do_quota(char *blk_device, char *fs_type, struct fstab_rec *rec)
+{
+    int force_check = 0;
+    if (!strcmp(fs_type, "ext4")) {
+        /*
+         * Some system images do not have tune2fs for licensing reasons
+         * Detect these and skip reserve blocks.
+         */
+        if (access(TUNE2FS_BIN, X_OK)) {
+            ERROR("Not running %s on %s (executable not in system image)\n",
+                  TUNE2FS_BIN, blk_device);
+        } else {
+            char* arg1 = NULL;
+            char* arg2 = NULL;
+            int status = 0;
+            int ret = 0;
+            int fd = TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC));
+            if (fd >= 0) {
+                struct ext4_super_block sb;
+                ret = read_super_block(fd, &sb);
+                if (ret < 0) {
+                    ERROR("Can't read '%s' super block: %s\n", blk_device, strerror(errno));
+                    goto out;
+                }
+
+                int has_quota = (sb.s_feature_ro_compat
+                        & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
+                int want_quota = fs_mgr_is_quota(rec) != 0;
+
+                if (has_quota == want_quota) {
+                    INFO("Requested quota status is match on %s\n", blk_device);
+                    goto out;
+                } else if (want_quota) {
+                    INFO("Enabling quota on %s\n", blk_device);
+                    arg1 = "-Oquota";
+                    arg2 = "-Qusrquota,grpquota";
+                    force_check = 1;
+                } else {
+                    INFO("Disabling quota on %s\n", blk_device);
+                    arg1 = "-Q^usrquota,^grpquota";
+                    arg2 = "-O^quota";
+                }
+            } else {
+                ERROR("Failed to open '%s': %s\n", blk_device, strerror(errno));
+                return force_check;
+            }
+
+            char *tune2fs_argv[] = {
+                TUNE2FS_BIN,
+                arg1,
+                arg2,
+                blk_device,
+            };
+            ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv), tune2fs_argv,
+                                          &status, true, LOG_KLOG | LOG_FILE,
+                                          true, NULL, NULL, 0);
+            if (ret < 0) {
+                /* No need to check for error in fork, we can't really handle it now */
+                ERROR("Failed trying to run %s\n", TUNE2FS_BIN);
+            }
+      out:
+            close(fd);
+        }
+    }
+    return force_check;
+}
+
 static void do_reserved_size(char *blk_device, char *fs_type, struct fstab_rec *rec)
 {
     /* Check for the types of filesystems we know how to check */
@@ -417,7 +484,10 @@
                 continue;
             }
 
-            if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+            int force_check = do_quota(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+                                       &fstab->recs[i]);
+
+            if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
                 check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
                          fstab->recs[i].mount_point);
             }
@@ -787,7 +857,10 @@
             wait_for_file(n_blk_device, WAIT_TIMEOUT);
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+        int force_check = do_quota(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+                                   &fstab->recs[i]);
+
+        if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
             check_fs(n_blk_device, fstab->recs[i].fs_type,
                      fstab->recs[i].mount_point);
         }
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index 90c7435..41fb746 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -62,29 +62,31 @@
 };
 
 static struct flag_list fs_mgr_flags[] = {
-    { "wait",        MF_WAIT },
-    { "check",       MF_CHECK },
-    { "encryptable=",MF_CRYPT },
-    { "forceencrypt=",MF_FORCECRYPT },
-    { "fileencryption=",MF_FILEENCRYPTION },
-    { "forcefdeorfbe=",MF_FORCEFDEORFBE },
-    { "nonremovable",MF_NONREMOVABLE },
-    { "voldmanaged=",MF_VOLDMANAGED},
-    { "length=",     MF_LENGTH },
-    { "recoveryonly",MF_RECOVERYONLY },
-    { "swapprio=",   MF_SWAPPRIO },
-    { "zramsize=",   MF_ZRAMSIZE },
-    { "max_comp_streams=",   MF_MAX_COMP_STREAMS },
-    { "verify",      MF_VERIFY },
-    { "noemulatedsd", MF_NOEMULATEDSD },
-    { "notrim",       MF_NOTRIM },
-    { "formattable", MF_FORMATTABLE },
-    { "slotselect",  MF_SLOTSELECT },
-    { "nofail",      MF_NOFAIL },
-    { "latemount",   MF_LATEMOUNT },
-    { "reservedsize=", MF_RESERVEDSIZE },
-    { "defaults",    0 },
-    { 0,             0 },
+    { "wait",               MF_WAIT },
+    { "check",              MF_CHECK },
+    { "encryptable=",       MF_CRYPT },
+    { "forceencrypt=",      MF_FORCECRYPT },
+    { "fileencryption=",    MF_FILEENCRYPTION },
+    { "forcefdeorfbe=",     MF_FORCEFDEORFBE },
+    { "nonremovable",       MF_NONREMOVABLE },
+    { "voldmanaged=",       MF_VOLDMANAGED},
+    { "length=",            MF_LENGTH },
+    { "recoveryonly",       MF_RECOVERYONLY },
+    { "swapprio=",          MF_SWAPPRIO },
+    { "zramsize=",          MF_ZRAMSIZE },
+    { "max_comp_streams=",  MF_MAX_COMP_STREAMS },
+    { "verifyatboot",       MF_VERIFYATBOOT },
+    { "verify",             MF_VERIFY },
+    { "noemulatedsd",       MF_NOEMULATEDSD },
+    { "notrim",             MF_NOTRIM },
+    { "formattable",        MF_FORMATTABLE },
+    { "slotselect",         MF_SLOTSELECT },
+    { "nofail",             MF_NOFAIL },
+    { "latemount",          MF_LATEMOUNT },
+    { "reservedsize=",      MF_RESERVEDSIZE },
+    { "quota",              MF_QUOTA },
+    { "defaults",           0 },
+    { 0,                    0 },
 };
 
 #define EM_SOFTWARE 1
@@ -586,3 +588,8 @@
 {
     return fstab->fs_mgr_flags & MF_LATEMOUNT;
 }
+
+int fs_mgr_is_quota(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_QUOTA;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 23c97e4..db86afa 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -65,28 +65,30 @@
  *
  */
 
-#define MF_WAIT         0x1
-#define MF_CHECK        0x2
-#define MF_CRYPT        0x4
-#define MF_NONREMOVABLE 0x8
-#define MF_VOLDMANAGED  0x10
-#define MF_LENGTH       0x20
-#define MF_RECOVERYONLY 0x40
-#define MF_SWAPPRIO     0x80
-#define MF_ZRAMSIZE     0x100
-#define MF_VERIFY       0x200
-#define MF_FORCECRYPT   0x400
-#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
-                                 external storage */
-#define MF_NOTRIM       0x1000
-#define MF_FILEENCRYPTION 0x2000
-#define MF_FORMATTABLE  0x4000
-#define MF_SLOTSELECT   0x8000
-#define MF_FORCEFDEORFBE 0x10000
-#define MF_LATEMOUNT    0x20000
-#define MF_NOFAIL       0x40000
+#define MF_WAIT                  0x1
+#define MF_CHECK                 0x2
+#define MF_CRYPT                 0x4
+#define MF_NONREMOVABLE          0x8
+#define MF_VOLDMANAGED          0x10
+#define MF_LENGTH               0x20
+#define MF_RECOVERYONLY         0x40
+#define MF_SWAPPRIO             0x80
+#define MF_ZRAMSIZE            0x100
+#define MF_VERIFY              0x200
+#define MF_FORCECRYPT          0x400
+#define MF_NOEMULATEDSD        0x800 /* no emulated sdcard daemon, sd card is the only
+                                        external storage */
+#define MF_NOTRIM             0x1000
+#define MF_FILEENCRYPTION     0x2000
+#define MF_FORMATTABLE        0x4000
+#define MF_SLOTSELECT         0x8000
+#define MF_FORCEFDEORFBE     0x10000
+#define MF_LATEMOUNT         0x20000
+#define MF_NOFAIL            0x40000
+#define MF_VERIFYATBOOT      0x80000
 #define MF_MAX_COMP_STREAMS 0x100000
-#define MF_RESERVEDSIZE 0x200000
+#define MF_RESERVEDSIZE     0x200000
+#define MF_QUOTA            0x400000
 
 #define DM_BUF_SIZE 4096
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index ed22e90..e7a0a1d 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -123,6 +123,7 @@
 int fs_mgr_is_formattable(struct fstab_rec *fstab);
 int fs_mgr_is_nofail(struct fstab_rec *fstab);
 int fs_mgr_is_latemount(struct fstab_rec *fstab);
+int fs_mgr_is_quota(struct fstab_rec *fstab);
 int fs_mgr_swapon_all(struct fstab *fstab);
 
 int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);