From owner-svn-src-all@freebsd.org Tue Dec 24 19:00:21 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id D49CE1EE536; Tue, 24 Dec 2019 19:00:21 +0000 (UTC) (envelope-from pfg@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 47j5Bs5yksz46rW; Tue, 24 Dec 2019 19:00:21 +0000 (UTC) (envelope-from pfg@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id AD9944990; Tue, 24 Dec 2019 19:00:21 +0000 (UTC) (envelope-from pfg@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id xBOJ0LoU026841; Tue, 24 Dec 2019 19:00:21 GMT (envelope-from pfg@FreeBSD.org) Received: (from pfg@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id xBOJ0K4n026833; Tue, 24 Dec 2019 19:00:20 GMT (envelope-from pfg@FreeBSD.org) Message-Id: <201912241900.xBOJ0K4n026833@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: pfg set sender to pfg@FreeBSD.org using -f From: "Pedro F. Giffuni" Date: Tue, 24 Dec 2019 19:00:20 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r356060 - head/usr.sbin/fstyp X-SVN-Group: head X-SVN-Commit-Author: pfg X-SVN-Commit-Paths: head/usr.sbin/fstyp X-SVN-Commit-Revision: 356060 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 24 Dec 2019 19:00:21 -0000 Author: pfg Date: Tue Dec 24 19:00:20 2019 New Revision: 356060 URL: https://svnweb.freebsd.org/changeset/base/356060 Log: sbin/fstyp: recgonize Dragonfly's hammer and hammer2. This is based on DragonFly's implementation from about 2019-09-13. It only contains the basic code and header information to identify the disks. Relnotes: yes Differential Revision: https://reviews.freebsd.org/D13369 Added: head/usr.sbin/fstyp/hammer.c (contents, props changed) head/usr.sbin/fstyp/hammer2.c (contents, props changed) head/usr.sbin/fstyp/hammer2_disk.h (contents, props changed) head/usr.sbin/fstyp/hammer_disk.h (contents, props changed) Modified: head/usr.sbin/fstyp/Makefile head/usr.sbin/fstyp/fstyp.8 head/usr.sbin/fstyp/fstyp.c head/usr.sbin/fstyp/fstyp.h Modified: head/usr.sbin/fstyp/Makefile ============================================================================== --- head/usr.sbin/fstyp/Makefile Tue Dec 24 18:38:06 2019 (r356059) +++ head/usr.sbin/fstyp/Makefile Tue Dec 24 19:00:20 2019 (r356060) @@ -3,7 +3,8 @@ .include PROG= fstyp -SRCS= apfs.c cd9660.c exfat.c ext2fs.c fstyp.c geli.c hfsplus.c msdosfs.c ntfs.c ufs.c +SRCS= apfs.c cd9660.c exfat.c ext2fs.c fstyp.c geli.c hammer.c \ + hammer2.c hfsplus.c msdosfs.c ntfs.c ufs.c .if ${MK_ZFS} != "no" SRCS += zfs.c Modified: head/usr.sbin/fstyp/fstyp.8 ============================================================================== --- head/usr.sbin/fstyp/fstyp.8 Tue Dec 24 18:38:06 2019 (r356059) +++ head/usr.sbin/fstyp/fstyp.8 Tue Dec 24 19:00:20 2019 (r356060) @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 26, 2017 +.Dd December 24, 2019 .Dt FSTYP 8 .Os .Sh NAME @@ -67,6 +67,10 @@ exfat ext2fs .It geli +.It +hammer +.It +hammer2 .It msdosfs .It Modified: head/usr.sbin/fstyp/fstyp.c ============================================================================== --- head/usr.sbin/fstyp/fstyp.c Tue Dec 24 18:38:06 2019 (r356059) +++ head/usr.sbin/fstyp/fstyp.c Tue Dec 24 19:00:20 2019 (r356060) @@ -69,6 +69,8 @@ static struct { { "exfat", &fstyp_exfat, false, EXFAT_ENC }, { "ext2fs", &fstyp_ext2fs, false, NULL }, { "geli", &fstyp_geli, true, NULL }, + { "hammer", &fstyp_hammer, true, NULL }, + { "hammer2", &fstyp_hammer2, true, NULL }, { "hfs+", &fstyp_hfsp, false, NULL }, { "msdosfs", &fstyp_msdosfs, false, NULL }, { "ntfs", &fstyp_ntfs, false, NTFS_ENC }, Modified: head/usr.sbin/fstyp/fstyp.h ============================================================================== --- head/usr.sbin/fstyp/fstyp.h Tue Dec 24 18:38:06 2019 (r356059) +++ head/usr.sbin/fstyp/fstyp.h Tue Dec 24 19:00:20 2019 (r356060) @@ -55,6 +55,8 @@ int fstyp_cd9660(FILE *fp, char *label, size_t size); int fstyp_exfat(FILE *fp, char *label, size_t size); int fstyp_ext2fs(FILE *fp, char *label, size_t size); int fstyp_geli(FILE *fp, char *label, size_t size); +int fstyp_hammer(FILE *fp, char *label, size_t size); +int fstyp_hammer2(FILE *fp, char *label, size_t size); int fstyp_hfsp(FILE *fp, char *label, size_t size); int fstyp_msdosfs(FILE *fp, char *label, size_t size); int fstyp_ntfs(FILE *fp, char *label, size_t size); Added: head/usr.sbin/fstyp/hammer.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/fstyp/hammer.c Tue Dec 24 19:00:20 2019 (r356060) @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2016 The DragonFly Project + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include + +#include "hammer_disk.h" + +#include "fstyp.h" + +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); +} + +static int +__test_ondisk(const hammer_volume_ondisk_t ondisk) +{ + static int count = 0; + static hammer_uuid_t fsid, fstype; + static char label[64]; + + if (ondisk->vol_signature != HAMMER_FSBUF_VOLUME && + ondisk->vol_signature != HAMMER_FSBUF_VOLUME_REV) + return (1); + if (ondisk->vol_rootvol != HAMMER_ROOT_VOLNO) + return (2); + if (ondisk->vol_no < 0 || ondisk->vol_no > HAMMER_MAX_VOLUMES - 1) + return (3); + if (ondisk->vol_count < 1 || ondisk->vol_count > HAMMER_MAX_VOLUMES) + return (4); + + if (count == 0) { + count = ondisk->vol_count; + assert(count != 0); + memcpy(&fsid, &ondisk->vol_fsid, sizeof(fsid)); + memcpy(&fstype, &ondisk->vol_fstype, sizeof(fstype)); + strncpy(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); + if (strncmp(ondisk->vol_label, label, sizeof(label))) + return (8); + } + + return (0); +} + +int +fstyp_hammer(FILE *fp, char *label, size_t size) +{ + hammer_volume_ondisk_t ondisk; + int error = 1; + + ondisk = __read_ondisk(fp); + if (ondisk->vol_no != HAMMER_ROOT_VOLNO) + goto done; + if (ondisk->vol_count != 1) + goto done; + if (__test_ondisk(ondisk)) + goto done; + + strlcpy(label, ondisk->vol_label, size); + error = 0; +done: + free(ondisk); + return (error); +} + +static int +__test_volume(const char *volpath) +{ + hammer_volume_ondisk_t ondisk; + FILE *fp; + int volno = -1; + + if ((fp = fopen(volpath, "r")) == NULL) + err(1, "failed to open %s", volpath); + + ondisk = __read_ondisk(fp); + fclose(fp); + if (__test_ondisk(ondisk)) + goto done; + + volno = ondisk->vol_no; +done: + free(ondisk); + return (volno); +} + +static int +__fsvtyp_hammer(const char *blkdevs, char *label, size_t size, int partial) +{ + hammer_volume_ondisk_t ondisk; + FILE *fp; + char *dup, *p, *volpath, x[HAMMER_MAX_VOLUMES]; + int i, volno, error = 1; + + memset(x, 0, sizeof(x)); + dup = strdup(blkdevs); + p = dup; + + while (p) { + volpath = p; + if ((p = strchr(p, ':')) != NULL) + *p++ = '\0'; + if ((volno = __test_volume(volpath)) == -1) + break; + x[volno]++; + } + + 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 done; + if (partial) + goto success; + + for (i = 0; i < HAMMER_MAX_VOLUMES; i++) + if (x[i] > 1) + goto done; + for (i = 0; i < HAMMER_MAX_VOLUMES; i++) + if (x[i] == 0) + break; + if (ondisk->vol_count != i) + goto done; + for (; i < HAMMER_MAX_VOLUMES; i++) + if (x[i] != 0) + goto done; +success: + strlcpy(label, ondisk->vol_label, size); + error = 0; +done: + 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)); +} Added: head/usr.sbin/fstyp/hammer2.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/fstyp/hammer2.c Tue Dec 24 19:00:20 2019 (r356060) @@ -0,0 +1,152 @@ +/*- + * Copyright (c) 2017-2019 The DragonFly Project + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#include "hammer2_disk.h" + +#include "fstyp.h" + +static hammer2_volume_data_t* +__read_voldata(FILE *fp) +{ + hammer2_volume_data_t *voldata; + + voldata = read_buf(fp, 0, sizeof(*voldata)); + if (voldata == NULL) + err(1, "failed to read volume data"); + + return (voldata); +} + +static int +__test_voldata(const hammer2_volume_data_t *voldata) +{ + if (voldata->magic != HAMMER2_VOLUME_ID_HBO && + voldata->magic != HAMMER2_VOLUME_ID_ABO) + return (1); + + return (0); +} + +static int +__read_label(FILE *fp, char *label, size_t size) +{ + hammer2_blockref_t broot, best, *bref; + hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media; + hammer2_off_t io_off, io_base; + size_t bytes, io_bytes, boff; + int i, best_i, error = 0; + + best_i = -1; + memset(&best, 0, sizeof(best)); + + for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) { + 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])); + broot.mirror_tid = vols[i]->voldata.mirror_tid; + if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { + best_i = i; + best = broot; + } + } + if (best_i == -1) { + warnx("Failed to find volume header from zones"); + error = 1; + goto done; + } + + bref = &vols[best_i]->voldata.sroot_blockset.blockref[0]; + if (bref->type != HAMMER2_BREF_TYPE_INODE) { + warnx("Superroot blockref type is not inode"); + error = 2; + goto done; + } + + bytes = bref->data_off & HAMMER2_OFF_MASK_RADIX; + if (bytes) + bytes = (size_t)1 << bytes; + if (bytes != sizeof(hammer2_inode_data_t)) { + warnx("Superroot blockref size does not match inode size"); + error = 3; + goto done; + } + + io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; + io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); + boff = io_off - io_base; + + io_bytes = HAMMER2_MINIOSIZE; + while (io_bytes + boff < bytes) + io_bytes <<= 1; + if (io_bytes > sizeof(*media)) { + warnx("Invalid I/O bytes"); + error = 4; + goto done; + } + + media = read_buf(fp, io_base, io_bytes); + if (boff) + memcpy(media, (char*)media + boff, bytes); + + strlcpy(label, (char*)media->ipdata.filename, size); + free(media); +done: + for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) + free(vols[i]); + + return (error); +} + +int +fstyp_hammer2(FILE *fp, char *label, size_t size) +{ + hammer2_volume_data_t *voldata; + int error = 1; + + voldata = __read_voldata(fp); + if (__test_voldata(voldata)) + goto done; + + error = __read_label(fp, label, size); +done: + free(voldata); + return (error); +} Added: head/usr.sbin/fstyp/hammer2_disk.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/fstyp/hammer2_disk.h Tue Dec 24 19:00:20 2019 (r356060) @@ -0,0 +1,1390 @@ +/*- + * Copyright (c) 2011-2018 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * by Venkatesh Srinivas + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _HAMMER2_DISK_H_ +#define _HAMMER2_DISK_H_ + +#ifndef _SYS_UUID_H_ +#include +#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 + * filesystem. Note that all fields for on-disk structures are naturally + * aligned. The host endian format is typically used - compatibility is + * possible if the implementation detects reversed endian and adjusts accesses + * accordingly. + * + * HAMMER2 primarily revolves around the directory topology: inodes, + * directory entries, and block tables. Block device buffer cache buffers + * are always 64KB. Logical file buffers are typically 16KB. All data + * references utilize 64-bit byte offsets. + * + * Free block management is handled independently using blocks reserved by + * the media topology. + */ + +/* + * The data at the end of a file or directory may be a fragment in order + * to optimize storage efficiency. The minimum fragment size is 1KB. + * Since allocations are in powers of 2 fragments must also be sized in + * powers of 2 (1024, 2048, ... 65536). + * + * For the moment the maximum allocation size is HAMMER2_PBUFSIZE (64K), + * which is 2^16. Larger extents may be supported in the future. Smaller + * fragments might be supported in the future (down to 64 bytes is possible), + * but probably will not be. + * + * A full indirect block use supports 512 x 128-byte blockrefs in a 64KB + * buffer. Indirect blocks down to 1KB are supported to keep small + * directories small. + * + * A maximally sized file (2^64-1 bytes) requires ~6 indirect block levels + * using 64KB indirect blocks (128 byte refs, 512 or radix 9 per indblk). + * + * 16(datablk) + 9 + 9 + 9 + 9 + 9 + 9 = ~70. + * 16(datablk) + 7 + 9 + 9 + 9 + 9 + 9 = ~68. (smaller top level indblk) + * + * The actual depth depends on copies redundancy and whether the filesystem + * has chosen to use a smaller indirect block size at the top level or not. + */ +#define HAMMER2_ALLOC_MIN 1024 /* minimum allocation size */ +#define HAMMER2_RADIX_MIN 10 /* minimum allocation size 2^N */ +#define HAMMER2_ALLOC_MAX 65536 /* maximum allocation size */ +#define HAMMER2_RADIX_MAX 16 /* maximum allocation size 2^N */ +#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 + * blocks except the block straddling EOF. + * + * HAMMER2_SEGSIZE - Allocation map segment size, typically 4MB + * (space represented by a level0 bitmap). + */ + +#define HAMMER2_SEGSIZE (1 << HAMMER2_FREEMAP_LEVEL0_RADIX) +#define HAMMER2_SEGRADIX HAMMER2_FREEMAP_LEVEL0_RADIX + +#define HAMMER2_PBUFRADIX 16 /* physical buf (1<<16) bytes */ +#define HAMMER2_PBUFSIZE 65536 +#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 +#define HAMMER2_IND_RADIX_MIN 12 +#define HAMMER2_IND_RADIX_NOM HAMMER2_LBUFRADIX +#define HAMMER2_IND_RADIX_MAX HAMMER2_PBUFRADIX +#define HAMMER2_IND_COUNT_MIN (HAMMER2_IND_BYTES_MIN / \ + sizeof(hammer2_blockref_t)) +#define HAMMER2_IND_COUNT_MAX (HAMMER2_IND_BYTES_MAX / \ + sizeof(hammer2_blockref_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. + * + * 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) +#define HAMMER2_EMBEDDED_BYTES 512 /* inode blockset/dd size */ +#define HAMMER2_EMBEDDED_RADIX 9 + +#define HAMMER2_PBUFMASK (HAMMER2_PBUFSIZE - 1) +#define HAMMER2_LBUFMASK (HAMMER2_LBUFSIZE - 1) +#define HAMMER2_SEGMASK (HAMMER2_SEGSIZE - 1) + +#define HAMMER2_LBUFMASK64 ((hammer2_off_t)HAMMER2_LBUFMASK) +#define HAMMER2_PBUFSIZE64 ((hammer2_off_t)HAMMER2_PBUFSIZE) +#define HAMMER2_PBUFMASK64 ((hammer2_off_t)HAMMER2_PBUFMASK) +#define HAMMER2_SEGSIZE64 ((hammer2_off_t)HAMMER2_SEGSIZE) +#define HAMMER2_SEGMASK64 ((hammer2_off_t)HAMMER2_SEGMASK) + +#define HAMMER2_UUID_STRING "5cbb9ad1-862d-11dc-a94d-01301bb8a9f5" + +/* + * A 4MB segment is reserved at the beginning of each 2GB zone. 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. + * + * 4MB = 64 x 64K blocks. Each 4MB segment is broken down as follows: + * + * ========== + * 0 volume header (for the first four 2GB zones) + * 1 freemap00 level1 FREEMAP_LEAF (256 x 128B bitmap data per 1GB) + * 2 level2 FREEMAP_NODE (256 x 128B indirect block per 256GB) + * 3 level3 FREEMAP_NODE (256 x 128B indirect block per 64TB) + * 4 level4 FREEMAP_NODE (256 x 128B indirect block per 16PB) + * 5 level5 FREEMAP_NODE (256 x 128B indirect block per 4EB) + * 6 freemap01 level1 (rotation) + * 7 level2 + * 8 level3 + * 9 level4 + * 10 level5 + * 11 freemap02 level1 (rotation) + * 12 level2 + * 13 level3 + * 14 level4 + * 15 level5 + * 16 freemap03 level1 (rotation) + * 17 level2 + * 18 level3 + * 19 level4 + * 20 level5 + * 21 freemap04 level1 (rotation) + * 22 level2 + * 23 level3 + * 24 level4 + * 25 level5 + * 26 freemap05 level1 (rotation) + * 27 level2 + * 28 level3 + * 29 level4 + * 30 level5 + * 31 freemap06 level1 (rotation) + * 32 level2 + * 33 level3 + * 34 level4 + * 35 level5 + * 36 freemap07 level1 (rotation) + * 37 level2 + * 38 level3 + * 39 level4 + * 40 level5 + * 41 unused + * .. unused + * 63 unused + * ========== + * + * The first four 2GB zones contain volume headers and volume header backups. + * After that the volume header block# is reserved for future use. Similarly, + * there are many blocks related to various Freemap levels which are not + * used in every segment and those are also reserved for future use. + * Note that each FREEMAP_LEAF or FREEMAP_NODE uses 32KB out of 64KB slot. + * + * Freemap (see the FREEMAP document) + * + * The freemap utilizes blocks #1-40 in 8 sets of 5 blocks. Each block in + * a set represents a level of depth in the freemap topology. Eight sets + * exist to prevent live updates from disturbing the state of the freemap + * were a crash/reboot to occur. That is, a live update is not committed + * until the update's flush reaches the volume root. There are FOUR volume + * roots representing the last four synchronization points, so the freemap + * must be consistent no matter which volume root is chosen by the mount + * code. + * + * Each freemap set is 5 x 64K blocks and represents the 1GB, 256GB, 64TB, + * 16PB and 4EB indirect map. The volume header itself has a set of 4 freemap + * blockrefs representing another 2 bits, giving us a total 64 bits of + * representable address space. + * + * The Level 0 64KB block represents 1GB of storage represented by 32KB + * (256 x struct hammer2_bmap_data). Each structure represents 4MB of storage + * and has a 512 bit bitmap, using 2 bits to represent a 16KB chunk of + * storage. These 2 bits represent the following states: + * + * 00 Free + * 01 (reserved) (Possibly partially allocated) + * 10 Possibly free + * 11 Allocated + * + * One important thing to note here is that the freemap resolution is 16KB, + * but the minimum storage allocation size is 1KB. The hammer2 vfs keeps + * track of sub-allocations in memory, which means that on a unmount or reboot + * the entire 16KB of a partially allocated block will be considered fully + * allocated. It is possible for fragmentation to build up over time, but + * defragmentation is fairly easy to accomplish since all modifications + * allocate a new block. + * + * The Second thing to note is that due to the way snapshots and inode + * replication works, deleting a file cannot immediately free the related + * space. Furthermore, deletions often do not bother to traverse the + * block subhierarchy being deleted. And to go even further, whole + * sub-directory trees can be deleted simply by deleting the directory inode + * at the top. So even though we have a symbol to represent a 'possibly free' + * block (binary 10), only the bulk free scanning code can actually use it. + * Normal 'rm's or other deletions do not. + * + * WARNING! ZONE_SEG and VOLUME_ALIGN must be a multiple of 1<= ZONE_SEG. + * + * In Summary: + * + * (1) Modifications to freemap blocks 'allocate' a new copy (aka use a block + * from the next set). The new copy is reused until a flush occurs at + * which point the next modification will then rotate to the next set. + */ +#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_NEWFS_ALIGN (HAMMER2_VOLUME_ALIGN) +#define HAMMER2_NEWFS_ALIGN64 ((hammer2_off_t)HAMMER2_VOLUME_ALIGN) +#define HAMMER2_NEWFS_ALIGNMASK (HAMMER2_VOLUME_ALIGN - 1) +#define HAMMER2_NEWFS_ALIGNMASK64 ((hammer2_off_t)HAMMER2_NEWFS_ALIGNMASK) + +#define HAMMER2_ZONE_BYTES64 (2LLU * 1024 * 1024 * 1024) +#define HAMMER2_ZONE_MASK64 (HAMMER2_ZONE_BYTES64 - 1) +#define HAMMER2_ZONE_SEG (4 * 1024 * 1024) +#define HAMMER2_ZONE_SEG64 ((hammer2_off_t)HAMMER2_ZONE_SEG) +#define HAMMER2_ZONE_BLOCKS_SEG (HAMMER2_ZONE_SEG / HAMMER2_PBUFSIZE) + +#define HAMMER2_ZONE_FREEMAP_INC 5 /* 5 deep */ + +#define HAMMER2_ZONE_VOLHDR 0 /* volume header or backup */ +#define HAMMER2_ZONE_FREEMAP_00 1 /* normal freemap rotation */ +#define HAMMER2_ZONE_FREEMAP_01 6 /* normal freemap rotation */ +#define HAMMER2_ZONE_FREEMAP_02 11 /* normal freemap rotation */ +#define HAMMER2_ZONE_FREEMAP_03 16 /* normal freemap rotation */ +#define HAMMER2_ZONE_FREEMAP_04 21 /* normal freemap rotation */ +#define HAMMER2_ZONE_FREEMAP_05 26 /* normal freemap rotation */ +#define HAMMER2_ZONE_FREEMAP_06 31 /* normal freemap rotation */ +#define HAMMER2_ZONE_FREEMAP_07 36 /* normal freemap rotation */ +#define HAMMER2_ZONE_FREEMAP_END 41 /* (non-inclusive) */ + +#define HAMMER2_ZONE_UNUSED41 41 +#define HAMMER2_ZONE_UNUSED42 42 +#define HAMMER2_ZONE_UNUSED43 43 +#define HAMMER2_ZONE_UNUSED44 44 +#define HAMMER2_ZONE_UNUSED45 45 +#define HAMMER2_ZONE_UNUSED46 46 +#define HAMMER2_ZONE_UNUSED47 47 +#define HAMMER2_ZONE_UNUSED48 48 +#define HAMMER2_ZONE_UNUSED49 49 +#define HAMMER2_ZONE_UNUSED50 50 +#define HAMMER2_ZONE_UNUSED51 51 +#define HAMMER2_ZONE_UNUSED52 52 +#define HAMMER2_ZONE_UNUSED53 53 +#define HAMMER2_ZONE_UNUSED54 54 +#define HAMMER2_ZONE_UNUSED55 55 +#define HAMMER2_ZONE_UNUSED56 56 +#define HAMMER2_ZONE_UNUSED57 57 +#define HAMMER2_ZONE_UNUSED58 58 +#define HAMMER2_ZONE_UNUSED59 59 +#define HAMMER2_ZONE_UNUSED60 60 +#define HAMMER2_ZONE_UNUSED61 61 +#define HAMMER2_ZONE_UNUSED62 62 +#define HAMMER2_ZONE_UNUSED63 63 +#define HAMMER2_ZONE_END 64 /* non-inclusive */ + +#define HAMMER2_NFREEMAPS 8 /* FREEMAP_00 - FREEMAP_07 */ + + /* relative to FREEMAP_x */ +#define HAMMER2_ZONEFM_LEVEL1 0 /* 1GB leafmap */ +#define HAMMER2_ZONEFM_LEVEL2 1 /* 256GB indmap */ +#define HAMMER2_ZONEFM_LEVEL3 2 /* 64TB indmap */ +#define HAMMER2_ZONEFM_LEVEL4 3 /* 16PB indmap */ +#define HAMMER2_ZONEFM_LEVEL5 4 /* 4EB indmap */ +/* LEVEL6 is a set of 4 blockrefs in the volume header 16EB */ + +/* + * Freemap radix. Assumes a set-count of 4, 128-byte blockrefs, + * 32KB indirect block for freemap (LEVELN_PSIZE below). + * + * Leaf entry represents 4MB of storage broken down into a 512-bit + * bitmap, 2-bits per entry. So course bitmap item represents 16KB. + */ +#if HAMMER2_SET_COUNT != 4 +#error "hammer2_disk.h - freemap assumes SET_COUNT is 4" +#endif +#define HAMMER2_FREEMAP_LEVEL6_RADIX 64 /* 16EB (end) */ +#define HAMMER2_FREEMAP_LEVEL5_RADIX 62 /* 4EB */ +#define HAMMER2_FREEMAP_LEVEL4_RADIX 54 /* 16PB */ +#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_LEVELN_PSIZE 32768 /* physical bytes */ + +#define HAMMER2_FREEMAP_LEVEL5_SIZE ((hammer2_off_t)1 << \ + HAMMER2_FREEMAP_LEVEL5_RADIX) +#define HAMMER2_FREEMAP_LEVEL4_SIZE ((hammer2_off_t)1 << \ + HAMMER2_FREEMAP_LEVEL4_RADIX) +#define HAMMER2_FREEMAP_LEVEL3_SIZE ((hammer2_off_t)1 << \ + HAMMER2_FREEMAP_LEVEL3_RADIX) +#define HAMMER2_FREEMAP_LEVEL2_SIZE ((hammer2_off_t)1 << \ + HAMMER2_FREEMAP_LEVEL2_RADIX) +#define HAMMER2_FREEMAP_LEVEL1_SIZE ((hammer2_off_t)1 << \ + HAMMER2_FREEMAP_LEVEL1_RADIX) +#define HAMMER2_FREEMAP_LEVEL0_SIZE ((hammer2_off_t)1 << \ + HAMMER2_FREEMAP_LEVEL0_RADIX) + +#define HAMMER2_FREEMAP_LEVEL5_MASK (HAMMER2_FREEMAP_LEVEL5_SIZE - 1) +#define HAMMER2_FREEMAP_LEVEL4_MASK (HAMMER2_FREEMAP_LEVEL4_SIZE - 1) +#define HAMMER2_FREEMAP_LEVEL3_MASK (HAMMER2_FREEMAP_LEVEL3_SIZE - 1) +#define HAMMER2_FREEMAP_LEVEL2_MASK (HAMMER2_FREEMAP_LEVEL2_SIZE - 1) +#define HAMMER2_FREEMAP_LEVEL1_MASK (HAMMER2_FREEMAP_LEVEL1_SIZE - 1) +#define HAMMER2_FREEMAP_LEVEL0_MASK (HAMMER2_FREEMAP_LEVEL0_SIZE - 1) + +#define HAMMER2_FREEMAP_COUNT (int)(HAMMER2_FREEMAP_LEVELN_PSIZE / \ + sizeof(hammer2_bmap_data_t)) + +/* + * XXX I made a mistake and made the reserved area begin at each LEVEL1 zone, + * which is on a 1GB demark. This will eat a little more space but for + * 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)) + +/* + * 16KB bitmap granularity (x2 bits per entry). + */ +#define HAMMER2_FREEMAP_BLOCK_RADIX 14 +#define HAMMER2_FREEMAP_BLOCK_SIZE (1 << HAMMER2_FREEMAP_BLOCK_RADIX) +#define HAMMER2_FREEMAP_BLOCK_MASK (HAMMER2_FREEMAP_BLOCK_SIZE - 1) + +/* + * bitmap[] structure. 2 bits per HAMMER2_FREEMAP_BLOCK_SIZE. + * + * 8 x 64-bit elements, 2 bits per block. + * 32 blocks (radix 5) per element. + * representing INDEX_SIZE bytes worth of storage per element. + */ + +typedef uint64_t hammer2_bitmap_t; + +#define HAMMER2_BMAP_ALLONES ((hammer2_bitmap_t)-1) +#define HAMMER2_BMAP_ELEMENTS 8 +#define HAMMER2_BMAP_BITS_PER_ELEMENT 64 +#define HAMMER2_BMAP_INDEX_RADIX 5 /* 32 blocks per element */ +#define HAMMER2_BMAP_BLOCKS_PER_ELEMENT (1 << HAMMER2_BMAP_INDEX_RADIX) + +#define HAMMER2_BMAP_INDEX_SIZE (HAMMER2_FREEMAP_BLOCK_SIZE * \ + HAMMER2_BMAP_BLOCKS_PER_ELEMENT) +#define HAMMER2_BMAP_INDEX_MASK (HAMMER2_BMAP_INDEX_SIZE - 1) + +#define HAMMER2_BMAP_SIZE (HAMMER2_BMAP_INDEX_SIZE * \ + HAMMER2_BMAP_ELEMENTS) +#define HAMMER2_BMAP_MASK (HAMMER2_BMAP_SIZE - 1) + +/* + * Two linear areas can be reserved after the initial 4MB segment in the base + * zone (the one starting at offset 0). These areas are NOT managed by the + * block allocator and do not fall under HAMMER2 crc checking rules based + * at the volume header (but can be self-CRCd internally, depending). + */ +#define HAMMER2_BOOT_MIN_BYTES HAMMER2_VOLUME_ALIGN +#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) + +/* + * Most HAMMER2 types are implemented as unsigned 64-bit integers. + * Transaction ids are monotonic. + * + * We utilize 32-bit iSCSI CRCs. + */ +typedef uint64_t hammer2_tid_t; +typedef uint64_t hammer2_off_t; +typedef uint64_t hammer2_key_t; +typedef uint32_t hammer2_crc32_t; + +/* + * Miscellanious ranges (all are unsigned). + */ +#define HAMMER2_TID_MIN 1ULL +#define HAMMER2_TID_MAX 0xFFFFFFFFFFFFFFFFULL +#define HAMMER2_KEY_MIN 0ULL +#define HAMMER2_KEY_MAX 0xFFFFFFFFFFFFFFFFULL +#define HAMMER2_OFFSET_MIN 0ULL +#define HAMMER2_OFFSET_MAX 0xFFFFFFFFFFFFFFFFULL + +/* + * HAMMER2 data offset special cases and masking. + * + * All HAMMER2 data offsets have to be broken down into a 64K buffer base + * offset (HAMMER2_OFF_MASK_HI) and a 64K buffer index (HAMMER2_OFF_MASK_LO). + * + * Indexes into physical buffers are always 64-byte aligned. The low 6 bits + * of the data offset field specifies how large the data chunk being pointed + * to as a power of 2. The theoretical minimum radix is thus 6 (The space + * needed in the low bits of the data offset field). However, the practical + * minimum allocation chunk size is 1KB (a radix of 10), so HAMMER2 sets + * HAMMER2_RADIX_MIN to 10. The maximum radix is currently 16 (64KB), but + * we fully intend to support larger extents in the future. + * + * WARNING! A radix of 0 (such as when data_off is all 0's) is a special + * 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 + */ +#define HAMMER2_DIRHASH_VISIBLE 0x8000000000000000ULL +#define HAMMER2_DIRHASH_USERMSK 0x7FFFFFFFFFFFFFFFULL +#define HAMMER2_DIRHASH_LOMASK 0x0000000000007FFFULL +#define HAMMER2_DIRHASH_HIMASK 0xFFFFFFFFFFFF0000ULL +#define HAMMER2_DIRHASH_FORCED 0x0000000000008000ULL /* bit forced on */ + +#define HAMMER2_SROOT_KEY 0x0000000000000000ULL /* volume to sroot */ +#define HAMMER2_BOOT_KEY 0xd9b36ce135528000ULL /* sroot to BOOT PFS */ + +/************************************************************************ + * DMSG SUPPORT * + ************************************************************************ + * LNK_VOLCONF + * + * All HAMMER2 directories directly under the super-root on your local + * media can be mounted separately, even if they share the same physical + * device. + * + * When you do a HAMMER2 mount you are effectively tying into a HAMMER2 + * cluster via local media. The local media does not have to participate + * in the cluster, other than to provide the hammer2_volconf[] array and + * root inode for the mount. + * + * This is important: The mount device path you specify serves to bootstrap + * your entry into the cluster, but your mount will make active connections + * to ALL copy elements in the hammer2_volconf[] array which match the + * PFSID of the directory in the super-root that you specified. The local + * media path does not have to be mentioned in this array but becomes part + * of the cluster based on its type and access rights. ALL ELEMENTS ARE + * TREATED ACCORDING TO TYPE NO MATTER WHICH ONE YOU MOUNT FROM. + * + * The actual cluster may be far larger than the elements you list in the + * hammer2_volconf[] array. You list only the elements you wish to + * directly connect to and you are able to access the rest of the cluster + * indirectly through those connections. + * + * WARNING! This structure must be exactly 128 bytes long for its config + * array to fit in the volume header. + */ +struct hammer2_volconf { + uint8_t copyid; /* 00 copyid 0-255 (must match slot) */ + uint8_t inprog; /* 01 operation in progress, or 0 */ + uint8_t chain_to; /* 02 operation chaining to, or 0 */ + uint8_t chain_from; /* 03 operation chaining from, or 0 */ + uint16_t flags; /* 04-05 flags field */ + uint8_t error; /* 06 last operational error */ + uint8_t priority; /* 07 priority and round-robin flag */ + uint8_t remote_pfs_type;/* 08 probed direct remote PFS type */ + uint8_t reserved08[23]; /* 09-1F */ + uuid_t pfs_clid; /* 20-2F copy target must match this uuid */ + uint8_t label[16]; /* 30-3F import/export label */ + uint8_t path[64]; /* 40-7F target specification string or key */ +} __packed; + +typedef struct hammer2_volconf hammer2_volconf_t; + +#define DMSG_VOLF_ENABLED 0x0001 +#define DMSG_VOLF_INPROG 0x0002 +#define DMSG_VOLF_CONN_RR 0x80 /* round-robin at same priority */ +#define DMSG_VOLF_CONN_EF 0x40 /* media errors flagged */ +#define DMSG_VOLF_CONN_PRI 0x0F /* select priority 0-15 (15=best) */ + +struct dmsg_lnk_hammer2_volconf { + dmsg_hdr_t head; + hammer2_volconf_t copy; /* copy spec */ + int32_t index; + int32_t unused01; + uuid_t mediaid; + int64_t reserved02[32]; +} __packed; + +typedef struct dmsg_lnk_hammer2_volconf dmsg_lnk_hammer2_volconf_t; + +#define DMSG_LNK_HAMMER2_VOLCONF DMSG_LNK(DMSG_LNK_CMD_HAMMER2_VOLCONF, \ *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***