• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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 
usage(void)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  */
remove_journal_device(ext2_filsys fs)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 */
release_blocks_proc(ext2_filsys fs,blk_t * blocknr,int blockcnt EXT2FS_ATTR ((unused)),void * private EXT2FS_ATTR ((unused)))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  */
remove_journal_inode(ext2_filsys fs)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  */
update_mntopts(ext2_filsys fs,char * mntopts)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  */
update_feature_set(ext2_filsys fs,char * features)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  */
add_journal(ext2_filsys fs)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 
parse_e2label_options(int argc,char ** argv)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 
parse_time(char * str)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 
parse_tune2fs_options(int argc,char ** argv)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 
do_findfs(int argc,char ** argv)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  */
parse_extended_opts(ext2_filsys fs,const char * opts)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 
main(int argc,char ** argv)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