1 /*- 2 * Copyright (c) 2017-2019 The DragonFly Project 3 * Copyright (c) 2017-2019 Tomohiro Kusumi <tkusumi@netbsd.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <stdbool.h> 31 #include <string.h> 32 #include <err.h> 33 #include <assert.h> 34 #include <uuid.h> 35 #include <vfs/hammer2/hammer2_disk.h> 36 37 #include "fstyp.h" 38 39 static ssize_t 40 get_file_size(FILE *fp) 41 { 42 ssize_t siz; 43 44 if (fseek(fp, 0, SEEK_END) == -1) { 45 warnx("hammer2: failed to seek media end"); 46 return (-1); 47 } 48 49 siz = ftell(fp); 50 if (siz == -1) { 51 warnx("hammer2: failed to tell media end"); 52 return (-1); 53 } 54 55 return (siz); 56 } 57 58 static hammer2_volume_data_t * 59 read_voldata(FILE *fp, int i) 60 { 61 if (i < 0 || i >= HAMMER2_NUM_VOLHDRS) 62 return (NULL); 63 64 if (i * HAMMER2_ZONE_BYTES64 >= get_file_size(fp)) 65 return (NULL); 66 67 return (read_buf(fp, i * HAMMER2_ZONE_BYTES64, 68 sizeof(hammer2_volume_data_t))); 69 } 70 71 static int 72 test_voldata(FILE *fp) 73 { 74 hammer2_volume_data_t *voldata; 75 int i; 76 static int count = 0; 77 static uuid_t fsid, fstype; 78 79 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) { 80 if (i * HAMMER2_ZONE_BYTES64 >= get_file_size(fp)) 81 break; 82 voldata = read_voldata(fp, i); 83 if (voldata == NULL) { 84 warnx("hammer2: failed to read volume data"); 85 return (1); 86 } 87 if (voldata->magic != HAMMER2_VOLUME_ID_HBO && 88 voldata->magic != HAMMER2_VOLUME_ID_ABO) { 89 free(voldata); 90 return (1); 91 } 92 if (voldata->volu_id > HAMMER2_MAX_VOLUMES - 1) { 93 free(voldata); 94 return (1); 95 } 96 if (voldata->nvolumes > HAMMER2_MAX_VOLUMES) { 97 free(voldata); 98 return (1); 99 } 100 101 if (count == 0) { 102 count = voldata->nvolumes; 103 memcpy(&fsid, &voldata->fsid, sizeof(fsid)); 104 memcpy(&fstype, &voldata->fstype, sizeof(fstype)); 105 } else { 106 if (voldata->nvolumes != count) { 107 free(voldata); 108 return (1); 109 } 110 if (!uuid_equal(&fsid, &voldata->fsid, NULL)) { 111 free(voldata); 112 return (1); 113 } 114 if (!uuid_equal(&fstype, &voldata->fstype, NULL)) { 115 free(voldata); 116 return (1); 117 } 118 } 119 free(voldata); 120 } 121 122 return (0); 123 } 124 125 static hammer2_media_data_t* 126 read_media(FILE *fp, const hammer2_blockref_t *bref, size_t *media_bytes) 127 { 128 hammer2_media_data_t *media; 129 hammer2_off_t io_off, io_base; 130 size_t bytes, io_bytes, boff, fbytes; 131 132 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 133 if (bytes) 134 bytes = (size_t)1 << bytes; 135 *media_bytes = bytes; 136 137 if (!bytes) { 138 warnx("hammer2: blockref has no data"); 139 return (NULL); 140 } 141 142 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 143 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1); 144 boff = io_off - io_base; 145 146 io_bytes = HAMMER2_LBUFSIZE; 147 while (io_bytes + boff < bytes) 148 io_bytes <<= 1; 149 150 if (io_bytes > sizeof(hammer2_media_data_t)) { 151 warnx("hammer2: invalid I/O bytes"); 152 return (NULL); 153 } 154 155 /* 156 * XXX fp is currently always root volume, so read fails if io_base is 157 * beyond root volume limit. Fail with a message before read_buf() then. 158 */ 159 fbytes = get_file_size(fp); 160 if (fbytes == -1) { 161 warnx("hammer2: failed to get media size"); 162 return (NULL); 163 } 164 if (io_base >= fbytes) { 165 warnx("hammer2: XXX read beyond HAMMER2 root volume limit unsupported"); 166 return (NULL); 167 } 168 169 if (fseek(fp, io_base, SEEK_SET) == -1) { 170 warnx("hammer2: failed to seek media"); 171 return (NULL); 172 } 173 media = read_buf(fp, io_base, io_bytes); 174 if (media == NULL) { 175 warnx("hammer2: failed to read media"); 176 return (NULL); 177 } 178 if (boff) 179 memcpy(media, (char *)media + boff, bytes); 180 181 return (media); 182 } 183 184 static int 185 find_pfs(FILE *fp, const hammer2_blockref_t *bref, const char *pfs, bool *res) 186 { 187 hammer2_media_data_t *media; 188 hammer2_inode_data_t ipdata; 189 hammer2_blockref_t *bscan; 190 size_t bytes; 191 int i, bcount; 192 193 media = read_media(fp, bref, &bytes); 194 if (media == NULL) 195 return (-1); 196 197 switch (bref->type) { 198 case HAMMER2_BREF_TYPE_INODE: 199 ipdata = media->ipdata; 200 if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) { 201 bscan = &ipdata.u.blockset.blockref[0]; 202 bcount = HAMMER2_SET_COUNT; 203 } else { 204 bscan = NULL; 205 bcount = 0; 206 if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) { 207 if (memchr(ipdata.filename, 0, 208 sizeof(ipdata.filename))) { 209 if (!strcmp( 210 (const char*)ipdata.filename, pfs)) 211 *res = true; 212 } else { 213 if (strlen(pfs) > 0 && 214 !memcmp(ipdata.filename, pfs, 215 strlen(pfs))) 216 *res = true; 217 } 218 } else { 219 free(media); 220 return (-1); 221 } 222 } 223 break; 224 case HAMMER2_BREF_TYPE_INDIRECT: 225 bscan = &media->npdata[0]; 226 bcount = bytes / sizeof(hammer2_blockref_t); 227 break; 228 default: 229 bscan = NULL; 230 bcount = 0; 231 break; 232 } 233 234 for (i = 0; i < bcount; ++i) { 235 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) { 236 if (find_pfs(fp, &bscan[i], pfs, res) == -1) { 237 free(media); 238 return (-1); 239 } 240 } 241 } 242 free(media); 243 244 return (0); 245 } 246 247 static char* 248 extract_device_name(const char *devpath) 249 { 250 char *p, *head; 251 252 if (!devpath) 253 return (NULL); 254 255 p = strdup(devpath); 256 head = p; 257 258 p = strchr(p, '@'); 259 if (p) 260 *p = 0; 261 262 p = strrchr(head, '/'); 263 if (p) { 264 p++; 265 if (*p == 0) { 266 free(head); 267 return (NULL); 268 } 269 p = strdup(p); 270 free(head); 271 return (p); 272 } 273 274 return (head); 275 } 276 277 static int 278 read_label(FILE *fp, char *label, size_t size, const char *devpath) 279 { 280 hammer2_blockref_t broot, best, *bref; 281 hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media; 282 size_t bytes; 283 bool res = false; 284 int i, best_i, error = 1; 285 const char *pfs; 286 char *devname; 287 288 best_i = -1; 289 memset(vols, 0, sizeof(vols)); 290 memset(&best, 0, sizeof(best)); 291 292 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) { 293 if (i * HAMMER2_ZONE_BYTES64 >= get_file_size(fp)) 294 break; 295 memset(&broot, 0, sizeof(broot)); 296 broot.type = HAMMER2_BREF_TYPE_VOLUME; 297 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 298 vols[i] = (void*)read_voldata(fp, i); 299 if (vols[i] == NULL) { 300 warnx("hammer2: failed to read volume data"); 301 goto fail; 302 } 303 broot.mirror_tid = vols[i]->voldata.mirror_tid; 304 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 305 best_i = i; 306 best = broot; 307 } 308 } 309 310 bref = &vols[best_i]->voldata.sroot_blockset.blockref[0]; 311 if (bref->type != HAMMER2_BREF_TYPE_INODE) { 312 /* Don't print error as devpath could be non-root volume. */ 313 goto fail; 314 } 315 316 media = read_media(fp, bref, &bytes); 317 if (media == NULL) { 318 goto fail; 319 } 320 321 /* 322 * fstyp_function in DragonFly takes an additional devpath argument 323 * which doesn't exist in FreeBSD and NetBSD. 324 */ 325 #ifdef HAS_DEVPATH 326 pfs = strchr(devpath, '@'); 327 if (!pfs) { 328 assert(strlen(devpath)); 329 switch (devpath[strlen(devpath) - 1]) { 330 case 'a': 331 pfs = "BOOT"; 332 break; 333 case 'd': 334 pfs = "ROOT"; 335 break; 336 default: 337 pfs = "DATA"; 338 break; 339 } 340 } else 341 pfs++; 342 343 if (strlen(pfs) > HAMMER2_INODE_MAXNAME) { 344 goto fail; 345 } 346 devname = extract_device_name(devpath); 347 #else 348 pfs = ""; 349 devname = extract_device_name(NULL); 350 assert(!devname); 351 #endif 352 353 /* Add device name to help support multiple autofs -media mounts. */ 354 if (find_pfs(fp, bref, pfs, &res) == 0 && res) { 355 if (devname) 356 snprintf(label, size, "%s_%s", pfs, devname); 357 else 358 strlcpy(label, pfs, size); 359 } else { 360 memset(label, 0, size); 361 memcpy(label, media->ipdata.filename, 362 sizeof(media->ipdata.filename)); 363 if (devname) { 364 strlcat(label, "_", size); 365 strlcat(label, devname, size); 366 } 367 } 368 if (devname) 369 free(devname); 370 free(media); 371 error = 0; 372 fail: 373 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) 374 free(vols[i]); 375 376 return (error); 377 } 378 379 int 380 fstyp_hammer2(FILE *fp, char *label, size_t size, const char *devpath) 381 { 382 hammer2_volume_data_t *voldata = read_voldata(fp, 0); 383 int error = 1; 384 385 if (voldata->volu_id != HAMMER2_ROOT_VOLUME) 386 goto fail; 387 if (voldata->nvolumes != 0) 388 goto fail; 389 if (test_voldata(fp)) 390 goto fail; 391 392 error = read_label(fp, label, size, devpath); 393 fail: 394 free(voldata); 395 return (error); 396 } 397 398 static int 399 __fsvtyp_hammer2(const char *blkdevs, char *label, size_t size, int partial) 400 { 401 hammer2_volume_data_t *voldata = NULL; 402 FILE *fp = NULL; 403 char *dup = NULL, *target_label = NULL, *p, *volpath, *rootvolpath; 404 char x[HAMMER2_MAX_VOLUMES]; 405 int i, volid, error = 1; 406 407 if (!blkdevs) 408 goto fail; 409 410 memset(x, 0, sizeof(x)); 411 p = dup = strdup(blkdevs); 412 if ((p = strchr(p, '@')) != NULL) { 413 *p++ = '\0'; 414 target_label = p; 415 } 416 p = dup; 417 418 volpath = NULL; 419 rootvolpath = NULL; 420 volid = -1; 421 while (p) { 422 volpath = p; 423 if ((p = strchr(p, ':')) != NULL) 424 *p++ = '\0'; 425 if ((fp = fopen(volpath, "r")) == NULL) { 426 warnx("hammer2: failed to open %s", volpath); 427 goto fail; 428 } 429 if (test_voldata(fp)) 430 break; 431 voldata = read_voldata(fp, 0); 432 fclose(fp); 433 if (voldata == NULL) { 434 warnx("hammer2: failed to read volume data"); 435 goto fail; 436 } 437 volid = voldata->volu_id; 438 free(voldata); 439 voldata = NULL; 440 if (volid < 0 || volid >= HAMMER2_MAX_VOLUMES) 441 goto fail; 442 x[volid]++; 443 if (volid == HAMMER2_ROOT_VOLUME) 444 rootvolpath = volpath; 445 } 446 447 /* If no rootvolpath, proceed only if partial mode with volpath. */ 448 if (rootvolpath) 449 volpath = rootvolpath; 450 else if (!partial || !volpath) 451 goto fail; 452 if ((fp = fopen(volpath, "r")) == NULL) { 453 warnx("hammer2: failed to open %s", volpath); 454 goto fail; 455 } 456 voldata = read_voldata(fp, 0); 457 if (voldata == NULL) { 458 warnx("hammer2: failed to read volume data"); 459 goto fail; 460 } 461 462 if (volid == -1) 463 goto fail; 464 if (partial) 465 goto success; 466 467 for (i = 0; i < HAMMER2_MAX_VOLUMES; i++) 468 if (x[i] > 1) 469 goto fail; 470 for (i = 0; i < HAMMER2_MAX_VOLUMES; i++) 471 if (x[i] == 0) 472 break; 473 if (voldata->nvolumes != i) 474 goto fail; 475 for (; i < HAMMER2_MAX_VOLUMES; i++) 476 if (x[i] != 0) 477 goto fail; 478 success: 479 /* Reconstruct @label format path using only root volume. */ 480 if (target_label) { 481 size_t siz = strlen(volpath) + strlen(target_label) + 2; 482 p = calloc(1, siz); 483 snprintf(p, siz, "%s@%s", volpath, target_label); 484 volpath = p; 485 } 486 error = read_label(fp, label, size, volpath); 487 if (target_label) 488 free(p); 489 /* If in partial mode, read label but ignore error. */ 490 if (partial) 491 error = 0; 492 fail: 493 if (fp) 494 fclose(fp); 495 free(voldata); 496 free(dup); 497 return (error); 498 } 499 500 int 501 fsvtyp_hammer2(const char *blkdevs, char *label, size_t size) 502 { 503 return (__fsvtyp_hammer2(blkdevs, label, size, 0)); 504 } 505 506 int 507 fsvtyp_hammer2_partial(const char *blkdevs, char *label, size_t size) 508 { 509 return (__fsvtyp_hammer2(blkdevs, label, size, 1)); 510 } 511