705 lines
20 KiB
C
705 lines
20 KiB
C
/*
|
|
* Copyright 1996-2001 Hans Reiser
|
|
*/
|
|
#include "fsck.h"
|
|
|
|
|
|
/* key1 and key2 are pointer to deh_offset of the struct reiserfs_de_head */
|
|
int comp_dir_entries (void * key1, void * key2)
|
|
{
|
|
__u32 off1, off2;
|
|
|
|
off1 = le32_to_cpu (*(__u32 *)key1);
|
|
off2 = le32_to_cpu (*(__u32 *)key2);
|
|
|
|
if (off1 < off2)
|
|
return -1;
|
|
if (off1 > off2)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void init_tb_struct (struct tree_balance * tb, struct super_block * s, struct path * path, int size)
|
|
{
|
|
memset (tb, '\0', sizeof(struct tree_balance));
|
|
tb->tb_sb = s;
|
|
tb->tb_path = path;
|
|
|
|
PATH_OFFSET_PBUFFER(path, ILLEGAL_PATH_ELEMENT_OFFSET) = NULL;
|
|
PATH_OFFSET_POSITION(path, ILLEGAL_PATH_ELEMENT_OFFSET) = 0;
|
|
tb->insert_size[0] = size;
|
|
}
|
|
|
|
|
|
struct tree_balance * cur_tb = 0;
|
|
|
|
void reiserfsck_paste_into_item (struct path * path, const char * body, int size)
|
|
{
|
|
struct tree_balance tb;
|
|
|
|
init_tb_struct (&tb, fs, path, size);
|
|
if (fix_nodes (/*tb.transaction_handle,*/ M_PASTE, &tb, 0/*ih*/) != CARRY_ON)
|
|
//fix_nodes(options, tree_balance, ih_to_option, body_to_option)
|
|
|
|
die ("reiserfsck_paste_into_item: fix_nodes failed");
|
|
|
|
do_balance (/*tb.transaction_handle,*/ &tb, 0, body, M_PASTE, 0/*zero num*/);
|
|
}
|
|
|
|
|
|
void reiserfsck_insert_item (struct path * path, struct item_head * ih, const char * body)
|
|
{
|
|
struct tree_balance tb;
|
|
|
|
init_tb_struct (&tb, fs, path, IH_SIZE + ih_item_len(ih));
|
|
if (fix_nodes (/*tb.transaction_handle,*/ M_INSERT, &tb, ih/*, body*/) != CARRY_ON)
|
|
die ("reiserfsck_insert_item: fix_nodes failed");
|
|
do_balance (/*tb.transaction_handle,*/ &tb, ih, body, M_INSERT, 0/*zero num*/);
|
|
}
|
|
|
|
|
|
static void free_unformatted_nodes (struct item_head * ih, struct buffer_head * bh)
|
|
{
|
|
__u32 * punfm = (__u32 *)B_I_PITEM (bh, ih);
|
|
int i;
|
|
|
|
for (i = 0; i < I_UNFM_NUM (ih); i ++, punfm ++)
|
|
if (*punfm) {
|
|
struct buffer_head * to_be_forgotten;
|
|
|
|
to_be_forgotten = find_buffer (fs->s_dev, *punfm, fs->s_blocksize);
|
|
if (to_be_forgotten) {
|
|
//atomic_inc(&to_be_forgotten->b_count);
|
|
to_be_forgotten->b_count ++;
|
|
bforget (to_be_forgotten);
|
|
}
|
|
|
|
reiserfs_free_block (fs, *punfm);
|
|
}
|
|
}
|
|
|
|
|
|
void reiserfsck_delete_item (struct path * path, int temporary)
|
|
{
|
|
struct tree_balance tb;
|
|
struct item_head * ih = PATH_PITEM_HEAD (path);
|
|
|
|
if (is_indirect_ih (ih) && !temporary)
|
|
free_unformatted_nodes (ih, PATH_PLAST_BUFFER (path));
|
|
|
|
init_tb_struct (&tb, fs, path, -(IH_SIZE + ih_item_len(ih)));
|
|
|
|
if (fix_nodes (/*tb.transaction_handle,*/ M_DELETE, &tb, 0/*ih*/) != CARRY_ON)
|
|
die ("reiserfsck_delete_item: fix_nodes failed");
|
|
|
|
do_balance (/*tb.transaction_handle,*/ &tb, 0, 0, M_DELETE, 0/*zero num*/);
|
|
}
|
|
|
|
|
|
void reiserfsck_cut_from_item (struct path * path, int cut_size)
|
|
{
|
|
struct tree_balance tb;
|
|
struct item_head * ih;
|
|
|
|
if (cut_size >= 0)
|
|
die ("reiserfsck_cut_from_item: cut size == %d", cut_size);
|
|
|
|
if (is_indirect_ih (ih = PATH_PITEM_HEAD (path))) {
|
|
__u32 unfm_ptr = B_I_POS_UNFM_POINTER (PATH_PLAST_BUFFER (path), ih, I_UNFM_NUM (ih) - 1);
|
|
if (unfm_ptr) {
|
|
struct buffer_head * to_be_forgotten;
|
|
|
|
to_be_forgotten = find_buffer (fs->s_dev, le32_to_cpu (unfm_ptr), fs->s_blocksize);
|
|
if (to_be_forgotten) {
|
|
//atomic_inc(&to_be_forgotten->b_count);
|
|
to_be_forgotten->b_count ++;
|
|
bforget (to_be_forgotten);
|
|
}
|
|
reiserfs_free_block (fs, le32_to_cpu (unfm_ptr));
|
|
}
|
|
}
|
|
|
|
|
|
init_tb_struct (&tb, fs, path, cut_size);
|
|
|
|
if (fix_nodes (/*tb.transaction_handle,*/ M_CUT, &tb, 0) != CARRY_ON)
|
|
die ("reiserfsck_cut_from_item: fix_nodes failed");
|
|
|
|
do_balance (/*tb.transaction_handle,*/ &tb, 0, 0, M_CUT, 0/*zero num*/);
|
|
}
|
|
|
|
|
|
/* uget_lkey is utils clone of stree.c/get_lkey */
|
|
struct key * uget_lkey (struct path * path)
|
|
{
|
|
int pos, offset = path->path_length;
|
|
struct buffer_head * bh;
|
|
|
|
if (offset < FIRST_PATH_ELEMENT_OFFSET)
|
|
die ("uget_lkey: illegal offset in the path (%d)", offset);
|
|
|
|
|
|
/* While not higher in path than first element. */
|
|
while (offset-- > FIRST_PATH_ELEMENT_OFFSET) {
|
|
if (! buffer_uptodate (PATH_OFFSET_PBUFFER (path, offset)) )
|
|
die ("uget_lkey: parent is not uptodate");
|
|
|
|
/* Parent at the path is not in the tree now. */
|
|
if (! B_IS_IN_TREE (bh = PATH_OFFSET_PBUFFER (path, offset)))
|
|
die ("uget_lkey: buffer on the path is not in tree");
|
|
|
|
/* Check whether position in the parent is correct. */
|
|
if ((pos = PATH_OFFSET_POSITION (path, offset)) > B_NR_ITEMS (bh))
|
|
die ("uget_lkey: invalid position (%d) in the path", pos);
|
|
|
|
/* Check whether parent at the path really points to the child. */
|
|
if (B_N_CHILD_NUM (bh, pos) != PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr)
|
|
die ("uget_lkey: invalid block number (%d). Must be %d",
|
|
B_N_CHILD_NUM (bh, pos), PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr);
|
|
|
|
/* Return delimiting key if position in the parent is not equal to zero. */
|
|
if (pos)
|
|
return B_N_PDELIM_KEY(bh, pos - 1);
|
|
}
|
|
|
|
/* there is no left delimiting key */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* uget_rkey is utils clone of stree.c/get_rkey */
|
|
struct key * uget_rkey (struct path * path)
|
|
{
|
|
int pos, offset = path->path_length;
|
|
struct buffer_head * bh;
|
|
|
|
if (offset < FIRST_PATH_ELEMENT_OFFSET)
|
|
die ("uget_rkey: illegal offset in the path (%d)", offset);
|
|
|
|
while (offset-- > FIRST_PATH_ELEMENT_OFFSET) {
|
|
if (! buffer_uptodate (PATH_OFFSET_PBUFFER (path, offset)))
|
|
die ("uget_rkey: parent is not uptodate");
|
|
|
|
/* Parent at the path is not in the tree now. */
|
|
if (! B_IS_IN_TREE (bh = PATH_OFFSET_PBUFFER (path, offset)))
|
|
die ("uget_rkey: buffer on the path is not in tree");
|
|
|
|
/* Check whether position in the parrent is correct. */
|
|
if ((pos = PATH_OFFSET_POSITION (path, offset)) > B_NR_ITEMS (bh))
|
|
die ("uget_rkey: invalid position (%d) in the path", pos);
|
|
|
|
/* Check whether parent at the path really points to the child. */
|
|
if (B_N_CHILD_NUM (bh, pos) != PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr)
|
|
die ("uget_rkey: invalid block number (%d). Must be %d",
|
|
B_N_CHILD_NUM (bh, pos), PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr);
|
|
|
|
/* Return delimiting key if position in the parent is not the last one. */
|
|
if (pos != B_NR_ITEMS (bh))
|
|
return B_N_PDELIM_KEY(bh, pos);
|
|
}
|
|
|
|
/* there is no right delimiting key */
|
|
return 0;
|
|
}
|
|
|
|
|
|
inline int ubin_search (void * key, void * base, int num, int width, __u32 *ppos, comp_function_t comp_func)
|
|
{
|
|
__u32 rbound, lbound, j;
|
|
|
|
lbound = 0;
|
|
|
|
if (num == 0){
|
|
*ppos = 0;
|
|
return ITEM_NOT_FOUND;
|
|
}
|
|
|
|
rbound = num - 1;
|
|
|
|
for (j = (rbound + lbound) / 2; lbound <= rbound; j = (rbound + lbound) / 2) {
|
|
switch (comp_func ((void *)((char *)base + j * width), key ) ) {
|
|
case -1:/* second is greater */
|
|
lbound = j + 1;
|
|
continue;
|
|
|
|
case 1: /* first is greater */
|
|
if (j == 0){
|
|
*ppos = lbound;
|
|
return ITEM_NOT_FOUND;
|
|
}
|
|
rbound = j - 1;
|
|
continue;
|
|
|
|
case 0:
|
|
*ppos = j;
|
|
return ITEM_FOUND;
|
|
}
|
|
}
|
|
|
|
*ppos = lbound;
|
|
return ITEM_NOT_FOUND;
|
|
}
|
|
|
|
|
|
/* this searches in tree through items */
|
|
int usearch_by_key (struct super_block * s, struct key * key, struct path * path)
|
|
{
|
|
struct buffer_head * bh;
|
|
unsigned long block = SB_ROOT_BLOCK (s);
|
|
struct path_element * curr;
|
|
int retval;
|
|
|
|
path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
|
|
while (1) {
|
|
curr = PATH_OFFSET_PELEMENT (path, ++ path->path_length);
|
|
bh = curr->pe_buffer = bread (s->s_dev, block, s->s_blocksize);
|
|
if (bh == 0)
|
|
reiserfs_panic ("usearch_by_key: unable to read %lu block on device 0x%x\n",block, s->s_dev);
|
|
retval = ubin_search (key, B_N_PKEY (bh, 0), B_NR_ITEMS (bh),
|
|
is_leaf_node (bh) ? IH_SIZE : KEY_SIZE, &(curr->pe_position), comp_keys);
|
|
if (retval == ITEM_FOUND) {
|
|
/* key found, return if this is leaf level */
|
|
if (is_leaf_node (bh)) {
|
|
path->pos_in_item = 0;
|
|
return ITEM_FOUND;
|
|
}
|
|
curr->pe_position ++;
|
|
} else {
|
|
/* key not found in the node */
|
|
if (is_leaf_node (bh))
|
|
return ITEM_NOT_FOUND;
|
|
}
|
|
block = B_N_CHILD_NUM (bh, curr->pe_position);
|
|
}
|
|
die ("search_by_key: you can not get here");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* key is key of directory entry. This searches in tree through items and in
|
|
the found directory item as well */
|
|
int usearch_by_entry_key (struct super_block * s, struct key * key, struct path * path)
|
|
{
|
|
struct buffer_head * bh;
|
|
struct item_head * ih;
|
|
struct key tmpkey;
|
|
|
|
if (usearch_by_key (s, key, path) == ITEM_FOUND) {
|
|
/* entry found */
|
|
path->pos_in_item = 0;
|
|
return POSITION_FOUND;
|
|
}
|
|
|
|
bh = PATH_PLAST_BUFFER (path);
|
|
|
|
if (PATH_LAST_POSITION (path) == 0) {
|
|
/* previous item does not exist, that means we are in leftmost leaf of
|
|
the tree */
|
|
if (uget_lkey (path) != 0)
|
|
die ("search_by_entry_key: invalid position after search_by_key");
|
|
|
|
if (not_of_one_file (B_N_PKEY (bh, 0), key)) {
|
|
path->pos_in_item = 0;
|
|
return DIRECTORY_NOT_FOUND;
|
|
}
|
|
|
|
if (!is_direntry_ih (get_ih (path))) {
|
|
fsck_progress ("search_by_entry_key: directory expected to have this key %K\n", key);
|
|
return REGULAR_FILE_FOUND;
|
|
}
|
|
|
|
/* position for name insertion is found */
|
|
path->pos_in_item = 0;
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
|
|
/* take previous item */
|
|
PATH_LAST_POSITION (path) --;
|
|
ih = PATH_PITEM_HEAD (path);
|
|
if (not_of_one_file (&(ih->ih_key), key) || !is_direntry_ih(ih)) {
|
|
/* previous item belongs to another object or is stat data, check next
|
|
item */
|
|
|
|
PATH_LAST_POSITION (path) ++;
|
|
if (PATH_LAST_POSITION (path) < B_NR_ITEMS (bh))
|
|
{
|
|
/* found item is not last item of the node */
|
|
struct item_head * next_ih = B_N_PITEM_HEAD (bh, PATH_LAST_POSITION (path));
|
|
|
|
if (not_of_one_file (&(next_ih->ih_key), key))
|
|
{
|
|
path->pos_in_item = 0;
|
|
return DIRECTORY_NOT_FOUND;
|
|
}
|
|
if (!is_direntry_ih(next_ih))
|
|
{
|
|
/* there is an item in the tree, but it is not a directory item */
|
|
reiserfs_warning (stderr, "search_by_entry_key: directory expected to have this key %k\n",
|
|
key);
|
|
return REGULAR_FILE_FOUND;
|
|
}
|
|
} else {
|
|
/* found item is last item of the node */
|
|
struct key * next_key = uget_rkey (path);
|
|
|
|
if (next_key == 0 || not_of_one_file (next_key, key))
|
|
{
|
|
/* there is not any part of such directory in the tree */
|
|
path->pos_in_item = 0;
|
|
return DIRECTORY_NOT_FOUND;
|
|
}
|
|
|
|
if (!is_direntry_key (next_key))
|
|
{
|
|
/* there is an item in the tree, but it is not a directory item */
|
|
fsck_progress ("search_by_entry_key: directory expected to have this key %k\n",
|
|
key);
|
|
return REGULAR_FILE_FOUND;
|
|
}
|
|
|
|
// we got right delimiting key - search for it - the entry will be
|
|
// pasted in position 0
|
|
copy_key (&tmpkey, next_key);
|
|
pathrelse (path);
|
|
if (usearch_by_key (s, &tmpkey, path) != ITEM_FOUND || PATH_LAST_POSITION (path) != 0)
|
|
die ("search_by_entry_key: item not found by corresponding delimiting key");
|
|
}
|
|
|
|
/* next item is the part of this directory */
|
|
path->pos_in_item = 0;
|
|
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
|
|
/* previous item is part of desired directory */
|
|
if (ubin_search (&(key->u.k_offset_v1.k_offset), B_I_DEH (bh, ih), ih_entry_count (ih),
|
|
DEH_SIZE, &(path->pos_in_item), comp_dir_entries) == ITEM_FOUND)
|
|
return POSITION_FOUND;
|
|
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
|
|
|
|
/* key is key of byte in the regular file. This searches in tree
|
|
through items and in the found item as well */
|
|
int usearch_by_position (struct super_block * s, struct key * key, int version, struct path * path)
|
|
{
|
|
struct buffer_head * bh;
|
|
struct item_head * ih;
|
|
|
|
if (usearch_by_key (s, key, path) == ITEM_FOUND)
|
|
{
|
|
ih = PATH_PITEM_HEAD (path);
|
|
|
|
if (!is_direct_ih(ih) && !is_indirect_ih(ih))
|
|
return DIRECTORY_FOUND;
|
|
path->pos_in_item = 0;
|
|
return POSITION_FOUND;
|
|
}
|
|
|
|
bh = PATH_PLAST_BUFFER (path);
|
|
ih = PATH_PITEM_HEAD (path);
|
|
|
|
|
|
if ( (PATH_LAST_POSITION(path) < B_NR_ITEMS (bh)) &&
|
|
!not_of_one_file (&ih->ih_key, key) &&
|
|
(get_offset(&ih->ih_key) == get_offset(key)) )
|
|
{
|
|
|
|
if (!is_direct_ih(ih) && !is_indirect_ih(ih))
|
|
return DIRECTORY_FOUND;
|
|
path->pos_in_item = 0;
|
|
|
|
|
|
return POSITION_FOUND;
|
|
}
|
|
|
|
if (PATH_LAST_POSITION (path) == 0) {
|
|
/* previous item does not exist, that means we are in leftmost leaf of the tree */
|
|
if (!not_of_one_file (B_N_PKEY (bh, 0), key)) {
|
|
if (!is_direct_ih(ih) && !is_indirect_ih (ih))
|
|
return DIRECTORY_FOUND;
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
return FILE_NOT_FOUND;
|
|
}
|
|
|
|
|
|
/* take previous item */
|
|
PATH_LAST_POSITION (path) --;
|
|
ih = PATH_PITEM_HEAD (path);
|
|
|
|
if (not_of_one_file (&ih->ih_key, key) || is_stat_data_ih(ih)) {
|
|
struct key * next_key;
|
|
|
|
/* previous item belongs to another object or is a stat data, check next item */
|
|
PATH_LAST_POSITION (path) ++;
|
|
if (PATH_LAST_POSITION (path) < B_NR_ITEMS (PATH_PLAST_BUFFER (path)))
|
|
/* next key is in the same node */
|
|
next_key = B_N_PKEY (PATH_PLAST_BUFFER (path), PATH_LAST_POSITION (path));
|
|
else
|
|
next_key = uget_rkey (path);
|
|
if (next_key == 0 || not_of_one_file (next_key, key)) {
|
|
/* there is no any part of such file in the tree */
|
|
path->pos_in_item = 0;
|
|
return FILE_NOT_FOUND;
|
|
}
|
|
|
|
if (is_direntry_key (next_key)) {
|
|
fsck_log ("\nusearch_by_position: looking for %k found a directory with the same key\n", next_key);
|
|
return DIRECTORY_FOUND;
|
|
}
|
|
/* next item is the part of this file */
|
|
path->pos_in_item = 0;
|
|
if ( get_offset(next_key) == get_offset(key) ) {
|
|
pathrelse(path);
|
|
if (usearch_by_key (s, next_key, path) != ITEM_FOUND) {
|
|
reiserfs_panic ("usearch_by_position: keys must be equals %k %k",
|
|
next_key, &PATH_PITEM_HEAD (path)->ih_key);
|
|
}
|
|
return POSITION_FOUND;
|
|
}
|
|
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
|
|
if (is_direntry_ih(ih)) {
|
|
return DIRECTORY_FOUND;
|
|
}
|
|
if (is_stat_data_ih(ih)) {
|
|
PATH_LAST_POSITION (path) ++;
|
|
return FILE_NOT_FOUND;
|
|
}
|
|
|
|
/* previous item is part of desired file */
|
|
|
|
|
|
//if (is_key_in_item (bh,ih,key,bh->b_size)) {
|
|
if (I_K_KEY_IN_ITEM (ih, key, bh->b_size)) {
|
|
path->pos_in_item = get_offset (key) - get_offset (&ih->ih_key);
|
|
if (is_indirect_ih (ih) )
|
|
path->pos_in_item /= bh->b_size;
|
|
return POSITION_FOUND;
|
|
}
|
|
|
|
path->pos_in_item = is_indirect_ih (ih) ? I_UNFM_NUM (ih) : ih_item_len (ih);
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
|
|
|
|
static unsigned long first_child (struct buffer_head * bh)
|
|
{
|
|
return child_block_number (bh, 0);
|
|
}
|
|
|
|
#if 0
|
|
static unsigned long last_child (struct buffer_head * bh)
|
|
{
|
|
return child_block_number (bh, node_item_number (bh));
|
|
}
|
|
#endif
|
|
|
|
static unsigned long get_child (int pos, struct buffer_head * parent)
|
|
{
|
|
if (pos == -1)
|
|
return -1;
|
|
|
|
if (pos > B_NR_ITEMS (parent))
|
|
die ("get_child: no child found, should not happen: %d of %d", pos, B_NR_ITEMS (parent));
|
|
return child_block_number (parent, pos);
|
|
|
|
/*
|
|
for (i = 0; i < B_NR_ITEMS (parent); i ++)
|
|
{
|
|
if (child_block_number (parent, i) == block)
|
|
return child_block_number (parent, i + 1);
|
|
}
|
|
die ("next_child: no child found: should not happen");
|
|
return 0;
|
|
*/
|
|
}
|
|
|
|
|
|
static void print (int cur, int total)
|
|
{
|
|
printf ("/%3d (of %3d)", cur, total);fflush (stdout);
|
|
}
|
|
|
|
|
|
/* erase /XXX(of XXX) */
|
|
static void erase (void)
|
|
{
|
|
printf ("\b\b\b\b\b\b\b\b\b\b\b\b\b");
|
|
printf (" ");
|
|
printf ("\b\b\b\b\b\b\b\b\b\b\b\b\b");
|
|
fflush (stdout);
|
|
}
|
|
|
|
#if 0
|
|
void pass_through_tree2 (struct super_block * s, do_after_read_t action1,
|
|
do_on_full_path_t action2)
|
|
{
|
|
struct buffer_head * path[MAX_HEIGHT] = {0,};
|
|
int total[MAX_HEIGHT] = {0,};
|
|
int cur[MAX_HEIGHT] = {0,};
|
|
int h = 0;
|
|
unsigned long block = SB_ROOT_BLOCK (s);
|
|
int del_p;
|
|
|
|
if (block >= SB_BLOCK_COUNT (s) || not_data_block (s, block))
|
|
return;
|
|
|
|
while ( 1 ) {
|
|
if (path[h])
|
|
die ("pass_through_tree: empty slot expected");
|
|
if (h)
|
|
print (cur[h - 1], total[h - 1]);
|
|
|
|
path[h] = bread (s->s_dev, block, s->s_blocksize);
|
|
get_child (-1, path[h]);
|
|
|
|
if (path[h] == 0)
|
|
reiserfs_warning ("pass_through_tree: unable to read %lu block on device 0x%x\n",
|
|
block, s->s_dev);
|
|
|
|
del_p = 0;
|
|
if (path[h] && action1) {
|
|
if (action1 (s, path, h)) {
|
|
;
|
|
#if 0
|
|
// something wrong with a buffer we just have read
|
|
if (opt_fsck_mode == FSCK_FAST_REBUILD){
|
|
//need to change the way we are going on
|
|
del_p = 1;
|
|
if (h == 0)
|
|
break;
|
|
} else {
|
|
;
|
|
//reiserfs_panic (s, "Run reiserfsck with --rebuild-tree\n");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (!path[h] || is_leaf_node (path[h]))
|
|
{
|
|
if (path[h] && action2) {
|
|
if (action2 (s, path, h)) {
|
|
;
|
|
#if 0
|
|
if (opt_fsck_mode == FSCK_FAST_REBUILD) {
|
|
//need to change the way we are going on
|
|
del_p = 1;
|
|
if (h == 0)
|
|
break;
|
|
} else {
|
|
;
|
|
//reiserfs_panic (s, "Run reiserfsck with --rebuild-tree\n");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (path[h])
|
|
brelse (path[h]);
|
|
if (h)
|
|
erase ();
|
|
|
|
while (h && (!path[h-1] || cur[h-1] == total[h-1] ))
|
|
{
|
|
path[h] = 0;
|
|
h --;
|
|
if (path[h])
|
|
brelse (path[h]);
|
|
if (h)
|
|
erase ();
|
|
}
|
|
|
|
if (h == 0) {
|
|
path[h] = 0;
|
|
break;
|
|
}
|
|
|
|
if (path[h])
|
|
cur[h - 1] ++;
|
|
if (del_p){
|
|
total[h-1]--;
|
|
del_p = 0;
|
|
}
|
|
block = get_child (cur[h - 1] - 1, path[h-1]);
|
|
path[h] = 0;
|
|
continue;
|
|
}
|
|
total[h] = B_NR_ITEMS (path[h]) + 1;
|
|
cur[h] = 1;
|
|
block = first_child (path[h]);
|
|
h ++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void pass_through_tree (struct super_block * s, do_after_read_t action1,
|
|
do_on_full_path_t action2)
|
|
{
|
|
struct buffer_head * path[MAX_HEIGHT] = {0,};
|
|
int total[MAX_HEIGHT] = {0,};
|
|
int cur[MAX_HEIGHT] = {0,};
|
|
int h = 0;
|
|
unsigned long block = SB_ROOT_BLOCK (s);
|
|
|
|
|
|
if (block >= SB_BLOCK_COUNT (s) || not_data_block (s, block)) {
|
|
fsck_progress ("\nBad root block %lu. (--rebuild-tree did not complete)\n", block);
|
|
return;
|
|
}
|
|
|
|
|
|
while ( 1 ) {
|
|
if (path[h])
|
|
die ("pass_through_tree: empty slot expected");
|
|
if (h)
|
|
print (cur[h - 1], total[h - 1]);
|
|
|
|
path[h] = bread (s->s_dev, block, s->s_blocksize);
|
|
if (path[h] == 0)
|
|
/* FIXME: handle case when read failed */
|
|
die ("pass_through_tree: unable to read %lu block on device 0x%x\n",
|
|
block, s->s_dev);
|
|
|
|
if (action1)
|
|
action1 (s, path, h);
|
|
|
|
if (is_leaf_node (path[h])) {
|
|
if (action2)
|
|
action2 (s, path, h);
|
|
|
|
brelse (path[h]);
|
|
if (h)
|
|
erase ();
|
|
|
|
while (h && (cur[h-1] == total[h-1])) {
|
|
path[h] = 0;
|
|
h --;
|
|
brelse (path[h]);
|
|
if (h)
|
|
erase ();
|
|
}
|
|
|
|
if (h == 0) {
|
|
path[h] = 0;
|
|
break;
|
|
}
|
|
|
|
block = get_child (cur[h - 1], path[h-1]);
|
|
cur[h - 1] ++;
|
|
path[h] = 0;
|
|
continue;
|
|
}
|
|
total[h] = B_NR_ITEMS (path[h]) + 1;
|
|
cur[h] = 1;
|
|
block = first_child (path[h]);
|
|
h ++;
|
|
}
|
|
}
|