Files
reiserfsprogs/fsck/main.c
T

710 lines
19 KiB
C

/*
* Copyright 1996-2000 Hans Reiser
*/
#include "fsck.h"
#include <getopt.h>
#include "../version.h"
#define print_usage_and_exit() die ("Usage: %s [options] "\
" device\n"\
"\n"\
"Options:\n\n"\
" --check\t\tconsistency checking (default)\n"\
" --rebuild-sb\t\tsuper block checking and rebuilding if needed\n"\
" --rebuild-tree\tforce fsck to rebuild filesystem from scratch\n"\
" \t\t\t(takes a long time)\n"\
" --interactive, -i\tmake fsck to stop after every stage\n"\
" -l | --logfile logfile\n"\
" \t\t\tmake fsck to complain to specifed file\n"\
" -b | --scan-marked-in-bitmap file\n"\
" \t\t\tbuild tree of blocks marked in the bitmapfile\n"\
" -c | --create-bitmap-file\n"\
" \t\t\tsave bitmap of found leaves\n"\
" -x | --fix-fixable\tfix corruptions which can be fixed w/o --rebuild-tree\n"\
" -o | --fix-non-critical\n"\
" \t\t\tfix strange modes, file sizes to real size and\n"\
" \t\t\trelocate files using busy objectids\n"\
" -q | --quiet\t\tno speed info\n"\
" -n | --nolog\t\t suppresses all logs\n"\
" -V\t\t\tprints version and exits\n"\
" -a\t\t\tmakes fsck to do nothing\n"\
" -p\t\t\tdo nothing, exist for compatibility with fsck(8)\n"\
" -r\n", argv[0]);
/* fsck is called with one non-optional argument - file name of device
containing reiserfs. This function parses other options, sets flags
based on parsing and returns non-optional argument */
static char * parse_options (struct fsck_data * data, int argc, char * argv [])
{
int c;
static int mode = FSCK_CHECK;
data->scan_area = USED_BLOCKS;
while (1) {
static struct option options[] = {
/* modes */
{"check", no_argument, &mode, FSCK_CHECK},
{"rebuild-sb", no_argument, &mode, FSCK_SB},
{"rebuild-tree", no_argument, &mode, FSCK_REBUILD},
/*
{"fast-rebuild", no_argument, &opt_fsck_mode, FSCK_FAST_REBUILD},
*/
/* options */
{"logfile", required_argument, 0, 'l'},
{"interactive", no_argument, 0, 'i'},
{"fix-fixable", no_argument, 0, 'x'},
{"fix-non-critical", no_argument, 0, 'o'},
{"quiet", no_argument, 0, 'q'},
{"nolog", no_argument, 0, 'n'},
/* if file exists ad reiserfs can be load of it - only
blocks marked used in that bitmap will be read */
{"scan-marked-in-bitmap", required_argument, 0, 'b'},
/* */
{"create-leaf-bitmap", required_argument, 0, 'c'},
/* all blocks will be read */
{"scan-whole-partition", no_argument, 0, 'S'},
/* special option: will mark free blocks used, zero all
unformatted node pointers and mark them free */
{"zero-files", no_argument, &mode, FSCK_ZERO_FILES},
{0, 0, 0, 0}
};
int option_index;
c = getopt_long (argc, argv, "iql:b:Sc:xoVaprt:n",
options, &option_index);
if (c == -1)
break;
switch (c) {
case 0:
/* long option specifying fsck mode is found */
break;
case 'i': /* --interactive */
data->options |= OPT_INTERACTIVE;
break;
case 'q': /* --quiet */
data->options |= OPT_QUIET;
break;
case 'l': /* --logfile */
asprintf (&data->log_file_name, "%s", optarg);
data->log = fopen (optarg, "w");
if (!data->log)
fprintf (stderr, "reiserfsck: could not open \'%s\': %m", optarg);
break;
case 'b': /* --scan-marked-in-bitmap */
/* will try to load a bitmap from a file and read only
blocks marked in it. That bitmap could be created by
previous run of reiserfsck with -c */
asprintf (&data->bitmap_file_name, "%s", optarg);
data->scan_area = EXTERN_BITMAP;
break;
case 'S': /* --scan-whole-partition */
data->scan_area = ALL_BLOCKS;
break;
case 'c': /* --create-leaf-bitmap */
asprintf (&data->new_bitmap_file_name, "%s", optarg);
data->options |= OPT_SAVE_EXTERN_BITMAP;
break;
case 'x': /* --fix-fixable */
data->options |= OPT_FIX_FIXABLE;
break;
case 'o': /* --fix-non-critical */
data->options |= OPT_FIX_NON_CRITICAL;
break;
case 'n': /* --nolog */
data->options |= OPT_SILENT;
break;
case 'V':
case 'p': /* these say reiserfsck to do nothing */
case 'r':
case 'a':
mode = DO_NOTHING;
break;
case 't':
mode = DO_TEST;
data->test = atoi (optarg);
break;
default:
print_usage_and_exit();
}
}
if (optind != argc - 1 && mode != DO_NOTHING)
/* only one non-option argument is permitted */
print_usage_and_exit();
data->mode = mode;
if (!data->log)
data->log = stderr;
return argv[optind];
}
reiserfs_filsys_t fs;
static void reset_super_block (reiserfs_filsys_t fs)
{
set_free_blocks (fs->s_rs, SB_BLOCK_COUNT (fs));
set_root_block (fs->s_rs, ~0);
set_tree_height (fs->s_rs, ~0);
/* make file system invalid unless fsck done () */
set_state (fs->s_rs, REISERFS_ERROR_FS);
if (is_reiser2fs_magic_string (fs->s_rs)) {
set_version (fs->s_rs, REISERFS_VERSION_2);
}
if (is_reiserfs_magic_string (fs->s_rs)) {
set_version (fs->s_rs, REISERFS_VERSION_1);
}
/* can be not set yet. If so, hash function will be set when first dir
entry will be found */
fs->s_hash_function = code2func (rs_hash (fs->s_rs));
/* objectid map is not touched */
mark_buffer_dirty (fs->s_sbh);
bwrite (fs->s_sbh);
}
reiserfs_bitmap_t uninsertable_leaf_bitmap;
int g_blocks_to_read;
/* on-disk bitmap is read, fetch it. create new bitmap, mark used blocks which
are always used (skipped, super block, journal area, bitmaps), create other
auxiliary bitmaps */
static void init_bitmaps (reiserfs_filsys_t fs)
{
unsigned long i;
unsigned long block_count;
unsigned long tmp;
block_count = SB_BLOCK_COUNT (fs);
switch (stats (fs)->scan_area) {
case ALL_BLOCKS:
fsck_disk_bitmap (fs) = reiserfs_create_bitmap (block_count);
reiserfs_bitmap_fill (fsck_disk_bitmap (fs));
fsck_progress ("Whole device (%d blocks) is to be scanned\n",
reiserfs_bitmap_ones (fsck_disk_bitmap (fs)));
break;
case USED_BLOCKS:
fsck_progress ("Loading on-disk bitmap .. ");
fsck_disk_bitmap (fs) = reiserfs_create_bitmap (block_count);
reiserfs_fetch_disk_bitmap (fsck_disk_bitmap (fs), fs);
fsck_progress ("%d bits set - done\n",
reiserfs_bitmap_ones (fsck_disk_bitmap (fs)));
break;
case EXTERN_BITMAP:
fsck_disk_bitmap (fs) = reiserfs_bitmap_load (stats (fs)->bitmap_file_name);
if (!fsck_disk_bitmap (fs))
reiserfs_panic ("could not load fitmap from \"%s\"",
stats (fs)->bitmap_file_name);
break;
default:
reiserfs_panic ("No area to scan specified");
}
/* pass 0 will skip super block and journal areas and bitmap blocks, find
how many blocks have to be read */
tmp = 0;
for (i = 0; i <= fs->s_sbh->b_blocknr; i ++) {
if (!reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), i))
continue;
reiserfs_bitmap_clear_bit (fsck_disk_bitmap (fs), i);
tmp ++;
}
/* unmark bitmaps */
for (i = 0; i < rs_bmap_nr (fs->s_rs); i ++) {
unsigned long block;
block = SB_AP_BITMAP (fs)[i]->b_blocknr;
if (!reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), block))
continue;
reiserfs_bitmap_clear_bit (fsck_disk_bitmap (fs), block);
tmp ++;
}
/* unmark journal area */
for (i = rs_journal_start (fs->s_rs);
i <= rs_journal_start (fs->s_rs) + rs_journal_size (fs->s_rs); i ++) {
if (!reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), i))
continue;
reiserfs_bitmap_clear_bit (fsck_disk_bitmap (fs), i);
tmp ++;
}
reiserfs_warning (stderr, "Skipping %d blocks (super block, journal, "
"bitmaps) %d blocks will be read\n",
tmp, reiserfs_bitmap_ones (fsck_disk_bitmap (fs)));
#if 0
{
int tmp = 0;
int tmp2 = 0;
int tmp3 = 0;
int j;
for (i = 0; i < block_count; i += 32) {
if (i + 32 < block_count && !*(int *)&(fsck_disk_bitmap (fs)->bm_map[i/8])) {
tmp2 ++;
continue;
}
tmp3 ++;
for (j = 0; j < 32 && ((i + j) < block_count); j ++) {
if (!reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), i + j))
continue;
if (not_data_block (fs, i + j)) {
reiserfs_bitmap_clear_bit (fsck_disk_bitmap (fs), i + j);
tmp ++;
continue;
}
}
}
/*
for (i = 0; i < block_count; i ++) {
if (!fsck_disk_bitmap (fs)->bm_map[i / 8])
continue;
if (!reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), i))
continue;
if (not_data_block (fs, i)) {
if (reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), i))
tmp ++;
reiserfs_bitmap_clear_bit (fsck_disk_bitmap (fs), i);
continue;
}
}
*/
reiserfs_warning (stderr, "%d not data blocks cleared (skipped %d checked %d)\n", tmp, tmp2, tmp3);
}
#endif
fsck_new_bitmap (fs) = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs));
/* mark_block_used skips 0, ste the bit explicitly */
reiserfs_bitmap_set_bit (fsck_new_bitmap (fs), 0);
/* mark other skipped blocks and super block used */
for (i = 1; i <= SB_BUFFER_WITH_SB (fs)->b_blocknr; i ++)
mark_block_used (i);
/* mark bitmap blocks as used */
for (i = 0; i < SB_BMAP_NR (fs); i ++)
mark_block_used (SB_AP_BITMAP (fs)[i]->b_blocknr);
/* mark journal area as used */
for (i = 0; i < JOURNAL_BLOCK_COUNT + 1; i ++)
mark_block_used (i + SB_JOURNAL_BLOCK (fs));
uninsertable_leaf_bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs));
reiserfs_bitmap_fill (uninsertable_leaf_bitmap);
fsck_allocable_bitmap (fs) = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs));
reiserfs_bitmap_fill (fsck_allocable_bitmap (fs));
}
#define REBUILD_WARNING \
"\nThis is an experimental version of reiserfsck, MAKE A BACKUP FIRST!\n\
Don't run this program unless something is broken. \n\
Some types of random FS damage can be recovered\n\
from by this program, which basically throws away the internal nodes\n\
of the tree and then reconstructs them. This program is for use only\n\
by the desperate, and is of only beta quality. Email\n\
reiserfs@devlinux.com with bug reports. \nWill rebuild the filesystem tree\n"
/*
warning #2
you seem to be running this automatically. you are almost
certainly doing it by mistake as a result of some script that
doesn't know what it does. doing nothing, rerun without -p if you
really intend to do this. */
void warn_what_will_be_done (struct fsck_data * data)
{
fsck_progress ("\n");
/* warn about fsck mode */
switch (data->mode) {
case FSCK_CHECK:
fsck_progress ("Will read-only check consistency of the partition\n");
if (data->options & OPT_FIX_FIXABLE)
fsck_progress ("\tWill fix what can be fixed w/o --rebuild-tree\n");
break;
case FSCK_SB:
fsck_progress ("Will check SB and rebuild if it is needed\n");
break;
case FSCK_REBUILD:
{
fsck_progress (REBUILD_WARNING);
if (data->options & OPT_INTERACTIVE)
fsck_progress ("\tWill stop after every stage and ask for "
"confirmation before continuing\n");
if (data->options & OPT_SAVE_EXTERN_BITMAP)
fsck_progress ("Will save list of found leaves in '%s'\n",
data->new_bitmap_file_name);
if (data->bitmap_file_name)
fsck_progress ("\tWill try to load bitmap of leaves from file '%s'\n",
data->bitmap_file_name);
if (data->options & OPT_FIX_NON_CRITICAL)
fsck_progress ("\tWill fix following non-critical things:\n"
"\t\tunknown file modes will be set to regular files\n"
"\t\tfile sizes will be set to real file size\n"
"\t\tfiles sharing busy inode number will be relocated\n");
break;
}
case FSCK_ZERO_FILES:
fsck_progress ("Will zero existing files and mark free blocks as used\n");
}
fsck_progress ("Will put log info to '%s'\n", (data->log != stderr) ?
data->log_file_name : "stderr");
if (!user_confirmed ("Do you want to run this program?[N/Yes] (note need to type Yes):", "Yes\n"))
exit (0);
}
static void start_rebuild (reiserfs_filsys_t fs)
{
reset_super_block (fs);
init_bitmaps (fs);
proper_id_map (fs) = init_id_map ();
semantic_id_map (fs) = init_id_map ();
}
/* called before semantic pass starts */
static void end_rebuilding (reiserfs_filsys_t fs)
{
reiserfs_flush_bitmap (fsck_new_bitmap (fs), fs);
flush_objectid_map (proper_id_map (fs), fs);
set_fsck_state (fs->s_rs, TREE_IS_BUILT);
set_free_blocks (fs->s_rs, reiserfs_bitmap_zeros (fsck_new_bitmap (fs)));
mark_buffer_dirty (SB_BUFFER_WITH_SB (fs));
/* write all dirty blocks */
fsck_progress ("Syncing.."); fflush (stdout);
reiserfs_flush (fs);
fsck_progress ("done\n"); fflush (stdout);
/* release what will not be needed */
reiserfs_delete_bitmap (fsck_disk_bitmap (fs));
reiserfs_delete_bitmap (fsck_allocable_bitmap (fs));
/* FIXME: could be not a bitmap */
reiserfs_delete_bitmap (uninsertable_leaf_bitmap);
if (fsck_user_confirmed (fs, "Tree building completed. "
"You can stop now and restart from this point later "
"(this is probably not what you need). Do you want to stop? ",
"Yes\n", 0/*default*/)) {
reiserfs_close (fs);
exit (4);
}
}
static int skip_rebuilding (reiserfs_filsys_t fs)
{
if (fsck_state (fs->s_rs) == TREE_IS_BUILT) {
if (fsck_user_confirmed (fs, "S+ tree of filesystem looks built. Skip rebuilding? ", "Yes\n", 0/*default*/)) {
fsck_new_bitmap (fs) = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs));
reiserfs_fetch_disk_bitmap (fsck_new_bitmap (fs), fs);
proper_id_map (fs) = init_id_map ();
fetch_objectid_map (proper_id_map (fs), fs);
semantic_id_map (fs) = init_id_map ();
return 1;
}
}
return 0;
}
static void start_continuing (reiserfs_filsys_t fs)
{
fsck_allocable_bitmap (fs) = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs));
reiserfs_bitmap_copy (fsck_allocable_bitmap (fs), fsck_new_bitmap (fs));
}
static void the_end (reiserfs_filsys_t fs)
{
reiserfs_flush_bitmap (fsck_new_bitmap (fs), fs);
flush_objectid_map (proper_id_map (fs), fs);
set_fsck_state (fs->s_rs, 0);
set_free_blocks (fs->s_rs, reiserfs_bitmap_zeros (fsck_new_bitmap (fs)));
set_state (fs->s_rs, REISERFS_VALID_FS);
mark_buffer_dirty (SB_BUFFER_WITH_SB (fs));
/* write all dirty blocks */
fsck_progress ("Syncing.."); fflush (stderr);
reiserfs_flush (fs);
sync ();
fsck_progress ("done\n"); fflush (stderr);
reiserfs_delete_bitmap (fsck_new_bitmap (fs));
free_id_map (&proper_id_map(fs));
if (semantic_id_map(fs))
free_id_map (&semantic_id_map(fs));
reiserfs_close (fs);
fsck_progress ("Done\n"); fflush (stderr);
}
static void rebuild_tree (reiserfs_filsys_t fs)
{
if (is_mounted (fs->file_name)) {
fsck_progress ("rebuild_tree: can not rebuild tree of mounted filesystem\n");
return;
}
reiserfs_reopen (fs, O_RDWR);
/* FIXME: for regular file take care of of file size */
/* rebuild starts with journal replaying */
reiserfs_replay_journal (fs);
if (!skip_rebuilding (fs)) {
fsck_progress ("Rebuilding..\n");
start_rebuild (fs);
pass_0 (fs);
/* passes 1 and 2. building of the tree */
pass_1_pass_2_build_the_tree ();
end_rebuilding (fs);
}
/* re-building of filesystem tree is now separated of sematic pass of the
fsck */
start_continuing (fs);
/* 3. semantic pass */
pass_3_semantic ();
/* if --lost+found is set - link unaccessed directories to lost+found
directory */
pass_3a_look_for_lost (fs);
/* 4. look for unaccessed items in the leaves */
pass_4_check_unaccessed_items ();
the_end (fs);
}
static void zero_files (reiserfs_filsys_t fs)
{
init_bitmaps (fs);
reiserfs_reopen (fs, O_RDWR);
pass_0 (fs);
}
/* check umounted or read-only mounted filesystems only */
static void check_fs (reiserfs_filsys_t fs)
{
if (!is_mounted (fs->file_name)) {
/* filesystem is not mounted, replay journal before checking */
reiserfs_reopen (fs, O_RDWR);
reiserfs_replay_journal (fs);
reiserfs_reopen (fs, O_RDONLY);
} else {
/* filesystem seems mounted. we do not check filesystems mounted with
r/w permissions */
if (!is_mounted_read_only (fs->file_name)) {
fsck_progress ("Device %s is mounted w/ write permissions, can not check it\n",
fs->file_name);
reiserfs_close (fs);
exit (0);
}
fsck_progress ("Filesystem seems mounted read-only. Skipping journal replay..\n");
if (fsck_fix_fixable (fs)) {
fsck_progress ("--fix-fixable ignored\n");
stats(fs)->options &= ~OPT_FIX_FIXABLE;
}
}
fsck_disk_bitmap (fs) = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs));
reiserfs_fetch_disk_bitmap (fsck_disk_bitmap (fs), fs);
if (fsck_fix_fixable (fs))
reiserfs_reopen (fs, O_RDWR);
/*proper_id_map (fs) = init_id_map ();*/
semantic_id_map (fs) = init_id_map ();
check_fs_tree (fs);
semantic_check ();
reiserfs_delete_bitmap (fsck_disk_bitmap (fs));
/*free_id_map (proper_id_map (fs));*/
free_id_map (&semantic_id_map (fs));
reiserfs_close (fs);
}
#include <sys/resource.h>
int main (int argc, char * argv [])
{
char * file_name;
struct fsck_data * data;
struct rlimit rlim = {0xffffffff, 0xffffffff};
print_banner ("reiserfsck");
/* this is only needed (and works) when running under 2.4 on regural files */
if (setrlimit (RLIMIT_FSIZE, &rlim) == -1) {
reiserfs_warning (stderr, "could not setrlimit: %m");
}
data = getmem (sizeof (struct fsck_data));
file_name = parse_options (data, argc, argv);
if (data->mode == DO_NOTHING) {
freemem (data);
return 0;
}
warn_what_will_be_done (data); /* and ask confirmation Yes */
fs = reiserfs_open (file_name, O_RDONLY, 0, data);
if (!fs)
die ("reiserfsck: could not open filesystem on \"%s\"", file_name);
if (fsck_mode (fs) == FSCK_SB) {
reiserfs_reopen (fs, O_RDWR);
rebuild_sb (fs);
reiserfs_close (fs);
return 0;
}
if (no_reiserfs_found (fs)) {
fsck_progress ("reiserfsck: --rebuild-sb may restore reiserfs super block\n");
reiserfs_close (fs);
return 0;
}
fs->block_allocator = reiserfsck_reiserfs_new_blocknrs;
fs->block_deallocator = reiserfsck_reiserfs_free_block;
if (fsck_mode (fs) == FSCK_CHECK) {
check_fs (fs);
return 0;
}
#ifdef FAST_REBUILD_READY /* and tested */
if (opt_fsck_mode == FSCK_FAST_REBUILD) {
__u32 root_block = SB_ROOT_BLOCK(fs);
reopen_read_write (file_name);
printf ("Replaying log..");
reiserfs_replay_journal (fs);
printf ("done\n");
if (opt_fsck == 1)
printf ("ReiserFS : checking %s\n",file_name);
else
printf ("Rebuilding..\n");
reset_super_block (fs);
SB_DISK_SUPER_BLOCK(fs)->s_root_block = cpu_to_le32 (root_block);
init_bitmaps (fs);
/* 1,2. building of the tree */
recover_internal_tree(fs);
/* 3. semantic pass */
pass3_semantic ();
/* if --lost+found is set - link unaccessed directories to
lost+found directory */
look_for_lost (fs);
/* 4. look for unaccessed items in the leaves */
pass4_check_unaccessed_items ();
end_fsck ();
}
#endif /* FAST REBUILD READY */
if (fsck_mode (fs) == FSCK_ZERO_FILES)
zero_files (fs);
if (fsck_mode (fs) != FSCK_REBUILD && fsck_mode (fs) != DO_TEST)
return 0;
/* the --rebuild-tree is here */
rebuild_tree (fs);
return 0;
}