xref: /dragonfly/sbin/hammer2/cmd_info.c (revision 7d3e9a5b)
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 	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
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
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
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
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
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
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