Esempio n. 1
0
int ext4_ext_migrate(struct inode *inode, struct file *filp,
                unsigned int cmd, unsigned long arg)
{
    handle_t *handle;
    int retval = 0, i;
    __le32 *i_data;
    ext4_lblk_t blk_count = 0;
    struct ext4_inode_info *ei;
    struct inode *tmp_inode = NULL;
    struct list_blocks_struct lb;
    unsigned long max_entries;

    if (!test_opt(inode->i_sb, EXTENTS))
        /*
         * if mounted with noextents we don't allow the migrate
         */
        return -EINVAL;

    if ((EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL))
        return -EINVAL;

    if (S_ISLNK(inode->i_mode) && inode->i_blocks == 0)
        /*
         * don't migrate fast symlink
         */
        return retval;

    handle = ext4_journal_start(inode,
                    EXT4_DATA_TRANS_BLOCKS(inode->i_sb) +
                    EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
                    2 * EXT4_QUOTA_INIT_BLOCKS(inode->i_sb)
                    + 1);
    if (IS_ERR(handle)) {
        retval = PTR_ERR(handle);
        goto err_out;
    }
    tmp_inode = ext4_new_inode(handle,
                inode->i_sb->s_root->d_inode,
                S_IFREG);
    if (IS_ERR(tmp_inode)) {
        retval = -ENOMEM;
        ext4_journal_stop(handle);
        tmp_inode = NULL;
        goto err_out;
    }
    i_size_write(tmp_inode, i_size_read(inode));
    /*
     * We don't want the inode to be reclaimed
     * if we got interrupted in between. We have
     * this tmp inode carrying reference to the
     * data blocks of the original file. We set
     * the i_nlink to zero at the last stage after
     * switching the original file to extent format
     */
    tmp_inode->i_nlink = 1;

    ext4_ext_tree_init(handle, tmp_inode);
    ext4_orphan_add(handle, tmp_inode);
    ext4_journal_stop(handle);

    /*
     * start with one credit accounted for
     * superblock modification.
     *
     * For the tmp_inode we already have commited the
     * trascation that created the inode. Later as and
     * when we add extents we extent the journal
     */
    /*
     * inode_mutex prevent write and truncate on the file. Read still goes
     * through. We take i_data_sem in ext4_ext_swap_inode_data before we
     * switch the inode format to prevent read.
     */
    mutex_lock(&(inode->i_mutex));
    /*
     * Even though we take i_mutex we can still cause block allocation
     * via mmap write to holes. If we have allocated new blocks we fail
     * migrate.  New block allocation will clear EXT4_EXT_MIGRATE flag.
     * The flag is updated with i_data_sem held to prevent racing with
     * block allocation.
     */
    down_read((&EXT4_I(inode)->i_data_sem));
    EXT4_I(inode)->i_flags = EXT4_I(inode)->i_flags | EXT4_EXT_MIGRATE;
    up_read((&EXT4_I(inode)->i_data_sem));

    handle = ext4_journal_start(inode, 1);

    ei = EXT4_I(inode);
    i_data = ei->i_data;
    memset(&lb, 0, sizeof(lb));

    /* 32 bit block address 4 bytes */
    max_entries = inode->i_sb->s_blocksize >> 2;
    for (i = 0; i < EXT4_NDIR_BLOCKS; i++, blk_count++) {
        if (i_data[i]) {
            retval = update_extent_range(handle, tmp_inode,
                        le32_to_cpu(i_data[i]),
                        blk_count, &lb);
            if (retval)
                goto err_out;
        }
    }
    if (i_data[EXT4_IND_BLOCK]) {
        retval = update_ind_extent_range(handle, tmp_inode,
                    le32_to_cpu(i_data[EXT4_IND_BLOCK]),
                    &blk_count, &lb);
            if (retval)
                goto err_out;
    } else
        blk_count +=  max_entries;
    if (i_data[EXT4_DIND_BLOCK]) {
        retval = update_dind_extent_range(handle, tmp_inode,
                    le32_to_cpu(i_data[EXT4_DIND_BLOCK]),
                    &blk_count, &lb);
            if (retval)
                goto err_out;
    } else
        blk_count += max_entries * max_entries;
    if (i_data[EXT4_TIND_BLOCK]) {
        retval = update_tind_extent_range(handle, tmp_inode,
                    le32_to_cpu(i_data[EXT4_TIND_BLOCK]),
                    &blk_count, &lb);
            if (retval)
                goto err_out;
    }
    /*
     * Build the last extent
     */
    retval = finish_range(handle, tmp_inode, &lb);
err_out:
    if (retval)
        /*
         * Failure case delete the extent information with the
         * tmp_inode
         */
        free_ext_block(handle, tmp_inode);
    else {
        retval = ext4_ext_swap_inode_data(handle, inode, tmp_inode);
        if (retval)
            /*
             * if we fail to swap inode data free the extent
             * details of the tmp inode
             */
            free_ext_block(handle, tmp_inode);
    }

    /* We mark the tmp_inode dirty via ext4_ext_tree_init. */
    if (ext4_journal_extend(handle, 1) != 0)
        ext4_journal_restart(handle, 1);

    /*
     * Mark the tmp_inode as of size zero
     */
    i_size_write(tmp_inode, 0);

    /*
     * set the  i_blocks count to zero
     * so that the ext4_delete_inode does the
     * right job
     *
     * We don't need to take the i_lock because
     * the inode is not visible to user space.
     */
    tmp_inode->i_blocks = 0;

    /* Reset the extent details */
    ext4_ext_tree_init(handle, tmp_inode);

    /*
     * Set the i_nlink to zero so that
     * generic_drop_inode really deletes the
     * inode
     */
    tmp_inode->i_nlink = 0;

    ext4_journal_stop(handle);
    mutex_unlock(&(inode->i_mutex));

    if (tmp_inode)
        iput(tmp_inode);

    return retval;
}
Esempio n. 2
0
static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
{
	struct inode *inode = file_inode(filp);
	struct super_block *sb = inode->i_sb;
	struct ext4_inode_info *ei = EXT4_I(inode);
	int err, rc;
	handle_t *handle;
	kprojid_t kprojid;
	struct ext4_iloc iloc;
	struct ext4_inode *raw_inode;
	struct dquot *transfer_to[MAXQUOTAS] = { };

	if (!ext4_has_feature_project(sb)) {
		if (projid != EXT4_DEF_PROJID)
			return -EOPNOTSUPP;
		else
			return 0;
	}

	if (EXT4_INODE_SIZE(sb) <= EXT4_GOOD_OLD_INODE_SIZE)
		return -EOPNOTSUPP;

	kprojid = make_kprojid(&init_user_ns, (projid_t)projid);

	if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
		return 0;

	err = mnt_want_write_file(filp);
	if (err)
		return err;

	err = -EPERM;
	inode_lock(inode);
	/* Is it quota file? Do not allow user to mess with it */
	if (ext4_is_quota_file(inode))
		goto out_unlock;

	err = ext4_get_inode_loc(inode, &iloc);
	if (err)
		goto out_unlock;

	raw_inode = ext4_raw_inode(&iloc);
	if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) {
		err = ext4_expand_extra_isize(inode,
					      EXT4_SB(sb)->s_want_extra_isize,
					      &iloc);
		if (err)
			goto out_unlock;
	} else {
		brelse(iloc.bh);
	}

	dquot_initialize(inode);

	handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
		EXT4_QUOTA_INIT_BLOCKS(sb) +
		EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
	if (IS_ERR(handle)) {
		err = PTR_ERR(handle);
		goto out_unlock;
	}

	err = ext4_reserve_inode_write(handle, inode, &iloc);
	if (err)
		goto out_stop;

	transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
	if (!IS_ERR(transfer_to[PRJQUOTA])) {

		/* __dquot_transfer() calls back ext4_get_inode_usage() which
		 * counts xattr inode references.
		 */
		down_read(&EXT4_I(inode)->xattr_sem);
		err = __dquot_transfer(inode, transfer_to);
		up_read(&EXT4_I(inode)->xattr_sem);
		dqput(transfer_to[PRJQUOTA]);
		if (err)
			goto out_dirty;
	}

	EXT4_I(inode)->i_projid = kprojid;
	inode->i_ctime = current_time(inode);
out_dirty:
	rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
	if (!err)
		err = rc;
out_stop:
	ext4_journal_stop(handle);
out_unlock:
	inode_unlock(inode);
	mnt_drop_write_file(filp);
	return err;
}
Esempio n. 3
0
static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
{
	struct inode *inode = file_inode(filp);
	struct super_block *sb = inode->i_sb;
	struct ext4_inode_info *ei = EXT4_I(inode);
	int err, rc;
	handle_t *handle;
	kprojid_t kprojid;
	struct ext4_iloc iloc;
	struct ext4_inode *raw_inode;

	if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
			EXT4_FEATURE_RO_COMPAT_PROJECT)) {
		if (projid != EXT4_DEF_PROJID)
			return -EOPNOTSUPP;
		else
			return 0;
	}

	if (EXT4_INODE_SIZE(sb) <= EXT4_GOOD_OLD_INODE_SIZE)
		return -EOPNOTSUPP;

	kprojid = make_kprojid(&init_user_ns, (projid_t)projid);

	if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
		return 0;

	err = mnt_want_write_file(filp);
	if (err)
		return err;

	err = -EPERM;
	inode_lock(inode);
	/* Is it quota file? Do not allow user to mess with it */
	if (IS_NOQUOTA(inode))
		goto out_unlock;

	err = ext4_get_inode_loc(inode, &iloc);
	if (err)
		goto out_unlock;

	raw_inode = ext4_raw_inode(&iloc);
	if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) {
		err = -EOVERFLOW;
		brelse(iloc.bh);
		goto out_unlock;
	}
	brelse(iloc.bh);

	dquot_initialize(inode);

	handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
		EXT4_QUOTA_INIT_BLOCKS(sb) +
		EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
	if (IS_ERR(handle)) {
		err = PTR_ERR(handle);
		goto out_unlock;
	}

	err = ext4_reserve_inode_write(handle, inode, &iloc);
	if (err)
		goto out_stop;

	if (sb_has_quota_limits_enabled(sb, PRJQUOTA)) {
		struct dquot *transfer_to[MAXQUOTAS] = { };

		transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
		if (transfer_to[PRJQUOTA]) {
			err = __dquot_transfer(inode, transfer_to);
			dqput(transfer_to[PRJQUOTA]);
			if (err)
				goto out_dirty;
		}
	}
	EXT4_I(inode)->i_projid = kprojid;
	inode->i_ctime = ext4_current_time(inode);
out_dirty:
	rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
	if (!err)
		err = rc;
out_stop:
	ext4_journal_stop(handle);
out_unlock:
	inode_unlock(inode);
	mnt_drop_write_file(filp);
	return err;
}