Whamcloud - gitweb
Many files:
[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, j;
25         struct ext2_group_desc *new;
26         char            *buf;
27         int             old_numblocks, numblocks, adjblocks;
28         
29         fs = rfs->new_fs;
30         fs->super->s_blocks_count = new_size;
31         ext2fs_mark_super_dirty(fs);
32         ext2fs_mark_bb_dirty(fs);
33         ext2fs_mark_ib_dirty(fs);
34
35 retry:
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         }
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;
168                 }
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;
179
180                 retval = ext2fs_allocate_group_table(fs, i, 0);
181                 if (retval)
182                         return retval;
183
184                 for (blk=fs->group_desc[i].bg_inode_table, j=0;
185                      j < fs->inode_blocks_per_group;
186                      blk++, j++) {
187                         retval = io_channel_write_blk(fs->io, blk, 1, buf);
188                         if (retval)
189                                 return retval;
190                 }
191                 group_block += fs->super->s_blocks_per_group;
192         }
193         return 0;
194 }
195
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  */
201 static errcode_t determine_relocations(ext2_resize_t rfs)
202 {
203         int     i, j;
204         blk_t   blk, group_blk;
205         unsigned long old_blocks, new_blocks;
206         errcode_t       retval;
207         ext2_filsys     fs = rfs->new_fs;
208
209         retval = ext2fs_allocate_block_bitmap(rfs->old_fs,
210                                               "blocks to be moved",
211                                               &rfs->reserve_blocks);
212         if (retval)
213                 return retval;
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         }
225         
226         old_blocks = rfs->old_fs->desc_blocks;
227         new_blocks = fs->desc_blocks;
228
229         if (old_blocks == new_blocks)
230                 return 0;
231
232         group_blk = rfs->old_fs->super->s_first_data_block;
233         /*
234          * If we're reducing the number of descriptor blocks, this
235          * makes life easy.  :-)   We just have to mark some extra
236          * blocks as free.
237          */
238         if (old_blocks > new_blocks) {
239                 for (i = 0; i < fs->group_desc_count; i++) {
240                         if (!ext2fs_bg_has_super(fs, i)) {
241                                 group_blk += fs->super->s_blocks_per_group;
242                                 continue;
243                         }
244                         for (blk = group_blk+1+old_blocks;
245                              blk < group_blk+1+new_blocks; blk++)
246                                 ext2fs_unmark_block_bitmap(fs->block_map,
247                                                            blk);
248                         group_blk += fs->super->s_blocks_per_group;
249                 }
250                 return 0;
251         }
252         /*
253          * If we're increasing the number of descriptor blocks, life
254          * gets interesting....  
255          */
256         for (i = 0; i < fs->group_desc_count; i++) {
257                 if (!ext2fs_bg_has_super(fs, i))
258                         goto next_group;
259
260                 for (blk = group_blk;
261                      blk < group_blk + 1 + new_blocks; blk++) {
262                         ext2fs_mark_block_bitmap(rfs->reserve_blocks, blk);
263                         ext2fs_mark_block_bitmap(fs->block_map, blk);
264
265                         /*
266                          * Check to see if we overlap with the inode
267                          * or block bitmap
268                          */
269                         if (blk == fs->group_desc[i].bg_inode_bitmap)
270                                 fs->group_desc[i].bg_block_bitmap = 0;  
271                         if (blk == fs->group_desc[i].bg_inode_bitmap)
272                                 fs->group_desc[i].bg_inode_bitmap = 0;
273
274                         /*
275                          * Check to see if we overlap with the inode
276                          * table
277                          */
278                         if (blk < fs->group_desc[i].bg_inode_table)
279                                 continue;
280                         if (blk >= (fs->group_desc[i].bg_inode_table +
281                                     fs->inode_blocks_per_group))
282                                 continue;
283                         fs->group_desc[i].bg_inode_table = 0;
284                         blk = fs->group_desc[i].bg_inode_table +
285                                 fs->inode_blocks_per_group - 1;
286                 }
287                 if (fs->group_desc[i].bg_inode_table &&
288                     fs->group_desc[i].bg_inode_bitmap &&
289                     fs->group_desc[i].bg_block_bitmap)
290                         goto next_group;
291
292                 /*
293                  * Allocate the missing bitmap and inode table
294                  * structures, passing in rfs->reserve_blocks to
295                  * prevent a conflict.  
296                  */
297                 if (fs->group_desc[i].bg_block_bitmap)
298                         ext2fs_mark_block_bitmap(rfs->reserve_blocks,
299                                  fs->group_desc[i].bg_block_bitmap);
300                 if (fs->group_desc[i].bg_inode_bitmap)
301                         ext2fs_mark_block_bitmap(rfs->reserve_blocks,
302                                  fs->group_desc[i].bg_inode_bitmap);
303                 if (fs->group_desc[i].bg_inode_table)
304                         for (blk = fs->group_desc[i].bg_inode_table, j=0;
305                              j < fs->inode_blocks_per_group ; j++, blk++)
306                                 ext2fs_mark_block_bitmap(rfs->reserve_blocks,
307                                                          blk);
308
309                 retval = ext2fs_allocate_group_table(fs, i,
310                                                      rfs->reserve_blocks);
311                 if (retval)
312                         return retval;
313
314                 /*
315                  * Now make sure these blocks are reserved in the new
316                  * block bitmap
317                  */
318                 ext2fs_mark_block_bitmap(fs->block_map,
319                                          fs->group_desc[i].bg_block_bitmap);
320                 ext2fs_mark_block_bitmap(fs->block_map,
321                                          fs->group_desc[i].bg_inode_bitmap);
322
323                 for (blk = fs->group_desc[i].bg_inode_table, j=0;
324                      j < fs->inode_blocks_per_group ; j++, blk++)
325                         ext2fs_mark_block_bitmap(fs->block_map, blk);
326                 
327                 /*
328                  * Mark the inode tables which will need to move, and
329                  * restore the old inode table location (for now)
330                  */
331                 if (fs->group_desc[i].bg_inode_table !=
332                     rfs->old_fs->group_desc[i].bg_inode_table) {
333                         rfs->move_itable[i] = fs->group_desc[i].bg_inode_table;
334                         fs->group_desc[i].bg_inode_table =
335                                 rfs->old_fs->group_desc[i].bg_inode_table;
336                 }
337                 
338         next_group:
339                 group_blk += rfs->new_fs->super->s_blocks_per_group;
340         }
341 }
342
343
344 /*
345  * This is the top-level routine which does the dirty deed....
346  */
347 errcode_t resize_fs(ext2_filsys fs, blk_t new_size)
348 {
349         ext2_resize_t   rfs;
350         errcode_t       retval;
351
352         retval = ext2fs_read_bitmaps(fs);
353         if (retval)
354                 return retval;
355         
356         /*
357          * Create the data structure
358          */
359         rfs = malloc(sizeof(struct ext2_resize_struct));
360         if (!rfs)
361                 return ENOMEM;
362         memset(rfs, 0, sizeof(struct ext2_resize_struct));
363
364         rfs->move_itable = malloc(sizeof(blk_t) * fs->group_desc_count);
365         if (!rfs->move_itable) {
366                 retval = ENOMEM;
367                 goto errout;
368         }
369         memset(rfs->move_itable, 0, sizeof(blk_t) * fs->group_desc_count);
370         
371         rfs->old_fs = fs;
372         retval = ext2fs_dup_handle(fs, &rfs->new_fs);
373         if (retval)
374                 goto errout;
375
376         retval = adjust_superblock(rfs, new_size);
377         if (retval)
378                 goto errout;
379
380         retval = determine_relocations(rfs);
381         if (retval)
382                 goto errout;
383
384         printf("\nOld superblock:\n");
385         list_super(rfs->old_fs->super);
386         printf("\n\nNew superblock:\n");
387         list_super(rfs->new_fs->super);
388         printf("\n");
389
390         retval = ext2fs_move_blocks(rfs->old_fs, rfs->reserve_blocks,
391                                     EXT2_BMOVE_GET_DBLIST);
392
393         retval = ext2fs_close(rfs->new_fs);
394         if (retval)
395                 return retval;
396         
397         ext2fs_free(rfs->old_fs);
398         
399         return 0;
400
401 errout:
402         if (rfs->move_itable)
403                 free(rfs->move_itable);
404         if (rfs->new_fs)
405                 ext2fs_free(rfs->new_fs);
406         free(rfs);
407         return retval;
408 }
409