blob: cdbdb3de57db76aebef80cc7dfcf35a929d9da12 [file] [log] [blame]
/*
* Copyright 2000-2004 by Hans Reiser, licensing governed by
* reiserfsprogs/README
*/
#include "resize.h"
#include <time.h>
static unsigned long int_node_cnt = 0, int_moved_cnt = 0;
static unsigned long leaf_node_cnt = 0, leaf_moved_cnt = 0;
static unsigned long unfm_node_cnt = 0, unfm_moved_cnt = 0;
static unsigned long total_node_cnt = 0;
static unsigned long total_moved_cnt = 0;
static unsigned long unused_block;
static unsigned long blocks_used;
static int block_count_mismatch = 0;
static reiserfs_bitmap_t *bmp;
static struct reiserfs_super_block *ondisk_sb;
/* abnornal exit from block reallocation process */
static void quit_resizer(reiserfs_filsys_t fs)
{
/* save changes to bitmap blocks */
reiserfs_close(fs);
/* leave fs in ERROR state */
reiserfs_exit(1, "fs shrinking was not completed successfully, "
"run reiserfsck.");
}
/* block moving */
static unsigned long move_generic_block(reiserfs_filsys_t fs,
unsigned long block,
unsigned long bnd, int h)
{
struct buffer_head *bh, *bh2;
/* primitive fsck */
if (block > get_sb_block_count(ondisk_sb)) {
fprintf(stderr, "resize_reiserfs: invalid block number "
"(%lu) found.\n", block);
quit_resizer(fs);
}
/* progress bar, 3D style :) */
if (opt_verbose)
print_how_far(stderr, &total_node_cnt, blocks_used, 1, 0);
else
total_node_cnt++;
/* infinite loop check */
if (total_node_cnt > blocks_used && !block_count_mismatch) {
fputs("resize_reiserfs: warning: block count exeeded\n",
stderr);
block_count_mismatch = 1;
}
if (block < bnd) /* block will not be moved */
return 0;
/* move wrong block */
bh = bread(fs->fs_dev, block, fs->fs_blocksize);
if (!bh)
reiserfs_exit(1, "move_generic_block: bread failed.\n");
reiserfs_bitmap_find_zero_bit(bmp, &unused_block);
if (unused_block == 0 || unused_block >= bnd) {
fputs("resize_reiserfs: can\'t find free block\n", stderr);
quit_resizer(fs);
}
/* blocknr changing */
bh2 = getblk(fs->fs_dev, unused_block, fs->fs_blocksize);
memcpy(bh2->b_data, bh->b_data, bh2->b_size);
reiserfs_bitmap_clear_bit(bmp, block);
reiserfs_bitmap_set_bit(bmp, unused_block);
brelse(bh);
mark_buffer_uptodate(bh2, 1);
mark_buffer_dirty(bh2);
bwrite(bh2);
brelse(bh2);
total_moved_cnt++;
return unused_block;
}
static unsigned long move_unformatted_block(reiserfs_filsys_t fs,
unsigned long block,
unsigned long bnd, int h)
{
unsigned long b;
unfm_node_cnt++;
b = move_generic_block(fs, block, bnd, h);
if (b)
unfm_moved_cnt++;
return b;
}
/* recursive function processing all tree nodes */
static unsigned long move_formatted_block(reiserfs_filsys_t fs,
unsigned long block,
unsigned long bnd, int h)
{
struct buffer_head *bh;
struct item_head *ih;
unsigned long new_blocknr = 0;
int node_is_internal = 0;
unsigned int i, j;
bh = bread(fs->fs_dev, block, fs->fs_blocksize);
if (!bh)
reiserfs_exit(1, "move_formatted_block: bread failed");
if (is_leaf_node(bh)) {
leaf_node_cnt++;
for (i = 0; i < B_NR_ITEMS(bh); i++) {
ih = item_head(bh, i);
/* skip the bad blocks. */
if (get_key_objectid(&ih->ih_key) == BADBLOCK_OBJID &&
get_key_dirid(&ih->ih_key) == BADBLOCK_DIRID)
continue;
if (is_indirect_ih(ih)) {
__le32 *indirect;
indirect = (__le32 *) ih_item_body(bh, ih);
for (j = 0; j < I_UNFM_NUM(ih); j++) {
unsigned long unfm_block;
if (d32_get(indirect, j) == 0) /* hole */
continue;
unfm_block =
move_unformatted_block(fs,
d32_get
(indirect,
j), bnd,
h + 1);
if (unfm_block) {
d32_put(indirect, j,
unfm_block);
mark_buffer_dirty(bh);
}
}
}
}
} else if (is_internal_node(bh)) { /* internal node */
int_node_cnt++;
node_is_internal = 1;
for (i = 0; i <= B_NR_ITEMS(bh); i++) {
unsigned long moved_block;
moved_block =
move_formatted_block(fs,
get_dc_child_blocknr(B_N_CHILD
(bh, i)),
bnd, h + 1);
if (moved_block) {
set_dc_child_blocknr(B_N_CHILD(bh, i),
moved_block);
mark_buffer_dirty(bh);
}
}
} else {
DIE("block (%lu) has invalid format\n", block);
}
if (buffer_dirty(bh)) {
mark_buffer_uptodate(bh, 1);
bwrite(bh);
}
brelse(bh);
new_blocknr = move_generic_block(fs, block, bnd, h);
if (new_blocknr) {
if (node_is_internal)
int_moved_cnt++;
else
leaf_moved_cnt++;
}
return new_blocknr;
}
int shrink_fs(reiserfs_filsys_t fs, long long int blocks)
{
unsigned long n_root_block;
unsigned int bmap_nr_new;
unsigned long bad_count;
ondisk_sb = fs->fs_ondisk_sb;
bmap_nr_new = (blocks - 1) / (8 * fs->fs_blocksize) + 1;
/* is shrinking possible ? */
if (get_sb_block_count(ondisk_sb) - blocks >
get_sb_free_blocks(ondisk_sb) + reiserfs_fs_bmap_nr(fs) -
bmap_nr_new) {
fprintf(stderr,
"resize_reiserfs: can\'t shrink fs; too many "
"blocks already allocated\n");
return -1;
}
/* warn about alpha version */
{
int c;
printf("You are running BETA version of reiserfs shrinker.\n"
"This version is only for testing or VERY CAREFUL use.\n"
"Backup of you data is recommended.\n\n"
"Do you want to continue? [y/N]:");
fflush(stdout);
c = getchar();
if (c != 'y' && c != 'Y')
exit(1);
}
reiserfs_reopen(fs, O_RDWR);
if (reiserfs_open_ondisk_bitmap(fs))
reiserfs_exit(1, "cannot open ondisk bitmap");
bmp = fs->fs_bitmap2;
ondisk_sb = fs->fs_ondisk_sb;
set_sb_fs_state(fs->fs_ondisk_sb, FS_ERROR);
mark_buffer_uptodate(fs->fs_super_bh, 1);
mark_buffer_dirty(fs->fs_super_bh);
bwrite(fs->fs_super_bh);
/* calculate number of data blocks */
blocks_used = get_sb_block_count(fs->fs_ondisk_sb)
- get_sb_free_blocks(fs->fs_ondisk_sb)
- reiserfs_fs_bmap_nr(fs)
- get_jp_journal_size(sb_jp(fs->fs_ondisk_sb))
- REISERFS_DISK_OFFSET_IN_BYTES / fs->fs_blocksize - 2; /* superblock itself and 1 descriptor after the journal */
unused_block = 1;
if (opt_verbose) {
printf("Processing the tree: ");
fflush(stdout);
}
n_root_block = move_formatted_block(fs, get_sb_root_block(ondisk_sb),
blocks, 0);
if (n_root_block)
set_sb_root_block(ondisk_sb, n_root_block);
if (opt_verbose)
printf("\n\nnodes processed (moved):\n"
"int %lu (%lu),\n"
"leaves %lu (%lu),\n"
"unfm %lu (%lu),\n"
"total %lu (%lu).\n\n",
int_node_cnt, int_moved_cnt,
leaf_node_cnt, leaf_moved_cnt,
unfm_node_cnt, unfm_moved_cnt,
(unsigned long)total_node_cnt, total_moved_cnt);
if (block_count_mismatch) {
fprintf(stderr, "resize_reiserfs: data block count %lu"
" doesn\'t match data block count %lu from super block\n",
(unsigned long)total_node_cnt, blocks_used);
}
{
unsigned long l;
/* make sure that none of truncated block are in use */
printf("check for used blocks in truncated region\n");
for (l = blocks; l < fs->fs_bitmap2->bm_bit_size; l++) {
if ((l % (fs->fs_blocksize * 8)) == 0)
continue;
if (reiserfs_bitmap_test_bit(fs->fs_bitmap2, l))
printf("<%lu>", l);
}
printf("\n");
}
badblock_list(fs, mark_badblock, NULL);
if (fs->fs_badblocks_bm) {
bad_count = reiserfs_bitmap_ones(fs->fs_badblocks_bm);
reiserfs_shrink_bitmap(fs->fs_badblocks_bm, blocks);
add_badblock_list(fs, 1);
bad_count -= reiserfs_bitmap_ones(fs->fs_badblocks_bm);
} else
bad_count = 0;
reiserfs_shrink_bitmap(fs->fs_bitmap2, blocks);
set_sb_free_blocks(ondisk_sb, get_sb_free_blocks(ondisk_sb)
- (get_sb_block_count(ondisk_sb) - blocks)
+ (reiserfs_fs_bmap_nr(fs) - bmap_nr_new)
+ bad_count);
set_sb_block_count(ondisk_sb, blocks);
set_sb_bmap_nr(ondisk_sb, bmap_nr_new);
set_sb_bmap_nr(fs->fs_ondisk_sb,
reiserfs_bmap_over(bmap_nr_new) ? 0 : bmap_nr_new);
return 0;
}