xfs: log new xattr values for NVREPLACEXXX operations

For NVREPLACEXXX operations, make it possible to log the old and new
attr values, since this variant does VLOOKUP operations.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 95b11ec..3c7fb09 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -430,13 +430,15 @@
 
 	/*
 	 * NVREPLACE operations require the caller to set the old and new names
-	 * explicitly.
+	 * and values explicitly.
 	 */
 	ASSERT(args->new_namelen > 0);
 
 	args->name = args->new_name;
 	args->namelen = args->new_namelen;
 	args->hashval = xfs_da_hashname(args->name, args->namelen);
+	args->value = args->new_value;
+	args->valuelen = args->new_valuelen;
 	return replace_state;
 }
 
diff --git a/libxfs/xfs_da_btree.h b/libxfs/xfs_da_btree.h
index 6132787..3f3313a 100644
--- a/libxfs/xfs_da_btree.h
+++ b/libxfs/xfs_da_btree.h
@@ -60,7 +60,9 @@
 	int		new_namelen;	/* new attr name len */
 	uint8_t		filetype;	/* filetype of inode for directories */
 	void		*value;		/* set of bytes (maybe contain NULLs) */
+	void		*new_value;	/* new xattr value (may contain NULLs) */
 	int		valuelen;	/* length of value */
+	int		new_valuelen;	/* length of new value */
 	unsigned int	attr_filter;	/* XFS_ATTR_{ROOT,SECURE,INCOMPLETE} */
 	unsigned int	attr_flags;	/* XATTR_{CREATE,REPLACE} */
 	xfs_dahash_t	hashval;	/* hash value of name */
diff --git a/libxfs/xfs_log_format.h b/libxfs/xfs_log_format.h
index a1581dc..711654f 100644
--- a/libxfs/xfs_log_format.h
+++ b/libxfs/xfs_log_format.h
@@ -115,11 +115,11 @@
 #define XLOG_REG_TYPE_BUD_FORMAT	26
 #define XLOG_REG_TYPE_ATTRI_FORMAT	27
 #define XLOG_REG_TYPE_ATTRD_FORMAT	28
-#define XLOG_REG_TYPE_ATTR_NAME	29
+#define XLOG_REG_TYPE_ATTR_NAME		29
 #define XLOG_REG_TYPE_ATTR_VALUE	30
 #define XLOG_REG_TYPE_ATTR_NEWNAME	31
-#define XLOG_REG_TYPE_MAX		31
-
+#define XLOG_REG_TYPE_ATTR_NEWVALUE	32
+#define XLOG_REG_TYPE_MAX		32
 
 /*
  * Flags to log operation header
@@ -980,7 +980,13 @@
 struct xfs_attri_log_format {
 	uint16_t	alfi_type;	/* attri log item type */
 	uint16_t	alfi_size;	/* size of this item */
-	uint32_t	__pad;		/* pad to 64 bit aligned */
+
+	/*
+	 * For NVREPLACEXXX, this is the length of the new xattr value.
+	 * alfi_value_len contains the length of the old xattr value.
+	 */
+	uint32_t	alfi_newvalue_len;
+
 	uint64_t	alfi_id;	/* attri identifier */
 	uint64_t	alfi_ino;	/* the inode for this attr operation */
 	uint32_t	alfi_op_flags;	/* marks the op as a set or remove */
diff --git a/logprint/log_redo.c b/logprint/log_redo.c
index af6c62b..c705bb1 100644
--- a/logprint/log_redo.c
+++ b/logprint/log_redo.c
@@ -700,18 +700,22 @@
 	const void	*newname_ptr,
 	unsigned int	newname_len,
 	const void	*value_ptr,
-	unsigned int	value_len)
+	unsigned int	value_len,
+	const void	*newvalue_ptr,
+	unsigned int	newvalue_len)
 {
 	if (newname_ptr && name_ptr) {
 		dump_pptr("OLDNAME", name_ptr, name_len, value_ptr, value_len);
-		dump_pptr("NEWNAME", newname_ptr, newname_len, "", 0);
+		dump_pptr("NEWNAME", newname_ptr, newname_len, newvalue_ptr,
+				newvalue_len);
 		return;
 	}
 
 	if (name_ptr)
 		dump_pptr("NAME", name_ptr, name_len, value_ptr, value_len);
 	if (newname_ptr)
-		dump_pptr("NNAME", newname_ptr, newname_len, NULL, 0);
+		dump_pptr("NNAME", newname_ptr, newname_len, newvalue_ptr,
+				newvalue_len);
 }
 
 static inline unsigned int
@@ -729,9 +733,10 @@
 	struct xfs_attri_log_format	*src_f = NULL;
 	xlog_op_header_t		*head = NULL;
 	void				*name_ptr = NULL, *newname_ptr = NULL;
-	void				*value_ptr = NULL;
+	void				*value_ptr = NULL, *newvalue_ptr = NULL;
 	uint				dst_len;
 	unsigned int			name_len, newname_len = 0;
+	unsigned int			value_len = 0, newvalue_len = 0;
 	int				error = 0;
 
 	dst_len = sizeof(struct xfs_attri_log_format);
@@ -757,21 +762,26 @@
 	if (xfs_attr_log_item_op(src_f) == XFS_ATTRI_OP_FLAGS_NVREPLACEXXX) {
 		name_len = src_f->alfi_oldname_len;
 		newname_len = src_f->alfi_newname_len;
+		value_len = src_f->alfi_value_len;
+		newvalue_len = src_f->alfi_newvalue_len;
 	} else if (xfs_attr_log_item_op(src_f) == XFS_ATTRI_OP_FLAGS_NVREPLACE) {
 		name_len = src_f->alfi_oldname_len;
 		newname_len = src_f->alfi_newname_len;
+		value_len = src_f->alfi_value_len;
 	} else {
 		name_len = src_f->alfi_name_len;
+		value_len = src_f->alfi_value_len;
 	}
 
-	printf(_("ATTRI:  #regs: %d	f: 0x%x, ino: 0x%llx, attr_filter: 0x%x, name_len: %d, newname_len: %d, value_len: %d  id: 0x%llx\n"),
+	printf(_("ATTRI:  #regs: %d	f: 0x%x, ino: 0x%llx, attr_filter: 0x%x, name_len: %d, newname_len: %d, value_len: %d, newvalue_len: %d  id: 0x%llx\n"),
 			src_f->alfi_size,
 			src_f->alfi_op_flags,
 			(unsigned long long)src_f->alfi_ino,
 			src_f->alfi_attr_filter,
 			name_len,
 			newname_len,
-			src_f->alfi_value_len,
+			value_len,
+			newvalue_len,
 			(unsigned long long)src_f->alfi_id);
 
 	if (name_len > 0) {
@@ -798,14 +808,27 @@
 			goto error;
 	}
 
-	if (src_f->alfi_value_len > 0) {
+	if (value_len > 0) {
 		printf(_("\n"));
 		(*i)++;
 		head = (xlog_op_header_t *)*ptr;
 		xlog_print_op_header(head, *i, ptr);
 		value_ptr = *ptr;
-		error = xlog_print_trans_attri_value(ptr, be32_to_cpu(head->oh_len),
-				src_f->alfi_value_len);
+		error = xlog_print_trans_attri_value(ptr,
+				be32_to_cpu(head->oh_len), value_len, "value");
+		if (error)
+			goto error;
+	}
+
+	if (newvalue_len > 0) {
+		printf(_("\n"));
+		(*i)++;
+		head = (xlog_op_header_t *)*ptr;
+		xlog_print_op_header(head, *i, ptr);
+		newvalue_ptr = *ptr;
+		error = xlog_print_trans_attri_value(ptr,
+				be32_to_cpu(head->oh_len), newvalue_len,
+				"newvalue");
 		if (error)
 			goto error;
 	}
@@ -813,7 +836,8 @@
 	if (src_f->alfi_attr_filter & XFS_ATTR_PARENT)
 		dump_pptr_update(name_ptr, name_len,
 				 newname_ptr, newname_len,
-				 value_ptr, src_f->alfi_value_len);
+				 value_ptr, value_len,
+				 newvalue_ptr, newvalue_len);
 error:
 	free(src_f);
 
@@ -838,17 +862,18 @@
 xlog_print_trans_attri_value(
 	char				**ptr,
 	uint				src_len,
-	int				value_len)
+	unsigned int			value_len,
+	const char			*tag)
 {
 	int len = min(value_len, src_len);
 
-	printf(_("ATTRI:  value len:%u\n"), value_len);
+	printf(_("ATTRI:  %s len: %u\n"), tag, value_len);
 	print_or_dump(*ptr, len);
 
 	*ptr += src_len;
 
 	return 0;
-}	/* xlog_print_trans_attri_value */
+}
 
 void
 xlog_recover_print_attri(
@@ -857,8 +882,9 @@
 	struct xfs_attri_log_format	*f, *src_f = NULL;
 	uint				src_len, dst_len;
 	void				*name_ptr = NULL, *newname_ptr = NULL;
-	void				*value_ptr = NULL;
+	void				*value_ptr = NULL, *newvalue_ptr = NULL;
 	unsigned int			name_len, newname_len = 0;
+	unsigned int			value_len = 0, newvalue_len = 0;
 	int				region = 0;
 
 	src_f = (struct xfs_attri_log_format *)item->ri_buf[0].i_addr;
@@ -881,21 +907,26 @@
 	if (xfs_attr_log_item_op(f) == XFS_ATTRI_OP_FLAGS_NVREPLACEXXX) {
 		name_len = f->alfi_oldname_len;
 		newname_len = f->alfi_newname_len;
+		value_len = f->alfi_value_len;
+		newvalue_len = f->alfi_newvalue_len;
 	} else if (xfs_attr_log_item_op(f) == XFS_ATTRI_OP_FLAGS_NVREPLACE) {
 		name_len = f->alfi_oldname_len;
 		newname_len = f->alfi_newname_len;
+		value_len = f->alfi_value_len;
 	} else {
 		name_len = f->alfi_name_len;
+		value_len = f->alfi_value_len;
 	}
 
-	printf(_("ATTRI:  #regs: %d	f: 0x%x, ino: 0x%llx, attr_filter: 0x%x, name_len: %d, newname_len:%d, value_len: %d  id: 0x%llx\n"),
+	printf(_("ATTRI:  #regs: %d	f: 0x%x, ino: 0x%llx, attr_filter: 0x%x, name_len: %d, newname_len:%d, value_len: %d, newvalue_len: %d  id: 0x%llx\n"),
 			f->alfi_size,
 			f->alfi_op_flags,
 			(unsigned long long)f->alfi_ino,
 			f->alfi_attr_filter,
 			name_len,
 			newname_len,
-			f->alfi_value_len,
+			value_len,
+			newvalue_len,
 			(unsigned long long)f->alfi_id);
 
 	if (name_len > 0) {
@@ -913,22 +944,29 @@
 		newname_ptr = item->ri_buf[region].i_addr;
 	}
 
-	if (f->alfi_value_len > 0) {
-		int len = f->alfi_value_len;
-
-		if (len > MAX_ATTR_VAL_PRINT)
-			len = MAX_ATTR_VAL_PRINT;
+	if (value_len > 0) {
+		int	len = min(MAX_ATTR_VAL_PRINT, value_len);
 
 		region++;
-		printf(_("ATTRI:  value len:%u\n"), f->alfi_value_len);
+		printf(_("ATTRI:  value len:%u\n"), value_len);
 		print_or_dump((char *)item->ri_buf[region].i_addr, len);
 		value_ptr = item->ri_buf[region].i_addr;
 	}
 
+	if (newvalue_len > 0) {
+		int	len = min(MAX_ATTR_VAL_PRINT, newvalue_len);
+
+		region++;
+		printf(_("ATTRI:  newvalue len:%u\n"), newvalue_len);
+		print_or_dump((char *)item->ri_buf[region].i_addr, len);
+		newvalue_ptr = item->ri_buf[region].i_addr;
+	}
+
 	if (src_f->alfi_attr_filter & XFS_ATTR_PARENT)
 		dump_pptr_update(name_ptr, name_len,
 				 newname_ptr, newname_len,
-				 value_ptr, src_f->alfi_value_len);
+				 value_ptr, value_len,
+				 newvalue_ptr, newvalue_len);
 
 out:
 	free(f);
diff --git a/logprint/logprint.h b/logprint/logprint.h
index 067226f..51ba061 100644
--- a/logprint/logprint.h
+++ b/logprint/logprint.h
@@ -60,7 +60,8 @@
 
 extern int xlog_print_trans_attri(char **ptr, uint src_len, int *i);
 extern int xlog_print_trans_attri_name(char **ptr, uint src_len, const char *tag);
-extern int xlog_print_trans_attri_value(char **ptr, uint src_len, int value_len);
+extern int xlog_print_trans_attri_value(char **ptr, uint src_len,
+		unsigned int value_len, const char *tag);
 extern void xlog_recover_print_attri(struct xlog_recover_item *item);
 extern int xlog_print_trans_attrd(char **ptr, uint len);
 extern void xlog_recover_print_attrd(struct xlog_recover_item *item);