summaryrefslogtreecommitdiff
path: root/e2fsck/e2fsck.c
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>1997-04-26 13:21:57 +0000
committerTheodore Ts'o <tytso@mit.edu>1997-04-26 13:21:57 +0000
commit3839e65723771b85975f4263102dd3ceec4523c0 (patch)
tree02fde6f8259837e842e12bdc97bb9f92e4155b44 /e2fsck/e2fsck.c
downloade2fsprogs-3839e65723771b85975f4263102dd3ceec4523c0.tar.gz
Many files:
Checkin of e2fsprogs 0.5b
Diffstat (limited to 'e2fsck/e2fsck.c')
-rw-r--r--e2fsck/e2fsck.c481
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;
+}