]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - libxfs/xfs_inode_fork.c
xfs: introduce the CoW fork
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_inode_fork.c
index eda590f0a0c47921db16c5529199637e934e87c4..8212f77fe6625da3c3bc55c33f5c4582ad63a8b9 100644 (file)
@@ -202,9 +202,14 @@ xfs_iformat_fork(
                XFS_ERROR_REPORT("xfs_iformat(7)", XFS_ERRLEVEL_LOW, ip->i_mount);
                return -EFSCORRUPTED;
        }
-       if (error) {
+       if (error)
                return error;
+
+       if (xfs_is_reflink_inode(ip)) {
+               ASSERT(ip->i_cowfp == NULL);
+               xfs_ifork_init_cow(ip);
        }
+
        if (!XFS_DFORK_Q(dip))
                return 0;
 
@@ -243,6 +248,9 @@ xfs_iformat_fork(
        if (error) {
                kmem_zone_free(xfs_ifork_zone, ip->i_afp);
                ip->i_afp = NULL;
+               if (ip->i_cowfp)
+                       kmem_zone_free(xfs_ifork_zone, ip->i_cowfp);
+               ip->i_cowfp = NULL;
                xfs_idestroy_fork(ip, XFS_DATA_FORK);
        }
        return error;
@@ -757,6 +765,9 @@ xfs_idestroy_fork(
        if (whichfork == XFS_ATTR_FORK) {
                kmem_zone_free(xfs_ifork_zone, ip->i_afp);
                ip->i_afp = NULL;
+       } else if (whichfork == XFS_COW_FORK) {
+               kmem_zone_free(xfs_ifork_zone, ip->i_cowfp);
+               ip->i_cowfp = NULL;
        }
 }
 
@@ -944,6 +955,19 @@ xfs_iext_get_ext(
        }
 }
 
+/* Convert bmap state flags to an inode fork. */
+struct xfs_ifork *
+xfs_iext_state_to_fork(
+       struct xfs_inode        *ip,
+       int                     state)
+{
+       if (state & BMAP_COWFORK)
+               return ip->i_cowfp;
+       else if (state & BMAP_ATTRFORK)
+               return ip->i_afp;
+       return &ip->i_df;
+}
+
 /*
  * Insert new item(s) into the extent records for incore inode
  * fork 'ifp'.  'count' new items are inserted at index 'idx'.
@@ -956,7 +980,7 @@ xfs_iext_insert(
        xfs_bmbt_irec_t *new,           /* items to insert */
        int             state)          /* type of extent conversion */
 {
-       xfs_ifork_t     *ifp = (state & BMAP_ATTRFORK) ? ip->i_afp : &ip->i_df;
+       xfs_ifork_t     *ifp = xfs_iext_state_to_fork(ip, state);
        xfs_extnum_t    i;              /* extent record index */
 
        trace_xfs_iext_insert(ip, idx, new, state, _RET_IP_);
@@ -1206,7 +1230,7 @@ xfs_iext_remove(
        int             ext_diff,       /* number of extents to remove */
        int             state)          /* type of extent conversion */
 {
-       xfs_ifork_t     *ifp = (state & BMAP_ATTRFORK) ? ip->i_afp : &ip->i_df;
+       xfs_ifork_t     *ifp = xfs_iext_state_to_fork(ip, state);
        xfs_extnum_t    nextents;       /* number of extents in file */
        int             new_size;       /* size of extents after removal */
 
@@ -1953,3 +1977,20 @@ xfs_iext_irec_update_extoffs(
                ifp->if_u1.if_ext_irec[i].er_extoff += ext_diff;
        }
 }
+
+/*
+ * Initialize an inode's copy-on-write fork.
+ */
+void
+xfs_ifork_init_cow(
+       struct xfs_inode        *ip)
+{
+       if (ip->i_cowfp)
+               return;
+
+       ip->i_cowfp = kmem_zone_zalloc(xfs_ifork_zone,
+                                      KM_SLEEP | KM_NOFS);
+       ip->i_cowfp->if_flags = XFS_IFEXTENTS;
+       ip->i_cformat = XFS_DINODE_FMT_EXTENTS;
+       ip->i_cnextents = 0;
+}