2 * Copyright (c) 2015 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@dragonflybsd.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
36 typedef void (*cmd_callback)(const void *, hammer2_blockref_t *, int);
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);
42 static void info_callback1(const void *, hammer2_blockref_t *, int);
43 static void info_callback2(const void *, hammer2_blockref_t *, int);
47 h2disk_check_serno(cmd_callback fn)
51 if ((dir = opendir("/dev/serno")) != NULL) {
54 while ((den = readdir(dir)) != NULL) {
60 if (!strcmp(den->d_name, ".") ||
61 !strcmp(den->d_name, ".."))
63 ptr = strrchr(den->d_name, '.');
64 if (ptr && sscanf(ptr, ".s%d%c", &slice, &part) == 2) {
65 asprintf(&devpath, "/dev/serno/%s",
67 h2disk_check(devpath, fn);
77 h2disk_check_dm(cmd_callback fn)
81 if ((dir = opendir("/dev/mapper")) != NULL) {
84 while ((den = readdir(dir)) != NULL) {
87 if (!strcmp(den->d_name, ".") ||
88 !strcmp(den->d_name, "..") ||
89 !strcmp(den->d_name, "control"))
91 asprintf(&devpath, "/dev/mapper/%s",
93 h2disk_check(devpath, fn);
102 h2disk_check_misc(cmd_callback fn)
106 if ((dir = opendir("/dev")) != NULL) {
109 while ((den = readdir(dir)) != NULL) {
112 if (!strcmp(den->d_name, ".") ||
113 !strcmp(den->d_name, ".."))
115 if (strncmp(den->d_name, "ad", 2) &&
116 strncmp(den->d_name, "vn", 2))
118 if (!strcmp(den->d_name, "vn"))
120 if (!strncmp(den->d_name, "ad", 2) &&
123 asprintf(&devpath, "/dev/%s", den->d_name);
124 h2disk_check(devpath, fn);
132 cmd_info(int ac, const char **av)
136 for (i = 0; i < ac; ++i)
137 h2disk_check(av[i], info_callback1);
139 h2disk_check_serno(info_callback1);
140 h2disk_check_dm(info_callback1);
141 h2disk_check_misc(info_callback1);
147 TAILQ_ENTRY(pfs_entry) entry;
148 char name[NAME_MAX+1];
152 static TAILQ_HEAD(, pfs_entry) head;
156 info_callback1(const void *path, hammer2_blockref_t *bref, int fd)
160 printf("%s:\n", (const char*)path);
163 h2pfs_check(fd, bref, info_callback2);
166 "ClusterId (pfs_clid) "
168 while ((p = TAILQ_FIRST(&head)) != NULL) {
169 printf(" %s %s\n", p->s, p->name);
170 TAILQ_REMOVE(&head, p, entry);
177 info_callback2(const void *data,
178 hammer2_blockref_t *bref __unused, int fd __unused)
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;
186 hammer2_uuid_to_str(&meta->pfs_clid, &pfs_id_str);
187 if (meta->pfs_type == HAMMER2_PFSTYPE_MASTER)
188 type_str = hammer2_pfssubtype_to_str(meta->pfs_subtype);
190 type_str = hammer2_pfstype_to_str(meta->pfs_type);
191 e = calloc(1, sizeof(*e));
192 snprintf(e->name, sizeof(e->name), "%s", ipdata->filename);
193 snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str);
196 p = TAILQ_FIRST(&head);
198 if (strcmp(e->name, p->name) <= 0) {
199 TAILQ_INSERT_BEFORE(p, e, entry);
202 p = TAILQ_NEXT(p, entry);
205 TAILQ_INSERT_TAIL(&head, e, entry);
208 static void mount_callback1(const void *, hammer2_blockref_t *, int);
209 static void mount_callback2(const void *, hammer2_blockref_t *, int);
210 static void cmd_mountall_alarm(int signo);
212 static volatile sig_atomic_t DidAlarm;
215 cmd_mountall(int ac, const char **av)
220 for (i = 0; i < ac; ++i)
221 h2disk_check(av[i], mount_callback1);
223 h2disk_check_serno(mount_callback1);
224 h2disk_check_dm(mount_callback1);
225 h2disk_check_misc(mount_callback1);
227 signal(SIGALRM, cmd_mountall_alarm);
230 pid = wait3(NULL, 0, NULL);
231 if (pid < 0 && errno == ECHILD)
233 if (pid < 0 && DidAlarm) {
234 printf("Timeout waiting for mounts to complete\n");
245 cmd_mountall_alarm(int signo __unused)
250 static const char *mount_path;
251 static const char *mount_comp;
255 mount_callback1(const void *devpath, hammer2_blockref_t *bref, int fd)
257 mount_path = devpath;
258 mount_comp = strrchr(devpath, '/');
261 h2pfs_check(fd, bref, mount_callback2);
267 mount_callback2(const void *data,
268 hammer2_blockref_t *bref __unused, int fd)
270 const hammer2_inode_data_t *ipdata = data;
275 if (strcmp(ipdata->filename, "LOCAL") == 0) {
276 if ((tfd = open("/dev/null", O_RDONLY)) >= 0) {
280 perror("open(/dev/null)");
283 asprintf(&tmp_path, "/var/hammer2/LOCAL.%s", mount_comp);
284 asprintf(&label, "%s@LOCAL", mount_path);
285 mkdir("/var/hammer2", 0700);
286 mkdir(tmp_path, 0700);
287 printf("mount %s\n", tmp_path);
289 execl("/sbin/mount_hammer2",
302 h2disk_check(const char *devpath, cmd_callback callback1)
304 hammer2_blockref_t broot;
305 hammer2_blockref_t best;
306 hammer2_media_data_t media;
307 struct partinfo partinfo;
312 fd = open(devpath, O_RDONLY);
314 fprintf(stderr, "Unable to open \"%s\"\n", devpath);
317 if (ioctl(fd, DIOCGPART, &partinfo) == -1) {
318 fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath);
323 * Check partition or slice for HAMMER2 designation. Validate the
324 * designation either from the fstype (typically set for disklabel
325 * partitions), or the fstype_uuid (typically set for direct-mapped
326 * hammer2 GPT slices).
328 if (partinfo.fstype != FS_HAMMER2) {
331 int is_nil = uuid_is_nil(&partinfo.fstype_uuid, NULL);
333 uuid_from_string(HAMMER2_UUID_STRING, &h2uuid, &status);
334 if (!is_nil && (status != uuid_s_ok ||
335 uuid_compare(&partinfo.fstype_uuid, &h2uuid, NULL) != 0)) {
341 * Find the best volume header.
344 bzero(&best, sizeof(best));
345 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
346 bzero(&broot, sizeof(broot));
347 broot.type = HAMMER2_BREF_TYPE_VOLUME;
348 broot.data_off = (i * HAMMER2_ZONE_BYTES64) |
350 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
351 if (read(fd, &media, HAMMER2_PBUFSIZE) ==
352 (ssize_t)HAMMER2_PBUFSIZE &&
353 media.voldata.magic == HAMMER2_VOLUME_ID_HBO) {
354 broot.mirror_tid = media.voldata.mirror_tid;
355 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
362 callback1(devpath, &best, fd);
369 h2pfs_check(int fd, hammer2_blockref_t *bref, cmd_callback callback2)
371 hammer2_media_data_t media;
372 hammer2_blockref_t *bscan;
380 hammer2_off_t io_off;
381 hammer2_off_t io_base;
383 bytes = (size_t)1 << (bref->data_off & HAMMER2_OFF_MASK_RADIX);
385 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
386 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
388 boff = io_off - io_base;
390 io_bytes = HAMMER2_LBUFSIZE;
391 while (io_bytes + boff < bytes)
394 if (io_bytes > sizeof(media)) {
395 printf("(bad block size %zu)\n", bytes);
398 if (bref->type != HAMMER2_BREF_TYPE_DATA) {
399 lseek(fd, io_base, SEEK_SET);
400 if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) {
401 printf("(media read failed)\n");
405 bcopy((char *)&media + boff, &media, bytes);
412 * Check data integrity in verbose mode, otherwise we are just doing
413 * a quick meta-data scan. Meta-data integrity is always checked.
414 * (Also see the check above that ensures the media data is loaded,
415 * otherwise there's no data to check!).
417 if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
418 switch(HAMMER2_DEC_CHECK(bref->methods)) {
419 case HAMMER2_CHECK_NONE:
421 case HAMMER2_CHECK_DISABLED:
423 case HAMMER2_CHECK_ISCSI32:
424 cv = hammer2_icrc32(&media, bytes);
425 if (bref->check.iscsi32.value != cv) {
426 printf("\t(icrc failed %02x:%08x/%08x)\n",
428 bref->check.iscsi32.value,
432 case HAMMER2_CHECK_XXHASH64:
433 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
434 if (bref->check.xxhash64.value != cv64) {
435 printf("\t(xxhash failed %02x:%016jx/%016jx)\n",
437 bref->check.xxhash64.value,
441 case HAMMER2_CHECK_SHA192:
443 case HAMMER2_CHECK_FREEMAP:
444 cv = hammer2_icrc32(&media, bytes);
445 if (bref->check.freemap.icrc32 != cv) {
446 printf("\t(fcrc %02x:%08x/%08x)\n",
448 bref->check.freemap.icrc32,
456 case HAMMER2_BREF_TYPE_INODE:
457 if (media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
458 if ((media.ipdata.meta.op_flags &
459 HAMMER2_OPFLAG_DIRECTDATA) == 0) {
460 bscan = &media.ipdata.u.blockset.blockref[0];
461 bcount = HAMMER2_SET_COUNT;
463 } else if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
464 callback2(&media.ipdata, bref, fd);
472 case HAMMER2_BREF_TYPE_INDIRECT:
473 bscan = &media.npdata[0];
474 bcount = bytes / sizeof(hammer2_blockref_t);
476 case HAMMER2_BREF_TYPE_VOLUME:
477 bscan = &media.voldata.sroot_blockset.blockref[0];
478 bcount = HAMMER2_SET_COUNT;
483 for (i = 0; i < bcount; ++i) {
484 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
485 h2pfs_check(fd, &bscan[i], callback2);