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