From 7ed474b4820a13b43c8637a8559c6305743484b6 Mon Sep 17 00:00:00 2001
From: Tomohiro Kusumi <tkusumi@netbsd.org>
Date: Sun, 7 Mar 2021 09:08:29 +0900
Subject: [PATCH] usr.sbin/fstyp: Sync with DragonFly

---
 usr.sbin/fstyp/hammer.c       | 115 ++++-------------
 usr.sbin/fstyp/hammer2.c      | 156 ++++++++++++++++++-----
 usr.sbin/fstyp/hammer2_disk.h | 224 +++++++++++++---------------------
 3 files changed, 226 insertions(+), 269 deletions(-)

diff --git a/usr.sbin/fstyp/hammer.c b/usr.sbin/fstyp/hammer.c
index 6fdad1d642a..4e8dd023885 100644
--- a/usr.sbin/fstyp/hammer.c
+++ b/usr.sbin/fstyp/hammer.c
@@ -35,7 +35,7 @@ __FBSDID("$FreeBSD$");
 #include <stdlib.h>
 #include <string.h>
 #include <err.h>
-#include <assert.h>
+#include <uuid.h>
 
 #include <sys/types.h>
 
@@ -46,13 +46,7 @@ __FBSDID("$FreeBSD$");
 static hammer_volume_ondisk_t
 read_ondisk(FILE *fp)
 {
-	hammer_volume_ondisk_t ondisk;
-
-	ondisk = read_buf(fp, 0, sizeof(*ondisk));
-	if (ondisk == NULL)
-		err(1, "failed to read ondisk");
-
-	return (ondisk);
+	return (read_buf(fp, 0, sizeof(struct hammer_volume_ondisk)));
 }
 
 static int
@@ -66,27 +60,28 @@ test_ondisk(const hammer_volume_ondisk_t ondisk)
 	    ondisk->vol_signature != HAMMER_FSBUF_VOLUME_REV)
 		return (1);
 	if (ondisk->vol_rootvol != HAMMER_ROOT_VOLNO)
-		return (2);
+		return (1);
 	if (ondisk->vol_no < 0 || ondisk->vol_no > HAMMER_MAX_VOLUMES - 1)
-		return (3);
+		return (1);
 	if (ondisk->vol_count < 1 || ondisk->vol_count > HAMMER_MAX_VOLUMES)
-		return (4);
+		return (1);
 
 	if (count == 0) {
 		count = ondisk->vol_count;
-		assert(count != 0);
+		if (count == 0)
+			return (1);
 		memcpy(&fsid, &ondisk->vol_fsid, sizeof(fsid));
 		memcpy(&fstype, &ondisk->vol_fstype, sizeof(fstype));
 		strlcpy(label, ondisk->vol_label, sizeof(label));
 	} else {
 		if (ondisk->vol_count != count)
-			return (5);
-		if (memcmp(&ondisk->vol_fsid, &fsid, sizeof(fsid)))
-			return (6);
-		if (memcmp(&ondisk->vol_fstype, &fstype, sizeof(fstype)))
-			return (7);
+			return (1);
+		if (!uuid_equal(&ondisk->vol_fsid, &fsid, NULL))
+			return (1);
+		if (!uuid_equal(&ondisk->vol_fstype, &fstype, NULL))
+			return (1);
 		if (strcmp(ondisk->vol_label, label))
-			return (8);
+			return (1);
 	}
 
 	return (0);
@@ -99,6 +94,8 @@ fstyp_hammer(FILE *fp, char *label, size_t size)
 	int error = 1;
 
 	ondisk = read_ondisk(fp);
+	if (!ondisk)
+		goto fail;
 	if (ondisk->vol_no != HAMMER_ROOT_VOLNO)
 		goto fail;
 	if (ondisk->vol_count != 1)
@@ -116,93 +113,23 @@ fstyp_hammer(FILE *fp, char *label, size_t size)
 static int
 test_volume(const char *volpath)
 {
-	hammer_volume_ondisk_t ondisk;
+	hammer_volume_ondisk_t ondisk = NULL;
 	FILE *fp;
 	int volno = -1;
 
 	if ((fp = fopen(volpath, "r")) == NULL)
-		err(1, "failed to open %s", volpath);
+		goto fail;
 
 	ondisk = read_ondisk(fp);
-	fclose(fp);
+	if (!ondisk)
+		goto fail;
 	if (test_ondisk(ondisk))
 		goto fail;
 
 	volno = ondisk->vol_no;
 fail:
+	if (fp)
+		fclose(fp);
 	free(ondisk);
 	return (volno);
 }
-
-static int
-__fsvtyp_hammer(const char *blkdevs, char *label, size_t size, int partial)
-{
-	hammer_volume_ondisk_t ondisk = NULL;
-	FILE *fp;
-	char *dup, *p, *volpath, x[HAMMER_MAX_VOLUMES];
-	int i, volno, error = 1;
-
-	if (!blkdevs)
-		goto fail;
-
-	memset(x, 0, sizeof(x));
-	dup = strdup(blkdevs);
-	p = dup;
-
-	volpath = NULL;
-	volno = -1;
-	while (p) {
-		volpath = p;
-		if ((p = strchr(p, ':')) != NULL)
-			*p++ = '\0';
-		if ((volno = test_volume(volpath)) == -1)
-			break;
-		assert(volno >= 0);
-		assert(volno < HAMMER_MAX_VOLUMES);
-		x[volno]++;
-	}
-
-	if (!volpath)
-		err(1, "invalid path %s", blkdevs);
-	if ((fp = fopen(volpath, "r")) == NULL)
-		err(1, "failed to open %s", volpath);
-	ondisk = read_ondisk(fp);
-	fclose(fp);
-
-	free(dup);
-
-	if (volno == -1)
-		goto fail;
-	if (partial)
-		goto success;
-
-	for (i = 0; i < HAMMER_MAX_VOLUMES; i++)
-		if (x[i] > 1)
-			goto fail;
-	for (i = 0; i < HAMMER_MAX_VOLUMES; i++)
-		if (x[i] == 0)
-			break;
-	if (ondisk->vol_count != i)
-		goto fail;
-	for (; i < HAMMER_MAX_VOLUMES; i++)
-		if (x[i] != 0)
-			goto fail;
-success:
-	strlcpy(label, ondisk->vol_label, size);
-	error = 0;
-fail:
-	free(ondisk);
-	return (error);
-}
-
-int
-fsvtyp_hammer(const char *blkdevs, char *label, size_t size)
-{
-	return (__fsvtyp_hammer(blkdevs, label, size, 0));
-}
-
-int
-fsvtyp_hammer_partial(const char *blkdevs, char *label, size_t size)
-{
-	return (__fsvtyp_hammer(blkdevs, label, size, 1));
-}
diff --git a/usr.sbin/fstyp/hammer2.c b/usr.sbin/fstyp/hammer2.c
index dfd026649fd..807c2e96959 100644
--- a/usr.sbin/fstyp/hammer2.c
+++ b/usr.sbin/fstyp/hammer2.c
@@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
 #include <string.h>
 #include <err.h>
 #include <assert.h>
+#include <uuid.h>
 
 #include <sys/types.h>
 
@@ -44,24 +45,88 @@ __FBSDID("$FreeBSD$");
 
 #include "fstyp.h"
 
-static hammer2_volume_data_t*
-read_voldata(FILE *fp)
+static ssize_t
+get_file_size(FILE *fp)
 {
-	hammer2_volume_data_t *voldata;
+	ssize_t siz;
+
+	if (fseek(fp, 0, SEEK_END) == -1) {
+		warnx("hammer2: failed to seek media end");
+		return (-1);
+	}
+
+	siz = ftell(fp);
+	if (siz == -1) {
+		warnx("hammer2: failed to tell media end");
+		return (-1);
+	}
+
+	return (siz);
+}
+
+static hammer2_volume_data_t *
+read_voldata(FILE *fp, int i)
+{
+	if (i < 0 || i >= HAMMER2_NUM_VOLHDRS)
+		return (NULL);
 
-	voldata = read_buf(fp, 0, sizeof(*voldata));
-	if (voldata == NULL)
-		err(1, "failed to read volume data");
+	if (i * HAMMER2_ZONE_BYTES64 >= get_file_size(fp))
+		return (NULL);
 
-	return (voldata);
+	return (read_buf(fp, i * HAMMER2_ZONE_BYTES64,
+	    sizeof(hammer2_volume_data_t)));
 }
 
 static int
-test_voldata(const hammer2_volume_data_t *voldata)
+test_voldata(FILE *fp)
 {
-	if (voldata->magic != HAMMER2_VOLUME_ID_HBO &&
-	    voldata->magic != HAMMER2_VOLUME_ID_ABO)
-		return (1);
+	hammer2_volume_data_t *voldata;
+	int i;
+	static int count = 0;
+	static uuid_t fsid, fstype;
+
+	for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
+		if (i * HAMMER2_ZONE_BYTES64 >= get_file_size(fp))
+			break;
+		voldata = read_voldata(fp, i);
+		if (voldata == NULL) {
+			warnx("hammer2: failed to read volume data");
+			return (1);
+		}
+		if (voldata->magic != HAMMER2_VOLUME_ID_HBO &&
+		    voldata->magic != HAMMER2_VOLUME_ID_ABO) {
+			free(voldata);
+			return (1);
+		}
+		if (voldata->volu_id > HAMMER2_MAX_VOLUMES - 1) {
+			free(voldata);
+			return (1);
+		}
+		if (voldata->nvolumes > HAMMER2_MAX_VOLUMES) {
+			free(voldata);
+			return (1);
+		}
+
+		if (count == 0) {
+			count = voldata->nvolumes;
+			memcpy(&fsid, &voldata->fsid, sizeof(fsid));
+			memcpy(&fstype, &voldata->fstype, sizeof(fstype));
+		} else {
+			if (voldata->nvolumes != count) {
+				free(voldata);
+				return (1);
+			}
+			if (!uuid_equal(&fsid, &voldata->fsid, NULL)) {
+				free(voldata);
+				return (1);
+			}
+			if (!uuid_equal(&fstype, &voldata->fstype, NULL)) {
+				free(voldata);
+				return (1);
+			}
+		}
+		free(voldata);
+	}
 
 	return (0);
 }
@@ -71,7 +136,7 @@ read_media(FILE *fp, const hammer2_blockref_t *bref, size_t *media_bytes)
 {
 	hammer2_media_data_t *media;
 	hammer2_off_t io_off, io_base;
-	size_t bytes, io_bytes, boff;
+	size_t bytes, io_bytes, boff, fbytes;
 
 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
 	if (bytes)
@@ -79,30 +144,44 @@ read_media(FILE *fp, const hammer2_blockref_t *bref, size_t *media_bytes)
 	*media_bytes = bytes;
 
 	if (!bytes) {
-		warnx("blockref has no data");
+		warnx("hammer2: blockref has no data");
 		return (NULL);
 	}
 
 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
-	io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
+	io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
 	boff = io_off - io_base;
 
-	io_bytes = HAMMER2_MINIOSIZE;
+	io_bytes = HAMMER2_LBUFSIZE;
 	while (io_bytes + boff < bytes)
 		io_bytes <<= 1;
 
 	if (io_bytes > sizeof(hammer2_media_data_t)) {
-		warnx("invalid I/O bytes");
+		warnx("hammer2: invalid I/O bytes");
+		return (NULL);
+	}
+
+	/*
+	 * XXX fp is currently always root volume, so read fails if io_base is
+	 * beyond root volume limit. Fail with a message before read_buf() then.
+	 */
+	fbytes = get_file_size(fp);
+	if (fbytes == -1) {
+		warnx("hammer2: failed to get media size");
+		return (NULL);
+	}
+	if (io_base >= fbytes) {
+		warnx("hammer2: XXX read beyond HAMMER2 root volume limit unsupported");
 		return (NULL);
 	}
 
 	if (fseek(fp, io_base, SEEK_SET) == -1) {
-		warnx("failed to seek media");
+		warnx("hammer2: failed to seek media");
 		return (NULL);
 	}
 	media = read_buf(fp, io_base, io_bytes);
 	if (media == NULL) {
-		warnx("failed to read media");
+		warnx("hammer2: failed to read media");
 		return (NULL);
 	}
 	if (boff)
@@ -145,8 +224,10 @@ find_pfs(FILE *fp, const hammer2_blockref_t *bref, const char *pfs, bool *res)
 					    strlen(pfs)))
 						*res = true;
 				}
-			} else
-				assert(0);
+			} else {
+				free(media);
+				return (-1);
+			}
 		}
 		break;
 	case HAMMER2_BREF_TYPE_INDIRECT:
@@ -178,7 +259,7 @@ extract_device_name(const char *devpath)
 	char *p, *head;
 
 	if (!devpath)
-		return NULL;
+		return (NULL);
 
 	p = strdup(devpath);
 	head = p;
@@ -192,18 +273,18 @@ extract_device_name(const char *devpath)
 		p++;
 		if (*p == 0) {
 			free(head);
-			return NULL;
+			return (NULL);
 		}
 		p = strdup(p);
 		free(head);
-		return p;
+		return (p);
 	}
 
-	return head;
+	return (head);
 }
 
 static int
-read_label(FILE *fp, char *label, size_t size)
+read_label(FILE *fp, char *label, size_t size, const char *devpath)
 {
 	hammer2_blockref_t broot, best, *bref;
 	hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media;
@@ -214,14 +295,20 @@ read_label(FILE *fp, char *label, size_t size)
 	char *devname;
 
 	best_i = -1;
+	memset(vols, 0, sizeof(vols));
 	memset(&best, 0, sizeof(best));
 
 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
+		if (i * HAMMER2_ZONE_BYTES64 >= get_file_size(fp))
+			break;
 		memset(&broot, 0, sizeof(broot));
 		broot.type = HAMMER2_BREF_TYPE_VOLUME;
 		broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
-		vols[i] = read_buf(fp, broot.data_off & ~HAMMER2_OFF_MASK_RADIX,
-		    sizeof(*vols[i]));
+		vols[i] = (void*)read_voldata(fp, i);
+		if (vols[i] == NULL) {
+			warnx("hammer2: failed to read volume data");
+			goto fail;
+		}
 		broot.mirror_tid = vols[i]->voldata.mirror_tid;
 		if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
 			best_i = i;
@@ -231,7 +318,7 @@ read_label(FILE *fp, char *label, size_t size)
 
 	bref = &vols[best_i]->voldata.sroot_blockset.blockref[0];
 	if (bref->type != HAMMER2_BREF_TYPE_INODE) {
-		warnx("blockref type is not inode");
+		/* Don't print error as devpath could be non-root volume. */
 		goto fail;
 	}
 
@@ -241,7 +328,7 @@ read_label(FILE *fp, char *label, size_t size)
 	}
 
 	pfs = "";
-	devname = extract_device_name(NULL);
+	devname = extract_device_name(devpath);
 	assert(!devname); /* Currently always NULL in FreeBSD. */
 
 	/* Add device name to help support multiple autofs -media mounts. */
@@ -273,14 +360,17 @@ read_label(FILE *fp, char *label, size_t size)
 int
 fstyp_hammer2(FILE *fp, char *label, size_t size)
 {
-	hammer2_volume_data_t *voldata;
+	hammer2_volume_data_t *voldata = read_voldata(fp, 0);
 	int error = 1;
 
-	voldata = read_voldata(fp);
-	if (test_voldata(voldata))
+	if (voldata->volu_id != HAMMER2_ROOT_VOLUME)
+		goto fail;
+	if (voldata->nvolumes != 0)
+		goto fail;
+	if (test_voldata(fp))
 		goto fail;
 
-	error = read_label(fp, label, size);
+	error = read_label(fp, label, size, NULL);
 fail:
 	free(voldata);
 	return (error);
diff --git a/usr.sbin/fstyp/hammer2_disk.h b/usr.sbin/fstyp/hammer2_disk.h
index 89afafbdc9a..9db8c8dc424 100644
--- a/usr.sbin/fstyp/hammer2_disk.h
+++ b/usr.sbin/fstyp/hammer2_disk.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2011-2018 The DragonFly Project.  All rights reserved.
+ * Copyright (c) 2011-2019 The DragonFly Project.  All rights reserved.
  *
  * This code is derived from software contributed to The DragonFly Project
  * by Matthew Dillon <dillon@dragonflybsd.org>
@@ -41,30 +41,6 @@
 #ifndef _SYS_UUID_H_
 #include <sys/uuid.h>
 #endif
-#ifndef _SYS_DMSG_H_
-/*
- * dmsg_hdr must be 64 bytes
- */
-struct dmsg_hdr {
-	uint16_t	magic;		/* 00 sanity, synchro, endian */
-	uint16_t	reserved02;	/* 02 */
-	uint32_t	salt;		/* 04 random salt helps w/crypto */
-
-	uint64_t	msgid;		/* 08 message transaction id */
-	uint64_t	circuit;	/* 10 circuit id or 0	*/
-	uint64_t	reserved18;	/* 18 */
-
-	uint32_t	cmd;		/* 20 flags | cmd | hdr_size / ALIGN */
-	uint32_t	aux_crc;	/* 24 auxillary data crc */
-	uint32_t	aux_bytes;	/* 28 auxillary data length (bytes) */
-	uint32_t	error;		/* 2C error code or 0 */
-	uint64_t	aux_descr;	/* 30 negotiated OOB data descr */
-	uint32_t	reserved38;	/* 38 */
-	uint32_t	hdr_crc;	/* 3C (aligned) extended header crc */
-};
-
-typedef struct dmsg_hdr dmsg_hdr_t;
-#endif
 
 /*
  * The structures below represent the on-disk media structures for the HAMMER2
@@ -113,15 +89,6 @@ typedef struct dmsg_hdr dmsg_hdr_t;
 #define HAMMER2_RADIX_KEY	64	/* number of bits in key */
 
 /*
- * MINALLOCSIZE		- The minimum allocation size.  This can be smaller
- *		  	  or larger than the minimum physical IO size.
- *
- *			  NOTE: Should not be larger than 1K since inodes
- *				are 1K.
- *
- * MINIOSIZE		- The minimum IO size.  This must be less than
- *			  or equal to HAMMER2_LBUFSIZE.
- *
  * HAMMER2_LBUFSIZE	- Nominal buffer size for I/O rollups.
  *
  * HAMMER2_PBUFSIZE	- Topological block size used by files for all
@@ -139,12 +106,6 @@ typedef struct dmsg_hdr dmsg_hdr_t;
 #define HAMMER2_LBUFRADIX	14	/* logical buf (1<<14) bytes */
 #define HAMMER2_LBUFSIZE	16384
 
-/*
- * Generally speaking we want to use 16K and 64K I/Os
- */
-#define HAMMER2_MINIORADIX	HAMMER2_LBUFRADIX
-#define HAMMER2_MINIOSIZE	HAMMER2_LBUFSIZE
-
 #define HAMMER2_IND_BYTES_MIN	4096
 #define HAMMER2_IND_BYTES_NOM	HAMMER2_LBUFSIZE
 #define HAMMER2_IND_BYTES_MAX	HAMMER2_PBUFSIZE
@@ -158,17 +119,12 @@ typedef struct dmsg_hdr dmsg_hdr_t;
 
 /*
  * In HAMMER2, arrays of blockrefs are fully set-associative, meaning that
- * any element can occur at any index and holes can be anywhere.  As a
- * future optimization we will be able to flag that such arrays are sorted
- * and thus optimize lookups, but for now we don't.
+ * any element can occur at any index and holes can be anywhere.
  *
  * Inodes embed either 512 bytes of direct data or an array of 4 blockrefs,
  * resulting in highly efficient storage for files <= 512 bytes and for files
  * <= 512KB.  Up to 4 directory entries can be referenced from a directory
  * without requiring an indirect block.
- *
- * Indirect blocks are typically either 4KB (64 blockrefs / ~4MB represented),
- * or 64KB (1024 blockrefs / ~64MB represented).
  */
 #define HAMMER2_SET_RADIX		2	/* radix 2 = 4 entries */
 #define HAMMER2_SET_COUNT		(1 << HAMMER2_SET_RADIX)
@@ -188,10 +144,9 @@ typedef struct dmsg_hdr dmsg_hdr_t;
 #define HAMMER2_UUID_STRING	"5cbb9ad1-862d-11dc-a94d-01301bb8a9f5"
 
 /*
- * A 4MB segment is reserved at the beginning of each 2GB zone.  This segment
+ * A 4MB segment is reserved at the beginning of each 1GB.  This segment
  * contains the volume header (or backup volume header), the free block
- * table, and possibly other information in the future.  A 4MB segment for
- * freemap is reserved at the beginning of every 1GB.
+ * table, and possibly other information in the future.
  *
  * 4MB = 64 x 64K blocks.  Each 4MB segment is broken down as follows:
  *
@@ -303,7 +258,7 @@ typedef struct dmsg_hdr dmsg_hdr_t;
 #define HAMMER2_VOLUME_ALIGN		(8 * 1024 * 1024)
 #define HAMMER2_VOLUME_ALIGN64		((hammer2_off_t)HAMMER2_VOLUME_ALIGN)
 #define HAMMER2_VOLUME_ALIGNMASK	(HAMMER2_VOLUME_ALIGN - 1)
-#define HAMMER2_VOLUME_ALIGNMASK64     ((hammer2_off_t)HAMMER2_VOLUME_ALIGNMASK)
+#define HAMMER2_VOLUME_ALIGNMASK64	((hammer2_off_t)HAMMER2_VOLUME_ALIGNMASK)
 
 #define HAMMER2_NEWFS_ALIGN		(HAMMER2_VOLUME_ALIGN)
 #define HAMMER2_NEWFS_ALIGN64		((hammer2_off_t)HAMMER2_VOLUME_ALIGN)
@@ -380,7 +335,7 @@ typedef struct dmsg_hdr dmsg_hdr_t;
 #define HAMMER2_FREEMAP_LEVEL3_RADIX	46	/* 64TB */
 #define HAMMER2_FREEMAP_LEVEL2_RADIX	38	/* 256GB */
 #define HAMMER2_FREEMAP_LEVEL1_RADIX	30	/* 1GB */
-#define HAMMER2_FREEMAP_LEVEL0_RADIX	22	/* 4MB (128by in l-1 leaf) */
+#define HAMMER2_FREEMAP_LEVEL0_RADIX	22	/* 4MB (x 256 in l-1 leaf) */
 
 #define HAMMER2_FREEMAP_LEVELN_PSIZE	32768	/* physical bytes */
 
@@ -413,7 +368,7 @@ typedef struct dmsg_hdr dmsg_hdr_t;
  *     now we retain compatibility and make FMZONEBASE every 1GB
  */
 #define H2FMZONEBASE(key)	((key) & ~HAMMER2_FREEMAP_LEVEL1_MASK)
-#define H2FMBASE(key, radix)	((key) & ~(((hammer2_off_t)1 << (radix)) - 1))
+#define H2FMBASE(key, radix)	rounddown2(key, (hammer2_off_t)1 << (radix))
 
 /*
  * 16KB bitmap granularity (x2 bits per entry).
@@ -456,9 +411,9 @@ typedef uint64_t			hammer2_bitmap_t;
 #define HAMMER2_BOOT_NOM_BYTES		(64*1024*1024)
 #define HAMMER2_BOOT_MAX_BYTES		(256*1024*1024)
 
-#define HAMMER2_REDO_MIN_BYTES		HAMMER2_VOLUME_ALIGN
-#define HAMMER2_REDO_NOM_BYTES		(256*1024*1024)
-#define HAMMER2_REDO_MAX_BYTES		(1024*1024*1024)
+#define HAMMER2_AUX_MIN_BYTES		HAMMER2_VOLUME_ALIGN
+#define HAMMER2_AUX_NOM_BYTES		(256*1024*1024)
+#define HAMMER2_AUX_MAX_BYTES		(1024*1024*1024)
 
 /*
  * Most HAMMER2 types are implemented as unsigned 64-bit integers.
@@ -499,12 +454,10 @@ typedef uint32_t hammer2_crc32_t;
  *	    case which means no data associated with the blockref, and
  *	    not the '1 byte' it would otherwise calculate to.
  */
-#define HAMMER2_OFF_BAD		((hammer2_off_t)-1)
 #define HAMMER2_OFF_MASK	0xFFFFFFFFFFFFFFC0ULL
 #define HAMMER2_OFF_MASK_LO	(HAMMER2_OFF_MASK & HAMMER2_PBUFMASK64)
 #define HAMMER2_OFF_MASK_HI	(~HAMMER2_PBUFMASK64)
 #define HAMMER2_OFF_MASK_RADIX	0x000000000000003FULL
-#define HAMMER2_MAX_COPIES	6
 
 /*
  * HAMMER2 directory support and pre-defined keys
@@ -571,6 +524,7 @@ typedef struct hammer2_volconf hammer2_volconf_t;
 #define DMSG_VOLF_CONN_EF	0x40	/* media errors flagged */
 #define DMSG_VOLF_CONN_PRI	0x0F	/* select priority 0-15 (15=best) */
 
+#if 0	 /* Not needed for fstype(8) */
 struct dmsg_lnk_hammer2_volconf {
 	dmsg_hdr_t		head;
 	hammer2_volconf_t	copy;	/* copy spec */
@@ -586,6 +540,7 @@ typedef struct dmsg_lnk_hammer2_volconf dmsg_lnk_hammer2_volconf_t;
 					  dmsg_lnk_hammer2_volconf)
 
 #define H2_LNK_VOLCONF(msg)	((dmsg_lnk_hammer2_volconf_t *)(msg)->any.buf)
+#endif
 
 /*
  * HAMMER2 directory entry header (embedded in blockref)  exactly 16 bytes
@@ -759,11 +714,13 @@ typedef struct hammer2_blockref hammer2_blockref_t;
 #define HAMMER2_BREF_TYPE_DIRENT	4
 #define HAMMER2_BREF_TYPE_FREEMAP_NODE	5
 #define HAMMER2_BREF_TYPE_FREEMAP_LEAF	6
+#define HAMMER2_BREF_TYPE_INVALID	7
 #define HAMMER2_BREF_TYPE_FREEMAP	254	/* pseudo-type */
 #define HAMMER2_BREF_TYPE_VOLUME	255	/* pseudo-type */
 
 #define HAMMER2_BREF_FLAG_PFSROOT	0x01	/* see also related opflag */
-#define HAMMER2_BREF_FLAG_ZERO		0x02
+#define HAMMER2_BREF_FLAG_ZERO		0x02	/* NO LONGER USED */
+#define HAMMER2_BREF_FLAG_EMERG_MIP	0x04	/* emerg modified-in-place */
 
 /*
  * Encode/decode check mode and compression mode for
@@ -815,17 +772,12 @@ typedef struct hammer2_blockref hammer2_blockref_t;
 
 /*
  * HAMMER2 block references are collected into sets of 4 blockrefs.  These
- * sets are fully associative, meaning the elements making up a set are
- * not sorted in any way and may contain duplicate entries, holes, or
- * entries which shortcut multiple levels of indirection.  Sets are used
- * in various ways:
+ * sets are fully associative, meaning the elements making up a set may
+ * contain duplicate entries, holes, but valid elements are always sorted.
  *
- * (1) When redundancy is desired a set may contain several duplicate
- *     entries pointing to different copies of the same data.  Up to 4 copies
- *     are supported.
- *
- * (2) The blockrefs in a set can shortcut multiple levels of indirections
- *     within the bounds imposed by the parent of set.
+ * When redundancy is desired a set may contain several duplicate
+ * entries pointing to different copies of the same data.  Up to 4 copies
+ * are supported. Not implemented.
  *
  * When a set fills up another level of indirection is inserted, moving
  * some or all of the set's contents into indirect blocks placed under the
@@ -833,9 +785,6 @@ typedef struct hammer2_blockref hammer2_blockref_t;
  * until the set actually becomes full (that is, the entries in the set can
  * shortcut the indirect blocks when the set is not full).  Depending on how
  * things are filled multiple indirect blocks will eventually be created.
- *
- * Indirect blocks are typically 4KB (64 entres) or 64KB (1024 entries) and
- * are also treated as fully set-associative.
  */
 struct hammer2_blockset {
 	hammer2_blockref_t	blockref[HAMMER2_SET_COUNT];
@@ -899,6 +848,32 @@ typedef struct hammer2_blockset hammer2_blockset_t;
  *	     01 (reserved)
  *	     10 Possibly free
  *           11 Allocated
+ *
+ * ==========
+ * level6 freemap
+ * blockref[0]       : 4EB
+ * blockref[1]       : 4EB
+ * blockref[2]       : 4EB
+ * blockref[3]       : 4EB
+ * -----------------------------------------------------------------------
+ * 4 x 128B = 512B   : 4 x 4EB = 16EB
+ *
+ * level2-5 FREEMAP_NODE
+ * blockref[0]       : 1GB,256GB,64TB,16PB
+ * blockref[1]       : 1GB,256GB,64TB,16PB
+ * ...
+ * blockref[255]     : 1GB,256GB,64TB,16PB
+ * -----------------------------------------------------------------------
+ * 256 x 128B = 32KB : 256 x 1GB,256GB,64TB,16PB = 256GB,64TB,16PB,4EB
+ *
+ * level1 FREEMAP_LEAF
+ * bmap_data[0]      : 8 x 8B = 512bits = 256 x 2bits -> 256 x 16KB = 4MB
+ * bmap_data[1]      : 8 x 8B = 512bits = 256 x 2bits -> 256 x 16KB = 4MB
+ * ...
+ * bmap_data[255]    : 8 x 8B = 512bits = 256 x 2bits -> 256 x 16KB = 4MB
+ * -----------------------------------------------------------------------
+ * 256 x 128B = 32KB : 256 x 4MB = 1GB
+ * ==========
  */
 struct hammer2_bmap_data {
 	int32_t linear;		/* 00 linear sub-granular allocation offset */
@@ -919,12 +894,7 @@ struct hammer2_bmap_data {
 typedef struct hammer2_bmap_data hammer2_bmap_data_t;
 
 /*
- * XXX "Inodes ARE directory entries" is no longer the case.  Hardlinks are
- * dirents which refer to the same inode#, which is how filesystems usually
- * implement hardlink.  The following comments need to be updated.
- *
- * In HAMMER2 inodes ARE directory entries, with a special exception for
- * hardlinks.  The inode number is stored in the inode rather than being
+ * The inode number is stored in the inode rather than being
  * based on the location of the inode (since the location moves every time
  * the inode or anything underneath the inode is modified).
  *
@@ -948,18 +918,6 @@ typedef struct hammer2_bmap_data hammer2_bmap_data_t;
  *
  * The compression mode can be changed at any time in the inode and is
  * recorded on a blockref-by-blockref basis.
- *
- * Hardlinks are supported via the inode map.  Essentially the way a hardlink
- * works is that all individual directory entries representing the same file
- * are special cased and specify the same inode number.  The actual file
- * is placed in the nearest parent directory that is parent to all instances
- * of the hardlink.  If all hardlinks to a file are in the same directory
- * the actual file will also be placed in that directory.  This file uses
- * the inode number as the directory entry key and is invisible to normal
- * directory scans.  Real directory entry keys are differentiated from the
- * inode number key via bit 63.  Access to the hardlink silently looks up
- * the real file and forwards all operations to that file.  Removal of the
- * last hardlink also removes the real file.
  */
 #define HAMMER2_INODE_BYTES		1024	/* (asserted by code) */
 #define HAMMER2_INODE_MAXNAME		256	/* maximum name in bytes */
@@ -1020,7 +978,7 @@ struct hammer2_inode_meta {
 	uint8_t		check_algo;	/* 0085 check code request & algo */
 	uint8_t		pfs_nmasters;	/* 0086 (if PFSROOT) if multi-master */
 	uint8_t		pfs_type;	/* 0087 (if PFSROOT) node type */
-	uint64_t	pfs_inum;	/* 0088 (if PFSROOT) inum allocator */
+	hammer2_tid_t	pfs_inum;	/* 0088 (if PFSROOT) inum allocator */
 	uuid_t		pfs_clid;	/* 0090 (if PFSROOT) cluster uuid */
 	uuid_t		pfs_fsid;	/* 00A0 (if PFSROOT) unique uuid */
 
@@ -1148,37 +1106,8 @@ typedef struct hammer2_inode_data hammer2_inode_data_t;
 #define HAMMER2_PFSMODE_RW		0x02
 
 /*
- *				Allocation Table
- *
- */
-
-
-/*
- * Flags (8 bits) - blockref, for freemap only
- *
- * Note that the minimum chunk size is 1KB so we could theoretically have
- * 10 bits here, but we might have some future extension that allows a
- * chunk size down to 256 bytes and if so we will need bits 8 and 9.
- */
-#define HAMMER2_AVF_SELMASK		0x03	/* select group */
-#define HAMMER2_AVF_ALL_ALLOC		0x04	/* indicate all allocated */
-#define HAMMER2_AVF_ALL_FREE		0x08	/* indicate all free */
-#define HAMMER2_AVF_RESERVED10		0x10
-#define HAMMER2_AVF_RESERVED20		0x20
-#define HAMMER2_AVF_RESERVED40		0x40
-#define HAMMER2_AVF_RESERVED80		0x80
-#define HAMMER2_AVF_AVMASK32		((uint32_t)0xFFFFFF00LU)
-#define HAMMER2_AVF_AVMASK64		((uint64_t)0xFFFFFFFFFFFFFF00LLU)
-
-#define HAMMER2_AV_SELECT_A		0x00
-#define HAMMER2_AV_SELECT_B		0x01
-#define HAMMER2_AV_SELECT_C		0x02
-#define HAMMER2_AV_SELECT_D		0x03
-
-/*
- * The volume header eats a 64K block.  There is currently an issue where
- * we want to try to fit all nominal filesystem updates in a 512-byte section
- * but it may be a lost cause due to the need for a blockset.
+ * The volume header eats a 64K block at the beginning of each 2GB zone
+ * up to four copies.
  *
  * All information is stored in host byte order.  The volume header's magic
  * number may be checked to determine the byte order.  If you wish to mount
@@ -1205,7 +1134,7 @@ typedef struct hammer2_inode_data hammer2_inode_data_t;
  *	 allow HAMMER2 to pick up the others when it checks the copyinfo[]
  *	 array on mount.
  *
- * NOTE: root_blockref points to the super-root directory, not the root
+ * NOTE: sroot_blockset points to the super-root directory, not the root
  *	 directory.  The root directory will be a subdirectory under the
  *	 super-root.
  *
@@ -1213,17 +1142,20 @@ typedef struct hammer2_inode_data hammer2_inode_data_t;
  *	 snapshots (readonly or writable).  It is possible to do a
  *	 null-mount of the super-root using special path constructions
  *	 relative to your mounted root.
- *
- * NOTE: HAMMER2 allows any subdirectory tree to be managed as if it were
- *	 a PFS, including mirroring and storage quota operations, and this is
- *	 preferred over creating discrete PFSs in the super-root.  Instead
- *	 the super-root is most typically used to create writable snapshots,
- *	 alternative roots, and so forth.  The super-root is also used by
- *	 the automatic snapshotting mechanism.
  */
 #define HAMMER2_VOLUME_ID_HBO	0x48414d3205172011LLU
 #define HAMMER2_VOLUME_ID_ABO	0x11201705324d4148LLU
 
+/*
+ * If volume version is HAMMER2_VOL_VERSION_MULTI_VOLUMES or above, max
+ * HAMMER2_MAX_VOLUMES volumes are supported. There must be 1 (and only 1)
+ * volume with volume id HAMMER2_ROOT_VOLUME.
+ * Otherwise filesystem only supports 1 volume, and that volume must have
+ * volume id HAMMER2_ROOT_VOLUME(0) which was a reserved field then.
+ */
+#define HAMMER2_MAX_VOLUMES	64
+#define HAMMER2_ROOT_VOLUME	0
+
 struct hammer2_volume_data {
 	/*
 	 * sector #0 - 512 bytes
@@ -1240,15 +1172,17 @@ struct hammer2_volume_data {
 	uint8_t		copyid;			/* 0038 copyid of phys vol */
 	uint8_t		freemap_version;	/* 0039 freemap algorithm */
 	uint8_t		peer_type;		/* 003A HAMMER2_PEER_xxx */
-	uint8_t		reserved003B;		/* 003B */
-	uint32_t	reserved003C;		/* 003C */
+	uint8_t		volu_id;		/* 003B */
+	uint8_t		nvolumes;		/* 003C */
+	uint8_t		reserved003D;		/* 003D */
+	uint16_t	reserved003E;		/* 003E */
 
 	uuid_t		fsid;			/* 0040 */
 	uuid_t		fstype;			/* 0050 */
 
 	/*
 	 * allocator_size is precalculated at newfs time and does not include
-	 * reserved blocks, boot, or redo areas.
+	 * reserved blocks, boot, or aux areas.
 	 *
 	 * Initial non-reserved-area allocations do not use the freemap
 	 * but instead adjust alloc_iterator.  Dynamic allocations take
@@ -1272,7 +1206,9 @@ struct hammer2_volume_data {
 	hammer2_tid_t	reserved0088;		/* 0088 */
 	hammer2_tid_t	freemap_tid;		/* 0090 committed tid (fmap) */
 	hammer2_tid_t	bulkfree_tid;		/* 0098 bulkfree incremental */
-	hammer2_tid_t	reserved00A0[5];	/* 00A0-00C7 */
+	hammer2_tid_t	reserved00A0[4];	/* 00A0-00BF */
+
+	hammer2_off_t	total_size;		/* 00C0 Total volume size, bytes */
 
 	/*
 	 * Copyids are allocated dynamically from the copyexists bitmap.
@@ -1302,19 +1238,25 @@ struct hammer2_volume_data {
 	/*
 	 * sector #1 - 512 bytes
 	 *
-	 * The entire sector is used by a blockset.
+	 * The entire sector is used by a blockset, but currently only first
+	 * blockref is used.
 	 */
 	hammer2_blockset_t sroot_blockset;	/* 0200-03FF Superroot dir */
 
 	/*
-	 * sector #2-7
+	 * sector #2-6
 	 */
 	char	sector2[512];			/* 0400-05FF reserved */
 	char	sector3[512];			/* 0600-07FF reserved */
 	hammer2_blockset_t freemap_blockset;	/* 0800-09FF freemap  */
 	char	sector5[512];			/* 0A00-0BFF reserved */
 	char	sector6[512];			/* 0C00-0DFF reserved */
-	char	sector7[512];			/* 0E00-0FFF reserved */
+
+	/*
+	 * sector #7 - 512 bytes
+	 * Maximum 64 volume offsets within logical offset.
+	 */
+	hammer2_off_t volu_loff[HAMMER2_MAX_VOLUMES];
 
 	/*
 	 * sector #8-71	- 32768 bytes
@@ -1323,10 +1265,6 @@ struct hammer2_volume_data {
 	 * specify local and remote copies operating as masters or slaves.
 	 * copyid's 0 and 255 are reserved (0 indicates an empty slot and 255
 	 * indicates the local media).
-	 *
-	 * Each inode contains a set of up to 8 copyids, either inherited
-	 * from its parent or explicitly specified in the inode, which
-	 * indexes into this array.
 	 */
 						/* 1000-8FFF copyinfo config */
 	hammer2_volconf_t copyinfo[HAMMER2_COPYID_COUNT];
@@ -1370,9 +1308,11 @@ typedef struct hammer2_volume_data hammer2_volume_data_t;
 #define HAMMER2_VOLUME_ICRC1_SIZE	(512)
 #define HAMMER2_VOLUME_ICRCVH_SIZE	(65536 - 4)
 
+#define HAMMER2_VOL_VERSION_MULTI_VOLUMES	2
+
 #define HAMMER2_VOL_VERSION_MIN		1
-#define HAMMER2_VOL_VERSION_DEFAULT	1
-#define HAMMER2_VOL_VERSION_WIP 	2
+#define HAMMER2_VOL_VERSION_DEFAULT	HAMMER2_VOL_VERSION_MULTI_VOLUMES
+#define HAMMER2_VOL_VERSION_WIP		(HAMMER2_VOL_VERSION_MULTI_VOLUMES + 1)
 
 #define HAMMER2_NUM_VOLHDRS		4
 
-- 
2.21.0

