1 /*- 2 * Copyright (c) 2004 Max Khon 3 * Copyright (c) 2014 Juniper Networks, Inc. 4 * Copyright (c) 2006-2016 Maxim Sobolev <sobomax@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/bio.h> 34 #include <sys/endian.h> 35 #include <sys/errno.h> 36 #include <sys/kernel.h> 37 #include <sys/lock.h> 38 #include <sys/mutex.h> 39 #include <sys/malloc.h> 40 #include <sys/sysctl.h> 41 #include <sys/systm.h> 42 #include <sys/kthread.h> 43 44 #include <geom/geom.h> 45 46 #include <geom/uzip/g_uzip.h> 47 #include <geom/uzip/g_uzip_cloop.h> 48 #include <geom/uzip/g_uzip_softc.h> 49 #include <geom/uzip/g_uzip_dapi.h> 50 #include <geom/uzip/g_uzip_zlib.h> 51 #include <geom/uzip/g_uzip_lzma.h> 52 #include <geom/uzip/g_uzip_wrkthr.h> 53 54 #include "opt_geom.h" 55 56 MALLOC_DEFINE(M_GEOM_UZIP, "geom_uzip", "GEOM UZIP data structures"); 57 58 FEATURE(geom_uzip, "GEOM read-only compressed disks support"); 59 60 struct g_uzip_blk { 61 uint64_t offset; 62 uint32_t blen; 63 #define BLEN_UNDEF UINT32_MAX 64 }; 65 66 #ifndef ABS 67 #define ABS(a) ((a) < 0 ? -(a) : (a)) 68 #endif 69 70 #define BLK_IN_RANGE(mcn, bcn, ilen) \ 71 (((bcn) != BLEN_UNDEF) && ( \ 72 ((ilen) >= 0 && (mcn >= bcn) && (mcn <= ((intmax_t)(bcn) + (ilen)))) || \ 73 ((ilen) < 0 && (mcn <= bcn) && (mcn >= ((intmax_t)(bcn) + (ilen)))) \ 74 )) 75 76 #ifdef GEOM_UZIP_DEBUG 77 # define GEOM_UZIP_DBG_DEFAULT 3 78 #else 79 # define GEOM_UZIP_DBG_DEFAULT 0 80 #endif 81 82 #define GUZ_DBG_ERR 1 83 #define GUZ_DBG_INFO 2 84 #define GUZ_DBG_IO 3 85 #define GUZ_DBG_TOC 4 86 87 SYSCTL_DECL(_kern_geom); 88 SYSCTL_NODE(_kern_geom, OID_AUTO, uzip, CTLFLAG_RW, 0, "GEOM_UZIP stuff"); 89 static u_int g_uzip_debug = GEOM_UZIP_DBG_DEFAULT; 90 SYSCTL_UINT(_kern_geom_uzip, OID_AUTO, debug, CTLFLAG_RWTUN, &g_uzip_debug, 0, 91 "Debug level (0-4)"); 92 static u_int g_uzip_debug_block = BLEN_UNDEF; 93 SYSCTL_UINT(_kern_geom_uzip, OID_AUTO, debug_block, CTLFLAG_RWTUN, 94 &g_uzip_debug_block, 0, "Debug operations around specific cluster#"); 95 96 #define DPRINTF(lvl, a) \ 97 if ((lvl) <= g_uzip_debug) { \ 98 printf a; \ 99 } 100 #define DPRINTF_BLK(lvl, cn, a) \ 101 if ((lvl) <= g_uzip_debug || \ 102 BLK_IN_RANGE(cn, g_uzip_debug_block, 8) || \ 103 BLK_IN_RANGE(cn, g_uzip_debug_block, -8)) { \ 104 printf a; \ 105 } 106 #define DPRINTF_BRNG(lvl, bcn, ecn, a) \ 107 KASSERT(bcn < ecn, ("DPRINTF_BRNG: invalid range (%ju, %ju)", \ 108 (uintmax_t)bcn, (uintmax_t)ecn)); \ 109 if (((lvl) <= g_uzip_debug) || \ 110 BLK_IN_RANGE(g_uzip_debug_block, bcn, \ 111 (intmax_t)ecn - (intmax_t)bcn)) { \ 112 printf a; \ 113 } 114 115 #define UZIP_CLASS_NAME "UZIP" 116 117 /* 118 * Maximum allowed valid block size (to prevent foot-shooting) 119 */ 120 #define MAX_BLKSZ (MAXPHYS) 121 122 static char CLOOP_MAGIC_START[] = "#!/bin/sh\n"; 123 124 static void g_uzip_read_done(struct bio *bp); 125 static void g_uzip_do(struct g_uzip_softc *, struct bio *bp); 126 127 static void 128 g_uzip_softc_free(struct g_uzip_softc *sc, struct g_geom *gp) 129 { 130 131 if (gp != NULL) { 132 DPRINTF(GUZ_DBG_INFO, ("%s: %d requests, %d cached\n", 133 gp->name, sc->req_total, sc->req_cached)); 134 } 135 136 mtx_lock(&sc->queue_mtx); 137 sc->wrkthr_flags |= GUZ_SHUTDOWN; 138 wakeup(sc); 139 while (!(sc->wrkthr_flags & GUZ_EXITING)) { 140 msleep(sc->procp, &sc->queue_mtx, PRIBIO, "guzfree", 141 hz / 10); 142 } 143 mtx_unlock(&sc->queue_mtx); 144 145 sc->dcp->free(sc->dcp); 146 free(sc->toc, M_GEOM_UZIP); 147 mtx_destroy(&sc->queue_mtx); 148 mtx_destroy(&sc->last_mtx); 149 free(sc->last_buf, M_GEOM_UZIP); 150 free(sc, M_GEOM_UZIP); 151 } 152 153 static int 154 g_uzip_cached(struct g_geom *gp, struct bio *bp) 155 { 156 struct g_uzip_softc *sc; 157 off_t ofs; 158 size_t blk, blkofs, usz; 159 160 sc = gp->softc; 161 ofs = bp->bio_offset + bp->bio_completed; 162 blk = ofs / sc->blksz; 163 mtx_lock(&sc->last_mtx); 164 if (blk == sc->last_blk) { 165 blkofs = ofs % sc->blksz; 166 usz = sc->blksz - blkofs; 167 if (bp->bio_resid < usz) 168 usz = bp->bio_resid; 169 memcpy(bp->bio_data + bp->bio_completed, sc->last_buf + blkofs, 170 usz); 171 sc->req_cached++; 172 mtx_unlock(&sc->last_mtx); 173 174 DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: offset=%jd: got %jd bytes " 175 "from cache\n", __func__, gp->name, bp, (intmax_t)ofs, 176 (intmax_t)usz)); 177 178 bp->bio_completed += usz; 179 bp->bio_resid -= usz; 180 181 if (bp->bio_resid == 0) { 182 g_io_deliver(bp, 0); 183 return (1); 184 } 185 } else 186 mtx_unlock(&sc->last_mtx); 187 188 return (0); 189 } 190 191 #define BLK_ENDS(sc, bi) ((sc)->toc[(bi)].offset + \ 192 (sc)->toc[(bi)].blen) 193 194 #define BLK_IS_CONT(sc, bi) (BLK_ENDS((sc), (bi) - 1) == \ 195 (sc)->toc[(bi)].offset) 196 #define BLK_IS_NIL(sc, bi) ((sc)->toc[(bi)].blen == 0) 197 198 #define TOFF_2_BOFF(sc, pp, bi) ((sc)->toc[(bi)].offset - \ 199 (sc)->toc[(bi)].offset % (pp)->sectorsize) 200 #define TLEN_2_BLEN(sc, pp, bp, ei) roundup(BLK_ENDS((sc), (ei)) - \ 201 (bp)->bio_offset, (pp)->sectorsize) 202 203 static int 204 g_uzip_request(struct g_geom *gp, struct bio *bp) 205 { 206 struct g_uzip_softc *sc; 207 struct bio *bp2; 208 struct g_consumer *cp; 209 struct g_provider *pp; 210 off_t ofs, start_blk_ofs; 211 size_t i, start_blk, end_blk, zsize; 212 213 if (g_uzip_cached(gp, bp) != 0) 214 return (1); 215 216 sc = gp->softc; 217 218 cp = LIST_FIRST(&gp->consumer); 219 pp = cp->provider; 220 221 ofs = bp->bio_offset + bp->bio_completed; 222 start_blk = ofs / sc->blksz; 223 KASSERT(start_blk < sc->nblocks, ("start_blk out of range")); 224 end_blk = howmany(ofs + bp->bio_resid, sc->blksz); 225 KASSERT(end_blk <= sc->nblocks, ("end_blk out of range")); 226 227 for (; BLK_IS_NIL(sc, start_blk) && start_blk < end_blk; start_blk++) { 228 /* Fill in any leading Nil blocks */ 229 start_blk_ofs = ofs % sc->blksz; 230 zsize = MIN(sc->blksz - start_blk_ofs, bp->bio_resid); 231 DPRINTF_BLK(GUZ_DBG_IO, start_blk, ("%s/%s: %p/%ju: " 232 "filling %ju zero bytes\n", __func__, gp->name, gp, 233 (uintmax_t)bp->bio_completed, (uintmax_t)zsize)); 234 bzero(bp->bio_data + bp->bio_completed, zsize); 235 bp->bio_completed += zsize; 236 bp->bio_resid -= zsize; 237 ofs += zsize; 238 } 239 240 if (start_blk == end_blk) { 241 KASSERT(bp->bio_resid == 0, ("bp->bio_resid is invalid")); 242 /* 243 * No non-Nil data is left, complete request immediately. 244 */ 245 DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: all done returning %ju " 246 "bytes\n", __func__, gp->name, gp, 247 (uintmax_t)bp->bio_completed)); 248 g_io_deliver(bp, 0); 249 return (1); 250 } 251 252 for (i = start_blk + 1; i < end_blk; i++) { 253 /* Trim discontinuous areas if any */ 254 if (!BLK_IS_CONT(sc, i)) { 255 end_blk = i; 256 break; 257 } 258 } 259 260 DPRINTF_BRNG(GUZ_DBG_IO, start_blk, end_blk, ("%s/%s: %p: " 261 "start=%u (%ju), end=%u (%ju)\n", __func__, gp->name, bp, 262 (u_int)start_blk, (uintmax_t)sc->toc[start_blk].offset, 263 (u_int)end_blk, (uintmax_t)BLK_ENDS(sc, end_blk - 1))); 264 265 bp2 = g_clone_bio(bp); 266 if (bp2 == NULL) { 267 g_io_deliver(bp, ENOMEM); 268 return (1); 269 } 270 bp2->bio_done = g_uzip_read_done; 271 272 bp2->bio_offset = TOFF_2_BOFF(sc, pp, start_blk); 273 while (1) { 274 bp2->bio_length = TLEN_2_BLEN(sc, pp, bp2, end_blk - 1); 275 if (bp2->bio_length <= MAXPHYS) 276 break; 277 if (end_blk == (start_blk + 1)) { 278 break; 279 } 280 end_blk--; 281 } 282 283 DPRINTF(GUZ_DBG_IO, ("%s/%s: bp2->bio_length = %jd\n", 284 __func__, gp->name, (intmax_t)bp2->bio_length)); 285 286 bp2->bio_data = malloc(bp2->bio_length, M_GEOM_UZIP, M_NOWAIT); 287 if (bp2->bio_data == NULL) { 288 g_destroy_bio(bp2); 289 g_io_deliver(bp, ENOMEM); 290 return (1); 291 } 292 293 DPRINTF_BRNG(GUZ_DBG_IO, start_blk, end_blk, ("%s/%s: %p: " 294 "reading %jd bytes from offset %jd\n", __func__, gp->name, bp, 295 (intmax_t)bp2->bio_length, (intmax_t)bp2->bio_offset)); 296 297 g_io_request(bp2, cp); 298 return (0); 299 } 300 301 static void 302 g_uzip_read_done(struct bio *bp) 303 { 304 struct bio *bp2; 305 struct g_geom *gp; 306 struct g_uzip_softc *sc; 307 308 bp2 = bp->bio_parent; 309 gp = bp2->bio_to->geom; 310 sc = gp->softc; 311 312 mtx_lock(&sc->queue_mtx); 313 bioq_disksort(&sc->bio_queue, bp); 314 mtx_unlock(&sc->queue_mtx); 315 wakeup(sc); 316 } 317 318 static void 319 g_uzip_do(struct g_uzip_softc *sc, struct bio *bp) 320 { 321 struct bio *bp2; 322 struct g_provider *pp; 323 struct g_consumer *cp; 324 struct g_geom *gp; 325 char *data, *data2; 326 off_t ofs; 327 size_t blk, blkofs, len, ulen, firstblk; 328 int err; 329 330 bp2 = bp->bio_parent; 331 gp = bp2->bio_to->geom; 332 333 cp = LIST_FIRST(&gp->consumer); 334 pp = cp->provider; 335 336 bp2->bio_error = bp->bio_error; 337 if (bp2->bio_error != 0) 338 goto done; 339 340 /* Make sure there's forward progress. */ 341 if (bp->bio_completed == 0) { 342 bp2->bio_error = ECANCELED; 343 goto done; 344 } 345 346 ofs = bp2->bio_offset + bp2->bio_completed; 347 firstblk = blk = ofs / sc->blksz; 348 blkofs = ofs % sc->blksz; 349 data = bp->bio_data + sc->toc[blk].offset % pp->sectorsize; 350 data2 = bp2->bio_data + bp2->bio_completed; 351 while (bp->bio_completed && bp2->bio_resid) { 352 if (blk > firstblk && !BLK_IS_CONT(sc, blk)) { 353 DPRINTF_BLK(GUZ_DBG_IO, blk, ("%s/%s: %p: backref'ed " 354 "cluster #%u requested, looping around\n", 355 __func__, gp->name, bp2, (u_int)blk)); 356 goto done; 357 } 358 ulen = MIN(sc->blksz - blkofs, bp2->bio_resid); 359 len = sc->toc[blk].blen; 360 DPRINTF(GUZ_DBG_IO, ("%s/%s: %p/%ju: data2=%p, ulen=%u, " 361 "data=%p, len=%u\n", __func__, gp->name, gp, 362 bp->bio_completed, data2, (u_int)ulen, data, (u_int)len)); 363 if (len == 0) { 364 /* All zero block: no cache update */ 365 bzero(data2, ulen); 366 } else if (len <= bp->bio_completed) { 367 mtx_lock(&sc->last_mtx); 368 err = sc->dcp->decompress(sc->dcp, gp->name, data, 369 len, sc->last_buf); 370 if (err != 0) { 371 sc->last_blk = -1; 372 mtx_unlock(&sc->last_mtx); 373 bp2->bio_error = EILSEQ; 374 DPRINTF(GUZ_DBG_ERR, ("%s/%s: decompress" 375 "(%p) failed\n", __func__, gp->name, 376 sc->dcp)); 377 goto done; 378 } 379 sc->last_blk = blk; 380 memcpy(data2, sc->last_buf + blkofs, ulen); 381 mtx_unlock(&sc->last_mtx); 382 err = sc->dcp->rewind(sc->dcp, gp->name); 383 if (err != 0) { 384 bp2->bio_error = EILSEQ; 385 DPRINTF(GUZ_DBG_ERR, ("%s/%s: rewind(%p) " 386 "failed\n", __func__, gp->name, sc->dcp)); 387 goto done; 388 } 389 data += len; 390 } else 391 break; 392 393 data2 += ulen; 394 bp2->bio_completed += ulen; 395 bp2->bio_resid -= ulen; 396 bp->bio_completed -= len; 397 blkofs = 0; 398 blk++; 399 } 400 401 done: 402 /* Finish processing the request. */ 403 free(bp->bio_data, M_GEOM_UZIP); 404 g_destroy_bio(bp); 405 if (bp2->bio_error != 0 || bp2->bio_resid == 0) 406 g_io_deliver(bp2, bp2->bio_error); 407 else 408 g_uzip_request(gp, bp2); 409 } 410 411 static void 412 g_uzip_start(struct bio *bp) 413 { 414 struct g_provider *pp; 415 struct g_geom *gp; 416 struct g_uzip_softc *sc; 417 418 pp = bp->bio_to; 419 gp = pp->geom; 420 421 DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: cmd=%d, offset=%jd, length=%jd, " 422 "buffer=%p\n", __func__, gp->name, bp, bp->bio_cmd, 423 (intmax_t)bp->bio_offset, (intmax_t)bp->bio_length, bp->bio_data)); 424 425 sc = gp->softc; 426 sc->req_total++; 427 428 if (bp->bio_cmd != BIO_READ) { 429 g_io_deliver(bp, EOPNOTSUPP); 430 return; 431 } 432 433 bp->bio_resid = bp->bio_length; 434 bp->bio_completed = 0; 435 436 g_uzip_request(gp, bp); 437 } 438 439 static void 440 g_uzip_orphan(struct g_consumer *cp) 441 { 442 struct g_geom *gp; 443 444 g_trace(G_T_TOPOLOGY, "%s(%p/%s)", __func__, cp, cp->provider->name); 445 g_topology_assert(); 446 447 gp = cp->geom; 448 g_uzip_softc_free(gp->softc, gp); 449 gp->softc = NULL; 450 g_wither_geom(gp, ENXIO); 451 } 452 453 static int 454 g_uzip_access(struct g_provider *pp, int dr, int dw, int de) 455 { 456 struct g_geom *gp; 457 struct g_consumer *cp; 458 459 gp = pp->geom; 460 cp = LIST_FIRST(&gp->consumer); 461 KASSERT (cp != NULL, ("g_uzip_access but no consumer")); 462 463 if (cp->acw + dw > 0) 464 return (EROFS); 465 466 return (g_access(cp, dr, dw, de)); 467 } 468 469 static void 470 g_uzip_spoiled(struct g_consumer *cp) 471 { 472 struct g_geom *gp; 473 474 gp = cp->geom; 475 g_trace(G_T_TOPOLOGY, "%s(%p/%s)", __func__, cp, gp->name); 476 g_topology_assert(); 477 478 g_uzip_softc_free(gp->softc, gp); 479 gp->softc = NULL; 480 g_wither_geom(gp, ENXIO); 481 } 482 483 static int 484 g_uzip_parse_toc(struct g_uzip_softc *sc, struct g_provider *pp, 485 struct g_geom *gp) 486 { 487 uint32_t i, j, backref_to; 488 uint64_t max_offset, min_offset; 489 490 min_offset = sizeof(struct cloop_header) + 491 (sc->nblocks + 1) * sizeof(uint64_t); 492 max_offset = sc->toc[0].offset - 1; 493 for (i = 0; i < sc->nblocks; i++) { 494 /* First do some bounds checking */ 495 if ((sc->toc[i].offset < min_offset) || 496 (sc->toc[i].offset > pp->mediasize)) { 497 goto error_offset; 498 } 499 DPRINTF_BLK(GUZ_DBG_IO, i, ("%s: cluster #%u " 500 "sc->toc[i].offset=%ju max_offset=%ju\n", gp->name, 501 (u_int)i, (uintmax_t)sc->toc[i].offset, 502 (uintmax_t)max_offset)); 503 backref_to = BLEN_UNDEF; 504 if (sc->toc[i].offset < max_offset) { 505 /* 506 * For the backref'ed blocks search already parsed 507 * TOC entries for the matching offset and copy the 508 * size from matched entry. 509 */ 510 for (j = 0; j <= i; j++) { 511 if (sc->toc[j].offset == sc->toc[i].offset && 512 !BLK_IS_NIL(sc, j)) { 513 break; 514 } 515 if (j != i) { 516 continue; 517 } 518 DPRINTF(GUZ_DBG_ERR, ("%s: cannot match " 519 "backref'ed offset at cluster #%u\n", 520 gp->name, i)); 521 return (-1); 522 } 523 sc->toc[i].blen = sc->toc[j].blen; 524 backref_to = j; 525 } else { 526 /* 527 * For the "normal blocks" seek forward until we hit 528 * block whose offset is larger than ours and assume 529 * it's going to be the next one. 530 */ 531 for (j = i + 1; j < sc->nblocks; j++) { 532 if (sc->toc[j].offset > max_offset) { 533 break; 534 } 535 } 536 sc->toc[i].blen = sc->toc[j].offset - 537 sc->toc[i].offset; 538 if (BLK_ENDS(sc, i) > pp->mediasize) { 539 DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u " 540 "extends past media boundary (%ju > %ju)\n", 541 gp->name, (u_int)i, 542 (uintmax_t)BLK_ENDS(sc, i), 543 (intmax_t)pp->mediasize)); 544 return (-1); 545 } 546 KASSERT(max_offset <= sc->toc[i].offset, ( 547 "%s: max_offset is incorrect: %ju", 548 gp->name, (uintmax_t)max_offset)); 549 max_offset = BLK_ENDS(sc, i) - 1; 550 } 551 DPRINTF_BLK(GUZ_DBG_TOC, i, ("%s: cluster #%u, original %u " 552 "bytes, in %u bytes", gp->name, i, sc->blksz, 553 sc->toc[i].blen)); 554 if (backref_to != BLEN_UNDEF) { 555 DPRINTF_BLK(GUZ_DBG_TOC, i, (" (->#%u)", 556 (u_int)backref_to)); 557 } 558 DPRINTF_BLK(GUZ_DBG_TOC, i, ("\n")); 559 } 560 return (0); 561 562 error_offset: 563 DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u: invalid offset %ju, " 564 "min_offset=%ju mediasize=%jd\n", gp->name, (u_int)i, 565 sc->toc[i].offset, min_offset, pp->mediasize)); 566 return (-1); 567 } 568 569 static struct g_geom * 570 g_uzip_taste(struct g_class *mp, struct g_provider *pp, int flags) 571 { 572 int error; 573 uint32_t i, total_offsets, offsets_read, blk; 574 void *buf; 575 struct cloop_header *header; 576 struct g_consumer *cp; 577 struct g_geom *gp; 578 struct g_provider *pp2; 579 struct g_uzip_softc *sc; 580 enum { 581 G_UZIP = 1, 582 G_ULZMA 583 } type; 584 585 g_trace(G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name); 586 g_topology_assert(); 587 588 /* Skip providers that are already open for writing. */ 589 if (pp->acw > 0) 590 return (NULL); 591 592 buf = NULL; 593 594 /* 595 * Create geom instance. 596 */ 597 gp = g_new_geomf(mp, "%s.uzip", pp->name); 598 cp = g_new_consumer(gp); 599 error = g_attach(cp, pp); 600 if (error == 0) 601 error = g_access(cp, 1, 0, 0); 602 if (error) { 603 goto e1; 604 } 605 g_topology_unlock(); 606 607 /* 608 * Read cloop header, look for CLOOP magic, perform 609 * other validity checks. 610 */ 611 DPRINTF(GUZ_DBG_INFO, ("%s: media sectorsize %u, mediasize %jd\n", 612 gp->name, pp->sectorsize, (intmax_t)pp->mediasize)); 613 buf = g_read_data(cp, 0, pp->sectorsize, NULL); 614 if (buf == NULL) 615 goto e2; 616 header = (struct cloop_header *) buf; 617 if (strncmp(header->magic, CLOOP_MAGIC_START, 618 sizeof(CLOOP_MAGIC_START) - 1) != 0) { 619 DPRINTF(GUZ_DBG_ERR, ("%s: no CLOOP magic\n", gp->name)); 620 goto e3; 621 } 622 623 switch (header->magic[CLOOP_OFS_COMPR]) { 624 case CLOOP_COMP_LZMA: 625 case CLOOP_COMP_LZMA_DDP: 626 type = G_ULZMA; 627 if (header->magic[CLOOP_OFS_VERSN] < CLOOP_MINVER_LZMA) { 628 DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n", 629 gp->name)); 630 goto e3; 631 } 632 DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_LZMA image found\n", 633 gp->name)); 634 break; 635 case CLOOP_COMP_LIBZ: 636 case CLOOP_COMP_LIBZ_DDP: 637 type = G_UZIP; 638 if (header->magic[CLOOP_OFS_VERSN] < CLOOP_MINVER_ZLIB) { 639 DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n", 640 gp->name)); 641 goto e3; 642 } 643 DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_ZLIB image found\n", 644 gp->name)); 645 break; 646 default: 647 DPRINTF(GUZ_DBG_ERR, ("%s: unsupported image type\n", 648 gp->name)); 649 goto e3; 650 } 651 652 /* 653 * Initialize softc and read offsets. 654 */ 655 sc = malloc(sizeof(*sc), M_GEOM_UZIP, M_WAITOK | M_ZERO); 656 gp->softc = sc; 657 sc->blksz = ntohl(header->blksz); 658 sc->nblocks = ntohl(header->nblocks); 659 if (sc->blksz % 512 != 0) { 660 printf("%s: block size (%u) should be multiple of 512.\n", 661 gp->name, sc->blksz); 662 goto e4; 663 } 664 if (sc->blksz > MAX_BLKSZ) { 665 printf("%s: block size (%u) should not be larger than %d.\n", 666 gp->name, sc->blksz, MAX_BLKSZ); 667 } 668 total_offsets = sc->nblocks + 1; 669 if (sizeof(struct cloop_header) + 670 total_offsets * sizeof(uint64_t) > pp->mediasize) { 671 printf("%s: media too small for %u blocks\n", 672 gp->name, sc->nblocks); 673 goto e4; 674 } 675 sc->toc = malloc(total_offsets * sizeof(struct g_uzip_blk), 676 M_GEOM_UZIP, M_WAITOK | M_ZERO); 677 offsets_read = MIN(total_offsets, 678 (pp->sectorsize - sizeof(*header)) / sizeof(uint64_t)); 679 for (i = 0; i < offsets_read; i++) { 680 sc->toc[i].offset = be64toh(((uint64_t *) (header + 1))[i]); 681 sc->toc[i].blen = BLEN_UNDEF; 682 } 683 DPRINTF(GUZ_DBG_INFO, ("%s: %u offsets in the first sector\n", 684 gp->name, offsets_read)); 685 for (blk = 1; offsets_read < total_offsets; blk++) { 686 uint32_t nread; 687 688 free(buf, M_GEOM); 689 buf = g_read_data( 690 cp, blk * pp->sectorsize, pp->sectorsize, NULL); 691 if (buf == NULL) 692 goto e5; 693 nread = MIN(total_offsets - offsets_read, 694 pp->sectorsize / sizeof(uint64_t)); 695 DPRINTF(GUZ_DBG_TOC, ("%s: %u offsets read from sector %d\n", 696 gp->name, nread, blk)); 697 for (i = 0; i < nread; i++) { 698 sc->toc[offsets_read + i].offset = 699 be64toh(((uint64_t *) buf)[i]); 700 sc->toc[offsets_read + i].blen = BLEN_UNDEF; 701 } 702 offsets_read += nread; 703 } 704 free(buf, M_GEOM); 705 buf = NULL; 706 offsets_read -= 1; 707 DPRINTF(GUZ_DBG_INFO, ("%s: done reading %u block offsets from %u " 708 "sectors\n", gp->name, offsets_read, blk)); 709 if (sc->nblocks != offsets_read) { 710 DPRINTF(GUZ_DBG_ERR, ("%s: read %s offsets than expected " 711 "blocks\n", gp->name, 712 sc->nblocks < offsets_read ? "more" : "less")); 713 goto e5; 714 } 715 /* 716 * "Fake" last+1 block, to make it easier for the TOC parser to 717 * iterate without making the last element a special case. 718 */ 719 sc->toc[sc->nblocks].offset = pp->mediasize; 720 /* Massage TOC (table of contents), make sure it is sound */ 721 if (g_uzip_parse_toc(sc, pp, gp) != 0) { 722 DPRINTF(GUZ_DBG_ERR, ("%s: TOC error\n", gp->name)); 723 goto e5; 724 } 725 mtx_init(&sc->last_mtx, "geom_uzip cache", NULL, MTX_DEF); 726 mtx_init(&sc->queue_mtx, "geom_uzip wrkthread", NULL, MTX_DEF); 727 bioq_init(&sc->bio_queue); 728 sc->last_blk = -1; 729 sc->last_buf = malloc(sc->blksz, M_GEOM_UZIP, M_WAITOK); 730 sc->req_total = 0; 731 sc->req_cached = 0; 732 733 if (type == G_UZIP) { 734 sc->dcp = g_uzip_zlib_ctor(sc->blksz); 735 } else { 736 sc->dcp = g_uzip_lzma_ctor(sc->blksz); 737 } 738 if (sc->dcp == NULL) { 739 goto e6; 740 } 741 742 sc->uzip_do = &g_uzip_do; 743 744 error = kproc_create(g_uzip_wrkthr, sc, &sc->procp, 0, 0, "%s", 745 gp->name); 746 if (error != 0) { 747 goto e7; 748 } 749 750 g_topology_lock(); 751 pp2 = g_new_providerf(gp, "%s", gp->name); 752 pp2->sectorsize = 512; 753 pp2->mediasize = (off_t)sc->nblocks * sc->blksz; 754 pp2->stripesize = pp->stripesize; 755 pp2->stripeoffset = pp->stripeoffset; 756 g_error_provider(pp2, 0); 757 g_access(cp, -1, 0, 0); 758 759 DPRINTF(GUZ_DBG_INFO, ("%s: taste ok (%d, %jd), (%d, %d), %x\n", 760 gp->name, pp2->sectorsize, (intmax_t)pp2->mediasize, 761 pp2->stripeoffset, pp2->stripesize, pp2->flags)); 762 DPRINTF(GUZ_DBG_INFO, ("%s: %u x %u blocks\n", gp->name, sc->nblocks, 763 sc->blksz)); 764 return (gp); 765 766 e7: 767 sc->dcp->free(sc->dcp); 768 e6: 769 free(sc->last_buf, M_GEOM); 770 mtx_destroy(&sc->queue_mtx); 771 mtx_destroy(&sc->last_mtx); 772 e5: 773 free(sc->toc, M_GEOM); 774 e4: 775 free(gp->softc, M_GEOM_UZIP); 776 e3: 777 if (buf != NULL) { 778 free(buf, M_GEOM); 779 } 780 e2: 781 g_topology_lock(); 782 g_access(cp, -1, 0, 0); 783 e1: 784 g_detach(cp); 785 g_destroy_consumer(cp); 786 g_destroy_geom(gp); 787 788 return (NULL); 789 } 790 791 static int 792 g_uzip_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp) 793 { 794 struct g_provider *pp; 795 796 g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, gp->name); 797 g_topology_assert(); 798 799 if (gp->softc == NULL) { 800 DPRINTF(GUZ_DBG_ERR, ("%s(%s): gp->softc == NULL\n", __func__, 801 gp->name)); 802 return (ENXIO); 803 } 804 805 KASSERT(gp != NULL, ("NULL geom")); 806 pp = LIST_FIRST(&gp->provider); 807 KASSERT(pp != NULL, ("NULL provider")); 808 if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) 809 return (EBUSY); 810 811 g_uzip_softc_free(gp->softc, gp); 812 gp->softc = NULL; 813 g_wither_geom(gp, ENXIO); 814 815 return (0); 816 } 817 818 static struct g_class g_uzip_class = { 819 .name = UZIP_CLASS_NAME, 820 .version = G_VERSION, 821 .taste = g_uzip_taste, 822 .destroy_geom = g_uzip_destroy_geom, 823 824 .start = g_uzip_start, 825 .orphan = g_uzip_orphan, 826 .access = g_uzip_access, 827 .spoiled = g_uzip_spoiled, 828 }; 829 830 DECLARE_GEOM_CLASS(g_uzip_class, g_uzip); 831 MODULE_DEPEND(g_uzip, zlib, 1, 1, 1); 832