unix.c revision f8188fff23dc2d9c9f858fb21264e46b17672825
1/*
2 * unix.c - The unix-specific code for e2fsck
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#include <stdio.h>
13#ifdef HAVE_STDLIB_H
14#include <stdlib.h>
15#endif
16#include <string.h>
17#include <fcntl.h>
18#include <ctype.h>
19#include <termios.h>
20#include <time.h>
21#ifdef HAVE_GETOPT_H
22#include <getopt.h>
23#endif
24#include <unistd.h>
25#ifdef HAVE_ERRNO_H
26#include <errno.h>
27#endif
28#ifdef HAVE_MNTENT_H
29#include <mntent.h>
30#endif
31#include <sys/ioctl.h>
32#include <malloc.h>
33
34#include "et/com_err.h"
35#include "e2fsck.h"
36#include "problem.h"
37#include "../version.h"
38
39extern int isatty(int);
40
41/* Command line options */
42static int blocksize = 0;
43static int swapfs = 0;
44static int normalize_swapfs = 0;
45static int cflag = 0;		/* check disk */
46static int show_version_only = 0;
47static int force = 0;
48static int verbose = 0;
49
50static int replace_bad_blocks = 0;
51static char *bad_blocks_file = 0;
52
53static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
54
55static int root_filesystem = 0;
56static int read_only_root = 0;
57
58static void usage(e2fsck_t ctx)
59{
60	fprintf(stderr,
61		"Usage: %s [-panyrcdfvstFSV] [-b superblock] [-B blocksize]\n"
62		"\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
63		"\t\t[-l|-L bad_blocks_file] device\n", ctx->program_name);
64	exit(FSCK_USAGE);
65}
66
67static void show_stats(e2fsck_t	ctx)
68{
69	ext2_filsys fs = ctx->fs;
70	int inodes, inodes_used, blocks, blocks_used;
71	int dir_links;
72	int num_files, num_links;
73	int frag_percent;
74
75	dir_links = 2 * ctx->fs_directory_count - 1;
76	num_files = ctx->fs_total_count - dir_links;
77	num_links = ctx->fs_links_count - dir_links;
78	inodes = fs->super->s_inodes_count;
79	inodes_used = (fs->super->s_inodes_count -
80		       fs->super->s_free_inodes_count);
81	blocks = fs->super->s_blocks_count;
82	blocks_used = (fs->super->s_blocks_count -
83		       fs->super->s_free_blocks_count);
84
85	frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
86	frag_percent = (frag_percent + 5) / 10;
87
88	if (!verbose) {
89		printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
90		       ctx->device_name, inodes_used, inodes,
91		       frag_percent / 10, frag_percent % 10,
92		       blocks_used, blocks);
93		return;
94	}
95	printf ("\n%8d inode%s used (%d%%)\n", inodes_used,
96		(inodes_used != 1) ? "s" : "",
97		100 * inodes_used / inodes);
98	printf ("%8d non-contiguous inodes (%0d.%d%%)\n",
99		ctx->fs_fragmented, frag_percent / 10, frag_percent % 10);
100	printf ("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n",
101		ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
102	printf ("%8d block%s used (%d%%)\n"
103		"%8d bad block%s\n", blocks_used,
104		(blocks_used != 1) ? "s" : "",
105		100 * blocks_used / blocks, ctx->fs_badblocks_count,
106		ctx->fs_badblocks_count != 1 ? "s" : "");
107	printf ("\n%8d regular file%s\n"
108		"%8d director%s\n"
109		"%8d character device file%s\n"
110		"%8d block device file%s\n"
111		"%8d fifo%s\n"
112		"%8d link%s\n"
113		"%8d symbolic link%s (%d fast symbolic link%s)\n"
114		"%8d socket%s\n"
115		"--------\n"
116		"%8d file%s\n",
117		ctx->fs_regular_count,
118		(ctx->fs_regular_count != 1) ? "s" : "",
119		ctx->fs_directory_count,
120		(ctx->fs_directory_count != 1) ? "ies" : "y",
121		ctx->fs_chardev_count,
122		(ctx->fs_chardev_count != 1) ? "s" : "",
123		ctx->fs_blockdev_count,
124		(ctx->fs_blockdev_count != 1) ? "s" : "",
125		ctx->fs_fifo_count,
126		(ctx->fs_fifo_count != 1) ? "s" : "",
127		ctx->fs_links_count - dir_links,
128		((ctx->fs_links_count - dir_links) != 1) ? "s" : "",
129		ctx->fs_symlinks_count,
130		(ctx->fs_symlinks_count != 1) ? "s" : "",
131		ctx->fs_fast_symlinks_count,
132		(ctx->fs_fast_symlinks_count != 1) ? "s" : "",
133		ctx->fs_sockets_count, (ctx->fs_sockets_count != 1) ? "s" : "",
134		ctx->fs_total_count - dir_links,
135		((ctx->fs_total_count - dir_links) != 1) ? "s" : "");
136}
137
138static void check_mount(e2fsck_t ctx)
139{
140	errcode_t	retval;
141	int		mount_flags, cont, fd;
142
143	retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
144	if (retval) {
145		com_err("ext2fs_check_if_mount", retval,
146			"while determining whether %s is mounted.",
147			ctx->filesystem_name);
148		return;
149	}
150	if (!(mount_flags & EXT2_MF_MOUNTED))
151		return;
152
153#if (defined(__linux__) && defined(HAVE_MNTENT_H))
154	/*
155	 * If the root is mounted read-only, then /etc/mtab is
156	 * probably not correct; so we won't issue a warning based on
157	 * it.
158	 */
159	fd = open(MOUNTED, O_RDWR);
160	if (fd < 0) {
161		if (errno == EROFS)
162			return;
163	} else
164		close(fd);
165#endif
166
167	if (ctx->options & E2F_OPT_READONLY) {
168		printf("Warning!  %s is mounted.\n", ctx->device_name);
169		return;
170	}
171
172	printf("%s is mounted.\n\n", ctx->device_name);
173	printf("\007\007\007\007WARNING!!!  Running e2fsck on a mounted filesystem "
174	       "may cause\nSEVERE filesystem damage.\007\007\007\n\n");
175	if (isatty (0) && isatty (1))
176		cont = ask_yn("Do you really want to continue", -1);
177	else
178		cont = 0;
179	if (!cont) {
180		printf ("check aborted.\n");
181		exit (0);
182	}
183	return;
184}
185
186static void sync_disks(NOARGS)
187{
188	sync();
189	sync();
190	sleep(1);
191	sync();
192}
193
194/*
195 * This routine checks to see if a filesystem can be skipped; if so,
196 * it will exit with E2FSCK_OK.  Under some conditions it will print a
197 * message explaining why a check is being forced.
198 */
199static void check_if_skip(e2fsck_t ctx)
200{
201	ext2_filsys fs = ctx->fs;
202	const char *reason = NULL;
203
204	if (force || bad_blocks_file || cflag || swapfs)
205		return;
206
207	if (fs->super->s_state & EXT2_ERROR_FS)
208		reason = "contains a file system with errors";
209	else if (fs->super->s_mnt_count >=
210		 (unsigned) fs->super->s_max_mnt_count)
211		reason = "has reached maximal mount count";
212	else if (fs->super->s_checkinterval &&
213		 time(0) >= (fs->super->s_lastcheck +
214			     fs->super->s_checkinterval))
215		reason = "has gone too long without being checked";
216	else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
217		reason = "was not cleanly unmounted";
218	if (reason) {
219		printf("%s %s, check forced.\n", ctx->device_name, reason);
220		return;
221	}
222	printf("%s: clean, %d/%d files, %d/%d blocks\n", ctx->device_name,
223	       fs->super->s_inodes_count - fs->super->s_free_inodes_count,
224	       fs->super->s_inodes_count,
225	       fs->super->s_blocks_count - fs->super->s_free_blocks_count,
226	       fs->super->s_blocks_count);
227	ext2fs_close(fs);
228	exit(FSCK_OK);
229}
230
231
232#define PATH_SET "PATH=/sbin"
233
234static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
235{
236	int		flush = 0;
237	int		c;
238#ifdef MTRACE
239	extern void	*mallwatch;
240#endif
241	char		*oldpath = getenv("PATH");
242	e2fsck_t	ctx;
243	errcode_t	retval;
244
245	retval = e2fsck_allocate_context(&ctx);
246	if (retval)
247		return retval;
248
249	*ret_ctx = ctx;
250
251	/* Update our PATH to include /sbin  */
252	if (oldpath) {
253		char *newpath;
254
255		newpath = malloc(sizeof (PATH_SET) + 1 + strlen (oldpath));
256		if (!newpath)
257			fatal_error(ctx, "Couldn't malloc() newpath");
258		strcpy (newpath, PATH_SET);
259		strcat (newpath, ":");
260		strcat (newpath, oldpath);
261		putenv (newpath);
262	} else
263		putenv (PATH_SET);
264
265	setbuf(stdout, NULL);
266	setbuf(stderr, NULL);
267	initialize_ext2_error_table();
268
269	if (argc && *argv)
270		ctx->program_name = *argv;
271	else
272		ctx->program_name = "e2fsck";
273	while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:N:Ss")) != EOF)
274		switch (c) {
275		case 'p':
276		case 'a':
277			ctx->options |= E2F_OPT_PREEN;
278			ctx->options &= ~(E2F_OPT_YES|E2F_OPT_NO);
279			break;
280		case 'n':
281			ctx->options |= E2F_OPT_NO;
282			ctx->options &= ~(E2F_OPT_YES|E2F_OPT_PREEN);
283			break;
284		case 'y':
285			ctx->options |= E2F_OPT_YES;
286			ctx->options &= ~(E2F_OPT_PREEN|E2F_OPT_NO);
287			break;
288		case 't':
289#ifdef RESOURCE_TRACK
290			if (ctx->options & E2F_OPT_TIME)
291				ctx->options |= E2F_OPT_TIME2;
292			else
293				ctx->options |= E2F_OPT_TIME;
294#else
295			fprintf(stderr, "The -t option is not "
296				"supported on this version of e2fsck.\n");
297#endif
298			break;
299		case 'c':
300			cflag++;
301			ctx->options |= E2F_OPT_CHECKBLOCKS;
302			break;
303		case 'r':
304			/* What we do by default, anyway! */
305			break;
306		case 'b':
307			ctx->use_superblock = atoi(optarg);
308			break;
309		case 'B':
310			blocksize = atoi(optarg);
311			break;
312		case 'I':
313			ctx->inode_buffer_blocks = atoi(optarg);
314			break;
315		case 'P':
316			ctx->process_inode_size = atoi(optarg);
317			break;
318		case 'L':
319			replace_bad_blocks++;
320		case 'l':
321			bad_blocks_file = malloc(strlen(optarg)+1);
322			if (!bad_blocks_file)
323				fatal_error(ctx,
324					    "Couldn't malloc bad_blocks_file");
325			strcpy(bad_blocks_file, optarg);
326			break;
327		case 'd':
328			ctx->options |= E2F_OPT_DEBUG;
329			break;
330		case 'f':
331			force = 1;
332			break;
333		case 'F':
334#ifdef BLKFLSBUF
335			flush = 1;
336#else
337			fatal_error(ctx, "-F not supported");
338#endif
339			break;
340		case 'v':
341			verbose = 1;
342			break;
343		case 'V':
344			show_version_only = 1;
345			break;
346#ifdef MTRACE
347		case 'M':
348			mallwatch = (void *) strtol(optarg, NULL, 0);
349			break;
350#endif
351		case 'N':
352			ctx->device_name = optarg;
353			break;
354		case 's':
355			normalize_swapfs = 1;
356		case 'S':
357			swapfs = 1;
358			break;
359		default:
360			usage(ctx);
361		}
362	if (show_version_only)
363		return 0;
364	if (optind != argc - 1)
365		usage(ctx);
366	if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file &&
367	    !cflag && !swapfs)
368		ctx->options |= E2F_OPT_READONLY;
369	ctx->filesystem_name = argv[optind];
370	if (ctx->device_name == 0)
371		ctx->device_name = ctx->filesystem_name;
372	if (flush) {
373#ifdef BLKFLSBUF
374		int	fd = open(ctx->filesystem_name, O_RDONLY, 0);
375
376		if (fd < 0) {
377			com_err("open", errno, "while opening %s for flushing",
378				ctx->filesystem_name);
379			exit(FSCK_ERROR);
380		}
381		if (ioctl(fd, BLKFLSBUF, 0) < 0) {
382			com_err("BLKFLSBUF", errno, "while trying to flush %s",
383				ctx->filesystem_name);
384			exit(FSCK_ERROR);
385		}
386		close(fd);
387#else
388		fatal_error(ctx, "BLKFLSBUF not supported");
389#endif /* BLKFLSBUF */
390	}
391	if (swapfs) {
392		if (cflag || bad_blocks_file) {
393			fprintf(stderr, "Incompatible options not "
394				"allowed when byte-swapping.\n");
395			exit(FSCK_ERROR);
396		}
397	}
398	return 0;
399}
400
401static const char *my_ver_string = E2FSPROGS_VERSION;
402static const char *my_ver_date = E2FSPROGS_DATE;
403
404int main (int argc, char *argv[])
405{
406	errcode_t	retval = 0;
407	int		exit_value = FSCK_OK;
408	int		i;
409	ext2_filsys	fs = 0;
410	io_manager	io_ptr;
411	struct ext2fs_sb *s;
412	const char	*lib_ver_date;
413	int		my_ver, lib_ver;
414	e2fsck_t	ctx;
415	struct problem_context pctx;
416	int flags, run_result;
417
418	clear_problem_context(&pctx);
419#ifdef MTRACE
420	mtrace();
421#endif
422#ifdef MCHECK
423	mcheck(0);
424#endif
425	my_ver = ext2fs_parse_version_string(my_ver_string);
426	lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
427	if (my_ver > lib_ver) {
428		fprintf( stderr, "Error: ext2fs library version "
429			"out of date!\n");
430		show_version_only++;
431	}
432
433	retval = PRS(argc, argv, &ctx);
434	if (retval) {
435		com_err("e2fsck", retval,
436			"while trying to initialize program");
437		exit(1);
438	}
439
440#ifdef RESOURCE_TRACK
441	init_resource_track(&ctx->global_rtrack);
442#endif
443
444	if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
445		fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n",
446			 my_ver_string, my_ver_date, EXT2FS_VERSION,
447			 EXT2FS_DATE);
448
449	if (show_version_only) {
450		fprintf(stderr, "\tUsing %s, %s\n",
451			error_message(EXT2_ET_BASE), lib_ver_date);
452		exit(0);
453	}
454
455	check_mount(ctx);
456
457	if (!(ctx->options & E2F_OPT_PREEN) &&
458	    !(ctx->options & E2F_OPT_NO) &&
459	    !(ctx->options & E2F_OPT_YES)) {
460		if (!isatty (0) || !isatty (1))
461			fatal_error(ctx,
462				    "need terminal for interactive repairs");
463	}
464	ctx->superblock = ctx->use_superblock;
465restart:
466#if 1
467	io_ptr = unix_io_manager;
468#else
469	io_ptr = test_io_manager;
470	test_io_backing_manager = unix_io_manager;
471#endif
472	sync_disks();
473	flags = (ctx->options & E2F_OPT_READONLY) ? 0 : EXT2_FLAG_RW;
474	if (ctx->superblock && blocksize) {
475		retval = ext2fs_open(ctx->filesystem_name, flags,
476				     ctx->superblock, blocksize, io_ptr, &fs);
477	} else if (ctx->superblock) {
478		for (i=0; possible_block_sizes[i]; i++) {
479			retval = ext2fs_open(ctx->filesystem_name, flags,
480					     ctx->superblock,
481					     possible_block_sizes[i],
482					     io_ptr, &fs);
483			if (!retval)
484				break;
485		}
486	} else
487		retval = ext2fs_open(ctx->filesystem_name, flags,
488				     0, 0, io_ptr, &fs);
489	if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
490	    ((retval == EXT2_ET_BAD_MAGIC) ||
491	     ((retval == 0) && ext2fs_check_desc(fs)))) {
492		if (!fs || (fs->group_desc_count > 1)) {
493			printf("%s trying backup blocks...\n",
494			       retval ? "Couldn't find ext2 superblock," :
495			       "Group descriptors look bad...");
496			ctx->superblock = get_backup_sb(fs);
497			if (fs)
498				ext2fs_close(fs);
499			goto restart;
500		}
501	}
502	if (retval) {
503		com_err(ctx->program_name, retval, "while trying to open %s",
504			ctx->filesystem_name);
505		if (retval == EXT2_ET_REV_TOO_HIGH)
506			printf ("Get a newer version of e2fsck!\n");
507		else if (retval == EXT2_ET_SHORT_READ)
508			printf ("Could this be a zero-length partition?\n");
509		else if ((retval == EPERM) || (retval == EACCES))
510			printf("You must have %s access to the "
511			       "filesystem or be root\n",
512			       (ctx->options & E2F_OPT_READONLY) ?
513			       "r/o" : "r/w");
514		else if (retval == ENXIO)
515			printf("Possibly non-existent or swap device?\n");
516#ifdef EROFS
517		else if (retval == EROFS)
518			printf("Disk write-protected; use the -n option"
519			       "to do a read-only\n"
520			       "check of the device.\n");
521
522#endif
523		else
524			fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
525		exit(FSCK_ERROR);
526	}
527	ctx->fs = fs;
528	fs->private = ctx;
529#ifdef	EXT2_CURRENT_REV
530	if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) {
531		com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH,
532			"while trying to open %s",
533			ctx->filesystem_name);
534	get_newer:
535		fatal_error(ctx, "Get a newer version of e2fsck!");
536	}
537#endif
538	/*
539	 * Check for compatibility with the feature sets.  We need to
540	 * be more stringent than ext2fs_open().
541	 */
542	s = (struct ext2fs_sb *) fs->super;
543	if ((s->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
544	    (s->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
545		com_err(ctx->program_name, EXT2_ET_UNSUPP_FEATURE,
546			"(%s)", ctx->filesystem_name);
547		goto get_newer;
548	}
549	if (s->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
550		com_err(ctx->program_name, EXT2_ET_RO_UNSUPP_FEATURE,
551			"(%s)", ctx->filesystem_name);
552		goto get_newer;
553	}
554
555	/*
556	 * If the user specified a specific superblock, presumably the
557	 * master superblock has been trashed.  So we mark the
558	 * superblock as dirty, so it can be written out.
559	 */
560	if (ctx->superblock &&
561	    !(ctx->options & E2F_OPT_READONLY))
562		ext2fs_mark_super_dirty(fs);
563
564	/*
565	 * Don't overwrite the backup superblock and block
566	 * descriptors, until we're sure the filesystem is OK....
567	 */
568	fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
569
570	ehandler_init(fs->io);
571
572	if (ctx->superblock)
573		set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
574	check_super_block(ctx);
575	if (ctx->flags & E2F_FLAG_ABORT)
576		exit(FSCK_ERROR);
577	check_if_skip(ctx);
578	if (bad_blocks_file)
579		read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
580	else if (cflag)
581		test_disk(ctx);
582	if (ctx->flags & E2F_FLAG_ABORT)
583		exit(FSCK_ERROR);
584
585	if (normalize_swapfs) {
586		if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ==
587		    ext2fs_native_flag()) {
588			fprintf(stderr, "%s: Filesystem byte order "
589				"already normalized.\n", ctx->device_name);
590			exit(FSCK_ERROR);
591		}
592	}
593	if (swapfs) {
594		swap_filesys(ctx);
595		if (ctx->flags & E2F_FLAG_ABORT)
596			exit(FSCK_ERROR);
597	}
598
599	/*
600	 * Mark the system as valid, 'til proven otherwise
601	 */
602	ext2fs_mark_valid(fs);
603
604	retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
605	if (retval) {
606		com_err(ctx->program_name, retval,
607			"while reading bad blocks inode");
608		preenhalt(ctx);
609		printf("This doesn't bode well, but we'll try to go on...\n");
610	}
611
612	run_result = e2fsck_run(ctx);
613	if (run_result == E2F_FLAG_RESTART) {
614		ext2fs_close(fs);
615		printf("Restarting e2fsck from the beginning...\n");
616		retval = e2fsck_reset_context(ctx);
617		if (retval) {
618			com_err(ctx->program_name, retval,
619				"while resetting context");
620			exit(1);
621		}
622		goto restart;
623	}
624	if (run_result & E2F_FLAG_ABORT)
625		exit(FSCK_ERROR);
626	if (run_result & E2F_FLAG_CANCEL)
627		ext2fs_unmark_valid(fs);
628
629#ifdef MTRACE
630	mtrace_print("Cleanup");
631#endif
632	if (ext2fs_test_changed(fs)) {
633		exit_value = FSCK_NONDESTRUCT;
634		if (!(ctx->options & E2F_OPT_PREEN))
635			printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n",
636			       ctx->device_name);
637		if (root_filesystem && !read_only_root) {
638			printf("%s: ***** REBOOT LINUX *****\n",
639			       ctx->device_name);
640			exit_value = FSCK_REBOOT;
641		}
642	}
643	if (ext2fs_test_valid(fs))
644		fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
645	else
646		exit_value = FSCK_UNCORRECTED;
647	if (!(ctx->options & E2F_OPT_READONLY)) {
648		if (ext2fs_test_valid(fs)) {
649			if (!(fs->super->s_state & EXT2_VALID_FS))
650				exit_value = FSCK_NONDESTRUCT;
651			fs->super->s_state = EXT2_VALID_FS;
652		} else
653			fs->super->s_state &= ~EXT2_VALID_FS;
654		fs->super->s_mnt_count = 0;
655		fs->super->s_lastcheck = time(NULL);
656		ext2fs_mark_super_dirty(fs);
657	}
658	show_stats(ctx);
659
660	e2fsck_write_bitmaps(ctx);
661	ext2fs_close(fs);
662	sync_disks();
663
664#ifdef RESOURCE_TRACK
665	if (ctx->options & E2F_OPT_TIME)
666		print_resource_track(NULL, &ctx->global_rtrack);
667#endif
668
669	e2fsck_free_context(ctx);
670
671	return exit_value;
672}
673