xref: /freebsd/lib/geom/mirror/geom_mirror.c (revision 1323ec57)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2004-2009 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5  * All rights reserved.
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  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <paths.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdint.h>
39 #include <string.h>
40 #include <strings.h>
41 #include <assert.h>
42 #include <libgeom.h>
43 #include <geom/mirror/g_mirror.h>
44 #include <core/geom.h>
45 #include <misc/subr.h>
46 
47 uint32_t lib_version = G_LIB_VERSION;
48 uint32_t version = G_MIRROR_VERSION;
49 
50 #define	GMIRROR_BALANCE		"load"
51 #define	GMIRROR_SLICE		"4096"
52 #define	GMIRROR_PRIORITY	"0"
53 
54 static void mirror_main(struct gctl_req *req, unsigned flags);
55 static void mirror_activate(struct gctl_req *req);
56 static void mirror_clear(struct gctl_req *req);
57 static void mirror_dump(struct gctl_req *req);
58 static void mirror_label(struct gctl_req *req);
59 static void mirror_resize(struct gctl_req *req, unsigned flags);
60 
61 struct g_command class_commands[] = {
62 	{ "activate", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS,
63 	    "[-v] name prov ..."
64 	},
65 	{ "clear", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS,
66 	    "[-v] prov ..."
67 	},
68 	{ "configure", G_FLAG_VERBOSE, NULL,
69 	    {
70 		{ 'a', "autosync", NULL, G_TYPE_BOOL },
71 		{ 'b', "balance", "", G_TYPE_STRING },
72 		{ 'd', "dynamic", NULL, G_TYPE_BOOL },
73 		{ 'f', "failsync", NULL, G_TYPE_BOOL },
74 		{ 'F', "nofailsync", NULL, G_TYPE_BOOL },
75 		{ 'h', "hardcode", NULL, G_TYPE_BOOL },
76 		{ 'n', "noautosync", NULL, G_TYPE_BOOL },
77 		{ 'p', "priority", "-1", G_TYPE_NUMBER },
78 		{ 's', "slice", "-1", G_TYPE_NUMBER },
79 		G_OPT_SENTINEL
80 	    },
81 	    "[-adfFhnv] [-b balance] [-s slice] name\n"
82 	    "[-v] -p priority name prov"
83 	},
84 	{ "create", G_FLAG_VERBOSE, NULL,
85 	    {
86 		{ 'b', "balance", GMIRROR_BALANCE, G_TYPE_STRING },
87 		{ 'F', "nofailsync", NULL, G_TYPE_BOOL },
88 		{ 'n', "noautosync", NULL, G_TYPE_BOOL },
89 		{ 's', "slice", GMIRROR_SLICE, G_TYPE_NUMBER },
90 		G_OPT_SENTINEL
91 	    },
92 	    "[-Fnv] [-b balance] [-s slice] name prov ..."
93 	},
94 	{ "deactivate", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
95 	    "[-v] name prov ..."
96 	},
97 	{ "destroy", G_FLAG_VERBOSE, NULL,
98 	    {
99 		{ 'f', "force", NULL, G_TYPE_BOOL },
100 		G_OPT_SENTINEL
101 	    },
102 	    "[-fv] name ..."
103 	},
104 	{ "dump", 0, mirror_main, G_NULL_OPTS,
105 	    "prov ..."
106 	},
107 	{ "forget", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
108 	    "name ..."
109 	},
110 	{ "label", G_FLAG_VERBOSE, mirror_main,
111 	    {
112 		{ 'b', "balance", GMIRROR_BALANCE, G_TYPE_STRING },
113 		{ 'F', "nofailsync", NULL, G_TYPE_BOOL },
114 		{ 'h', "hardcode", NULL, G_TYPE_BOOL },
115 		{ 'n', "noautosync", NULL, G_TYPE_BOOL },
116 		{ 's', "slice", GMIRROR_SLICE, G_TYPE_NUMBER },
117 		G_OPT_SENTINEL
118 	    },
119 	    "[-Fhnv] [-b balance] [-s slice] name prov ..."
120 	},
121 	{ "insert", G_FLAG_VERBOSE, NULL,
122 	    {
123 		{ 'h', "hardcode", NULL, G_TYPE_BOOL },
124 		{ 'i', "inactive", NULL, G_TYPE_BOOL },
125 		{ 'p', "priority", GMIRROR_PRIORITY, G_TYPE_NUMBER },
126 		G_OPT_SENTINEL
127 	    },
128 	    "[-hiv] [-p priority] name prov ..."
129 	},
130 	{ "rebuild", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
131 	    "[-v] name prov ..."
132 	},
133 	{ "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
134 	    "[-v] name prov ..."
135 	},
136 	{ "resize", G_FLAG_VERBOSE, mirror_resize,
137 	    {
138 		{ 's', "size", "*", G_TYPE_STRING },
139 		G_OPT_SENTINEL
140 	    },
141 	    "[-s size] [-v] name"
142 	},
143 	{ "stop", G_FLAG_VERBOSE, NULL,
144 	    {
145 		{ 'f', "force", NULL, G_TYPE_BOOL },
146 		G_OPT_SENTINEL
147 	    },
148 	    "[-fv] name ..."
149 	},
150 	G_CMD_SENTINEL
151 };
152 
153 static int verbose = 0;
154 
155 static void
156 mirror_main(struct gctl_req *req, unsigned flags)
157 {
158 	const char *name;
159 
160 	if ((flags & G_FLAG_VERBOSE) != 0)
161 		verbose = 1;
162 
163 	name = gctl_get_ascii(req, "verb");
164 	if (name == NULL) {
165 		gctl_error(req, "No '%s' argument.", "verb");
166 		return;
167 	}
168 	if (strcmp(name, "label") == 0)
169 		mirror_label(req);
170 	else if (strcmp(name, "clear") == 0)
171 		mirror_clear(req);
172 	else if (strcmp(name, "dump") == 0)
173 		mirror_dump(req);
174 	else if (strcmp(name, "activate") == 0)
175 		mirror_activate(req);
176 	else
177 		gctl_error(req, "Unknown command: %s.", name);
178 }
179 
180 static void
181 mirror_label(struct gctl_req *req)
182 {
183 	struct g_mirror_metadata md;
184 	u_char sector[512];
185 	const char *str;
186 	unsigned sectorsize;
187 	off_t mediasize;
188 	intmax_t val;
189 	int error, i, nargs, bal, hardcode;
190 
191 	bzero(sector, sizeof(sector));
192 	nargs = gctl_get_int(req, "nargs");
193 	if (nargs < 2) {
194 		gctl_error(req, "Too few arguments.");
195 		return;
196 	}
197 
198 	strlcpy(md.md_magic, G_MIRROR_MAGIC, sizeof(md.md_magic));
199 	md.md_version = G_MIRROR_VERSION;
200 	str = gctl_get_ascii(req, "arg0");
201 	strlcpy(md.md_name, str, sizeof(md.md_name));
202 	md.md_mid = arc4random();
203 	md.md_all = nargs - 1;
204 	md.md_mflags = 0;
205 	md.md_dflags = 0;
206 	md.md_genid = 0;
207 	md.md_syncid = 1;
208 	md.md_sync_offset = 0;
209 	val = gctl_get_intmax(req, "slice");
210 	md.md_slice = val;
211 	str = gctl_get_ascii(req, "balance");
212 	bal = balance_id(str);
213 	if (bal == -1) {
214 		gctl_error(req, "Invalid balance algorithm.");
215 		return;
216 	}
217 	md.md_balance = bal;
218 	if (gctl_get_int(req, "noautosync"))
219 		md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
220 	if (gctl_get_int(req, "nofailsync"))
221 		md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
222 	hardcode = gctl_get_int(req, "hardcode");
223 
224 	/*
225 	 * Calculate sectorsize by finding least common multiple from
226 	 * sectorsizes of every disk and find the smallest mediasize.
227 	 */
228 	mediasize = 0;
229 	sectorsize = 0;
230 	for (i = 1; i < nargs; i++) {
231 		unsigned ssize;
232 		off_t msize;
233 
234 		str = gctl_get_ascii(req, "arg%d", i);
235 		msize = g_get_mediasize(str);
236 		ssize = g_get_sectorsize(str);
237 		if (msize == 0 || ssize == 0) {
238 			gctl_error(req, "Can't get informations about %s: %s.",
239 			    str, strerror(errno));
240 			return;
241 		}
242 		msize -= ssize;
243 		if (mediasize == 0 || (mediasize > 0 && msize < mediasize))
244 			mediasize = msize;
245 		if (sectorsize == 0)
246 			sectorsize = ssize;
247 		else
248 			sectorsize = g_lcm(sectorsize, ssize);
249 	}
250 	md.md_mediasize = mediasize;
251 	md.md_sectorsize = sectorsize;
252 	md.md_mediasize -= (md.md_mediasize % md.md_sectorsize);
253 
254 	/*
255 	 * Clear last sector first, to spoil all components if device exists.
256 	 */
257 	for (i = 1; i < nargs; i++) {
258 		str = gctl_get_ascii(req, "arg%d", i);
259 		error = g_metadata_clear(str, NULL);
260 		if (error != 0) {
261 			gctl_error(req, "Can't store metadata on %s: %s.", str,
262 			    strerror(error));
263 			return;
264 		}
265 	}
266 
267 	/*
268 	 * Ok, store metadata (use disk number as priority).
269 	 */
270 	for (i = 1; i < nargs; i++) {
271 		str = gctl_get_ascii(req, "arg%d", i);
272 		md.md_did = arc4random();
273 		md.md_priority = i - 1;
274 		md.md_provsize = g_get_mediasize(str);
275 		assert(md.md_provsize != 0);
276 		if (!hardcode)
277 			bzero(md.md_provider, sizeof(md.md_provider));
278 		else {
279 			if (strncmp(str, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
280 				str += sizeof(_PATH_DEV) - 1;
281 			strlcpy(md.md_provider, str, sizeof(md.md_provider));
282 		}
283 		mirror_metadata_encode(&md, sector);
284 		error = g_metadata_store(str, sector, sizeof(sector));
285 		if (error != 0) {
286 			fprintf(stderr, "Can't store metadata on %s: %s.\n",
287 			    str, strerror(error));
288 			gctl_error(req, "Not fully done.");
289 			continue;
290 		}
291 		if (verbose)
292 			printf("Metadata value stored on %s.\n", str);
293 	}
294 }
295 
296 static void
297 mirror_clear(struct gctl_req *req)
298 {
299 	const char *name;
300 	int error, i, nargs;
301 
302 	nargs = gctl_get_int(req, "nargs");
303 	if (nargs < 1) {
304 		gctl_error(req, "Too few arguments.");
305 		return;
306 	}
307 
308 	for (i = 0; i < nargs; i++) {
309 		name = gctl_get_ascii(req, "arg%d", i);
310 		error = g_metadata_clear(name, G_MIRROR_MAGIC);
311 		if (error != 0) {
312 			fprintf(stderr, "Can't clear metadata on %s: %s.\n",
313 			    name, strerror(error));
314 			gctl_error(req, "Not fully done.");
315 			continue;
316 		}
317 		if (verbose)
318 			printf("Metadata cleared on %s.\n", name);
319 	}
320 }
321 
322 static void
323 mirror_dump(struct gctl_req *req)
324 {
325 	struct g_mirror_metadata md, tmpmd;
326 	const char *name;
327 	int error, i, nargs;
328 
329 	nargs = gctl_get_int(req, "nargs");
330 	if (nargs < 1) {
331 		gctl_error(req, "Too few arguments.");
332 		return;
333 	}
334 
335 	for (i = 0; i < nargs; i++) {
336 		name = gctl_get_ascii(req, "arg%d", i);
337 		error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd),
338 		    G_MIRROR_MAGIC);
339 		if (error != 0) {
340 			fprintf(stderr, "Can't read metadata from %s: %s.\n",
341 			    name, strerror(error));
342 			gctl_error(req, "Not fully done.");
343 			continue;
344 		}
345 		if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) {
346 			fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
347 			    name);
348 			gctl_error(req, "Not fully done.");
349 			continue;
350 		}
351 		printf("Metadata on %s:\n", name);
352 		mirror_metadata_dump(&md);
353 		printf("\n");
354 	}
355 }
356 
357 static void
358 mirror_activate(struct gctl_req *req)
359 {
360 	struct g_mirror_metadata md, tmpmd;
361 	const char *name, *path;
362 	int error, i, nargs;
363 
364 	nargs = gctl_get_int(req, "nargs");
365 	if (nargs < 2) {
366 		gctl_error(req, "Too few arguments.");
367 		return;
368 	}
369 	name = gctl_get_ascii(req, "arg0");
370 
371 	for (i = 1; i < nargs; i++) {
372 		path = gctl_get_ascii(req, "arg%d", i);
373 		error = g_metadata_read(path, (u_char *)&tmpmd, sizeof(tmpmd),
374 		    G_MIRROR_MAGIC);
375 		if (error != 0) {
376 			fprintf(stderr, "Cannot read metadata from %s: %s.\n",
377 			    path, strerror(error));
378 			gctl_error(req, "Not fully done.");
379 			continue;
380 		}
381 		if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) {
382 			fprintf(stderr,
383 			    "MD5 hash mismatch for provider %s, skipping.\n",
384 			    path);
385 			gctl_error(req, "Not fully done.");
386 			continue;
387 		}
388 		if (strcmp(md.md_name, name) != 0) {
389 			fprintf(stderr,
390 			    "Provider %s is not the mirror %s component.\n",
391 			    path, name);
392 			gctl_error(req, "Not fully done.");
393 			continue;
394 		}
395 		md.md_dflags &= ~G_MIRROR_DISK_FLAG_INACTIVE;
396 		mirror_metadata_encode(&md, (u_char *)&tmpmd);
397 		error = g_metadata_store(path, (u_char *)&tmpmd, sizeof(tmpmd));
398 		if (error != 0) {
399 			fprintf(stderr, "Cannot write metadata from %s: %s.\n",
400 			    path, strerror(error));
401 			gctl_error(req, "Not fully done.");
402 			continue;
403 		}
404 		if (verbose)
405 			printf("Provider %s activated.\n", path);
406 	}
407 }
408 
409 static struct gclass *
410 find_class(struct gmesh *mesh, const char *name)
411 {
412 	struct gclass *classp;
413 
414 	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
415 		if (strcmp(classp->lg_name, name) == 0)
416 			return (classp);
417 	}
418 	return (NULL);
419 }
420 
421 static struct ggeom *
422 find_geom(struct gclass *classp, const char *name)
423 {
424 	struct ggeom *gp;
425 
426 	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
427 		if (strcmp(gp->lg_name, name) == 0)
428 			return (gp);
429 	}
430 	return (NULL);
431 }
432 
433 static void
434 mirror_resize(struct gctl_req *req, unsigned flags __unused)
435 {
436 	struct gmesh mesh;
437 	struct gclass *classp;
438 	struct ggeom *gp;
439 	struct gprovider *pp;
440 	struct gconsumer *cp;
441 	off_t size;
442 	int error, nargs;
443 	const char *name, *g;
444 	char ssize[30];
445 
446 	nargs = gctl_get_int(req, "nargs");
447 	if (nargs != 1)
448 		errx(EXIT_FAILURE, "Invalid number of arguments.");
449 	name = gctl_get_ascii(req, "class");
450 	if (name == NULL)
451 		abort();
452 	g = gctl_get_ascii(req, "arg0");
453 	if (g == NULL)
454 		abort();
455 	error = geom_gettree_geom(&mesh, name, g, 1);
456 	if (error)
457 		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
458 	classp = find_class(&mesh, name);
459 	if (classp == NULL)
460 		errx(EXIT_FAILURE, "Class %s not found.", name);
461 	gp = find_geom(classp, g);
462 	if (gp == NULL)
463 		errx(EXIT_FAILURE, "No such geom: %s.", g);
464 	pp = LIST_FIRST(&gp->lg_provider);
465 	if (pp == NULL)
466 		errx(EXIT_FAILURE, "Provider of geom %s not found.", g);
467 	size = pp->lg_mediasize;
468 	name = gctl_get_ascii(req, "size");
469 	if (name == NULL)
470 		errx(EXIT_FAILURE, "The size is not specified.");
471 	if (*name == '*') {
472 #define	CSZ(c)	((c)->lg_provider->lg_mediasize - \
473     (c)->lg_provider->lg_sectorsize)
474 		/* Find the maximum possible size */
475 		LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
476 			if (CSZ(cp) > size)
477 				size = CSZ(cp);
478 		}
479 		LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
480 			if (CSZ(cp) < size)
481 				size = CSZ(cp);
482 		}
483 #undef CSZ
484 		if (size == pp->lg_mediasize)
485 			errx(EXIT_FAILURE,
486 			    "Cannot expand provider %s\n",
487 			    pp->lg_name);
488 	} else {
489 		error = g_parse_lba(name, pp->lg_sectorsize, &size);
490 		if (error)
491 			errc(EXIT_FAILURE, error, "Invalid size param");
492 		size *= pp->lg_sectorsize;
493 	}
494 	snprintf(ssize, sizeof(ssize), "%ju", (uintmax_t)size);
495 	gctl_change_param(req, "size", -1, ssize);
496 	geom_deletetree(&mesh);
497 	gctl_issue(req);
498 }
499