318 lines
8.3 KiB
C
318 lines
8.3 KiB
C
/*
|
|
* Copyright 2000-2004 by Hans Reiser, licensing governed by
|
|
* reiserfsprogs/README
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "resize.h"
|
|
#include "misc/unaligned.h"
|
|
#include "util/misc.h"
|
|
|
|
#include <time.h>
|
|
#include <fcntl.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 reiserfs_sb_t * ondisk_sb;
|
|
|
|
/* abnornal exit from block reallocation process */
|
|
static void quit_resizer(reiserfs_filsys_t * fs)
|
|
{
|
|
/* save changes to bitmap blocks */
|
|
reiserfs_fs_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)
|
|
{
|
|
reiserfs_bh_t * bh, * bh2;
|
|
|
|
/* primitive fsck */
|
|
if (block > reiserfs_sb_get_blocks(ondisk_sb)) {
|
|
fprintf(stderr, "resize_reiserfs: invalid block number "
|
|
"(%lu) found.\n", block);
|
|
quit_resizer(fs);
|
|
}
|
|
|
|
/* progress bar, 3D style :) */
|
|
if (opt_verbose)
|
|
util_misc_progress(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 = reiserfs_buffer_read(fs->fs_dev, block, fs->fs_blocksize);
|
|
|
|
if (!bh)
|
|
reiserfs_exit (1, "move_generic_block: reiserfs_buffer_read 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 = reiserfs_buffer_open(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);
|
|
|
|
reiserfs_buffer_close(bh);
|
|
reiserfs_buffer_mkuptodate(bh2,1);
|
|
reiserfs_buffer_mkdirty(bh2);
|
|
reiserfs_buffer_write(bh2);
|
|
reiserfs_buffer_close(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)
|
|
{
|
|
reiserfs_bh_t * bh;
|
|
reiserfs_ih_t *ih;
|
|
unsigned long new_blocknr = 0;
|
|
int node_is_internal = 0;
|
|
unsigned int i, j;
|
|
|
|
bh = reiserfs_buffer_read(fs->fs_dev, block, fs->fs_blocksize);
|
|
|
|
if (!bh)
|
|
reiserfs_exit (1, "move_formatted_block: reiserfs_buffer_read failed");
|
|
|
|
if (reiserfs_leaf_head (bh)) {
|
|
leaf_node_cnt++;
|
|
|
|
for (i = 0; i < reiserfs_node_items(bh); i++) {
|
|
ih = reiserfs_ih_at(bh, i);
|
|
|
|
/* skip the bad blocks. */
|
|
if (reiserfs_key_get_oid (&ih->ih_key) == REISERFS_BAD_OID &&
|
|
reiserfs_key_get_did (&ih->ih_key) == REISERFS_BAD_DID)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (reiserfs_ih_ext(ih)) {
|
|
__u32 * extent;
|
|
|
|
extent = (__u32 *)reiserfs_item_by_ih (bh, ih);
|
|
for (j = 0; j < reiserfs_ext_count(ih); j++) {
|
|
unsigned long unfm_block;
|
|
|
|
/* hole */
|
|
if (d32_get (extent, j) == 0)
|
|
continue;
|
|
|
|
unfm_block = move_unformatted_block(fs, d32_get (extent, j),
|
|
bnd, h + 1);
|
|
if (unfm_block) {
|
|
d32_put (extent, j, unfm_block);
|
|
reiserfs_buffer_mkdirty(bh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (reiserfs_int_head (bh)) { /* internal node */
|
|
int_node_cnt++;
|
|
node_is_internal = 1;
|
|
|
|
for (i=0; i <= reiserfs_node_items(bh); i++) {
|
|
unsigned long moved_block;
|
|
moved_block =
|
|
move_formatted_block(fs, reiserfs_dc_get_nr(
|
|
reiserfs_int_at (bh, i)), bnd, h+1);
|
|
if (moved_block) {
|
|
reiserfs_dc_set_nr (reiserfs_int_at (bh, i),
|
|
moved_block);
|
|
reiserfs_buffer_mkdirty(bh);
|
|
}
|
|
}
|
|
} else {
|
|
misc_die("Block (%lu) has invalid format\n", block);
|
|
}
|
|
|
|
if (reiserfs_buffer_isdirty(bh)) {
|
|
reiserfs_buffer_mkuptodate(bh,1);
|
|
reiserfs_buffer_write(bh);
|
|
}
|
|
|
|
reiserfs_buffer_close(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 new_blocks)
|
|
{
|
|
unsigned long n_root_block;
|
|
unsigned int bmap_nr_new;
|
|
unsigned int bmap_nr_old;
|
|
unsigned long bad_count;
|
|
unsigned long old_blocks;
|
|
|
|
ondisk_sb = fs->fs_ondisk_sb;
|
|
|
|
old_blocks = reiserfs_sb_get_blocks(ondisk_sb);
|
|
bmap_nr_old = reiserfs_bmap_nr(old_blocks, fs->fs_blocksize);
|
|
bmap_nr_new = reiserfs_bmap_nr(new_blocks, fs->fs_blocksize);
|
|
|
|
/* is shrinking possible ? */
|
|
if (old_blocks - new_blocks >
|
|
reiserfs_sb_get_free(ondisk_sb) + bmap_nr_old - 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_fs_reopen(fs, O_RDWR);
|
|
if (reiserfs_bitmap_open(fs))
|
|
reiserfs_exit(1, "cannot open ondisk bitmap");
|
|
bmp = fs->fs_bitmap2;
|
|
ondisk_sb = fs->fs_ondisk_sb;
|
|
|
|
reiserfs_sb_set_state (fs->fs_ondisk_sb, FS_ERROR);
|
|
reiserfs_buffer_mkuptodate(fs->fs_super_bh, 1);
|
|
reiserfs_buffer_mkdirty(fs->fs_super_bh);
|
|
reiserfs_buffer_write(fs->fs_super_bh);
|
|
|
|
/* calculate number of data blocks */
|
|
blocks_used =
|
|
old_blocks
|
|
- reiserfs_sb_get_free(fs->fs_ondisk_sb)
|
|
- bmap_nr_old
|
|
- reiserfs_jp_get_size(reiserfs_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, reiserfs_sb_get_root(ondisk_sb),
|
|
new_blocks, 0);
|
|
|
|
if (n_root_block)
|
|
reiserfs_sb_set_root (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 = new_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");
|
|
}
|
|
|
|
reiserfs_badblock_traverse(fs, reiserfs_badblock_extract, NULL);
|
|
|
|
if (fs->fs_badblocks_bm) {
|
|
bad_count = reiserfs_bitmap_ones(fs->fs_badblocks_bm);
|
|
reiserfs_bitmap_shrink (fs->fs_badblocks_bm, new_blocks);
|
|
reiserfs_badblock_flush(fs, 1);
|
|
bad_count -= reiserfs_bitmap_ones(fs->fs_badblocks_bm);
|
|
} else
|
|
bad_count = 0;
|
|
|
|
reiserfs_bitmap_shrink (fs->fs_bitmap2, new_blocks);
|
|
|
|
reiserfs_sb_set_free (ondisk_sb, reiserfs_sb_get_free(ondisk_sb)
|
|
- (old_blocks - new_blocks)
|
|
+ (bmap_nr_old - bmap_nr_new)
|
|
+ bad_count);
|
|
reiserfs_sb_set_blocks (ondisk_sb, new_blocks);
|
|
reiserfs_sb_set_bmaps (ondisk_sb, bmap_nr_new);
|
|
|
|
return 0;
|
|
}
|