Whamcloud - gitweb
Fix memory leaks in e2fsck's journal.c, and remove some cases
[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 #include <stdio.h>
13 #ifdef HAVE_STDLIB_H
14 #include <stdlib.h>
15 #endif
16 #include <string.h>
17 #include <fcntl.h>
18 #include <ctype.h>
19 #include <time.h>
20 #ifdef HAVE_SIGNAL_H
21 #include <signal.h>
22 #endif
23 #ifdef HAVE_GETOPT_H
24 #include <getopt.h>
25 #else
26 extern char *optarg;
27 extern int optind;
28 #endif
29 #include <unistd.h>
30 #ifdef HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 #ifdef HAVE_MNTENT_H
34 #include <mntent.h>
35 #endif
36 #include <sys/ioctl.h>
37 #include <malloc.h>
38
39 #include "et/com_err.h"
40 #include "e2fsck.h"
41 #include "problem.h"
42 #include "../version.h"
43
44 /* Command line options */
45 static int blocksize = 0;
46 static int swapfs = 0;
47 static int normalize_swapfs = 0;
48 static int cflag = 0;           /* check disk */
49 static int show_version_only = 0;
50 static int force = 0;
51 static int verbose = 0;
52
53 static int replace_bad_blocks = 0;
54 static char *bad_blocks_file = 0;
55
56 static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
57
58 static int root_filesystem = 0;
59 static int read_only_root = 0;
60
61 e2fsck_t e2fsck_global_ctx;     /* Try your very best not to use this! */
62
63 static void usage(e2fsck_t ctx)
64 {
65         fprintf(stderr,
66                 _("Usage: %s [-panyrcdfvstFSV] [-b superblock] [-B blocksize]\n"
67                 "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
68                 "\t\t[-l|-L bad_blocks_file] [-C fd] [-j ext-journal] device\n"),
69                 ctx->program_name);
70
71         fprintf(stderr, _("\nEmergency help:\n"
72                 " -p                   Automatic repair (no questions)\n"
73                 " -n                   Make no changes to the filesystem\n"
74                 " -y                   Assume \"yes\" to all questions\n"
75                 " -c                   Check for bad blocks\n"
76                 " -f                   Force checking even if filesystem is marked clean\n"));
77         fprintf(stderr, _(""
78                 " -v                   Be verbose\n"
79                 " -b superblock        Use alternative superblock\n"
80                 " -B blocksize         Force blocksize when looking for superblock\n"
81                 " -j external-journal  Set location of the external journal\n"
82                 " -l bad_blocks_file   Add to badblocks list\n"
83                 " -L bad_blocks_file   Set badblocks list\n"
84                 ));
85
86         exit(FSCK_USAGE);
87 }
88
89 static void show_stats(e2fsck_t ctx)
90 {
91         ext2_filsys fs = ctx->fs;
92         int inodes, inodes_used, blocks, blocks_used;
93         int dir_links;
94         int num_files, num_links;
95         int frag_percent;
96
97         dir_links = 2 * ctx->fs_directory_count - 1;
98         num_files = ctx->fs_total_count - dir_links;
99         num_links = ctx->fs_links_count - dir_links;
100         inodes = fs->super->s_inodes_count;
101         inodes_used = (fs->super->s_inodes_count -
102                        fs->super->s_free_inodes_count);
103         blocks = fs->super->s_blocks_count;
104         blocks_used = (fs->super->s_blocks_count -
105                        fs->super->s_free_blocks_count);
106
107         frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
108         frag_percent = (frag_percent + 5) / 10;
109         
110         if (!verbose) {
111                 printf(_("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n"),
112                        ctx->device_name, inodes_used, inodes,
113                        frag_percent / 10, frag_percent % 10,
114                        blocks_used, blocks);
115                 return;
116         }
117         /*
118          * This is a bit ugly. But I think there will nearly always be more
119          * than one "thing" to report about, so I won't try writing complex
120          * code to handle one/two/many forms of all words.
121          * Some languages (Italian, at least) never uses the plural form
122          * of foreign words, so in real life this could not be a problem.
123          * md@linux.it - 2000-1-15
124          */
125 #ifdef ENABLE_NLS
126         printf (_("\n%8d inodes used (%d%%)\n"), inodes_used,
127                 (inodes_used != 1), 100 * inodes_used / inodes);
128         printf (_("%8d non-contiguous inodes (%0d.%d%%)\n"),
129                 ctx->fs_fragmented, frag_percent / 10, frag_percent % 10);
130         printf (_("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
131                 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
132         printf (_("%8d blocks used (%d%%)\n"
133                 "%8d bad blocks\n"), blocks_used,
134                 (int) ((long long) 100 * blocks_used / blocks),
135                 ctx->fs_badblocks_count);
136         printf (_("\n%8d regular files\n"
137                 "%8d directories\n"
138                 "%8d character device files\n"
139                 "%8d block device files\n"
140                 "%8d fifos\n"
141                 "%8d links\n"
142                 "%8d symbolic links (%d fast symbolic links)\n"
143                 "%8d sockets\n"
144                 "--------\n"
145                 "%8d files\n"),
146                 ctx->fs_regular_count,
147                 ctx->fs_directory_count,
148                 ctx->fs_chardev_count,
149                 ctx->fs_blockdev_count,
150                 ctx->fs_fifo_count,
151                 ctx->fs_links_count - dir_links,
152                 ctx->fs_symlinks_count,
153                 ctx->fs_fast_symlinks_count,
154                 ctx->fs_sockets_count,
155                 ctx->fs_total_count - dir_links);
156 #else
157         printf ("\n%8d inode%s used (%d%%)\n", inodes_used,
158                 (inodes_used != 1) ? "s" : "",
159                 100 * inodes_used / inodes);
160         printf ("%8d non-contiguous inodes (%0d.%d%%)\n",
161                 ctx->fs_fragmented, frag_percent / 10, frag_percent % 10);
162         printf ("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n",
163                 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
164         printf ("%8d block%s used (%d%%)\n"
165                 "%8d bad block%s\n", blocks_used,
166                 (blocks_used != 1) ? "s" : "",
167                 100 * blocks_used / blocks, ctx->fs_badblocks_count,
168                 ctx->fs_badblocks_count != 1 ? "s" : "");
169         printf ("\n%8d regular file%s\n"
170                 "%8d director%s\n"
171                 "%8d character device file%s\n"
172                 "%8d block device file%s\n"
173                 "%8d fifo%s\n"
174                 "%8d link%s\n"
175                 "%8d symbolic link%s (%d fast symbolic link%s)\n"
176                 "%8d socket%s\n"
177                 "--------\n"
178                 "%8d file%s\n",
179                 ctx->fs_regular_count,
180                 (ctx->fs_regular_count != 1) ? "s" : "",
181                 ctx->fs_directory_count,
182                 (ctx->fs_directory_count != 1) ? "ies" : "y",
183                 ctx->fs_chardev_count,
184                 (ctx->fs_chardev_count != 1) ? "s" : "",
185                 ctx->fs_blockdev_count,
186                 (ctx->fs_blockdev_count != 1) ? "s" : "",
187                 ctx->fs_fifo_count,
188                 (ctx->fs_fifo_count != 1) ? "s" : "",
189                 ctx->fs_links_count - dir_links,
190                 ((ctx->fs_links_count - dir_links) != 1) ? "s" : "",
191                 ctx->fs_symlinks_count,
192                 (ctx->fs_symlinks_count != 1) ? "s" : "",
193                 ctx->fs_fast_symlinks_count,
194                 (ctx->fs_fast_symlinks_count != 1) ? "s" : "",
195                 ctx->fs_sockets_count, (ctx->fs_sockets_count != 1) ? "s" : "",
196                 ctx->fs_total_count - dir_links,
197                 ((ctx->fs_total_count - dir_links) != 1) ? "s" : "");
198 #endif
199 }
200
201 static void check_mount(e2fsck_t ctx)
202 {
203         errcode_t       retval;
204         int             mount_flags, cont, fd;
205
206         retval = ext2fs_check_if_mounted(ctx->filesystem_name, &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         if (!(mount_flags & EXT2_MF_MOUNTED))
214                 return;
215
216 #if (defined(__linux__) && defined(HAVE_MNTENT_H))
217         /*
218          * If the root is mounted read-only, then /etc/mtab is
219          * probably not correct; so we won't issue a warning based on
220          * it.
221          */
222         fd = open(MOUNTED, O_RDWR);
223         if (fd < 0) {
224                 if (errno == EROFS)
225                         return;
226         } else
227                 close(fd);
228 #endif
229         
230         if (ctx->options & E2F_OPT_READONLY) {
231                 printf(_("Warning!  %s is mounted.\n"), ctx->filesystem_name);
232                 return;
233         }
234
235         printf(_("%s is mounted.  "), ctx->filesystem_name);
236         if (!isatty(0) || !isatty(1))
237                 fatal_error(ctx, _("Cannot continue, aborting.\n\n"));
238         printf(_("\n\n\007\007\007\007WARNING!!!  "
239                "Running e2fsck on a mounted filesystem may cause\n"
240                "SEVERE filesystem damage.\007\007\007\n\n"));
241         cont = ask_yn(_("Do you really want to continue"), -1);
242         if (!cont) {
243                 printf (_("check aborted.\n"));
244                 exit (0);
245         }
246         return;
247 }
248
249 /*
250  * This routine checks to see if a filesystem can be skipped; if so,
251  * it will exit with E2FSCK_OK.  Under some conditions it will print a
252  * message explaining why a check is being forced.
253  */
254 static void check_if_skip(e2fsck_t ctx)
255 {
256         ext2_filsys fs = ctx->fs;
257         const char *reason = NULL;
258         unsigned int reason_arg = 0;
259         
260         if (force || bad_blocks_file || cflag || swapfs)
261                 return;
262         
263         if (fs->super->s_state & EXT2_ERROR_FS)
264                 reason = _(" contains a file system with errors");
265         else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
266                 reason = _(" was not cleanly unmounted");
267         else if ((fs->super->s_max_mnt_count > 0) &&
268                  (fs->super->s_mnt_count >=
269                   (unsigned) fs->super->s_max_mnt_count)) {
270                 reason = _(" has been mounted %u times without being checked");
271                 reason_arg = fs->super->s_mnt_count;
272         } else if (fs->super->s_checkinterval &&
273                  time(0) >= (fs->super->s_lastcheck +
274                              fs->super->s_checkinterval)) {
275                 reason = _(" has gone %u days without being checked");
276                 reason_arg = (time(0) - fs->super->s_lastcheck)/(3600*24);
277         }
278         if (reason) {
279                 fputs(ctx->device_name, stdout);
280                 printf(reason, reason_arg);
281                 fputs(_(", check forced.\n"), stdout);
282                 return;
283         }
284         printf(_("%s: clean, %d/%d files, %d/%d blocks\n"), ctx->device_name,
285                fs->super->s_inodes_count - fs->super->s_free_inodes_count,
286                fs->super->s_inodes_count,
287                fs->super->s_blocks_count - fs->super->s_free_blocks_count,
288                fs->super->s_blocks_count);
289         ext2fs_close(fs);
290         ctx->fs = NULL;
291         e2fsck_free_context(ctx);
292         exit(FSCK_OK);
293 }
294
295 /*
296  * For completion notice
297  */
298 struct percent_tbl {
299         int     max_pass;
300         int     table[32];
301 };
302 struct percent_tbl e2fsck_tbl = {
303         5, { 0, 70, 90, 92,  95, 100 }
304 };
305 static char bar[] =
306         "==============================================================="
307         "===============================================================";
308 static char spaces[] =
309         "                                                               "
310         "                                                               ";
311
312 static float calc_percent(struct percent_tbl *tbl, int pass, int curr,
313                           int max)
314 {
315         float   percent;
316         
317         if (pass <= 0)
318                 return 0.0;
319         if (pass > tbl->max_pass || max == 0)
320                 return 100.0;
321         percent = ((float) curr) / ((float) max);
322         return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
323                 + tbl->table[pass-1]);
324 }
325
326 extern void e2fsck_clear_progbar(e2fsck_t ctx)
327 {
328         if (!(ctx->flags & E2F_FLAG_PROG_BAR))
329                 return;
330         
331         printf("%s\r", spaces + (sizeof(spaces) - 80));
332         ctx->flags &= ~E2F_FLAG_PROG_BAR;
333 }
334
335 static int e2fsck_update_progress(e2fsck_t ctx, int pass,
336                                   unsigned long cur, unsigned long max)
337 {
338         static const char spinner[] = "\\|/-";
339         char buf[80];
340         int     i;
341         float percent;
342         int     tick;
343         struct timeval  tv;
344         static int dpywidth = 0;
345
346         if (pass == 0)
347                 return 0;
348         
349         if (ctx->progress_fd) {
350                 sprintf(buf, "%d %lu %lu\n", pass, cur, max);
351                 write(ctx->progress_fd, buf, strlen(buf));
352         } else {
353                 if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
354                         return 0;
355                 if (dpywidth == 0) {
356                         dpywidth = 66 - strlen(ctx->device_name);
357                         dpywidth = 8 * (dpywidth / 8);
358                 }
359                 /*
360                  * Calculate the new progress position.  If the
361                  * percentage hasn't changed, then we skip out right
362                  * away. 
363                  */
364                 percent = calc_percent(&e2fsck_tbl, pass, cur, max);
365                 if (ctx->progress_last_percent == (int) 10 * percent)
366                         return 0;
367                 ctx->progress_last_percent = (int) 10 * percent;
368
369                 /*
370                  * If we've already updated the spinner once within
371                  * the last 1/8th of a second, no point doing it
372                  * again.
373                  */
374                 gettimeofday(&tv, NULL);
375                 tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
376                 if ((tick == ctx->progress_last_time) &&
377                     (cur != max) && (cur != 0))
378                         return 0;
379                 ctx->progress_last_time = tick;
380
381                 /*
382                  * Advance the spinner, and note that the progress bar
383                  * will be on the screen
384                  */
385                 ctx->progress_pos = (ctx->progress_pos+1) & 3;
386                 ctx->flags |= E2F_FLAG_PROG_BAR;
387                 
388                 i = ((percent * dpywidth) + 50) / 100;
389                 printf("%s: |%s%s", ctx->device_name,
390                        bar + (sizeof(bar) - (i+1)),
391                        spaces + (sizeof(spaces) - (dpywidth - i + 1)));
392                 if (percent == 100.0)
393                         fputc('|', stdout);
394                 else
395                         fputc(spinner[ctx->progress_pos & 3], stdout);
396                 printf(" %4.1f%%   \r", percent);
397                 if (percent == 100.0)
398                         e2fsck_clear_progbar(ctx);
399                 fflush(stdout);
400         }
401         return 0;
402 }
403
404 #define PATH_SET "PATH=/sbin"
405
406 static void reserve_stdio_fds(void)
407 {
408         int     fd;
409
410         while (1) {
411                 fd = open("/dev/null", O_RDWR);
412                 if (fd > 2)
413                         break;
414                 if (fd < 0) {
415                         fprintf(stderr, _("ERROR: Couldn't open "
416                                 "/dev/null (%s)\n"),
417                                 strerror(errno));
418                         break;
419                 }
420         }
421         close(fd);
422 }
423
424 #ifdef HAVE_SIGNAL_H
425 static void signal_progress_on(int sig)
426 {
427         e2fsck_t ctx = e2fsck_global_ctx;
428
429         if (!ctx)
430                 return;
431
432         ctx->progress = e2fsck_update_progress;
433         ctx->progress_fd = 0;
434 }
435
436 static void signal_progress_off(int sig)
437 {
438         e2fsck_t ctx = e2fsck_global_ctx;
439
440         if (!ctx)
441                 return;
442
443         e2fsck_clear_progbar(ctx);
444         ctx->progress = 0;
445 }
446 #endif
447
448 static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
449 {
450         int             flush = 0;
451         int             c, fd;
452 #ifdef MTRACE
453         extern void     *mallwatch;
454 #endif
455         e2fsck_t        ctx;
456         errcode_t       retval;
457 #ifdef HAVE_SIGNAL_H
458         struct sigaction        sa;
459 #endif
460
461         retval = e2fsck_allocate_context(&ctx);
462         if (retval)
463                 return retval;
464
465         *ret_ctx = ctx;
466
467         setbuf(stdout, NULL);
468         setbuf(stderr, NULL);
469         initialize_ext2_error_table();
470         
471         if (argc && *argv)
472                 ctx->program_name = *argv;
473         else
474                 ctx->program_name = "e2fsck";
475         while ((c = getopt (argc, argv, "panyrcC:B:dfvtFVM:b:I:j:P:l:L:N:Ss")) != EOF)
476                 switch (c) {
477                 case 'C':
478                         ctx->progress = e2fsck_update_progress;
479                         ctx->progress_fd = atoi(optarg);
480                         if (!ctx->progress_fd)
481                                 break;
482                         /* Validate the file descriptor to avoid disasters */
483                         fd = dup(ctx->progress_fd);
484                         if (fd < 0) {
485                                 fprintf(stderr,
486                                 _("Error validating file descriptor %d: %s\n"),
487                                         ctx->progress_fd,
488                                         error_message(errno));
489                                 fatal_error(ctx,
490                         _("Invalid completion information file descriptor"));
491                         } else
492                                 close(fd);
493                         break;
494                 case 'p':
495                 case 'a':
496                         ctx->options |= E2F_OPT_PREEN;
497                         ctx->options &= ~(E2F_OPT_YES|E2F_OPT_NO);
498                         break;
499                 case 'n':
500                         ctx->options |= E2F_OPT_NO;
501                         ctx->options &= ~(E2F_OPT_YES|E2F_OPT_PREEN);
502                         break;
503                 case 'y':
504                         ctx->options |= E2F_OPT_YES;
505                         ctx->options &= ~(E2F_OPT_PREEN|E2F_OPT_NO);
506                         break;
507                 case 't':
508 #ifdef RESOURCE_TRACK
509                         if (ctx->options & E2F_OPT_TIME)
510                                 ctx->options |= E2F_OPT_TIME2;
511                         else
512                                 ctx->options |= E2F_OPT_TIME;
513 #else
514                         fprintf(stderr, _("The -t option is not "
515                                 "supported on this version of e2fsck.\n"));
516 #endif
517                         break;
518                 case 'c':
519                         cflag++;
520                         ctx->options |= E2F_OPT_CHECKBLOCKS;
521                         break;
522                 case 'r':
523                         /* What we do by default, anyway! */
524                         break;
525                 case 'b':
526                         ctx->use_superblock = atoi(optarg);
527                         break;
528                 case 'B':
529                         blocksize = atoi(optarg);
530                         break;
531                 case 'I':
532                         ctx->inode_buffer_blocks = atoi(optarg);
533                         break;
534                 case 'j':
535                         ctx->journal_name = optarg;
536                         break;
537                 case 'P':
538                         ctx->process_inode_size = atoi(optarg);
539                         break;
540                 case 'L':
541                         replace_bad_blocks++;
542                 case 'l':
543                         bad_blocks_file = (char *) malloc(strlen(optarg)+1);
544                         if (!bad_blocks_file)
545                                 fatal_error(ctx,
546                                             "Couldn't malloc bad_blocks_file");
547                         strcpy(bad_blocks_file, optarg);
548                         break;
549                 case 'd':
550                         ctx->options |= E2F_OPT_DEBUG;
551                         break;
552                 case 'f':
553                         force = 1;
554                         break;
555                 case 'F':
556                         flush = 1;
557                         break;
558                 case 'v':
559                         verbose = 1;
560                         break;
561                 case 'V':
562                         show_version_only = 1;
563                         break;
564 #ifdef MTRACE
565                 case 'M':
566                         mallwatch = (void *) strtol(optarg, NULL, 0);
567                         break;
568 #endif
569                 case 'N':
570                         ctx->device_name = optarg;
571                         break;
572 #ifdef ENABLE_SWAPFS
573                 case 's':
574                         normalize_swapfs = 1;
575                 case 'S':
576                         swapfs = 1;
577                         break;
578 #else
579                 case 's':
580                 case 'S':
581                         fprintf(stderr, _("Byte-swapping filesystems "
582                                           "not compiled in this version "
583                                           "of e2fsck\n"));
584                         exit(1);
585 #endif
586                 default:
587                         usage(ctx);
588                 }
589         if (show_version_only)
590                 return 0;
591         if (optind != argc - 1)
592                 usage(ctx);
593         if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file &&
594             !cflag && !swapfs)
595                 ctx->options |= E2F_OPT_READONLY;
596         ctx->filesystem_name = argv[optind];
597         if (flush) {
598                 int     fd = open(ctx->filesystem_name, O_RDONLY, 0);
599
600                 if (fd < 0) {
601                         com_err("open", errno,
602                                 _("while opening %s for flushing"),
603                                 ctx->filesystem_name);
604                         fatal_error(ctx, 0);
605                 }
606                 if ((retval = ext2fs_sync_device(fd, 1))) {
607                         com_err("ext2fs_sync_device", retval,
608                                 _("while trying to flush %s"),
609                                 ctx->filesystem_name);
610                         fatal_error(ctx, 0);
611                 }
612                 close(fd);
613         }
614 #ifdef ENABLE_SWAPFS
615         if (swapfs) {
616                 if (cflag || bad_blocks_file) {
617                         fprintf(stderr, _("Incompatible options not "
618                                 "allowed when byte-swapping.\n"));
619                         exit(FSCK_USAGE);
620                 }
621         }
622 #endif
623 #ifdef HAVE_SIGNAL_H
624         /*
625          * Set up signal action
626          */
627         memset(&sa, 0, sizeof(struct sigaction));
628 #ifdef SA_RESTART
629         sa.sa_flags = SA_RESTART;
630 #endif
631         e2fsck_global_ctx = ctx;
632         sa.sa_handler = signal_progress_on;
633         sigaction(SIGUSR1, &sa, 0);
634         sa.sa_handler = signal_progress_off;
635         sigaction(SIGUSR2, &sa, 0);
636 #endif
637
638         /* Update our PATH to include /sbin if we need to run badblocks  */
639         if (cflag) {
640                 char *oldpath = getenv("PATH");
641                 if (oldpath) {
642                         char *newpath;
643
644                         newpath = (char *) malloc(sizeof (PATH_SET) + 1 +
645                                                   strlen (oldpath));
646                         if (!newpath)
647                                 fatal_error(ctx, "Couldn't malloc() newpath");
648                         strcpy (newpath, PATH_SET);
649                         strcat (newpath, ":");
650                         strcat (newpath, oldpath);
651                         putenv (newpath);
652                 } else
653                         putenv (PATH_SET);
654         }
655         return 0;
656 }
657
658 static const char *my_ver_string = E2FSPROGS_VERSION;
659 static const char *my_ver_date = E2FSPROGS_DATE;
660                                         
661 int main (int argc, char *argv[])
662 {
663         errcode_t       retval = 0;
664         int             exit_value = FSCK_OK;
665         int             i;
666         ext2_filsys     fs = 0;
667         io_manager      io_ptr;
668         struct ext2_super_block *sb;
669         const char      *lib_ver_date;
670         int             my_ver, lib_ver;
671         e2fsck_t        ctx;
672         struct problem_context pctx;
673         int flags, run_result;
674         
675         clear_problem_context(&pctx);
676 #ifdef MTRACE
677         mtrace();
678 #endif
679 #ifdef MCHECK
680         mcheck(0);
681 #endif
682 #ifdef ENABLE_NLS
683         setlocale(LC_MESSAGES, "");
684         bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
685         textdomain(NLS_CAT_NAME);
686 #endif
687         my_ver = ext2fs_parse_version_string(my_ver_string);
688         lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
689         if (my_ver > lib_ver) {
690                 fprintf( stderr, _("Error: ext2fs library version "
691                         "out of date!\n"));
692                 show_version_only++;
693         }
694         
695         retval = PRS(argc, argv, &ctx);
696         if (retval) {
697                 com_err("e2fsck", retval,
698                         _("while trying to initialize program"));
699                 exit(FSCK_ERROR);
700         }
701         reserve_stdio_fds();
702         
703 #ifdef RESOURCE_TRACK
704         init_resource_track(&ctx->global_rtrack);
705 #endif
706
707         if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
708                 fprintf (stderr, _("e2fsck %s, %s for EXT2 FS %s, %s\n"),
709                          my_ver_string, my_ver_date, EXT2FS_VERSION,
710                          EXT2FS_DATE);
711
712         if (show_version_only) {
713                 fprintf(stderr, _("\tUsing %s, %s\n"),
714                         error_message(EXT2_ET_BASE), lib_ver_date);
715                 exit(FSCK_OK);
716         }
717         
718         check_mount(ctx);
719         
720         if (!(ctx->options & E2F_OPT_PREEN) &&
721             !(ctx->options & E2F_OPT_NO) &&
722             !(ctx->options & E2F_OPT_YES)) {
723                 if (!isatty (0) || !isatty (1))
724                         fatal_error(ctx,
725                                     _("need terminal for interactive repairs"));
726         }
727         ctx->superblock = ctx->use_superblock;
728 restart:
729 #if 1
730         io_ptr = unix_io_manager;
731 #else
732         io_ptr = test_io_manager;
733         test_io_backing_manager = unix_io_manager;
734 #endif
735         flags = 0;
736         if ((ctx->options & E2F_OPT_READONLY) == 0)
737                 flags |= EXT2_FLAG_RW;
738
739         if (ctx->superblock && blocksize) {
740                 retval = ext2fs_open(ctx->filesystem_name, flags,
741                                      ctx->superblock, blocksize, io_ptr, &fs);
742         } else if (ctx->superblock) {
743                 for (i=0; possible_block_sizes[i]; i++) {
744                         retval = ext2fs_open(ctx->filesystem_name, flags,
745                                              ctx->superblock,
746                                              possible_block_sizes[i],
747                                              io_ptr, &fs);
748                         if (!retval)
749                                 break;
750                 }
751         } else 
752                 retval = ext2fs_open(ctx->filesystem_name, flags, 
753                                      0, 0, io_ptr, &fs);
754         if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) && 
755             ((retval == EXT2_ET_BAD_MAGIC) ||
756              ((retval == 0) && ext2fs_check_desc(fs)))) {
757                 if (!fs || (fs->group_desc_count > 1)) {
758                         printf(_("%s trying backup blocks...\n"),
759                                retval ? _("Couldn't find ext2 superblock,") :
760                                _("Group descriptors look bad..."));
761                         ctx->superblock = get_backup_sb(fs);
762                         if (fs)
763                                 ext2fs_close(fs);
764                         goto restart;
765                 }
766         }
767         if (retval) {
768                 com_err(ctx->program_name, retval, _("while trying to open %s"),
769                         ctx->filesystem_name);
770                 if (retval == EXT2_ET_REV_TOO_HIGH) {
771                         printf(_("The filesystem revision is apparently "
772                                "too high for this version of e2fsck.\n"
773                                "(Or the filesystem superblock "
774                                "is corrupt)\n\n"));
775                         fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
776                 } else if (retval == EXT2_ET_SHORT_READ)
777                         printf(_("Could this be a zero-length partition?\n"));
778                 else if ((retval == EPERM) || (retval == EACCES))
779                         printf(_("You must have %s access to the "
780                                "filesystem or be root\n"),
781                                (ctx->options & E2F_OPT_READONLY) ?
782                                "r/o" : "r/w");
783                 else if (retval == ENXIO)
784                         printf(_("Possibly non-existent or swap device?\n"));
785 #ifdef EROFS
786                 else if (retval == EROFS)
787                         printf(_("Disk write-protected; use the -n option "
788                                "to do a read-only\n"
789                                "check of the device.\n"));
790 #endif
791                 else
792                         fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
793                 fatal_error(ctx, 0);
794         }
795         ctx->fs = fs;
796         fs->priv_data = ctx;
797         sb = fs->super;
798         if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
799                 com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH,
800                         _("while trying to open %s"),
801                         ctx->filesystem_name);
802         get_newer:
803                 fatal_error(ctx, _("Get a newer version of e2fsck!"));
804         }
805
806         /*
807          * Set the device name, which is used whenever we print error
808          * or informational messages to the user.
809          */
810         if (ctx->device_name == 0 &&
811             (sb->s_volume_name[0] != 0)) {
812                 char *cp = malloc(sizeof(sb->s_volume_name)+1);
813                 if (cp) {
814                         strncpy(cp, sb->s_volume_name,
815                                 sizeof(sb->s_volume_name));
816                         cp[sizeof(sb->s_volume_name)] = 0;
817                         ctx->device_name = cp;
818                 }
819         }
820         if (ctx->device_name == 0)
821                 ctx->device_name = ctx->filesystem_name;
822
823         /*
824          * Make sure the ext3 superblock fields are consistent.
825          */
826         retval = e2fsck_check_ext3_journal(ctx);
827         if (retval) {
828                 com_err(ctx->program_name, retval,
829                         _("while checking ext3 journal for %s"),
830                         ctx->device_name);
831                 ext2fs_close(ctx->fs);
832                 fatal_error(ctx, 0);
833         }
834
835         /*
836          * Check to see if we need to do ext3-style recovery.  If so,
837          * do it, and then restart the fsck.
838          */
839         if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
840                 if (ctx->options & E2F_OPT_READONLY) {
841                         printf(_("Warning: skipping journal recovery "
842                                  "because doing a read-only filesystem "
843                                  "check.\n"));
844                         io_channel_flush(ctx->fs->io);
845                 } else {
846                         retval = e2fsck_run_ext3_journal(ctx);
847                         if (retval) {
848                                 com_err(ctx->program_name, retval,
849                                 _("while recovering ext3 journal of %s"),
850                                         ctx->device_name);
851                                 fatal_error(ctx, 0);
852                         }
853                         ext2fs_close(ctx->fs);
854                         ctx->fs = 0;
855                         goto restart;
856                 }
857         }
858
859         /*
860          * Check for compatibility with the feature sets.  We need to
861          * be more stringent than ext2fs_open().
862          */
863         if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
864             (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
865                 com_err(ctx->program_name, EXT2_ET_UNSUPP_FEATURE,
866                         "(%s)", ctx->device_name);
867                 goto get_newer;
868         }
869         if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
870                 com_err(ctx->program_name, EXT2_ET_RO_UNSUPP_FEATURE,
871                         "(%s)", ctx->device_name);
872                 goto get_newer;
873         }
874 #ifdef ENABLE_COMPRESSION
875         if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
876                 com_err(ctx->program_name, 0,
877                         _("Warning: compression support is experimental.\n"));
878 #endif
879         
880         /*
881          * If the user specified a specific superblock, presumably the
882          * master superblock has been trashed.  So we mark the
883          * superblock as dirty, so it can be written out.
884          */
885         if (ctx->superblock &&
886             !(ctx->options & E2F_OPT_READONLY))
887                 ext2fs_mark_super_dirty(fs);
888
889         /*
890          * Don't overwrite the backup superblock and block
891          * descriptors, until we're sure the filesystem is OK....
892          */
893         fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
894
895         ehandler_init(fs->io);
896
897         if (ctx->superblock)
898                 set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
899         check_super_block(ctx);
900         if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
901                 fatal_error(ctx, 0);
902         check_if_skip(ctx);
903         if (bad_blocks_file)
904                 read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
905         else if (cflag)
906                 test_disk(ctx);
907         if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
908                 fatal_error(ctx, 0);
909 #ifdef ENABLE_SWAPFS
910         if (normalize_swapfs) {
911                 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ==
912                     ext2fs_native_flag()) {
913                         fprintf(stderr, _("%s: Filesystem byte order "
914                                 "already normalized.\n"), ctx->device_name);
915                         fatal_error(ctx, 0);
916                 }
917         }
918         if (swapfs) {
919                 swap_filesys(ctx);
920                 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
921                         fatal_error(ctx, 0);
922         }
923 #endif
924
925         /*
926          * Mark the system as valid, 'til proven otherwise
927          */
928         ext2fs_mark_valid(fs);
929
930         retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
931         if (retval) {
932                 com_err(ctx->program_name, retval,
933                         _("while reading bad blocks inode"));
934                 preenhalt(ctx);
935                 printf(_("This doesn't bode well,"
936                          " but we'll try to go on...\n"));
937         }
938
939         run_result = e2fsck_run(ctx);
940         e2fsck_clear_progbar(ctx);
941         if (run_result == E2F_FLAG_RESTART) {
942                 printf(_("Restarting e2fsck from the beginning...\n"));
943                 retval = e2fsck_reset_context(ctx);
944                 if (retval) {
945                         com_err(ctx->program_name, retval,
946                                 _("while resetting context"));
947                         fatal_error(ctx, 0);
948                 }
949                 ext2fs_close(fs);
950                 goto restart;
951         }
952         if (run_result & E2F_FLAG_SIGNAL_MASK)
953                 fatal_error(ctx, 0);
954         if (run_result & E2F_FLAG_CANCEL)
955                 ext2fs_unmark_valid(fs);
956
957 #ifdef MTRACE
958         mtrace_print("Cleanup");
959 #endif
960         if (ext2fs_test_changed(fs)) {
961                 exit_value = FSCK_NONDESTRUCT;
962                 if (!(ctx->options & E2F_OPT_PREEN))
963                     printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
964                                ctx->device_name);
965                 if (root_filesystem && !read_only_root) {
966                         printf(_("%s: ***** REBOOT LINUX *****\n"),
967                                ctx->device_name);
968                         exit_value = FSCK_REBOOT;
969                 }
970         }
971         if (ext2fs_test_valid(fs))
972                 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
973         else {
974                 printf(_("\n%s: ********** WARNING: Filesystem still has "
975                          "errors **********\n\n"), ctx->device_name);
976                 exit_value = FSCK_UNCORRECTED;
977         }
978         if (!(ctx->options & E2F_OPT_READONLY)) {
979                 if (ext2fs_test_valid(fs)) {
980                         if (!(sb->s_state & EXT2_VALID_FS))
981                                 exit_value = FSCK_NONDESTRUCT;
982                         sb->s_state = EXT2_VALID_FS;
983                 } else
984                         sb->s_state &= ~EXT2_VALID_FS;
985                 sb->s_mnt_count = 0;
986                 sb->s_lastcheck = time(NULL);
987                 ext2fs_mark_super_dirty(fs);
988         }
989         show_stats(ctx);
990
991         e2fsck_write_bitmaps(ctx);
992         
993 #ifdef RESOURCE_TRACK
994         if (ctx->options & E2F_OPT_TIME)
995                 print_resource_track(NULL, &ctx->global_rtrack);
996 #endif
997
998         ext2fs_close(fs);
999         ctx->fs = NULL;
1000         e2fsck_free_context(ctx);
1001         
1002         return exit_value;
1003 }