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