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