]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - resize/resize2fs.c
ChangeLog, unix_io.c:
[thirdparty/e2fsprogs.git] / resize / resize2fs.c
CommitLineData
24b2c7a7
TT
1/*
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;
1e1da29f 24 unsigned long i, j;
24b2c7a7 25 struct ext2_group_desc *new;
1e1da29f
TT
26 char *buf;
27 int old_numblocks, numblocks, adjblocks;
24b2c7a7
TT
28
29 fs = rfs->new_fs;
30 fs->super->s_blocks_count = new_size;
1e1da29f
TT
31 ext2fs_mark_super_dirty(fs);
32 ext2fs_mark_bb_dirty(fs);
33 ext2fs_mark_ib_dirty(fs);
24b2c7a7
TT
34
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 }
1e1da29f
TT
115
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;
24b2c7a7 168 }
1e1da29f
TT
169 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;
24b2c7a7 179
1e1da29f
TT
180 retval = ext2fs_allocate_group_table(fs, i, 0);
181 if (retval)
182 return retval;
24b2c7a7 183
1e1da29f
TT
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;
24b2c7a7 190 }
1e1da29f 191 group_block += fs->super->s_blocks_per_group;
24b2c7a7 192 }
1e1da29f 193 return 0;
24b2c7a7
TT
194}
195
24b2c7a7
TT
196/*
197 * 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{
052db4b7 203 int i, j, max, adj;
24b2c7a7
TT
204 blk_t blk, group_blk;
205 unsigned long old_blocks, new_blocks;
206 errcode_t retval;
1e1da29f 207 ext2_filsys fs = rfs->new_fs;
24b2c7a7
TT
208
209 retval = ext2fs_allocate_block_bitmap(rfs->old_fs,
210 "blocks to be moved",
1e1da29f 211 &rfs->reserve_blocks);
24b2c7a7
TT
212 if (retval)
213 return retval;
1e1da29f
TT
214
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 }
24b2c7a7
TT
225
226 old_blocks = rfs->old_fs->desc_blocks;
1e1da29f
TT
227 new_blocks = fs->desc_blocks;
228
229 if (old_blocks == new_blocks)
230 return 0;
24b2c7a7 231
052db4b7
TT
232 max = fs->group_desc_count;
233 if (max > rfs->old_fs->group_desc_count)
234 max = rfs->old_fs->group_desc_count;
24b2c7a7
TT
235 group_blk = rfs->old_fs->super->s_first_data_block;
236 /*
237 * If we're reducing the number of descriptor blocks, this
238 * makes life easy. :-) We just have to mark some extra
239 * blocks as free.
240 */
241 if (old_blocks > new_blocks) {
052db4b7 242 for (i = 0; i < max; i++) {
1e1da29f
TT
243 if (!ext2fs_bg_has_super(fs, i)) {
244 group_blk += fs->super->s_blocks_per_group;
24b2c7a7
TT
245 continue;
246 }
247 for (blk = group_blk+1+old_blocks;
052db4b7 248 blk < group_blk+1+new_blocks; blk++) {
1e1da29f 249 ext2fs_unmark_block_bitmap(fs->block_map,
24b2c7a7 250 blk);
052db4b7
TT
251 rfs->needed_blocks--;
252 }
1e1da29f 253 group_blk += fs->super->s_blocks_per_group;
24b2c7a7 254 }
1e1da29f 255 return 0;
24b2c7a7
TT
256 }
257 /*
258 * If we're increasing the number of descriptor blocks, life
1e1da29f 259 * gets interesting....
24b2c7a7 260 */
052db4b7 261 for (i = 0; i < max; i++) {
1e1da29f
TT
262 if (!ext2fs_bg_has_super(fs, i))
263 goto next_group;
264
265 for (blk = group_blk;
266 blk < group_blk + 1 + new_blocks; blk++) {
267 ext2fs_mark_block_bitmap(rfs->reserve_blocks, blk);
268 ext2fs_mark_block_bitmap(fs->block_map, blk);
269
270 /*
271 * Check to see if we overlap with the inode
272 * or block bitmap
273 */
052db4b7
TT
274 if (blk == fs->group_desc[i].bg_block_bitmap) {
275 fs->group_desc[i].bg_block_bitmap = 0;
276 rfs->needed_blocks++;
277 }
278 if (blk == fs->group_desc[i].bg_inode_bitmap) {
1e1da29f 279 fs->group_desc[i].bg_inode_bitmap = 0;
052db4b7
TT
280 rfs->needed_blocks++;
281 }
1e1da29f
TT
282 /*
283 * Check to see if we overlap with the inode
284 * table
285 */
286 if (blk < fs->group_desc[i].bg_inode_table)
24b2c7a7 287 continue;
1e1da29f
TT
288 if (blk >= (fs->group_desc[i].bg_inode_table +
289 fs->inode_blocks_per_group))
290 continue;
291 fs->group_desc[i].bg_inode_table = 0;
292 blk = fs->group_desc[i].bg_inode_table +
293 fs->inode_blocks_per_group - 1;
24b2c7a7 294 }
1e1da29f
TT
295 if (fs->group_desc[i].bg_inode_table &&
296 fs->group_desc[i].bg_inode_bitmap &&
297 fs->group_desc[i].bg_block_bitmap)
298 goto next_group;
24b2c7a7 299
1e1da29f
TT
300 /*
301 * Allocate the missing bitmap and inode table
302 * structures, passing in rfs->reserve_blocks to
303 * prevent a conflict.
304 */
305 if (fs->group_desc[i].bg_block_bitmap)
306 ext2fs_mark_block_bitmap(rfs->reserve_blocks,
307 fs->group_desc[i].bg_block_bitmap);
308 if (fs->group_desc[i].bg_inode_bitmap)
309 ext2fs_mark_block_bitmap(rfs->reserve_blocks,
310 fs->group_desc[i].bg_inode_bitmap);
311 if (fs->group_desc[i].bg_inode_table)
312 for (blk = fs->group_desc[i].bg_inode_table, j=0;
313 j < fs->inode_blocks_per_group ; j++, blk++)
314 ext2fs_mark_block_bitmap(rfs->reserve_blocks,
315 blk);
24b2c7a7 316
1e1da29f
TT
317 retval = ext2fs_allocate_group_table(fs, i,
318 rfs->reserve_blocks);
319 if (retval)
320 return retval;
24b2c7a7 321
1e1da29f
TT
322 /*
323 * Now make sure these blocks are reserved in the new
324 * block bitmap
325 */
326 ext2fs_mark_block_bitmap(fs->block_map,
327 fs->group_desc[i].bg_block_bitmap);
328 ext2fs_mark_block_bitmap(fs->block_map,
329 fs->group_desc[i].bg_inode_bitmap);
24b2c7a7 330
052db4b7
TT
331 /*
332 * The inode table, if we need to relocate it, is
333 * handled specially. We have to reserve the blocks
334 * for both the old and the new inode table, since we
335 * can't have the inode table be destroyed during the
336 * block relocation phase.
337 */
338 adj = fs->group_desc[i].bg_inode_table -
339 rfs->old_fs->group_desc[i].bg_inode_table;
340 if (!adj)
341 goto next_group; /* inode table not moved */
342
343 /*
344 * Figure out how many blocks we need to have free.
345 * This takes into account that we need to reserve
346 * both inode tables, which may be overallping.
347 */
348 if (adj < 0)
349 adj = -adj;
350 if (adj > fs->inode_blocks_per_group)
351 adj = fs->inode_blocks_per_group;
352 rfs->needed_blocks += fs->inode_blocks_per_group + adj;
353
354 /*
355 * Mark the new inode table as in use in the new block
356 * allocation bitmap.
357 */
1e1da29f
TT
358 for (blk = fs->group_desc[i].bg_inode_table, j=0;
359 j < fs->inode_blocks_per_group ; j++, blk++)
360 ext2fs_mark_block_bitmap(fs->block_map, blk);
1e1da29f 361 /*
052db4b7
TT
362 * Make sure the old inode table is reserved in the
363 * block reservation bitmap.
1e1da29f 364 */
052db4b7
TT
365 for (blk = rfs->old_fs->group_desc[i].bg_inode_table, j=0;
366 j < fs->inode_blocks_per_group ; j++, blk++)
367 ext2fs_mark_block_bitmap(rfs->reserve_blocks, blk);
1e1da29f
TT
368
369 next_group:
370 group_blk += rfs->new_fs->super->s_blocks_per_group;
371 }
052db4b7 372 return 0;
1e1da29f 373}
24b2c7a7
TT
374
375
052db4b7
TT
376/*
377 * A very scary routine --- this one moves the inode table around!!!
378 *
379 * After this you have to use the rfs->new_fs file handle to read and
380 * write inodes.
381 */
382errcode_t move_itables(ext2_resize_t rfs)
383{
384 int i, max;
385 ext2_filsys fs = rfs->new_fs;
386 char *buf;
387 blk_t old, new;
388 errcode_t retval, err;
389
390 printf("Hide the women and children --- "
391 "commencing inode table moves!!\n");
392
393 max = fs->group_desc_count;
394 if (max > rfs->old_fs->group_desc_count)
395 max = rfs->old_fs->group_desc_count;
396
397 buf = malloc(fs->blocksize * fs->inode_blocks_per_group);
398 if (!buf)
399 return ENOMEM;
400
401 for (i=0; i < max; i++) {
402 old = rfs->old_fs->group_desc[i].bg_inode_table;
403 new = fs->group_desc[i].bg_inode_table;
404
405 printf("Group %d block %ld->%ld\n", i, old, new);
406
407 if (old == new)
408 continue;
409
410 retval = io_channel_read_blk(fs->io, old,
411 fs->inode_blocks_per_group, buf);
412 if (retval)
413 goto backout;
414 retval = io_channel_write_blk(fs->io, new,
415 fs->inode_blocks_per_group, buf);
416 if (retval) {
417 io_channel_write_blk(fs->io, old,
418 fs->inode_blocks_per_group, buf);
419 goto backout;
420 }
421 }
422 ext2fs_flush(rfs->new_fs);
423 printf("Inode table move finished.\n");
424 return 0;
425
426backout:
427 printf("Error: %s; now backing out!\n", error_message(retval));
428 while (--i >= 0) {
429 printf("Group %d block %ld->%ld\n", i, new, old);
430 old = rfs->old_fs->group_desc[i].bg_inode_table;
431 new = fs->group_desc[i].bg_inode_table;
432
433 err = io_channel_read_blk(fs->io, new,
434 fs->inode_blocks_per_group, buf);
435 if (err)
436 continue;
437 err = io_channel_write_blk(fs->io, old,
438 fs->inode_blocks_per_group, buf);
439 }
440 return retval;
441}
442
443/*
444 * Finally, recalculate the summary information
445 */
446static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs)
447{
448 blk_t blk;
449 ino_t ino;
450 int group = 0;
451 int count = 0;
452 int total_free = 0;
453 int group_free = 0;
454
455 /*
456 * First calculate the block statistics
457 */
458 for (blk = fs->super->s_first_data_block;
459 blk < fs->super->s_blocks_count; blk++) {
460 if (!ext2fs_fast_test_block_bitmap(fs->block_map, blk)) {
461 group_free++;
462 total_free++;
463 }
464 count++;
465 if ((count == fs->super->s_blocks_per_group) ||
466 (blk == fs->super->s_blocks_count-1)) {
467 fs->group_desc[group++].bg_free_blocks_count =
468 group_free;
469 count = 0;
470 group_free = 0;
471 }
472 }
473 fs->super->s_free_blocks_count = total_free;
474
475 /*
476 * Next, calculate the inode statistics
477 */
478 group_free = 0;
479 total_free = 0;
480 count = 0;
481 group = 0;
482 for (ino = 1; ino <= fs->super->s_inodes_count; ino++) {
483 if (!ext2fs_fast_test_inode_bitmap(fs->inode_map, ino)) {
484 group_free++;
485 total_free++;
486 }
487 count++;
488 if ((count == fs->super->s_inodes_per_group) ||
489 (ino == fs->super->s_inodes_count)) {
490 fs->group_desc[group++].bg_free_inodes_count =
491 group_free;
492 count = 0;
493 group_free = 0;
494 }
495 }
496 fs->super->s_free_inodes_count = total_free;
497 ext2fs_mark_super_dirty(fs);
498 return 0;
499}
500
501
502
24b2c7a7
TT
503/*
504 * This is the top-level routine which does the dirty deed....
505 */
506errcode_t resize_fs(ext2_filsys fs, blk_t new_size)
507{
508 ext2_resize_t rfs;
509 errcode_t retval;
510
1e1da29f
TT
511 retval = ext2fs_read_bitmaps(fs);
512 if (retval)
513 return retval;
514
24b2c7a7 515 /*
1e1da29f 516 * Create the data structure
24b2c7a7
TT
517 */
518 rfs = malloc(sizeof(struct ext2_resize_struct));
519 if (!rfs)
520 return ENOMEM;
521 memset(rfs, 0, sizeof(struct ext2_resize_struct));
522
523 rfs->old_fs = fs;
524 retval = ext2fs_dup_handle(fs, &rfs->new_fs);
1e1da29f
TT
525 if (retval)
526 goto errout;
527
24b2c7a7
TT
528 retval = adjust_superblock(rfs, new_size);
529 if (retval)
530 goto errout;
1e1da29f
TT
531
532 retval = determine_relocations(rfs);
533 if (retval)
534 goto errout;
535
052db4b7
TT
536 printf("Number of free blocks: %d, Needed: %d\n",
537 fs->super->s_free_blocks_count, rfs->needed_blocks);
538
539 if (rfs->needed_blocks > fs->super->s_free_blocks_count) {
540 retval = ENOSPC;
541 goto errout;
542 }
543
1e1da29f
TT
544 printf("\nOld superblock:\n");
545 list_super(rfs->old_fs->super);
546 printf("\n\nNew superblock:\n");
547 list_super(rfs->new_fs->super);
548 printf("\n");
549
550 retval = ext2fs_move_blocks(rfs->old_fs, rfs->reserve_blocks,
052db4b7 551 rfs->new_fs->block_map,
1e1da29f 552 EXT2_BMOVE_GET_DBLIST);
052db4b7
TT
553 if (retval)
554 return retval;
1e1da29f 555
052db4b7
TT
556 retval = ext2fs_inode_move(rfs);
557 if (retval)
558 return retval;
559
560 retval = move_itables(rfs);
561 if (retval)
562 return retval;
563
564 retval = ext2fs_calculate_summary_stats(rfs->new_fs);
565 if (retval)
566 return retval;
567
1e1da29f
TT
568 retval = ext2fs_close(rfs->new_fs);
569 if (retval)
570 return retval;
571
572 ext2fs_free(rfs->old_fs);
24b2c7a7
TT
573
574 return 0;
575
576errout:
1e1da29f
TT
577 if (rfs->new_fs)
578 ext2fs_free(rfs->new_fs);
24b2c7a7
TT
579 free(rfs);
580 return retval;
581}