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 static 147 void 148 info_callback1(const void *path, hammer2_blockref_t *bref, int fd) 149 { 150 printf("%s:\n", (const char*)path); 151 h2pfs_check(fd, bref, info_callback2); 152 } 153 154 static 155 void 156 info_callback2(const void *pfsname, 157 hammer2_blockref_t *bref __unused, int fd __unused) 158 { 159 printf(" %s\n", (const char*)pfsname); 160 } 161 162 static void mount_callback1(const void *, hammer2_blockref_t *, int); 163 static void mount_callback2(const void *, hammer2_blockref_t *, int); 164 static void cmd_mountall_alarm(int signo); 165 166 static volatile sig_atomic_t DidAlarm; 167 168 int 169 cmd_mountall(int ac, const char **av) 170 { 171 int i; 172 pid_t pid; 173 174 for (i = 0; i < ac; ++i) 175 h2disk_check(av[i], mount_callback1); 176 if (ac == 0) { 177 h2disk_check_serno(mount_callback1); 178 h2disk_check_dm(mount_callback1); 179 h2disk_check_misc(mount_callback1); 180 } 181 signal(SIGALRM, cmd_mountall_alarm); 182 for (;;) { 183 alarm(15); 184 pid = wait3(NULL, 0, NULL); 185 if (pid < 0 && errno == ECHILD) 186 break; 187 if (pid < 0 && DidAlarm) { 188 printf("Timeout waiting for mounts to complete\n"); 189 break; 190 } 191 } 192 alarm(0); 193 194 return 0; 195 } 196 197 static 198 void 199 cmd_mountall_alarm(int signo __unused) 200 { 201 DidAlarm = 1; 202 } 203 204 static const char *mount_path; 205 static const char *mount_comp; 206 207 static 208 void 209 mount_callback1(const void *devpath, hammer2_blockref_t *bref, int fd) 210 { 211 mount_path = devpath; 212 mount_comp = strrchr(devpath, '/'); 213 if (mount_comp) { 214 ++mount_comp; 215 h2pfs_check(fd, bref, mount_callback2); 216 } 217 } 218 219 static 220 void 221 mount_callback2(const void *pfsname, 222 hammer2_blockref_t *bref __unused, int fd) 223 { 224 char *tmp_path; 225 char *label; 226 int tfd; 227 228 if (strcmp(pfsname, "LOCAL") == 0) { 229 if ((tfd = open("/dev/null", O_RDONLY)) >= 0) { 230 dup2(tfd, fd); 231 close(tfd); 232 } else { 233 perror("open(/dev/null)"); 234 exit(1); 235 } 236 asprintf(&tmp_path, "/var/hammer2/LOCAL.%s", mount_comp); 237 asprintf(&label, "%s@LOCAL", mount_path); 238 mkdir("/var/hammer2", 0700); 239 mkdir(tmp_path, 0700); 240 printf("mount %s\n", tmp_path); 241 if (fork() == 0) { 242 execl("/sbin/mount_hammer2", 243 "mount", 244 label, 245 tmp_path, 246 NULL); 247 } 248 free(label); 249 free(tmp_path); 250 } 251 } 252 253 static 254 void 255 h2disk_check(const char *devpath, cmd_callback callback1) 256 { 257 hammer2_blockref_t broot; 258 hammer2_blockref_t best; 259 hammer2_media_data_t media; 260 struct partinfo partinfo; 261 int fd; 262 int i; 263 int best_i; 264 265 fd = open(devpath, O_RDONLY); 266 if (fd < 0) { 267 fprintf(stderr, "Unable to open \"%s\"\n", devpath); 268 return; 269 } 270 if (ioctl(fd, DIOCGPART, &partinfo) == -1) { 271 fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath); 272 goto done; 273 } 274 275 /* 276 * Check partition or slice for HAMMER2 designation. Validate the 277 * designation either from the fstype (typically set for disklabel 278 * partitions), or the fstype_uuid (typically set for direct-mapped 279 * hammer2 GPT slices). 280 */ 281 if (partinfo.fstype != FS_HAMMER2) { 282 uint32_t status; 283 uuid_t h2uuid; 284 int is_nil = uuid_is_nil(&partinfo.fstype_uuid, NULL); 285 286 uuid_from_string(HAMMER2_UUID_STRING, &h2uuid, &status); 287 if (!is_nil && (status != uuid_s_ok || 288 uuid_compare(&partinfo.fstype_uuid, &h2uuid, NULL) != 0)) { 289 goto done; 290 } 291 } 292 293 /* 294 * Find the best volume header. 295 */ 296 best_i = -1; 297 bzero(&best, sizeof(best)); 298 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 299 bzero(&broot, sizeof(broot)); 300 broot.type = HAMMER2_BREF_TYPE_VOLUME; 301 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | 302 HAMMER2_PBUFRADIX; 303 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET); 304 if (read(fd, &media, HAMMER2_PBUFSIZE) == 305 (ssize_t)HAMMER2_PBUFSIZE && 306 media.voldata.magic == HAMMER2_VOLUME_ID_HBO) { 307 broot.mirror_tid = media.voldata.mirror_tid; 308 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 309 best_i = i; 310 best = broot; 311 } 312 } 313 } 314 if (best_i >= 0) 315 callback1(devpath, &best, fd); 316 done: 317 close(fd); 318 } 319 320 static 321 void 322 h2pfs_check(int fd, hammer2_blockref_t *bref, cmd_callback callback2) 323 { 324 hammer2_media_data_t media; 325 hammer2_blockref_t *bscan; 326 int bcount; 327 int i; 328 size_t bytes; 329 size_t io_bytes; 330 size_t boff; 331 uint32_t cv; 332 uint64_t cv64; 333 hammer2_off_t io_off; 334 hammer2_off_t io_base; 335 336 bytes = (size_t)1 << (bref->data_off & HAMMER2_OFF_MASK_RADIX); 337 338 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 339 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1); 340 io_bytes = bytes; 341 boff = io_off - io_base; 342 343 io_bytes = HAMMER2_LBUFSIZE; 344 while (io_bytes + boff < bytes) 345 io_bytes <<= 1; 346 347 if (io_bytes > sizeof(media)) { 348 printf("(bad block size %zu)\n", bytes); 349 return; 350 } 351 if (bref->type != HAMMER2_BREF_TYPE_DATA) { 352 lseek(fd, io_base, SEEK_SET); 353 if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) { 354 printf("(media read failed)\n"); 355 return; 356 } 357 if (boff) 358 bcopy((char *)&media + boff, &media, bytes); 359 } 360 361 bscan = NULL; 362 bcount = 0; 363 364 /* 365 * Check data integrity in verbose mode, otherwise we are just doing 366 * a quick meta-data scan. Meta-data integrity is always checked. 367 * (Also see the check above that ensures the media data is loaded, 368 * otherwise there's no data to check!). 369 */ 370 if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) { 371 switch(HAMMER2_DEC_CHECK(bref->methods)) { 372 case HAMMER2_CHECK_NONE: 373 break; 374 case HAMMER2_CHECK_DISABLED: 375 break; 376 case HAMMER2_CHECK_ISCSI32: 377 cv = hammer2_icrc32(&media, bytes); 378 if (bref->check.iscsi32.value != cv) { 379 printf("\t(icrc failed %02x:%08x/%08x)\n", 380 bref->methods, 381 bref->check.iscsi32.value, 382 cv); 383 } 384 break; 385 case HAMMER2_CHECK_XXHASH64: 386 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED); 387 if (bref->check.xxhash64.value != cv64) { 388 printf("\t(xxhash failed %02x:%016jx/%016jx)\n", 389 bref->methods, 390 bref->check.xxhash64.value, 391 cv64); 392 } 393 break; 394 case HAMMER2_CHECK_SHA192: 395 break; 396 case HAMMER2_CHECK_FREEMAP: 397 cv = hammer2_icrc32(&media, bytes); 398 if (bref->check.freemap.icrc32 != cv) { 399 printf("\t(fcrc %02x:%08x/%08x)\n", 400 bref->methods, 401 bref->check.freemap.icrc32, 402 cv); 403 } 404 break; 405 } 406 } 407 408 switch(bref->type) { 409 case HAMMER2_BREF_TYPE_INODE: 410 if (media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) { 411 if ((media.ipdata.meta.op_flags & 412 HAMMER2_OPFLAG_DIRECTDATA) == 0) { 413 bscan = &media.ipdata.u.blockset.blockref[0]; 414 bcount = HAMMER2_SET_COUNT; 415 } 416 } else if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) { 417 callback2((char*)media.ipdata.filename, bref, fd); 418 bscan = NULL; 419 bcount = 0; 420 } else { 421 bscan = NULL; 422 bcount = 0; 423 } 424 break; 425 case HAMMER2_BREF_TYPE_INDIRECT: 426 bscan = &media.npdata[0]; 427 bcount = bytes / sizeof(hammer2_blockref_t); 428 break; 429 case HAMMER2_BREF_TYPE_VOLUME: 430 bscan = &media.voldata.sroot_blockset.blockref[0]; 431 bcount = HAMMER2_SET_COUNT; 432 break; 433 default: 434 break; 435 } 436 for (i = 0; i < bcount; ++i) { 437 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) 438 h2pfs_check(fd, &bscan[i], callback2); 439 } 440 } 441