Esempio n. 1
0
/*
 * Select the desired position for the next block in a file.
 *
 * we try to mimic what Remy does in inode_getblk/block_getblk
 *
 * we note: blocknr == 0 means that we're about to allocate either
 * a direct block or a pointer block at the first level of indirection
 * (In other words, stuff that will go in i_db[] or i_ib[])
 *
 * blocknr != 0 means that we're allocating a block that is none
 * of the above. Then, blocknr tells us the number of the block
 * that will hold the pointer
 */
e4fs_daddr_t
ext2_blkpref(struct inode *ip, e2fs_lbn_t lbn, int indx, e2fs_daddr_t *bap,
             e2fs_daddr_t blocknr)
{
    int	tmp;
    mtx_assert(EXT2_MTX(ip->i_ump), MA_OWNED);

    /* if the next block is actually what we thought it is,
       then set the goal to what we thought it should be
    */
    if (ip->i_next_alloc_block == lbn && ip->i_next_alloc_goal != 0)
        return ip->i_next_alloc_goal;

    /* now check whether we were provided with an array that basically
       tells us previous blocks to which we want to stay closeby
    */
    if (bap)
        for (tmp = indx - 1; tmp >= 0; tmp--)
            if (bap[tmp])
                return bap[tmp];

    /* else let's fall back to the blocknr, or, if there is none,
       follow the rule that a block should be allocated near its inode
    */
    return blocknr ? blocknr :
           (e2fs_daddr_t)(ip->i_block_group *
                          EXT2_BLOCKS_PER_GROUP(ip->i_e2fs)) +
           ip->i_e2fs->e2fs->e2fs_first_dblock;
}
Esempio n. 2
0
/*
 * Allocate a block in the filesystem.
 *
 * A preference may be optionally specified. If a preference is given
 * the following hierarchy is used to allocate a block:
 *   1) allocate the requested block.
 *   2) allocate a rotationally optimal block in the same cylinder.
 *   3) allocate a block in the same cylinder group.
 *   4) quadradically rehash into other cylinder groups, until an
 *        available block is located.
 * If no block preference is given the following hierarchy is used
 * to allocate a block:
 *   1) allocate a block in the cylinder group that contains the
 *        inode for the file.
 *   2) quadradically rehash into other cylinder groups, until an
 *        available block is located.
 */
int
ext2_alloc(struct inode *ip, daddr_t lbn, e4fs_daddr_t bpref, int size,
           struct ucred *cred, e4fs_daddr_t *bnp)
{
    struct m_ext2fs *fs;
    struct ext2mount *ump;
    int32_t bno;
    int cg;
    *bnp = 0;
    fs = ip->i_e2fs;
    ump = ip->i_ump;
    mtx_assert(EXT2_MTX(ump), MA_OWNED);
#ifdef INVARIANTS
    if ((u_int)size > fs->e2fs_bsize || blkoff(fs, size) != 0) {
        vn_printf(ip->i_devvp, "bsize = %lu, size = %d, fs = %s\n",
                  (long unsigned int)fs->e2fs_bsize, size, fs->e2fs_fsmnt);
        panic("ext2_alloc: bad size");
    }
    if (cred == NOCRED)
        panic("ext2_alloc: missing credential");
#endif /* INVARIANTS */
    if (size == fs->e2fs_bsize && fs->e2fs->e2fs_fbcount == 0)
        goto nospace;
    if (cred->cr_uid != 0 &&
            fs->e2fs->e2fs_fbcount < fs->e2fs->e2fs_rbcount)
        goto nospace;
    if (bpref >= fs->e2fs->e2fs_bcount)
        bpref = 0;
    if (bpref == 0)
        cg = ino_to_cg(fs, ip->i_number);
    else
        cg = dtog(fs, bpref);
    bno = (daddr_t)ext2_hashalloc(ip, cg, bpref, fs->e2fs_bsize,
                                  ext2_alloccg);
    if (bno > 0) {
        /* set next_alloc fields as done in block_getblk */
        ip->i_next_alloc_block = lbn;
        ip->i_next_alloc_goal = bno;

        ip->i_blocks += btodb(fs->e2fs_bsize);
        ip->i_flag |= IN_CHANGE | IN_UPDATE;
        *bnp = bno;
        return (0);
    }
nospace:
    EXT2_UNLOCK(ump);
    ext2_fserr(fs, cred->cr_uid, "filesystem full");
    uprintf("\n%s: write failed, filesystem is full\n", fs->e2fs_fsmnt);
    return (ENOSPC);
}
Esempio n. 3
0
/*
 * Implement the cylinder overflow algorithm.
 *
 * The policy implemented by this algorithm is:
 *   1) allocate the block in its requested cylinder group.
 *   2) quadradically rehash on the cylinder group number.
 *   3) brute force search for a free block.
 */
static u_long
ext2_hashalloc(struct inode *ip, int cg, long pref, int size,
               daddr_t (*allocator)(struct inode *, int, daddr_t, int))
{
    struct m_ext2fs *fs;
    ino_t result;
    int i, icg = cg;

    mtx_assert(EXT2_MTX(ip->i_ump), MA_OWNED);
    fs = ip->i_e2fs;
    /*
     * 1: preferred cylinder group
     */
    result = (*allocator)(ip, cg, pref, size);
    if (result)
        return (result);
    /*
     * 2: quadratic rehash
     */
    for (i = 1; i < fs->e2fs_gcount; i *= 2) {
        cg += i;
        if (cg >= fs->e2fs_gcount)
            cg -= fs->e2fs_gcount;
        result = (*allocator)(ip, cg, 0, size);
        if (result)
            return (result);
    }
    /*
     * 3: brute force search
     * Note that we start at i == 2, since 0 was checked initially,
     * and 1 is always checked in the quadratic rehash.
     */
    cg = (icg + 2) % fs->e2fs_gcount;
    for (i = 2; i < fs->e2fs_gcount; i++) {
        result = (*allocator)(ip, cg, 0, size);
        if (result)
            return (result);
        cg++;
        if (cg == fs->e2fs_gcount)
            cg = 0;
    }
    return (0);
}
Esempio n. 4
0
/*
 * 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);
}
Esempio n. 5
0
/*
 * Find a cylinder to place a directory.
 *
 * The policy implemented by this algorithm is to allocate a
 * directory inode in the same cylinder group as its parent
 * directory, but also to reserve space for its files inodes
 * and data. Restrict the number of directories which may be
 * allocated one after another in the same cylinder group
 * without intervening allocation of files.
 *
 * If we allocate a first level directory then force allocation
 * in another cylinder group.
 *
 */
static u_long
ext2_dirpref(struct inode *pip)
{
    struct m_ext2fs *fs;
    int cg, prefcg, cgsize;
    u_int avgifree, avgbfree, avgndir, curdirsize;
    u_int minifree, minbfree, maxndir;
    u_int mincg, minndir;
    u_int dirsize, maxcontigdirs;

    mtx_assert(EXT2_MTX(pip->i_ump), MA_OWNED);
    fs = pip->i_e2fs;

    avgifree = fs->e2fs->e2fs_ficount / fs->e2fs_gcount;
    avgbfree = fs->e2fs->e2fs_fbcount / fs->e2fs_gcount;
    avgndir  = fs->e2fs_total_dir / fs->e2fs_gcount;

    /*
     * Force allocation in another cg if creating a first level dir.
     */
    ASSERT_VOP_LOCKED(ITOV(pip), "ext2fs_dirpref");
    if (ITOV(pip)->v_vflag & VV_ROOT) {
        prefcg = arc4random() % fs->e2fs_gcount;
        mincg = prefcg;
        minndir = fs->e2fs_ipg;
        for (cg = prefcg; cg < fs->e2fs_gcount; cg++)
            if (fs->e2fs_gd[cg].ext2bgd_ndirs < minndir &&
                    fs->e2fs_gd[cg].ext2bgd_nifree >= avgifree &&
                    fs->e2fs_gd[cg].ext2bgd_nbfree >= avgbfree) {
                mincg = cg;
                minndir = fs->e2fs_gd[cg].ext2bgd_ndirs;
            }
        for (cg = 0; cg < prefcg; cg++)
            if (fs->e2fs_gd[cg].ext2bgd_ndirs < minndir &&
                    fs->e2fs_gd[cg].ext2bgd_nifree >= avgifree &&
                    fs->e2fs_gd[cg].ext2bgd_nbfree >= avgbfree) {
                mincg = cg;
                minndir = fs->e2fs_gd[cg].ext2bgd_ndirs;
            }

        return (mincg);
    }

    /*
     * Count various limits which used for
     * optimal allocation of a directory inode.
     */
    maxndir = min(avgndir + fs->e2fs_ipg / 16, fs->e2fs_ipg);
    minifree = avgifree - avgifree / 4;
    if (minifree < 1)
        minifree = 1;
    minbfree = avgbfree - avgbfree / 4;
    if (minbfree < 1)
        minbfree = 1;
    cgsize = fs->e2fs_fsize * fs->e2fs_fpg;
    dirsize = AVGDIRSIZE;
    curdirsize = avgndir ? (cgsize - avgbfree * fs->e2fs_bsize) / avgndir : 0;
    if (dirsize < curdirsize)
        dirsize = curdirsize;
    maxcontigdirs = min((avgbfree * fs->e2fs_bsize) / dirsize, 255);
    maxcontigdirs = min(maxcontigdirs, fs->e2fs_ipg / AFPDIR);
    if (maxcontigdirs == 0)
        maxcontigdirs = 1;

    /*
     * Limit number of dirs in one cg and reserve space for
     * regular files, but only if we have no deficit in
     * inodes or space.
     */
    prefcg = ino_to_cg(fs, pip->i_number);
    for (cg = prefcg; cg < fs->e2fs_gcount; cg++)
        if (fs->e2fs_gd[cg].ext2bgd_ndirs < maxndir &&
                fs->e2fs_gd[cg].ext2bgd_nifree >= minifree &&
                fs->e2fs_gd[cg].ext2bgd_nbfree >= minbfree) {
            if (fs->e2fs_contigdirs[cg] < maxcontigdirs)
                return (cg);
        }
    for (cg = 0; cg < prefcg; cg++)
        if (fs->e2fs_gd[cg].ext2bgd_ndirs < maxndir &&
                fs->e2fs_gd[cg].ext2bgd_nifree >= minifree &&
                fs->e2fs_gd[cg].ext2bgd_nbfree >= minbfree) {
            if (fs->e2fs_contigdirs[cg] < maxcontigdirs)
                return (cg);
        }
    /*
     * This is a backstop when we have deficit in space.
     */
    for (cg = prefcg; cg < fs->e2fs_gcount; cg++)
        if (fs->e2fs_gd[cg].ext2bgd_nifree >= avgifree)
            return (cg);
    for (cg = 0; cg < prefcg; cg++)
        if (fs->e2fs_gd[cg].ext2bgd_nifree >= avgifree)
            break;
    return (cg);
}