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