diff options
author | Theodore Ts'o <tytso@mit.edu> | 1997-04-26 13:21:57 +0000 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 1997-04-26 13:21:57 +0000 |
commit | 3839e65723771b85975f4263102dd3ceec4523c0 (patch) | |
tree | 02fde6f8259837e842e12bdc97bb9f92e4155b44 /e2fsck/e2fsck.c | |
download | e2fsprogs-3839e65723771b85975f4263102dd3ceec4523c0.tar.gz |
Many files:
Checkin of e2fsprogs 0.5b
Diffstat (limited to 'e2fsck/e2fsck.c')
-rw-r--r-- | e2fsck/e2fsck.c | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c new file mode 100644 index 00000000..37d202b5 --- /dev/null +++ b/e2fsck/e2fsck.c @@ -0,0 +1,481 @@ +/* + * e2fsck.c - a consistency checker for the new extended file system. + * + * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be + * redistributed under the terms of the GNU Public License. + */ + +/* Usage: e2fsck [-dfpnsvy] device + * -d -- debugging this program + * -f -- check the fs even if it is marked valid + * -p -- "preen" the filesystem + * -n -- open the filesystem r/o mode; never try to fix problems + * -v -- verbose (tells how many files) + * -y -- always answer yes to questions + * + * The device may be a block device or a image of one, but this isn't + * enforced (but it's not much fun on a character device :-). + */ + +#include <string.h> +#include <fcntl.h> +#include <ctype.h> +#include <termios.h> +#include <time.h> +#include <getopt.h> +#include <unistd.h> +#include <mntent.h> +#include <sys/ioctl.h> +#include <malloc.h> + +#include "et/com_err.h" +#include "e2fsck.h" +#include "../version.h" + +extern int isatty(int); + +const char * program_name = "e2fsck"; +const char * device_name = NULL; + +/* Command line options */ +int nflag = 0; +int yflag = 0; +int tflag = 0; /* Do timing */ +int cflag = 0; /* check disk */ +int preen = 0; +int rwflag = 1; +int inode_buffer_blocks = 0; +blk_t superblock; +int blocksize = 0; +int verbose = 0; +int list = 0; +int debug = 0; +int force = 0; +static int show_version_only = 0; + +static int replace_bad_blocks = 0; +static char *bad_blocks_file = 0; + +static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0}; + +struct resource_track global_rtrack; + +static int root_filesystem = 0; +static int read_only_root = 0; + +static void usage(NOARGS) +{ + fprintf(stderr, + "Usage: %s [-panyrdfvtFV] [-b superblock] [-B blocksize]\n" + "\t\tdevice\n", program_name); + exit(FSCK_USAGE); +} + +static void show_stats(ext2_filsys fs) +{ + int inodes, inodes_used, blocks, blocks_used; + int dir_links; + int num_files, num_links; + + dir_links = 2 * fs_directory_count - 1; + num_files = fs_total_count - dir_links; + num_links = fs_links_count - dir_links; + inodes = fs->super->s_inodes_count; + inodes_used = (fs->super->s_inodes_count - + fs->super->s_free_inodes_count); + blocks = fs->super->s_blocks_count; + blocks_used = (fs->super->s_blocks_count - + fs->super->s_free_blocks_count); + + if (!verbose) { + printf("%s: %d/%d files, %d/%d blocks\n", device_name, + inodes_used, inodes, blocks_used, blocks); + return; + } + printf ("\n%6d inode%s used (%d%%)\n", inodes_used, + (inodes_used != 1) ? "s" : "", + 100 * inodes_used / inodes); + printf ("%6d block%s used (%d%%)\n" + "%6d bad block%s\n", blocks_used, + (blocks_used != 1) ? "s" : "", + 100 * blocks_used / blocks, fs_badblocks_count, + fs_badblocks_count != 1 ? "s" : ""); + printf ("\n%6d regular file%s\n" + "%6d director%s\n" + "%6d character device file%s\n" + "%6d block device file%s\n" + "%6d fifo%s\n" + "%6d link%s\n" + "%6d symbolic link%s (%d fast symbolic link%s)\n" + "%6d socket%s\n" + "------\n" + "%6d file%s\n", + fs_regular_count, (fs_regular_count != 1) ? "s" : "", + fs_directory_count, (fs_directory_count != 1) ? "ies" : "y", + fs_chardev_count, (fs_chardev_count != 1) ? "s" : "", + fs_blockdev_count, (fs_blockdev_count != 1) ? "s" : "", + fs_fifo_count, (fs_fifo_count != 1) ? "s" : "", + fs_links_count - dir_links, + ((fs_links_count - dir_links) != 1) ? "s" : "", + fs_symlinks_count, (fs_symlinks_count != 1) ? "s" : "", + fs_fast_symlinks_count, (fs_fast_symlinks_count != 1) ? "s" : "", + fs_sockets_count, (fs_sockets_count != 1) ? "s" : "", + fs_total_count - dir_links, + ((fs_total_count - dir_links) != 1) ? "s" : ""); +} + +static void check_mount(NOARGS) +{ + FILE * f; + struct mntent * mnt; + int cont; + int fd; + + if ((f = setmntent (MOUNTED, "r")) == NULL) + return; + while ((mnt = getmntent (f)) != NULL) + if (strcmp (device_name, mnt->mnt_fsname) == 0) + break; + endmntent (f); + if (!mnt) + return; + + if (!strcmp(mnt->mnt_dir, "/")) + root_filesystem = 1; + + /* + * If the root is mounted read-only, then /etc/mtab is + * probably not correct; so we won't issue a warning based on + * it. + */ + fd = open(MOUNTED, O_RDWR); + if (fd < 0) { + if (errno == EROFS) { + read_only_root = 1; + return; + } + } else + close(fd); + + if (!rwflag) { + printf("Warning! %s is mounted.\n", device_name); + return; + } + + printf ("%s is mounted. ", device_name); + if (isatty (0) && isatty (1)) + cont = ask_yn("Do you really want to continue", -1); + else + cont = 0; + if (!cont) { + printf ("check aborted.\n"); + exit (0); + } + return; +} + +static void sync_disks(NOARGS) +{ + sync(); + sync(); + sleep(1); + sync(); +} + +static void check_super_block(ext2_filsys fs) +{ + blk_t first_block, last_block; + int blocks_per_group = fs->super->s_blocks_per_group; + int i; + + first_block = fs->super->s_first_data_block; + last_block = first_block + blocks_per_group; + + for (i = 0; i < fs->group_desc_count; i++) { + if ((fs->group_desc[i].bg_block_bitmap < first_block) || + (fs->group_desc[i].bg_block_bitmap >= last_block)) { + printf("Block bitmap %ld for group %d not in group.\n", + fs->group_desc[i].bg_block_bitmap, i); + fatal_error(0); + } + if ((fs->group_desc[i].bg_inode_bitmap < first_block) || + (fs->group_desc[i].bg_inode_bitmap >= last_block)) { + printf("Inode bitmap %ld for group %d not in group.\n", + fs->group_desc[i].bg_inode_bitmap, i); + fatal_error(0); + } + if ((fs->group_desc[i].bg_inode_table < first_block) || + ((fs->group_desc[i].bg_inode_table + + fs->inode_blocks_per_group - 1) >= last_block)) { + printf("Inode table %ld for group %d not in group.\n", + fs->group_desc[i].bg_inode_table, i); + fatal_error(0); + } + first_block += fs->super->s_blocks_per_group; + last_block += fs->super->s_blocks_per_group; + } + return; +} + +/* + * This routine checks to see if a filesystem can be skipped; if so, + * it will exit with E2FSCK_OK. Under some conditions it will print a + * message explaining why a check is being forced. + */ +static void check_if_skip(ext2_filsys fs) +{ + const char *reason = NULL; + + if (force || bad_blocks_file || cflag) + return; + + if (fs->super->s_state & EXT2_ERROR_FS) + reason = "contains a file system with errors"; + else if (fs->super->s_mnt_count >= + (unsigned) fs->super->s_max_mnt_count) + reason = "has reached maximal mount count"; + else if (fs->super->s_checkinterval && + time(0) >= (fs->super->s_lastcheck + + fs->super->s_checkinterval)) + reason = "has gone too long without being checked"; + if (reason) { + printf("%s %s, check forced.\n", device_name, reason); + return; + } + if (fs->super->s_state & EXT2_VALID_FS) { + printf("%s is clean, no check.\n", device_name); + exit(FSCK_OK); + } +} + +static void PRS(int argc, char *argv[]) +{ + int flush = 0; + char c; +#ifdef MTRACE + extern void *mallwatch; +#endif + char *oldpath, newpath[PATH_MAX]; + + /* Update our PATH to include /sbin */ + strcpy(newpath, "PATH=/sbin:"); + if ((oldpath = getenv("PATH")) != NULL) + strcat(newpath, oldpath); + putenv(newpath); + + setbuf(stdout, NULL); + setbuf(stderr, NULL); + initialize_ext2_error_table(); + + if (argc && *argv) + program_name = *argv; + while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:")) != EOF) + switch (c) { + case 'p': + case 'a': + preen = 1; + yflag = nflag = 0; + break; + case 'n': + nflag = 1; + preen = yflag = 0; + break; + case 'y': + yflag = 1; + preen = nflag = 0; + break; + case 't': + tflag++; + break; + case 'c': + cflag++; + break; + case 'r': + /* What we do by default, anyway! */ + break; + case 'b': + superblock = atoi(optarg); + break; + case 'B': + blocksize = atoi(optarg); + break; + case 'I': + inode_buffer_blocks = atoi(optarg); + break; + case 'P': + process_inode_size = atoi(optarg); + break; + case 'L': + replace_bad_blocks++; + case 'l': + bad_blocks_file = malloc(strlen(optarg)+1); + if (!bad_blocks_file) + fatal_error("Couldn't malloc bad_blocks_file"); + strcpy(bad_blocks_file, optarg); + break; + case 'd': + debug = 1; + break; + case 'f': + force = 1; + break; + case 'F': + flush = 1; + break; + case 'v': + verbose = 1; + break; + case 'V': + show_version_only = 1; + break; +#ifdef MTRACE + case 'M': + mallwatch = (void *) strtol(optarg, NULL, 0); + break; +#endif + default: + usage (); + } + if (show_version_only) + return; + if (optind != argc - 1) + usage (); + if (nflag && !bad_blocks_file && !cflag) + rwflag = 0; + device_name = argv[optind]; + if (flush) { + int fd = open(device_name, O_RDONLY, 0); + + if (fd < 0) { + com_err("open", errno, "while opening %s for flushing", + device_name); + exit(FSCK_ERROR); + } + if (ioctl(fd, BLKFLSBUF, 0) < 0) { + com_err("BLKFLSBUF", errno, "while trying to flush %s", + device_name); + exit(FSCK_ERROR); + } + close(fd); + } +} + +int main (int argc, char *argv[]) +{ + errcode_t retval = 0; + int exit_value = FSCK_OK; + int i; + ext2_filsys fs; + +#ifdef MTRACE + mtrace(); +#endif +#ifdef MCHECK + mcheck(0); +#endif + + init_resource_track(&global_rtrack); + + PRS(argc, argv); + + if (!preen) + fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n", + E2FSPROGS_VERSION, E2FSPROGS_DATE, + EXT2FS_VERSION, EXT2FS_DATE); + + if (show_version_only) + exit(0); + + check_mount(); + + if (!preen && !nflag && !yflag) { + if (!isatty (0) || !isatty (1)) + die ("need terminal for interactive repairs"); + } + sync_disks(); + if (superblock && blocksize) { + retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0, + superblock, blocksize, unix_io_manager, + &fs); + } else if (superblock) { + for (i=0; possible_block_sizes[i]; i++) { + retval = ext2fs_open(device_name, + rwflag ? EXT2_FLAG_RW : 0, + superblock, + possible_block_sizes[i], + unix_io_manager, &fs); + if (!retval) + break; + } + } else + retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0, + 0, 0, unix_io_manager, &fs); + if (retval) { + com_err(program_name, retval, "while trying to open %s", + device_name); + printf("Couldn't find valid filesystem superblock.\n"); + fatal_error(0); + } + /* + * If the user specified a specific superblock, presumably the + * master superblock has been trashed. So we mark the + * superblock as dirty, so it can be written out. + */ + if (superblock && rwflag) + ext2fs_mark_super_dirty(fs); + + ehandler_init(fs->io); + + check_super_block(fs); + check_if_skip(fs); + if (bad_blocks_file) + read_bad_blocks_file(fs, bad_blocks_file, replace_bad_blocks); + else if (cflag) + test_disk(fs); + + /* + * Mark the system as valid, 'til proven otherwise + */ + ext2fs_mark_valid(fs); + + pass1(fs); + pass2(fs); + pass3(fs); + pass4(fs); + pass5(fs); + +#ifdef MTRACE + mtrace_print("Cleanup"); +#endif + if (ext2fs_test_changed(fs)) { + exit_value = FSCK_NONDESTRUCT; + if (!preen) + printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n", + device_name); + if (root_filesystem && !read_only_root) { + printf("%s: ***** REBOOT LINUX *****\n", device_name); + exit_value = FSCK_REBOOT; + } + } + if (!ext2fs_test_valid(fs)) + exit_value = FSCK_UNCORRECTED; + if (rwflag) { + if (ext2fs_test_valid(fs)) + fs->super->s_state = EXT2_VALID_FS; + else + fs->super->s_state &= ~EXT2_VALID_FS; + fs->super->s_mnt_count = 0; + fs->super->s_lastcheck = time(NULL); + ext2fs_mark_super_dirty(fs); + } + show_stats(fs); + + write_bitmaps(fs); + ext2fs_close(fs); + sync_disks(); + + if (tflag) + print_resource_track(&global_rtrack); + + return exit_value; +} |