Commit bc22c17e authored by Alain Knaff's avatar Alain Knaff Committed by H. Peter Anvin

bzip2/lzma: library support for gzip, bzip2 and lzma decompression

Impact: Replaces inflate.c with a wrapper around zlib_inflate; new library code

This is the first part of the bzip2/lzma patch

The bzip patch is based on an idea by Christian Ludwig, includes support for
compressing the kernel with bzip2 or lzma rather than gzip. Both
compressors give smaller sizes than gzip.  Lzma's decompresses faster
than bzip2.

It also supports ramdisks and initramfs' compressed using these two
compressors.

The functionality has been successfully used for a couple of years by
the udpcast project

This version applies to "tip" kernel 2.6.28

This part contains:
- changed inflate.c to accomodate rest of patch
- implementation of bzip2 compression (not used at this stage yet)
- implementation of lzma compression (not used at this stage yet)
- Makefile routines to support bzip2 and lzma kernel compression
Signed-off-by: default avatarAlain Knaff <alain@knaff.lu>
Signed-off-by: default avatarH. Peter Anvin <hpa@zytor.com>
parent 7d3b56ba
#ifndef DECOMPRESS_BUNZIP2_H
#define DECOMPRESS_BUNZIP2_H
int bunzip2(unsigned char *inbuf, int len,
int(*fill)(void*, unsigned int),
int(*flush)(void*, unsigned int),
unsigned char *output,
int *pos,
void(*error)(char *x));
#endif
#ifndef DECOMPRESS_GENERIC_H
#define DECOMPRESS_GENERIC_H
/* Minimal chunksize to be read.
*Bzip2 prefers at least 4096
*Lzma prefers 0x10000 */
#define COMPR_IOBUF_SIZE 4096
typedef int (*decompress_fn) (unsigned char *inbuf, int len,
int(*fill)(void*, unsigned int),
int(*writebb)(void*, unsigned int),
unsigned char *output,
int *posp,
void(*error)(char *x));
/* inbuf - input buffer
*len - len of pre-read data in inbuf
*fill - function to fill inbuf if empty
*writebb - function to write out outbug
*posp - if non-null, input position (number of bytes read) will be
* returned here
*
*If len != 0, the inbuf is initialized (with as much data), and fill
*should not be called
*If len = 0, the inbuf is allocated, but empty. Its size is IOBUF_SIZE
*fill should be called (repeatedly...) to read data, at most IOBUF_SIZE
*/
#endif
#ifndef INFLATE_H
#define INFLATE_H
/* Other housekeeping constants */
#define INBUFSIZ 4096
int gunzip(unsigned char *inbuf, int len,
int(*fill)(void*, unsigned int),
int(*flush)(void*, unsigned int),
unsigned char *output,
int *pos,
void(*error_fn)(char *x));
#endif
/*
* linux/compr_mm.h
*
* Memory management for pre-boot and ramdisk uncompressors
*
* Authors: Alain Knaff <alain@knaff.lu>
*
*/
#ifndef DECOMPR_MM_H
#define DECOMPR_MM_H
#ifdef STATIC
/* Code active when included from pre-boot environment: */
/* A trivial malloc implementation, adapted from
* malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
*/
static unsigned long malloc_ptr;
static int malloc_count;
static void *malloc(int size)
{
void *p;
if (size < 0)
error("Malloc error");
if (!malloc_ptr)
malloc_ptr = free_mem_ptr;
malloc_ptr = (malloc_ptr + 3) & ~3; /* Align */
p = (void *)malloc_ptr;
malloc_ptr += size;
if (free_mem_end_ptr && malloc_ptr >= free_mem_end_ptr)
error("Out of memory");
malloc_count++;
return p;
}
static void free(void *where)
{
malloc_count--;
if (!malloc_count)
malloc_ptr = free_mem_ptr;
}
#define large_malloc(a) malloc(a)
#define large_free(a) free(a)
#define set_error_fn(x)
#define INIT
#else /* STATIC */
/* Code active when compiled standalone for use when loading ramdisk: */
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
/* Use defines rather than static inline in order to avoid spurious
* warnings when not needed (indeed large_malloc / large_free are not
* needed by inflate */
#define malloc(a) kmalloc(a, GFP_KERNEL)
#define free(a) kfree(a)
#define large_malloc(a) vmalloc(a)
#define large_free(a) vfree(a)
static void(*error)(char *m);
#define set_error_fn(x) error = x;
#define INIT __init
#define STATIC
#include <linux/init.h>
#endif /* STATIC */
#endif /* DECOMPR_MM_H */
#ifndef DECOMPRESS_UNLZMA_H
#define DECOMPRESS_UNLZMA_H
int unlzma(unsigned char *, int,
int(*fill)(void*, unsigned int),
int(*flush)(void*, unsigned int),
unsigned char *output,
int *posp,
void(*error)(char *x)
);
#endif
This diff is collapsed.
#ifdef STATIC
/* Pre-boot environment: included */
/* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots
* errors about console_printk etc... on ARM */
#define _LINUX_KERNEL_H
#include "zlib_inflate/inftrees.c"
#include "zlib_inflate/inffast.c"
#include "zlib_inflate/inflate.c"
#else /* STATIC */
/* initramfs et al: linked */
#include <linux/zutil.h>
#include "zlib_inflate/inftrees.h"
#include "zlib_inflate/inffast.h"
#include "zlib_inflate/inflate.h"
#include "zlib_inflate/infutil.h"
#endif /* STATIC */
#include <linux/decompress/mm.h>
#define INBUF_LEN (16*1024)
/* Included from initramfs et al code */
STATIC int INIT gunzip(unsigned char *buf, int len,
int(*fill)(void*, unsigned int),
int(*flush)(void*, unsigned int),
unsigned char *out_buf,
int *pos,
void(*error_fn)(char *x)) {
u8 *zbuf;
struct z_stream_s *strm;
int rc;
size_t out_len;
set_error_fn(error_fn);
rc = -1;
if (flush) {
out_len = 0x8100; /* 32 K */
out_buf = malloc(out_len);
} else {
out_len = 0x7fffffff; /* no limit */
}
if (!out_buf) {
error("Out of memory while allocating output buffer");
goto gunzip_nomem1;
}
if (buf)
zbuf = buf;
else {
zbuf = malloc(INBUF_LEN);
len = 0;
}
if (!zbuf) {
error("Out of memory while allocating input buffer");
goto gunzip_nomem2;
}
strm = malloc(sizeof(*strm));
if (strm == NULL) {
error("Out of memory while allocating z_stream");
goto gunzip_nomem3;
}
strm->workspace = malloc(flush ? zlib_inflate_workspacesize() :
sizeof(struct inflate_state));
if (strm->workspace == NULL) {
error("Out of memory while allocating workspace");
goto gunzip_nomem4;
}
if (len == 0)
len = fill(zbuf, INBUF_LEN);
/* verify the gzip header */
if (len < 10 ||
zbuf[0] != 0x1f || zbuf[1] != 0x8b || zbuf[2] != 0x08) {
if (pos)
*pos = 0;
error("Not a gzip file");
goto gunzip_5;
}
/* skip over gzip header (1f,8b,08... 10 bytes total +
* possible asciz filename)
*/
strm->next_in = zbuf + 10;
/* skip over asciz filename */
if (zbuf[3] & 0x8) {
while (strm->next_in[0])
strm->next_in++;
strm->next_in++;
}
strm->avail_in = len - 10;
strm->next_out = out_buf;
strm->avail_out = out_len;
rc = zlib_inflateInit2(strm, -MAX_WBITS);
if (!flush) {
WS(strm)->inflate_state.wsize = 0;
WS(strm)->inflate_state.window = NULL;
}
while (rc == Z_OK) {
if (strm->avail_in == 0) {
/* TODO: handle case where both pos and fill are set */
len = fill(zbuf, INBUF_LEN);
if (len < 0) {
rc = -1;
error("read error");
break;
}
strm->next_in = zbuf;
strm->avail_in = len;
}
rc = zlib_inflate(strm, 0);
/* Write any data generated */
if (flush && strm->next_out > out_buf) {
int l = strm->next_out - out_buf;
if (l != flush(out_buf, l)) {
rc = -1;
error("write error");
break;
}
strm->next_out = out_buf;
strm->avail_out = out_len;
}
/* after Z_FINISH, only Z_STREAM_END is "we unpacked it all" */
if (rc == Z_STREAM_END) {
rc = 0;
break;
} else if (rc != Z_OK) {
error("uncompression error");
rc = -1;
}
}
zlib_inflateEnd(strm);
if (pos)
/* add + 8 to skip over trailer */
*pos = strm->next_in - zbuf+8;
gunzip_5:
free(strm->workspace);
gunzip_nomem4:
free(strm);
gunzip_nomem3:
if (!buf)
free(zbuf);
gunzip_nomem2:
if (flush)
free(out_buf);
gunzip_nomem1:
return rc; /* returns Z_OK (0) if successful */
}
#define decompress gunzip
This diff is collapsed.
#ifndef INFLATE_H
#define INFLATE_H
/* inflate.h -- internal inflate state definition
* Copyright (C) 1995-2004 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
......@@ -105,3 +108,4 @@ struct inflate_state {
unsigned short work[288]; /* work area for code table building */
code codes[ENOUGH]; /* space for code tables */
};
#endif
#ifndef INFTREES_H
#define INFTREES_H
/* inftrees.h -- header to use inftrees.c
* Copyright (C) 1995-2005 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
......@@ -53,3 +56,4 @@ typedef enum {
extern int zlib_inflate_table (codetype type, unsigned short *lens,
unsigned codes, code **table,
unsigned *bits, unsigned short *work);
#endif
......@@ -186,3 +186,17 @@ quiet_cmd_gzip = GZIP $@
cmd_gzip = gzip -f -9 < $< > $@
# Bzip2
# ---------------------------------------------------------------------------
# Bzip2 does not include size in file... so we have to fake that
size_append=$(CONFIG_SHELL) $(srctree)/scripts/bin_size
quiet_cmd_bzip2 = BZIP2 $@
cmd_bzip2 = (bzip2 -9 < $< ; $(size_append) $<) > $@ || (rm -f $@ ; false)
# Lzma
# ---------------------------------------------------------------------------
quiet_cmd_lzma = LZMA $@
cmd_lzma = (lzma -9 -c $< ; $(size_append) $<) >$@ || (rm -f $@ ; false)
#!/bin/sh
if [ $# = 0 ] ; then
echo Usage: $0 file
fi
size_dec=`stat -c "%s" $1`
size_hex_echo_string=`printf "%08x" $size_dec |
sed 's/\(..\)\(..\)\(..\)\(..\)/\\\\x\4\\\\x\3\\\\x\2\\\\x\1/g'`
/bin/echo -ne $size_hex_echo_string
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment