// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2015 Red Hat, Inc.
 * All Rights Reserved.
 */

#include "libxfs.h"
#include "command.h"
#include "init.h"
#include "output.h"
#include "libxlog.h"
#include "logformat.h"

#define MAX_LSUNIT	256 * 1024	/* max log buf. size */

static int
logformat_f(int argc, char **argv)
{
	xfs_daddr_t	head_blk;
	xfs_daddr_t	tail_blk;
	int		logversion;
	int		lsunit = -1;
	int		cycle = -1;
	int		error;
	int		c;

	logversion = xfs_has_logv2(mp) ? 2 : 1;

	while ((c = getopt(argc, argv, "c:s:")) != EOF) {
		switch (c) {
		case 'c':
			cycle = strtol(optarg, NULL, 0);
			if (cycle == 0) {
				dbprintf("invalid cycle\n");
				return -1;
			}
			break;
		case 's':
			lsunit = strtol(optarg, NULL, 0);
			/*
			 * The log stripe unit must be block aligned and no
			 * larger than 256k.
			 */
			if (lsunit > 1 &&
			    (lsunit % mp->m_sb.sb_blocksize ||
			    (logversion == 2 && lsunit > MAX_LSUNIT))) {
				dbprintf("invalid log stripe unit\n");
				return -1;
			}
			break;
		default:
			dbprintf("invalid option\n");
			return -1;
		}
	}

	/*
	 * Check whether the log is dirty. This also determines the current log
	 * cycle if we have to use it by default below.
	 */
	memset(mp->m_log, 0, sizeof(struct xlog));
	mp->m_log->l_mp = mp;
	mp->m_log->l_dev = mp->m_logdev_targp;
	mp->m_log->l_logBBsize = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks);
	mp->m_log->l_logBBstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart);
	mp->m_log->l_sectBBsize = BBSIZE;
	if (xfs_has_sector(mp))
		mp->m_log->l_sectBBsize <<= (mp->m_sb.sb_logsectlog - BBSHIFT);
	mp->m_log->l_sectBBsize = BTOBB(mp->m_log->l_sectBBsize);

	error = xlog_find_tail(mp->m_log, &head_blk, &tail_blk);
	if (error) {
		dbprintf("could not find log head/tail\n");
		return -1;
	}
	if (head_blk != tail_blk) {
		dbprintf(_(
			"The log is dirty. Please mount to replay the log.\n"));
		return -1;
	}

	/*
	 * Use the current cycle and/or log stripe unit if either is not
	 * provided by the user.
	 */
	if (cycle < 0)
		cycle = mp->m_log->l_curr_cycle;
	if (lsunit < 0)
		lsunit = mp->m_sb.sb_logsunit;

	dbprintf("Formatting the log to cycle %d, stripe unit %d bytes.\n",
		 cycle, lsunit);
	error = -libxfs_log_clear(mp->m_logdev_targp, NULL,
				 mp->m_log->l_logBBstart,
				 mp->m_log->l_logBBsize,
				 &mp->m_sb.sb_uuid, logversion, lsunit,
				 XLOG_FMT, cycle, false);
	if (error) {
		dbprintf("error formatting log - %d\n", error);
		return error;
	}

	return 0;
}

static void
logformat_help(void)
{
	dbprintf(_(
"\n"
" The 'logformat' command reformats (clears) the log to the specified log\n"
" cycle and log stripe unit. If the log cycle is not specified, the log is\n"
" reformatted to the current cycle. If the log stripe unit is not specified,\n"
" the stripe unit from the filesystem superblock is used.\n"
"\n"
	));
}

static const struct cmdinfo logformat_cmd = {
	.name =		"logformat",
	.altname =	NULL,
	.cfunc =	logformat_f,
	.argmin =	0,
	.argmax =	4,
	.canpush =	0,
	.args =		N_("[-c cycle] [-s sunit]"),
	.oneline =	N_("reformat the log"),
	.help =		logformat_help,
};

void
logformat_init(void)
{
	if (!expert_mode)
		return;

	add_command(&logformat_cmd);
}

static void
print_logres(
	int			i,
	struct xfs_trans_res	*res)
{
	dbprintf(_("type %d logres %u logcount %d flags 0x%x\n"),
		i, res->tr_logres, res->tr_logcount, res->tr_logflags);
}

static int
logres_f(
	int			argc,
	char			**argv)
{
	struct xfs_trans_res	resv;
	struct xfs_trans_res	*res;
	struct xfs_trans_res	*end_res;
	int			i;

	res = (struct xfs_trans_res *)M_RES(mp);
	end_res = (struct xfs_trans_res *)(M_RES(mp) + 1);
	for (i = 0; res < end_res; i++, res++)
		print_logres(i, res);

	libxfs_log_get_max_trans_res(mp, &resv);
	dbprintf(_("minlogsize logres %u logcount %d\n"),
			resv.tr_logres, resv.tr_logcount);

	return 0;
}

static void
logres_help(void)
{
	dbprintf(_(
"\n"
" The 'logres' command prints information about all log reservation types.\n"
" This includes the reservation space, the intended transaction roll count,\n"
" and the reservation flags, if any.\n"
"\n"
	));
}

static const struct cmdinfo logres_cmd = {
	.name =		"logres",
	.altname =	NULL,
	.cfunc =	logres_f,
	.argmin =	0,
	.argmax =	0,
	.canpush =	0,
	.args =		NULL,
	.oneline =	N_("dump log reservations"),
	.help =		logres_help,
};

void
logres_init(void)
{
	add_command(&logres_cmd);
}
