| // SPDX-License-Identifier: GPL-2.0 | 
 | /* | 
 |  * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. | 
 |  * All Rights Reserved. | 
 |  */ | 
 |  | 
 | #include "libxfs.h" | 
 | #include "command.h" | 
 | #include "output.h" | 
 | #include "init.h" | 
 |  | 
 | #define	M(A)	(1 << CT_ ## A) | 
 | #define	agblock_to_bytes(x)	\ | 
 | 	((uint64_t)(x) << mp->m_sb.sb_blocklog) | 
 | #define	agino_to_bytes(x)	\ | 
 | 	((uint64_t)(x) << mp->m_sb.sb_inodelog) | 
 | #define	agnumber_to_bytes(x)	\ | 
 | 	agblock_to_bytes((uint64_t)(x) * mp->m_sb.sb_agblocks) | 
 | #define	daddr_to_bytes(x)	\ | 
 | 	((uint64_t)(x) << BBSHIFT) | 
 | #define	fsblock_to_bytes(x)	\ | 
 | 	(agnumber_to_bytes(XFS_FSB_TO_AGNO(mp, (x))) + \ | 
 | 	 agblock_to_bytes(XFS_FSB_TO_AGBNO(mp, (x)))) | 
 | #define	ino_to_bytes(x)		\ | 
 | 	(agnumber_to_bytes(XFS_INO_TO_AGNO(mp, (x))) + \ | 
 | 	 agino_to_bytes(XFS_INO_TO_AGINO(mp, (x)))) | 
 | #define	inoidx_to_bytes(x)	\ | 
 | 	((uint64_t)(x) << mp->m_sb.sb_inodelog) | 
 |  | 
 | typedef enum { | 
 | 	CT_NONE = -1, | 
 | 	CT_AGBLOCK,		/* xfs_agblock_t */ | 
 | 	CT_AGINO,		/* xfs_agino_t */ | 
 | 	CT_AGNUMBER,		/* xfs_agno_t */ | 
 | 	CT_BBOFF,		/* byte offset in daddr */ | 
 | 	CT_BLKOFF,		/* byte offset in fsb/agb */ | 
 | 	CT_BYTE,		/* byte in filesystem */ | 
 | 	CT_DADDR,		/* daddr_t */ | 
 | 	CT_FSBLOCK,		/* xfs_fsblock_t */ | 
 | 	CT_INO,			/* xfs_ino_t */ | 
 | 	CT_INOIDX,		/* index of inode in fsblock */ | 
 | 	CT_INOOFF,		/* byte offset in inode */ | 
 | 	NCTS | 
 | } ctype_t; | 
 |  | 
 | typedef struct ctydesc { | 
 | 	ctype_t		ctype; | 
 | 	int		allowed; | 
 | 	const char	**names; | 
 | } ctydesc_t; | 
 |  | 
 | typedef union { | 
 | 	xfs_agblock_t	agblock; | 
 | 	xfs_agino_t	agino; | 
 | 	xfs_agnumber_t	agnumber; | 
 | 	int		bboff; | 
 | 	int		blkoff; | 
 | 	uint64_t	byte; | 
 | 	xfs_daddr_t	daddr; | 
 | 	xfs_fsblock_t	fsblock; | 
 | 	xfs_ino_t	ino; | 
 | 	int		inoidx; | 
 | 	int		inooff; | 
 | } cval_t; | 
 |  | 
 | static uint64_t		bytevalue(ctype_t ctype, cval_t *val); | 
 | static int		convert_f(int argc, char **argv); | 
 | static int		getvalue(char *s, ctype_t ctype, cval_t *val); | 
 | static ctype_t		lookupcty(char *ctyname); | 
 |  | 
 | static const char	*agblock_names[] = { "agblock", "agbno", NULL }; | 
 | static const char	*agino_names[] = { "agino", "aginode", NULL }; | 
 | static const char	*agnumber_names[] = { "agnumber", "agno", NULL }; | 
 | static const char	*bboff_names[] = { "bboff", "daddroff", NULL }; | 
 | static const char	*blkoff_names[] = { "blkoff", "fsboff", "agboff", | 
 | 					    NULL }; | 
 | static const char	*byte_names[] = { "byte", "fsbyte", NULL }; | 
 | static const char	*daddr_names[] = { "daddr", "bb", NULL }; | 
 | static const char	*fsblock_names[] = { "fsblock", "fsb", "fsbno", NULL }; | 
 | static const char	*ino_names[] = { "ino", "inode", NULL }; | 
 | static const char	*inoidx_names[] = { "inoidx", "offset", NULL }; | 
 | static const char	*inooff_names[] = { "inooff", "inodeoff", NULL }; | 
 |  | 
 | static const ctydesc_t	ctydescs[NCTS] = { | 
 | 	{ CT_AGBLOCK, M(AGNUMBER)|M(BBOFF)|M(BLKOFF)|M(INOIDX)|M(INOOFF), | 
 | 	  agblock_names }, | 
 | 	{ CT_AGINO, M(AGNUMBER)|M(INOOFF), agino_names }, | 
 | 	{ CT_AGNUMBER, | 
 | 	  M(AGBLOCK)|M(AGINO)|M(BBOFF)|M(BLKOFF)|M(INOIDX)|M(INOOFF), | 
 | 	  agnumber_names }, | 
 | 	{ CT_BBOFF, M(AGBLOCK)|M(AGNUMBER)|M(DADDR)|M(FSBLOCK), bboff_names }, | 
 | 	{ CT_BLKOFF, M(AGBLOCK)|M(AGNUMBER)|M(FSBLOCK), blkoff_names }, | 
 | 	{ CT_BYTE, 0, byte_names }, | 
 | 	{ CT_DADDR, M(BBOFF), daddr_names }, | 
 | 	{ CT_FSBLOCK, M(BBOFF)|M(BLKOFF)|M(INOIDX), fsblock_names }, | 
 | 	{ CT_INO, M(INOOFF), ino_names }, | 
 | 	{ CT_INOIDX, M(AGBLOCK)|M(AGNUMBER)|M(FSBLOCK)|M(INOOFF), | 
 | 	  inoidx_names }, | 
 | 	{ CT_INOOFF, | 
 | 	  M(AGBLOCK)|M(AGINO)|M(AGNUMBER)|M(FSBLOCK)|M(INO)|M(INOIDX), | 
 | 	  inooff_names }, | 
 | }; | 
 |  | 
 | static const cmdinfo_t	convert_cmd = | 
 | 	{ "convert", NULL, convert_f, 3, 9, 0, "type num [type num]... type", | 
 | 	  "convert from one address form to another", NULL }; | 
 |  | 
 | static uint64_t | 
 | bytevalue(ctype_t ctype, cval_t *val) | 
 | { | 
 | 	switch (ctype) { | 
 | 	case CT_AGBLOCK: | 
 | 		return agblock_to_bytes(val->agblock); | 
 | 	case CT_AGINO: | 
 | 		return agino_to_bytes(val->agino); | 
 | 	case CT_AGNUMBER: | 
 | 		return agnumber_to_bytes(val->agnumber); | 
 | 	case CT_BBOFF: | 
 | 		return (uint64_t)val->bboff; | 
 | 	case CT_BLKOFF: | 
 | 		return (uint64_t)val->blkoff; | 
 | 	case CT_BYTE: | 
 | 		return val->byte; | 
 | 	case CT_DADDR: | 
 | 		return daddr_to_bytes(val->daddr); | 
 | 	case CT_FSBLOCK: | 
 | 		return fsblock_to_bytes(val->fsblock); | 
 | 	case CT_INO: | 
 | 		return ino_to_bytes(val->ino); | 
 | 	case CT_INOIDX: | 
 | 		return inoidx_to_bytes(val->inoidx); | 
 | 	case CT_INOOFF: | 
 | 		return (uint64_t)val->inooff; | 
 | 	case CT_NONE: | 
 | 	case NCTS: | 
 | 		break; | 
 | 	} | 
 | 	/* NOTREACHED */ | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | convert_f(int argc, char **argv) | 
 | { | 
 | 	ctype_t		c; | 
 | 	int		conmask; | 
 | 	cval_t		cvals[NCTS] = {}; | 
 | 	int		i; | 
 | 	int		mask; | 
 | 	uint64_t	v; | 
 | 	ctype_t		wtype; | 
 |  | 
 | 	/* move past the "convert" command */ | 
 | 	argc--; | 
 | 	argv++; | 
 |  | 
 | 	if ((argc % 2) != 1) { | 
 | 		dbprintf(_("bad argument count %d to convert, expected 3,5,7,9 " | 
 | 			 "arguments\n"), argc); | 
 | 		return 0; | 
 | 	} | 
 | 	if ((wtype = lookupcty(argv[argc - 1])) == CT_NONE) { | 
 | 		dbprintf(_("unknown conversion type %s\n"), argv[argc - 1]); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	for (i = mask = conmask = 0; i < (argc - 1) / 2; i++) { | 
 | 		c = lookupcty(argv[i * 2]); | 
 | 		if (c == CT_NONE) { | 
 | 			dbprintf(_("unknown conversion type %s\n"), argv[i * 2]); | 
 | 			return 0; | 
 | 		} | 
 | 		if (c == wtype) { | 
 | 			dbprintf(_("result type same as argument\n")); | 
 | 			return 0; | 
 | 		} | 
 | 		if (conmask & (1 << c)) { | 
 | 			dbprintf(_("conflicting conversion type %s\n"), | 
 | 				argv[i * 2]); | 
 | 			return 0; | 
 | 		} | 
 | 		if (!getvalue(argv[i * 2 + 1], c, &cvals[c])) | 
 | 			return 0; | 
 | 		mask |= 1 << c; | 
 | 		conmask |= ~ctydescs[c].allowed; | 
 | 	} | 
 | 	if (cur_agno != NULLAGNUMBER && (conmask & M(AGNUMBER)) == 0) { | 
 | 		cvals[CT_AGNUMBER].agnumber = cur_agno; | 
 | 		mask |= M(AGNUMBER); | 
 | 	} | 
 | 	v = 0; | 
 | 	for (c = (ctype_t)0; c < NCTS; c++) { | 
 | 		if (!(mask & (1 << c))) | 
 | 			continue; | 
 | 		v += bytevalue(c, &cvals[c]); | 
 | 	} | 
 | 	switch (wtype) { | 
 | 	case CT_AGBLOCK: | 
 | 		v = xfs_daddr_to_agbno(mp, v >> BBSHIFT); | 
 | 		break; | 
 | 	case CT_AGINO: | 
 | 		v = (v >> mp->m_sb.sb_inodelog) % | 
 | 		    XFS_AGB_TO_AGINO(mp, mp->m_sb.sb_agblocks); | 
 | 		break; | 
 | 	case CT_AGNUMBER: | 
 | 		v = xfs_daddr_to_agno(mp, v >> BBSHIFT); | 
 | 		break; | 
 | 	case CT_BBOFF: | 
 | 		v &= BBMASK; | 
 | 		break; | 
 | 	case CT_BLKOFF: | 
 | 		v &= mp->m_blockmask; | 
 | 		break; | 
 | 	case CT_BYTE: | 
 | 		break; | 
 | 	case CT_DADDR: | 
 | 		v >>= BBSHIFT; | 
 | 		break; | 
 | 	case CT_FSBLOCK: | 
 | 		v = XFS_DADDR_TO_FSB(mp, v >> BBSHIFT); | 
 | 		break; | 
 | 	case CT_INO: | 
 | 		v = XFS_AGINO_TO_INO(mp, xfs_daddr_to_agno(mp, v >> BBSHIFT), | 
 | 			(v >> mp->m_sb.sb_inodelog) % | 
 | 			XFS_AGB_TO_AGINO(mp, mp->m_sb.sb_agblocks)); | 
 | 		break; | 
 | 	case CT_INOIDX: | 
 | 		v = (v >> mp->m_sb.sb_inodelog) & (mp->m_sb.sb_inopblock - 1); | 
 | 		break; | 
 | 	case CT_INOOFF: | 
 | 		v &= mp->m_sb.sb_inodesize - 1; | 
 | 		break; | 
 | 	case CT_NONE: | 
 | 	case NCTS: | 
 | 		/* NOTREACHED */ | 
 | 		break; | 
 | 	} | 
 | 	dbprintf("0x%llx (%llu)\n", v, v); | 
 | 	return 0; | 
 | } | 
 |  | 
 | void | 
 | convert_init(void) | 
 | { | 
 | 	add_command(&convert_cmd); | 
 | } | 
 |  | 
 | static int | 
 | getvalue(char *s, ctype_t ctype, cval_t *val) | 
 | { | 
 | 	char		*p; | 
 | 	uint64_t	v; | 
 |  | 
 | 	v = strtoull(s, &p, 0); | 
 | 	if (*p != '\0') { | 
 | 		dbprintf(_("%s is not a number\n"), s); | 
 | 		return 0; | 
 | 	} | 
 | 	switch (ctype) { | 
 | 	case CT_AGBLOCK: | 
 | 		val->agblock = (xfs_agblock_t)v; | 
 | 		break; | 
 | 	case CT_AGINO: | 
 | 		val->agino = (xfs_agino_t)v; | 
 | 		break; | 
 | 	case CT_AGNUMBER: | 
 | 		val->agnumber = (xfs_agnumber_t)v; | 
 | 		break; | 
 | 	case CT_BBOFF: | 
 | 		val->bboff = (int)v; | 
 | 		break; | 
 | 	case CT_BLKOFF: | 
 | 		val->blkoff = (int)v; | 
 | 		break; | 
 | 	case CT_BYTE: | 
 | 		val->byte = (uint64_t)v; | 
 | 		break; | 
 | 	case CT_DADDR: | 
 | 		val->daddr = (xfs_daddr_t)v; | 
 | 		break; | 
 | 	case CT_FSBLOCK: | 
 | 		val->fsblock = (xfs_fsblock_t)v; | 
 | 		break; | 
 | 	case CT_INO: | 
 | 		val->ino = (xfs_ino_t)v; | 
 | 		break; | 
 | 	case CT_INOIDX: | 
 | 		val->inoidx = (int)v; | 
 | 		break; | 
 | 	case CT_INOOFF: | 
 | 		val->inooff = (int)v; | 
 | 		break; | 
 | 	case CT_NONE: | 
 | 	case NCTS: | 
 | 		/* NOTREACHED */ | 
 | 		break; | 
 | 	} | 
 | 	return 1; | 
 | } | 
 |  | 
 | static ctype_t | 
 | lookupcty(char *ctyname) | 
 | { | 
 | 	ctype_t		cty; | 
 | 	const char	**name; | 
 |  | 
 | 	for (cty = (ctype_t)0; cty < NCTS; cty++) { | 
 | 		for (name = ctydescs[cty].names; *name; name++) { | 
 | 			if (strcmp(ctyname, *name) == 0) | 
 | 				return cty; | 
 | 		} | 
 | 	} | 
 | 	return CT_NONE; | 
 | } |