예제 #1
0
/*
 * Allocate a block in the file system.
 *
 * this takes the framework from ffs_alloc. To implement the
 * actual allocation, it calls ext2_new_block, the ported version
 * of the same Linux routine.
 *
 * we note that this is always called in connection with ext2_blkpref
 *
 * preallocation is done as Linux does it
 */
int
ext2_alloc(struct inode *ip, daddr_t lbn, daddr_t bpref, int size,
	   struct ucred *cred, daddr_t *bnp)
{
	struct ext2_sb_info *fs;
	daddr_t bno;
#if QUOTA
	int error;
#endif

	*bnp = 0;
	fs = ip->i_e2fs;
#if DIAGNOSTIC
	if ((u_int)size > fs->s_blocksize || blkoff(fs, size) != 0) {
		kprintf("dev = %s, bsize = %lu, size = %d, fs = %s\n",
		    devtoname(ip->i_dev), fs->s_blocksize, size, fs->fs_fsmnt);
		panic("ext2_alloc: bad size");
	}
	if (cred == NOCRED)
		panic("ext2_alloc: missing credential");
#endif /* DIAGNOSTIC */
	if (size == fs->s_blocksize && fs->s_es->s_free_blocks_count == 0)
		goto nospace;
	if (cred->cr_uid != 0 &&
		fs->s_es->s_free_blocks_count < fs->s_es->s_r_blocks_count)
		goto nospace;
#if QUOTA
	if ((error = ext2_chkdq(ip, (long)btodb(size), cred, 0)) != 0)
		return (error);
#endif
	if (bpref >= fs->s_es->s_blocks_count)
		bpref = 0;
	/* call the Linux code */
#ifdef EXT2_PREALLOCATE
	/* To have a preallocation hit, we must
	 * - have at least one block preallocated
	 * - and our preferred block must have that block number or one below
	 */
        if (ip->i_prealloc_count &&
            (bpref == ip->i_prealloc_block ||
             bpref + 1 == ip->i_prealloc_block))
        {
                bno = ip->i_prealloc_block++;
                ip->i_prealloc_count--;
                /* ext2_debug ("preallocation hit (%lu/%lu).\n",
                            ++alloc_hits, ++alloc_attempts); */

		/* Linux gets, clears, and releases the buffer at this
		   point - we don't have to that; we leave it to the caller
		 */
        } else {
                ext2_discard_prealloc (ip);
                /* ext2_debug ("preallocation miss (%lu/%lu).\n",
                            alloc_hits, ++alloc_attempts); */
                if (S_ISREG(ip->i_mode))
                        bno = ext2_new_block
                                (ITOV(ip)->v_mount, bpref,
                                 &ip->i_prealloc_count,
                                 &ip->i_prealloc_block);
                else
			bno = (daddr_t)ext2_new_block(ITOV(ip)->v_mount,
					bpref, 0, 0);
        }
#else
	bno = (daddr_t)ext2_new_block(ITOV(ip)->v_mount, bpref, 0, 0);
#endif

	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(size);
		ip->i_flag |= IN_CHANGE | IN_UPDATE;
		*bnp = bno;
		return (0);
	}
#if QUOTA
	/*
	 * Restore user's disk quota because allocation failed.
	 */
	ext2_chkdq(ip, (long)-btodb(size), cred, FORCE);
#endif
nospace:
	ext2_fserr(fs, cred->cr_uid, "file system full");
	uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt);
	return (ENOSPC);
}
예제 #2
0
/*
 * Truncate the inode oip to at most length size, freeing the
 * disk blocks.
 */
int
ext2_truncate(struct vnode *vp, off_t length, int flags, struct ucred *cred)
{
    struct vnode *ovp = vp;
    daddr_t lastblock;
    struct inode *oip;
    daddr_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR];
    daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];
    struct ext2_sb_info *fs;
    struct buf *bp;
    int offset, size, level;
    long count, nblocks, blocksreleased = 0;
    int i;
    int aflags, error, allerror;
    off_t osize;
    /*
    kprintf("ext2_truncate called %d to %d\n", VTOI(ovp)->i_number, length);
    */	/*
	 * negative file sizes will totally break the code below and
	 * are not meaningful anyways.
	 */
    if (length < 0)
        return EFBIG;

    oip = VTOI(ovp);
    if (ovp->v_type == VLNK &&
            oip->i_size < ovp->v_mount->mnt_maxsymlinklen) {
#if DIAGNOSTIC
        if (length != 0)
            panic("ext2_truncate: partial truncate of symlink");
#endif
        bzero((char *)&oip->i_shortlink, (u_int)oip->i_size);
        oip->i_size = 0;
        oip->i_flag |= IN_CHANGE | IN_UPDATE;
        return (EXT2_UPDATE(ovp, 1));
    }
    if (oip->i_size == length) {
        oip->i_flag |= IN_CHANGE | IN_UPDATE;
        return (EXT2_UPDATE(ovp, 0));
    }
#if QUOTA
    if ((error = ext2_getinoquota(oip)) != 0)
        return (error);
#endif
    fs = oip->i_e2fs;
    osize = oip->i_size;
    ext2_discard_prealloc(oip);
    /*
     * Lengthen the size of the file. We must ensure that the
     * last byte of the file is allocated. Since the smallest
     * value of osize is 0, length will be at least 1.
     */
    if (osize < length) {
        offset = blkoff(fs, length - 1);
        lbn = lblkno(fs, length - 1);
        aflags = B_CLRBUF;
        if (flags & IO_SYNC)
            aflags |= B_SYNC;
        vnode_pager_setsize(ovp, length);
        error = ext2_balloc(oip, lbn, offset + 1, cred, &bp, aflags);
        if (error) {
            vnode_pager_setsize(ovp, osize);
            return (error);
        }
        oip->i_size = length;
        if (aflags & IO_SYNC)
            bwrite(bp);
        else
            bawrite(bp);
        oip->i_flag |= IN_CHANGE | IN_UPDATE;
        return (EXT2_UPDATE(ovp, 1));
    }
    /*
     * Shorten the size of the file. If the file is not being
     * truncated to a block boundry, the contents of the
     * partial block following the end of the file must be
     * zero'ed in case it ever become accessable again because
     * of subsequent file growth.
     */
    /* I don't understand the comment above */
    offset = blkoff(fs, length);
    if (offset == 0) {
        oip->i_size = length;
    } else {
        lbn = lblkno(fs, length);
        aflags = B_CLRBUF;
        if (flags & IO_SYNC)
            aflags |= B_SYNC;
        error = ext2_balloc(oip, lbn, offset, cred, &bp, aflags);
        if (error)
            return (error);
        oip->i_size = length;
        size = blksize(fs, oip, lbn);
        bzero((char *)bp->b_data + offset, (u_int)(size - offset));
        allocbuf(bp, size);
        if (aflags & IO_SYNC)
            bwrite(bp);
        else
            bawrite(bp);
    }
    /*
     * Calculate index into inode's block list of
     * last direct and indirect blocks (if any)
     * which we want to keep.  Lastblock is -1 when
     * the file is truncated to 0.
     */
    lastblock = lblkno(fs, length + fs->s_blocksize - 1) - 1;
    lastiblock[SINGLE] = lastblock - NDADDR;
    lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);
    lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);
    nblocks = btodb(fs->s_blocksize);
    /*
     * Update file and block pointers on disk before we start freeing
     * blocks.  If we crash before free'ing blocks below, the blocks
     * will be returned to the free list.  lastiblock values are also
     * normalized to -1 for calls to ext2_indirtrunc below.
     */
    bcopy((caddr_t)&oip->i_db[0], (caddr_t)oldblks, sizeof oldblks);
    for (level = TRIPLE; level >= SINGLE; level--)
        if (lastiblock[level] < 0) {
            oip->i_ib[level] = 0;
            lastiblock[level] = -1;
        }
    for (i = NDADDR - 1; i > lastblock; i--)
        oip->i_db[i] = 0;
    oip->i_flag |= IN_CHANGE | IN_UPDATE;
    allerror = EXT2_UPDATE(ovp, 1);

    /*
     * Having written the new inode to disk, save its new configuration
     * and put back the old block pointers long enough to process them.
     * Note that we save the new block configuration so we can check it
     * when we are done.
     */
    bcopy((caddr_t)&oip->i_db[0], (caddr_t)newblks, sizeof newblks);
    bcopy((caddr_t)oldblks, (caddr_t)&oip->i_db[0], sizeof oldblks);
    oip->i_size = osize;
    error = vtruncbuf(ovp, length, (int)fs->s_blocksize);
    if (error && (allerror == 0))
        allerror = error;

    /*
     * Indirect blocks first.
     */
    indir_lbn[SINGLE] = -NDADDR;
    indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) - 1;
    indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1;
    for (level = TRIPLE; level >= SINGLE; level--) {
        bn = oip->i_ib[level];
        if (bn != 0) {
            error = ext2_indirtrunc(oip, indir_lbn[level],
                                    fsbtodoff(fs, bn), lastiblock[level], level, &count);
            if (error)
                allerror = error;
            blocksreleased += count;
            if (lastiblock[level] < 0) {
                oip->i_ib[level] = 0;
                ext2_blkfree(oip, bn, fs->s_frag_size);
                blocksreleased += nblocks;
            }
        }
        if (lastiblock[level] >= 0)
            goto done;
    }

    /*
     * All whole direct blocks or frags.
     */
    for (i = NDADDR - 1; i > lastblock; i--) {
        long bsize;

        bn = oip->i_db[i];
        if (bn == 0)
            continue;
        oip->i_db[i] = 0;
        bsize = blksize(fs, oip, i);
        ext2_blkfree(oip, bn, bsize);
        blocksreleased += btodb(bsize);
    }
    if (lastblock < 0)
        goto done;

    /*
     * Finally, look for a change in size of the
     * last direct block; release any frags.
     */
    bn = oip->i_db[lastblock];
    if (bn != 0) {
        long oldspace, newspace;

        /*
         * Calculate amount of space we're giving
         * back as old block size minus new block size.
         */
        oldspace = blksize(fs, oip, lastblock);
        oip->i_size = length;
        newspace = blksize(fs, oip, lastblock);
        if (newspace == 0)
            panic("itrunc: newspace");
        if (oldspace - newspace > 0) {
            /*
             * Block number of space to be free'd is
             * the old block # plus the number of frags
             * required for the storage we're keeping.
             */
            bn += numfrags(fs, newspace);
            ext2_blkfree(oip, bn, oldspace - newspace);
            blocksreleased += btodb(oldspace - newspace);
        }
    }
done:
#if DIAGNOSTIC
    for (level = SINGLE; level <= TRIPLE; level++)
        if (newblks[NDADDR + level] != oip->i_ib[level])
            panic("itrunc1");
    for (i = 0; i < NDADDR; i++)
        if (newblks[i] != oip->i_db[i])
            panic("itrunc2");
    if (length == 0 && (!RB_EMPTY(&ovp->v_rbdirty_tree) ||
                        !RB_EMPTY(&ovp->v_rbclean_tree)))
        panic("itrunc3");
#endif /* DIAGNOSTIC */
    /*
     * Put back the real size.
     */
    oip->i_size = length;
    oip->i_blocks -= blocksreleased;
    if (oip->i_blocks < 0)			/* sanity */
        oip->i_blocks = 0;
    oip->i_flag |= IN_CHANGE;
    vnode_pager_setsize(ovp, length);
#if QUOTA
    ext2_chkdq(oip, -blocksreleased, NOCRED, 0);
#endif
    return (allerror);
}