2015-03-11 03:38:07

by Li Xi

[permalink] [raw]
Subject: [PATCH 0/2] add project quota support

An implementation of project quota support for ext4 has been
proposed. And the following patches provide project quota
support for e2fsprogs.

Any comments or feedbacks are appreciated.

Regards,
- Li Xi

Li Xi (2):
quota: cleanup codes for new quota type.
quota: add project quota support

debugfs/set_fields.c | 2 +
e2fsck/pass1.c | 28 ++++++++++--
e2fsck/quota.c | 28 +++++-------
e2fsck/unix.c | 26 +++++-----
lib/e2p/feature.c | 2 +
lib/e2p/ls.c | 31 ++++++++++---
lib/e2p/pf.c | 1 +
lib/ext2fs/ext2_fs.h | 12 +++--
lib/ext2fs/ext2fs.h | 3 +-
lib/ext2fs/swapfs.c | 2 +
lib/quota/mkquota.c | 110 +++++++++++++++++++++++++++------------------
lib/quota/quotaio.c | 82 +++++++++++++++++++++++-----------
lib/quota/quotaio.h | 72 ++++++++++++++++++++++--------
lib/quota/quotaio_tree.c | 2 +-
misc/chattr.c | 3 +-
misc/ext4.5.in | 5 ++
misc/mke2fs.c | 13 +++--
misc/tune2fs.c | 88 +++++++++++++++++++++---------------
18 files changed, 331 insertions(+), 179 deletions(-)



2015-03-11 03:38:09

by Li Xi

[permalink] [raw]
Subject: [PATCH 1/2] quota: cleanup codes for new quota type.

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 <[email protected]>
---
debugfs/set_fields.c | 1 +
e2fsck/pass1.c | 28 +++++++++++--
e2fsck/quota.c | 28 +++++--------
e2fsck/unix.c | 26 ++++++------
lib/e2p/ls.c | 30 +++++++++++---
lib/quota/mkquota.c | 99 +++++++++++++++++++++++++--------------------
lib/quota/quotaio.c | 73 ++++++++++++++++++++++------------
lib/quota/quotaio.h | 63 ++++++++++++++++++++++-------
lib/quota/quotaio_tree.c | 2 +-
misc/mke2fs.c | 8 ++--
misc/tune2fs.c | 81 +++++++++++++++++++++-----------------
11 files changed, 271 insertions(+), 168 deletions(-)

diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index 60695ad..9325ab9 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -39,6 +39,7 @@
#include "debugfs.h"
#include "uuid/uuid.h"
#include "e2p/e2p.h"
+#include "quota/quotaio.h"

static struct ext2_super_block set_sb;
static struct ext2_inode_large set_inode;
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 2b4aae7..05af82a 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -891,6 +891,28 @@ out:
return 0;
}

+static int quota_inum_is_super(struct ext2_super_block *sb, ext2_ino_t ino)
+{
+ enum quota_type qtype;
+
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++)
+ if (*quota_sb_inump(sb, qtype) == ino)
+ return 1;
+
+ return 0;
+}
+
+static int quota_inum_is_reserved(ext2_ino_t ino)
+{
+ enum quota_type qtype;
+
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++)
+ if (quota_type2inum(qtype) == ino)
+ return 1;
+
+ return 0;
+}
+
void e2fsck_pass1(e2fsck_t ctx)
{
int i;
@@ -1429,13 +1451,11 @@ 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 ((fs->super->s_feature_ro_compat &
EXT4_FEATURE_RO_COMPAT_QUOTA) &&
- ((fs->super->s_usr_quota_inum == ino) ||
- (fs->super->s_grp_quota_inum == ino))) {
+ quota_inum_is_super(fs->super, ino)) {
if (!LINUX_S_ISREG(inode->i_mode) &&
fix_problem(ctx, PR_1_QUOTA_BAD_MODE,
&pctx)) {
diff --git a/e2fsck/quota.c b/e2fsck/quota.c
index c6bbb9a..6ffd4ad 100644
--- a/e2fsck/quota.c
+++ b/e2fsck/quota.c
@@ -18,7 +18,7 @@
#include "quota/quotaio.h"

static void move_quota_inode(ext2_filsys fs, ext2_ino_t from_ino,
- ext2_ino_t to_ino, int qtype)
+ ext2_ino_t to_ino, enum quota_type qtype)
{
struct ext2_inode inode;
errcode_t retval;
@@ -63,6 +63,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);

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

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

return;
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 96551a1..c5d76d9 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1191,7 +1191,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();
@@ -1637,13 +1638,12 @@ print_unsupp_features:

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

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

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

if (ctx->qctx) {
- int i, needs_writeout;
- for (i = 0; i < MAXQUOTAS; i++) {
- if (qtype != -1 && qtype != i)
+ int needs_writeout;
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ if (((1 << qtype) & qtype_bits) == 0)
continue;
needs_writeout = 0;
- pctx.num = i;
- retval = quota_compare_and_update(ctx->qctx, i,
+ pctx.num = qtype;
+ retval = quota_compare_and_update(ctx->qctx, qtype,
&needs_writeout);
if ((retval || needs_writeout) &&
fix_problem(ctx, PR_6_UPDATE_QUOTAS, &pctx))
- quota_write_inode(ctx->qctx, i);
+ quota_write_inode(ctx->qctx, 1 << qtype);
}
quota_release_context(&ctx->qctx);
}
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index a7ea38a..93e202d 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -23,6 +23,7 @@
#include <time.h>

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

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

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

inode_blocks_per_group = (((sb->s_inodes_per_group *
EXT2_INODE_SIZE(sb)) +
@@ -434,13 +451,12 @@ void list_super2(struct ext2_super_block * 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 (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
fprintf(f, "Checksum type: %s\n",
checksum_type(sb->s_checksum_type));
diff --git a/lib/quota/mkquota.c b/lib/quota/mkquota.c
index 0ece088..825457b 100644
--- a/lib/quota/mkquota.c
+++ b/lib/quota/mkquota.c
@@ -65,7 +65,7 @@ static void print_dquot(const char *desc, struct dquot *dq)
* Returns 0 if not able to find the quota file, otherwise returns its
* inode number.
*/
-int quota_file_exists(ext2_filsys fs, int qtype, int fmt)
+int quota_file_exists(ext2_filsys fs, enum quota_type qtype, int fmt)
{
char qf_name[256];
errcode_t ret;
@@ -87,12 +87,11 @@ int quota_file_exists(ext2_filsys fs, int qtype, int fmt)
/*
* 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);
@@ -100,7 +99,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;
@@ -110,8 +109,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))
@@ -143,9 +141,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;
@@ -168,15 +167,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;
@@ -194,7 +193,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;
@@ -230,11 +229,20 @@ 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)
- return inode_uid(*inode);
- return inode_gid(*inode);
+ assert(qtype >= 0);
+ assert(qtype < MAXQUOTAS);
+ switch (qtype) {
+ case USRQUOTA:
+ return inode_uid(*inode);
+ case GRPQUOTA:
+ return inode_gid(*inode);
+ default:
+ return 0;
+ }
+
+ return 0;
}

static void quota_dnode_free(dnode_t *node,
@@ -249,11 +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;
+ enum quota_type qtype;
int i;

err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx);
@@ -263,9 +273,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++) {
+ for (qtype = 0; i < MAXQUOTAS; qtype++) {
ctx->quota_file[i] = NULL;
- if ((qtype != -1) && (i != qtype))
+ if (((1 << qtype) & qtype_bits) == 0)
continue;
err = ext2fs_get_mem(sizeof(dict_t), &dict);
if (err) {
@@ -273,7 +283,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);
}
@@ -287,26 +297,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]);
}
}
}
@@ -343,7 +353,7 @@ void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
{
struct dquot *dq;
dict_t *dict;
- int i;
+ enum quota_type qtype;

if (!qctx)
return;
@@ -351,10 +361,10 @@ void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
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;
}
@@ -369,7 +379,7 @@ void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
{
struct dquot *dq;
dict_t *dict;
- int i;
+ enum quota_type qtype;

if (!qctx)
return;
@@ -377,10 +387,10 @@ void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
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;
}
}
@@ -394,7 +404,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;
@@ -402,10 +412,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;
}
}
@@ -538,7 +548,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;
@@ -552,7 +563,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;
@@ -577,7 +588,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;
diff --git a/lib/quota/quotaio.c b/lib/quota/quotaio.c
index c7e5f87..b1f4a4f 100644
--- a/lib/quota/quotaio.c
+++ b/lib/quota/quotaio.c
@@ -15,6 +15,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
+#include <assert.h>

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

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

-const char *quota_get_qf_path(const char *mntpt, int qtype, int fmt,
+const char *quota_get_qf_path(const char *mntpt, enum quota_type qtype, int fmt,
char *path_buf, size_t path_buf_size)
{
char qf_name[QUOTA_NAME_LEN];
@@ -114,11 +135,16 @@ errcode_t quota_inode_truncate(ext2_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;
@@ -198,14 +224,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)
@@ -215,14 +242,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));
@@ -230,8 +253,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;
@@ -252,13 +275,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;
}
@@ -268,7 +291,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:
@@ -314,7 +337,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;
@@ -324,11 +348,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);
@@ -354,7 +375,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/quota/quotaio.h b/lib/quota/quotaio.h
index 7ca7830..8b8a945 100644
--- a/lib/quota/quotaio.h
+++ b/lib/quota/quotaio.h
@@ -44,9 +44,20 @@

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

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

typedef struct quota_ctx *quota_ctx_t;

@@ -104,7 +115,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 +185,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 +201,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,29 +210,47 @@ void update_grace_times(struct dquot *q);
than maxlen of extensions[] and fmtnames[] (plus 2) found in quotaio.c */
#define QUOTA_NAME_LEN 16

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

/* 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, int fmt);
-void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype);
-errcode_t quota_compare_and_update(quota_ctx_t qctx, int qtype,
+errcode_t quota_remove_inode(ext2_filsys fs, enum quota_type qtype);
+int quota_file_exists(ext2_filsys fs, enum quota_type qtype, int fmt);
+void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, enum quota_type qtype);
+errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype,
int *usage_inconsistent);

-
+#include <assert.h>
+
+static inline ext2_ino_t *quota_sb_inump(struct ext2_super_block *sb, enum quota_type qtype)
+{
+ assert(qtype >= 0);
+ assert(qtype < MAXQUOTAS);
+ switch (qtype) {
+ case USRQUOTA:
+ return &sb->s_usr_quota_inum;
+ case GRPQUOTA:
+ return &sb->s_grp_quota_inum;
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}

#endif /* GUARD_QUOTAIO_H */
diff --git a/lib/quota/quotaio_tree.c b/lib/quota/quotaio_tree.c
index e7f3e95..2a85698 100644
--- a/lib/quota/quotaio_tree.c
+++ b/lib/quota/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 aeb852f..18da59d 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -95,7 +95,7 @@ static int lazy_itable_init;
static int packed_meta_blocks;
static char *bad_blocks_filename = NULL;
static __u32 fs_stride;
-static int quotatype = -1; /* Initialize both user and group quotas by default */
+static unsigned int quotatype_bits = QUOTA_ALL_BIT; /* Initialize all quotas by default */
static __u64 offset;
static blk64_t journal_location = ~0LL;
static int proceed_delay = -1;
@@ -1012,9 +1012,9 @@ static void parse_extended_opts(struct ext2_super_block *param,
continue;
}
if (!strncmp(arg, "usr", 3)) {
- quotatype = 0;
+ quotatype_bits = QUOTA_USR_BIT;
} else if (!strncmp(arg, "grp", 3)) {
- quotatype = 1;
+ quotatype_bits = QUOTA_GRP_BIT;
} else {
fprintf(stderr,
_("Invalid quotatype parameter: %s\n"),
@@ -2646,7 +2646,7 @@ static int create_quota_inodes(ext2_filsys fs)

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

return 0;
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 258121f..eb85e26 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;
@@ -963,6 +963,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)))
@@ -1279,9 +1280,9 @@ mmp_error:
*/
if (!Q_flag) {
Q_flag = 1;
- /* Enable both user quota and group quota by default */
- usrquota = QOPT_ENABLE;
- grpquota = QOPT_ENABLE;
+ /* Enable all quota by default */
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++)
+ quota_enable[qtype] = QOPT_ENABLE;
}
sb->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA;
}
@@ -1296,9 +1297,9 @@ mmp_error:
fputs(_("\nWarning: '^quota' option overrides '-Q'"
"arguments.\n"), stderr);
Q_flag = 1;
- /* Disable both user quota and group quota by default */
- usrquota = QOPT_DISABLE;
- grpquota = QOPT_DISABLE;
+ /* Disable all quota by default */
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++)
+ quota_enable[qtype] = QOPT_DISABLE;
}

if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
@@ -1414,43 +1415,51 @@ 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,
- QFMT_VFS_V1)) > 0)
- quota_update_limits(qctx, qf_ino, USRQUOTA);
- quota_write_inode(qctx, USRQUOTA);
- } else if (usrquota == QOPT_DISABLE) {
- quota_remove_inode(fs, USRQUOTA);
+ 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,
- QFMT_VFS_V1)) > 0)
- quota_update_limits(qctx, qf_ino, GRPQUOTA);
- quota_write_inode(qctx, GRPQUOTA);
- } else if (grpquota == QOPT_DISABLE) {
- quota_remove_inode(fs, GRPQUOTA);
+ for (qtype = 0 ; qtype < MAXQUOTAS; qtype++) {
+ if (quota_enable[qtype] == QOPT_ENABLE &&
+ *quota_sb_inump(fs->super, qtype) != 0) {
+ if ((qf_ino = quota_file_exists(fs, qtype,
+ QFMT_VFS_V1)) > 0)
+ quota_update_limits(qctx, qf_ino, qtype);
+ quota_write_inode(qctx, 1 << qtype);
+ } else if (quota_enable[qtype] == QOPT_DISABLE) {
+ quota_remove_inode(fs, qtype);
+ }
}

quota_release_context(&qctx);

- if ((usrquota == QOPT_ENABLE) || (grpquota == QOPT_ENABLE)) {
+ if (enable) {
fs->super->s_feature_ro_compat |= EXT4_FEATURE_RO_COMPAT_QUOTA;
ext2fs_mark_super_dirty(fs);
- } else if (!fs->super->s_usr_quota_inum &&
- !fs->super->s_grp_quota_inum) {
- fs->super->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA;
- ext2fs_mark_super_dirty(fs);
+ } else {
+ for (qtype = 0 ; qtype < MAXQUOTAS; qtype++)
+ if (*quota_sb_inump(fs->super, qtype) != 0)
+ break;
+ if (qtype == MAXQUOTAS) {
+ fs->super->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA;
+ ext2fs_mark_super_dirty(fs);
+ }
}

return;
@@ -1479,13 +1488,13 @@ static void parse_quota_opts(const char *opts)
}

if (strcmp(token, "usrquota") == 0) {
- usrquota = QOPT_ENABLE;
+ quota_enable[USRQUOTA] = QOPT_ENABLE;
} else if (strcmp(token, "^usrquota") == 0) {
- usrquota = QOPT_DISABLE;
+ quota_enable[USRQUOTA] = QOPT_DISABLE;
} else if (strcmp(token, "grpquota") == 0) {
- grpquota = QOPT_ENABLE;
+ quota_enable[GRPQUOTA] = QOPT_ENABLE;
} else if (strcmp(token, "^grpquota") == 0) {
- grpquota = QOPT_DISABLE;
+ quota_enable[GRPQUOTA] = QOPT_DISABLE;
} else {
fputs(_("\nBad quota options specified.\n\n"
"Following valid quota options are available "
--
1.7.1


2015-03-11 03:38:11

by Li Xi

[permalink] [raw]
Subject: [PATCH 2/2] quota: add project quota support

This patch adds project quota support so that a file system can
be formated with project quota feature enabled.

Signed-off-by: Li Xi <[email protected]>
---
debugfs/set_fields.c | 1 +
lib/e2p/feature.c | 2 ++
lib/e2p/ls.c | 1 +
lib/e2p/pf.c | 1 +
lib/ext2fs/ext2_fs.h | 12 ++++++++----
lib/ext2fs/ext2fs.h | 3 ++-
lib/ext2fs/swapfs.c | 2 ++
lib/quota/mkquota.c | 11 +++++++++++
lib/quota/quotaio.c | 9 ++++++++-
lib/quota/quotaio.h | 11 ++++++++---
misc/chattr.c | 3 ++-
misc/ext4.5.in | 5 +++++
misc/mke2fs.c | 5 ++++-
misc/tune2fs.c | 7 ++++++-
14 files changed, 61 insertions(+), 12 deletions(-)

diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index 9325ab9..221649a 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -150,6 +150,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/lib/e2p/feature.c b/lib/e2p/feature.c
index 30d2db1..3d482af 100644
--- a/lib/e2p/feature.c
+++ b/lib/e2p/feature.c
@@ -68,6 +68,8 @@ static struct feature feature_list[] = {
"metadata_csum"},
{ E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_REPLICA,
"replica" },
+ { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_PROJECT,
+ "project"},

{ E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION,
"compression" },
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index 93e202d..bddf0c9 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/e2p/pf.c b/lib/e2p/pf.c
index 788c445..feaa171 100644
--- a/lib/e2p/pf.c
+++ b/lib/e2p/pf.c
@@ -51,6 +51,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 10cb650..6b9f243 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -50,7 +50,7 @@
#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */
#define EXT2_JOURNAL_INO 8 /* Journal inode */
-#define EXT2_EXCLUDE_INO 9 /* The "exclude" inode, for snapshots */
+#define EXT4_PRJ_QUOTA_INO 9 /* Project quota inode */
#define EXT4_REPLICA_INO 10 /* Used by non-upstream feature */

/* First non-reserved inode for old ext2 filesystems */
@@ -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
@@ -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 \
@@ -683,7 +685,8 @@ struct ext2_super_block {
__u32 s_overhead_blocks; /* overhead blocks/clusters in fs */
__u32 s_backup_bgs[2]; /* If sparse_super2 enabled */
__u8 s_encrypt_algos[4]; /* Encryption algorithms in use */
- __u32 s_reserved[105]; /* Padding to the end of the block */
+ __u32 s_prj_quota_inum; /* inode number of project quota file */
+ __u32 s_reserved[104]; /* Padding to the end of the block */
__u32 s_checksum; /* crc32c(superblock) */
};

@@ -754,6 +757,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_PROJECT 0x2000 /* Project quota */

#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index f090df1..719416a 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -608,7 +608,8 @@ typedef struct ext2_icount *ext2_icount_t;
EXT4_FEATURE_RO_COMPAT_GDT_CSUM|\
EXT4_FEATURE_RO_COMPAT_BIGALLOC|\
EXT4_LIB_RO_COMPAT_QUOTA|\
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\
+ EXT4_FEATURE_RO_COMPAT_PROJECT)

/*
* These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed
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/quota/mkquota.c b/lib/quota/mkquota.c
index 825457b..7ce3bb3 100644
--- a/lib/quota/mkquota.c
+++ b/lib/quota/mkquota.c
@@ -231,6 +231,12 @@ 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;
+ //int is_large_inode = 0;
+
+ //if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
+ // is_large_inode = 1;
+
assert(qtype >= 0);
assert(qtype < MAXQUOTAS);
switch (qtype) {
@@ -238,6 +244,11 @@ static inline qid_t get_qid(struct ext2_inode *inode, enum quota_type qtype)
return inode_uid(*inode);
case GRPQUOTA:
return inode_gid(*inode);
+ case PRJQUOTA:
+ large_inode = (struct ext2_inode_large *) inode;
+ assert(//is_large_inode &&
+ large_inode->i_extra_isize >= 32);
+ return inode_gid(*inode); //XXX
default:
return 0;
}
diff --git a/lib/quota/quotaio.c b/lib/quota/quotaio.c
index b1f4a4f..53fad1c 100644
--- a/lib/quota/quotaio.c
+++ b/lib/quota/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/quota/quotaio.h b/lib/quota/quotaio.h
index 8b8a945..b7d6195 100644
--- a/lib/quota/quotaio.h
+++ b/lib/quota/quotaio.h
@@ -47,9 +47,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"
@@ -57,7 +58,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;

@@ -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 */
@@ -246,6 +249,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/chattr.c b/misc/chattr.c
index f130108..f5fa397 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' },
diff --git a/misc/ext4.5.in b/misc/ext4.5.in
index 19302a7..a69104a 100644
--- a/misc/ext4.5.in
+++ b/misc/ext4.5.in
@@ -214,6 +214,11 @@ shared storage environments.
@[email protected] which existed
@QUOTA_MAN_COMMENT@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 18da59d..906425a 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -1015,6 +1015,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", 4)) {
+ quotatype_bits = QUOTA_PRJ_BIT;
} else {
fprintf(stderr,
_("Invalid quotatype parameter: %s\n"),
@@ -1088,7 +1090,8 @@ static __u32 ok_features[3] = {
#ifdef CONFIG_QUOTA
EXT4_FEATURE_RO_COMPAT_QUOTA|
#endif
- 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 eb85e26..b3ce8d2 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -160,7 +160,8 @@ static __u32 ok_features[3] = {
#ifdef CONFIG_QUOTA
EXT4_FEATURE_RO_COMPAT_QUOTA |
#endif
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM |
+ EXT4_FEATURE_RO_COMPAT_PROJECT
};

static __u32 clear_ok_features[3] = {
@@ -1495,6 +1496,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 "
--
1.7.1