diff mbox

[v1,2/4] Add project quota support

Message ID 1446046933-26571-3-git-send-email-lixi@ddn.com
State Accepted, archived
Headers show

Commit Message

Li Xi Oct. 28, 2015, 3:42 p.m. UTC
Signed-off-by: Li Xi <lixi@ddn.com>
Change-Id: I30e6e56f8d45e26e2c6c5a1ffae1cf32f1c923cb
---
 debugfs/set_fields.c            |    1 +
 e2fsck/pass1.c                  |   17 ++++++++++++-----
 lib/e2p/ls.c                    |    1 +
 lib/ext2fs/ext2_fs.h            |    6 +++++-
 lib/ext2fs/swapfs.c             |    2 ++
 lib/ext2fs/tst_inode_size.c     |    1 +
 lib/ext2fs/tst_super_size.c     |    3 ++-
 lib/support/mkquota.c           |    5 +++++
 lib/support/quotaio.c           |    9 ++++++++-
 lib/support/quotaio.h           |   11 ++++++++---
 misc/mke2fs.c                   |    6 ++++++
 misc/tune2fs.c                  |    4 ++++
 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          |   15 ++++++++-------
 17 files changed, 73 insertions(+), 28 deletions(-)

Comments

Albino B Neto Oct. 29, 2015, 10:18 a.m. UTC | #1
2015-10-28 13:42 GMT-02:00 Li Xi <pkuelelixi@gmail.com>:
> Signed-off-by: Li Xi <lixi@ddn.com>
> Change-Id: I30e6e56f8d45e26e2c6c5a1ffae1cf32f1c923cb
> ---

Description ?

    Albino
--
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
Andreas Dilger Dec. 12, 2015, 11:04 a.m. UTC | #2
On Oct 28, 2015, at 9:42 AM, Li Xi <pkuelelixi@gmail.com> wrote:
> 
> Signed-off-by: Li Xi <lixi@ddn.com>
> Change-Id: I30e6e56f8d45e26e2c6c5a1ffae1cf32f1c923cb
> ---
> debugfs/set_fields.c            |    1 +
> e2fsck/pass1.c                  |   17 ++++++++++++-----
> lib/e2p/ls.c                    |    1 +
> lib/ext2fs/ext2_fs.h            |    6 +++++-
> lib/ext2fs/swapfs.c             |    2 ++
> lib/ext2fs/tst_inode_size.c     |    1 +
> lib/ext2fs/tst_super_size.c     |    3 ++-
> lib/support/mkquota.c           |    5 +++++
> lib/support/quotaio.c           |    9 ++++++++-
> lib/support/quotaio.h           |   11 ++++++++---
> misc/mke2fs.c                   |    6 ++++++
> misc/tune2fs.c                  |    4 ++++
> 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          |   15 ++++++++-------
> 17 files changed, 73 insertions(+), 28 deletions(-)
> 
> diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
> index 57ef871..c027d8e 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 23d15bd..eb3d19d 100644
> --- a/e2fsck/pass1.c
> +++ b/e2fsck/pass1.c
> @@ -965,13 +965,20 @@ 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)
> -			return 1;
> +	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
> +		if (quota_type2inum(qtype) == ino) {
> +			if (qtype != PRJQUOTA)
> +				return 1;

No need for "else" after return.

> +			else if (quota_inum_is_super(fs->super, ino))
> +				return 1;
> +			else
> +				return 0;

The complexity of this check makes me feel that it is doing the wrong
thing.  quota_type2inum() returns only the reserved quota inodes
EXT4_USR_QUOTA_INO, EXT4_GRP_QUOTA_INO, EXT4_PRJ_QUOTA_INO even
if they are not in use.  Is the intent of checking quota_inum_is_super()
to see if the superblock is referencing the reserved inode also?  Why
would it matter, instead of:

                        return (ino < fs->super->s_first_ino);

That would always be true for USR and GRP, and would be true if PRJ
quota was enabled at format time, or if something else increased the
reserved inode count, in which case the EXT4_PRJ_QUOTA_INO should
also be handled in the same way as USR and GRP inodes.

If we decide that (ino < fs->super->s_first_ino) is correct, it could
be moved into the 1/4 patch I think, though it doesn't really matter.

> +		}
> +	}
> 
> 	return 0;
> }
> @@ -1526,7 +1533,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 ((fs->super->s_feature_ro_compat &
> 					EXT4_FEATURE_RO_COMPAT_QUOTA) &&
> diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
> index 4e80372..d9594cd 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 const *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 cfeaa05..a5f4124 100644
> --- a/lib/ext2fs/ext2_fs.h
> +++ b/lib/ext2fs/ext2_fs.h
> @@ -52,6 +52,7 @@
> #define EXT2_JOURNAL_INO	 8	/* Journal inode */
> #define EXT2_EXCLUDE_INO	 9	/* The "exclude" inode, for snapshots */
> #define EXT4_REPLICA_INO	10	/* Used by non-upstream feature */
> +#define EXT4_PRJ_QUOTA_INO	11	/* Project quota inode */

This should be reserved as EXT4_LOST_FOUND_INO just to avoid many potential
compatibility problems, and then use "12" for EXT4_PRJ_QUOTA_INO.

> /* First non-reserved inode for old ext2 filesystems */
> #define EXT2_GOOD_OLD_FIRST_INO	11

This value should be left as-is, and then a new constant used to increase the
reserved inodes significantly, like:

#define EXT2_NEW_FIRST_INO 32

Some places need to keep using EXT2_GOOD_OLD_FIRST_INO, but we should consider
to change ext2fs_initialize() to use EXT2_NEW_FIRST_INO as well, or should
that only be done on a case-by-case basis for features that need a new
reserved inode, and hope that the feature flags will deter other tools
that assume "11 = lost+found"?

> @@ -473,6 +474,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 +508,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(large_inode) ((large_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 +722,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 */
> +	__u32   s_prj_quota_inum;	/* inode number of project quota file */
> +	__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);

It seems to me that this should be using offsetof() instead of hard-coding
the field offsets, since that seems prone to error, but this is not worse
than any of the other code here.

> 	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 b74c885..442fd38 100644
> --- a/lib/support/mkquota.c
> +++ b/lib/support/mkquota.c
> @@ -233,11 +233,16 @@ 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;

No space after typecast.

> +			return inode_projid(*large_inode);
> 		default:
> 			return 0;
> 	}
> diff --git a/lib/support/quotaio.c b/lib/support/quotaio.c
> index bd8123e..6f52409 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 */
> @@ -56,6 +60,9 @@ ext2_ino_t quota_type2inum(enum quota_type qtype)
> 	case GRPQUOTA:
> 		return EXT4_GRP_QUOTA_INO;
> 		break;
> +	case PRJQUOTA:
> +		return EXT4_PRJ_QUOTA_INO;
> +		break;
> 	default:
> 		return 0;
> 		break;
> diff --git a/lib/support/quotaio.h b/lib/support/quotaio.h
> index fc114e2..438d077 100644
> --- a/lib/support/quotaio.h
> +++ b/lib/support/quotaio.h
> @@ -46,9 +46,10 @@ typedef int64_t qsize_t;	/* Type in which we store size limitations */
> enum quota_type {
> 	USRQUOTA = 0,
> 	GRPQUOTA = 1,
> +	PRJQUOTA = 2,
> };
> 
> -#define MAXQUOTAS 2
> +#define MAXQUOTAS 3

This should be the last item in the enum.

> #if MAXQUOTAS > 32
> #error "cannot have more than 32 quota types to fit in qtype_bits"
> @@ -56,7 +57,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;
> @@ -72,7 +74,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 */
> @@ -240,6 +243,8 @@ static inline ext2_ino_t *quota_sb_inump(struct ext2_super_block *sb, enum quota
> 			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 5ead18e..97aa8c4 100644
> --- a/misc/mke2fs.c
> +++ b/misc/mke2fs.c
> @@ -1017,6 +1017,8 @@ static void parse_extended_opts(struct ext2_super_block *param,
> 				quotatype_bits = QUOTA_USR_BIT;
> 			} else if (!strncmp(arg, "grp", 3)) {
> 				quotatype_bits = QUOTA_GRP_BIT;
> +			} else if (!strncmp(arg, "prj", 3)) {
> +				quotatype_bits = QUOTA_PRJ_BIT;

See comments on previous patch.  This would only allow enabling a single
quota type at a time, which probably isn't enough if someone wants to
enable both "usr,grp" but not "prj".

> 			} else {
> 				fprintf(stderr,
> 					_("Invalid quotatype parameter: %s\n"),
> @@ -2961,6 +2963,10 @@ int main (int argc, char *argv[])
> 		exit(ext2fs_close_free(&fs) ? 1 : 0);
> 	}
> 
> +	if (EXT2_HAS_RO_COMPAT_FEATURE(&fs_param,
> +				       EXT4_FEATURE_RO_COMPAT_QUOTA))
> +		fs->super->s_first_ino = EXT2_GOOD_OLD_FIRST_INO + 1;

This changes compatibility, but hides the details deep in the code here
and doesn't gain much benefit for the future.  This should use the
EXT2_NEW_FIRST_INO constant as described above.

> +
> 	if (bad_blocks_filename)
> 		read_bb_file(fs, &bb_list, bad_blocks_filename);
> 	if (cflag)
> diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> index 1684225..94e962c 100644
> --- a/misc/tune2fs.c
> +++ b/misc/tune2fs.c
> @@ -1500,6 +1500,10 @@ static void parse_quota_opts(const char *opts)
> 			quota_enable[GRPQUOTA] = QOPT_ENABLE;
> 		} else if (strcmp(token, "^grpquota") == 0) {
> 			quota_enable[GRPQUOTA] = QOPT_DISABLE;
> +		} else if (strcmp(token, "prjquota") == 0) {
> +			quota_enable[PRJQUOTA] = QOPT_ENABLE;
> +		} else if (strcmp(token, "^prjquota") == 0) {
> +			quota_enable[PRJQUOTA] = QOPT_DISABLE;
> 		} else {
> 			fputs(_("\nBad quota options specified.\n\n"
> 				"Following valid quota options are available "
> 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..dc8eca9 100644
> --- a/tests/m_quota/expect.1
> +++ b/tests/m_quota/expect.1
> @@ -12,7 +12,7 @@ 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>
> @@ -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
> @@ -40,11 +40,12 @@ Mount count:              0
> Check interval:           15552000 (6 months)
> Reserved blocks uid:      0
> Reserved blocks gid:      0
> -First inode:              11
> +First inode:              12
> Inode size:	          128
> Default directory hash:   half_md4
> User quota inode:         3
> Group quota inode:        4
> +Project quota inode:      11
> 
> 
> 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
> --
> 1.7.1
> 


Cheers, Andreas
diff mbox

Patch

diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index 57ef871..c027d8e 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 23d15bd..eb3d19d 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -965,13 +965,20 @@  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)
-			return 1;
+	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+		if (quota_type2inum(qtype) == ino) {
+			if (qtype != PRJQUOTA)
+				return 1;
+			else if (quota_inum_is_super(fs->super, ino))
+				return 1;
+			else
+				return 0;
+		}
+	}
 
 	return 0;
 }
@@ -1526,7 +1533,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 ((fs->super->s_feature_ro_compat &
 					EXT4_FEATURE_RO_COMPAT_QUOTA) &&
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index 4e80372..d9594cd 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 const *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 cfeaa05..a5f4124 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -52,6 +52,7 @@ 
 #define EXT2_JOURNAL_INO	 8	/* Journal inode */
 #define EXT2_EXCLUDE_INO	 9	/* The "exclude" inode, for snapshots */
 #define EXT4_REPLICA_INO	10	/* Used by non-upstream feature */
+#define EXT4_PRJ_QUOTA_INO	11	/* Project quota inode */
 
 /* First non-reserved inode for old ext2 filesystems */
 #define EXT2_GOOD_OLD_FIRST_INO	11
@@ -473,6 +474,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 +508,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(large_inode) ((large_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 +722,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 */
+	__u32   s_prj_quota_inum;	/* inode number of project quota file */
+	__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 b74c885..442fd38 100644
--- a/lib/support/mkquota.c
+++ b/lib/support/mkquota.c
@@ -233,11 +233,16 @@  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;
+			return inode_projid(*large_inode);
 		default:
 			return 0;
 	}
diff --git a/lib/support/quotaio.c b/lib/support/quotaio.c
index bd8123e..6f52409 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 */
@@ -56,6 +60,9 @@  ext2_ino_t quota_type2inum(enum quota_type qtype)
 	case GRPQUOTA:
 		return EXT4_GRP_QUOTA_INO;
 		break;
+	case PRJQUOTA:
+		return EXT4_PRJ_QUOTA_INO;
+		break;
 	default:
 		return 0;
 		break;
diff --git a/lib/support/quotaio.h b/lib/support/quotaio.h
index fc114e2..438d077 100644
--- a/lib/support/quotaio.h
+++ b/lib/support/quotaio.h
@@ -46,9 +46,10 @@  typedef int64_t qsize_t;	/* Type in which we store size limitations */
 enum quota_type {
 	USRQUOTA = 0,
 	GRPQUOTA = 1,
+	PRJQUOTA = 2,
 };
 
-#define MAXQUOTAS 2
+#define MAXQUOTAS 3
 
 #if MAXQUOTAS > 32
 #error "cannot have more than 32 quota types to fit in qtype_bits"
@@ -56,7 +57,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;
@@ -72,7 +74,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 */
@@ -240,6 +243,8 @@  static inline ext2_ino_t *quota_sb_inump(struct ext2_super_block *sb, enum quota
 			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 5ead18e..97aa8c4 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -1017,6 +1017,8 @@  static void parse_extended_opts(struct ext2_super_block *param,
 				quotatype_bits = QUOTA_USR_BIT;
 			} else if (!strncmp(arg, "grp", 3)) {
 				quotatype_bits = QUOTA_GRP_BIT;
+			} else if (!strncmp(arg, "prj", 3)) {
+				quotatype_bits = QUOTA_PRJ_BIT;
 			} else {
 				fprintf(stderr,
 					_("Invalid quotatype parameter: %s\n"),
@@ -2961,6 +2963,10 @@  int main (int argc, char *argv[])
 		exit(ext2fs_close_free(&fs) ? 1 : 0);
 	}
 
+	if (EXT2_HAS_RO_COMPAT_FEATURE(&fs_param,
+				       EXT4_FEATURE_RO_COMPAT_QUOTA))
+		fs->super->s_first_ino = EXT2_GOOD_OLD_FIRST_INO + 1;
+
 	if (bad_blocks_filename)
 		read_bb_file(fs, &bb_list, bad_blocks_filename);
 	if (cflag)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 1684225..94e962c 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -1500,6 +1500,10 @@  static void parse_quota_opts(const char *opts)
 			quota_enable[GRPQUOTA] = QOPT_ENABLE;
 		} else if (strcmp(token, "^grpquota") == 0) {
 			quota_enable[GRPQUOTA] = QOPT_DISABLE;
+		} else if (strcmp(token, "prjquota") == 0) {
+			quota_enable[PRJQUOTA] = QOPT_ENABLE;
+		} else if (strcmp(token, "^prjquota") == 0) {
+			quota_enable[PRJQUOTA] = QOPT_DISABLE;
 		} else {
 			fputs(_("\nBad quota options specified.\n\n"
 				"Following valid quota options are available "
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..dc8eca9 100644
--- a/tests/m_quota/expect.1
+++ b/tests/m_quota/expect.1
@@ -12,7 +12,7 @@  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>
@@ -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
@@ -40,11 +40,12 @@  Mount count:              0
 Check interval:           15552000 (6 months)
 Reserved blocks uid:      0
 Reserved blocks gid:      0
-First inode:              11
+First inode:              12
 Inode size:	          128
 Default directory hash:   half_md4
 User quota inode:         3
 Group quota inode:        4
+Project quota inode:      11
 
 
 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