tune2fs.c revision 690e693cafa83eadbf7ce80291f53a7e89e9c2cc
1/*
2 * tune2fs.c - Change the file system parameters on an ext2 file system
3 *
4 * Copyright (C) 1992, 1993, 1994  Remy Card <card@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@duteca.et.tudelft.nl)
22 *                F.W. ten Wolde (franky@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@tirka.gun.de)
26 */
27
28#define _XOPEN_SOURCE /* 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
35extern char *optarg;
36extern 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
58const char * program_name = "tune2fs";
59char * device_name;
60char * new_label, *new_last_mounted, *new_UUID;
61char * io_options;
62static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag;
63static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag;
64static time_t last_check_time;
65static int print_label;
66static int max_mount_count, mount_count, mount_flags;
67static unsigned long interval, reserved_ratio, reserved_blocks;
68static unsigned long resgid, resuid;
69static unsigned short errors;
70static int open_flag;
71static char *features_cmd;
72static char *mntopts_cmd;
73
74int journal_size, journal_flags;
75char *journal_device;
76
77static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n");
78
79void do_findfs(int argc, char **argv);
80
81static void usage(void)
82{
83	fprintf(stderr,
84		_("Usage: %s [-c max-mounts-count] [-e errors-behavior] "
85		  "[-g group]\n"
86		  "\t[-i interval[d|m|w]] [-j] [-J journal-options]\n"
87		  "\t[-l] [-s sparse-flag] [-m reserved-blocks-percent]\n"
88		  "\t[-o [^]mount-options[,...]] [-r reserved-blocks-count]\n"
89		  "\t[-u user] [-C mount-count] [-L volume-label] "
90		  "[-M last-mounted-dir]\n"
91		  "\t[-O [^]feature[,...]] [-T last-check-time] [-U UUID]"
92		  " device\n"), program_name);
93	exit (1);
94}
95
96static __u32 ok_features[3] = {
97	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
98		EXT2_FEATURE_COMPAT_DIR_INDEX,	/* Compat */
99	EXT2_FEATURE_INCOMPAT_FILETYPE,		/* Incompat */
100	EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER	/* R/O compat */
101};
102
103/*
104 * Remove an external journal from the filesystem
105 */
106static void remove_journal_device(ext2_filsys fs)
107{
108	char		*journal_path;
109	ext2_filsys	jfs;
110	char		buf[1024];
111	journal_superblock_t	*jsb;
112	int		i, nr_users;
113	errcode_t	retval;
114	int		commit_remove_journal = 0;
115	io_manager	io_ptr;
116
117	if (f_flag)
118		commit_remove_journal = 1; /* force removal even if error */
119
120	uuid_unparse(fs->super->s_journal_uuid, buf);
121	journal_path = blkid_get_devname(NULL, "UUID", buf);
122
123	if (!journal_path) {
124		journal_path =
125			ext2fs_find_block_device(fs->super->s_journal_dev);
126		if (!journal_path)
127			return;
128	}
129
130#ifdef CONFIG_TESTIO_DEBUG
131	io_ptr = test_io_manager;
132	test_io_backing_manager = unix_io_manager;
133#else
134	io_ptr = unix_io_manager;
135#endif
136	retval = ext2fs_open(journal_path, EXT2_FLAG_RW|
137			     EXT2_FLAG_JOURNAL_DEV_OK, 0,
138			     fs->blocksize, io_ptr, &jfs);
139	if (retval) {
140		com_err(program_name, retval,
141			_("while trying to open external journal"));
142		goto no_valid_journal;
143	}
144	if (!(jfs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
145		fprintf(stderr, _("%s is not a journal device.\n"),
146			journal_path);
147		goto no_valid_journal;
148	}
149
150	/* Get the journal superblock */
151	if ((retval = io_channel_read_blk(jfs->io, 1, -1024, buf))) {
152		com_err(program_name, retval,
153			_("while reading journal superblock"));
154		goto no_valid_journal;
155	}
156
157	jsb = (journal_superblock_t *) buf;
158	if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
159	    (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) {
160		fputs(_("Journal superblock not found!\n"), stderr);
161		goto no_valid_journal;
162	}
163
164	/* Find the filesystem UUID */
165	nr_users = ntohl(jsb->s_nr_users);
166	for (i=0; i < nr_users; i++) {
167		if (memcmp(fs->super->s_uuid,
168			   &jsb->s_users[i*16], 16) == 0)
169			break;
170	}
171	if (i >= nr_users) {
172		fputs(_("Filesystem's UUID not found on journal device.\n"),
173		      stderr);
174		commit_remove_journal = 1;
175		goto no_valid_journal;
176	}
177	nr_users--;
178	for (i=0; i < nr_users; i++)
179		memcpy(&jsb->s_users[i*16], &jsb->s_users[(i+1)*16], 16);
180	jsb->s_nr_users = htonl(nr_users);
181
182	/* Write back the journal superblock */
183	if ((retval = io_channel_write_blk(jfs->io, 1, -1024, buf))) {
184		com_err(program_name, retval,
185			"while writing journal superblock.");
186		goto no_valid_journal;
187	}
188
189	commit_remove_journal = 1;
190
191no_valid_journal:
192	if (commit_remove_journal == 0) {
193		fputs(_("Journal NOT removed\n"), stderr);
194		exit(1);
195	}
196	fs->super->s_journal_dev = 0;
197	uuid_clear(fs->super->s_journal_uuid);
198	ext2fs_mark_super_dirty(fs);
199	fputs(_("Journal removed\n"), stdout);
200	free(journal_path);
201}
202
203/* Helper function for remove_journal_inode */
204static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
205			       int blockcnt EXT2FS_ATTR((unused)),
206			       void *private EXT2FS_ATTR((unused)))
207{
208	blk_t	block;
209	int	group;
210
211	block = *blocknr;
212	ext2fs_unmark_block_bitmap(fs->block_map,block);
213	group = ext2fs_group_of_blk(fs, block);
214	fs->group_desc[group].bg_free_blocks_count++;
215	fs->super->s_free_blocks_count++;
216	return 0;
217}
218
219/*
220 * Remove the journal inode from the filesystem
221 */
222static void remove_journal_inode(ext2_filsys fs)
223{
224	struct ext2_inode	inode;
225	errcode_t		retval;
226	ino_t			ino = fs->super->s_journal_inum;
227
228	retval = ext2fs_read_inode(fs, ino,  &inode);
229	if (retval) {
230		com_err(program_name, retval,
231			_("while reading journal inode"));
232		exit(1);
233	}
234	if (ino == EXT2_JOURNAL_INO) {
235		retval = ext2fs_read_bitmaps(fs);
236		if (retval) {
237			com_err(program_name, retval,
238				_("while reading bitmaps"));
239			exit(1);
240		}
241		retval = ext2fs_block_iterate(fs, ino, 0, NULL,
242					      release_blocks_proc, NULL);
243		if (retval) {
244			com_err(program_name, retval,
245				_("while clearing journal inode"));
246			exit(1);
247		}
248		memset(&inode, 0, sizeof(inode));
249		ext2fs_mark_bb_dirty(fs);
250		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
251	} else
252		inode.i_flags &= ~EXT2_IMMUTABLE_FL;
253	retval = ext2fs_write_inode(fs, ino, &inode);
254	if (retval) {
255		com_err(program_name, retval,
256			_("while writing journal inode"));
257		exit(1);
258	}
259	fs->super->s_journal_inum = 0;
260	ext2fs_mark_super_dirty(fs);
261}
262
263/*
264 * Update the default mount options
265 */
266static void update_mntopts(ext2_filsys fs, char *mntopts)
267{
268	struct ext2_super_block *sb= fs->super;
269
270	if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0)) {
271		fprintf(stderr, _("Invalid mount option set: %s\n"),
272			mntopts);
273		exit(1);
274	}
275	ext2fs_mark_super_dirty(fs);
276}
277
278/*
279 * Update the feature set as provided by the user.
280 */
281static void update_feature_set(ext2_filsys fs, char *features)
282{
283	int sparse, old_sparse, filetype, old_filetype;
284	int journal, old_journal, dxdir, old_dxdir;
285	struct ext2_super_block *sb= fs->super;
286	__u32	old_compat, old_incompat, old_ro_compat;
287
288	old_compat = sb->s_feature_compat;
289	old_ro_compat = sb->s_feature_ro_compat;
290	old_incompat = sb->s_feature_incompat;
291
292	old_sparse = sb->s_feature_ro_compat &
293		EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
294	old_filetype = sb->s_feature_incompat &
295		EXT2_FEATURE_INCOMPAT_FILETYPE;
296	old_journal = sb->s_feature_compat &
297		EXT3_FEATURE_COMPAT_HAS_JOURNAL;
298	old_dxdir = sb->s_feature_compat &
299		EXT2_FEATURE_COMPAT_DIR_INDEX;
300	if (e2p_edit_feature(features, &sb->s_feature_compat,
301			     ok_features)) {
302		fprintf(stderr, _("Invalid filesystem option set: %s\n"),
303			features);
304		exit(1);
305	}
306	sparse = sb->s_feature_ro_compat &
307		EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
308	filetype = sb->s_feature_incompat &
309		EXT2_FEATURE_INCOMPAT_FILETYPE;
310	journal = sb->s_feature_compat &
311		EXT3_FEATURE_COMPAT_HAS_JOURNAL;
312	dxdir = sb->s_feature_compat &
313		EXT2_FEATURE_COMPAT_DIR_INDEX;
314	if (old_journal && !journal) {
315		if ((mount_flags & EXT2_MF_MOUNTED) &&
316		    !(mount_flags & EXT2_MF_READONLY)) {
317			fputs(_("The has_journal flag may only be "
318				"cleared when the filesystem is\n"
319				"unmounted or mounted "
320				"read-only.\n"), stderr);
321			exit(1);
322		}
323		if (sb->s_feature_incompat &
324		    EXT3_FEATURE_INCOMPAT_RECOVER) {
325			fputs(_("The needs_recovery flag is set.  "
326				"Please run e2fsck before clearing\n"
327				"the has_journal flag.\n"), stderr);
328			exit(1);
329		}
330		if (sb->s_journal_inum) {
331			remove_journal_inode(fs);
332		}
333		if (sb->s_journal_dev) {
334			remove_journal_device(fs);
335		}
336	}
337	if (journal && !old_journal) {
338		/*
339		 * If adding a journal flag, let the create journal
340		 * code below handle creating setting the flag and
341		 * creating the journal.  We supply a default size if
342		 * necessary.
343		 */
344		if (!journal_size)
345			journal_size = -1;
346		sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
347	}
348	if (dxdir && !old_dxdir) {
349		if (!sb->s_def_hash_version)
350			sb->s_def_hash_version = EXT2_HASH_TEA;
351		if (uuid_is_null((unsigned char *) sb->s_hash_seed))
352			uuid_generate((unsigned char *) sb->s_hash_seed);
353	}
354
355	if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
356	    (sb->s_feature_compat || sb->s_feature_ro_compat ||
357	     sb->s_feature_incompat))
358		ext2fs_update_dynamic_rev(fs);
359	if ((sparse != old_sparse) ||
360	    (filetype != old_filetype)) {
361		sb->s_state &= ~EXT2_VALID_FS;
362		printf("\n%s\n", _(please_fsck));
363	}
364	if ((old_compat != sb->s_feature_compat) ||
365	    (old_ro_compat != sb->s_feature_ro_compat) ||
366	    (old_incompat != sb->s_feature_incompat))
367		ext2fs_mark_super_dirty(fs);
368}
369
370/*
371 * Add a journal to the filesystem.
372 */
373static void add_journal(ext2_filsys fs)
374{
375	unsigned long journal_blocks;
376	errcode_t	retval;
377	ext2_filsys	jfs;
378	io_manager	io_ptr;
379
380	if (fs->super->s_feature_compat &
381	    EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
382		fputs(_("The filesystem already has a journal.\n"), stderr);
383		goto err;
384	}
385	if (journal_device) {
386		check_plausibility(journal_device);
387		check_mount(journal_device, 0, _("journal"));
388#ifdef CONFIG_TESTIO_DEBUG
389		io_ptr = test_io_manager;
390		test_io_backing_manager = unix_io_manager;
391#else
392		io_ptr = unix_io_manager;
393#endif
394		retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
395				     EXT2_FLAG_JOURNAL_DEV_OK, 0,
396				     fs->blocksize, io_ptr, &jfs);
397		if (retval) {
398			com_err(program_name, retval,
399				_("\n\twhile trying to open journal on %s\n"),
400				journal_device);
401			goto err;
402		}
403		printf(_("Creating journal on device %s: "),
404		       journal_device);
405		fflush(stdout);
406
407		retval = ext2fs_add_journal_device(fs, jfs);
408		ext2fs_close(jfs);
409		if (retval) {
410			com_err (program_name, retval,
411				 _("while adding filesystem to journal on %s"),
412				 journal_device);
413			goto err;
414		}
415		fputs(_("done\n"), stdout);
416	} else if (journal_size) {
417		fputs(_("Creating journal inode: "), stdout);
418		fflush(stdout);
419		journal_blocks = figure_journal_size(journal_size, fs);
420
421		retval = ext2fs_add_journal_inode(fs, journal_blocks,
422						  journal_flags);
423		if (retval) {
424			fprintf(stderr, "\n");
425			com_err(program_name, retval,
426				_("\n\twhile trying to create journal file"));
427			exit(1);
428		} else
429			fputs(_("done\n"), stdout);
430		/*
431		 * If the filesystem wasn't mounted, we need to force
432		 * the block group descriptors out.
433		 */
434		if ((mount_flags & EXT2_MF_MOUNTED) == 0)
435			fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
436	}
437	print_check_message(fs);
438	return;
439
440err:
441	if (journal_device)
442		free(journal_device);
443	exit(1);
444}
445
446
447static void parse_e2label_options(int argc, char ** argv)
448{
449	if ((argc < 2) || (argc > 3)) {
450		fputs(_("Usage: e2label device [newlabel]\n"), stderr);
451		exit(1);
452	}
453	io_options = strchr(argv[1], '?');
454	if (io_options)
455		*io_options++ = 0;
456	device_name = blkid_get_devname(NULL, argv[1], NULL);
457	if (!device_name) {
458		com_err("e2label", 0, _("Unable to resolve '%s'"),
459			argv[1]);
460		exit(1);
461	}
462	if (argc == 3) {
463		open_flag = EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK;
464		L_flag = 1;
465		new_label = argv[2];
466	} else
467		print_label++;
468}
469
470static time_t parse_time(char *str)
471{
472	struct	tm	ts;
473
474	if (strcmp(str, "now") == 0) {
475		return (time(0));
476	}
477	memset(&ts, 0, sizeof(ts));
478#ifdef HAVE_STRPTIME
479	strptime(str, "%Y%m%d%H%M%S", &ts);
480#else
481	sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
482	       &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
483	ts.tm_year -= 1900;
484	ts.tm_mon -= 1;
485	if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
486	    ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
487	    ts.tm_min > 59 || ts.tm_sec > 61)
488		ts.tm_mday = 0;
489#endif
490	if (ts.tm_mday == 0) {
491		com_err(program_name, 0,
492			_("Couldn't parse date/time specifier: %s"),
493			str);
494		usage();
495	}
496	return (mktime(&ts));
497}
498
499static void parse_tune2fs_options(int argc, char **argv)
500{
501	int c;
502	char * tmp;
503	struct group * gr;
504	struct passwd * pw;
505
506	printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
507	while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:J:L:M:O:T:U:")) != EOF)
508		switch (c)
509		{
510			case 'c':
511				max_mount_count = strtol (optarg, &tmp, 0);
512				if (*tmp || max_mount_count > 16000) {
513					com_err (program_name, 0,
514						 _("bad mounts count - %s"),
515						 optarg);
516					usage();
517				}
518				if (max_mount_count == 0)
519					max_mount_count = -1;
520				c_flag = 1;
521				open_flag = EXT2_FLAG_RW;
522				break;
523			case 'C':
524				mount_count = strtoul (optarg, &tmp, 0);
525				if (*tmp || mount_count > 16000) {
526					com_err (program_name, 0,
527						 _("bad mounts count - %s"),
528						 optarg);
529					usage();
530				}
531				C_flag = 1;
532				open_flag = EXT2_FLAG_RW;
533				break;
534			case 'e':
535				if (strcmp (optarg, "continue") == 0)
536					errors = EXT2_ERRORS_CONTINUE;
537				else if (strcmp (optarg, "remount-ro") == 0)
538					errors = EXT2_ERRORS_RO;
539				else if (strcmp (optarg, "panic") == 0)
540					errors = EXT2_ERRORS_PANIC;
541				else {
542					com_err (program_name, 0,
543						 _("bad error behavior - %s"),
544						 optarg);
545					usage();
546				}
547				e_flag = 1;
548				open_flag = EXT2_FLAG_RW;
549				break;
550			case 'f': /* Force */
551				f_flag = 1;
552				break;
553			case 'g':
554				resgid = strtoul (optarg, &tmp, 0);
555				if (*tmp) {
556					gr = getgrnam (optarg);
557					if (gr == NULL)
558						tmp = optarg;
559					else {
560						resgid = gr->gr_gid;
561						*tmp =0;
562					}
563				}
564				if (*tmp) {
565					com_err (program_name, 0,
566						 _("bad gid/group name - %s"),
567						 optarg);
568					usage();
569				}
570				g_flag = 1;
571				open_flag = EXT2_FLAG_RW;
572				break;
573			case 'i':
574				interval = strtoul (optarg, &tmp, 0);
575				switch (*tmp) {
576				case 's':
577					tmp++;
578					break;
579				case '\0':
580				case 'd':
581				case 'D': /* days */
582					interval *= 86400;
583					if (*tmp != '\0')
584						tmp++;
585					break;
586				case 'm':
587				case 'M': /* months! */
588					interval *= 86400 * 30;
589					tmp++;
590					break;
591				case 'w':
592				case 'W': /* weeks */
593					interval *= 86400 * 7;
594					tmp++;
595					break;
596				}
597				if (*tmp || interval > (365 * 86400)) {
598					com_err (program_name, 0,
599						_("bad interval - %s"), optarg);
600					usage();
601				}
602				i_flag = 1;
603				open_flag = EXT2_FLAG_RW;
604				break;
605			case 'j':
606				if (!journal_size)
607					journal_size = -1;
608				open_flag = EXT2_FLAG_RW;
609				break;
610			case 'J':
611				parse_journal_opts(optarg);
612				open_flag = EXT2_FLAG_RW;
613				break;
614			case 'l':
615				l_flag = 1;
616				break;
617			case 'L':
618				new_label = optarg;
619				L_flag = 1;
620				open_flag = EXT2_FLAG_RW |
621					EXT2_FLAG_JOURNAL_DEV_OK;
622				break;
623			case 'm':
624				reserved_ratio = strtoul (optarg, &tmp, 0);
625				if (*tmp || reserved_ratio > 50) {
626					com_err (program_name, 0,
627						 _("bad reserved block ratio - %s"),
628						 optarg);
629					usage();
630				}
631				m_flag = 1;
632				open_flag = EXT2_FLAG_RW;
633				break;
634			case 'M':
635				new_last_mounted = optarg;
636				M_flag = 1;
637				open_flag = EXT2_FLAG_RW;
638				break;
639			case 'o':
640				if (mntopts_cmd) {
641					com_err (program_name, 0,
642					 _("-o may only be specified once"));
643					usage();
644				}
645				mntopts_cmd = optarg;
646				open_flag = EXT2_FLAG_RW;
647				break;
648
649			case 'O':
650				if (features_cmd) {
651					com_err (program_name, 0,
652					 _("-O may only be specified once"));
653					usage();
654				}
655				features_cmd = optarg;
656				open_flag = EXT2_FLAG_RW;
657				break;
658			case 'r':
659				reserved_blocks = strtoul (optarg, &tmp, 0);
660				if (*tmp) {
661					com_err (program_name, 0,
662						 _("bad reserved blocks count - %s"),
663						 optarg);
664					usage();
665				}
666				r_flag = 1;
667				open_flag = EXT2_FLAG_RW;
668				break;
669			case 's':
670				s_flag = atoi(optarg);
671				open_flag = EXT2_FLAG_RW;
672				break;
673			case 'T':
674				T_flag = 1;
675				last_check_time = parse_time(optarg);
676				open_flag = EXT2_FLAG_RW;
677				break;
678			case 'u':
679				resuid = strtoul (optarg, &tmp, 0);
680				if (*tmp) {
681					pw = getpwnam (optarg);
682					if (pw == NULL)
683						tmp = optarg;
684					else {
685						resuid = pw->pw_uid;
686						*tmp = 0;
687					}
688				}
689				if (*tmp) {
690					com_err (program_name, 0,
691						 _("bad uid/user name - %s"),
692						 optarg);
693					usage();
694				}
695				u_flag = 1;
696				open_flag = EXT2_FLAG_RW;
697				break;
698			case 'U':
699				new_UUID = optarg;
700				U_flag = 1;
701				open_flag = EXT2_FLAG_RW |
702					EXT2_FLAG_JOURNAL_DEV_OK;
703				break;
704			default:
705				usage();
706		}
707	if (optind < argc - 1 || optind == argc)
708		usage();
709	if (!open_flag && !l_flag)
710		usage();
711	io_options = strchr(argv[optind], '?');
712	if (io_options)
713		*io_options++ = 0;
714	device_name = blkid_get_devname(NULL, argv[optind], NULL);
715	if (!device_name) {
716		com_err("tune2fs", 0, _("Unable to resolve '%s'"),
717			argv[optind]);
718		exit(1);
719	}
720}
721
722void do_findfs(int argc, char **argv)
723{
724	char	*dev;
725
726	if ((argc != 2) ||
727	    (strncmp(argv[1], "LABEL=", 6) && strncmp(argv[1], "UUID=", 5))) {
728		fprintf(stderr, "Usage: findfs LABEL=<label>|UUID=<uuid>\n");
729		exit(2);
730	}
731	dev = blkid_get_devname(NULL, argv[1], NULL);
732	if (!dev) {
733		com_err("findfs", 0, _("Unable to resolve '%s'"),
734			argv[1]);
735		exit(1);
736	}
737	puts(dev);
738	exit(0);
739}
740
741
742int main (int argc, char ** argv)
743{
744	errcode_t retval;
745	ext2_filsys fs;
746	struct ext2_super_block *sb;
747	io_manager io_ptr;
748
749#ifdef ENABLE_NLS
750	setlocale(LC_MESSAGES, "");
751	setlocale(LC_CTYPE, "");
752	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
753	textdomain(NLS_CAT_NAME);
754#endif
755	if (argc && *argv)
756		program_name = *argv;
757	initialize_ext2_error_table();
758
759	if (strcmp(get_progname(argv[0]), "findfs") == 0)
760		do_findfs(argc, argv);
761	if (strcmp(get_progname(argv[0]), "e2label") == 0)
762		parse_e2label_options(argc, argv);
763	else
764		parse_tune2fs_options(argc, argv);
765
766#ifdef CONFIG_TESTIO_DEBUG
767	io_ptr = test_io_manager;
768	test_io_backing_manager = unix_io_manager;
769#else
770	io_ptr = unix_io_manager;
771#endif
772	retval = ext2fs_open2(device_name, io_options, open_flag,
773			      0, 0, io_ptr, &fs);
774        if (retval) {
775		com_err (program_name, retval, _("while trying to open %s"),
776			 device_name);
777		fprintf(stderr,
778			_("Couldn't find valid filesystem superblock.\n"));
779		exit(1);
780	}
781	sb = fs->super;
782	if (print_label) {
783		/* For e2label emulation */
784		printf("%.*s\n", (int) sizeof(sb->s_volume_name),
785		       sb->s_volume_name);
786		exit(0);
787	}
788	retval = ext2fs_check_if_mounted(device_name, &mount_flags);
789	if (retval) {
790		com_err("ext2fs_check_if_mount", retval,
791			_("while determining whether %s is mounted."),
792			device_name);
793		exit(1);
794	}
795	/* Normally we only need to write out the superblock */
796	fs->flags |= EXT2_FLAG_SUPER_ONLY;
797
798	if (c_flag) {
799		sb->s_max_mnt_count = max_mount_count;
800		ext2fs_mark_super_dirty(fs);
801		printf (_("Setting maximal mount count to %d\n"),
802			max_mount_count);
803	}
804	if (C_flag) {
805		sb->s_mnt_count = mount_count;
806		ext2fs_mark_super_dirty(fs);
807		printf (_("Setting current mount count to %d\n"), mount_count);
808	}
809	if (e_flag) {
810		sb->s_errors = errors;
811		ext2fs_mark_super_dirty(fs);
812		printf (_("Setting error behavior to %d\n"), errors);
813	}
814	if (g_flag) {
815		sb->s_def_resgid = resgid;
816		ext2fs_mark_super_dirty(fs);
817		printf (_("Setting reserved blocks gid to %lu\n"), resgid);
818	}
819	if (i_flag) {
820		sb->s_checkinterval = interval;
821		ext2fs_mark_super_dirty(fs);
822		printf (_("Setting interval between check %lu seconds\n"), interval);
823	}
824	if (m_flag) {
825		sb->s_r_blocks_count = (sb->s_blocks_count / 100)
826			* reserved_ratio;
827		ext2fs_mark_super_dirty(fs);
828		printf (_("Setting reserved blocks percentage to %lu (%u blocks)\n"),
829			reserved_ratio, sb->s_r_blocks_count);
830	}
831	if (r_flag) {
832		if (reserved_blocks >= sb->s_blocks_count) {
833			com_err (program_name, 0,
834				 _("reserved blocks count is too big (%lu)"),
835				 reserved_blocks);
836			exit (1);
837		}
838		sb->s_r_blocks_count = reserved_blocks;
839		ext2fs_mark_super_dirty(fs);
840		printf (_("Setting reserved blocks count to %lu\n"),
841			reserved_blocks);
842	}
843	if (s_flag == 1) {
844		if (sb->s_feature_ro_compat &
845		    EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
846			fputs(_("\nThe filesystem already has sparse "
847				"superblocks.\n"), stderr);
848		else {
849			sb->s_feature_ro_compat |=
850				EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
851			sb->s_state &= ~EXT2_VALID_FS;
852			ext2fs_mark_super_dirty(fs);
853			printf(_("\nSparse superblock flag set.  %s"),
854			       _(please_fsck));
855		}
856	}
857	if (s_flag == 0) {
858		if (!(sb->s_feature_ro_compat &
859		      EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
860			fputs(_("\nThe filesystem already has sparse "
861				"superblocks disabled.\n"), stderr);
862		else {
863			sb->s_feature_ro_compat &=
864				~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
865			sb->s_state &= ~EXT2_VALID_FS;
866			fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
867			ext2fs_mark_super_dirty(fs);
868			printf(_("\nSparse superblock flag cleared.  %s"),
869			       _(please_fsck));
870		}
871	}
872	if (T_flag) {
873		sb->s_lastcheck = last_check_time;
874		ext2fs_mark_super_dirty(fs);
875		printf(_("Setting time filesystem last checked to %s\n"),
876		       ctime(&last_check_time));
877	}
878	if (u_flag) {
879		sb->s_def_resuid = resuid;
880		ext2fs_mark_super_dirty(fs);
881		printf (_("Setting reserved blocks uid to %lu\n"), resuid);
882	}
883	if (L_flag) {
884		if (strlen(new_label) > sizeof(sb->s_volume_name))
885			fputs(_("Warning: label too long, truncating.\n"),
886			      stderr);
887		memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
888		strncpy(sb->s_volume_name, new_label,
889			sizeof(sb->s_volume_name));
890		ext2fs_mark_super_dirty(fs);
891	}
892	if (M_flag) {
893		memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
894		strncpy(sb->s_last_mounted, new_last_mounted,
895			sizeof(sb->s_last_mounted));
896		ext2fs_mark_super_dirty(fs);
897	}
898	if (mntopts_cmd)
899		update_mntopts(fs, mntopts_cmd);
900	if (features_cmd)
901		update_feature_set(fs, features_cmd);
902	if (journal_size || journal_device)
903		add_journal(fs);
904
905	if (U_flag) {
906		if ((strcasecmp(new_UUID, "null") == 0) ||
907		    (strcasecmp(new_UUID, "clear") == 0)) {
908			uuid_clear(sb->s_uuid);
909		} else if (strcasecmp(new_UUID, "time") == 0) {
910			uuid_generate_time(sb->s_uuid);
911		} else if (strcasecmp(new_UUID, "random") == 0) {
912			uuid_generate(sb->s_uuid);
913		} else if (uuid_parse(new_UUID, sb->s_uuid)) {
914			com_err(program_name, 0, _("Invalid UUID format\n"));
915			exit(1);
916		}
917		ext2fs_mark_super_dirty(fs);
918	}
919
920	if (l_flag)
921		list_super (sb);
922	return (ext2fs_close (fs) ? 1 : 0);
923}
924