#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>

#include <ufs/ffs/fs.h>

#ifndef FS_UFS2EA_MAGIC
#define FS_UFS2EA_MAGIC 0x19012038 /* UFS2 with extattrs */
#endif
#ifndef FS_UFS2EA_MAGIC_SWAPPED
#define FS_UFS2EA_MAGIC_SWAPPED 0x38200119
#endif

int
main(int argc, char **argv)
{
	const char *path;
	char replybuf[32];
	struct fs *fs, *fs2;
	uint32_t dev_bsize, newmagic;
	off_t off;
	char *buf, *buf2;
	int fd, i;

	if (argc != 2) {
		errx(1, "usage: %s <path>", getprogname());
	}

	path = argv[1];

	fd = open(path, O_RDWR);
	if (fd < 0) {
		err(1, "open");
	}

	buf = malloc(SBLOCKSIZE);
	if (buf == NULL) {
		err(1, "malloc");
	}
	fs = (void *)buf;

	buf2 = malloc(SBLOCKSIZE);
	if (buf2 == NULL) {
		err(1, "malloc2");
	}
	fs2 = (void *)buf2;

	if (pread(fd, buf, SBLOCKSIZE, SBLOCK_UFS2) != SBLOCKSIZE) {
		err(1, "pread");
	}

	switch (fs->fs_magic) {
	case FS_UFS2EA_MAGIC:
	case FS_UFS2EA_MAGIC_SWAPPED:
		printf("%s file system is already UFS2ea\n", path);
		exit(0);
		break;

	case FS_UFS2_MAGIC:
		newmagic = FS_UFS2EA_MAGIC;
		break;

	case FS_UFS2_MAGIC_SWAPPED:
		newmagic = FS_UFS2EA_MAGIC_SWAPPED;
		break;

	default:
		printf("%s does not contain a UFS2 file system\n", path);
		exit(0);
		break;
	}

	dev_bsize = fs->fs_fsize / FFS_FSBTODB(fs, 1);

	for (i = 0; i < fs->fs_ncg; i++) {
		off = (off_t)(FFS_FSBTODB(fs, cgsblock(fs, i))) * dev_bsize;
		if (pread(fd, buf2, SBLOCKSIZE, off) != SBLOCKSIZE) {
			err(1, "pread2");
		}
		switch (fs2->fs_magic) {
		case FS_UFS2EA_MAGIC:
		case FS_UFS2EA_MAGIC_SWAPPED:
		case FS_UFS2_MAGIC:
		case FS_UFS2_MAGIC_SWAPPED:
			break;

		default:
			printf("alt superblock cg %d not recognized\n", i);
			exit(1);
		}
		if (fs2->fs_magic != fs->fs_magic) {
			errx(1, "primary/alt superblock mismatch cg %d: 0x%08x 0x%08x",
			     i, fs->fs_magic, fs2->fs_magic);
		}
	}

	printf("%s contains an original NetBSD UFS2 file system.\n", path);
	printf("\n");

	printf("This utility converts UFS2 to UFS2ea (UFS2 with extattr support)\n");
	printf("WITHOUT deleting possibly corrupted extattr block references.\n");
	printf("\n");

	printf("In previous releases of NetBSD, UFS2 did not know about extattrs,\n");
	printf("so if an inode has non-zero extattr block pointers then an old fsck\n");
	printf("may have freed the blocks that these pointers point to.\n");

	printf("This utility is intended to be used ONLY by people who have\n");
	printf("created extattrs in original NetBSD UFS2 file systems using a kernel\n");
	printf("from time period after UFS2 extattr support was added to NetBSD\n");
	printf("and before the separate UFS2ea fs type was created.\n");
	printf("Using this utility incorrectly may corrupt your file system!\n");
	printf("\n");

	printf("Proceed with conversion [yes/no] ? ");
	fflush(stdout);
	if (fgets(replybuf, sizeof(replybuf), stdin) == NULL ||
	    strcmp(replybuf, "yes\n")) {
		printf("Aborted.\n");
		exit(0);
	}

	fs->fs_magic = newmagic;
	fs2->fs_magic = newmagic;

	for (i = 0; i < fs->fs_ncg; i++) {
		off = (off_t)(FFS_FSBTODB(fs, cgsblock(fs, i))) * dev_bsize;
		if (pwrite(fd, buf2, SBLOCKSIZE, off) != SBLOCKSIZE) {
			err(1, "pwrite alternate sb %d", i);
		}
	}
	if (pwrite(fd, buf, SBLOCKSIZE, SBLOCK_UFS2) != SBLOCKSIZE) {
		err(1, "pwrite primary sb");
	}

	printf("Conversion successful.\n");
	printf("*** RUN \"fsck -fy %s\" TO VERIFY THIS FILE SYSTEM ***\n", path);
	exit(0);
}
