Whamcloud - gitweb
Fix false positives from valgrind: memcpy via no-op structure copy
[tools/e2fsprogs.git] / resize / resize2fs.c
1 /*
2  * resize2fs.c --- ext2 main routine
3  *
4  * Copyright (C) 1997, 1998 by Theodore Ts'o and
5  *      PowerQuest, Inc.
6  *
7  * Copyright (C) 1999, 2000 by Theosore Ts'o
8  * 
9  * %Begin-Header%
10  * This file may be redistributed under the terms of the GNU Public
11  * License.
12  * %End-Header%
13  */
14
15 /*
16  * Resizing a filesystem consists of the following phases:
17  *
18  *      1.  Adjust superblock and write out new parts of the inode
19  *              table
20  *      2.  Determine blocks which need to be relocated, and copy the
21  *              contents of blocks from their old locations to the new ones.
22  *      3.  Scan the inode table, doing the following:
23  *              a.  If blocks have been moved, update the block
24  *                      pointers in the inodes and indirect blocks to
25  *                      point at the new block locations.
26  *              b.  If parts of the inode table need to be evacuated,
27  *                      copy inodes from their old locations to their
28  *                      new ones.
29  *              c.  If (b) needs to be done, note which blocks contain
30  *                      directory information, since we will need to
31  *                      update the directory information.
32  *      4.  Update the directory blocks with the new inode locations.
33  *      5.  Move the inode tables, if necessary.
34  */
35
36 #include "resize2fs.h"
37 #include <time.h>
38
39 #ifdef __linux__                        /* Kludge for debugging */
40 #define RESIZE2FS_DEBUG
41 #endif
42
43 static errcode_t adjust_superblock(ext2_resize_t rfs, blk_t new_size);
44 static errcode_t blocks_to_move(ext2_resize_t rfs);
45 static errcode_t block_mover(ext2_resize_t rfs);
46 static errcode_t inode_scan_and_fix(ext2_resize_t rfs);
47 static errcode_t inode_ref_fix(ext2_resize_t rfs);
48 static errcode_t move_itables(ext2_resize_t rfs);
49 static errcode_t fix_resize_inode(ext2_filsys fs);
50 static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs);
51
52 /*
53  * Some helper CPP macros
54  */
55 #define FS_BLOCK_BM(fs, i) ((fs)->group_desc[(i)].bg_block_bitmap)
56 #define FS_INODE_BM(fs, i) ((fs)->group_desc[(i)].bg_inode_bitmap)
57 #define FS_INODE_TB(fs, i) ((fs)->group_desc[(i)].bg_inode_table)
58
59 #define IS_BLOCK_BM(fs, i, blk) ((blk) == FS_BLOCK_BM((fs),(i)))
60 #define IS_INODE_BM(fs, i, blk) ((blk) == FS_INODE_BM((fs),(i)))
61
62 #define IS_INODE_TB(fs, i, blk) (((blk) >= FS_INODE_TB((fs), (i))) && \
63                                  ((blk) < (FS_INODE_TB((fs), (i)) + \
64                                            (fs)->inode_blocks_per_group)))
65
66
67
68 /*
69  * This is the top-level routine which does the dirty deed....
70  */
71 errcode_t resize_fs(ext2_filsys fs, blk_t *new_size, int flags,
72                     errcode_t (*progress)(ext2_resize_t rfs, int pass,
73                                      unsigned long cur,
74                                      unsigned long max_val))
75 {
76         ext2_resize_t   rfs;
77         errcode_t       retval;
78
79         retval = ext2fs_read_bitmaps(fs);
80         if (retval)
81                 return retval;
82         
83         /*
84          * Create the data structure
85          */
86         retval = ext2fs_get_mem(sizeof(struct ext2_resize_struct), &rfs);
87         if (retval)
88                 return retval;
89         memset(rfs, 0, sizeof(struct ext2_resize_struct));
90
91         rfs->old_fs = fs;
92         rfs->flags = flags;
93         rfs->itable_buf  = 0;
94         rfs->progress = progress;
95         retval = ext2fs_dup_handle(fs, &rfs->new_fs);
96         if (retval)
97                 goto errout;
98
99         retval = adjust_superblock(rfs, *new_size);
100         if (retval)
101                 goto errout;
102
103         *new_size = rfs->new_fs->super->s_blocks_count;
104
105         retval = blocks_to_move(rfs);
106         if (retval)
107                 goto errout;
108
109 #ifdef RESIZE2FS_DEBUG
110         if (rfs->flags & RESIZE_DEBUG_BMOVE)
111                 printf("Number of free blocks: %d/%d, Needed: %d\n",
112                        rfs->old_fs->super->s_free_blocks_count,
113                        rfs->new_fs->super->s_free_blocks_count,
114                        rfs->needed_blocks);
115 #endif
116         
117         retval = block_mover(rfs);
118         if (retval)
119                 goto errout;
120
121         retval = inode_scan_and_fix(rfs);
122         if (retval)
123                 goto errout;
124
125         retval = inode_ref_fix(rfs);
126         if (retval)
127                 goto errout;
128
129         retval = move_itables(rfs);
130         if (retval)
131                 goto errout;
132
133         retval = ext2fs_calculate_summary_stats(rfs->new_fs);
134         if (retval)
135                 goto errout;
136         
137         retval = fix_resize_inode(rfs->new_fs);
138         if (retval)
139                 goto errout;
140
141         retval = ext2fs_close(rfs->new_fs);
142         if (retval)
143                 goto errout;
144
145         rfs->flags = flags;
146         
147         ext2fs_free(rfs->old_fs);
148         if (rfs->itable_buf)
149                 ext2fs_free_mem(&rfs->itable_buf);
150         ext2fs_free_mem(&rfs);
151         
152         return 0;
153
154 errout:
155         if (rfs->new_fs)
156                 ext2fs_free(rfs->new_fs);
157         if (rfs->itable_buf)
158                 ext2fs_free_mem(&rfs->itable_buf);
159         ext2fs_free_mem(&rfs);
160         return retval;
161 }
162
163 /* --------------------------------------------------------------------
164  *
165  * Resize processing, phase 1.
166  *
167  * In this phase we adjust the in-memory superblock information, and
168  * initialize any new parts of the inode table.  The new parts of the
169  * inode table are created in virgin disk space, so we can abort here
170  * without any side effects.
171  * --------------------------------------------------------------------
172  */
173
174 /*
175  * This routine adjusts the superblock and other data structures...
176  */
177 static errcode_t adjust_superblock(ext2_resize_t rfs, blk_t new_size)
178 {
179         ext2_filsys fs;
180         int             overhead = 0;
181         int             rem, adj = 0;
182         errcode_t       retval;
183         ext2_ino_t      real_end;
184         blk_t           blk, group_block;
185         unsigned long   i, j, old_desc_blocks;
186         int             old_numblocks, numblocks, adjblocks;
187         unsigned int    meta_bg, meta_bg_size;
188         int             has_super;
189         unsigned long   max_group;
190         
191         fs = rfs->new_fs;
192         fs->super->s_blocks_count = new_size;
193         ext2fs_mark_super_dirty(fs);
194         ext2fs_mark_bb_dirty(fs);
195         ext2fs_mark_ib_dirty(fs);
196
197 retry:
198         fs->group_desc_count = (fs->super->s_blocks_count -
199                                 fs->super->s_first_data_block +
200                                 EXT2_BLOCKS_PER_GROUP(fs->super) - 1)
201                 / EXT2_BLOCKS_PER_GROUP(fs->super);
202         if (fs->group_desc_count == 0)
203                 return EXT2_ET_TOOSMALL;
204         fs->desc_blocks = (fs->group_desc_count +
205                            EXT2_DESC_PER_BLOCK(fs->super) - 1)
206                 / EXT2_DESC_PER_BLOCK(fs->super);
207
208         /*
209          * Overhead is the number of bookkeeping blocks per group.  It
210          * includes the superblock backup, the group descriptor
211          * backups, the inode bitmap, the block bitmap, and the inode
212          * table.
213          */
214         overhead = (int) (2 + fs->inode_blocks_per_group);
215
216         if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1))
217                 overhead += 1 + fs->desc_blocks + 
218                         fs->super->s_reserved_gdt_blocks;
219
220         /*
221          * See if the last group is big enough to support the
222          * necessary data structures.  If not, we need to get rid of
223          * it.
224          */
225         rem = (fs->super->s_blocks_count - fs->super->s_first_data_block) %
226                 fs->super->s_blocks_per_group;
227         if ((fs->group_desc_count == 1) && rem && (rem < overhead))
228                 return EXT2_ET_TOOSMALL;
229         if (rem && (rem < overhead+50)) {
230                 fs->super->s_blocks_count -= rem;
231                 goto retry;
232         }
233         /*
234          * Adjust the number of inodes
235          */
236         fs->super->s_inodes_count = fs->super->s_inodes_per_group *
237                 fs->group_desc_count;
238
239         /*
240          * Adjust the number of free blocks
241          */
242         blk = rfs->old_fs->super->s_blocks_count;
243         if (blk > fs->super->s_blocks_count)
244                 fs->super->s_free_blocks_count -=
245                         (blk - fs->super->s_blocks_count);
246         else
247                 fs->super->s_free_blocks_count +=
248                         (fs->super->s_blocks_count - blk);
249
250         /*
251          * Adjust the number of reserved blocks
252          */
253         blk = rfs->old_fs->super->s_r_blocks_count * 100 /
254                 rfs->old_fs->super->s_blocks_count;
255         fs->super->s_r_blocks_count = ((fs->super->s_blocks_count * blk)
256                                        / 100);
257
258         /*
259          * Adjust the bitmaps for size
260          */
261         retval = ext2fs_resize_inode_bitmap(fs->super->s_inodes_count,
262                                             fs->super->s_inodes_count,
263                                             fs->inode_map);
264         if (retval) goto errout;
265         
266         real_end = ((EXT2_BLOCKS_PER_GROUP(fs->super)
267                      * fs->group_desc_count)) - 1 +
268                              fs->super->s_first_data_block;
269         retval = ext2fs_resize_block_bitmap(fs->super->s_blocks_count-1,
270                                             real_end, fs->block_map);
271
272         if (retval) goto errout;
273
274         /*
275          * Reallocate the group descriptors as necessary.
276          */
277         if (rfs->old_fs->desc_blocks != fs->desc_blocks) {
278                 retval = ext2fs_resize_mem(rfs->old_fs->desc_blocks *
279                                            fs->blocksize,
280                                            fs->desc_blocks * fs->blocksize,
281                                            &fs->group_desc);
282                 if (retval)
283                         goto errout;
284         }
285
286         /*
287          * Check to make sure there are enough inodes
288          */
289         if ((rfs->old_fs->super->s_inodes_count -
290              rfs->old_fs->super->s_free_inodes_count) >
291             rfs->new_fs->super->s_inodes_count) {
292                 retval = ENOSPC;
293                 goto errout;
294         }
295
296         /*
297          * If the resize_inode feature is set, and we are changing the
298          * number of descriptor blocks, then adjust
299          * s_reserved_gdt_blocks if possible to avoid needing to move
300          * the inode table either now or in the future.
301          */
302         if ((fs->super->s_feature_compat & 
303              EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
304             (rfs->old_fs->desc_blocks != fs->desc_blocks)) {
305                 int new;
306
307                 new = ((int) fs->super->s_reserved_gdt_blocks) + 
308                         (rfs->old_fs->desc_blocks - fs->desc_blocks);
309                 if (new < 0)
310                         new = 0;
311                 if (new > fs->blocksize/4)
312                         new = fs->blocksize/4;
313                 fs->super->s_reserved_gdt_blocks = new;
314                 if (new == 0)
315                         fs->super->s_feature_compat &= 
316                                 ~EXT2_FEATURE_COMPAT_RESIZE_INODE;
317         }
318
319         /*
320          * If we are shrinking the number block groups, we're done and
321          * can exit now.
322          */
323         if (rfs->old_fs->group_desc_count > fs->group_desc_count) {
324                 retval = 0;
325                 goto errout;
326         }
327         /*
328          * Fix the count of the last (old) block group
329          */
330         old_numblocks = (rfs->old_fs->super->s_blocks_count -
331                          rfs->old_fs->super->s_first_data_block) %
332                                  rfs->old_fs->super->s_blocks_per_group;
333         if (!old_numblocks)
334                 old_numblocks = rfs->old_fs->super->s_blocks_per_group;
335         if (rfs->old_fs->group_desc_count == fs->group_desc_count) {
336                 numblocks = (rfs->new_fs->super->s_blocks_count -
337                              rfs->new_fs->super->s_first_data_block) %
338                                      rfs->new_fs->super->s_blocks_per_group;
339                 if (!numblocks)
340                         numblocks = rfs->new_fs->super->s_blocks_per_group;
341         } else
342                 numblocks = rfs->new_fs->super->s_blocks_per_group;
343         i = rfs->old_fs->group_desc_count - 1;
344         fs->group_desc[i].bg_free_blocks_count += (numblocks-old_numblocks);
345                 
346         /*
347          * If the number of block groups is staying the same, we're
348          * done and can exit now.  (If the number block groups is
349          * shrinking, we had exited earlier.)
350          */
351         if (rfs->old_fs->group_desc_count >= fs->group_desc_count) {
352                 retval = 0;
353                 goto errout;
354         }
355         /*
356          * Initialize the new block group descriptors
357          */
358         retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
359                                 &rfs->itable_buf);
360         if (retval)
361                 goto errout;
362
363         memset(rfs->itable_buf, 0, fs->blocksize * fs->inode_blocks_per_group);
364         group_block = fs->super->s_first_data_block +
365                 rfs->old_fs->group_desc_count * fs->super->s_blocks_per_group;
366
367         adj = rfs->old_fs->group_desc_count;
368         max_group = fs->group_desc_count - adj;
369         if (rfs->progress) {
370                 retval = rfs->progress(rfs, E2_RSZ_EXTEND_ITABLE_PASS,
371                                        0, max_group);
372                 if (retval)
373                         goto errout;
374         }
375         if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
376                 old_desc_blocks = fs->super->s_first_meta_bg;
377         else
378                 old_desc_blocks = fs->desc_blocks + 
379                         fs->super->s_reserved_gdt_blocks;
380         for (i = rfs->old_fs->group_desc_count;
381              i < fs->group_desc_count; i++) {
382                 memset(&fs->group_desc[i], 0,
383                        sizeof(struct ext2_group_desc));
384                 adjblocks = 0;
385
386                 if (i == fs->group_desc_count-1) {
387                         numblocks = (fs->super->s_blocks_count -
388                                      fs->super->s_first_data_block) %
389                                              fs->super->s_blocks_per_group;
390                         if (!numblocks)
391                                 numblocks = fs->super->s_blocks_per_group;
392                 } else
393                         numblocks = fs->super->s_blocks_per_group;
394
395                 has_super = ext2fs_bg_has_super(fs, i);
396                 if (has_super) {
397                         ext2fs_mark_block_bitmap(fs->block_map, group_block);
398                         adjblocks++;
399                 }
400                 meta_bg_size = (fs->blocksize /
401                                 sizeof (struct ext2_group_desc));
402                 meta_bg = i / meta_bg_size;
403                 if (!(fs->super->s_feature_incompat &
404                       EXT2_FEATURE_INCOMPAT_META_BG) ||
405                     (meta_bg < fs->super->s_first_meta_bg)) {
406                         if (has_super) {
407                                 for (j=0; j < old_desc_blocks; j++)
408                                         ext2fs_mark_block_bitmap(fs->block_map,
409                                                          group_block + 1 + j);
410                                 adjblocks += old_desc_blocks;
411                         }
412                 } else {
413                         if (has_super)
414                                 has_super = 1;
415                         if (((i % meta_bg_size) == 0) ||
416                             ((i % meta_bg_size) == 1) ||
417                             ((i % meta_bg_size) == (meta_bg_size-1)))
418                                 ext2fs_mark_block_bitmap(fs->block_map,
419                                                  group_block + has_super);
420                 }
421                 
422                 adjblocks += 2 + fs->inode_blocks_per_group;
423                 
424                 numblocks -= adjblocks;
425                 fs->super->s_free_blocks_count -= adjblocks;
426                 fs->super->s_free_inodes_count +=
427                         fs->super->s_inodes_per_group;
428                 fs->group_desc[i].bg_free_blocks_count = numblocks;
429                 fs->group_desc[i].bg_free_inodes_count =
430                         fs->super->s_inodes_per_group;
431                 fs->group_desc[i].bg_used_dirs_count = 0;
432
433                 retval = ext2fs_allocate_group_table(fs, i, 0);
434                 if (retval) goto errout;
435
436                 /*
437                  * Write out the new inode table
438                  */
439                 retval = io_channel_write_blk(fs->io,
440                                               fs->group_desc[i].bg_inode_table,
441                                               fs->inode_blocks_per_group,
442                                               rfs->itable_buf);
443                 if (retval) goto errout;
444
445                 io_channel_flush(fs->io);
446                 if (rfs->progress) {
447                         retval = rfs->progress(rfs, E2_RSZ_EXTEND_ITABLE_PASS,
448                                                i - adj + 1, max_group);
449                         if (retval)
450                                 goto errout;
451                 }
452                 group_block += fs->super->s_blocks_per_group;
453         }
454         io_channel_flush(fs->io);
455         retval = 0;
456
457 errout:
458         return retval;
459 }
460
461 /* --------------------------------------------------------------------
462  *
463  * Resize processing, phase 2.
464  *
465  * In this phase we adjust determine which blocks need to be moved, in
466  * blocks_to_move().  We then copy the blocks to their ultimate new
467  * destinations using block_mover().  Since we are copying blocks to
468  * their new locations, again during this pass we can abort without
469  * any problems.
470  * --------------------------------------------------------------------
471  */
472
473 /*
474  * This helper function creates a block bitmap with all of the
475  * filesystem meta-data blocks.
476  */
477 static errcode_t mark_table_blocks(ext2_filsys fs,
478                                    ext2fs_block_bitmap bmap)
479 {
480         blk_t                   block, b;
481         unsigned int            j;
482         dgrp_t                  i;
483         unsigned long           meta_bg, meta_bg_size;
484         int                     has_super;
485         unsigned int            old_desc_blocks;
486         errcode_t               retval;
487
488         meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc));
489         block = fs->super->s_first_data_block;
490         if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
491                 old_desc_blocks = fs->super->s_first_meta_bg;
492         else
493                 old_desc_blocks = fs->desc_blocks + 
494                         fs->super->s_reserved_gdt_blocks;
495         for (i = 0; i < fs->group_desc_count; i++) {
496                 ext2fs_reserve_super_and_bgd(fs, i, bmap);
497         
498                 /*
499                  * Mark the blocks used for the inode table
500                  */
501                 for (j = 0, b = fs->group_desc[i].bg_inode_table;
502                      j < (unsigned int) fs->inode_blocks_per_group;
503                      j++, b++)
504                         ext2fs_mark_block_bitmap(bmap, b);
505                             
506                 /*
507                  * Mark block used for the block bitmap 
508                  */
509                 ext2fs_mark_block_bitmap(bmap,
510                                          fs->group_desc[i].bg_block_bitmap);
511
512                 /*
513                  * Mark block used for the inode bitmap 
514                  */
515                 ext2fs_mark_block_bitmap(bmap,
516                                          fs->group_desc[i].bg_inode_bitmap);
517                 block += fs->super->s_blocks_per_group;
518         }
519         return 0;
520 }
521
522 /*
523  * This function checks to see if a particular block (either a
524  * superblock or a block group descriptor) overlaps with an inode or
525  * block bitmap block, or with the inode table.
526  */
527 static void mark_fs_metablock(ext2_resize_t rfs,
528                               ext2fs_block_bitmap meta_bmap,
529                               int group, blk_t blk)
530 {
531         ext2_filsys     fs = rfs->new_fs;
532         
533         ext2fs_mark_block_bitmap(rfs->reserve_blocks, blk);
534         ext2fs_mark_block_bitmap(fs->block_map, blk);
535
536         /*
537          * Check to see if we overlap with the inode or block bitmap,
538          * or the inode tables.  If not, and the block is in use, then
539          * mark it as a block to be moved.
540          */
541         if (IS_BLOCK_BM(fs, group, blk)) {
542                 FS_BLOCK_BM(fs, group) = 0;
543                 rfs->needed_blocks++;
544         } else if (IS_INODE_BM(fs, group, blk)) {
545                 FS_INODE_BM(fs, group) = 0;
546                 rfs->needed_blocks++;
547         } else if (IS_INODE_TB(fs, group, blk)) {
548                 FS_INODE_TB(fs, group) = 0;
549                 rfs->needed_blocks++;
550         } else if (ext2fs_test_block_bitmap(rfs->old_fs->block_map, blk) &&
551                    !ext2fs_test_block_bitmap(meta_bmap, blk)) {
552                 ext2fs_mark_block_bitmap(rfs->move_blocks, blk);
553                 rfs->needed_blocks++;
554         }
555 }
556
557
558 /*
559  * This routine marks and unmarks reserved blocks in the new block
560  * bitmap.  It also determines which blocks need to be moved and
561  * places this information into the move_blocks bitmap.
562  */
563 static errcode_t blocks_to_move(ext2_resize_t rfs)
564 {
565         int             j, has_super;
566         dgrp_t          i, max_groups;
567         blk_t           blk, group_blk;
568         unsigned long   old_blocks, new_blocks;
569         unsigned int    meta_bg, meta_bg_size;
570         errcode_t       retval;
571         ext2_filsys     fs, old_fs;
572         ext2fs_block_bitmap     meta_bmap;
573
574         fs = rfs->new_fs;
575         old_fs = rfs->old_fs;
576         if (old_fs->super->s_blocks_count > fs->super->s_blocks_count)
577                 fs = rfs->old_fs;
578         
579         retval = ext2fs_allocate_block_bitmap(fs, _("reserved blocks"),
580                                               &rfs->reserve_blocks);
581         if (retval)
582                 return retval;
583
584         retval = ext2fs_allocate_block_bitmap(fs, _("blocks to be moved"),
585                                               &rfs->move_blocks);
586         if (retval)
587                 return retval;
588
589         retval = ext2fs_allocate_block_bitmap(fs, _("meta-data blocks"), 
590                                               &meta_bmap);
591         if (retval)
592                 return retval;
593         
594         retval = mark_table_blocks(old_fs, meta_bmap);
595         if (retval)
596                 return retval;
597
598         fs = rfs->new_fs;
599         
600         /*
601          * If we're shrinking the filesystem, we need to move all of
602          * the blocks that don't fit any more
603          */
604         for (blk = fs->super->s_blocks_count;
605              blk < old_fs->super->s_blocks_count; blk++) {
606                 if (ext2fs_test_block_bitmap(old_fs->block_map, blk) &&
607                     !ext2fs_test_block_bitmap(meta_bmap, blk)) {
608                         ext2fs_mark_block_bitmap(rfs->move_blocks, blk);
609                         rfs->needed_blocks++;
610                 }
611                 ext2fs_mark_block_bitmap(rfs->reserve_blocks, blk);
612         }
613         
614         if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) {
615                 old_blocks = old_fs->super->s_first_meta_bg;
616                 new_blocks = fs->super->s_first_meta_bg;
617         } else {
618                 old_blocks = old_fs->desc_blocks + old_fs->super->s_reserved_gdt_blocks;
619                 new_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
620         }
621         
622         if (old_blocks == new_blocks) {
623                 retval = 0;
624                 goto errout;
625         }
626
627         max_groups = fs->group_desc_count;
628         if (max_groups > old_fs->group_desc_count)
629                 max_groups = old_fs->group_desc_count;
630         group_blk = old_fs->super->s_first_data_block;
631         /*
632          * If we're reducing the number of descriptor blocks, this
633          * makes life easy.  :-)   We just have to mark some extra
634          * blocks as free.
635          */
636         if (old_blocks > new_blocks) {
637                 for (i = 0; i < max_groups; i++) {
638                         if (!ext2fs_bg_has_super(fs, i)) {
639                                 group_blk += fs->super->s_blocks_per_group;
640                                 continue;
641                         }
642                         for (blk = group_blk+1+new_blocks;
643                              blk < group_blk+1+old_blocks; blk++) {
644                                 ext2fs_unmark_block_bitmap(fs->block_map,
645                                                            blk);
646                                 rfs->needed_blocks--;
647                         }
648                         group_blk += fs->super->s_blocks_per_group;
649                 }
650                 retval = 0;
651                 goto errout;
652         }
653         /*
654          * If we're increasing the number of descriptor blocks, life
655          * gets interesting....  
656          */
657         meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc));
658         for (i = 0; i < max_groups; i++) {
659                 has_super = ext2fs_bg_has_super(fs, i);
660                 if (has_super)
661                         mark_fs_metablock(rfs, meta_bmap, i, group_blk);
662
663                 meta_bg = i / meta_bg_size;
664                 if (!(fs->super->s_feature_incompat &
665                       EXT2_FEATURE_INCOMPAT_META_BG) ||
666                     (meta_bg < fs->super->s_first_meta_bg)) {
667                         if (has_super) {
668                                 for (blk = group_blk+1;
669                                      blk < group_blk + 1 + new_blocks; blk++)
670                                         mark_fs_metablock(rfs, meta_bmap, 
671                                                           i, blk);
672                         }
673                 } else {
674                         if (has_super)
675                                 has_super = 1;
676                         if (((i % meta_bg_size) == 0) ||
677                             ((i % meta_bg_size) == 1) ||
678                             ((i % meta_bg_size) == (meta_bg_size-1)))
679                                 mark_fs_metablock(rfs, meta_bmap, i,
680                                                   group_blk + has_super);
681                 }
682
683                 if (fs->group_desc[i].bg_inode_table &&
684                     fs->group_desc[i].bg_inode_bitmap &&
685                     fs->group_desc[i].bg_block_bitmap)
686                         goto next_group;
687
688                 /*
689                  * Reserve the existing meta blocks that we know
690                  * aren't to be moved.
691                  */
692                 if (fs->group_desc[i].bg_block_bitmap)
693                         ext2fs_mark_block_bitmap(rfs->reserve_blocks,
694                                  fs->group_desc[i].bg_block_bitmap);
695                 if (fs->group_desc[i].bg_inode_bitmap)
696                         ext2fs_mark_block_bitmap(rfs->reserve_blocks,
697                                  fs->group_desc[i].bg_inode_bitmap);
698                 if (fs->group_desc[i].bg_inode_table)
699                         for (blk = fs->group_desc[i].bg_inode_table, j=0;
700                              j < fs->inode_blocks_per_group ; j++, blk++)
701                                 ext2fs_mark_block_bitmap(rfs->reserve_blocks,
702                                                          blk);
703
704                 /*
705                  * Allocate the missing data structures
706                  */
707                 retval = ext2fs_allocate_group_table(fs, i,
708                                                      rfs->reserve_blocks);
709                 if (retval)
710                         goto errout;
711
712                 /*
713                  * For those structures that have changed, we need to
714                  * do bookkeepping.
715                  */
716                 if (FS_BLOCK_BM(old_fs, i) !=
717                     (blk = FS_BLOCK_BM(fs, i))) {
718                         ext2fs_mark_block_bitmap(fs->block_map, blk);
719                         if (ext2fs_test_block_bitmap(old_fs->block_map, blk) &&
720                             !ext2fs_test_block_bitmap(meta_bmap, blk))
721                                 ext2fs_mark_block_bitmap(rfs->move_blocks,
722                                                          blk);
723                 }
724                 if (FS_INODE_BM(old_fs, i) !=
725                     (blk = FS_INODE_BM(fs, i))) {
726                         ext2fs_mark_block_bitmap(fs->block_map, blk);
727                         if (ext2fs_test_block_bitmap(old_fs->block_map, blk) &&
728                             !ext2fs_test_block_bitmap(meta_bmap, blk))
729                                 ext2fs_mark_block_bitmap(rfs->move_blocks,
730                                                          blk);
731                 }
732
733                 /*
734                  * The inode table, if we need to relocate it, is
735                  * handled specially.  We have to reserve the blocks
736                  * for both the old and the new inode table, since we
737                  * can't have the inode table be destroyed during the
738                  * block relocation phase.
739                  */
740                 if (FS_INODE_TB(fs, i) == FS_INODE_TB(old_fs, i))
741                         goto next_group; /* inode table not moved */
742
743                 rfs->needed_blocks += fs->inode_blocks_per_group;
744
745                 /*
746                  * Mark the new inode table as in use in the new block
747                  * allocation bitmap, and move any blocks that might 
748                  * be necessary.
749                  */
750                 for (blk = fs->group_desc[i].bg_inode_table, j=0;
751                      j < fs->inode_blocks_per_group ; j++, blk++) {
752                         ext2fs_mark_block_bitmap(fs->block_map, blk);
753                         if (ext2fs_test_block_bitmap(old_fs->block_map, blk) &&
754                             !ext2fs_test_block_bitmap(meta_bmap, blk))
755                                 ext2fs_mark_block_bitmap(rfs->move_blocks,
756                                                          blk);
757                 }
758                 
759                 /*
760                  * Make sure the old inode table is reserved in the
761                  * block reservation bitmap.
762                  */
763                 for (blk = rfs->old_fs->group_desc[i].bg_inode_table, j=0;
764                      j < fs->inode_blocks_per_group ; j++, blk++)
765                         ext2fs_mark_block_bitmap(rfs->reserve_blocks, blk);
766                 
767         next_group:
768                 group_blk += rfs->new_fs->super->s_blocks_per_group;
769         }
770         retval = 0;
771
772 errout:
773         if (meta_bmap)
774                 ext2fs_free_block_bitmap(meta_bmap);
775         
776         return retval;
777 }
778
779 /*
780  * This helper function tries to allocate a new block.  We try to
781  * avoid hitting the original group descriptor blocks at least at
782  * first, since we want to make it possible to recover from a badly
783  * aborted resize operation as much as possible.
784  *
785  * In the future, I may further modify this routine to balance out
786  * where we get the new blocks across the various block groups.
787  * Ideally we would allocate blocks that corresponded with the block
788  * group of the containing inode, and keep contiguous blocks
789  * together.  However, this very difficult to do efficiently, since we
790  * don't have the necessary information up front.
791  */
792
793 #define AVOID_OLD       1
794 #define DESPERATION     2
795
796 static void init_block_alloc(ext2_resize_t rfs)
797 {
798         rfs->alloc_state = AVOID_OLD;
799         rfs->new_blk = rfs->new_fs->super->s_first_data_block;
800 #if 0
801         /* HACK for testing */
802         if (rfs->new_fs->super->s_blocks_count >
803             rfs->old_fs->super->s_blocks_count)
804                 rfs->new_blk = rfs->old_fs->super->s_blocks_count;
805 #endif
806 }
807
808 static blk_t get_new_block(ext2_resize_t rfs)
809 {
810         ext2_filsys     fs = rfs->new_fs;
811         
812         while (1) {
813                 if (rfs->new_blk >= fs->super->s_blocks_count) {
814                         if (rfs->alloc_state == DESPERATION)
815                                 return 0;
816
817 #ifdef RESIZE2FS_DEBUG
818                         if (rfs->flags & RESIZE_DEBUG_BMOVE)
819                                 printf("Going into desperation mode "
820                                        "for block allocations\n");
821 #endif                  
822                         rfs->alloc_state = DESPERATION;
823                         rfs->new_blk = fs->super->s_first_data_block;
824                         continue;
825                 }
826                 if (ext2fs_test_block_bitmap(fs->block_map, rfs->new_blk) ||
827                     ext2fs_test_block_bitmap(rfs->reserve_blocks,
828                                              rfs->new_blk) ||
829                     ((rfs->alloc_state == AVOID_OLD) &&
830                      (rfs->new_blk < rfs->old_fs->super->s_blocks_count) &&
831                      ext2fs_test_block_bitmap(rfs->old_fs->block_map,
832                                               rfs->new_blk))) {
833                         rfs->new_blk++;
834                         continue;
835                 }
836                 return rfs->new_blk;
837         }
838 }
839
840 static errcode_t block_mover(ext2_resize_t rfs)
841 {
842         blk_t                   blk, old_blk, new_blk;
843         ext2_filsys             fs = rfs->new_fs;
844         ext2_filsys             old_fs = rfs->old_fs;
845         errcode_t               retval;
846         int                     size, c;
847         int                     to_move, moved;
848         ext2_badblocks_list     badblock_list = 0;
849         int                     bb_modified = 0;
850         
851         retval = ext2fs_read_bb_inode(old_fs, &badblock_list);
852         if (retval)
853                 return retval;
854
855         new_blk = fs->super->s_first_data_block;
856         if (!rfs->itable_buf) {
857                 retval = ext2fs_get_mem(fs->blocksize *
858                                         fs->inode_blocks_per_group,
859                                         &rfs->itable_buf);
860                 if (retval)
861                         return retval;
862         }
863         retval = ext2fs_create_extent_table(&rfs->bmap, 0);
864         if (retval)
865                 return retval;
866
867         /*
868          * The first step is to figure out where all of the blocks
869          * will go.
870          */
871         to_move = moved = 0;
872         init_block_alloc(rfs);
873         for (blk = old_fs->super->s_first_data_block;
874              blk < old_fs->super->s_blocks_count; blk++) {
875                 if (!ext2fs_test_block_bitmap(old_fs->block_map, blk))
876                         continue;
877                 if (!ext2fs_test_block_bitmap(rfs->move_blocks, blk))
878                         continue;
879                 if (ext2fs_badblocks_list_test(badblock_list, blk)) {
880                         ext2fs_badblocks_list_del(badblock_list, blk);
881                         bb_modified++;
882                         continue;
883                 }
884
885                 new_blk = get_new_block(rfs);
886                 if (!new_blk) {
887                         retval = ENOSPC;
888                         goto errout;
889                 }
890                 ext2fs_mark_block_bitmap(fs->block_map, new_blk);
891                 ext2fs_add_extent_entry(rfs->bmap, blk, new_blk);
892                 to_move++;
893         }
894         
895         if (to_move == 0) {
896                 if (rfs->bmap) {
897                         ext2fs_free_extent_table(rfs->bmap);
898                         rfs->bmap = 0;
899                 }
900                 retval = 0;
901                 goto errout;
902         }
903
904         /*
905          * Step two is to actually move the blocks
906          */
907         retval =  ext2fs_iterate_extent(rfs->bmap, 0, 0, 0);
908         if (retval) goto errout;
909
910         if (rfs->progress) {
911                 retval = (rfs->progress)(rfs, E2_RSZ_BLOCK_RELOC_PASS,
912                                          0, to_move);
913                 if (retval)
914                         goto errout;
915         }
916         while (1) {
917                 retval = ext2fs_iterate_extent(rfs->bmap, &old_blk, &new_blk, &size);
918                 if (retval) goto errout;
919                 if (!size)
920                         break;
921 #ifdef RESIZE2FS_DEBUG
922                 if (rfs->flags & RESIZE_DEBUG_BMOVE)
923                         printf("Moving %d blocks %u->%u\n",
924                                size, old_blk, new_blk);
925 #endif
926                 do {
927                         c = size;
928                         if (c > fs->inode_blocks_per_group)
929                                 c = fs->inode_blocks_per_group;
930                         retval = io_channel_read_blk(fs->io, old_blk, c,
931                                                      rfs->itable_buf);
932                         if (retval) goto errout;
933                         retval = io_channel_write_blk(fs->io, new_blk, c,
934                                                       rfs->itable_buf);
935                         if (retval) goto errout;
936                         size -= c;
937                         new_blk += c;
938                         old_blk += c;
939                         moved += c;
940                         if (rfs->progress) {
941                                 io_channel_flush(fs->io);
942                                 retval = (rfs->progress)(rfs,
943                                                 E2_RSZ_BLOCK_RELOC_PASS,
944                                                 moved, to_move);
945                                 if (retval)
946                                         goto errout;
947                         }
948                 } while (size > 0);
949                 io_channel_flush(fs->io);
950         }
951
952 errout:
953         if (badblock_list) {
954                 if (!retval && bb_modified)
955                         retval = ext2fs_update_bb_inode(old_fs,
956                                                         badblock_list);
957                 ext2fs_badblocks_list_free(badblock_list);
958         }
959         return retval;
960 }
961
962
963 /* --------------------------------------------------------------------
964  *
965  * Resize processing, phase 3
966  *
967  * --------------------------------------------------------------------
968  */
969
970
971 struct process_block_struct {
972         ext2_resize_t           rfs;
973         ext2_ino_t              ino;
974         struct ext2_inode *     inode;
975         errcode_t               error;
976         int                     is_dir;
977         int                     changed;
978 };
979
980 static int process_block(ext2_filsys fs, blk_t  *block_nr,
981                          e2_blkcnt_t blockcnt, 
982                          blk_t ref_block EXT2FS_ATTR((unused)),
983                          int ref_offset EXT2FS_ATTR((unused)), void *priv_data)
984 {
985         struct process_block_struct *pb;
986         errcode_t       retval;
987         blk_t           block, new_block;
988         int             ret = 0;
989
990         pb = (struct process_block_struct *) priv_data;
991         block = *block_nr;
992         if (pb->rfs->bmap) {
993                 new_block = ext2fs_extent_translate(pb->rfs->bmap, block);
994                 if (new_block) {
995                         *block_nr = new_block;
996                         ret |= BLOCK_CHANGED;
997                         pb->changed = 1;
998 #ifdef RESIZE2FS_DEBUG
999                         if (pb->rfs->flags & RESIZE_DEBUG_BMOVE)
1000                                 printf("ino=%u, blockcnt=%lld, %u->%u\n", 
1001                                        pb->ino, blockcnt, block, new_block);
1002 #endif
1003                         block = new_block;
1004                 }
1005         }
1006         if (pb->is_dir) {
1007                 retval = ext2fs_add_dir_block(fs->dblist, pb->ino,
1008                                               block, (int) blockcnt);
1009                 if (retval) {
1010                         pb->error = retval;
1011                         ret |= BLOCK_ABORT;
1012                 }
1013         }
1014         return ret;
1015 }
1016
1017 /*
1018  * Progress callback
1019  */
1020 static errcode_t progress_callback(ext2_filsys fs, 
1021                                    ext2_inode_scan scan EXT2FS_ATTR((unused)),
1022                                    dgrp_t group, void * priv_data)
1023 {
1024         ext2_resize_t rfs = (ext2_resize_t) priv_data;
1025         errcode_t               retval;
1026
1027         /*
1028          * This check is to protect against old ext2 libraries.  It
1029          * shouldn't be needed against new libraries.
1030          */
1031         if ((group+1) == 0)
1032                 return 0;
1033
1034         if (rfs->progress) {
1035                 io_channel_flush(fs->io);
1036                 retval = (rfs->progress)(rfs, E2_RSZ_INODE_SCAN_PASS,
1037                                          group+1, fs->group_desc_count);
1038                 if (retval)
1039                         return retval;
1040         }
1041         
1042         return 0;
1043 }
1044
1045 static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
1046 {
1047         struct process_block_struct     pb;
1048         ext2_ino_t              ino, new_inode;
1049         struct ext2_inode       inode;
1050         ext2_inode_scan         scan = NULL;
1051         errcode_t               retval;
1052         int                     group;
1053         char                    *block_buf = 0;
1054         ext2_ino_t              start_to_move;
1055         blk_t                   orig_size, new_block;
1056         
1057         if ((rfs->old_fs->group_desc_count <=
1058              rfs->new_fs->group_desc_count) &&
1059             !rfs->bmap)
1060                 return 0;
1061
1062         /*
1063          * Save the original size of the old filesystem, and
1064          * temporarily set the size to be the new size if the new size
1065          * is larger.  We need to do this to avoid catching an error
1066          * by the block iterator routines
1067          */
1068         orig_size = rfs->old_fs->super->s_blocks_count;
1069         if (orig_size < rfs->new_fs->super->s_blocks_count)
1070                 rfs->old_fs->super->s_blocks_count =
1071                         rfs->new_fs->super->s_blocks_count;
1072
1073         retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan);
1074         if (retval) goto errout;
1075
1076         retval = ext2fs_init_dblist(rfs->old_fs, 0);
1077         if (retval) goto errout;
1078         retval = ext2fs_get_mem(rfs->old_fs->blocksize * 3, &block_buf);
1079         if (retval) goto errout;
1080
1081         start_to_move = (rfs->new_fs->group_desc_count *
1082                          rfs->new_fs->super->s_inodes_per_group);
1083         
1084         if (rfs->progress) {
1085                 retval = (rfs->progress)(rfs, E2_RSZ_INODE_SCAN_PASS,
1086                                          0, rfs->old_fs->group_desc_count);
1087                 if (retval)
1088                         goto errout;
1089         }
1090         ext2fs_set_inode_callback(scan, progress_callback, (void *) rfs);
1091         pb.rfs = rfs;
1092         pb.inode = &inode;
1093         pb.error = 0;
1094         new_inode = EXT2_FIRST_INODE(rfs->new_fs->super);
1095         /*
1096          * First, copy all of the inodes that need to be moved
1097          * elsewhere in the inode table
1098          */
1099         while (1) {
1100                 retval = ext2fs_get_next_inode(scan, &ino, &inode);
1101                 if (retval) goto errout;
1102                 if (!ino)
1103                         break;
1104
1105                 if (inode.i_links_count == 0 && ino != EXT2_RESIZE_INO)
1106                         continue; /* inode not in use */
1107
1108                 pb.is_dir = LINUX_S_ISDIR(inode.i_mode);
1109                 pb.changed = 0;
1110
1111                 if (inode.i_file_acl && rfs->bmap) {
1112                         new_block = ext2fs_extent_translate(rfs->bmap, 
1113                                                             inode.i_file_acl);
1114                         if (new_block) {
1115                                 inode.i_file_acl = new_block;
1116                                 retval = ext2fs_write_inode(rfs->old_fs, 
1117                                                             ino, &inode);
1118                                 if (retval) goto errout;
1119                         }
1120                 }
1121                 
1122                 if (ext2fs_inode_has_valid_blocks(&inode) &&
1123                     (rfs->bmap || pb.is_dir)) {
1124                         pb.ino = ino;
1125                         retval = ext2fs_block_iterate2(rfs->old_fs,
1126                                                        ino, 0, block_buf,
1127                                                        process_block, &pb);
1128                         if (retval)
1129                                 goto errout;
1130                         if (pb.error) {
1131                                 retval = pb.error;
1132                                 goto errout;
1133                         }
1134                 }
1135
1136                 if (ino <= start_to_move)
1137                         continue; /* Don't need to move it. */
1138
1139                 /*
1140                  * Find a new inode
1141                  */
1142                 while (1) { 
1143                         if (!ext2fs_test_inode_bitmap(rfs->new_fs->inode_map, 
1144                                                       new_inode))
1145                                 break;
1146                         new_inode++;
1147                         if (new_inode > rfs->new_fs->super->s_inodes_count) {
1148                                 retval = ENOSPC;
1149                                 goto errout;
1150                         }
1151                 }
1152                 ext2fs_mark_inode_bitmap(rfs->new_fs->inode_map, new_inode);
1153                 if (pb.changed) {
1154                         /* Get the new version of the inode */
1155                         retval = ext2fs_read_inode(rfs->old_fs, ino, &inode);
1156                         if (retval) goto errout;
1157                 }
1158                 inode.i_ctime = time(0);
1159                 retval = ext2fs_write_inode(rfs->old_fs, new_inode, &inode);
1160                 if (retval) goto errout;
1161
1162                 group = (new_inode-1) / EXT2_INODES_PER_GROUP(rfs->new_fs->super);
1163                 if (LINUX_S_ISDIR(inode.i_mode))
1164                         rfs->new_fs->group_desc[group].bg_used_dirs_count++;
1165                 
1166 #ifdef RESIZE2FS_DEBUG
1167                 if (rfs->flags & RESIZE_DEBUG_INODEMAP)
1168                         printf("Inode moved %u->%u\n", ino, new_inode);
1169 #endif
1170                 if (!rfs->imap) {
1171                         retval = ext2fs_create_extent_table(&rfs->imap, 0);
1172                         if (retval)
1173                                 goto errout;
1174                 }
1175                 ext2fs_add_extent_entry(rfs->imap, ino, new_inode);
1176         }
1177         io_channel_flush(rfs->old_fs->io);
1178
1179 errout:
1180         rfs->old_fs->super->s_blocks_count = orig_size;
1181         if (rfs->bmap) {
1182                 ext2fs_free_extent_table(rfs->bmap);
1183                 rfs->bmap = 0;
1184         }
1185         if (scan)
1186                 ext2fs_close_inode_scan(scan);
1187         if (block_buf)
1188                 ext2fs_free_mem(&block_buf);
1189         return retval;
1190 }
1191
1192 /* --------------------------------------------------------------------
1193  *
1194  * Resize processing, phase 4.
1195  *
1196  * --------------------------------------------------------------------
1197  */
1198
1199 struct istruct {
1200         ext2_resize_t rfs;
1201         errcode_t       err;
1202         unsigned long   max_dirs;
1203         int             num;
1204 };
1205
1206 static int check_and_change_inodes(ext2_ino_t dir, 
1207                                    int entry EXT2FS_ATTR((unused)),
1208                                    struct ext2_dir_entry *dirent, int offset,
1209                                    int  blocksize EXT2FS_ATTR((unused)),
1210                                    char *buf EXT2FS_ATTR((unused)), 
1211                                    void *priv_data)
1212 {
1213         struct istruct *is = (struct istruct *) priv_data;
1214         struct ext2_inode       inode;
1215         ext2_ino_t              new_inode;
1216         errcode_t               retval;
1217
1218         if (is->rfs->progress && offset == 0) {
1219                 io_channel_flush(is->rfs->old_fs->io);
1220                 is->err = (is->rfs->progress)(is->rfs,
1221                                               E2_RSZ_INODE_REF_UPD_PASS,
1222                                               ++is->num, is->max_dirs);
1223                 if (is->err)
1224                         return DIRENT_ABORT;
1225         }
1226
1227         if (!dirent->inode)
1228                 return 0;
1229
1230         new_inode = ext2fs_extent_translate(is->rfs->imap, dirent->inode);
1231
1232         if (!new_inode)
1233                 return 0;
1234 #ifdef RESIZE2FS_DEBUG
1235         if (is->rfs->flags & RESIZE_DEBUG_INODEMAP)
1236                 printf("Inode translate (dir=%u, name=%.*s, %u->%u)\n",
1237                        dir, dirent->name_len&0xFF, dirent->name,
1238                        dirent->inode, new_inode);
1239 #endif
1240
1241         dirent->inode = new_inode;
1242
1243         /* Update the directory mtime and ctime */
1244         retval = ext2fs_read_inode(is->rfs->old_fs, dir, &inode);
1245         if (retval == 0) {
1246                 inode.i_mtime = inode.i_ctime = time(0);
1247                 ext2fs_write_inode(is->rfs->old_fs, dir, &inode);
1248         }
1249
1250         return DIRENT_CHANGED;
1251 }
1252
1253 static errcode_t inode_ref_fix(ext2_resize_t rfs)
1254 {
1255         errcode_t               retval;
1256         struct istruct          is;
1257         
1258         if (!rfs->imap)
1259                 return 0;
1260        
1261         /*
1262          * Now, we iterate over all of the directories to update the
1263          * inode references
1264          */
1265         is.num = 0;
1266         is.max_dirs = ext2fs_dblist_count(rfs->old_fs->dblist);
1267         is.rfs = rfs;
1268         is.err = 0;
1269
1270         if (rfs->progress) {
1271                 retval = (rfs->progress)(rfs, E2_RSZ_INODE_REF_UPD_PASS,
1272                                          0, is.max_dirs);
1273                 if (retval)
1274                         goto errout;
1275         }
1276         
1277         retval = ext2fs_dblist_dir_iterate(rfs->old_fs->dblist,
1278                                            DIRENT_FLAG_INCLUDE_EMPTY, 0,
1279                                            check_and_change_inodes, &is);
1280         if (retval)
1281                 goto errout;
1282         if (is.err) {
1283                 retval = is.err;
1284                 goto errout;
1285         }
1286
1287 errout:
1288         ext2fs_free_extent_table(rfs->imap);
1289         rfs->imap = 0;
1290         return retval;
1291 }
1292
1293
1294 /* --------------------------------------------------------------------
1295  *
1296  * Resize processing, phase 5.
1297  *
1298  * In this phase we actually move the inode table around, and then
1299  * update the summary statistics.  This is scary, since aborting here
1300  * will potentially scramble the filesystem.  (We are moving the
1301  * inode tables around in place, and so the potential for lost data,
1302  * or at the very least scrambling the mapping between filenames and
1303  * inode numbers is very high in case of a power failure here.)
1304  * --------------------------------------------------------------------
1305  */
1306
1307
1308 /*
1309  * A very scary routine --- this one moves the inode table around!!!
1310  *
1311  * After this you have to use the rfs->new_fs file handle to read and
1312  * write inodes.
1313  */
1314 static errcode_t move_itables(ext2_resize_t rfs)
1315 {
1316         int             n, num, size, diff;
1317         dgrp_t          i, max_groups;
1318         ext2_filsys     fs = rfs->new_fs;
1319         char            *cp;
1320         blk_t           old_blk, new_blk, blk;
1321         errcode_t       retval;
1322         int             j, to_move, moved;
1323
1324         max_groups = fs->group_desc_count;
1325         if (max_groups > rfs->old_fs->group_desc_count)
1326                 max_groups = rfs->old_fs->group_desc_count;
1327
1328         size = fs->blocksize * fs->inode_blocks_per_group;
1329         if (!rfs->itable_buf) {
1330                 retval = ext2fs_get_mem(size, &rfs->itable_buf);
1331                 if (retval)
1332                         return retval;
1333         }
1334
1335         /*
1336          * Figure out how many inode tables we need to move
1337          */
1338         to_move = moved = 0;
1339         for (i=0; i < max_groups; i++)
1340                 if (rfs->old_fs->group_desc[i].bg_inode_table !=
1341                     fs->group_desc[i].bg_inode_table)
1342                         to_move++;
1343
1344         if (to_move == 0)
1345                 return 0;
1346
1347         if (rfs->progress) {
1348                 retval = rfs->progress(rfs, E2_RSZ_MOVE_ITABLE_PASS,
1349                                        0, to_move);
1350                 if (retval)
1351                         goto errout;
1352         }
1353
1354         rfs->old_fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
1355
1356         for (i=0; i < max_groups; i++) {
1357                 old_blk = rfs->old_fs->group_desc[i].bg_inode_table;
1358                 new_blk = fs->group_desc[i].bg_inode_table;
1359                 diff = new_blk - old_blk;
1360                 
1361 #ifdef RESIZE2FS_DEBUG
1362                 if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) 
1363                         printf("Itable move group %d block %u->%u (diff %d)\n",
1364                                i, old_blk, new_blk, diff);
1365 #endif
1366                 
1367                 if (!diff)
1368                         continue;
1369
1370                 retval = io_channel_read_blk(fs->io, old_blk,
1371                                              fs->inode_blocks_per_group,
1372                                              rfs->itable_buf);
1373                 if (retval) 
1374                         goto errout;
1375                 /*
1376                  * The end of the inode table segment often contains
1377                  * all zeros, and we're often only moving the inode
1378                  * table down a block or two.  If so, we can optimize
1379                  * things by not rewriting blocks that we know to be zero
1380                  * already.
1381                  */
1382                 for (cp = rfs->itable_buf+size, n=0; n < size; n++, cp--)
1383                         if (*cp)
1384                                 break;
1385                 n = n >> EXT2_BLOCK_SIZE_BITS(fs->super);
1386 #ifdef RESIZE2FS_DEBUG
1387                 if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) 
1388                         printf("%d blocks of zeros...\n", n);
1389 #endif
1390                 num = fs->inode_blocks_per_group;
1391                 if (n > diff)
1392                         num -= n;
1393
1394                 retval = io_channel_write_blk(fs->io, new_blk,
1395                                               num, rfs->itable_buf);
1396                 if (retval) {
1397                         io_channel_write_blk(fs->io, old_blk,
1398                                              num, rfs->itable_buf);
1399                         goto errout;
1400                 }
1401                 if (n > diff) {
1402                         retval = io_channel_write_blk(fs->io,
1403                               old_blk + fs->inode_blocks_per_group,
1404                               diff, (rfs->itable_buf +
1405                                      (fs->inode_blocks_per_group - diff) *
1406                                      fs->blocksize));
1407                         if (retval)
1408                                 goto errout;
1409                 }
1410
1411                 for (blk = rfs->old_fs->group_desc[i].bg_inode_table, j=0;
1412                      j < fs->inode_blocks_per_group ; j++, blk++)
1413                         ext2fs_unmark_block_bitmap(fs->block_map, blk);
1414
1415                 rfs->old_fs->group_desc[i].bg_inode_table = new_blk;
1416                 ext2fs_mark_super_dirty(rfs->old_fs);
1417                 ext2fs_flush(rfs->old_fs);
1418
1419                 if (rfs->progress) {
1420                         retval = rfs->progress(rfs, E2_RSZ_MOVE_ITABLE_PASS,
1421                                                ++moved, to_move);
1422                         if (retval)
1423                                 goto errout;
1424                 }
1425         }
1426         mark_table_blocks(fs, fs->block_map);
1427         ext2fs_flush(fs);
1428 #ifdef RESIZE2FS_DEBUG
1429         if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) 
1430                 printf("Inode table move finished.\n");
1431 #endif
1432         return 0;
1433         
1434 errout:
1435         return retval;
1436 }
1437
1438 /*
1439  * Fix the resize inode 
1440  */
1441 static errcode_t fix_resize_inode(ext2_filsys fs)
1442 {
1443         struct ext2_inode       inode;
1444         errcode_t               retval;
1445         char *                  block_buf;
1446
1447         if (!(fs->super->s_feature_compat & 
1448               EXT2_FEATURE_COMPAT_RESIZE_INODE))
1449                 return 0;
1450
1451         retval = ext2fs_get_mem(fs->blocksize, &block_buf);
1452         if (retval) goto errout;
1453
1454         retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
1455         if (retval) goto errout;
1456
1457         inode.i_blocks = fs->blocksize/512;
1458
1459         retval = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode);
1460         if (retval) goto errout;
1461
1462         if (!inode.i_block[EXT2_DIND_BLOCK]) {
1463                 /* 
1464                  * Avoid zeroing out block #0; that's rude.  This
1465                  * should never happen anyway since the filesystem
1466                  * should be fsck'ed and we assume it is consistent.
1467                  */
1468                 fprintf(stderr, 
1469                         _("Should never happen: resize inode corrupt!\n"));
1470                 exit(1);
1471         }
1472
1473         memset(block_buf, 0, fs->blocksize);
1474
1475         retval = io_channel_write_blk(fs->io, inode.i_block[EXT2_DIND_BLOCK],
1476                                       1, block_buf);
1477         if (retval) goto errout;
1478         
1479         retval = ext2fs_create_resize_inode(fs);
1480         if (retval)
1481                 goto errout;
1482
1483 errout:
1484         if (block_buf)
1485                 ext2fs_free_mem(&block_buf);
1486         return retval;
1487 }
1488
1489 /*
1490  * Finally, recalculate the summary information
1491  */
1492 static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs)
1493 {
1494         blk_t           blk;
1495         ext2_ino_t      ino;
1496         unsigned int    group = 0;
1497         unsigned int    count = 0;
1498         int             total_free = 0;
1499         int             group_free = 0;
1500
1501         /*
1502          * First calculate the block statistics
1503          */
1504         for (blk = fs->super->s_first_data_block;
1505              blk < fs->super->s_blocks_count; blk++) {
1506                 if (!ext2fs_fast_test_block_bitmap(fs->block_map, blk)) {
1507                         group_free++;
1508                         total_free++;
1509                 }
1510                 count++;
1511                 if ((count == fs->super->s_blocks_per_group) ||
1512                     (blk == fs->super->s_blocks_count-1)) {
1513                         fs->group_desc[group++].bg_free_blocks_count =
1514                                 group_free;
1515                         count = 0;
1516                         group_free = 0;
1517                 }
1518         }
1519         fs->super->s_free_blocks_count = total_free;
1520         
1521         /*
1522          * Next, calculate the inode statistics
1523          */
1524         group_free = 0;
1525         total_free = 0;
1526         count = 0;
1527         group = 0;
1528         for (ino = 1; ino <= fs->super->s_inodes_count; ino++) {
1529                 if (!ext2fs_fast_test_inode_bitmap(fs->inode_map, ino)) {
1530                         group_free++;
1531                         total_free++;
1532                 }
1533                 count++;
1534                 if ((count == fs->super->s_inodes_per_group) ||
1535                     (ino == fs->super->s_inodes_count)) {
1536                         fs->group_desc[group++].bg_free_inodes_count =
1537                                 group_free;
1538                         count = 0;
1539                         group_free = 0;
1540                 }
1541         }
1542         fs->super->s_free_inodes_count = total_free;
1543         ext2fs_mark_super_dirty(fs);
1544         return 0;
1545 }