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
h2disk_check_serno(cmd_callback fn)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
h2disk_check_dm(cmd_callback fn)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
h2disk_check_misc(cmd_callback fn)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
cmd_info(int ac,const char ** av)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
info_callback1(const void * path,hammer2_blockref_t * bref,int fd)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
info_callback2(const void * data,hammer2_blockref_t * bref __unused,int fd __unused)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
cmd_mountall(int ac,const char ** av)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
cmd_mountall_alarm(int signo __unused)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
mount_callback1(const void * devpath,hammer2_blockref_t * bref,int fd)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
mount_callback2(const void * data,hammer2_blockref_t * bref __unused,int fd)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
h2disk_check(const char * devpath,cmd_callback callback1)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
h2pfs_check(int fd,hammer2_blockref_t * bref,cmd_callback callback2)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