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