On 2011-07-20, at 12:40 PM, Aditya Kali wrote: > This patch adds support for setting the quota feature in superblock > and allows selectively creating quota inodes (user or group or both) > in the superblock. Currently, modifying the quota feature is only > supported when the filesystem is unmounted. > Also, when setting the quota feature, tune2fs will use aquota.user or > aquota.group file inode number in superblock if these files exist. > Otherwise it will initialize empty quota inodes #3 and #4 and use them. > > Here is how it works: > # Set quota feature and initialize both (user and group) quota inodes > $ tune2fs -O quota /dev/ram1 > > # Enable only one type of quota > $ tune2fs -Q usrquota /dev/ram1 I was looking at this patch, and would like some clarification. Does enabling quota also update the quota files from the filesystem, or does that need a full e2fsck? On large filesystems, it would be very desirable to allow enabling the quota by having tune2fs do a fast inode table scan (which can complete in a couple of minutes even for a large filesystem) instead of potentially hours. It looks like it would be enough to call compute_quota() after the quota files are opened, before release_quota_context() is called. In our past experience, scanning just the inode table for quotas on large filesystems only takes a few minutes. Also, this avoids the need to exit from tune2fs with the RO_COMPAT_QUOTA feature enabled, but the filesystem is not valid because the quota file is empty, and in fact the below code doesn't even warn that e2fsck needs to be run. > # Enable grpquota, disable usrquota > $ tune2fs -Q ^usrquota,grpquota /dev/ram1 > > # Clear quota feature and remove quota inodes > $ tune2fs -O ^quota /dev/ram1 > > Signed-off-by: Aditya Kali <adityakali@xxxxxxxxxx> > --- > misc/Makefile.in | 14 +++-- > misc/tune2fs.8.in | 15 +++++ > misc/tune2fs.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++-- > 3 files changed, 167 insertions(+), 10 deletions(-) > > diff --git a/misc/Makefile.in b/misc/Makefile.in > index 2f7908c..a23adcd 100644 > --- a/misc/Makefile.in > +++ b/misc/Makefile.in > @@ -143,24 +143,26 @@ e2initrd_helper: e2initrd_helper.o $(DEPLIBS) $(DEPLIBBLKID) $(LIBEXT2FS) > $(LIBBLKID) $(LIBEXT2FS) $(LIBINTL) > > tune2fs: $(TUNE2FS_OBJS) $(DEPLIBS) $(DEPLIBS_E2P) $(DEPLIBBLKID) \ > - $(DEPLIBUUID) $(LIBEXT2FS) > + $(DEPLIBUUID) $(DEPLIBQUOTA) $(LIBEXT2FS) > $(E) " LD $@" > $(Q) $(CC) $(ALL_LDFLAGS) -o tune2fs $(TUNE2FS_OBJS) $(LIBS) \ > - $(LIBBLKID) $(LIBUUID) $(LIBEXT2FS) $(LIBS_E2P) $(LIBINTL) > + $(LIBBLKID) $(LIBUUID) $(LIBQUOTA) $(LIBEXT2FS) $(LIBS_E2P) \ > + $(LIBINTL) > > tune2fs.static: $(TUNE2FS_OBJS) $(STATIC_DEPLIBS) $(STATIC_LIBE2P) $(DEPSTATIC_LIBBLKID) > $(E) " LD $@" > $(Q) $(CC) $(LDFLAGS_STATIC) -o tune2fs.static $(TUNE2FS_OBJS) \ > $(STATIC_LIBS) $(STATIC_LIBBLKID) $(STATIC_LIBUUID) \ > - $(STATIC_LIBE2P) $(LIBINTL) > + $(STATIC_LIBQUOTA) $(STATIC_LIBE2P) $(LIBINTL) > > tune2fs.profiled: $(PROFILED_TUNE2FS_OBJS) $(PROFILED_DEPLIBS) \ > - $(PROFILED_E2P) $(DEPPROFILED_LIBBLKID) $(DEPPROFILED_LIBUUID) > + $(PROFILED_E2P) $(DEPPROFILED_LIBBLKID) $(DEPPROFILED_LIBUUID) \ > + $(DEPPROFILED_LIBQUOTA) > $(E) " LD $@" > $(Q) $(CC) $(ALL_LDFLAGS) -g -pg -o tune2fs.profiled \ > $(PROFILED_TUNE2FS_OBJS) $(PROFILED_LIBBLKID) \ > - $(PROFILED_LIBUUID) $(PROFILED_LIBE2P) $(LIBINTL) \ > - $(PROFILED_LIBS) > + $(PROFILED_LIBUUID) $(PROFILED_LIBQUOTA) $(PROFILED_LIBE2P) \ > + $(LIBINTL) $(PROFILED_LIBS) > > blkid: $(BLKID_OBJS) $(DEPLIBBLKID) $(LIBEXT2FS) > $(E) " LD $@" > diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in > index 233f85a..89bc1d9 100644 > --- a/misc/tune2fs.8.in > +++ b/misc/tune2fs.8.in > @@ -77,6 +77,11 @@ tune2fs \- adjust tunable filesystem parameters on ext2/ext3/ext4 filesystems > .RI [^] feature [,...] > ] > [ > +.B \-Q > +.I quota-options > +] > +[ > +[ > .B \-T > .I time-last-checked > ] > @@ -561,6 +566,16 @@ features are only supported by the ext4 filesystem. > .BI \-r " reserved-blocks-count" > Set the number of reserved filesystem blocks. > .TP > +.BI \-Q " quota-options" > +Sets 'quota' feature on the superblock and works on the quota files for the > +given quota type. Quota options could be one or more of the following: > +.RS 1.2i > +.TP > +.BR [^]usrquota > +Sets/clears user quota inode in the superblock. > +.BR [^]usrquota > +Sets/clears group quota inode in the superblock. > +.TP > .BI \-T " time-last-checked" > Set the time the filesystem was last checked using > .BR e2fsck . > diff --git a/misc/tune2fs.c b/misc/tune2fs.c > index 5bf5187..3c81898 100644 > --- a/misc/tune2fs.c > +++ b/misc/tune2fs.c > @@ -55,16 +55,22 @@ extern int optind; > #include "jfs_user.h" > #include "util.h" > #include "blkid/blkid.h" > +#include "quota/mkquota.h" > > #include "../version.h" > #include "nls-enable.h" > > +#define QOPT_ENABLE (1) > +#define QOPT_DISABLE (-1) > + > +extern int ask_yn(const char *string, int def); > + > const char *program_name = "tune2fs"; > char *device_name; > char *new_label, *new_last_mounted, *new_UUID; > char *io_options; > static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag; > -static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag; > +static int m_flag, M_flag, Q_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag; > static int I_flag; > static time_t last_check_time; > static int print_label; > @@ -82,6 +88,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; > > int journal_size, journal_flags; > char *journal_device; > @@ -131,7 +138,8 @@ static __u32 ok_features[3] = { > EXT4_FEATURE_RO_COMPAT_DIR_NLINK| > EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| > EXT4_FEATURE_RO_COMPAT_GDT_CSUM | > - EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER > + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER | > + EXT4_FEATURE_RO_COMPAT_QUOTA > }; > > static __u32 clear_ok_features[3] = { > @@ -147,7 +155,8 @@ static __u32 clear_ok_features[3] = { > EXT4_FEATURE_RO_COMPAT_HUGE_FILE| > EXT4_FEATURE_RO_COMPAT_DIR_NLINK| > EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| > - EXT4_FEATURE_RO_COMPAT_GDT_CSUM > + EXT4_FEATURE_RO_COMPAT_GDT_CSUM | > + EXT4_FEATURE_RO_COMPAT_QUOTA > }; > > /* > @@ -477,6 +486,36 @@ static void update_feature_set(ext2_filsys fs, char *features) > fs->flags &= ~EXT2_FLAG_SUPER_ONLY; > } > > + if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT, > + EXT4_FEATURE_RO_COMPAT_QUOTA)) { > + /* > + * Set the Q_flag here and handle the quota options in the code > + * below. > + */ > + if (!Q_flag) { > + Q_flag = 1; > + /* Enable both user quota and group quota by default */ > + usrquota = QOPT_ENABLE; > + grpquota = QOPT_ENABLE; > + } > + sb->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA; > + } > + > + if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT, > + EXT4_FEATURE_RO_COMPAT_QUOTA)) { > + /* > + * Set the Q_flag here and handle the quota options in the code > + * below. > + */ > + if (Q_flag) > + 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; > + } > + > if (sb->s_rev_level == EXT2_GOOD_OLD_REV && > (sb->s_feature_compat || sb->s_feature_ro_compat || > sb->s_feature_incompat)) > @@ -576,6 +615,93 @@ err: > exit(1); > } > > +void handle_quota_options(ext2_filsys fs) > +{ > + quota_ctx_t qctx; > + errcode_t retval; > + ext2_ino_t qf_ino; > + > + if (!usrquota && !grpquota) > + /* Nothing to do. */ > + return; > + > + init_quota_context(&qctx, fs, -1); > + > + if (usrquota == QOPT_ENABLE && !fs->super->s_usr_quota_inum) { > + if ((qf_ino = quota_file_exists(fs, USRQUOTA, QFMT_VFS_V1)) > 0) > + set_sb_quota_inum(fs, qf_ino, USRQUOTA); > + else > + write_quota_inode(qctx, USRQUOTA); > + } else if (usrquota == QOPT_DISABLE) { > + remove_quota_inode(fs, USRQUOTA); > + } > + > + if (grpquota == QOPT_ENABLE && !fs->super->s_grp_quota_inum) { > + if ((qf_ino = quota_file_exists(fs, GRPQUOTA, QFMT_VFS_V1)) > 0) > + set_sb_quota_inum(fs, qf_ino, GRPQUOTA); > + else > + write_quota_inode(qctx, GRPQUOTA); > + } else if (grpquota == QOPT_DISABLE) { > + remove_quota_inode(fs, GRPQUOTA); > + } > + > + release_quota_context(&qctx); > + > + if ((usrquota == QOPT_ENABLE) || (grpquota == QOPT_ENABLE)) { > + fs->super->s_feature_ro_compat |= EXT4_FEATURE_RO_COMPAT_QUOTA; > + ext2fs_mark_super_dirty(fs); > + } else if ((usrquota == QOPT_DISABLE) && (grpquota == QOPT_DISABLE)) { > + fs->super->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA; > + ext2fs_mark_super_dirty(fs); > + } > + > + return; > +} > + > +void parse_quota_opts(const char *opts) > +{ > + char *buf, *token, *next, *p, *arg; > + 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); > + } > + } > + free(buf); > +} > + > + > > static void parse_e2label_options(int argc, char ** argv) > { > @@ -641,7 +767,7 @@ static void parse_tune2fs_options(int argc, char **argv) > open_flag = 0; > > printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); > - while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:E:I:J:L:M:O:T:U:")) != EOF) > + while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:E:I:J:L:M:O:Q:T:U:")) != EOF) > switch (c) { > case 'c': > max_mount_count = strtol(optarg, &tmp, 0); > @@ -796,6 +922,11 @@ static void parse_tune2fs_options(int argc, char **argv) > features_cmd = optarg; > open_flag = EXT2_FLAG_RW; > break; > + case 'Q': > + Q_flag = 1; > + parse_quota_opts(optarg); > + open_flag = EXT2_FLAG_RW; > + break; > case 'r': > reserved_blocks = strtoul(optarg, &tmp, 0); > if (*tmp) { > @@ -1790,6 +1921,15 @@ retry_open: > if (journal_size || journal_device) > add_journal(fs); > > + if (Q_flag) { > + if (mount_flags & EXT2_MF_MOUNTED) { > + fputs(_("The quota feature may only be changed when " > + "the filesystem is unmounted.\n"), stderr); > + exit(1); > + } > + handle_quota_options(fs); > + } > + > if (U_flag) { > int set_csum = 0; > dgrp_t i; > -- > 1.7.3.1 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html Cheers, Andreas -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html