All of lore.kernel.org
 help / color / mirror / Atom feed
* [v3 0/5] Add project quota support for e2fsprogs
@ 2016-01-12  8:56 Li Xi
  2016-01-12  8:56 ` [v3 1/5] Clean up codes for adding new quota type Li Xi
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Li Xi @ 2016-01-12  8:56 UTC (permalink / raw)
  To: linux-ext4, tytso, adilger, jack, viro, hch, dmonakhov

The following patches adds project quota support for e2fsprogs. The
first patch cleans up current codes to prepare for adding project
quota support. And other patches adds project ID as well as project
feature support for e2fsprogs.

Li Xi (5):
  Clean up codes for adding new quota type
  Add project feature flag EXT4_FEATURE_RO_COMPAT_PROJECT
  Add project quota support
  Add inherit flags for project quota
  Add project ID support for chattr/lsattr

 debugfs/quota.c                 |    2 +-
 debugfs/set_fields.c            |    2 +
 e2fsck/pass1.c                  |   46 +++++++++---
 e2fsck/pass4.c                  |    3 +-
 e2fsck/quota.c                  |   28 +++----
 e2fsck/unix.c                   |   26 +++---
 lib/e2p/Makefile.in             |   11 ++-
 lib/e2p/e2p.h                   |    2 +
 lib/e2p/feature.c               |    2 +
 lib/e2p/ls.c                    |   28 +++++--
 lib/e2p/pf.c                    |    1 +
 lib/ext2fs/ext2_fs.h            |   12 ++-
 lib/ext2fs/ext2fs.h             |    3 +-
 lib/ext2fs/swapfs.c             |    2 +
 lib/ext2fs/tst_inode_size.c     |    1 +
 lib/ext2fs/tst_super_size.c     |    3 +-
 lib/support/mkquota.c           |  167 ++++++++++++++++++++++++++------------
 lib/support/quotaio.c           |   85 ++++++++++++++------
 lib/support/quotaio.h           |   76 +++++++++++++-----
 lib/support/quotaio_tree.c      |    2 +-
 misc/chattr.1.in                |    7 ++
 misc/chattr.c                   |   34 ++++++++-
 misc/ext4.5.in                  |    5 +
 misc/lsattr.1.in                |    5 +-
 misc/lsattr.c                   |   18 ++++-
 misc/mke2fs.c                   |   54 ++++++++++---
 misc/tune2fs.8.in               |    3 +
 misc/tune2fs.c                  |  159 ++++++++++++++++++++-----------------
 tests/d_fallocate_blkmap/expect |    4 +-
 tests/f_create_symlinks/expect  |    8 +-
 tests/m_bigjournal/expect.1     |    4 +-
 tests/m_large_file/expect.1     |    4 +-
 tests/m_quota/expect.1          |   17 ++--
 tests/m_quota/script            |    2 +-
 34 files changed, 559 insertions(+), 267 deletions(-)


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [v3 1/5] Clean up codes for adding new quota type
  2016-01-12  8:56 [v3 0/5] Add project quota support for e2fsprogs Li Xi
@ 2016-01-12  8:56 ` Li Xi
  2016-01-12  8:56 ` [v3 2/5] Add project feature flag EXT4_FEATURE_RO_COMPAT_PROJECT Li Xi
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Li Xi @ 2016-01-12  8:56 UTC (permalink / raw)
  To: linux-ext4, tytso, adilger, jack, viro, hch, dmonakhov

Project quota related fields are reserved in Linux kernel.
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).

Signed-off-by: Li Xi <lixi@ddn.com>
Signed-off-by: Wang Shilong <wshilong@ddn.com>
---
 debugfs/quota.c            |    2 +-
 debugfs/set_fields.c       |    1 +
 e2fsck/pass1.c             |   28 ++++++++-
 e2fsck/quota.c             |   28 ++++------
 e2fsck/unix.c              |   26 ++++----
 lib/e2p/ls.c               |   27 +++++++--
 lib/support/mkquota.c      |  127 ++++++++++++++++++++++++++--------------
 lib/support/quotaio.c      |   66 +++++++++++++--------
 lib/support/quotaio.h      |   69 +++++++++++++++-------
 lib/support/quotaio_tree.c |    2 +-
 misc/mke2fs.c              |   34 +++++++----
 misc/tune2fs.c             |  138 +++++++++++++++++++++-----------------------
 12 files changed, 332 insertions(+), 216 deletions(-)

diff --git a/debugfs/quota.c b/debugfs/quota.c
index 8c94486..9b8dbaf 100644
--- a/debugfs/quota.c
+++ b/debugfs/quota.c
@@ -42,7 +42,7 @@ static int load_quota_ctx(char *progname)
 	if (current_qctx)
 		return 0;
 
-	retval = quota_init_context(&current_qctx, current_fs, -1);
+	retval = quota_init_context(&current_qctx, current_fs, QUOTA_ALL_BIT);
 	if (retval) {
 		com_err(current_fs->device_name, retval,
 			"while trying to load quota information");
diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index ec2340d..bc63802 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -40,6 +40,7 @@
 #include "debugfs.h"
 #include "uuid/uuid.h"
 #include "e2p/e2p.h"
+#include "support/quotaio.h"
 
 static struct ext2_super_block set_sb;
 static struct ext2_inode_large set_inode;
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 5a8d4a1..9a987d2 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -954,6 +954,28 @@ out:
 	}
 }
 
+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;
@@ -1502,12 +1524,10 @@ void e2fsck_pass1(e2fsck_t ctx)
 							inode_size, "pass1");
 				failed_csum = 0;
 			}
-		} 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 (ext2fs_has_feature_quota(fs->super) &&
-			    ((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)) {
diff --git a/e2fsck/quota.c b/e2fsck/quota.c
index 4c431f8..bc4cc07 100644
--- a/e2fsck/quota.c
+++ b/e2fsck/quota.c
@@ -17,7 +17,7 @@
 #include "problem.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;
@@ -61,6 +61,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);
 
@@ -68,22 +70,14 @@ void e2fsck_hide_quota(e2fsck_t ctx)
 	    !ext2fs_has_feature_quota(sb))
 		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;
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 51bcd69..8899f28 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1338,7 +1338,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();
@@ -1778,13 +1779,12 @@ print_unsupp_features:
 
 	if (ext2fs_has_feature_quota(sb)) {
 		/* 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);
@@ -1826,18 +1826,18 @@ no_journal:
 			ctx->device_name : ctx->filesystem_name);
 		exit_value |= FSCK_CANCELED;
 	} else if (ctx->qctx && !ctx->invalid_bitmaps) {
-		int i, needs_writeout;
+		int needs_writeout;
 
-		for (i = 0; i < MAXQUOTAS; i++) {
-			if (qtype != -1 && qtype != i)
+		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);
 	}
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index 6c82857..51f0ab1 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -23,6 +23,7 @@
 #include <time.h>
 
 #include "e2p.h"
+#include "support/quotaio.h"
 
 static void print_user (unsigned short uid, FILE *f)
 {
@@ -206,11 +207,25 @@ static const char *checksum_type(__u8 type)
 	}
 }
 
+static const char *quota_prefix[MAXQUOTAS] = {
+	[USRQUOTA] = "User quota inode:",
+	[GRPQUOTA] = "Group quota inode:",
+};
+
+/**
+ * Convert type of quota to written representation
+ */
+static const char *quota_type2prefix(enum quota_type qtype)
+{
+	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,12 +449,12 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
 		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 (ext2fs_has_feature_metadata_csum(sb)) {
 		fprintf(f, "Checksum type:            %s\n",
diff --git a/lib/support/mkquota.c b/lib/support/mkquota.c
index 00e96f8..2c7f436 100644
--- a/lib/support/mkquota.c
+++ b/lib/support/mkquota.c
@@ -67,7 +67,7 @@ static void print_dquot(const char *desc EXT2FS_ATTR((unused)),
  * 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 quota_file_exists(ext2_filsys fs, enum quota_type qtype)
 {
 	char qf_name[256];
 	errcode_t ret;
@@ -89,12 +89,11 @@ int quota_file_exists(ext2_filsys fs, int qtype)
 /*
  * 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);
@@ -102,7 +101,7 @@ void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype)
 	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;
@@ -112,8 +111,7 @@ errcode_t quota_remove_inode(ext2_filsys fs, int qtype)
 		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))
@@ -145,9 +143,10 @@ static void write_dquots(dict_t *dict, struct quota_handle *qh)
 	}
 }
 
-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;
@@ -170,15 +169,15 @@ errcode_t quota_write_inode(quota_ctx_t qctx, int qtype)
 		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;
@@ -196,7 +195,7 @@ errcode_t quota_write_inode(quota_ctx_t qctx, int qtype)
 		}
 
 		/* 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;
@@ -232,11 +231,18 @@ static int dict_uint_cmp(const void *a, const void *b)
 		return -1;
 }
 
-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)
+	switch (qtype) {
+	case USRQUOTA:
 		return inode_uid(*inode);
-	return inode_gid(*inode);
+	case GRPQUOTA:
+		return inode_gid(*inode);
+	default:
+		return 0;
+	}
+
+	return 0;
 }
 
 static void quota_dnode_free(dnode_t *node,
@@ -251,12 +257,13 @@ static void quota_dnode_free(dnode_t *node,
 /*
  * 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)
 {
 	errcode_t err;
 	dict_t	*dict;
 	quota_ctx_t ctx;
-	int	i;
+	enum quota_type	qtype;
 
 	err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx);
 	if (err) {
@@ -265,9 +272,9 @@ errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype)
 	}
 
 	memset(ctx, 0, sizeof(struct quota_ctx));
-	for (i = 0; i < MAXQUOTAS; i++) {
-		ctx->quota_file[i] = NULL;
-		if ((qtype != -1) && (i != qtype))
+	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+		ctx->quota_file[qtype] = NULL;
+		if (((1 << qtype) & qtype_bits) == 0)
 			continue;
 		err = ext2fs_get_mem(sizeof(dict_t), &dict);
 		if (err) {
@@ -275,7 +282,7 @@ errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype)
 			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);
 	}
@@ -289,26 +296,26 @@ void quota_release_context(quota_ctx_t *qctx)
 {
 	errcode_t err;
 	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);
 		}
-		if (ctx->quota_file[i]) {
-			err = quota_file_close(ctx, ctx->quota_file[i]);
+		if (ctx->quota_file[qtype]) {
+			err = quota_file_close(ctx, ctx->quota_file[qtype]);
 			if (err) {
 				log_err("Cannot close quotafile: %s",
 					strerror(errno));
-				ext2fs_free_mem(&ctx->quota_file[i]);
+				ext2fs_free_mem(&ctx->quota_file[qtype]);
 			}
 		}
 	}
@@ -346,7 +353,7 @@ void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode,
 {
 	struct dquot	*dq;
 	dict_t		*dict;
-	int		i;
+	enum quota_type	qtype;
 
 	if (!qctx)
 		return;
@@ -354,10 +361,10 @@ void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode,
 	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;
 		}
@@ -373,7 +380,7 @@ void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode,
 {
 	struct dquot	*dq;
 	dict_t		*dict;
-	int		i;
+	enum quota_type	qtype;
 
 	if (!qctx)
 		return;
@@ -381,10 +388,10 @@ void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode,
 	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;
 		}
 	}
@@ -398,7 +405,7 @@ void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode,
 {
 	struct dquot	*dq;
 	dict_t		*dict;
-	int		i;
+	enum quota_type	qtype;
 
 	if (!qctx)
 		return;
@@ -406,10 +413,10 @@ void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode,
 	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;
 		}
 	}
@@ -542,7 +549,8 @@ static errcode_t quota_write_all_dquots(struct quota_handle *qh,
 /*
  * 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;
@@ -556,7 +564,7 @@ errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino, int type)
 		return err;
 	}
 
-	err = quota_file_open(qctx, qh, qf_ino, type, -1, 0);
+	err = quota_file_open(qctx, qh, qf_ino, qtype, -1, 0);
 	if (err) {
 		log_err("Open quota file failed");
 		goto out;
@@ -581,7 +589,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)
 {
 	struct quota_handle qh;
@@ -632,3 +640,32 @@ out_close_qh:
 out:
 	return err;
 }
+
+int parse_quota_opts(const char *opts, int (*func)(), void *data)
+{
+	char	*buf, *token, *next, *p;
+	int	len;
+	int	ret = 0;
+
+	len = strlen(opts);
+	buf = malloc(len + 1);
+	if (!buf) {
+		fprintf(stderr,
+			"Couldn't allocate memory to parse quota options!\n");
+		return -ENOMEM;
+	}
+	strcpy(buf, opts);
+	for (token = buf; token && *token; token = next) {
+		p = strchr(token, ',');
+		next = 0;
+		if (p) {
+			*p = 0;
+			next = p + 1;
+		}
+		ret = func(token, data);
+		if (ret)
+			break;
+	}
+	free(buf);
+	return ret;
+}
diff --git a/lib/support/quotaio.c b/lib/support/quotaio.c
index d30c980..864a3b6 100644
--- a/lib/support/quotaio.c
+++ b/lib/support/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,30 @@ 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];
+	if (qtype < 0 || qtype >= MAXQUOTAS)
+		return "unknown";
+	return extensions[qtype];
+}
+
+ext2_ino_t quota_type2inum(enum quota_type qtype)
+{
+	switch (qtype) {
+	case USRQUOTA:
+		return EXT4_USR_QUOTA_INO;
+	case GRPQUOTA:
+		return EXT4_GRP_QUOTA_INO;
+	default:
+		return 0;
+	}
+	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;
@@ -99,11 +115,16 @@ errcode_t quota_inode_truncate(ext2_filsys fs, ext2_ino_t ino)
 {
 	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;
@@ -183,14 +204,15 @@ static unsigned int quota_read_nomount(struct quota_file *qf,
  * Detect quota format and initialize quota IO
  */
 errcode_t quota_file_open(quota_ctx_t qctx, struct quota_handle *h,
-			  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_filsys fs = qctx->fs;
 	ext2_file_t e2_file;
 	errcode_t err;
 	int allocated_handle = 0;
 
-	if (type >= MAXQUOTAS)
+	if (qtype >= MAXQUOTAS)
 		return EINVAL;
 
 	if (fmt == -1)
@@ -200,14 +222,10 @@ errcode_t quota_file_open(quota_ctx_t qctx, struct quota_handle *h,
 	if (err)
 		return err;
 
-	if (qf_ino == 0) {
-		if (type == USRQUOTA)
-			qf_ino = fs->super->s_usr_quota_inum;
-		else
-			qf_ino = fs->super->s_grp_quota_inum;
-	}
+	if (qf_ino == 0)
+		qf_ino = *quota_sb_inump(fs->super, qtype)
 
-	log_debug("Opening quota ino=%lu, type=%d", qf_ino, type);
+	log_debug("Opening quota ino=%lu, type=%d", qf_ino, qtype);
 	err = ext2fs_file_open(fs, qf_ino, flags, &e2_file);
 	if (err) {
 		log_err("ext2fs_file_open failed: %s", error_message(err));
@@ -215,8 +233,8 @@ errcode_t quota_file_open(quota_ctx_t qctx, struct quota_handle *h,
 	}
 
 	if (!h) {
-		if (qctx->quota_file[type]) {
-			h = qctx->quota_file[type];
+		if (qctx->quota_file[qtype]) {
+			h = qctx->quota_file[qtype];
 			if (((flags & EXT2_FILE_WRITE) == 0) ||
 			    (h->qh_file_flags & EXT2_FILE_WRITE))
 				return 0;
@@ -237,13 +255,13 @@ errcode_t quota_file_open(quota_ctx_t qctx, struct quota_handle *h,
 	h->e2fs_read = quota_read_nomount;
 	h->qh_file_flags = flags;
 	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");
 		goto errout;
 	}
@@ -253,7 +271,7 @@ errcode_t quota_file_open(quota_ctx_t qctx, struct quota_handle *h,
 		goto errout;
 	}
 	if (allocated_handle)
-		qctx->quota_file[type] = h;
+		qctx->quota_file[qtype] = h;
 
 	return 0;
 errout:
@@ -298,7 +316,8 @@ static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino)
 /*
  * 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;
@@ -308,11 +327,8 @@ errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs, int type, in
 		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);
@@ -338,7 +354,7 @@ errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs, int type, in
 	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;
diff --git a/lib/support/quotaio.h b/lib/support/quotaio.h
index 9d580ae..e5df9b3 100644
--- a/lib/support/quotaio.h
+++ b/lib/support/quotaio.h
@@ -10,9 +10,9 @@
  * {
  *	quota_ctx_t qctx;
  *
- *	quota_init_context(&qctx, fs, -1);
+ *	quota_init_context(&qctx, fs, QUOTA_ALL_BIT);
  *	{
- *		quota_compute_usage(qctx, -1);
+ *		quota_compute_usage(qctx, QUOTA_ALL_BIT);
  *		AND/OR
  *		quota_data_add/quota_data_sub/quota_data_inodes();
  *	}
@@ -43,9 +43,19 @@
 
 typedef int64_t qsize_t;	/* Type in which we store size limitations */
 
-#define MAXQUOTAS 2
-#define USRQUOTA 0
-#define GRPQUOTA 1
+enum quota_type {
+	USRQUOTA = 0,
+	GRPQUOTA = 1,
+	MAXQUOTAS = 2,
+};
+
+#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)
 
 typedef struct quota_ctx *quota_ctx_t;
 struct dict_t;
@@ -104,7 +114,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_file_flags;
 	int qh_io_flags;	/* IO flags for file */
@@ -174,12 +184,13 @@ extern struct quotafile_ops quotafile_ops_meta;
 /* Open existing quotafile of given type (and verify its format) on given
  * filesystem. */
 errcode_t quota_file_open(quota_ctx_t qctx, struct quota_handle *h,
-			  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(quota_ctx_t qctx, struct quota_handle *h);
@@ -189,7 +200,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);
 
@@ -197,27 +209,42 @@ 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_name(enum quota_type, int fmt, char *buf);
 
 /* 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, enum quota_type qtype);
+errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino,
+			      enum quota_type type);
 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);
-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,
-				   int *usage_inconsistent);
-
-
+errcode_t quota_remove_inode(ext2_filsys fs, enum quota_type qtype);
+int quota_file_exists(ext2_filsys fs, enum quota_type qtype);
+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);
+int parse_quota_opts(const char *opts, int (*func)(), void *data);
+
+static inline ext2_ino_t *quota_sb_inump(struct ext2_super_block *sb,
+					 enum quota_type qtype)
+{
+	switch (qtype) {
+	case USRQUOTA:
+		return &sb->s_usr_quota_inum;
+	case GRPQUOTA:
+		return &sb->s_grp_quota_inum;
+	default:
+		return NULL;
+	}
+
+	return NULL;
+}
 
 #endif /* GUARD_QUOTAIO_H */
diff --git a/lib/support/quotaio_tree.c b/lib/support/quotaio_tree.c
index e7f3e95..2a85698 100644
--- a/lib/support/quotaio_tree.c
+++ b/lib/support/quotaio_tree.c
@@ -587,7 +587,7 @@ static void check_reference(struct quota_handle *h, unsigned int blk)
 			"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,
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index dd467f2..d34f625 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -95,7 +95,8 @@ 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 */
+/* Initialize usr/grp quotas by default */
+static unsigned int quotatype_bits = (QUOTA_USR_BIT | QUOTA_GRP_BIT);
 static __u64	offset;
 static blk64_t journal_location = ~0LL;
 static int	proceed_delay = -1;
@@ -770,12 +771,28 @@ static int set_os(struct ext2_super_block *sb, char *os)
 
 #define PATH_SET "PATH=/sbin"
 
+static int option_handle_function(char *token, void *data)
+{
+	if (!strncmp(token, "usr", 3)) {
+		quotatype_bits |= QUOTA_USR_BIT;
+	} else if (!strncmp(token, "grp", 3)) {
+		quotatype_bits |= QUOTA_GRP_BIT;
+	} else {
+		fprintf(stderr, _("Invalid quotatype parameter: %s\n"),
+				token);
+		return 1;
+	}
+	return 0;
+
+}
+
 static void parse_extended_opts(struct ext2_super_block *param,
 				const char *opts)
 {
 	char	*buf, *token, *next, *p, *arg, *badopt = 0;
 	int	len;
 	int	r_usage = 0;
+	int	ret;
 
 	len = strlen(opts);
 	buf = malloc(len+1);
@@ -1008,14 +1025,9 @@ static void parse_extended_opts(struct ext2_super_block *param,
 				badopt = token;
 				continue;
 			}
-			if (!strncmp(arg, "usr", 3)) {
-				quotatype = 0;
-			} else if (!strncmp(arg, "grp", 3)) {
-				quotatype = 1;
-			} else {
-				fprintf(stderr,
-					_("Invalid quotatype parameter: %s\n"),
-					arg);
+			ret = parse_quota_opts(arg, option_handle_function,
+					       NULL);
+			if (ret) {
 				r_usage++;
 				continue;
 			}
@@ -2646,9 +2658,9 @@ static int create_quota_inodes(ext2_filsys fs)
 {
 	quota_ctx_t qctx;
 
-	quota_init_context(&qctx, fs, -1);
+	quota_init_context(&qctx, fs, QUOTA_ALL_BIT);
 	quota_compute_usage(qctx);
-	quota_write_inode(qctx, quotatype);
+	quota_write_inode(qctx, quotatype_bits);
 	quota_release_context(&qctx);
 
 	return 0;
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 4d57399..1e05428 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -95,7 +95,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;
 static int feature_64bit;
 static int fsck_requested;
@@ -958,6 +958,7 @@ static int update_feature_set(ext2_filsys fs, char *features)
 	int		type_err;
 	unsigned int	mask_err;
 	errcode_t	err;
+	enum quota_type qtype;
 
 #define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \
 				((&sb->s_feature_compat)[(type)] & (mask)))
@@ -1265,9 +1266,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;
 		}
 		ext2fs_clear_feature_quota(sb);
 	}
@@ -1282,9 +1283,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 (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_ENCRYPT)) {
@@ -1406,87 +1407,76 @@ static void handle_quota_options(ext2_filsys fs)
 {
 	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_compute_usage(qctx);
-
-	if (usrquota == QOPT_ENABLE && !fs->super->s_usr_quota_inum) {
-		if ((qf_ino = quota_file_exists(fs, USRQUOTA)) > 0)
-			quota_update_limits(qctx, qf_ino, USRQUOTA);
-		quota_write_inode(qctx, USRQUOTA);
-	} else if (usrquota == QOPT_DISABLE) {
-		quota_remove_inode(fs, USRQUOTA);
+	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 (grpquota == QOPT_ENABLE && !fs->super->s_grp_quota_inum) {
-		if ((qf_ino = quota_file_exists(fs, GRPQUOTA)) > 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)) > 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) {
 		ext2fs_set_feature_quota(fs->super);
 		ext2fs_mark_super_dirty(fs);
-	} else if (!fs->super->s_usr_quota_inum &&
-		   !fs->super->s_grp_quota_inum) {
-		ext2fs_clear_feature_quota(fs->super);
-		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;
 }
 
-static void parse_quota_opts(const char *opts)
+static int option_handle_function(char *token, void *data)
 {
-	char	*buf, *token, *next, *p;
-	int	len;
-
-	len = strlen(opts);
-	buf = malloc(len+1);
-	if (!buf) {
-		fputs(_("Couldn't allocate memory to parse quota "
-			"options!\n"), stderr);
-		exit(1);
-	}
-	strcpy(buf, opts);
-	for (token = buf; token && *token; token = next) {
-		p = strchr(token, ',');
-		next = 0;
-		if (p) {
-			*p = 0;
-			next = p+1;
-		}
-
-		if (strcmp(token, "usrquota") == 0) {
-			usrquota = QOPT_ENABLE;
-		} else if (strcmp(token, "^usrquota") == 0) {
-			usrquota = QOPT_DISABLE;
-		} else if (strcmp(token, "grpquota") == 0) {
-			grpquota = QOPT_ENABLE;
-		} else if (strcmp(token, "^grpquota") == 0) {
-			grpquota = QOPT_DISABLE;
-		} else {
-			fputs(_("\nBad quota options specified.\n\n"
-				"Following valid quota options are available "
-				"(pass by separating with comma):\n"
-				"\t[^]usrquota\n"
-				"\t[^]grpquota\n"
-				"\n\n"), stderr);
-			free(buf);
-			exit(1);
-		}
+	if (strncmp(token, "usr", 3) == 0) {
+		quota_enable[USRQUOTA] = QOPT_ENABLE;
+	} else if (strncmp(token, "^usr", 4) == 0) {
+		quota_enable[USRQUOTA] = QOPT_DISABLE;
+	} else if (strncmp(token, "grp", 3) == 0) {
+		quota_enable[GRPQUOTA] = QOPT_ENABLE;
+	} else if (strncmp(token, "^grp", 4) == 0) {
+		quota_enable[GRPQUOTA] = QOPT_DISABLE;
+	} else {
+		fputs(_("\nBad quota options specified.\n\n"
+			"Following valid quota options are available "
+			"(pass by separating with comma):\n"
+			"\t[^]usr[quota]\n"
+			"\t[^]grp[quota]\n"
+			"\n\n"), stderr);
+		return 1;
 	}
-	free(buf);
+	return 0;
 }
 
 static void parse_e2label_options(int argc, char ** argv)
@@ -1549,6 +1539,7 @@ static void parse_tune2fs_options(int argc, char **argv)
 	char *tmp;
 	struct group *gr;
 	struct passwd *pw;
+	int ret;
 	char optstring[100] = "c:e:fg:i:jlm:o:r:s:u:C:E:I:J:L:M:O:T:U:z:Q:";
 
 	open_flag = 0;
@@ -1709,7 +1700,10 @@ static void parse_tune2fs_options(int argc, char **argv)
 			break;
 		case 'Q':
 			Q_flag = 1;
-			parse_quota_opts(optarg);
+			ret = parse_quota_opts(optarg, option_handle_function,
+					       NULL);
+			if (ret)
+				exit(1);
 			open_flag = EXT2_FLAG_RW;
 			break;
 		case 'r':
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [v3 2/5] Add project feature flag EXT4_FEATURE_RO_COMPAT_PROJECT
  2016-01-12  8:56 [v3 0/5] Add project quota support for e2fsprogs Li Xi
  2016-01-12  8:56 ` [v3 1/5] Clean up codes for adding new quota type Li Xi
@ 2016-01-12  8:56 ` Li Xi
  2016-01-12  8:56 ` [v3 3/5] Add project quota support Li Xi
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Li Xi @ 2016-01-12  8:56 UTC (permalink / raw)
  To: linux-ext4, tytso, adilger, jack, viro, hch, dmonakhov

This patch add project feature flag EXT4_FEATURE_RO_COMPAT_PROJECT.
Project feature is a read-only compat feature. Thus, an ext4 file
system with project feature enabled could only be read by ext4
kernel module without project feature support.

Signed-off-by: Li Xi <lixi@ddn.com>
Signed-off-by: Wang Shilong <wshilong@ddn.com>
---
 lib/e2p/feature.c    |    2 ++
 lib/ext2fs/ext2_fs.h |    2 ++
 lib/ext2fs/ext2fs.h  |    3 ++-
 misc/ext4.5.in       |    5 +++++
 misc/mke2fs.c        |    3 ++-
 misc/tune2fs.c       |    3 ++-
 6 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c
index 737b0b9..17d2ad0 100644
--- a/lib/e2p/feature.c
+++ b/lib/e2p/feature.c
@@ -70,6 +70,8 @@ static struct feature feature_list[] = {
 			"replica" },
 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_READONLY,
 			"read-only" },
+	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_PROJECT,
+			"project"},
 
 	{	E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION,
 			"compression" },
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 1f62c58..4221a6a 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -791,6 +791,7 @@ struct ext2_super_block {
 #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM	0x0400
 #define EXT4_FEATURE_RO_COMPAT_REPLICA		0x0800
 #define EXT4_FEATURE_RO_COMPAT_READONLY		0x1000
+#define EXT4_FEATURE_RO_COMPAT_PROJECT		0x2000 /* Project quota */
 
 
 #define EXT2_FEATURE_INCOMPAT_COMPRESSION	0x0001
@@ -882,6 +883,7 @@ EXT4_FEATURE_RO_COMPAT_FUNCS(bigalloc,		4, BIGALLOC)
 EXT4_FEATURE_RO_COMPAT_FUNCS(metadata_csum,	4, METADATA_CSUM)
 EXT4_FEATURE_RO_COMPAT_FUNCS(replica,		4, REPLICA)
 EXT4_FEATURE_RO_COMPAT_FUNCS(readonly,		4, READONLY)
+EXT4_FEATURE_RO_COMPAT_FUNCS(project,		4, PROJECT)
 
 EXT4_FEATURE_INCOMPAT_FUNCS(compression,	2, COMPRESSION)
 EXT4_FEATURE_INCOMPAT_FUNCS(filetype,		2, FILETYPE)
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 30e913c..f6fed2c 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -591,7 +591,8 @@ typedef struct ext2_icount *ext2_icount_t;
 					 EXT4_FEATURE_RO_COMPAT_BIGALLOC|\
 					 EXT4_FEATURE_RO_COMPAT_QUOTA|\
 					 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\
-					 EXT4_FEATURE_RO_COMPAT_READONLY)
+					 EXT4_FEATURE_RO_COMPAT_READONLY |\
+					 EXT4_FEATURE_RO_COMPAT_PROJECT)
 
 /*
  * These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed
diff --git a/misc/ext4.5.in b/misc/ext4.5.in
index dbd6dde..e892743 100644
--- a/misc/ext4.5.in
+++ b/misc/ext4.5.in
@@ -214,6 +214,11 @@ Causes the quota files (i.e., user.quota and
 group.quota which existed
 in the older quota design) to be hidden inodes.
 .TP
+.B project
+.br
+This ext4 feature provides project quota support. With this feature,
+the project ID of inode will be managed when the filesystem is mounted.
+.TP
 .B resize_inode
 .br
 This file system feature indicates that space has been reserved so
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index d34f625..48f15a5 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -1096,7 +1096,8 @@ static __u32 ok_features[3] = {
 		EXT4_FEATURE_RO_COMPAT_GDT_CSUM|
 		EXT4_FEATURE_RO_COMPAT_BIGALLOC|
 		EXT4_FEATURE_RO_COMPAT_QUOTA|
-		EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
+		EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|
+		EXT4_FEATURE_RO_COMPAT_PROJECT
 };
 
 
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 1e05428..4aff3fd 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -159,7 +159,8 @@ static __u32 ok_features[3] = {
 		EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER |
 		EXT4_FEATURE_RO_COMPAT_QUOTA |
 		EXT4_FEATURE_RO_COMPAT_METADATA_CSUM |
-		EXT4_FEATURE_RO_COMPAT_READONLY
+		EXT4_FEATURE_RO_COMPAT_READONLY |
+		EXT4_FEATURE_RO_COMPAT_PROJECT
 };
 
 static __u32 clear_ok_features[3] = {
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [v3 3/5] Add project quota support
  2016-01-12  8:56 [v3 0/5] Add project quota support for e2fsprogs Li Xi
  2016-01-12  8:56 ` [v3 1/5] Clean up codes for adding new quota type Li Xi
  2016-01-12  8:56 ` [v3 2/5] Add project feature flag EXT4_FEATURE_RO_COMPAT_PROJECT Li Xi
@ 2016-01-12  8:56 ` Li Xi
  2016-01-12  8:56 ` [v3 4/5] Add inherit flags for project quota Li Xi
  2016-01-12  8:56 ` [v3 5/5] Add project ID support for chattr/lsattr Li Xi
  4 siblings, 0 replies; 9+ messages in thread
From: Li Xi @ 2016-01-12  8:56 UTC (permalink / raw)
  To: linux-ext4, tytso, adilger, jack, viro, hch, dmonakhov

This patch adds project quota support. An new quota type PRJQUOTA(2)
is added. EXT4_PRJ_QUOTA_INO(11) is reserved for project quota inode.
The super block reservers an field s_prj_quota_inum for saving
project quota inode. And each inode adds an internal field i_projid
for saving its project ID.

Signed-off-by: Li Xi <lixi@ddn.com>
Signed-off-by: Wang Shilong <wshilong@ddn.com>
---
 debugfs/set_fields.c            |    1 +
 e2fsck/pass1.c                  |   24 ++++++++++++++--------
 e2fsck/pass4.c                  |    3 +-
 e2fsck/quota.c                  |    2 +-
 lib/e2p/ls.c                    |    1 +
 lib/ext2fs/ext2_fs.h            |    5 +++-
 lib/ext2fs/swapfs.c             |    2 +
 lib/ext2fs/tst_inode_size.c     |    1 +
 lib/ext2fs/tst_super_size.c     |    3 +-
 lib/support/mkquota.c           |   40 +++++++++++++++++++++++++++++++-------
 lib/support/quotaio.c           |   27 ++++++++++++++++++++-----
 lib/support/quotaio.h           |   13 ++++++++---
 misc/mke2fs.c                   |   17 ++++++++++++++++
 misc/tune2fs.8.in               |    3 ++
 misc/tune2fs.c                  |   24 ++++++++++++++++++++--
 tests/d_fallocate_blkmap/expect |    4 +-
 tests/f_create_symlinks/expect  |    8 +++---
 tests/m_bigjournal/expect.1     |    4 +-
 tests/m_large_file/expect.1     |    4 +-
 tests/m_quota/expect.1          |   17 ++++++++-------
 tests/m_quota/script            |    2 +-
 21 files changed, 152 insertions(+), 53 deletions(-)

diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index bc63802..ce4a04b 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -153,6 +153,7 @@ static struct field_set_info super_fields[] = {
 	{ "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 },
+	{ "prj_quota_inum", &set_sb.s_prj_quota_inum, 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 },
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 9a987d2..06295c4 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -102,7 +102,7 @@ struct process_block_struct {
 
 struct process_inode_block {
 	ext2_ino_t ino;
-	struct ext2_inode inode;
+	struct ext2_inode_large inode;
 };
 
 struct scan_callback_struct {
@@ -965,12 +965,12 @@ static int quota_inum_is_super(struct ext2_super_block *sb, ext2_ino_t ino)
 	return 0;
 }
 
-static int quota_inum_is_reserved(ext2_ino_t ino)
+static int quota_inum_is_reserved(ext2_filsys fs, ext2_ino_t ino)
 {
 	enum quota_type qtype;
 
 	for (qtype = 0; qtype < MAXQUOTAS; qtype++)
-		if (quota_type2inum(qtype) == ino)
+		if (quota_type2inum(qtype, fs->super) == ino)
 			return 1;
 
 	return 0;
@@ -1524,7 +1524,7 @@ void e2fsck_pass1(e2fsck_t ctx)
 							inode_size, "pass1");
 				failed_csum = 0;
 			}
-		} else if (quota_inum_is_reserved(ino)) {
+		} else if (quota_inum_is_reserved(fs, ino)) {
 			ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
 			if (ext2fs_has_feature_quota(fs->super) &&
 			    quota_inum_is_super(fs->super, ino)) {
@@ -1761,7 +1761,8 @@ void e2fsck_pass1(e2fsck_t ctx)
 		     inode->i_block[EXT2_TIND_BLOCK] ||
 		     ext2fs_file_acl_block(fs, inode))) {
 			inodes_to_process[process_inode_count].ino = ino;
-			inodes_to_process[process_inode_count].inode = *inode;
+			inodes_to_process[process_inode_count].inode =
+					*(struct ext2_inode_large *)inode;
 			process_inode_count++;
 		} else
 			check_blocks(ctx, &pctx, block_buf);
@@ -1924,7 +1925,9 @@ static void process_inodes(e2fsck_t ctx, char *block_buf)
 		      sizeof(struct process_inode_block), process_inode_cmp);
 	clear_problem_context(&pctx);
 	for (i=0; i < process_inode_count; i++) {
-		pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
+		ctx->stashed_inode = (struct ext2_inode *)
+					&inodes_to_process[i].inode;
+		pctx.inode = ctx->stashed_inode;
 		pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
 
 #if 0
@@ -1962,8 +1965,10 @@ static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b)
 		 * inodes, so it's OK to pass NULL to
 		 * ext2fs_file_acl_block() here.
 		 */
-		ret = ext2fs_file_acl_block(0, &(ib_a->inode)) -
-			ext2fs_file_acl_block(0, &(ib_b->inode));
+		ret = ext2fs_file_acl_block(0,
+				(struct ext2_inode *)&(ib_a->inode)) -
+			ext2fs_file_acl_block(0,
+				(struct ext2_inode *)&(ib_b->inode));
 	if (ret == 0)
 		ret = ib_a->ino - ib_b->ino;
 	return ret;
@@ -3107,7 +3112,8 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 		}
 	}
 
-	if (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) {
+	if (ino != quota_type2inum(PRJQUOTA, fs->super) &&
+	    (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super))) {
 		quota_data_add(ctx->qctx, inode, ino,
 			       pb.num_blocks * fs->blocksize);
 		quota_data_inodes(ctx->qctx, inode, ino, +1);
diff --git a/e2fsck/pass4.c b/e2fsck/pass4.c
index bc9a2c4..c490438 100644
--- a/e2fsck/pass4.c
+++ b/e2fsck/pass4.c
@@ -142,7 +142,8 @@ void e2fsck_pass4(e2fsck_t ctx)
 				if ((ctx->progress)(ctx, 4, group, maxgroup))
 					goto errout;
 		}
-		if (i == EXT2_BAD_INO ||
+		if (i == quota_type2inum(PRJQUOTA, ctx->fs->super) ||
+		    i == EXT2_BAD_INO ||
 		    (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
 			continue;
 		if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, i)) ||
diff --git a/e2fsck/quota.c b/e2fsck/quota.c
index bc4cc07..43282a6 100644
--- a/e2fsck/quota.c
+++ b/e2fsck/quota.c
@@ -72,7 +72,7 @@ void e2fsck_hide_quota(e2fsck_t ctx)
 
 	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
 		pctx.ino = *quota_sb_inump(sb, qtype);
-		quota_ino = quota_type2inum(qtype);
+		quota_ino = quota_type2inum(qtype, sb);
 		if (pctx.ino && (pctx.ino != quota_ino) &&
 		    fix_problem(ctx, PR_0_HIDE_QUOTA, &pctx)) {
 			move_quota_inode(fs, pctx.ino, quota_ino, qtype);
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index 51f0ab1..6f5eff6 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -210,6 +210,7 @@ static const char *checksum_type(__u8 type)
 static const char *quota_prefix[MAXQUOTAS] = {
 	[USRQUOTA] = "User quota inode:",
 	[GRPQUOTA] = "Group quota inode:",
+	[PRJQUOTA] = "Project quota inode:",
 };
 
 /**
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 4221a6a..52c6e23 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -473,6 +473,7 @@ struct ext2_inode_large {
 	__u32	i_crtime;	/* File creation time */
 	__u32	i_crtime_extra;	/* extra File creation time (nsec << 2 | epoch)*/
 	__u32	i_version_hi;	/* high 32 bits for 64-bit version */
+	__u32   i_projid;       /* Project ID */
 };
 
 #define EXT4_INODE_CSUM_HI_EXTRA_END	\
@@ -506,6 +507,7 @@ struct ext2_inode_large {
 
 #define inode_uid(inode)	((inode).i_uid | (inode).osd2.linux2.l_i_uid_high << 16)
 #define inode_gid(inode)	((inode).i_gid | (inode).osd2.linux2.l_i_gid_high << 16)
+#define inode_projid(inode)	((inode).i_projid)
 #define ext2fs_set_i_uid_high(inode,x) ((inode).osd2.linux2.l_i_uid_high = (x))
 #define ext2fs_set_i_gid_high(inode,x) ((inode).osd2.linux2.l_i_gid_high = (x))
 
@@ -719,7 +721,8 @@ struct ext2_super_block {
 	__u8	s_encrypt_algos[4];	/* Encryption algorithms in use  */
 	__u8	s_encrypt_pw_salt[16];	/* Salt used for string2key algorithm */
 	__le32	s_lpf_ino;		/* Location of the lost+found inode */
-	__le32	s_reserved[100];	/* Padding to the end of the block */
+	__le32  s_prj_quota_inum;	/* inode for tracking project quota */
+	__le32	s_reserved[99];		/* Padding to the end of the block */
 	__u32	s_checksum;		/* crc32c(superblock) */
 };
 
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index ee7a455..bc50fd4 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -320,6 +320,8 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
 		t->i_crtime_extra = ext2fs_swab32(f->i_crtime_extra);
 	if (extra_isize >= 28)
 		t->i_version_hi = ext2fs_swab32(f->i_version_hi);
+	if (extra_isize >= 32)
+		t->i_projid = ext2fs_swab32(f->i_projid);
 
 	i = sizeof(struct ext2_inode) + extra_isize + sizeof(__u32);
 	if (bufsize < (int) i)
diff --git a/lib/ext2fs/tst_inode_size.c b/lib/ext2fs/tst_inode_size.c
index e20ec98..cc5d165 100644
--- a/lib/ext2fs/tst_inode_size.c
+++ b/lib/ext2fs/tst_inode_size.c
@@ -81,6 +81,7 @@ int main(int argc, char **argv)
 	check_field(i_crtime, 4);
 	check_field(i_crtime_extra, 4);
 	check_field(i_version_hi, 4);
+	check_field(i_projid, 4);
 	/* This size will change as new fields are added */
 	do_field("Large inode end", 0, 0, cur_offset, sizeof(inode));
 #endif
diff --git a/lib/ext2fs/tst_super_size.c b/lib/ext2fs/tst_super_size.c
index 8e3c21f..9b25cce 100644
--- a/lib/ext2fs/tst_super_size.c
+++ b/lib/ext2fs/tst_super_size.c
@@ -140,7 +140,8 @@ int main(int argc, char **argv)
 	check_field(s_encrypt_algos, 4);
 	check_field(s_encrypt_pw_salt, 16);
 	check_field(s_lpf_ino, 4);
-	check_field(s_reserved, 100 * 4);
+	check_field(s_prj_quota_inum, 4);
+	check_field(s_reserved, 99 * 4);
 	check_field(s_checksum, 4);
 	do_field("Superblock end", 0, 0, cur_offset, 1024);
 #endif
diff --git a/lib/support/mkquota.c b/lib/support/mkquota.c
index 2c7f436..bccf5ca 100644
--- a/lib/support/mkquota.c
+++ b/lib/support/mkquota.c
@@ -112,10 +112,22 @@ errcode_t quota_remove_inode(ext2_filsys fs, enum quota_type qtype)
 		return retval;
 	}
 	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))
 		quota_inode_truncate(fs, qf_ino);
+	else {
+		struct ext2_inode inode;
+
+		quota_inode_truncate(fs, qf_ino);
+		retval = ext2fs_read_inode(fs, qf_ino, &inode);
+		if (!retval) {
+			memset(&inode, 0, sizeof(struct ext2_inode));
+			ext2fs_write_inode(fs, qf_ino, &inode);
+		}
+		ext2fs_inode_alloc_stats2(fs, qf_ino, -1, 0);
+		ext2fs_mark_ib_dirty(fs);
+
+	}
+	quota_set_sb_inum(fs, 0, qtype);
 
 	ext2fs_mark_super_dirty(fs);
 	fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
@@ -233,11 +245,17 @@ static int dict_uint_cmp(const void *a, const void *b)
 
 static inline qid_t get_qid(struct ext2_inode *inode, enum quota_type qtype)
 {
+	struct ext2_inode_large *large_inode;
+
 	switch (qtype) {
 	case USRQUOTA:
 		return inode_uid(*inode);
 	case GRPQUOTA:
 		return inode_gid(*inode);
+	case PRJQUOTA:
+		large_inode = (struct ext2_inode_large *)inode;
+		if (large_inode->i_extra_isize >= 32)
+			return inode_projid(*large_inode);
 	default:
 		return 0;
 	}
@@ -427,9 +445,10 @@ errcode_t quota_compute_usage(quota_ctx_t qctx)
 	ext2_filsys fs;
 	ext2_ino_t ino;
 	errcode_t ret;
-	struct ext2_inode inode;
+	struct ext2_inode *inode = NULL;
 	qsize_t space;
 	ext2_inode_scan scan;
+	int inode_size;
 
 	if (!qctx)
 		return 0;
@@ -440,9 +459,13 @@ errcode_t quota_compute_usage(quota_ctx_t qctx)
 		log_err("while opening inode scan. ret=%ld", ret);
 		return ret;
 	}
+	inode_size = EXT2_INODE_SIZE(fs->super);
+	inode = (struct ext2_inode *)malloc(inode_size);
+	if (!inode)
+		return -ENOMEM;
 
 	while (1) {
-		ret = ext2fs_get_next_inode(scan, &ino, &inode);
+		ret = ext2fs_get_next_inode_full(scan, &ino, inode, inode_size);
 		if (ret) {
 			log_err("while getting next inode. ret=%ld", ret);
 			ext2fs_close_inode_scan(scan);
@@ -450,12 +473,13 @@ errcode_t quota_compute_usage(quota_ctx_t qctx)
 		}
 		if (ino == 0)
 			break;
-		if (inode.i_links_count &&
+		if (inode->i_links_count &&
+		    ino != quota_type2inum(PRJQUOTA, fs->super) &&
 		    (ino == EXT2_ROOT_INO ||
 		     ino >= EXT2_FIRST_INODE(fs->super))) {
-			space = ext2fs_inode_i_blocks(fs, &inode) << 9;
-			quota_data_add(qctx, &inode, ino, space);
-			quota_data_inodes(qctx, &inode, ino, +1);
+			space = ext2fs_inode_i_blocks(fs, inode) << 9;
+			quota_data_add(qctx, inode, ino, space);
+			quota_data_inodes(qctx, inode, ino, +1);
 		}
 	}
 
diff --git a/lib/support/quotaio.c b/lib/support/quotaio.c
index 864a3b6..3113848 100644
--- a/lib/support/quotaio.c
+++ b/lib/support/quotaio.c
@@ -20,7 +20,11 @@
 #include "common.h"
 #include "quotaio.h"
 
-static const char * const extensions[MAXQUOTAS] = {"user", "group"};
+static const char * const extensions[MAXQUOTAS] = {
+	[USRQUOTA] = "user",
+	[GRPQUOTA] = "group",
+	[PRJQUOTA] = "project",
+};
 static const char * const basenames[] = {
 	"",		/* undefined */
 	"quota",	/* QFMT_VFS_OLD */
@@ -45,13 +49,16 @@ const char *quota_type2name(enum quota_type qtype)
 	return extensions[qtype];
 }
 
-ext2_ino_t quota_type2inum(enum quota_type qtype)
+ext2_ino_t quota_type2inum(enum quota_type qtype,
+			   struct ext2_super_block *sb)
 {
 	switch (qtype) {
 	case USRQUOTA:
 		return EXT4_USR_QUOTA_INO;
 	case GRPQUOTA:
 		return EXT4_GRP_QUOTA_INO;
+	case PRJQUOTA:
+		return sb->s_prj_quota_inum;
 	default:
 		return 0;
 	}
@@ -121,7 +128,7 @@ errcode_t quota_inode_truncate(ext2_filsys fs, ext2_ino_t ino)
 		return err;
 
 	for (qtype = 0; qtype < MAXQUOTAS; qtype++)
-		if (ino == quota_type2inum(qtype))
+		if (ino == quota_type2inum(qtype, fs->super))
 			break;
 
 	if (qtype != MAXQUOTAS) {
@@ -321,15 +328,23 @@ errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs,
 {
 	ext2_file_t e2_file;
 	int err;
-	unsigned long qf_inum;
+	ext2_ino_t qf_inum = 0;
 
 	if (fmt == -1)
 		fmt = QFMT_VFS_V1;
 
 	h->qh_qf.fs = fs;
-	qf_inum = quota_type2inum(qtype);
-	if (qf_inum == 0)
+	qf_inum = quota_type2inum(qtype, fs->super);
+	if (qf_inum == 0 && qtype == PRJQUOTA) {
+		err = ext2fs_new_inode(fs, EXT2_ROOT_INO, LINUX_S_IFREG | 0600,
+					  0, &qf_inum);
+		if (err)
+			return -1;
+		ext2fs_inode_alloc_stats2(fs, qf_inum, +1, 0);
+		ext2fs_mark_ib_dirty(fs);
+	} else if (qf_inum == 0) {
 		return -1;
+	}
 
 	err = ext2fs_read_bitmaps(fs);
 	if (err)
diff --git a/lib/support/quotaio.h b/lib/support/quotaio.h
index e5df9b3..017a02c 100644
--- a/lib/support/quotaio.h
+++ b/lib/support/quotaio.h
@@ -46,7 +46,8 @@ typedef int64_t qsize_t;	/* Type in which we store size limitations */
 enum quota_type {
 	USRQUOTA = 0,
 	GRPQUOTA = 1,
-	MAXQUOTAS = 2,
+	PRJQUOTA = 2,
+	MAXQUOTAS = 3,
 };
 
 #if MAXQUOTAS > 32
@@ -55,7 +56,8 @@ enum quota_type {
 
 #define QUOTA_USR_BIT (1 << USRQUOTA)
 #define QUOTA_GRP_BIT (1 << GRPQUOTA)
-#define QUOTA_ALL_BIT (QUOTA_USR_BIT | QUOTA_GRP_BIT)
+#define QUOTA_PRJ_BIT (1 << PRJQUOTA)
+#define QUOTA_ALL_BIT (QUOTA_USR_BIT | QUOTA_GRP_BIT | QUOTA_PRJ_BIT)
 
 typedef struct quota_ctx *quota_ctx_t;
 struct dict_t;
@@ -71,7 +73,8 @@ struct quota_ctx {
  */
 #define INITQMAGICS {\
 	0xd9c01f11,	/* USRQUOTA */\
-	0xd9c01927	/* GRPQUOTA */\
+	0xd9c01927,	/* GRPQUOTA */\
+	0xd9c03f14	/* PRJQUOTA */\
 }
 
 /* Size of blocks in which are counted size limits in generic utility parts */
@@ -201,7 +204,7 @@ struct dquot *get_empty_dquot(void);
 errcode_t quota_inode_truncate(ext2_filsys fs, ext2_ino_t ino);
 
 const char *quota_type2name(enum quota_type qtype);
-ext2_ino_t quota_type2inum(enum quota_type qtype);
+ext2_ino_t quota_type2inum(enum quota_type qtype, struct ext2_super_block *sb);
 
 void update_grace_times(struct dquot *q);
 
@@ -240,6 +243,8 @@ static inline ext2_ino_t *quota_sb_inump(struct ext2_super_block *sb,
 		return &sb->s_usr_quota_inum;
 	case GRPQUOTA:
 		return &sb->s_grp_quota_inum;
+	case PRJQUOTA:
+		return &sb->s_prj_quota_inum;
 	default:
 		return NULL;
 	}
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 48f15a5..082a590 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -777,6 +777,8 @@ static int option_handle_function(char *token, void *data)
 		quotatype_bits |= QUOTA_USR_BIT;
 	} else if (!strncmp(token, "grp", 3)) {
 		quotatype_bits |= QUOTA_GRP_BIT;
+	} else if (!strncmp(token, "prj", 3)) {
+		quotatype_bits |= QUOTA_PRJ_BIT;
 	} else {
 		fprintf(stderr, _("Invalid quotatype parameter: %s\n"),
 				token);
@@ -2371,6 +2373,19 @@ profile_error:
 		exit(1);
 	}
 
+	/*
+	 * If inode size is 128 and project quota is enabled, we need
+	 * to notify users that project ID will never be useful.
+	 */
+	if (ext2fs_has_feature_project(&fs_param) &&
+	    fs_param.s_inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
+		com_err(program_name, 0,
+			_("%d byte inodes are too small for project quota; "
+			  "specify larger size"),
+			fs_param.s_inode_size);
+		exit(1);
+	}
+
 	/* Make sure number of inodes specified will fit in 32 bits */
 	if (num_inodes == 0) {
 		unsigned long long n;
@@ -3112,6 +3127,8 @@ no_journal:
 
 	if (ext2fs_has_feature_bigalloc(&fs_param))
 		fix_cluster_bg_counts(fs);
+	if (ext2fs_has_feature_project(&fs_param))
+		quotatype_bits |= QUOTA_PRJ_BIT;
 	if (ext2fs_has_feature_quota(&fs_param))
 		create_quota_inodes(fs);
 
diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in
index b5a1d3b..d508d41 100644
--- a/misc/tune2fs.8.in
+++ b/misc/tune2fs.8.in
@@ -626,6 +626,9 @@ Sets/clears user quota inode in the superblock.
 .TP
 .BR [^]grpquota
 Sets/clears group quota inode in the superblock.
+.TP
+.BR [^]prjquota
+Sets/clears project quota inode in the superblock.
 .RE
 .TP
 .BI \-T " time-last-checked"
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 4aff3fd..bb7ac3b 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -1267,13 +1267,26 @@ mmp_error:
 		 */
 		if (!Q_flag) {
 			Q_flag = 1;
-			/* Enable all quota by default */
-			for (qtype = 0; qtype < MAXQUOTAS; qtype++)
-				quota_enable[qtype] = QOPT_ENABLE;
+			/* Enable usr/grp quota by default */
+			for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+				if (qtype != PRJQUOTA)
+					quota_enable[qtype] = QOPT_ENABLE;
+				else
+					quota_enable[qtype] = QOPT_DISABLE;
+			}
 		}
 		ext2fs_clear_feature_quota(sb);
 	}
 
+	if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
+				EXT4_FEATURE_RO_COMPAT_PROJECT)) {
+		if (!Q_flag && !ext2fs_has_feature_quota(sb))
+			fputs(_("\nWarning: enabled project without quota together\n"),
+				stderr);
+		Q_flag = 1;
+		quota_enable[PRJQUOTA] = QOPT_ENABLE;
+	}
+
 	if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
 				EXT4_FEATURE_RO_COMPAT_QUOTA)) {
 		/*
@@ -1468,12 +1481,17 @@ static int option_handle_function(char *token, void *data)
 		quota_enable[GRPQUOTA] = QOPT_ENABLE;
 	} else if (strncmp(token, "^grp", 4) == 0) {
 		quota_enable[GRPQUOTA] = QOPT_DISABLE;
+	} else if (strncmp(token, "prj", 3) == 0) {
+		quota_enable[PRJQUOTA] = QOPT_ENABLE;
+	} else if (strncmp(token, "^prj", 4) == 0) {
+		quota_enable[PRJQUOTA] = QOPT_DISABLE;
 	} else {
 		fputs(_("\nBad quota options specified.\n\n"
 			"Following valid quota options are available "
 			"(pass by separating with comma):\n"
 			"\t[^]usr[quota]\n"
 			"\t[^]grp[quota]\n"
+			"\t[^]prj[quota]\n"
 			"\n\n"), stderr);
 		return 1;
 	}
diff --git a/tests/d_fallocate_blkmap/expect b/tests/d_fallocate_blkmap/expect
index f7ae606..a66b06a 100644
--- a/tests/d_fallocate_blkmap/expect
+++ b/tests/d_fallocate_blkmap/expect
@@ -21,7 +21,7 @@ User:     0   Group:     0   Size: 40960
 File ACL: 0    Directory ACL: 0
 Links: 1   Blockcount: 82
 Fragment:  Address: 0    Number: 0    Size: 0
-Size of extra inode fields: 28
+Size of extra inode fields: 32
 BLOCKS:
 (0-1):1312-1313, (2-11):8000-8009, (IND):8010, (12-39):8011-8038
 TOTAL: 41
@@ -33,7 +33,7 @@ User:     0   Group:     0   Size: 10240000
 File ACL: 0    Directory ACL: 0
 Links: 1   Blockcount: 20082
 Fragment:  Address: 0    Number: 0    Size: 0
-Size of extra inode fields: 28
+Size of extra inode fields: 32
 BLOCKS:
 (0-11):10000-10011, (IND):10012, (12-267):10013-10268, (DIND):10269, (IND):10270, (268-523):10271-10526, (IND):10527, (524-779):10528-10783, (IND):10784, (780-1035):10785-11040, (IND):11041, (1036-1291):11042-11297, (IND):11298, (1292-1547):11299-11554, (IND):11555, (1548-1803):11556-11811, (IND):11812, (1804-2059):11813-12068, (IND):12069, (2060-2315):12070-12325, (IND):12326, (2316-2571):12327-12582, (IND):12583, (2572-2827):12584-12839, (IND):12840, (2828-3083):12841-13096, (IND):13097, (3084-3339):13098-13353, (IND):13354, (3340-3595):13355-13610, (IND):13611, (3596-3851):13612-13867, (IND):13868, (3852-4107):13869-14124, (IND):14125, (4108-4363):14126-14381, (IND):14382, (4364-4619):14383-14638, (IND):14639, (4620-4875):14640-14895, (IND):14896, (4876-5131):14897-15152, (IND):15153, 
 (5132-5387):15154-15409, (IND):15410, (5388-5643):15411-15666, (IND):15667, (5644-5899):15668-15923, (IND):15924, (5900-6155):15925-16180, (IND):16181, (6156-6411):16182-16437, (IND):16438, (6412-6667):16439-16694, (IND):16695, (6668-6923):16696-16951, (IND):16952, (6924-7179):16953-17208, (IND):17209, (7180-7435):17210-17465, (IND):17466, (7436-7691):17467-17722, (IND):17723, (7692-7947):17724-17979, (IND):17980, (7948-8203):17981-18236, (IND):18237, (8204-8459):18238-18493, (IND):18494, (8460-8715):18495-18750, (IND):18751, (8716-8971):18752-19007, (IND):19008, (8972-9227):19009-19264, (IND):19265, (9228-9483):19266-19521, (IND):19522, (9484-9739):19523-19778, (IND):19779, (9740-9995):19780-20035, (IND):20036, (9996-9999):20037-20040
 TOTAL: 10041
diff --git a/tests/f_create_symlinks/expect b/tests/f_create_symlinks/expect
index 47fa468..6e1553c 100644
--- a/tests/f_create_symlinks/expect
+++ b/tests/f_create_symlinks/expect
@@ -23,7 +23,7 @@ User:     0   Group:     0   Size: 31
 File ACL: 0    Directory ACL: 0
 Links: 1   Blockcount: 0
 Fragment:  Address: 0    Number: 0    Size: 0
-Size of extra inode fields: 28
+Size of extra inode fields: 32
 Fast link dest: "/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
 debugfs -R "stat /l_70" test.img
 Inode: 13   Type: symlink    Mode:  0777   Flags: 0x10000000
@@ -32,7 +32,7 @@ User:     0   Group:     0   Size: 71
 File ACL: 0    Directory ACL: 0
 Links: 1   Blockcount: 0
 Fragment:  Address: 0    Number: 0    Size: 0
-Size of extra inode fields: 28
+Size of extra inode fields: 32
 Extended attributes:
   system.data (11)
 Fast link dest: "/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
@@ -43,7 +43,7 @@ User:     0   Group:     0   Size: 501
 File ACL: 0    Directory ACL: 0
 Links: 1   Blockcount: 2
 Fragment:  Address: 0    Number: 0    Size: 0
-Size of extra inode fields: 28
+Size of extra inode fields: 32
 EXTENTS:
 (0):153
 debugfs -R "stat /l_1023" test.img
@@ -53,7 +53,7 @@ User:     0   Group:     0   Size: 1024
 File ACL: 0    Directory ACL: 0
 Links: 1   Blockcount: 2
 Fragment:  Address: 0    Number: 0    Size: 0
-Size of extra inode fields: 28
+Size of extra inode fields: 32
 EXTENTS:
 (0):154
 debugfs -R "stat /l_1024" test.img
diff --git a/tests/m_bigjournal/expect.1 b/tests/m_bigjournal/expect.1
index 61d85f9..8900596 100644
--- a/tests/m_bigjournal/expect.1
+++ b/tests/m_bigjournal/expect.1
@@ -35,8 +35,8 @@ Reserved blocks uid:      0
 Reserved blocks gid:      0
 First inode:              11
 Inode size:	          256
-Required extra isize:     28
-Desired extra isize:      28
+Required extra isize:     32
+Desired extra isize:      32
 Journal inode:            8
 Default directory hash:   half_md4
 Journal backup:           inode blocks
diff --git a/tests/m_large_file/expect.1 b/tests/m_large_file/expect.1
index 4acca41..06c8257 100644
--- a/tests/m_large_file/expect.1
+++ b/tests/m_large_file/expect.1
@@ -40,8 +40,8 @@ Reserved blocks uid:      0
 Reserved blocks gid:      0
 First inode:              11
 Inode size:	          256
-Required extra isize:     28
-Desired extra isize:      28
+Required extra isize:     32
+Desired extra isize:      32
 Default directory hash:   half_md4
 
 
diff --git a/tests/m_quota/expect.1 b/tests/m_quota/expect.1
index 787871c..967781b 100644
--- a/tests/m_quota/expect.1
+++ b/tests/m_quota/expect.1
@@ -6,19 +6,19 @@ Allocating group tables:      \b\b\b\b\bdone
 Writing inode tables:      \b\b\b\b\bdone                            
 Writing superblocks and filesystem accounting information:      \b\b\b\b\bdone
 
-Filesystem features: ext_attr resize_inode dir_index filetype sparse_super quota
+Filesystem features: ext_attr resize_inode dir_index filetype sparse_super quota project
 Pass 1: Checking inodes, blocks, and sizes
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-test_filesys: 11/32768 files (18.2% non-contiguous), 5703/131072 blocks
+test_filesys: 12/32768 files (25.0% non-contiguous), 5709/131072 blocks
 Exit status is 0
 Filesystem volume name:   <none>
 Last mounted on:          <not available>
 Filesystem magic number:  0xEF53
 Filesystem revision #:    1 (dynamic)
-Filesystem features:      ext_attr resize_inode dir_index filetype sparse_super quota
+Filesystem features:      ext_attr resize_inode dir_index filetype sparse_super quota project
 Default mount options:    (none)
 Filesystem state:         clean
 Errors behavior:          Continue
@@ -26,8 +26,8 @@ Filesystem OS type:       Linux
 Inode count:              32768
 Block count:              131072
 Reserved block count:     6553
-Free blocks:              125369
-Free inodes:              32757
+Free blocks:              125363
+Free inodes:              32756
 First block:              1
 Block size:               1024
 Fragment size:            1024
@@ -45,6 +45,7 @@ Inode size:	          128
 Default directory hash:   half_md4
 User quota inode:         3
 Group quota inode:        4
+Project quota inode:      12
 
 
 Group 0: (Blocks 1-8192)
@@ -52,9 +53,9 @@ Group 0: (Blocks 1-8192)
   Reserved GDT blocks at 3-258
   Block bitmap at 259 (+258), Inode bitmap at 260 (+259)
   Inode table at 261-516 (+260)
-  7650 free blocks, 2037 free inodes, 2 directories
-  Free blocks: 543-8192
-  Free inodes: 12-2048
+  7644 free blocks, 2036 free inodes, 2 directories
+  Free blocks: 549-8192
+  Free inodes: 13-2048
 Group 1: (Blocks 8193-16384)
   Backup superblock at 8193, Group descriptors at 8194-8194
   Reserved GDT blocks at 8195-8450
diff --git a/tests/m_quota/script b/tests/m_quota/script
index fe63939..57a682e 100644
--- a/tests/m_quota/script
+++ b/tests/m_quota/script
@@ -1,6 +1,6 @@
 DESCRIPTION="enable quota feature on mkfs"
 FS_SIZE=131072
-MKE2FS_OPTS="-O quota"
+MKE2FS_OPTS="-O quota,project"
 if [ "$QUOTA" != "y" ]; then
 	echo "$test_name: $DESCRIPTION: skipped"
 	return 0
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [v3 4/5] Add inherit flags for project quota
  2016-01-12  8:56 [v3 0/5] Add project quota support for e2fsprogs Li Xi
                   ` (2 preceding siblings ...)
  2016-01-12  8:56 ` [v3 3/5] Add project quota support Li Xi
@ 2016-01-12  8:56 ` Li Xi
  2016-01-12  8:56 ` [v3 5/5] Add project ID support for chattr/lsattr Li Xi
  4 siblings, 0 replies; 9+ messages in thread
From: Li Xi @ 2016-01-12  8:56 UTC (permalink / raw)
  To: linux-ext4, tytso, adilger, jack, viro, hch, dmonakhov

This patch add EXT4_PROJINHERIT_FL to enable inherit feature for
project ID. If an directory has its inherit flag set, all its
newly created children will inherit its project ID. Conversely,
new inodes will get a default project ID (i.e. zero). Also, no
hard link or rename is permitted if the directory and child has
different project ID.

Signed-off-by: Li Xi <lixi@ddn.com>
Signed-off-by: Wang Shilong <wshilong@ddn.com>
---
 lib/e2p/pf.c         |    1 +
 lib/ext2fs/ext2_fs.h |    5 +++--
 misc/chattr.c        |    3 ++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/lib/e2p/pf.c b/lib/e2p/pf.c
index 8961727..27421a6 100644
--- a/lib/e2p/pf.c
+++ b/lib/e2p/pf.c
@@ -46,6 +46,7 @@ static struct flags_name flags_array[] = {
 	{ EXT4_HUGE_FILE_FL, "h", "Huge_file" },
 	{ FS_NOCOW_FL, "C", "No_COW" },
 	{ EXT4_INLINE_DATA_FL, "N", "Inline_Data" },
+	{ EXT4_PROJINHERIT_FL, "P", "Project_Iherit" },
 	{ 0, NULL, NULL }
 };
 
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 52c6e23..74650db 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -326,10 +326,11 @@ struct ext2_dx_tail {
 #define EXT4_SNAPFILE_DELETED_FL	0x04000000  /* Snapshot is being deleted */
 #define EXT4_SNAPFILE_SHRUNK_FL		0x08000000  /* Snapshot shrink has completed */
 #define EXT4_INLINE_DATA_FL		0x10000000 /* Inode has inline data */
+#define EXT4_PROJINHERIT_FL		0x20000000 /* Create with parents projid */
 #define EXT2_RESERVED_FL		0x80000000 /* reserved for ext2 lib */
 
-#define EXT2_FL_USER_VISIBLE		0x004BDFFF /* User visible flags */
-#define EXT2_FL_USER_MODIFIABLE		0x004B80FF /* User modifiable flags */
+#define EXT2_FL_USER_VISIBLE		0x204BDFFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE		0x204B80FF /* User modifiable flags */
 
 /*
  * ioctl commands
diff --git a/misc/chattr.c b/misc/chattr.c
index e55d693..40069c9 100644
--- a/misc/chattr.c
+++ b/misc/chattr.c
@@ -83,7 +83,7 @@ static unsigned long sf;
 static void usage(void)
 {
 	fprintf(stderr,
-		_("Usage: %s [-RVf] [-+=aAcCdDeijsStTu] [-v version] files...\n"),
+		_("Usage: %s [-RVf] [-+=aAcCdDeijPsStTu] [-v version] files...\n"),
 		program_name);
 	exit(1);
 }
@@ -103,6 +103,7 @@ static const struct flags_char flags_array[] = {
 	{ EXT4_EXTENTS_FL, 'e'},
 	{ EXT2_IMMUTABLE_FL, 'i' },
 	{ EXT3_JOURNAL_DATA_FL, 'j' },
+	{ EXT4_PROJINHERIT_FL, 'P' },
 	{ EXT2_SECRM_FL, 's' },
 	{ EXT2_UNRM_FL, 'u' },
 	{ EXT2_NOTAIL_FL, 't' },
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [v3 5/5] Add project ID support for chattr/lsattr
  2016-01-12  8:56 [v3 0/5] Add project quota support for e2fsprogs Li Xi
                   ` (3 preceding siblings ...)
  2016-01-12  8:56 ` [v3 4/5] Add inherit flags for project quota Li Xi
@ 2016-01-12  8:56 ` Li Xi
  2016-03-01  4:24   ` Theodore Ts'o
  4 siblings, 1 reply; 9+ messages in thread
From: Li Xi @ 2016-01-12  8:56 UTC (permalink / raw)
  To: linux-ext4, tytso, adilger, jack, viro, hch, dmonakhov

Chattr and lsattr can be used to set or get project ID:
chattr -p <project id> file
lsattr -p file

Signed-off-by: Li Xi <lixi@ddn.com>
Signed-off-by: Wang Shilong <wshilong@ddn.com>
---
 lib/e2p/Makefile.in |   11 +++++++++--
 lib/e2p/e2p.h       |    2 ++
 misc/chattr.1.in    |    7 +++++++
 misc/chattr.c       |   33 +++++++++++++++++++++++++++++++--
 misc/lsattr.1.in    |    5 ++++-
 misc/lsattr.c       |   18 ++++++++++++++++--
 6 files changed, 69 insertions(+), 7 deletions(-)

diff --git a/lib/e2p/Makefile.in b/lib/e2p/Makefile.in
index ab036a6..2ab93f4 100644
--- a/lib/e2p/Makefile.in
+++ b/lib/e2p/Makefile.in
@@ -19,7 +19,7 @@ all::	e2p.pc
 OBJS=		feature.o fgetflags.o fsetflags.o fgetversion.o fsetversion.o \
 		getflags.o getversion.o hashstr.o iod.o ls.o mntopts.o \
 		parse_num.o pe.o pf.o ps.o setflags.o setversion.o uuid.o \
-		ostype.o percent.o crypto_mode.o
+		ostype.o percent.o crypto_mode.o fgetproject.o fsetproject.o
 
 SRCS=		$(srcdir)/feature.c $(srcdir)/fgetflags.c \
 		$(srcdir)/fsetflags.c $(srcdir)/fgetversion.c \
@@ -28,7 +28,8 @@ SRCS=		$(srcdir)/feature.c $(srcdir)/fgetflags.c \
 		$(srcdir)/ls.c $(srcdir)/mntopts.c $(srcdir)/parse_num.c \
 		$(srcdir)/pe.c $(srcdir)/pf.c $(srcdir)/ps.c \
 		$(srcdir)/setflags.c $(srcdir)/setversion.c $(srcdir)/uuid.c \
-		$(srcdir)/ostype.c $(srcdir)/percent.c $(srcdir)/crypto_mode.c
+		$(srcdir)/ostype.c $(srcdir)/percent.c $(srcdir)/crypto_modei.c \
+		$(srcdir)/fgetproject.c $(srcdir)/fsetproject.c
 HFILES= e2p.h
 
 LIBRARY= libe2p
@@ -137,6 +138,12 @@ fgetversion.o: $(srcdir)/fgetversion.c $(top_builddir)/lib/config.h \
 fsetversion.o: $(srcdir)/fsetversion.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
  $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+fsetproject.o: $(srcdir)/fsetproject.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h $(srcdir)/project.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+fgetproject.o: $(srcdir)/fgetproject.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h $(srcdir)/project.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
 getflags.o: $(srcdir)/getflags.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
  $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
diff --git a/lib/e2p/e2p.h b/lib/e2p/e2p.h
index 5fa41f4..66b83b4 100644
--- a/lib/e2p/e2p.h
+++ b/lib/e2p/e2p.h
@@ -32,6 +32,8 @@ int fgetflags (const char * name, unsigned long * flags);
 int fgetversion (const char * name, unsigned long * version);
 int fsetflags (const char * name, unsigned long flags);
 int fsetversion (const char * name, unsigned long version);
+int fgetproject(const char *name, unsigned long *project);
+int fsetproject(const char *name, unsigned long project);
 int getflags (int fd, unsigned long * flags);
 int getversion (int fd, unsigned long * version);
 int iterate_on_dir (const char * dir_name,
diff --git a/misc/chattr.1.in b/misc/chattr.1.in
index 1d6b057..9cd14b2 100644
--- a/misc/chattr.1.in
+++ b/misc/chattr.1.in
@@ -12,6 +12,10 @@ chattr \- change file attributes on a Linux file system
 .I version
 ]
 [
+.B \-p
+.I project
+]
+[
 .I mode
 ]
 .I files...
@@ -71,6 +75,9 @@ Suppress most error messages.
 .TP
 .BI \-v " version"
 Set the file's version/generation number.
+.TP
+.BI \-p " project"
+Set the file's project number.
 .SH ATTRIBUTES
 A file with the 'a' attribute set can only be open in append mode for writing.
 Only the superuser or a process possessing the CAP_LINUX_IMMUTABLE
diff --git a/misc/chattr.c b/misc/chattr.c
index 40069c9..982a593 100644
--- a/misc/chattr.c
+++ b/misc/chattr.c
@@ -64,6 +64,9 @@ static int set_version;
 
 static unsigned long version;
 
+static int set_project;
+static unsigned long project;
+
 static int recursive;
 static int verbose;
 static int silent;
@@ -83,7 +86,7 @@ static unsigned long sf;
 static void usage(void)
 {
 	fprintf(stderr,
-		_("Usage: %s [-RVf] [-+=aAcCdDeijPsStTu] [-v version] files...\n"),
+		_("Usage: %s [-pRVf] [-+=aAcCdDeijPsStTu] [-v version] files...\n"),
 		program_name);
 	exit(1);
 }
@@ -146,6 +149,20 @@ static int decode_arg (int * i, int argc, char ** argv)
 				silent = 1;
 				continue;
 			}
+			if (*p == 'p') {
+				(*i)++;
+				if (*i >= argc)
+					usage ();
+				project = strtol (argv[*i], &tmp, 0);
+				if (*tmp) {
+					com_err (program_name, 0,
+						 _("bad project - %s\n"),
+						 argv[*i]);
+					usage ();
+				}
+				set_project = 1;
+				continue;
+			}
 			if (*p == 'v') {
 				(*i)++;
 				if (*i >= argc)
@@ -249,6 +266,18 @@ static int change_attributes(const char * name)
 			return -1;
 		}
 	}
+	if (set_project) {
+		if (verbose)
+			printf (_("Project of %s set as %lu\n"), name, version);
+		if (fsetproject (name, project) == -1) {
+			if (!silent)
+				com_err (program_name, errno,
+					 _("while setting project on %s"),
+					 name);
+			return -1;
+		}
+
+	}
 	if (S_ISDIR(st.st_mode) && recursive)
 		return iterate_on_dir (name, chattr_dir_proc, NULL);
 	return 0;
@@ -312,7 +341,7 @@ int main (int argc, char ** argv)
 		fputs("Can't both set and unset same flag.\n", stderr);
 		exit (1);
 	}
-	if (!(add || rem || set || set_version)) {
+	if (!(add || rem || set || set_version || set_project )) {
 		fputs(_("Must use '-v', =, - or +\n"), stderr);
 		exit (1);
 	}
diff --git a/misc/lsattr.1.in b/misc/lsattr.1.in
index 7798a34..bc137f2 100644
--- a/misc/lsattr.1.in
+++ b/misc/lsattr.1.in
@@ -5,7 +5,7 @@ lsattr \- list file attributes on a Linux second extended file system
 .SH SYNOPSIS
 .B lsattr
 [
-.B \-RVadv
+.B \-RVadpv
 ]
 [
 .I files...
@@ -31,6 +31,9 @@ List directories like other files, rather than listing their contents.
 .TP
 .B \-v
 List the file's version/generation number.
+.TP
+.B \-p
+List the file's project number.
 .SH AUTHOR
 .B lsattr
 was written by Remy Card <Remy.Card@linux.org>.  It is currently being
diff --git a/misc/lsattr.c b/misc/lsattr.c
index f6b4384..786e95c 100644
--- a/misc/lsattr.c
+++ b/misc/lsattr.c
@@ -60,6 +60,7 @@ static unsigned pf_options;
 static int recursive;
 static int verbose;
 static int generation_opt;
+static int project_opt;
 
 #ifdef _LFS64_LARGEFILE
 #define LSTAT		lstat64
@@ -71,7 +72,7 @@ static int generation_opt;
 
 static void usage(void)
 {
-	fprintf(stderr, _("Usage: %s [-RVadlv] [files...]\n"), program_name);
+	fprintf(stderr, _("Usage: %s [-RVadlpv] [files...]\n"), program_name);
 	exit(1);
 }
 
@@ -79,12 +80,22 @@ static int list_attributes (const char * name)
 {
 	unsigned long flags;
 	unsigned long generation;
+	unsigned long project;
 
 	if (fgetflags (name, &flags) == -1) {
 		com_err (program_name, errno, _("While reading flags on %s"),
 			 name);
 		return -1;
 	}
+	if (project_opt) {
+		if (fgetproject(name, &project) == -1) {
+			com_err (program_name, errno,
+				 _("While reading project on %s"),
+				 name);
+			return -1;
+		}
+		printf ("%5lu ", project);
+	}
 	if (generation_opt) {
 		if (fgetversion (name, &generation) == -1) {
 			com_err (program_name, errno,
@@ -171,7 +182,7 @@ int main (int argc, char ** argv)
 #endif
 	if (argc && *argv)
 		program_name = *argv;
-	while ((c = getopt (argc, argv, "RVadlv")) != EOF)
+	while ((c = getopt (argc, argv, "RVadlvp")) != EOF)
 		switch (c)
 		{
 			case 'R':
@@ -192,6 +203,9 @@ int main (int argc, char ** argv)
 			case 'v':
 				generation_opt = 1;
 				break;
+			case 'p':
+				project_opt = 1;
+				break;
 			default:
 				usage();
 		}
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [v3 5/5] Add project ID support for chattr/lsattr
  2016-01-12  8:56 ` [v3 5/5] Add project ID support for chattr/lsattr Li Xi
@ 2016-03-01  4:24   ` Theodore Ts'o
  2016-03-05 17:39     ` Theodore Ts'o
  2016-03-06  0:13     ` Li Xi
  0 siblings, 2 replies; 9+ messages in thread
From: Theodore Ts'o @ 2016-03-01  4:24 UTC (permalink / raw)
  To: Li Xi; +Cc: linux-ext4

On Tue, Jan 12, 2016 at 05:56:48PM +0900, Li Xi wrote:
> Chattr and lsattr can be used to set or get project ID:
> chattr -p <project id> file
> lsattr -p file
> 
> Signed-off-by: Li Xi <lixi@ddn.com>
> Signed-off-by: Wang Shilong <wshilong@ddn.com>
> ---
>  lib/e2p/Makefile.in |   11 +++++++++--
>  lib/e2p/e2p.h       |    2 ++
>  misc/chattr.1.in    |    7 +++++++
>  misc/chattr.c       |   33 +++++++++++++++++++++++++++++++--
>  misc/lsattr.1.in    |    5 ++++-
>  misc/lsattr.c       |   18 ++++++++++++++++--
>  6 files changed, 69 insertions(+), 7 deletions(-)

Hi, this patch is missing the new files for fgetproject.c and
fsetproject.c.   Can you resend them, please?

						- Ted

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [v3 5/5] Add project ID support for chattr/lsattr
  2016-03-01  4:24   ` Theodore Ts'o
@ 2016-03-05 17:39     ` Theodore Ts'o
  2016-03-06  0:13     ` Li Xi
  1 sibling, 0 replies; 9+ messages in thread
From: Theodore Ts'o @ 2016-03-05 17:39 UTC (permalink / raw)
  To: Li Xi; +Cc: linux-ext4

On Mon, Feb 29, 2016 at 11:24:58PM -0500, Theodore Ts'o wrote:
> Hi, this patch is missing the new files for fgetproject.c and
> fsetproject.c.   Can you send them, please?

Hearing nothing, I've come up with my own versions of these two files
and checked in the project ID support into e2fsprogs.

        	       	   	      - Ted

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [v3 5/5] Add project ID support for chattr/lsattr
  2016-03-01  4:24   ` Theodore Ts'o
  2016-03-05 17:39     ` Theodore Ts'o
@ 2016-03-06  0:13     ` Li Xi
  1 sibling, 0 replies; 9+ messages in thread
From: Li Xi @ 2016-03-06  0:13 UTC (permalink / raw)
  To: Theodore Ts'o; +Cc: Ext4 Developers List

Hi Teb, sorry for my late reply.

We are improving the patches internally. But it was on a different branch.
And I am rebasing it to master branch. I will push the patch series again
in a few hours.

Regards,
Li Xi

On Tue, Mar 1, 2016 at 12:24 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> On Tue, Jan 12, 2016 at 05:56:48PM +0900, Li Xi wrote:
>> Chattr and lsattr can be used to set or get project ID:
>> chattr -p <project id> file
>> lsattr -p file
>>
>> Signed-off-by: Li Xi <lixi@ddn.com>
>> Signed-off-by: Wang Shilong <wshilong@ddn.com>
>> ---
>>  lib/e2p/Makefile.in |   11 +++++++++--
>>  lib/e2p/e2p.h       |    2 ++
>>  misc/chattr.1.in    |    7 +++++++
>>  misc/chattr.c       |   33 +++++++++++++++++++++++++++++++--
>>  misc/lsattr.1.in    |    5 ++++-
>>  misc/lsattr.c       |   18 ++++++++++++++++--
>>  6 files changed, 69 insertions(+), 7 deletions(-)
>
> Hi, this patch is missing the new files for fgetproject.c and
> fsetproject.c.   Can you resend them, please?
>
>                                                 - Ted

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2016-03-06  0:13 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-12  8:56 [v3 0/5] Add project quota support for e2fsprogs Li Xi
2016-01-12  8:56 ` [v3 1/5] Clean up codes for adding new quota type Li Xi
2016-01-12  8:56 ` [v3 2/5] Add project feature flag EXT4_FEATURE_RO_COMPAT_PROJECT Li Xi
2016-01-12  8:56 ` [v3 3/5] Add project quota support Li Xi
2016-01-12  8:56 ` [v3 4/5] Add inherit flags for project quota Li Xi
2016-01-12  8:56 ` [v3 5/5] Add project ID support for chattr/lsattr Li Xi
2016-03-01  4:24   ` Theodore Ts'o
2016-03-05 17:39     ` Theodore Ts'o
2016-03-06  0:13     ` Li Xi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.