geom_virstor.c revision 181471
1172302Spjd/*-
2172302Spjd * Copyright (c) 2005 Ivan Voras <ivoras@freebsd.org>
3172302Spjd *
4172302Spjd * Redistribution and use in source and binary forms, with or without
5172302Spjd * modification, are permitted provided that the following conditions
6172302Spjd * are met:
7172302Spjd * 1. Redistributions of source code must retain the above copyright
8172302Spjd *    notice, this list of conditions and the following disclaimer.
9172302Spjd * 2. Redistributions in binary form must reproduce the above copyright
10172302Spjd *    notice, this list of conditions and the following disclaimer in the
11172302Spjd *    documentation and/or other materials provided with the distribution.
12172302Spjd *
13172302Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
14172302Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15172302Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16172302Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
17172302Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18172302Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19172302Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20172302Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21172302Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22172302Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23172302Spjd * SUCH DAMAGE.
24172302Spjd */
25172302Spjd
26172302Spjd#include <sys/cdefs.h>
27172302Spjd__FBSDID("$FreeBSD: head/sbin/geom/class/virstor/geom_virstor.c 181471 2008-08-09 16:47:30Z ivoras $");
28172302Spjd
29172302Spjd#include <sys/param.h>
30172302Spjd#include <errno.h>
31172302Spjd#include <paths.h>
32172302Spjd#include <stdio.h>
33172302Spjd#include <stdlib.h>
34172302Spjd#include <stdint.h>
35172302Spjd#include <string.h>
36172302Spjd#include <strings.h>
37172302Spjd#include <fcntl.h>
38172302Spjd#include <unistd.h>
39172302Spjd#include <libgeom.h>
40172302Spjd#include <err.h>
41172302Spjd#include <assert.h>
42172302Spjd
43172302Spjd#include <core/geom.h>
44172302Spjd#include <misc/subr.h>
45172302Spjd
46172302Spjd#include <geom/virstor/g_virstor_md.h>
47172302Spjd#include <geom/virstor/g_virstor.h>
48172302Spjd
49172302Spjduint32_t lib_version = G_LIB_VERSION;
50172302Spjduint32_t version = G_VIRSTOR_VERSION;
51172302Spjdstatic intmax_t chunk_size = 4 * 1024 * 1024; /* in kB (default: 4 MB) */
52172302Spjdstatic intmax_t vir_size = 2ULL << 40; /* in MB (default: 2 TB) */
53172302Spjd
54172302Spjd#if G_LIB_VERSION == 1
55172302Spjd/* Support RELENG_6 */
56172302Spjd#define G_TYPE_BOOL G_TYPE_NONE
57172302Spjd#endif
58172302Spjd
59172302Spjd/*
60172302Spjd * virstor_main gets called by the geom(8) utility
61172302Spjd */
62172302Spjdstatic void virstor_main(struct gctl_req *req, unsigned flags);
63172302Spjd
64172302Spjdstruct g_command class_commands[] = {
65172302Spjd	{"clear", G_FLAG_VERBOSE, virstor_main, G_NULL_OPTS, NULL,
66172302Spjd		"[-v] prov ..."
67172302Spjd	},
68172302Spjd	{"dump", 0, virstor_main, G_NULL_OPTS, NULL,
69172302Spjd		"prov ..."
70172302Spjd	},
71172302Spjd	{"label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, virstor_main,
72172302Spjd		{
73172302Spjd			{'h', "hardcode", NULL, G_TYPE_BOOL},
74172302Spjd			{'m', "chunk_size", &chunk_size, G_TYPE_NUMBER},
75172302Spjd			{'s', "vir_size", &vir_size, G_TYPE_NUMBER},
76172302Spjd			G_OPT_SENTINEL
77172302Spjd		},
78172302Spjd		NULL, "[-h] [-v] [-m chunk_size] [-s vir_size] name provider0 [provider1 ...]"
79172302Spjd	},
80172302Spjd	{"destroy", G_FLAG_VERBOSE, NULL,
81172302Spjd		{
82172302Spjd			{'f', "force", NULL, G_TYPE_BOOL},
83172302Spjd			G_OPT_SENTINEL
84172302Spjd		},
85172302Spjd		NULL, "[-fv] name ..."
86172302Spjd	},
87172302Spjd	{"stop", G_FLAG_VERBOSE, NULL,
88172302Spjd		{
89172302Spjd			{'f', "force", NULL, G_TYPE_BOOL},
90172302Spjd			G_OPT_SENTINEL
91172302Spjd		},
92172302Spjd		NULL, "[-fv] name ... (alias for \"destroy\")"
93172302Spjd	},
94172302Spjd	{"add", G_FLAG_VERBOSE, NULL,
95172302Spjd		{
96172302Spjd			{'h', "hardcode", NULL, G_TYPE_BOOL},
97172302Spjd			G_OPT_SENTINEL
98172302Spjd		},
99172302Spjd		NULL, "[-vh] name prov [prov ...]"
100172302Spjd	},
101172302Spjd	{"remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, NULL,
102172302Spjd		"[-v] name ..."
103172302Spjd	},
104172302Spjd	G_CMD_SENTINEL
105172302Spjd};
106172302Spjd
107172302Spjdstatic int verbose = 0;
108172302Spjd
109172302Spjd/* Helper functions' declarations */
110172302Spjdstatic void virstor_clear(struct gctl_req *req);
111172302Spjdstatic void virstor_dump(struct gctl_req *req);
112172302Spjdstatic void virstor_label(struct gctl_req *req);
113172302Spjd
114172302Spjd/* Dispatcher function (no real work done here, only verbose flag recorder) */
115172302Spjdstatic void
116172302Spjdvirstor_main(struct gctl_req *req, unsigned flags)
117172302Spjd{
118172302Spjd	const char *name;
119172302Spjd
120172302Spjd	if ((flags & G_FLAG_VERBOSE) != 0)
121172302Spjd		verbose = 1;
122172302Spjd
123172302Spjd	name = gctl_get_ascii(req, "verb");
124172302Spjd	if (name == NULL) {
125172302Spjd		gctl_error(req, "No '%s' argument.", "verb");
126172302Spjd		return;
127172302Spjd	}
128172302Spjd	if (strcmp(name, "label") == 0)
129172302Spjd		virstor_label(req);
130172302Spjd	else if (strcmp(name, "clear") == 0)
131172302Spjd		virstor_clear(req);
132172302Spjd	else if (strcmp(name, "dump") == 0)
133172302Spjd		virstor_dump(req);
134172302Spjd	else
135172302Spjd		gctl_error(req, "%s: Unknown command: %s.", __func__, name);
136172302Spjd
137172302Spjd	/* No CTASSERT in userland
138172302Spjd	CTASSERT(VIRSTOR_MAP_BLOCK_ENTRIES*VIRSTOR_MAP_ENTRY_SIZE == MAXPHYS);
139172302Spjd	*/
140172302Spjd}
141172302Spjd
142172302Spjdstatic void
143172302Spjdpathgen(const char *name, char *path, size_t size)
144172302Spjd{
145172302Spjd
146172302Spjd	if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) != 0)
147172302Spjd		snprintf(path, size, "%s%s", _PATH_DEV, name);
148172302Spjd	else
149172302Spjd		strlcpy(path, name, size);
150172302Spjd}
151172302Spjd
152172302Spjdstatic int
153172302Spjdmy_g_metadata_store(const char *name, u_char *md, size_t size)
154172302Spjd{
155172302Spjd	char path[MAXPATHLEN];
156172302Spjd	unsigned sectorsize;
157172302Spjd	off_t mediasize;
158172302Spjd	u_char *sector;
159172302Spjd	int error, fd;
160172302Spjd
161172302Spjd	pathgen(name, path, sizeof(path));
162172302Spjd	sector = NULL;
163172302Spjd	error = 0;
164172302Spjd
165172302Spjd	fd = open(path, O_RDWR);
166172302Spjd	if (fd == -1)
167172302Spjd		return (errno);
168172302Spjd	mediasize = g_get_mediasize(name);
169172302Spjd	if (mediasize == 0) {
170172302Spjd		error = errno;
171172302Spjd		goto out;
172172302Spjd	}
173172302Spjd	sectorsize = g_get_sectorsize(name);
174172302Spjd	if (sectorsize == 0) {
175172302Spjd		error = errno;
176172302Spjd		goto out;
177172302Spjd	}
178172302Spjd	assert(sectorsize >= size);
179172302Spjd	sector = malloc(sectorsize);
180172302Spjd	if (sector == NULL) {
181172302Spjd		error = ENOMEM;
182172302Spjd		goto out;
183172302Spjd	}
184172302Spjd	bcopy(md, sector, size);
185181471Sivoras	if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
186172302Spjd	    (ssize_t)sectorsize) {
187172302Spjd		error = errno;
188172302Spjd		goto out;
189172302Spjd	}
190172302Spjdout:
191172302Spjd	if (sector != NULL)
192172302Spjd		free(sector);
193172302Spjd	close(fd);
194172302Spjd	return (error);
195172302Spjd}
196172302Spjd
197172302Spjd/*
198172302Spjd * Labels a new geom Meaning: parses and checks the parameters, calculates &
199172302Spjd * writes metadata to the relevant providers so when the next round of
200172302Spjd * "tasting" comes (which will be just after the provider(s) are closed) geom
201172302Spjd * can be instantiated with the tasted metadata.
202172302Spjd */
203172302Spjdstatic void
204172302Spjdvirstor_label(struct gctl_req *req)
205172302Spjd{
206172302Spjd	struct g_virstor_metadata md;
207172302Spjd	off_t msize;
208172302Spjd	unsigned char *sect;
209172302Spjd	unsigned int i;
210172302Spjd	size_t ssize, secsize;
211172302Spjd	const char *name;
212172302Spjd	char param[32];
213172302Spjd	int hardcode, nargs, error;
214172302Spjd	struct virstor_map_entry *map;
215172302Spjd	size_t total_chunks;	/* We'll run out of memory if
216172302Spjd				   this needs to be bigger. */
217172302Spjd	unsigned int map_chunks; /* Chunks needed by the map (map size). */
218172302Spjd	size_t map_size;	/* In bytes. */
219172302Spjd	ssize_t written;
220172302Spjd	int fd;
221172302Spjd
222172302Spjd	nargs = gctl_get_int(req, "nargs");
223172302Spjd	if (nargs < 2) {
224172302Spjd		gctl_error(req, "Too few arguments (%d): expecting: name "
225172302Spjd		    "provider0 [provider1 ...]", nargs);
226172302Spjd		return;
227172302Spjd	}
228172302Spjd
229172302Spjd	hardcode = gctl_get_int(req, "hardcode");
230172302Spjd
231172302Spjd	/*
232172302Spjd	 * Initialize constant parts of metadata: magic signature, version,
233172302Spjd	 * name.
234172302Spjd	 */
235172302Spjd	bzero(&md, sizeof(md));
236172302Spjd	strlcpy(md.md_magic, G_VIRSTOR_MAGIC, sizeof(md.md_magic));
237172302Spjd	md.md_version = G_VIRSTOR_VERSION;
238172302Spjd	name = gctl_get_ascii(req, "arg0");
239172302Spjd	if (name == NULL) {
240172302Spjd		gctl_error(req, "No 'arg%u' argument.", 0);
241172302Spjd		return;
242172302Spjd	}
243172302Spjd	strlcpy(md.md_name, name, sizeof(md.md_name));
244172302Spjd
245172302Spjd	md.md_virsize = (off_t)gctl_get_intmax(req, "vir_size");
246172302Spjd	md.md_chunk_size = gctl_get_intmax(req, "chunk_size");
247172302Spjd	md.md_count = nargs - 1;
248172302Spjd
249172302Spjd	if (md.md_virsize == 0 || md.md_chunk_size == 0) {
250172302Spjd		gctl_error(req, "Virtual size and chunk size must be non-zero");
251172302Spjd		return;
252172302Spjd	}
253172302Spjd
254172302Spjd	if (md.md_chunk_size % MAXPHYS != 0) {
255172302Spjd		/* XXX: This is not strictly needed, but it's convenient to
256172302Spjd		 * impose some limitations on it, so why not MAXPHYS. */
257172302Spjd		size_t new_size = (md.md_chunk_size / MAXPHYS) * MAXPHYS;
258172302Spjd		if (new_size < md.md_chunk_size)
259172302Spjd			new_size += MAXPHYS;
260172302Spjd		fprintf(stderr, "Resizing chunk size to be a multiple of "
261172302Spjd		    "MAXPHYS (%d kB).\n", MAXPHYS / 1024);
262172302Spjd		fprintf(stderr, "New chunk size: %zu kB\n", new_size / 1024);
263172302Spjd		md.md_chunk_size = new_size;
264172302Spjd	}
265172302Spjd
266172302Spjd	if (md.md_virsize % md.md_chunk_size != 0) {
267172302Spjd		off_t chunk_count = md.md_virsize / md.md_chunk_size;
268172302Spjd		md.md_virsize = chunk_count * md.md_chunk_size;
269172302Spjd		fprintf(stderr, "Resizing virtual size to be a multiple of "
270172302Spjd		    "chunk size.\n");
271172302Spjd		fprintf(stderr, "New virtual size: %zu MB\n",
272172302Spjd		    (size_t)(md.md_virsize/(1024 * 1024)));
273172302Spjd	}
274172302Spjd
275181471Sivoras	msize = secsize = 0;
276172302Spjd	for (i = 1; i < (unsigned)nargs; i++) {
277172302Spjd		snprintf(param, sizeof(param), "arg%u", i);
278172302Spjd		name = gctl_get_ascii(req, param);
279172302Spjd		ssize = g_get_sectorsize(name);
280172302Spjd		if (ssize == 0)
281172302Spjd			fprintf(stderr, "%s for %s\n", strerror(errno), name);
282172302Spjd		msize += g_get_mediasize(name);
283172302Spjd		if (secsize == 0)
284172302Spjd			secsize = ssize;
285172302Spjd		else if (secsize != ssize) {
286172302Spjd			gctl_error(req, "Devices need to have same sector size "
287172302Spjd			    "(%u on %s needs to be %u).",
288172302Spjd			    (u_int)ssize, name, (u_int)secsize);
289172302Spjd			return;
290172302Spjd		}
291172302Spjd	}
292172302Spjd
293181471Sivoras	if (secsize == 0) {
294181471Sivoras		gctl_error(req, "Device not specified");
295181471Sivoras		return;
296181471Sivoras	}
297181471Sivoras
298172302Spjd	if (md.md_chunk_size % secsize != 0) {
299172302Spjd		fprintf(stderr, "Error: chunk size is not a multiple of sector "
300172302Spjd		    "size.");
301172302Spjd		gctl_error(req, "Chunk size (in bytes) must be multiple of %u.",
302172302Spjd		    (unsigned int)secsize);
303172302Spjd		return;
304172302Spjd	}
305172302Spjd
306172302Spjd	total_chunks = md.md_virsize / md.md_chunk_size;
307172302Spjd	map_size = total_chunks * sizeof(*map);
308172302Spjd	assert(md.md_virsize % md.md_chunk_size == 0);
309172302Spjd
310172302Spjd	ssize = map_size % secsize;
311172302Spjd	if (ssize != 0) {
312172302Spjd		size_t add_chunks = (secsize - ssize) / sizeof(*map);
313172302Spjd		total_chunks += add_chunks;
314172302Spjd		md.md_virsize = (off_t)total_chunks * (off_t)md.md_chunk_size;
315172302Spjd		map_size = total_chunks * sizeof(*map);
316172302Spjd		fprintf(stderr, "Resizing virtual size to fit virstor "
317172302Spjd		    "structures.\n");
318172302Spjd		fprintf(stderr, "New virtual size: %ju MB (%zu new chunks)\n",
319172302Spjd		    (uintmax_t)(md.md_virsize / (1024 * 1024)), add_chunks);
320172302Spjd	}
321172302Spjd
322172302Spjd	if (verbose)
323172302Spjd		printf("Total virtual chunks: %zu (%zu MB each), %ju MB total "
324172302Spjd		    "virtual size.\n",
325172302Spjd		    total_chunks, (size_t)(md.md_chunk_size / (1024 * 1024)),
326172302Spjd		    md.md_virsize/(1024 * 1024));
327172302Spjd
328172302Spjd	if ((off_t)md.md_virsize < msize)
329172302Spjd		fprintf(stderr, "WARNING: Virtual storage size < Physical "
330172302Spjd		    "available storage (%ju < %ju)\n", md.md_virsize, msize);
331172302Spjd
332172302Spjd	/* Clear last sector first to spoil all components if device exists. */
333172302Spjd	if (verbose)
334172302Spjd		printf("Clearing metadata on");
335172302Spjd
336172302Spjd	for (i = 1; i < (unsigned)nargs; i++) {
337172302Spjd		snprintf(param, sizeof(param), "arg%u", i);
338172302Spjd		name = gctl_get_ascii(req, param);
339172302Spjd
340172302Spjd		if (verbose)
341172302Spjd			printf(" %s", name);
342172302Spjd
343172302Spjd		msize = g_get_mediasize(name);
344172302Spjd		ssize = g_get_sectorsize(name);
345172302Spjd		if (msize == 0 || ssize == 0) {
346172302Spjd			gctl_error(req, "Can't retrieve information about "
347172302Spjd			    "%s: %s.", name, strerror(errno));
348172302Spjd			return;
349172302Spjd		}
350173721Sjb		if (msize < (off_t) MAX(md.md_chunk_size*4, map_size))
351172302Spjd			gctl_error(req, "Device %s is too small", name);
352172302Spjd		error = g_metadata_clear(name, NULL);
353172302Spjd		if (error != 0) {
354172302Spjd			gctl_error(req, "Can't clear metadata on %s: %s.", name,
355172302Spjd			    strerror(error));
356172302Spjd			return;
357172302Spjd		}
358172302Spjd	}
359172302Spjd
360172302Spjd
361172302Spjd	/* Write allocation table to the first provider - this needs to be done
362172302Spjd	 * before metadata is written because when kernel tastes it it's too
363172302Spjd	 * late */
364172302Spjd	name = gctl_get_ascii(req, "arg1"); /* device with metadata */
365172302Spjd	if (verbose)
366172302Spjd		printf(".\nWriting allocation table to %s...", name);
367172302Spjd
368172302Spjd	/* How many chunks does the map occupy? */
369172302Spjd	map_chunks = map_size/md.md_chunk_size;
370172302Spjd	if (map_size % md.md_chunk_size != 0)
371172302Spjd		map_chunks++;
372172302Spjd	if (verbose) {
373172302Spjd		printf(" (%zu MB, %d chunks) ", map_size/(1024*1024), map_chunks);
374172302Spjd		fflush(stdout);
375172302Spjd	}
376172302Spjd
377172302Spjd	if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0)
378172302Spjd		fd = open(name, O_RDWR);
379172302Spjd	else {
380172302Spjd		sprintf(param, "%s%s", _PATH_DEV, name);
381172302Spjd		fd = open(param, O_RDWR);
382172302Spjd	}
383172302Spjd	if (fd < 0)
384172302Spjd		gctl_error(req, "Cannot open provider %s to write map", name);
385172302Spjd
386172302Spjd	/* Do it with calloc because there might be a need to set up chunk flags
387172302Spjd	 * in the future */
388172302Spjd	map = calloc(total_chunks, sizeof(*map));
389172302Spjd	if (map == NULL) {
390172302Spjd		gctl_error(req,
391172302Spjd		    "Out of memory (need %zu bytes for allocation map)",
392172302Spjd		    map_size);
393172302Spjd	}
394172302Spjd
395172302Spjd	written = pwrite(fd, map, map_size, 0);
396172302Spjd	free(map);
397172302Spjd	if ((size_t)written != map_size) {
398172302Spjd		if (verbose) {
399172302Spjd			fprintf(stderr, "\nTried to write %zu, written %zd (%s)\n",
400172302Spjd			    map_size, written, strerror(errno));
401172302Spjd		}
402172302Spjd		gctl_error(req, "Error writing out allocation map!");
403172302Spjd		return;
404172302Spjd	}
405172302Spjd	close (fd);
406172302Spjd
407172302Spjd	if (verbose)
408172302Spjd		printf("\nStoring metadata on ");
409172302Spjd
410172302Spjd	/*
411172302Spjd	 * ID is randomly generated, unique for a geom. This is used to
412172302Spjd	 * recognize all providers belonging to one geom.
413172302Spjd	 */
414172302Spjd	md.md_id = arc4random();
415172302Spjd
416172302Spjd	/* Ok, store metadata. */
417172302Spjd	for (i = 1; i < (unsigned)nargs; i++) {
418172302Spjd		snprintf(param, sizeof(param), "arg%u", i);
419172302Spjd		name = gctl_get_ascii(req, param);
420172302Spjd
421172302Spjd		msize = g_get_mediasize(name);
422172302Spjd		ssize = g_get_sectorsize(name);
423172302Spjd
424172302Spjd		if (verbose)
425172302Spjd			printf("%s ", name);
426172302Spjd
427172302Spjd		/* this provider's position/type in geom */
428172302Spjd		md.no = i - 1;
429172302Spjd		/* this provider's size */
430172302Spjd		md.provsize = msize;
431172302Spjd		/* chunk allocation info */
432172302Spjd		md.chunk_count = md.provsize / md.md_chunk_size;
433172302Spjd		if (verbose)
434172302Spjd			printf("(%u chunks) ", md.chunk_count);
435172302Spjd		/* Check to make sure last sector is unused */
436173721Sjb		if ((off_t)(md.chunk_count * md.md_chunk_size) > msize-ssize)
437172302Spjd		    md.chunk_count--;
438172302Spjd		md.chunk_next = 0;
439172302Spjd		if (i != 1) {
440172302Spjd			md.chunk_reserved = 0;
441172302Spjd			md.flags = 0;
442172302Spjd		} else {
443172302Spjd			md.chunk_reserved = map_chunks * 2;
444172302Spjd			md.flags = VIRSTOR_PROVIDER_ALLOCATED |
445172302Spjd			    VIRSTOR_PROVIDER_CURRENT;
446172302Spjd			md.chunk_next = md.chunk_reserved;
447172302Spjd			if (verbose)
448172302Spjd				printf("(%u reserved) ", md.chunk_reserved);
449172302Spjd		}
450172302Spjd
451172302Spjd		if (!hardcode)
452172302Spjd			bzero(md.provider, sizeof(md.provider));
453172302Spjd		else {
454172302Spjd			/* convert "/dev/something" to "something" */
455172302Spjd			if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0) {
456172302Spjd				strlcpy(md.provider, name + strlen(_PATH_DEV),
457172302Spjd				    sizeof(md.provider));
458172302Spjd			} else
459172302Spjd				strlcpy(md.provider, name, sizeof(md.provider));
460172302Spjd		}
461172302Spjd		sect = malloc(ssize);
462172302Spjd		bzero(sect, ssize);
463172302Spjd		if (sect == NULL)
464172302Spjd			err(1, "Cannot allocate sector of %zu bytes", ssize);
465172302Spjd		virstor_metadata_encode(&md, sect);
466172302Spjd		error = my_g_metadata_store(name, sect, ssize);
467172302Spjd		free(sect);
468172302Spjd		if (error != 0) {
469172302Spjd			if (verbose)
470172302Spjd				printf("\n");
471172302Spjd			fprintf(stderr, "Can't store metadata on %s: %s.\n",
472172302Spjd			    name, strerror(error));
473172302Spjd			gctl_error(req,
474172302Spjd			    "Not fully done (error storing metadata).");
475172302Spjd			return;
476172302Spjd		}
477172302Spjd	}
478172302Spjd#if 0
479172302Spjd	if (verbose)
480172302Spjd		printf("\n");
481172302Spjd#endif
482172302Spjd}
483172302Spjd
484172302Spjd/* Clears metadata on given provider(s) IF it's owned by us */
485172302Spjdstatic void
486172302Spjdvirstor_clear(struct gctl_req *req)
487172302Spjd{
488172302Spjd	const char *name;
489172302Spjd	char param[32];
490172302Spjd	unsigned i;
491172302Spjd	int nargs, error;
492172302Spjd	int fd;
493172302Spjd
494172302Spjd	nargs = gctl_get_int(req, "nargs");
495172302Spjd	if (nargs < 1) {
496172302Spjd		gctl_error(req, "Too few arguments.");
497172302Spjd		return;
498172302Spjd	}
499172302Spjd	for (i = 0; i < (unsigned)nargs; i++) {
500172302Spjd		snprintf(param, sizeof(param), "arg%u", i);
501172302Spjd		name = gctl_get_ascii(req, param);
502172302Spjd
503172302Spjd		error = g_metadata_clear(name, G_VIRSTOR_MAGIC);
504172302Spjd		if (error != 0) {
505172302Spjd			fprintf(stderr, "Can't clear metadata on %s: %s "
506172302Spjd			    "(do I own it?)\n", name, strerror(error));
507172302Spjd			gctl_error(req,
508172302Spjd			    "Not fully done (can't clear metadata).");
509172302Spjd			continue;
510172302Spjd		}
511172302Spjd		if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0)
512172302Spjd			fd = open(name, O_RDWR);
513172302Spjd		else {
514172302Spjd			sprintf(param, "%s%s", _PATH_DEV, name);
515172302Spjd			fd = open(param, O_RDWR);
516172302Spjd		}
517172302Spjd		if (fd < 0) {
518172302Spjd			gctl_error(req, "Cannot clear header sector for %s",
519172302Spjd			    name);
520172302Spjd			continue;
521172302Spjd		}
522172302Spjd		if (verbose)
523172302Spjd			printf("Metadata cleared on %s.\n", name);
524172302Spjd	}
525172302Spjd}
526172302Spjd
527172302Spjd/* Print some metadata information */
528172302Spjdstatic void
529172302Spjdvirstor_metadata_dump(const struct g_virstor_metadata *md)
530172302Spjd{
531172302Spjd	printf("          Magic string: %s\n", md->md_magic);
532172302Spjd	printf("      Metadata version: %u\n", (u_int) md->md_version);
533172302Spjd	printf("           Device name: %s\n", md->md_name);
534172302Spjd	printf("             Device ID: %u\n", (u_int) md->md_id);
535172302Spjd	printf("        Provider index: %u\n", (u_int) md->no);
536172302Spjd	printf("      Active providers: %u\n", (u_int) md->md_count);
537172302Spjd	printf("    Hardcoded provider: %s\n",
538172302Spjd	    md->provider[0] != '\0' ? md->provider : "(not hardcoded)");
539172302Spjd	printf("          Virtual size: %u MB\n",
540172302Spjd	    (unsigned int)(md->md_virsize/(1024 * 1024)));
541172302Spjd	printf("            Chunk size: %u kB\n", md->md_chunk_size / 1024);
542172302Spjd	printf("    Chunks on provider: %u\n", md->chunk_count);
543172302Spjd	printf("           Chunks free: %u\n", md->chunk_count - md->chunk_next);
544172302Spjd	printf("       Reserved chunks: %u\n", md->chunk_reserved);
545172302Spjd}
546172302Spjd
547172302Spjd/* Called by geom(8) via gvirstor_main() to dump metadata information */
548172302Spjdstatic void
549172302Spjdvirstor_dump(struct gctl_req *req)
550172302Spjd{
551172302Spjd	struct g_virstor_metadata md;
552172302Spjd	u_char tmpmd[512];	/* temporary buffer */
553172302Spjd	const char *name;
554172302Spjd	char param[16];
555172302Spjd	int nargs, error, i;
556172302Spjd
557172302Spjd	assert(sizeof(tmpmd) >= sizeof(md));
558172302Spjd
559172302Spjd	nargs = gctl_get_int(req, "nargs");
560172302Spjd	if (nargs < 1) {
561172302Spjd		gctl_error(req, "Too few arguments.");
562172302Spjd		return;
563172302Spjd	}
564172302Spjd	for (i = 0; i < nargs; i++) {
565172302Spjd		snprintf(param, sizeof(param), "arg%u", i);
566172302Spjd		name = gctl_get_ascii(req, param);
567172302Spjd
568172302Spjd		error = g_metadata_read(name, (u_char *) & tmpmd, sizeof(tmpmd),
569172302Spjd		    G_VIRSTOR_MAGIC);
570172302Spjd		if (error != 0) {
571172302Spjd			fprintf(stderr, "Can't read metadata from %s: %s.\n",
572172302Spjd			    name, strerror(error));
573172302Spjd			gctl_error(req,
574172302Spjd			    "Not fully done (error reading metadata).");
575172302Spjd			continue;
576172302Spjd		}
577172302Spjd		virstor_metadata_decode((u_char *) & tmpmd, &md);
578172302Spjd		printf("Metadata on %s:\n", name);
579172302Spjd		virstor_metadata_dump(&md);
580172302Spjd		printf("\n");
581172302Spjd	}
582172302Spjd}
583