// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
 * Copyright (c) 2016 Oracle, Inc.
 * All Rights Reserved.
 */
#include "libxfs.h"
#include "libxlog.h"

#include "logprint.h"

/* Extent Free Items */

static int
xfs_efi_copy_format(
	char			  *buf,
	uint			  len,
	struct xfs_efi_log_format *dst_efi_fmt,
	int			  continued)
{
	uint i;
	uint nextents = ((struct xfs_efi_log_format *)buf)->efi_nextents;
	uint dst_len = xfs_efi_log_format_sizeof(nextents);
	uint len32 = xfs_efi_log_format32_sizeof(nextents);
	uint len64 = xfs_efi_log_format64_sizeof(nextents);

	if (len == dst_len || continued) {
		memcpy((char *)dst_efi_fmt, buf, len);
		return 0;
	} else if (len == len32) {
		struct xfs_efi_log_format_32 *src_efi_fmt_32 =
			(struct xfs_efi_log_format_32 *)buf;

		dst_efi_fmt->efi_type	 = src_efi_fmt_32->efi_type;
		dst_efi_fmt->efi_size	 = src_efi_fmt_32->efi_size;
		dst_efi_fmt->efi_nextents = src_efi_fmt_32->efi_nextents;
		dst_efi_fmt->efi_id	   = src_efi_fmt_32->efi_id;
		for (i = 0; i < dst_efi_fmt->efi_nextents; i++) {
			dst_efi_fmt->efi_extents[i].ext_start =
				src_efi_fmt_32->efi_extents[i].ext_start;
			dst_efi_fmt->efi_extents[i].ext_len =
				src_efi_fmt_32->efi_extents[i].ext_len;
		}
		return 0;
	} else if (len == len64) {
		struct xfs_efi_log_format_64 *src_efi_fmt_64 =
			(struct xfs_efi_log_format_64 *)buf;

		dst_efi_fmt->efi_type	 = src_efi_fmt_64->efi_type;
		dst_efi_fmt->efi_size	 = src_efi_fmt_64->efi_size;
		dst_efi_fmt->efi_nextents = src_efi_fmt_64->efi_nextents;
		dst_efi_fmt->efi_id	   = src_efi_fmt_64->efi_id;
		for (i = 0; i < dst_efi_fmt->efi_nextents; i++) {
			dst_efi_fmt->efi_extents[i].ext_start =
				src_efi_fmt_64->efi_extents[i].ext_start;
			dst_efi_fmt->efi_extents[i].ext_len =
				src_efi_fmt_64->efi_extents[i].ext_len;
		}
		return 0;
	}
	fprintf(stderr, _("%s: bad size of efi format: %u; expected %u or %u; nextents = %u\n"),
		progname, len, len32, len64, nextents);
	return 1;
}

int
xlog_print_trans_efi(
	char				**ptr,
	uint				src_len,
	int				continued)
{
	const char			*item_name = "EFI?";
	struct xfs_efi_log_format	*src_f, *f = NULL;
	uint				dst_len;
	struct xfs_extent		*ex;
	int				i;
	int				error = 0;
	int				core_size = offsetof(
			struct xfs_efi_log_format, efi_extents);

	/*
	 * memmove to ensure 8-byte alignment for the long longs in
	 * xfs_efi_log_format structure
	 */
	if ((src_f = (struct xfs_efi_log_format *)malloc(src_len)) == NULL) {
		fprintf(stderr, _("%s: xlog_print_trans_efi: malloc failed\n"), progname);
		exit(1);
	}
	memmove((char*)src_f, *ptr, src_len);
	*ptr += src_len;

	/* convert to native format */
	dst_len = xfs_efi_log_format_sizeof(src_f->efi_nextents);

	if (continued && src_len < core_size) {
		printf(_("EFI: Not enough data to decode further\n"));
		error = 1;
		goto error;
	}

	if ((f = (struct xfs_efi_log_format *)malloc(dst_len)) == NULL) {
		fprintf(stderr, _("%s: xlog_print_trans_efi: malloc failed\n"), progname);
		exit(1);
	}
	if (xfs_efi_copy_format((char*)src_f, src_len, f, continued)) {
		error = 1;
		goto error;
	}

	switch (f->efi_type) {
	case XFS_LI_EFI:	item_name = "EFI"; break;
	case XFS_LI_EFI_RT:	item_name = "EFI_RT"; break;
	}

	printf(_("%s:  #regs: %d	num_extents: %u  id: 0x%llx\n"),
			item_name, f->efi_size, f->efi_nextents,
			(unsigned long long)f->efi_id);

	if (continued) {
		printf(_("EFI free extent data skipped (CONTINUE set, no space)\n"));
		goto error;
	}

	ex = f->efi_extents;
	for (i=0; i < f->efi_nextents; i++) {
		printf("(s: 0x%llx, l: %u) ",
			(unsigned long long)ex->ext_start, ex->ext_len);
		if (i % 4 == 3) printf("\n");
		ex++;
	}
	if (i % 4 != 0)
		printf("\n");
error:
	free(src_f);
	free(f);
	return error;
}	/* xlog_print_trans_efi */

void
xlog_recover_print_efi(
	struct xlog_recover_item	*item)
{
	const char			*item_name = "EFI?";
	struct xfs_efi_log_format	*f, *src_f;
	struct xfs_extent		*ex;
	int				i;
	uint				src_len, dst_len;

	src_f = (struct xfs_efi_log_format *)item->ri_buf[0].iov_base;
	src_len = item->ri_buf[0].iov_len;
	/*
	 * An xfs_efi_log_format structure contains a variable length array
	 * as the last field.
	 * Each element is of size xfs_extent_32_t or xfs_extent_64_t.
	 * Need to convert to native format.
	 */
	dst_len = sizeof(struct xfs_efi_log_format) +
		(src_f->efi_nextents) * sizeof(struct xfs_extent);
	if ((f = (struct xfs_efi_log_format *)malloc(dst_len)) == NULL) {
		fprintf(stderr, _("%s: xlog_recover_print_efi: malloc failed\n"),
			progname);
		exit(1);
	}
	if (xfs_efi_copy_format((char*)src_f, src_len, f, 0)) {
		free(f);
		return;
	}

	switch (f->efi_type) {
	case XFS_LI_EFI:	item_name = "EFI"; break;
	case XFS_LI_EFI_RT:	item_name = "EFI_RT"; break;
	}

	printf(_("	%s:  #regs:%d	num_extents:%u  id:0x%llx\n"),
			item_name, f->efi_size, f->efi_nextents,
			(unsigned long long)f->efi_id);
	ex = f->efi_extents;
	printf("	");
	for (i=0; i< f->efi_nextents; i++) {
		printf("(s: 0x%llx, l: %u) ",
			(unsigned long long)ex->ext_start, ex->ext_len);
		if (i % 4 == 3)
			printf("\n");
		ex++;
	}
	if (i % 4 != 0)
		printf("\n");
	free(f);
}

int
xlog_print_trans_efd(
	char				**ptr,
	uint				len)
{
	const char			*item_name = "EFD?";
	struct xfs_efd_log_format	*f;
	struct xfs_efd_log_format	lbuf;

	/* size without extents at end */
	uint core_size = sizeof(struct xfs_efd_log_format);

	/*
	 * memmove to ensure 8-byte alignment for the long longs in
	 * xfs_efd_log_format structure
	 */
	memmove(&lbuf, *ptr, min(core_size, len));
	f = &lbuf;

	switch (f->efd_type) {
	case XFS_LI_EFD:	item_name = "EFD"; break;
	case XFS_LI_EFD_RT:	item_name = "EFD_RT"; break;
	}

	*ptr += len;
	if (len >= core_size) {
		printf(_("%s:  #regs: %d	num_extents: %d  id: 0x%llx\n"),
				item_name, f->efd_size, f->efd_nextents,
				(unsigned long long)f->efd_efi_id);

		/* don't print extents as they are not used */

		return 0;
	} else {
		printf(_("EFD: Not enough data to decode further\n"));
	return 1;
	}
}	/* xlog_print_trans_efd */

void
xlog_recover_print_efd(
	struct xlog_recover_item	*item)
{
	const char			*item_name = "EFD?";
	struct xfs_efd_log_format	*f;

	f = (struct xfs_efd_log_format *)item->ri_buf[0].iov_base;

	switch (f->efd_type) {
	case XFS_LI_EFD:	item_name = "EFD"; break;
	case XFS_LI_EFD_RT:	item_name = "EFD_RT"; break;
	}

	/*
	 * An xfs_efd_log_format structure contains a variable length array
	 * as the last field.
	 * Each element is of size xfs_extent_32_t or xfs_extent_64_t.
	 * However, the extents are never used and won't be printed.
	 */
	printf(_("	%s:  #regs: %d	num_extents: %d  id: 0x%llx\n"),
			item_name, f->efd_size, f->efd_nextents,
			(unsigned long long)f->efd_efi_id);
}

/* Reverse Mapping Update Items */

static int
xfs_rui_copy_format(
	char			  *buf,
	uint			  len,
	struct xfs_rui_log_format *dst_fmt,
	int			  continued)
{
	uint nextents = ((struct xfs_rui_log_format *)buf)->rui_nextents;
	uint dst_len = xfs_rui_log_format_sizeof(nextents);

	if (len == dst_len || continued) {
		memcpy((char *)dst_fmt, buf, len);
		return 0;
	}
	fprintf(stderr, _("%s: bad size of RUI format: %u; expected %u; nextents = %u\n"),
		progname, len, dst_len, nextents);
	return 1;
}

int
xlog_print_trans_rui(
	char			**ptr,
	uint			src_len,
	int			continued)
{
	const char		*item_name = "RUI?";
	struct xfs_rui_log_format	*src_f, *f = NULL;
	uint			dst_len;
	uint			nextents;
	struct xfs_map_extent	*ex;
	int			i;
	int			error = 0;
	int			core_size;

	core_size = offsetof(struct xfs_rui_log_format, rui_extents);

	/*
	 * memmove to ensure 8-byte alignment for the long longs in
	 * struct xfs_rui_log_format structure
	 */
	src_f = malloc(src_len);
	if (src_f == NULL) {
		fprintf(stderr, _("%s: %s: malloc failed\n"),
			progname, __func__);
		exit(1);
	}
	memmove((char*)src_f, *ptr, src_len);
	*ptr += src_len;

	/* convert to native format */
	nextents = src_f->rui_nextents;
	dst_len = xfs_rui_log_format_sizeof(nextents);

	if (continued && src_len < core_size) {
		printf(_("RUI: Not enough data to decode further\n"));
		error = 1;
		goto error;
	}

	f = malloc(dst_len);
	if (f == NULL) {
		fprintf(stderr, _("%s: %s: malloc failed\n"),
			progname, __func__);
		exit(1);
	}
	if (xfs_rui_copy_format((char *)src_f, src_len, f, continued)) {
		error = 1;
		goto error;
	}

	switch (f->rui_type) {
	case XFS_LI_RUI:	item_name = "RUI"; break;
	case XFS_LI_RUI_RT:	item_name = "RUI_RT"; break;
	}

	printf(_("%s:  #regs: %d	num_extents: %d  id: 0x%llx\n"),
			item_name, f->rui_size, f->rui_nextents,
			(unsigned long long)f->rui_id);

	if (continued) {
		printf(_("RUI extent data skipped (CONTINUE set, no space)\n"));
		goto error;
	}

	ex = f->rui_extents;
	for (i=0; i < f->rui_nextents; i++) {
		printf("(s: 0x%llx, l: %d, own: %lld, off: %llu, f: 0x%x) ",
			(unsigned long long)ex->me_startblock, ex->me_len,
			(long long)ex->me_owner,
			(unsigned long long)ex->me_startoff, ex->me_flags);
		printf("\n");
		ex++;
	}
error:
	free(src_f);
	free(f);
	return error;
}

void
xlog_recover_print_rui(
	struct xlog_recover_item	*item)
{
	char				*src_f;
	uint				src_len;

	src_f = item->ri_buf[0].iov_base;
	src_len = item->ri_buf[0].iov_len;

	xlog_print_trans_rui(&src_f, src_len, 0);
}

int
xlog_print_trans_rud(
	char				**ptr,
	uint				len)
{
	const char			*item_name = "RUD?";
	struct xfs_rud_log_format	*f;
	struct xfs_rud_log_format	lbuf;

	/* size without extents at end */
	uint core_size = sizeof(struct xfs_rud_log_format);

	/*
	 * memmove to ensure 8-byte alignment for the long longs in
	 * xfs_rud_log_format structure
	 */
	memmove(&lbuf, *ptr, min(core_size, len));
	f = &lbuf;

	switch (f->rud_type) {
	case XFS_LI_RUD:	item_name = "RUD"; break;
	case XFS_LI_RUD_RT:	item_name = "RUD_RT"; break;
	}

	*ptr += len;
	if (len >= core_size) {
		printf(_("%s:  #regs: %d	                 id: 0x%llx\n"),
				item_name, f->rud_size,
				(unsigned long long)f->rud_rui_id);

		/* don't print extents as they are not used */

		return 0;
	} else {
		printf(_("RUD: Not enough data to decode further\n"));
		return 1;
	}
}

void
xlog_recover_print_rud(
	struct xlog_recover_item	*item)
{
	char				*f;

	f = item->ri_buf[0].iov_base;
	xlog_print_trans_rud(&f, sizeof(struct xfs_rud_log_format));
}

/* Reference Count Update Items */

static int
xfs_cui_copy_format(
	struct xfs_cui_log_format *cui,
	uint			  len,
	struct xfs_cui_log_format *dst_fmt,
	int			  continued)
{
	uint nextents;
	uint dst_len;

	nextents = cui->cui_nextents;
	dst_len = xfs_cui_log_format_sizeof(nextents);

	if (len == dst_len || continued) {
		memcpy(dst_fmt, cui, len);
		return 0;
	}
	fprintf(stderr, _("%s: bad size of CUI format: %u; expected %u; nextents = %u\n"),
		progname, len, dst_len, nextents);
	return 1;
}

int
xlog_print_trans_cui(
	char			**ptr,
	uint			src_len,
	int			continued)
{
	const char		*item_name = "CUI?";
	struct xfs_cui_log_format	*src_f, *f = NULL;
	uint			dst_len;
	uint			nextents;
	struct xfs_phys_extent	*ex;
	int			i;
	int			error = 0;
	int			core_size;

	core_size = offsetof(struct xfs_cui_log_format, cui_extents);

	src_f = malloc(src_len);
	if (src_f == NULL) {
		fprintf(stderr, _("%s: %s: malloc failed\n"),
			progname, __func__);
		exit(1);
	}
	memcpy(src_f, *ptr, src_len);
	*ptr += src_len;

	/* convert to native format */
	nextents = src_f->cui_nextents;
	dst_len = xfs_cui_log_format_sizeof(nextents);

	if (continued && src_len < core_size) {
		printf(_("CUI: Not enough data to decode further\n"));
		error = 1;
		goto error;
	}

	f = malloc(dst_len);
	if (f == NULL) {
		fprintf(stderr, _("%s: %s: malloc failed\n"),
			progname, __func__);
		exit(1);
	}
	if (xfs_cui_copy_format(src_f, src_len, f, continued)) {
		error = 1;
		goto error;
	}

	switch (f->cui_type) {
	case XFS_LI_CUI:	item_name = "CUI"; break;
	case XFS_LI_CUI_RT:	item_name = "CUI_RT"; break;
	}

	printf(_("%s:  #regs: %d	num_extents: %d  id: 0x%llx\n"),
			item_name, f->cui_size, f->cui_nextents,
			(unsigned long long)f->cui_id);

	if (continued) {
		printf(_("CUI extent data skipped (CONTINUE set, no space)\n"));
		goto error;
	}

	ex = f->cui_extents;
	for (i=0; i < f->cui_nextents; i++) {
		printf("(s: 0x%llx, l: %d, f: 0x%x) ",
			(unsigned long long)ex->pe_startblock, ex->pe_len,
			ex->pe_flags);
		printf("\n");
		ex++;
	}
error:
	free(src_f);
	free(f);
	return error;
}

void
xlog_recover_print_cui(
	struct xlog_recover_item	*item)
{
	char				*src_f;
	uint				src_len;

	src_f = item->ri_buf[0].iov_base;
	src_len = item->ri_buf[0].iov_len;

	xlog_print_trans_cui(&src_f, src_len, 0);
}

int
xlog_print_trans_cud(
	char				**ptr,
	uint				len)
{
	const char			*item_name = "CUD?";
	struct xfs_cud_log_format	*f;
	struct xfs_cud_log_format	lbuf;

	/* size without extents at end */
	uint core_size = sizeof(struct xfs_cud_log_format);

	memcpy(&lbuf, *ptr, min(core_size, len));
	f = &lbuf;

	switch (f->cud_type) {
	case XFS_LI_CUD:	item_name = "CUD"; break;
	case XFS_LI_CUD_RT:	item_name = "CUD_RT"; break;
	}

	*ptr += len;
	if (len >= core_size) {
		printf(_("%s:  #regs: %d	                 id: 0x%llx\n"),
				item_name, f->cud_size,
				(unsigned long long)f->cud_cui_id);

		/* don't print extents as they are not used */

		return 0;
	} else {
		printf(_("CUD: Not enough data to decode further\n"));
		return 1;
	}
}

void
xlog_recover_print_cud(
	struct xlog_recover_item	*item)
{
	char				*f;

	f = item->ri_buf[0].iov_base;
	xlog_print_trans_cud(&f, sizeof(struct xfs_cud_log_format));
}

/* Block Mapping Update Items */

static int
xfs_bui_copy_format(
	struct xfs_bui_log_format *bui,
	uint			  len,
	struct xfs_bui_log_format *dst_fmt,
	int			  continued)
{
	uint nextents;
	uint dst_len;

	nextents = bui->bui_nextents;
	dst_len = xfs_bui_log_format_sizeof(nextents);

	if (len == dst_len || continued) {
		memcpy(dst_fmt, bui, len);
		return 0;
	}
	fprintf(stderr, _("%s: bad size of BUI format: %u; expected %u; nextents = %u\n"),
		progname, len, dst_len, nextents);
	return 1;
}

int
xlog_print_trans_bui(
	char			**ptr,
	uint			src_len,
	int			continued)
{
	struct xfs_bui_log_format	*src_f, *f = NULL;
	uint			dst_len;
	uint			nextents;
	struct xfs_map_extent	*ex;
	int			i;
	int			error = 0;
	int			core_size;

	core_size = offsetof(struct xfs_bui_log_format, bui_extents);

	src_f = malloc(src_len);
	if (src_f == NULL) {
		fprintf(stderr, _("%s: %s: malloc failed\n"),
			progname, __func__);
		exit(1);
	}
	memcpy(src_f, *ptr, src_len);
	*ptr += src_len;

	/* convert to native format */
	nextents = src_f->bui_nextents;
	dst_len = xfs_bui_log_format_sizeof(nextents);

	if (continued && src_len < core_size) {
		printf(_("BUI: Not enough data to decode further\n"));
		error = 1;
		goto error;
	}

	f = malloc(dst_len);
	if (f == NULL) {
		fprintf(stderr, _("%s: %s: malloc failed\n"),
			progname, __func__);
		exit(1);
	}
	if (xfs_bui_copy_format(src_f, src_len, f, continued)) {
		error = 1;
		goto error;
	}

	printf(_("BUI:  #regs: %d	num_extents: %d  id: 0x%llx\n"),
		f->bui_size, f->bui_nextents, (unsigned long long)f->bui_id);

	if (continued) {
		printf(_("BUI extent data skipped (CONTINUE set, no space)\n"));
		goto error;
	}

	ex = f->bui_extents;
	for (i=0; i < f->bui_nextents; i++) {
		printf("(s: 0x%llx, l: %d, own: %lld, off: %llu, f: 0x%x) ",
			(unsigned long long)ex->me_startblock, ex->me_len,
			(long long)ex->me_owner,
			(unsigned long long)ex->me_startoff, ex->me_flags);
		printf("\n");
		ex++;
	}
error:
	free(src_f);
	free(f);
	return error;
}

void
xlog_recover_print_bui(
	struct xlog_recover_item	*item)
{
	char				*src_f;
	uint				src_len;

	src_f = item->ri_buf[0].iov_base;
	src_len = item->ri_buf[0].iov_len;

	xlog_print_trans_bui(&src_f, src_len, 0);
}

int
xlog_print_trans_bud(
	char				**ptr,
	uint				len)
{
	struct xfs_bud_log_format	*f;
	struct xfs_bud_log_format	lbuf;

	/* size without extents at end */
	uint core_size = sizeof(struct xfs_bud_log_format);

	memcpy(&lbuf, *ptr, min(core_size, len));
	f = &lbuf;
	*ptr += len;
	if (len >= core_size) {
		printf(_("BUD:  #regs: %d	                 id: 0x%llx\n"),
			f->bud_size,
			(unsigned long long)f->bud_bui_id);

		/* don't print extents as they are not used */

		return 0;
	} else {
		printf(_("BUD: Not enough data to decode further\n"));
		return 1;
	}
}

void
xlog_recover_print_bud(
	struct xlog_recover_item	*item)
{
	char				*f;

	f = item->ri_buf[0].iov_base;
	xlog_print_trans_bud(&f, sizeof(struct xfs_bud_log_format));
}

/* Attr Items */

static int
xfs_attri_copy_log_format(
	char				*buf,
	uint				len,
	struct xfs_attri_log_format	*dst_attri_fmt)
{
	uint dst_len = sizeof(struct xfs_attri_log_format);

	if (len == dst_len) {
		memcpy((char *)dst_attri_fmt, buf, len);
		return 0;
	}

	fprintf(stderr, _("%s: bad size of attri format: %u; expected %u\n"),
		progname, len, dst_len);
	return 1;
}

static void
dump_pptr(
	const char			*tag,
	const void			*name_ptr,
	unsigned int			name_len,
	const void			*value_ptr,
	unsigned int			value_len)
{
	const struct xfs_parent_rec	*rec = value_ptr;

	if (value_len < sizeof(struct xfs_parent_rec)) {
		printf("PPTR: %s CORRUPT\n", tag);
		return;
	}

	printf("PPTR: %s attr_namelen %u attr_valuelen %u\n", tag, name_len, value_len);
	printf("PPTR: %s parent_ino %llu parent_gen %u name '%.*s'\n",
			tag,
			(unsigned long long)be64_to_cpu(rec->p_ino),
			(unsigned int)be32_to_cpu(rec->p_gen),
			name_len,
			(char *)name_ptr);
}

static void
dump_pptr_update(
	const void	*name_ptr,
	unsigned int	name_len,
	const void	*new_name_ptr,
	unsigned int	new_name_len,
	const void	*value_ptr,
	unsigned int	value_len,
	const void	*new_value_ptr,
	unsigned int	new_value_len)
{
	if (new_name_ptr && name_ptr) {
		dump_pptr("OLDNAME", name_ptr, name_len, value_ptr, value_len);
		dump_pptr("NEWNAME", new_name_ptr, new_name_len, new_value_ptr,
				new_value_len);
		return;
	}

	if (name_ptr)
		dump_pptr("NAME", name_ptr, name_len, value_ptr, value_len);
	if (new_name_ptr)
		dump_pptr("NEWNAME", new_name_ptr, new_name_len, new_value_ptr,
				new_value_len);
}

static inline unsigned int
xfs_attr_log_item_op(const struct xfs_attri_log_format *attrp)
{
	return attrp->alfi_op_flags & XFS_ATTRI_OP_FLAGS_TYPE_MASK;
}

int
xlog_print_trans_attri(
	char				**ptr,
	uint				src_len,
	int				*i)
{
	struct xfs_attri_log_format	*src_f = NULL;
	struct xlog_op_header		*head = NULL;
	void				*name_ptr = NULL;
	void				*new_name_ptr = NULL;
	void				*value_ptr = NULL;
	void				*new_value_ptr = NULL;
	uint				dst_len;
	unsigned int			name_len = 0;
	unsigned int			new_name_len = 0;
	unsigned int			value_len = 0;
	unsigned int			new_value_len = 0;
	int				error = 0;

	dst_len = sizeof(struct xfs_attri_log_format);
	if (src_len != dst_len) {
		fprintf(stderr, _("%s: bad size of attri format: %u; expected %u\n"),
				progname, src_len, dst_len);
		return 1;
	}

	/*
	 * memmove to ensure 8-byte alignment for the long longs in
	 * xfs_attri_log_format_t structure
	 */
	src_f = malloc(src_len);
	if (!src_f) {
		fprintf(stderr, _("%s: xlog_print_trans_attri: malloc failed\n"),
				progname);
		exit(1);
	}
	memmove((char*)src_f, *ptr, src_len);
	*ptr += src_len;

	if (xfs_attr_log_item_op(src_f) == XFS_ATTRI_OP_FLAGS_PPTR_REPLACE) {
		name_len      = src_f->alfi_old_name_len;
		new_name_len  = src_f->alfi_new_name_len;
		value_len     = src_f->alfi_value_len;
		new_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, igen: 0x%x, attr_filter: 0x%x, name_len: %u, new_name_len: %u, value_len: %u, new_value_len: %u  id: 0x%llx\n"),
			src_f->alfi_size,
			src_f->alfi_op_flags,
			(unsigned long long)src_f->alfi_ino,
			(unsigned int)src_f->alfi_igen,
			src_f->alfi_attr_filter,
			name_len,
			new_name_len,
			value_len,
			new_value_len,
			(unsigned long long)src_f->alfi_id);

	if (name_len > 0) {
		printf(_("\n"));
		(*i)++;
		head = (struct xlog_op_header *)*ptr;
		xlog_print_op_header(head, *i, ptr);
		name_ptr = *ptr;
		error = xlog_print_trans_attri_name(ptr,
				be32_to_cpu(head->oh_len), "name");
		if (error)
			goto error;
	}

	if (new_name_len > 0) {
		printf(_("\n"));
		(*i)++;
		head = (struct xlog_op_header *)*ptr;
		xlog_print_op_header(head, *i, ptr);
		new_name_ptr = *ptr;
		error = xlog_print_trans_attri_name(ptr,
				be32_to_cpu(head->oh_len), "newname");
		if (error)
			goto error;
	}

	if (value_len > 0) {
		printf(_("\n"));
		(*i)++;
		head = (struct xlog_op_header *)*ptr;
		xlog_print_op_header(head, *i, ptr);
		value_ptr = *ptr;
		error = xlog_print_trans_attri_value(ptr,
				be32_to_cpu(head->oh_len), value_len, "value");
		if (error)
			goto error;
	}

	if (new_value_len > 0) {
		printf(_("\n"));
		(*i)++;
		head = (struct xlog_op_header *)*ptr;
		xlog_print_op_header(head, *i, ptr);
		new_value_ptr = *ptr;
		error = xlog_print_trans_attri_value(ptr,
				be32_to_cpu(head->oh_len), new_value_len,
				"newvalue");
		if (error)
			goto error;
	}

	if (src_f->alfi_attr_filter & XFS_ATTR_PARENT)
		dump_pptr_update(name_ptr, name_len,
				 new_name_ptr, new_name_len,
				 value_ptr, value_len,
				 new_value_ptr, new_value_len);
error:
	free(src_f);

	return error;
}	/* xlog_print_trans_attri */

int
xlog_print_trans_attri_name(
	char				**ptr,
	uint				src_len,
	const char			*tag)
{
	printf(_("ATTRI:  %s len:%u\n"), tag, src_len);
	print_or_dump(*ptr, src_len);

	*ptr += src_len;

	return 0;
}

int
xlog_print_trans_attri_value(
	char				**ptr,
	uint				src_len,
	int				value_len,
	const char			*tag)
{
	int len = min(value_len, src_len);

	printf(_("ATTRI:  %s len:%u\n"), tag, value_len);
	print_or_dump(*ptr, len);

	*ptr += src_len;

	return 0;
}

void
xlog_recover_print_attri(
	struct xlog_recover_item	*item)
{
	struct xfs_attri_log_format	*f, *src_f = NULL;
	void				*name_ptr = NULL;
	void				*new_name_ptr = NULL;
	void				*value_ptr = NULL;
	void				*new_value_ptr = NULL;
	uint				src_len, dst_len;
	unsigned int			name_len = 0;
	unsigned int			new_name_len = 0;
	unsigned int			value_len = 0;
	unsigned int			new_value_len = 0;
	int				region = 0;

	src_f = (struct xfs_attri_log_format *)item->ri_buf[0].iov_base;
	src_len = item->ri_buf[region].iov_len;

	/*
	 * An xfs_attri_log_format structure contains a attribute name and
	 * variable length value  as the last field.
	 */
	dst_len = sizeof(struct xfs_attri_log_format);

	if ((f = ((struct xfs_attri_log_format *)malloc(dst_len))) == NULL) {
		fprintf(stderr, _("%s: xlog_recover_print_attri: malloc failed\n"),
			progname);
		exit(1);
	}
	if (xfs_attri_copy_log_format((char*)src_f, src_len, f))
		goto out;

	if (xfs_attr_log_item_op(f) == XFS_ATTRI_OP_FLAGS_PPTR_REPLACE) {
		name_len      = f->alfi_old_name_len;
		new_name_len  = f->alfi_new_name_len;
		value_len     = f->alfi_value_len;
		new_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, igen: 0x%x, attr_filter: 0x%x, name_len: %u, new_name_len: %u, value_len: %d, new_value_len: %u  id: 0x%llx\n"),
			f->alfi_size,
			f->alfi_op_flags,
			(unsigned long long)f->alfi_ino,
			(unsigned int)f->alfi_igen,
			f->alfi_attr_filter,
			name_len,
			new_name_len,
			value_len,
			new_value_len,
			(unsigned long long)f->alfi_id);

	if (name_len > 0) {
		region++;
		printf(_("ATTRI:  name len:%u\n"), name_len);
		print_or_dump((char *)item->ri_buf[region].iov_base,
			       name_len);
		name_ptr = item->ri_buf[region].iov_base;
	}

	if (new_name_len > 0) {
		region++;
		printf(_("ATTRI:  newname len:%u\n"), new_name_len);
		print_or_dump((char *)item->ri_buf[region].iov_base,
			       new_name_len);
		new_name_ptr = item->ri_buf[region].iov_base;
	}

	if (value_len > 0) {
		int	len = min(MAX_ATTR_VAL_PRINT, value_len);

		region++;
		printf(_("ATTRI:  value len:%u\n"), value_len);
		print_or_dump((char *)item->ri_buf[region].iov_base, len);
		value_ptr = item->ri_buf[region].iov_base;
	}

	if (new_value_len > 0) {
		int	len = min(MAX_ATTR_VAL_PRINT, new_value_len);

		region++;
		printf(_("ATTRI:  newvalue len:%u\n"), new_value_len);
		print_or_dump((char *)item->ri_buf[region].iov_base, len);
		new_value_ptr = item->ri_buf[region].iov_base;
	}

	if (src_f->alfi_attr_filter & XFS_ATTR_PARENT)
		dump_pptr_update(name_ptr, name_len,
				 new_name_ptr, new_name_len,
				 value_ptr, value_len,
				 new_value_ptr, new_value_len);

out:
	free(f);

}

int
xlog_print_trans_attrd(char **ptr, uint len)
{
	struct xfs_attrd_log_format *f;
	struct xfs_attrd_log_format lbuf;
	uint core_size = sizeof(struct xfs_attrd_log_format);

	memcpy(&lbuf, *ptr, MIN(core_size, len));
	f = &lbuf;
	*ptr += len;
	if (len >= core_size) {
		printf(_("ATTRD:  #regs: %d	id: 0x%llx\n"),
			f->alfd_size,
			(unsigned long long)f->alfd_alf_id);
		return 0;
	} else {
		printf(_("ATTRD: Not enough data to decode further\n"));
		return 1;
	}
}	/* xlog_print_trans_attrd */

void
xlog_recover_print_attrd(
	struct xlog_recover_item		*item)
{
	struct xfs_attrd_log_format	*f;

	f = (struct xfs_attrd_log_format *)item->ri_buf[0].iov_base;

	printf(_("	ATTRD:  #regs: %d	id: 0x%llx\n"),
		f->alfd_size,
		(unsigned long long)f->alfd_alf_id);
}

/* Atomic Extent Swapping Items */

static int
xfs_xmi_copy_format(
	struct xfs_xmi_log_format *xmi,
	uint			  len,
	struct xfs_xmi_log_format *dst_fmt,
	int			  continued)
{
	if (len == sizeof(struct xfs_xmi_log_format) || continued) {
		memcpy(dst_fmt, xmi, len);
		return 0;
	}
	fprintf(stderr, _("%s: bad size of XMI format: %u; expected %zu\n"),
		progname, len, sizeof(struct xfs_xmi_log_format));
	return 1;
}

int
xlog_print_trans_xmi(
	char			**ptr,
	uint			src_len,
	int			continued)
{
	struct xfs_xmi_log_format *src_f, *f = NULL;
	int			error = 0;

	src_f = malloc(src_len);
	if (src_f == NULL) {
		fprintf(stderr, _("%s: %s: malloc failed\n"),
			progname, __func__);
		exit(1);
	}
	memcpy(src_f, *ptr, src_len);
	*ptr += src_len;

	/* convert to native format */
	if (continued && src_len < sizeof(struct xfs_xmi_log_format)) {
		printf(_("XMI: Not enough data to decode further\n"));
		error = 1;
		goto error;
	}

	f = malloc(sizeof(struct xfs_xmi_log_format));
	if (f == NULL) {
		fprintf(stderr, _("%s: %s: malloc failed\n"),
			progname, __func__);
		exit(1);
	}
	if (xfs_xmi_copy_format(src_f, src_len, f, continued)) {
		error = 1;
		goto error;
	}

	printf(_("XMI:  #regs: %d	num_extents: 1  id: 0x%llx\n"),
		f->xmi_size, (unsigned long long)f->xmi_id);

	if (continued) {
		printf(_("XMI extent data skipped (CONTINUE set, no space)\n"));
		goto error;
	}

	printf("(ino1: 0x%llx, igen1: 0x%x, ino2: 0x%llx, igen2: 0x%x, off1: %lld, off2: %lld, len: %lld, flags: 0x%llx)\n",
		(unsigned long long)f->xmi_inode1,
		(unsigned int)f->xmi_igen1,
		(unsigned long long)f->xmi_inode2,
		(unsigned int)f->xmi_igen2,
		(unsigned long long)f->xmi_startoff1,
		(unsigned long long)f->xmi_startoff2,
		(unsigned long long)f->xmi_blockcount,
		(unsigned long long)f->xmi_flags);
error:
	free(src_f);
	free(f);
	return error;
}

void
xlog_recover_print_xmi(
	struct xlog_recover_item	*item)
{
	char				*src_f;
	uint				src_len;

	src_f = item->ri_buf[0].iov_base;
	src_len = item->ri_buf[0].iov_len;

	xlog_print_trans_xmi(&src_f, src_len, 0);
}

int
xlog_print_trans_xmd(
	char				**ptr,
	uint				len)
{
	struct xfs_xmd_log_format	*f;
	struct xfs_xmd_log_format	lbuf;

	/* size without extents at end */
	uint core_size = sizeof(struct xfs_xmd_log_format);

	memcpy(&lbuf, *ptr, min(core_size, len));
	f = &lbuf;
	*ptr += len;
	if (len >= core_size) {
		printf(_("XMD:  #regs: %d	                 id: 0x%llx\n"),
			f->xmd_size,
			(unsigned long long)f->xmd_xmi_id);

		/* don't print extents as they are not used */

		return 0;
	} else {
		printf(_("XMD: Not enough data to decode further\n"));
		return 1;
	}
}

void
xlog_recover_print_xmd(
	struct xlog_recover_item	*item)
{
	char				*f;

	f = item->ri_buf[0].iov_base;
	xlog_print_trans_xmd(&f, sizeof(struct xfs_xmd_log_format));
}
