return error;
}
+/*
+ * Before updating xattrs, add an attribute fork if the inode doesn't have.
+ * (inode must not be locked when we call this routine)
+ */
+static int
+xfs_attr_ensure_fork(
+ struct xfs_da_args *args,
+ bool rsvd)
+{
+ int sf_size;
+
+ if (xfs_inode_has_attr_fork(args->dp))
+ return 0;
+
+ sf_size = sizeof(struct xfs_attr_sf_hdr) +
+ xfs_attr_sf_entsize_byname(args->namelen,
+ args->valuelen);
+
+ return xfs_bmap_add_attrfork(args->dp, sf_size, rsvd);
+}
+
+/*
+ * Before updating xattrs, make sure we can handle adding to the extent count.
+ * There must be a transaction and the ILOCK must be held.
+ */
+static int
+xfs_attr_ensure_iext(
+ struct xfs_da_args *args,
+ int nr)
+{
+ int error;
+
+ error = xfs_iext_count_may_overflow(args->dp, XFS_ATTR_FORK, nr);
+ if (error == -EFBIG)
+ return xfs_iext_count_upgrade(args->trans, args->dp, nr);
+ return error;
+}
+
/*
* Note: If args->value is NULL the attribute will be removed, just like the
* Linux ->setattr API.
XFS_STATS_INC(mp, xs_attr_set);
args->total = xfs_attr_calc_size(args, &local);
- /*
- * If the inode doesn't have an attribute fork, add one.
- * (inode must not be locked when we call this routine)
- */
- if (xfs_inode_has_attr_fork(dp) == 0) {
- int sf_size = sizeof(struct xfs_attr_sf_hdr) +
- xfs_attr_sf_entsize_byname(args->namelen,
- args->valuelen);
-
- error = xfs_bmap_add_attrfork(dp, sf_size, rsvd);
- if (error)
- return error;
- }
+ error = xfs_attr_ensure_fork(args, rsvd);
+ if (error)
+ return error;
if (!local)
rmt_blks = xfs_attr3_rmt_blocks(mp, args->valuelen);
return error;
if (args->value || xfs_inode_hasattr(dp)) {
- error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK,
+ error = xfs_attr_ensure_iext(args,
XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
- if (error == -EFBIG)
- error = xfs_iext_count_upgrade(args->trans, dp,
- XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
if (error)
goto out_trans_cancel;
}
goto out_unlock;
}
+/*
+ * Ensure that the xattr structure maps @args->name to @args->value.
+ *
+ * The caller must have initialized @args, attached dquots, and must not hold
+ * any ILOCKs. Only XATTR_CREATE may be specified in @args->xattr_flags.
+ * Reserved data blocks may be used if @rsvd is set.
+ *
+ * Returns -EEXIST if XATTR_CREATE was specified and the name already exists.
+ */
+int
+xfs_attr_setname(
+ struct xfs_da_args *args,
+ bool rsvd)
+{
+ struct xfs_inode *dp = args->dp;
+ struct xfs_mount *mp = dp->i_mount;
+ struct xfs_trans_res tres;
+ unsigned int total;
+ int rmt_extents = 0;
+ int error, local;
+
+ ASSERT(!(args->xattr_flags & XATTR_REPLACE));
+ ASSERT(!args->trans);
+
+ args->total = xfs_attr_calc_size(args, &local);
+
+ error = xfs_attr_ensure_fork(args, rsvd);
+ if (error)
+ return error;
+
+ if (!local)
+ rmt_extents = XFS_IEXT_ATTR_MANIP_CNT(
+ xfs_attr3_rmt_blocks(mp, args->valuelen));
+
+ xfs_init_attr_trans(args, &tres, &total);
+ error = xfs_trans_alloc_inode(dp, &tres, total, 0, rsvd, &args->trans);
+ if (error)
+ return error;
+
+ error = xfs_attr_ensure_iext(args, rmt_extents);
+ if (error)
+ goto out_trans_cancel;
+
+ error = xfs_attr_lookup(args);
+ switch (error) {
+ case -EEXIST:
+ /* Pure create fails if the attr already exists */
+ if (args->xattr_flags & XATTR_CREATE)
+ goto out_trans_cancel;
+ if (args->attr_filter & XFS_ATTR_PARENT)
+ xfs_attr_defer_parent(args, XFS_ATTR_DEFER_REPLACE);
+ else
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE);
+ break;
+ case -ENOATTR:
+ if (args->attr_filter & XFS_ATTR_PARENT)
+ xfs_attr_defer_parent(args, XFS_ATTR_DEFER_SET);
+ else
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
+ break;
+ default:
+ goto out_trans_cancel;
+ }
+
+ xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
+ error = xfs_trans_commit(args->trans);
+out_unlock:
+ args->trans = NULL;
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ return error;
+
+out_trans_cancel:
+ xfs_trans_cancel(args->trans);
+ goto out_unlock;
+}
+
+/*
+ * Ensure that the xattr structure does not map @args->name to @args->value.
+ *
+ * The caller must have initialized @args, attached dquots, and must not hold
+ * any ILOCKs. Reserved data blocks may be used if @rsvd is set.
+ *
+ * Returns -ENOATTR if the name did not already exist.
+ */
+int
+xfs_attr_removename(
+ struct xfs_da_args *args,
+ bool rsvd)
+{
+ struct xfs_inode *dp = args->dp;
+ struct xfs_mount *mp = dp->i_mount;
+ struct xfs_trans_res tres;
+ unsigned int total;
+ int rmt_extents;
+ int error;
+
+ ASSERT(!args->trans);
+
+ rmt_extents = XFS_IEXT_ATTR_MANIP_CNT(
+ xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX));
+
+ xfs_init_attr_trans(args, &tres, &total);
+ error = xfs_trans_alloc_inode(dp, &tres, total, 0, rsvd, &args->trans);
+ if (error)
+ return error;
+
+ if (xfs_inode_hasattr(dp)) {
+ error = xfs_attr_ensure_iext(args, rmt_extents);
+ if (error)
+ goto out_trans_cancel;
+ }
+
+ error = xfs_attr_lookup(args);
+ if (error != -EEXIST)
+ goto out_trans_cancel;
+
+ if (args->attr_filter & XFS_ATTR_PARENT)
+ xfs_attr_defer_parent(args, XFS_ATTR_DEFER_REMOVE);
+ else
+ xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE);
+ xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
+ error = xfs_trans_commit(args->trans);
+out_unlock:
+ args->trans = NULL;
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ return error;
+
+out_trans_cancel:
+ xfs_trans_cancel(args->trans);
+ goto out_unlock;
+}
+
/*========================================================================
* External routines when attribute list is inside the inode
*========================================================================*/