1176417Sthompsa/*-
2176417Sthompsa * Copyright (c) 2008 Andrew Thompson <thompsa@FreeBSD.org>
3176417Sthompsa * All rights reserved.
4176417Sthompsa *
5176417Sthompsa * Redistribution and use in source and binary forms, with or without
6176417Sthompsa * modification, are permitted provided that the following conditions
7176417Sthompsa * are met:
8176417Sthompsa * 1. Redistributions of source code must retain the above copyright
9176417Sthompsa *    notice, this list of conditions and the following disclaimer.
10176417Sthompsa * 2. Redistributions in binary form must reproduce the above copyright
11176417Sthompsa *    notice, this list of conditions and the following disclaimer in the
12176417Sthompsa *    documentation and/or other materials provided with the distribution.
13176417Sthompsa *
14176417Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15176417Sthompsa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16176417Sthompsa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17176417Sthompsa * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18176417Sthompsa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19176417Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20176417Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21176417Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22176417Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23176417Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24176417Sthompsa * SUCH DAMAGE.
25176417Sthompsa */
26176417Sthompsa
27176417Sthompsa#include <sys/cdefs.h>
28176417Sthompsa__FBSDID("$FreeBSD: stable/11/sys/geom/linux_lvm/g_linux_lvm.c 332640 2018-04-17 02:18:04Z kevans $");
29176417Sthompsa
30176417Sthompsa#include <sys/ctype.h>
31176417Sthompsa#include <sys/param.h>
32176417Sthompsa#include <sys/bio.h>
33176417Sthompsa#include <sys/kernel.h>
34176417Sthompsa#include <sys/limits.h>
35176417Sthompsa#include <sys/malloc.h>
36176417Sthompsa#include <sys/queue.h>
37176417Sthompsa#include <sys/sysctl.h>
38176417Sthompsa#include <sys/systm.h>
39176417Sthompsa
40176417Sthompsa#include <geom/geom.h>
41176417Sthompsa#include <sys/endian.h>
42176417Sthompsa
43176417Sthompsa#include <geom/linux_lvm/g_linux_lvm.h>
44176417Sthompsa
45219029SnetchildFEATURE(geom_linux_lvm, "GEOM Linux LVM partitioning support");
46219029Snetchild
47176417Sthompsa/* Declare malloc(9) label */
48176417Sthompsastatic MALLOC_DEFINE(M_GLLVM, "gllvm", "GEOM_LINUX_LVM Data");
49176417Sthompsa
50176417Sthompsa/* GEOM class methods */
51176417Sthompsastatic g_access_t g_llvm_access;
52176417Sthompsastatic g_init_t g_llvm_init;
53176417Sthompsastatic g_orphan_t g_llvm_orphan;
54176417Sthompsastatic g_orphan_t g_llvm_taste_orphan;
55176417Sthompsastatic g_start_t g_llvm_start;
56176417Sthompsastatic g_taste_t g_llvm_taste;
57176417Sthompsastatic g_ctl_destroy_geom_t g_llvm_destroy_geom;
58176417Sthompsa
59176417Sthompsastatic void	g_llvm_done(struct bio *);
60176417Sthompsastatic void	g_llvm_remove_disk(struct g_llvm_vg *, struct g_consumer *);
61176417Sthompsastatic int	g_llvm_activate_lv(struct g_llvm_vg *, struct g_llvm_lv *);
62176417Sthompsastatic int	g_llvm_add_disk(struct g_llvm_vg *, struct g_provider *, char *);
63176417Sthompsastatic void	g_llvm_free_vg(struct g_llvm_vg *);
64176417Sthompsastatic int	g_llvm_destroy(struct g_llvm_vg *, int);
65176417Sthompsastatic int	g_llvm_read_label(struct g_consumer *, struct g_llvm_label *);
66176417Sthompsastatic int	g_llvm_read_md(struct g_consumer *, struct g_llvm_metadata *,
67176417Sthompsa		    struct g_llvm_label *);
68176417Sthompsa
69176417Sthompsastatic int	llvm_label_decode(const u_char *, struct g_llvm_label *, int);
70176417Sthompsastatic int	llvm_md_decode(const u_char *, struct g_llvm_metadata *,
71176417Sthompsa		    struct g_llvm_label *);
72176417Sthompsastatic int	llvm_textconf_decode(u_char *, int,
73176417Sthompsa		    struct g_llvm_metadata *);
74176417Sthompsastatic int	llvm_textconf_decode_pv(char **, char *, struct g_llvm_vg *);
75176417Sthompsastatic int	llvm_textconf_decode_lv(char **, char *, struct g_llvm_vg *);
76176417Sthompsastatic int	llvm_textconf_decode_sg(char **, char *, struct g_llvm_lv *);
77176417Sthompsa
78176417SthompsaSYSCTL_DECL(_kern_geom);
79176417SthompsaSYSCTL_NODE(_kern_geom, OID_AUTO, linux_lvm, CTLFLAG_RW, 0,
80176417Sthompsa    "GEOM_LINUX_LVM stuff");
81176417Sthompsastatic u_int g_llvm_debug = 0;
82267992ShselaskySYSCTL_UINT(_kern_geom_linux_lvm, OID_AUTO, debug, CTLFLAG_RWTUN, &g_llvm_debug, 0,
83176417Sthompsa    "Debug level");
84176417Sthompsa
85176417SthompsaLIST_HEAD(, g_llvm_vg) vg_list;
86176417Sthompsa
87176417Sthompsa/*
88176417Sthompsa * Called to notify geom when it's been opened, and for what intent
89176417Sthompsa */
90176417Sthompsastatic int
91176417Sthompsag_llvm_access(struct g_provider *pp, int dr, int dw, int de)
92176417Sthompsa{
93176417Sthompsa	struct g_consumer *c;
94176417Sthompsa	struct g_llvm_vg *vg;
95176417Sthompsa	struct g_geom *gp;
96176417Sthompsa	int error;
97176417Sthompsa
98176417Sthompsa	KASSERT(pp != NULL, ("%s: NULL provider", __func__));
99176417Sthompsa	gp = pp->geom;
100176417Sthompsa	KASSERT(gp != NULL, ("%s: NULL geom", __func__));
101176417Sthompsa	vg = gp->softc;
102176417Sthompsa
103176417Sthompsa	if (vg == NULL) {
104176417Sthompsa		/* It seems that .access can be called with negative dr,dw,dx
105176417Sthompsa		 * in this case but I want to check for myself */
106176417Sthompsa		G_LLVM_DEBUG(0, "access(%d, %d, %d) for %s",
107176417Sthompsa		    dr, dw, de, pp->name);
108176417Sthompsa
109176417Sthompsa		/* This should only happen when geom is withered so
110176417Sthompsa		 * allow only negative requests */
111176417Sthompsa		KASSERT(dr <= 0 && dw <= 0 && de <= 0,
112176417Sthompsa		    ("%s: Positive access for %s", __func__, pp->name));
113176417Sthompsa		if (pp->acr + dr == 0 && pp->acw + dw == 0 && pp->ace + de == 0)
114176417Sthompsa			G_LLVM_DEBUG(0,
115176417Sthompsa			    "Device %s definitely destroyed", pp->name);
116176417Sthompsa		return (0);
117176417Sthompsa	}
118176417Sthompsa
119176417Sthompsa	/* Grab an exclusive bit to propagate on our consumers on first open */
120176417Sthompsa	if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)
121176417Sthompsa		de++;
122176417Sthompsa	/* ... drop it on close */
123176417Sthompsa	if (pp->acr + dr == 0 && pp->acw + dw == 0 && pp->ace + de == 0)
124176417Sthompsa		de--;
125176417Sthompsa
126176417Sthompsa	error = ENXIO;
127176417Sthompsa	LIST_FOREACH(c, &gp->consumer, consumer) {
128176417Sthompsa		KASSERT(c != NULL, ("%s: consumer is NULL", __func__));
129176417Sthompsa		error = g_access(c, dr, dw, de);
130176417Sthompsa		if (error != 0) {
131176417Sthompsa			struct g_consumer *c2;
132176417Sthompsa
133176417Sthompsa			/* Backout earlier changes */
134176417Sthompsa			LIST_FOREACH(c2, &gp->consumer, consumer) {
135176417Sthompsa				if (c2 == c) /* all eariler components fixed */
136176417Sthompsa					return (error);
137176417Sthompsa				g_access(c2, -dr, -dw, -de);
138176417Sthompsa			}
139176417Sthompsa		}
140176417Sthompsa	}
141176417Sthompsa
142176417Sthompsa	return (error);
143176417Sthompsa}
144176417Sthompsa
145176417Sthompsa/*
146176417Sthompsa * Dismantle bio_queue and destroy its components
147176417Sthompsa */
148176417Sthompsastatic void
149176417Sthompsabioq_dismantle(struct bio_queue_head *bq)
150176417Sthompsa{
151176417Sthompsa	struct bio *b;
152176417Sthompsa
153176417Sthompsa	for (b = bioq_first(bq); b != NULL; b = bioq_first(bq)) {
154176417Sthompsa		bioq_remove(bq, b);
155176417Sthompsa		g_destroy_bio(b);
156176417Sthompsa	}
157176417Sthompsa}
158176417Sthompsa
159176417Sthompsa/*
160176417Sthompsa * GEOM .done handler
161176417Sthompsa * Can't use standard handler because one requested IO may
162176417Sthompsa * fork into additional data IOs
163176417Sthompsa */
164176417Sthompsastatic void
165176417Sthompsag_llvm_done(struct bio *b)
166176417Sthompsa{
167176417Sthompsa	struct bio *parent_b;
168176417Sthompsa
169176417Sthompsa	parent_b = b->bio_parent;
170176417Sthompsa
171176417Sthompsa	if (b->bio_error != 0) {
172176417Sthompsa		G_LLVM_DEBUG(0, "Error %d for offset=%ju, length=%ju on %s",
173176417Sthompsa		    b->bio_error, b->bio_offset, b->bio_length,
174176417Sthompsa		    b->bio_to->name);
175176417Sthompsa		if (parent_b->bio_error == 0)
176176417Sthompsa			parent_b->bio_error = b->bio_error;
177176417Sthompsa	}
178176417Sthompsa
179176417Sthompsa	parent_b->bio_inbed++;
180176417Sthompsa	parent_b->bio_completed += b->bio_completed;
181176417Sthompsa
182176417Sthompsa	if (parent_b->bio_children == parent_b->bio_inbed) {
183176417Sthompsa		parent_b->bio_completed = parent_b->bio_length;
184176417Sthompsa		g_io_deliver(parent_b, parent_b->bio_error);
185176417Sthompsa	}
186176417Sthompsa	g_destroy_bio(b);
187176417Sthompsa}
188176417Sthompsa
189176417Sthompsastatic void
190176417Sthompsag_llvm_start(struct bio *bp)
191176417Sthompsa{
192176417Sthompsa	struct g_provider *pp;
193176417Sthompsa	struct g_llvm_vg *vg;
194176417Sthompsa	struct g_llvm_pv *pv;
195176417Sthompsa	struct g_llvm_lv *lv;
196176417Sthompsa	struct g_llvm_segment *sg;
197176417Sthompsa	struct bio *cb;
198176417Sthompsa	struct bio_queue_head bq;
199176417Sthompsa	size_t chunk_size;
200176417Sthompsa	off_t offset, length;
201176417Sthompsa	char *addr;
202176417Sthompsa	u_int count;
203176417Sthompsa
204176417Sthompsa	pp = bp->bio_to;
205176417Sthompsa	lv = pp->private;
206176417Sthompsa	vg = pp->geom->softc;
207176417Sthompsa
208176417Sthompsa	switch (bp->bio_cmd) {
209176417Sthompsa	case BIO_READ:
210176417Sthompsa	case BIO_WRITE:
211176417Sthompsa	case BIO_DELETE:
212176417Sthompsa	/* XXX BIO_GETATTR allowed? */
213176417Sthompsa		break;
214176417Sthompsa	default:
215176417Sthompsa		g_io_deliver(bp, EOPNOTSUPP);
216176417Sthompsa		return;
217176417Sthompsa	}
218176417Sthompsa
219176417Sthompsa	bioq_init(&bq);
220176417Sthompsa
221176417Sthompsa	chunk_size = vg->vg_extentsize;
222176417Sthompsa	addr = bp->bio_data;
223176417Sthompsa	offset = bp->bio_offset;	/* virtual offset and length */
224176417Sthompsa	length = bp->bio_length;
225176417Sthompsa
226176417Sthompsa	while (length > 0) {
227176417Sthompsa		size_t chunk_index, in_chunk_offset, in_chunk_length;
228176417Sthompsa
229176417Sthompsa		pv = NULL;
230176417Sthompsa		cb = g_clone_bio(bp);
231176417Sthompsa		if (cb == NULL) {
232176417Sthompsa			bioq_dismantle(&bq);
233176417Sthompsa			if (bp->bio_error == 0)
234176417Sthompsa				bp->bio_error = ENOMEM;
235176417Sthompsa			g_io_deliver(bp, bp->bio_error);
236176417Sthompsa			return;
237176417Sthompsa		}
238176417Sthompsa
239176417Sthompsa		/* get the segment and the pv */
240176417Sthompsa		if (lv->lv_sgcount == 1) {
241176417Sthompsa			/* skip much of the calculations for a single sg */
242176417Sthompsa			chunk_index = 0;
243176417Sthompsa			in_chunk_offset = 0;
244176417Sthompsa			in_chunk_length = length;
245176417Sthompsa			sg = lv->lv_firstsg;
246176417Sthompsa			pv = sg->sg_pv;
247176417Sthompsa			cb->bio_offset = offset + sg->sg_pvoffset;
248176417Sthompsa		} else {
249176417Sthompsa			chunk_index = offset / chunk_size; /* round downwards */
250176417Sthompsa			in_chunk_offset = offset % chunk_size;
251176417Sthompsa			in_chunk_length =
252176417Sthompsa			    min(length, chunk_size - in_chunk_offset);
253176417Sthompsa
254176417Sthompsa			/* XXX could be faster */
255176417Sthompsa			LIST_FOREACH(sg, &lv->lv_segs, sg_next) {
256176417Sthompsa				if (chunk_index >= sg->sg_start &&
257176417Sthompsa				    chunk_index <= sg->sg_end) {
258176417Sthompsa					/* adjust chunk index for sg start */
259176417Sthompsa					chunk_index -= sg->sg_start;
260176417Sthompsa					pv = sg->sg_pv;
261176417Sthompsa					break;
262176417Sthompsa				}
263176417Sthompsa			}
264176417Sthompsa			cb->bio_offset =
265176417Sthompsa			    (off_t)chunk_index * (off_t)chunk_size
266176417Sthompsa			    + in_chunk_offset + sg->sg_pvoffset;
267176417Sthompsa		}
268176417Sthompsa
269176417Sthompsa		KASSERT(pv != NULL, ("Can't find PV for chunk %zu",
270176417Sthompsa		    chunk_index));
271176417Sthompsa
272176417Sthompsa		cb->bio_to = pv->pv_gprov;
273176417Sthompsa		cb->bio_done = g_llvm_done;
274176417Sthompsa		cb->bio_length = in_chunk_length;
275176417Sthompsa		cb->bio_data = addr;
276176417Sthompsa		cb->bio_caller1 = pv;
277176417Sthompsa		bioq_disksort(&bq, cb);
278176417Sthompsa
279176417Sthompsa		G_LLVM_DEBUG(5,
280176417Sthompsa		    "Mapped %s(%ju, %ju) on %s to %zu(%zu,%zu) @ %s:%ju",
281176417Sthompsa		    bp->bio_cmd == BIO_READ ? "R" : "W",
282176417Sthompsa		    offset, length, lv->lv_name,
283176417Sthompsa		    chunk_index, in_chunk_offset, in_chunk_length,
284176417Sthompsa		    pv->pv_name, cb->bio_offset);
285176417Sthompsa
286176417Sthompsa		addr += in_chunk_length;
287176417Sthompsa		length -= in_chunk_length;
288176417Sthompsa		offset += in_chunk_length;
289176417Sthompsa	}
290176417Sthompsa
291176417Sthompsa	/* Fire off bio's here */
292176417Sthompsa	count = 0;
293176417Sthompsa	for (cb = bioq_first(&bq); cb != NULL; cb = bioq_first(&bq)) {
294176417Sthompsa		bioq_remove(&bq, cb);
295176417Sthompsa		pv = cb->bio_caller1;
296176417Sthompsa		cb->bio_caller1 = NULL;
297176417Sthompsa		G_LLVM_DEBUG(6, "firing bio to %s, offset=%ju, length=%ju",
298176417Sthompsa		    cb->bio_to->name, cb->bio_offset, cb->bio_length);
299176417Sthompsa		g_io_request(cb, pv->pv_gcons);
300176417Sthompsa		count++;
301176417Sthompsa	}
302176417Sthompsa	if (count == 0) { /* We handled everything locally */
303176417Sthompsa		bp->bio_completed = bp->bio_length;
304176417Sthompsa		g_io_deliver(bp, 0);
305176417Sthompsa	}
306176417Sthompsa}
307176417Sthompsa
308176417Sthompsastatic void
309176417Sthompsag_llvm_remove_disk(struct g_llvm_vg *vg, struct g_consumer *cp)
310176417Sthompsa{
311176417Sthompsa	struct g_llvm_pv *pv;
312176417Sthompsa	struct g_llvm_lv *lv;
313176417Sthompsa	struct g_llvm_segment *sg;
314176417Sthompsa	int found;
315176417Sthompsa
316176417Sthompsa	KASSERT(cp != NULL, ("Non-valid disk in %s.", __func__));
317176417Sthompsa	pv = (struct g_llvm_pv *)cp->private;
318176417Sthompsa
319176417Sthompsa	G_LLVM_DEBUG(0, "Disk %s removed from %s.", cp->provider->name,
320176417Sthompsa	    pv->pv_name);
321176417Sthompsa
322176417Sthompsa	LIST_FOREACH(lv, &vg->vg_lvs, lv_next) {
323176417Sthompsa		/* Find segments that map to this disk */
324176417Sthompsa		found = 0;
325176417Sthompsa		LIST_FOREACH(sg, &lv->lv_segs, sg_next) {
326176417Sthompsa			if (sg->sg_pv == pv) {
327176417Sthompsa				sg->sg_pv = NULL;
328176417Sthompsa				lv->lv_sgactive--;
329176417Sthompsa				found = 1;
330176417Sthompsa				break;
331176417Sthompsa			}
332176417Sthompsa		}
333176417Sthompsa		if (found) {
334176417Sthompsa			G_LLVM_DEBUG(0, "Device %s removed.",
335176417Sthompsa			    lv->lv_gprov->name);
336306764Smav			g_wither_provider(lv->lv_gprov, ENXIO);
337176417Sthompsa			lv->lv_gprov = NULL;
338176417Sthompsa		}
339176417Sthompsa	}
340176417Sthompsa
341176417Sthompsa	if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0)
342176417Sthompsa		g_access(cp, -cp->acr, -cp->acw, -cp->ace);
343176417Sthompsa	g_detach(cp);
344176417Sthompsa	g_destroy_consumer(cp);
345176417Sthompsa}
346176417Sthompsa
347176417Sthompsastatic void
348176417Sthompsag_llvm_orphan(struct g_consumer *cp)
349176417Sthompsa{
350176417Sthompsa	struct g_llvm_vg *vg;
351176417Sthompsa	struct g_geom *gp;
352176417Sthompsa
353176417Sthompsa	g_topology_assert();
354176417Sthompsa	gp = cp->geom;
355176417Sthompsa	vg = gp->softc;
356176417Sthompsa	if (vg == NULL)
357176417Sthompsa		return;
358176417Sthompsa
359176417Sthompsa	g_llvm_remove_disk(vg, cp);
360176417Sthompsa	g_llvm_destroy(vg, 1);
361176417Sthompsa}
362176417Sthompsa
363176417Sthompsastatic int
364176417Sthompsag_llvm_activate_lv(struct g_llvm_vg *vg, struct g_llvm_lv *lv)
365176417Sthompsa{
366176417Sthompsa	struct g_geom *gp;
367176417Sthompsa	struct g_provider *pp;
368176417Sthompsa
369176417Sthompsa	g_topology_assert();
370176417Sthompsa
371176417Sthompsa	KASSERT(lv->lv_sgactive == lv->lv_sgcount, ("segment missing"));
372176417Sthompsa
373176417Sthompsa	gp = vg->vg_geom;
374176417Sthompsa	pp = g_new_providerf(gp, "linux_lvm/%s-%s", vg->vg_name, lv->lv_name);
375176417Sthompsa	pp->mediasize = vg->vg_extentsize * (off_t)lv->lv_extentcount;
376176417Sthompsa	pp->sectorsize = vg->vg_sectorsize;
377176417Sthompsa	g_error_provider(pp, 0);
378176417Sthompsa	lv->lv_gprov = pp;
379176417Sthompsa	pp->private = lv;
380176417Sthompsa
381176417Sthompsa	G_LLVM_DEBUG(1, "Created %s, %juM", pp->name,
382176417Sthompsa	    pp->mediasize / (1024*1024));
383176417Sthompsa
384176417Sthompsa	return (0);
385176417Sthompsa}
386176417Sthompsa
387176417Sthompsastatic int
388176417Sthompsag_llvm_add_disk(struct g_llvm_vg *vg, struct g_provider *pp, char *uuid)
389176417Sthompsa{
390176417Sthompsa	struct g_geom *gp;
391176417Sthompsa	struct g_consumer *cp, *fcp;
392176417Sthompsa	struct g_llvm_pv *pv;
393176417Sthompsa	struct g_llvm_lv *lv;
394176417Sthompsa	struct g_llvm_segment *sg;
395176417Sthompsa	int error;
396176417Sthompsa
397176417Sthompsa	g_topology_assert();
398176417Sthompsa
399176417Sthompsa	LIST_FOREACH(pv, &vg->vg_pvs, pv_next) {
400176417Sthompsa		if (strcmp(pv->pv_uuid, uuid) == 0)
401176417Sthompsa			break;	/* found it */
402176417Sthompsa	}
403176417Sthompsa	if (pv == NULL) {
404176417Sthompsa		G_LLVM_DEBUG(3, "uuid %s not found in pv list", uuid);
405176417Sthompsa		return (ENOENT);
406176417Sthompsa	}
407176417Sthompsa	if (pv->pv_gprov != NULL) {
408176417Sthompsa		G_LLVM_DEBUG(0, "disk %s already initialised in %s",
409176417Sthompsa		    pv->pv_name, vg->vg_name);
410176417Sthompsa		return (EEXIST);
411176417Sthompsa	}
412176417Sthompsa
413176417Sthompsa	pv->pv_start *= vg->vg_sectorsize;
414176417Sthompsa	gp = vg->vg_geom;
415176417Sthompsa	fcp = LIST_FIRST(&gp->consumer);
416176417Sthompsa
417176417Sthompsa	cp = g_new_consumer(gp);
418176417Sthompsa	error = g_attach(cp, pp);
419176417Sthompsa	G_LLVM_DEBUG(1, "Attached %s to %s at offset %ju",
420176417Sthompsa	    pp->name, pv->pv_name, pv->pv_start);
421176417Sthompsa
422176417Sthompsa	if (error != 0) {
423176417Sthompsa		G_LLVM_DEBUG(0, "cannot attach %s to %s",
424176417Sthompsa		    pp->name, vg->vg_name);
425176417Sthompsa		g_destroy_consumer(cp);
426176417Sthompsa		return (error);
427176417Sthompsa	}
428176417Sthompsa
429176417Sthompsa	if (fcp != NULL) {
430176417Sthompsa		if (fcp->provider->sectorsize != pp->sectorsize) {
431176417Sthompsa			G_LLVM_DEBUG(0, "Provider %s of %s has invalid "
432176417Sthompsa			    "sector size (%d)", pp->name, vg->vg_name,
433176417Sthompsa			    pp->sectorsize);
434176417Sthompsa			return (EINVAL);
435176417Sthompsa		}
436176417Sthompsa		if (fcp->acr > 0 || fcp->acw || fcp->ace > 0) {
437176417Sthompsa			/* Replicate access permissions from first "live"
438176417Sthompsa			 * consumer to the new one */
439176417Sthompsa			error = g_access(cp, fcp->acr, fcp->acw, fcp->ace);
440176417Sthompsa			if (error != 0) {
441176417Sthompsa				g_detach(cp);
442176417Sthompsa				g_destroy_consumer(cp);
443176417Sthompsa				return (error);
444176417Sthompsa			}
445176417Sthompsa		}
446176417Sthompsa	}
447176417Sthompsa
448176417Sthompsa	cp->private = pv;
449176417Sthompsa	pv->pv_gcons = cp;
450176417Sthompsa	pv->pv_gprov = pp;
451176417Sthompsa
452176417Sthompsa	LIST_FOREACH(lv, &vg->vg_lvs, lv_next) {
453176417Sthompsa		/* Find segments that map to this disk */
454176417Sthompsa		LIST_FOREACH(sg, &lv->lv_segs, sg_next) {
455176417Sthompsa			if (strcmp(sg->sg_pvname, pv->pv_name) == 0) {
456176417Sthompsa				/* avtivate the segment */
457176417Sthompsa				KASSERT(sg->sg_pv == NULL,
458176417Sthompsa				    ("segment already mapped"));
459176417Sthompsa				sg->sg_pvoffset =
460176417Sthompsa				    (off_t)sg->sg_pvstart * vg->vg_extentsize
461176417Sthompsa				    + pv->pv_start;
462176417Sthompsa				sg->sg_pv = pv;
463176417Sthompsa				lv->lv_sgactive++;
464176417Sthompsa
465176417Sthompsa				G_LLVM_DEBUG(2, "%s: %d to %d @ %s:%d"
466176417Sthompsa				    " offset %ju sector %ju",
467176417Sthompsa				    lv->lv_name, sg->sg_start, sg->sg_end,
468176417Sthompsa				    sg->sg_pvname, sg->sg_pvstart,
469176417Sthompsa				    sg->sg_pvoffset,
470176417Sthompsa				    sg->sg_pvoffset / vg->vg_sectorsize);
471176417Sthompsa			}
472176417Sthompsa		}
473176417Sthompsa		/* Activate any lvs waiting on this disk */
474176417Sthompsa		if (lv->lv_gprov == NULL && lv->lv_sgactive == lv->lv_sgcount) {
475176417Sthompsa			error = g_llvm_activate_lv(vg, lv);
476176417Sthompsa			if (error)
477176417Sthompsa				break;
478176417Sthompsa		}
479176417Sthompsa	}
480176417Sthompsa	return (error);
481176417Sthompsa}
482176417Sthompsa
483176417Sthompsastatic void
484176417Sthompsag_llvm_init(struct g_class *mp)
485176417Sthompsa{
486176417Sthompsa	LIST_INIT(&vg_list);
487176417Sthompsa}
488176417Sthompsa
489176417Sthompsastatic void
490176417Sthompsag_llvm_free_vg(struct g_llvm_vg *vg)
491176417Sthompsa{
492176417Sthompsa	struct g_llvm_pv *pv;
493176417Sthompsa	struct g_llvm_lv *lv;
494176417Sthompsa	struct g_llvm_segment *sg;
495176417Sthompsa
496176417Sthompsa	/* Free all the structures */
497176417Sthompsa	while ((pv = LIST_FIRST(&vg->vg_pvs)) != NULL) {
498176417Sthompsa		LIST_REMOVE(pv, pv_next);
499176417Sthompsa		free(pv, M_GLLVM);
500176417Sthompsa	}
501176417Sthompsa	while ((lv = LIST_FIRST(&vg->vg_lvs)) != NULL) {
502176417Sthompsa		while ((sg = LIST_FIRST(&lv->lv_segs)) != NULL) {
503176417Sthompsa			LIST_REMOVE(sg, sg_next);
504176417Sthompsa			free(sg, M_GLLVM);
505176417Sthompsa		}
506176417Sthompsa		LIST_REMOVE(lv, lv_next);
507176417Sthompsa		free(lv, M_GLLVM);
508176417Sthompsa	}
509176417Sthompsa	LIST_REMOVE(vg, vg_next);
510176417Sthompsa	free(vg, M_GLLVM);
511176417Sthompsa}
512176417Sthompsa
513176417Sthompsastatic void
514176417Sthompsag_llvm_taste_orphan(struct g_consumer *cp)
515176417Sthompsa{
516176417Sthompsa
517176417Sthompsa	KASSERT(1 == 0, ("%s called while tasting %s.", __func__,
518176417Sthompsa	    cp->provider->name));
519176417Sthompsa}
520176417Sthompsa
521176417Sthompsastatic struct g_geom *
522176417Sthompsag_llvm_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
523176417Sthompsa{
524176417Sthompsa	struct g_consumer *cp;
525176417Sthompsa	struct g_geom *gp;
526176417Sthompsa	struct g_llvm_label ll;
527176417Sthompsa	struct g_llvm_metadata md;
528176417Sthompsa	struct g_llvm_vg *vg;
529176417Sthompsa	int error;
530176417Sthompsa
531176417Sthompsa	bzero(&md, sizeof(md));
532176417Sthompsa
533176417Sthompsa	g_topology_assert();
534176417Sthompsa	g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name);
535176417Sthompsa	gp = g_new_geomf(mp, "linux_lvm:taste");
536176417Sthompsa	/* This orphan function should be never called. */
537176417Sthompsa	gp->orphan = g_llvm_taste_orphan;
538176417Sthompsa	cp = g_new_consumer(gp);
539176417Sthompsa	g_attach(cp, pp);
540176417Sthompsa	error = g_llvm_read_label(cp, &ll);
541176417Sthompsa	if (!error)
542176417Sthompsa		error = g_llvm_read_md(cp, &md, &ll);
543176417Sthompsa	g_detach(cp);
544176417Sthompsa	g_destroy_consumer(cp);
545176417Sthompsa	g_destroy_geom(gp);
546176417Sthompsa	if (error != 0)
547176417Sthompsa		return (NULL);
548176417Sthompsa
549176417Sthompsa	vg = md.md_vg;
550176417Sthompsa	if (vg->vg_geom == NULL) {
551176417Sthompsa		/* new volume group */
552176417Sthompsa		gp = g_new_geomf(mp, "%s", vg->vg_name);
553176417Sthompsa		gp->start = g_llvm_start;
554176417Sthompsa		gp->spoiled = g_llvm_orphan;
555176417Sthompsa		gp->orphan = g_llvm_orphan;
556176417Sthompsa		gp->access = g_llvm_access;
557176417Sthompsa		vg->vg_sectorsize = pp->sectorsize;
558176417Sthompsa		vg->vg_extentsize *= vg->vg_sectorsize;
559176417Sthompsa		vg->vg_geom = gp;
560176417Sthompsa		gp->softc = vg;
561176417Sthompsa		G_LLVM_DEBUG(1, "Created volume %s, extent size %zuK",
562176417Sthompsa		    vg->vg_name, vg->vg_extentsize / 1024);
563176417Sthompsa	}
564176417Sthompsa
565176417Sthompsa	/* initialise this disk in the volume group */
566176417Sthompsa	g_llvm_add_disk(vg, pp, ll.ll_uuid);
567176417Sthompsa	return (vg->vg_geom);
568176417Sthompsa}
569176417Sthompsa
570176417Sthompsastatic int
571176417Sthompsag_llvm_destroy(struct g_llvm_vg *vg, int force)
572176417Sthompsa{
573176417Sthompsa	struct g_provider *pp;
574176417Sthompsa	struct g_geom *gp;
575176417Sthompsa
576176417Sthompsa	g_topology_assert();
577176417Sthompsa	if (vg == NULL)
578176417Sthompsa		return (ENXIO);
579176417Sthompsa	gp = vg->vg_geom;
580176417Sthompsa
581176417Sthompsa	LIST_FOREACH(pp, &gp->provider, provider) {
582176417Sthompsa		if (pp->acr != 0 || pp->acw != 0 || pp->ace != 0) {
583176417Sthompsa			G_LLVM_DEBUG(1, "Device %s is still open (r%dw%de%d)",
584176417Sthompsa			    pp->name, pp->acr, pp->acw, pp->ace);
585176417Sthompsa			if (!force)
586176417Sthompsa				return (EBUSY);
587176417Sthompsa		}
588176417Sthompsa	}
589176417Sthompsa
590176417Sthompsa	g_llvm_free_vg(gp->softc);
591176417Sthompsa	gp->softc = NULL;
592176417Sthompsa	g_wither_geom(gp, ENXIO);
593176417Sthompsa	return (0);
594176417Sthompsa}
595176417Sthompsa
596176417Sthompsastatic int
597176417Sthompsag_llvm_destroy_geom(struct gctl_req *req __unused, struct g_class *mp __unused,
598176417Sthompsa    struct g_geom *gp)
599176417Sthompsa{
600176417Sthompsa	struct g_llvm_vg *vg;
601176417Sthompsa
602176417Sthompsa	vg = gp->softc;
603176417Sthompsa	return (g_llvm_destroy(vg, 0));
604176417Sthompsa}
605176417Sthompsa
606176417Sthompsaint
607176417Sthompsag_llvm_read_label(struct g_consumer *cp, struct g_llvm_label *ll)
608176417Sthompsa{
609176417Sthompsa	struct g_provider *pp;
610176417Sthompsa	u_char *buf;
611176417Sthompsa	int i, error = 0;
612176417Sthompsa
613176417Sthompsa	g_topology_assert();
614176417Sthompsa
615176417Sthompsa	/* The LVM label is stored on the first four sectors */
616176417Sthompsa	error = g_access(cp, 1, 0, 0);
617176417Sthompsa	if (error != 0)
618176417Sthompsa		return (error);
619176417Sthompsa	pp = cp->provider;
620176417Sthompsa	g_topology_unlock();
621176417Sthompsa	buf = g_read_data(cp, 0, pp->sectorsize * 4, &error);
622176417Sthompsa	g_topology_lock();
623176417Sthompsa	g_access(cp, -1, 0, 0);
624176417Sthompsa	if (buf == NULL) {
625176417Sthompsa		G_LLVM_DEBUG(1, "Cannot read metadata from %s (error=%d)",
626176417Sthompsa		    pp->name, error);
627176417Sthompsa		return (error);
628176417Sthompsa	}
629176417Sthompsa
630176417Sthompsa	/* Search the four sectors for the LVM label. */
631176417Sthompsa	for (i = 0; i < 4; i++) {
632176417Sthompsa		error = llvm_label_decode(&buf[i * pp->sectorsize], ll, i);
633176417Sthompsa		if (error == 0)
634176417Sthompsa			break;	/* found it */
635176417Sthompsa	}
636176417Sthompsa	g_free(buf);
637176417Sthompsa	return (error);
638176417Sthompsa}
639176417Sthompsa
640176417Sthompsaint
641176417Sthompsag_llvm_read_md(struct g_consumer *cp, struct g_llvm_metadata *md,
642176417Sthompsa    struct g_llvm_label *ll)
643176417Sthompsa{
644176417Sthompsa	struct g_provider *pp;
645176417Sthompsa	u_char *buf;
646176417Sthompsa	int error;
647176417Sthompsa	int size;
648176417Sthompsa
649176417Sthompsa	g_topology_assert();
650176417Sthompsa
651176417Sthompsa	error = g_access(cp, 1, 0, 0);
652176417Sthompsa	if (error != 0)
653176417Sthompsa		return (error);
654176417Sthompsa	pp = cp->provider;
655176417Sthompsa	g_topology_unlock();
656176417Sthompsa	buf = g_read_data(cp, ll->ll_md_offset, pp->sectorsize, &error);
657176417Sthompsa	g_topology_lock();
658176417Sthompsa	g_access(cp, -1, 0, 0);
659176417Sthompsa	if (buf == NULL) {
660176417Sthompsa		G_LLVM_DEBUG(0, "Cannot read metadata from %s (error=%d)",
661176417Sthompsa		    cp->provider->name, error);
662176417Sthompsa		return (error);
663176417Sthompsa	}
664176417Sthompsa
665176417Sthompsa	error = llvm_md_decode(buf, md, ll);
666176417Sthompsa	g_free(buf);
667176417Sthompsa	if (error != 0) {
668176417Sthompsa		return (error);
669176417Sthompsa	}
670176417Sthompsa
671176417Sthompsa	G_LLVM_DEBUG(1, "reading LVM2 config @ %s:%ju", pp->name,
672176417Sthompsa		    ll->ll_md_offset + md->md_reloffset);
673176417Sthompsa	error = g_access(cp, 1, 0, 0);
674176417Sthompsa	if (error != 0)
675176417Sthompsa		return (error);
676176417Sthompsa	pp = cp->provider;
677176417Sthompsa	g_topology_unlock();
678176417Sthompsa	/* round up to the nearest sector */
679176417Sthompsa	size = md->md_relsize +
680176417Sthompsa	    (pp->sectorsize - md->md_relsize % pp->sectorsize);
681176417Sthompsa	buf = g_read_data(cp, ll->ll_md_offset + md->md_reloffset, size, &error);
682176417Sthompsa	g_topology_lock();
683176417Sthompsa	g_access(cp, -1, 0, 0);
684176417Sthompsa	if (buf == NULL) {
685176417Sthompsa		G_LLVM_DEBUG(0, "Cannot read LVM2 config from %s (error=%d)",
686176417Sthompsa		    pp->name, error);
687176417Sthompsa		return (error);
688176417Sthompsa	}
689176417Sthompsa	buf[md->md_relsize] = '\0';
690176417Sthompsa	G_LLVM_DEBUG(10, "LVM config:\n%s\n", buf);
691176417Sthompsa	error = llvm_textconf_decode(buf, md->md_relsize, md);
692176417Sthompsa	g_free(buf);
693176417Sthompsa
694176417Sthompsa	return (error);
695176417Sthompsa}
696176417Sthompsa
697176417Sthompsastatic int
698176417Sthompsallvm_label_decode(const u_char *data, struct g_llvm_label *ll, int sector)
699176417Sthompsa{
700176417Sthompsa	uint64_t off;
701176417Sthompsa	char *uuid;
702176417Sthompsa
703176417Sthompsa	/* Magic string */
704176417Sthompsa	if (bcmp("LABELONE", data , 8) != 0)
705176417Sthompsa		return (EINVAL);
706176417Sthompsa
707176417Sthompsa	/* We only support LVM2 text format */
708176417Sthompsa	if (bcmp("LVM2 001", data + 24, 8) != 0) {
709176417Sthompsa		G_LLVM_DEBUG(0, "Unsupported LVM format");
710176417Sthompsa		return (EINVAL);
711176417Sthompsa	}
712176417Sthompsa
713176417Sthompsa	ll->ll_sector = le64dec(data + 8);
714176417Sthompsa	ll->ll_crc = le32dec(data + 16);
715176417Sthompsa	ll->ll_offset = le32dec(data + 20);
716176417Sthompsa
717176417Sthompsa	if (ll->ll_sector != sector) {
718176417Sthompsa		G_LLVM_DEBUG(0, "Expected sector %ju, found at %d",
719176417Sthompsa		    ll->ll_sector, sector);
720176417Sthompsa		return (EINVAL);
721176417Sthompsa	}
722176417Sthompsa
723176417Sthompsa	off = ll->ll_offset;
724176417Sthompsa	/*
725176417Sthompsa	 * convert the binary uuid to string format, the format is
726176417Sthompsa	 * xxxxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxxxx (6-4-4-4-4-4-6)
727176417Sthompsa	 */
728176417Sthompsa	uuid = ll->ll_uuid;
729176417Sthompsa	bcopy(data + off, uuid, 6);
730176417Sthompsa	off += 6;
731176417Sthompsa	uuid += 6;
732176417Sthompsa	*uuid++ = '-';
733176417Sthompsa	for (int i = 0; i < 5; i++) {
734176417Sthompsa		bcopy(data + off, uuid, 4);
735176417Sthompsa		off += 4;
736176417Sthompsa		uuid += 4;
737176417Sthompsa		*uuid++ = '-';
738176417Sthompsa	}
739176417Sthompsa	bcopy(data + off, uuid, 6);
740176417Sthompsa	off += 6;
741176417Sthompsa	uuid += 6;
742176417Sthompsa	*uuid++ = '\0';
743176417Sthompsa
744176417Sthompsa	ll->ll_size = le64dec(data + off);
745176417Sthompsa	off += 8;
746176417Sthompsa	ll->ll_pestart = le64dec(data + off);
747176417Sthompsa	off += 16;
748176417Sthompsa
749176417Sthompsa	/* Only one data section is supported */
750176417Sthompsa	if (le64dec(data + off) != 0) {
751176417Sthompsa		G_LLVM_DEBUG(0, "Only one data section supported");
752176417Sthompsa		return (EINVAL);
753176417Sthompsa	}
754176417Sthompsa
755176417Sthompsa	off += 16;
756176417Sthompsa	ll->ll_md_offset = le64dec(data + off);
757176417Sthompsa	off += 8;
758176417Sthompsa	ll->ll_md_size = le64dec(data + off);
759176417Sthompsa	off += 8;
760176417Sthompsa
761176417Sthompsa	G_LLVM_DEBUG(1, "LVM metadata: offset=%ju, size=%ju", ll->ll_md_offset,
762176417Sthompsa	    ll->ll_md_size);
763176417Sthompsa
764176417Sthompsa	/* Only one data section is supported */
765176417Sthompsa	if (le64dec(data + off) != 0) {
766176417Sthompsa		G_LLVM_DEBUG(0, "Only one metadata section supported");
767176417Sthompsa		return (EINVAL);
768176417Sthompsa	}
769176417Sthompsa
770176417Sthompsa	G_LLVM_DEBUG(2, "label uuid=%s", ll->ll_uuid);
771176417Sthompsa	G_LLVM_DEBUG(2, "sector=%ju, crc=%u, offset=%u, size=%ju, pestart=%ju",
772176417Sthompsa	    ll->ll_sector, ll->ll_crc, ll->ll_offset, ll->ll_size,
773176417Sthompsa	    ll->ll_pestart);
774176417Sthompsa
775176417Sthompsa	return (0);
776176417Sthompsa}
777176417Sthompsa
778176417Sthompsastatic int
779176417Sthompsallvm_md_decode(const u_char *data, struct g_llvm_metadata *md,
780176417Sthompsa    struct g_llvm_label *ll)
781176417Sthompsa{
782176417Sthompsa	uint64_t off;
783176417Sthompsa	char magic[16];
784176417Sthompsa
785176417Sthompsa	off = 0;
786176417Sthompsa	md->md_csum = le32dec(data + off);
787176417Sthompsa	off += 4;
788176417Sthompsa	bcopy(data + off, magic, 16);
789176417Sthompsa	off += 16;
790176417Sthompsa	md->md_version = le32dec(data + off);
791176417Sthompsa	off += 4;
792176417Sthompsa	md->md_start = le64dec(data + off);
793176417Sthompsa	off += 8;
794176417Sthompsa	md->md_size = le64dec(data + off);
795176417Sthompsa	off += 8;
796176417Sthompsa
797176417Sthompsa	if (bcmp(G_LLVM_MAGIC, magic, 16) != 0) {
798176417Sthompsa		G_LLVM_DEBUG(0, "Incorrect md magic number");
799176417Sthompsa		return (EINVAL);
800176417Sthompsa	}
801176417Sthompsa	if (md->md_version != 1) {
802176417Sthompsa		G_LLVM_DEBUG(0, "Incorrect md version number (%u)",
803176417Sthompsa		    md->md_version);
804176417Sthompsa		return (EINVAL);
805176417Sthompsa	}
806176417Sthompsa	if (md->md_start != ll->ll_md_offset) {
807176417Sthompsa		G_LLVM_DEBUG(0, "Incorrect md offset (%ju)", md->md_start);
808176417Sthompsa		return (EINVAL);
809176417Sthompsa	}
810176417Sthompsa
811176417Sthompsa	/* Aparently only one is ever returned */
812176417Sthompsa	md->md_reloffset = le64dec(data + off);
813176417Sthompsa	off += 8;
814176417Sthompsa	md->md_relsize = le64dec(data + off);
815176417Sthompsa	off += 16;	/* XXX skipped checksum */
816176417Sthompsa
817176417Sthompsa	if (le64dec(data + off) != 0) {
818176417Sthompsa		G_LLVM_DEBUG(0, "Only one reloc supported");
819176417Sthompsa		return (EINVAL);
820176417Sthompsa	}
821176417Sthompsa
822176417Sthompsa	G_LLVM_DEBUG(3, "reloc: offset=%ju, size=%ju",
823176417Sthompsa	    md->md_reloffset, md->md_relsize);
824176417Sthompsa	G_LLVM_DEBUG(3, "md: version=%u, start=%ju, size=%ju",
825176417Sthompsa	    md->md_version, md->md_start, md->md_size);
826176417Sthompsa
827176417Sthompsa	return (0);
828176417Sthompsa}
829176417Sthompsa
830176417Sthompsa#define	GRAB_INT(key, tok1, tok2, v)					\
831176417Sthompsa	if (tok1 && tok2 && strncmp(tok1, key, sizeof(key)) == 0) {	\
832176417Sthompsa		v = strtol(tok2, &tok1, 10);				\
833176417Sthompsa		if (tok1 == tok2)					\
834176417Sthompsa			/* strtol did not eat any of the buffer */	\
835176417Sthompsa			goto bad;					\
836176417Sthompsa		continue;						\
837176417Sthompsa	}
838176417Sthompsa
839176417Sthompsa#define	GRAB_STR(key, tok1, tok2, v, len)				\
840176417Sthompsa	if (tok1 && tok2 && strncmp(tok1, key, sizeof(key)) == 0) {	\
841176417Sthompsa		strsep(&tok2, "\"");					\
842176417Sthompsa		if (tok2 == NULL)					\
843176417Sthompsa			continue;					\
844176417Sthompsa		tok1 = strsep(&tok2, "\"");				\
845176417Sthompsa		if (tok2 == NULL)					\
846176417Sthompsa			continue;					\
847176417Sthompsa		strncpy(v, tok1, len);					\
848176417Sthompsa		continue;						\
849176417Sthompsa	}
850176417Sthompsa
851176417Sthompsa#define	SPLIT(key, value, str)						\
852176417Sthompsa	key = strsep(&value, str);					\
853176417Sthompsa	/* strip trailing whitespace on the key */			\
854176417Sthompsa	for (char *t = key; *t != '\0'; t++)				\
855176417Sthompsa		if (isspace(*t)) {					\
856176417Sthompsa			*t = '\0';					\
857176417Sthompsa			break;						\
858176417Sthompsa		}
859176417Sthompsa
860194924Slulfstatic size_t
861194924Slulfllvm_grab_name(char *name, const char *tok)
862194924Slulf{
863194924Slulf	size_t len;
864194924Slulf
865194924Slulf	len = 0;
866194924Slulf	if (tok == NULL)
867194924Slulf		return (0);
868194924Slulf	if (tok[0] == '-')
869194924Slulf		return (0);
870194924Slulf	if (strcmp(tok, ".") == 0 || strcmp(tok, "..") == 0)
871194924Slulf		return (0);
872194924Slulf	while (tok[len] && (isalpha(tok[len]) || isdigit(tok[len]) ||
873194924Slulf	    tok[len] == '.' || tok[len] == '_' || tok[len] == '-' ||
874194924Slulf	    tok[len] == '+') && len < G_LLVM_NAMELEN - 1)
875194924Slulf		len++;
876194924Slulf	bcopy(tok, name, len);
877194924Slulf	name[len] = '\0';
878194924Slulf	return (len);
879194924Slulf}
880194924Slulf
881176417Sthompsastatic int
882176417Sthompsallvm_textconf_decode(u_char *data, int buflen, struct g_llvm_metadata *md)
883176417Sthompsa{
884176417Sthompsa	struct g_llvm_vg	*vg;
885176417Sthompsa	char *buf = data;
886176417Sthompsa	char *tok, *v;
887176417Sthompsa	char name[G_LLVM_NAMELEN];
888176417Sthompsa	char uuid[G_LLVM_UUIDLEN];
889194924Slulf	size_t len;
890176417Sthompsa
891176417Sthompsa	if (buf == NULL || *buf == '\0')
892176417Sthompsa		return (EINVAL);
893176417Sthompsa
894176417Sthompsa	tok = strsep(&buf, "\n");
895176417Sthompsa	if (tok == NULL)
896176417Sthompsa		return (EINVAL);
897194924Slulf	len = llvm_grab_name(name, tok);
898176417Sthompsa	if (len == 0)
899176417Sthompsa		return (EINVAL);
900176417Sthompsa
901176417Sthompsa	/* check too see if the vg has already been loaded off another disk */
902176417Sthompsa	LIST_FOREACH(vg, &vg_list, vg_next) {
903176417Sthompsa		if (strcmp(vg->vg_name, name) == 0) {
904176417Sthompsa			uuid[0] = '\0';
905176417Sthompsa			/* grab the volume group uuid */
906176417Sthompsa			while ((tok = strsep(&buf, "\n")) != NULL) {
907176417Sthompsa				if (strstr(tok, "{"))
908176417Sthompsa					break;
909176417Sthompsa				if (strstr(tok, "=")) {
910176417Sthompsa					SPLIT(v, tok, "=");
911176417Sthompsa					GRAB_STR("id", v, tok, uuid,
912176417Sthompsa					    sizeof(uuid));
913176417Sthompsa				}
914176417Sthompsa			}
915176417Sthompsa			if (strcmp(vg->vg_uuid, uuid) == 0) {
916176417Sthompsa				/* existing vg */
917176417Sthompsa				md->md_vg = vg;
918176417Sthompsa				return (0);
919176417Sthompsa			}
920176417Sthompsa			/* XXX different volume group with name clash! */
921176417Sthompsa			G_LLVM_DEBUG(0,
922176417Sthompsa			    "%s already exists, volume group not loaded", name);
923176417Sthompsa			return (EINVAL);
924176417Sthompsa		}
925176417Sthompsa	}
926176417Sthompsa
927176417Sthompsa	vg = malloc(sizeof(*vg), M_GLLVM, M_NOWAIT|M_ZERO);
928176417Sthompsa	if (vg == NULL)
929176417Sthompsa		return (ENOMEM);
930176417Sthompsa
931176417Sthompsa	strncpy(vg->vg_name, name, sizeof(vg->vg_name));
932176417Sthompsa	LIST_INIT(&vg->vg_pvs);
933176417Sthompsa	LIST_INIT(&vg->vg_lvs);
934176417Sthompsa
935176417Sthompsa#define	VOL_FOREACH(func, tok, buf, p)					\
936176417Sthompsa	while ((tok = strsep(buf, "\n")) != NULL) {			\
937176417Sthompsa		if (strstr(tok, "{")) {					\
938176417Sthompsa			func(buf, tok, p);				\
939176417Sthompsa			continue;					\
940176417Sthompsa		}							\
941176417Sthompsa		if (strstr(tok, "}"))					\
942176417Sthompsa			break;						\
943176417Sthompsa	}
944176417Sthompsa
945176417Sthompsa	while ((tok = strsep(&buf, "\n")) != NULL) {
946176417Sthompsa		if (strcmp(tok, "physical_volumes {") == 0) {
947176417Sthompsa			VOL_FOREACH(llvm_textconf_decode_pv, tok, &buf, vg);
948176417Sthompsa			continue;
949176417Sthompsa		}
950176417Sthompsa		if (strcmp(tok, "logical_volumes {") == 0) {
951176417Sthompsa			VOL_FOREACH(llvm_textconf_decode_lv, tok, &buf, vg);
952176417Sthompsa			continue;
953176417Sthompsa		}
954176417Sthompsa		if (strstr(tok, "{")) {
955176417Sthompsa			G_LLVM_DEBUG(2, "unknown section %s", tok);
956176417Sthompsa			continue;
957176417Sthompsa		}
958176417Sthompsa
959176417Sthompsa		/* parse 'key = value' lines */
960176417Sthompsa		if (strstr(tok, "=")) {
961176417Sthompsa			SPLIT(v, tok, "=");
962176417Sthompsa			GRAB_STR("id", v, tok, vg->vg_uuid, sizeof(vg->vg_uuid));
963176417Sthompsa			GRAB_INT("extent_size", v, tok, vg->vg_extentsize);
964176417Sthompsa			continue;
965176417Sthompsa		}
966176417Sthompsa	}
967176417Sthompsa	/* basic checking */
968176417Sthompsa	if (vg->vg_extentsize == 0)
969176417Sthompsa		goto bad;
970176417Sthompsa
971176417Sthompsa	md->md_vg = vg;
972176417Sthompsa	LIST_INSERT_HEAD(&vg_list, vg, vg_next);
973176417Sthompsa	G_LLVM_DEBUG(3, "vg: name=%s uuid=%s", vg->vg_name, vg->vg_uuid);
974176417Sthompsa	return(0);
975176417Sthompsa
976176417Sthompsabad:
977176417Sthompsa	g_llvm_free_vg(vg);
978176417Sthompsa	return (-1);
979176417Sthompsa}
980176417Sthompsa#undef	VOL_FOREACH
981176417Sthompsa
982176417Sthompsastatic int
983176417Sthompsallvm_textconf_decode_pv(char **buf, char *tok, struct g_llvm_vg *vg)
984176417Sthompsa{
985176417Sthompsa	struct g_llvm_pv	*pv;
986176417Sthompsa	char *v;
987194924Slulf	size_t len;
988176417Sthompsa
989176417Sthompsa	if (*buf == NULL || **buf == '\0')
990176417Sthompsa		return (EINVAL);
991176417Sthompsa
992176417Sthompsa	pv = malloc(sizeof(*pv), M_GLLVM, M_NOWAIT|M_ZERO);
993176417Sthompsa	if (pv == NULL)
994176417Sthompsa		return (ENOMEM);
995176417Sthompsa
996176417Sthompsa	pv->pv_vg = vg;
997176417Sthompsa	len = 0;
998176417Sthompsa	if (tok == NULL)
999176417Sthompsa		goto bad;
1000194924Slulf	len = llvm_grab_name(pv->pv_name, tok);
1001176417Sthompsa	if (len == 0)
1002176417Sthompsa		goto bad;
1003176417Sthompsa
1004176417Sthompsa	while ((tok = strsep(buf, "\n")) != NULL) {
1005176417Sthompsa		if (strstr(tok, "{"))
1006176417Sthompsa			goto bad;
1007176417Sthompsa
1008176417Sthompsa		if (strstr(tok, "}"))
1009176417Sthompsa			break;
1010176417Sthompsa
1011176417Sthompsa		/* parse 'key = value' lines */
1012176417Sthompsa		if (strstr(tok, "=")) {
1013176417Sthompsa			SPLIT(v, tok, "=");
1014176417Sthompsa			GRAB_STR("id", v, tok, pv->pv_uuid, sizeof(pv->pv_uuid));
1015176417Sthompsa			GRAB_INT("pe_start", v, tok, pv->pv_start);
1016176417Sthompsa			GRAB_INT("pe_count", v, tok, pv->pv_count);
1017176417Sthompsa			continue;
1018176417Sthompsa		}
1019176417Sthompsa	}
1020176417Sthompsa	if (tok == NULL)
1021176417Sthompsa		goto bad;
1022176417Sthompsa	/* basic checking */
1023176417Sthompsa	if (pv->pv_count == 0)
1024176417Sthompsa		goto bad;
1025176417Sthompsa
1026176417Sthompsa	LIST_INSERT_HEAD(&vg->vg_pvs, pv, pv_next);
1027176417Sthompsa	G_LLVM_DEBUG(3, "pv: name=%s uuid=%s", pv->pv_name, pv->pv_uuid);
1028176417Sthompsa
1029176417Sthompsa	return (0);
1030176417Sthompsabad:
1031176417Sthompsa	free(pv, M_GLLVM);
1032176417Sthompsa	return (-1);
1033176417Sthompsa}
1034176417Sthompsa
1035176417Sthompsastatic int
1036176417Sthompsallvm_textconf_decode_lv(char **buf, char *tok, struct g_llvm_vg *vg)
1037176417Sthompsa{
1038176417Sthompsa	struct g_llvm_lv	*lv;
1039176417Sthompsa	struct g_llvm_segment *sg;
1040176417Sthompsa	char *v;
1041194924Slulf	size_t len;
1042176417Sthompsa
1043176417Sthompsa	if (*buf == NULL || **buf == '\0')
1044176417Sthompsa		return (EINVAL);
1045176417Sthompsa
1046176417Sthompsa	lv = malloc(sizeof(*lv), M_GLLVM, M_NOWAIT|M_ZERO);
1047176417Sthompsa	if (lv == NULL)
1048176417Sthompsa		return (ENOMEM);
1049176417Sthompsa
1050176417Sthompsa	lv->lv_vg = vg;
1051176417Sthompsa	LIST_INIT(&lv->lv_segs);
1052176417Sthompsa
1053176417Sthompsa	if (tok == NULL)
1054176417Sthompsa		goto bad;
1055194924Slulf	len = llvm_grab_name(lv->lv_name, tok);
1056176417Sthompsa	if (len == 0)
1057176417Sthompsa		goto bad;
1058176417Sthompsa
1059176417Sthompsa	while ((tok = strsep(buf, "\n")) != NULL) {
1060176417Sthompsa		if (strstr(tok, "{")) {
1061176417Sthompsa			if (strstr(tok, "segment")) {
1062176417Sthompsa				llvm_textconf_decode_sg(buf, tok, lv);
1063176417Sthompsa				continue;
1064176417Sthompsa			} else
1065176417Sthompsa				/* unexpected section */
1066176417Sthompsa				goto bad;
1067176417Sthompsa		}
1068176417Sthompsa
1069176417Sthompsa		if (strstr(tok, "}"))
1070176417Sthompsa			break;
1071176417Sthompsa
1072176417Sthompsa		/* parse 'key = value' lines */
1073176417Sthompsa		if (strstr(tok, "=")) {
1074176417Sthompsa			SPLIT(v, tok, "=");
1075176417Sthompsa			GRAB_STR("id", v, tok, lv->lv_uuid, sizeof(lv->lv_uuid));
1076176417Sthompsa			GRAB_INT("segment_count", v, tok, lv->lv_sgcount);
1077176417Sthompsa			continue;
1078176417Sthompsa		}
1079176417Sthompsa	}
1080176417Sthompsa	if (tok == NULL)
1081176417Sthompsa		goto bad;
1082176417Sthompsa	if (lv->lv_sgcount == 0 || lv->lv_sgcount != lv->lv_numsegs)
1083176417Sthompsa		/* zero or incomplete segment list */
1084176417Sthompsa		goto bad;
1085176417Sthompsa
1086176417Sthompsa	/* Optimize for only one segment on the pv */
1087176417Sthompsa	lv->lv_firstsg = LIST_FIRST(&lv->lv_segs);
1088176417Sthompsa	LIST_INSERT_HEAD(&vg->vg_lvs, lv, lv_next);
1089176417Sthompsa	G_LLVM_DEBUG(3, "lv: name=%s uuid=%s", lv->lv_name, lv->lv_uuid);
1090176417Sthompsa
1091176417Sthompsa	return (0);
1092176417Sthompsabad:
1093176417Sthompsa	while ((sg = LIST_FIRST(&lv->lv_segs)) != NULL) {
1094176417Sthompsa		LIST_REMOVE(sg, sg_next);
1095176417Sthompsa		free(sg, M_GLLVM);
1096176417Sthompsa	}
1097176417Sthompsa	free(lv, M_GLLVM);
1098176417Sthompsa	return (-1);
1099176417Sthompsa}
1100176417Sthompsa
1101176417Sthompsastatic int
1102176417Sthompsallvm_textconf_decode_sg(char **buf, char *tok, struct g_llvm_lv *lv)
1103176417Sthompsa{
1104176417Sthompsa	struct g_llvm_segment *sg;
1105176417Sthompsa	char *v;
1106176417Sthompsa	int count = 0;
1107176417Sthompsa
1108176417Sthompsa	if (*buf == NULL || **buf == '\0')
1109176417Sthompsa		return (EINVAL);
1110176417Sthompsa
1111176417Sthompsa	sg = malloc(sizeof(*sg), M_GLLVM, M_NOWAIT|M_ZERO);
1112176417Sthompsa	if (sg == NULL)
1113176417Sthompsa		return (ENOMEM);
1114176417Sthompsa
1115176417Sthompsa	while ((tok = strsep(buf, "\n")) != NULL) {
1116176417Sthompsa		/* only a single linear stripe is supported */
1117176417Sthompsa		if (strstr(tok, "stripe_count")) {
1118176417Sthompsa			SPLIT(v, tok, "=");
1119176417Sthompsa			GRAB_INT("stripe_count", v, tok, count);
1120176417Sthompsa			if (count != 1)
1121176417Sthompsa				goto bad;
1122176417Sthompsa		}
1123176417Sthompsa
1124176417Sthompsa		if (strstr(tok, "{"))
1125176417Sthompsa			goto bad;
1126176417Sthompsa
1127176417Sthompsa		if (strstr(tok, "}"))
1128176417Sthompsa			break;
1129176417Sthompsa
1130176417Sthompsa		if (strcmp(tok, "stripes = [") == 0) {
1131176417Sthompsa			tok = strsep(buf, "\n");
1132176417Sthompsa			if (tok == NULL)
1133176417Sthompsa				goto bad;
1134176417Sthompsa
1135176417Sthompsa			strsep(&tok, "\"");
1136176417Sthompsa			if (tok == NULL)
1137176417Sthompsa				goto bad;	/* missing open quotes */
1138176417Sthompsa			v = strsep(&tok, "\"");
1139176417Sthompsa			if (tok == NULL)
1140176417Sthompsa				goto bad;	/* missing close quotes */
1141176417Sthompsa			strncpy(sg->sg_pvname, v, sizeof(sg->sg_pvname));
1142176417Sthompsa			if (*tok != ',')
1143176417Sthompsa				goto bad;	/* missing comma for stripe */
1144176417Sthompsa			tok++;
1145176417Sthompsa
1146176417Sthompsa			sg->sg_pvstart = strtol(tok, &v, 10);
1147176417Sthompsa			if (v == tok)
1148176417Sthompsa				/* strtol did not eat any of the buffer */
1149176417Sthompsa				goto bad;
1150176417Sthompsa
1151176417Sthompsa			continue;
1152176417Sthompsa		}
1153176417Sthompsa
1154176417Sthompsa		/* parse 'key = value' lines */
1155176417Sthompsa		if (strstr(tok, "=")) {
1156176417Sthompsa			SPLIT(v, tok, "=");
1157176417Sthompsa			GRAB_INT("start_extent", v, tok, sg->sg_start);
1158176417Sthompsa			GRAB_INT("extent_count", v, tok, sg->sg_count);
1159176417Sthompsa			continue;
1160176417Sthompsa		}
1161176417Sthompsa	}
1162176417Sthompsa	if (tok == NULL)
1163176417Sthompsa		goto bad;
1164176417Sthompsa	/* basic checking */
1165176417Sthompsa	if (count != 1 || sg->sg_count == 0)
1166176417Sthompsa		goto bad;
1167176417Sthompsa
1168176417Sthompsa	sg->sg_end = sg->sg_start + sg->sg_count - 1;
1169176417Sthompsa	lv->lv_numsegs++;
1170176417Sthompsa	lv->lv_extentcount += sg->sg_count;
1171176417Sthompsa	LIST_INSERT_HEAD(&lv->lv_segs, sg, sg_next);
1172176417Sthompsa
1173176417Sthompsa	return (0);
1174176417Sthompsabad:
1175176417Sthompsa	free(sg, M_GLLVM);
1176176417Sthompsa	return (-1);
1177176417Sthompsa}
1178176417Sthompsa#undef	GRAB_INT
1179176417Sthompsa#undef	GRAB_STR
1180176417Sthompsa#undef	SPLIT
1181176417Sthompsa
1182176417Sthompsastatic struct g_class g_llvm_class = {
1183176417Sthompsa	.name = G_LLVM_CLASS_NAME,
1184176417Sthompsa	.version = G_VERSION,
1185176417Sthompsa	.init = g_llvm_init,
1186176417Sthompsa	.taste = g_llvm_taste,
1187176417Sthompsa	.destroy_geom = g_llvm_destroy_geom
1188176417Sthompsa};
1189176417Sthompsa
1190176417SthompsaDECLARE_GEOM_CLASS(g_llvm_class, g_linux_lvm);
1191332640SkevansMODULE_VERSION(geom_linux_lvm, 0);
1192