Whamcloud - gitweb
ChangeLog, Makefile.in, configure.in:
[tools/e2fsprogs.git] / resize / resize2fs.c
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  */
16 static 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;
24         unsigned long   i;
25         struct ext2_group_desc *new;
26         
27         fs = rfs->new_fs;
28         fs->super->s_blocks_count = new_size;
29
30 retry:
31         fs->group_desc_count = (fs->super->s_blocks_count -
32                                 fs->super->s_first_data_block +
33                                 EXT2_BLOCKS_PER_GROUP(fs->super) - 1)
34                 / EXT2_BLOCKS_PER_GROUP(fs->super);
35         if (fs->group_desc_count == 0)
36                 return EXT2_ET_TOOSMALL;
37         fs->desc_blocks = (fs->group_desc_count +
38                            EXT2_DESC_PER_BLOCK(fs->super) - 1)
39                 / EXT2_DESC_PER_BLOCK(fs->super);
40
41         /*
42          * Overhead is the number of bookkeeping blocks per group.  It
43          * includes the superblock backup, the group descriptor
44          * backups, the inode bitmap, the block bitmap, and the inode
45          * table.
46          *
47          * XXX Not all block groups need the descriptor blocks, but
48          * being clever is tricky...
49          */
50         overhead = 3 + fs->desc_blocks + fs->inode_blocks_per_group;
51         
52         /*
53          * See if the last group is big enough to support the
54          * necessary data structures.  If not, we need to get rid of
55          * it.
56          */
57         rem = (fs->super->s_blocks_count - fs->super->s_first_data_block) %
58                 fs->super->s_blocks_per_group;
59         if ((fs->group_desc_count == 1) && rem && (rem < overhead))
60                 return EXT2_ET_TOOSMALL;
61         if (rem && (rem < overhead+50)) {
62                 fs->super->s_blocks_count -= rem;
63                 goto retry;
64         }
65         /*
66          * Adjust the number of inodes
67          */
68         fs->super->s_inodes_count = fs->super->s_inodes_per_group *
69                 fs->group_desc_count;
70
71         /*
72          * Adjust the number of free blocks
73          */
74         blk = rfs->old_fs->super->s_blocks_count;
75         if (blk > fs->super->s_blocks_count)
76                 fs->super->s_free_blocks_count -=
77                         (blk - fs->super->s_blocks_count);
78         else
79                 fs->super->s_free_blocks_count +=
80                         (fs->super->s_blocks_count - blk);
81
82         /*
83          * Adjust the bitmaps for size
84          */
85         retval = ext2fs_resize_inode_bitmap(fs->super->s_inodes_count,
86                                             fs->super->s_inodes_count,
87                                             fs->inode_map);
88         if (retval)
89                 return retval;
90         
91         real_end = ((EXT2_BLOCKS_PER_GROUP(fs->super)
92                      * fs->group_desc_count)) - 1 +
93                              fs->super->s_first_data_block;
94         retval = ext2fs_resize_block_bitmap(fs->super->s_blocks_count-1,
95                                             real_end, fs->block_map);
96
97         if (retval)
98                 return retval;
99
100         /*
101          * Reallocate the group descriptors as necessary.
102          */
103         if (rfs->old_fs->desc_blocks != fs->desc_blocks) {
104                 new = realloc(fs->group_desc,
105                               fs->desc_blocks * fs->blocksize);
106                 if (!new)
107                         return ENOMEM;
108                 fs->group_desc = new;
109         }
110         group_block = rfs->old_fs->super->s_first_data_block;
111         for (i = 0; i < fs->group_desc_count; i++) {
112                 if (i < rfs->old_fs->group_desc_count) {
113                         group_block += fs->super->s_blocks_per_group;
114                         continue;
115                 }
116                 /* XXXX */
117         }
118         
119         return 0;
120 }
121
122 /*
123  * This routine reserves a block in the new filesystem.  If the block
124  * is already used, we mark it as needing relocation.  Otherwise, we
125  * just mark it as used.
126  */
127 static reserve_block(ext2_resize_t rfs, blk_t blk)
128 {
129         if (ext2fs_test_block_bitmap(rfs->new_fs->block_map, blk))
130                 ext2fs_mark_block_bitmap(rfs->move_blocks, blk);
131         else
132                 ext2fs_mark_block_bitmap(rfs->new_fs->block_map, blk);
133 }
134
135 /*
136  * This routine is a helper function for determine_relocations().  It
137  * is called for each block group which has a superblock, and for
138  * which we need to expand the size of the descriptor table.  We have
139  * to account for the fact that in some cases we will need to move the
140  * inode table, which will mean moving or reserving blocks at the end
141  * of the inode table, since the inode table will be moved down to
142  * make space.
143  *
144  * "And the block group descriptors waddled across the street..."
145  */
146 static void make_way_for_descriptors(ext2_resize_t rfs,
147                                      int block_group,
148                                      blk_t group_blk)
149 {
150         blk_t           blk, start_blk, end_blk, itable, move_by;
151         unsigned long   i;
152         ext2_filsys     fs;
153         
154         start_blk = group_blk + rfs->old_fs->desc_blocks + 1;
155         end_blk = group_blk + rfs->new_fs->desc_blocks + 1;
156         fs = rfs->new_fs;
157         itable = fs->group_desc[block_group].bg_inode_table;
158         if (end_blk > itable) {
159                 move_by = itable - end_blk;
160                 for (blk = itable, i=0; i < move_by; blk++, i++) {
161                         ext2fs_unmark_block_bitmap(fs->block_map, blk);
162                         reserve_block(rfs, blk+fs->inode_blocks_per_group);
163                 }
164                 end_blk -= move_by;
165                 fs->group_desc[i].bg_inode_table += move_by;
166         }
167         for (blk = start_blk; blk < end_blk; blk++)
168                 reserve_block(rfs, blk);
169 }
170
171
172 /*
173  * This routine marks and unmarks reserved blocks in the new block
174  * bitmap.  It also determines which blocks need to be moved and
175  * places this information into the move_blocks bitmap.
176  */
177 static errcode_t determine_relocations(ext2_resize_t rfs)
178 {
179         int     i;
180         blk_t   blk, group_blk;
181         unsigned long old_blocks, new_blocks;
182         errcode_t       retval;
183
184         retval = ext2fs_allocate_block_bitmap(rfs->old_fs,
185                                               "blocks to be moved",
186                                               &rfs->move_blocks);
187         if (retval)
188                 return retval;
189         
190         old_blocks = rfs->old_fs->desc_blocks;
191         new_blocks = rfs->new_fs->desc_blocks;
192
193         group_blk = rfs->old_fs->super->s_first_data_block;
194         /*
195          * If we're reducing the number of descriptor blocks, this
196          * makes life easy.  :-)   We just have to mark some extra
197          * blocks as free.
198          */
199         if (old_blocks > new_blocks) {
200                 for (i = 0; i < rfs->new_fs->group_desc_count; i++) {
201                         if (!ext2fs_bg_has_super(rfs->new_fs, i)) {
202                                 group_blk += rfs->new_fs->super->s_blocks_per_group;
203                                 continue;
204                         }
205                         for (blk = group_blk+1+old_blocks;
206                              blk < group_blk+1+new_blocks; blk++)
207                                 ext2fs_unmark_block_bitmap(rfs->new_fs->block_map,
208                                                            blk);
209                         group_blk += rfs->new_fs->super->s_blocks_per_group;
210                 }
211         }
212         /*
213          * If we're increasing the number of descriptor blocks, life
214          * gets interesting.  In some cases, we will need to move the
215          * inode table.
216          */
217         if (old_blocks < new_blocks) {
218                 for (i = 0; i < rfs->new_fs->group_desc_count; i++) {
219                         if (!ext2fs_bg_has_super(rfs->new_fs, i)) {
220                                 group_blk += rfs->new_fs->super->s_blocks_per_group;
221                                 continue;
222                         }
223                         make_way_for_descriptors(rfs, i, group_blk);
224                         group_blk += rfs->new_fs->super->s_blocks_per_group;
225                 }
226         }
227         /*
228          * Finally, if we're shrinking the filesystem, we need to
229          * move all of the blocks that don't fit any more
230          */
231         for (blk = rfs->new_fs->super->s_blocks_count;
232              blk < rfs->old_fs->super->s_blocks_count; blk++) {
233                 if (ext2fs_test_block_bitmap(rfs->old_fs->block_map, blk))
234                         ext2fs_mark_block_bitmap(rfs->move_blocks, blk);
235
236         }
237 }
238
239
240
241
242
243
244 /*
245  * This is the top-level routine which does the dirty deed....
246  */
247 errcode_t resize_fs(ext2_filsys fs, blk_t new_size)
248 {
249         ext2_resize_t   rfs;
250         errcode_t       retval;
251
252         /*
253          * First, create the data structure
254          */
255         rfs = malloc(sizeof(struct ext2_resize_struct));
256         if (!rfs)
257                 return ENOMEM;
258         memset(rfs, 0, sizeof(struct ext2_resize_struct));
259
260         rfs->old_fs = fs;
261         retval = ext2fs_dup_handle(fs, &rfs->new_fs);
262         if (retval) {
263                 free(rfs);
264                 return retval;
265         }
266         retval = adjust_superblock(rfs, new_size);
267         if (retval)
268                 goto errout;
269         
270         return 0;
271
272 errout:
273         ext2fs_free(rfs->new_fs);
274         free(rfs);
275         return retval;
276 }
277