@@ -1026,6 +1026,7 @@ struct xfs_icreate_log {
#define XFS_ATTRI_OP_FLAGS_SET 1 /* Set the attribute */
#define XFS_ATTRI_OP_FLAGS_REMOVE 2 /* Remove the attribute */
#define XFS_ATTRI_OP_FLAGS_REPLACE 3 /* Replace the attribute */
+#define XFS_ATTRI_OP_FLAGS_PPTR_REMOVE 5 /* Remove parent pointer */
#define XFS_ATTRI_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
/*
@@ -491,6 +491,14 @@ xfs_attri_validate(
return false;
switch (op) {
+ case XFS_ATTRI_OP_FLAGS_PPTR_REMOVE:
+ if (!xfs_has_parent(mp))
+ return false;
+ if (attrp->alfi_value_len == 0)
+ return false;
+ if (!(attrp->alfi_attr_filter & XFS_ATTR_PARENT))
+ return false;
+ fallthrough;
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
if (attrp->alfi_value_len > XATTR_SIZE_MAX)
@@ -595,6 +603,7 @@ xfs_attri_recover_work(
else
attr->xattri_dela_state = xfs_attr_init_add_state(args);
break;
+ case XFS_ATTRI_OP_FLAGS_PPTR_REMOVE:
case XFS_ATTRI_OP_FLAGS_REMOVE:
attr->xattri_dela_state = xfs_attr_init_remove_state(args);
break;
@@ -753,6 +762,36 @@ xfs_attr_defer_add(
trace_xfs_attr_defer_add(new->xattri_dela_state, args->dp);
}
+void
+xfs_attr_defer_parent(
+ struct xfs_da_args *args,
+ enum xfs_attr_defer_op op)
+{
+ struct xfs_attr_intent *new;
+
+ ASSERT(xfs_has_parent(args->dp->i_mount));
+ ASSERT(args->attr_filter & XFS_ATTR_PARENT);
+ ASSERT(args->op_flags & XFS_DA_OP_LOGGED);
+
+ new = kmem_cache_zalloc(xfs_attr_intent_cache, GFP_NOFS | __GFP_NOFAIL);
+ new->xattri_da_args = args;
+
+ switch (op) {
+ case XFS_ATTR_DEFER_SET:
+ case XFS_ATTR_DEFER_REPLACE:
+ /* will be added in subsequent patches */
+ ASSERT(0);
+ break;
+ case XFS_ATTR_DEFER_REMOVE:
+ new->xattri_op_flags = XFS_ATTRI_OP_FLAGS_PPTR_REMOVE;
+ new->xattri_dela_state = xfs_attr_init_remove_state(args);
+ break;
+ }
+
+ xfs_defer_add(args->trans, &new->xattri_list, &xfs_attr_defer_type);
+ trace_xfs_attr_defer_add(new->xattri_dela_state, args->dp);
+}
+
const struct xfs_defer_op_type xfs_attr_defer_type = {
.name = "attr",
.max_items = 1,
@@ -801,6 +840,16 @@ xlog_recover_attri_commit_pass2(
/* Check the number of log iovecs makes sense for the op code. */
op = xfs_attr_log_item_op(attri_formatp);
switch (op) {
+ case XFS_ATTRI_OP_FLAGS_PPTR_REMOVE:
+ /* Log item, attr name, attr value */
+ if (item->ri_total != 3) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+ name_len = attri_formatp->alfi_name_len;
+ value_len = attri_formatp->alfi_value_len;
+ break;
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
/* Log item, attr name, attr value */
@@ -876,12 +925,16 @@ xlog_recover_attri_commit_pass2(
return -EFSCORRUPTED;
}
fallthrough;
+ case XFS_ATTRI_OP_FLAGS_PPTR_REMOVE:
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
/*
* Regular xattr set/remove/replace operations require a name
* and do not take a newname. Values are optional for set and
* replace.
+ *
+ * Name-value remove operations must have a name, do not
+ * take a newname, and can take a value.
*/
if (attr_name == NULL || name_len == 0) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
@@ -58,5 +58,7 @@ enum xfs_attr_defer_op {
};
void xfs_attr_defer_add(struct xfs_da_args *args, enum xfs_attr_defer_op op);
+void xfs_attr_defer_parent(struct xfs_da_args *args,
+ enum xfs_attr_defer_op op);
#endif /* __XFS_ATTR_ITEM_H__ */