Home | History | Annotate | Line # | Download | only in fstyp
      1  1.9  christos /*        $NetBSD: hammer2.c,v 1.9 2021/12/02 14:26:42 christos Exp $      */
      2  1.1   tkusumi 
      3  1.1   tkusumi /*-
      4  1.1   tkusumi  * Copyright (c) 2017-2019 The DragonFly Project
      5  1.1   tkusumi  * Copyright (c) 2017-2019 Tomohiro Kusumi <tkusumi (at) netbsd.org>
      6  1.1   tkusumi  * All rights reserved.
      7  1.1   tkusumi  *
      8  1.1   tkusumi  * Redistribution and use in source and binary forms, with or without
      9  1.1   tkusumi  * modification, are permitted provided that the following conditions
     10  1.1   tkusumi  * are met:
     11  1.1   tkusumi  * 1. Redistributions of source code must retain the above copyright
     12  1.1   tkusumi  *    notice, this list of conditions and the following disclaimer.
     13  1.1   tkusumi  * 2. Redistributions in binary form must reproduce the above copyright
     14  1.1   tkusumi  *    notice, this list of conditions and the following disclaimer in the
     15  1.1   tkusumi  *    documentation and/or other materials provided with the distribution.
     16  1.1   tkusumi  *
     17  1.1   tkusumi  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
     18  1.1   tkusumi  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     19  1.1   tkusumi  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     20  1.1   tkusumi  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
     21  1.1   tkusumi  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     22  1.1   tkusumi  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     23  1.1   tkusumi  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     24  1.1   tkusumi  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     25  1.1   tkusumi  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     26  1.1   tkusumi  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     27  1.1   tkusumi  * SUCH DAMAGE.
     28  1.1   tkusumi  */
     29  1.1   tkusumi #include <sys/cdefs.h>
     30  1.9  christos __KERNEL_RCSID(0, "$NetBSD: hammer2.c,v 1.9 2021/12/02 14:26:42 christos Exp $");
     31  1.9  christos 
     32  1.9  christos #include <sys/param.h>
     33  1.9  christos #include <sys/ioctl.h>
     34  1.9  christos #include <sys/disk.h>
     35  1.1   tkusumi 
     36  1.1   tkusumi #include <stdio.h>
     37  1.1   tkusumi #include <stdlib.h>
     38  1.1   tkusumi #include <stdbool.h>
     39  1.1   tkusumi #include <string.h>
     40  1.1   tkusumi #include <err.h>
     41  1.1   tkusumi #include <assert.h>
     42  1.7   tkusumi #include <uuid.h>
     43  1.1   tkusumi 
     44  1.1   tkusumi #include "fstyp.h"
     45  1.1   tkusumi #include "hammer2_disk.h"
     46  1.1   tkusumi 
     47  1.7   tkusumi static ssize_t
     48  1.7   tkusumi get_file_size(FILE *fp)
     49  1.1   tkusumi {
     50  1.7   tkusumi 	ssize_t siz;
     51  1.9  christos 	struct dkwedge_info dkw;
     52  1.9  christos 
     53  1.9  christos 	if (ioctl(fileno(fp), DIOCGWEDGEINFO, &dkw) != -1) {
     54  1.9  christos 		return (ssize_t)dkw.dkw_size * DEV_BSIZE;
     55  1.9  christos 	}
     56  1.7   tkusumi 
     57  1.7   tkusumi 	if (fseek(fp, 0, SEEK_END) == -1) {
     58  1.7   tkusumi 		warnx("hammer2: failed to seek media end");
     59  1.9  christos 		return -1;
     60  1.7   tkusumi 	}
     61  1.7   tkusumi 
     62  1.7   tkusumi 	siz = ftell(fp);
     63  1.7   tkusumi 	if (siz == -1) {
     64  1.7   tkusumi 		warnx("hammer2: failed to tell media end");
     65  1.9  christos 		return -1;
     66  1.7   tkusumi 	}
     67  1.7   tkusumi 
     68  1.9  christos 	return siz;
     69  1.7   tkusumi }
     70  1.7   tkusumi 
     71  1.7   tkusumi static hammer2_volume_data_t *
     72  1.7   tkusumi read_voldata(FILE *fp, int i)
     73  1.7   tkusumi {
     74  1.7   tkusumi 	if (i < 0 || i >= HAMMER2_NUM_VOLHDRS)
     75  1.9  christos 		return NULL;
     76  1.1   tkusumi 
     77  1.7   tkusumi 	if ((hammer2_off_t)i * (hammer2_off_t)HAMMER2_ZONE_BYTES64 >= (hammer2_off_t)get_file_size(fp))
     78  1.9  christos 		return NULL;
     79  1.1   tkusumi 
     80  1.9  christos 	return read_buf(fp, (off_t)i * (off_t)HAMMER2_ZONE_BYTES64,
     81  1.9  christos 	    sizeof(hammer2_volume_data_t));
     82  1.1   tkusumi }
     83  1.1   tkusumi 
     84  1.1   tkusumi static int
     85  1.7   tkusumi test_voldata(FILE *fp)
     86  1.1   tkusumi {
     87  1.7   tkusumi 	hammer2_volume_data_t *voldata;
     88  1.7   tkusumi 	int i;
     89  1.7   tkusumi 	static int count = 0;
     90  1.7   tkusumi 	static uuid_t fsid, fstype;
     91  1.7   tkusumi 
     92  1.7   tkusumi 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
     93  1.7   tkusumi 		if ((hammer2_off_t)i * (hammer2_off_t)HAMMER2_ZONE_BYTES64 >= (hammer2_off_t)get_file_size(fp))
     94  1.7   tkusumi 			break;
     95  1.7   tkusumi 		voldata = read_voldata(fp, i);
     96  1.7   tkusumi 		if (voldata == NULL) {
     97  1.7   tkusumi 			warnx("hammer2: failed to read volume data");
     98  1.9  christos 			return 1;
     99  1.7   tkusumi 		}
    100  1.7   tkusumi 		if (voldata->magic != HAMMER2_VOLUME_ID_HBO &&
    101  1.7   tkusumi 		    voldata->magic != HAMMER2_VOLUME_ID_ABO) {
    102  1.7   tkusumi 			free(voldata);
    103  1.9  christos 			return 1;
    104  1.7   tkusumi 		}
    105  1.7   tkusumi 		if (voldata->volu_id > HAMMER2_MAX_VOLUMES - 1) {
    106  1.7   tkusumi 			free(voldata);
    107  1.9  christos 			return 1;
    108  1.7   tkusumi 		}
    109  1.7   tkusumi 		if (voldata->nvolumes > HAMMER2_MAX_VOLUMES) {
    110  1.7   tkusumi 			free(voldata);
    111  1.9  christos 			return 1;
    112  1.7   tkusumi 		}
    113  1.7   tkusumi 
    114  1.7   tkusumi 		if (count == 0) {
    115  1.7   tkusumi 			count = voldata->nvolumes;
    116  1.7   tkusumi 			memcpy(&fsid, &voldata->fsid, sizeof(fsid));
    117  1.7   tkusumi 			memcpy(&fstype, &voldata->fstype, sizeof(fstype));
    118  1.7   tkusumi 		} else {
    119  1.7   tkusumi 			if (voldata->nvolumes != count) {
    120  1.7   tkusumi 				free(voldata);
    121  1.9  christos 				return 1;
    122  1.7   tkusumi 			}
    123  1.7   tkusumi 			if (!uuid_equal(&fsid, &voldata->fsid, NULL)) {
    124  1.7   tkusumi 				free(voldata);
    125  1.9  christos 				return 1;
    126  1.7   tkusumi 			}
    127  1.7   tkusumi 			if (!uuid_equal(&fstype, &voldata->fstype, NULL)) {
    128  1.7   tkusumi 				free(voldata);
    129  1.9  christos 				return 1;
    130  1.7   tkusumi 			}
    131  1.7   tkusumi 		}
    132  1.7   tkusumi 		free(voldata);
    133  1.7   tkusumi 	}
    134  1.1   tkusumi 
    135  1.9  christos 	return 0;
    136  1.1   tkusumi }
    137  1.1   tkusumi 
    138  1.1   tkusumi static hammer2_media_data_t*
    139  1.1   tkusumi read_media(FILE *fp, const hammer2_blockref_t *bref, size_t *media_bytes)
    140  1.1   tkusumi {
    141  1.1   tkusumi 	hammer2_media_data_t *media;
    142  1.1   tkusumi 	hammer2_off_t io_off, io_base;
    143  1.7   tkusumi 	size_t bytes, io_bytes, boff, fbytes;
    144  1.1   tkusumi 
    145  1.1   tkusumi 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
    146  1.1   tkusumi 	if (bytes)
    147  1.1   tkusumi 		bytes = (size_t)1 << bytes;
    148  1.1   tkusumi 	*media_bytes = bytes;
    149  1.1   tkusumi 
    150  1.1   tkusumi 	if (!bytes) {
    151  1.7   tkusumi 		warnx("hammer2: blockref has no data");
    152  1.9  christos 		return NULL;
    153  1.1   tkusumi 	}
    154  1.1   tkusumi 
    155  1.1   tkusumi 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
    156  1.6   tkusumi 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
    157  1.8    martin 	boff = (size_t)((hammer2_off_t)io_off - io_base);
    158  1.1   tkusumi 
    159  1.6   tkusumi 	io_bytes = HAMMER2_LBUFSIZE;
    160  1.1   tkusumi 	while (io_bytes + boff < bytes)
    161  1.1   tkusumi 		io_bytes <<= 1;
    162  1.1   tkusumi 
    163  1.1   tkusumi 	if (io_bytes > sizeof(hammer2_media_data_t)) {
    164  1.7   tkusumi 		warnx("hammer2: invalid I/O bytes");
    165  1.9  christos 		return NULL;
    166  1.1   tkusumi 	}
    167  1.1   tkusumi 
    168  1.7   tkusumi 	/*
    169  1.7   tkusumi 	 * XXX fp is currently always root volume, so read fails if io_base is
    170  1.7   tkusumi 	 * beyond root volume limit. Fail with a message before read_buf() then.
    171  1.7   tkusumi 	 */
    172  1.7   tkusumi 	fbytes = (size_t)get_file_size(fp);
    173  1.7   tkusumi 	if ((ssize_t)fbytes == -1) {
    174  1.7   tkusumi 		warnx("hammer2: failed to get media size");
    175  1.9  christos 		return NULL;
    176  1.7   tkusumi 	}
    177  1.7   tkusumi 	if (io_base >= fbytes) {
    178  1.7   tkusumi 		warnx("hammer2: XXX read beyond HAMMER2 root volume limit unsupported");
    179  1.9  christos 		return NULL;
    180  1.7   tkusumi 	}
    181  1.7   tkusumi 
    182  1.7   tkusumi 	if (fseeko(fp, (off_t)io_base, SEEK_SET) == -1) {
    183  1.7   tkusumi 		warnx("hammer2: failed to seek media");
    184  1.9  christos 		return NULL;
    185  1.1   tkusumi 	}
    186  1.1   tkusumi 	media = read_buf(fp, (off_t)io_base, io_bytes);
    187  1.1   tkusumi 	if (media == NULL) {
    188  1.7   tkusumi 		warnx("hammer2: failed to read media");
    189  1.9  christos 		return NULL;
    190  1.1   tkusumi 	}
    191  1.1   tkusumi 	if (boff)
    192  1.1   tkusumi 		memcpy(media, (char *)media + boff, bytes);
    193  1.1   tkusumi 
    194  1.9  christos 	return media;
    195  1.1   tkusumi }
    196  1.1   tkusumi 
    197  1.1   tkusumi static int
    198  1.1   tkusumi find_pfs(FILE *fp, const hammer2_blockref_t *bref, const char *pfs, bool *res)
    199  1.1   tkusumi {
    200  1.1   tkusumi 	hammer2_media_data_t *media;
    201  1.1   tkusumi 	hammer2_inode_data_t ipdata;
    202  1.1   tkusumi 	hammer2_blockref_t *bscan;
    203  1.1   tkusumi 	size_t bytes;
    204  1.1   tkusumi 	int i, bcount;
    205  1.1   tkusumi 
    206  1.1   tkusumi 	media = read_media(fp, bref, &bytes);
    207  1.1   tkusumi 	if (media == NULL)
    208  1.9  christos 		return -1;
    209  1.1   tkusumi 
    210  1.1   tkusumi 	switch (bref->type) {
    211  1.1   tkusumi 	case HAMMER2_BREF_TYPE_INODE:
    212  1.1   tkusumi 		ipdata = media->ipdata;
    213  1.5   tkusumi 		if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
    214  1.1   tkusumi 			bscan = &ipdata.u.blockset.blockref[0];
    215  1.1   tkusumi 			bcount = HAMMER2_SET_COUNT;
    216  1.1   tkusumi 		} else {
    217  1.1   tkusumi 			bscan = NULL;
    218  1.1   tkusumi 			bcount = 0;
    219  1.1   tkusumi 			if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
    220  1.1   tkusumi 				if (memchr(ipdata.filename, 0,
    221  1.1   tkusumi 				    sizeof(ipdata.filename))) {
    222  1.1   tkusumi 					if (!strcmp(
    223  1.1   tkusumi 					    (const char*)ipdata.filename, pfs))
    224  1.1   tkusumi 						*res = true;
    225  1.1   tkusumi 				} else {
    226  1.1   tkusumi 					if (strlen(pfs) > 0 &&
    227  1.1   tkusumi 					    !memcmp(ipdata.filename, pfs,
    228  1.1   tkusumi 					    strlen(pfs)))
    229  1.1   tkusumi 						*res = true;
    230  1.1   tkusumi 				}
    231  1.7   tkusumi 			} else {
    232  1.7   tkusumi 				free(media);
    233  1.9  christos 				return -1;
    234  1.7   tkusumi 			}
    235  1.1   tkusumi 		}
    236  1.1   tkusumi 		break;
    237  1.1   tkusumi 	case HAMMER2_BREF_TYPE_INDIRECT:
    238  1.1   tkusumi 		bscan = &media->npdata[0];
    239  1.1   tkusumi 		bcount = (int)(bytes / sizeof(hammer2_blockref_t));
    240  1.1   tkusumi 		break;
    241  1.1   tkusumi 	default:
    242  1.1   tkusumi 		bscan = NULL;
    243  1.1   tkusumi 		bcount = 0;
    244  1.1   tkusumi 		break;
    245  1.1   tkusumi 	}
    246  1.1   tkusumi 
    247  1.1   tkusumi 	for (i = 0; i < bcount; ++i) {
    248  1.1   tkusumi 		if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) {
    249  1.1   tkusumi 			if (find_pfs(fp, &bscan[i], pfs, res) == -1) {
    250  1.1   tkusumi 				free(media);
    251  1.9  christos 				return -1;
    252  1.1   tkusumi 			}
    253  1.1   tkusumi 		}
    254  1.1   tkusumi 	}
    255  1.1   tkusumi 	free(media);
    256  1.1   tkusumi 
    257  1.9  christos 	return 0;
    258  1.1   tkusumi }
    259  1.1   tkusumi 
    260  1.1   tkusumi static char*
    261  1.1   tkusumi extract_device_name(const char *devpath)
    262  1.1   tkusumi {
    263  1.1   tkusumi 	char *p, *head;
    264  1.1   tkusumi 
    265  1.1   tkusumi 	if (!devpath)
    266  1.9  christos 		return NULL;
    267  1.1   tkusumi 
    268  1.1   tkusumi 	p = strdup(devpath);
    269  1.1   tkusumi 	head = p;
    270  1.1   tkusumi 
    271  1.1   tkusumi 	p = strchr(p, '@');
    272  1.1   tkusumi 	if (p)
    273  1.1   tkusumi 		*p = 0;
    274  1.1   tkusumi 
    275  1.1   tkusumi 	p = strrchr(head, '/');
    276  1.1   tkusumi 	if (p) {
    277  1.1   tkusumi 		p++;
    278  1.1   tkusumi 		if (*p == 0) {
    279  1.1   tkusumi 			free(head);
    280  1.9  christos 			return NULL;
    281  1.1   tkusumi 		}
    282  1.1   tkusumi 		p = strdup(p);
    283  1.1   tkusumi 		free(head);
    284  1.9  christos 		return p;
    285  1.1   tkusumi 	}
    286  1.1   tkusumi 
    287  1.9  christos 	return head;
    288  1.1   tkusumi }
    289  1.1   tkusumi 
    290  1.1   tkusumi static int
    291  1.7   tkusumi read_label(FILE *fp, char *label, size_t size, const char *devpath)
    292  1.1   tkusumi {
    293  1.1   tkusumi 	hammer2_blockref_t broot, best, *bref;
    294  1.1   tkusumi 	hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media;
    295  1.1   tkusumi 	size_t bytes;
    296  1.1   tkusumi 	bool res = false;
    297  1.3   tkusumi 	int i, best_i, error = 1;
    298  1.1   tkusumi 	const char *pfs;
    299  1.1   tkusumi 	char *devname;
    300  1.1   tkusumi 
    301  1.1   tkusumi 	best_i = -1;
    302  1.7   tkusumi 	memset(vols, 0, sizeof(vols));
    303  1.1   tkusumi 	memset(&best, 0, sizeof(best));
    304  1.1   tkusumi 
    305  1.1   tkusumi 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
    306  1.7   tkusumi 		if ((hammer2_off_t)i * (hammer2_off_t)HAMMER2_ZONE_BYTES64 >= (hammer2_off_t)get_file_size(fp))
    307  1.7   tkusumi 			break;
    308  1.1   tkusumi 		memset(&broot, 0, sizeof(broot));
    309  1.1   tkusumi 		broot.type = HAMMER2_BREF_TYPE_VOLUME;
    310  1.7   tkusumi 		broot.data_off = ((hammer2_off_t)i * (hammer2_off_t)HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
    311  1.7   tkusumi 		vols[i] = (void*)read_voldata(fp, i);
    312  1.7   tkusumi 		if (vols[i] == NULL) {
    313  1.7   tkusumi 			warnx("hammer2: failed to read volume data");
    314  1.7   tkusumi 			goto fail;
    315  1.7   tkusumi 		}
    316  1.1   tkusumi 		broot.mirror_tid = vols[i]->voldata.mirror_tid;
    317  1.1   tkusumi 		if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
    318  1.1   tkusumi 			best_i = i;
    319  1.1   tkusumi 			best = broot;
    320  1.1   tkusumi 		}
    321  1.1   tkusumi 	}
    322  1.1   tkusumi 
    323  1.1   tkusumi 	bref = &vols[best_i]->voldata.sroot_blockset.blockref[0];
    324  1.1   tkusumi 	if (bref->type != HAMMER2_BREF_TYPE_INODE) {
    325  1.7   tkusumi 		/* Don't print error as devpath could be non-root volume. */
    326  1.3   tkusumi 		goto fail;
    327  1.1   tkusumi 	}
    328  1.1   tkusumi 
    329  1.1   tkusumi 	media = read_media(fp, bref, &bytes);
    330  1.1   tkusumi 	if (media == NULL) {
    331  1.3   tkusumi 		goto fail;
    332  1.1   tkusumi 	}
    333  1.1   tkusumi 
    334  1.1   tkusumi 	/*
    335  1.1   tkusumi 	 * fstyp_function in DragonFly takes an additional devpath argument
    336  1.1   tkusumi 	 * which doesn't exist in FreeBSD and NetBSD.
    337  1.1   tkusumi 	 */
    338  1.1   tkusumi #ifdef HAS_DEVPATH
    339  1.1   tkusumi 	pfs = strchr(devpath, '@');
    340  1.1   tkusumi 	if (!pfs) {
    341  1.1   tkusumi 		assert(strlen(devpath));
    342  1.1   tkusumi 		switch (devpath[strlen(devpath) - 1]) {
    343  1.1   tkusumi 		case 'a':
    344  1.1   tkusumi 			pfs = "BOOT";
    345  1.1   tkusumi 			break;
    346  1.1   tkusumi 		case 'd':
    347  1.1   tkusumi 			pfs = "ROOT";
    348  1.1   tkusumi 			break;
    349  1.1   tkusumi 		default:
    350  1.1   tkusumi 			pfs = "DATA";
    351  1.1   tkusumi 			break;
    352  1.1   tkusumi 		}
    353  1.1   tkusumi 	} else
    354  1.1   tkusumi 		pfs++;
    355  1.1   tkusumi 
    356  1.1   tkusumi 	if (strlen(pfs) > HAMMER2_INODE_MAXNAME) {
    357  1.3   tkusumi 		goto fail;
    358  1.1   tkusumi 	}
    359  1.1   tkusumi 	devname = extract_device_name(devpath);
    360  1.1   tkusumi #else
    361  1.1   tkusumi 	pfs = "";
    362  1.1   tkusumi 	devname = extract_device_name(NULL);
    363  1.1   tkusumi 	assert(!devname);
    364  1.1   tkusumi #endif
    365  1.1   tkusumi 
    366  1.1   tkusumi 	/* Add device name to help support multiple autofs -media mounts. */
    367  1.1   tkusumi 	if (find_pfs(fp, bref, pfs, &res) == 0 && res) {
    368  1.1   tkusumi 		if (devname)
    369  1.1   tkusumi 			snprintf(label, size, "%s_%s", pfs, devname);
    370  1.1   tkusumi 		else
    371  1.1   tkusumi 			strlcpy(label, pfs, size);
    372  1.1   tkusumi 	} else {
    373  1.1   tkusumi 		memset(label, 0, size);
    374  1.1   tkusumi 		memcpy(label, media->ipdata.filename,
    375  1.1   tkusumi 		    sizeof(media->ipdata.filename));
    376  1.1   tkusumi 		if (devname) {
    377  1.1   tkusumi 			strlcat(label, "_", size);
    378  1.1   tkusumi 			strlcat(label, devname, size);
    379  1.1   tkusumi 		}
    380  1.1   tkusumi 	}
    381  1.1   tkusumi 	if (devname)
    382  1.1   tkusumi 		free(devname);
    383  1.1   tkusumi 	free(media);
    384  1.3   tkusumi 	error = 0;
    385  1.3   tkusumi fail:
    386  1.1   tkusumi 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++)
    387  1.1   tkusumi 		free(vols[i]);
    388  1.1   tkusumi 
    389  1.9  christos 	return error;
    390  1.1   tkusumi }
    391  1.1   tkusumi 
    392  1.1   tkusumi int
    393  1.1   tkusumi fstyp_hammer2(FILE *fp, char *label, size_t size)
    394  1.1   tkusumi {
    395  1.7   tkusumi 	hammer2_volume_data_t *voldata = read_voldata(fp, 0);
    396  1.1   tkusumi 	int error = 1;
    397  1.1   tkusumi 
    398  1.9  christos 	if (voldata == NULL)
    399  1.9  christos 		goto fail;
    400  1.7   tkusumi 	if (voldata->volu_id != HAMMER2_ROOT_VOLUME)
    401  1.7   tkusumi 		goto fail;
    402  1.7   tkusumi 	if (voldata->nvolumes != 0)
    403  1.7   tkusumi 		goto fail;
    404  1.7   tkusumi 	if (test_voldata(fp))
    405  1.7   tkusumi 		goto fail;
    406  1.7   tkusumi 
    407  1.7   tkusumi 	error = read_label(fp, label, size, NULL);
    408  1.7   tkusumi fail:
    409  1.7   tkusumi 	free(voldata);
    410  1.9  christos 	return error;
    411  1.7   tkusumi }
    412  1.7   tkusumi 
    413  1.7   tkusumi static int
    414  1.7   tkusumi __fsvtyp_hammer2(const char *blkdevs, char *label, size_t size, int partial)
    415  1.7   tkusumi {
    416  1.7   tkusumi 	hammer2_volume_data_t *voldata = NULL;
    417  1.7   tkusumi 	FILE *fp = NULL;
    418  1.7   tkusumi 	char *dup = NULL, *target_label = NULL, *p, *volpath, *rootvolpath;
    419  1.7   tkusumi 	char x[HAMMER2_MAX_VOLUMES];
    420  1.7   tkusumi 	int i, volid, error = 1;
    421  1.7   tkusumi 
    422  1.7   tkusumi 	if (!blkdevs)
    423  1.7   tkusumi 		goto fail;
    424  1.7   tkusumi 
    425  1.7   tkusumi 	memset(x, 0, sizeof(x));
    426  1.7   tkusumi 	p = dup = strdup(blkdevs);
    427  1.7   tkusumi 	if ((p = strchr(p, '@')) != NULL) {
    428  1.7   tkusumi 		*p++ = '\0';
    429  1.7   tkusumi 		target_label = p;
    430  1.7   tkusumi 	}
    431  1.7   tkusumi 	p = dup;
    432  1.7   tkusumi 
    433  1.7   tkusumi 	volpath = NULL;
    434  1.7   tkusumi 	rootvolpath = NULL;
    435  1.7   tkusumi 	volid = -1;
    436  1.7   tkusumi 	while (p) {
    437  1.7   tkusumi 		volpath = p;
    438  1.7   tkusumi 		if ((p = strchr(p, ':')) != NULL)
    439  1.7   tkusumi 			*p++ = '\0';
    440  1.7   tkusumi 		if ((fp = fopen(volpath, "r")) == NULL) {
    441  1.7   tkusumi 			warnx("hammer2: failed to open %s", volpath);
    442  1.7   tkusumi 			goto fail;
    443  1.7   tkusumi 		}
    444  1.7   tkusumi 		if (test_voldata(fp))
    445  1.7   tkusumi 			break;
    446  1.7   tkusumi 		voldata = read_voldata(fp, 0);
    447  1.7   tkusumi 		fclose(fp);
    448  1.7   tkusumi 		if (voldata == NULL) {
    449  1.7   tkusumi 			warnx("hammer2: failed to read volume data");
    450  1.7   tkusumi 			goto fail;
    451  1.7   tkusumi 		}
    452  1.7   tkusumi 		volid = voldata->volu_id;
    453  1.7   tkusumi 		free(voldata);
    454  1.7   tkusumi 		voldata = NULL;
    455  1.7   tkusumi 		if (volid < 0 || volid >= HAMMER2_MAX_VOLUMES)
    456  1.7   tkusumi 			goto fail;
    457  1.7   tkusumi 		x[volid]++;
    458  1.7   tkusumi 		if (volid == HAMMER2_ROOT_VOLUME)
    459  1.7   tkusumi 			rootvolpath = volpath;
    460  1.7   tkusumi 	}
    461  1.7   tkusumi 
    462  1.7   tkusumi 	/* If no rootvolpath, proceed only if partial mode with volpath. */
    463  1.7   tkusumi 	if (rootvolpath)
    464  1.7   tkusumi 		volpath = rootvolpath;
    465  1.7   tkusumi 	else if (!partial || !volpath)
    466  1.7   tkusumi 		goto fail;
    467  1.7   tkusumi 	if ((fp = fopen(volpath, "r")) == NULL) {
    468  1.7   tkusumi 		warnx("hammer2: failed to open %s", volpath);
    469  1.7   tkusumi 		goto fail;
    470  1.7   tkusumi 	}
    471  1.7   tkusumi 	voldata = read_voldata(fp, 0);
    472  1.7   tkusumi 	if (voldata == NULL) {
    473  1.7   tkusumi 		warnx("hammer2: failed to read volume data");
    474  1.7   tkusumi 		goto fail;
    475  1.7   tkusumi 	}
    476  1.7   tkusumi 
    477  1.7   tkusumi 	if (volid == -1)
    478  1.3   tkusumi 		goto fail;
    479  1.7   tkusumi 	if (partial)
    480  1.7   tkusumi 		goto success;
    481  1.1   tkusumi 
    482  1.7   tkusumi 	for (i = 0; i < HAMMER2_MAX_VOLUMES; i++)
    483  1.7   tkusumi 		if (x[i] > 1)
    484  1.7   tkusumi 			goto fail;
    485  1.7   tkusumi 	for (i = 0; i < HAMMER2_MAX_VOLUMES; i++)
    486  1.7   tkusumi 		if (x[i] == 0)
    487  1.7   tkusumi 			break;
    488  1.7   tkusumi 	if (voldata->nvolumes != i)
    489  1.7   tkusumi 		goto fail;
    490  1.7   tkusumi 	for (; i < HAMMER2_MAX_VOLUMES; i++)
    491  1.7   tkusumi 		if (x[i] != 0)
    492  1.7   tkusumi 			goto fail;
    493  1.7   tkusumi success:
    494  1.7   tkusumi 	/* Reconstruct @label format path using only root volume. */
    495  1.7   tkusumi 	if (target_label) {
    496  1.7   tkusumi 		size_t siz = strlen(volpath) + strlen(target_label) + 2;
    497  1.7   tkusumi 		p = calloc(1, siz);
    498  1.7   tkusumi 		snprintf(p, siz, "%s@%s", volpath, target_label);
    499  1.7   tkusumi 		volpath = p;
    500  1.7   tkusumi 	}
    501  1.7   tkusumi 	error = read_label(fp, label, size, volpath);
    502  1.7   tkusumi 	if (target_label)
    503  1.7   tkusumi 		free(p);
    504  1.7   tkusumi 	/* If in partial mode, read label but ignore error. */
    505  1.7   tkusumi 	if (partial)
    506  1.7   tkusumi 		error = 0;
    507  1.3   tkusumi fail:
    508  1.7   tkusumi 	if (fp)
    509  1.7   tkusumi 		fclose(fp);
    510  1.1   tkusumi 	free(voldata);
    511  1.7   tkusumi 	free(dup);
    512  1.9  christos 	return error;
    513  1.1   tkusumi }
    514  1.7   tkusumi 
    515  1.7   tkusumi int
    516  1.7   tkusumi fsvtyp_hammer2(const char *blkdevs, char *label, size_t size)
    517  1.7   tkusumi {
    518  1.9  christos 	return __fsvtyp_hammer2(blkdevs, label, size, 0);
    519  1.7   tkusumi }
    520  1.7   tkusumi 
    521  1.7   tkusumi int
    522  1.7   tkusumi fsvtyp_hammer2_partial(const char *blkdevs, char *label, size_t size)
    523  1.7   tkusumi {
    524  1.9  christos 	return __fsvtyp_hammer2(blkdevs, label, size, 1);
    525  1.7   tkusumi }
    526