/* readboth --- read the md0 device and the disks that make it up, and
** 	compare the blocks
*/

# include <fcntl.h>
# include <stdio.h>
# include <errno.h>
# include <string.h>
# include <stdlib.h>

# define BLOCK_SIZE				(32*1024)

# define RAID5_ALGORITHM_LEFT_ASYMMETRIC 	(0)
# define RAID5_ALGORITHM_RIGHT_ASYMMETRIC 	(1)
# define RAID5_ALGORITHM_LEFT_SYMMETRIC 	(2)
# define RAID5_ALGORITHM_RIGHT_SYMMETRIC 	(3)

# define RAID_DISKS				(4)
# define DATA_DISKS				(3)

char *progname = "readboth";

typedef struct {
	char	*dev_name;
	int	fd;
} raid_tab_t;

static raid_tab_t raid_tab[RAID_DISKS] = {
	{ "/dev/hda12", 0 },
	{ "/dev/hda13", 0 },
	{ "/dev/hda14", 0 },
	{ "/dev/hda15", 0 },
};

static void init_array (void);
static void read_md (unsigned long blockno);
static void read_array (unsigned long blockno);
static unsigned long raid5_compute_block (unsigned long r_sector, 
	unsigned int raid_disks, unsigned int data_disks, unsigned int *dd_idx,
	unsigned int *pd_idx, int blocks_per_chunk);

main ()
{
	unsigned long block;
	int dd_idx, pd_idx;
	int blocks_per_chunk;

	init_array ();

	system ("/sbin/raidstart /dev/md0");
	for (block = 0; block < 20; block++)
		read_md (block);

	system ("/sbin/raidstop /dev/md0");
	for (block = 0; block < 20; block++)
		read_array (block);

	exit (0);
}

static void
init_array (void)
{
	int i;
	raid_tab_t *rp;

	for (rp = raid_tab; rp < raid_tab + RAID_DISKS; rp++) {
		rp->fd = open (rp->dev_name, O_RDONLY);
		if (rp->fd < 0) {
			fprintf (stderr, "%s: can't open %s for reading (%s)\n",
			    progname, rp->dev_name, strerror (errno));
			exit (1);
		}
	}
}

static void
read_md (unsigned long blockno)
{
	static int fd = -1;
	long buf[BLOCK_SIZE / sizeof (long)];

	if (fd == -1) {
		fd = open ("/dev/md0", O_RDONLY);
		if (fd < 0) {
			fprintf (stderr, "%s: can't open %s for reading (%s)\n",
			    progname, "/dev/md0", strerror (errno));
			exit (2);
		}
	}

	if (lseek (fd, (long) (blockno * BLOCK_SIZE), SEEK_SET) < 0) {
		fprintf (stderr, "%s: lseek on /dev/md0 failed (%s)\n",
		    progname, strerror (errno));
		exit (3);
	}

	if (read (fd, (char *) buf, sizeof (buf)) < 0) {
		fprintf (stderr, "%s: read on /dev/md0 failed (%s)\n",
		    progname, strerror (errno));
		exit (4);
	}

	printf ("   read_md: block %lu, *block = %d\n", blockno, *buf);

}

static void
read_array (unsigned long blockno)
{
	long buf[BLOCK_SIZE / sizeof (long)];
	unsigned long disk_blockno;
	int dd_idx, pd_idx, fd;

	disk_blockno = raid5_compute_block (blockno, RAID_DISKS, DATA_DISKS, 
	    &dd_idx, &pd_idx, 1);

	fd = raid_tab[dd_idx].fd;

	if (lseek (fd, (long) (disk_blockno * BLOCK_SIZE), SEEK_SET) < 0) {
		fprintf (stderr, "%s: lseek on %s failed (%s)\n",
		    progname, raid_tab[dd_idx].dev_name, strerror (errno));
		exit (3);
	}

	if (read (fd, (char *) buf, sizeof (buf)) < 0) {
		fprintf (stderr, "%s: read on %s failed (%s)\n",
		    progname, raid_tab[dd_idx].dev_name, strerror (errno));
		exit (4);
	}

	printf ("read_array: block %lu, *block = %d\n", blockno, *buf);

}

static unsigned long
raid5_compute_block (unsigned long r_sector, 
	unsigned int raid_disks,
	unsigned int data_disks, 
	unsigned int *dd_idx,
	unsigned int *pd_idx, int blocks_per_chunk)
{
	unsigned long stripe;
	unsigned long chunk_number;
	unsigned int chunk_offset;
	unsigned long new_sector;
	int algorithm = RAID5_ALGORITHM_LEFT_SYMMETRIC;

# ifdef TESTING
# define PU(V)	printf ("%s = %6lu ", #V, V)
# else
# define PU(V)
# endif /* TESTING */

	PU (r_sector);

	/* First compute the information on this sector */

	/*
	 * Compute the chunk number and the sector offset inside the chunk
	 */
	chunk_number = r_sector / blocks_per_chunk;
	chunk_offset = r_sector % blocks_per_chunk;

#if 0
	PU (chunk_number);
	PU (chunk_offset);
#endif

	/*
	 * Compute the stripe number
	 */
	stripe = chunk_number / data_disks;

#if 0
	PU (stripe);
#endif

	/*
	 * Compute the data disk and parity disk indexes inside the stripe
	 */
	*dd_idx = chunk_number % data_disks;

	/*
	 * Select the parity disk based on the user selected algorithm.
	 */
	switch (algorithm) {
	case RAID5_ALGORITHM_LEFT_ASYMMETRIC:
		*pd_idx = data_disks - stripe % raid_disks;
		if (*dd_idx >= *pd_idx)
			(*dd_idx)++;
		break;
	case RAID5_ALGORITHM_RIGHT_ASYMMETRIC:
		*pd_idx = stripe % raid_disks;
		if (*dd_idx >= *pd_idx)
			(*dd_idx)++;
		break;
	case RAID5_ALGORITHM_LEFT_SYMMETRIC:
		*pd_idx = data_disks - stripe % raid_disks;
		*dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
		break;
	case RAID5_ALGORITHM_RIGHT_SYMMETRIC:
		*pd_idx = stripe % raid_disks;
		*dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
		break;
	default:
		fprintf (stderr, "raid5: unsupported algorithm %d\n", 
		    algorithm);
	}

	PU (*dd_idx);
	PU (*pd_idx);

	/*
	 * Finally, compute the new sector number
	 */

	new_sector = stripe * blocks_per_chunk + chunk_offset;

	PU (new_sector);

	return new_sector;
}
