60a698826d
i386). Source code is under the GNU GPL license. Approved by: mux (mentor)
761 lines
22 KiB
C
761 lines
22 KiB
C
/*-
|
|
* Copyright 2000 Hans Reiser
|
|
* See README for licensing and copyright details
|
|
*
|
|
* Ported to FreeBSD by Jean-Sébastien Pédron <jspedron@club-internet.fr>
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <gnu/reiserfs/reiserfs_fs.h>
|
|
|
|
/* Minimal possible key. It is never in the tree. */
|
|
const struct key MIN_KEY = {
|
|
0,
|
|
0,
|
|
{ {0, 0}, }
|
|
};
|
|
|
|
/* Maximal possible key. It is never in the tree. */
|
|
const struct key MAX_KEY = {
|
|
0xffffffff,
|
|
0xffffffff,
|
|
{ {0xffffffff, 0xffffffff }, }
|
|
};
|
|
|
|
/* Does the buffer contain a disk block which is in the tree. */
|
|
inline int
|
|
B_IS_IN_TREE(const struct buf *p_s_bp)
|
|
{
|
|
|
|
return (B_LEVEL(p_s_bp) != FREE_LEVEL);
|
|
}
|
|
|
|
/* To gets item head in le form */
|
|
inline void
|
|
copy_item_head(struct item_head *p_v_to, const struct item_head *p_v_from)
|
|
{
|
|
|
|
memcpy(p_v_to, p_v_from, IH_SIZE);
|
|
}
|
|
|
|
/*
|
|
* k1 is pointer to on-disk structure which is stored in little-endian
|
|
* form. k2 is pointer to cpu variable. For key of items of the same
|
|
* object this returns 0.
|
|
* Returns: -1 if key1 < key2, 0 if key1 == key2 or 1 if key1 > key2
|
|
*/
|
|
/*inline*/ int
|
|
comp_short_keys(const struct key *le_key, const struct cpu_key *cpu_key)
|
|
{
|
|
const uint32_t *p_s_le_u32, *p_s_cpu_u32;
|
|
int n_key_length = REISERFS_SHORT_KEY_LEN;
|
|
|
|
p_s_le_u32 = (const uint32_t *)le_key;
|
|
p_s_cpu_u32 = (const uint32_t *)&cpu_key->on_disk_key;
|
|
for(; n_key_length--; ++p_s_le_u32, ++p_s_cpu_u32) {
|
|
if (le32toh(*p_s_le_u32) < *p_s_cpu_u32)
|
|
return (-1);
|
|
if (le32toh(*p_s_le_u32) > *p_s_cpu_u32)
|
|
return (1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* k1 is pointer to on-disk structure which is stored in little-endian
|
|
* form. k2 is pointer to cpu variable. Compare keys using all 4 key
|
|
* fields.
|
|
* Returns: -1 if key1 < key2, 0 if key1 = key2 or 1 if key1 > key2
|
|
*/
|
|
/*inline*/ int
|
|
comp_keys(const struct key *le_key, const struct cpu_key *cpu_key)
|
|
{
|
|
int retval;
|
|
|
|
retval = comp_short_keys(le_key, cpu_key);
|
|
if (retval)
|
|
return retval;
|
|
|
|
if (le_key_k_offset(le_key_version(le_key), le_key) <
|
|
cpu_key_k_offset(cpu_key))
|
|
return (-1);
|
|
if (le_key_k_offset(le_key_version(le_key), le_key) >
|
|
cpu_key_k_offset(cpu_key))
|
|
return (1);
|
|
|
|
if (cpu_key->key_length == 3)
|
|
return (0);
|
|
|
|
/* This part is needed only when tail conversion is in progress */
|
|
if (le_key_k_type(le_key_version(le_key), le_key) <
|
|
cpu_key_k_type(cpu_key))
|
|
return (-1);
|
|
|
|
if (le_key_k_type(le_key_version(le_key), le_key) >
|
|
cpu_key_k_type(cpu_key))
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Release all buffers in the path. */
|
|
void
|
|
pathrelse(struct path *p_s_search_path)
|
|
{
|
|
struct buf *bp;
|
|
int n_path_offset = p_s_search_path->path_length;
|
|
|
|
while (n_path_offset > ILLEGAL_PATH_ELEMENT_OFFSET) {
|
|
bp = PATH_OFFSET_PBUFFER(p_s_search_path, n_path_offset--);
|
|
free(bp->b_data, M_REISERFSPATH);
|
|
free(bp, M_REISERFSPATH);
|
|
}
|
|
|
|
p_s_search_path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
|
|
}
|
|
|
|
/*
|
|
* This does not say which one is bigger, it only returns 1 if keys
|
|
* are not equal, 0 otherwise
|
|
*/
|
|
inline int
|
|
comp_le_keys(const struct key *k1, const struct key *k2)
|
|
{
|
|
|
|
return (memcmp(k1, k2, sizeof(struct key)));
|
|
}
|
|
|
|
/*
|
|
* Binary search toolkit function. Search for an item in the array by
|
|
* the item key.
|
|
* Returns: 1 if found, 0 if not found;
|
|
* *p_n_pos = number of the searched element if found, else the
|
|
* number of the first element that is larger than p_v_key.
|
|
*/
|
|
/*
|
|
* For those not familiar with binary search: n_lbound is the leftmost
|
|
* item that it could be, n_rbound the rightmost item that it could be.
|
|
* We examine the item halfway between n_lbound and n_rbound, and that
|
|
* tells us either that we can increase n_lbound, or decrease n_rbound,
|
|
* or that we have found it, or if n_lbound <= n_rbound that there are
|
|
* no possible items, and we have not found it. With each examination we
|
|
* cut the number of possible items it could be by one more than half
|
|
* rounded down, or we find it.
|
|
*/
|
|
inline int
|
|
bin_search(const void *p_v_key, /* Key to search for. */
|
|
const void *p_v_base, /* First item in the array. */
|
|
int p_n_num, /* Number of items in the array. */
|
|
int p_n_width, /* Item size in the array. searched. Lest the
|
|
reader be confused, note that this is crafted
|
|
as a general function, and when it is applied
|
|
specifically to the array of item headers in
|
|
a node, p_n_width is actually the item header
|
|
size not the item size. */
|
|
int *p_n_pos) /* Number of the searched for element. */
|
|
{
|
|
int n_rbound, n_lbound, n_j;
|
|
|
|
for (n_j = ((n_rbound = p_n_num - 1) + (n_lbound = 0)) / 2;
|
|
n_lbound <= n_rbound; n_j = (n_rbound + n_lbound) / 2) {
|
|
switch (COMP_KEYS((const struct key *)
|
|
((const char *)p_v_base + n_j * p_n_width),
|
|
(const struct cpu_key *)p_v_key)) {
|
|
case -1:
|
|
n_lbound = n_j + 1;
|
|
continue;
|
|
case 1:
|
|
n_rbound = n_j - 1;
|
|
continue;
|
|
case 0:
|
|
*p_n_pos = n_j;
|
|
return (ITEM_FOUND); /* Key found in the array. */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* bin_search did not find given key, it returns position of key,
|
|
* that is minimal and greater than the given one.
|
|
*/
|
|
*p_n_pos = n_lbound;
|
|
return (ITEM_NOT_FOUND);
|
|
}
|
|
|
|
/*
|
|
* Get delimiting key of the buffer by looking for it in the buffers in
|
|
* the path, starting from the bottom of the path, and going upwards. We
|
|
* must check the path's validity at each step. If the key is not in the
|
|
* path, there is no delimiting key in the tree (buffer is first or last
|
|
* buffer in tree), and in this case we return a special key, either
|
|
* MIN_KEY or MAX_KEY.
|
|
*/
|
|
inline const struct key *
|
|
get_lkey(const struct path *p_s_chk_path,
|
|
const struct reiserfs_sb_info *p_s_sbi)
|
|
{
|
|
struct buf *p_s_parent;
|
|
int n_position, n_path_offset = p_s_chk_path->path_length;
|
|
|
|
/* While not higher in path than first element. */
|
|
while (n_path_offset-- > FIRST_PATH_ELEMENT_OFFSET) {
|
|
/* Parent at the path is not in the tree now. */
|
|
if (!B_IS_IN_TREE(p_s_parent =
|
|
PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)))
|
|
return (&MAX_KEY);
|
|
|
|
/* Check whether position in the parent is correct. */
|
|
if ((n_position = PATH_OFFSET_POSITION(p_s_chk_path,
|
|
n_path_offset)) > B_NR_ITEMS(p_s_parent))
|
|
return (&MAX_KEY);
|
|
|
|
/*
|
|
* Check whether parent at the path really points to
|
|
* the child.
|
|
*/
|
|
if (B_N_CHILD_NUM(p_s_parent, n_position) !=
|
|
(PATH_OFFSET_PBUFFER(p_s_chk_path,
|
|
n_path_offset + 1)->b_blkno
|
|
/ btodb(p_s_sbi->s_blocksize)))
|
|
return (&MAX_KEY);
|
|
|
|
/*
|
|
* Return delimiting key if position in the parent is not
|
|
* equal to zero.
|
|
*/
|
|
if (n_position)
|
|
return (B_N_PDELIM_KEY(p_s_parent, n_position - 1));
|
|
}
|
|
|
|
/* Return MIN_KEY if we are in the root of the buffer tree. */
|
|
if ((PATH_OFFSET_PBUFFER(p_s_chk_path,
|
|
FIRST_PATH_ELEMENT_OFFSET)->b_blkno
|
|
/ btodb(p_s_sbi->s_blocksize)) == SB_ROOT_BLOCK(p_s_sbi))
|
|
return (&MIN_KEY);
|
|
|
|
return (&MAX_KEY);
|
|
}
|
|
|
|
/* Get delimiting key of the buffer at the path and its right neighbor. */
|
|
inline const struct key *
|
|
get_rkey(const struct path *p_s_chk_path,
|
|
const struct reiserfs_sb_info *p_s_sbi)
|
|
{
|
|
struct buf *p_s_parent;
|
|
int n_position, n_path_offset = p_s_chk_path->path_length;
|
|
|
|
while (n_path_offset-- > FIRST_PATH_ELEMENT_OFFSET) {
|
|
/* Parent at the path is not in the tree now. */
|
|
if (!B_IS_IN_TREE(p_s_parent =
|
|
PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)))
|
|
return (&MIN_KEY);
|
|
|
|
/* Check whether position in the parent is correct. */
|
|
if ((n_position = PATH_OFFSET_POSITION(p_s_chk_path,
|
|
n_path_offset)) >
|
|
B_NR_ITEMS(p_s_parent))
|
|
return (&MIN_KEY);
|
|
|
|
/*
|
|
* Check whether parent at the path really points to the
|
|
* child.
|
|
*/
|
|
if (B_N_CHILD_NUM(p_s_parent, n_position) !=
|
|
(PATH_OFFSET_PBUFFER(p_s_chk_path,
|
|
n_path_offset + 1)->b_blkno
|
|
/ btodb(p_s_sbi->s_blocksize)))
|
|
return (&MIN_KEY);
|
|
|
|
/*
|
|
* Return delimiting key if position in the parent is not
|
|
* the last one.
|
|
*/
|
|
if (n_position != B_NR_ITEMS(p_s_parent))
|
|
return (B_N_PDELIM_KEY(p_s_parent, n_position));
|
|
}
|
|
|
|
/* Return MAX_KEY if we are in the root of the buffer tree. */
|
|
if ((PATH_OFFSET_PBUFFER(p_s_chk_path,
|
|
FIRST_PATH_ELEMENT_OFFSET)->b_blkno
|
|
/ btodb(p_s_sbi->s_blocksize)) == SB_ROOT_BLOCK(p_s_sbi))
|
|
return (&MAX_KEY);
|
|
|
|
return (&MIN_KEY);
|
|
}
|
|
|
|
int
|
|
reiserfs_check_path(struct path *p)
|
|
{
|
|
|
|
if (p->path_length != ILLEGAL_PATH_ELEMENT_OFFSET)
|
|
reiserfs_log(LOG_WARNING, "path not properly relsed\n");
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Check whether a key is contained in the tree rooted from a buffer at
|
|
* a path. This works by looking at the left and right delimiting keys
|
|
* for the buffer in the last path_element in the path. These delimiting
|
|
* keys are stored at least one level above that buffer in the tree.
|
|
* If the buffer is the first or last node in the tree order then one
|
|
* of the delimiting keys may be absent, and in this case get_lkey and
|
|
* get_rkey return a special key which is MIN_KEY or MAX_KEY.
|
|
*/
|
|
static inline int
|
|
key_in_buffer(
|
|
struct path *p_s_chk_path, /* Path which should be checked. */
|
|
const struct cpu_key *p_s_key, /* Key which should be checked. */
|
|
struct reiserfs_sb_info *p_s_sbi) /* Super block pointer. */
|
|
{
|
|
|
|
if (COMP_KEYS(get_lkey(p_s_chk_path, p_s_sbi), p_s_key) == 1)
|
|
/* left delimiting key is bigger, that the key we look for */
|
|
return (0);
|
|
|
|
if (COMP_KEYS(get_rkey(p_s_chk_path, p_s_sbi), p_s_key) != 1)
|
|
/* p_s_key must be less than right delimitiing key */
|
|
return (0);
|
|
|
|
return (1);
|
|
}
|
|
|
|
#if 0
|
|
/* XXX Il ne semble pas y avoir de compteur de référence dans struct buf */
|
|
inline void
|
|
decrement_bcount(struct buf *p_s_bp)
|
|
{
|
|
|
|
if (p_s_bp) {
|
|
if (atomic_read(&(p_s_bp->b_count))) {
|
|
put_bh(p_s_bp);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Decrement b_count field of the all buffers in the path. */
|
|
void
|
|
decrement_counters_in_path(struct path *p_s_search_path)
|
|
{
|
|
|
|
pathrelse(p_s_search_path);
|
|
#if 0
|
|
int n_path_offset = p_s_search_path->path_length;
|
|
|
|
while (n_path_offset > ILLEGAL_PATH_ELEMENT_OFFSET) {
|
|
struct buf *bp;
|
|
|
|
bp = PATH_OFFSET_PBUFFER(p_s_search_path, n_path_offset--);
|
|
decrement_bcount(bp);
|
|
}
|
|
|
|
p_s_search_path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
is_leaf(char *buf, int blocksize, struct buf *bp)
|
|
{
|
|
struct item_head *ih;
|
|
struct block_head *blkh;
|
|
int used_space, prev_location, i, nr;
|
|
|
|
blkh = (struct block_head *)buf;
|
|
if (blkh_level(blkh) != DISK_LEAF_NODE_LEVEL) {
|
|
reiserfs_log(LOG_WARNING, "this should be caught earlier");
|
|
return (0);
|
|
}
|
|
|
|
nr = blkh_nr_item(blkh);
|
|
if (nr < 1 || nr >
|
|
((blocksize - BLKH_SIZE) / (IH_SIZE + MIN_ITEM_LEN))) {
|
|
/* Item number is too big or too small */
|
|
reiserfs_log(LOG_WARNING, "nr_item seems wrong\n");
|
|
return (0);
|
|
}
|
|
|
|
ih = (struct item_head *)(buf + BLKH_SIZE) + nr - 1;
|
|
used_space = BLKH_SIZE + IH_SIZE * nr + (blocksize - ih_location(ih));
|
|
if (used_space != blocksize - blkh_free_space(blkh)) {
|
|
/*
|
|
* Free space does not match to calculated amount of
|
|
* use space
|
|
*/
|
|
reiserfs_log(LOG_WARNING, "free space seems wrong\n");
|
|
return (0);
|
|
}
|
|
|
|
/* FIXME: it is_leaf will hit performance too much - we may have
|
|
* return 1 here */
|
|
|
|
/* Check tables of item heads */
|
|
ih = (struct item_head *)(buf + BLKH_SIZE);
|
|
prev_location = blocksize;
|
|
for (i = 0; i < nr; i++, ih++) {
|
|
if (le_ih_k_type(ih) == TYPE_ANY) {
|
|
reiserfs_log(LOG_WARNING,
|
|
"wrong item type for item\n");
|
|
return (0);
|
|
}
|
|
if (ih_location(ih) >= blocksize ||
|
|
ih_location(ih) < IH_SIZE * nr) {
|
|
reiserfs_log(LOG_WARNING,
|
|
"item location seems wrong\n");
|
|
return (0);
|
|
}
|
|
if (ih_item_len(ih) < 1 ||
|
|
ih_item_len(ih) > MAX_ITEM_LEN(blocksize)) {
|
|
reiserfs_log(LOG_WARNING, "item length seems wrong\n");
|
|
return (0);
|
|
}
|
|
if (prev_location - ih_location(ih) != ih_item_len(ih)) {
|
|
reiserfs_log(LOG_WARNING,
|
|
"item location seems wrong (second one)\n");
|
|
return (0);
|
|
}
|
|
prev_location = ih_location(ih);
|
|
}
|
|
|
|
/* One may imagine much more checks */
|
|
return 1;
|
|
}
|
|
|
|
/* Returns 1 if buf looks like an internal node, 0 otherwise */
|
|
static int
|
|
is_internal(char *buf, int blocksize, struct buf *bp)
|
|
{
|
|
int nr, used_space;
|
|
struct block_head *blkh;
|
|
|
|
blkh = (struct block_head *)buf;
|
|
nr = blkh_level(blkh);
|
|
if (nr <= DISK_LEAF_NODE_LEVEL || nr > MAX_HEIGHT) {
|
|
/* This level is not possible for internal nodes */
|
|
reiserfs_log(LOG_WARNING, "this should be caught earlier\n");
|
|
return (0);
|
|
}
|
|
|
|
nr = blkh_nr_item(blkh);
|
|
if (nr > (blocksize - BLKH_SIZE - DC_SIZE) / (KEY_SIZE + DC_SIZE)) {
|
|
/*
|
|
* For internal which is not root we might check min
|
|
* number of keys
|
|
*/
|
|
reiserfs_log(LOG_WARNING, "number of key seems wrong\n");
|
|
return (0);
|
|
}
|
|
|
|
used_space = BLKH_SIZE + KEY_SIZE * nr + DC_SIZE * (nr + 1);
|
|
if (used_space != blocksize - blkh_free_space(blkh)) {
|
|
reiserfs_log(LOG_WARNING,
|
|
"is_internal: free space seems wrong\n");
|
|
return (0);
|
|
}
|
|
|
|
/* One may imagine much more checks */
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Make sure that bh contains formatted node of reiserfs tree of
|
|
* 'level'-th level
|
|
*/
|
|
static int
|
|
is_tree_node(struct buf *bp, int level)
|
|
{
|
|
if (B_LEVEL(bp) != level) {
|
|
reiserfs_log(LOG_WARNING,
|
|
"node level (%d) doesn't match to the "
|
|
"expected one (%d)\n", B_LEVEL (bp), level);
|
|
return (0);
|
|
}
|
|
|
|
if (level == DISK_LEAF_NODE_LEVEL)
|
|
return (is_leaf(bp->b_data, bp->b_bcount, bp));
|
|
|
|
return (is_internal(bp->b_data, bp->b_bcount, bp));
|
|
}
|
|
|
|
int
|
|
search_by_key(struct reiserfs_sb_info *p_s_sbi,
|
|
const struct cpu_key * p_s_key, /* Key to search. */
|
|
struct path * p_s_search_path, /* This structure was allocated and
|
|
initialized by the calling function.
|
|
It is filled up by this function. */
|
|
int n_stop_level) /* How far down the tree to search. To
|
|
stop at leaf level - set to
|
|
DISK_LEAF_NODE_LEVEL */
|
|
{
|
|
int error;
|
|
int n_node_level, n_retval;
|
|
int n_block_number, expected_level, fs_gen;
|
|
struct path_element *p_s_last_element;
|
|
struct buf *p_s_bp, *tmp_bp;
|
|
|
|
/*
|
|
* As we add each node to a path we increase its count. This means that
|
|
* we must be careful to release all nodes in a path before we either
|
|
* discard the path struct or re-use the path struct, as we do here.
|
|
*/
|
|
decrement_counters_in_path(p_s_search_path);
|
|
|
|
/*
|
|
* With each iteration of this loop we search through the items in the
|
|
* current node, and calculate the next current node(next path element)
|
|
* for the next iteration of this loop...
|
|
*/
|
|
n_block_number = SB_ROOT_BLOCK(p_s_sbi);
|
|
expected_level = -1;
|
|
|
|
reiserfs_log(LOG_DEBUG, "root block: #%d\n", n_block_number);
|
|
|
|
while (1) {
|
|
/* Prep path to have another element added to it. */
|
|
reiserfs_log(LOG_DEBUG, "path element #%d\n",
|
|
p_s_search_path->path_length);
|
|
p_s_last_element = PATH_OFFSET_PELEMENT(p_s_search_path,
|
|
++p_s_search_path->path_length);
|
|
fs_gen = get_generation(p_s_sbi);
|
|
|
|
/*
|
|
* Read the next tree node, and set the last element in the
|
|
* path to have a pointer to it.
|
|
*/
|
|
reiserfs_log(LOG_DEBUG, "reading block #%d\n",
|
|
n_block_number);
|
|
if ((error = bread(p_s_sbi->s_devvp,
|
|
n_block_number * btodb(p_s_sbi->s_blocksize),
|
|
p_s_sbi->s_blocksize, NOCRED, &tmp_bp)) != 0) {
|
|
reiserfs_log(LOG_DEBUG, "error reading block\n");
|
|
p_s_search_path->path_length--;
|
|
pathrelse(p_s_search_path);
|
|
return (IO_ERROR);
|
|
}
|
|
reiserfs_log(LOG_DEBUG, "blkno = %ju, lblkno = %ju\n",
|
|
(intmax_t)tmp_bp->b_blkno, (intmax_t)tmp_bp->b_lblkno);
|
|
|
|
/*
|
|
* As i didn't found a way to handle the lock correctly,
|
|
* i copy the data into a fake buffer
|
|
*/
|
|
reiserfs_log(LOG_DEBUG, "allocating p_s_bp\n");
|
|
p_s_bp = malloc(sizeof *p_s_bp, M_REISERFSPATH, M_WAITOK);
|
|
if (!p_s_bp) {
|
|
reiserfs_log(LOG_DEBUG, "error allocating memory\n");
|
|
p_s_search_path->path_length--;
|
|
pathrelse(p_s_search_path);
|
|
brelse(tmp_bp);
|
|
return (IO_ERROR);
|
|
}
|
|
reiserfs_log(LOG_DEBUG, "copying struct buf\n");
|
|
bcopy(tmp_bp, p_s_bp, sizeof(struct buf));
|
|
|
|
reiserfs_log(LOG_DEBUG, "allocating p_s_bp->b_data\n");
|
|
p_s_bp->b_data = malloc(p_s_sbi->s_blocksize,
|
|
M_REISERFSPATH, M_WAITOK);
|
|
if (!p_s_bp->b_data) {
|
|
reiserfs_log(LOG_DEBUG, "error allocating memory\n");
|
|
p_s_search_path->path_length--;
|
|
pathrelse(p_s_search_path);
|
|
free(p_s_bp, M_REISERFSPATH);
|
|
brelse(tmp_bp);
|
|
return (IO_ERROR);
|
|
}
|
|
reiserfs_log(LOG_DEBUG, "copying buffer data\n");
|
|
bcopy(tmp_bp->b_data, p_s_bp->b_data, p_s_sbi->s_blocksize);
|
|
brelse(tmp_bp);
|
|
tmp_bp = NULL;
|
|
|
|
reiserfs_log(LOG_DEBUG, "...done\n");
|
|
p_s_last_element->pe_buffer = p_s_bp;
|
|
|
|
if (expected_level == -1)
|
|
expected_level = SB_TREE_HEIGHT(p_s_sbi);
|
|
expected_level--;
|
|
reiserfs_log(LOG_DEBUG, "expected level: %d (%d)\n",
|
|
expected_level, SB_TREE_HEIGHT(p_s_sbi));
|
|
|
|
/* XXX */
|
|
/*
|
|
* It is possible that schedule occurred. We must check
|
|
* whether the key to search is still in the tree rooted
|
|
* from the current buffer. If not then repeat search
|
|
* from the root.
|
|
*/
|
|
if (fs_changed(fs_gen, p_s_sbi) &&
|
|
(!B_IS_IN_TREE(p_s_bp) ||
|
|
B_LEVEL(p_s_bp) != expected_level ||
|
|
!key_in_buffer(p_s_search_path, p_s_key, p_s_sbi))) {
|
|
reiserfs_log(LOG_DEBUG,
|
|
"the key isn't in the tree anymore\n");
|
|
decrement_counters_in_path(p_s_search_path);
|
|
|
|
/*
|
|
* Get the root block number so that we can repeat
|
|
* the search starting from the root.
|
|
*/
|
|
n_block_number = SB_ROOT_BLOCK(p_s_sbi);
|
|
expected_level = -1;
|
|
|
|
/* Repeat search from the root */
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Make sure, that the node contents look like a node of
|
|
* certain level
|
|
*/
|
|
if (!is_tree_node(p_s_bp, expected_level)) {
|
|
reiserfs_log(LOG_WARNING,
|
|
"invalid format found in block %ju. Fsck?",
|
|
(intmax_t)p_s_bp->b_blkno);
|
|
pathrelse (p_s_search_path);
|
|
return (IO_ERROR);
|
|
}
|
|
|
|
/* Ok, we have acquired next formatted node in the tree */
|
|
n_node_level = B_LEVEL(p_s_bp);
|
|
reiserfs_log(LOG_DEBUG, "block info:\n");
|
|
reiserfs_log(LOG_DEBUG, " node level: %d\n",
|
|
n_node_level);
|
|
reiserfs_log(LOG_DEBUG, " nb of items: %d\n",
|
|
B_NR_ITEMS(p_s_bp));
|
|
reiserfs_log(LOG_DEBUG, " free space: %d bytes\n",
|
|
B_FREE_SPACE(p_s_bp));
|
|
reiserfs_log(LOG_DEBUG, "bin_search with :\n"
|
|
" p_s_key = (objectid=%d, dirid=%d)\n"
|
|
" B_NR_ITEMS(p_s_bp) = %d\n"
|
|
" p_s_last_element->pe_position = %d (path_length = %d)\n",
|
|
p_s_key->on_disk_key.k_objectid,
|
|
p_s_key->on_disk_key.k_dir_id,
|
|
B_NR_ITEMS(p_s_bp),
|
|
p_s_last_element->pe_position,
|
|
p_s_search_path->path_length);
|
|
n_retval = bin_search(p_s_key, B_N_PITEM_HEAD(p_s_bp, 0),
|
|
B_NR_ITEMS(p_s_bp),
|
|
(n_node_level == DISK_LEAF_NODE_LEVEL) ? IH_SIZE : KEY_SIZE,
|
|
&(p_s_last_element->pe_position));
|
|
reiserfs_log(LOG_DEBUG, "bin_search result: %d\n",
|
|
n_retval);
|
|
if (n_node_level == n_stop_level) {
|
|
reiserfs_log(LOG_DEBUG, "stop level reached (%s)\n",
|
|
n_retval == ITEM_FOUND ? "found" : "not found");
|
|
return (n_retval);
|
|
}
|
|
|
|
/* We are not in the stop level */
|
|
if (n_retval == ITEM_FOUND)
|
|
/*
|
|
* Item has been found, so we choose the pointer
|
|
* which is to the right of the found one
|
|
*/
|
|
p_s_last_element->pe_position++;
|
|
|
|
/*
|
|
* If item was not found we choose the position which is
|
|
* to the left of the found item. This requires no code,
|
|
* bin_search did it already.
|
|
*/
|
|
|
|
/*
|
|
* So we have chosen a position in the current node which
|
|
* is an internal node. Now we calculate child block number
|
|
* by position in the node.
|
|
*/
|
|
n_block_number = B_N_CHILD_NUM(p_s_bp,
|
|
p_s_last_element->pe_position);
|
|
}
|
|
|
|
reiserfs_log(LOG_DEBUG, "done\n");
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Form the path to an item and position in this item which contains
|
|
* file byte defined by p_s_key. If there is no such item corresponding
|
|
* to the key, we point the path to the item with maximal key less than
|
|
* p_s_key, and *p_n_pos_in_item is set to one past the last entry/byte
|
|
* in the item. If searching for entry in a directory item, and it is
|
|
* not found, *p_n_pos_in_item is set to one entry more than the entry
|
|
* with maximal key which is less than the sought key.
|
|
*
|
|
* Note that if there is no entry in this same node which is one more,
|
|
* then we point to an imaginary entry. For direct items, the position
|
|
* is in units of bytes, for indirect items the position is in units
|
|
* of blocknr entries, for directory items the position is in units of
|
|
* directory entries.
|
|
*/
|
|
|
|
/* The function is NOT SCHEDULE-SAFE! */
|
|
int
|
|
search_for_position_by_key(struct reiserfs_sb_info *p_s_sbi,
|
|
const struct cpu_key *p_cpu_key, /* Key to search (cpu variable) */
|
|
struct path *p_s_search_path) /* Filled up by this function. */
|
|
{
|
|
int retval, n_blk_size;
|
|
off_t item_offset, offset;
|
|
struct item_head *p_le_ih; /* Pointer to on-disk structure */
|
|
struct reiserfs_dir_entry de;
|
|
|
|
/* If searching for directory entry. */
|
|
if (is_direntry_cpu_key(p_cpu_key))
|
|
return (search_by_entry_key(p_s_sbi, p_cpu_key,
|
|
p_s_search_path, &de));
|
|
|
|
/* If not searching for directory entry. */
|
|
|
|
/* If item is found. */
|
|
retval = search_item(p_s_sbi, p_cpu_key, p_s_search_path);
|
|
if (retval == IO_ERROR)
|
|
return (retval);
|
|
if (retval == ITEM_FOUND) {
|
|
if (ih_item_len(B_N_PITEM_HEAD(
|
|
PATH_PLAST_BUFFER(p_s_search_path),
|
|
PATH_LAST_POSITION(p_s_search_path))) == 0) {
|
|
reiserfs_log(LOG_WARNING, "item length equals zero\n");
|
|
}
|
|
|
|
pos_in_item(p_s_search_path) = 0;
|
|
return (POSITION_FOUND);
|
|
}
|
|
|
|
if (PATH_LAST_POSITION(p_s_search_path) == 0) {
|
|
reiserfs_log(LOG_WARNING, "position equals zero\n");
|
|
}
|
|
|
|
/* Item is not found. Set path to the previous item. */
|
|
p_le_ih = B_N_PITEM_HEAD(PATH_PLAST_BUFFER(p_s_search_path),
|
|
--PATH_LAST_POSITION(p_s_search_path));
|
|
n_blk_size = p_s_sbi->s_blocksize;
|
|
|
|
if (comp_short_keys(&(p_le_ih->ih_key), p_cpu_key)) {
|
|
return (FILE_NOT_FOUND);
|
|
}
|
|
|
|
item_offset = le_ih_k_offset(p_le_ih);
|
|
offset = cpu_key_k_offset(p_cpu_key);
|
|
|
|
/* Needed byte is contained in the item pointed to by the path.*/
|
|
if (item_offset <= offset &&
|
|
item_offset + op_bytes_number(p_le_ih, n_blk_size) > offset) {
|
|
pos_in_item(p_s_search_path) = offset - item_offset;
|
|
if (is_indirect_le_ih(p_le_ih)) {
|
|
pos_in_item(p_s_search_path) /= n_blk_size;
|
|
}
|
|
return (POSITION_FOUND);
|
|
}
|
|
|
|
/* Needed byte is not contained in the item pointed to by the
|
|
* path. Set pos_in_item out of the item. */
|
|
if (is_indirect_le_ih(p_le_ih))
|
|
pos_in_item(p_s_search_path) =
|
|
ih_item_len(p_le_ih) / UNFM_P_SIZE;
|
|
else
|
|
pos_in_item(p_s_search_path) =
|
|
ih_item_len(p_le_ih);
|
|
|
|
return (POSITION_NOT_FOUND);
|
|
}
|