/*
 * Copyright 1996-2004 by Hans Reiser, licensing governed by 
 * reiserfsprogs/README
 */

#include "fsck.h"
#include <getopt.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <signal.h>
#include <limits.h>

extern int screen_width;
extern int screen_savebuffer_len;
extern char *screen_savebuffer;

reiserfs_filsys_t * fs;
char * badblocks_file;

#define print_usage_and_exit() {						\
fsck_progress ("Usage: %s [mode] [options] "					\
" device\n"									\
"\n"										\
"Modes:\n"									\
"  --check\t\t\tconsistency checking (default)\n"				\
"  --fix-fixable\t\t\tfix corruptions which can be fixed without \n"		\
"  \t\t\t\t--rebuild-tree\n"							\
"  --rebuild-sb\t\t\tsuper block checking and rebuilding if needed\n"		\
"  \t\t\t\t(may require --rebuild-tree afterwards)\n"				\
"  --rebuild-tree\t\tforce fsck to rebuild filesystem from scratch\n"		\
"  \t\t\t\t(takes a long time)\n"						\
"  --clean-attributes\t\tclean garbage in reserved fields in StatDatas \n"	\
"Options:\n"									\
"  -j | --journal device\t\tspecify journal if relocated\n"			\
"  -B | --badblocks file\t\tfile with list of all bad blocks on the fs\n"			\
"  -l | --logfile file\t\tmake fsck to complain to specifed file\n"		\
"  -n | --nolog\t\t\tmake fsck to not complain\n"				\
"  -z | --adjust-size\t\tfix file sizes to real size\n"				\
"  -q | --quiet\t\t\tno speed info\n"						\
"  -y | --yes\t\t\tno confirmations\n"						\
"  -f | --force\t\tforce checking even if the file system is marked clean\n"\
"  -V\t\t\t\tprints version and exits\n"					\
"  -a and -p\t\t\tsome light-weight auto checks for bootup\n"			\
"  -r\t\t\tignored\n"							\
"Expert options:\n"								\
"  --no-journal-available\tdo not open nor replay journal\n"			\
"  -S | --scan-whole-partition\tbuild tree of all blocks of the device\n\n",	\
  argv[0]);									\
										\
  exit(EXIT_OK);								\
}

/*
   -B works with --fix-fixable
        fixes indirect pointers pointed to
	badblocks, adds badblocks to badblock list in fs.
    and with --rebuild
        builds the tree without pointers to badblocks (internal,
	indirect), adds badblocks to badblock list in fs.  
*/

/*
Hidden usage:
Modes:
"  --rollback-fsck-changes\n\t\t\trollback all changes made by fsck\n"\
Options:
"  -b | --scan-marked-in-bitmap file\n"\
"  \t\t\tbuild tree of blocks marked in the bitmapfile\n"\
"  -R | --rollback-data file\n"\
"  \t\t\tback up all changes to this file or rollback from this file\n"\
"  \t\t\tpreviously backed up changes with --rollback-fsck-changes\n"\
"  -d dumpfile\n"\
"  \t\t\tto test fsck pass by pass - dump into dumpfile all needed\n"\
"  \t\t\tinfo for the next pass and load on the start of the next pass\n"\
"  -i | --interactive\tmake fsck to stop after every stage\n"\
"  -h | --hash hashname\n"\
"  -g | --background\n"\
"  -t \t\tdo test\n"\
*/


/* 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;
    static int flag;

    data->rebuild.scan_area = USED_BLOCKS;
    while (1) {
	static struct option options[] = {
	    /* modes */
	    {"check", no_argument, &mode, FSCK_CHECK},
	    {"fix-fixable", no_argument, &mode, FSCK_FIX_FIXABLE},
	    {"rebuild-sb", no_argument, &mode, FSCK_SB},
	    {"rebuild-tree", no_argument, &mode, FSCK_REBUILD},
	    {"rollback-fsck-changes", no_argument, &mode, FSCK_ROLLBACK_CHANGES},
	    {"clean-attributes", no_argument, &mode, FSCK_CLEAN_ATTRIBUTES},
	    /* options */
	    {"logfile", required_argument, 0, 'l'},
	    {"badblocks", required_argument, 0, 'B'},
	    {"interactive", no_argument, 0, 'i'},
	    {"adjust-size", no_argument, 0, 'z'},
	    {"quiet", no_argument, 0, 'q'},
	    {"yes", no_argument, 0, 'y'},
	    {"force", no_argument, 0, 'f'},
	    {"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-passes-dump", required_argument, 0, 'd'},
	
	    /* all blocks will be read */
	    {"scan-whole-partition", no_argument, 0, 'S'},
	    /* useful for -S */
	    {"hash", required_argument, 0, 'h'},

            /* preparing rollback data*/
	    {"rollback-data", required_argument, 0, 'R'},
	    
	    {"journal", required_argument, 0, 'j'},
	    {"no-journal-available", no_argument, &flag, OPT_SKIP_JOURNAL},
	    
	    {"bad-block-file", required_argument, 0, 'B'},

	    /* start reiserfsck in background and exit */
	    {"background", no_argument, 0, 'g'},

	    {0, 0, 0, 0}
	};
	int option_index;
      
	c = getopt_long (argc, argv, "iql:nb:Szd:R:h:j:gafVrpyt:B:",
			 options, &option_index);
	if (c == -1)
	    break;
	
	switch (c) {
	case 0:
	    /* long-only options */
	    if (flag == OPT_SKIP_JOURNAL) {
		/* no journal available */
		data->options |= OPT_SKIP_JOURNAL;
		flag = 0;
	    }
	    break; 

	case 'i': /* --interactive */
	    data->options |= OPT_INTERACTIVE;
	    break;

	case 'q': /* --quiet */
	    data->options |= OPT_QUIET;
	    break;

	case 'y': /* --quiet */
	    data->options |= OPT_YES;
	    break;

	case 'l': /* --logfile */
	    data->log_file_name = optarg;
	    /*asprintf (&data->log_file_name, "%s", optarg);*/
	    data->log = fopen (optarg, "w");
	    if (!data->log)
		fprintf (stderr, "reiserfsck: Cannot not open \'%s\': %s", 
		    optarg, strerror(errno));	    
	    break;

	case 'n': /* --nolog */
	    data->options |= OPT_SILENT;
	    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 */
	    data->rebuild.bitmap_file_name = optarg;
	    /*asprintf (&data->rebuild.bitmap_file_name, "%s", optarg);*/
	    data->rebuild.scan_area = EXTERN_BITMAP;
	    break;

	case 'S': /* --scan-whole-partition */
	    data->rebuild.scan_area = ALL_BLOCKS;
	    break;

#if 0
	case 'J': /* take all blocks which are leaves in journal area and put
                     them into tree item by item (DO NOT USE IT UNTIL YOU KNOW
                     WHAT ARE YOU DOING) */
	    data->rebuild.use_journal_area = 1;
	    break;
#endif
	case 'd': /* --create-passes-dump */
	    asprintf (&data->rebuild.passes_dump_file_name, "%s", optarg);
	    data->options |= OPT_SAVE_PASSES_DUMP;
	    break;
	
	case 'z': /* --adjust-file-size */
	    data->options |= OPT_ADJUST_FILE_SIZE;
	    break;

	case 'h': /* --hash: suppose that this hash was used on a filesystem */
	    asprintf (&data->rebuild.defined_hash, "%s", optarg);
	    if (name2func (data->rebuild.defined_hash) == 0)
		reiserfs_panic ("reiserfsck: Unknown hash is specified: %s",
				data->rebuild.defined_hash);
	    data->options |= OPT_HASH_DEFINED;
	    break;

	case 'j': /* specified relocated journal device */
	    data->journal_dev_name = optarg;
	    break;

	case 'R': /* preparing rollback data */
	    asprintf (&data->rebuild.rollback_file, "%s", optarg);
	    data->options |= OPT_SAVE_ROLLBACK;
	    break;
	
	case 'B': /* list of phisically corrupted blocks */
	    asprintf (&badblocks_file, "%s", optarg);
	    data->options |= BADBLOCKS_FILE;
	    break;

	case 'g': /* --background */
	    data->options |= OPT_BACKGROUND;
	    break;

	case 'a':
	case 'p':
		data->options |= OPT_QUIET;
		mode = FSCK_AUTO;
		break;
	
	case 'f':
		data->options |= OPT_FORCE;
		break;
	case 'r': /* ignored */
	    break;
	    
	case 'V': /* cause fsck to do nothing */
	    mode = DO_NOTHING;
	    break;
	
	case 't':
	    mode = DO_TEST;
	    data->rebuild.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();

    if (mode != FSCK_REBUILD && 
               (data->rebuild.scan_area == EXTERN_BITMAP ||
                data->rebuild.scan_area == ALL_BLOCKS || 
                data->options & OPT_SAVE_PASSES_DUMP))
	/* wrong options for this mode */
	print_usage_and_exit();

/*
    if (data->options & OPT_ADJUST_FILE_SIZE) {
        if ((mode != FSCK_REBUILD) && (mode != FSCK_FIX_FIXABLE)) 
            print_usage_and_exit();
    }
*/    
    
    if (data->options & OPT_SAVE_ROLLBACK) {
        if (mode == FSCK_SB)
            print_usage_and_exit();        
    }

    if (mode == FSCK_ROLLBACK_CHANGES) {
        if ((data->options & OPT_SAVE_ROLLBACK) == 0)
            print_usage_and_exit();        
    }

    if ((data->options & BADBLOCKS_FILE) && mode != FSCK_REBUILD && 
	mode != FSCK_FIX_FIXABLE)
    {
	fprintf(stderr, "Badblocks can be specified with --fix-fixable or "
		"--rebuild-tree only.\n");
	print_usage_and_exit();
    }

    if ((mode == FSCK_REBUILD) && (data->options & OPT_YES))
	data->options &= ~OPT_YES;
    
    data->mode = mode;
    if (!data->log)
	data->log = stdout;   

    return argv[optind];
}


#define REBUILD_WARNING \
"*************************************************************\n\
** Do not  run  the  program  with  --rebuild-tree  unless **\n\
** something is broken and MAKE A BACKUP  before using it. **\n\
** If you have bad sectors on a drive  it is usually a bad **\n\
** idea to continue using it. Then you probably should get **\n\
** a working hard drive, copy the file system from the bad **\n\
** drive  to the good one -- dd_rescue is  a good tool for **\n\
** that -- and only then run this program.                 **\n\
** If you are using the latest reiserfsprogs and  it fails **\n\
** please  email bug reports to reiserfs-list@namesys.com, **\n\
** providing  as  much  information  as  possible --  your **\n\
** hardware,  kernel,  patches,  settings,  all reiserfsck **\n\
** messages  (including version),  the reiserfsck logfile, **\n\
** check  the  syslog file  for  any  related information. **\n\
** If you would like advice on using this program, support **\n\
** is available  for $25 at  www.namesys.com/support.html. **\n\
*************************************************************\n\
\nWill rebuild the filesystem (%s) tree\n"

#define START_WARNING \
"*************************************************************\n\
** If you are using the latest reiserfsprogs and  it fails **\n\
** please  email bug reports to reiserfs-list@namesys.com, **\n\
** providing  as  much  information  as  possible --  your **\n\
** hardware,  kernel,  patches,  settings,  all reiserfsck **\n\
** messages  (including version),  the reiserfsck logfile, **\n\
** check  the  syslog file  for  any  related information. **\n\
** If you would like advice on using this program, support **\n\
** is available  for $25 at  www.namesys.com/support.html. **\n\
*************************************************************\n\
\n"


void warn_what_will_be_done (char * file_name, struct fsck_data * data)
{
    FILE * warn_to;

    warn_to = (data->progress ? data->progress : stderr);

    if (data->mode == FSCK_REBUILD)
	reiserfs_warning (warn_to, REBUILD_WARNING, file_name);
    else
	reiserfs_warning (warn_to, START_WARNING);
    
    /* warn about fsck mode */
    switch (data->mode) {
    case FSCK_CHECK:
	reiserfs_warning (warn_to, "Will read-only check consistency of the "
	    "filesystem on %s\n", file_name);
	
	break;

    case FSCK_FIX_FIXABLE:
        
	reiserfs_warning (warn_to, "Will check consistency of the filesystem "
	    "on %s\n", file_name);
        reiserfs_warning (warn_to, "and will fix what can be fixed without "
	    "--rebuild-tree\n");
	
	break;

    case FSCK_SB:
	reiserfs_warning (warn_to, "Will check superblock and rebuild it if "
	    "needed\n");
	
	break;

    case FSCK_REBUILD:
	if (data->options & OPT_SAVE_PASSES_DUMP) {
	    reiserfs_warning (warn_to, "Will run only 1 step of the rebuilding, "
		"write state file '%s' and exit\n", 
		data->rebuild.passes_dump_file_name);
	} else if (data->options & OPT_INTERACTIVE)
	    reiserfs_warning (warn_to, "Will stop after every stage and ask for "
                "confirmation before continuing\n");

	if (data->rebuild.bitmap_file_name)
	    reiserfs_warning (warn_to, "Will try to load a bitmap--of all "
		"ReiserFS leaves in the partition--from the file \n'%s'\n",
		data->rebuild.bitmap_file_name);

	if (data->options & OPT_ADJUST_FILE_SIZE)
	    reiserfs_warning (warn_to, "\tWill set file sizes in their metadata "
		"to real file sizes actually found by fsck.\n");

	if (data->options & OPT_HASH_DEFINED)
	    reiserfs_warning (warn_to, "\tSuppose \"%s\" hash is in use\n",
		data->rebuild.defined_hash);
	
	break;
	
    case FSCK_ROLLBACK_CHANGES:
	reiserfs_warning (warn_to, "Will rollback all data saved in %s into %s\n",
	    data->rebuild.rollback_file, file_name);
	
        break;
    case FSCK_CLEAN_ATTRIBUTES:
	reiserfs_warning (warn_to, "Will clean file attributes on %s\n", 
	    file_name);
        break;
    case FSCK_AUTO:
	return;
    }

    if (data->options & OPT_SAVE_ROLLBACK && data->mode != FSCK_ROLLBACK_CHANGES)
        reiserfs_warning (warn_to, "Will save all blocks to be changed into "
	    "file '%s'\n", data->rebuild.rollback_file);

    if (data->options & BADBLOCKS_FILE)
        reiserfs_warning (warn_to,
            "Bad block list will contain only blocks specified in '%s' "
	    "file\n", badblocks_file);

    reiserfs_warning (warn_to, "Will put log info to '%s'\n", 
	(data->log == stdout) ? "stdout" : 
	(data->log_file_name ? data->log_file_name : "fsck.run"));
    
    if (!(data->options & OPT_YES) && !user_confirmed (warn_to, "\nDo you want to "
	"run this program?[N/Yes] (note need to type Yes if you do):", "Yes\n"))
	exit (EXIT_USER);
}

static dma_info_t dma_info;
static dma_info_t old_dma_info;

void check_dma() {
    old_dma_info = dma_info;
    if (get_dma_info(&dma_info) == -1) {
	fsck_log("get_dma_info failed %s\n", strerror (errno));
	return;
    }
    
    if (dma_info.dma != old_dma_info.dma) {
	if (dma_info.dma == 0) {
	    printf("\n********************************************************************\n");
	    printf("* Warning: It was just detected that dma mode was turned off while *\n");
	    printf("* operating -- probably  due  to some  problem with your hardware. *\n");
	    printf("* Please check your hardware and have a look into the syslog file. *\n");
	    printf("* Note: running with --rebuild-tree on faulty hardware may destroy *\n");
	    printf("* your data.                                                       *\n");
	    printf("********************************************************************\n");
	    if (fsck_log_file (fs) != stdout)
		fsck_log("WARNING: dma mode has been turned off.\n");
	}
    }
    if (dma_info.speed != old_dma_info.speed) {
	if (dma_info.speed < old_dma_info.speed) {
	    printf("\n********************************************************************\n");
	    printf("* Warning: It was just detected that dma speed was descreased while*\n");
	    printf("* operating -- probably  due  to some  problem with your hardware. *\n");
	    printf("* Please check your hardware and have a look into the syslog file. *\n");
	    printf("* Note: running with --rebuild-tree on faulty hardware may destroy *\n");
	    printf("* your data.                                                       *\n");
	    printf("********************************************************************\n");
	    if (fsck_log_file (fs) != stdout)
		fsck_log("WARNING: dma speed has been descreased.\n");	    
	}
    }
    
    alarm(1);
}

void register_timer() {
    memset(&dma_info, 0, sizeof(dma_info));
    memset(&old_dma_info, 0, sizeof(old_dma_info));
    
    dma_info.fd = fs->fs_dev;

    if (prepare_dma_check(&dma_info) != 0)
	return;

    if (get_dma_info(&dma_info) == -1) {
	fsck_log("get_dma_info failed %s\n", strerror (errno));
	return;
    }

    if (dma_info.dma == 0) {
	printf("\n******************************************************\n");
	printf("* Warning: The dma on your hard drive is turned off. *\n");
	printf("* This may really slow down the fsck process.        *\n");
	printf("******************************************************\n");
	if (fsck_log_file (fs) != stdout)
	    fsck_log("WARNING: DMA is turned off\n");
    } 
    
    signal(SIGALRM, check_dma);
    alarm(1);
}

static void reset_super_block (reiserfs_filsys_t * fs)
{
    struct reiserfs_super_block * sb;
    struct reiserfs_journal_header * jh;
    
    sb = fs->fs_ondisk_sb;

    set_sb_free_blocks (sb, get_sb_block_count (sb));
    set_sb_root_block (sb, 0);
    set_sb_tree_height (sb, ~0);

    /* make file system invalid unless fsck finished () */
    set_sb_fs_state (sb, get_sb_fs_state (sb) | FS_FATAL);

/*
    if ( is_reiser2fs_jr_magic_string (sb) ) {???
	set_sb_version (sb, REISERFS_VERSION_3);
    }
    if (is_reiser2fs_magic_string (sb)) {
	set_sb_version (sb, REISERFS_FORMAT_3_6);
    }
    if (is_reiserfs_magic_string (sb)) {
	set_sb_version (sb, REISERFS_FORMAT_3_5);
    }
*/
    /* make sure that hash code in super block match to set hash function */
    set_sb_hash_code (sb, func2code (fs->fs_hash_function));
    
    if (fsck_hash_defined (fs)) {
	/* --hash was specifed */
	fs->fs_hash_function = name2func (fsck_data (fs)->rebuild.defined_hash);
	set_sb_hash_code (sb, func2code (fs->fs_hash_function));
    }
    
    if (reiserfs_journal_opened (fs)) {	
	jh = (struct reiserfs_journal_header *)fs->fs_jh_bh->b_data;
    
	/* reset journal params if needed. */
	if (memcmp(sb_jp(sb), &jh->jh_journal, sizeof (struct journal_params))) {
	    if (is_reiserfs_jr_magic_string (sb)) 
		memcpy (sb_jp(sb), &jh->jh_journal, sizeof (struct journal_params));
	    else {
		set_sb_reserved_for_journal (sb, 0);
	    
		set_jp_journal_dev (sb_jp(sb), 0);
		set_jp_journal_magic (sb_jp(sb), get_random());
		set_jp_journal_1st_block (sb_jp(sb), get_journal_start_must (fs));
		set_jp_journal_size (sb_jp(sb),
		    journal_default_size (fs->fs_super_bh->b_blocknr, fs->fs_blocksize));	    
		set_jp_journal_max_trans_len (sb_jp(sb),
		    advise_journal_max_trans_len(
			get_jp_journal_max_trans_len (sb_jp(sb)),
			get_jp_journal_size (sb_jp(sb)),
			fs->fs_blocksize, 0));
		set_jp_journal_max_batch (sb_jp(sb), 
		    advise_journal_max_batch(get_jp_journal_max_trans_len (sb_jp(sb))));
		set_jp_journal_max_commit_age (sb_jp(sb), advise_journal_max_commit_age());
		set_jp_journal_max_trans_age (sb_jp(sb), advise_journal_max_trans_age());
		
		set_jp_journal_dev (&jh->jh_journal, 0);
		set_jp_journal_magic (&jh->jh_journal, 
				      get_jp_journal_magic(sb_jp(sb)));
		set_jp_journal_1st_block (&jh->jh_journal, 
		    get_jp_journal_1st_block(sb_jp(sb)));
		set_jp_journal_size (&jh->jh_journal, get_jp_journal_size(sb_jp(sb)));
		set_jp_journal_max_trans_len (&jh->jh_journal, 
		    get_jp_journal_max_trans_len(sb_jp(sb)));
		set_jp_journal_max_batch (&jh->jh_journal, 
		    get_jp_journal_max_batch(sb_jp(sb)));
		set_jp_journal_max_commit_age (&jh->jh_journal, 
		    get_jp_journal_max_commit_age(sb_jp(sb)));
		set_jp_journal_max_trans_age (&jh->jh_journal, 
		    get_jp_journal_max_trans_age(sb_jp(sb))); 
	    }
	}
    }

    /* objectid map is not touched */
    mark_buffer_dirty (fs->fs_super_bh);
    bwrite (fs->fs_super_bh);
    if (!(fsck_data(fs)->options & OPT_SAVE_PASSES_DUMP))
        mark_buffer_do_not_flush (fs->fs_super_bh);
}


#define START_FROM_THE_BEGINNING 	1
#define START_FROM_PASS_1 		2
#define START_FROM_PASS_2 		3
#define START_FROM_SEMANTIC 		4
#define START_FROM_LOST_FOUND 		5
#define START_FROM_PASS_4 		6

/* this decides where to start from  */
static int where_to_start_from (reiserfs_filsys_t * fs)
{
    int ret;
    FILE * fp = 0;
    int last_run_state;
    
    last_run_state = get_sb_fs_state (fs->fs_ondisk_sb);
    if (last_run_state == 0 || !fsck_run_one_step (fs))
	/**/
	return START_FROM_THE_BEGINNING;
    
    /* We are able to perform the next step only if there is a file with the previous 
     * step results. */
    fp = open_file (state_dump_file (fs), "r");
    if (fp == 0) {
	set_sb_fs_state (fs->fs_ondisk_sb, 0);
	return START_FROM_THE_BEGINNING;
    }

    /* check start and end magics of dump file */
    ret = is_stage_magic_correct (fp);
    
    if (ret <= 0 || ret != last_run_state)
	return START_FROM_THE_BEGINNING;


    switch (last_run_state) {
    case PASS_0_DONE:
	/* skip pass 0 */
	if (!fsck_user_confirmed (fs, "Pass 0 seems finished. Start from pass 1?(Yes)",
				  "Yes\n", 1))
	    fsck_exit ("Run without -d then\n");
	
	load_pass_0_result (fp, fs);
	fclose (fp);
	return START_FROM_PASS_1;
	
    case PASS_1_DONE:
	/* skip pass 1 */
	if (!fsck_user_confirmed (fs, "Passes 0 and 1 seems finished. Start from "
	    "pass 2?(Yes)", "Yes\n", 1)) 
	{
	    fsck_exit ("Run without -d then\n");
	}
	
	load_pass_1_result (fp, fs);
	fclose (fp);
	return START_FROM_PASS_2;
	
    case TREE_IS_BUILT:
	if (!fsck_user_confirmed (fs, "Internal tree of filesystem looks built. "
	    "Skip rebuilding?(Yes)", "Yes\n", 1))
	{
	    fsck_exit ("Run without -d then\n");
	}
	
	load_pass_2_result (fs);
	fclose (fp);
	return START_FROM_SEMANTIC;
    case SEMANTIC_DONE:
	if (!fsck_user_confirmed (fs, "Passes 0 and 1 seems finished. Start from "
	    "pass 2?(Yes)", "Yes\n", 1))
	{
	    fsck_exit ("Run without -d then\n");
	}
	
	load_semantic_result (fp, fs);
	fclose (fp);
	return START_FROM_LOST_FOUND;
    case LOST_FOUND_DONE:
	if (!fsck_user_confirmed (fs, "Passes 0 and 1 seems finished. Start from "
	    "pass 2?(Yes)", "Yes\n", 1))
	{
	    fsck_exit ("Run without -d then\n");
	}
	
	load_lost_found_result (fs);
	fclose (fp);
	return START_FROM_PASS_4;
    }
    
    return START_FROM_THE_BEGINNING;
}

static void reiserfs_update_interval_fields(reiserfs_filsys_t *fs)
{
	/* Not supported on v3.5 */
	if (get_sb_version (fs->fs_ondisk_sb) == REISERFS_FORMAT_3_5)
		return;

	set_sb_v2_lastcheck(fs->fs_ondisk_sb, time(NULL));
	set_sb_v2_mnt_count(fs->fs_ondisk_sb, 1);

	if (get_sb_v2_max_mnt_count(fs->fs_ondisk_sb) == 0)
		set_sb_v2_max_mnt_count(fs->fs_ondisk_sb,
					      DEFAULT_MAX_MNT_COUNT);
	if (get_sb_v2_check_interval(fs->fs_ondisk_sb) == 0)
		set_sb_v2_check_interval(fs->fs_ondisk_sb,
					       DEFAULT_CHECK_INTERVAL);
}

static void mark_filesystem_consistent (reiserfs_filsys_t * fs)
{
    if (!is_opened_rw (fs))
        return;

    if (!reiserfs_journal_opened (fs)) {
	/* make sure journal is not standard */
	if (!is_reiserfs_jr_magic_string (fs->fs_ondisk_sb))
	    reiserfs_exit(EXIT_OPER, "Filesystem with default journal "
			  "must be opened.");
	
	fsck_progress ("WARNING: You must use reiserfstune to specify a new "
	    "journal before mounting it.\n");
	
	/* mark filesystem such that it is not mountable until 
	 * new journaldevice is defined */	
	set_jp_journal_magic (sb_jp (fs->fs_ondisk_sb), NEED_TUNE);
    }

    set_sb_umount_state (fs->fs_ondisk_sb, FS_CLEANLY_UMOUNTED);
    set_sb_fs_state (fs->fs_ondisk_sb, FS_CONSISTENT);
    reiserfs_update_interval_fields(fs);
    
    mark_buffer_dirty (fs->fs_super_bh);
}

static void reiserfsck_replay_journal (reiserfs_filsys_t * fs) {
    struct reiserfs_super_block *on_place_sb;
    int sb_size = reiserfs_super_block_size(fs->fs_ondisk_sb);
    
    /* keep the super_block in the separate memory to avoid problems with replaying 
     * broken parameters. */
    on_place_sb = (struct reiserfs_super_block *)fs->fs_super_bh->b_data;
    fs->fs_ondisk_sb = getmem (sb_size);
    memcpy (fs->fs_ondisk_sb, on_place_sb, sb_size);

    replay_journal (fs);

    /* Copy checked reliable sb fields from backed up sb to a new one. */
    set_sb_block_count(on_place_sb, get_sb_block_count(fs->fs_ondisk_sb));
    memcpy(sb_jp(on_place_sb), sb_jp(fs->fs_ondisk_sb), 
	sizeof(struct journal_params));    
    set_sb_block_size(on_place_sb, get_sb_block_size(fs->fs_ondisk_sb));
    set_sb_oid_maxsize(on_place_sb, get_sb_oid_maxsize(fs->fs_ondisk_sb));
    memcpy(on_place_sb->s_v1.s_magic, fs->fs_ondisk_sb->s_v1.s_magic, 10);
    set_sb_hash_code(on_place_sb, get_sb_hash_code(fs->fs_ondisk_sb));
    set_sb_bmap_nr(on_place_sb, get_sb_bmap_nr(fs->fs_ondisk_sb));
    set_sb_version(on_place_sb, get_sb_version(fs->fs_ondisk_sb));
    set_sb_reserved_for_journal(on_place_sb, 
	get_sb_reserved_for_journal(fs->fs_ondisk_sb));
    
    if (sb_size == SB_SIZE) {
	set_sb_v2_flags(on_place_sb, get_sb_v2_flags(fs->fs_ondisk_sb));
	memcpy(on_place_sb->s_uuid, fs->fs_ondisk_sb->s_uuid, 16);
	memcpy(on_place_sb->s_label, fs->fs_ondisk_sb->s_label, 16);
    }

    /* get rid of SB copy */
    freemem (fs->fs_ondisk_sb);
    fs->fs_ondisk_sb = on_place_sb;
}

static int the_end (reiserfs_filsys_t * fs)
{
    struct reiserfs_super_block * sb;
    int ret = EXIT_FIXED;

    sb = fs->fs_ondisk_sb;

    /* put bitmap and objectid map on place */
    reiserfs_delete_bitmap (fs->fs_bitmap2);
    fs->fs_bitmap2 = fsck_new_bitmap (fs);
    if (!fs->fs_bitmap2->bm_dirty)
	die ("Bitmap not dirty");

//    id_map_flush(proper_id_map (fs), fs);
    id_map_flush(semantic_id_map (fs), fs);
    id_map_free(proper_id_map (fs));
    id_map_free(semantic_id_map (fs));

/*    set_sb_free_blocks (sb, reiserfs_bitmap_zeros (fsck_new_bitmap (fs)));*/

    mark_filesystem_consistent (fs);
    clear_buffer_do_not_flush (fs->fs_super_bh);

    if (fsck_data(fs)->mounted == MF_RO) {
	reiserfs_warning(stderr, "\nThe partition is mounted ro. It "
			 "is better to umount and mount it again.\n\n");
	ret = EXIT_REBOOT;
    }

    /* write all dirty blocks */
    fsck_progress ("Syncing..");
    fs->fs_dirt = 1;
    clean_after_dma_check(fs->fs_dev, &dma_info);
    fsck_progress ("finished\n");
    reiserfs_close (fs);

    return ret;
}

/* check umounted or read-only mounted filesystems only */
static void prepare_fs_for_check(reiserfs_filsys_t * fs) {
    /* The method could be called from auto_check already. */
    if (fs->fs_flags == O_RDWR) 
	return;

    reiserfs_reopen (fs, O_RDWR);
    
    fsck_data(fs)->mounted = misc_device_mounted(fs->fs_file_name);
    
    if (fsck_data(fs)->mounted > 0) {
	if (fsck_data(fs)->mounted == MF_RW) {
	    fsck_progress ("Partition %s is mounted with write permissions, "
		"cannot check it\n", fs->fs_file_name);
	    reiserfs_close(fs);
	    exit(EXIT_USER);
	}
	
	/* If not CHECK mode, lock the process in the memory. */
	if (fsck_mode (fs) != FSCK_CHECK) {
	    if (mlockall(MCL_CURRENT)) {
		    reiserfs_exit(EXIT_OPER, "Failed to lock the process to "
				  "fsck the mounted ro partition. %s.\n", 
				  strerror(errno));
	    }
	}
	
	if (fsck_skip_journal (fs)) {
		reiserfs_exit(EXIT_USER, "Jounrnal of the mounted "
			      "filesystem must be specified.\n");
	}
	
	if (!reiserfs_journal_opened (fs)) {
	    /* just to make sure */
	    reiserfs_panic ("Journal is not opened");
	} else if (reiserfs_journal_params_check(fs)) {
	    reiserfs_close (fs);
	    exit(EXIT_FATAL);
	}
	
	fsck_progress ("Filesystem seems mounted read-only. Skipping journal "
	    "replay.\n");
    } else if (!fsck_skip_journal (fs)) {
	if (reiserfs_journal_params_check(fs)) {
	    reiserfs_close (fs);
	    exit(EXIT_FATAL);
	}
	
	/* filesystem is not mounted, replay journal before checking */
        reiserfsck_replay_journal (fs);
    }
}

static void rebuild_tree (reiserfs_filsys_t * fs) {
    time_t t;
    int ret;

    init_rollback_file (state_rollback_file(fs), &fs->fs_blocksize, 
	fsck_data(fs)->log);
    
    prepare_fs_for_check(fs);
    
    ret = reiserfs_open_ondisk_bitmap (fs);
    if (ret < 0) {
        fsck_progress ("reiserfsck: Could not open bitmap\n");
	reiserfs_close (fs);
	exit(EXIT_OPER);
    } else if (ret > 0) {
	fsck_log("Zero bit found in on-disk bitmap after the last valid bit. "
	    "Fixed.\n");
    }
 
    time (&t);
    fsck_progress ("###########\n"
		   "reiserfsck --rebuild-tree started at %s"
		   "###########\n", ctime (&t));

    switch (where_to_start_from (fs)) {
    case START_FROM_THE_BEGINNING:
	reset_super_block (fs);
	pass_0 (fs);

    case START_FROM_PASS_1:
	reset_super_block (fs);
	pass_1 (fs);
	
    case START_FROM_PASS_2:
	pass_2 (fs);

    case START_FROM_SEMANTIC:
	pass_3_semantic (fs);

	/* if --lost+found is set - link unaccessed directories to lost+found
	   directory */
    case START_FROM_LOST_FOUND:	
	pass_3a_look_for_lost (fs);
	
    case START_FROM_PASS_4:
	/* 4. look for unaccessed items in the leaves */
	pass_4_check_unaccessed_items ();
	
	ret = the_end (fs);
    }

    close_rollback_file ();
       
    time (&t);
    fsck_progress ("###########\n"
		   "reiserfsck finished at %s"
		   "###########\n", ctime (&t));
    exit (ret);
}

static void clean_attributes (reiserfs_filsys_t * fs) {
    time_t t;

    time (&t);

    if (get_sb_umount_state (fs->fs_ondisk_sb) != FS_CLEANLY_UMOUNTED) {
        fsck_progress ("Filesystem is not clean\n"
	    "Check consistency of the partition first.\n");
        exit(EXIT_USER);
    }
    if (get_sb_fs_state (fs->fs_ondisk_sb) != FS_CONSISTENT) {
        fsck_progress ("Filesystem seems to be in unconsistent state.\n"
	    "Check consistency of the partition first.\n");
        exit(EXIT_USER);
    }

    if (get_reiserfs_format (fs->fs_ondisk_sb) != REISERFS_FORMAT_3_6) {
        fsck_progress ("Filesystems of 3_5 format do not support extended "
	    "attributes.\n");
	
        exit(EXIT_USER);
    }
    fsck_progress ("###########\n"
	           "reiserfsck --clean-attributes started at %s"
                   "###########\n", ctime (&t));

    init_rollback_file (state_rollback_file(fs), &fs->fs_blocksize, 
	fsck_data(fs)->log);

    prepare_fs_for_check(fs);

    do_clean_attributes (fs);

    clean_after_dma_check(fs->fs_dev, &dma_info);

    fsck_progress ("###########\n"
		   "reiserfsck finished at %s"
		   "###########\n", ctime (&t));
    reiserfs_close (fs);
    close_rollback_file ();

    exit (EXIT_FIXED);

}

static int reiserfs_check_auto_state(reiserfs_filsys_t *fs)
{
	time_t now = time(NULL);
	time_t lastcheck = get_sb_v2_lastcheck(fs->fs_ondisk_sb);
	unsigned int mnt_count = get_sb_v2_mnt_count(fs->fs_ondisk_sb);
	unsigned int max_mnt_count = get_sb_v2_max_mnt_count(fs->fs_ondisk_sb);
	unsigned int interval =  get_sb_v2_check_interval(fs->fs_ondisk_sb);

	/* v3.5 file systems don't have the superblock fields for this */
	if (get_sb_version (fs->fs_ondisk_sb) == REISERFS_FORMAT_3_5)
		return 1;

	if (lastcheck == 0 || mnt_count == 0 || max_mnt_count == 0 ||
	    interval == 0) {
		fprintf(stderr, "File system hasn't been enabled for faster "
		        "boot-time checking. It will be enabled after a "
			"successful run.\n");
		return 1;
	}

	if (interval != UINT_MAX && now > lastcheck + interval) {
		fprintf(stderr, "File system hasn't been checked in %u days. "
		        "Checking now.\n", (now - lastcheck) / (60*60*24));
		return 1;
	}

	if (interval != UINT_MAX && lastcheck > now) {
		fprintf(stderr, "File system check timestamp is in the future. "
			"Checking now.\n");
		return 1;
	}

	if (mnt_count > max_mnt_count && max_mnt_count != USHRT_MAX) {
		fprintf(stderr, "File system has been mounted %u times "
		        "without being checked. Checking now.\n", mnt_count);
		return 1;
	}

	return 0;
}

static int auto_check (reiserfs_filsys_t *fs) {
    __u16 state;
    int retval = 0;
    int force = fsck_data(fs)->options & OPT_FORCE;
    
    print_super_block (stdout, fs, fs->fs_file_name, fs->fs_super_bh, 1);
    
    state = get_sb_fs_state (fs->fs_ondisk_sb);
    if ((state & FS_FATAL) == FS_FATAL) {
	fprintf(stderr, "Filesystem seems to have fatal corruptions. Running "
	    "with --rebuild-tree is required.\n");
	goto error;
    }

    if ((state & FS_ERROR) == FS_ERROR) {
	fprintf(stderr, "Some corruptions on the filesystem were detected. Switching to "
	    "the --fix-fixable mode.\n");
	/* run fixable pass. */
	return 0;
    }
    
    if (state != FS_CONSISTENT)
	fprintf(stderr, "Some strange state was specified in the super block. "
	    "Do usual check.\n");

    if (get_sb_umount_state (fs->fs_ondisk_sb) == FS_CLEANLY_UMOUNTED &&
	!force && !reiserfs_check_auto_state(fs))
	    exit(EXIT_OK);
    prepare_fs_for_check(fs);
    if (!force && !reiserfs_check_auto_state(fs))
	    exit(EXIT_OK);

    /* Check bitmaps. */
    retval = reiserfs_open_ondisk_bitmap (fs);
    
    if (retval > 0) {
	fsck_log("Zero bit found in on-disk bitmap after the last valid bit. "
	    "Switching to --fix-fixable mode.\n");
	/* run fixable pass. */
	return 0;
    } else if (retval < 0) {
        fsck_progress ("reiserfsck: Could not open bitmap\n");
	goto error;
    }
    
    if (get_sb_block_count (fs->fs_ondisk_sb) - 
	get_sb_free_blocks(fs->fs_ondisk_sb) != 
	fs->fs_bitmap2->bm_set_bits)
    {
	fsck_log("Wrong amount of used blocks. Switching to the --fix-fixable mode.\n");
	/* run fixable pass. */
	return 0;
    }

    check_fs_tree (fs);
    
    if (fsck_data (fs)->check.fatal_corruptions) {		
	fprintf(stderr, "%lu fatal corruption(s) found in the root block. Running "
	    "with the --rebuild-tree is required.\n", 
	    fsck_data (fs)->check.fatal_corruptions);
	goto fatal_error;
    } else if (fsck_data (fs)->check.fixable_corruptions) {
        /* seems that this cannot happen. */
	fprintf(stderr, "%lu fixable corruption(s) found. Switching to "
	    "the --fix-fixable mode.\n", fsck_data (fs)->check.fixable_corruptions);
	fsck_data (fs)->check.fixable_corruptions = 0;
	/* run fixable pass. */
	return 0;
    }

    reiserfs_update_interval_fields(fs);
    mark_buffer_dirty(fs->fs_super_bh);
    bwrite(fs->fs_super_bh);
    fs->fs_dirt = 1;

    clean_after_dma_check(fs->fs_dev, &dma_info);
    
    reiserfs_close (fs);
    /* do not do anything else. */    
    exit (EXIT_OK);

fatal_error:
    set_sb_fs_state(fs->fs_ondisk_sb, FS_FATAL);
    mark_buffer_dirty (fs->fs_super_bh);
    bwrite(fs->fs_super_bh);	
error:
    clean_after_dma_check(fs->fs_dev, &dma_info);
    reiserfs_close(fs);
    exit(EXIT_FATAL);
}

/* check umounted or read-only mounted filesystems only */
static void check_fs (reiserfs_filsys_t * fs)
{
    int retval = EXIT_OK;
    time_t t;

    time (&t);

    if (fsck_mode (fs) != FSCK_FIX_FIXABLE) {
        fsck_progress ("###########\n"
	               "reiserfsck --check started at %s"
                       "###########\n", ctime (&t));
    } else {
        fsck_progress ("###########\n"
	               "reiserfsck --fix-fixable started at %s"
                       "###########\n", ctime (&t));
    }

    init_rollback_file (state_rollback_file(fs), &fs->fs_blocksize, 
	fsck_data(fs)->log);
    
    prepare_fs_for_check (fs);

    if (!fs->fs_bitmap2)
	/* It could be done on auto_check already. */
	retval = reiserfs_open_ondisk_bitmap (fs);

    if (retval > 0) {
	if (fsck_mode(fs) != FSCK_FIX_FIXABLE) {
	    fsck_log("Zero bit found in on-disk bitmap after the last valid "
		"bit.\n");
	    
	    one_more_corruption(fs, FIXABLE);
	} else {
	    fsck_log("Zero bit found in on-disk bitmap after the last valid "
		"bit. Fixed.\n");
	}
    } else if (retval < 0) {
        fsck_progress ("reiserfsck: Could not open bitmap\n");
	reiserfs_close (fs);
	exit(EXIT_OPER);
    }

    check_fs_tree (fs);

    semantic_check ();
    
    if (fsck_data (fs)->check.fatal_corruptions) {
	fsck_progress ("%lu found corruptions can be fixed only when running with "
	    "--rebuild-tree\n", fsck_data (fs)->check.fatal_corruptions);
	
        set_sb_fs_state (fs->fs_ondisk_sb, FS_FATAL);
        mark_buffer_dirty (fs->fs_super_bh);
	retval = EXIT_FATAL;
    } else if (fsck_data (fs)->check.fixable_corruptions) {
        /* fixable corruptions found */
	if (fsck_mode (fs) == FSCK_FIX_FIXABLE) {
            /* fixable corruptions found and fix-fixable has not fixed them, 
	     * do rebuild-tree */
	    
            fsck_progress ("Fatal error: %lu fixable corruptions found after "
		"--fix-fixable.\n", fsck_data (fs)->check.fixable_corruptions);
	    retval = EXIT_OPER;
        } else {
	    fsck_progress ("%lu found corruptions can be fixed when running with "
		"--fix-fixable\n", fsck_data (fs)->check.fixable_corruptions);
	    
	    retval = EXIT_FIXABLE;
        }
        set_sb_fs_state (fs->fs_ondisk_sb, FS_ERROR);
        mark_buffer_dirty (fs->fs_super_bh);
    } else {
	fsck_progress ("No corruptions found\n");
	stage_report (5, fs);

	if (fsck_mode(fs) != FSCK_CHECK) {
		if (misc_device_mounted(fs->fs_file_name) == MF_RO) {
			reiserfs_warning(stderr, "\nThe partition is mounted ro. It is better "
					 "to umount and mount it again.\n\n");
			retval = EXIT_REBOOT;
		} else 
			retval = EXIT_FIXED;
	} else
		retval = EXIT_OK;

	mark_filesystem_consistent (fs);
    }
   
    if (fsck_mode (fs) == FSCK_FIX_FIXABLE && !fsck_data (fs)->check.fatal_corruptions)
        id_map_flush(proper_id_map (fs), fs);
        
    id_map_free(proper_id_map (fs));
    clean_after_dma_check(fs->fs_dev, &dma_info);
    
    //clear_relocated_list();    
    
    time (&t);
    fsck_progress ("###########\n"
		   "reiserfsck finished at %s"
		   "###########\n", ctime (&t));

    reiserfs_close (fs);
    close_rollback_file ();
    exit(retval);
}

static int open_devices_for_rollback (char * file_name, 
    struct fsck_data * data) 
{
    int fd;

    fd = open (file_name, O_RDWR | O_LARGEFILE);

    if (fd == -1) {
        reiserfs_warning (stderr, "reiserfsck: Cannot not open the fs "
	    "partition %s\n", file_name);

        return -1;
    }

    fs = getmem (sizeof (*fs));
    fs->fs_dev = fd;
    fs->fs_vp = data;
    asprintf (&fs->fs_file_name, "%s", file_name);

    if (data->journal_dev_name && 
	strcmp (data->journal_dev_name, file_name)) 
    {
	fs->fs_journal_dev = open (data->journal_dev_name, 
	    O_RDWR | O_LARGEFILE);
	if (fs->fs_journal_dev == -1) {
	    reiserfs_warning (stderr, "Cannot open journal partition\n");
     	    return -1;
	}
    }

    if (open_rollback_file (state_rollback_file(fs), fsck_data(fs)->log))
        return -1;

    return 0;    	
}

static void fsck_rollback (reiserfs_filsys_t * fs) {
    time_t t;

    time (&t);
    fsck_progress ("###########\n"
	           "reiserfsck --rollback-fsck-changes started at %s"
                   "###########\n", ctime (&t));

    do_fsck_rollback (fs->fs_dev, fs->fs_journal_dev, fsck_progress_file (fs));
    close_rollback_file ();

    close (fs->fs_journal_dev);
    free (fs->fs_file_name);
    fs->fs_file_name = 0;
    close (fs->fs_dev);
    freemem (fs);

    time (&t);
    fsck_progress ("###########\n"
		   "reiserfsck finished at %s"
		   "###########\n", ctime (&t));

    exit(EXIT_FIXED);
}

int main (int argc, char * argv [])
{
    char * file_name;
    struct fsck_data * data;
    struct rlimit rlim = {RLIM_INFINITY, RLIM_INFINITY};
    char *width;
    int retval;
    int error;
    
    width = getenv("COLUMNS");
    if ( width )
	screen_width = atoi(width);
    
    if (screen_width == 0)
	screen_width = 80; // We default to 80 characters wide screen
    screen_width--;
    screen_savebuffer_len=screen_width;
    screen_savebuffer=getmem(screen_width+1);
    memset(screen_savebuffer,0,screen_savebuffer_len+1);
    
    lost_found_dir_key.k2_dir_id = cpu_to_le32(lost_found_dir_key.k2_dir_id);
    lost_found_dir_key.k2_objectid = cpu_to_le32(lost_found_dir_key.k2_objectid);
    /* this is only needed (and works) when running under 2.4 on regular files */
    if (setrlimit (RLIMIT_FSIZE, &rlim) == -1) {
	reiserfs_warning (stderr, "Cannot change the system limit for file size "
	    "with setrlimit: %s\n", strerror(errno));
    }

    data = getmem (sizeof (struct fsck_data));

    file_name = parse_options (data, argc, argv);

    if (data->mode != FSCK_AUTO)
	print_banner ("reiserfsck");

    if (data->mode == DO_NOTHING) {
	freemem (data);
	exit(EXIT_OK);
    }

    if (data->options & OPT_BACKGROUND) {
	/* running in background reiserfsck appends progress information into
           'fsck.run'. Logs get there if log file was not specified*/
	data->options |= OPT_QUIET;
	data->progress = fopen ("fsck.run", "a+");
	if (!data->progress) {
	    reiserfs_exit(EXIT_OPER, "reiserfsck: Cannot not open \"fsck.run\"");
	}

	if (data->log == stdout)
	    /* no log file specifed - redirect log into 'fsck.run' */
	    data->log = data->progress;

	retval = fork ();
	if (retval == -1) {
	    reiserfs_panic ("reiserfsck: Fork failed: %s", strerror(errno));
	} else if (retval != 0) {
	    exit(EXIT_OPER);
	}
	reiserfs_warning (stderr, "\nReiserfsck is running in background as "
	    "[%d],\nmake sure that it gets all the confirmations from stdin "
	    "that it requests.\n\n", getpid ());
    }

    /* This asks for confirmation also. */
    if (data->mode != FSCK_AUTO)
	warn_what_will_be_done(file_name, data);

    if (data->mode == FSCK_ROLLBACK_CHANGES) {
    	if (open_devices_for_rollback (file_name, data) == -1)
    	    exit(EXIT_OPER);
    } else {
	fs = reiserfs_open (file_name, O_RDONLY, &error, data, 
	    data->mode != FSCK_SB);
		
	if (error) {
		reiserfs_exit(EXIT_OPER, "Failed to open the device "
			      "'%s': %s\n\n", file_name, strerror(error));
	} 
	
	if (data->mode != FSCK_SB) {
	    if (no_reiserfs_found (fs)) {
		reiserfs_exit(EXIT_OPER, "Failed to open the filesystem.\n\n"
			      "If the partition table has not been changed, "
			      "and the partition is\nvalid  and  it really  "
			      "contains  a reiserfs  partition,  then the\n"
			      "superblock  is corrupted and you need to run "
			      "this utility with\n--rebuild-sb.\n");
	    }
	    if (fsck_skip_journal (fs) && 
		!is_reiserfs_jr_magic_string (fs->fs_ondisk_sb)) 
	    {
		reiserfs_warning (stderr, "Filesystem with default journal found, "
			"--no-journal-available is ignored\n");
		fsck_data(fs)->options &= ~OPT_SKIP_JOURNAL;
	    }
	
	    if (!fsck_skip_journal (fs)) {
		retval = reiserfs_open_journal(fs, data->journal_dev_name, O_RDONLY);
		
		if (retval) {
	            fsck_progress ("Failed to open the journal device (%s).\n", 
			data->journal_dev_name);
		    
		    if (retval == 1) {
			    fsck_progress ("Run --rebuild-sb to rebuild journal parameters.\n");
		    }
		    
		    reiserfs_close (fs);
		    exit(EXIT_OPER);
	        }
	    }
	
	    if (data->options & BADBLOCKS_FILE) {
		if (create_badblock_bitmap (fs, badblocks_file) != 0) 
		    exit(EXIT_OPER);
	    }
	    
	    register_timer();
    	}
    }

    switch (data->mode) {
    case FSCK_SB:
	rebuild_sb (fs, file_name, data);
	break;
    
    case FSCK_AUTO:
	/* perform some light-weight checks. If error, do fixable job. */
	if (auto_check (fs))
	    break;
	data->mode = FSCK_FIX_FIXABLE;
    case FSCK_CHECK:
    case FSCK_FIX_FIXABLE:
	check_fs (fs);
	break;

    case FSCK_REBUILD:
    case DO_TEST:
	rebuild_tree (fs);
	break;

    case FSCK_ROLLBACK_CHANGES:
	fsck_rollback (fs);
 	break;
    case FSCK_CLEAN_ATTRIBUTES:
	clean_attributes (fs);
    }
    
    exit(EXIT_OPER);
}

