1 /* 2 * Copyright (c) 2015 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@dragonflybsd.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 #include "hammer2.h" 35 36 typedef void (*cmd_callback)(const void *, hammer2_blockref_t *, int); 37 38 static void h2disk_check(const char *devpath, cmd_callback callback1); 39 static void h2pfs_check(int fd, hammer2_blockref_t *bref, 40 cmd_callback callback2); 41 42 static void info_callback1(const void *, hammer2_blockref_t *, int); 43 static void info_callback2(const void *, hammer2_blockref_t *, int); 44 45 static 46 void 47 h2disk_check_serno(cmd_callback fn) 48 { 49 DIR *dir; 50 51 if ((dir = opendir("/dev/serno")) != NULL) { 52 struct dirent *den; 53 54 while ((den = readdir(dir)) != NULL) { 55 const char *ptr; 56 int slice; 57 char part; 58 char *devpath; 59 60 if (!strcmp(den->d_name, ".") || 61 !strcmp(den->d_name, "..")) 62 continue; 63 ptr = strrchr(den->d_name, '.'); 64 if (ptr && sscanf(ptr, ".s%d%c", &slice, &part) == 2) { 65 asprintf(&devpath, "/dev/serno/%s", 66 den->d_name); 67 h2disk_check(devpath, fn); 68 free(devpath); 69 } 70 } 71 closedir(dir); 72 } 73 } 74 75 static 76 void 77 h2disk_check_dm(cmd_callback fn) 78 { 79 DIR *dir; 80 81 if ((dir = opendir("/dev/mapper")) != NULL) { 82 struct dirent *den; 83 84 while ((den = readdir(dir)) != NULL) { 85 char *devpath; 86 87 if (!strcmp(den->d_name, ".") || 88 !strcmp(den->d_name, "..") || 89 !strcmp(den->d_name, "control")) 90 continue; 91 asprintf(&devpath, "/dev/mapper/%s", 92 den->d_name); 93 h2disk_check(devpath, fn); 94 free(devpath); 95 } 96 closedir(dir); 97 } 98 } 99 100 static 101 void 102 h2disk_check_misc(cmd_callback fn) 103 { 104 DIR *dir; 105 106 if ((dir = opendir("/dev")) != NULL) { 107 struct dirent *den; 108 109 while ((den = readdir(dir)) != NULL) { 110 char *devpath; 111 112 if (!strcmp(den->d_name, ".") || 113 !strcmp(den->d_name, "..")) 114 continue; 115 if (strncmp(den->d_name, "ad", 2) && 116 strncmp(den->d_name, "vn", 2)) 117 continue; 118 if (!strcmp(den->d_name, "vn")) 119 continue; 120 if (!strncmp(den->d_name, "ad", 2) && 121 den->d_namlen <= 3) 122 continue; 123 asprintf(&devpath, "/dev/%s", den->d_name); 124 h2disk_check(devpath, fn); 125 free(devpath); 126 } 127 closedir(dir); 128 } 129 } 130 131 int 132 cmd_info(int ac, const char **av) 133 { 134 int i; 135 136 for (i = 0; i < ac; ++i) 137 h2disk_check(av[i], info_callback1); 138 if (ac == 0) { 139 h2disk_check_serno(info_callback1); 140 h2disk_check_dm(info_callback1); 141 h2disk_check_misc(info_callback1); 142 } 143 return 0; 144 } 145 146 struct pfs_entry { 147 TAILQ_ENTRY(pfs_entry) entry; 148 char name[NAME_MAX+1]; 149 char s[NAME_MAX+1]; 150 }; 151 152 static TAILQ_HEAD(, pfs_entry) head; 153 154 static 155 void 156 info_callback1(const void *path, hammer2_blockref_t *bref, int fd) 157 { 158 struct pfs_entry *p; 159 160 printf("%s:\n", (const char*)path); 161 162 TAILQ_INIT(&head); 163 h2pfs_check(fd, bref, info_callback2); 164 165 printf(" Type " 166 "ClusterId (pfs_clid) " 167 "Label\n"); 168 while ((p = TAILQ_FIRST(&head)) != NULL) { 169 printf(" %s %s\n", p->s, p->name); 170 TAILQ_REMOVE(&head, p, entry); 171 free(p); 172 } 173 } 174 175 static 176 void 177 info_callback2(const void *data, 178 hammer2_blockref_t *bref __unused, int fd __unused) 179 { 180 const hammer2_inode_data_t *ipdata = data; 181 const hammer2_inode_meta_t *meta = &ipdata->meta; 182 char *pfs_id_str = NULL; 183 const char *type_str; 184 struct pfs_entry *p, *e; 185 uuid_t uuid; 186 187 uuid = meta->pfs_clid; 188 hammer2_uuid_to_str(&uuid, &pfs_id_str); 189 if (meta->pfs_type == HAMMER2_PFSTYPE_MASTER) { 190 if (meta->pfs_subtype == HAMMER2_PFSSUBTYPE_NONE) 191 type_str = "MASTER"; 192 else 193 type_str = hammer2_pfssubtype_to_str(meta->pfs_subtype); 194 } else { 195 type_str = hammer2_pfstype_to_str(meta->pfs_type); 196 } 197 e = calloc(1, sizeof(*e)); 198 snprintf(e->name, sizeof(e->name), "%s", ipdata->filename); 199 snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str); 200 free(pfs_id_str); 201 202 p = TAILQ_FIRST(&head); 203 while (p) { 204 if (strcmp(e->name, p->name) <= 0) { 205 TAILQ_INSERT_BEFORE(p, e, entry); 206 break; 207 } 208 p = TAILQ_NEXT(p, entry); 209 } 210 if (!p) 211 TAILQ_INSERT_TAIL(&head, e, entry); 212 } 213 214 static void mount_callback1(const void *, hammer2_blockref_t *, int); 215 static void mount_callback2(const void *, hammer2_blockref_t *, int); 216 static void cmd_mountall_alarm(int signo); 217 218 static volatile sig_atomic_t DidAlarm; 219 220 int 221 cmd_mountall(int ac, const char **av) 222 { 223 int i; 224 pid_t pid; 225 226 for (i = 0; i < ac; ++i) 227 h2disk_check(av[i], mount_callback1); 228 if (ac == 0) { 229 h2disk_check_serno(mount_callback1); 230 h2disk_check_dm(mount_callback1); 231 h2disk_check_misc(mount_callback1); 232 } 233 signal(SIGALRM, cmd_mountall_alarm); 234 for (;;) { 235 alarm(15); 236 pid = wait3(NULL, 0, NULL); 237 if (pid < 0 && errno == ECHILD) 238 break; 239 if (pid < 0 && DidAlarm) { 240 printf("Timeout waiting for mounts to complete\n"); 241 break; 242 } 243 } 244 alarm(0); 245 246 return 0; 247 } 248 249 static 250 void 251 cmd_mountall_alarm(int signo __unused) 252 { 253 DidAlarm = 1; 254 } 255 256 static const char *mount_path; 257 static const char *mount_comp; 258 259 static 260 void 261 mount_callback1(const void *devpath, hammer2_blockref_t *bref, int fd) 262 { 263 mount_path = devpath; 264 mount_comp = strrchr(devpath, '/'); 265 if (mount_comp) { 266 ++mount_comp; 267 h2pfs_check(fd, bref, mount_callback2); 268 } 269 } 270 271 static 272 void 273 mount_callback2(const void *data, 274 hammer2_blockref_t *bref __unused, int fd) 275 { 276 const hammer2_inode_data_t *ipdata = data; 277 char *tmp_path; 278 char *label; 279 int tfd; 280 281 if (strcmp(ipdata->filename, "LOCAL") == 0) { 282 if ((tfd = open("/dev/null", O_RDONLY)) >= 0) { 283 dup2(tfd, fd); 284 close(tfd); 285 } else { 286 perror("open(/dev/null)"); 287 exit(1); 288 } 289 asprintf(&tmp_path, "/var/hammer2/LOCAL.%s", mount_comp); 290 asprintf(&label, "%s@LOCAL", mount_path); 291 mkdir("/var/hammer2", 0700); 292 mkdir(tmp_path, 0700); 293 printf("mount %s\n", tmp_path); 294 if (fork() == 0) { 295 execl("/sbin/mount_hammer2", 296 "mount", 297 label, 298 tmp_path, 299 NULL); 300 } 301 free(label); 302 free(tmp_path); 303 } 304 } 305 306 static 307 void 308 h2disk_check(const char *devpath, cmd_callback callback1) 309 { 310 hammer2_blockref_t broot; 311 hammer2_blockref_t best; 312 hammer2_media_data_t media; 313 hammer2_volume_data_t *voldata; 314 struct partinfo partinfo; 315 int fd; 316 int i; 317 int best_i; 318 319 fd = open(devpath, O_RDONLY); 320 if (fd < 0) { 321 fprintf(stderr, "Unable to open \"%s\"\n", devpath); 322 return; 323 } 324 if (ioctl(fd, DIOCGPART, &partinfo) == -1) { 325 fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath); 326 goto done; 327 } 328 329 /* 330 * Check partition or slice for HAMMER2 designation. Validate the 331 * designation either from the fstype (typically set for disklabel 332 * partitions), or the fstype_uuid (typically set for direct-mapped 333 * hammer2 GPT slices). 334 */ 335 if (partinfo.fstype != FS_HAMMER2) { 336 uint32_t status; 337 uuid_t h2uuid; 338 int is_nil = uuid_is_nil(&partinfo.fstype_uuid, NULL); 339 340 uuid_from_string(HAMMER2_UUID_STRING, &h2uuid, &status); 341 if (!is_nil && (status != uuid_s_ok || 342 uuid_compare(&partinfo.fstype_uuid, &h2uuid, NULL) != 0)) { 343 goto done; 344 } 345 } 346 347 /* 348 * Find the best volume header. 349 */ 350 best_i = -1; 351 bzero(&best, sizeof(best)); 352 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 353 bzero(&broot, sizeof(broot)); 354 broot.type = HAMMER2_BREF_TYPE_VOLUME; 355 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | 356 HAMMER2_PBUFRADIX; 357 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET); 358 if (read(fd, &media, HAMMER2_PBUFSIZE) != 359 (ssize_t)HAMMER2_PBUFSIZE) 360 continue; 361 voldata = &media.voldata; 362 if (voldata->magic != HAMMER2_VOLUME_ID_HBO) 363 continue; 364 /* XXX multiple volumes currently unsupported */ 365 if (voldata->nvolumes > 1) 366 break; 367 broot.mirror_tid = voldata->mirror_tid; 368 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 369 best_i = i; 370 best = broot; 371 } 372 } 373 if (best_i >= 0) 374 callback1(devpath, &best, fd); 375 done: 376 close(fd); 377 } 378 379 static 380 void 381 h2pfs_check(int fd, hammer2_blockref_t *bref, cmd_callback callback2) 382 { 383 hammer2_media_data_t media; 384 hammer2_blockref_t *bscan; 385 int bcount; 386 int i; 387 size_t bytes; 388 size_t io_bytes; 389 size_t boff; 390 uint32_t cv; 391 uint64_t cv64; 392 hammer2_off_t io_off; 393 hammer2_off_t io_base; 394 395 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 396 if (bytes) 397 bytes = (size_t)1 << bytes; 398 399 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 400 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1); 401 io_bytes = bytes; 402 boff = io_off - io_base; 403 404 io_bytes = HAMMER2_LBUFSIZE; 405 while (io_bytes + boff < bytes) 406 io_bytes <<= 1; 407 408 if (io_bytes > sizeof(media)) { 409 printf("(bad block size %zu)\n", bytes); 410 return; 411 } 412 if (bref->type != HAMMER2_BREF_TYPE_DATA) { 413 lseek(fd, io_base, SEEK_SET); 414 if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) { 415 printf("(media read failed)\n"); 416 return; 417 } 418 if (boff) 419 bcopy((char *)&media + boff, &media, bytes); 420 } 421 422 bscan = NULL; 423 bcount = 0; 424 425 /* 426 * Check data integrity in verbose mode, otherwise we are just doing 427 * a quick meta-data scan. Meta-data integrity is always checked. 428 * (Also see the check above that ensures the media data is loaded, 429 * otherwise there's no data to check!). 430 */ 431 if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) { 432 switch(HAMMER2_DEC_CHECK(bref->methods)) { 433 case HAMMER2_CHECK_NONE: 434 break; 435 case HAMMER2_CHECK_DISABLED: 436 break; 437 case HAMMER2_CHECK_ISCSI32: 438 cv = hammer2_icrc32(&media, bytes); 439 if (bref->check.iscsi32.value != cv) { 440 printf("\t(icrc failed %02x:%08x/%08x)\n", 441 bref->methods, 442 bref->check.iscsi32.value, 443 cv); 444 } 445 break; 446 case HAMMER2_CHECK_XXHASH64: 447 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED); 448 if (bref->check.xxhash64.value != cv64) { 449 printf("\t(xxhash failed %02x:%016jx/%016jx)\n", 450 bref->methods, 451 bref->check.xxhash64.value, 452 cv64); 453 } 454 break; 455 case HAMMER2_CHECK_SHA192: 456 break; 457 case HAMMER2_CHECK_FREEMAP: 458 cv = hammer2_icrc32(&media, bytes); 459 if (bref->check.freemap.icrc32 != cv) { 460 printf("\t(fcrc %02x:%08x/%08x)\n", 461 bref->methods, 462 bref->check.freemap.icrc32, 463 cv); 464 } 465 break; 466 } 467 } 468 469 switch(bref->type) { 470 case HAMMER2_BREF_TYPE_INODE: 471 if (media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) { 472 if ((media.ipdata.meta.op_flags & 473 HAMMER2_OPFLAG_DIRECTDATA) == 0) { 474 bscan = &media.ipdata.u.blockset.blockref[0]; 475 bcount = HAMMER2_SET_COUNT; 476 } 477 } else if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) { 478 callback2(&media.ipdata, bref, fd); 479 bscan = NULL; 480 bcount = 0; 481 } else { 482 bscan = NULL; 483 bcount = 0; 484 } 485 break; 486 case HAMMER2_BREF_TYPE_INDIRECT: 487 bscan = &media.npdata[0]; 488 bcount = bytes / sizeof(hammer2_blockref_t); 489 break; 490 case HAMMER2_BREF_TYPE_VOLUME: 491 bscan = &media.voldata.sroot_blockset.blockref[0]; 492 bcount = HAMMER2_SET_COUNT; 493 break; 494 default: 495 break; 496 } 497 for (i = 0; i < bcount; ++i) { 498 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) 499 h2pfs_check(fd, &bscan[i], callback2); 500 } 501 } 502