2 * resize2fs.c --- ext2 main routine
4 * Copyright (C) 1997 Theodore Ts'o
11 #include "resize2fs.h"
14 * This routine adjusts the superblock and other data structures...
16 static errcode_t adjust_superblock(ext2_resize_t rfs, blk_t new_size)
23 blk_t blk, group_block;
25 struct ext2_group_desc *new;
28 fs->super->s_blocks_count = new_size;
31 fs->group_desc_count = (fs->super->s_blocks_count -
32 fs->super->s_first_data_block +
33 EXT2_BLOCKS_PER_GROUP(fs->super) - 1)
34 / EXT2_BLOCKS_PER_GROUP(fs->super);
35 if (fs->group_desc_count == 0)
36 return EXT2_ET_TOOSMALL;
37 fs->desc_blocks = (fs->group_desc_count +
38 EXT2_DESC_PER_BLOCK(fs->super) - 1)
39 / EXT2_DESC_PER_BLOCK(fs->super);
42 * Overhead is the number of bookkeeping blocks per group. It
43 * includes the superblock backup, the group descriptor
44 * backups, the inode bitmap, the block bitmap, and the inode
47 * XXX Not all block groups need the descriptor blocks, but
48 * being clever is tricky...
50 overhead = 3 + fs->desc_blocks + fs->inode_blocks_per_group;
53 * See if the last group is big enough to support the
54 * necessary data structures. If not, we need to get rid of
57 rem = (fs->super->s_blocks_count - fs->super->s_first_data_block) %
58 fs->super->s_blocks_per_group;
59 if ((fs->group_desc_count == 1) && rem && (rem < overhead))
60 return EXT2_ET_TOOSMALL;
61 if (rem && (rem < overhead+50)) {
62 fs->super->s_blocks_count -= rem;
66 * Adjust the number of inodes
68 fs->super->s_inodes_count = fs->super->s_inodes_per_group *
72 * Adjust the number of free blocks
74 blk = rfs->old_fs->super->s_blocks_count;
75 if (blk > fs->super->s_blocks_count)
76 fs->super->s_free_blocks_count -=
77 (blk - fs->super->s_blocks_count);
79 fs->super->s_free_blocks_count +=
80 (fs->super->s_blocks_count - blk);
83 * Adjust the bitmaps for size
85 retval = ext2fs_resize_inode_bitmap(fs->super->s_inodes_count,
86 fs->super->s_inodes_count,
91 real_end = ((EXT2_BLOCKS_PER_GROUP(fs->super)
92 * fs->group_desc_count)) - 1 +
93 fs->super->s_first_data_block;
94 retval = ext2fs_resize_block_bitmap(fs->super->s_blocks_count-1,
95 real_end, fs->block_map);
101 * Reallocate the group descriptors as necessary.
103 if (rfs->old_fs->desc_blocks != fs->desc_blocks) {
104 new = realloc(fs->group_desc,
105 fs->desc_blocks * fs->blocksize);
108 fs->group_desc = new;
110 group_block = rfs->old_fs->super->s_first_data_block;
111 for (i = 0; i < fs->group_desc_count; i++) {
112 if (i < rfs->old_fs->group_desc_count) {
113 group_block += fs->super->s_blocks_per_group;
123 * This routine reserves a block in the new filesystem. If the block
124 * is already used, we mark it as needing relocation. Otherwise, we
125 * just mark it as used.
127 static reserve_block(ext2_resize_t rfs, blk_t blk)
129 if (ext2fs_test_block_bitmap(rfs->new_fs->block_map, blk))
130 ext2fs_mark_block_bitmap(rfs->move_blocks, blk);
132 ext2fs_mark_block_bitmap(rfs->new_fs->block_map, blk);
136 * This routine is a helper function for determine_relocations(). It
137 * is called for each block group which has a superblock, and for
138 * which we need to expand the size of the descriptor table. We have
139 * to account for the fact that in some cases we will need to move the
140 * inode table, which will mean moving or reserving blocks at the end
141 * of the inode table, since the inode table will be moved down to
144 * "And the block group descriptors waddled across the street..."
146 static void make_way_for_descriptors(ext2_resize_t rfs,
150 blk_t blk, start_blk, end_blk, itable, move_by;
154 start_blk = group_blk + rfs->old_fs->desc_blocks + 1;
155 end_blk = group_blk + rfs->new_fs->desc_blocks + 1;
157 itable = fs->group_desc[block_group].bg_inode_table;
158 if (end_blk > itable) {
159 move_by = itable - end_blk;
160 for (blk = itable, i=0; i < move_by; blk++, i++) {
161 ext2fs_unmark_block_bitmap(fs->block_map, blk);
162 reserve_block(rfs, blk+fs->inode_blocks_per_group);
165 fs->group_desc[i].bg_inode_table += move_by;
167 for (blk = start_blk; blk < end_blk; blk++)
168 reserve_block(rfs, blk);
173 * This routine marks and unmarks reserved blocks in the new block
174 * bitmap. It also determines which blocks need to be moved and
175 * places this information into the move_blocks bitmap.
177 static errcode_t determine_relocations(ext2_resize_t rfs)
180 blk_t blk, group_blk;
181 unsigned long old_blocks, new_blocks;
184 retval = ext2fs_allocate_block_bitmap(rfs->old_fs,
185 "blocks to be moved",
190 old_blocks = rfs->old_fs->desc_blocks;
191 new_blocks = rfs->new_fs->desc_blocks;
193 group_blk = rfs->old_fs->super->s_first_data_block;
195 * If we're reducing the number of descriptor blocks, this
196 * makes life easy. :-) We just have to mark some extra
199 if (old_blocks > new_blocks) {
200 for (i = 0; i < rfs->new_fs->group_desc_count; i++) {
201 if (!ext2fs_bg_has_super(rfs->new_fs, i)) {
202 group_blk += rfs->new_fs->super->s_blocks_per_group;
205 for (blk = group_blk+1+old_blocks;
206 blk < group_blk+1+new_blocks; blk++)
207 ext2fs_unmark_block_bitmap(rfs->new_fs->block_map,
209 group_blk += rfs->new_fs->super->s_blocks_per_group;
213 * If we're increasing the number of descriptor blocks, life
214 * gets interesting. In some cases, we will need to move the
217 if (old_blocks < new_blocks) {
218 for (i = 0; i < rfs->new_fs->group_desc_count; i++) {
219 if (!ext2fs_bg_has_super(rfs->new_fs, i)) {
220 group_blk += rfs->new_fs->super->s_blocks_per_group;
223 make_way_for_descriptors(rfs, i, group_blk);
224 group_blk += rfs->new_fs->super->s_blocks_per_group;
228 * Finally, if we're shrinking the filesystem, we need to
229 * move all of the blocks that don't fit any more
231 for (blk = rfs->new_fs->super->s_blocks_count;
232 blk < rfs->old_fs->super->s_blocks_count; blk++) {
233 if (ext2fs_test_block_bitmap(rfs->old_fs->block_map, blk))
234 ext2fs_mark_block_bitmap(rfs->move_blocks, blk);
245 * This is the top-level routine which does the dirty deed....
247 errcode_t resize_fs(ext2_filsys fs, blk_t new_size)
253 * First, create the data structure
255 rfs = malloc(sizeof(struct ext2_resize_struct));
258 memset(rfs, 0, sizeof(struct ext2_resize_struct));
261 retval = ext2fs_dup_handle(fs, &rfs->new_fs);
266 retval = adjust_superblock(rfs, new_size);
273 ext2fs_free(rfs->new_fs);