Changeset View
Changeset View
Standalone View
Standalone View
sys/geom/uzip/g_uzip.c
Show All 25 Lines | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_geom.h" | |||||
#include "opt_zstdio.h" | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/bio.h> | #include <sys/bio.h> | ||||
#include <sys/endian.h> | #include <sys/endian.h> | ||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/kthread.h> | #include <sys/kthread.h> | ||||
#include <geom/geom.h> | #include <geom/geom.h> | ||||
#include <geom/uzip/g_uzip.h> | #include <geom/uzip/g_uzip.h> | ||||
#include <geom/uzip/g_uzip_cloop.h> | #include <geom/uzip/g_uzip_cloop.h> | ||||
#include <geom/uzip/g_uzip_softc.h> | #include <geom/uzip/g_uzip_softc.h> | ||||
#include <geom/uzip/g_uzip_dapi.h> | #include <geom/uzip/g_uzip_dapi.h> | ||||
#include <geom/uzip/g_uzip_zlib.h> | #include <geom/uzip/g_uzip_zlib.h> | ||||
#include <geom/uzip/g_uzip_lzma.h> | #include <geom/uzip/g_uzip_lzma.h> | ||||
#ifdef ZSTDIO | |||||
#include <geom/uzip/g_uzip_zstd.h> | |||||
#endif | |||||
#include <geom/uzip/g_uzip_wrkthr.h> | #include <geom/uzip/g_uzip_wrkthr.h> | ||||
#include "opt_geom.h" | |||||
MALLOC_DEFINE(M_GEOM_UZIP, "geom_uzip", "GEOM UZIP data structures"); | MALLOC_DEFINE(M_GEOM_UZIP, "geom_uzip", "GEOM UZIP data structures"); | ||||
FEATURE(geom_uzip, "GEOM read-only compressed disks support"); | FEATURE(geom_uzip, "GEOM read-only compressed disks support"); | ||||
struct g_uzip_blk { | struct g_uzip_blk { | ||||
uint64_t offset; | uint64_t offset; | ||||
uint32_t blen; | uint32_t blen; | ||||
unsigned char last:1; | unsigned char last:1; | ||||
▲ Show 20 Lines • Show All 523 Lines • ▼ Show 20 Lines | if (sc->toc[i].offset < max_offset) { | ||||
backref_to = j; | backref_to = j; | ||||
} else { | } else { | ||||
last_blk = &sc->toc[i]; | last_blk = &sc->toc[i]; | ||||
/* | /* | ||||
* For the "normal blocks" seek forward until we hit | * For the "normal blocks" seek forward until we hit | ||||
* block whose offset is larger than ours and assume | * block whose offset is larger than ours and assume | ||||
* it's going to be the next one. | * it's going to be the next one. | ||||
*/ | */ | ||||
for (j = i + 1; j < sc->nblocks; j++) { | for (j = i + 1; j < sc->nblocks + 1; j++) { | ||||
if (sc->toc[j].offset > max_offset) { | if (sc->toc[j].offset > max_offset) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
sc->toc[i].blen = sc->toc[j].offset - | sc->toc[i].blen = sc->toc[j].offset - | ||||
sc->toc[i].offset; | sc->toc[i].offset; | ||||
if (BLK_ENDS(sc, i) > pp->mediasize) { | if (BLK_ENDS(sc, i) > pp->mediasize) { | ||||
DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u " | DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u " | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | g_uzip_taste(struct g_class *mp, struct g_provider *pp, int flags) | ||||
void *buf; | void *buf; | ||||
struct cloop_header *header; | struct cloop_header *header; | ||||
struct g_consumer *cp; | struct g_consumer *cp; | ||||
struct g_geom *gp; | struct g_geom *gp; | ||||
struct g_provider *pp2; | struct g_provider *pp2; | ||||
struct g_uzip_softc *sc; | struct g_uzip_softc *sc; | ||||
enum { | enum { | ||||
G_UZIP = 1, | G_UZIP = 1, | ||||
G_ULZMA | G_ULZMA, | ||||
G_ZSTD, | |||||
} type; | } type; | ||||
char cloop_version; | |||||
g_trace(G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name); | g_trace(G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name); | ||||
g_topology_assert(); | g_topology_assert(); | ||||
/* Skip providers that are already open for writing. */ | /* Skip providers that are already open for writing. */ | ||||
if (pp->acw > 0) | if (pp->acw > 0) | ||||
return (NULL); | return (NULL); | ||||
Show All 30 Lines | if (buf == NULL) | ||||
goto e2; | goto e2; | ||||
header = (struct cloop_header *) buf; | header = (struct cloop_header *) buf; | ||||
if (strncmp(header->magic, CLOOP_MAGIC_START, | if (strncmp(header->magic, CLOOP_MAGIC_START, | ||||
sizeof(CLOOP_MAGIC_START) - 1) != 0) { | sizeof(CLOOP_MAGIC_START) - 1) != 0) { | ||||
DPRINTF(GUZ_DBG_ERR, ("%s: no CLOOP magic\n", gp->name)); | DPRINTF(GUZ_DBG_ERR, ("%s: no CLOOP magic\n", gp->name)); | ||||
goto e3; | goto e3; | ||||
} | } | ||||
cloop_version = header->magic[CLOOP_OFS_VERSN]; | |||||
switch (header->magic[CLOOP_OFS_COMPR]) { | switch (header->magic[CLOOP_OFS_COMPR]) { | ||||
case CLOOP_COMP_LZMA: | case CLOOP_COMP_LZMA: | ||||
case CLOOP_COMP_LZMA_DDP: | case CLOOP_COMP_LZMA_DDP: | ||||
type = G_ULZMA; | type = G_ULZMA; | ||||
if (header->magic[CLOOP_OFS_VERSN] < CLOOP_MINVER_LZMA) { | if (cloop_version < CLOOP_MINVER_LZMA) { | ||||
DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n", | DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n", | ||||
gp->name)); | gp->name)); | ||||
goto e3; | goto e3; | ||||
} | } | ||||
DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_LZMA image found\n", | DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_LZMA image found\n", | ||||
gp->name)); | gp->name)); | ||||
break; | break; | ||||
case CLOOP_COMP_LIBZ: | case CLOOP_COMP_LIBZ: | ||||
case CLOOP_COMP_LIBZ_DDP: | case CLOOP_COMP_LIBZ_DDP: | ||||
type = G_UZIP; | type = G_UZIP; | ||||
if (header->magic[CLOOP_OFS_VERSN] < CLOOP_MINVER_ZLIB) { | if (cloop_version < CLOOP_MINVER_ZLIB) { | ||||
DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n", | DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n", | ||||
gp->name)); | gp->name)); | ||||
goto e3; | goto e3; | ||||
} | } | ||||
DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_ZLIB image found\n", | DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_ZLIB image found\n", | ||||
gp->name)); | gp->name)); | ||||
break; | break; | ||||
case CLOOP_COMP_ZSTD: | |||||
case CLOOP_COMP_ZSTD_DDP: | |||||
if (cloop_version < CLOOP_MINVER_ZSTD) { | |||||
DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n", | |||||
gp->name)); | |||||
goto e3; | |||||
} | |||||
#ifdef ZSTDIO | |||||
DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_ZSTD image found.\n", | |||||
gp->name)); | |||||
type = G_ZSTD; | |||||
#else | |||||
DPRINTF(GUZ_DBG_ERR, ("%s: GEOM_UZIP_ZSTD image found, but " | |||||
"this kernel was configured with Zstd disabled.\n", | |||||
gp->name)); | |||||
goto e3; | |||||
#endif | |||||
break; | |||||
default: | default: | ||||
DPRINTF(GUZ_DBG_ERR, ("%s: unsupported image type\n", | DPRINTF(GUZ_DBG_ERR, ("%s: unsupported image type\n", | ||||
gp->name)); | gp->name)); | ||||
goto e3; | goto e3; | ||||
} | } | ||||
/* | /* | ||||
* Initialize softc and read offsets. | * Initialize softc and read offsets. | ||||
Show All 23 Lines | #endif | ||||
offsets_read = MIN(total_offsets, | offsets_read = MIN(total_offsets, | ||||
(pp->sectorsize - sizeof(*header)) / sizeof(uint64_t)); | (pp->sectorsize - sizeof(*header)) / sizeof(uint64_t)); | ||||
for (i = 0; i < offsets_read; i++) { | for (i = 0; i < offsets_read; i++) { | ||||
sc->toc[i].offset = be64toh(((uint64_t *) (header + 1))[i]); | sc->toc[i].offset = be64toh(((uint64_t *) (header + 1))[i]); | ||||
sc->toc[i].blen = BLEN_UNDEF; | sc->toc[i].blen = BLEN_UNDEF; | ||||
} | } | ||||
DPRINTF(GUZ_DBG_INFO, ("%s: %u offsets in the first sector\n", | DPRINTF(GUZ_DBG_INFO, ("%s: %u offsets in the first sector\n", | ||||
gp->name, offsets_read)); | gp->name, offsets_read)); | ||||
/* | |||||
* The following invalidates the "header" pointer into the first | |||||
* block's "buf." | |||||
*/ | |||||
header = NULL; | |||||
for (blk = 1; offsets_read < total_offsets; blk++) { | for (blk = 1; offsets_read < total_offsets; blk++) { | ||||
uint32_t nread; | uint32_t nread; | ||||
free(buf, M_GEOM); | free(buf, M_GEOM); | ||||
buf = g_read_data( | buf = g_read_data( | ||||
cp, blk * pp->sectorsize, pp->sectorsize, NULL); | cp, blk * pp->sectorsize, pp->sectorsize, NULL); | ||||
if (buf == NULL) | if (buf == NULL) | ||||
goto e5; | goto e5; | ||||
Show All 15 Lines | DPRINTF(GUZ_DBG_INFO, ("%s: done reading %u block offsets from %u " | ||||
"sectors\n", gp->name, offsets_read, blk)); | "sectors\n", gp->name, offsets_read, blk)); | ||||
if (sc->nblocks != offsets_read) { | if (sc->nblocks != offsets_read) { | ||||
DPRINTF(GUZ_DBG_ERR, ("%s: read %s offsets than expected " | DPRINTF(GUZ_DBG_ERR, ("%s: read %s offsets than expected " | ||||
"blocks\n", gp->name, | "blocks\n", gp->name, | ||||
sc->nblocks < offsets_read ? "more" : "less")); | sc->nblocks < offsets_read ? "more" : "less")); | ||||
goto e5; | goto e5; | ||||
} | } | ||||
if (type == G_UZIP) { | switch (type) { | ||||
case G_UZIP: | |||||
sc->dcp = g_uzip_zlib_ctor(sc->blksz); | sc->dcp = g_uzip_zlib_ctor(sc->blksz); | ||||
} else { | break; | ||||
case G_ULZMA: | |||||
sc->dcp = g_uzip_lzma_ctor(sc->blksz); | sc->dcp = g_uzip_lzma_ctor(sc->blksz); | ||||
} | break; | ||||
if (sc->dcp == NULL) { | #ifdef ZSTDIO | ||||
case G_ZSTD: | |||||
sc->dcp = g_uzip_zstd_ctor(sc->blksz); | |||||
break; | |||||
#endif | |||||
default: | |||||
goto e5; | goto e5; | ||||
} | } | ||||
/* | /* | ||||
* "Fake" last+1 block, to make it easier for the TOC parser to | * The last+1 block was not always initialized by earlier versions of | ||||
* iterate without making the last element a special case. | * mkuzip(8). However, *if* it is initialized, the difference between | ||||
* its offset and the prior block's offset represents the length of the | |||||
* final real compressed block, and this is significant to the | |||||
* decompressor. | |||||
*/ | */ | ||||
if (cloop_version >= CLOOP_MINVER_RELIABLE_LASTBLKSZ && | |||||
sc->toc[sc->nblocks].offset != 0) { | |||||
if (sc->toc[sc->nblocks].offset > pp->mediasize) { | |||||
DPRINTF(GUZ_DBG_ERR, | |||||
("%s: bogus n+1 offset %ju > mediasize %ju\n", | |||||
gp->name, (uintmax_t)sc->toc[sc->nblocks].offset, | |||||
(uintmax_t)pp->mediasize)); | |||||
goto e6; | |||||
} | |||||
} else { | |||||
sc->toc[sc->nblocks].offset = pp->mediasize; | sc->toc[sc->nblocks].offset = pp->mediasize; | ||||
} | |||||
/* Massage TOC (table of contents), make sure it is sound */ | /* Massage TOC (table of contents), make sure it is sound */ | ||||
if (g_uzip_parse_toc(sc, pp, gp) != 0) { | if (g_uzip_parse_toc(sc, pp, gp) != 0) { | ||||
DPRINTF(GUZ_DBG_ERR, ("%s: TOC error\n", gp->name)); | DPRINTF(GUZ_DBG_ERR, ("%s: TOC error\n", gp->name)); | ||||
goto e6; | goto e6; | ||||
} | } | ||||
mtx_init(&sc->last_mtx, "geom_uzip cache", NULL, MTX_DEF); | mtx_init(&sc->last_mtx, "geom_uzip cache", NULL, MTX_DEF); | ||||
mtx_init(&sc->queue_mtx, "geom_uzip wrkthread", NULL, MTX_DEF); | mtx_init(&sc->queue_mtx, "geom_uzip wrkthread", NULL, MTX_DEF); | ||||
bioq_init(&sc->bio_queue); | bioq_init(&sc->bio_queue); | ||||
▲ Show 20 Lines • Show All 97 Lines • Show Last 20 Lines |