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