diff options
-rw-r--r-- | fs/xfs/libxfs/xfs_attr.c | 165 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_attr.h | 2 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_log_format.h | 1 | ||||
-rw-r--r-- | fs/xfs/xfs_attr_item.c | 9 | ||||
-rw-r--r-- | fs/xfs/xfs_trace.h | 4 |
5 files changed, 110 insertions, 71 deletions
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 60878ebc07ba..54f90d66b206 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -670,6 +670,81 @@ xfs_attr_lookup( return xfs_attr_node_hasname(args, NULL); } +static int +xfs_attr_item_init( + struct xfs_da_args *args, + unsigned int op_flags, /* op flag (set or remove) */ + struct xfs_attr_item **attr) /* new xfs_attr_item */ +{ + + struct xfs_attr_item *new; + + new = kmem_zalloc(sizeof(struct xfs_attr_item), KM_NOFS); + new->xattri_op_flags = op_flags; + new->xattri_da_args = args; + + *attr = new; + return 0; +} + +/* Sets an attribute for an inode as a deferred operation */ +static int +xfs_attr_defer_add( + struct xfs_da_args *args) +{ + struct xfs_attr_item *new; + int error = 0; + + error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_SET, &new); + if (error) + return error; + + new->xattri_dela_state = XFS_DAS_UNINIT; + xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list); + trace_xfs_attr_defer_add(new->xattri_dela_state, args->dp); + + return 0; +} + +/* Sets an attribute for an inode as a deferred operation */ +static int +xfs_attr_defer_replace( + struct xfs_da_args *args) +{ + struct xfs_attr_item *new; + int error = 0; + + error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_REPLACE, &new); + if (error) + return error; + + new->xattri_dela_state = XFS_DAS_UNINIT; + xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list); + trace_xfs_attr_defer_replace(new->xattri_dela_state, args->dp); + + return 0; +} + +/* Removes an attribute for an inode as a deferred operation */ +static int +xfs_attr_defer_remove( + struct xfs_da_args *args) +{ + + struct xfs_attr_item *new; + int error; + + error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_REMOVE, &new); + if (error) + return error; + + new->xattri_dela_state = XFS_DAS_UNINIT; + xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list); + trace_xfs_attr_defer_remove(new->xattri_dela_state, args->dp); + + return 0; +} + /* * Note: If args->value is NULL the attribute will be removed, just like the * Linux ->setattr API. @@ -758,29 +833,35 @@ xfs_attr_set( } error = xfs_attr_lookup(args); - if (args->value) { - if (error == -EEXIST && (args->attr_flags & XATTR_CREATE)) - goto out_trans_cancel; - if (error == -ENOATTR && (args->attr_flags & XATTR_REPLACE)) - goto out_trans_cancel; - if (error != -ENOATTR && error != -EEXIST) + switch (error) { + case -EEXIST: + /* if no value, we are performing a remove operation */ + if (!args->value) { + error = xfs_attr_defer_remove(args); + break; + } + /* Pure create fails if the attr already exists */ + if (args->attr_flags & XATTR_CREATE) goto out_trans_cancel; - error = xfs_attr_set_deferred(args); - if (error) + error = xfs_attr_defer_replace(args); + break; + case -ENOATTR: + /* Can't remove what isn't there. */ + if (!args->value) goto out_trans_cancel; - /* shortform attribute has already been committed */ - if (!args->trans) - goto out_unlock; - } else { - if (error != -EEXIST) + /* Pure replace fails if no existing attr to replace. */ + if (args->attr_flags & XATTR_REPLACE) goto out_trans_cancel; - error = xfs_attr_remove_deferred(args); - if (error) - goto out_trans_cancel; + error = xfs_attr_defer_add(args); + break; + default: + goto out_trans_cancel; } + if (error) + goto out_trans_cancel; /* * If this is a synchronous mount, make sure that the @@ -844,58 +925,6 @@ xfs_attrd_destroy_cache(void) xfs_attrd_cache = NULL; } -STATIC int -xfs_attr_item_init( - struct xfs_da_args *args, - unsigned int op_flags, /* op flag (set or remove) */ - struct xfs_attr_item **attr) /* new xfs_attr_item */ -{ - - struct xfs_attr_item *new; - - new = kmem_zalloc(sizeof(struct xfs_attr_item), KM_NOFS); - new->xattri_op_flags = op_flags; - new->xattri_da_args = args; - - *attr = new; - return 0; -} - -/* Sets an attribute for an inode as a deferred operation */ -int -xfs_attr_set_deferred( - struct xfs_da_args *args) -{ - struct xfs_attr_item *new; - int error = 0; - - error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_SET, &new); - if (error) - return error; - - xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list); - - return 0; -} - -/* Removes an attribute for an inode as a deferred operation */ -int -xfs_attr_remove_deferred( - struct xfs_da_args *args) -{ - - struct xfs_attr_item *new; - int error; - - error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_REMOVE, &new); - if (error) - return error; - - xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list); - - return 0; -} - /*======================================================================== * External routines when attribute list is inside the inode *========================================================================*/ diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h index f6c13d2bfbcd..c9c867e3406c 100644 --- a/fs/xfs/libxfs/xfs_attr.h +++ b/fs/xfs/libxfs/xfs_attr.h @@ -521,8 +521,6 @@ bool xfs_attr_namecheck(const void *name, size_t length); int xfs_attr_calc_size(struct xfs_da_args *args, int *local); void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres, unsigned int *total); -int xfs_attr_set_deferred(struct xfs_da_args *args); -int xfs_attr_remove_deferred(struct xfs_da_args *args); extern struct kmem_cache *xfs_attri_cache; extern struct kmem_cache *xfs_attrd_cache; diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h index a27492e99673..f7edd1ecf6d9 100644 --- a/fs/xfs/libxfs/xfs_log_format.h +++ b/fs/xfs/libxfs/xfs_log_format.h @@ -908,6 +908,7 @@ struct xfs_icreate_log { */ #define XFS_ATTR_OP_FLAGS_SET 1 /* Set the attribute */ #define XFS_ATTR_OP_FLAGS_REMOVE 2 /* Remove the attribute */ +#define XFS_ATTR_OP_FLAGS_REPLACE 3 /* Replace the attribute */ #define XFS_ATTR_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */ /* diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c index 96933a3c0dcd..ee8b140a2801 100644 --- a/fs/xfs/xfs_attr_item.c +++ b/fs/xfs/xfs_attr_item.c @@ -318,6 +318,7 @@ xfs_xattri_finish_update( switch (op) { case XFS_ATTR_OP_FLAGS_SET: + case XFS_ATTR_OP_FLAGS_REPLACE: error = xfs_attr_set_iter(attr); break; case XFS_ATTR_OP_FLAGS_REMOVE: @@ -507,8 +508,14 @@ xfs_attri_validate( return false; /* alfi_op_flags should be either a set or remove */ - if (op != XFS_ATTR_OP_FLAGS_SET && op != XFS_ATTR_OP_FLAGS_REMOVE) + switch (op) { + case XFS_ATTR_OP_FLAGS_SET: + case XFS_ATTR_OP_FLAGS_REPLACE: + case XFS_ATTR_OP_FLAGS_REMOVE: + break; + default: return false; + } if (attrp->alfi_value_len > XATTR_SIZE_MAX) return false; diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index fec4198b738b..01ce0401aa32 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -4154,6 +4154,10 @@ DEFINE_DAS_STATE_EVENT(xfs_attr_leaf_addname_return); DEFINE_DAS_STATE_EVENT(xfs_attr_node_addname_return); DEFINE_DAS_STATE_EVENT(xfs_attr_remove_iter_return); DEFINE_DAS_STATE_EVENT(xfs_attr_rmtval_remove_return); +DEFINE_DAS_STATE_EVENT(xfs_attr_defer_add); +DEFINE_DAS_STATE_EVENT(xfs_attr_defer_replace); +DEFINE_DAS_STATE_EVENT(xfs_attr_defer_remove); + TRACE_EVENT(xfs_force_shutdown, TP_PROTO(struct xfs_mount *mp, int ptag, int flags, const char *fname, |