Merge branch 'vendor/OPENRESOLV'
[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/fcntl.h>
44 #include <sys/buf.h>
45 #include <sys/uuid.h>
46 #include <sys/objcache.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,
116                                          (ronly ? FREAD : FREAD | FWRITE),
117                                          FSCRED, NULL);
118                         if (error == 0)
119                                 e->open = 1;
120                         else
121                                 hprintf("failed to open %s %d\n", path, error);
122                 }
123                 vn_unlock(devvp);
124                 if (error)
125                         return error;
126                 KKASSERT(e->open);
127         }
128
129         return 0;
130 }
131
132 int
133 hammer2_close_devvp(const hammer2_devvp_list_t *devvpl, int ronly)
134 {
135         hammer2_devvp_t *e;
136         struct vnode *devvp;
137
138         TAILQ_FOREACH(e, devvpl, entry) {
139                 devvp = e->devvp;
140                 KKASSERT(devvp);
141                 if (e->open) {
142                         vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
143                         vinvalbuf(devvp, (ronly ? 0 : V_SAVE), 0, 0);
144                         VOP_CLOSE(devvp, (ronly ? FREAD : FREAD|FWRITE), NULL);
145                         vn_unlock(devvp);
146                         e->open = 0;
147                 }
148         }
149
150         return 0;
151 }
152
153 int
154 hammer2_init_devvp(const char *blkdevs, int rootmount,
155                    hammer2_devvp_list_t *devvpl)
156 {
157         hammer2_devvp_t *e;
158         struct vnode *devvp;
159         const char *p;
160         char *path;
161         int i, error = 0;
162
163         KKASSERT(TAILQ_EMPTY(devvpl));
164         KKASSERT(blkdevs); /* could be empty string */
165         p = blkdevs;
166
167         path = objcache_get(namei_oc, M_WAITOK);
168         while (1) {
169                 strcpy(path, "");
170                 if (*p != '/') {
171                         strcpy(path, "/dev/"); /* relative path */
172                 }
173                 /* scan beyond "/dev/" */
174                 for (i = strlen(path); i < MAXPATHLEN-1; ++i) {
175                         if (*p == '\0') {
176                                 break;
177                         } else if (*p == ':') {
178                                 p++;
179                                 break;
180                         } else {
181                                 path[i] = *p;
182                                 p++;
183                         }
184                 }
185                 path[i] = '\0';
186                 /* path shorter than "/dev/" means invalid or done */
187                 if (strlen(path) <= strlen("/dev/")) {
188                         if (strlen(p)) {
189                                 hprintf("ignore incomplete path %s\n", path);
190                                 continue;
191                         } else {
192                                 /* end of string */
193                                 KKASSERT(*p == '\0');
194                                 break;
195                         }
196                 }
197                 /* lookup path from above */
198                 KKASSERT(strncmp(path, "/dev/", 5) == 0);
199                 devvp = NULL;
200                 error = hammer2_lookup_device(path, rootmount, &devvp);
201                 if (error) {
202                         KKASSERT(!devvp);
203                         hprintf("failed to lookup %s %d\n", path, error);
204                         break;
205                 }
206                 KKASSERT(devvp);
207                 e = kmalloc(sizeof(*e), M_HAMMER2, M_WAITOK | M_ZERO);
208                 e->devvp = devvp;
209                 e->path = kstrdup(path, M_HAMMER2);
210                 TAILQ_INSERT_TAIL(devvpl, e, entry);
211         }
212         objcache_put(namei_oc, path);
213
214         return error;
215 }
216
217 void
218 hammer2_cleanup_devvp(hammer2_devvp_list_t *devvpl)
219 {
220         hammer2_devvp_t *e;
221
222         while (!TAILQ_EMPTY(devvpl)) {
223                 e = TAILQ_FIRST(devvpl);
224                 TAILQ_REMOVE(devvpl, e, entry);
225                 /* devvp */
226                 KKASSERT(e->devvp);
227                 e->devvp->v_rdev->si_mountpoint = NULL;
228                 vrele(e->devvp);
229                 e->devvp = NULL;
230                 /* path */
231                 KKASSERT(e->path);
232                 kfree(e->path, M_HAMMER2);
233                 e->path = NULL;
234                 kfree(e, M_HAMMER2);
235         }
236 }
237
238 static int
239 hammer2_verify_volumes_common(const hammer2_volume_t *volumes)
240 {
241         const hammer2_volume_t *vol;
242         struct partinfo part;
243         const char *path;
244         int i;
245
246         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
247                 vol = &volumes[i];
248                 if (vol->id == -1)
249                         continue;
250                 path = vol->dev->path;
251                 /* check volume fields are initialized */
252                 if (!vol->dev->devvp) {
253                         hprintf("%s has NULL devvp\n", path);
254                         return EINVAL;
255                 }
256                 if (vol->offset == (hammer2_off_t)-1) {
257                         hprintf("%s has bad offset 0x%016jx\n", path,
258                                 (intmax_t)vol->offset);
259                         return EINVAL;
260                 }
261                 if (vol->size == (hammer2_off_t)-1) {
262                         hprintf("%s has bad size 0x%016jx\n", path,
263                                 (intmax_t)vol->size);
264                         return EINVAL;
265                 }
266                 /* check volume size vs block device size */
267                 if (VOP_IOCTL(vol->dev->devvp, DIOCGPART, (void*)&part, 0,
268                               curthread->td_ucred , NULL) == 0) {
269                         if (vol->size > part.media_size) {
270                                 hprintf("%s's size 0x%016jx exceeds device size "
271                                         "0x%016jx\n", path, (intmax_t)vol->size,
272                                         part.media_size);
273                                 return EINVAL;
274                         }
275                 }
276         }
277
278         return 0;
279 }
280
281 static int
282 hammer2_verify_volumes_1(const hammer2_volume_t *volumes,
283                          const hammer2_volume_data_t *rootvoldata)
284 {
285         const hammer2_volume_t *vol;
286         hammer2_off_t off;
287         const char *path;
288         int i, nvolumes = 0;
289
290         /* check initialized volume count */
291         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
292                 vol = &volumes[i];
293                 if (vol->id != -1)
294                         nvolumes++;
295         }
296         if (nvolumes != 1) {
297                 hprintf("only 1 volume supported\n");
298                 return EINVAL;
299         }
300
301         /* check volume header */
302         if (rootvoldata->volu_id) {
303                 hprintf("volume id %d must be 0\n", rootvoldata->volu_id);
304                 return EINVAL;
305         }
306         if (rootvoldata->nvolumes) {
307                 hprintf("volume count %d must be 0\n", rootvoldata->nvolumes);
308                 return EINVAL;
309         }
310         if (rootvoldata->total_size) {
311                 hprintf("total size 0x%016jx must be 0\n",
312                         (intmax_t)rootvoldata->total_size);
313                 return EINVAL;
314         }
315         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
316                 off = rootvoldata->volu_loff[i];
317                 if (off) {
318                         hprintf("volume offset[%d] 0x%016jx must be 0\n", i,
319                                 (intmax_t)off);
320                         return EINVAL;
321                 }
322         }
323
324         /* check volume */
325         vol = &volumes[0];
326         path = vol->dev->path;
327         if (vol->id) {
328                 hprintf("%s has non zero id %d\n", path, vol->id);
329                 return EINVAL;
330         }
331         if (vol->offset) {
332                 hprintf("%s has non zero offset 0x%016jx\n", path,
333                         (intmax_t)vol->offset);
334                 return EINVAL;
335         }
336         if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) {
337                 hprintf("%s's size is not 0x%016jx aligned\n", path,
338                         (intmax_t)HAMMER2_VOLUME_ALIGN);
339                 return EINVAL;
340         }
341
342         return 0;
343 }
344
345 static int
346 hammer2_verify_volumes_2(const hammer2_volume_t *volumes,
347                          const hammer2_volume_data_t *rootvoldata)
348 {
349         const hammer2_volume_t *vol;
350         hammer2_off_t off, total_size = 0;
351         const char *path;
352         int i, nvolumes = 0;
353
354         /* check initialized volume count */
355         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
356                 vol = &volumes[i];
357                 if (vol->id != -1) {
358                         nvolumes++;
359                         total_size += vol->size;
360                 }
361         }
362
363         /* check volume header */
364         if (rootvoldata->volu_id != HAMMER2_ROOT_VOLUME) {
365                 hprintf("volume id %d must be %d\n", rootvoldata->volu_id,
366                         HAMMER2_ROOT_VOLUME);
367                 return EINVAL;
368         }
369         if (rootvoldata->nvolumes != nvolumes) {
370                 hprintf("volume header requires %d devices, %d specified\n",
371                         rootvoldata->nvolumes, nvolumes);
372                 return EINVAL;
373         }
374         if (rootvoldata->total_size != total_size) {
375                 hprintf("total size 0x%016jx does not equal sum of volumes 0x%016jx\n",
376                         rootvoldata->total_size, total_size);
377                 return EINVAL;
378         }
379         for (i = 0; i < nvolumes; ++i) {
380                 off = rootvoldata->volu_loff[i];
381                 if (off == (hammer2_off_t)-1) {
382                         hprintf("volume offset[%d] 0x%016jx must not be -1\n",
383                                 i, (intmax_t)off);
384                         return EINVAL;
385                 }
386         }
387         for (i = nvolumes; i < HAMMER2_MAX_VOLUMES; ++i) {
388                 off = rootvoldata->volu_loff[i];
389                 if (off != (hammer2_off_t)-1) {
390                         hprintf("volume offset[%d] 0x%016jx must be -1\n",
391                                 i, (intmax_t)off);
392                         return EINVAL;
393                 }
394         }
395
396         /* check volumes */
397         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
398                 vol = &volumes[i];
399                 if (vol->id == -1)
400                         continue;
401                 path = vol->dev->path;
402                 /* check offset */
403                 if (vol->offset & HAMMER2_FREEMAP_LEVEL1_MASK) {
404                         hprintf("%s's offset 0x%016jx not 0x%016jx aligned\n",
405                                 path, (intmax_t)vol->offset,
406                                 HAMMER2_FREEMAP_LEVEL1_SIZE);
407                         return EINVAL;
408                 }
409                 /* check vs previous volume */
410                 if (i) {
411                         if (vol->id <= (vol-1)->id) {
412                                 hprintf("%s has inconsistent id %d\n", path,
413                                         vol->id);
414                                 return EINVAL;
415                         }
416                         if (vol->offset != (vol-1)->offset + (vol-1)->size) {
417                                 hprintf("%s has inconsistent offset 0x%016jx\n",
418                                         path, (intmax_t)vol->offset);
419                                 return EINVAL;
420                         }
421                 } else { /* first */
422                         if (vol->offset) {
423                                 hprintf("%s has non zero offset 0x%016jx\n",
424                                         path, (intmax_t)vol->offset);
425                                 return EINVAL;
426                         }
427                 }
428                 /* check size for non-last and last volumes */
429                 if (i != rootvoldata->nvolumes - 1) {
430                         if (vol->size < HAMMER2_FREEMAP_LEVEL1_SIZE) {
431                                 hprintf("%s's size must be >= 0x%016jx\n", path,
432                                         (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE);
433                                 return EINVAL;
434                         }
435                         if (vol->size & HAMMER2_FREEMAP_LEVEL1_MASK) {
436                                 hprintf("%s's size is not 0x%016jx aligned\n",
437                                         path,
438                                         (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE);
439                                 return EINVAL;
440                         }
441                 } else { /* last */
442                         if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) {
443                                 hprintf("%s's size is not 0x%016jx aligned\n",
444                                         path,
445                                         (intmax_t)HAMMER2_VOLUME_ALIGN);
446                                 return EINVAL;
447                         }
448                 }
449         }
450
451         return 0;
452 }
453
454 static int
455 hammer2_verify_volumes(const hammer2_volume_t *volumes,
456                        const hammer2_volume_data_t *rootvoldata)
457 {
458         int error;
459
460         error = hammer2_verify_volumes_common(volumes);
461         if (error)
462                 return error;
463
464         if (rootvoldata->version >= HAMMER2_VOL_VERSION_MULTI_VOLUMES)
465                 return hammer2_verify_volumes_2(volumes, rootvoldata);
466         else
467                 return hammer2_verify_volumes_1(volumes, rootvoldata);
468 }
469
470 /*
471  * Returns zone# of returned volume header or < 0 on failure.
472  */
473 static int
474 hammer2_read_volume_header(struct vnode *devvp, const char *path,
475                            hammer2_volume_data_t *voldata)
476 {
477         hammer2_volume_data_t *vd;
478         struct buf *bp = NULL;
479         hammer2_crc32_t crc0, crc1;
480         int zone = -1;
481         int i;
482
483         /*
484          * There are up to 4 copies of the volume header (syncs iterate
485          * between them so there is no single master).  We don't trust the
486          * volu_size field so we don't know precisely how large the filesystem
487          * is, so depend on the OS to return an error if we go beyond the
488          * block device's EOF.
489          */
490         for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
491                 if (bread(devvp, i * HAMMER2_ZONE_BYTES64, HAMMER2_VOLUME_BYTES,
492                           &bp)) {
493                         brelse(bp);
494                         bp = NULL;
495                         continue;
496                 }
497
498                 vd = (struct hammer2_volume_data *)bp->b_data;
499                 /* verify volume header magic */
500                 if ((vd->magic != HAMMER2_VOLUME_ID_HBO) &&
501                     (vd->magic != HAMMER2_VOLUME_ID_ABO)) {
502                         hprintf("%s #%d: bad magic\n", path, i);
503                         brelse(bp);
504                         bp = NULL;
505                         continue;
506                 }
507
508                 if (vd->magic == HAMMER2_VOLUME_ID_ABO) {
509                         /* XXX: Reversed-endianness filesystem */
510                         hprintf("%s #%d: reverse-endian filesystem detected\n",
511                                 path, i);
512                         brelse(bp);
513                         bp = NULL;
514                         continue;
515                 }
516
517                 /* verify volume header CRC's */
518                 crc0 = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
519                 crc1 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC0_OFF,
520                                       HAMMER2_VOLUME_ICRC0_SIZE);
521                 if (crc0 != crc1) {
522                         hprintf("%s #%d: volume header crc mismatch sect0 %08x/%08x\n",
523                                 path, i, crc0, crc1);
524                         brelse(bp);
525                         bp = NULL;
526                         continue;
527                 }
528                 crc0 = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
529                 crc1 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC1_OFF,
530                                       HAMMER2_VOLUME_ICRC1_SIZE);
531                 if (crc0 != crc1) {
532                         hprintf("%s #%d: volume header crc mismatch sect1 %08x/%08x\n",
533                                 path, i, crc0, crc1);
534                         brelse(bp);
535                         bp = NULL;
536                         continue;
537                 }
538                 crc0 = vd->icrc_volheader;
539                 crc1 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRCVH_OFF,
540                                       HAMMER2_VOLUME_ICRCVH_SIZE);
541                 if (crc0 != crc1) {
542                         hprintf("%s #%d: volume header crc mismatch vh %08x/%08x\n",
543                                 path, i, crc0, crc1);
544                         brelse(bp);
545                         bp = NULL;
546                         continue;
547                 }
548
549                 if (zone == -1 || voldata->mirror_tid < vd->mirror_tid) {
550                         *voldata = *vd;
551                         zone = i;
552                 }
553                 brelse(bp);
554                 bp = NULL;
555         }
556
557         if (zone == -1) {
558                 hprintf("%s has no valid volume headers\n", path);
559                 return -EINVAL;
560         }
561         return zone;
562 }
563
564 int
565 hammer2_init_volumes(struct mount *mp, const hammer2_devvp_list_t *devvpl,
566                      hammer2_volume_t *volumes,
567                      hammer2_volume_data_t *rootvoldata,
568                      struct vnode **rootvoldevvp)
569 {
570         hammer2_devvp_t *e;
571         hammer2_volume_data_t *voldata;
572         hammer2_volume_t *vol;
573         struct vnode *devvp;
574         const char *path;
575         uuid_t fsid, fstype;
576         int i, zone, error = 0, version = -1, nvolumes = 0;
577
578         for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
579                 vol = &volumes[i];
580                 vol->dev = NULL;
581                 vol->id = -1;
582                 vol->offset = (hammer2_off_t)-1;
583                 vol->size = (hammer2_off_t)-1;
584         }
585
586         voldata = kmalloc(sizeof(*voldata), M_HAMMER2, M_WAITOK | M_ZERO);
587         bzero(&fsid, sizeof(fsid));
588         bzero(&fstype, sizeof(fstype));
589         bzero(rootvoldata, sizeof(*rootvoldata));
590
591         TAILQ_FOREACH(e, devvpl, entry) {
592                 devvp = e->devvp;
593                 path = e->path;
594                 KKASSERT(devvp);
595
596                 /* returns negative error or positive zone# */
597                 error = hammer2_read_volume_header(devvp, path, voldata);
598                 if (error < 0) {
599                         hprintf("failed to read %s's volume header\n", path);
600                         error = -error;
601                         goto done;
602                 }
603                 zone = error;
604                 error = 0; /* reset error */
605
606                 if (voldata->volu_id >= HAMMER2_MAX_VOLUMES) {
607                         hprintf("%s has bad volume id %d\n", path,
608                                 voldata->volu_id);
609                         error = EINVAL;
610                         goto done;
611                 }
612                 vol = &volumes[voldata->volu_id];
613                 if (vol->id != -1) {
614                         hprintf("%s already initialized\n", path);
615                         error = EINVAL;
616                         goto done;
617                 }
618                 /* all headers must have the same version, nvolumes and uuid */
619                 if (version == -1) {
620                         version = voldata->version;
621                         nvolumes = voldata->nvolumes;
622                         fsid = voldata->fsid;
623                         fstype = voldata->fstype;
624                 } else {
625                         if (version != (int)voldata->version) {
626                                 hprintf("volume version mismatch %d vs %d\n",
627                                         version, (int)voldata->version);
628                                 error = ENXIO;
629                                 goto done;
630                         }
631                         if (nvolumes != voldata->nvolumes) {
632                                 hprintf("volume count mismatch %d vs %d\n",
633                                         nvolumes, voldata->nvolumes);
634                                 error = ENXIO;
635                                 goto done;
636                         }
637                         if (bcmp(&fsid, &voldata->fsid, sizeof(fsid))) {
638                                 hprintf("fsid uuid mismatch\n");
639                                 error = ENXIO;
640                                 goto done;
641                         }
642                         if (bcmp(&fstype, &voldata->fstype, sizeof(fstype))) {
643                                 hprintf("fstype uuid mismatch\n");
644                                 error = ENXIO;
645                                 goto done;
646                         }
647                 }
648                 if (version < HAMMER2_VOL_VERSION_MIN ||
649                     version > HAMMER2_VOL_VERSION_WIP) {
650                         hprintf("bad volume version %d\n", version);
651                         error = EINVAL;
652                         goto done;
653                 }
654                 /* all per-volume tests passed */
655                 vol->dev = e;
656                 vol->id = voldata->volu_id;
657                 vol->offset = voldata->volu_loff[vol->id];
658                 vol->size = voldata->volu_size;
659                 if (vol->id == HAMMER2_ROOT_VOLUME) {
660                         bcopy(voldata, rootvoldata, sizeof(*rootvoldata));
661                         KKASSERT(*rootvoldevvp == NULL);
662                         *rootvoldevvp = e->devvp;
663                 }
664                 devvp->v_rdev->si_mountpoint = mp;
665                 hprintf("\"%s\" zone=%d id=%d offset=0x%016jx size=0x%016jx\n",
666                         path, zone, vol->id, (intmax_t)vol->offset,
667                         (intmax_t)vol->size);
668         }
669 done:
670         if (!error)
671                 error = hammer2_verify_volumes(volumes, rootvoldata);
672         kfree(voldata, M_HAMMER2);
673
674         return error;
675 }
676
677 hammer2_volume_t*
678 hammer2_get_volume(hammer2_dev_t *hmp, hammer2_off_t offset)
679 {
680         hammer2_volume_t *vol, *ret = NULL;
681         int i;
682
683         offset &= ~HAMMER2_OFF_MASK_RADIX;
684
685         /* locking is unneeded until volume-add support */
686         //hammer2_voldata_lock(hmp);
687         /* do binary search if users really use this many supported volumes */
688         for (i = 0; i < hmp->nvolumes; ++i) {
689                 vol = &hmp->volumes[i];
690                 if ((offset >= vol->offset) &&
691                     (offset < vol->offset + vol->size)) {
692                         ret = vol;
693                         break;
694                 }
695         }
696         //hammer2_voldata_unlock(hmp);
697
698         if (!ret)
699                 panic("no volume for offset 0x%016jx", (intmax_t)offset);
700
701         KKASSERT(ret);
702         KKASSERT(ret->dev);
703         KKASSERT(ret->dev->devvp);
704         KKASSERT(ret->dev->path);
705
706         return ret;
707 }