geom_virstor.c revision 213662
1250661Sdavidcs/*-
2250661Sdavidcs * Copyright (c) 2005 Ivan Voras <ivoras@freebsd.org>
3250661Sdavidcs *
4250661Sdavidcs * Redistribution and use in source and binary forms, with or without
5250661Sdavidcs * modification, are permitted provided that the following conditions
6250661Sdavidcs * are met:
7250661Sdavidcs * 1. Redistributions of source code must retain the above copyright
8250661Sdavidcs *    notice, this list of conditions and the following disclaimer.
9250661Sdavidcs * 2. Redistributions in binary form must reproduce the above copyright
10250661Sdavidcs *    notice, this list of conditions and the following disclaimer in the
11250661Sdavidcs *    documentation and/or other materials provided with the distribution.
12250661Sdavidcs *
13250661Sdavidcs * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
14250661Sdavidcs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15250661Sdavidcs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16250661Sdavidcs * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
17250661Sdavidcs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18250661Sdavidcs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19250661Sdavidcs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20250661Sdavidcs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21250661Sdavidcs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22250661Sdavidcs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23250661Sdavidcs * SUCH DAMAGE.
24250661Sdavidcs */
25250661Sdavidcs
26250661Sdavidcs#include <sys/cdefs.h>
27250661Sdavidcs__FBSDID("$FreeBSD: head/sbin/geom/class/virstor/geom_virstor.c 213662 2010-10-09 20:20:27Z ae $");
28250661Sdavidcs
29250661Sdavidcs#include <sys/param.h>
30250661Sdavidcs#include <errno.h>
31250661Sdavidcs#include <paths.h>
32250661Sdavidcs#include <stdio.h>
33250661Sdavidcs#include <stdlib.h>
34250661Sdavidcs#include <stdint.h>
35250661Sdavidcs#include <string.h>
36250661Sdavidcs#include <strings.h>
37250661Sdavidcs#include <fcntl.h>
38250661Sdavidcs#include <unistd.h>
39250661Sdavidcs#include <libgeom.h>
40250661Sdavidcs#include <err.h>
41250661Sdavidcs#include <assert.h>
42250661Sdavidcs
43250661Sdavidcs#include <core/geom.h>
44250661Sdavidcs#include <misc/subr.h>
45250661Sdavidcs
46250661Sdavidcs#include <geom/virstor/g_virstor_md.h>
47250661Sdavidcs#include <geom/virstor/g_virstor.h>
48250661Sdavidcs
49250661Sdavidcsuint32_t lib_version = G_LIB_VERSION;
50250661Sdavidcsuint32_t version = G_VIRSTOR_VERSION;
51250661Sdavidcs
52250661Sdavidcs#define	GVIRSTOR_CHUNK_SIZE	"4M"
53250661Sdavidcs#define	GVIRSTOR_VIR_SIZE	"2T"
54250661Sdavidcs
55250661Sdavidcs#if G_LIB_VERSION == 1
56250661Sdavidcs/* Support RELENG_6 */
57250661Sdavidcs#define G_TYPE_BOOL G_TYPE_NONE
58250661Sdavidcs#endif
59250661Sdavidcs
60250661Sdavidcs/*
61250661Sdavidcs * virstor_main gets called by the geom(8) utility
62250661Sdavidcs */
63250661Sdavidcsstatic void virstor_main(struct gctl_req *req, unsigned flags);
64250661Sdavidcs
65250661Sdavidcsstruct g_command class_commands[] = {
66250661Sdavidcs	{ "clear", G_FLAG_VERBOSE, virstor_main, G_NULL_OPTS,
67250661Sdavidcs	    "[-v] prov ..."
68250661Sdavidcs	},
69250661Sdavidcs	{ "dump", 0, virstor_main, G_NULL_OPTS,
70250661Sdavidcs	    "prov ..."
71250661Sdavidcs	},
72250661Sdavidcs	{ "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, virstor_main,
73250661Sdavidcs	    {
74250661Sdavidcs		{ 'h', "hardcode", NULL, G_TYPE_BOOL},
75250661Sdavidcs		{ 'm', "chunk_size", GVIRSTOR_CHUNK_SIZE, G_TYPE_NUMBER},
76250661Sdavidcs		{ 's', "vir_size", GVIRSTOR_VIR_SIZE, G_TYPE_NUMBER},
77250661Sdavidcs		G_OPT_SENTINEL
78250661Sdavidcs	    },
79250661Sdavidcs	    "[-h] [-v] [-m chunk_size] [-s vir_size] name provider0 [provider1 ...]"
80250661Sdavidcs	},
81250661Sdavidcs	{ "destroy", G_FLAG_VERBOSE, NULL,
82250661Sdavidcs	    {
83250661Sdavidcs		{ 'f', "force", NULL, G_TYPE_BOOL},
84250661Sdavidcs		G_OPT_SENTINEL
85250661Sdavidcs	    },
86250661Sdavidcs	    "[-fv] name ..."
87250661Sdavidcs	},
88250661Sdavidcs	{ "stop", G_FLAG_VERBOSE, NULL,
89250661Sdavidcs	    {
90250661Sdavidcs		{ 'f', "force", NULL, G_TYPE_BOOL},
91250661Sdavidcs		G_OPT_SENTINEL
92250661Sdavidcs	    },
93250661Sdavidcs	    "[-fv] name ... (alias for \"destroy\")"
94250661Sdavidcs	},
95250661Sdavidcs	{ "add", G_FLAG_VERBOSE, NULL,
96250661Sdavidcs	    {
97250661Sdavidcs		{ 'h', "hardcode", NULL, G_TYPE_BOOL},
98250661Sdavidcs		G_OPT_SENTINEL
99250661Sdavidcs	    },
100250661Sdavidcs	    "[-vh] name prov [prov ...]"
101250661Sdavidcs	},
102250661Sdavidcs	{ "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
103250661Sdavidcs	    "[-v] name ..."
104250661Sdavidcs	},
105250661Sdavidcs	G_CMD_SENTINEL
106250661Sdavidcs};
107250661Sdavidcs
108250661Sdavidcsstatic int verbose = 0;
109250661Sdavidcs
110250661Sdavidcs/* Helper functions' declarations */
111250661Sdavidcsstatic void virstor_clear(struct gctl_req *req);
112250661Sdavidcsstatic void virstor_dump(struct gctl_req *req);
113250661Sdavidcsstatic void virstor_label(struct gctl_req *req);
114250661Sdavidcs
115250661Sdavidcs/* Dispatcher function (no real work done here, only verbose flag recorder) */
116250661Sdavidcsstatic void
117250661Sdavidcsvirstor_main(struct gctl_req *req, unsigned flags)
118250661Sdavidcs{
119250661Sdavidcs	const char *name;
120250661Sdavidcs
121250661Sdavidcs	if ((flags & G_FLAG_VERBOSE) != 0)
122250661Sdavidcs		verbose = 1;
123250661Sdavidcs
124250661Sdavidcs	name = gctl_get_ascii(req, "verb");
125250661Sdavidcs	if (name == NULL) {
126250661Sdavidcs		gctl_error(req, "No '%s' argument.", "verb");
127250661Sdavidcs		return;
128250661Sdavidcs	}
129250661Sdavidcs	if (strcmp(name, "label") == 0)
130250661Sdavidcs		virstor_label(req);
131250661Sdavidcs	else if (strcmp(name, "clear") == 0)
132250661Sdavidcs		virstor_clear(req);
133250661Sdavidcs	else if (strcmp(name, "dump") == 0)
134250661Sdavidcs		virstor_dump(req);
135250661Sdavidcs	else
136250661Sdavidcs		gctl_error(req, "%s: Unknown command: %s.", __func__, name);
137250661Sdavidcs
138250661Sdavidcs	/* No CTASSERT in userland
139250661Sdavidcs	CTASSERT(VIRSTOR_MAP_BLOCK_ENTRIES*VIRSTOR_MAP_ENTRY_SIZE == MAXPHYS);
140250661Sdavidcs	*/
141250661Sdavidcs}
142250661Sdavidcs
143250661Sdavidcsstatic void
144250661Sdavidcspathgen(const char *name, char *path, size_t size)
145250661Sdavidcs{
146250661Sdavidcs
147250661Sdavidcs	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0)
148250661Sdavidcs		snprintf(path, size, "%s%s", _PATH_DEV, name);
149250661Sdavidcs	else
150250661Sdavidcs		strlcpy(path, name, size);
151250661Sdavidcs}
152250661Sdavidcs
153250661Sdavidcsstatic int
154250661Sdavidcsmy_g_metadata_store(const char *name, u_char *md, size_t size)
155250661Sdavidcs{
156250661Sdavidcs	char path[MAXPATHLEN];
157250661Sdavidcs	unsigned sectorsize;
158250661Sdavidcs	off_t mediasize;
159250661Sdavidcs	u_char *sector;
160250661Sdavidcs	int error, fd;
161250661Sdavidcs
162250661Sdavidcs	pathgen(name, path, sizeof(path));
163250661Sdavidcs	sector = NULL;
164250661Sdavidcs	error = 0;
165250661Sdavidcs
166250661Sdavidcs	fd = open(path, O_RDWR);
167250661Sdavidcs	if (fd == -1)
168250661Sdavidcs		return (errno);
169250661Sdavidcs	mediasize = g_get_mediasize(name);
170250661Sdavidcs	if (mediasize == 0) {
171250661Sdavidcs		error = errno;
172250661Sdavidcs		goto out;
173250661Sdavidcs	}
174250661Sdavidcs	sectorsize = g_get_sectorsize(name);
175250661Sdavidcs	if (sectorsize == 0) {
176250661Sdavidcs		error = errno;
177250661Sdavidcs		goto out;
178250661Sdavidcs	}
179250661Sdavidcs	assert(sectorsize >= size);
180250661Sdavidcs	sector = malloc(sectorsize);
181250661Sdavidcs	if (sector == NULL) {
182250661Sdavidcs		error = ENOMEM;
183250661Sdavidcs		goto out;
184250661Sdavidcs	}
185250661Sdavidcs	bcopy(md, sector, size);
186250661Sdavidcs	if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
187250661Sdavidcs	    (ssize_t)sectorsize) {
188250661Sdavidcs		error = errno;
189250661Sdavidcs		goto out;
190250661Sdavidcs	}
191250661Sdavidcsout:
192250661Sdavidcs	if (sector != NULL)
193250661Sdavidcs		free(sector);
194250661Sdavidcs	close(fd);
195250661Sdavidcs	return (error);
196250661Sdavidcs}
197250661Sdavidcs
198250661Sdavidcs/*
199250661Sdavidcs * Labels a new geom Meaning: parses and checks the parameters, calculates &
200250661Sdavidcs * writes metadata to the relevant providers so when the next round of
201250661Sdavidcs * "tasting" comes (which will be just after the provider(s) are closed) geom
202250661Sdavidcs * can be instantiated with the tasted metadata.
203250661Sdavidcs */
204250661Sdavidcsstatic void
205250661Sdavidcsvirstor_label(struct gctl_req *req)
206250661Sdavidcs{
207250661Sdavidcs	struct g_virstor_metadata md;
208250661Sdavidcs	off_t msize;
209250661Sdavidcs	unsigned char *sect;
210250661Sdavidcs	unsigned int i;
211250661Sdavidcs	size_t ssize, secsize;
212250661Sdavidcs	const char *name;
213250661Sdavidcs	char param[32];
214250661Sdavidcs	int hardcode, nargs, error;
215250661Sdavidcs	struct virstor_map_entry *map;
216250661Sdavidcs	size_t total_chunks;	/* We'll run out of memory if
217250661Sdavidcs				   this needs to be bigger. */
218250661Sdavidcs	unsigned int map_chunks; /* Chunks needed by the map (map size). */
219250661Sdavidcs	size_t map_size;	/* In bytes. */
220250661Sdavidcs	ssize_t written;
221250661Sdavidcs	int fd;
222250661Sdavidcs
223250661Sdavidcs	nargs = gctl_get_int(req, "nargs");
224250661Sdavidcs	if (nargs < 2) {
225250661Sdavidcs		gctl_error(req, "Too few arguments (%d): expecting: name "
226250661Sdavidcs		    "provider0 [provider1 ...]", nargs);
227250661Sdavidcs		return;
228250661Sdavidcs	}
229250661Sdavidcs
230250661Sdavidcs	hardcode = gctl_get_int(req, "hardcode");
231250661Sdavidcs
232250661Sdavidcs	/*
233250661Sdavidcs	 * Initialize constant parts of metadata: magic signature, version,
234250661Sdavidcs	 * name.
235250661Sdavidcs	 */
236250661Sdavidcs	bzero(&md, sizeof(md));
237250661Sdavidcs	strlcpy(md.md_magic, G_VIRSTOR_MAGIC, sizeof(md.md_magic));
238250661Sdavidcs	md.md_version = G_VIRSTOR_VERSION;
239250661Sdavidcs	name = gctl_get_ascii(req, "arg0");
240250661Sdavidcs	if (name == NULL) {
241250661Sdavidcs		gctl_error(req, "No 'arg%u' argument.", 0);
242250661Sdavidcs		return;
243250661Sdavidcs	}
244250661Sdavidcs	strlcpy(md.md_name, name, sizeof(md.md_name));
245250661Sdavidcs
246250661Sdavidcs	md.md_virsize = (off_t)gctl_get_intmax(req, "vir_size");
247250661Sdavidcs	md.md_chunk_size = gctl_get_intmax(req, "chunk_size");
248250661Sdavidcs	md.md_count = nargs - 1;
249250661Sdavidcs
250250661Sdavidcs	if (md.md_virsize == 0 || md.md_chunk_size == 0) {
251250661Sdavidcs		gctl_error(req, "Virtual size and chunk size must be non-zero");
252250661Sdavidcs		return;
253250661Sdavidcs	}
254250661Sdavidcs
255250661Sdavidcs	if (md.md_chunk_size % MAXPHYS != 0) {
256250661Sdavidcs		/* XXX: This is not strictly needed, but it's convenient to
257250661Sdavidcs		 * impose some limitations on it, so why not MAXPHYS. */
258250661Sdavidcs		size_t new_size = (md.md_chunk_size / MAXPHYS) * MAXPHYS;
259250661Sdavidcs		if (new_size < md.md_chunk_size)
260250661Sdavidcs			new_size += MAXPHYS;
261250661Sdavidcs		fprintf(stderr, "Resizing chunk size to be a multiple of "
262250661Sdavidcs		    "MAXPHYS (%d kB).\n", MAXPHYS / 1024);
263250661Sdavidcs		fprintf(stderr, "New chunk size: %zu kB\n", new_size / 1024);
264250661Sdavidcs		md.md_chunk_size = new_size;
265250661Sdavidcs	}
266250661Sdavidcs
267250661Sdavidcs	if (md.md_virsize % md.md_chunk_size != 0) {
268250661Sdavidcs		off_t chunk_count = md.md_virsize / md.md_chunk_size;
269250661Sdavidcs		md.md_virsize = chunk_count * md.md_chunk_size;
270250661Sdavidcs		fprintf(stderr, "Resizing virtual size to be a multiple of "
271250661Sdavidcs		    "chunk size.\n");
272250661Sdavidcs		fprintf(stderr, "New virtual size: %zu MB\n",
273250661Sdavidcs		    (size_t)(md.md_virsize/(1024 * 1024)));
274250661Sdavidcs	}
275250661Sdavidcs
276250661Sdavidcs	msize = secsize = 0;
277250661Sdavidcs	for (i = 1; i < (unsigned)nargs; i++) {
278250661Sdavidcs		snprintf(param, sizeof(param), "arg%u", i);
279250661Sdavidcs		name = gctl_get_ascii(req, param);
280250661Sdavidcs		ssize = g_get_sectorsize(name);
281250661Sdavidcs		if (ssize == 0)
282250661Sdavidcs			fprintf(stderr, "%s for %s\n", strerror(errno), name);
283250661Sdavidcs		msize += g_get_mediasize(name);
284250661Sdavidcs		if (secsize == 0)
285250661Sdavidcs			secsize = ssize;
286250661Sdavidcs		else if (secsize != ssize) {
287250661Sdavidcs			gctl_error(req, "Devices need to have same sector size "
288250661Sdavidcs			    "(%u on %s needs to be %u).",
289250661Sdavidcs			    (u_int)ssize, name, (u_int)secsize);
290250661Sdavidcs			return;
291250661Sdavidcs		}
292250661Sdavidcs	}
293250661Sdavidcs
294250661Sdavidcs	if (secsize == 0) {
295250661Sdavidcs		gctl_error(req, "Device not specified");
296250661Sdavidcs		return;
297250661Sdavidcs	}
298250661Sdavidcs
299250661Sdavidcs	if (md.md_chunk_size % secsize != 0) {
300250661Sdavidcs		fprintf(stderr, "Error: chunk size is not a multiple of sector "
301250661Sdavidcs		    "size.");
302250661Sdavidcs		gctl_error(req, "Chunk size (in bytes) must be multiple of %u.",
303250661Sdavidcs		    (unsigned int)secsize);
304250661Sdavidcs		return;
305250661Sdavidcs	}
306250661Sdavidcs
307250661Sdavidcs	total_chunks = md.md_virsize / md.md_chunk_size;
308250661Sdavidcs	map_size = total_chunks * sizeof(*map);
309250661Sdavidcs	assert(md.md_virsize % md.md_chunk_size == 0);
310250661Sdavidcs
311250661Sdavidcs	ssize = map_size % secsize;
312250661Sdavidcs	if (ssize != 0) {
313250661Sdavidcs		size_t add_chunks = (secsize - ssize) / sizeof(*map);
314250661Sdavidcs		total_chunks += add_chunks;
315250661Sdavidcs		md.md_virsize = (off_t)total_chunks * (off_t)md.md_chunk_size;
316250661Sdavidcs		map_size = total_chunks * sizeof(*map);
317250661Sdavidcs		fprintf(stderr, "Resizing virtual size to fit virstor "
318250661Sdavidcs		    "structures.\n");
319250661Sdavidcs		fprintf(stderr, "New virtual size: %ju MB (%zu new chunks)\n",
320250661Sdavidcs		    (uintmax_t)(md.md_virsize / (1024 * 1024)), add_chunks);
321250661Sdavidcs	}
322250661Sdavidcs
323250661Sdavidcs	if (verbose)
324250661Sdavidcs		printf("Total virtual chunks: %zu (%zu MB each), %ju MB total "
325250661Sdavidcs		    "virtual size.\n",
326250661Sdavidcs		    total_chunks, (size_t)(md.md_chunk_size / (1024 * 1024)),
327250661Sdavidcs		    md.md_virsize/(1024 * 1024));
328250661Sdavidcs
329250661Sdavidcs	if ((off_t)md.md_virsize < msize)
330250661Sdavidcs		fprintf(stderr, "WARNING: Virtual storage size < Physical "
331250661Sdavidcs		    "available storage (%ju < %ju)\n", md.md_virsize, msize);
332250661Sdavidcs
333250661Sdavidcs	/* Clear last sector first to spoil all components if device exists. */
334250661Sdavidcs	if (verbose)
335250661Sdavidcs		printf("Clearing metadata on");
336250661Sdavidcs
337250661Sdavidcs	for (i = 1; i < (unsigned)nargs; i++) {
338250661Sdavidcs		snprintf(param, sizeof(param), "arg%u", i);
339250661Sdavidcs		name = gctl_get_ascii(req, param);
340250661Sdavidcs
341250661Sdavidcs		if (verbose)
342250661Sdavidcs			printf(" %s", name);
343250661Sdavidcs
344250661Sdavidcs		msize = g_get_mediasize(name);
345250661Sdavidcs		ssize = g_get_sectorsize(name);
346250661Sdavidcs		if (msize == 0 || ssize == 0) {
347250661Sdavidcs			gctl_error(req, "Can't retrieve information about "
348250661Sdavidcs			    "%s: %s.", name, strerror(errno));
349250661Sdavidcs			return;
350250661Sdavidcs		}
351250661Sdavidcs		if (msize < (off_t) MAX(md.md_chunk_size*4, map_size))
352250661Sdavidcs			gctl_error(req, "Device %s is too small", name);
353250661Sdavidcs		error = g_metadata_clear(name, NULL);
354250661Sdavidcs		if (error != 0) {
355250661Sdavidcs			gctl_error(req, "Can't clear metadata on %s: %s.", name,
356250661Sdavidcs			    strerror(error));
357250661Sdavidcs			return;
358250661Sdavidcs		}
359250661Sdavidcs	}
360250661Sdavidcs
361250661Sdavidcs
362250661Sdavidcs	/* Write allocation table to the first provider - this needs to be done
363250661Sdavidcs	 * before metadata is written because when kernel tastes it it's too
364250661Sdavidcs	 * late */
365250661Sdavidcs	name = gctl_get_ascii(req, "arg1"); /* device with metadata */
366250661Sdavidcs	if (verbose)
367250661Sdavidcs		printf(".\nWriting allocation table to %s...", name);
368250661Sdavidcs
369250661Sdavidcs	/* How many chunks does the map occupy? */
370250661Sdavidcs	map_chunks = map_size/md.md_chunk_size;
371250661Sdavidcs	if (map_size % md.md_chunk_size != 0)
372250661Sdavidcs		map_chunks++;
373250661Sdavidcs	if (verbose) {
374250661Sdavidcs		printf(" (%zu MB, %d chunks) ", map_size/(1024*1024), map_chunks);
375250661Sdavidcs		fflush(stdout);
376250661Sdavidcs	}
377250661Sdavidcs
378250661Sdavidcs	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
379250661Sdavidcs		fd = open(name, O_RDWR);
380250661Sdavidcs	else {
381250661Sdavidcs		sprintf(param, "%s%s", _PATH_DEV, name);
382250661Sdavidcs		fd = open(param, O_RDWR);
383250661Sdavidcs	}
384250661Sdavidcs	if (fd < 0)
385250661Sdavidcs		gctl_error(req, "Cannot open provider %s to write map", name);
386250661Sdavidcs
387250661Sdavidcs	/* Do it with calloc because there might be a need to set up chunk flags
388250661Sdavidcs	 * in the future */
389250661Sdavidcs	map = calloc(total_chunks, sizeof(*map));
390250661Sdavidcs	if (map == NULL) {
391250661Sdavidcs		gctl_error(req,
392250661Sdavidcs		    "Out of memory (need %zu bytes for allocation map)",
393250661Sdavidcs		    map_size);
394250661Sdavidcs	}
395250661Sdavidcs
396250661Sdavidcs	written = pwrite(fd, map, map_size, 0);
397250661Sdavidcs	free(map);
398250661Sdavidcs	if ((size_t)written != map_size) {
399250661Sdavidcs		if (verbose) {
400250661Sdavidcs			fprintf(stderr, "\nTried to write %zu, written %zd (%s)\n",
401250661Sdavidcs			    map_size, written, strerror(errno));
402250661Sdavidcs		}
403250661Sdavidcs		gctl_error(req, "Error writing out allocation map!");
404250661Sdavidcs		return;
405250661Sdavidcs	}
406250661Sdavidcs	close (fd);
407250661Sdavidcs
408250661Sdavidcs	if (verbose)
409250661Sdavidcs		printf("\nStoring metadata on ");
410250661Sdavidcs
411250661Sdavidcs	/*
412250661Sdavidcs	 * ID is randomly generated, unique for a geom. This is used to
413250661Sdavidcs	 * recognize all providers belonging to one geom.
414250661Sdavidcs	 */
415250661Sdavidcs	md.md_id = arc4random();
416250661Sdavidcs
417250661Sdavidcs	/* Ok, store metadata. */
418250661Sdavidcs	for (i = 1; i < (unsigned)nargs; i++) {
419250661Sdavidcs		snprintf(param, sizeof(param), "arg%u", i);
420250661Sdavidcs		name = gctl_get_ascii(req, param);
421250661Sdavidcs
422250661Sdavidcs		msize = g_get_mediasize(name);
423250661Sdavidcs		ssize = g_get_sectorsize(name);
424250661Sdavidcs
425250661Sdavidcs		if (verbose)
426250661Sdavidcs			printf("%s ", name);
427250661Sdavidcs
428250661Sdavidcs		/* this provider's position/type in geom */
429250661Sdavidcs		md.no = i - 1;
430250661Sdavidcs		/* this provider's size */
431250661Sdavidcs		md.provsize = msize;
432250661Sdavidcs		/* chunk allocation info */
433250661Sdavidcs		md.chunk_count = md.provsize / md.md_chunk_size;
434250661Sdavidcs		if (verbose)
435250661Sdavidcs			printf("(%u chunks) ", md.chunk_count);
436250661Sdavidcs		/* Check to make sure last sector is unused */
437250661Sdavidcs		if ((off_t)(md.chunk_count * md.md_chunk_size) > msize-ssize)
438250661Sdavidcs		    md.chunk_count--;
439250661Sdavidcs		md.chunk_next = 0;
440250661Sdavidcs		if (i != 1) {
441250661Sdavidcs			md.chunk_reserved = 0;
442250661Sdavidcs			md.flags = 0;
443250661Sdavidcs		} else {
444250661Sdavidcs			md.chunk_reserved = map_chunks * 2;
445250661Sdavidcs			md.flags = VIRSTOR_PROVIDER_ALLOCATED |
446250661Sdavidcs			    VIRSTOR_PROVIDER_CURRENT;
447250661Sdavidcs			md.chunk_next = md.chunk_reserved;
448250661Sdavidcs			if (verbose)
449250661Sdavidcs				printf("(%u reserved) ", md.chunk_reserved);
450250661Sdavidcs		}
451250661Sdavidcs
452250661Sdavidcs		if (!hardcode)
453250661Sdavidcs			bzero(md.provider, sizeof(md.provider));
454250661Sdavidcs		else {
455250661Sdavidcs			/* convert "/dev/something" to "something" */
456250661Sdavidcs			if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) {
457250661Sdavidcs				strlcpy(md.provider, name + sizeof(_PATH_DEV) - 1,
458250661Sdavidcs				    sizeof(md.provider));
459250661Sdavidcs			} else
460250661Sdavidcs				strlcpy(md.provider, name, sizeof(md.provider));
461250661Sdavidcs		}
462250661Sdavidcs		sect = malloc(ssize);
463250661Sdavidcs		if (sect == NULL)
464250661Sdavidcs			err(1, "Cannot allocate sector of %zu bytes", ssize);
465250661Sdavidcs		bzero(sect, ssize);
466250661Sdavidcs		virstor_metadata_encode(&md, sect);
467250661Sdavidcs		error = my_g_metadata_store(name, sect, ssize);
468250661Sdavidcs		free(sect);
469250661Sdavidcs		if (error != 0) {
470250661Sdavidcs			if (verbose)
471250661Sdavidcs				printf("\n");
472250661Sdavidcs			fprintf(stderr, "Can't store metadata on %s: %s.\n",
473250661Sdavidcs			    name, strerror(error));
474250661Sdavidcs			gctl_error(req,
475250661Sdavidcs			    "Not fully done (error storing metadata).");
476250661Sdavidcs			return;
477250661Sdavidcs		}
478250661Sdavidcs	}
479250661Sdavidcs#if 0
480250661Sdavidcs	if (verbose)
481250661Sdavidcs		printf("\n");
482250661Sdavidcs#endif
483250661Sdavidcs}
484250661Sdavidcs
485250661Sdavidcs/* Clears metadata on given provider(s) IF it's owned by us */
486250661Sdavidcsstatic void
487250661Sdavidcsvirstor_clear(struct gctl_req *req)
488250661Sdavidcs{
489250661Sdavidcs	const char *name;
490250661Sdavidcs	char param[32];
491250661Sdavidcs	unsigned i;
492250661Sdavidcs	int nargs, error;
493250661Sdavidcs	int fd;
494250661Sdavidcs
495250661Sdavidcs	nargs = gctl_get_int(req, "nargs");
496250661Sdavidcs	if (nargs < 1) {
497250661Sdavidcs		gctl_error(req, "Too few arguments.");
498250661Sdavidcs		return;
499250661Sdavidcs	}
500250661Sdavidcs	for (i = 0; i < (unsigned)nargs; i++) {
501250661Sdavidcs		snprintf(param, sizeof(param), "arg%u", i);
502250661Sdavidcs		name = gctl_get_ascii(req, param);
503250661Sdavidcs
504250661Sdavidcs		error = g_metadata_clear(name, G_VIRSTOR_MAGIC);
505250661Sdavidcs		if (error != 0) {
506250661Sdavidcs			fprintf(stderr, "Can't clear metadata on %s: %s "
507250661Sdavidcs			    "(do I own it?)\n", name, strerror(error));
508250661Sdavidcs			gctl_error(req,
509250661Sdavidcs			    "Not fully done (can't clear metadata).");
510250661Sdavidcs			continue;
511250661Sdavidcs		}
512250661Sdavidcs		if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
513250661Sdavidcs			fd = open(name, O_RDWR);
514250661Sdavidcs		else {
515250661Sdavidcs			sprintf(param, "%s%s", _PATH_DEV, name);
516250661Sdavidcs			fd = open(param, O_RDWR);
517250661Sdavidcs		}
518250661Sdavidcs		if (fd < 0) {
519250661Sdavidcs			gctl_error(req, "Cannot clear header sector for %s",
520250661Sdavidcs			    name);
521250661Sdavidcs			continue;
522250661Sdavidcs		}
523250661Sdavidcs		if (verbose)
524250661Sdavidcs			printf("Metadata cleared on %s.\n", name);
525250661Sdavidcs	}
526250661Sdavidcs}
527250661Sdavidcs
528250661Sdavidcs/* Print some metadata information */
529250661Sdavidcsstatic void
530250661Sdavidcsvirstor_metadata_dump(const struct g_virstor_metadata *md)
531250661Sdavidcs{
532250661Sdavidcs	printf("          Magic string: %s\n", md->md_magic);
533250661Sdavidcs	printf("      Metadata version: %u\n", (u_int) md->md_version);
534250661Sdavidcs	printf("           Device name: %s\n", md->md_name);
535250661Sdavidcs	printf("             Device ID: %u\n", (u_int) md->md_id);
536250661Sdavidcs	printf("        Provider index: %u\n", (u_int) md->no);
537250661Sdavidcs	printf("      Active providers: %u\n", (u_int) md->md_count);
538250661Sdavidcs	printf("    Hardcoded provider: %s\n",
539250661Sdavidcs	    md->provider[0] != '\0' ? md->provider : "(not hardcoded)");
540250661Sdavidcs	printf("          Virtual size: %u MB\n",
541250661Sdavidcs	    (unsigned int)(md->md_virsize/(1024 * 1024)));
542250661Sdavidcs	printf("            Chunk size: %u kB\n", md->md_chunk_size / 1024);
543250661Sdavidcs	printf("    Chunks on provider: %u\n", md->chunk_count);
544250661Sdavidcs	printf("           Chunks free: %u\n", md->chunk_count - md->chunk_next);
545250661Sdavidcs	printf("       Reserved chunks: %u\n", md->chunk_reserved);
546250661Sdavidcs}
547250661Sdavidcs
548250661Sdavidcs/* Called by geom(8) via gvirstor_main() to dump metadata information */
549250661Sdavidcsstatic void
550250661Sdavidcsvirstor_dump(struct gctl_req *req)
551250661Sdavidcs{
552250661Sdavidcs	struct g_virstor_metadata md;
553250661Sdavidcs	u_char tmpmd[512];	/* temporary buffer */
554250661Sdavidcs	const char *name;
555250661Sdavidcs	char param[16];
556250661Sdavidcs	int nargs, error, i;
557250661Sdavidcs
558250661Sdavidcs	assert(sizeof(tmpmd) >= sizeof(md));
559250661Sdavidcs
560250661Sdavidcs	nargs = gctl_get_int(req, "nargs");
561250661Sdavidcs	if (nargs < 1) {
562250661Sdavidcs		gctl_error(req, "Too few arguments.");
563250661Sdavidcs		return;
564250661Sdavidcs	}
565250661Sdavidcs	for (i = 0; i < nargs; i++) {
566250661Sdavidcs		snprintf(param, sizeof(param), "arg%u", i);
567250661Sdavidcs		name = gctl_get_ascii(req, param);
568250661Sdavidcs
569250661Sdavidcs		error = g_metadata_read(name, (u_char *) & tmpmd, sizeof(tmpmd),
570250661Sdavidcs		    G_VIRSTOR_MAGIC);
571250661Sdavidcs		if (error != 0) {
572250661Sdavidcs			fprintf(stderr, "Can't read metadata from %s: %s.\n",
573250661Sdavidcs			    name, strerror(error));
574250661Sdavidcs			gctl_error(req,
575250661Sdavidcs			    "Not fully done (error reading metadata).");
576250661Sdavidcs			continue;
577250661Sdavidcs		}
578250661Sdavidcs		virstor_metadata_decode((u_char *) & tmpmd, &md);
579250661Sdavidcs		printf("Metadata on %s:\n", name);
580250661Sdavidcs		virstor_metadata_dump(&md);
581250661Sdavidcs		printf("\n");
582250661Sdavidcs	}
583250661Sdavidcs}
584250661Sdavidcs