Home | History | Annotate | Download | only in misc
      1 /*
      2  * tune2fs.c - Change the file system parameters on an ext2 file system
      3  *
      4  * Copyright (C) 1992, 1993, 1994  Remy Card <card (at) masi.ibp.fr>
      5  *                                 Laboratoire MASI, Institut Blaise Pascal
      6  *                                 Universite Pierre et Marie Curie (Paris VI)
      7  *
      8  * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
      9  *
     10  * %Begin-Header%
     11  * This file may be redistributed under the terms of the GNU Public
     12  * License.
     13  * %End-Header%
     14  */
     15 
     16 /*
     17  * History:
     18  * 93/06/01	- Creation
     19  * 93/10/31	- Added the -c option to change the maximal mount counts
     20  * 93/12/14	- Added -l flag to list contents of superblock
     21  *                M.J.E. Mol (marcel (at) duteca.et.tudelft.nl)
     22  *                F.W. ten Wolde (franky (at) duteca.et.tudelft.nl)
     23  * 93/12/29	- Added the -e option to change errors behavior
     24  * 94/02/27	- Ported to use the ext2fs library
     25  * 94/03/06	- Added the checks interval from Uwe Ohse (uwe (at) tirka.gun.de)
     26  */
     27 
     28 #define _XOPEN_SOURCE 500 /* for inclusion of strptime() */
     29 #define _BSD_SOURCE /* for inclusion of strcasecmp() */
     30 #include <fcntl.h>
     31 #include <grp.h>
     32 #ifdef HAVE_GETOPT_H
     33 #include <getopt.h>
     34 #else
     35 extern char *optarg;
     36 extern int optind;
     37 #endif
     38 #include <pwd.h>
     39 #include <stdio.h>
     40 #include <stdlib.h>
     41 #include <string.h>
     42 #include <time.h>
     43 #include <unistd.h>
     44 #include <sys/types.h>
     45 
     46 #include "ext2fs/ext2_fs.h"
     47 #include "ext2fs/ext2fs.h"
     48 #include "et/com_err.h"
     49 #include "uuid/uuid.h"
     50 #include "e2p/e2p.h"
     51 #include "jfs_user.h"
     52 #include "util.h"
     53 #include "blkid/blkid.h"
     54 
     55 #include "../version.h"
     56 #include "nls-enable.h"
     57 
     58 /*
     59  * Tune2fs supports these features in addition to the standard features.
     60  */
     61 #define EXT2_TUNE2FS_INCOMPAT	(EXT3_FEATURE_INCOMPAT_EXTENTS)
     62 #define EXT2_TUNE2FS_RO_COMPAT	(EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
     63 				 EXT4_FEATURE_RO_COMPAT_GDT_CSUM|	\
     64 				 EXT4_FEATURE_RO_COMPAT_DIR_NLINK|	\
     65 				 EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)
     66 
     67 
     68 const char * program_name = "tune2fs";
     69 char * device_name;
     70 char * new_label, *new_last_mounted, *new_UUID;
     71 char * io_options;
     72 static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag;
     73 static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag;
     74 static time_t last_check_time;
     75 static int print_label;
     76 static int max_mount_count, mount_count, mount_flags;
     77 static unsigned long interval, reserved_blocks;
     78 static double reserved_ratio;
     79 static unsigned long resgid, resuid;
     80 static unsigned short errors;
     81 static int open_flag;
     82 static char *features_cmd;
     83 static char *mntopts_cmd;
     84 static int stride, stripe_width;
     85 static int stride_set, stripe_width_set;
     86 static char *extended_cmd;
     87 
     88 int journal_size, journal_flags;
     89 char *journal_device;
     90 
     91 static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n");
     92 
     93 void do_findfs(int argc, char **argv);
     94 
     95 static void usage(void)
     96 {
     97 	fprintf(stderr,
     98 		_("Usage: %s [-c max_mounts_count] [-e errors_behavior] "
     99 		  "[-g group]\n"
    100 		  "\t[-i interval[d|m|w]] [-j] [-J journal_options] [-l]\n"
    101 		  "\t[-m reserved_blocks_percent] "
    102 		  "[-o [^]mount_options[,...]] \n"
    103 		  "\t[-r reserved_blocks_count] [-u user] [-C mount_count] "
    104 		  "[-L volume_label]\n"
    105 		  "\t[-M last_mounted_dir] [-O [^]feature[,...]]\n"
    106 		  "\t[-E extended-option[,...]] [-T last_check_time] "
    107 		  "[-U UUID] device\n"), program_name);
    108 	exit (1);
    109 }
    110 
    111 static __u32 ok_features[3] = {
    112 	/* Compat */
    113 	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
    114 		EXT2_FEATURE_COMPAT_DIR_INDEX,
    115 	/* Incompat */
    116 	EXT2_FEATURE_INCOMPAT_FILETYPE,
    117 	/* R/O compat */
    118 	EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
    119 		EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
    120 };
    121 
    122 static __u32 clear_ok_features[3] = {
    123 	/* Compat */
    124 	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
    125 		EXT2_FEATURE_COMPAT_RESIZE_INODE |
    126 		EXT2_FEATURE_COMPAT_DIR_INDEX,
    127 	/* Incompat */
    128 	EXT2_FEATURE_INCOMPAT_FILETYPE,
    129 	/* R/O compat */
    130 	EXT2_FEATURE_RO_COMPAT_LARGE_FILE
    131 };
    132 
    133 /*
    134  * Remove an external journal from the filesystem
    135  */
    136 static void remove_journal_device(ext2_filsys fs)
    137 {
    138 	char		*journal_path;
    139 	ext2_filsys	jfs;
    140 	char		buf[1024];
    141 	journal_superblock_t	*jsb;
    142 	int		i, nr_users;
    143 	errcode_t	retval;
    144 	int		commit_remove_journal = 0;
    145 	io_manager	io_ptr;
    146 
    147 	if (f_flag)
    148 		commit_remove_journal = 1; /* force removal even if error */
    149 
    150 	uuid_unparse(fs->super->s_journal_uuid, buf);
    151 	journal_path = blkid_get_devname(NULL, "UUID", buf);
    152 
    153 	if (!journal_path) {
    154 		journal_path =
    155 			ext2fs_find_block_device(fs->super->s_journal_dev);
    156 		if (!journal_path)
    157 			return;
    158 	}
    159 
    160 #ifdef CONFIG_TESTIO_DEBUG
    161 	io_ptr = test_io_manager;
    162 	test_io_backing_manager = unix_io_manager;
    163 #else
    164 	io_ptr = unix_io_manager;
    165 #endif
    166 	retval = ext2fs_open(journal_path, EXT2_FLAG_RW|
    167 			     EXT2_FLAG_JOURNAL_DEV_OK, 0,
    168 			     fs->blocksize, io_ptr, &jfs);
    169 	if (retval) {
    170 		com_err(program_name, retval,
    171 			_("while trying to open external journal"));
    172 		goto no_valid_journal;
    173 	}
    174 	if (!(jfs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
    175 		fprintf(stderr, _("%s is not a journal device.\n"),
    176 			journal_path);
    177 		goto no_valid_journal;
    178 	}
    179 
    180 	/* Get the journal superblock */
    181 	if ((retval = io_channel_read_blk(jfs->io, 1, -1024, buf))) {
    182 		com_err(program_name, retval,
    183 			_("while reading journal superblock"));
    184 		goto no_valid_journal;
    185 	}
    186 
    187 	jsb = (journal_superblock_t *) buf;
    188 	if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
    189 	    (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) {
    190 		fputs(_("Journal superblock not found!\n"), stderr);
    191 		goto no_valid_journal;
    192 	}
    193 
    194 	/* Find the filesystem UUID */
    195 	nr_users = ntohl(jsb->s_nr_users);
    196 	for (i=0; i < nr_users; i++) {
    197 		if (memcmp(fs->super->s_uuid,
    198 			   &jsb->s_users[i*16], 16) == 0)
    199 			break;
    200 	}
    201 	if (i >= nr_users) {
    202 		fputs(_("Filesystem's UUID not found on journal device.\n"),
    203 		      stderr);
    204 		commit_remove_journal = 1;
    205 		goto no_valid_journal;
    206 	}
    207 	nr_users--;
    208 	for (i=0; i < nr_users; i++)
    209 		memcpy(&jsb->s_users[i*16], &jsb->s_users[(i+1)*16], 16);
    210 	jsb->s_nr_users = htonl(nr_users);
    211 
    212 	/* Write back the journal superblock */
    213 	if ((retval = io_channel_write_blk(jfs->io, 1, -1024, buf))) {
    214 		com_err(program_name, retval,
    215 			"while writing journal superblock.");
    216 		goto no_valid_journal;
    217 	}
    218 
    219 	commit_remove_journal = 1;
    220 
    221 no_valid_journal:
    222 	if (commit_remove_journal == 0) {
    223 		fputs(_("Journal NOT removed\n"), stderr);
    224 		exit(1);
    225 	}
    226 	fs->super->s_journal_dev = 0;
    227 	uuid_clear(fs->super->s_journal_uuid);
    228 	ext2fs_mark_super_dirty(fs);
    229 	fputs(_("Journal removed\n"), stdout);
    230 	free(journal_path);
    231 }
    232 
    233 /* Helper function for remove_journal_inode */
    234 static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
    235 			       int blockcnt EXT2FS_ATTR((unused)),
    236 			       void *private EXT2FS_ATTR((unused)))
    237 {
    238 	blk_t	block;
    239 	int	group;
    240 
    241 	block = *blocknr;
    242 	ext2fs_unmark_block_bitmap(fs->block_map,block);
    243 	group = ext2fs_group_of_blk(fs, block);
    244 	fs->group_desc[group].bg_free_blocks_count++;
    245 	fs->super->s_free_blocks_count++;
    246 	return 0;
    247 }
    248 
    249 /*
    250  * Remove the journal inode from the filesystem
    251  */
    252 static void remove_journal_inode(ext2_filsys fs)
    253 {
    254 	struct ext2_inode	inode;
    255 	errcode_t		retval;
    256 	ino_t			ino = fs->super->s_journal_inum;
    257 
    258 	retval = ext2fs_read_inode(fs, ino,  &inode);
    259 	if (retval) {
    260 		com_err(program_name, retval,
    261 			_("while reading journal inode"));
    262 		exit(1);
    263 	}
    264 	if (ino == EXT2_JOURNAL_INO) {
    265 		retval = ext2fs_read_bitmaps(fs);
    266 		if (retval) {
    267 			com_err(program_name, retval,
    268 				_("while reading bitmaps"));
    269 			exit(1);
    270 		}
    271 		retval = ext2fs_block_iterate(fs, ino, 0, NULL,
    272 					      release_blocks_proc, NULL);
    273 		if (retval) {
    274 			com_err(program_name, retval,
    275 				_("while clearing journal inode"));
    276 			exit(1);
    277 		}
    278 		memset(&inode, 0, sizeof(inode));
    279 		ext2fs_mark_bb_dirty(fs);
    280 		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
    281 	} else
    282 		inode.i_flags &= ~EXT2_IMMUTABLE_FL;
    283 	retval = ext2fs_write_inode(fs, ino, &inode);
    284 	if (retval) {
    285 		com_err(program_name, retval,
    286 			_("while writing journal inode"));
    287 		exit(1);
    288 	}
    289 	fs->super->s_journal_inum = 0;
    290 	ext2fs_mark_super_dirty(fs);
    291 }
    292 
    293 /*
    294  * Update the default mount options
    295  */
    296 static void update_mntopts(ext2_filsys fs, char *mntopts)
    297 {
    298 	struct ext2_super_block *sb= fs->super;
    299 
    300 	if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0)) {
    301 		fprintf(stderr, _("Invalid mount option set: %s\n"),
    302 			mntopts);
    303 		exit(1);
    304 	}
    305 	ext2fs_mark_super_dirty(fs);
    306 }
    307 
    308 /*
    309  * Update the feature set as provided by the user.
    310  */
    311 static void update_feature_set(ext2_filsys fs, char *features)
    312 {
    313 	struct ext2_super_block *sb= fs->super;
    314 	__u32	old_compat, old_incompat, old_ro_compat;
    315 	__u32		old_features[3];
    316 	int		type_err;
    317 	unsigned int	mask_err;
    318 
    319 #define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \
    320 				((&sb->s_feature_compat)[(type)] & (mask)))
    321 #define FEATURE_OFF(type, mask) ((old_features[(type)] & (mask)) && \
    322 				 !((&sb->s_feature_compat)[(type)] & (mask)))
    323 #define FEATURE_CHANGED(type, mask) ((mask) & \
    324 		     (old_features[(type)] ^ (&sb->s_feature_compat)[(type)]))
    325 
    326 	old_features[E2P_FEATURE_COMPAT] = sb->s_feature_compat;
    327 	old_features[E2P_FEATURE_INCOMPAT] = sb->s_feature_incompat;
    328 	old_features[E2P_FEATURE_RO_INCOMPAT] = sb->s_feature_ro_compat;
    329 
    330 	if (e2p_edit_feature2(features, &sb->s_feature_compat,
    331 			      ok_features, clear_ok_features,
    332 			      &type_err, &mask_err)) {
    333 		if (!mask_err)
    334 			fprintf(stderr,
    335 				_("Invalid filesystem option set: %s\n"),
    336 				features);
    337 		else if (type_err & E2P_FEATURE_NEGATE_FLAG)
    338 			fprintf(stderr, _("Clearing filesystem feature '%s' "
    339 					  "not supported.\n"),
    340 				e2p_feature2string(type_err &
    341 						   E2P_FEATURE_TYPE_MASK,
    342 						   mask_err));
    343 		else
    344 			fprintf(stderr, _("Setting filesystem feature '%s' "
    345 					  "not supported.\n"),
    346 				e2p_feature2string(type_err, mask_err));
    347 		exit(1);
    348 	}
    349 
    350 	if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
    351 		if ((mount_flags & EXT2_MF_MOUNTED) &&
    352 		    !(mount_flags & EXT2_MF_READONLY)) {
    353 			fputs(_("The has_journal flag may only be "
    354 				"cleared when the filesystem is\n"
    355 				"unmounted or mounted "
    356 				"read-only.\n"), stderr);
    357 			exit(1);
    358 		}
    359 		if (sb->s_feature_incompat &
    360 		    EXT3_FEATURE_INCOMPAT_RECOVER) {
    361 			fputs(_("The needs_recovery flag is set.  "
    362 				"Please run e2fsck before clearing\n"
    363 				"the has_journal flag.\n"), stderr);
    364 			exit(1);
    365 		}
    366 		if (sb->s_journal_inum) {
    367 			remove_journal_inode(fs);
    368 		}
    369 		if (sb->s_journal_dev) {
    370 			remove_journal_device(fs);
    371 		}
    372 	}
    373 
    374 	if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
    375 		/*
    376 		 * If adding a journal flag, let the create journal
    377 		 * code below handle creating setting the flag and
    378 		 * creating the journal.  We supply a default size if
    379 		 * necessary.
    380 		 */
    381 		if (!journal_size)
    382 			journal_size = -1;
    383 		sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
    384 	}
    385 
    386 	if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX)) {
    387 		if (!sb->s_def_hash_version)
    388 			sb->s_def_hash_version = EXT2_HASH_TEA;
    389 		if (uuid_is_null((unsigned char *) sb->s_hash_seed))
    390 			uuid_generate((unsigned char *) sb->s_hash_seed);
    391 	}
    392 
    393 	if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
    394 	    (sb->s_feature_compat || sb->s_feature_ro_compat ||
    395 	     sb->s_feature_incompat))
    396 		ext2fs_update_dynamic_rev(fs);
    397 
    398 	if (FEATURE_CHANGED(E2P_FEATURE_RO_INCOMPAT,
    399 			    EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
    400 	    FEATURE_CHANGED(E2P_FEATURE_INCOMPAT,
    401 			    EXT2_FEATURE_INCOMPAT_FILETYPE) ||
    402 	    FEATURE_CHANGED(E2P_FEATURE_COMPAT,
    403 			    EXT2_FEATURE_COMPAT_RESIZE_INODE) ||
    404 	    FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
    405 			EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
    406 		sb->s_state &= ~EXT2_VALID_FS;
    407 		printf("\n%s\n", _(please_fsck));
    408 	}
    409 
    410 	if ((old_features[E2P_FEATURE_COMPAT] != sb->s_feature_compat) ||
    411 	    (old_features[E2P_FEATURE_INCOMPAT] != sb->s_feature_incompat) ||
    412 	    (old_features[E2P_FEATURE_RO_INCOMPAT] != sb->s_feature_ro_compat))
    413 		ext2fs_mark_super_dirty(fs);
    414 }
    415 
    416 /*
    417  * Add a journal to the filesystem.
    418  */
    419 static void add_journal(ext2_filsys fs)
    420 {
    421 	unsigned long journal_blocks;
    422 	errcode_t	retval;
    423 	ext2_filsys	jfs;
    424 	io_manager	io_ptr;
    425 
    426 	if (fs->super->s_feature_compat &
    427 	    EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
    428 		fputs(_("The filesystem already has a journal.\n"), stderr);
    429 		goto err;
    430 	}
    431 	if (journal_device) {
    432 		check_plausibility(journal_device);
    433 		check_mount(journal_device, 0, _("journal"));
    434 #ifdef CONFIG_TESTIO_DEBUG
    435 		io_ptr = test_io_manager;
    436 		test_io_backing_manager = unix_io_manager;
    437 #else
    438 		io_ptr = unix_io_manager;
    439 #endif
    440 		retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
    441 				     EXT2_FLAG_JOURNAL_DEV_OK, 0,
    442 				     fs->blocksize, io_ptr, &jfs);
    443 		if (retval) {
    444 			com_err(program_name, retval,
    445 				_("\n\twhile trying to open journal on %s\n"),
    446 				journal_device);
    447 			goto err;
    448 		}
    449 		printf(_("Creating journal on device %s: "),
    450 		       journal_device);
    451 		fflush(stdout);
    452 
    453 		retval = ext2fs_add_journal_device(fs, jfs);
    454 		ext2fs_close(jfs);
    455 		if (retval) {
    456 			com_err (program_name, retval,
    457 				 _("while adding filesystem to journal on %s"),
    458 				 journal_device);
    459 			goto err;
    460 		}
    461 		fputs(_("done\n"), stdout);
    462 	} else if (journal_size) {
    463 		fputs(_("Creating journal inode: "), stdout);
    464 		fflush(stdout);
    465 		journal_blocks = figure_journal_size(journal_size, fs);
    466 
    467 		retval = ext2fs_add_journal_inode(fs, journal_blocks,
    468 						  journal_flags);
    469 		if (retval) {
    470 			fprintf(stderr, "\n");
    471 			com_err(program_name, retval,
    472 				_("\n\twhile trying to create journal file"));
    473 			exit(1);
    474 		} else
    475 			fputs(_("done\n"), stdout);
    476 		/*
    477 		 * If the filesystem wasn't mounted, we need to force
    478 		 * the block group descriptors out.
    479 		 */
    480 		if ((mount_flags & EXT2_MF_MOUNTED) == 0)
    481 			fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
    482 	}
    483 	print_check_message(fs);
    484 	return;
    485 
    486 err:
    487 	if (journal_device)
    488 		free(journal_device);
    489 	exit(1);
    490 }
    491 
    492 
    493 static void parse_e2label_options(int argc, char ** argv)
    494 {
    495 	if ((argc < 2) || (argc > 3)) {
    496 		fputs(_("Usage: e2label device [newlabel]\n"), stderr);
    497 		exit(1);
    498 	}
    499 	io_options = strchr(argv[1], '?');
    500 	if (io_options)
    501 		*io_options++ = 0;
    502 	device_name = blkid_get_devname(NULL, argv[1], NULL);
    503 	if (!device_name) {
    504 		com_err("e2label", 0, _("Unable to resolve '%s'"),
    505 			argv[1]);
    506 		exit(1);
    507 	}
    508 	open_flag = EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_JOURNAL_DEV_OK;
    509 	if (argc == 3) {
    510 		open_flag |= EXT2_FLAG_RW;
    511 		L_flag = 1;
    512 		new_label = argv[2];
    513 	} else
    514 		print_label++;
    515 }
    516 
    517 static time_t parse_time(char *str)
    518 {
    519 	struct	tm	ts;
    520 
    521 	if (strcmp(str, "now") == 0) {
    522 		return (time(0));
    523 	}
    524 	memset(&ts, 0, sizeof(ts));
    525 #ifdef HAVE_STRPTIME
    526 	strptime(str, "%Y%m%d%H%M%S", &ts);
    527 #else
    528 	sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
    529 	       &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
    530 	ts.tm_year -= 1900;
    531 	ts.tm_mon -= 1;
    532 	if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
    533 	    ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
    534 	    ts.tm_min > 59 || ts.tm_sec > 61)
    535 		ts.tm_mday = 0;
    536 #endif
    537 	if (ts.tm_mday == 0) {
    538 		com_err(program_name, 0,
    539 			_("Couldn't parse date/time specifier: %s"),
    540 			str);
    541 		usage();
    542 	}
    543 	return (mktime(&ts));
    544 }
    545 
    546 static void parse_tune2fs_options(int argc, char **argv)
    547 {
    548 	int c;
    549 	char * tmp;
    550 	struct group * gr;
    551 	struct passwd * pw;
    552 
    553 	open_flag = EXT2_FLAG_SOFTSUPP_FEATURES;
    554 
    555 	printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
    556 	while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:E:J:L:M:O:T:U:")) != EOF)
    557 		switch (c)
    558 		{
    559 			case 'c':
    560 				max_mount_count = strtol (optarg, &tmp, 0);
    561 				if (*tmp || max_mount_count > 16000) {
    562 					com_err (program_name, 0,
    563 						 _("bad mounts count - %s"),
    564 						 optarg);
    565 					usage();
    566 				}
    567 				if (max_mount_count == 0)
    568 					max_mount_count = -1;
    569 				c_flag = 1;
    570 				open_flag = EXT2_FLAG_RW;
    571 				break;
    572 			case 'C':
    573 				mount_count = strtoul (optarg, &tmp, 0);
    574 				if (*tmp || mount_count > 16000) {
    575 					com_err (program_name, 0,
    576 						 _("bad mounts count - %s"),
    577 						 optarg);
    578 					usage();
    579 				}
    580 				C_flag = 1;
    581 				open_flag = EXT2_FLAG_RW;
    582 				break;
    583 			case 'e':
    584 				if (strcmp (optarg, "continue") == 0)
    585 					errors = EXT2_ERRORS_CONTINUE;
    586 				else if (strcmp (optarg, "remount-ro") == 0)
    587 					errors = EXT2_ERRORS_RO;
    588 				else if (strcmp (optarg, "panic") == 0)
    589 					errors = EXT2_ERRORS_PANIC;
    590 				else {
    591 					com_err (program_name, 0,
    592 						 _("bad error behavior - %s"),
    593 						 optarg);
    594 					usage();
    595 				}
    596 				e_flag = 1;
    597 				open_flag = EXT2_FLAG_RW;
    598 				break;
    599 			case 'E':
    600 				extended_cmd = optarg;
    601 				open_flag |= EXT2_FLAG_RW;
    602 				break;
    603 			case 'f': /* Force */
    604 				f_flag = 1;
    605 				break;
    606 			case 'g':
    607 				resgid = strtoul (optarg, &tmp, 0);
    608 				if (*tmp) {
    609 					gr = getgrnam (optarg);
    610 					if (gr == NULL)
    611 						tmp = optarg;
    612 					else {
    613 						resgid = gr->gr_gid;
    614 						*tmp =0;
    615 					}
    616 				}
    617 				if (*tmp) {
    618 					com_err (program_name, 0,
    619 						 _("bad gid/group name - %s"),
    620 						 optarg);
    621 					usage();
    622 				}
    623 				g_flag = 1;
    624 				open_flag = EXT2_FLAG_RW;
    625 				break;
    626 			case 'i':
    627 				interval = strtoul (optarg, &tmp, 0);
    628 				switch (*tmp) {
    629 				case 's':
    630 					tmp++;
    631 					break;
    632 				case '\0':
    633 				case 'd':
    634 				case 'D': /* days */
    635 					interval *= 86400;
    636 					if (*tmp != '\0')
    637 						tmp++;
    638 					break;
    639 				case 'm':
    640 				case 'M': /* months! */
    641 					interval *= 86400 * 30;
    642 					tmp++;
    643 					break;
    644 				case 'w':
    645 				case 'W': /* weeks */
    646 					interval *= 86400 * 7;
    647 					tmp++;
    648 					break;
    649 				}
    650 				if (*tmp) {
    651 					com_err (program_name, 0,
    652 						_("bad interval - %s"), optarg);
    653 					usage();
    654 				}
    655 				i_flag = 1;
    656 				open_flag = EXT2_FLAG_RW;
    657 				break;
    658 			case 'j':
    659 				if (!journal_size)
    660 					journal_size = -1;
    661 				open_flag = EXT2_FLAG_RW;
    662 				break;
    663 			case 'J':
    664 				parse_journal_opts(optarg);
    665 				open_flag = EXT2_FLAG_RW;
    666 				break;
    667 			case 'l':
    668 				l_flag = 1;
    669 				break;
    670 			case 'L':
    671 				new_label = optarg;
    672 				L_flag = 1;
    673 				open_flag |= EXT2_FLAG_RW |
    674 					EXT2_FLAG_JOURNAL_DEV_OK;
    675 				break;
    676 			case 'm':
    677 				reserved_ratio = strtod(optarg, &tmp);
    678 				if (*tmp || reserved_ratio > 50) {
    679 					com_err (program_name, 0,
    680 						 _("bad reserved block ratio - %s"),
    681 						 optarg);
    682 					usage();
    683 				}
    684 				m_flag = 1;
    685 				open_flag = EXT2_FLAG_RW;
    686 				break;
    687 			case 'M':
    688 				new_last_mounted = optarg;
    689 				M_flag = 1;
    690 				open_flag = EXT2_FLAG_RW;
    691 				break;
    692 			case 'o':
    693 				if (mntopts_cmd) {
    694 					com_err (program_name, 0,
    695 					 _("-o may only be specified once"));
    696 					usage();
    697 				}
    698 				mntopts_cmd = optarg;
    699 				open_flag = EXT2_FLAG_RW;
    700 				break;
    701 
    702 			case 'O':
    703 				if (features_cmd) {
    704 					com_err (program_name, 0,
    705 					 _("-O may only be specified once"));
    706 					usage();
    707 				}
    708 				features_cmd = optarg;
    709 				open_flag = EXT2_FLAG_RW;
    710 				break;
    711 			case 'r':
    712 				reserved_blocks = strtoul (optarg, &tmp, 0);
    713 				if (*tmp) {
    714 					com_err (program_name, 0,
    715 						 _("bad reserved blocks count - %s"),
    716 						 optarg);
    717 					usage();
    718 				}
    719 				r_flag = 1;
    720 				open_flag = EXT2_FLAG_RW;
    721 				break;
    722 			case 's': /* Deprecated */
    723 				s_flag = atoi(optarg);
    724 				open_flag = EXT2_FLAG_RW;
    725 				break;
    726 			case 'T':
    727 				T_flag = 1;
    728 				last_check_time = parse_time(optarg);
    729 				open_flag = EXT2_FLAG_RW;
    730 				break;
    731 			case 'u':
    732 				resuid = strtoul (optarg, &tmp, 0);
    733 				if (*tmp) {
    734 					pw = getpwnam (optarg);
    735 					if (pw == NULL)
    736 						tmp = optarg;
    737 					else {
    738 						resuid = pw->pw_uid;
    739 						*tmp = 0;
    740 					}
    741 				}
    742 				if (*tmp) {
    743 					com_err (program_name, 0,
    744 						 _("bad uid/user name - %s"),
    745 						 optarg);
    746 					usage();
    747 				}
    748 				u_flag = 1;
    749 				open_flag = EXT2_FLAG_RW;
    750 				break;
    751 			case 'U':
    752 				new_UUID = optarg;
    753 				U_flag = 1;
    754 				open_flag = EXT2_FLAG_RW |
    755 					EXT2_FLAG_JOURNAL_DEV_OK;
    756 				break;
    757 			default:
    758 				usage();
    759 		}
    760 	if (optind < argc - 1 || optind == argc)
    761 		usage();
    762 	if (!open_flag && !l_flag)
    763 		usage();
    764 	io_options = strchr(argv[optind], '?');
    765 	if (io_options)
    766 		*io_options++ = 0;
    767 	device_name = blkid_get_devname(NULL, argv[optind], NULL);
    768 	if (!device_name) {
    769 		com_err("tune2fs", 0, _("Unable to resolve '%s'"),
    770 			argv[optind]);
    771 		exit(1);
    772 	}
    773 }
    774 
    775 void do_findfs(int argc, char **argv)
    776 {
    777 	char	*dev;
    778 
    779 	if ((argc != 2) ||
    780 	    (strncmp(argv[1], "LABEL=", 6) && strncmp(argv[1], "UUID=", 5))) {
    781 		fprintf(stderr, "Usage: findfs LABEL=<label>|UUID=<uuid>\n");
    782 		exit(2);
    783 	}
    784 	dev = blkid_get_devname(NULL, argv[1], NULL);
    785 	if (!dev) {
    786 		com_err("findfs", 0, _("Unable to resolve '%s'"),
    787 			argv[1]);
    788 		exit(1);
    789 	}
    790 	puts(dev);
    791 	exit(0);
    792 }
    793 
    794 /*
    795  * Note!  If any extended options are incompatible with the
    796  * intersection of the SOFTSUPP features and those features explicitly
    797  * enabled for tune2fs, there needs to be an explicit test for them
    798  * here.
    799  */
    800 static void parse_extended_opts(ext2_filsys fs, const char *opts)
    801 {
    802 	char	*buf, *token, *next, *p, *arg;
    803 	int	len;
    804 	int	r_usage = 0;
    805 
    806 	len = strlen(opts);
    807 	buf = malloc(len+1);
    808 	if (!buf) {
    809 		fprintf(stderr,
    810 			_("Couldn't allocate memory to parse options!\n"));
    811 		exit(1);
    812 	}
    813 	strcpy(buf, opts);
    814 	for (token = buf; token && *token; token = next) {
    815 		p = strchr(token, ',');
    816 		next = 0;
    817 		if (p) {
    818 			*p = 0;
    819 			next = p+1;
    820 		}
    821 		arg = strchr(token, '=');
    822 		if (arg) {
    823 			*arg = 0;
    824 			arg++;
    825 		}
    826 		if (!strcmp(token, "test_fs")) {
    827 			fs->super->s_flags |= EXT2_FLAGS_TEST_FILESYS;
    828 			printf("Setting test filesystem flag\n");
    829 			ext2fs_mark_super_dirty(fs);
    830 		} else if (!strcmp(token, "^test_fs")) {
    831 			fs->super->s_flags &= ~EXT2_FLAGS_TEST_FILESYS;
    832 			printf("Clearing test filesystem flag\n");
    833 			ext2fs_mark_super_dirty(fs);
    834 		} else if (strcmp(token, "stride") == 0) {
    835 			if (!arg) {
    836 				r_usage++;
    837 				continue;
    838 			}
    839 			stride = strtoul(arg, &p, 0);
    840 			if (*p || (stride == 0)) {
    841 				fprintf(stderr,
    842 				       _("Invalid RAID stride: %s\n"),
    843 					arg);
    844 				r_usage++;
    845 				continue;
    846 			}
    847 			stride_set = 1;
    848 		} else if (strcmp(token, "stripe-width") == 0 ||
    849 			   strcmp(token, "stripe_width") == 0) {
    850 			if (!arg) {
    851 				r_usage++;
    852 				continue;
    853 			}
    854 			stripe_width = strtoul(arg, &p, 0);
    855 			if (*p || (stripe_width == 0)) {
    856 				fprintf(stderr,
    857 					_("Invalid RAID stripe-width: %s\n"),
    858 					arg);
    859 				r_usage++;
    860 				continue;
    861 			}
    862 			stripe_width_set = 1;
    863 		} else
    864 			r_usage++;
    865 	}
    866 	if (r_usage) {
    867 		fprintf(stderr, _("\nBad options specified.\n\n"
    868 			"Extended options are separated by commas, "
    869 			"and may take an argument which\n"
    870 			"\tis set off by an equals ('=') sign.\n\n"
    871 			"Valid extended options are:\n"
    872 			"\tstride=<RAID per-disk chunk size in blocks>\n"
    873 			"\tstripe-width=<RAID stride*data disks in blocks>\n"
    874 			"\ttest_fs\n"
    875 			"\t^test_fs\n"));
    876 		free(buf);
    877 		exit(1);
    878 	}
    879 	free(buf);
    880 }
    881 
    882 
    883 int main (int argc, char ** argv)
    884 {
    885 	errcode_t retval;
    886 	ext2_filsys fs;
    887 	struct ext2_super_block *sb;
    888 	io_manager io_ptr;
    889 
    890 #ifdef ENABLE_NLS
    891 	setlocale(LC_MESSAGES, "");
    892 	setlocale(LC_CTYPE, "");
    893 	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
    894 	textdomain(NLS_CAT_NAME);
    895 #endif
    896 	if (argc && *argv)
    897 		program_name = *argv;
    898 	add_error_table(&et_ext2_error_table);
    899 
    900 	if (strcmp(get_progname(argv[0]), "findfs") == 0)
    901 		do_findfs(argc, argv);
    902 	if (strcmp(get_progname(argv[0]), "e2label") == 0)
    903 		parse_e2label_options(argc, argv);
    904 	else
    905 		parse_tune2fs_options(argc, argv);
    906 
    907 #ifdef CONFIG_TESTIO_DEBUG
    908 	io_ptr = test_io_manager;
    909 	test_io_backing_manager = unix_io_manager;
    910 #else
    911 	io_ptr = unix_io_manager;
    912 #endif
    913 	retval = ext2fs_open2(device_name, io_options, open_flag,
    914 			      0, 0, io_ptr, &fs);
    915         if (retval) {
    916 		com_err (program_name, retval, _("while trying to open %s"),
    917 			 device_name);
    918 		fprintf(stderr,
    919 			_("Couldn't find valid filesystem superblock.\n"));
    920 		exit(1);
    921 	}
    922 	sb = fs->super;
    923 	fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
    924 	if ((sb->s_feature_incompat & !EXT2_TUNE2FS_INCOMPAT) ||
    925 	    (sb->s_feature_ro_compat & !EXT2_TUNE2FS_RO_COMPAT)) {
    926 		fprintf(stderr,
    927 			_("Filesystem %s has unsupported features enabled.\n"),
    928 			device_name);
    929 		exit(1);
    930 	}
    931 	if (print_label) {
    932 		/* For e2label emulation */
    933 		printf("%.*s\n", (int) sizeof(sb->s_volume_name),
    934 		       sb->s_volume_name);
    935 		remove_error_table(&et_ext2_error_table);
    936 		exit(0);
    937 	}
    938 	retval = ext2fs_check_if_mounted(device_name, &mount_flags);
    939 	if (retval) {
    940 		com_err("ext2fs_check_if_mount", retval,
    941 			_("while determining whether %s is mounted."),
    942 			device_name);
    943 		exit(1);
    944 	}
    945 	/* Normally we only need to write out the superblock */
    946 	fs->flags |= EXT2_FLAG_SUPER_ONLY;
    947 
    948 	if (c_flag) {
    949 		sb->s_max_mnt_count = max_mount_count;
    950 		ext2fs_mark_super_dirty(fs);
    951 		printf (_("Setting maximal mount count to %d\n"),
    952 			max_mount_count);
    953 	}
    954 	if (C_flag) {
    955 		sb->s_mnt_count = mount_count;
    956 		ext2fs_mark_super_dirty(fs);
    957 		printf (_("Setting current mount count to %d\n"), mount_count);
    958 	}
    959 	if (e_flag) {
    960 		sb->s_errors = errors;
    961 		ext2fs_mark_super_dirty(fs);
    962 		printf (_("Setting error behavior to %d\n"), errors);
    963 	}
    964 	if (g_flag) {
    965 		sb->s_def_resgid = resgid;
    966 		ext2fs_mark_super_dirty(fs);
    967 		printf (_("Setting reserved blocks gid to %lu\n"), resgid);
    968 	}
    969 	if (i_flag) {
    970 		sb->s_checkinterval = interval;
    971 		ext2fs_mark_super_dirty(fs);
    972 		printf (_("Setting interval between checks to %lu seconds\n"), interval);
    973 	}
    974 	if (m_flag) {
    975 		sb->s_r_blocks_count = e2p_percent(reserved_ratio,
    976 						   sb->s_blocks_count);
    977 		ext2fs_mark_super_dirty(fs);
    978 		printf (_("Setting reserved blocks percentage to %g%% (%u blocks)\n"),
    979 			reserved_ratio, sb->s_r_blocks_count);
    980 	}
    981 	if (r_flag) {
    982 		if (reserved_blocks >= sb->s_blocks_count/2) {
    983 			com_err (program_name, 0,
    984 				 _("reserved blocks count is too big (%lu)"),
    985 				 reserved_blocks);
    986 			exit (1);
    987 		}
    988 		sb->s_r_blocks_count = reserved_blocks;
    989 		ext2fs_mark_super_dirty(fs);
    990 		printf (_("Setting reserved blocks count to %lu\n"),
    991 			reserved_blocks);
    992 	}
    993 	if (s_flag == 1) {
    994 		if (sb->s_feature_ro_compat &
    995 		    EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
    996 			fputs(_("\nThe filesystem already has sparse "
    997 				"superblocks.\n"), stderr);
    998 		else {
    999 			sb->s_feature_ro_compat |=
   1000 				EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
   1001 			sb->s_state &= ~EXT2_VALID_FS;
   1002 			ext2fs_mark_super_dirty(fs);
   1003 			printf(_("\nSparse superblock flag set.  %s"),
   1004 			       _(please_fsck));
   1005 		}
   1006 	}
   1007 	if (s_flag == 0) {
   1008 		fputs(_("\nClearing the sparse superflag not supported.\n"),
   1009 		      stderr);
   1010 		exit(1);
   1011 	}
   1012 	if (T_flag) {
   1013 		sb->s_lastcheck = last_check_time;
   1014 		ext2fs_mark_super_dirty(fs);
   1015 		printf(_("Setting time filesystem last checked to %s\n"),
   1016 		       ctime(&last_check_time));
   1017 	}
   1018 	if (u_flag) {
   1019 		sb->s_def_resuid = resuid;
   1020 		ext2fs_mark_super_dirty(fs);
   1021 		printf (_("Setting reserved blocks uid to %lu\n"), resuid);
   1022 	}
   1023 	if (L_flag) {
   1024 		if (strlen(new_label) > sizeof(sb->s_volume_name))
   1025 			fputs(_("Warning: label too long, truncating.\n"),
   1026 			      stderr);
   1027 		memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
   1028 		strncpy(sb->s_volume_name, new_label,
   1029 			sizeof(sb->s_volume_name));
   1030 		ext2fs_mark_super_dirty(fs);
   1031 	}
   1032 	if (M_flag) {
   1033 		memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
   1034 		strncpy(sb->s_last_mounted, new_last_mounted,
   1035 			sizeof(sb->s_last_mounted));
   1036 		ext2fs_mark_super_dirty(fs);
   1037 	}
   1038 	if (mntopts_cmd)
   1039 		update_mntopts(fs, mntopts_cmd);
   1040 	if (features_cmd)
   1041 		update_feature_set(fs, features_cmd);
   1042 	if (extended_cmd)
   1043 		parse_extended_opts(fs, extended_cmd);
   1044 	if (journal_size || journal_device)
   1045 		add_journal(fs);
   1046 
   1047 	if (U_flag) {
   1048 		if ((strcasecmp(new_UUID, "null") == 0) ||
   1049 		    (strcasecmp(new_UUID, "clear") == 0)) {
   1050 			uuid_clear(sb->s_uuid);
   1051 		} else if (strcasecmp(new_UUID, "time") == 0) {
   1052 			uuid_generate_time(sb->s_uuid);
   1053 		} else if (strcasecmp(new_UUID, "random") == 0) {
   1054 			uuid_generate(sb->s_uuid);
   1055 		} else if (uuid_parse(new_UUID, sb->s_uuid)) {
   1056 			com_err(program_name, 0, _("Invalid UUID format\n"));
   1057 			exit(1);
   1058 		}
   1059 		ext2fs_mark_super_dirty(fs);
   1060 	}
   1061 
   1062 	if (l_flag)
   1063 		list_super (sb);
   1064 	if (stride_set) {
   1065 		sb->s_raid_stride = stride;
   1066 		ext2fs_mark_super_dirty(fs);
   1067 		printf(_("Setting stride size to %d\n"), stride);
   1068 	}
   1069 	if (stripe_width_set) {
   1070 		sb->s_raid_stripe_width = stripe_width;
   1071 		ext2fs_mark_super_dirty(fs);
   1072 		printf(_("Setting stripe width to %d\n"), stripe_width);
   1073 	}
   1074 	remove_error_table(&et_ext2_error_table);
   1075 	return (ext2fs_close (fs) ? 1 : 0);
   1076 }
   1077