xref: /dragonfly/sbin/hammer2/cmd_info.c (revision 5ca0a96d)
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 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
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
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 
186 	hammer2_uuid_to_str(&meta->pfs_clid, &pfs_id_str);
187 	if (meta->pfs_type == HAMMER2_PFSTYPE_MASTER) {
188 		if (meta->pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
189 			type_str = "MASTER";
190 		else
191 			type_str = hammer2_pfssubtype_to_str(meta->pfs_subtype);
192 	} else {
193 		type_str = hammer2_pfstype_to_str(meta->pfs_type);
194 	}
195 	e = calloc(1, sizeof(*e));
196 	snprintf(e->name, sizeof(e->name), "%s", ipdata->filename);
197 	snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str);
198 	free(pfs_id_str);
199 
200 	p = TAILQ_FIRST(&head);
201 	while (p) {
202 		if (strcmp(e->name, p->name) <= 0) {
203 			TAILQ_INSERT_BEFORE(p, e, entry);
204 			break;
205 		}
206 		p = TAILQ_NEXT(p, entry);
207 	}
208 	if (!p)
209 		TAILQ_INSERT_TAIL(&head, e, entry);
210 }
211 
212 static void mount_callback1(const void *, hammer2_blockref_t *, int);
213 static void mount_callback2(const void *, hammer2_blockref_t *, int);
214 static void cmd_mountall_alarm(int signo);
215 
216 static volatile sig_atomic_t DidAlarm;
217 
218 int
219 cmd_mountall(int ac, const char **av)
220 {
221 	int i;
222 	pid_t pid;
223 
224 	for (i = 0; i < ac; ++i)
225 		h2disk_check(av[i], mount_callback1);
226 	if (ac == 0) {
227 		h2disk_check_serno(mount_callback1);
228 		h2disk_check_dm(mount_callback1);
229 		h2disk_check_misc(mount_callback1);
230 	}
231 	signal(SIGALRM, cmd_mountall_alarm);
232 	for (;;) {
233 		alarm(15);
234 		pid = wait3(NULL, 0, NULL);
235 		if (pid < 0 && errno == ECHILD)
236 			break;
237 		if (pid < 0 && DidAlarm) {
238 			printf("Timeout waiting for mounts to complete\n");
239 			break;
240 		}
241 	}
242 	alarm(0);
243 
244 	return 0;
245 }
246 
247 static
248 void
249 cmd_mountall_alarm(int signo __unused)
250 {
251 	DidAlarm = 1;
252 }
253 
254 static const char *mount_path;
255 static const char *mount_comp;
256 
257 static
258 void
259 mount_callback1(const void *devpath, hammer2_blockref_t *bref, int fd)
260 {
261 	mount_path = devpath;
262 	mount_comp = strrchr(devpath, '/');
263 	if (mount_comp) {
264 		++mount_comp;
265 		h2pfs_check(fd, bref, mount_callback2);
266 	}
267 }
268 
269 static
270 void
271 mount_callback2(const void *data,
272 		hammer2_blockref_t *bref __unused, int fd)
273 {
274 	const hammer2_inode_data_t *ipdata = data;
275 	char *tmp_path;
276 	char *label;
277 	int tfd;
278 
279 	if (strcmp(ipdata->filename, "LOCAL") == 0) {
280 		if ((tfd = open("/dev/null", O_RDONLY)) >= 0) {
281 			dup2(tfd, fd);
282 			close(tfd);
283 		} else {
284 			perror("open(/dev/null)");
285 			exit(1);
286 		}
287 		asprintf(&tmp_path, "/var/hammer2/LOCAL.%s", mount_comp);
288 		asprintf(&label, "%s@LOCAL", mount_path);
289 		mkdir("/var/hammer2", 0700);
290 		mkdir(tmp_path, 0700);
291 		printf("mount %s\n", tmp_path);
292 		if (fork() == 0) {
293 			execl("/sbin/mount_hammer2",
294 			      "mount",
295 			      label,
296 			      tmp_path,
297 			      NULL);
298 		}
299 		free(label);
300 		free(tmp_path);
301 	}
302 }
303 
304 static
305 void
306 h2disk_check(const char *devpath, cmd_callback callback1)
307 {
308 	hammer2_blockref_t broot;
309 	hammer2_blockref_t best;
310 	hammer2_media_data_t media;
311 	hammer2_volume_data_t *voldata;
312 	struct partinfo partinfo;
313 	int fd;
314 	int i;
315 	int best_i;
316 
317 	fd = open(devpath, O_RDONLY);
318 	if (fd < 0) {
319 		fprintf(stderr, "Unable to open \"%s\"\n", devpath);
320 		return;
321 	}
322 	if (ioctl(fd, DIOCGPART, &partinfo) == -1) {
323 		fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath);
324 		goto done;
325 	}
326 
327 	/*
328 	 * Check partition or slice for HAMMER2 designation.  Validate the
329 	 * designation either from the fstype (typically set for disklabel
330 	 * partitions), or the fstype_uuid (typically set for direct-mapped
331 	 * hammer2 GPT slices).
332 	 */
333 	if (partinfo.fstype != FS_HAMMER2) {
334 		uint32_t status;
335 		uuid_t h2uuid;
336 		int is_nil = uuid_is_nil(&partinfo.fstype_uuid, NULL);
337 
338 		uuid_from_string(HAMMER2_UUID_STRING, &h2uuid, &status);
339 		if (!is_nil && (status != uuid_s_ok ||
340 		    uuid_compare(&partinfo.fstype_uuid, &h2uuid, NULL) != 0)) {
341 			goto done;
342 		}
343 	}
344 
345 	/*
346 	 * Find the best volume header.
347 	 */
348 	best_i = -1;
349 	bzero(&best, sizeof(best));
350 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
351 		bzero(&broot, sizeof(broot));
352 		broot.type = HAMMER2_BREF_TYPE_VOLUME;
353 		broot.data_off = (i * HAMMER2_ZONE_BYTES64) |
354 				 HAMMER2_PBUFRADIX;
355 		lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
356 		if (read(fd, &media, HAMMER2_PBUFSIZE) !=
357 		    (ssize_t)HAMMER2_PBUFSIZE)
358 			continue;
359 		voldata = &media.voldata;
360 		if (voldata->magic != HAMMER2_VOLUME_ID_HBO)
361 			continue;
362 		/* XXX multiple volumes currently unsupported */
363 		if (voldata->nvolumes > 1)
364 			break;
365 		broot.mirror_tid = voldata->mirror_tid;
366 		if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
367 			best_i = i;
368 			best = broot;
369 		}
370 	}
371 	if (best_i >= 0)
372 		callback1(devpath, &best, fd);
373 done:
374 	close(fd);
375 }
376 
377 static
378 void
379 h2pfs_check(int fd, hammer2_blockref_t *bref, cmd_callback callback2)
380 {
381 	hammer2_media_data_t media;
382 	hammer2_blockref_t *bscan;
383 	int bcount;
384 	int i;
385 	size_t bytes;
386 	size_t io_bytes;
387 	size_t boff;
388 	uint32_t cv;
389 	uint64_t cv64;
390 	hammer2_off_t io_off;
391 	hammer2_off_t io_base;
392 
393 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
394 	if (bytes)
395 		bytes = (size_t)1 << bytes;
396 
397 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
398 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
399 	io_bytes = bytes;
400 	boff = io_off - io_base;
401 
402 	io_bytes = HAMMER2_LBUFSIZE;
403 	while (io_bytes + boff < bytes)
404 		io_bytes <<= 1;
405 
406 	if (io_bytes > sizeof(media)) {
407 		printf("(bad block size %zu)\n", bytes);
408 		return;
409 	}
410 	if (bref->type != HAMMER2_BREF_TYPE_DATA) {
411 		lseek(fd, io_base, SEEK_SET);
412 		if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) {
413 			printf("(media read failed)\n");
414 			return;
415 		}
416 		if (boff)
417 			bcopy((char *)&media + boff, &media, bytes);
418 	}
419 
420 	bscan = NULL;
421 	bcount = 0;
422 
423 	/*
424 	 * Check data integrity in verbose mode, otherwise we are just doing
425 	 * a quick meta-data scan.  Meta-data integrity is always checked.
426 	 * (Also see the check above that ensures the media data is loaded,
427 	 * otherwise there's no data to check!).
428 	 */
429 	if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
430 		switch(HAMMER2_DEC_CHECK(bref->methods)) {
431 		case HAMMER2_CHECK_NONE:
432 			break;
433 		case HAMMER2_CHECK_DISABLED:
434 			break;
435 		case HAMMER2_CHECK_ISCSI32:
436 			cv = hammer2_icrc32(&media, bytes);
437 			if (bref->check.iscsi32.value != cv) {
438 				printf("\t(icrc failed %02x:%08x/%08x)\n",
439 				       bref->methods,
440 				       bref->check.iscsi32.value,
441 				       cv);
442 			}
443 			break;
444 		case HAMMER2_CHECK_XXHASH64:
445 			cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
446 			if (bref->check.xxhash64.value != cv64) {
447 				printf("\t(xxhash failed %02x:%016jx/%016jx)\n",
448 				       bref->methods,
449 				       bref->check.xxhash64.value,
450 				       cv64);
451 			}
452 			break;
453 		case HAMMER2_CHECK_SHA192:
454 			break;
455 		case HAMMER2_CHECK_FREEMAP:
456 			cv = hammer2_icrc32(&media, bytes);
457 			if (bref->check.freemap.icrc32 != cv) {
458 				printf("\t(fcrc %02x:%08x/%08x)\n",
459 					bref->methods,
460 					bref->check.freemap.icrc32,
461 					cv);
462 			}
463 			break;
464 		}
465 	}
466 
467 	switch(bref->type) {
468 	case HAMMER2_BREF_TYPE_INODE:
469 		if (media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
470 			if ((media.ipdata.meta.op_flags &
471 			     HAMMER2_OPFLAG_DIRECTDATA) == 0) {
472 				bscan = &media.ipdata.u.blockset.blockref[0];
473 				bcount = HAMMER2_SET_COUNT;
474 			}
475 		} else if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
476 			callback2(&media.ipdata, bref, fd);
477 			bscan = NULL;
478 			bcount = 0;
479 		} else {
480 			bscan = NULL;
481 			bcount = 0;
482 		}
483 		break;
484 	case HAMMER2_BREF_TYPE_INDIRECT:
485 		bscan = &media.npdata[0];
486 		bcount = bytes / sizeof(hammer2_blockref_t);
487 		break;
488 	case HAMMER2_BREF_TYPE_VOLUME:
489 		bscan = &media.voldata.sroot_blockset.blockref[0];
490 		bcount = HAMMER2_SET_COUNT;
491 		break;
492 	default:
493 		break;
494 	}
495 	for (i = 0; i < bcount; ++i) {
496 		if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
497 			h2pfs_check(fd, &bscan[i], callback2);
498 	}
499 }
500