blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i) { int bg; int has_super = 0; int ret_blk; if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || (i < fs->super->s_first_meta_bg)) return (group_block + i + 1); bg = EXT2_DESC_PER_BLOCK(fs->super) * i; if (ext2fs_bg_has_super(fs, bg)) has_super = 1; ret_blk = ext2fs_group_first_block(fs, bg) + has_super; /* * If group_block is not the normal value, we're trying to use * the backup group descriptors and superblock --- so use the * alternate location of the second block group in the * metablock group. Ideally we should be testing each bg * descriptor block individually for correctness, but we don't * have the infrastructure in place to do that. */ if (group_block != fs->super->s_first_data_block && ((ret_blk + fs->super->s_blocks_per_group) < fs->super->s_blocks_count)) ret_blk += fs->super->s_blocks_per_group; return ret_blk; }
/* * ext2fs_super_and_bgd_loc2() * @fs: ext2 fs pointer * @group given block group * @ret_super_blk: if !NULL, returns super block location * @ret_old_desc_blk: if !NULL, returns location of the old block * group descriptor * @ret_new_desc_blk: if !NULL, returns location of meta_bg block * group descriptor * @ret_used_blks: if !NULL, returns number of blocks used by * super block and group_descriptors. * * Returns errcode_t of 0 */ errcode_t ext2fs_super_and_bgd_loc2(ext2_filsys fs, dgrp_t group, blk64_t *ret_super_blk, blk64_t *ret_old_desc_blk, blk64_t *ret_new_desc_blk, blk_t *ret_used_blks) { blk64_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; unsigned int meta_bg, meta_bg_size; blk_t numblocks = 0; blk64_t old_desc_blocks; int has_super; group_block = ext2fs_group_first_block2(fs, group); if (group_block == 0 && fs->blocksize == 1024) group_block = 1; /* Deal with 1024 blocksize && bigalloc */ if (ext2fs_has_feature_meta_bg(fs->super)) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks; has_super = ext2fs_bg_has_super(fs, group); if (has_super) { super_blk = group_block; numblocks++; } meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); meta_bg = group / meta_bg_size; if (!ext2fs_has_feature_meta_bg(fs->super) || (meta_bg < fs->super->s_first_meta_bg)) { if (has_super) { old_desc_blk = group_block + 1; numblocks += old_desc_blocks; } } else { if (((group % meta_bg_size) == 0) || ((group % meta_bg_size) == 1) || ((group % meta_bg_size) == (meta_bg_size-1))) { if (has_super) has_super = 1; new_desc_blk = group_block + has_super; numblocks++; } } if (ret_super_blk) *ret_super_blk = super_blk; if (ret_old_desc_blk) *ret_old_desc_blk = old_desc_blk; if (ret_new_desc_blk) *ret_new_desc_blk = new_desc_blk; if (ret_used_blks) *ret_used_blks = numblocks; return 0; }
blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, blk64_t group_block, dgrp_t i) { int bg; int has_super = 0, group_zero_adjust = 0; blk64_t ret_blk; /* * On a bigalloc FS with 1K blocks, block 0 is reserved for non-ext4 * stuff, so adjust for that if we're being asked for group 0. */ if (i == 0 && fs->blocksize == 1024 && EXT2FS_CLUSTER_RATIO(fs) > 1) group_zero_adjust = 1; if (!ext2fs_has_feature_meta_bg(fs->super) || (i < fs->super->s_first_meta_bg)) return group_block + i + 1 + group_zero_adjust; bg = EXT2_DESC_PER_BLOCK(fs->super) * i; if (ext2fs_bg_has_super(fs, bg)) has_super = 1; ret_blk = ext2fs_group_first_block2(fs, bg); /* * If group_block is not the normal value, we're trying to use * the backup group descriptors and superblock --- so use the * alternate location of the second block group in the * metablock group. Ideally we should be testing each bg * descriptor block individually for correctness, but we don't * have the infrastructure in place to do that. */ if (group_block != fs->super->s_first_data_block && ((ret_blk + has_super + fs->super->s_blocks_per_group) < ext2fs_blocks_count(fs->super))) { ret_blk += fs->super->s_blocks_per_group; /* * If we're going to jump forward a block group, make sure * that we adjust has_super to account for the next group's * backup superblock (or lack thereof). */ if (ext2fs_bg_has_super(fs, bg + 1)) has_super = 1; else has_super = 0; } return ret_blk + has_super + group_zero_adjust; }
/* * ext2fs_super_and_bgd_loc2() * @fs: ext2 fs pointer * @group given block group * @ret_super_blk: if !NULL, returns super block location * @ret_old_desc_blk: if !NULL, returns location of the old block * group descriptor * @ret_new_desc_blk: if !NULL, returns location of meta_bg block * group descriptor * @ret_used_blks: if !NULL, returns number of blocks used by * super block and group_descriptors. * * Returns errcode_t of 0 */ errcode_t ext2fs_super_and_bgd_loc2(ext2_filsys fs, dgrp_t group, blk64_t *ret_super_blk, blk64_t *ret_old_desc_blk, blk64_t *ret_new_desc_blk, blk_t *ret_used_blks) { blk64_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; unsigned int meta_bg, meta_bg_size; blk_t numblocks = 0; blk64_t old_desc_blocks; int has_super; group_block = ext2fs_group_first_block2(fs, group); 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 + fs->super->s_reserved_gdt_blocks; has_super = ext2fs_bg_has_super(fs, group); if (has_super) { super_blk = group_block; numblocks++; } meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); meta_bg = group / meta_bg_size; if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || (meta_bg < fs->super->s_first_meta_bg)) { if (has_super) { old_desc_blk = group_block + 1; numblocks += old_desc_blocks; } } else { if (((group % meta_bg_size) == 0) || ((group % meta_bg_size) == 1) || ((group % meta_bg_size) == (meta_bg_size-1))) { if (has_super) has_super = 1; new_desc_blk = group_block + has_super; numblocks++; } } if (ret_super_blk) *ret_super_blk = super_blk; if (ret_old_desc_blk) *ret_old_desc_blk = old_desc_blk; if (ret_new_desc_blk) *ret_new_desc_blk = new_desc_blk; if (ret_used_blks) *ret_used_blks = numblocks; return 0; }
static void determine_fs_stride(ext2_filsys fs) { unsigned int group; unsigned long long sum; unsigned int has_sb, prev_has_sb = 0, num; int i_stride, b_stride; int flexbg_size = 1 << fs->super->s_log_groups_per_flex; if (fs->stride) return; num = 0; sum = 0; for (group = 0; group < fs->group_desc_count; group++) { has_sb = ext2fs_bg_has_super(fs, group); if (group == 0 || has_sb != prev_has_sb) goto next; b_stride = ext2fs_block_bitmap_loc(fs, group) - ext2fs_block_bitmap_loc(fs, group - 1) - fs->super->s_blocks_per_group; i_stride = ext2fs_inode_bitmap_loc(fs, group) - ext2fs_inode_bitmap_loc(fs, group - 1) - fs->super->s_blocks_per_group; if (b_stride != i_stride || b_stride < 0 || (flexbg_size > 1 && (group % flexbg_size == 0))) goto next; /* printf("group %d has stride %d\n", group, b_stride); */ sum += b_stride; num++; next: prev_has_sb = has_sb; } if (fs->group_desc_count > 12 && num < 3) sum = 0; if (num) fs->stride = sum / num; else fs->stride = 0; fs->super->s_raid_stride = fs->stride; ext2fs_mark_super_dirty(fs); #if 0 if (fs->stride) printf("Using RAID stride of %d\n", fs->stride); #endif }
static errcode_t adjust_fs_size(ext2_filsys fs, long long *new_size) { errcode_t retval; int overhead = 0; int rem; fs->super->s_blocks_count = (unsigned int)(*new_size / fs->blocksize); retry: fs->group_desc_count = ext2fs_div_ceil(fs->super->s_blocks_count - fs->super->s_first_data_block, EXT2_BLOCKS_PER_GROUP(fs->super)); if (fs->group_desc_count == 0) return EXT2_ET_TOOSMALL; fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(fs->super)); /* * 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. */ overhead = (int) (2 + fs->inode_blocks_per_group); if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) overhead += 1 + fs->desc_blocks + fs->super->s_reserved_gdt_blocks; /* * See if the last group is big enough to support the * necessary data structures. If not, we need to get rid of * it. */ rem = (fs->super->s_blocks_count - fs->super->s_first_data_block) % fs->super->s_blocks_per_group; if ((fs->group_desc_count == 1) && rem && (rem < overhead)) return EXT2_ET_TOOSMALL; if (rem && (rem < overhead+50)) { fs->super->s_blocks_count -= rem; goto retry; } *new_size = ((long long)fs->super->s_blocks_count * (long long)fs->blocksize); return 0; }
static void show_stats(ext2_filsys fs) { struct ext2_super_block *s = fs->super; char buf[80]; char *os; blk_t group_block; dgrp_t i; int need, col_left; if (fs_param.s_blocks_count != s->s_blocks_count) fprintf(stderr, _("warning: %u blocks unused.\n\n"), fs_param.s_blocks_count - s->s_blocks_count); memset(buf, 0, sizeof(buf)); strncpy(buf, s->s_volume_name, sizeof(s->s_volume_name)); printf(_("Filesystem label=%s\n"), buf); fputs(_("OS type: "), stdout); os = e2p_os2string(fs->super->s_creator_os); fputs(os, stdout); free(os); printf("\n"); printf(_("Block size=%u (log=%u)\n"), fs->blocksize, s->s_log_block_size); printf(_("Fragment size=%u (log=%u)\n"), fs->fragsize, s->s_log_frag_size); printf(_("%u inodes, %u blocks\n"), s->s_inodes_count, s->s_blocks_count); #ifdef EMBED printf(_("%u blocks (%d%%) reserved for the super user\n"), s->s_r_blocks_count, 100 * s->s_r_blocks_count / s->s_blocks_count); #else printf(_("%u blocks (%2.2f%%) reserved for the super user\n"), s->s_r_blocks_count, 100.0 * s->s_r_blocks_count / s->s_blocks_count); #endif 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 printf(_("%u block group\n"), fs->group_desc_count); printf(_("%u blocks per group, %u fragments per group\n"), s->s_blocks_per_group, s->s_frags_per_group); printf(_("%u inodes per group\n"), s->s_inodes_per_group); if (fs->group_desc_count == 1) { printf("\n"); return; } printf(_("Superblock backups stored on blocks: ")); group_block = s->s_first_data_block; col_left = 0; for (i = 1; i < fs->group_desc_count; i++) { group_block += s->s_blocks_per_group; if (!ext2fs_bg_has_super(fs, i)) continue; if (i != 1) printf(", "); need = int_log10(group_block) + 2; if (need > col_left) { printf("\n\t"); col_left = 72; } col_left -= need; printf("%u", group_block); } printf("\n\n"); }
/* * This function returns the location of the superblock, block group * descriptors for a given block group. It currently returns the * number of free blocks assuming that inode table and allocation * bitmaps will be in the group. This is not necessarily the case * when the flex_bg feature is enabled, so callers should take care! * It was only really intended for use by mke2fs, and even there it's * not that useful. In the future, when we redo this function for * 64-bit block numbers, we should probably return the number of * blocks used by the super block and group descriptors instead. * * See also the comment for ext2fs_reserve_super_and_bgd() */ 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) { blk_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; unsigned int meta_bg, meta_bg_size; blk_t numblocks, old_desc_blocks; int has_super; group_block = ext2fs_group_first_block(fs, group); 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 + fs->super->s_reserved_gdt_blocks; if (group == fs->group_desc_count-1) { numblocks = (fs->super->s_blocks_count - fs->super->s_first_data_block) % fs->super->s_blocks_per_group; if (!numblocks) numblocks = fs->super->s_blocks_per_group; } else numblocks = fs->super->s_blocks_per_group; has_super = ext2fs_bg_has_super(fs, group); if (has_super) { super_blk = group_block; numblocks--; } meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); meta_bg = group / meta_bg_size; if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || (meta_bg < fs->super->s_first_meta_bg)) { if (has_super) { old_desc_blk = group_block + 1; numblocks -= old_desc_blocks; } } else { if (((group % meta_bg_size) == 0) || ((group % meta_bg_size) == 1) || ((group % meta_bg_size) == (meta_bg_size-1))) { if (has_super) has_super = 1; new_desc_blk = group_block + has_super; numblocks--; } } numblocks -= 2 + fs->inode_blocks_per_group; if (ret_super_blk) *ret_super_blk = super_blk; if (ret_old_desc_blk) *ret_old_desc_blk = old_desc_blk; if (ret_new_desc_blk) *ret_new_desc_blk = new_desc_blk; if (ret_meta_bg) *ret_meta_bg = meta_bg; return (numblocks); }
errcode_t online_resize_fs(ext2_filsys fs, const char *mtpt, blk64_t *new_size, int flags EXT2FS_ATTR((unused))) { #ifdef __linux__ struct ext2_new_group_input input; struct ext4_new_group_input input64; struct ext2_super_block *sb = fs->super; unsigned long new_desc_blocks; ext2_filsys new_fs; errcode_t retval; double percent; dgrp_t i; blk_t size; int fd, overhead; int use_old_ioctl = 1; int no_meta_bg_resize = 0; int no_resize_ioctl = 0; if (getenv("RESIZE2FS_KERNEL_VERSION")) { char *version_to_emulate = getenv("RESIZE2FS_KERNEL_VERSION"); int kvers = parse_version_number(version_to_emulate); if (kvers < VERSION_CODE(3, 7, 0)) no_meta_bg_resize = 1; if (kvers < VERSION_CODE(3, 3, 0)) no_resize_ioctl = 1; } if (ext2fs_has_feature_sparse_super2(fs->super) && (access("/sys/fs/ext4/features/sparse_super2", R_OK) != 0)) { com_err(program_name, 0, _("kernel does not support online " "resize with sparse_super2")); exit(1); } printf(_("Filesystem at %s is mounted on %s; " "on-line resizing required\n"), fs->device_name, mtpt); if (*new_size < ext2fs_blocks_count(sb)) { com_err(program_name, 0, _("On-line shrinking not supported")); exit(1); } /* * If the number of descriptor blocks is going to increase, * the on-line resizing inode must be present. */ new_desc_blocks = ext2fs_div_ceil( ext2fs_div64_ceil(*new_size - fs->super->s_first_data_block, EXT2_BLOCKS_PER_GROUP(fs->super)), EXT2_DESC_PER_BLOCK(fs->super)); printf("old_desc_blocks = %lu, new_desc_blocks = %lu\n", fs->desc_blocks, new_desc_blocks); /* * Do error checking to make sure the resize will be successful. */ if ((access("/sys/fs/ext4/features/meta_bg_resize", R_OK) != 0) || no_meta_bg_resize) { if (!ext2fs_has_feature_resize_inode(fs->super) && (new_desc_blocks != fs->desc_blocks)) { com_err(program_name, 0, _("Filesystem does not support online resizing")); exit(1); } if (ext2fs_has_feature_resize_inode(fs->super) && new_desc_blocks > (fs->desc_blocks + fs->super->s_reserved_gdt_blocks)) { com_err(program_name, 0, _("Not enough reserved gdt blocks for resizing")); exit(1); } if ((ext2fs_blocks_count(sb) > MAX_32_NUM) || (*new_size > MAX_32_NUM)) { com_err(program_name, 0, _("Kernel does not support resizing a file system this large")); exit(1); } } fd = open(mtpt, O_RDONLY); if (fd < 0) { com_err(program_name, errno, _("while trying to open mountpoint %s"), mtpt); exit(1); } if (no_resize_ioctl) { printf(_("Old resize interface requested.\n")); } else if (ioctl(fd, EXT4_IOC_RESIZE_FS, new_size)) { /* * If kernel does not support EXT4_IOC_RESIZE_FS, use the * old online resize. Note that the old approach does not * handle >32 bit file systems * * Sigh, if we are running a 32-bit binary on a 64-bit * kernel (which happens all the time on the MIPS * architecture in Debian, but can happen on other CPU * architectures as well) we will get EINVAL returned * when an ioctl doesn't exist, at least up to Linux * 3.1. See compat_sys_ioctl() in fs/compat_ioctl.c * in the kernel sources. This is probably a kernel * bug, but work around it here. */ if ((errno != ENOTTY) && (errno != EINVAL)) { if (errno == EPERM) com_err(program_name, 0, _("Permission denied to resize filesystem")); else com_err(program_name, errno, _("While checking for on-line resizing " "support")); exit(1); } } else { close(fd); return 0; } size = ext2fs_blocks_count(sb); if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) { if (errno == EPERM) com_err(program_name, 0, _("Permission denied to resize filesystem")); else if (errno == ENOTTY) com_err(program_name, 0, _("Kernel does not support online resizing")); else com_err(program_name, errno, _("While checking for on-line resizing support")); exit(1); } percent = (ext2fs_r_blocks_count(sb) * 100.0) / ext2fs_blocks_count(sb); retval = ext2fs_read_bitmaps(fs); if (retval) { close(fd); return retval; } retval = ext2fs_dup_handle(fs, &new_fs); if (retval) { close(fd); return retval; } /* The current method of adding one block group at a time to a * mounted filesystem means it is impossible to accommodate the * flex_bg allocation method of placing the metadata together * in a single block group. For now we "fix" this issue by * using the traditional layout for new block groups, where * each block group is self-contained and contains its own * bitmap blocks and inode tables. This means we don't get * the layout advantages of flex_bg in the new block groups, * but at least it allows on-line resizing to function. */ ext2fs_clear_feature_flex_bg(new_fs->super); retval = adjust_fs_info(new_fs, fs, 0, *new_size); if (retval) { close(fd); return retval; } printf(_("Performing an on-line resize of %s to %llu (%dk) blocks.\n"), fs->device_name, *new_size, fs->blocksize / 1024); size = fs->group_desc_count * sb->s_blocks_per_group + sb->s_first_data_block; if (size > *new_size) size = *new_size; if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) { com_err(program_name, errno, _("While trying to extend the last group")); exit(1); } for (i = fs->group_desc_count; i < new_fs->group_desc_count; i++) { overhead = (int) (2 + new_fs->inode_blocks_per_group); if (ext2fs_bg_has_super(new_fs, new_fs->group_desc_count - 1)) overhead += 1 + new_fs->desc_blocks + new_fs->super->s_reserved_gdt_blocks; input.group = i; input.block_bitmap = ext2fs_block_bitmap_loc(new_fs, i); input.inode_bitmap = ext2fs_inode_bitmap_loc(new_fs, i); input.inode_table = ext2fs_inode_table_loc(new_fs, i); input.blocks_count = ext2fs_group_blocks_count(new_fs, i); input.reserved_blocks = (blk_t) (percent * input.blocks_count / 100.0); #if 0 printf("new block bitmap is at 0x%04x\n", input.block_bitmap); printf("new inode bitmap is at 0x%04x\n", input.inode_bitmap); printf("new inode table is at 0x%04x-0x%04x\n", input.inode_table, input.inode_table + new_fs->inode_blocks_per_group-1); printf("new group has %u blocks\n", input.blocks_count); printf("new group will reserve %d blocks\n", input.reserved_blocks); printf("new group has %d free blocks\n", ext2fs_bg_free_blocks_count(new_fs, i), printf("new group has %d free inodes (%d blocks)\n", ext2fs_bg_free_inodes_count(new_fs, i), new_fs->inode_blocks_per_group); printf("Adding group #%d\n", input.group); #endif if (use_old_ioctl && ioctl(fd, EXT2_IOC_GROUP_ADD, &input) == 0) continue; else use_old_ioctl = 0; input64.group = input.group; input64.block_bitmap = input.block_bitmap; input64.inode_bitmap = input.inode_bitmap; input64.inode_table = input.inode_table; input64.blocks_count = input.blocks_count; input64.reserved_blocks = input.reserved_blocks; input64.unused = input.unused; if (ioctl(fd, EXT4_IOC_GROUP_ADD, &input64) < 0) { com_err(program_name, errno, _("While trying to add group #%d"), input.group); exit(1); } } ext2fs_free(new_fs); close(fd); return 0; #else printf(_("Filesystem at %s is mounted on %s, and on-line resizing is " "not supported on this system.\n"), fs->device_name, mtpt); exit(1); #endif }
errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; struct ext2_super_block *super; int frags_per_block; unsigned int rem; unsigned int overhead = 0; unsigned int ipg; dgrp_t i; blk_t numblocks; int rsv_gdt; int io_flags; char *buf; char c; if (!param || !param->s_blocks_count) return EXT2_ET_INVALID_ARGUMENT; retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags | EXT2_FLAG_RW; fs->umask = 022; #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; #endif io_flags = IO_FLAG_RW; if (flags & EXT2_FLAG_EXCLUSIVE) io_flags |= IO_FLAG_EXCLUSIVE; retval = manager->open(name, io_flags, &fs->io); if (retval) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); if (retval) goto cleanup; fs->super = super; memset(super, 0, SUPERBLOCK_SIZE); #define set_field(field, default) (super->field = param->field ? \ param->field : (default)) super->s_magic = EXT2_SUPER_MAGIC; super->s_state = EXT2_VALID_FS; set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */ set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */ set_field(s_first_data_block, super->s_log_block_size ? 0 : 1); set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT); set_field(s_errors, EXT2_ERRORS_DEFAULT); set_field(s_feature_compat, 0); 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) { 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) { set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); } set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL); super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL); super->s_creator_os = CREATOR_OS; fs->blocksize = EXT2_BLOCK_SIZE(super); fs->fragsize = EXT2_FRAG_SIZE(super); frags_per_block = fs->blocksize / fs->fragsize; /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */ set_field(s_blocks_per_group, fs->blocksize * 8); if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; super->s_blocks_count = param->s_blocks_count; super->s_r_blocks_count = param->s_r_blocks_count; if (super->s_r_blocks_count >= param->s_blocks_count) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } /* * 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) { fs->group_desc_count = 0; ext2fs_mark_super_dirty(fs); *ret_fs = fs; return 0; } retry: fs->group_desc_count = ext2fs_div_ceil(super->s_blocks_count - super->s_first_data_block, EXT2_BLOCKS_PER_GROUP(super)); if (fs->group_desc_count == 0) { retval = EXT2_ET_TOOSMALL; goto cleanup; } fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(super)); i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; set_field(s_inodes_count, super->s_blocks_count / i); /* * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so * that we have enough inodes for the filesystem(!) */ if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) super->s_inodes_count = EXT2_FIRST_INODE(super)+1; /* * There should be at least as many inodes as the user * requested. Figure out how many inodes per group that * should be. But make sure that we don't allocate more than * one bitmap's worth of inodes each group. */ ipg = ext2fs_div_ceil(super->s_inodes_count, fs->group_desc_count); if (ipg > fs->blocksize * 8) { if (super->s_blocks_per_group >= 256) { /* Try again with slightly different parameters */ super->s_blocks_per_group -= 8; super->s_blocks_count = param->s_blocks_count; super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; goto retry; } else return EXT2_ET_TOO_MANY_INODES; } if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) ipg = EXT2_MAX_INODES_PER_GROUP(super); ipg_retry: super->s_inodes_per_group = ipg; /* * Make sure the number of inodes per group completely fills * the inode table blocks in the descriptor. If not, add some * additional inodes/group. Waste not, want not... */ fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); super->s_inodes_per_group = ((fs->inode_blocks_per_group * EXT2_BLOCK_SIZE(super)) / EXT2_INODE_SIZE(super)); /* * Finally, make sure the number of inodes per group is a * multiple of 8. This is needed to simplify the bitmap * splicing code. */ super->s_inodes_per_group &= ~7; fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); /* * adjust inode count to reflect the adjusted inodes_per_group */ if ((__u64)super->s_inodes_per_group * fs->group_desc_count > ~0U) { ipg--; goto ipg_retry; } super->s_inodes_count = super->s_inodes_per_group * fs->group_desc_count; 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. */ 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) return EXT2_ET_TOO_MANY_INODES; /* * See if the last group is big enough to support the * necessary data structures. If not, we need to get rid of * it. */ rem = ((super->s_blocks_count - super->s_first_data_block) % super->s_blocks_per_group); if ((fs->group_desc_count == 1) && rem && (rem < overhead)) return EXT2_ET_TOOSMALL; if (rem && (rem < overhead+50)) { super->s_blocks_count -= rem; goto retry; } /* * At this point we know how big the filesystem will be. So * we can do any and all allocations that depend on the block * count. */ retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) goto cleanup; sprintf(buf, "block bitmap for %s", fs->device_name); retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; sprintf(buf, "inode bitmap for %s", fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; ext2fs_free_mem(&buf); retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize, &fs->group_desc); if (retval) goto cleanup; memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); /* * Reserve the superblock and group descriptors for each * group, and fill in the correct group statistics for group. * Note that although the block bitmap, inode bitmap, and * inode table have not been allocated (and in fact won't be * by this routine), they are accounted for nevertheless. */ super->s_free_blocks_count = 0; for (i = 0; i < fs->group_desc_count; i++) { numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); super->s_free_blocks_count += numblocks; fs->group_desc[i].bg_free_blocks_count = numblocks; fs->group_desc[i].bg_free_inodes_count = fs->super->s_inodes_per_group; fs->group_desc[i].bg_used_dirs_count = 0; } c = (char) 255; if (((int) c) == -1) { super->s_flags |= EXT2_FLAGS_SIGNED_HASH; } else { super->s_flags |= EXT2_FLAGS_UNSIGNED_HASH; } ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); ext2fs_mark_ib_dirty(fs); io_channel_set_blksize(fs->io, fs->blocksize); *ret_fs = fs; return 0; cleanup: ext2fs_free(fs); return retval; }
errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; struct ext2_super_block *super; unsigned int rem; unsigned int overhead = 0; unsigned int ipg; dgrp_t i; blk64_t free_blocks; blk_t numblocks; int rsv_gdt; int csum_flag; int bigalloc_flag; int io_flags; unsigned reserved_inos; char *buf = 0; char c; double reserved_ratio; if (!param || !ext2fs_blocks_count(param)) return EXT2_ET_INVALID_ARGUMENT; retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags | EXT2_FLAG_RW; fs->umask = 022; fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE; #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; #endif io_flags = IO_FLAG_RW; if (flags & EXT2_FLAG_EXCLUSIVE) io_flags |= IO_FLAG_EXCLUSIVE; if (flags & EXT2_FLAG_DIRECT_IO) io_flags |= IO_FLAG_DIRECT_IO; retval = manager->open(name, io_flags, &fs->io); if (retval) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); if (retval) goto cleanup; fs->super = super; memset(super, 0, SUPERBLOCK_SIZE); #define set_field(field, default) (super->field = param->field ? \ param->field : (default)) #define assign_field(field) (super->field = param->field) super->s_magic = EXT2_SUPER_MAGIC; super->s_state = EXT2_VALID_FS; bigalloc_flag = EXT2_HAS_RO_COMPAT_FEATURE(param, EXT4_FEATURE_RO_COMPAT_BIGALLOC); assign_field(s_log_block_size); if (bigalloc_flag) { set_field(s_log_cluster_size, super->s_log_block_size+4); if (super->s_log_block_size > super->s_log_cluster_size) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } } else super->s_log_cluster_size = super->s_log_block_size; set_field(s_first_data_block, super->s_log_cluster_size ? 0 : 1); set_field(s_max_mnt_count, 0); set_field(s_errors, EXT2_ERRORS_DEFAULT); set_field(s_feature_compat, 0); set_field(s_feature_incompat, 0); set_field(s_feature_ro_compat, 0); set_field(s_default_mount_opts, 0); set_field(s_first_meta_bg, 0); set_field(s_raid_stride, 0); /* default stride size: 0 */ set_field(s_raid_stripe_width, 0); /* default stripe width: 0 */ set_field(s_log_groups_per_flex, 0); set_field(s_flags, 0); assign_field(s_backup_bgs[0]); assign_field(s_backup_bgs[1]); 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) { set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); if (super->s_inode_size >= sizeof(struct ext2_inode_large)) { int extra_isize = sizeof(struct ext2_inode_large) - EXT2_GOOD_OLD_INODE_SIZE; set_field(s_min_extra_isize, extra_isize); set_field(s_want_extra_isize, extra_isize); } } else { super->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; super->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; } set_field(s_checkinterval, 0); super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL); super->s_creator_os = CREATOR_OS; fs->fragsize = fs->blocksize = EXT2_BLOCK_SIZE(super); fs->cluster_ratio_bits = super->s_log_cluster_size - super->s_log_block_size; if (bigalloc_flag) { unsigned long long bpg; if (param->s_blocks_per_group && param->s_clusters_per_group && ((param->s_clusters_per_group * EXT2FS_CLUSTER_RATIO(fs)) != param->s_blocks_per_group)) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } if (param->s_clusters_per_group) assign_field(s_clusters_per_group); else if (param->s_blocks_per_group) super->s_clusters_per_group = param->s_blocks_per_group / EXT2FS_CLUSTER_RATIO(fs); else if (super->s_log_cluster_size + 15 < 32) super->s_clusters_per_group = fs->blocksize * 8; else super->s_clusters_per_group = (fs->blocksize - 1) * 8; if (super->s_clusters_per_group > EXT2_MAX_CLUSTERS_PER_GROUP(super)) super->s_clusters_per_group = EXT2_MAX_CLUSTERS_PER_GROUP(super); bpg = EXT2FS_C2B(fs, (unsigned long long) super->s_clusters_per_group); if (bpg >= (((unsigned long long) 1) << 32)) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } super->s_blocks_per_group = bpg; } else { set_field(s_blocks_per_group, fs->blocksize * 8); if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); super->s_clusters_per_group = super->s_blocks_per_group; } ext2fs_blocks_count_set(super, ext2fs_blocks_count(param) & ~((blk64_t) EXT2FS_CLUSTER_MASK(fs))); ext2fs_r_blocks_count_set(super, ext2fs_r_blocks_count(param)); if (ext2fs_r_blocks_count(super) >= ext2fs_blocks_count(param)) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } set_field(s_mmp_update_interval, 0); /* * 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) { fs->group_desc_count = 0; ext2fs_mark_super_dirty(fs); *ret_fs = fs; return 0; } retry: fs->group_desc_count = (dgrp_t) ext2fs_div64_ceil( ext2fs_blocks_count(super) - super->s_first_data_block, EXT2_BLOCKS_PER_GROUP(super)); if (fs->group_desc_count == 0) { retval = EXT2_ET_TOOSMALL; goto cleanup; } set_field(s_desc_size, super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? EXT2_MIN_DESC_SIZE_64BIT : 0); fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(super)); i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT && (ext2fs_blocks_count(super) / i) > (1ULL << 32)) set_field(s_inodes_count, ~0U); else set_field(s_inodes_count, ext2fs_blocks_count(super) / i); /* * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so * that we have enough inodes for the filesystem(!) */ if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) super->s_inodes_count = EXT2_FIRST_INODE(super)+1; /* * There should be at least as many inodes as the user * requested. Figure out how many inodes per group that * should be. But make sure that we don't allocate more than * one bitmap's worth of inodes each group. */ ipg = ext2fs_div_ceil(super->s_inodes_count, fs->group_desc_count); if (ipg > fs->blocksize * 8) { if (!bigalloc_flag && super->s_blocks_per_group >= 256) { /* Try again with slightly different parameters */ super->s_blocks_per_group -= 8; ext2fs_blocks_count_set(super, ext2fs_blocks_count(param)); super->s_clusters_per_group = super->s_blocks_per_group; goto retry; } else { retval = EXT2_ET_TOO_MANY_INODES; goto cleanup; } } if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) ipg = EXT2_MAX_INODES_PER_GROUP(super); ipg_retry: super->s_inodes_per_group = ipg; /* * Make sure the number of inodes per group completely fills * the inode table blocks in the descriptor. If not, add some * additional inodes/group. Waste not, want not... */ fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); super->s_inodes_per_group = ((fs->inode_blocks_per_group * EXT2_BLOCK_SIZE(super)) / EXT2_INODE_SIZE(super)); /* * Finally, make sure the number of inodes per group is a * multiple of 8. This is needed to simplify the bitmap * splicing code. */ if (super->s_inodes_per_group < 8) super->s_inodes_per_group = 8; super->s_inodes_per_group &= ~7; fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); /* * adjust inode count to reflect the adjusted inodes_per_group */ if ((__u64)super->s_inodes_per_group * fs->group_desc_count > ~0U) { ipg--; goto ipg_retry; } super->s_inodes_count = super->s_inodes_per_group * fs->group_desc_count; 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; } /* * Calculate the maximum number of bookkeeping blocks per * group. It includes the superblock, the block group * descriptors, the block bitmap, the inode bitmap, the inode * table, and the reserved gdt blocks. */ overhead = (int) (3 + fs->inode_blocks_per_group + 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) { retval = EXT2_ET_TOO_MANY_INODES; goto cleanup; } /* * See if the last group is big enough to support the * necessary data structures. If not, we need to get rid of * it. We need to recalculate the overhead for the last block * group, since it might or might not have a superblock * backup. */ 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; rem = ((ext2fs_blocks_count(super) - super->s_first_data_block) % super->s_blocks_per_group); if ((fs->group_desc_count == 1) && rem && (rem < overhead)) { retval = EXT2_ET_TOOSMALL; goto cleanup; } if (rem && (rem < overhead+50)) { ext2fs_blocks_count_set(super, ext2fs_blocks_count(super) - rem); /* * If blocks count is changed, we need to recalculate * reserved blocks count not to exceed 50%. */ reserved_ratio = 100.0 * ext2fs_r_blocks_count(param) / ext2fs_blocks_count(param); ext2fs_r_blocks_count_set(super, reserved_ratio * ext2fs_blocks_count(super) / 100.0); goto retry; } /* * At this point we know how big the filesystem will be. So * we can do any and all allocations that depend on the block * count. */ /* Set up the locations of the backup superblocks */ if (super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2) { if (super->s_backup_bgs[0] >= fs->group_desc_count) super->s_backup_bgs[0] = fs->group_desc_count - 1; if (super->s_backup_bgs[1] >= fs->group_desc_count) super->s_backup_bgs[1] = fs->group_desc_count - 1; if (super->s_backup_bgs[0] == super->s_backup_bgs[1]) super->s_backup_bgs[1] = 0; if (super->s_backup_bgs[0] > super->s_backup_bgs[1]) { __u32 t = super->s_backup_bgs[0]; super->s_backup_bgs[0] = super->s_backup_bgs[1]; super->s_backup_bgs[1] = t; } } retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) goto cleanup; strcpy(buf, "block bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_subcluster_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; strcpy(buf, "inode bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; ext2fs_free_mem(&buf); retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &fs->group_desc); if (retval) goto cleanup; memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); /* * Reserve the superblock and group descriptors for each * group, and fill in the correct group statistics for group. * Note that although the block bitmap, inode bitmap, and * inode table have not been allocated (and in fact won't be * by this routine), they are accounted for nevertheless. * * If FLEX_BG meta-data grouping is used, only account for the * superblock and group descriptors (the inode tables and * bitmaps will be accounted for when allocated). */ free_blocks = 0; csum_flag = ext2fs_has_group_desc_csum(fs); reserved_inos = super->s_first_ino; for (i = 0; i < fs->group_desc_count; i++) { /* * Don't set the BLOCK_UNINIT group for the last group * because the block bitmap needs to be padded. */ if (csum_flag) { if (i != fs->group_desc_count - 1) ext2fs_bg_flags_set(fs, i, EXT2_BG_BLOCK_UNINIT); ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); numblocks = super->s_inodes_per_group; if (reserved_inos) { if (numblocks > reserved_inos) { numblocks -= reserved_inos; reserved_inos = 0; } else { reserved_inos -= numblocks; numblocks = 0; } } ext2fs_bg_itable_unused_set(fs, i, numblocks); } numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); if (fs->super->s_log_groups_per_flex) numblocks += 2 + fs->inode_blocks_per_group; free_blocks += numblocks; ext2fs_bg_free_blocks_count_set(fs, i, numblocks); ext2fs_bg_free_inodes_count_set(fs, i, fs->super->s_inodes_per_group); ext2fs_bg_used_dirs_count_set(fs, i, 0); ext2fs_group_desc_csum_set(fs, i); } free_blocks &= ~EXT2FS_CLUSTER_MASK(fs); ext2fs_free_blocks_count_set(super, free_blocks); c = (char) 255; if (((int) c) == -1) { super->s_flags |= EXT2_FLAGS_SIGNED_HASH; } else { super->s_flags |= EXT2_FLAGS_UNSIGNED_HASH; } ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); ext2fs_mark_ib_dirty(fs); io_channel_set_blksize(fs->io, fs->blocksize); *ret_fs = fs; return 0; cleanup: free(buf); ext2fs_free(fs); return retval; }
static void check_block_bitmaps(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; blk_t i, super; int *free_array; int group = 0; unsigned int blocks = 0; unsigned int free_blocks = 0; int group_free = 0; int actual, bitmap; struct problem_context pctx; int problem, save_problem, fixit, had_problem; errcode_t retval; int lazy_bg = 0; int skip_group = 0; clear_problem_context(&pctx); free_array = (int *) e2fsck_allocate_memory(ctx, fs->group_desc_count * sizeof(int), "free block count array"); if ((fs->super->s_first_data_block < ext2fs_get_block_bitmap_start(ctx->block_found_map)) || (fs->super->s_blocks_count-1 > ext2fs_get_block_bitmap_end(ctx->block_found_map))) { pctx.num = 1; pctx.blk = fs->super->s_first_data_block; pctx.blk2 = fs->super->s_blocks_count -1; pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map); pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map); fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ goto errout; } if ((fs->super->s_first_data_block < ext2fs_get_block_bitmap_start(fs->block_map)) || (fs->super->s_blocks_count-1 > ext2fs_get_block_bitmap_end(fs->block_map))) { pctx.num = 2; pctx.blk = fs->super->s_first_data_block; pctx.blk2 = fs->super->s_blocks_count -1; pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map); pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map); fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); ctx->flags |= E2F_FLAG_ABORT; /* fatal */ goto errout; } if (EXT2_HAS_COMPAT_FEATURE(fs->super, EXT2_FEATURE_COMPAT_LAZY_BG)) lazy_bg++; redo_counts: had_problem = 0; save_problem = 0; pctx.blk = pctx.blk2 = NO_BLK; if (lazy_bg && (fs->group_desc[group].bg_flags & EXT2_BG_BLOCK_UNINIT)) skip_group++; super = fs->super->s_first_data_block; for (i = fs->super->s_first_data_block; i < fs->super->s_blocks_count; i++) { actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i); if (skip_group) { if ((i >= super) && (i <= super + fs->desc_blocks) && ext2fs_bg_has_super(fs, group)) bitmap = 1; else if (i == fs->group_desc[group].bg_block_bitmap) bitmap = 1; else if (i == fs->group_desc[group].bg_inode_bitmap) bitmap = 1; else if (i >= fs->group_desc[group].bg_inode_table && (i < fs->group_desc[group].bg_inode_table + fs->inode_blocks_per_group)) bitmap = 1; else bitmap = 0; actual = (actual != 0); } else bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i); if (actual == bitmap) goto do_counts; if (!actual && bitmap) { /* * Block not used, but marked in use in the bitmap. */ problem = PR_5_BLOCK_UNUSED; } else { /* * Block used, but not marked in use in the bitmap. */ problem = PR_5_BLOCK_USED; } if (pctx.blk == NO_BLK) { pctx.blk = pctx.blk2 = i; save_problem = problem; } else { if ((problem == save_problem) && (pctx.blk2 == i-1)) pctx.blk2++; else { print_bitmap_problem(ctx, save_problem, &pctx); pctx.blk = pctx.blk2 = i; save_problem = problem; } } ctx->flags |= E2F_FLAG_PROG_SUPPRESS; had_problem++; do_counts: if (!bitmap && !skip_group) { group_free++; free_blocks++; } blocks ++; if ((blocks == fs->super->s_blocks_per_group) || (i == fs->super->s_blocks_count-1)) { free_array[group] = group_free; group ++; blocks = 0; group_free = 0; skip_group = 0; super += fs->super->s_blocks_per_group; if (ctx->progress) if ((ctx->progress)(ctx, 5, group, fs->group_desc_count*2)) goto errout; if (lazy_bg && (i != fs->super->s_blocks_count-1) && (fs->group_desc[group].bg_flags & EXT2_BG_BLOCK_UNINIT)) skip_group++; } } if (pctx.blk != NO_BLK) print_bitmap_problem(ctx, save_problem, &pctx); if (had_problem) fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP); else fixit = -1; ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS; if (fixit == 1) { ext2fs_free_block_bitmap(fs->block_map); retval = ext2fs_copy_bitmap(ctx->block_found_map, &fs->block_map); if (retval) { clear_problem_context(&pctx); fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; goto errout; } ext2fs_set_bitmap_padding(fs->block_map); ext2fs_mark_bb_dirty(fs); /* Redo the counts */ blocks = 0; free_blocks = 0; group_free = 0; group = 0; memset(free_array, 0, fs->group_desc_count * sizeof(int)); goto redo_counts; } else if (fixit == 0) ext2fs_unmark_valid(fs); for (i = 0; i < fs->group_desc_count; i++) { if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) { pctx.group = i; pctx.blk = fs->group_desc[i].bg_free_blocks_count; pctx.blk2 = free_array[i]; if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP, &pctx)) { fs->group_desc[i].bg_free_blocks_count = free_array[i]; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } } if (free_blocks != fs->super->s_free_blocks_count) { pctx.group = 0; pctx.blk = fs->super->s_free_blocks_count; pctx.blk2 = free_blocks; if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) { fs->super->s_free_blocks_count = free_blocks; ext2fs_mark_super_dirty(fs); } else ext2fs_unmark_valid(fs); } errout: ext2fs_free_mem(&free_array); }
errcode_t online_resize_fs(ext2_filsys fs, const char *mtpt, blk64_t *new_size, int flags EXT2FS_ATTR((unused))) { #ifdef __linux__ struct ext2_new_group_input input; struct ext4_new_group_input input64; struct ext2_super_block *sb = fs->super; unsigned long new_desc_blocks; ext2_filsys new_fs; errcode_t retval; double percent; dgrp_t i; blk64_t size; int fd, overhead; int use_old_ioctl = 1; printf(_("Filesystem at %s is mounted on %s; " "on-line resizing required\n"), fs->device_name, mtpt); if (*new_size < ext2fs_blocks_count(sb)) { com_err(program_name, 0, _("On-line shrinking not supported")); exit(1); } /* * If the number of descriptor blocks is going to increase, * the on-line resizing inode must be present. */ new_desc_blocks = ext2fs_div_ceil( ext2fs_div64_ceil(*new_size - fs->super->s_first_data_block, EXT2_BLOCKS_PER_GROUP(fs->super)), EXT2_DESC_PER_BLOCK(fs->super)); printf("old desc_blocks = %lu, new_desc_blocks = %lu\n", fs->desc_blocks, new_desc_blocks); if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) && new_desc_blocks != fs->desc_blocks) { com_err(program_name, 0, _("Filesystem does not support online resizing")); exit(1); } fd = open(mtpt, O_RDONLY); if (fd < 0) { com_err(program_name, errno, _("while trying to open mountpoint %s"), mtpt); exit(1); } size=ext2fs_blocks_count(sb); if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) { if (errno == EPERM) com_err(program_name, 0, _("Permission denied to resize filesystem")); else if (errno == ENOTTY) com_err(program_name, 0, _("Kernel does not support online resizing")); else com_err(program_name, errno, _("While checking for on-line resizing support")); exit(1); } percent = (ext2fs_r_blocks_count(sb) * 100.0) / ext2fs_blocks_count(sb); retval = ext2fs_read_bitmaps(fs); if (retval) return retval; retval = ext2fs_dup_handle(fs, &new_fs); if (retval) return retval; /* The current method of adding one block group at a time to a * mounted filesystem means it is impossible to accomodate the * flex_bg allocation method of placing the metadata together * in a single block group. For now we "fix" this issue by * using the traditional layout for new block groups, where * each block group is self-contained and contains its own * bitmap blocks and inode tables. This means we don't get * the layout advantages of flex_bg in the new block groups, * but at least it allows on-line resizing to function. */ new_fs->super->s_feature_incompat &= ~EXT4_FEATURE_INCOMPAT_FLEX_BG; retval = adjust_fs_info(new_fs, fs, 0, *new_size); if (retval) return retval; printf(_("Performing an on-line resize of %s to %llu (%dk) blocks.\n"), fs->device_name, *new_size, fs->blocksize / 1024); size = fs->group_desc_count * sb->s_blocks_per_group + sb->s_first_data_block; if (size > *new_size) size = *new_size; if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) { com_err(program_name, errno, _("While trying to extend the last group")); exit(1); } for (i = fs->group_desc_count; i < new_fs->group_desc_count; i++) { overhead = (int) (2 + new_fs->inode_blocks_per_group); if (ext2fs_bg_has_super(new_fs, new_fs->group_desc_count - 1)) overhead += 1 + new_fs->desc_blocks + new_fs->super->s_reserved_gdt_blocks; input.group = i; input.block_bitmap = ext2fs_block_bitmap_loc(new_fs, i); input.inode_bitmap = ext2fs_inode_bitmap_loc(new_fs, i); input.inode_table = ext2fs_inode_table_loc(new_fs, i); input.blocks_count = sb->s_blocks_per_group; if (i == new_fs->group_desc_count-1) { input.blocks_count = ext2fs_blocks_count(new_fs->super) - sb->s_first_data_block - (i * sb->s_blocks_per_group); } input.reserved_blocks = (blk_t) (percent * input.blocks_count / 100.0); #if 0 printf("new block bitmap is at 0x%04x\n", input.block_bitmap); printf("new inode bitmap is at 0x%04x\n", input.inode_bitmap); printf("new inode table is at 0x%04x-0x%04x\n", input.inode_table, input.inode_table + new_fs->inode_blocks_per_group-1); printf("new group has %u blocks\n", input.blocks_count); printf("new group will reserve %d blocks\n", input.reserved_blocks); printf("new group has %d free blocks\n", ext2fs_bg_free_blocks_count(new_fs, i), printf("new group has %d free inodes (%d blocks)\n", ext2fs_bg_free_inodes_count(new_fs, i), new_fs->inode_blocks_per_group); printf("Adding group #%d\n", input.group); #endif if (use_old_ioctl && ioctl(fd, EXT2_IOC_GROUP_ADD, &input) == 0) continue; else use_old_ioctl = 0; input64.group = input.group; input64.block_bitmap = input.block_bitmap; input64.inode_bitmap = input.inode_bitmap; input64.inode_table = input.inode_table; input64.blocks_count = input.blocks_count; input64.reserved_blocks = input.reserved_blocks; input64.unused = input.unused; if (ioctl(fd, EXT4_IOC_GROUP_ADD, &input64) < 0) { com_err(program_name, errno, _("While trying to add group #%d"), input.group); exit(1); } } ext2fs_free(new_fs); close(fd); return 0; #else printf(_("Filesystem at %s is mounted on %s, and on-line resizing is " "not supported on this system.\n"), fs->device_name, mtpt); exit(1); #endif }
errcode_t online_resize_fs(ext2_filsys fs, const char *mtpt, blk_t *new_size, int flags) { struct ext2_new_group_input input; struct ext2_super_block *sb = fs->super; ext2_filsys new_fs; errcode_t retval; dgrp_t i; blk_t size; int fd, r_frac, overhead; printf(_("Filesystem at %s is mounted on %s; " "on-line resizing required\n"), fs->device_name, mtpt); if (*new_size < sb->s_blocks_count) { printf(_("On-line shrinking from %u to %u not supported.\n"), sb->s_blocks_count, *new_size); exit(1); } fd = open(mtpt, O_RDONLY); if (fd < 0) { com_err(program_name, errno, _("while trying to open mountpoint %s"), mtpt); exit(1); } size=sb->s_blocks_count; if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) { if (errno == EPERM) com_err(program_name, 0, _("Permission denied to resize filesystem")); else if (errno == ENOTTY) com_err(program_name, 0, _("Filesystem does not support online resizing")); else com_err(program_name, errno, _("While checking for on-line resizing support")); exit(1); } r_frac = ((100 * sb->s_r_blocks_count) + sb->s_blocks_count-1) / sb->s_blocks_count; retval = ext2fs_read_bitmaps(fs); if (retval) return retval; retval = ext2fs_dup_handle(fs, &new_fs); if (retval) return retval; retval = adjust_fs_info(new_fs, fs, *new_size); if (retval) return retval; printf(_("Performing an on-line resize of %s to %d (%dk) blocks.\n"), fs->device_name, *new_size, fs->blocksize / 1024); size = fs->group_desc_count * sb->s_blocks_per_group + sb->s_first_data_block; if (size > *new_size) size = *new_size; if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) { com_err(program_name, errno, _("While trying to extend the last group")); exit(1); } for (i = fs->group_desc_count; i < new_fs->group_desc_count; i++) { overhead = (int) (2 + new_fs->inode_blocks_per_group); if (ext2fs_bg_has_super(new_fs, new_fs->group_desc_count - 1)) overhead += 1 + new_fs->desc_blocks + new_fs->super->s_reserved_gdt_blocks; input.group = i; input.block_bitmap = new_fs->group_desc[i].bg_block_bitmap; input.inode_bitmap = new_fs->group_desc[i].bg_inode_bitmap; input.inode_table = new_fs->group_desc[i].bg_inode_table; input.blocks_count = sb->s_blocks_per_group; if (i == new_fs->group_desc_count-1) { input.blocks_count = new_fs->super->s_blocks_count - sb->s_first_data_block - (i * sb->s_blocks_per_group); } input.reserved_blocks = input.blocks_count * r_frac / 100; #if 0 printf("new block bitmap is at 0x%04x\n", input.block_bitmap); printf("new inode bitmap is at 0x%04x\n", input.inode_bitmap); printf("new inode table is at 0x%04x-0x%04x\n", input.inode_table, input.inode_table + new_fs->inode_blocks_per_group-1); printf("new group has %d blocks\n", input.blocks_count); printf("new group will reserve %d blocks\n", input.reserved_blocks); printf("new group has %d free blocks\n", new_fs->group_desc[i].bg_free_blocks_count); printf("new group has %d free inodes (%d blocks)\n", new_fs->group_desc[i].bg_free_inodes_count, new_fs->inode_blocks_per_group); printf("Adding group #%d\n", input.group); #endif if (ioctl(fd, EXT2_IOC_GROUP_ADD, &input) < 0) { com_err(program_name, errno, _("While trying to add group #%d"), input.group); exit(1); } } ext2fs_free(new_fs); close(fd); return 0; }