xref: /dragonfly/sbin/hammer2/cmd_info.c (revision e5a92d33)
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 static void h2disk_check(const char *devpath,
37 		    void (*callback1)(const char *, hammer2_blockref_t *, int));
38 static void h2pfs_check(int fd, hammer2_blockref_t *bref,
39 		    void (*callback2)(const char *, hammer2_blockref_t *, int));
40 
41 static void info_callback1(const char *, hammer2_blockref_t *, int);
42 static void info_callback2(const char *, hammer2_blockref_t *, int);
43 
44 typedef void (*cmd_callback)(const char *, hammer2_blockref_t *, int);
45 
46 static
47 void
48 h2disk_check_serno(cmd_callback fn)
49 {
50 	DIR *dir;
51 
52 	if ((dir = opendir("/dev/serno")) != NULL) {
53 		struct dirent *den;
54 
55 		while ((den = readdir(dir)) != NULL) {
56 			const char *ptr;
57 			int slice;
58 			char part;
59 			char *devpath;
60 
61 			if (!strcmp(den->d_name, ".") ||
62 			    !strcmp(den->d_name, ".."))
63 				continue;
64 			ptr = strrchr(den->d_name, '.');
65 			if (ptr && sscanf(ptr, ".s%d%c", &slice, &part) == 2) {
66 				asprintf(&devpath, "/dev/serno/%s",
67 					 den->d_name);
68 				h2disk_check(devpath, fn);
69 				free(devpath);
70 			}
71 		}
72 		closedir(dir);
73 	}
74 }
75 
76 static
77 void
78 h2disk_check_dm(cmd_callback fn)
79 {
80 	DIR *dir;
81 
82 	if ((dir = opendir("/dev/mapper")) != NULL) {
83 		struct dirent *den;
84 
85 		while ((den = readdir(dir)) != NULL) {
86 			char *devpath;
87 
88 			if (!strcmp(den->d_name, ".") ||
89 			    !strcmp(den->d_name, "..") ||
90 			    !strcmp(den->d_name, "control"))
91 				continue;
92 			asprintf(&devpath, "/dev/mapper/%s",
93 				 den->d_name);
94 			h2disk_check(devpath, fn);
95 			free(devpath);
96 		}
97 		closedir(dir);
98 	}
99 }
100 
101 static
102 void
103 h2disk_check_misc(cmd_callback fn)
104 {
105 	DIR *dir;
106 
107 	if ((dir = opendir("/dev")) != NULL) {
108 		struct dirent *den;
109 
110 		while ((den = readdir(dir)) != NULL) {
111 			char *devpath;
112 
113 			if (!strcmp(den->d_name, ".") ||
114 			    !strcmp(den->d_name, ".."))
115 				continue;
116 			if (strncmp(den->d_name, "ad", 2) &&
117 			    strncmp(den->d_name, "vn", 2))
118 				continue;
119 			if (!strcmp(den->d_name, "vn"))
120 				continue;
121 			if (!strncmp(den->d_name, "ad", 2) &&
122 			    den->d_namlen <= 3)
123 				continue;
124 			asprintf(&devpath, "/dev/%s", den->d_name);
125 			h2disk_check(devpath, fn);
126 			free(devpath);
127 		}
128 		closedir(dir);
129 	}
130 }
131 
132 int
133 cmd_info(int ac, const char **av)
134 {
135 	int i;
136 
137 	for (i = 0; i < ac; ++i)
138 		h2disk_check(av[i], info_callback1);
139 	if (ac == 0) {
140 		h2disk_check_serno(info_callback1);
141 		h2disk_check_dm(info_callback1);
142 		h2disk_check_misc(info_callback1);
143 	}
144 	return 0;
145 }
146 
147 static
148 void
149 info_callback1(const char *path, hammer2_blockref_t *bref, int fd)
150 {
151 	printf("%s:\n", path);
152 	h2pfs_check(fd, bref, info_callback2);
153 }
154 
155 static
156 void
157 info_callback2(const char *pfsname,
158 	       hammer2_blockref_t *bref __unused, int fd __unused)
159 {
160 	printf("    %s\n", pfsname);
161 }
162 
163 static void mount_callback1(const char *, hammer2_blockref_t *, int);
164 static void mount_callback2(const char *, hammer2_blockref_t *, int);
165 static void cmd_mountall_alarm(int signo);
166 
167 static volatile sig_atomic_t DidAlarm;
168 
169 int
170 cmd_mountall(int ac, const char **av)
171 {
172 	int i;
173 	pid_t pid;
174 
175 	for (i = 0; i < ac; ++i)
176 		h2disk_check(av[i], mount_callback1);
177 	if (ac == 0) {
178 		h2disk_check_serno(mount_callback1);
179 		h2disk_check_dm(mount_callback1);
180 		h2disk_check_misc(mount_callback1);
181 	}
182 	signal(SIGALRM, cmd_mountall_alarm);
183 	for (;;) {
184 		alarm(15);
185 		pid = wait3(NULL, 0, NULL);
186 		if (pid < 0 && errno == ECHILD)
187 			break;
188 		if (pid < 0 && DidAlarm) {
189 			printf("Timeout waiting for mounts to complete\n");
190 			break;
191 		}
192 	}
193 	alarm(0);
194 
195 	return 0;
196 }
197 
198 static
199 void
200 cmd_mountall_alarm(int signo __unused)
201 {
202 	DidAlarm = 1;
203 }
204 
205 static const char *mount_path;
206 static const char *mount_comp;
207 
208 static
209 void
210 mount_callback1(const char *devpath, hammer2_blockref_t *bref, int fd)
211 {
212 	mount_path = devpath;
213 	mount_comp = strrchr(devpath, '/');
214 	if (mount_comp) {
215 		++mount_comp;
216 		h2pfs_check(fd, bref, mount_callback2);
217 	}
218 }
219 
220 static
221 void
222 mount_callback2(const char *pfsname,
223 		hammer2_blockref_t *bref __unused, int fd)
224 {
225 	char *tmp_path;
226 	char *label;
227 	int tfd;
228 
229 	if (strcmp(pfsname, "LOCAL") == 0) {
230 		if ((tfd = open("/dev/null", O_RDONLY)) >= 0) {
231 			dup2(tfd, fd);
232 			close(tfd);
233 		} else {
234 			perror("open(/dev/null)");
235 			exit(1);
236 		}
237 		asprintf(&tmp_path, "/var/hammer2/LOCAL.%s", mount_comp);
238 		asprintf(&label, "%s@LOCAL", mount_path);
239 		mkdir("/var/hammer2", 0700);
240 		mkdir(tmp_path, 0700);
241 		printf("mount %s\n", tmp_path);
242 		if (fork() == 0) {
243 			execl("/sbin/mount_hammer2",
244 			      "mount",
245 			      label,
246 			      tmp_path,
247 			      NULL);
248 		}
249 		free(label);
250 		free(tmp_path);
251 	}
252 }
253 
254 /*
255  * Support
256  */
257 static
258 void
259 h2disk_check(const char *devpath,
260 	     void (*callback1)(const char *, hammer2_blockref_t *, int))
261 {
262 	hammer2_blockref_t broot;
263 	hammer2_blockref_t best;
264 	hammer2_media_data_t media;
265 	struct partinfo partinfo;
266 	int fd;
267 	int i;
268 	int best_i;
269 
270 	fd = open(devpath, O_RDONLY);
271 	if (fd < 0) {
272 		fprintf(stderr, "Unable to open \"%s\"\n", devpath);
273 		return;
274 	}
275 	if (ioctl(fd, DIOCGPART, &partinfo) == -1) {
276 		fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath);
277 		goto done;
278 	}
279 
280 	/*
281 	 * Check partition or slice for HAMMER2 designation.  Validate the
282 	 * designation either from the fstype (typically set for disklabel
283 	 * partitions), or the fstype_uuid (typically set for direct-mapped
284 	 * hammer2 GPT slices).
285 	 */
286 	if (partinfo.fstype != FS_HAMMER2) {
287 		uint32_t status;
288 		uuid_t h2uuid;
289 		int is_nil = uuid_is_nil(&partinfo.fstype_uuid, NULL);
290 
291 		uuid_from_string(HAMMER2_UUID_STRING, &h2uuid, &status);
292 		if (!is_nil && (status != uuid_s_ok ||
293 		    uuid_compare(&partinfo.fstype_uuid, &h2uuid, NULL) != 0)) {
294 			goto done;
295 		}
296 	}
297 
298 	/*
299 	 * Find the best volume header.
300 	 */
301 	best_i = -1;
302 	bzero(&best, sizeof(best));
303 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
304 		bzero(&broot, sizeof(broot));
305 		broot.type = HAMMER2_BREF_TYPE_VOLUME;
306 		broot.data_off = (i * HAMMER2_ZONE_BYTES64) |
307 				 HAMMER2_PBUFRADIX;
308 		lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
309 		if (read(fd, &media, HAMMER2_PBUFSIZE) ==
310 		    (ssize_t)HAMMER2_PBUFSIZE &&
311 		    media.voldata.magic == HAMMER2_VOLUME_ID_HBO) {
312 			broot.mirror_tid = media.voldata.mirror_tid;
313 			if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
314 				best_i = i;
315 				best = broot;
316 			}
317 		}
318 	}
319 	if (best_i >= 0)
320 		callback1(devpath, &best, fd);
321 done:
322 	close(fd);
323 }
324 
325 static
326 void
327 h2pfs_check(int fd, hammer2_blockref_t *bref,
328 	    void (*callback2)(const char *, hammer2_blockref_t *, int))
329 {
330 	hammer2_media_data_t media;
331 	hammer2_blockref_t *bscan;
332 	int bcount;
333 	int i;
334 	size_t bytes;
335 	size_t io_bytes;
336 	size_t boff;
337 	uint32_t cv;
338 	uint64_t cv64;
339 	hammer2_off_t io_off;
340 	hammer2_off_t io_base;
341 
342 	bytes = (size_t)1 << (bref->data_off & HAMMER2_OFF_MASK_RADIX);
343 
344 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
345 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
346 	io_bytes = bytes;
347 	boff = io_off - io_base;
348 
349 	io_bytes = HAMMER2_MINIOSIZE;
350 	while (io_bytes + boff < bytes)
351 		io_bytes <<= 1;
352 
353 	if (io_bytes > sizeof(media)) {
354 		printf("(bad block size %zd)\n", bytes);
355 		return;
356 	}
357 	if (bref->type != HAMMER2_BREF_TYPE_DATA) {
358 		lseek(fd, io_base, SEEK_SET);
359 		if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) {
360 			printf("(media read failed)\n");
361 			return;
362 		}
363 		if (boff)
364 			bcopy((char *)&media + boff, &media, bytes);
365 	}
366 
367 	bscan = NULL;
368 	bcount = 0;
369 
370 	/*
371 	 * Check data integrity in verbose mode, otherwise we are just doing
372 	 * a quick meta-data scan.  Meta-data integrity is always checked.
373 	 * (Also see the check above that ensures the media data is loaded,
374 	 * otherwise there's no data to check!).
375 	 */
376 	if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
377 		switch(HAMMER2_DEC_CHECK(bref->methods)) {
378 		case HAMMER2_CHECK_NONE:
379 			break;
380 		case HAMMER2_CHECK_DISABLED:
381 			break;
382 		case HAMMER2_CHECK_ISCSI32:
383 			cv = hammer2_icrc32(&media, bytes);
384 			if (bref->check.iscsi32.value != cv) {
385 				printf("\t(icrc failed %02x:%08x/%08x)\n",
386 				       bref->methods,
387 				       bref->check.iscsi32.value,
388 				       cv);
389 			}
390 			break;
391 		case HAMMER2_CHECK_XXHASH64:
392 			cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
393 			if (bref->check.xxhash64.value != cv64) {
394 				printf("\t(xxhash failed %02x:%016jx/%016jx)\n",
395 				       bref->methods,
396 				       bref->check.xxhash64.value,
397 				       cv64);
398 			}
399 			break;
400 		case HAMMER2_CHECK_SHA192:
401 			break;
402 		case HAMMER2_CHECK_FREEMAP:
403 			cv = hammer2_icrc32(&media, bytes);
404 			if (bref->check.freemap.icrc32 != cv) {
405 				printf("\t(fcrc %02x:%08x/%08x)\n",
406 					bref->methods,
407 					bref->check.freemap.icrc32,
408 					cv);
409 			}
410 			break;
411 		}
412 	}
413 
414 	switch(bref->type) {
415 	case HAMMER2_BREF_TYPE_EMPTY:
416 		break;
417 	case HAMMER2_BREF_TYPE_INODE:
418 		if (media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
419 			if ((media.ipdata.meta.op_flags &
420 			     HAMMER2_OPFLAG_DIRECTDATA) == 0) {
421 				bscan = &media.ipdata.u.blockset.blockref[0];
422 				bcount = HAMMER2_SET_COUNT;
423 			}
424 		} else
425 		if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
426 			callback2((char*)media.ipdata.filename, bref, fd);
427 			bscan = NULL;
428 			bcount = 0;
429 		} else {
430 			bscan = NULL;
431 			bcount = 0;
432 		}
433 		break;
434 	case HAMMER2_BREF_TYPE_INDIRECT:
435 		bscan = &media.npdata[0];
436 		bcount = bytes / sizeof(hammer2_blockref_t);
437 		break;
438 	case HAMMER2_BREF_TYPE_DATA:
439 		break;
440 	case HAMMER2_BREF_TYPE_VOLUME:
441 		bscan = &media.voldata.sroot_blockset.blockref[0];
442 		bcount = HAMMER2_SET_COUNT;
443 		break;
444 	default:
445 		break;
446 	}
447 	for (i = 0; i < bcount; ++i) {
448 		if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
449 			h2pfs_check(fd, &bscan[i], callback2);
450 	}
451 }
452