blob: b2fbc6811ea8e9998a00308a51bce0d125c6fe00 [file] [log] [blame]
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +00001/*
2 * resize2fs.c --- ext2 main routine
3 *
4 * Copyright (C) 1997 Theodore Ts'o
5 *
6 * %Begin-Header%
7 * All rights reserved.
8 * %End-Header%
9 */
10
11#include "resize2fs.h"
12
13/*
14 * This routine adjusts the superblock and other data structures...
15 */
16static errcode_t adjust_superblock(ext2_resize_t rfs, blk_t new_size)
17{
18 ext2_filsys fs;
19 int overhead = 0;
20 int rem;
21 errcode_t retval;
22 ino_t real_end;
23 blk_t blk, group_block;
Theodore Ts'o1e1da291997-06-09 14:51:29 +000024 unsigned long i, j;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +000025 struct ext2_group_desc *new;
Theodore Ts'o1e1da291997-06-09 14:51:29 +000026 char *buf;
27 int old_numblocks, numblocks, adjblocks;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +000028
29 fs = rfs->new_fs;
30 fs->super->s_blocks_count = new_size;
Theodore Ts'o1e1da291997-06-09 14:51:29 +000031 ext2fs_mark_super_dirty(fs);
32 ext2fs_mark_bb_dirty(fs);
33 ext2fs_mark_ib_dirty(fs);
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +000034
35retry:
36 fs->group_desc_count = (fs->super->s_blocks_count -
37 fs->super->s_first_data_block +
38 EXT2_BLOCKS_PER_GROUP(fs->super) - 1)
39 / EXT2_BLOCKS_PER_GROUP(fs->super);
40 if (fs->group_desc_count == 0)
41 return EXT2_ET_TOOSMALL;
42 fs->desc_blocks = (fs->group_desc_count +
43 EXT2_DESC_PER_BLOCK(fs->super) - 1)
44 / EXT2_DESC_PER_BLOCK(fs->super);
45
46 /*
47 * Overhead is the number of bookkeeping blocks per group. It
48 * includes the superblock backup, the group descriptor
49 * backups, the inode bitmap, the block bitmap, and the inode
50 * table.
51 *
52 * XXX Not all block groups need the descriptor blocks, but
53 * being clever is tricky...
54 */
55 overhead = 3 + fs->desc_blocks + fs->inode_blocks_per_group;
56
57 /*
58 * See if the last group is big enough to support the
59 * necessary data structures. If not, we need to get rid of
60 * it.
61 */
62 rem = (fs->super->s_blocks_count - fs->super->s_first_data_block) %
63 fs->super->s_blocks_per_group;
64 if ((fs->group_desc_count == 1) && rem && (rem < overhead))
65 return EXT2_ET_TOOSMALL;
66 if (rem && (rem < overhead+50)) {
67 fs->super->s_blocks_count -= rem;
68 goto retry;
69 }
70 /*
71 * Adjust the number of inodes
72 */
73 fs->super->s_inodes_count = fs->super->s_inodes_per_group *
74 fs->group_desc_count;
75
76 /*
77 * Adjust the number of free blocks
78 */
79 blk = rfs->old_fs->super->s_blocks_count;
80 if (blk > fs->super->s_blocks_count)
81 fs->super->s_free_blocks_count -=
82 (blk - fs->super->s_blocks_count);
83 else
84 fs->super->s_free_blocks_count +=
85 (fs->super->s_blocks_count - blk);
86
87 /*
88 * Adjust the bitmaps for size
89 */
90 retval = ext2fs_resize_inode_bitmap(fs->super->s_inodes_count,
91 fs->super->s_inodes_count,
92 fs->inode_map);
93 if (retval)
94 return retval;
95
96 real_end = ((EXT2_BLOCKS_PER_GROUP(fs->super)
97 * fs->group_desc_count)) - 1 +
98 fs->super->s_first_data_block;
99 retval = ext2fs_resize_block_bitmap(fs->super->s_blocks_count-1,
100 real_end, fs->block_map);
101
102 if (retval)
103 return retval;
104
105 /*
106 * Reallocate the group descriptors as necessary.
107 */
108 if (rfs->old_fs->desc_blocks != fs->desc_blocks) {
109 new = realloc(fs->group_desc,
110 fs->desc_blocks * fs->blocksize);
111 if (!new)
112 return ENOMEM;
113 fs->group_desc = new;
114 }
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000115
116 /*
117 * Fix the count of the last (old) block group
118 */
119 if (rfs->old_fs->group_desc_count > fs->group_desc_count)
120 return 0;
121 old_numblocks = (rfs->old_fs->super->s_blocks_count -
122 rfs->old_fs->super->s_first_data_block) %
123 rfs->old_fs->super->s_blocks_per_group;
124 if (!old_numblocks)
125 old_numblocks = rfs->old_fs->super->s_blocks_per_group;
126 if (rfs->old_fs->group_desc_count == fs->group_desc_count) {
127 numblocks = (rfs->new_fs->super->s_blocks_count -
128 rfs->new_fs->super->s_first_data_block) %
129 rfs->new_fs->super->s_blocks_per_group;
130 if (!numblocks)
131 numblocks = rfs->new_fs->super->s_blocks_per_group;
132 } else
133 numblocks = rfs->new_fs->super->s_blocks_per_group;
134 i = rfs->old_fs->group_desc_count - 1;
135 fs->group_desc[i].bg_free_blocks_count += (numblocks-old_numblocks);
136
137 /*
138 * Initialize the new block group descriptors
139 */
140 if (rfs->old_fs->group_desc_count >= fs->group_desc_count)
141 return 0;
142 buf = malloc(fs->blocksize);
143 if (!buf)
144 return ENOMEM;
145 memset(buf, 0, fs->blocksize);
146 group_block = fs->super->s_first_data_block +
147 rfs->old_fs->group_desc_count * fs->super->s_blocks_per_group;
148 for (i = rfs->old_fs->group_desc_count;
149 i < fs->group_desc_count; i++) {
150 memset(&fs->group_desc[i], 0,
151 sizeof(struct ext2_group_desc));
152 adjblocks = 0;
153
154 if (i == fs->group_desc_count-1) {
155 numblocks = (fs->super->s_blocks_count -
156 fs->super->s_first_data_block) %
157 fs->super->s_blocks_per_group;
158 if (!numblocks)
159 numblocks = fs->super->s_blocks_per_group;
160 } else
161 numblocks = fs->super->s_blocks_per_group;
162
163 if (ext2fs_bg_has_super(fs, i)) {
164 for (j=0; j < fs->desc_blocks+1; j++)
165 ext2fs_mark_block_bitmap(fs->block_map,
166 group_block + j);
167 adjblocks = 1 + fs->desc_blocks;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000168 }
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000169 adjblocks += 2 + fs->inode_blocks_per_group;
170
171 numblocks -= adjblocks;
172 fs->super->s_free_blocks_count -= adjblocks;
173 fs->super->s_free_inodes_count +=
174 fs->super->s_inodes_per_group;
175 fs->group_desc[i].bg_free_blocks_count = numblocks;
176 fs->group_desc[i].bg_free_inodes_count =
177 fs->super->s_inodes_per_group;
178 fs->group_desc[i].bg_used_dirs_count = 0;
179
180 retval = ext2fs_allocate_group_table(fs, i, 0);
181 if (retval)
182 return retval;
183
184 for (blk=fs->group_desc[i].bg_inode_table, j=0;
185 j < fs->inode_blocks_per_group;
186 blk++, j++) {
187 retval = io_channel_write_blk(fs->io, blk, 1, buf);
188 if (retval)
189 return retval;
190 }
191 group_block += fs->super->s_blocks_per_group;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000192 }
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000193 return 0;
194}
195
196/*
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000197 * This routine marks and unmarks reserved blocks in the new block
198 * bitmap. It also determines which blocks need to be moved and
199 * places this information into the move_blocks bitmap.
200 */
201static errcode_t determine_relocations(ext2_resize_t rfs)
202{
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000203 int i, j;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000204 blk_t blk, group_blk;
205 unsigned long old_blocks, new_blocks;
206 errcode_t retval;
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000207 ext2_filsys fs = rfs->new_fs;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000208
209 retval = ext2fs_allocate_block_bitmap(rfs->old_fs,
210 "blocks to be moved",
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000211 &rfs->reserve_blocks);
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000212 if (retval)
213 return retval;
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000214
215 /*
216 * If we're shrinking the filesystem, we need to move all of
217 * the blocks that don't fit any more
218 */
219 for (blk = fs->super->s_blocks_count;
220 blk < rfs->old_fs->super->s_blocks_count; blk++) {
221 if (ext2fs_test_block_bitmap(rfs->old_fs->block_map, blk))
222 rfs->needed_blocks++;
223 ext2fs_mark_block_bitmap(rfs->reserve_blocks, blk);
224 }
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000225
226 old_blocks = rfs->old_fs->desc_blocks;
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000227 new_blocks = fs->desc_blocks;
228
229 if (old_blocks == new_blocks)
230 return 0;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000231
232 group_blk = rfs->old_fs->super->s_first_data_block;
233 /*
234 * If we're reducing the number of descriptor blocks, this
235 * makes life easy. :-) We just have to mark some extra
236 * blocks as free.
237 */
238 if (old_blocks > new_blocks) {
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000239 for (i = 0; i < fs->group_desc_count; i++) {
240 if (!ext2fs_bg_has_super(fs, i)) {
241 group_blk += fs->super->s_blocks_per_group;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000242 continue;
243 }
244 for (blk = group_blk+1+old_blocks;
245 blk < group_blk+1+new_blocks; blk++)
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000246 ext2fs_unmark_block_bitmap(fs->block_map,
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000247 blk);
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000248 group_blk += fs->super->s_blocks_per_group;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000249 }
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000250 return 0;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000251 }
252 /*
253 * If we're increasing the number of descriptor blocks, life
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000254 * gets interesting....
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000255 */
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000256 for (i = 0; i < fs->group_desc_count; i++) {
257 if (!ext2fs_bg_has_super(fs, i))
258 goto next_group;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000259
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000260 for (blk = group_blk;
261 blk < group_blk + 1 + new_blocks; blk++) {
262 ext2fs_mark_block_bitmap(rfs->reserve_blocks, blk);
263 ext2fs_mark_block_bitmap(fs->block_map, blk);
264
265 /*
266 * Check to see if we overlap with the inode
267 * or block bitmap
268 */
269 if (blk == fs->group_desc[i].bg_inode_bitmap)
270 fs->group_desc[i].bg_block_bitmap = 0;
271 if (blk == fs->group_desc[i].bg_inode_bitmap)
272 fs->group_desc[i].bg_inode_bitmap = 0;
273
274 /*
275 * Check to see if we overlap with the inode
276 * table
277 */
278 if (blk < fs->group_desc[i].bg_inode_table)
279 continue;
280 if (blk >= (fs->group_desc[i].bg_inode_table +
281 fs->inode_blocks_per_group))
282 continue;
283 fs->group_desc[i].bg_inode_table = 0;
284 blk = fs->group_desc[i].bg_inode_table +
285 fs->inode_blocks_per_group - 1;
286 }
287 if (fs->group_desc[i].bg_inode_table &&
288 fs->group_desc[i].bg_inode_bitmap &&
289 fs->group_desc[i].bg_block_bitmap)
290 goto next_group;
291
292 /*
293 * Allocate the missing bitmap and inode table
294 * structures, passing in rfs->reserve_blocks to
295 * prevent a conflict.
296 */
297 if (fs->group_desc[i].bg_block_bitmap)
298 ext2fs_mark_block_bitmap(rfs->reserve_blocks,
299 fs->group_desc[i].bg_block_bitmap);
300 if (fs->group_desc[i].bg_inode_bitmap)
301 ext2fs_mark_block_bitmap(rfs->reserve_blocks,
302 fs->group_desc[i].bg_inode_bitmap);
303 if (fs->group_desc[i].bg_inode_table)
304 for (blk = fs->group_desc[i].bg_inode_table, j=0;
305 j < fs->inode_blocks_per_group ; j++, blk++)
306 ext2fs_mark_block_bitmap(rfs->reserve_blocks,
307 blk);
308
309 retval = ext2fs_allocate_group_table(fs, i,
310 rfs->reserve_blocks);
311 if (retval)
312 return retval;
313
314 /*
315 * Now make sure these blocks are reserved in the new
316 * block bitmap
317 */
318 ext2fs_mark_block_bitmap(fs->block_map,
319 fs->group_desc[i].bg_block_bitmap);
320 ext2fs_mark_block_bitmap(fs->block_map,
321 fs->group_desc[i].bg_inode_bitmap);
322
323 for (blk = fs->group_desc[i].bg_inode_table, j=0;
324 j < fs->inode_blocks_per_group ; j++, blk++)
325 ext2fs_mark_block_bitmap(fs->block_map, blk);
326
327 /*
328 * Mark the inode tables which will need to move, and
329 * restore the old inode table location (for now)
330 */
331 if (fs->group_desc[i].bg_inode_table !=
332 rfs->old_fs->group_desc[i].bg_inode_table) {
333 rfs->move_itable[i] = fs->group_desc[i].bg_inode_table;
334 fs->group_desc[i].bg_inode_table =
335 rfs->old_fs->group_desc[i].bg_inode_table;
336 }
337
338 next_group:
339 group_blk += rfs->new_fs->super->s_blocks_per_group;
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000340 }
341}
342
343
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000344/*
345 * This is the top-level routine which does the dirty deed....
346 */
347errcode_t resize_fs(ext2_filsys fs, blk_t new_size)
348{
349 ext2_resize_t rfs;
350 errcode_t retval;
351
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000352 retval = ext2fs_read_bitmaps(fs);
353 if (retval)
354 return retval;
355
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000356 /*
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000357 * Create the data structure
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000358 */
359 rfs = malloc(sizeof(struct ext2_resize_struct));
360 if (!rfs)
361 return ENOMEM;
362 memset(rfs, 0, sizeof(struct ext2_resize_struct));
363
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000364 rfs->move_itable = malloc(sizeof(blk_t) * fs->group_desc_count);
365 if (!rfs->move_itable) {
366 retval = ENOMEM;
367 goto errout;
368 }
369 memset(rfs->move_itable, 0, sizeof(blk_t) * fs->group_desc_count);
370
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000371 rfs->old_fs = fs;
372 retval = ext2fs_dup_handle(fs, &rfs->new_fs);
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000373 if (retval)
374 goto errout;
375
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000376 retval = adjust_superblock(rfs, new_size);
377 if (retval)
378 goto errout;
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000379
380 retval = determine_relocations(rfs);
381 if (retval)
382 goto errout;
383
384 printf("\nOld superblock:\n");
385 list_super(rfs->old_fs->super);
386 printf("\n\nNew superblock:\n");
387 list_super(rfs->new_fs->super);
388 printf("\n");
389
390 retval = ext2fs_move_blocks(rfs->old_fs, rfs->reserve_blocks,
391 EXT2_BMOVE_GET_DBLIST);
392
393 retval = ext2fs_close(rfs->new_fs);
394 if (retval)
395 return retval;
396
397 ext2fs_free(rfs->old_fs);
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000398
399 return 0;
400
401errout:
Theodore Ts'o1e1da291997-06-09 14:51:29 +0000402 if (rfs->move_itable)
403 free(rfs->move_itable);
404 if (rfs->new_fs)
405 ext2fs_free(rfs->new_fs);
Theodore Ts'o24b2c7a1997-06-07 20:42:58 +0000406 free(rfs);
407 return retval;
408}
409