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 assert(0); 220 } 221 break; 222 case HAMMER2_BREF_TYPE_INDIRECT: 223 bscan = &media->npdata[0]; 224 bcount = bytes / sizeof(hammer2_blockref_t); 225 break; 226 default: 227 bscan = NULL; 228 bcount = 0; 229 break; 230 } 231 232 for (i = 0; i < bcount; ++i) { 233 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) { 234 if (find_pfs(fp, &bscan[i], pfs, res) == -1) { 235 free(media); 236 return (-1); 237 } 238 } 239 } 240 free(media); 241 242 return (0); 243 } 244 245 static char* 246 extract_device_name(const char *devpath) 247 { 248 char *p, *head; 249 250 if (!devpath) 251 return (NULL); 252 253 p = strdup(devpath); 254 head = p; 255 256 p = strchr(p, '@'); 257 if (p) 258 *p = 0; 259 260 p = strrchr(head, '/'); 261 if (p) { 262 p++; 263 if (*p == 0) { 264 free(head); 265 return (NULL); 266 } 267 p = strdup(p); 268 free(head); 269 return (p); 270 } 271 272 return (head); 273 } 274 275 static int 276 read_label(FILE *fp, char *label, size_t size, const char *devpath) 277 { 278 hammer2_blockref_t broot, best, *bref; 279 hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media; 280 size_t bytes; 281 bool res = false; 282 int i, best_i, error = 1; 283 const char *pfs; 284 char *devname; 285 286 best_i = -1; 287 memset(vols, 0, sizeof(vols)); 288 memset(&best, 0, sizeof(best)); 289 290 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) { 291 if (i * HAMMER2_ZONE_BYTES64 >= get_file_size(fp)) 292 break; 293 memset(&broot, 0, sizeof(broot)); 294 broot.type = HAMMER2_BREF_TYPE_VOLUME; 295 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 296 vols[i] = (void*)read_voldata(fp, i); 297 if (vols[i] == NULL) { 298 warnx("hammer2: failed to read volume data"); 299 goto fail; 300 } 301 broot.mirror_tid = vols[i]->voldata.mirror_tid; 302 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 303 best_i = i; 304 best = broot; 305 } 306 } 307 308 bref = &vols[best_i]->voldata.sroot_blockset.blockref[0]; 309 if (bref->type != HAMMER2_BREF_TYPE_INODE) { 310 /* Don't print error as devpath could be non-root volume. */ 311 goto fail; 312 } 313 314 media = read_media(fp, bref, &bytes); 315 if (media == NULL) { 316 goto fail; 317 } 318 319 /* 320 * fstyp_function in DragonFly takes an additional devpath argument 321 * which doesn't exist in FreeBSD and NetBSD. 322 */ 323 #ifdef HAS_DEVPATH 324 pfs = strchr(devpath, '@'); 325 if (!pfs) { 326 assert(strlen(devpath)); 327 switch (devpath[strlen(devpath) - 1]) { 328 case 'a': 329 pfs = "BOOT"; 330 break; 331 case 'd': 332 pfs = "ROOT"; 333 break; 334 default: 335 pfs = "DATA"; 336 break; 337 } 338 } else 339 pfs++; 340 341 if (strlen(pfs) > HAMMER2_INODE_MAXNAME) { 342 goto fail; 343 } 344 devname = extract_device_name(devpath); 345 #else 346 pfs = ""; 347 devname = extract_device_name(NULL); 348 assert(!devname); 349 #endif 350 351 /* Add device name to help support multiple autofs -media mounts. */ 352 if (find_pfs(fp, bref, pfs, &res) == 0 && res) { 353 if (devname) 354 snprintf(label, size, "%s_%s", pfs, devname); 355 else 356 strlcpy(label, pfs, size); 357 } else { 358 memset(label, 0, size); 359 memcpy(label, media->ipdata.filename, 360 sizeof(media->ipdata.filename)); 361 if (devname) { 362 strlcat(label, "_", size); 363 strlcat(label, devname, size); 364 } 365 } 366 if (devname) 367 free(devname); 368 free(media); 369 error = 0; 370 fail: 371 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) 372 free(vols[i]); 373 374 return (error); 375 } 376 377 int 378 fstyp_hammer2(FILE *fp, char *label, size_t size, const char *devpath) 379 { 380 hammer2_volume_data_t *voldata = read_voldata(fp, 0); 381 int error = 1; 382 383 if (voldata->volu_id != HAMMER2_ROOT_VOLUME) 384 goto fail; 385 if (voldata->nvolumes != 0) 386 goto fail; 387 if (test_voldata(fp)) 388 goto fail; 389 390 error = read_label(fp, label, size, devpath); 391 fail: 392 free(voldata); 393 return (error); 394 } 395 396 static int 397 __fsvtyp_hammer2(const char *blkdevs, char *label, size_t size, int partial) 398 { 399 hammer2_volume_data_t *voldata = NULL; 400 FILE *fp = NULL; 401 char *dup = NULL, *target_label = NULL, *p, *volpath, *rootvolpath; 402 char x[HAMMER2_MAX_VOLUMES]; 403 int i, volid, error = 1; 404 405 if (!blkdevs) 406 goto fail; 407 408 memset(x, 0, sizeof(x)); 409 p = dup = strdup(blkdevs); 410 if ((p = strchr(p, '@')) != NULL) { 411 *p++ = '\0'; 412 target_label = p; 413 } 414 p = dup; 415 416 volpath = NULL; 417 rootvolpath = NULL; 418 volid = -1; 419 while (p) { 420 volpath = p; 421 if ((p = strchr(p, ':')) != NULL) 422 *p++ = '\0'; 423 if ((fp = fopen(volpath, "r")) == NULL) { 424 warnx("hammer2: failed to open %s", volpath); 425 goto fail; 426 } 427 if (test_voldata(fp)) 428 break; 429 voldata = read_voldata(fp, 0); 430 fclose(fp); 431 if (voldata == NULL) { 432 warnx("hammer2: failed to read volume data"); 433 goto fail; 434 } 435 volid = voldata->volu_id; 436 free(voldata); 437 voldata = NULL; 438 assert(volid >= 0); 439 assert(volid < HAMMER2_MAX_VOLUMES); 440 x[volid]++; 441 if (volid == HAMMER2_ROOT_VOLUME) 442 rootvolpath = volpath; 443 } 444 445 /* If no rootvolpath, proceed only if partial mode with volpath. */ 446 if (rootvolpath) 447 volpath = rootvolpath; 448 else if (!partial || !volpath) 449 goto fail; 450 if ((fp = fopen(volpath, "r")) == NULL) { 451 warnx("hammer2: failed to open %s", volpath); 452 goto fail; 453 } 454 voldata = read_voldata(fp, 0); 455 if (voldata == NULL) { 456 warnx("hammer2: failed to read volume data"); 457 goto fail; 458 } 459 460 if (volid == -1) 461 goto fail; 462 if (partial) 463 goto success; 464 465 for (i = 0; i < HAMMER2_MAX_VOLUMES; i++) 466 if (x[i] > 1) 467 goto fail; 468 for (i = 0; i < HAMMER2_MAX_VOLUMES; i++) 469 if (x[i] == 0) 470 break; 471 if (voldata->nvolumes != i) 472 goto fail; 473 for (; i < HAMMER2_MAX_VOLUMES; i++) 474 if (x[i] != 0) 475 goto fail; 476 success: 477 /* Reconstruct @label format path using only root volume. */ 478 if (target_label) { 479 int siz = strlen(volpath) + strlen(target_label) + 2; 480 p = calloc(1, siz); 481 snprintf(p, siz, "%s@%s", volpath, target_label); 482 volpath = p; 483 } 484 error = read_label(fp, label, size, volpath); 485 if (target_label) 486 free(p); 487 /* If in partial mode, read label but ignore error. */ 488 if (partial) 489 error = 0; 490 fail: 491 if (fp) 492 fclose(fp); 493 free(voldata); 494 free(dup); 495 return (error); 496 } 497 498 int 499 fsvtyp_hammer2(const char *blkdevs, char *label, size_t size) 500 { 501 return (__fsvtyp_hammer2(blkdevs, label, size, 0)); 502 } 503 504 int 505 fsvtyp_hammer2_partial(const char *blkdevs, char *label, size_t size) 506 { 507 return (__fsvtyp_hammer2(blkdevs, label, size, 1)); 508 } 509