/* 
 * Simple program to unstripe a RAID-5 array from disk image files.
 * This worked successfully on a Buffalo Drive Station Quattro.
 *
 * Assumptions:
 *     Stripe width: 512 bytes (1 sector)
 *     Left Asymmetric (backward parity distribution)
 *
 * D1  D2  D3  D4
 * ==============
 * S1  S2  S3  SP
 * S4  S5  SP  S6
 * S7  SP  S8  S9
 * SP  S10 S11 S12
 * S12 S13 S14 SP
 * S15 S16 SP  S17
 *      ...
 */
 
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

#ifndef D1
#define D1 "/dev/loop1"
#define D2 "/dev/loop2"
#define D3 "/dev/loop3"
#define D4 "/dev/loop4"
#endif

#ifndef OUTFILE
#define OUTFILE "/tmp/diskimage.img"
#endif

/* For testing purposes, only iterate 10k times. */
#ifndef LOOPS
#define LOOPS  10000
#endif

#define STRIPE_SIZE 512

int main(int argc, char *argv[])
{
    unsigned long start_sector = 0;
    unsigned long long start_offset = 1024;
    unsigned char buf[STRIPE_SIZE];
    int i;
    int sts = 0;
    unsigned long iteration = 0;
    FILE *fp1 = fopen(D1, "rb");
    FILE *fp2 = fopen(D2, "rb");
    FILE *fp3 = fopen(D3, "rb");
    FILE *fp4 = fopen(D4, "rb");
    FILE *outfp = fopen(OUTFILE, "wb");
    FILE *fps[] = 
        { fp1, fp2, fp3, fp4, 
          fp1, fp2, fp3, fp4,
          fp1, fp2, fp3, fp4,
          fp1, fp2, fp3, fp4 };

    unsigned char stripe[] = 
        { 0, 0, 0, 1,
          0, 0, 1, 0, 
          0, 1, 0, 0, 
          1, 0, 0, 0 };

    off_t offset = 0;

    if (!fp1 || !fp2 || !fp3 || !fp4 || !outfp)
    {
        perror("fopen");
        return 1;
    }

    if (argc > 1)
    {
        start_sector = strtoul(argv[1], NULL, 0);
        start_offset = start_sector;
        start_offset *= STRIPE_SIZE;
        offset = start_offset;
        fseeko(fp1, offset, SEEK_SET);
        fseeko(fp2, offset, SEEK_SET);
        fseeko(fp3, offset, SEEK_SET);
        fseeko(fp4, offset, SEEK_SET);
    }

    while ((LOOPS==0 && !feof(fp4)) || (LOOPS && iteration < LOOPS)) {
        for (i=0; i<16; i++)
        {
            sts = fread(buf, 1, STRIPE_SIZE, fps[i]);
            /*
             * Read stripe data sequentially, discard parity.
             * Probably faster to read than seeking over discarded data?
             */
            if (!stripe[i])
            {
                fwrite(buf, 1, STRIPE_SIZE, outfp);
            }
        }
        printf("Iteration = %ld\n", iteration++);
    }
    return 0;
}