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,
116 (ronly ? FREAD : FREAD | FWRITE),
121 hprintf("failed to open %s %d\n", path, error);
133 hammer2_close_devvp(const hammer2_devvp_list_t *devvpl, int ronly)
138 TAILQ_FOREACH(e, devvpl, entry) {
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);
154 hammer2_init_devvp(const char *blkdevs, int rootmount,
155 hammer2_devvp_list_t *devvpl)
163 KKASSERT(TAILQ_EMPTY(devvpl));
164 KKASSERT(blkdevs); /* could be empty string */
167 path = objcache_get(namei_oc, M_WAITOK);
171 strcpy(path, "/dev/"); /* relative path */
173 /* scan beyond "/dev/" */
174 for (i = strlen(path); i < MAXPATHLEN-1; ++i) {
177 } else if (*p == ':') {
186 /* path shorter than "/dev/" means invalid or done */
187 if (strlen(path) <= strlen("/dev/")) {
189 hprintf("ignore incomplete path %s\n", path);
193 KKASSERT(*p == '\0');
197 /* lookup path from above */
198 KKASSERT(strncmp(path, "/dev/", 5) == 0);
200 error = hammer2_lookup_device(path, rootmount, &devvp);
203 hprintf("failed to lookup %s %d\n", path, error);
207 e = kmalloc(sizeof(*e), M_HAMMER2, M_WAITOK | M_ZERO);
209 e->path = kstrdup(path, M_HAMMER2);
210 TAILQ_INSERT_TAIL(devvpl, e, entry);
212 objcache_put(namei_oc, path);
218 hammer2_cleanup_devvp(hammer2_devvp_list_t *devvpl)
222 while (!TAILQ_EMPTY(devvpl)) {
223 e = TAILQ_FIRST(devvpl);
224 TAILQ_REMOVE(devvpl, e, entry);
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,
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_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)
571 hammer2_volume_data_t *voldata;
572 hammer2_volume_t *vol;
576 int i, zone, error = 0, version = -1, nvolumes = 0;
578 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
582 vol->offset = (hammer2_off_t)-1;
583 vol->size = (hammer2_off_t)-1;
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));
591 TAILQ_FOREACH(e, devvpl, entry) {
596 /* returns negative error or positive zone# */
597 error = hammer2_read_volume_header(devvp, path, voldata);
599 hprintf("failed to read %s's volume header\n", path);
604 error = 0; /* reset error */
606 if (voldata->volu_id >= HAMMER2_MAX_VOLUMES) {
607 hprintf("%s has bad volume id %d\n", path,
612 vol = &volumes[voldata->volu_id];
614 hprintf("%s already initialized\n", path);
618 /* all headers must have the same version, nvolumes and uuid */
620 version = voldata->version;
621 nvolumes = voldata->nvolumes;
622 fsid = voldata->fsid;
623 fstype = voldata->fstype;
625 if (version != (int)voldata->version) {
626 hprintf("volume version mismatch %d vs %d\n",
627 version, (int)voldata->version);
631 if (nvolumes != voldata->nvolumes) {
632 hprintf("volume count mismatch %d vs %d\n",
633 nvolumes, voldata->nvolumes);
637 if (bcmp(&fsid, &voldata->fsid, sizeof(fsid))) {
638 hprintf("fsid uuid mismatch\n");
642 if (bcmp(&fstype, &voldata->fstype, sizeof(fstype))) {
643 hprintf("fstype uuid mismatch\n");
648 if (version < HAMMER2_VOL_VERSION_MIN ||
649 version > HAMMER2_VOL_VERSION_WIP) {
650 hprintf("bad volume version %d\n", version);
654 /* all per-volume tests passed */
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;
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);
671 error = hammer2_verify_volumes(volumes, rootvoldata);
672 kfree(voldata, M_HAMMER2);
678 hammer2_get_volume(hammer2_dev_t *hmp, hammer2_off_t offset)
680 hammer2_volume_t *vol, *ret = NULL;
683 offset &= ~HAMMER2_OFF_MASK_RADIX;
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)) {
696 //hammer2_voldata_unlock(hmp);
699 panic("no volume for offset 0x%016jx", (intmax_t)offset);
703 KKASSERT(ret->dev->devvp);
704 KKASSERT(ret->dev->path);