aboutsummaryrefslogtreecommitdiff
path: root/fs/xfs/libxfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/libxfs')
-rw-r--r--fs/xfs/libxfs/xfs_ag.c45
-rw-r--r--fs/xfs/libxfs/xfs_ag.h5
-rw-r--r--fs/xfs/libxfs/xfs_ag_resv.c2
-rw-r--r--fs/xfs/libxfs/xfs_alloc.c10
-rw-r--r--fs/xfs/libxfs/xfs_alloc_btree.c2
-rw-r--r--fs/xfs/libxfs/xfs_attr.c191
-rw-r--r--fs/xfs/libxfs/xfs_attr.h9
-rw-r--r--fs/xfs/libxfs/xfs_attr_leaf.c219
-rw-r--r--fs/xfs/libxfs/xfs_attr_leaf.h4
-rw-r--r--fs/xfs/libxfs/xfs_attr_remote.c2
-rw-r--r--fs/xfs/libxfs/xfs_bit.c2
-rw-r--r--fs/xfs/libxfs/xfs_bmap.c2
-rw-r--r--fs/xfs/libxfs/xfs_bmap_btree.c2
-rw-r--r--fs/xfs/libxfs/xfs_btree.c2
-rw-r--r--fs/xfs/libxfs/xfs_btree_mem.c2
-rw-r--r--fs/xfs/libxfs/xfs_btree_staging.c2
-rw-r--r--fs/xfs/libxfs/xfs_da_btree.c63
-rw-r--r--fs/xfs/libxfs/xfs_da_btree.h2
-rw-r--r--fs/xfs/libxfs/xfs_da_format.h2
-rw-r--r--fs/xfs/libxfs/xfs_defer.c8
-rw-r--r--fs/xfs/libxfs/xfs_dir2.c21
-rw-r--r--fs/xfs/libxfs/xfs_dir2_block.c2
-rw-r--r--fs/xfs/libxfs/xfs_dir2_data.c2
-rw-r--r--fs/xfs/libxfs/xfs_dir2_leaf.c2
-rw-r--r--fs/xfs/libxfs/xfs_dir2_node.c2
-rw-r--r--fs/xfs/libxfs/xfs_dir2_sf.c2
-rw-r--r--fs/xfs/libxfs/xfs_dquot_buf.c2
-rw-r--r--fs/xfs/libxfs/xfs_errortag.h8
-rw-r--r--fs/xfs/libxfs/xfs_exchmaps.c2
-rw-r--r--fs/xfs/libxfs/xfs_fs.h194
-rw-r--r--fs/xfs/libxfs/xfs_group.c2
-rw-r--r--fs/xfs/libxfs/xfs_health.h5
-rw-r--r--fs/xfs/libxfs/xfs_ialloc.c13
-rw-r--r--fs/xfs/libxfs/xfs_ialloc_btree.c2
-rw-r--r--fs/xfs/libxfs/xfs_iext_tree.c2
-rw-r--r--fs/xfs/libxfs/xfs_inode_buf.c6
-rw-r--r--fs/xfs/libxfs/xfs_inode_fork.c2
-rw-r--r--fs/xfs/libxfs/xfs_inode_util.c2
-rw-r--r--fs/xfs/libxfs/xfs_log_format.h7
-rw-r--r--fs/xfs/libxfs/xfs_log_rlimit.c2
-rw-r--r--fs/xfs/libxfs/xfs_metadir.c2
-rw-r--r--fs/xfs/libxfs/xfs_metafile.c7
-rw-r--r--fs/xfs/libxfs/xfs_ondisk.h52
-rw-r--r--fs/xfs/libxfs/xfs_parent.c16
-rw-r--r--fs/xfs/libxfs/xfs_refcount.c6
-rw-r--r--fs/xfs/libxfs/xfs_refcount_btree.c2
-rw-r--r--fs/xfs/libxfs/xfs_rmap.c2
-rw-r--r--fs/xfs/libxfs/xfs_rmap_btree.c2
-rw-r--r--fs/xfs/libxfs/xfs_rtbitmap.c2
-rw-r--r--fs/xfs/libxfs/xfs_rtgroup.c57
-rw-r--r--fs/xfs/libxfs/xfs_rtgroup.h17
-rw-r--r--fs/xfs/libxfs/xfs_rtrefcount_btree.c2
-rw-r--r--fs/xfs/libxfs/xfs_rtrmap_btree.c2
-rw-r--r--fs/xfs/libxfs/xfs_sb.c5
-rw-r--r--fs/xfs/libxfs/xfs_symlink_remote.c2
-rw-r--r--fs/xfs/libxfs/xfs_trans_inode.c2
-rw-r--r--fs/xfs/libxfs/xfs_trans_resv.c2
-rw-r--r--fs/xfs/libxfs/xfs_trans_space.c2
-rw-r--r--fs/xfs/libxfs/xfs_types.c2
-rw-r--r--fs/xfs/libxfs/xfs_zones.c151
-rw-r--r--fs/xfs/libxfs/xfs_zones.h6
61 files changed, 821 insertions, 376 deletions
diff --git a/fs/xfs/libxfs/xfs_ag.c b/fs/xfs/libxfs/xfs_ag.c
index e6ba914f6d06..dcd2f93b6a6c 100644
--- a/fs/xfs/libxfs/xfs_ag.c
+++ b/fs/xfs/libxfs/xfs_ag.c
@@ -5,7 +5,7 @@
* All rights reserved.
*/
-#include "xfs.h"
+#include "xfs_platform.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
@@ -110,10 +110,7 @@ xfs_perag_uninit(
struct xfs_group *xg)
{
#ifdef __KERNEL__
- struct xfs_perag *pag = to_perag(xg);
-
- cancel_delayed_work_sync(&pag->pag_blockgc_work);
- xfs_buf_cache_destroy(&pag->pag_bcache);
+ cancel_delayed_work_sync(&to_perag(xg)->pag_blockgc_work);
#endif
}
@@ -224,7 +221,7 @@ xfs_perag_alloc(
struct xfs_perag *pag;
int error;
- pag = kzalloc(sizeof(*pag), GFP_KERNEL);
+ pag = kzalloc_obj(*pag);
if (!pag)
return -ENOMEM;
@@ -235,10 +232,6 @@ xfs_perag_alloc(
INIT_RADIX_TREE(&pag->pag_ici_root, GFP_ATOMIC);
#endif /* __KERNEL__ */
- error = xfs_buf_cache_init(&pag->pag_bcache);
- if (error)
- goto out_free_perag;
-
/*
* Pre-calculated geometry
*/
@@ -250,12 +243,10 @@ xfs_perag_alloc(
error = xfs_group_insert(mp, pag_group(pag), index, XG_TYPE_AG);
if (error)
- goto out_buf_cache_destroy;
+ goto out_free_perag;
return 0;
-out_buf_cache_destroy:
- xfs_buf_cache_destroy(&pag->pag_bcache);
out_free_perag:
kfree(pag);
return error;
@@ -872,6 +863,34 @@ resv_err:
return err2;
}
+void
+xfs_growfs_compute_deltas(
+ struct xfs_mount *mp,
+ xfs_rfsblock_t nb,
+ int64_t *deltap,
+ xfs_agnumber_t *nagcountp)
+{
+ xfs_rfsblock_t nb_div, nb_mod;
+ int64_t delta;
+ xfs_agnumber_t nagcount;
+
+ nb_div = nb;
+ nb_mod = do_div(nb_div, mp->m_sb.sb_agblocks);
+ if (nb_mod && nb_mod >= XFS_MIN_AG_BLOCKS)
+ nb_div++;
+ else if (nb_mod)
+ nb = nb_div * mp->m_sb.sb_agblocks;
+
+ if (nb_div > XFS_MAX_AGNUMBER + 1) {
+ nb_div = XFS_MAX_AGNUMBER + 1;
+ nb = nb_div * mp->m_sb.sb_agblocks;
+ }
+ nagcount = nb_div;
+ delta = nb - mp->m_sb.sb_dblocks;
+ *deltap = delta;
+ *nagcountp = nagcount;
+}
+
/*
* Extent the AG indicated by the @id by the length passed in
*/
diff --git a/fs/xfs/libxfs/xfs_ag.h b/fs/xfs/libxfs/xfs_ag.h
index 1f24cfa27321..16a9b43a3c27 100644
--- a/fs/xfs/libxfs/xfs_ag.h
+++ b/fs/xfs/libxfs/xfs_ag.h
@@ -85,8 +85,6 @@ struct xfs_perag {
int pag_ici_reclaimable; /* reclaimable inodes */
unsigned long pag_ici_reclaim_cursor; /* reclaim restart point */
- struct xfs_buf_cache pag_bcache;
-
/* background prealloc block trimming */
struct delayed_work pag_blockgc_work;
#endif /* __KERNEL__ */
@@ -331,6 +329,9 @@ struct aghdr_init_data {
int xfs_ag_init_headers(struct xfs_mount *mp, struct aghdr_init_data *id);
int xfs_ag_shrink_space(struct xfs_perag *pag, struct xfs_trans **tpp,
xfs_extlen_t delta);
+void
+xfs_growfs_compute_deltas(struct xfs_mount *mp, xfs_rfsblock_t nb,
+ int64_t *deltap, xfs_agnumber_t *nagcountp);
int xfs_ag_extend_space(struct xfs_perag *pag, struct xfs_trans *tp,
xfs_extlen_t len);
int xfs_ag_get_geometry(struct xfs_perag *pag, struct xfs_ag_geometry *ageo);
diff --git a/fs/xfs/libxfs/xfs_ag_resv.c b/fs/xfs/libxfs/xfs_ag_resv.c
index 8ac8230c3d3c..c4cdcc570d61 100644
--- a/fs/xfs/libxfs/xfs_ag_resv.c
+++ b/fs/xfs/libxfs/xfs_ag_resv.c
@@ -3,7 +3,7 @@
* Copyright (C) 2016 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <darrick.wong@oracle.com>
*/
-#include "xfs.h"
+#include "xfs_platform.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index ad381c73abc4..d99602bcc16f 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -3,7 +3,7 @@
* Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
-#include "xfs.h"
+#include "xfs_platform.h"
#include "xfs_fs.h"
#include "xfs_format.h"
#include "xfs_log_format.h"
@@ -376,8 +376,8 @@ xfs_alloc_compute_diff(
xfs_agblock_t freeend; /* end of freespace extent */
xfs_agblock_t newbno1; /* return block number */
xfs_agblock_t newbno2; /* other new block number */
- xfs_extlen_t newlen1=0; /* length with newbno1 */
- xfs_extlen_t newlen2=0; /* length with newbno2 */
+ xfs_extlen_t newlen1 = 0; /* length with newbno1 */
+ xfs_extlen_t newlen2 = 0; /* length with newbno2 */
xfs_agblock_t wantend; /* end of target extent */
bool userdata = datatype & XFS_ALLOC_USERDATA;
@@ -577,8 +577,8 @@ xfs_alloc_fixup_trees(
int i; /* operation results */
xfs_agblock_t nfbno1; /* first new free startblock */
xfs_agblock_t nfbno2; /* second new free startblock */
- xfs_extlen_t nflen1=0; /* first new free length */
- xfs_extlen_t nflen2=0; /* second new free length */
+ xfs_extlen_t nflen1 = 0; /* first new free length */
+ xfs_extlen_t nflen2 = 0; /* second new free length */
struct xfs_mount *mp;
bool fixup_longest = false;
diff --git a/fs/xfs/libxfs/xfs_alloc_btree.c b/fs/xfs/libxfs/xfs_alloc_btree.c
index fa1f03c1331e..29f6ec1c3f6f 100644
--- a/fs/xfs/libxfs/xfs_alloc_btree.c
+++ b/fs/xfs/libxfs/xfs_alloc_btree.c
@@ -3,7 +3,7 @@
* Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
-#include "xfs.h"
+#include "xfs_platform.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 8c04acd30d48..93caa1dae501 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -3,7 +3,7 @@
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
-#include "xfs.h"
+#include "xfs_platform.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
@@ -50,7 +50,6 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
*/
STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
-STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
/*
* Internal routines when attribute list is more than one block.
@@ -351,16 +350,14 @@ xfs_attr_set_resv(
*/
STATIC int
xfs_attr_try_sf_addname(
- struct xfs_inode *dp,
struct xfs_da_args *args)
{
-
int error;
/*
* Build initial attribute list (if required).
*/
- if (dp->i_af.if_format == XFS_DINODE_FMT_EXTENTS)
+ if (args->dp->i_af.if_format == XFS_DINODE_FMT_EXTENTS)
xfs_attr_shortform_create(args);
error = xfs_attr_shortform_addname(args);
@@ -372,9 +369,9 @@ xfs_attr_try_sf_addname(
* NOTE: this is also the error path (EEXIST, etc).
*/
if (!error)
- xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
+ xfs_trans_ichgtime(args->trans, args->dp, XFS_ICHGTIME_CHG);
- if (xfs_has_wsync(dp->i_mount))
+ if (xfs_has_wsync(args->dp->i_mount))
xfs_trans_set_sync(args->trans);
return error;
@@ -385,10 +382,9 @@ xfs_attr_sf_addname(
struct xfs_attr_intent *attr)
{
struct xfs_da_args *args = attr->xattri_da_args;
- struct xfs_inode *dp = args->dp;
int error = 0;
- error = xfs_attr_try_sf_addname(dp, args);
+ error = xfs_attr_try_sf_addname(args);
if (error != -ENOSPC) {
ASSERT(!error || error == -EEXIST);
attr->xattri_dela_state = XFS_DAS_DONE;
@@ -979,11 +975,12 @@ xfs_attr_lookup(
return error;
if (xfs_attr_is_leaf(dp)) {
- error = xfs_attr_leaf_hasname(args, &bp);
-
- if (bp)
- xfs_trans_brelse(args->trans, bp);
-
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner,
+ 0, &bp);
+ if (error)
+ return error;
+ error = xfs_attr3_leaf_lookup_int(bp, args);
+ xfs_trans_brelse(args->trans, bp);
return error;
}
@@ -1032,6 +1029,95 @@ trans_cancel:
}
/*
+ * Decide if it is theoretically possible to try to bypass the attr intent
+ * mechanism for better performance. Other constraints (e.g. available space
+ * in the existing structure) are not considered here.
+ */
+static inline bool
+xfs_attr_can_shortcut(
+ const struct xfs_inode *ip)
+{
+ return xfs_inode_has_attr_fork(ip) && xfs_attr_is_shortform(ip);
+}
+
+/* Try to set an attr in one transaction or fall back to attr intents. */
+int
+xfs_attr_setname(
+ struct xfs_da_args *args,
+ int rmt_blks)
+{
+ int error;
+
+ if (!rmt_blks && xfs_attr_can_shortcut(args->dp)) {
+ args->op_flags |= XFS_DA_OP_ADDNAME;
+
+ error = xfs_attr_try_sf_addname(args);
+ if (error != -ENOSPC)
+ return error;
+ }
+
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
+ return 0;
+}
+
+/* Try to remove an attr in one transaction or fall back to attr intents. */
+int
+xfs_attr_removename(
+ struct xfs_da_args *args)
+{
+ if (xfs_attr_can_shortcut(args->dp))
+ return xfs_attr_sf_removename(args);
+
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE);
+ return 0;
+}
+
+/* Try to replace an attr in one transaction or fall back to attr intents. */
+int
+xfs_attr_replacename(
+ struct xfs_da_args *args,
+ int rmt_blks)
+{
+ int error;
+
+ if (rmt_blks || !xfs_attr_can_shortcut(args->dp)) {
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE);
+ return 0;
+ }
+
+ error = xfs_attr_shortform_replace(args);
+ if (error != -ENOSPC)
+ return error;
+
+ args->op_flags |= XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE;
+
+ error = xfs_attr_sf_removename(args);
+ if (error)
+ return error;
+
+ if (args->attr_filter & XFS_ATTR_PARENT) {
+ /*
+ * Move the new name/value to the regular name/value slots and
+ * zero out the new name/value slots because we don't need to
+ * log them for a PPTR_SET operation.
+ */
+ xfs_attr_update_pptr_replace_args(args);
+ args->new_name = NULL;
+ args->new_namelen = 0;
+ args->new_value = NULL;
+ args->new_valuelen = 0;
+ }
+ args->op_flags &= ~XFS_DA_OP_REPLACE;
+
+ error = xfs_attr_try_sf_addname(args);
+ if (error != -ENOSPC)
+ return error;
+
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
+ return 0;
+}
+
+/*
* Make a change to the xattr structure.
*
* The caller must have initialized @args, attached dquots, and must not hold
@@ -1111,14 +1197,19 @@ xfs_attr_set(
case -EEXIST:
if (op == XFS_ATTRUPDATE_REMOVE) {
/* if no value, we are performing a remove operation */
- xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE);
+ error = xfs_attr_removename(args);
+ if (error)
+ goto out_trans_cancel;
break;
}
/* Pure create fails if the attr already exists */
if (op == XFS_ATTRUPDATE_CREATE)
goto out_trans_cancel;
- xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE);
+
+ error = xfs_attr_replacename(args, rmt_blks);
+ if (error)
+ goto out_trans_cancel;
break;
case -ENOATTR:
/* Can't remove what isn't there. */
@@ -1128,7 +1219,10 @@ xfs_attr_set(
/* Pure replace fails if no existing attr to replace. */
if (op == XFS_ATTRUPDATE_REPLACE)
goto out_trans_cancel;
- xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
+
+ error = xfs_attr_setname(args, rmt_blks);
+ if (error)
+ goto out_trans_cancel;
break;
default:
goto out_trans_cancel;
@@ -1223,27 +1317,6 @@ xfs_attr_shortform_addname(
*========================================================================*/
/*
- * Return EEXIST if attr is found, or ENOATTR if not
- */
-STATIC int
-xfs_attr_leaf_hasname(
- struct xfs_da_args *args,
- struct xfs_buf **bp)
-{
- int error = 0;
-
- error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner, 0, bp);
- if (error)
- return error;
-
- error = xfs_attr3_leaf_lookup_int(*bp, args);
- if (error != -ENOATTR && error != -EEXIST)
- xfs_trans_brelse(args->trans, *bp);
-
- return error;
-}
-
-/*
* Remove a name from the leaf attribute list structure
*
* This leaf block cannot have a "remote" value, we only call this routine
@@ -1253,25 +1326,22 @@ STATIC int
xfs_attr_leaf_removename(
struct xfs_da_args *args)
{
- struct xfs_inode *dp;
- struct xfs_buf *bp;
+ struct xfs_inode *dp = args->dp;
int error, forkoff;
+ struct xfs_buf *bp;
trace_xfs_attr_leaf_removename(args);
- /*
- * Remove the attribute.
- */
- dp = args->dp;
-
- error = xfs_attr_leaf_hasname(args, &bp);
- if (error == -ENOATTR) {
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner, 0, &bp);
+ if (error)
+ return error;
+ error = xfs_attr3_leaf_lookup_int(bp, args);
+ if (error != -EEXIST) {
xfs_trans_brelse(args->trans, bp);
- if (args->op_flags & XFS_DA_OP_RECOVERY)
+ if (error == -ENOATTR && (args->op_flags & XFS_DA_OP_RECOVERY))
return 0;
return error;
- } else if (error != -EEXIST)
- return error;
+ }
xfs_attr3_leaf_remove(bp, args);
@@ -1295,23 +1365,20 @@ xfs_attr_leaf_removename(
* Returns 0 on successful retrieval, otherwise an error.
*/
STATIC int
-xfs_attr_leaf_get(xfs_da_args_t *args)
+xfs_attr_leaf_get(
+ struct xfs_da_args *args)
{
- struct xfs_buf *bp;
- int error;
+ struct xfs_buf *bp;
+ int error;
trace_xfs_attr_leaf_get(args);
- error = xfs_attr_leaf_hasname(args, &bp);
-
- if (error == -ENOATTR) {
- xfs_trans_brelse(args->trans, bp);
- return error;
- } else if (error != -EEXIST)
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner, 0, &bp);
+ if (error)
return error;
-
-
- error = xfs_attr3_leaf_getvalue(bp, args);
+ error = xfs_attr3_leaf_lookup_int(bp, args);
+ if (error == -EEXIST)
+ error = xfs_attr3_leaf_getvalue(bp, args);
xfs_trans_brelse(args->trans, bp);
return error;
}
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 0e51d0723f9a..67fd9c75ac3f 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -55,7 +55,8 @@ struct xfs_attr_list_context {
struct xfs_trans *tp;
struct xfs_inode *dp; /* inode */
struct xfs_attrlist_cursor_kern cursor; /* position in list */
- void *buffer; /* output buffer */
+ /* output buffer */
+ void *buffer __counted_by_ptr(bufsize);
/*
* Abort attribute list iteration if non-zero. Can be used to pass
@@ -573,7 +574,7 @@ struct xfs_trans_res xfs_attr_set_resv(const struct xfs_da_args *args);
*/
static inline bool
xfs_attr_is_shortform(
- struct xfs_inode *ip)
+ const struct xfs_inode *ip)
{
return ip->i_af.if_format == XFS_DINODE_FMT_LOCAL ||
(ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS &&
@@ -649,4 +650,8 @@ void xfs_attr_intent_destroy_cache(void);
int xfs_attr_sf_totsize(struct xfs_inode *dp);
int xfs_attr_add_fork(struct xfs_inode *ip, int size, int rsvd);
+int xfs_attr_setname(struct xfs_da_args *args, int rmt_blks);
+int xfs_attr_removename(struct xfs_da_args *args);
+int xfs_attr_replacename(struct xfs_da_args *args, int rmt_blks);
+
#endif /* __XFS_ATTR_H__ */
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index 91c1b30ebaab..2b78041e8672 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -4,7 +4,7 @@
* Copyright (c) 2013 Red Hat, Inc.
* All Rights Reserved.
*/
-#include "xfs.h"
+#include "xfs_platform.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
@@ -75,6 +75,59 @@ STATIC void xfs_attr3_leaf_moveents(struct xfs_da_args *args,
int move_count);
STATIC int xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index);
+/* Compute the byte offset of the end of the leaf entry array. */
+static inline int
+xfs_attr_leaf_entries_end(
+ unsigned int hdrcount,
+ const struct xfs_attr_leafblock *leaf)
+{
+ return hdrcount * sizeof(struct xfs_attr_leaf_entry) +
+ xfs_attr3_leaf_hdr_size(leaf);
+}
+
+static inline bool
+ichdr_freemaps_overlap(
+ const struct xfs_attr3_icleaf_hdr *ichdr,
+ unsigned int x,
+ unsigned int y)
+{
+ const unsigned int xend =
+ ichdr->freemap[x].base + ichdr->freemap[x].size;
+ const unsigned int yend =
+ ichdr->freemap[y].base + ichdr->freemap[y].size;
+
+ /* empty slots do not overlap */
+ if (!ichdr->freemap[x].size || !ichdr->freemap[y].size)
+ return false;
+
+ return ichdr->freemap[x].base < yend && xend > ichdr->freemap[y].base;
+}
+
+static inline xfs_failaddr_t
+xfs_attr_leaf_ichdr_freemaps_verify(
+ const struct xfs_attr3_icleaf_hdr *ichdr,
+ const struct xfs_attr_leafblock *leaf)
+{
+ unsigned int entries_end =
+ xfs_attr_leaf_entries_end(ichdr->count, leaf);
+ int i;
+
+ if (ichdr_freemaps_overlap(ichdr, 0, 1))
+ return __this_address;
+ if (ichdr_freemaps_overlap(ichdr, 0, 2))
+ return __this_address;
+ if (ichdr_freemaps_overlap(ichdr, 1, 2))
+ return __this_address;
+
+ for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
+ if (ichdr->freemap[i].size > 0 &&
+ ichdr->freemap[i].base < entries_end)
+ return __this_address;
+ }
+
+ return NULL;
+}
+
/*
* attr3 block 'firstused' conversion helpers.
*
@@ -218,6 +271,8 @@ xfs_attr3_leaf_hdr_to_disk(
hdr3->freemap[i].base = cpu_to_be16(from->freemap[i].base);
hdr3->freemap[i].size = cpu_to_be16(from->freemap[i].size);
}
+
+ ASSERT(xfs_attr_leaf_ichdr_freemaps_verify(from, to) == NULL);
return;
}
to->hdr.info.forw = cpu_to_be32(from->forw);
@@ -233,6 +288,8 @@ xfs_attr3_leaf_hdr_to_disk(
to->hdr.freemap[i].base = cpu_to_be16(from->freemap[i].base);
to->hdr.freemap[i].size = cpu_to_be16(from->freemap[i].size);
}
+
+ ASSERT(xfs_attr_leaf_ichdr_freemaps_verify(from, to) == NULL);
}
static xfs_failaddr_t
@@ -385,6 +442,10 @@ xfs_attr3_leaf_verify(
return __this_address;
}
+ fa = xfs_attr_leaf_ichdr_freemaps_verify(&ichdr, leaf);
+ if (fa)
+ return fa;
+
return NULL;
}
@@ -782,6 +843,44 @@ xfs_attr_sf_findname(
}
/*
+ * Replace a shortform xattr if it's the right length. Returns 0 on success,
+ * -ENOSPC if the length is wrong, or -ENOATTR if the attr was not found.
+ */
+int
+xfs_attr_shortform_replace(
+ struct xfs_da_args *args)
+{
+ struct xfs_attr_sf_entry *sfe;
+
+ ASSERT(args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL);
+
+ trace_xfs_attr_sf_replace(args);
+
+ sfe = xfs_attr_sf_findname(args);
+ if (!sfe)
+ return -ENOATTR;
+
+ if (args->attr_filter & XFS_ATTR_PARENT) {
+ if (sfe->namelen != args->new_namelen ||
+ sfe->valuelen != args->new_valuelen)
+ return -ENOSPC;
+
+ memcpy(sfe->nameval, args->new_name, sfe->namelen);
+ memcpy(&sfe->nameval[sfe->namelen], args->new_value,
+ sfe->valuelen);
+ } else {
+ if (sfe->valuelen != args->valuelen)