Commit 09bf27a0 authored by Tao Ma's avatar Tao Ma Committed by Joel Becker
Browse files

ocfs2: Implement ocfs2_reflink.



Implement ocfs2_reflink.

Signed-off-by: default avatarTao Ma <tao.ma@oracle.com>
parent 0fe9b66c
Loading
Loading
Loading
Loading
+123 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
#include "extent_map.h"
#include "aops.h"
#include "xattr.h"
#include "namei.h"

#include <linux/bio.h>
#include <linux/blkdev.h>
@@ -4022,3 +4023,125 @@ out:

	return ret;
}

static int __ocfs2_reflink(struct dentry *old_dentry,
			   struct buffer_head *old_bh,
			   struct inode *new_inode,
			   bool preserve)
{
	int ret;
	struct inode *inode = old_dentry->d_inode;
	struct buffer_head *new_bh = NULL;

	ret = filemap_fdatawrite(inode->i_mapping);
	if (ret) {
		mlog_errno(ret);
		goto out;
	}

	ret = ocfs2_attach_refcount_tree(inode, old_bh);
	if (ret) {
		mlog_errno(ret);
		goto out;
	}

	mutex_lock(&new_inode->i_mutex);
	ret = ocfs2_inode_lock(new_inode, &new_bh, 1);
	if (ret) {
		mlog_errno(ret);
		goto out_unlock;
	}

	ret = ocfs2_create_reflink_node(inode, old_bh,
					new_inode, new_bh, preserve);
	if (ret) {
		mlog_errno(ret);
		goto inode_unlock;
	}

	if (OCFS2_I(inode)->ip_dyn_features & OCFS2_HAS_XATTR_FL) {
		ret = ocfs2_reflink_xattrs(inode, old_bh,
					   new_inode, new_bh,
					   preserve);
		if (ret)
			mlog_errno(ret);
	}
inode_unlock:
	ocfs2_inode_unlock(new_inode, 1);
	brelse(new_bh);
out_unlock:
	mutex_unlock(&new_inode->i_mutex);
out:
	if (!ret) {
		ret = filemap_fdatawait(inode->i_mapping);
		if (ret)
			mlog_errno(ret);
	}
	return ret;
}

static int ocfs2_reflink(struct dentry *old_dentry, struct inode *dir,
			 struct dentry *new_dentry, bool preserve)
{
	int error;
	struct inode *inode = old_dentry->d_inode;
	struct buffer_head *old_bh = NULL;
	struct inode *new_orphan_inode = NULL;

	if (!ocfs2_refcount_tree(OCFS2_SB(inode->i_sb)))
		return -EOPNOTSUPP;

	error = ocfs2_create_inode_in_orphan(dir, inode->i_mode,
					     &new_orphan_inode);
	if (error) {
		mlog_errno(error);
		goto out;
	}

	error = ocfs2_inode_lock(inode, &old_bh, 1);
	if (error) {
		mlog_errno(error);
		goto out;
	}

	down_write(&OCFS2_I(inode)->ip_xattr_sem);
	down_write(&OCFS2_I(inode)->ip_alloc_sem);
	error = __ocfs2_reflink(old_dentry, old_bh,
				new_orphan_inode, preserve);
	up_write(&OCFS2_I(inode)->ip_alloc_sem);
	up_write(&OCFS2_I(inode)->ip_xattr_sem);

	ocfs2_inode_unlock(inode, 1);
	brelse(old_bh);

	if (error) {
		mlog_errno(error);
		goto out;
	}

	/* If the security isn't preserved, we need to re-initialize them. */
	if (!preserve) {
		error = ocfs2_init_security_and_acl(dir, new_orphan_inode);
		if (error)
			mlog_errno(error);
	}
out:
	if (!error) {
		error = ocfs2_mv_orphaned_inode_to_new(dir, new_orphan_inode,
						       new_dentry);
		if (error)
			mlog_errno(error);
	}

	if (new_orphan_inode) {
		/*
		 * We need to open_unlock the inode no matter whether we
		 * succeed or not, so that other nodes can delete it later.
		 */
		ocfs2_open_unlock(new_orphan_inode);
		if (error)
			iput(new_orphan_inode);
	}

	return error;
}