]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/geom/uzip/g_uzip.c
MFC r343473:
[FreeBSD/stable/10.git] / sys / geom / uzip / g_uzip.c
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         unsigned char last:1;
64         unsigned char padded:1;
65 #define BLEN_UNDEF      UINT32_MAX
66 };
67
68 #ifndef ABS
69 #define ABS(a)                  ((a) < 0 ? -(a) : (a))
70 #endif
71
72 #define BLK_IN_RANGE(mcn, bcn, ilen)    \
73     (((bcn) != BLEN_UNDEF) && ( \
74         ((ilen) >= 0 && (mcn >= bcn) && (mcn <= ((intmax_t)(bcn) + (ilen)))) || \
75         ((ilen) < 0 && (mcn <= bcn) && (mcn >= ((intmax_t)(bcn) + (ilen)))) \
76     ))
77
78 #ifdef GEOM_UZIP_DEBUG
79 # define GEOM_UZIP_DBG_DEFAULT  3
80 #else
81 # define GEOM_UZIP_DBG_DEFAULT  0
82 #endif
83
84 #define GUZ_DBG_ERR     1
85 #define GUZ_DBG_INFO    2
86 #define GUZ_DBG_IO      3
87 #define GUZ_DBG_TOC     4
88
89 #define GUZ_DEV_SUFX    ".uzip"
90 #define GUZ_DEV_NAME(p) (p GUZ_DEV_SUFX)
91
92 static char g_uzip_attach_to[MAXPATHLEN] = {"*"};
93 static char g_uzip_noattach_to[MAXPATHLEN] = {GUZ_DEV_NAME("*")};
94 TUNABLE_STR("kern.geom.uzip.attach_to", g_uzip_attach_to,
95     sizeof(g_uzip_attach_to));
96 TUNABLE_STR("kern.geom.uzip.noattach_to", g_uzip_noattach_to,
97     sizeof(g_uzip_noattach_to));
98
99 SYSCTL_DECL(_kern_geom);
100 SYSCTL_NODE(_kern_geom, OID_AUTO, uzip, CTLFLAG_RW, 0, "GEOM_UZIP stuff");
101 static u_int g_uzip_debug = GEOM_UZIP_DBG_DEFAULT;
102 SYSCTL_UINT(_kern_geom_uzip, OID_AUTO, debug, CTLFLAG_RWTUN, &g_uzip_debug, 0,
103     "Debug level (0-4)");
104 static u_int g_uzip_debug_block = BLEN_UNDEF;
105 SYSCTL_UINT(_kern_geom_uzip, OID_AUTO, debug_block, CTLFLAG_RWTUN,
106     &g_uzip_debug_block, 0, "Debug operations around specific cluster#");
107
108 #define DPRINTF(lvl, a)         \
109         if ((lvl) <= g_uzip_debug) { \
110                 printf a; \
111         }
112 #define DPRINTF_BLK(lvl, cn, a) \
113         if ((lvl) <= g_uzip_debug || \
114             BLK_IN_RANGE(cn, g_uzip_debug_block, 8) || \
115             BLK_IN_RANGE(cn, g_uzip_debug_block, -8)) { \
116                 printf a; \
117         }
118 #define DPRINTF_BRNG(lvl, bcn, ecn, a) \
119         KASSERT(bcn < ecn, ("DPRINTF_BRNG: invalid range (%ju, %ju)", \
120             (uintmax_t)bcn, (uintmax_t)ecn)); \
121         if (((lvl) <= g_uzip_debug) || \
122             BLK_IN_RANGE(g_uzip_debug_block, bcn, \
123              (intmax_t)ecn - (intmax_t)bcn)) { \
124                 printf a; \
125         }
126
127 #define UZIP_CLASS_NAME "UZIP"
128
129 /*
130  * Maximum allowed valid block size (to prevent foot-shooting)
131  */
132 #define MAX_BLKSZ       (MAXPHYS)
133
134 static char CLOOP_MAGIC_START[] = "#!/bin/sh\n";
135
136 static void g_uzip_read_done(struct bio *bp);
137 static void g_uzip_do(struct g_uzip_softc *, struct bio *bp);
138
139 static void
140 g_uzip_softc_free(struct g_uzip_softc *sc, struct g_geom *gp)
141 {
142
143         if (gp != NULL) {
144                 DPRINTF(GUZ_DBG_INFO, ("%s: %d requests, %d cached\n",
145                     gp->name, sc->req_total, sc->req_cached));
146         }
147
148         mtx_lock(&sc->queue_mtx);
149         sc->wrkthr_flags |= GUZ_SHUTDOWN;
150         wakeup(sc);
151         while (!(sc->wrkthr_flags & GUZ_EXITING)) {
152                 msleep(sc->procp, &sc->queue_mtx, PRIBIO, "guzfree",
153                     hz / 10);
154         }
155         mtx_unlock(&sc->queue_mtx);
156
157         sc->dcp->free(sc->dcp);
158         free(sc->toc, M_GEOM_UZIP);
159         mtx_destroy(&sc->queue_mtx);
160         mtx_destroy(&sc->last_mtx);
161         free(sc->last_buf, M_GEOM_UZIP);
162         free(sc, M_GEOM_UZIP);
163 }
164
165 static int
166 g_uzip_cached(struct g_geom *gp, struct bio *bp)
167 {
168         struct g_uzip_softc *sc;
169         off_t ofs;
170         size_t blk, blkofs, usz;
171
172         sc = gp->softc;
173         ofs = bp->bio_offset + bp->bio_completed;
174         blk = ofs / sc->blksz;
175         mtx_lock(&sc->last_mtx);
176         if (blk == sc->last_blk) {
177                 blkofs = ofs % sc->blksz;
178                 usz = sc->blksz - blkofs;
179                 if (bp->bio_resid < usz)
180                         usz = bp->bio_resid;
181                 memcpy(bp->bio_data + bp->bio_completed, sc->last_buf + blkofs,
182                     usz);
183                 sc->req_cached++;
184                 mtx_unlock(&sc->last_mtx);
185
186                 DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: offset=%jd: got %jd bytes "
187                     "from cache\n", __func__, gp->name, bp, (intmax_t)ofs,
188                     (intmax_t)usz));
189
190                 bp->bio_completed += usz;
191                 bp->bio_resid -= usz;
192
193                 if (bp->bio_resid == 0) {
194                         g_io_deliver(bp, 0);
195                         return (1);
196                 }
197         } else
198                 mtx_unlock(&sc->last_mtx);
199
200         return (0);
201 }
202
203 #define BLK_ENDS(sc, bi)        ((sc)->toc[(bi)].offset + \
204     (sc)->toc[(bi)].blen)
205
206 #define BLK_IS_CONT(sc, bi)     (BLK_ENDS((sc), (bi) - 1) == \
207     (sc)->toc[(bi)].offset)
208 #define BLK_IS_NIL(sc, bi)      ((sc)->toc[(bi)].blen == 0)
209
210 #define TOFF_2_BOFF(sc, pp, bi)     ((sc)->toc[(bi)].offset - \
211     (sc)->toc[(bi)].offset % (pp)->sectorsize)
212 #define TLEN_2_BLEN(sc, pp, bp, ei) ((BLK_ENDS((sc), (ei)) - \
213     (bp)->bio_offset + (pp)->sectorsize - 1) / \
214     (pp)->sectorsize * (pp)->sectorsize)
215
216 static int
217 g_uzip_request(struct g_geom *gp, struct bio *bp)
218 {
219         struct g_uzip_softc *sc;
220         struct bio *bp2;
221         struct g_consumer *cp;
222         struct g_provider *pp;
223         off_t ofs, start_blk_ofs;
224         size_t i, start_blk, end_blk, zsize;
225
226         if (g_uzip_cached(gp, bp) != 0)
227                 return (1);
228
229         sc = gp->softc;
230
231         cp = LIST_FIRST(&gp->consumer);
232         pp = cp->provider;
233
234         ofs = bp->bio_offset + bp->bio_completed;
235         start_blk = ofs / sc->blksz;
236         KASSERT(start_blk < sc->nblocks, ("start_blk out of range"));
237         end_blk = (ofs + bp->bio_resid + sc->blksz - 1) / sc->blksz;
238         KASSERT(end_blk <= sc->nblocks, ("end_blk out of range"));
239
240         for (; BLK_IS_NIL(sc, start_blk) && start_blk < end_blk; start_blk++) {
241                 /* Fill in any leading Nil blocks */
242                 start_blk_ofs = ofs % sc->blksz;
243                 zsize = MIN(sc->blksz - start_blk_ofs, bp->bio_resid);
244                 DPRINTF_BLK(GUZ_DBG_IO, start_blk, ("%s/%s: %p/%ju: "
245                     "filling %ju zero bytes\n", __func__, gp->name, gp,
246                     (uintmax_t)bp->bio_completed, (uintmax_t)zsize));
247                 bzero(bp->bio_data + bp->bio_completed, zsize);
248                 bp->bio_completed += zsize;
249                 bp->bio_resid -= zsize;
250                 ofs += zsize;
251         }
252
253         if (start_blk == end_blk) {
254                 KASSERT(bp->bio_resid == 0, ("bp->bio_resid is invalid"));
255                 /*
256                  * No non-Nil data is left, complete request immediately.
257                  */
258                 DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: all done returning %ju "
259                     "bytes\n", __func__, gp->name, gp,
260                     (uintmax_t)bp->bio_completed));
261                 g_io_deliver(bp, 0);
262                 return (1);
263         }
264
265         for (i = start_blk + 1; i < end_blk; i++) {
266                 /* Trim discontinuous areas if any */
267                 if (!BLK_IS_CONT(sc, i)) {
268                         end_blk = i;
269                         break;
270                 }
271         }
272
273         DPRINTF_BRNG(GUZ_DBG_IO, start_blk, end_blk, ("%s/%s: %p: "
274             "start=%u (%ju[%jd]), end=%u (%ju)\n", __func__, gp->name, bp,
275             (u_int)start_blk, (uintmax_t)sc->toc[start_blk].offset,
276             (intmax_t)sc->toc[start_blk].blen,
277             (u_int)end_blk, (uintmax_t)BLK_ENDS(sc, end_blk - 1)));
278
279         bp2 = g_clone_bio(bp);
280         if (bp2 == NULL) {
281                 g_io_deliver(bp, ENOMEM);
282                 return (1);
283         }
284         bp2->bio_done = g_uzip_read_done;
285
286         bp2->bio_offset = TOFF_2_BOFF(sc, pp, start_blk);
287         while (1) {
288                 bp2->bio_length = TLEN_2_BLEN(sc, pp, bp2, end_blk - 1);
289                 if (bp2->bio_length <= MAXPHYS) {
290                         break;
291                 }
292                 if (end_blk == (start_blk + 1)) {
293                         break;
294                 }
295                 end_blk--;
296         }
297
298         DPRINTF(GUZ_DBG_IO, ("%s/%s: bp2->bio_length = %jd, "
299             "bp2->bio_offset = %jd\n", __func__, gp->name,
300             (intmax_t)bp2->bio_length, (intmax_t)bp2->bio_offset));
301
302         bp2->bio_data = malloc(bp2->bio_length, M_GEOM_UZIP, M_NOWAIT);
303         if (bp2->bio_data == NULL) {
304                 g_destroy_bio(bp2);
305                 g_io_deliver(bp, ENOMEM);
306                 return (1);
307         }
308
309         DPRINTF_BRNG(GUZ_DBG_IO, start_blk, end_blk, ("%s/%s: %p: "
310             "reading %jd bytes from offset %jd\n", __func__, gp->name, bp,
311             (intmax_t)bp2->bio_length, (intmax_t)bp2->bio_offset));
312
313         g_io_request(bp2, cp);
314         return (0);
315 }
316
317 static void
318 g_uzip_read_done(struct bio *bp)
319 {
320         struct bio *bp2;
321         struct g_geom *gp;
322         struct g_uzip_softc *sc;
323
324         bp2 = bp->bio_parent;
325         gp = bp2->bio_to->geom;
326         sc = gp->softc;
327
328         mtx_lock(&sc->queue_mtx);
329         bioq_disksort(&sc->bio_queue, bp);
330         mtx_unlock(&sc->queue_mtx);
331         wakeup(sc);
332 }
333
334 static int
335 g_uzip_memvcmp(const void *memory, unsigned char val, size_t size)
336 {
337         const u_char *mm;
338
339         mm = (const u_char *)memory;
340         return (*mm == val) && memcmp(mm, mm + 1, size - 1) == 0;
341 }
342
343 static void
344 g_uzip_do(struct g_uzip_softc *sc, struct bio *bp)
345 {
346         struct bio *bp2;
347         struct g_provider *pp;
348         struct g_consumer *cp;
349         struct g_geom *gp;
350         char *data, *data2;
351         off_t ofs;
352         size_t blk, blkofs, len, ulen, firstblk;
353         int err;
354
355         bp2 = bp->bio_parent;
356         gp = bp2->bio_to->geom;
357
358         cp = LIST_FIRST(&gp->consumer);
359         pp = cp->provider;
360
361         bp2->bio_error = bp->bio_error;
362         if (bp2->bio_error != 0)
363                 goto done;
364
365         /* Make sure there's forward progress. */
366         if (bp->bio_completed == 0) {
367                 bp2->bio_error = ECANCELED;
368                 goto done;
369         }
370
371         ofs = bp2->bio_offset + bp2->bio_completed;
372         firstblk = blk = ofs / sc->blksz;
373         blkofs = ofs % sc->blksz;
374         data = bp->bio_data + sc->toc[blk].offset % pp->sectorsize;
375         data2 = bp2->bio_data + bp2->bio_completed;
376         while (bp->bio_completed && bp2->bio_resid) {
377                 if (blk > firstblk && !BLK_IS_CONT(sc, blk)) {
378                         DPRINTF_BLK(GUZ_DBG_IO, blk, ("%s/%s: %p: backref'ed "
379                             "cluster #%u requested, looping around\n",
380                             __func__, gp->name, bp2, (u_int)blk));
381                         goto done;
382                 }
383                 ulen = MIN(sc->blksz - blkofs, bp2->bio_resid);
384                 len = sc->toc[blk].blen;
385                 DPRINTF(GUZ_DBG_IO, ("%s/%s: %p/%ju: data2=%p, ulen=%u, "
386                     "data=%p, len=%u\n", __func__, gp->name, gp,
387                     bp->bio_completed, data2, (u_int)ulen, data, (u_int)len));
388                 if (len == 0) {
389                         /* All zero block: no cache update */
390 zero_block:
391                         bzero(data2, ulen);
392                 } else if (len <= bp->bio_completed) {
393                         mtx_lock(&sc->last_mtx);
394                         err = sc->dcp->decompress(sc->dcp, gp->name, data,
395                             len, sc->last_buf);
396                         if (err != 0 && sc->toc[blk].last != 0) {
397                                 /*
398                                  * Last block decompression has failed, check
399                                  * if it's just zero padding.
400                                  */
401                                 if (g_uzip_memvcmp(data, '\0', len) == 0) {
402                                         sc->toc[blk].blen = 0;
403                                         sc->last_blk = -1;
404                                         mtx_unlock(&sc->last_mtx);
405                                         len = 0;
406                                         goto zero_block;
407                                 }
408                         }
409                         if (err != 0) {
410                                 sc->last_blk = -1;
411                                 mtx_unlock(&sc->last_mtx);
412                                 bp2->bio_error = EILSEQ;
413                                 DPRINTF(GUZ_DBG_ERR, ("%s/%s: decompress"
414                                     "(%p, %ju, %ju) failed\n", __func__,
415                                     gp->name, sc->dcp, (uintmax_t)blk,
416                                     (uintmax_t)len));
417                                 goto done;
418                         }
419                         sc->last_blk = blk;
420                         memcpy(data2, sc->last_buf + blkofs, ulen);
421                         mtx_unlock(&sc->last_mtx);
422                         err = sc->dcp->rewind(sc->dcp, gp->name);
423                         if (err != 0) {
424                                 bp2->bio_error = EILSEQ;
425                                 DPRINTF(GUZ_DBG_ERR, ("%s/%s: rewind(%p) "
426                                     "failed\n", __func__, gp->name, sc->dcp));
427                                 goto done;
428                         }
429                         data += len;
430                 } else
431                         break;
432
433                 data2 += ulen;
434                 bp2->bio_completed += ulen;
435                 bp2->bio_resid -= ulen;
436                 bp->bio_completed -= len;
437                 blkofs = 0;
438                 blk++;
439         }
440
441 done:
442         /* Finish processing the request. */
443         free(bp->bio_data, M_GEOM_UZIP);
444         g_destroy_bio(bp);
445         if (bp2->bio_error != 0 || bp2->bio_resid == 0)
446                 g_io_deliver(bp2, bp2->bio_error);
447         else
448                 g_uzip_request(gp, bp2);
449 }
450
451 static void
452 g_uzip_start(struct bio *bp)
453 {
454         struct g_provider *pp;
455         struct g_geom *gp;
456         struct g_uzip_softc *sc;
457
458         pp = bp->bio_to;
459         gp = pp->geom;
460
461         DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: cmd=%d, offset=%jd, length=%jd, "
462             "buffer=%p\n", __func__, gp->name, bp, bp->bio_cmd,
463             (intmax_t)bp->bio_offset, (intmax_t)bp->bio_length, bp->bio_data));
464
465         sc = gp->softc;
466         sc->req_total++;
467
468         if (bp->bio_cmd != BIO_READ) {
469                 g_io_deliver(bp, EOPNOTSUPP);
470                 return;
471         }
472
473         bp->bio_resid = bp->bio_length;
474         bp->bio_completed = 0;
475
476         g_uzip_request(gp, bp);
477 }
478
479 static void
480 g_uzip_orphan(struct g_consumer *cp)
481 {
482         struct g_geom *gp;
483
484         g_trace(G_T_TOPOLOGY, "%s(%p/%s)", __func__, cp, cp->provider->name);
485         g_topology_assert();
486
487         gp = cp->geom;
488         g_uzip_softc_free(gp->softc, gp);
489         gp->softc = NULL;
490         g_wither_geom(gp, ENXIO);
491 }
492
493 static int
494 g_uzip_access(struct g_provider *pp, int dr, int dw, int de)
495 {
496         struct g_geom *gp;
497         struct g_consumer *cp;
498
499         gp = pp->geom;
500         cp = LIST_FIRST(&gp->consumer);
501         KASSERT (cp != NULL, ("g_uzip_access but no consumer"));
502
503         if (cp->acw + dw > 0)
504                 return (EROFS);
505
506         return (g_access(cp, dr, dw, de));
507 }
508
509 static void
510 g_uzip_spoiled(struct g_consumer *cp)
511 {
512         struct g_geom *gp;
513
514         G_VALID_CONSUMER(cp);
515         gp = cp->geom;
516         g_trace(G_T_TOPOLOGY, "%s(%p/%s)", __func__, cp, gp->name);
517         g_topology_assert();
518
519         g_uzip_softc_free(gp->softc, gp);
520         gp->softc = NULL;
521         g_wither_geom(gp, ENXIO);
522 }
523
524 static int
525 g_uzip_parse_toc(struct g_uzip_softc *sc, struct g_provider *pp,
526     struct g_geom *gp)
527 {
528         uint32_t i, j, backref_to;
529         uint64_t max_offset, min_offset;
530         struct g_uzip_blk *last_blk;
531
532         min_offset = sizeof(struct cloop_header) +
533             (sc->nblocks + 1) * sizeof(uint64_t);
534         max_offset = sc->toc[0].offset - 1;
535         last_blk = &sc->toc[0];
536         for (i = 0; i < sc->nblocks; i++) {
537                 /* First do some bounds checking */
538                 if ((sc->toc[i].offset < min_offset) ||
539                     (sc->toc[i].offset > pp->mediasize)) {
540                         goto error_offset;
541                 }
542                 DPRINTF_BLK(GUZ_DBG_IO, i, ("%s: cluster #%u "
543                     "offset=%ju max_offset=%ju\n", gp->name,
544                     (u_int)i, (uintmax_t)sc->toc[i].offset,
545                     (uintmax_t)max_offset));
546                 backref_to = BLEN_UNDEF;
547                 if (sc->toc[i].offset < max_offset) {
548                         /*
549                          * For the backref'ed blocks search already parsed
550                          * TOC entries for the matching offset and copy the
551                          * size from matched entry.
552                          */
553                         for (j = 0; j <= i; j++) {
554                                 if (sc->toc[j].offset == sc->toc[i].offset &&
555                                     !BLK_IS_NIL(sc, j)) {
556                                         break;
557                                 }
558                                 if (j != i) {
559                                         continue;
560                                 }
561                                 DPRINTF(GUZ_DBG_ERR, ("%s: cannot match "
562                                     "backref'ed offset at cluster #%u\n",
563                                     gp->name, i));
564                                 return (-1);
565                         }
566                         sc->toc[i].blen = sc->toc[j].blen;
567                         backref_to = j;
568                 } else {
569                         last_blk = &sc->toc[i];
570                         /*
571                          * For the "normal blocks" seek forward until we hit
572                          * block whose offset is larger than ours and assume
573                          * it's going to be the next one.
574                          */
575                         for (j = i + 1; j < sc->nblocks; j++) {
576                                 if (sc->toc[j].offset > max_offset) {
577                                         break;
578                                 }
579                         }
580                         sc->toc[i].blen = sc->toc[j].offset -
581                             sc->toc[i].offset;
582                         if (BLK_ENDS(sc, i) > pp->mediasize) {
583                                 DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u "
584                                     "extends past media boundary (%ju > %ju)\n",
585                                     gp->name, (u_int)i,
586                                     (uintmax_t)BLK_ENDS(sc, i),
587                                     (intmax_t)pp->mediasize));
588                                 return (-1);
589                         }
590                         KASSERT(max_offset <= sc->toc[i].offset, (
591                             "%s: max_offset is incorrect: %ju",
592                             gp->name, (uintmax_t)max_offset));
593                         max_offset = BLK_ENDS(sc, i) - 1;
594                 }
595                 DPRINTF_BLK(GUZ_DBG_TOC, i, ("%s: cluster #%u, original %u "
596                     "bytes, in %u bytes", gp->name, i, sc->blksz,
597                     sc->toc[i].blen));
598                 if (backref_to != BLEN_UNDEF) {
599                         DPRINTF_BLK(GUZ_DBG_TOC, i, (" (->#%u)",
600                             (u_int)backref_to));
601                 }
602                 DPRINTF_BLK(GUZ_DBG_TOC, i, ("\n"));
603         }
604         last_blk->last = 1;
605         /* Do a second pass to validate block lengths */
606         for (i = 0; i < sc->nblocks; i++) {
607                 if (sc->toc[i].blen > sc->dcp->max_blen) {
608                         if (sc->toc[i].last == 0) {
609                                 DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u "
610                                     "length (%ju) exceeds "
611                                     "max_blen (%ju)\n", gp->name, i,
612                                     (uintmax_t)sc->toc[i].blen,
613                                     (uintmax_t)sc->dcp->max_blen));
614                                 return (-1);
615                         }
616                         DPRINTF(GUZ_DBG_INFO, ("%s: cluster #%u extra "
617                             "padding is detected, trimmed to %ju\n",
618                             gp->name, i, (uintmax_t)sc->dcp->max_blen));
619                             sc->toc[i].blen = sc->dcp->max_blen;
620                         sc->toc[i].padded = 1;
621                 }
622         }
623         return (0);
624
625 error_offset:
626         DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u: invalid offset %ju, "
627             "min_offset=%ju mediasize=%jd\n", gp->name, (u_int)i,
628             sc->toc[i].offset, min_offset, pp->mediasize));
629         return (-1);
630 }
631
632 static struct g_geom *
633 g_uzip_taste(struct g_class *mp, struct g_provider *pp, int flags)
634 {
635         int error;
636         uint32_t i, total_offsets, offsets_read, blk;
637         void *buf;
638         struct cloop_header *header;
639         struct g_consumer *cp;
640         struct g_geom *gp;
641         struct g_provider *pp2;
642         struct g_uzip_softc *sc;
643         enum {
644                 G_UZIP = 1,
645                 G_ULZMA
646         } type;
647
648         g_trace(G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name);
649         g_topology_assert();
650
651         /* Skip providers that are already open for writing. */
652         if (pp->acw > 0)
653                 return (NULL);
654
655         if ((fnmatch(g_uzip_attach_to, pp->name, 0) != 0) ||
656             (fnmatch(g_uzip_noattach_to, pp->name, 0) == 0)) {
657                 DPRINTF(GUZ_DBG_INFO, ("%s(%s,%s), ignoring\n", __func__,
658                     mp->name, pp->name));
659                 return (NULL);
660         }
661
662         buf = NULL;
663
664         /*
665          * Create geom instance.
666          */
667         gp = g_new_geomf(mp, GUZ_DEV_NAME("%s"), pp->name);
668         cp = g_new_consumer(gp);
669         error = g_attach(cp, pp);
670         if (error == 0)
671                 error = g_access(cp, 1, 0, 0);
672         if (error) {
673                 goto e1;
674         }
675         g_topology_unlock();
676
677         /*
678          * Read cloop header, look for CLOOP magic, perform
679          * other validity checks.
680          */
681         DPRINTF(GUZ_DBG_INFO, ("%s: media sectorsize %u, mediasize %jd\n",
682             gp->name, pp->sectorsize, (intmax_t)pp->mediasize));
683         buf = g_read_data(cp, 0, pp->sectorsize, NULL);
684         if (buf == NULL)
685                 goto e2;
686         header = (struct cloop_header *) buf;
687         if (strncmp(header->magic, CLOOP_MAGIC_START,
688             sizeof(CLOOP_MAGIC_START) - 1) != 0) {
689                 DPRINTF(GUZ_DBG_ERR, ("%s: no CLOOP magic\n", gp->name));
690                 goto e3;
691         }
692
693         switch (header->magic[CLOOP_OFS_COMPR]) {
694         case CLOOP_COMP_LZMA:
695         case CLOOP_COMP_LZMA_DDP:
696                 type = G_ULZMA;
697                 if (header->magic[CLOOP_OFS_VERSN] < CLOOP_MINVER_LZMA) {
698                         DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n",
699                             gp->name));
700                         goto e3;
701                 }
702                 DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_LZMA image found\n",
703                     gp->name));
704                 break;
705         case CLOOP_COMP_LIBZ:
706         case CLOOP_COMP_LIBZ_DDP:
707                 type = G_UZIP;
708                 if (header->magic[CLOOP_OFS_VERSN] < CLOOP_MINVER_ZLIB) {
709                         DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n",
710                             gp->name));
711                         goto e3;
712                 }
713                 DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_ZLIB image found\n",
714                     gp->name));
715                 break;
716         default:
717                 DPRINTF(GUZ_DBG_ERR, ("%s: unsupported image type\n",
718                     gp->name));
719                 goto e3;
720         }
721
722         /*
723          * Initialize softc and read offsets.
724          */
725         sc = malloc(sizeof(*sc), M_GEOM_UZIP, M_WAITOK | M_ZERO);
726         gp->softc = sc;
727         sc->blksz = ntohl(header->blksz);
728         sc->nblocks = ntohl(header->nblocks);
729         if (sc->blksz % 512 != 0) {
730                 printf("%s: block size (%u) should be multiple of 512.\n",
731                     gp->name, sc->blksz);
732                 goto e4;
733         }
734         if (sc->blksz > MAX_BLKSZ) {
735                 printf("%s: block size (%u) should not be larger than %d.\n",
736                     gp->name, sc->blksz, MAX_BLKSZ);
737         }
738         total_offsets = sc->nblocks + 1;
739         if (sizeof(struct cloop_header) +
740             total_offsets * sizeof(uint64_t) > pp->mediasize) {
741                 printf("%s: media too small for %u blocks\n",
742                     gp->name, sc->nblocks);
743                 goto e4;
744         }
745         sc->toc = malloc(total_offsets * sizeof(struct g_uzip_blk),
746             M_GEOM_UZIP, M_WAITOK | M_ZERO);
747         offsets_read = MIN(total_offsets,
748             (pp->sectorsize - sizeof(*header)) / sizeof(uint64_t));
749         for (i = 0; i < offsets_read; i++) {
750                 sc->toc[i].offset = be64toh(((uint64_t *) (header + 1))[i]);
751                 sc->toc[i].blen = BLEN_UNDEF;
752         }
753         DPRINTF(GUZ_DBG_INFO, ("%s: %u offsets in the first sector\n",
754                gp->name, offsets_read));
755         for (blk = 1; offsets_read < total_offsets; blk++) {
756                 uint32_t nread;
757
758                 free(buf, M_GEOM);
759                 buf = g_read_data(
760                     cp, blk * pp->sectorsize, pp->sectorsize, NULL);
761                 if (buf == NULL)
762                         goto e5;
763                 nread = MIN(total_offsets - offsets_read,
764                      pp->sectorsize / sizeof(uint64_t));
765                 DPRINTF(GUZ_DBG_TOC, ("%s: %u offsets read from sector %d\n",
766                     gp->name, nread, blk));
767                 for (i = 0; i < nread; i++) {
768                         sc->toc[offsets_read + i].offset =
769                             be64toh(((uint64_t *) buf)[i]);
770                         sc->toc[offsets_read + i].blen = BLEN_UNDEF;
771                 }
772                 offsets_read += nread;
773         }
774         free(buf, M_GEOM);
775         buf = NULL;
776         offsets_read -= 1;
777         DPRINTF(GUZ_DBG_INFO, ("%s: done reading %u block offsets from %u "
778             "sectors\n", gp->name, offsets_read, blk));
779         if (sc->nblocks != offsets_read) {
780                 DPRINTF(GUZ_DBG_ERR, ("%s: read %s offsets than expected "
781                     "blocks\n", gp->name,
782                     sc->nblocks < offsets_read ? "more" : "less"));
783                 goto e5;
784         }
785
786         if (type == G_UZIP) {
787                 sc->dcp = g_uzip_zlib_ctor(sc->blksz);
788         } else {
789                 sc->dcp = g_uzip_lzma_ctor(sc->blksz);
790         }
791         if (sc->dcp == NULL) {
792                 goto e5;
793         }
794
795         /*
796          * "Fake" last+1 block, to make it easier for the TOC parser to
797          * iterate without making the last element a special case.
798          */
799         sc->toc[sc->nblocks].offset = pp->mediasize;
800         /* Massage TOC (table of contents), make sure it is sound */
801         if (g_uzip_parse_toc(sc, pp, gp) != 0) {
802                 DPRINTF(GUZ_DBG_ERR, ("%s: TOC error\n", gp->name));
803                 goto e6;
804         }
805         mtx_init(&sc->last_mtx, "geom_uzip cache", NULL, MTX_DEF);
806         mtx_init(&sc->queue_mtx, "geom_uzip wrkthread", NULL, MTX_DEF);
807         bioq_init(&sc->bio_queue);
808         sc->last_blk = -1;
809         sc->last_buf = malloc(sc->blksz, M_GEOM_UZIP, M_WAITOK);
810         sc->req_total = 0;
811         sc->req_cached = 0;
812
813         sc->uzip_do = &g_uzip_do;
814
815         error = kproc_create(g_uzip_wrkthr, sc, &sc->procp, 0, 0, "%s",
816             gp->name);
817         if (error != 0) {
818                 goto e7;
819         }
820
821         g_topology_lock();
822         pp2 = g_new_providerf(gp, "%s", gp->name);
823         pp2->sectorsize = 512;
824         pp2->mediasize = (off_t)sc->nblocks * sc->blksz;
825         pp2->stripesize = pp->stripesize;
826         pp2->stripeoffset = pp->stripeoffset;
827         g_error_provider(pp2, 0);
828         g_access(cp, -1, 0, 0);
829
830         DPRINTF(GUZ_DBG_INFO, ("%s: taste ok (%d, %jd), (%d, %d), %x\n",
831             gp->name, pp2->sectorsize, (intmax_t)pp2->mediasize,
832             pp2->stripeoffset, pp2->stripesize, pp2->flags));
833         DPRINTF(GUZ_DBG_INFO, ("%s: %u x %u blocks\n", gp->name, sc->nblocks,
834             sc->blksz));
835         return (gp);
836
837 e7:
838         free(sc->last_buf, M_GEOM);
839         mtx_destroy(&sc->queue_mtx);
840         mtx_destroy(&sc->last_mtx);
841 e6:
842         sc->dcp->free(sc->dcp);
843 e5:
844         free(sc->toc, M_GEOM);
845 e4:
846         free(gp->softc, M_GEOM_UZIP);
847 e3:
848         if (buf != NULL) {
849                 free(buf, M_GEOM);
850         }
851 e2:
852         g_topology_lock();
853         g_access(cp, -1, 0, 0);
854 e1:
855         g_detach(cp);
856         g_destroy_consumer(cp);
857         g_destroy_geom(gp);
858
859         return (NULL);
860 }
861
862 static int
863 g_uzip_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
864 {
865         struct g_provider *pp;
866
867         KASSERT(gp != NULL, ("NULL geom"));
868         g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, gp->name);
869         g_topology_assert();
870
871         if (gp->softc == NULL) {
872                 DPRINTF(GUZ_DBG_ERR, ("%s(%s): gp->softc == NULL\n", __func__,
873                     gp->name));
874                 return (ENXIO);
875         }
876
877         pp = LIST_FIRST(&gp->provider);
878         KASSERT(pp != NULL, ("NULL provider"));
879         if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)
880                 return (EBUSY);
881
882         g_uzip_softc_free(gp->softc, gp);
883         gp->softc = NULL;
884         g_wither_geom(gp, ENXIO);
885
886         return (0);
887 }
888
889 static struct g_class g_uzip_class = {
890         .name = UZIP_CLASS_NAME,
891         .version = G_VERSION,
892         .taste = g_uzip_taste,
893         .destroy_geom = g_uzip_destroy_geom,
894
895         .start = g_uzip_start,
896         .orphan = g_uzip_orphan,
897         .access = g_uzip_access,
898         .spoiled = g_uzip_spoiled,
899 };
900
901 DECLARE_GEOM_CLASS(g_uzip_class, g_uzip);
902 MODULE_DEPEND(g_uzip, zlib, 1, 1, 1);