2016-03-06 04:15:05

by Li Xi

[permalink] [raw]
Subject: [v4 0/6] Add project quota support for e2fsprogs

The following patches adds project quota support for e2fsprogs. The
first two patches 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 (6):
Always read full inode structure
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/icheck.c | 26 ++++--
debugfs/lsdel.c | 30 ++++---
debugfs/ncheck.c | 20 +++--
debugfs/quota.c | 2 +-
debugfs/set_fields.c | 3 +
e2fsck/iscan.c | 12 ++-
e2fsck/pass1.c | 56 ++++++++++--
e2fsck/pass1b.c | 27 ++++--
e2fsck/pass4.c | 3 +-
e2fsck/quota.c | 28 +++----
e2fsck/scantest.c | 26 ++++--
e2fsck/unix.c | 26 +++---
lib/e2p/Makefile.in | 11 ++-
lib/e2p/e2p.h | 2 +
lib/e2p/feature.c | 2 +
lib/e2p/fgetproject.c | 57 ++++++++++++
lib/e2p/fsetproject.c | 66 ++++++++++++++
lib/e2p/ls.c | 28 +++++--
lib/e2p/pf.c | 1 +
lib/ext2fs/bmove.c | 37 +++++---
lib/ext2fs/ext2_fs.h | 16 +++-
lib/ext2fs/ext2fs.h | 3 +-
lib/ext2fs/swapfs.c | 17 ++--
lib/ext2fs/tst_inode_size.c | 1 +
lib/ext2fs/tst_iscan.c | 17 +++-
lib/ext2fs/tst_super_size.c | 3 +-
lib/support/mkquota.c | 181 ++++++++++++++++++++++++++------------
lib/support/quotaio.c | 85 ++++++++++++------
lib/support/quotaio.h | 80 ++++++++++++-----
lib/support/quotaio_tree.c | 2 +-
misc/chattr.1.in | 7 ++
misc/chattr.c | 34 +++++++-
misc/e2image.c | 41 ++++++---
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 | 187 ++++++++++++++++++++++-----------------
resize/resize2fs.c | 3 +-
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 | 115 ++++++++++++------------
tests/m_quota/script | 2 +-
46 files changed, 953 insertions(+), 409 deletions(-)
create mode 100644 lib/e2p/fgetproject.c
create mode 100644 lib/e2p/fsetproject.c



2016-03-06 04:15:09

by Li Xi

[permalink] [raw]
Subject: [v4 2/6] Clean up codes for adding new quota type

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 <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
---
debugfs/quota.c | 2 +-
debugfs/set_fields.c | 1 +
e2fsck/pass1.c | 41 ++++++++++++-
e2fsck/quota.c | 28 ++++------
e2fsck/unix.c | 26 ++++----
lib/e2p/ls.c | 27 +++++++--
lib/support/mkquota.c | 127 ++++++++++++++++++++++++++--------------
lib/support/quotaio.c | 67 +++++++++++++--------
lib/support/quotaio.h | 73 +++++++++++++++++------
lib/support/quotaio_tree.c | 2 +-
misc/mke2fs.c | 34 +++++++----
misc/tune2fs.c | 138 +++++++++++++++++++++-----------------------
12 files changed, 351 insertions(+), 215 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 48aa302..62898e5 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -954,6 +954,41 @@ out:
}
}

+/*
+ * Check if the passed ino is one of the used superblock quota inodes.
+ *
+ * Before the quota inodes were journaled, older superblock quota inodes
+ * were just regular files in the filesystem and not reserved inodes. This
+ * checks if the passed ino is one of the s_*_quota_inum superblock fields,
+ * which may not always be the same as the EXT4_*_QUOTA_INO fields.
+ */
+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;
+}
+
+/*
+ * Check if the passed ino is one of the reserved quota inodes.
+ * This checks if the inode number is one of the reserved EXT4_*_QUOTA_INO
+ * inodes. These inodes may or may not be in use by the quota feature.
+ */
+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, fs->super) == ino)
+ return 1;
+
+ return 0;
+}
+
void e2fsck_pass1(e2fsck_t ctx)
{
int i;
@@ -1502,12 +1537,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(fs, 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..f98053b 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, fs->super);
+ 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 dc458d7..7324c86 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;
}
}
@@ -551,7 +558,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;
@@ -565,7 +573,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;
@@ -590,7 +598,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;
@@ -641,3 +649,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..5d693a4 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,31 @@ 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,
+ struct ext2_super_block *sb)
+{
+ 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 +116,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, fs->super))
+ 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 +205,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 +223,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 +234,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 +256,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 +272,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 +317,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 +328,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, fs->super);
+ if (qf_inum == 0)
return -1;

err = ext2fs_read_bitmaps(fs);
@@ -338,7 +355,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..f55f5cb 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, struct ext2_super_block *);

void update_grace_times(struct dquot *q);

@@ -197,27 +209,48 @@ 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, 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);

-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);
-
-
+/*
+ * Return pointer to reserved inode field in superblock for given quota type.
+ *
+ * This allows the caller to get or set the quota inode by type without the
+ * need for the quota array to be contiguous in the superbock.
+ */
+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 b072ab9..3e03797 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


2016-03-06 04:15:07

by Li Xi

[permalink] [raw]
Subject: [v4 1/6] Always read full inode structure

Project quota need use some extra field of inode for computing quota
accounting, this patch tries to use ext2fs_get_next_inode_full()
everywhere to read full inode into memeory.

It also fixes a bug that only copy small inode in the function.

Signed-off-by: Wang Shilong <[email protected]>
---
debugfs/icheck.c | 26 ++++++++++++++++++--------
debugfs/lsdel.c | 30 +++++++++++++++++++-----------
debugfs/ncheck.c | 20 ++++++++++++++------
e2fsck/iscan.c | 12 ++++++++++--
e2fsck/pass1.c | 12 +++++++-----
e2fsck/pass1b.c | 27 +++++++++++++++++++--------
e2fsck/scantest.c | 26 ++++++++++++++++++--------
lib/ext2fs/bmove.c | 37 +++++++++++++++++++++++++------------
lib/ext2fs/tst_iscan.c | 17 ++++++++++++++---
lib/support/mkquota.c | 27 ++++++++++++++++++---------
misc/e2image.c | 41 ++++++++++++++++++++++++++---------------
misc/tune2fs.c | 28 ++++++++++++++++++----------
resize/resize2fs.c | 3 ++-
13 files changed, 208 insertions(+), 98 deletions(-)

diff --git a/debugfs/icheck.c b/debugfs/icheck.c
index 3b9bd14..9f36d27 100644
--- a/debugfs/icheck.c
+++ b/debugfs/icheck.c
@@ -60,8 +60,9 @@ void do_icheck(int argc, char **argv)
int i;
ext2_inode_scan scan = 0;
ext2_ino_t ino;
- struct ext2_inode inode;
errcode_t retval;
+ struct ext2_inode *inode;
+ int inode_size;
char *block_buf;

if (argc < 2) {
@@ -71,8 +72,14 @@ void do_icheck(int argc, char **argv)
if (check_fs_open(argv[0]))
return;

+ inode_size = EXT2_INODE_SIZE(current_fs->super);
+ retval = ext2fs_get_mem(inode_size, &inode);
+ if (retval)
+ return;
+
bw.barray = malloc(sizeof(struct block_info) * argc);
if (!bw.barray) {
+ ext2fs_free_mem(&inode);
com_err("icheck", ENOMEM,
"while allocating inode info array");
return;
@@ -99,7 +106,8 @@ void do_icheck(int argc, char **argv)
}

do {
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
} while (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE);
if (retval) {
com_err("icheck", retval, "while starting inode scan");
@@ -109,27 +117,27 @@ void do_icheck(int argc, char **argv)
while (ino) {
blk64_t blk;

- if (!inode.i_links_count)
+ if (!inode->i_links_count)
goto next;

bw.inode = ino;

- blk = ext2fs_file_acl_block(current_fs, &inode);
+ blk = ext2fs_file_acl_block(current_fs, inode);
if (blk) {
icheck_proc(current_fs, &blk, 0,
0, 0, &bw);
if (bw.blocks_left == 0)
break;
- ext2fs_file_acl_block_set(current_fs, &inode, blk);
+ ext2fs_file_acl_block_set(current_fs, inode, blk);
}

- if (!ext2fs_inode_has_valid_blocks2(current_fs, &inode))
+ if (!ext2fs_inode_has_valid_blocks2(current_fs, inode))
goto next;
/*
* To handle filesystems touched by 0.3c extfs; can be
* removed later.
*/
- if (inode.i_dtime)
+ if (inode->i_dtime)
goto next;

retval = ext2fs_block_iterate3(current_fs, ino,
@@ -146,7 +154,8 @@ void do_icheck(int argc, char **argv)

next:
do {
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
} while (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE);
if (retval) {
com_err("icheck", retval,
@@ -165,6 +174,7 @@ void do_icheck(int argc, char **argv)
}

error_out:
+ ext2fs_free_mem(&inode);
free(bw.barray);
free(block_buf);
if (scan)
diff --git a/debugfs/lsdel.c b/debugfs/lsdel.c
index 7d5e7d8..6d11e3b 100644
--- a/debugfs/lsdel.c
+++ b/debugfs/lsdel.c
@@ -78,7 +78,8 @@ void do_lsdel(int argc, char **argv)
int num_delarray, max_delarray;
ext2_inode_scan scan = 0;
ext2_ino_t ino;
- struct ext2_inode inode;
+ struct ext2_inode *inode;
+ int inode_size;
errcode_t retval;
char *block_buf;
int i;
@@ -121,9 +122,14 @@ void do_lsdel(int argc, char **argv)
"while opening inode scan");
goto error_out;
}
+ inode_size = EXT2_INODE_SIZE(current_fs->super);
+ retval = ext2fs_get_mem(inode_size, &inode);
+ if (retval)
+ goto error_out;

do {
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
} while (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE);
if (retval) {
com_err("ls_deleted_inodes", retval,
@@ -132,8 +138,8 @@ void do_lsdel(int argc, char **argv)
}

while (ino) {
- if ((inode.i_dtime == 0) ||
- (secs && ((unsigned) abs(now - secs) > inode.i_dtime)))
+ if ((inode->i_dtime == 0) ||
+ (secs && ((unsigned) abs(now - secs) > inode->i_dtime)))
goto next;

lsd.inode = ino;
@@ -141,7 +147,7 @@ void do_lsdel(int argc, char **argv)
lsd.free_blocks = 0;
lsd.bad_blocks = 0;

- if (ext2fs_inode_has_valid_blocks2(current_fs, &inode)) {
+ if (ext2fs_inode_has_valid_blocks2(current_fs, inode)) {
retval = ext2fs_block_iterate3(current_fs, ino,
BLOCK_FLAG_READ_ONLY,
block_buf,
@@ -153,7 +159,7 @@ void do_lsdel(int argc, char **argv)
}
}
if ((lsd.free_blocks && !lsd.bad_blocks) ||
- inode.i_flags & EXT4_INLINE_DATA_FL) {
+ inode->i_flags & EXT4_INLINE_DATA_FL) {
if (num_delarray >= max_delarray) {
max_delarray += 50;
delarray = realloc(delarray,
@@ -167,10 +173,10 @@ void do_lsdel(int argc, char **argv)
}

delarray[num_delarray].ino = ino;
- delarray[num_delarray].mode = inode.i_mode;
- delarray[num_delarray].uid = inode_uid(inode);
- delarray[num_delarray].size = EXT2_I_SIZE(&inode);
- delarray[num_delarray].dtime = (__s32) inode.i_dtime;
+ delarray[num_delarray].mode = inode->i_mode;
+ delarray[num_delarray].uid = inode_uid(*inode);
+ delarray[num_delarray].size = EXT2_I_SIZE(inode);
+ delarray[num_delarray].dtime = (__s32) inode->i_dtime;
delarray[num_delarray].num_blocks = lsd.num_blocks;
delarray[num_delarray].free_blocks = lsd.free_blocks;
num_delarray++;
@@ -178,7 +184,8 @@ void do_lsdel(int argc, char **argv)

next:
do {
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
} while (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE);
if (retval) {
com_err("ls_deleted_inodes", retval,
@@ -205,6 +212,7 @@ void do_lsdel(int argc, char **argv)
close_pager(out);

error_out:
+ ext2fs_free_mem(&inode);
free(block_buf);
free(delarray);
if (scan)
diff --git a/debugfs/ncheck.c b/debugfs/ncheck.c
index 5d9b5d2..3becd59 100644
--- a/debugfs/ncheck.c
+++ b/debugfs/ncheck.c
@@ -95,7 +95,8 @@ void do_ncheck(int argc, char **argv)
int c, i;
ext2_inode_scan scan = 0;
ext2_ino_t ino;
- struct ext2_inode inode;
+ struct ext2_inode *inode;
+ int inode_size;
errcode_t retval;
char *tmp;

@@ -145,9 +146,14 @@ void do_ncheck(int argc, char **argv)
com_err("ncheck", retval, "while opening inode scan");
goto error_out;
}
+ inode_size = EXT2_INODE_SIZE(current_fs->super);
+ retval = ext2fs_get_mem(inode_size, &inode);
+ if (retval)
+ goto error_out;

do {
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
} while (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE);
if (retval) {
com_err("ncheck", retval, "while starting inode scan");
@@ -156,16 +162,16 @@ void do_ncheck(int argc, char **argv)

printf("Inode\tPathname\n");
while (ino) {
- if (!inode.i_links_count)
+ if (!inode->i_links_count)
goto next;
/*
* To handle filesystems touched by 0.3c extfs; can be
* removed later.
*/
- if (inode.i_dtime)
+ if (inode->i_dtime)
goto next;
/* Ignore anything that isn't a directory */
- if (!LINUX_S_ISDIR(inode.i_mode))
+ if (!LINUX_S_ISDIR(inode->i_mode))
goto next;

iw.position = 0;
@@ -187,7 +193,8 @@ void do_ncheck(int argc, char **argv)

next:
do {
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
} while (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE);

if (retval) {
@@ -198,6 +205,7 @@ void do_ncheck(int argc, char **argv)
}

error_out:
+ ext2fs_free_mem(inode);
free(iw.iarray);
if (scan)
ext2fs_close_inode_scan(scan);
diff --git a/e2fsck/iscan.c b/e2fsck/iscan.c
index 52cad11..6ea9e11 100644
--- a/e2fsck/iscan.c
+++ b/e2fsck/iscan.c
@@ -97,7 +97,8 @@ int main (int argc, char *argv[])
ext2_filsys fs;
ext2_ino_t ino;
__u32 num_inodes = 0;
- struct ext2_inode inode;
+ struct ext2_inode *inode;
+ int inode_size;
ext2_inode_scan scan;

init_resource_track(&global_rtrack);
@@ -120,8 +121,14 @@ int main (int argc, char *argv[])
exit(1);
}

+ inode_size = EXT2_INODE_SIZE(current_fs->super);
+ retval = ext2fs_get_mem(inode_size, &inode);
+ if (retval)
+ exit(1);
+
while (1) {
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
if (retval) {
com_err(program_name, retval,
_("while getting next inode"));
@@ -132,6 +139,7 @@ int main (int argc, char *argv[])
num_inodes++;
}

+ ext2fs_free_mem(&inode);
print_resource_track(NULL, &global_rtrack);
printf(_("%u inodes scanned.\n"), num_inodes);

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 5a8d4a1..48aa302 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 {
@@ -1741,7 +1741,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);
@@ -1904,7 +1905,8 @@ 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;
+ pctx.inode = ctx->stashed_inode =
+ (struct ext2_inode *)&inodes_to_process[i].inode;
pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;

#if 0
@@ -1942,8 +1944,8 @@ 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;
diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c
index 2cbf82a..20e0d02 100644
--- a/e2fsck/pass1b.c
+++ b/e2fsck/pass1b.c
@@ -271,7 +271,8 @@ static void pass1b(e2fsck_t ctx, char *block_buf)
{
ext2_filsys fs = ctx->fs;
ext2_ino_t ino = 0;
- struct ext2_inode inode;
+ struct ext2_inode *inode;
+ int inode_size;
ext2_inode_scan scan;
struct process_block_struct pb;
struct problem_context pctx;
@@ -288,7 +289,15 @@ static void pass1b(e2fsck_t ctx, char *block_buf)
ctx->flags |= E2F_FLAG_ABORT;
return;
}
- ctx->stashed_inode = &inode;
+ inode_size = EXT2_INODE_SIZE(fs->super);
+ pctx.errcode = ext2fs_get_mem(inode_size, &inode);
+ if (pctx.errcode) {
+ ext2fs_close_inode_scan(scan);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+
+ ctx->stashed_inode = inode;
pb.ctx = ctx;
pb.pctx = &pctx;
pctx.str = "pass1b";
@@ -297,7 +306,8 @@ static void pass1b(e2fsck_t ctx, char *block_buf)
if (e2fsck_mmp_update(fs))
fatal_error(ctx, 0);
}
- pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
+ pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
continue;
if (pctx.errcode) {
@@ -315,24 +325,24 @@ static void pass1b(e2fsck_t ctx, char *block_buf)

pb.ino = ino;
pb.dup_blocks = 0;
- pb.inode = &inode;
+ pb.inode = inode;
pb.cur_cluster = ~0;
pb.phys_cluster = ~0;
pb.last_blk = 0;
pb.pctx->blk = pb.pctx->blk2 = 0;

- if (ext2fs_inode_has_valid_blocks2(fs, &inode) ||
+ if (ext2fs_inode_has_valid_blocks2(fs, inode) ||
(ino == EXT2_BAD_INO))
pctx.errcode = ext2fs_block_iterate3(fs, ino,
BLOCK_FLAG_READ_ONLY, block_buf,
process_pass1b_block, &pb);
/* If the feature is not set, attrs will be cleared later anyway */
if (ext2fs_has_feature_xattr(fs->super) &&
- ext2fs_file_acl_block(fs, &inode)) {
- blk64_t blk = ext2fs_file_acl_block(fs, &inode);
+ ext2fs_file_acl_block(fs, inode)) {
+ blk64_t blk = ext2fs_file_acl_block(fs, inode);
process_pass1b_block(fs, &blk,
BLOCK_COUNT_EXTATTR, 0, 0, &pb);
- ext2fs_file_acl_block_set(fs, &inode, blk);
+ ext2fs_file_acl_block_set(fs, inode, blk);
}
if (pb.dup_blocks) {
if (ino != EXT2_BAD_INO) {
@@ -348,6 +358,7 @@ static void pass1b(e2fsck_t ctx, char *block_buf)
if (pctx.errcode)
fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
}
+ ext2fs_free_mem(&inode);
ext2fs_close_inode_scan(scan);
e2fsck_use_inode_shortcuts(ctx, 0);
}
diff --git a/e2fsck/scantest.c b/e2fsck/scantest.c
index 6131141..87a5b97 100644
--- a/e2fsck/scantest.c
+++ b/e2fsck/scantest.c
@@ -88,12 +88,12 @@ static void print_resource_track(struct resource_track *track)
int main (int argc, char *argv[])
{
errcode_t retval = 0;
- int exit_value = 0;
int i;
ext2_filsys fs;
ext2_inode_scan scan;
ext2_ino_t ino;
- struct ext2_inode inode;
+ struct ext2_inode *inode;
+ int inode_size;

printf(_("size of inode=%d\n"), sizeof(inode));

@@ -114,17 +114,27 @@ int main (int argc, char *argv[])
com_err(argv[0], retval, _("while opening inode scan"));
exit(1);
}
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+
+ inode_size = EXT2_INODE_SIZE(fs->super);
+ retval = ext2fs_get_mem(inode_size, &inode);
+ if (retval) {
+ com_err(argv[0], retval, _("while allocating inode memory"));
+ exit(1);
+ }
+
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
if (retval) {
com_err(argv[0], retval, _("while starting inode scan"));
exit(1);
}
while (ino) {
- if (!inode.i_links_count)
+ if (!inode->i_links_count)
goto next;
- printf("%lu\n", inode.i_blocks);
+ printf("%lu\n", inode->i_blocks);
next:
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
if (retval) {
com_err(argv[0], retval,
_("while doing inode scan"));
@@ -133,9 +143,9 @@ int main (int argc, char *argv[])
}


+ ext2fs_free_mem(&inode);
ext2fs_close_free(&fs);

print_resource_track(&global_rtrack);
-
- return exit_value;
+ return 0;
}
diff --git a/lib/ext2fs/bmove.c b/lib/ext2fs/bmove.c
index e2ea405..93f6ebd 100644
--- a/lib/ext2fs/bmove.c
+++ b/lib/ext2fs/bmove.c
@@ -100,11 +100,17 @@ errcode_t ext2fs_move_blocks(ext2_filsys fs,
int flags)
{
ext2_ino_t ino;
- struct ext2_inode inode;
+ struct ext2_inode *inode;
+ int inode_size;
errcode_t retval;
struct process_block_struct pb;
ext2_inode_scan scan;
- char *block_buf;
+ char *block_buf = NULL;
+
+ inode_size = EXT2_INODE_SIZE(fs->super);
+ retval = ext2fs_get_mem(inode_size, &inode);
+ if (retval)
+ return retval;

retval = ext2fs_open_inode_scan(fs, 0, &scan);
if (retval)
@@ -132,20 +138,21 @@ errcode_t ext2fs_move_blocks(ext2_filsys fs,
}
retval = ext2fs_init_dblist(fs, 0);
if (retval)
- return retval;
+ goto error;
}

- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
if (retval)
- return retval;
+ goto error;

while (ino) {
- if ((inode.i_links_count == 0) ||
- !ext2fs_inode_has_valid_blocks2(fs, &inode))
+ if ((inode->i_links_count == 0) ||
+ !ext2fs_inode_has_valid_blocks2(fs, inode))
goto next;

pb.ino = ino;
- pb.inode = &inode;
+ pb.inode = inode;

pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) &&
flags & EXT2_BMOVE_GET_DBLIST);
@@ -153,15 +160,21 @@ errcode_t ext2fs_move_blocks(ext2_filsys fs,
retval = ext2fs_block_iterate3(fs, ino, 0, block_buf,
process_block, &pb);
if (retval)
- return retval;
- if (pb.error)
- return pb.error;
+ goto error;
+ if (pb.error) {
+ retval = pb.error;
+ goto error;
+ }

next:
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
goto next;
}
+error:
+ ext2fs_free_mem(&inode);
+ ext2fs_free_mem(&block_buf);
return 0;
}

diff --git a/lib/ext2fs/tst_iscan.c b/lib/ext2fs/tst_iscan.c
index 70bfbec..6e72c9d 100644
--- a/lib/ext2fs/tst_iscan.c
+++ b/lib/ext2fs/tst_iscan.c
@@ -140,7 +140,8 @@ static void setup(void)
*/
static void iterate(void)
{
- struct ext2_inode inode;
+ struct ext2_inode *inode;
+ int inode_size;
ext2_inode_scan scan;
errcode_t retval;
ext2_ino_t ino;
@@ -150,14 +151,23 @@ static void iterate(void)
com_err("iterate", retval, "While opening inode scan");
exit(1);
}
+
+ inode_size = EXT2_INODE_SIZE(test_fs->super);
+ retval = ext2fs_get_mem(inode_size, &inode);
+ if (retval) {
+ com_err("iterate", retval, "while allocating inode mem");
+ exit(1);
+ }
printf("Reading blocks: ");
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
if (retval) {
com_err("iterate", retval, "while reading first inode");
exit(1);
}
while (ino) {
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
ext2fs_mark_inode_bitmap2(bad_inode_map, ino);
continue;
@@ -169,6 +179,7 @@ static void iterate(void)
}
}
printf("\n");
+ ext2fs_free_mem(&inode);
ext2fs_close_inode_scan(scan);
}

diff --git a/lib/support/mkquota.c b/lib/support/mkquota.c
index 00e96f8..dc458d7 100644
--- a/lib/support/mkquota.c
+++ b/lib/support/mkquota.c
@@ -420,41 +420,50 @@ 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;
+ int inode_size;
qsize_t space;
ext2_inode_scan scan;

if (!qctx)
return 0;

+ inode_size = EXT2_INODE_SIZE(qctx->fs->super);
+ ret = ext2fs_get_mem(inode_size, &inode);
+ if (ret)
+ return ret;
+
fs = qctx->fs;
ret = ext2fs_open_inode_scan(fs, 0, &scan);
if (ret) {
+ free(inode);
log_err("while opening inode scan. ret=%ld", ret);
return ret;
}

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);
- return ret;
+ goto err;
}
if (ino == 0)
break;
- if (inode.i_links_count &&
+ if (inode->i_links_count &&
(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);
}
}

+err:
ext2fs_close_inode_scan(scan);
+ free(inode);

- return 0;
+ return ret;
}

struct scan_dquots_data {
diff --git a/misc/e2image.c b/misc/e2image.c
index e9b4ae2..cf9e597 100644
--- a/misc/e2image.c
+++ b/misc/e2image.c
@@ -1258,7 +1258,8 @@ static void output_qcow2_meta_data_blocks(ext2_filsys fs, int fd)
static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags)
{
struct process_block_struct pb;
- struct ext2_inode inode;
+ struct ext2_inode *inode;
+ int inode_size;
ext2_inode_scan scan;
ext2_ino_t ino;
errcode_t retval;
@@ -1301,10 +1302,19 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags)
exit(1);
}

+ inode_size = EXT2_INODE_SIZE(fs->super);
+ retval = ext2fs_get_mem(inode_size, &inode);
+ if (retval) {
+ com_err(program_name, retval,
+ _("while allocating inode memory"));
+ exit(1);
+ }
+
use_inode_shortcuts(fs, 1);
- stashed_inode = &inode;
+ stashed_inode = inode;
while (1) {
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
continue;
if (retval) {
@@ -1314,22 +1324,22 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags)
}
if (ino == 0)
break;
- if (!inode.i_links_count)
+ if (!inode->i_links_count)
continue;
- if (ext2fs_file_acl_block(fs, &inode)) {
+ if (ext2fs_file_acl_block(fs, inode)) {
ext2fs_mark_block_bitmap2(meta_block_map,
- ext2fs_file_acl_block(fs, &inode));
+ ext2fs_file_acl_block(fs, inode));
meta_blocks_count++;
}
- if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
+ if (!ext2fs_inode_has_valid_blocks2(fs, inode))
continue;

stashed_ino = ino;
pb.ino = ino;
- pb.is_dir = LINUX_S_ISDIR(inode.i_mode);
- if (LINUX_S_ISDIR(inode.i_mode) ||
- (LINUX_S_ISLNK(inode.i_mode) &&
- ext2fs_inode_has_valid_blocks2(fs, &inode)) ||
+ pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
+ if (LINUX_S_ISDIR(inode->i_mode) ||
+ (LINUX_S_ISLNK(inode->i_mode) &&
+ ext2fs_inode_has_valid_blocks2(fs, inode)) ||
ino == fs->super->s_journal_inum) {
retval = ext2fs_block_iterate3(fs, ino,
BLOCK_FLAG_READ_ONLY, block_buf,
@@ -1341,10 +1351,10 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags)
exit(1);
}
} else {
- if ((inode.i_flags & EXT4_EXTENTS_FL) ||
- inode.i_block[EXT2_IND_BLOCK] ||
- inode.i_block[EXT2_DIND_BLOCK] ||
- inode.i_block[EXT2_TIND_BLOCK] || all_data) {
+ if ((inode->i_flags & EXT4_EXTENTS_FL) ||
+ inode->i_block[EXT2_IND_BLOCK] ||
+ inode->i_block[EXT2_DIND_BLOCK] ||
+ inode->i_block[EXT2_TIND_BLOCK] || all_data) {
retval = ext2fs_block_iterate3(fs,
ino, BLOCK_FLAG_READ_ONLY, block_buf,
process_file_block, &pb);
@@ -1363,6 +1373,7 @@ static void write_raw_image_file(ext2_filsys fs, int fd, int type, int flags)
else
output_meta_data_blocks(fs, fd, flags);

+ ext2fs_free_mem(&inode);
ext2fs_free_mem(&block_buf);
ext2fs_close_inode_scan(scan);
ext2fs_free_block_bitmap(meta_block_map);
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 4d57399..b072ab9 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -2164,26 +2164,33 @@ static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap)
ext2_ino_t ino;
blk64_t blk;
char *block_buf = 0;
- struct ext2_inode inode;
+ struct ext2_inode *inode;
+ int inode_size;
ext2_inode_scan scan = NULL;

- retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf);
+ inode_size = EXT2_INODE_SIZE(fs->super);
+ retval = ext2fs_get_mem(inode_size, &inode);
if (retval)
return retval;

+ retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf);
+ if (retval)
+ goto err_out;
+
retval = ext2fs_open_inode_scan(fs, 0, &scan);
if (retval)
goto err_out;

while (1) {
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ retval = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
if (retval)
goto err_out;

if (!ino)
break;

- if (inode.i_links_count == 0)
+ if (inode->i_links_count == 0)
continue; /* inode not in use */

/* FIXME!!
@@ -2193,26 +2200,26 @@ static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap)
* Do we need to fix this ??
*/

- if (ext2fs_file_acl_block(fs, &inode) &&
+ if (ext2fs_file_acl_block(fs, inode) &&
ext2fs_test_block_bitmap2(bmap,
- ext2fs_file_acl_block(fs, &inode))) {
+ ext2fs_file_acl_block(fs, inode))) {
blk = translate_block(ext2fs_file_acl_block(fs,
- &inode));
+ inode));
if (!blk)
continue;

- ext2fs_file_acl_block_set(fs, &inode, blk);
+ ext2fs_file_acl_block_set(fs, inode, blk);

/*
* Write the inode to disk so that inode table
* resizing can work
*/
- retval = ext2fs_write_inode(fs, ino, &inode);
+ retval = ext2fs_write_inode(fs, ino, inode);
if (retval)
goto err_out;
}

- if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
+ if (!ext2fs_inode_has_valid_blocks2(fs, inode))
continue;

retval = ext2fs_block_iterate3(fs, ino, 0, block_buf,
@@ -2223,6 +2230,7 @@ static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap)
}

err_out:
+ ext2fs_free_mem(&inode);
ext2fs_free_mem(&block_buf);
ext2fs_close_inode_scan(scan);

diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index f8df679..e516073 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -2118,7 +2118,8 @@ errout:
ext2fs_close_inode_scan(scan);
if (block_buf)
ext2fs_free_mem(&block_buf);
- free(inode);
+ if (inode)
+ ext2fs_free_mem(&inode);
return retval;
}

--
1.7.1


2016-03-06 04:15:11

by Li Xi

[permalink] [raw]
Subject: [v4 3/6] Add project feature flag EXT4_FEATURE_RO_COMPAT_PROJECT

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 <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
---
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 3e03797..2fbdf33 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


2016-03-06 04:15:14

by Li Xi

[permalink] [raw]
Subject: [v4 4/6] Add project quota support

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 <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
---
debugfs/set_fields.c | 2 +
e2fsck/pass1.c | 3 +-
e2fsck/pass4.c | 3 +-
lib/e2p/ls.c | 1 +
lib/ext2fs/ext2_fs.h | 9 +++-
lib/ext2fs/swapfs.c | 17 ++++---
lib/ext2fs/tst_inode_size.c | 1 +
lib/ext2fs/tst_super_size.c | 3 +-
lib/support/mkquota.c | 27 ++++++++-
lib/support/quotaio.c | 20 ++++++-
lib/support/quotaio.h | 11 +++-
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 | 115 ++++++++++++++++++++-------------------
tests/m_quota/script | 2 +-
20 files changed, 188 insertions(+), 90 deletions(-)

diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index bc63802..29e8996 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 },
@@ -230,6 +231,7 @@ static struct field_set_info inode_fields[] = {
4, parse_time },
{ "crtime_extra", &set_inode.i_crtime_extra, NULL,
4, parse_uint, FLAG_ALIAS },
+ { "i_projid", &set_inode.i_projid, NULL, 4, parse_uint },
{ "bmap", NULL, NULL, 4, parse_bmap, FLAG_ARRAY },
{ 0, 0, 0, 0 }
};
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 62898e5..07d2936 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -3122,7 +3122,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/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..fa3b309 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 \
@@ -483,6 +484,10 @@ struct ext2_inode_large {

#define i_checksum_lo osd2.linux2.l_i_checksum_lo

+#define inode_includes(size, field) \
+ (size >= (sizeof(((struct ext2_inode_large *)0)->field) + \
+ offsetof(struct ext2_inode_large, field)))
+
#if defined(__KERNEL__) || defined(__linux__)
#define i_reserved1 osd1.linux1.l_i_reserved1
#define i_frag osd2.linux2.l_i_frag
@@ -506,6 +511,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 +725,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..18535e5 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -306,20 +306,23 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
return;
}

- if (extra_isize >= 4)
+ inode_size = EXT2_GOOD_OLD_INODE_SIZE + extra_isize;
+ if (inode_includes(inode_size, i_checksum_hi))
t->i_checksum_hi = ext2fs_swab16(f->i_checksum_hi);
- if (extra_isize >= 8)
+ if (inode_includes(inode_size, i_ctime_extra))
t->i_ctime_extra = ext2fs_swab32(f->i_ctime_extra);
- if (extra_isize >= 12)
+ if (inode_includes(inode_size, i_mtime_extra))
t->i_mtime_extra = ext2fs_swab32(f->i_mtime_extra);
- if (extra_isize >= 16)
+ if (inode_includes(inode_size, i_atime_extra))
t->i_atime_extra = ext2fs_swab32(f->i_atime_extra);
- if (extra_isize >= 20)
+ if (inode_includes(inode_size, i_crtime))
t->i_crtime = ext2fs_swab32(f->i_crtime);
- if (extra_isize >= 24)
+ if (inode_includes(inode_size, i_crtime_extra))
t->i_crtime_extra = ext2fs_swab32(f->i_crtime_extra);
- if (extra_isize >= 28)
+ if (inode_includes(inode_size, i_version_hi))
t->i_version_hi = ext2fs_swab32(f->i_version_hi);
+ if (inode_includes(inode_size, i_projid))
+ t->i_projid = ext2fs_swab16(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 7324c86..9048c47 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))
+ 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,20 @@ 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 inode_size;
+
switch (qtype) {
case USRQUOTA:
return inode_uid(*inode);
case GRPQUOTA:
return inode_gid(*inode);
+ case PRJQUOTA:
+ large_inode = (struct ext2_inode_large *)inode;
+ inode_size = EXT2_GOOD_OLD_INODE_SIZE +
+ large_inode->i_extra_isize;
+ if (inode_includes(inode_size, i_projid))
+ return inode_projid(*large_inode);
default:
return 0;
}
diff --git a/lib/support/quotaio.c b/lib/support/quotaio.c
index 5d693a4..ba3a422 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 */
@@ -53,6 +57,8 @@ ext2_ino_t quota_type2inum(enum quota_type qtype,
return EXT4_USR_QUOTA_INO;
case GRPQUOTA:
return EXT4_GRP_QUOTA_INO;
+ case PRJQUOTA:
+ return sb->s_prj_quota_inum;
default:
return 0;
}
@@ -322,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;
+ unsigned long qf_inum = 0;

if (fmt == -1)
fmt = QFMT_VFS_V1;

h->qh_qf.fs = fs;
qf_inum = quota_type2inum(qtype, fs->super);
- if (qf_inum == 0)
+ 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 f55f5cb..8deb544 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 */
@@ -246,6 +249,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 2fbdf33..3d1aa8b 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..8cdad30 100644
--- a/tests/m_quota/expect.1
+++ b/tests/m_quota/expect.1
@@ -6,19 +6,19 @@ Allocating group tables: done
Writing inode tables: done
Writing superblocks and filesystem accounting information: done

-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), 9805/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: 121267
+Free inodes: 32756
First block: 1
Block size: 1024
Fragment size: 1024
@@ -35,123 +35,126 @@ Reserved GDT blocks: 256
Blocks per group: 8192
Fragments per group: 8192
Inodes per group: 2048
-Inode blocks per group: 256
+Inode blocks per group: 512
Mount count: 0
Check interval: 15552000 (6 months)
Reserved blocks uid: 0
Reserved blocks gid: 0
First inode: 11
-Inode size: 128
+Inode size: 256
+Required extra isize: 32
+Desired extra isize: 32
Default directory hash: half_md4
User quota inode: 3
Group quota inode: 4
+Project quota inode: 12


Group 0: (Blocks 1-8192)
Primary superblock at 1, Group descriptors at 2-2
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
+ Inode table at 261-772 (+260)
+ 7388 free blocks, 2036 free inodes, 2 directories
+ Free blocks: 805-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
Block bitmap at 8451 (+258), Inode bitmap at 8452 (+259)
- Inode table at 8453-8708 (+260)
- 7676 free blocks, 2048 free inodes, 0 directories
- Free blocks: 8709-16384
+ Inode table at 8453-8964 (+260)
+ 7420 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 8965-16384
Free inodes: 2049-4096
Group 2: (Blocks 16385-24576)
Block bitmap at 16385 (+0), Inode bitmap at 16386 (+1)
- Inode table at 16387-16642 (+2)
- 7934 free blocks, 2048 free inodes, 0 directories
- Free blocks: 16643-24576
+ Inode table at 16387-16898 (+2)
+ 7678 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 16899-24576
Free inodes: 4097-6144
Group 3: (Blocks 24577-32768)
Backup superblock at 24577, Group descriptors at 24578-24578
Reserved GDT blocks at 24579-24834
Block bitmap at 24835 (+258), Inode bitmap at 24836 (+259)
- Inode table at 24837-25092 (+260)
- 7676 free blocks, 2048 free inodes, 0 directories
- Free blocks: 25093-32768
+ Inode table at 24837-25348 (+260)
+ 7420 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 25349-32768
Free inodes: 6145-8192
Group 4: (Blocks 32769-40960)
Block bitmap at 32769 (+0), Inode bitmap at 32770 (+1)
- Inode table at 32771-33026 (+2)
- 7934 free blocks, 2048 free inodes, 0 directories
- Free blocks: 33027-40960
+ Inode table at 32771-33282 (+2)
+ 7678 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 33283-40960
Free inodes: 8193-10240
Group 5: (Blocks 40961-49152)
Backup superblock at 40961, Group descriptors at 40962-40962
Reserved GDT blocks at 40963-41218
Block bitmap at 41219 (+258), Inode bitmap at 41220 (+259)
- Inode table at 41221-41476 (+260)
- 7676 free blocks, 2048 free inodes, 0 directories
- Free blocks: 41477-49152
+ Inode table at 41221-41732 (+260)
+ 7420 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 41733-49152
Free inodes: 10241-12288
Group 6: (Blocks 49153-57344)
Block bitmap at 49153 (+0), Inode bitmap at 49154 (+1)
- Inode table at 49155-49410 (+2)
- 7934 free blocks, 2048 free inodes, 0 directories
- Free blocks: 49411-57344
+ Inode table at 49155-49666 (+2)
+ 7678 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 49667-57344
Free inodes: 12289-14336
Group 7: (Blocks 57345-65536)
Backup superblock at 57345, Group descriptors at 57346-57346
Reserved GDT blocks at 57347-57602
Block bitmap at 57603 (+258), Inode bitmap at 57604 (+259)
- Inode table at 57605-57860 (+260)
- 7676 free blocks, 2048 free inodes, 0 directories
- Free blocks: 57861-65536
+ Inode table at 57605-58116 (+260)
+ 7420 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 58117-65536
Free inodes: 14337-16384
Group 8: (Blocks 65537-73728)
Block bitmap at 65537 (+0), Inode bitmap at 65538 (+1)
- Inode table at 65539-65794 (+2)
- 7934 free blocks, 2048 free inodes, 0 directories
- Free blocks: 65795-73728
+ Inode table at 65539-66050 (+2)
+ 7678 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 66051-73728
Free inodes: 16385-18432
Group 9: (Blocks 73729-81920)
Backup superblock at 73729, Group descriptors at 73730-73730
Reserved GDT blocks at 73731-73986
Block bitmap at 73987 (+258), Inode bitmap at 73988 (+259)
- Inode table at 73989-74244 (+260)
- 7676 free blocks, 2048 free inodes, 0 directories
- Free blocks: 74245-81920
+ Inode table at 73989-74500 (+260)
+ 7420 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 74501-81920
Free inodes: 18433-20480
Group 10: (Blocks 81921-90112)
Block bitmap at 81921 (+0), Inode bitmap at 81922 (+1)
- Inode table at 81923-82178 (+2)
- 7934 free blocks, 2048 free inodes, 0 directories
- Free blocks: 82179-90112
+ Inode table at 81923-82434 (+2)
+ 7678 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 82435-90112
Free inodes: 20481-22528
Group 11: (Blocks 90113-98304)
Block bitmap at 90113 (+0), Inode bitmap at 90114 (+1)
- Inode table at 90115-90370 (+2)
- 7934 free blocks, 2048 free inodes, 0 directories
- Free blocks: 90371-98304
+ Inode table at 90115-90626 (+2)
+ 7678 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 90627-98304
Free inodes: 22529-24576
Group 12: (Blocks 98305-106496)
Block bitmap at 98305 (+0), Inode bitmap at 98306 (+1)
- Inode table at 98307-98562 (+2)
- 7934 free blocks, 2048 free inodes, 0 directories
- Free blocks: 98563-106496
+ Inode table at 98307-98818 (+2)
+ 7678 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 98819-106496
Free inodes: 24577-26624
Group 13: (Blocks 106497-114688)
Block bitmap at 106497 (+0), Inode bitmap at 106498 (+1)
- Inode table at 106499-106754 (+2)
- 7934 free blocks, 2048 free inodes, 0 directories
- Free blocks: 106755-114688
+ Inode table at 106499-107010 (+2)
+ 7678 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 107011-114688
Free inodes: 26625-28672
Group 14: (Blocks 114689-122880)
Block bitmap at 114689 (+0), Inode bitmap at 114690 (+1)
- Inode table at 114691-114946 (+2)
- 7934 free blocks, 2048 free inodes, 0 directories
- Free blocks: 114947-122880
+ Inode table at 114691-115202 (+2)
+ 7678 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 115203-122880
Free inodes: 28673-30720
Group 15: (Blocks 122881-131071)
Block bitmap at 122881 (+0), Inode bitmap at 122882 (+1)
- Inode table at 122883-123138 (+2)
- 7933 free blocks, 2048 free inodes, 0 directories
- Free blocks: 123139-131071
+ Inode table at 122883-123394 (+2)
+ 7677 free blocks, 2048 free inodes, 0 directories
+ Free blocks: 123395-131071
Free inodes: 30721-32768
diff --git a/tests/m_quota/script b/tests/m_quota/script
index fe63939..32a12b9 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 -I 256"
if [ "$QUOTA" != "y" ]; then
echo "$test_name: $DESCRIPTION: skipped"
return 0
--
1.7.1


2016-03-06 04:15:16

by Li Xi

[permalink] [raw]
Subject: [v4 5/6] Add inherit flags for project quota

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 <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
---
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 fa3b309..d16dd80 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


2016-03-06 04:15:18

by Li Xi

[permalink] [raw]
Subject: [v4 6/6] Add project ID support for chattr/lsattr

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 <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
---
lib/e2p/Makefile.in | 11 ++++++-
lib/e2p/e2p.h | 2 +
lib/e2p/fgetproject.c | 57 ++++++++++++++++++++++++++++++++++++++++++
lib/e2p/fsetproject.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++
misc/chattr.1.in | 7 +++++
misc/chattr.c | 33 +++++++++++++++++++++++-
misc/lsattr.1.in | 5 +++-
misc/lsattr.c | 18 ++++++++++++-
8 files changed, 192 insertions(+), 7 deletions(-)
create mode 100644 lib/e2p/fgetproject.c
create mode 100644 lib/e2p/fsetproject.c

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/lib/e2p/fgetproject.c b/lib/e2p/fgetproject.c
new file mode 100644
index 0000000..a94f13a
--- /dev/null
+++ b/lib/e2p/fgetproject.c
@@ -0,0 +1,57 @@
+/*
+ * fgetproject.c - Get a file projectid on an ext4 file system
+ *
+ * Copyright (C) 2016 DataDirect Networks JP.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+
+#include "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include "project.h"
+
+#include "e2p.h"
+
+int fgetproject(const char *name, unsigned long *project)
+{
+ struct stat buf;
+ int fd;
+ int ret;
+ int save_errno = 0;
+ struct fsxattr fsxattr;
+
+ if (stat(name, &buf) == -1)
+ return -1;
+
+ fd = open(name, OPEN_FLAGS);
+ if (fd == -1)
+ return -1;
+
+ ret = ioctl(fd, EXT4_IOC_FSGETXATTR, &fsxattr);
+ if (ret < 0) {
+ save_errno = errno;
+ ret = -1;
+ }
+
+ close(fd);
+ if (save_errno)
+ errno = save_errno;
+ if (ret >= 0)
+ *project = fsxattr.fsx_projid;
+ return ret;
+}
diff --git a/lib/e2p/fsetproject.c b/lib/e2p/fsetproject.c
new file mode 100644
index 0000000..13b6566
--- /dev/null
+++ b/lib/e2p/fsetproject.c
@@ -0,0 +1,66 @@
+/*
+ * fsetproject.c - Set a file projectid on an ext4 file system
+ *
+ * Copyright (C) 2016 DataDirect Networks JP.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+
+#include "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include "project.h"
+
+#include "e2p.h"
+
+int fsetproject(const char *name, unsigned long project)
+{
+ struct stat buf;
+ int fd;
+ int ret;
+ int save_errno = 0;
+ struct fsxattr fsxattr;
+
+ if (stat(name, &buf) == -1)
+ return -1;
+
+ fd = open(name, OPEN_FLAGS);
+ if (fd == -1)
+ return -1;
+
+ ret = ioctl(fd, EXT4_IOC_FSGETXATTR, &fsxattr);
+ if (ret < 0) {
+ save_errno = errno;
+ ret = -1;
+ goto out;
+ }
+
+ fsxattr.fsx_projid = project;
+
+ ret = ioctl(fd, EXT4_IOC_FSSETXATTR, &fsxattr);
+ if (ret < 0) {
+ save_errno = errno;
+ ret = -1;
+ goto out;
+ }
+
+out:
+ close(fd);
+ if (save_errno)
+ errno = save_errno;
+ return ret;
+}
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 <[email protected]>. 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


2016-03-06 05:46:25

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [v4 1/6] Always read full inode structure

On Sun, Mar 06, 2016 at 01:14:51PM +0900, Li Xi wrote:
> Project quota need use some extra field of inode for computing quota
> accounting, this patch tries to use ext2fs_get_next_inode_full()
> everywhere to read full inode into memeory.
>
> It also fixes a bug that only copy small inode in the function.
>
> Signed-off-by: Wang Shilong <[email protected]>

Um, no. Most of the places where we are currently using
ext2fs_get_next_inode() is because they **don't** need the full inode.
For example, consider debugfs's icheck command. Why would it need the
project quota id? Answer: it doesn't.

If you think there is a bug, or some place where we should be using
the large inode, fine. Let's take a look at it. But doing a whole
sale conversion of ext2fs_get_next_inode() to
ext2fs_get_next_inode_full() makes e2fsprogs much less efficient, and
for no good purpose.

- Ted

2016-03-06 05:56:48

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [v4 6/6] Add project ID support for chattr/lsattr

I'll take a closer look at the changes in your v4 patchset over v3,
but the f[sg]etproject.c files I put together are better in that they
are more portable for non-Linux architectures, so I think I'm going to
keep my version.

I'll push out an updated e2fsprogs git tree on Monday, and at that
point if you have any questions about the integration work I did, or
you have any other bugs, let's handle them as patches against the
updated next branch.

Cheers,

- Ted

2016-03-06 06:27:33

by Andreas Dilger

[permalink] [raw]
Subject: Re: [v4 1/6] Always read full inode structure


> On Mar 5, 2016, at 10:46 PM, Theodore Ts'o <[email protected]> wrote:
>
> On Sun, Mar 06, 2016 at 01:14:51PM +0900, Li Xi wrote:
>> Project quota need use some extra field of inode for computing quota
>> accounting, this patch tries to use ext2fs_get_next_inode_full()
>> everywhere to read full inode into memeory.
>>
>> It also fixes a bug that only copy small inode in the function.
>>
>> Signed-off-by: Wang Shilong <[email protected]>
>
> Um, no. Most of the places where we are currently using
> ext2fs_get_next_inode() is because they **don't** need the full inode.
> For example, consider debugfs's icheck command. Why would it need the
> project quota id? Answer: it doesn't.
>
> If you think there is a bug, or some place where we should be using
> the large inode, fine. Let's take a look at it. But doing a whole
> sale conversion of ext2fs_get_next_inode() to
> ext2fs_get_next_inode_full() makes e2fsprogs much less efficient, and
> for no good purpose.

Do you think it really makes e2fsprogs less efficient? The disk IO has
already happened, and definitely included the whole inode even if only
the small inode data was requested. The ext2fs block cache will still
cache the whole inode block, so fetching the whole inode is no overhead.

In contrast, several places in the code are doing extra work to fetch
the large inode data after having fetched the small inode data. It is
also fairly confusing in different parts of the code which "know" that
the inode pointer is pointing to a full inode buffer, so it is a lot
cleaner if we just always read the full inode data everywhere.

Even better would be if the API explicitly just passed ext4_inode_large
everywhere, which wouldn't break the ABI, but it might cause problems
for anything that encodes the argument types (e.g. C++). At least if
the e2fsprogs internal functions are reading the full inode the code is
easier to understand.

Cheers, Andreas






Attachments:
signature.asc (833.00 B)
Message signed with OpenPGP using GPGMail

2016-03-06 10:49:11

by Li Xi

[permalink] [raw]
Subject: Re: [v4 6/6] Add project ID support for chattr/lsattr

Sure. No problem. I will run tests with your patches as soon as they are out.

On Sun, Mar 6, 2016 at 1:56 PM, Theodore Ts'o <[email protected]> wrote:
> I'll take a closer look at the changes in your v4 patchset over v3,
> but the f[sg]etproject.c files I put together are better in that they
> are more portable for non-Linux architectures, so I think I'm going to
> keep my version.
>
> I'll push out an updated e2fsprogs git tree on Monday, and at that
> point if you have any questions about the integration work I did, or
> you have any other bugs, let's handle them as patches against the
> updated next branch.
>
> Cheers,
>
> - Ted

2016-03-06 19:31:38

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [v4 1/6] Always read full inode structure

On Sat, Mar 05, 2016 at 11:27:25PM -0700, Andreas Dilger wrote:
> Do you think it really makes e2fsprogs less efficient? The disk IO has
> already happened, and definitely included the whole inode even if only
> the small inode data was requested. The ext2fs block cache will still
> cache the whole inode block, so fetching the whole inode is no overhead.

I'm concerned about all of the extra memory allocation and
deallocation that we would need to do. If you have a million inodes,
that's a million malloc()'s and free()'s.


> In contrast, several places in the code are doing extra work to fetch
> the large inode data after having fetched the small inode data. It is
> also fairly confusing in different parts of the code which "know" that
> the inode pointer is pointing to a full inode buffer, so it is a lot
> cleaner if we just always read the full inode data everywhere.

Can you point at some of these places? See below, but I think it's a
lot more complicated to do what you are suggested.

> Even better would be if the API explicitly just passed ext4_inode_large
> everywhere, which wouldn't break the ABI, but it might cause problems
> for anything that encodes the argument types (e.g. C++). At least if
> the e2fsprogs internal functions are reading the full inode the code is
> easier to understand.

For the inode structure, for better or for worse, we have a "caller
allocates" convention. So we can't just fill in the full inode unless
the caller explicitly requests it, and tells us how much space it has
available.

Also, if the caller passes in a pointer to struct ext2_inode, the
library can't assume it's a full inode. Fortunately, in the vast
majority of the places where the library needs to look at the inode,
it doesn't need to look at the full inode.

Cheers,

- Ted