blob: ab3342aed6332ddf06846852ebc0f3feae0e0bd6 [file] [log] [blame]
/*
* 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>
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" \
" -V\t\t\t\tprints version and exits\n" \
" -a and -p\t\t\tsome light-weight auto checks for bootup\n" \
" -f and -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'},
{"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':
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 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);
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);
reiserfs_close (fs);
fsck_progress ("finished\n");
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);
reiserfs_close (fs);
close_rollback_file ();
fsck_progress ("###########\n"
"reiserfsck finished at %s"
"###########\n", ctime (&t));
exit (EXIT_FIXED);
}
/* Do not allow buffers to be flushed after finishing to avoid another bitmap
* reading on mounting. */
static void fsck_sleep() {
int res;
res = fork();
if (res == -1) {
reiserfs_panic ("reiserfsck: Fork failed: %s", strerror(errno));
} else if (res == 0) {
/* Make the child process to sleep for 5 secs. */
sleep(5);
}
}
static int auto_check (reiserfs_filsys_t *fs) {
__u16 state;
int retval = 0;
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");
prepare_fs_for_check(fs);
/* 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;
}
clean_after_dma_check(fs->fs_dev, &dma_info);
fsck_sleep();
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);
reiserfs_close (fs);
close_rollback_file ();
//clear_relocated_list();
time (&t);
fsck_progress ("###########\n"
"reiserfsck finished at %s"
"###########\n", ctime (&t));
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);
}