xref: /dragonfly/sbin/hammer2/cmd_info.c (revision 2b7dbe20)
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 	struct partinfo partinfo;
312 	int fd;
313 	int i;
314 	int best_i;
315 
316 	fd = open(devpath, O_RDONLY);
317 	if (fd < 0) {
318 		fprintf(stderr, "Unable to open \"%s\"\n", devpath);
319 		return;
320 	}
321 	if (ioctl(fd, DIOCGPART, &partinfo) == -1) {
322 		fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath);
323 		goto done;
324 	}
325 
326 	/*
327 	 * Check partition or slice for HAMMER2 designation.  Validate the
328 	 * designation either from the fstype (typically set for disklabel
329 	 * partitions), or the fstype_uuid (typically set for direct-mapped
330 	 * hammer2 GPT slices).
331 	 */
332 	if (partinfo.fstype != FS_HAMMER2) {
333 		uint32_t status;
334 		uuid_t h2uuid;
335 		int is_nil = uuid_is_nil(&partinfo.fstype_uuid, NULL);
336 
337 		uuid_from_string(HAMMER2_UUID_STRING, &h2uuid, &status);
338 		if (!is_nil && (status != uuid_s_ok ||
339 		    uuid_compare(&partinfo.fstype_uuid, &h2uuid, NULL) != 0)) {
340 			goto done;
341 		}
342 	}
343 
344 	/*
345 	 * Find the best volume header.
346 	 */
347 	best_i = -1;
348 	bzero(&best, sizeof(best));
349 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
350 		bzero(&broot, sizeof(broot));
351 		broot.type = HAMMER2_BREF_TYPE_VOLUME;
352 		broot.data_off = (i * HAMMER2_ZONE_BYTES64) |
353 				 HAMMER2_PBUFRADIX;
354 		lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
355 		if (read(fd, &media, HAMMER2_PBUFSIZE) ==
356 		    (ssize_t)HAMMER2_PBUFSIZE &&
357 		    media.voldata.magic == HAMMER2_VOLUME_ID_HBO) {
358 			broot.mirror_tid = media.voldata.mirror_tid;
359 			if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
360 				best_i = i;
361 				best = broot;
362 			}
363 		}
364 	}
365 	if (best_i >= 0)
366 		callback1(devpath, &best, fd);
367 done:
368 	close(fd);
369 }
370 
371 static
372 void
373 h2pfs_check(int fd, hammer2_blockref_t *bref, cmd_callback callback2)
374 {
375 	hammer2_media_data_t media;
376 	hammer2_blockref_t *bscan;
377 	int bcount;
378 	int i;
379 	size_t bytes;
380 	size_t io_bytes;
381 	size_t boff;
382 	uint32_t cv;
383 	uint64_t cv64;
384 	hammer2_off_t io_off;
385 	hammer2_off_t io_base;
386 
387 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
388 	if (bytes)
389 		bytes = (size_t)1 << bytes;
390 
391 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
392 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
393 	io_bytes = bytes;
394 	boff = io_off - io_base;
395 
396 	io_bytes = HAMMER2_LBUFSIZE;
397 	while (io_bytes + boff < bytes)
398 		io_bytes <<= 1;
399 
400 	if (io_bytes > sizeof(media)) {
401 		printf("(bad block size %zu)\n", bytes);
402 		return;
403 	}
404 	if (bref->type != HAMMER2_BREF_TYPE_DATA) {
405 		lseek(fd, io_base, SEEK_SET);
406 		if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) {
407 			printf("(media read failed)\n");
408 			return;
409 		}
410 		if (boff)
411 			bcopy((char *)&media + boff, &media, bytes);
412 	}
413 
414 	bscan = NULL;
415 	bcount = 0;
416 
417 	/*
418 	 * Check data integrity in verbose mode, otherwise we are just doing
419 	 * a quick meta-data scan.  Meta-data integrity is always checked.
420 	 * (Also see the check above that ensures the media data is loaded,
421 	 * otherwise there's no data to check!).
422 	 */
423 	if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
424 		switch(HAMMER2_DEC_CHECK(bref->methods)) {
425 		case HAMMER2_CHECK_NONE:
426 			break;
427 		case HAMMER2_CHECK_DISABLED:
428 			break;
429 		case HAMMER2_CHECK_ISCSI32:
430 			cv = hammer2_icrc32(&media, bytes);
431 			if (bref->check.iscsi32.value != cv) {
432 				printf("\t(icrc failed %02x:%08x/%08x)\n",
433 				       bref->methods,
434 				       bref->check.iscsi32.value,
435 				       cv);
436 			}
437 			break;
438 		case HAMMER2_CHECK_XXHASH64:
439 			cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
440 			if (bref->check.xxhash64.value != cv64) {
441 				printf("\t(xxhash failed %02x:%016jx/%016jx)\n",
442 				       bref->methods,
443 				       bref->check.xxhash64.value,
444 				       cv64);
445 			}
446 			break;
447 		case HAMMER2_CHECK_SHA192:
448 			break;
449 		case HAMMER2_CHECK_FREEMAP:
450 			cv = hammer2_icrc32(&media, bytes);
451 			if (bref->check.freemap.icrc32 != cv) {
452 				printf("\t(fcrc %02x:%08x/%08x)\n",
453 					bref->methods,
454 					bref->check.freemap.icrc32,
455 					cv);
456 			}
457 			break;
458 		}
459 	}
460 
461 	switch(bref->type) {
462 	case HAMMER2_BREF_TYPE_INODE:
463 		if (media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
464 			if ((media.ipdata.meta.op_flags &
465 			     HAMMER2_OPFLAG_DIRECTDATA) == 0) {
466 				bscan = &media.ipdata.u.blockset.blockref[0];
467 				bcount = HAMMER2_SET_COUNT;
468 			}
469 		} else if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
470 			callback2(&media.ipdata, bref, fd);
471 			bscan = NULL;
472 			bcount = 0;
473 		} else {
474 			bscan = NULL;
475 			bcount = 0;
476 		}
477 		break;
478 	case HAMMER2_BREF_TYPE_INDIRECT:
479 		bscan = &media.npdata[0];
480 		bcount = bytes / sizeof(hammer2_blockref_t);
481 		break;
482 	case HAMMER2_BREF_TYPE_VOLUME:
483 		bscan = &media.voldata.sroot_blockset.blockref[0];
484 		bcount = HAMMER2_SET_COUNT;
485 		break;
486 	default:
487 		break;
488 	}
489 	for (i = 0; i < bcount; ++i) {
490 		if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
491 			h2pfs_check(fd, &bscan[i], callback2);
492 	}
493 }
494