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