diff -ru linux-2.0.37.orig/fs/ext2/super.c linux/fs/ext2/super.c --- linux-2.0.37.orig/fs/ext2/super.c Wed Jul 14 09:41:47 1999 +++ linux/fs/ext2/super.c Mon Jul 26 00:14:33 1999 @@ -11,6 +11,8 @@ * linux/fs/minix/inode.c * * Copyright (C) 1991, 1992 Linus Torvalds + * + * Online resize by Andreas Dilger (adilger@enel.ucalgary.ca), July 1999 */ #include @@ -140,7 +142,8 @@ */ static int parse_options (char * options, unsigned long * sb_block, unsigned short *resuid, unsigned short * resgid, - unsigned long * mount_options) + unsigned long * mount_options, + unsigned long * n_blocks_count) { char * this_char; char * value; @@ -214,6 +217,20 @@ else if (!strcmp (this_char, "nogrpid") || !strcmp (this_char, "sysvgroups")) clear_opt (*mount_options, GRPID); + else if (!strcmp (this_char, "resize")){ + printk ("EXT2-fs: resize value: %s\n", value); + if (!value || !*value){ + printk ("EXT2-fs: resize requires number of " + "blocks\n"); + return 0; + } + *n_blocks_count = simple_strtoul (value, &value, 0); + if (*value) { + printk ("EXT2-fs: invalid resize option: %s\n", + value); + return 0; + } + } else if (!strcmp (this_char, "resgid")) { if (!value || !*value) { printk ("EXT2-fs: the resgid option requires " @@ -329,7 +346,7 @@ if (gdp->bg_block_bitmap < block || gdp->bg_block_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb)) { - ext2_error (sb, "ext2_check_descriptors", + ext2_warning (sb, "ext2_check_descriptors", "Block bitmap for group %d" " not in group (block %lu)!", i, (unsigned long) gdp->bg_block_bitmap); @@ -338,7 +355,7 @@ if (gdp->bg_inode_bitmap < block || gdp->bg_inode_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb)) { - ext2_error (sb, "ext2_check_descriptors", + ext2_warning (sb, "ext2_check_descriptors", "Inode bitmap for group %d" " not in group (block %lu)!", i, (unsigned long) gdp->bg_inode_bitmap); @@ -348,7 +365,7 @@ gdp->bg_inode_table + sb->u.ext2_sb.s_itb_per_group >= block + EXT2_BLOCKS_PER_GROUP(sb)) { - ext2_error (sb, "ext2_check_descriptors", + ext2_warning (sb, "ext2_check_descriptors", "Inode table for group %d" " not in group (block %lu)!", i, (unsigned long) gdp->bg_inode_table); @@ -360,6 +377,172 @@ return 1; } +static int ext2_read_descriptors (struct super_block * sb, + unsigned long logic_sb_block, + unsigned long blocks_count) +{ + int db_start; + int db_count; + unsigned long groups_count; + unsigned long o_groups_count; + struct buffer_head ** group_desc = NULL; + struct buffer_head ** o_group_desc; + int i, j; + + o_group_desc = sb->u.ext2_sb.s_group_desc; + o_groups_count = sb->u.ext2_sb.s_groups_count; + + db_start = sb->u.ext2_sb.s_db_per_group + o_groups_count ? 1 : 0; + groups_count = (blocks_count - sb->u.ext2_sb.s_es->s_first_data_block + + EXT2_BLOCKS_PER_GROUP(sb) - 1) / + EXT2_BLOCKS_PER_GROUP(sb); + db_count = (groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / + EXT2_DESC_PER_BLOCK(sb); + if (test_opt (sb, DEBUG)) + printk ("EXT2-fs: db_orig=%lu, db_start=%d, db_count=%d " + "logic_sb_block=%lu, blocks_count=%lu\n", + sb->u.ext2_sb.s_db_per_group, + db_start, db_count, logic_sb_block, blocks_count); + + if (db_start < db_count) { + group_desc = (struct buffer_head **) kmalloc (db_count * + sizeof (struct buffer_head *), GFP_KERNEL); + if (group_desc == NULL) { + ext2_warning (sb, "ext2_read_descriptors", + "not enough memory for %d groups\n", + db_count); + return 0; + } + for (i = db_start; i < db_count; i++) { + group_desc[i] = bread (sb->s_dev, + logic_sb_block + i + 1, + sb->s_blocksize); + if (!group_desc[i]) { + for (j = db_start; j < i; j++) + brelse (group_desc[j]); + kfree_s (group_desc, db_count * + sizeof (struct buffer_head *)); + ext2_warning (sb, "ext2_read_descriptors", + "unable to read group block %d\n", + i); + return 0; + } + } + + /* + * Copy over old descriptor blocks previously loaded, if any. + * We need to set s_group_desc and s_groups_count now so that + * ext2_check_descriptors() will check the new groups as well. + * We SHOULD be safe if o_group_desc == NULL, because we copy 0 + * bytes. Need to check with someone who knows... + */ + memcpy (group_desc, o_group_desc, sb->u.ext2_sb.s_db_per_group * + sizeof (struct buffer_head *)); + sb->u.ext2_sb.s_group_desc = group_desc; + } + sb->u.ext2_sb.s_groups_count = groups_count; + + if (!ext2_check_descriptors (sb)) { + sb->u.ext2_sb.s_groups_count = o_groups_count; + sb->u.ext2_sb.s_group_desc = o_group_desc; + for (j = db_start; j < db_count; j++) + brelse (group_desc[j]); + if (group_desc != NULL) + kfree_s (group_desc, + db_count * sizeof (struct buffer_head *)); + return 0; + } + + if (o_group_desc && o_group_desc != sb->u.ext2_sb.s_group_desc) + kfree_s (o_group_desc, sb->u.ext2_sb.s_db_per_group * + sizeof (struct buffer_head *)); + sb->u.ext2_sb.s_db_per_group = db_count; + + return 1; +} + +static void ext2_resize_fs (struct super_block * sb, + struct ext2_super_block * es, + unsigned long n_blocks_count) +{ + unsigned long o_blocks_count; + unsigned long o_groups_count; + unsigned long last, add; + unsigned long reserved; + struct buffer_head * bh; + struct dquot_operations * dq_op_save = sb->dq_op; + struct inode inode; + unsigned long i; + + o_blocks_count = es->s_blocks_count; + o_groups_count = sb->u.ext2_sb.s_groups_count; + + if (test_opt (sb, DEBUG)) + printk ("EXT2-fs: attempting resize from %lu to %lu blocks\n", + o_blocks_count, n_blocks_count); + + if (n_blocks_count < o_blocks_count) { + ext2_warning (sb, "ext2_resize_fs", + "unable to shrink filesystem, resize aborted"); + return; + } + else if (n_blocks_count == o_blocks_count) + return; + + /* See if the device is actually as big as what was requested */ + bh = bread (sb->s_dev, n_blocks_count, EXT2_BLOCK_SIZE(sb)); + if (!bh) { + ext2_warning (sb, "ext2_resize_fs", + "unable to read last block, resize aborted"); + return; + } + brelse (bh); + + lock_super (sb); + if (!ext2_read_descriptors (sb, sb->u.ext2_sb.s_sbh->b_blocknr, + n_blocks_count)) { + ext2_warning (sb, "ext2_resize_fs", + "group descriptor error, resize aborted"); + unlock_super (sb); + return; + } + unlock_super (sb); + + /* + * Handle the remaining blocks in the last partial group. For + * reserved calculation, let's hope we have more than 2^8 blocks + * in the original FS... If you change the reserved scaling, you + * also need to fix the scale in ext2_update_group(). + */ + o_blocks_count = es->s_blocks_count; + reserved = es->s_r_blocks_count / (o_blocks_count >> 8); + last = (o_blocks_count - 1) % EXT2_BLOCKS_PER_GROUP(sb); + add = last + EXT2_BLOCKS_PER_GROUP(sb) > n_blocks_count ? + n_blocks_count - o_blocks_count : + EXT2_BLOCKS_PER_GROUP(sb) - last; + es->s_blocks_count += add; + es->s_r_blocks_count += add * reserved >> 8; + /* + * Fake out an inode enough to "free" the new blocks in this group. + */ + inode.i_sb = sb; + sb->dq_op = NULL; + if (test_opt (sb, DEBUG)) + printk ("EXT2-fs: ext2_resize_fs: freeing %lu blocks\n", add); + ext2_free_blocks (&inode, o_blocks_count, add); + sb->dq_op = dq_op_save; + + /* + * Update superblock with remaining group block/inode counts + */ + for (i = o_groups_count; i < sb->u.ext2_sb.s_groups_count; i++) + ext2_update_group (sb, i, reserved); + if (es->s_blocks_count != n_blocks_count) + ext2_warning (sb, "ext2_resize_fs", + "new block count does not match specified size" + "(%lu != %lu)", o_blocks_count, n_blocks_count); +} + #define log2(n) ffz(~(n)) struct super_block * ext2_read_super (struct super_block * sb, void * data, @@ -368,17 +551,17 @@ struct buffer_head * bh; struct ext2_super_block * es; unsigned long sb_block = 1; + unsigned long n_blocks_count = 0; unsigned short resuid = EXT2_DEF_RESUID; unsigned short resgid = EXT2_DEF_RESGID; unsigned long logic_sb_block = 1; kdev_t dev = sb->s_dev; - int db_count; - int i, j; + int i; sb->u.ext2_sb.s_mount_opt = 0; set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL); if (!parse_options ((char *) data, &sb_block, &resuid, &resgid, - &sb->u.ext2_sb.s_mount_opt)) { + &sb->u.ext2_sb.s_mount_opt, &n_blocks_count)) { sb->s_dev = 0; return NULL; } @@ -451,6 +634,9 @@ goto failed_mount; } } + if (test_opt (sb, DEBUG)) + printk ("EXT2-fs: logic_sb_block=%lu (%lub), b_blocknr=%lu\n", + logic_sb_block, sb->s_blocksize, bh->b_blocknr); if (es->s_rev_level == EXT2_GOOD_OLD_REV) { sb->u.ext2_sb.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; sb->u.ext2_sb.s_first_ino = EXT2_GOOD_OLD_FIRST_INO; @@ -531,35 +717,12 @@ goto failed_mount; } - sb->u.ext2_sb.s_groups_count = (es->s_blocks_count - - es->s_first_data_block + - EXT2_BLOCKS_PER_GROUP(sb) - 1) / - EXT2_BLOCKS_PER_GROUP(sb); - db_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / - EXT2_DESC_PER_BLOCK(sb); - sb->u.ext2_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL); - if (sb->u.ext2_sb.s_group_desc == NULL) { - printk ("EXT2-fs: not enough memory\n"); - goto failed_mount; - } - for (i = 0; i < db_count; i++) { - sb->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1, - sb->s_blocksize); - if (!sb->u.ext2_sb.s_group_desc[i]) { - for (j = 0; j < i; j++) - brelse (sb->u.ext2_sb.s_group_desc[j]); - kfree_s (sb->u.ext2_sb.s_group_desc, - db_count * sizeof (struct buffer_head *)); - printk ("EXT2-fs: unable to read group descriptors\n"); - goto failed_mount; - } - } - if (!ext2_check_descriptors (sb)) { - for (j = 0; j < db_count; j++) - brelse (sb->u.ext2_sb.s_group_desc[j]); - kfree_s (sb->u.ext2_sb.s_group_desc, - db_count * sizeof (struct buffer_head *)); - printk ("EXT2-fs: group descriptors corrupted !\n"); + sb->u.ext2_sb.s_db_per_group = 0; + sb->u.ext2_sb.s_groups_count = 0; + sb->u.ext2_sb.s_group_desc = NULL; + if (!ext2_read_descriptors (sb, logic_sb_block, es->s_blocks_count)) { + ext2_error (sb, "ext2_read_super", + "group descriptor error, unable to mount"); goto failed_mount; } for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) { @@ -570,25 +733,26 @@ } sb->u.ext2_sb.s_loaded_inode_bitmaps = 0; sb->u.ext2_sb.s_loaded_block_bitmaps = 0; - sb->u.ext2_sb.s_db_per_group = db_count; unlock_super (sb); /* * set up enough so that it can read an inode */ - sb->s_dev = dev; sb->s_op = &ext2_sops; if (!(sb->s_mounted = iget (sb, EXT2_ROOT_INO))) { sb->s_dev = 0; - for (i = 0; i < db_count; i++) + for (i = 0; i < sb->u.ext2_sb.s_db_per_group; i++) if (sb->u.ext2_sb.s_group_desc[i]) brelse (sb->u.ext2_sb.s_group_desc[i]); kfree_s (sb->u.ext2_sb.s_group_desc, - db_count * sizeof (struct buffer_head *)); + sb->u.ext2_sb.s_db_per_group * + sizeof (struct buffer_head *)); brelse (bh); printk ("EXT2-fs: get root inode failed\n"); MOD_DEC_USE_COUNT; return NULL; } + if (n_blocks_count != 0) + ext2_resize_fs (sb, es, n_blocks_count); ext2_setup_super (sb, es); return sb; } @@ -635,6 +799,7 @@ struct ext2_super_block * es; unsigned short resuid = sb->u.ext2_sb.s_resuid; unsigned short resgid = sb->u.ext2_sb.s_resgid; + unsigned long n_blocks_count = 0; unsigned long new_mount_opt; unsigned long tmp; @@ -643,14 +808,15 @@ */ new_mount_opt = EXT2_MOUNT_CHECK_NORMAL; if (!parse_options (data, &tmp, &resuid, &resgid, - &new_mount_opt)) + &new_mount_opt, &n_blocks_count)) return -EINVAL; sb->u.ext2_sb.s_mount_opt = new_mount_opt; sb->u.ext2_sb.s_resuid = resuid; sb->u.ext2_sb.s_resgid = resgid; es = sb->u.ext2_sb.s_es; - if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) + if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY) && + n_blocks_count == 0) return 0; if (*flags & MS_RDONLY) { if (es->s_state & EXT2_VALID_FS || @@ -674,6 +840,10 @@ */ sb->u.ext2_sb.s_mount_state = es->s_state; sb->s_flags &= ~MS_RDONLY; + if (n_blocks_count != 0) { + ext2_resize_fs (sb, es, n_blocks_count); + sb->u.ext2_sb.s_mount_state |= EXT2_VALID_FS; + } ext2_setup_super (sb, es); } return 0; @@ -729,7 +899,8 @@ tmp.f_type = EXT2_SUPER_MAGIC; tmp.f_bsize = sb->s_blocksize; tmp.f_blocks = sb->u.ext2_sb.s_es->s_blocks_count - overhead; - tmp.f_bfree = ext2_count_free_blocks (sb); + tmp.f_bfree = test_opt (sb, DEBUG) ? ext2_count_free_blocks (sb) : + sb->u.ext2_sb.s_es->s_free_blocks_count; tmp.f_bavail = tmp.f_bfree - sb->u.ext2_sb.s_es->s_r_blocks_count; if (tmp.f_bfree < sb->u.ext2_sb.s_es->s_r_blocks_count) tmp.f_bavail = 0; diff -ru linux-2.0.37.orig/fs/ext2/balloc.c linux/fs/ext2/balloc.c --- linux-2.0.37.orig/fs/ext2/balloc.c Thu Jul 22 16:35:44 1999 +++ linux/fs/ext2/balloc.c Sun Jul 25 22:42:36 1999 @@ -243,6 +243,37 @@ return slot; } +void ext2_update_group (struct super_block * sb, unsigned int block_group, + unsigned int reserved) +{ + struct ext2_group_desc * gdp; + struct ext2_super_block * es; + unsigned int blocks; + unsigned int inodes; + + lock_super(sb); + es = sb->u.ext2_sb.s_es; + gdp = get_group_desc (sb, block_group, NULL); + + blocks = gdp->bg_free_blocks_count; + es->s_free_blocks_count += blocks; + es->s_blocks_count += blocks; + es->s_r_blocks_count += blocks * reserved >> 8; + + inodes = gdp->bg_free_inodes_count; + es->s_free_inodes_count += inodes; + es->s_inodes_count += inodes; + + if (test_opt (sb, DEBUG)) + printk ("EXT2-fs: ext2_update_group: group %u: " + "%u blocks, %u inodes\n", + block_group, blocks, inodes); + + mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + sb->s_dirt = 1; + unlock_super(sb); +} + void ext2_free_blocks (const struct inode * inode, unsigned long block, unsigned long count) { @@ -602,13 +633,13 @@ unsigned long ext2_count_free_blocks (struct super_block * sb) { -#ifdef EXT2FS_DEBUG struct ext2_super_block * es; unsigned long desc_count, bitmap_count, x; int bitmap_nr; struct ext2_group_desc * gdp; int i; + if (test_opt(sb, DEBUG) && test_opt(sb, CHECK_STRICT)) { lock_super (sb); es = sb->u.ext2_sb.s_es; desc_count = 0; @@ -632,9 +665,9 @@ es->s_free_blocks_count, desc_count, bitmap_count); unlock_super (sb); return bitmap_count; -#else + } + else return sb->u.ext2_sb.s_es->s_free_blocks_count; -#endif } static inline int block_in_use (unsigned long block, diff -ru linux-2.0.37.orig/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h --- linux-2.0.37.orig/include/linux/ext2_fs.h Wed Nov 12 21:45:40 1997 +++ linux/include/linux/ext2_fs.h Sat Jul 24 18:05:16 1999 @@ -444,6 +444,8 @@ __u32 *, __u32 *, int *); extern void ext2_free_blocks (const struct inode *, unsigned long, unsigned long); +extern void ext2_update_group (struct super_block *, unsigned int, + unsigned int); extern unsigned long ext2_count_free_blocks (struct super_block *); extern void ext2_check_blocks_bitmap (struct super_block *);