Merge branch 'vendor/DHCPCD'
[dragonfly.git] / sys / vfs / hammer2 / hammer2_ondisk.c
1 /*
2  * Copyright (c) 2020 Tomohiro Kusumi <tkusumi@netbsd.org>
3  * Copyright (c) 2020 The DragonFly Project
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to The DragonFly Project
7  * by Matthew Dillon <dillon@dragonflybsd.org>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  * 3. Neither the name of The DragonFly Project nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific, prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/queue.h>
40 #include <sys/nlookup.h>
41 #include <sys/vnode.h>
42 #include <sys/mount.h>
43 #include <sys/buf.h>
44 #include <sys/uuid.h>
45 #include <sys/objcache.h>
46 #include <sys/lock.h>
47
48 #include "hammer2.h"
49
50 #define hprintf(X, ...) kprintf("hammer2_ondisk: " X, ## __VA_ARGS__)
51
52 static int
53 hammer2_lookup_device(const char *path, int rootmount, struct vnode **devvp)
54 {
55         struct vnode *vp = NULL;
56         struct nlookupdata nd;
57         int error = 0;
58
59         KKASSERT(path);
60         KKASSERT(*path != '\0');
61
62         if (rootmount) {
63                 error = bdevvp(kgetdiskbyname(path), &vp);
64                 if (error)
65                         hprintf("cannot find %s %d\n", path, error);
66         } else {
67                 error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
68                 if (error == 0)
69                         error = nlookup(&nd);
70                 if (error == 0)
71                         error = cache_vref(&nd.nl_nch, nd.nl_cred, &vp);
72                 if (error)
73                         hprintf("failed to nlookup %s %d\n", path, error);
74                 nlookup_done(&nd);
75         }
76
77         if (error == 0) {
78                 KKASSERT(vp);
79                 if (!vn_isdisk(vp, &error)) {
80                         KKASSERT(error);
81                         hprintf("%s not a block device %d\n", path, error);
82                 }
83         }
84
85         if (error && vp) {
86                 vrele(vp);
87                 vp = NULL;
88         }
89
90         *devvp = vp;
91         return error;
92 }
93
94 int
95 hammer2_open_devvp(const hammer2_devvp_list_t *devvpl, int ronly)
96 {
97         hammer2_devvp_t *e;
98         struct vnode *devvp;
99         const char *path;
100         int count, error;
101
102         TAILQ_FOREACH(e, devvpl, entry) {
103                 devvp = e->devvp;
104                 path = e->path;
105                 KKASSERT(devvp);
106                 count = vcount(devvp);
107                 if (count > 0) {
108                         hprintf("%s already has %d references\n", path, count);
109                         return EBUSY;
110                 }
111                 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
112                 error = vinvalbuf(devvp, V_SAVE, 0, 0);
113                 if (error == 0) {
114                         KKASSERT(!e->open);
115                         error = VOP_OPEN(devvp, (ronly ? FREAD : FREAD|FWRITE),
116                                          FSCRED, NULL);
117                         if (error == 0)
118                                 e->open = 1;
119                         else
120                                 hprintf("failed to open %s %d\n", path, error);
121                 }
122                 vn_unlock(devvp);
123                 if (error)
124                         return error;
125                 KKASSERT(e->open);
126         }
127
128         return 0;
129 }
130
131 int
132 hammer2_close_devvp(const hammer2_devvp_list_t *devvpl, int ronly)
133 {
134         hammer2_devvp_t *e;
135         struct vnode *devvp;
136
137         TAILQ_FOREACH(e, devvpl, entry) {
138                 devvp = e->devvp;
139                 KKASSERT(devvp);
140                 if (e->open) {
141                         vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
142                         vinvalbuf(devvp, (ronly ? 0 : V_SAVE), 0, 0);
143                         VOP_CLOSE(devvp, (ronly ? FREAD : FREAD|FWRITE), NULL);
144                         vn_unlock(devvp);
145                         e->open = 0;
146                 }
147         }
148
149         return 0;
150 }
151
152 int
153 hammer2_init_devvp(const char *blkdevs, int rootmount,
154                    hammer2_devvp_list_t *devvpl)
155 {
156         hammer2_devvp_t *e;
157         struct vnode *devvp;
158         const char *p;
159         char *path;
160         int i, error = 0;
161
162         KKASSERT(TAILQ_EMPTY(devvpl));
163         KKASSERT(blkdevs); /* could be empty string */
164         p = blkdevs;
165
166         path = objcache_get(namei_oc, M_WAITOK);
167         while (1) {
168                 strcpy(path, "");
169                 if (*p != '/') {
170                         strcpy(path, "/dev/"); /* relative path */
171                 }
172                 /* scan beyond "/dev/" */
173                 for (i = strlen(path); i < MAXPATHLEN-1; ++i) {
174                         if (*p == '\0') {
175                                 break;
176                         } else if (*p == ':') {
177                                 p++;
178                                 break;
179                         } else {
180                                 path[i] = *p;
181                                 p++;
182                         }
183                 }
184                 path[i] = '\0';
185                 /* path shorter than "/dev/" means invalid or done */
186                 if (strlen(path) <= strlen("/dev/")) {
187                         if (strlen(p)) {
188                                 hprintf("ignore incomplete path %s\n", path);
189                                 continue;
190                         } else {
191                                 /* end of string */
192                                 KKASSERT(*p == '\0');
193                                 break;
194                         }
195                 }
196                 /* lookup path from above */
197                 KKASSERT(strncmp(path, "/dev/", 5) == 0);
198                 devvp = NULL;
199                 error = hammer2_lookup_device(path, rootmount, &devvp);
200                 if (error) {
201                         KKASSERT(!devvp);
202                         hprintf("failed to lookup %s %d\n", path, error);
203                         break;
204                 }
205                 KKASSERT(devvp);
206                 e = kmalloc(sizeof(*e), M_HAMMER2, M_WAITOK | M_ZERO);
207                 e->devvp = devvp;
208                 e->path = kstrdup(path, M_HAMMER2);
209                 TAILQ_INSERT_TAIL(devvpl, e, entry);
210         }
211         objcache_put(namei_oc, path);
212
213         return error;
214 }
215
216 void
217 hammer2_cleanup_devvp(hammer2_devvp_list_t *devvpl)
218 {
219         hammer2_devvp_t *e;
220
221         while (!TAILQ_EMPTY(devvpl)) {
222                 e = TAILQ_FIRST(devvpl);
223                 TAILQ_REMOVE(devvpl, e, entry);
224                 /* devvp */
225                 KKASSERT(e->devvp);
226                 e->devvp->v_rdev->si_mountpoint = NULL;
227                 vrele(e->devvp);
228                 e->devvp = NULL;
229                 /* path */
230                 KKASSERT(e->path);
231                 kfree(e->path, M_HAMMER2);
232                 e->path = NULL;
233                 kfree(e, M_HAMMER2);
234         }
235 }
236
237 static int
238 hammer2_verify_volumes_common(const hammer2_volume_t *volumes)
239 {
240         const hammer2_volume_t *vol;
241         struct partinfo part;
242         const char *path;
243         int i;
244
245         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
246                 vol = &volumes[i];
247                 if (vol->id == -1)
248                         continue;
249                 path = vol->dev->path;
250                 /* check volume fields are initialized */
251                 if (!vol->dev->devvp) {
252                         hprintf("%s has NULL devvp\n", path);
253                         return EINVAL;
254                 }
255                 if (vol->offset == (hammer2_off_t)-1) {
256                         hprintf("%s has bad offset 0x%016jx\n", path,
257                                 (intmax_t)vol->offset);
258                         return EINVAL;
259                 }
260                 if (vol->size == (hammer2_off_t)-1) {
261                         hprintf("%s has bad size 0x%016jx\n", path,
262                                 (intmax_t)vol->size);
263                         return EINVAL;
264                 }
265                 /* check volume size vs block device size */
266                 if (VOP_IOCTL(vol->dev->devvp, DIOCGPART, (void*)&part, 0,
267                               curthread->td_ucred , NULL) == 0) {
268                         if (vol->size > part.media_size) {
269                                 hprintf("%s's size 0x%016jx exceeds device size "
270                                         "0x%016jx\n", path, (intmax_t)vol->size,
271                                         part.media_size);
272                                 return EINVAL;
273                         }
274                 }
275         }
276
277         return 0;
278 }
279
280 static int
281 hammer2_verify_volumes_1(const hammer2_volume_t *volumes,
282                          const hammer2_volume_data_t *rootvoldata)
283 {
284         const hammer2_volume_t *vol;
285         hammer2_off_t off;
286         const char *path;
287         int i, nvolumes = 0;
288
289         /* check initialized volume count */
290         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
291                 vol = &volumes[i];
292                 if (vol->id != -1)
293                         nvolumes++;
294         }
295         if (nvolumes != 1) {
296                 hprintf("only 1 volume supported\n");
297                 return EINVAL;
298         }
299
300         /* check volume header */
301         if (rootvoldata->volu_id) {
302                 hprintf("volume id %d must be 0\n", rootvoldata->volu_id);
303                 return EINVAL;
304         }
305         if (rootvoldata->nvolumes) {
306                 hprintf("volume count %d must be 0\n", rootvoldata->nvolumes);
307                 return EINVAL;
308         }
309         if (rootvoldata->total_size) {
310                 hprintf("total size 0x%016jx must be 0\n",
311                         (intmax_t)rootvoldata->total_size);
312                 return EINVAL;
313         }
314         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
315                 off = rootvoldata->volu_loff[i];
316                 if (off) {
317                         hprintf("volume offset[%d] 0x%016jx must be 0\n", i,
318                                 (intmax_t)off);
319                         return EINVAL;
320                 }
321         }
322
323         /* check volume */
324         vol = &volumes[0];
325         path = vol->dev->path;
326         if (vol->id) {
327                 hprintf("%s has non zero id %d\n", path, vol->id);
328                 return EINVAL;
329         }
330         if (vol->offset) {
331                 hprintf("%s has non zero offset 0x%016jx\n", path,
332                         (intmax_t)vol->offset);
333                 return EINVAL;
334         }
335         if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) {
336                 hprintf("%s's size is not 0x%016jx aligned\n", path,
337                         (intmax_t)HAMMER2_VOLUME_ALIGN);
338                 return EINVAL;
339         }
340
341         return 0;
342 }
343
344 static int
345 hammer2_verify_volumes_2(const hammer2_volume_t *volumes,
346                          const hammer2_volume_data_t *rootvoldata)
347 {
348         const hammer2_volume_t *vol;
349         hammer2_off_t off, total_size = 0;
350         const char *path;
351         int i, nvolumes = 0;
352
353         /* check initialized volume count */
354         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
355                 vol = &volumes[i];
356                 if (vol->id != -1) {
357                         nvolumes++;
358                         total_size += vol->size;
359                 }
360         }
361
362         /* check volume header */
363         if (rootvoldata->volu_id != HAMMER2_ROOT_VOLUME) {
364                 hprintf("volume id %d must be %d\n", rootvoldata->volu_id,
365                         HAMMER2_ROOT_VOLUME);
366                 return EINVAL;
367         }
368         if (rootvoldata->nvolumes != nvolumes) {
369                 hprintf("volume header requires %d devices, %d specified\n",
370                         rootvoldata->nvolumes, nvolumes);
371                 return EINVAL;
372         }
373         if (rootvoldata->total_size != total_size) {
374                 hprintf("total size 0x%016jx does not equal sum of volumes 0x%016jx\n",
375                         rootvoldata->total_size, total_size);
376                 return EINVAL;
377         }
378         for (i = 0; i < nvolumes; ++i) {
379                 off = rootvoldata->volu_loff[i];
380                 if (off == (hammer2_off_t)-1) {
381                         hprintf("volume offset[%d] 0x%016jx must not be -1\n",
382                                 i, (intmax_t)off);
383                         return EINVAL;
384                 }
385         }
386         for (i = nvolumes; i < HAMMER2_MAX_VOLUMES; ++i) {
387                 off = rootvoldata->volu_loff[i];
388                 if (off != (hammer2_off_t)-1) {
389                         hprintf("volume offset[%d] 0x%016jx must be -1\n",
390                                 i, (intmax_t)off);
391                         return EINVAL;
392                 }
393         }
394
395         /* check volumes */
396         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
397                 vol = &volumes[i];
398                 if (vol->id == -1)
399                         continue;
400                 path = vol->dev->path;
401                 /* check offset */
402                 if (vol->offset & HAMMER2_FREEMAP_LEVEL1_MASK) {
403                         hprintf("%s's offset 0x%016jx not 0x%016jx aligned\n",
404                                 path, (intmax_t)vol->offset,
405                                 HAMMER2_FREEMAP_LEVEL1_SIZE);
406                         return EINVAL;
407                 }
408                 /* check vs previous volume */
409                 if (i) {
410                         if (vol->id <= (vol-1)->id) {
411                                 hprintf("%s has inconsistent id %d\n", path,
412                                         vol->id);
413                                 return EINVAL;
414                         }
415                         if (vol->offset != (vol-1)->offset + (vol-1)->size) {
416                                 hprintf("%s has inconsistent offset 0x%016jx\n",
417                                         path, (intmax_t)vol->offset);
418                                 return EINVAL;
419                         }
420                 } else { /* first */
421                         if (vol->offset) {
422                                 hprintf("%s has non zero offset 0x%016jx\n",
423                                         path, (intmax_t)vol->offset);
424                                 return EINVAL;
425                         }
426                 }
427                 /* check size for non-last and last volumes */
428                 if (i != rootvoldata->nvolumes - 1) {
429                         if (vol->size < HAMMER2_FREEMAP_LEVEL1_SIZE) {
430                                 hprintf("%s's size must be >= 0x%016jx\n", path,
431                                         (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE);
432                                 return EINVAL;
433                         }
434                         if (vol->size & HAMMER2_FREEMAP_LEVEL1_MASK) {
435                                 hprintf("%s's size is not 0x%016jx aligned\n",
436                                         path,
437                                         (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE);
438                                 return EINVAL;
439                         }
440                 } else { /* last */
441                         if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) {
442                                 hprintf("%s's size is not 0x%016jx aligned\n",
443                                         path,
444                                         (intmax_t)HAMMER2_VOLUME_ALIGN);
445                                 return EINVAL;
446                         }
447                 }
448         }
449
450         return 0;
451 }
452
453 static int
454 hammer2_verify_volumes(const hammer2_volume_t *volumes,
455                        const hammer2_volume_data_t *rootvoldata)
456 {
457         int error;
458
459         error = hammer2_verify_volumes_common(volumes);
460         if (error)
461                 return error;
462
463         if (rootvoldata->version >= HAMMER2_VOL_VERSION_MULTI_VOLUMES)
464                 return hammer2_verify_volumes_2(volumes, rootvoldata);
465         else
466                 return hammer2_verify_volumes_1(volumes, rootvoldata);
467 }
468
469 /*
470  * Returns zone# of returned volume header or < 0 on failure.
471  */
472 static int
473 hammer2_read_volume_header(struct vnode *devvp, const char *path,
474                            hammer2_volume_data_t *voldata)
475 {
476         hammer2_volume_data_t *vd;
477         struct buf *bp = NULL;
478         hammer2_crc32_t crc0, crc1;
479         int zone = -1;
480         int i;
481
482         /*
483          * There are up to 4 copies of the volume header (syncs iterate
484          * between them so there is no single master).  We don't trust the
485          * volu_size field so we don't know precisely how large the filesystem
486          * is, so depend on the OS to return an error if we go beyond the
487          * block device's EOF.
488          */
489         for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
490                 if (bread(devvp, i * HAMMER2_ZONE_BYTES64, HAMMER2_VOLUME_BYTES,
491                           &bp)) {
492                         brelse(bp);
493                         bp = NULL;
494                         continue;
495                 }
496
497                 vd = (struct hammer2_volume_data *)bp->b_data;
498                 /* verify volume header magic */
499                 if ((vd->magic != HAMMER2_VOLUME_ID_HBO) &&
500                     (vd->magic != HAMMER2_VOLUME_ID_ABO)) {
501                         hprintf("%s #%d: bad magic\n", path, i);
502                         brelse(bp);
503                         bp = NULL;
504                         continue;
505                 }
506
507                 if (vd->magic == HAMMER2_VOLUME_ID_ABO) {
508                         /* XXX: Reversed-endianness filesystem */
509                         hprintf("%s #%d: reverse-endian filesystem detected\n",
510                                 path, i);
511                         brelse(bp);
512                         bp = NULL;
513                         continue;
514                 }
515
516                 /* verify volume header CRC's */
517                 crc0 = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
518                 crc1 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC0_OFF,
519                                       HAMMER2_VOLUME_ICRC0_SIZE);
520                 if (crc0 != crc1) {
521                         hprintf("%s #%d: volume header crc mismatch sect0 %08x/%08x\n",
522                                 path, i, crc0, crc1);
523                         brelse(bp);
524                         bp = NULL;
525                         continue;
526                 }
527                 crc0 = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
528                 crc1 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC1_OFF,
529                                       HAMMER2_VOLUME_ICRC1_SIZE);
530                 if (crc0 != crc1) {
531                         hprintf("%s #%d: volume header crc mismatch sect1 %08x/%08x\n",
532                                 path, i, crc0, crc1);
533                         brelse(bp);
534                         bp = NULL;
535                         continue;
536                 }
537                 crc0 = vd->icrc_volheader;
538                 crc1 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRCVH_OFF,
539                                       HAMMER2_VOLUME_ICRCVH_SIZE);
540                 if (crc0 != crc1) {
541                         hprintf("%s #%d: volume header crc mismatch vh %08x/%08x\n",
542                                 path, i, crc0, crc1);
543                         brelse(bp);
544                         bp = NULL;
545                         continue;
546                 }
547
548                 if (zone == -1 || voldata->mirror_tid < vd->mirror_tid) {
549                         *voldata = *vd;
550                         zone = i;
551                 }
552                 brelse(bp);
553                 bp = NULL;
554         }
555
556         if (zone == -1) {
557                 hprintf("%s has no valid volume headers\n", path);
558                 return -EINVAL;
559         }
560         return zone;
561 }
562
563 static void
564 hammer2_print_uuid_mismatch(uuid_t *uuid1, uuid_t *uuid2, const char *id)
565 {
566         char buf1[64], buf2[64];
567
568         snprintf_uuid(buf1, sizeof(buf1), uuid1);
569         snprintf_uuid(buf2, sizeof(buf2), uuid2);
570
571         hprintf("%s uuid mismatch %s vs %s\n", id, buf1, buf2);
572 }
573
574 int
575 hammer2_init_volumes(struct mount *mp, const hammer2_devvp_list_t *devvpl,
576                      hammer2_volume_t *volumes,
577                      hammer2_volume_data_t *rootvoldata,
578                      int *rootvolzone,
579                      struct vnode **rootvoldevvp)
580 {
581         hammer2_devvp_t *e;
582         hammer2_volume_data_t *voldata;
583         hammer2_volume_t *vol;
584         struct vnode *devvp;
585         const char *path;
586         uuid_t fsid, fstype;
587         int i, zone, error = 0, version = -1, nvolumes = 0;
588
589         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
590                 vol = &volumes[i];
591                 vol->dev = NULL;
592                 vol->id = -1;
593                 vol->offset = (hammer2_off_t)-1;
594                 vol->size = (hammer2_off_t)-1;
595         }
596
597         voldata = kmalloc(sizeof(*voldata), M_HAMMER2, M_WAITOK | M_ZERO);
598         bzero(&fsid, sizeof(fsid));
599         bzero(&fstype, sizeof(fstype));
600         bzero(rootvoldata, sizeof(*rootvoldata));
601
602         TAILQ_FOREACH(e, devvpl, entry) {
603                 devvp = e->devvp;
604                 path = e->path;
605                 KKASSERT(devvp);
606
607                 /* returns negative error or positive zone# */
608                 error = hammer2_read_volume_header(devvp, path, voldata);
609                 if (error < 0) {
610                         hprintf("failed to read %s's volume header\n", path);
611                         error = -error;
612                         goto done;
613                 }
614                 zone = error;
615                 error = 0; /* reset error */
616
617                 if (voldata->volu_id >= HAMMER2_MAX_VOLUMES) {
618                         hprintf("%s has bad volume id %d\n", path,
619                                 voldata->volu_id);
620                         error = EINVAL;
621                         goto done;
622                 }
623                 vol = &volumes[voldata->volu_id];
624                 if (vol->id != -1) {
625                         hprintf("%s already initialized\n", path);
626                         error = EINVAL;
627                         goto done;
628                 }
629                 /* all headers must have the same version, nvolumes and uuid */
630                 if (version == -1) {
631                         version = voldata->version;
632                         nvolumes = voldata->nvolumes;
633                         fsid = voldata->fsid;
634                         fstype = voldata->fstype;
635                 } else {
636                         if (version != (int)voldata->version) {
637                                 hprintf("volume version mismatch %d vs %d\n",
638                                         version, (int)voldata->version);
639                                 error = ENXIO;
640                                 goto done;
641                         }
642                         if (nvolumes != voldata->nvolumes) {
643                                 hprintf("volume count mismatch %d vs %d\n",
644                                         nvolumes, voldata->nvolumes);
645                                 error = ENXIO;
646                                 goto done;
647                         }
648                         if (bcmp(&fsid, &voldata->fsid, sizeof(fsid))) {
649                                 hammer2_print_uuid_mismatch(&fsid,
650                                                             &voldata->fsid, "fsid");
651                                 error = ENXIO;
652                                 goto done;
653                         }
654                         if (bcmp(&fstype, &voldata->fstype, sizeof(fstype))) {
655                                 hammer2_print_uuid_mismatch(&fstype,
656                                                             &voldata->fstype, "fstype");
657                                 error = ENXIO;
658                                 goto done;
659                         }
660                 }
661                 if (version < HAMMER2_VOL_VERSION_MIN ||
662                     version > HAMMER2_VOL_VERSION_WIP) {
663                         hprintf("bad volume version %d\n", version);
664                         error = EINVAL;
665                         goto done;
666                 }
667                 /* all per-volume tests passed */
668                 vol->dev = e;
669                 vol->id = voldata->volu_id;
670                 vol->offset = voldata->volu_loff[vol->id];
671                 vol->size = voldata->volu_size;
672                 if (vol->id == HAMMER2_ROOT_VOLUME) {
673                         bcopy(voldata, rootvoldata, sizeof(*rootvoldata));
674                         *rootvolzone = zone;
675                         KKASSERT(*rootvoldevvp == NULL);
676                         *rootvoldevvp = e->devvp;
677                 }
678                 devvp->v_rdev->si_mountpoint = mp;
679                 hprintf("\"%s\" zone=%d id=%d offset=0x%016jx size=0x%016jx\n",
680                         path, zone, vol->id, (intmax_t)vol->offset,
681                         (intmax_t)vol->size);
682         }
683 done:
684         if (!error)
685                 error = hammer2_verify_volumes(volumes, rootvoldata);
686         kfree(voldata, M_HAMMER2);
687
688         return error;
689 }
690
691 hammer2_volume_t*
692 hammer2_get_volume(hammer2_dev_t *hmp, hammer2_off_t offset)
693 {
694         hammer2_volume_t *vol, *ret = NULL;
695         int i;
696
697         offset &= ~HAMMER2_OFF_MASK_RADIX;
698
699         /* locking is unneeded until volume-add support */
700         //hammer2_voldata_lock(hmp);
701         /* do binary search if users really use this many supported volumes */
702         for (i = 0; i < hmp->nvolumes; ++i) {
703                 vol = &hmp->volumes[i];
704                 if ((offset >= vol->offset) &&
705                     (offset < vol->offset + vol->size)) {
706                         ret = vol;
707                         break;
708                 }
709         }
710         //hammer2_voldata_unlock(hmp);
711
712         if (!ret)
713                 panic("no volume for offset 0x%016jx", (intmax_t)offset);
714
715         KKASSERT(ret);
716         KKASSERT(ret->dev);
717         KKASSERT(ret->dev->devvp);
718         KKASSERT(ret->dev->path);
719
720         return ret;
721 }