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 static void h2disk_check(const char *devpath, 37 void (*callback1)(const char *, hammer2_blockref_t *, int)); 38 static void h2pfs_check(int fd, hammer2_blockref_t *bref, 39 void (*callback2)(const char *, hammer2_blockref_t *, int)); 40 41 static void info_callback1(const char *, hammer2_blockref_t *, int); 42 static void info_callback2(const char *, hammer2_blockref_t *, int); 43 44 typedef void (*cmd_callback)(const char *, hammer2_blockref_t *, int); 45 46 static 47 void 48 h2disk_check_serno(cmd_callback fn) 49 { 50 DIR *dir; 51 52 if ((dir = opendir("/dev/serno")) != NULL) { 53 struct dirent *den; 54 55 while ((den = readdir(dir)) != NULL) { 56 const char *ptr; 57 int slice; 58 char part; 59 char *devpath; 60 61 if (!strcmp(den->d_name, ".") || 62 !strcmp(den->d_name, "..")) 63 continue; 64 ptr = strrchr(den->d_name, '.'); 65 if (ptr && sscanf(ptr, ".s%d%c", &slice, &part) == 2) { 66 asprintf(&devpath, "/dev/serno/%s", 67 den->d_name); 68 h2disk_check(devpath, fn); 69 free(devpath); 70 } 71 } 72 closedir(dir); 73 } 74 } 75 76 static 77 void 78 h2disk_check_dm(cmd_callback fn) 79 { 80 DIR *dir; 81 82 if ((dir = opendir("/dev/mapper")) != NULL) { 83 struct dirent *den; 84 85 while ((den = readdir(dir)) != NULL) { 86 char *devpath; 87 88 if (!strcmp(den->d_name, ".") || 89 !strcmp(den->d_name, "..") || 90 !strcmp(den->d_name, "control")) 91 continue; 92 asprintf(&devpath, "/dev/mapper/%s", 93 den->d_name); 94 h2disk_check(devpath, fn); 95 free(devpath); 96 } 97 closedir(dir); 98 } 99 } 100 101 static 102 void 103 h2disk_check_misc(cmd_callback fn) 104 { 105 DIR *dir; 106 107 if ((dir = opendir("/dev")) != NULL) { 108 struct dirent *den; 109 110 while ((den = readdir(dir)) != NULL) { 111 char *devpath; 112 113 if (!strcmp(den->d_name, ".") || 114 !strcmp(den->d_name, "..")) 115 continue; 116 if (strncmp(den->d_name, "ad", 2) && 117 strncmp(den->d_name, "vn", 2)) 118 continue; 119 if (!strcmp(den->d_name, "vn")) 120 continue; 121 if (!strncmp(den->d_name, "ad", 2) && 122 den->d_namlen <= 3) 123 continue; 124 asprintf(&devpath, "/dev/%s", den->d_name); 125 h2disk_check(devpath, fn); 126 free(devpath); 127 } 128 closedir(dir); 129 } 130 } 131 132 int 133 cmd_info(int ac, const char **av) 134 { 135 int i; 136 137 for (i = 0; i < ac; ++i) 138 h2disk_check(av[i], info_callback1); 139 if (ac == 0) { 140 h2disk_check_serno(info_callback1); 141 h2disk_check_dm(info_callback1); 142 h2disk_check_misc(info_callback1); 143 } 144 return 0; 145 } 146 147 static 148 void 149 info_callback1(const char *path, hammer2_blockref_t *bref, int fd) 150 { 151 printf("%s:\n", path); 152 h2pfs_check(fd, bref, info_callback2); 153 } 154 155 static 156 void 157 info_callback2(const char *pfsname, 158 hammer2_blockref_t *bref __unused, int fd __unused) 159 { 160 printf(" %s\n", pfsname); 161 } 162 163 static void mount_callback1(const char *, hammer2_blockref_t *, int); 164 static void mount_callback2(const char *, hammer2_blockref_t *, int); 165 static void cmd_mountall_alarm(int signo); 166 167 static volatile sig_atomic_t DidAlarm; 168 169 int 170 cmd_mountall(int ac, const char **av) 171 { 172 int i; 173 pid_t pid; 174 175 for (i = 0; i < ac; ++i) 176 h2disk_check(av[i], mount_callback1); 177 if (ac == 0) { 178 h2disk_check_serno(mount_callback1); 179 h2disk_check_dm(mount_callback1); 180 h2disk_check_misc(mount_callback1); 181 } 182 signal(SIGALRM, cmd_mountall_alarm); 183 for (;;) { 184 alarm(15); 185 pid = wait3(NULL, 0, NULL); 186 if (pid < 0 && errno == ECHILD) 187 break; 188 if (pid < 0 && DidAlarm) { 189 printf("Timeout waiting for mounts to complete\n"); 190 break; 191 } 192 } 193 alarm(0); 194 195 return 0; 196 } 197 198 static 199 void 200 cmd_mountall_alarm(int signo __unused) 201 { 202 DidAlarm = 1; 203 } 204 205 static const char *mount_path; 206 static const char *mount_comp; 207 208 static 209 void 210 mount_callback1(const char *devpath, hammer2_blockref_t *bref, int fd) 211 { 212 mount_path = devpath; 213 mount_comp = strrchr(devpath, '/'); 214 if (mount_comp) { 215 ++mount_comp; 216 h2pfs_check(fd, bref, mount_callback2); 217 } 218 } 219 220 static 221 void 222 mount_callback2(const char *pfsname, 223 hammer2_blockref_t *bref __unused, int fd) 224 { 225 char *tmp_path; 226 char *label; 227 int tfd; 228 229 if (strcmp(pfsname, "LOCAL") == 0) { 230 if ((tfd = open("/dev/null", O_RDONLY)) >= 0) { 231 dup2(tfd, fd); 232 close(tfd); 233 } else { 234 perror("open(/dev/null)"); 235 exit(1); 236 } 237 asprintf(&tmp_path, "/var/hammer2/LOCAL.%s", mount_comp); 238 asprintf(&label, "%s@LOCAL", mount_path); 239 mkdir("/var/hammer2", 0700); 240 mkdir(tmp_path, 0700); 241 printf("mount %s\n", tmp_path); 242 if (fork() == 0) { 243 execl("/sbin/mount_hammer2", 244 "mount", 245 label, 246 tmp_path, 247 NULL); 248 } 249 free(label); 250 free(tmp_path); 251 } 252 } 253 254 /* 255 * Support 256 */ 257 static 258 void 259 h2disk_check(const char *devpath, 260 void (*callback1)(const char *, hammer2_blockref_t *, int)) 261 { 262 hammer2_blockref_t broot; 263 hammer2_blockref_t best; 264 hammer2_media_data_t media; 265 struct partinfo partinfo; 266 int fd; 267 int i; 268 int best_i; 269 270 fd = open(devpath, O_RDONLY); 271 if (fd < 0) { 272 fprintf(stderr, "Unable to open \"%s\"\n", devpath); 273 return; 274 } 275 if (ioctl(fd, DIOCGPART, &partinfo) == -1) { 276 fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath); 277 goto done; 278 } 279 280 /* 281 * Check partition or slice for HAMMER2 designation. Validate the 282 * designation either from the fstype (typically set for disklabel 283 * partitions), or the fstype_uuid (typically set for direct-mapped 284 * hammer2 GPT slices). 285 */ 286 if (partinfo.fstype != FS_HAMMER2) { 287 uint32_t status; 288 uuid_t h2uuid; 289 int is_nil = uuid_is_nil(&partinfo.fstype_uuid, NULL); 290 291 uuid_from_string(HAMMER2_UUID_STRING, &h2uuid, &status); 292 if (!is_nil && (status != uuid_s_ok || 293 uuid_compare(&partinfo.fstype_uuid, &h2uuid, NULL) != 0)) { 294 goto done; 295 } 296 } 297 298 /* 299 * Find the best volume header. 300 */ 301 best_i = -1; 302 bzero(&best, sizeof(best)); 303 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 304 bzero(&broot, sizeof(broot)); 305 broot.type = HAMMER2_BREF_TYPE_VOLUME; 306 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | 307 HAMMER2_PBUFRADIX; 308 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET); 309 if (read(fd, &media, HAMMER2_PBUFSIZE) == 310 (ssize_t)HAMMER2_PBUFSIZE && 311 media.voldata.magic == HAMMER2_VOLUME_ID_HBO) { 312 broot.mirror_tid = media.voldata.mirror_tid; 313 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 314 best_i = i; 315 best = broot; 316 } 317 } 318 } 319 if (best_i >= 0) 320 callback1(devpath, &best, fd); 321 done: 322 close(fd); 323 } 324 325 static 326 void 327 h2pfs_check(int fd, hammer2_blockref_t *bref, 328 void (*callback2)(const char *, hammer2_blockref_t *, int)) 329 { 330 hammer2_media_data_t media; 331 hammer2_blockref_t *bscan; 332 int bcount; 333 int i; 334 size_t bytes; 335 size_t io_bytes; 336 size_t boff; 337 uint32_t cv; 338 uint64_t cv64; 339 hammer2_off_t io_off; 340 hammer2_off_t io_base; 341 342 bytes = (size_t)1 << (bref->data_off & HAMMER2_OFF_MASK_RADIX); 343 344 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 345 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 346 io_bytes = bytes; 347 boff = io_off - io_base; 348 349 io_bytes = HAMMER2_MINIOSIZE; 350 while (io_bytes + boff < bytes) 351 io_bytes <<= 1; 352 353 if (io_bytes > sizeof(media)) { 354 printf("(bad block size %zd)\n", bytes); 355 return; 356 } 357 if (bref->type != HAMMER2_BREF_TYPE_DATA) { 358 lseek(fd, io_base, SEEK_SET); 359 if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) { 360 printf("(media read failed)\n"); 361 return; 362 } 363 if (boff) 364 bcopy((char *)&media + boff, &media, bytes); 365 } 366 367 bscan = NULL; 368 bcount = 0; 369 370 /* 371 * Check data integrity in verbose mode, otherwise we are just doing 372 * a quick meta-data scan. Meta-data integrity is always checked. 373 * (Also see the check above that ensures the media data is loaded, 374 * otherwise there's no data to check!). 375 */ 376 if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) { 377 switch(HAMMER2_DEC_CHECK(bref->methods)) { 378 case HAMMER2_CHECK_NONE: 379 break; 380 case HAMMER2_CHECK_DISABLED: 381 break; 382 case HAMMER2_CHECK_ISCSI32: 383 cv = hammer2_icrc32(&media, bytes); 384 if (bref->check.iscsi32.value != cv) { 385 printf("\t(icrc failed %02x:%08x/%08x)\n", 386 bref->methods, 387 bref->check.iscsi32.value, 388 cv); 389 } 390 break; 391 case HAMMER2_CHECK_XXHASH64: 392 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED); 393 if (bref->check.xxhash64.value != cv64) { 394 printf("\t(xxhash failed %02x:%016jx/%016jx)\n", 395 bref->methods, 396 bref->check.xxhash64.value, 397 cv64); 398 } 399 break; 400 case HAMMER2_CHECK_SHA192: 401 break; 402 case HAMMER2_CHECK_FREEMAP: 403 cv = hammer2_icrc32(&media, bytes); 404 if (bref->check.freemap.icrc32 != cv) { 405 printf("\t(fcrc %02x:%08x/%08x)\n", 406 bref->methods, 407 bref->check.freemap.icrc32, 408 cv); 409 } 410 break; 411 } 412 } 413 414 switch(bref->type) { 415 case HAMMER2_BREF_TYPE_EMPTY: 416 break; 417 case HAMMER2_BREF_TYPE_INODE: 418 if (media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) { 419 if ((media.ipdata.meta.op_flags & 420 HAMMER2_OPFLAG_DIRECTDATA) == 0) { 421 bscan = &media.ipdata.u.blockset.blockref[0]; 422 bcount = HAMMER2_SET_COUNT; 423 } 424 } else 425 if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) { 426 callback2((char*)media.ipdata.filename, bref, fd); 427 bscan = NULL; 428 bcount = 0; 429 } else { 430 bscan = NULL; 431 bcount = 0; 432 } 433 break; 434 case HAMMER2_BREF_TYPE_INDIRECT: 435 bscan = &media.npdata[0]; 436 bcount = bytes / sizeof(hammer2_blockref_t); 437 break; 438 case HAMMER2_BREF_TYPE_DATA: 439 break; 440 case HAMMER2_BREF_TYPE_VOLUME: 441 bscan = &media.voldata.sroot_blockset.blockref[0]; 442 bcount = HAMMER2_SET_COUNT; 443 break; 444 default: 445 break; 446 } 447 for (i = 0; i < bcount; ++i) { 448 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) 449 h2pfs_check(fd, &bscan[i], callback2); 450 } 451 } 452