diff mbox

[4/5] tune2fs: Add support for turning on quota feature

Message ID 1311187206-30553-5-git-send-email-adityakali@google.com
State Superseded, archived
Headers show

Commit Message

Aditya Kali July 20, 2011, 6:40 p.m. UTC
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

 # 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@google.com>
---
 misc/Makefile.in  |   14 +++--
 misc/tune2fs.8.in |   15 +++++
 misc/tune2fs.c    |  148 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 167 insertions(+), 10 deletions(-)

Comments

Andreas Dilger Sept. 9, 2011, 10:39 p.m. UTC | #1
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@google.com>
> ---
> 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@vger.kernel.org
> 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@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Aditya Kali Sept. 9, 2011, 11:41 p.m. UTC | #2
On Fri, Sep 9, 2011 at 3:39 PM, Andreas Dilger <adilger@dilger.ca> wrote:
> 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.
>
Currently, if there are no existing aquota.user and aquota.group
files, then tune2fs will create empty quota files and it will need an
e2fsck to update the quota information.

> 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.
>
Yes. Calling compute_quota() is all that is needed. I thought that the
few minutes of time for doing full inode scan would be considered "too
expensive" for tune2fs (compared to tune2fs's other operations which
simply do the minimum needed to turn features on/off). Also, the
workflow I was assuming that the user will first run tune2fs to set
the feature and then run e2fsck to soon after to update quotas. I will
add computing the quotas during tune2fs since it doesn't seem to be a
problem.

>> # 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@google.com>
>> ---
>> 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@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>
> Cheers, Andreas
>
>
>
>
>
>

Thanks,
--
Aditya
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

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;