* This could probably be made into a module, because it is not often in use.
*/
-#include <linux/config.h>
#define EXT3FS_DEBUG
{
struct ext3_sb_info *sbi = EXT3_SB(sb);
struct ext3_super_block *es = sbi->s_es;
- unsigned start = le32_to_cpu(es->s_blocks_count);
- unsigned end = start + input->blocks_count;
+ ext3_fsblk_t start = le32_to_cpu(es->s_blocks_count);
+ ext3_fsblk_t end = start + input->blocks_count;
unsigned group = input->group;
- unsigned itend = input->inode_table + EXT3_SB(sb)->s_itb_per_group;
+ ext3_fsblk_t itend = input->inode_table + sbi->s_itb_per_group;
unsigned overhead = ext3_bg_has_super(sb, group) ?
(1 + ext3_bg_num_gdb(sb, group) +
le16_to_cpu(es->s_reserved_gdt_blocks)) : 0;
- unsigned metaend = start + overhead;
- struct buffer_head *bh;
- int free_blocks_count;
+ ext3_fsblk_t metaend = start + overhead;
+ struct buffer_head *bh = NULL;
+ ext3_grpblk_t free_blocks_count;
int err = -EINVAL;
input->free_blocks_count = free_blocks_count =
input->blocks_count - 2 - overhead - sbi->s_itb_per_group;
if (test_opt(sb, DEBUG))
- printk("EXT3-fs: adding %s group %u: %u blocks "
+ printk(KERN_DEBUG "EXT3-fs: adding %s group %u: %u blocks "
"(%d free, %u reserved)\n",
ext3_bg_has_super(sb, input->group) ? "normal" :
"no-super", input->group, input->blocks_count,
ext3_warning(sb, __FUNCTION__, "Bad blocks count %u",
input->blocks_count);
else if (!(bh = sb_bread(sb, end - 1)))
- ext3_warning(sb, __FUNCTION__, "Cannot read last block (%u)",
+ ext3_warning(sb, __FUNCTION__,
+ "Cannot read last block ("E3FSBLK")",
end - 1);
else if (outside(input->block_bitmap, start, end))
ext3_warning(sb, __FUNCTION__,
else if (outside(input->inode_table, start, end) ||
outside(itend - 1, start, end))
ext3_warning(sb, __FUNCTION__,
- "Inode table not in group (blocks %u-%u)",
+ "Inode table not in group (blocks %u-"E3FSBLK")",
input->inode_table, itend - 1);
else if (input->inode_bitmap == input->block_bitmap)
ext3_warning(sb, __FUNCTION__,
input->block_bitmap);
else if (inside(input->block_bitmap, input->inode_table, itend))
ext3_warning(sb, __FUNCTION__,
- "Block bitmap (%u) in inode table (%u-%u)",
+ "Block bitmap (%u) in inode table (%u-"E3FSBLK")",
input->block_bitmap, input->inode_table, itend-1);
else if (inside(input->inode_bitmap, input->inode_table, itend))
ext3_warning(sb, __FUNCTION__,
- "Inode bitmap (%u) in inode table (%u-%u)",
+ "Inode bitmap (%u) in inode table (%u-"E3FSBLK")",
input->inode_bitmap, input->inode_table, itend-1);
else if (inside(input->block_bitmap, start, metaend))
ext3_warning(sb, __FUNCTION__,
- "Block bitmap (%u) in GDT table (%u-%u)",
+ "Block bitmap (%u) in GDT table"
+ " ("E3FSBLK"-"E3FSBLK")",
input->block_bitmap, start, metaend - 1);
else if (inside(input->inode_bitmap, start, metaend))
ext3_warning(sb, __FUNCTION__,
- "Inode bitmap (%u) in GDT table (%u-%u)",
+ "Inode bitmap (%u) in GDT table"
+ " ("E3FSBLK"-"E3FSBLK")",
input->inode_bitmap, start, metaend - 1);
else if (inside(input->inode_table, start, metaend) ||
inside(itend - 1, start, metaend))
ext3_warning(sb, __FUNCTION__,
- "Inode table (%u-%u) overlaps GDT table (%u-%u)",
+ "Inode table (%u-"E3FSBLK") overlaps"
+ "GDT table ("E3FSBLK"-"E3FSBLK")",
input->inode_table, itend - 1, start, metaend - 1);
- else {
- brelse(bh);
+ else
err = 0;
- }
+ brelse(bh);
return err;
}
static struct buffer_head *bclean(handle_t *handle, struct super_block *sb,
- unsigned long blk)
+ ext3_fsblk_t blk)
{
struct buffer_head *bh;
int err;
bh = sb_getblk(sb, blk);
- set_buffer_uptodate(bh);
+ if (!bh)
+ return ERR_PTR(-EIO);
if ((err = ext3_journal_get_write_access(handle, bh))) {
brelse(bh);
bh = ERR_PTR(err);
- } else
+ } else {
+ lock_buffer(bh);
memset(bh->b_data, 0, sb->s_blocksize);
+ set_buffer_uptodate(bh);
+ unlock_buffer(bh);
+ }
return bh;
}
* changing blocks outside the actual filesystem. We still do journaling to
* ensure the recovery is correct in case of a failure just after resize.
* If any part of this fails, we simply abort the resize.
- *
- * We only pass inode because of the ext3 journal wrappers.
*/
-static int setup_new_group_blocks(struct super_block *sb, struct inode *inode,
+static int setup_new_group_blocks(struct super_block *sb,
struct ext3_new_group_data *input)
{
struct ext3_sb_info *sbi = EXT3_SB(sb);
- unsigned long start = input->group * sbi->s_blocks_per_group +
- le32_to_cpu(sbi->s_es->s_first_data_block);
+ ext3_fsblk_t start = ext3_group_first_block_no(sb, input->group);
int reserved_gdb = ext3_bg_has_super(sb, input->group) ?
le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) : 0;
unsigned long gdblocks = ext3_bg_num_gdb(sb, input->group);
struct buffer_head *bh;
handle_t *handle;
- unsigned long block;
- int bit;
+ ext3_fsblk_t block;
+ ext3_grpblk_t bit;
int i;
int err = 0, err2;
- handle = ext3_journal_start(inode, reserved_gdb + gdblocks +
- 2 + sbi->s_itb_per_group);
+ handle = ext3_journal_start_sb(sb, reserved_gdb + gdblocks +
+ 2 + sbi->s_itb_per_group);
if (IS_ERR(handle))
return PTR_ERR(handle);
ext3_debug("update backup group %#04lx (+%d)\n", block, bit);
gdb = sb_getblk(sb, block);
- set_buffer_uptodate(gdb);
+ if (!gdb) {
+ err = -EIO;
+ goto exit_bh;
+ }
if ((err = ext3_journal_get_write_access(handle, gdb))) {
brelse(gdb);
goto exit_bh;
}
- memcpy(gdb->b_data, sbi->s_group_desc[i], bh->b_size);
+ lock_buffer(bh);
+ memcpy(gdb->b_data, sbi->s_group_desc[i]->b_data, bh->b_size);
+ set_buffer_uptodate(gdb);
+ unlock_buffer(bh);
ext3_journal_dirty_metadata(handle, gdb);
ext3_set_bit(bit, bh->b_data);
brelse(gdb);
i < sbi->s_itb_per_group; i++, bit++, block++) {
struct buffer_head *it;
- ext3_debug("clear inode block %#04x (+%ld)\n", block, bit);
+ ext3_debug("clear inode block %#04lx (+%d)\n", block, bit);
if (IS_ERR(it = bclean(handle, sb, block))) {
err = PTR_ERR(it);
goto exit_bh;
* 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 ext3_list_backups(struct super_block *sb, unsigned *three,
- unsigned *five, unsigned *seven)
+static unsigned ext3_list_backups(struct super_block *sb, unsigned *three,
+ unsigned *five, unsigned *seven)
{
unsigned *min = three;
int mult = 3;
static int verify_reserved_gdb(struct super_block *sb,
struct buffer_head *primary)
{
- const unsigned long blk = primary->b_blocknr;
+ const ext3_fsblk_t blk = primary->b_blocknr;
const unsigned long end = EXT3_SB(sb)->s_groups_count;
unsigned three = 1;
unsigned five = 5;
unsigned seven = 7;
unsigned grp;
- __u32 *p = (__u32 *)primary->b_data;
+ __le32 *p = (__le32 *)primary->b_data;
int gdbackups = 0;
while ((grp = ext3_list_backups(sb, &three, &five, &seven)) < end) {
if (le32_to_cpu(*p++) != grp * EXT3_BLOCKS_PER_GROUP(sb) + blk){
ext3_warning(sb, __FUNCTION__,
- "reserved GDT %ld missing grp %d (%ld)\n",
+ "reserved GDT "E3FSBLK
+ " missing grp %d ("E3FSBLK")",
blk, grp,
grp * EXT3_BLOCKS_PER_GROUP(sb) + blk);
return -EINVAL;
struct super_block *sb = inode->i_sb;
struct ext3_super_block *es = EXT3_SB(sb)->s_es;
unsigned long gdb_num = input->group / EXT3_DESC_PER_BLOCK(sb);
- unsigned long gdb_off = input->group % EXT3_DESC_PER_BLOCK(sb);
- unsigned long gdblock = EXT3_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num;
+ ext3_fsblk_t gdblock = EXT3_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num;
struct buffer_head **o_group_desc, **n_group_desc;
struct buffer_head *dind;
int gdbackups;
struct ext3_iloc iloc;
- __u32 *data;
+ __le32 *data;
int err;
if (test_opt(sb, DEBUG))
- printk("EXT3-fs: ext3_add_new_gdb: adding group block %lu\n",
+ printk(KERN_DEBUG
+ "EXT3-fs: ext3_add_new_gdb: adding group block %lu\n",
gdb_num);
/*
if (EXT3_SB(sb)->s_sbh->b_blocknr !=
le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block)) {
ext3_warning(sb, __FUNCTION__,
- "won't resize using backup superblock at %lu\n",
- EXT3_SB(sb)->s_sbh->b_blocknr);
+ "won't resize using backup superblock at %llu",
+ (unsigned long long)EXT3_SB(sb)->s_sbh->b_blocknr);
return -EPERM;
}
goto exit_bh;
}
- data = (__u32 *)dind->b_data;
+ data = (__le32 *)dind->b_data;
if (le32_to_cpu(data[gdb_num % EXT3_ADDR_PER_BLOCK(sb)]) != gdblock) {
ext3_warning(sb, __FUNCTION__,
- "new group %u GDT block %lu not reserved\n",
+ "new group %u GDT block "E3FSBLK" not reserved",
input->group, gdblock);
err = -EINVAL;
goto exit_dind;
if ((err = ext3_reserve_inode_write(handle, inode, &iloc)))
goto exit_dindj;
- n_group_desc = (struct buffer_head **)kmalloc((gdb_num + 1) *
- sizeof(struct buffer_head *), GFP_KERNEL);
+ n_group_desc = kmalloc((gdb_num + 1) * sizeof(struct buffer_head *),
+ GFP_KERNEL);
if (!n_group_desc) {
err = -ENOMEM;
ext3_warning (sb, __FUNCTION__,
* these blocks, because they are marked as in-use from being in the
* reserved inode, and will become GDT blocks (primary and backup).
*/
- /*
- printk("removing block %d = %ld from dindir %ld[%ld]\n",
- ((__u32 *)(dind->b_data))[gdb_off], gdblock, dind->b_blocknr,
- gdb_num); */
data[gdb_num % EXT3_ADDR_PER_BLOCK(sb)] = 0;
ext3_journal_dirty_metadata(handle, dind);
brelse(dind);
struct buffer_head **primary;
struct buffer_head *dind;
struct ext3_iloc iloc;
- unsigned long blk;
- __u32 *data, *end;
+ ext3_fsblk_t blk;
+ __le32 *data, *end;
int gdbackups = 0;
int res, i;
int err;
}
blk = EXT3_SB(sb)->s_sbh->b_blocknr + 1 + EXT3_SB(sb)->s_gdb_count;
- data = (__u32 *)dind->b_data + EXT3_SB(sb)->s_gdb_count;
- end = (__u32 *)dind->b_data + EXT3_ADDR_PER_BLOCK(sb);
+ data = (__le32 *)dind->b_data + EXT3_SB(sb)->s_gdb_count;
+ end = (__le32 *)dind->b_data + EXT3_ADDR_PER_BLOCK(sb);
/* Get each reserved primary GDT block and verify it holds backups */
for (res = 0; res < reserved_gdb; res++, blk++) {
if (le32_to_cpu(*data) != blk) {
ext3_warning(sb, __FUNCTION__,
- "reserved block %lu not at offset %ld\n",
- blk, (long)(data - (__u32 *)dind->b_data));
+ "reserved block "E3FSBLK
+ " not at offset %ld",
+ blk,
+ (long)(data - (__le32 *)dind->b_data));
err = -EINVAL;
goto exit_bh;
}
goto exit_bh;
}
if (++data >= end)
- data = (__u32 *)dind->b_data;
+ data = (__le32 *)dind->b_data;
}
for (i = 0; i < reserved_gdb; i++) {
blk = input->group * EXT3_BLOCKS_PER_GROUP(sb);
for (i = 0; i < reserved_gdb; i++) {
int err2;
- data = (__u32 *)primary[i]->b_data;
+ data = (__le32 *)primary[i]->b_data;
/* printk("reserving backup %lu[%u] = %lu\n",
primary[i]->b_blocknr, gdbackups,
blk + primary[i]->b_blocknr); */
* need to worry about last changing from sbi->s_groups_count, because the
* worst that can happen is that we do not copy the full number of backups
* at this time. The resize which changed s_groups_count will backup again.
- *
- * We only pass inode because of the ext3 journal wrappers.
*/
-static void update_backups(struct super_block *sb, struct inode *inode,
+static void update_backups(struct super_block *sb,
int blk_off, char *data, int size)
{
struct ext3_sb_info *sbi = EXT3_SB(sb);
handle_t *handle;
int err = 0, err2;
- handle = ext3_journal_start(inode, EXT3_MAX_TRANS_DATA);
+ handle = ext3_journal_start_sb(sb, EXT3_MAX_TRANS_DATA);
if (IS_ERR(handle)) {
group = 1;
err = PTR_ERR(handle);
break;
bh = sb_getblk(sb, group * bpg + blk_off);
- set_buffer_uptodate(bh);
- ext3_debug(sb, __FUNCTION__, "update metadata backup %#04lx\n",
- bh->b_blocknr);
+ if (!bh) {
+ err = -EIO;
+ break;
+ }
+ ext3_debug("update metadata backup %#04lx\n",
+ (unsigned long)bh->b_blocknr);
if ((err = ext3_journal_get_write_access(handle, bh)))
break;
+ lock_buffer(bh);
memcpy(bh->b_data, data, size);
if (rest)
memset(bh->b_data + size, 0, rest);
+ set_buffer_uptodate(bh);
+ unlock_buffer(bh);
ext3_journal_dirty_metadata(handle, bh);
brelse(bh);
}
if (err) {
ext3_warning(sb, __FUNCTION__,
"can't update backup for group %d (err %d), "
- "forcing fsck on next reboot\n", group, err);
+ "forcing fsck on next reboot", group, err);
sbi->s_mount_state &= ~EXT3_VALID_FS;
- sbi->s_es->s_state &= ~cpu_to_le16(EXT3_VALID_FS);
+ sbi->s_es->s_state &= cpu_to_le16(~EXT3_VALID_FS);
mark_buffer_dirty(sbi->s_sbh);
}
}
struct buffer_head *primary = NULL;
struct ext3_group_desc *gdp;
struct inode *inode = NULL;
- struct inode bogus;
handle_t *handle;
int gdb_off, gdb_num;
int err, err2;
if (gdb_off == 0 && !EXT3_HAS_RO_COMPAT_FEATURE(sb,
EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
ext3_warning(sb, __FUNCTION__,
- "Can't resize non-sparse filesystem further\n");
+ "Can't resize non-sparse filesystem further");
return -EPERM;
}
+ if (le32_to_cpu(es->s_blocks_count) + input->blocks_count <
+ le32_to_cpu(es->s_blocks_count)) {
+ ext3_warning(sb, __FUNCTION__, "blocks_count overflow\n");
+ return -EINVAL;
+ }
+
+ if (le32_to_cpu(es->s_inodes_count) + EXT3_INODES_PER_GROUP(sb) <
+ le32_to_cpu(es->s_inodes_count)) {
+ ext3_warning(sb, __FUNCTION__, "inodes_count overflow\n");
+ return -EINVAL;
+ }
+
if (reserved_gdb || gdb_off == 0) {
if (!EXT3_HAS_COMPAT_FEATURE(sb,
EXT3_FEATURE_COMPAT_RESIZE_INODE)){
ext3_warning(sb, __FUNCTION__,
- "No reserved GDT blocks, can't resize\n");
+ "No reserved GDT blocks, can't resize");
return -EPERM;
}
inode = iget(sb, EXT3_RESIZE_INO);
if (!inode || is_bad_inode(inode)) {
ext3_warning(sb, __FUNCTION__,
- "Error opening resize inode\n");
+ "Error opening resize inode");
iput(inode);
return -ENOENT;
}
- } else {
- /* Used only for ext3 journal wrapper functions to get sb */
- inode = &bogus;
- bogus.i_sb = sb;
}
if ((err = verify_group_input(sb, input)))
goto exit_put;
- if ((err = setup_new_group_blocks(sb, inode, input)))
+ if ((err = setup_new_group_blocks(sb, input)))
goto exit_put;
/*
* are adding a group with superblock/GDT backups we will also
* modify each of the reserved GDT dindirect blocks.
*/
- handle = ext3_journal_start(inode, ext3_bg_has_super(sb, input->group) ?
- 3 + reserved_gdb : 4);
+ handle = ext3_journal_start_sb(sb,
+ ext3_bg_has_super(sb, input->group) ?
+ 3 + reserved_gdb : 4);
if (IS_ERR(handle)) {
err = PTR_ERR(handle);
goto exit_put;
}
lock_super(sb);
- if (input->group != EXT3_SB(sb)->s_groups_count) {
+ if (input->group != sbi->s_groups_count) {
ext3_warning(sb, __FUNCTION__,
- "multiple resizers run on filesystem!\n");
+ "multiple resizers run on filesystem!");
+ err = -EBUSY;
goto exit_journal;
}
} else if ((err = add_new_gdb(handle, inode, input, &primary)))
goto exit_journal;
- /* Finally update group descriptor block for new group */
+ /*
+ * OK, now we've set up the new group. Time to make it active.
+ *
+ * Current kernels don't lock all allocations via lock_super(),
+ * so we have to be safe wrt. concurrent accesses the group
+ * data. So we need to be careful to set all of the relevant
+ * group descriptor data etc. *before* we enable the group.
+ *
+ * The key field here is sbi->s_groups_count: as long as
+ * that retains its old value, nobody is going to access the new
+ * group.
+ *
+ * So first we update all the descriptor metadata for the new
+ * group; then we update the total disk blocks count; then we
+ * update the groups count to enable the group; then finally we
+ * update the free space counts so that the system can start
+ * using the new disk blocks.
+ */
+
+ /* Update group descriptor block for new group */
gdp = (struct ext3_group_desc *)primary->b_data + gdb_off;
gdp->bg_block_bitmap = cpu_to_le32(input->block_bitmap);
gdp->bg_free_blocks_count = cpu_to_le16(input->free_blocks_count);
gdp->bg_free_inodes_count = cpu_to_le16(EXT3_INODES_PER_GROUP(sb));
- EXT3_SB(sb)->s_groups_count++;
- ext3_journal_dirty_metadata(handle, primary);
-
- /* Update superblock with new block counts */
+ /*
+ * Make the new blocks and inodes valid next. We do this before
+ * increasing the group count so that once the group is enabled,
+ * all of its blocks and inodes are already valid.
+ *
+ * We always allocate group-by-group, then block-by-block or
+ * inode-by-inode within a group, so enabling these
+ * blocks/inodes before the group is live won't actually let us
+ * allocate the new space yet.
+ */
es->s_blocks_count = cpu_to_le32(le32_to_cpu(es->s_blocks_count) +
input->blocks_count);
- es->s_free_blocks_count =
- cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) +
- input->free_blocks_count);
- es->s_r_blocks_count = cpu_to_le32(le32_to_cpu(es->s_r_blocks_count) +
- input->reserved_blocks);
es->s_inodes_count = cpu_to_le32(le32_to_cpu(es->s_inodes_count) +
EXT3_INODES_PER_GROUP(sb));
- es->s_free_inodes_count =
- cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) +
- EXT3_INODES_PER_GROUP(sb));
- ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh);
+
+ /*
+ * We need to protect s_groups_count against other CPUs seeing
+ * inconsistent state in the superblock.
+ *
+ * The precise rules we use are:
+ *
+ * * Writers of s_groups_count *must* hold lock_super
+ * AND
+ * * Writers must perform a smp_wmb() after updating all dependent
+ * data and before modifying the groups count
+ *
+ * * Readers must hold lock_super() over the access
+ * OR
+ * * Readers must perform an smp_rmb() after reading the groups count
+ * and before reading any dependent data.
+ *
+ * NB. These rules can be relaxed when checking the group count
+ * while freeing data, as we can only allocate from a block
+ * group after serialising against the group count, and we can
+ * only then free after serialising in turn against that
+ * allocation.
+ */
+ smp_wmb();
+
+ /* Update the global fs size fields */
+ sbi->s_groups_count++;
+
+ ext3_journal_dirty_metadata(handle, primary);
+
+ /* Update the reserved block counts only once the new group is
+ * active. */
+ es->s_r_blocks_count = cpu_to_le32(le32_to_cpu(es->s_r_blocks_count) +
+ input->reserved_blocks);
+
+ /* Update the free space counts */
+ percpu_counter_mod(&sbi->s_freeblocks_counter,
+ input->free_blocks_count);
+ percpu_counter_mod(&sbi->s_freeinodes_counter,
+ EXT3_INODES_PER_GROUP(sb));
+
+ ext3_journal_dirty_metadata(handle, sbi->s_sbh);
sb->s_dirt = 1;
exit_journal:
unlock_super(sb);
- handle->h_sync = 1;
if ((err2 = ext3_journal_stop(handle)) && !err)
err = err2;
if (!err) {
- update_backups(sb, inode, sbi->s_sbh->b_blocknr, (char *)es,
+ update_backups(sb, sbi->s_sbh->b_blocknr, (char *)es,
sizeof(struct ext3_super_block));
- update_backups(sb, inode, primary->b_blocknr, primary->b_data,
+ update_backups(sb, primary->b_blocknr, primary->b_data,
primary->b_size);
}
exit_put:
- if (inode != &bogus)
- iput(inode);
+ iput(inode);
return err;
} /* ext3_group_add */
* GDT blocks are reserved to grow to the desired size.
*/
int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es,
- unsigned long n_blocks_count)
+ ext3_fsblk_t n_blocks_count)
{
- unsigned long o_blocks_count;
+ ext3_fsblk_t o_blocks_count;
unsigned long o_groups_count;
- unsigned long last;
- int add;
- struct inode *inode;
+ ext3_grpblk_t last;
+ ext3_grpblk_t add;
struct buffer_head * bh;
handle_t *handle;
int err;
+ unsigned long freed_blocks;
+ /* We don't need to worry about locking wrt other resizers just
+ * yet: we're going to revalidate es->s_blocks_count after
+ * taking lock_super() below. */
o_blocks_count = le32_to_cpu(es->s_blocks_count);
o_groups_count = EXT3_SB(sb)->s_groups_count;
if (test_opt(sb, DEBUG))
- printk("EXT3-fs: extending last group from %lu to %lu blocks\n",
+ printk(KERN_DEBUG "EXT3-fs: extending last group from "E3FSBLK" uto "E3FSBLK" blocks\n",
o_blocks_count, n_blocks_count);
if (n_blocks_count == 0 || n_blocks_count == o_blocks_count)
return 0;
+ if (n_blocks_count > (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) {
+ printk(KERN_ERR "EXT3-fs: filesystem on %s:"
+ " too large to resize to %lu blocks safely\n",
+ sb->s_id, n_blocks_count);
+ if (sizeof(sector_t) < 8)
+ ext3_warning(sb, __FUNCTION__,
+ "CONFIG_LBD not enabled\n");
+ return -EINVAL;
+ }
+
if (n_blocks_count < o_blocks_count) {
ext3_warning(sb, __FUNCTION__,
"can't shrink FS - resize aborted");
if (last == 0) {
ext3_warning(sb, __FUNCTION__,
- "need to use ext2online to resize further\n");
+ "need to use ext2online to resize further");
return -EPERM;
}
add = EXT3_BLOCKS_PER_GROUP(sb) - last;
+ if (o_blocks_count + add < o_blocks_count) {
+ ext3_warning(sb, __FUNCTION__, "blocks_count overflow");
+ return -EINVAL;
+ }
+
if (o_blocks_count + add > n_blocks_count)
add = n_blocks_count - o_blocks_count;
if (o_blocks_count + add < n_blocks_count)
ext3_warning(sb, __FUNCTION__,
- "will only finish group (%lu blocks, %u new)",
+ "will only finish group ("E3FSBLK
+ " blocks, %u new)",
o_blocks_count + add, add);
/* See if the device is actually as big as what was requested */
}
brelse(bh);
- /* Get a bogus inode to "free" the new blocks in this group. */
- if (!(inode = new_inode(sb))) {
- ext3_warning(sb, __FUNCTION__,
- "error getting dummy resize inode");
- return -ENOMEM;
- }
- inode->i_ino = 0;
-
- EXT3_I(inode)->i_state = EXT3_STATE_RESIZE;
-
/* We will update the superblock, one block bitmap, and
* one group descriptor via ext3_free_blocks().
*/
- handle = ext3_journal_start(inode, 3);
+ handle = ext3_journal_start_sb(sb, 3);
if (IS_ERR(handle)) {
err = PTR_ERR(handle);
ext3_warning(sb, __FUNCTION__, "error %d on journal start",err);
lock_super(sb);
if (o_blocks_count != le32_to_cpu(es->s_blocks_count)) {
ext3_warning(sb, __FUNCTION__,
- "multiple resizers run on filesystem!\n");
+ "multiple resizers run on filesystem!");
+ unlock_super(sb);
err = -EBUSY;
goto exit_put;
}
ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh);
sb->s_dirt = 1;
unlock_super(sb);
- ext3_debug("freeing blocks %ld through %ld\n", o_blocks_count,
+ ext3_debug("freeing blocks %lu through "E3FSBLK"\n", o_blocks_count,
o_blocks_count + add);
- ext3_free_blocks(handle, inode, o_blocks_count, add);
- ext3_debug("freed blocks %ld through %ld\n", o_blocks_count,
+ ext3_free_blocks_sb(handle, sb, o_blocks_count, add, &freed_blocks);
+ ext3_debug("freed blocks "E3FSBLK" through "E3FSBLK"\n", o_blocks_count,
o_blocks_count + add);
if ((err = ext3_journal_stop(handle)))
goto exit_put;
if (test_opt(sb, DEBUG))
- printk("EXT3-fs: extended group to %u blocks\n",
+ printk(KERN_DEBUG "EXT3-fs: extended group to %u blocks\n",
le32_to_cpu(es->s_blocks_count));
- update_backups(sb, inode, EXT3_SB(sb)->s_sbh->b_blocknr, (char *)es,
+ update_backups(sb, EXT3_SB(sb)->s_sbh->b_blocknr, (char *)es,
sizeof(struct ext3_super_block));
exit_put:
- iput(inode);
-
return err;
} /* ext3_group_extend */