|
@@ -191,6 +191,128 @@ xfs_attr_calc_size(
|
|
|
return nblks;
|
|
|
}
|
|
|
|
|
|
+STATIC int
|
|
|
+xfs_attr_try_sf_addname(
|
|
|
+ struct xfs_inode *dp,
|
|
|
+ struct xfs_da_args *args)
|
|
|
+{
|
|
|
+
|
|
|
+ struct xfs_mount *mp = dp->i_mount;
|
|
|
+ int error, error2;
|
|
|
+
|
|
|
+ error = xfs_attr_shortform_addname(args);
|
|
|
+ if (error == -ENOSPC)
|
|
|
+ return error;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Commit the shortform mods, and we're done.
|
|
|
+ * NOTE: this is also the error path (EEXIST, etc).
|
|
|
+ */
|
|
|
+ if (!error && (args->flags & ATTR_KERNOTIME) == 0)
|
|
|
+ xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
|
|
|
+
|
|
|
+ if (mp->m_flags & XFS_MOUNT_WSYNC)
|
|
|
+ xfs_trans_set_sync(args->trans);
|
|
|
+
|
|
|
+ error2 = xfs_trans_commit(args->trans);
|
|
|
+ args->trans = NULL;
|
|
|
+ return error ? error : error2;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Set the attribute specified in @args.
|
|
|
+ */
|
|
|
+int
|
|
|
+xfs_attr_set_args(
|
|
|
+ struct xfs_da_args *args,
|
|
|
+ struct xfs_buf **leaf_bp)
|
|
|
+{
|
|
|
+ struct xfs_inode *dp = args->dp;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the attribute list is non-existent or a shortform list,
|
|
|
+ * upgrade it to a single-leaf-block attribute list.
|
|
|
+ */
|
|
|
+ if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
|
|
|
+ (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
|
|
|
+ dp->i_d.di_anextents == 0)) {
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Build initial attribute list (if required).
|
|
|
+ */
|
|
|
+ if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS)
|
|
|
+ xfs_attr_shortform_create(args);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Try to add the attr to the attribute list in the inode.
|
|
|
+ */
|
|
|
+ error = xfs_attr_try_sf_addname(dp, args);
|
|
|
+ if (error != -ENOSPC)
|
|
|
+ return error;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * It won't fit in the shortform, transform to a leaf block.
|
|
|
+ * GROT: another possible req'mt for a double-split btree op.
|
|
|
+ */
|
|
|
+ error = xfs_attr_shortform_to_leaf(args, leaf_bp);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Prevent the leaf buffer from being unlocked so that a
|
|
|
+ * concurrent AIL push cannot grab the half-baked leaf
|
|
|
+ * buffer and run into problems with the write verifier.
|
|
|
+ */
|
|
|
+ xfs_trans_bhold(args->trans, *leaf_bp);
|
|
|
+
|
|
|
+ error = xfs_defer_finish(&args->trans);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Commit the leaf transformation. We'll need another
|
|
|
+ * (linked) transaction to add the new attribute to the
|
|
|
+ * leaf.
|
|
|
+ */
|
|
|
+ error = xfs_trans_roll_inode(&args->trans, dp);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+ xfs_trans_bjoin(args->trans, *leaf_bp);
|
|
|
+ *leaf_bp = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
|
|
|
+ error = xfs_attr_leaf_addname(args);
|
|
|
+ else
|
|
|
+ error = xfs_attr_node_addname(args);
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Remove the attribute specified in @args.
|
|
|
+ */
|
|
|
+int
|
|
|
+xfs_attr_remove_args(
|
|
|
+ struct xfs_da_args *args)
|
|
|
+{
|
|
|
+ struct xfs_inode *dp = args->dp;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ if (!xfs_inode_hasattr(dp)) {
|
|
|
+ error = -ENOATTR;
|
|
|
+ } else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
|
|
|
+ ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
|
|
|
+ error = xfs_attr_shortform_remove(args);
|
|
|
+ } else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
|
|
|
+ error = xfs_attr_leaf_removename(args);
|
|
|
+ } else {
|
|
|
+ error = xfs_attr_node_removename(args);
|
|
|
+ }
|
|
|
+
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
int
|
|
|
xfs_attr_set(
|
|
|
struct xfs_inode *dp,
|
|
@@ -204,7 +326,7 @@ xfs_attr_set(
|
|
|
struct xfs_da_args args;
|
|
|
struct xfs_trans_res tres;
|
|
|
int rsvd = (flags & ATTR_ROOT) != 0;
|
|
|
- int error, err2, local;
|
|
|
+ int error, local;
|
|
|
|
|
|
XFS_STATS_INC(mp, xs_attr_set);
|
|
|
|
|
@@ -255,93 +377,17 @@ xfs_attr_set(
|
|
|
error = xfs_trans_reserve_quota_nblks(args.trans, dp, args.total, 0,
|
|
|
rsvd ? XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES :
|
|
|
XFS_QMOPT_RES_REGBLKS);
|
|
|
- if (error) {
|
|
|
- xfs_iunlock(dp, XFS_ILOCK_EXCL);
|
|
|
- xfs_trans_cancel(args.trans);
|
|
|
- return error;
|
|
|
- }
|
|
|
+ if (error)
|
|
|
+ goto out_trans_cancel;
|
|
|
|
|
|
xfs_trans_ijoin(args.trans, dp, 0);
|
|
|
-
|
|
|
- /*
|
|
|
- * If the attribute list is non-existent or a shortform list,
|
|
|
- * upgrade it to a single-leaf-block attribute list.
|
|
|
- */
|
|
|
- if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
|
|
|
- (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
|
|
|
- dp->i_d.di_anextents == 0)) {
|
|
|
-
|
|
|
- /*
|
|
|
- * Build initial attribute list (if required).
|
|
|
- */
|
|
|
- if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS)
|
|
|
- xfs_attr_shortform_create(&args);
|
|
|
-
|
|
|
- /*
|
|
|
- * Try to add the attr to the attribute list in
|
|
|
- * the inode.
|
|
|
- */
|
|
|
- error = xfs_attr_shortform_addname(&args);
|
|
|
- if (error != -ENOSPC) {
|
|
|
- /*
|
|
|
- * Commit the shortform mods, and we're done.
|
|
|
- * NOTE: this is also the error path (EEXIST, etc).
|
|
|
- */
|
|
|
- ASSERT(args.trans != NULL);
|
|
|
-
|
|
|
- /*
|
|
|
- * If this is a synchronous mount, make sure that
|
|
|
- * the transaction goes to disk before returning
|
|
|
- * to the user.
|
|
|
- */
|
|
|
- if (mp->m_flags & XFS_MOUNT_WSYNC)
|
|
|
- xfs_trans_set_sync(args.trans);
|
|
|
-
|
|
|
- if (!error && (flags & ATTR_KERNOTIME) == 0) {
|
|
|
- xfs_trans_ichgtime(args.trans, dp,
|
|
|
- XFS_ICHGTIME_CHG);
|
|
|
- }
|
|
|
- err2 = xfs_trans_commit(args.trans);
|
|
|
- xfs_iunlock(dp, XFS_ILOCK_EXCL);
|
|
|
-
|
|
|
- return error ? error : err2;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * It won't fit in the shortform, transform to a leaf block.
|
|
|
- * GROT: another possible req'mt for a double-split btree op.
|
|
|
- */
|
|
|
- error = xfs_attr_shortform_to_leaf(&args, &leaf_bp);
|
|
|
- if (error)
|
|
|
- goto out;
|
|
|
- /*
|
|
|
- * Prevent the leaf buffer from being unlocked so that a
|
|
|
- * concurrent AIL push cannot grab the half-baked leaf
|
|
|
- * buffer and run into problems with the write verifier.
|
|
|
- */
|
|
|
- xfs_trans_bhold(args.trans, leaf_bp);
|
|
|
- error = xfs_defer_finish(&args.trans);
|
|
|
- if (error)
|
|
|
- goto out;
|
|
|
-
|
|
|
- /*
|
|
|
- * Commit the leaf transformation. We'll need another (linked)
|
|
|
- * transaction to add the new attribute to the leaf, which
|
|
|
- * means that we have to hold & join the leaf buffer here too.
|
|
|
- */
|
|
|
- error = xfs_trans_roll_inode(&args.trans, dp);
|
|
|
- if (error)
|
|
|
- goto out;
|
|
|
- xfs_trans_bjoin(args.trans, leaf_bp);
|
|
|
- leaf_bp = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
|
|
|
- error = xfs_attr_leaf_addname(&args);
|
|
|
- else
|
|
|
- error = xfs_attr_node_addname(&args);
|
|
|
+ error = xfs_attr_set_args(&args, &leaf_bp);
|
|
|
if (error)
|
|
|
- goto out;
|
|
|
+ goto out_release_leaf;
|
|
|
+ if (!args.trans) {
|
|
|
+ /* shortform attribute has already been committed */
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
|
* If this is a synchronous mount, make sure that the
|
|
@@ -358,17 +404,17 @@ xfs_attr_set(
|
|
|
*/
|
|
|
xfs_trans_log_inode(args.trans, dp, XFS_ILOG_CORE);
|
|
|
error = xfs_trans_commit(args.trans);
|
|
|
+out_unlock:
|
|
|
xfs_iunlock(dp, XFS_ILOCK_EXCL);
|
|
|
-
|
|
|
return error;
|
|
|
|
|
|
-out:
|
|
|
+out_release_leaf:
|
|
|
if (leaf_bp)
|
|
|
xfs_trans_brelse(args.trans, leaf_bp);
|
|
|
+out_trans_cancel:
|
|
|
if (args.trans)
|
|
|
xfs_trans_cancel(args.trans);
|
|
|
- xfs_iunlock(dp, XFS_ILOCK_EXCL);
|
|
|
- return error;
|
|
|
+ goto out_unlock;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -423,17 +469,7 @@ xfs_attr_remove(
|
|
|
*/
|
|
|
xfs_trans_ijoin(args.trans, dp, 0);
|
|
|
|
|
|
- if (!xfs_inode_hasattr(dp)) {
|
|
|
- error = -ENOATTR;
|
|
|
- } else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
|
|
|
- ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
|
|
|
- error = xfs_attr_shortform_remove(&args);
|
|
|
- } else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
|
|
|
- error = xfs_attr_leaf_removename(&args);
|
|
|
- } else {
|
|
|
- error = xfs_attr_node_removename(&args);
|
|
|
- }
|
|
|
-
|
|
|
+ error = xfs_attr_remove_args(&args);
|
|
|
if (error)
|
|
|
goto out;
|
|
|
|