/*
 * GZIP Compression functions for kernel crash dumps.
 *
 * Created by: Matt Robinson (yakker@sourceforge.net)
 * Copyright 2001 Matt D. Robinson.  All rights reserved.
 *
 * This code is released under version 2 of the GNU GPL.
 */

/* header files */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/dump.h>
#include "dump_zlib.c"

/*
 * -----------------------------------------------------------------------
 *                       D E F I N I T I O N S
 * -----------------------------------------------------------------------
 */
#define DUMP_MODULE_NAME "dump_gzip"
#define DUMP_PRINTN(format, args...) \
	printk("%s: " format , DUMP_MODULE_NAME , ## args);
#define DUMP_PRINT(format, args...) \
	printk(format , ## args);

/*
 * Name: dump_compress_gzip_alloc()
 * Func: The kmalloc function for gzip dump compression.
 */
static void *
dump_compress_gzip_alloc(void *arg, unsigned int items, unsigned int size)
{
	return ((void *)kmalloc((size * items), GFP_ATOMIC));
}

/*
 * Name: dump_compress_gzip_free()
 * Func: The kfree function for gzip dump compression.
 */
static void
dump_compress_gzip_free(void *arg, void *ptr)
{
	kfree(ptr);
	return;
}

/*
 * Name: dump_compress_gzip()
 * Func: Compress a DUMP_PAGE_SIZE page using gzip-style algorithms (the.
 *       deflate functions similar to what's used in PPP).
 */
static int
dump_compress_gzip(char *old, int oldsize, char *new, int newsize)
{
	/* error code and dump stream */
	int err;
	z_stream dump_stream;

	/* setup the alloc/free functions */
	dump_stream.zalloc = dump_compress_gzip_alloc;
	dump_stream.zfree = dump_compress_gzip_free;
	dump_stream.opaque = (void *)0;

	if ((err = deflateInit(&dump_stream, Z_BEST_COMPRESSION)) != Z_OK) {
		/* fall back to RLE compression */
		DUMP_PRINT("dump_compress_gzip(): deflateInit() "
			"failed (%d)!\n", err);
		return (0);
	}

	/* use old (page of memory) and size (DUMP_PAGE_SIZE) as in-streams */
	dump_stream.next_in = old;
	dump_stream.avail_in = oldsize;

	/* out streams are new (dpcpage) and new size (DUMP_DPC_PAGE_SIZE) */
	dump_stream.next_out = new;
	dump_stream.avail_out = newsize;

	/* deflate the page -- check for error */
	err = deflate(&dump_stream, Z_FINISH);
	if (err != Z_STREAM_END) {
		/* zero is return code here */
		(void)deflateEnd(&dump_stream);
		DUMP_PRINT("dump_compress_gzip(): deflate() failed (%d)!\n",
			err);
		return (0);
	}

	/* let's end the deflated compression stream */
	if ((err = deflateEnd(&dump_stream)) != Z_OK) {
		DUMP_PRINT("dump_compress_gzip(): deflateEnd() "
			"failed (%d)!\n", err);
	}

	/* return the compressed byte total (if it's smaller) */
	if (dump_stream.total_out >= oldsize) {
		return (oldsize);
	}
	return (dump_stream.total_out);
}

/* setup the gzip compression functionality */
static dump_compress_t dump_gzip_compression = {
	compress_type:	DUMP_COMPRESS_GZIP,
	compress_func:	dump_compress_gzip,
};

/*
 * Name: dump_compress_gzip_init()
 * Func: Initialize gzip as a compression mechanism.
 */
int __init
dump_compress_gzip_init(void)
{
	dump_register_compression(&dump_gzip_compression);
	return (0);
}

/*
 * Name: dump_compress_gzip_cleanup()
 * Func: Remove gzip as a compression mechanism.
 */
void __init
dump_compress_gzip_cleanup(void)
{
	dump_unregister_compression(DUMP_COMPRESS_GZIP);
}

/* module initialization */
module_init(dump_compress_gzip_init);
module_exit(dump_compress_gzip_cleanup);
