summaryrefslogtreecommitdiff
path: root/usr/src/common/fs/decompress.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/common/fs/decompress.c')
-rw-r--r--usr/src/common/fs/decompress.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/usr/src/common/fs/decompress.c b/usr/src/common/fs/decompress.c
new file mode 100644
index 0000000000..8c78302c6f
--- /dev/null
+++ b/usr/src/common/fs/decompress.c
@@ -0,0 +1,308 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Decompression module for stand alone file systems.
+ */
+
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/vnode.h>
+#include <sys/bootvfs.h>
+#include <sys/filep.h>
+#include <zmod/zlib.h>
+
+#ifdef _BOOT
+#include "../common/util.h"
+#else
+#include <sys/sunddi.h>
+#endif
+
+#define MAX_DECOMP_BUFS 8
+#define GZIP_ID_BYTE_1 0x1f
+#define GZIP_ID_BYTE_2 0x8b
+#define GZIP_CM_DEFLATE 0x08
+#define SEEKBUFSIZE 8192
+
+#ifdef _BOOT
+#define dprintf if (cf_debug) printf
+#else
+#define dprintf if (cf_debug) printf
+
+#endif
+
+extern int bootrd_debug;
+extern void *bkmem_alloc(size_t);
+extern void bkmem_free(void *, size_t);
+
+caddr_t scratch_bufs[MAX_DECOMP_BUFS]; /* array of free scratch mem bufs */
+int decomp_bufcnt; /* total no, of allocated decomp bufs */
+int free_dcomp_bufs; /* no. of free decomp bufs */
+char seek_scrbuf[SEEKBUFSIZE]; /* buffer for seeking */
+int cf_debug; /* non-zero enables debug prints */
+
+void *
+cf_alloc(void *opaque, unsigned int items, unsigned int size)
+{
+ fileid_t *filep;
+ unsigned int nbytes;
+ caddr_t ptr;
+
+ filep = (fileid_t *)opaque;
+ nbytes = roundup(items * size, sizeof (long));
+ if (nbytes > (DECOMP_BUFSIZE - filep->fi_dcscrused)) {
+ ptr = bkmem_alloc(nbytes);
+ } else {
+ ptr = &filep->fi_dcscrbuf[filep->fi_dcscrused];
+ filep->fi_dcscrused += nbytes;
+ }
+ bzero(ptr, nbytes);
+ return (ptr);
+}
+
+/*
+ * Decompression scratch memory free routine, does nothing since we free
+ * the entire scratch area all at once on file close.
+ */
+/* ARGSUSED */
+void
+cf_free(void *opaque, void *addr)
+{
+}
+
+/*
+ * Read the first block of the file described by filep and determine if
+ * the file is gzip-compressed. If so, the compressed flag will be set
+ * in the fileid_t struct pointed to by filep and it will be initialized
+ * for doing decompression on reads to the file.
+ */
+int
+cf_check_compressed(fileid_t *filep)
+{
+ unsigned char *filebytes;
+ z_stream *zsp;
+
+ filep->fi_offset = 0;
+ if ((filep->fi_getblock)(filep) == -1)
+ return (-1);
+ filep->fi_offset = 0;
+ filep->fi_count = 0;
+ filep->fi_cfoff = 0;
+ filebytes = (unsigned char *)filep->fi_memp;
+ if (filebytes[0] != GZIP_ID_BYTE_1 || filebytes[1] != GZIP_ID_BYTE_2 ||
+ filebytes[2] != GZIP_CM_DEFLATE)
+ return (0); /* not compressed */
+ filep->fi_flags |= FI_COMPRESSED;
+
+ dprintf("file %s is compressed\n", filep->fi_path);
+
+ /*
+ * Allocate decompress scratch buffer
+ */
+ if (free_dcomp_bufs) {
+ filep->fi_dcscrbuf = scratch_bufs[--free_dcomp_bufs];
+ } else {
+ filep->fi_dcscrbuf = bkmem_alloc(DECOMP_BUFSIZE);
+ decomp_bufcnt++;
+ }
+ filep->fi_dcscrused = 0;
+ zsp = bkmem_alloc(sizeof (*zsp));
+ filep->fi_dcstream = zsp;
+ /*
+ * Initialize the decompression stream
+ */
+ bzero(zsp, sizeof (*zsp));
+ zsp->opaque = filep;
+ zsp->zalloc = cf_alloc;
+ zsp->zfree = cf_free;
+ zsp->avail_in = 0;
+ zsp->next_in = NULL;
+ zsp->avail_out = 0;
+ zsp->next_out = NULL;
+ if (inflateInit2(zsp, -MAX_WBITS) != Z_OK)
+ return (-1);
+ return (0);
+}
+
+/*
+ * If the file described by fileid_t struct at *filep is compressed
+ * free any resources associated with the decompression. (decompression
+ * buffer, etc.).
+ */
+void
+cf_close(fileid_t *filep)
+{
+ if ((filep->fi_flags & FI_COMPRESSED) == 0)
+ return;
+ dprintf("cf_close: %s\n", filep->fi_path);
+ (void) inflateEnd(filep->fi_dcstream);
+ bkmem_free(filep->fi_dcstream, sizeof (z_stream));
+ if (free_dcomp_bufs == MAX_DECOMP_BUFS) {
+ bkmem_free(filep->fi_dcscrbuf, DECOMP_BUFSIZE);
+ } else {
+ scratch_bufs[free_dcomp_bufs++] = filep->fi_dcscrbuf;
+ }
+}
+
+void
+cf_rewind(fileid_t *filep)
+{
+ z_stream *zsp;
+
+ dprintf("cf_rewind: %s\n", filep->fi_path);
+ zsp = filep->fi_dcstream;
+ zsp->avail_in = 0;
+ zsp->next_in = NULL;
+ (void) inflateReset(zsp);
+ filep->fi_cfoff = 0;
+}
+
+#define FLG_FHCRC 0x02 /* crc field present */
+#define FLG_FEXTRA 0x04 /* "extra" field present */
+#define FLG_FNAME 0x08 /* file name field present */
+#define FLG_FCOMMENT 0x10 /* comment field present */
+
+/*
+ * Calculate the header length of a gzip compressed file
+ */
+static int
+cf_headerlen(unsigned char *hp)
+{
+ unsigned char flag;
+ int xlen, hlen = 0;
+
+ hlen += 3; /* skip ID bytes and compression method */
+ flag = hp[hlen];
+ hlen += 7; /* skip flag, mtime(4 bytes), xfl, and os */
+ if (flag & FLG_FEXTRA) {
+ xlen = hp[hlen++];
+ xlen += hp[hlen++] << 8;
+ hlen += xlen; /* skip extra field */
+ }
+ if (flag & FLG_FNAME)
+ hlen += strlen((char *)&hp[hlen]) + 1; /* skip file name */
+ if (flag & FLG_FCOMMENT)
+ hlen += strlen((char *)&hp[hlen]) + 1; /* skip comment */
+ if (flag & FLG_FHCRC)
+ hlen += 2; /* skip crc */
+ return (hlen);
+}
+
+/*
+ * Read at the current uncompressed offset from the compressed file described
+ * by *filep. Will return decompressed data.
+ */
+int
+cf_read(fileid_t *filep, caddr_t buf, size_t count)
+{
+ z_stream *zsp;
+ struct inode *ip;
+ int err = Z_OK;
+ int hlen, infbytes;
+ off_t soff;
+ caddr_t smemp;
+
+ dprintf("cf_read: %s ", filep->fi_path);
+ dprintf("%lx bytes\n", count);
+ zsp = filep->fi_dcstream;
+ ip = filep->fi_inode;
+ dprintf(" reading at offset %lx\n", zsp->total_out);
+ zsp->next_out = (unsigned char *)buf;
+ zsp->avail_out = count;
+ while (zsp->avail_out != 0) {
+ if (zsp->avail_in == 0 && filep->fi_cfoff < ip->i_size) {
+ /*
+ * read a block of the file to inflate
+ */
+ soff = filep->fi_offset;
+ smemp = filep->fi_memp;
+ filep->fi_memp = NULL;
+ filep->fi_offset = filep->fi_cfoff;
+ filep->fi_count = 0;
+ if ((*filep->fi_getblock)(filep) == -1)
+ return (-1);
+ filep->fi_offset = soff;
+ zsp->next_in = (unsigned char *)filep->fi_memp;
+ zsp->avail_in = filep->fi_count;
+ filep->fi_memp = smemp;
+ /*
+ * If we are reading the first block of the file, we
+ * need to skip over the header bytes before inflating
+ */
+ if (filep->fi_cfoff == 0) {
+ hlen = cf_headerlen(zsp->next_in);
+ zsp->next_in += hlen;
+ zsp->avail_in -= hlen;
+ }
+ filep->fi_cfoff += filep->fi_count;
+ }
+ infbytes = zsp->avail_out;
+ dprintf("attempting inflate of %x bytes to buf at: %lx\n",
+ zsp->avail_out, (unsigned long)zsp->next_out);
+ err = inflate(zsp, Z_NO_FLUSH);
+ infbytes -= zsp->avail_out;
+ dprintf("inflated %x bytes, errcode=%d\n", infbytes, err);
+ /*
+ * break out if we hit end of the compressed file
+ * or the end of the compressed byte stream
+ */
+ if (filep->fi_cfoff >= ip->i_size || err == Z_STREAM_END)
+ break;
+ }
+ dprintf("cf_read: returned %lx bytes\n", count - zsp->avail_out);
+ return (count - zsp->avail_out);
+}
+
+/*
+ * Seek to the location specified by addr
+ */
+void
+cf_seek(fileid_t *filep, off_t addr, int whence)
+{
+ z_stream *zsp;
+ int readsz;
+
+ dprintf("cf_seek: %s ", filep->fi_path);
+ dprintf("to %lx\n", addr);
+ zsp = filep->fi_dcstream;
+ if (whence == SEEK_CUR)
+ addr += zsp->total_out;
+ /*
+ * To seek backwards, must rewind and seek forwards
+ */
+ if (addr < zsp->total_out) {
+ cf_rewind(filep);
+ filep->fi_offset = 0;
+ } else {
+ addr -= zsp->total_out;
+ }
+ while (addr > 0) {
+ readsz = MIN(addr, SEEKBUFSIZE);
+ (void) cf_read(filep, seek_scrbuf, readsz);
+ addr -= readsz;
+ }
+}