1509798eaSPedro F. Giffuni /*- 2509798eaSPedro F. Giffuni * Copyright (c) 2017-2019 The DragonFly Project 39d529ed8SPedro F. Giffuni * Copyright (c) 2017-2019 Tomohiro Kusumi <tkusumi@netbsd.org> 4509798eaSPedro F. Giffuni * All rights reserved. 5509798eaSPedro F. Giffuni * 6509798eaSPedro F. Giffuni * This software was developed by Edward Tomasz Napierala under sponsorship 7509798eaSPedro F. Giffuni * from the FreeBSD Foundation. 8509798eaSPedro F. Giffuni * 9509798eaSPedro F. Giffuni * Redistribution and use in source and binary forms, with or without 10509798eaSPedro F. Giffuni * modification, are permitted provided that the following conditions 11509798eaSPedro F. Giffuni * are met: 12509798eaSPedro F. Giffuni * 1. Redistributions of source code must retain the above copyright 13509798eaSPedro F. Giffuni * notice, this list of conditions and the following disclaimer. 14509798eaSPedro F. Giffuni * 2. Redistributions in binary form must reproduce the above copyright 15509798eaSPedro F. Giffuni * notice, this list of conditions and the following disclaimer in the 16509798eaSPedro F. Giffuni * documentation and/or other materials provided with the distribution. 17509798eaSPedro F. Giffuni * 18509798eaSPedro F. Giffuni * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19509798eaSPedro F. Giffuni * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20509798eaSPedro F. Giffuni * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21509798eaSPedro F. Giffuni * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22509798eaSPedro F. Giffuni * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23509798eaSPedro F. Giffuni * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24509798eaSPedro F. Giffuni * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25509798eaSPedro F. Giffuni * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26509798eaSPedro F. Giffuni * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27509798eaSPedro F. Giffuni * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28509798eaSPedro F. Giffuni * SUCH DAMAGE. 29509798eaSPedro F. Giffuni */ 30509798eaSPedro F. Giffuni 31509798eaSPedro F. Giffuni #include <sys/cdefs.h> 32509798eaSPedro F. Giffuni #include <stdio.h> 33509798eaSPedro F. Giffuni #include <stdlib.h> 349d529ed8SPedro F. Giffuni #include <stdbool.h> 35509798eaSPedro F. Giffuni #include <string.h> 36509798eaSPedro F. Giffuni #include <err.h> 379d529ed8SPedro F. Giffuni #include <assert.h> 38509798eaSPedro F. Giffuni 39509798eaSPedro F. Giffuni #include <sys/types.h> 40509798eaSPedro F. Giffuni 41509798eaSPedro F. Giffuni #include "hammer2_disk.h" 42509798eaSPedro F. Giffuni 43509798eaSPedro F. Giffuni #include "fstyp.h" 44509798eaSPedro F. Giffuni 45509798eaSPedro F. Giffuni static hammer2_volume_data_t* 469d529ed8SPedro F. Giffuni read_voldata(FILE *fp) 47509798eaSPedro F. Giffuni { 48509798eaSPedro F. Giffuni hammer2_volume_data_t *voldata; 49509798eaSPedro F. Giffuni 50509798eaSPedro F. Giffuni voldata = read_buf(fp, 0, sizeof(*voldata)); 51509798eaSPedro F. Giffuni if (voldata == NULL) 52509798eaSPedro F. Giffuni err(1, "failed to read volume data"); 53509798eaSPedro F. Giffuni 54509798eaSPedro F. Giffuni return (voldata); 55509798eaSPedro F. Giffuni } 56509798eaSPedro F. Giffuni 57509798eaSPedro F. Giffuni static int 589d529ed8SPedro F. Giffuni test_voldata(const hammer2_volume_data_t *voldata) 59509798eaSPedro F. Giffuni { 60509798eaSPedro F. Giffuni if (voldata->magic != HAMMER2_VOLUME_ID_HBO && 61509798eaSPedro F. Giffuni voldata->magic != HAMMER2_VOLUME_ID_ABO) 62509798eaSPedro F. Giffuni return (1); 63509798eaSPedro F. Giffuni 64509798eaSPedro F. Giffuni return (0); 65509798eaSPedro F. Giffuni } 66509798eaSPedro F. Giffuni 679d529ed8SPedro F. Giffuni static hammer2_media_data_t* 689d529ed8SPedro F. Giffuni read_media(FILE *fp, const hammer2_blockref_t *bref, size_t *media_bytes) 699d529ed8SPedro F. Giffuni { 709d529ed8SPedro F. Giffuni hammer2_media_data_t *media; 719d529ed8SPedro F. Giffuni hammer2_off_t io_off, io_base; 729d529ed8SPedro F. Giffuni size_t bytes, io_bytes, boff; 739d529ed8SPedro F. Giffuni 749d529ed8SPedro F. Giffuni bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 759d529ed8SPedro F. Giffuni if (bytes) 769d529ed8SPedro F. Giffuni bytes = (size_t)1 << bytes; 779d529ed8SPedro F. Giffuni *media_bytes = bytes; 789d529ed8SPedro F. Giffuni 799d529ed8SPedro F. Giffuni if (!bytes) { 809d529ed8SPedro F. Giffuni warnx("blockref has no data"); 819d529ed8SPedro F. Giffuni return (NULL); 829d529ed8SPedro F. Giffuni } 839d529ed8SPedro F. Giffuni 849d529ed8SPedro F. Giffuni io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 859d529ed8SPedro F. Giffuni io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 869d529ed8SPedro F. Giffuni boff = io_off - io_base; 879d529ed8SPedro F. Giffuni 889d529ed8SPedro F. Giffuni io_bytes = HAMMER2_MINIOSIZE; 899d529ed8SPedro F. Giffuni while (io_bytes + boff < bytes) 909d529ed8SPedro F. Giffuni io_bytes <<= 1; 919d529ed8SPedro F. Giffuni 929d529ed8SPedro F. Giffuni if (io_bytes > sizeof(hammer2_media_data_t)) { 939d529ed8SPedro F. Giffuni warnx("invalid I/O bytes"); 949d529ed8SPedro F. Giffuni return (NULL); 959d529ed8SPedro F. Giffuni } 969d529ed8SPedro F. Giffuni 979d529ed8SPedro F. Giffuni if (fseek(fp, io_base, SEEK_SET) == -1) { 989d529ed8SPedro F. Giffuni warnx("failed to seek media"); 999d529ed8SPedro F. Giffuni return (NULL); 1009d529ed8SPedro F. Giffuni } 1019d529ed8SPedro F. Giffuni media = read_buf(fp, io_base, io_bytes); 1029d529ed8SPedro F. Giffuni if (media == NULL) { 1039d529ed8SPedro F. Giffuni warnx("failed to read media"); 1049d529ed8SPedro F. Giffuni return (NULL); 1059d529ed8SPedro F. Giffuni } 1069d529ed8SPedro F. Giffuni if (boff) 1079d529ed8SPedro F. Giffuni memcpy(media, (char *)media + boff, bytes); 1089d529ed8SPedro F. Giffuni 1099d529ed8SPedro F. Giffuni return (media); 1109d529ed8SPedro F. Giffuni } 1119d529ed8SPedro F. Giffuni 112509798eaSPedro F. Giffuni static int 1139d529ed8SPedro F. Giffuni find_pfs(FILE *fp, const hammer2_blockref_t *bref, const char *pfs, bool *res) 1149d529ed8SPedro F. Giffuni { 1159d529ed8SPedro F. Giffuni hammer2_media_data_t *media; 1169d529ed8SPedro F. Giffuni hammer2_inode_data_t ipdata; 1179d529ed8SPedro F. Giffuni hammer2_blockref_t *bscan; 1189d529ed8SPedro F. Giffuni size_t bytes; 1199d529ed8SPedro F. Giffuni int i, bcount; 1209d529ed8SPedro F. Giffuni 1219d529ed8SPedro F. Giffuni media = read_media(fp, bref, &bytes); 1229d529ed8SPedro F. Giffuni if (media == NULL) 1239d529ed8SPedro F. Giffuni return (-1); 1249d529ed8SPedro F. Giffuni 1259d529ed8SPedro F. Giffuni switch (bref->type) { 1269d529ed8SPedro F. Giffuni case HAMMER2_BREF_TYPE_INODE: 1279d529ed8SPedro F. Giffuni ipdata = media->ipdata; 1280d2913adSPedro F. Giffuni if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) { 1299d529ed8SPedro F. Giffuni bscan = &ipdata.u.blockset.blockref[0]; 1309d529ed8SPedro F. Giffuni bcount = HAMMER2_SET_COUNT; 1319d529ed8SPedro F. Giffuni } else { 1329d529ed8SPedro F. Giffuni bscan = NULL; 1339d529ed8SPedro F. Giffuni bcount = 0; 1349d529ed8SPedro F. Giffuni if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) { 1359d529ed8SPedro F. Giffuni if (memchr(ipdata.filename, 0, 1369d529ed8SPedro F. Giffuni sizeof(ipdata.filename))) { 1379d529ed8SPedro F. Giffuni if (!strcmp( 1389d529ed8SPedro F. Giffuni (const char*)ipdata.filename, pfs)) 1399d529ed8SPedro F. Giffuni *res = true; 1409d529ed8SPedro F. Giffuni } else { 1419d529ed8SPedro F. Giffuni if (strlen(pfs) > 0 && 1429d529ed8SPedro F. Giffuni !memcmp(ipdata.filename, pfs, 1439d529ed8SPedro F. Giffuni strlen(pfs))) 1449d529ed8SPedro F. Giffuni *res = true; 1459d529ed8SPedro F. Giffuni } 1469d529ed8SPedro F. Giffuni } else 1479d529ed8SPedro F. Giffuni assert(0); 1489d529ed8SPedro F. Giffuni } 1499d529ed8SPedro F. Giffuni break; 1509d529ed8SPedro F. Giffuni case HAMMER2_BREF_TYPE_INDIRECT: 1519d529ed8SPedro F. Giffuni bscan = &media->npdata[0]; 1529d529ed8SPedro F. Giffuni bcount = bytes / sizeof(hammer2_blockref_t); 1539d529ed8SPedro F. Giffuni break; 1549d529ed8SPedro F. Giffuni default: 1559d529ed8SPedro F. Giffuni bscan = NULL; 1569d529ed8SPedro F. Giffuni bcount = 0; 1579d529ed8SPedro F. Giffuni break; 1589d529ed8SPedro F. Giffuni } 1599d529ed8SPedro F. Giffuni 1609d529ed8SPedro F. Giffuni for (i = 0; i < bcount; ++i) { 1619d529ed8SPedro F. Giffuni if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) { 1629d529ed8SPedro F. Giffuni if (find_pfs(fp, &bscan[i], pfs, res) == -1) { 1639d529ed8SPedro F. Giffuni free(media); 1649d529ed8SPedro F. Giffuni return (-1); 1659d529ed8SPedro F. Giffuni } 1669d529ed8SPedro F. Giffuni } 1679d529ed8SPedro F. Giffuni } 1689d529ed8SPedro F. Giffuni free(media); 1699d529ed8SPedro F. Giffuni 1709d529ed8SPedro F. Giffuni return (0); 1719d529ed8SPedro F. Giffuni } 1729d529ed8SPedro F. Giffuni 1739d529ed8SPedro F. Giffuni static char* 1749d529ed8SPedro F. Giffuni extract_device_name(const char *devpath) 1759d529ed8SPedro F. Giffuni { 1769d529ed8SPedro F. Giffuni char *p, *head; 1779d529ed8SPedro F. Giffuni 1789d529ed8SPedro F. Giffuni if (!devpath) 1799d529ed8SPedro F. Giffuni return NULL; 1809d529ed8SPedro F. Giffuni 1819d529ed8SPedro F. Giffuni p = strdup(devpath); 1829d529ed8SPedro F. Giffuni head = p; 1839d529ed8SPedro F. Giffuni 1849d529ed8SPedro F. Giffuni p = strchr(p, '@'); 1859d529ed8SPedro F. Giffuni if (p) 1869d529ed8SPedro F. Giffuni *p = 0; 1879d529ed8SPedro F. Giffuni 1889d529ed8SPedro F. Giffuni p = strrchr(head, '/'); 1899d529ed8SPedro F. Giffuni if (p) { 1909d529ed8SPedro F. Giffuni p++; 1919d529ed8SPedro F. Giffuni if (*p == 0) { 1929d529ed8SPedro F. Giffuni free(head); 1939d529ed8SPedro F. Giffuni return NULL; 1949d529ed8SPedro F. Giffuni } 1959d529ed8SPedro F. Giffuni p = strdup(p); 1969d529ed8SPedro F. Giffuni free(head); 1979d529ed8SPedro F. Giffuni return p; 1989d529ed8SPedro F. Giffuni } 1999d529ed8SPedro F. Giffuni 2009d529ed8SPedro F. Giffuni return head; 2019d529ed8SPedro F. Giffuni } 2029d529ed8SPedro F. Giffuni 2039d529ed8SPedro F. Giffuni static int 2049d529ed8SPedro F. Giffuni read_label(FILE *fp, char *label, size_t size) 205509798eaSPedro F. Giffuni { 206509798eaSPedro F. Giffuni hammer2_blockref_t broot, best, *bref; 207509798eaSPedro F. Giffuni hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media; 2089d529ed8SPedro F. Giffuni size_t bytes; 2099d529ed8SPedro F. Giffuni bool res = false; 2109d529ed8SPedro F. Giffuni int i, best_i, error = 1; 2119d529ed8SPedro F. Giffuni const char *pfs; 2129d529ed8SPedro F. Giffuni char *devname; 213509798eaSPedro F. Giffuni 214509798eaSPedro F. Giffuni best_i = -1; 215509798eaSPedro F. Giffuni memset(&best, 0, sizeof(best)); 216509798eaSPedro F. Giffuni 217509798eaSPedro F. Giffuni for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) { 218509798eaSPedro F. Giffuni memset(&broot, 0, sizeof(broot)); 219509798eaSPedro F. Giffuni broot.type = HAMMER2_BREF_TYPE_VOLUME; 220509798eaSPedro F. Giffuni broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 221509798eaSPedro F. Giffuni vols[i] = read_buf(fp, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 222509798eaSPedro F. Giffuni sizeof(*vols[i])); 223*878ede1aSMark Johnston if (vols[i] == NULL) 224*878ede1aSMark Johnston errx(1, "failed to read volume header"); 225509798eaSPedro F. Giffuni broot.mirror_tid = vols[i]->voldata.mirror_tid; 226509798eaSPedro F. Giffuni if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 227509798eaSPedro F. Giffuni best_i = i; 228509798eaSPedro F. Giffuni best = broot; 229509798eaSPedro F. Giffuni } 230509798eaSPedro F. Giffuni } 231509798eaSPedro F. Giffuni 232509798eaSPedro F. Giffuni bref = &vols[best_i]->voldata.sroot_blockset.blockref[0]; 233509798eaSPedro F. Giffuni if (bref->type != HAMMER2_BREF_TYPE_INODE) { 2349d529ed8SPedro F. Giffuni warnx("blockref type is not inode"); 2359d529ed8SPedro F. Giffuni goto fail; 236509798eaSPedro F. Giffuni } 237509798eaSPedro F. Giffuni 2389d529ed8SPedro F. Giffuni media = read_media(fp, bref, &bytes); 2399d529ed8SPedro F. Giffuni if (media == NULL) { 2409d529ed8SPedro F. Giffuni goto fail; 241509798eaSPedro F. Giffuni } 242509798eaSPedro F. Giffuni 2439d529ed8SPedro F. Giffuni pfs = ""; 2449d529ed8SPedro F. Giffuni devname = extract_device_name(NULL); 2459d529ed8SPedro F. Giffuni assert(!devname); /* Currently always NULL in FreeBSD. */ 246509798eaSPedro F. Giffuni 2479d529ed8SPedro F. Giffuni /* Add device name to help support multiple autofs -media mounts. */ 2489d529ed8SPedro F. Giffuni if (find_pfs(fp, bref, pfs, &res) == 0 && res) { 2499d529ed8SPedro F. Giffuni if (devname) 2509d529ed8SPedro F. Giffuni snprintf(label, size, "%s_%s", pfs, devname); 2519d529ed8SPedro F. Giffuni else 2529d529ed8SPedro F. Giffuni strlcpy(label, pfs, size); 2539d529ed8SPedro F. Giffuni } else { 2549d529ed8SPedro F. Giffuni memset(label, 0, size); 2559d529ed8SPedro F. Giffuni memcpy(label, media->ipdata.filename, 2569d529ed8SPedro F. Giffuni sizeof(media->ipdata.filename)); 2579d529ed8SPedro F. Giffuni if (devname) { 2589d529ed8SPedro F. Giffuni strlcat(label, "_", size); 2599d529ed8SPedro F. Giffuni strlcat(label, devname, size); 260509798eaSPedro F. Giffuni } 2619d529ed8SPedro F. Giffuni } 2629d529ed8SPedro F. Giffuni if (devname) 2639d529ed8SPedro F. Giffuni free(devname); 264509798eaSPedro F. Giffuni free(media); 2659d529ed8SPedro F. Giffuni error = 0; 2669d529ed8SPedro F. Giffuni fail: 267509798eaSPedro F. Giffuni for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) 268509798eaSPedro F. Giffuni free(vols[i]); 269509798eaSPedro F. Giffuni 270509798eaSPedro F. Giffuni return (error); 271509798eaSPedro F. Giffuni } 272509798eaSPedro F. Giffuni 273509798eaSPedro F. Giffuni int 274509798eaSPedro F. Giffuni fstyp_hammer2(FILE *fp, char *label, size_t size) 275509798eaSPedro F. Giffuni { 276509798eaSPedro F. Giffuni hammer2_volume_data_t *voldata; 277509798eaSPedro F. Giffuni int error = 1; 278509798eaSPedro F. Giffuni 2799d529ed8SPedro F. Giffuni voldata = read_voldata(fp); 2809d529ed8SPedro F. Giffuni if (test_voldata(voldata)) 2819d529ed8SPedro F. Giffuni goto fail; 282509798eaSPedro F. Giffuni 2839d529ed8SPedro F. Giffuni error = read_label(fp, label, size); 2849d529ed8SPedro F. Giffuni fail: 285509798eaSPedro F. Giffuni free(voldata); 286509798eaSPedro F. Giffuni return (error); 287509798eaSPedro F. Giffuni } 288