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>
43 #include <sys/fcntl.h>
46 #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 e->devvp->v_rdev->si_mountpoint = NULL;
231 kfree(e->path, M_HAMMER2);
238 hammer2_verify_volumes_common(const hammer2_volume_t *volumes)
240 const hammer2_volume_t *vol;
241 struct partinfo part;
245 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
249 path = vol->dev->path;
250 /* check volume fields are initialized */
251 if (!vol->dev->devvp) {
252 hprintf("%s has NULL devvp\n", path);
255 if (vol->offset == (hammer2_off_t)-1) {
256 hprintf("%s has bad offset 0x%016jx\n", path,
257 (intmax_t)vol->offset);
260 if (vol->size == (hammer2_off_t)-1) {
261 hprintf("%s has bad size 0x%016jx\n", path,
262 (intmax_t)vol->size);
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,
281 hammer2_verify_volumes_1(const hammer2_volume_t *volumes,
282 const hammer2_volume_data_t *rootvoldata)
284 const hammer2_volume_t *vol;
289 /* check initialized volume count */
290 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
296 hprintf("only 1 volume supported\n");
300 /* check volume header */
301 if (rootvoldata->volu_id) {
302 hprintf("volume id %d must be 0\n", rootvoldata->volu_id);
305 if (rootvoldata->nvolumes) {
306 hprintf("volume count %d must be 0\n", rootvoldata->nvolumes);
309 if (rootvoldata->total_size) {
310 hprintf("total size 0x%016jx must be 0\n",
311 (intmax_t)rootvoldata->total_size);
314 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
315 off = rootvoldata->volu_loff[i];
317 hprintf("volume offset[%d] 0x%016jx must be 0\n", i,
325 path = vol->dev->path;
327 hprintf("%s has non zero id %d\n", path, vol->id);
331 hprintf("%s has non zero offset 0x%016jx\n", path,
332 (intmax_t)vol->offset);
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);
345 hammer2_verify_volumes_2(const hammer2_volume_t *volumes,
346 const hammer2_volume_data_t *rootvoldata)
348 const hammer2_volume_t *vol;
349 hammer2_off_t off, total_size = 0;
353 /* check initialized volume count */
354 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
358 total_size += vol->size;
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);
368 if (rootvoldata->nvolumes != nvolumes) {
369 hprintf("volume header requires %d devices, %d specified\n",
370 rootvoldata->nvolumes, nvolumes);
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);
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",
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",
396 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
400 path = vol->dev->path;
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);
408 /* check vs previous volume */
410 if (vol->id <= (vol-1)->id) {
411 hprintf("%s has inconsistent id %d\n", path,
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);
422 hprintf("%s has non zero offset 0x%016jx\n",
423 path, (intmax_t)vol->offset);
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);
434 if (vol->size & HAMMER2_FREEMAP_LEVEL1_MASK) {
435 hprintf("%s's size is not 0x%016jx aligned\n",
437 (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE);
441 if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) {
442 hprintf("%s's size is not 0x%016jx aligned\n",
444 (intmax_t)HAMMER2_VOLUME_ALIGN);
454 hammer2_verify_volumes(const hammer2_volume_t *volumes,
455 const hammer2_volume_data_t *rootvoldata)
459 error = hammer2_verify_volumes_common(volumes);
463 if (rootvoldata->version >= HAMMER2_VOL_VERSION_MULTI_VOLUMES)
464 return hammer2_verify_volumes_2(volumes, rootvoldata);
466 return hammer2_verify_volumes_1(volumes, rootvoldata);
470 * Returns zone# of returned volume header or < 0 on failure.
473 hammer2_read_volume_header(struct vnode *devvp, const char *path,
474 hammer2_volume_data_t *voldata)
476 hammer2_volume_data_t *vd;
477 struct buf *bp = NULL;
478 hammer2_crc32_t crc0, crc1;
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.
489 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
490 if (bread(devvp, i * HAMMER2_ZONE_BYTES64, HAMMER2_VOLUME_BYTES,
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);
507 if (vd->magic == HAMMER2_VOLUME_ID_ABO) {
508 /* XXX: Reversed-endianness filesystem */
509 hprintf("%s #%d: reverse-endian filesystem detected\n",
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);
521 hprintf("%s #%d: volume header crc mismatch sect0 %08x/%08x\n",
522 path, i, crc0, crc1);
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);
531 hprintf("%s #%d: volume header crc mismatch sect1 %08x/%08x\n",
532 path, i, crc0, crc1);
537 crc0 = vd->icrc_volheader;
538 crc1 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRCVH_OFF,
539 HAMMER2_VOLUME_ICRCVH_SIZE);
541 hprintf("%s #%d: volume header crc mismatch vh %08x/%08x\n",
542 path, i, crc0, crc1);
548 if (zone == -1 || voldata->mirror_tid < vd->mirror_tid) {
557 hprintf("%s has no valid volume headers\n", path);
564 hammer2_print_uuid_mismatch(uuid_t *uuid1, uuid_t *uuid2, const char *id)
566 char buf1[64], buf2[64];
568 snprintf_uuid(buf1, sizeof(buf1), uuid1);
569 snprintf_uuid(buf2, sizeof(buf2), uuid2);
571 hprintf("%s uuid mismatch %s vs %s\n", id, buf1, buf2);
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 struct vnode **rootvoldevvp)
581 hammer2_volume_data_t *voldata;
582 hammer2_volume_t *vol;
586 int i, zone, error = 0, version = -1, nvolumes = 0;
588 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
592 vol->offset = (hammer2_off_t)-1;
593 vol->size = (hammer2_off_t)-1;
596 voldata = kmalloc(sizeof(*voldata), M_HAMMER2, M_WAITOK | M_ZERO);
597 bzero(&fsid, sizeof(fsid));
598 bzero(&fstype, sizeof(fstype));
599 bzero(rootvoldata, sizeof(*rootvoldata));
601 TAILQ_FOREACH(e, devvpl, entry) {
606 /* returns negative error or positive zone# */
607 error = hammer2_read_volume_header(devvp, path, voldata);
609 hprintf("failed to read %s's volume header\n", path);
614 error = 0; /* reset error */
616 if (voldata->volu_id >= HAMMER2_MAX_VOLUMES) {
617 hprintf("%s has bad volume id %d\n", path,
622 vol = &volumes[voldata->volu_id];
624 hprintf("%s already initialized\n", path);
628 /* all headers must have the same version, nvolumes and uuid */
630 version = voldata->version;
631 nvolumes = voldata->nvolumes;
632 fsid = voldata->fsid;
633 fstype = voldata->fstype;
635 if (version != (int)voldata->version) {
636 hprintf("volume version mismatch %d vs %d\n",
637 version, (int)voldata->version);
641 if (nvolumes != voldata->nvolumes) {
642 hprintf("volume count mismatch %d vs %d\n",
643 nvolumes, voldata->nvolumes);
647 if (bcmp(&fsid, &voldata->fsid, sizeof(fsid))) {
648 hammer2_print_uuid_mismatch(&fsid,
649 &voldata->fsid, "fsid");
653 if (bcmp(&fstype, &voldata->fstype, sizeof(fstype))) {
654 hammer2_print_uuid_mismatch(&fstype,
655 &voldata->fstype, "fstype");
660 if (version < HAMMER2_VOL_VERSION_MIN ||
661 version > HAMMER2_VOL_VERSION_WIP) {
662 hprintf("bad volume version %d\n", version);
666 /* all per-volume tests passed */
668 vol->id = voldata->volu_id;
669 vol->offset = voldata->volu_loff[vol->id];
670 vol->size = voldata->volu_size;
671 if (vol->id == HAMMER2_ROOT_VOLUME) {
672 bcopy(voldata, rootvoldata, sizeof(*rootvoldata));
673 KKASSERT(*rootvoldevvp == NULL);
674 *rootvoldevvp = e->devvp;
676 devvp->v_rdev->si_mountpoint = mp;
677 hprintf("\"%s\" zone=%d id=%d offset=0x%016jx size=0x%016jx\n",
678 path, zone, vol->id, (intmax_t)vol->offset,
679 (intmax_t)vol->size);
683 error = hammer2_verify_volumes(volumes, rootvoldata);
684 kfree(voldata, M_HAMMER2);
690 hammer2_get_volume(hammer2_dev_t *hmp, hammer2_off_t offset)
692 hammer2_volume_t *vol, *ret = NULL;
695 offset &= ~HAMMER2_OFF_MASK_RADIX;
697 /* locking is unneeded until volume-add support */
698 //hammer2_voldata_lock(hmp);
699 /* do binary search if users really use this many supported volumes */
700 for (i = 0; i < hmp->nvolumes; ++i) {
701 vol = &hmp->volumes[i];
702 if ((offset >= vol->offset) &&
703 (offset < vol->offset + vol->size)) {
708 //hammer2_voldata_unlock(hmp);
711 panic("no volume for offset 0x%016jx", (intmax_t)offset);
715 KKASSERT(ret->dev->devvp);
716 KKASSERT(ret->dev->path);