// 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 = ((xfs_efi_log_format_t *)buf)->efi_nextents;
	uint dst_len = sizeof(xfs_efi_log_format_t) + (nextents - 1) * sizeof(xfs_extent_t);
	uint len32 = sizeof(xfs_efi_log_format_32_t) + (nextents - 1) * sizeof(xfs_extent_32_t);
	uint len64 = sizeof(xfs_efi_log_format_64_t) + (nextents - 1) * sizeof(xfs_extent_64_t);

	if (len == dst_len || continued) {
		memcpy((char *)dst_efi_fmt, buf, len);
		return 0;
	} else if (len == len32) {
		xfs_efi_log_format_32_t *src_efi_fmt_32 = (xfs_efi_log_format_32_t *)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) {
		xfs_efi_log_format_64_t *src_efi_fmt_64 = (xfs_efi_log_format_64_t *)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)
{
	xfs_efi_log_format_t	*src_f, *f = NULL;
	uint			dst_len;
	xfs_extent_t		*ex;
	int			i;
	int			error = 0;
	int			core_size = offsetof(xfs_efi_log_format_t, efi_extents);

	/*
	 * memmove to ensure 8-byte alignment for the long longs in
	 * xfs_efi_log_format_t structure
	 */
	if ((src_f = (xfs_efi_log_format_t *)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 = sizeof(xfs_efi_log_format_t) + (src_f->efi_nextents - 1) * sizeof(xfs_extent_t);

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

	if ((f = (xfs_efi_log_format_t *)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;
	}

	printf(_("EFI:  #regs: %d	num_extents: %d  id: 0x%llx\n"),
		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: %d) ",
			(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(
	xlog_recover_item_t	*item)
{
	xfs_efi_log_format_t	*f, *src_f;
	xfs_extent_t		*ex;
	int			i;
	uint			src_len, dst_len;

	src_f = (xfs_efi_log_format_t *)item->ri_buf[0].i_addr;
	src_len = item->ri_buf[0].i_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(xfs_efi_log_format_t) +
		(src_f->efi_nextents - 1) * sizeof(xfs_extent_t);
	if ((f = (xfs_efi_log_format_t *)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;
	}

	printf(_("	EFI:  #regs:%d	num_extents:%d  id:0x%llx\n"),
		   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: %d) ",
			(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)
{
	xfs_efd_log_format_t *f;
	xfs_efd_log_format_t lbuf;
	/* size without extents at end */
	uint core_size = sizeof(xfs_efd_log_format_t) - sizeof(xfs_extent_t);

	/*
	 * memmove to ensure 8-byte alignment for the long longs in
	 * xfs_efd_log_format_t structure
	 */
	memmove(&lbuf, *ptr, min(core_size, len));
	f = &lbuf;
	*ptr += len;
	if (len >= core_size) {
		printf(_("EFD:  #regs: %d	num_extents: %d  id: 0x%llx\n"),
			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(
	xlog_recover_item_t	*item)
{
	xfs_efd_log_format_t	*f;

	f = (xfs_efd_log_format_t *)item->ri_buf[0].i_addr;
	/*
	 * 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(_("	EFD:  #regs: %d	num_extents: %d  id: 0x%llx\n"),
		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)
{
	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;
	}

	printf(_("RUI:  #regs: %d	num_extents: %d  id: 0x%llx\n"),
		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].i_addr;
	src_len = item->ri_buf[0].i_len;

	xlog_print_trans_rui(&src_f, src_len, 0);
}

int
xlog_print_trans_rud(
	char				**ptr,
	uint				len)
{
	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_efd_log_format_t structure
	 */
	memmove(&lbuf, *ptr, min(core_size, len));
	f = &lbuf;
	*ptr += len;
	if (len >= core_size) {
		printf(_("RUD:  #regs: %d	                 id: 0x%llx\n"),
			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].i_addr;
	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)
{
	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;
	}

	printf(_("CUI:  #regs: %d	num_extents: %d  id: 0x%llx\n"),
		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].i_addr;
	src_len = item->ri_buf[0].i_len;

	xlog_print_trans_cui(&src_f, src_len, 0);
}

int
xlog_print_trans_cud(
	char				**ptr,
	uint				len)
{
	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;
	*ptr += len;
	if (len >= core_size) {
		printf(_("CUD:  #regs: %d	                 id: 0x%llx\n"),
			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].i_addr;
	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].i_addr;
	src_len = item->ri_buf[0].i_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].i_addr;
	xlog_print_trans_bud(&f, sizeof(struct xfs_bud_log_format));
}
