1503 lines
40 KiB
C
1503 lines
40 KiB
C
/*
|
|
* Copyright 1996-2001 Hans Reiser
|
|
*/
|
|
|
|
#include "fsck.h"
|
|
|
|
|
|
|
|
static unsigned long tmp_zeroed;
|
|
|
|
/* pass 0 scans the partition (used part). It creates two maps which will be
|
|
used on the pass 1. These are a map of nodes looking like leaves and a map
|
|
of "bad" unformatted nodes. */
|
|
|
|
|
|
/* leaves */
|
|
reiserfs_bitmap_t leaves_bitmap;
|
|
#define pass0_is_leaf(block) __is_marked (leaves, block)
|
|
#define pass0_mark_leaf(block) __mark (leaves, block)
|
|
|
|
/* nodes which are referred to from only one indirect item */
|
|
reiserfs_bitmap_t good_unfm_bitmap;
|
|
#define pass0_is_good_unfm(block) __is_marked (good_unfm, block)
|
|
#define pass0_mark_good_unfm(block) __mark (good_unfm, block)
|
|
#define pass0_unmark_good_unfm(block) __unmark (good_unfm, block)
|
|
|
|
/* nodes which are referred to from more than one indirect item */
|
|
reiserfs_bitmap_t bad_unfm_bitmap;
|
|
#define pass0_is_bad_unfm(block) __is_marked (bad_unfm, block)
|
|
#define pass0_mark_bad_unfm(block) __mark (bad_unfm, block)
|
|
#define pass0_unmark_bad_unfm(block) __unmark (bad_unfm, block)
|
|
|
|
|
|
|
|
/* there are three way to say of which blocks the tree should be built off:
|
|
default - */
|
|
static void make_aux_bitmaps (reiserfs_filsys_t fs)
|
|
{
|
|
|
|
/* bitmap of leaves found on the device. It will be saved if -c specified */
|
|
leaves_bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs));
|
|
|
|
good_unfm_bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs));
|
|
|
|
bad_unfm_bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs));
|
|
|
|
}
|
|
|
|
|
|
/* register block some indirect item points to */
|
|
static void register_unfm (unsigned long block)
|
|
{
|
|
if (!pass0_is_good_unfm (block) && !pass0_is_bad_unfm (block)) {
|
|
/* this block was not pointed by other indirect items yet */
|
|
pass0_mark_good_unfm (block);
|
|
return;
|
|
}
|
|
|
|
if (pass0_is_good_unfm (block)) {
|
|
/* block was pointed once already, unmark it in bitmap of good
|
|
unformatted nodes and mark in bitmap of bad pointers */
|
|
pass0_unmark_good_unfm (block);
|
|
pass0_mark_bad_unfm (block);
|
|
return;
|
|
}
|
|
|
|
assert (pass0_is_bad_unfm (block));
|
|
}
|
|
|
|
|
|
/* 'upper' item is correct if 'upper + 2' exists and its key is greater than
|
|
key of 'upper' */
|
|
static int upper_correct (struct buffer_head * bh, struct item_head * upper,
|
|
int upper_item_num)
|
|
{
|
|
if (upper_item_num + 2 < B_NR_ITEMS (bh)) {
|
|
if (comp_keys (&upper->ih_key, &(upper + 2)->ih_key) != -1)
|
|
/* item-num's item is out of order of order */
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* there is no item above the "bad pair" */
|
|
return 2;
|
|
}
|
|
|
|
|
|
/* 'lower' item is correct if 'lower - 2' exists and its key is smaller than
|
|
key of 'lower' */
|
|
static int lower_correct (struct buffer_head * bh, struct item_head * lower,
|
|
int lower_item_num)
|
|
{
|
|
if (lower_item_num - 2 >= 0) {
|
|
if (comp_keys (&(lower - 2)->ih_key, &lower->ih_key) != -1)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
|
|
/* return 1 if something was changed */
|
|
static int correct_key_format (struct item_head * ih)
|
|
{
|
|
int dirty = 0;
|
|
|
|
if (is_stat_data_ih (ih)) {
|
|
/* for stat data we have no way to check whether key format in item
|
|
head matches to the key format found from the key directly */
|
|
if (ih_item_len (ih) == SD_V1_SIZE) {
|
|
if (ih_key_format (ih) != KEY_FORMAT_1) {
|
|
fsck_log ("correct_key_format: ih_key_format of (%H) is set to format 1\n",
|
|
ih);
|
|
set_key_format (ih, KEY_FORMAT_1);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
if (ih_item_len (ih) == SD_SIZE) {
|
|
if (ih_key_format (ih) != KEY_FORMAT_2) {
|
|
fsck_log ("correct_key_format: ih_key_format of (%H) is set to format 2\n",
|
|
ih);
|
|
set_key_format (ih, KEY_FORMAT_2);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
die ("stat data of wrong length");
|
|
}
|
|
|
|
if (key_format (&ih->ih_key) != ih_key_format (ih)) {
|
|
fsck_log ("correct_key_format: ih_key_format of (%H) is set to format found in the key\n",
|
|
ih);
|
|
set_key_format (ih, key_format (&ih->ih_key));
|
|
dirty = 1;
|
|
}
|
|
|
|
if (type_unknown (&ih->ih_key)) {
|
|
/* FIXME: */
|
|
set_type (key_format (&ih->ih_key), &ih->ih_key, TYPE_DIRECT);
|
|
dirty = 1;
|
|
}
|
|
|
|
return dirty;
|
|
}
|
|
|
|
#if 0
|
|
/* fixme: we might try all available hashes */
|
|
static int prob_name (reiserfs_filsys_t fs,
|
|
char ** name, int max_len, __u32 deh_offset)
|
|
{
|
|
int start; /* */
|
|
int len;
|
|
|
|
for (start = 0; start < max_len; start ++) {
|
|
for (len = 0; len < max_len - start; len ++) {
|
|
if (is_properly_hashed (fs, *name + start, len + 1, deh_offset)) {
|
|
*name = *name + start;
|
|
return len + 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
static void hash_hits_init (reiserfs_filsys_t fs)
|
|
{
|
|
stats (fs)->hash_amount = known_hashes ();
|
|
stats (fs)->hash_hits = getmem (sizeof (unsigned long) * stats (fs)->hash_amount);
|
|
return;
|
|
}
|
|
|
|
|
|
static void add_hash_hit (reiserfs_filsys_t fs, int hash_code)
|
|
{
|
|
stats (fs)->hash_hits [hash_code] ++;
|
|
}
|
|
|
|
|
|
/* deh_location look reasonable, try to find name length. return 0 if
|
|
we failed */
|
|
static int try_to_get_name_length (struct item_head * ih, struct reiserfs_de_head * deh,
|
|
int i)
|
|
{
|
|
int len;
|
|
|
|
len = name_length (ih, deh, i);
|
|
if (i == 0 || !de_bad_location (deh - 1))
|
|
return (len > 0) ? len : 0;
|
|
|
|
/* previous entry had bad location so we had no way to find
|
|
name length */
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* define this if you are using -t to debug recovering of corrupted directory
|
|
item */
|
|
#define DEBUG_VERIFY_DENTRY
|
|
#undef DEBUG_VERIFY_DENTRY
|
|
|
|
|
|
/* check directory item and try to recover something */
|
|
static int verify_directory_item (reiserfs_filsys_t fs, struct buffer_head * bh,
|
|
int item_num)
|
|
{
|
|
struct item_head * ih;
|
|
struct item_head tmp;
|
|
char * item;
|
|
struct reiserfs_de_head * deh;
|
|
char * name;
|
|
int name_len;
|
|
int bad;
|
|
int i, j;
|
|
#if 0
|
|
int bad_entries; /* how many bad neighboring entries */
|
|
int total_entry_len;
|
|
char * entries, * end;
|
|
#endif
|
|
int dirty;
|
|
int entry_count;
|
|
int hash_code;
|
|
int bad_locations;
|
|
|
|
#ifdef DEBUG_VERIFY_DENTRY
|
|
char * direntries;
|
|
#endif
|
|
|
|
|
|
ih = B_N_PITEM_HEAD (bh, item_num);
|
|
item = B_I_PITEM (bh,ih);
|
|
deh = (struct reiserfs_de_head *)item;
|
|
|
|
dirty = 0;
|
|
bad_locations = 0;
|
|
entry_count = ih_entry_count (ih);
|
|
|
|
|
|
/* check deh_location */
|
|
for (i = 0; i < ih_entry_count (ih); i ++) {
|
|
/* silently fix deh_state */
|
|
if (deh [i].deh_state != (1 << DEH_Visible)) {
|
|
deh [i].deh_state = cpu_to_le16 (1 << DEH_Visible);
|
|
mark_buffer_dirty (bh);
|
|
}
|
|
if (dir_entry_bad_location (deh + i, ih, !i))
|
|
mark_de_bad_location (deh + i);
|
|
}
|
|
|
|
#ifdef DEBUG_VERIFY_DENTRY
|
|
direntries = getmem (ih_entry_count (ih) * sizeof (int));
|
|
|
|
printf ("entries with bad locations: ");
|
|
for (i = 0; i < ih_entry_count (ih); i ++) {
|
|
if (de_bad_location (deh + i))
|
|
printf ("%d ", i);
|
|
}
|
|
printf ("\n");
|
|
#endif /* DEBUG_VERIFY_DENTRY */
|
|
|
|
|
|
/* find entries names in which have mismatching deh_offset */
|
|
for (i = ih_entry_count (ih) - 1; i >= 0; i --) {
|
|
if (de_bad (deh + i))
|
|
/* bad location */
|
|
continue;
|
|
|
|
if (i) {
|
|
if (deh_location (deh + i - 1) < deh_location (deh + i))
|
|
mark_de_bad_location (deh + i - 1);
|
|
}
|
|
|
|
name = name_in_entry (deh + i, i);
|
|
/* we found a name, but we not always we can get its length as
|
|
it depends on deh_location of previous entry */
|
|
name_len = try_to_get_name_length (ih, deh + i, i);
|
|
|
|
#ifdef DEBUG_VERIFY_DENTRY
|
|
if (name_len == 0)
|
|
printf ("trying to find name length for %d-th entry\n", i);
|
|
#endif /* DEBUG_VERIFY_DENTRY */
|
|
if (is_dot (name, name_len)) {
|
|
if (i != 0)
|
|
fsck_log ("block %lu: item %d: \".\" is %d-th entry\n",
|
|
bh->b_blocknr, item_num, i);
|
|
/* check and fix "." */
|
|
|
|
if (deh_offset (deh + i) != DOT_OFFSET) {
|
|
deh[i].deh_offset = cpu_to_le32 (DOT_OFFSET);
|
|
mark_buffer_dirty (bh);
|
|
}
|
|
/* "." must point to the directory it is in */
|
|
if (not_of_one_file (&(deh[i].deh_dir_id), &(ih->ih_key))) {
|
|
fsck_log ("verify_direntry: block %lu, item %H has entry \".\" "
|
|
"pointing to (%K) instead of (%K)\n",
|
|
bh->b_blocknr, ih,
|
|
&(deh[i].deh_dir_id), &(ih->ih_key));
|
|
deh[i].deh_dir_id = key_dir_id (&ih->ih_key);
|
|
deh[i].deh_objectid = key_objectid (&ih->ih_key);
|
|
mark_buffer_dirty (bh);
|
|
}
|
|
} else if (is_dot_dot (name, name_len)) {
|
|
if (i != 1)
|
|
fsck_log ("block %lu: item %d: \"..\" is %d-th entry\n",
|
|
bh->b_blocknr, item_num, i);
|
|
|
|
/* check and fix ".." */
|
|
if (deh_offset (deh + i) != DOT_DOT_OFFSET)
|
|
deh[i].deh_offset = cpu_to_le32 (DOT_DOT_OFFSET);
|
|
} else {
|
|
int min_length, max_length;
|
|
|
|
/* check other name */
|
|
|
|
if (name_len == 0) {
|
|
/* we do not know the length of name - we will try to find it */
|
|
min_length = 1;
|
|
max_length = item + ih_item_len (ih) - name;
|
|
} else
|
|
/* we kow name length, so we will try only one name length */
|
|
min_length = max_length = name_len;
|
|
|
|
for (j = min_length; j <= max_length; j ++) {
|
|
hash_code = find_hash_in_use (name, j,
|
|
GET_HASH_VALUE (deh_offset (deh + i)),
|
|
rs_hash (fs->s_rs));
|
|
add_hash_hit (fs, hash_code);
|
|
if (code2func (hash_code) != 0) {
|
|
/* deh_offset matches to some hash of the name */
|
|
if (!name_len) {
|
|
fsck_log ("pass0: block %lu, item %H: entry %d. found a name \"%.*s\" "
|
|
"matching to deh_offset %u. FIXME: should set deh_location "
|
|
"of previous entry (not ready)\n",
|
|
bh->b_blocknr, ih, i, j, name, deh_offset (deh + i));
|
|
/* FIXME: if next byte is 0 we think that the name is aligned to 8 byte boundary */
|
|
if (i) {
|
|
deh[i - 1].deh_location = cpu_to_le16 (deh_location (deh + i) +
|
|
((name[j] || SB_VERSION (fs) == REISERFS_VERSION_1) ? j : ROUND_UP (j)));
|
|
mark_de_good_location (deh + i - 1);
|
|
mark_buffer_dirty (bh);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (j == max_length + 1) {
|
|
/* deh_offset does not match to anything. it will be
|
|
deleted for now, but maybe we could just fix a
|
|
deh_offset if it is in ordeer */
|
|
mark_de_bad_offset (deh + i);
|
|
}
|
|
}
|
|
} /* for */
|
|
|
|
|
|
|
|
#if 0
|
|
/* find entries names in which have mismatching deh_offset */
|
|
for (i = 0; i < ih_entry_count (ih); i ++) {
|
|
if (de_bad (deh + i))
|
|
/* bad location */
|
|
continue;
|
|
|
|
name = name_in_entry (deh + i, i);
|
|
/* we found a name, but we not always we can get its length as
|
|
it depends on deh_location of previous entry */
|
|
name_len = try_to_get_name_length (ih, deh + i, i);
|
|
|
|
if (i == 0 && is_dot (name, name_len)) {
|
|
|
|
/* check and fix "." */
|
|
|
|
if (deh_offset (deh + i) != DOT_OFFSET) {
|
|
deh[i].deh_offset = cpu_to_le32 (DOT_OFFSET);
|
|
mark_buffer_dirty (bh);
|
|
}
|
|
/* "." must point to the directory it is in */
|
|
if (not_of_one_file (&(deh[i].deh_dir_id), &(ih->ih_key))) {
|
|
fsck_log ("verify_direntry: block %lu, item %H has entry \".\" "
|
|
"pointing to (%K) instead of (%K)\n",
|
|
bh->b_blocknr, ih,
|
|
&(deh[i].deh_dir_id), &(ih->ih_key));
|
|
deh[i].deh_dir_id = key_dir_id (&ih->ih_key);
|
|
deh[i].deh_objectid = key_objectid (&ih->ih_key);
|
|
mark_buffer_dirty (bh);
|
|
}
|
|
} else if (i == 1 && is_dot_dot (name, name_len)) {
|
|
|
|
/* check and fix ".." */
|
|
|
|
if (deh_offset (deh + i) != DOT_DOT_OFFSET)
|
|
deh[i].deh_offset = cpu_to_le32 (DOT_DOT_OFFSET);
|
|
} else {
|
|
int min_length, max_length;
|
|
|
|
/* check other name */
|
|
|
|
if (name_len == 0) {
|
|
/* we do not know the length of name - we will try to find it */
|
|
min_length = 1;
|
|
max_length = item + ih_item_len (ih) - name;
|
|
} else
|
|
/* we kow name length, so we will try only one name length */
|
|
min_length = max_length = name_len;
|
|
|
|
for (j = min_length; j <= max_length; j ++) {
|
|
hash_code = find_hash_in_use (name, j,
|
|
GET_HASH_VALUE (deh_offset (deh + i)),
|
|
rs_hash (fs->s_rs));
|
|
add_hash_hit (fs, hash_code);
|
|
if (code2func (hash_code) != 0) {
|
|
/* deh_offset matches to some hash of the name */
|
|
if (!name_len) {
|
|
fsck_log ("pass0: block %lu, item %H: entry %d. found a name \"%.*s\" "
|
|
"matching to deh_offset %u. FIXME: should set deh_location "
|
|
"of previous entry (not ready)\n",
|
|
bh->b_blocknr, ih, i, j, name, deh_offset (deh + i));
|
|
/* FIXME: if next byte is 0 we think that the name is aligned to 8 byte boundary */
|
|
deh[i - 1].deh_location = cpu_to_le16 (deh_location (deh + i) +
|
|
((name[j] || SB_VERSION (fs) == REISERFS_VERSION_1) ? j : ROUND_UP (j)));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (j == max_length + 1) {
|
|
/* deh_offset does not match to anything. it will be
|
|
deleted for now, but maybe we could just fix a
|
|
deh_offset if it is in ordeer */
|
|
mark_de_bad_offset (deh + i);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG_VERIFY_DENTRY
|
|
printf ("entries with mismatching deh_offsets: ");
|
|
for (i = 0; i < ih_entry_count (ih); i ++) {
|
|
if (de_bad_offset (deh + i))
|
|
printf ("%d ", i);
|
|
}
|
|
printf ("\n");
|
|
#endif /* DEBUG_VERIFY_DENTRY */
|
|
|
|
|
|
/* correct deh_locations such that code cutting entries will not get
|
|
screwed up */
|
|
{
|
|
int prev_loc;
|
|
int loc_fixed;
|
|
|
|
|
|
prev_loc = ih_item_len (ih);
|
|
for (i = 0; i < ih_entry_count (ih); i ++) {
|
|
loc_fixed = 0;
|
|
if (de_bad_location (deh + i)) {
|
|
deh[i].deh_location = cpu_to_le16 (prev_loc/* - 1*/);
|
|
mark_buffer_dirty (bh);
|
|
loc_fixed = 1;
|
|
} else {
|
|
if (deh_location (deh + i) >= prev_loc) {
|
|
deh[i].deh_location = cpu_to_le16 (prev_loc/* - 1*/);
|
|
mark_buffer_dirty (bh);
|
|
loc_fixed = 1;
|
|
}
|
|
}
|
|
|
|
prev_loc = deh_location (deh + i);
|
|
|
|
if (i == ih_entry_count (ih) - 1) {
|
|
/* last entry starts right after an array of dir entry headers */
|
|
if (!de_bad (deh + i) &&
|
|
deh_location (deh + i) != (DEH_SIZE * ih_entry_count (ih))) {
|
|
/* free space in the directory item */
|
|
fsck_log ("verify_direntry: block %lu, item %H has free space\n",
|
|
bh->b_blocknr, ih);
|
|
cut_entry (fs, bh, item_num, ih_entry_count (ih), 0);
|
|
}
|
|
if (deh_location (deh + i) != (DEH_SIZE * ih_entry_count (ih))) {
|
|
deh[i].deh_location = cpu_to_le16 (DEH_SIZE * ih_entry_count (ih));
|
|
loc_fixed = 1;
|
|
mark_buffer_dirty (bh);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_VERIFY_DENTRY
|
|
if (loc_fixed)
|
|
direntries [i] = 1;
|
|
#endif
|
|
} /* for */
|
|
|
|
#ifdef DEBUG_VERIFY_DENTRY
|
|
printf ("entries with fixed deh_locations: ");
|
|
for (i = 0; i < ih_entry_count (ih); i ++) {
|
|
if (direntries [i])
|
|
printf ("%d ", i);
|
|
}
|
|
printf ("\n");
|
|
#endif /* DEBUG_VERIFY_DENTRY */
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_VERIFY_DENTRY
|
|
printf (" N location name\n");
|
|
for (i = 0; i < ih_entry_count (ih); i ++) {
|
|
if (de_bad (deh + i) ||
|
|
(i && de_bad (deh + i - 1)) || /* previous entry marked bad */
|
|
(i < ih_entry_count (ih) - 1 && de_bad (deh + i + 1))) { /* next entry is marked bad */
|
|
/* print only entries to be deleted and their nearest neighbors */
|
|
printf ("%3d: %8d ", i, deh_location (deh + i));
|
|
if (de_bad (deh + i))
|
|
printf ("will be deleted\n");
|
|
else
|
|
printf ("\"%.*s\"\n", name_length (ih, deh + i, i),
|
|
name_in_entry (deh + i, i));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bad = 0;
|
|
tmp = *ih;
|
|
|
|
/* delete entries which are marked bad */
|
|
for (i = 0; i < ih_entry_count (ih); i ++) {
|
|
deh = B_I_DEH (bh, ih) + i;
|
|
if (de_bad (deh)) {
|
|
bad ++;
|
|
if (ih_entry_count (ih) == 1) {
|
|
delete_item (fs, bh, item_num);
|
|
break;
|
|
} else {
|
|
cut_entry (fs, bh, item_num, i, 1);
|
|
}
|
|
i --;
|
|
}
|
|
}
|
|
|
|
if (bad == ih_entry_count (&tmp)) {
|
|
fsck_log ("pass0: block %lu, item %H - all entries were deleted\n", bh->b_blocknr, &tmp);
|
|
return 0;
|
|
}
|
|
|
|
deh = B_I_DEH (bh, ih);
|
|
if (get_offset (&ih->ih_key) != deh_offset (deh)) {
|
|
fsck_log ("verify_direntry: block %lu, item %H: k_offset and deh_offset %u mismatched\n",
|
|
bh->b_blocknr, ih, deh_offset (deh));
|
|
set_offset (KEY_FORMAT_1, &ih->ih_key, deh_offset (deh));
|
|
mark_buffer_dirty (bh);
|
|
}
|
|
|
|
if (bad)
|
|
fsck_log ("pass0: block %lu, item %H: %d entries were deleted of \n",
|
|
bh->b_blocknr, &tmp, bad);
|
|
|
|
return 0;
|
|
|
|
#if 0
|
|
|
|
/* FIXME: temporary */
|
|
if (bad_locations > ih_entry_count (ih) / 2) {
|
|
fsck_log ("pass0: block %u: item %d (%H) had too bad directory - deleted\n",
|
|
bh->b_blocknr, item_num, ih);
|
|
delete_item (fs, bh, item_num);
|
|
return 0;
|
|
}
|
|
|
|
if (!dirty)
|
|
return 0;
|
|
|
|
/* something is broken */
|
|
|
|
fsck_log ("pass0: block %lu: %d-th item (%H) has %d bad entries..",
|
|
bh->b_blocknr, item_num, ih, dirty);
|
|
|
|
if (get_offset (&ih->ih_key) == DOT_OFFSET) {
|
|
/* first item of directory - make sure that "." and ".." are in place */
|
|
if (deh_offset (deh) != DOT_OFFSET || name_in_entry (deh, 0)[0] != '.') {
|
|
deh->deh_offset = cpu_to_le32 (DOT_OFFSET);
|
|
name_in_entry (deh, 0)[0] = '.';
|
|
}
|
|
if (deh_offset (deh + 1) != DOT_DOT_OFFSET ||
|
|
name_in_entry (deh + 1, 1)[0] != '.' || name_in_entry (deh + 1, 1)[1] != '.') {
|
|
(deh + 1)->deh_offset = cpu_to_le32 (DOT_DOT_OFFSET);
|
|
name_in_entry (deh + 1, 1)[0] = '.';
|
|
name_in_entry (deh + 1, 1)[1] = '.';
|
|
}
|
|
}
|
|
|
|
end = item + ih_item_len (ih);
|
|
deh += ih_entry_count (ih);
|
|
entries = (char *)deh;
|
|
total_entry_len = ih_item_len (ih) - DEH_SIZE * ih_entry_count (ih);
|
|
i = ih_entry_count (ih);
|
|
|
|
bad_entries = 0;
|
|
do {
|
|
i --;
|
|
deh --;
|
|
name_len = prob_name (fs, &entries, total_entry_len, deh_offset (deh));
|
|
if (!name_len) {
|
|
if (!bad_entries) {
|
|
deh->deh_location = cpu_to_le16 (entries - item);
|
|
} else {
|
|
deh->deh_location = cpu_to_le16 (deh_location (deh + 1) + 1);
|
|
}
|
|
bad_entries ++;
|
|
|
|
/*fsck_log ("verify_directory_item: entry %d: in string \'%s\' there is no substring matching hash %ld\n",
|
|
i, bad_name (entries, total_entry_len), masked_offset);*/
|
|
mark_de_bad (deh);
|
|
continue;
|
|
}
|
|
bad_entries = 0;
|
|
/*fsck_log ("verify_directory_item: entry %d: found \"%s\" name matching hash %ld\n",
|
|
i, bad_name (entries, name_len), masked_offset);*/
|
|
|
|
/* 'entries' points now to the name which match given offset -
|
|
so, set deh_location */
|
|
deh->deh_location = cpu_to_le16 (entries - item);
|
|
deh->deh_state = 0;
|
|
mark_de_visible (deh);
|
|
|
|
entries += name_len;
|
|
total_entry_len = end - entries;
|
|
/* skip padding zeros */
|
|
while (!*entries) {
|
|
entries ++;
|
|
total_entry_len --;
|
|
}
|
|
/* 'entries' points now at the place where next (previous)
|
|
entry should start */
|
|
} while ((char *)deh != item);
|
|
|
|
|
|
/* fixme: this will not work if all entries are to be deleted */
|
|
for (i = 0; i < ih_entry_count (ih); i ++, deh ++) {
|
|
deh = (struct reiserfs_de_head *)B_I_PITEM (bh, ih) + i;
|
|
if (de_bad (deh)) {
|
|
if (ih_entry_count (ih) == 1) {
|
|
delete_item (fs, bh, i);
|
|
break;
|
|
} else {
|
|
cut_entry (fs, bh, item_num, i);
|
|
}
|
|
i --;
|
|
}
|
|
/*
|
|
fsck_log ("verify_directory_item: %d-th entry is to be deleted: "
|
|
"\"%s\" does not match to hash %lu\n",
|
|
i, bad_name (name_in_entry (deh, i), name_length (ih, deh, i)),
|
|
deh_offset (deh));
|
|
*/
|
|
}
|
|
|
|
fsck_log ("%d entries were deleted\n", entry_count - ih_entry_count (ih));
|
|
mark_buffer_dirty (bh);
|
|
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
/* do this on pass 0 with every leaf marked used */
|
|
|
|
/* FIXME: we can improve fixing of broken keys: we can ssfe direct items which
|
|
go after stat data and have broken keys */
|
|
static void pass0_correct_leaf (reiserfs_filsys_t fs,
|
|
struct buffer_head * bh)
|
|
{
|
|
int i, j;
|
|
struct item_head * ih;
|
|
__u32 * ind_item;
|
|
unsigned long unfm_ptr;
|
|
int dirty = 0;
|
|
|
|
start_again:
|
|
|
|
ih = B_N_PITEM_HEAD (bh, 0);
|
|
for (i = 0; i < node_item_number (bh); i ++, ih ++) {
|
|
if (ih->ih_key.k_dir_id == 0 || ih->ih_key.k_objectid == 0) {
|
|
/* sometimes stat datas get k_objectid==0 or k_dir_id==0 */
|
|
if (i == (node_item_number (bh) - 1)) {
|
|
/* */
|
|
if (i == 0) {
|
|
fsck_log ("block %lu: item %d: (%H) is alone in the block\n",
|
|
bh->b_blocknr, i, ih);
|
|
return;
|
|
}
|
|
/* delete last item */
|
|
delete_item (fs, bh, i - 1);
|
|
return;
|
|
}
|
|
|
|
/* there is next item: if it is not stat data - take its k_dir_id
|
|
and k_objectid. if key order will be still wrong - the changed
|
|
item will be deleted */
|
|
if (!is_stat_data_ih (ih + 1)) {
|
|
fsck_log ("block %lu: item %d: (%H) fixed to ", bh->b_blocknr, i, ih);
|
|
ih->ih_key.k_dir_id = (ih + 1)->ih_key.k_dir_id;
|
|
ih->ih_key.k_objectid = (ih + 1)->ih_key.k_objectid;
|
|
set_offset (KEY_FORMAT_1, &ih->ih_key, 0);
|
|
set_type (KEY_FORMAT_1, &ih->ih_key, TYPE_STAT_DATA);
|
|
fsck_log ("(%H)\n", ih);
|
|
dirty = 1;
|
|
} else if (i == 0) {
|
|
delete_item (fs, bh, i);
|
|
goto start_again;
|
|
}
|
|
}
|
|
|
|
/* this recovers corruptions like the below:
|
|
1774 1732 0 0
|
|
116262638 1732 1 3
|
|
1774 1736 0 0 */
|
|
if (i && is_stat_data_ih (ih - 1) && !is_stat_data_ih (ih)) {
|
|
if (ih->ih_key.k_objectid != (ih - 1)->ih_key.k_objectid ||
|
|
ih->ih_key.k_dir_id != (ih - 1)->ih_key.k_dir_id ||
|
|
get_offset (&ih->ih_key) != 1) {
|
|
if (is_direntry_ih (ih)) {
|
|
fsck_log ("block %lu: item %d: no \".\" entry found in "
|
|
"the first item of a directory\n", bh->b_blocknr, i);
|
|
} else {
|
|
fsck_log ("block %lu: item %d: (%H) fixed to ",
|
|
bh->b_blocknr, i, ih);
|
|
ih->ih_key.k_dir_id = (ih - 1)->ih_key.k_dir_id;
|
|
ih->ih_key.k_objectid = (ih - 1)->ih_key.k_objectid;
|
|
|
|
if (ih_item_len (ih - 1) == SD_SIZE) {
|
|
/* stat data is new, therefore this item is new too */
|
|
set_offset (KEY_FORMAT_2, &(ih->ih_key), 1);
|
|
if (ih_entry_count (ih) != 0xffff)
|
|
set_type (KEY_FORMAT_2, &(ih->ih_key), TYPE_INDIRECT);
|
|
else
|
|
set_type (KEY_FORMAT_2, &(ih->ih_key), TYPE_DIRECT);
|
|
set_key_format (ih, KEY_FORMAT_2);
|
|
} else {
|
|
/* stat data is old, therefore this item is old too */
|
|
set_offset (KEY_FORMAT_1, &(ih->ih_key), 1);
|
|
if (ih_entry_count (ih) != 0xffff)
|
|
set_type (KEY_FORMAT_1, &(ih->ih_key), TYPE_INDIRECT);
|
|
else
|
|
set_type (KEY_FORMAT_1, &(ih->ih_key), TYPE_DIRECT);
|
|
set_key_format (ih, KEY_FORMAT_1);
|
|
}
|
|
fsck_log ("%H\n", ih);
|
|
dirty = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* FIXME: corruptions like:
|
|
56702 66802 1 2
|
|
56702 65536 0 0
|
|
56702 66803 1 2
|
|
do not get recovered (both last items will be deleted) */
|
|
/* delete item if it is not in correct order of object items */
|
|
if (i && not_of_one_file (&ih->ih_key, &(ih - 1)->ih_key) &&
|
|
!is_stat_data_ih (ih)) {
|
|
fsck_log ("block %lu: item %d: %H follows non stat item %H - deleted\n",
|
|
bh->b_blocknr, i, ih, ih - 1);
|
|
delete_item (fs, bh, i);
|
|
goto start_again;
|
|
}
|
|
|
|
if (i && comp_keys (&(ih - 1)->ih_key, &ih->ih_key) != -1) {
|
|
/* previous item has key not smaller than the key of currect item */
|
|
if (is_stat_data_ih (ih - 1) && !is_stat_data_ih (ih)) {
|
|
/* fix key of stat data such as if it was stat data of that item */
|
|
fsck_log ("pass0: block %lu: %d-th item %k is out of order, made a stat data of %d-th (%k)\n",
|
|
bh->b_blocknr, i - 1, &(ih - 1)->ih_key, i, &ih->ih_key);
|
|
(ih - 1)->ih_key.k_dir_id = ih->ih_key.k_dir_id;
|
|
(ih - 1)->ih_key.k_objectid = ih->ih_key.k_objectid;
|
|
set_offset (KEY_FORMAT_1, &(ih - 1)->ih_key, 0);
|
|
set_type (KEY_FORMAT_1, &(ih - 1)->ih_key, TYPE_STAT_DATA);
|
|
dirty = 1;
|
|
} else {
|
|
/* ok, we have to delete one of these two - decide which one */
|
|
int retval;
|
|
|
|
/* something will be deleted */
|
|
dirty = 1;
|
|
retval = upper_correct (bh, ih - 1, i - 1);
|
|
switch (retval) {
|
|
case 0:
|
|
/* delete upper item */
|
|
fsck_log ("pass0: block %lu: %d-th (upper) item (%k) is out of order, deleted\n",
|
|
bh->b_blocknr, i - 1, &(ih - 1)->ih_key);
|
|
delete_item (fs, bh, i - 1);
|
|
goto start_again;
|
|
|
|
case 1:
|
|
/* delete lower item */
|
|
fsck_log ("pass0: block %lu: %d-th (lower) item (%k) is out of order, deleted\n",
|
|
bh->b_blocknr, i, &ih->ih_key);
|
|
delete_item (fs, bh, i);
|
|
goto start_again;
|
|
|
|
default:
|
|
/* upper item was the first item of a node */
|
|
}
|
|
|
|
retval = lower_correct (bh, ih, i);
|
|
switch (retval) {
|
|
case 0:
|
|
/* delete lower item */
|
|
fsck_log ("pass0: block %lu: %d-th (lower) item (%k) is out of order, deleted\n",
|
|
bh->b_blocknr, i, &ih->ih_key);
|
|
delete_item (fs, bh, i);
|
|
goto start_again;
|
|
|
|
case 1:
|
|
/* delete upper item */
|
|
fsck_log ("pass0: block %lu: %d-th (upper) item (%k) is out of order, deleted\n",
|
|
bh->b_blocknr, i - 1, &(ih - 1)->ih_key);
|
|
delete_item (fs, bh, i - 1);
|
|
goto start_again;
|
|
|
|
default:
|
|
/* there wer only two items in a node, so we could not
|
|
decide what to delete, go and ask user */
|
|
}
|
|
fsck_log ("pass0: which of these items looks better (other will be deleted)?\n"
|
|
"%H\n%H\n", ih - 1, ih);
|
|
if (fsck_user_confirmed (fs, "1 or 2?", "1\n", 1))
|
|
delete_item (fs, bh, i - 1);
|
|
else
|
|
delete_item (fs, bh, i);
|
|
goto start_again;
|
|
}
|
|
}
|
|
|
|
if (is_stat_data_ih (ih) && (ih_item_len (ih) != SD_SIZE &&
|
|
ih_item_len (ih) != SD_V1_SIZE)) {
|
|
fsck_log ("pass0: block %lu, stat data of wrong length %H - deleted\n",
|
|
bh, ih);
|
|
delete_item (fs, bh, i);
|
|
goto start_again;
|
|
}
|
|
|
|
dirty += correct_key_format (ih);
|
|
|
|
if (is_stat_data_ih (ih)) {
|
|
;/*correct_stat_data (fs, bh, i);*/
|
|
}
|
|
|
|
if (is_direntry_ih (ih)) {
|
|
verify_directory_item (fs, bh, i);
|
|
continue;
|
|
}
|
|
|
|
if (!is_indirect_ih (ih))
|
|
continue;
|
|
|
|
ind_item = (__u32 *)B_I_PITEM (bh, ih);
|
|
for (j = 0; j < I_UNFM_NUM (ih); j ++) {
|
|
unfm_ptr = le32_to_cpu (ind_item [j]);
|
|
if (!unfm_ptr)
|
|
continue;
|
|
|
|
if (fsck_mode (fs) == FSCK_ZERO_FILES) {
|
|
/* FIXME: this is temporary mode of fsck */
|
|
ind_item [j] = 0;
|
|
reiserfs_bitmap_clear_bit (fsck_new_bitmap(fs), unfm_ptr);
|
|
tmp_zeroed ++;
|
|
dirty = 1;
|
|
continue;
|
|
}
|
|
|
|
if (not_data_block (fs, unfm_ptr) || /* journal area or bitmap or super block */
|
|
unfm_ptr >= SB_BLOCK_COUNT (fs)) {/* garbage in pointer */
|
|
|
|
stats (fs)->wrong_pointers ++;
|
|
/*
|
|
fsck_log ("pass0: %d-th pointer (%lu) in item %k (leaf block %lu) is wrong\n",
|
|
j, unfm_ptr, &ih->ih_key, bh->b_blocknr);
|
|
*/
|
|
ind_item [j] = 0;
|
|
dirty = 1;
|
|
continue;
|
|
}
|
|
#if 0
|
|
if (!was_block_used (unfm_ptr)) {
|
|
/* this will get to a pool of allocable blocks */
|
|
ind_item [j] = 0;
|
|
dirty = 1;
|
|
stat_wrong_pointer_found (fs);
|
|
continue;
|
|
}
|
|
#endif
|
|
/* mark block in bitmaps of unformatted nodes */
|
|
register_unfm (unfm_ptr);
|
|
}
|
|
}
|
|
|
|
/* mark all objectids in use */
|
|
ih = B_N_PITEM_HEAD (bh, 0);
|
|
for (i = 0; i < node_item_number (bh); i ++, ih ++) {
|
|
mark_objectid_really_used (proper_id_map (fs), le32_to_cpu (ih->ih_key.k_dir_id));
|
|
mark_objectid_really_used (proper_id_map (fs), le32_to_cpu (ih->ih_key.k_objectid));
|
|
}
|
|
|
|
if (node_item_number (bh) < 1) {
|
|
/* pass 1 will skip this */
|
|
stats(fs)->all_contents_removed ++;
|
|
fsck_log ("pass0: block %lu got all items deleted\n",
|
|
bh->b_blocknr);
|
|
} else {
|
|
/* pass1 will use this bitmap */
|
|
pass0_mark_leaf (bh->b_blocknr);
|
|
|
|
}
|
|
if (dirty) {
|
|
stats(fs)->leaves_corrected ++;
|
|
mark_buffer_dirty (bh);
|
|
}
|
|
}
|
|
|
|
|
|
static int is_bad_sd (struct item_head * ih, char * item)
|
|
{
|
|
struct stat_data * sd = (struct stat_data *)item;
|
|
|
|
if (ih->ih_key.u.k_offset_v1.k_offset || ih->ih_key.u.k_offset_v1.k_uniqueness) {
|
|
reiserfs_warning (stderr, "Bad SD? %H\n", ih);
|
|
return 1;
|
|
}
|
|
|
|
if (ih_item_len (ih) == SD_V1_SIZE) {
|
|
/* looks like old stat data */
|
|
if (ih_key_format (ih) != KEY_FORMAT_1)
|
|
fsck_log ("item %H has wrong format\n", ih);
|
|
return 0;
|
|
}
|
|
|
|
if (!S_ISDIR (sd->sd_mode) && !S_ISREG(sd->sd_mode) &&
|
|
!S_ISCHR (sd->sd_mode) && !S_ISBLK(sd->sd_mode) &&
|
|
!S_ISLNK (sd->sd_mode) && !S_ISFIFO(sd->sd_mode) &&
|
|
!S_ISSOCK(sd->sd_mode)) {
|
|
/*fsck_log ("file %k unexpected mode encountered 0%o\n", &ih->ih_key, sd->sd_mode)*/;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int is_bad_directory (struct item_head * ih, char * item, int dev, int blocksize)
|
|
{
|
|
int i;
|
|
char * name;
|
|
int namelen, entrylen;
|
|
struct reiserfs_de_head * deh = (struct reiserfs_de_head *)item;
|
|
__u32 prev_offset = 0;
|
|
__u16 prev_location = ih_item_len (ih);
|
|
int min_entry_size = 1;/* we have no way to understand whether the
|
|
filesystem were created in 3.6 format or
|
|
converted to it. So, we assume that minimal name
|
|
length is 1 */
|
|
|
|
if (ih_item_len (ih) / (DEH_SIZE + min_entry_size) < ih_entry_count (ih))
|
|
/* entry count is too big */
|
|
return 1;
|
|
|
|
for (i = 0; i < ih_entry_count (ih); i ++, deh ++) {
|
|
entrylen = entry_length(ih, deh, i);
|
|
if (entrylen > REISERFS_MAX_NAME_LEN (blocksize)) {
|
|
return 1;
|
|
}
|
|
if (deh_offset (deh) <= prev_offset) {
|
|
return 1;
|
|
}
|
|
prev_offset = deh_offset (deh);
|
|
|
|
if (deh_location(deh) + entrylen != prev_location) {
|
|
return 1;
|
|
}
|
|
prev_location = deh_location (deh);
|
|
|
|
namelen = name_length (ih, deh, i);
|
|
name = name_in_entry (deh, i);
|
|
if (!is_properly_hashed (fs, name, namelen, deh_offset (deh))) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* change incorrect block adresses by 0. Do not consider such item as incorrect */
|
|
static int is_bad_indirect (struct item_head * ih, char * item, int dev, int blocksize)
|
|
{
|
|
int i;
|
|
int bad = 0;
|
|
int blocks;
|
|
|
|
if (ih_item_len(ih) % UNFM_P_SIZE) {
|
|
fsck_log ("is_bad_indirect: indirect item of %H of invalid length\n", ih);
|
|
return 1;
|
|
}
|
|
|
|
blocks = SB_BLOCK_COUNT (fs);
|
|
|
|
for (i = 0; i < I_UNFM_NUM (ih); i ++) {
|
|
__u32 * ind = (__u32 *)item;
|
|
|
|
if (le32_to_cpu (ind[i]) >= blocks) {
|
|
bad ++;
|
|
fsck_log ("is_bad_indirect: %d-th pointer of item %H looks bad (%lu)\n",
|
|
i, ih, le32_to_cpu (ind [i]));
|
|
continue;
|
|
}
|
|
}
|
|
return bad;
|
|
}
|
|
|
|
|
|
/* this is used by pass1.c:save_item and check.c:is_leaf_bad */
|
|
int is_bad_item (struct buffer_head * bh, struct item_head * ih, char * item)
|
|
{
|
|
int blocksize, dev;
|
|
|
|
blocksize = bh->b_size;
|
|
dev = bh->b_dev;
|
|
|
|
// FIXME: refuse transparently bad items
|
|
if (ih->ih_key.k_dir_id == ih->ih_key.k_objectid)
|
|
return 1;
|
|
if (!ih->ih_key.k_dir_id || !ih->ih_key.k_objectid)
|
|
return 1;
|
|
|
|
if (is_stat_data_ih(ih))
|
|
return is_bad_sd (ih, item);
|
|
|
|
if (is_direntry_ih (ih))
|
|
return is_bad_directory (ih, item, dev, blocksize);
|
|
|
|
if (is_indirect_ih (ih))
|
|
return is_bad_indirect (ih, item, dev, blocksize);
|
|
|
|
if (is_direct_ih (ih))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int is_leaf_bad (struct buffer_head * bh)
|
|
{
|
|
int i;
|
|
struct item_head * ih;
|
|
int bad = 0;
|
|
|
|
assert (is_leaf_node (bh));
|
|
|
|
for (i = 0, ih = B_N_PITEM_HEAD (bh, 0); i < B_NR_ITEMS (bh); i ++, ih ++) {
|
|
if (is_bad_item (bh, ih, B_I_PITEM (bh, ih))) {
|
|
fsck_log ("is_leaf_bad: block %lu: %d-th item (%H) is bad\n",
|
|
bh->b_blocknr, i, ih);
|
|
bad = 1;
|
|
continue;
|
|
}
|
|
|
|
if (i && bad_pair (fs, bh, i)) {
|
|
fsck_log ("is_leaf_bad: block %luL %d-th item (%H) and "
|
|
"the next one (%H) are in wrong order\n",
|
|
bh->b_blocknr, i - 1, ih - 1, ih);
|
|
bad = 1;
|
|
}
|
|
}
|
|
|
|
return bad;
|
|
}
|
|
|
|
|
|
static void go_through (reiserfs_filsys_t fs)
|
|
{
|
|
struct buffer_head * bh;
|
|
int i;
|
|
int what_node;
|
|
unsigned long done = 0, total;
|
|
|
|
if (fsck_mode (fs) == DO_TEST) {
|
|
/* just to test pass0_correct_leaf */
|
|
bh = bread (fs->s_dev, stats(fs)->test, fs->s_blocksize);
|
|
|
|
/*
|
|
if (is_leaf_bad (bh)) {
|
|
fsck_progress ("############### bad #################\n");
|
|
}
|
|
*/
|
|
pass0_correct_leaf (fs, bh);
|
|
|
|
print_block (stdout, fs, bh, 3, -1, -1);
|
|
|
|
if (is_leaf_bad (bh)) {
|
|
fsck_progress ("############### still bad #################\n");
|
|
}
|
|
brelse (bh);
|
|
reiserfs_free (fs);
|
|
exit(4);
|
|
}
|
|
|
|
|
|
total = reiserfs_bitmap_ones (fsck_disk_bitmap (fs));
|
|
fsck_progress ("\nPass 0 (%lu (of %lu) blocks will be read):\n",
|
|
total, SB_BLOCK_COUNT (fs));
|
|
|
|
|
|
for (i = 0; i < SB_BLOCK_COUNT (fs); i ++) {
|
|
if (!is_to_be_read (fs, i))
|
|
continue;
|
|
|
|
print_how_far (&done, total, 1, fsck_quiet (fs));
|
|
|
|
bh = bread (fs->s_dev, i, fs->s_blocksize);
|
|
if (!bh) {
|
|
/* we were reading one block at time, and failed, so mark
|
|
block bad */
|
|
fsck_progress ("pass0: reading block %lu failed\n", i);
|
|
continue;
|
|
}
|
|
|
|
if (not_data_block (fs, i))
|
|
reiserfs_panic ("not data block found");
|
|
|
|
stats (fs)->analyzed ++;
|
|
what_node = who_is_this (bh->b_data, fs->s_blocksize);
|
|
if ( what_node != THE_LEAF ) {
|
|
brelse (bh);
|
|
continue;
|
|
}
|
|
pass0_correct_leaf (fs, bh);
|
|
brelse (bh);
|
|
}
|
|
|
|
|
|
#if 0
|
|
for (i = 0; i < SB_BLOCK_COUNT (fs); i += nr_to_read) {
|
|
to_scan = how_many_to_scan (fs, i, nr_to_read);
|
|
if (to_scan) {
|
|
print_how_far (&done, total, to_scan, fsck_quiet (fs));
|
|
|
|
/* at least one of nr_to_read blocks is to be checked */
|
|
bbh = bread (fs->s_dev, i / nr_to_read, fs->s_blocksize * nr_to_read);
|
|
if (bbh) {
|
|
for (j = 0; j < nr_to_read; j ++) {
|
|
if (!is_to_be_read (fs, i + j))
|
|
continue;
|
|
|
|
if (not_data_block (fs, i + j))
|
|
reiserfs_panic ("not data block found");
|
|
|
|
stats (fs)->analyzed ++;
|
|
|
|
data = bbh->b_data + j * fs->s_blocksize;
|
|
what_node = who_is_this (data, fs->s_blocksize);
|
|
if ( what_node != THE_LEAF ) {
|
|
continue;
|
|
}
|
|
|
|
/* the node looks like a leaf, but it still can be
|
|
not perfect */
|
|
bh = make_buffer (fs->s_dev, i + j, fs->s_blocksize, data);
|
|
|
|
/*printf ("block %lu .. ", bh->b_blocknr);fflush(stdout);*/
|
|
pass0_correct_leaf (fs, bh);
|
|
/*printf ("ok\n");fflush(stdout);*/
|
|
|
|
brelse (bh);
|
|
}
|
|
|
|
if (buffer_dirty (bbh))
|
|
bwrite (bbh);
|
|
bforget (bbh);
|
|
} else {
|
|
done -= to_scan;
|
|
/* bread failed */
|
|
if (nr_to_read != 1) {
|
|
/* we tryied to read bunch of blocks. Try to read them by one */
|
|
nr_to_read = 1;
|
|
i --;
|
|
continue;
|
|
} else {
|
|
/* we were reading one block at time, and failed, so mark
|
|
block bad */
|
|
fsck_progress ("pass0: block %lu is bad, marked used\n", i);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nr_to_read == 1 && ((i + 1) % NR_TO_READ) == 0) {
|
|
/* we have read NR_TO_READ blocks one at time, switch back to
|
|
reading NR_TO_READ blocks at time */
|
|
i -= (NR_TO_READ - 1);
|
|
nr_to_read = NR_TO_READ;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/* just in case */
|
|
mark_objectid_really_used (proper_id_map (fs), REISERFS_ROOT_OBJECTID);
|
|
|
|
fsck_progress ("\n");
|
|
|
|
if (fsck_save_leaf_bitmap (fs)) {
|
|
reiserfs_bitmap_save (stats (fs)->new_bitmap_file_name, leaves_bitmap);
|
|
}
|
|
}
|
|
|
|
|
|
/* this makes a map of blocks which can be allocated when fsck will continue: */
|
|
static void find_allocable_blocks (reiserfs_filsys_t fs)
|
|
{
|
|
int i;
|
|
|
|
|
|
fsck_progress ("Looking for allocable blocks .. ");
|
|
stats (fs)->all_blocks = SB_BLOCK_COUNT (fs);
|
|
|
|
/* find how many leaves are not pointed by any indirect items */
|
|
for (i = 0; i < SB_BLOCK_COUNT (fs); i ++) {
|
|
if (not_data_block (fs, i))
|
|
continue;
|
|
|
|
#if 0
|
|
if (!was_block_used (i)) {
|
|
/* marked free in the on-disk bitmap - so it is allocable */
|
|
make_allocable (i);
|
|
stat_allocable_found (fs);
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
if (pass0_is_leaf (i)) {
|
|
/* block looks like a reiserfs leaf */
|
|
stats(fs)->leaves ++;
|
|
if (pass0_is_good_unfm (i) || pass0_is_bad_unfm (i))
|
|
/* leaf to which one or more indirect items point to */
|
|
stats(fs)->pointed_leaves ++;
|
|
}
|
|
|
|
if (pass0_is_good_unfm (i) && pass0_is_bad_unfm (i))
|
|
die ("find_allocable_blocks: bad and good unformatted");
|
|
|
|
if (pass0_is_good_unfm (i)) {
|
|
/* blocks which were pointed only once */
|
|
stats(fs)->pointed ++;
|
|
stats(fs)->pointed_once ++;
|
|
continue;
|
|
}
|
|
if (pass0_is_bad_unfm (i)) {
|
|
/* blocks pointed more than once */
|
|
stats(fs)->pointed ++;
|
|
stats(fs)->pointed_more_than_once ++;
|
|
continue;
|
|
}
|
|
|
|
/* blocks which marked used but are not leaves and are not
|
|
pointed (internals in short) get into bitmap of allocable
|
|
blocks */
|
|
if (!pass0_is_leaf (i)) {
|
|
make_allocable (i);
|
|
stats(fs)->allocable ++;
|
|
}
|
|
}
|
|
fsck_progress ("ok\n");
|
|
}
|
|
|
|
#if 0
|
|
int are_there_used_leaves (unsigned long from, int count)
|
|
{
|
|
int i;
|
|
int used;
|
|
|
|
used = 0;
|
|
for (i = 0; i < count; i ++) {
|
|
if ((SB_BLOCK_COUNT (fs) > from + i) &&
|
|
pass0_is_leaf (from + i))
|
|
used ++;
|
|
}
|
|
return used;
|
|
}
|
|
#endif
|
|
|
|
int is_used_leaf (unsigned long block)
|
|
{
|
|
return pass0_is_leaf (block);
|
|
}
|
|
|
|
|
|
int how_many_leaves_were_there (void)
|
|
{
|
|
return stats (fs)->leaves;
|
|
}
|
|
|
|
/* these are used to correct uformatted node pointers */
|
|
int is_bad_unformatted (unsigned long block)
|
|
{
|
|
return pass0_is_bad_unfm (block);
|
|
}
|
|
|
|
|
|
/* this is for check only. With this we make sure that all pointers we
|
|
put into tree on pass 1 do not point to leaves (FIXME), do not
|
|
point to journal, bitmap, etc, do not point out of fs boundary and
|
|
are marked used in on-disk bitmap */
|
|
int still_bad_unfm_ptr_1 (unsigned long block)
|
|
{
|
|
if (!block)
|
|
return 0;
|
|
if (pass0_is_leaf (block))
|
|
return 1;
|
|
if (pass0_is_bad_unfm (block) && !is_bad_unfm_in_tree_once (block))
|
|
return 1;
|
|
if (not_data_block (fs, block))
|
|
return 1;
|
|
/*
|
|
if (!was_block_used (block))
|
|
return 1;
|
|
*/
|
|
if (block >= stats (fs)->all_blocks)
|
|
return 1;
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
/* pointers to data block which get into tree are checked with this */
|
|
int still_bad_unfm_ptr_2 (unsigned long block)
|
|
{
|
|
if (!block)
|
|
return 0;
|
|
if (is_block_used (block))
|
|
return 1;
|
|
if (block >= stats (fs)->all_blocks)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* these are used to allocate blocks for tree building */
|
|
int are_there_allocable_blocks (int amout_needed)
|
|
{
|
|
if (reiserfs_bitmap_zeros (fsck_allocable_bitmap (fs)) < amout_needed) {
|
|
int zeros = 0, i;
|
|
|
|
fsck_progress ("Hmm, there are not enough allocable blocks, checking bitmap...");fflush (stdout);
|
|
for (i = 0; i < fsck_allocable_bitmap (fs)->bm_bit_size; i ++)
|
|
if (!reiserfs_bitmap_test_bit (fsck_allocable_bitmap (fs), i))
|
|
zeros ++;
|
|
fsck_progress ("there are %d zeros, btw\n", zeros);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
unsigned long alloc_block (void)
|
|
{
|
|
unsigned long block = 0; /* FIXME: start point could be used */
|
|
|
|
if (reiserfs_bitmap_find_zero_bit (fsck_allocable_bitmap (fs), &block)) {
|
|
die ("alloc_block: allocable blocks counter is wrong");
|
|
return 0;
|
|
}
|
|
reiserfs_bitmap_set_bit (fsck_allocable_bitmap (fs), block);
|
|
return block;
|
|
}
|
|
|
|
|
|
void make_allocable (unsigned long block)
|
|
{
|
|
reiserfs_bitmap_clear_bit (fsck_allocable_bitmap (fs), block);
|
|
}
|
|
|
|
|
|
unsigned long how_many_uninsertables_were_there (void)
|
|
{
|
|
return stats (fs)->uninsertable_leaves;
|
|
}
|
|
|
|
|
|
unsigned long how_many_items_were_saved (void)
|
|
{
|
|
return stats (fs)->saved_on_pass1;
|
|
}
|
|
|
|
|
|
static void choose_hash_function (reiserfs_filsys_t fs)
|
|
{
|
|
unsigned long max;
|
|
int hash_code;
|
|
int i;
|
|
|
|
max = 0;
|
|
hash_code = func2code (0);
|
|
|
|
for (i = 0; i < stats (fs)->hash_amount; i ++) {
|
|
/* remember hash whihc got more hits */
|
|
if (stats (fs)->hash_hits [i] > max) {
|
|
hash_code = i;
|
|
max = stats (fs)->hash_hits [i];
|
|
}
|
|
|
|
if (stats (fs)->hash_hits [i])
|
|
fsck_log ("%s got %lu hits\n", code2name (i),
|
|
stats (fs)->hash_hits [i]);
|
|
}
|
|
if (max == 0) {
|
|
/* no names were found. take either super block value or
|
|
default */
|
|
reiserfs_hash (fs) = code2func (rs_hash (fs->s_rs));
|
|
if (!reiserfs_hash (fs))
|
|
reiserfs_hash (fs) = code2func (DEFAULT_HASH);
|
|
return;
|
|
}
|
|
|
|
/* compare the most appropriate hash with the hash set in super block */
|
|
if (hash_code != rs_hash (fs->s_rs)) {
|
|
fsck_progress ("Selected hash (%s) does not match to one set in super block (%s).\n",
|
|
code2name (hash_code), code2name (rs_hash (fs->s_rs)));
|
|
/*
|
|
if (!fsck_user_confirmed (fs, "Overwrite?(Yes)", "Yes", 1)) {
|
|
fsck_progress ("Not confirmed\n");
|
|
exit (4);
|
|
}
|
|
*/
|
|
set_hash (fs->s_rs, hash_code);
|
|
} else {
|
|
fsck_progress ("\t%s hash is selected\n", code2name (hash_code));
|
|
}
|
|
|
|
reiserfs_hash (fs) = code2func (hash_code);
|
|
}
|
|
|
|
|
|
int pass_0 (reiserfs_filsys_t fs)
|
|
{
|
|
if (fsck_log_file (fs) != stderr)
|
|
/* this is just to separate warnings in the log file */
|
|
fsck_log ("####### Pass 0 #######\n");
|
|
|
|
if (fsck_mode (fs) == FSCK_ZERO_FILES) {
|
|
reiserfs_bitmap_fill (fsck_new_bitmap (fs));
|
|
fsck_progress ("Zeroing existing files - all blocks marked used\n");
|
|
}
|
|
|
|
make_aux_bitmaps (fs);
|
|
|
|
/* pass0 gathers statistics about hash hits */
|
|
hash_hits_init (fs);
|
|
|
|
/* scan the partition */
|
|
go_through (fs);
|
|
|
|
/* find blocks which can be allocated */
|
|
find_allocable_blocks (fs);
|
|
|
|
if (fsck_mode (fs) == FSCK_ZERO_FILES) {
|
|
/* flush bitmaps and exit */
|
|
fsck_progress ("Zeroing existing files - zeroed %lu blocks\n", tmp_zeroed);
|
|
reiserfs_flush_bitmap (fsck_new_bitmap (fs), fs);
|
|
reiserfs_close (fs);
|
|
exit (0);
|
|
}
|
|
|
|
|
|
choose_hash_function (fs);
|
|
|
|
stage_report (0, fs);
|
|
return 0;
|
|
}
|
|
|
|
|
|
#if 0
|
|
|
|
/* node looks like a leaf (block head and ih_item_length & ih_location
|
|
of item_head array are correct. This function recovers (tries to) node's key
|
|
table and directory items. */
|
|
static void recover_leaf (reiserfs_filsys_t fs, struct buffer_head * bh)
|
|
{
|
|
int i;
|
|
struct item_head * ih;
|
|
|
|
/* mark transparently corrupted items - bad */
|
|
ih = B_N_PITEM_HEAD (bh, 0);
|
|
for (i = 0; i < node_item_number (bh); i ++, ih ++) {
|
|
if (type_unknown (&ih->ih_key) ||
|
|
ih->ih_key.k_dir_id == 0 ||
|
|
ih->ih_key.k_objectid) {
|
|
mark_ih_bad (ih);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|