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