e2fsprogs: project quota
cover e2fsprogs: project quota
Projects quota allows to enforce disk quota for several subtrees or even individual files on the filesystem. Each inode is marked with project-id (independently from uid and gid) and accounted into corresponding project quota. New files inherits project id from directory where they are created.
This patchset adds required support into mkfs, fsck, tune2fs, debugfs, chattr and lsattr.
All patches are available at github:
https://github.com/koct9i/linux --branch project
https://github.com/koct9i/e2fsprogs --branch project
https://github.com/koct9i/quota-tools --branch project
Ext4 layout
-----------
Project id introduce ro-compatible feature 'project'.
Inode project id is stored in place of obsolete field 'i_faddr' (that trick was suggested by Darrick J. Wong in previous discussions of project quota). Linux never used that field and fsck checks that it contains zero.
Quota information is stored in reserved/special inode №11. For symmetry with other quotas inode number is stored in superblock.
By default only 10 inodes are reserved for special usage.
After this patch mkfs will reserve 20 inodes if feature project is enabled. I've add option to resize2fs to reserve more inodes in existing filestsrem. (See patchset [PATCH RFC v1 0/4] e2fsprogs: reserve more special inodes)
For now project quota supports only modern 'hidden' journaled mode.
---
Konstantin Khlebnikov (4):
e2fsprogs: introduce project id feature
e2fsprogs: add project quota support
e2fsprogs: add tests for project quota
e2fsprogs: add project id into lsattr and chattr
debugfs/debugfs.c | 40 ++++++++--- debugfs/quota.c | 8 +- debugfs/set_fields.c | 4 + e2fsck/pass1.c | 14 +++- e2fsck/pass2.c | 3 + e2fsck/quota.c | 11 +++ e2fsck/unix.c | 25 ++++--- lib/e2p/Makefile.in | 5 + lib/e2p/e2p.h | 2 + lib/e2p/feature.c | 2 + lib/e2p/ls.c | 3 + lib/e2p/project.c | 55 +++++++++++++++ lib/ext2fs/ext2_fs.h | 19 ++++- lib/ext2fs/ext2fs.h | 3 + lib/ext2fs/swapfs.c | 3 + lib/ext2fs/tst_inode_size.c | 2 - lib/ext2fs/tst_super_size.c | 3 + lib/quota/mkquota.c | 42 ++++++++++-- lib/quota/quotaio.c | 23 ++++-- lib/quota/quotaio.h | 7 +- misc/chattr.1.in | 7 ++ misc/chattr.c | 34 +++++++++ misc/ext4.5.in | 7 ++ misc/lsattr.1.in | 5 + misc/lsattr.c | 19 +++++ misc/mke2fs.8.in | 5 + misc/mke2fs.c | 26 ++++++- misc/tune2fs.8.in | 9 ++ misc/tune2fs.c | 77 +++++++++++++++++++-- tests/m_project/expect.1 | 156 +++++++++++++++++++++++++++++++++++++++++++ tests/m_project/script | 7 ++ tests/t_project_1on/expect | 66 ++++++++++++++++++ tests/t_project_1on/name | 1 tests/t_project_1on/script | 68 +++++++++++++++++++ tests/t_project_2off/expect | 78 ++++++++++++++++++++++ tests/t_project_2off/name | 1 tests/t_project_2off/script | 54 +++++++++++++++ 37 files changed, 821 insertions(+), 73 deletions(-) create mode 100644 lib/e2p/project.c create mode 100644 tests/m_project/expect.1 create mode 100644 tests/m_project/script create mode 100644 tests/t_project_1on/expect create mode 100644 tests/t_project_1on/name create mode 100644 tests/t_project_1on/script create mode 100644 tests/t_project_2off/expect create mode 100644 tests/t_project_2off/name create mode 100644 tests/t_project_2off/script
--
Signature
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC PATCH 1/4 e2fsprogs: introduce project id feature
* add feature EXT4_FEATURE_RO_COMPAT_PROJECT
* reuse obsolete inode field i_faddr as i_project
* add field s_prj_quota_inum into superblock for project quota inode
--- debugfs/debugfs.c | 40 ++++++++++++++++++++++++++++------------ debugfs/set_fields.c | 4 +++- e2fsck/pass1.c | 5 ++++- e2fsck/pass2.c | 3 ++- lib/e2p/feature.c | 2 ++ lib/e2p/ls.c | 3 +++ lib/ext2fs/ext2_fs.h | 9 ++++++--- lib/ext2fs/ext2fs.h | 3 ++- lib/ext2fs/swapfs.c | 3 ++- lib/ext2fs/tst_inode_size.c | 2 +- lib/ext2fs/tst_super_size.c | 3 ++- misc/mke2fs.c | 3 ++- misc/tune2fs.c | 28 ++++++++++++++++++++++++++-- 13 files changed, 83 insertions(+), 25 deletions(-)
debugfs/debugfs.c +28 -12
@@ -713,7 +713,7 @@ void internal_dump_inode(FILE *out, const char *prefix, int do_dump_blocks) {const char *i_type; -char frag, fsize; +struct ext2_super_block *sb = current_fs->super;int os = current_fs->super->s_creator_os;struct ext2_inode_large *large_inode;int is_large_inode =0 ;
@@ -741,8 +741,11 @@ void internal_dump_inode(FILE *out, const char *prefix,fprintf (out,"%sGeneration: %u Version: 0x%08x\n" , prefix, inode->i_generation, inode->osd1.linux1.l_i_version); } -fprintf (out,"%sUser: %5d Group: %5d Size: " , +fprintf (out,"%sUser: %5d Group: %5d " , prefix,inode_uid (*inode),inode_gid (*inode)); +if (EXT2_HAS_RO_COMPAT_FEATURE (sb,EXT4_FEATURE_RO_COMPAT_PROJECT )) +fprintf (out,"Project: %5d " , large_inode->i_project); +fprintf (out,"Size: " );if (LINUX_S_ISREG (inode->i_mode))fprintf (out,"%llu\n" ,EXT2_I_SIZE (inode));else
@@ -768,16 +771,22 @@ void internal_dump_inode(FILE *out, const char *prefix,else fprintf (out,"%sLinks: %d Blockcount: %u\n" , prefix, inode->i_links_count, inode->i_blocks); -switch (os) { -case EXT2_OS_HURD : - frag = inode->osd2.hurd2.h_i_frag; - fsize = inode->osd2.hurd2.h_i_fsize; -break ; -default : - frag = fsize =0 ; + +if (!EXT2_HAS_RO_COMPAT_FEATURE (sb,EXT4_FEATURE_RO_COMPAT_PROJECT )) { +char frag, fsize; + +switch (os) { +case EXT2_OS_HURD : + frag = inode->osd2.hurd2.h_i_frag; + fsize = inode->osd2.hurd2.h_i_fsize; +break ; +default : + frag = fsize =0 ; + } +fprintf (out,"%sFragment: Address: %d Number: %d Size: %d\n" , + prefix, inode->i_faddr, frag, fsize); } -fprintf (out,"%sFragment: Address: %d Number: %d Size: %d\n" , - prefix, inode->i_faddr, frag, fsize); +if (is_large_inode && large_inode->i_extra_isize >=24 ) { fprintf(out, "%s ctime: 0x%08x:%08x -- %s", prefix, inode->i_ctime, large_inode->i_ctime_extra,
@@ -1256,7 +1265,14 @@ void do_modify_inode(int argc, char *argv[])modify_u32 (argv[0 ],"Translator Block" , decimal_format, &inode.osd1.hurd1.h_i_translator); -modify_u32 (argv[0 ],"Fragment address" , decimal_format, &inode.i_faddr); +if (EXT2_HAS_RO_COMPAT_FEATURE (current_fs->super, +EXT4_FEATURE_RO_COMPAT_PROJECT )) +modify_u32 (argv[0 ],"Project ID" , decimal_format, + &inode.i_project); +else +modify_u32 (argv[0 ],"Fragment address" , decimal_format, + &inode.i_faddr); +switch (os) {case EXT2_OS_HURD : frag = &inode.osd2.hurd2.h_i_frag;
debugfs/set_fields.c +3 -1
@@ -167,6 +167,7 @@ static struct field_set_info super_fields[] = { { "last_error_line", &set_sb.s_last_error_line, NULL, 4, parse_uint }, { "encrypt_algos", &set_sb.s_encrypt_algos, NULL, 1, parse_uint, FLAG_ARRAY, 4 }, + { "prj_quota_inum", &set_sb.s_prj_quota_inum, NULL, 4, parse_uint }, { 0, 0, 0, 0 } };
@@ -200,7 +201,8 @@ static struct field_set_info inode_fields[] = { {"file_acl" , &set_inode.i_file_acl, &set_inode.osd2.linux2.l_i_file_acl_high, 6, parse_uint }, { "dir_acl", &set_inode.i_dir_acl, NULL, 4, parse_uint, FLAG_ALIAS }, - { "faddr", &set_inode.i_faddr, NULL, 4, parse_uint }, + { "project", &set_inode.i_project, NULL, 4, parse_uint }, + { "faddr", &set_inode.i_faddr, NULL, 4, parse_uint, FLAG_ALIAS }, { "frag", &set_inode.osd2.hurd2.h_i_frag, NULL, 1, parse_uint, FLAG_ALIAS }, { "fsize", &set_inode.osd2.hurd2.h_i_fsize, NULL, 1, parse_uint }, { "checksum", &set_inode.osd2.linux2.l_i_checksum_lo,
e2fsck/pass1.c +4 -1
@@ -1567,7 +1567,10 @@ void e2fsck_pass1(e2fsck_t ctx) frag = fsize =0 ; } -if (inode->i_faddr || frag || fsize || +if (inode->i_faddr && !EXT2_HAS_RO_COMPAT_FEATURE (sb, +EXT4_FEATURE_RO_COMPAT_PROJECT )) +mark_inode_bad (ctx, ino); +if (frag || fsize || (LINUX_S_ISDIR (inode->i_mode) && inode->i_dir_acl))mark_inode_bad (ctx, ino); if ((fs->super->s_creator_os == EXT2_OS_LINUX) &&
e2fsck/pass2.c +2 -1
@@ -1746,7 +1746,8 @@ int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, problem =0 ; } -if (inode.i_faddr) { +if (inode.i_faddr && !EXT2_HAS_RO_COMPAT_FEATURE (fs->super, +EXT4_FEATURE_RO_COMPAT_PROJECT )) {if (fix_problem (ctx,PR_2_FADDR_ZERO , &pctx)) { inode.i_faddr =0 ; inode_modified++;
lib/e2p/feature.c +2 -0
@@ -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" },
lib/e2p/ls.c +3 -0
@@ -440,6 +440,9 @@ void list_super2(struct ext2_super_block * sb, FILE *f)if (sb->s_grp_quota_inum)fprintf (f,"Group quota inode: %u\n" , sb->s_grp_quota_inum); +if (sb->s_prj_quota_inum) +fprintf (f,"Project quota inode: %u\n" , + sb->s_prj_quota_inum);if (sb->s_feature_ro_compat &EXT4_FEATURE_RO_COMPAT_METADATA_CSUM ) { fprintf(f, "Checksum type: %s\n",
lib/ext2fs/ext2_fs.h +6 -3
@@ -398,7 +398,7 @@ struct ext2_inode {__u32 i_generation;/* File version (for NFS) */ __u32 i_file_acl;/* File ACL */ __u32 i_size_high;/* Formerly i_dir_acl, directory ACL */ -__u32 i_faddr;/* Fragment address */ +__u32 i_project;/* Formerly i_faddr, fragment address */ union { struct { __u16 l_i_blocks_hi;
@@ -446,7 +446,7 @@ struct ext2_inode_large {__u32 i_generation;/* File version (for NFS) */ __u32 i_file_acl;/* File ACL */ __u32 i_size_high;/* Formerly i_dir_acl, directory ACL */ -__u32 i_faddr;/* Fragment address */ +__u32 i_project;/* Formerly i_faddr, fragment address */ union { struct { __u16 l_i_blocks_hi;
@@ -480,6 +480,7 @@ struct ext2_inode_large { EXT2_GOOD_OLD_INODE_SIZE)#define i_dir_acl i_size_high +#define i_faddr i_project#define i_checksum_lo osd2.linux2.l_i_checksum_lo
@@ -683,7 +684,8 @@ struct ext2_super_block {__u32 s_overhead_blocks;/* overhead blocks/clusters in fs */ __u32 s_backup_bgs[2 ];/* If sparse_super2 enabled */ __u8 s_encrypt_algos[4 ];/* Encryption algorithms in use */ -__u32 s_reserved[105 ];/* Padding to the end of the block */ +__u32 s_prj_quota_inum;/* inode number of project quota file */ +__u32 s_reserved[104 ];/* Padding to the end of the block */ __u32 s_checksum;/* crc32c(superblock) */ };
@@ -755,6 +757,7 @@ struct ext2_super_block {#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400#define EXT4_FEATURE_RO_COMPAT_REPLICA 0x0800#define EXT4_FEATURE_RO_COMPAT_READONLY 0x1000 +#define EXT4_FEATURE_RO_COMPAT_PROJECT 0x2000/* Project ID / Quota */ #define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
lib/ext2fs/ext2fs.h +2 -1
@@ -610,7 +610,8 @@ typedef struct ext2_icount *ext2_icount_t;EXT4_FEATURE_RO_COMPAT_BIGALLOC |\EXT4_LIB_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
lib/ext2fs/swapfs.c +2 -1
@@ -82,6 +82,7 @@ void ext2fs_swap_super(struct ext2_super_block * sb) sb->s_usr_quota_inum =ext2fs_swab32 (sb->s_usr_quota_inum); sb->s_grp_quota_inum =ext2fs_swab32 (sb->s_grp_quota_inum); sb->s_overhead_blocks =ext2fs_swab32 (sb->s_overhead_blocks); + sb->s_prj_quota_inum =ext2fs_swab32 (sb->s_prj_quota_inum); sb->s_checksum =ext2fs_swab32 (sb->s_checksum);for (i=0 ; i <4 ; i++)
@@ -257,7 +258,7 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, t->i_block[i] = f->i_block[i]; } t->i_generation =ext2fs_swab32 (f->i_generation); - t->i_faddr =ext2fs_swab32 (f->i_faddr); + t->i_project =ext2fs_swab32 (f->i_project);switch (fs->super->s_creator_os) {case EXT2_OS_LINUX :
lib/ext2fs/tst_inode_size.c +1 -1
@@ -65,7 +65,7 @@ int main(int argc, char **argv)check_field (i_generation,4 );check_field (i_file_acl,4 );check_field (i_size_high,4 ); -check_field (i_faddr,4 ); +check_field (i_project,4 );check_field (osd2.linux2.l_i_blocks_hi,2 );check_field (osd2.linux2.l_i_file_acl_high,2 );check_field (osd2.linux2.l_i_uid_high,2 );
lib/ext2fs/tst_super_size.c +2 -1
@@ -137,7 +137,8 @@ int main(int argc, char **argv)check_field (s_overhead_blocks,4 );check_field (s_backup_bgs,8 );check_field (s_encrypt_algos,4 ); -check_field (s_reserved,105 *4 ); +check_field (s_prj_quota_inum,4 ); +check_field (s_reserved,104 *4 );check_field (s_checksum,4 );do_field ("Superblock end" ,0 ,0 , cur_offset,1024 );#endif
misc/mke2fs.c +2 -1
@@ -1107,7 +1107,8 @@ static __u32 ok_features[3] = {#ifdef CONFIG_QUOTA EXT4_FEATURE_RO_COMPAT_QUOTA | #endif -EXT4_FEATURE_RO_COMPAT_METADATA_CSUM +EXT4_FEATURE_RO_COMPAT_METADATA_CSUM | +EXT4_FEATURE_RO_COMPAT_PROJECT };
misc/tune2fs.c +26 -2
@@ -161,7 +161,8 @@ static __u32 ok_features[3] = { EXT4_FEATURE_RO_COMPAT_QUOTA | #endif 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] = {
@@ -184,7 +185,8 @@ static __u32 clear_ok_features[3] = {EXT4_FEATURE_RO_COMPAT_QUOTA | #endifEXT4_FEATURE_RO_COMPAT_METADATA_CSUM | -EXT4_FEATURE_RO_COMPAT_READONLY +EXT4_FEATURE_RO_COMPAT_READONLY | +EXT4_FEATURE_RO_COMPAT_PROJECT };/**
@@ -1303,6 +1305,28 @@ mmp_error: grpquota =QOPT_DISABLE ; } +if (FEATURE_CHANGED (E2P_FEATURE_RO_INCOMPAT , +EXT4_FEATURE_RO_COMPAT_PROJECT ) && + (mount_flags &EXT2_MF_MOUNTED ) && + !(mount_flags &EXT2_MF_READONLY )) { +fputs (_ ("The project feature may only be " +"changed when the filesystem is\n" +"unmounted or mounted read-only.\n" ), stderr); +return 1 ; + } + +if (FEATURE_ON (E2P_FEATURE_RO_INCOMPAT , +EXT4_FEATURE_RO_COMPAT_PROJECT )) { + sb->s_feature_ro_compat |=EXT4_FEATURE_RO_COMPAT_PROJECT ; + } + +if (FEATURE_OFF (E2P_FEATURE_RO_INCOMPAT , +EXT4_FEATURE_RO_COMPAT_PROJECT )) { + sb->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_PROJECT ; +/* fsck will reset i_project (i_faddr) for us. */ +request_fsck_afterwards (fs); + } +if (sb->s_rev_level ==EXT2_GOOD_OLD_REV && (sb->s_feature_compat || sb->s_feature_ro_compat || sb->s_feature_incompat))
RFC PATCH 2/4 e2fsprogs: add project quota support
In reply to Konstantin KhlebnikovThis patch adds third quota type: project quota.
For symmetry with other quotas inode number is stored in superblock but for now project quota supports only modern 'hidden' journal mode in inode 11. (non-hidden mode might be useful because if can work without reserved inode)
When feature 'project' is enabled mke2fs automatically enables project quota and reserves first 20 inodes for special usage (instead of default 10). Changing that reservation unconditionally breaks too much tests where inode numbers are present in expected output or even hardcoded: for example in "tst_inline_data".
--- debugfs/quota.c | 8 +++++--- e2fsck/pass1.c | 9 ++++++--- e2fsck/quota.c | 11 +++++++++++ e2fsck/unix.c | 25 ++++++++++++++++--------- lib/ext2fs/ext2_fs.h | 5 +++++ lib/quota/mkquota.c | 42 ++++++++++++++++++++++++++++++++++++------ lib/quota/quotaio.c | 23 ++++++++++++++--------- lib/quota/quotaio.h | 7 +++++-- misc/ext4.5.in | 7 +++++++ misc/mke2fs.8.in | 5 ++++- misc/mke2fs.c | 23 +++++++++++++++++++++-- misc/tune2fs.8.in | 9 +++++++++ misc/tune2fs.c | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 13 files changed, 183 insertions(+), 40 deletions(-)
debugfs/quota.c +5 -3
@@ -25,7 +25,7 @@ extern char *optarg;#include "debugfs.h" -const char *quota_type[] = {"user" ,"group" ,NULL }; +const char *quota_type[] = {"user" ,"group" ,"project" ,NULL };static int load_quota_ctx (char *progname) {
@@ -121,7 +121,8 @@ void do_list_quota(int argc, char *argv[])return ;printf ("%8s %8s %8s %8s %8s %8s %8s\n" , - (type ==0 ) ?"user id" :"group id" , + (type ==USRQUOTA ) ?"user id" : + (type ==GRPQUOTA ) ?"group id" :"project" ,"blocks" ,"quota" ,"limit" ,"inodes" ,"quota" ,"limit" ); qh = current_qctx->quota_file[type]; retval = qh->qh_ops->scan_dquots (qh, list_quota_callback,NULL );
@@ -155,7 +156,8 @@ void do_get_quota(int argc, char *argv[])return ;printf ("%8s %8s %8s %8s %8s %8s %8s\n" , - (type ==0 ) ?"user id" :"group id" , + (type ==USRQUOTA ) ?"user id" : + (type ==GRPQUOTA ) ?"group id" :"project" ,"blocks" ,"quota" ,"limit" ,"inodes" ,"quota" ,"limit" ); qh = current_qctx->quota_file[type];
e2fsck/pass1.c +6 -3
@@ -1437,12 +1437,15 @@ void e2fsck_pass1(e2fsck_t ctx) failed_csum =0 ; } }else if ((ino ==EXT4_USR_QUOTA_INO ) || - (ino ==EXT4_GRP_QUOTA_INO )) { + (ino ==EXT4_GRP_QUOTA_INO ) || + (ino <EXT2_FIRST_INODE (fs->super) && + ino ==EXT4_PRJ_QUOTA_INO )) {ext2fs_mark_inode_bitmap2 (ctx->inode_used_map, ino);if ((fs->super->s_feature_ro_compat &EXT4_FEATURE_RO_COMPAT_QUOTA ) && - ((fs->super->s_usr_quota_inum == ino) || - (fs->super->s_grp_quota_inum == ino))) { + ((sb->s_usr_quota_inum == ino) || + (sb->s_grp_quota_inum == ino) || + (sb->s_prj_quota_inum == ino))) {if (!LINUX_S_ISREG (inode->i_mode) &&fix_problem (ctx,PR_1_QUOTA_BAD_MODE , &pctx)) {
e2fsck/quota.c +11 -0
@@ -88,5 +88,16 @@ void e2fsck_hide_quota(e2fsck_t ctx) sb->s_grp_quota_inum =EXT4_GRP_QUOTA_INO ; } + pctx.ino = sb->s_prj_quota_inum; +if ((sb->s_feature_ro_compat &EXT4_FEATURE_RO_COMPAT_PROJECT ) && + sb->s_prj_quota_inum && + (sb->s_prj_quota_inum !=EXT4_PRJ_QUOTA_INO ) && + (sb->s_first_ino >EXT4_PRJ_QUOTA_INO ) && +fix_problem (ctx,PR_0_HIDE_QUOTA , &pctx)) { +move_quota_inode (fs, sb->s_prj_quota_inum,EXT4_PRJ_QUOTA_INO , +PRJQUOTA ); + sb->s_prj_quota_inum =EXT4_PRJ_QUOTA_INO ; + } +return ; }
e2fsck/unix.c +16 -9
@@ -1636,14 +1636,20 @@ print_unsupp_features: journal_size =-1 ;if (sb->s_feature_ro_compat &EXT4_FEATURE_RO_COMPAT_QUOTA ) { -/* Quotas were enabled. Do quota accounting during fsck. */ -if ((sb->s_usr_quota_inum && sb->s_grp_quota_inum) || - (!sb->s_usr_quota_inum && !sb->s_grp_quota_inum)) - qtype =-1 ; -else - qtype = sb->s_usr_quota_inum ?USRQUOTA :GRPQUOTA ; - -quota_init_context (&ctx->qctx, ctx->fs, qtype); +/* +* Quotas were enabled. Do quota accounting during fsck. +* If no quotas in-use then initialize all supported types. +*/ + qtype =-2 ; +if (sb->s_usr_quota_inum) + qtype =USRQUOTA ; +if (sb->s_grp_quota_inum) + qtype = (qtype ==-2 ) ?GRPQUOTA :-1 ; +if (sb->s_prj_quota_inum &&EXT2_HAS_RO_COMPAT_FEATURE (sb, +EXT4_FEATURE_RO_COMPAT_PROJECT )) + qtype = (qtype ==-2 ) ?PRJQUOTA :-1 ; + +quota_init_context (&ctx->qctx, ctx->fs, qtype <0 ?-1 : qtype); } run_result =e2fsck_run (ctx);
@@ -1682,7 +1688,8 @@ no_journal:if (ctx->qctx) {int i, needs_writeout;for (i =0 ; i <MAXQUOTAS ; i++) { -if (qtype !=-1 && qtype != i) +if ((qtype ==-1 && !quota_get_sb_inum (fs, i)) || + (qtype >=0 && qtype != i))continue ; needs_writeout =0 ; pctx.num = i;
lib/ext2fs/ext2_fs.h +5 -0
@@ -56,6 +56,11 @@/* First non-reserved inode for old ext2 filesystems */ #define EXT2_GOOD_OLD_FIRST_INO 11 +#define EXT4_PRJ_QUOTA_INO 11/* Project quota inode */ + +/* Default first non-reserved inode for new filesystems */ +#define EXT4_DEFAULT_FIRST_INO 21 +/* * The second extended file system magic number */
lib/quota/mkquota.c +36 -6
74 lines not on this page · raw message
lib/quota/quotaio.c +14 -9
@@ -19,7 +19,7 @@#include "common.h" #include "quotaio.h" -static const char *const extensions[MAXQUOTAS ] = {"user" ,"group" }; +static const char *const extensions[MAXQUOTAS ] = {"user" ,"group" ,"project" };static const char *const basenames[] = {"" ,/* undefined */ "quota" ,/* QFMT_VFS_OLD */
@@ -118,7 +118,8 @@ errcode_t quota_inode_truncate(ext2_filsys fs, ext2_ino_t ino)if ((err =ext2fs_read_inode (fs, ino, &inode)))return err; -if ((ino ==EXT4_USR_QUOTA_INO ) || (ino ==EXT4_GRP_QUOTA_INO )) { +if ((ino ==EXT4_USR_QUOTA_INO ) || (ino ==EXT4_GRP_QUOTA_INO ) || + (ino ==EXT4_PRJ_QUOTA_INO )) { inode.i_dtime = fs->now ? fs->now :time (0 );if (!ext2fs_inode_has_valid_blocks2 (fs, &inode))return 0 ;
@@ -215,12 +216,8 @@ 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_get_sb_inum (fs, type);log_debug ("Opening quota ino=%lu, type=%d" , qf_ino, type); err =ext2fs_file_open (fs, qf_ino, flags, &e2_file);
@@ -318,19 +315,27 @@ errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs, int type, in {ext2_file_t e2_file;int err; -unsigned long qf_inum; +ext2_ino_t qf_inum;if (fmt ==-1 ) 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 if (type ==PRJQUOTA ) + qf_inum =EXT4_PRJ_QUOTA_INO ;else return -1 ; +if (qf_inum >=EXT2_FIRST_INO (fs->super)) { +log_err ("quota inode is not special" ); +return EXT2_ET_BAD_INODE_NUM ; + } + err =ext2fs_read_bitmaps (fs);if (err) goto out_err;
lib/quota/quotaio.h +5 -2
@@ -44,9 +44,10 @@typedef int64_t qsize_t ;/* Type in which we store size limitations */ -#define MAXQUOTAS 2 +#define MAXQUOTAS 3#define USRQUOTA 0#define GRPQUOTA 1 +#define PRJQUOTA 2typedef struct quota_ctx *quota_ctx_t ;
@@ -61,7 +62,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 */
@@ -216,6 +218,7 @@ void quota_release_context(quota_ctx_t *qctx);errcode_t quota_remove_inode (ext2_filsys fs,int qtype);int quota_file_exists (ext2_filsys fs,int qtype,int fmt); +ext2_ino_t quota_get_sb_inum (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);
misc/ext4.5.in +7 -0
@@ -253,6 +253,13 @@ the file system using and it also speeds up the time required for .BR mke2fs (8) to create the file system. +.TP +.B project +.br +This filesystem feature adds third identificator to inode - "project ID". +Projects provides mechanism for aggregating subtrees or individual files +scattered across filesystem and enforcing disk quotas for them regardless +of users and groups who own them. .RE .SH MOUNT OPTIONS This section describes mount options which are specific to ext2, ext3,
misc/mke2fs.8.in +4 -1
@@ -379,11 +379,14 @@ command-line option to .BR resize2fs (8). @[email protected] @[email protected] quotatype -@QUOTA_MAN_COMMENT@Specify which quota type ('usr' or 'grp') is to be +@QUOTA_MAN_COMMENT@Specify which quota type ('usr', 'grp' or 'prj') is to be @QUOTA_MAN_COMMENT@initialized. This option has effect only if the @[email protected] quota @QUOTA_MAN_COMMENT@feature is set. Without this extended option, the default @QUOTA_MAN_COMMENT@behavior is to initialize both user and group quotas. +@QUOTA_MAN_COMMENT@If feature +@[email protected] project +@QUOTA_MAN_COMMENT@is set then project quota is also initialized by default. .RE .TP .BI \-f " fragment-size"
misc/mke2fs.c +21 -2
@@ -1029,9 +1029,11 @@ static void parse_extended_opts(struct ext2_super_block *param,continue ; }if (!strncmp (arg,"usr" ,3 )) { - quotatype =0 ; + quotatype =USRQUOTA ; }else if (!strncmp (arg,"grp" ,3 )) { - quotatype =1 ; + quotatype =GRPQUOTA ; + }else if (!strncmp (arg,"prj" ,3 )) { + quotatype =PRJQUOTA ; }else { fprintf(stderr, _("Invalid quotatype parameter: %s\n"),
@@ -2449,6 +2451,23 @@ profile_error: fs_param.s_backup_bgs[1 ] = ~0 ; } +#ifdef CONFIG_QUOTA +if (EXT2_HAS_RO_COMPAT_FEATURE (&fs_param, +EXT4_FEATURE_RO_COMPAT_PROJECT )) { +/* Feature "project" automacally enables project quota */ +if (!EXT2_HAS_RO_COMPAT_FEATURE (&fs_param, +EXT4_FEATURE_RO_COMPAT_QUOTA )) { + fs_param.s_feature_ro_compat |= +EXT4_FEATURE_RO_COMPAT_QUOTA ; +if (quotatype ==-1 ) + quotatype =PRJQUOTA ; + } +/* Reserve special inode for project quota */ +if (fs_param.s_first_ino <=EXT4_PRJ_QUOTA_INO ) + fs_param.s_first_ino =EXT4_DEFAULT_FIRST_INO ; + } +#endif +free (fs_type);free (usage_types); }
misc/tune2fs.8.in +9 -0
@@ -580,6 +580,12 @@ keep a high watermark for the unused inodes in a filesystem, to reduce time. This first e2fsck run after enabling this feature will take the full time, but subsequent e2fsck runs will take only a fraction of the original time, depending on how full the file system is. +.TP +.B project +Add project ID to inode. +@QUOTA_MAN_COMMENT@And project quota if feature +@[email protected] quota +@QUOTA_MAN_COMMENT@is enabled. .RE .IP After setting or clearing
@@ -622,6 +628,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"
misc/tune2fs.c +44 -5
112 lines not on this page · raw message
RFC PATCH 3/4 e2fsprogs: add tests for project quota
In reply to Konstantin Khlebnikovt_project_1on: enable project quota by resize2fs/tune2fs t_project_2off disable project quota by tune2fs
--- tests/m_project/expect.1 | 156 +++++++++++++++++++++++++++++++++++++++++++ tests/m_project/script | 7 ++ tests/t_project_1on/expect | 66 ++++++++++++++++++ tests/t_project_1on/name | 1 tests/t_project_1on/script | 68 +++++++++++++++++++ tests/t_project_2off/expect | 78 ++++++++++++++++++++++ tests/t_project_2off/name | 1 tests/t_project_2off/script | 54 +++++++++++++++ 8 files changed, 431 insertions(+) create mode 100644 tests/m_project/expect.1 create mode 100644 tests/m_project/script create mode 100644 tests/t_project_1on/expect create mode 100644 tests/t_project_1on/name create mode 100644 tests/t_project_1on/script create mode 100644 tests/t_project_2off/expect create mode 100644 tests/t_project_2off/name create mode 100644 tests/t_project_2off/script
tests/m_project/expect.1 new file +156 -0
@@ -0,0 +1,156 @@ +Creating filesystem with 131072 1k blocks and 32768 inodes +Superblock backups stored on blocks: + 8193, 24577, 40961, 57345, 73729 + +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 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: 21/32768 files (4.8% non-contiguous), 5697/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 project +Default mount options: (none) +Filesystem state: clean +Errors behavior: Continue +Filesystem OS type: Linux +Inode count: 32768 +Block count: 131072 +Reserved block count: 6553 +Free blocks: 125375 +Free inodes: 32747 +First block: 1 +Block size: 1024 +Fragment size: 1024 +Reserved GDT blocks: 256 +Blocks per group: 8192 +Fragments per group: 8192 +Inodes per group: 2048 +Inode blocks per group: 256 +Mount count: 0 +Check interval: 15552000 (6 months) +Reserved blocks uid: 0 +Reserved blocks gid: 0 +First inode: 21 +Inode size: 128 +Default directory hash: half_md4 +Project quota inode: 11 + + +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) + 7656 free blocks, 2027 free inodes, 2 directories + Free blocks: 537-8192 + Free inodes: 22-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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + Free inodes: 30721-32768
tests/m_project/script new file +7 -0
@@ -0,0 +1,7 @@ +DESCRIPTION="enable project feature on mkfs" +FS_SIZE=131072 +MKE2FS_OPTS="-O project" +DUMPE2FS_IGNORE_80COL=1 +export DUMPE2FS_IGNORE_80COL +. $cmd_dir/run_mke2fs +unset DUMPE2FS_IGNORE_80COL
tests/t_project_1on/expect new file +66 -0
66 lines not on this page · raw message
tests/t_project_1on/name new file +1 -0
@@ -0,0 +1 @@ +enable project quota using tune2fs
tests/t_project_1on/script new file +68 -0
68 lines not on this page · raw message
tests/t_project_2off/expect new file +78 -0
78 lines not on this page · raw message
tests/t_project_2off/name new file +1 -0
@@ -0,0 +1 @@ +disable project quota using tune2fs
tests/t_project_2off/script new file +54 -0
54 lines not on this page · raw message
RFC PATCH 4/4 e2fsprogs: add project id into lsattr and chattr
In reply to Konstantin KhlebnikovKernel will provide generic vfs ioctls for project id: FS_IOC_GET/SETPROJECT. This patch adds option '-p' to lsattr and chattr for getting/setting project id.
--- lib/e2p/Makefile.in | 5 +++-- lib/e2p/e2p.h | 2 ++ lib/e2p/project.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/ext2fs/ext2_fs.h | 5 +++++ misc/chattr.1.in | 7 ++++++ misc/chattr.c | 34 ++++++++++++++++++++++++++++--- misc/lsattr.1.in | 5 ++++- misc/lsattr.c | 19 +++++++++++++++-- 8 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 lib/e2p/project.c
lib/e2p/Makefile.in +3 -2
@@ -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 project.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_mode.c \ + $(srcdir)/project.c HFILES= e2p.h LIBRARY= libe2p
lib/e2p/e2p.h +2 -0
@@ -44,6 +44,8 @@ void print_flags (FILE * f, unsigned long flags, unsigned options);void print_fs_state (FILE * f,unsigned short state);int setflags (int fd,unsigned long flags);int setversion (int fd,unsigned long version); +int fgetproject (const char *name,unsigned *project); +int fsetproject (const char *name,unsigned project);const char *e2p_feature2string (int compat,unsigned int mask);const char *e2p_jrnl_feature2string (int compat,unsigned int mask);
lib/e2p/project.c new file +55 -0
55 lines not on this page · raw message
lib/ext2fs/ext2_fs.h +5 -0
@@ -376,6 +376,11 @@ struct ext4_new_group_input {#define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input)#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64) +#ifndef FS_IOC_GETPROJECT +#define FS_IOC_GETPROJECT _IOR('f', 20, unsigned) +#define FS_IOC_SETPROJECT _IOW('f', 21, unsigned) +#endif +/* * Structure of an inode on the disk */
misc/chattr.1.in +7 -0
@@ -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 id. .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
misc/chattr.c +31 -3
@@ -64,6 +64,9 @@ static int set_version;static unsigned long version; +static int set_project; +static unsigned 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] [-+=aAcCdDeijsStTu] [-v version] files...\n" ), +_ ("Usage: %s [-RVf] [-+=aAcCdDeijsStTu] [-v version] [-p project] files...\n" ), program_name);exit (1 ); }
@@ -159,6 +162,20 @@ static int decode_arg (int * i, int argc, char ** argv) set_version =1 ;continue ; } +if (*p =='p' ) { + (*i)++; +if (*i >= argc) +usage (); + project =strtoul (argv[*i], &tmp,0 ); +if (*tmp) { +com_err (program_name,0 , +_ ("bad project - %s\n" ), + argv[*i]); +usage (); + } + set_project =1 ; +continue ; + }if ((fl =get_flag (*p)) ==0 )usage (); rf |= fl;
@@ -248,6 +265,17 @@ static int change_attributes(const char * name)return -1 ; } } +if (set_project) { +if (verbose) +printf (_ ("Project of %s set as %u\n" ), name, project); +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 ;
@@ -311,8 +339,8 @@ 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)) { -fputs (_ ("Must use '-v', =, - or +\n" ), stderr); +if (!(add || rem || set || set_version || set_project)) { +fputs (_ ("Must use '-v', '-p', =, - or +\n" ), stderr);exit (1 ); }if (verbose)
misc/lsattr.1.in +4 -1
@@ -5,7 +5,7 @@ lsattr \- list file attributes on a Linux second extended file system .SH SYNOPSIS .B lsattr [ -.B \-RVadv +.B \-RVadvp ] [ .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 id. .SH AUTHOR .B lsattr was written by Remy Card <[email protected]>. It is currently being
misc/lsattr.c +17 -2
@@ -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 [-RVadlvp] [files...]\n" ), program_name);exit (1 ); }
@@ -94,6 +95,17 @@ static int list_attributes (const char * name) }printf ("%5lu " , generation); } +if (project_opt) { +unsigned project; + +if (fgetproject (name, &project) ==-1 ) { +com_err (program_name, errno, +_ ("While reading project on %s" ), + name); +return -1 ; + } +printf ("%5u " , project); + }if (pf_options &PFOPT_LONG ) {printf ("%-28s " , name);print_flags (stdout, flags, pf_options);
@@ -171,7 +183,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 +204,9 @@ int main (int argc, char ** argv)case 'v' : generation_opt =1 ;break ; +case 'p' : + project_opt =1 ; +break ;default :usage (); }