📄 resize.c
字号:
/* * linux/fs/ext3/resize.c * * Support for resizing an ext3 filesystem while it is mounted. * * Copyright (C) 2001, 2002 Andreas Dilger <adilger@clusterfs.com> * * This could probably be made into a module, because it is not often in use. */#define EXT3FS_DEBUG#include <linux/ext3_jbd.h>#include <linux/errno.h>#include <linux/slab.h>#define outside(b, first, last) ((b) < (first) || (b) >= (last))#define inside(b, first, last) ((b) >= (first) && (b) < (last))static int verify_group_input(struct super_block *sb, struct ext3_new_group_data *input){ struct ext3_sb_info *sbi = EXT3_SB(sb); struct ext3_super_block *es = sbi->s_es; ext3_fsblk_t start = le32_to_cpu(es->s_blocks_count); ext3_fsblk_t end = start + input->blocks_count; unsigned group = input->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; 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(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, free_blocks_count, input->reserved_blocks); if (group != sbi->s_groups_count) ext3_warning(sb, __FUNCTION__, "Cannot add at group %u (only %lu groups)", input->group, sbi->s_groups_count); else if ((start - le32_to_cpu(es->s_first_data_block)) % EXT3_BLOCKS_PER_GROUP(sb)) ext3_warning(sb, __FUNCTION__, "Last group not full"); else if (input->reserved_blocks > input->blocks_count / 5) ext3_warning(sb, __FUNCTION__, "Reserved blocks too high (%u)", input->reserved_blocks); else if (free_blocks_count < 0) 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 ("E3FSBLK")", end - 1); else if (outside(input->block_bitmap, start, end)) ext3_warning(sb, __FUNCTION__, "Block bitmap not in group (block %u)", input->block_bitmap); else if (outside(input->inode_bitmap, start, end)) ext3_warning(sb, __FUNCTION__, "Inode bitmap not in group (block %u)", input->inode_bitmap); else if (outside(input->inode_table, start, end) || outside(itend - 1, start, end)) ext3_warning(sb, __FUNCTION__, "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__, "Block bitmap same as inode bitmap (%u)", input->block_bitmap); else if (inside(input->block_bitmap, input->inode_table, itend)) ext3_warning(sb, __FUNCTION__, "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-"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" " ("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" " ("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-"E3FSBLK") overlaps" "GDT table ("E3FSBLK"-"E3FSBLK")", input->inode_table, itend - 1, start, metaend - 1); else err = 0; brelse(bh); return err;}static struct buffer_head *bclean(handle_t *handle, struct super_block *sb, ext3_fsblk_t blk){ struct buffer_head *bh; int err; bh = sb_getblk(sb, blk); if (!bh) return ERR_PTR(-EIO); if ((err = ext3_journal_get_write_access(handle, bh))) { brelse(bh); bh = ERR_PTR(err); } else { lock_buffer(bh); memset(bh->b_data, 0, sb->s_blocksize); set_buffer_uptodate(bh); unlock_buffer(bh); } return bh;}/* * To avoid calling the atomic setbit hundreds or thousands of times, we only * need to use it within a single byte (to ensure we get endianness right). * We can use memset for the rest of the bitmap as there are no other users. */static void mark_bitmap_end(int start_bit, int end_bit, char *bitmap){ int i; if (start_bit >= end_bit) return; ext3_debug("mark end bits +%d through +%d used\n", start_bit, end_bit); for (i = start_bit; i < ((start_bit + 7) & ~7UL); i++) ext3_set_bit(i, bitmap); if (i < end_bit) memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3);}/* * If we have fewer than thresh credits, extend by EXT3_MAX_TRANS_DATA. * If that fails, restart the transaction & regain write access for the * buffer head which is used for block_bitmap modifications. */static int extend_or_restart_transaction(handle_t *handle, int thresh, struct buffer_head *bh){ int err; if (handle->h_buffer_credits >= thresh) return 0; err = ext3_journal_extend(handle, EXT3_MAX_TRANS_DATA); if (err < 0) return err; if (err) { err = ext3_journal_restart(handle, EXT3_MAX_TRANS_DATA); if (err) return err; err = ext3_journal_get_write_access(handle, bh); if (err) return err; } return 0;}/* * Set up the block and inode bitmaps, and the inode table for the new group. * This doesn't need to be part of the main transaction, since we are only * 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. */static int setup_new_group_blocks(struct super_block *sb, struct ext3_new_group_data *input){ struct ext3_sb_info *sbi = EXT3_SB(sb); 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; ext3_fsblk_t block; ext3_grpblk_t bit; int i; int err = 0, err2; /* This transaction may be extended/restarted along the way */ handle = ext3_journal_start_sb(sb, EXT3_MAX_TRANS_DATA); if (IS_ERR(handle)) return PTR_ERR(handle); lock_super(sb); if (input->group != sbi->s_groups_count) { err = -EBUSY; goto exit_journal; } if (IS_ERR(bh = bclean(handle, sb, input->block_bitmap))) { err = PTR_ERR(bh); goto exit_journal; } if (ext3_bg_has_super(sb, input->group)) { ext3_debug("mark backup superblock %#04lx (+0)\n", start); ext3_set_bit(0, bh->b_data); } /* Copy all of the GDT blocks into the backup in this group */ for (i = 0, bit = 1, block = start + 1; i < gdblocks; i++, block++, bit++) { struct buffer_head *gdb; ext3_debug("update backup group %#04lx (+%d)\n", block, bit); err = extend_or_restart_transaction(handle, 1, bh); if (err) goto exit_bh; gdb = sb_getblk(sb, block); if (!gdb) { err = -EIO; goto exit_bh; } if ((err = ext3_journal_get_write_access(handle, gdb))) { brelse(gdb); goto exit_bh; } lock_buffer(gdb); memcpy(gdb->b_data, sbi->s_group_desc[i]->b_data, gdb->b_size); set_buffer_uptodate(gdb); unlock_buffer(gdb); ext3_journal_dirty_metadata(handle, gdb); ext3_set_bit(bit, bh->b_data); brelse(gdb); } /* Zero out all of the reserved backup group descriptor table blocks */ for (i = 0, bit = gdblocks + 1, block = start + bit; i < reserved_gdb; i++, block++, bit++) { struct buffer_head *gdb; ext3_debug("clear reserved block %#04lx (+%d)\n", block, bit); err = extend_or_restart_transaction(handle, 1, bh); if (err) goto exit_bh; if (IS_ERR(gdb = bclean(handle, sb, block))) { err = PTR_ERR(bh); goto exit_bh; } ext3_journal_dirty_metadata(handle, gdb); ext3_set_bit(bit, bh->b_data); brelse(gdb); } ext3_debug("mark block bitmap %#04x (+%ld)\n", input->block_bitmap, input->block_bitmap - start); ext3_set_bit(input->block_bitmap - start, bh->b_data); ext3_debug("mark inode bitmap %#04x (+%ld)\n", input->inode_bitmap, input->inode_bitmap - start); ext3_set_bit(input->inode_bitmap - start, bh->b_data); /* Zero out all of the inode table blocks */ for (i = 0, block = input->inode_table, bit = block - start; i < sbi->s_itb_per_group; i++, bit++, block++) { struct buffer_head *it; ext3_debug("clear inode block %#04lx (+%d)\n", block, bit); err = extend_or_restart_transaction(handle, 1, bh); if (err) goto exit_bh; if (IS_ERR(it = bclean(handle, sb, block))) { err = PTR_ERR(it); goto exit_bh; } ext3_journal_dirty_metadata(handle, it); brelse(it); ext3_set_bit(bit, bh->b_data); } err = extend_or_restart_transaction(handle, 2, bh); if (err) goto exit_bh; mark_bitmap_end(input->blocks_count, EXT3_BLOCKS_PER_GROUP(sb), bh->b_data); ext3_journal_dirty_metadata(handle, bh); brelse(bh); /* Mark unused entries in inode bitmap used */ ext3_debug("clear inode bitmap %#04x (+%ld)\n", input->inode_bitmap, input->inode_bitmap - start); if (IS_ERR(bh = bclean(handle, sb, input->inode_bitmap))) { err = PTR_ERR(bh); goto exit_journal; } mark_bitmap_end(EXT3_INODES_PER_GROUP(sb), EXT3_BLOCKS_PER_GROUP(sb), bh->b_data); ext3_journal_dirty_metadata(handle, bh);exit_bh: brelse(bh);exit_journal: unlock_super(sb); if ((err2 = ext3_journal_stop(handle)) && !err) err = err2; return err;}/* * 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, ... */static unsigned ext3_list_backups(struct super_block *sb, unsigned *three, unsigned *five, unsigned *seven){ unsigned *min = three; int mult = 3; unsigned ret; if (!EXT3_HAS_RO_COMPAT_FEATURE(sb, EXT3_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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -