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: stable/11/sbin/geom/class/virstor/geom_virstor.c 330726 2018-03-10 02:15:45Z asomers $");
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
145static void
146pathgen(const char *name, char *path, size_t size)
147{
148
149	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0)
150		snprintf(path, size, "%s%s", _PATH_DEV, name);
151	else
152		strlcpy(path, name, size);
153}
154
155static int
156my_g_metadata_store(const char *name, u_char *md, size_t size)
157{
158	char path[MAXPATHLEN];
159	unsigned sectorsize;
160	off_t mediasize;
161	u_char *sector;
162	int error, fd;
163
164	pathgen(name, path, sizeof(path));
165	sector = NULL;
166	error = 0;
167
168	fd = open(path, O_RDWR);
169	if (fd == -1)
170		return (errno);
171	mediasize = g_get_mediasize(name);
172	if (mediasize == 0) {
173		error = errno;
174		goto out;
175	}
176	sectorsize = g_get_sectorsize(name);
177	if (sectorsize == 0) {
178		error = errno;
179		goto out;
180	}
181	assert(sectorsize >= size);
182	sector = malloc(sectorsize);
183	if (sector == NULL) {
184		error = ENOMEM;
185		goto out;
186	}
187	bcopy(md, sector, size);
188	bzero(sector + size, sectorsize - size);
189	if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
190	    (ssize_t)sectorsize) {
191		error = errno;
192		goto out;
193	}
194out:
195	if (sector != NULL)
196		free(sector);
197	close(fd);
198	return (error);
199}
200
201/*
202 * Labels a new geom Meaning: parses and checks the parameters, calculates &
203 * writes metadata to the relevant providers so when the next round of
204 * "tasting" comes (which will be just after the provider(s) are closed) geom
205 * can be instantiated with the tasted metadata.
206 */
207static void
208virstor_label(struct gctl_req *req)
209{
210	struct g_virstor_metadata md;
211	off_t msize;
212	unsigned char *sect;
213	unsigned int i;
214	size_t ssize, secsize;
215	const char *name;
216	char param[32];
217	int hardcode, nargs, error;
218	struct virstor_map_entry *map;
219	size_t total_chunks;	/* We'll run out of memory if
220				   this needs to be bigger. */
221	unsigned int map_chunks; /* Chunks needed by the map (map size). */
222	size_t map_size;	/* In bytes. */
223	ssize_t written;
224	int fd;
225
226	nargs = gctl_get_int(req, "nargs");
227	if (nargs < 2) {
228		gctl_error(req, "Too few arguments (%d): expecting: name "
229		    "provider0 [provider1 ...]", nargs);
230		return;
231	}
232
233	hardcode = gctl_get_int(req, "hardcode");
234
235	/*
236	 * Initialize constant parts of metadata: magic signature, version,
237	 * name.
238	 */
239	bzero(&md, sizeof(md));
240	strlcpy(md.md_magic, G_VIRSTOR_MAGIC, sizeof(md.md_magic));
241	md.md_version = G_VIRSTOR_VERSION;
242	name = gctl_get_ascii(req, "arg0");
243	if (name == NULL) {
244		gctl_error(req, "No 'arg%u' argument.", 0);
245		return;
246	}
247	strlcpy(md.md_name, name, sizeof(md.md_name));
248
249	md.md_virsize = (off_t)gctl_get_intmax(req, "vir_size");
250	md.md_chunk_size = gctl_get_intmax(req, "chunk_size");
251	md.md_count = nargs - 1;
252
253	if (md.md_virsize == 0 || md.md_chunk_size == 0) {
254		gctl_error(req, "Virtual size and chunk size must be non-zero");
255		return;
256	}
257
258	if (md.md_chunk_size % MAXPHYS != 0) {
259		/* XXX: This is not strictly needed, but it's convenient to
260		 * impose some limitations on it, so why not MAXPHYS. */
261		size_t new_size = rounddown(md.md_chunk_size, MAXPHYS);
262		if (new_size < md.md_chunk_size)
263			new_size += MAXPHYS;
264		fprintf(stderr, "Resizing chunk size to be a multiple of "
265		    "MAXPHYS (%d kB).\n", MAXPHYS / 1024);
266		fprintf(stderr, "New chunk size: %zu kB\n", new_size / 1024);
267		md.md_chunk_size = new_size;
268	}
269
270	if (md.md_virsize % md.md_chunk_size != 0) {
271		off_t chunk_count = md.md_virsize / md.md_chunk_size;
272		md.md_virsize = chunk_count * md.md_chunk_size;
273		fprintf(stderr, "Resizing virtual size to be a multiple of "
274		    "chunk size.\n");
275		fprintf(stderr, "New virtual size: %zu MB\n",
276		    (size_t)(md.md_virsize/(1024 * 1024)));
277	}
278
279	msize = secsize = 0;
280	for (i = 1; i < (unsigned)nargs; i++) {
281		snprintf(param, sizeof(param), "arg%u", i);
282		name = gctl_get_ascii(req, "%s", param);
283		ssize = g_get_sectorsize(name);
284		if (ssize == 0)
285			fprintf(stderr, "%s for %s\n", strerror(errno), name);
286		msize += g_get_mediasize(name);
287		if (secsize == 0)
288			secsize = ssize;
289		else if (secsize != ssize) {
290			gctl_error(req, "Devices need to have same sector size "
291			    "(%u on %s needs to be %u).",
292			    (u_int)ssize, name, (u_int)secsize);
293			return;
294		}
295	}
296
297	if (secsize == 0) {
298		gctl_error(req, "Device not specified");
299		return;
300	}
301
302	if (md.md_chunk_size % secsize != 0) {
303		fprintf(stderr, "Error: chunk size is not a multiple of sector "
304		    "size.");
305		gctl_error(req, "Chunk size (in bytes) must be multiple of %u.",
306		    (unsigned int)secsize);
307		return;
308	}
309
310	total_chunks = md.md_virsize / md.md_chunk_size;
311	map_size = total_chunks * sizeof(*map);
312	assert(md.md_virsize % md.md_chunk_size == 0);
313
314	ssize = map_size % secsize;
315	if (ssize != 0) {
316		size_t add_chunks = (secsize - ssize) / sizeof(*map);
317		total_chunks += add_chunks;
318		md.md_virsize = (off_t)total_chunks * (off_t)md.md_chunk_size;
319		map_size = total_chunks * sizeof(*map);
320		fprintf(stderr, "Resizing virtual size to fit virstor "
321		    "structures.\n");
322		fprintf(stderr, "New virtual size: %ju MB (%zu new chunks)\n",
323		    (uintmax_t)(md.md_virsize / (1024 * 1024)), add_chunks);
324	}
325
326	if (verbose)
327		printf("Total virtual chunks: %zu (%zu MB each), %ju MB total "
328		    "virtual size.\n",
329		    total_chunks, (size_t)(md.md_chunk_size / (1024 * 1024)),
330		    md.md_virsize/(1024 * 1024));
331
332	if ((off_t)md.md_virsize < msize)
333		fprintf(stderr, "WARNING: Virtual storage size < Physical "
334		    "available storage (%ju < %ju)\n", md.md_virsize, msize);
335
336	/* Clear last sector first to spoil all components if device exists. */
337	if (verbose)
338		printf("Clearing metadata on");
339
340	for (i = 1; i < (unsigned)nargs; i++) {
341		snprintf(param, sizeof(param), "arg%u", i);
342		name = gctl_get_ascii(req, "%s", param);
343
344		if (verbose)
345			printf(" %s", name);
346
347		msize = g_get_mediasize(name);
348		ssize = g_get_sectorsize(name);
349		if (msize == 0 || ssize == 0) {
350			gctl_error(req, "Can't retrieve information about "
351			    "%s: %s.", name, strerror(errno));
352			return;
353		}
354		if (msize < (off_t) MAX(md.md_chunk_size*4, map_size))
355			gctl_error(req, "Device %s is too small", name);
356		error = g_metadata_clear(name, NULL);
357		if (error != 0) {
358			gctl_error(req, "Can't clear metadata on %s: %s.", name,
359			    strerror(error));
360			return;
361		}
362	}
363
364
365	/* Write allocation table to the first provider - this needs to be done
366	 * before metadata is written because when kernel tastes it it's too
367	 * late */
368	name = gctl_get_ascii(req, "arg1"); /* device with metadata */
369	if (verbose)
370		printf(".\nWriting allocation table to %s...", name);
371
372	/* How many chunks does the map occupy? */
373	map_chunks = map_size/md.md_chunk_size;
374	if (map_size % md.md_chunk_size != 0)
375		map_chunks++;
376	if (verbose) {
377		printf(" (%zu MB, %d chunks) ", map_size/(1024*1024), map_chunks);
378		fflush(stdout);
379	}
380
381	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
382		fd = open(name, O_RDWR);
383	else {
384		sprintf(param, "%s%s", _PATH_DEV, name);
385		fd = open(param, O_RDWR);
386	}
387	if (fd < 0)
388		gctl_error(req, "Cannot open provider %s to write map", name);
389
390	/* Do it with calloc because there might be a need to set up chunk flags
391	 * in the future */
392	map = calloc(total_chunks, sizeof(*map));
393	if (map == NULL) {
394		gctl_error(req,
395		    "Out of memory (need %zu bytes for allocation map)",
396		    map_size);
397	}
398
399	written = pwrite(fd, map, map_size, 0);
400	free(map);
401	if ((size_t)written != map_size) {
402		if (verbose) {
403			fprintf(stderr, "\nTried to write %zu, written %zd (%s)\n",
404			    map_size, written, strerror(errno));
405		}
406		gctl_error(req, "Error writing out allocation map!");
407		return;
408	}
409	close (fd);
410
411	if (verbose)
412		printf("\nStoring metadata on ");
413
414	/*
415	 * ID is randomly generated, unique for a geom. This is used to
416	 * recognize all providers belonging to one geom.
417	 */
418	md.md_id = arc4random();
419
420	/* Ok, store metadata. */
421	for (i = 1; i < (unsigned)nargs; i++) {
422		snprintf(param, sizeof(param), "arg%u", i);
423		name = gctl_get_ascii(req, "%s", param);
424
425		msize = g_get_mediasize(name);
426		ssize = g_get_sectorsize(name);
427
428		if (verbose)
429			printf("%s ", name);
430
431		/* this provider's position/type in geom */
432		md.no = i - 1;
433		/* this provider's size */
434		md.provsize = msize;
435		/* chunk allocation info */
436		md.chunk_count = md.provsize / md.md_chunk_size;
437		if (verbose)
438			printf("(%u chunks) ", md.chunk_count);
439		/* Check to make sure last sector is unused */
440		if ((off_t)(md.chunk_count * md.md_chunk_size) > (off_t)(msize-ssize))
441		    md.chunk_count--;
442		md.chunk_next = 0;
443		if (i != 1) {
444			md.chunk_reserved = 0;
445			md.flags = 0;
446		} else {
447			md.chunk_reserved = map_chunks * 2;
448			md.flags = VIRSTOR_PROVIDER_ALLOCATED |
449			    VIRSTOR_PROVIDER_CURRENT;
450			md.chunk_next = md.chunk_reserved;
451			if (verbose)
452				printf("(%u reserved) ", md.chunk_reserved);
453		}
454
455		if (!hardcode)
456			bzero(md.provider, sizeof(md.provider));
457		else {
458			/* convert "/dev/something" to "something" */
459			if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) {
460				strlcpy(md.provider, name + sizeof(_PATH_DEV) - 1,
461				    sizeof(md.provider));
462			} else
463				strlcpy(md.provider, name, sizeof(md.provider));
464		}
465		sect = malloc(ssize);
466		if (sect == NULL)
467			err(1, "Cannot allocate sector of %zu bytes", ssize);
468		bzero(sect, ssize);
469		virstor_metadata_encode(&md, sect);
470		error = my_g_metadata_store(name, sect, ssize);
471		free(sect);
472		if (error != 0) {
473			if (verbose)
474				printf("\n");
475			fprintf(stderr, "Can't store metadata on %s: %s.\n",
476			    name, strerror(error));
477			gctl_error(req,
478			    "Not fully done (error storing metadata).");
479			return;
480		}
481	}
482#if 0
483	if (verbose)
484		printf("\n");
485#endif
486}
487
488/* Clears metadata on given provider(s) IF it's owned by us */
489static void
490virstor_clear(struct gctl_req *req)
491{
492	const char *name;
493	char param[32];
494	unsigned i;
495	int nargs, error;
496	int fd;
497
498	nargs = gctl_get_int(req, "nargs");
499	if (nargs < 1) {
500		gctl_error(req, "Too few arguments.");
501		return;
502	}
503	for (i = 0; i < (unsigned)nargs; i++) {
504		snprintf(param, sizeof(param), "arg%u", i);
505		name = gctl_get_ascii(req, "%s", param);
506
507		error = g_metadata_clear(name, G_VIRSTOR_MAGIC);
508		if (error != 0) {
509			fprintf(stderr, "Can't clear metadata on %s: %s "
510			    "(do I own it?)\n", name, strerror(error));
511			gctl_error(req,
512			    "Not fully done (can't clear metadata).");
513			continue;
514		}
515		if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
516			fd = open(name, O_RDWR);
517		else {
518			sprintf(param, "%s%s", _PATH_DEV, name);
519			fd = open(param, O_RDWR);
520		}
521		if (fd < 0) {
522			gctl_error(req, "Cannot clear header sector for %s",
523			    name);
524			continue;
525		}
526		if (verbose)
527			printf("Metadata cleared on %s.\n", name);
528	}
529}
530
531/* Print some metadata information */
532static void
533virstor_metadata_dump(const struct g_virstor_metadata *md)
534{
535	printf("          Magic string: %s\n", md->md_magic);
536	printf("      Metadata version: %u\n", (u_int) md->md_version);
537	printf("           Device name: %s\n", md->md_name);
538	printf("             Device ID: %u\n", (u_int) md->md_id);
539	printf("        Provider index: %u\n", (u_int) md->no);
540	printf("      Active providers: %u\n", (u_int) md->md_count);
541	printf("    Hardcoded provider: %s\n",
542	    md->provider[0] != '\0' ? md->provider : "(not hardcoded)");
543	printf("          Virtual size: %u MB\n",
544	    (unsigned int)(md->md_virsize/(1024 * 1024)));
545	printf("            Chunk size: %u kB\n", md->md_chunk_size / 1024);
546	printf("    Chunks on provider: %u\n", md->chunk_count);
547	printf("           Chunks free: %u\n", md->chunk_count - md->chunk_next);
548	printf("       Reserved chunks: %u\n", md->chunk_reserved);
549}
550
551/* Called by geom(8) via gvirstor_main() to dump metadata information */
552static void
553virstor_dump(struct gctl_req *req)
554{
555	struct g_virstor_metadata md;
556	u_char tmpmd[512];	/* temporary buffer */
557	const char *name;
558	char param[16];
559	int nargs, error, i;
560
561	assert(sizeof(tmpmd) >= sizeof(md));
562
563	nargs = gctl_get_int(req, "nargs");
564	if (nargs < 1) {
565		gctl_error(req, "Too few arguments.");
566		return;
567	}
568	for (i = 0; i < nargs; i++) {
569		snprintf(param, sizeof(param), "arg%u", i);
570		name = gctl_get_ascii(req, "%s", param);
571
572		error = g_metadata_read(name, (u_char *) & tmpmd, sizeof(tmpmd),
573		    G_VIRSTOR_MAGIC);
574		if (error != 0) {
575			fprintf(stderr, "Can't read metadata from %s: %s.\n",
576			    name, strerror(error));
577			gctl_error(req,
578			    "Not fully done (error reading metadata).");
579			continue;
580		}
581		virstor_metadata_decode((u_char *) & tmpmd, &md);
582		printf("Metadata on %s:\n", name);
583		virstor_metadata_dump(&md);
584		printf("\n");
585	}
586}
587