e2fsck.c revision 3839e65723771b85975f4263102dd3ceec4523c0
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;
54static int show_version_only = 0;
55
56static int replace_bad_blocks = 0;
57static char *bad_blocks_file = 0;
58
59static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
60
61struct resource_track	global_rtrack;
62
63static int root_filesystem = 0;
64static int read_only_root = 0;
65
66static void usage(NOARGS)
67{
68	fprintf(stderr,
69		"Usage: %s [-panyrdfvtFV] [-b superblock] [-B blocksize]\n"
70		"\t\tdevice\n", program_name);
71	exit(FSCK_USAGE);
72}
73
74static void show_stats(ext2_filsys fs)
75{
76	int inodes, inodes_used, blocks, blocks_used;
77	int dir_links;
78	int num_files, num_links;
79
80	dir_links = 2 * fs_directory_count - 1;
81	num_files = fs_total_count - dir_links;
82	num_links = fs_links_count - dir_links;
83	inodes = fs->super->s_inodes_count;
84	inodes_used = (fs->super->s_inodes_count -
85		       fs->super->s_free_inodes_count);
86	blocks = fs->super->s_blocks_count;
87	blocks_used = (fs->super->s_blocks_count -
88		       fs->super->s_free_blocks_count);
89
90	if (!verbose) {
91		printf("%s: %d/%d files, %d/%d blocks\n", device_name,
92		       inodes_used, inodes, blocks_used, blocks);
93		return;
94	}
95	printf ("\n%6d inode%s used (%d%%)\n", inodes_used,
96		(inodes_used != 1) ? "s" : "",
97		100 * inodes_used / inodes);
98	printf ("%6d block%s used (%d%%)\n"
99		"%6d bad block%s\n", blocks_used,
100		(blocks_used != 1) ? "s" : "",
101		100 * blocks_used / blocks, fs_badblocks_count,
102		fs_badblocks_count != 1 ? "s" : "");
103	printf ("\n%6d regular file%s\n"
104		"%6d director%s\n"
105		"%6d character device file%s\n"
106		"%6d block device file%s\n"
107		"%6d fifo%s\n"
108		"%6d link%s\n"
109		"%6d symbolic link%s (%d fast symbolic link%s)\n"
110		"%6d socket%s\n"
111		"------\n"
112		"%6d file%s\n",
113		fs_regular_count, (fs_regular_count != 1) ? "s" : "",
114		fs_directory_count, (fs_directory_count != 1) ? "ies" : "y",
115		fs_chardev_count, (fs_chardev_count != 1) ? "s" : "",
116		fs_blockdev_count, (fs_blockdev_count != 1) ? "s" : "",
117		fs_fifo_count, (fs_fifo_count != 1) ? "s" : "",
118		fs_links_count - dir_links,
119		((fs_links_count - dir_links) != 1) ? "s" : "",
120		fs_symlinks_count, (fs_symlinks_count != 1) ? "s" : "",
121		fs_fast_symlinks_count, (fs_fast_symlinks_count != 1) ? "s" : "",
122		fs_sockets_count, (fs_sockets_count != 1) ? "s" : "",
123		fs_total_count - dir_links,
124		((fs_total_count - dir_links) != 1) ? "s" : "");
125}
126
127static void check_mount(NOARGS)
128{
129	FILE * f;
130	struct mntent * mnt;
131	int cont;
132	int fd;
133
134	if ((f = setmntent (MOUNTED, "r")) == NULL)
135		return;
136	while ((mnt = getmntent (f)) != NULL)
137		if (strcmp (device_name, mnt->mnt_fsname) == 0)
138			break;
139	endmntent (f);
140	if (!mnt)
141		return;
142
143	if  (!strcmp(mnt->mnt_dir, "/"))
144		root_filesystem = 1;
145
146	/*
147	 * If the root is mounted read-only, then /etc/mtab is
148	 * probably not correct; so we won't issue a warning based on
149	 * it.
150	 */
151	fd = open(MOUNTED, O_RDWR);
152	if (fd < 0) {
153		if (errno == EROFS) {
154			read_only_root = 1;
155			return;
156		}
157	} else
158		close(fd);
159
160	if (!rwflag) {
161		printf("Warning!  %s is mounted.\n", device_name);
162		return;
163	}
164
165	printf ("%s is mounted.  ", device_name);
166	if (isatty (0) && isatty (1))
167		cont = ask_yn("Do you really want to continue", -1);
168	else
169		cont = 0;
170	if (!cont) {
171		printf ("check aborted.\n");
172		exit (0);
173	}
174	return;
175}
176
177static void sync_disks(NOARGS)
178{
179	sync();
180	sync();
181	sleep(1);
182	sync();
183}
184
185static void check_super_block(ext2_filsys fs)
186{
187	blk_t	first_block, last_block;
188	int	blocks_per_group = fs->super->s_blocks_per_group;
189	int	i;
190
191	first_block =  fs->super->s_first_data_block;
192	last_block = first_block + blocks_per_group;
193
194	for (i = 0; i < fs->group_desc_count; i++) {
195		if ((fs->group_desc[i].bg_block_bitmap < first_block) ||
196		    (fs->group_desc[i].bg_block_bitmap >= last_block)) {
197			printf("Block bitmap %ld for group %d not in group.\n",
198			       fs->group_desc[i].bg_block_bitmap, i);
199			fatal_error(0);
200		}
201		if ((fs->group_desc[i].bg_inode_bitmap < first_block) ||
202		    (fs->group_desc[i].bg_inode_bitmap >= last_block)) {
203			printf("Inode bitmap %ld for group %d not in group.\n",
204			       fs->group_desc[i].bg_inode_bitmap, i);
205			fatal_error(0);
206		}
207		if ((fs->group_desc[i].bg_inode_table < first_block) ||
208		    ((fs->group_desc[i].bg_inode_table +
209		      fs->inode_blocks_per_group - 1) >= last_block)) {
210			printf("Inode table %ld for group %d not in group.\n",
211			       fs->group_desc[i].bg_inode_table, i);
212			fatal_error(0);
213		}
214		first_block += fs->super->s_blocks_per_group;
215		last_block += fs->super->s_blocks_per_group;
216	}
217	return;
218}
219
220/*
221 * This routine checks to see if a filesystem can be skipped; if so,
222 * it will exit with E2FSCK_OK.  Under some conditions it will print a
223 * message explaining why a check is being forced.
224 */
225static void check_if_skip(ext2_filsys fs)
226{
227	const char *reason = NULL;
228
229	if (force || bad_blocks_file || cflag)
230		return;
231
232	if (fs->super->s_state & EXT2_ERROR_FS)
233		reason = "contains a file system with errors";
234	else if (fs->super->s_mnt_count >=
235		 (unsigned) fs->super->s_max_mnt_count)
236		reason = "has reached maximal mount count";
237	else if (fs->super->s_checkinterval &&
238		 time(0) >= (fs->super->s_lastcheck +
239			     fs->super->s_checkinterval))
240		reason = "has gone too long without being checked";
241	if (reason) {
242		printf("%s %s, check forced.\n", device_name, reason);
243		return;
244	}
245	if (fs->super->s_state & EXT2_VALID_FS) {
246		printf("%s is clean, no check.\n", device_name);
247		exit(FSCK_OK);
248	}
249}
250
251static void PRS(int argc, char *argv[])
252{
253	int flush = 0;
254	char c;
255#ifdef MTRACE
256	extern void *mallwatch;
257#endif
258	char *oldpath, newpath[PATH_MAX];
259
260	/* Update our PATH to include /sbin  */
261	strcpy(newpath, "PATH=/sbin:");
262	if ((oldpath = getenv("PATH")) != NULL)
263		strcat(newpath, oldpath);
264	putenv(newpath);
265
266	setbuf(stdout, NULL);
267	setbuf(stderr, NULL);
268	initialize_ext2_error_table();
269
270	if (argc && *argv)
271		program_name = *argv;
272	while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:")) != EOF)
273		switch (c) {
274		case 'p':
275		case 'a':
276			preen = 1;
277			yflag = nflag = 0;
278			break;
279		case 'n':
280			nflag = 1;
281			preen = yflag = 0;
282			break;
283		case 'y':
284			yflag = 1;
285			preen = nflag = 0;
286			break;
287		case 't':
288			tflag++;
289			break;
290		case 'c':
291			cflag++;
292			break;
293		case 'r':
294			/* What we do by default, anyway! */
295			break;
296		case 'b':
297			superblock = atoi(optarg);
298			break;
299		case 'B':
300			blocksize = atoi(optarg);
301			break;
302		case 'I':
303			inode_buffer_blocks = atoi(optarg);
304			break;
305		case 'P':
306			process_inode_size = atoi(optarg);
307			break;
308		case 'L':
309			replace_bad_blocks++;
310		case 'l':
311			bad_blocks_file = malloc(strlen(optarg)+1);
312			if (!bad_blocks_file)
313				fatal_error("Couldn't malloc bad_blocks_file");
314			strcpy(bad_blocks_file, optarg);
315			break;
316		case 'd':
317			debug = 1;
318			break;
319		case 'f':
320			force = 1;
321			break;
322		case 'F':
323			flush = 1;
324			break;
325		case 'v':
326			verbose = 1;
327			break;
328		case 'V':
329			show_version_only = 1;
330			break;
331#ifdef MTRACE
332		case 'M':
333			mallwatch = (void *) strtol(optarg, NULL, 0);
334			break;
335#endif
336		default:
337			usage ();
338		}
339	if (show_version_only)
340		return;
341	if (optind != argc - 1)
342		usage ();
343	if (nflag && !bad_blocks_file && !cflag)
344		rwflag = 0;
345	device_name = argv[optind];
346	if (flush) {
347		int	fd = open(device_name, O_RDONLY, 0);
348
349		if (fd < 0) {
350			com_err("open", errno, "while opening %s for flushing",
351				device_name);
352			exit(FSCK_ERROR);
353		}
354		if (ioctl(fd, BLKFLSBUF, 0) < 0) {
355			com_err("BLKFLSBUF", errno, "while trying to flush %s",
356				device_name);
357			exit(FSCK_ERROR);
358		}
359		close(fd);
360	}
361}
362
363int main (int argc, char *argv[])
364{
365	errcode_t	retval = 0;
366	int		exit_value = FSCK_OK;
367	int		i;
368	ext2_filsys	fs;
369
370#ifdef MTRACE
371	mtrace();
372#endif
373#ifdef MCHECK
374	mcheck(0);
375#endif
376
377	init_resource_track(&global_rtrack);
378
379	PRS(argc, argv);
380
381	if (!preen)
382		fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n",
383			 E2FSPROGS_VERSION, E2FSPROGS_DATE,
384			 EXT2FS_VERSION, EXT2FS_DATE);
385
386	if (show_version_only)
387		exit(0);
388
389	check_mount();
390
391	if (!preen && !nflag && !yflag) {
392		if (!isatty (0) || !isatty (1))
393			die ("need terminal for interactive repairs");
394	}
395	sync_disks();
396	if (superblock && blocksize) {
397		retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0,
398				     superblock, blocksize, unix_io_manager,
399				     &fs);
400	} else if (superblock) {
401		for (i=0; possible_block_sizes[i]; i++) {
402			retval = ext2fs_open(device_name,
403					     rwflag ? EXT2_FLAG_RW : 0,
404					     superblock,
405					     possible_block_sizes[i],
406					     unix_io_manager, &fs);
407			if (!retval)
408				break;
409		}
410	} else
411		retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0,
412				     0, 0, unix_io_manager, &fs);
413	if (retval) {
414		com_err(program_name, retval, "while trying to open %s",
415			device_name);
416		printf("Couldn't find valid filesystem superblock.\n");
417		fatal_error(0);
418	}
419	/*
420	 * If the user specified a specific superblock, presumably the
421	 * master superblock has been trashed.  So we mark the
422	 * superblock as dirty, so it can be written out.
423	 */
424	if (superblock && rwflag)
425		ext2fs_mark_super_dirty(fs);
426
427	ehandler_init(fs->io);
428
429	check_super_block(fs);
430	check_if_skip(fs);
431	if (bad_blocks_file)
432		read_bad_blocks_file(fs, bad_blocks_file, replace_bad_blocks);
433	else if (cflag)
434		test_disk(fs);
435
436	/*
437	 * Mark the system as valid, 'til proven otherwise
438	 */
439	ext2fs_mark_valid(fs);
440
441	pass1(fs);
442	pass2(fs);
443	pass3(fs);
444	pass4(fs);
445	pass5(fs);
446
447#ifdef MTRACE
448	mtrace_print("Cleanup");
449#endif
450	if (ext2fs_test_changed(fs)) {
451		exit_value = FSCK_NONDESTRUCT;
452		if (!preen)
453			printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n",
454			       device_name);
455		if (root_filesystem && !read_only_root) {
456			printf("%s: ***** REBOOT LINUX *****\n", device_name);
457			exit_value = FSCK_REBOOT;
458		}
459	}
460	if (!ext2fs_test_valid(fs))
461		exit_value = FSCK_UNCORRECTED;
462	if (rwflag) {
463		if (ext2fs_test_valid(fs))
464			fs->super->s_state = EXT2_VALID_FS;
465		else
466			fs->super->s_state &= ~EXT2_VALID_FS;
467		fs->super->s_mnt_count = 0;
468		fs->super->s_lastcheck = time(NULL);
469		ext2fs_mark_super_dirty(fs);
470	}
471	show_stats(fs);
472
473	write_bitmaps(fs);
474	ext2fs_close(fs);
475	sync_disks();
476
477	if (tflag)
478		print_resource_track(&global_rtrack);
479
480	return exit_value;
481}
482