/*
 * inopatch.c — DF-3015/DF-3016 on-disk dinode patcher (runs as root, guest).
 *
 * usage: inopatch <image> <ino> get            - show dinode summary
 *        inopatch <image> <ino> setsize <hex>  - set di_size (64-bit LE)
 *
 * Computes the byte offset of inode <ino> using the real FFS macros from
 * the image's own superblock, self-verifies that the dinode at that offset
 * has a sane di_gen/di_mode, and patches di_size.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>

#include <sys/types.h>
#define _KERNEL_STRUCTURES
#define MAXFRAG 8
#include "dinode.h"
#include "fs.h"

int
main(int argc, char **argv)
{
	struct fs fsb;
	struct ufs1_dinode di;
	off_t off;
	int fd;
	unsigned long ino;

	if (argc < 4) {
		fprintf(stderr, "usage: %s img ino get|setsize hexval\n",
		    argv[0]);
		return (2);
	}
	ino = strtoul(argv[2], NULL, 0);
	fd = open(argv[1], O_RDWR);
	if (fd < 0) { perror("open"); return (1); }
	if (pread(fd, &fsb, sizeof(fsb), 8192) != sizeof(fsb)) {
		perror("pread sb"); return (1);
	}
	if (fsb.fs_magic != FS_MAGIC) {
		fprintf(stderr, "bad magic %x\n", fsb.fs_magic);
		return (1);
	}
	/* byte offset of the inode's dinode (FFS addresses are in fragments) */
	off = (off_t)(cgimin(&fsb, ino_to_cg(&fsb, ino)) +
	    (ino % fsb.fs_ipg) / INOPB(&fsb)) * fsb.fs_fsize +
	    ((ino % fsb.fs_ipg) % INOPB(&fsb)) * sizeof(struct ufs1_dinode);
	if (pread(fd, &di, sizeof(di), off) != sizeof(di)) {
		perror("pread dinode"); return (1);
	}
	printf("ino %lu at byte off %lld: mode=%o nlink=%d size=%llu "
	    "blocks=%d db0=%d gen=%d\n",
	    ino, (long long)off, di.di_mode, di.di_nlink,
	    (unsigned long long)di.di_size, di.di_blocks, di.di_db[0],
	    di.di_gen);

	if (strcmp(argv[3], "setsize") == 0 && argc > 4) {
		uint64_t v = strtoull(argv[4], NULL, 0);
		di.di_size = v;
		if (pwrite(fd, &di, sizeof(di), off) != sizeof(di)) {
			perror("pwrite"); return (1);
		}
		printf("di_size patched to 0x%llx\n",
		    (unsigned long long)v);
	}
	close(fd);
	return (0);
}
