e2fsck.c revision 21c84b71e205b5ab13f14343da5645dcc985856d
1/*
2 * e2fsck.c - a consistency checker for the new extended file system.
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12/* Usage: e2fsck [-dfpnsvy] device
13 *	-d -- debugging this program
14 *	-f -- check the fs even if it is marked valid
15 *	-p -- "preen" the filesystem
16 * 	-n -- open the filesystem r/o mode; never try to fix problems
17 *	-v -- verbose (tells how many files)
18 * 	-y -- always answer yes to questions
19 *
20 * The device may be a block device or a image of one, but this isn't
21 * enforced (but it's not much fun on a character device :-).
22 */
23
24#include <stdio.h>
25#ifdef HAVE_STDLIB_H
26#include <stdlib.h>
27#endif
28#include <string.h>
29#include <fcntl.h>
30#include <ctype.h>
31#include <termios.h>
32#include <time.h>
33#ifdef HAVE_GETOPT_H
34#include <getopt.h>
35#endif
36#include <unistd.h>
37#ifdef HAVE_ERRNO_H
38#include <errno.h>
39#endif
40#ifdef HAVE_MNTENT_H
41#include <mntent.h>
42#endif
43#include <sys/ioctl.h>
44#include <malloc.h>
45
46#include "et/com_err.h"
47#include "uuid/uuid.h"
48#include "e2fsck.h"
49#include "problem.h"
50#include "../version.h"
51
52extern int isatty(int);
53
54const char * program_name = "e2fsck";
55const char * device_name = NULL;
56const char * filesystem_name = NULL;
57
58/* Command line options */
59int nflag = 0;
60int yflag = 0;
61int tflag = 0;			/* Do timing */
62int cflag = 0;			/* check disk */
63int preen = 0;
64int rwflag = 1;
65int swapfs = 0;
66int normalize_swapfs = 0;
67int inode_buffer_blocks = 0;
68blk_t use_superblock;
69blk_t superblock;
70int blocksize = 0;
71int verbose = 0;
72int list = 0;
73int debug = 0;
74int force = 0;
75int invalid_bitmaps = 0;
76static int show_version_only = 0;
77
78static int replace_bad_blocks = 0;
79static char *bad_blocks_file = 0;
80
81static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
82
83struct resource_track	global_rtrack;
84
85static int root_filesystem = 0;
86static int read_only_root = 0;
87
88int *invalid_inode_bitmap;
89int *invalid_block_bitmap;
90int *invalid_inode_table;
91int restart_e2fsck = 0;
92
93static void usage(NOARGS)
94{
95	fprintf(stderr,
96		"Usage: %s [-panyrcdfvstFSV] [-b superblock] [-B blocksize]\n"
97		"\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
98		"\t\t[-l|-L bad_blocks_file] device\n", program_name);
99	exit(FSCK_USAGE);
100}
101
102static void show_stats(ext2_filsys fs)
103{
104	int inodes, inodes_used, blocks, blocks_used;
105	int dir_links;
106	int num_files, num_links;
107	int frag_percent;
108
109	dir_links = 2 * fs_directory_count - 1;
110	num_files = fs_total_count - dir_links;
111	num_links = fs_links_count - dir_links;
112	inodes = fs->super->s_inodes_count;
113	inodes_used = (fs->super->s_inodes_count -
114		       fs->super->s_free_inodes_count);
115	blocks = fs->super->s_blocks_count;
116	blocks_used = (fs->super->s_blocks_count -
117		       fs->super->s_free_blocks_count);
118
119	frag_percent = (10000 * fs_fragmented) / inodes_used;
120	frag_percent = (frag_percent + 5) / 10;
121
122	if (!verbose) {
123		printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
124		       device_name, inodes_used, inodes,
125		       frag_percent / 10, frag_percent % 10,
126		       blocks_used, blocks);
127		return;
128	}
129	printf ("\n%8d inode%s used (%d%%)\n", inodes_used,
130		(inodes_used != 1) ? "s" : "",
131		100 * inodes_used / inodes);
132	printf ("%8d non-contiguous inodes (%0d.%d%%)\n",
133		fs_fragmented, frag_percent / 10, frag_percent % 10);
134	printf ("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n",
135		fs_ind_count, fs_dind_count, fs_tind_count);
136	printf ("%8d block%s used (%d%%)\n"
137		"%8d bad block%s\n", blocks_used,
138		(blocks_used != 1) ? "s" : "",
139		100 * blocks_used / blocks, fs_badblocks_count,
140		fs_badblocks_count != 1 ? "s" : "");
141	printf ("\n%8d regular file%s\n"
142		"%8d director%s\n"
143		"%8d character device file%s\n"
144		"%8d block device file%s\n"
145		"%8d fifo%s\n"
146		"%8d link%s\n"
147		"%8d symbolic link%s (%d fast symbolic link%s)\n"
148		"%8d socket%s\n"
149		"--------\n"
150		"%8d file%s\n",
151		fs_regular_count, (fs_regular_count != 1) ? "s" : "",
152		fs_directory_count, (fs_directory_count != 1) ? "ies" : "y",
153		fs_chardev_count, (fs_chardev_count != 1) ? "s" : "",
154		fs_blockdev_count, (fs_blockdev_count != 1) ? "s" : "",
155		fs_fifo_count, (fs_fifo_count != 1) ? "s" : "",
156		fs_links_count - dir_links,
157		((fs_links_count - dir_links) != 1) ? "s" : "",
158		fs_symlinks_count, (fs_symlinks_count != 1) ? "s" : "",
159		fs_fast_symlinks_count, (fs_fast_symlinks_count != 1) ? "s" : "",
160		fs_sockets_count, (fs_sockets_count != 1) ? "s" : "",
161		fs_total_count - dir_links,
162		((fs_total_count - dir_links) != 1) ? "s" : "");
163}
164
165static void check_mount(NOARGS)
166{
167	errcode_t	retval;
168	int		mount_flags, cont, fd;
169
170	retval = ext2fs_check_if_mounted(filesystem_name, &mount_flags);
171	if (retval) {
172		com_err("ext2fs_check_if_mount", retval,
173			"while determining whether %s is mounted.",
174			filesystem_name);
175		return;
176	}
177	if (!(mount_flags & EXT2_MF_MOUNTED))
178		return;
179
180#if (defined(__linux__) && defined(HAVE_MNTENT_H))
181	/*
182	 * If the root is mounted read-only, then /etc/mtab is
183	 * probably not correct; so we won't issue a warning based on
184	 * it.
185	 */
186	fd = open(MOUNTED, O_RDWR);
187	if (fd < 0) {
188		if (errno == EROFS)
189			return;
190	} else
191		close(fd);
192#endif
193
194	if (!rwflag) {
195		printf("Warning!  %s is mounted.\n", device_name);
196		return;
197	}
198
199	printf ("%s is mounted.  ", device_name);
200	if (isatty (0) && isatty (1))
201		cont = ask_yn("Do you really want to continue", -1);
202	else
203		cont = 0;
204	if (!cont) {
205		printf ("check aborted.\n");
206		exit (0);
207	}
208	return;
209}
210
211static void sync_disks(NOARGS)
212{
213	sync();
214	sync();
215	sleep(1);
216	sync();
217}
218
219#define MIN_CHECK 1
220#define MAX_CHECK 2
221
222static const char *corrupt_msg =
223"\nThe superblock could not be read or does not describe a correct ext2\n"
224"filesystem.  If the device is valid and it really contains an ext2\n"
225"filesystem (and not swap or ufs or something else), then the superblock\n"
226"is corrupt, and you might try running e2fsck with an alternate superblock:\n"
227"    e2fsck -b 8193 <device>\n\n";
228
229static void check_super_value(const char *descr, unsigned long value,
230			      int flags, unsigned long min, unsigned long max)
231{
232	if (((flags & MIN_CHECK) && (value < min)) ||
233	    ((flags & MAX_CHECK) && (value > max))) {
234		printf("Corruption found in superblock.  (%s = %lu).\n",
235		       descr, value);
236		printf(corrupt_msg);
237		fatal_error(0);
238	}
239}
240
241static void relocate_hint(void)
242{
243	static hint_issued = 0;
244
245	/*
246	 * Only issue the hint once, and only if we're using the
247	 * primary superblocks.
248	 */
249	if (hint_issued || superblock)
250		return;
251
252	printf("Note: if there is several inode or block bitmap blocks\n"
253	       "which require relocation, or one part of the inode table\n"
254	       "which must be moved, you may wish to try running e2fsck\n"
255	       "the '-b 8193' option first.  The problem may lie only with\n"
256	       "the primary block group descriptor, and the backup block\n"
257	       "group descriptor may be OK.\n\n");
258	hint_issued = 1;
259}
260
261
262static void check_super_block(ext2_filsys fs)
263{
264	blk_t	first_block, last_block;
265	struct ext2fs_sb *s = (struct ext2fs_sb *) fs->super;
266	blk_t	blocks_per_group = fs->super->s_blocks_per_group;
267	int	i;
268	blk_t	should_be;
269	errcode_t retval;
270	struct problem_context	pctx;
271
272	clear_problem_context(&pctx);
273
274	/*
275	 * Verify the super block constants...
276	 */
277	check_super_value("inodes_count", s->s_inodes_count,
278			  MIN_CHECK, 1, 0);
279	check_super_value("blocks_count", s->s_blocks_count,
280			  MIN_CHECK, 1, 0);
281	check_super_value("first_data_block", s->s_first_data_block,
282			  MAX_CHECK, 0, s->s_blocks_count);
283	check_super_value("log_frag_size", s->s_log_frag_size,
284			  MAX_CHECK, 0, 2);
285	check_super_value("log_block_size", s->s_log_block_size,
286			  MIN_CHECK | MAX_CHECK, s->s_log_frag_size,
287			  2);
288	check_super_value("frags_per_group", s->s_frags_per_group,
289			  MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s));
290	check_super_value("blocks_per_group", s->s_blocks_per_group,
291			  MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s));
292	check_super_value("inodes_per_group", s->s_inodes_per_group,
293			  MIN_CHECK, 1, 0);
294	check_super_value("r_blocks_count", s->s_r_blocks_count,
295			  MAX_CHECK, 0, s->s_blocks_count);
296
297	retval = ext2fs_get_device_size(filesystem_name, EXT2_BLOCK_SIZE(s),
298					&should_be);
299	if (retval) {
300		com_err("ext2fs_get_device_size", retval,
301			"while trying to check physical size of filesystem");
302		fatal_error(0);
303	}
304	if (should_be < s->s_blocks_count) {
305		printf("The filesystem size (according to the superblock) is %d blocks\n", s->s_blocks_count);
306		printf("The physical size of the device is %d blocks\n",
307		       should_be);
308		printf("Either the superblock or the partition table is likely to be corrupt!\n");
309		preenhalt(fs);
310		if (ask("Abort", 1))
311			fatal_error(0);
312	}
313
314	if (s->s_log_block_size != s->s_log_frag_size) {
315		printf("Superblock block_size = %d, fragsize = %d.\n",
316		       EXT2_BLOCK_SIZE(s), EXT2_FRAG_SIZE(s));
317		printf("This version of e2fsck does not support fragment "
318		       "sizes different\n"
319		       "from the block size.\n");
320		fatal_error(0);
321	}
322
323	should_be = s->s_frags_per_group /
324		(s->s_log_block_size - s->s_log_frag_size + 1);
325	if (s->s_blocks_per_group != should_be) {
326		printf("Superblock blocks_per_group = %u, should "
327		       "have been %u\n", s->s_blocks_per_group,
328		       should_be);
329		printf(corrupt_msg);
330		fatal_error(0);
331	}
332
333	should_be = (s->s_log_block_size == 0) ? 1 : 0;
334	if (s->s_first_data_block != should_be) {
335		printf("Superblock first_data_block = %u, should "
336		       "have been %u\n", s->s_first_data_block,
337		       should_be);
338		printf(corrupt_msg);
339		fatal_error(0);
340	}
341
342	/*
343	 * Verify the group descriptors....
344	 */
345	first_block =  fs->super->s_first_data_block;
346	last_block = first_block + blocks_per_group;
347
348	for (i = 0; i < fs->group_desc_count; i++) {
349		pctx.group = i;
350
351		if (i == fs->group_desc_count - 1)
352			last_block = fs->super->s_blocks_count;
353		if ((fs->group_desc[i].bg_block_bitmap < first_block) ||
354		    (fs->group_desc[i].bg_block_bitmap >= last_block)) {
355			relocate_hint();
356			pctx.blk = fs->group_desc[i].bg_block_bitmap;
357			if (fix_problem(fs, PR_0_BB_NOT_GROUP, &pctx)) {
358				fs->group_desc[i].bg_block_bitmap = 0;
359				invalid_block_bitmap[i]++;
360				invalid_bitmaps++;
361			}
362		}
363		if ((fs->group_desc[i].bg_inode_bitmap < first_block) ||
364		    (fs->group_desc[i].bg_inode_bitmap >= last_block)) {
365			relocate_hint();
366			pctx.blk = fs->group_desc[i].bg_inode_bitmap;
367			if (fix_problem(fs, PR_0_IB_NOT_GROUP, &pctx)) {
368				fs->group_desc[i].bg_inode_bitmap = 0;
369				invalid_inode_bitmap[i]++;
370				invalid_bitmaps++;
371			}
372		}
373		if ((fs->group_desc[i].bg_inode_table < first_block) ||
374		    ((fs->group_desc[i].bg_inode_table +
375		      fs->inode_blocks_per_group - 1) >= last_block)) {
376			relocate_hint();
377			pctx.blk = fs->group_desc[i].bg_inode_table;
378			if (fix_problem(fs, PR_0_ITABLE_NOT_GROUP, &pctx)) {
379				fs->group_desc[i].bg_inode_table = 0;
380				invalid_inode_table[i]++;
381				invalid_bitmaps++;
382			}
383		}
384		first_block += fs->super->s_blocks_per_group;
385		last_block += fs->super->s_blocks_per_group;
386	}
387	/*
388	 * If we have invalid bitmaps, set the error state of the
389	 * filesystem.
390	 */
391	if (invalid_bitmaps && rwflag) {
392		fs->super->s_state &= ~EXT2_VALID_FS;
393		ext2fs_mark_super_dirty(fs);
394	}
395
396	/*
397	 * If the UUID field isn't assigned, assign it.
398	 */
399	if (rwflag && uuid_is_null(s->s_uuid)) {
400		if (preen)
401			printf("%s: Adding UUID to filesystem.\n",
402			       device_name);
403		else
404			printf("Filesystem did not have a UUID; "
405			       "generating one.\n\n");
406		uuid_generate(s->s_uuid);
407		ext2fs_mark_super_dirty(fs);
408	}
409	return;
410}
411
412/*
413 * This routine checks to see if a filesystem can be skipped; if so,
414 * it will exit with E2FSCK_OK.  Under some conditions it will print a
415 * message explaining why a check is being forced.
416 */
417static void check_if_skip(ext2_filsys fs)
418{
419	const char *reason = NULL;
420
421	if (force || bad_blocks_file || cflag || swapfs)
422		return;
423
424	if (fs->super->s_state & EXT2_ERROR_FS)
425		reason = "contains a file system with errors";
426	else if (fs->super->s_mnt_count >=
427		 (unsigned) fs->super->s_max_mnt_count)
428		reason = "has reached maximal mount count";
429	else if (fs->super->s_checkinterval &&
430		 time(0) >= (fs->super->s_lastcheck +
431			     fs->super->s_checkinterval))
432		reason = "has gone too long without being checked";
433	else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
434		reason = "was not cleanly unmounted";
435	if (reason) {
436		printf("%s %s, check forced.\n", device_name, reason);
437		return;
438	}
439	printf("%s: clean, %d/%d files, %d/%d blocks\n", device_name,
440	       fs->super->s_inodes_count - fs->super->s_free_inodes_count,
441	       fs->super->s_inodes_count,
442	       fs->super->s_blocks_count - fs->super->s_free_blocks_count,
443	       fs->super->s_blocks_count);
444	ext2fs_close(fs);
445	exit(FSCK_OK);
446}
447
448#define PATH_SET "PATH=/sbin"
449
450static void PRS(int argc, char *argv[])
451{
452	int		flush = 0;
453	char		c;
454#ifdef MTRACE
455	extern void	*mallwatch;
456#endif
457	char		*oldpath = getenv("PATH");
458
459	/* Update our PATH to include /sbin  */
460	if (oldpath) {
461		char *newpath;
462
463		newpath = malloc(sizeof (PATH_SET) + 1 + strlen (oldpath));
464		if (!newpath)
465			fatal_error("Couldn't malloc() newpath");
466		strcpy (newpath, PATH_SET);
467		strcat (newpath, ":");
468		strcat (newpath, oldpath);
469		putenv (newpath);
470	} else
471		putenv (PATH_SET);
472
473	setbuf(stdout, NULL);
474	setbuf(stderr, NULL);
475	initialize_ext2_error_table();
476
477	if (argc && *argv)
478		program_name = *argv;
479	while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:N:Ss")) != EOF)
480		switch (c) {
481		case 'p':
482		case 'a':
483			preen = 1;
484			yflag = nflag = 0;
485			break;
486		case 'n':
487			nflag = 1;
488			preen = yflag = 0;
489			break;
490		case 'y':
491			yflag = 1;
492			preen = nflag = 0;
493			break;
494		case 't':
495			tflag++;
496			break;
497		case 'c':
498			cflag++;
499			break;
500		case 'r':
501			/* What we do by default, anyway! */
502			break;
503		case 'b':
504			use_superblock = atoi(optarg);
505			break;
506		case 'B':
507			blocksize = atoi(optarg);
508			break;
509		case 'I':
510			inode_buffer_blocks = atoi(optarg);
511			break;
512		case 'P':
513			process_inode_size = atoi(optarg);
514			break;
515		case 'L':
516			replace_bad_blocks++;
517		case 'l':
518			bad_blocks_file = malloc(strlen(optarg)+1);
519			if (!bad_blocks_file)
520				fatal_error("Couldn't malloc bad_blocks_file");
521			strcpy(bad_blocks_file, optarg);
522			break;
523		case 'd':
524			debug = 1;
525			break;
526		case 'f':
527			force = 1;
528			break;
529		case 'F':
530#ifdef BLKFLSBUF
531			flush = 1;
532#else
533			fatal_error ("-F not supported");
534#endif
535			break;
536		case 'v':
537			verbose = 1;
538			break;
539		case 'V':
540			show_version_only = 1;
541			break;
542#ifdef MTRACE
543		case 'M':
544			mallwatch = (void *) strtol(optarg, NULL, 0);
545			break;
546#endif
547		case 'N':
548			device_name = optarg;
549			break;
550		case 's':
551			normalize_swapfs = 1;
552		case 'S':
553			swapfs = 1;
554			break;
555		default:
556			usage ();
557		}
558	if (show_version_only)
559		return;
560	if (optind != argc - 1)
561		usage ();
562	if (nflag && !bad_blocks_file && !cflag && !swapfs)
563		rwflag = 0;
564	filesystem_name = argv[optind];
565	if (device_name == 0)
566		device_name = filesystem_name;
567	if (flush) {
568#ifdef BLKFLSBUF
569		int	fd = open(filesystem_name, O_RDONLY, 0);
570
571		if (fd < 0) {
572			com_err("open", errno, "while opening %s for flushing",
573				filesystem_name);
574			exit(FSCK_ERROR);
575		}
576		if (ioctl(fd, BLKFLSBUF, 0) < 0) {
577			com_err("BLKFLSBUF", errno, "while trying to flush %s",
578				filesystem_name);
579			exit(FSCK_ERROR);
580		}
581		close(fd);
582#else
583		fatal_error ("BLKFLSBUF not supported");
584#endif /* BLKFLSBUF */
585	}
586	if (swapfs) {
587		if (cflag || bad_blocks_file) {
588			fprintf(stderr, "Incompatible options not "
589				"allowed when byte-swapping.\n");
590			fatal_error(0);
591		}
592	}
593}
594
595int main (int argc, char *argv[])
596{
597	errcode_t	retval = 0;
598	int		exit_value = FSCK_OK;
599	int		i;
600	ext2_filsys	fs = 0;
601	io_manager	io_ptr;
602	struct ext2fs_sb *s;
603
604#ifdef MTRACE
605	mtrace();
606#endif
607#ifdef MCHECK
608	mcheck(0);
609#endif
610
611	init_resource_track(&global_rtrack);
612
613	PRS(argc, argv);
614
615	if (!preen || show_version_only)
616		fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n",
617			 E2FSPROGS_VERSION, E2FSPROGS_DATE,
618			 EXT2FS_VERSION, EXT2FS_DATE);
619
620	if (show_version_only) {
621		fprintf(stderr, "\tUsing %s\n",
622			error_message(EXT2_ET_BASE));
623		exit(0);
624	}
625
626	check_mount();
627
628	if (!preen && !nflag && !yflag) {
629		if (!isatty (0) || !isatty (1))
630			die ("need terminal for interactive repairs");
631	}
632	superblock = use_superblock;
633restart:
634#if 1
635	io_ptr = unix_io_manager;
636#else
637	io_ptr = test_io_manager;
638	test_io_backing_manager = unix_io_manager;
639#endif
640	sync_disks();
641	if (superblock && blocksize) {
642		retval = ext2fs_open(filesystem_name,
643				     rwflag ? EXT2_FLAG_RW : 0,
644				     superblock, blocksize, io_ptr, &fs);
645	} else if (superblock) {
646		for (i=0; possible_block_sizes[i]; i++) {
647			retval = ext2fs_open(filesystem_name,
648					     rwflag ? EXT2_FLAG_RW : 0,
649					     superblock,
650					     possible_block_sizes[i],
651					     io_ptr, &fs);
652			if (!retval)
653				break;
654		}
655	} else
656		retval = ext2fs_open(filesystem_name,
657				     rwflag ? EXT2_FLAG_RW : 0,
658				     0, 0, io_ptr, &fs);
659	if (!superblock && !preen &&
660	    ((retval == EXT2_ET_BAD_MAGIC) ||
661	     ((retval == 0) && ext2fs_check_desc(fs)))) {
662		if (!fs || (fs->group_desc_count > 1)) {
663			printf("%s trying backup blocks...\n",
664			       retval ? "Couldn't find ext2 superblock," :
665			       "Group descriptors look bad...");
666			superblock = 8193;
667			if (fs)
668				ext2fs_close(fs);
669			goto restart;
670		}
671	}
672	if (retval) {
673		com_err(program_name, retval, "while trying to open %s",
674			filesystem_name);
675		if (retval == EXT2_ET_REV_TOO_HIGH)
676			printf ("Get a newer version of e2fsck!\n");
677		else if (retval == EXT2_ET_SHORT_READ)
678			printf ("Could this be a zero-length partition?\n");
679		else if ((retval == EPERM) || (retval == EACCES))
680			printf("You must have %s access to the "
681			       "filesystem or be root\n",
682			       rwflag ? "r/w" : "r/o");
683		else if (retval == ENXIO)
684			printf("Possibly non-existent or swap device?\n");
685		else
686			printf(corrupt_msg);
687		fatal_error(0);
688	}
689#ifdef	EXT2_CURRENT_REV
690	if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) {
691		com_err(program_name, EXT2_ET_REV_TOO_HIGH,
692			"while trying to open %s",
693			filesystem_name);
694		goto get_newer;
695	}
696#endif
697	/*
698	 * Check for compatibility with the feature sets.  We have to
699	 * check because we need to be more stringent than ext2fs_open
700	 */
701	s = (struct ext2fs_sb *) fs->super;
702	if (s->s_feature_compat || s->s_feature_incompat ||
703	    s->s_feature_ro_compat) {
704		com_err(program_name, EXT2_ET_UNSUPP_FEATURE,
705			" (%s)", filesystem_name);
706	get_newer:
707		printf ("Get a newer version of e2fsck!\n");
708		fatal_error(0);
709	}
710
711	/*
712	 * If the user specified a specific superblock, presumably the
713	 * master superblock has been trashed.  So we mark the
714	 * superblock as dirty, so it can be written out.
715	 */
716	if (superblock && rwflag)
717		ext2fs_mark_super_dirty(fs);
718
719	/*
720	 * Don't overwrite the backup superblock and block
721	 * descriptors, until we're sure the filesystem is OK....
722	 */
723	fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
724
725	ehandler_init(fs->io);
726
727	invalid_inode_bitmap = allocate_memory(sizeof(int) *
728					       fs->group_desc_count,
729					       "invalid_inode_bitmap");
730	invalid_block_bitmap = allocate_memory(sizeof(int) *
731					       fs->group_desc_count,
732					       "invalid_block_bitmap");
733	invalid_inode_table = allocate_memory(sizeof(int) *
734					      fs->group_desc_count,
735					      "invalid_inode_table");
736
737	check_super_block(fs);
738	check_if_skip(fs);
739	if (bad_blocks_file)
740		read_bad_blocks_file(fs, bad_blocks_file, replace_bad_blocks);
741	else if (cflag)
742		test_disk(fs);
743
744	if (normalize_swapfs) {
745		if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ==
746		    ext2fs_native_flag()) {
747			fprintf(stderr, "%s: Filesystem byte order "
748				"already normalized.\n", device_name);
749			fatal_error(0);
750		}
751	}
752	if (swapfs)
753		swap_filesys(fs);
754
755	/*
756	 * Mark the system as valid, 'til proven otherwise
757	 */
758	ext2fs_mark_valid(fs);
759
760	retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
761	if (retval) {
762		com_err(program_name, retval,
763			"while reading bad blocks inode");
764		preenhalt(fs);
765		printf("This doesn't bode well, but we'll try to go on...\n");
766	}
767
768	pass1(fs);
769	free(invalid_inode_bitmap);
770	free(invalid_block_bitmap);
771	free(invalid_inode_table);
772	if (restart_e2fsck) {
773		ext2fs_close(fs);
774		printf("Restarting e2fsck from the beginning...\n");
775		restart_e2fsck = 0;
776		superblock = use_superblock;
777		goto restart;
778	}
779	pass2(fs);
780	pass3(fs);
781	pass4(fs);
782	pass5(fs);
783
784#ifdef MTRACE
785	mtrace_print("Cleanup");
786#endif
787	if (ext2fs_test_changed(fs)) {
788		exit_value = FSCK_NONDESTRUCT;
789		if (!preen)
790			printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n",
791			       device_name);
792		if (root_filesystem && !read_only_root) {
793			printf("%s: ***** REBOOT LINUX *****\n", device_name);
794			exit_value = FSCK_REBOOT;
795		}
796	}
797	if (ext2fs_test_valid(fs))
798		fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
799	else
800		exit_value = FSCK_UNCORRECTED;
801	if (rwflag) {
802		if (ext2fs_test_valid(fs)) {
803			if (!(fs->super->s_state & EXT2_VALID_FS))
804				exit_value = FSCK_NONDESTRUCT;
805			fs->super->s_state = EXT2_VALID_FS;
806		} else
807			fs->super->s_state &= ~EXT2_VALID_FS;
808		fs->super->s_mnt_count = 0;
809		fs->super->s_lastcheck = time(NULL);
810		ext2fs_mark_super_dirty(fs);
811	}
812	show_stats(fs);
813
814	write_bitmaps(fs);
815	ext2fs_close(fs);
816	sync_disks();
817
818	if (tflag)
819		print_resource_track(&global_rtrack);
820
821	return exit_value;
822}
823