1133640Sfjoe/*-
2133640Sfjoe * Copyright (c) 2004 Max Khon
3269456Smarcel * Copyright (c) 2014 Juniper Networks, Inc.
4303167Ssobomax * Copyright (c) 2006-2016 Maxim Sobolev <sobomax@FreeBSD.org>
5133640Sfjoe * All rights reserved.
6133640Sfjoe *
7133640Sfjoe * Redistribution and use in source and binary forms, with or without
8133640Sfjoe * modification, are permitted provided that the following conditions
9133640Sfjoe * are met:
10133640Sfjoe * 1. Redistributions of source code must retain the above copyright
11133640Sfjoe *    notice, this list of conditions and the following disclaimer.
12133640Sfjoe * 2. Redistributions in binary form must reproduce the above copyright
13133640Sfjoe *    notice, this list of conditions and the following disclaimer in the
14133640Sfjoe *    documentation and/or other materials provided with the distribution.
15133640Sfjoe *
16133640Sfjoe * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17133640Sfjoe * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18133640Sfjoe * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19133640Sfjoe * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20133640Sfjoe * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21133640Sfjoe * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22133640Sfjoe * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23133640Sfjoe * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24133640Sfjoe * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25133640Sfjoe * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26133640Sfjoe * SUCH DAMAGE.
27133640Sfjoe */
28133640Sfjoe
29133640Sfjoe#include <sys/cdefs.h>
30133640Sfjoe__FBSDID("$FreeBSD: stable/10/sys/geom/uzip/g_uzip.c 343605 2019-01-31 11:36:28Z avos $");
31133640Sfjoe
32133640Sfjoe#include <sys/param.h>
33133640Sfjoe#include <sys/bio.h>
34133640Sfjoe#include <sys/endian.h>
35133640Sfjoe#include <sys/errno.h>
36133640Sfjoe#include <sys/kernel.h>
37133640Sfjoe#include <sys/lock.h>
38133640Sfjoe#include <sys/mutex.h>
39133640Sfjoe#include <sys/malloc.h>
40266220Sloos#include <sys/sysctl.h>
41133640Sfjoe#include <sys/systm.h>
42303167Ssobomax#include <sys/kthread.h>
43133640Sfjoe
44133640Sfjoe#include <geom/geom.h>
45133640Sfjoe
46303167Ssobomax#include <geom/uzip/g_uzip.h>
47303167Ssobomax#include <geom/uzip/g_uzip_cloop.h>
48303167Ssobomax#include <geom/uzip/g_uzip_softc.h>
49303167Ssobomax#include <geom/uzip/g_uzip_dapi.h>
50303167Ssobomax#include <geom/uzip/g_uzip_zlib.h>
51303167Ssobomax#include <geom/uzip/g_uzip_lzma.h>
52303167Ssobomax#include <geom/uzip/g_uzip_wrkthr.h>
53219029Snetchild
54303167Ssobomax#include "opt_geom.h"
55303167Ssobomax
56303167SsobomaxMALLOC_DEFINE(M_GEOM_UZIP, "geom_uzip", "GEOM UZIP data structures");
57303167Ssobomax
58303167SsobomaxFEATURE(geom_uzip, "GEOM read-only compressed disks support");
59303167Ssobomax
60303167Ssobomaxstruct g_uzip_blk {
61303167Ssobomax        uint64_t offset;
62303167Ssobomax        uint32_t blen;
63303167Ssobomax        unsigned char last:1;
64303167Ssobomax        unsigned char padded:1;
65303167Ssobomax#define BLEN_UNDEF      UINT32_MAX
66303167Ssobomax};
67303167Ssobomax
68303167Ssobomax#ifndef ABS
69303167Ssobomax#define	ABS(a)			((a) < 0 ? -(a) : (a))
70303167Ssobomax#endif
71303167Ssobomax
72303167Ssobomax#define BLK_IN_RANGE(mcn, bcn, ilen)	\
73303167Ssobomax    (((bcn) != BLEN_UNDEF) && ( \
74303167Ssobomax	((ilen) >= 0 && (mcn >= bcn) && (mcn <= ((intmax_t)(bcn) + (ilen)))) || \
75303167Ssobomax	((ilen) < 0 && (mcn <= bcn) && (mcn >= ((intmax_t)(bcn) + (ilen)))) \
76303167Ssobomax    ))
77303167Ssobomax
78133640Sfjoe#ifdef GEOM_UZIP_DEBUG
79303167Ssobomax# define GEOM_UZIP_DBG_DEFAULT	3
80133640Sfjoe#else
81303167Ssobomax# define GEOM_UZIP_DBG_DEFAULT	0
82133640Sfjoe#endif
83133640Sfjoe
84303167Ssobomax#define	GUZ_DBG_ERR	1
85303167Ssobomax#define	GUZ_DBG_INFO	2
86303167Ssobomax#define	GUZ_DBG_IO	3
87303167Ssobomax#define	GUZ_DBG_TOC	4
88133640Sfjoe
89303167Ssobomax#define	GUZ_DEV_SUFX	".uzip"
90303167Ssobomax#define	GUZ_DEV_NAME(p)	(p GUZ_DEV_SUFX)
91303167Ssobomax
92303167Ssobomaxstatic char g_uzip_attach_to[MAXPATHLEN] = {"*"};
93303167Ssobomaxstatic char g_uzip_noattach_to[MAXPATHLEN] = {GUZ_DEV_NAME("*")};
94303167SsobomaxTUNABLE_STR("kern.geom.uzip.attach_to", g_uzip_attach_to,
95303167Ssobomax    sizeof(g_uzip_attach_to));
96303167SsobomaxTUNABLE_STR("kern.geom.uzip.noattach_to", g_uzip_noattach_to,
97303167Ssobomax    sizeof(g_uzip_noattach_to));
98303167Ssobomax
99303167SsobomaxSYSCTL_DECL(_kern_geom);
100303167SsobomaxSYSCTL_NODE(_kern_geom, OID_AUTO, uzip, CTLFLAG_RW, 0, "GEOM_UZIP stuff");
101303167Ssobomaxstatic u_int g_uzip_debug = GEOM_UZIP_DBG_DEFAULT;
102303167SsobomaxSYSCTL_UINT(_kern_geom_uzip, OID_AUTO, debug, CTLFLAG_RWTUN, &g_uzip_debug, 0,
103303167Ssobomax    "Debug level (0-4)");
104303167Ssobomaxstatic u_int g_uzip_debug_block = BLEN_UNDEF;
105303167SsobomaxSYSCTL_UINT(_kern_geom_uzip, OID_AUTO, debug_block, CTLFLAG_RWTUN,
106303167Ssobomax    &g_uzip_debug_block, 0, "Debug operations around specific cluster#");
107303167Ssobomax
108303167Ssobomax#define	DPRINTF(lvl, a)		\
109303167Ssobomax	if ((lvl) <= g_uzip_debug) { \
110303167Ssobomax		printf a; \
111303167Ssobomax	}
112303167Ssobomax#define	DPRINTF_BLK(lvl, cn, a)	\
113303167Ssobomax	if ((lvl) <= g_uzip_debug || \
114303167Ssobomax	    BLK_IN_RANGE(cn, g_uzip_debug_block, 8) || \
115303167Ssobomax	    BLK_IN_RANGE(cn, g_uzip_debug_block, -8)) { \
116303167Ssobomax		printf a; \
117303167Ssobomax	}
118303167Ssobomax#define	DPRINTF_BRNG(lvl, bcn, ecn, a) \
119303167Ssobomax	KASSERT(bcn < ecn, ("DPRINTF_BRNG: invalid range (%ju, %ju)", \
120303167Ssobomax	    (uintmax_t)bcn, (uintmax_t)ecn)); \
121303167Ssobomax	if (((lvl) <= g_uzip_debug) || \
122303167Ssobomax	    BLK_IN_RANGE(g_uzip_debug_block, bcn, \
123303167Ssobomax	     (intmax_t)ecn - (intmax_t)bcn)) { \
124303167Ssobomax		printf a; \
125303167Ssobomax	}
126303167Ssobomax
127266220Sloos#define	UZIP_CLASS_NAME	"UZIP"
128133640Sfjoe
129133640Sfjoe/*
130133640Sfjoe * Maximum allowed valid block size (to prevent foot-shooting)
131133640Sfjoe */
132303167Ssobomax#define	MAX_BLKSZ	(MAXPHYS)
133133640Sfjoe
134133640Sfjoestatic char CLOOP_MAGIC_START[] = "#!/bin/sh\n";
135133640Sfjoe
136303167Ssobomaxstatic void g_uzip_read_done(struct bio *bp);
137303167Ssobomaxstatic void g_uzip_do(struct g_uzip_softc *, struct bio *bp);
138133640Sfjoe
139133640Sfjoestatic void
140133640Sfjoeg_uzip_softc_free(struct g_uzip_softc *sc, struct g_geom *gp)
141133640Sfjoe{
142266220Sloos
143133640Sfjoe	if (gp != NULL) {
144303167Ssobomax		DPRINTF(GUZ_DBG_INFO, ("%s: %d requests, %d cached\n",
145289952Sngie		    gp->name, sc->req_total, sc->req_cached));
146133640Sfjoe	}
147303167Ssobomax
148303167Ssobomax	mtx_lock(&sc->queue_mtx);
149303167Ssobomax	sc->wrkthr_flags |= GUZ_SHUTDOWN;
150303167Ssobomax	wakeup(sc);
151303167Ssobomax	while (!(sc->wrkthr_flags & GUZ_EXITING)) {
152303167Ssobomax		msleep(sc->procp, &sc->queue_mtx, PRIBIO, "guzfree",
153303167Ssobomax		    hz / 10);
154266220Sloos	}
155303167Ssobomax	mtx_unlock(&sc->queue_mtx);
156303167Ssobomax
157303167Ssobomax	sc->dcp->free(sc->dcp);
158303167Ssobomax	free(sc->toc, M_GEOM_UZIP);
159303167Ssobomax	mtx_destroy(&sc->queue_mtx);
160133640Sfjoe	mtx_destroy(&sc->last_mtx);
161133640Sfjoe	free(sc->last_buf, M_GEOM_UZIP);
162133640Sfjoe	free(sc, M_GEOM_UZIP);
163133640Sfjoe}
164133640Sfjoe
165269456Smarcelstatic int
166269456Smarcelg_uzip_cached(struct g_geom *gp, struct bio *bp)
167269456Smarcel{
168269456Smarcel	struct g_uzip_softc *sc;
169269456Smarcel	off_t ofs;
170269456Smarcel	size_t blk, blkofs, usz;
171269456Smarcel
172269456Smarcel	sc = gp->softc;
173269456Smarcel	ofs = bp->bio_offset + bp->bio_completed;
174269456Smarcel	blk = ofs / sc->blksz;
175269456Smarcel	mtx_lock(&sc->last_mtx);
176269456Smarcel	if (blk == sc->last_blk) {
177269456Smarcel		blkofs = ofs % sc->blksz;
178269456Smarcel		usz = sc->blksz - blkofs;
179269456Smarcel		if (bp->bio_resid < usz)
180269456Smarcel			usz = bp->bio_resid;
181269456Smarcel		memcpy(bp->bio_data + bp->bio_completed, sc->last_buf + blkofs,
182269456Smarcel		    usz);
183269456Smarcel		sc->req_cached++;
184269456Smarcel		mtx_unlock(&sc->last_mtx);
185269456Smarcel
186303167Ssobomax		DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: offset=%jd: got %jd bytes "
187303167Ssobomax		    "from cache\n", __func__, gp->name, bp, (intmax_t)ofs,
188303167Ssobomax		    (intmax_t)usz));
189269456Smarcel
190269456Smarcel		bp->bio_completed += usz;
191269456Smarcel		bp->bio_resid -= usz;
192269456Smarcel
193269456Smarcel		if (bp->bio_resid == 0) {
194269456Smarcel			g_io_deliver(bp, 0);
195269456Smarcel			return (1);
196269456Smarcel		}
197269456Smarcel	} else
198269456Smarcel		mtx_unlock(&sc->last_mtx);
199269456Smarcel
200269456Smarcel	return (0);
201269456Smarcel}
202269456Smarcel
203303167Ssobomax#define BLK_ENDS(sc, bi)	((sc)->toc[(bi)].offset + \
204303167Ssobomax    (sc)->toc[(bi)].blen)
205303167Ssobomax
206303167Ssobomax#define BLK_IS_CONT(sc, bi)	(BLK_ENDS((sc), (bi) - 1) == \
207303167Ssobomax    (sc)->toc[(bi)].offset)
208303167Ssobomax#define	BLK_IS_NIL(sc, bi)	((sc)->toc[(bi)].blen == 0)
209303167Ssobomax
210303167Ssobomax#define TOFF_2_BOFF(sc, pp, bi)	    ((sc)->toc[(bi)].offset - \
211303167Ssobomax    (sc)->toc[(bi)].offset % (pp)->sectorsize)
212303167Ssobomax#define TLEN_2_BLEN(sc, pp, bp, ei) ((BLK_ENDS((sc), (ei)) - \
213303167Ssobomax    (bp)->bio_offset + (pp)->sectorsize - 1) / \
214303167Ssobomax    (pp)->sectorsize * (pp)->sectorsize)
215303167Ssobomax
216269456Smarcelstatic int
217269456Smarcelg_uzip_request(struct g_geom *gp, struct bio *bp)
218269456Smarcel{
219269456Smarcel	struct g_uzip_softc *sc;
220269456Smarcel	struct bio *bp2;
221269456Smarcel	struct g_consumer *cp;
222269456Smarcel	struct g_provider *pp;
223303167Ssobomax	off_t ofs, start_blk_ofs;
224303167Ssobomax	size_t i, start_blk, end_blk, zsize;
225269456Smarcel
226269456Smarcel	if (g_uzip_cached(gp, bp) != 0)
227269456Smarcel		return (1);
228269456Smarcel
229269456Smarcel	sc = gp->softc;
230269456Smarcel
231269456Smarcel	cp = LIST_FIRST(&gp->consumer);
232269456Smarcel	pp = cp->provider;
233269456Smarcel
234269456Smarcel	ofs = bp->bio_offset + bp->bio_completed;
235269456Smarcel	start_blk = ofs / sc->blksz;
236269456Smarcel	KASSERT(start_blk < sc->nblocks, ("start_blk out of range"));
237269456Smarcel	end_blk = (ofs + bp->bio_resid + sc->blksz - 1) / sc->blksz;
238269456Smarcel	KASSERT(end_blk <= sc->nblocks, ("end_blk out of range"));
239269456Smarcel
240303167Ssobomax	for (; BLK_IS_NIL(sc, start_blk) && start_blk < end_blk; start_blk++) {
241303167Ssobomax		/* Fill in any leading Nil blocks */
242303167Ssobomax		start_blk_ofs = ofs % sc->blksz;
243303167Ssobomax		zsize = MIN(sc->blksz - start_blk_ofs, bp->bio_resid);
244303167Ssobomax		DPRINTF_BLK(GUZ_DBG_IO, start_blk, ("%s/%s: %p/%ju: "
245303167Ssobomax		    "filling %ju zero bytes\n", __func__, gp->name, gp,
246303167Ssobomax		    (uintmax_t)bp->bio_completed, (uintmax_t)zsize));
247303167Ssobomax		bzero(bp->bio_data + bp->bio_completed, zsize);
248303167Ssobomax		bp->bio_completed += zsize;
249303167Ssobomax		bp->bio_resid -= zsize;
250303167Ssobomax		ofs += zsize;
251303167Ssobomax	}
252269456Smarcel
253303167Ssobomax	if (start_blk == end_blk) {
254303167Ssobomax		KASSERT(bp->bio_resid == 0, ("bp->bio_resid is invalid"));
255303167Ssobomax		/*
256303167Ssobomax		 * No non-Nil data is left, complete request immediately.
257303167Ssobomax		 */
258303167Ssobomax		DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: all done returning %ju "
259303167Ssobomax		    "bytes\n", __func__, gp->name, gp,
260303167Ssobomax		    (uintmax_t)bp->bio_completed));
261303167Ssobomax		g_io_deliver(bp, 0);
262303167Ssobomax		return (1);
263303167Ssobomax	}
264303167Ssobomax
265303167Ssobomax	for (i = start_blk + 1; i < end_blk; i++) {
266303167Ssobomax		/* Trim discontinuous areas if any */
267303167Ssobomax		if (!BLK_IS_CONT(sc, i)) {
268303167Ssobomax			end_blk = i;
269303167Ssobomax			break;
270303167Ssobomax		}
271303167Ssobomax	}
272303167Ssobomax
273303167Ssobomax	DPRINTF_BRNG(GUZ_DBG_IO, start_blk, end_blk, ("%s/%s: %p: "
274303167Ssobomax	    "start=%u (%ju[%jd]), end=%u (%ju)\n", __func__, gp->name, bp,
275303167Ssobomax	    (u_int)start_blk, (uintmax_t)sc->toc[start_blk].offset,
276303167Ssobomax	    (intmax_t)sc->toc[start_blk].blen,
277303167Ssobomax	    (u_int)end_blk, (uintmax_t)BLK_ENDS(sc, end_blk - 1)));
278303167Ssobomax
279303167Ssobomax	bp2 = g_clone_bio(bp);
280303167Ssobomax	if (bp2 == NULL) {
281303167Ssobomax		g_io_deliver(bp, ENOMEM);
282303167Ssobomax		return (1);
283303167Ssobomax	}
284303167Ssobomax	bp2->bio_done = g_uzip_read_done;
285303167Ssobomax
286303167Ssobomax	bp2->bio_offset = TOFF_2_BOFF(sc, pp, start_blk);
287269456Smarcel	while (1) {
288303167Ssobomax		bp2->bio_length = TLEN_2_BLEN(sc, pp, bp2, end_blk - 1);
289303167Ssobomax		if (bp2->bio_length <= MAXPHYS) {
290269456Smarcel			break;
291303167Ssobomax		}
292303167Ssobomax		if (end_blk == (start_blk + 1)) {
293303167Ssobomax			break;
294303167Ssobomax		}
295269456Smarcel		end_blk--;
296269456Smarcel	}
297269456Smarcel
298303167Ssobomax	DPRINTF(GUZ_DBG_IO, ("%s/%s: bp2->bio_length = %jd, "
299303167Ssobomax	    "bp2->bio_offset = %jd\n", __func__, gp->name,
300303167Ssobomax	    (intmax_t)bp2->bio_length, (intmax_t)bp2->bio_offset));
301303167Ssobomax
302269456Smarcel	bp2->bio_data = malloc(bp2->bio_length, M_GEOM_UZIP, M_NOWAIT);
303269456Smarcel	if (bp2->bio_data == NULL) {
304269456Smarcel		g_destroy_bio(bp2);
305269456Smarcel		g_io_deliver(bp, ENOMEM);
306269456Smarcel		return (1);
307269456Smarcel	}
308269456Smarcel
309303167Ssobomax	DPRINTF_BRNG(GUZ_DBG_IO, start_blk, end_blk, ("%s/%s: %p: "
310303167Ssobomax	    "reading %jd bytes from offset %jd\n", __func__, gp->name, bp,
311269456Smarcel	    (intmax_t)bp2->bio_length, (intmax_t)bp2->bio_offset));
312269456Smarcel
313269456Smarcel	g_io_request(bp2, cp);
314269456Smarcel	return (0);
315269456Smarcel}
316269456Smarcel
317133640Sfjoestatic void
318303167Ssobomaxg_uzip_read_done(struct bio *bp)
319133640Sfjoe{
320133640Sfjoe	struct bio *bp2;
321303167Ssobomax	struct g_geom *gp;
322303167Ssobomax	struct g_uzip_softc *sc;
323303167Ssobomax
324303167Ssobomax	bp2 = bp->bio_parent;
325303167Ssobomax	gp = bp2->bio_to->geom;
326303167Ssobomax	sc = gp->softc;
327303167Ssobomax
328303167Ssobomax	mtx_lock(&sc->queue_mtx);
329303167Ssobomax	bioq_disksort(&sc->bio_queue, bp);
330303167Ssobomax	mtx_unlock(&sc->queue_mtx);
331303167Ssobomax	wakeup(sc);
332303167Ssobomax}
333303167Ssobomax
334303167Ssobomaxstatic int
335303167Ssobomaxg_uzip_memvcmp(const void *memory, unsigned char val, size_t size)
336303167Ssobomax{
337303167Ssobomax	const u_char *mm;
338303167Ssobomax
339303167Ssobomax	mm = (const u_char *)memory;
340303167Ssobomax	return (*mm == val) && memcmp(mm, mm + 1, size - 1) == 0;
341303167Ssobomax}
342303167Ssobomax
343303167Ssobomaxstatic void
344303167Ssobomaxg_uzip_do(struct g_uzip_softc *sc, struct bio *bp)
345303167Ssobomax{
346303167Ssobomax	struct bio *bp2;
347269456Smarcel	struct g_provider *pp;
348133640Sfjoe	struct g_consumer *cp;
349133640Sfjoe	struct g_geom *gp;
350269456Smarcel	char *data, *data2;
351269456Smarcel	off_t ofs;
352303167Ssobomax	size_t blk, blkofs, len, ulen, firstblk;
353303167Ssobomax	int err;
354133640Sfjoe
355133640Sfjoe	bp2 = bp->bio_parent;
356269456Smarcel	gp = bp2->bio_to->geom;
357133640Sfjoe
358269456Smarcel	cp = LIST_FIRST(&gp->consumer);
359269456Smarcel	pp = cp->provider;
360269456Smarcel
361133640Sfjoe	bp2->bio_error = bp->bio_error;
362133640Sfjoe	if (bp2->bio_error != 0)
363133640Sfjoe		goto done;
364133640Sfjoe
365269456Smarcel	/* Make sure there's forward progress. */
366269456Smarcel	if (bp->bio_completed == 0) {
367269456Smarcel		bp2->bio_error = ECANCELED;
368269456Smarcel		goto done;
369269456Smarcel	}
370269456Smarcel
371269456Smarcel	ofs = bp2->bio_offset + bp2->bio_completed;
372303167Ssobomax	firstblk = blk = ofs / sc->blksz;
373269456Smarcel	blkofs = ofs % sc->blksz;
374303167Ssobomax	data = bp->bio_data + sc->toc[blk].offset % pp->sectorsize;
375269456Smarcel	data2 = bp2->bio_data + bp2->bio_completed;
376269456Smarcel	while (bp->bio_completed && bp2->bio_resid) {
377303167Ssobomax		if (blk > firstblk && !BLK_IS_CONT(sc, blk)) {
378303167Ssobomax			DPRINTF_BLK(GUZ_DBG_IO, blk, ("%s/%s: %p: backref'ed "
379303167Ssobomax			    "cluster #%u requested, looping around\n",
380303167Ssobomax			    __func__, gp->name, bp2, (u_int)blk));
381303167Ssobomax			goto done;
382303167Ssobomax		}
383269456Smarcel		ulen = MIN(sc->blksz - blkofs, bp2->bio_resid);
384303167Ssobomax		len = sc->toc[blk].blen;
385303167Ssobomax		DPRINTF(GUZ_DBG_IO, ("%s/%s: %p/%ju: data2=%p, ulen=%u, "
386303167Ssobomax		    "data=%p, len=%u\n", __func__, gp->name, gp,
387303167Ssobomax		    bp->bio_completed, data2, (u_int)ulen, data, (u_int)len));
388168999Ssimokawa		if (len == 0) {
389168999Ssimokawa			/* All zero block: no cache update */
390303167Ssobomaxzero_block:
391269456Smarcel			bzero(data2, ulen);
392269456Smarcel		} else if (len <= bp->bio_completed) {
393269456Smarcel			mtx_lock(&sc->last_mtx);
394303167Ssobomax			err = sc->dcp->decompress(sc->dcp, gp->name, data,
395303167Ssobomax			    len, sc->last_buf);
396303167Ssobomax			if (err != 0 && sc->toc[blk].last != 0) {
397303167Ssobomax				/*
398303167Ssobomax				 * Last block decompression has failed, check
399303167Ssobomax				 * if it's just zero padding.
400303167Ssobomax				 */
401303167Ssobomax				if (g_uzip_memvcmp(data, '\0', len) == 0) {
402303167Ssobomax					sc->toc[blk].blen = 0;
403303167Ssobomax					sc->last_blk = -1;
404303167Ssobomax					mtx_unlock(&sc->last_mtx);
405303167Ssobomax					len = 0;
406303167Ssobomax					goto zero_block;
407303167Ssobomax				}
408303167Ssobomax			}
409303167Ssobomax			if (err != 0) {
410269456Smarcel				sc->last_blk = -1;
411269456Smarcel				mtx_unlock(&sc->last_mtx);
412269456Smarcel				bp2->bio_error = EILSEQ;
413303167Ssobomax				DPRINTF(GUZ_DBG_ERR, ("%s/%s: decompress"
414303167Ssobomax				    "(%p, %ju, %ju) failed\n", __func__,
415303167Ssobomax				    gp->name, sc->dcp, (uintmax_t)blk,
416303167Ssobomax				    (uintmax_t)len));
417269456Smarcel				goto done;
418269456Smarcel			}
419269456Smarcel			sc->last_blk = blk;
420269456Smarcel			memcpy(data2, sc->last_buf + blkofs, ulen);
421269456Smarcel			mtx_unlock(&sc->last_mtx);
422303167Ssobomax			err = sc->dcp->rewind(sc->dcp, gp->name);
423303167Ssobomax			if (err != 0) {
424269456Smarcel				bp2->bio_error = EILSEQ;
425303167Ssobomax				DPRINTF(GUZ_DBG_ERR, ("%s/%s: rewind(%p) "
426303167Ssobomax				    "failed\n", __func__, gp->name, sc->dcp));
427269456Smarcel				goto done;
428269456Smarcel			}
429269456Smarcel			data += len;
430269456Smarcel		} else
431266220Sloos			break;
432133640Sfjoe
433269456Smarcel		data2 += ulen;
434133640Sfjoe		bp2->bio_completed += ulen;
435269456Smarcel		bp2->bio_resid -= ulen;
436269456Smarcel		bp->bio_completed -= len;
437269456Smarcel		blkofs = 0;
438269456Smarcel		blk++;
439133640Sfjoe	}
440133640Sfjoe
441133640Sfjoedone:
442269456Smarcel	/* Finish processing the request. */
443133640Sfjoe	free(bp->bio_data, M_GEOM_UZIP);
444133640Sfjoe	g_destroy_bio(bp);
445269456Smarcel	if (bp2->bio_error != 0 || bp2->bio_resid == 0)
446269456Smarcel		g_io_deliver(bp2, bp2->bio_error);
447269456Smarcel	else
448269456Smarcel		g_uzip_request(gp, bp2);
449133640Sfjoe}
450133640Sfjoe
451133640Sfjoestatic void
452133640Sfjoeg_uzip_start(struct bio *bp)
453133640Sfjoe{
454269456Smarcel	struct g_provider *pp;
455133640Sfjoe	struct g_geom *gp;
456133640Sfjoe	struct g_uzip_softc *sc;
457133640Sfjoe
458133640Sfjoe	pp = bp->bio_to;
459133640Sfjoe	gp = pp->geom;
460133640Sfjoe
461303167Ssobomax	DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: cmd=%d, offset=%jd, length=%jd, "
462303167Ssobomax	    "buffer=%p\n", __func__, gp->name, bp, bp->bio_cmd,
463303167Ssobomax	    (intmax_t)bp->bio_offset, (intmax_t)bp->bio_length, bp->bio_data));
464133640Sfjoe
465133640Sfjoe	sc = gp->softc;
466133640Sfjoe	sc->req_total++;
467133640Sfjoe
468269456Smarcel	if (bp->bio_cmd != BIO_READ) {
469269456Smarcel		g_io_deliver(bp, EOPNOTSUPP);
470133640Sfjoe		return;
471133640Sfjoe	}
472266220Sloos
473269456Smarcel	bp->bio_resid = bp->bio_length;
474269456Smarcel	bp->bio_completed = 0;
475133640Sfjoe
476269456Smarcel	g_uzip_request(gp, bp);
477133640Sfjoe}
478133640Sfjoe
479133640Sfjoestatic void
480133640Sfjoeg_uzip_orphan(struct g_consumer *cp)
481133640Sfjoe{
482133640Sfjoe	struct g_geom *gp;
483133640Sfjoe
484266220Sloos	g_trace(G_T_TOPOLOGY, "%s(%p/%s)", __func__, cp, cp->provider->name);
485133640Sfjoe	g_topology_assert();
486133640Sfjoe
487133640Sfjoe	gp = cp->geom;
488133640Sfjoe	g_uzip_softc_free(gp->softc, gp);
489133640Sfjoe	gp->softc = NULL;
490238198Strasz	g_wither_geom(gp, ENXIO);
491133640Sfjoe}
492133640Sfjoe
493133640Sfjoestatic int
494133640Sfjoeg_uzip_access(struct g_provider *pp, int dr, int dw, int de)
495133640Sfjoe{
496133640Sfjoe	struct g_geom *gp;
497133640Sfjoe	struct g_consumer *cp;
498133640Sfjoe
499133640Sfjoe	gp = pp->geom;
500133640Sfjoe	cp = LIST_FIRST(&gp->consumer);
501133640Sfjoe	KASSERT (cp != NULL, ("g_uzip_access but no consumer"));
502133640Sfjoe
503150735Sfjoe	if (cp->acw + dw > 0)
504266220Sloos		return (EROFS);
505150735Sfjoe
506133640Sfjoe	return (g_access(cp, dr, dw, de));
507133640Sfjoe}
508133640Sfjoe
509133640Sfjoestatic void
510133640Sfjoeg_uzip_spoiled(struct g_consumer *cp)
511133640Sfjoe{
512133640Sfjoe	struct g_geom *gp;
513133640Sfjoe
514303167Ssobomax	G_VALID_CONSUMER(cp);
515133640Sfjoe	gp = cp->geom;
516266220Sloos	g_trace(G_T_TOPOLOGY, "%s(%p/%s)", __func__, cp, gp->name);
517133640Sfjoe	g_topology_assert();
518133640Sfjoe
519133640Sfjoe	g_uzip_softc_free(gp->softc, gp);
520133640Sfjoe	gp->softc = NULL;
521133640Sfjoe	g_wither_geom(gp, ENXIO);
522133640Sfjoe}
523133640Sfjoe
524303167Ssobomaxstatic int
525303167Ssobomaxg_uzip_parse_toc(struct g_uzip_softc *sc, struct g_provider *pp,
526303167Ssobomax    struct g_geom *gp)
527303167Ssobomax{
528303167Ssobomax	uint32_t i, j, backref_to;
529303167Ssobomax	uint64_t max_offset, min_offset;
530303167Ssobomax	struct g_uzip_blk *last_blk;
531303167Ssobomax
532303167Ssobomax	min_offset = sizeof(struct cloop_header) +
533303167Ssobomax	    (sc->nblocks + 1) * sizeof(uint64_t);
534303167Ssobomax	max_offset = sc->toc[0].offset - 1;
535303167Ssobomax	last_blk = &sc->toc[0];
536303167Ssobomax	for (i = 0; i < sc->nblocks; i++) {
537303167Ssobomax		/* First do some bounds checking */
538303167Ssobomax		if ((sc->toc[i].offset < min_offset) ||
539303167Ssobomax		    (sc->toc[i].offset > pp->mediasize)) {
540303167Ssobomax			goto error_offset;
541303167Ssobomax		}
542303167Ssobomax		DPRINTF_BLK(GUZ_DBG_IO, i, ("%s: cluster #%u "
543303167Ssobomax		    "offset=%ju max_offset=%ju\n", gp->name,
544303167Ssobomax		    (u_int)i, (uintmax_t)sc->toc[i].offset,
545303167Ssobomax		    (uintmax_t)max_offset));
546303167Ssobomax		backref_to = BLEN_UNDEF;
547303167Ssobomax		if (sc->toc[i].offset < max_offset) {
548303167Ssobomax			/*
549303167Ssobomax			 * For the backref'ed blocks search already parsed
550303167Ssobomax			 * TOC entries for the matching offset and copy the
551303167Ssobomax			 * size from matched entry.
552303167Ssobomax			 */
553303167Ssobomax			for (j = 0; j <= i; j++) {
554303167Ssobomax                                if (sc->toc[j].offset == sc->toc[i].offset &&
555303167Ssobomax				    !BLK_IS_NIL(sc, j)) {
556303167Ssobomax                                        break;
557303167Ssobomax                                }
558303167Ssobomax                                if (j != i) {
559303167Ssobomax					continue;
560303167Ssobomax				}
561303167Ssobomax				DPRINTF(GUZ_DBG_ERR, ("%s: cannot match "
562303167Ssobomax				    "backref'ed offset at cluster #%u\n",
563303167Ssobomax				    gp->name, i));
564303167Ssobomax				return (-1);
565303167Ssobomax			}
566303167Ssobomax			sc->toc[i].blen = sc->toc[j].blen;
567303167Ssobomax			backref_to = j;
568303167Ssobomax		} else {
569303167Ssobomax			last_blk = &sc->toc[i];
570303167Ssobomax			/*
571303167Ssobomax			 * For the "normal blocks" seek forward until we hit
572303167Ssobomax			 * block whose offset is larger than ours and assume
573303167Ssobomax			 * it's going to be the next one.
574303167Ssobomax			 */
575303167Ssobomax			for (j = i + 1; j < sc->nblocks; j++) {
576303167Ssobomax				if (sc->toc[j].offset > max_offset) {
577303167Ssobomax					break;
578303167Ssobomax				}
579303167Ssobomax			}
580303167Ssobomax			sc->toc[i].blen = sc->toc[j].offset -
581303167Ssobomax			    sc->toc[i].offset;
582303167Ssobomax			if (BLK_ENDS(sc, i) > pp->mediasize) {
583303167Ssobomax				DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u "
584303167Ssobomax				    "extends past media boundary (%ju > %ju)\n",
585303167Ssobomax				    gp->name, (u_int)i,
586303167Ssobomax				    (uintmax_t)BLK_ENDS(sc, i),
587303167Ssobomax				    (intmax_t)pp->mediasize));
588303167Ssobomax				return (-1);
589303167Ssobomax			}
590303167Ssobomax			KASSERT(max_offset <= sc->toc[i].offset, (
591303167Ssobomax			    "%s: max_offset is incorrect: %ju",
592303167Ssobomax			    gp->name, (uintmax_t)max_offset));
593303167Ssobomax			max_offset = BLK_ENDS(sc, i) - 1;
594303167Ssobomax		}
595303167Ssobomax		DPRINTF_BLK(GUZ_DBG_TOC, i, ("%s: cluster #%u, original %u "
596303167Ssobomax		    "bytes, in %u bytes", gp->name, i, sc->blksz,
597303167Ssobomax		    sc->toc[i].blen));
598303167Ssobomax		if (backref_to != BLEN_UNDEF) {
599303167Ssobomax			DPRINTF_BLK(GUZ_DBG_TOC, i, (" (->#%u)",
600303167Ssobomax			    (u_int)backref_to));
601303167Ssobomax		}
602303167Ssobomax		DPRINTF_BLK(GUZ_DBG_TOC, i, ("\n"));
603303167Ssobomax	}
604303167Ssobomax	last_blk->last = 1;
605303167Ssobomax	/* Do a second pass to validate block lengths */
606303167Ssobomax	for (i = 0; i < sc->nblocks; i++) {
607303167Ssobomax		if (sc->toc[i].blen > sc->dcp->max_blen) {
608303167Ssobomax			if (sc->toc[i].last == 0) {
609303167Ssobomax				DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u "
610303167Ssobomax				    "length (%ju) exceeds "
611303167Ssobomax				    "max_blen (%ju)\n", gp->name, i,
612303167Ssobomax				    (uintmax_t)sc->toc[i].blen,
613303167Ssobomax				    (uintmax_t)sc->dcp->max_blen));
614303167Ssobomax				return (-1);
615303167Ssobomax			}
616303167Ssobomax			DPRINTF(GUZ_DBG_INFO, ("%s: cluster #%u extra "
617303167Ssobomax			    "padding is detected, trimmed to %ju\n",
618303167Ssobomax			    gp->name, i, (uintmax_t)sc->dcp->max_blen));
619303167Ssobomax			    sc->toc[i].blen = sc->dcp->max_blen;
620303167Ssobomax			sc->toc[i].padded = 1;
621303167Ssobomax		}
622303167Ssobomax	}
623303167Ssobomax	return (0);
624303167Ssobomax
625303167Ssobomaxerror_offset:
626303167Ssobomax	DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u: invalid offset %ju, "
627303167Ssobomax	    "min_offset=%ju mediasize=%jd\n", gp->name, (u_int)i,
628303167Ssobomax	    sc->toc[i].offset, min_offset, pp->mediasize));
629303167Ssobomax	return (-1);
630303167Ssobomax}
631303167Ssobomax
632133640Sfjoestatic struct g_geom *
633133640Sfjoeg_uzip_taste(struct g_class *mp, struct g_provider *pp, int flags)
634133640Sfjoe{
635133640Sfjoe	int error;
636133640Sfjoe	uint32_t i, total_offsets, offsets_read, blk;
637133640Sfjoe	void *buf;
638133640Sfjoe	struct cloop_header *header;
639133640Sfjoe	struct g_consumer *cp;
640133640Sfjoe	struct g_geom *gp;
641133640Sfjoe	struct g_provider *pp2;
642133640Sfjoe	struct g_uzip_softc *sc;
643303167Ssobomax	enum {
644303167Ssobomax		G_UZIP = 1,
645303167Ssobomax		G_ULZMA
646303167Ssobomax	} type;
647133640Sfjoe
648266220Sloos	g_trace(G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name);
649133640Sfjoe	g_topology_assert();
650197898Spjd
651197898Spjd	/* Skip providers that are already open for writing. */
652197898Spjd	if (pp->acw > 0)
653197898Spjd		return (NULL);
654197898Spjd
655303167Ssobomax	if ((fnmatch(g_uzip_attach_to, pp->name, 0) != 0) ||
656303167Ssobomax	    (fnmatch(g_uzip_noattach_to, pp->name, 0) == 0)) {
657303167Ssobomax		DPRINTF(GUZ_DBG_INFO, ("%s(%s,%s), ignoring\n", __func__,
658303167Ssobomax		    mp->name, pp->name));
659303167Ssobomax		return (NULL);
660303167Ssobomax	}
661303167Ssobomax
662133640Sfjoe	buf = NULL;
663133640Sfjoe
664133640Sfjoe	/*
665133640Sfjoe	 * Create geom instance.
666133640Sfjoe	 */
667303167Ssobomax	gp = g_new_geomf(mp, GUZ_DEV_NAME("%s"), pp->name);
668133640Sfjoe	cp = g_new_consumer(gp);
669133640Sfjoe	error = g_attach(cp, pp);
670133640Sfjoe	if (error == 0)
671133640Sfjoe		error = g_access(cp, 1, 0, 0);
672133640Sfjoe	if (error) {
673303167Ssobomax		goto e1;
674133640Sfjoe	}
675133640Sfjoe	g_topology_unlock();
676133640Sfjoe
677133640Sfjoe	/*
678133640Sfjoe	 * Read cloop header, look for CLOOP magic, perform
679133640Sfjoe	 * other validity checks.
680133640Sfjoe	 */
681303167Ssobomax	DPRINTF(GUZ_DBG_INFO, ("%s: media sectorsize %u, mediasize %jd\n",
682266220Sloos	    gp->name, pp->sectorsize, (intmax_t)pp->mediasize));
683152971Ssobomax	buf = g_read_data(cp, 0, pp->sectorsize, NULL);
684152967Ssobomax	if (buf == NULL)
685303167Ssobomax		goto e2;
686133640Sfjoe	header = (struct cloop_header *) buf;
687133640Sfjoe	if (strncmp(header->magic, CLOOP_MAGIC_START,
688266220Sloos	    sizeof(CLOOP_MAGIC_START) - 1) != 0) {
689303167Ssobomax		DPRINTF(GUZ_DBG_ERR, ("%s: no CLOOP magic\n", gp->name));
690303167Ssobomax		goto e3;
691133640Sfjoe	}
692133640Sfjoe
693303167Ssobomax	switch (header->magic[CLOOP_OFS_COMPR]) {
694303167Ssobomax	case CLOOP_COMP_LZMA:
695303167Ssobomax	case CLOOP_COMP_LZMA_DDP:
696303167Ssobomax		type = G_ULZMA;
697303167Ssobomax		if (header->magic[CLOOP_OFS_VERSN] < CLOOP_MINVER_LZMA) {
698303167Ssobomax			DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n",
699303167Ssobomax			    gp->name));
700303167Ssobomax			goto e3;
701303167Ssobomax		}
702303167Ssobomax		DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_LZMA image found\n",
703303167Ssobomax		    gp->name));
704303167Ssobomax		break;
705303167Ssobomax	case CLOOP_COMP_LIBZ:
706303167Ssobomax	case CLOOP_COMP_LIBZ_DDP:
707303167Ssobomax		type = G_UZIP;
708303167Ssobomax		if (header->magic[CLOOP_OFS_VERSN] < CLOOP_MINVER_ZLIB) {
709303167Ssobomax			DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n",
710303167Ssobomax			    gp->name));
711303167Ssobomax			goto e3;
712303167Ssobomax		}
713303167Ssobomax		DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_ZLIB image found\n",
714303167Ssobomax		    gp->name));
715303167Ssobomax		break;
716303167Ssobomax	default:
717303167Ssobomax		DPRINTF(GUZ_DBG_ERR, ("%s: unsupported image type\n",
718303167Ssobomax		    gp->name));
719303167Ssobomax                goto e3;
720303167Ssobomax        }
721303167Ssobomax
722133640Sfjoe	/*
723133640Sfjoe	 * Initialize softc and read offsets.
724133640Sfjoe	 */
725137936Sfjoe	sc = malloc(sizeof(*sc), M_GEOM_UZIP, M_WAITOK | M_ZERO);
726133640Sfjoe	gp->softc = sc;
727133640Sfjoe	sc->blksz = ntohl(header->blksz);
728133640Sfjoe	sc->nblocks = ntohl(header->nblocks);
729133640Sfjoe	if (sc->blksz % 512 != 0) {
730133640Sfjoe		printf("%s: block size (%u) should be multiple of 512.\n",
731133640Sfjoe		    gp->name, sc->blksz);
732303167Ssobomax		goto e4;
733133640Sfjoe	}
734133640Sfjoe	if (sc->blksz > MAX_BLKSZ) {
735133640Sfjoe		printf("%s: block size (%u) should not be larger than %d.\n",
736133640Sfjoe		    gp->name, sc->blksz, MAX_BLKSZ);
737133640Sfjoe	}
738133640Sfjoe	total_offsets = sc->nblocks + 1;
739133640Sfjoe	if (sizeof(struct cloop_header) +
740133640Sfjoe	    total_offsets * sizeof(uint64_t) > pp->mediasize) {
741133640Sfjoe		printf("%s: media too small for %u blocks\n",
742266220Sloos		    gp->name, sc->nblocks);
743303167Ssobomax		goto e4;
744133640Sfjoe	}
745303167Ssobomax	sc->toc = malloc(total_offsets * sizeof(struct g_uzip_blk),
746303167Ssobomax	    M_GEOM_UZIP, M_WAITOK | M_ZERO);
747133640Sfjoe	offsets_read = MIN(total_offsets,
748133640Sfjoe	    (pp->sectorsize - sizeof(*header)) / sizeof(uint64_t));
749303167Ssobomax	for (i = 0; i < offsets_read; i++) {
750303167Ssobomax		sc->toc[i].offset = be64toh(((uint64_t *) (header + 1))[i]);
751303167Ssobomax		sc->toc[i].blen = BLEN_UNDEF;
752303167Ssobomax	}
753303167Ssobomax	DPRINTF(GUZ_DBG_INFO, ("%s: %u offsets in the first sector\n",
754133640Sfjoe	       gp->name, offsets_read));
755133640Sfjoe	for (blk = 1; offsets_read < total_offsets; blk++) {
756133640Sfjoe		uint32_t nread;
757133640Sfjoe
758135461Sfjoe		free(buf, M_GEOM);
759133640Sfjoe		buf = g_read_data(
760152971Ssobomax		    cp, blk * pp->sectorsize, pp->sectorsize, NULL);
761152967Ssobomax		if (buf == NULL)
762303167Ssobomax			goto e5;
763133640Sfjoe		nread = MIN(total_offsets - offsets_read,
764133640Sfjoe		     pp->sectorsize / sizeof(uint64_t));
765303167Ssobomax		DPRINTF(GUZ_DBG_TOC, ("%s: %u offsets read from sector %d\n",
766133640Sfjoe		    gp->name, nread, blk));
767133640Sfjoe		for (i = 0; i < nread; i++) {
768303167Ssobomax			sc->toc[offsets_read + i].offset =
769133640Sfjoe			    be64toh(((uint64_t *) buf)[i]);
770303167Ssobomax			sc->toc[offsets_read + i].blen = BLEN_UNDEF;
771133640Sfjoe		}
772133640Sfjoe		offsets_read += nread;
773133640Sfjoe	}
774266220Sloos	free(buf, M_GEOM);
775303167Ssobomax	buf = NULL;
776303167Ssobomax	offsets_read -= 1;
777303167Ssobomax	DPRINTF(GUZ_DBG_INFO, ("%s: done reading %u block offsets from %u "
778303167Ssobomax	    "sectors\n", gp->name, offsets_read, blk));
779303167Ssobomax	if (sc->nblocks != offsets_read) {
780303167Ssobomax		DPRINTF(GUZ_DBG_ERR, ("%s: read %s offsets than expected "
781303167Ssobomax		    "blocks\n", gp->name,
782303167Ssobomax		    sc->nblocks < offsets_read ? "more" : "less"));
783303167Ssobomax		goto e5;
784303167Ssobomax	}
785303167Ssobomax
786303167Ssobomax	if (type == G_UZIP) {
787303167Ssobomax		sc->dcp = g_uzip_zlib_ctor(sc->blksz);
788303167Ssobomax	} else {
789303167Ssobomax		sc->dcp = g_uzip_lzma_ctor(sc->blksz);
790303167Ssobomax	}
791303167Ssobomax	if (sc->dcp == NULL) {
792303167Ssobomax		goto e5;
793303167Ssobomax	}
794303167Ssobomax
795303167Ssobomax	/*
796303167Ssobomax	 * "Fake" last+1 block, to make it easier for the TOC parser to
797303167Ssobomax	 * iterate without making the last element a special case.
798303167Ssobomax	 */
799303167Ssobomax	sc->toc[sc->nblocks].offset = pp->mediasize;
800303167Ssobomax	/* Massage TOC (table of contents), make sure it is sound */
801303167Ssobomax	if (g_uzip_parse_toc(sc, pp, gp) != 0) {
802303167Ssobomax		DPRINTF(GUZ_DBG_ERR, ("%s: TOC error\n", gp->name));
803303167Ssobomax		goto e6;
804303167Ssobomax	}
805133640Sfjoe	mtx_init(&sc->last_mtx, "geom_uzip cache", NULL, MTX_DEF);
806303167Ssobomax	mtx_init(&sc->queue_mtx, "geom_uzip wrkthread", NULL, MTX_DEF);
807303167Ssobomax	bioq_init(&sc->bio_queue);
808133640Sfjoe	sc->last_blk = -1;
809133640Sfjoe	sc->last_buf = malloc(sc->blksz, M_GEOM_UZIP, M_WAITOK);
810133640Sfjoe	sc->req_total = 0;
811133640Sfjoe	sc->req_cached = 0;
812133640Sfjoe
813303167Ssobomax	sc->uzip_do = &g_uzip_do;
814303167Ssobomax
815303167Ssobomax	error = kproc_create(g_uzip_wrkthr, sc, &sc->procp, 0, 0, "%s",
816303167Ssobomax	    gp->name);
817303167Ssobomax	if (error != 0) {
818303167Ssobomax		goto e7;
819303167Ssobomax	}
820303167Ssobomax
821133640Sfjoe	g_topology_lock();
822133640Sfjoe	pp2 = g_new_providerf(gp, "%s", gp->name);
823133640Sfjoe	pp2->sectorsize = 512;
824168445Ssimokawa	pp2->mediasize = (off_t)sc->nblocks * sc->blksz;
825266220Sloos	pp2->stripesize = pp->stripesize;
826266220Sloos	pp2->stripeoffset = pp->stripeoffset;
827133640Sfjoe	g_error_provider(pp2, 0);
828133640Sfjoe	g_access(cp, -1, 0, 0);
829133640Sfjoe
830303167Ssobomax	DPRINTF(GUZ_DBG_INFO, ("%s: taste ok (%d, %jd), (%d, %d), %x\n",
831303167Ssobomax	    gp->name, pp2->sectorsize, (intmax_t)pp2->mediasize,
832133640Sfjoe	    pp2->stripeoffset, pp2->stripesize, pp2->flags));
833303167Ssobomax	DPRINTF(GUZ_DBG_INFO, ("%s: %u x %u blocks\n", gp->name, sc->nblocks,
834303167Ssobomax	    sc->blksz));
835133640Sfjoe	return (gp);
836133640Sfjoe
837303167Ssobomaxe7:
838303167Ssobomax	free(sc->last_buf, M_GEOM);
839303167Ssobomax	mtx_destroy(&sc->queue_mtx);
840303167Ssobomax	mtx_destroy(&sc->last_mtx);
841303167Ssobomaxe6:
842303167Ssobomax	sc->dcp->free(sc->dcp);
843303167Ssobomaxe5:
844303167Ssobomax	free(sc->toc, M_GEOM);
845303167Ssobomaxe4:
846303167Ssobomax	free(gp->softc, M_GEOM_UZIP);
847303167Ssobomaxe3:
848303167Ssobomax	if (buf != NULL) {
849303167Ssobomax		free(buf, M_GEOM);
850303167Ssobomax	}
851303167Ssobomaxe2:
852133640Sfjoe	g_topology_lock();
853133640Sfjoe	g_access(cp, -1, 0, 0);
854303167Ssobomaxe1:
855133640Sfjoe	g_detach(cp);
856133640Sfjoe	g_destroy_consumer(cp);
857133640Sfjoe	g_destroy_geom(gp);
858266220Sloos
859133640Sfjoe	return (NULL);
860133640Sfjoe}
861133640Sfjoe
862133640Sfjoestatic int
863133640Sfjoeg_uzip_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
864133640Sfjoe{
865133640Sfjoe	struct g_provider *pp;
866133640Sfjoe
867343605Savos	KASSERT(gp != NULL, ("NULL geom"));
868266220Sloos	g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, gp->name);
869133640Sfjoe	g_topology_assert();
870133640Sfjoe
871133640Sfjoe	if (gp->softc == NULL) {
872303167Ssobomax		DPRINTF(GUZ_DBG_ERR, ("%s(%s): gp->softc == NULL\n", __func__,
873303167Ssobomax		    gp->name));
874133640Sfjoe		return (ENXIO);
875133640Sfjoe	}
876133640Sfjoe
877133640Sfjoe	pp = LIST_FIRST(&gp->provider);
878133640Sfjoe	KASSERT(pp != NULL, ("NULL provider"));
879133640Sfjoe	if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)
880133640Sfjoe		return (EBUSY);
881133640Sfjoe
882133640Sfjoe	g_uzip_softc_free(gp->softc, gp);
883133640Sfjoe	gp->softc = NULL;
884133640Sfjoe	g_wither_geom(gp, ENXIO);
885266220Sloos
886133640Sfjoe	return (0);
887133640Sfjoe}
888133640Sfjoe
889133640Sfjoestatic struct g_class g_uzip_class = {
890133640Sfjoe	.name = UZIP_CLASS_NAME,
891133640Sfjoe	.version = G_VERSION,
892133640Sfjoe	.taste = g_uzip_taste,
893133640Sfjoe	.destroy_geom = g_uzip_destroy_geom,
894133640Sfjoe
895133640Sfjoe	.start = g_uzip_start,
896133640Sfjoe	.orphan = g_uzip_orphan,
897133640Sfjoe	.access = g_uzip_access,
898133640Sfjoe	.spoiled = g_uzip_spoiled,
899133640Sfjoe};
900133640Sfjoe
901154686SfjoeDECLARE_GEOM_CLASS(g_uzip_class, g_uzip);
902154686SfjoeMODULE_DEPEND(g_uzip, zlib, 1, 1, 1);
903