2 * Copyright (c) 2020 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2020 The DragonFly Project
6 * This code is derived from software contributed to The DragonFly Project
7 * by Matthew Dillon <dillon@dragonflybsd.org>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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
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.
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
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>
45 #include <sys/objcache.h>
50 #define hprintf(X, ...) kprintf("hammer2_ondisk: " X, ## __VA_ARGS__)
53 hammer2_lookup_device(const char *path
, int rootmount
, struct vnode
**devvp
)
55 struct vnode
*vp
= NULL
;
56 struct nlookupdata nd
;
60 KKASSERT(*path
!= '\0');
63 error
= bdevvp(kgetdiskbyname(path
), &vp
);
65 hprintf("cannot find %s %d\n", path
, error
);
67 error
= nlookup_init(&nd
, path
, UIO_SYSSPACE
, NLC_FOLLOW
);
71 error
= cache_vref(&nd
.nl_nch
, nd
.nl_cred
, &vp
);
73 hprintf("failed to nlookup %s %d\n", path
, error
);
79 if (!vn_isdisk(vp
, &error
)) {
81 hprintf("%s not a block device %d\n", path
, error
);
95 hammer2_open_devvp(const hammer2_devvp_list_t
*devvpl
, int ronly
)
102 TAILQ_FOREACH(e
, devvpl
, entry
) {
106 count
= vcount(devvp
);
108 hprintf("%s already has %d references\n", path
, count
);
111 vn_lock(devvp
, LK_EXCLUSIVE
| LK_RETRY
);
112 error
= vinvalbuf(devvp
, V_SAVE
, 0, 0);
115 error
= VOP_OPEN(devvp
, (ronly
? FREAD
: FREAD
|FWRITE
),
120 hprintf("failed to open %s %d\n", path
, error
);
132 hammer2_close_devvp(const hammer2_devvp_list_t
*devvpl
, int ronly
)
137 TAILQ_FOREACH(e
, devvpl
, entry
) {
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
);
153 hammer2_init_devvp(const char *blkdevs
, int rootmount
,
154 hammer2_devvp_list_t
*devvpl
)
162 KKASSERT(TAILQ_EMPTY(devvpl
));
163 KKASSERT(blkdevs
); /* could be empty string */
166 path
= objcache_get(namei_oc
, M_WAITOK
);
170 strcpy(path
, "/dev/"); /* relative path */
172 /* scan beyond "/dev/" */
173 for (i
= strlen(path
); i
< MAXPATHLEN
-1; ++i
) {
176 } else if (*p
== ':') {
185 /* path shorter than "/dev/" means invalid or done */
186 if (strlen(path
) <= strlen("/dev/")) {
188 hprintf("ignore incomplete path %s\n", path
);
192 KKASSERT(*p
== '\0');
196 /* lookup path from above */
197 KKASSERT(strncmp(path
, "/dev/", 5) == 0);
199 error
= hammer2_lookup_device(path
, rootmount
, &devvp
);
202 hprintf("failed to lookup %s %d\n", path
, error
);
206 e
= kmalloc(sizeof(*e
), M_HAMMER2
, M_WAITOK
| M_ZERO
);
208 e
->path
= kstrdup(path
, M_HAMMER2
);
209 TAILQ_INSERT_TAIL(devvpl
, e
, entry
);
211 objcache_put(namei_oc
, path
);
217 hammer2_cleanup_devvp(hammer2_devvp_list_t
*devvpl
)
221 while (!TAILQ_EMPTY(devvpl
)) {
222 e
= TAILQ_FIRST(devvpl
);
223 TAILQ_REMOVE(devvpl
, e
, entry
);
226 if (e
->devvp
->v_rdev
)
227 e
->devvp
->v_rdev
->si_mountpoint
= NULL
;
232 kfree(e
->path
, M_HAMMER2
);
239 hammer2_verify_volumes_common(const hammer2_volume_t
*volumes
)
241 const hammer2_volume_t
*vol
;
242 struct partinfo part
;
246 for (i
= 0; i
< HAMMER2_MAX_VOLUMES
; ++i
) {
250 path
= vol
->dev
->path
;
251 /* check volume fields are initialized */
252 if (!vol
->dev
->devvp
) {
253 hprintf("%s has NULL devvp\n", path
);
256 if (vol
->offset
== (hammer2_off_t
)-1) {
257 hprintf("%s has bad offset 0x%016jx\n", path
,
258 (intmax_t)vol
->offset
);
261 if (vol
->size
== (hammer2_off_t
)-1) {
262 hprintf("%s has bad size 0x%016jx\n", path
,
263 (intmax_t)vol
->size
);
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
,
282 hammer2_verify_volumes_1(const hammer2_volume_t
*volumes
,
283 const hammer2_volume_data_t
*rootvoldata
)
285 const hammer2_volume_t
*vol
;
290 /* check initialized volume count */
291 for (i
= 0; i
< HAMMER2_MAX_VOLUMES
; ++i
) {
297 hprintf("only 1 volume supported\n");
301 /* check volume header */
302 if (rootvoldata
->volu_id
) {
303 hprintf("volume id %d must be 0\n", rootvoldata
->volu_id
);
306 if (rootvoldata
->nvolumes
) {
307 hprintf("volume count %d must be 0\n", rootvoldata
->nvolumes
);
310 if (rootvoldata
->total_size
) {
311 hprintf("total size 0x%016jx must be 0\n",
312 (intmax_t)rootvoldata
->total_size
);
315 for (i
= 0; i
< HAMMER2_MAX_VOLUMES
; ++i
) {
316 off
= rootvoldata
->volu_loff
[i
];
318 hprintf("volume offset[%d] 0x%016jx must be 0\n", i
,
325 vol
= &volumes
[HAMMER2_ROOT_VOLUME
];
326 path
= vol
->dev
->path
;
328 hprintf("%s has non zero id %d\n", path
, vol
->id
);
332 hprintf("%s has non zero offset 0x%016jx\n", path
,
333 (intmax_t)vol
->offset
);
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
);
346 hammer2_verify_volumes_2(const hammer2_volume_t
*volumes
,
347 const hammer2_volume_data_t
*rootvoldata
)
349 const hammer2_volume_t
*vol
;
350 hammer2_off_t off
, total_size
= 0;
354 /* check initialized volume count */
355 for (i
= 0; i
< HAMMER2_MAX_VOLUMES
; ++i
) {
359 total_size
+= vol
->size
;
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
);
369 if (rootvoldata
->nvolumes
!= nvolumes
) {
370 hprintf("volume header requires %d devices, %d specified\n",
371 rootvoldata
->nvolumes
, nvolumes
);
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
);
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",
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",
397 for (i
= 0; i
< HAMMER2_MAX_VOLUMES
; ++i
) {
401 path
= vol
->dev
->path
;
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
);
409 /* check vs previous volume */
411 if (vol
->id
<= (vol
-1)->id
) {
412 hprintf("%s has inconsistent id %d\n", path
,
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
);
423 hprintf("%s has non zero offset 0x%016jx\n",
424 path
, (intmax_t)vol
->offset
);
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
);
435 if (vol
->size
& HAMMER2_FREEMAP_LEVEL1_MASK
) {
436 hprintf("%s's size is not 0x%016jx aligned\n",
438 (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE
);
442 if (vol
->size
& HAMMER2_VOLUME_ALIGNMASK64
) {
443 hprintf("%s's size is not 0x%016jx aligned\n",
445 (intmax_t)HAMMER2_VOLUME_ALIGN
);
455 hammer2_verify_volumes(const hammer2_volume_t
*volumes
,
456 const hammer2_volume_data_t
*rootvoldata
)
460 error
= hammer2_verify_volumes_common(volumes
);
464 if (rootvoldata
->version
>= HAMMER2_VOL_VERSION_MULTI_VOLUMES
)
465 return hammer2_verify_volumes_2(volumes
, rootvoldata
);
467 return hammer2_verify_volumes_1(volumes
, rootvoldata
);
471 * Returns zone# of returned volume header or < 0 on failure.
474 hammer2_read_volume_header(struct vnode
*devvp
, const char *path
,
475 hammer2_volume_data_t
*voldata
)
477 hammer2_volume_data_t
*vd
;
478 struct buf
*bp
= NULL
;
479 hammer2_crc32_t crc0
, crc1
;
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.
490 for (i
= 0; i
< HAMMER2_NUM_VOLHDRS
; ++i
) {
491 if (bread(devvp
, i
* HAMMER2_ZONE_BYTES64
, HAMMER2_VOLUME_BYTES
,
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
);
508 if (vd
->magic
== HAMMER2_VOLUME_ID_ABO
) {
509 /* XXX: Reversed-endianness filesystem */
510 hprintf("%s #%d: reverse-endian filesystem detected\n",
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
);
522 hprintf("%s #%d: volume header crc mismatch sect0 %08x/%08x\n",
523 path
, i
, crc0
, crc1
);
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
);
532 hprintf("%s #%d: volume header crc mismatch sect1 %08x/%08x\n",
533 path
, i
, crc0
, crc1
);
538 crc0
= vd
->icrc_volheader
;
539 crc1
= hammer2_icrc32(bp
->b_data
+ HAMMER2_VOLUME_ICRCVH_OFF
,
540 HAMMER2_VOLUME_ICRCVH_SIZE
);
542 hprintf("%s #%d: volume header crc mismatch vh %08x/%08x\n",
543 path
, i
, crc0
, crc1
);
549 if (zone
== -1 || voldata
->mirror_tid
< vd
->mirror_tid
) {
558 hprintf("%s has no valid volume headers\n", path
);
565 hammer2_print_uuid_mismatch(uuid_t
*uuid1
, uuid_t
*uuid2
, const char *id
)
567 char buf1
[64], buf2
[64];
569 snprintf_uuid(buf1
, sizeof(buf1
), uuid1
);
570 snprintf_uuid(buf2
, sizeof(buf2
), uuid2
);
572 hprintf("%s uuid mismatch %s vs %s\n", id
, buf1
, buf2
);
576 hammer2_init_volumes(struct mount
*mp
, const hammer2_devvp_list_t
*devvpl
,
577 hammer2_volume_t
*volumes
,
578 hammer2_volume_data_t
*rootvoldata
,
580 struct vnode
**rootvoldevvp
)
583 hammer2_volume_data_t
*voldata
;
584 hammer2_volume_t
*vol
;
588 int i
, zone
, error
= 0, version
= -1, nvolumes
= 0;
590 for (i
= 0; i
< HAMMER2_MAX_VOLUMES
; ++i
) {
594 vol
->offset
= (hammer2_off_t
)-1;
595 vol
->size
= (hammer2_off_t
)-1;
598 voldata
= kmalloc(sizeof(*voldata
), M_HAMMER2
, M_WAITOK
| M_ZERO
);
599 bzero(&fsid
, sizeof(fsid
));
600 bzero(&fstype
, sizeof(fstype
));
601 bzero(rootvoldata
, sizeof(*rootvoldata
));
603 TAILQ_FOREACH(e
, devvpl
, entry
) {
608 /* returns negative error or positive zone# */
609 error
= hammer2_read_volume_header(devvp
, path
, voldata
);
611 hprintf("failed to read %s's volume header\n", path
);
616 error
= 0; /* reset error */
618 if (voldata
->volu_id
>= HAMMER2_MAX_VOLUMES
) {
619 hprintf("%s has bad volume id %d\n", path
,
624 vol
= &volumes
[voldata
->volu_id
];
626 hprintf("volume id %d already initialized\n",
631 /* all headers must have the same version, nvolumes and uuid */
633 version
= voldata
->version
;
634 nvolumes
= voldata
->nvolumes
;
635 fsid
= voldata
->fsid
;
636 fstype
= voldata
->fstype
;
638 if (version
!= (int)voldata
->version
) {
639 hprintf("volume version mismatch %d vs %d\n",
640 version
, (int)voldata
->version
);
644 if (nvolumes
!= voldata
->nvolumes
) {
645 hprintf("volume count mismatch %d vs %d\n",
646 nvolumes
, voldata
->nvolumes
);
650 if (bcmp(&fsid
, &voldata
->fsid
, sizeof(fsid
))) {
651 hammer2_print_uuid_mismatch(&fsid
,
652 &voldata
->fsid
, "fsid");
656 if (bcmp(&fstype
, &voldata
->fstype
, sizeof(fstype
))) {
657 hammer2_print_uuid_mismatch(&fstype
,
658 &voldata
->fstype
, "fstype");
663 if (version
< HAMMER2_VOL_VERSION_MIN
||
664 version
> HAMMER2_VOL_VERSION_WIP
) {
665 hprintf("bad volume version %d\n", version
);
669 /* all per-volume tests passed */
671 vol
->id
= voldata
->volu_id
;
672 vol
->offset
= voldata
->volu_loff
[vol
->id
];
673 vol
->size
= voldata
->volu_size
;
674 if (vol
->id
== HAMMER2_ROOT_VOLUME
) {
675 bcopy(voldata
, rootvoldata
, sizeof(*rootvoldata
));
677 KKASSERT(*rootvoldevvp
== NULL
);
678 *rootvoldevvp
= e
->devvp
;
680 devvp
->v_rdev
->si_mountpoint
= mp
;
681 hprintf("\"%s\" zone=%d id=%d offset=0x%016jx size=0x%016jx\n",
682 path
, zone
, vol
->id
, (intmax_t)vol
->offset
,
683 (intmax_t)vol
->size
);
687 if (!rootvoldata
->version
) {
688 hprintf("root volume not found\n");
692 error
= hammer2_verify_volumes(volumes
, rootvoldata
);
694 kfree(voldata
, M_HAMMER2
);
700 hammer2_get_volume(hammer2_dev_t
*hmp
, hammer2_off_t offset
)
702 hammer2_volume_t
*vol
, *ret
= NULL
;
705 offset
&= ~HAMMER2_OFF_MASK_RADIX
;
707 /* locking is unneeded until volume-add support */
708 //hammer2_voldata_lock(hmp);
709 /* do binary search if users really use this many supported volumes */
710 for (i
= 0; i
< hmp
->nvolumes
; ++i
) {
711 vol
= &hmp
->volumes
[i
];
712 if ((offset
>= vol
->offset
) &&
713 (offset
< vol
->offset
+ vol
->size
)) {
718 //hammer2_voldata_unlock(hmp);
721 panic("no volume for offset 0x%016jx", (intmax_t)offset
);
725 KKASSERT(ret
->dev
->devvp
);
726 KKASSERT(ret
->dev
->path
);