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