/*
 * DF-3163 PoC: FIONREAD on an O_RDWR fifo descriptor always reports 0.
 *
 * Root cause: fifo_ioctl() (sys/vfs/fifofs/fifo_vnops.c:436-461) forwards the
 * ioctl to BOTH sockets when the fd is FREAD|FWRITE.  For FIONREAD,
 * soo_ioctl() sets *(int*)data = so->so_rcv.ssb_cc
 * (sys/kern/sys_socket.c:152-155).  The FREAD pass stores the true byte
 * count from the read socket; the FWRITE pass then OVERWRITES it with the
 * write socket's so_rcv.ssb_cc, which is always 0 (the write socket of the
 * internal socket pair never receives anything).
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int
main(void)
{
	const char *path = "/tmp/df3163_fifo";
	int n = -1, n_ctl = -1, rw, rfd, wfd, bad = 0;

	unlink(path);
	if (mkfifo(path, 0600) < 0) {
		perror("mkfifo");
		exit(1);
	}
	/* O_RDWR fifo open never blocks (own counters satisfy the rendezvous) */
	rw = open(path, O_RDWR);
	rfd = open(path, O_RDONLY | O_NONBLOCK);
	wfd = open(path, O_WRONLY);
	if (rw < 0 || rfd < 0 || wfd < 0) {
		perror("open");
		exit(1);
	}
	if (write(wfd, "hello", 5) != 5) {
		perror("write");
		exit(1);
	}
	usleep(100000);

	if (ioctl(rw, FIONREAD, &n) < 0) {
		perror("ioctl(FIONREAD, O_RDWR fd)");
		exit(1);
	}
	if (ioctl(rfd, FIONREAD, &n_ctl) < 0) {
		perror("ioctl(FIONREAD, O_RDONLY fd)");
		exit(1);
	}
	printf("FIONREAD on O_RDWR fd = %d (expected 5)\n", n);
	printf("FIONREAD on O_RDONLY fd = %d (control, expected 5)\n", n_ctl);

	if (n == 0 && n_ctl == 5) {
		printf("BUG-DF-3163: O_RDWR FIONREAD clobbered by the "
		       "write-socket pass\n");
		bad = 1;
	}
	printf("VERDICT: %s\n", bad ? "reproduced" : "not_reproduced");
	unlink(path);
	return (bad ? 0 : 1);
}
