/*
 * DF-2568 — v7: large-file flush race on configurable fs.
 *
 * Uses LARGER writes (FILE_KB per file) to force indirect-block chains,
 * making the downward flush recursion take longer and widening the :662-665
 * window for the concurrent unlink to arrive and block on the chain lock.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <errno.h>

static volatile sig_atomic_t stop = 0;
static void on_alarm(int s){ (void)s;stop=1; }

#define BLK 65536

static const char *g_dir;
static int g_pool;
static int g_filekb;

static void dirtier(int id, long iters)
{
    char path[512], *buf;
    long i;
    unsigned seed = (unsigned)(id * 7 + 1);
    int blocks = g_filekb * 1024 / BLK;
    if (blocks < 1) blocks = 1;

    buf = malloc(BLK);
    if (!buf) _exit(1);
    memset(buf, (char)(id + 0x43), BLK);

    for (i = 0; i < iters && !stop; i++) {
        int idx = rand_r(&seed) % g_pool;
        int fd, b;
        snprintf(path, sizeof(path), "%s/pool_%d", g_dir, idx);
        fd = open(path, O_RDWR | O_CREAT, 0666);
        if (fd < 0) continue;
        buf[0] = (char)(id ^ i);
        for (b = 0; b < blocks; b++)
            write(fd, buf, BLK);
        fsync(fd);  /* dispatches flush xop, BLOCKS — concurrent unlink races */
        close(fd);
    }
    free(buf);
    _exit(0);
}

static void unlinker(int id, long iters)
{
    char path[512];
    long i;
    unsigned seed = (unsigned)(id * 13 + 3);
    for (i = 0; i < iters && !stop; i++) {
        int idx = rand_r(&seed) % g_pool;
        int fd;
        snprintf(path, sizeof(path), "%s/pool_%d", g_dir, idx);
        unlink(path);
        fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0666);
        if (fd >= 0) { char c=(char)(id^i); write(fd,&c,1); close(fd); }
    }
    _exit(0);
}

static void syncer(int id, long iters)
{
    long i;
    for (i = 0; i < iters && !stop; i++)
        sync();
    _exit(0);
}

int main(int argc, char **argv)
{
    const char *dir = NULL;
    int ndirty = 12, nunlink = 12, nsync = 4;
    int pool = 16, filekb = 256;
    long iters = 99999999;
    int timeout = 0, opt;

    while ((opt = getopt(argc, argv, "d:D:U:s:p:f:i:t:")) != -1) {
        switch (opt) {
        case 'd': dir = optarg; break;
        case 'D': ndirty = atoi(optarg); break;
        case 'U': nunlink = atoi(optarg); break;
        case 's': nsync = atoi(optarg); break;
        case 'p': pool = atoi(optarg); break;
        case 'f': filekb = atoi(optarg); break;
        case 'i': iters = atol(optarg); break;
        case 't': timeout = atoi(optarg); break;
        default:
            fprintf(stderr,"usage: %s -d dir [-D ndirty] [-U nunlink] [-s nsync] [-p pool] [-f filekb] [-i iters] [-t sec]\n",argv[0]);
            return 2;
        }
    }
    if (!dir) { fprintf(stderr,"%s: need -d dir\n",argv[0]); return 2; }
    g_dir = dir; g_pool = pool; g_filekb = filekb;

    fprintf(stderr,"DF-2568 v7: dir=%s dirty=%d unlink=%d sync=%d pool=%d filekb=%d timeout=%d\n",
            dir, ndirty, nunlink, nsync, pool, filekb, timeout);

    /* pre-populate pool with large files */
    {
        char path[512], *buf; int i, fd, b;
        int blocks = filekb * 1024 / BLK; if (blocks < 1) blocks = 1;
        buf = malloc(BLK); memset(buf, 'P', BLK);
        for (i = 0; i < pool; i++) {
            snprintf(path, sizeof(path), "%s/pool_%d", dir, i);
            fd = open(path, O_CREAT|O_TRUNC|O_RDWR, 0666);
            if (fd >= 0) { for (b=0;b<blocks;b++) write(fd,buf,BLK); close(fd); }
        }
        free(buf);
    }

    signal(SIGALRM, on_alarm);
    if (timeout > 0) alarm((unsigned)timeout);

    int nkids = ndirty + nunlink + nsync;
    pid_t *kids = calloc(nkids, sizeof(pid_t));
    int k, ki = 0;
    for (k=0;k<ndirty;k++){pid_t p=fork();if(p<0){perror("fork");return 1;}if(p==0)dirtier(k,iters);kids[ki++]=p;}
    for (k=0;k<nunlink;k++){pid_t p=fork();if(p<0){perror("fork");return 1;}if(p==0)unlinker(k,iters);kids[ki++]=p;}
    for (k=0;k<nsync;k++){pid_t p=fork();if(p<0){perror("fork");return 1;}if(p==0)syncer(k,iters);kids[ki++]=p;}
    for (k=0;k<nkids;k++) if(kids[k]>0) waitpid(kids[k],NULL,0);
    fprintf(stderr,"DF-2568 v7: done (no panic)\n");
    return 0;
}
