e2fsck.c revision f3db3566b5e1342e49dffc5ec3f418a838584194
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#include <getopt.h>
26#include <unistd.h>
27#include <mntent.h>
28#include <sys/ioctl.h>
29#include <malloc.h>
30
31#include "et/com_err.h"
32#include "e2fsck.h"
33#include "../version.h"
34
35extern int isatty(int);
36
37const char * program_name = "e2fsck";
38const char * device_name = NULL;
39
40/* Command line options */
41int nflag = 0;
42int yflag = 0;
43int tflag = 0;			/* Do timing */
44int cflag = 0;			/* check disk */
45int preen = 0;
46int rwflag = 1;
47int inode_buffer_blocks = 0;
48blk_t superblock;
49int blocksize = 0;
50int verbose = 0;
51int list = 0;
52int debug = 0;
53int force = 0;
54int invalid_bitmaps = 0;
55static int show_version_only = 0;
56
57static int replace_bad_blocks = 0;
58static char *bad_blocks_file = 0;
59
60static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
61
62struct resource_track	global_rtrack;
63
64static int root_filesystem = 0;
65static int read_only_root = 0;
66
67int *invalid_inode_bitmap;
68int *invalid_block_bitmap;
69int *invalid_inode_table;
70int restart_e2fsck = 0;
71
72static void usage(NOARGS)
73{
74	fprintf(stderr,
75		"Usage: %s [-panyrcdfvtFV] [-b superblock] [-B blocksize]\n"
76		"\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
77		"\t\t[-l|-L bad_blocks_file] device\n", program_name);
78	exit(FSCK_USAGE);
79}
80
81static void show_stats(ext2_filsys fs)
82{
83	int inodes, inodes_used, blocks, blocks_used;
84	int dir_links;
85	int num_files, num_links;
86
87	dir_links = 2 * fs_directory_count - 1;
88	num_files = fs_total_count - dir_links;
89	num_links = fs_links_count - dir_links;
90	inodes = fs->super->s_inodes_count;
91	inodes_used = (fs->super->s_inodes_count -
92		       fs->super->s_free_inodes_count);
93	blocks = fs->super->s_blocks_count;
94	blocks_used = (fs->super->s_blocks_count -
95		       fs->super->s_free_blocks_count);
96
97	if (!verbose) {
98		printf("%s: %d/%d files, %d/%d blocks\n", device_name,
99		       inodes_used, inodes, blocks_used, blocks);
100		return;
101	}
102	printf ("\n%6d inode%s used (%d%%)\n", inodes_used,
103		(inodes_used != 1) ? "s" : "",
104		100 * inodes_used / inodes);
105	printf ("%6d block%s used (%d%%)\n"
106		"%6d bad block%s\n", blocks_used,
107		(blocks_used != 1) ? "s" : "",
108		100 * blocks_used / blocks, fs_badblocks_count,
109		fs_badblocks_count != 1 ? "s" : "");
110	printf ("\n%6d regular file%s\n"
111		"%6d director%s\n"
112		"%6d character device file%s\n"
113		"%6d block device file%s\n"
114		"%6d fifo%s\n"
115		"%6d link%s\n"
116		"%6d symbolic link%s (%d fast symbolic link%s)\n"
117		"%6d socket%s\n"
118		"------\n"
119		"%6d file%s\n",
120		fs_regular_count, (fs_regular_count != 1) ? "s" : "",
121		fs_directory_count, (fs_directory_count != 1) ? "ies" : "y",
122		fs_chardev_count, (fs_chardev_count != 1) ? "s" : "",
123		fs_blockdev_count, (fs_blockdev_count != 1) ? "s" : "",
124		fs_fifo_count, (fs_fifo_count != 1) ? "s" : "",
125		fs_links_count - dir_links,
126		((fs_links_count - dir_links) != 1) ? "s" : "",
127		fs_symlinks_count, (fs_symlinks_count != 1) ? "s" : "",
128		fs_fast_symlinks_count, (fs_fast_symlinks_count != 1) ? "s" : "",
129		fs_sockets_count, (fs_sockets_count != 1) ? "s" : "",
130		fs_total_count - dir_links,
131		((fs_total_count - dir_links) != 1) ? "s" : "");
132}
133
134static void check_mount(NOARGS)
135{
136	FILE * f;
137	struct mntent * mnt;
138	int cont;
139	int fd;
140
141	if ((f = setmntent (MOUNTED, "r")) == NULL)
142		return;
143	while ((mnt = getmntent (f)) != NULL)
144		if (strcmp (device_name, mnt->mnt_fsname) == 0)
145			break;
146	endmntent (f);
147	if (!mnt)
148		return;
149
150	if  (!strcmp(mnt->mnt_dir, "/"))
151		root_filesystem = 1;
152
153	/*
154	 * If the root is mounted read-only, then /etc/mtab is
155	 * probably not correct; so we won't issue a warning based on
156	 * it.
157	 */
158	fd = open(MOUNTED, O_RDWR);
159	if (fd < 0) {
160		if (errno == EROFS) {
161			read_only_root = 1;
162			return;
163		}
164	} else
165		close(fd);
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
221	/*
222	 * Verify the super block constants...
223	 */
224	check_super_value("inodes_count", s->s_inodes_count,
225			  MIN_CHECK, 1, 0);
226	check_super_value("blocks_count", s->s_blocks_count,
227			  MIN_CHECK, 1, 0);
228	check_super_value("first_data_block", s->s_first_data_block,
229			  MAX_CHECK, 0, s->s_blocks_count);
230	check_super_value("log_frag_size", s->s_log_frag_size,
231			  MAX_CHECK, 0, 2);
232	check_super_value("log_block_size", s->s_log_block_size,
233			  MIN_CHECK | MAX_CHECK, s->s_log_frag_size,
234			  2);
235	check_super_value("frags_per_group", s->s_frags_per_group,
236			  MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s));
237	check_super_value("blocks_per_group", s->s_blocks_per_group,
238			  MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s));
239	check_super_value("inodes_per_group", s->s_inodes_per_group,
240			  MIN_CHECK, 1, 0);
241	check_super_value("r_blocks_count", s->s_r_blocks_count,
242			  MAX_CHECK, 0, s->s_blocks_count);
243
244	if (s->s_log_block_size != s->s_log_frag_size) {
245		printf("Superblock block_size = %d, fragsize = %d.\n",
246		       EXT2_BLOCK_SIZE(s), EXT2_FRAG_SIZE(s));
247		printf("This version of e2fsck does not support fragment "
248		       "sizes different\n"
249		       "from the block size.\n");
250		fatal_error(0);
251	}
252
253	should_be = s->s_frags_per_group /
254		(s->s_log_block_size - s->s_log_frag_size + 1);
255	if (s->s_blocks_per_group != should_be) {
256		printf("Superblock blocks_per_group = %lu, should "
257		       "have been %lu\n", s->s_blocks_per_group,
258		       should_be);
259		printf(corrupt_msg);
260	}
261
262	should_be = (s->s_log_block_size == 0) ? 1 : 0;
263	if (s->s_first_data_block != should_be) {
264		printf("Superblock first_data_block = %lu, should "
265		       "have been %lu\n", s->s_first_data_block,
266		       should_be);
267		printf(corrupt_msg);
268	}
269
270	/*
271	 * Verify the group descriptors....
272	 */
273	first_block =  fs->super->s_first_data_block;
274	last_block = first_block + blocks_per_group;
275
276	for (i = 0; i < fs->group_desc_count; i++) {
277		if (i == fs->group_desc_count - 1)
278			last_block = fs->super->s_blocks_count;
279		if ((fs->group_desc[i].bg_block_bitmap < first_block) ||
280		    (fs->group_desc[i].bg_block_bitmap >= last_block)) {
281			printf("Block bitmap %lu for group %d is "
282			       "not in group.\n",
283			       fs->group_desc[i].bg_block_bitmap, i);
284			preenhalt();
285			if (!ask("Continue (and relocate)", 1)) {
286				fatal_error(0);
287			}
288			fs->group_desc[i].bg_block_bitmap = 0;
289			invalid_block_bitmap[i]++;
290			invalid_bitmaps++;
291		}
292		if ((fs->group_desc[i].bg_inode_bitmap < first_block) ||
293		    (fs->group_desc[i].bg_inode_bitmap >= last_block)) {
294			printf("Warning: Inode bitmap %lu for group %d "
295			       "not in group.\n",
296			       fs->group_desc[i].bg_inode_bitmap, i);
297			preenhalt();
298			if (!ask("Continue", 1)) {
299				fatal_error(0);
300			}
301			fs->group_desc[i].bg_inode_bitmap = 0;
302			invalid_inode_bitmap[i]++;
303			invalid_bitmaps++;
304		}
305		if ((fs->group_desc[i].bg_inode_table < first_block) ||
306		    ((fs->group_desc[i].bg_inode_table +
307		      fs->inode_blocks_per_group - 1) >= last_block)) {
308			printf("Warning: Inode table %lu for group %d "
309			       "not in group.\n",
310			       fs->group_desc[i].bg_inode_table, i);
311			printf("WARNING: SEVERE DATA LOSS POSSIBLE.\n");
312			preenhalt();
313			if (!ask("Continue", 1)) {
314				fatal_error(0);
315			}
316			fs->group_desc[i].bg_inode_table = 0;
317			invalid_inode_table[i]++;
318			invalid_bitmaps++;
319		}
320		first_block += fs->super->s_blocks_per_group;
321		last_block += fs->super->s_blocks_per_group;
322	}
323	return;
324}
325
326/*
327 * This routine checks to see if a filesystem can be skipped; if so,
328 * it will exit with E2FSCK_OK.  Under some conditions it will print a
329 * message explaining why a check is being forced.
330 */
331static void check_if_skip(ext2_filsys fs)
332{
333	const char *reason = NULL;
334
335	if (force || bad_blocks_file || cflag)
336		return;
337
338	if (fs->super->s_state & EXT2_ERROR_FS)
339		reason = "contains a file system with errors";
340	else if (fs->super->s_mnt_count >=
341		 (unsigned) fs->super->s_max_mnt_count)
342		reason = "has reached maximal mount count";
343	else if (fs->super->s_checkinterval &&
344		 time(0) >= (fs->super->s_lastcheck +
345			     fs->super->s_checkinterval))
346		reason = "has gone too long without being checked";
347	if (reason) {
348		printf("%s %s, check forced.\n", device_name, reason);
349		return;
350	}
351	if (fs->super->s_state & EXT2_VALID_FS) {
352		printf("%s is clean, no check.\n", device_name);
353		exit(FSCK_OK);
354	}
355}
356
357static void PRS(int argc, char *argv[])
358{
359	int		flush = 0;
360	char		c;
361#ifdef MTRACE
362	extern void	*mallwatch;
363#endif
364	char		*oldpath;
365	static char	newpath[PATH_MAX];
366
367	/* Update our PATH to include /sbin  */
368	strcpy(newpath, "PATH=/sbin:");
369	if ((oldpath = getenv("PATH")) != NULL)
370		strcat(newpath, oldpath);
371	putenv(newpath);
372
373	setbuf(stdout, NULL);
374	setbuf(stderr, NULL);
375	initialize_ext2_error_table();
376
377	if (argc && *argv)
378		program_name = *argv;
379	while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:")) != EOF)
380		switch (c) {
381		case 'p':
382		case 'a':
383			preen = 1;
384			yflag = nflag = 0;
385			break;
386		case 'n':
387			nflag = 1;
388			preen = yflag = 0;
389			break;
390		case 'y':
391			yflag = 1;
392			preen = nflag = 0;
393			break;
394		case 't':
395			tflag++;
396			break;
397		case 'c':
398			cflag++;
399			break;
400		case 'r':
401			/* What we do by default, anyway! */
402			break;
403		case 'b':
404			superblock = atoi(optarg);
405			break;
406		case 'B':
407			blocksize = atoi(optarg);
408			break;
409		case 'I':
410			inode_buffer_blocks = atoi(optarg);
411			break;
412		case 'P':
413			process_inode_size = atoi(optarg);
414			break;
415		case 'L':
416			replace_bad_blocks++;
417		case 'l':
418			bad_blocks_file = malloc(strlen(optarg)+1);
419			if (!bad_blocks_file)
420				fatal_error("Couldn't malloc bad_blocks_file");
421			strcpy(bad_blocks_file, optarg);
422			break;
423		case 'd':
424			debug = 1;
425			break;
426		case 'f':
427			force = 1;
428			break;
429		case 'F':
430			flush = 1;
431			break;
432		case 'v':
433			verbose = 1;
434			break;
435		case 'V':
436			show_version_only = 1;
437			break;
438#ifdef MTRACE
439		case 'M':
440			mallwatch = (void *) strtol(optarg, NULL, 0);
441			break;
442#endif
443		default:
444			usage ();
445		}
446	if (show_version_only)
447		return;
448	if (optind != argc - 1)
449		usage ();
450	if (nflag && !bad_blocks_file && !cflag)
451		rwflag = 0;
452	device_name = argv[optind];
453	if (flush) {
454		int	fd = open(device_name, O_RDONLY, 0);
455
456		if (fd < 0) {
457			com_err("open", errno, "while opening %s for flushing",
458				device_name);
459			exit(FSCK_ERROR);
460		}
461		if (ioctl(fd, BLKFLSBUF, 0) < 0) {
462			com_err("BLKFLSBUF", errno, "while trying to flush %s",
463				device_name);
464			exit(FSCK_ERROR);
465		}
466		close(fd);
467	}
468}
469
470int main (int argc, char *argv[])
471{
472	errcode_t	retval = 0;
473	int		exit_value = FSCK_OK;
474	int		i;
475	ext2_filsys	fs;
476
477#ifdef MTRACE
478	mtrace();
479#endif
480#ifdef MCHECK
481	mcheck(0);
482#endif
483
484	init_resource_track(&global_rtrack);
485
486	PRS(argc, argv);
487
488	if (!preen)
489		fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n",
490			 E2FSPROGS_VERSION, E2FSPROGS_DATE,
491			 EXT2FS_VERSION, EXT2FS_DATE);
492
493	if (show_version_only)
494		exit(0);
495
496	check_mount();
497
498	if (!preen && !nflag && !yflag) {
499		if (!isatty (0) || !isatty (1))
500			die ("need terminal for interactive repairs");
501	}
502restart:
503	sync_disks();
504	if (superblock && blocksize) {
505		retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0,
506				     superblock, blocksize, unix_io_manager,
507				     &fs);
508	} else if (superblock) {
509		for (i=0; possible_block_sizes[i]; i++) {
510			retval = ext2fs_open(device_name,
511					     rwflag ? EXT2_FLAG_RW : 0,
512					     superblock,
513					     possible_block_sizes[i],
514					     unix_io_manager, &fs);
515			if (!retval)
516				break;
517		}
518	} else
519		retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0,
520				     0, 0, unix_io_manager, &fs);
521	if (retval) {
522		com_err(program_name, retval, "while trying to open %s",
523			device_name);
524		if (retval == EXT2_ET_REV_TOO_HIGH)
525			printf ("Get a newer version of e2fsck!\n");
526		else
527			printf(corrupt_msg);
528		fatal_error(0);
529	}
530
531#ifdef	EXT2_CURRENT_REV
532	if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) {
533		com_err(program_name, retval, "while trying to open %s",
534			device_name);
535		printf ("Get a newer version of e2fsck!\n");
536		fatal_error(0);
537	}
538#endif
539	/*
540	 * If the user specified a specific superblock, presumably the
541	 * master superblock has been trashed.  So we mark the
542	 * superblock as dirty, so it can be written out.
543	 */
544	if (superblock && rwflag)
545		ext2fs_mark_super_dirty(fs);
546
547	ehandler_init(fs->io);
548
549	invalid_inode_bitmap = allocate_memory(sizeof(int) *
550					       fs->group_desc_count,
551					       "invalid_inode_bitmap");
552	invalid_block_bitmap = allocate_memory(sizeof(int) *
553					       fs->group_desc_count,
554					       "invalid_block_bitmap");
555	invalid_inode_table = allocate_memory(sizeof(int) *
556					      fs->group_desc_count,
557					      "invalid_inode_table");
558
559	check_super_block(fs);
560	check_if_skip(fs);
561	if (bad_blocks_file)
562		read_bad_blocks_file(fs, bad_blocks_file, replace_bad_blocks);
563	else if (cflag)
564		test_disk(fs);
565
566	/*
567	 * Mark the system as valid, 'til proven otherwise
568	 */
569	ext2fs_mark_valid(fs);
570
571	pass1(fs);
572	if (restart_e2fsck) {
573		ext2fs_close(fs);
574		printf("Restarting e2fsck from the beginning...\n");
575		restart_e2fsck = 0;
576		goto restart;
577	}
578	pass2(fs);
579	pass3(fs);
580	pass4(fs);
581	pass5(fs);
582
583#ifdef MTRACE
584	mtrace_print("Cleanup");
585#endif
586	if (ext2fs_test_changed(fs)) {
587		exit_value = FSCK_NONDESTRUCT;
588		if (!preen)
589			printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n",
590			       device_name);
591		if (root_filesystem && !read_only_root) {
592			printf("%s: ***** REBOOT LINUX *****\n", device_name);
593			exit_value = FSCK_REBOOT;
594		}
595	}
596	if (!ext2fs_test_valid(fs))
597		exit_value = FSCK_UNCORRECTED;
598	if (rwflag) {
599		if (ext2fs_test_valid(fs))
600			fs->super->s_state = EXT2_VALID_FS;
601		else
602			fs->super->s_state &= ~EXT2_VALID_FS;
603		fs->super->s_mnt_count = 0;
604		fs->super->s_lastcheck = time(NULL);
605		ext2fs_mark_super_dirty(fs);
606	}
607	show_stats(fs);
608
609	write_bitmaps(fs);
610	ext2fs_close(fs);
611	sync_disks();
612
613	if (tflag)
614		print_resource_track(&global_rtrack);
615
616	return exit_value;
617}
618