/*
 * DF-4714 trigger: ONE 32KB write() spanning both data blocks of a
 * RAID-5 stripe (volume sectors [128,192), stripesize 32s, 3 sds:
 * parity ordinal 0, data ordinals 1 and 2).
 *
 * Block 1 pattern: 0xAA   (volume sectors [128,160) -> data sd s1)
 * Block 2 pattern: 0x55   (volume sectors [160,192) -> data sd s2)
 *
 * With s1+s2 both in a "usable-for-write-but-not-up" state (sd_reborn,
 * e.g. after their shared drive flaps down->up), bre5()'s second-bad-
 * subdisk guard at vinumraid5.c:404 only fires when checksdstate()
 * returns non-zero. checksdstate(sd_reborn, WRITE) returns REQUEST_OK,
 * so m.badsdno is silently OVERWRITTEN (line 420) and the request is
 * built as a single-subdisk degraded write: s1's block is read (old
 * data), only s2's NEW user data is XORed into parity, neither data
 * subdisk is written, and the write() returns SUCCESS.
 *
 * Expected (buggy kernel):   write OK, readback returns stale bytes.
 * Expected (fixed kernel):   write fails EIO (two bad data columns).
 */
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define VOL	"/dev/vinum/vol/rv"
#define OFF	(128 * 512)		/* sector 128 */
#define LEN	(64 * 512)		/* 64 sectors = 2 blocks */

unsigned char wbuf[LEN], rbuf[LEN];

int main(void)
{
	int fd, rc, i, bad;

	for (i = 0; i < 32 * 512; i++) wbuf[i] = 0xAA;   /* block 1 */
	for (i = 32 * 512; i < LEN; i++) wbuf[i] = 0x55; /* block 2 */
	memset(rbuf, 0, LEN);

	fd = open(VOL, O_RDWR);
	if (fd < 0) { perror("open"); return 2; }

	rc = lseek(fd, OFF, SEEK_SET);
	if (rc < 0) { perror("lseek"); return 2; }
	rc = write(fd, wbuf, LEN);
	if (rc != LEN) {
		perror("write");
		printf("RESULT: write FAILED (%d) - correct for a fixed kernel\n", rc);
		return 0;
	}
	printf("write: OK (32KB) - buggy kernel accepted a 2-bad-column write\n");

	sleep(1);
	rc = lseek(fd, OFF, SEEK_SET);
	if (rc < 0) { perror("lseek2"); return 2; }
	rc = read(fd, rbuf, LEN);
	if (rc != LEN) {
		perror("read");
		printf("RESULT: read FAILED (%d)\n", rc);
		return 0;
	}
	bad = 0;
	for (i = 0; i < 32 * 512; i++) if (rbuf[i] != 0xAA) { bad = 1; break; }
	for (i = 32 * 512; i < LEN; i++) if (rbuf[i] != 0x55) { bad = 1; break; }
	if (bad) {
		printf("RESULT: *** SILENT DATA LOSS: readback != written ***\n");
		printf("  block1[0..3]=%02x %02x (want aa aa), block2[0..3]=%02x %02x (want 55 55)\n",
		    rbuf[0], rbuf[1], rbuf[32*512], rbuf[32*512+1]);
		return 1;
	}
	printf("RESULT: readback matches (no data loss)\n");
	return 0;
}
