From d323f8fb369089b97d6f3bf0f8d64ceeab0b10f5 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Wed, 15 Dec 2004 14:39:16 -0500 Subject: [PATCH] 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. --- e2fsck/pass1.c | 13 +++- lib/ext2fs/Makefile.in | 10 +++ lib/ext2fs/alloc_sb.c | 3 +- lib/ext2fs/closefs.c | 29 +------- lib/ext2fs/ext2_err.et.in | 3 + lib/ext2fs/ext2fs.h | 12 +++- lib/ext2fs/initialize.c | 73 +++++++++++++++---- lib/ext2fs/res_gdt.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++ lib/ext2fs/sparse.c | 78 ++++++++++++++++++++ misc/mke2fs.c | 131 +++++++++++++++++++++++++++------- 10 files changed, 458 insertions(+), 70 deletions(-) create mode 100644 lib/ext2fs/res_gdt.c create mode 100644 lib/ext2fs/sparse.c diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 2fde92b..9632214 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -1525,7 +1525,18 @@ static int process_block(ext2_filsys fs, return 0; } - mark_block_used(ctx, blk); + if (p->ino == EXT2_RESIZE_INO) { + if (blockcnt >= 0) { + /* Check that the block is in the correct place + * in the appropriate backup reserved gdt area */ + } else if (blockcnt == BLOCK_COUNT_IND) { + /* Check that the block is in the correct place + * in the primary reserved gdt area */ + } else /* The resize inode's DIND block should be + * allocated as a normal block. */ + mark_block_used(ctx, blk); + } else + mark_block_used(ctx, blk); p->num_blocks++; if (blockcnt >= 0) p->last_block = blockcnt; diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in index 0ad0078..6543e39 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 e4733c9..ef40b93 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 71fa9e4..6fe012c 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 b3ada29..99ffa2e 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 6a02127..4904a3b 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 2ee06b5..3b80f46 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 0000000..727b826 --- /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 +#include +#include +#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 0000000..90b028f --- /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 + +#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; +} diff --git a/misc/mke2fs.c b/misc/mke2fs.c index fcc9eec..8bf5c18 100644 --- a/misc/mke2fs.c +++ b/misc/mke2fs.c @@ -94,7 +94,7 @@ static void usage(void) " [-N number-of-inodes]\n\t[-m reserved-blocks-percentage] " "[-o creator-os] [-g blocks-per-group]\n\t[-L volume-label] " "[-M last-mounted-directory] [-O feature[,...]]\n\t" - "[-r fs-revision] [-R raid_opts] [-qvSV] device [blocks-count]\n"), + "[-r fs-revision] [-R options] [-qvSV] device [blocks-count]\n"), program_name); exit(1); } @@ -714,6 +714,11 @@ static void show_stats(ext2_filsys fs) s->s_r_blocks_count, 100.0 * s->s_r_blocks_count / s->s_blocks_count); printf(_("First data block=%u\n"), s->s_first_data_block); + if (s->s_reserved_gdt_blocks) + printf(_("Maximum filesystem blocks=%lu\n"), + (s->s_reserved_gdt_blocks + fs->desc_blocks) * + (fs->blocksize / sizeof(struct ext2_group_desc)) * + s->s_blocks_per_group); if (fs->group_desc_count > 1) printf(_("%u block groups\n"), fs->group_desc_count); else @@ -726,7 +731,7 @@ static void show_stats(ext2_filsys fs) printf("\n"); return; } - + printf(_("Superblock backups stored on blocks: ")); group_block = s->s_first_data_block; col_left = 0; @@ -768,17 +773,17 @@ static int set_os(struct ext2_super_block *sb, char *os) #define PATH_SET "PATH=/sbin" -static void parse_raid_opts(const char *opts) +static void parse_r_opts(struct ext2_super_block *param, const char *opts) { char *buf, *token, *next, *p, *arg; int len; - int raid_usage = 0; + int r_usage = 0; len = strlen(opts); buf = malloc(len+1); if (!buf) { - fprintf(stderr, _("Couldn't allocate memory to parse " - "raid options!\n")); + fprintf(stderr, + _("Couldn't allocate memory to parse options!\n")); exit(1); } strcpy(buf, opts); @@ -788,7 +793,7 @@ static void parse_raid_opts(const char *opts) if (p) { *p = 0; next = p+1; - } + } arg = strchr(token, '='); if (arg) { *arg = 0; @@ -796,32 +801,91 @@ static void parse_raid_opts(const char *opts) } if (strcmp(token, "stride") == 0) { if (!arg) { - raid_usage++; + r_usage++; continue; } fs_stride = strtoul(arg, &p, 0); if (*p || (fs_stride == 0)) { fprintf(stderr, _("Invalid stride parameter.\n")); - raid_usage++; + r_usage++; + continue; + } + } else if (!strcmp(token, "resize")) { + unsigned long resize = 1; + int tmp; + + if (!arg) { + r_usage++; + continue; + } + + p = &arg[strlen(arg) - 1]; + + switch(*p++) { + case 'T': + case 't': resize <<= 10; /* no break */ + case 'G': + case 'g': resize <<= 10; /* no break */ + case 'M': + case 'm': resize <<= 10; /* no break */ + case 'K': + case 'k': resize >>= param->s_log_block_size -10; *p = 0; break; + case 'b': resize >>= param->s_log_block_size - 9; *p = 0; break; + case '0': break; + case '1': break; + case '2': break; + case '3': break; + case '4': break; + case '5': break; + case '6': break; + case '7': break; + case '8': break; + case '9': break; + default: r_usage++; continue; + } + + resize *= strtoul(arg, NULL, 0); + + if (resize == 0) { + fprintf(stderr, + _("Invalid resize parameter.\n")); + r_usage++; continue; } + param->s_feature_compat |= + EXT2_FEATURE_COMPAT_RESIZE_INODE; + tmp = param->s_blocks_per_group; + if (tmp > EXT2_MAX_BLOCKS_PER_GROUP(param)) + tmp = EXT2_MAX_BLOCKS_PER_GROUP(param); + resize = (resize + tmp - 1) / tmp; + tmp = (1 << param->s_log_block_size) / + sizeof(struct ext2_group_desc); + resize = (resize + tmp - 1) / tmp; + /* XXX param->s_res_gdt_blocks = resize - existing + cur_groups = (resize - sb->s_first_data_block + + EXT2_BLOCKS_PER_GROUP(super) - 1) /bpg; + cur_gdb = (cur_groups + gdpb - 1) / gdpb; + */ + } else - raid_usage++; + r_usage++; } - if (raid_usage) { - fprintf(stderr, _("\nBad raid options specified.\n\n" - "Raid options are separated by commas, " + if (r_usage) { + fprintf(stderr, _("\nBad options specified.\n\n" + "Options are separated by commas, " "and may take an argument which\n" "\tis set off by an equals ('=') sign.\n\n" "Valid raid options are:\n" - "\tstride=\n\n")); + "\tstride=\n" + "\tresize=\n\n")); exit(1); } } static __u32 ok_features[3] = { EXT3_FEATURE_COMPAT_HAS_JOURNAL | + EXT2_FEATURE_COMPAT_RESIZE_INODE | EXT2_FEATURE_COMPAT_DIR_INDEX, /* Compat */ EXT2_FEATURE_INCOMPAT_FILETYPE| /* Incompat */ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV| @@ -844,9 +908,8 @@ static void PRS(int argc, char *argv[]) ext2_ino_t num_inodes = 0; errcode_t retval; char * oldpath = getenv("PATH"); - char * raid_opts = 0; + char * r_opts = 0; const char * fs_type = 0; - int default_features = 1; blk_t dev_size; #ifdef __linux__ struct utsname ut; @@ -1056,14 +1119,12 @@ static void PRS(int argc, char *argv[]) mount_dir = optarg; break; case 'O': - if (!strcmp(optarg, "none") || default_features) { + if (!strcmp(optarg, "none")) { param.s_feature_compat = 0; param.s_feature_incompat = 0; param.s_feature_ro_compat = 0; - default_features = 0; - } - if (!strcmp(optarg, "none")) break; + } if (e2p_edit_feature(optarg, ¶m.s_feature_compat, ok_features)) { @@ -1073,7 +1134,7 @@ static void PRS(int argc, char *argv[]) } break; case 'R': - raid_opts = optarg; + r_opts = optarg; break; case 'S': super_only = 1; @@ -1093,7 +1154,7 @@ static void PRS(int argc, char *argv[]) device_name = argv[optind]; optind++; if (optind < argc) { - unsigned long tmp2 = strtoul(argv[optind++], &tmp, 0); + unsigned long long tmp2 = strtoull(argv[optind++], &tmp, 0); if ((*tmp) || (tmp2 > 0xfffffffful)) { com_err(program_name, 0, _("bad blocks count - %s"), @@ -1115,9 +1176,6 @@ static void PRS(int argc, char *argv[]) exit(0); } - if (raid_opts) - parse_raid_opts(raid_opts); - /* * If there's no blocksize specified and there is a journal * device, use it to figure out the blocksize @@ -1262,6 +1320,20 @@ static void PRS(int argc, char *argv[]) set_fs_defaults(fs_type, ¶m, blocksize, sector_size, &inode_ratio); blocksize = EXT2_BLOCK_SIZE(¶m); + if (r_opts) + parse_r_opts(¶m, r_opts); + + /* Since sparse_super is the default, we would only have a problem + * here if it was explicitly disabled. + */ + if ((param.s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) && + !(param.s_feature_ro_compat&EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + com_err(program_name, 0, + _("reserved online resize blocks not supported " + "on non-sparse filesystem")); + exit(1); + } + if (param.s_blocks_per_group) { if (param.s_blocks_per_group < 256 || param.s_blocks_per_group > 8 * (unsigned) blocksize) { @@ -1299,9 +1371,8 @@ static void PRS(int argc, char *argv[]) * Calculate number of blocks to reserve */ param.s_r_blocks_count = (param.s_blocks_count * reserved_ratio) / 100; - } - + int main (int argc, char *argv[]) { errcode_t retval = 0; @@ -1460,6 +1531,12 @@ int main (int argc, char *argv[]) create_lost_and_found(fs); reserve_inodes(fs); create_bad_block_inode(fs, bb_list); + retval = ext2fs_create_resize_inode(fs); + if (retval) { + com_err("ext2fs_create_resize_inode", retval, + _("while reserving blocks for online resize")); + exit(1); + } } if (journal_device) { -- 1.8.3.1