diff options
author | Theodore Ts'o <tytso@mit.edu> | 2004-12-15 14:39:16 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2004-12-15 14:39:16 -0500 |
commit | d323f8fb369089b97d6f3bf0f8d64ceeab0b10f5 (patch) | |
tree | 3467ae14a562a89d7cc09e27441ecf3d250da853 /lib | |
parent | acb79d9d19737cdd3c64bfd83c101fdeed47766e (diff) | |
download | e2fsprogs-d323f8fb369089b97d6f3bf0f8d64ceeab0b10f5.tar.gz |
Add support for on-line resizing ala the resize inode. This patch
is taken from Fedora Core 3's e2fsprogs 1.35-11.2.src.rpm's
e2fsprogs-resize.patch.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ext2fs/Makefile.in | 10 | ||||
-rw-r--r-- | lib/ext2fs/alloc_sb.c | 3 | ||||
-rw-r--r-- | lib/ext2fs/closefs.c | 29 | ||||
-rw-r--r-- | lib/ext2fs/ext2_err.et.in | 3 | ||||
-rw-r--r-- | lib/ext2fs/ext2fs.h | 12 | ||||
-rw-r--r-- | lib/ext2fs/initialize.c | 73 | ||||
-rw-r--r-- | lib/ext2fs/res_gdt.c | 176 | ||||
-rw-r--r-- | lib/ext2fs/sparse.c | 78 |
8 files changed, 342 insertions, 42 deletions
diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in index 0ad00789..6543e396 100644 --- a/lib/ext2fs/Makefile.in +++ b/lib/ext2fs/Makefile.in @@ -58,7 +58,9 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \ openfs.o \ read_bb.o \ read_bb_file.o \ + res_gdt.o \ rw_bitmaps.o \ + sparse.o \ swapfs.o \ unix_io.o \ unlink.o \ @@ -115,8 +117,10 @@ SRCS= ext2_err.c \ $(srcdir)/openfs.c \ $(srcdir)/read_bb.c \ $(srcdir)/read_bb_file.c \ + $(srcdir)/res_gdt.c \ $(srcdir)/rs_bitmap.c \ $(srcdir)/rw_bitmaps.c \ + $(srcdir)/sparse.c \ $(srcdir)/swapfs.c \ $(srcdir)/test_io.c \ $(srcdir)/unix_io.c \ @@ -460,6 +464,10 @@ read_bb_file.o: $(srcdir)/read_bb_file.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h +res_gdt.o: $(srcdir)/res_gdt.c $(srcdir)/ext2_fs.h \ + $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ + $(top_builddir)/lib/ext2fs/ext2_err.h rs_bitmap.o: $(srcdir)/rs_bitmap.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ @@ -468,6 +476,8 @@ rw_bitmaps.o: $(srcdir)/rw_bitmaps.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h $(srcdir)/e2image.h +sparse.o: $(srcdir)/sparse.c $(srcdir)/ext2_fs.h \ + $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h swapfs.o: $(srcdir)/swapfs.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ diff --git a/lib/ext2fs/alloc_sb.c b/lib/ext2fs/alloc_sb.c index e4733c99..ef40b938 100644 --- a/lib/ext2fs/alloc_sb.c +++ b/lib/ext2fs/alloc_sb.c @@ -40,7 +40,8 @@ int ext2fs_reserve_super_and_bgd(ext2_filsys fs, if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else - old_desc_blocks = fs->desc_blocks; + old_desc_blocks = + fs->desc_blocks + fs->super->s_reserved_gdt_blocks; if (super_blk || (group == 0)) ext2fs_mark_block_bitmap(bmap, super_blk); diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c index 71fa9e49..6fe012ce 100644 --- a/lib/ext2fs/closefs.c +++ b/lib/ext2fs/closefs.c @@ -19,32 +19,6 @@ #include "ext2_fs.h" #include "ext2fsP.h" -static int test_root(int a, int b) -{ - if (a == 0) - return 1; - while (1) { - if (a == 1) - return 1; - if (a % b) - return 0; - a = a / b; - } -} - -int ext2fs_bg_has_super(ext2_filsys fs, int group_block) -{ - if (!(fs->super->s_feature_ro_compat & - EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) - return 1; - - if (test_root(group_block, 3) || (test_root(group_block, 5)) || - test_root(group_block, 7)) - return 1; - - return 0; -} - int ext2fs_super_and_bgd_loc(ext2_filsys fs, dgrp_t group, blk_t *ret_super_blk, @@ -63,7 +37,8 @@ int ext2fs_super_and_bgd_loc(ext2_filsys fs, if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else - old_desc_blocks = fs->desc_blocks; + old_desc_blocks = + fs->desc_blocks + fs->super->s_reserved_gdt_blocks; if (group == fs->group_desc_count-1) { numblocks = (fs->super->s_blocks_count - diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in index b3ada297..99ffa2ec 100644 --- a/lib/ext2fs/ext2_err.et.in +++ b/lib/ext2fs/ext2_err.et.in @@ -287,5 +287,8 @@ ec EXT2_ET_TOO_MANY_INODES, ec EXT2_ET_NOT_IMAGE_FILE, "E2image snapshot not in use" +ec EXT2_ET_RES_GDT_BLOCKS, + "Too many reserved group descriptor blocks" + end diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index 6a02127a..4904a3bd 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -430,6 +430,7 @@ typedef struct ext2_icount *ext2_icount_t; #define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\ EXT2_FEATURE_COMPAT_IMAGIC_INODES|\ EXT3_FEATURE_COMPAT_HAS_JOURNAL|\ + EXT2_FEATURE_COMPAT_RESIZE_INODE|\ EXT2_FEATURE_COMPAT_DIR_INDEX|\ EXT2_FEATURE_COMPAT_EXT_ATTR) @@ -600,14 +601,12 @@ extern errcode_t ext2fs_check_desc(ext2_filsys fs); /* closefs.c */ extern errcode_t ext2fs_close(ext2_filsys fs); extern errcode_t ext2fs_flush(ext2_filsys fs); -extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block); extern int ext2fs_super_and_bgd_loc(ext2_filsys fs, dgrp_t group, blk_t *ret_super_blk, blk_t *ret_old_desc_blk, blk_t *ret_new_desc_blk, int *ret_meta_bg); -extern void ext2fs_update_dynamic_rev(ext2_filsys fs); /* cmp_bitmaps.c */ extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, @@ -888,6 +887,9 @@ extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, void (*invalid)(ext2_filsys fs, blk_t blk)); +/* res_gdt.c */ +extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs); + /* rs_bitmap.c */ extern errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, __u32 new_real_end, @@ -899,6 +901,12 @@ extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, ext2fs_generic_bitmap *dest); +/* sparse.c */ +extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block); +extern void ext2fs_update_dynamic_rev(ext2_filsys fs); +extern unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned *three, + unsigned *five, unsigned *seven); + /* swapfs.c */ extern void ext2fs_swap_super(struct ext2_super_block * super); extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp); diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c index 2ee06b5c..3b80f468 100644 --- a/lib/ext2fs/initialize.c +++ b/lib/ext2fs/initialize.c @@ -58,6 +58,35 @@ #endif #define EXT2_DFL_CHECKINTERVAL (86400L * 180L) +/* + * Calculate the number of GDT blocks to reserve for online filesystem growth. + * The absolute maximum number of GDT blocks we can reserve is determined by + * the number of block pointers that can fit into a single block. + */ +static int calc_reserved_gdt_blocks(ext2_filsys fs) +{ + struct ext2_super_block *sb = fs->super; + unsigned long bpg = sb->s_blocks_per_group; + unsigned int gdpb = fs->blocksize / sizeof(struct ext2_group_desc); + unsigned long max_blocks = 0xffffffff; + unsigned long rsv_groups; + int rsv_gdb; + + /* We set it at 1024x the current filesystem size, or + * the upper block count limit (2^32), whichever is lower. + */ + if (sb->s_blocks_count < max_blocks / 1024) + max_blocks = sb->s_blocks_count * 1024; + rsv_groups = (max_blocks - sb->s_first_data_block + bpg - 1) / bpg; + rsv_gdb = (rsv_groups + gdpb - 1) / gdpb - fs->desc_blocks; + if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb)) + rsv_gdb = EXT2_ADDR_PER_BLOCK(sb); + printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %lu\n", + max_blocks, rsv_groups, rsv_gdb); + + return rsv_gdb; +} + errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs) @@ -72,6 +101,7 @@ errcode_t ext2fs_initialize(const char *name, int flags, unsigned int ipg; dgrp_t i; blk_t numblocks; + int rsv_gdt; char *buf; if (!param || !param->s_blocks_count) @@ -120,10 +150,14 @@ errcode_t ext2fs_initialize(const char *name, int flags, set_field(s_feature_incompat, 0); set_field(s_feature_ro_compat, 0); set_field(s_first_meta_bg, 0); - if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) - return EXT2_ET_UNSUPP_FEATURE; - if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) - return EXT2_ET_RO_UNSUPP_FEATURE; + if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { + retval = EXT2_ET_UNSUPP_FEATURE; + goto cleanup; + } + if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { + retval = EXT2_ET_RO_UNSUPP_FEATURE; + goto cleanup; + } set_field(s_rev_level, EXT2_GOOD_OLD_REV); if (super->s_rev_level >= EXT2_DYNAMIC_REV) { @@ -157,8 +191,7 @@ errcode_t ext2fs_initialize(const char *name, int flags, * If we're creating an external journal device, we don't need * to bother with the rest. */ - if (super->s_feature_incompat & - EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; ext2fs_mark_super_dirty(fs); *ret_fs = fs; @@ -170,8 +203,10 @@ retry: super->s_first_data_block + EXT2_BLOCKS_PER_GROUP(super) - 1) / EXT2_BLOCKS_PER_GROUP(super); - if (fs->group_desc_count == 0) - return EXT2_ET_TOOSMALL; + if (fs->group_desc_count == 0) { + retval = EXT2_ET_TOOSMALL; + goto cleanup; + } fs->desc_blocks = (fs->group_desc_count + EXT2_DESC_PER_BLOCK(super) - 1) / EXT2_DESC_PER_BLOCK(super); @@ -244,15 +279,29 @@ retry: super->s_free_inodes_count = super->s_inodes_count; /* + * check the number of reserved group descriptor table blocks + */ + if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) + rsv_gdt = calc_reserved_gdt_blocks(fs); + else + rsv_gdt = 0; + set_field(s_reserved_gdt_blocks, rsv_gdt); + if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { + retval = EXT2_ET_RES_GDT_BLOCKS; + goto cleanup; + } + + /* * Overhead is the number of bookkeeping blocks per group. It * includes the superblock backup, the group descriptor * backups, the inode bitmap, the block bitmap, and the inode * table. - * - * XXX Not all block groups need the descriptor blocks, but - * being clever is tricky... */ - overhead = (int) (3 + fs->desc_blocks + fs->inode_blocks_per_group); + + overhead = (int) (2 + fs->inode_blocks_per_group); + + if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) + overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; /* This can only happen if the user requested too many inodes */ if (overhead > super->s_blocks_per_group) diff --git a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c new file mode 100644 index 00000000..727b8267 --- /dev/null +++ b/lib/ext2fs/res_gdt.c @@ -0,0 +1,176 @@ +/* + * res_gdt.h --- reserve blocks for growing the group descriptor table + * during online resizing. + * + * Copyright (C) 2002 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * This code assumes that the reserved blocks have already been marked in-use + * during ext2fs_initialize(), so that they are not allocated for other + * uses before we can add them to the resize inode (which has to come + * after the creation of the inode table). + */ +errcode_t ext2fs_create_resize_inode(ext2_filsys fs) +{ + errcode_t retval, retval2; + struct ext2_super_block *sb; + struct ext2_inode inode; + __u32 *dindir_buf, *gdt_buf; + int rsv_add; + unsigned long long apb, inode_size; + blk_t dindir_blk, rsv_off, gdt_off, gdt_blk; + int dindir_dirty = 0, inode_dirty = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + sb = fs->super; + if (!sb->s_reserved_gdt_blocks) + return 0; + + retval = ext2fs_get_mem(2 * fs->blocksize, (void **)&dindir_buf); + if (retval) + goto out_free; + gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize); + + retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); + if (retval) + goto out_free; + + /* Maximum possible file size (we donly use the dindirect blocks) */ + apb = EXT2_ADDR_PER_BLOCK(sb); + rsv_add = fs->blocksize / 512; + if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) { + printf("reading GDT dindir %u\n", dindir_blk); + retval = io_channel_read_blk(fs->io, dindir_blk, 1, dindir_buf); + if (retval) + goto out_inode; + } else { + blk_t goal = 3 + sb->s_reserved_gdt_blocks + + fs->desc_blocks + fs->inode_blocks_per_group; + + retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk); + if (retval) + goto out_free; + inode.i_mode = LINUX_S_IFREG | 0600; + inode.i_links_count = 1; + inode.i_block[EXT2_DIND_BLOCK] = dindir_blk; + inode.i_blocks = rsv_add; + memset(dindir_buf, 0, fs->blocksize); +#ifdef RES_GDT_DEBUG + printf("allocated GDT dindir %u\n", dindir_blk); +#endif + dindir_dirty = inode_dirty = 1; + inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS; + inode_size *= fs->blocksize; + inode.i_size = inode_size & 0xFFFFFFFF; + inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF; + if(inode.i_size_high) { + sb->s_feature_ro_compat |= + EXT2_FEATURE_RO_COMPAT_LARGE_FILE; + } + inode.i_ctime = time(0); + } + + for (rsv_off = 0, gdt_off = fs->desc_blocks, + gdt_blk = sb->s_first_data_block + 1 + gdt_off; + rsv_off < sb->s_reserved_gdt_blocks; + rsv_off++, gdt_off++, gdt_blk++) { + unsigned int three = 1, five = 5, seven = 7; + unsigned int grp, last = 0; + int gdt_dirty = 0; + + gdt_off %= apb; + if (!dindir_buf[gdt_off]) { + /* FIXME XXX XXX + blk_t new_blk; + + retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk); + if (retval) + goto out_free; + if (new_blk != gdt_blk) { + // XXX free block + retval = -1; // XXX + } + */ + gdt_dirty = dindir_dirty = inode_dirty = 1; + memset(gdt_buf, 0, fs->blocksize); + dindir_buf[gdt_off] = gdt_blk; + inode.i_blocks += rsv_add; +#ifdef RES_GDT_DEBUG + printf("added primary GDT block %u at %u[%u]\n", + gdt_blk, dindir_blk, gdt_off); +#endif + } else if (dindir_buf[gdt_off] == gdt_blk) { + printf("reading primary GDT block %u\n", gdt_blk); + retval = io_channel_read_blk(fs->io,gdt_blk,1,gdt_buf); + if (retval) + goto out_dindir; + } else { + printf("bad primary GDT %u != %u at %u[%u]\n", + dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off); + retval = -1; // XXX + goto out_dindir; + } + + while ((grp = ext2fs_list_backups(fs, &three, &five, &seven)) < + fs->group_desc_count) { + blk_t expect = gdt_blk + grp * sb->s_blocks_per_group; + + if (!gdt_buf[last]) { +#ifdef RES_GDT_DEBUG + printf("added backup GDT %u grp %u@%u[%u]\n", + expect, grp, gdt_blk, last); +#endif + gdt_buf[last] = expect; + inode.i_blocks += rsv_add; + gdt_dirty = inode_dirty = 1; + } else if (gdt_buf[last] != expect) { + printf("bad backup GDT %u != %u at %u[%u]\n", + gdt_buf[last], expect, gdt_blk, last); + retval = -1; // XXX + goto out_dindir; + } + last++; + } + if (gdt_dirty) { +#ifdef RES_GDT_DEBUG + printf("writing primary GDT block %u\n", gdt_blk); +#endif + retval = io_channel_write_blk(fs->io,gdt_blk,1,gdt_buf); + if (retval) + goto out_dindir; + } + } + +out_dindir: + if (dindir_dirty) { + retval2 = io_channel_write_blk(fs->io, dindir_blk,1,dindir_buf); + if (!retval) + retval = retval2; + } +out_inode: + printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks, + inode.i_size); + if (inode_dirty) { + inode.i_atime = inode.i_mtime = time(0); + retval2 = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode); + if (!retval) + retval = retval2; + } +out_free: + ext2fs_free_mem((void **)&dindir_buf); + return retval; +} + diff --git a/lib/ext2fs/sparse.c b/lib/ext2fs/sparse.c new file mode 100644 index 00000000..90b028f0 --- /dev/null +++ b/lib/ext2fs/sparse.c @@ -0,0 +1,78 @@ +/* + * sparse.c --- find the groups in an ext2 filesystem with metadata backups + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * Copyright (C) 2002 Andreas Dilger. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include <stdio.h> + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static int test_root(int a, int b) +{ + if (a == 0) + return 1; + while (1) { + if (a == 1) + return 1; + if (a % b) + return 0; + a = a / b; + } +} + +int ext2fs_bg_has_super(ext2_filsys fs, int group_block) +{ + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) + return 1; + + if (test_root(group_block, 3) || (test_root(group_block, 5)) || + test_root(group_block, 7)) + return 1; + + return 0; +} + +/* + * Iterate through the groups which hold BACKUP superblock/GDT copies in an + * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before + * calling this for the first time. In a sparse filesystem it will be the + * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... + * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... + */ +unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three, + unsigned int *five, unsigned int *seven) +{ + unsigned int *min = three; + int mult = 3; + unsigned int ret; + + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + ret = *min; + *min += 1; + return ret; + } + + if (*five < *min) { + min = five; + mult = 5; + } + if (*seven < *min) { + min = seven; + mult = 7; + } + + ret = *min; + *min *= mult; + + return ret; +} |