/* Decisive procfs test: child is SIGKILLed (guaranteed dead), reaped, then
 * we probe fstat(fd) on the held /proc/<child>/file vnode.  If pfs_pfind
 * returns NULL after death, getattr returns ENOENT -> fchown triggers leak. */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

int main(void) {
    char path[64];
    int fd = -1, rc, i;
    pid_t pid, w;
    struct stat st;

    setvbuf(stderr, NULL, _IONBF, 0);
    pid = fork();
    if (pid < 0) { perror("fork"); return 2; }
    if (pid == 0) { pause(); _exit(0); }   /* child blocks forever */

    /* parent opens a procfs vnode of the (alive) child */
    const char *tries[] = { "file", "mem", "cmdline", "etype", "map", NULL };
    for (i = 0; tries[i] && fd < 0; i++) {
        snprintf(path, sizeof(path), "/proc/%d/%s", (int)pid, tries[i]);
        fd = open(path, O_RDONLY);
    }
    if (fd < 0) { snprintf(path,sizeof(path),"/proc/%d",(int)pid); fd = open(path, O_RDONLY); }
    if (fd < 0) { fprintf(stderr,"open failed: %s\n", strerror(errno)); kill(pid,SIGKILL); waitpid(pid,NULL,0); return 2; }
    fprintf(stderr,"[kill] opened %s fd=%d for pid %d\n", path, fd, (int)pid);

    /* verify child alive, then SIGKILL + reap */
    if (kill(pid, 0) == 0) fprintf(stderr,"[kill] child alive\n");
    kill(pid, SIGKILL);
    while ((w = waitpid(pid, NULL, 0)) < 0 && errno == EINTR) {}
    fprintf(stderr,"[kill] child %d SIGKILLed+reaped; kill(0)=%d (%s)\n",
            (int)pid, kill(pid,0), strerror(errno));

    for (i = 0; i < 30; i++) {
        errno = 0;
        rc = fstat(fd, &st);
        fprintf(stderr,"[kill] t+%4.1fs fstat rc=%d errno=%d (%s)\n",
                i*0.2, rc, errno, rc?strerror(errno):"ok");
        if (rc < 0) break;
        usleep(200000);
    }
    if (rc == 0) { fprintf(stderr,"[kill] getattr never failed\n"); return 3; }

    fprintf(stderr,"[kill] getattr FAILS -> fchown to trigger setfown leak\n");
    errno=0; rc=fchown(fd,-1,-1);
    fprintf(stderr,"[kill] fchown#1 rc=%d errno=%d (%s) <- LEAK if rc<0\n",rc,errno,rc?strerror(errno):"ok");
    fprintf(stderr,"[kill] fchown#2 -> expect HANG\n");
    errno=0; rc=fchown(fd,-1,-1);
    fprintf(stderr,"[kill] fchown#2 rc=%d errno=%d -- RETURNED\n",rc,errno);
    return 0;
}
