/*
 * patch_frag.c - patch fs->fs_frag in an FFS image to an out-of-{1,2,4,8}
 * value, to reproduce DF-0919 (kernel panic in ffs_isblock/ffs_clrblock/
 * ffs_setblock/ffs_isfree_block, or OOB in ffs_fragacct).
 *
 * Build against the tree's own fs.h:
 *   cc -I<repo>/sys -o patch_frag patch_frag.c
 * Use:
 *   ./patch_frag <image> <new_frag>
 *
 * The kernel's superblock lives at SBOFF = 8192 (BBSIZE).  We locate the
 * fs_frag field by offsetof() against <ufs/fs.h> so this tool stays correct
 * if the struct layout ever changes.
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stddef.h>
#if defined(__DragonFly__)
# include <vfs/ufs/ufs_types.h>
# include <vfs/ufs/fs.h>
#else
# include <ufs/fs.h>
#endif

#ifndef SBOFF
# define SBOFF  BBSIZE
#endif

int
main(int argc, char **argv)
{
    const char *path;
    int fd, newfrag;
    struct stat st;
    char *m;
    int32_t *fragp;

    if (argc != 3) {
        fprintf(stderr, "usage: %s <image> <new_frag>\n", argv[0]);
        return 2;
    }
    path = argv[1];
    newfrag = atoi(argv[2]);

    fd = open(path, O_RDWR);
    if (fd < 0) { perror("open"); return 1; }
    if (fstat(fd, &st) < 0) { perror("fstat"); return 1; }
    if (st.st_size < SBOFF + (off_t)sizeof(struct fs)) {
        fprintf(stderr, "image too small\n");
        return 1;
    }

    m = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (m == MAP_FAILED) { perror("mmap"); return 1; }

    fragp = (int32_t *)(m + SBOFF + offsetof(struct fs, fs_frag));
    printf("old fs_frag = %d\n", *fragp);
    *fragp = newfrag;
    printf("new fs_frag = %d\n", *fragp);

    if (msync(m, st.st_size, MS_SYNC) < 0) { perror("msync"); return 1; }
    munmap(m, st.st_size);
    close(fd);
    return 0;
}
