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