xref: /dragonfly/sbin/hammer2/cmd_info.c (revision a31d3627)
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 		type_str = hammer2_pfssubtype_to_str(meta->pfs_subtype);
189 	else
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);
194 	free(pfs_id_str);
195 
196 	p = TAILQ_FIRST(&head);
197 	while (p) {
198 		if (strcmp(e->name, p->name) <= 0) {
199 			TAILQ_INSERT_BEFORE(p, e, entry);
200 			break;
201 		}
202 		p = TAILQ_NEXT(p, entry);
203 	}
204 	if (!p)
205 		TAILQ_INSERT_TAIL(&head, e, entry);
206 }
207 
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);
211 
212 static volatile sig_atomic_t DidAlarm;
213 
214 int
215 cmd_mountall(int ac, const char **av)
216 {
217 	int i;
218 	pid_t pid;
219 
220 	for (i = 0; i < ac; ++i)
221 		h2disk_check(av[i], mount_callback1);
222 	if (ac == 0) {
223 		h2disk_check_serno(mount_callback1);
224 		h2disk_check_dm(mount_callback1);
225 		h2disk_check_misc(mount_callback1);
226 	}
227 	signal(SIGALRM, cmd_mountall_alarm);
228 	for (;;) {
229 		alarm(15);
230 		pid = wait3(NULL, 0, NULL);
231 		if (pid < 0 && errno == ECHILD)
232 			break;
233 		if (pid < 0 && DidAlarm) {
234 			printf("Timeout waiting for mounts to complete\n");
235 			break;
236 		}
237 	}
238 	alarm(0);
239 
240 	return 0;
241 }
242 
243 static
244 void
245 cmd_mountall_alarm(int signo __unused)
246 {
247 	DidAlarm = 1;
248 }
249 
250 static const char *mount_path;
251 static const char *mount_comp;
252 
253 static
254 void
255 mount_callback1(const void *devpath, hammer2_blockref_t *bref, int fd)
256 {
257 	mount_path = devpath;
258 	mount_comp = strrchr(devpath, '/');
259 	if (mount_comp) {
260 		++mount_comp;
261 		h2pfs_check(fd, bref, mount_callback2);
262 	}
263 }
264 
265 static
266 void
267 mount_callback2(const void *data,
268 		hammer2_blockref_t *bref __unused, int fd)
269 {
270 	const hammer2_inode_data_t *ipdata = data;
271 	char *tmp_path;
272 	char *label;
273 	int tfd;
274 
275 	if (strcmp(ipdata->filename, "LOCAL") == 0) {
276 		if ((tfd = open("/dev/null", O_RDONLY)) >= 0) {
277 			dup2(tfd, fd);
278 			close(tfd);
279 		} else {
280 			perror("open(/dev/null)");
281 			exit(1);
282 		}
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);
288 		if (fork() == 0) {
289 			execl("/sbin/mount_hammer2",
290 			      "mount",
291 			      label,
292 			      tmp_path,
293 			      NULL);
294 		}
295 		free(label);
296 		free(tmp_path);
297 	}
298 }
299 
300 static
301 void
302 h2disk_check(const char *devpath, cmd_callback callback1)
303 {
304 	hammer2_blockref_t broot;
305 	hammer2_blockref_t best;
306 	hammer2_media_data_t media;
307 	struct partinfo partinfo;
308 	int fd;
309 	int i;
310 	int best_i;
311 
312 	fd = open(devpath, O_RDONLY);
313 	if (fd < 0) {
314 		fprintf(stderr, "Unable to open \"%s\"\n", devpath);
315 		return;
316 	}
317 	if (ioctl(fd, DIOCGPART, &partinfo) == -1) {
318 		fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath);
319 		goto done;
320 	}
321 
322 	/*
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).
327 	 */
328 	if (partinfo.fstype != FS_HAMMER2) {
329 		uint32_t status;
330 		uuid_t h2uuid;
331 		int is_nil = uuid_is_nil(&partinfo.fstype_uuid, NULL);
332 
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)) {
336 			goto done;
337 		}
338 	}
339 
340 	/*
341 	 * Find the best volume header.
342 	 */
343 	best_i = -1;
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) |
349 				 HAMMER2_PBUFRADIX;
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) {
356 				best_i = i;
357 				best = broot;
358 			}
359 		}
360 	}
361 	if (best_i >= 0)
362 		callback1(devpath, &best, fd);
363 done:
364 	close(fd);
365 }
366 
367 static
368 void
369 h2pfs_check(int fd, hammer2_blockref_t *bref, cmd_callback callback2)
370 {
371 	hammer2_media_data_t media;
372 	hammer2_blockref_t *bscan;
373 	int bcount;
374 	int i;
375 	size_t bytes;
376 	size_t io_bytes;
377 	size_t boff;
378 	uint32_t cv;
379 	uint64_t cv64;
380 	hammer2_off_t io_off;
381 	hammer2_off_t io_base;
382 
383 	bytes = (size_t)1 << (bref->data_off & HAMMER2_OFF_MASK_RADIX);
384 
385 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
386 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
387 	io_bytes = bytes;
388 	boff = io_off - io_base;
389 
390 	io_bytes = HAMMER2_LBUFSIZE;
391 	while (io_bytes + boff < bytes)
392 		io_bytes <<= 1;
393 
394 	if (io_bytes > sizeof(media)) {
395 		printf("(bad block size %zu)\n", bytes);
396 		return;
397 	}
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");
402 			return;
403 		}
404 		if (boff)
405 			bcopy((char *)&media + boff, &media, bytes);
406 	}
407 
408 	bscan = NULL;
409 	bcount = 0;
410 
411 	/*
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!).
416 	 */
417 	if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
418 		switch(HAMMER2_DEC_CHECK(bref->methods)) {
419 		case HAMMER2_CHECK_NONE:
420 			break;
421 		case HAMMER2_CHECK_DISABLED:
422 			break;
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",
427 				       bref->methods,
428 				       bref->check.iscsi32.value,
429 				       cv);
430 			}
431 			break;
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",
436 				       bref->methods,
437 				       bref->check.xxhash64.value,
438 				       cv64);
439 			}
440 			break;
441 		case HAMMER2_CHECK_SHA192:
442 			break;
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",
447 					bref->methods,
448 					bref->check.freemap.icrc32,
449 					cv);
450 			}
451 			break;
452 		}
453 	}
454 
455 	switch(bref->type) {
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;
462 			}
463 		} else if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
464 			callback2(&media.ipdata, bref, fd);
465 			bscan = NULL;
466 			bcount = 0;
467 		} else {
468 			bscan = NULL;
469 			bcount = 0;
470 		}
471 		break;
472 	case HAMMER2_BREF_TYPE_INDIRECT:
473 		bscan = &media.npdata[0];
474 		bcount = bytes / sizeof(hammer2_blockref_t);
475 		break;
476 	case HAMMER2_BREF_TYPE_VOLUME:
477 		bscan = &media.voldata.sroot_blockset.blockref[0];
478 		bcount = HAMMER2_SET_COUNT;
479 		break;
480 	default:
481 		break;
482 	}
483 	for (i = 0; i < bcount; ++i) {
484 		if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
485 			h2pfs_check(fd, &bscan[i], callback2);
486 	}
487 }
488