/* DF-3071 - node-limit TOCTOU with per-directory parallelism (avoids the
 * parent-dnode lock serialization). usage: df3071d <mnt> <ndirs> <prefill_per_dir> <nthreads>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
static pthread_barrier_t bar;
static char dirp[512]; static int ndirs; static volatile int go=0;
static long total=0; static pthread_mutex_t mx=PTHREAD_MUTEX_INITIALIZER;
static void *w(void *arg){
    long id=(long)arg; char p[600];
    pthread_barrier_wait(&bar);
    snprintf(p,sizeof p,"%s/d%d/b%ld",dirp,(int)(id%ndirs),id);
    int fd=open(p,O_CREAT|O_EXCL,0644);
    int ok=(fd>=0); if(fd>=0) close(fd);
    pthread_mutex_lock(&mx); total+=ok; pthread_mutex_unlock(&mx);
    return NULL;
}
int main(int argc,char**argv){
    pthread_t th[128]; long i,n; char p[600]; int d,f;
    if(argc!=5){fprintf(stderr,"usage: df3071d mnt ndirs prefill_per_dir nthreads\n");return 2;}
    snprintf(dirp,sizeof dirp,"%s",argv[1]);
    ndirs=atoi(argv[2]); int pre=atoi(argv[3]); n=atol(argv[4]); if(n>128)n=128;
    long nodes=1; /* root */
    for(d=0;d<ndirs;d++){ snprintf(p,sizeof p,"%s/d%d",dirp,d);
        if(mkdir(p,0777)==0) nodes++;
        for(f=0;f<pre;f++){ char q[640]; snprintf(q,sizeof q,"%s/p%d",p,f);
            int fd=open(q,O_CREAT|O_EXCL,0644); if(fd>=0){close(fd);nodes++;} } }
    pthread_barrier_init(&bar,NULL,n+1);
    for(i=0;i<n;i++) pthread_create(&th[i],NULL,w,(void*)i);
    pthread_barrier_wait(&bar);
    for(i=0;i<n;i++) pthread_join(th[i],NULL);
    printf("PREFILL_NODES=%ld BURST_OK=%ld CAP=67 => FINAL=%ld %s\n",
           nodes,total,nodes+total,(nodes+total>67)?"OVERSHOOT":"at-or-below-cap");
    return 0;
}
