Patchwork [v4] e2fsprogs: clean up codes for adding new quota type

login
register
mail settings
Submitter Li Xi
Date March 26, 2014, 2:27 a.m.
Message ID <CAPTn0cA-LLj1GwpemqCOc4gOc-yuHYnYAbKGDC1fn-MccPeuMg@mail.gmail.com>
Download mbox | patch
Permalink /patch/333764/
State New
Headers show

Comments

Li Xi - March 26, 2014, 2:27 a.m.
Adding directory/project quota support to ext4 is widely discussed
these days. E2fsprogs has to be updated if we want that new feature.
As a preparation for it, this patch cleans up quota codes of e2fsprogs
so as to make it easier to add new quota type(s).

Changelog:
* V4:
  - Change quota_type_t to enum quota_type.
* V3:
  - Add check of MAXQUOTAS <= 32
  - Change iterator from "i"/"type" to "qtype".
  - Add assertion of qtype range in quota_type2* funtions
  - Rename quota_type2ino to quota_type2inum
  - Change get_qid()
  - Replace quota_type2offset() with quota_sb_inump()
* V2:
  - Change type2* functions to quota_type2*
  - Use uncontinuous array of s_quota_inum
  - Add quota_type_t
  - Fix a problem of quota_write_inode() in the first patch
  - Rename USRQUOTA_BIT/GRPQUOTA_BIT/
ALLQUOTA_BIT to QUOTA_*_BIT
  - Code style change

Signed-off-by: Li Xi <lixi <at> ddn.com>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
--
 static blk64_t journal_location = ~0LL;

@@ -995,9 +995,9 @@ static void parse_extended_opts(struct e
                 continue;
             }
             if (!strncmp(arg, "usr", 3)) {
-                quotatype = 0;
+                quotatype_bits = QUOTA_USR_BIT;
             } else if (!strncmp(arg, "grp", 3)) {
-                quotatype = 1;
+                quotatype_bits = QUOTA_GRP_BIT;
             } else {
                 fprintf(stderr,
                     _("Invalid quotatype parameter: %s\n"),
@@ -2569,7 +2569,7 @@ static int create_quota_inodes(ext2_fils

     quota_init_context(&qctx, fs, -1);
     quota_compute_usage(qctx);
-    quota_write_inode(qctx, quotatype);
+    quota_write_inode(qctx, quotatype_bits);
     quota_release_context(&qctx);

     return 0;
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

Index: e2fsprogs.git/lib/e2p/ls.c
===================================================================
--- e2fsprogs.git.orig/lib/e2p/ls.c
+++ e2fsprogs.git/lib/e2p/ls.c
@@ -23,6 +23,7 @@ 
 #include <time.h>

 #include "e2p.h"
+#include "quota/quotaio.h"

 static void print_user (unsigned short uid, FILE *f)
 {
@@ -206,11 +207,27 @@  static const char *checksum_type(__u8 ty
     }
 }

+static const char const *quota_prefix[MAXQUOTAS] = {
+        [USRQUOTA] = "User quota inode:",
+        [GRPQUOTA] = "Group quota inode:",
+};
+
+/**
+ * Convert type of quota to written representation
+ */
+const char *quota_type2prefix(enum quota_type qtype)
+{
+    assert(qtype >= 0);
+    assert(qtype < MAXQUOTAS);
+    return quota_prefix[qtype];
+}
+
 void list_super2(struct ext2_super_block * sb, FILE *f)
 {
     int inode_blocks_per_group;
     char buf[80], *str;
     time_t    tm;
+    enum quota_type qtype;

     inode_blocks_per_group = (((sb->s_inodes_per_group *
                     EXT2_INODE_SIZE(sb)) +
@@ -434,13 +451,12 @@  void list_super2(struct ext2_super_block
         fprintf(f, "MMP update interval:      %u\n",
             sb->s_mmp_update_interval);
     }
-    if (sb->s_usr_quota_inum)
-        fprintf(f, "User quota inode:         %u\n",
-            sb->s_usr_quota_inum);
-    if (sb->s_grp_quota_inum)
-        fprintf(f, "Group quota inode:        %u\n",
-            sb->s_grp_quota_inum);
-
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+        if (*quota_sb_inump(sb, qtype) != 0)
+            fprintf(f, "%-26s%u\n",
+                quota_type2prefix(qtype),
+                *quota_sb_inump(sb, qtype));
+    }
     if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
         fprintf(f, "Checksum type:            %s\n",
             checksum_type(sb->s_checksum_type));
Index: e2fsprogs.git/lib/ext2fs/ext2_fs.h
===================================================================
--- e2fsprogs.git.orig/lib/ext2fs/ext2_fs.h
+++ e2fsprogs.git/lib/ext2fs/ext2_fs.h
@@ -666,8 +666,7 @@  struct ext2_super_block {
     __u8    s_last_error_func[32];    /* function where the error happened */
 #define EXT4_S_ERR_END ext4_offsetof(struct ext2_super_block, s_mount_opts)
     __u8    s_mount_opts[64];
-    __u32    s_usr_quota_inum;    /* inode number of user quota file */
-    __u32    s_grp_quota_inum;    /* inode number of group quota file */
+    __u32    s_quota_inum[2];    /* inode numbers of quota files */
     __u32    s_overhead_blocks;    /* overhead blocks/clusters in fs */
     __u32    s_backup_bgs[2];    /* If sparse_super2 enabled */
     __u32   s_reserved[106];        /* Padding to the end of the block */
Index: e2fsprogs.git/lib/quota/mkquota.c
===================================================================
--- e2fsprogs.git.orig/lib/quota/mkquota.c
+++ e2fsprogs.git/lib/quota/mkquota.c
@@ -51,7 +51,7 @@  static void print_inode(struct ext2_inod
  * Returns 0 if not able to find the quota file, otherwise returns its
  * inode number.
  */
-int quota_file_exists(ext2_filsys fs, int qtype, int fmt)
+int quota_file_exists(ext2_filsys fs, enum quota_type qtype, int fmt)
 {
     char qf_name[256];
     errcode_t ret;
@@ -73,12 +73,11 @@  int quota_file_exists(ext2_filsys fs, in
 /*
  * Set the value for reserved quota inode number field in superblock.
  */
-void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype)
+void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, enum quota_type qtype)
 {
     ext2_ino_t *inump;

-    inump = (qtype == USRQUOTA) ? &fs->super->s_usr_quota_inum :
-        &fs->super->s_grp_quota_inum;
+    inump = quota_sb_inump(fs->super, qtype);

     log_debug("setting quota ino in superblock: ino=%u, type=%d", ino,
          qtype);
@@ -86,7 +85,7 @@  void quota_set_sb_inum(ext2_filsys fs, e
     ext2fs_mark_super_dirty(fs);
 }

-errcode_t quota_remove_inode(ext2_filsys fs, int qtype)
+errcode_t quota_remove_inode(ext2_filsys fs, enum quota_type qtype)
 {
     ext2_ino_t qf_ino;
     errcode_t    retval;
@@ -96,8 +95,7 @@  errcode_t quota_remove_inode(ext2_filsys
         log_err("Couldn't read bitmaps: %s", error_message(retval));
         return retval;
     }
-    qf_ino = (qtype == USRQUOTA) ? fs->super->s_usr_quota_inum :
-        fs->super->s_grp_quota_inum;
+    qf_ino = *quota_sb_inump(fs->super, qtype);
     quota_set_sb_inum(fs, 0, qtype);
     /* Truncate the inode only if its a reserved one. */
     if (qf_ino < EXT2_FIRST_INODE(fs->super))
@@ -128,9 +126,10 @@  static void write_dquots(dict_t *dict, s
     }
 }

-errcode_t quota_write_inode(quota_ctx_t qctx, int qtype)
+errcode_t quota_write_inode(quota_ctx_t qctx, unsigned int qtype_bits)
 {
-    int        retval = 0, i;
+    int        retval = 0;
+    enum quota_type    qtype;
     dict_t        *dict;
     ext2_filsys    fs;
     struct quota_handle *h = NULL;
@@ -153,15 +152,15 @@  errcode_t quota_write_inode(quota_ctx_t
         goto out;
     }

-    for (i = 0; i < MAXQUOTAS; i++) {
-        if ((qtype != -1) && (i != qtype))
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+        if (((1 << qtype) & qtype_bits) == 0)
             continue;

-        dict = qctx->quota_dict[i];
+        dict = qctx->quota_dict[qtype];
         if (!dict)
             continue;

-        retval = quota_file_create(h, fs, i, fmt);
+        retval = quota_file_create(h, fs, qtype, fmt);
         if (retval < 0) {
             log_err("Cannot initialize io on quotafile");
             continue;
@@ -179,7 +178,7 @@  errcode_t quota_write_inode(quota_ctx_t
         }

         /* Set quota inode numbers in superblock. */
-        quota_set_sb_inum(fs, h->qh_qf.ino, i);
+        quota_set_sb_inum(fs, h->qh_qf.ino, qtype);
         ext2fs_mark_super_dirty(fs);
         ext2fs_mark_bb_dirty(fs);
         fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
@@ -210,11 +209,20 @@  static int dict_uint_cmp(const void *a,
     return c - d;
 }

-static inline qid_t get_qid(struct ext2_inode *inode, int qtype)
+static inline qid_t get_qid(struct ext2_inode *inode, enum quota_type qtype)
 {
-    if (qtype == USRQUOTA)
-        return inode_uid(*inode);
-    return inode_gid(*inode);
+    assert(qtype >= 0);
+    assert(qtype < MAXQUOTAS);
+    switch (qtype) {
+        case USRQUOTA:
+            return inode_uid(*inode);
+        case GRPQUOTA:
+            return inode_gid(*inode);
+        default:
+            return 0;
+    }
+
+    return 0;
 }

 static void quota_dnode_free(dnode_t *node,
@@ -229,9 +237,11 @@  static void quota_dnode_free(dnode_t *no
 /*
  * Set up the quota tracking data structures.
  */
-errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype)
+errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs,
+                 unsigned int qtype_bits)
 {
-    int    i, err = 0;
+    int        err = 0;
+    enum quota_type    qtype;
     dict_t    *dict;
     quota_ctx_t ctx;

@@ -242,8 +252,8 @@  errcode_t quota_init_context(quota_ctx_t
     }

     memset(ctx, 0, sizeof(struct quota_ctx));
-    for (i = 0; i < MAXQUOTAS; i++) {
-        if ((qtype != -1) && (i != qtype))
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+        if (((1 << qtype) & qtype_bits) == 0)
             continue;
         err = ext2fs_get_mem(sizeof(dict_t), &dict);
         if (err) {
@@ -251,7 +261,7 @@  errcode_t quota_init_context(quota_ctx_t
             quota_release_context(&ctx);
             return err;
         }
-        ctx->quota_dict[i] = dict;
+        ctx->quota_dict[qtype] = dict;
         dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp);
         dict_set_allocator(dict, NULL, quota_dnode_free, NULL);
     }
@@ -264,16 +274,16 @@  errcode_t quota_init_context(quota_ctx_t
 void quota_release_context(quota_ctx_t *qctx)
 {
     dict_t    *dict;
-    int    i;
+    enum quota_type    qtype;
     quota_ctx_t ctx;

     if (!qctx)
         return;

     ctx = *qctx;
-    for (i = 0; i < MAXQUOTAS; i++) {
-        dict = ctx->quota_dict[i];
-        ctx->quota_dict[i] = 0;
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+        dict = ctx->quota_dict[qtype];
+        ctx->quota_dict[qtype] = 0;
         if (dict) {
             dict_free_nodes(dict);
             free(dict);
@@ -312,7 +322,7 @@  void quota_data_add(quota_ctx_t qctx, st
 {
     struct dquot    *dq;
     dict_t        *dict;
-    int        i;
+    enum quota_type    qtype;

     if (!qctx)
         return;
@@ -320,10 +330,10 @@  void quota_data_add(quota_ctx_t qctx, st
     log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
             inode_uid(*inode),
             inode_gid(*inode), space);
-    for (i = 0; i < MAXQUOTAS; i++) {
-        dict = qctx->quota_dict[i];
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+        dict = qctx->quota_dict[qtype];
         if (dict) {
-            dq = get_dq(dict, get_qid(inode, i));
+            dq = get_dq(dict, get_qid(inode, qtype));
             if (dq)
                 dq->dq_dqb.dqb_curspace += space;
         }
@@ -338,7 +348,7 @@  void quota_data_sub(quota_ctx_t qctx, st
 {
     struct dquot    *dq;
     dict_t        *dict;
-    int        i;
+    enum quota_type    qtype;

     if (!qctx)
         return;
@@ -346,10 +356,10 @@  void quota_data_sub(quota_ctx_t qctx, st
     log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
             inode_uid(*inode),
             inode_gid(*inode), space);
-    for (i = 0; i < MAXQUOTAS; i++) {
-        dict = qctx->quota_dict[i];
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+        dict = qctx->quota_dict[qtype];
         if (dict) {
-            dq = get_dq(dict, get_qid(inode, i));
+            dq = get_dq(dict, get_qid(inode, qtype));
             dq->dq_dqb.dqb_curspace -= space;
         }
     }
@@ -363,7 +373,7 @@  void quota_data_inodes(quota_ctx_t qctx,
 {
     struct dquot    *dq;
     dict_t        *dict;
-    int        i;
+    enum quota_type    qtype;

     if (!qctx)
         return;
@@ -371,10 +381,10 @@  void quota_data_inodes(quota_ctx_t qctx,
     log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino,
             inode_uid(*inode),
             inode_gid(*inode), adjust);
-    for (i = 0; i < MAXQUOTAS; i++) {
-        dict = qctx->quota_dict[i];
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+        dict = qctx->quota_dict[qtype];
         if (dict) {
-            dq = get_dq(dict, get_qid(inode, i));
+            dq = get_dq(dict, get_qid(inode, qtype));
             dq->dq_dqb.dqb_curinodes += adjust;
         }
     }
@@ -504,7 +514,8 @@  static errcode_t quota_write_all_dquots(
 /*
  * Updates the in-memory quota limits from the given quota inode.
  */
-errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino, int type)
+errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino,
+                  enum quota_type qtype)
 {
     struct quota_handle *qh;
     errcode_t err;
@@ -518,7 +529,7 @@  errcode_t quota_update_limits(quota_ctx_
         return err;
     }

-    err = quota_file_open(qh, qctx->fs, qf_ino, type, -1, 0);
+    err = quota_file_open(qh, qctx->fs, qf_ino, qtype, -1, 0);
     if (err) {
         log_err("Open quota file failed");
         goto out;
@@ -543,7 +554,7 @@  out:
  * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is
  * set to 1 if the supplied and on-disk quota usage values are not identical.
  */
-errcode_t quota_compare_and_update(quota_ctx_t qctx, int qtype,
+errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype,
                    int *usage_inconsistent)
 {
     ext2_filsys fs = qctx->fs;
@@ -555,8 +566,7 @@  errcode_t quota_compare_and_update(quota
     if (!qctx->quota_dict[qtype])
         goto out;

-    qf_ino = qtype == USRQUOTA ? fs->super->s_usr_quota_inum :
-                     fs->super->s_grp_quota_inum;
+    qf_ino = *quota_sb_inump(fs->super, qtype);
     err = quota_file_open(&qh, fs, qf_ino, qtype, -1, 0);
     if (err) {
         log_err("Open quota file failed");
Index: e2fsprogs.git/e2fsck/pass1.c
===================================================================
--- e2fsprogs.git.orig/e2fsck/pass1.c
+++ e2fsprogs.git/e2fsck/pass1.c
@@ -589,6 +589,28 @@  static errcode_t recheck_bad_inode_check
     return 0;
 }

+static int quota_inum_is_super(struct ext2_super_block *sb, ext2_ino_t ino)
+{
+    enum quota_type qtype;
+
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++)
+        if (*quota_sb_inump(sb, qtype) == ino)
+            return 1;
+
+    return 0;
+}
+
+static int quota_inum_is_reserved(ext2_ino_t ino)
+{
+    enum quota_type qtype;
+
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++)
+        if (quota_type2inum(qtype) == ino)
+            return 1;
+
+    return 0;
+}
+
 void e2fsck_pass1(e2fsck_t ctx)
 {
     int    i;
@@ -1001,13 +1023,11 @@  void e2fsck_pass1(e2fsck_t ctx)
                 e2fsck_write_inode_full(ctx, ino, inode,
                             inode_size, "pass1");
             }
-        } else if ((ino == EXT4_USR_QUOTA_INO) ||
-               (ino == EXT4_GRP_QUOTA_INO)) {
+        } else if (quota_inum_is_reserved(ino)) {
             ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
             if ((fs->super->s_feature_ro_compat &
                     EXT4_FEATURE_RO_COMPAT_QUOTA) &&
-                ((fs->super->s_usr_quota_inum == ino) ||
-                 (fs->super->s_grp_quota_inum == ino))) {
+                quota_inum_is_super(fs->super, ino)) {
                 if (!LINUX_S_ISREG(inode->i_mode) &&
                     fix_problem(ctx, PR_1_QUOTA_BAD_MODE,
                             &pctx)) {
Index: e2fsprogs.git/e2fsck/quota.c
===================================================================
--- e2fsprogs.git.orig/e2fsck/quota.c
+++ e2fsprogs.git/e2fsck/quota.c
@@ -19,7 +19,7 @@ 
 #include "quota/quotaio.h"

 static void move_quota_inode(ext2_filsys fs, ext2_ino_t from_ino,
-                 ext2_ino_t to_ino, int qtype)
+                 ext2_ino_t to_ino, enum quota_type qtype)
 {
     struct ext2_inode    inode;
     errcode_t        retval;
@@ -64,6 +64,8 @@  void e2fsck_hide_quota(e2fsck_t ctx)
     struct ext2_super_block *sb = ctx->fs->super;
     struct problem_context    pctx;
     ext2_filsys        fs = ctx->fs;
+    enum quota_type qtype;
+    ext2_ino_t quota_ino;

     clear_problem_context(&pctx);

@@ -71,22 +73,14 @@  void e2fsck_hide_quota(e2fsck_t ctx)
         !(sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_QUOTA))
         return;

-    pctx.ino = sb->s_usr_quota_inum;
-    if (sb->s_usr_quota_inum &&
-        (sb->s_usr_quota_inum != EXT4_USR_QUOTA_INO) &&
-        fix_problem(ctx, PR_0_HIDE_QUOTA, &pctx)) {
-        move_quota_inode(fs, sb->s_usr_quota_inum, EXT4_USR_QUOTA_INO,
-                 USRQUOTA);
-        sb->s_usr_quota_inum = EXT4_USR_QUOTA_INO;
-    }
-
-    pctx.ino = sb->s_grp_quota_inum;
-    if (sb->s_grp_quota_inum &&
-        (sb->s_grp_quota_inum != EXT4_GRP_QUOTA_INO) &&
-        fix_problem(ctx, PR_0_HIDE_QUOTA, &pctx)) {
-        move_quota_inode(fs, sb->s_grp_quota_inum, EXT4_GRP_QUOTA_INO,
-                 GRPQUOTA);
-        sb->s_grp_quota_inum = EXT4_GRP_QUOTA_INO;
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+        pctx.ino = *quota_sb_inump(sb, qtype);
+        quota_ino = quota_type2inum(qtype);
+        if (pctx.ino && (pctx.ino != quota_ino) &&
+            fix_problem(ctx, PR_0_HIDE_QUOTA, &pctx)) {
+            move_quota_inode(fs, pctx.ino, quota_ino, qtype);
+            *quota_sb_inump(sb, qtype) = quota_ino;
+        }
     }

     return;
Index: e2fsprogs.git/e2fsck/unix.c
===================================================================
--- e2fsprogs.git.orig/e2fsck/unix.c
+++ e2fsprogs.git/e2fsck/unix.c
@@ -1182,7 +1182,8 @@  int main (int argc, char *argv[])
     int old_bitmaps;
     __u32 features[3];
     char *cp;
-    int qtype = -99;  /* quota type */
+    unsigned int qtype_bits = 0;
+    enum quota_type qtype;

     clear_problem_context(&pctx);
     sigcatcher_setup();
@@ -1625,13 +1626,12 @@  print_unsupp_features:

     if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_QUOTA) {
         /* Quotas were enabled. Do quota accounting during fsck. */
-        if ((sb->s_usr_quota_inum && sb->s_grp_quota_inum) ||
-            (!sb->s_usr_quota_inum && !sb->s_grp_quota_inum))
-            qtype = -1;
-        else
-            qtype = sb->s_usr_quota_inum ? USRQUOTA : GRPQUOTA;
+        for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+            if (*quota_sb_inump(sb, qtype) != 0)
+                qtype_bits |= 1 << qtype;
+        }

-        quota_init_context(&ctx->qctx, ctx->fs, qtype);
+        quota_init_context(&ctx->qctx, ctx->fs, qtype_bits);
     }

     run_result = e2fsck_run(ctx);
@@ -1668,17 +1668,17 @@  print_unsupp_features:
 no_journal:

     if (ctx->qctx) {
-        int i, needs_writeout;
-        for (i = 0; i < MAXQUOTAS; i++) {
-            if (qtype != -1 && qtype != i)
+        int needs_writeout;
+        for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+            if (((1 << qtype) & qtype_bits) == 0)
                 continue;
             needs_writeout = 0;
-            pctx.num = i;
-            retval = quota_compare_and_update(ctx->qctx, i,
+            pctx.num = qtype;
+            retval = quota_compare_and_update(ctx->qctx, qtype,
                               &needs_writeout);
             if ((retval || needs_writeout) &&
                 fix_problem(ctx, PR_6_UPDATE_QUOTAS, &pctx))
-                quota_write_inode(ctx->qctx, i);
+                quota_write_inode(ctx->qctx, 1 << qtype);
         }
         quota_release_context(&ctx->qctx);
     }
Index: e2fsprogs.git/lib/quota/mkquota.h
===================================================================
--- e2fsprogs.git.orig/lib/quota/mkquota.h
+++ e2fsprogs.git/lib/quota/mkquota.h
@@ -10,14 +10,13 @@ 
  * {
  *    quota_ctx_t qctx;
  *
- *    quota_init_context(&qctx, fs, -1);
+ *    quota_init_context(&qctx, fs, QUOTA_ALL_BIT);
  *    {
  *        quota_compute_usage(qctx, -1);
  *        AND/OR
  *        quota_data_add/quota_data_sub/quota_data_inodes();
  *    }
- *    quota_write_inode(qctx, USRQUOTA);
- *    quota_write_inode(qctx, GRPQUOTA);
+ *    quota_write_inode(qctx, QUOTA_ALL_BIT);
  *    quota_release_context(&qctx);
  * }
  *
@@ -43,22 +42,24 @@  struct quota_ctx {
 };

 /* In mkquota.c */
-errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype);
+errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs,
+                 unsigned int qtype_bits);
 void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode,
ext2_ino_t ino,
         int adjust);
 void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
         qsize_t space);
 void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
         qsize_t space);
-errcode_t quota_write_inode(quota_ctx_t qctx, int qtype);
-errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino, int type);
+errcode_t quota_write_inode(quota_ctx_t qctx, unsigned int qtype_bits);
+errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino,
+                  enum quota_type qtype);
 errcode_t quota_compute_usage(quota_ctx_t qctx);
 void quota_release_context(quota_ctx_t *qctx);

-errcode_t quota_remove_inode(ext2_filsys fs, int qtype);
-int quota_file_exists(ext2_filsys fs, int qtype, int fmt);
-void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype);
-errcode_t quota_compare_and_update(quota_ctx_t qctx, int qtype,
+errcode_t quota_remove_inode(ext2_filsys fs, enum quota_type qtype);
+int quota_file_exists(ext2_filsys fs, enum quota_type qtype, int fmt);
+void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, enum quota_type qtype);
+errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype,
                    int *usage_inconsistent);

 #endif  /* __QUOTA_QUOTAIO_H__ */
Index: e2fsprogs.git/lib/quota/quotaio.c
===================================================================
--- e2fsprogs.git.orig/lib/quota/quotaio.c
+++ e2fsprogs.git/lib/quota/quotaio.c
@@ -15,6 +15,7 @@ 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/file.h>
+#include <assert.h>

 #include "common.h"
 #include "quotaio.h"
@@ -37,15 +38,35 @@  struct disk_dqheader {
 /**
  * Convert type of quota to written representation
  */
-const char *type2name(int type)
+const char *quota_type2name(enum quota_type qtype)
 {
-    return extensions[type];
+    assert(qtype >= 0);
+    assert(qtype < MAXQUOTAS);
+    return extensions[qtype];
+}
+
+ext2_ino_t quota_type2inum(enum quota_type qtype)
+{
+    assert(qtype >= 0);
+    assert(qtype < MAXQUOTAS);
+    switch (qtype) {
+    case USRQUOTA:
+        return EXT4_USR_QUOTA_INO;
+        break;
+    case GRPQUOTA:
+        return EXT4_GRP_QUOTA_INO;
+        break;
+    default:
+        return 0;
+        break;
+    }
+    return 0;
 }

 /**
  * Creates a quota file name for given type and format.
  */
-const char *quota_get_qf_name(int type, int fmt, char *buf)
+const char *quota_get_qf_name(enum quota_type type, int fmt, char *buf)
 {
     if (!buf)
         return NULL;
@@ -55,7 +76,7 @@  const char *quota_get_qf_name(int type,
     return buf;
 }

-const char *quota_get_qf_path(const char *mntpt, int qtype, int fmt,
+const char *quota_get_qf_path(const char *mntpt, enum quota_type
qtype, int fmt,
                   char *path_buf, size_t path_buf_size)
 {
     char qf_name[QUOTA_NAME_LEN];
@@ -114,11 +135,16 @@  errcode_t quota_inode_truncate(ext2_fils
 {
     struct ext2_inode inode;
     errcode_t err;
+    enum quota_type qtype;

     if ((err = ext2fs_read_inode(fs, ino, &inode)))
         return err;

-    if ((ino == EXT4_USR_QUOTA_INO) || (ino == EXT4_GRP_QUOTA_INO)) {
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++)
+        if (ino == quota_type2inum(qtype))
+            break;
+
+    if (qtype != MAXQUOTAS) {
         inode.i_dtime = fs->now ? fs->now : time(0);
         if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
             return 0;
@@ -198,7 +224,8 @@  static unsigned int quota_read_nomount(s
  * Detect quota format and initialize quota IO
  */
 errcode_t quota_file_open(struct quota_handle *h, ext2_filsys fs,
-              ext2_ino_t qf_ino, int type, int fmt, int flags)
+              ext2_ino_t qf_ino, enum quota_type qtype, int fmt,
+              int flags)
 {
     ext2_file_t e2_file;
     errcode_t err;
@@ -223,13 +250,13 @@  errcode_t quota_file_open(struct quota_h
     h->e2fs_write = quota_write_nomount;
     h->e2fs_read = quota_read_nomount;
     h->qh_io_flags = 0;
-    h->qh_type = type;
+    h->qh_type = qtype;
     h->qh_fmt = fmt;
     memset(&h->qh_info, 0, sizeof(h->qh_info));
     h->qh_ops = &quotafile_ops_2;

     if (h->qh_ops->check_file &&
-        (h->qh_ops->check_file(h, type, fmt) == 0)) {
+        (h->qh_ops->check_file(h, qtype, fmt) == 0)) {
         log_err("qh_ops->check_file failed");
         ext2fs_file_close(e2_file);
         return -1;
@@ -280,7 +307,8 @@  static errcode_t quota_inode_init_new(ex
 /*
  * Create new quotafile of specified format on given filesystem
  */
-errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs,
int type, int fmt)
+errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs,
+                enum quota_type qtype, int fmt)
 {
     ext2_file_t e2_file;
     int err;
@@ -290,11 +318,8 @@  errcode_t quota_file_create(struct quota
         fmt = QFMT_VFS_V1;

     h->qh_qf.fs = fs;
-    if (type == USRQUOTA)
-        qf_inum = EXT4_USR_QUOTA_INO;
-    else if (type == GRPQUOTA)
-        qf_inum = EXT4_GRP_QUOTA_INO;
-    else
+    qf_inum = quota_type2inum(qtype);
+    if (qf_inum == 0)
         return -1;

     err = ext2fs_read_bitmaps(fs);
@@ -310,7 +335,7 @@  errcode_t quota_file_create(struct quota
     h->e2fs_write = quota_write_nomount;
     h->e2fs_read = quota_read_nomount;

-    log_debug("Creating quota ino=%lu, type=%d", qf_inum, type);
+    log_debug("Creating quota ino=%lu, type=%d", qf_inum, qtype);
     err = ext2fs_file_open(fs, qf_inum,
             EXT2_FILE_WRITE | EXT2_FILE_CREATE, &e2_file);
     if (err) {
@@ -320,7 +345,7 @@  errcode_t quota_file_create(struct quota
     h->qh_qf.e2_file = e2_file;

     h->qh_io_flags = 0;
-    h->qh_type = type;
+    h->qh_type = qtype;
     h->qh_fmt = fmt;
     memset(&h->qh_info, 0, sizeof(h->qh_info));
     h->qh_ops = &quotafile_ops_2;
Index: e2fsprogs.git/lib/quota/quotaio.h
===================================================================
--- e2fsprogs.git.orig/lib/quota/quotaio.h
+++ e2fsprogs.git/lib/quota/quotaio.h
@@ -16,9 +16,20 @@ 

 typedef int64_t qsize_t;    /* Type in which we store size limitations */

+enum quota_type {
+    USRQUOTA = 0,
+    GRPQUOTA = 1,
+};
+
 #define MAXQUOTAS 2
-#define USRQUOTA 0
-#define GRPQUOTA 1
+
+#if MAXQUOTAS > 32
+#error "cannot have more than 32 quota types to fit in qtype_bits"
+#endif
+
+#define QUOTA_USR_BIT (1 << USRQUOTA)
+#define QUOTA_GRP_BIT (1 << GRPQUOTA)
+#define QUOTA_ALL_BIT (QUOTA_USR_BIT | QUOTA_GRP_BIT)

 /*
  * Definitions of magics and versions of current quota files
@@ -68,7 +79,7 @@  struct quota_file {

 /* Structure for one opened quota file */
 struct quota_handle {
-    int qh_type;        /* Type of quotafile */
+    enum quota_type qh_type;    /* Type of quotafile */
     int qh_fmt;        /* Quotafile format */
     int qh_io_flags;    /* IO flags for file */
     struct quota_file qh_qf;
@@ -135,12 +146,13 @@  extern struct quotafile_ops quotafile_op
 /* Open existing quotafile of given type (and verify its format) on given
  * filesystem. */
 errcode_t quota_file_open(struct quota_handle *h, ext2_filsys fs,
-              ext2_ino_t qf_ino, int type, int fmt, int flags);
+              ext2_ino_t qf_ino, enum quota_type type, int fmt,
+              int flags);


 /* Create new quotafile of specified format on given filesystem */
 errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs,
-                int type, int fmt);
+                enum quota_type qtype, int fmt);

 /* Close quotafile */
 errcode_t quota_file_close(struct quota_handle *h);
@@ -150,7 +162,8 @@  struct dquot *get_empty_dquot(void);

 errcode_t quota_inode_truncate(ext2_filsys fs, ext2_ino_t ino);

-const char *type2name(int type);
+const char *quota_type2name(enum quota_type qtype);
+ext2_ino_t quota_type2inum(enum quota_type qtype);

 void update_grace_times(struct dquot *q);

@@ -158,8 +171,31 @@  void update_grace_times(struct dquot *q)
    than maxlen of extensions[] and fmtnames[] (plus 2) found in quotaio.c */
 #define QUOTA_NAME_LEN 16

-const char *quota_get_qf_name(int type, int fmt, char *buf);
-const char *quota_get_qf_path(const char *mntpt, int qtype, int fmt,
+const char *quota_get_qf_name(enum quota_type type, int fmt, char *buf);
+const char *quota_get_qf_path(const char *mntpt, enum quota_type
qtype, int fmt,
                   char *path_buf, size_t path_buf_size);

+#include <assert.h>
+
+static inline ext2_ino_t *quota_sb_inump(struct ext2_super_block *sb,
enum quota_type qtype)
+{
+    assert(qtype >= 0);
+    assert(qtype < MAXQUOTAS);
+    switch (qtype) {
+        case USRQUOTA:
+            return &sb->s_quota_inum[0];
+        case GRPQUOTA:
+            return &sb->s_quota_inum[1];
+        /* Skip s_overhead_blocks like this */
+        /*
+        case PRJQUOTA:
+            return &sb->s_quota_inum[3];
+        */
+        default:
+            return NULL;
+    }
+
+    return NULL;
+}
+
 #endif /* GUARD_QUOTAIO_H */
Index: e2fsprogs.git/misc/tune2fs.c
===================================================================
--- e2fsprogs.git.orig/misc/tune2fs.c
+++ e2fsprogs.git/misc/tune2fs.c
@@ -94,7 +94,7 @@  static int stride_set, stripe_width_set;
 static char *extended_cmd;
 static unsigned long new_inode_size;
 static char *ext_mount_opts;
-static int usrquota, grpquota;
+static int quota_enable[MAXQUOTAS];
 static int rewrite_checksums;

 int journal_size, journal_flags;
@@ -875,6 +875,7 @@  static int update_feature_set(ext2_filsy
     __u32        old_features[3];
     int        type_err;
     unsigned int    mask_err;
+    enum quota_type    qtype;

 #define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \
                 ((&sb->s_feature_compat)[(type)] & (mask)))
@@ -1121,9 +1122,9 @@  mmp_error:
          */
         if (!Q_flag) {
             Q_flag = 1;
-            /* Enable both user quota and group quota by default */
-            usrquota = QOPT_ENABLE;
-            grpquota = QOPT_ENABLE;
+            /* Enable all quota by default */
+            for (qtype = 0; qtype < MAXQUOTAS; qtype++)
+                quota_enable[qtype] = QOPT_ENABLE;
         }
         sb->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA;
     }
@@ -1138,9 +1139,9 @@  mmp_error:
             fputs(_("\nWarning: '^quota' option overrides '-Q'"
                 "arguments.\n"), stderr);
         Q_flag = 1;
-        /* Disable both user quota and group quota by default */
-        usrquota = QOPT_DISABLE;
-        grpquota = QOPT_DISABLE;
+        /* Disable all quota by default */
+        for (qtype = 0; qtype < MAXQUOTAS; qtype++)
+            quota_enable[qtype] = QOPT_DISABLE;
     }

     if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
@@ -1254,47 +1255,55 @@  static void handle_quota_options(ext2_fi
 {
     quota_ctx_t qctx;
     ext2_ino_t qf_ino;
+    enum quota_type qtype;
+    int enable = 0;

-    if (!usrquota && !grpquota)
+    for (qtype = 0 ; qtype < MAXQUOTAS; qtype++)
+        if (quota_enable[qtype] != 0)
+            break;
+    if (qtype == MAXQUOTAS)
         /* Nothing to do. */
         return;

-    quota_init_context(&qctx, fs, -1);
-
-    if (usrquota == QOPT_ENABLE || grpquota == QOPT_ENABLE)
+    quota_init_context(&qctx, fs, QUOTA_ALL_BIT);
+    for (qtype = 0 ; qtype < MAXQUOTAS; qtype++) {
+        if (quota_enable[qtype] == QOPT_ENABLE) {
+            enable = 1;
+            break;
+        }
+    }
+    if (enable)
         quota_compute_usage(qctx);

-    if (usrquota == QOPT_ENABLE && !fs->super->s_usr_quota_inum) {
-        if ((qf_ino = quota_file_exists(fs, USRQUOTA,
-                        QFMT_VFS_V1)) > 0)
-            quota_update_limits(qctx, qf_ino, USRQUOTA);
-        quota_write_inode(qctx, USRQUOTA);
-    } else if (usrquota == QOPT_DISABLE) {
-        quota_remove_inode(fs, USRQUOTA);
-    }
-
-    if (grpquota == QOPT_ENABLE && !fs->super->s_grp_quota_inum) {
-        if ((qf_ino = quota_file_exists(fs, GRPQUOTA,
-                        QFMT_VFS_V1)) > 0)
-            quota_update_limits(qctx, qf_ino, GRPQUOTA);
-        quota_write_inode(qctx, GRPQUOTA);
-    } else if (grpquota == QOPT_DISABLE) {
-        quota_remove_inode(fs, GRPQUOTA);
+    for (qtype = 0 ; qtype < MAXQUOTAS; qtype++) {
+        if (quota_enable[qtype] == QOPT_ENABLE &&
+            *quota_sb_inump(fs->super, qtype) != 0) {
+            if ((qf_ino = quota_file_exists(fs, qtype,
+                            QFMT_VFS_V1)) > 0)
+                quota_update_limits(qctx, qf_ino, qtype);
+            quota_write_inode(qctx, 1 << qtype);
+        } else if (quota_enable[qtype] == QOPT_DISABLE) {
+            quota_remove_inode(fs, qtype);
+        }
     }

     quota_release_context(&qctx);

-    if ((usrquota == QOPT_ENABLE) || (grpquota == QOPT_ENABLE)) {
+    if (enable) {
         fprintf(stderr, "%s", _("\nWarning: the quota feature is still "
                   "under development\n"
                   "See https://ext4.wiki.kernel.org/"
                   "index.php/Quota for more information\n\n"));
         fs->super->s_feature_ro_compat |= EXT4_FEATURE_RO_COMPAT_QUOTA;
         ext2fs_mark_super_dirty(fs);
-    } else if (!fs->super->s_usr_quota_inum &&
-           !fs->super->s_grp_quota_inum) {
-        fs->super->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA;
-        ext2fs_mark_super_dirty(fs);
+    } else {
+        for (qtype = 0 ; qtype < MAXQUOTAS; qtype++)
+            if (*quota_sb_inump(fs->super, qtype) != 0)
+                break;
+        if (qtype == MAXQUOTAS) {
+            fs->super->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA;
+            ext2fs_mark_super_dirty(fs);
+        }
     }

     return;
@@ -1323,13 +1332,13 @@  static void parse_quota_opts(const char
         }

         if (strcmp(token, "usrquota") == 0) {
-            usrquota = QOPT_ENABLE;
+            quota_enable[USRQUOTA] = QOPT_ENABLE;
         } else if (strcmp(token, "^usrquota") == 0) {
-            usrquota = QOPT_DISABLE;
+            quota_enable[USRQUOTA] = QOPT_DISABLE;
         } else if (strcmp(token, "grpquota") == 0) {
-            grpquota = QOPT_ENABLE;
+            quota_enable[GRPQUOTA] = QOPT_ENABLE;
         } else if (strcmp(token, "^grpquota") == 0) {
-            grpquota = QOPT_DISABLE;
+            quota_enable[GRPQUOTA] = QOPT_DISABLE;
         } else {
             fputs(_("\nBad quota options specified.\n\n"
                 "Following valid quota options are available "
Index: e2fsprogs.git/debugfs/set_fields.c
===================================================================
--- e2fsprogs.git.orig/debugfs/set_fields.c
+++ e2fsprogs.git/debugfs/set_fields.c
@@ -39,6 +39,7 @@ 
 #include "debugfs.h"
 #include "uuid/uuid.h"
 #include "e2p/e2p.h"
+#include "quota/quotaio.h"

 static struct ext2_super_block set_sb;
 static struct ext2_inode_large set_inode;
@@ -147,8 +148,8 @@  static struct field_set_info super_field
       NULL, 8, parse_uint },
     { "snapshot_list", &set_sb.s_snapshot_list, NULL, 4, parse_uint },
     { "mount_opts",  &set_sb.s_mount_opts, NULL, 64, parse_string },
-    { "usr_quota_inum", &set_sb.s_usr_quota_inum, NULL, 4, parse_uint },
-    { "grp_quota_inum", &set_sb.s_grp_quota_inum, NULL, 4, parse_uint },
+    { "usr_quota_inum", &set_sb.s_quota_inum[0], NULL, 4, parse_uint },
+    { "grp_quota_inum", &set_sb.s_quota_inum[1], NULL, 4, parse_uint },
     { "overhead_blocks", &set_sb.s_overhead_blocks, NULL, 4, parse_uint },
     { "backup_bgs", &set_sb.s_backup_bgs[0], NULL, 4, parse_uint,
       FLAG_ARRAY, 2 },
Index: e2fsprogs.git/lib/ext2fs/tst_super_size.c
===================================================================
--- e2fsprogs.git.orig/lib/ext2fs/tst_super_size.c
+++ e2fsprogs.git/lib/ext2fs/tst_super_size.c
@@ -132,8 +132,7 @@  int main(int argc, char **argv)
     check_field(s_last_error_block, 8);
     check_field(s_last_error_func, 32);
     check_field(s_mount_opts, 64);
-    check_field(s_usr_quota_inum, 4);
-    check_field(s_grp_quota_inum, 4);
+    check_field(s_quota_inum, 4 * 2);
     check_field(s_overhead_blocks, 4);
     check_field(s_backup_bgs, 8);
     check_field(s_reserved, 106 * 4);
Index: e2fsprogs.git/lib/quota/quotaio_tree.c
===================================================================
--- e2fsprogs.git.orig/lib/quota/quotaio_tree.c
+++ e2fsprogs.git/lib/quota/quotaio_tree.c
@@ -587,7 +587,7 @@  static void check_reference(struct quota
             "Please run e2fsck (8) to fix it.",
             blk,
             h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
-            type2name(h->qh_type));
+            quota_type2name(h->qh_type));
 }

 static int report_tree(struct dquot *dquot, unsigned int blk, int depth,
Index: e2fsprogs.git/misc/mke2fs.c
===================================================================
--- e2fsprogs.git.orig/misc/mke2fs.c
+++ e2fsprogs.git/misc/mke2fs.c
@@ -93,7 +93,7 @@  static int    lazy_itable_init;
 static int    packed_meta_blocks;
 static char    *bad_blocks_filename = NULL;
 static __u32    fs_stride;
-static int    quotatype = -1;  /* Initialize both user and group
quotas by default */
+static unsigned int quotatype_bits = QUOTA_ALL_BIT;  /* Initialize
all quotas by default */
 static __u64    offset;