ocfs2: Implement ocfs2_reflink.
authorTao Ma <tao.ma@oracle.com>
Mon, 21 Sep 2009 02:38:17 +0000 (10:38 +0800)
committerJoel Becker <joel.becker@oracle.com>
Wed, 23 Sep 2009 03:09:49 +0000 (20:09 -0700)
Implement ocfs2_reflink.

Signed-off-by: Tao Ma <tao.ma@oracle.com>
fs/ocfs2/refcounttree.c

index 5d88e76..7a8a384 100644 (file)
@@ -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;
+}