|
@@ -147,7 +147,10 @@ xfs_bmap_local_to_extents(
|
|
|
xfs_fsblock_t *firstblock, /* first block allocated in xaction */
|
|
|
xfs_extlen_t total, /* total blocks needed by transaction */
|
|
|
int *logflagsp, /* inode logging flags */
|
|
|
- int whichfork); /* data or attr fork */
|
|
|
+ int whichfork, /* data or attr fork */
|
|
|
+ void (*init_fn)(struct xfs_buf *bp,
|
|
|
+ struct xfs_inode *ip,
|
|
|
+ struct xfs_ifork *ifp));
|
|
|
|
|
|
/*
|
|
|
* Search the extents list for the inode, for the extent containing bno.
|
|
@@ -357,7 +360,42 @@ xfs_bmap_add_attrfork_extents(
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Called from xfs_bmap_add_attrfork to handle local format files.
|
|
|
+ * Block initialisation functions for local to extent format conversion.
|
|
|
+ * As these get more complex, they will be moved to the relevant files,
|
|
|
+ * but for now they are too simple to worry about.
|
|
|
+ */
|
|
|
+STATIC void
|
|
|
+xfs_bmap_local_to_extents_init_fn(
|
|
|
+ struct xfs_buf *bp,
|
|
|
+ struct xfs_inode *ip,
|
|
|
+ struct xfs_ifork *ifp)
|
|
|
+{
|
|
|
+ bp->b_ops = &xfs_bmbt_buf_ops;
|
|
|
+ memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes);
|
|
|
+}
|
|
|
+
|
|
|
+STATIC void
|
|
|
+xfs_symlink_local_to_remote(
|
|
|
+ struct xfs_buf *bp,
|
|
|
+ struct xfs_inode *ip,
|
|
|
+ struct xfs_ifork *ifp)
|
|
|
+{
|
|
|
+ /* remote symlink blocks are not verifiable until CRCs come along */
|
|
|
+ bp->b_ops = NULL;
|
|
|
+ memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Called from xfs_bmap_add_attrfork to handle local format files. Each
|
|
|
+ * different data fork content type needs a different callout to do the
|
|
|
+ * conversion. Some are basic and only require special block initialisation
|
|
|
+ * callouts for the data formating, others (directories) are so specialised they
|
|
|
+ * handle everything themselves.
|
|
|
+ *
|
|
|
+ * XXX (dgc): investigate whether directory conversion can use the generic
|
|
|
+ * formatting callout. It should be possible - it's just a very complex
|
|
|
+ * formatter. it would also require passing the transaction through to the init
|
|
|
+ * function.
|
|
|
*/
|
|
|
STATIC int /* error */
|
|
|
xfs_bmap_add_attrfork_local(
|
|
@@ -368,25 +406,29 @@ xfs_bmap_add_attrfork_local(
|
|
|
int *flags) /* inode logging flags */
|
|
|
{
|
|
|
xfs_da_args_t dargs; /* args for dir/attr code */
|
|
|
- int error; /* error return value */
|
|
|
- xfs_mount_t *mp; /* mount structure pointer */
|
|
|
|
|
|
if (ip->i_df.if_bytes <= XFS_IFORK_DSIZE(ip))
|
|
|
return 0;
|
|
|
+
|
|
|
if (S_ISDIR(ip->i_d.di_mode)) {
|
|
|
- mp = ip->i_mount;
|
|
|
memset(&dargs, 0, sizeof(dargs));
|
|
|
dargs.dp = ip;
|
|
|
dargs.firstblock = firstblock;
|
|
|
dargs.flist = flist;
|
|
|
- dargs.total = mp->m_dirblkfsbs;
|
|
|
+ dargs.total = ip->i_mount->m_dirblkfsbs;
|
|
|
dargs.whichfork = XFS_DATA_FORK;
|
|
|
dargs.trans = tp;
|
|
|
- error = xfs_dir2_sf_to_block(&dargs);
|
|
|
- } else
|
|
|
- error = xfs_bmap_local_to_extents(tp, ip, firstblock, 1, flags,
|
|
|
- XFS_DATA_FORK);
|
|
|
- return error;
|
|
|
+ return xfs_dir2_sf_to_block(&dargs);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (S_ISLNK(ip->i_d.di_mode))
|
|
|
+ return xfs_bmap_local_to_extents(tp, ip, firstblock, 1,
|
|
|
+ flags, XFS_DATA_FORK,
|
|
|
+ xfs_symlink_local_to_remote);
|
|
|
+
|
|
|
+ return xfs_bmap_local_to_extents(tp, ip, firstblock, 1, flags,
|
|
|
+ XFS_DATA_FORK,
|
|
|
+ xfs_bmap_local_to_extents_init_fn);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -3219,7 +3261,10 @@ xfs_bmap_local_to_extents(
|
|
|
xfs_fsblock_t *firstblock, /* first block allocated in xaction */
|
|
|
xfs_extlen_t total, /* total blocks needed by transaction */
|
|
|
int *logflagsp, /* inode logging flags */
|
|
|
- int whichfork) /* data or attr fork */
|
|
|
+ int whichfork,
|
|
|
+ void (*init_fn)(struct xfs_buf *bp,
|
|
|
+ struct xfs_inode *ip,
|
|
|
+ struct xfs_ifork *ifp))
|
|
|
{
|
|
|
int error; /* error return value */
|
|
|
int flags; /* logging flags returned */
|
|
@@ -3239,12 +3284,12 @@ xfs_bmap_local_to_extents(
|
|
|
xfs_buf_t *bp; /* buffer for extent block */
|
|
|
xfs_bmbt_rec_host_t *ep;/* extent record pointer */
|
|
|
|
|
|
+ ASSERT((ifp->if_flags &
|
|
|
+ (XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) == XFS_IFINLINE);
|
|
|
memset(&args, 0, sizeof(args));
|
|
|
args.tp = tp;
|
|
|
args.mp = ip->i_mount;
|
|
|
args.firstblock = *firstblock;
|
|
|
- ASSERT((ifp->if_flags &
|
|
|
- (XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) == XFS_IFINLINE);
|
|
|
/*
|
|
|
* Allocate a block. We know we need only one, since the
|
|
|
* file currently fits in an inode.
|
|
@@ -3258,17 +3303,20 @@ xfs_bmap_local_to_extents(
|
|
|
}
|
|
|
args.total = total;
|
|
|
args.minlen = args.maxlen = args.prod = 1;
|
|
|
- if ((error = xfs_alloc_vextent(&args)))
|
|
|
+ error = xfs_alloc_vextent(&args);
|
|
|
+ if (error)
|
|
|
goto done;
|
|
|
- /*
|
|
|
- * Can't fail, the space was reserved.
|
|
|
- */
|
|
|
+
|
|
|
+ /* Can't fail, the space was reserved. */
|
|
|
ASSERT(args.fsbno != NULLFSBLOCK);
|
|
|
ASSERT(args.len == 1);
|
|
|
*firstblock = args.fsbno;
|
|
|
bp = xfs_btree_get_bufl(args.mp, tp, args.fsbno, 0);
|
|
|
- bp->b_ops = &xfs_bmbt_buf_ops;
|
|
|
- memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes);
|
|
|
+
|
|
|
+ /* initialise the block and copy the data */
|
|
|
+ init_fn(bp, ip, ifp);
|
|
|
+
|
|
|
+ /* account for the change in fork size and log everything */
|
|
|
xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1);
|
|
|
xfs_bmap_forkoff_reset(args.mp, ip, whichfork);
|
|
|
xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
|
|
@@ -4915,8 +4963,32 @@ xfs_bmapi_write(
|
|
|
XFS_STATS_INC(xs_blk_mapw);
|
|
|
|
|
|
if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
|
|
|
+ /*
|
|
|
+ * XXX (dgc): This assumes we are only called for inodes that
|
|
|
+ * contain content neutral data in local format. Anything that
|
|
|
+ * contains caller-specific data in local format that needs
|
|
|
+ * transformation to move to a block format needs to do the
|
|
|
+ * conversion to extent format itself.
|
|
|
+ *
|
|
|
+ * Directory data forks and attribute forks handle this
|
|
|
+ * themselves, but with the addition of metadata verifiers every
|
|
|
+ * data fork in local format now contains caller specific data
|
|
|
+ * and as such conversion through this function is likely to be
|
|
|
+ * broken.
|
|
|
+ *
|
|
|
+ * The only likely user of this branch is for remote symlinks,
|
|
|
+ * but we cannot overwrite the data fork contents of the symlink
|
|
|
+ * (EEXIST occurs higher up the stack) and so it will never go
|
|
|
+ * from local format to extent format here. Hence I don't think
|
|
|
+ * this branch is ever executed intentionally and we should
|
|
|
+ * consider removing it and asserting that xfs_bmapi_write()
|
|
|
+ * cannot be called directly on local format forks. i.e. callers
|
|
|
+ * are completely responsible for local to extent format
|
|
|
+ * conversion, not xfs_bmapi_write().
|
|
|
+ */
|
|
|
error = xfs_bmap_local_to_extents(tp, ip, firstblock, total,
|
|
|
- &bma.logflags, whichfork);
|
|
|
+ &bma.logflags, whichfork,
|
|
|
+ xfs_bmap_local_to_extents_init_fn);
|
|
|
if (error)
|
|
|
goto error0;
|
|
|
}
|