xref: /trueos/sbin/geom/class/virstor/geom_virstor.c (revision 8fe640108653f13042f1b15213769e338aa524f6)
1 /*-
2  * Copyright (c) 2005 Ivan Voras <ivoras@freebsd.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include <sys/param.h>
30 #include <errno.h>
31 #include <paths.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <libgeom.h>
40 #include <err.h>
41 #include <assert.h>
42 
43 #include <core/geom.h>
44 #include <misc/subr.h>
45 
46 #include <geom/virstor/g_virstor_md.h>
47 #include <geom/virstor/g_virstor.h>
48 
49 uint32_t lib_version = G_LIB_VERSION;
50 uint32_t version = G_VIRSTOR_VERSION;
51 
52 #define	GVIRSTOR_CHUNK_SIZE	"4M"
53 #define	GVIRSTOR_VIR_SIZE	"2T"
54 
55 #if G_LIB_VERSION == 1
56 /* Support RELENG_6 */
57 #define G_TYPE_BOOL G_TYPE_NONE
58 #endif
59 
60 /*
61  * virstor_main gets called by the geom(8) utility
62  */
63 static void virstor_main(struct gctl_req *req, unsigned flags);
64 
65 struct g_command class_commands[] = {
66 	{ "clear", G_FLAG_VERBOSE, virstor_main, G_NULL_OPTS,
67 	    "[-v] prov ..."
68 	},
69 	{ "dump", 0, virstor_main, G_NULL_OPTS,
70 	    "prov ..."
71 	},
72 	{ "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, virstor_main,
73 	    {
74 		{ 'h', "hardcode", NULL, G_TYPE_BOOL},
75 		{ 'm', "chunk_size", GVIRSTOR_CHUNK_SIZE, G_TYPE_NUMBER},
76 		{ 's', "vir_size", GVIRSTOR_VIR_SIZE, G_TYPE_NUMBER},
77 		G_OPT_SENTINEL
78 	    },
79 	    "[-h] [-v] [-m chunk_size] [-s vir_size] name provider0 [provider1 ...]"
80 	},
81 	{ "destroy", G_FLAG_VERBOSE, NULL,
82 	    {
83 		{ 'f', "force", NULL, G_TYPE_BOOL},
84 		G_OPT_SENTINEL
85 	    },
86 	    "[-fv] name ..."
87 	},
88 	{ "stop", G_FLAG_VERBOSE, NULL,
89 	    {
90 		{ 'f', "force", NULL, G_TYPE_BOOL},
91 		G_OPT_SENTINEL
92 	    },
93 	    "[-fv] name ... (alias for \"destroy\")"
94 	},
95 	{ "add", G_FLAG_VERBOSE, NULL,
96 	    {
97 		{ 'h', "hardcode", NULL, G_TYPE_BOOL},
98 		G_OPT_SENTINEL
99 	    },
100 	    "[-vh] name prov [prov ...]"
101 	},
102 	{ "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
103 	    "[-v] name ..."
104 	},
105 	G_CMD_SENTINEL
106 };
107 
108 static int verbose = 0;
109 
110 /* Helper functions' declarations */
111 static void virstor_clear(struct gctl_req *req);
112 static void virstor_dump(struct gctl_req *req);
113 static void virstor_label(struct gctl_req *req);
114 
115 /* Dispatcher function (no real work done here, only verbose flag recorder) */
116 static void
virstor_main(struct gctl_req * req,unsigned flags)117 virstor_main(struct gctl_req *req, unsigned flags)
118 {
119 	const char *name;
120 
121 	if ((flags & G_FLAG_VERBOSE) != 0)
122 		verbose = 1;
123 
124 	name = gctl_get_ascii(req, "verb");
125 	if (name == NULL) {
126 		gctl_error(req, "No '%s' argument.", "verb");
127 		return;
128 	}
129 	if (strcmp(name, "label") == 0)
130 		virstor_label(req);
131 	else if (strcmp(name, "clear") == 0)
132 		virstor_clear(req);
133 	else if (strcmp(name, "dump") == 0)
134 		virstor_dump(req);
135 	else
136 		gctl_error(req, "%s: Unknown command: %s.", __func__, name);
137 
138 	/* No CTASSERT in userland
139 	CTASSERT(VIRSTOR_MAP_BLOCK_ENTRIES*VIRSTOR_MAP_ENTRY_SIZE == MAXPHYS);
140 	*/
141 }
142 
143 static void
pathgen(const char * name,char * path,size_t size)144 pathgen(const char *name, char *path, size_t size)
145 {
146 
147 	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0)
148 		snprintf(path, size, "%s%s", _PATH_DEV, name);
149 	else
150 		strlcpy(path, name, size);
151 }
152 
153 static int
my_g_metadata_store(const char * name,u_char * md,size_t size)154 my_g_metadata_store(const char *name, u_char *md, size_t size)
155 {
156 	char path[MAXPATHLEN];
157 	unsigned sectorsize;
158 	off_t mediasize;
159 	u_char *sector;
160 	int error, fd;
161 
162 	pathgen(name, path, sizeof(path));
163 	sector = NULL;
164 	error = 0;
165 
166 	fd = open(path, O_RDWR);
167 	if (fd == -1)
168 		return (errno);
169 	mediasize = g_get_mediasize(name);
170 	if (mediasize == 0) {
171 		error = errno;
172 		goto out;
173 	}
174 	sectorsize = g_get_sectorsize(name);
175 	if (sectorsize == 0) {
176 		error = errno;
177 		goto out;
178 	}
179 	assert(sectorsize >= size);
180 	sector = malloc(sectorsize);
181 	if (sector == NULL) {
182 		error = ENOMEM;
183 		goto out;
184 	}
185 	bcopy(md, sector, size);
186 	if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
187 	    (ssize_t)sectorsize) {
188 		error = errno;
189 		goto out;
190 	}
191 out:
192 	if (sector != NULL)
193 		free(sector);
194 	close(fd);
195 	return (error);
196 }
197 
198 /*
199  * Labels a new geom Meaning: parses and checks the parameters, calculates &
200  * writes metadata to the relevant providers so when the next round of
201  * "tasting" comes (which will be just after the provider(s) are closed) geom
202  * can be instantiated with the tasted metadata.
203  */
204 static void
virstor_label(struct gctl_req * req)205 virstor_label(struct gctl_req *req)
206 {
207 	struct g_virstor_metadata md;
208 	off_t msize;
209 	unsigned char *sect;
210 	unsigned int i;
211 	size_t ssize, secsize;
212 	const char *name;
213 	char param[32];
214 	int hardcode, nargs, error;
215 	struct virstor_map_entry *map;
216 	size_t total_chunks;	/* We'll run out of memory if
217 				   this needs to be bigger. */
218 	unsigned int map_chunks; /* Chunks needed by the map (map size). */
219 	size_t map_size;	/* In bytes. */
220 	ssize_t written;
221 	int fd;
222 
223 	nargs = gctl_get_int(req, "nargs");
224 	if (nargs < 2) {
225 		gctl_error(req, "Too few arguments (%d): expecting: name "
226 		    "provider0 [provider1 ...]", nargs);
227 		return;
228 	}
229 
230 	hardcode = gctl_get_int(req, "hardcode");
231 
232 	/*
233 	 * Initialize constant parts of metadata: magic signature, version,
234 	 * name.
235 	 */
236 	bzero(&md, sizeof(md));
237 	strlcpy(md.md_magic, G_VIRSTOR_MAGIC, sizeof(md.md_magic));
238 	md.md_version = G_VIRSTOR_VERSION;
239 	name = gctl_get_ascii(req, "arg0");
240 	if (name == NULL) {
241 		gctl_error(req, "No 'arg%u' argument.", 0);
242 		return;
243 	}
244 	strlcpy(md.md_name, name, sizeof(md.md_name));
245 
246 	md.md_virsize = (off_t)gctl_get_intmax(req, "vir_size");
247 	md.md_chunk_size = gctl_get_intmax(req, "chunk_size");
248 	md.md_count = nargs - 1;
249 
250 	if (md.md_virsize == 0 || md.md_chunk_size == 0) {
251 		gctl_error(req, "Virtual size and chunk size must be non-zero");
252 		return;
253 	}
254 
255 	if (md.md_chunk_size % MAXPHYS != 0) {
256 		/* XXX: This is not strictly needed, but it's convenient to
257 		 * impose some limitations on it, so why not MAXPHYS. */
258 		size_t new_size = (md.md_chunk_size / MAXPHYS) * MAXPHYS;
259 		if (new_size < md.md_chunk_size)
260 			new_size += MAXPHYS;
261 		fprintf(stderr, "Resizing chunk size to be a multiple of "
262 		    "MAXPHYS (%d kB).\n", MAXPHYS / 1024);
263 		fprintf(stderr, "New chunk size: %zu kB\n", new_size / 1024);
264 		md.md_chunk_size = new_size;
265 	}
266 
267 	if (md.md_virsize % md.md_chunk_size != 0) {
268 		off_t chunk_count = md.md_virsize / md.md_chunk_size;
269 		md.md_virsize = chunk_count * md.md_chunk_size;
270 		fprintf(stderr, "Resizing virtual size to be a multiple of "
271 		    "chunk size.\n");
272 		fprintf(stderr, "New virtual size: %zu MB\n",
273 		    (size_t)(md.md_virsize/(1024 * 1024)));
274 	}
275 
276 	msize = secsize = 0;
277 	for (i = 1; i < (unsigned)nargs; i++) {
278 		snprintf(param, sizeof(param), "arg%u", i);
279 		name = gctl_get_ascii(req, "%s", param);
280 		ssize = g_get_sectorsize(name);
281 		if (ssize == 0)
282 			fprintf(stderr, "%s for %s\n", strerror(errno), name);
283 		msize += g_get_mediasize(name);
284 		if (secsize == 0)
285 			secsize = ssize;
286 		else if (secsize != ssize) {
287 			gctl_error(req, "Devices need to have same sector size "
288 			    "(%u on %s needs to be %u).",
289 			    (u_int)ssize, name, (u_int)secsize);
290 			return;
291 		}
292 	}
293 
294 	if (secsize == 0) {
295 		gctl_error(req, "Device not specified");
296 		return;
297 	}
298 
299 	if (md.md_chunk_size % secsize != 0) {
300 		fprintf(stderr, "Error: chunk size is not a multiple of sector "
301 		    "size.");
302 		gctl_error(req, "Chunk size (in bytes) must be multiple of %u.",
303 		    (unsigned int)secsize);
304 		return;
305 	}
306 
307 	total_chunks = md.md_virsize / md.md_chunk_size;
308 	map_size = total_chunks * sizeof(*map);
309 	assert(md.md_virsize % md.md_chunk_size == 0);
310 
311 	ssize = map_size % secsize;
312 	if (ssize != 0) {
313 		size_t add_chunks = (secsize - ssize) / sizeof(*map);
314 		total_chunks += add_chunks;
315 		md.md_virsize = (off_t)total_chunks * (off_t)md.md_chunk_size;
316 		map_size = total_chunks * sizeof(*map);
317 		fprintf(stderr, "Resizing virtual size to fit virstor "
318 		    "structures.\n");
319 		fprintf(stderr, "New virtual size: %ju MB (%zu new chunks)\n",
320 		    (uintmax_t)(md.md_virsize / (1024 * 1024)), add_chunks);
321 	}
322 
323 	if (verbose)
324 		printf("Total virtual chunks: %zu (%zu MB each), %ju MB total "
325 		    "virtual size.\n",
326 		    total_chunks, (size_t)(md.md_chunk_size / (1024 * 1024)),
327 		    md.md_virsize/(1024 * 1024));
328 
329 	if ((off_t)md.md_virsize < msize)
330 		fprintf(stderr, "WARNING: Virtual storage size < Physical "
331 		    "available storage (%ju < %ju)\n", md.md_virsize, msize);
332 
333 	/* Clear last sector first to spoil all components if device exists. */
334 	if (verbose)
335 		printf("Clearing metadata on");
336 
337 	for (i = 1; i < (unsigned)nargs; i++) {
338 		snprintf(param, sizeof(param), "arg%u", i);
339 		name = gctl_get_ascii(req, "%s", param);
340 
341 		if (verbose)
342 			printf(" %s", name);
343 
344 		msize = g_get_mediasize(name);
345 		ssize = g_get_sectorsize(name);
346 		if (msize == 0 || ssize == 0) {
347 			gctl_error(req, "Can't retrieve information about "
348 			    "%s: %s.", name, strerror(errno));
349 			return;
350 		}
351 		if (msize < (off_t) MAX(md.md_chunk_size*4, map_size))
352 			gctl_error(req, "Device %s is too small", name);
353 		error = g_metadata_clear(name, NULL);
354 		if (error != 0) {
355 			gctl_error(req, "Can't clear metadata on %s: %s.", name,
356 			    strerror(error));
357 			return;
358 		}
359 	}
360 
361 
362 	/* Write allocation table to the first provider - this needs to be done
363 	 * before metadata is written because when kernel tastes it it's too
364 	 * late */
365 	name = gctl_get_ascii(req, "arg1"); /* device with metadata */
366 	if (verbose)
367 		printf(".\nWriting allocation table to %s...", name);
368 
369 	/* How many chunks does the map occupy? */
370 	map_chunks = map_size/md.md_chunk_size;
371 	if (map_size % md.md_chunk_size != 0)
372 		map_chunks++;
373 	if (verbose) {
374 		printf(" (%zu MB, %d chunks) ", map_size/(1024*1024), map_chunks);
375 		fflush(stdout);
376 	}
377 
378 	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
379 		fd = open(name, O_RDWR);
380 	else {
381 		sprintf(param, "%s%s", _PATH_DEV, name);
382 		fd = open(param, O_RDWR);
383 	}
384 	if (fd < 0)
385 		gctl_error(req, "Cannot open provider %s to write map", name);
386 
387 	/* Do it with calloc because there might be a need to set up chunk flags
388 	 * in the future */
389 	map = calloc(total_chunks, sizeof(*map));
390 	if (map == NULL) {
391 		gctl_error(req,
392 		    "Out of memory (need %zu bytes for allocation map)",
393 		    map_size);
394 	}
395 
396 	written = pwrite(fd, map, map_size, 0);
397 	free(map);
398 	if ((size_t)written != map_size) {
399 		if (verbose) {
400 			fprintf(stderr, "\nTried to write %zu, written %zd (%s)\n",
401 			    map_size, written, strerror(errno));
402 		}
403 		gctl_error(req, "Error writing out allocation map!");
404 		return;
405 	}
406 	close (fd);
407 
408 	if (verbose)
409 		printf("\nStoring metadata on ");
410 
411 	/*
412 	 * ID is randomly generated, unique for a geom. This is used to
413 	 * recognize all providers belonging to one geom.
414 	 */
415 	md.md_id = arc4random();
416 
417 	/* Ok, store metadata. */
418 	for (i = 1; i < (unsigned)nargs; i++) {
419 		snprintf(param, sizeof(param), "arg%u", i);
420 		name = gctl_get_ascii(req, "%s", param);
421 
422 		msize = g_get_mediasize(name);
423 		ssize = g_get_sectorsize(name);
424 
425 		if (verbose)
426 			printf("%s ", name);
427 
428 		/* this provider's position/type in geom */
429 		md.no = i - 1;
430 		/* this provider's size */
431 		md.provsize = msize;
432 		/* chunk allocation info */
433 		md.chunk_count = md.provsize / md.md_chunk_size;
434 		if (verbose)
435 			printf("(%u chunks) ", md.chunk_count);
436 		/* Check to make sure last sector is unused */
437 		if ((off_t)(md.chunk_count * md.md_chunk_size) > (off_t)(msize-ssize))
438 		    md.chunk_count--;
439 		md.chunk_next = 0;
440 		if (i != 1) {
441 			md.chunk_reserved = 0;
442 			md.flags = 0;
443 		} else {
444 			md.chunk_reserved = map_chunks * 2;
445 			md.flags = VIRSTOR_PROVIDER_ALLOCATED |
446 			    VIRSTOR_PROVIDER_CURRENT;
447 			md.chunk_next = md.chunk_reserved;
448 			if (verbose)
449 				printf("(%u reserved) ", md.chunk_reserved);
450 		}
451 
452 		if (!hardcode)
453 			bzero(md.provider, sizeof(md.provider));
454 		else {
455 			/* convert "/dev/something" to "something" */
456 			if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) {
457 				strlcpy(md.provider, name + sizeof(_PATH_DEV) - 1,
458 				    sizeof(md.provider));
459 			} else
460 				strlcpy(md.provider, name, sizeof(md.provider));
461 		}
462 		sect = malloc(ssize);
463 		if (sect == NULL)
464 			err(1, "Cannot allocate sector of %zu bytes", ssize);
465 		bzero(sect, ssize);
466 		virstor_metadata_encode(&md, sect);
467 		error = my_g_metadata_store(name, sect, ssize);
468 		free(sect);
469 		if (error != 0) {
470 			if (verbose)
471 				printf("\n");
472 			fprintf(stderr, "Can't store metadata on %s: %s.\n",
473 			    name, strerror(error));
474 			gctl_error(req,
475 			    "Not fully done (error storing metadata).");
476 			return;
477 		}
478 	}
479 #if 0
480 	if (verbose)
481 		printf("\n");
482 #endif
483 }
484 
485 /* Clears metadata on given provider(s) IF it's owned by us */
486 static void
virstor_clear(struct gctl_req * req)487 virstor_clear(struct gctl_req *req)
488 {
489 	const char *name;
490 	char param[32];
491 	unsigned i;
492 	int nargs, error;
493 	int fd;
494 
495 	nargs = gctl_get_int(req, "nargs");
496 	if (nargs < 1) {
497 		gctl_error(req, "Too few arguments.");
498 		return;
499 	}
500 	for (i = 0; i < (unsigned)nargs; i++) {
501 		snprintf(param, sizeof(param), "arg%u", i);
502 		name = gctl_get_ascii(req, "%s", param);
503 
504 		error = g_metadata_clear(name, G_VIRSTOR_MAGIC);
505 		if (error != 0) {
506 			fprintf(stderr, "Can't clear metadata on %s: %s "
507 			    "(do I own it?)\n", name, strerror(error));
508 			gctl_error(req,
509 			    "Not fully done (can't clear metadata).");
510 			continue;
511 		}
512 		if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
513 			fd = open(name, O_RDWR);
514 		else {
515 			sprintf(param, "%s%s", _PATH_DEV, name);
516 			fd = open(param, O_RDWR);
517 		}
518 		if (fd < 0) {
519 			gctl_error(req, "Cannot clear header sector for %s",
520 			    name);
521 			continue;
522 		}
523 		if (verbose)
524 			printf("Metadata cleared on %s.\n", name);
525 	}
526 }
527 
528 /* Print some metadata information */
529 static void
virstor_metadata_dump(const struct g_virstor_metadata * md)530 virstor_metadata_dump(const struct g_virstor_metadata *md)
531 {
532 	printf("          Magic string: %s\n", md->md_magic);
533 	printf("      Metadata version: %u\n", (u_int) md->md_version);
534 	printf("           Device name: %s\n", md->md_name);
535 	printf("             Device ID: %u\n", (u_int) md->md_id);
536 	printf("        Provider index: %u\n", (u_int) md->no);
537 	printf("      Active providers: %u\n", (u_int) md->md_count);
538 	printf("    Hardcoded provider: %s\n",
539 	    md->provider[0] != '\0' ? md->provider : "(not hardcoded)");
540 	printf("          Virtual size: %u MB\n",
541 	    (unsigned int)(md->md_virsize/(1024 * 1024)));
542 	printf("            Chunk size: %u kB\n", md->md_chunk_size / 1024);
543 	printf("    Chunks on provider: %u\n", md->chunk_count);
544 	printf("           Chunks free: %u\n", md->chunk_count - md->chunk_next);
545 	printf("       Reserved chunks: %u\n", md->chunk_reserved);
546 }
547 
548 /* Called by geom(8) via gvirstor_main() to dump metadata information */
549 static void
virstor_dump(struct gctl_req * req)550 virstor_dump(struct gctl_req *req)
551 {
552 	struct g_virstor_metadata md;
553 	u_char tmpmd[512];	/* temporary buffer */
554 	const char *name;
555 	char param[16];
556 	int nargs, error, i;
557 
558 	assert(sizeof(tmpmd) >= sizeof(md));
559 
560 	nargs = gctl_get_int(req, "nargs");
561 	if (nargs < 1) {
562 		gctl_error(req, "Too few arguments.");
563 		return;
564 	}
565 	for (i = 0; i < nargs; i++) {
566 		snprintf(param, sizeof(param), "arg%u", i);
567 		name = gctl_get_ascii(req, "%s", param);
568 
569 		error = g_metadata_read(name, (u_char *) & tmpmd, sizeof(tmpmd),
570 		    G_VIRSTOR_MAGIC);
571 		if (error != 0) {
572 			fprintf(stderr, "Can't read metadata from %s: %s.\n",
573 			    name, strerror(error));
574 			gctl_error(req,
575 			    "Not fully done (error reading metadata).");
576 			continue;
577 		}
578 		virstor_metadata_decode((u_char *) & tmpmd, &md);
579 		printf("Metadata on %s:\n", name);
580 		virstor_metadata_dump(&md);
581 		printf("\n");
582 	}
583 }
584