|
@@ -3876,6 +3876,63 @@ xfs_bmap_btalloc(
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * For a remap operation, just "allocate" an extent at the address that the
|
|
|
+ * caller passed in, and ensure that the AGFL is the right size. The caller
|
|
|
+ * will then map the "allocated" extent into the file somewhere.
|
|
|
+ */
|
|
|
+STATIC int
|
|
|
+xfs_bmap_remap_alloc(
|
|
|
+ struct xfs_bmalloca *ap)
|
|
|
+{
|
|
|
+ struct xfs_trans *tp = ap->tp;
|
|
|
+ struct xfs_mount *mp = tp->t_mountp;
|
|
|
+ xfs_agblock_t bno;
|
|
|
+ struct xfs_alloc_arg args;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * validate that the block number is legal - the enables us to detect
|
|
|
+ * and handle a silent filesystem corruption rather than crashing.
|
|
|
+ */
|
|
|
+ memset(&args, 0, sizeof(struct xfs_alloc_arg));
|
|
|
+ args.tp = ap->tp;
|
|
|
+ args.mp = ap->tp->t_mountp;
|
|
|
+ bno = *ap->firstblock;
|
|
|
+ args.agno = XFS_FSB_TO_AGNO(mp, bno);
|
|
|
+ args.agbno = XFS_FSB_TO_AGBNO(mp, bno);
|
|
|
+ if (args.agno >= mp->m_sb.sb_agcount ||
|
|
|
+ args.agbno >= mp->m_sb.sb_agblocks)
|
|
|
+ return -EFSCORRUPTED;
|
|
|
+
|
|
|
+ /* "Allocate" the extent from the range we passed in. */
|
|
|
+ trace_xfs_bmap_remap_alloc(ap->ip, *ap->firstblock, ap->length);
|
|
|
+ ap->blkno = bno;
|
|
|
+ ap->ip->i_d.di_nblocks += ap->length;
|
|
|
+ xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
|
|
|
+
|
|
|
+ /* Fix the freelist, like a real allocator does. */
|
|
|
+ args.datatype = ap->datatype;
|
|
|
+ args.pag = xfs_perag_get(args.mp, args.agno);
|
|
|
+ ASSERT(args.pag);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The freelist fixing code will decline the allocation if
|
|
|
+ * the size and shape of the free space doesn't allow for
|
|
|
+ * allocating the extent and updating all the metadata that
|
|
|
+ * happens during an allocation. We're remapping, not
|
|
|
+ * allocating, so skip that check by pretending to be freeing.
|
|
|
+ */
|
|
|
+ error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING);
|
|
|
+ if (error)
|
|
|
+ goto error0;
|
|
|
+error0:
|
|
|
+ xfs_perag_put(args.pag);
|
|
|
+ if (error)
|
|
|
+ trace_xfs_bmap_remap_alloc_error(ap->ip, error, _RET_IP_);
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file.
|
|
|
* It figures out where to ask the underlying allocator to put the new extent.
|
|
@@ -3884,6 +3941,8 @@ STATIC int
|
|
|
xfs_bmap_alloc(
|
|
|
struct xfs_bmalloca *ap) /* bmap alloc argument struct */
|
|
|
{
|
|
|
+ if (ap->flags & XFS_BMAPI_REMAP)
|
|
|
+ return xfs_bmap_remap_alloc(ap);
|
|
|
if (XFS_IS_REALTIME_INODE(ap->ip) &&
|
|
|
xfs_alloc_is_userdata(ap->datatype))
|
|
|
return xfs_bmap_rtalloc(ap);
|
|
@@ -4442,6 +4501,9 @@ xfs_bmapi_write(
|
|
|
ASSERT(len > 0);
|
|
|
ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL);
|
|
|
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
|
|
|
+ ASSERT(!(flags & XFS_BMAPI_REMAP) || whichfork == XFS_DATA_FORK);
|
|
|
+ ASSERT(!(flags & XFS_BMAPI_PREALLOC) || !(flags & XFS_BMAPI_REMAP));
|
|
|
+ ASSERT(!(flags & XFS_BMAPI_CONVERT) || !(flags & XFS_BMAPI_REMAP));
|
|
|
|
|
|
/* zeroing is for currently only for data extents, not metadata */
|
|
|
ASSERT((flags & (XFS_BMAPI_METADATA | XFS_BMAPI_ZERO)) !=
|
|
@@ -4502,6 +4564,12 @@ xfs_bmapi_write(
|
|
|
inhole = eof || bma.got.br_startoff > bno;
|
|
|
wasdelay = !inhole && isnullstartblock(bma.got.br_startblock);
|
|
|
|
|
|
+ /*
|
|
|
+ * Make sure we only reflink into a hole.
|
|
|
+ */
|
|
|
+ if (flags & XFS_BMAPI_REMAP)
|
|
|
+ ASSERT(inhole);
|
|
|
+
|
|
|
/*
|
|
|
* First, deal with the hole before the allocated space
|
|
|
* that we found, if any.
|