This makes it more consistent with e2fsprogs. Signed-off-by: Jeff Mahoney <jeffm@suse.com>
1415 lines
41 KiB
C
1415 lines
41 KiB
C
/*
|
|
* Copyright 1996-2004 by Hans Reiser, licensing governed by
|
|
* reiserfsprogs/README
|
|
*/
|
|
|
|
#include "fsck.h"
|
|
|
|
static int do_items_have_the_same_type(struct item_head *ih,
|
|
struct reiserfs_key *key)
|
|
{
|
|
return (get_type(&ih->ih_key) == get_type(key)) ? 1 : 0;
|
|
}
|
|
|
|
static int are_items_in_the_same_node(struct reiserfs_path *path)
|
|
{
|
|
return (PATH_LAST_POSITION(path) <
|
|
B_NR_ITEMS(PATH_PLAST_BUFFER(path)) - 1) ? 1 : 0;
|
|
}
|
|
|
|
static void cut_last_unfm_pointer(struct reiserfs_path *path,
|
|
struct item_head *ih)
|
|
{
|
|
set_ih_free_space(ih, 0);
|
|
if (I_UNFM_NUM(ih) == 1)
|
|
reiserfsck_delete_item(path, 0);
|
|
else
|
|
reiserfsck_cut_from_item(path, -((int)UNFM_P_SIZE));
|
|
}
|
|
|
|
/*
|
|
if this is not a symlink - make it of_this_size;
|
|
otherwise find a size and return it in symlink_size;
|
|
*/
|
|
static unsigned long indirect_to_direct(struct reiserfs_path *path, __u64 len,
|
|
int symlink)
|
|
{
|
|
struct buffer_head *bh = PATH_PLAST_BUFFER(path);
|
|
struct item_head *ih = tp_item_head(path);
|
|
__u32 unfm_ptr;
|
|
struct buffer_head *unfm_bh = NULL;
|
|
struct item_head ins_ih;
|
|
char *buf;
|
|
char bad_drct[fs->fs_blocksize];
|
|
|
|
/* direct item to insert */
|
|
memset(&ins_ih, 0, sizeof(ins_ih));
|
|
if (symlink) {
|
|
set_ih_key_format(&ins_ih, KEY_FORMAT_1);
|
|
} else {
|
|
set_ih_key_format(&ins_ih, get_ih_key_format(ih));
|
|
}
|
|
set_key_dirid(&ins_ih.ih_key, get_key_dirid(&ih->ih_key));
|
|
set_key_objectid(&ins_ih.ih_key, get_key_objectid(&ih->ih_key));
|
|
set_type_and_offset(get_ih_key_format(&ins_ih), &ins_ih.ih_key,
|
|
get_offset(&ih->ih_key) + (I_UNFM_NUM(ih) -
|
|
1) * bh->b_size,
|
|
TYPE_DIRECT);
|
|
|
|
// we do not know what length this item should be
|
|
unfm_ptr = d32_get((__le32 *) tp_item_body(path), I_UNFM_NUM(ih) - 1);
|
|
if (unfm_ptr && (unfm_bh = bread(bh->b_dev, unfm_ptr, bh->b_size))) {
|
|
/* we can read the block */
|
|
buf = unfm_bh->b_data;
|
|
|
|
} else {
|
|
/* we cannot read the block */
|
|
if (unfm_ptr)
|
|
fsck_log
|
|
("indirect_to_direct: Reading of the block (%lu), pointed to by the file %K, failed\n",
|
|
unfm_ptr, &ih->ih_key);
|
|
memset(bad_drct, 0, fs->fs_blocksize);
|
|
buf = bad_drct;
|
|
}
|
|
/*
|
|
if (len > MAX_DIRECT_ITEM_LEN (fs->fs_blocksize)) {
|
|
fsck_log ("indirect_to_direct: canot create such a long item %d (%K), "
|
|
"Cutting it down to %d byte\n", len, &ih->ih_key, MAX_DIRECT_ITEM_LEN (fs->fs_blocksize) - 8);
|
|
len = MAX_DIRECT_ITEM_LEN (fs->fs_blocksize) - 8;
|
|
}
|
|
|
|
if (!len) {
|
|
buf = bad_link;
|
|
len = strlen (bad_link);
|
|
}
|
|
*/
|
|
set_ih_item_len(&ins_ih,
|
|
(get_ih_key_format(ih) ==
|
|
KEY_FORMAT_2) ? ROUND_UP(len) : len);
|
|
set_ih_free_space(&ins_ih, MAX_US_INT);
|
|
|
|
mark_ih_become_tail(ih);
|
|
mark_buffer_dirty(bh);
|
|
// last last unformatted node pointer
|
|
path->pos_in_item = I_UNFM_NUM(ih) - 1;
|
|
cut_last_unfm_pointer(path, ih);
|
|
|
|
/* insert direct item */
|
|
if (reiserfs_search_by_key_4(fs, &(ins_ih.ih_key), path) == ITEM_FOUND)
|
|
reiserfs_panic
|
|
("indirect_to_direct: The direct item %k should not exist yet.",
|
|
&(ins_ih.ih_key));
|
|
reiserfsck_insert_item(path, &ins_ih, (const char *)(buf));
|
|
|
|
brelse(unfm_bh);
|
|
|
|
/* put to stat data offset of first byte in direct item */
|
|
return get_offset(&ins_ih.ih_key); //offset;
|
|
}
|
|
|
|
/* 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 delete_N_items_after_key(struct reiserfs_key *start_key,
|
|
struct si **save_here, int skip_dir_items,
|
|
int n_to_delete)
|
|
{
|
|
struct reiserfs_path path;
|
|
// struct reiserfs_key key = *start_key;
|
|
struct reiserfs_key *rkey;
|
|
int count = 0;
|
|
|
|
while (1) {
|
|
reiserfs_search_by_key_4(fs, start_key, &path);
|
|
|
|
if (get_item_pos(&path) == B_NR_ITEMS(get_bh(&path))) {
|
|
rkey = uget_rkey(&path);
|
|
if (rkey && !not_of_one_file(start_key, rkey)) {
|
|
/* file continues in the right neighbor */
|
|
copy_key(start_key, rkey);
|
|
pathrelse(&path);
|
|
continue;
|
|
}
|
|
/* there is no more items with this key */
|
|
pathrelse(&path);
|
|
return 1;
|
|
}
|
|
|
|
if (is_stat_data_ih(tp_item_head(&path)))
|
|
fix_obviously_wrong_sd_mode(&path);
|
|
|
|
rkey = &(tp_item_head(&path))->ih_key;
|
|
if (not_of_one_file(start_key, rkey)) {
|
|
/* there are no more item with this key */
|
|
pathrelse(&path);
|
|
return 1;
|
|
}
|
|
|
|
copy_key(start_key, rkey);
|
|
/* ok, item found, but make sure that it is not a directory one */
|
|
if ((is_stat_data_key(rkey)
|
|
&& !not_a_directory(tp_item_body(&path)))
|
|
|| (is_direntry_key(rkey))) {
|
|
if (skip_dir_items) {
|
|
/* item of directory found. Leave it in the tree */
|
|
set_offset(KEY_FORMAT_1, start_key,
|
|
get_offset(start_key) + 1);
|
|
pathrelse(&path);
|
|
continue;
|
|
} else {
|
|
reiserfs_panic
|
|
("delete_N_items_after_key: No directory item of %K are "
|
|
"expected", start_key);
|
|
}
|
|
}
|
|
if (save_here != NULL)
|
|
*save_here =
|
|
save_and_delete_file_item(*save_here, &path);
|
|
else
|
|
reiserfsck_delete_item(&path, 0);
|
|
|
|
count++;
|
|
|
|
if (count == n_to_delete)
|
|
break;
|
|
|
|
pathrelse(&path);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* returns 1 when file looks correct, -1 if directory items appeared
|
|
there, 0 - only holes in the file found */
|
|
/* when it returns, key->k_offset is offset of the last item of file */
|
|
/* sd_size is zero if we do not need to convert any indirect to direct */
|
|
int are_file_items_correct(struct item_head *sd_ih, void *sd, __u64 * size,
|
|
__u32 * blocks, int mark_passed_items, int *symlink)
|
|
{
|
|
struct reiserfs_path path;
|
|
struct item_head *ih;
|
|
struct reiserfs_key *next_key, *key;
|
|
__u32 sd_first_direct_byte = 0;
|
|
__u64 sd_size;
|
|
unsigned int i;
|
|
int retval, was_tail = 0;
|
|
int had_direct = 0;
|
|
int key_version = get_ih_key_format(sd_ih);
|
|
int next_is_another_object = 0;
|
|
__u64 last_unfm_offset = 0;
|
|
int will_convert = 0;
|
|
int should_convert;
|
|
|
|
should_convert = (fsck_mode(fs) != FSCK_REBUILD) || mark_passed_items;
|
|
key = &sd_ih->ih_key;
|
|
get_sd_size(sd_ih, sd, &sd_size);
|
|
|
|
if (key_version == KEY_FORMAT_1)
|
|
get_sd_first_direct_byte(sd_ih, sd, &sd_first_direct_byte);
|
|
|
|
set_offset(key_version, key, 1);
|
|
set_type(key_version, key, TYPE_DIRECT);
|
|
|
|
/* correct size and st_blocks */
|
|
*size = 0;
|
|
*blocks = 0;
|
|
|
|
path.path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
|
|
|
|
do {
|
|
retval = reiserfs_search_by_position(fs, key, key_version, &path);
|
|
if (retval == POSITION_FOUND && path.pos_in_item != 0)
|
|
die("are_file_items_correct: All bytes we look for must be first items byte (position 0).");
|
|
|
|
switch (retval) {
|
|
case POSITION_FOUND:
|
|
/**/ ih = tp_item_head(&path);
|
|
|
|
if (ih_was_tail(ih)) {
|
|
was_tail = 1;
|
|
}
|
|
|
|
set_type(key_version, key, get_type(&ih->ih_key));
|
|
|
|
if (mark_passed_items == 1) {
|
|
mark_item_reachable(ih,
|
|
PATH_PLAST_BUFFER(&path));
|
|
}
|
|
// does not change path
|
|
next_key = reiserfs_next_key(&path);
|
|
|
|
if (next_key == 0 || not_of_one_file(key, next_key) ||
|
|
(!is_indirect_key(next_key)
|
|
&& !is_direct_key(next_key))) {
|
|
next_is_another_object = 1;
|
|
will_convert = (is_indirect_ih(ih) && sd_size
|
|
&& (I_UNFM_NUM(ih) > 0));
|
|
if (will_convert) {
|
|
last_unfm_offset =
|
|
get_offset(key) +
|
|
fs->fs_blocksize * (I_UNFM_NUM(ih) -
|
|
1);
|
|
/* if symlink or
|
|
[ 1. sd_size points somewhere into last unfm block
|
|
2. one item of the file was direct before for 3_6 || FDB points to the tail correctly for 3_5
|
|
3. we can have a tail in the file of a such size ] */
|
|
will_convert = will_convert
|
|
&& (sd_size >= last_unfm_offset)
|
|
&& (sd_size <
|
|
last_unfm_offset +
|
|
fs->fs_blocksize)
|
|
&& !STORE_TAIL_IN_UNFM(sd_size,
|
|
sd_size -
|
|
last_unfm_offset
|
|
+ 1,
|
|
fs->
|
|
fs_blocksize);
|
|
|
|
will_convert = will_convert && (*symlink
|
|
||
|
|
((key_version == KEY_FORMAT_1) && (sd_first_direct_byte == last_unfm_offset)) || ((key_version == KEY_FORMAT_2) && was_tail));
|
|
}
|
|
}
|
|
|
|
if (should_convert) {
|
|
*symlink = *symlink && (will_convert
|
|
|| is_direct_key(&ih->
|
|
ih_key));
|
|
|
|
if (!(*symlink)
|
|
&& key_version != get_ih_key_format(ih)) {
|
|
if (fsck_mode(fs) == FSCK_CHECK) {
|
|
fsck_log
|
|
("are_file_items_correct: vpf-10250: block %lu, item (%d): The item format (%H)"
|
|
" is not equal to SD format (%d)\n",
|
|
get_bh(&path)->b_blocknr,
|
|
PATH_LAST_POSITION(&path),
|
|
ih, key_version);
|
|
one_more_corruption(fs,
|
|
FIXABLE);
|
|
} else {
|
|
fsck_log
|
|
("are_file_items_correct: vpf-10280: block %lu, item (%d): The item format (%H)"
|
|
" is not equal to SD format (%d) - fixed.\n",
|
|
get_bh(&path)->b_blocknr,
|
|
PATH_LAST_POSITION(&path),
|
|
ih, key_version);
|
|
|
|
set_type_and_offset(key_version,
|
|
&ih->ih_key,
|
|
get_offset
|
|
(&ih->
|
|
ih_key),
|
|
get_type
|
|
(&ih->
|
|
ih_key));
|
|
set_ih_key_format(ih,
|
|
key_version);
|
|
mark_buffer_dirty(get_bh
|
|
(&path));
|
|
}
|
|
}
|
|
|
|
if (*symlink && is_direct_key(&ih->ih_key)) {
|
|
/* symlink. Check that it is of KEY_FORMAT_1 */
|
|
if (fsck_mode(fs) == FSCK_CHECK) {
|
|
if ((get_ih_key_format(ih) !=
|
|
KEY_FORMAT_1)
|
|
|| (key_format(&ih->ih_key)
|
|
!= KEY_FORMAT_1)) {
|
|
fsck_log
|
|
("are_file_items_correct: vpf-10732: block %lu, item (%d): The symlink format (%H)"
|
|
" is not equal to 3.5 format (%d)\n",
|
|
get_bh(&path)->
|
|
b_blocknr,
|
|
PATH_LAST_POSITION
|
|
(&path), ih,
|
|
KEY_FORMAT_1);
|
|
one_more_corruption(fs,
|
|
FIXABLE);
|
|
}
|
|
} else {
|
|
if ((get_ih_key_format(ih) !=
|
|
KEY_FORMAT_1)
|
|
|| (key_format(&ih->ih_key)
|
|
!= KEY_FORMAT_1)) {
|
|
fsck_log
|
|
("are_file_items_correct: vpf-10732: block %lu, item (%d): The symlink format (%H)"
|
|
" is not equal to 3.5 format (%d)\n",
|
|
get_bh(&path)->
|
|
b_blocknr,
|
|
PATH_LAST_POSITION
|
|
(&path), ih,
|
|
KEY_FORMAT_1);
|
|
set_type_and_offset
|
|
(KEY_FORMAT_1,
|
|
&ih->ih_key,
|
|
get_offset(&ih->
|
|
ih_key),
|
|
get_type(&ih->
|
|
ih_key));
|
|
set_ih_key_format(ih,
|
|
KEY_FORMAT_1);
|
|
mark_buffer_dirty(get_bh
|
|
(&path));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (will_convert)
|
|
*size = sd_size;
|
|
else
|
|
*size =
|
|
get_offset(&ih->ih_key) +
|
|
get_bytes_number(ih,
|
|
fs->fs_blocksize) -
|
|
1;
|
|
|
|
if (get_type(&ih->ih_key) == TYPE_INDIRECT) {
|
|
if (*symlink) /* symlinks must be calculated as dirs */
|
|
*blocks =
|
|
dir_size2st_blocks(*size);
|
|
else
|
|
for (i = 0; i < I_UNFM_NUM(ih);
|
|
i++) {
|
|
__le32 *ind =
|
|
(__le32 *)
|
|
tp_item_body(&path);
|
|
|
|
if (d32_get(ind, i) !=
|
|
0)
|
|
*blocks +=
|
|
(fs->
|
|
fs_blocksize
|
|
>> 9);
|
|
}
|
|
} else if (get_type(&ih->ih_key) == TYPE_DIRECT) {
|
|
if (*symlink) /* symlinks must be calculated as dirs */
|
|
*blocks =
|
|
dir_size2st_blocks(*size);
|
|
else if (!had_direct)
|
|
*blocks +=
|
|
(fs->fs_blocksize >> 9);
|
|
|
|
/* calculate only the first direct byte */
|
|
had_direct++;
|
|
}
|
|
}
|
|
|
|
if (next_is_another_object) {
|
|
/* next item does not exists or is of another object,
|
|
therefore all items of file are correct */
|
|
if (will_convert) {
|
|
if (fsck_mode(fs) == FSCK_CHECK) {
|
|
/* here it can be symlink only */
|
|
fsck_log
|
|
("are_file_items_correct: The indirect item should be converted back"
|
|
" to direct %K\n",
|
|
&ih->ih_key);
|
|
one_more_corruption(fs,
|
|
FIXABLE);
|
|
pathrelse(&path);
|
|
} else {
|
|
__le32 *ind =
|
|
(__le32 *) tp_item_body(&path);
|
|
/* DEBUG message.
|
|
fsck_log ("are_file_items_correct: The indirect item is converted back to direct %K\n", &ih->ih_key);
|
|
*/
|
|
if (d32_get
|
|
(ind,
|
|
I_UNFM_NUM(ih) - 1) == 0)
|
|
*blocks +=
|
|
(fs->
|
|
fs_blocksize >> 9);
|
|
|
|
/* path is released here. */
|
|
sd_first_direct_byte =
|
|
indirect_to_direct(&path,
|
|
sd_size -
|
|
last_unfm_offset
|
|
+ 1,
|
|
*symlink);
|
|
/* last item of the file is direct item */
|
|
set_offset(key_version, key,
|
|
sd_first_direct_byte);
|
|
set_type(key_version, key,
|
|
TYPE_DIRECT);
|
|
}
|
|
} else
|
|
pathrelse(&path);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* next item is item of this file */
|
|
if (get_offset(&ih->ih_key) +
|
|
get_bytes_number(ih,
|
|
fs->fs_blocksize) !=
|
|
get_offset(next_key)) {
|
|
/* next item has incorrect offset (hole or overlapping) */
|
|
pathrelse(&path);
|
|
return 0;
|
|
}
|
|
if (do_items_have_the_same_type(ih, next_key) == 1
|
|
&& are_items_in_the_same_node(&path) == 1) {
|
|
/* two indirect items or two direct items in the same leaf. FIXME: Second will be deleted */
|
|
pathrelse(&path);
|
|
return 0;
|
|
}
|
|
|
|
/* items are of different types or are in different nodes */
|
|
/*
|
|
if (get_offset (&ih->ih_key) + get_bytes_number (ih, fs->fs_blocksize) != get_offset (next_key))
|
|
{
|
|
// indirect item free space is not set properly
|
|
if (!is_indirect_ih (ih) ) //|| get_ih_free_space(ih) == 0)
|
|
fsck_log ("are_file_items_correct: "
|
|
"item must be indirect and must have invalid free space (%H)", ih);
|
|
|
|
if (fsck_mode (fs) == FSCK_REBUILD)
|
|
{
|
|
set_ih_free_space(ih, 0);
|
|
mark_buffer_dirty (PATH_PLAST_BUFFER (&path));
|
|
}
|
|
}
|
|
*/
|
|
/* next item exists */
|
|
set_type_and_offset(key_version, key,
|
|
get_offset(next_key),
|
|
get_type(next_key));
|
|
|
|
if (comp_keys(key, next_key))
|
|
reiserfs_panic
|
|
("are_file_items_correct: Internal tree is in inconsistent state, "
|
|
"the current item key %K and the next key %K must match",
|
|
key, next_key);
|
|
pathrelse(&path);
|
|
break;
|
|
|
|
case POSITION_NOT_FOUND:
|
|
/* We always must have next key found. Exception is first byte. It does not have to exist */
|
|
if (get_offset(key) != 1)
|
|
reiserfs_panic
|
|
("are_file_items_correct: Position (offset == %llu) in the middle of"
|
|
"the file %K was not found.",
|
|
get_offset(key), key);
|
|
pathrelse(&path);
|
|
return 0;
|
|
|
|
case FILE_NOT_FOUND:
|
|
if (get_offset(key) != 1)
|
|
reiserfs_panic
|
|
("are_file_items_correct: File %K must be found as we found its StatData.",
|
|
key);
|
|
pathrelse(&path);
|
|
return 1;
|
|
|
|
case DIRECTORY_FOUND:
|
|
pathrelse(&path);
|
|
return -1;
|
|
}
|
|
} while (1);
|
|
|
|
die("are_file_items_correct: Cannot reach here");
|
|
return 0;
|
|
}
|
|
|
|
/* delete all items and put them back (after that file should have
|
|
correct sequence of items.
|
|
if should_relocate is specified then it relocates items to a new id.
|
|
if should_change_ih is specified then the key in ih is changed also. */
|
|
void rewrite_file(struct item_head *ih, int should_relocate,
|
|
int should_change_ih)
|
|
{
|
|
struct reiserfs_key key;
|
|
struct si *si;
|
|
__u32 new_objectid = 0;
|
|
int moved_items;
|
|
struct reiserfs_key old, new;
|
|
int nothing_else = 0;
|
|
|
|
/* starting with the leftmost one - look for all items of file,
|
|
store and delete and */
|
|
key = ih->ih_key;
|
|
set_type_and_offset(KEY_FORMAT_1, &key, SD_OFFSET, TYPE_STAT_DATA);
|
|
|
|
si = 0;
|
|
nothing_else = 0;
|
|
|
|
nothing_else =
|
|
delete_N_items_after_key(&key, &si,
|
|
should_relocate /* do not skip dir items */
|
|
, 10);
|
|
|
|
if (should_relocate && (si || should_change_ih)) {
|
|
/* get new objectid for relocation or get objectid with which file
|
|
was relocated already */
|
|
new_objectid = objectid_for_relocation(&ih->ih_key);
|
|
if (should_change_ih)
|
|
set_key_objectid(&ih->ih_key, new_objectid);
|
|
|
|
if (si) {
|
|
old = si->si_ih.ih_key;
|
|
set_key_objectid(&(si->si_ih.ih_key), new_objectid);
|
|
new = si->si_ih.ih_key;
|
|
}
|
|
}
|
|
|
|
moved_items = 0;
|
|
|
|
while (si) {
|
|
while (si && (nothing_else || si->si_next)) {
|
|
if (should_relocate) {
|
|
set_key_objectid(&(si->si_ih.ih_key),
|
|
new_objectid);
|
|
moved_items++;
|
|
}
|
|
|
|
insert_item_separately(&(si->si_ih), si->si_dnm_data,
|
|
1 /*was in tree */ );
|
|
si = remove_saved_item(si);
|
|
}
|
|
if (!nothing_else)
|
|
nothing_else =
|
|
delete_N_items_after_key(&key, &si,
|
|
should_relocate
|
|
/* do not skip dir items */
|
|
, 10);
|
|
}
|
|
|
|
if (moved_items)
|
|
fsck_log("rewrite_file: %d items of file %K moved to %K\n",
|
|
moved_items, &old, &new);
|
|
|
|
}
|
|
|
|
/* file must have correct sequence of items and tail must be stored in
|
|
unformatted pointer;
|
|
pos in bh is a position of SD */
|
|
static int make_file_writeable(struct buffer_head *bh, int pos)
|
|
/* struct item_head * sd_ih, void * sd */
|
|
{
|
|
struct item_head *sd_ih = item_head(bh, pos);
|
|
struct item_head sd_ih_copy;
|
|
struct stat_data *sd = (struct stat_data *)item_body(bh, pos);
|
|
struct stat_data sd_copy;
|
|
__u64 size;
|
|
__u32 blocks;
|
|
__u16 mode;
|
|
int retval, symlink;
|
|
|
|
sd_ih_copy = *sd_ih;
|
|
memcpy(&sd_copy, sd, get_ih_item_len(sd_ih));
|
|
get_sd_mode(sd_ih, sd, &mode);
|
|
symlink = S_ISLNK(mode);
|
|
|
|
if (ih_writable(sd_ih))
|
|
return 1;
|
|
else {
|
|
mark_ih_writable(sd_ih);
|
|
mark_buffer_dirty(bh);
|
|
}
|
|
|
|
retval =
|
|
are_file_items_correct(&sd_ih_copy, &sd_copy, &size, &blocks,
|
|
0 /*do not mark accessed */ , &symlink);
|
|
if (retval == 1)
|
|
/* file looks correct */
|
|
return 1;
|
|
|
|
rewrite_file(sd_ih, 0, 0);
|
|
/*fsck_data (fs)->rebuild.rewritten ++; */
|
|
|
|
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
|
|
|
size = 0;
|
|
|
|
if (are_file_items_correct
|
|
(&sd_ih_copy, &sd_copy, &size, &blocks,
|
|
0 /*do not mark accessed */ , &symlink) == 0) {
|
|
fsck_log
|
|
("WARNING: file was rewritten but still is not correct %K\n",
|
|
&sd_ih->ih_key);
|
|
return -1;
|
|
}
|
|
|
|
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* this inserts __first__ indirect item (having k_offset == 1 and only
|
|
one unfm pointer) into tree */
|
|
static int create_first_item_of_file(struct item_head *ih, char *item,
|
|
struct reiserfs_path *path,
|
|
int was_in_tree)
|
|
{
|
|
__le32 unfm_ptr;
|
|
__le32 *ni = 0;
|
|
struct buffer_head *unbh;
|
|
struct item_head indih;
|
|
unsigned int i;
|
|
int retval;
|
|
|
|
mark_item_unreachable(&indih);
|
|
|
|
copy_key(&(indih.ih_key), &(ih->ih_key));
|
|
set_ih_item_len(&indih, UNFM_P_SIZE);
|
|
|
|
set_ih_free_space(&indih, 0);
|
|
if (get_offset(&ih->ih_key) > fs->fs_blocksize) {
|
|
/* insert indirect item containing 0 unfm pointer */
|
|
unfm_ptr = 0;
|
|
retval = 0;
|
|
} else {
|
|
if (is_direct_ih(ih)) {
|
|
/* copy direct item to new unformatted node. Save information about it */
|
|
int len = get_bytes_number(ih, fs->fs_blocksize);
|
|
|
|
unbh =
|
|
reiserfsck_get_new_buffer(PATH_PLAST_BUFFER(path)->
|
|
b_blocknr);
|
|
memset(unbh->b_data, 0, unbh->b_size);
|
|
unfm_ptr = cpu_to_le32(unbh->b_blocknr);
|
|
memcpy(unbh->b_data + get_offset(&ih->ih_key) - 1, item,
|
|
len);
|
|
|
|
set_ih_free_space(&indih, fs->fs_blocksize -
|
|
len - (get_offset(&ih->ih_key) - 1));
|
|
|
|
mark_ih_was_tail(&indih);
|
|
mark_buffer_dirty(unbh);
|
|
mark_buffer_uptodate(unbh, 1);
|
|
brelse(unbh);
|
|
|
|
retval = len;
|
|
} else {
|
|
/* take first unformatted pointer from an indirect item */
|
|
//free_sp = ih_get_free_space(0, ih, item);
|
|
|
|
set_ih_item_len(&indih, get_ih_item_len(ih));
|
|
ni = getmem(get_ih_item_len(ih));
|
|
memcpy(ni, (item), get_ih_item_len(ih));
|
|
|
|
if (!was_in_tree) {
|
|
for (i = 0; i < I_UNFM_NUM(ih); i++) {
|
|
if (still_bad_unfm_ptr_2
|
|
(d32_get(ni, i)))
|
|
reiserfs_panic
|
|
("create_first_item_of_file: The file %K has a pointer to the bad block (%u)",
|
|
&ih->ih_key, d32_get(ni,
|
|
i));
|
|
mark_block_used(d32_get(ni, i), 0);
|
|
}
|
|
}
|
|
|
|
retval =
|
|
fs->fs_blocksize * get_ih_item_len(ih) /
|
|
UNFM_P_SIZE;
|
|
}
|
|
}
|
|
|
|
set_ih_key_format(&indih, get_ih_key_format(ih));
|
|
//ih_version(&indih) = ih_version(ih);
|
|
set_offset(key_format(&(ih->ih_key)), &indih.ih_key, 1);
|
|
set_type(key_format(&(ih->ih_key)), &indih.ih_key, TYPE_INDIRECT);
|
|
|
|
if (ni) {
|
|
reiserfsck_insert_item(path, &indih, (const char *)ni);
|
|
freemem(ni);
|
|
} else {
|
|
reiserfsck_insert_item(path, &indih, (const char *)&unfm_ptr);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* path points to first part of tail. Function copies file tail into unformatted node and returns
|
|
its block number. */
|
|
/* we convert direct item that is on the path to indirect. we need a number of free block for
|
|
unformatted node. reiserfs_new_blocknrs will start from block number returned by this function */
|
|
static unsigned long block_to_start(struct reiserfs_path *path)
|
|
{
|
|
struct buffer_head *bh;
|
|
struct item_head *ih;
|
|
__u32 blk;
|
|
|
|
bh = PATH_PLAST_BUFFER(path);
|
|
ih = tp_item_head(path);
|
|
if (get_offset(&ih->ih_key) == 1 || PATH_LAST_POSITION(path) == 0)
|
|
return bh->b_blocknr;
|
|
|
|
ih--;
|
|
blk = d32_get((__le32 *) ih_item_body(bh, ih), I_UNFM_NUM(ih) - 1);
|
|
return (blk ? blk : bh->b_blocknr);
|
|
}
|
|
|
|
static void direct2indirect2(unsigned long unfm, struct reiserfs_path *path)
|
|
{
|
|
struct item_head *ih;
|
|
struct reiserfs_key key;
|
|
struct buffer_head *unbh;
|
|
__le32 ni;
|
|
int copied = 0;
|
|
int file_format;
|
|
|
|
ih = tp_item_head(path);
|
|
copy_key(&key, &(ih->ih_key));
|
|
|
|
file_format = key_format(&key);
|
|
|
|
if (get_offset(&key) % fs->fs_blocksize != 1) {
|
|
/* look for first part of tail */
|
|
pathrelse(path);
|
|
set_offset(file_format, &key,
|
|
(get_offset(&key) & ~(fs->fs_blocksize - 1)) + 1);
|
|
if (reiserfs_search_by_key_4(fs, &key, path) != ITEM_FOUND)
|
|
die("direct2indirect: can not find first part of tail");
|
|
}
|
|
|
|
unbh = reiserfsck_get_new_buffer(unfm ? unfm : block_to_start(path));
|
|
memset(unbh->b_data, 0, unbh->b_size);
|
|
|
|
/* delete parts of tail coping their contents to new buffer */
|
|
do {
|
|
__u64 len;
|
|
|
|
ih = tp_item_head(path);
|
|
|
|
len = get_bytes_number(ih, fs->fs_blocksize);
|
|
|
|
memcpy(unbh->b_data + copied,
|
|
ih_item_body(PATH_PLAST_BUFFER(path), ih), len);
|
|
|
|
// save_unfm_overwriting (unbh->b_blocknr, ih);
|
|
copied += len;
|
|
set_offset(file_format, &key, get_offset(&key) + len);
|
|
|
|
reiserfsck_delete_item(path, 0);
|
|
|
|
} while (reiserfs_search_by_key_4(fs, &key, path) == ITEM_FOUND);
|
|
|
|
pathrelse(path);
|
|
|
|
/* paste or insert pointer to the unformatted node */
|
|
set_offset(file_format, &key, get_offset(&key) - copied);
|
|
// set_offset (ih_key_format (ih), &key, get_offset (&key) - copied);
|
|
// key.k_offset -= copied;
|
|
ni = cpu_to_le32(unbh->b_blocknr);
|
|
|
|
if (reiserfs_search_by_position(fs, &key, file_format, path) == FILE_NOT_FOUND) {
|
|
struct item_head insih;
|
|
|
|
copy_key(&(insih.ih_key), &key);
|
|
set_ih_key_format(&insih, file_format);
|
|
set_type(get_ih_key_format(&insih), &insih.ih_key,
|
|
TYPE_INDIRECT);
|
|
set_ih_free_space(&insih, 0);
|
|
mark_item_unreachable(&insih);
|
|
set_ih_item_len(&insih, UNFM_P_SIZE);
|
|
mark_ih_was_tail(&insih);
|
|
reiserfsck_insert_item(path, &insih, (const char *)&(ni));
|
|
} else {
|
|
ih = tp_item_head(path);
|
|
|
|
if (!is_indirect_ih(ih))
|
|
reiserfs_panic
|
|
("%s: The item, which is supposed to be deleted, found in the tree - %k",
|
|
__FUNCTION__, &ih->ih_key);
|
|
if (get_bytes_number(ih, fs->fs_blocksize) +
|
|
get_offset(&ih->ih_key) != get_offset(&key))
|
|
reiserfs_panic
|
|
("%s: The item %k with the wrong offset found in the block %lu block",
|
|
__FUNCTION__, &ih->ih_key,
|
|
PATH_PLAST_BUFFER(path)->b_blocknr);
|
|
|
|
mark_ih_was_tail(ih);
|
|
mark_buffer_dirty(get_bh(path));
|
|
reiserfsck_paste_into_item(path, (const char *)&ni,
|
|
UNFM_P_SIZE);
|
|
}
|
|
|
|
mark_buffer_dirty(unbh);
|
|
mark_buffer_uptodate(unbh, 1);
|
|
brelse(unbh);
|
|
|
|
if (reiserfs_search_by_position(fs, &key, file_format, path) != POSITION_FOUND
|
|
|| !is_indirect_ih(tp_item_head(path)))
|
|
reiserfs_panic
|
|
("direct2indirect: The data %k, which are supposed to be converted, are not found",
|
|
&key);
|
|
return;
|
|
}
|
|
|
|
static int append_to_unformatted_node(struct item_head *comingih,
|
|
struct item_head *ih, char *item,
|
|
struct reiserfs_path *path,
|
|
unsigned int coming_len)
|
|
{
|
|
struct buffer_head *bh, *unbh = NULL;
|
|
__u64 end_of_data, free_space;
|
|
__u16 offset = get_offset(&comingih->ih_key) % fs->fs_blocksize - 1;
|
|
__u32 unfm_ptr;
|
|
int zero_number;
|
|
|
|
bh = PATH_PLAST_BUFFER(path);
|
|
unfm_ptr = d32_get((__le32 *) ih_item_body(bh, ih), I_UNFM_NUM(ih) - 1);
|
|
|
|
/* append to free space of the last unformatted node of indirect item ih */
|
|
free_space =
|
|
get_offset(&ih->ih_key) + fs->fs_blocksize * I_UNFM_NUM(ih) -
|
|
get_offset(&comingih->ih_key);
|
|
|
|
if (free_space < coming_len)
|
|
reiserfs_panic
|
|
("%s: block %lu: The unformatted node %u, pointed by the file %k, does not have enough free space"
|
|
"for appending %llu bytes", __FUNCTION__, bh->b_blocknr,
|
|
unfm_ptr, &ih->ih_key, coming_len);
|
|
|
|
if (fs->fs_blocksize < free_space)
|
|
reiserfs_panic
|
|
("%s: block %lu: The unformatted node %u, pointed by the file %k, does not have enough free space"
|
|
"for appending %llu bytes", __FUNCTION__, bh->b_blocknr,
|
|
&ih->ih_key, unfm_ptr, &ih->ih_key, coming_len);
|
|
|
|
end_of_data = fs->fs_blocksize - free_space;
|
|
zero_number = offset - end_of_data;
|
|
|
|
if (unfm_ptr && !not_data_block(fs, unfm_ptr)) {
|
|
unbh = bread(fs->fs_dev, unfm_ptr, fs->fs_blocksize);
|
|
if (!is_block_used(unfm_ptr))
|
|
reiserfs_panic
|
|
("%s: block %lu: The unformatted node %u, pointed by the file %k, is marked as unused",
|
|
__FUNCTION__, bh->b_blocknr, unfm_ptr,
|
|
&ih->ih_key);
|
|
|
|
if (unbh == 0)
|
|
unfm_ptr = 0;
|
|
}
|
|
|
|
if (!unfm_ptr || not_data_block(fs, unfm_ptr)) {
|
|
/* indirect item points to block which can not be pointed or to 0, in
|
|
any case we have to allocate new node */
|
|
/*if (unfm_ptr == 0 || unfm_ptr >= SB_BLOCK_COUNT (fs)) { */
|
|
unbh = reiserfsck_get_new_buffer(bh->b_blocknr);
|
|
memset(unbh->b_data, 0, unbh->b_size);
|
|
d32_put((__le32 *) ih_item_body(bh, ih), I_UNFM_NUM(ih) - 1,
|
|
unbh->b_blocknr);
|
|
/*mark_block_unformatted (unbh->b_blocknr); */
|
|
mark_buffer_dirty(bh);
|
|
}
|
|
memset(unbh->b_data + end_of_data, 0, zero_number);
|
|
memcpy(unbh->b_data + offset, item, coming_len);
|
|
|
|
// save_unfm_overwriting (unbh->b_blocknr, comingih);
|
|
|
|
free_space -= (zero_number + coming_len);
|
|
set_ih_free_space(ih,
|
|
get_ih_free_space(ih) - (zero_number + coming_len));
|
|
memset(unbh->b_data + offset + coming_len, 0, free_space);
|
|
// mark_buffer_uptodate (unbh, 0);
|
|
mark_buffer_uptodate(unbh, 1);
|
|
mark_buffer_dirty(unbh);
|
|
brelse(unbh);
|
|
pathrelse(path);
|
|
return coming_len;
|
|
}
|
|
|
|
/* this appends file with one unformatted node pointer (since balancing
|
|
algorithm limitation). This pointer can be 0, or new allocated block or
|
|
pointer from indirect item that is being inserted into tree */
|
|
int reiserfsck_append_file(struct item_head *comingih, char *item, int pos,
|
|
struct reiserfs_path *path, int was_in_tree)
|
|
{
|
|
__le32 *ni;
|
|
struct buffer_head *unbh;
|
|
int retval;
|
|
struct item_head *ih = tp_item_head(path);
|
|
__u32 bytes_number;
|
|
int i, count = 0;
|
|
|
|
if (!is_indirect_ih(ih))
|
|
reiserfs_panic
|
|
("reiserfsck_append_file: Operation is not allowed for non-indirect item %k",
|
|
&ih->ih_key);
|
|
|
|
if (is_direct_ih(comingih)) {
|
|
unsigned int coming_len =
|
|
get_bytes_number(comingih, fs->fs_blocksize);
|
|
|
|
if (get_offset(&comingih->ih_key) <
|
|
get_offset(&ih->ih_key) +
|
|
fs->fs_blocksize * I_UNFM_NUM(ih)) {
|
|
/* direct item fits to free space of indirect item */
|
|
return append_to_unformatted_node(comingih, ih, item,
|
|
path, coming_len);
|
|
}
|
|
|
|
unbh =
|
|
reiserfsck_get_new_buffer(PATH_PLAST_BUFFER(path)->
|
|
b_blocknr);
|
|
memset(unbh->b_data, 0, unbh->b_size);
|
|
memcpy(unbh->b_data +
|
|
get_offset(&comingih->ih_key) % unbh->b_size - 1, item,
|
|
coming_len);
|
|
|
|
mark_buffer_dirty(unbh);
|
|
mark_buffer_uptodate(unbh, 1);
|
|
|
|
ni = getmem(UNFM_P_SIZE);
|
|
d32_put(ni, 0, unbh->b_blocknr);
|
|
count = 1;
|
|
|
|
brelse(unbh);
|
|
retval = coming_len;
|
|
} else {
|
|
/* coming item is indirect item */
|
|
|
|
bytes_number = get_bytes_number(ih, fs->fs_blocksize);
|
|
if (get_offset(&comingih->ih_key) + pos * fs->fs_blocksize !=
|
|
get_offset(&ih->ih_key) + bytes_number)
|
|
reiserfs_panic
|
|
("reiserfsck_append_file: file %K: Cannot append indirect pointers of the offset "
|
|
"(%LLu) at the position %LLu\n", &comingih->ih_key,
|
|
get_offset(&comingih->ih_key) +
|
|
pos * fs->fs_blocksize,
|
|
get_offset(&ih->ih_key) + bytes_number);
|
|
|
|
/* take unformatted pointer from an indirect item */
|
|
count = I_UNFM_NUM(comingih) - pos;
|
|
ni = getmem(count * UNFM_P_SIZE);
|
|
memcpy(ni, (item + pos * UNFM_P_SIZE), count * UNFM_P_SIZE);
|
|
|
|
if (!was_in_tree) {
|
|
for (i = 0; i < count; i++) {
|
|
if (still_bad_unfm_ptr_2(d32_get(ni, i)))
|
|
die("reiserfsck_append_file: Trying to insert a pointer to illegal block (%u)", d32_get(ni, i));
|
|
mark_block_used(d32_get(ni, i), 0);
|
|
}
|
|
}
|
|
|
|
retval = fs->fs_blocksize * count;
|
|
}
|
|
|
|
reiserfsck_paste_into_item(path, (const char *)ni, count * UNFM_P_SIZE);
|
|
freemem(ni);
|
|
return retval;
|
|
}
|
|
|
|
long long int must_there_be_a_hole(struct item_head *comingih,
|
|
struct reiserfs_path *path)
|
|
{
|
|
struct item_head *ih = tp_item_head(path);
|
|
|
|
if (is_direct_ih(ih)) {
|
|
direct2indirect2(0, path);
|
|
ih = tp_item_head(path);
|
|
}
|
|
|
|
path->pos_in_item = I_UNFM_NUM(ih);
|
|
|
|
return (get_offset(&comingih->ih_key) -
|
|
get_offset(&ih->ih_key)) / fs->fs_blocksize - I_UNFM_NUM(ih);
|
|
/*
|
|
if (get_offset (&ih->ih_key) + (I_UNFM_NUM (ih) + 1) * fs->fs_blocksize <= get_offset (&comingih->ih_key))
|
|
return 1;
|
|
|
|
return 0;*/
|
|
}
|
|
|
|
int reiserfs_append_zero_unfm_ptr(struct reiserfs_path *path,
|
|
long long int p_count)
|
|
{
|
|
__le32 *ni;
|
|
long long int count;
|
|
|
|
if (is_direct_ih(tp_item_head(path)))
|
|
/* convert direct item to indirect */
|
|
direct2indirect2(0, path);
|
|
|
|
count = MAX_INDIRECT_ITEM_LEN(fs->fs_blocksize) / UNFM_P_SIZE;
|
|
|
|
if (p_count <= count)
|
|
count = p_count;
|
|
|
|
ni = getmem(count * UNFM_P_SIZE);
|
|
memset(ni, 0, count * UNFM_P_SIZE);
|
|
|
|
reiserfsck_paste_into_item(path, (const char *)ni, count * UNFM_P_SIZE);
|
|
freemem(ni);
|
|
return 0;
|
|
}
|
|
|
|
/* write direct item to unformatted node */
|
|
/* coming item is direct */
|
|
static int overwrite_by_direct_item(struct item_head *comingih, char *item,
|
|
struct reiserfs_path *path)
|
|
{
|
|
__u32 unfm_ptr;
|
|
struct buffer_head *unbh, *bh;
|
|
struct item_head *ih;
|
|
int offset;
|
|
__u64 coming_len = get_bytes_number(comingih, fs->fs_blocksize);
|
|
|
|
bh = PATH_PLAST_BUFFER(path);
|
|
ih = tp_item_head(path);
|
|
|
|
unfm_ptr = d32_get((__le32 *) ih_item_body(bh, ih), path->pos_in_item);
|
|
unbh = 0;
|
|
|
|
if (unfm_ptr != 0 && unfm_ptr < get_sb_block_count(fs->fs_ondisk_sb)) {
|
|
/**/ unbh = bread(fs->fs_dev, unfm_ptr, bh->b_size);
|
|
if (!is_block_used(unfm_ptr))
|
|
die("overwrite_by_direct_item: block %lu, item %d, pointer %d: The pointed block" "(%u) being overwritten is marked as unused.", bh->b_blocknr, PATH_LAST_POSITION(path), path->pos_in_item, unfm_ptr);
|
|
if (unbh == 0)
|
|
unfm_ptr = 0;
|
|
}
|
|
if (unfm_ptr == 0 || unfm_ptr >= get_sb_block_count(fs->fs_ondisk_sb)) {
|
|
if ((unbh = reiserfsck_get_new_buffer(bh->b_blocknr)) != NULL) {
|
|
memset(unbh->b_data, 0, unbh->b_size);
|
|
d32_put((__le32 *) ih_item_body(bh, ih), path->pos_in_item,
|
|
unbh->b_blocknr);
|
|
mark_buffer_dirty(bh);
|
|
} else {
|
|
die("overwrite_by_direct_item: Could not allocate a new block for new data");
|
|
}
|
|
}
|
|
|
|
offset = (get_offset(&comingih->ih_key) % bh->b_size) - 1;
|
|
if (offset + coming_len > bh->b_size)
|
|
die("overwrite_by_direct_item: The length of the file after insertion (offset=%lu, length=%u)" "will exceed the maximal possible length.", (long unsigned)get_offset(&comingih->ih_key), (unsigned)coming_len);
|
|
|
|
memcpy(unbh->b_data + offset, item, coming_len);
|
|
|
|
// save_unfm_overwriting (unbh->b_blocknr, comingih);
|
|
|
|
if ((path->pos_in_item == (I_UNFM_NUM(ih) - 1)) &&
|
|
(bh->b_size - 0 /*ih_free_space (ih) */ ) < (offset + coming_len)) {
|
|
set_ih_free_space(ih, bh->b_size - (offset + coming_len));
|
|
mark_buffer_dirty(bh);
|
|
}
|
|
mark_buffer_dirty(unbh);
|
|
// mark_buffer_uptodate (unbh, 0);
|
|
mark_buffer_uptodate(unbh, 1);
|
|
brelse(unbh);
|
|
return coming_len;
|
|
}
|
|
|
|
#if 0
|
|
|
|
void overwrite_unfm_by_unfm(unsigned long unfm_in_tree,
|
|
unsigned long coming_unfm, int bytes_in_unfm)
|
|
{
|
|
struct overwritten_unfm_segment *unfm_os_list; /* list of overwritten segments of the unformatted node */
|
|
struct overwritten_unfm_segment unoverwritten_segment;
|
|
struct buffer_head *bh_in_tree, *coming_bh;
|
|
|
|
if (!test_bit
|
|
(coming_unfm % (fs->fs_blocksize * 8),
|
|
SB_AP_BITMAP(fs)[coming_unfm / (fs->fs_blocksize * 8)]->b_data))
|
|
/* block (pointed by indirect item) is free, we do not have to keep its contents */
|
|
return;
|
|
|
|
/* coming block is marked as used in disk bitmap. Put its contents to
|
|
block in tree preserving everything, what has been overwritten there by
|
|
direct items */
|
|
unfm_os_list =
|
|
find_overwritten_unfm(unfm_in_tree, bytes_in_unfm,
|
|
&unoverwritten_segment);
|
|
if (unfm_os_list) {
|
|
/* add_event (UNFM_OVERWRITING_UNFM); */
|
|
bh_in_tree = bread(fs->fs_dev, unfm_in_tree, fs->fs_blocksize);
|
|
coming_bh = bread(fs->fs_dev, coming_unfm, fs->fs_blocksize);
|
|
if (bh_in_tree == 0 || coming_bh == 0)
|
|
return;
|
|
|
|
while (get_unoverwritten_segment
|
|
(unfm_os_list, &unoverwritten_segment)) {
|
|
if (unoverwritten_segment.ous_begin < 0
|
|
|| unoverwritten_segment.ous_end > bytes_in_unfm - 1
|
|
|| unoverwritten_segment.ous_begin >
|
|
unoverwritten_segment.ous_end)
|
|
die("overwrite_unfm_by_unfm: invalid segment found (%d %d)", unoverwritten_segment.ous_begin, unoverwritten_segment.ous_end);
|
|
|
|
memcpy(bh_in_tree->b_data +
|
|
unoverwritten_segment.ous_begin,
|
|
coming_bh->b_data +
|
|
unoverwritten_segment.ous_begin,
|
|
unoverwritten_segment.ous_end -
|
|
unoverwritten_segment.ous_begin + 1);
|
|
mark_buffer_dirty(bh_in_tree);
|
|
}
|
|
|
|
brelse(bh_in_tree);
|
|
brelse(coming_bh);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* put unformatted node pointers from incoming item over the in-tree ones */
|
|
static int overwrite_by_indirect_item(struct item_head *comingih,
|
|
__le32 * coming_item,
|
|
struct reiserfs_path *path,
|
|
int *pos_in_coming_item)
|
|
{
|
|
struct buffer_head *bh = PATH_PLAST_BUFFER(path);
|
|
struct item_head *ih = tp_item_head(path);
|
|
int written;
|
|
__le32 *item_in_tree;
|
|
int src_unfm_ptrs, dest_unfm_ptrs, to_copy, i, dirty = 0;
|
|
|
|
item_in_tree = (__le32 *) ih_item_body(bh, ih) + path->pos_in_item;
|
|
coming_item += *pos_in_coming_item;
|
|
|
|
dest_unfm_ptrs = I_UNFM_NUM(ih) - path->pos_in_item;
|
|
src_unfm_ptrs = I_UNFM_NUM(comingih) - *pos_in_coming_item;
|
|
|
|
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 = get_bytes_number(comingih, fs->fs_blocksize) -
|
|
/* free_sp - */ *pos_in_coming_item * fs->fs_blocksize;
|
|
*pos_in_coming_item = I_UNFM_NUM(comingih);
|
|
to_copy = src_unfm_ptrs;
|
|
if (dest_unfm_ptrs == src_unfm_ptrs)
|
|
set_ih_free_space(ih, 0 /* free_sp */ );
|
|
} else {
|
|
/* only part of coming item overlaps item in the tree */
|
|
*pos_in_coming_item += dest_unfm_ptrs;
|
|
written = dest_unfm_ptrs * fs->fs_blocksize;
|
|
to_copy = dest_unfm_ptrs;
|
|
set_ih_free_space(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));
|
|
mark_block_used(d32_get(coming_item, i), 0);
|
|
dirty++;
|
|
}
|
|
}
|
|
if (dirty)
|
|
mark_buffer_dirty(bh);
|
|
return written;
|
|
}
|
|
|
|
static int reiserfsck_overwrite_file(struct item_head *comingih, char *item,
|
|
struct reiserfs_path *path,
|
|
int *pos_in_coming_item, int was_in_tree)
|
|
{
|
|
__u32 unfm_ptr;
|
|
int written = 0;
|
|
struct item_head *ih = tp_item_head(path);
|
|
|
|
if (not_of_one_file(ih, &(comingih->ih_key)))
|
|
reiserfs_panic
|
|
("reiserfsck_overwrite_file: The file to be overwritten %K must be of"
|
|
" the same as the new data %K", &ih->ih_key,
|
|
&comingih->ih_key);
|
|
|
|
if (is_direct_ih(ih)) {
|
|
unfm_ptr = 0;
|
|
if (is_indirect_ih(comingih)) {
|
|
if (get_offset(&ih->ih_key) % fs->fs_blocksize != 1)
|
|
reiserfs_panic
|
|
("reiserfsck_overwrite_file: The second part of the tail %k can not"
|
|
" be overwritten by indirect item %k",
|
|
&ih->ih_key, &comingih->ih_key);
|
|
/* use pointer from coming indirect item */
|
|
unfm_ptr = d32_get((__le32 *) item, *pos_in_coming_item);
|
|
if (!was_in_tree) {
|
|
if (still_bad_unfm_ptr_2(unfm_ptr))
|
|
die("reiserfsck_overwrite_file: The pointer to the unformatted block (%u)" " points to the bad area.", unfm_ptr);
|
|
}
|
|
}
|
|
/* */
|
|
direct2indirect2(unfm_ptr, path);
|
|
}
|
|
if (is_direct_ih(comingih)) {
|
|
written = overwrite_by_direct_item(comingih, item, path);
|
|
} else {
|
|
if (was_in_tree)
|
|
reiserfs_panic
|
|
("reiserfsck_overwrite_file: Item %k we are going to overwrite with"
|
|
" %k cannot not be in the tree yet", &ih->ih_key,
|
|
&comingih->ih_key);
|
|
written =
|
|
overwrite_by_indirect_item(comingih, (__le32 *) item, path,
|
|
pos_in_coming_item);
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
/*
|
|
*/
|
|
int reiserfsck_file_write(struct item_head *ih, char *item, int was_in_tree)
|
|
{
|
|
struct reiserfs_path path;
|
|
int count, pos_in_coming_item;
|
|
long long int retval, written;
|
|
struct reiserfs_key key;
|
|
int file_format = KEY_FORMAT_UNDEFINED;
|
|
int relocated = 0;
|
|
|
|
if (!was_in_tree) {
|
|
__u16 mode;
|
|
|
|
/* We already inserted all SD items. If we cannot find SD of this item - skip it */
|
|
memset(&key, 0, sizeof(key));
|
|
|
|
check_again:
|
|
copy_short_key(&key, &(ih->ih_key));
|
|
|
|
if (reiserfs_search_by_key_4(fs, &key, &path) != ITEM_FOUND) {
|
|
fsck_log
|
|
("vpf-10260: The file we are inserting the new item (%H) into has no"
|
|
" StatData, insertion was skipped\n", ih);
|
|
pathrelse(&path);
|
|
return 0;
|
|
}
|
|
|
|
/*SD found */
|
|
file_format = get_ih_key_format(tp_item_head(&path));
|
|
get_sd_mode(tp_item_head(&path), tp_item_body(&path), &mode);
|
|
|
|
if (file_format != get_ih_key_format(ih)) {
|
|
/* Not for symlinks and not for items which should be relocted. */
|
|
if (((S_ISDIR(mode) && is_direntry_ih(ih)) ||
|
|
(!S_ISDIR(mode) && !is_direntry_ih(ih))) &&
|
|
!S_ISLNK(mode)) {
|
|
set_type_and_offset(file_format, &ih->ih_key,
|
|
get_offset(&ih->ih_key),
|
|
get_type(&ih->ih_key));
|
|
set_ih_key_format(ih, file_format);
|
|
}
|
|
}
|
|
|
|
if (!relocated && should_relocate(ih)) {
|
|
rewrite_file(ih, 1, 1 /*change new_ih */ );
|
|
pathrelse(&path);
|
|
relocated = 1;
|
|
|
|
/* object has been relocated but we should not mark it as used in semantic map,
|
|
as it does not exist at pass2 and we do not get here for relocation as
|
|
was_in_tree == 1 */
|
|
|
|
goto check_again;
|
|
}
|
|
|
|
if (make_file_writeable(get_bh(&path), get_item_pos(&path)) ==
|
|
-1) {
|
|
/* write was not completed. Skip that item. Maybe it should be saved to lost_found */
|
|
fsck_log
|
|
("reiserfsck_file_write: WARNING: The file we are inserting the new item %k into was"
|
|
" not recovered and is still in inconsistent state, insertion was skipped\n",
|
|
&ih->ih_key);
|
|
pathrelse(&path);
|
|
return 0;
|
|
}
|
|
|
|
pathrelse(&path);
|
|
}
|
|
|
|
count = get_bytes_number(ih, fs->fs_blocksize);
|
|
pos_in_coming_item = 0;
|
|
|
|
copy_key(&key, &(ih->ih_key));
|
|
|
|
while (count) {
|
|
|
|
retval = reiserfs_search_by_position(fs, &key, key_format(&key), &path);
|
|
|
|
/* if there are items of bigger offset than we are looking for and
|
|
there is no item between wamted offset and SD, insert first item */
|
|
if (retval == POSITION_NOT_FOUND &&
|
|
(PATH_LAST_POSITION(&path) >= B_NR_ITEMS(get_bh(&path)) ||
|
|
get_offset(&tp_item_head(&path)->ih_key) > get_offset(&key)))
|
|
retval = FILE_NOT_FOUND;
|
|
|
|
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);
|
|
|
|
if (retval == POSITION_FOUND) {
|
|
written =
|
|
reiserfsck_overwrite_file(ih, item, &path,
|
|
&pos_in_coming_item,
|
|
was_in_tree);
|
|
count -= written;
|
|
set_offset(key_format(&key), &key,
|
|
get_offset(&key) + written);
|
|
}
|
|
if (retval == FILE_NOT_FOUND) {
|
|
written =
|
|
create_first_item_of_file(ih, item, &path,
|
|
was_in_tree);
|
|
count -= written;
|
|
|
|
set_offset(key_format(&key), &key,
|
|
get_offset(&key) + written);
|
|
}
|
|
if (retval == POSITION_NOT_FOUND) {
|
|
|
|
if (is_direct_ih(ih)) {
|
|
mark_ih_was_tail(tp_item_head(&path));
|
|
mark_buffer_dirty(get_bh(&path));
|
|
}
|
|
|
|
if ((written = must_there_be_a_hole(ih, &path)) > 0) {
|
|
reiserfs_append_zero_unfm_ptr(&path, written);
|
|
} else {
|
|
written =
|
|
reiserfsck_append_file(ih, item,
|
|
pos_in_coming_item,
|
|
&path, was_in_tree);
|
|
count -= written;
|
|
set_offset(key_format(&key), &key,
|
|
get_offset(&key) + written);
|
|
pos_in_coming_item +=
|
|
written / fs->fs_blocksize;
|
|
}
|
|
}
|
|
if (count < 0)
|
|
reiserfs_panic
|
|
("reiserfsck_file_write: We wrote into the file %K more bytes than needed - count (%d) < 0.",
|
|
&key, count);
|
|
pathrelse(&path);
|
|
}
|
|
|
|
/* This is a test for writing into the file. If not sure that file data are consistent after
|
|
reiserfsck_file_write - uncomment this clause: */
|
|
|
|
/* if (!was_in_tree && are_file_items_correct (&ih->ih_key,
|
|
(file_format == KEY_FORMAT_UNDEFINED) ? get_ih_key_format (ih) : file_format,
|
|
&size, &blocks, 0, symlink, 0) == 0)
|
|
reiserfs_panic ("reiserfsck_file_write: item was not inserted properly\n");*/
|
|
|
|
return get_bytes_number(ih, fs->fs_blocksize);
|
|
}
|
|
|
|
void one_more_corruption(reiserfs_filsys_t fs, int kind)
|
|
{
|
|
if (kind == FATAL)
|
|
fsck_check_stat(fs)->fatal_corruptions++;
|
|
else if (kind == FIXABLE)
|
|
fsck_check_stat(fs)->fixable_corruptions++;
|
|
}
|
|
|
|
void one_less_corruption(reiserfs_filsys_t fs, int kind)
|
|
{
|
|
if (kind == FATAL)
|
|
fsck_check_stat(fs)->fatal_corruptions--;
|
|
else if (kind == FIXABLE)
|
|
fsck_check_stat(fs)->fixable_corruptions--;
|
|
}
|