diff -ru -X linux-2.3.99p2-online/exclude.lst linux-2.3.99p2-ext2base/CREDITS linux-2.3.99p2-online/CREDITS --- linux-2.3.99p2-ext2base/CREDITS Sun Mar 19 23:19:04 2000 +++ linux-2.3.99p2-online/CREDITS Sun Mar 19 23:24:27 2000 @@ -559,6 +559,14 @@ S: Warrendale, Pennsylvania 15086 S: USA +N: Andreas Dilger +E: adilger@home.com +W: http://www-mddsp.enel.ucalgary.ca/People/adilger/ +D: Ext2 filesystem online resize capability +S: 630 Schooner Cove N.W. +S: Calgary, AB +S: Canada T3L 1Z1 + N: Alex deVries E: adevries@thepuffingroup.com D: Various SGI parts, bits of HAL2 and Newport, PA-RISC Linux. diff -ru -X linux-2.3.99p2-online/exclude.lst linux-2.3.99p2-ext2base/Documentation/Configure.help linux-2.3.99p2-online/Documentation/Configure.help --- linux-2.3.99p2-ext2base/Documentation/Configure.help Sun Mar 19 23:19:05 2000 +++ linux-2.3.99p2-online/Documentation/Configure.help Sun Mar 19 23:24:28 2000 @@ -9324,6 +9324,21 @@ compiled as a module, and so this could be dangerous. Most everyone wants to say Y here. +Online resize for ext2 filesystems +CONFIG_EXT2_RESIZE + This option gives you the ability to increase the size of an ext2 + filesystem while it is mounted (in use). In order to do this, you + must also be able to resize the underlying disk partition, probably + via a Logical Volume Manager (LVM), metadevice (MD), or hardware + RAID device - none of that capability is included in this feature. + If you don't know what any of these things are, or you haven't + configured your kernel for them, you should probably say N here. + If you choose Y, then your kernel will be about 3k larger, and you + will need to download some additional software + (http://www-mddsp.enel.ucalgary.ca/People/adilger/online-ext2/) in + order to actually resize your filesystem, otherwise this feature will + just sit unused inside the kernel. + BFS file system support (EXPERIMENTAL) CONFIG_BFS_FS Boot File System (BFS) is a file system used under SCO UnixWare to diff -ru -X linux-2.3.99p2-online/exclude.lst linux-2.3.99p2-ext2base/fs/Config.in linux-2.3.99p2-online/fs/Config.in --- linux-2.3.99p2/fs/Config.in Sun Mar 19 22:24:46 2000 +++ linux-2.3.99p2-online/fs/Config.in Sun Mar 26 21:11:20 2000 @@ -65,6 +65,9 @@ tristate 'ROM file system support' CONFIG_ROMFS_FS tristate 'Second extended fs support' CONFIG_EXT2_FS +if [ "$CONFIG_EXT2_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' Online ext2 resize support (DANGEROUS)' CONFIG_EXT2_RESIZE +fi tristate 'System V and Coherent file system support (read only)' CONFIG_SYSV_FS if [ "$CONFIG_SYSV_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then diff -ru -X linux-2.3.99p2-online/exclude.lst linux-2.3.99p2-ext2base/fs/ext2/super.c linux-2.3.99p2-online/fs/ext2/super.c --- linux-2.3.99p2-ext2base/fs/ext2/super.c Sun Mar 19 23:11:33 2000 +++ linux-2.3.99p2-online/fs/ext2/super.c Mon Mar 20 03:46:59 2000 @@ -14,6 +14,7 @@ * * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 + * Online resize by Andreas Dilger (adilger@home.com), July 1999 */ #include @@ -139,7 +140,9 @@ */ 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, + unsigned long * resgdt) { char * this_char; char * value; @@ -216,6 +219,31 @@ else if (!strcmp (this_char, "nogrpid") || !strcmp (this_char, "sysvgroups")) clear_opt (*mount_options, GRPID); +#ifdef CONFIG_EXT2_RESIZE + else if (!strcmp (this_char, "resize")) { + printk ("EXT2-fs: parse_options: resize=%s\n", value); + if (!n_blocks_count) { + printk("EXT2-fs: resize option only available " + "for remount\n"); + return 0; + } + if (!value || !*value) { + printk ("EXT2-fs: resize requires number of " + "blocks\n"); + return 0; + } + *n_blocks_count = simple_strtoul (value, &value, 0); + if (*value == ':') { + value++; + *resgdt = simple_strtoul (value, &value, 0); + } + if (*value) { + printk ("EXT2-fs: invalid resize option: %s\n", + value); + return 0; + } + } +#endif /* CONFIG_EXT2_RESIZE */ else if (!strcmp (this_char, "resgid")) { if (!value || !*value) { printk ("EXT2-fs: the resgid option requires " @@ -339,7 +367,7 @@ if (le32_to_cpu(gdp->bg_block_bitmap) < block || le32_to_cpu(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) le32_to_cpu(gdp->bg_block_bitmap)); @@ -348,7 +376,7 @@ if (le32_to_cpu(gdp->bg_inode_bitmap) < block || le32_to_cpu(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) le32_to_cpu(gdp->bg_inode_bitmap)); @@ -358,7 +386,7 @@ le32_to_cpu(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) le32_to_cpu(gdp->bg_inode_table)); @@ -460,6 +488,164 @@ return 0; } /* ext2_read_descriptors */ +#ifdef CONFIG_EXT2_RESIZE +static int ext2_update_group (struct super_block * sb, unsigned int block_group, + unsigned int reserved, unsigned int resgdt) +{ + struct ext2_group_desc * gdp; + struct ext2_super_block * es; + int blocks; + int m_blocks; + int inodes; + int shrink = reserved > 100 ? 1 : 0; + + lock_super (sb); + es = sb->u.ext2_sb.s_es; + gdp = ext2_get_group_desc (sb, block_group, NULL); + + inodes = le32_to_cpu(gdp->bg_free_inodes_count); + blocks = le32_to_cpu(gdp->bg_free_blocks_count); + m_blocks = ((inodes * EXT2_INODE_SIZE(sb)) + EXT2_BLOCK_SIZE(sb) - 1) / + EXT2_BLOCK_SIZE(sb) + 2 + (ext2_group_sparse(block_group) ? + 1 + sb->u.ext2_sb.s_db_per_group + resgdt : 0) + blocks; + + es->s_free_inodes_count = + cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + inodes); + es->s_inodes_count = cpu_to_le32(le32_to_cpu(es->s_inodes_count) + + inodes); + + es->s_free_blocks_count = + cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) + blocks); + es->s_blocks_count = cpu_to_le32(le32_to_cpu(es->s_blocks_count) + + m_blocks); + es->s_r_blocks_count = cpu_to_le32(le32_to_cpu(es->s_r_blocks_count) + + m_blocks * reserved / 100); + + mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + sb->s_dirt = 1; + unlock_super (sb); + + if (test_opt (sb, DEBUG)) { + printk ("EXT2-fs: ext2_update_group: %s group %u: " + "%u inodes, %u blocks (%u free), %d (%d%%) reserved\n", + shrink ? "removing" : "adding", block_group, inodes, + m_blocks, blocks, m_blocks * reserved / 100, reserved); + printk ("EXT2-fs: ext2_update_group: %u inodes (%u free), " + "%u blocks (%u free)\n", + le32_to_cpu(es->s_inodes_count), + le32_to_cpu(es->s_free_inodes_count), + le32_to_cpu(es->s_blocks_count), + le32_to_cpu(es->s_free_blocks_count)); + } + + return 0; +} /* ext2_update_group */ + +/* Resize the filesystem to the new number of blocks specified. If required, + * the new group descriptors should have already been configured for us (we + * check that they are valid). + */ +static int ext2_resize_fs (struct super_block * sb, + struct ext2_super_block * es, + unsigned long n_blocks_count, unsigned long resgdt) +{ + unsigned long o_blocks_count; + unsigned long o_groups_count; + unsigned long last, add; + unsigned long reserved; + struct buffer_head * bh; + struct inode inode; + unsigned long i; + int err = 0; + + o_blocks_count = le32_to_cpu(es->s_blocks_count); + o_groups_count = sb->u.ext2_sb.s_groups_count; + + if (test_opt (sb, DEBUG)) + printk ("EXT2-fs: ext2_resize_fs: from %lu to %lu blocks\n", + o_blocks_count, n_blocks_count); + + if (n_blocks_count == 0 || n_blocks_count == o_blocks_count) + return 0; + + /* Until we can shrink the FS, we need to check this */ + if (n_blocks_count < o_blocks_count) { + ext2_warning (sb, __FUNCTION__, "error: can't shrink FS!"); + return -EBUSY; + } + + /* See if the device is actually as big as what was requested */ + bh = bread (sb->s_dev, n_blocks_count - 1, EXT2_BLOCK_SIZE(sb)); + if (!bh) { + ext2_warning (sb, __FUNCTION__, + "unable to read last block, resize aborted"); + return -ENOSPC; + } + brelse (bh); + + /* For reserved percentage calculation, we avoid 32-bit overflow. */ + reserved = o_blocks_count > 10000000 ? + (es->s_r_blocks_count + o_blocks_count / 200) / (o_blocks_count / 100) : + (es->s_r_blocks_count * 100 + o_blocks_count / 2) / o_blocks_count; + + lock_super (sb); + if ((err = ext2_read_descriptors (sb, sb->u.ext2_sb.s_sbh->b_blocknr, + n_blocks_count))) { + ext2_warning (sb, __FUNCTION__, + "group descriptor error %d, resize aborted", err); + unlock_super (sb); + return err; + } + + /* Handle the remaining blocks in the last partial group. */ + last = (o_blocks_count - + le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block)) % + EXT2_BLOCKS_PER_GROUP(sb); + if (last != 0) { /* The last group isn't full yet */ + add = EXT2_BLOCKS_PER_GROUP(sb) - last; + if (add + o_blocks_count > n_blocks_count) + add = n_blocks_count - o_blocks_count; + es->s_blocks_count += add; + es->s_r_blocks_count += add * reserved / 100; + mark_buffer_dirty (sb->u.ext2_sb.s_sbh, 1); + sb->s_dirt = 1; + unlock_super (sb); + /* + * Fake out an inode enough to "free" the new blocks in this + * group. Turn off quotas for this inode so it doesn't get + * confused by freeing blocks that don't really exist yet. + */ + inode.i_sb = sb; + for (i = 0; i < MAXQUOTAS; i++) + inode.i_dquot[i] = NODQUOT; + ext2_free_blocks (&inode, o_blocks_count, add); + if (test_opt (sb, DEBUG)) + printk ("EXT2-fs: ext2_resize_fs: added %lu new blocks " + "to %lu current blocks\n", + add, o_blocks_count); + } else + unlock_super (sb); + + /* + * Update superblock with remaining new group block/inode counts + */ + for (i = o_groups_count; i < sb->u.ext2_sb.s_groups_count && + !(err = ext2_update_group (sb, i, reserved, resgdt)); i++) + /* empty loop */; + if (err || le32_to_cpu(es->s_blocks_count) != n_blocks_count) { + ext2_warning (sb, __FUNCTION__, + "specified size does not match new block count " + "(%lu != %u) (err %d)", n_blocks_count, + le32_to_cpu(es->s_blocks_count), err); + sb->u.ext2_sb.s_mount_state &= ~EXT2_VALID_FS; + } else + sb->u.ext2_sb.s_mount_state |= EXT2_VALID_FS; + + return err; +} /* ext2_resize_fs */ +#else /* !CONFIG_EXT2_RESIZE */ +#define ext2_resize_fs(sb, es, n_blocks_count, resgdt) 0 +#endif /* CONFIG_EXT2_RESIZE */ #define log2(n) ffz(~(n)) @@ -494,7 +680,7 @@ 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, NULL, NULL)) { return NULL; } @@ -740,6 +928,8 @@ 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 resgdt = 0; unsigned long new_mount_opt; unsigned long tmp; @@ -748,14 +936,15 @@ */ new_mount_opt = EXT2_MOUNT_CHECK_NORMAL; if (!parse_options (data, &tmp, &resuid, &resgid, - &new_mount_opt)) + &new_mount_opt, &n_blocks_count, &resgdt)) 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 (le16_to_cpu(es->s_state) & EXT2_VALID_FS || @@ -779,6 +968,8 @@ */ sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state); sb->s_flags &= ~MS_RDONLY; + if ((tmp = ext2_resize_fs (sb, es, n_blocks_count, resgdt))) + return tmp; ext2_setup_super (sb, es); } return 0; diff -ru -X linux-2.3.99p2-online/exclude.lst linux-2.3.99p2-ext2base/include/linux/ext2_fs.h linux-2.3.99p2-online/include/linux/ext2_fs.h --- linux-2.3.99p2-ext2base/include/linux/ext2_fs.h Sun Mar 19 23:10:30 2000 +++ linux-2.3.99p2-online/include/linux/ext2_fs.h Mon Mar 20 03:13:05 2000 @@ -61,6 +61,7 @@ #define EXT2_ACL_DATA_INO 4 /* ACL inode */ #define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ #define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ +#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ /* First non-reserved inode for old ext2 filesystems */ #define EXT2_GOOD_OLD_FIRST_INO 11