1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2017-2019 The DragonFly Project 5 * Copyright (c) 2017-2019 Tomohiro Kusumi <tkusumi@netbsd.org> 6 * All rights reserved. 7 * 8 * This software was developed by Edward Tomasz Napierala under sponsorship 9 * from the FreeBSD Foundation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <stdbool.h> 37 #include <string.h> 38 #include <err.h> 39 #include <assert.h> 40 41 #include <sys/types.h> 42 43 #include "hammer2_disk.h" 44 45 #include "fstyp.h" 46 47 static hammer2_volume_data_t* 48 read_voldata(FILE *fp) 49 { 50 hammer2_volume_data_t *voldata; 51 52 voldata = read_buf(fp, 0, sizeof(*voldata)); 53 if (voldata == NULL) 54 err(1, "failed to read volume data"); 55 56 return (voldata); 57 } 58 59 static int 60 test_voldata(const hammer2_volume_data_t *voldata) 61 { 62 if (voldata->magic != HAMMER2_VOLUME_ID_HBO && 63 voldata->magic != HAMMER2_VOLUME_ID_ABO) 64 return (1); 65 66 return (0); 67 } 68 69 static hammer2_media_data_t* 70 read_media(FILE *fp, const hammer2_blockref_t *bref, size_t *media_bytes) 71 { 72 hammer2_media_data_t *media; 73 hammer2_off_t io_off, io_base; 74 size_t bytes, io_bytes, boff; 75 76 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 77 if (bytes) 78 bytes = (size_t)1 << bytes; 79 *media_bytes = bytes; 80 81 if (!bytes) { 82 warnx("blockref has no data"); 83 return (NULL); 84 } 85 86 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 87 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 88 boff = io_off - io_base; 89 90 io_bytes = HAMMER2_MINIOSIZE; 91 while (io_bytes + boff < bytes) 92 io_bytes <<= 1; 93 94 if (io_bytes > sizeof(hammer2_media_data_t)) { 95 warnx("invalid I/O bytes"); 96 return (NULL); 97 } 98 99 if (fseek(fp, io_base, SEEK_SET) == -1) { 100 warnx("failed to seek media"); 101 return (NULL); 102 } 103 media = read_buf(fp, io_base, io_bytes); 104 if (media == NULL) { 105 warnx("failed to read media"); 106 return (NULL); 107 } 108 if (boff) 109 memcpy(media, (char *)media + boff, bytes); 110 111 return (media); 112 } 113 114 static int 115 find_pfs(FILE *fp, const hammer2_blockref_t *bref, const char *pfs, bool *res) 116 { 117 hammer2_media_data_t *media; 118 hammer2_inode_data_t ipdata; 119 hammer2_blockref_t *bscan; 120 size_t bytes; 121 int i, bcount; 122 123 media = read_media(fp, bref, &bytes); 124 if (media == NULL) 125 return (-1); 126 127 switch (bref->type) { 128 case HAMMER2_BREF_TYPE_INODE: 129 ipdata = media->ipdata; 130 if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) { 131 bscan = &ipdata.u.blockset.blockref[0]; 132 bcount = HAMMER2_SET_COUNT; 133 } else { 134 bscan = NULL; 135 bcount = 0; 136 if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) { 137 if (memchr(ipdata.filename, 0, 138 sizeof(ipdata.filename))) { 139 if (!strcmp( 140 (const char*)ipdata.filename, pfs)) 141 *res = true; 142 } else { 143 if (strlen(pfs) > 0 && 144 !memcmp(ipdata.filename, pfs, 145 strlen(pfs))) 146 *res = true; 147 } 148 } else 149 assert(0); 150 } 151 break; 152 case HAMMER2_BREF_TYPE_INDIRECT: 153 bscan = &media->npdata[0]; 154 bcount = bytes / sizeof(hammer2_blockref_t); 155 break; 156 default: 157 bscan = NULL; 158 bcount = 0; 159 break; 160 } 161 162 for (i = 0; i < bcount; ++i) { 163 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) { 164 if (find_pfs(fp, &bscan[i], pfs, res) == -1) { 165 free(media); 166 return (-1); 167 } 168 } 169 } 170 free(media); 171 172 return (0); 173 } 174 175 static char* 176 extract_device_name(const char *devpath) 177 { 178 char *p, *head; 179 180 if (!devpath) 181 return NULL; 182 183 p = strdup(devpath); 184 head = p; 185 186 p = strchr(p, '@'); 187 if (p) 188 *p = 0; 189 190 p = strrchr(head, '/'); 191 if (p) { 192 p++; 193 if (*p == 0) { 194 free(head); 195 return NULL; 196 } 197 p = strdup(p); 198 free(head); 199 return p; 200 } 201 202 return head; 203 } 204 205 static int 206 read_label(FILE *fp, char *label, size_t size) 207 { 208 hammer2_blockref_t broot, best, *bref; 209 hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media; 210 size_t bytes; 211 bool res = false; 212 int i, best_i, error = 1; 213 const char *pfs; 214 char *devname; 215 216 best_i = -1; 217 memset(&best, 0, sizeof(best)); 218 219 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) { 220 memset(&broot, 0, sizeof(broot)); 221 broot.type = HAMMER2_BREF_TYPE_VOLUME; 222 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 223 vols[i] = read_buf(fp, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 224 sizeof(*vols[i])); 225 if (vols[i] == NULL) 226 errx(1, "failed to read volume header"); 227 broot.mirror_tid = vols[i]->voldata.mirror_tid; 228 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 229 best_i = i; 230 best = broot; 231 } 232 } 233 234 bref = &vols[best_i]->voldata.sroot_blockset.blockref[0]; 235 if (bref->type != HAMMER2_BREF_TYPE_INODE) { 236 warnx("blockref type is not inode"); 237 goto fail; 238 } 239 240 media = read_media(fp, bref, &bytes); 241 if (media == NULL) { 242 goto fail; 243 } 244 245 pfs = ""; 246 devname = extract_device_name(NULL); 247 assert(!devname); /* Currently always NULL in FreeBSD. */ 248 249 /* Add device name to help support multiple autofs -media mounts. */ 250 if (find_pfs(fp, bref, pfs, &res) == 0 && res) { 251 if (devname) 252 snprintf(label, size, "%s_%s", pfs, devname); 253 else 254 strlcpy(label, pfs, size); 255 } else { 256 memset(label, 0, size); 257 memcpy(label, media->ipdata.filename, 258 sizeof(media->ipdata.filename)); 259 if (devname) { 260 strlcat(label, "_", size); 261 strlcat(label, devname, size); 262 } 263 } 264 if (devname) 265 free(devname); 266 free(media); 267 error = 0; 268 fail: 269 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) 270 free(vols[i]); 271 272 return (error); 273 } 274 275 int 276 fstyp_hammer2(FILE *fp, char *label, size_t size) 277 { 278 hammer2_volume_data_t *voldata; 279 int error = 1; 280 281 voldata = read_voldata(fp); 282 if (test_voldata(voldata)) 283 goto fail; 284 285 error = read_label(fp, label, size); 286 fail: 287 free(voldata); 288 return (error); 289 } 290