|
@@ -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;
|
|
|
+}
|