mirror of
https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
synced 2025-01-17 05:53:27 +08:00
5596defa1e
unix.c: Add support for calculating a progress bar if the -C0 option is given. The function e2fsck_clear_progbar() clears the progress bar and must be called before any message is issued. SIGUSR1 will enable the progress bar, and SIGUSR2 will disable the progress bar. This is used by fsck to handle parallel filesystem checks. Also, set the device_name from the filesystem label if it is available. e2fsck.h: Add new flags E2F_FLAG_PROG_BAR and E2F_FLAG_PROG_SUPRESS. Add new field in the e2fsck structure which contains the last tenth of a percent printed for the user. message.c (print_e2fsck_message): Add call to e2fsck_clear_progbar(). pass1.c (e2fsck_pass1): pass2.c (e2fsck_pass2): pass3.c (e2fsck_pass3): pass4.c (e2fsck_pass4): pass5.c (e2fsck_pass5): Add call to e2fsck_clear_progbar when printing the resource tracking information. pass5.c (check_block_bitmaps, check_inode_bitmaps): If there is an error in the bitmaps, suppress printing the progress bar using the suppression flag for the remainder of the check, in order to clean up the display.
821 lines
20 KiB
C
821 lines
20 KiB
C
/*
|
|
* unix.c - The unix-specific code for e2fsck
|
|
*
|
|
* Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
|
|
*
|
|
* %Begin-Header%
|
|
* This file may be redistributed under the terms of the GNU Public
|
|
* License.
|
|
* %End-Header%
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <termios.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#ifdef HAVE_GETOPT_H
|
|
#include <getopt.h>
|
|
#endif
|
|
#include <unistd.h>
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
#ifdef HAVE_MNTENT_H
|
|
#include <mntent.h>
|
|
#endif
|
|
#include <sys/ioctl.h>
|
|
#include <malloc.h>
|
|
|
|
#include "et/com_err.h"
|
|
#include "e2fsck.h"
|
|
#include "problem.h"
|
|
#include "../version.h"
|
|
|
|
extern int isatty(int);
|
|
|
|
/* Command line options */
|
|
static int blocksize = 0;
|
|
static int swapfs = 0;
|
|
static int normalize_swapfs = 0;
|
|
static int cflag = 0; /* check disk */
|
|
static int show_version_only = 0;
|
|
static int force = 0;
|
|
static int verbose = 0;
|
|
|
|
static int replace_bad_blocks = 0;
|
|
static char *bad_blocks_file = 0;
|
|
|
|
static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
|
|
|
|
static int root_filesystem = 0;
|
|
static int read_only_root = 0;
|
|
|
|
static void usage(e2fsck_t ctx)
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: %s [-panyrcdfvstFSV] [-b superblock] [-B blocksize]\n"
|
|
"\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
|
|
"\t\t[-l|-L bad_blocks_file] [-C fd] device\n",
|
|
ctx->program_name);
|
|
exit(FSCK_USAGE);
|
|
}
|
|
|
|
static void show_stats(e2fsck_t ctx)
|
|
{
|
|
ext2_filsys fs = ctx->fs;
|
|
int inodes, inodes_used, blocks, blocks_used;
|
|
int dir_links;
|
|
int num_files, num_links;
|
|
int frag_percent;
|
|
|
|
dir_links = 2 * ctx->fs_directory_count - 1;
|
|
num_files = ctx->fs_total_count - dir_links;
|
|
num_links = ctx->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);
|
|
|
|
frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
|
|
frag_percent = (frag_percent + 5) / 10;
|
|
|
|
if (!verbose) {
|
|
printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
|
|
ctx->device_name, inodes_used, inodes,
|
|
frag_percent / 10, frag_percent % 10,
|
|
blocks_used, blocks);
|
|
return;
|
|
}
|
|
printf ("\n%8d inode%s used (%d%%)\n", inodes_used,
|
|
(inodes_used != 1) ? "s" : "",
|
|
100 * inodes_used / inodes);
|
|
printf ("%8d non-contiguous inodes (%0d.%d%%)\n",
|
|
ctx->fs_fragmented, frag_percent / 10, frag_percent % 10);
|
|
printf (" # of inodes with ind/dind/tind blocks: %d/%d/%d\n",
|
|
ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
|
|
printf ("%8d block%s used (%d%%)\n"
|
|
"%8d bad block%s\n", blocks_used,
|
|
(blocks_used != 1) ? "s" : "",
|
|
100 * blocks_used / blocks, ctx->fs_badblocks_count,
|
|
ctx->fs_badblocks_count != 1 ? "s" : "");
|
|
printf ("\n%8d regular file%s\n"
|
|
"%8d director%s\n"
|
|
"%8d character device file%s\n"
|
|
"%8d block device file%s\n"
|
|
"%8d fifo%s\n"
|
|
"%8d link%s\n"
|
|
"%8d symbolic link%s (%d fast symbolic link%s)\n"
|
|
"%8d socket%s\n"
|
|
"--------\n"
|
|
"%8d file%s\n",
|
|
ctx->fs_regular_count,
|
|
(ctx->fs_regular_count != 1) ? "s" : "",
|
|
ctx->fs_directory_count,
|
|
(ctx->fs_directory_count != 1) ? "ies" : "y",
|
|
ctx->fs_chardev_count,
|
|
(ctx->fs_chardev_count != 1) ? "s" : "",
|
|
ctx->fs_blockdev_count,
|
|
(ctx->fs_blockdev_count != 1) ? "s" : "",
|
|
ctx->fs_fifo_count,
|
|
(ctx->fs_fifo_count != 1) ? "s" : "",
|
|
ctx->fs_links_count - dir_links,
|
|
((ctx->fs_links_count - dir_links) != 1) ? "s" : "",
|
|
ctx->fs_symlinks_count,
|
|
(ctx->fs_symlinks_count != 1) ? "s" : "",
|
|
ctx->fs_fast_symlinks_count,
|
|
(ctx->fs_fast_symlinks_count != 1) ? "s" : "",
|
|
ctx->fs_sockets_count, (ctx->fs_sockets_count != 1) ? "s" : "",
|
|
ctx->fs_total_count - dir_links,
|
|
((ctx->fs_total_count - dir_links) != 1) ? "s" : "");
|
|
}
|
|
|
|
static void check_mount(e2fsck_t ctx)
|
|
{
|
|
errcode_t retval;
|
|
int mount_flags, cont, fd;
|
|
|
|
retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
|
|
if (retval) {
|
|
com_err("ext2fs_check_if_mount", retval,
|
|
"while determining whether %s is mounted.",
|
|
ctx->filesystem_name);
|
|
return;
|
|
}
|
|
if (!(mount_flags & EXT2_MF_MOUNTED))
|
|
return;
|
|
|
|
#if (defined(__linux__) && defined(HAVE_MNTENT_H))
|
|
/*
|
|
* 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)
|
|
return;
|
|
} else
|
|
close(fd);
|
|
#endif
|
|
|
|
if (ctx->options & E2F_OPT_READONLY) {
|
|
printf("Warning! %s is mounted.\n", ctx->filesystem_name);
|
|
return;
|
|
}
|
|
|
|
printf("%s is mounted. ", ctx->filesystem_name);
|
|
if (!isatty(0) || !isatty(1)) {
|
|
printf("Cannot continue, aborting.\n\n");
|
|
exit(FSCK_ERROR);
|
|
}
|
|
printf("\n\n\007\007\007\007WARNING!!! "
|
|
"Running e2fsck on a mounted filesystem may cause\n"
|
|
"SEVERE filesystem damage.\007\007\007\n\n");
|
|
cont = ask_yn("Do you really want to continue", -1);
|
|
if (!cont) {
|
|
printf ("check aborted.\n");
|
|
exit (0);
|
|
}
|
|
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(e2fsck_t ctx)
|
|
{
|
|
ext2_filsys fs = ctx->fs;
|
|
const char *reason = NULL;
|
|
|
|
if (force || bad_blocks_file || cflag || swapfs)
|
|
return;
|
|
|
|
if (fs->super->s_state & EXT2_ERROR_FS)
|
|
reason = "contains a file system with errors";
|
|
else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
|
|
reason = "was not cleanly unmounted";
|
|
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", ctx->device_name, reason);
|
|
return;
|
|
}
|
|
printf("%s: clean, %d/%d files, %d/%d blocks\n", ctx->device_name,
|
|
fs->super->s_inodes_count - fs->super->s_free_inodes_count,
|
|
fs->super->s_inodes_count,
|
|
fs->super->s_blocks_count - fs->super->s_free_blocks_count,
|
|
fs->super->s_blocks_count);
|
|
ext2fs_close(fs);
|
|
exit(FSCK_OK);
|
|
}
|
|
|
|
/*
|
|
* For completion notice
|
|
*/
|
|
struct percent_tbl {
|
|
int max_pass;
|
|
int table[32];
|
|
};
|
|
struct percent_tbl e2fsck_tbl = {
|
|
5, { 0, 70, 90, 92, 95, 100 }
|
|
};
|
|
static int dpywidth = 50;
|
|
static char bar[] =
|
|
"==============================================================="
|
|
"===============================================================";
|
|
static char spaces[] =
|
|
" "
|
|
" ";
|
|
|
|
static float calc_percent(struct percent_tbl *tbl, int pass, int curr,
|
|
int max)
|
|
{
|
|
float percent;
|
|
|
|
if (pass <= 0)
|
|
return 0.0;
|
|
if (pass > tbl->max_pass)
|
|
return 100.0;
|
|
percent = ((float) curr) / ((float) max);
|
|
return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
|
|
+ tbl->table[pass-1]);
|
|
}
|
|
|
|
extern void e2fsck_clear_progbar(e2fsck_t ctx)
|
|
{
|
|
if (!(ctx->flags & E2F_FLAG_PROG_BAR))
|
|
return;
|
|
|
|
printf("%s\r", spaces + (sizeof(spaces) - 80));
|
|
ctx->flags &= ~E2F_FLAG_PROG_BAR;
|
|
}
|
|
|
|
static int e2fsck_update_progress(e2fsck_t ctx, int pass,
|
|
unsigned long cur, unsigned long max)
|
|
{
|
|
const char spinner[] = "\\|/-";
|
|
char buf[80];
|
|
int i;
|
|
float percent;
|
|
|
|
if (pass == 0)
|
|
return 0;
|
|
|
|
if (ctx->progress_fd) {
|
|
sprintf(buf, "%d %lu %lu\n", pass, cur, max);
|
|
write(ctx->progress_fd, buf, strlen(buf));
|
|
} else {
|
|
if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
|
|
return 0;
|
|
ctx->progress_pos = (ctx->progress_pos+1) & 3;
|
|
ctx->flags |= E2F_FLAG_PROG_BAR;
|
|
percent = calc_percent(&e2fsck_tbl, pass, cur, max);
|
|
if (ctx->progress_last_percent == (int) 1000 * percent)
|
|
return 0;
|
|
ctx->progress_last_percent = (int) 1000 * percent;
|
|
i = ((percent * dpywidth) + 50) / 100;
|
|
printf("%s: |%s%s", ctx->device_name,
|
|
bar + (sizeof(bar) - (i+1)),
|
|
spaces + (sizeof(spaces) - (dpywidth - i + 1)));
|
|
if (percent == 100.0)
|
|
fputc('|', stdout);
|
|
else
|
|
fputc(spinner[ctx->progress_pos & 3], stdout);
|
|
printf(" %4.1f%% \r", percent);
|
|
if (percent == 100.0)
|
|
e2fsck_clear_progbar(ctx);
|
|
fflush(stdout);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define PATH_SET "PATH=/sbin"
|
|
|
|
static void reserve_stdio_fds(NOARGS)
|
|
{
|
|
int fd;
|
|
|
|
while (1) {
|
|
fd = open("/dev/null", O_RDWR);
|
|
if (fd > 2)
|
|
break;
|
|
if (fd < 0) {
|
|
fprintf(stderr, "ERROR: Couldn't open "
|
|
"/dev/null (%s)\n",
|
|
strerror(errno));
|
|
break;
|
|
}
|
|
}
|
|
close(fd);
|
|
}
|
|
|
|
static e2fsck_t global_signal_ctx;
|
|
|
|
static void signal_progress_on(int sig)
|
|
{
|
|
e2fsck_t ctx = global_signal_ctx;
|
|
|
|
if (!ctx)
|
|
return;
|
|
|
|
ctx->progress = e2fsck_update_progress;
|
|
ctx->progress_fd = 0;
|
|
}
|
|
|
|
static void signal_progress_off(int sig)
|
|
{
|
|
e2fsck_t ctx = global_signal_ctx;
|
|
|
|
if (!ctx)
|
|
return;
|
|
|
|
e2fsck_clear_progbar(ctx);
|
|
ctx->progress = 0;
|
|
}
|
|
|
|
static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
|
|
{
|
|
int flush = 0;
|
|
int c;
|
|
#ifdef MTRACE
|
|
extern void *mallwatch;
|
|
#endif
|
|
char *oldpath = getenv("PATH");
|
|
e2fsck_t ctx;
|
|
errcode_t retval;
|
|
struct sigaction sa;
|
|
|
|
retval = e2fsck_allocate_context(&ctx);
|
|
if (retval)
|
|
return retval;
|
|
|
|
*ret_ctx = ctx;
|
|
|
|
/* Update our PATH to include /sbin */
|
|
if (oldpath) {
|
|
char *newpath;
|
|
|
|
newpath = (char *) malloc(sizeof (PATH_SET) + 1 +
|
|
strlen (oldpath));
|
|
if (!newpath)
|
|
fatal_error(ctx, "Couldn't malloc() newpath");
|
|
strcpy (newpath, PATH_SET);
|
|
strcat (newpath, ":");
|
|
strcat (newpath, oldpath);
|
|
putenv (newpath);
|
|
} else
|
|
putenv (PATH_SET);
|
|
|
|
setbuf(stdout, NULL);
|
|
setbuf(stderr, NULL);
|
|
initialize_ext2_error_table();
|
|
|
|
if (argc && *argv)
|
|
ctx->program_name = *argv;
|
|
else
|
|
ctx->program_name = "e2fsck";
|
|
while ((c = getopt (argc, argv, "panyrcC:B:dfvtFVM:b:I:P:l:L:N:Ss")) != EOF)
|
|
switch (c) {
|
|
case 'C':
|
|
ctx->progress = e2fsck_update_progress;
|
|
ctx->progress_fd = atoi(optarg);
|
|
break;
|
|
case 'p':
|
|
case 'a':
|
|
ctx->options |= E2F_OPT_PREEN;
|
|
ctx->options &= ~(E2F_OPT_YES|E2F_OPT_NO);
|
|
break;
|
|
case 'n':
|
|
ctx->options |= E2F_OPT_NO;
|
|
ctx->options &= ~(E2F_OPT_YES|E2F_OPT_PREEN);
|
|
break;
|
|
case 'y':
|
|
ctx->options |= E2F_OPT_YES;
|
|
ctx->options &= ~(E2F_OPT_PREEN|E2F_OPT_NO);
|
|
break;
|
|
case 't':
|
|
#ifdef RESOURCE_TRACK
|
|
if (ctx->options & E2F_OPT_TIME)
|
|
ctx->options |= E2F_OPT_TIME2;
|
|
else
|
|
ctx->options |= E2F_OPT_TIME;
|
|
#else
|
|
fprintf(stderr, "The -t option is not "
|
|
"supported on this version of e2fsck.\n");
|
|
#endif
|
|
break;
|
|
case 'c':
|
|
cflag++;
|
|
ctx->options |= E2F_OPT_CHECKBLOCKS;
|
|
break;
|
|
case 'r':
|
|
/* What we do by default, anyway! */
|
|
break;
|
|
case 'b':
|
|
ctx->use_superblock = atoi(optarg);
|
|
break;
|
|
case 'B':
|
|
blocksize = atoi(optarg);
|
|
break;
|
|
case 'I':
|
|
ctx->inode_buffer_blocks = atoi(optarg);
|
|
break;
|
|
case 'P':
|
|
ctx->process_inode_size = atoi(optarg);
|
|
break;
|
|
case 'L':
|
|
replace_bad_blocks++;
|
|
case 'l':
|
|
bad_blocks_file = (char *) malloc(strlen(optarg)+1);
|
|
if (!bad_blocks_file)
|
|
fatal_error(ctx,
|
|
"Couldn't malloc bad_blocks_file");
|
|
strcpy(bad_blocks_file, optarg);
|
|
break;
|
|
case 'd':
|
|
ctx->options |= E2F_OPT_DEBUG;
|
|
break;
|
|
case 'f':
|
|
force = 1;
|
|
break;
|
|
case 'F':
|
|
#ifdef BLKFLSBUF
|
|
flush = 1;
|
|
#else
|
|
fatal_error(ctx, "-F not supported");
|
|
#endif
|
|
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
|
|
case 'N':
|
|
ctx->device_name = optarg;
|
|
break;
|
|
case 's':
|
|
normalize_swapfs = 1;
|
|
case 'S':
|
|
swapfs = 1;
|
|
break;
|
|
default:
|
|
usage(ctx);
|
|
}
|
|
if (show_version_only)
|
|
return 0;
|
|
if (optind != argc - 1)
|
|
usage(ctx);
|
|
if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file &&
|
|
!cflag && !swapfs)
|
|
ctx->options |= E2F_OPT_READONLY;
|
|
ctx->filesystem_name = argv[optind];
|
|
if (flush) {
|
|
#ifdef BLKFLSBUF
|
|
int fd = open(ctx->filesystem_name, O_RDONLY, 0);
|
|
|
|
if (fd < 0) {
|
|
com_err("open", errno, "while opening %s for flushing",
|
|
ctx->filesystem_name);
|
|
exit(FSCK_ERROR);
|
|
}
|
|
if (ioctl(fd, BLKFLSBUF, 0) < 0) {
|
|
com_err("BLKFLSBUF", errno, "while trying to flush %s",
|
|
ctx->filesystem_name);
|
|
exit(FSCK_ERROR);
|
|
}
|
|
close(fd);
|
|
#else
|
|
fatal_error(ctx, "BLKFLSBUF not supported");
|
|
#endif /* BLKFLSBUF */
|
|
}
|
|
if (swapfs) {
|
|
if (cflag || bad_blocks_file) {
|
|
fprintf(stderr, "Incompatible options not "
|
|
"allowed when byte-swapping.\n");
|
|
exit(FSCK_ERROR);
|
|
}
|
|
}
|
|
/*
|
|
* Set up signal action
|
|
*/
|
|
memset(&sa, 0, sizeof(struct sigaction));
|
|
#ifdef SA_RESTART
|
|
sa.sa_flags = SA_RESTART;
|
|
#endif
|
|
global_signal_ctx = ctx;
|
|
sa.sa_handler = signal_progress_on;
|
|
sigaction(SIGUSR1, &sa, 0);
|
|
sa.sa_handler = signal_progress_off;
|
|
sigaction(SIGUSR2, &sa, 0);
|
|
return 0;
|
|
}
|
|
|
|
static const char *my_ver_string = E2FSPROGS_VERSION;
|
|
static const char *my_ver_date = E2FSPROGS_DATE;
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
errcode_t retval = 0;
|
|
int exit_value = FSCK_OK;
|
|
int i;
|
|
ext2_filsys fs = 0;
|
|
io_manager io_ptr;
|
|
struct ext2fs_sb *s;
|
|
const char *lib_ver_date;
|
|
int my_ver, lib_ver;
|
|
e2fsck_t ctx;
|
|
struct problem_context pctx;
|
|
int flags, run_result;
|
|
|
|
clear_problem_context(&pctx);
|
|
#ifdef MTRACE
|
|
mtrace();
|
|
#endif
|
|
#ifdef MCHECK
|
|
mcheck(0);
|
|
#endif
|
|
my_ver = ext2fs_parse_version_string(my_ver_string);
|
|
lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
|
|
if (my_ver > lib_ver) {
|
|
fprintf( stderr, "Error: ext2fs library version "
|
|
"out of date!\n");
|
|
show_version_only++;
|
|
}
|
|
|
|
retval = PRS(argc, argv, &ctx);
|
|
if (retval) {
|
|
com_err("e2fsck", retval,
|
|
"while trying to initialize program");
|
|
exit(1);
|
|
}
|
|
reserve_stdio_fds();
|
|
|
|
#ifdef RESOURCE_TRACK
|
|
init_resource_track(&ctx->global_rtrack);
|
|
#endif
|
|
|
|
if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
|
|
fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n",
|
|
my_ver_string, my_ver_date, EXT2FS_VERSION,
|
|
EXT2FS_DATE);
|
|
|
|
if (show_version_only) {
|
|
fprintf(stderr, "\tUsing %s, %s\n",
|
|
error_message(EXT2_ET_BASE), lib_ver_date);
|
|
exit(0);
|
|
}
|
|
|
|
check_mount(ctx);
|
|
|
|
if (!(ctx->options & E2F_OPT_PREEN) &&
|
|
!(ctx->options & E2F_OPT_NO) &&
|
|
!(ctx->options & E2F_OPT_YES)) {
|
|
if (!isatty (0) || !isatty (1))
|
|
fatal_error(ctx,
|
|
"need terminal for interactive repairs");
|
|
}
|
|
ctx->superblock = ctx->use_superblock;
|
|
restart:
|
|
#if 1
|
|
io_ptr = unix_io_manager;
|
|
#else
|
|
io_ptr = test_io_manager;
|
|
test_io_backing_manager = unix_io_manager;
|
|
#endif
|
|
flags = (ctx->options & E2F_OPT_READONLY) ? 0 : EXT2_FLAG_RW;
|
|
if (ctx->superblock && blocksize) {
|
|
retval = ext2fs_open(ctx->filesystem_name, flags,
|
|
ctx->superblock, blocksize, io_ptr, &fs);
|
|
} else if (ctx->superblock) {
|
|
for (i=0; possible_block_sizes[i]; i++) {
|
|
retval = ext2fs_open(ctx->filesystem_name, flags,
|
|
ctx->superblock,
|
|
possible_block_sizes[i],
|
|
io_ptr, &fs);
|
|
if (!retval)
|
|
break;
|
|
}
|
|
} else
|
|
retval = ext2fs_open(ctx->filesystem_name, flags,
|
|
0, 0, io_ptr, &fs);
|
|
if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
|
|
((retval == EXT2_ET_BAD_MAGIC) ||
|
|
((retval == 0) && ext2fs_check_desc(fs)))) {
|
|
if (!fs || (fs->group_desc_count > 1)) {
|
|
printf("%s trying backup blocks...\n",
|
|
retval ? "Couldn't find ext2 superblock," :
|
|
"Group descriptors look bad...");
|
|
ctx->superblock = get_backup_sb(fs);
|
|
if (fs)
|
|
ext2fs_close(fs);
|
|
goto restart;
|
|
}
|
|
}
|
|
if (retval) {
|
|
com_err(ctx->program_name, retval, "while trying to open %s",
|
|
ctx->filesystem_name);
|
|
if (retval == EXT2_ET_REV_TOO_HIGH) {
|
|
printf("The filesystem revision is apparently "
|
|
"too high for this version of e2fsck.\n"
|
|
"(Or the filesystem superblock "
|
|
"is corrupt)\n\n");
|
|
fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
|
|
} else if (retval == EXT2_ET_SHORT_READ)
|
|
printf("Could this be a zero-length partition?\n");
|
|
else if ((retval == EPERM) || (retval == EACCES))
|
|
printf("You must have %s access to the "
|
|
"filesystem or be root\n",
|
|
(ctx->options & E2F_OPT_READONLY) ?
|
|
"r/o" : "r/w");
|
|
else if (retval == ENXIO)
|
|
printf("Possibly non-existent or swap device?\n");
|
|
#ifdef EROFS
|
|
else if (retval == EROFS)
|
|
printf("Disk write-protected; use the -n option "
|
|
"to do a read-only\n"
|
|
"check of the device.\n");
|
|
#endif
|
|
else
|
|
fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
|
|
exit(FSCK_ERROR);
|
|
}
|
|
ctx->fs = fs;
|
|
fs->priv_data = ctx;
|
|
#ifdef EXT2_CURRENT_REV
|
|
if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) {
|
|
com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH,
|
|
"while trying to open %s",
|
|
ctx->filesystem_name);
|
|
get_newer:
|
|
fatal_error(ctx, "Get a newer version of e2fsck!");
|
|
}
|
|
#endif
|
|
/*
|
|
* Check for compatibility with the feature sets. We need to
|
|
* be more stringent than ext2fs_open().
|
|
*/
|
|
s = (struct ext2fs_sb *) fs->super;
|
|
if ((s->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
|
|
(s->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
|
|
com_err(ctx->program_name, EXT2_ET_UNSUPP_FEATURE,
|
|
"(%s)", ctx->filesystem_name);
|
|
goto get_newer;
|
|
}
|
|
if (s->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
|
|
com_err(ctx->program_name, EXT2_ET_RO_UNSUPP_FEATURE,
|
|
"(%s)", ctx->filesystem_name);
|
|
goto get_newer;
|
|
}
|
|
if (ctx->device_name == 0 &&
|
|
(s->s_volume_name[0] != 0)) {
|
|
char *cp = malloc(sizeof(s->s_volume_name)+1);
|
|
if (cp) {
|
|
strncpy(cp, s->s_volume_name,
|
|
sizeof(s->s_volume_name));
|
|
cp[sizeof(s->s_volume_name)] = 0;
|
|
ctx->device_name = cp;
|
|
}
|
|
}
|
|
if (ctx->device_name == 0)
|
|
ctx->device_name = ctx->filesystem_name;
|
|
|
|
/*
|
|
* 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 (ctx->superblock &&
|
|
!(ctx->options & E2F_OPT_READONLY))
|
|
ext2fs_mark_super_dirty(fs);
|
|
|
|
/*
|
|
* Don't overwrite the backup superblock and block
|
|
* descriptors, until we're sure the filesystem is OK....
|
|
*/
|
|
fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
|
|
|
|
ehandler_init(fs->io);
|
|
|
|
if (ctx->superblock)
|
|
set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
|
|
check_super_block(ctx);
|
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
|
exit(FSCK_ERROR);
|
|
check_if_skip(ctx);
|
|
if (bad_blocks_file)
|
|
read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
|
|
else if (cflag)
|
|
test_disk(ctx);
|
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
|
exit(FSCK_ERROR);
|
|
|
|
if (normalize_swapfs) {
|
|
if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ==
|
|
ext2fs_native_flag()) {
|
|
fprintf(stderr, "%s: Filesystem byte order "
|
|
"already normalized.\n", ctx->device_name);
|
|
exit(FSCK_ERROR);
|
|
}
|
|
}
|
|
if (swapfs) {
|
|
swap_filesys(ctx);
|
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
|
exit(FSCK_ERROR);
|
|
}
|
|
|
|
/*
|
|
* Mark the system as valid, 'til proven otherwise
|
|
*/
|
|
ext2fs_mark_valid(fs);
|
|
|
|
retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
|
|
if (retval) {
|
|
com_err(ctx->program_name, retval,
|
|
"while reading bad blocks inode");
|
|
preenhalt(ctx);
|
|
printf("This doesn't bode well, but we'll try to go on...\n");
|
|
}
|
|
|
|
run_result = e2fsck_run(ctx);
|
|
e2fsck_clear_progbar(ctx);
|
|
if (run_result == E2F_FLAG_RESTART) {
|
|
printf("Restarting e2fsck from the beginning...\n");
|
|
retval = e2fsck_reset_context(ctx);
|
|
if (retval) {
|
|
com_err(ctx->program_name, retval,
|
|
"while resetting context");
|
|
exit(1);
|
|
}
|
|
ext2fs_close(fs);
|
|
goto restart;
|
|
}
|
|
if (run_result & E2F_FLAG_SIGNAL_MASK)
|
|
exit(FSCK_ERROR);
|
|
if (run_result & E2F_FLAG_CANCEL)
|
|
ext2fs_unmark_valid(fs);
|
|
|
|
#ifdef MTRACE
|
|
mtrace_print("Cleanup");
|
|
#endif
|
|
if (ext2fs_test_changed(fs)) {
|
|
exit_value = FSCK_NONDESTRUCT;
|
|
if (!(ctx->options & E2F_OPT_PREEN))
|
|
printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n",
|
|
ctx->device_name);
|
|
if (root_filesystem && !read_only_root) {
|
|
printf("%s: ***** REBOOT LINUX *****\n",
|
|
ctx->device_name);
|
|
exit_value = FSCK_REBOOT;
|
|
}
|
|
}
|
|
if (ext2fs_test_valid(fs))
|
|
fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
|
|
else
|
|
exit_value = FSCK_UNCORRECTED;
|
|
if (!(ctx->options & E2F_OPT_READONLY)) {
|
|
if (ext2fs_test_valid(fs)) {
|
|
if (!(fs->super->s_state & EXT2_VALID_FS))
|
|
exit_value = FSCK_NONDESTRUCT;
|
|
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(ctx);
|
|
|
|
e2fsck_write_bitmaps(ctx);
|
|
|
|
#ifdef RESOURCE_TRACK
|
|
if (ctx->options & E2F_OPT_TIME)
|
|
print_resource_track(NULL, &ctx->global_rtrack);
|
|
#endif
|
|
|
|
e2fsck_free_context(ctx);
|
|
ext2fs_close(fs);
|
|
|
|
return exit_value;
|
|
}
|