Files
reiserfsprogs/fsck/ustree.c
T

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 ++;
}
}