unix.c revision d1154eb460efe588eaed3d439c1caaca149fa362
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#define _XOPEN_SOURCE 600 /* for inclusion of sa_handler in Solaris */
13
14#include "config.h"
15#include <stdio.h>
16#ifdef HAVE_STDLIB_H
17#include <stdlib.h>
18#endif
19#include <string.h>
20#include <fcntl.h>
21#include <ctype.h>
22#include <time.h>
23#ifdef HAVE_SIGNAL_H
24#include <signal.h>
25#endif
26#ifdef HAVE_GETOPT_H
27#include <getopt.h>
28#else
29extern char *optarg;
30extern int optind;
31#endif
32#include <unistd.h>
33#ifdef HAVE_ERRNO_H
34#include <errno.h>
35#endif
36#ifdef HAVE_MNTENT_H
37#include <mntent.h>
38#endif
39#ifdef HAVE_SYS_IOCTL_H
40#include <sys/ioctl.h>
41#endif
42#ifdef HAVE_MALLOC_H
43#include <malloc.h>
44#endif
45#ifdef HAVE_SYS_TYPES_H
46#include <sys/types.h>
47#endif
48#ifdef HAVE_DIRENT_H
49#include <dirent.h>
50#endif
51
52#include "e2p/e2p.h"
53#include "et/com_err.h"
54#include "e2p/e2p.h"
55#include "e2fsck.h"
56#include "problem.h"
57#include "../version.h"
58
59/* Command line options */
60static int cflag;		/* check disk */
61static int show_version_only;
62static int verbose;
63
64static int replace_bad_blocks;
65static int keep_bad_blocks;
66static char *bad_blocks_file;
67
68e2fsck_t e2fsck_global_ctx;	/* Try your very best not to use this! */
69
70#ifdef CONFIG_JBD_DEBUG		/* Enabled by configure --enable-jfs-debug */
71int journal_enable_debug = -1;
72#endif
73
74static void usage(e2fsck_t ctx)
75{
76	fprintf(stderr,
77		_("Usage: %s [-panyrcdfvtDFV] [-b superblock] [-B blocksize]\n"
78		"\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
79		"\t\t[-l|-L bad_blocks_file] [-C fd] [-j external_journal]\n"
80		"\t\t[-E extended-options] device\n"),
81		ctx->program_name);
82
83	fprintf(stderr, _("\nEmergency help:\n"
84		" -p                   Automatic repair (no questions)\n"
85		" -n                   Make no changes to the filesystem\n"
86		" -y                   Assume \"yes\" to all questions\n"
87		" -c                   Check for bad blocks and add them to the badblock list\n"
88		" -f                   Force checking even if filesystem is marked clean\n"));
89	fprintf(stderr, _(""
90		" -v                   Be verbose\n"
91		" -b superblock        Use alternative superblock\n"
92		" -B blocksize         Force blocksize when looking for superblock\n"
93		" -j external_journal  Set location of the external journal\n"
94		" -l bad_blocks_file   Add to badblocks list\n"
95		" -L bad_blocks_file   Set badblocks list\n"
96		));
97
98	exit(FSCK_USAGE);
99}
100
101static void show_stats(e2fsck_t	ctx)
102{
103	ext2_filsys fs = ctx->fs;
104	ext2_ino_t inodes, inodes_used;
105	blk64_t blocks, blocks_used;
106	unsigned int dir_links;
107	unsigned int num_files, num_links;
108	int frag_percent_file, frag_percent_dir, frag_percent_total;
109	int i, j;
110
111	dir_links = 2 * ctx->fs_directory_count - 1;
112	num_files = ctx->fs_total_count - dir_links;
113	num_links = ctx->fs_links_count - dir_links;
114	inodes = fs->super->s_inodes_count;
115	inodes_used = (fs->super->s_inodes_count -
116		       fs->super->s_free_inodes_count);
117	blocks = ext2fs_blocks_count(fs->super);
118	blocks_used = (ext2fs_blocks_count(fs->super) -
119		       ext2fs_free_blocks_count(fs->super));
120
121	frag_percent_file = (10000 * ctx->fs_fragmented) / inodes_used;
122	frag_percent_file = (frag_percent_file + 5) / 10;
123
124	frag_percent_dir = (10000 * ctx->fs_fragmented_dir) / inodes_used;
125	frag_percent_dir = (frag_percent_dir + 5) / 10;
126
127	frag_percent_total = ((10000 * (ctx->fs_fragmented +
128					ctx->fs_fragmented_dir))
129			      / inodes_used);
130	frag_percent_total = (frag_percent_total + 5) / 10;
131
132	if (!verbose) {
133		printf(_("%s: %u/%u files (%0d.%d%% non-contiguous), %llu/%llu blocks\n"),
134		       ctx->device_name, inodes_used, inodes,
135		       frag_percent_total / 10, frag_percent_total % 10,
136		       blocks_used, blocks);
137		return;
138	}
139	printf (P_("\n%8u inode used (%2.2f%%)\n", "\n%8u inodes used (%2.2f%%)\n",
140		   inodes_used), inodes_used, 100.0 * inodes_used / inodes);
141	printf (P_("%8u non-contiguous file (%0d.%d%%)\n",
142		   "%8u non-contiguous files (%0d.%d%%)\n",
143		   ctx->fs_fragmented),
144		ctx->fs_fragmented, frag_percent_file / 10,
145		frag_percent_file % 10);
146	printf (P_("%8u non-contiguous directory (%0d.%d%%)\n",
147		   "%8u non-contiguous directories (%0d.%d%%)\n",
148		   ctx->fs_fragmented_dir),
149		ctx->fs_fragmented_dir, frag_percent_dir / 10,
150		frag_percent_dir % 10);
151	printf (_("         # of inodes with ind/dind/tind blocks: %u/%u/%u\n"),
152		ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
153
154	for (j=MAX_EXTENT_DEPTH_COUNT-1; j >=0; j--)
155		if (ctx->extent_depth_count[j])
156			break;
157	if (++j) {
158		printf (_("         Extent depth histogram: "));
159		for (i=0; i < j; i++) {
160			if (i)
161				fputc('/', stdout);
162			printf("%u", ctx->extent_depth_count[i]);
163		}
164		fputc('\n', stdout);
165	}
166
167	printf (P_("%8llu block used (%2.2f%%)\n",
168		   "%8llu blocks used (%2.2f%%)\n",
169		   blocks_used), blocks_used, 100.0 * blocks_used / blocks);
170	printf (P_("%8u bad block\n", "%8u bad blocks\n",
171		   ctx->fs_badblocks_count), ctx->fs_badblocks_count);
172	printf (P_("%8u large file\n", "%8u large files\n",
173		   ctx->large_files), ctx->large_files);
174	printf (P_("\n%8u regular file\n", "\n%8u regular files\n",
175		   ctx->fs_regular_count), ctx->fs_regular_count);
176	printf (P_("%8u directory\n", "%8u directories\n",
177		   ctx->fs_directory_count), ctx->fs_directory_count);
178	printf (P_("%8u character device file\n",
179		   "%8u character device files\n", ctx->fs_chardev_count),
180		ctx->fs_chardev_count);
181	printf (P_("%8u block device file\n", "%8u block device files\n",
182		   ctx->fs_blockdev_count), ctx->fs_blockdev_count);
183	printf (P_("%8u fifo\n", "%8u fifos\n", ctx->fs_fifo_count),
184		ctx->fs_fifo_count);
185	printf (P_("%8u link\n", "%8u links\n",
186		   ctx->fs_links_count - dir_links),
187		ctx->fs_links_count - dir_links);
188	printf (P_("%8u symbolic link", "%8u symbolic links",
189		   ctx->fs_symlinks_count), ctx->fs_symlinks_count);
190	printf (P_(" (%u fast symbolic link)\n", " (%u fast symbolic links)\n",
191		   ctx->fs_fast_symlinks_count), ctx->fs_fast_symlinks_count);
192	printf (P_("%8u socket\n", "%8u sockets\n", ctx->fs_sockets_count),
193		ctx->fs_sockets_count);
194	printf ("--------\n");
195	printf (P_("%8u file\n", "%8u files\n",
196		   ctx->fs_total_count - dir_links),
197		ctx->fs_total_count - dir_links);
198}
199
200static void check_mount(e2fsck_t ctx)
201{
202	errcode_t	retval;
203	int		cont;
204
205	retval = ext2fs_check_if_mounted(ctx->filesystem_name,
206					 &ctx->mount_flags);
207	if (retval) {
208		com_err("ext2fs_check_if_mount", retval,
209			_("while determining whether %s is mounted."),
210			ctx->filesystem_name);
211		return;
212	}
213
214	/*
215	 * If the filesystem isn't mounted, or it's the root
216	 * filesystem and it's mounted read-only, and we're not doing
217	 * a read/write check, then everything's fine.
218	 */
219	if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
220	    ((ctx->mount_flags & EXT2_MF_ISROOT) &&
221	     (ctx->mount_flags & EXT2_MF_READONLY) &&
222	     !(ctx->options & E2F_OPT_WRITECHECK)))
223		return;
224
225	if ((ctx->options & E2F_OPT_READONLY) &&
226	    !(ctx->options & E2F_OPT_WRITECHECK)) {
227		printf(_("Warning!  %s is mounted.\n"), ctx->filesystem_name);
228		return;
229	}
230
231	printf(_("%s is mounted.  "), ctx->filesystem_name);
232	if (!ctx->interactive)
233		fatal_error(ctx, _("Cannot continue, aborting.\n\n"));
234	printf(_("\n\n\007\007\007\007WARNING!!!  "
235	       "The filesystem is mounted.   If you continue you ***WILL***\n"
236	       "cause ***SEVERE*** filesystem damage.\007\007\007\n\n"));
237	cont = ask_yn(_("Do you really want to continue"), 0);
238	if (!cont) {
239		printf (_("check aborted.\n"));
240		exit (0);
241	}
242	return;
243}
244
245static int is_on_batt(void)
246{
247	FILE	*f;
248	DIR	*d;
249	char	tmp[80], tmp2[80], fname[80];
250	unsigned int	acflag;
251	struct dirent*	de;
252
253	f = fopen("/proc/apm", "r");
254	if (f) {
255		if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
256			acflag = 1;
257		fclose(f);
258		return (acflag != 1);
259	}
260	d = opendir("/proc/acpi/ac_adapter");
261	if (d) {
262		while ((de=readdir(d)) != NULL) {
263			if (!strncmp(".", de->d_name, 1))
264				continue;
265			snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
266				 de->d_name);
267			f = fopen(fname, "r");
268			if (!f)
269				continue;
270			if (fscanf(f, "%s %s", tmp2, tmp) != 2)
271				tmp[0] = 0;
272			fclose(f);
273			if (strncmp(tmp, "off-line", 8) == 0) {
274				closedir(d);
275				return 1;
276			}
277		}
278		closedir(d);
279	}
280	return 0;
281}
282
283/*
284 * This routine checks to see if a filesystem can be skipped; if so,
285 * it will exit with E2FSCK_OK.  Under some conditions it will print a
286 * message explaining why a check is being forced.
287 */
288static void check_if_skip(e2fsck_t ctx)
289{
290	ext2_filsys fs = ctx->fs;
291	struct problem_context pctx;
292	const char *reason = NULL;
293	unsigned int reason_arg = 0;
294	long next_check;
295	int batt = is_on_batt();
296	int defer_check_on_battery;
297	int broken_system_clock;
298	time_t lastcheck;
299
300	profile_get_boolean(ctx->profile, "options", "broken_system_clock",
301			    0, 0, &broken_system_clock);
302	if (ctx->flags & E2F_FLAG_TIME_INSANE)
303		broken_system_clock = 1;
304	profile_get_boolean(ctx->profile, "options",
305			    "defer_check_on_battery", 0, 1,
306			    &defer_check_on_battery);
307	if (!defer_check_on_battery)
308		batt = 0;
309
310	if ((ctx->options & E2F_OPT_FORCE) || bad_blocks_file || cflag)
311		return;
312
313	if (ctx->options & E2F_OPT_JOURNAL_ONLY)
314		goto skip;
315
316	lastcheck = fs->super->s_lastcheck;
317	if (lastcheck > ctx->now)
318		lastcheck -= ctx->time_fudge;
319	if ((fs->super->s_state & EXT2_ERROR_FS) ||
320	    !ext2fs_test_valid(fs))
321		reason = _(" contains a file system with errors");
322	else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
323		reason = _(" was not cleanly unmounted");
324	else if (check_backup_super_block(ctx))
325		reason = _(" primary superblock features different from backup");
326	else if ((fs->super->s_max_mnt_count > 0) &&
327		 (fs->super->s_mnt_count >=
328		  (unsigned) fs->super->s_max_mnt_count)) {
329		reason = _(" has been mounted %u times without being checked");
330		reason_arg = fs->super->s_mnt_count;
331		if (batt && (fs->super->s_mnt_count <
332			     (unsigned) fs->super->s_max_mnt_count*2))
333			reason = 0;
334	} else if (!broken_system_clock && fs->super->s_checkinterval &&
335		   (ctx->now < lastcheck)) {
336		reason = _(" has filesystem last checked time in the future");
337		if (batt)
338			reason = 0;
339	} else if (!broken_system_clock && fs->super->s_checkinterval &&
340		   ((ctx->now - lastcheck) >=
341		    ((time_t) fs->super->s_checkinterval))) {
342		reason = _(" has gone %u days without being checked");
343		reason_arg = (ctx->now - fs->super->s_lastcheck)/(3600*24);
344		if (batt && ((ctx->now - fs->super->s_lastcheck) <
345			     fs->super->s_checkinterval*2))
346			reason = 0;
347	}
348	if (reason) {
349		fputs(ctx->device_name, stdout);
350		printf(reason, reason_arg);
351		fputs(_(", check forced.\n"), stdout);
352		return;
353	}
354
355	/*
356	 * Update the global counts from the block group counts.  This
357	 * is needed since modern kernels don't update the global
358	 * counts so as to avoid locking the entire file system.  So
359	 * if the filesystem is not unmounted cleanly, the global
360	 * counts may not be accurate.  Update them here if we can,
361	 * for the benefit of users who might examine the file system
362	 * using dumpe2fs.  (This is for cosmetic reasons only.)
363	 */
364	clear_problem_context(&pctx);
365	pctx.ino = fs->super->s_free_inodes_count;
366	pctx.ino2 = ctx->free_inodes;
367	if ((pctx.ino != pctx.ino2) &&
368	    !(ctx->options & E2F_OPT_READONLY) &&
369	    fix_problem(ctx, PR_0_FREE_INODE_COUNT, &pctx)) {
370		fs->super->s_free_inodes_count = ctx->free_inodes;
371		ext2fs_mark_super_dirty(fs);
372	}
373	clear_problem_context(&pctx);
374	pctx.blk = ext2fs_free_blocks_count(fs->super);
375	pctx.blk2 = ctx->free_blocks;
376	if ((pctx.blk != pctx.blk2) &&
377	    !(ctx->options & E2F_OPT_READONLY) &&
378	    fix_problem(ctx, PR_0_FREE_BLOCK_COUNT, &pctx)) {
379		ext2fs_free_blocks_count_set(fs->super, ctx->free_blocks);
380		ext2fs_mark_super_dirty(fs);
381	}
382
383	/* Print the summary message when we're skipping a full check */
384	printf(_("%s: clean, %u/%u files, %llu/%llu blocks"), ctx->device_name,
385	       fs->super->s_inodes_count - fs->super->s_free_inodes_count,
386	       fs->super->s_inodes_count,
387	       ext2fs_blocks_count(fs->super) -
388	       ext2fs_free_blocks_count(fs->super),
389	       ext2fs_blocks_count(fs->super));
390	next_check = 100000;
391	if (fs->super->s_max_mnt_count > 0) {
392		next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
393		if (next_check <= 0)
394			next_check = 1;
395	}
396	if (!broken_system_clock && fs->super->s_checkinterval &&
397	    ((ctx->now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
398		next_check = 1;
399	if (next_check <= 5) {
400		if (next_check == 1) {
401			if (batt)
402				fputs(_(" (check deferred; on battery)"),
403				      stdout);
404			else
405				fputs(_(" (check after next mount)"), stdout);
406		} else
407			printf(_(" (check in %ld mounts)"), next_check);
408	}
409	fputc('\n', stdout);
410skip:
411	ext2fs_close(fs);
412	ctx->fs = NULL;
413	e2fsck_free_context(ctx);
414	exit(FSCK_OK);
415}
416
417/*
418 * For completion notice
419 */
420struct percent_tbl {
421	int	max_pass;
422	int	table[32];
423};
424struct percent_tbl e2fsck_tbl = {
425	5, { 0, 70, 90, 92,  95, 100 }
426};
427static char bar[128], spaces[128];
428
429static float calc_percent(struct percent_tbl *tbl, int pass, int curr,
430			  int max)
431{
432	float	percent;
433
434	if (pass <= 0)
435		return 0.0;
436	if (pass > tbl->max_pass || max == 0)
437		return 100.0;
438	percent = ((float) curr) / ((float) max);
439	return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
440		+ tbl->table[pass-1]);
441}
442
443extern void e2fsck_clear_progbar(e2fsck_t ctx)
444{
445	if (!(ctx->flags & E2F_FLAG_PROG_BAR))
446		return;
447
448	printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
449	       ctx->stop_meta);
450	fflush(stdout);
451	ctx->flags &= ~E2F_FLAG_PROG_BAR;
452}
453
454int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
455			   unsigned int dpynum)
456{
457	static const char spinner[] = "\\|/-";
458	int	i;
459	unsigned int	tick;
460	struct timeval	tv;
461	int dpywidth;
462	int fixed_percent;
463
464	if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
465		return 0;
466
467	/*
468	 * Calculate the new progress position.  If the
469	 * percentage hasn't changed, then we skip out right
470	 * away.
471	 */
472	fixed_percent = (int) ((10 * percent) + 0.5);
473	if (ctx->progress_last_percent == fixed_percent)
474		return 0;
475	ctx->progress_last_percent = fixed_percent;
476
477	/*
478	 * If we've already updated the spinner once within
479	 * the last 1/8th of a second, no point doing it
480	 * again.
481	 */
482	gettimeofday(&tv, NULL);
483	tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
484	if ((tick == ctx->progress_last_time) &&
485	    (fixed_percent != 0) && (fixed_percent != 1000))
486		return 0;
487	ctx->progress_last_time = tick;
488
489	/*
490	 * Advance the spinner, and note that the progress bar
491	 * will be on the screen
492	 */
493	ctx->progress_pos = (ctx->progress_pos+1) & 3;
494	ctx->flags |= E2F_FLAG_PROG_BAR;
495
496	dpywidth = 66 - strlen(label);
497	dpywidth = 8 * (dpywidth / 8);
498	if (dpynum)
499		dpywidth -= 8;
500
501	i = ((percent * dpywidth) + 50) / 100;
502	printf("%s%s: |%s%s", ctx->start_meta, label,
503	       bar + (sizeof(bar) - (i+1)),
504	       spaces + (sizeof(spaces) - (dpywidth - i + 1)));
505	if (fixed_percent == 1000)
506		fputc('|', stdout);
507	else
508		fputc(spinner[ctx->progress_pos & 3], stdout);
509	printf(" %4.1f%%  ", percent);
510	if (dpynum)
511		printf("%u\r", dpynum);
512	else
513		fputs(" \r", stdout);
514	fputs(ctx->stop_meta, stdout);
515
516	if (fixed_percent == 1000)
517		e2fsck_clear_progbar(ctx);
518	fflush(stdout);
519
520	return 0;
521}
522
523static int e2fsck_update_progress(e2fsck_t ctx, int pass,
524				  unsigned long cur, unsigned long max)
525{
526	char buf[1024];
527	float percent;
528
529	if (pass == 0)
530		return 0;
531
532	if (ctx->progress_fd) {
533		snprintf(buf, sizeof(buf), "%d %lu %lu %s\n",
534			 pass, cur, max, ctx->device_name);
535		write_all(ctx->progress_fd, buf, strlen(buf));
536	} else {
537		percent = calc_percent(&e2fsck_tbl, pass, cur, max);
538		e2fsck_simple_progress(ctx, ctx->device_name,
539				       percent, 0);
540	}
541	return 0;
542}
543
544#define PATH_SET "PATH=/sbin"
545
546/*
547 * Make sure 0,1,2 file descriptors are open, so that we don't open
548 * the filesystem using the same file descriptor as stdout or stderr.
549 */
550static void reserve_stdio_fds(void)
551{
552	int	fd = 0;
553
554	while (fd <= 2) {
555		fd = open("/dev/null", O_RDWR);
556		if (fd < 0) {
557			fprintf(stderr, _("ERROR: Couldn't open "
558				"/dev/null (%s)\n"),
559				strerror(errno));
560			break;
561		}
562	}
563}
564
565#ifdef HAVE_SIGNAL_H
566static void signal_progress_on(int sig EXT2FS_ATTR((unused)))
567{
568	e2fsck_t ctx = e2fsck_global_ctx;
569
570	if (!ctx)
571		return;
572
573	ctx->progress = e2fsck_update_progress;
574}
575
576static void signal_progress_off(int sig EXT2FS_ATTR((unused)))
577{
578	e2fsck_t ctx = e2fsck_global_ctx;
579
580	if (!ctx)
581		return;
582
583	e2fsck_clear_progbar(ctx);
584	ctx->progress = 0;
585}
586
587static void signal_cancel(int sig EXT2FS_ATTR((unused)))
588{
589	e2fsck_t ctx = e2fsck_global_ctx;
590
591	if (!ctx)
592		exit(FSCK_CANCELED);
593
594	ctx->flags |= E2F_FLAG_CANCEL;
595}
596#endif
597
598static void parse_extended_opts(e2fsck_t ctx, const char *opts)
599{
600	char	*buf, *token, *next, *p, *arg;
601	int	ea_ver;
602	int	extended_usage = 0;
603
604	buf = string_copy(ctx, opts, 0);
605	for (token = buf; token && *token; token = next) {
606		p = strchr(token, ',');
607		next = 0;
608		if (p) {
609			*p = 0;
610			next = p+1;
611		}
612		arg = strchr(token, '=');
613		if (arg) {
614			*arg = 0;
615			arg++;
616		}
617		if (strcmp(token, "ea_ver") == 0) {
618			if (!arg) {
619				extended_usage++;
620				continue;
621			}
622			ea_ver = strtoul(arg, &p, 0);
623			if (*p ||
624			    ((ea_ver != 1) && (ea_ver != 2))) {
625				fprintf(stderr,
626					_("Invalid EA version.\n"));
627				extended_usage++;
628				continue;
629			}
630			ctx->ext_attr_ver = ea_ver;
631		} else if (strcmp(token, "fragcheck") == 0) {
632			ctx->options |= E2F_OPT_FRAGCHECK;
633			continue;
634		} else if (strcmp(token, "journal_only") == 0) {
635			if (arg) {
636				extended_usage++;
637				continue;
638			}
639			ctx->options |= E2F_OPT_JOURNAL_ONLY;
640		} else if (strcmp(token, "discard") == 0) {
641			ctx->options |= E2F_OPT_DISCARD;
642			continue;
643		} else if (strcmp(token, "nodiscard") == 0) {
644			ctx->options &= ~E2F_OPT_DISCARD;
645			continue;
646		} else {
647			fprintf(stderr, _("Unknown extended option: %s\n"),
648				token);
649			extended_usage++;
650		}
651	}
652	free(buf);
653
654	if (extended_usage) {
655		fputs(("\nExtended options are separated by commas, "
656		       "and may take an argument which\n"
657		       "is set off by an equals ('=') sign.  "
658		       "Valid extended options are:\n"), stderr);
659		fputs(("\tea_ver=<ea_version (1 or 2)>\n"), stderr);
660		fputs(("\tfragcheck\n"), stderr);
661		fputs(("\tjournal_only\n"), stderr);
662		fputs(("\tdiscard\n"), stderr);
663		fputs(("\tnodiscard\n"), stderr);
664		fputc('\n', stderr);
665		exit(1);
666	}
667}
668
669static void syntax_err_report(const char *filename, long err, int line_num)
670{
671	fprintf(stderr,
672		_("Syntax error in e2fsck config file (%s, line #%d)\n\t%s\n"),
673		filename, line_num, error_message(err));
674	exit(FSCK_ERROR);
675}
676
677static const char *config_fn[] = { ROOT_SYSCONFDIR "/e2fsck.conf", 0 };
678
679static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
680{
681	int		flush = 0;
682	int		c, fd;
683#ifdef MTRACE
684	extern void	*mallwatch;
685#endif
686	e2fsck_t	ctx;
687	errcode_t	retval;
688#ifdef HAVE_SIGNAL_H
689	struct sigaction	sa;
690#endif
691	char		*extended_opts = 0;
692	char		*cp;
693	int 		res;		/* result of sscanf */
694#ifdef CONFIG_JBD_DEBUG
695	char 		*jbd_debug;
696#endif
697
698	retval = e2fsck_allocate_context(&ctx);
699	if (retval)
700		return retval;
701
702	*ret_ctx = ctx;
703
704	setvbuf(stdout, NULL, _IONBF, BUFSIZ);
705	setvbuf(stderr, NULL, _IONBF, BUFSIZ);
706	if (isatty(0) && isatty(1)) {
707		ctx->interactive = 1;
708	} else {
709		ctx->start_meta[0] = '\001';
710		ctx->stop_meta[0] = '\002';
711	}
712	memset(bar, '=', sizeof(bar)-1);
713	memset(spaces, ' ', sizeof(spaces)-1);
714	add_error_table(&et_ext2_error_table);
715	add_error_table(&et_prof_error_table);
716	blkid_get_cache(&ctx->blkid, NULL);
717
718	if (argc && *argv)
719		ctx->program_name = *argv;
720	else
721		ctx->program_name = "e2fsck";
722
723	while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
724		switch (c) {
725		case 'C':
726			ctx->progress = e2fsck_update_progress;
727			res = sscanf(optarg, "%d", &ctx->progress_fd);
728			if (res != 1)
729				goto sscanf_err;
730
731			if (ctx->progress_fd < 0) {
732				ctx->progress = 0;
733				ctx->progress_fd = ctx->progress_fd * -1;
734			}
735			if (!ctx->progress_fd)
736				break;
737			/* Validate the file descriptor to avoid disasters */
738			fd = dup(ctx->progress_fd);
739			if (fd < 0) {
740				fprintf(stderr,
741				_("Error validating file descriptor %d: %s\n"),
742					ctx->progress_fd,
743					error_message(errno));
744				fatal_error(ctx,
745			_("Invalid completion information file descriptor"));
746			} else
747				close(fd);
748			break;
749		case 'D':
750			ctx->options |= E2F_OPT_COMPRESS_DIRS;
751			break;
752		case 'E':
753			extended_opts = optarg;
754			break;
755		case 'p':
756		case 'a':
757			if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
758			conflict_opt:
759				fatal_error(ctx,
760	_("Only one of the options -p/-a, -n or -y may be specified."));
761			}
762			ctx->options |= E2F_OPT_PREEN;
763			break;
764		case 'n':
765			if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
766				goto conflict_opt;
767			ctx->options |= E2F_OPT_NO;
768			break;
769		case 'y':
770			if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
771				goto conflict_opt;
772			ctx->options |= E2F_OPT_YES;
773			break;
774		case 't':
775#ifdef RESOURCE_TRACK
776			if (ctx->options & E2F_OPT_TIME)
777				ctx->options |= E2F_OPT_TIME2;
778			else
779				ctx->options |= E2F_OPT_TIME;
780#else
781			fprintf(stderr, _("The -t option is not "
782				"supported on this version of e2fsck.\n"));
783#endif
784			break;
785		case 'c':
786			if (cflag++)
787				ctx->options |= E2F_OPT_WRITECHECK;
788			ctx->options |= E2F_OPT_CHECKBLOCKS;
789			break;
790		case 'r':
791			/* What we do by default, anyway! */
792			break;
793		case 'b':
794			res = sscanf(optarg, "%llu", &ctx->use_superblock);
795			if (res != 1)
796				goto sscanf_err;
797			ctx->flags |= E2F_FLAG_SB_SPECIFIED;
798			break;
799		case 'B':
800			ctx->blocksize = atoi(optarg);
801			break;
802		case 'I':
803			res = sscanf(optarg, "%d", &ctx->inode_buffer_blocks);
804			if (res != 1)
805				goto sscanf_err;
806			break;
807		case 'j':
808			ctx->journal_name = blkid_get_devname(ctx->blkid,
809							      optarg, NULL);
810			if (!ctx->journal_name) {
811				com_err(ctx->program_name, 0,
812					_("Unable to resolve '%s'"),
813					optarg);
814				fatal_error(ctx, 0);
815			}
816			break;
817		case 'P':
818			res = sscanf(optarg, "%d", &ctx->process_inode_size);
819			if (res != 1)
820				goto sscanf_err;
821			break;
822		case 'L':
823			replace_bad_blocks++;
824		case 'l':
825			bad_blocks_file = string_copy(ctx, optarg, 0);
826			break;
827		case 'd':
828			ctx->options |= E2F_OPT_DEBUG;
829			break;
830		case 'f':
831			ctx->options |= E2F_OPT_FORCE;
832			break;
833		case 'F':
834			flush = 1;
835			break;
836		case 'v':
837			verbose = 1;
838			break;
839		case 'V':
840			show_version_only = 1;
841			break;
842#ifdef MTRACE
843		case 'M':
844			mallwatch = (void *) strtol(optarg, NULL, 0);
845			break;
846#endif
847		case 'N':
848			ctx->device_name = string_copy(ctx, optarg, 0);
849			break;
850		case 'k':
851			keep_bad_blocks++;
852			break;
853		default:
854			usage(ctx);
855		}
856	if (show_version_only)
857		return 0;
858	if (optind != argc - 1)
859		usage(ctx);
860	if ((ctx->options & E2F_OPT_NO) &&
861	    (ctx->options & E2F_OPT_COMPRESS_DIRS)) {
862		com_err(ctx->program_name, 0,
863			_("The -n and -D options are incompatible."));
864		fatal_error(ctx, 0);
865	}
866	if ((ctx->options & E2F_OPT_NO) && cflag) {
867		com_err(ctx->program_name, 0,
868			_("The -n and -c options are incompatible."));
869		fatal_error(ctx, 0);
870	}
871	if ((ctx->options & E2F_OPT_NO) && bad_blocks_file) {
872		com_err(ctx->program_name, 0,
873			_("The -n and -l/-L options are incompatible."));
874		fatal_error(ctx, 0);
875	}
876	if (ctx->options & E2F_OPT_NO)
877		ctx->options |= E2F_OPT_READONLY;
878
879	ctx->io_options = strchr(argv[optind], '?');
880	if (ctx->io_options)
881		*ctx->io_options++ = 0;
882	ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
883	if (!ctx->filesystem_name) {
884		com_err(ctx->program_name, 0, _("Unable to resolve '%s'"),
885			argv[optind]);
886		fatal_error(ctx, 0);
887	}
888	if (extended_opts)
889		parse_extended_opts(ctx, extended_opts);
890
891	if ((cp = getenv("E2FSCK_CONFIG")) != NULL)
892		config_fn[0] = cp;
893	profile_set_syntax_err_cb(syntax_err_report);
894	profile_init(config_fn, &ctx->profile);
895
896	if (flush) {
897		fd = open(ctx->filesystem_name, O_RDONLY, 0);
898		if (fd < 0) {
899			com_err("open", errno,
900				_("while opening %s for flushing"),
901				ctx->filesystem_name);
902			fatal_error(ctx, 0);
903		}
904		if ((retval = ext2fs_sync_device(fd, 1))) {
905			com_err("ext2fs_sync_device", retval,
906				_("while trying to flush %s"),
907				ctx->filesystem_name);
908			fatal_error(ctx, 0);
909		}
910		close(fd);
911	}
912	if (cflag && bad_blocks_file) {
913		fprintf(stderr, _("The -c and the -l/-L options may "
914				  "not be both used at the same time.\n"));
915		exit(FSCK_USAGE);
916	}
917#ifdef HAVE_SIGNAL_H
918	/*
919	 * Set up signal action
920	 */
921	memset(&sa, 0, sizeof(struct sigaction));
922	sa.sa_handler = signal_cancel;
923	sigaction(SIGINT, &sa, 0);
924	sigaction(SIGTERM, &sa, 0);
925#ifdef SA_RESTART
926	sa.sa_flags = SA_RESTART;
927#endif
928	e2fsck_global_ctx = ctx;
929	sa.sa_handler = signal_progress_on;
930	sigaction(SIGUSR1, &sa, 0);
931	sa.sa_handler = signal_progress_off;
932	sigaction(SIGUSR2, &sa, 0);
933#endif
934
935	/* Update our PATH to include /sbin if we need to run badblocks  */
936	if (cflag) {
937		char *oldpath = getenv("PATH");
938		char *newpath;
939		int len = sizeof(PATH_SET) + 1;
940
941		if (oldpath)
942			len += strlen(oldpath);
943
944		newpath = malloc(len);
945		if (!newpath)
946			fatal_error(ctx, "Couldn't malloc() newpath");
947		strcpy(newpath, PATH_SET);
948
949		if (oldpath) {
950			strcat(newpath, ":");
951			strcat(newpath, oldpath);
952		}
953		putenv(newpath);
954	}
955#ifdef CONFIG_JBD_DEBUG
956	jbd_debug = getenv("E2FSCK_JBD_DEBUG");
957	if (jbd_debug) {
958		res = sscanf(jbd_debug, "%d", &journal_enable_debug);
959		if (res != 1) {
960			fprintf(stderr,
961			        _("E2FSCK_JBD_DEBUG \"%s\" not an integer\n\n"),
962			        jbd_debug);
963			exit (1);
964		}
965	}
966#endif
967	return 0;
968
969sscanf_err:
970	fprintf(stderr, _("\nInvalid non-numeric argument to -%c (\"%s\")\n\n"),
971	        c, optarg);
972	exit (1);
973}
974
975static errcode_t try_open_fs(e2fsck_t ctx, int flags, io_manager io_ptr,
976			     ext2_filsys *ret_fs)
977{
978	errcode_t retval;
979
980	*ret_fs = NULL;
981	if (ctx->superblock && ctx->blocksize) {
982		retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
983				      flags, ctx->superblock, ctx->blocksize,
984				      io_ptr, ret_fs);
985	} else if (ctx->superblock) {
986		int blocksize;
987		for (blocksize = EXT2_MIN_BLOCK_SIZE;
988		     blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
989			if (*ret_fs) {
990				ext2fs_free(*ret_fs);
991				*ret_fs = NULL;
992			}
993			retval = ext2fs_open2(ctx->filesystem_name,
994					      ctx->io_options, flags,
995					      ctx->superblock, blocksize,
996					      io_ptr, ret_fs);
997			if (!retval)
998				break;
999		}
1000	} else
1001		retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
1002				      flags, 0, 0, io_ptr, ret_fs);
1003	return retval;
1004}
1005
1006static const char *my_ver_string = E2FSPROGS_VERSION;
1007static const char *my_ver_date = E2FSPROGS_DATE;
1008
1009int main (int argc, char *argv[])
1010{
1011	errcode_t	retval = 0, retval2 = 0, orig_retval = 0;
1012	int		exit_value = FSCK_OK;
1013	ext2_filsys	fs = 0;
1014	io_manager	io_ptr;
1015	struct ext2_super_block *sb;
1016	const char	*lib_ver_date;
1017	int		my_ver, lib_ver;
1018	e2fsck_t	ctx;
1019	blk_t		orig_superblock;
1020	struct problem_context pctx;
1021	int flags, run_result;
1022	int journal_size;
1023	int sysval, sys_page_size = 4096;
1024	int old_bitmaps;
1025	__u32 features[3];
1026	char *cp;
1027
1028	clear_problem_context(&pctx);
1029	sigcatcher_setup();
1030#ifdef MTRACE
1031	mtrace();
1032#endif
1033#ifdef MCHECK
1034	mcheck(0);
1035#endif
1036#ifdef ENABLE_NLS
1037	setlocale(LC_MESSAGES, "");
1038	setlocale(LC_CTYPE, "");
1039	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
1040	textdomain(NLS_CAT_NAME);
1041#endif
1042	my_ver = ext2fs_parse_version_string(my_ver_string);
1043	lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
1044	if (my_ver > lib_ver) {
1045		fprintf( stderr, _("Error: ext2fs library version "
1046			"out of date!\n"));
1047		show_version_only++;
1048	}
1049
1050	retval = PRS(argc, argv, &ctx);
1051	if (retval) {
1052		com_err("e2fsck", retval,
1053			_("while trying to initialize program"));
1054		exit(FSCK_ERROR);
1055	}
1056	reserve_stdio_fds();
1057
1058	init_resource_track(&ctx->global_rtrack, NULL);
1059	if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
1060		fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
1061			 my_ver_date);
1062
1063	if (show_version_only) {
1064		fprintf(stderr, _("\tUsing %s, %s\n"),
1065			error_message(EXT2_ET_BASE), lib_ver_date);
1066		exit(FSCK_OK);
1067	}
1068
1069	check_mount(ctx);
1070
1071	if (!(ctx->options & E2F_OPT_PREEN) &&
1072	    !(ctx->options & E2F_OPT_NO) &&
1073	    !(ctx->options & E2F_OPT_YES)) {
1074		if (!ctx->interactive)
1075			fatal_error(ctx,
1076				    _("need terminal for interactive repairs"));
1077	}
1078	ctx->superblock = ctx->use_superblock;
1079restart:
1080#ifdef CONFIG_TESTIO_DEBUG
1081	if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
1082		io_ptr = test_io_manager;
1083		test_io_backing_manager = unix_io_manager;
1084	} else
1085#endif
1086		io_ptr = unix_io_manager;
1087	flags = EXT2_FLAG_NOFREE_ON_ERROR;
1088	profile_get_boolean(ctx->profile, "options", "old_bitmaps", 0, 0,
1089			    &old_bitmaps);
1090	if (!old_bitmaps)
1091		flags |= EXT2_FLAG_64BITS;
1092	if ((ctx->options & E2F_OPT_READONLY) == 0)
1093		flags |= EXT2_FLAG_RW;
1094	if ((ctx->mount_flags & EXT2_MF_MOUNTED) == 0)
1095		flags |= EXT2_FLAG_EXCLUSIVE;
1096
1097	retval = try_open_fs(ctx, flags, io_ptr, &fs);
1098
1099	if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
1100	    !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
1101	    ((retval == EXT2_ET_BAD_MAGIC) ||
1102	     (retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
1103	     ((retval == 0) && (retval2 = ext2fs_check_desc(fs))))) {
1104		if (retval2 == ENOMEM) {
1105			retval = retval2;
1106			goto failure;
1107		}
1108		if (fs->flags & EXT2_FLAG_NOFREE_ON_ERROR) {
1109			ext2fs_free(fs);
1110			fs = NULL;
1111		}
1112		if (!fs || (fs->group_desc_count > 1)) {
1113			printf(_("%s: %s trying backup blocks...\n"),
1114			       ctx->program_name,
1115			       retval ? _("Superblock invalid,") :
1116			       _("Group descriptors look bad..."));
1117			orig_superblock = ctx->superblock;
1118			get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
1119			if (fs)
1120				ext2fs_close(fs);
1121			orig_retval = retval;
1122			retval = try_open_fs(ctx, flags, io_ptr, &fs);
1123			if ((orig_retval == 0) && retval != 0) {
1124				if (fs)
1125					ext2fs_close(fs);
1126				com_err(ctx->program_name, retval,
1127					"when using the backup blocks");
1128				printf(_("%s: going back to original "
1129					 "superblock\n"), ctx->program_name);
1130				ctx->superblock = orig_superblock;
1131				retval = try_open_fs(ctx, flags, io_ptr, &fs);
1132			}
1133		}
1134	}
1135	if (((retval == EXT2_ET_UNSUPP_FEATURE) ||
1136	     (retval == EXT2_ET_RO_UNSUPP_FEATURE)) &&
1137	    fs && fs->super) {
1138		sb = fs->super;
1139		features[0] = (sb->s_feature_compat &
1140			       ~EXT2_LIB_FEATURE_COMPAT_SUPP);
1141		features[1] = (sb->s_feature_incompat &
1142			       ~EXT2_LIB_FEATURE_INCOMPAT_SUPP);
1143		features[2] = (sb->s_feature_ro_compat &
1144			       ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP);
1145		if (features[0] || features[1] || features[2])
1146			goto print_unsupp_features;
1147	}
1148failure:
1149	if (retval) {
1150		if (orig_retval)
1151			retval = orig_retval;
1152		com_err(ctx->program_name, retval, _("while trying to open %s"),
1153			ctx->filesystem_name);
1154		if (retval == EXT2_ET_REV_TOO_HIGH) {
1155			printf(_("The filesystem revision is apparently "
1156			       "too high for this version of e2fsck.\n"
1157			       "(Or the filesystem superblock "
1158			       "is corrupt)\n\n"));
1159			fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
1160		} else if (retval == EXT2_ET_SHORT_READ)
1161			printf(_("Could this be a zero-length partition?\n"));
1162		else if ((retval == EPERM) || (retval == EACCES))
1163			printf(_("You must have %s access to the "
1164			       "filesystem or be root\n"),
1165			       (ctx->options & E2F_OPT_READONLY) ?
1166			       "r/o" : "r/w");
1167		else if (retval == ENXIO)
1168			printf(_("Possibly non-existent or swap device?\n"));
1169		else if (retval == EBUSY)
1170			printf(_("Filesystem mounted or opened exclusively "
1171				 "by another program?\n"));
1172		else if (retval == ENOENT)
1173			printf(_("Possibly non-existent device?\n"));
1174#ifdef EROFS
1175		else if (retval == EROFS)
1176			printf(_("Disk write-protected; use the -n option "
1177			       "to do a read-only\n"
1178			       "check of the device.\n"));
1179#endif
1180		else
1181			fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
1182		fatal_error(ctx, 0);
1183	}
1184	/*
1185	 * We only update the master superblock because (a) paranoia;
1186	 * we don't want to corrupt the backup superblocks, and (b) we
1187	 * don't need to update the mount count and last checked
1188	 * fields in the backup superblock (the kernel doesn't update
1189	 * the backup superblocks anyway).  With newer versions of the
1190	 * library this flag is set by ext2fs_open2(), but we set this
1191	 * here just to be sure.  (No, we don't support e2fsck running
1192	 * with some other libext2fs than the one that it was shipped
1193	 * with, but just in case....)
1194	 */
1195	fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
1196
1197	if (!(ctx->flags & E2F_FLAG_GOT_DEVSIZE)) {
1198		__u32 blocksize = EXT2_BLOCK_SIZE(fs->super);
1199		int need_restart = 0;
1200
1201		pctx.errcode = ext2fs_get_device_size2(ctx->filesystem_name,
1202						       blocksize,
1203						       &ctx->num_blocks);
1204		/*
1205		 * The floppy driver refuses to allow anyone else to
1206		 * open the device if has been opened with O_EXCL;
1207		 * this is unlike other block device drivers in Linux.
1208		 * To handle this, we close the filesystem and then
1209		 * reopen the filesystem after we get the device size.
1210		 */
1211		if (pctx.errcode == EBUSY) {
1212			ext2fs_close(fs);
1213			need_restart++;
1214			pctx.errcode =
1215				ext2fs_get_device_size2(ctx->filesystem_name,
1216							blocksize,
1217							&ctx->num_blocks);
1218		}
1219		if (pctx.errcode == EXT2_ET_UNIMPLEMENTED)
1220			ctx->num_blocks = 0;
1221		else if (pctx.errcode) {
1222			fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
1223			ctx->flags |= E2F_FLAG_ABORT;
1224			fatal_error(ctx, 0);
1225		}
1226		ctx->flags |= E2F_FLAG_GOT_DEVSIZE;
1227		if (need_restart)
1228			goto restart;
1229	}
1230
1231	ctx->fs = fs;
1232	fs->priv_data = ctx;
1233	fs->now = ctx->now;
1234	sb = fs->super;
1235	if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
1236		com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH,
1237			_("while trying to open %s"),
1238			ctx->filesystem_name);
1239	get_newer:
1240		fatal_error(ctx, _("Get a newer version of e2fsck!"));
1241	}
1242
1243	/*
1244	 * Set the device name, which is used whenever we print error
1245	 * or informational messages to the user.
1246	 */
1247	if (ctx->device_name == 0 &&
1248	    (sb->s_volume_name[0] != 0)) {
1249		ctx->device_name = string_copy(ctx, sb->s_volume_name,
1250					       sizeof(sb->s_volume_name));
1251	}
1252	if (ctx->device_name == 0)
1253		ctx->device_name = string_copy(ctx, ctx->filesystem_name, 0);
1254	for (cp = ctx->device_name; *cp; cp++)
1255		if (isspace(*cp) || *cp == ':')
1256			*cp = '_';
1257
1258	ehandler_init(fs->io);
1259
1260	if ((ctx->mount_flags & EXT2_MF_MOUNTED) &&
1261	    !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER))
1262		goto skip_journal;
1263
1264	/*
1265	 * Make sure the ext3 superblock fields are consistent.
1266	 */
1267	retval = e2fsck_check_ext3_journal(ctx);
1268	if (retval) {
1269		com_err(ctx->program_name, retval,
1270			_("while checking ext3 journal for %s"),
1271			ctx->device_name);
1272		fatal_error(ctx, 0);
1273	}
1274
1275	/*
1276	 * Check to see if we need to do ext3-style recovery.  If so,
1277	 * do it, and then restart the fsck.
1278	 */
1279	if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
1280		if (ctx->options & E2F_OPT_READONLY) {
1281			printf(_("Warning: skipping journal recovery "
1282				 "because doing a read-only filesystem "
1283				 "check.\n"));
1284			io_channel_flush(ctx->fs->io);
1285		} else {
1286			if (ctx->flags & E2F_FLAG_RESTARTED) {
1287				/*
1288				 * Whoops, we attempted to run the
1289				 * journal twice.  This should never
1290				 * happen, unless the hardware or
1291				 * device driver is being bogus.
1292				 */
1293				com_err(ctx->program_name, 0,
1294					_("unable to set superblock flags on %s\n"), ctx->device_name);
1295				fatal_error(ctx, 0);
1296			}
1297			retval = e2fsck_run_ext3_journal(ctx);
1298			if (retval) {
1299				com_err(ctx->program_name, retval,
1300				_("while recovering ext3 journal of %s"),
1301					ctx->device_name);
1302				fatal_error(ctx, 0);
1303			}
1304			ext2fs_close(ctx->fs);
1305			ctx->fs = 0;
1306			ctx->flags |= E2F_FLAG_RESTARTED;
1307			goto restart;
1308		}
1309	}
1310
1311skip_journal:
1312	/*
1313	 * Check for compatibility with the feature sets.  We need to
1314	 * be more stringent than ext2fs_open().
1315	 */
1316	features[0] = sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP;
1317	features[1] = sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP;
1318	features[2] = (sb->s_feature_ro_compat &
1319		       ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP);
1320print_unsupp_features:
1321	if (features[0] || features[1] || features[2]) {
1322		int	i, j;
1323		__u32	*mask = features, m;
1324
1325		fprintf(stderr, _("%s has unsupported feature(s):"),
1326			ctx->filesystem_name);
1327
1328		for (i=0; i <3; i++,mask++) {
1329			for (j=0,m=1; j < 32; j++, m<<=1) {
1330				if (*mask & m)
1331					fprintf(stderr, " %s",
1332						e2p_feature2string(i, m));
1333			}
1334		}
1335		putc('\n', stderr);
1336		goto get_newer;
1337	}
1338#ifdef ENABLE_COMPRESSION
1339	if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
1340		com_err(ctx->program_name, 0,
1341			_("Warning: compression support is experimental.\n"));
1342#endif
1343#ifndef ENABLE_HTREE
1344	if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
1345		com_err(ctx->program_name, 0,
1346			_("E2fsck not compiled with HTREE support,\n\t"
1347			  "but filesystem %s has HTREE directories.\n"),
1348			ctx->device_name);
1349		goto get_newer;
1350	}
1351#endif
1352
1353	/*
1354	 * If the user specified a specific superblock, presumably the
1355	 * master superblock has been trashed.  So we mark the
1356	 * superblock as dirty, so it can be written out.
1357	 */
1358	if (ctx->superblock &&
1359	    !(ctx->options & E2F_OPT_READONLY))
1360		ext2fs_mark_super_dirty(fs);
1361
1362	/*
1363	 * Calculate the number of filesystem blocks per pagesize.  If
1364	 * fs->blocksize > page_size, set the number of blocks per
1365	 * pagesize to 1 to avoid division by zero errors.
1366	 */
1367#ifdef _SC_PAGESIZE
1368	sysval = sysconf(_SC_PAGESIZE);
1369	if (sysval > 0)
1370		sys_page_size = sysval;
1371#endif /* _SC_PAGESIZE */
1372	ctx->blocks_per_page = sys_page_size / fs->blocksize;
1373	if (ctx->blocks_per_page == 0)
1374		ctx->blocks_per_page = 1;
1375
1376	if (ctx->superblock)
1377		set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
1378	ext2fs_mark_valid(fs);
1379	check_super_block(ctx);
1380	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
1381		fatal_error(ctx, 0);
1382	check_if_skip(ctx);
1383	check_resize_inode(ctx);
1384	if (bad_blocks_file)
1385		read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
1386	else if (cflag)
1387		read_bad_blocks_file(ctx, 0, !keep_bad_blocks); /* Test disk */
1388	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
1389		fatal_error(ctx, 0);
1390
1391	/*
1392	 * Mark the system as valid, 'til proven otherwise
1393	 */
1394	ext2fs_mark_valid(fs);
1395
1396	retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
1397	if (retval) {
1398		com_err(ctx->program_name, retval,
1399			_("while reading bad blocks inode"));
1400		preenhalt(ctx);
1401		printf(_("This doesn't bode well,"
1402			 " but we'll try to go on...\n"));
1403	}
1404
1405	/*
1406	 * Save the journal size in megabytes.
1407	 * Try and use the journal size from the backup else let e2fsck
1408	 * find the default journal size.
1409	 */
1410	if (sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS)
1411		journal_size = (sb->s_jnl_blocks[15] << (32 - 20)) |
1412			       (sb->s_jnl_blocks[16] >> 20);
1413	else
1414		journal_size = -1;
1415
1416	if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_QUOTA) {
1417		int qtype;
1418		/* Quotas were enabled. Do quota accounting during fsck. */
1419		if ((sb->s_usr_quota_inum && sb->s_grp_quota_inum) ||
1420		    (!sb->s_usr_quota_inum && !sb->s_grp_quota_inum))
1421			qtype = -1;
1422		else
1423			qtype = sb->s_usr_quota_inum ? USRQUOTA : GRPQUOTA;
1424
1425		init_quota_context(&ctx->qctx, ctx->fs, qtype);
1426	}
1427
1428	run_result = e2fsck_run(ctx);
1429	e2fsck_clear_progbar(ctx);
1430
1431	if (ctx->flags & E2F_FLAG_JOURNAL_INODE) {
1432		if (fix_problem(ctx, PR_6_RECREATE_JOURNAL, &pctx)) {
1433			if (journal_size < 1024)
1434				journal_size = ext2fs_default_journal_size(ext2fs_blocks_count(fs->super));
1435			if (journal_size < 0) {
1436				fs->super->s_feature_compat &=
1437					~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
1438				fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
1439				com_err(ctx->program_name, 0,
1440					_("Couldn't determine journal size"));
1441				goto no_journal;
1442			}
1443			printf(_("Creating journal (%d blocks): "),
1444			       journal_size);
1445			fflush(stdout);
1446			retval = ext2fs_add_journal_inode(fs,
1447							  journal_size, 0);
1448			if (retval) {
1449				com_err("Error ", retval,
1450					_("\n\twhile trying to create journal"));
1451				goto no_journal;
1452			}
1453			printf(_(" Done.\n"));
1454			printf(_("\n*** journal has been re-created - "
1455				       "filesystem is now ext3 again ***\n"));
1456		}
1457	}
1458no_journal:
1459
1460	if (ctx->qctx) {
1461		write_quota_inode(ctx->qctx, -1);
1462		release_quota_context(&ctx->qctx);
1463	}
1464
1465	if (run_result == E2F_FLAG_RESTART) {
1466		printf(_("Restarting e2fsck from the beginning...\n"));
1467		retval = e2fsck_reset_context(ctx);
1468		if (retval) {
1469			com_err(ctx->program_name, retval,
1470				_("while resetting context"));
1471			fatal_error(ctx, 0);
1472		}
1473		ext2fs_close(fs);
1474		goto restart;
1475	}
1476	if (run_result & E2F_FLAG_CANCEL) {
1477		printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
1478		       ctx->device_name : ctx->filesystem_name);
1479		exit_value |= FSCK_CANCELED;
1480	}
1481	if (run_result & E2F_FLAG_ABORT)
1482		fatal_error(ctx, _("aborted"));
1483	if (check_backup_super_block(ctx)) {
1484		fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
1485		ext2fs_mark_super_dirty(fs);
1486	}
1487
1488#ifdef MTRACE
1489	mtrace_print("Cleanup");
1490#endif
1491	if (ext2fs_test_changed(fs)) {
1492		exit_value |= FSCK_NONDESTRUCT;
1493		if (!(ctx->options & E2F_OPT_PREEN))
1494		    printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
1495			       ctx->device_name);
1496		if (ctx->mount_flags & EXT2_MF_ISROOT) {
1497			printf(_("%s: ***** REBOOT LINUX *****\n"),
1498			       ctx->device_name);
1499			exit_value |= FSCK_REBOOT;
1500		}
1501	}
1502	if (!ext2fs_test_valid(fs) ||
1503	    ((exit_value & FSCK_CANCELED) &&
1504	     (sb->s_state & EXT2_ERROR_FS))) {
1505		printf(_("\n%s: ********** WARNING: Filesystem still has "
1506			 "errors **********\n\n"), ctx->device_name);
1507		exit_value |= FSCK_UNCORRECTED;
1508		exit_value &= ~FSCK_NONDESTRUCT;
1509	}
1510	if (exit_value & FSCK_CANCELED) {
1511		int	allow_cancellation;
1512
1513		profile_get_boolean(ctx->profile, "options",
1514				    "allow_cancellation", 0, 0,
1515				    &allow_cancellation);
1516		exit_value &= ~FSCK_NONDESTRUCT;
1517		if (allow_cancellation && ext2fs_test_valid(fs) &&
1518		    (sb->s_state & EXT2_VALID_FS) &&
1519		    !(sb->s_state & EXT2_ERROR_FS))
1520			exit_value = 0;
1521	} else {
1522		show_stats(ctx);
1523		if (!(ctx->options & E2F_OPT_READONLY)) {
1524			if (ext2fs_test_valid(fs)) {
1525				if (!(sb->s_state & EXT2_VALID_FS))
1526					exit_value |= FSCK_NONDESTRUCT;
1527				sb->s_state = EXT2_VALID_FS;
1528			} else
1529				sb->s_state &= ~EXT2_VALID_FS;
1530			sb->s_mnt_count = 0;
1531			if (!(ctx->flags & E2F_FLAG_TIME_INSANE))
1532				sb->s_lastcheck = ctx->now;
1533			memset(((char *) sb) + EXT4_S_ERR_START, 0,
1534			       EXT4_S_ERR_LEN);
1535			ext2fs_mark_super_dirty(fs);
1536		}
1537	}
1538
1539	if ((run_result & E2F_FLAG_CANCEL) == 0 &&
1540	    sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM &&
1541	    !(ctx->options & E2F_OPT_READONLY)) {
1542		retval = ext2fs_set_gdt_csum(ctx->fs);
1543		if (retval) {
1544			com_err(ctx->program_name, retval,
1545				_("while setting block group checksum info"));
1546			fatal_error(ctx, 0);
1547		}
1548	}
1549
1550	e2fsck_write_bitmaps(ctx);
1551	io_channel_flush(ctx->fs->io);
1552	print_resource_track(ctx, NULL, &ctx->global_rtrack, ctx->fs->io);
1553
1554	ext2fs_close(fs);
1555	ctx->fs = NULL;
1556	free(ctx->journal_name);
1557
1558	e2fsck_free_context(ctx);
1559	remove_error_table(&et_ext2_error_table);
1560	remove_error_table(&et_prof_error_table);
1561	return exit_value;
1562}
1563