blob: 2621c836a42c47c01790c2b8d3d7f7eb70c45103 [file] [log] [blame]
/*
* Copyright 1996-2004 by Hans Reiser, licensing governed by
* reiserfsprogs/README
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "fsck.h"
#include <sys/stat.h>
#include "misc/unaligned.h"
#include "misc/malloc.h"
#include "util/misc.h"
/* this works for both new and old stat data */
#define st_mode(sd) le16_to_cpu(((reiserfs_sd_t *)(sd))->sd_mode)
#define st_mtime_v1(sd) le32_to_cpu(((reiserfs_sd_v1_t *)(sd))->sd_mtime)
#define st_mtime_v2(sd) le32_to_cpu(((reiserfs_sd_t *)(sd))->sd_mtime)
static int fsck_tree_insert_file (reiserfs_ih_t * ih,
char * item, int check);
/* either both sd-s are new of both are old */
static void fsck_tree_overwrite_stat (reiserfs_ih_t * new_ih,
void * new_item,
reiserfs_path_t * path)
{
__u16 new_mode, old_mode;
reiserfs_stat_get_mode (new_ih, new_item, &new_mode);
reiserfs_stat_get_mode (REISERFS_PATH_IH (path),
REISERFS_PATH_ITEM (path), &old_mode);
if ((S_ISREG (new_mode) && S_ISDIR (old_mode)) ||
(S_ISDIR (new_mode) && S_ISREG (old_mode)))
{
reiserfs_panic("Cannot overwrite SD [%H] with SD [%H].\n",
REISERFS_PATH_IH (path), new_ih);
}
if (S_ISREG (new_mode) && !S_ISREG (old_mode)) {
/* in tree we have not regular file - overwrite its stat data
with stat data of regular file */
memcpy (REISERFS_PATH_ITEM (path), new_item,
reiserfs_ih_get_len (REISERFS_PATH_IH (path)));
reiserfs_buffer_mkdirty (REISERFS_PATH_LEAF (path));
return;
}
if (S_ISREG (old_mode) && !S_ISREG (new_mode)) {
/* new stat data is not a stat data of regular file, keep
regular file stat data in tree */
return;
}
/* if coming stat data has newer mtime - use that */
if (reiserfs_ih_format_v1 (new_ih)) {
if (st_mtime_v1 (new_item) > st_mtime_v1 (REISERFS_PATH_ITEM (path))) {
memcpy (REISERFS_PATH_ITEM (path), new_item, REISERFS_SD_SIZE_V1);
reiserfs_buffer_mkdirty (REISERFS_PATH_LEAF (path));
}
} else {
if (st_mtime_v2 (new_item) > st_mtime_v2 (REISERFS_PATH_ITEM (path))) {
memcpy (REISERFS_PATH_ITEM (path), new_item, REISERFS_SD_SIZE);
reiserfs_buffer_mkdirty (REISERFS_PATH_LEAF (path));
}
}
return;
}
/* insert sd item if it does not exist, overwrite it otherwise */
static void fsck_tree_insert_stat (reiserfs_ih_t * new_ih,
char * new_item, int check)
{
reiserfs_path_t path;
__u32 oid;
if (check) {
oid = fsck_relocate_check (new_ih, !not_a_directory(new_item));
if (oid == 1) {
fsck_file_relocate (&new_ih->ih_key,
not_a_directory(new_item) ? 1 : 0);
} else if (oid) {
reiserfs_key_set_oid (&new_ih->ih_key, oid);
}
}
/* if we will have to insert item into tree - it is ready */
zero_nlink (new_ih, new_item);
fsck_item_mkunreach (new_ih);
/* we are sure now that if we are inserting stat data of a
directory - there are no items with the same key which are not
items of a directory, and that if we are inserting stat data is
of not a directory - it either has new key already or there are
no items with this key which are items of a directory */
if (reiserfs_tree_search_item (fs, &(new_ih->ih_key), &path) == ITEM_FOUND) {
/* this stat data is found */
if (reiserfs_ih_get_format (REISERFS_PATH_IH(&path)) !=
reiserfs_ih_get_format (new_ih))
{
/* in tree stat data and a new one are of different
formats */
fsck_log ("%s: Inserting the StatData %K, mode (%M)...",
__FUNCTION__, &(new_ih->ih_key), st_mode (new_item));
if (reiserfs_ih_format_v1 (new_ih)) {
/* sd to be inserted is of V1, where as sd in
the tree is of V2 */
fsck_log ("found newer in the tree, mode (%M), insersion was "
"skipped.\n", st_mode (REISERFS_PATH_ITEM (&path)));
reiserfs_tree_pathrelse (&path);
} else {
/* the stat data in the tree is sd_v1 */
fsck_log ("older sd, mode (%M), is replaced with it.\n",
st_mode (REISERFS_PATH_ITEM (&path)));
reiserfs_tree_delete (fs, &path, 0/*not temporary*/);
reiserfs_tree_search_item (fs, &new_ih->ih_key, &path);
reiserfs_tree_insert (fs, &path, new_ih, new_item);
}
} else {
/* both stat data are of the same version */
fsck_tree_overwrite_stat (new_ih, new_item, &path);
reiserfs_tree_pathrelse (&path);
}
return;
}
/* item not found, insert a new one */
reiserfs_tree_insert (fs, &path, new_ih, new_item);
}
static int fsck_tree_insert_prep (reiserfs_filsys_t *fs,
reiserfs_ih_t *ih,
int directory)
{
reiserfs_path_t path;
reiserfs_key_t key;
__u32 oid = 0;
int step = 0;
/* Check if the ois has been already relocated. */
if (!directory && (oid = fsck_relocate_get_oid(&ih->ih_key)))
reiserfs_key_set_oid(&ih->ih_key, oid);
memset(&key, 0, sizeof(key));
while (1) {
reiserfs_key_copy2(&key, &ih->ih_key);
/* Check if the StatData item presents. */
if ((reiserfs_tree_search_item (fs, &key, &path) != ITEM_FOUND) ||
(directory && not_a_directory(REISERFS_PATH_ITEM(&path))) ||
(!directory && !not_a_directory(REISERFS_PATH_ITEM(&path))))
{
fsck_log ("vpf-10260: The file we are inserting the new item "
"(%H) into has no StatData, insertion is skipped\n", ih);
reiserfs_tree_pathrelse (&path);
return 1;
}
reiserfs_tree_pathrelse(&path);
if (!step && !oid) {
oid = fsck_relocate_check(ih, directory);
if (oid == 1) {
fsck_file_relocate (&ih->ih_key, directory ? 0 : 1);
} else if (oid) {
reiserfs_key_set_oid(&ih->ih_key, oid);
} else {
return 0;
}
step = 1;
continue;
}
/* Check/Fix the format. */
#if 0
format = reiserfs_ih_get_format (REISERFS_PATH_IH(&path));
reiserfs_stat_get_mode (REISERFS_PATH_IH(&path),
REISERFS_PATH_ITEM(&path), &mode);
if (format != reiserfs_ih_get_format (ih)) {
/* Not for symlinks and not for items which should be relocted. */
if (((S_ISDIR(mode) && reiserfs_ih_dir(ih)) ||
(!S_ISDIR(mode) && !reiserfs_ih_dir(ih))) &&
!S_ISLNK(mode))
{
reiserfs_key_set_sec (format, &ih->ih_key,
reiserfs_key_get_off (&ih->ih_key),
reiserfs_key_get_type (&ih->ih_key));
reiserfs_ih_set_format(ih, file_format);
}
}
#endif
return 0;
}
}
/* this tries to put each item entry to the tree, if there is no items
of the directory, insert item containing 1 entry */
static void fsck_tree_insert_entry (reiserfs_ih_t * comingih,
char * item, int check)
{
char buf[REISERFS_NAME_MAX];
reiserfs_deh_t *deh;
char *name;
int namelen;
int i, step;
step = 0;
/* Check if the SD item exists */
if (check) {
if (fsck_relocate_check (comingih, 1))
fsck_file_relocate (&comingih->ih_key, 0);
}
deh = (reiserfs_deh_t *)item;
for (i = 0; i < reiserfs_ih_get_entries (comingih); i ++, deh ++) {
name = reiserfs_deh_name (deh, i);
namelen = reiserfs_direntry_name_len (comingih, deh, i);
if (!reiserfs_hash_correct (&fs->hash, name, namelen,
reiserfs_deh_get_off (deh)))
{
reiserfs_panic ("%s: The entry (%d) \"%.*s\" of the directory "
"%k has badly hashed entry", __FUNCTION__, i,
namelen, name, &comingih->ih_key);
}
buf[0] = '\0';
sprintf (buf, "%.*s", namelen, name);
/* 1 for fsck is important: if there is no any items of this
directory in the tree yet - new item will be inserted
marked not reached */
reiserfs_tree_insert_entry (fs, &(comingih->ih_key), buf,
reiserfs_direntry_entry_len (comingih, deh, i),
(reiserfs_key_t *)&(deh->deh2_dir_id),
1 << IH_Unreachable);
}
}
/* start_key is the key after which N items need to be deleted
save_here is a pointer where deleted items need to be saved if save is set.
start_key is the first undeleted item.
return whether we are sure there is nothing else of this file
*/
int fsck_tree_delete(reiserfs_key_t * start_key,
saveitem_t ** save_here,
int skip_dir_items,
tree_modify_t func,
void *data)
{
const reiserfs_key_t *rkey;
reiserfs_path_t path;
int count = 0;
int ret;
while (1) {
reiserfs_tree_search_body (fs, start_key, &path);
if (REISERFS_PATH_LEAF_POS (&path) ==
reiserfs_node_items (REISERFS_PATH_LEAF (&path)))
{
rkey = reiserfs_tree_rkey (&path, fs);
if (rkey && !reiserfs_key_comp2 (start_key, rkey)) {
/* file continues in the right neighbor */
reiserfs_key_copy (start_key, rkey);
reiserfs_tree_pathrelse (&path);
continue;
}
/* there is no more items with this key */
reiserfs_tree_pathrelse (&path);
return count;
}
rkey = &(REISERFS_PATH_IH (&path))->ih_key;
if (reiserfs_key_comp2 (start_key, rkey)) {
/* there are no more item with this key */
reiserfs_tree_pathrelse (&path);
return count;
}
reiserfs_key_copy (start_key, rkey);
/* Let it to be here to return either the last deleted key in
@start_key or the first not deleted. */
/* if (n_to_delete && count == n_to_delete) {
reiserfs_tree_pathrelse (&path);
break;
}
*/
/* ok, item found, but make sure that it is not a directory one */
if ((reiserfs_key_stat (rkey) &&
!not_a_directory (REISERFS_PATH_ITEM (&path))) ||
(reiserfs_key_dir (rkey)))
{
if (skip_dir_items) {
/* item of directory found. Leave it in the tree */
reiserfs_key_set_off(KEY_FORMAT_1, start_key,
reiserfs_key_get_off(start_key) + 1);
reiserfs_tree_pathrelse (&path);
continue;
} else {
reiserfs_panic ("%s: No directory item of %K are expected",
__FUNCTION__, rkey);
}
}
/* If removing not by the counter, break when it is save to do --
an indirect item is encountered. */
if ((ret = func(&path, data)) < 0) {
// if (n_to_delete == 0 && count && reiserfs_key_ext(rkey)) {
reiserfs_tree_pathrelse (&path);
break;
}
if (save_here != NULL)
fsck_item_save(&path, save_here);
reiserfs_tree_delete (fs, &path, save_here ? 1 : 0);
count++;
if (ret > 0)
break;
}
return count;
}
/* 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 - this condition skipped for now). */
/* pointers to data block which get into tree are checked with this */
static int still_bad_unfm_ptr (unsigned long block) {
if (!block)
return 0;
if (is_block_used (block))
return 1;
if (block >= reiserfs_sb_get_blocks (fs->fs_ondisk_sb))
return 1;
return 0;
}
static int fsck_tree_create_item (reiserfs_filsys_t *fs,
reiserfs_path_t * path,
reiserfs_ih_t * ih,
char * item,
int pos,
int alloc)
{
reiserfs_ih_t newih;
__u32 body_off;
__u64 key_off;
int count, i;
int format;
body_off = pos * (reiserfs_ih_direct(ih) ? 1 : REISERFS_EXT_SIZE);
key_off = pos * (reiserfs_ih_direct(ih) ? 1 : fs->fs_blocksize);
format = reiserfs_key_format(&ih->ih_key);
memcpy(&newih, ih, sizeof(newih));
fsck_item_mkunreach (&newih);
reiserfs_ih_set_len (&newih, reiserfs_ih_get_len (ih) - body_off);
reiserfs_ih_set_format (&newih, reiserfs_ih_get_format (ih));
reiserfs_ih_set_loc(&newih, 0);
reiserfs_key_set_off (format, &newih.ih_key,
reiserfs_key_get_off(&newih.ih_key) + key_off);
if (alloc && reiserfs_ih_ext(ih)) {
count = reiserfs_ext_count (ih);
for (i = pos; i < count; i++) {
if (still_bad_unfm_ptr (d32_get(item, i)))
reiserfs_panic ("%s: The file %K has a pointer to "
"the bad block (%u)", __FUNCTION__,
&ih->ih_key, d32_get(item, i));
mark_block_used (d32_get(item, i), 0);
}
}
reiserfs_tree_insert (fs, path, &newih, item + body_off);
return reiserfs_leaf_ibytes (ih, fs->fs_blocksize) - key_off;
}
int fsck_tree_insert_zero_ptr (reiserfs_filsys_t *fs,
reiserfs_key_t *key,
long long int p_count,
__u16 flags)
{
reiserfs_path_t path;
long long int count;
reiserfs_ih_t *ih;
int format;
__u32 * ni;
int ret;
ret = reiserfs_tree_search_position (fs, key, &path);
if (ret == DIRECTORY_FOUND || ret == POSITION_FOUND) {
reiserfs_panic("%s: The object [%k] must be a file and the "
"position must be absent.\n", __FUNCTION__,
key);
}
count = REISERFS_ITEM_MAX(fs->fs_blocksize) / REISERFS_EXT_SIZE;
p_count = (p_count + fs->fs_blocksize - 1) /
fs->fs_blocksize * fs->fs_blocksize;
if (p_count / fs->fs_blocksize <= count)
count = p_count / fs->fs_blocksize;
ni = misc_getmem (count * REISERFS_EXT_SIZE);
if (path.pos_in_item) {
/* Position is not found, append to the existent item. */
ih = REISERFS_PATH_IH (&path);
if (reiserfs_item_count(ih) != path.pos_in_item) {
reiserfs_panic("%s: not expected position (%u) in the "
"middle of the item %k.\n", __FUNCTION__,
path.pos_in_item, &ih->ih_key);
}
if ((flags & (1 << IH_Unreachable)) ||
reiserfs_ih_direct(ih) || must_there_be_a_hole(ih, key))
{
REISERFS_PATH_LEAF_POS (&path) ++;
path.pos_in_item = 0;
} else {
reiserfs_tree_insert_unit(fs, &path, ni, count * REISERFS_EXT_SIZE);
if (path.pos_in_item == 0)
misc_die("Not expected position");
}
}
if (path.pos_in_item == 0) {
/* Either file not found or position not found, create a new item. */
reiserfs_ih_t indih;
memset(&indih, 0, sizeof(indih));
reiserfs_ih_set_flags(&indih, flags);
reiserfs_key_copy (&(indih.ih_key), key);
reiserfs_ih_set_len (&indih, count * REISERFS_EXT_SIZE);
reiserfs_ih_set_free (&indih, 0);
format = reiserfs_key_format(key);
reiserfs_ih_set_format (&indih, format);
reiserfs_key_set_type (format, &indih.ih_key, TYPE_EXTENT);
reiserfs_tree_insert (fs, &path, &indih, ni);
}
misc_freemem(ni);
return count * fs->fs_blocksize;
}
long long int must_there_be_a_hole (const reiserfs_ih_t *ih,
const reiserfs_key_t *key)
{
return (reiserfs_key_get_off (key) -
reiserfs_key_get_off(&ih->ih_key) -
reiserfs_leaf_ibytes(ih, fs->fs_blocksize));
}
void fsck_tree_merge(reiserfs_path_t *path) {
int start, len, next_start, next_len;
reiserfs_ih_t *next;
reiserfs_ih_t *ih;
reiserfs_bh_t *bh;
char *buf;
int pos;
bh = REISERFS_PATH_LEAF(path);
pos = REISERFS_PATH_LEAF_POS(path);
if (pos >= reiserfs_node_items(bh) || pos < 1)
misc_die("%s: block (%lu), item (%d), nothing to merge with.",
__FUNCTION__, bh->b_blocknr, pos);
ih = reiserfs_ih_at(bh, pos) - 1;
next = ih + 1;
start = reiserfs_ih_get_loc(ih);
len = reiserfs_ih_get_len(ih);
next_start = reiserfs_ih_get_loc(next);
next_len = reiserfs_ih_get_len(next);
if (!(buf = misc_malloc(next_len)))
misc_die("%s: Failed to allocate %d bytes.", __FUNCTION__, next_len);
if (!reiserfs_ih_dir(ih)) {
memmove(buf, bh->b_data + next_start, next_len);
memmove(bh->b_data + next_start, bh->b_data + start, len);
memmove(bh->b_data + next_start + len, buf, next_len);
reiserfs_ih_set_free(ih, reiserfs_ih_get_free(next));
} else {
reiserfs_deh_t *deh;
int move;
int i;
move = reiserfs_ih_get_entries(ih) * REISERFS_DEH_SIZE;
memmove(buf, bh->b_data + next_start, next_len);
memmove(bh->b_data + next_start, bh->b_data + start, move);
memmove(bh->b_data + next_start + move, buf, next_len);
move /= REISERFS_DEH_SIZE;
reiserfs_ih_set_entries(ih, reiserfs_ih_get_entries(next) + move);
deh = (reiserfs_deh_t *)(bh->b_data + next_start);
for (i = 0; i < move; i++, deh++)
reiserfs_deh_set_loc(deh, reiserfs_deh_get_loc(deh) + next_len);
move *= REISERFS_DEH_SIZE;
for (i = 0; i < reiserfs_ih_get_entries(next); i++, deh++)
reiserfs_deh_set_loc(deh, reiserfs_deh_get_loc(deh) + move);
}
misc_freemem(buf);
reiserfs_ih_set_loc(ih, next_start);
reiserfs_ih_set_len(ih, len + next_len);
reiserfs_ih_set_len(next, 0);
reiserfs_tree_delete (fs, path, 0);
}
static int cb_tree_rewrite(reiserfs_path_t *path, void *data) {
reiserfs_ih_t *ih;
__u64 *off;
ih = REISERFS_PATH_IH(path);
off = (__u64 *)data;
if (reiserfs_key_get_off(&ih->ih_key) >= *off)
return -1;
return 0;
}
void fsck_tree_rewrite(reiserfs_filsys_t *fs,
const reiserfs_key_t *start_key,
__u64 end_offset, __u16 flags)
{
reiserfs_key_t key;
saveitem_t *si;
__u64 start;
int format;
si = NULL;
start = reiserfs_key_get_off(start_key);
start = MISC_DOWN(start, fs->fs_blocksize) + 1;
end_offset = MISC_UP(end_offset - 1, fs->fs_blocksize) + 1;
format = reiserfs_key_format(start_key);
reiserfs_key_copy(&key, start_key);
reiserfs_key_set_off (format, &key, start);
fsck_tree_delete(&key, &si, 0, cb_tree_rewrite, &end_offset);
/* Insert needed zero pointers. */
while (start < end_offset) {
reiserfs_key_set_off (format, &key, start);
start += fsck_tree_insert_zero_ptr(fs, &key,
end_offset - start, flags);
}
/* Insert removed items back, over have inserted zero pointers. */
while (si) {
fsck_tree_insert_file(&si->si_ih, si->si_dnm_data, 0);
si = fsck_item_free (si);
}
}
/* Check if an overlapping exists and if it does delete all items overlapped
with the one pointed by @ih, insert zeroes since @ih offset and insert have
removed items back. */
static int fsck_tree_overwrite_prep(reiserfs_filsys_t *fs,
const reiserfs_key_t *found_key,
const reiserfs_key_t *start_key,
__u64 count)
{
__u64 end;
if (reiserfs_key_comp2(found_key, start_key))
return 0;
end = reiserfs_key_get_off (start_key) + count;
end = MISC_UP(end - 1, fs->fs_blocksize) + 1;
if (end <= reiserfs_key_get_off(found_key))
return 0;
fsck_tree_rewrite(fs, start_key, end, 1 << IH_Unreachable);
return 1;
}
/* put unformatted node pointers from incoming item over the in-tree ones */
static int fsck_tree_overwrite_by_extent (reiserfs_ih_t * comingih,
__u32 * coming_item,
reiserfs_path_t * path,
int pos, int check)
{
reiserfs_bh_t * bh = REISERFS_PATH_LEAF (path);
reiserfs_ih_t * ih = REISERFS_PATH_IH (path);
int written;
__u32 * item_in_tree;
int src_unfm_ptrs, dest_unfm_ptrs, to_copy, i, dirty = 0;
item_in_tree = (__u32 *)reiserfs_item_by_ih (bh, ih) + path->pos_in_item;
coming_item += pos;
dest_unfm_ptrs = reiserfs_ext_count (ih) - path->pos_in_item;
src_unfm_ptrs = reiserfs_ext_count (comingih) - pos;
if (dest_unfm_ptrs >= src_unfm_ptrs) {
/* whole coming item (comingih) fits into item in tree (ih) starting
with path->pos_in_item */
//free_sp = ih_get_free_space(0, comingih, (char *)coming_item);
written = reiserfs_leaf_ibytes (comingih, fs->fs_blocksize) -
/* free_sp - */ pos * fs->fs_blocksize;
to_copy = src_unfm_ptrs;
if (dest_unfm_ptrs == src_unfm_ptrs)
reiserfs_ih_set_free(ih, 0 /* free_sp */ );
} else {
/* only part of coming item overlaps item in the tree */
written = dest_unfm_ptrs * fs->fs_blocksize;
to_copy = dest_unfm_ptrs;
reiserfs_ih_set_free(ih, 0);
}
for (i = 0; i < to_copy; i ++) {
if (d32_get (coming_item, i) != 0 && d32_get (item_in_tree, i) == 0) {
/* overwrite holes only by correct a pointer in the coming item
which must be correct */
d32_put (item_in_tree, i, d32_get (coming_item, i));
if (check) {
if (still_bad_unfm_ptr (d32_get (coming_item, i)))
misc_die ("%s: The unformatted block pointer "
"(%u) points to the bad area.",
__FUNCTION__, d32_get (coming_item, i));
mark_block_used (d32_get (coming_item, i), 0);
}
dirty ++;
}
}
if (dirty)
reiserfs_buffer_mkdirty (bh);
return written;
}
/* write direct item to unformatted node */
/* coming item is direct */
static int fsck_tree_overwrite_by_direct(reiserfs_ih_t * comingih,
char * item,
reiserfs_path_t * path)
{
reiserfs_bh_t * unbh, * bh;
reiserfs_ih_t * ih;
__u64 coming_len;
__u32 unfm_ptr;
int offset;
bh = REISERFS_PATH_LEAF (path);
ih = REISERFS_PATH_IH (path);
unfm_ptr = d32_get ((__u32 *)reiserfs_item_by_ih(bh, ih),
path->pos_in_item);
unbh = 0;
if (unfm_ptr != 0 && unfm_ptr < reiserfs_sb_get_blocks (fs->fs_ondisk_sb)) {
unbh = reiserfs_buffer_read (fs->fs_dev, unfm_ptr, bh->b_size);
if (!is_block_used (unfm_ptr)) {
misc_die ("%s: block %lu, item %d, pointer %d: The pointed "
"block (%u) being overwritten is marked as unused.",
__FUNCTION__, bh->b_blocknr,
REISERFS_PATH_LEAF_POS(path),
path->pos_in_item, unfm_ptr);
}
if (unbh == 0)
unfm_ptr = 0;
}
if (unfm_ptr == 0 ||
unfm_ptr >= reiserfs_sb_get_blocks (fs->fs_ondisk_sb))
{
if ((unbh = reiserfsck_get_new_buffer (bh->b_blocknr)) != NULL) {
memset (unbh->b_data, 0, unbh->b_size);
d32_put ((__u32 *)reiserfs_item_by_ih(bh, ih),
path->pos_in_item, unbh->b_blocknr);
reiserfs_buffer_mkdirty (bh);
} else {
misc_die ("%s: Could not allocate a new block "
"for new data", __FUNCTION__);
}
/* If zero extent is overwritten with the direct, mark WAS_TAIL. */
reiserfs_ih_mktail(ih);
}
coming_len = reiserfs_leaf_ibytes (comingih, fs->fs_blocksize);
offset = (reiserfs_key_get_off (&comingih->ih_key) % bh->b_size) - 1;
if (offset + coming_len > bh->b_size) {
misc_die ("%s: The length of the file after insertion "
"(offset=%lu, length=%u) will exceed the maximal "
"possible length.", __FUNCTION__,
(long unsigned)reiserfs_key_get_off(&comingih->ih_key),
(unsigned)coming_len);
}
memcpy (unbh->b_data + offset, item, coming_len);
if ((path->pos_in_item == (reiserfs_ext_count (ih) - 1)) &&
(bh->b_size - 0/*ih_free_space (ih)*/) < (offset + coming_len)) {
reiserfs_ih_set_free (ih, bh->b_size - (offset + coming_len)) ;
reiserfs_buffer_mkdirty (bh);
}
reiserfs_buffer_mkdirty (unbh);
reiserfs_buffer_mkuptodate (unbh, 1);
reiserfs_buffer_close (unbh);
return coming_len;
}
static int fsck_tree_overwrite_file (reiserfs_ih_t * comingih,
char * item,
reiserfs_path_t * path,
int pos, int check)
{
reiserfs_ih_t * ih = REISERFS_PATH_IH (path);
reiserfs_key_t key;
int written = 0;
__u64 off;
int ret;
if (reiserfs_key_comp2 (ih, &(comingih->ih_key)))
reiserfs_panic ("%s: The file to be overwritten %K must be of "
"the same as the new data %K", __FUNCTION__,
&ih->ih_key, &comingih->ih_key);
if (reiserfs_ih_direct (ih)) {
reiserfs_key_copy(&key, &comingih->ih_key);
off = reiserfs_key_get_off(&comingih->ih_key);
off += pos * (reiserfs_ih_direct(comingih) ? 1 : fs->fs_blocksize);
reiserfs_key_set_off(reiserfs_key_format(&key), &key, off);
reiserfs_tree_pathrelse(path);
fsck_tree_rewrite(fs, &ih->ih_key,
reiserfs_key_get_off(&ih->ih_key) + 1,
1 << IH_Unreachable);
ret = reiserfs_tree_search_position(fs, &key, path);
if (ret == DIRECTORY_FOUND || ret == FILE_NOT_FOUND ||
!reiserfs_ih_ext(REISERFS_PATH_IH(path)))
{
reiserfs_panic ("%s: The data %k, which are supposed to be "
"converted, are not found", __FUNCTION__, &key);
}
}
if (reiserfs_ih_direct (comingih)) {
written = fsck_tree_overwrite_by_direct(comingih, item, path);
} else {
written = fsck_tree_overwrite_by_extent(comingih, (__u32 *)item,
path, pos, check);
}
return written;
}
/* this appends file with one unformatted node pointer (since balancing
algorithm limitation). This pointer can be 0, or new allocated block or
pointer from extent item that is being inserted into tree */
static int fsck_tree_append_file (reiserfs_ih_t * comingih,
char * item, int pos,
reiserfs_path_t * path,
int check)
{
__u32 * ni;
reiserfs_bh_t * unbh;
int retval;
reiserfs_ih_t * ih = REISERFS_PATH_IH (path);
__u32 bytes_number;
int i, count = 0;
if (!reiserfs_ih_ext (ih))
reiserfs_panic ("%s: Operation is not allowed for non-extent "
"item %k", __FUNCTION__, &ih->ih_key);
if (reiserfs_ih_direct (comingih)) {
unsigned int coming_len =
reiserfs_leaf_ibytes (comingih, fs->fs_blocksize);
reiserfs_ih_mktail (ih);
reiserfs_buffer_mkdirty (REISERFS_PATH_LEAF(path));
if (reiserfs_key_get_off (&comingih->ih_key) <
reiserfs_key_get_off (&ih->ih_key) +
fs->fs_blocksize * reiserfs_ext_count (ih))
{
/* direct item fits to free space of extent item */
reiserfs_panic("%s: inserting [%H], pos (%d) into [%H], "
"should not get here.\n", __FUNCTION__,
comingih, pos, ih);
}
unbh = reiserfsck_get_new_buffer (REISERFS_PATH_LEAF (path)->b_blocknr);
memset (unbh->b_data, 0, unbh->b_size);
memcpy (unbh->b_data + reiserfs_key_get_off (&comingih->ih_key) %
unbh->b_size - 1, item, coming_len);
reiserfs_buffer_mkdirty (unbh);
reiserfs_buffer_mkuptodate (unbh, 1);
ni = misc_getmem (REISERFS_EXT_SIZE);
d32_put (ni, 0, unbh->b_blocknr);
count = 1;
reiserfs_buffer_close (unbh);
retval = coming_len;
} else {
/* coming item is extent item */
bytes_number = reiserfs_leaf_ibytes (ih, fs->fs_blocksize);
if (reiserfs_key_get_off (&comingih->ih_key) +
pos * fs->fs_blocksize !=
reiserfs_key_get_off (&ih->ih_key) + bytes_number)
{
reiserfs_panic ("%s: file %K: Cannot append extent pointers "
"of the offset (%llu) at the position %llu\n",
__FUNCTION__, &comingih->ih_key,
reiserfs_key_get_off (&comingih->ih_key) +
pos * fs->fs_blocksize,
reiserfs_key_get_off (&ih->ih_key) +
bytes_number);
}
/* take unformatted pointer from an extent item */
count = reiserfs_ext_count(comingih) - pos;
ni = misc_getmem (count * REISERFS_EXT_SIZE);
memcpy (ni, (item + pos * REISERFS_EXT_SIZE), count * REISERFS_EXT_SIZE);
if (check) {
for (i = 0; i < count; i++ ) {
if (still_bad_unfm_ptr (d32_get (ni, i)))
misc_die ("%s: Trying to insert a pointer to illegal "
"block (%u)", __FUNCTION__, d32_get (ni, i));
mark_block_used (d32_get (ni, i), 0);
}
}
retval = fs->fs_blocksize * count;
}
reiserfs_tree_insert_unit (fs, path, (const char *)ni, count * REISERFS_EXT_SIZE);
misc_freemem (ni);
return retval;
}
static int fsck_tree_insert_file (reiserfs_ih_t * ih,
char * item,
int check)
{
long long int retval, written;
const reiserfs_key_t *p_key;
const reiserfs_ih_t *p_ih;
reiserfs_path_t path;
reiserfs_key_t key;
int count, pos;
/* Check if the SD item exists */
if (check && fsck_tree_insert_prep(fs, ih, 0))
return 0;
pos = 0;
count = reiserfs_leaf_ibytes (ih, fs->fs_blocksize);
reiserfs_key_copy (&key, &(ih->ih_key));
while (count) {
if (count < 0) {
reiserfs_panic ("%s: We wrote into the file %K more "
"bytes than needed - count (%d) < 0.",
__FUNCTION__, &key, count);
}
retval = reiserfs_tree_search_position (fs, &key, &path);
if (retval == DIRECTORY_FOUND) {
reiserfs_panic ("The directory was found at the place of the file "
"we are going to insert the item %k into", &key);
}
written = 0;
if (retval == POSITION_FOUND) {
written = fsck_tree_overwrite_file (ih, item, &path,
pos, check);
}
if (retval == POSITION_NOT_FOUND) {
if (REISERFS_PATH_LEAF_POS (&path) >=
reiserfs_node_items (REISERFS_PATH_LEAF(&path)))
{
p_key = reiserfs_tree_next_key(&path, fs);
} else {
p_key = &REISERFS_PATH_IH(&path)->ih_key;
}
if (path.pos_in_item == 0) {
/* There is nothing of this file before the found item.
Check the overlapping with the found/next item. */
if (fsck_tree_overwrite_prep(fs, p_key, &key, count)) {
reiserfs_tree_pathrelse (&path);
continue;
}
/* No overlapping, insert a new item. */
written = fsck_tree_create_item(fs, &path, ih,
item, pos, check);
} else {
const reiserfs_key_t *next = reiserfs_tree_next_key(&path, fs);
/* Check the overlapping with the next item. */
if (fsck_tree_overwrite_prep(fs, next, &key, count)) {
reiserfs_tree_pathrelse (&path);
continue;
}
/* No overlapping, convert the current into the extent and
insert a new / append to the existent. */
p_ih = REISERFS_PATH_IH(&path);
written = must_there_be_a_hole (p_ih, &key);
if (written <= 0 && reiserfs_key_direct(p_key)) {
reiserfs_tree_pathrelse(&path);
fsck_tree_rewrite(fs, &p_ih->ih_key,
reiserfs_key_get_off(&key) + 1,
1 << IH_Unreachable);
continue;
}
if (written > 0) {
REISERFS_PATH_LEAF_POS (&path) ++;
path.pos_in_item = 0;
written = fsck_tree_create_item(fs, &path, ih,
item, pos, check);
} else {
written = fsck_tree_append_file (ih, item, pos,
&path, check);
}
}
}
if (retval == FILE_NOT_FOUND) {
written = fsck_tree_create_item(fs, &path, ih,
item, pos, check);
}
reiserfs_tree_pathrelse (&path);
count -= written;
pos += written / (reiserfs_ih_direct(ih) ? 1 : fs->fs_blocksize);
reiserfs_key_set_off (reiserfs_key_format (&key), &key,
reiserfs_key_get_off (&key) + written);
}
/* This is a test for writing into the file. If not sure that file data
are consistent after fsck_tree_insert_file - uncomment this clause: */
/* if (!check && are_file_items_correct (&ih->ih_key,
(format == KEY_FORMAT_UNDEFINED) ?
reiserfs_ih_get_format (ih) : format,
&size, &blocks, 0, symlink, 0) == 0)
reiserfs_panic ("%s: item was not inserted properly\n", __FUNCTION__);*/
return reiserfs_leaf_ibytes (ih, fs->fs_blocksize);
}
void fsck_tree_insert_item (reiserfs_ih_t * ih,
char * item, int check)
{
if (reiserfs_key_get_did (&ih->ih_key) ==
reiserfs_key_get_oid (&ih->ih_key))
{
reiserfs_panic ("%s: The item being inserted has "
"the bad key %H", __FUNCTION__, ih);
}
if (reiserfs_ih_stat (ih)) {
fsck_tree_insert_stat (ih, item, check);
} else if (reiserfs_ih_dir (ih)) {
fsck_tree_insert_entry (ih, item, check);
} else {
fsck_tree_insert_file (ih, item, check);
}
}
void fsck_tree_trav (reiserfs_filsys_t * fs,
path_func_t action1,
path_func_t action2,
int depth)
{
reiserfs_path_t path;
reiserfs_bh_t *bh;
int total[REISERFS_PATH_MAX] = {0,};
unsigned long limit, done;
unsigned long block;
int problem;
int pos;
memset(&path, 0, sizeof(path));
path.path_length = REISERFS_PATH_OFFINIT;
done = 0;
limit = 1;
block = reiserfs_sb_get_root (fs->fs_ondisk_sb);
if (reiserfs_fs_block(fs, block) != BT_UNKNOWN) {
misc_die ("\nBad root block %lu. (--rebuild-tree "
"did not complete)\n", block);
}
while ( 1 ) {
problem = 0;
if (REISERFS_PATH_LEAF(&path))
misc_die ("%s: empty slot expected.\n", __FUNCTION__);
if (fs->fs_badblocks_bm &&
reiserfs_bitmap_test_bit (fs->fs_badblocks_bm, block))
{
fsck_log ("%s: block %lu specified in badblock list found in "
"tree, whole subtree skipped\n", __FUNCTION__, block);
fsck_data (fs)->check.bad_nodes++;
one_more_corruption (fs, FATAL);
if (path.path_length == REISERFS_PATH_OFFINIT) {
reiserfs_buffer_close (REISERFS_PATH_LEAF(&path));
REISERFS_PATH_LEAF(&path) = 0;
break;
}
problem = 1;
} else {
REISERFS_PATH_LEAF(&path) =
reiserfs_buffer_read (fs->fs_dev, block, fs->fs_blocksize);
if (REISERFS_PATH_LEAF(&path) == 0)
/* FIXME: handle case when read failed */
misc_die ("%s: unable to read %lu block on device 0x%x\n",
__FUNCTION__, block, fs->fs_dev);
if (action1)
if ((problem = action1 (fs, &path))) {
fsck_log (" the problem in the internal node occured "
"(%lu), whole subtree is skipped\n", block);
fsck_check_stat(fs)->bad_nodes++;
if (path.path_length == REISERFS_PATH_OFFINIT) {
reiserfs_buffer_close (REISERFS_PATH_LEAF(&path));
REISERFS_PATH_LEAF(&path) = 0;
break;
}
}
}
/* Time to stop. */
if (path.path_length - REISERFS_PATH_OFFINIT == depth)
problem ++;
if (problem || reiserfs_leaf_head(REISERFS_PATH_LEAF(&path))) {
if ((path.path_length > REISERFS_PATH_OFFINIT) &&
!fsck_quiet(fs))
{
util_misc_progress (fsck_progress_file (fs),
&done, limit, 1, 2);
}
if (!problem && action2)
action2 (fs, &path);
reiserfs_buffer_close (REISERFS_PATH_LEAF(&path));
while ((path.path_length > REISERFS_PATH_OFFINIT) &&
(REISERFS_PATH_POS(&path, path.path_length - 1) ==
total[path.path_length - 1] - 1 || problem))
{
problem = 0;
REISERFS_PATH_LEAF(&path) = 0;
path.path_length --;
reiserfs_buffer_close (REISERFS_PATH_LEAF(&path));
limit /= total[path.path_length];
done /= total[path.path_length];
}
REISERFS_PATH_LEAF(&path) = 0;
if (path.path_length == REISERFS_PATH_OFFINIT)
break;
bh = REISERFS_PATH_BUFFER(&path, path.path_length - 1);
REISERFS_PATH_POS(&path, path.path_length - 1) ++;
pos = REISERFS_PATH_POS(&path, path.path_length - 1);
block = reiserfs_dc_get_nr(reiserfs_int_at(bh, pos));
continue;
}
total[path.path_length] =
reiserfs_node_items (REISERFS_PATH_LEAF(&path)) + 1;
limit *= total[path.path_length];
done = done * total[path.path_length];
REISERFS_PATH_LEAF_POS(&path) = 0;
block = reiserfs_dc_get_nr (reiserfs_int_at (REISERFS_PATH_LEAF(&path), 0));
path.path_length ++;
}
}