tune2fs.c revision 5027751530980c61e313d265b1367fee90589cf4
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 600 /* for inclusion of strptime() */
29#define _BSD_SOURCE /* for inclusion of strcasecmp() */
30#include "config.h"
31#include <fcntl.h>
32#include <grp.h>
33#ifdef HAVE_GETOPT_H
34#include <getopt.h>
35#else
36extern char *optarg;
37extern int optind;
38#endif
39#include <pwd.h>
40#include <stdio.h>
41#ifdef HAVE_STDLIB_H
42#include <stdlib.h>
43#endif
44#include <string.h>
45#include <time.h>
46#include <unistd.h>
47#include <sys/types.h>
48#include <libgen.h>
49#include <limits.h>
50
51#include "ext2fs/ext2_fs.h"
52#include "ext2fs/ext2fs.h"
53#include "et/com_err.h"
54#include "uuid/uuid.h"
55#include "e2p/e2p.h"
56#include "jfs_user.h"
57#include "util.h"
58#include "blkid/blkid.h"
59#include "quota/mkquota.h"
60
61#include "../version.h"
62#include "nls-enable.h"
63
64#define QOPT_ENABLE	(1)
65#define QOPT_DISABLE	(-1)
66
67extern int ask_yn(const char *string, int def);
68
69const char *program_name = "tune2fs";
70char *device_name;
71char *new_label, *new_last_mounted, *new_UUID;
72char *io_options;
73static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag;
74static int m_flag, M_flag, Q_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag;
75static int I_flag;
76static int clear_mmp;
77static time_t last_check_time;
78static int print_label;
79static int max_mount_count, mount_count, mount_flags;
80static unsigned long interval;
81static blk64_t reserved_blocks;
82static double reserved_ratio;
83static unsigned long resgid, resuid;
84static unsigned short errors;
85static int open_flag;
86static char *features_cmd;
87static char *mntopts_cmd;
88static int stride, stripe_width;
89static int stride_set, stripe_width_set;
90static char *extended_cmd;
91static unsigned long new_inode_size;
92static char *ext_mount_opts;
93static int usrquota, grpquota;
94
95int journal_size, journal_flags;
96char *journal_device;
97
98static struct list_head blk_move_list;
99
100struct blk_move {
101	struct list_head list;
102	blk64_t old_loc;
103	blk64_t new_loc;
104};
105
106
107static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n");
108
109#ifdef CONFIG_BUILD_FINDFS
110void do_findfs(int argc, char **argv);
111#endif
112
113static void usage(void)
114{
115	fprintf(stderr,
116		_("Usage: %s [-c max_mounts_count] [-e errors_behavior] "
117		  "[-g group]\n"
118		  "\t[-i interval[d|m|w]] [-j] [-J journal_options] [-l]\n"
119		  "\t[-m reserved_blocks_percent] "
120		  "[-o [^]mount_options[,...]] [-p mmp_update_interval]\n"
121		  "\t[-r reserved_blocks_count] [-u user] [-C mount_count] "
122		  "[-L volume_label]\n"
123		  "\t[-M last_mounted_dir] [-O [^]feature[,...]]\n"
124		  "\t[-E extended-option[,...]] [-T last_check_time] "
125		  "[-U UUID]\n\t[ -I new_inode_size ] device\n"), program_name);
126	exit(1);
127}
128
129static __u32 ok_features[3] = {
130	/* Compat */
131	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
132		EXT2_FEATURE_COMPAT_DIR_INDEX,
133	/* Incompat */
134	EXT2_FEATURE_INCOMPAT_FILETYPE |
135		EXT3_FEATURE_INCOMPAT_EXTENTS |
136		EXT4_FEATURE_INCOMPAT_FLEX_BG |
137		EXT4_FEATURE_INCOMPAT_MMP,
138	/* R/O compat */
139	EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
140		EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
141		EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
142		EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
143		EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
144		EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER |
145		EXT4_FEATURE_RO_COMPAT_QUOTA
146};
147
148static __u32 clear_ok_features[3] = {
149	/* Compat */
150	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
151		EXT2_FEATURE_COMPAT_RESIZE_INODE |
152		EXT2_FEATURE_COMPAT_DIR_INDEX,
153	/* Incompat */
154	EXT2_FEATURE_INCOMPAT_FILETYPE |
155		EXT4_FEATURE_INCOMPAT_FLEX_BG |
156		EXT4_FEATURE_INCOMPAT_MMP,
157	/* R/O compat */
158	EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
159		EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
160		EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
161		EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
162		EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
163		EXT4_FEATURE_RO_COMPAT_QUOTA
164};
165
166/*
167 * Remove an external journal from the filesystem
168 */
169static int remove_journal_device(ext2_filsys fs)
170{
171	char		*journal_path;
172	ext2_filsys	jfs;
173	char		buf[1024];
174	journal_superblock_t	*jsb;
175	int		i, nr_users;
176	errcode_t	retval;
177	int		commit_remove_journal = 0;
178	io_manager	io_ptr;
179
180	if (f_flag)
181		commit_remove_journal = 1; /* force removal even if error */
182
183	uuid_unparse(fs->super->s_journal_uuid, buf);
184	journal_path = blkid_get_devname(NULL, "UUID", buf);
185
186	if (!journal_path) {
187		journal_path =
188			ext2fs_find_block_device(fs->super->s_journal_dev);
189		if (!journal_path)
190			goto no_valid_journal;
191	}
192
193#ifdef CONFIG_TESTIO_DEBUG
194	if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
195		io_ptr = test_io_manager;
196		test_io_backing_manager = unix_io_manager;
197	} else
198#endif
199		io_ptr = unix_io_manager;
200	retval = ext2fs_open(journal_path, EXT2_FLAG_RW|
201			     EXT2_FLAG_JOURNAL_DEV_OK, 0,
202			     fs->blocksize, io_ptr, &jfs);
203	if (retval) {
204		com_err(program_name, retval,
205			_("while trying to open external journal"));
206		goto no_valid_journal;
207	}
208	if (!(jfs->super->s_feature_incompat &
209	      EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
210		fprintf(stderr, _("%s is not a journal device.\n"),
211			journal_path);
212		goto no_valid_journal;
213	}
214
215	/* Get the journal superblock */
216	if ((retval = io_channel_read_blk64(jfs->io, 1, -1024, buf))) {
217		com_err(program_name, retval,
218			_("while reading journal superblock"));
219		goto no_valid_journal;
220	}
221
222	jsb = (journal_superblock_t *) buf;
223	if ((jsb->s_header.h_magic != (unsigned)ntohl(JFS_MAGIC_NUMBER)) ||
224	    (jsb->s_header.h_blocktype != (unsigned)ntohl(JFS_SUPERBLOCK_V2))) {
225		fputs(_("Journal superblock not found!\n"), stderr);
226		goto no_valid_journal;
227	}
228
229	/* Find the filesystem UUID */
230	nr_users = ntohl(jsb->s_nr_users);
231	for (i = 0; i < nr_users; i++) {
232		if (memcmp(fs->super->s_uuid, &jsb->s_users[i * 16], 16) == 0)
233			break;
234	}
235	if (i >= nr_users) {
236		fputs(_("Filesystem's UUID not found on journal device.\n"),
237		      stderr);
238		commit_remove_journal = 1;
239		goto no_valid_journal;
240	}
241	nr_users--;
242	for (i = 0; i < nr_users; i++)
243		memcpy(&jsb->s_users[i * 16], &jsb->s_users[(i + 1) * 16], 16);
244	jsb->s_nr_users = htonl(nr_users);
245
246	/* Write back the journal superblock */
247	if ((retval = io_channel_write_blk64(jfs->io, 1, -1024, buf))) {
248		com_err(program_name, retval,
249			"while writing journal superblock.");
250		goto no_valid_journal;
251	}
252
253	commit_remove_journal = 1;
254
255no_valid_journal:
256	if (commit_remove_journal == 0) {
257		fputs(_("Cannot locate journal device. It was NOT removed\n"
258			"Use -f option to remove missing journal device.\n"),
259		      stderr);
260		return 1;
261	}
262	fs->super->s_journal_dev = 0;
263	uuid_clear(fs->super->s_journal_uuid);
264	ext2fs_mark_super_dirty(fs);
265	fputs(_("Journal removed\n"), stdout);
266	free(journal_path);
267
268	return 0;
269}
270
271/* Helper function for remove_journal_inode */
272static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr,
273			       e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
274			       blk64_t ref_block EXT2FS_ATTR((unused)),
275			       int ref_offset EXT2FS_ATTR((unused)),
276			       void *private EXT2FS_ATTR((unused)))
277{
278	blk64_t	block;
279	int	group;
280
281	block = *blocknr;
282	ext2fs_unmark_block_bitmap2(fs->block_map, block);
283	group = ext2fs_group_of_blk2(fs, block);
284	ext2fs_bg_free_blocks_count_set(fs, group, ext2fs_bg_free_blocks_count(fs, group) + 1);
285	ext2fs_group_desc_csum_set(fs, group);
286	ext2fs_free_blocks_count_add(fs->super, EXT2FS_CLUSTER_RATIO(fs));
287	return 0;
288}
289
290/*
291 * Remove the journal inode from the filesystem
292 */
293static errcode_t remove_journal_inode(ext2_filsys fs)
294{
295	struct ext2_inode	inode;
296	errcode_t		retval;
297	ino_t			ino = fs->super->s_journal_inum;
298
299	retval = ext2fs_read_inode(fs, ino,  &inode);
300	if (retval) {
301		com_err(program_name, retval,
302			_("while reading journal inode"));
303		return retval;
304	}
305	if (ino == EXT2_JOURNAL_INO) {
306		retval = ext2fs_read_bitmaps(fs);
307		if (retval) {
308			com_err(program_name, retval,
309				_("while reading bitmaps"));
310			return retval;
311		}
312		retval = ext2fs_block_iterate3(fs, ino,
313					       BLOCK_FLAG_READ_ONLY, NULL,
314					       release_blocks_proc, NULL);
315		if (retval) {
316			com_err(program_name, retval,
317				_("while clearing journal inode"));
318			return retval;
319		}
320		memset(&inode, 0, sizeof(inode));
321		ext2fs_mark_bb_dirty(fs);
322		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
323	} else
324		inode.i_flags &= ~EXT2_IMMUTABLE_FL;
325	retval = ext2fs_write_inode(fs, ino, &inode);
326	if (retval) {
327		com_err(program_name, retval,
328			_("while writing journal inode"));
329		return retval;
330	}
331	fs->super->s_journal_inum = 0;
332	ext2fs_mark_super_dirty(fs);
333
334	return 0;
335}
336
337/*
338 * Update the default mount options
339 */
340static int update_mntopts(ext2_filsys fs, char *mntopts)
341{
342	struct ext2_super_block *sb = fs->super;
343
344	if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0)) {
345		fprintf(stderr, _("Invalid mount option set: %s\n"),
346			mntopts);
347		return 1;
348	}
349	ext2fs_mark_super_dirty(fs);
350
351	return 0;
352}
353
354static void request_fsck_afterwards(ext2_filsys fs)
355{
356	static int requested = 0;
357
358	if (requested++)
359		return;
360	fs->super->s_state &= ~EXT2_VALID_FS;
361	printf("\n%s\n", _(please_fsck));
362	if (mount_flags & EXT2_MF_READONLY)
363		printf(_("(and reboot afterwards!)\n"));
364}
365
366/*
367 * Update the feature set as provided by the user.
368 */
369static int update_feature_set(ext2_filsys fs, char *features)
370{
371	struct ext2_super_block *sb = fs->super;
372	struct ext2_group_desc *gd;
373	__u32		old_features[3];
374	int		i, type_err;
375	unsigned int	mask_err;
376
377#define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \
378				((&sb->s_feature_compat)[(type)] & (mask)))
379#define FEATURE_OFF(type, mask) ((old_features[(type)] & (mask)) && \
380				 !((&sb->s_feature_compat)[(type)] & (mask)))
381#define FEATURE_CHANGED(type, mask) ((mask) & \
382		     (old_features[(type)] ^ (&sb->s_feature_compat)[(type)]))
383
384	old_features[E2P_FEATURE_COMPAT] = sb->s_feature_compat;
385	old_features[E2P_FEATURE_INCOMPAT] = sb->s_feature_incompat;
386	old_features[E2P_FEATURE_RO_INCOMPAT] = sb->s_feature_ro_compat;
387
388	if (e2p_edit_feature2(features, &sb->s_feature_compat,
389			      ok_features, clear_ok_features,
390			      &type_err, &mask_err)) {
391		if (!mask_err)
392			fprintf(stderr,
393				_("Invalid filesystem option set: %s\n"),
394				features);
395		else if (type_err & E2P_FEATURE_NEGATE_FLAG)
396			fprintf(stderr, _("Clearing filesystem feature '%s' "
397					  "not supported.\n"),
398				e2p_feature2string(type_err &
399						   E2P_FEATURE_TYPE_MASK,
400						   mask_err));
401		else
402			fprintf(stderr, _("Setting filesystem feature '%s' "
403					  "not supported.\n"),
404				e2p_feature2string(type_err, mask_err));
405		return 1;
406	}
407
408	if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
409		if ((mount_flags & EXT2_MF_MOUNTED) &&
410		    !(mount_flags & EXT2_MF_READONLY)) {
411			fputs(_("The has_journal feature may only be "
412				"cleared when the filesystem is\n"
413				"unmounted or mounted "
414				"read-only.\n"), stderr);
415			return 1;
416		}
417		if (sb->s_feature_incompat &
418		    EXT3_FEATURE_INCOMPAT_RECOVER) {
419			fputs(_("The needs_recovery flag is set.  "
420				"Please run e2fsck before clearing\n"
421				"the has_journal flag.\n"), stderr);
422			return 1;
423		}
424		if (sb->s_journal_inum) {
425			if (remove_journal_inode(fs))
426				return 1;
427		}
428		if (sb->s_journal_dev) {
429			if (remove_journal_device(fs))
430				return 1;
431		}
432	}
433	if (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP)) {
434		int error;
435
436		if ((mount_flags & EXT2_MF_MOUNTED) ||
437		    (mount_flags & EXT2_MF_READONLY)) {
438			fputs(_("The multiple mount protection feature can't\n"
439				"be set if the filesystem is mounted or\n"
440				"read-only.\n"), stderr);
441			return 1;
442		}
443
444		error = ext2fs_mmp_init(fs);
445		if (error) {
446			fputs(_("\nError while enabling multiple mount "
447				"protection feature."), stderr);
448			return 1;
449		}
450
451		/*
452		 * We want to update group desc with the new free blocks count
453		 */
454		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
455
456		printf(_("Multiple mount protection has been enabled "
457			 "with update interval %ds.\n"),
458		       sb->s_mmp_update_interval);
459	}
460
461	if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP)) {
462		int error;
463
464		if (mount_flags & EXT2_MF_READONLY) {
465			fputs(_("The multiple mount protection feature cannot\n"
466				"be disabled if the filesystem is readonly.\n"),
467				stderr);
468			return 1;
469		}
470
471		error = ext2fs_read_bitmaps(fs);
472		if (error) {
473			fputs(_("Error while reading bitmaps\n"), stderr);
474			return 1;
475		}
476
477		error = ext2fs_mmp_read(fs, sb->s_mmp_block, NULL);
478		if (error) {
479			struct mmp_struct *mmp_cmp = fs->mmp_cmp;
480
481			if (error == EXT2_ET_MMP_MAGIC_INVALID)
482				printf(_("Magic number in MMP block does not "
483					 "match. expected: %x, actual: %x\n"),
484					 EXT4_MMP_MAGIC, mmp_cmp->mmp_magic);
485			else
486				com_err(program_name, error,
487					_("while reading MMP block."));
488			goto mmp_error;
489		}
490
491		/* We need to force out the group descriptors as well */
492		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
493		ext2fs_block_alloc_stats(fs, sb->s_mmp_block, -1);
494mmp_error:
495		sb->s_mmp_block = 0;
496		sb->s_mmp_update_interval = 0;
497	}
498
499	if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
500		/*
501		 * If adding a journal flag, let the create journal
502		 * code below handle setting the flag and creating the
503		 * journal.  We supply a default size if necessary.
504		 */
505		if (!journal_size)
506			journal_size = -1;
507		sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
508	}
509
510	if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX)) {
511		if (!sb->s_def_hash_version)
512			sb->s_def_hash_version = EXT2_HASH_HALF_MD4;
513		if (uuid_is_null((unsigned char *) sb->s_hash_seed))
514			uuid_generate((unsigned char *) sb->s_hash_seed);
515	}
516
517	if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG)) {
518		if (ext2fs_check_desc(fs)) {
519			fputs(_("Clearing the flex_bg flag would "
520				"cause the the filesystem to be\n"
521				"inconsistent.\n"), stderr);
522			return 1;
523		}
524	}
525
526	if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
527			    EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) {
528		if ((mount_flags & EXT2_MF_MOUNTED) &&
529		    !(mount_flags & EXT2_MF_READONLY)) {
530			fputs(_("The huge_file feature may only be "
531				"cleared when the filesystem is\n"
532				"unmounted or mounted "
533				"read-only.\n"), stderr);
534			return 1;
535		}
536	}
537
538	if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
539		       EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
540		for (i = 0; i < fs->group_desc_count; i++) {
541			gd = ext2fs_group_desc(fs, fs->group_desc, i);
542			gd->bg_itable_unused = 0;
543			gd->bg_flags = EXT2_BG_INODE_ZEROED;
544			ext2fs_group_desc_csum_set(fs, i);
545		}
546		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
547	}
548
549	if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
550			EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
551		for (i = 0; i < fs->group_desc_count; i++) {
552			gd = ext2fs_group_desc(fs, fs->group_desc, i);
553			if ((gd->bg_flags & EXT2_BG_INODE_ZEROED) == 0) {
554				/*
555				 * XXX what we really should do is zap
556				 * uninitialized inode tables instead.
557				 */
558				request_fsck_afterwards(fs);
559				break;
560			}
561			gd->bg_itable_unused = 0;
562			gd->bg_flags = 0;
563			gd->bg_checksum = 0;
564		}
565		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
566	}
567
568	if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
569				EXT4_FEATURE_RO_COMPAT_QUOTA)) {
570		/*
571		 * Set the Q_flag here and handle the quota options in the code
572		 * below.
573		 */
574		if (!Q_flag) {
575			Q_flag = 1;
576			/* Enable both user quota and group quota by default */
577			usrquota = QOPT_ENABLE;
578			grpquota = QOPT_ENABLE;
579		}
580		sb->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA;
581	}
582
583	if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
584				EXT4_FEATURE_RO_COMPAT_QUOTA)) {
585		/*
586		 * Set the Q_flag here and handle the quota options in the code
587		 * below.
588		 */
589		if (Q_flag)
590			fputs(_("\nWarning: '^quota' option overrides '-Q'"
591				"arguments.\n"), stderr);
592		Q_flag = 1;
593		/* Disable both user quota and group quota by default */
594		usrquota = QOPT_DISABLE;
595		grpquota = QOPT_DISABLE;
596	}
597
598	if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
599	    (sb->s_feature_compat || sb->s_feature_ro_compat ||
600	     sb->s_feature_incompat))
601		ext2fs_update_dynamic_rev(fs);
602
603	if (FEATURE_CHANGED(E2P_FEATURE_RO_INCOMPAT,
604			    EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
605	    FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
606			EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
607	    FEATURE_CHANGED(E2P_FEATURE_INCOMPAT,
608			    EXT2_FEATURE_INCOMPAT_FILETYPE) ||
609	    FEATURE_CHANGED(E2P_FEATURE_COMPAT,
610			    EXT2_FEATURE_COMPAT_RESIZE_INODE) ||
611	    FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
612			EXT2_FEATURE_RO_COMPAT_LARGE_FILE))
613		request_fsck_afterwards(fs);
614
615	if ((old_features[E2P_FEATURE_COMPAT] != sb->s_feature_compat) ||
616	    (old_features[E2P_FEATURE_INCOMPAT] != sb->s_feature_incompat) ||
617	    (old_features[E2P_FEATURE_RO_INCOMPAT] != sb->s_feature_ro_compat))
618		ext2fs_mark_super_dirty(fs);
619
620	return 0;
621}
622
623/*
624 * Add a journal to the filesystem.
625 */
626static int add_journal(ext2_filsys fs)
627{
628	unsigned long journal_blocks;
629	errcode_t	retval;
630	ext2_filsys	jfs;
631	io_manager	io_ptr;
632
633	if (fs->super->s_feature_compat &
634	    EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
635		fputs(_("The filesystem already has a journal.\n"), stderr);
636		goto err;
637	}
638	if (journal_device) {
639		check_plausibility(journal_device);
640		check_mount(journal_device, 0, _("journal"));
641#ifdef CONFIG_TESTIO_DEBUG
642		if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
643			io_ptr = test_io_manager;
644			test_io_backing_manager = unix_io_manager;
645		} else
646#endif
647			io_ptr = unix_io_manager;
648		retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
649				     EXT2_FLAG_JOURNAL_DEV_OK, 0,
650				     fs->blocksize, io_ptr, &jfs);
651		if (retval) {
652			com_err(program_name, retval,
653				_("\n\twhile trying to open journal on %s\n"),
654				journal_device);
655			goto err;
656		}
657		printf(_("Creating journal on device %s: "),
658		       journal_device);
659		fflush(stdout);
660
661		retval = ext2fs_add_journal_device(fs, jfs);
662		ext2fs_close(jfs);
663		if (retval) {
664			com_err(program_name, retval,
665				_("while adding filesystem to journal on %s"),
666				journal_device);
667			goto err;
668		}
669		fputs(_("done\n"), stdout);
670	} else if (journal_size) {
671		fputs(_("Creating journal inode: "), stdout);
672		fflush(stdout);
673		journal_blocks = figure_journal_size(journal_size, fs);
674
675		retval = ext2fs_add_journal_inode(fs, journal_blocks,
676						  journal_flags);
677		if (retval) {
678			fprintf(stderr, "\n");
679			com_err(program_name, retval,
680				_("\n\twhile trying to create journal file"));
681			return retval;
682		} else
683			fputs(_("done\n"), stdout);
684		/*
685		 * If the filesystem wasn't mounted, we need to force
686		 * the block group descriptors out.
687		 */
688		if ((mount_flags & EXT2_MF_MOUNTED) == 0)
689			fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
690	}
691	print_check_message(fs->super->s_max_mnt_count,
692			    fs->super->s_checkinterval);
693	return 0;
694
695err:
696	free(journal_device);
697	return 1;
698}
699
700void handle_quota_options(ext2_filsys fs)
701{
702	quota_ctx_t qctx;
703	ext2_ino_t qf_ino;
704
705	if (!usrquota && !grpquota)
706		/* Nothing to do. */
707		return;
708
709	quota_init_context(&qctx, fs, -1);
710
711	if (usrquota == QOPT_ENABLE || grpquota == QOPT_ENABLE)
712		quota_compute_usage(qctx);
713
714	if (usrquota == QOPT_ENABLE && !fs->super->s_usr_quota_inum) {
715		if ((qf_ino = quota_file_exists(fs, USRQUOTA,
716						QFMT_VFS_V1)) > 0)
717			quota_update_limits(qctx, qf_ino, USRQUOTA);
718		quota_write_inode(qctx, USRQUOTA);
719	} else if (usrquota == QOPT_DISABLE) {
720		quota_remove_inode(fs, USRQUOTA);
721	}
722
723	if (grpquota == QOPT_ENABLE && !fs->super->s_grp_quota_inum) {
724		if ((qf_ino = quota_file_exists(fs, GRPQUOTA,
725						QFMT_VFS_V1)) > 0)
726			quota_update_limits(qctx, qf_ino, GRPQUOTA);
727		quota_write_inode(qctx, GRPQUOTA);
728	} else if (grpquota == QOPT_DISABLE) {
729		quota_remove_inode(fs, GRPQUOTA);
730	}
731
732	quota_release_context(&qctx);
733
734	if ((usrquota == QOPT_ENABLE) || (grpquota == QOPT_ENABLE)) {
735		fs->super->s_feature_ro_compat |= EXT4_FEATURE_RO_COMPAT_QUOTA;
736		ext2fs_mark_super_dirty(fs);
737	} else if ((usrquota == QOPT_DISABLE) && (grpquota == QOPT_DISABLE)) {
738		fs->super->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA;
739		ext2fs_mark_super_dirty(fs);
740	}
741
742	return;
743}
744
745void parse_quota_opts(const char *opts)
746{
747	char	*buf, *token, *next, *p;
748	int	len;
749
750	len = strlen(opts);
751	buf = malloc(len+1);
752	if (!buf) {
753		fputs(_("Couldn't allocate memory to parse quota "
754			"options!\n"), stderr);
755		exit(1);
756	}
757	strcpy(buf, opts);
758	for (token = buf; token && *token; token = next) {
759		p = strchr(token, ',');
760		next = 0;
761		if (p) {
762			*p = 0;
763			next = p+1;
764		}
765
766		if (strcmp(token, "usrquota") == 0) {
767			usrquota = QOPT_ENABLE;
768		} else if (strcmp(token, "^usrquota") == 0) {
769			usrquota = QOPT_DISABLE;
770		} else if (strcmp(token, "grpquota") == 0) {
771			grpquota = QOPT_ENABLE;
772		} else if (strcmp(token, "^grpquota") == 0) {
773			grpquota = QOPT_DISABLE;
774		} else {
775			fputs(_("\nBad quota options specified.\n\n"
776				"Following valid quota options are available "
777				"(pass by separating with comma):\n"
778				"\t[^]usrquota\n"
779				"\t[^]grpquota\n"
780				"\n\n"), stderr);
781			free(buf);
782			exit(1);
783		}
784	}
785	free(buf);
786}
787
788
789
790static void parse_e2label_options(int argc, char ** argv)
791{
792	if ((argc < 2) || (argc > 3)) {
793		fputs(_("Usage: e2label device [newlabel]\n"), stderr);
794		exit(1);
795	}
796	io_options = strchr(argv[1], '?');
797	if (io_options)
798		*io_options++ = 0;
799	device_name = blkid_get_devname(NULL, argv[1], NULL);
800	if (!device_name) {
801		com_err("e2label", 0, _("Unable to resolve '%s'"),
802			argv[1]);
803		exit(1);
804	}
805	open_flag = EXT2_FLAG_JOURNAL_DEV_OK;
806	if (argc == 3) {
807		open_flag |= EXT2_FLAG_RW;
808		L_flag = 1;
809		new_label = argv[2];
810	} else
811		print_label++;
812}
813
814static time_t parse_time(char *str)
815{
816	struct	tm	ts;
817
818	if (strcmp(str, "now") == 0) {
819		return (time(0));
820	}
821	memset(&ts, 0, sizeof(ts));
822#ifdef HAVE_STRPTIME
823	strptime(str, "%Y%m%d%H%M%S", &ts);
824#else
825	sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
826	       &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
827	ts.tm_year -= 1900;
828	ts.tm_mon -= 1;
829	if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
830	    ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
831	    ts.tm_min > 59 || ts.tm_sec > 61)
832		ts.tm_mday = 0;
833#endif
834	if (ts.tm_mday == 0) {
835		com_err(program_name, 0,
836			_("Couldn't parse date/time specifier: %s"),
837			str);
838		usage();
839	}
840	ts.tm_isdst = -1;
841	return (mktime(&ts));
842}
843
844static void parse_tune2fs_options(int argc, char **argv)
845{
846	int c;
847	char *tmp;
848	struct group *gr;
849	struct passwd *pw;
850
851	open_flag = 0;
852
853	printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
854	while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:E:I:J:L:M:O:Q:T:U:")) != EOF)
855		switch (c) {
856		case 'c':
857			max_mount_count = strtol(optarg, &tmp, 0);
858			if (*tmp || max_mount_count > 16000) {
859				com_err(program_name, 0,
860					_("bad mounts count - %s"),
861					optarg);
862				usage();
863			}
864			if (max_mount_count == 0)
865				max_mount_count = -1;
866			c_flag = 1;
867			open_flag = EXT2_FLAG_RW;
868			break;
869		case 'C':
870			mount_count = strtoul(optarg, &tmp, 0);
871			if (*tmp || mount_count > 16000) {
872				com_err(program_name, 0,
873					_("bad mounts count - %s"),
874					optarg);
875				usage();
876			}
877			C_flag = 1;
878			open_flag = EXT2_FLAG_RW;
879			break;
880		case 'e':
881			if (strcmp(optarg, "continue") == 0)
882				errors = EXT2_ERRORS_CONTINUE;
883			else if (strcmp(optarg, "remount-ro") == 0)
884				errors = EXT2_ERRORS_RO;
885			else if (strcmp(optarg, "panic") == 0)
886				errors = EXT2_ERRORS_PANIC;
887			else {
888				com_err(program_name, 0,
889					_("bad error behavior - %s"),
890					optarg);
891				usage();
892			}
893			e_flag = 1;
894			open_flag = EXT2_FLAG_RW;
895			break;
896		case 'E':
897			extended_cmd = optarg;
898			open_flag |= EXT2_FLAG_RW;
899			break;
900		case 'f': /* Force */
901			f_flag = 1;
902			break;
903		case 'g':
904			resgid = strtoul(optarg, &tmp, 0);
905			if (*tmp) {
906				gr = getgrnam(optarg);
907				if (gr == NULL)
908					tmp = optarg;
909				else {
910					resgid = gr->gr_gid;
911					*tmp = 0;
912				}
913			}
914			if (*tmp) {
915				com_err(program_name, 0,
916					_("bad gid/group name - %s"),
917					optarg);
918				usage();
919			}
920			g_flag = 1;
921			open_flag = EXT2_FLAG_RW;
922			break;
923		case 'i':
924			interval = strtoul(optarg, &tmp, 0);
925			switch (*tmp) {
926			case 's':
927				tmp++;
928				break;
929			case '\0':
930			case 'd':
931			case 'D': /* days */
932				interval *= 86400;
933				if (*tmp != '\0')
934					tmp++;
935				break;
936			case 'm':
937			case 'M': /* months! */
938				interval *= 86400 * 30;
939				tmp++;
940				break;
941			case 'w':
942			case 'W': /* weeks */
943				interval *= 86400 * 7;
944				tmp++;
945				break;
946			}
947			if (*tmp) {
948				com_err(program_name, 0,
949					_("bad interval - %s"), optarg);
950				usage();
951			}
952			i_flag = 1;
953			open_flag = EXT2_FLAG_RW;
954			break;
955		case 'j':
956			if (!journal_size)
957				journal_size = -1;
958			open_flag = EXT2_FLAG_RW;
959			break;
960		case 'J':
961			parse_journal_opts(optarg);
962			open_flag = EXT2_FLAG_RW;
963			break;
964		case 'l':
965			l_flag = 1;
966			break;
967		case 'L':
968			new_label = optarg;
969			L_flag = 1;
970			open_flag |= EXT2_FLAG_RW |
971				EXT2_FLAG_JOURNAL_DEV_OK;
972			break;
973		case 'm':
974			reserved_ratio = strtod(optarg, &tmp);
975			if (*tmp || reserved_ratio > 50 ||
976			    reserved_ratio < 0) {
977				com_err(program_name, 0,
978					_("bad reserved block ratio - %s"),
979					optarg);
980				usage();
981			}
982			m_flag = 1;
983			open_flag = EXT2_FLAG_RW;
984			break;
985		case 'M':
986			new_last_mounted = optarg;
987			M_flag = 1;
988			open_flag = EXT2_FLAG_RW;
989			break;
990		case 'o':
991			if (mntopts_cmd) {
992				com_err(program_name, 0,
993					_("-o may only be specified once"));
994				usage();
995			}
996			mntopts_cmd = optarg;
997			open_flag = EXT2_FLAG_RW;
998			break;
999		case 'O':
1000			if (features_cmd) {
1001				com_err(program_name, 0,
1002					_("-O may only be specified once"));
1003				usage();
1004			}
1005			features_cmd = optarg;
1006			open_flag = EXT2_FLAG_RW;
1007			break;
1008		case 'Q':
1009			Q_flag = 1;
1010			parse_quota_opts(optarg);
1011			open_flag = EXT2_FLAG_RW;
1012			break;
1013		case 'r':
1014			reserved_blocks = strtoul(optarg, &tmp, 0);
1015			if (*tmp) {
1016				com_err(program_name, 0,
1017					_("bad reserved blocks count - %s"),
1018					optarg);
1019				usage();
1020			}
1021			r_flag = 1;
1022			open_flag = EXT2_FLAG_RW;
1023			break;
1024		case 's': /* Deprecated */
1025			s_flag = atoi(optarg);
1026			open_flag = EXT2_FLAG_RW;
1027			break;
1028		case 'T':
1029			T_flag = 1;
1030			last_check_time = parse_time(optarg);
1031			open_flag = EXT2_FLAG_RW;
1032			break;
1033		case 'u':
1034				resuid = strtoul(optarg, &tmp, 0);
1035				if (*tmp) {
1036					pw = getpwnam(optarg);
1037					if (pw == NULL)
1038						tmp = optarg;
1039					else {
1040						resuid = pw->pw_uid;
1041						*tmp = 0;
1042					}
1043				}
1044				if (*tmp) {
1045					com_err(program_name, 0,
1046						_("bad uid/user name - %s"),
1047						optarg);
1048					usage();
1049				}
1050				u_flag = 1;
1051				open_flag = EXT2_FLAG_RW;
1052				break;
1053		case 'U':
1054			new_UUID = optarg;
1055			U_flag = 1;
1056			open_flag = EXT2_FLAG_RW |
1057				EXT2_FLAG_JOURNAL_DEV_OK;
1058			break;
1059		case 'I':
1060			new_inode_size = strtoul(optarg, &tmp, 0);
1061			if (*tmp) {
1062				com_err(program_name, 0,
1063					_("bad inode size - %s"),
1064					optarg);
1065				usage();
1066			}
1067			if (!((new_inode_size &
1068			       (new_inode_size - 1)) == 0)) {
1069				com_err(program_name, 0,
1070					_("Inode size must be a "
1071					  "power of two- %s"),
1072					optarg);
1073				usage();
1074			}
1075			open_flag = EXT2_FLAG_RW;
1076			I_flag = 1;
1077			break;
1078		default:
1079			usage();
1080		}
1081	if (optind < argc - 1 || optind == argc)
1082		usage();
1083	if (!open_flag && !l_flag)
1084		usage();
1085	io_options = strchr(argv[optind], '?');
1086	if (io_options)
1087		*io_options++ = 0;
1088	device_name = blkid_get_devname(NULL, argv[optind], NULL);
1089	if (!device_name) {
1090		com_err("tune2fs", 0, _("Unable to resolve '%s'"),
1091			argv[optind]);
1092		exit(1);
1093	}
1094}
1095
1096#ifdef CONFIG_BUILD_FINDFS
1097void do_findfs(int argc, char **argv)
1098{
1099	char	*dev;
1100
1101	if ((argc != 2) ||
1102	    (strncmp(argv[1], "LABEL=", 6) && strncmp(argv[1], "UUID=", 5))) {
1103		fprintf(stderr, "Usage: findfs LABEL=<label>|UUID=<uuid>\n");
1104		exit(2);
1105	}
1106	dev = blkid_get_devname(NULL, argv[1], NULL);
1107	if (!dev) {
1108		com_err("findfs", 0, _("Unable to resolve '%s'"),
1109			argv[1]);
1110		exit(1);
1111	}
1112	puts(dev);
1113	exit(0);
1114}
1115#endif
1116
1117static int parse_extended_opts(ext2_filsys fs, const char *opts)
1118{
1119	char	*buf, *token, *next, *p, *arg;
1120	int	len, hash_alg;
1121	int	r_usage = 0;
1122
1123	len = strlen(opts);
1124	buf = malloc(len+1);
1125	if (!buf) {
1126		fprintf(stderr,
1127			_("Couldn't allocate memory to parse options!\n"));
1128		return 1;
1129	}
1130	strcpy(buf, opts);
1131	for (token = buf; token && *token; token = next) {
1132		p = strchr(token, ',');
1133		next = 0;
1134		if (p) {
1135			*p = 0;
1136			next = p+1;
1137		}
1138		arg = strchr(token, '=');
1139		if (arg) {
1140			*arg = 0;
1141			arg++;
1142		}
1143		if (strcmp(token, "clear-mmp") == 0 ||
1144		    strcmp(token, "clear_mmp") == 0) {
1145			clear_mmp = 1;
1146		} else if (strcmp(token, "mmp_update_interval") == 0) {
1147			unsigned long interval;
1148			if (!arg) {
1149				r_usage++;
1150				continue;
1151			}
1152			interval = strtoul(arg, &p, 0);
1153			if (*p) {
1154				fprintf(stderr,
1155					_("Invalid mmp_update_interval: %s\n"),
1156					arg);
1157				r_usage++;
1158				continue;
1159			}
1160			if (interval == 0) {
1161				interval = EXT4_MMP_UPDATE_INTERVAL;
1162			} else if (interval > EXT4_MMP_MAX_UPDATE_INTERVAL) {
1163				fprintf(stderr,
1164					_("mmp_update_interval too big: %lu\n"),
1165					interval);
1166				r_usage++;
1167				continue;
1168			}
1169			printf(P_("Setting multiple mount protection update "
1170				  "interval to %lu second\n",
1171				  "Setting multiple mount protection update "
1172				  "interval to %lu seconds\n", interval),
1173			       interval);
1174			fs->super->s_mmp_update_interval = interval;
1175			ext2fs_mark_super_dirty(fs);
1176		} else if (!strcmp(token, "test_fs")) {
1177			fs->super->s_flags |= EXT2_FLAGS_TEST_FILESYS;
1178			printf("Setting test filesystem flag\n");
1179			ext2fs_mark_super_dirty(fs);
1180		} else if (!strcmp(token, "^test_fs")) {
1181			fs->super->s_flags &= ~EXT2_FLAGS_TEST_FILESYS;
1182			printf("Clearing test filesystem flag\n");
1183			ext2fs_mark_super_dirty(fs);
1184		} else if (strcmp(token, "stride") == 0) {
1185			if (!arg) {
1186				r_usage++;
1187				continue;
1188			}
1189			stride = strtoul(arg, &p, 0);
1190			if (*p) {
1191				fprintf(stderr,
1192					_("Invalid RAID stride: %s\n"),
1193					arg);
1194				r_usage++;
1195				continue;
1196			}
1197			stride_set = 1;
1198		} else if (strcmp(token, "stripe-width") == 0 ||
1199			   strcmp(token, "stripe_width") == 0) {
1200			if (!arg) {
1201				r_usage++;
1202				continue;
1203			}
1204			stripe_width = strtoul(arg, &p, 0);
1205			if (*p) {
1206				fprintf(stderr,
1207					_("Invalid RAID stripe-width: %s\n"),
1208					arg);
1209				r_usage++;
1210				continue;
1211			}
1212			stripe_width_set = 1;
1213		} else if (strcmp(token, "hash_alg") == 0 ||
1214			   strcmp(token, "hash-alg") == 0) {
1215			if (!arg) {
1216				r_usage++;
1217				continue;
1218			}
1219			hash_alg = e2p_string2hash(arg);
1220			if (hash_alg < 0) {
1221				fprintf(stderr,
1222					_("Invalid hash algorithm: %s\n"),
1223					arg);
1224				r_usage++;
1225				continue;
1226			}
1227			fs->super->s_def_hash_version = hash_alg;
1228			printf(_("Setting default hash algorithm "
1229				 "to %s (%d)\n"),
1230			       arg, hash_alg);
1231			ext2fs_mark_super_dirty(fs);
1232		} else if (!strcmp(token, "mount_opts")) {
1233			if (!arg) {
1234				r_usage++;
1235				continue;
1236			}
1237			if (strlen(arg) >= sizeof(fs->super->s_mount_opts)) {
1238				fprintf(stderr,
1239					"Extended mount options too long\n");
1240				continue;
1241			}
1242			ext_mount_opts = strdup(arg);
1243		} else
1244			r_usage++;
1245	}
1246	if (r_usage) {
1247		fprintf(stderr, _("\nBad options specified.\n\n"
1248			"Extended options are separated by commas, "
1249			"and may take an argument which\n"
1250			"\tis set off by an equals ('=') sign.\n\n"
1251			"Valid extended options are:\n"
1252			"\tclear_mmp\n"
1253			"\thash_alg=<hash algorithm>\n"
1254			"\tmount_opts=<extended default mount options>\n"
1255			"\tstride=<RAID per-disk chunk size in blocks>\n"
1256			"\tstripe_width=<RAID stride*data disks in blocks>\n"
1257			"\ttest_fs\n"
1258			"\t^test_fs\n"));
1259		free(buf);
1260		return 1;
1261	}
1262	free(buf);
1263
1264	return 0;
1265}
1266
1267/*
1268 * Fill in the block bitmap bmap with the information regarding the
1269 * blocks to be moved
1270 */
1271static int get_move_bitmaps(ext2_filsys fs, int new_ino_blks_per_grp,
1272			    ext2fs_block_bitmap bmap)
1273{
1274	dgrp_t i;
1275	int retval;
1276	ext2_badblocks_list bb_list = 0;
1277	blk64_t j, needed_blocks = 0;
1278	blk64_t start_blk, end_blk;
1279
1280	retval = ext2fs_read_bb_inode(fs, &bb_list);
1281	if (retval)
1282		return retval;
1283
1284	for (i = 0; i < fs->group_desc_count; i++) {
1285		start_blk = ext2fs_inode_table_loc(fs, i) +
1286					fs->inode_blocks_per_group;
1287
1288		end_blk = ext2fs_inode_table_loc(fs, i) +
1289					new_ino_blks_per_grp;
1290
1291		for (j = start_blk; j < end_blk; j++) {
1292			if (ext2fs_test_block_bitmap2(fs->block_map, j)) {
1293				/*
1294				 * IF the block is a bad block we fail
1295				 */
1296				if (ext2fs_badblocks_list_test(bb_list, j)) {
1297					ext2fs_badblocks_list_free(bb_list);
1298					return ENOSPC;
1299				}
1300
1301				ext2fs_mark_block_bitmap2(bmap, j);
1302			} else {
1303				/*
1304				 * We are going to use this block for
1305				 * inode table. So mark them used.
1306				 */
1307				ext2fs_mark_block_bitmap2(fs->block_map, j);
1308			}
1309		}
1310		needed_blocks += end_blk - start_blk;
1311	}
1312
1313	ext2fs_badblocks_list_free(bb_list);
1314	if (needed_blocks > ext2fs_free_blocks_count(fs->super))
1315		return ENOSPC;
1316
1317	return 0;
1318}
1319
1320static int ext2fs_is_meta_block(ext2_filsys fs, blk_t blk)
1321{
1322	dgrp_t group;
1323	group = ext2fs_group_of_blk(fs, blk);
1324	if (ext2fs_block_bitmap_loc(fs, group) == blk)
1325		return 1;
1326	if (ext2fs_inode_bitmap_loc(fs, group) == blk)
1327		return 1;
1328	return 0;
1329}
1330
1331static int ext2fs_is_block_in_group(ext2_filsys fs, dgrp_t group, blk_t blk)
1332{
1333	blk_t start_blk, end_blk;
1334	start_blk = fs->super->s_first_data_block +
1335			EXT2_BLOCKS_PER_GROUP(fs->super) * group;
1336	/*
1337	 * We cannot get new block beyond end_blk for for the last block group
1338	 * so we can check with EXT2_BLOCKS_PER_GROUP even for last block group
1339	 */
1340	end_blk   = start_blk + EXT2_BLOCKS_PER_GROUP(fs->super);
1341	if (blk >= start_blk && blk <= end_blk)
1342		return 1;
1343	return 0;
1344}
1345
1346static int move_block(ext2_filsys fs, ext2fs_block_bitmap bmap)
1347{
1348
1349	char *buf;
1350	dgrp_t group = 0;
1351	errcode_t retval;
1352	int meta_data = 0;
1353	blk64_t blk, new_blk, goal;
1354	struct blk_move *bmv;
1355
1356	retval = ext2fs_get_mem(fs->blocksize, &buf);
1357	if (retval)
1358		return retval;
1359
1360	for (new_blk = blk = fs->super->s_first_data_block;
1361	     blk < ext2fs_blocks_count(fs->super); blk++) {
1362		if (!ext2fs_test_block_bitmap2(bmap, blk))
1363			continue;
1364
1365		if (ext2fs_is_meta_block(fs, blk)) {
1366			/*
1367			 * If the block is mapping a fs meta data block
1368			 * like group desc/block bitmap/inode bitmap. We
1369			 * should find a block in the same group and fix
1370			 * the respective fs metadata pointers. Otherwise
1371			 * fail
1372			 */
1373			group = ext2fs_group_of_blk(fs, blk);
1374			goal = ext2fs_group_first_block2(fs, group);
1375			meta_data = 1;
1376
1377		} else {
1378			goal = new_blk;
1379		}
1380		retval = ext2fs_new_block2(fs, goal, NULL, &new_blk);
1381		if (retval)
1382			goto err_out;
1383
1384		/* new fs meta data block should be in the same group */
1385		if (meta_data && !ext2fs_is_block_in_group(fs, group, new_blk)) {
1386			retval = ENOSPC;
1387			goto err_out;
1388		}
1389
1390		/* Mark this block as allocated */
1391		ext2fs_mark_block_bitmap2(fs->block_map, new_blk);
1392
1393		/* Add it to block move list */
1394		retval = ext2fs_get_mem(sizeof(struct blk_move), &bmv);
1395		if (retval)
1396			goto err_out;
1397
1398		bmv->old_loc = blk;
1399		bmv->new_loc = new_blk;
1400
1401		list_add(&(bmv->list), &blk_move_list);
1402
1403		retval = io_channel_read_blk64(fs->io, blk, 1, buf);
1404		if (retval)
1405			goto err_out;
1406
1407		retval = io_channel_write_blk64(fs->io, new_blk, 1, buf);
1408		if (retval)
1409			goto err_out;
1410	}
1411
1412err_out:
1413	ext2fs_free_mem(&buf);
1414	return retval;
1415}
1416
1417static blk64_t translate_block(blk64_t blk)
1418{
1419	struct list_head *entry;
1420	struct blk_move *bmv;
1421
1422	list_for_each(entry, &blk_move_list) {
1423		bmv = list_entry(entry, struct blk_move, list);
1424		if (bmv->old_loc == blk)
1425			return bmv->new_loc;
1426	}
1427
1428	return 0;
1429}
1430
1431static int process_block(ext2_filsys fs EXT2FS_ATTR((unused)),
1432			 blk64_t *block_nr,
1433			 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
1434			 blk64_t ref_block EXT2FS_ATTR((unused)),
1435			 int ref_offset EXT2FS_ATTR((unused)),
1436			 void *priv_data)
1437{
1438	int ret = 0;
1439	blk64_t new_blk;
1440	ext2fs_block_bitmap bmap = (ext2fs_block_bitmap) priv_data;
1441
1442	if (!ext2fs_test_block_bitmap2(bmap, *block_nr))
1443		return 0;
1444	new_blk = translate_block(*block_nr);
1445	if (new_blk) {
1446		*block_nr = new_blk;
1447		/*
1448		 * This will force the ext2fs_write_inode in the iterator
1449		 */
1450		ret |= BLOCK_CHANGED;
1451	}
1452
1453	return ret;
1454}
1455
1456static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap)
1457{
1458	errcode_t retval = 0;
1459	ext2_ino_t ino;
1460	blk64_t blk;
1461	char *block_buf = 0;
1462	struct ext2_inode inode;
1463	ext2_inode_scan	scan = NULL;
1464
1465	retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf);
1466	if (retval)
1467		return retval;
1468
1469	retval = ext2fs_open_inode_scan(fs, 0, &scan);
1470	if (retval)
1471		goto err_out;
1472
1473	while (1) {
1474		retval = ext2fs_get_next_inode(scan, &ino, &inode);
1475		if (retval)
1476			goto err_out;
1477
1478		if (!ino)
1479			break;
1480
1481		if (inode.i_links_count == 0)
1482			continue; /* inode not in use */
1483
1484		/* FIXME!!
1485		 * If we end up modifying the journal inode
1486		 * the sb->s_jnl_blocks will differ. But a
1487		 * subsequent e2fsck fixes that.
1488		 * Do we need to fix this ??
1489		 */
1490
1491		if (ext2fs_file_acl_block(fs, &inode) &&
1492		    ext2fs_test_block_bitmap2(bmap,
1493					ext2fs_file_acl_block(fs, &inode))) {
1494			blk = translate_block(ext2fs_file_acl_block(fs,
1495								    &inode));
1496			if (!blk)
1497				continue;
1498
1499			ext2fs_file_acl_block_set(fs, &inode, blk);
1500
1501			/*
1502			 * Write the inode to disk so that inode table
1503			 * resizing can work
1504			 */
1505			retval = ext2fs_write_inode(fs, ino, &inode);
1506			if (retval)
1507				goto err_out;
1508		}
1509
1510		if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
1511			continue;
1512
1513		retval = ext2fs_block_iterate3(fs, ino, 0, block_buf,
1514					       process_block, bmap);
1515		if (retval)
1516			goto err_out;
1517
1518	}
1519
1520err_out:
1521	ext2fs_free_mem(&block_buf);
1522
1523	return retval;
1524}
1525
1526/*
1527 * We need to scan for inode and block bitmaps that may need to be
1528 * moved.  This can take place if the filesystem was formatted for
1529 * RAID arrays using the mke2fs's extended option "stride".
1530 */
1531static int group_desc_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap)
1532{
1533	dgrp_t i;
1534	blk_t blk, new_blk;
1535
1536	for (i = 0; i < fs->group_desc_count; i++) {
1537		blk = ext2fs_block_bitmap_loc(fs, i);
1538		if (ext2fs_test_block_bitmap2(bmap, blk)) {
1539			new_blk = translate_block(blk);
1540			if (!new_blk)
1541				continue;
1542			ext2fs_block_bitmap_loc_set(fs, i, new_blk);
1543		}
1544
1545		blk = ext2fs_inode_bitmap_loc(fs, i);
1546		if (ext2fs_test_block_bitmap2(bmap, blk)) {
1547			new_blk = translate_block(blk);
1548			if (!new_blk)
1549				continue;
1550			ext2fs_inode_bitmap_loc_set(fs, i, new_blk);
1551		}
1552	}
1553	return 0;
1554}
1555
1556static int expand_inode_table(ext2_filsys fs, unsigned long new_ino_size)
1557{
1558	dgrp_t i;
1559	blk64_t blk;
1560	errcode_t retval;
1561	int new_ino_blks_per_grp;
1562	unsigned int j;
1563	char *old_itable = NULL, *new_itable = NULL;
1564	char *tmp_old_itable = NULL, *tmp_new_itable = NULL;
1565	unsigned long old_ino_size;
1566	int old_itable_size, new_itable_size;
1567
1568	old_itable_size = fs->inode_blocks_per_group * fs->blocksize;
1569	old_ino_size = EXT2_INODE_SIZE(fs->super);
1570
1571	new_ino_blks_per_grp = ext2fs_div_ceil(
1572					EXT2_INODES_PER_GROUP(fs->super) *
1573					new_ino_size,
1574					fs->blocksize);
1575
1576	new_itable_size = new_ino_blks_per_grp * fs->blocksize;
1577
1578	retval = ext2fs_get_mem(old_itable_size, &old_itable);
1579	if (retval)
1580		return retval;
1581
1582	retval = ext2fs_get_mem(new_itable_size, &new_itable);
1583	if (retval)
1584		goto err_out;
1585
1586	tmp_old_itable = old_itable;
1587	tmp_new_itable = new_itable;
1588
1589	for (i = 0; i < fs->group_desc_count; i++) {
1590		blk = ext2fs_inode_table_loc(fs, i);
1591		retval = io_channel_read_blk64(fs->io, blk,
1592				fs->inode_blocks_per_group, old_itable);
1593		if (retval)
1594			goto err_out;
1595
1596		for (j = 0; j < EXT2_INODES_PER_GROUP(fs->super); j++) {
1597			memcpy(new_itable, old_itable, old_ino_size);
1598
1599			memset(new_itable+old_ino_size, 0,
1600					new_ino_size - old_ino_size);
1601
1602			new_itable += new_ino_size;
1603			old_itable += old_ino_size;
1604		}
1605
1606		/* reset the pointer */
1607		old_itable = tmp_old_itable;
1608		new_itable = tmp_new_itable;
1609
1610		retval = io_channel_write_blk64(fs->io, blk,
1611					new_ino_blks_per_grp, new_itable);
1612		if (retval)
1613			goto err_out;
1614	}
1615
1616	/* Update the meta data */
1617	fs->inode_blocks_per_group = new_ino_blks_per_grp;
1618	fs->super->s_inode_size = new_ino_size;
1619
1620err_out:
1621	if (old_itable)
1622		ext2fs_free_mem(&old_itable);
1623
1624	if (new_itable)
1625		ext2fs_free_mem(&new_itable);
1626
1627	return retval;
1628}
1629
1630static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs)
1631{
1632	blk64_t		blk;
1633	ext2_ino_t	ino;
1634	unsigned int	group = 0;
1635	unsigned int	count = 0;
1636	int		total_free = 0;
1637	int		group_free = 0;
1638
1639	/*
1640	 * First calculate the block statistics
1641	 */
1642	for (blk = fs->super->s_first_data_block;
1643	     blk < ext2fs_blocks_count(fs->super); blk++) {
1644		if (!ext2fs_fast_test_block_bitmap2(fs->block_map, blk)) {
1645			group_free++;
1646			total_free++;
1647		}
1648		count++;
1649		if ((count == fs->super->s_blocks_per_group) ||
1650		    (blk == ext2fs_blocks_count(fs->super)-1)) {
1651			ext2fs_bg_free_blocks_count_set(fs, group++,
1652							group_free);
1653			count = 0;
1654			group_free = 0;
1655		}
1656	}
1657	total_free = EXT2FS_C2B(fs, total_free);
1658	ext2fs_free_blocks_count_set(fs->super, total_free);
1659
1660	/*
1661	 * Next, calculate the inode statistics
1662	 */
1663	group_free = 0;
1664	total_free = 0;
1665	count = 0;
1666	group = 0;
1667
1668	/* Protect loop from wrap-around if s_inodes_count maxed */
1669	for (ino = 1; ino <= fs->super->s_inodes_count && ino > 0; ino++) {
1670		if (!ext2fs_fast_test_inode_bitmap2(fs->inode_map, ino)) {
1671			group_free++;
1672			total_free++;
1673		}
1674		count++;
1675		if ((count == fs->super->s_inodes_per_group) ||
1676		    (ino == fs->super->s_inodes_count)) {
1677			ext2fs_bg_free_inodes_count_set(fs, group++,
1678							group_free);
1679			count = 0;
1680			group_free = 0;
1681		}
1682	}
1683	fs->super->s_free_inodes_count = total_free;
1684	ext2fs_mark_super_dirty(fs);
1685	return 0;
1686}
1687
1688#define list_for_each_safe(pos, pnext, head) \
1689	for (pos = (head)->next, pnext = pos->next; pos != (head); \
1690	     pos = pnext, pnext = pos->next)
1691
1692static void free_blk_move_list(void)
1693{
1694	struct list_head *entry, *tmp;
1695	struct blk_move *bmv;
1696
1697	list_for_each_safe(entry, tmp, &blk_move_list) {
1698		bmv = list_entry(entry, struct blk_move, list);
1699		list_del(entry);
1700		ext2fs_free_mem(&bmv);
1701	}
1702	return;
1703}
1704
1705static int resize_inode(ext2_filsys fs, unsigned long new_size)
1706{
1707	errcode_t retval;
1708	int new_ino_blks_per_grp;
1709	ext2fs_block_bitmap bmap;
1710
1711	retval = ext2fs_read_inode_bitmap(fs);
1712	if (retval) {
1713		fputs(_("Failed to read inode bitmap\n"), stderr);
1714		return retval;
1715	}
1716	retval = ext2fs_read_block_bitmap(fs);
1717	if (retval) {
1718		fputs(_("Failed to read block bitmap\n"), stderr);
1719		return retval;
1720	}
1721	INIT_LIST_HEAD(&blk_move_list);
1722
1723
1724	new_ino_blks_per_grp = ext2fs_div_ceil(
1725					EXT2_INODES_PER_GROUP(fs->super)*
1726					new_size,
1727					fs->blocksize);
1728
1729	/* We may change the file system.
1730	 * Mark the file system as invalid so that
1731	 * the user is prompted to run fsck.
1732	 */
1733	fs->super->s_state &= ~EXT2_VALID_FS;
1734
1735	retval = ext2fs_allocate_block_bitmap(fs, _("blocks to be moved"),
1736						&bmap);
1737	if (retval) {
1738		fputs(_("Failed to allocate block bitmap when "
1739				"increasing inode size\n"), stderr);
1740		return retval;
1741	}
1742	retval = get_move_bitmaps(fs, new_ino_blks_per_grp, bmap);
1743	if (retval) {
1744		fputs(_("Not enough space to increase inode size \n"), stderr);
1745		goto err_out;
1746	}
1747	retval = move_block(fs, bmap);
1748	if (retval) {
1749		fputs(_("Failed to relocate blocks during inode resize \n"),
1750		      stderr);
1751		goto err_out;
1752	}
1753	retval = inode_scan_and_fix(fs, bmap);
1754	if (retval)
1755		goto err_out_undo;
1756
1757	retval = group_desc_scan_and_fix(fs, bmap);
1758	if (retval)
1759		goto err_out_undo;
1760
1761	retval = expand_inode_table(fs, new_size);
1762	if (retval)
1763		goto err_out_undo;
1764
1765	ext2fs_calculate_summary_stats(fs);
1766
1767	fs->super->s_state |= EXT2_VALID_FS;
1768	/* mark super block and block bitmap as dirty */
1769	ext2fs_mark_super_dirty(fs);
1770	ext2fs_mark_bb_dirty(fs);
1771
1772err_out:
1773	free_blk_move_list();
1774	ext2fs_free_block_bitmap(bmap);
1775
1776	return retval;
1777
1778err_out_undo:
1779	free_blk_move_list();
1780	ext2fs_free_block_bitmap(bmap);
1781	fputs(_("Error in resizing the inode size.\n"
1782			"Run e2undo to undo the "
1783			"file system changes. \n"), stderr);
1784
1785	return retval;
1786}
1787
1788static int tune2fs_setup_tdb(const char *name, io_manager *io_ptr)
1789{
1790	errcode_t retval = 0;
1791	const char *tdb_dir;
1792	char *tdb_file;
1793	char *dev_name, *tmp_name;
1794
1795#if 0 /* FIXME!! */
1796	/*
1797	 * Configuration via a conf file would be
1798	 * nice
1799	 */
1800	profile_get_string(profile, "scratch_files",
1801					"directory", 0, 0,
1802					&tdb_dir);
1803#endif
1804	tmp_name = strdup(name);
1805	if (!tmp_name) {
1806	alloc_fn_fail:
1807		com_err(program_name, ENOMEM,
1808			_("Couldn't allocate memory for tdb filename\n"));
1809		return ENOMEM;
1810	}
1811	dev_name = basename(tmp_name);
1812
1813	tdb_dir = getenv("E2FSPROGS_UNDO_DIR");
1814	if (!tdb_dir)
1815		tdb_dir = "/var/lib/e2fsprogs";
1816
1817	if (!strcmp(tdb_dir, "none") || (tdb_dir[0] == 0) ||
1818	    access(tdb_dir, W_OK))
1819		return 0;
1820
1821	tdb_file = malloc(strlen(tdb_dir) + 9 + strlen(dev_name) + 7 + 1);
1822	if (!tdb_file)
1823		goto alloc_fn_fail;
1824	sprintf(tdb_file, "%s/tune2fs-%s.e2undo", tdb_dir, dev_name);
1825
1826	if (!access(tdb_file, F_OK)) {
1827		if (unlink(tdb_file) < 0) {
1828			retval = errno;
1829			com_err(program_name, retval,
1830				_("while trying to delete %s"),
1831				tdb_file);
1832			free(tdb_file);
1833			return retval;
1834		}
1835	}
1836
1837	set_undo_io_backing_manager(*io_ptr);
1838	*io_ptr = undo_io_manager;
1839	set_undo_io_backup_file(tdb_file);
1840	printf(_("To undo the tune2fs operation please run "
1841		 "the command\n    e2undo %s %s\n\n"),
1842		 tdb_file, name);
1843	free(tdb_file);
1844	free(tmp_name);
1845	return retval;
1846}
1847
1848int main(int argc, char **argv)
1849{
1850	errcode_t retval;
1851	ext2_filsys fs;
1852	struct ext2_super_block *sb;
1853	io_manager io_ptr, io_ptr_orig = NULL;
1854	int rc = 0;
1855
1856#ifdef ENABLE_NLS
1857	setlocale(LC_MESSAGES, "");
1858	setlocale(LC_CTYPE, "");
1859	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
1860	textdomain(NLS_CAT_NAME);
1861	set_com_err_gettext(gettext);
1862#endif
1863	if (argc && *argv)
1864		program_name = *argv;
1865	add_error_table(&et_ext2_error_table);
1866
1867#ifdef CONFIG_BUILD_FINDFS
1868	if (strcmp(get_progname(argv[0]), "findfs") == 0)
1869		do_findfs(argc, argv);
1870#endif
1871	if (strcmp(get_progname(argv[0]), "e2label") == 0)
1872		parse_e2label_options(argc, argv);
1873	else
1874		parse_tune2fs_options(argc, argv);
1875
1876#ifdef CONFIG_TESTIO_DEBUG
1877	if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_DEBUG")) {
1878		io_ptr = test_io_manager;
1879		test_io_backing_manager = unix_io_manager;
1880	} else
1881#endif
1882		io_ptr = unix_io_manager;
1883
1884retry_open:
1885	if ((open_flag & EXT2_FLAG_RW) == 0 || f_flag)
1886		open_flag |= EXT2_FLAG_SKIP_MMP;
1887
1888	open_flag |= EXT2_FLAG_64BITS;
1889
1890	/* keep the filesystem struct around to dump MMP data */
1891	open_flag |= EXT2_FLAG_NOFREE_ON_ERROR;
1892
1893	retval = ext2fs_open2(device_name, io_options, open_flag,
1894			      0, 0, io_ptr, &fs);
1895	if (retval) {
1896		com_err(program_name, retval,
1897			_("while trying to open %s"),
1898			device_name);
1899		if (retval == EXT2_ET_MMP_FSCK_ON ||
1900		    retval == EXT2_ET_MMP_UNKNOWN_SEQ)
1901			dump_mmp_msg(fs->mmp_buf,
1902				     _("If you are sure the filesystem "
1903				       "is not in use on any node, run:\n"
1904				       "'tune2fs -f -E clear_mmp {device}'\n"));
1905		else if (retval == EXT2_ET_MMP_FAILED)
1906			dump_mmp_msg(fs->mmp_buf, NULL);
1907		else if (retval == EXT2_ET_MMP_MAGIC_INVALID)
1908			fprintf(stderr,
1909				_("MMP block magic is bad. Try to fix it by "
1910				  "running:\n'e2fsck -f %s'\n"), device_name);
1911		else if (retval != EXT2_ET_MMP_FAILED)
1912			fprintf(stderr,
1913			     _("Couldn't find valid filesystem superblock.\n"));
1914
1915		ext2fs_free(fs);
1916		exit(1);
1917	}
1918	fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;
1919
1920	if (I_flag && !io_ptr_orig) {
1921		/*
1922		 * Check the inode size is right so we can issue an
1923		 * error message and bail before setting up the tdb
1924		 * file.
1925		 */
1926		if (new_inode_size == EXT2_INODE_SIZE(fs->super)) {
1927			fprintf(stderr, _("The inode size is already %lu\n"),
1928				new_inode_size);
1929			rc = 1;
1930			goto closefs;
1931		}
1932		if (new_inode_size < EXT2_INODE_SIZE(fs->super)) {
1933			fprintf(stderr, _("Shrinking the inode size is "
1934					  "not supported\n"));
1935			rc = 1;
1936			goto closefs;
1937		}
1938
1939		/*
1940		 * If inode resize is requested use the
1941		 * Undo I/O manager
1942		 */
1943		io_ptr_orig = io_ptr;
1944		retval = tune2fs_setup_tdb(device_name, &io_ptr);
1945		if (retval) {
1946			rc = 1;
1947			goto closefs;
1948		}
1949		if (io_ptr != io_ptr_orig) {
1950			ext2fs_close(fs);
1951			goto retry_open;
1952		}
1953	}
1954
1955	sb = fs->super;
1956	fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
1957
1958	if (print_label) {
1959		/* For e2label emulation */
1960		printf("%.*s\n", (int) sizeof(sb->s_volume_name),
1961		       sb->s_volume_name);
1962		remove_error_table(&et_ext2_error_table);
1963		goto closefs;
1964	}
1965
1966	retval = ext2fs_check_if_mounted(device_name, &mount_flags);
1967	if (retval) {
1968		com_err("ext2fs_check_if_mount", retval,
1969			_("while determining whether %s is mounted."),
1970			device_name);
1971		rc = 1;
1972		goto closefs;
1973	}
1974	/* Normally we only need to write out the superblock */
1975	fs->flags |= EXT2_FLAG_SUPER_ONLY;
1976
1977	if (c_flag) {
1978		sb->s_max_mnt_count = max_mount_count;
1979		ext2fs_mark_super_dirty(fs);
1980		printf(_("Setting maximal mount count to %d\n"),
1981		       max_mount_count);
1982	}
1983	if (C_flag) {
1984		sb->s_mnt_count = mount_count;
1985		ext2fs_mark_super_dirty(fs);
1986		printf(_("Setting current mount count to %d\n"), mount_count);
1987	}
1988	if (e_flag) {
1989		sb->s_errors = errors;
1990		ext2fs_mark_super_dirty(fs);
1991		printf(_("Setting error behavior to %d\n"), errors);
1992	}
1993	if (g_flag) {
1994		sb->s_def_resgid = resgid;
1995		ext2fs_mark_super_dirty(fs);
1996		printf(_("Setting reserved blocks gid to %lu\n"), resgid);
1997	}
1998	if (i_flag) {
1999		if (interval >= (1ULL << 32)) {
2000			com_err(program_name, 0,
2001				_("interval between checks is too big (%lu)"),
2002				interval);
2003			rc = 1;
2004			goto closefs;
2005		}
2006		sb->s_checkinterval = interval;
2007		ext2fs_mark_super_dirty(fs);
2008		printf(_("Setting interval between checks to %lu seconds\n"),
2009		       interval);
2010	}
2011	if (m_flag) {
2012		ext2fs_r_blocks_count_set(sb, reserved_ratio *
2013					  ext2fs_blocks_count(sb) / 100.0);
2014		ext2fs_mark_super_dirty(fs);
2015		printf (_("Setting reserved blocks percentage to %g%% (%llu blocks)\n"),
2016			reserved_ratio, ext2fs_r_blocks_count(sb));
2017	}
2018	if (r_flag) {
2019		if (reserved_blocks > ext2fs_blocks_count(sb)/2) {
2020			com_err(program_name, 0,
2021				_("reserved blocks count is too big (%llu)"),
2022				reserved_blocks);
2023			rc = 1;
2024			goto closefs;
2025		}
2026		ext2fs_r_blocks_count_set(sb, reserved_blocks);
2027		ext2fs_mark_super_dirty(fs);
2028		printf(_("Setting reserved blocks count to %llu\n"),
2029		       reserved_blocks);
2030	}
2031	if (s_flag == 1) {
2032		if (sb->s_feature_ro_compat &
2033		    EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
2034			fputs(_("\nThe filesystem already has sparse "
2035				"superblocks.\n"), stderr);
2036		else {
2037			sb->s_feature_ro_compat |=
2038				EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
2039			sb->s_state &= ~EXT2_VALID_FS;
2040			ext2fs_mark_super_dirty(fs);
2041			printf(_("\nSparse superblock flag set.  %s"),
2042			       _(please_fsck));
2043		}
2044	}
2045	if (s_flag == 0) {
2046		fputs(_("\nClearing the sparse superflag not supported.\n"),
2047		      stderr);
2048		rc = 1;
2049		goto closefs;
2050	}
2051	if (T_flag) {
2052		sb->s_lastcheck = last_check_time;
2053		ext2fs_mark_super_dirty(fs);
2054		printf(_("Setting time filesystem last checked to %s\n"),
2055		       ctime(&last_check_time));
2056	}
2057	if (u_flag) {
2058		sb->s_def_resuid = resuid;
2059		ext2fs_mark_super_dirty(fs);
2060		printf(_("Setting reserved blocks uid to %lu\n"), resuid);
2061	}
2062	if (L_flag) {
2063		if (strlen(new_label) > sizeof(sb->s_volume_name))
2064			fputs(_("Warning: label too long, truncating.\n"),
2065			      stderr);
2066		memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
2067		strncpy(sb->s_volume_name, new_label,
2068			sizeof(sb->s_volume_name));
2069		ext2fs_mark_super_dirty(fs);
2070	}
2071	if (M_flag) {
2072		memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
2073		strncpy(sb->s_last_mounted, new_last_mounted,
2074			sizeof(sb->s_last_mounted));
2075		ext2fs_mark_super_dirty(fs);
2076	}
2077	if (mntopts_cmd) {
2078		rc = update_mntopts(fs, mntopts_cmd);
2079		if (rc)
2080			goto closefs;
2081	}
2082	if (features_cmd) {
2083		rc = update_feature_set(fs, features_cmd);
2084		if (rc)
2085			goto closefs;
2086	}
2087	if (extended_cmd) {
2088		rc = parse_extended_opts(fs, extended_cmd);
2089		if (rc)
2090			goto closefs;
2091		if (clear_mmp && !f_flag) {
2092			fputs(_("Error in using clear_mmp. "
2093				"It must be used with -f\n"),
2094			      stderr);
2095			goto closefs;
2096		}
2097	}
2098	if (clear_mmp) {
2099		rc = ext2fs_mmp_clear(fs);
2100		goto closefs;
2101	}
2102	if (journal_size || journal_device) {
2103		rc = add_journal(fs);
2104		if (rc)
2105			goto closefs;
2106	}
2107
2108	if (Q_flag) {
2109		if (mount_flags & EXT2_MF_MOUNTED) {
2110			fputs(_("The quota feature may only be changed when "
2111				"the filesystem is unmounted.\n"), stderr);
2112			rc = 1;
2113			goto closefs;
2114		}
2115		handle_quota_options(fs);
2116	}
2117
2118	if (U_flag) {
2119		int set_csum = 0;
2120		dgrp_t i;
2121
2122		if (sb->s_feature_ro_compat &
2123		    EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
2124			/*
2125			 * Determine if the block group checksums are
2126			 * correct so we know whether or not to set
2127			 * them later on.
2128			 */
2129			for (i = 0; i < fs->group_desc_count; i++)
2130				if (!ext2fs_group_desc_csum_verify(fs, i))
2131					break;
2132			if (i >= fs->group_desc_count)
2133				set_csum = 1;
2134		}
2135		if ((strcasecmp(new_UUID, "null") == 0) ||
2136		    (strcasecmp(new_UUID, "clear") == 0)) {
2137			uuid_clear(sb->s_uuid);
2138		} else if (strcasecmp(new_UUID, "time") == 0) {
2139			uuid_generate_time(sb->s_uuid);
2140		} else if (strcasecmp(new_UUID, "random") == 0) {
2141			uuid_generate(sb->s_uuid);
2142		} else if (uuid_parse(new_UUID, sb->s_uuid)) {
2143			com_err(program_name, 0, _("Invalid UUID format\n"));
2144			rc = 1;
2145			goto closefs;
2146		}
2147		if (set_csum) {
2148			for (i = 0; i < fs->group_desc_count; i++)
2149				ext2fs_group_desc_csum_set(fs, i);
2150			fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
2151		}
2152		ext2fs_mark_super_dirty(fs);
2153	}
2154	if (I_flag) {
2155		if (mount_flags & EXT2_MF_MOUNTED) {
2156			fputs(_("The inode size may only be "
2157				"changed when the filesystem is "
2158				"unmounted.\n"), stderr);
2159			rc = 1;
2160			goto closefs;
2161		}
2162		if (fs->super->s_feature_incompat &
2163		    EXT4_FEATURE_INCOMPAT_FLEX_BG) {
2164			fputs(_("Changing the inode size not supported for "
2165				"filesystems with the flex_bg\n"
2166				"feature enabled.\n"),
2167			      stderr);
2168			rc = 1;
2169			goto closefs;
2170		}
2171		/*
2172		 * We want to update group descriptor also
2173		 * with the new free inode count
2174		 */
2175		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
2176		if (resize_inode(fs, new_inode_size) == 0) {
2177			printf(_("Setting inode size %lu\n"),
2178							new_inode_size);
2179		} else {
2180			printf(_("Failed to change inode size\n"));
2181			rc = 1;
2182			goto closefs;
2183		}
2184	}
2185
2186	if (l_flag)
2187		list_super(sb);
2188	if (stride_set) {
2189		sb->s_raid_stride = stride;
2190		ext2fs_mark_super_dirty(fs);
2191		printf(_("Setting stride size to %d\n"), stride);
2192	}
2193	if (stripe_width_set) {
2194		sb->s_raid_stripe_width = stripe_width;
2195		ext2fs_mark_super_dirty(fs);
2196		printf(_("Setting stripe width to %d\n"), stripe_width);
2197	}
2198	if (ext_mount_opts) {
2199		strncpy((char *)(fs->super->s_mount_opts), ext_mount_opts,
2200			sizeof(fs->super->s_mount_opts));
2201		fs->super->s_mount_opts[sizeof(fs->super->s_mount_opts)-1] = 0;
2202		ext2fs_mark_super_dirty(fs);
2203		printf(_("Setting extended default mount options to '%s'\n"),
2204		       ext_mount_opts);
2205		free(ext_mount_opts);
2206	}
2207	free(device_name);
2208	remove_error_table(&et_ext2_error_table);
2209
2210closefs:
2211	if (rc) {
2212		ext2fs_mmp_stop(fs);
2213		exit(1);
2214	}
2215
2216	return (ext2fs_close(fs) ? 1 : 0);
2217}
2218