1229537Sray/*-
2229537Sray * Copyright (c) 2010-2012 Aleksandr Rybalko
3229537Sray * Copyright (c) 2004 Max Khon
4229537Sray * All rights reserved.
5229537Sray *
6229537Sray * Redistribution and use in source and binary forms, with or without
7229537Sray * modification, are permitted provided that the following conditions
8229537Sray * are met:
9229537Sray * 1. Redistributions of source code must retain the above copyright
10229537Sray *    notice, this list of conditions and the following disclaimer.
11229537Sray * 2. Redistributions in binary form must reproduce the above copyright
12229537Sray *    notice, this list of conditions and the following disclaimer in the
13229537Sray *    documentation and/or other materials provided with the distribution.
14229537Sray *
15229537Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16229537Sray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17229537Sray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18229537Sray * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19229537Sray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20229537Sray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21229537Sray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22229537Sray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23229537Sray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24229537Sray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25229537Sray * SUCH DAMAGE.
26229537Sray */
27229537Sray
28229537Sray/*
29229537Sray * GEOM UNCOMPRESS module - simple decompression module for use with read-only
30229537Sray * copressed images maked by mkuzip(8) or mkulzma(8) utilities.
31229537Sray *
32229537Sray * To enable module in kernel config, put this line:
33229537Sray * device	geom_uncompress
34229537Sray */
35229537Sray
36229537Sray#include <sys/cdefs.h>
37229537Sray__FBSDID("$FreeBSD$");
38229537Sray
39229537Sray#include <sys/param.h>
40229537Sray#include <sys/bio.h>
41229537Sray#include <sys/endian.h>
42229537Sray#include <sys/errno.h>
43229537Sray#include <sys/kernel.h>
44229537Sray#include <sys/lock.h>
45229537Sray#include <sys/mutex.h>
46229537Sray#include <sys/malloc.h>
47229537Sray#include <sys/systm.h>
48229537Sray
49229537Sray#include <geom/geom.h>
50229537Sray
51229537Sray#include <net/zlib.h>
52229537Sray#include <contrib/xz-embedded/linux/include/linux/xz.h>
53229537Sray
54229537Sray#ifdef GEOM_UNCOMPRESS_DEBUG
55229537Sray#define	DPRINTF(a)	printf a
56229537Srayextern int g_debugflags;
57229537Sray#else
58229537Sray#define	DPRINTF(a)
59229537Sray#endif
60229537Sray
61229537Sraystatic MALLOC_DEFINE(M_GEOM_UNCOMPRESS, "geom_uncompress",
62229537Sray    "GEOM UNCOMPRESS data structures");
63229537Sray
64229537Sray#define	UNCOMPRESS_CLASS_NAME	"UNCOMPRESS"
65229537Sray#define	GEOM_UZIP_MAJVER '2'
66229537Sray#define	GEOM_ULZMA_MAJVER '3'
67229537Sray
68229537Sray/*
69229537Sray * Maximum allowed valid block size (to prevent foot-shooting)
70229537Sray */
71229537Sray#define	MAX_BLKSZ	(MAXPHYS)
72229537Sray
73229537Sray/*
74229537Sray * Integer values (block size, number of blocks, offsets)
75229537Sray * are stored in big-endian (network) order on disk and struct cloop_header
76229537Sray * and in native order in struct g_uncompress_softc
77229537Sray */
78229537Sray
79229537Sray#define	CLOOP_MAGIC_LEN	128
80229537Sraystatic char CLOOP_MAGIC_START[] = "#!/bin/sh\n";
81229537Sray
82229537Sraystruct cloop_header {
83229537Sray	char magic[CLOOP_MAGIC_LEN];	/* cloop magic */
84229537Sray	uint32_t blksz;			/* block size */
85229537Sray	uint32_t nblocks;		/* number of blocks */
86229537Sray};
87229537Sray
88229537Sraystruct g_uncompress_softc {
89229537Sray	uint32_t blksz;			/* block size */
90229537Sray	uint32_t nblocks;		/* number of blocks */
91229537Sray	uint64_t *offsets;
92229537Sray	enum {
93229537Sray		GEOM_UZIP = 1,
94229537Sray		GEOM_ULZMA
95229537Sray	} type;
96229537Sray
97229537Sray	struct mtx last_mtx;
98229537Sray	uint32_t last_blk;		/* last blk no */
99229537Sray	char *last_buf;			/* last blk data */
100229537Sray	int req_total;			/* total requests */
101229537Sray	int req_cached;			/* cached requests */
102229537Sray
103229537Sray	/* XZ decoder structs */
104229537Sray	struct xz_buf *b;
105229537Sray	struct xz_dec *s;
106229537Sray	z_stream *zs;
107229537Sray};
108229537Sray
109229537Sraystatic void
110229537Srayg_uncompress_softc_free(struct g_uncompress_softc *sc, struct g_geom *gp)
111229537Sray{
112229537Sray
113229537Sray	if (gp != NULL) {
114229537Sray		printf("%s: %d requests, %d cached\n",
115229537Sray		    gp->name, sc->req_total, sc->req_cached);
116229537Sray	}
117229537Sray	if (sc->offsets != NULL) {
118229537Sray		free(sc->offsets, M_GEOM_UNCOMPRESS);
119229537Sray		sc->offsets = 0;
120229537Sray	}
121229537Sray
122229537Sray	switch (sc->type) {
123229537Sray	case GEOM_ULZMA:
124229537Sray		if (sc->b) {
125229537Sray			free(sc->b, M_GEOM_UNCOMPRESS);
126229537Sray			sc->b = 0;
127229537Sray		}
128229537Sray		if (sc->s) {
129229537Sray			xz_dec_end(sc->s);
130229537Sray			sc->s = 0;
131229537Sray		}
132229537Sray		break;
133229537Sray	case GEOM_UZIP:
134229537Sray		if (sc->zs) {
135229537Sray			inflateEnd(sc->zs);
136229537Sray			free(sc->zs, M_GEOM_UNCOMPRESS);
137229537Sray			sc->zs = 0;
138229537Sray		}
139229537Sray		break;
140229537Sray	}
141229537Sray
142229537Sray	mtx_destroy(&sc->last_mtx);
143229537Sray	free(sc->last_buf, M_GEOM_UNCOMPRESS);
144229537Sray	free(sc, M_GEOM_UNCOMPRESS);
145229537Sray}
146229537Sray
147229537Sraystatic void *
148229537Srayz_alloc(void *nil, u_int type, u_int size)
149229537Sray{
150229537Sray	void *ptr;
151229537Sray
152229537Sray	ptr = malloc(type * size, M_GEOM_UNCOMPRESS, M_NOWAIT);
153229537Sray	return (ptr);
154229537Sray}
155229537Sray
156229537Sraystatic void
157229537Srayz_free(void *nil, void *ptr)
158229537Sray{
159229537Sray
160229537Sray	free(ptr, M_GEOM_UNCOMPRESS);
161229537Sray}
162229537Sray
163229537Sraystatic void
164229537Srayg_uncompress_done(struct bio *bp)
165229537Sray{
166229537Sray	struct g_uncompress_softc *sc;
167229537Sray	struct g_provider *pp, *pp2;
168229537Sray	struct g_consumer *cp;
169229537Sray	struct g_geom *gp;
170229537Sray	struct bio *bp2;
171229537Sray	uint32_t start_blk, i;
172229537Sray	off_t pos, upos;
173229537Sray	size_t bsize;
174229537Sray	int err;
175229537Sray
176229537Sray	err = 0;
177229537Sray	bp2 = bp->bio_parent;
178229537Sray	pp = bp2->bio_to;
179229537Sray	gp = pp->geom;
180229537Sray	cp = LIST_FIRST(&gp->consumer);
181229537Sray	pp2 = cp->provider;
182229537Sray	sc = gp->softc;
183229537Sray	DPRINTF(("%s: done\n", gp->name));
184229537Sray
185229537Sray	bp2->bio_error = bp->bio_error;
186229537Sray	if (bp2->bio_error != 0)
187229537Sray		goto done;
188229537Sray
189229537Sray	/*
190229537Sray	 * Example:
191229537Sray	 * Uncompressed block size = 65536
192229537Sray	 * User request: 65540-261632
193229537Sray	 * (4 uncompressed blocks -4B at start, -512B at end)
194229537Sray	 *
195229537Sray	 * We have 512(secsize)*63(nsec) = 32256B at offset 1024
196229537Sray	 * From:  1024  bp->bio_offset = 1024
197229537Sray	 * To:   33280  bp->bio_length = 33280 - 1024 = 32256
198229537Sray	 * Compressed blocks: 0-1020, 1020-1080, 1080-4845, 4845-12444,
199229537Sray	 * 	12444-33210, 33210-44100, ...
200229537Sray	 *
201229537Sray	 * Get start_blk from user request:
202229537Sray	 * start_blk = bp2->bio_offset / 65536 = 65540/65536 = 1
203229537Sray	 * bsize (block size of parent) = pp2->sectorsize (Now is 4B)
204229537Sray	 * pos(in stream from parent) = sc->offsets[start_blk] % bsize =
205229537Sray	 *    = sc->offsets[1] % 4 = 1020 % 4 = 0
206229537Sray	 */
207229537Sray
208229537Sray	/*
209229537Sray	 * Uncompress data.
210229537Sray	 */
211229537Sray	start_blk = bp2->bio_offset / sc->blksz;
212229537Sray	bsize = pp2->sectorsize;
213229537Sray	pos = sc->offsets[start_blk] % bsize;
214229537Sray	upos = 0;
215229537Sray
216229537Sray	DPRINTF(("%s: done: bio_length %lld bio_completed %lld start_blk %d, "
217229537Sray		"pos %lld, upos %lld (%lld, %d, %d)\n",
218229537Sray	    gp->name, bp->bio_length, bp->bio_completed, start_blk, pos, upos,
219229537Sray	    bp2->bio_offset, sc->blksz, bsize));
220229537Sray
221229537Sray	for (i = start_blk; upos < bp2->bio_length; i++) {
222229537Sray		off_t len, dlen, ulen, uoff;
223229537Sray
224229537Sray		uoff = i == start_blk ? bp2->bio_offset % sc->blksz : 0;
225229537Sray		ulen = MIN(sc->blksz - uoff, bp2->bio_length - upos);
226229537Sray		dlen = len = sc->offsets[i + 1] - sc->offsets[i];
227229537Sray
228229537Sray		DPRINTF(("%s: done: inflate block %d, start %lld, end %lld "
229229537Sray			"len %lld\n",
230229537Sray		    gp->name, i, sc->offsets[i], sc->offsets[i + 1], len));
231229537Sray
232229537Sray		if (len == 0) {
233229537Sray			/* All zero block: no cache update */
234229537Sray			bzero(bp2->bio_data + upos, ulen);
235229537Sray			upos += ulen;
236229537Sray			bp2->bio_completed += ulen;
237229537Sray			continue;
238229537Sray		}
239229537Sray
240229537Sray		mtx_lock(&sc->last_mtx);
241229537Sray
242229537Sray#ifdef GEOM_UNCOMPRESS_DEBUG
243229537Sray		if (g_debugflags & 32)
244229537Sray			hexdump(bp->bio_data + pos, dlen, 0, 0);
245229537Sray#endif
246229537Sray
247229537Sray		switch (sc->type) {
248229537Sray		case GEOM_ULZMA:
249229537Sray			sc->b->in = bp->bio_data + pos;
250229537Sray			sc->b->out = sc->last_buf;
251229537Sray			sc->b->in_pos = sc->b->out_pos = 0;
252229537Sray			sc->b->in_size = dlen;
253229537Sray			sc->b->out_size = (size_t)-1;
254229537Sray
255229537Sray			err = (xz_dec_run(sc->s, sc->b) != XZ_STREAM_END) ?
256229537Sray			    1 : 0;
257229537Sray			/* TODO decoder recovery, if needed */
258229537Sray			break;
259229537Sray		case GEOM_UZIP:
260229537Sray			sc->zs->next_in = bp->bio_data + pos;
261229537Sray			sc->zs->avail_in = dlen;
262229537Sray			sc->zs->next_out = sc->last_buf;
263229537Sray			sc->zs->avail_out = sc->blksz;
264229537Sray
265229537Sray			err = (inflate(sc->zs, Z_FINISH) != Z_STREAM_END) ?
266229537Sray			    1 : 0;
267229537Sray			if ((err) && (inflateReset(sc->zs) != Z_OK))
268229537Sray				printf("%s: UZIP decoder reset failed\n",
269229537Sray				     gp->name);
270229537Sray			break;
271229537Sray		}
272229537Sray
273229537Sray		if (err) {
274229537Sray			sc->last_blk = -1;
275229537Sray			mtx_unlock(&sc->last_mtx);
276229537Sray			DPRINTF(("%s: done: inflate failed, code=%d\n",
277229537Sray			    gp->name, err));
278229537Sray			bp2->bio_error = EIO;
279229537Sray			goto done;
280229537Sray		}
281229537Sray
282229537Sray#ifdef GEOM_UNCOMPRESS_DEBUG
283229537Sray		if (g_debugflags & 32)
284229537Sray			hexdump(sc->last_buf, sc->b->out_size, 0, 0);
285229537Sray#endif
286229537Sray
287229537Sray		sc->last_blk = i;
288229537Sray		DPRINTF(("%s: done: inflated \n", gp->name));
289229537Sray		memcpy(bp2->bio_data + upos, sc->last_buf + uoff, ulen);
290229537Sray		mtx_unlock(&sc->last_mtx);
291229537Sray
292229537Sray		pos += len;
293229537Sray		upos += ulen;
294229537Sray		bp2->bio_completed += ulen;
295229537Sray	}
296229537Sray
297229537Sraydone:
298229537Sray	/*
299229537Sray	 * Finish processing the request.
300229537Sray	 */
301229537Sray	DPRINTF(("%s: done: (%d, %lld, %ld)\n",
302229537Sray	    gp->name, bp2->bio_error, bp2->bio_completed, bp2->bio_resid));
303229537Sray	free(bp->bio_data, M_GEOM_UNCOMPRESS);
304229537Sray	g_destroy_bio(bp);
305229537Sray	g_io_deliver(bp2, bp2->bio_error);
306229537Sray}
307229537Sray
308229537Sraystatic void
309229537Srayg_uncompress_start(struct bio *bp)
310229537Sray{
311229537Sray	struct g_uncompress_softc *sc;
312229537Sray	struct g_provider *pp, *pp2;
313229537Sray	struct g_consumer *cp;
314229537Sray	struct g_geom *gp;
315229537Sray	struct bio *bp2;
316229537Sray	uint32_t start_blk, end_blk;
317229537Sray	size_t bsize;
318229537Sray
319229537Sray
320229537Sray	pp = bp->bio_to;
321229537Sray	gp = pp->geom;
322229537Sray	DPRINTF(("%s: start (%s) to %s off=%lld len=%lld\n", gp->name,
323229537Sray		(bp->bio_cmd==BIO_READ) ? "BIO_READ" : "BIO_WRITE*",
324229537Sray		pp->name, bp->bio_offset, bp->bio_length));
325229537Sray
326229537Sray	if (bp->bio_cmd != BIO_READ) {
327229537Sray		g_io_deliver(bp, EOPNOTSUPP);
328229537Sray		return;
329229537Sray	}
330229537Sray
331229537Sray	cp = LIST_FIRST(&gp->consumer);
332229537Sray	pp2 = cp->provider;
333229537Sray	sc = gp->softc;
334229537Sray
335229537Sray	start_blk = bp->bio_offset / sc->blksz;
336229537Sray	end_blk   = howmany(bp->bio_offset + bp->bio_length, sc->blksz);
337229537Sray	KASSERT(start_blk < sc->nblocks,
338229537Sray		("start_blk out of range"));
339229537Sray	KASSERT(end_blk <= sc->nblocks,
340229537Sray		("end_blk out of range"));
341229537Sray
342229537Sray	sc->req_total++;
343229537Sray	if (start_blk + 1 == end_blk) {
344229537Sray		mtx_lock(&sc->last_mtx);
345229537Sray		if (start_blk == sc->last_blk) {
346229537Sray			off_t uoff;
347229537Sray
348229537Sray			uoff = bp->bio_offset % sc->blksz;
349229537Sray			KASSERT(bp->bio_length <= sc->blksz - uoff,
350229537Sray			    ("cached data error"));
351229537Sray			memcpy(bp->bio_data, sc->last_buf + uoff,
352229537Sray			    bp->bio_length);
353229537Sray			sc->req_cached++;
354229537Sray			mtx_unlock(&sc->last_mtx);
355229537Sray
356229537Sray			DPRINTF(("%s: start: cached 0 + %lld, "
357229537Sray			    "%lld + 0 + %lld\n",
358229537Sray			    gp->name, bp->bio_length, uoff, bp->bio_length));
359229537Sray			bp->bio_completed = bp->bio_length;
360229537Sray			g_io_deliver(bp, 0);
361229537Sray			return;
362229537Sray		}
363229537Sray		mtx_unlock(&sc->last_mtx);
364229537Sray	}
365229537Sray
366229537Sray	bp2 = g_clone_bio(bp);
367229537Sray	if (bp2 == NULL) {
368229537Sray		g_io_deliver(bp, ENOMEM);
369229537Sray		return;
370229537Sray	}
371229537Sray	DPRINTF(("%s: start (%d..%d), %s: %d + %llu, %s: %d + %llu\n",
372229537Sray	    gp->name, start_blk, end_blk,
373229537Sray	    pp->name, pp->sectorsize, pp->mediasize,
374229537Sray	    pp2->name, pp2->sectorsize, pp2->mediasize));
375229537Sray
376229537Sray	bsize = pp2->sectorsize;
377229537Sray
378229537Sray	bp2->bio_done = g_uncompress_done;
379229537Sray	bp2->bio_offset = rounddown(sc->offsets[start_blk],bsize);
380229537Sray	bp2->bio_length = roundup(sc->offsets[end_blk],bsize) -
381229537Sray	    bp2->bio_offset;
382229537Sray	bp2->bio_data = malloc(bp2->bio_length, M_GEOM_UNCOMPRESS, M_NOWAIT);
383229537Sray
384229537Sray	DPRINTF(("%s: start %lld + %lld -> %lld + %lld -> %lld + %lld\n",
385229537Sray	    gp->name,
386229537Sray	    bp->bio_offset, bp->bio_length,
387229537Sray	    sc->offsets[start_blk],
388229537Sray	    sc->offsets[end_blk] - sc->offsets[start_blk],
389229537Sray	    bp2->bio_offset, bp2->bio_length));
390229537Sray
391229537Sray	if (bp2->bio_data == NULL) {
392229537Sray		g_destroy_bio(bp2);
393229537Sray		g_io_deliver(bp, ENOMEM);
394229537Sray		return;
395229537Sray	}
396229537Sray
397229537Sray	g_io_request(bp2, cp);
398229537Sray	DPRINTF(("%s: start ok\n", gp->name));
399229537Sray}
400229537Sray
401229537Sraystatic void
402229537Srayg_uncompress_orphan(struct g_consumer *cp)
403229537Sray{
404229537Sray	struct g_geom *gp;
405229537Sray
406229537Sray	g_trace(G_T_TOPOLOGY, "%s(%p/%s)", __func__, cp,
407229537Sray		cp->provider->name);
408229537Sray	g_topology_assert();
409229537Sray
410229537Sray	gp = cp->geom;
411229537Sray	g_uncompress_softc_free(gp->softc, gp);
412229537Sray	gp->softc = NULL;
413249148Smav	g_wither_geom(gp, ENXIO);
414229537Sray}
415229537Sray
416229537Sraystatic int
417229537Srayg_uncompress_access(struct g_provider *pp, int dr, int dw, int de)
418229537Sray{
419229537Sray	struct g_consumer *cp;
420229537Sray	struct g_geom *gp;
421229537Sray
422229537Sray	gp = pp->geom;
423229537Sray	cp = LIST_FIRST(&gp->consumer);
424229537Sray	KASSERT (cp != NULL, ("g_uncompress_access but no consumer"));
425229537Sray
426229537Sray	if (cp->acw + dw > 0)
427229537Sray		return (EROFS);
428229537Sray
429229537Sray	return (g_access(cp, dr, dw, de));
430229537Sray}
431229537Sray
432229537Sraystatic void
433229537Srayg_uncompress_spoiled(struct g_consumer *cp)
434229537Sray{
435229537Sray	struct g_geom *gp;
436229537Sray
437229537Sray	gp = cp->geom;
438229537Sray	g_trace(G_T_TOPOLOGY, "%s(%p/%s)", __func__, cp, gp->name);
439229537Sray	g_topology_assert();
440229537Sray
441229537Sray	g_uncompress_softc_free(gp->softc, gp);
442229537Sray	gp->softc = NULL;
443229537Sray	g_wither_geom(gp, ENXIO);
444229537Sray}
445229537Sray
446229537Sraystatic struct g_geom *
447229537Srayg_uncompress_taste(struct g_class *mp, struct g_provider *pp, int flags)
448229537Sray{
449229537Sray	struct cloop_header *header;
450229537Sray	struct g_uncompress_softc *sc;
451229537Sray	struct g_provider *pp2;
452229537Sray	struct g_consumer *cp;
453229537Sray	struct g_geom *gp;
454229537Sray	uint32_t i, total_offsets, offsets_read, type;
455229537Sray	uint8_t *buf;
456229537Sray	int error;
457229537Sray
458229537Sray	g_trace(G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name);
459229537Sray	g_topology_assert();
460229537Sray
461229537Sray	/* Skip providers that are already open for writing. */
462229537Sray	if (pp->acw > 0)
463229537Sray		return (NULL);
464229537Sray
465229537Sray	buf = NULL;
466229537Sray
467229537Sray	/*
468229537Sray	 * Create geom instance.
469229537Sray	 */
470229537Sray	gp = g_new_geomf(mp, "%s.uncompress", pp->name);
471229537Sray	cp = g_new_consumer(gp);
472229537Sray	error = g_attach(cp, pp);
473229537Sray	if (error == 0)
474229537Sray		error = g_access(cp, 1, 0, 0);
475229537Sray	if (error) {
476229537Sray		g_detach(cp);
477229537Sray		g_destroy_consumer(cp);
478229537Sray		g_destroy_geom(gp);
479229537Sray		return (NULL);
480229537Sray	}
481229537Sray	g_topology_unlock();
482229537Sray
483229537Sray	/*
484229537Sray	 * Read cloop header, look for CLOOP magic, perform
485229537Sray	 * other validity checks.
486229537Sray	 */
487229537Sray	DPRINTF(("%s: media sectorsize %u, mediasize %lld\n",
488229537Sray	    gp->name, pp->sectorsize, pp->mediasize));
489229537Sray
490229537Sray	i = roundup(sizeof(struct cloop_header), pp->sectorsize);
491229537Sray	buf = g_read_data(cp, 0, i, NULL);
492229537Sray	if (buf == NULL)
493229537Sray		goto err;
494229537Sray
495229537Sray	header = (struct cloop_header *) buf;
496229537Sray	if (strncmp(header->magic, CLOOP_MAGIC_START,
497229537Sray		    sizeof(CLOOP_MAGIC_START) - 1) != 0) {
498229537Sray		DPRINTF(("%s: no CLOOP magic\n", gp->name));
499229537Sray		goto err;
500229537Sray	}
501229537Sray
502229537Sray	switch (header->magic[0x0b]) {
503229537Sray	case 'L':
504229537Sray		type = GEOM_ULZMA;
505229537Sray		if (header->magic[0x0c] < GEOM_ULZMA_MAJVER) {
506229537Sray			DPRINTF(("%s: image version too old\n", gp->name));
507229537Sray			goto err;
508229537Sray		}
509229537Sray		printf("%s: GEOM_ULZMA image found\n", gp->name);
510229537Sray		break;
511229537Sray	case 'V':
512229537Sray		type = GEOM_UZIP;
513229537Sray		if (header->magic[0x0c] < GEOM_UZIP_MAJVER) {
514229537Sray			DPRINTF(("%s: image version too old\n", gp->name));
515229537Sray			goto err;
516229537Sray		}
517229537Sray		printf("%s: GEOM_UZIP image found\n", gp->name);
518229537Sray		break;
519229537Sray	default:
520229537Sray		DPRINTF(("%s: unsupported image type\n", gp->name));
521229537Sray		goto err;
522229537Sray	}
523229537Sray
524229537Sray	DPRINTF(("%s: found CLOOP magic\n", gp->name));
525229537Sray	/*
526229537Sray	 * Initialize softc and read offsets.
527229537Sray	 */
528229537Sray	sc = malloc(sizeof(*sc), M_GEOM_UNCOMPRESS, M_WAITOK | M_ZERO);
529229537Sray	gp->softc = sc;
530229537Sray	sc->type = type;
531229537Sray	sc->blksz = ntohl(header->blksz);
532229537Sray	sc->nblocks = ntohl(header->nblocks);
533229537Sray	if (sc->blksz % 4 != 0) {
534229537Sray		printf("%s: block size (%u) should be multiple of 4.\n",
535229537Sray		    gp->name, sc->blksz);
536229537Sray		goto err;
537229537Sray	}
538229537Sray	if (sc->blksz > MAX_BLKSZ) {
539229537Sray		printf("%s: block size (%u) should not be larger than %d.\n",
540229537Sray		    gp->name, sc->blksz, MAX_BLKSZ);
541229537Sray	}
542229537Sray	total_offsets = sc->nblocks + 1;
543229537Sray	if (sizeof(struct cloop_header) +
544229537Sray	    total_offsets * sizeof(uint64_t) > pp->mediasize) {
545229537Sray		printf("%s: media too small for %u blocks\n",
546229537Sray		    gp->name, sc->nblocks);
547229537Sray		goto err;
548229537Sray	}
549229537Sray	sc->offsets = malloc(
550229537Sray	    total_offsets * sizeof(uint64_t), M_GEOM_UNCOMPRESS, M_WAITOK);
551229537Sray	offsets_read = MIN(total_offsets,
552229537Sray	    (pp->sectorsize - sizeof(*header)) / sizeof(uint64_t));
553229537Sray	for (i = 0; i < offsets_read; i++)
554229537Sray		sc->offsets[i] = be64toh(((uint64_t *) (header + 1))[i]);
555229537Sray	DPRINTF(("%s: %u offsets in the first sector\n",
556229537Sray	    gp->name, offsets_read));
557229537Sray
558229537Sray	free(buf, M_GEOM);
559229537Sray	i = roundup((sizeof(struct cloop_header) +
560229537Sray		total_offsets * sizeof(uint64_t)),pp->sectorsize);
561229537Sray	buf = g_read_data(cp, 0, i, NULL);
562229537Sray	if (buf == NULL)
563229537Sray		goto err;
564229537Sray	for (i = 0; i <= total_offsets; i++) {
565229537Sray		sc->offsets[i] = be64toh(((uint64_t *)
566229537Sray		    (buf+sizeof(struct cloop_header)))[i]);
567229537Sray	}
568229537Sray	DPRINTF(("%s: done reading offsets\n", gp->name));
569229537Sray	mtx_init(&sc->last_mtx, "geom_uncompress cache", NULL, MTX_DEF);
570229537Sray	sc->last_blk = -1;
571229537Sray	sc->last_buf = malloc(sc->blksz, M_GEOM_UNCOMPRESS, M_WAITOK);
572229537Sray	sc->req_total = 0;
573229537Sray	sc->req_cached = 0;
574229537Sray
575229537Sray	switch (sc->type) {
576229537Sray	case GEOM_ULZMA:
577229537Sray		xz_crc32_init();
578229537Sray		sc->s = xz_dec_init(XZ_SINGLE, 0);
579229537Sray		sc->b = (struct xz_buf*)malloc(sizeof(struct xz_buf),
580229537Sray		    M_GEOM_UNCOMPRESS, M_WAITOK);
581229537Sray		break;
582229537Sray	case GEOM_UZIP:
583229537Sray		sc->zs = (z_stream *)malloc(sizeof(z_stream),
584229537Sray		    M_GEOM_UNCOMPRESS, M_WAITOK);
585229537Sray		sc->zs->zalloc = z_alloc;
586229537Sray		sc->zs->zfree = z_free;
587229537Sray		if (inflateInit(sc->zs) != Z_OK) {
588229537Sray			goto err;
589229537Sray		}
590229537Sray		break;
591229537Sray	}
592229537Sray
593229537Sray	g_topology_lock();
594229537Sray	pp2 = g_new_providerf(gp, "%s", gp->name);
595229537Sray	pp2->sectorsize = 512;
596229537Sray	pp2->mediasize = (off_t)sc->nblocks * sc->blksz;
597229537Sray	pp2->flags = pp->flags & G_PF_CANDELETE;
598229537Sray	if (pp->stripesize > 0) {
599229537Sray		pp2->stripesize = pp->stripesize;
600229537Sray		pp2->stripeoffset = pp->stripeoffset;
601229537Sray	}
602229537Sray	g_error_provider(pp2, 0);
603229537Sray	free(buf, M_GEOM);
604229537Sray	g_access(cp, -1, 0, 0);
605229537Sray
606229537Sray	DPRINTF(("%s: taste ok (%d, %lld), (%d, %d), %x\n",
607229537Sray	    gp->name,
608229537Sray	    pp2->sectorsize, pp2->mediasize,
609229537Sray	    pp2->stripeoffset, pp2->stripesize, pp2->flags));
610229537Sray	printf("%s: %u x %u blocks\n",
611229537Sray	    gp->name, sc->nblocks, sc->blksz);
612229537Sray	return (gp);
613229537Sray
614229537Srayerr:
615229537Sray	g_topology_lock();
616229537Sray	g_access(cp, -1, 0, 0);
617229537Sray	if (buf != NULL)
618229537Sray		free(buf, M_GEOM);
619229537Sray	if (gp->softc != NULL) {
620229537Sray		g_uncompress_softc_free(gp->softc, NULL);
621229537Sray		gp->softc = NULL;
622229537Sray	}
623229537Sray	g_detach(cp);
624229537Sray	g_destroy_consumer(cp);
625229537Sray	g_destroy_geom(gp);
626229537Sray	return (NULL);
627229537Sray}
628229537Sray
629229537Sraystatic int
630229537Srayg_uncompress_destroy_geom(struct gctl_req *req, struct g_class *mp,
631229537Sray			  struct g_geom *gp)
632229537Sray{
633229537Sray	struct g_provider *pp;
634229537Sray
635229537Sray	g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, gp->name);
636229537Sray	g_topology_assert();
637229537Sray
638229537Sray	if (gp->softc == NULL) {
639229537Sray		printf("%s(%s): gp->softc == NULL\n", __func__, gp->name);
640229537Sray		return (ENXIO);
641229537Sray	}
642229537Sray
643229537Sray	KASSERT(gp != NULL, ("NULL geom"));
644229537Sray	pp = LIST_FIRST(&gp->provider);
645229537Sray	KASSERT(pp != NULL, ("NULL provider"));
646229537Sray	if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)
647229537Sray		return (EBUSY);
648229537Sray
649229537Sray	g_uncompress_softc_free(gp->softc, gp);
650229537Sray	gp->softc = NULL;
651229537Sray	g_wither_geom(gp, ENXIO);
652229537Sray	return (0);
653229537Sray}
654229537Sray
655229537Sraystatic struct g_class g_uncompress_class = {
656229537Sray	.name = UNCOMPRESS_CLASS_NAME,
657229537Sray	.version = G_VERSION,
658229537Sray	.taste = g_uncompress_taste,
659229537Sray	.destroy_geom = g_uncompress_destroy_geom,
660229537Sray
661229537Sray	.start = g_uncompress_start,
662229537Sray	.orphan = g_uncompress_orphan,
663229537Sray	.access = g_uncompress_access,
664229537Sray	.spoiled = g_uncompress_spoiled,
665229537Sray};
666229537Sray
667229537SrayDECLARE_GEOM_CLASS(g_uncompress_class, g_uncompress);
668229537Sray
669