/* DF-5756 — mount the crafted image with KICONV (cs_local=cs_dos=UTF-8) and
 * readdir: shows how the kernel's iconv_ucs handles the unpaired surrogates
 * stored in the LFN entries (sys/libiconv/iconv_ucs.c:255 checks only the
 * 0xd8 high-surrogate prefix; a leading U+DC00-DFFF unit is accepted at
 * :264-266 and only fails later in ucs4_to_utf8 when outbuf is too small,
 * degrading to a silent '?' instead of an invalid-input rejection).
 */
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/iconv.h>
#include <vfs/msdosfs/msdosfsmount.h>

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

int
main(int argc, char **argv)
{
	struct msdosfs_args args;
	char buf[8192];
	long basep = 0;
	int fd, n;

	if (argc != 3)
		errx(1, "usage: %s <dev> <dir>", argv[0]);

	memset(&args, 0, sizeof(args));
	args.fspec = argv[1];
	args.uid = 0;
	args.gid = 0;
	args.mask = 0777 & ALLPERMS;	/* literal mode bits (not umask) */
	args.dirmask = 0777 & ALLPERMS;
	args.flags = MSDOSFSMNT_KICONV | MSDOSFSMNT_LONGNAME;
	strlcpy(args.cs_local, "UTF-8", sizeof(args.cs_local));
	strlcpy(args.cs_dos, "UTF-8", sizeof(args.cs_dos));

	if (mount("msdos", argv[2], 0, &args) < 0)
		err(1, "mount msdos %s", argv[1]);
	printf("[+] mounted %s at %s (KICONV cs_local=UTF-8 cs_dos=UTF-8)\n",
	    argv[1], argv[2]);

	fd = open(argv[2], O_RDONLY);
	if (fd < 0)
		err(1, "open %s", argv[2]);
	printf("--- dirents:\n");
	for (;;) {
		n = getdirentries(fd, buf, sizeof(buf), &basep);
		if (n <= 0)
			break;
		off_t off;
		for (off = 0; off < n;) {
			struct dirent *d = (struct dirent *)(buf + off);
			size_t i;
			printf("    namlen=%d name=\"", d->d_namlen);
			for (i = 0; i < d->d_namlen; i++) {
				unsigned char c = (unsigned char)d->d_name[i];
				if (c >= 0x20 && c < 0x7f)
					putchar(c);
				else
					printf("\\x%02x", c);
			}
			printf("\"\n      hex: ");
			for (i = 0; i < d->d_namlen; i++)
				printf("%02x ", (unsigned char)d->d_name[i]);
			printf("\n");
			off += _DIRENT_DIRSIZ(d);
		}
	}
	close(fd);
	printf("[+] done (unmount from shell)\n");
	return 0;
}
