/* * This code assumes that the reserved blocks have already been marked in-use * during ext2fs_initialize(), so that they are not allocated for other * uses before we can add them to the resize inode (which has to come * after the creation of the inode table). */ errcode_t ext2fs_create_resize_inode(ext2_filsys fs) { errcode_t retval, retval2; struct ext2_super_block *sb; struct ext2_inode inode; __u32 *dindir_buf, *gdt_buf; unsigned long long apb, inode_size; /* FIXME-64 - can't deal with extents */ blk_t dindir_blk, rsv_off, gdt_off, gdt_blk; int dindir_dirty = 0, inode_dirty = 0, sb_blk = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); sb = fs->super; retval = ext2fs_get_array(2, fs->blocksize, &dindir_buf); if (retval) return retval; gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize); retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); if (retval) goto out_free; /* * File systems with a blocksize of 1024 and bigalloc have * sb->s_first_data_block of 0; yet the superblock is still at * block #1. We compensate for it here. */ sb_blk = sb->s_first_data_block; if (fs->blocksize == 1024 && sb_blk == 0) sb_blk = 1; /* Maximum possible file size (we donly use the dindirect blocks) */ apb = EXT2_ADDR_PER_BLOCK(sb); if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) { #ifdef RES_GDT_DEBUG printf("reading GDT dindir %u\n", dindir_blk); #endif retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf); if (retval) goto out_inode; } else { blk_t goal = sb_blk + fs->desc_blocks + sb->s_reserved_gdt_blocks + 2 + fs->inode_blocks_per_group; retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk); if (retval) goto out_free; inode.i_mode = LINUX_S_IFREG | 0600; inode.i_links_count = 1; inode.i_block[EXT2_DIND_BLOCK] = dindir_blk; ext2fs_iblk_set(fs, &inode, 1); memset(dindir_buf, 0, fs->blocksize); #ifdef RES_GDT_DEBUG printf("allocated GDT dindir %u\n", dindir_blk); #endif dindir_dirty = inode_dirty = 1; inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS; inode_size *= fs->blocksize; retval = ext2fs_inode_size_set(fs, &inode, inode_size); if (retval) goto out_free; inode.i_ctime = fs->now ? fs->now : time(0); } for (rsv_off = 0, gdt_off = fs->desc_blocks, gdt_blk = sb_blk + 1 + fs->desc_blocks; rsv_off < sb->s_reserved_gdt_blocks; rsv_off++, gdt_off++, gdt_blk++) { unsigned int three = 1, five = 5, seven = 7; unsigned int grp, last = 0; int gdt_dirty = 0; gdt_off %= apb; if (!dindir_buf[gdt_off]) { /* FIXME XXX XXX blk_t new_blk; retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk); if (retval) goto out_free; if (new_blk != gdt_blk) { // XXX free block retval = -1; // XXX } */ gdt_dirty = dindir_dirty = inode_dirty = 1; memset(gdt_buf, 0, fs->blocksize); dindir_buf[gdt_off] = gdt_blk; ext2fs_iblk_add_blocks(fs, &inode, 1); #ifdef RES_GDT_DEBUG printf("added primary GDT block %u at %u[%u]\n", gdt_blk, dindir_blk, gdt_off); #endif } else if (dindir_buf[gdt_off] == gdt_blk) { #ifdef RES_GDT_DEBUG printf("reading primary GDT block %u\n", gdt_blk); #endif retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf); if (retval) goto out_dindir; } else { #ifdef RES_GDT_DEBUG printf("bad primary GDT %u != %u at %u[%u]\n", dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off); #endif retval = EXT2_ET_RESIZE_INODE_CORRUPT; goto out_dindir; } while ((grp = list_backups(fs, &three, &five, &seven)) < fs->group_desc_count) { blk_t expect = gdt_blk + grp * sb->s_blocks_per_group; if (!gdt_buf[last]) { #ifdef RES_GDT_DEBUG printf("added backup GDT %u grp %u@%u[%u]\n", expect, grp, gdt_blk, last); #endif gdt_buf[last] = expect; ext2fs_iblk_add_blocks(fs, &inode, 1); gdt_dirty = inode_dirty = 1; } else if (gdt_buf[last] != expect) { #ifdef RES_GDT_DEBUG printf("bad backup GDT %u != %u at %u[%u]\n", gdt_buf[last], expect, gdt_blk, last); #endif retval = EXT2_ET_RESIZE_INODE_CORRUPT; goto out_dindir; } last++; } if (gdt_dirty) { #ifdef RES_GDT_DEBUG printf("writing primary GDT block %u\n", gdt_blk); #endif retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf); if (retval) goto out_dindir; } } out_dindir: if (dindir_dirty) { retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf); if (!retval) retval = retval2; } out_inode: #ifdef RES_GDT_DEBUG printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks, inode.i_size); #endif if (inode_dirty) { inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0); retval2 = ext2fs_write_new_inode(fs, EXT2_RESIZE_INO, &inode); if (!retval) retval = retval2; } out_free: ext2fs_free_mem(&dindir_buf); return retval; }
/* * Common code for mount and mountroot. */ static int ext2_mountfs(struct vnode *devvp, struct mount *mp) { struct ext2mount *ump; struct buf *bp; struct m_ext2fs *fs; struct ext2fs *es; struct cdev *dev = devvp->v_rdev; struct g_consumer *cp; struct bufobj *bo; struct csum *sump; int error; int ronly; int i, size; int32_t *lp; ronly = vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0); /* XXX: use VOP_ACESS to check FS perms */ DROP_GIANT(); g_topology_lock(); error = g_vfs_open(devvp, &cp, "ext2fs", ronly ? 0 : 1); g_topology_unlock(); PICKUP_GIANT(); VOP_UNLOCK(devvp, 0); if (error) return (error); /* XXX: should we check for some sectorsize or 512 instead? */ if (((SBSIZE % cp->provider->sectorsize) != 0) || (SBSIZE < cp->provider->sectorsize)) { DROP_GIANT(); g_topology_lock(); g_vfs_close(cp); g_topology_unlock(); PICKUP_GIANT(); return (EINVAL); } bo = &devvp->v_bufobj; bo->bo_private = cp; bo->bo_ops = g_vfs_bufops; if (devvp->v_rdev->si_iosize_max != 0) mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max; if (mp->mnt_iosize_max > MAXPHYS) mp->mnt_iosize_max = MAXPHYS; bp = NULL; ump = NULL; if ((error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) != 0) goto out; es = (struct ext2fs *)bp->b_data; if (ext2_check_sb_compat(es, dev, ronly) != 0) { error = EINVAL; /* XXX needs translation */ goto out; } if ((es->e2fs_state & E2FS_ISCLEAN) == 0 || (es->e2fs_state & E2FS_ERRORS)) { if (ronly || (mp->mnt_flag & MNT_FORCE)) { printf( "WARNING: Filesystem was not properly dismounted\n"); } else { printf( "WARNING: R/W mount denied. Filesystem is not clean - run fsck\n"); error = EPERM; goto out; } } ump = malloc(sizeof(*ump), M_EXT2MNT, M_WAITOK | M_ZERO); /* * I don't know whether this is the right strategy. Note that * we dynamically allocate both an ext2_sb_info and an ext2_super_block * while Linux keeps the super block in a locked buffer. */ ump->um_e2fs = malloc(sizeof(struct m_ext2fs), M_EXT2MNT, M_WAITOK); ump->um_e2fs->e2fs = malloc(sizeof(struct ext2fs), M_EXT2MNT, M_WAITOK); mtx_init(EXT2_MTX(ump), "EXT2FS", "EXT2FS Lock", MTX_DEF); bcopy(es, ump->um_e2fs->e2fs, (u_int)sizeof(struct ext2fs)); if ((error = compute_sb_data(devvp, ump->um_e2fs->e2fs, ump->um_e2fs))) goto out; /* * Calculate the maximum contiguous blocks and size of cluster summary * array. In FFS this is done by newfs; however, the superblock * in ext2fs doesn't have these variables, so we can calculate * them here. */ ump->um_e2fs->e2fs_maxcontig = MAX(1, MAXPHYS / ump->um_e2fs->e2fs_bsize); if (ump->um_e2fs->e2fs_maxcontig > 0) ump->um_e2fs->e2fs_contigsumsize = MIN(ump->um_e2fs->e2fs_maxcontig, EXT2_MAXCONTIG); else ump->um_e2fs->e2fs_contigsumsize = 0; if (ump->um_e2fs->e2fs_contigsumsize > 0) { size = ump->um_e2fs->e2fs_gcount * sizeof(int32_t); ump->um_e2fs->e2fs_maxcluster = malloc(size, M_EXT2MNT, M_WAITOK); size = ump->um_e2fs->e2fs_gcount * sizeof(struct csum); ump->um_e2fs->e2fs_clustersum = malloc(size, M_EXT2MNT, M_WAITOK); lp = ump->um_e2fs->e2fs_maxcluster; sump = ump->um_e2fs->e2fs_clustersum; for (i = 0; i < ump->um_e2fs->e2fs_gcount; i++, sump++) { *lp++ = ump->um_e2fs->e2fs_contigsumsize; sump->cs_init = 0; sump->cs_sum = malloc((ump->um_e2fs->e2fs_contigsumsize + 1) * sizeof(int32_t), M_EXT2MNT, M_WAITOK | M_ZERO); } } brelse(bp); bp = NULL; fs = ump->um_e2fs; fs->e2fs_ronly = ronly; /* ronly is set according to mnt_flags */ /* * If the fs is not mounted read-only, make sure the super block is * always written back on a sync(). */ fs->e2fs_wasvalid = fs->e2fs->e2fs_state & E2FS_ISCLEAN ? 1 : 0; if (ronly == 0) { fs->e2fs_fmod = 1; /* mark it modified */ fs->e2fs->e2fs_state &= ~E2FS_ISCLEAN; /* set fs invalid */ } mp->mnt_data = ump; mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; mp->mnt_maxsymlinklen = EXT2_MAXSYMLINKLEN; MNT_ILOCK(mp); mp->mnt_flag |= MNT_LOCAL; MNT_IUNLOCK(mp); ump->um_mountp = mp; ump->um_dev = dev; ump->um_devvp = devvp; ump->um_bo = &devvp->v_bufobj; ump->um_cp = cp; /* * Setting those two parameters allowed us to use * ufs_bmap w/o changse! */ ump->um_nindir = EXT2_ADDR_PER_BLOCK(fs); ump->um_bptrtodb = fs->e2fs->e2fs_log_bsize + 1; ump->um_seqinc = EXT2_FRAGS_PER_BLOCK(fs); if (ronly == 0) ext2_sbupdate(ump, MNT_WAIT); /* * Initialize filesystem stat information in mount struct. */ MNT_ILOCK(mp); mp->mnt_kern_flag |= MNTK_MPSAFE | MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED; MNT_IUNLOCK(mp); return (0); out: if (bp) brelse(bp); if (cp != NULL) { DROP_GIANT(); g_topology_lock(); g_vfs_close(cp); g_topology_unlock(); PICKUP_GIANT(); } if (ump) { mtx_destroy(EXT2_MTX(ump)); free(ump->um_e2fs->e2fs_gd, M_EXT2MNT); free(ump->um_e2fs->e2fs_contigdirs, M_EXT2MNT); free(ump->um_e2fs->e2fs, M_EXT2MNT); free(ump->um_e2fs, M_EXT2MNT); free(ump, M_EXT2MNT); mp->mnt_data = NULL; } return (error); }
struct super_block * ext2_read_super (struct super_block * sb, void * data, int silent) { struct buffer_head * bh; struct ext2_sb_info * sbi = EXT2_SB(sb); struct ext2_super_block * es; unsigned long sb_block = 1; unsigned short resuid = EXT2_DEF_RESUID; unsigned short resgid = EXT2_DEF_RESGID; unsigned long block; unsigned long logic_sb_block = 1; unsigned long offset = 0; kdev_t dev = sb->s_dev; int blocksize = BLOCK_SIZE; int db_count; int i, j; /* * See what the current blocksize for the device is, and * use that as the blocksize. Otherwise (or if the blocksize * is smaller than the default) use the default. * This is important for devices that have a hardware * sectorsize that is larger than the default. */ blocksize = get_hardsect_size(dev); if(blocksize < BLOCK_SIZE ) blocksize = BLOCK_SIZE; sb->u.ext2_sb.s_mount_opt = 0; if (!parse_options ((char *) data, &sb_block, &resuid, &resgid, &sb->u.ext2_sb.s_mount_opt)) { return NULL; } if (set_blocksize(dev, blocksize) < 0) { printk ("EXT2-fs: unable to set blocksize %d\n", blocksize); return NULL; } sb->s_blocksize = blocksize; /* * If the superblock doesn't start on a sector boundary, * calculate the offset. FIXME(eric) this doesn't make sense * that we would have to do this. */ if (blocksize != BLOCK_SIZE) { logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize; offset = (sb_block*BLOCK_SIZE) % blocksize; } if (!(bh = sb_bread(sb, logic_sb_block))) { printk ("EXT2-fs: unable to read superblock\n"); return NULL; } /* * Note: s_es must be initialized as soon as possible because * some ext2 macro-instructions depend on its value */ es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sb->u.ext2_sb.s_es = es; sb->s_magic = le16_to_cpu(es->s_magic); if (sb->s_magic != EXT2_SUPER_MAGIC) { if (!silent) printk ("VFS: Can't find ext2 filesystem on dev %s.\n", bdevname(dev)); goto failed_mount; } if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV && (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_INCOMPAT_FEATURE(sb, ~0U))) printk("EXT2-fs warning: feature flags set on rev 0 fs, " "running e2fsck is recommended\n"); /* * Check feature flags regardless of the revision level, since we * previously didn't change the revision level when setting the flags, * so there is a chance incompat flags are set on a rev 0 filesystem. */ if ((i = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP))) { printk("EXT2-fs: %s: couldn't mount because of " "unsupported optional features (%x).\n", bdevname(dev), i); goto failed_mount; } if (!(sb->s_flags & MS_RDONLY) && (i = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){ printk("EXT2-fs: %s: couldn't mount RDWR because of " "unsupported optional features (%x).\n", bdevname(dev), i); goto failed_mount; } if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) ext2_warning(sb, __FUNCTION__, "mounting ext3 filesystem as ext2\n"); sb->s_blocksize_bits = le32_to_cpu(EXT2_SB(sb)->s_es->s_log_block_size) + 10; sb->s_blocksize = 1 << sb->s_blocksize_bits; sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits); /* If the blocksize doesn't match, re-read the thing.. */ if (sb->s_blocksize != blocksize) { blocksize = sb->s_blocksize; brelse(bh); if (set_blocksize(dev, blocksize) < 0) { printk(KERN_ERR "EXT2-fs: blocksize too small for device.\n"); return NULL; } logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize; offset = (sb_block*BLOCK_SIZE) % blocksize; bh = sb_bread(sb, logic_sb_block); if(!bh) { printk("EXT2-fs: Couldn't read superblock on " "2nd try.\n"); goto failed_mount; } es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sb->u.ext2_sb.s_es = es; if (es->s_magic != le16_to_cpu(EXT2_SUPER_MAGIC)) { printk ("EXT2-fs: Magic mismatch, very weird !\n"); goto failed_mount; } } if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) { sbi->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; sbi->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; } else { sbi->s_inode_size = le16_to_cpu(es->s_inode_size); sbi->s_first_ino = le32_to_cpu(es->s_first_ino); if ((sbi->s_inode_size < EXT2_GOOD_OLD_INODE_SIZE) || (sbi->s_inode_size & (sbi->s_inode_size - 1)) || (sbi->s_inode_size > blocksize)) { printk ("EXT2-fs: unsupported inode size: %d\n", sbi->s_inode_size); goto failed_mount; } } sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE << le32_to_cpu(es->s_log_frag_size); if (sb->u.ext2_sb.s_frag_size) sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize / sb->u.ext2_sb.s_frag_size; else sb->s_magic = 0; sb->u.ext2_sb.s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group); sb->u.ext2_sb.s_frags_per_group = le32_to_cpu(es->s_frags_per_group); sb->u.ext2_sb.s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group); sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb); sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group / sb->u.ext2_sb.s_inodes_per_block; sb->u.ext2_sb.s_desc_per_block = sb->s_blocksize / sizeof (struct ext2_group_desc); sb->u.ext2_sb.s_sbh = bh; if (resuid != EXT2_DEF_RESUID) sb->u.ext2_sb.s_resuid = resuid; else sb->u.ext2_sb.s_resuid = le16_to_cpu(es->s_def_resuid); if (resgid != EXT2_DEF_RESGID) sb->u.ext2_sb.s_resgid = resgid; else sb->u.ext2_sb.s_resgid = le16_to_cpu(es->s_def_resgid); sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state); sb->u.ext2_sb.s_addr_per_block_bits = log2 (EXT2_ADDR_PER_BLOCK(sb)); sb->u.ext2_sb.s_desc_per_block_bits = log2 (EXT2_DESC_PER_BLOCK(sb)); if (sb->s_magic != EXT2_SUPER_MAGIC) { if (!silent) printk ("VFS: Can't find an ext2 filesystem on dev " "%s.\n", bdevname(dev)); goto failed_mount; } if (sb->s_blocksize != bh->b_size) { if (!silent) printk ("VFS: Unsupported blocksize on dev " "%s.\n", bdevname(dev)); goto failed_mount; } if (sb->s_blocksize != sb->u.ext2_sb.s_frag_size) { printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n", sb->u.ext2_sb.s_frag_size, sb->s_blocksize); goto failed_mount; } if (sb->u.ext2_sb.s_blocks_per_group > sb->s_blocksize * 8) { printk ("EXT2-fs: #blocks per group too big: %lu\n", sb->u.ext2_sb.s_blocks_per_group); goto failed_mount; } if (sb->u.ext2_sb.s_frags_per_group > sb->s_blocksize * 8) { printk ("EXT2-fs: #fragments per group too big: %lu\n", sb->u.ext2_sb.s_frags_per_group); goto failed_mount; } if (sb->u.ext2_sb.s_inodes_per_group > sb->s_blocksize * 8) { printk ("EXT2-fs: #inodes per group too big: %lu\n", sb->u.ext2_sb.s_inodes_per_group); goto failed_mount; } sb->u.ext2_sb.s_groups_count = (le32_to_cpu(es->s_blocks_count) - le32_to_cpu(es->s_first_data_block) + EXT2_BLOCKS_PER_GROUP(sb) - 1) / EXT2_BLOCKS_PER_GROUP(sb); db_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / EXT2_DESC_PER_BLOCK(sb); sb->u.ext2_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL); if (sb->u.ext2_sb.s_group_desc == NULL) { printk ("EXT2-fs: not enough memory\n"); goto failed_mount; } for (i = 0; i < db_count; i++) { block = descriptor_loc(sb, logic_sb_block, i); sbi->s_group_desc[i] = sb_bread(sb, block); if (!sbi->s_group_desc[i]) { for (j = 0; j < i; j++) brelse (sbi->s_group_desc[j]); kfree(sbi->s_group_desc); printk ("EXT2-fs: unable to read group descriptors\n"); goto failed_mount; } } if (!ext2_check_descriptors (sb)) { printk ("EXT2-fs: group descriptors corrupted!\n"); db_count = i; goto failed_mount2; } for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) { sb->u.ext2_sb.s_inode_bitmap_number[i] = 0; sb->u.ext2_sb.s_inode_bitmap[i] = NULL; sb->u.ext2_sb.s_block_bitmap_number[i] = 0; sb->u.ext2_sb.s_block_bitmap[i] = NULL; } sb->u.ext2_sb.s_loaded_inode_bitmaps = 0; sb->u.ext2_sb.s_loaded_block_bitmaps = 0; sb->u.ext2_sb.s_gdb_count = db_count; /* * set up enough so that it can read an inode */ sb->s_op = &ext2_sops; sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO)); if (!sb->s_root || !S_ISDIR(sb->s_root->d_inode->i_mode) || !sb->s_root->d_inode->i_blocks || !sb->s_root->d_inode->i_size) { if (sb->s_root) { dput(sb->s_root); sb->s_root = NULL; printk(KERN_ERR "EXT2-fs: corrupt root inode, run e2fsck\n"); } else printk(KERN_ERR "EXT2-fs: get root inode failed\n"); goto failed_mount2; } ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY); return sb; failed_mount2: for (i = 0; i < db_count; i++) brelse(sb->u.ext2_sb.s_group_desc[i]); kfree(sb->u.ext2_sb.s_group_desc); failed_mount: brelse(bh); return NULL; }
static int ext2_fill_super(struct super_block *sb, void *data, int silent) { struct buffer_head * bh; struct ext2_sb_info * sbi; struct ext2_super_block * es; struct inode *root; unsigned long block; unsigned long sb_block = get_sb_block(&data); unsigned long logic_sb_block; unsigned long offset = 0; unsigned long def_mount_opts; long ret = -EINVAL; int blocksize = BLOCK_SIZE; int db_count; int i, j; __le32 features; int err; err = -ENOMEM; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) goto failed_unlock; sbi->s_blockgroup_lock = kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL); if (!sbi->s_blockgroup_lock) { kfree(sbi); goto failed_unlock; } sb->s_fs_info = sbi; sbi->s_sb_block = sb_block; spin_lock_init(&sbi->s_lock); /* * See what the current blocksize for the device is, and * use that as the blocksize. Otherwise (or if the blocksize * is smaller than the default) use the default. * This is important for devices that have a hardware * sectorsize that is larger than the default. */ blocksize = sb_min_blocksize(sb, BLOCK_SIZE); if (!blocksize) { ext2_msg(sb, KERN_ERR, "error: unable to set blocksize"); goto failed_sbi; } /* * If the superblock doesn't start on a hardware sector boundary, * calculate the offset. */ if (blocksize != BLOCK_SIZE) { logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize; offset = (sb_block*BLOCK_SIZE) % blocksize; } else { logic_sb_block = sb_block; } if (!(bh = sb_bread(sb, logic_sb_block))) { ext2_msg(sb, KERN_ERR, "error: unable to read superblock"); goto failed_sbi; } /* * Note: s_es must be initialized as soon as possible because * some ext2 macro-instructions depend on its value */ es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sbi->s_es = es; sb->s_magic = le16_to_cpu(es->s_magic); if (sb->s_magic != EXT2_SUPER_MAGIC) goto cantfind_ext2; /* Set defaults before we parse the mount options */ def_mount_opts = le32_to_cpu(es->s_default_mount_opts); if (def_mount_opts & EXT2_DEFM_DEBUG) set_opt(sbi->s_mount_opt, DEBUG); if (def_mount_opts & EXT2_DEFM_BSDGROUPS) set_opt(sbi->s_mount_opt, GRPID); if (def_mount_opts & EXT2_DEFM_UID16) set_opt(sbi->s_mount_opt, NO_UID32); #ifdef CONFIG_EXT2_FS_XATTR if (def_mount_opts & EXT2_DEFM_XATTR_USER) set_opt(sbi->s_mount_opt, XATTR_USER); #endif #ifdef CONFIG_EXT2_FS_POSIX_ACL if (def_mount_opts & EXT2_DEFM_ACL) set_opt(sbi->s_mount_opt, POSIX_ACL); #endif if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC) set_opt(sbi->s_mount_opt, ERRORS_PANIC); else if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_CONTINUE) set_opt(sbi->s_mount_opt, ERRORS_CONT); else set_opt(sbi->s_mount_opt, ERRORS_RO); sbi->s_resuid = le16_to_cpu(es->s_def_resuid); sbi->s_resgid = le16_to_cpu(es->s_def_resgid); set_opt(sbi->s_mount_opt, RESERVATION); if (!parse_options((char *) data, sb)) goto failed_mount; sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | ((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); ext2_xip_verify_sb(sb); /* see if bdev supports xip, unset EXT2_MOUNT_XIP if not */ if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV && (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_INCOMPAT_FEATURE(sb, ~0U))) ext2_msg(sb, KERN_WARNING, "warning: feature flags set on rev 0 fs, " "running e2fsck is recommended"); /* * Check feature flags regardless of the revision level, since we * previously didn't change the revision level when setting the flags, * so there is a chance incompat flags are set on a rev 0 filesystem. */ features = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP); if (features) { ext2_msg(sb, KERN_ERR, "error: couldn't mount because of " "unsupported optional features (%x)", le32_to_cpu(features)); goto failed_mount; } if (!(sb->s_flags & MS_RDONLY) && (features = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){ ext2_msg(sb, KERN_ERR, "error: couldn't mount RDWR because of " "unsupported optional features (%x)", le32_to_cpu(features)); goto failed_mount; } blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size); if (ext2_use_xip(sb) && blocksize != PAGE_SIZE) { if (!silent) ext2_msg(sb, KERN_ERR, "error: unsupported blocksize for xip"); goto failed_mount; } /* If the blocksize doesn't match, re-read the thing.. */ if (sb->s_blocksize != blocksize) { brelse(bh); if (!sb_set_blocksize(sb, blocksize)) { ext2_msg(sb, KERN_ERR, "error: bad blocksize %d", blocksize); goto failed_sbi; } logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize; offset = (sb_block*BLOCK_SIZE) % blocksize; bh = sb_bread(sb, logic_sb_block); if(!bh) { ext2_msg(sb, KERN_ERR, "error: couldn't read" "superblock on 2nd try"); goto failed_sbi; } es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sbi->s_es = es; if (es->s_magic != cpu_to_le16(EXT2_SUPER_MAGIC)) { ext2_msg(sb, KERN_ERR, "error: magic mismatch"); goto failed_mount; } } sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits); sb->s_max_links = EXT2_LINK_MAX; if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) { sbi->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; sbi->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; } else { sbi->s_inode_size = le16_to_cpu(es->s_inode_size); sbi->s_first_ino = le32_to_cpu(es->s_first_ino); if ((sbi->s_inode_size < EXT2_GOOD_OLD_INODE_SIZE) || !is_power_of_2(sbi->s_inode_size) || (sbi->s_inode_size > blocksize)) { ext2_msg(sb, KERN_ERR, "error: unsupported inode size: %d", sbi->s_inode_size); goto failed_mount; } } sbi->s_frag_size = EXT2_MIN_FRAG_SIZE << le32_to_cpu(es->s_log_frag_size); if (sbi->s_frag_size == 0) goto cantfind_ext2; sbi->s_frags_per_block = sb->s_blocksize / sbi->s_frag_size; sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group); sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group); sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group); if (EXT2_INODE_SIZE(sb) == 0) goto cantfind_ext2; sbi->s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb); if (sbi->s_inodes_per_block == 0 || sbi->s_inodes_per_group == 0) goto cantfind_ext2; sbi->s_itb_per_group = sbi->s_inodes_per_group / sbi->s_inodes_per_block; sbi->s_desc_per_block = sb->s_blocksize / sizeof (struct ext2_group_desc); sbi->s_sbh = bh; sbi->s_mount_state = le16_to_cpu(es->s_state); sbi->s_addr_per_block_bits = ilog2 (EXT2_ADDR_PER_BLOCK(sb)); sbi->s_desc_per_block_bits = ilog2 (EXT2_DESC_PER_BLOCK(sb)); if (sb->s_magic != EXT2_SUPER_MAGIC) goto cantfind_ext2; if (sb->s_blocksize != bh->b_size) { if (!silent) ext2_msg(sb, KERN_ERR, "error: unsupported blocksize"); goto failed_mount; } if (sb->s_blocksize != sbi->s_frag_size) { ext2_msg(sb, KERN_ERR, "error: fragsize %lu != blocksize %lu" "(not supported yet)", sbi->s_frag_size, sb->s_blocksize); goto failed_mount; } if (sbi->s_blocks_per_group > sb->s_blocksize * 8) { ext2_msg(sb, KERN_ERR, "error: #blocks per group too big: %lu", sbi->s_blocks_per_group); goto failed_mount; } if (sbi->s_frags_per_group > sb->s_blocksize * 8) { ext2_msg(sb, KERN_ERR, "error: #fragments per group too big: %lu", sbi->s_frags_per_group); goto failed_mount; } if (sbi->s_inodes_per_group > sb->s_blocksize * 8) { ext2_msg(sb, KERN_ERR, "error: #inodes per group too big: %lu", sbi->s_inodes_per_group); goto failed_mount; } if (EXT2_BLOCKS_PER_GROUP(sb) == 0) goto cantfind_ext2; sbi->s_groups_count = ((le32_to_cpu(es->s_blocks_count) - le32_to_cpu(es->s_first_data_block) - 1) / EXT2_BLOCKS_PER_GROUP(sb)) + 1; db_count = (sbi->s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / EXT2_DESC_PER_BLOCK(sb); sbi->s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL); if (sbi->s_group_desc == NULL) { ext2_msg(sb, KERN_ERR, "error: not enough memory"); goto failed_mount; } bgl_lock_init(sbi->s_blockgroup_lock); sbi->s_debts = kcalloc(sbi->s_groups_count, sizeof(*sbi->s_debts), GFP_KERNEL); if (!sbi->s_debts) { ext2_msg(sb, KERN_ERR, "error: not enough memory"); goto failed_mount_group_desc; } for (i = 0; i < db_count; i++) { block = descriptor_loc(sb, logic_sb_block, i); sbi->s_group_desc[i] = sb_bread(sb, block); if (!sbi->s_group_desc[i]) { for (j = 0; j < i; j++) brelse (sbi->s_group_desc[j]); ext2_msg(sb, KERN_ERR, "error: unable to read group descriptors"); goto failed_mount_group_desc; } } if (!ext2_check_descriptors (sb)) { ext2_msg(sb, KERN_ERR, "group descriptors corrupted"); goto failed_mount2; } sbi->s_gdb_count = db_count; get_random_bytes(&sbi->s_next_generation, sizeof(u32)); spin_lock_init(&sbi->s_next_gen_lock); /* per fileystem reservation list head & lock */ spin_lock_init(&sbi->s_rsv_window_lock); sbi->s_rsv_window_root = RB_ROOT; /* * Add a single, static dummy reservation to the start of the * reservation window list --- it gives us a placeholder for * append-at-start-of-list which makes the allocation logic * _much_ simpler. */ sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED; sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED; sbi->s_rsv_window_head.rsv_alloc_hit = 0; sbi->s_rsv_window_head.rsv_goal_size = 0; ext2_rsv_window_add(sb, &sbi->s_rsv_window_head); err = percpu_counter_init(&sbi->s_freeblocks_counter, ext2_count_free_blocks(sb)); if (!err) { err = percpu_counter_init(&sbi->s_freeinodes_counter, ext2_count_free_inodes(sb)); } if (!err) { err = percpu_counter_init(&sbi->s_dirs_counter, ext2_count_dirs(sb)); } if (err) { ext2_msg(sb, KERN_ERR, "error: insufficient memory"); goto failed_mount3; } /* * set up enough so that it can read an inode */ sb->s_op = &ext2_sops; sb->s_export_op = &ext2_export_ops; sb->s_xattr = ext2_xattr_handlers; #ifdef CONFIG_QUOTA sb->dq_op = &dquot_operations; sb->s_qcop = &dquot_quotactl_ops; #endif root = ext2_iget(sb, EXT2_ROOT_INO); if (IS_ERR(root)) { ret = PTR_ERR(root); goto failed_mount3; } if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { iput(root); ext2_msg(sb, KERN_ERR, "error: corrupt root inode, run e2fsck"); goto failed_mount3; } sb->s_root = d_make_root(root); if (!sb->s_root) { ext2_msg(sb, KERN_ERR, "error: get root inode failed"); ret = -ENOMEM; goto failed_mount3; } if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) ext2_msg(sb, KERN_WARNING, "warning: mounting ext3 filesystem as ext2"); if (ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY)) sb->s_flags |= MS_RDONLY; ext2_write_super(sb); return 0; cantfind_ext2: if (!silent) ext2_msg(sb, KERN_ERR, "error: can't find an ext2 filesystem on dev %s.", sb->s_id); goto failed_mount; failed_mount3: percpu_counter_destroy(&sbi->s_freeblocks_counter); percpu_counter_destroy(&sbi->s_freeinodes_counter); percpu_counter_destroy(&sbi->s_dirs_counter); failed_mount2: for (i = 0; i < db_count; i++) brelse(sbi->s_group_desc[i]); failed_mount_group_desc: kfree(sbi->s_group_desc); kfree(sbi->s_debts); failed_mount: brelse(bh); failed_sbi: sb->s_fs_info = NULL; kfree(sbi->s_blockgroup_lock); kfree(sbi); failed_unlock: return ret; }
static int ext2_fill_super(struct super_block *sb, void *data, int silent) { struct buffer_head * bh; struct ext2_sb_info * sbi; struct ext2_super_block * es; struct inode *root; unsigned long block; unsigned long sb_block = get_sb_block(&data); unsigned long logic_sb_block; unsigned long offset = 0; unsigned long def_mount_opts; int blocksize = BLOCK_SIZE; int db_count; int i, j; __le32 features; sbi = kmalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) return -ENOMEM; sb->s_fs_info = sbi; memset(sbi, 0, sizeof(*sbi)); /* * See what the current blocksize for the device is, and * use that as the blocksize. Otherwise (or if the blocksize * is smaller than the default) use the default. * This is important for devices that have a hardware * sectorsize that is larger than the default. */ blocksize = sb_min_blocksize(sb, BLOCK_SIZE); if (!blocksize) { printk ("EXT2-fs: unable to set blocksize\n"); goto failed_sbi; } /* * If the superblock doesn't start on a hardware sector boundary, * calculate the offset. */ if (blocksize != BLOCK_SIZE) { logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize; offset = (sb_block*BLOCK_SIZE) % blocksize; } else { logic_sb_block = sb_block; } if (!(bh = sb_bread(sb, logic_sb_block))) { printk ("EXT2-fs: unable to read superblock\n"); goto failed_sbi; } /* * Note: s_es must be initialized as soon as possible because * some ext2 macro-instructions depend on its value */ es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sbi->s_es = es; sb->s_magic = le16_to_cpu(es->s_magic); if (sb->s_magic != EXT2_SUPER_MAGIC) goto cantfind_ext2; /* Set defaults before we parse the mount options */ def_mount_opts = le32_to_cpu(es->s_default_mount_opts); if (def_mount_opts & EXT2_DEFM_DEBUG) set_opt(sbi->s_mount_opt, DEBUG); if (def_mount_opts & EXT2_DEFM_BSDGROUPS) set_opt(sbi->s_mount_opt, GRPID); if (def_mount_opts & EXT2_DEFM_UID16) set_opt(sbi->s_mount_opt, NO_UID32); if (def_mount_opts & EXT2_DEFM_XATTR_USER) set_opt(sbi->s_mount_opt, XATTR_USER); if (def_mount_opts & EXT2_DEFM_ACL) set_opt(sbi->s_mount_opt, POSIX_ACL); if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC) set_opt(sbi->s_mount_opt, ERRORS_PANIC); else if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_RO) set_opt(sbi->s_mount_opt, ERRORS_RO); sbi->s_resuid = le16_to_cpu(es->s_def_resuid); sbi->s_resgid = le16_to_cpu(es->s_def_resgid); if (!parse_options ((char *) data, sbi)) goto failed_mount; sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | ((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV && (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_INCOMPAT_FEATURE(sb, ~0U))) printk("EXT2-fs warning: feature flags set on rev 0 fs, " "running e2fsck is recommended\n"); /* * Check feature flags regardless of the revision level, since we * previously didn't change the revision level when setting the flags, * so there is a chance incompat flags are set on a rev 0 filesystem. */ features = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP); if (features) { printk("EXT2-fs: %s: couldn't mount because of " "unsupported optional features (%x).\n", sb->s_id, le32_to_cpu(features)); goto failed_mount; } if (!(sb->s_flags & MS_RDONLY) && (features = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){ printk("EXT2-fs: %s: couldn't mount RDWR because of " "unsupported optional features (%x).\n", sb->s_id, le32_to_cpu(features)); goto failed_mount; } blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size); /* If the blocksize doesn't match, re-read the thing.. */ if (sb->s_blocksize != blocksize) { brelse(bh); if (!sb_set_blocksize(sb, blocksize)) { printk(KERN_ERR "EXT2-fs: blocksize too small for device.\n"); goto failed_sbi; } logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize; offset = (sb_block*BLOCK_SIZE) % blocksize; bh = sb_bread(sb, logic_sb_block); if(!bh) { printk("EXT2-fs: Couldn't read superblock on " "2nd try.\n"); goto failed_sbi; } es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sbi->s_es = es; if (es->s_magic != cpu_to_le16(EXT2_SUPER_MAGIC)) { printk ("EXT2-fs: Magic mismatch, very weird !\n"); goto failed_mount; } } sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits); if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) { sbi->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; sbi->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; } else { sbi->s_inode_size = le16_to_cpu(es->s_inode_size); sbi->s_first_ino = le32_to_cpu(es->s_first_ino); if ((sbi->s_inode_size < EXT2_GOOD_OLD_INODE_SIZE) || (sbi->s_inode_size & (sbi->s_inode_size - 1)) || (sbi->s_inode_size > blocksize)) { printk ("EXT2-fs: unsupported inode size: %d\n", sbi->s_inode_size); goto failed_mount; } } sbi->s_frag_size = EXT2_MIN_FRAG_SIZE << le32_to_cpu(es->s_log_frag_size); if (sbi->s_frag_size == 0) goto cantfind_ext2; sbi->s_frags_per_block = sb->s_blocksize / sbi->s_frag_size; sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group); sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group); sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group); if (EXT2_INODE_SIZE(sb) == 0) goto cantfind_ext2; sbi->s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb); if (sbi->s_inodes_per_block == 0) goto cantfind_ext2; sbi->s_itb_per_group = sbi->s_inodes_per_group / sbi->s_inodes_per_block; sbi->s_desc_per_block = sb->s_blocksize / sizeof (struct ext2_group_desc); sbi->s_sbh = bh; sbi->s_mount_state = le16_to_cpu(es->s_state); sbi->s_addr_per_block_bits = log2 (EXT2_ADDR_PER_BLOCK(sb)); sbi->s_desc_per_block_bits = log2 (EXT2_DESC_PER_BLOCK(sb)); if (sb->s_magic != EXT2_SUPER_MAGIC) goto cantfind_ext2; if (sb->s_blocksize != bh->b_size) { if (!silent) printk ("VFS: Unsupported blocksize on dev " "%s.\n", sb->s_id); goto failed_mount; } if (sb->s_blocksize != sbi->s_frag_size) { printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n", sbi->s_frag_size, sb->s_blocksize); goto failed_mount; } if (sbi->s_blocks_per_group > sb->s_blocksize * 8) { printk ("EXT2-fs: #blocks per group too big: %lu\n", sbi->s_blocks_per_group); goto failed_mount; } if (sbi->s_frags_per_group > sb->s_blocksize * 8) { printk ("EXT2-fs: #fragments per group too big: %lu\n", sbi->s_frags_per_group); goto failed_mount; } if (sbi->s_inodes_per_group > sb->s_blocksize * 8) { printk ("EXT2-fs: #inodes per group too big: %lu\n", sbi->s_inodes_per_group); goto failed_mount; } if (EXT2_BLOCKS_PER_GROUP(sb) == 0) goto cantfind_ext2; sbi->s_groups_count = (le32_to_cpu(es->s_blocks_count) - le32_to_cpu(es->s_first_data_block) + EXT2_BLOCKS_PER_GROUP(sb) - 1) / EXT2_BLOCKS_PER_GROUP(sb); db_count = (sbi->s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / EXT2_DESC_PER_BLOCK(sb); sbi->s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL); if (sbi->s_group_desc == NULL) { printk ("EXT2-fs: not enough memory\n"); goto failed_mount; } percpu_counter_init(&sbi->s_freeblocks_counter); percpu_counter_init(&sbi->s_freeinodes_counter); percpu_counter_init(&sbi->s_dirs_counter); bgl_lock_init(&sbi->s_blockgroup_lock); sbi->s_debts = kmalloc(sbi->s_groups_count * sizeof(*sbi->s_debts), GFP_KERNEL); if (!sbi->s_debts) { printk ("EXT2-fs: not enough memory\n"); goto failed_mount_group_desc; } memset(sbi->s_debts, 0, sbi->s_groups_count * sizeof(*sbi->s_debts)); for (i = 0; i < db_count; i++) { block = descriptor_loc(sb, logic_sb_block, i); sbi->s_group_desc[i] = sb_bread(sb, block); if (!sbi->s_group_desc[i]) { for (j = 0; j < i; j++) brelse (sbi->s_group_desc[j]); printk ("EXT2-fs: unable to read group descriptors\n"); goto failed_mount_group_desc; } } if (!ext2_check_descriptors (sb)) { printk ("EXT2-fs: group descriptors corrupted!\n"); db_count = i; goto failed_mount2; } sbi->s_gdb_count = db_count; get_random_bytes(&sbi->s_next_generation, sizeof(u32)); spin_lock_init(&sbi->s_next_gen_lock); /* * set up enough so that it can read an inode */ sb->s_op = &ext2_sops; sb->s_export_op = &ext2_export_ops; sb->s_xattr = ext2_xattr_handlers; root = iget(sb, EXT2_ROOT_INO); sb->s_root = d_alloc_root(root); if (!sb->s_root) { iput(root); printk(KERN_ERR "EXT2-fs: get root inode failed\n"); goto failed_mount2; } if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { dput(sb->s_root); sb->s_root = NULL; printk(KERN_ERR "EXT2-fs: corrupt root inode, run e2fsck\n"); goto failed_mount2; } if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) ext2_warning(sb, __FUNCTION__, "mounting ext3 filesystem as ext2\n"); ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY); percpu_counter_mod(&sbi->s_freeblocks_counter, ext2_count_free_blocks(sb)); percpu_counter_mod(&sbi->s_freeinodes_counter, ext2_count_free_inodes(sb)); percpu_counter_mod(&sbi->s_dirs_counter, ext2_count_dirs(sb)); return 0; cantfind_ext2: if (!silent) printk("VFS: Can't find an ext2 filesystem on dev %s.\n", sb->s_id); goto failed_mount; failed_mount2: for (i = 0; i < db_count; i++) brelse(sbi->s_group_desc[i]); failed_mount_group_desc: kfree(sbi->s_group_desc); kfree(sbi->s_debts); failed_mount: brelse(bh); failed_sbi: sb->s_fs_info = NULL; kfree(sbi); return -EINVAL; }
/* * This code assumes that the reserved blocks have already been marked in-use * during ext2fs_initialize(), so that they are not allocated for other * uses before we can add them to the resize inode (which has to come * after the creation of the inode table). */ errcode_t ext2fs_create_resize_inode(ext2_filsys fs) { errcode_t retval, retval2; struct ext2_super_block *sb; struct ext2_inode inode; __u32 *dindir_buf, *gdt_buf; int rsv_add; unsigned long long apb, inode_size; blk_t dindir_blk, rsv_off, gdt_off, gdt_blk; int dindir_dirty = 0, inode_dirty = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); sb = fs->super; retval = ext2fs_get_mem(2 * fs->blocksize, (void *)&dindir_buf); if (retval) goto out_free; gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize); retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); if (retval) goto out_free; /* Maximum possible file size (we donly use the dindirect blocks) */ apb = EXT2_ADDR_PER_BLOCK(sb); rsv_add = fs->blocksize / 512; if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) { #ifdef RES_GDT_DEBUG printf("reading GDT dindir %u\n", dindir_blk); #endif retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf); if (retval) goto out_inode; } else { blk_t goal = 3 + sb->s_reserved_gdt_blocks + fs->desc_blocks + fs->inode_blocks_per_group; retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk); if (retval) goto out_free; inode.i_mode = LINUX_S_IFREG | 0600; inode.i_links_count = 1; inode.i_block[EXT2_DIND_BLOCK] = dindir_blk; inode.i_blocks = rsv_add; memset(dindir_buf, 0, fs->blocksize); #ifdef RES_GDT_DEBUG printf("allocated GDT dindir %u\n", dindir_blk); #endif dindir_dirty = inode_dirty = 1; inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS; inode_size *= fs->blocksize; inode.i_size = inode_size & 0xFFFFFFFF; inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF; if (inode.i_size_high) { sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE; } inode.i_ctime = time(NULL); } for (rsv_off = 0, gdt_off = fs->desc_blocks, gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks; rsv_off < sb->s_reserved_gdt_blocks; rsv_off++, gdt_off++, gdt_blk++) { unsigned int three = 1, five = 5, seven = 7; unsigned int grp, last = 0; int gdt_dirty = 0; gdt_off %= apb; if (!dindir_buf[gdt_off]) { /* FIXME XXX XXX blk_t new_blk; retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk); if (retval) goto out_free; if (new_blk != gdt_blk) { // XXX free block retval = -1; // XXX } */ gdt_dirty = dindir_dirty = inode_dirty = 1; memset(gdt_buf, 0, fs->blocksize); dindir_buf[gdt_off] = gdt_blk; inode.i_blocks += rsv_add; #ifdef RES_GDT_DEBUG printf("added primary GDT block %u at %u[%u]\n", gdt_blk, dindir_blk, gdt_off); #endif } else if (dindir_buf[gdt_off] == gdt_blk) { #ifdef RES_GDT_DEBUG printf("reading primary GDT block %u\n", gdt_blk); #endif retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf); if (retval) goto out_dindir; } else { #ifdef RES_GDT_DEBUG printf("bad primary GDT %u != %u at %u[%u]\n", dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off); #endif retval = EXT2_ET_RESIZE_INODE_CORRUPT; goto out_dindir; } while ((grp = list_backups(fs, &three, &five, &seven)) < fs->group_desc_count) { blk_t expect = gdt_blk + grp * sb->s_blocks_per_group; if (!gdt_buf[last]) { #ifdef RES_GDT_DEBUG printf("added backup GDT %u grp %u@%u[%u]\n", expect, grp, gdt_blk, last); #endif gdt_buf[last] = expect; inode.i_blocks += rsv_add; gdt_dirty = inode_dirty = 1; } else if (gdt_buf[last] != expect) { #ifdef RES_GDT_DEBUG printf("bad backup GDT %u != %u at %u[%u]\n", gdt_buf[last], expect, gdt_blk, last); #endif retval = EXT2_ET_RESIZE_INODE_CORRUPT; goto out_dindir; } last++; } if (gdt_dirty) { #ifdef RES_GDT_DEBUG printf("writing primary GDT block %u\n", gdt_blk); #endif retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf); if (retval) goto out_dindir; } } out_dindir: if (dindir_dirty) { retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf); if (!retval) retval = retval2; } out_inode: #ifdef RES_GDT_DEBUG printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks, inode.i_size); #endif if (inode_dirty) { inode.i_atime = inode.i_mtime = time(NULL); retval2 = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode); if (!retval) retval = retval2; } out_free: ext2fs_free_mem((void *)&dindir_buf); return retval; }
struct super_block * ext2_read_super (struct super_block * sb, void * data, int silent) { struct buffer_head * bh; struct ext2_super_block * es; unsigned long sb_block = 1; unsigned short resuid = EXT2_DEF_RESUID; unsigned short resgid = EXT2_DEF_RESGID; unsigned long logic_sb_block = 1; kdev_t dev = sb->s_dev; int db_count; int i, j; 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->s_dev = 0; return NULL; } MOD_INC_USE_COUNT; lock_super (sb); set_blocksize (dev, BLOCK_SIZE); if (!(bh = bread (dev, sb_block, BLOCK_SIZE))) { sb->s_dev = 0; unlock_super (sb); printk ("EXT2-fs: unable to read superblock\n"); MOD_DEC_USE_COUNT; return NULL; } /* * Note: s_es must be initialized s_es as soon as possible because * some ext2 macro-instructions depend on its value */ es = (struct ext2_super_block *) bh->b_data; sb->u.ext2_sb.s_es = es; sb->s_magic = es->s_magic; if (sb->s_magic != EXT2_SUPER_MAGIC) { if (!silent) printk ("VFS: Can't find an ext2 filesystem on dev " "%s.\n", kdevname(dev)); failed_mount: sb->s_dev = 0; unlock_super (sb); if (bh) brelse(bh); MOD_DEC_USE_COUNT; return NULL; } if (es->s_rev_level > EXT2_GOOD_OLD_REV) { if (es->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPP) { printk("EXT2-fs: %s: couldn't mount because of " "unsupported optional features.\n", kdevname(dev)); goto failed_mount; } if (!(sb->s_flags & MS_RDONLY) && (es->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPP)) { printk("EXT2-fs: %s: couldn't mount RDWR because of " "unsupported optional features.\n", kdevname(dev)); goto failed_mount; } } sb->s_blocksize_bits = sb->u.ext2_sb.s_es->s_log_block_size + 10; sb->s_blocksize = 1 << sb->s_blocksize_bits; if (sb->s_blocksize != BLOCK_SIZE && (sb->s_blocksize == 1024 || sb->s_blocksize == 2048 || sb->s_blocksize == 4096)) { unsigned long offset; brelse (bh); set_blocksize (dev, sb->s_blocksize); logic_sb_block = (sb_block*BLOCK_SIZE) / sb->s_blocksize; offset = (sb_block*BLOCK_SIZE) % sb->s_blocksize; bh = bread (dev, logic_sb_block, sb->s_blocksize); if(!bh) { printk("EXT2-fs: Couldn't read superblock on " "2nd try.\n"); goto failed_mount; } es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sb->u.ext2_sb.s_es = es; if (es->s_magic != EXT2_SUPER_MAGIC) { printk ("EXT2-fs: Magic mismatch, very weird !\n"); goto failed_mount; } } if (es->s_rev_level == EXT2_GOOD_OLD_REV) { sb->u.ext2_sb.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; sb->u.ext2_sb.s_first_ino = EXT2_GOOD_OLD_FIRST_INO; } else { sb->u.ext2_sb.s_inode_size = es->s_inode_size; sb->u.ext2_sb.s_first_ino = es->s_first_ino; if (sb->u.ext2_sb.s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) { printk ("EXT2-fs: unsupported inode size: %d\n", sb->u.ext2_sb.s_inode_size); goto failed_mount; } } sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE << es->s_log_frag_size; if (sb->u.ext2_sb.s_frag_size) sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize / sb->u.ext2_sb.s_frag_size; else sb->s_magic = 0; sb->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group; sb->u.ext2_sb.s_frags_per_group = es->s_frags_per_group; sb->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group; sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb); sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group / sb->u.ext2_sb.s_inodes_per_block; sb->u.ext2_sb.s_desc_per_block = sb->s_blocksize / sizeof (struct ext2_group_desc); sb->u.ext2_sb.s_sbh = bh; if (resuid != EXT2_DEF_RESUID) sb->u.ext2_sb.s_resuid = resuid; else sb->u.ext2_sb.s_resuid = es->s_def_resuid; if (resgid != EXT2_DEF_RESGID) sb->u.ext2_sb.s_resgid = resgid; else sb->u.ext2_sb.s_resgid = es->s_def_resgid; sb->u.ext2_sb.s_mount_state = es->s_state; sb->u.ext2_sb.s_rename_lock = 0; sb->u.ext2_sb.s_rename_wait = NULL; sb->u.ext2_sb.s_addr_per_block_bits = log2 (EXT2_ADDR_PER_BLOCK(sb)); sb->u.ext2_sb.s_desc_per_block_bits = log2 (EXT2_DESC_PER_BLOCK(sb)); if (sb->s_magic != EXT2_SUPER_MAGIC) { if (!silent) printk ("VFS: Can't find an ext2 filesystem on dev " "%s.\n", kdevname(dev)); goto failed_mount; } if (sb->s_blocksize != bh->b_size) { if (!silent) printk ("VFS: Unsupported blocksize on dev " "%s.\n", kdevname(dev)); goto failed_mount; } if (sb->s_blocksize != sb->u.ext2_sb.s_frag_size) { printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n", sb->u.ext2_sb.s_frag_size, sb->s_blocksize); goto failed_mount; } if (sb->u.ext2_sb.s_blocks_per_group > sb->s_blocksize * 8) { printk ("EXT2-fs: #blocks per group too big: %lu\n", sb->u.ext2_sb.s_blocks_per_group); goto failed_mount; } if (sb->u.ext2_sb.s_frags_per_group > sb->s_blocksize * 8) { printk ("EXT2-fs: #fragments per group too big: %lu\n", sb->u.ext2_sb.s_frags_per_group); goto failed_mount; } if (sb->u.ext2_sb.s_inodes_per_group > sb->s_blocksize * 8) { printk ("EXT2-fs: #inodes per group too big: %lu\n", sb->u.ext2_sb.s_inodes_per_group); goto failed_mount; } sb->u.ext2_sb.s_groups_count = (es->s_blocks_count - es->s_first_data_block + EXT2_BLOCKS_PER_GROUP(sb) - 1) / EXT2_BLOCKS_PER_GROUP(sb); db_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / EXT2_DESC_PER_BLOCK(sb); sb->u.ext2_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL); if (sb->u.ext2_sb.s_group_desc == NULL) { printk ("EXT2-fs: not enough memory\n"); goto failed_mount; } for (i = 0; i < db_count; i++) { sb->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1, sb->s_blocksize); if (!sb->u.ext2_sb.s_group_desc[i]) { for (j = 0; j < i; j++) brelse (sb->u.ext2_sb.s_group_desc[j]); kfree_s (sb->u.ext2_sb.s_group_desc, db_count * sizeof (struct buffer_head *)); printk ("EXT2-fs: unable to read group descriptors\n"); goto failed_mount; } } if (!ext2_check_descriptors (sb)) { for (j = 0; j < db_count; j++) brelse (sb->u.ext2_sb.s_group_desc[j]); kfree_s (sb->u.ext2_sb.s_group_desc, db_count * sizeof (struct buffer_head *)); printk ("EXT2-fs: group descriptors corrupted !\n"); goto failed_mount; } for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) { sb->u.ext2_sb.s_inode_bitmap_number[i] = 0; sb->u.ext2_sb.s_inode_bitmap[i] = NULL; sb->u.ext2_sb.s_block_bitmap_number[i] = 0; sb->u.ext2_sb.s_block_bitmap[i] = NULL; } sb->u.ext2_sb.s_loaded_inode_bitmaps = 0; sb->u.ext2_sb.s_loaded_block_bitmaps = 0; sb->u.ext2_sb.s_db_per_group = db_count; unlock_super (sb); /* * set up enough so that it can read an inode */ sb->s_dev = dev; sb->s_op = &ext2_sops; if (!(sb->s_mounted = iget (sb, EXT2_ROOT_INO))) { sb->s_dev = 0; for (i = 0; i < db_count; i++) if (sb->u.ext2_sb.s_group_desc[i]) brelse (sb->u.ext2_sb.s_group_desc[i]); kfree_s (sb->u.ext2_sb.s_group_desc, db_count * sizeof (struct buffer_head *)); brelse (bh); printk ("EXT2-fs: get root inode failed\n"); MOD_DEC_USE_COUNT; return NULL; } ext2_setup_super (sb, es); return sb; }
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 fsw_status_t fsw_ext2_volume_mount(struct fsw_ext2_volume *vol) { fsw_status_t status; void *buffer; fsw_u32 blocksize; fsw_u32 groupcnt, groupno, gdesc_per_block, gdesc_bno, gdesc_index; struct ext2_group_desc *gdesc; int i; struct fsw_string s; // allocate memory to keep the superblock around status = fsw_alloc(sizeof(struct ext2_super_block), &vol->sb); if (status) return status; // read the superblock into its buffer fsw_set_blocksize(vol, EXT2_SUPERBLOCK_BLOCKSIZE, EXT2_SUPERBLOCK_BLOCKSIZE); status = fsw_block_get(vol, EXT2_SUPERBLOCK_BLOCKNO, 0, &buffer); if (status) return status; fsw_memcpy(vol->sb, buffer, sizeof(struct ext2_super_block)); fsw_block_release(vol, EXT2_SUPERBLOCK_BLOCKNO, buffer); // check the superblock if (vol->sb->s_magic != EXT2_SUPER_MAGIC) return FSW_UNSUPPORTED; if (vol->sb->s_rev_level != EXT2_GOOD_OLD_REV && vol->sb->s_rev_level != EXT2_DYNAMIC_REV) return FSW_UNSUPPORTED; if (vol->sb->s_rev_level == EXT2_DYNAMIC_REV && (vol->sb->s_feature_incompat & ~(EXT2_FEATURE_INCOMPAT_FILETYPE | EXT3_FEATURE_INCOMPAT_RECOVER))) return FSW_UNSUPPORTED; /* if (vol->sb->s_rev_level == EXT2_DYNAMIC_REV && (vol->sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER)) Print(L"Ext2 WARNING: This ext3 file system needs recovery, trying to use it anyway.\n"); */ // set real blocksize blocksize = EXT2_BLOCK_SIZE(vol->sb); fsw_set_blocksize(vol, blocksize, blocksize); // get other info from superblock vol->ind_bcnt = EXT2_ADDR_PER_BLOCK(vol->sb); vol->dind_bcnt = vol->ind_bcnt * vol->ind_bcnt; vol->inode_size = EXT2_INODE_SIZE(vol->sb); for (i = 0; i < 16; i++) if (vol->sb->s_volume_name[i] == 0) break; s.type = FSW_STRING_TYPE_ISO88591; s.size = s.len = i; s.data = vol->sb->s_volume_name; status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s); if (status) return status; // read the group descriptors to get inode table offsets groupcnt = ((vol->sb->s_inodes_count - 2) / vol->sb->s_inodes_per_group) + 1; gdesc_per_block = (vol->g.phys_blocksize / sizeof(struct ext2_group_desc)); status = fsw_alloc(sizeof(fsw_u32) * groupcnt, &vol->inotab_bno); if (status) return status; for (groupno = 0; groupno < groupcnt; groupno++) { // get the block group descriptor gdesc_bno = (vol->sb->s_first_data_block + 1) + groupno / gdesc_per_block; gdesc_index = groupno % gdesc_per_block; status = fsw_block_get(vol, gdesc_bno, 1, (void **)&buffer); if (status) return status; gdesc = ((struct ext2_group_desc *)(buffer)) + gdesc_index; vol->inotab_bno[groupno] = gdesc->bg_inode_table; fsw_block_release(vol, gdesc_bno, buffer); } // setup the root dnode status = fsw_dnode_create_root(vol, EXT2_ROOT_INO, &vol->g.root); if (status) return status; FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext2_volume_mount: success, blocksize %d\n"), blocksize)); return FSW_SUCCESS; }
static void parse_extended_opts(struct ext2_super_block *param, const char *opts) { char *buf, *token, *next, *p, *arg, *badopt = ""; int len; int r_usage = 0; len = strlen(opts); buf = malloc(len+1); if (!buf) { fprintf(stderr, _("Couldn't allocate memory to parse options!\n")); exit(1); } strcpy(buf, opts); for (token = buf; token && *token; token = next) { p = strchr(token, ','); next = 0; if (p) { *p = 0; next = p+1; } arg = strchr(token, '='); if (arg) { *arg = 0; arg++; } if (strcmp(token, "stride") == 0) { if (!arg) { r_usage++; badopt = token; continue; } param->s_raid_stride = strtoul(arg, &p, 0); if (*p || (param->s_raid_stride == 0)) { fprintf(stderr, _("Invalid stride parameter: %s\n"), arg); r_usage++; continue; } } else if (strcmp(token, "stripe-width") == 0 || strcmp(token, "stripe_width") == 0) { if (!arg) { r_usage++; badopt = token; continue; } param->s_raid_stripe_width = strtoul(arg, &p, 0); if (*p || (param->s_raid_stripe_width == 0)) { fprintf(stderr, _("Invalid stripe-width parameter: %s\n"), arg); r_usage++; continue; } } else if (!strcmp(token, "resize")) { unsigned long resize, bpg, rsv_groups; unsigned long group_desc_count, desc_blocks; unsigned int gdpb, blocksize; int rsv_gdb; if (!arg) { r_usage++; badopt = token; continue; } resize = parse_num_blocks(arg, param->s_log_block_size); if (resize == 0) { fprintf(stderr, _("Invalid resize parameter: %s\n"), arg); r_usage++; continue; } if (resize <= param->s_blocks_count) { fprintf(stderr, _("The resize maximum must be greater " "than the filesystem size.\n")); r_usage++; continue; } blocksize = EXT2_BLOCK_SIZE(param); bpg = param->s_blocks_per_group; if (!bpg) bpg = blocksize * 8; gdpb = blocksize / sizeof(struct ext2_group_desc); group_desc_count = ext2fs_div_ceil(param->s_blocks_count, bpg); desc_blocks = (group_desc_count + gdpb - 1) / gdpb; rsv_groups = ext2fs_div_ceil(resize, bpg); rsv_gdb = ext2fs_div_ceil(rsv_groups, gdpb) - desc_blocks; if (rsv_gdb > (int) EXT2_ADDR_PER_BLOCK(param)) rsv_gdb = EXT2_ADDR_PER_BLOCK(param); if (rsv_gdb > 0) { if (param->s_rev_level == EXT2_GOOD_OLD_REV) { fprintf(stderr, _("On-line resizing not supported with revision 0 filesystems\n")); free(buf); exit(1); } param->s_feature_compat |= EXT2_FEATURE_COMPAT_RESIZE_INODE; param->s_reserved_gdt_blocks = rsv_gdb; } } else if (!strcmp(token, "test_fs")) { param->s_flags |= EXT2_FLAGS_TEST_FILESYS; } else { r_usage++; badopt = token; } } if (r_usage) { fprintf(stderr, _("\nBad option(s) specified: %s\n\n" "Extended options are separated by commas, " "and may take an argument which\n" "\tis set off by an equals ('=') sign.\n\n" "Valid extended options are:\n" "\tstride=<RAID per-disk data chunk in blocks>\n" "\tstripe-width=<RAID stride * data disks in blocks>\n" "\tresize=<resize maximum size in blocks>\n\n" "\ttest_fs\n"), badopt); free(buf); exit(1); } if (param->s_raid_stride && (param->s_raid_stripe_width % param->s_raid_stride) != 0) fprintf(stderr, _("\nWarning: RAID stripe-width %u not an even " "multiple of stride %u.\n\n"), param->s_raid_stripe_width, param->s_raid_stride); free(buf); }