--- linux-2.6.25/Documentation/filesystems/e2compress.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/Documentation/filesystems/e2compress.txt	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,117 @@
+Transparent compression for ext2 filesystem
+===========================================
+
+What this document is.
+----------------------
+This document is intended for explaining how e2compress has been implented/ported
+in kernel 2.4. It also give a status of current work. You need to have e2compress
+knowledge (i.e. to know how e2compress works, from a general point of view)
+
+What this document is not.
+--------------------------
+This document is not a full explaination of how e2compress work. For this,
+there are other documents such as fs/ext2/Readme.e2compr file for the technical
+point of view and user manual can be found at <http://e2compr.sourceforge.net/>.
+This site is also a place were you will find many information about e2compress
+development for kernel 2.4, tools, manuals and so on.
+
+
+Introduction
+============
+
+This is a first adaptation of e2compress for kernel 2.4. The work has been done
+by Alcatel (Alcatel Business Systems - R&D) at Illkirch. It has been started
+from the latest patch provided by Peter Moulder for kernel 2.2,
+i.e. e2compr-0.4.39-patch-2.2.18.
+It is full compatible with previous version.
+Here after you will first find some explainations about the choices mades for
+the development, and then the status of current work from functionnal point of
+view.
+
+
+Development
+===========
+
+As for previous patches, most interesting happens when reading in ext2_readpage
+and when writing in ext2_writepage and ext2_file_write.
+In fact, in 2.2 kernel, compression occures on cluster of blocks. So when reading
+or writing a part of a file, we first have to compute the cluster on which I/O
+occures, then we have to get every buffers of the cluster, uncompress the data if
+needed, then reading/writing happens "as for normal files".
+In 2.4 kernels, I/O occures through page cache: i.e. when reading/writing to a
+part of the file, first the corresponding page is get, we then get the needed
+buffers, which point to the page; this means that for keeping same work as for 2.2,
+we have to use the notion of cluster of page. For getting every buffers of a cluster,
+we first get every pages of the cluster, then get buffers of every pages...
+
+So, things happens as follow:
+
+ext2_readpage
+-------------
+
+If data corresponding to the page are in a compressed cluster, this functions perfoms
+more works: instead of reading one page, it reads the whole "cluster of pages".
+In fact, anyway, we have to read all compressed buffer. Once we have got all buffers
+of the cluster, uncompressed (at least a part of) the data, and located the part of
+the uncompressed data which correspond to the requested page, there is not any more 
+lot of work for also reading (i.e. doing some memcpy) other pages belonging to this
+cluster.
+So, the first reading of the first page of the cluster is quite longer, but then,
+every pages of the cluster are uptodate in the cache.
+
+ext2_writepage
+--------------
+An overhead has been added for pages belonging to a compressed cluster.
+In fact, if cluster is still compressed on the disk, we can't directly write the
+page (which contains uncompressed data) in the middle of a compressed cluster.
+So, we first have to uncompress the whole cluster on the disk, then we can write the
+new data of the dirty page(s).
+
+ext2_file_write
+---------------
+This replaces `generic_file_write' when e2compress option is activated.
+It is a copy of `generic_file_write'. The main difference is that instead of looping
+page by page in `generic_file_write', we loops on cluster of page.
+In each loop:
+	* we compute the cluster on which beginning of data (to be written) belongs to.
+	* then, we get all pages of the cluster.
+	* If cluster is a compressed one, we read all pages, and uncompress it.
+	  Otherwise, we perfoms a `prepare_write' (as in generic_file_write).
+	* We copy the data on each page from user space,
+	* Call `commit_write' on dirty pages.
+	* When reaching end of cluster, we compress it. (As in 2.2)
+
+Note: Another implentation could have been to keep generic_file_write, and add an overhead
+to `ext2_prepare_write' and `ext2_commit_write'; on the first access to a page of a compressed
+cluster, whole cluster will be uncompressed (i.e. all pages of the cluster will be read and
+uncompressed in `ext2_prepare_write') and when commiting the last page of the cluster,
+compression occures...
+
+ext2_open_file
+--------------
+In 2.4.16 kernel, this function has been added for treating the case of files opened for
+"direct IO". Direct IO is not supported on compressed file. So opening a file by this way
+is forbidden.
+
+Other places in ext2
+--------------------
+Other changes occures as in 2.2 for managing the compression flags of files and specific
+`COMPRESSED_BLK_ADDR' address for compressed blocks.
+So please, refer to existing documentation for 2.2 about this topic.
+
+Status
+======
+Today (middle of december 2001), e2compress on kernel 2.4.16 has been tested on i386
+architecture, is used with success by tens of people in the department from some weeks.
+It is full fonctionnal on ix86, full compatible with 2.2 version of e2compress.
+It should work on other architecture, but has NOT been tested.
+Please, note the following:
+	* No performance tests have been done.
+	* I don't proclaim that code is optimized (and it is probably not, but I hope that
+	  "gurus" will not find it too bad)
+So, I think I can say that there is no known "big" bug or "blocking" bug.
+
+Some strange things has been observed in very limit case, i.e. when memory is overloaded.
+
+
+As usual, this e2compress comes without warranty, use it at your won risk, etc...
--- linux-2.6.25/Documentation/filesystems/ext2_compression	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/Documentation/filesystems/ext2_compression	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,42 @@
+Release notes
+=============
+
+(These are sorted by version so that you don't have to reread things.
+However, all of them are still important, or else I'd have removed
+them.)
+
+
+New with 0.4.5
+--------------
+
+Adapted the 2.6.10 kernel patch to  2.6.18.5 (patched Vector Linux kernel).
+NOTE: Post 2.6.10 kernel changed from a semaphore in the inode data structure
+to a mutex. See fs/ext2/ChangeLog.e2compr for more details. Added ifdef code
+to force real-time write of inode data to disk in support of mounting USB 
+thumbdrives as root file system. TLL Mar 6, 2007.
+
+
+New with 0.4.3
+--------------
+
+If you get many many compression error messages in your kernel system
+logs, then it is probably an error in the error checking rather than
+in the de/compression algorithms.
+
+
+New with 0.4.0
+--------------
+
+Starting with e2compr-0.4, you must reply `y' to CONFIG_EXPERIMENTAL
+if you wish to be asked for ext2 compression.  (This is in readiness
+for inclusion into the standard kernel.)
+
+Don't rely on `chattr +X' (or the corresponding ioctl).  That
+interface is broken.  It doesn't guarantee exclusivity for any kernel
+after 2.1.42, so I've mostly removed support for it here.  (I still
+keep it partially available for diagnostic purposes.)  A replacement
+interface will probably arrive later.  If you wish to implement it
+yourself, write to me <pjm@bofh.asn.au> and I'll send you my
+plans for how to implement it.  (I don't mind how you implement it,
+but I can at least inform you of the various race conditions etc. to
+avoid.)
--- linux-2.6.25/fs/Makefile	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/Makefile	2008-07-31 15:13:12.000000000 +0200
@@ -65,6 +65,11 @@ obj-y				+= devpts/
 obj-$(CONFIG_PROFILING)		+= dcookies.o
 obj-$(CONFIG_DLM)		+= dlm/
  
+ifneq ($(CONFIG_EXT2_FS),n)
+ifneq ($(CONFIG_EXT2_FS),)
+obj-m				+= ext2/
+endif
+endif
 # Do not add any filesystems before this line
 obj-$(CONFIG_REISERFS_FS)	+= reiserfs/
 obj-$(CONFIG_EXT3_FS)		+= ext3/ # Before ext2 so root fs can be ext3
--- linux-2.6.25/fs/fcntl.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/fcntl.c	2008-07-31 15:13:12.000000000 +0200
@@ -24,6 +24,13 @@
 #include <asm/siginfo.h>
 #include <asm/uaccess.h>
 
+#ifdef CONFIG_EXT2_COMPRESS	
+//mw: deny O_DIRECT on file with compression
+#include <linux/ext2_fs.h>
+#include "ext2/ext2.h"
+#include "ext2/assert.h"
+#endif
+
 void set_close_on_exec(unsigned int fd, int flag)
 {
 	struct files_struct *files = current->files;
@@ -231,6 +238,14 @@ static int setfl(int fd, struct file * f
 		if (!filp->f_mapping || !filp->f_mapping->a_ops ||
 			!filp->f_mapping->a_ops->direct_IO)
 				return -EINVAL;
+
+#ifdef CONFIG_EXT2_COMPRESS			
+		//mw: if we have a compressed ext2 file: deny!
+		//    TODO: maybe check fs-type first!
+		assert(!(EXT2_I(inode)->i_flags & (EXT2_COMPR_FL|EXT2_COMPRBLK_FL)));
+		if (EXT2_I(inode)->i_flags & (EXT2_COMPR_FL|EXT2_COMPRBLK_FL))
+			return -EINVAL;
+#endif		
 	}
 
 	if (filp->f_op && filp->f_op->check_flags)
--- linux-2.6.25/fs/ext2/Makefile	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/ext2/Makefile	2008-07-31 15:13:12.000000000 +0200
@@ -2,10 +2,21 @@
 # Makefile for the linux ext2-filesystem routines.
 #
 
+ifeq ($(CONFIG_EXT2_COMPRESS),y)
+
+obj-$(CONFIG_EXT2_HAVE_BZIP2)		+= bzip2/
+obj-$(CONFIG_EXT2_HAVE_LZO)		+= lzo/
+obj-$(CONFIG_EXT2_HAVE_LZV1)		+= lzv1/
+obj-$(CONFIG_EXT2_HAVE_LZRW3A)		+= lzrw3a/
+
+COMPRESS_STUFF := adler32.o compress.o none.o compress_syms.o e2zlib.o \
+		  $($(obj-y):%/=%/ext2-compr-%.o)
+endif
+
 obj-$(CONFIG_EXT2_FS) += ext2.o
 
 ext2-y := balloc.o dir.o file.o fsync.o ialloc.o inode.o \
-	  ioctl.o namei.o super.o symlink.o
+ 	  ioctl.o namei.o super.o symlink.o $(COMPRESS_STUFF)
 
 ext2-$(CONFIG_EXT2_FS_XATTR)	 += xattr.o xattr_user.o xattr_trusted.o
 ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o
--- linux-2.6.25/fs/ext2/super.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/ext2/super.c	2008-08-01 16:02:46.000000000 +0200
@@ -16,6 +16,19 @@
  *        David S. Miller (davem@caip.rutgers.edu), 1995
  */
 
+/*
+ *  Copyright (C) 1995  Antoine Dumesnil de Maricourt (dumesnil@etca.fr)
+ *    (transparent compression code)
+ */
+
+/*
+ *  Copyright (C) 2001 Alcatel Business Systems - R&D Illkirch
+ *    (transparent compression code)
+ *  Pierre Peiffer (pierre.peiffer@sxb.bsf.alcatel.fr),
+ *  Denis Richard (denis.richard@sxb.bsf.alcatel.fr)
+ *  Adapted from patch e2compr-0.4.39-patch-2.2.18 .
+ */
+
 #include <linux/module.h>
 #include <linux/string.h>
 #include <linux/fs.h>
@@ -32,7 +45,8 @@
 #include <linux/mount.h>
 #include <linux/log2.h>
 #include <asm/uaccess.h>
-#include "ext2.h"
+#include <linux/vmalloc.h>
+#include <linux/ext2_fs_c.h>
 #include "xattr.h"
 #include "acl.h"
 #include "xip.h"
@@ -108,6 +122,14 @@ void ext2_update_dynamic_rev(struct supe
 	 */
 }
 
+#ifdef CONFIG_EXT2_COMPRESS
+/*
+ *    A reference count for the working area used by
+ *    compression code.
+ */
+static int ext2_wa_ref = 0;
+#endif
+
 static void ext2_put_super (struct super_block * sb)
 {
 	int db_count;
@@ -134,6 +156,21 @@ static void ext2_put_super (struct super
 	sb->s_fs_info = NULL;
 	kfree(sbi);
 
+#ifdef CONFIG_EXT2_COMPRESS
+	if (--ext2_wa_ref == 0) {
+		if (ext2_rd_wa != NULL) {
+			vfree (ext2_rd_wa);
+			ext2_rd_wa = NULL;
+		}
+#ifdef CONFIG_EXT2_SEPARATE_WORK_AREAS
+		if (ext2_wr_wa != NULL) {
+			vfree (ext2_wr_wa);
+			ext2_wr_wa = NULL;
+		}
+# endif
+	}
+#endif
+
 	return;
 }
 
@@ -389,7 +426,11 @@ enum {
 	Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic,
 	Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug,
 	Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr,
-	Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, Opt_quota,
+	Opt_acl, Opt_noacl,
+#ifdef CONFIG_EXT2_COMPRESS
+ 	Opt_force_compat,
+#endif
+	Opt_xip, Opt_ignore, Opt_err, Opt_quota,
 	Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation
 };
 
@@ -422,6 +463,9 @@ static match_table_t tokens = {
 	{Opt_ignore, "noquota"},
 	{Opt_quota, "quota"},
 	{Opt_usrquota, "usrquota"},
+#ifdef CONFIG_EXT2_COMPRESS
+ 	{Opt_force_compat, "force-compat"},
+#endif
 	{Opt_reservation, "reservation"},
 	{Opt_noreservation, "noreservation"},
 	{Opt_err, NULL}
@@ -564,6 +608,11 @@ static int parse_options (char * options
 			clear_opt(sbi->s_mount_opt, RESERVATION);
 			printk("reservations OFF\n");
 			break;
+#ifdef CONFIG_EXT2_COMPRESS
+ 		case Opt_force_compat:
+ 			set_opt(sbi->s_mount_opt, FORCE_COMPAT);
+ 			break;
+#endif
 		case Opt_ignore:
 			break;
 		default:
@@ -586,7 +635,11 @@ static int ext2_setup_super (struct supe
 		res = MS_RDONLY;
 	}
 	if (read_only)
+#ifdef CONFIG_EXT2_COMPRESS
+		goto readonly;
+#else /* CONFIG_EXT2_COMPRESS */
 		return res;
+#endif /* CONFIG_EXT2_COMPRESS */
 	if (!(sbi->s_mount_state & EXT2_VALID_FS))
 		printk ("EXT2-fs warning: mounting unchecked fs, "
 			"running e2fsck is recommended\n");
@@ -615,6 +668,107 @@ static int ext2_setup_super (struct supe
 			EXT2_BLOCKS_PER_GROUP(sb),
 			EXT2_INODES_PER_GROUP(sb),
 			sbi->s_mount_opt);
+
+#ifdef CONFIG_EXT2_COMPRESS
+	/* Try to allocate the working area for compression. */
+	/* (Maybe this should be moved to compress.c.) */
+# include "e2compr_version.h"
+#ifdef CONFIG_EXT2_COMPRESS
+ readonly:
+#endif /* CONFIG_EXT2_COMPRESS */
+# ifdef CONFIG_EXT2_SEPARATE_WORK_AREAS
+	if ((ext2_rd_wa == NULL) || (ext2_wr_wa == NULL)) {
+		size_t rsize = 0;
+		size_t wsize = 0;
+		int	 i;
+
+#ifdef CONFIG_HIGHMEM
+		printk (KERN_INFO "e2compr " E2COMPR_VERSION " with high-mem support, separate working areas.\n");
+#else
+		printk (KERN_INFO "e2compr " E2COMPR_VERSION " without high-mem support, separate working areas.\n");
+#endif
+		for (i = 0; i < EXT2_N_ALGORITHMS; i++) {
+			if (ext2_algorithm_table[i].avail) {
+				size_t hsize;
+
+				hsize = ext2_algorithm_table[i].init(
+						EXT2_ALG_INIT_COMPRESS);
+				if (wsize < hsize)
+					wsize = hsize;
+				hsize = ext2_algorithm_table[i].init(
+						EXT2_ALG_INIT_DECOMPRESS);
+				if (rsize < hsize)
+					rsize = hsize;
+			}
+		}
+		rsize += 2 * EXT2_MAX_CLUSTER_BYTES;
+		ext2_rd_wa = vmalloc (rsize);
+		if (ext2_rd_wa == NULL)
+			printk ("EXT2-fs: can't allocate working area; "
+				"compression turned off.\n");
+		else {
+			ext2_rd_wa_size = rsize - 2 * EXT2_MAX_CLUSTER_BYTES;
+#  ifdef EXT2_COMPR_REPORT_WA
+			printk (KERN_INFO "EXT2-fs: rd_wa=%p--%p (%d)\n",
+				ext2_rd_wa, (char *)ext2_rd_wa + rsize, rsize);
+#  endif
+		}
+		wsize += 2 * EXT2_MAX_CLUSTER_BYTES;
+		ext2_wr_wa = vmalloc (wsize);
+		if (ext2_wr_wa == NULL)
+			printk ("EXT2-fs: can't allocate working area; "
+				"compression turned off.\n");
+		else {
+			ext2_wr_wa_size = wsize - 2 * EXT2_MAX_CLUSTER_BYTES;
+#  ifdef EXT2_COMPR_REPORT_WA
+			printk (KERN_INFO "EXT2-fs: wr_wa=%p--%p (%d)\n",
+				ext2_wr_wa, (char *)ext2_wr_wa + wsize, wsize);
+#  endif
+		}
+	}
+# else /* ! CONFIG_EXT2_SEPARATE_WORK_AREAS */
+	if (ext2_rd_wa == NULL) {
+		size_t max_hsize = 0;
+		int	 i;
+
+#ifdef CONFIG_HIGHMEM
+		printk (KERN_INFO "e2compr " E2COMPR_VERSION " with high-mem support, single working area.\n");
+#else
+		printk (KERN_INFO "e2compr " E2COMPR_VERSION " without high-mem support, single working area.\n");
+#endif
+		for (i = 0; i < EXT2_N_ALGORITHMS; i++) {
+			if (ext2_algorithm_table[i].avail) {
+				size_t hsize;
+
+				hsize = ext2_algorithm_table[i].init(
+						EXT2_ALG_INIT_COMPRESS);
+				if (max_hsize < hsize)
+					max_hsize = hsize;
+				hsize = ext2_algorithm_table[i].init(
+						EXT2_ALG_INIT_DECOMPRESS);
+				if (max_hsize < hsize)
+					max_hsize = hsize;
+			}
+		}
+		max_hsize += 2 * EXT2_MAX_CLUSTER_BYTES;
+		ext2_rd_wa = vmalloc (max_hsize);
+		if (ext2_rd_wa == NULL)
+			printk ("EXT2-fs: can't allocate working area; "
+				"compression turned off.\n");
+		else {
+			ext2_rd_wa_size = (max_hsize
+					   - 2 * EXT2_MAX_CLUSTER_BYTES);
+#  ifdef EXT2_COMPR_REPORT
+			printk (KERN_INFO "EXT2-fs: wa=%p--%p (%d)\n",
+				ext2_rd_wa, (char *)ext2_rd_wa + max_hsize,
+				max_hsize);
+#  endif
+		}
+	}
+# endif
+	ext2_wa_ref++;
+#endif
+
 	return res;
 }
 
@@ -851,18 +1005,107 @@ static int ext2_fill_super(struct super_
 	 */
 	features = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP);
 	if (features) {
+		if (test_opt(sb, FORCE_COMPAT)) {
+			printk("EXT2-fs: %s: has incompatibilities %#x "
+			    "but ignoring as you request.\n",
+			    sb->s_id, (le32_to_cpu(es->s_feature_incompat)
+			    & ~EXT2_FEATURE_INCOMPAT_SUPP));
+			/*
+			 * todo: Maybe convert the number to a list of
+			 * strings, e.g. "compression".
+			 */
+		} else {
 		printk("EXT2-fs: %s: couldn't mount because of "
-		       "unsupported optional features (%x).\n",
-		       sb->s_id, le32_to_cpu(features));
+			    "unsupported optional features (%x).\n", sb->s_id,
+			    le32_to_cpu(features));
+		goto failed_mount;
+	}
+	}
+#ifdef CONFIG_EXT2_COMPRESS
+	/* Check that required algorithms are available. */
+	/* todo: Provide a mount option to override this. */
+	/*
+	 * Philosophical bug: we assume that an algorithm's
+	 * module is available if and only if this kernel was
+	 * compiled with that algorithm as a module.  This is
+	 * untrue, but it is unclear what the right thing to
+	 * do is.
+	 */
+	j = 0; /* error flag */
+	if ((es->s_feature_incompat
+	    & cpu_to_le32(EXT2_FEATURE_INCOMPAT_COMPRESSION))
+	    && (es->s_algorithm_usage_bitmap
+	    & ~cpu_to_le32(EXT2_ALGORITHMS_SUPP))) {
+		/*
+		 * The filesystem employs an algorithm not
+		 * supported by this filesystem.  Issue warning or
+		 * error.
+		 */
+		for (i = 0; i < 32; i++) {
+			if (!(es->s_algorithm_usage_bitmap
+			    & cpu_to_le32(1 << i))
+			    || ((EXT2_ALGORITHMS_SUPP
+			    & (1 << i))))
+				continue;
+		/*
+		 * TODO: Can't this message be moved outside
+		 * of the for loop?
+		 */
+		if (!j) {
+			if (test_opt(sb, FORCE_COMPAT))
+				printk(KERN_WARNING
+				    "EXT2-fs: %s: "
+				    "uses unsupported "
+				    "compression algorithms",
+				    sb->s_id);
+			else
+				printk("EXT2-fs: %s: couldn't mount "
+				    "because of unsupported "
+				    "compression algorithms",
+				    sb->s_id);
+			j = 1;
+		}
+		if (i < EXT2_N_ALGORITHMS)
+			printk(" %s", ext2_algorithm_table[i].name);
+		else
+			printk(" %u", i);
+		}
+	}
+	if (j) {
+		if (test_opt(sb, FORCE_COMPAT))
+			printk(" but ignoring as you request.\n");
+		else {
+			printk(".\n");
 		goto failed_mount;
 	}
+	}
+#endif /* CONFIG_EXT2_COMPRESS */
 	if (!(sb->s_flags & MS_RDONLY) &&
 	    (features = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){
+		if (test_opt(sb, FORCE_COMPAT)) {
+			printk(KERN_WARNING
+			    "EXT2-fs: %s: contains unsupported optional "
+			    "features %#x, but ignoring as you request.\n",
+			    sb->s_id,
+			    (le32_to_cpu(es->s_feature_ro_compat)
+			    & ~EXT2_FEATURE_RO_COMPAT_SUPP));
+			/*
+			 * todo: Maybe print a list of strings
+			 * (e.g. "sparse_super") rather than
+			 * the bitmap.
+			 */
+		} else {
 		printk("EXT2-fs: %s: couldn't mount RDWR because of "
 		       "unsupported optional features (%x).\n",
 		       sb->s_id, le32_to_cpu(features));
 		goto failed_mount;
 	}
+	}
+#if 0
+	/* Standard ext2 doesn't set s_blocksize_bits!  Is this a bug? */
+	sb->s_blocksize_bits = BLOCK_SIZE_BITS +
+	    le32_to_cpu(sbi->s_es->s_log_block_size);
+#endif
 
 	blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
 
--- linux-2.6.25/fs/ext2/Readme.e2compr	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/Readme.e2compr	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,511 @@
+
+  0. Introduction
+  ~~~~~~~~~~~~~~~
+
+This file gives some technical information on e2compr and how it's
+implemented.  
+
+More general information on e2compr can be found at
+http://e2compr.sourceforge.net/.
+
+The first couple of sections of this document are written for those
+who have no interest in the source code but just want to know enough
+to be able to predict and understand e2compr behaviour and its
+implications.
+
+Section 3 describes the e2compr-specific ext2 attributes for a file
+(i.e. chattr things).
+
+Section 4 describes the e2compr ioctls from the point of view of a
+user-mode C programmer.
+
+Section 5 gives more detail about the file format on disk.
+
+Section 6 gives details on what's written where, i.e. a map of e2compr
+code in the kernel.
+
+
+Authorship: section 2 is written mainly by Antoine; the remainder is
+written by Peter.
+
+Questions should be sent to the e2compr mailing list,
+e2compr-misc@lists.sourceforge.net, or to the current maintainers,
+bothie@users.sourceforge.net and whitpa@users.sourceforge.net.
+
+
+  1. The idea
+  ~~~~~~~~~~~
+
+See section `E2compr implementation' in the main e2compr texinfo
+documentation for an introduction to how e2compr works.  (Type
+`info "(e2compr)Implementation"' at the shell prompt.)  It was
+originally written as part of the file you're now reading.
+
+
+  2. More details
+  ~~~~~~~~~~~~~~~
+
+Every compressed file stores its cluster size in the inode structure
+(in the ext2 attribute flags field).
+This (the cluster size) is the most important information: when
+knowing the cluster size, we can convert a block number into a cluster
+number, get the cluster the block belongs to, and then get the block.
+The inode's flags field also keeps the algorithm that is used to compress data
+written to the file.
+
+(The algorithm that was used to compress a given
+cluster is stored in the cluster head near the beginning of the
+compressed data.  This may differ from the current algorithm
+identified in the inode, which is only used to determine which
+algorithm to use at the time clusters are written.)
+
+The algorithm id and the cluster size are stored in the i_flags field
+(thus reducing the number of possible flags).  We also create some new
+flags: the COMPRBLK flags tells if there is at least one compressed
+cluster in the file, the ECOMPR flag indicates that an error (related
+to compression) occurred while reading from or writing to this file.
+If it is set, the file becomes read-only.  (In previous releases, you
+were denied even read access to the file unless you set the NOCOMPR
+flag.  There might be some benefit in returning to the old behaviour
+if decompressing erroneous data can cause an OOPS, but I think it
+would be better to correct the decompressors.  Others may disagree,
+pointing out that it costs CPU time to check for incorrect data.)
+
+Beside the information stored into the inode, each cluster holds some
+data.  Here is the cluster_head structure for e2compr-0.4:
+
+struct ext2_cluster_head {
+  __u16 magic;		/* == EXT2_COMPRESS_MAGIC_04X. */
+  __u8  method;		/* compression method id. */
+  __u8  holemap_nbytes;	/* length of holemap[] array */
+  __u32 checksum;	/* adler32 checksum.  Checksum covers all fields
+			   below this one, and the compressed data. */
+  __u32 ulen;		/* size of uncompressed data */
+  __u32 clen;		/* size of compressed data (excluding cluster head) */
+  __u8  holemap[0];     /* bitmap describing where to put holes. */
+};
+
+The `magic' field is a magic number.  It is used to detect filesystem
+corruption, and can also be used for data recovery purposes.  (The
+e2compress program for e2compr-0.3 does this.)
+
+The `checksum' field contains an Adler-32 checksum on the fields below
+it in the struct and the compressed data.  Its purpose is to protect
+us from buffer overruns caused by corrupted data.
+
+The `ulen' field says how many bytes are stored in the cluster, when
+uncompressed.
+
+The `clen' field says how many bytes are held in the cluster, when
+compressed.
+
+The `method'
+field identifies the algorithm that was used to compress the cluster
+(this id will be used to uncompress the cluster, not the one stored
+into the inode that will be used only to compress a new cluster).
+
+The variable-length `holemap' array says where to put hole blocks when
+decompressing data.  The `holemap_nbytes' field gives the length of
+this array.  Iff holemap_nbytes is zero then there are no holes (other
+than at the end of the cluster, as determined by ulen versus cluster
+size).
+
+The compressed data immediately follows the holemap array (with no
+padding before it).
+
+
+Compressing a cluster is done in the following way:  We first get every
+block in the cluster and compute the bitmap.  We then compress the
+non-hole data, and store back the compressed data into the existing
+blocks.  Unused blocks are then freed.
+
+Decompressing a cluster is done in the following way:  We get the
+cluster head and retrieve the bitmap.  Missing blocks are allocated and
+put where the bitmap says, and then compressed data is decompressed and
+stored back into the blocks.
+
+
+Reading from a compressed cluster is really easy: get the blocks,
+decompress them into a working area, and get the bytes we want from
+the working area.  Writing to a compressed cluster is done by first
+decompressing the cluster, and then write to it, as if it were a
+normal file.  The file is then marked so that the cluster will be
+recompressed later.  [pjm: Do we decompress the cluster even if it's
+to be entirely written over?]
+
+In the current version, compression really occurs only when the inode
+is put (which in turn only occurs when no processes have the file
+open).  This may change.
+
+
+  3. Ext2 file attributes
+  ~~~~~~~~~~~~~~~~~~~~~~~
+
+Attribute     Lsattr  Meaning
+~~~~~~~~~     ~~~~~~  ~~~~~~~
+EXT2_SECRM_FL	   s  Secure deletion (not yet implemented)
+EXT2_UNRM_FL	   u  Undelete-able.  (Not yet implemented.)
+EXT2_COMPR_FL	   c  Future writes to this file should be compressed.
+                      (Clearing this flag decompresses the file if it
+		      is a regular file and there is space to do so;
+		      see the e2compr FAQ for details.)
+EXT2_SYNC_FL	   S  Synchronous updates.  (As far as I know, this is
+                      not yet fully implemented.)
+EXT2_IMMUTABLE_FL  i  Immutable file.
+EXT2_APPEND_FL	   a  Writes to file may only append.
+EXT2_NODUMP_FL	   d  Not a candidate for backup with dump(8).
+EXT2_NOATIME_FL    A  No access time updates.
+EXT2_DIRTY_FL	   Z  De/compression is yet to happen.  Read the
+                      source for exact meaning.
+EXT2_COMPRBLK_FL   B  File contains one or more compressed clusters.
+EXT2_NOCOMPR_FL    X  Access raw compressed data.  This isn't really
+		      supported at the moment; user-space access is
+		      yet to be worked out for 0.4.
+EXT2_ECOMPR_FL	   E  Compression error associated with this file
+EXT2_BTREE_FL      I  B-tree indexed directory (seemingly not yet implemented)
+EXT2_RESERVED_FL   -  (reserved for ext2 lib)
+
+See the chattr(1) man page for more verbose descriptions of the
+non-e2compr flags.
+
+
+  4. Ioctls available
+  ~~~~~~~~~~~~~~~~~~~
+
+  In brief
+  ~~~~~~~~
+
+Action             Ioctl                    To kernel	 From kernel
+~~~~~~             ~~~~~                    ~~~~~~~~~    ~~~~~~~~~~~
+Get cluster bit    EXT2_IOC_GETCLUSTERBIT   Cluster num  1 or 0 (cmp,uncmp)
+Recognize compressed                        Cluster num  -
+                   EXT2_IOC_RECOGNIZE_COMPRESSED
+Get algorithm      EXT2_IOC_GETCOMPRMETHOD  -		 Id
+Set algorithm      EXT2_IOC_SETCOMPRMETHOD  Id		 -
+Get cluster size   EXT2_IOC_GETCLUSTERSIZE  -		 Cluster size
+Set cluster size   EXT2_IOC_SETCLUSTERSIZE  Cluster size -
+Get attributes     EXT2_IOC_GETFLAGS	    -		 Flags
+Set attributes     EXT2_IOC_SETFLAGS	    Flags	 -
+Get block size     FIGETBSZ		    -		 Block size
+
+#include <linux/ext2_fs.h> to use any of these ioctls, except FIGETBSZ,
+which requires <linux/fs.h>.
+
+To find out what errors can be returned by these ioctls, read
+fs/ext2/ioctl.c (for all of the above ioctls except FIGETBSZ) or
+fs/ioctl.c (for FIGETBSZ).
+
+
+  Setting or testing a cluster bit
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[Note: user-space access to compression details are yet to be worked out,
+so this section may not be accurate.]
+
+EXT2_IOC_GETCLUSTERBIT sets *arg to 1 if the specified cluster (0 for first
+cluster, 1 for second, etc.) is stored in compressed form.
+
+To make the kernel consider a certain cluster to be compressed (after
+you've done the compression yourself, in user space), use
+EXT2_IOC_RECOGNIZE_COMPRESSED.  This ioctl checks the validity of the
+cluster's data, then marks it as compressed (if valid).  This ioctl
+requires special priveleges, because if the compressed data is not
+valid then it may be possible to crash the system (due to buffer
+overruns).
+
+
+  Setting or getting the compression algorithm
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+EXT2_IOC_SETCOMPRMETHOD sets the default compression method (stored in
+the inode).  This is the compression method that is used for future
+writes.  In the current version of e2compr [accurate at 0.4.36], this
+does not cause a change to how
+existing clusters are stored, except when the compression method
+changes from `none' to something else, in which case the kernel
+attempts to compress ,all currently-uncompressed clusters` using the
+new algorithm.  It is an error to use this ioctl on a file without the
+compressed attribute.
+
+EXT2_IOC_GETCOMPRMETHOD sets *arg to the current compression method.
+
+In either case, Id is one of: EXT2_DEFER_METH, EXT2_LZV1_METH,
+EXT2_AUTO_METH, EXT2_NEVER_METH, EXT2_BZIP2_METH, EXT2_LZO1X_1_METH,
+EXT2_LZRW3A_METH (deprecated), EXT2_GZIP1_METH, EXT2_GZIP2_METH, ...,
+EXT2_GZIP9_METH.
+
+
+  Setting or getting the cluster size
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+EXT2_IOC_SETCLUSTERSIZE sets the cluster size to the value of *arg.
+This ioctl fails if there are already compressed clusters in the file
+(as determined by checking the EXT2_COMPRBLK_FL attribute).
+
+EXT2_IOC_GETCLUSTERSIZE sets *arg to the current cluster size.
+Surprisingly, this ioctl succeeds even if the EXT2_COMPR_FL attribute
+is clear.  (Maybe this will change in future, since the result is
+meaningless.) 
+
+In either case, the size is one of {4, 8, 16, 32}, and represents the
+number of blocks per cluster.  To convert to or from a number of
+bytes, use the FIGETBSZ ioctl.
+
+
+  Setting or getting the ext2 file attributes
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These ioctls (EXT2_IOC_GETFLAGS and EXT2_IOC_SETFLAGS) are not
+e2compr-specific, but some attributes are e2compr-specific.
+
+*arg consists of the set of attributes for that file OR'ed together.
+E.g. a value of (EXT2_COMPR_FL | EXT2_COMPRBLK_FL | EXT2_NODUMP_FL)
+for a regular file means that the file contains one or more compressed
+clusters, and should not be backed up when using dump(8).
+
+See section 3 for a description of the various attributes.
+
+Note that although the compression method and cluster size are
+physically stored in the flags field on disk this information is
+masked out (i.e. set to zero) for GETFLAGS if the kernel has e2compr compiled in.
+If the kernel does not have e2compr compiled in, then this information
+is not masked out.  See section 5 for how the cluster size and
+compression method is stored if you wish to work with ,kernels without
+e2compr`.
+
+
+  Getting the block size
+  ~~~~~~~~~~~~~~~~~~~~~~
+
+This ioctl (FIGETBSZ) is not e2compr-specific, but is useful in
+interpreting a cluster size (which is specified as a number of blocks
+rather than bytes or kilobytes).
+
+*arg is set to the block size (in bytes) of the file.  For ext2 files,
+this is one of {1024,2048,4096}.  It is the same value for all files
+on the same filesystem.
+
+You must #include <linux/fs.h> to use this ioctl (unlike the rest of
+the ioctls listed here, which require <linux/ext2_fs.h>).
+
+
+  5. File format
+  ~~~~~~~~~~~~~~
+
+A note on byte ordering.  All current versions of the kernel and
+e2compr write to disk in little-endian format, so the 16-bit number
+`0x8EC7' would be written as a 0xC7 byte followed by a 0x8E byte.
+Unless you want to know the most general rule for byte ordering, you
+can skip to the `Inode' heading.
+
+In kernel 2.0, the ext2 fs is written to disk in the native byte
+ordering.  On x86 machines, this means little endian; most other
+architectures are big-endian (so the same 16-bit number would be
+written as an 0x8E byte followed by 0xC7).
+
+On kernel 2.1 and later, the ext2 fs (including e2compr data) is
+written in little-endian order regardless of the host architecture.
+
+
+  5.1. Inode
+  ~~~~~~~~~~
+
+fs/inode.c controls the reading and writing of inode information
+to/from disk; consult this file (functions ext2_read_inode(),
+ext2_update_inode() and/or ext2_write_inode()) for any detail omitted
+from this section.
+
+The physical structure of an inode is struct ext2_inode (defined in
+include/linux/ext2_fs.h).
+
+
+The i_flags member contains the ext2 file attributes, as well as
+cluster size and compression method.  
+
+The normal flags are stored in the low 23 bits.  Only the low 12 bits
+are defined at present, including 4 flags introduced by the e2compr
+patch.  See ext2_fs.h for the flag meanings (search for
+EXT2_SECRM_FL).
+
+Bits 23 through 25 hold the cluster size, or more precisely the log2 of
+the number of filesystem blocks per cluster (excluding the first cluster;
+see ext2_first_cluster_nblocks in include/linux/ext2_fs_c.h).
+
+Bits 26 through 30 store the compression method.  See the definitions
+for EXT2_LZV1_METH etc. in ext2_fs_c.h for the interpretation.
+
+Bit 31 is reserved for ext2 lib (which means that programs like e2fsck
+store things there during its operation but it isn't used by the
+kernel).
+
+
+  Data blocks
+  ~~~~~~~~~~~
+
+Uncompressed clusters are stored just as they would be without
+e2compr.  So if there are no compressed clusters then the file
+is stored identically to any other file.
+
+
+If a cluster is compressed, then the first non-hole block starts with
+a `cluster head', as defined in struct ext2_cluster_head in ext2_fs.h.
+
+The magic number (i.e. the value of the `magic' field) is 0x8ec7.
+`method' holds one of EXT2_LZV1_ID and the like.  `reserved_0'
+contains zero.  `ubitmap' describes where the uncompressed data goes.
+(Recall that when we compress a cluster, we only compress the data
+from non-hole blocks, so we need to know where the holes and non-holes
+go when we decompress the data.)  A `0' bit means a hole and a `1' bit
+means a data block; bit 0 refers to the first block, b1 the second,
+and so on.
+
+
+The block positions within the file where the compressed data is held
+is a subset of where the uncompressed data would be held.  Further, if the
+uncompressed data occupies u non-hole blocks and this compresses to c
+blocks, then the compressed data occupies the first c non-hole blocks
+of the file (and the remainder are freed).
+
+[This paragraph is an expansion of the preceeding: if you understood
+the preceeding paragraph then skip this one.]  Consider an array
+cblock[] where cblock[0] holds the block number on disk (or 0 to
+represent a hole) of the first block of a certain cluster of a file,
+cblock[1] the second, and so on.  (If you are familiar with the bmap
+array or the format of first-level indirect blocks, then cblock[] is a
+section of that array.)  Suppose that the cluster size of this file is
+16 blocks.  Suppose too that, when uncompressed, blocks 0, 1, 5 and 6
+of the cluster are holes but the other 12 blocks (2,3,4,7,8,...,15)
+contain data.  (Thus the bitmap is 0x0000ff9c.)  Now if we compress this 
+cluster to just 5 blocks, then cblock[0], [1], [5] and [6] will continue 
+to be holes, ,the positions of the compressed data blocks` are stored in 
+cblock[2], cblock[3], [4], [7] and [8], the blocks referenced by 
+cblock[9] through cblock[15] are freed, and cblock[9] through cblock[15] 
+are set to zero.
+
+
+  6. What's coded where
+  ~~~~~~~~~~~~~~~~~~~~~
+
+File names in this section are relative to linux/fs/ext2, except for
+ext2_fs.h which is in linux/include/linux.
+
+Most of the action happens in compress.c; though note that a few
+small, commonly-used routines are written as inline functions in
+ext2_fs.h.
+
+ext2_readpage() and ext2_mmap() are in file.c.  ext2_file_write() is
+also there.
+
+Routines to read/write the inode from/to disk are in inode.c.
+
+super.c contains some e2compr initialisation code (such as allocating
+the e2compr work area).
+
+All ioctl handling is in ioctl.c.
+
+acl.c is where we deny open() access in a couple of situations (if the
+EXT2_NOCOMPR_FL is set and another process has the file open; and we
+deny write access to a file with EXT2_ECOMPR_FL set).
+
+ialloc.c contains code in ext2_new_inode() for newly-created files to
+inherit compression attributes from the directory in which they're
+created.
+
+truncate.c handles truncation, i.e. zeroing any part of the cluster
+bitmap that's been truncated, and decompressing the final cluster (but
+marking dirty so that we try to recompress it on file close) if the
+new size is part-way through a compressed cluster, so that zeroing
+over the truncated data works.
+
+linux/include/linux/ext2_fs_i.h has the definition of the
+ext2-specific parts of the in-memory inode.  (The on-disk inode is
+defined in ext2_fs.h.)
+
+linux/mm/filemap.c is also interesting, though there's no
+e2compr-specific code there.  Similarly linux/include/linux/mm.h and
+linux/include/linux/fs.h.
+
+generic_readpage() is in linux/fs/buffer.c.  Also all buffer handling.
+
+
+The cleanup scheme
+~~~~~~~~~~~~~~~~~~
+
+inode->u.ext2_i.i_compr_flags has only a single bit defined:
+EXT2_CLEANUP_FL.  This bit gets set to 1 to indicate that
+ext2_cleanup_compressed_inode() needs to be called.  
+
+There is a related flag stored on disk as well as in memory:
+EXT2_DIRTY_FL of i_flags.  If ext2_cleanup_compressed_inode() couldn't
+finish it's job (e.g. due to I/O error) then it clears EXT2_CLEANUP_FL
+of i_compr_flags, but leaves EXT2_DIRTY_FL high.
+
+In ext2_read_inode(), if EXT2_DIRTY_FL is high then EXT2_CLEANUP_FL is
+raised, in the hope that ,whatever was preventing
+ext2_cleanup_compressed_inode() from finishing` is now past.
+
+Except for ext2_read_inode() as noted above, everything that raises
+EXT2_CLEANUP_FL (i.e. ext2_write_file(), ext2_ioctl() and
+ext2_truncate()) also raises EXT2_DIRTY_FL.
+
+Nothing lowers either EXT2_CLEANUP_FL or EXT2_DIRTY_FL except
+ext2_cleanup_compressed_inode() (and one or both of new_inode and
+delete_inode routines).
+
+
+One feels that at least one of these cleanup flags ought to
+disappear.  The main use of the persistent EXT2_DIRTY_FL is where the
+user does `chattr -c' in order to decompress the file, but there isn't
+enough space on the device to do this.  We can get rid of this problem
+by having ext2_ioctl() call ext2_cleanup_compressed_inode()
+try to
+
+
+Notes on a few variables
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Don't confuse the inode->i_dirt flag with (inode->u.ext2_i.i_flags &
+EXT2_DIRTY_FL).  See section `The cleanup scheme' above for a
+description of EXT2_DIRTY_FL.
+
+
+inode->u.ext2_i.i_clu_nblocks,
+inode->u.ext2_i.i_log2_clu_nblocks:
+
+i_clu_nblocks is always equal to ,1 << i_clu_nblocks` (except during a
+couple of cycles while they're being changed; I haven't consciously
+tried to avoid problems for SMP machines in this respect).
+
+i_clu_nblocks is the number of blocks per cluster for this inode.
+
+Old information: these variables were previously called
+`i_cluster_bits' and `i_cluster_size'.  They were in an array:
+
+inode->u.ext2_i.i_cluster_bits[2], 
+inode->u.ext2_i.i_cluster_size[2]: 
+
+I believe the reason these were declared as an array was for the case
+where someone changes the cluster size of a file that was already
+compressed.  (Reason for this belief: All readers of these fields use
+[0].  On creation (ialloc), read_inode, and `chattr +c' (where
+previously uncompressed), both [0] and [1] are updated.  On change
+(IOC_SET_CLUSTERSIZE), only [0] is updated.)  Since ,changing cluster
+size of an already-compressed file` isn't implemented, I've renamed
+them and made them scalars rather than arrays.
+
+
+inode->u.ext2_i.i_flags: When the e2compr patch is applied, this
+variable only holds the low 24 bits of the on-disk i_flags field.
+(Without the e2compr patch applied, all 32 bits are available.  An
+interesting side effect of this is that user programs can access the
+compression algorithm and cluster size on kernels without e2compr
+patch by using the EXT2_IOC_GETFLAGS, EXT2_IOC_SETFLAGS ioctls.)
+
+
+inode->u.ext2_i.i_compr_method: Holds the compression method
+identifier.  Starting from e2compr-0.4.0, this is different from an
+algorithm identifier: an example of a method is gzip9; the
+corresponding algorithm is gzip.  See compress.c for where
+ext2_method_table and ext2_algorithm_table are defined.  ext2_fs.h has
+some enumerations for addressing these tables (search for
+`EXT2_NONE_METH' and `EXT2_NONE_ALG').
--- linux-2.6.25/fs/ext2/ialloc.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/ext2/ialloc.c	2008-07-31 15:13:12.000000000 +0200
@@ -12,6 +12,19 @@
  *        David S. Miller (davem@caip.rutgers.edu), 1995
  */
 
+/*
+ *  Copyright (C) 1995  Antoine Dumesnil de Maricourt (dumesnil@etca.fr)
+ *    (transparent compression code)
+ */
+
+/*
+ *  Copyright (C) 2001 Alcatel Business Systems - R&D Illkirch
+ *    (transparent compression code)
+ *  Pierre Peiffer (pierre.peiffer@sxb.bsf.alcatel.fr),
+ *  Denis Richard (denis.richard@sxb.bsf.alcatel.fr)
+ *  Adapted from patch e2compr-0.4.39-patch-2.2.18 .
+ */
+
 #include <linux/quotaops.h>
 #include <linux/sched.h>
 #include <linux/backing-dev.h>
@@ -20,6 +33,7 @@
 #include "ext2.h"
 #include "xattr.h"
 #include "acl.h"
+#include "debug.h"
 
 /*
  * ialloc.c contains the inodes allocation and deallocation routines
@@ -476,7 +490,7 @@ struct inode *ext2_new_inode(struct inod
 		gdp = ext2_get_group_desc(sb, group, &bh2);
 		brelse(bitmap_bh);
 		bitmap_bh = read_inode_bitmap(sb, group);
-		if (!bitmap_bh) {
+		if (!bitmap_bh) { EXT2_I(inode)->i_flags &= ~EXT2_COMPR_FL;
 			err = -EIO;
 			goto fail;
 		}
@@ -570,6 +584,18 @@ got:
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
 	memset(ei->i_data, 0, sizeof(ei->i_data));
 	ei->i_flags = EXT2_I(dir)->i_flags & ~EXT2_BTREE_FL;
+#ifdef CONFIG_EXT2_COMPRESS
+	/*
+	 *      The EXT2_COMPR flag is inherited from the parent
+	 *      directory as well as the cluster size and the compression
+	 *      algorithm.
+	 */
+	ei->i_log2_clu_nblocks = EXT2_I(dir)->i_log2_clu_nblocks;
+	ei->i_clu_nblocks = EXT2_I(dir)->i_clu_nblocks;
+	ei->i_compr_method = EXT2_I(dir)->i_compr_method;
+	ei->i_compr_flags = 0;
+	
+#endif
 	if (S_ISLNK(mode))
 		ei->i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL);
 	/* dirsync is only applied to directories */
--- linux-2.6.25/fs/ext2/ChangeLog.e2compr	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/ChangeLog.e2compr	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,631 @@
+25 Mar 2007
+
+	Terry Loveall <loveall@iinet.com>
+
+	* compress.c:
+	  bug fix: ext2_count_blocks: init head_bh for each iteration.
+	  bug fix: ext2_count_blocks: add set clen=ulen for uncompressable clusters.
+	  bug fix: ext2_compress_cluster: replacement and inlining of an 
+	   invalidate_inode_buffers function to keep root filesystem changes
+	   uptodate on disk (prevents umounting root file system to update).
+	  warning fix: ext2_compress_cluster: various variables initialized.
+	  ext2_compress_cluster: removed #ifdef NDEBUG
+	  bug fix: ext2_compress_cluster: defined maxclus, calculate and set for:
+	  bug fix: ext2_compress_cluster: set i_size for uncompressed clusters.
+	  ext2_cleanup_compressed_inode: changed error message to indicate 'Z'
+	   flag was caused by trying to un/compress already open file.
+	  bug fix: cp to compr dir: Truncate uncompressed files to their
+	   uncompressed length, i.e. force kernel to update inode and sb
+
+	* inode.c:
+	  bug fix: ext2_get_block: restored changed: loop to bforget
+
+	* ioctl.c:
+	  ext2_ioctl: scrubbed 'B' flag on file uncompress.
+
+28 Feb 2005
+
+	Yabo Ding <bobfree_cn@yahoo.com.cn>,<yding@wyse.com>
+
+	* Corrected page unlocking in inode.c.
+
+19 Feb 2005
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* Added corrections le32_to_cpu in critical areas of compress.c
+	* Optimized function exit code in inode.c.
+
+26 Jan 2005
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* ../Kconfig: Added EXT2_FS dependency for algorithms (such that they
+	  behave like dep_tristate under the old system).  Partially fixed a
+	  problem with GZIP not being set as the default if eligible.  Updated
+	  the help for most items.
+
+	* compress.c: commented out quota-disabled assertion, as it applies too
+	  generally.
+
+	* ../../arch/i386/defconfig: corrected missing and obsolete default
+	  options.
+
+	* ../../include/linux/assert.h, ../../include/linux/ext2_fs_c.h,
+	  debug.h, ioctl.c, lzo/{Makefile,lzoconf.h}: for tidiness, moved
+	  assert.h into this directory, and amended other files as required.
+	  <linux/assert.h> is an obsolete Linux-2.2-ism; this really ought to
+	  be fixed properly at some stage.
+
+	* compress.c: update for 2.6.10: buffer_insert_list() no longer exists,
+	  instantiated the body of this function inline instead.
+
+	* file.c: update for 2.6.10: current->rlim is now current->signal->rlim.
+
+	* super.c, inode.c: update for 2.6.10: ext2_put_inode had been removed,
+	  so I had to reinstate it (for e2compr use only).
+
+30 Sep 2004
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* compress.c: fixed LZRW3A availability mistake in
+	  ext2_algorithm_table.
+
+	* new-method-howto.c: updated.
+
+29 Sep 2004
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* ioctl.c: for completeness, added atomic_read to a commented-out
+	  i_count access.
+
+	* Readme.e2compr: minor corrections.
+
+02 Aug 2004
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* lzrw3a/lzrw3a.c: added (UBYTE *) casts to avoid compiler warnings.
+
+01 May 2004
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* inode.c: fix from Guy Bilodeau <guy.bilodeau@colubris.com>: zero the
+	  epg array after failed ext2_get_cluster_extra_pages() before exiting
+	  ext2_readpage().
+
+	* compress.c: fixes from Guy Bilodeau <guy.bilodeau@colubris.com>:
+	  in ext2_get_cluster_pages() and ext2_get_cluster_extra_pages() npg
+	  must be int, otherwise "while (--npg >= 0)" is never true; NULL-test
+	  epg[npg] in ext2_get_cluster_extra_pages() before attempting page
+	  release.
+
+25 Mar 2004
+
+Version 0.4.44 released.
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* Makefile, {bzip2,gzip,lzo,lzrw3a,lzv1}/Makefile: changed target names
+	  to ext2-compr-{bzip2,gzip,lzo,lzwr3a,lzv1}.  This both ensures that
+	  the revised automatic module loading scheme works (see below), and
+	  removes any possible ambiguity as to an algorithm module's purpose.
+
+	* bzip/, lzrw/, lzv/: renamed to bzip2, lzrw3a and lzv1 respectively,
+	  for consistency with kernel config defines and algorithm names in
+	  compress.c.
+
+29 Feb 2004
+
+	* compress.c, inode.c, ioctl.c: changed code preceding request_module()
+	  to request the algorithm by name rather than number.  The
+	  modules.conf aliases are therefore no longer required (removed
+	  Modules.e2compr).
+
+22 Feb 2004
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* ext2_fs_c.h, compress.c: fixed broken trace_e2c() calls, associated
+	  with EXT2_COMPR_REPORT instead of EXT2_COMPR_DEBUG.
+
+17 Feb 2004
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* ioctl.c: added S_ISREG safety checks to EXT2_IOC_SETCOMPRMETHOD ioctl
+	  to prevent inadvertent marking of directories with EXT2_DIRTY_FL and
+	  EXT2_CLEANUP_FL, or attempts to decompress them.
+
+22 Jan 2004
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* various: reinstated the LZRW3A algorithm using code from an old 2.2
+	  kernel patch, and updated e2compr-lzrw3a.c with module author,
+	  description and license info.
+
+	  LZRW3A was removed due to concerns with patent encumbrances, however
+	  LZRW3 is still used in the Linux ftape driver, and public opinion is
+	  that LZW-derivatives of this sort ought to be OK to use in GPL code.
+
+	* compress.c: allow for the case where a WA is legitimately NULL when
+	  ext2_register_compression_module() is called.  This can happen!
+
+	* ext2_fs_c.h: CONFIG_EXT2_DEFAULT_COMPR_METHOD_NONE is never defined,
+	  should be CONFIG_EXT2_DEFAULT_COMPR_METHOD_DEFER.
+
+19 Jan 2004
+
+Version 0.4.43a released
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* lzo/lzoconf.h, lzo/lzo_conf.h: minimal modifications to allow
+	  compilation with -nostdinc.  This code is a mess!
+
+	* inode.c: fixed a broken EXT2_COMPR_REPORT_VERBOSE printk.
+
+	* compress.c: moved n_clusters assignment ahead of its first use.
+
+	* compress.c, file.c, iname.c: use atomic_read() to read i_count.
+
+	* gzip/Makefile, gzip/deflate.c: Simplified #ifs such that assembly
+	  routines are used in a wider range of circumstances; assume user has
+	  at least a 586 if CONFIG_EXT2_COMPR_X86_CODE is set, and assume it
+	  is a 686 or better if CONFIG_X86_F00F_WORKS_OK=y.
+
+	* Configure.help: various updates, removed entry for LZRW3A algorithm.
+
+	* bzip/init.c,gzip/e2compr_gzip.c,lzo/e2compr_lzo.c,lzv/e2compr_lzv.c:
+	  Add module info.
+
+	* bzip/lib_bzip_e.c: Removed superfluous ##s.
+
+	* inode.c: e2compr additions to module info.
+
+	* ioctl.c: change obsolete refs to fsuser() to capable(CAP_FOWNER).
+
+	* compress.c: change EXT2_COMPR_DEBUG to EXT2_COMPR_REPORT_VERBOSE
+
+	* compress.c: integrate compression verification fixes from
+	  Ulrich Holeschak <UHoleschak@bihl-wiedemann.de>.
+
+	* Readme.e2compr, e2compress.txt, ext2_compression: updated URLs and
+	  contact details.
+
+	* project name reverted to "e2compr", relocated to sourceforge.net.
+	  CHANGES.e2compress integrated into this file.
+
+15 Mar 2003
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* compress.c, inode.c: updated for page wait queues (2.4.20 kernel).
+
+Changes from 0.4.42 to 0.4.43 :
+===============================
+	Pierre Peiffer <pierre.peiffer@sxb.bsf.alcatel.fr>
+	Denis Richard <denis.richard@sxb.bsf.alcatel.fr>
+
+	- Deadlock correction when compressing cluster and sync pages.
+	- Management of MAPPED and DIRTY buffer flags.
+	- Test of block number in compression.
+	- Test if page is dirty to allocate buffer on device when compressing.
+	- When decompress cluster use the size of the cluster and not the size
+	  of the file, because it can be called from vmtruncate (the size of the 
+	  file has already changed).
+	- Management of working area lock.
+	- When (de)compress file containing holes, the data must be moved and not
+	  only the block number.
+	- New function ext2_decompress_pages() to allocate blocks for a cluster
+	  already (block) decompressed. It is now called in ext2_decompress_cluster()
+	  and not only in ext2_file_write().
+
+Changes from 0.4.41 to 0.4.42 :
+===============================
+	Pierre Peiffer <pierre.peiffer@sxb.bsf.alcatel.fr>
+	Denis Richard <denis.richard@sxb.bsf.alcatel.fr>
+
+	- Delete the i_blocks field decrementation (Thanks to Peter Wächtler).
+	- Clear dirty bit of buffers not in compressed area, after compression.
+	- Unlock pages before sync of inode, after compression.
+	- Change parameters (OSYNC_METADATA|OSYNC_DATA) of generic_osync_inode()
+	  calls to write data inode.
+	- Ext2_readpage() returns an error code.
+	- Allocation of working area even when readonly mount.
+	- Clear dirty bit of buffers after uncompress in ext2_readpage.
+	- Unlock page after free buffers in error case in ext2_readpage.
+
+Version 0.4.41 is the first version of e2compress patch for kernel 2.4.16
+=========================================================================
+	Pierre Peiffer <pierre.peiffer@sxb.bsf.alcatel.fr>
+	Denis Richard <denis.richard@sxb.bsf.alcatel.fr>
+
+1998-12-16  Peter Moulder  <reiter@netspace.net.au>
+
+	* compress.c (ext2_register_compression_module): Unlock working
+ 	area before we exit, even if ENOMEM.  Set ext2_rd_wa_size
+ 	correctly if decompression needs > compression needs.  (Never
+ 	happens with current algorithms, but in the process I simplified
+ 	the code a little.)
+	(ext2_decompress_inode): Moved assertion to before blocking calls,
+ 	and added a non-asserted test (of the same condition) after the
+ 	blocking calls.
+	(ext2_cleanup_compressed_inode): Repeat some tests after some
+	blocking calls (down, DQUOT_INIT).
+
+	* ext2_fs_c.h: Remove last remnants of `ifndef EXT2_HOLEMAP'
+	code.
+	(ext2_offset_is_clu_boundary): Tiny efficiency tweek.
+
+1998-11-30  Peter Moulder  <reiter@netspace.net.au>
+
+	* compress.c (ext2_compress_cluster, ext2_decompress_cluster): 
+	* ioctl.c (ext2_ioctl): 
+	* file.c (ext2_readpage):
+	Make request_module() call dependent on CONFIG_KMOD.
+
+1998-11-22  Peter Moulder  <pjm@opensource.captech.com>
+
+	* compress.c (ext2_mark_algorithm_use): Adjust s_rev_level when 
+	adding compression to the superblock.
+
+1998-11-01  Jan Rêkorajski  <baggins@hunter.mimuw.edu.pl>
+
+	* Merged with patch 0.4.21
+	* Updated defconfigs for all architectures
+	* Cleared some forgotten code
+
+1998-10-31  Jan Rêkorajski  <baggins@hunter.mimuw.edu.pl>
+
+	* Made yesterday statement false :)
+	I followed Peter's sugestion to make working area pointer
+	a parameter to de/compression functions.  Now there is
+	no problem selecting both CONFIG_EXT2_SEPARATE_WORK_AREAS and
+	CONFIG_EXT2_VERIFY_COMPRESSION.
+
+1998-10-30  Jan Rêkorajski  <baggins@hunter.mimuw.edu.pl>
+
+	* Hello, my name is Dexter, and I'm boy genius ;)
+
+	* Ha, nailed The Bug, and squashed it :)
+	The problem with separate work areas was due to
+	compression verification.  It's clear that
+	CONFIG_EXT2_SEPARATE_WORK_AREAS and CONFIG_EXT2_VERIFY_COMPRESSION
+	are mutually exclusive.  Now, only fix the *config stuff
+	and I'm done with "known bug" hunting.
+
+1998-10-28  Jan Rêkorajski  <baggins@hunter.mimuw.edu.pl>
+
+	* OK, working area pointers separated. Didn't help :(
+	Oopsed while testing.  Seems that "critical bug" was
+	something more...
+
+	* Fixed a bug in ext2_register_compression_module,
+	I forgot to put locks around memory reallocation.
+
+1998-10-26 (later that day ;)  Jan Rêkorajski  <baggins@hunter.mimuw.edu.pl>
+
+	* more separate areas work...
+	Now I know what is that pjm's "critical bug", it's fundametal
+	design screw up.  (Structures do _NOT_ change their size magically,
+	sizeof is a const, so using ext2_wa[0/1] was a BAD idea)
+	Only 6 more (ext2_wa != NULL) lines to go...
+
+1998-10-26  Jan Rêkorajski  <baggins@hunter.mimuw.edu.pl>
+
+	* patch against 2.1.126
+
+	* started fixing separate work areas problems (a.k.a. races ;)
+	Every algorithm uses separate variables to store pointers to
+	read/write work areas.  Now they can compress and decompress
+	in the same time.
+
+	Apparently, it will save us a lot of memory :)
+	See this:
+
+	ALG	C MEM	D MEM
+	---------------------
+	bzip2	~150K	~150K
+	gzip	 200K	  64K
+	lzo	~128K	   0K
+	lzrw	 ~16K	 ~16K
+	lzv	  32K	   0K
+	---------------------
+	MAX	 200K	 150K
+
+	So, in worst case with separate work areas we save ~50K _kernel_,
+	_unswappable_ memory :)
+
+1998-11-01  Peter Moulder  <reiter@netspace.net.au>
+
+	* Misc small changes.
+
+1998-10-18  Jan Rêkorajski  <baggins@hunter.mimuw.edu.pl>
+
+	* compress.c: fix "off by one" maxlen in ex2_compress_cluster
+
+	* modularized bzip2, gzip, lzo, lzv, lzrw
+		- added ext2_compress_(un)register_module
+		  see new-method-howto.c or lzv/e2compr_lzv.c
+	* updated gzip to zlib 1.1.3 (was 0.93!)
+	* fixed lzo - yes it works now :)
+	
+1998-06-30  Peter Moulder  <reiter@netspace.net.au>
+
+	* binfmt_elf.c (elf_core_dump): 
+	* binfmt_aout.c (do_aout_core_dump):
+	Fix deadlock.  OK, make that 0.4.15.
+
+	* Release e2compr-0.4.14-patch-2.1.107.
+
+	* truncate.c, binfmt_aout.c: resolve with 2.1.107.
+
+	* truncate.c: Don't test feature flag; EXT2_COMPRESSED_BLKADDR
+	is proof enough.
+
+1998-06-15  Peter Moulder  <reiter@netspace.net.au>
+
+	* compress.c: Reset i_next_alloc_block and i_next_alloc goal
+	to zero after compressing a cluster.
+
+1998-06-11  Peter Moulder  <reiter@netspace.net.au>
+
+	* compress.c: Fix [stupid] race condition.  Added many diagnostic
+	checks, too, which I haven't yet removed.
+
+1998-06-10  Peter Moulder  <reiter@netspace.net.au>
+
+	* ext2_fs_c.h: Add aliases for some #definitions.
+
+1998-06-09  Peter Moulder  <reiter@netspace.net.au>
+
+	* file.c (ext2_readpage): Allow for page sizes bigger than 8KB,
+	for certain ARM processors.
+
+	* ioctl.c (ext2_ioctl): Remove spurious ext2_warning about
+ 	"COMPRBLK w/o COMPR".
+
+1998-06-06  Peter Moulder  <reiter@netspace.net.au>
+
+	* inode.c (block_getblk): Moved COMPRESSED_BLKADDR macro to
+	ext2_fs_c.h, and removed the inode parameter.  We no longer
+	consult the superblock to check for e2compr support; if the block
+	address is 0xffffffff then we say it's compressed.
+
+1998-05-21  Peter Moulder  <reiter@netspace.net.au>
+
+	* compress.c (ext2_count_blocks): Do more consistency checks on
+	cluster head.
+	(ext2_compress_cluster): Improve information display for changing
+	b_blockno problem.  Check return code of ext2_get_key().
+
+1998-05-12  Peter Moulder  <reiter@netspace.net.au>
+
+	* compress.c (ext2_cleanup_compressed_inode,
+ 	ext2_decompress_inode): Use DQUOT_INIT() for 2.1.101+ kernels.
+
+1998-03-07  Peter Moulder  <pjm@bofh.asn.au>
+
+	* compress.c, lzo/, ext2_fs_c.h: Add lzo algorithm.  (Patch sent
+	in by Chris Old <cold@irdeto.com>.)
+
+1998-03-06  Peter Moulder  <pjm@bofh.asn.au>
+
+	* compress.c, file.c (ext2_readpage), ext2_fs_c.h: Support
+	CONFIG_EXT2_SEPARATE_WORK_AREAS.
+
+	* Release 0.4.2.
+
+1998-03-02  Peter Moulder  <pjm@bofh.asn.au>
+
+	* ioctl.c (ext2_ioctl: EXT2_IOC_SETFLAGS): Properly clear
+	EXT2_DIRTY_FL if we drop EXT2_COMPR_FL.
+
+1998-02-12  Peter Moulder  <pjm@bofh.asn.au>
+
+	* compress.c (ext2_decompress_inode,
+	ext2_cleanup_compressed_inode): Initialise quotas.
+
+1998-01-24  Peter Moulder  <pjm@bofh.asn.au>
+
+	* compress.c (ext2_get_cluster_blocks): Number of blocks to fetch
+ 	shouldn't be calculated from i_size; just use clu_nblocks.
+	(i_size is short when truncate() is called.)  I suspect that this
+ 	change should also apply to 0.3.6.
+
+1998-01-09  Peter Moulder  <pjm@bofh.asn.au>
+
+	CHANGES FOR 0.4.0.  (Various files.)
+
+	* Get rid of ,compressed cluster` bitmaps.  Store
+	EXT2_COMPRESSED_BLKADDR (new constant, currently 0xffffffff) in
+	last blockno of cluster to mark as compressed.
+
+	* First cluster is no longer in general the same size as other
+ 	clusters of a file.  See ext2_first_cluster_nblocks() and
+ 	surrounding macros in ext2_fs.h.
+
+	* Blocknos stored contiguously at the beginning of the cluster
+ 	rather than having holes in places where uncompressed data has
+ 	holes.
+
+	* Mark incompatibilities in s_feature_incompat and
+	s_algorithm_usage_bitmap (new field).
+
+	* Always use i_sem, never I_LOCK.  (This change is overdue, and
+	should probably be back-ported.)
+
+	* ioctl.c: Allow EXT2_ECOMPR_FL to be either raised from userspace (not
+ 	just cleared).
+
+	* ioctl.c: Don't ,raise EXT2_DIRTY_FL just because of toggling of
+	EXT2_NOCOMPR_FL`.
+
+	* ext2_fs.h, ioctl.h: New ioctl EXT2_IOC_RECOGNIZE_COMPRESSED to
+	replace EXT2_IOC_SETCLUSTERBIT.  EXT2_IOC_CLRCLUSTERBIT has been
+	removed.
+
+	* Have ext2_ioctl() decompress the file immediately if
+	EXT2_COMPR_FL is cleared from a regular file.  Report the error if
+	the request couldn't be carried out.
+
+	* Use two-level compression method table.  ext2_algorithm_table
+ 	keeps the `name', `avail', `init', `compress', `decompress'
+ 	fields, while there is a new ext2_method_table with two fields: an
+ 	index into ext2_algorithm_table, and an argument to the
+ 	de/compressor.  See compress.c.
+
+	* Writes are compressed as soon as the end of a cluster is
+ 	reached, instead of waiting until close().
+
+	* EXT2_IOC_SETCOMPRMETHOD doesn't fail for unknown compression
+	method.  However, it writes 1 or 0 to *arg depending on whether or
+	not (respectively) the requested method is known to the kernel.
+
+	* fs/Config.in: CONFIG_EXT2_COMPRESS question only asked if
+	CONFIG_EXPERIMENTAL=y.
+
+	* arch/*/defconfig: Default answer for CONFIG_EXT2_COMPRESS
+	changed to `n'.
+
+	* Do checksum on _compressed_ data not uncompressed.  The intent
+ 	is to prevent data corruption from skrogging kernel memory or
+ 	causing illegal memory references.  The `reserved_2' field has been
+	removed from the cluster head and replaced with `checksum'.
+
+	* Removed `bitmap' and `reserved0' from cluster head and replaced
+	with `holemap' and `holemap_nbytes'.  holemap[] is a bitmap of
+	where to put holes.  (It is declared as a zero-length array of
+	unsigned char.)  Unlike the former `bitmap' field, we don't
+	include holes at the end of the cluster, a `1' represents a hole
+	instead of a `0' in `bitmap', and of course it is variable-length.
+	This paves the way for longer clusters.  However, I haven't yet
+	increased the length of clen or ulen, which would be necessary for
+	clusters longer than 64KB, and desirable (no 0==64K hack) even for
+	64KB clusters.
+
+	* New methods `never' and `auto'.  `auto' is largely
+	unimplemented (see compress.c).
+
+	* Gratuitous name changes.  Many things using word `uncompress'
+ 	changed to `decompress'.  (`Decompressed' always means `having
+ 	been decompressed', whereas `uncompressed' can also (and usually
+ 	does) mean `not having been compressed'.)
+  	ext2_put_compressed_cluster() split into
+ 	ext2_cleanup_compressed_cluster() and ext2_decompress_inode().
+  	head->len becomes head->ulen.  CONFIG_EXT2_USE_* becomes
+ 	s/USE/HAVE/.  EXT2_COMPR_DEBUG* become EXT2_COMPR_REPORT*.  Maybe
+ 	some other things.
+
+Tue Dec  9 19:53:12 1997  Peter Moulder  <pjm@bofh.asn.au>
+
+	* ioctl.c (ext2_ioctl): Don't ,try to prevent 4KB clusters on
+	machines with 8KB pages`.
+
+Mon Dec  8 17:48:19 1997  Peter Moulder  <pjm@bofh.asn.au>
+
+	* namei.c (ext2_create): implement CONFIG_GZ_HACK
+
+	* compress.c (ext2_lock_wa): call signal_pending() instead of
+	testing the bitmaps.  (Necessary for 2.1.68 POSIX.1b signal
+	handling.)
+
+	* file.c (ext2_readpage): Attempt to correct rare bug for
+	machines with 8KB (or larger) pages.
+
+Mon Dec  8 16:31:38 1997  Peter Moulder  <pjm@bofh.asn.au>
+
+	* inode.c, ext2_fs.h: Add support for a couple of e2compr-
+	reserved inodes.  (This change will probably be reversed,
+	but it's harmless for now.)
+
+Fri Aug  1 15:10:48 1997  Peter Moulder  <pjm@bofh.asn.au>
+
+	* compress.c (ext2_unlock_wa): Call schedule() if other processes
+	are waiting for the working area.
+
+Sat Jul 26 08:28:08 1997  Peter Moulder  <pjm@bofh.asn.au>
+
+	* Makefile: e2compr files were getting compiled even if
+ 	CONFIG_EXT2_COMPRESS wasn't defined.  Makefile now tests for this
+ 	being defined.
+
+	* acl.c: Not even root gets write access to a file with a
+ 	compression error.
+
+	* compress.c (ext2_get_cluster_bit): Reverse change of Apr 21,
+	i.e. return any positive number instead of converting to 1.
+	(Apr 21 change was needlessly cautious.)
+
+	* inode.c:
+	* compress.c:
+	* file.c: 
+	Access to the cluster head does endian conversion.
+	Access to inode has endian conversion in 2.1 but not in 2.0 --
+	just like the standard kernel.
+	* ext2_byteorder.h: new file.  (Only needed in 2.0.)
+	
+	* file.c (ext2_readpage): Rewrite.  Handles errors better.  Also
+ 	takes better care of working area lock.
+
+	* compress.c:
+	* file.c:
+	* inode.c:
+	* truncate.c:
+	Change printk level to KERN_DEBUG for things surrounded by
+	EXT2_COMPR_REPORT*.
+
+	* inode.c: Allow bmap() if EXT2_NOCOMPR_FL set.
+
+	* inode.c: Don't take high bit of i_flags into compr_meth.  (The
+	high bit is reserved for ext2 lib.)  Also, don't write it
+	from compr_meth (though the result should be the same: the
+	bit is set to zero).
+
+	* ioctl.c (ext2_ioctl): Call verify_area in a few places
+	where previously we didn't.
+
+	* super.c (ext2_setup_super): The debug string on mount didn't seem
+	to be printing, so I changed `%p' to `%lx' (plus an appropriate cast)
+	to see if it would change things.
+
+	* compress.c (ext2_uncompress_cluster, ext2_uncompress_blocks): In
+ 	the consistency checks, consider the head size as well as
+ 	compr_len in calculation.
+
+	* gzip/deflate.c (deflate_fast, deflate_slow):
+	* lzv/lzv1.c:
+	Yield CPU every fifth of a second (ifdef __KERNEL__).
+
+	* include/linux/ext2_fs.h: Change definition of EXT2_ECOMPR_FL
+ 	from 0x0080 to 0x0800 to avoid clash with EXT2_NOATIME_FL in 2.1.
+  	* Change name EXT2_MAX_CLUSTER_SIZE to EXT2_MAX_CLUSTER_BLOCKS.
+
+Tue Apr 29 12:00:00 1997  Peter Moulder  <pjm@bofh.asn.au>
+
+	* ioctl.c (ext2_ioctl): Call invalidate_inode_pages() if
+	EXT2_NOCOMPR_FL is toggled.
+
+Mon Apr 21 15:50:16 1997  Peter Moulder  <pjm@bofh.asn.au>
+
+	* ext2_fs.h (ext2_cluster_is_compressed): If
+	ext2_get_cluster_bit() returns error then pass the error back.
+	* compress.c (ext2_get_cluster_bit): Return 1 rather than any +ve num.
+	ext2_cluster_is_compressed() is the only caller.
+
+	* compress.c (ext2_free_key,ext2_get_key,ext2_next_key): Make static.
+
+Thu Jan 16 12:00:00 1997  Peter Moulder  <pjm@bofh.asn.au>
+
+	* file.c (ext2_mmap): Previously we would deny mmap access if
+	there were an error or if compression were disabled (NOCOMPR).  
+	These checks have both been removed.
+	It was wrong to disallow mmap access to a file with NOCOMPR,
+	but it's more questionable whether or not we should allow
+	compression to a file with ECOMPR.
+
+This file is not very accurate, particularly the older changes.
--- linux-2.6.25/fs/ext2/bzip2/init.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/bzip2/init.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,60 @@
+#include "lib_bzip.h"
+#include "lib_bzip_private.h"
+#include <linux/fs.h>
+#include <linux/ext2_fs_c.h>
+#ifdef MODULE
+# include <linux/module.h>
+MODULE_AUTHOR("Julian Seward");
+MODULE_DESCRIPTION("Bzip2 algorithm for EXT2 file compression");
+MODULE_LICENSE("GPL");
+#endif
+
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+#if 0
+void *bzip2_work_area;
+#endif
+
+size_t bzip_init(int action)
+{
+	switch (action) {
+		case EXT2_ALG_INIT_COMPRESS:
+			return MAX(sizeof(Lib_Bzip_Encode_Storage_Ty),
+					sizeof(Lib_Bzip_Decode_Storage_Ty));
+		case EXT2_ALG_INIT_DECOMPRESS:
+			return sizeof(Lib_Bzip_Decode_Storage_Ty);
+		default:
+			return 0;
+	}
+   /* sizeof(encoding space) == 147268,
+      sizeof(decoding space) == 144180
+      (though this may have changed slightly by the time you read
+      this comment). */
+}
+
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	struct ext2_algorithm bzip_alg;
+
+	bzip_alg.name = NULL;
+	bzip_alg.avail = 1;
+	bzip_alg.init = ext2_iBZIP2;
+	bzip_alg.compress = ext2_wBZIP2;
+	bzip_alg.decompress = ext2_rBZIP2;
+
+	return ext2_register_compression_module(EXT2_BZIP2_ALG, 
+			MAX(sizeof(Lib_Bzip_Encode_Storage_Ty),
+				sizeof(Lib_Bzip_Decode_Storage_Ty)),
+			sizeof(Lib_Bzip_Decode_Storage_Ty),
+			&bzip_alg);
+}
+
+void cleanup_module(void)
+{
+	ext2_unregister_compression_module(EXT2_BZIP2_ALG);
+}
+
+#endif
--- linux-2.6.25/fs/ext2/bzip2/lib_bzip_e.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/bzip2/lib_bzip_e.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,973 @@
+
+/*-------------------------------------------------------------*/
+/*-- bzip-like compression of 32k blocks        lib_bzip_e.c --*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is part of lib_bzip, a block-sorting compression 
+  library designed to compress 32kbyte blocks, for on-the-fly
+  disk compression/decompression.
+
+  Version 0.02, 4-Jan-1998
+
+  Copyright (C) 1996, 1997, 1998 by Julian Seward.
+     Guildford, Surrey, UK
+     email: jseward@acm.org
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--*/
+
+
+#include "lib_bzip_private.h"
+#include "lib_bzip.h"
+
+
+/*---------------------------------------------*/
+/*--
+   Compressed block format:
+
+      UChar 0xa2      -- version 0.02 of this lib
+      
+      (a single bit indicating whether or not this
+       block was randomised)
+
+      UInt16 origPtr  -- rotation posn of original
+
+      (table indicating which chars are in use)
+
+      (Huffman code length tables, one for each
+       of N_GROUPS)
+
+      (selector table)
+
+      (codes for the MTF values proper)
+
+--*/
+
+#ifdef __KERNEL__
+# include <linux/kernel.h>
+# include <linux/linkage.h>
+# include <linux/sched.h>
+# include <asm/param.h>
+# define __NO_VERSION__
+# include <linux/module.h>
+
+static unsigned long next_brk = 0;
+extern unsigned long volatile jiffies;
+
+/* Time slice of a fifth of a second. */
+# define MAYBE_SCHEDULE                 \
+   if (jiffies > next_brk) {            \
+      schedule();                       \
+      next_brk = jiffies + HZ / 5;      \
+   }
+
+# define e_verbosity 0  /* EXT2_BZIP2_VERBOSITY */
+# define do_status_printf(_v, _a...) printk(KERN_DEBUG "bzip2: " _a)
+# define status_printf(_v, _a...) do { if (e_verbosity >= (_v)) printk(KERN_DEBUG "bzip2: " _a); } while(0)
+#else
+
+# define MAYBE_SCHEDULE /*--*/
+# define e_verbosity est->verb
+# define do_status_printf(_v, _a...) fprintf(stderr, "" _a)
+# define status_printf(_v, _a...) do { if (e_verbosity >= (_v)) fprintf(stderr, "" _a); } while(0)
+#endif
+
+/*---------------------------------------------*/
+/*-- 
+   Bit stream writing stuff (boring but necessary).
+--*/
+
+static void 
+bsInit_e ( Lib_Bzip_Encode_Storage_Ty *est )
+{
+   est->outctr = 0;
+   est->bsLive = 0;
+   est->bsBuff = 0;
+}
+
+
+#define bs_MAKE_SPACE                         \
+do {                                          \
+   while (est->bsLive >= 8) {                 \
+      est->outbuff[est->outctr++]             \
+         = (UChar)(est->bsBuff >> 24);        \
+      est->bsBuff <<= 8;                      \
+      est->bsLive -= 8;                       \
+   }                                          \
+} while(0)
+
+
+static inline
+void 
+bsW ( Lib_Bzip_Encode_Storage_Ty *est, Int32 n, UInt32 v )
+{
+   bs_MAKE_SPACE;
+   est->bsBuff |= (v << (32 - est->bsLive - n));
+   est->bsLive += n;
+}
+
+
+static void 
+bsFlush_e ( Lib_Bzip_Encode_Storage_Ty *est )
+{
+   while (est->bsLive > 0) {
+      est->outbuff[est->outctr++]
+         = (UChar)(est->bsBuff >> 24);
+      est->bsBuff <<= 8;
+      est->bsLive -= 8;
+   }
+}
+
+
+/*---------------------------------------------*/
+static inline
+Bool 
+fullGt ( Lib_Bzip_Encode_Storage_Ty *est, Int32 i1, Int32 i2 )
+{
+   UInt16 s1, s2;
+   Int32 k;
+
+   if (i1 == i2) return False;
+
+   s1 = est->block[i1]; s2 = est->block[i2];
+   if (s1 != s2) return (s1 > s2);
+   i1 += 2; i2 += 2;
+
+   s1 = est->block[i1]; s2 = est->block[i2];
+   if (s1 != s2) return (s1 > s2);
+   i1 += 2; i2 += 2;
+
+   s1 = est->block[i1]; s2 = est->block[i2];
+   if (s1 != s2) return (s1 > s2);
+   i1 += 2; i2 += 2;
+
+   k = est->nblock + 16;
+
+   do {
+
+      s1 = est->block[i1]; s2 = est->block[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1 += 2; i2 += 2;
+
+      s1 = est->block[i1]; s2 = est->block[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1 += 2; i2 += 2;
+
+      s1 = est->block[i1]; s2 = est->block[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1 += 2; i2 += 2;
+
+      if (i1 >= (Int32) est->nblock)
+         i1 -= est->nblock;
+      if (i2 >= (Int32) est->nblock)
+         i2 -= est->nblock;
+
+      k -= 6;
+      est->workDone ++;
+   }
+      while (k >= 0);
+
+   return False;
+}
+
+
+/*---------------------------------------------*/
+/*  This qsort is derived from Weiss' book 
+    "Data Structures and Algorithm Analysis in C",
+    Section 7.7.
+*/
+
+#define ISORT_BELOW 10
+
+#define SWAP(za,zb)                                               \
+do { Int32 zl = (za); Int32 zr = (zb);                            \
+     UInt16 zt = est->zptr[zl]; est->zptr[zl] = est->zptr[zr];    \
+     est->zptr[zr] = zt;                                          \
+   } while(0)
+
+static void 
+sortBucket ( Lib_Bzip_Encode_Storage_Ty *est, Int32 left, Int32 right )
+{
+   UInt16 v;
+   Int32 pivot;
+   Int32 i, j;
+   Int32 wuC;
+
+   UInt16 stackL[20];
+   UInt16 stackR[20];
+
+   Int32 sp = 0;
+   Int32 wuL = left;
+   Int32 wuR = right;
+
+   if (left >= right) return;
+
+   while (True) {
+
+      /*-- 
+         At the beginning of this loop, wuL and wuR hold the
+         bounds of the next work-unit.  First, though, check
+         that we are not out of CPU time.
+      --*/
+      MAYBE_SCHEDULE;
+
+      if (wuR - wuL > ISORT_BELOW) {
+
+         /*--
+            a large Work Unit; partition-exchange 
+         --*/
+         wuC = (wuL + wuR) >> 1;
+         if (fullGt ( est, 1+(Int32)est->zptr[wuL], 1+(Int32)est->zptr[wuC] ))
+            SWAP ( wuL, wuC );
+         if (fullGt ( est, 1+(Int32)est->zptr[wuL], 1+(Int32)est->zptr[wuR] ))
+            SWAP ( wuL, wuR );
+         if (fullGt ( est, 1+(Int32)est->zptr[wuC], 1+(Int32)est->zptr[wuR] ))
+            SWAP ( wuC, wuR );
+
+         SWAP ( wuC, wuR-1 );
+         pivot = 1 + (Int32)est->zptr[wuR-1];
+
+         i = wuL;
+         j = wuR - 1;
+         for (;;) {
+            do i++; while (fullGt ( est, pivot, 1+(Int32)est->zptr[i] ));
+            do j--; while (fullGt ( est, 1+(Int32)est->zptr[j], pivot ));
+            if (i < j) SWAP ( i, j ); else break;
+            if (!est->randomised && est->workDone > est->workLimit) return;
+         }
+         SWAP ( i, wuR-1 );
+
+         if ((i - wuL) > (wuR - i)) {
+            stackL[sp] = wuL; stackR[sp] = i-1; sp++; wuL = i+1;
+         } else {
+            stackL[sp] = i+1; stackR[sp] = wuR; sp++; wuR = i-1;
+         }
+
+      } else {
+
+         /*--
+            a small Work-Unit; insertion-sort it
+         --*/                
+         for (i = wuL + 1; i <= wuR; i++) {
+            v = est->zptr[i];
+            j = i;
+            while ( fullGt ( est, 1+(Int32)est->zptr[j-1], 1+(Int32)v )) {
+               est->zptr[j] = est->zptr[j-1];
+               j = j - 1;
+               if (j <= wuL) break;
+            }
+            est->zptr[j] = v;
+            if (!est->randomised && est->workDone > est->workLimit) return;
+         }
+         if (sp == 0) return;
+         sp--; wuL = stackL[sp]; wuR = stackR[sp];
+
+      } /* if this is a small work-unit */
+   }
+}
+
+#undef RC
+#undef SWAP
+#undef ISORT_BELOW
+
+
+
+
+/*---------------------------------------------*/
+#define BIGFREQ(zz) (est->ftab[(zz)+1] - est->ftab[(zz)])
+
+#define FREQ(zz)    (est->ftab[(zz)*3 +3] - est->ftab[(zz)*3 +0])
+#define FREQ_LT(zz) (est->ftab[(zz)*3 +1] - est->ftab[(zz)*3 +0])
+#define FREQ_EQ(zz) (est->ftab[(zz)*3 +2] - est->ftab[(zz)*3 +1])
+#define FREQ_GT(zz) (est->ftab[(zz)*3 +3] - est->ftab[(zz)*3 +2])
+
+#define TIMES3(zz) ((zz)+(zz)+(zz))
+
+static void 
+sortBlock ( Lib_Bzip_Encode_Storage_Ty *est )
+{
+   Int32 j, k, totalOrdered, bucket;
+   unsigned i;
+   UInt16 s, sLo, sHi;
+   Int32 ftabNo;
+
+   MAYBE_SCHEDULE;
+
+   status_printf(3, "      bucket sort ...\n");
+
+   // stripe the block, and set up overshoot area
+
+   if (est->nblock == 1) {
+      est->block[0] &= 0xff00;
+      est->block[0] = (est->block[0] >> 8) | est->block[0];
+   } else {
+      for (i = 0; i < est->nblock - 1; i++) {
+         est->block[i] &= 0xff00;
+         est->block[i] |= (est->block[i+1] >> 8);
+      }
+      est->block[est->nblock-1] &= 0xff00;
+      est->block[est->nblock-1] |= (est->block[0] >> 8);
+   }
+
+   for (i = 0; i < N_OVERSHOOT; i++)
+      est->block[i + est->nblock] = est->block[i % est->nblock];
+
+
+   // set up freq table
+
+   for (i = 0; i < 769; i++) est->ftab[i] = 0;
+
+   for (i = 0; i < est->nblock; i++) {
+      s = est->block[i];
+      sHi = s >> 8;
+      sLo = s & 0xFF;
+      if (sHi > sLo) ftabNo = TIMES3(sHi) + 0; else
+      if (sHi < sLo) ftabNo = TIMES3(sHi) + 2; else
+                     ftabNo = TIMES3(sHi) + 1;
+      est->ftab[ftabNo]++;
+   }
+
+   for (i = 1; i < 769; i++) est->ftab[i] += est->ftab[i-1];
+
+   for (i = 0; i < est->nblock; i++) {
+      s = est->block[i];
+      sHi = s >> 8;
+      sLo = s & 0xFF;
+      if (sHi > sLo) ftabNo = TIMES3(sHi) + 0; else
+      if (sHi < sLo) ftabNo = TIMES3(sHi) + 2; else
+                     ftabNo = TIMES3(sHi) + 1;
+      j = est->ftab[ftabNo] - 1;
+      est->ftab[ftabNo] = j;
+      est->zptr[j] = i;
+   }
+
+   for (i = 0; i < 256; i++)
+      est->inUse[i] = (FREQ(i) > 0);
+   for (i = 0; i < 256; i++)
+      est->rorder[i] = i;
+
+   /* */
+   {
+      Int32 vv;
+      Int32 h = 1;
+      do h = 3 * h + 1; while (h <= 256);
+      do {
+         h = h / 3;
+         for (i = h; i <= 255; i++) {
+            vv = est->rorder[i];
+            j = i;
+            while ( FREQ(est->rorder[j-h]) > FREQ(vv) ) {
+               est->rorder[j] = est->rorder[j-h];
+               j = j - h;
+               if (j < (Int32) h) break;
+            }
+            est->rorder[j] = vv;
+         }
+      } while (h != 1);
+   }
+
+   totalOrdered = 0;
+   for (i = 0; i < 256; i++) {
+      MAYBE_SCHEDULE;
+
+      bucket = est->rorder[i];
+
+      if (FREQ_LT(bucket) > 1) {
+	 status_printf ( 3, "      sort 0x%2x <  size %-4d   ", 
+			 bucket, FREQ_LT(bucket) );
+         sortBucket ( est,
+                      est->ftab[TIMES3(bucket) +0], 
+                      est->ftab[TIMES3(bucket) +1]-1 );
+         totalOrdered += FREQ_LT(bucket);
+         status_printf ( 3, "done %d\n", totalOrdered );
+         if (!est->randomised && est->workDone > est->workLimit) return;
+      }
+
+      if (FREQ_GT(bucket) > 1) {
+	 status_printf ( 3, "      sort 0x%2x >  size %-4d   ", 
+			 bucket, FREQ_GT(bucket) );
+         sortBucket ( est,
+                      est->ftab[TIMES3(bucket) +2], 
+                      est->ftab[TIMES3(bucket) +3]-1 );
+         totalOrdered += FREQ_LT(bucket);
+         status_printf ( 3, "done %d\n", totalOrdered );
+         if (!est->randomised && est->workDone > est->workLimit) return;
+      }
+
+      if (FREQ_EQ(bucket) > 1) {
+         Int32 put0, get0, put1, get1;
+         UChar c1;
+         Int32 sbn = TIMES3(bucket) + 1;
+         Int32 lo = est->ftab[sbn];
+         Int32 hi = est->ftab[sbn+1] - 1;
+         UChar ssc = (UChar)bucket;
+
+	 status_printf ( 3, "      copy 0x%2x =  size %-4d   ", 
+			 bucket, FREQ_EQ(bucket) );
+
+         put0 = lo;
+         get0 = est->ftab[TIMES3(bucket)];
+         put1 = hi;
+         get1 = est->ftab[TIMES3(bucket)+3] - 1;
+         while (get0 < put0) {
+            j = est->zptr[get0]-1; if (j < 0) j += est->nblock;
+            c1 = (UChar)(est->block[j] >> 8);
+            if (c1 == ssc) { est->zptr[put0] = j; put0++; };
+            get0++;
+         }
+         while (get1 > put1) {
+            j = est->zptr[get1]-1; if (j < 0) j += est->nblock;
+            c1 = (UChar)(est->block[j] >> 8);
+            if (c1 == ssc) { est->zptr[put1] = j; put1--; };
+            get1--;
+         }
+         totalOrdered += FREQ_EQ(bucket);
+         status_printf ( 3, "done %d\n", totalOrdered );
+      }
+
+      if (i < 255 && FREQ(bucket) < 256) {
+         Int32 loLim = est->ftab[TIMES3(bucket)];
+         Int32 hiLim = est->ftab[TIMES3(bucket) + 3];
+         for (j = 0, k = loLim;   k < hiLim;   j++, k++) {
+            UInt16 a2upd = est->zptr[k];
+            est->block[a2upd] = (est->block[a2upd] & 0xff00) | ((UInt16)j);
+            if (a2upd < N_OVERSHOOT)
+               est->block[a2upd + est->nblock] 
+                  = (est->block[a2upd + est->nblock] & 0xff00) | ((UInt16)j);
+         }
+      }
+
+   }
+}
+
+#undef FREQ
+#undef FREQ_LT
+#undef FREQ_EQ
+#undef FREQ_GT
+#undef TIMES3
+
+
+/*---------------------------------------------*/
+static void makeMaps_e ( Lib_Bzip_Encode_Storage_Ty *est )
+{
+   unsigned i;
+
+   est->nInUse = 0;
+   for (i = 0; i < 256; i++)
+      if (est->inUse[i]) {
+         est->unseqToSeq[i] = est->nInUse;
+         est->nInUse++;
+      }
+}
+
+
+
+/*---------------------------------------------*/
+static void 
+generateMTFValues ( Lib_Bzip_Encode_Storage_Ty *est )
+{
+   UChar   yy[256];
+   unsigned i;
+   Int32   j;
+   UChar   tmp;
+   UChar   tmp2;
+   Int32   zPend;
+   Int32   wr;
+   Int32   EOB;
+
+   MAYBE_SCHEDULE;
+
+   makeMaps_e (est);
+   EOB = est->nInUse + RUNBASE - 1;
+
+   for (i = 0; (Int32) i <= EOB; i++) est->mtfFreq[i] = 0;
+
+   wr = 0;
+   zPend = 0;
+   for (i = 0; (Int32) i < est->nInUse; i++)
+      yy[i] = (UChar) i;
+
+   for (i = 0; i < est->nblock; i++) {
+      UChar ll_i;
+
+      j = est->zptr[i] - 1; if (j < 0) j = est->nblock-1;      
+      ll_i = est->unseqToSeq[est->block[j] >> 8];
+
+      j = 0;
+      tmp = yy[j];
+      while ( ll_i != tmp ) {
+         j++;
+         tmp2 = tmp;
+         tmp = yy[j];
+         yy[j] = tmp2;
+      };
+      yy[0] = tmp;
+
+      if (j == 0) {
+         zPend++;
+      } else {
+         if (zPend > 0) {
+            zPend--;
+            while (True) {
+               switch (zPend % RUNBASE) {
+                  case 0: est->zptr[wr] = RUNA; wr++; est->mtfFreq[RUNA]++; break;
+                  case 1: est->zptr[wr] = RUNB; wr++; est->mtfFreq[RUNB]++; break;
+               };
+               if (zPend < RUNBASE) break;
+               zPend = (zPend - RUNBASE) / RUNBASE;
+            };
+            zPend = 0;
+         }
+         est->zptr[wr] = j+RUNBASE-1; wr++; est->mtfFreq[j+RUNBASE-1]++;
+      }
+   }
+
+   if (zPend > 0) {
+      zPend--;
+      while (True) {
+         switch (zPend % RUNBASE) {
+            case 0:  est->zptr[wr] = RUNA; wr++; est->mtfFreq[RUNA]++; break;
+            case 1:  est->zptr[wr] = RUNB; wr++; est->mtfFreq[RUNB]++; break;
+         };
+         if (zPend < RUNBASE) break;
+         zPend = (zPend - RUNBASE) / RUNBASE;
+      };
+   }
+
+   est->zptr[wr] = EOB; wr++; est->mtfFreq[EOB]++;
+
+   est->nMTF = wr;
+}
+
+
+/*---------------------------------------------*/
+#define LESSER_ICOST  0
+#define GREATER_ICOST 15
+
+static Bool
+sendMTFValues ( Lib_Bzip_Encode_Storage_Ty *est )
+{
+   Int32 v, t, i, j, gs, ge, totc, bt, bc, iter;
+   Int32 alphaSize, selCtr;
+   UInt31 nSelectors;
+   Int32 nBytes;
+
+   /*--
+   UChar  len [N_GROUPS][MAX_ALPHA_SIZE];
+   is a global since the decoder also needs it.
+
+   UInt32 code[N_GROUPS][MAX_ALPHA_SIZE];
+   Int32  rfreq[N_GROUPS][MAX_ALPHA_SIZE];
+   are also globals only used in this proc.
+   Made global to keep stack frame size small.
+   --*/
+
+   UInt16 cost[N_GROUPS];
+   Int32  fave[N_GROUPS];
+
+   MAYBE_SCHEDULE;
+
+   status_printf ( 3, 
+                "      %u in block, %d after MTF & 1-2 coding, %u+2 syms in use\n", 
+                est->nblock, est->nMTF, est->nInUse );
+
+   alphaSize = est->nInUse + RUNBASE;
+   for (t = 0; t < N_GROUPS; t++)
+      for (v = 0; v < alphaSize; v++)
+         est->len[t][v] = GREATER_ICOST;
+
+   /*--- Generate an initial set of coding tables ---*/
+   { 
+      Int32 nPart, remF, tFreq, aFreq;
+
+      nPart = N_GROUPS;
+      remF  = est->nMTF;
+      gs = 0;
+      while (nPart > 0) {
+         tFreq = remF / nPart;
+         ge = gs-1;
+         aFreq = 0;
+         while (aFreq < tFreq && ge < alphaSize-1) {
+            ge++;
+            aFreq += est->mtfFreq[ge];
+         }
+
+         if (ge > gs 
+             && nPart != N_GROUPS && nPart != 1 
+             && ((N_GROUPS-nPart) % 2 == 1)) {
+            aFreq -= est->mtfFreq[ge];
+            ge--;
+         }
+#ifndef __KERNEL__  /* floating point (todo) */
+         status_printf ( 3, "      initial group %d, [%d .. %d], has %d syms (%4.1f%%)\n",
+			 nPart, gs, ge, aFreq, 
+			 (100.0 * (float)aFreq) / (float)est->nMTF );
+#endif
+         for (v = 0; v < alphaSize; v++)
+            if (v >= gs && v <= ge) 
+               est->len[nPart-1][v] = LESSER_ICOST; else
+               est->len[nPart-1][v] = GREATER_ICOST;
+ 
+         nPart--;
+         gs = ge+1;
+         remF -= aFreq;
+      }
+   }
+
+   /*--- 
+      Iterate up to N_ITERS times to improve the tables.
+   ---*/
+   for (iter = 0; iter < N_ITERS; iter++) {
+
+      MAYBE_SCHEDULE;
+
+      for (t = 0; t < N_GROUPS; t++) fave[t] = 0;
+
+      for (t = 0; t < N_GROUPS; t++)
+         for (v = 0; v < alphaSize; v++)
+            est->rfreq[t][v] = 0;
+
+      nSelectors = 0;
+      totc = 0;
+      gs = 0;
+      while (True) {
+
+         /*--- Set group start & end marks. --*/
+         if (gs >= est->nMTF) break;
+         ge = gs + G_SIZE - 1; 
+         if (ge >= est->nMTF) ge = est->nMTF-1;
+
+         /*-- 
+            Calculate the cost of this group as coded
+            by each of the coding tables.
+         --*/
+         for (t = 0; t < N_GROUPS; t++)
+	    cost[t] = 0;
+
+	 /* */
+         {
+            register UInt16 cost0, cost1;
+
+#if N_GROUPS != 2
+# error "bad assumption here"
+#endif
+            cost0 = cost1 = 0;
+            for (i = gs; i <= ge; i++) { 
+               UInt16 icv = est->zptr[i];
+               cost0 += est->len[0][icv];
+               cost1 += est->len[1][icv];
+            }
+            cost[0] = cost0; cost[1] = cost1;
+         }
+ 
+         /*-- 
+            Find the coding table which is best for this group,
+            and record its identity in the selector table.
+         --*/
+         bc = 999999999; bt = -1;
+         for (t = 0; t < N_GROUPS; t++)
+            if (cost[t] < bc) { bc = cost[t]; bt = t; };
+         totc += bc;
+         fave[bt]++;
+         est->selector[nSelectors] = bt;
+         nSelectors++;
+
+         /*-- 
+            Increment the symbol frequencies for the selected table.
+          --*/
+         for (i = gs; i <= ge; i++)
+            est->rfreq[bt][ est->zptr[i] ]++;
+
+         gs = ge+1;
+      }
+
+#ifndef __KERNEL__  /* too hard... todo */
+      if (e_verbosity >= 3) {
+         status_printf ( 3, "      pass %d: size is %d, grp uses are ", 
+			 iter+1, totc/8 );
+         for (t = 0; t < N_GROUPS; t++)
+            status_printf ( 3, "%d ", fave[t] );
+         fprintf ( stderr, "\n" );
+      }
+#endif
+
+      /*--
+        Recompute the tables based on the accumulated frequencies.
+      --*/
+      for (t = 0; t < N_GROUPS; t++)
+         hbMakeCodeLengths ( 
+            &(est->len[t][0]), &(est->rfreq[t][0]), alphaSize, 20,
+            &(est->heap_tmp[0]), &(est->weight_tmp[0]), &(est->parent_tmp[0])
+         );
+   }
+
+   /*--- Assign actual codes for the tables. --*/
+   for (t = 0; t < N_GROUPS; t++) {
+      unsigned minLen = 32;
+      unsigned maxLen = 0;
+
+      for (i = 0; i < alphaSize; i++) {
+         if (est->len[t][i] > maxLen)
+	    maxLen = est->len[t][i];
+         if (est->len[t][i] < minLen)
+	    minLen = est->len[t][i];
+      }
+      hbAssignCodes ( &(est->code[t][0]), &(est->len[t][0]), 
+                      minLen, maxLen, alphaSize );
+   }
+
+   /*--- Transmit the mapping table. ---*/
+   { 
+      Bool inUse16[16];
+
+      for (i = 0; i < 16; i++) {
+          inUse16[i] = 0;
+          for (j = 0; j < 16; j++)
+             if (est->inUse[i * 16 + j])
+		inUse16[i] = 1;
+      }
+     
+      nBytes = est->outctr;
+      for (i = 0; i < 16; i++)
+	 bsW(est, 1, inUse16[i]);
+
+      for (i = 0; i < 16; i++)
+         if (inUse16[i]) {
+            for (j = 0; j < 16; j++)
+               if (est->inUse[i * 16 + j]) 
+                  { bsW(est, 1,1); }
+	       else 
+                  { bsW(est, 1,0); };
+         }
+      status_printf ( 3, " bytes: mapping %d, ", est->outctr-nBytes );
+   }
+
+   /*--- Now the selectors. ---*/
+   nBytes = est->outctr;
+   bsW ( est, 10, nSelectors );
+   for (i = 0; i < (Int32) nSelectors; i++)
+      bsW(est, 1, (est->selector[i] != 0));
+
+   status_printf ( 3, "selectors %d, ", est->outctr-nBytes );
+
+   /*--- Now the coding tables. ---*/
+   nBytes = est->outctr;
+
+   for (t = 0; t < N_GROUPS; t++) {
+      unsigned curr = est->len[t][0];
+      bsW ( est, 5, curr );
+      for (i = 0; i < alphaSize; i++) {
+         while (curr < est->len[t][i]) { bsW(est, 2,2); curr++; /* 2 -> binary 10 */ };
+         while (curr > est->len[t][i]) { bsW(est, 2,3); curr--; /* 3 -> binary 11 */ };
+         bsW ( est, 1, 0 );
+      }
+   }
+
+   status_printf ( 3, "code lengths %d, ", est->outctr - nBytes );
+
+
+   /*--
+      Compute the total size of output, and check whether 
+      it will fit into the caller's output buffer, and, 
+      if not, give up now.
+   --*/
+   {
+      Int32 totalBits = 8 * est->outctr;
+
+      selCtr = 0;
+      gs = 0;
+      while (True) {
+         UChar* ltab;
+         if (gs >= est->nMTF) break;
+         ge = gs + G_SIZE - 1; 
+         if (ge >= est->nMTF) ge = est->nMTF-1;
+         ltab = &(est->len[est->selector[selCtr]][0]);
+         for (i = gs; i <= ge; i++)
+            totalBits += ltab[est->zptr[i]];
+         gs = ge+1;
+         selCtr++;
+      }
+      /*-- allow for worst-case flushing of bit buffer --*/
+      totalBits += 32;
+      /*-- convert to bytes --*/
+      totalBits = (totalBits + 7) / 8;
+      est->predictedSize = totalBits;
+      if (totalBits > est->totalSizeAllowed) return False;
+   }
+
+
+   /*--- And finally, the block data proper ---*/
+   nBytes = est->outctr;
+   selCtr = 0;
+   gs = 0;
+   while (True) {
+      UChar*  ltab;
+      UInt32* ctab;
+
+      if (gs >= est->nMTF) break;
+      ge = gs + G_SIZE - 1; 
+      if (ge >= est->nMTF) ge = est->nMTF-1;
+      ltab = &(est->len [est->selector[selCtr]][0]);
+      ctab = &(est->code[est->selector[selCtr]][0]);
+      for (i = gs; i <= ge; i++) {
+         UInt16 zptr_i = est->zptr[i];
+         bsW ( est, ltab[zptr_i], ctab[zptr_i] );
+      }
+      gs = ge+1;
+      selCtr++;
+   }
+
+   status_printf ( 3, "codes %d\n", est->outctr-nBytes );
+
+   return True;
+}
+
+
+/*---------------------------------------------*/
+Int16 const rNums[64] = {    
+   619, 720, 127, 481, 931, 816, 813, 233, 566, 247, 
+   985, 724, 205, 454, 863, 491, 741, 242, 949, 214, 
+   733, 859, 335, 708, 621, 574, 73, 654, 730, 472, 
+   419, 436, 278, 496, 867, 210, 399, 680, 480, 51, 
+   878, 465, 811, 169, 869, 675, 611, 697, 867, 561, 
+   862, 687, 507, 283, 482, 129, 807, 591, 733, 623, 
+   150, 238, 59, 379
+};
+
+
+static void 
+randomiseBlock ( Lib_Bzip_Encode_Storage_Ty *est )
+{
+   unsigned i;
+   RAND_DECLS;
+
+   for (i = 0; i < est->nblock; i++) {
+      RAND_UPD_MASK;
+      est->block[i] ^= RAND_MASK << 8;
+   }
+}
+
+
+/*---------------------------------------------*/
+size_t 
+bzip_compressBlock ( unsigned char *in_buf, 
+		     unsigned char *out_buf,
+		     void *heap,
+		     size_t num_in_buf,
+		     size_t num_out_buf,
+		     int param /* unused */ )
+{
+   size_t i;
+   Bool fitsOk;
+   Lib_Bzip_Encode_Storage_Ty *est;
+
+   if (num_in_buf == 0)
+      return 0; /* Some functions (e.g. sortBlock() don't like nblock to equal 0. */
+
+   if (!try_module_get(THIS_MODULE))
+      return 0;
+
+#if 0
+   est = (Lib_Bzip_Encode_Storage_Ty *) bzip2_work_area_C;
+#else
+   est = (Lib_Bzip_Encode_Storage_Ty *) heap;
+#endif
+
+   est->totalSizeAllowed = num_out_buf;
+   est->verb             = 0;
+   est->nblock           = num_in_buf;
+
+   // make est->block a copy of in_buf <<\'d 8
+
+   for (i = 0; i < num_in_buf; i++)
+      est->block[i] = ((UInt16)in_buf[i]) << 8;
+
+   // try to sort it
+   est->randomised = False;
+   est->workLimit  = 250 * est->nblock;
+   est->workDone   = 0;
+   sortBlock (est);
+#ifndef __KERNEL__  /* floating point (todo) */
+   status_printf ( 2, "      %d work, %u nblock, %5.2f ratio\n",
+		   est->workDone, est->nblock,
+		   (float)est->workDone /
+		   (float)(est->nblock==0 ? 1 : est->nblock) );
+#endif
+   // if sorting failed because of too much work, 
+   // randomise and try again (this can\'t fail).
+
+   if (est->workDone > est->workLimit) {
+      status_printf ( 2, "      sorting aborted; randomising block\n" );
+      randomiseBlock (est);
+      est->randomised = True;
+      est->workLimit = 0;
+      est->workDone = 0;
+      sortBlock (est);
+#ifndef __KERNEL__  /* floating point (todo) */
+      status_printf ( 3, "      %d work, %d nblock, %5.2f ratio\n",
+		      est->workDone, est->nblock,
+		      (float)est->workDone /
+		      (float)(est->nblock==0 ? 1 : est->nblock) );
+#endif
+   }
+
+   // find the posn of the original string
+
+   i = 0;
+   while (i < est->nblock && est->zptr[i] != 0)
+      i++;
+
+   est->outbuff = out_buf;
+   bsInit_e (est);
+   bsW ( est, 8, 0xa2 );  /* version number */
+
+   if (est->randomised) bsW(est, 1,1); else bsW(est, 1,0);
+
+   bsW ( est, 16, i );
+
+   generateMTFValues (est);
+
+   fitsOk = sendMTFValues (est);
+   if (!fitsOk) {
+      module_put(THIS_MODULE);
+      return 0;
+   }
+
+   if (est->outctr > est->predictedSize) {
+#ifdef __KERNEL__
+      printk ( KERN_ERR
+	       "bzip2 panic: predicted size (%d) less than actual (%d)\n",
+	       est->predictedSize, est->outctr );
+      module_put(THIS_MODULE);
+      return 0;
+#else
+      fprintf ( stderr, "panic: predicted size is too small\n" );
+      fprintf ( stderr, "       predicted %d, actual %d\n", 
+                       est->predictedSize, est->outctr );
+      exit(1);
+#endif
+   }
+
+   bsFlush_e (est);
+
+   module_put(THIS_MODULE);
+   return est->outctr;
+}
+
+
+/*-------------------------------------------------------------*/
+/*-- end                                        lib_bzip_e.c --*/
+/*-------------------------------------------------------------*/
--- linux-2.6.25/fs/ext2/bzip2/lib_bzip_huffman.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/bzip2/lib_bzip_huffman.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,210 @@
+
+/*-------------------------------------------------------------*/
+/*-- Huffman coding low-level stuff                          --*/
+/*--                                      lib_bzip_huffman.c --*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is part of lib_bzip, a block-sorting compression 
+  library designed to compress 32kbyte blocks, for on-the-fly
+  disk compression/decompression.
+
+  Version 0.02, 4-Jan-1998
+
+  Copyright (C) 1996, 1997, 1998 by Julian Seward.
+     Guildford, Surrey, UK
+     email: jseward@acm.org
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--*/
+
+
+#include "lib_bzip_private.h"
+
+
+/*---------------------------------------------------*/
+/*--- Huffman coding low-level stuff              ---*/
+/*---------------------------------------------------*/
+
+#define WEIGHTOF(zz0)  ((zz0) & 0xffffff00U)
+#define DEPTHOF(zz1)   ((zz1) & 0x000000ffU)
+#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3))
+
+#define ADDWEIGHTS(zw1,zw2)                           \
+   (WEIGHTOF(zw1)+WEIGHTOF(zw2)) |                    \
+   (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2)))
+
+#define UPHEAP(z)                                     \
+do {                                                  \
+   Int32 zz, tmp;                                     \
+   zz = z; tmp = heap[zz];                            \
+   while (weight[tmp] < weight[heap[zz >> 1]]) {      \
+      heap[zz] = heap[zz >> 1];                       \
+      zz >>= 1;                                       \
+   }                                                  \
+   heap[zz] = tmp;                                    \
+} while(0)
+
+#define DOWNHEAP(z)                                   \
+do {                                                  \
+   Int32 zz, yy, tmp;                                 \
+   zz = z; tmp = heap[zz];                            \
+   while (True) {                                     \
+      yy = zz << 1;                                   \
+      if (yy > nHeap) break;                          \
+      if (yy < nHeap &&                               \
+          weight[heap[yy+1]] < weight[heap[yy]])      \
+         yy++;                                        \
+      if (weight[tmp] < weight[heap[yy]]) break;      \
+      heap[zz] = heap[yy];                            \
+      zz = yy;                                        \
+   }                                                  \
+   heap[zz] = tmp;                                    \
+} while(0)
+
+
+/*---------------------------------------------*/
+void hbMakeCodeLengths ( UChar* len, 
+                         Int32* freq,
+                         Int32  alphaSize,
+                         Int32  maxLen,
+                         // temporaries
+                         Int32* heap,
+                         Int32* weight,
+                         Int32* parent)
+{
+   /*--
+      Nodes and heap entries run from 1.  Entry 0
+      for both the heap and nodes is a sentinel.
+   --*/
+   Int32 nNodes, nHeap, n1, n2, i, j, k;
+   Bool  tooLong;
+
+   /*--
+      Now passed in from caller, so as to keep stack
+      frame small.
+   Int32 heap   [ MAX_ALPHA_SIZE + 2 ];
+   Int32 weight [ MAX_ALPHA_SIZE * 2 ];
+   Int32 parent [ MAX_ALPHA_SIZE * 2 ]; 
+   --*/
+
+   for (i = 0; i < alphaSize; i++)
+      weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8;
+
+   while (True) {
+
+      nNodes = alphaSize;
+      nHeap = 0;
+
+      heap[0] = 0;
+      weight[0] = 0;
+      parent[0] = -2;
+
+      for (i = 1; i <= alphaSize; i++) {
+         parent[i] = -1;
+         nHeap++;
+         heap[nHeap] = i;
+         UPHEAP(nHeap);
+      }
+   
+      while (nHeap > 1) {
+         n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
+         n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
+         nNodes++;
+         parent[n1] = parent[n2] = nNodes;
+         weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]);
+         parent[nNodes] = -1;
+         nHeap++;
+         heap[nHeap] = nNodes;
+         UPHEAP(nHeap);
+      }
+
+      tooLong = False;
+      for (i = 1; i <= alphaSize; i++) {
+         j = 0;
+         k = i;
+         while (parent[k] >= 0) { k = parent[k]; j++; }
+         len[i-1] = j;
+         if (j > maxLen) tooLong = True;
+      }
+      
+      if (! tooLong) break;
+
+      for (i = 1; i < alphaSize; i++) {
+         j = weight[i] >> 8;
+         j = 1 + (j / 2);
+         weight[i] = j << 8;
+      }
+   }
+}
+
+
+/*---------------------------------------------*/
+void hbAssignCodes ( UInt32 *code,
+                     UChar *length,
+                     unsigned minLen,
+                     unsigned maxLen,
+                     Int32 alphaSize )
+{
+   Int32 i;
+   UInt32 vec;
+   unsigned n;
+
+   vec = 0;
+   for (n = minLen; n <= maxLen; n++) {
+      for (i = 0; i < alphaSize; i++)
+         if (length[i] == n) { code[i] = vec; vec++; };
+      vec <<= 1;
+   }
+}
+
+
+/*---------------------------------------------*/
+void hbCreateDecodeTables ( Int32 *limit,
+                            Int32 *base,
+                            Int32 *perm,
+                            UChar *length,
+                            Int32 minLen,
+                            Int32 maxLen,
+                            Int32 alphaSize )
+{
+   Int32 pp, i, j, vec;
+
+   pp = 0;
+   for (i = minLen; i <= maxLen; i++)
+      for (j = 0; j < alphaSize; j++)
+         if (length[j] == i) { perm[pp] = j; pp++; };
+
+   for (i = 0; i < MAX_CODE_LEN; i++) base[i] = 0;
+   for (i = 0; i < alphaSize; i++) base[length[i]+1]++;
+
+   for (i = 1; i < MAX_CODE_LEN; i++) base[i] += base[i-1];
+
+   for (i = 0; i < MAX_CODE_LEN; i++) limit[i] = 0;
+   vec = 0;
+
+   for (i = minLen; i <= maxLen; i++) {
+      vec += (base[i+1] - base[i]);
+      limit[i] = vec-1;
+      vec <<= 1;
+   }
+   for (i = minLen + 1; i <= maxLen; i++)
+      base[i] = ((limit[i-1] + 1) << 1) - base[i];
+}
+
+
+/*-------------------------------------------------------------*/
+/*-- end                                  lib_bzip_huffman.c --*/
+/*-------------------------------------------------------------*/
--- linux-2.6.25/fs/ext2/bzip2/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/bzip2/Makefile	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,11 @@
+#
+# Makefile for the linux compression routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+obj-$(CONFIG_EXT2_HAVE_BZIP2) := ext2-compr-bzip2.o
+ext2-compr-bzip2-y := lib_bzip_huffman.o lib_bzip_e.o lib_bzip_d.o init.o
--- linux-2.6.25/fs/ext2/bzip2/TODO	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/bzip2/TODO	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,27 @@
+
+Test thoroughly
+
+   -- does it work for very small blocks (down to 1 byte?)
+      (Should do)
+
+      pjm: Incidentally, I've inserted a check `if (ulen == 0) return 0;'
+      as I don't think that the ulen==0 case would work correctly.
+
+      ulen =< 1024 shouldn't be an issue for e2compr, though: the 
+      compressor shouldn't even be called in this case.  When ext2fs 
+      supports fragments, this may change.  However, I suspect that the 
+      smallest possible allocation unit will even then be quite large.
+
+   -- does it always reliably ensure that bzip_compressBlock
+      returns 0 if the compressed data won't fit into the
+      specified space?  (Should do)
+
+      pjm: It doesn't.
+
+
+Check that all stack frames have insignificant size
+(I think so)
+pjm: I haven't found anything looking like a problem,
+     but I haven't been very thorough.
+
+Properly measure compress/decompress speeds 
--- linux-2.6.25/fs/ext2/bzip2/lib_bzip_d.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/bzip2/lib_bzip_d.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,427 @@
+
+/*-------------------------------------------------------------*/
+/*-- bzip-like decompression of 32KB blocks     lib_bzip_d.c --*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is part of lib_bzip, a block-sorting compression 
+  library designed to compress 32kbyte blocks, for on-the-fly
+  disk compression/decompression.
+
+  Version 0.02, 4-Jan-1998
+
+  Copyright (C) 1996, 1997, 1998 by Julian Seward.
+     Guildford, Surrey, UK
+     email: jseward@acm.org
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--*/
+
+
+#include "lib_bzip_private.h"
+#include "lib_bzip.h"
+
+#ifdef __KERNEL__
+# include <linux/linkage.h>
+# include <asm/param.h>
+# include <linux/string.h>
+# include <linux/sched.h>
+# define __NO_VERSION__
+# include <linux/module.h>
+
+static unsigned long next_brk = 0;
+extern unsigned long volatile jiffies;
+
+/* Time slice of a fifth of a second. */
+# define MAYBE_SCHEDULE do {            \
+   if (jiffies > next_brk) {            \
+      schedule();                       \
+      next_brk = jiffies + HZ / 5;      \
+   }} while(0)
+
+#else
+
+# include <string.h>
+# define MAYBE_SCHEDULE do { } while(0)
+
+#endif
+
+
+/*---------------------------------------------*/
+/*--
+   Bit stream reading stuff.
+--*/
+
+static void 
+bsInit_d ( Lib_Bzip_Decode_Storage_Ty *dst )
+{
+   dst->inctr = 0;
+   dst->bsLive = 0;
+   dst->bsBuff = 0;
+}
+
+
+#define bsNEEDR(nz)                                   \
+do {	                                              \
+   while (dst->bsLive < nz) {                          \
+      Int32 zzi = (Int32)(dst->inbuff[dst->inctr++]);   \
+      dst->bsBuff = (dst->bsBuff << 8) | (zzi & 0xffL); \
+      dst->bsLive += 8;                                \
+   }                                                  \
+} while(0)
+
+
+static inline
+UInt32 
+bsR ( Lib_Bzip_Decode_Storage_Ty *dst, Int32 n )
+{
+   UInt32 v;
+   bsNEEDR ( n );
+   v = (dst->bsBuff >> (dst->bsLive-n)) & ((1 << n)-1);
+   dst->bsLive -= n;
+   return v;
+}
+
+/*---------------------------------------------*/
+static void 
+makeMaps_d ( Lib_Bzip_Decode_Storage_Ty *dst )
+{
+   Int32 i;
+   dst->nInUse = 0;
+   for (i = 0; i < 256; i++)
+      if (dst->inUse[i]) {
+         dst->seqToUnseq[dst->nInUse] = i;
+         dst->nInUse++;
+      }
+}
+
+
+/*---------------------------------------------*/
+static void 
+recvDecodingTables ( Lib_Bzip_Decode_Storage_Ty *dst )
+{
+   Int32  i, j, t, nSelectors, alphaSize;
+   Int32  minLen, maxLen;
+   Bool inUse16[16];
+
+   dst->origPtr = bsR ( dst, 16 );
+
+   /*--- Receive the mapping table ---*/
+   for (i = 0; i < 16; i++)
+      if (bsR(dst, 1) == 1) 
+         inUse16[i] = True; else 
+         inUse16[i] = False;
+
+   for (i = 0; i < 256; i++) dst->inUse[i] = False;
+
+   for (i = 0; i < 16; i++)
+      if (inUse16[i])
+         for (j = 0; j < 16; j++)
+            if (bsR(dst, 1) == 1) dst->inUse[i * 16 + j] = True;
+
+   makeMaps_d (dst);
+   alphaSize = dst->nInUse + RUNBASE;
+
+   /*--- Now the selectors ---*/
+   nSelectors = bsR ( dst, 10 );
+   for (i = 0; i < nSelectors; i++)
+      dst->selector[i] = bsR(dst, 1);
+
+   /*--- Now the coding tables ---*/
+   for (t = 0; t < N_GROUPS; t++) {
+      Int32 curr = bsR ( dst, 5 );
+      for (i = 0; i < alphaSize; i++) {
+         while (bsR(dst, 1) == 1) {
+            if (bsR(dst, 1) == 0) curr++; else curr--;
+         }
+         dst->len[t][i] = curr;
+      }
+   }
+
+   /*--- Create the Huffman decoding tables ---*/
+   for (t = 0; t < N_GROUPS; t++) {
+      minLen = 32;
+      maxLen = 0;
+      for (i = 0; i < alphaSize; i++) {
+         if (dst->len[t][i] > maxLen) maxLen = dst->len[t][i];
+         if (dst->len[t][i] < minLen) minLen = dst->len[t][i];
+      }
+      hbCreateDecodeTables ( 
+         &(dst->limit[t][0]), &(dst->base[t][0]),
+         &(dst->perm[t][0]), &(dst->len[t][0]),
+         minLen, maxLen, alphaSize
+      );
+      dst->minLens[t] = minLen;
+   }
+}
+
+
+/*---------------------------------------------*/
+#define GET_MTF_VAL(lval)                 \
+do {                                      \
+   Int32 zn, zvec, zj;                    \
+   if (groupPos == 0) {                   \
+      groupNo++;                          \
+      groupPos = G_SIZE;                  \
+      gSel = dst->selector[groupNo];       \
+      gMinlen = dst->minLens[gSel];        \
+      gLimit = &(dst->limit[gSel][0]);     \
+      gPerm = &(dst->perm[gSel][0]);       \
+      gBase = &(dst->base[gSel][0]);       \
+   }                                      \
+   groupPos--;                            \
+   zn = gMinlen; zvec = bsR ( dst, zn );       \
+   while (zvec > gLimit[zn]) {            \
+      zn++; zj = bsR ( dst, 1 );               \
+      zvec = (zvec << 1) | zj;            \
+   };                                     \
+   lval = gPerm[zvec - gBase[zn]];        \
+} while(0)
+
+
+/*---------------------------------------------*/
+static void 
+decodeBlock ( Lib_Bzip_Decode_Storage_Ty *dst,
+              UInt32* cc, 
+              UChar*  mtfa, 
+              Int32*  mtfbase )
+{
+   Int32  nextSym;
+   Int32  EOB, groupNo, groupPos, gSel;
+   Int32  gMinlen = 0;
+   Int32* gLimit = NULL;
+   Int32* gBase = NULL;
+   Int32* gPerm = NULL;
+
+   Int32 lastR, op;
+
+   /*--
+      Now passed in from caller, so as to keep stack
+      frame small.
+   UInt32 cc[256];
+   UChar  mtfa   [MTFA_SIZE];
+   Int32  mtfbase[256 / MTFL_SIZE];
+   --*/
+
+   MAYBE_SCHEDULE;
+
+   recvDecodingTables (dst);
+
+   EOB      = dst->nInUse + RUNBASE - 1;
+   groupNo  = -1;
+   groupPos = 0;
+
+   memset(cc, 0, 256 * sizeof(*cc));
+
+   //-- MTF init
+   {
+      unsigned i, j, k;
+
+      k = MTFA_SIZE-1;
+      for (i = 256 / MTFL_SIZE; i-- > 0;) {
+         for (j = MTFL_SIZE; j-- > 0;) {
+            mtfa[k] = (UChar)(i * MTFL_SIZE + j);
+            k--;
+         }
+         mtfbase[i] = k + 1;
+      }
+   }
+   //-- end MTF init
+
+   lastR = -1;
+
+   MAYBE_SCHEDULE;
+
+   GET_MTF_VAL(nextSym);
+
+   while (True) {
+
+      if (nextSym < RUNBASE) {
+         UChar ch;
+         Int32 sum = -1;
+         Int32 N = 1;
+         do {
+            if (nextSym == RUNA) sum = sum + (0+1) * N;
+            else if (nextSym == RUNB) sum = sum + (1+1) * N;
+            N = N * RUNBASE;
+            GET_MTF_VAL(nextSym);
+         }
+            while (nextSym < RUNBASE);
+
+         sum++;
+         ch = dst->seqToUnseq[ mtfa[mtfbase[0]] ];
+
+         while (sum > 0) {
+            lastR++;
+            dst->tvec[lastR] = (ch << 24) | cc[ch];
+            cc[ch]++;
+            sum--;
+         };
+
+         continue;
+
+      } else if (nextSym == EOB) {
+
+         break;
+
+      } else {
+
+         UChar tmp, ch;
+         lastR++;
+
+         //-- tmp = MTF ( nextSym-(RUNBASE-1) );
+         {
+            Int32 i, j, k, n, p, lno, off;
+            n = (Int32)(nextSym - (RUNBASE-1) );
+
+            if (n < MTFL_SIZE) { // avoid general-case expense
+               p = mtfbase[0];
+               tmp = mtfa[p+n];
+               while (n > 3) {
+                  Int32 z = p+n;
+                  mtfa[(z)  ] = mtfa[(z)-1];
+                  mtfa[(z)-1] = mtfa[(z)-2];
+                  mtfa[(z)-2] = mtfa[(z)-3];
+                  mtfa[(z)-3] = mtfa[(z)-4];
+                  n -= 4;
+               }
+               while (n > 0) { mtfa[(p+n)] = mtfa[(p+n)-1]; n--; };
+               mtfa[p] = tmp;
+            } else { // general case
+               lno = n / MTFL_SIZE;
+               off = n % MTFL_SIZE;
+               p = mtfbase[lno] + off;
+               tmp = mtfa[p];
+               while (p > mtfbase[lno]) { mtfa[p] = mtfa[p-1]; p--; };
+               mtfbase[lno]++;
+               while (lno > 0) {
+                  mtfbase[lno]--;
+                  mtfa[mtfbase[lno]] = mtfa[mtfbase[lno-1] + MTFL_SIZE - 1];
+                  lno--;
+               }
+               mtfbase[0]--;
+               mtfa[mtfbase[0]] = tmp;
+               if (mtfbase[0] == 0) {
+                  k = MTFA_SIZE-1;
+                  for (i = 256 / MTFL_SIZE-1; i >= 0; i--) {
+                     for (j = MTFL_SIZE-1; j >= 0; j--) {
+                        mtfa[k] = mtfa[mtfbase[i] + j];
+                        k--;
+                     }
+                     mtfbase[i] = k + 1;
+                  }
+	       }
+            }
+         }
+         //-- end tmp = MTF ( nextSym-(RUNBASE-1) );
+
+         ch = dst->seqToUnseq[tmp];
+         dst->tvec[lastR] = (ch << 24) | cc[ch]; 
+         cc[ch]++;
+
+         GET_MTF_VAL(nextSym);
+         continue;
+      }
+   }
+
+   MAYBE_SCHEDULE;
+
+   /* */
+   {
+      UInt32 k;
+      unsigned i;
+      Int32 sum;
+
+      k = dst->origPtr;
+      sum = 0;
+      for (i = 0; i < 256; i++) {
+	 sum += cc[i];
+	 cc[i] = sum - cc[i];
+      }
+
+      op = lastR+1;
+      dst->nblock = op;
+      while (op > 0) {
+	 UInt32 u;
+	 UChar v;
+	 u = dst->tvec[k]; 
+	 v = u >> 24; 
+	 k = (u & 0x00ffffff) + cc[v];
+	 op--; dst->outbuff[op] = v;
+      }
+   }
+}
+
+
+/*---------------------------------------------*/
+static void 
+unrandomiseBlock ( Lib_Bzip_Decode_Storage_Ty *dst )
+{
+   Int32 i;
+   RAND_DECLS;
+
+   for (i = 0; i < dst->nblock; i++) {
+      RAND_UPD_MASK;
+      dst->outbuff[i] ^= RAND_MASK;
+   }
+}
+
+/*---------------------------------------------*/
+size_t 
+bzip_uncompressBlock ( unsigned char *in_buf, 
+		       unsigned char *out_buf,
+		       void *heap,
+		       size_t num_in_buf, /* unused */
+		       size_t num_out_buf, /* unused */
+		       int param ) /* unused */
+{
+   Lib_Bzip_Decode_Storage_Ty *dst;
+
+   if (!try_module_get(THIS_MODULE))
+      return 0;
+
+#if 0
+   dst = (Lib_Bzip_Decode_Storage_Ty *) bzip2_work_area_D;
+#else
+   dst = (Lib_Bzip_Decode_Storage_Ty *) heap;
+#endif
+   dst->inbuff = in_buf;
+   dst->outbuff = out_buf;
+
+   bsInit_d (dst);
+
+   if (bsR ( dst, 8 ) != 0xa2) {
+      module_put(THIS_MODULE);
+      return 0;
+   }
+
+   dst->randomised = False;
+   if (bsR(dst, 1) == 1) dst->randomised = True;
+
+   decodeBlock ( dst,
+                 &(dst->cc_tmp[0]),
+		 &(dst->mtfa_tmp[0]),
+		 &(dst->mtfbase_tmp[0]) );
+
+   if (dst->randomised) unrandomiseBlock(dst);
+
+   module_put(THIS_MODULE);;
+   return dst->nblock;
+}
+
+
+/*-------------------------------------------------------------*/
+/*-- end                                        lib_bzip_d.c --*/
+/*-------------------------------------------------------------*/
--- linux-2.6.25/fs/ext2/bzip2/NOTES	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/bzip2/NOTES	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,17 @@
+
+bzip_compressBlock does check that the output will fit into
+the specified space, but only after creating about 1k of
+output first.  So e2compr must ensure that at least 1k
+of output space is available before calling bzip_compressBlock.
+
+bzip_decompressBlock *ignores* the output space indication
+passed by e2compr, and just assumes that N_BLOCK bytes
+of space are available to decompress into.  Does this matter?
+It also ignores the input buffer size indication, since 
+it the end-of-block is detected by interpreting the compressed
+bitstream.
+
+Compression of very repetitive blocks can be quite slow.  
+Blocks which compress too slowly are heavily randomised;
+this limits the max compression ratio to about 15:1.  Most
+blocks are unaffected, tho.
--- linux-2.6.25/fs/ext2/bzip2/lib_bzip.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/bzip2/lib_bzip.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,32 @@
+#ifdef __KERNEL__
+# include <linux/types.h>
+#else
+# include <stdio.h> /* size_t */
+#endif
+
+#define bzip_compressBlock ext2_wBZIP2
+#define bzip_uncompressBlock ext2_rBZIP2
+#define bzip_init ext2_iBZIP2
+
+extern
+size_t 
+bzip_compressBlock ( unsigned char *in_buf, 
+                     unsigned char *out_buf,
+		     void *heap,
+                     size_t num_in_buf,
+                     size_t num_out_buf,
+                     int param );
+
+extern
+size_t 
+bzip_uncompressBlock ( unsigned char *in_buf, 
+                       unsigned char *out_buf,
+		       void *heap,
+                       size_t num_in_buf,
+                       size_t num_out_buf,
+                       int param );
+
+
+extern
+size_t 
+bzip_init ( int action );
--- linux-2.6.25/fs/ext2/bzip2/Makefile.lib	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/bzip2/Makefile.lib	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,30 @@
+
+CC=gcc
+CFLAGS=-m486 -Wall -Winline -O2 -fomit-frame-pointer -funroll-loops
+
+#CC=lcc -Dinline=
+#CFLAGS= -A
+
+#CC=gcc -g
+#CFLAGS= -Wall -Wshadow
+
+OBJS= lib_bzip_huffman.o  \
+      lib_bzip_e.o        \
+      lib_bzip_d.o        \
+      init.o
+
+all: lib testdriver.o
+	$(CC) $(CFLAGS) -o testdriver testdriver.o -L. -lthelib
+
+clean: 
+	rm -f *.o libthelib.a testdriver
+
+lib: $(OBJS) lib_bzip.h lib_bzip_private.h
+	rm -f libthelib.a
+	ar clq libthelib.a $(OBJS)
+
+.c.o: $*.o lib_bzip.h lib_bzip_private.h
+	$(CC) $(CFLAGS) -c $*.c -o $*.o
+
+tarfile:
+	tar cvf lib_bzip-0.02.tar *.c *.h Makefile ChangeLog TODO NOTES
--- linux-2.6.25/fs/ext2/bzip2/ChangeLog	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/bzip2/ChangeLog	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,24 @@
+Sun Jan  4 19:58:56 1998  Me  <sewardj@muraroa>
+
+	* lib_bzip_e.c (sortBlock): Added SRC-124 Sec Q6a/Q6b
+	optimisation for strings of identical chars.  Makes a
+	big difference for files with sections of repeated chars.
+
+	* Made version 0.02
+
+Sat Jan  3 20:18:29 1998  Me  <sewardj@muraroa>
+
+	* lib_bzip_d.c: Added a couple of scheduling points
+
+	* lib_bzip_e.c (sortBucket): Changed stackL/R to UInt16.  Added
+	work-limit and scheduling checks to improve worst-case
+	responsiveness.
+	
+	(sendMTFValues): Changed N_GROUPS to 2, and got rid of
+	the selector MTF mechanism.  Fixed bug in prediction of compressed
+	data size.
+
+Mon Dec 29 13:57:51 1997  Me  <sewardj@muraroa>
+
+	* Created version 0.00
+
--- linux-2.6.25/fs/ext2/bzip2/lib_bzip_private.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/bzip2/lib_bzip_private.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,253 @@
+
+/*-------------------------------------------------------------*/
+/*-- private header for lib_bzip          lib_bzip_private.h --*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is part of lib_bzip, a block-sorting compression 
+  library designed to compress 32kbyte blocks, for on-the-fly
+  disk compression/decompression.
+
+  Version 0.02, 3-Jan-1998
+
+  Copyright (C) 1996, 1997, 1998 by Julian Seward.
+     Guildford, Surrey, UK
+     email: jseward@acm.org
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--*/
+
+
+#ifndef LIB_BZIP_PRIVATE_H
+#define LIB_BZIP_PRIVATE_H
+
+
+#ifndef __KERNEL__
+# include <stdlib.h>
+# include <stdio.h>
+/* unused: #include <assert.h> */
+#endif
+
+
+/*---------------------------------------------*/
+// useful types
+
+typedef char            Char;
+typedef unsigned char   Bool;
+typedef unsigned char   UChar;
+typedef int             Int32;
+typedef unsigned int    UInt32;
+/* UInt31 is used for things that can safely be cast to either UInt32
+   or Int32 */
+typedef unsigned int    UInt31;
+typedef short           Int16;
+typedef unsigned short  UInt16;
+
+#define True  ((Bool)1)
+#define False ((Bool)0)
+
+
+
+/*---------------------------------------------*/
+// global constants
+
+// max block size, 
+// and max pointer denormalisation when sorting
+#define N_BLOCK     32768
+#define N_OVERSHOOT (1U+12U)
+
+// parameters for run-len coding of zeroes
+#define RUNBASE 2
+#define RUNA 0
+#define RUNB 1
+
+// parameters for the back end
+#define MAX_ALPHA_SIZE (256 + RUNBASE)
+#define MAX_SELECTORS (2 + (N_BLOCK / G_SIZE))
+#define MAX_CODE_LEN 23
+
+// Don\'t change N_GROUPS; doing so will make the
+// back end totally not work (not even slightly!)
+// sendMTFValues() and the compressed file format
+// now assume that N_GROUPS == 2.
+#define N_GROUPS 2
+#define G_SIZE   50
+#define N_ITERS  4
+
+
+
+/*---------------------------------------------*/
+// a type containing all storage needed for compression
+typedef
+   struct {
+      // verbosity of debugging output
+      Int32 verb;
+
+      // block size, block, pointers to block
+      unsigned nblock;
+      UInt16 block[N_BLOCK + N_OVERSHOOT];
+      UInt16 zptr [N_BLOCK];
+
+      // freq table and running order, both for sorting
+      Int32  ftab  [769];
+      Int32  rorder[256];
+
+      // regarding block randomisation during sorting
+      Bool  randomised;
+      Int32 workDone;
+      Int32 workLimit;
+
+      // a record of which chars are used in block,
+      // and a mapping into contiguous values 0 .. nInUse-1
+      Bool  inUse[256];
+      Int32 nInUse;
+      UChar unseqToSeq[256];
+
+      // to do with MTF values
+      Int32 mtfFreq[MAX_ALPHA_SIZE];
+      Int32 nMTF;
+
+      // to do with calculating Huffman coding tables
+      UChar  len  [N_GROUPS][MAX_ALPHA_SIZE];
+      UInt32 code [N_GROUPS][MAX_ALPHA_SIZE];
+      Int32  rfreq[N_GROUPS][MAX_ALPHA_SIZE];
+      UChar  selector   [MAX_SELECTORS];
+
+      // temporaries used only for calculating
+      // Huffman code lengths
+      Int32 heap_tmp   [ MAX_ALPHA_SIZE + 2 ];
+      Int32 weight_tmp [ MAX_ALPHA_SIZE * 2 ];
+      Int32 parent_tmp [ MAX_ALPHA_SIZE * 2 ]; 
+
+      // bit stream buffering
+      UInt32 bsBuff;
+      Int32  bsLive;
+      Int32  outctr;
+      UChar* outbuff;
+      Int32  totalSizeAllowed;
+      Int32  predictedSize;  // only for debugging
+   }
+   Lib_Bzip_Encode_Storage_Ty;
+
+
+/*---------------------------------------------*/
+// constants for the fast MTF decoder
+#define MTFA_SIZE 4096
+#define MTFL_SIZE 16
+
+
+// a type containing all storage needed for decompression
+typedef
+   struct {
+      Bool   inUse[256];
+      UInt16 origPtr;
+      Int32  nInUse;
+      UChar  seqToUnseq[256];
+      Bool   randomised;
+
+      // temporaries used only for MTF decoding
+      UInt32 cc_tmp      [256];
+      UChar  mtfa_tmp    [MTFA_SIZE];
+      Int32  mtfbase_tmp [256 / MTFL_SIZE];
+
+      // the selector tables
+      UChar  selector   [MAX_SELECTORS];
+ 
+      // huffman decoding tables
+      UChar len    [N_GROUPS][MAX_ALPHA_SIZE];
+      Int32 limit  [N_GROUPS][MAX_ALPHA_SIZE];
+      Int32 base   [N_GROUPS][MAX_ALPHA_SIZE];
+      Int32 perm   [N_GROUPS][MAX_ALPHA_SIZE];
+      Int32 minLens[N_GROUPS];
+
+      // for the inverse BWT
+      UInt32 tvec[N_BLOCK];
+
+      // bit stream buffering
+      UInt32 bsBuff;
+      Int32  bsLive;
+      Int32  inctr;
+
+      // buffers, and size of uncompressed block
+      UChar* inbuff;
+      UChar* outbuff;
+      Int32  nblock;
+   }
+   Lib_Bzip_Decode_Storage_Ty;
+
+
+/*---------------------------------------------*/
+// Huffman coding helper functions
+
+extern 
+void 
+hbMakeCodeLengths ( UChar* len, 
+                    Int32* freq,
+                    Int32  alphaSize,
+                    Int32  maxLen,
+                    Int32* heap,
+                    Int32* weight,
+                    Int32* parent );
+
+extern 
+void 
+hbAssignCodes ( UInt32 *code,
+                UChar *length,
+                unsigned minLen,
+                unsigned maxLen,
+                Int32 alphaSize );
+
+extern 
+void 
+hbCreateDecodeTables ( Int32 *limit,
+                       Int32 *base,
+                       Int32 *perm,
+                       UChar *length,
+                       Int32 minLen,
+                       Int32 maxLen,
+                       Int32 alphaSize );
+
+
+/*---------------------------------------------*/
+// stuff for randomising blocks
+
+// numbers themselves are in lib_bzip_e.c
+#define rNums bzip2_rNums /* don't pollute name space */
+extern Int16 const rNums[64];
+
+#define RAND_DECLS                                \
+   Int16 rNToGo = 0;                              \
+   Int16 rTPos  = 0;                              \
+   Int16 rMask  = 1;
+
+#define RAND_MASK ((rNToGo == 1) ? rMask : 0)
+
+#define RAND_UPD_MASK                             \
+   if (rNToGo == 0) {                             \
+      rNToGo = rNums[rTPos] / 10;                 \
+      rTPos++; if (rTPos == 64) rTPos = 0;        \
+      rMask++; if (rMask == 256) rMask = 1;       \
+   }                                              \
+   rNToGo--;
+
+#if 0
+extern void *bzip2_work_area;
+#endif
+
+#endif
+
+/*-------------------------------------------------------------*/
+/*-- end                                  lib_bzip_private.h --*/
+/*-------------------------------------------------------------*/
--- linux-2.6.25/fs/ext2/balloc.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/ext2/balloc.c	2008-07-31 15:13:12.000000000 +0200
@@ -11,8 +11,17 @@
  *        David S. Miller (davem@caip.rutgers.edu), 1995
  */
 
-#include "ext2.h"
+/*
+ *  Copyright (C) 2001 Alcatel Business Systems - R&D Illkirch
+ *    (transparent compression code)
+ *  Pierre Peiffer (pierre.peiffer@sxb.bsf.alcatel.fr) - Denis Richard (denis.
+richard@sxb.bsf.alcatel.fr)
+ *  Adapted from patch e2compr-0.4.39-patch-2.2.18 .
+ */
+
 #include <linux/quotaops.h>
+#include <linux/ext2_fs_c.h>
+#include "debug.h"
 #include <linux/sched.h>
 #include <linux/buffer_head.h>
 #include <linux/capability.h>
@@ -498,6 +507,13 @@ void ext2_free_blocks (struct inode * in
 	struct ext2_super_block * es = sbi->s_es;
 	unsigned freed = 0, group_freed;
 
+
+#ifdef CONFIG_EXT2_COMPRESS
+	assert((block != EXT2_COMPRESSED_BLKADDR)
+		|| !S_ISREG(inode->i_mode)
+		|| !(EXT2_SB(sb)->s_es->s_feature_incompat
+			& cpu_to_le32(EXT2_FEATURE_INCOMPAT_COMPRESSION)));
+#endif
 	if (block < le32_to_cpu(es->s_first_data_block) ||
 	    block + count < block ||
 	    block + count > le32_to_cpu(es->s_blocks_count)) {
@@ -523,11 +539,11 @@ do_more:
 		overflow = bit + count - EXT2_BLOCKS_PER_GROUP(sb);
 		count -= overflow;
 	}
+
 	brelse(bitmap_bh);
-	bitmap_bh = read_block_bitmap(sb, block_group);
+	bitmap_bh = read_block_bitmap(sb, block_group);		//mw: causes pg_count == 4
 	if (!bitmap_bh)
 		goto error_return;
-
 	desc = ext2_get_group_desc (sb, block_group, &bh2);
 	if (!desc)
 		goto error_return;
@@ -546,10 +562,18 @@ do_more:
 	}
 
 	for (i = 0, group_freed = 0; i < count; i++) {
+		//printk("ext2_free_blocks: inode: %lu, block: %lu, mutex:%i\ n", inode->i_ino, block+1, atomic_read(&inode->i_mutex.count) );
+		//dump_stack(); //mw
+		//if (check_name2(inode))   //DEBUG ONLY
+		//{	
+		//	;//printk("ext2_free_block: %lu, block: %lu\n", inode->i_ino, block+1);
+		//	//dump_stack();
+		//}
 		if (!ext2_clear_bit_atomic(sb_bgl_lock(sbi, block_group),
 						bit + i, bitmap_bh->b_data)) {
 			ext2_error(sb, __FUNCTION__,
-				"bit already cleared for block %lu", block + i);
+				"bit already cleared for block %lu (see above), inode: %lu", block + i, inode->i_ino);
+				//dump_stack(); //mw								
 		} else {
 			group_freed++;
 		}
--- linux-2.6.25/fs/ext2/e2zlib.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/e2zlib.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,107 @@
+
+/*****
+
+   New gzip Module, which uses zlib functions for compression
+
+   (c) UniCon Software GmbH
+
+*****/
+
+// Andreas 20.4.2007
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs_c.h>
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/zlib.h>
+#include <linux/vmalloc.h>
+
+static struct crypto_comp *tfm;
+
+
+size_t ext2_iZLIB (int action)
+{
+        printk(KERN_DEBUG "ext2_iZLIB: action = %s\n", action == EXT2_ALG_INIT_COMPRESS ? "EXT2_ALG_INIT_COMPRESS" : "EXT2_ALG_INIT_DECOMPRESS");
+        switch (action) {
+                case EXT2_ALG_INIT_COMPRESS:
+                    printk(KERN_DEBUG "ext2_iZLIB: loading crypto module deflate\n");
+		    //tfm = __crypto_alloc_tfm("deflate", 0); //mw
+			tfm = crypto_alloc_comp("deflate", 0, CRYPTO_ALG_ASYNC);
+		    if (tfm == NULL) {
+			printk(KERN_DEBUG "ext2_iZLIB: failed to load transform for deflate\n");
+			return zlib_deflate_workspacesize();
+		    }
+		    return 0;
+                case EXT2_ALG_INIT_DECOMPRESS:
+			return zlib_deflate_workspacesize();
+                default:
+                        return 0;
+        }
+}
+
+
+size_t ext2_wZLIB (__u8 *ibuf, __u8 *obuf, void *heap,
+                size_t ilen, size_t olen, int level)
+{
+    int ret, dlen;
+
+    //printk(KERN_DEBUG "ext2_wZLIB ilen %d olen %d\n", ilen, olen);
+    if (!try_module_get(THIS_MODULE))
+        return 0;
+    
+    dlen = olen;
+
+    ret = crypto_comp_compress(tfm, ibuf, ilen, obuf, &dlen);
+
+    if ( ret == -EAGAIN) {
+         __u8 *tmp_obuf = NULL;
+         dlen = ilen + (ilen / 6) + 12 + 1024;
+         //printk("recall crypto_comp_compress new o_length %d (as buffer) \n", dlen);
+
+         tmp_obuf = vmalloc(dlen);
+         if (tmp_obuf == NULL) printk("tmp_obuff == NULL !!!\n");
+
+         // New Handle
+         crypto_free_comp(tfm);
+
+         //tfm = __crypto_alloc_tfm("deflate", 0); //mw
+		 tfm = crypto_alloc_comp("deflate", 0, CRYPTO_ALG_ASYNC);
+
+         ret = crypto_comp_compress(tfm, ibuf, ilen, tmp_obuf, &dlen);
+         //printk("ret from recall %d dlen %d (result) slen %d (source) olen %d (requested)\n", ret, dlen, ilen, olen);
+         return 0;
+    }   
+
+    if (ret) {
+       //printk(KERN_DEBUG "ext2_wZLIB: crypto_comp_compress faild %d ilen %d olen %d\n", ret, ilen, olen);
+       return 0;
+    }
+    //printk(KERN_DEBUG "ext2_wZLIB: ratio %d->%d\n", ilen, dlen);
+    
+    return dlen;
+}
+
+
+size_t ext2_rZLIB (__u8 *ibuf, __u8 *obuf, void *heap,
+                size_t ilen, size_t olen, int ignored)
+{
+    int ret, dlen;
+
+
+    if (!try_module_get(THIS_MODULE))
+        return 0;
+
+    dlen = olen;
+    ret = crypto_comp_decompress(tfm, ibuf, ilen, obuf, &dlen );
+
+    if (ret) {
+       //printk(KERN_DEBUG "ext2_wZLIB: crypto_comp_decompress faild %d\n", ret);
+       return 0;
+    }
+    //printk("ext2_rZLIB: ratio %d->%d\n", ilen, dlen);
+
+    return dlen;
+}
+
+
--- linux-2.6.25/fs/ext2/lzrw3a/e2compr-lzrw3a.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzrw3a/e2compr-lzrw3a.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,81 @@
+#include <linux/fs.h>
+#include <linux/ext2_fs_c.h>
+#include <linux/types.h>
+#include <linux/module.h>
+
+#include "lzrw.h"
+
+#ifdef MODULE
+MODULE_AUTHOR("Ross Williams and Antoine de Maricourt");
+MODULE_DESCRIPTION("Lev-Zimpel Ross Williams 3A algorithm for EXT2 file compression");
+MODULE_LICENSE("GPL");
+#endif
+
+#define U(X)	((unsigned long) X)
+#define MEM_REQ	(U(4096)*(U(sizeof(unsigned char *))) + (U(16)))
+
+extern int ext2_LZRW3A_compress(UBYTE *p_src_first, UBYTE *p_dst_first,
+				UBYTE *heap, int  src_len, int p_dst_len);
+extern int ext2_LZRW3A_decompress(UBYTE *p_src_first, UBYTE *p_dst,
+				UBYTE *heap, int src_len, int dst_len);
+
+size_t ext2_iLZRW3A(int action)
+{
+	switch (action) {
+		case EXT2_ALG_INIT_COMPRESS:
+			return MEM_REQ;
+		case EXT2_ALG_INIT_DECOMPRESS:
+			return MEM_REQ;
+		default:
+			return 0;
+	}
+}
+
+size_t ext2_wLZRW3A (unsigned char *p_src_first, unsigned char *p_dst_first,
+		void *heap, size_t src_len, size_t p_dst_len, int xarg)
+{
+	int ret_code;
+
+	if (!try_module_get(THIS_MODULE))
+		return 0;
+	ret_code = ext2_LZRW3A_compress(p_src_first, p_dst_first, heap, src_len, p_dst_len);
+	module_put(THIS_MODULE);
+	return ret_code;
+}
+
+size_t ext2_rLZRW3A (unsigned char *p_src_first, unsigned char *p_dst,
+		void *heap, size_t src_len, size_t dst_len, int xarg)
+{
+	int ret_code;
+
+	if (!try_module_get(THIS_MODULE))
+		return 0;
+	ret_code = ext2_LZRW3A_decompress(p_src_first, p_dst, heap, src_len, dst_len);
+	module_put(THIS_MODULE);
+	return ret_code;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+        struct ext2_algorithm lzrw3a_alg;
+
+        lzrw3a_alg.name = NULL;
+        lzrw3a_alg.avail = 1;
+        lzrw3a_alg.init = ext2_iLZRW3A;
+        lzrw3a_alg.compress = ext2_wLZRW3A;
+        lzrw3a_alg.decompress = ext2_rLZRW3A;
+
+        return ext2_register_compression_module(EXT2_LZRW3A_ALG,
+                        MEM_REQ, MEM_REQ,
+                        &lzrw3a_alg);
+}
+
+void cleanup_module(void)
+{
+        ext2_unregister_compression_module(EXT2_LZRW3A_ALG);
+}
+
+#endif
+
--- linux-2.6.25/fs/ext2/lzrw3a/lzrw3a.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzrw3a/lzrw3a.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,795 @@
+/*****************************************************************************/
+/*                                                                           */
+/*                                  LZRW3-A.C                                */
+/*                                                                           */
+/*****************************************************************************/
+/*                                                                           */
+/* Author  : Ross Williams.                                                  */
+/* Date    : 15-Jul-1991.                                                    */
+/* Release : 1.                                                              */
+/*                                                                           */
+/*****************************************************************************/
+/*                                                                           */
+/* This file contains an implementation of the LZRW3-A data compression      */
+/* algorithm in the C programming language.                                  */
+/*                                                                           */
+/* The LZRW3-A algorithm has the following features:                         */
+/*                                                                           */
+/*    1 Requires only 16K of memory (for both compression and decompression).*/
+/*    2 The compressor   runs about two   times faster than Unix compress's. */
+/*    3 The decompressor runs about three times faster than Unix compress's. */
+/*    4 Yields a few percent better compression than Unix compress for       */
+/*      most files.                                                          */
+/*    5 Allows you to dial up extra compression at a speed cost in the       */
+/*      compressor. The speed of the decompressor is not affected.           */
+/*    6 Algorithm is deterministic.                                          */
+/*    8 This implementation in C is in the public domain.                    */
+/*                                                                           */
+/* (Timing tests for the speed comparison were performed on a Pyramid 9820.) */
+/*                                                                           */
+/* LZRW3-A is LZRW3 with a deepened hash table. This simple change yields    */
+/* about a 6% (absolute) improvement in compression.                         */
+/*                                                                           */
+/* Here are the results of applying this code, compiled under THINK C 4.0    */
+/* and running on a Mac-SE (8MHz 68000), to the standard calgary corpus.     */
+/*                                                                           */
+/*     +----------------------------------------------------------------+    */
+/*     | DATA COMPRESSION TEST                                          |    */
+/*     | =====================                                          |    */
+/*     | Time of run     : Mon 15-Jul-1991 05:29PM                      |    */
+/*     | Timing accuracy : One part in 100                              |    */
+/*     | Context length  : 262144 bytes (= 256.0000K)                   |    */
+/*     | Test suite      : Calgary Corpus Suite                         |    */
+/*     | Files in suite  : 14                                           |    */
+/*     | Algorithm       : LZRW3-A                                      |    */
+/*     | Note: All averages are calculated from the un-rounded values.  |    */
+/*     +----------------------------------------------------------------+    */
+/*     | File Name   Length  CxB  ComLen  %Remn  Bits  Com K/s  Dec K/s |    */
+/*     | ----------  ------  ---  ------  -----  ----  -------  ------- |    */
+/*     | rpus:Bib.D  111261    1   49044   44.1  3.53     8.47    31.19 |    */
+/*     | us:Book1.D  768771    3  420464   54.7  4.38     7.27    30.07 |    */
+/*     | us:Book2.D  610856    3  277955   45.5  3.64     8.51    33.40 |    */
+/*     | rpus:Geo.D  102400    1   84218   82.2  6.58     4.23    15.04 |    */
+/*     | pus:News.D  377109    2  192880   51.1  4.09     7.08    25.89 |    */
+/*     | pus:Obj1.D   21504    1   12651   58.8  4.71     5.23    17.44 |    */
+/*     | pus:Obj2.D  246814    1  108044   43.8  3.50     8.01    28.11 |    */
+/*     | s:Paper1.D   53161    1   24526   46.1  3.69     8.11    30.24 |    */
+/*     | s:Paper2.D   82199    1   39483   48.0  3.84     8.11    32.04 |    */
+/*     | rpus:Pic.D  513216    2  111622   21.7  1.74    10.64    49.31 |    */
+/*     | us:Progc.D   39611    1   17923   45.2  3.62     8.06    29.01 |    */
+/*     | us:Progl.D   71646    1   24362   34.0  2.72    10.74    39.51 |    */
+/*     | us:Progp.D   49379    1   16805   34.0  2.72    10.64    37.58 |    */
+/*     | us:Trans.D   93695    1   30296   32.3  2.59    11.02    38.06 |    */
+/*     +----------------------------------------------------------------+    */
+/*     | Average     224401    1  100733   45.8  3.67     8.29    31.21 |    */
+/*     +----------------------------------------------------------------+    */
+/*                                                                           */
+/*****************************************************************************/
+
+#include "lzrw.h"
+/******************************************************************************/
+
+/*
+  The following structure is returned by the "compress" function below when 
+  the user asks the function to return identifying information.             
+  The most important field in the record is the working memory field which  
+  tells the calling program how much working memory should be passed to     
+  "compress" when it is called to perform a compression or decompression.   
+  LZRW3-A uses the same amount of memory during compression and             
+  decompression. For more information on this structure see "lzrw.h".       
+  The alignment fudge below really only needs to be 4 (but I play it safe!).
+  The id looks non-random, but it really was generated by coin tossing!     
+  */
+
+#define U(X)            ((ULONG) X)
+#define SIZE_P_BYTE     (U(sizeof(UBYTE *)))
+#define ALIGNMENT_FUDGE (U(16))
+#define MEM_REQ ( U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE )
+
+/*
+   static struct compress_identity identity =
+   {
+     U(0x01B90B91),                         * Algorithm identification number. * 
+     MEM_REQ,                               * Working memory (bytes) required. * 
+     "LZRW3-A",                             * Name of algorithm.               * 
+     "1.0",                                 * Version number of algorithm.     * 
+     "15-Jul-1990",                         * Date of algorithm.               * 
+     "Ross N. Williams",                    * Author of algorithm.             * 
+     "Renaissance Software",                * Affiliation of author.           * 
+   };
+*/
+
+/******************************************************************************/
+/*                                                                            */
+/* BRIEF DESCRIPTION OF THE LZRW3-A ALGORITHM                                 */
+/* ==========================================                                 */
+/* Note: Before attempting to understand this algorithm, you should first     */
+/* understand the LZRW3 algorithm from which this algorithm is derived.       */
+/*                                                                            */
+/* The LZRW3-A algorithm is identical to the LZRW3 algorithm except that the  */
+/* hash table has been "deepened". The LZRW3 algorithm has a hash table of    */
+/* 4096 pointers which point to strings in the buffer. LZRW3-A generalizes    */
+/* this to 4096/(2^n) partitions each of which contains (2^n) pointers.       */
+/* In LZRW3-A, the hash function hashes to a partition number.                */
+/*                                                                            */
+/* During the processing of each phrase, LZRW3 overwrites the pointer in the  */
+/* position selected by the hash function. LZRW3-A overwrites one of the      */
+/* pointers in the partition that was selected by the hash function.          */
+/*                                                                            */
+/* When searching for a match, LZRW3-A matches against all (2^n) strings      */
+/* pointed to by the pointers in the target partition.                        */
+/*                                                                            */
+/* Deep hash tables were used in early versions of LZRW1 in late 1989, but    */
+/* were discarded in an effort to increase speed (which was the primary       */
+/* requirement for LZRW1). They were revived for use in LZRW3-A in order to   */
+/* produce an algorithm with compression performance competitive with Unix    */
+/* compress.                                                                  */
+/*                                                                            */
+/* Until 14-Jul-1991, deep hash tables used in prototype LZRW* algorithms     */
+/* used a queue discipline within each partition. Upon the arrival of a new   */
+/* pointer, the pointers in the partition would be block copied back one      */
+/* position (with the oldest pointer being overwritten) and the new pointer   */
+/* being inserted in the space at the front (the youngest position).          */
+/* This meant that pointers to the (2^n) most recent phrases corresponding to */
+/* each hash was kept. The only flaw in this system was the time-consuming    */
+/* block copy operation which was cheap for shallow tables but expensive for  */
+/* deep tables.                                                               */
+/*                                                                            */
+/* The traditional solution to ring buffer block copy problems is to maintain */
+/* a cyclic counter which points to the "head" of the queue. However, this    */
+/* would have required one counter to be stored for each partition and would  */
+/* have been slightly messy. After some thought (on 14-Jul-1991) a better     */
+/* solution was found. Instead of maintaining a counter for each partition,   */
+/* LZRW3-A maintains a single counter for all partitions! This counter is     */
+/* maintained in both the compressor and decompressor and means that the      */
+/* algorithm (effectively) overwrites a RANDOM element of the partition to be */
+/* updated. The result was to increase the speed of the compressor and        */
+/* decompressor, to make the decompressor's speed independent from whatever   */
+/* depth was selected, and to impair compression by less than 1% absolute.    */
+/*                                                                            */
+/* Setting the depth is a speed/compression tradeoff. The table below gives   */
+/* the tradeoff observed for a typical 50K text file on a Mac-SE.             */
+/* Note: %Rem=Percentage Remaining (after compression).                       */
+/*                                                                            */
+/*      Depth    %Rem    CmpK/s  DecK/s                                       */
+/*          1    45.2    14.77   32.24                                        */
+/*          2    42.6    12.12   31.26                                        */
+/*          4    40.9    10.28   31.91                                        */
+/*          8    40.0     7.81   32.36                                        */
+/*         16    39.5     5.30   32.47                                        */
+/*         32    39.0     3.23   32.59                                        */
+/*                                                                            */
+/* I have chosen a depth of 8 as the "default" depth for LZRW3-A. If you use  */
+/* a depth different to this (e.g. 4), you should use the name LZRW3-A(4) to  */
+/* indicate that a different depth is being used. LZRW3-A(8) is an acceptable */
+/* longhand for LZRW3-A.                                                      */
+/*                                                                            */
+/* To change the depth, search for "HERE IT IS" in the rest of this file.     */
+/*                                                                            */
+/*                                  +---+                                     */
+/*                                  |___|4095                                 */
+/*                                  |===|                                     */
+/*              +---------------------*_|<---+   /----+---\                   */
+/*              |                   |___|    +---|Hash    |                   */
+/*              |    512 partitions |___|        |Function|                   */
+/*              |    of 8 pointers  |===|        \--------/                   */
+/*              |    each (or any   |___|0            ^                       */
+/*              |    a*b=4096)      +---+             |                       */
+/*              |                   Hash        +-----+                       */
+/*              |                   Table       |                             */
+/*              |                              ---                            */
+/*              v                              ^^^                            */
+/*      +-------------------------------------|----------------+              */
+/*      ||||||||||||||||||||||||||||||||||||||||||||||||||||||||              */
+/*      +-------------------------------------|----------------+              */
+/*      |                                     |1......18|      |              */
+/*      |<------- Lempel=History ------------>|<--Ziv-->|      |              */
+/*      |     (=bytes already processed)      |<-Still to go-->|              */
+/*      |<-------------------- INPUT BLOCK ------------------->|              */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/*                     DEFINITION OF COMPRESSED FILE FORMAT                   */
+/*                     ====================================                   */
+/*    The file consists of zero  */
+/*    or more GROUPS, each of which represents one or more bytes.             */
+/*  * Each group consists of two bytes of CONTROL information followed by     */
+/*    sixteen ITEMs except for the last group which can contain from one      */
+/*    to sixteen items.                                                       */
+/*  * An item can be either a LITERAL item or a COPY item.                    */
+/*  * Each item corresponds to a bit in the control bytes.                    */
+/*  * The first control byte corresponds to the first 8 items in the group    */
+/*    with bit 0 corresponding to the first item in the group and bit 7 to    */
+/*    the eighth item in the group.                                           */
+/*  * The second control byte corresponds to the second 8 items in the group  */
+/*    with bit 0 corresponding to the ninth item in the group and bit 7 to    */
+/*    the sixteenth item in the group.                                        */
+/*  * A zero bit in a control word means that the corresponding item is a     */
+/*    literal item. A one bit corresponds to a copy item.                     */
+/*  * A literal item consists of a single byte which represents itself.       */
+/*  * A copy item consists of two bytes that represent from 3 to 18 bytes.    */
+/*  * The first  byte in a copy item will be denoted C1.                      */
+/*  * The second byte in a copy item will be denoted C2.                      */
+/*  * Bits will be selected using square brackets.                            */
+/*    For example: C1[0..3] is the low nibble of the first control byte.      */
+/*    of copy item C1.                                                        */
+/*  * The LENGTH of a copy item is defined to be C1[0..3]+3 which is a number */
+/*    in the range [3,18].                                                    */
+/*  * The INDEX of a copy item is defined to be C1[4..7]*256+C2[0..8] which   */
+/*    is a number in the range [0,4095].                                      */
+/*  * A copy item represents the sequence of bytes                            */
+/*       text[POS-OFFSET..POS-OFFSET+LENGTH-1] where                          */
+/*          text   is the entire text of the uncompressed string.             */
+/*          POS    is the index in the text of the character following the    */
+/*                   string represented by all the items preceeding the item  */
+/*                   being defined.                                           */
+/*          OFFSET is obtained from INDEX by looking up the hash table.       */
+/*                                                                            */
+/******************************************************************************/
+
+/* When I first started to get concerned about the portability of my C code,  */
+/* I switched over to using only macro defined types UBYTE, UWORD, ULONG and  */
+/* one or two others. While, these are useful for most purposes, they impair  */
+/* efficiency as, if I have a variable whose range will be [0,1000], I will   */
+/* declare it as a UWORD. This will translate into (say) "short int" and      */
+/* hence may be less efficient than just an "int" which represents the        */
+/* natural size of the machine. Before releasing LZRW3-A, I realized this     */
+/* mistake. Unfortunately, I can't access the ftp archive with my portability */
+/* header in it in time for this algorithm's release and so I am including an */
+/* extra definition. The definition UCARD stands for an unsigned (cardinal)   */
+/* type that can hold values in the range [0,32767]. This is within the ANSI  */
+/* range of a standard int or unsigned. No assumption about overflow of this  */
+/* type is made in the code (i.e. all usages are within range and I do not    */
+/* use the value -1 to detect the end of loops.).                             */
+/* You can use either "unsigned" or just "int" here depending on which is     */
+/* more efficient in your environment (both the same probably).               */
+#define UCARD unsigned
+
+/* The following constant defines the maximum length of an uncompressed item. */
+/* This definition must not be changed; its value is hardwired into the code. */
+/* The longest number of bytes that can be spanned by a single item is 18     */
+/* for the longest copy item.                                                 */
+#define MAX_RAW_ITEM (18)
+
+/* The following constant defines the maximum length of a compressed group.   */
+/* This definition must not be changed; its value is hardwired into the code. */
+/* A compressed group consists of two control bytes followed by up to 16      */
+/* compressed items each of which can have a maximum length of two bytes.     */
+#define MAX_CMP_GROUP (2+16*2)
+
+/* This constant defines the number of pointers in the hash table. The number */
+/* of partitions multiplied by the number of pointers in each partition must  */
+/* multiply out to this value of 4096. In LZRW1, LZRW1-A, and LZRW2, this     */
+/* table length value can be changed. However, in LZRW3-A (and LZRW3), the    */
+/* table length cannot be changed because it is connected directly to the     */
+/* coding scheme which is hardwired (the table index of a single pointer is   */
+/* transmitted in the 12-bit index field). So don't change this constant!     */
+#define HASH_TABLE_LENGTH (4096)
+
+/* HERE IT IS: THE PLACE TO CHANGE THE HASH TABLE DEPTH!                      */
+/* The following definition is the log_2 of the depth of the hash table. This */
+/* constant can be in the range [0,1,2,3,...,12]. Increasing the depth        */
+/* increases compression at the expense of speed. However, you are not likely */
+/* to see much of a compression improvement (e.g. not more than 0.5%) above a */
+/* value of 6 and the algorithm will start to get very slow. See the table in */
+/* the earlier comments block for an idea of the trade-off involved.          */
+/* Note: The parentheses are to avoid macro substitution funnies.             */
+/* Note: The LZRW3-A default is a value of (3).                               */
+/* Note: If you end up choosing a value of 0, you should use LZRW3 instead.   */
+/* Note: Changing the value of HASH_TABLE_DEPTH_BITS is the ONLY thing you    */
+/* have to do to change the depth, so go ahead and recompile now!             */
+/* Note: I have tested LZRW3-A for DEPTH_BITS=0,1,2,3,4 and a few other       */
+/* values. However, I have not tested it for 12 as I can't wait that long!    */
+#define HASH_TABLE_DEPTH_BITS (3)      /* Must be in range [0,12].            */
+
+/* The following definitions are all self-explanatory and follow from the     */
+/* definition of HASH_TABLE_DEPTH_BITS and the hardwired requirement that the */
+/* hash table contain exactly 4096 pointers.                                  */
+#define PARTITION_LENGTH_BITS (12-HASH_TABLE_DEPTH_BITS)
+#define PARTITION_LENGTH      (1<<PARTITION_LENGTH_BITS)
+#define HASH_TABLE_DEPTH      (1<<HASH_TABLE_DEPTH_BITS )
+#define HASH_MASK             (PARTITION_LENGTH-1)
+#define DEPTH_MASK            (HASH_TABLE_DEPTH-1)
+
+/* LZRW3-A, unlike LZRW1(-A), must initialize its hash table so as to enable  */
+/* the compressor and decompressor to stay in step maintaining identical hash */
+/* tables. In an early version of LZRW3, the tables were simply               */
+/* initialized to zero and a check for zero was included just before the      */
+/* matching code. However, this test costs time. A better solution is to      */
+/* initialize all the entries in the hash table to point to a constant        */
+/* string. The decompressor does the same. This solution requires no extra    */
+/* test. The contents of the string do not matter so long as the string is    */
+/* the same for the compressor and decompressor and contains at least         */
+/* MAX_RAW_ITEM bytes. I chose consecutive decimal digits because they do not */
+/* have white space problems (e.g. there is no chance that the compiler will  */
+/* replace more than one space by a TAB) and because they make the length of  */
+/* the string obvious by inspection.                                          */
+#define START_STRING_18 ((UBYTE *) "123456789012345678")
+/* fixme: I (pjm) am not at all sure that the above string cannot be written to. */
+
+/* The following macro accepts a pointer PTR to three consecutive bytes in    */
+/* memory and hashes them into an integer that is a hash table index that     */
+/* points to the zeroth (first) element of a partition. Thus, the hash        */
+/* function really hashes to a partition number but, for convenience,         */
+/* multiplies it up to yield a hash table index. From all this, we see that   */
+/* the resultant number is in the range [0,HASH_TABLE_LENGTH-1] and is a      */
+/* multiple of HASH_TABLE_DEPTH.                                              */
+/* A macro is used, because in LZRW3-A we have to hash more than once.        */
+#if 0
+#define HASH(PTR) \
+ ( \
+     (((40543*(((*(PTR))<<8)^((*((PTR)+1))<<4)^(*((PTR)+2))))>>4) & HASH_MASK) \
+  << HASH_TABLE_DEPTH_BITS \
+ )
+
+#else
+#define HASH(PTR) \
+ ( \
+     ( ((((*(PTR)<<3)^(*((PTR)+1)))<<3)^(*((PTR)+2))) & HASH_MASK) \
+  << HASH_TABLE_DEPTH_BITS \
+ )
+#endif
+
+/* Another operation that is performed more than once is the updating of the  */
+/* hash table. Here two macros are defined to simplify update operations.     */
+/* Updating consists of identifying and overwriting a pointer in a partition  */
+/* with a newer pointer and then updating the global cycle value.             */
+/* These macros accept the new pointer (NEWPTR) and either a pointer to       */
+/* (P_BASE) or the index of (I_BASE) the zeroth (first, or base) pointer in   */
+/* the partition that is to be updated. The macros use the 'cycle' variable   */
+/* to locate and overwrite a pointer and then update the cycle value.         */
+/* Note: Hardcoding 'cycle' in this macro is naughty (it should really be a   */
+/* macro parameter), but I have done so because it neatens up the code.       */
+#define UPDATE_P(P_BASE,NEWPTR) \
+{(P_BASE)[cycle++]=(NEWPTR); cycle&=DEPTH_MASK;}
+
+#define UPDATE_I(I_BASE,NEWPTR) \
+{hash[(I_BASE)+cycle++]=(NEWPTR); cycle&=DEPTH_MASK;}
+
+/* This constant supplies a legal (in-range) hash table index for use when    */
+/* a legal-but-don't-care index is required.                                  */
+#define ANY_HASH_INDEX (0)
+
+/******************************************************************************/
+#if 0
+void *ext2_lzrw3a_htab;
+#endif
+
+EXPORT int ext2_LZRW3A_compress
+           (UBYTE *p_src_first, UBYTE *p_dst_first, void *heap, int src_len,
+	    int p_dst_len)
+/* Input  : Specify input block using p_src_first and src_len.                */
+/* Input  : Point p_dst_first to the start of the output zone (OZ).           */
+/* Input  : Point p_dst_len to a ULONG to receive the output length.          */
+/* Input  : Input block and output zone must not overlap.                     */
+/* Output : Length of output block written to *p_dst_len.                     */
+/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. May   */
+/* Output : write in OZ=Mem[p_dst_first..p_dst_first+src_len+MAX_CMP_GROUP-1].*/
+/* Output : Upon completion *p_dst_len contains final size or 0               */
+{
+  /* p_src and p_dst step through the source and destination blocks.           */
+  UBYTE const *p_src = p_src_first;
+  UBYTE *p_dst = p_dst_first;
+
+  /* The following variables are never modified and are used in the            */
+  /* calculations that determine when the main loop terminates.                */
+  UBYTE const * const p_src_post  = p_src_first + src_len;
+  UBYTE       * const p_dst_post  = p_dst_first + p_dst_len;
+  UBYTE const * const p_src_max1  = p_src_first + src_len - MAX_RAW_ITEM;
+  UBYTE const * const p_src_max16 = p_src_first + src_len - (MAX_RAW_ITEM * 16);
+
+  /* The variables 'p_control' and 'control' are used to buffer control bits.  */
+  /* Before each group is processed, the next two bytes of the output block    */
+  /* are set aside for the control word for the group about to be processed.   */
+  /* 'p_control' is set to point to the first byte of that word. Meanwhile,    */
+  /* 'control' buffers the control bits being generated during the processing  */
+  /* of the group. Instead of having a counter to keep track of how many items */
+  /* have been processed (=the number of bits in the control word), at the     */
+  /* start of each group, the top word of 'control' is filled with 1 bits.     */
+  /* As 'control' is shifted for each item, the 1 bits in the top word are     */
+  /* absorbed or destroyed. When they all run out (i.e. when the top word is   */
+  /* all zero bits, we know that we are at the end of a group.                 */
+  #define TOPWORD 0xFFFF0000
+  UBYTE *p_control;
+  ULONG control=TOPWORD;
+
+  /* The variable 'hash' always points to the first element of the hash table. */
+#if 0
+  UBYTE **hash= (UBYTE **) ext2_lzrw3a_htab;
+#else
+  UBYTE **hash= (UBYTE **) heap;
+#endif
+
+  /* The following two variables represent the literal buffer. p_h1 points to  */
+  /* the partition (i.e. the zero'th (first) element of the partition)         */
+  /* corresponding to the youngest literal. p_h2 points to the partition       */
+  /* corresponding to the second youngest literal.                             */
+  /* The value zero denotes an "empty" buffer value with p_h1=0 => p_h2=0.     */
+  UBYTE **p_h1=0;
+  UBYTE **p_h2=0;
+
+  /* The following variable holds the current 'cycle' value. This value cycles */
+  /* through the range [0,HASH_TABLE_DEPTH-1], being incremented every time    */
+  /* the hash table is updated. The value gives the within-partition number of */
+  /* the next pointer to be overwritten. The decompressor maintains a cycle    */
+  /* value in synchrony.                                                       */
+  UCARD cycle=0;
+
+  /* Reserve the first two bytes of output as the control word for the first group. */
+  /* Note: This is undone at the end if the input block is empty.              */
+  p_control = p_dst;
+  p_dst += 2;
+
+  /* Initialize all elements of the hash table to point to a constant string.  */
+  /* Use of an unrolled loop speeds this up considerably.                      */
+  /* These variables should really be declared "register", but I am worried    */
+  /* about the possibility that extra register declarations will tempt stupid  */
+  /* compilers to allocate all registers before they get to the innermostloop. */
+  {
+   UCARD i;
+   UBYTE **p_h = hash;
+   #define ZH *p_h++=START_STRING_18
+   for (i = 256; i--;)     /* 256=HASH_TABLE_LENGTH/16. */
+     {ZH;ZH;ZH;ZH;
+      ZH;ZH;ZH;ZH;
+      ZH;ZH;ZH;ZH;
+      ZH;ZH;ZH;ZH;}
+   #undef ZH
+  }
+
+  /* The main loop processes either 1 or 16 items per iteration. As its        */
+  /* termination logic is complicated, I have opted for an infinite loop       */
+  /* structure containing 'break' and 'goto' statements.                       */
+  while (TRUE)
+    {/* Begin main processing loop. */
+
+     /* Note: All the variables here except unroll should be defined within    */
+     /*       the inner loop. Unfortunately the loop hasn't got a block.       */
+      UBYTE const *p_ziv = (void *) 0; /* Points to first byte of current Ziv.       */
+      UCARD unroll;              /* Loop counter for unrolled inner loop.      */
+      UCARD index;               /* Index of current partition.                */
+      UBYTE **p_h0;              /* Pointer to current partition.              */
+      register UCARD d;          /* Depth looping variable.                    */
+      register UCARD bestlen;    /* Holds the best length seen so far.         */
+      register UCARD bestpos;    /* Holds number of best pointer seen so far.  */
+
+     /* Test for overrun and jump to overrun code if necessary.                */
+     if (p_dst > p_dst_post)	/* tbi: This looks wrong.  Why not '>=' ? */
+	goto overrun;
+
+     /* The following cascade of if statements efficiently catches and deals   */
+     /* with varying degrees of closeness to the end of the input block.       */
+     /* When we get very close to the end, we stop updating the table and      */
+     /* code the remaining bytes as literals. This makes the code simpler.     */
+     unroll=16;
+     if (p_src>p_src_max16)
+       {
+	unroll=1;
+	if (p_src>p_src_max1)
+	  {
+	   if (p_src==p_src_post)
+	      break;
+	   else
+	      {p_h0=&hash[ANY_HASH_INDEX]; /* Avoid undefined pointer. */
+	       goto literal;}
+	  }
+       }
+
+     /* This inner unrolled loop processes 'unroll' (whose value is either 1   */
+     /* or 16) items. I have chosen to implement this loop with labels and     */
+     /* gotos to heighten the ease with which the loop may be implemented with */
+     /* a single decrement and branch instruction in assembly language and     */
+     /* also because the labels act as highly readable place markers.          */
+     /* (Also because we jump into the loop for endgame literals (see above)). */
+
+     begin_unrolled_loop:
+
+	p_ziv=p_src;
+
+	/* To process the next phrase, we hash the next three bytes to obtain  */
+	/* an index to the zeroth (first) pointer in a target partition. We    */
+	/* get the pointer.                                                    */
+	index=HASH(p_src);
+	p_h0=&hash[index];
+
+	/* This next part runs through the pointers in the partition matching  */
+	/* the bytes they point to in the Lempel with the bytes in the Ziv.    */
+	/* The length (bestlen) and within-partition pointer number (bestpos)  */
+	/* of the longest match so far is maintained and is the output of this */
+	/* segment of code. The s[bestlen]==... is an optimization only.       */
+	bestlen=0;
+	bestpos=0;
+	for (d=0;d<HASH_TABLE_DEPTH;d++)
+	  {
+	   register UBYTE const *s=p_src;
+	   register UBYTE *p=p_h0[d];
+	   register UCARD len;
+	   if (s[bestlen] == p[bestlen])
+	     {
+	      #define PS *p++!=*s++
+	      (void) (PS || PS || PS || PS || PS || PS || PS || PS || PS ||
+		      PS || PS || PS || PS || PS || PS || PS || PS || PS || s++);
+	      len = s - p_src - 1;
+	      if (len>bestlen)
+		{
+		 bestpos=d;
+		 bestlen=len;
+		}
+	     }
+	  }
+
+	/* The length of the longest match determines whether we code a */
+	/* literal item or a copy item.                                 */
+
+	if (bestlen<3)
+	  {
+	   /* Literal. */
+
+	   /* Code the literal byte as itself and a zero control bit.          */
+	   literal: *p_dst++=*p_src++; control&=0xFFFEFFFF;
+
+	   /* We have just coded a literal. If we had two pending ones, that   */
+	   /* makes three and we can update the hash table.                    */
+	   if (p_h2!=0)
+	      {UPDATE_P(p_h2, (UBYTE *)p_ziv - 2);}
+
+	   /* In any case, rotate the hash table pointers for next time. */
+	   p_h2=p_h1;
+	   p_h1=p_h0;
+
+	  }
+	else
+	  {
+	   /* Copy */
+
+	   /* To code a copy item, we construct a hash table index of the      */
+	   /* winning pointer (index+=bestpos) and code it and the best length */
+	   /* into a 2 byte code word. Bump up p_src.                          */
+	   index+=bestpos;
+	   *p_dst++=((index&0xF00)>>4)|(bestlen-3);
+	   *p_dst++=index&0xFF;
+	   p_src+=bestlen;
+
+	   /* As we have just coded three bytes, we are now in a position to   */
+	   /* update the hash table with the literal bytes that were pending   */
+	   /* upon the arrival of extra context bytes.                         */
+	   if (p_h1!=0)
+	     {
+	      if (p_h2!=0)
+		{UPDATE_P(p_h2,(UBYTE *)p_ziv-2); p_h2=0;}
+	      UPDATE_P(p_h1,(UBYTE *)p_ziv-1); p_h1=0;
+	     }
+
+	   /* In any case, we can update the hash table based on the current   */
+	   /* position as we just coded at least three bytes in a copy items.  */
+	   UPDATE_P(p_h0,(UBYTE *)p_ziv);
+	  }
+	control>>=1;
+
+	/* This loop is all set up for a decrement and jump instruction! */
+     if (--unroll) goto begin_unrolled_loop;
+
+     /* At this point it will nearly always be the end of a group in which     */
+     /* case, we have to do some control-word processing. However, near the    */
+     /* end of the input block, the inner unrolled loop is only executed once. */
+     /* This necessitates the 'if' test.                                       */
+     if ((control&TOPWORD)==0)
+       {
+	/* Write the control word to the place we saved for it in the output. */
+	*p_control++=  control     &0xFF;
+	*p_control  = (control>>8) &0xFF;
+
+	/* Reserve the next word in the output block for the control word */
+	/* for the group about to be processed.                           */
+	p_control=p_dst; p_dst+=2;
+
+	/* Reset the control bits buffer. */
+	control=TOPWORD;
+       }
+
+    } /* End main processing loop. */
+
+  /* After the main processing loop has executed, all the input bytes have     */
+  /* been processed. However, the control word has still to be written to the  */
+  /* word reserved for it in the output at the start of the most recent group. */
+  /* Before writing, the control word has to be shifted so that all the bits   */
+  /* are in the right place. The "empty" bit positions are filled with 1s      */
+  /* which partially fill the top word.                                        */
+  while(control&TOPWORD) control>>=1;
+  *p_control++= control     &0xFF;
+  *p_control++=(control>>8) &0xFF;
+
+  /* If the last group contained no items, delete the control word too.        */
+  if (p_control==p_dst) p_dst-=2;
+
+  /* Write the length of the output block to the dst_len parameter and return. */
+  return p_dst - p_dst_first;
+
+  /* Jump here as soon as an overrun is detected. An overrun is defined to     */
+  /* have occurred if p_dst>p_dst_first+*p_dst_len as set by caller. */
+  /* The algorithm checks for overruns at least at the end of each group       */
+  /* which means that the maximum overrun is MAX_CMP_GROUP bytes.              */
+  /* Once an overrun occurs, the only thing to do is to return an error        */
+  overrun:
+  return 0;
+}
+
+/******************************************************************************/
+
+#ifndef LZRW3A_ASM
+EXPORT int ext2_LZRW3A_decompress
+           (UBYTE *p_src_first, UBYTE *p_dst, void *heap, int src_len,
+	    int dst_len)
+/* Input  : Specify input block using p_src_first.                */
+/* Input  : Point p_dst to the start of the output zone.                */
+/* Input  : dst_len contains output length */
+/* Input  : Input block and output zone must not overlap. User knows          */
+/* Input  : upperbound on output block length from earlier compression.       */
+/* Input  : In any case, maximum expansion possible is nine times.            */
+/* Input  : src_len contains max input length */
+/* Output : Writes only  in Mem[p_dst..p_dst+dst_len-1].       */
+{
+ UBYTE *p_dst_first = p_dst;
+
+ /* Byte pointers scan through the input and output blocks.   */
+ register UBYTE *p_src = p_src_first;
+
+ /* The following two variables are never modified and are used to control    */
+ /* the main loop.                                                            */
+ UBYTE * const p_dst_post  = p_dst+dst_len;
+ UBYTE * const p_dst_max16 = p_dst+dst_len-MAX_RAW_ITEM*16;
+
+ /* The hash table is the only resident of the working memory. The hash table */
+ /* contains HASH_TABLE_LENGTH=4096 pointers to positions in the history. To  */
+ /* keep Macintoshes happy, it is longword aligned.                           */
+#if 0
+ UBYTE **hash = (UBYTE **) ext2_lzrw3a_htab;
+#else
+ UBYTE **hash = (UBYTE **) heap;
+#endif
+
+ /* The variable 'control' is used to buffer the control bits which appear in */
+ /* groups of 16 bits (control words) at the start of each compressed group.  */
+ /* When each group is read, bit 16 of the register is set to one. Whenever   */
+ /* a new bit is needed, the register is shifted right. When the value of the */
+ /* register becomes 1, we know that we have reached the end of a group.      */
+ /* Initializing the register to 1 thus instructs the code to follow that it  */
+ /* should read a new control word immediately.                               */
+ register ULONG control=1;
+
+ /* The value of 'literals' is always in the range 0..3. It is the number of  */
+ /* consecutive literal items just seen. We have to record this number so as  */
+ /* to know when to update the hash table. When literals gets to 3, there     */
+ /* have been three consecutive literals and we can update at the position of */
+ /* the oldest of the three.                                                  */
+ register UCARD literals=0;
+
+ /* The following variable holds the current 'cycle' value. This value cycles */
+ /* through the range [0,HASH_TABLE_DEPTH-1], being incremented every time    */
+ /* the hash table is updated. The value give the within-partition number of  */
+ /* the next pointer to be overwritten. The compressor maintains a cycle      */
+ /* value in synchrony.                                                       */
+ UCARD cycle=0;
+
+ /* Initialize all elements of the hash table to point to a constant string.  */
+ /* Use of an unrolled loop speeds this up considerably.                      */
+ /* The comment about register declarations above similar code in the         */
+ /* compressor applies here too.                                              */
+ {
+  UCARD i; 
+  UBYTE **p_h = hash;
+
+  #define ZJ *p_h++=START_STRING_18
+  for (i=0;i<256;i++)     /* 256=HASH_TABLE_LENGTH/16. */
+    {ZJ;ZJ;ZJ;ZJ;
+     ZJ;ZJ;ZJ;ZJ;
+     ZJ;ZJ;ZJ;ZJ;
+     ZJ;ZJ;ZJ;ZJ;}
+  #undef ZJ
+ }
+
+ /* The outer loop processes either 1 or 16 items per iteration depending on  */
+ /* how close p_dst is to the end of the output block.                         */
+ while (p_dst < p_dst_post && src_len >= 0)
+   {/* Start of outer loop */
+
+    register UCARD unroll;   /* Counts unrolled loop executions.              */
+
+    /* When 'control' has the value 1, it means that the 16 buffered control  */
+    /* bits that were read in at the start of the current group have all been */
+    /* shifted out and that all that is left is the 1 bit that was injected   */
+    /* into bit 16 at the start of the current group. When we reach the end   */
+    /* of a group, we have to load a new control word and inject a new 1 bit. */
+    if (control==1)
+      {
+       control=0x10000|*p_src++;
+       control|=(*p_src++)<<8;
+       src_len -= 2;
+      }
+
+    /* If it is possible that we are within 16 groups from the end of the     */
+    /* input, execute the unrolled loop only once, else process a whole group */
+    /* of 16 items by looping 16 times.                                       */
+    unroll= p_dst<=p_dst_max16 ? 16 : 1;
+
+    /* This inner loop processes one phrase (item) per iteration. */
+    while (unroll--)
+      { /* Begin unrolled inner loop. */
+
+       /* Process a literal or copy item depending on the next control bit. */
+       if (control&1)
+         {
+          /* Copy item. */
+
+          register UBYTE *p;           /* Points to place from which to copy. */
+          register UCARD lenmt;        /* Length of copy item minus three.    */
+          register UBYTE *p_ziv = p_dst; /* Pointer to start of current Ziv.    */
+          register UCARD index;        /* Index of hash table copy pointer.   */
+
+          /* Read and dismantle the copy word. Work out from where to copy.   */
+          lenmt=*p_src++;
+          index=((lenmt&0xF0)<<4)|*p_src++;
+	  src_len -= 2;
+          p=hash[index];
+          lenmt&=0xF;
+
+          /* Now perform the copy using a half unrolled loop. */
+          *p_dst++=*p++;
+          *p_dst++=*p++;
+          *p_dst++=*p++;
+          while (lenmt--)
+             *p_dst++=*p++;
+
+          /* Because we have just received 3 or more bytes in a copy item     */
+          /* (whose bytes we have just installed in the output), we are now   */
+          /* in a position to flush all the pending literal hashings that had */
+          /* been postponed for lack of bytes.                                */
+          if (literals>0)
+            {
+             register UBYTE *r = p_ziv - literals;;
+             UPDATE_I(HASH(r),r);
+             if (literals==2)
+                {r++; UPDATE_I(HASH(r),r);}
+             literals=0;
+            }
+
+          /* In any case, we can immediately update the hash table with the   */
+          /* current position. We don't need to do a HASH(...) to work out    */
+          /* where to put the pointer, as the compressor just told us!!!      */
+          UPDATE_I(index&(~DEPTH_MASK),p_ziv);
+         }
+       else
+         {
+          /* Literal item. */
+
+          /* Copy over the literal byte. */
+          *p_dst++=*p_src++;
+	  src_len--;
+
+          /* If we now have three literals waiting to be hashed into the hash */
+          /* table, we can do one of them now (because there are three).      */
+          if (++literals == 3)
+             {register UBYTE *p=p_dst-3;
+              UPDATE_I(HASH(p),p); literals=2;}
+         }
+
+       /* Shift the control buffer so the next control bit is in bit 0. */
+       control>>=1;
+
+      } /* End unrolled inner loop. */
+
+   } /* End of outer loop */
+
+#if 0
+   if(src_len < 0) return 0;
+   return (p_src-p_src_first);
+#else
+   return p_dst - p_dst_first;
+#endif
+}
+#endif
+
+/******************************************************************************/
+/*                              End of LZRW3-A.C                              */
+/******************************************************************************/
--- linux-2.6.25/fs/ext2/lzrw3a/lzrw.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzrw3a/lzrw.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,106 @@
+/* Author : Ross Williams.                                                    */
+/* Date   : December 1989.                                                    */
+/* This header file and the C functions have been modified in order to        */
+/* include them in the DouBle device driver by Jean-Marc Verbavatz, Feb 1994. */
+/*                                                                            */
+/* This header file defines the interface to a set of lzrw functions          */
+/* each member of which implements a particular data compression              */
+/* algorithm.                                                                 */
+/*                                                                            */
+/* Normally in C programming, for each .H file, there is a corresponding .C   */
+/* file that implements the functions promised in the .H file.                */
+/* Here, there are many .C files corresponding to this header file.           */
+/* Each comforming implementation file contains a single function             */
+/* called that implements a single data compression                           */
+/* algorithm that conforms with the interface specified in this header file.  */
+/*                                                                            */
+/******************************************************************************/
+/* See the formal C definition later for a description of the parameters.     */
+/*                                                                            */
+/* Although compression algorithms usually compress data, there will always   */
+/* be data that a given compressor will expand (this can be proven).          */
+/*                                                                            */
+/* Unfortunately, in general, the only way to tell if an algorithm will       */
+/* expand a particular block of data is to run the algorithm on the data.     */
+/* If the algorithm does not continuously monitor how many output bytes it    */
+/* has written, it might write an output block far larger than the input      */
+/* block before realizing that it has done so.                                */
+/* On the other hand, continuous checks on output length are inefficient.     */
+/*                                                                            */
+/* The problem does not arise for decompression.                              */
+/*                                                                            */
+/******************************************************************************/
+/* Macro definitions and types that are likely to change between computers.   */
+/*                                                                            */
+#ifndef DONE_PORT       /* Only do this if not previously done.               */
+   #ifndef TRUE
+	#define TRUE 1
+	#define FALSE 0
+   #endif
+   #ifndef UBYTE
+      #define UBYTE unsigned char      /* Unsigned byte                       */
+      #define UWORD unsigned short     /* Unsigned word (2 bytes)             */
+      #define ULONG unsigned long      /* Unsigned word (4 bytes)             */
+  #endif
+
+   #define DONE_PORT                   /* Don't do all this again.            */
+   #define LOCAL static                /* For non-exported routines.          */
+   #define EXPORT                      /* Signals exported function.          */
+
+#endif
+
+#if 0
+extern void *ext2_lzrw3a_htab;
+#endif
+
+int  rlzrw2(        /* function for decompression algorithm. */
+UBYTE   *src_adr,      /* Address of input  data.            */
+UBYTE   *dst_adr,      /* Address of output data.            */
+int      src_len,      /* Length  of output data.            */
+int      dst_len       /* Length  of output data.            */
+);
+
+void  wlzrw2(        /* function for compression algorithm. */
+UBYTE   *src_adr,      /* Address of input  data.           */
+int      src_len,      /* Length  of input  data.           */
+UBYTE   *dst_adr,      /* Address of output data.           */
+int   *p_dst_len       /* Pointer to a longword for         */
+                       /*    Length of output data. */
+);
+
+int  rlzrw3(        /* function for decompression algorithm. */
+UBYTE   *src_adr,      /* Address of input  data.                             */
+UBYTE   *dst_adr,      /* Address of output data.                             */
+ULONG    dst_len       /* Length  of output data.                             */
+);
+
+void  wlzrw3(        /* function for compression algorithm. */
+UBYTE   *src_adr,      /* Address of input  data.                             */
+ULONG    src_len,      /* Length  of input  data.                             */
+UBYTE   *dst_adr,      /* Address of output data.                             */
+ULONG *p_dst_len       /* Pointer to a longword where routine will write:     */
+                       /*    Length of output data. */
+);
+/*
+ * NOTE: lzrw3 is not implemented in the DouBle driver version 0.2
+ */
+
+int  rlzrw3a(        /* function for decompression algorithm. */
+UBYTE   *src_adr,      /* Address of input  data.             */
+UBYTE   *dst_adr,      /* Address of output data.             */
+int      src_len,      /* Length  of input data.              */
+int      dst_len       /* Length  of output data.             */
+);
+
+void  wlzrw3a(        /* function for compression algorithm. */
+UBYTE   *src_adr,      /* Address of input  data.            */
+int      src_len,      /* Length  of input  data.            */
+UBYTE   *dst_adr,      /* Address of output data.            */
+int   *p_dst_len       /* Pointer to a longword for          */
+                       /*    Length of output data.          */
+);
+
+/******************************************************************************/
+/*                             End of LZRW.H                                  */
+/******************************************************************************/
+
--- linux-2.6.25/fs/ext2/lzrw3a/rlzrw3a.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzrw3a/rlzrw3a.S	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,350 @@
+/*	
+ *	LZRW3A decompression written in assembly.
+ *
+ *	Written by Antoine de Maricourt (dumesnil@etca.fr)
+ *	                17 Mar 1995
+ *
+ *	NOTE: The use of this algorithm may be restricted by some
+ *	      patent. Please check if this is the case in your 
+ *	      country before using it. As far as I know LZRW3A is
+ *	      patended in the USA.
+ *
+ *	In order to be used, the compression routine (file lrzw3a.c)
+ *	must be updated to use the following hash function :
+ *
+ *	#define HASH(PTR) (\
+ *	  (((((*(PTR)<<3)^(*((PTR)+1)))<<3)^(*((PTR)+2)))&HASH_MASK)\
+ *	  << HASH_TABLE_DEPTH_BITS)
+ *
+ *	The hash table depth is hardwired to 8.
+ *
+ *	Interface :
+ *	-----------
+ *
+ *	   int rLZRW3A (char *ib, char *ob, int il, int ol);
+ *
+ *              ib = input buffer (compressed data)
+ *              ob = output buffer (uncompressed data)
+ *		il = number of bytes in the input buffer
+ *		ol = number of bytes to be uncompressed
+ *
+ *	The routine may write more (i.e., ???) bytes than requested
+ *	The returned value is the number of bytes written to output
+ *	buffer.
+ *
+ *	The algorithm and the coding method have been taken from
+ *	file lzrw3a.c originaly written by Ross Williams.
+ *
+ *	-----
+ *	Copyright (c) 1995 Antoine de Maricourt. Redistribution
+ *	of this file is permitted under the GNU Public License.
+ */
+
+#include <linux/linkage.h>
+
+#define A0	32
+#define A1	36
+#if 0
+#define A2	40
+#define A3	44
+#else
+#define A2	44
+#define A3	48
+#endif
+#define L0
+
+#if 0
+#define HTAB	SYMBOL_NAME(ext2_lzrw3a_htab)
+#else
+#define HTAB	40(%esp)
+#endif
+
+#if 0
+.comm HTAB, 4				# hash table
+#endif
+
+LC0:	.ascii "123456789012345678\0"
+
+	/* for i386 */
+#define bswap rol $16,
+
+/*
+ *	Initialize the hash table. Some might want to do
+ *	it with an unrolled loop. It would not be faster
+ *	on my system.
+ */
+
+#define InitTable()			\
+	movl HTAB, %edi;		\
+	movl $4096, %ecx;		\
+	movl $LC0, %eax;		\
+	rep; stosl
+
+#define CopyOneByte			\
+	movb (%esi), %dh;		\
+	movb %dh, 3(%edi)
+
+#define CopyByte(o1,o2)			\
+	movb o1(%esi), %dh;		\
+	movb %dh, o2(%edi)
+
+/*
+ *	Update the hash key with the given byte. Note that
+ *	the key is shifted after the xor, and not before.
+ *	This will make it easier to add the cycle count.
+ */
+
+#define UpdateHashKey			\
+	bswap %ebx;			\
+	xorb %dh, %bl;			\
+	andw $0x1ff, %bx;		\
+	shlw $3, %bx;			\
+	bswap %ebx
+
+/*
+ *	Update the hashtable with the current position. The
+ *	cycle count is incremented.
+ */
+
+#define UpdateHashTable			\
+	andl $7, %edx;			\
+	bswap %ebx;			\
+	addw %bx, %dx;			\
+	movl %edi, (%ebp,%edx,4);	\
+	incb %dl;			\
+	bswap %ebx
+
+/*
+ *	Copy a byte from the input buffer to the output
+ *	and remember the value. This will be used instead
+ *	of movsb when we need the value to update the hash
+ *	key.
+ */
+
+#define CopyLiteral			\
+	CopyOneByte;			\
+	incl %esi;			\
+	incl %edi;			\
+	UpdateHashKey
+
+	.text
+
+ENTRY(ext2_LZRW3A_decompress)
+	pushl %ebp
+	pushl %edi
+	pushl %esi
+	pushl %ebx			# be safe
+	pushl %ecx
+	pushl %edx
+	subl $4, %esp
+
+	InitTable ()
+
+	movl A0(%esp), %esi		# esi = input buffer
+	movl A1(%esp), %edi		# edi = output buffer
+	subl $3, %edi
+	addl %esi, A2(%esp)		# input limit
+	movl A3(%esp), %eax
+	addl %edi, %eax	
+	movl %eax, A3(%esp)		# output limit
+	subl $288, %eax
+	movl %eax, L0(%esp)
+
+	/*
+	 *	eax = control word
+	 *	ebx = hash key (high) + literal count (bl) + loop count (bh)
+	 *	ecx = tmp data
+	 *	edx = cycle count (dl) + tmp data (dh)
+	 *	esi = source pointer
+	 *	edi = destination pointer
+	 *	ebp = hash table address
+	 */
+
+	xorl %eax, %eax			# we need a control word
+	movl %eax, %ebx			# hash key is 0
+	movl %eax, %edx			# cycle is 0
+	movl HTAB, %ebp			# get pointer to hash table
+	movb $3, %bl	
+
+	/*
+	 *	Load a fresh control word if needed
+	 */
+
+L10:	cmpl %esi, A2(%esp)		# check for input overflow
+	jna L40
+
+	cmp $1, %eax
+	ja L11
+	movb (%esi), %al
+	movb 1(%esi), %ah
+	lea 2(%esi), %esi
+	orl $0x10000, %eax
+
+L11:	movb $16, %bh
+	cmpl L0(%esp), %edi
+	jbe L12
+	movb $1, %bh
+
+L12:	shrl $1, %eax
+	jc L20				# test control bit
+
+	/*
+	 *	This is a literal
+	 */
+
+	CopyLiteral
+
+	decb %bl
+	jne L13
+	UpdateHashTable
+	incb %bl
+
+L13:	decb %bh			# loop 
+	jne L12
+
+	cmpl A3(%esp), %edi		# check for output overflow
+	jb L10				# and continue
+	jmp L40
+
+	/*
+	 *	This is a copy item
+	 */
+
+
+	ALIGN
+L20:
+	/*
+	 *	Load the index into ecx, and the len into dh
+	 */
+
+	xorl %ecx, %ecx			# clear high part of ecx
+	movb (%esi), %ch		# load 4 high bits of index
+	movb 1(%esi), %cl		# load 8 low bits of index
+	shrb $4, %ch			# adjust index
+
+	movl (%ebp,%ecx,4), %ecx	# get pointer to item
+	xchg %ecx, %esi			# put it into esi, save esi into ecx
+
+	/*
+	 *	Copy first byte
+         */
+
+	CopyLiteral
+	decb %bl
+	jne L22
+	UpdateHashTable
+	incb %bl
+
+	/*
+	 *	Copy second byte
+         */
+
+L22:	CopyLiteral
+	decb %bl
+	jne L23
+	UpdateHashTable
+
+	/*
+	 *	Copy third byte
+	 */
+
+L23:	CopyLiteral
+	UpdateHashTable			# do not need to test bl.
+	movb $3, %bl
+
+	/*
+	 *	Copy the remaining bytes
+	 */
+	
+	testb $1, (%ecx)
+	je L24
+	CopyOneByte
+	incl %esi
+	incl %edi
+
+L24:	testb $2, (%ecx)
+	je L25
+
+	CopyByte (0, 3)
+	CopyByte (1, 4)
+
+	lea 2(%esi), %esi
+	lea 2(%edi), %edi
+
+L25:	cmp %esi, %edi
+	ja L30
+
+	testb $4, (%ecx)
+	je L26
+
+	CopyByte (0, 3)
+	CopyByte (1, 4)
+	CopyByte (2, 5)
+	CopyByte (3, 6)
+
+	lea 4(%esi), %esi
+	lea 4(%edi), %edi
+
+L26:	testb $8, (%ecx)
+	je L27
+
+	CopyByte (0, 3)
+	CopyByte (1, 4)
+	CopyByte (2, 5)
+	CopyByte (3, 6)
+	CopyByte (4, 7)
+	CopyByte (5, 8)
+	CopyByte (6, 9)
+	CopyByte (7, 10)
+
+	lea 8(%esi), %esi
+	lea 8(%edi), %edi
+
+L27:	lea 2(%ecx), %esi		# restore input pointer
+
+	decb %bh			# loop 
+	jne L12
+
+	cmpl A3(%esp), %edi		# check for output overflow
+	jb L10				# and continue
+	jmp L40
+
+	/*
+	 *	Copy the remaining bytes, but using movsl
+	 *	when we know the strings does not overlap.
+	 */
+	
+	ALIGN
+
+L30:	lea 3(%edi), %edi
+	testb $4, (%ecx)
+	je L31
+	movsl
+L31:	testb $8, (%ecx)
+	je L32
+	movsl
+	movsl
+
+L32:	lea -3(%edi), %edi
+	lea  2(%ecx), %esi		# restore input pointer
+	decb %bh			# loop 
+	jne L12
+
+	cmpl A3(%esp), %edi		# check for output overflow
+	jb L10				# and continue
+	
+	/*
+	 *	The end.
+	 */
+
+L40:	lea 3(%edi), %eax
+	subl A1(%esp),%eax
+
+	addl $4, %esp
+	popl %edx
+	popl %ecx
+	popl %ebx
+	popl %esi
+	popl %edi
+	popl %ebp
+	ret
--- linux-2.6.25/fs/ext2/lzrw3a/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzrw3a/Makefile	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,19 @@
+#
+# Makefile for the linux compression routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.S.o:
+	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+
+obj-$(CONFIG_EXT2_HAVE_LZRW3A) := ext2-compr-lzrw3a.o
+
+ifeq ($(CONFIG_EXT2_COMPR_X86_CODE),y)
+ext2-compr-lzrw3a-y := e2compr-lzrw3a.o rlzrw3a.o wlzrw3a.o
+else
+ext2-compr-lzrw3a-y := e2compr-lzrw3a.o lzrw3a.o
+endif
--- linux-2.6.25/fs/ext2/lzrw3a/wlzrw3a.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzrw3a/wlzrw3a.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,776 @@
+/******************************************************************************/
+/*                                                                            */
+/*                                   LZRW3-A.C                                */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/* Author  : Ross Williams.                                                   */
+/* Date    : 15-Jul-1991.                                                     */
+/* Release : 1.                                                               */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/* This file contains an implementation of the LZRW3-A data compression       */
+/* algorithm in the C programming language.                                   */
+/*                                                                            */
+/* The LZRW3-A algorithm has the following features:                          */
+/*                                                                            */
+/*    1 Requires only 16K of memory (for both compression and decompression). */
+/*    2 The compressor   runs about two   times faster than Unix compress's.  */
+/*    3 The decompressor runs about three times faster than Unix compress's.  */
+/*    4 Yields a few percent better compression than Unix compress for        */
+/*      most files.                                                           */
+/*    5 Allows you to dial up extra compression at a speed cost in the        */
+/*      compressor. The speed of the decompressor is not affected.            */
+/*    6 Algorithm is deterministic.                                           */
+/*    8 This implementation in C is in the public domain.                     */
+/*                                                                            */
+/* (Timing tests for the speed comparison were performed on a Pyramid 9820.)  */
+/*                                                                            */
+/* LZRW3-A is LZRW3 with a deepened hash table. This simple change yields     */
+/* about a 6% (absolute) improvement in compression.                          */
+/*                                                                            */
+/* Here are the results of applying this code, compiled under THINK C 4.0     */
+/* and running on a Mac-SE (8MHz 68000), to the standard calgary corpus.      */
+/*                                                                            */
+/*     +----------------------------------------------------------------+     */
+/*     | DATA COMPRESSION TEST                                          |     */
+/*     | =====================                                          |     */
+/*     | Time of run     : Mon 15-Jul-1991 05:29PM                      |     */
+/*     | Timing accuracy : One part in 100                              |     */
+/*     | Context length  : 262144 bytes (= 256.0000K)                   |     */
+/*     | Test suite      : Calgary Corpus Suite                         |     */
+/*     | Files in suite  : 14                                           |     */
+/*     | Algorithm       : LZRW3-A                                      |     */
+/*     | Note: All averages are calculated from the un-rounded values.  |     */
+/*     +----------------------------------------------------------------+     */
+/*     | File Name   Length  CxB  ComLen  %Remn  Bits  Com K/s  Dec K/s |     */
+/*     | ----------  ------  ---  ------  -----  ----  -------  ------- |     */
+/*     | rpus:Bib.D  111261    1   49044   44.1  3.53     8.47    31.19 |     */
+/*     | us:Book1.D  768771    3  420464   54.7  4.38     7.27    30.07 |     */
+/*     | us:Book2.D  610856    3  277955   45.5  3.64     8.51    33.40 |     */
+/*     | rpus:Geo.D  102400    1   84218   82.2  6.58     4.23    15.04 |     */
+/*     | pus:News.D  377109    2  192880   51.1  4.09     7.08    25.89 |     */
+/*     | pus:Obj1.D   21504    1   12651   58.8  4.71     5.23    17.44 |     */
+/*     | pus:Obj2.D  246814    1  108044   43.8  3.50     8.01    28.11 |     */
+/*     | s:Paper1.D   53161    1   24526   46.1  3.69     8.11    30.24 |     */
+/*     | s:Paper2.D   82199    1   39483   48.0  3.84     8.11    32.04 |     */
+/*     | rpus:Pic.D  513216    2  111622   21.7  1.74    10.64    49.31 |     */
+/*     | us:Progc.D   39611    1   17923   45.2  3.62     8.06    29.01 |     */
+/*     | us:Progl.D   71646    1   24362   34.0  2.72    10.74    39.51 |     */
+/*     | us:Progp.D   49379    1   16805   34.0  2.72    10.64    37.58 |     */
+/*     | us:Trans.D   93695    1   30296   32.3  2.59    11.02    38.06 |     */
+/*     +----------------------------------------------------------------+     */
+/*     | Average     224401    1  100733   45.8  3.67     8.29    31.21 |     */
+/*     +----------------------------------------------------------------+     */
+/*                                                                            */
+/******************************************************************************/
+
+#include "lzrw.h"
+/******************************************************************************/
+
+/* The following structure is returned by the "compress" function below when  */
+/* the user asks the function to return identifying information.              */
+/* The most important field in the record is the working memory field which   */
+/* tells the calling program how much working memory should be passed to      */
+/* "compress" when it is called to perform a compression or decompression.    */
+/* LZRW3-A uses the same amount of memory during compression and              */
+/* decompression. For more information on this structure see "lzrw.h".        */
+/* The alignment fudge below really only needs to be 4 (but I play it safe!). */
+/* The id looks non-random, but it really was generated by coin tossing!      */
+
+#define U(X)            ((ULONG) X)
+#define SIZE_P_BYTE     (U(sizeof(UBYTE *)))
+#define ALIGNMENT_FUDGE (U(16))
+#define MEM_REQ ( U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE )
+
+/*static struct compress_identity identity =
+{
+ U(0x01B90B91),                            * Algorithm identification number. * 
+ MEM_REQ,                                  * Working memory (bytes) required. * 
+ "LZRW3-A",                                * Name of algorithm.               * 
+ "1.0",                                    * Version number of algorithm.     * 
+ "15-Jul-1990",                            * Date of algorithm.               * 
+ "Ross N. Williams",                       * Author of algorithm.             * 
+ "Renaissance Software",                   * Affiliation of author.           * 
+};*/
+
+/******************************************************************************/
+/*                                                                            */
+/* BRIEF DESCRIPTION OF THE LZRW3-A ALGORITHM                                 */
+/* ==========================================                                 */
+/* Note: Before attempting to understand this algorithm, you should first     */
+/* understand the LZRW3 algorithm from which this algorithm is derived.       */
+/*                                                                            */
+/* The LZRW3-A algorithm is identical to the LZRW3 algorithm except that the  */
+/* hash table has been "deepened". The LZRW3 algorithm has a hash table of    */
+/* 4096 pointers which point to strings in the buffer. LZRW3-A generalizes    */
+/* this to 4096/(2^n) partitions each of which contains (2^n) pointers.       */
+/* In LZRW3-A, the hash function hashes to a partition number.                */
+/*                                                                            */
+/* During the processing of each phrase, LZRW3 overwrites the pointer in the  */
+/* position selected by the hash function. LZRW3-A overwrites one of the      */
+/* pointers in the partition that was selected by the hash function.          */
+/*                                                                            */
+/* When searching for a match, LZRW3-A matches against all (2^n) strings      */
+/* pointed to by the pointers in the target partition.                        */
+/*                                                                            */
+/* Deep hash tables were used in early versions of LZRW1 in late 1989, but    */
+/* were discarded in an effort to increase speed (which was the primary       */
+/* requirement for LZRW1). They were revived for use in LZRW3-A in order to   */
+/* produce an algorithm with compression performance competitive with Unix    */
+/* compress.                                                                  */
+/*                                                                            */
+/* Until 14-Jul-1991, deep hash tables used in prototype LZRW* algorithms     */
+/* used a queue discipline within each partition. Upon the arrival of a new   */
+/* pointer, the pointers in the partition would be block copied back one      */
+/* position (with the oldest pointer being overwritten) and the new pointer   */
+/* being inserted in the space at the front (the youngest position).          */
+/* This meant that pointers to the (2^n) most recent phrases corresponding to */
+/* each hash was kept. The only flaw in this system was the time-consuming    */
+/* block copy operation which was cheap for shallow tables but expensive for  */
+/* deep tables.                                                               */
+/*                                                                            */
+/* The traditional solution to ring buffer block copy problems is to maintain */
+/* a cyclic counter which points to the "head" of the queue. However, this    */
+/* would have required one counter to be stored for each partition and would  */
+/* have been slightly messy. After some thought (on 14-Jul-1991) a better     */
+/* solution was found. Instead of maintaining a counter for each partition,   */
+/* LZRW3-A maintains a single counter for all partitions! This counter is     */
+/* maintained in both the compressor and decompressor and means that the      */
+/* algorithm (effectively) overwrites a RANDOM element of the partition to be */
+/* updated. The result was to increase the speed of the compressor and        */
+/* decompressor, to make the decompressor's speed independent from whatever   */
+/* depth was selected, and to impair compression by less than 1% absolute.    */
+/*                                                                            */
+/* Setting the depth is a speed/compression tradeoff. The table below gives   */
+/* the tradeoff observed for a typical 50K text file on a Mac-SE.             */
+/* Note: %Rem=Percentage Remaining (after compression).                       */
+/*                                                                            */
+/*      Depth    %Rem    CmpK/s  DecK/s                                       */
+/*          1    45.2    14.77   32.24                                        */
+/*          2    42.6    12.12   31.26                                        */
+/*          4    40.9    10.28   31.91                                        */
+/*          8    40.0     7.81   32.36                                        */
+/*         16    39.5     5.30   32.47                                        */
+/*         32    39.0     3.23   32.59                                        */
+/*                                                                            */
+/* I have chosen a depth of 8 as the "default" depth for LZRW3-A. If you use  */
+/* a depth different to this (e.g. 4), you should use the name LZRW3-A(4) to  */
+/* indicate that a different depth is being used. LZRW3-A(8) is an acceptable */
+/* longhand for LZRW3-A.                                                      */
+/*                                                                            */
+/* To change the depth, search for "HERE IT IS" in the rest of this file.     */
+/*                                                                            */
+/*                                  +---+                                     */
+/*                                  |___|4095                                 */
+/*                                  |===|                                     */
+/*              +---------------------*_|<---+   /----+---\                   */
+/*              |                   |___|    +---|Hash    |                   */
+/*              |    512 partitions |___|        |Function|                   */
+/*              |    of 8 pointers  |===|        \--------/                   */
+/*              |    each (or any   |___|0            ^                       */
+/*              |    a*b=4096)      +---+             |                       */
+/*              |                   Hash        +-----+                       */
+/*              |                   Table       |                             */
+/*              |                              ---                            */
+/*              v                              ^^^                            */
+/*      +-------------------------------------|----------------+              */
+/*      ||||||||||||||||||||||||||||||||||||||||||||||||||||||||              */
+/*      +-------------------------------------|----------------+              */
+/*      |                                     |1......18|      |              */
+/*      |<------- Lempel=History ------------>|<--Ziv-->|      |              */
+/*      |     (=bytes already processed)      |<-Still to go-->|              */
+/*      |<-------------------- INPUT BLOCK ------------------->|              */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/*                     DEFINITION OF COMPRESSED FILE FORMAT                   */
+/*                     ====================================                   */
+/*    The file consists of zero  */
+/*    or more GROUPS, each of which represents one or more bytes.             */
+/*  * Each group consists of two bytes of CONTROL information followed by     */
+/*    sixteen ITEMs except for the last group which can contain from one      */
+/*    to sixteen items.                                                       */
+/*  * An item can be either a LITERAL item or a COPY item.                    */
+/*  * Each item corresponds to a bit in the control bytes.                    */
+/*  * The first control byte corresponds to the first 8 items in the group    */
+/*    with bit 0 corresponding to the first item in the group and bit 7 to    */
+/*    the eighth item in the group.                                           */
+/*  * The second control byte corresponds to the second 8 items in the group  */
+/*    with bit 0 corresponding to the ninth item in the group and bit 7 to    */
+/*    the sixteenth item in the group.                                        */
+/*  * A zero bit in a control word means that the corresponding item is a     */
+/*    literal item. A one bit corresponds to a copy item.                     */
+/*  * A literal item consists of a single byte which represents itself.       */
+/*  * A copy item consists of two bytes that represent from 3 to 18 bytes.    */
+/*  * The first  byte in a copy item will be denoted C1.                      */
+/*  * The second byte in a copy item will be denoted C2.                      */
+/*  * Bits will be selected using square brackets.                            */
+/*    For example: C1[0..3] is the low nibble of the first control byte.      */
+/*    of copy item C1.                                                        */
+/*  * The LENGTH of a copy item is defined to be C1[0..3]+3 which is a number */
+/*    in the range [3,18].                                                    */
+/*  * The INDEX of a copy item is defined to be C1[4..7]*256+C2[0..8] which   */
+/*    is a number in the range [0,4095].                                      */
+/*  * A copy item represents the sequence of bytes                            */
+/*       text[POS-OFFSET..POS-OFFSET+LENGTH-1] where                          */
+/*          text   is the entire text of the uncompressed string.             */
+/*          POS    is the index in the text of the character following the    */
+/*                   string represented by all the items preceeding the item  */
+/*                   being defined.                                           */
+/*          OFFSET is obtained from INDEX by looking up the hash table.       */
+/*                                                                            */
+/******************************************************************************/
+
+/* When I first started to get concerned about the portability of my C code,  */
+/* I switched over to using only macro defined types UBYTE, UWORD, ULONG and  */
+/* one or two others. While, these are useful for most purposes, they impair  */
+/* efficiency as, if I have a variable whose range will be [0,1000], I will   */
+/* declare it as a UWORD. This will translate into (say) "short int" and      */
+/* hence may be less efficient than just an "int" which represents the        */
+/* natural size of the machine. Before releasing LZRW3-A, I realized this     */
+/* mistake. Unfortunately, I can't access the ftp archive with my portability */
+/* header in it in time for this algorithm's release and so I am including an */
+/* extra definition. The definition UCARD stands for an unsigned (cardinal)   */
+/* type that can hold values in the range [0,32767]. This is within the ANSI  */
+/* range of a standard int or unsigned. No assumption about overflow of this  */
+/* type is made in the code (i.e. all usages are within range and I do not    */
+/* use the value -1 to detect the end of loops.).                             */
+/* You can use either "unsigned" or just "int" here depending on which is     */
+/* more efficient in your environment (both the same probably).               */
+#define UCARD unsigned
+
+/* The following constant defines the maximum length of an uncompressed item. */
+/* This definition must not be changed; its value is hardwired into the code. */
+/* The longest number of bytes that can be spanned by a single item is 18     */
+/* for the longest copy item.                                                 */
+#define MAX_RAW_ITEM (18)
+
+/* The following constant defines the maximum length of a compressed group.   */
+/* This definition must not be changed; its value is hardwired into the code. */
+/* A compressed group consists of two control bytes followed by up to 16      */
+/* compressed items each of which can have a maximum length of two bytes.     */
+#define MAX_CMP_GROUP (2+16*2)
+
+/* This constant defines the number of pointers in the hash table. The number */
+/* of partitions multiplied by the number of pointers in each partition must  */
+/* multiply out to this value of 4096. In LZRW1, LZRW1-A, and LZRW2, this     */
+/* table length value can be changed. However, in LZRW3-A (and LZRW3), the    */
+/* table length cannot be changed because it is connected directly to the     */
+/* coding scheme which is hardwired (the table index of a single pointer is   */
+/* transmitted in the 12-bit index field). So don't change this constant!     */
+#define HASH_TABLE_LENGTH (4096)
+
+/* HERE IT IS: THE PLACE TO CHANGE THE HASH TABLE DEPTH!                      */
+/* The following definition is the log_2 of the depth of the hash table. This */
+/* constant can be in the range [0,1,2,3,...,12]. Increasing the depth        */
+/* increases compression at the expense of speed. However, you are not likely */
+/* to see much of a compression improvement (e.g. not more than 0.5%) above a */
+/* value of 6 and the algorithm will start to get very slow. See the table in */
+/* the earlier comments block for an idea of the trade-off involved.          */
+/* Note: The parentheses are to avoid macro substitution funnies.             */
+/* Note: The LZRW3-A default is a value of (3).                               */
+/* Note: If you end up choosing a value of 0, you should use LZRW3 instead.   */
+/* Note: Changing the value of HASH_TABLE_DEPTH_BITS is the ONLY thing you    */
+/* have to do to change the depth, so go ahead and recompile now!             */
+/* Note: I have tested LZRW3-A for DEPTH_BITS=0,1,2,3,4 and a few other       */
+/* values. However, I have not tested it for 12 as I can't wait that long!    */
+#define HASH_TABLE_DEPTH_BITS (3)      /* Must be in range [0,12].            */
+
+/* The following definitions are all self-explanatory and follow from the     */
+/* definition of HASH_TABLE_DEPTH_BITS and the hardwired requirement that the */
+/* hash table contain exactly 4096 pointers.                                  */
+#define PARTITION_LENGTH_BITS (12-HASH_TABLE_DEPTH_BITS)
+#define PARTITION_LENGTH      (1<<PARTITION_LENGTH_BITS)
+#define HASH_TABLE_DEPTH      (1<<HASH_TABLE_DEPTH_BITS )
+#define HASH_MASK             (PARTITION_LENGTH-1)
+#define DEPTH_MASK            (HASH_TABLE_DEPTH-1)
+
+/* LZRW3-A, unlike LZRW1(-A), must initialize its hash table so as to enable  */
+/* the compressor and decompressor to stay in step maintaining identical hash */
+/* tables. In an early version of LZRW3, the tables were simply               */
+/* initialized to zero and a check for zero was included just before the      */
+/* matching code. However, this test costs time. A better solution is to      */
+/* initialize all the entries in the hash table to point to a constant        */
+/* string. The decompressor does the same. This solution requires no extra    */
+/* test. The contents of the string do not matter so long as the string is    */
+/* the same for the compressor and decompressor and contains at least         */
+/* MAX_RAW_ITEM bytes. I chose consecutive decimal digits because they do not */
+/* have white space problems (e.g. there is no chance that the compiler will  */
+/* replace more than one space by a TAB) and because they make the length of  */
+/* the string obvious by inspection.                                          */
+#define START_STRING_18 ((UBYTE *) "123456789012345678")
+
+/* The following macro accepts a pointer PTR to three consecutive bytes in    */
+/* memory and hashes them into an integer that is a hash table index that     */
+/* points to the zeroth (first) element of a partition. Thus, the hash        */
+/* function really hashes to a partition number but, for convenience,         */
+/* multiplies it up to yield a hash table index. From all this, we see that   */
+/* the resultant number is in the range [0,HASH_TABLE_LENGTH-1] and is a      */
+/* multiple of HASH_TABLE_DEPTH.                                              */
+/* A macro is used, because in LZRW3-A we have to hash more than once.        */
+#if 0
+#define HASH(PTR) \
+ ( \
+     (((40543*(((*(PTR))<<8)^((*((PTR)+1))<<4)^(*((PTR)+2))))>>4) & HASH_MASK) \
+  << HASH_TABLE_DEPTH_BITS \
+ )
+
+#else
+#define HASH(PTR) \
+ ( \
+     ( ((((*(PTR)<<3)^(*((PTR)+1)))<<3)^(*((PTR)+2))) & HASH_MASK) \
+  << HASH_TABLE_DEPTH_BITS \
+ )
+#endif
+
+/* Another operation that is performed more than once is the updating of the  */
+/* hash table. Here two macros are defined to simplify update operations.     */
+/* Updating consists of identifying and overwriting a pointer in a partition  */
+/* with a newer pointer and then updating the global cycle value.             */
+/* These macros accept the new pointer (NEWPTR) and either a pointer to       */
+/* (P_BASE) or the index of (I_BASE) the zeroth (first, or base) pointer in   */
+/* the partition that is to be updated. The macros use the 'cycle' variable   */
+/* to locate and overwrite a pointer and then update the cycle value.         */
+/* Note: Hardcoding 'cycle' in this macro is naughty (it should really be a   */
+/* macro parameter), but I have done so because it neatens up the code.       */
+#define UPDATE_P(P_BASE,NEWPTR) \
+{(P_BASE)[cycle++]=(NEWPTR); cycle&=DEPTH_MASK;}
+
+#define UPDATE_I(I_BASE,NEWPTR) \
+{hash[(I_BASE)+cycle++]=(NEWPTR); cycle&=DEPTH_MASK;}
+
+/* This constant supplies a legal (in-range) hash table index for use when    */
+/* a legal-but-don't-care index is required.                                  */
+#define ANY_HASH_INDEX (0)
+
+/******************************************************************************/
+#if 0
+void *ext2_lzrw3a_htab;
+#endif
+
+EXPORT int ext2_LZRW3A_compress
+           (UBYTE *p_src_first, UBYTE *p_dst_first, void *heap, int src_len,
+	    int p_dst_len)
+/* Input  : Specify input block using p_src_first and src_len.                */
+/* Input  : Point p_dst_first to the start of the output zone (OZ).           */
+/* Input  : Point p_dst_len to a ULONG to receive the output length.          */
+/* Input  : Input block and output zone must not overlap.                     */
+/* Output : Length of output block written to *p_dst_len.                     */
+/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. May   */
+/* Output : write in OZ=Mem[p_dst_first..p_dst_first+src_len+MAX_CMP_GROUP-1].*/
+/* Output : Upon completion *p_dst_len contains final size or 0               */
+{
+ /* p_src and p_dst step through the source and destination blocks.           */
+ UBYTE *p_src = p_src_first;
+ UBYTE *p_dst = p_dst_first;
+
+ /* The following variables are never modified and are used in the            */
+ /* calculations that determine when the main loop terminates.                */
+ UBYTE *p_src_post  = p_src_first+src_len;
+ UBYTE *p_dst_post  = p_dst_first+p_dst_len;
+ UBYTE *p_src_max1  = p_src_first+src_len-MAX_RAW_ITEM;
+ UBYTE *p_src_max16 = p_src_first+src_len-MAX_RAW_ITEM*16;
+
+ /* The variables 'p_control' and 'control' are used to buffer control bits.  */
+ /* Before each group is processed, the next two bytes of the output block    */
+ /* are set aside for the control word for the group about to be processed.   */
+ /* 'p_control' is set to point to the first byte of that word. Meanwhile,    */
+ /* 'control' buffers the control bits being generated during the processing  */
+ /* of the group. Instead of having a counter to keep track of how many items */
+ /* have been processed (=the number of bits in the control word), at the     */
+ /* start of each group, the top word of 'control' is filled with 1 bits.     */
+ /* As 'control' is shifted for each item, the 1 bits in the top word are     */
+ /* absorbed or destroyed. When they all run out (i.e. when the top word is   */
+ /* all zero bits, we know that we are at the end of a group.                 */
+ #define TOPWORD 0xFFFF0000
+ UBYTE *p_control;
+ ULONG control=TOPWORD;
+
+ /* The variable 'hash' always points to the first element of the hash table. */
+#if 0
+ UBYTE **hash= (UBYTE **) ext2_lzrw3a_htab;
+#else
+ UBYTE **hash= (UBYTE **) heap;
+#endif
+
+ /* The following two variables represent the literal buffer. p_h1 points to  */
+ /* the partition (i.e. the zero'th (first) element of the partition)         */
+ /* corresponding to the youngest literal. p_h2 points to the partition       */
+ /* corresponding to the second youngest literal.                             */
+ /* The value zero denotes an "empty" buffer value with p_h1=0 => p_h2=0.     */
+ UBYTE **p_h1=0;
+ UBYTE **p_h2=0;
+
+ /* The following variable holds the current 'cycle' value. This value cycles */
+ /* through the range [0,HASH_TABLE_DEPTH-1], being incremented every time    */
+ /* the hash table is updated. The value gives the within-partition number of */
+ /* the next pointer to be overwritten. The decompressor maintains a cycle    */
+ /* value in synchrony.                                                       */
+ UCARD cycle=0;
+
+ /* Reserve the first word of output as the control word for the first group. */
+ /* Note: This is undone at the end if the input block is empty.              */
+ p_control=p_dst; p_dst+=2;
+
+ /* Initialize all elements of the hash table to point to a constant string.  */
+ /* Use of an unrolled loop speeds this up considerably.                      */
+ /* These variables should really be declared "register", but I am worried    */
+ /* about the possibility that extra register declarations will tempt stupid  */
+ /* compilers to allocate all registers before they get to the innermostloop. */
+ {UCARD i; UBYTE **p_h=hash;
+  #define ZH *p_h++=START_STRING_18
+  for (i=0;i<256;i++)     /* 256=HASH_TABLE_LENGTH/16. */
+    {ZH;ZH;ZH;ZH;
+     ZH;ZH;ZH;ZH;
+     ZH;ZH;ZH;ZH;
+     ZH;ZH;ZH;ZH;}
+ }
+
+ /* The main loop processes either 1 or 16 items per iteration. As its        */
+ /* termination logic is complicated, I have opted for an infinite loop       */
+ /* structure containing 'break' and 'goto' statements.                       */
+ while (TRUE)
+   {/* Begin main processing loop. */
+
+    /* Note: All the variables here except unroll should be defined within    */
+    /*       the inner loop. Unfortunately the loop hasn't got a block.       */
+     UBYTE *p_ziv;              /* Points to first byte of current Ziv.       */
+     UCARD unroll;              /* Loop counter for unrolled inner loop.      */
+     UCARD index;               /* Index of current partition.                */
+     UBYTE **p_h0;              /* Pointer to current partition.              */
+     register UCARD d;          /* Depth looping variable.                    */
+     register UCARD bestlen;    /* Holds the best length seen so far.         */
+     register UCARD bestpos;    /* Holds number of best pointer seen so far.  */
+
+    /* Test for overrun and jump to overrun code if necessary.                */
+    if (p_dst>p_dst_post)
+       goto overrun;
+
+    /* The following cascade of if statements efficiently catches and deals   */
+    /* with varying degrees of closeness to the end of the input block.       */
+    /* When we get very close to the end, we stop updating the table and      */
+    /* code the remaining bytes as literals. This makes the code simpler.     */
+    unroll=16;
+    if (p_src>p_src_max16)
+      {
+       unroll=1;
+       if (p_src>p_src_max1)
+         {
+          if (p_src==p_src_post)
+             break;
+          else
+             {p_h0=&hash[ANY_HASH_INDEX]; /* Avoid undefined pointer. */
+              goto literal;}
+         }
+      }
+
+    /* This inner unrolled loop processes 'unroll' (whose value is either 1   */
+    /* or 16) items. I have chosen to implement this loop with labels and     */
+    /* gotos to heighten the ease with which the loop may be implemented with */
+    /* a single decrement and branch instruction in assembly language and     */
+    /* also because the labels act as highly readable place markers.          */
+    /* (Also because we jump into the loop for endgame literals (see above)). */
+
+    begin_unrolled_loop:
+
+       p_ziv=p_src;
+
+       /* To process the next phrase, we hash the next three bytes to obtain  */
+       /* an index to the zeroth (first) pointer in a target partition. We    */
+       /* get the pointer.                                                    */
+       index=HASH(p_src);
+       p_h0=&hash[index];
+
+       /* This next part runs through the pointers in the partition matching  */
+       /* the bytes they point to in the Lempel with the bytes in the Ziv.    */
+       /* The length (bestlen) and within-partition pointer number (bestpos)  */
+       /* of the longest match so far is maintained and is the output of this */
+       /* segment of code. The s[bestlen]==... is an optimization only.       */
+       bestlen=0;
+       bestpos=0;
+       for (d=0;d<HASH_TABLE_DEPTH;d++)
+         {
+          register UBYTE *s=p_src;
+          register UBYTE *p=p_h0[d];
+          register UCARD len;
+          if (s[bestlen] == p[bestlen])
+            {
+             #define PS *p++!=*s++
+             (void) (PS || PS || PS || PS || PS || PS || PS || PS || PS ||
+                     PS || PS || PS || PS || PS || PS || PS || PS || PS || s++);
+             len=s-p_src-1;
+             if (len>bestlen)
+               {
+                bestpos=d;
+                bestlen=len;
+               }
+            }
+         }
+
+       /* The length of the longest match determines whether we code a */
+       /* literal item or a copy item.                                 */
+
+       if (bestlen<3)
+         {
+          /* Literal. */
+
+          /* Code the literal byte as itself and a zero control bit.          */
+          literal: *p_dst++=*p_src++; control&=0xFFFEFFFF;
+
+          /* We have just coded a literal. If we had two pending ones, that   */
+          /* makes three and we can update the hash table.                    */
+          if (p_h2!=0)
+             {UPDATE_P(p_h2,p_ziv-2);}
+
+          /* In any case, rotate the hash table pointers for next time. */
+          p_h2=p_h1; p_h1=p_h0;
+
+         }
+       else
+         {
+          /* Copy */
+
+          /* To code a copy item, we construct a hash table index of the      */
+          /* winning pointer (index+=bestpos) and code it and the best length */
+          /* into a 2 byte code word. Bump up p_src.                          */
+          index+=bestpos;
+          *p_dst++=((index&0xF00)>>4)|(bestlen-3);
+          *p_dst++=index&0xFF;
+          p_src+=bestlen;
+
+          /* As we have just coded three bytes, we are now in a position to   */
+          /* update the hash table with the literal bytes that were pending   */
+          /* upon the arrival of extra context bytes.                         */
+          if (p_h1!=0)
+            {
+             if (p_h2!=0)
+               {UPDATE_P(p_h2,p_ziv-2); p_h2=0;}
+             UPDATE_P(p_h1,p_ziv-1); p_h1=0;
+            }
+
+          /* In any case, we can update the hash table based on the current   */
+          /* position as we just coded at least three bytes in a copy items.  */
+          UPDATE_P(p_h0,p_ziv);
+         }
+       control>>=1;
+
+       /* This loop is all set up for a decrement and jump instruction! */
+    if (--unroll) goto begin_unrolled_loop;
+
+    /* At this point it will nearly always be the end of a group in which     */
+    /* case, we have to do some control-word processing. However, near the    */
+    /* end of the input block, the inner unrolled loop is only executed once. */
+    /* This necessitates the 'if' test.                                       */
+    if ((control&TOPWORD)==0)
+      {
+       /* Write the control word to the place we saved for it in the output. */
+       *p_control++=  control     &0xFF;
+       *p_control  = (control>>8) &0xFF;
+
+       /* Reserve the next word in the output block for the control word */
+       /* for the group about to be processed.                           */
+       p_control=p_dst; p_dst+=2;
+
+       /* Reset the control bits buffer. */
+       control=TOPWORD;
+      }
+
+   } /* End main processing loop. */
+
+ /* After the main processing loop has executed, all the input bytes have     */
+ /* been processed. However, the control word has still to be written to the  */
+ /* word reserved for it in the output at the start of the most recent group. */
+ /* Before writing, the control word has to be shifted so that all the bits   */
+ /* are in the right place. The "empty" bit positions are filled with 1s      */
+ /* which partially fill the top word.                                        */
+ while(control&TOPWORD) control>>=1;
+ *p_control++= control     &0xFF;
+ *p_control++=(control>>8) &0xFF;
+
+ /* If the last group contained no items, delete the control word too.        */
+ if (p_control==p_dst) p_dst-=2;
+
+ /* Write the length of the output block to the dst_len parameter and return. */
+ return p_dst-p_dst_first;
+
+ /* Jump here as soon as an overrun is detected. An overrun is defined to     */
+ /* have occurred if p_dst>p_dst_first+*p_dst_len as set by caller. */
+ /* The algorithm checks for overruns at least at the end of each group       */
+ /* which means that the maximum overrun is MAX_CMP_GROUP bytes.              */
+ /* Once an overrun occurs, the only thing to do is to return an error        */
+ overrun:
+ return 0;
+}
+
+/******************************************************************************/
+
+#if 0
+EXPORT int ext2_LZRW3A_decompress
+           (UBYTE *p_src_first, UBYTE *p_dst, int src_len, int dst_len)
+/* Input  : Specify input block using p_src_first.                */
+/* Input  : Point p_dst to the start of the output zone.                */
+/* Input  : dst_len contains output length */
+/* Input  : Input block and output zone must not overlap. User knows          */
+/* Input  : upperbound on output block length from earlier compression.       */
+/* Input  : In any case, maximum expansion possible is nine times.            */
+/* Input  : src_len contains max input length */
+/* Output : Writes only  in Mem[p_dst..p_dst+dst_len-1].       */
+{
+ UBYTE *p_dst_first = p_dst;
+
+ /* Byte pointers scan through the input and output blocks.   */
+ register UBYTE *p_src = p_src_first;
+
+ /* The following two variables are never modified and are used to control    */
+ /* the main loop.                                                            */
+ UBYTE *p_dst_post  = p_dst+dst_len;
+ UBYTE *p_dst_max16 = p_dst+dst_len-MAX_RAW_ITEM*16;
+
+ /* The hash table is the only resident of the working memory. The hash table */
+ /* contains HASH_TABLE_LENGTH=4096 pointers to positions in the history. To  */
+ /* keep Macintoshes happy, it is longword aligned.                           */
+ UBYTE **hash = (UBYTE **) ext2_lzrw3a_htab;
+
+ /* The variable 'control' is used to buffer the control bits which appear in */
+ /* groups of 16 bits (control words) at the start of each compressed group.  */
+ /* When each group is read, bit 16 of the register is set to one. Whenever   */
+ /* a new bit is needed, the register is shifted right. When the value of the */
+ /* register becomes 1, we know that we have reached the end of a group.      */
+ /* Initializing the register to 1 thus instructs the code to follow that it  */
+ /* should read a new control word immediately.                               */
+ register ULONG control=1;
+
+ /* The value of 'literals' is always in the range 0..3. It is the number of  */
+ /* consecutive literal items just seen. We have to record this number so as  */
+ /* to know when to update the hash table. When literals gets to 3, there     */
+ /* have been three consecutive literals and we can update at the position of */
+ /* the oldest of the three.                                                  */
+ register UCARD literals=0;
+
+ /* The following variable holds the current 'cycle' value. This value cycles */
+ /* through the range [0,HASH_TABLE_DEPTH-1], being incremented every time    */
+ /* the hash table is updated. The value give the within-partition number of  */
+ /* the next pointer to be overwritten. The compressor maintains a cycle      */
+ /* value in synchrony.                                                       */
+ UCARD cycle=0;
+
+ /* Initialize all elements of the hash table to point to a constant string.  */
+ /* Use of an unrolled loop speeds this up considerably.                      */
+ /* The comment about register declarations above similar code in the         */
+ /* compressor applies here too.                                              */
+ {UCARD i; UBYTE **p_h=hash;
+  #define ZJ *p_h++=START_STRING_18
+  for (i=0;i<256;i++)     /* 256=HASH_TABLE_LENGTH/16. */
+    {ZJ;ZJ;ZJ;ZJ;
+     ZJ;ZJ;ZJ;ZJ;
+     ZJ;ZJ;ZJ;ZJ;
+     ZJ;ZJ;ZJ;ZJ;}
+ }
+
+ /* The outer loop processes either 1 or 16 items per iteration depending on  */
+ /* how close p_dst is to the end of the output block.                         */
+ while (p_dst < p_dst_post && src_len >= 0)
+   {/* Start of outer loop */
+
+    register UCARD unroll;   /* Counts unrolled loop executions.              */
+
+    /* When 'control' has the value 1, it means that the 16 buffered control  */
+    /* bits that were read in at the start of the current group have all been */
+    /* shifted out and that all that is left is the 1 bit that was injected   */
+    /* into bit 16 at the start of the current group. When we reach the end   */
+    /* of a group, we have to load a new control word and inject a new 1 bit. */
+    if (control==1)
+      {
+       control=0x10000|*p_src++;
+       control|=(*p_src++)<<8;
+       src_len -= 2;
+      }
+
+    /* If it is possible that we are within 16 groups from the end of the     */
+    /* input, execute the unrolled loop only once, else process a whole group */
+    /* of 16 items by looping 16 times.                                       */
+    unroll= p_dst<=p_dst_max16 ? 16 : 1;
+
+    /* This inner loop processes one phrase (item) per iteration. */
+    while (unroll--)
+      { /* Begin unrolled inner loop. */
+
+       /* Process a literal or copy item depending on the next control bit. */
+       if (control&1)
+         {
+          /* Copy item. */
+
+          register UBYTE *p;           /* Points to place from which to copy. */
+          register UCARD lenmt;        /* Length of copy item minus three.    */
+          register UBYTE *p_ziv=p_dst; /* Pointer to start of current Ziv.    */
+          register UCARD index;        /* Index of hash table copy pointer.   */
+
+          /* Read and dismantle the copy word. Work out from where to copy.   */
+          lenmt=*p_src++;
+          index=((lenmt&0xF0)<<4)|*p_src++;
+	  src_len -= 2;
+          p=hash[index];
+          lenmt&=0xF;
+
+          /* Now perform the copy using a half unrolled loop. */
+          *p_dst++=*p++;
+          *p_dst++=*p++;
+          *p_dst++=*p++;
+          while (lenmt--)
+             *p_dst++=*p++;
+
+          /* Because we have just received 3 or more bytes in a copy item     */
+          /* (whose bytes we have just installed in the output), we are now   */
+          /* in a position to flush all the pending literal hashings that had */
+          /* been postponed for lack of bytes.                                */
+          if (literals>0)
+            {
+             register UBYTE *r=p_ziv-literals;;
+             UPDATE_I(HASH(r),r);
+             if (literals==2)
+                {r++; UPDATE_I(HASH(r),r);}
+             literals=0;
+            }
+
+          /* In any case, we can immediately update the hash table with the   */
+          /* current position. We don't need to do a HASH(...) to work out    */
+          /* where to put the pointer, as the compressor just told us!!!      */
+          UPDATE_I(index&(~DEPTH_MASK),p_ziv);
+         }
+       else
+         {
+          /* Literal item. */
+
+          /* Copy over the literal byte. */
+          *p_dst++=*p_src++;
+	  src_len--;
+
+          /* If we now have three literals waiting to be hashed into the hash */
+          /* table, we can do one of them now (because there are three).      */
+          if (++literals == 3)
+             {register UBYTE *p=p_dst-3;
+              UPDATE_I(HASH(p),p); literals=2;}
+         }
+
+       /* Shift the control buffer so the next control bit is in bit 0. */
+       control>>=1;
+
+      } /* End unrolled inner loop. */
+
+   } /* End of outer loop */
+
+#if 0
+   if(src_len < 0) return 0;
+   return (p_src-p_src_first);
+#else
+   return p_dst - p_dst_first;
+#endif
+}
+#endif
+
+/******************************************************************************/
+/*                              End of LZRW3-A.C                              */
+/******************************************************************************/
--- linux-2.6.25/fs/ext2/debug.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/debug.h	2008-08-01 16:00:24.000000000 +0200
@@ -0,0 +1,40 @@
+/* This file contains support for assert() and
+   contains the various debugging parameters.
+
+   Previously EXT2_COMPR_REPORT was in ext2_fs.h, but changing that file
+   causes most of the kernel to be rebuilt during subsequent compiles. */
+
+/* At some stage it would be nice to have a CONFIG_ option 
+   for the amount of time to spend catching bugs.  It would be
+   an int from 0 through 100, where 0 means `don't do any assert's
+   at all', n means `try not to spend more than n% of total system
+   (kernel) time in debugging'. */
+
+#undef  NDEBUG /* fixme: #define this for release */
+#include "assert.h"
+
+/* fs_assert: Like assert(), but truth of expression depends on
+   integrity of fs as well as on program logic. */
+#define fs_assert(_x) assert(_x)
+
+#undef  EXT2_COMPR_DEBUG
+#undef  EXT2_COMPR_REPORT
+
+//#define EXT2_COMPR_DEBUG
+//#define EXT2_COMPR_REPORT
+//#define EXT2_COMPR_REPORT_VERBOSE
+//#define EXT2_COMPR_REPORT_PUT
+//# define EXT2_COMPR_REPORT_FILEOPEN
+//#define EXT2_COMPR_REPORT_MUTEX
+
+
+#ifdef  EXT2_COMPR_REPORT
+//# define EXT2_COMPR_REPORT_PUT
+//# define EXT2_COMPR_REPORT_WA
+//# define EXT2_COMPR_REPORT_MUTEX
+//# define EXT2_COMPR_REPORT_ALLOC  /* disk allocation etc. */
+//# define EXT2_COMPR_REPORT_ALGORITHMS /* Compression algorithms */
+//# define EXT2_COMPR_REPORT_VERBOSE /* Various things I don't think
+//				      useful at the moment. */
+//#define EXT2_COMPR_REPORT_VERBOSE_INODE
+#endif
--- linux-2.6.25/fs/ext2/inode.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/ext2/inode.c	2008-08-01 16:10:33.000000000 +0200
@@ -22,6 +22,24 @@
  *  Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000
  */
 
+/*
+ *  Copyright (C) 1995  Antoine Dumesnil de Maricourt (dumesnil@etca.fr)
+ *    (transparent compression code)
+ */
+
+/*
+ *  Copyright (C) 2001 Alcatel Business Systems - R&D Illkirch FRANCE
+ *
+ *    Transparent compression code for 2.4 kernel.
+ *
+ *  Denis Richard (denis.richard@sxb.bsf.alcatel.fr)
+ *  Pierre Peiffer (pierre.peiffer@sxb.bsf.alcatel.fr)
+ *
+ *  Adapted from patch e2compr-0.4.39-patch-2.2.18 .
+ */
+
+
+#include <linux/kmod.h>
 #include <linux/smp_lock.h>
 #include <linux/time.h>
 #include <linux/highuid.h>
@@ -31,16 +49,52 @@
 #include <linux/writeback.h>
 #include <linux/buffer_head.h>
 #include <linux/mpage.h>
-#include "ext2.h"
+#include <linux/ext2_fs_c.h>
+#include <linux/spinlock.h>
+
 #include "acl.h"
 #include "xip.h"
+#include "debug.h"
 
+#ifdef CONFIG_EXT2_COMPRESS
+MODULE_AUTHOR("Remy Card and others; compression extension by Antoine Dumesnil de Maricourt, Peter Moulder, and others");
+MODULE_DESCRIPTION("Second Extended Filesystem with transparent compression");
+#else
 MODULE_AUTHOR("Remy Card and others");
 MODULE_DESCRIPTION("Second Extended Filesystem");
+#endif
 MODULE_LICENSE("GPL");
 
+
 static int ext2_update_inode(struct inode * inode, int do_sync);
 
+#ifdef CONFIG_EXT2_COMPRESS
+/* mw: this function counts all references 
+ * to this inode. this is necessary to
+ * refuse un/compression if the file has
+ * more than one refernce, I guess. */ 
+int ext2_get_dcount(struct inode *inode)
+{        
+	struct dentry *dentry;
+    	struct list_head *head, *next, *tmp;
+    	int count;
+    	
+    	head = &inode->i_dentry;
+    	next = inode->i_dentry.next;
+    	count = 0;
+    	while (next != head) {
+    		dentry = list_entry(next, struct dentry, d_alias);
+    		tmp = next;
+    		next = tmp->next;
+    		count += atomic_read(&dentry->d_count);
+    		//mw: similar to fs/dcache.c
+    	}
+        
+        return count;
+}
+#endif
+
+
 /*
  * Test whether an inode is a fast symlink.
  */
@@ -58,6 +112,9 @@ static inline int ext2_inode_is_fast_sym
  */
 void ext2_delete_inode (struct inode * inode)
 {
+	//mw: seems to be necessary for repeated "lilo" calls
+	//mutex_lock(&inode->i_mutex);
+
 	truncate_inode_pages(&inode->i_data, 0);
 
 	if (is_bad_inode(inode))
@@ -67,13 +124,19 @@ void ext2_delete_inode (struct inode * i
 	ext2_update_inode(inode, inode_needs_sync(inode));
 
 	inode->i_size = 0;
-	if (inode->i_blocks)
+	if (inode->i_blocks){
 		ext2_truncate (inode);
+	}
 	ext2_free_inode (inode);
+	//mw
+	//mutex_unlock(&inode->i_mutex); 
 
 	return;
 no_delete:
 	clear_inode(inode);	/* We must guarantee clearing of inode... */
+	
+	//mw
+	//mutex_unlock(&inode->i_mutex); 
 }
 
 typedef struct {
@@ -207,7 +270,7 @@ static Indirect *ext2_get_branch(struct 
 	*err = 0;
 	/* i_data is not going away, no lock needed */
 	add_chain (chain, NULL, EXT2_I(inode)->i_data + *offsets);
-	if (!p->key)
+	if (HOLE_BLKADDR(p->key))
 		goto no_block;
 	while (--depth) {
 		bh = sb_bread(sb, le32_to_cpu(p->key));
@@ -218,7 +281,7 @@ static Indirect *ext2_get_branch(struct 
 			goto changed;
 		add_chain(++p, bh, (__le32*)bh->b_data + *++offsets);
 		read_unlock(&EXT2_I(inode)->i_meta_lock);
-		if (!p->key)
+		if (HOLE_BLKADDR(p->key))
 			goto no_block;
 	}
 	return NULL;
@@ -264,7 +327,7 @@ static unsigned long ext2_find_near(stru
 
 	/* Try to find previous block */
 	for (p = ind->p - 1; p >= start; p--)
-		if (*p)
+		if (!HOLE_BLKADDR(*p))
 			return le32_to_cpu(*p);
 
 	/* No such thing, so let's try location of indirect block */
@@ -464,7 +527,8 @@ static int ext2_alloc_branch(struct inod
 		 */
 		bh = sb_getblk(inode->i_sb, new_blocks[n-1]);
 		branch[n].bh = bh;
-		lock_buffer(bh);
+ 		if (!buffer_uptodate(bh))
+ 			wait_on_buffer(bh);
 		memset(bh->b_data, 0, blocksize);
 		branch[n].p = (__le32 *) bh->b_data + offsets[n];
 		branch[n].key = cpu_to_le32(new_blocks[n]);
@@ -480,7 +544,6 @@ static int ext2_alloc_branch(struct inod
 				*(branch[n].p + i) = cpu_to_le32(++current_block);
 		}
 		set_buffer_uptodate(bh);
-		unlock_buffer(bh);
 		mark_buffer_dirty_inode(bh, inode);
 		/* We used to sync bh here if IS_SYNC(inode).
 		 * But we now rely upon generic_osync_inode()
@@ -686,7 +749,8 @@ cleanup:
 	return err;
 changed:
 	while (partial > chain) {
-		brelse(partial->bh);
+ 		bforget(partial->bh);
+//		brelse(partial->bh); /*mw: this is generic ext2 code */
 		partial--;
 	}
 	goto reread;
@@ -705,21 +769,612 @@ int ext2_get_block(struct inode *inode, 
 
 }
 
+#ifdef CONFIG_EXT2_COMPRESS
+/*
+ *    Readpage method that will take care of decompression.
+ */
+/* effic: I (pjm) think tht at present, reading a 32KB cluster 4KB at
+   a time does `decompress 4KB' for the first 4KB, then `decompress
+   8KB' for the second, and so on.  See if we can provide the page
+   cache with all the pages in a cluster.  The problem is, we don't
+   want to erase anything tht hasn't been written to disk, so we can't
+   just call update_vm_cache().  The plan at present is to remember
+   what the contents of ext2_rd_wa.u come from, and don't bother
+   decompressing anything if the working area already contains the
+   right data.  However, this is only a win where adjacent calls to
+   ext2_decompress_blocks() request the same cluster.  We could force
+   that by copying some code from generic_file_read() (but check for
+   deadlocks before doing anything like that), but instead I'm taking
+   the more passive approach of hoping for the best. */
+static int ext2_readpage(struct file *file, struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	struct page *pg[EXT2_MAX_CLUSTER_PAGES], *epg[EXT2_MAX_CLUSTER_PAGES];
+	u32 cluster0, max_cluster;
+	int i, blockOfCluster, blocksToDo, npg;
+	const int inc = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
+	struct ext2_inode_info *ei = EXT2_I(page->mapping->host);
+#ifdef CONFIG_HIGHMEM	
+	int kmapped = 0; //mw
+#endif	
+	
+	//struct address_space myspace_extra;
+	int iClusterCnt;
+
+	/* For directories, fall out through default routine */
+	if (S_ISDIR(inode->i_mode))
+		return block_read_full_page(page,ext2_get_block);
+
+	/* The semaphore prevents us trying to compress and decompress
+	   the cluster at the same time, or compress a cluster in the
+	   middle of reading it (thinking it to be uncompressed).
+
+	   You may not like the fact that we hold the semaphore across
+	   readpage (given that it isn't held without e2compr compiled
+	   in), but it does guarantee that we won't compress the
+	   cluster during readpage.  (OTOH, it's unlikely, if not
+	   impossible, for someone to ,compress a cluster and rewrite
+	   the blocks` before the readpage completes.) */
+	/* This procedure used to have `#ifndef EXT2_LOCK_BUFFERS'
+	   around all the semaphore stuff, and unlocked each buffer
+	   before brelsing them ifdef EXT2_LOCK_BUFFERS.  I (pjm,
+	   1998-01-20) have removed that because (a) EXT2_LOCK_BUFFERS
+	   isn't #defined anywhere, and doesn't appear outside of this
+	   function, and (b) I haven't looked at what effect locking
+	   the buffers has.  You may like to reintroduce the idea of
+	   buffer locking to this function if you're more familiar
+	   with buffer locking than I, and believe that the full i_sem
+	   isn't necessary to protect from races (people seeing raw
+	   compressed data) between readpage and ext2_file_write(),
+	   ext2_compress_cluster() and ext2_truncate(). */
+	unlock_page(page);
+	mutex_lock(&inode->i_mutex);
+	
+	assert (atomic_read(&inode->i_mutex.count) <= 0); /* i.e. mutex_lock */
+	
+	//mw: added EXT2_COMPR_FL, because EXT2_COMPRBLK_FL mit change without mutex !!!
+	if ( !(ei->i_flags & (EXT2_COMPRBLK_FL|EXT2_COMPR_FL))  
+		||  (ei->i_flags & EXT2_NOCOMPR_FL) )
+	{
+		goto readpage_uncompressed;
+	}
+
+	{
+		register u32 blockOfFile
+		    = (page->index << PAGE_CACHE_SHIFT) >> inode->i_sb->s_blocksize_bits;
+
+		blocksToDo = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
+		cluster0 = ext2_block_to_cluster(inode, blockOfFile);
+		max_cluster = ext2_block_to_cluster
+		    (inode, blockOfFile + blocksToDo - 1);
+		blockOfCluster
+		    = blockOfFile - ext2_cluster_block0(inode, cluster0);
+	}
+
+	/* return -???, any idea which code. do_generic_file_read() cares, ext2_readpages() doesn't. 
+	   maybe I should look at the "generic" readpage() and see what it returns in this case */
+
+	/* Check if any part of the requested area contains part of a
+	   compressed cluster.  If not, we can use default ext2_readpage().
+
+	   (Note that we don't have to worry about a cluster becoming
+	   compressed in the meantime, because we have the semaphore.)
+
+	   A page can cover up to 9 clusters.  (The maximum can only
+	   occur with 32KB pages, 4KB clusters, and a non-page-aligned
+	   offset.  Thanks go to Kurt Fitzner for reporting that
+	   page offsets needn't be aligned; see generic_file_mmap().)  */
+	/* */
+	{
+	int isCmp[(PAGE_SIZE >> 12) + 1];
+	u8 *dst;
+	unsigned clu_ix;
+
+	assert (max_cluster - cluster0 < sizeof(isCmp)/sizeof(*isCmp));
+	for (clu_ix = 0; cluster0 + clu_ix <= max_cluster; clu_ix++) {
+		isCmp[clu_ix] = ext2_cluster_is_compressed_fn (inode, cluster0 + clu_ix);
+		if (isCmp[clu_ix] < 0){
+			printk("IO-ERROR: isCmp\n");
+			goto io_error;
+		}
+	} 
+	
+	for (clu_ix = 0; cluster0 + clu_ix <= max_cluster; clu_ix++)
+		if (isCmp[clu_ix] > 0)
+			goto readpage_compressed;
+	/* fall through */
+	readpage_uncompressed:
+	{
+		int rc=0;
+		lock_page(page);
+
+		/* Did somebody else fill it already? */
+		if (PageUptodate(page) ){  //mw: necessary for DEBUG! anyway checked in do_generic_mapping_read
+			unlock_page(page);
+		}
+		else {
+			//try_to_free_buffers(page);
+			rc = block_read_full_page(page,ext2_get_block);
+		}
+		mutex_unlock(&inode->i_mutex);
+		
+		return rc;
+	}
+
+	readpage_compressed:
+	if (ext2_rd_wa == NULL)
+	{
+		printk("NO WA!\n");
+		goto io_error;
+	}
+	
+
+	/* Copied from block_read_full_page */
+/*	if (!PageLocked(page)) */
+/*	PAGE_BUG(page); */
+	lock_page(page);
+	if (PageUptodate(page)) { 
+		unlock_page(page);
+		mutex_unlock(&inode->i_mutex);
+		return(0);
+	}
+	get_page(page);
+
+	ClearPageUptodate(page);
+	ClearPageError(page);
+
+	dst = (u8 *) page_address(page);
+	for (clu_ix = 0; cluster0 + clu_ix <= max_cluster; clu_ix++) {
+		struct buffer_head *bh[EXT2_MAX_CLUSTER_BLOCKS];
+		int nbh, blocksThisClu;
+		
+		for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+			pg[i] = NULL;
+			epg[i] = NULL;
+		}
+			
+/*		clear_bit(PG_locked, &page->flags); */
+		npg = ext2_cluster_npages(inode, cluster0 + clu_ix);
+		nbh = ext2_get_cluster_pages(inode, cluster0 + clu_ix, pg, page, 0);
+		
+		if (nbh <= 0) {
+			for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++)
+			printk("no pages\n");
+			goto out;
+		}		
+		iClusterCnt =  ext2_cluster_npages(inode, cluster0);
+		
+		nbh =  ext2_get_cluster_extra_pages(inode, cluster0 + clu_ix, pg, epg);
+		if (nbh <= 0) 
+		{
+			for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++)
+				epg[i] = NULL;
+			printk("no extra pages\n");
+			goto out;
+		}
+		assert (iClusterCnt = ext2_cluster_npages(inode, cluster0));
+		
+#ifdef CONFIG_HIGHMEM
+		ext2_kmap_cluster_pages(page, pg, epg);
+		kmapped = 1;
+#endif
+		
+		nbh = ext2_get_cluster_blocks(inode, cluster0 + clu_ix, bh, pg, epg, 0);
+		if (nbh <= 0)
+		{
+			printk("no blocks\n");
+			goto out;
+		}	
+			
+		/* How many blocks (including holes) we need from this cluster. */
+		{
+			blocksThisClu = (ext2_cluster_nblocks(inode, cluster0 +
+			    clu_ix) - blockOfCluster);
+			if (blocksThisClu > blocksToDo)
+				blocksThisClu = blocksToDo;
+		}
+
+		if (isCmp[clu_ix]) {
+			u8 const *src;
+			int n, nbytes_wanted;
+			struct ext2_cluster_head *head;
+			unsigned meth;
+# ifdef CONFIG_KMOD
+			unsigned alg;
+# endif
+
+			bh[0]->b_data = page_address(bh[0]->b_page);
+            head = (struct ext2_cluster_head *) bh[0]->b_data;
+
+			/* jmr 1998-10-28 Hope this is the last time I'm moving this code.
+			 * Module loading must be done _before_ we lock wa, just think what
+			 * can happen if we reallocate wa when somebody else uses it...
+			 */
+			meth = head->method; /* only a byte, so no swabbing needed. */
+			if (meth >= EXT2_N_METHODS) {
+				printk("illegal method id\n");
+				ext2_warning(inode->i_sb,
+				    "illegal method id",
+				    "inode = %lu, id = %u",
+				    inode->i_ino, meth);
+				goto out;
+			}
+# ifdef CONFIG_KMOD
+			alg = ext2_method_table[meth].alg;
+			if (!ext2_algorithm_table[alg].avail) {
+				char str[32];
+
+				sprintf(str, "ext2-compr-%s", ext2_algorithm_table[alg].name);
+				request_module(str);
+			}
+# endif /* CONFIG_KMOD */
+
+			ext2_lock_rd_wa_uninterruptible();
+
+			/* Calculate nbytes_wanted. */
+			{
+				unsigned nblk_wanted, i;
+
+				/* We want to decompress the whole cluster */
+				//nblk_wanted = ext2_cluster_nblocks(inode, cluster0 + clu_ix);
+				nblk_wanted = npg << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); //* mw: FIXED */
+				
+				for (i = nblk_wanted; i != 0;)
+					if (((--i >> 3) < head->holemap_nbytes)
+					    && (head->holemap[i >> 3] & (1 << (i & 7))))
+						--nblk_wanted;
+				nbytes_wanted = (nblk_wanted
+				    << inode->i_sb->s_blocksize_bits);
+			}
+
+			/* Decompress. */
+			n = ext2_decompress_blocks(inode, bh, nbh, nbytes_wanted, cluster0 + clu_ix);
+			if (n < 0) {
+				assert (nbh >= 0);
+				ext2_unlock_rd_wa();
+				printk("noblocks decompressed\n");
+				goto out;
+			}
+			if ((nbytes_wanted >= le32_to_cpu(head->ulen))
+			    && (n >= nbytes_wanted)) {
+				assert (ext2_rd_wa_ucontents.ino == ~0ul);
+				ext2_rd_wa_ucontents.ino = inode->i_ino;
+				ext2_rd_wa_ucontents.dev = inode->i_rdev;
+				ext2_rd_wa_ucontents.cluster = cluster0 + clu_ix;
+			}
+
+# ifdef EXT2_COMPR_REPORT_VERBOSE_INODE
+			if (ei->i_flags & EXT2_COMPR_FL)
+				printk(KERN_DEBUG "ext2: mmap %04x:%lu: blocksToDo=%d, blockOfCluster=%d, blocksThisClu=%d, clu_nblocks=%d\n",
+				    inode->i_rdev,
+				    inode->i_ino,
+				    blocksToDo,
+				    blockOfCluster,
+				    blocksThisClu,
+				    ext2_cluster_nblocks(inode, cluster0 + clu_ix));
+# endif
+
+			/* */
+			{
+			unsigned i;
+			int ipg;
+			
+			i = ext2_cluster_nblocks(inode, cluster0 + clu_ix) - 1;
+			//i = (npg << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits)) - 1; /*mw: FIXED!!!  (here: shift = 2Bit) */
+			//if(i+1 != ext2_cluster_nblocks(inode, cluster0 + clu_ix))
+			//printk("npg=%i, nbh=%i, npgf=%i, nbhf =%i, cluster:%i, dec_blk:%i, b_wanted:%i, size:%i\n ", ext2_cluster_npages(inode, cluster0 + clu_ix),  ext2_cluster_nblocks(inode, cluster0 + clu_ix), npgtest, i+1, cluster0 + clu_ix, n, nbytes_wanted, inode->i_size);
+			blockOfCluster = 0;
+			assert(n > 0);
+			src = ext2_rd_wa->u + nbytes_wanted - inode->i_sb->s_blocksize;
+#ifdef  EXT2_COMPR_REPORT			
+			trace_e2c("ext2_readpage: copy data inc=%d blocksThisClu=%d, n=%d\n", inc, blocksThisClu, n);
+#endif			
+			for (ipg = npg - 1; ipg >= 0; ipg--) {
+				if (pg[ipg] == NULL) {
+				i -= inc;
+				src -= PAGE_SIZE;
+				continue;
+			}
+			if (((inode->i_size-1) >> PAGE_SHIFT) ==  pg[ipg]->index) {
+				n = ((inode->i_size-1) & (PAGE_SIZE -1)) >> inode->i_sb->s_blocksize_bits;
+				i -= ((blocksThisClu-1) - n);
+				src -= ((blocksThisClu-1) - n) << inode->i_sb->s_blocksize_bits;
+			} else {
+				n = blocksThisClu - 1;
+			}
+			if (PageUptodate(pg[ipg])  ) {
+				for (;n >= 0;n--, i--) {
+					if (((i >> 3) >= head->holemap_nbytes)
+					    || !(head->holemap[i >> 3] & (1 << (i & 7)))) {
+						src -= inode->i_sb->s_blocksize;
+					}
+				}
+			} else {
+                
+				dst = (u8 *) page_address(pg[ipg]) + (n << inode->i_sb->s_blocksize_bits);
+				
+				for (;
+				    n >= 0;
+				    n--, i--, dst -= inode->i_sb->s_blocksize) {
+					assert(!buffer_dirty(bh[i]));
+					clear_buffer_dirty(bh[i]);   //mw: had a refile_buffer in 2.4 
+					if (((i >> 3) >= head->holemap_nbytes)
+					    || !(head->holemap[i >> 3] & (1 << (i & 7)))) {
+						assert(i >= 0);
+						memcpy(dst, src, inode->i_sb->s_blocksize);
+						src -= inode->i_sb->s_blocksize;
+					} else {
+						assert(i >= 0);
+						memset (dst, 0, inode->i_sb->s_blocksize);
+					}
+					//clear_bit(BH_Uptodate, &bh[i]->b_state);
+ 				}
+				SetPageUptodate(pg[ipg]);
+			}
+			} 
+			}
+			ext2_unlock_rd_wa();          
+		} else {
+			/* Uncompressed cluster.  Just copy the data.  */
+			int n;
+
+# ifdef EXT2_COMPR_REPORT_VERBOSE_INODE
+			if (ei->i_flags & EXT2_COMPR_FL)
+				printk(KERN_DEBUG
+				    "ext2: mmap %lu: blocksToDo = %d, "
+				    "blockOfCluster = %d, clu_nblocks = %d\n",
+				    inode->i_ino, blocksToDo, blockOfCluster,
+				    ext2_cluster_nblocks(inode, cluster0 +
+				    clu_ix));
+# endif
+
+			for (n = 0;
+			    n < blocksThisClu;
+			    n++, dst += inode->i_sb->s_blocksize) {
+				if ((blockOfCluster + n < nbh)
+				    && (bh[blockOfCluster + n] != NULL))
+				{
+					memcpy(dst,
+					    bh[blockOfCluster + n]->b_data,
+					    inode->i_sb->s_blocksize);
+				}
+				else
+				{
+					memset(dst, 0, inode->i_sb->s_blocksize);
+				}
+			}
+			blockOfCluster = 0;
+		} // end uncompressed Cluster
+
+		blocksToDo -= blocksThisClu;
+
+#ifdef CONFIG_HIGHMEM
+		if (kmapped)
+			ext2_kunmap_cluster_pages(page, pg, epg);
+#endif
+
+		for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+			if (epg[i] != NULL) {
+				
+				ClearPageDirty(epg[i]);			
+				ClearPageUptodate(epg[i]);	
+				try_to_free_buffers(epg[i]);
+				unlock_page(epg[i]);
+				assert(page_count(epg[i]) <= 1);
+				page_cache_release(epg[i]);
+			}
+		}
+
+		for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+			if (pg[i] == NULL)
+				break;		
+			if (pg[i] == page)
+				continue;
+			unlock_page(pg[i]);
+			page_cache_release(pg[i]);
+		}		
+		//mw
+		assert (isCmp[clu_ix] == ext2_cluster_is_compressed_fn (inode, cluster0 + clu_ix));
+	} // end for-loop: Cluster	
+	} 
+
+	SetPageUptodate(page);		
+	unlock_page(page);
+	atomic_dec(&page->_count);
+	mutex_unlock(&inode->i_mutex);
+	return 0;
+	
+	out:
+
+#ifdef CONFIG_HIGHMEM
+	if (kmapped)
+		ext2_kunmap_cluster_pages(page, pg, epg);
+#endif
+
+	for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+		if (epg[i] != NULL) {
+			
+			ClearPageDirty(epg[i]);			
+			ClearPageUptodate(epg[i]);		
+			try_to_free_buffers(epg[i]);
+			unlock_page(epg[i]);
+			assert(page_count(epg[i]) <= 1);
+			page_cache_release(epg[i]);
+		}
+	}
+
+	for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+		if (pg[i] == NULL)
+			break;
+		if (pg[i] == page)
+			continue;
+		unlock_page(pg[i]);
+		page_cache_release(pg[i]);	
+	}
+ 	mutex_unlock(&inode->i_mutex);
+ 	return 0;
+
+	io_error:
+#ifdef CONFIG_HIGHMEM
+	if (kmapped)
+		ext2_kunmap_cluster_pages(page, pg, epg);
+#endif
+	SetPageError(page);
+	unlock_page(page);
+	atomic_dec(&page->_count);
+	mutex_unlock(&inode->i_mutex);
+	printk("Readpage: IOERROR\n");
+	return -EIO; /* it is tested in do_generic_file_read(), ...     */
+}
+#endif /* CONFIG_EXT2_COMPRESS */
+
 static int ext2_writepage(struct page *page, struct writeback_control *wbc)
 {
+/* mw (24/06/2008):
+ *         WRITEPAGE: this code was also in e2compr 2.4 and once removed by yaboo ding.
+ *         ext2_writepage() is also called for dirty pages. Usually we write using file_write() which 
+ *         wraps correctly to compressed files. BUT: a writeable memory map might
+ *         produce dirty pages, which will be written back normally. this should/might fail.
+ *         The following code should fix this bug, but this was not tested yet. 
+ */
+#ifdef CONFIG_EXT2_COMPRESS
+#undef USE_WRITEPAGE
+//#define USE_WRITEPAGE	
+#ifdef USE_WRITEPAGE	
+
+    struct ext2_inode_info *ei = EXT2_I(page->mapping->host);
+    int retval;
+
+    struct inode *inode = page->mapping->host;
+    u32 cluster0, max_cluster;                                        
+    int blocksToDo;                                              
+                                                                                 
+    unlock_page(page);  
+    //mw: do we need this ???
+    //if (!(ei->i_compr_flags & EXT2_OSYNC_INODE)) {          
+    /* trace_e2c("ext2_writepage: inode"); */                                          
+    mutex_lock(&inode->i_mutex);           
+    /* trace_e2c(" down\n"); */                                   
+    //}                                                                  
+    if (!(ei->i_flags & EXT2_COMPRBLK_FL)                       
+    	|| (ei->i_flags & EXT2_NOCOMPR_FL) )
+    {                               
+    	//mw: do we need this ???
+    	//if (!(ei->i_compr_flags & EXT2_OSYNC_INODE)) {           
+    		/* trace_e2c("ext2_writepage: inode up 1\n"); */                                 
+    		mutex_unlock(&inode->i_mutex);                                       
+    	//}
+    	lock_page(page);                                           
+    	return block_write_full_page(page, ext2_get_block, wbc);
+    }                                                                
+    /* */   
+    {                                                                                  
+    	register u32 blockOfFile                              
+        	= (page->index << PAGE_CACHE_SHIFT) >> inode->i_sb->s_blocksize_bits;
+                                                                        
+    	blocksToDo = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;      
+        cluster0 = ext2_block_to_cluster(inode, blockOfFile);          
+        max_cluster = ext2_block_to_cluster(inode, blockOfFile + blocksToDo - 1);      
+    }                                                             
+                                                                         
+    /* Check if any part of the requested area contains part of a                        
+       compressed cluster.  If not, we can use default ext2_writepage(). 
+       
+       (Note that we don't have to worry about a cluster becoming
+       compressed in the meantime, because we have the semaphore.)            
+                                                                              
+       A page can cover up to 9 clusters.  (The maximum can only              
+       occur with 32KB pages, 4KB clusters, and a non-page-aligned   
+       offset.  Thanks go to Kurt Fitzner for reporting that               
+       page offsets needn't be aligned; see generic_file_mmap().)  */                  
+                                                                                            
+    {                                                                   
+    	int isCmp[(PAGE_SIZE >> 12) + 1];                                 
+        unsigned clu_ix;                                          
+                                                                     
+        assert (max_cluster - cluster0 < sizeof(isCmp)/sizeof(*isCmp));
+        for (clu_ix = 0; cluster0 + clu_ix <= max_cluster; clu_ix++) { 
+        	isCmp[clu_ix] = ext2_cluster_is_compressed_fn (inode, cluster0 + clu_ix);     
+            if (isCmp[clu_ix] < 0) {                                            
+            	//mw: do we need this ???if (!(ei->i_compr_flags & EXT2_OSYNC_INODE)) {  
+            		/* trace_e2c("ext2_writepage: inode up 2\n"); */
+            		lock_page(page);
+                	mutex_unlock(&inode->i_mutex);                                            
+                //}                                                               
+                return -EIO;                                                    
+            }                                                     
+         }
+        
+         for (clu_ix = 0; cluster0 + clu_ix <= max_cluster; clu_ix++)   
+        	if (isCmp[clu_ix] > 0)                                                     
+                    ext2_decompress_cluster(inode, cluster0 + clu_ix);
+         
+         //mw: do we need this ???
+         //if (!(ei->i_compr_flags & EXT2_OSYNC_INODE)) {                            
+        	 /* trace_e2c("ext2_writepage: inode up 3\n"); */
+         mutex_unlock(&inode->i_mutex);                                                   
+         //}
+         lock_page(page);
+         
+         /* fall through */                                            
+    }                                                                                 
+#endif /* CONFIG_EXT2_COMPRESS */
+#endif       
 	return block_write_full_page(page, ext2_get_block, wbc);
 }
 
+#ifndef CONFIG_EXT2_COMPRESS
 static int ext2_readpage(struct file *file, struct page *page)
 {
 	return mpage_readpage(page, ext2_get_block);
 }
+#endif
 
 static int
 ext2_readpages(struct file *file, struct address_space *mapping,
 		struct list_head *pages, unsigned nr_pages)
 {
+#ifdef CONFIG_EXT2_COMPRESS
+/*
+ * For now, just read each page into cache and don't worry about emitting BIOs.
+ * (whitpa 02 Aug 2004).
+ */
+
+	unsigned page_idx;
+	struct pagevec lru_pvec;
+	int iError;
+	
+	pagevec_init(&lru_pvec, 0);
+
+	for (page_idx = 0; page_idx < nr_pages; page_idx++) {
+		struct page *page = list_entry(pages->prev, struct page, lru);
+        
+		prefetchw(&page->flags);
+		list_del(&page->lru);
+		
+		iError = add_to_page_cache(page, mapping, page->index,	GFP_KERNEL);
+		if (!iError) {
+			if (!PageUptodate(page))  
+			{
+				(void) ext2_readpage(file, page);
+			}
+			else
+			{
+				unlock_page(page);
+			}
+			if (!pagevec_add(&lru_pvec, page))
+				__pagevec_lru_add(&lru_pvec);
+		} else {
+			page_cache_release(page);
+		}
+		
+	}
+	pagevec_lru_add(&lru_pvec);
+	BUG_ON(!list_empty(pages));
+	return 0;
+#else
 	return mpage_readpages(mapping, pages, nr_pages, ext2_get_block);
+#endif
 }
 
 int __ext2_write_begin(struct file *file, struct address_space *mapping,
@@ -759,11 +1414,58 @@ static int ext2_nobh_writepage(struct pa
 	return nobh_writepage(page, ext2_get_block, wbc);
 }
 
+#ifdef CONFIG_EXT2_COMPRESS
+static sector_t ext2_do_bmap(struct address_space *mapping, sector_t block)
+#else
 static sector_t ext2_bmap(struct address_space *mapping, sector_t block)
+#endif
 {
 	return generic_block_bmap(mapping,block,ext2_get_block);
 }
 
+#ifdef CONFIG_EXT2_COMPRESS
+/* Return 0 instead of EXT2_COMPRESSED_BLKADDR if EXT2_NOCOMPR_FL
+ * high.  This is necessary for us to be able to use
+ * generic_readpage() when EXT2_NOCOMPR_FL is high.
+ */
+static sector_t ext2_bmap(struct address_space *mapping, sector_t block)
+{
+	sector_t result;
+	struct inode *inode = mapping->host;
+
+	if ((EXT2_I(inode)->i_flags & (EXT2_COMPRBLK_FL | EXT2_NOCOMPR_FL))
+	    == (EXT2_COMPRBLK_FL | 0)) {
+		int err;
+
+		err = ext2_cluster_is_compressed_fn
+		    (inode, ext2_block_to_cluster(inode, block));
+		if (err > 0)
+			ext2_warning (inode->i_sb, "ext2_bmap",
+			    "compressed cluster, inode %lu",
+			    inode->i_ino);
+		if (err != 0)
+			return 0;
+	}
+
+	result = ext2_do_bmap(mapping, block);
+	if (result != EXT2_COMPRESSED_BLKADDR)
+		return result;
+
+	if (!(EXT2_SB(inode->i_sb)->s_es->s_feature_incompat
+	    & cpu_to_le32(EXT2_FEATURE_INCOMPAT_COMPRESSION)))
+		ext2_error(inode->i_sb, "ext2_bmap",
+		    "compressed_blkaddr (ino %lu, blk %lu) "
+		    "on non-compressed fs",
+		    inode->i_ino, (unsigned long) block);
+	if (!S_ISREG(inode->i_mode))
+		ext2_error(inode->i_sb, "ext2_bmap",
+		    "compressed_blkaddr for non-regular file "
+		    "(ino %lu, blk %lu)",
+		    inode->i_ino, (unsigned long) block);
+	return 0;
+}
+#endif /* CONFIG_EXT2_COMPRESS */
+
 static ssize_t
 ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
 			loff_t offset, unsigned long nr_segs)
@@ -778,6 +1480,18 @@ ext2_direct_IO(int rw, struct kiocb *ioc
 static int
 ext2_writepages(struct address_space *mapping, struct writeback_control *wbc)
 {
+#ifdef CONFIG_EXT2_COMPRESS
+#ifdef USE_WRITEPAGE
+	struct ext2_inode_info *ei = EXT2_I(mapping->host);
+	if ( (ei->i_flags & EXT2_COMPRBLK_FL) 
+			 && !(ei->i_flags & EXT2_NOCOMPR_FL))
+	{
+		//NULL will invoke ext2_writepage for writeback, hopefully.
+		return mpage_writepages(mapping, wbc, NULL);
+	}
+	else
+#endif	
+#endif
 	return mpage_writepages(mapping, wbc, ext2_get_block);
 }
 
@@ -925,6 +1639,12 @@ static inline void ext2_free_data(struct
 
 	for ( ; p < q ; p++) {
 		nr = le32_to_cpu(*p);
+#ifdef CONFIG_EXT2_COMPRESS
+		if (nr == EXT2_COMPRESSED_BLKADDR) {
+			*p = 0;
+			continue;
+		}
+#endif
 		if (nr) {
 			*p = 0;
 			/* accumulate blocks to free if they're contiguous */
@@ -969,6 +1689,12 @@ static void ext2_free_branches(struct in
 			nr = le32_to_cpu(*p);
 			if (!nr)
 				continue;
+#ifdef CONFIG_EXT2_COMPRESS
+			if (nr == EXT2_COMPRESSED_BLKADDR) {
+			  *p = 0;
+			  continue;
+			}
+#endif
 			*p = 0;
 			bh = sb_bread(inode->i_sb, nr);
 			/*
@@ -993,6 +1719,95 @@ static void ext2_free_branches(struct in
 		ext2_free_data(inode, p, q);
 }
 
+/* pjm 1998-01-14: As far as I can tell, "I don't do any locking" is
+  no longer correct, as i_sem is downed for all write() and
+  truncate() stuff except where it doesn't matter (e.g. new inode). */
+
+#ifdef CONFIG_EXT2_COMPRESS
+/* If the EXT2_ECOMPR_FL bit is high, then things can go rather badly.
+  This can only happen if access permission was obtained before the
+  flag was raised.  Also, it shouldn't be too much of a problem
+  unless the end point of truncation is a compressed cluster with a
+  compression error. */
+
+ /* From what I (Antoine) understand, the complexity of the truncate
+    code is due to the fact that we don't want to free blocks that
+    are still referenced.  It does not ensure that concurrent read
+    operation will terminate properly, i.e., the semantic of reading
+    while somebody truncates is undefined (you can either get the old
+    data if you got the blocks before, or get plenty of zeros
+    otherwise). */
+
+/* todo: Provide error trapping in readiness for when i_op->truncate
+  allows a return code. */
+static void fix_compression (struct inode * inode)
+{
+	struct ext2_inode_info *ei = EXT2_I(inode);		
+	/*if (atomic_read(&inode->i_mutex.count) > 0)
+	{
+		printk("Assert Mutex failed for file: %s \n", inode_name(inode, 0));
+		dump_stack();
+	}*/
+		
+	assert (ei->i_flags & EXT2_COMPRBLK_FL);  /* one or more compressed clusters */
+	assert ((atomic_read(&inode->i_mutex.count) < 1)
+		|| ((inode->i_nlink == 0)
+		    && (atomic_read(&inode->i_count) == 0)));
+	/* pjm 1998-01-14: I think the below comment can safely be removed, as
+	   it's impossible for someone to be compressing during truncate(), because
+	   i_sem is down. */
+	/*   Dans le cas ou les clusters peuvent etre compresses, cela pose
+	     un probleme : il faudrait stopper aussi si le cluster est
+	     comprime et ne contient pas plus de donnees que i_size ne
+	     permet. Sinon, on peut passer son temps a decompresser un
+	     cluster que quelqu'un d'autre compresse en meme
+	     temps... (TODO).  Cela ne peut arriver que si on reverifie apres
+	     coup si le cluster est non compresse (ce qu'on fait a l'heure
+	     actuelle) => faire autrement.
+
+	     pjm fixme tr
+
+	     If the clusters can be compressed, we'd have a problem: we'd
+	     also need to stop if the cluster is compressed and doesn't
+	     contain more data than i_size permits.  Otherwise we can spend
+	     time decompressing a cluster that someone else is compressing
+	     at the same time.  (TODO.)  This can only happen if we reverify
+	     "apres coup" ("after the event"? "after each time"?) "si" ("if"
+	     or "that") the cluster is not compressed (as we are currently
+	     doing) => do differently. */
+
+	/* todo: Handle errors from ext2_cluster_is_compressed().
+	   (Except ext2_truncate() currently silently ignores errors
+	   anyway.) */
+
+	if (!ext2_offset_is_clu_boundary(inode, inode->i_size)
+	    && (! ( ei->i_flags & EXT2_NOCOMPR_FL))
+	    && (ext2_cluster_is_compressed_fn
+		  (inode, ext2_offset_to_cluster (inode, inode->i_size))
+		> 0)) {
+		trace_e2c("fix_compression: inode:%lu decompress_cluster!\n", inode->i_ino);
+		ext2_decompress_cluster(inode, ext2_offset_to_cluster(inode, inode->i_size));
+		/* todo: Check the return code of
+		   ext2_decompress_cluster().  (Then again, I don't
+		   know how to report an error anyway.
+		   ext2_truncate() silently ignores errors.) */
+	  
+		/* Organise for the cluster to be recompressed later. */
+		assert (ei->i_flags & EXT2_COMPR_FL);
+		
+		ei->i_flags |= EXT2_DIRTY_FL;
+		ei->i_compr_flags |= EXT2_CLEANUP_FL;
+		mark_inode_dirty(inode);
+	} else
+		/* If there are no more compressed clusters, then
+		   remove the EXT2_COMPRBLK_FL.  Not essential from a
+		   safety point of view, but friendlier.  We only do
+		   this in the `else' because the cleanup function
+		   will handle it in the `if' case. */
+		ext2_update_comprblk(inode);
+}
+#endif
+
 void ext2_truncate(struct inode *inode)
 {
 	__le32 *i_data = EXT2_I(inode)->i_data;
@@ -1014,6 +1829,26 @@ void ext2_truncate(struct inode *inode)
 	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
 		return;
 
+#ifdef CONFIG_EXT2_COMPRESS
+	/* If the new size is in the middle of a compressed cluster,
+	   then we decompress it, and set things up to be recompressed
+	   later.
+
+	   todo: It isn't very nice to get ENOSPC on truncate.  We
+	   can't completely remove the possibility (unless the
+	   compression algorithms obey the rule `shorter input never
+	   gives longer output') but we could greatly reduce the
+	   possibility, e.g. by moving the fix_compression() function
+	   to compress.c, and have it decompress and immediately
+	   recompress the cluster, without allocating blocks for the
+	   full decompressed data. */
+	if (EXT2_I(inode)->i_flags & EXT2_COMPRBLK_FL) {
+	  	trace_e2c("ext2_truncate: ino=%ld sz=%d\n", inode->i_ino, (int)inode->i_size);
+	 	fix_compression(inode);
+		truncate_inode_pages(inode->i_mapping, inode->i_size);	    
+	}
+#endif
+
 	blocksize = inode->i_sb->s_blocksize;
 	iblock = (inode->i_size + blocksize-1)
 					>> EXT2_BLOCK_SIZE_BITS(inode->i_sb);
@@ -1235,7 +2070,73 @@ struct inode *ext2_iget (struct super_bl
 		goto bad_inode;
 	}
 	inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
+#ifdef CONFIG_EXT2_COMPRESS
+	ei->i_flags = 0x807fffff & le32_to_cpu(raw_inode->i_flags);
+	ei->i_compr_flags = 0;
+	if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)) {
+		
+		if (S_ISDIR(inode->i_mode)) 
+		{
+			//mw:
+			//mutex_lock(&inode->i_mutex);
+			if (S_ISDIR(inode->i_mode))
+			{
+				ei->i_flags &= ~(EXT2_COMPRBLK_FL | EXT2_DIRTY_FL);  //modify!!!
+			}
+			//mutex_unlock(&inode->i_mutex);
+		}
+		
+		/* The above shouldn't be necessary unless someone's
+		 * been playing with EXT2_IOC_SETFLAGS on a non-e2compr
+		 * kernel, or the inode has been scribbled on.
+		 */
+		if (ei->i_flags & (EXT2_COMPR_FL | EXT2_COMPRBLK_FL)) {
+			ei->i_compr_method
+			    = (le32_to_cpu(raw_inode->i_flags) >> 26) & 0x1f;
+			ei->i_log2_clu_nblocks
+			    = (le32_to_cpu(raw_inode->i_flags) >> 23) & 0x7;
+			if ((ei->i_log2_clu_nblocks < 2)
+			    || (ei->i_log2_clu_nblocks > 5)) {
+				if ((ei->i_log2_clu_nblocks == 0)
+				    && !(ei->i_flags & EXT2_COMPRBLK_FL)) {
+					/* The EXT2_COMPR_FL flag was
+					 * raised under a kernel
+					 * without e2compr support.
+					 */
+					if (S_ISREG(inode->i_mode))
+						ei->i_flags |= EXT2_DIRTY_FL;
+					/* Todo: once we're sure the kernel can
+					 * handle [log2_]clu_nblocks==0, get rid
+					 * of the next statement.
+					 */
+					ei->i_log2_clu_nblocks
+					    = EXT2_DEFAULT_LOG2_CLU_NBLOCKS;
+				} else {
+					ei->i_flags |= EXT2_ECOMPR_FL;
+					ext2_error(inode->i_sb,
+					    "ext2_read_inode",
+					    "inode %lu is corrupted: "
+					    "log2_clu_nblocks=%u",
+					    inode->i_ino,
+					    ei->i_log2_clu_nblocks);
+				}
+			}
+		} else {
+			ei->i_compr_method = EXT2_DEFAULT_COMPR_METHOD;
+			ei->i_log2_clu_nblocks
+			    = EXT2_DEFAULT_LOG2_CLU_NBLOCKS;
+		}
+	if (ei->i_log2_clu_nblocks >
+	    (EXT2_LOG2_MAX_CLUSTER_BYTES - inode->i_sb->s_blocksize_bits))
+		ei->i_log2_clu_nblocks = (EXT2_LOG2_MAX_CLUSTER_BYTES
+		    - inode->i_sb->s_blocksize_bits);
+	ei->i_clu_nblocks = 1 << ei->i_log2_clu_nblocks;
+	if (ei->i_flags & EXT2_DIRTY_FL)
+		ei->i_compr_flags = EXT2_CLEANUP_FL;
+	}
+#else /* !CONFIG_EXT2_COMPRESS */
 	ei->i_flags = le32_to_cpu(raw_inode->i_flags);
+#endif
 	ei->i_faddr = le32_to_cpu(raw_inode->i_faddr);
 	ei->i_frag_no = raw_inode->i_frag;
 	ei->i_frag_size = raw_inode->i_fsize;
@@ -1356,7 +2257,35 @@ static int ext2_update_inode(struct inod
 
 	raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
 	raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
+#ifdef CONFIG_EXT2_COMPRESS
+	if ((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))
+	    && (ei->i_flags & (EXT2_COMPR_FL | EXT2_COMPRBLK_FL))) {
+		if ((ei->i_log2_clu_nblocks < 2)
+		    || (ei->i_log2_clu_nblocks > 5)) {
+			ei->i_flags |= EXT2_ECOMPR_FL;
+			ext2_error (inode->i_sb, "ext2_write_inode",
+			    "inode %lu is corrupted: log2_clu_nblocks=%u",
+			    inode->i_ino, ei->i_log2_clu_nblocks);
+		}
+		assert (ei->i_clu_nblocks == (1 << ei->i_log2_clu_nblocks));
+		assert (ei->i_compr_method < 0x20);
+		raw_inode->i_flags = cpu_to_le32
+		    ((ei->i_flags & 0x807fffff)
+		    | (ei->i_compr_method << 26)
+		    | (ei->i_log2_clu_nblocks << 23));
+	} else
+	{
+		//mw: i_mutex was introduced and disabled again: deadlock with lilo
+		//	mutex_lock(&inode->i_mutex); //mw
+		raw_inode->i_flags = cpu_to_le32	//modify !!!
+		   (ei->i_flags
+		   & 0x807fffff /* no compr meth/size */
+		   & ~(EXT2_COMPR_FL | EXT2_COMPRBLK_FL | EXT2_IMMUTABLE_FL | EXT2_ECOMPR_FL | EXT2_NOCOMPR_FL));
+		//	mutex_unlock(&inode->i_mutex); //mw
+	}
+#else
 	raw_inode->i_flags = cpu_to_le32(ei->i_flags);
+#endif
 	raw_inode->i_faddr = cpu_to_le32(ei->i_faddr);
 	raw_inode->i_frag = ei->i_frag_no;
 	raw_inode->i_fsize = ei->i_frag_size;
@@ -1440,6 +2369,7 @@ int ext2_setattr(struct dentry *dentry, 
 			return error;
 	}
 	error = inode_setattr(inode, iattr);
+/* Paul Whittaker tweak 19 Feb 2005 */
 	if (!error && (iattr->ia_valid & ATTR_MODE))
 		error = ext2_acl_chmod(inode);
 	return error;
--- linux-2.6.25/fs/ext2/new-method-howto.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/new-method-howto.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,249 @@
+/* 
+   (This document isn't finished, but it does include the most 
+   important information.)
+
+   This file hopes to give some documentation on how to add a new 
+   compression method to e2compr.
+
+   LZO looks like a good method to add, for example.  (Good for files
+   that are usually accessed when CPU already fully loaded.)
+
+
+   Requirements
+   ============
+
+   You need to know how much memory is needed.  (You get a pointer to
+   a large area of memory before you start to compress or decompress.
+   You need to know in advance -- i.e. at compile time -- how big you
+   need this area to be.)
+
+   You need to check that your routines don't use recursion or
+   anything else likely to use large amounts of stack space.
+
+   For future compatibility, you should try to make the routines re-entrant.
+   E.g. for all variables that are either at file scope or are declared static,
+   expect that they can be clobbered whenever you call schedule().
+   One way of dealing with this would be to move the variables to the heap
+   (remembering to increase your heap size requirement declaration);
+   another is to store local (i.e. stack space) copies of such vars.
+   At the moment, the most likely sort of reentrancy requirement is that
+   compression and decompression must be able to happen simultaneously.
+   So the most important thing is vars that are common to your compression
+   and decompression routines.
+
+   Write wrapper functions that conform to the interface that the
+   kernel expects.  Namely, you're given a pointers to two 32KB areas
+   (one for the input data, one for the output data; or put another
+   way, one for compressed data and one for uncompressed data), a
+   pointer to a large area of memory for your own use, you're told how
+   long the input data is, how long the output data is expected to be.
+   You must never write past the end of the 32KB output buffer, or
+   write to the input buffer.
+   
+   (If, for performance reasons, you think you may overrun the output
+   buffer by no more than a known amount: the heap and buffers are
+   next to each other, so you may be able to organise that overruns go
+   into the heap, and not use the first n bytes of the heap for
+   anything else.)
+   
+   Return zero if there is an error, or if the data can't be compressed; 
+   otherwise return the length of the output data.
+
+   More details follow.
+
+
+   Memory allocation
+   -----------------
+
+   There are several sources of memory:
+
+   + A large area (about 256KB, or however much hou say you need) of 
+     contiguous memory is made available to you.  This area is generally 
+     known as the "heap".
+     
+     You are encouraged to use the heap for most of your memory requirements, 
+     as it remains allocated whether or not you use it.  
+     
+     Data put here is not retained between invocations of your 
+     functions.
+     
+   + Stack space.  (This is what C uses for its automatic
+     (i.e. non-static) variables.)  Do not use much of this!  In
+     kernel space, there is only a page or two (I think that in 2.1
+     and later, it\'s two pages less ~1.5KB; and one page or less in
+     2.0 and earlier; a page is 4096 bytes on most machines) of stack
+     space per process.  This effectively rules out recursion in any
+     of your routines.  It also means that you can't have large arrays
+     as automatic (non-static) variables.
+
+
+
+   Interface:
+
+   There are three entry functions: a simple initialisation function,
+   a decompression function, and a compression function.
+
+   The initialisation function is to organise an area of memory for
+   you.
+
+     int ext2_iFOO (void *mem_start, int action);
+
+   The first time it's called is by fs/ext2/super.c, the first time an
+   ext2 filesystem is mounted (which is usually at boot time).
+   super.c wants to know how much memory (measured in bytes) you'll
+   need in this area.  (gzip requires 200KB; I think one of the others
+   requires 256KB.)  super.c passes NULL for mem_start.
+
+   Subsequently it's called by fs/ext2/compress.c (search for
+   `.init'), which passes you a pointer to an area of memory at least
+   as big as you told super.c that you need.  Assign this value to a
+   static variable (at file scope) so that your compression and
+   decompression routines know where the heap is.
+   
+   The 'int action' parameter tells your function what's going on, i.e.
+   if it has to initialize algorithm for compression or decompression -
+   this is important for simultaneous compression/decompression.
+
+   Changes to rest of e2compr code:
+
+   + Decide on a short identifier.  In these instructions I've used `foo'.
+     Check that your chosen identifier isn't used elsewhere by doing:
+
+       find /usr/src/linux/. -name '*.[ch]'|xargs grep -i foo
+
+     or some such.
+
+   + If you only have one new file to add to the source tree, call it
+     `fs/ext2/foo.c'.  Add foo.c to the definition of COMPRESS_STUFF
+     in fs/ext2/Makefile.
+
+     Otherwise `mkdir fs/ext2/foo', and put a Makefile in it.  If you
+     have .S files, lzrw/Makefile is a good starting point, otherwise
+     gzip/Makefile.  Add foo to the definition of SUB_DIRS in
+     fs/ext2/Makefile.
+
+   + Edit fs/Config.in.  Search for `LZV' (there should be two such
+     lines), duplicate each line, and change LZV to FOO.
+
+   + You will need three entry functions.  I suggest you call them
+     ext2_iFOO(), ext2_rFOO() and ext2_wFOO().
+     
+     The `i' function is the initialisation function.  It is called by
+     fs/ext2/super.c (which wants to know how much memory to allocate
+     for you, and which passes NULL as the parameter) and it is called
+     before your de/compression routines are called, to give you the
+     starting address of the heap.
+
+   * A good way of testing your routines is to link them into
+     e2compress before into the kernel, just so that you get the
+     benefit of memory- and crash-protection and of not having to
+     reboot the machine to change your code.  Change the Makefile in
+     src/e2compress, edit e2compress.c, search for `gzip' (ignoring
+     case), follow the pattern.
+
+   + Edit fs/ext2/compress.c.  Search for `GZIP'.  Follow the pattern
+     for the the other algorithms there.  (I.e. #undef FOO, #define
+     FOO to 1 or 0 according to whether or not CONFIG_EXT2_HAVE_FOO is
+     defined.)
+
+     Add an entry to ext2_algorithm_table[].  (Or rather, change one
+     of the entries with `0' in the second column.)  Again, follow the
+     existing pattern.
+
+   + Edit include/linux/ext2_fs.h and include/linux/ext2_fs_c.h.
+     Search for `GZIP' and follow the pattern.
+
+
+
+   */
+
+#include <linux/types.h>
+
+/* Your code needs to be able to be compiled into e2compress(1), so make sure
+   that kernel-specific things are wrapped in `#ifdef __KERNEL__'. */
+#ifdef __KERNEL__
+# include <linux/linkage.h>
+# include <asm/param.h>
+/* If you want your code to be able to be compiled as module add those
+ * two includes
+ */
+# include <linux/ext2_fs_c.h>
+# include <linux/module.h>
+asmlinkage void schedule(void);
+static unsigned long next_brk = 0;
+extern unsigned long volatile jiffies;
+#endif
+
+size_t ext2_iFOO (void *ptr, int action)
+{
+	switch (action) {
+		case EXT2_ALG_INIT_COMPRESS:
+			return 0;
+		case EXT2_ALG_INIT_DECOMPRESS:
+			return 0;
+		default:
+			return 0;
+	}
+}
+
+size_t ext2_rFOO (__u8 *ibuf, __u8 *obuf, size_t ilen, size_t olen, int xarg)
+{
+  /* I've never noticed decompression to be a speed problem, so
+     I don't bother calling schedule() from here. */
+
+  /* If you want your code to be able to be compiled as module add this code */
+  MOD_INC_USE_COUNT;
+  /* do decompression */
+  /* If you want your code to be able to be compiled as module add this code */
+  MOD_DEC_USE_COUNT;
+  return 0;
+}
+
+size_t ext2_wFOO (__u8 *ibuf, __u8 *obuf, size_t ilen, size_t olen, int xarg)
+{
+  /* If you want your code to be able to be compiled as module add this code */
+  MOD_INC_USE_COUNT;
+  for(;;)
+    {
+      /* Preferably put this bit in a place that's only executed
+         rarely rather than once per byte processed... */
+#ifdef __KERNEL__
+      /* Time slice of a fifth of a second. */
+      if (jiffies > next_brk)
+	{
+	  schedule();
+	  next_brk = jiffies + HZ / 5;
+	}
+#endif
+    }
+  /* If you want your code to be able to be compiled as module add this code */
+  MOD_DEC_USE_COUNT;
+  return 0; /* never reached */
+}
+
+/* If you want your code to be able to be compiled as module add this code */
+#ifdef MODULE
+
+int init_module(void)
+{
+	struct ext2_algorithm foo_alg;
+
+	foo_alg.name = NULL;
+	foo_alg.avail = 1;
+	foo_alg.init = ext2_iFOO;
+	foo_alg.compress = ext2_wFOO;
+	foo_alg.decompress = ext2_rFOO;
+
+	return ext2_register_compression_module(EXT2_FOO_ALG,
+			0, /* AMOUNT OF REQUIRED MEMORY FOR COMPRESSION */
+			0, /* AMOUNT OF REQUIRED MEMORY FOR DECOMPRESSION */
+			&foo_alg);
+}
+
+void cleanup_module(void)
+{
+	ext2_unregister_compression_module(EXT2_FOO_ALG);
+}
+
+#endif
+
--- linux-2.6.25/fs/ext2/file.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/ext2/file.c	2008-07-31 15:25:26.000000000 +0200
@@ -19,17 +19,70 @@
  */
 
 #include <linux/time.h>
-#include "ext2.h"
 #include "xattr.h"
 #include "acl.h"
 
 /*
- * Called when filp is released. This happens when all file descriptors
- * for a single struct file are closed. Note that different open() calls
- * for the same file yield different struct file structures.
+ *  Copyright (C) 2001 Alcatel Business Systems - R&D Illkirch FRANCE
+ *
+ *    Transparent compression code for 2.4 kernel.
+ *
+ *  Denis Richard (denis.richard@sxb.bsf.alcatel.fr)
+ *  Pierre Peiffer (pierre.peiffer@sxb.bsf.alcatel.fr)
+ *
+ *  Adapted from patch e2compr-0.4.39-patch-2.2.18 .
+ */
+
+#include <linux/fs.h>
+#include <linux/ext2_fs_c.h>
+#include "debug.h"
+#include <linux/buffer_head.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_EXT2_COMPRESS
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/swap.h>
+#include <linux/pagemap.h>
+#endif
+  
+/*
+ * Called when an inode is released. Note that this is different
+ * from ext2_open_file: open gets called at every open, but release
+ * gets called only when /all/ the files are closed.
+ */
+
+/*
+ * pjm 1998-01-09: I would note that this is different from `when no
+ * process has the inode open'.
  */
 static int ext2_release_file (struct inode * inode, struct file * filp)
 {
+#ifdef CONFIG_EXT2_COMPRESS
+	/*
+	 * Now's as good a time as any to clean up wrt compression.
+	 * Previously (before 2.1.4x) we waited until
+	 * ext2_put_inode(), but now the dcache sometimes delays that
+	 * call until umount time.
+	 */
+	//printk(KERN_DEBUG "ext2_release_file: pid=%d, i_ino=%lu, i_count=%d\n", current->pid, inode->i_ino,  atomic_read(&inode->i_count));
+	
+	if (S_ISREG (inode->i_mode)
+	    && inode->i_nlink
+	    && (EXT2_I(inode)->i_compr_flags & EXT2_CLEANUP_FL)) {
+#ifdef EXT2_COMPR_REPORT_PUT
+		printk(KERN_DEBUG "ext2_release_file: pid=%d, i_ino=%lu, i_count=%d\n", current->pid, inode->i_ino,  atomic_read(&inode->i_count));
+#endif
+		/*
+		 * todo: See how the return code of
+		 * ext2_release_file() is used, and decide whether it
+		 * might be appropriate to pass any errors to
+		 * caller.
+		 */
+		//dump_stack();
+		(void) ext2_cleanup_compressed_inode (inode);
+	}
+	
+#endif
 	if (filp->f_mode & FMODE_WRITE) {
 		mutex_lock(&EXT2_I(inode)->truncate_mutex);
 		ext2_discard_reservation(inode);
@@ -38,6 +91,398 @@ static int ext2_release_file (struct ino
 	return 0;
 }
 
+#ifdef CONFIG_EXT2_COMPRESS
+struct page_cluster {
+	struct page *	page;
+	loff_t		pos;
+	unsigned	bytes;
+	unsigned long	offset;
+	unsigned char	in_range;
+	const char *	buf;
+};
+
+#define PAGE_IN_RANGE 1
+#define PAGE_KMAPPED  2
+
+/*
+ * Write to a file through the page cache.
+ *
+ * We currently put everything into the page cache prior to writing it.
+ * This is not a problem when writing full pages. With partial pages,
+ * however, we first have to read the data into the cache, then
+ * dirty the page, and finally schedule it for writing. Alternatively, we
+ * could write-through just the portion of data that would go into that
+ * page, but that would kill performance for applications that write data
+ * line by line, and it's prone to race conditions.
+ *
+ * Note that this routine doesn't try to keep track of dirty pages. Each
+ * file system has to do this all by itself, unfortunately.
+ *                                                    okir@monad.swb.de
+ */
+ssize_t
+ext2_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos)
+{
+	struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
+	struct inode	*inode = mapping->host;
+	unsigned long	limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur, written, last_index;	   /* last page index */
+	loff_t	pos;
+	long	status;
+	int		err, osync_already; //, pagesPerCluster=0; /* number of pages per cluster */
+	unsigned	bytes;
+	u32		comprblk_mask=0;
+	struct ext2_inode_info *ei = EXT2_I(inode);
+
+	if (!(ei->i_flags & (EXT2_COMPR_FL|EXT2_COMPRBLK_FL)) 
+#undef DUD //mw: I think this is a buggy bug-fix
+#ifdef DUD
+			|| (count < inode->i_sb->s_blocksize) 
+#endif
+	)		
+	{
+		return do_sync_write(file, buf, count, ppos);
+	}
+
+	if ((ssize_t) count < 0)
+		return -EINVAL;
+
+	if (!access_ok(VERIFY_READ, buf, count))
+		return -EFAULT;
+
+#ifdef EXT2_COMPR_REPORT_MUTEX
+    printk(KERN_DEBUG "EXT2_FILE_WRITE_LOCK of PID %u @ inode:%lu\n", current->pid, inode->i_ino );
+#endif
+	mutex_lock(&inode->i_mutex);
+	down_read(&inode->i_alloc_sem); /* as used by ocsf2 TLL 02/21/07 */
+
+	pos = *ppos;
+	err = -EINVAL;
+	if (pos < 0)
+		goto out;
+
+	written = 0;
+
+	/* FIXME: this is for backwards compatibility with 2.4 */
+	if (!S_ISBLK(inode->i_mode) && file->f_flags & O_APPEND)
+	{
+		pos = inode->i_size;
+	}
+
+	/*
+	 * Check whether we've reached the file size limit.
+	 */
+	err = -EFBIG;
+
+	if (limit != RLIM_INFINITY) {
+		if (pos >= limit) {
+			send_sig(SIGXFSZ, current, 0);
+			goto out;
+		}
+		if (pos > 0xFFFFFFFFULL || count > limit - (u32)pos) {
+			/* send_sig(SIGXFSZ, current, 0); */
+			count = limit - (u32)pos;
+		}
+	}
+
+	/*
+	 *      LFS rule
+	 */
+	if ( pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) {
+		if (pos >= MAX_NON_LFS) {
+			send_sig(SIGXFSZ, current, 0);
+			goto out;
+		}
+		if (count > MAX_NON_LFS - (u32)pos) {
+			/* send_sig(SIGXFSZ, current, 0); */
+			count = MAX_NON_LFS - (u32)pos;
+		}
+	}
+
+	/*
+	 *	Are we about to exceed the fs block limit ?
+	 *
+	 *	If we have written data it becomes a short write
+	 *	If we have exceeded without writing data we send
+	 *	a signal and give them an EFBIG.
+	 *
+	 *	Linus frestrict idea will clean these up nicely..
+	 */
+	if (!S_ISBLK(inode->i_mode)) {
+		if (pos >= inode->i_sb->s_maxbytes) {
+			if (count || pos > inode->i_sb->s_maxbytes) {
+				send_sig(SIGXFSZ, current, 0);
+				err = -EFBIG;
+				goto out;
+			}
+			/* zero-length writes at ->s_maxbytes are OK */
+		}
+
+		if (pos + count > inode->i_sb->s_maxbytes)
+			count = inode->i_sb->s_maxbytes - pos;
+	} else { 
+		if (bdev_read_only(inode->i_sb->s_bdev)) {
+			err = -EPERM;
+			goto out;
+		}
+		if (pos >= inode->i_size) {
+			if (count || pos > inode->i_size) {
+				err = -ENOSPC;
+				goto out;
+			}
+		}
+
+		if (pos + count > inode->i_size)
+		{
+			count = inode->i_size - pos;			
+		}
+	}
+
+	err = 0;
+	if (count == 0)
+		goto out;
+
+	status  = 0;
+
+	if (file->f_flags & O_DIRECT)
+	{
+		err = -EINVAL;
+		goto out;
+	}
+	/*
+	 *	We must still check for EXT2_ECOMPR_FL, as it may have been
+	 *	set after we got the write permission to this file.
+	 */
+	if ((ei->i_flags & (EXT2_ECOMPR_FL | EXT2_NOCOMPR_FL))   == (EXT2_ECOMPR_FL | 0)) 
+	{
+		err = -EXT2_ECOMPR;
+		goto out;
+	}
+
+	remove_suid(file->f_dentry);
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+	mark_inode_dirty_sync(inode);
+
+	if ((pos+count) > inode->i_size)
+		last_index = (pos+count-1) >> PAGE_CACHE_SHIFT;
+	else
+		last_index = (inode->i_size-1) >> PAGE_CACHE_SHIFT;
+
+	comprblk_mask = ei->i_flags | ~EXT2_COMPRBLK_FL;
+
+	//mw: now do it cluster-wise
+	do {
+		//unsigned long index, offset, clusters_page_index0, 
+		unsigned long index, nextClusterFirstByte, cluster_compressed=0;
+		u32  cluster=0;
+		status = -ENOMEM;	/* we'll assign it later anyway */
+
+#ifdef EXT2_COMPRESS_WHEN_CLU	
+		ei->i_flags |= EXT2_COMPRBLK_FL;
+		assert( (file->f_flags & O_DIRECT) == 0);
+		assert(mapping_mapped(inode->i_mapping) == 0);
+#endif			
+
+		index = pos >> PAGE_CACHE_SHIFT;	/*mw: pageindex (start)*/
+		cluster = ext2_page_to_cluster(inode, index);		
+
+		/*
+		 * We decompress the cluster if needed, and write
+		 * the data as normal.  The cluster will be
+		 * compressed again when the inode is cleaned up.
+		 */
+		if ((comprblk_mask == ~(u32)0)
+		    && !(ei->i_flags & EXT2_NOCOMPR_FL)) {
+		      /* AUFFÃ„LLIG 2*/
+			/* assert (block == pos >> inode->i_sb->s_blocksize_bits); */
+
+			cluster_compressed = ext2_cluster_is_compressed_fn(inode, cluster);
+			if (cluster_compressed < 0) {
+				if (! written)
+					written = cluster_compressed;
+				break;
+			}
+		}
+
+		if (cluster_compressed > 0) {
+			/* Here, decompression take place  */
+			cluster_compressed = ext2_decompress_cluster(inode, cluster);
+			if (cluster_compressed < 0) {
+				if (! written) {
+					written = cluster_compressed;
+				}
+				break;
+			}
+		}
+			
+		nextClusterFirstByte = (ext2_cluster_page0(inode, cluster+1) * PAGE_CACHE_SIZE);	
+		bytes = nextClusterFirstByte - pos;   /*mw: bytes todo in this cluster*/
+		if (bytes > count) {
+			bytes = count; /*mw: if end of data*/
+		}
+	
+#ifdef EXT2_COMPR_DEBUG
+		//assert we stay inside the cluster!
+		{
+			int endpos;
+			int endindex;
+			int endcluster;
+			unsigned long thisClusterFirstByte;
+			int relstart, relend, startblock, endblock;
+			
+			thisClusterFirstByte = (ext2_cluster_page0(inode, cluster) * PAGE_CACHE_SIZE);
+			
+			relstart =   pos - thisClusterFirstByte;
+			relend   =  bytes + relstart;
+			
+			startblock = relstart >> 10; 
+			endblock = relend >> 10;
+			
+					
+			endpos = pos + bytes;
+			printk("do_sync_write cluster %d: inode:%lu, \t start:%i(%i), end:%i(%i), \t ccount:%d \t tcount:%d\n", cluster , inode->i_ino, relstart, startblock, relend , endblock,  (int)bytes, count);
+	 		endindex = (endpos-1) >> PAGE_CACHE_SHIFT;	/*mw: pageindex (start)*/
+			endcluster = ext2_page_to_cluster(inode, endindex);
+			assert(cluster == endcluster); 
+		}
+#endif
+	
+		//mw: must unlock here, do_sync_write() will aquire the mutex again
+		mutex_unlock(&inode->i_mutex);	
+		
+		//mw: this is pretty clever: we use the generic method now :-)
+		//printk("do_sync_write cluster %d, mapped:%i\n", cluster, mapping_mapped(inode->i_mapping));
+		//status = do_sync_write_nolock(file, buf,  bytes, &pos); //without locking mutex
+		status = do_sync_write(file, buf,  bytes, &pos);  //with locking mutex
+		assert(status>=0);
+		
+		mutex_lock(&inode->i_mutex);
+				
+		written += status;
+		count -= status;
+		buf += status;
+
+#ifdef EXT2_COMPRESS_WHEN_CLU
+		assert (ei->i_flags & EXT2_COMPRBLK_FL);
+		if ((ei->i_flags & EXT2_COMPR_FL)
+		    && (ext2_offset_is_clu_boundary(inode, pos))  //mw: rememeber: pos was 'curpos'
+		    && (ext2_wr_wa != NULL)) {
+			if  (mapping_mapped(inode->i_mapping) == 0 ) 
+			/*
+			 * Pierre Peiffer: For file mapped (via mmap, I mean),
+			 * compression will occure when releasing the file.
+			 * We must, in this case, avoid the pages (possibly
+			 * mapped by a process) to be compressed under them.
+			 */
+			{
+				int error;
+				assert(mapping_mapped(inode->i_mapping) == 0);
+				error = ext2_compress_cluster(inode, cluster);
+				/*if (ext2_cluster_is_compressed_fn(inode, cluster))
+					ext2_decompress_cluster(inode, cluster);*/
+				assert(mapping_mapped(inode->i_mapping) == 0);
+				/*
+				 * Actually, raising write_error may be a
+				 * mistake.  For example,
+				 * ext2_cleanup_compressed_cluster() doesn't
+				 * usually return any errors to user.  todo:
+				 * Have a look at ext2_compress_cluster, and
+				 * check whether its errors are such that they
+				 * should be returned to user.  Some of the
+				 * will be, of course, but it might be
+				 * possible for it to return without
+				 * change.
+				 */
+				if (error > 0)
+					comprblk_mask = ~(u32)0;
+			} else {
+#ifdef EXT2_COMPR_REPORT
+				char bdn[BDEVNAME_SIZE];
+				bdevname(inode->i_sb->s_bdev, bdn);
+#endif
+
+				trace_e2c("ext2_file_write: (dev. %s): "
+				    "ino=%ld, cluster=%d: file mapped, does "
+				    "not compress cluster\n",
+				    bdn, inode->i_ino, cluster);
+				ei->i_flags |= EXT2_DIRTY_FL;
+				ei->i_compr_flags |= EXT2_CLEANUP_FL;
+			}
+		}
+#endif
+
+	} while (count);
+	*ppos = pos;
+
+	/*
+	 * For now, when the user asks for O_SYNC, we'll actually
+	 * provide O_DSYNC.
+	 */
+	if (status >= 0) {
+		if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) {
+			if (ei->i_compr_flags & EXT2_OSYNC_INODE) {
+				osync_already = 1;
+			} else {
+				osync_already = 0;
+				ei->i_compr_flags |= EXT2_OSYNC_INODE;
+			}
+			/* Should 2nd arg be inode->i_mapping? */
+			status = generic_osync_inode(inode, file->f_mapping,
+			    OSYNC_METADATA|OSYNC_DATA);
+			if (osync_already == 0) {
+				ei->i_compr_flags &= ~EXT2_OSYNC_INODE;
+			}
+		}
+	}
+
+	err = written ? written : status;
+
+# ifdef EXT2_COMPRESS_WHEN_CLU
+	//mw: ext2_compress_cluster() might remove EXT2_COMPRBLK_FL
+	//if the file does not compress at all. this is NO error: remove next line?
+	//assert (ei->i_flags & EXT2_COMPRBLK_FL);
+	
+	ei->i_flags &= comprblk_mask;
+	if ((ei->i_flags & EXT2_COMPR_FL)
+	    && (!ext2_offset_is_clu_boundary(inode, pos)
+	    || (ext2_wr_wa == NULL)))
+	{
+		ei->i_flags |= EXT2_DIRTY_FL;
+		ei->i_compr_flags |= EXT2_CLEANUP_FL;
+	}
+# else
+	if (ei->i_flags & EXT2_COMPR_FL) {
+		ei->i_flags |= EXT2_DIRTY_FL;
+		ei->i_compr_flags |= EXT2_CLEANUP_FL;
+	}
+# endif
+out:
+
+#ifdef EXT2_COMPR_REPORT_MUTEX
+    printk(KERN_DEBUG "EXT2_FILE_WRITE_UNLOCK of PID %u @ inode:%lu\n", current->pid, inode->i_ino);
+#endif
+	up_read(&inode->i_alloc_sem); /* as used by ocsf2 TLL 02/21/07 */
+	mutex_unlock(&inode->i_mutex); 
+	return err;	
+}
+
+/*
+ * Called when an inode is about to be open.
+ * We use this to disallow opening RW large files on 32bit systems if
+ * the caller didn't specify O_LARGEFILE.  On 64bit systems we force
+ * on this flag in sys_open.
+ * Prevent opening compressed file with O_DIRECT.
+ */
+static int ext2_file_open(struct inode * inode, struct file * filp)
+{
+	if ((filp->f_flags & O_DIRECT) && (EXT2_I(inode)->i_flags &
+	    (EXT2_COMPR_FL|EXT2_COMPRBLK_FL)))
+		return -EINVAL;
+	if (!(filp->f_flags & O_LARGEFILE) && inode->i_size > MAX_NON_LFS)
+		return -EFBIG;
+			
+  	return 0;
+ }
+#endif /* CONFIG_EXT2_COMPRESS*/
+
 /*
  * We have mostly NULL's here: the current defaults are ok for
  * the ext2 filesystem.
@@ -45,7 +490,12 @@ static int ext2_release_file (struct ino
 const struct file_operations ext2_file_operations = {
 	.llseek		= generic_file_llseek,
 	.read		= do_sync_read,
+#ifdef CONFIG_EXT2_COMPRESS
+        .write          = ext2_file_write,
+#else
 	.write		= do_sync_write,
+#endif
+
 	.aio_read	= generic_file_aio_read,
 	.aio_write	= generic_file_aio_write,
 	.unlocked_ioctl = ext2_ioctl,
@@ -53,7 +503,11 @@ const struct file_operations ext2_file_o
 	.compat_ioctl	= ext2_compat_ioctl,
 #endif
 	.mmap		= generic_file_mmap,
+#ifdef CONFIG_EXT2_COMPRESS
+    .open           = ext2_file_open,
+#else
 	.open		= generic_file_open,
+#endif
 	.release	= ext2_release_file,
 	.fsync		= ext2_sync_file,
 	.splice_read	= generic_file_splice_read,
--- linux-2.6.25/fs/ext2/lzv1/wlzv1.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzv1/wlzv1.S	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,301 @@
+/*	
+ *	LZV compression written in assembly.
+ *
+ *	Written by Antoine de Maricourt (dumesnil@etca.fr)
+ *	                21 Mar 1995
+ *
+ *	NOTE: The use of this algorithm may be restricted by some
+ *	      patent. Please check if this is the case in your 
+ *	      country before using it.
+ *
+ *	Interface :
+ *	-----------
+ *
+ *	   int wLZV1 (char *ib, char *ob, int il, int ol);
+ *
+ *              ib = input buffer (uncompressed data)
+ *              ob = output buffer (compressed data)
+ *		il = number of bytes in the input buffer
+ *		ol = maximum number of bytes for compressed data
+ *
+ *	The routine may read more (i.e., 3) bytes than the specified
+ *	size for the input buffer. It may also write more (i.e., 264)
+ *	bytes than the specified size for the output buffer before it
+ *	realizes it must stop. The caller should be ready for that.
+ *	Returned value is the size in byte of the compressed data, or
+ *	0 if the data can not be compressed into less bytes than ol.
+ *
+ *	The algorithm and the coding method have been taken from
+ *	file lzv.c originaly written by Hermann Vogt, and whose
+ *	original copyright notice follows:
+ *
+ *	-------
+ *	Copyright (c) 1994 Hermann Vogt. Redistribution of this file is
+ *	permitted under the GNU Public Licence.
+ *
+ *	The method presented here is faster and compresses better
+ *	than lzrw1 and lzrw1-a. I named it lzv for "Lev-Zimpel-Vogt".
+ *	It uses ideas introduced by Ross Williams in his algorithm 
+ *	lzrw1 [R. N. Williams (1991): "An Extremly Fast ZIV-Lempel Data
+ *	Compression Algorithm", Proceedings IEEE Data Compression
+ *	Conference, Snowbird, Utah, 362-371] and by Fiala and Green
+ *	in their algorithm a1 [E. R. Fiala, D. H. Greene (1989): 
+ *	"Data Compression with Finite Windows", Communications of the
+ *	ACM, 4, 490-505]. Because lzv differs strongly from both,
+ *	I hope there will be no	patent problems. The hashing-method 
+ *	has been stolen from Jean-loup Gailly's (patent free) gzip.
+ *	-------
+ *	Copyright (c) 1995 Antoine de Maricourt. Redistribution
+ *	of this file is permitted under the GNU Public License.
+ */
+
+#include <linux/linkage.h>
+#include "lzv1.h"
+
+#if 0
+#define HTAB	SYMBOL_NAME(ext2_lzv1_htab)
+#else
+#define HTAB	40(%esp)
+#endif
+
+#define A0	32
+#define A1	36
+#if 0
+#define A2	40
+#define A3	44
+#else
+#define A2	44
+#define A3	48
+#endif
+#define L0
+
+	/* for i386 */
+#define bswap rol $16,
+
+#define OutputLiteral(_b)	\
+	movb _b, (%edi);	\
+	incl %edi;		\
+	andl $255, %ecx;	\
+	subl %ecx, %esi;	\
+	rep; movsb
+
+#define UpdateHashKey(_b)	\
+	shll $HSHIFT, %ebx;	\
+	xorb _b, %bl
+
+#define UpdateHashTable(_w)	\
+	andl $HMASK, %ebx;	\
+	movw _w, (%ebp,%ebx,2)
+
+#if 0
+.comm HTAB, 4				# hash table
+#endif
+
+	.text
+
+ENTRY(ext2_LZV1_compress)
+	pushl %ebp
+	pushl %edi
+	pushl %esi
+	pushl %ebx			# be safe
+	pushl %ecx
+	pushl %edx
+	subl $4, %esp
+
+	movl A0(%esp), %esi		# esi = input buffer
+	movl A1(%esp), %edi		# edi = output buffer
+	movl A2(%esp), %ecx
+	addl %esi, %ecx
+	movl %ecx, A2(%esp)		# input limit
+	subl $3, %ecx
+	movl %ecx, L0(%esp)
+	addl %edi, A3(%esp)		# output limit
+
+	movl HTAB, %ebp			# get pointer to hash table
+	xorl %ecx, %ecx			# clear literal len
+	xorl %edx, %edx			# clear input position
+	cld
+
+	movzbl (%esi), %ebx		# initialize hash key
+	UpdateHashKey (1(%esi))
+
+	/*
+	 *	eax = 
+	 *	ebx = hash key (bx)
+	 *	ecx = literal len (cl) + tmp data (ch)
+	 *	edx = 
+	 *	esi = source pointer
+	 *	edi = destination pointer
+	 *	ebp = hash table address
+	 */
+
+L10:	cmpl A2(%esp), %esi		# check for input overflow
+	jae L40
+
+	UpdateHashKey (2(%esi))		# update hash key
+
+	/*
+	 *	Test for potential match
+	 */
+
+	andl $HMASK, %ebx
+	movl %edx, %eax
+	movw (%ebp,%ebx,2), %ax		# eax = Hi(ip)|Lo(m_pos)
+	movw %dx, (%ebp,%ebx,2)		# edx = ip
+	cmpl %edx, %eax
+	jb L11
+	cmpl $0x10000, %eax
+	jb L30
+	subl $0x10000, %eax
+
+L11:	subl %edx, %eax			# eax = m_pos - ip [= -m_off - 1 < 0]
+	cmpl $-MOFF, %eax
+	jb L30
+	cmpl L0(%esp), %esi
+	jae L30
+
+	movb (%esi), %ch		# compare first byte
+	cmpb %ch, (%esi,%eax)
+	jne L30
+
+	movb 1(%esi), %ch		# compare second byte
+	cmpb %ch, 1(%esi,%eax)
+	jne L30
+
+	movb 2(%esi), %ch		# compare third byte
+	cmpb %ch, 2(%esi,%eax)
+	jne L30
+
+	/*
+	 *	Flush pending literal on output first,
+	 *	so that we do not need anymore its len.
+	 */
+
+	testb %cl, %cl
+	je L12
+	movb %cl, %ch
+	decb %ch
+	shlb $3, %ch
+	OutputLiteral (%ch)
+
+	/*
+	 *	Find length of current match
+	 */
+
+L12:	lea 2(%esi), %esi
+
+	movl A2(%esp), %ecx		# find maximum match len
+	subl %esi, %ecx
+	cmpl $MML, %ecx
+	jbe L13
+	movl $MML, %ecx
+
+L13:	bswap %ebx			# save hash key
+	movw %cx, %bx			# save max match len
+
+	xchg %eax, %edi			# save edi
+	lea (%esi,%edi), %edi		# get pointer to potential match
+
+	repe; cmpsb
+	jz L14
+	incl %ecx
+	decl %esi
+	decl %edi
+
+L14:	subw %cx, %bx			# match len
+
+	xchg %eax, %edi			# restore edi
+	subl %esi, %eax			# restore the offset
+
+	/*
+	 *	Output offset and length of match.
+	 */
+
+	notl %eax
+	movzwl %bx, %ecx
+
+	cmpw $7, %bx
+	jae L17
+
+	shlb $3, %ah			# output 2 bytes (len < 7)
+	orb %bl, %ah
+	movb %ah, (%edi)
+	jmp L18
+
+L17:	shlb $3, %ah			# output 3 bytes (len >= 7)
+	orb $7, %ah
+	movb %ah, (%edi)
+	subw $7, %bx
+	incl %edi
+	movb %bl, (%edi)
+
+L18:	movb %al, 1(%edi)
+	lea 2(%edi), %edi
+
+	/*
+	 *	Update hash table
+	 */
+
+	bswap %ebx
+	subl %ecx, %esi
+
+L16:	incl %edx
+	incl %esi
+	UpdateHashKey ((%esi))
+	UpdateHashTable (%dx)
+	decl %ecx
+	jne L16
+
+	incl %edx
+	UpdateHashKey (1(%esi))
+	UpdateHashTable (%dx)
+	incl %edx
+
+	xorl %ecx, %ecx			# reset literal len
+
+	cmpl A3(%esp), %edi		# check for output overflow
+	jbe L10				# and continue
+	jmp L40
+	
+	/*
+	 *	Not a match.
+	 */
+
+	ALIGN
+
+L30:	incl %edx; incl %esi
+	incb %cl			# update literal len
+
+	cmpb $MLL, %cl			# flush pending literal
+	jb L32				# if needed
+	OutputLiteral ($0xf8)
+	xorb %cl, %cl
+
+L32:	cmpl A3(%esp), %edi		# check for output overflow
+	jbe L10				# and continue
+	
+	/*
+	 *	The end.
+	 */
+
+L40:	testb %cl, %cl			# flush pending literal
+	je L41				# if needed
+	movb %cl, %ch
+	decb %ch
+	shlb $3, %ch
+	OutputLiteral (%ch)
+
+L41:	xorl %eax, %eax			# return 0 if output overflow
+	cmpl A3(%esp), %edi
+	jae L42 
+	movl %edi, %eax
+	subl A1(%esp),%eax
+
+L42:	addl $4, %esp
+	popl %edx
+	popl %ecx	
+	popl %ebx	
+	popl %esi
+	popl %edi
+	popl %ebp
+	ret
--- linux-2.6.25/fs/ext2/lzv1/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzv1/Makefile	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,19 @@
+#
+# Makefile for the linux compression routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.S.o:
+	$(CC) $(AFLAGS) -traditional -c $< -o $*.o
+
+obj-$(CONFIG_EXT2_HAVE_LZV1) := ext2-compr-lzv1.o
+
+ifeq ($(CONFIG_EXT2_COMPR_X86_CODE),y)
+ext2-compr-lzv1-y   := e2compr_lzv.o rlzv1.o wlzv1.o
+else
+ext2-compr-lzv1-y   := e2compr_lzv.o lzv1.o
+endif
--- linux-2.6.25/fs/ext2/lzv1/lzv1.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzv1/lzv1.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,21 @@
+#define HSIZE	0x4000
+#define HMASK	0x3fff
+#define HSHIFT	5
+
+#define MOFF	8192
+#define	MLL	32		/* Maximum literal length */
+#define	MML	261		/* Maximum match length   */
+
+#ifndef __ASSEMBLY__
+#if 0
+extern unsigned short *ext2_lzv1_htab;
+#endif
+
+typedef	unsigned char	uch;
+typedef	unsigned short	ush;
+typedef	unsigned int	uit;
+
+int ext2_LZV1_compress (uch *in, uch *out, ush *heap, int len, int out_len);
+int ext2_LZV1_decompress (uch *in, uch *out, int ilen, int len);
+#endif
+
--- linux-2.6.25/fs/ext2/lzv1/rlzv2.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzv1/rlzv2.S	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,178 @@
+/*	
+ *	LZV decompression written in assembly.
+ *
+ *	Written by Antoine de Maricourt (dumesnil@etca.fr)
+ *	                12 Apr 1995
+ *
+ *	NOTE: The use of this algorithm may be restricted by some
+ *	      patent. Please check if this is the case in your 
+ *	      country before using it.
+ *
+ *	Interface :
+ *	-----------
+ *
+ *	   int rLZV2 (char *ib, char *ob, int il, int ol);
+ *
+ *              ib = input buffer (compressed data)
+ *              ob = output buffer (uncompressed data)
+ *		il = number of bytes in the input buffer
+ *		ol = number of bytes to be uncompressed
+ *
+ *	The routine may write more (i.e., 65) bytes than requested
+ *	(and could be modified in order to write exactly the requested
+ *	number if this is really important). However, it will not write
+ *	more than the original number of bytes in the uncompressed
+ *	data. The returned value is the number of bytes written to
+ *	output buffer.
+ *
+ *	The coding method is different from the original one. This
+ *	allows matches that can be more distant. This also reduces
+ *	the maximum match length from 264 to 65.
+ *
+ *	The algorithm and the coding method have been taken from
+ *	file lzv.c originaly written by Hermann Vogt, and whose
+ *	original copyright notice follows:
+ *
+ *	-------
+ *	Copyright (c) 1994 Hermann Vogt. Redistribution of this file is
+ *	permitted under the GNU Public Licence.
+ *
+ *	The method presented here is faster and compresses better
+ *	than lzrw1 and lzrw1-a. I named it lzv for "Lev-Zimpel-Vogt".
+ *	It uses ideas introduced by Ross Williams in his algorithm 
+ *	lzrw1 [R. N. Williams (1991): "An Extremly Fast ZIV-Lempel Data
+ *	Compression Algorithm", Proceedings IEEE Data Compression
+ *	Conference, Snowbird, Utah, 362-371] and by Fiala and Green
+ *	in their algorithm a1 [E. R. Fiala, D. H. Greene (1989): 
+ *	"Data Compression with Finite Windows", Communications of the
+ *	ACM, 4, 490-505]. Because lzv differs strongly from both,
+ *	I hope there will be no	patent problems. The hashing-method 
+ *	has been stolen from Jean-loup Gailly's (patent free) gzip.
+ *	-------
+ *	Copyright (c) 1995 Antoine de Maricourt. Redistribution
+ *	of this file is permitted under the GNU Public License.
+ */
+
+#include <linux/linkage.h>
+
+#define A0	28
+#define A1	32
+#define A2	36
+#define A3	40
+
+	.text
+ENTRY(ext2_LZV2_decompress)
+	pushl %ebp
+	pushl %edi
+	pushl %esi
+	pushl %ebx			# be safe
+	pushl %ecx
+	pushl %edx
+
+	movl A0(%esp), %esi		# esi = input buffer
+	movl A1(%esp), %edi		# edi = output buffer
+	movl %esi, %ebx
+	addl A2(%esp), %ebx		# ebx = input limit
+	movl %edi, %edx
+	addl A3(%esp), %edx		# edx = output limit
+	xorl %ecx, %ecx
+	xorl %eax, %eax
+	cld
+
+	ALIGN
+
+L10:	cmpl %esi, %ebx			# check for input overflow
+	jna L13
+
+	/*
+	 *	Get the opcode
+	 */
+
+	movb (%esi), %al		# load opcode 
+	testb $7, %al			# test 3 lower bits
+	jne L11
+
+	/*
+	 *	This is a literal
+	 */
+
+	shrb $3, %al			# store its len into ecx
+	incb %al			
+	movb %al, %cl
+	incl %esi
+	rep; movsb
+	cmpl %edx, %edi			# check for output overflow
+	jb L10				# and continue
+	jmp L13
+
+	/*
+	 *	This is a match
+	 */
+
+	ALIGN
+
+L11:	movb %al, %cl
+	andb $7, %cl			# store its len into ecx
+	cmpb $7, %cl	
+	je L12
+
+	/*
+	 *	This is a small match
+	 */
+
+	addl $2, %ecx
+	shll $5, %eax
+	movb 1(%esi), %al
+	notl %eax
+	lea 2(%esi), %ebp		# save next value of esi
+	lea (%edi,%eax), %esi		# load pointer to match
+	cmpl A1(%esp), %esi		# safety check
+	jb L13
+	rep; movsb			# copy the match
+	xorl %eax, %eax
+	movl %ebp, %esi			# restore esi
+	cmpl %edx, %edi			# check for output overflow
+	jb L10				# and continue
+	jmp L13
+
+	/*
+	 *	This is a big match or a far match
+	 */
+
+	ALIGN
+
+L12:	movb 1(%esi), %cl
+	shll $7, %eax			# store the offset into eax
+	movb %cl, %al
+	andb $0xfc, %ah
+	andb $0x03, %al
+	orb %al, %ah
+	movb 2(%esi), %al
+	notl %eax
+	shrb $2, %cl			# adjust match len
+	addl $2, %ecx
+	lea 3(%esi), %ebp		# save next value of esi
+	lea (%edi,%eax), %esi		# load pointer to match
+	cmpl A1(%esp), %esi		# safety check
+	jb L13
+	rep; movsb			# copy the match
+	xorl %eax, %eax
+	movl %ebp, %esi			# restore esi
+	cmpl %edx, %edi			# check for output overflow
+	jb L10				# and continue
+
+	/*
+	 *	The end.
+	 */
+
+L13:	movl %edi, %eax
+	subl A1(%esp),%eax
+
+	popl %edx
+	popl %ecx
+	popl %ebx
+	popl %esi
+	popl %edi
+	popl %ebp
+	ret
+
--- linux-2.6.25/fs/ext2/lzv1/lzv1.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzv1/lzv1.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,245 @@
+/***********************************************************************
+**
+**	lzv.c -- an extremly fast compression/decompression-method
+**
+**	Written by Hermann Vogt
+**
+**		v 0.3 -- 94/03/08	aCembler version of rLZV built in.
+**		v 0.2 -- 94/03/04	Changes for usage with DouBle 0.2 built in.
+**		v 0.1 -- 94/03/01	Intensivly tested, removed all known bugs.
+**		v 0.0 -- 94/02/21	First Version.
+**
+**	Copyright (c) 1994 Hermann Vogt. Redistribution of this file is
+**	permitted under the GNU Public Licence.
+**
+**	The method presented here is faster and compresses better
+**	than lzrw1 and lzrw1-a. I named it lzv for "Lev-Zimpel-Vogt".
+**	It uses ideas introduced by Ross Williams in his algorithm lzrw1
+**	[R. N. Williams (1991): "An Extremly Fast ZIV-Lempel Data
+**	Compression Algorithm", Proceedings IEEE Data Compression
+**	Conference, Snowbird, Utah, 362-371] and by Fiala and Green in their
+**	algorithm a1 [E. R. Fiala, D. H. Greene (1989): "Data Compression
+**	with Finite Windows", Communications of the ACM, 4, 490-505].
+**	Because lzv differs strongly from both, I hope there will be no
+**	patent problems. The hashing-method has been stolen from Jean-loup
+**	Gailly's (patent free) gzip.
+**
+**	KNOWN PROBLEMS:
+**	-	My english is very bad.
+**  -	Badly commented. (I hope this will be better in the next
+**		version.)
+**	-	I'm not sure if lzv is free from patent problems.
+**
+***********************************************************************/
+
+#define	MLL	32		/*	Maximum len of chain of literals	*/
+#define	MML	(8+256)		/*	Maximum len of match			*/
+#define	MOFF	8191		/*	Maximum offset				*/
+#define	HSIZ	16384		/*	Size of Hashtable			*/
+
+typedef	unsigned char	uch;
+typedef	unsigned short	ush;
+typedef	unsigned int	uit;
+
+#if 0
+unsigned short *ext2_lzv1_htab = ((unsigned short*) 0L);
+#endif
+
+#ifdef __KERNEL__
+# include <linux/linkage.h>
+# include <linux/sched.h>
+# include <asm/param.h>
+static unsigned long next_brk = 0;
+extern unsigned long volatile jiffies;
+#endif
+
+int ext2_LZV1_compress (uch *in, uch *out, ush *heap, int len, int out_len)
+{
+	uit hval, op, ip, l_len, m_pos, m_off, m_len, maxlen;
+	ush *ext2_lzv1_htab = heap;
+
+	maxlen = out_len;
+	hval = ((in[0] << 5) ^ in[1]) & (HSIZ - 1);
+	ip = op = l_len = 0;
+	do {
+		hval = ((hval << 5) ^ in[ip+2]) & (HSIZ - 1);
+		m_pos = ext2_lzv1_htab[hval];
+		ext2_lzv1_htab[hval] = ip;
+
+#if 0
+		/* 
+		 *	If you want to compress more than 64K, uncomment
+		 *	the following lines.
+		 */
+
+		m_pos = (ip &~0xffff) + m_pos;
+		if (m_pos >= ip && m_pos >= 0x10000)
+		  m_pos -= 0x10000;
+#endif
+
+#ifdef __KERNEL__
+		/* Time slice of a fifth of a second. */
+		if (jiffies > next_brk)
+		  {
+		    schedule();
+		    next_brk = jiffies + HZ / 5;  
+		  }
+#endif
+
+		if (m_pos < ip &&
+				in[m_pos] == in[ip] &&
+				(m_off = ip - m_pos - 1) <= MOFF &&
+				ip + 4 < len &&
+				*(ush *)(in+m_pos+1) == *(ush *)(in+ip+1)) {
+			/*	We have found a match	*/
+			uit look = len - ip - 2;
+			if (look > MML) look = MML;
+			m_len = 2;
+			do {
+				m_len++;
+			} while(m_len != look && in[ip+m_len] == in[m_pos+m_len]);
+
+			if (op + 2 + l_len + 3 >= maxlen) return 0;
+			if (l_len != 0) {
+				out[op++] = (l_len - 1) << 3;
+				do {
+					out[op++] = in[ip-l_len--];
+				} while (l_len != 0);
+			}
+			m_len -= 2;
+			if (m_len <= 6) {
+				out[op++] = m_len | ((m_off >> 5) & 0xf8);
+			}
+			else {
+				out[op++] = 0x07 | ((m_off >> 5) & 0xf8);
+				out[op++] = m_len - 7;
+			}
+			out[op++] = m_off & 0xff;
+			ip++;
+			hval = ((hval << 5) ^ in[ip+2]) & (HSIZ - 1);
+			ext2_lzv1_htab[hval] = ip;
+			ip++;
+			do {
+				hval = ((hval << 5) ^ in[ip+2]) & (HSIZ - 1);
+				ext2_lzv1_htab[hval] = ip;
+				ip++;
+				m_len--;
+			} while (0 != m_len);
+		}
+		else {
+			/*	No match found	*/
+			ip++;
+			l_len++;
+			if (MLL == l_len) {
+				if (op + 2 + MLL >= maxlen) return 0;
+				out[op++] = 0xf8;
+				do {
+					out[op++] = in[ip-l_len--];
+				} while (l_len != 0);
+			}
+		}
+	} while (ip < len);
+	if (l_len != 0) {
+		if (op + l_len + 3 >= maxlen) return 0;
+		out[op++] = (l_len - 1) << 3;
+		do {
+			out[op++] = in[ip-l_len--];
+		} while (l_len != 0);
+	}
+	return op;
+}
+
+#ifndef LZV_ASM
+
+int ext2_LZV1_decompress (uch const * const in, uch * const out, int ilen, int len)
+{
+	register uit tbuf, c_len;
+	uch * const out_end = out + len;
+	register uch *op = out;
+	uch const * const in_end = in + ilen;
+	register uch const *ip = in;
+
+	do {
+		tbuf = *ip++;
+ 		c_len = tbuf & 0x07;
+		if (0 == c_len) {
+			c_len = (tbuf >> 3) + 1;
+			do *op++ = *ip++;
+			while (--c_len); /* effic: memcpy()? */
+		} else {
+			register uch *m_pos;
+
+			if (0x07 == c_len) c_len = *ip++ + 7;
+			m_pos = op - 1 - (((uit) (tbuf & 0xf8) << 5) | *ip++);
+#ifndef EXT2_NO_ERRCHK
+			/* If we don't check this then we segfault (if in user
+                           space) or leave process in uninteruptible state (if
+                           in kernel) if the data is corrupt. */
+			if (m_pos < out)
+			  return 0; /* Compression error. */
+#endif
+			*op++ = *m_pos++;
+			*op++ = *m_pos++;
+			do *op++ = *m_pos++;
+			while (--c_len);
+		}
+	} while (op < out_end && ip < in_end);
+	return op - out;
+#if 0
+	if(ip > in_end) return 0;
+	return (ip - in);
+#endif
+}
+
+#else
+
+/*
+**	aCembler version
+**
+**	IMPORTANT: Use gcc -O2 -fomit-frame-pointer!!!
+*/
+
+int ext2_LZV1_decompress (uch *in, uch *out, int ilen, int len)
+{
+	register uit tbuf;
+	register uch *ip;
+	register uch *op asm ("%edi");
+	register uch *m_pos asm ("%esi");
+	uch *out_end, *in_end;
+	register uit c_len asm ("%ecx");
+
+	ip = in;
+	op = out;
+	out_end = out + len;
+	in_end = in + ilen;
+	asm volatile ("cld");
+	do {
+		tbuf = *ip++;
+		c_len = tbuf & 0x07;
+		if (0 == c_len) {
+			c_len = (tbuf >> 3) + 1;
+			m_pos = ip;
+			ip += c_len;
+			asm volatile ("rep; movsb"
+				: /* no output */
+				: "g" (m_pos), "g" (op), "g" (c_len));
+		}
+		else {
+			if (0x07 == c_len) c_len = *ip++ + 9;
+			else c_len += 2;
+			m_pos = op - 1 - (((uit) (tbuf & 0xf8) << 5) | *ip++);
+			asm volatile ("rep; movsb"
+				: /* no output */
+				: "g" (m_pos), "g" (op), "g" (c_len));
+		}
+	} while (op < out_end && ip < in_end);
+
+	return op-out;
+#if 0
+	if(ip > in_end) return 0;
+	return (ip-in);
+#endif
+}
+
+#endif
+
--- linux-2.6.25/fs/ext2/lzv1/rlzv1.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzv1/rlzv1.S	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,168 @@
+/*	
+ *	LZV decompression written in assembly.
+ *
+ *	Written by Antoine de Maricourt (dumesnil@etca.fr)
+ *	                15 Mar 1995
+ *
+ *	NOTE: The use of this algorithm may be restricted by some
+ *	      patent. Please check if this is the case in your 
+ *	      country before using it.
+ *
+ *	Interface :
+ *	-----------
+ *
+ *	   int rLZV1 (char *ib, char *ob, int il, int ol);
+ *
+ *              ib = input buffer (compressed data)
+ *              ob = output buffer (uncompressed data)
+ *		il = number of bytes in the input buffer
+ *		ol = number of bytes to be uncompressed
+ *
+ *	The routine may write more (i.e., 264) bytes than requested
+ *	(and could be modified in order to write exactly the requested
+ *	number if this is really important). However, it will not write
+ *	more than the original number of bytes in the uncompressed
+ *	data. The returned value is the number of bytes written to
+ *	output buffer.
+ *
+ *	The algorithm and the coding method have been taken from
+ *	file lzv.c originaly written by Hermann Vogt, and whose
+ *	original copyright notice follows:
+ *
+ *	-------
+ *	Copyright (c) 1994 Hermann Vogt. Redistribution of this file is
+ *	permitted under the GNU Public Licence.
+ *
+ *	The method presented here is faster and compresses better
+ *	than lzrw1 and lzrw1-a. I named it lzv for "Lev-Zimpel-Vogt".
+ *	It uses ideas introduced by Ross Williams in his algorithm 
+ *	lzrw1 [R. N. Williams (1991): "An Extremly Fast ZIV-Lempel Data
+ *	Compression Algorithm", Proceedings IEEE Data Compression
+ *	Conference, Snowbird, Utah, 362-371] and by Fiala and Green
+ *	in their algorithm a1 [E. R. Fiala, D. H. Greene (1989): 
+ *	"Data Compression with Finite Windows", Communications of the
+ *	ACM, 4, 490-505]. Because lzv differs strongly from both,
+ *	I hope there will be no	patent problems. The hashing-method 
+ *	has been stolen from Jean-loup Gailly's (patent free) gzip.
+ *	-------
+ *	Copyright (c) 1995 Antoine de Maricourt. Redistribution
+ *	of this file is permitted under the GNU Public License.
+ */
+
+#include <linux/linkage.h>
+
+#define A0	28
+#define A1	32
+#define A2	36
+#define A3	40
+
+	.text
+ENTRY(ext2_LZV1_decompress)
+	pushl %ebp
+	pushl %edi
+	pushl %esi
+	pushl %ebx			# be safe
+	pushl %ecx
+	pushl %edx
+	
+	movl A0(%esp), %esi		# esi = input buffer
+	movl A1(%esp), %edi		# edi = output buffer
+	movl %esi, %ebx
+	addl A2(%esp), %ebx		# ebx = input limit
+	movl %edi, %edx
+	addl A3(%esp), %edx		# edx = output limit
+	xorl %ecx, %ecx
+	xorl %eax, %eax
+	cld
+
+	ALIGN
+
+L10:	cmpl %esi, %ebx			# check for input overflow
+	jna L13
+
+	/*
+	 *	Get the opcode
+	 */
+
+	movb (%esi), %al		# load opcode 
+	testb $7, %al			# test 3 lower bits
+	jne L11
+
+	/*
+	 *	This is a literal
+	 */
+
+	shrb $3, %al			# store its len into ecx
+	incb %al			
+	movb %al, %cl
+	incl %esi
+	rep; movsb
+	cmpl %edx, %edi			# check for output overflow
+	jb L10				# and continue
+	jmp L13
+
+	/*
+	 *	This is a match
+	 */
+
+	ALIGN
+
+L11:	movb %al, %cl
+	andb $7, %cl			# store its len into ecx
+	cmpb $7, %cl	
+	je L12
+
+	/*
+	 *	This is a small match
+	 */
+
+	addl $2, %ecx
+	shll $5, %eax			# store the offset into eax
+	movb 1(%esi), %al
+	notl %eax
+	lea 2(%esi), %ebp		# save next value of esi
+	lea (%edi,%eax), %esi		# load pointer to match
+	cmpl A1(%esp), %esi		# safety check
+	jb L13
+	rep; movsb			# copy the match
+	xorl %eax, %eax
+	movl %ebp, %esi			# restore esi
+	cmpl %edx, %edi			# check for output overflow
+	jb L10				# and continue
+	jmp L13
+
+	/*
+	 *	This is a big match
+	 */
+
+	ALIGN
+
+L12:	movb 1(%esi), %cl
+	addl $9, %ecx
+	shll $5, %eax			# store the offset into eax
+	movb 2(%esi), %al
+	notl %eax
+	lea 3(%esi), %ebp		# save next value of esi
+	lea (%edi,%eax), %esi		# load pointer to match
+	cmpl A1(%esp), %esi		# safety check
+	jb L13
+	rep; movsb			# copy the match
+	xorl %eax, %eax
+	movl %ebp, %esi			# restore esi
+	cmpl %edx, %edi			# check for output overflow
+	jb L10				# and continue
+
+	/*
+	 *	The end.
+	 */
+
+L13:	movl %edi, %eax
+	subl A1(%esp),%eax
+
+	popl %edx
+	popl %ecx
+	popl %ebx
+	popl %esi
+	popl %edi
+	popl %ebp
+	ret
--- linux-2.6.25/fs/ext2/lzv1/e2compr_lzv.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzv1/e2compr_lzv.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,71 @@
+#include <linux/fs.h>
+#include <linux/ext2_fs_c.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include "lzv1.h"
+
+#ifdef MODULE
+MODULE_AUTHOR("Hermann Vogt");
+MODULE_DESCRIPTION("Lev-Zimpel-Vogt algorithm for EXT2 file compression");
+MODULE_LICENSE("GPL");
+#endif
+
+size_t ext2_iLZV1 (int action)
+{
+	switch (action) {
+		case EXT2_ALG_INIT_COMPRESS:
+			return HSIZE * sizeof (unsigned short);
+		case EXT2_ALG_INIT_DECOMPRESS:
+			return 0;
+		default:
+			return 0;
+	}
+}
+
+size_t ext2_wLZV1 (unsigned char *in, unsigned char *out, void *heap, size_t len, size_t out_len, int xarg)
+{
+	int ret_code;
+
+	if (!try_module_get(THIS_MODULE))
+		return 0;
+	ret_code = ext2_LZV1_compress(in, out, heap, len, out_len);
+	module_put(THIS_MODULE);
+	return ret_code;
+}
+
+size_t ext2_rLZV1 (unsigned char *in, unsigned char *out, void *heap, size_t ilen, size_t len, int xarg)
+{
+	int ret_code;
+
+	if (!try_module_get(THIS_MODULE))
+		return 0;
+	ret_code = ext2_LZV1_decompress(in, out, ilen, len);
+	module_put(THIS_MODULE);
+	return ret_code;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	struct ext2_algorithm lzv_alg;
+
+	lzv_alg.name = NULL;
+	lzv_alg.avail = 1;
+	lzv_alg.init = ext2_iLZV1;
+	lzv_alg.compress = ext2_wLZV1;
+	lzv_alg.decompress = ext2_rLZV1;
+
+	return ext2_register_compression_module(EXT2_LZV1_ALG,
+			HSIZE * sizeof(unsigned short),
+			0,
+			&lzv_alg);
+}
+
+void cleanup_module(void)
+{
+	ext2_unregister_compression_module(EXT2_LZV1_ALG);
+}
+
+#endif
+
--- linux-2.6.25/fs/ext2/namei.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/ext2/namei.c	2008-07-31 15:13:12.000000000 +0200
@@ -30,10 +30,19 @@
  *        David S. Miller (davem@caip.rutgers.edu), 1995
  */
 
+/*
+ *  Copyright (C) 2001 Alcatel Business Systems - R&D Illkirch
+ *  Added by Pierre Peiffer (pierre.peiffer@sxb.bsf.alcatel.fr) (2001-08-09)
+ *  (Copied from pjm e2compr-0.4.39-patch-2.2.18 patch)
+ *    For e2compress: GZ_HACK: Do not compress file with .gz or .bz2
+ *    extension (option of kernel config)
+ */
+
 #include <linux/pagemap.h>
 #include "ext2.h"
 #include "xattr.h"
 #include "acl.h"
+#include "debug.h"
 #include "xip.h"
 
 static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
--- linux-2.6.25/fs/ext2/ChangeLog.e2compr-26port	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/ChangeLog.e2compr-26port	2008-08-01 15:59:20.000000000 +0200
@@ -0,0 +1,308 @@
+01 August 2008
+	Matthias Winkler <matthiaswinkler@users.sourceforge.net>
+	* released version 0.4.55
+	* complete code cleanup
+	* changed policy to ALWAYS_LOCKING pages in do_generic_mapping_read()
+	  => completely removed PG_Compr-Flag now!	
+
+31 July 2008
+	Matthias Winkler <matthiaswinkler@users.sourceforge.net>
+	* released version 0.4.54
+	* fixes rare himem bug: only occures if page > cluster in inode.c/readpage()
+	* fixes rare readpage bug in mm/filemap.c/do_generic_mapping_read():
+		PG_Compr flags dissallow reading a page while de/compressing.
+		Setting and unsetting it requires the page lock, with one exception
+		do_generic_mapping_read() in filemap.c. This is done because of performance 
+		reasons. Anyway, a simultaneous call of do_generic_mapping_read() for the SAME
+		page might break the PG_Compr-Mimic.
+
+		Solutions: Always lock any page before reading OR second(n-th) call of
+			   do_generic_mapping_read() busy waits until first is done.
+			   Default is busy wait now, ALWAYS_LOCK implemented as option via define.
+
+25 June 2008
+	Matthias Winkler <matthiaswinkler@users.sourceforge.net>
+	* released version 0.4.53
+	* fixes himem bug: unmapped block in ext2_decompress_cluster()
+	* fixes bdev bug: ext2_get_block() must be called for every block
+	  which cause ooops because of bdev == NULL. ext2_get_block() will
+	  set the correct bdev and the correct blocknumber of the block.
+	  
+	  NEVER assign bdev manually, because the blocknumber might be random then:
+	  	"block->b_bdev = something" (DON'T!)
+		
+	  ALWAYS use:
+	  	if (!buffer_mapped(block)) || (block->b_bdev == NULL)
+			ext2_get_block()
+	  
+	  Bdev bug is closely related to file holes (empty block in a file).
+	  If compressed data will be written to a former hole, then
+	  usually ext2_get_block() must be called with create.
+	  	ext2_get_block( , , , 1 /*create*/).
+
+	* fixed missing include in xattr.h
+	* EXT2_COMPRBLK might be removed during compression if a cluster 
+	  doesn't compress. During compression we re-raise EXT2_COMPRBLK
+	  flag after every cluster now.
+	* added missing export of __pagevec_free to (mm/page_alloc.c)
+	* deny O_DIRECT access mode after open of a file using fcntl()
+	  (in fs/fcntl.c).
+	* file.c: 
+	  Replaced ext2_filew_write() to use kernels generic
+          do_sync_write(). Writing on compressed files calls 
+          ext2_filew_write():
+		- divide write range into clusters
+		- ext2_decompress_cluster (if needed)
+		- do_sync_write()
+ 		- ext2_compress_cluster (if needed)
+	* inode.c:
+	  ext2_writepage()/ext2_writepages() usually writes back
+          dirty pages of an inode. They reside in the kernels page cache.
+	  This pages might e.g. be written/dirtied by a mmap()-ped file.
+	  Also generic_file_aio_write() uses ext2_writepage() finally.
+	  I don't see how the ext2_writepage() would handle compressed 
+          files, so I re-inserted and re-wrote this part of old 2.4 code.
+ 	  Don't know if this code (USE_WRITEPAGE) is needed at all.
+          So I leave it disabled by default. Enabled it might
+          leave compressed files with compression ratio of 100%. 
+	  Don't use yet!	  
+
+17 April 2008
+	Matthias Winkler <matthiaswinkler@users.sourceforge.net>
+	* first patch for kernel 2.6.25 released
+
+20 March 2008
+	Matthias Winkler <matthiaswinkler@users.sourceforge.net>
+	* version 0.4.52: EXT2_COMPRESS_WHEN_CLU didn't work. this
+	  feature enables compression during file write.
+
+15 Oct 2007
+	Matthias Winkler <matthiaswinkler@users.sourceforge.net>
+	* First offical Sourceforge release as version 0.4.51
+	* TODO: figure out what is necessary to enable swap 
+	  suppport for e2compr again (see mm/swapfile.c).	  
+
+27 Sep 2007
+	Matthias Winkler <matthiaswinkler@users.sourceforge.net>
+	* System stalled with a lot of I/O during de-compression of
+	  USB-Sticks, too. I replaced mark_buffer_dirty 
+	  with set_buffer_dirty. This achieves that ONLY the buffers 
+	  and not the pages are marked. Then I write back the 
+	  buffers with ll_rw_block() at the end of 
+	  ext2_decompress_cluster() and ext2_decompress_pages(). 
+	  This should stop flooding the system with dirty pages. 
+	  Because now every routine waits for its newly dirtied buffers.	  
+	  My system with 128MB of RAM is responding much more better during
+	  compression/decompression now. Desompression also seems
+	  to be a bit faster. 
+	  (this change is active with: #ifndef E2C_GENERIC_OSYNC)  
+
+25 Sep 2007
+	Matthias Winkler <matthiaswinkler@users.sourceforge.net>
+	* System stalled with a lot of I/O during compression of
+	  USB-Sticks. Seems generic_osync_inode() should not be 
+	  called in ext2_compress_cluster. Therefore I replaced
+	  it with ll_rw_block() to write the modified blocks
+	  directly back to disk. This gave also a ~100% better
+	  performance for compression.
+	
+9 Sep 2007
+	Matthias Winkler <matthiaswinkler@users.sourceforge.net>
+	* fixed bdev-bug. this bug appeared primarily when
+	  files contained holes. A page with holes, which 
+	  was dirty caused ext2_get_cluster_blocks [ext2_get_block()]
+	  to create ALL blocks of the page, even if there were holes!
+	  These allocated hole-blocks weren't set to 0 anywhere and
+	  therefore contained invalid data. I changed the
+	  code to never allocate these holes.
+	  
+	* ext2_truncate() added again to ext2_compress_cluster for
+	  uncompressed clusters. Fixes filesize errors reported by 
+	  "e2fsck -f /dev/..."	  
+
+24 Aug 2007
+	Matthias Winkler <matthiaswinkler@users.sourceforge.net>
+	
+	Major changes:	
+	* completly ported inode->i_mutex
+	
+	* clever CONFIG_GZ_HACK to reject "uncompressable" files 
+	  (according to their extension) early. The IOCTL in ioctl.c 
+	  which sets the compression on the file already rejects such 
+	  extensions now.
+	 
+	* new create_empty_buffers_e2c() was necessary, because the
+	  "extra"-pages should NOT have a valid i_mapping! Further the
+	  buffers needed to be initalized right.
+	
+	* proper block initalization (bdev-bug) in:
+	   - create_empty_buffers_e2c()
+	   - ext2_get_cluster_blocks
+	   
+	* in file.c copied:  
+	  ...with one single change at ext2_mapping_read in label page_ok:
+	  A new Page-Flag (page-flags.h) the so called "PG_compr"-Flag is 
+	  checked to assure the corresponding page is not under 
+	  compression/decompression. This was necessary because
+	  generic_mapping_read() doesn't lock() the page in ALL cases!!!
+	  Otherwise the generic_mapping_read() would have to lock EVERY page
+	  in the whole system before returning it....
+	  
+	* Fixed HiMem-Support: Balanced ALL kamp/kunmap calls. Unbalanced
+	  functions cause the system to hang at "kmap_himem()" after some 
+	  time. Can be seen with magic-sysctrl "altgr + prtscr + W".
+	
+	* ext2_decompres_cluster() didn't mark uptodate pages for writeback.
+	  Don't know how this method could EVER have worked...
+	  
+	* ext2_compress_cluster() caused an always increasing amount of dirty-pages
+	  (cat /proc/vmstat) which couldn't be wrote back by sync/umount.
+	  I think this was due the ClearPageDirty at the end of ext2_compress_cluster().
+	  
+	* introduced ext2_get_dcount() to savely determine if a file is really "open"
+	  and to abort compression/decompression in such a case. 
+	  
+	* Removed gzip completely and not working assembler code. Replaced by the
+	  kernels built-in zlib, which is pretty the same code... 
+	
+	* New kernel configuration interface
+	 
+	* Rollback of some unecessary "fixes"...
+	
+	TODO:
+	
+	* HiMem-Support:
+	  One might try to use kmap_atomic instead of kamp in ext2_readpage. kmap_atomic
+	  doesn't block and might speed up the regular page reading. might.		
+	
+20 April 2007
+	Andreas:
+		
+	* Replaced GZIP with zlib of the kernel because the assembly versions of existing
+	  compression modules crashed.
+	  
+	* Replaced gzip with the kernel zlib, which is built-in anyway
+	
+	* Initial HiMem-Support. 
+
+
+06 Mar 2007
+
+	Terry Loveall <loveall@iinet.com>
+
+	* adapted linux-2.6.10-e2compr-0.4.45-alpha0126.diff to 2.6.18.5 kernel
+
+	* replaced most instances of down/up(inode->i_sem) with 
+	  lock/unlock(inode->i_mutex). For exception see file.c, below.
+
+	* made various printk regularizations to uniquely identify each printk
+	  instance. Inserted missing KERN_DEBUG and KERN_WARNING.
+
+	* compress.c:
+	  bug fix: ext2_count_blocks: init head_bh for each iteration.
+	  bug fix: ext2_count_blocks: add set clen=ulen for uncompressable clusters.
+	  bug fix: ext2_compress_cluster: replacement and inlining of an 
+	   invalidate_inode_buffers function to keep root filesystem changes
+	   uptodate on disk (prevents umounting root file system to update).
+	  warning fix: ext2_compress_cluster: various variables initialized.
+	  ext2_compress_cluster: removed #ifdef NDEBUG
+	  bug fix: ext2_compress_cluster: defined maxclus, calculate and set for:
+	  bug fix: ext2_compress_cluster: set filesize for uncompressed clusters.
+	  ext2_cleanup_compressed_inode: changed error message to indicate 'Z'
+	   flag was caused by trying to un/compress already open file.
+	  bug fix: cp to compr dir: Truncate uncompressed files to their
+	   uncompressed length, i.e. force kernel to update inode and sb
+
+	* file.c:
+	  removed file->f_error code since f_error no longer in file struct.
+	  ext2_file_write: changed down/up i_sem to down_read/up_read i_alloc_sem
+
+	* inode.c:
+	  bug fix: ext2_get_block: restored changed: loop to bforget
+
+	* ioctl.c:
+	  ext2_ioctl: scrubbed 'B' flag on file uncompress.
+
+	* match[56]86.S:
+	  made code dependent on #ifdef CONFIG_REGPARM to compile with either
+	  register variable or stack variable parameter passing.
+
+28 Feb 2005
+
+	Yabo Ding <bobfree_cn@yahoo.com.cn>,<yding@wyse.com>
+
+	* Corrected page unlocking in inode.c.
+
+19 Feb 2005
+
+	Paul Whittaker <whitpa@users.sourceforge.net>
+
+	* Added corrections le32_to_cpu in critical areas of compress.c
+	* Optimized function exit code in inode.c.
+
+24 Aug 2004
+Yabo Ding <bobfree_cn@yahoo.com.cn>,<yding@wyse.com>
+
+  compress.c
+*  ext2_decompress_pages()
+     The old code cannot reread data from disk to a changed buffers data pointer in 2.6.x.
+     So, I copy memory data(decompressed) to a temporary buffer;
+     Then reread data(compressed) from disk, and copy to head;
+     Then copy back the memory data from temporary buffer.
+     It seems clumsy, but it works well.
+*  ext2_compress_cluster()
+     Force write to disk.
+
+  inode.c
+*  ext2_writepage()
+     Delete old code. All directly call block_write_full_page() function.
+
+* ../Kconfig
+    Change e2compr config as a submenu config
+
+04 Aug 2004
+
+Paul Whittaker <whitpa@users.sourceforge.net>
+
+* compress.c: replaced mark_buffer_dirty(x,y) with mark_buffer_dirty(x).  I'm
+  still not at all sure that this is sufficient.
+
+03 Aug 2004
+
+Paul Whittaker <whitpa@users.sourceforge.net>
+
+* ../../include/linux/ext2_fs_c.h: added missing prototypes for ext2_iLZRW3A(),
+  ext2_iLZRW3A(), ext2_rLZRW3A().
+
+02 Aug 2004
+
+Paul Whittaker <whitpa@users.sourceforge.net>
+
+* ../../mm/page_alloc.c: added EXPORT_SYMBOL(__pagevec_free).
+
+* ../../include/linux/pagemap.h, ../../mm/filemap.c: removed inline from
+  __grab_cache_page() declarations, added EXPORT_SYMBOL(__grab_cache_page).
+
+* ../../include/linux/mm.h, ../../mm/filemap.c: removed inline from
+  page_waitqueue() declarations, added EXPORT_SYMBOL(page_waitqueue).
+
+* bzip2/{lib_bzip_d,lib_bzip_e}.c, {gzip,lzo,lzrw3a,lzv1}/e2compr*.c:
+  replaced MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT with try_module_get()
+  and module_put() to avoid deprecation and safety warnings.
+
+* lzrw3a/lzrw3a.c: added (UBYTE *) casts to avoid compiler warnings.
+
+* compress.c, inode.c: incorporated Yabo's changes, correcting mistakes in
+  ext2_readpages() in inode.c.
+
+* removed printks for ext2_discard_prealloc from file.c and inode.c (not
+  needed now that this problem has been resolved).
+
+2.6.5 -> 2.6.7 updates:
+
+* ../../mm/filemap.c: rewrote CONFIG_EXT2_COMPRESS hunk for 2.6.7.
+
+* compress.c, file.c: use mapping_mapped(), since mapping->i_mmap has changed
+  and mapping->i_mmap_shared no longer exists.
+
+* inode.c: page->count becomes page->_count.
--- linux-2.6.25/fs/ext2/assert.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/assert.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,73 @@
+/* Assertion facility for the kernel.
+
+   todo: Provide some way of releasing resources when an assertion
+   fails.
+
+   --- Taken from /usr/include/assert.h.  Some things (such as ANSI /
+   SysV conformance) have been removed.  <sys/cdefs.h> macros have
+   been inlined. --- */
+
+/* Copyright (C) 1991, 1992, 1994, 1995 Free Software Foundation, Inc.
+This file (assert.h) is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the, 1992 Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifdef	_ASSERT_H
+# undef	_ASSERT_H
+# undef	assert
+#else
+/* This prints an "Assertion failed" message and aborts.  */
+static void __assert_fail (__const char *__assertion,
+			   __const char *__file,
+			   unsigned int __line,
+			   __const char *__function)
+     __attribute__ ((/*__noreturn__, */__unused__));
+
+static void
+__assert_fail(char const *assertion,
+	      char const *file,
+	      unsigned line,
+	      char const *function) 
+{
+	printk(KERN_CRIT "%s:%u: %s: Assertion `%s' failed.\n",
+	       file, line, function, assertion);
+	/* pjm: I don't want this in released code:
+           *(volatile int *)0 = 0; */
+}
+#endif /* !ASSERT_H */
+
+#define	_ASSERT_H	1
+
+#ifdef	NDEBUG
+# define assert(expr)		((void) 0)
+#else /* Not NDEBUG.  */
+
+# define assert(expr)							      \
+  ((void) ((expr) ||							      \
+	   (__assert_fail (#expr,					      \
+			   __FILE__, __LINE__, __ASSERT_FUNCTION), 0)))
+
+
+/* Version 2.4 and later of GCC define a magical variable `__PRETTY_FUNCTION__'
+   which contains the name of the function currently being defined.
+   This is broken in G++ before version 2.6.  */
+# if (!defined (__GNUC__) || __GNUC__ < 2 || \
+     __GNUC_MINOR__ < (defined (__cplusplus) ? 6 : 4))
+#  define __ASSERT_FUNCTION	((__const char *) 0)
+# else
+#  define __ASSERT_FUNCTION	__PRETTY_FUNCTION__
+# endif
+#endif /* NDEBUG.  */
--- linux-2.6.25/fs/ext2/none.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/none.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,5 @@
+#include <linux/types.h>
+
+size_t ext2_iNONE (int action) { return 0; }
+size_t ext2_rNONE (__u8 *ibuf, __u8 *obuf, void *wa, size_t ilen, size_t olen, int xarg) { return 0; }
+size_t ext2_wNONE (__u8 *ibuf, __u8 *obuf, void *wa, size_t ilen, size_t olen, int xarg) { return 0; }
--- linux-2.6.25/fs/ext2/adler32.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/adler32.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,43 @@
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* @(#) $Id: e2compr2.6.25.patch,v 1.1.2.1 2008/04/17 09:49:32 winkler Exp $ */
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i)  {s1 += buf[i]; s2 += s1;}
+#define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf)   DO8(buf,0); DO8(buf,8);
+
+/* ========================================================================= */
+unsigned long ext2_adler32(unsigned long adler, const unsigned char *buf, unsigned int len)
+{
+    unsigned long s1 = adler & 0xffff;
+    unsigned long s2 = (adler >> 16) & 0xffff;
+    int k;
+
+    if (buf == 0) return 1L;
+
+    while (len > 0) {
+        k = len < NMAX ? len : NMAX;
+        len -= k;
+        while (k >= 16) {
+            DO16(buf);
+	    buf += 16;
+            k -= 16;
+        }
+        if (k != 0) do {
+            s1 += *buf++;
+	    s2 += s1;
+        } while (--k);
+        s1 %= BASE;
+        s2 %= BASE;
+    }
+    return (s2 << 16) | s1;
+}
--- linux-2.6.25/fs/ext2/lzo/lzo_ptr.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/lzo_ptr.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,202 @@
+/* lzo_ptr.h -- low-level pointer constructs
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   markus.oberhumer@jk.uni-linz.ac.at
+ */
+
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the library and is subject
+   to change.
+ */
+
+
+#ifndef __LZO_PTR_H
+#define __LZO_PTR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* This is the lowest part of the LZO library.
+ * It deals with pointer representations at bit level.
+ */
+
+
+/***********************************************************************
+// Includes
+************************************************************************/
+
+#if defined(__LZO_DOS16) || defined(__LZO_WIN16)
+#  include <dos.h>
+#  if 1 && defined(__WATCOMC__)
+#    include <i86.h>
+     __LZO_EXTERN_C unsigned char _HShift;
+#    define __LZO_HShift	_HShift
+#  elif 1 && defined(_MSC_VER)
+     __LZO_EXTERN_C unsigned short __near _AHSHIFT;
+#    define __LZO_HShift	((unsigned) &_AHSHIFT)
+#  elif defined(__LZO_WIN16)
+#    define __LZO_HShift	3
+#  else
+#    define __LZO_HShift	12
+#  endif
+#  if !defined(_FP_SEG) && defined(FP_SEG)
+#    define _FP_SEG			FP_SEG
+#  endif
+#  if !defined(_FP_OFF) && defined(FP_OFF)
+#    define _FP_OFF			FP_OFF
+#  endif
+#endif
+
+
+/***********************************************************************
+// Integral types
+************************************************************************/
+
+/* ptrdiff_t */
+#if (UINT_MAX >= 0xffffffffL)
+   typedef ptrdiff_t        	lzo_ptrdiff_t;
+#else
+   typedef long             	lzo_ptrdiff_t;
+#endif
+
+
+/* Unsigned type that has *exactly* the same number of bits as a lzo_voidp */
+#if !defined(__LZO_HAVE_PTR_T)
+#  if defined(lzo_ptr_t)
+#    define __LZO_HAVE_PTR_T
+#  endif
+#endif
+#if !defined(__LZO_HAVE_PTR_T)
+#  if defined(SIZEOF_CHAR_P) && defined(SIZEOF_UNSIGNED_LONG)
+#    if (SIZEOF_CHAR_P == SIZEOF_UNSIGNED_LONG)
+       typedef unsigned long  	lzo_ptr_t;
+       typedef long           	lzo_sptr_t;
+#      define __LZO_HAVE_PTR_T
+#    endif
+#  endif
+#endif
+#if !defined(__LZO_HAVE_PTR_T)
+#  if defined(SIZEOF_CHAR_P) && defined(SIZEOF_UNSIGNED)
+#    if (SIZEOF_CHAR_P == SIZEOF_UNSIGNED)
+       typedef unsigned int   	lzo_ptr_t;
+       typedef int            	lzo_sptr_t;
+#      define __LZO_HAVE_PTR_T
+#    endif
+#  endif
+#endif
+#if !defined(__LZO_HAVE_PTR_T)
+#  if defined(SIZEOF_CHAR_P) && defined(SIZEOF_UNSIGNED_SHORT)
+#    if (SIZEOF_CHAR_P == SIZEOF_UNSIGNED_SHORT)
+       typedef unsigned short 	lzo_ptr_t;
+       typedef short          	lzo_sptr_t;
+#      define __LZO_HAVE_PTR_T
+#    endif
+#  endif
+#endif
+#if !defined(__LZO_HAVE_PTR_T)
+#  if defined(LZO_HAVE_CONFIG_H) || defined(SIZEOF_CHAR_P)
+#    error "no suitable type for lzo_ptr_t"
+#  else
+     typedef unsigned long  	lzo_ptr_t;
+     typedef long           	lzo_sptr_t;
+#    define __LZO_HAVE_PTR_T
+#  endif
+#endif
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+/* Always use the safe (=integral) version for pointer-comparisions.
+ * The compiler should optimize away the additional casts anyway.
+ *
+ * Note that this only works if the representation and ordering
+ * of the pointer and the integral is the same (at bit level).
+ *
+ * Most 16 bit compilers have their own view about pointers -
+ * fortunately they don't care about comparing pointers
+ * that are pointing to Nirvana.
+ */
+
+#if defined(__LZO_DOS16) || defined(__LZO_WIN16)
+#define PTR(a)				((lzo_bytep) (a))
+/* only need the low bits of the pointer -> offset is ok */
+#define PTR_ALIGNED_4(a)	((_FP_OFF(a) & 3) == 0)
+#define PTR_ALIGNED2_4(a,b)	(((_FP_OFF(a) | _FP_OFF(b)) & 3) == 0)
+#else
+#define PTR(a)				((lzo_ptr_t) (a))
+#define PTR_LINEAR(a)		PTR(a)
+#define PTR_ALIGNED_4(a)	((PTR_LINEAR(a) & 3) == 0)
+#define PTR_ALIGNED_8(a)	((PTR_LINEAR(a) & 7) == 0)
+#define PTR_ALIGNED2_4(a,b)	(((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0)
+#define PTR_ALIGNED2_8(a,b)	(((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0)
+#endif
+
+#define PTR_LT(a,b)			(PTR(a) < PTR(b))
+#define PTR_GE(a,b)			(PTR(a) >= PTR(b))
+#define PTR_DIFF(a,b)		((lzo_ptrdiff_t) (PTR(a) - PTR(b)))
+
+
+LZO_EXTERN(lzo_ptr_t)
+__lzo_ptr_linear(const lzo_voidp ptr);
+
+
+typedef union
+{
+	char			a_char;
+	unsigned char	a_uchar;
+	short			a_short;
+	unsigned short	a_ushort;
+	int				a_int;
+	unsigned int	a_uint;
+	long			a_long;
+	unsigned long	a_ulong;
+	lzo_int			a_lzo_int;
+	lzo_uint		a_lzo_uint;
+	lzo_int32		a_lzo_int32;
+	lzo_uint32		a_lzo_uint32;
+	ptrdiff_t		a_ptrdiff_t;
+	lzo_ptrdiff_t	a_lzo_ptrdiff_t;
+	lzo_ptr_t		a_lzo_ptr_t;
+	char *			a_charp;
+	lzo_bytep		a_lzo_bytep;
+	lzo_bytepp		a_lzo_bytepp;
+}
+lzo_align_t;
+
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* already included */
+
+/*
+vi:ts=4
+*/
+
--- linux-2.6.25/fs/ext2/lzo/lzo1x_d.ch	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/lzo1x_d.ch	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,443 @@
+/* lzo1x_d.ch -- implementation of the LZO1X decompression algorithm
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   markus.oberhumer@jk.uni-linz.ac.at
+ */
+
+
+#include "lzo1_d.ch"
+
+
+/***********************************************************************
+// decompress a block of data.
+************************************************************************/
+
+#if defined(DO_DECOMPRESS)
+LZO_PUBLIC(int)
+DO_DECOMPRESS  ( const lzo_byte *in , lzo_uint  in_len,
+                       lzo_byte *out, lzo_uint *out_len,
+                       lzo_voidp wrkmem )
+#endif
+{
+	register lzo_byte *op;
+	register const lzo_byte *ip;
+	register lzo_uint t;
+#if defined(COPY_DICT)
+	lzo_uint m_off;
+	const lzo_byte *dict_end;
+#else
+	register const lzo_byte *m_pos;
+#endif
+
+	const lzo_byte * const ip_end = in + in_len;
+#if defined(HAVE_ANY_OP)
+	lzo_byte * const op_end = out + *out_len;
+#endif
+#if defined(LZO1Z)
+	lzo_uint last_m_off = 0;
+#endif
+
+	LZO_UNUSED(wrkmem);
+
+#if defined(__LZO_QUERY_DECOMPRESS)
+	if (__LZO_IS_DECOMPRESS_QUERY(in,in_len,out,out_len,wrkmem))
+		return __LZO_QUERY_DECOMPRESS(in,in_len,out,out_len,wrkmem,0,0);
+#endif
+
+#if defined(COPY_DICT)
+	if (dict)
+	{
+		if (dict_len > M4_MAX_OFFSET)
+		{
+			dict += dict_len - M4_MAX_OFFSET;
+			dict_len = M4_MAX_OFFSET;
+		}
+		dict_end = dict + dict_len;
+	}
+	else
+	{
+		dict_len = 0;
+		dict_end = NULL;
+	}
+#endif /* COPY_DICT */
+
+	*out_len = 0;
+
+	op = out;
+	ip = in;
+
+	if (*ip > 17)
+	{
+		t = *ip++ - 17;
+		if (t < 4)
+			goto match_next;
+		assert(t > 0); NEED_OP(t); NEED_IP(t+1);
+		do *op++ = *ip++; while (--t > 0);
+		goto first_literal_run;
+	}
+
+	while (TEST_IP && TEST_OP)
+	{
+		t = *ip++;
+		if (t >= 16)
+			goto match;
+		/* a literal run */
+		if (t == 0)
+		{
+			NEED_IP(1);
+			while (*ip == 0)
+			{
+				t += 255;
+				ip++;
+				NEED_IP(1);
+			}
+			t += 15 + *ip++;
+		}
+		/* copy literals */
+		assert(t > 0); NEED_OP(t+3); NEED_IP(t+4);
+#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
+#if !defined(LZO_UNALIGNED_OK_4)
+		if (PTR_ALIGNED2_4(op,ip))
+		{
+#endif
+		* (lzo_uint32p) op = * (const lzo_uint32p) ip;
+		op += 4; ip += 4;
+		if (--t > 0)
+		{
+			if (t >= 4)
+			{
+				do {
+					* (lzo_uint32p) op = * (const lzo_uint32p) ip;
+					op += 4; ip += 4; t -= 4;
+				} while (t >= 4);
+				if (t > 0) do *op++ = *ip++; while (--t > 0);
+			}
+			else
+				do *op++ = *ip++; while (--t > 0);
+		}
+#if !defined(LZO_UNALIGNED_OK_4)
+		}
+		else
+#endif
+#endif
+#if !defined(LZO_UNALIGNED_OK_4)
+		{
+			*op++ = *ip++; *op++ = *ip++; *op++ = *ip++;
+			do *op++ = *ip++; while (--t > 0);
+		}
+#endif
+
+
+first_literal_run:
+
+
+		t = *ip++;
+		if (t >= 16)
+			goto match;
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+		m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2);
+		last_m_off = m_off;
+#else
+		m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2);
+#endif
+		NEED_OP(3);
+		t = 3; COPY_DICT(t,m_off)
+#else /* !COPY_DICT */
+#if defined(LZO1Z)
+		t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2);
+		m_pos = op - t;
+		last_m_off = t;
+#else
+		m_pos = op - (1 + M2_MAX_OFFSET);
+		m_pos -= t >> 2;
+		m_pos -= *ip++ << 2;
+#endif
+		TEST_LOOKBEHIND(m_pos,out); NEED_OP(3);
+		*op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos;
+#endif /* COPY_DICT */
+		goto match_done;
+
+
+		/* handle matches */
+		while (TEST_IP && TEST_OP)
+		{
+match:
+			if (t >= 64)				/* a M2 match */
+			{
+#if defined(COPY_DICT)
+#if defined(LZO1X)
+				m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3);
+				t = (t >> 5) - 1;
+#elif defined(LZO1Y)
+				m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2);
+				t = (t >> 4) - 3;
+#elif defined(LZO1Z)
+				m_off = t & 0x1f;
+				if (m_off >= 0x1c)
+					m_off = last_m_off;
+				else
+				{
+					m_off = 1 + (m_off << 6) + (*ip++ >> 2);
+					last_m_off = m_off;
+				}
+				t = (t >> 5) - 1;
+#endif
+#else /* !COPY_DICT */
+#if defined(LZO1X)
+				m_pos = op - 1;
+				m_pos -= (t >> 2) & 7;
+				m_pos -= *ip++ << 3;
+				t = (t >> 5) - 1;
+#elif defined(LZO1Y)
+				m_pos = op - 1;
+				m_pos -= (t >> 2) & 3;
+				m_pos -= *ip++ << 2;
+				t = (t >> 4) - 3;
+#elif defined(LZO1Z)
+				{
+					lzo_uint off = t & 0x1f;
+					m_pos = op;
+					if (off >= 0x1c)
+					{
+						assert(last_m_off > 0);
+						m_pos -= last_m_off;
+					}
+					else
+					{
+						off = 1 + (off << 6) + (*ip++ >> 2);
+						m_pos -= off;
+						last_m_off = off;
+					}
+				}
+				t = (t >> 5) - 1;
+#endif
+				TEST_LOOKBEHIND(m_pos,out); assert(t > 0); NEED_OP(t+3-1);
+				goto copy_match;
+#endif /* COPY_DICT */
+			}
+			else if (t >= 32)			/* a M3 match */
+			{
+				t &= 31;
+				if (t == 0)
+				{
+					NEED_IP(1);
+					while (*ip == 0)
+					{
+						t += 255;
+						ip++;
+						NEED_IP(1);
+					}
+					t += 31 + *ip++;
+				}
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+				m_off = 1 + (ip[0] << 6) + (ip[1] >> 2);
+				last_m_off = m_off;
+#else
+				m_off = 1 + (ip[0] >> 2) + (ip[1] << 6);
+#endif
+#else /* !COPY_DICT */
+#if defined(LZO1Z)
+				{
+					lzo_uint off = 1 + (ip[0] << 6) + (ip[1] >> 2);
+					m_pos = op - off;
+					last_m_off = off;
+				}
+#elif defined(LZO_UNALIGNED_OK_2) && (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN)
+				m_pos = op - 1;
+				m_pos -= (* (const lzo_ushortp) ip) >> 2;
+#else
+				m_pos = op - 1;
+				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+#endif
+#endif /* COPY_DICT */
+				ip += 2;
+			}
+			else if (t >= 16)			/* a M4 match */
+			{
+#if defined(COPY_DICT)
+				m_off = (t & 8) << 11;
+#else /* !COPY_DICT */
+				m_pos = op;
+				m_pos -= (t & 8) << 11;
+#endif /* COPY_DICT */
+				t &= 7;
+				if (t == 0)
+				{
+					NEED_IP(1);
+					while (*ip == 0)
+					{
+						t += 255;
+						ip++;
+						NEED_IP(1);
+					}
+					t += 7 + *ip++;
+				}
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+				m_off += (ip[0] << 6) + (ip[1] >> 2);
+#else
+				m_off += (ip[0] >> 2) + (ip[1] << 6);
+#endif
+				ip += 2;
+				if (m_off == 0)
+					goto eof_found;
+				m_off += 0x4000;
+#if defined(LZO1Z)
+				last_m_off = m_off;
+#endif
+#else /* !COPY_DICT */
+#if defined(LZO1Z)
+				m_pos -= (ip[0] << 6) + (ip[1] >> 2);
+#elif defined(LZO_UNALIGNED_OK_2) && (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN)
+				m_pos -= (* (const lzo_ushortp) ip) >> 2;
+#else
+				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+#endif
+				ip += 2;
+				if (m_pos == op)
+					goto eof_found;
+				m_pos -= 0x4000;
+#if defined(LZO1Z)
+				last_m_off = op - m_pos;
+#endif
+#endif /* COPY_DICT */
+			}
+			else							/* a M1 match */
+			{
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+				m_off = 1 + (t << 6) + (*ip++ >> 2);
+				last_m_off = m_off;
+#else
+				m_off = 1 + (t >> 2) + (*ip++ << 2);
+#endif
+				NEED_OP(2);
+				t = 2; COPY_DICT(t,m_off)
+#else /* !COPY_DICT */
+#if defined(LZO1Z)
+				t = 1 + (t << 6) + (*ip++ >> 2);
+				m_pos = op - t;
+				last_m_off = t;
+#else
+				m_pos = op - 1;
+				m_pos -= t >> 2;
+				m_pos -= *ip++ << 2;
+#endif
+				TEST_LOOKBEHIND(m_pos,out); NEED_OP(2);
+				*op++ = *m_pos++; *op++ = *m_pos;
+#endif /* COPY_DICT */
+				goto match_done;
+			}
+
+			/* copy match */
+#if defined(COPY_DICT)
+
+			NEED_OP(t+3-1);
+			t += 3-1; COPY_DICT(t,m_off)
+
+#else /* !COPY_DICT */
+
+			TEST_LOOKBEHIND(m_pos,out); assert(t > 0); NEED_OP(t+3-1);
+#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
+#if !defined(LZO_UNALIGNED_OK_4)
+			if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos))
+			{
+				assert((op - m_pos) >= 4);	/* both pointers are aligned */
+#else
+			if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4)
+			{
+#endif
+				* (lzo_uint32p) op = * (const lzo_uint32p) m_pos;
+				op += 4; m_pos += 4; t -= 4 - (3 - 1);
+				do {
+					* (lzo_uint32p) op = * (const lzo_uint32p) m_pos;
+					op += 4; m_pos += 4; t -= 4;
+				} while (t >= 4);
+				if (t > 0) do *op++ = *m_pos++; while (--t > 0);
+			}
+			else
+#endif
+			{
+copy_match:
+				*op++ = *m_pos++; *op++ = *m_pos++;
+				do *op++ = *m_pos++; while (--t > 0);
+			}
+
+#endif /* COPY_DICT */
+
+match_done:
+#if defined(LZO1Z)
+			t = ip[-1] & 3;
+#else
+			t = ip[-2] & 3;
+#endif
+			if (t == 0)
+				break;
+
+			/* copy literals */
+match_next:
+			assert(t > 0); NEED_OP(t); NEED_IP(t+1);
+			do *op++ = *ip++; while (--t > 0);
+			t = *ip++;
+		}
+	}
+
+#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP)
+	/* no EOF code was found */
+	*out_len = op - out;
+	return LZO_E_EOF_NOT_FOUND;
+#endif
+
+eof_found:
+	assert(t == 1);
+	*out_len = op - out;
+	return (ip == ip_end ? LZO_E_OK :
+	       (ip < ip_end  ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+
+
+#if defined(HAVE_NEED_IP)
+input_overrun:
+	*out_len = op - out;
+	return LZO_E_INPUT_OVERRUN;
+#endif
+
+#if defined(HAVE_NEED_OP)
+output_overrun:
+	*out_len = op - out;
+	return LZO_E_OUTPUT_OVERRUN;
+#endif
+
+#if defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND)
+lookbehind_overrun:
+	*out_len = op - out;
+	return LZO_E_LOOKBEHIND_OVERRUN;
+#endif
+}
+
+
+/*
+vi:ts=4
+*/
+
--- linux-2.6.25/fs/ext2/lzo/lzo1x_d1.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/lzo1x_d1.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,32 @@
+/* lzo1x_d1.c -- LZO1X decompression
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   markus.oberhumer@jk.uni-linz.ac.at
+ */
+
+
+#include "config1x.h"
+
+#undef LZO_TEST_DECOMPRESS_OVERRUN
+#define DO_DECOMPRESS		lzo1x_decompress
+
+#include "lzo1x_d.ch"
--- linux-2.6.25/fs/ext2/lzo/lzo_conf.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/lzo_conf.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,409 @@
+/* lzo_conf.h -- main internal configuration file for the the LZO library
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   markus.oberhumer@jk.uni-linz.ac.at
+ */
+
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the library and is subject
+   to change.
+ */
+
+
+#ifndef __LZO_CONF_H
+#define __LZO_CONF_H
+
+#if !defined(__LZO_IN_MINILZO)
+#  ifndef __LZOCONF_H
+#    include <lzoconf.h>
+#  endif
+#endif
+
+
+/***********************************************************************
+// autoconf section
+************************************************************************/
+
+#if 1					// we're in the kernel, so no libc
+#  define HAVE_MEMCMP
+#  define HAVE_MEMCPY
+#  define HAVE_MEMMOVE
+#  define HAVE_MEMSET
+#endif
+
+/* #if !defined(LZO_HAVE_CONFIG_H) */
+#if 0
+#  include <stddef.h>			/* ptrdiff_t, size_t */
+#  include <string.h>			/* memcpy, memmove, memcmp, memset */
+#  if !defined(NO_STDLIB_H)
+#    include <stdlib.h>
+#  endif
+/* #else */
+#  include <sys/types.h>
+#  if defined(STDC_HEADERS)
+#    include <string.h>
+#    include <stdlib.h>
+#  endif
+#  if defined(HAVE_STDDEF_H)
+#    include <stddef.h>
+#  endif
+#  if defined(HAVE_MEMORY_H)
+#    include <memory.h>
+#  endif
+#endif
+
+#if defined(__LZO_DOS16) || defined(__LZO_WIN16)
+#  define HAVE_MALLOC_H
+#  define HAVE_HALLOC
+#endif
+
+
+#undef NDEBUG
+#if !defined(LZO_DEBUG)
+#  define NDEBUG
+#endif
+#if defined(LZO_DEBUG) || !defined(NDEBUG)
+#  if !defined(NO_STDIO_H)
+#    include <stdio.h>
+#  endif
+#endif
+
+
+#if defined(__BOUNDS_CHECKING_ON)
+#  include <unchecked.h>
+#else
+#  define BOUNDS_CHECKING_OFF_DURING(stmt)		stmt
+#  define BOUNDS_CHECKING_OFF_IN_EXPR(expr)		(expr)
+#endif
+
+
+#if !defined(LZO_UNUSED)
+#  define LZO_UNUSED(parm)	(parm = parm)
+#endif
+
+
+#if !defined(__inline__) && !defined(__GNUC__)
+#  if defined(__cplusplus)
+#    define __inline__		inline
+#  else
+#    define __inline__		/* nothing */
+#  endif
+#endif
+
+
+#if defined(NO_MEMCMP)
+#  undef HAVE_MEMCMP
+#endif
+
+#if !defined(HAVE_MEMCMP)
+#  undef memcmp
+#  define memcmp	lzo_memcmp
+#endif
+#if !defined(HAVE_MEMCPY)
+#  undef memcpy
+#  define memcpy	lzo_memcpy
+#endif
+#if !defined(HAVE_MEMMOVE)
+#  undef memmove
+#  define memmove	lzo_memmove
+#endif
+#if !defined(HAVE_MEMSET)
+#  undef memset
+#  define memset	lzo_memset
+#endif
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+#if 1
+#  define LZO_BYTE(x)		((unsigned char) (x))
+#else
+#  define LZO_BYTE(x)		((unsigned char) ((x) & 0xff))
+#endif
+#if 0
+#  define LZO_USHORT(x)		((unsigned short) (x))
+#else
+#  define LZO_USHORT(x)		((unsigned short) ((x) & 0xffff))
+#endif
+
+#define LZO_MAX(a,b)		((a) >= (b) ? (a) : (b))
+#define LZO_MIN(a,b)		((a) <= (b) ? (a) : (b))
+#define LZO_MAX3(a,b,c)		((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c))
+#define LZO_MIN3(a,b,c)		((a) <= (b) ? LZO_MIN(a,c) : LZO_MIN(b,c))
+
+#define lzo_sizeof(type)	((lzo_uint) (sizeof(type)))
+
+#define LZO_HIGH(array)		((lzo_uint) (sizeof(array)/sizeof(*(array))))
+
+/* this always fits into 16 bits */
+#define LZO_SIZE(bits)		(1u << (bits))
+#define LZO_MASK(bits)		(LZO_SIZE(bits) - 1)
+
+#define LZO_LSIZE(bits)		(1ul << (bits))
+#define LZO_LMASK(bits)		(LZO_LSIZE(bits) - 1)
+
+#define LZO_USIZE(bits)		((lzo_uint) 1 << (bits))
+#define LZO_UMASK(bits)		(LZO_USIZE(bits) - 1)
+
+/* Maximum value of a signed/unsigned type.
+   Do not use casts, avoid overflows ! */
+#define LZO_STYPE_MAX(b)	(((1l  << (8*(b)-2)) - 1l)  + (1l  << (8*(b)-2)))
+#define LZO_UTYPE_MAX(b)	(((1ul << (8*(b)-1)) - 1ul) + (1ul << (8*(b)-1)))
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+#if !defined(SIZEOF_UNSIGNED)
+#  if (UINT_MAX == 0xffff)
+#    define SIZEOF_UNSIGNED			2
+#  elif (UINT_MAX == 0xffffffffL)
+#    define SIZEOF_UNSIGNED			4
+#  elif (UINT_MAX >= 0xffffffffL)
+#    define SIZEOF_UNSIGNED			8
+#  else
+#    error SIZEOF_UNSIGNED
+#  endif
+#endif
+
+#if !defined(SIZEOF_UNSIGNED_LONG)
+#  if (ULONG_MAX == 0xffffffffL)
+#    define SIZEOF_UNSIGNED_LONG	4
+#  elif (ULONG_MAX >= 0xffffffffL)
+#    define SIZEOF_UNSIGNED_LONG	8
+#  else
+#    error SIZEOF_UNSIGNED_LONG
+#  endif
+#endif
+
+
+#if !defined(SIZEOF_SIZE_T)
+#  define SIZEOF_SIZE_T				SIZEOF_UNSIGNED
+#endif
+#if !defined(SIZE_T_MAX)
+#  define SIZE_T_MAX				LZO_UTYPE_MAX(SIZEOF_SIZE_T)
+#endif
+
+
+/***********************************************************************
+// compiler and architecture specific stuff
+************************************************************************/
+
+/* Some defines that indicate if memory can be accessed at unaligned
+ * memory addresses. You should also test that this is actually faster
+ * even if it is allowed by your system.
+ */
+
+#if 1 && defined(__LZO_i386) && (UINT_MAX == 0xffffffffL)
+#  if !defined(LZO_UNALIGNED_OK_2) && (USHRT_MAX == 0xffff)
+#    define LZO_UNALIGNED_OK_2
+#  endif
+#  if !defined(LZO_UNALIGNED_OK_4) && (LZO_UINT32_MAX == 0xffffffffL)
+#    define LZO_UNALIGNED_OK_4
+#  endif
+#endif
+
+#if defined(LZO_UNALIGNED_OK_2) || defined(LZO_UNALIGNED_OK_4)
+#  if !defined(LZO_UNALIGNED_OK)
+#    define LZO_UNALIGNED_OK
+#  endif
+#endif
+
+#if defined(__LZO_NO_UNALIGNED)
+#  undef LZO_UNALIGNED_OK
+#  undef LZO_UNALIGNED_OK_2
+#  undef LZO_UNALIGNED_OK_4
+#endif
+
+#if defined(LZO_UNALIGNED_OK_2) && (USHRT_MAX != 0xffff)
+#  error "LZO_UNALIGNED_OK_2 must not be defined on this system"
+#endif
+#if defined(LZO_UNALIGNED_OK_4) && (LZO_UINT32_MAX != 0xffffffffL)
+#  error "LZO_UNALIGNED_OK_4 must not be defined on this system"
+#endif
+
+
+/* Many modern processors can transfer 32bit words much faster than
+ * bytes - this can significantly speed decompression.
+ */
+
+#if defined(__LZO_NO_ALIGNED)
+#  undef LZO_ALIGNED_OK_4
+#endif
+
+#if defined(LZO_ALIGNED_OK_4) && (LZO_UINT32_MAX != 0xffffffffL)
+#  error "LZO_ALIGNED_OK_4 must not be defined on this system"
+#endif
+
+
+/* Definitions for byte order, according to significance of bytes, from low
+ * addresses to high addresses. The value is what you get by putting '4'
+ * in the most significant byte, '3' in the second most significant byte,
+ * '2' in the second least significant byte, and '1' in the least
+ * significant byte.
+ * The byte order is only needed if we use LZO_UNALIGNED_OK.
+ */
+
+#define	LZO_LITTLE_ENDIAN		1234
+#define	LZO_BIG_ENDIAN			4321
+#define	LZO_PDP_ENDIAN			3412
+
+#if !defined(LZO_BYTE_ORDER)
+#  if defined(MFX_BYTE_ORDER)
+#    define LZO_BYTE_ORDER		MFX_BYTE_ORDER
+#  elif defined(__LZO_i386)
+#    define LZO_BYTE_ORDER		LZO_LITTLE_ENDIAN
+#  elif defined(BYTE_ORDER)
+#    define LZO_BYTE_ORDER		BYTE_ORDER
+#  elif defined(__BYTE_ORDER)
+#    define LZO_BYTE_ORDER		__BYTE_ORDER
+#  endif
+#endif
+
+#if defined(LZO_BYTE_ORDER)
+#  if (LZO_BYTE_ORDER != LZO_LITTLE_ENDIAN) && \
+      (LZO_BYTE_ORDER != LZO_BIG_ENDIAN)
+#    error "invalid LZO_BYTE_ORDER"
+#  endif
+#endif
+
+#if defined(LZO_UNALIGNED_OK) && !defined(LZO_BYTE_ORDER)
+#  error "LZO_BYTE_ORDER is not defined"
+#endif
+
+
+/***********************************************************************
+// optimization
+************************************************************************/
+
+/* gcc 2.6.3 and gcc 2.7.2 have a bug with 'register xxx __asm__("%yyy")' */
+#define LZO_OPTIMIZE_GNUC_i386_IS_BUGGY
+
+/* Help the gcc optimizer with register allocation. */
+#if defined(NDEBUG) && !defined(LZO_DEBUG) && !defined(__BOUNDS_CHECKING_ON)
+#  if defined(__GNUC__) && defined(__i386__)
+#    if !defined(LZO_OPTIMIZE_GNUC_i386_IS_BUGGY)
+#      define LZO_OPTIMIZE_GNUC_i386
+#    endif
+#  endif
+#endif
+
+
+/***********************************************************************
+// some globals
+************************************************************************/
+
+__LZO_EXTERN_C int __lzo_init_done;
+__LZO_EXTERN_C const lzo_byte __lzo_copyright[];
+LZO_EXTERN(const lzo_byte *) lzo_copyright(void);
+__LZO_EXTERN_C const lzo_uint32 _lzo_crc32_table[256];
+
+
+/***********************************************************************
+// ANSI C preprocessor macros
+************************************************************************/
+
+#define _LZO_STRINGIZE(x)			#x
+#define _LZO_MEXPAND(x)				_LZO_STRINGIZE(x)
+
+/* concatenate */
+#define _LZO_CONCAT2(a,b)			a ## b
+#define _LZO_CONCAT3(a,b,c)			a ## b ## c
+#define _LZO_CONCAT4(a,b,c,d)		a ## b ## c ## d
+#define _LZO_CONCAT5(a,b,c,d,e)		a ## b ## c ## d ## e
+
+/* expand and concatenate (by using one level of indirection) */
+#define _LZO_ECONCAT2(a,b)			_LZO_CONCAT2(a,b)
+#define _LZO_ECONCAT3(a,b,c)		_LZO_CONCAT3(a,b,c)
+#define _LZO_ECONCAT4(a,b,c,d)		_LZO_CONCAT4(a,b,c,d)
+#define _LZO_ECONCAT5(a,b,c,d,e)	_LZO_CONCAT5(a,b,c,d,e)
+
+
+/***********************************************************************
+// Query-interface to the algorithms
+************************************************************************/
+
+#if 0
+
+#define __LZO_IS_COMPRESS_QUERY(i,il,o,ol,w)	((lzo_voidp)(o) == (w))
+#define __LZO_QUERY_COMPRESS(i,il,o,ol,w,n,s) \
+				(*ol = (n)*(s), LZO_E_OK)
+
+#define __LZO_IS_DECOMPRESS_QUERY(i,il,o,ol,w)	((lzo_voidp)(o) == (w))
+#define __LZO_QUERY_DECOMPRESS(i,il,o,ol,w,n,s) \
+				(*ol = (n)*(s), LZO_E_OK)
+
+#define __LZO_IS_OPTIMIZE_QUERY(i,il,o,ol,w)	((lzo_voidp)(o) == (w))
+#define __LZO_QUERY_OPTIMIZE(i,il,o,ol,w,n,s) \
+				(*ol = (n)*(s), LZO_E_OK)
+
+#endif
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+#include "lzo_ptr.h"
+
+
+/* Generate compressed data in a deterministic way.
+ * This is fully portable, and compression can be faster as well.
+ * A reason NOT to be deterministic is when the block size is
+ * very small (e.g. 8kB) or the dictionary is big, because
+ * then the initialization of the dictionary becomes a relevant
+ * magnitude for compression speed.
+ */
+#define LZO_DETERMINISTIC
+
+
+#define LZO_DICT_USE_PTR
+#if defined(__LZO_DOS16) || defined(__LZO_WIN16) || defined(__LZO_STRICT_16BIT)
+#  undef LZO_DICT_USE_PTR
+#endif
+
+#if defined(LZO_DICT_USE_PTR)
+#  define lzo_dict_t	const lzo_bytep
+#  define lzo_dict_p	lzo_dict_t __LZO_MMODEL *
+#else
+#  define lzo_dict_t	lzo_uint
+#  define lzo_dict_p	lzo_dict_t __LZO_MMODEL *
+#endif
+
+#if !defined(lzo_moff_t)
+/* must be unsigned */
+#define lzo_moff_t		lzo_uint
+#endif
+
+
+#endif /* already included */
+
+/*
+vi:ts=4
+*/
+
--- linux-2.6.25/fs/ext2/lzo/lzo1x_c.ch	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/lzo1x_c.ch	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,352 @@
+/* lzo1x_1.c -- implementation of the LZO1X-1 compression algorithm
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   markus.oberhumer@jk.uni-linz.ac.at
+ */
+
+
+
+/***********************************************************************
+// compress a block of data.
+************************************************************************/
+
+static
+lzo_uint do_compress     ( const lzo_byte *in , lzo_uint  in_len,
+                                 lzo_byte *out, lzo_uint *out_len,
+                                 lzo_voidp wrkmem )
+{
+#if 0 && defined(__GNUC__) && defined(__i386__)
+	register const lzo_byte *ip __asm__("%esi");
+#else
+	register const lzo_byte *ip;
+#endif
+	lzo_byte *op;
+	const lzo_byte * const in_end = in + in_len;
+	const lzo_byte * const ip_end = in + in_len - M2_MAX_LEN - 5;
+	const lzo_byte *ii;
+	lzo_dict_p const dict = (lzo_dict_p) wrkmem;
+
+	op = out;
+	ip = in;
+	ii = ip;
+
+	ip += 4;
+	for (;;)
+	{
+#if 0 && defined(__GNUC__) && defined(__i386__)
+		register const lzo_byte *m_pos __asm__("%edi");
+#else
+		register const lzo_byte *m_pos;
+#endif
+		lzo_moff_t m_off;
+		lzo_uint m_len;
+		lzo_uint dindex;
+
+		DINDEX1(dindex,ip);
+		GINDEX(m_pos,m_off,dict,dindex,in);
+		if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET))
+			goto literal;
+#if 1
+		if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
+			goto try_match;
+		DINDEX2(dindex,ip);
+#endif
+		GINDEX(m_pos,m_off,dict,dindex,in);
+		if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET))
+			goto literal;
+		if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
+			goto try_match;
+		goto literal;
+
+
+try_match:
+#if 1 && defined(LZO_UNALIGNED_OK_2)
+		if (* (const lzo_ushortp) m_pos != * (const lzo_ushortp) ip)
+#else
+		if (m_pos[0] != ip[0] || m_pos[1] != ip[1])
+#endif
+		{
+		}
+		else
+		{
+			if (m_pos[2] == ip[2])
+			{
+#if 0
+				if (m_off <= M2_MAX_OFFSET)
+					goto match;
+				if (lit <= 3)
+					goto match;
+				if (lit == 3)			/* better compression, but slower */
+				{
+					assert(op - 2 > out); op[-2] |= LZO_BYTE(3);
+					*op++ = *ii++; *op++ = *ii++; *op++ = *ii++;
+					goto code_match;
+				}
+				if (m_pos[3] == ip[3])
+#endif
+					goto match;
+			}
+			else
+			{
+				/* still need a better way for finding M1 matches */
+#if 0
+				/* a M1 match */
+#if 0
+				if (m_off <= M1_MAX_OFFSET && lit > 0 && lit <= 3)
+#else
+				if (m_off <= M1_MAX_OFFSET && lit == 3)
+#endif
+				{
+					register lzo_uint t;
+
+					t = lit;
+					assert(op - 2 > out); op[-2] |= LZO_BYTE(t);
+					do *op++ = *ii++; while (--t > 0);
+					assert(ii == ip);
+					m_off -= 1;
+					*op++ = LZO_BYTE(M1_MARKER | ((m_off & 3) << 2));
+					*op++ = LZO_BYTE(m_off >> 2);
+					ip += 2;
+					goto match_done;
+				}
+#endif
+			}
+		}
+
+
+	/* a literal */
+literal:
+		UPDATE_I(dict,0,dindex,ip,in);
+		++ip;
+		if (ip >= ip_end)
+			break;
+		continue;
+
+
+	/* a match */
+match:
+		UPDATE_I(dict,0,dindex,ip,in);
+		/* store current literal run */
+		if (ip - ii > 0)
+		{
+			register lzo_uint t = ip - ii;
+
+			if (t <= 3)
+			{
+				assert(op - 2 > out);
+				op[-2] |= LZO_BYTE(t);
+			}
+			else if (t <= 18)
+				*op++ = LZO_BYTE(t - 3);
+			else
+			{
+				register lzo_uint tt = t - 18;
+
+				*op++ = 0;
+				while (tt > 255)
+				{
+					tt -= 255;
+					*op++ = 0;
+				}
+				assert(tt > 0);
+				*op++ = LZO_BYTE(tt);
+			}
+			do *op++ = *ii++; while (--t > 0);
+		}
+
+		/* code the match */
+		assert(ii == ip);
+		ip += 3;
+		if (m_pos[3] != *ip++ || m_pos[4] != *ip++ || m_pos[5] != *ip++ ||
+		    m_pos[6] != *ip++ || m_pos[7] != *ip++ || m_pos[8] != *ip++
+#ifdef LZO1Y
+		    || m_pos[ 9] != *ip++ || m_pos[10] != *ip++ || m_pos[11] != *ip++
+		    || m_pos[12] != *ip++ || m_pos[13] != *ip++ || m_pos[14] != *ip++
+#endif
+           )
+		{
+			--ip;
+			m_len = ip - ii;
+			assert(m_len >= 3); assert(m_len <= M2_MAX_LEN);
+
+			if (m_off <= M2_MAX_OFFSET)
+			{
+				m_off -= 1;
+#if defined(LZO1X)
+				*op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2));
+				*op++ = LZO_BYTE(m_off >> 3);
+#elif defined(LZO1Y)
+				*op++ = LZO_BYTE(((m_len + 1) << 4) | ((m_off & 3) << 2));
+				*op++ = LZO_BYTE(m_off >> 2);
+#endif
+			}
+			else if (m_off <= M3_MAX_OFFSET)
+			{
+				m_off -= 1;
+				*op++ = LZO_BYTE(M3_MARKER | (m_len - 2));
+				goto m3_m4_offset;
+			}
+			else
+#if defined(LZO1X)
+			{
+				m_off -= 0x4000;
+				assert(m_off > 0); assert(m_off <= 0x7fff);
+				*op++ = LZO_BYTE(M4_MARKER |
+				                 ((m_off & 0x4000) >> 11) | (m_len - 2));
+				goto m3_m4_offset;
+			}
+#elif defined(LZO1Y)
+				goto m4_match;
+#endif
+		}
+		else
+		{
+			{
+				const lzo_byte *end = in_end;
+				const lzo_byte *m = m_pos + M2_MAX_LEN + 1;
+				while (ip < end && *m == *ip)
+					m++, ip++;
+				m_len = (ip - ii);
+			}
+			assert(m_len > M2_MAX_LEN);
+
+			if (m_off <= M3_MAX_OFFSET)
+			{
+				m_off -= 1;
+				if (m_len <= 33)
+					*op++ = LZO_BYTE(M3_MARKER | (m_len - 2));
+				else
+				{
+					m_len -= 33;
+					*op++ = M3_MARKER | 0;
+					goto m3_m4_len;
+				}
+			}
+			else
+			{
+#if defined(LZO1Y)
+m4_match:
+#endif
+				m_off -= 0x4000;
+				assert(m_off > 0); assert(m_off <= 0x7fff);
+				if (m_len <= M4_MAX_LEN)
+					*op++ = LZO_BYTE(M4_MARKER |
+					                 ((m_off & 0x4000) >> 11) | (m_len - 2));
+				else
+				{
+					m_len -= M4_MAX_LEN;
+					*op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11));
+m3_m4_len:
+					while (m_len > 255)
+					{
+						m_len -= 255;
+						*op++ = 0;
+					}
+					assert(m_len > 0);
+					*op++ = LZO_BYTE(m_len);
+				}
+			}
+
+m3_m4_offset:
+			*op++ = LZO_BYTE((m_off & 63) << 2);
+			*op++ = LZO_BYTE(m_off >> 6);
+		}
+
+#if 0
+match_done:
+#endif
+		ii = ip;
+		if (ip >= ip_end)
+			break;
+	}
+
+	*out_len = op - out;
+	return (lzo_uint) (in_end - ii);
+}
+
+
+/***********************************************************************
+// public entry point
+************************************************************************/
+
+LZO_PUBLIC(int)
+DO_COMPRESS      ( const lzo_byte *in , lzo_uint  in_len,
+                         lzo_byte *out, lzo_uint *out_len,
+                         lzo_voidp wrkmem )
+{
+	lzo_byte *op = out;
+	lzo_uint t;
+
+#if defined(__LZO_QUERY_COMPRESS)
+	if (__LZO_IS_COMPRESS_QUERY(in,in_len,out,out_len,wrkmem))
+		return __LZO_QUERY_COMPRESS(in,in_len,out,out_len,wrkmem,D_SIZE,lzo_sizeof(lzo_dict_t));
+#endif
+
+	if (in_len <= M2_MAX_LEN + 5)
+		t = in_len;
+	else
+	{
+		t = do_compress(in,in_len,op,out_len,wrkmem);
+		op += *out_len;
+	}
+
+	if (t > 0)
+	{
+		const lzo_byte *ii = in + in_len - t;
+
+		if (op == out && t <= 238)
+			*op++ = LZO_BYTE(17 + t);
+		else if (t <= 3)
+			op[-2] |= LZO_BYTE(t);
+		else if (t <= 18)
+			*op++ = LZO_BYTE(t - 3);
+		else
+		{
+			lzo_uint tt = t - 18;
+
+			*op++ = 0;
+			while (tt > 255)
+			{
+				tt -= 255;
+				*op++ = 0;
+			}
+			assert(tt > 0);
+			*op++ = LZO_BYTE(tt);
+		}
+		do *op++ = *ii++; while (--t > 0);
+	}
+
+	*op++ = M4_MARKER | 1;
+	*op++ = 0;
+	*op++ = 0;
+
+	*out_len = op - out;
+	return LZO_E_OK;
+}
+
+
+/*
+vi:ts=4
+*/
+
--- linux-2.6.25/fs/ext2/lzo/lzoconf.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/lzoconf.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,354 @@
+/* lzoconf.h -- configuration for the LZO real-time data compression library
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   <markus.oberhumer@jk.uni-linz.ac.at>
+   http://wildsau.idv.uni-linz.ac.at/mfx/lzo.html
+ */
+
+
+#ifndef __LZOCONF_H
+#define __LZOCONF_H
+
+#define LZO_VERSION             0x1030
+#define LZO_VERSION_STRING      "1.03"
+#define LZO_VERSION_DATE        "Jan 18 1998"
+
+/* internal Autoconf configuration file - only used when building LZO */
+#if defined(LZO_HAVE_CONFIG_H)
+#  include <config.h>
+#endif
+#include <linux/kernel.h>	/* In lieu of <limits.h> */
+#include "assert.h"		/* In lieu of <assert.h> */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/***********************************************************************
+// architecture defines
+************************************************************************/
+
+#if !defined(__LZO_WIN) && !defined(__LZO_DOS) && !defined(__LZO_OS2)
+#  if defined(__WINDOWS__) || defined(_WINDOWS) || defined(_Windows)
+#    define __LZO_WIN
+#  elif defined(__WIN32__) || defined(_WIN32) || defined(WIN32)
+#    define __LZO_WIN
+#  elif defined(__NT__) || defined(__NT_DLL__) || defined(__WINDOWS_386__)
+#    define __LZO_WIN
+#  elif defined(__DOS__) || defined(__MSDOS__) || defined(MSDOS)
+#    define __LZO_DOS
+#  elif defined(__OS2__) || defined(__OS2V2__) || defined(OS2)
+#    define __LZO_OS2
+#  elif defined(__palmos__)
+#    define __LZO_PALMOS
+#  elif defined(__TOS__) || defined(__atarist__)
+#    define __LZO_TOS
+#  endif
+#endif
+
+#if (UINT_MAX < 0xffffffffL)
+#  if defined(__LZO_WIN)
+#    define __LZO_WIN16
+#  elif defined(__LZO_DOS)
+#    define __LZO_DOS16
+#  elif defined(__LZO_PALMOS)
+#    define __LZO_PALMOS16
+#  elif defined(__LZO_TOS)
+#    define __LZO_TOS16
+#  else
+#    error "16-bit target not supported - contact me for porting hints"
+#  endif
+#endif
+
+#if !defined(__LZO_i386)
+#  if defined(__LZO_DOS) || defined(__LZO_WIN16)
+#    define __LZO_i386
+#  elif defined(__i386__) || defined(__386__) || defined(_M_IX86)
+#    define __LZO_i386
+#  endif
+#endif
+
+#if defined(__LZO_STRICT_16BIT)
+#  if (UINT_MAX < 0xffffffffL)
+#    include <lzo16bit.h>
+#  endif
+#endif
+
+
+/***********************************************************************
+// integral and pointer types
+************************************************************************/
+
+/* Integral types with 32 bits or more */
+#if !defined(LZO_UINT32_MAX)
+#  if (UINT_MAX >= 0xffffffffL)
+     typedef unsigned int       lzo_uint32;
+     typedef int                lzo_int32;
+#    define LZO_UINT32_MAX      UINT_MAX
+#    define LZO_INT32_MAX       INT_MAX
+#    define LZO_INT32_MIN       INT_MIN
+#  elif (ULONG_MAX >= 0xffffffffL)
+     typedef unsigned long      lzo_uint32;
+     typedef long               lzo_int32;
+#    define LZO_UINT32_MAX      ULONG_MAX
+#    define LZO_INT32_MAX       LONG_MAX
+#    define LZO_INT32_MIN       LONG_MIN
+#  else
+#    error "lzo_uint32"
+#  endif
+#endif
+
+/* lzo_uint is used like size_t */
+#if !defined(LZO_UINT_MAX)
+#  if (UINT_MAX >= 0xffffffffL)
+     typedef unsigned int       lzo_uint;
+     typedef int                lzo_int;
+#    define LZO_UINT_MAX        UINT_MAX
+#    define LZO_INT_MAX         INT_MAX
+#    define LZO_INT_MIN         INT_MIN
+#  elif (ULONG_MAX >= 0xffffffffL)
+     typedef unsigned long      lzo_uint;
+     typedef long               lzo_int;
+#    define LZO_UINT_MAX        ULONG_MAX
+#    define LZO_INT_MAX         LONG_MAX
+#    define LZO_INT_MIN         LONG_MIN
+#  else
+#    error "lzo_uint"
+#  endif
+#endif
+
+
+/* Memory model that allows to access memory at offsets of lzo_uint.
+ * `Huge' pointers (16-bit DOS/Windows) are somewhat slow, but they
+ * work fine and I really don't care about 16-bit compiler
+ * optimizations nowadays.
+ */
+#if !defined(__LZO_MMODEL)
+#  if (LZO_UINT_MAX <= UINT_MAX)
+#    define __LZO_MMODEL
+#  elif defined(__LZO_DOS16) || defined(__LZO_WIN16)
+#    define __LZO_MMODEL        __huge
+#    define LZO_999_UNSUPPORTED
+#  elif defined(__LZO_PALMOS16) || defined(__LZO_TOS16)
+#    define __LZO_MMODEL
+#  else
+#    error "__LZO_MMODEL"
+#  endif
+#endif
+
+/* no typedef here because of const-pointer issues */
+#define lzo_byte                unsigned char __LZO_MMODEL
+#define lzo_bytep               unsigned char __LZO_MMODEL *
+#define lzo_charp               char __LZO_MMODEL *
+#define lzo_voidp               void __LZO_MMODEL *
+#define lzo_shortp              short __LZO_MMODEL *
+#define lzo_ushortp             unsigned short __LZO_MMODEL *
+#define lzo_uint32p             lzo_uint32 __LZO_MMODEL *
+#define lzo_int32p              lzo_int32 __LZO_MMODEL *
+#define lzo_uintp               lzo_uint __LZO_MMODEL *
+#define lzo_intp                lzo_int __LZO_MMODEL *
+#define lzo_voidpp              lzo_voidp __LZO_MMODEL *
+#define lzo_bytepp              lzo_bytep __LZO_MMODEL *
+
+typedef int lzo_bool;
+
+#ifndef lzo_sizeof_dict_t
+#  define lzo_sizeof_dict_t     sizeof(lzo_bytep)
+#endif
+
+
+/***********************************************************************
+// function types
+************************************************************************/
+
+/* linkage */
+#if !defined(__LZO_EXTERN_C)
+#  ifdef __cplusplus
+#    define __LZO_EXTERN_C      extern "C"
+#  else
+#    define __LZO_EXTERN_C      extern
+#  endif
+#endif
+
+/* calling conventions */
+#if !defined(__LZO_CDECL)
+#  if defined(__LZO_DOS16) || defined(__LZO_WIN16)
+#    define __LZO_CDECL         __far __cdecl
+#  elif defined(__LZO_i386) && defined(_MSC_VER)
+#    define __LZO_CDECL         __cdecl
+#  elif defined(__LZO_i386) && defined(__WATCOMC__)
+#    define __LZO_CDECL         __near __cdecl
+#  else
+#    define __LZO_CDECL
+#  endif
+#endif
+#if !defined(__LZO_ENTRY)
+#  define __LZO_ENTRY           __LZO_CDECL
+#endif
+
+/* DLL export information */
+#if !defined(__LZO_EXPORT1)
+#  define __LZO_EXPORT1
+#endif
+#if !defined(__LZO_EXPORT2)
+#  define __LZO_EXPORT2
+#endif
+
+/* calling convention for C functions */
+#if !defined(LZO_PUBLIC)
+#  define LZO_PUBLIC(_rettype)  __LZO_EXPORT1 _rettype __LZO_EXPORT2 __LZO_ENTRY
+#endif
+#if !defined(LZO_EXTERN)
+#  define LZO_EXTERN(_rettype)  __LZO_EXTERN_C LZO_PUBLIC(_rettype)
+#endif
+#if !defined(LZO_PRIVATE)
+#  define LZO_PRIVATE(_rettype) static _rettype __LZO_ENTRY
+#endif
+
+/* cdecl calling convention for assembler functions */
+#if !defined(LZO_PUBLIC_CDECL)
+#  define LZO_PUBLIC_CDECL(_rettype) \
+                __LZO_EXPORT1 _rettype __LZO_EXPORT2 __LZO_CDECL
+#endif
+#if !defined(LZO_EXTERN_CDECL)
+#  define LZO_EXTERN_CDECL(_rettype)  __LZO_EXTERN_C LZO_PUBLIC_CDECL(_rettype)
+#endif
+
+
+typedef int
+(__LZO_ENTRY *lzo_compress_t)   ( const lzo_byte *src, lzo_uint  src_len,
+                                        lzo_byte *dst, lzo_uint *dst_len,
+                                        lzo_voidp wrkmem );
+
+typedef int
+(__LZO_ENTRY *lzo_decompress_t) ( const lzo_byte *src, lzo_uint  src_len,
+                                        lzo_byte *dst, lzo_uint *dst_len,
+                                        lzo_voidp wrkmem );
+
+typedef int
+(__LZO_ENTRY *lzo_optimize_t)   (       lzo_byte *src, lzo_uint  src_len,
+                                        lzo_byte *dst, lzo_uint *dst_len,
+                                        lzo_voidp wrkmem );
+
+typedef int
+(__LZO_ENTRY *lzo_compress_dict_t)(const lzo_byte *src, lzo_uint  src_len,
+                                        lzo_byte *dst, lzo_uint *dst_len,
+                                        lzo_voidp wrkmem,
+                                  const lzo_byte *dict, lzo_uint dict_len );
+
+typedef int
+(__LZO_ENTRY *lzo_decompress_dict_t)(const lzo_byte *src, lzo_uint  src_len,
+                                        lzo_byte *dst, lzo_uint *dst_len,
+                                        lzo_voidp wrkmem,
+                                  const lzo_byte *dict, lzo_uint dict_len );
+
+
+/* a progress indicator callback function */
+typedef void (__LZO_ENTRY *lzo_progress_callback_t) (lzo_uint, lzo_uint);
+
+
+/***********************************************************************
+// error codes and prototypes
+************************************************************************/
+
+/* Error codes for the compression/decompression functions. Negative
+ * values are errors, positive values will be used for special but
+ * normal events.
+ */
+#define LZO_E_OK                    0
+#define LZO_E_ERROR                 (-1)
+#define LZO_E_OUT_OF_MEMORY         (-2)    /* not used right now */
+#define LZO_E_NOT_COMPRESSIBLE      (-3)    /* not used right now */
+#define LZO_E_INPUT_OVERRUN         (-4)
+#define LZO_E_OUTPUT_OVERRUN        (-5)
+#define LZO_E_LOOKBEHIND_OVERRUN    (-6)
+#define LZO_E_EOF_NOT_FOUND         (-7)
+#define LZO_E_INPUT_NOT_CONSUMED    (-8)
+
+
+/* lzo_init() should be the first function you call.
+ * Check the return code !
+ *
+ * lzo_init() is a macro to allow checking that the library and the
+ * compiler's view of various types are consistent.
+ */
+#define lzo_init() __lzo_init2(LZO_VERSION,(int)sizeof(short),(int)sizeof(int),\
+    (int)sizeof(long),(int)sizeof(lzo_uint32),(int)sizeof(lzo_uint),\
+    (int)lzo_sizeof_dict_t,(int)sizeof(char *),(int)sizeof(lzo_voidp),\
+    (int)sizeof(lzo_compress_t))
+LZO_EXTERN(int) __lzo_init2(unsigned,int,int,int,int,int,int,int,int,int);
+
+/* version functions (useful for shared libraries) */
+LZO_EXTERN(unsigned) lzo_version(void);
+LZO_EXTERN(const char *) lzo_version_string(void);
+LZO_EXTERN(const char *) lzo_version_date(void);
+LZO_EXTERN(const lzo_charp) _lzo_version_string(void);
+LZO_EXTERN(const lzo_charp) _lzo_version_date(void);
+
+/* string functions */
+LZO_EXTERN(int)
+lzo_memcmp(const lzo_voidp _s1, const lzo_voidp _s2, lzo_uint _len);
+LZO_EXTERN(lzo_voidp)
+lzo_memcpy(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len);
+LZO_EXTERN(lzo_voidp)
+lzo_memmove(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len);
+LZO_EXTERN(lzo_voidp)
+lzo_memset(lzo_voidp _s, int _c, lzo_uint _len);
+
+/* checksum functions */
+LZO_EXTERN(lzo_uint32)
+lzo_adler32(lzo_uint32 _adler, const lzo_byte *_buf, lzo_uint _len);
+LZO_EXTERN(lzo_uint32)
+lzo_crc32(lzo_uint32 _c, const lzo_byte *_buf, lzo_uint _len);
+
+/* memory functions */
+LZO_EXTERN(lzo_bytep) lzo_alloc(lzo_uint _nelems, lzo_uint _size);
+LZO_EXTERN(lzo_bytep) lzo_malloc(lzo_uint _size);
+LZO_EXTERN(void) lzo_free(lzo_voidp _ptr);
+
+extern lzo_bytep (__LZO_ENTRY *lzo_alloc_f) (lzo_uint,lzo_uint);
+extern void (__LZO_ENTRY *lzo_free_f) (lzo_voidp);
+
+/* misc. */
+LZO_EXTERN(lzo_bool) lzo_assert(int _expr);
+LZO_EXTERN(int) _lzo_config_check(void);
+typedef union { lzo_bytep p; lzo_uint u; } __lzo_pu_u;
+typedef union { lzo_bytep p; lzo_uint32 u32; } __lzo_pu32_u;
+
+/* align a char pointer on a boundary that is a multiple of `size' */
+LZO_EXTERN(unsigned) __lzo_align_gap(const lzo_voidp _ptr, lzo_uint _size);
+#define LZO_PTR_ALIGN_UP(_ptr,_size) \
+    ((_ptr) + (lzo_uint) __lzo_align_gap((const lzo_voidp)(_ptr),(lzo_uint)(_size)))
+
+/* depracted - backward compatibility */
+#define LZO_ALIGN(_ptr,_size) LZO_PTR_ALIGN_UP(_ptr,_size)
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* already included */
+
--- linux-2.6.25/fs/ext2/lzo/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/Makefile	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,13 @@
+#
+# Makefile for the linux compression routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+EXTRA_CFLAGS += -I$(TOPDIR)/fs/ext2
+
+obj-$(CONFIG_EXT2_HAVE_LZO) := ext2-compr-lzo.o
+ext2-compr-lzo-y := e2compr_lzo.o lzo1x_1o.o lzo1x_d1.o
--- linux-2.6.25/fs/ext2/lzo/lzo_util.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/lzo_util.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,192 @@
+/* lzo_util.h -- utilities for the LZO library
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   markus.oberhumer@jk.uni-linz.ac.at
+ */
+
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the library and is subject
+   to change.
+ */
+
+
+#ifndef __LZO_UTIL_H
+#define __LZO_UTIL_H
+
+#ifndef __LZO_CONF_H
+#  include "lzo_conf.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/***********************************************************************
+// fast memcpy that copies multiples of 8 byte chunks.
+// len is the number of bytes.
+// note: all parameters must be lvalues, len >= 8
+//       dest and src advance, len is undefined afterwards
+************************************************************************/
+
+#if 1 && defined(HAVE_MEMCPY)
+#if !defined(__LZO_DOS16) && !defined(__LZO_WIN16)
+
+#define MEMCPY8_DS(dest,src,len) \
+	memcpy(dest,src,len); \
+	dest += len; \
+	src += len
+
+#endif
+#endif
+
+
+#if 0 && !defined(MEMCPY8_DS)
+
+#define MEMCPY8_DS(dest,src,len) \
+	{ do { \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		len -= 8; \
+	} while (len > 0); }
+
+#endif
+
+
+#if !defined(MEMCPY8_DS)
+
+#define MEMCPY8_DS(dest,src,len) \
+	{ register lzo_uint __l = (len) / 8; \
+	do { \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+		*dest++ = *src++; \
+	} while (--__l > 0); }
+
+#endif
+
+
+/***********************************************************************
+// memcpy and pseudo-memmove
+// len is the number of bytes.
+// note: all parameters must be lvalues, len > 0
+//       dest and src advance, len is undefined afterwards
+************************************************************************/
+
+#define MEMCPY_DS(dest,src,len) \
+	do *dest++ = *src++; \
+	while (--len > 0)
+
+#define MEMMOVE_DS(dest,src,len) \
+	do *dest++ = *src++; \
+	while (--len > 0)
+
+
+/***********************************************************************
+// fast bzero that clears multiples of 8 pointers
+// n is the number of pointers.
+// note: n > 0
+//       s and n are undefined afterwards
+************************************************************************/
+
+#if 0 && defined(LZO_OPTIMIZE_GNUC_i386)
+
+#define BZERO8_PTR(s,l,n) \
+__asm__ __volatile__( \
+	"movl  %0,%%eax \n"             \
+	"movl  %1,%%edi \n"             \
+	"movl  %2,%%ecx \n"             \
+	"cld \n"                        \
+	"rep \n"                        \
+	"stosl %%eax,(%%edi) \n"        \
+	: /* no outputs */              \
+	:"g" (0),"g" (s),"g" (n)	  	\
+	:"eax","edi","ecx", "memory", "cc" \
+)
+
+#elif (LZO_UINT_MAX <= SIZE_T_MAX) && defined(HAVE_MEMSET)
+
+#define BZERO8_PTR(s,l,n) \
+	memset((lzo_voidp)(s),0,(lzo_uint)(l)*(n))
+
+#else
+
+#define BZERO8_PTR(s,l,n) \
+	lzo_memset((lzo_voidp)(s),0,(lzo_uint)(l)*(n))
+
+#endif
+
+
+/***********************************************************************
+// rotate (not used at the moment)
+************************************************************************/
+
+#if 0
+#if defined(__GNUC__) && defined(__i386__)
+
+unsigned char lzo_rotr8(unsigned char value, int shift);
+extern __inline__ unsigned char lzo_rotr8(unsigned char value, int shift)
+{
+	unsigned char result;
+
+	__asm__ __volatile__ ("movb %b1, %b0; rorb %b2, %b0"
+                      	: "=a"(result) : "g"(value), "c"(shift));
+	return result;
+}
+
+unsigned short lzo_rotr16(unsigned short value, int shift);
+extern __inline__ unsigned short lzo_rotr16(unsigned short value, int shift)
+{
+	unsigned short result;
+
+	__asm__ __volatile__ ("movw %b1, %b0; rorw %b2, %b0"
+                      	: "=a"(result) : "g"(value), "c"(shift));
+	return result;
+}
+
+#endif
+#endif
+
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* already included */
+
+/*
+vi:ts=4
+*/
--- linux-2.6.25/fs/ext2/lzo/lzo1x_1o.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/lzo1x_1o.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,38 @@
+/* lzo1x_1o.c -- LZO1X-1(15) compression
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   markus.oberhumer@jk.uni-linz.ac.at
+ */
+
+
+#define LZO_NEED_DICT_H
+#define D_BITS			15
+#define D_INDEX1(d,p)		d = DM((0x21*DX3(p,5,5,6)) >> 5)
+#define D_INDEX2(d,p)		d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f)
+
+#include "config1x.h"
+
+#define DO_COMPRESS		lzo1x_1_15_compress
+
+#include "lzo1x_c.ch"
--- linux-2.6.25/fs/ext2/lzo/e2compr_lzo.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/e2compr_lzo.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,150 @@
+#include <linux/fs.h>
+#include <linux/ext2_fs_c.h>
+#include "e2compr_lzo.h"
+#include "lzo1x.h"
+#ifdef __KERNEL__
+# include <linux/kernel.h>
+# include <linux/module.h>
+#endif
+
+#ifdef MODULE
+MODULE_AUTHOR("Markus Franz Xaver Johannes Oberhumer");
+MODULE_DESCRIPTION("Lev-Zimpel-Oberhumer algorithm for EXT2 file compression");
+MODULE_LICENSE("GPL");
+#endif
+
+#if 0
+static void *heap_start = NULL;
+static u8 *orig_start = NULL;
+#endif
+
+#if EXT2_MAX_CLU_NBYTES > (32*1024)
+# error "Check that lzo overrun allowance is still correct for this cluster size."
+#endif
+#define MAGIC_BYTE 42
+
+size_t 
+ext2_wLZO (u8 *in_buf, 
+	   u8 *out_buf,
+	   void *heap,
+	   size_t num_in_buf,
+	   size_t num_out_buf,
+	   int param)
+{
+	size_t clen;
+
+	if (!try_module_get(THIS_MODULE))
+		return 0;
+	if (heap == NULL)
+		goto err;
+	if (((u8 *)heap < out_buf)
+	    || ((u8 *)heap > out_buf + EXT2_MAX_CLUSTER_BYTES)) {
+#ifdef __KERNEL__
+		printk(KERN_WARNING
+		       "lzo: heap doesn't seem to be contiguous with out_buf.  orig_start: %p, out_buf: %p, diff: %d.  Returning zero.\n",
+			(u8 *)heap, out_buf, (u8 *)heap - out_buf);
+#else
+		fprintf(stderr,
+			"lzo: heap/out_buf misplacement.  "
+			"orig_start: %p, out_buf: %p, diff: %d\n",
+			heap, out_buf, heap - out_buf);
+#endif
+		goto err;
+	}
+	((unsigned char *)heap)[LZO_OVERRUN_LEN] = MAGIC_BYTE;
+	if (lzo1x_1_15_compress(in_buf, num_in_buf,
+				out_buf, &clen,
+				heap + LZO_OVERRUN_LEN + 1))
+		clen = 0;
+	if (((unsigned char *)heap)[LZO_OVERRUN_LEN] != MAGIC_BYTE) {
+		printk(KERN_ERR
+		       "lzo: overran allowed area.  Please report to e2compr list.\n");
+		goto err; /* play it safe */
+	}
+	if (clen > num_out_buf)
+		goto err;
+	module_put(THIS_MODULE);
+	return clen;
+
+err:
+	module_put(THIS_MODULE);
+	return 0;
+}
+
+size_t 
+ext2_rLZO (unsigned char *in_buf, 
+           unsigned char *out_buf,
+	   void *heap,
+           size_t num_in_buf,
+           size_t num_out_buf, /* N.B.: ignored. */
+           int param)
+{
+	int err;
+
+	if (!try_module_get(THIS_MODULE))
+		return 0;
+#if 0
+	if (heap_start == NULL) {
+		module_put(THIS_MODULE);
+		return 0;
+	}
+
+	err = lzo1x_decompress(in_buf, num_in_buf, out_buf, &num_out_buf,
+		heap_start);
+#else
+	err = lzo1x_decompress(in_buf, num_in_buf, out_buf, &num_out_buf, heap);
+#endif
+	if (err != LZO_E_OK) {
+		printk(KERN_WARNING
+		       "lzo1x_decompress() returned %d.\n", err);
+		module_put(THIS_MODULE);
+		return 0;
+	}
+	module_put(THIS_MODULE);
+	return num_out_buf;
+}
+
+size_t 
+ext2_iLZO (int action)
+{
+	/* The compressor can overrun its output buffer by 531 bytes
+	   (LZO_OVERRUN_LEN).  We use the 532nd byte to hold a magic
+	   number (which we check to detect overruns of the overrun
+	   buffer).  128KB is the working memory for _15.
+		orig_start_C = (u8 *) mem_start;
+		heap_start_C = orig_start_C + LZO_OVERRUN_LEN + 1;
+	*/
+	switch (action) {
+		case EXT2_ALG_INIT_COMPRESS:
+			return (128 * 1024) + LZO_OVERRUN_LEN + 1;
+		case EXT2_ALG_INIT_DECOMPRESS:
+			return 0;
+		default:
+			return 0;
+	}
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	struct ext2_algorithm lzo_alg;
+
+	lzo_alg.name = NULL;
+	lzo_alg.avail = 1;
+	lzo_alg.init = ext2_iLZO;
+	lzo_alg.compress = ext2_wLZO;
+	lzo_alg.decompress = ext2_rLZO;
+
+	return ext2_register_compression_module(EXT2_LZO_ALG, 
+			(128 * 1024) + LZO_OVERRUN_LEN + 1,
+			0,
+			&lzo_alg);
+}
+
+void cleanup_module(void)
+{
+	ext2_unregister_compression_module(EXT2_LZO_ALG);
+}
+
+#endif
--- linux-2.6.25/fs/ext2/lzo/lzo1x.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/lzo1x.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,168 @@
+/* lzo1x.h -- public interface of the LZO1X compression algorithm
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   <markus.oberhumer@jk.uni-linz.ac.at>
+   http://wildsau.idv.uni-linz.ac.at/mfx/lzo.html
+ */
+
+
+#ifndef __LZO1X_H
+#define __LZO1X_H
+
+#ifndef __LZOCONF_H
+#include "lzoconf.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+/* Memory required for the wrkmem parameter.
+ * When the required size is 0, you can also pass a NULL pointer.
+ */
+
+#define LZO1X_MEM_COMPRESS      LZO1X_1_MEM_COMPESS
+#define LZO1X_MEM_DECOMPRESS    (0)
+#define LZO1X_MEM_OPTIMIZE      (0)
+
+
+/* decompression */
+LZO_EXTERN(int)
+lzo1x_decompress        ( const lzo_byte *src, lzo_uint  src_len,
+                                lzo_byte *dst, lzo_uint *dst_len,
+                                lzo_voidp wrkmem /* NOT USED */ );
+
+/* safe decompression with overrun testing */
+LZO_EXTERN(int)
+lzo1x_decompress_safe   ( const lzo_byte *src, lzo_uint  src_len,
+                                lzo_byte *dst, lzo_uint *dst_len,
+                                lzo_voidp wrkmem /* NOT USED */ );
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+#define LZO1X_1_MEM_COMPRESS    ((lzo_uint32) (16384L * lzo_sizeof_dict_t))
+
+LZO_EXTERN(int)
+lzo1x_1_compress        ( const lzo_byte *src, lzo_uint  src_len,
+                                lzo_byte *dst, lzo_uint *dst_len,
+                                lzo_voidp wrkmem );
+
+
+/***********************************************************************
+// special compressor versions
+************************************************************************/
+
+/* this version needs only 8 kB work memory */
+#define LZO1X_1_11_MEM_COMPRESS ((lzo_uint32) (2048L * lzo_sizeof_dict_t))
+
+LZO_EXTERN(int)
+lzo1x_1_11_compress     ( const lzo_byte *src, lzo_uint  src_len,
+                                lzo_byte *dst, lzo_uint *dst_len,
+                                lzo_voidp wrkmem );
+
+
+/* this version needs 16 kB work memory */
+#define LZO1X_1_12_MEM_COMPRESS ((lzo_uint32) (4096L * lzo_sizeof_dict_t))
+
+LZO_EXTERN(int)
+lzo1x_1_12_compress     ( const lzo_byte *src, lzo_uint  src_len,
+                                lzo_byte *dst, lzo_uint *dst_len,
+                                lzo_voidp wrkmem );
+
+
+/* use this version if you need a little more compression speed */
+#define LZO1X_1_15_MEM_COMPRESS ((lzo_uint32) (32768L * lzo_sizeof_dict_t))
+
+LZO_EXTERN(int)
+lzo1x_1_15_compress     ( const lzo_byte *src, lzo_uint  src_len,
+                                lzo_byte *dst, lzo_uint *dst_len,
+                                lzo_voidp wrkmem );
+
+
+/***********************************************************************
+// better compression ratio at the cost of more memory and time
+************************************************************************/
+
+#define LZO1X_999_MEM_COMPRESS  ((lzo_uint32) (14 * 16384L * sizeof(short)))
+
+#if !defined(LZO_999_UNSUPPORTED)
+LZO_EXTERN(int)
+lzo1x_999_compress      ( const lzo_byte *src, lzo_uint  src_len,
+                                lzo_byte *dst, lzo_uint *dst_len,
+                                lzo_voidp wrkmem );
+#endif
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+#if !defined(LZO_999_UNSUPPORTED)
+LZO_EXTERN(int)
+lzo1x_999_compress_dict     ( const lzo_byte *in , lzo_uint  in_len,
+                                    lzo_byte *out, lzo_uint *out_len,
+                                    lzo_voidp wrkmem,
+                              const lzo_byte *dict, lzo_uint dict_len );
+
+LZO_EXTERN(int)
+lzo1x_999_compress_level    ( const lzo_byte *in , lzo_uint  in_len,
+                                    lzo_byte *out, lzo_uint *out_len,
+                                    lzo_voidp wrkmem,
+                              const lzo_byte *dict, lzo_uint dict_len,
+                                    lzo_progress_callback_t cb,
+                                    int compression_level );
+#endif
+
+LZO_EXTERN(int)
+lzo1x_decompress_dict_safe ( const lzo_byte *in,  lzo_uint  in_len,
+                                   lzo_byte *out, lzo_uint *out_len,
+                                   lzo_voidp wrkmem /* NOT USED */,
+                             const lzo_byte *dict, lzo_uint dict_len );
+
+
+/***********************************************************************
+// optimize a compressed data block
+************************************************************************/
+
+LZO_EXTERN(int)
+lzo1x_optimize          (       lzo_byte *in , lzo_uint  in_len,
+                                lzo_byte *out, lzo_uint *out_len,
+                                lzo_voidp wrkmem );
+
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* already included */
+
--- linux-2.6.25/fs/ext2/lzo/e2compr_lzo.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/e2compr_lzo.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,28 @@
+#ifdef __KERNEL__
+# include <linux/types.h>
+#else
+# include <stdio.h> /* size_t */
+#endif
+
+#define LZO_OVERRUN_LEN 531
+#define LZO_REQMEM ((128 * 1024) + LZO_OVERRUN_LEN + 1)
+
+extern size_t 
+ext2_wLZO ( unsigned char *in_buf, 
+            unsigned char *out_buf,
+	    void *heap,
+            size_t num_in_buf,
+            size_t num_out_buf,
+            int param );
+
+extern size_t 
+ext2_rLZO ( unsigned char *in_buf, 
+            unsigned char *out_buf,
+	    void *heap,
+            size_t num_in_buf,
+            size_t num_out_buf,
+            int param );
+
+
+extern size_t 
+ext2_iLZO ( int action );
--- linux-2.6.25/fs/ext2/lzo/config1x.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/config1x.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,106 @@
+/* config1x.h -- configuration for the LZO1X algorithm
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   markus.oberhumer@jk.uni-linz.ac.at
+ */
+
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the library and is subject
+   to change.
+ */
+
+
+#ifndef __LZO_CONFIG1X_H
+#define __LZO_CONFIG1X_H
+
+#if !defined(LZO1X) && !defined(LZO1Y) && !defined(LZO1Z)
+#  define LZO1X
+#endif
+
+#if !defined(__LZO_IN_MINILZO)
+#include "lzo1x.h"
+#endif
+#include "lzo_conf.h"
+#include "lzo_util.h"
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+#define LZO_EOF_CODE
+#undef LZO_DETERMINISTIC
+
+#define M1_MAX_OFFSET	0x0400
+#ifndef M2_MAX_OFFSET
+#define M2_MAX_OFFSET	0x0800
+#endif
+#define M3_MAX_OFFSET	0x4000
+#define M4_MAX_OFFSET	0xbfff
+
+#define MX_MAX_OFFSET	(M1_MAX_OFFSET + M2_MAX_OFFSET)
+
+#define M1_MIN_LEN		2
+#define M1_MAX_LEN		2
+#define M2_MIN_LEN		3
+#ifndef M2_MAX_LEN
+#define M2_MAX_LEN		8
+#endif
+#define M3_MIN_LEN		3
+#define M3_MAX_LEN		33
+#define M4_MIN_LEN		3
+#define M4_MAX_LEN		9
+
+#define M1_MARKER		0
+#define M2_MARKER		64
+#define M3_MARKER		32
+#define M4_MARKER		16
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+#ifndef MIN_LOOKAHEAD
+#define MIN_LOOKAHEAD		(M2_MAX_LEN + 1)
+#endif
+
+#if defined(LZO_NEED_DICT_H)
+
+#ifndef LZO_HASH
+#define LZO_HASH			LZO_HASH_LZO_INCREMENTAL_B
+#endif
+#define DL_MIN_LEN			M2_MIN_LEN
+#include "lzo_dict.h"
+
+#endif
+
+
+
+#endif /* already included */
+
+/*
+vi:ts=4
+*/
+
--- linux-2.6.25/fs/ext2/lzo/lzo_dict.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/lzo_dict.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,302 @@
+/* lzo_dict.h -- dictionary definitions for the the LZO library
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   markus.oberhumer@jk.uni-linz.ac.at
+ */
+
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the library and is subject
+   to change.
+ */
+
+
+#ifndef __LZO_DICT_H
+#define __LZO_DICT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+/***********************************************************************
+// dictionary size
+************************************************************************/
+
+/* dictionary needed for compression */
+#if !defined(D_BITS) && defined(DBITS)
+#  define D_BITS		DBITS
+#endif
+#if !defined(D_BITS)
+#  error D_BITS is not defined
+#endif
+#if (D_BITS < 16)
+#  define D_SIZE		LZO_SIZE(D_BITS)
+#  define D_MASK		LZO_MASK(D_BITS)
+#else
+#  define D_SIZE		LZO_USIZE(D_BITS)
+#  define D_MASK		LZO_UMASK(D_BITS)
+#endif
+#define D_HIGH			((D_MASK >> 1) + 1)
+
+
+/* dictionary depth */
+#if !defined(DD_BITS)
+#  define DD_BITS		0
+#endif
+#define DD_SIZE			LZO_SIZE(DD_BITS)
+#define DD_MASK			LZO_MASK(DD_BITS)
+
+/* dictionary length */
+#if !defined(DL_BITS)
+#  define DL_BITS		(D_BITS - DD_BITS)
+#endif
+#if (DL_BITS < 16)
+#  define DL_SIZE		LZO_SIZE(DL_BITS)
+#  define DL_MASK		LZO_MASK(DL_BITS)
+#else
+#  define DL_SIZE		LZO_USIZE(DL_BITS)
+#  define DL_MASK		LZO_UMASK(DL_BITS)
+#endif
+
+
+#if (D_BITS != DL_BITS + DD_BITS)
+#  error D_BITS does not match
+#endif
+#if (D_BITS < 8 || D_BITS > 18)
+#  error invalid D_BITS
+#endif
+#if (DL_BITS < 8 || DL_BITS > 20)
+#  error invalid DL_BITS
+#endif
+#if (DD_BITS < 0 || DD_BITS > 6)
+#  error invalid DD_BITS
+#endif
+
+
+#if !defined(DL_MIN_LEN)
+#  define DL_MIN_LEN	3
+#endif
+#if !defined(DL_SHIFT)
+#  define DL_SHIFT		((DL_BITS + (DL_MIN_LEN - 1)) / DL_MIN_LEN)
+#endif
+
+
+
+/***********************************************************************
+// dictionary access
+************************************************************************/
+
+#define LZO_HASH_GZIP					1
+#define LZO_HASH_GZIP_INCREMENTAL		2
+#define LZO_HASH_LZO_INCREMENTAL_A		3
+#define LZO_HASH_LZO_INCREMENTAL_B		4
+
+#if !defined(LZO_HASH)
+#  error choose a hashing strategy
+#endif
+
+
+#if (DL_MIN_LEN == 3)
+#  define _DV2_A(p,shift1,shift2) \
+		(((( (lzo_uint32)((p)[0]) << shift1) ^ (p)[1]) << shift2) ^ (p)[2])
+#  define _DV2_B(p,shift1,shift2) \
+		(((( (lzo_uint32)((p)[2]) << shift1) ^ (p)[1]) << shift2) ^ (p)[0])
+#  define _DV3_B(p,shift1,shift2,shift3) \
+		((_DV2_B((p)+1,shift1,shift2) << (shift3)) ^ (p)[0])
+#elif (DL_MIN_LEN == 2)
+#  define _DV2_A(p,shift1,shift2) \
+		(( (lzo_uint32)(p[0]) << shift1) ^ p[1])
+#  define _DV2_B(p,shift1,shift2) \
+		(( (lzo_uint32)(p[1]) << shift1) ^ p[2])
+#else
+#  error invalid DL_MIN_LEN
+#endif
+#define _DV_A(p,shift) 		_DV2_A(p,shift,shift)
+#define _DV_B(p,shift)	 	_DV2_B(p,shift,shift)
+#define DA2(p,s1,s2) \
+		(((((lzo_uint32)((p)[2]) << (s2)) + (p)[1]) << (s1)) + (p)[0])
+#define DS2(p,s1,s2) \
+		(((((lzo_uint32)((p)[2]) << (s2)) - (p)[1]) << (s1)) - (p)[0])
+#define DX2(p,s1,s2) \
+		(((((lzo_uint32)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0])
+#define DA3(p,s1,s2,s3) ((DA2((p)+1,s2,s3) << (s1)) + (p)[0])
+#define DS3(p,s1,s2,s3) ((DS2((p)+1,s2,s3) << (s1)) - (p)[0])
+#define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0])
+#define DMS(v,s)		((lzo_uint) (((v) & (D_MASK >> (s))) << (s)))
+#define DM(v)			DMS(v,0)
+
+
+
+#if (LZO_HASH == LZO_HASH_GZIP)
+   /* hash function like in gzip/zlib (deflate) */
+#  define _DINDEX(dv,p)		(_DV_A((p),DL_SHIFT))
+
+#elif (LZO_HASH == LZO_HASH_GZIP_INCREMENTAL)
+   /* incremental hash like in gzip/zlib (deflate) */
+#  define __LZO_HASH_INCREMENTAL
+#  define DVAL_FIRST(dv,p)	dv = _DV_A((p),DL_SHIFT)
+#  define DVAL_NEXT(dv,p)	dv = (((dv) << DL_SHIFT) ^ p[2])
+#  define _DINDEX(dv,p)		(dv)
+#  define DVAL_LOOKAHEAD	DL_MIN_LEN
+
+#elif (LZO_HASH == LZO_HASH_LZO_INCREMENTAL_A)
+   /* incremental LZO hash version A */
+#  define __LZO_HASH_INCREMENTAL
+#  define DVAL_FIRST(dv,p)	dv = _DV_A((p),5)
+#  define DVAL_NEXT(dv,p) \
+				dv ^= (lzo_uint32)(p[-1]) << (2*5); dv = (((dv) << 5) ^ p[2])
+#  define _DINDEX(dv,p)		((0x9f5f * (dv)) >> 5)
+#  define DVAL_LOOKAHEAD	DL_MIN_LEN
+
+#elif (LZO_HASH == LZO_HASH_LZO_INCREMENTAL_B)
+   /* incremental LZO hash version B */
+#  define __LZO_HASH_INCREMENTAL
+#  define DVAL_FIRST(dv,p)	dv = _DV_B((p),5)
+#  define DVAL_NEXT(dv,p) \
+				dv ^= p[-1]; dv = (((dv) >> 5) ^ ((lzo_uint32)(p[2]) << (2*5)))
+#  define _DINDEX(dv,p)		((0x9f5f * (dv)) >> 5)
+#  define DVAL_LOOKAHEAD	DL_MIN_LEN
+
+#else
+#  error choose a hashing strategy
+#endif
+
+
+#ifndef DINDEX
+#define DINDEX(dv,p)		((lzo_uint)((_DINDEX(dv,p)) & DL_MASK) << DD_BITS)
+#endif
+#if !defined(DINDEX1) && defined(D_INDEX1)
+#define DINDEX1				D_INDEX1
+#endif
+#if !defined(DINDEX2) && defined(D_INDEX2)
+#define DINDEX2				D_INDEX2
+#endif
+
+
+
+#if !defined(__LZO_HASH_INCREMENTAL)
+#  define DVAL_FIRST(dv,p)	((void) 0)
+#  define DVAL_NEXT(dv,p)	((void) 0)
+#  define DVAL_LOOKAHEAD	0
+#endif
+
+
+#if !defined(DVAL_ASSERT)
+#if defined(__LZO_HASH_INCREMENTAL) && !defined(NDEBUG)
+static void DVAL_ASSERT(lzo_uint32 dv, const lzo_byte *p)
+{
+	lzo_uint32 df;
+	DVAL_FIRST(df,(p));
+	assert(DINDEX(dv,p) == DINDEX(df,p));
+}
+#else
+#  define DVAL_ASSERT(dv,p)	((void) 0)
+#endif
+#endif
+
+
+
+/***********************************************************************
+// dictionary updating
+************************************************************************/
+
+#if defined(LZO_DICT_USE_PTR)
+#  define DENTRY(p,in)							(p)
+#  define GINDEX(m_pos,m_off,dict,dindex,in)	m_pos = dict[dindex]
+#else
+#  define DENTRY(p,in)							((lzo_uint) ((p)-(in)))
+#  define GINDEX(m_pos,m_off,dict,dindex,in)	m_off = dict[dindex]
+#endif
+
+
+#if (DD_BITS == 0)
+
+#  define UPDATE_D(dict,drun,dv,p,in)		dict[ DINDEX(dv,p) ] = DENTRY(p,in)
+#  define UPDATE_I(dict,drun,index,p,in)	dict[index] = DENTRY(p,in)
+#  define UPDATE_P(ptr,drun,p,in)			(ptr)[0] = DENTRY(p,in)
+
+#else
+
+#  define UPDATE_D(dict,drun,dv,p,in)	\
+		dict[ DINDEX(dv,p) + drun++ ] = DENTRY(p,in); drun &= DD_MASK
+#  define UPDATE_I(dict,drun,index,p,in)	\
+		dict[ (index) + drun++ ] = DENTRY(p,in); drun &= DD_MASK
+#  define UPDATE_P(ptr,drun,p,in)	\
+		(ptr) [ drun++ ] = DENTRY(p,in); drun &= DD_MASK
+
+#endif
+
+
+/***********************************************************************
+// test for a match
+************************************************************************/
+
+#if defined(LZO_DICT_USE_PTR)
+
+/* m_pos is either NULL or a valid pointer */
+#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \
+		(m_pos == NULL || (m_off = (lzo_moff_t) (ip - m_pos)) > max_offset)
+
+/* m_pos may point anywhere... */
+#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \
+	(BOUNDS_CHECKING_OFF_IN_EXPR( \
+		(PTR_LT(m_pos,in) || \
+		 (m_off = (lzo_moff_t) PTR_DIFF(ip,m_pos)) <= 0 || \
+		  m_off > max_offset) ))
+
+#else
+
+#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \
+		(m_off == 0 || \
+		 ((m_off = (lzo_moff_t) ((ip)-(in)) - m_off) > max_offset) || \
+		 (m_pos = (ip) - (m_off), 0) )
+
+#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \
+		((lzo_moff_t) ((ip)-(in)) <= m_off || \
+		 ((m_off = (lzo_moff_t) ((ip)-(in)) - m_off) > max_offset) || \
+		 (m_pos = (ip) - (m_off), 0) )
+
+#endif
+
+
+#if defined(LZO_DETERMINISTIC)
+#  define LZO_CHECK_MPOS	LZO_CHECK_MPOS_DET
+#else
+#  define LZO_CHECK_MPOS	LZO_CHECK_MPOS_NON_DET
+#endif
+
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* already included */
+
+/*
+vi:ts=4
+*/
+
--- linux-2.6.25/fs/ext2/lzo/lzo1_d.ch	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/lzo/lzo1_d.ch	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,139 @@
+/* lzo1_d.ch -- common decompression stuff
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   markus.oberhumer@jk.uni-linz.ac.at
+ */
+
+
+
+#if defined(LZO_TEST_DECOMPRESS_OVERRUN)
+#  if !defined(LZO_TEST_DECOMPRESS_OVERRUN_INPUT)
+#    define LZO_TEST_DECOMPRESS_OVERRUN_INPUT		2
+#  endif
+#  if !defined(LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT)
+#    define LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT		2
+#  endif
+#  if !defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND)
+#    define LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND
+#  endif
+#endif
+
+
+/***********************************************************************
+// Overrun detection is internally handled by these macros:
+//
+//   TEST_IP    test input overrun at loop begin
+//   NEED_IP    test input overrun at every input byte
+//
+//   TEST_OP    test output overrun at loop begin
+//   NEED_OP    test output overrun at every output byte
+//
+//   TEST_LOOKBEHIND    test match postion
+//
+// The fastest decompressor results when testing for no overruns
+// and using LZO_EOF_CODE.
+************************************************************************/
+
+#undef TEST_IP
+#undef TEST_OP
+#undef TEST_LOOKBEHIND
+#undef NEED_IP
+#undef NEED_OP
+#undef HAVE_TEST_IP
+#undef HAVE_TEST_OP
+#undef HAVE_NEED_IP
+#undef HAVE_NEED_OP
+#undef HAVE_ANY_IP
+#undef HAVE_ANY_OP
+
+
+#if defined(LZO_TEST_DECOMPRESS_OVERRUN_INPUT)
+#  if (LZO_TEST_DECOMPRESS_OVERRUN_INPUT >= 1)
+#    define TEST_IP				(ip < ip_end)
+#  endif
+#  if (LZO_TEST_DECOMPRESS_OVERRUN_INPUT >= 2)
+#    define NEED_IP(x) \
+			if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x))  goto input_overrun
+#  endif
+#endif
+
+#if defined(LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT)
+#  if (LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT >= 1)
+#    define TEST_OP				(op <= op_end)
+#  endif
+#  if (LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT >= 2)
+#    undef TEST_OP				/* don't need both of the tests here */
+#    define NEED_OP(x) \
+			if ((lzo_uint)(op_end - op) < (lzo_uint)(x))  goto output_overrun
+#  endif
+#endif
+
+#if defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND)
+#  define TEST_LOOKBEHIND(m_pos,out)	if (m_pos < out) goto lookbehind_overrun
+#else
+#  define TEST_LOOKBEHIND(m_pos,op)		((void) 0)
+#endif
+
+
+#if !defined(LZO_EOF_CODE) && !defined(TEST_IP)
+   /* if we have no EOF code, we have to test for the end of the input */
+#  define TEST_IP				(ip < ip_end)
+#endif
+
+
+#if defined(TEST_IP)
+#  define HAVE_TEST_IP
+#else
+#  define TEST_IP				1
+#endif
+#if defined(TEST_OP)
+#  define HAVE_TEST_OP
+#else
+#  define TEST_OP				1
+#endif
+
+#if defined(NEED_IP)
+#  define HAVE_NEED_IP
+#else
+#  define NEED_IP(x)			((void) 0)
+#endif
+#if defined(NEED_OP)
+#  define HAVE_NEED_OP
+#else
+#  define NEED_OP(x)			((void) 0)
+#endif
+
+
+#if defined(HAVE_TEST_IP) || defined(HAVE_NEED_IP)
+#  define HAVE_ANY_IP
+#endif
+#if defined(HAVE_TEST_OP) || defined(HAVE_NEED_OP)
+#  define HAVE_ANY_OP
+#endif
+
+
+
+/*
+vi:ts=4
+*/
+
--- linux-2.6.25/fs/ext2/e2compr_version.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/e2compr_version.h	2008-08-01 15:59:40.000000000 +0200
@@ -0,0 +1 @@
+#define E2COMPR_VERSION "0.4.55 (1 August 2008)"
--- linux-2.6.25/fs/ext2/ioctl.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/ext2/ioctl.c	2008-07-31 15:13:12.000000000 +0200
@@ -7,7 +7,18 @@
  * Universite Pierre et Marie Curie (Paris VI)
  */
 
-#include "ext2.h"
+/*
+ *  Copyright (C) 2001 Alcatel Business Systems - R&D Illkirch
+ *  Added by Pierre Peiffer (pierre.peiffer@sxb.bsf.alcatel.fr) (2001-08-09)
+ *  (Copied from pjm patch e2compr-0.4.39-patch-2.2.18)
+ *      e2compress stuff.
+ *
+ * << Copyright (C) 1995  Antoine Dumesnil de Maricourt (dumesnil@etca.fr) >>
+ * <<     (transparent compression code)                                 >>
+ */
+
+#include <linux/fs.h>
+#include <linux/ext2_fs_c.h>
 #include <linux/capability.h>
 #include <linux/time.h>
 #include <linux/sched.h>
@@ -15,6 +26,69 @@
 #include <linux/smp_lock.h>
 #include <asm/current.h>
 #include <asm/uaccess.h>
+#include <linux/stat.h>
+
+#ifdef CONFIG_EXT2_COMPRESS
+#include <linux/kmod.h>
+#include "assert.h"
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifdef CONFIG_EXT2_COMPRESS
+#ifdef CONFIG_GZ_HACK
+static int check_name(struct inode *ino)
+{
+  struct dentry *dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias);
+  if (dentry)
+    if (
+
+           (dentry->d_name.len >= 4) &&
+                 (((dentry->d_name.name[dentry->d_name.len - 2] == 'g')
+                   && (dentry->d_name.name[dentry->d_name.len - 1] == 'z')
+                   && ((dentry->d_name.name[dentry->d_name.len - 3] == '.')
+                       || (dentry->d_name.name[dentry->d_name.len - 4] == '.')))
+
+                  || ((dentry->d_name.name[dentry->d_name.len - 3] == 't')
+                      && (dentry->d_name.name[dentry->d_name.len - 2] == 'g')
+                      && (dentry->d_name.name[dentry->d_name.len - 1] == 'z')
+                      && (dentry->d_name.name[dentry->d_name.len - 4] == '.')
+                      && (dentry->d_name.len >= 5))
+
+                  || ((dentry->d_name.name[dentry->d_name.len - 3] == 'p')
+                      && (dentry->d_name.name[dentry->d_name.len - 2] == 'n')
+                      && (dentry->d_name.name[dentry->d_name.len - 1] == 'g')
+                      && (dentry->d_name.name[dentry->d_name.len - 4] == '.')
+                      && (dentry->d_name.len >= 5))
+
+                  || ((dentry->d_name.name[dentry->d_name.len - 3] == 'j')
+                      && (dentry->d_name.name[dentry->d_name.len - 2] == 'p')
+                      && (dentry->d_name.name[dentry->d_name.len - 1] == 'g')
+                      && (dentry->d_name.name[dentry->d_name.len - 4] == '.')
+                      && (dentry->d_name.len >= 5))
+
+                  || ((dentry->d_name.name[dentry->d_name.len - 3] == 'b')
+                      && (dentry->d_name.name[dentry->d_name.len - 2] == 'z')
+                      && (dentry->d_name.name[dentry->d_name.len - 1] == '2')
+                      && (dentry->d_name.name[dentry->d_name.len - 4] == '.')
+                      && (dentry->d_name.len >= 5))
+
+                  || ((dentry->d_name.name[dentry->d_name.len - 3] == 'm')
+                      && (dentry->d_name.name[dentry->d_name.len - 2] == 'n')
+                      && (dentry->d_name.name[dentry->d_name.len - 1] == 'g')
+                      && (dentry->d_name.name[dentry->d_name.len - 4] == '.')
+                      && (dentry->d_name.len >= 5))
+                  )
+       ) {
+        return 1;
+    }
+  return 0;
+}
+#endif
+#endif
+
 
 
 long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
@@ -23,6 +97,10 @@ long ext2_ioctl(struct file *filp, unsig
 	struct ext2_inode_info *ei = EXT2_I(inode);
 	unsigned int flags;
 	unsigned short rsv_window_size;
+#ifdef CONFIG_EXT2_COMPRESS
+       	unsigned long datum;
+       	int err;
+#endif
 
 	ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
 
@@ -46,7 +124,10 @@ long ext2_ioctl(struct file *filp, unsig
 		if (!S_ISDIR(inode->i_mode))
 			flags &= ~EXT2_DIRSYNC_FL;
 
+#ifdef CONFIG_EXT2_COMPRESS
 		mutex_lock(&inode->i_mutex);
+#endif
+
 		/* Is it quota file? Do not allow user to mess with it */
 		if (IS_NOQUOTA(inode)) {
 			mutex_unlock(&inode->i_mutex);
@@ -62,15 +143,139 @@ long ext2_ioctl(struct file *filp, unsig
 		 */
 		if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
 			if (!capable(CAP_LINUX_IMMUTABLE)) {
+#ifdef CONFIG_EXT2_COMPRESS
 				mutex_unlock(&inode->i_mutex);
+#endif					
 				return -EPERM;
 			}
 		}
 
 		flags = flags & EXT2_FL_USER_MODIFIABLE;
+#ifdef CONFIG_EXT2_COMPRESS
+		if (S_ISREG (inode->i_mode) || S_ISDIR (inode->i_mode)) {		
+			
+			/* pjm 1998-01-14: In previous versions of
+			     e2compr, the kernel forbade raising
+			     EXT2_ECOMPR_FL from userspace.  I can't
+			     think of any purpose for forbidding this,
+			     and I find it useful to raise
+			     EXT2_ECOMPR_FL for testing purposes, so
+			     I've removed the forbidding code. */
+			if (S_ISREG (inode->i_mode)
+			    && (EXT2_NOCOMPR_FL
+				& (flags ^ ei->i_flags))) {   // mw hint:  ^ is a (excluisive OR)
+				/* NOCOMPR_FL can only be changed if
+				   nobody else has the file opened.  */
+				/* pjm 1998-02-16: inode->i_count is
+				   useless to us because only dentries
+				   use inodes now.  Unfortunately,
+				   there isn't an easy way of finding
+				   the equivalent.  We'd have to go
+				   through all dentries using the
+				   inode, and sum their d_count
+				   values.  Rather than do that, I'd
+				   rather get rid of the exclusion
+				   constraint.  todo. */				
+				//printk("i_count: %i\n", atomic_read(&inode->i_count));
+				//if (atomic_read(&inode->i_count) > 1)
+				//if (0)
+				if (ext2_get_dcount(inode) > 1)
+				{
+					mutex_unlock(&inode->i_mutex); /*mw*/
+					return -ETXTBSY;
+				}
+				else {
+					/* pjm 970429: Discarding
+					     cached pages is not very
+					     clean, but should work. */
+					/* pjm 980114: Not quite.  We
+					     should also sync any
+					     mappings to buffers first.
+					     This isn't very important,
+					     as none of the current
+					     e2compr programs can
+					     trigger this, but todo. */
+					invalidate_remote_inode (inode);
+				}
+			}
+
+			if (EXT2_COMPR_FL
+			    & (flags ^ ei->i_flags)) {
+				if (flags & EXT2_COMPR_FL) {
+					if (ei->i_flags & EXT2_COMPRBLK_FL) {
+						/* There shouldn't actually be any
+						   compressed blocks, AFAIK.  However,
+						   this is still possible because sometimes
+						   COMPRBLK gets raised just to stop 
+						   us changing cluster size at the wrong
+						   time.
+
+						   todo: Call a function that just
+						   checks that there are not compressed
+						   clusters, and print a warning if any are
+						   found. */
+					} else {
+						int bits = MIN(EXT2_DEFAULT_LOG2_CLU_NBLOCKS,
+							       (EXT2_LOG2_MAX_CLUSTER_BYTES
+								- inode->i_sb->s_blocksize_bits));
+
+						ei->i_log2_clu_nblocks = bits;
+						ei->i_clu_nblocks = 1 << bits;
+					}
+					ei->i_compr_method = EXT2_DEFAULT_COMPR_METHOD;
+					if (S_ISREG (inode->i_mode)) {
+						//compress	
+#ifdef CONFIG_GZ_HACK
+						/*  mw: check for .gz-files and similar
+						 *  I think this is the most clever place for
+						 * rejecting files. They remain regular, uncompressed
+						 * files and though can be read bypassing all 
+						 * compression stuff (= fast) :-). And it seems to save 
+						 * space... somehow */						
+						if (check_name (inode))
+						{
+							//printk("non-compressable file extension\n");
+							mutex_unlock(&inode->i_mutex);
+							return 0;
+						}
+#endif
+						//set flags to trigger compression later on
+						flags |= EXT2_DIRTY_FL;
+						ei->i_compr_flags |= EXT2_CLEANUP_FL;
+					}
+				} else if (S_ISREG (inode->i_mode)) {
+					if (ei->i_flags & EXT2_COMPRBLK_FL) {
+						int err;
+						
+						if (ext2_get_dcount(inode) > 1){
+							mutex_unlock(&inode->i_mutex); //mw
+							return -ETXTBSY;
+						}
+						err = ext2_decompress_inode(inode);
+						if (err)
+						{
+				            mutex_unlock(&inode->i_mutex); //mw
+							return err;
+						}
+					}
+					ei->i_flags &= ~EXT2_DIRTY_FL;
+					ei->i_compr_flags &= ~EXT2_CLEANUP_FL;
+				}
+			}
+		}
+#endif
 		flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
+
+		/* bug fix: scrub 'B' flag from uncompressed files TLL 02/28/07 */
+		if (!(flags & EXT2_COMPR_FL) && (flags & EXT2_COMPRBLK_FL) )
+		{ 
+				flags &= ~EXT2_COMPRBLK_FL;
+		}
+
 		ei->i_flags = flags;
+#ifdef CONFIG_EXT2_COMPRESS		
 		mutex_unlock(&inode->i_mutex);
+#endif        
 
 		ext2_set_inode_flags(inode);
 		inode->i_ctime = CURRENT_TIME_SEC;
@@ -133,6 +338,185 @@ long ext2_ioctl(struct file *filp, unsig
 		mutex_unlock(&ei->truncate_mutex);
 		return 0;
 	}
+#ifdef CONFIG_EXT2_COMPRESS
+	case EXT2_IOC_GETCOMPRMETHOD:	/* Result means nothing if COMPR_FL is not set */
+ 		return put_user (ei->i_compr_method, (long *) arg);
+	case EXT2_IOC_SETCOMPRMETHOD:
+		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+			return -EPERM;
+		if (IS_RDONLY (inode))
+			return -EROFS;
+		if (get_user (datum, (long*) arg))
+			return -EFAULT;
+		if (!S_ISREG (inode->i_mode) && !S_ISDIR (inode->i_mode)) 
+			return -ENOSYS;
+		/* todo: Allow the below, but set initial value of
+		   i_compr_meth at read_inode() time (using default if
+		   !/) instead of +c time.  Same for cluster
+		   size. */
+		if ((unsigned) datum >= EXT2_N_METHODS)
+			return -EINVAL;
+		if (ei->i_compr_method != datum) {
+			if ((ei->i_compr_method == EXT2_NEVER_METH)
+			    && (ei->i_flags & EXT2_COMPR_FL))
+				return -EPERM;
+			/* If the previous method was `defer' then
+			   take a look at all uncompressed clusters
+			   and try to compress them.  (pjm 1997-04-16) */
+			if ((ei->i_compr_method == EXT2_DEFER_METH)
+			    && S_ISREG (inode->i_mode)) {
+				ei->i_flags |= EXT2_DIRTY_FL;
+				ei->i_compr_flags |= EXT2_CLEANUP_FL;
+			}
+			if ((datum == EXT2_NEVER_METH)
+			    && S_ISREG (inode->i_mode)) {
+				//printk("SETCOMPR\n");
+				if ((ei->i_flags & EXT2_COMPRBLK_FL))
+				{
+					/*mw*/
+					mutex_lock(&inode->i_mutex);
+					if (ext2_get_dcount(inode) > 1){
+						mutex_unlock(&inode->i_mutex); /*mw*/
+						return -ETXTBSY;
+					}
+					err = ext2_decompress_inode(inode);
+					mutex_unlock(&inode->i_mutex);
+					if ( err < 0)
+						return err;
+				}
+				ei->i_flags &= ~EXT2_DIRTY_FL;
+				ei->i_compr_flags &= ~EXT2_CLEANUP_FL;
+			}
+			ei->i_compr_method = datum;
+			inode->i_ctime = CURRENT_TIME;
+			mark_inode_dirty(inode);
+		}
+#ifdef CONFIG_KMOD
+		if (!ext2_algorithm_table[ext2_method_table[datum].alg].avail) {
+			char str[32];
+
+			sprintf(str, "ext2-compr-%s", ext2_algorithm_table[ext2_method_table[datum].alg].name);
+			request_module(str);
+		}
+#endif
+		datum = ((datum < EXT2_N_METHODS)
+			 && (ext2_algorithm_table[ext2_method_table[datum].alg].avail));
+		return put_user(datum, (long *)arg);
+
+	case EXT2_IOC_GETCLUSTERBIT:
+		if (get_user (datum, (long*) arg))
+			return -EFAULT;
+		if (!S_ISREG (inode->i_mode))
+			return -ENOSYS;
+		/* We don't do `down(&inode->i_sem)' here because
+		   there's no way for userspace to do the
+		   corresponding up().  Userspace must rely on
+		   EXT2_NOCOMPR_FL if it needs to lock. */
+		err = ext2_cluster_is_compressed (inode, datum);
+		if (err < 0)
+			return err;
+		return put_user ((err ? 1 : 0),
+				 (long *) arg);
+
+	case EXT2_IOC_RECOGNIZE_COMPRESSED:
+		if (get_user (datum, (long*) arg))
+			return -EFAULT;
+		if (!S_ISREG (inode->i_mode))
+			return -ENOSYS;
+		if (IS_RDONLY (inode))
+			return -EROFS;
+		return ext2_recognize_compressed (inode, datum);
+		
+	case EXT2_IOC_GETCLUSTERSIZE:
+		/* Result means nothing if COMPR_FL is not set (until
+                   SETCLUSTERSIZE w/o COMPR_FL is implemented;
+                   todo). */
+		if (!S_ISREG (inode->i_mode)
+		    && !S_ISDIR (inode->i_mode)) 
+			return -ENOSYS;
+		return put_user (ei->i_clu_nblocks, (long *) arg);
+
+	case EXT2_IOC_GETFIRSTCLUSTERSIZE:
+		/* Result means nothing if COMPR_FL is not set (until
+                   SETCLUSTERSIZE w/o COMPR_FL is implemented;
+                   todo). */
+		if (!S_ISREG (inode->i_mode)
+		    && !S_ISDIR (inode->i_mode)) 
+			return -ENOSYS;
+		return put_user (ext2_first_cluster_nblocks(inode), (long *) arg);
+
+	case EXT2_IOC_SETCLUSTERSIZE:
+		printk("EXT2_IOC_SETCLUSTERSIZE\n");
+		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+			return -EPERM;
+		if (IS_RDONLY (inode))
+			return -EROFS;
+		if (get_user (datum, (long *) arg))
+			return -EFAULT;
+		if (!S_ISREG (inode->i_mode)
+		    && !S_ISDIR (inode->i_mode)) 
+			return -ENOSYS;
+
+		/* These are the only possible cluster sizes.  The
+		   cluster size must be a power of two so that
+		   clusters don't straddle address (aka indirect)
+		   blocks.  At the moment, the upper limit is constrained
+		   by how much memory is allocated for de/compression.
+		   Also, the gzip algorithms have some optimisations
+		   that assume tht the input is no more than 32KB,
+		   and in compress.c we would need to zero more bits
+		   of head->holemap.  (In previous releases, the file
+		   format was limited to 32 blocks and under 64KB.) */
+// #if EXT2_MAX_CLUSTER_BLOCKS > 32 || EXT2_MAX_CLUSTER_NBYTES > 32768
+// # error "This code not updated for cluster size yet."
+// #endif
+		switch (datum) {
+		case (1 << 2): datum = 2; break;
+		case (1 << 3): datum = 3; break;
+		case (1 << 4): datum = 4; break;
+		case (1 << 5): datum = 5; break;
+		default: return -EINVAL;
+		}
+
+		assert (ei->i_clu_nblocks == (1 << ei->i_log2_clu_nblocks));
+		if (datum == ei->i_log2_clu_nblocks)
+			return 0;
+
+		if (ei->i_flags & EXT2_ECOMPR_FL)
+			return -EPERM;
+		if (!(ei->i_flags & EXT2_COMPR_FL))
+			return -ENOSYS;
+
+		/* We currently lack a mechanism to change the cluster
+		   size if there are already some compressed clusters.
+		   The compression must be done in userspace
+		   (e.g. with the e2compress program) instead.  */
+		if (ei->i_flags & EXT2_COMPRBLK_FL)
+			return -ENOSYS;
+
+		if (datum + inode->i_sb->s_blocksize_bits
+		    > EXT2_LOG2_MAX_CLUSTER_BYTES)
+			return -EINVAL;
+
+		ei->i_log2_clu_nblocks = datum;
+		ei->i_clu_nblocks = 1 << datum;
+		inode->i_ctime = CURRENT_TIME;
+		mark_inode_dirty(inode);
+		return 0;
+
+	case EXT2_IOC_GETCOMPRRATIO:		
+		if (!S_ISREG (inode->i_mode)) 
+			return -ENOSYS;
+		if (ei->i_flags & EXT2_ECOMPR_FL)
+			return -EPERM;
+		if ((long) (datum = ext2_count_blocks (inode)) < 0)
+			return datum;
+		if ((err = put_user ((long) datum, (long*) arg)))
+			return err;
+		return put_user ((long) inode->i_blocks, (long*) arg + 1);
+		
+		
+#endif
 	default:
 		return -ENOTTY;
 	}
--- linux-2.6.25/fs/ext2/compress_syms.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/compress_syms.c	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,19 @@
+
+/*
+ * linux/fs/ext2/compress_syms.c
+ *
+ * Exported kernel symbols for the ext2 compression.
+ * These symbols are used by compression algorithms.
+ */
+
+#include <linux/module.h>
+
+#include <linux/mm.h>
+#include <linux/ext2_fs_c.h>
+
+struct super_block;
+
+EXPORT_SYMBOL(ext2_adler32);
+EXPORT_SYMBOL(ext2_register_compression_module);
+EXPORT_SYMBOL(ext2_unregister_compression_module);
+
--- linux-2.6.25/fs/ext2/ext2.h	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/ext2/ext2.h	2008-07-31 15:13:12.000000000 +0200
@@ -37,6 +37,12 @@ struct ext2_inode_info {
 	struct ext2_block_alloc_info *i_block_alloc_info;
 
 	__u32	i_dir_start_lookup;
+#ifdef CONFIG_EXT2_COMPRESS
+	__u8    i_log2_clu_nblocks;
+	__u8    i_clu_nblocks;
+	__u8    i_compr_method;
+	__u8    i_compr_flags;
+#endif
 #ifdef CONFIG_EXT2_FS_XATTR
 	/*
 	 * Extended attributes can be read independently of the main file
--- linux-2.6.25/fs/ext2/compress.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/fs/ext2/compress.c	2008-08-01 16:31:04.000000000 +0200
@@ -0,0 +1,3756 @@
+/*
+ *  linux/fs/ext2/compress.c
+ *
+ *  Copyright (C) 1995  Antoine Dumesnil de Maricourt (dumesnil@etca.fr) 
+ *      (transparent compression code)
+ */
+
+/*
+ *  Copyright (C) 2001 Alcatel Business Systems - R&D Illkirch FRANCE
+ *
+ *  	Transparent compression code for 2.4 kernel.
+ *
+ *  Denis Richard (denis.richard@sxb.bsf.alcatel.fr)
+ *  Pierre Peiffer (pierre.peiffer@sxb.bsf.alcatel.fr)
+ *
+ *  Adapted from patch e2compr-0.4.39-patch-2.2.18 .
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include "debug.h"
+#include <linux/ext2_fs.h>
+#include <linux/ext2_fs_c.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/buffer_head.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/quotaops.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/swap.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/writeback.h>
+#include <linux/rmap.h>
+#include <linux/swap.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel_stat.h>
+#include <linux/swap.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/highmem.h>
+#include <linux/vmstat.h>
+#include <linux/file.h>
+#include <linux/writeback.h>
+#include <linux/blkdev.h>
+#include <linux/buffer_head.h>
+#include <linux/mm_inline.h>
+#include <linux/pagevec.h>
+#include <linux/backing-dev.h>
+#include <linux/rmap.h>
+#include <linux/topology.h>
+#include <linux/cpu.h>
+#include <linux/cpuset.h>
+#include <linux/notifier.h>
+#include <linux/rwsem.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <asm/tlbflush.h>
+#include <asm/div64.h>
+#include <linux/swapops.h>
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/* Working area to compress or decompress data.  This is a single
+   shared resource.  At present it is even shared between processors.
+   See fs/ext2/super.c for de/allocation.
+ 
+   I wouldn't mind having one working area dedicated to reads
+   (i.e. decompression).  The problem that would solve is that things
+   can be very jerky if most of your hard disk is compressed because
+   whenever an executable or something pages in from disk, it has to
+   wait for any compression to finish; compression can be slow.
+
+   Alternative solutions would be to get caching-to-swap working, or
+   just making sure that the compressing process(es) set themselves to
+   low priority when waiting on the working area and normal (or high)
+   priority once they've got it.
+
+   Another idea would be for compression to be preemptible by
+   decompression (unless compressing to a device very short of space).
+   This idea is at its best if compression happens during the idle
+   thread, where nothing has to wait for it.
+ 
+ */
+
+struct ext2_wa_S *ext2_rd_wa = NULL;
+#ifdef CONFIG_EXT2_SEPARATE_WORK_AREAS
+struct ext2_wa_S *ext2_wr_wa = NULL;
+#endif
+
+/* !!!   This is the size of _heap_ only   !!! */
+size_t ext2_rd_wa_size = 0;
+#ifdef CONFIG_EXT2_SEPARATE_WORK_AREAS
+size_t ext2_wr_wa_size = 0;
+#endif
+
+/* Use of this isn't fully implemented at time of writing (we update
+   it but never consult it), but the idea is to get around a problem
+   with readpage where it can decompress a cluster several times if
+   there are several pages to a cluster (which is the usual case).
+   The cost of the optimisation is: (1) this struct in memory and the
+   minor CPU cost of updating it, and (2) there's extra latency in
+   accessing the first page, because we decompress the whole cluster
+   rather than just enough to fill the page (as we previously did). */
+
+/* What's the best way of identifying a cluster?  Here we use the
+   three-tiered <inode->i_rdev, inode->i_ino, clusternum>. */
+struct ext2_wa_contents_S ext2_rd_wa_ucontents;
+#ifdef CONFIG_EXT2_SEPARATE_WORK_AREAS
+struct ext2_wa_contents_S ext2_wr_wa_ucontents;
+#endif
+
+/* Lock to prevent concurrent use of the working area. */
+static int ext2_wa_lock[EXT2_N_WA] = { 0 };
+
+# ifdef CONFIG_EXT2_SEPARATE_WORK_AREAS
+
+#  if EXT2_N_WA != 2
+#   error EXT2_N_WA constant has wrong value
+#  endif
+static wait_queue_head_t ext2_wa_wait[EXT2_N_WA] =
+    { __WAIT_QUEUE_HEAD_INITIALIZER(ext2_wa_wait[0]),
+    __WAIT_QUEUE_HEAD_INITIALIZER(ext2_wa_wait[1])
+};
+
+# else
+
+#  if EXT2_N_WA != 1
+#   error EXT2_N_WA constant has wrong value
+#  endif
+static wait_queue_head_t ext2_wa_wait[EXT2_N_WA] =
+    { __WAIT_QUEUE_HEAD_INITIALIZER(ext2_wa_wait[0]) };
+
+# endif
+
+
+#ifdef CONFIG_HIGHMEM
+#define restore_b_data_himem(bh)   assert(page_address(bh->b_page));  bh->b_data = page_address(bh->b_page) + bh_offset(bh)
+
+int ext2_kmap_cluster_pages(struct page *page, struct page *pg[],
+			    struct page *epg[])
+{
+    int i = 0;
+
+    for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+	if (!pg[i])
+	    break;
+	if (epg && epg[i])
+	    kmap(epg[i]);
+	else
+	    kmap(pg[i]);
+    }
+
+    if (page)
+	kmap(page);
+    return 0;
+}
+
+
+int ext2_kunmap_cluster_pages(struct page *page, struct page *pg[],
+			      struct page *epg[])
+{
+    int i = 0;
+
+    for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+	if (!pg[i])
+	    break;
+	if (epg && epg[i])
+	    kunmap(epg[i]);
+	else
+	    kunmap(pg[i]);
+    }
+
+    if (page)
+	kunmap(page);
+    return 0;
+}
+#else //nohimem:
+#define restore_b_data_himem(bh)	;
+#endif
+
+/*
+ *    Algorithm and method tables
+ */
+
+struct ext2_algorithm ext2_algorithm_table[] = {
+    /* Note: all algorithms must have the `name' field filled in.
+       This is used to autoload algorithm modules (ext2-compr-%s), and
+       in kernel printk. */
+    /* N.B. Do not renumber these algorithms!  (To do so is to change
+       the binary format.)  It's OK for `none' and `undef' to be
+       renumbered, though. */
+
+    /* Fields:
+       name; available; routines for:
+       init,  compress,   decompress. */
+#ifdef  CONFIG_EXT2_HAVE_LZV1
+    {"lzv1", 1, ext2_iLZV1, ext2_wLZV1, ext2_rLZV1},
+#else
+    {"lzv1", 0, ext2_iNONE, ext2_wNONE, ext2_rNONE},
+#endif
+
+#ifdef  CONFIG_EXT2_HAVE_LZRW3A
+    {"lzrw3a", 1, ext2_iLZRW3A, ext2_wLZRW3A, ext2_rLZRW3A},
+#else
+    {"lzrw3a", 0, ext2_iNONE, ext2_wNONE, ext2_rNONE},
+#endif
+
+    {"gzip", 1, ext2_iZLIB, ext2_wZLIB, ext2_rZLIB},	//Andreas: workaround  
+
+#ifdef  CONFIG_EXT2_HAVE_BZIP2
+    {"bzip2", 1, ext2_iBZIP2, ext2_wBZIP2, ext2_rBZIP2},
+#else
+    {"bzip2", 0, ext2_iNONE, ext2_wNONE, ext2_rNONE},
+#endif
+
+#ifdef  CONFIG_EXT2_HAVE_LZO
+    {"lzo", 1, ext2_iLZO, ext2_wLZO, ext2_rLZO},
+#else
+    {"lzo", 0, ext2_iNONE, ext2_wNONE, ext2_rNONE},
+#endif
+
+    {"none", 1, ext2_iNONE, ext2_wNONE, ext2_rNONE},
+
+    /* This "algorithm" is for unused entries in the method table.
+       It differs from EXT2_NONE_ALG in that it is considered
+       unavailable, whereas `none' is always available. */
+    {"undef", 0, ext2_iNONE, ext2_wNONE, ext2_rNONE},
+
+};
+
+/* Note: EXT2_N_ALGORITHMS can't be increased beyond 16 without
+   changing the width of the s_algorithms_used field in the in-memory
+   superblock.  The on-disk s_algorithms_used field is 32 bits long.
+   (This is in a state of flux.  Currently (1998-02-05) there is no
+   distinction: we always use the s_es copy. */
+
+
+/* The size of this table must be 32 to prevent Oopsen from 
+   invalid data.  We index this from 5 bits of i_flags, so
+   the size is (1 << 5) == 32. */
+struct ext2_method ext2_method_table[32] = {
+    /* Fields: algorithm id, algorithm argument. */
+    {EXT2_LZV1_ALG, 0},
+    {EXT2_NONE_ALG, 0},		/* 1: auto */
+    {EXT2_NONE_ALG, 0},		/* 2: defer */
+    {EXT2_NONE_ALG, 0},		/* 3: never */
+    {EXT2_BZIP2_ALG, 0},	/* 4: bzip2 */
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_LZRW3A_ALG, 0},	/* 8: lzrw3a */
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_LZO_ALG, 0},		/* 10: lzo1x_1 */
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_GZIP_ALG, 1},		/* 16 */
+    {EXT2_GZIP_ALG, 2},
+    {EXT2_GZIP_ALG, 3},
+    {EXT2_GZIP_ALG, 4},
+    {EXT2_GZIP_ALG, 5},
+    {EXT2_GZIP_ALG, 6},
+    {EXT2_GZIP_ALG, 7},
+    {EXT2_GZIP_ALG, 8},
+    {EXT2_GZIP_ALG, 9},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0},
+    {EXT2_UNDEF_ALG, 0}
+};
+
+/* Returns 0 on success, -ENOMEM on failure i.e. if working area
+ * is smaller than what module needs and we failed to allocate bigger one
+ */
+int ext2_register_compression_module(unsigned alg, size_t compress_memory,
+				     size_t decompress_memory,
+				     struct ext2_algorithm *new_algorithm)
+{
+    if (alg >= EXT2_N_ALGORITHMS) {
+	printk(KERN_ERR
+	       "EXT2-fs: trying to load invalid algorithm %u\n", alg);
+	return -EINVAL;
+    }
+    if (EXT2_ALGORITHMS_BUILTIN & (1 << alg)) {
+	printk(KERN_ERR
+	       "EXT2-fs: trying to load built-in algorithm %u!\n", alg);
+	return -EINVAL;
+    }
+
+    printk(KERN_INFO "EXT2-fs: loading compression algorithm %u\n", alg);
+
+#ifdef CONFIG_EXT2_SEPARATE_WORK_AREAS
+    if (ext2_wr_wa_size < compress_memory) {
+	struct ext2_wa_S *new_ext2_wa;
+	size_t size;
+
+	printk(KERN_DEBUG
+	       "EXT2-fs: allocating more memory for algorithm %u\n", alg);
+
+	if (ext2_wr_wa != NULL && ext2_lock_wr_wa() == 0)
+	    return -EINTR;	/* Maybe EBUSY would be better? */
+	size = compress_memory + 2 * EXT2_MAX_CLUSTER_BYTES;
+	new_ext2_wa = vmalloc(size);
+	if (new_ext2_wa == NULL) {
+	    printk(KERN_WARNING
+		   "EXT2-fs: can't allocate working area "
+		   "for algorithm %u.\n", alg);
+	    ext2_unlock_wr_wa();
+	    return -ENOMEM;
+	}
+# ifdef EXT2_COMPR_REPORT_CPR
+	printk(KERN_INFO "EXT2-fs: wr_wa=%p--%p (%u)\n",
+	       new_ext2_wa, (char *) new_ext2_wa + size, size);
+# endif
+	vfree(ext2_wr_wa);
+	ext2_wr_wa = new_ext2_wa;
+	ext2_wr_wa_size = compress_memory;
+	ext2_unlock_wr_wa();
+    }
+    if (ext2_rd_wa_size < decompress_memory) {
+	struct ext2_wa_S *new_ext2_wa;
+	size_t size;
+
+	printk(KERN_INFO
+	       "EXT2-fs: allocating more memory for algorithm %u\n", alg);
+
+	if (ext2_rd_wa != NULL && ext2_lock_rd_wa() == 0)
+	    return -EINTR;	/* Maybe EBUSY would be better? */
+	size = decompress_memory + 2 * EXT2_MAX_CLUSTER_BYTES;
+	new_ext2_wa = vmalloc(size);
+	if (new_ext2_wa == NULL) {
+	    printk(KERN_WARNING
+		   "EXT2-fs: can't allocate working area "
+		   "for algorithm %u.\n", alg);
+	    ext2_unlock_rd_wa();
+	    return -ENOMEM;
+	}
+# ifdef EXT2_COMPR_REPORT_CPR
+	printk(KERN_INFO "EXT2-fs: rd_wa=%p--%p (%u)\n",
+	       new_ext2_wa, (char *) new_ext2_wa + size, size);
+# endif
+	vfree(ext2_rd_wa);
+	ext2_rd_wa = new_ext2_wa;
+	ext2_rd_wa_size = decompress_memory;
+	ext2_unlock_rd_wa();
+    }
+#else
+    if (compress_memory < decompress_memory)
+	compress_memory = decompress_memory;
+    if (ext2_rd_wa_size < compress_memory) {
+	struct ext2_wa_S *new_ext2_wa;
+	size_t size;
+
+	printk(KERN_INFO
+	       "EXT2-fs: allocating more memory for algorithm %u\n", alg);
+
+	if (ext2_rd_wa != NULL && ext2_lock_rd_wa() == 0)
+	    return -EINTR;	/* Maybe EBUSY would be better? */
+	size = compress_memory + 2 * EXT2_MAX_CLUSTER_BYTES;
+	new_ext2_wa = vmalloc(size);
+	if (new_ext2_wa == NULL) {
+	    printk(KERN_WARNING
+		   "EXT2-fs: can't allocate working area "
+		   "for algorithm %u.\n", alg);
+	    ext2_unlock_rd_wa();
+	    return -ENOMEM;
+	}
+# ifdef EXT2_COMPR_REPORT_CPR
+	printk(KERN_INFO "EXT2-fs: wa=%p--%p (%u)\n",
+	       new_ext2_wa, (char *) new_ext2_wa + size, size);
+# endif
+	vfree(ext2_rd_wa);
+	ext2_rd_wa = new_ext2_wa;
+	ext2_rd_wa_size = compress_memory;
+	ext2_unlock_rd_wa();
+    }
+#endif
+    ext2_algorithm_table[alg].avail = 1;
+    ext2_algorithm_table[alg].init = new_algorithm->init;
+    ext2_algorithm_table[alg].compress = new_algorithm->compress;
+    ext2_algorithm_table[alg].decompress = new_algorithm->decompress;
+    printk(KERN_INFO "EXT2-fs: compression algorithm %u loaded\n", alg);
+
+    return 0;
+}
+
+void ext2_unregister_compression_module(unsigned alg)
+{
+    if (alg >= EXT2_N_ALGORITHMS)
+	printk(KERN_ERR
+	       "EXT2-fs: trying to unload invalid algorithm %u\n", alg);
+    else if (EXT2_ALGORITHMS_BUILTIN & (1 << alg))
+	printk(KERN_ERR
+	       "EXT2-fs: trying to unload non-module algorithm %u\n", alg);
+    else {
+	ext2_algorithm_table[alg].avail = 0;
+	ext2_algorithm_table[alg].init = ext2_iNONE;
+	ext2_algorithm_table[alg].compress = ext2_wNONE;
+	ext2_algorithm_table[alg].decompress = ext2_rNONE;
+	/* TODO: I guess we ought to shrink the compression heap here if possible. */
+	printk(KERN_INFO
+	       "EXT2-fs: compression algorithm %u unloaded\n", alg);
+    }
+}
+
+/* Returns 1 on success, 0 on "failure" (see code).
+
+   It is the caller's responsibility to check that the working area is
+   allocated. */
+int ext2_lock_wa(unsigned id)
+{
+    assert(id < EXT2_N_WA);
+    switch (id) {
+    case 0:
+	if (ext2_rd_wa == NULL) {
+	    printk(KERN_WARNING
+		   "ext2_lock_rd_wa: working area not allocated.\n");
+	    return 0;
+	}
+	break;
+#ifdef CONFIG_EXT2_SEPARATE_WORK_AREAS
+    case 1:
+	if (ext2_wr_wa == NULL) {
+	    printk(KERN_WARNING
+		   "ext2_lock_wr_wa: working area not allocated.\n");
+	    return 0;
+	}
+	break;
+#endif
+    default:
+	printk(KERN_WARNING "ext2_lock_rd_wa: wrong working area id.\n");
+	return 0;
+    }
+
+    while (ext2_wa_lock[id]) {
+#if 1
+	/* pjm 1998-02-19: I'm putting this back on trial (see
+	   comment in #else).  What's different is that
+	   callers check for ext2_wa==NULL themselves, and
+	   return EINTR instead of EBUSY when ext2_lock_wa
+	   returns zero. */
+	interruptible_sleep_on(&ext2_wa_wait[id]);
+#else
+	/* Better not to be interruptible: this will avoid
+	   some 'File busy' when processes get the sleep
+	   signal and are woken up later.  We could however
+	   avoid that if the access to the working area was
+	   made atomic (it is no longer because of IND_BITMAP
+	   being defined, and now (as of 0.3.5) because of
+	   schedule()).
+
+	   pjm 1998-01-13: I think `EBUSY' is meant rather
+	   than `File busy'. */
+	sleep_on(&ext2_wa_wait[id]);
+#endif
+	if (signal_pending(current))
+	    return 0;
+    }
+    ext2_wa_lock[id] = 1;
+#ifdef EXT2_COMPR_REPORT_WA
+    printk(KERN_DEBUG "ext2: process %u locks wa %u\n", current->pid, id);
+#endif
+    return 1;
+}
+
+
+/* ,Version of ext2_lock_wa` for ext2_readpage, because I don't know
+   what to do with signals arriving during ext2_readpage(). */
+void ext2_lock_rd_wa_uninterruptible(void)
+{
+    assert(ext2_rd_wa != NULL);
+    while (ext2_wa_lock[ext2_rd_wa_ix])
+	sleep_on(&ext2_wa_wait[ext2_rd_wa_ix]);
+    ext2_wa_lock[ext2_rd_wa_ix] = 1;
+#ifdef EXT2_COMPR_REPORT_WA
+    printk(KERN_DEBUG
+	   "ext2: process %u locks wa %u\n", current->pid, ext2_rd_wa_ix);
+#endif
+}
+
+
+void ext2_unlock_wa(unsigned id)
+{
+    assert(id < EXT2_N_WA);
+    ext2_wa_lock[id] = 0;
+
+#ifdef EXT2_COMPR_REPORT_WA
+    printk(KERN_DEBUG
+	   "ext2: process %u unlocks wa %u\n", current->pid, id);
+#endif
+
+    if (waitqueue_active(&ext2_wa_wait[id])) {
+	wake_up(&ext2_wa_wait[id]);
+	schedule();
+	/* pjm 1997-08-01: responsiveness can suffer if we
+	   call schedule() only when the working area is
+	   locked. */
+    }
+}
+
+
+static void ext2_mark_algorithm_use(struct inode *inode, unsigned alg)
+{
+    struct ext2_sb_info *sbi = EXT2_SB(inode->i_sb);
+
+    /* Hopefully, lock_super() isn't needed here, as we don't
+       block in the critical region.  True? */
+    assert(alg < EXT2_N_ALGORITHMS);
+    if (sbi->s_es->s_feature_incompat
+	& cpu_to_le32(EXT2_FEATURE_INCOMPAT_COMPRESSION)) {
+	sbi->s_es->s_algorithm_usage_bitmap |= cpu_to_le32(1 << alg);
+    } else {
+	struct ext2_super_block *es = sbi->s_es;
+
+	es->s_algorithm_usage_bitmap = cpu_to_le32(1 << alg);
+	es->s_feature_incompat
+	    |= cpu_to_le32(EXT2_FEATURE_INCOMPAT_COMPRESSION);
+	if (es->s_rev_level < EXT2_DYNAMIC_REV) {
+	    /* Raise the filesystem revision level to
+	       EXT2_DYNAMIC_REV so that s_feature_incompat
+	       is honoured (except in ancient kernels /
+	       e2fsprogs).  We must also initialize two
+	       other dynamic-rev fields.  The remaining
+	       fields are assumed to be already correct
+	       (e.g. still zeroed). */
+	    es->s_rev_level = cpu_to_le32(EXT2_DYNAMIC_REV);
+	    es->s_first_ino = cpu_to_le32(EXT2_GOOD_OLD_FIRST_INO);
+	    es->s_inode_size = cpu_to_le16(EXT2_GOOD_OLD_INODE_SIZE);
+	}
+    }
+    mark_buffer_dirty(sbi->s_sbh);
+}
+
+
+/* Displays an error message if algorithm ,alg` is not marked in use,
+   and then marks it in use. */
+static void ext2_ensure_algorithm_use(struct inode *inode, unsigned alg)
+{
+    assert(alg < EXT2_N_ALGORITHMS);
+
+    if (!(EXT2_SB(inode->i_sb)->s_es->s_algorithm_usage_bitmap
+	  & cpu_to_le32(1 << alg))) {
+	ext2_warning(inode->i_sb,
+		     "algorithm usage bitmap",
+		     "algorithm %s not marked used in inode %lu",
+		     ext2_algorithm_table[alg].name, inode->i_ino);
+	ext2_mark_algorithm_use(inode, alg);
+    }
+}
+
+
+/* Matthias Winkler (MW) out of cache bug fix 5-16-07 */
+static void create_empty_buffers_e2c(struct page *page,
+				     unsigned long blocksize,
+				     unsigned long b_state,
+				     struct inode *inode)
+{
+    struct buffer_head *bh, *head, *tail;
+
+    head = alloc_page_buffers(page, blocksize, 1);
+    bh = head;
+    do {
+	bh->b_state |= b_state;
+	tail = bh;
+	bh->b_bdev = NULL;	//mw: make it like 2.4
+	bh->b_blocknr = 0;	//mw: make it like 2.4
+	bh->b_end_io = NULL;	//mw: make it like 2.4
+	bh = bh->b_this_page;
+    } while (bh);
+    tail->b_this_page = head;
+    spin_lock(&inode->i_mapping->private_lock);
+    if (PageUptodate(page) || PageDirty(page)) {
+	bh = head;
+	do {
+	    if (PageDirty(page))
+		set_buffer_dirty(bh);
+	    if (PageUptodate(page))
+		set_buffer_uptodate(bh);
+	    bh = bh->b_this_page;
+	} while (bh != head);
+    }
+    attach_page_buffers(page, head);
+    spin_unlock(&inode->i_mapping->private_lock);
+}
+
+int ext2_get_cluster_pages(struct inode *inode, u32 cluster,
+			   struct page *pg[], struct page *page, int compr)
+{
+    int nbpg, npg, i;
+    u32 page0;	/* = position within file (not position within fs). */
+    u32 idx = 0;
+    struct page *cached_page;
+    struct pagevec lru_pvec;
+
+    /*mw */
+    for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++)
+	pg[i] = NULL;
+
+    cached_page = NULL;
+    pagevec_init(&lru_pvec, 0);
+
+    page0 = ext2_cluster_page0(inode, cluster);	
+    nbpg = ext2_cluster_npages(inode, cluster);	
+
+    if (compr && (((page0 + nbpg) << PAGE_CACHE_SHIFT) > inode->i_size))	
+	nbpg = ((inode->i_size - 1) >> PAGE_CACHE_SHIFT) - page0 + 1;	
+#ifdef  EXT2_COMPR_REPORT
+    trace_e2c("ext2_get_cluster_pages: page0=%d, nbpg=%d page=%ld\n",
+	      page0, nbpg, ((page != NULL) ? page->index : 0));
+#endif
+    for (npg = 0; npg < nbpg; npg++) {
+	if ((page == NULL) || ((page0 + npg) != page->index)) {
+		pg[npg] = __grab_cache_page(inode->i_mapping,  
+ 			    page0+npg); /* &cached_page, &lru_pvec);*/
+	    if (!pg[npg])
+		goto error;
+	} else {
+	    pg[npg] = page;
+	}
+	if (!page_has_buffers(pg[npg])) {
+	    ClearPageUptodate(pg[npg]);	
+	    ClearPageDirty(pg[npg]);	
+	    create_empty_buffers_e2c(pg[npg], inode->i_sb->s_blocksize, 0, inode);	
+	    if (unlikely(!page_has_buffers(pg[npg])))
+		trace_e2c("ext2_get_cluster_pages: NOMEM!\n");
+	    assert(!PageUptodate(pg[npg]));
+	    assert(!PageDirty(pg[npg]));
+	}
+    }
+    //set remaining pages to NULL
+    for (idx = npg; idx < EXT2_MAX_CLUSTER_PAGES; idx++)
+	pg[idx] = NULL;
+
+    if (cached_page)
+	page_cache_release(cached_page);
+    pagevec_lru_add(&lru_pvec);
+    pagevec_free(&lru_pvec);
+    return (npg);
+  error:
+    if (cached_page)
+	page_cache_release(cached_page);
+    pagevec_lru_add(&lru_pvec);
+    pagevec_free(&lru_pvec);
+    while (--npg >= 0) {
+	if ((page == NULL) || ((page0 + npg) != page->index)) {
+	    unlock_page(pg[npg]);
+	    page_cache_release(pg[npg]);
+	}
+	pg[npg] = NULL;
+    }
+    trace_e2c("ext2_get_cluster_pages: error no page\n");
+    return (-ENOMEM);
+}
+
+
+int ext2_get_cluster_extra_pages(struct inode *inode, u32 cluster,
+				 struct page *pg[], struct page *epg[])
+{
+    struct page *page;
+    int nbpg, npg, i;
+
+    for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++)
+	epg[i] = NULL;
+
+    nbpg = ext2_cluster_npages(inode, cluster);
+    for (npg = 0; npg < nbpg; npg++) {
+	if (pg[npg] == NULL)
+	    break;
+	if (PageUptodate(pg[npg])) {
+	    //page = page_cache_alloc(inode->i_mapping);   
+	    //mw: has gfp-mask of adress-space:  gfp_t mapping_gfp_mask(struct address_space * mapping)
+	    //    don't trigger. shrink_dcache_memory which might call ext2_cleanup_compressed_inode with the SAME mutex.
+	    page = __page_cache_alloc(GFP_NOFS);
+
+	    if (!page) {
+		goto error;
+	    }
+	    ClearPageError(page);
+	    ClearPageReferenced(page);
+	    ClearPageUptodate(page);
+	    ClearPageDirty(page);
+	    lock_page(page);
+	    page->index = pg[npg]->index;
+
+	    if (!page_has_buffers(page)) {
+		create_empty_buffers_e2c(page, inode->i_sb->s_blocksize, 0,
+					 inode);
+		/*mw : only the "extra_pages" for decompression need create_empty_buffers_unlocked, because
+		 *     they have no mapping-context and they must not have one. Otherwise they get need a page->index
+		 *     which belongs always to an address_space object (e.g.: inode). But I think this is not intented here.
+		 *     we just need thei buffers for a short time of decompression */
+		if (unlikely(!page_has_buffers(page)))
+		    return printk("Error: NOMEM!\n");
+	    }
+
+	    epg[npg] = page;
+#ifdef  EXT2_COMPR_REPORT
+	    trace_e2c
+		("ext2_get_cluster_extra_pages: allocated page idx=%ld\n",
+		 pg[npg]->index);
+#endif
+	} else {
+	    epg[npg] = NULL;
+	}
+    }
+    return (npg);
+  error:
+    while (--npg >= 0)
+	if (epg[npg]) {
+	    ClearPageDirty(epg[npg]);
+	    ClearPageUptodate(epg[npg]);
+	    try_to_free_buffers(epg[npg]);
+	    unlock_page(epg[npg]);
+	    assert(page_count(epg[npg]) == 1);
+	    page_cache_release(epg[npg]);
+	}
+    trace_e2c("ext2_get_cluster_extra_pages: error no page\n");
+    return (-ENOMEM);
+
+}
+
+/* Read every block in the cluster.  The blocks are stored in the bh
+   array, which must be big enough.
+
+   Return the number of block contained in the cluster, or -errno if an
+   error occured.  The buffers should be released by the caller
+   (unless an error occurred).
+ 
+   The inode must be locked, otherwise it is possible that we return
+   some out of date blocks.
+ 
+   Called by :
+ 
+         ext2_decompress_cluster()      [i_sem]
+         ext2_compress_cluster()        [i_sem]
+         ext2_readpage()      		[i_sem] */
+
+
+int ext2_get_cluster_blocks(struct inode *inode, u32 cluster,
+			    struct buffer_head *bh[], struct page *pg[],
+			    struct page *epg[], int compr)
+{
+    struct buffer_head *br[EXT2_MAX_CLUSTER_BLOCKS];
+    int nreq, nbh = 0, npg, i;
+    u32 clu_nblocks;
+    int err;
+    const int blocks = PAGE_CACHE_SIZE >> inode->i_sb->s_blocksize_bits;
+
+    /*mw */
+    for (i = 0; i < EXT2_MAX_CLUSTER_BLOCKS; i++)
+	bh[i] = NULL;
+
+    assert(atomic_read(&inode->i_mutex.count) <= 0);	/* i.e. mutex_lock */
+
+    /*
+     *  Request full cluster.
+     */
+    {
+	u32 endblk;
+	u32 block; /* = position within file (not position within fs). */
+	u32 nbpg;
+	u32 page0; /* = position within file (not position within fs). */
+	u32 idx;
+
+	block = ext2_cluster_block0(inode, cluster);
+	clu_nblocks = ext2_cluster_nblocks(inode, cluster);
+	/* impl: Don't shorten endblk for i_size.  The
+	   remaining blocks should be NULL anyway, except in
+	   the case when called from ext2_decompress_cluster
+	   from ext2_truncate, in which case i_size is short
+	   and we _want_ to get all of the blocks. */
+	endblk = block + clu_nblocks;
+
+	page0 = ext2_cluster_page0(inode, cluster);
+	nbpg = ext2_cluster_npages(inode, cluster);
+
+	if (compr
+	    && (((page0 + nbpg) << PAGE_CACHE_SHIFT) > inode->i_size)) {
+	    nbpg = ((inode->i_size - 1) >> PAGE_CACHE_SHIFT) - page0 + 1;
+	    endblk =
+		block +
+		(nbpg <<
+		 (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits));
+	}
+
+	idx = page0 << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
+#ifdef  EXT2_COMPR_REPORT
+	trace_e2c("ext2_get_cluster_blocks: page0=%d, nbpg=%d\n", page0,
+		  nbpg);
+#endif
+	for (npg = 0; npg < nbpg; npg++) {
+	    struct buffer_head *buffer;
+
+	    if ((epg != NULL) && (epg[npg] != NULL))
+		buffer = page_buffers(epg[npg]);
+	    else
+		buffer = page_buffers(pg[npg]);
+	    for (i = 0; i < blocks && (block + nbh) < endblk;
+		 buffer = buffer->b_this_page, i++) {
+		if (idx == (block + nbh)) {
+		    bh[nbh] = buffer;
+		    nbh++;
+		}
+		idx++;
+	    }
+	}
+#ifdef  EXT2_COMPR_REPORT
+	trace_e2c
+	    ("ext2_get_cluster_blocks: get every pages and %d buffers\n",
+	     nbh);
+#endif
+
+	for (nbh = 0, nreq = 0; block < endblk; nbh++) {
+	    assert(bh[nbh] != NULL);
+	    bh[nbh]->b_blocknr = 0;
+	    clear_bit(BH_Mapped, &bh[nbh]->b_state);
+
+	    //mw: does not work with 2.6 and holes!!!
+	    //err=ext2_get_block(inode, block++, bh[nbh], (PageDirty(bh[nbh]->b_page) ? 1 : 0));  
+	    err = ext2_get_block(inode, block++, bh[nbh], 0);
+	    /* mw: 0: we dont' create non existing blocks here
+	     *     let's do it just before the writeback, when we know, which blocks we really need...*/
+	    //err=ext2_get_block(inode, block++, bh[nbh], (buffer_dirty(bh[nbh]) ? 1 : 0));
+
+	    /* mw: bdev-bug-fix: for files which got compressed and now consume less buffers
+	     * ext2_get_block returns 0, for a empty-block. As these buffer were used before
+	     * the bh[nbh]->b_bdev might be != NULL or just invalid. So we set them explicitly
+	     * to NULL. */
+	    //printk("Get Block cluster %i: (%#x):%i Blk-NR:%lu(%lu)[%lu-%lu] Bdev:%#x(%#x), PGDirty:%i, mapped:%i, PID: %lu\n", cluster, bh[nbh], nbh, block, 
+
+	    //if we are not mapped, then the blocknr will be wrong
+	    //we set a bdev here the we will write to some "random" block
+	    if (!buffer_mapped(bh[nbh])) {
+		bh[nbh]->b_bdev = NULL;	/* don't write wrongly mapped blocks !!! */
+		/* mw: you encounter null pointer oops you MUST
+		 *         map your buffer using ext2_get_block()*/
+	    }
+
+	    if (bh[nbh]->b_blocknr != 0) {
+		if (!buffer_uptodate(bh[nbh])
+		    /* TODO: Do we need this
+		       `!buffer_locked' test? */
+		    && !buffer_locked(bh[nbh])
+		    && !PageDirty(bh[nbh]->b_page))
+		    br[nreq++] = bh[nbh];
+	    } else if ((err != 0)
+		       && (err != -EFBIG))
+		/* impl: for some unknown reason,
+		   ext2_getblk() returns -EFBIG if
+		   !create and there's a hole. ==> not right any more in 2.4 */
+		goto error;
+	}
+	for (i = nbh; i < EXT2_MAX_CLUSTER_BLOCKS; i++) {
+	    bh[i] = NULL;
+	}
+    }
+#ifdef  EXT2_COMPR_REPORT_CPR
+    trace_e2c("ext2_get_cluster_blocks: nreq=%d for cluster=%d\n", nreq,
+	      cluster);
+#endif
+
+    //read all blocks, which are not null-blocks
+    if (nreq > 0)
+	ll_rw_block(READ, nreq, br);
+
+    /*
+     *  Adjust nbh if we have some null blocks at end of cluster.
+     */
+    while ((nbh != 0) && (bh[nbh - 1]->b_blocknr == 0))
+	nbh--;
+
+    /*
+     *  Wait for blocks.
+     */
+    err = -EIO;
+    for (i = 0; i < nbh; i++)
+	if ((!PageDirty(bh[i]->b_page)) && (bh[i]->b_blocknr != 0)) {
+	    wait_on_buffer(bh[i]);
+	    if (!buffer_uptodate(bh[i])) {	/* Read error ??? */
+		trace_e2c
+		    ("ext2_get_cluster_blocks: wait_on_buffer error (blocknr=%ld)\n",
+		     bh[i]->b_blocknr);
+		goto error;
+	    }
+	}
+    assert(nbh <= EXT2_MAX_CLU_NBLOCKS);
+
+    return nbh;
+
+  error:
+    printk("ERROR: ext2_get_cluster_blocks()\n");
+    return err;
+}
+
+
+/* Iterations over block in the inode are done with a generic
+   iteration key mechanism.  We need one method to convert a block
+   number into a new key, one method to iterate (i.e., increment the
+   key) and one method to free the key.  The code could be shared with
+   truncate.c, as this mechanism is very general.
+ 
+   This code assumes tht nobody else can read or write the file
+   between ext2_get_key() and ext2_free_key(), so callers need to have
+   i_sem (which they all do anyway). */
+
+/* TODO: Get all of the bkey routines to return -errno instead of
+   true/false. */
+/* TODO: The bkey routines currently assume tht address blocks are
+   allocated even if all contained addresses are NULL, but this is not
+   true.  Make sure tht we differentiate between NULL block and error,
+   and then fix up ext2_set_key_blkaddr() and anything else (including
+   the pack/unpack routines). */
+struct ext2_bkey {
+    int level;
+    u32 block;
+    struct inode *inode;
+    int off[4];
+    u32 *ptr[4];
+    struct buffer_head *ibh[4];
+};
+
+
+/*
+ *    Method to convert a block number into a key.
+ *
+ *    Returns 1 on success, 0 on failure.  You may safely, but need
+ *    not, free the key even if ext2_get_key() fails. 
+ */
+static int ext2_get_key(struct ext2_bkey *key, struct inode *inode,
+			u32 block)
+{
+    int x, level;
+    int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+
+    assert(atomic_read(&inode->i_mutex.count) <= 0);
+
+    /*
+     *  The first step can be viewed as translating the
+     *    original block number in a special base (powers
+     *    of addr_per_block).
+     */
+
+    key->block = block;
+
+    key->off[0] = key->off[1] = key->off[2] = key->off[3] = 0;
+    key->ibh[0] = key->ibh[1] = key->ibh[2] = key->ibh[3] = NULL;
+    key->ptr[0] = key->ptr[1] = key->ptr[2] = key->ptr[3] = NULL;
+
+    if (block >= EXT2_NDIR_BLOCKS) {
+	block -= EXT2_NDIR_BLOCKS;
+
+	if (block >= addr_per_block) {
+	    block -= addr_per_block;
+
+	    if (block >= addr_per_block * addr_per_block) {
+		block -= addr_per_block * addr_per_block;
+
+		key->off[0] = EXT2_TIND_BLOCK;
+		key->off[1] = (block / (addr_per_block * addr_per_block));
+		key->off[2] =
+		    (block % (addr_per_block * addr_per_block)) /
+		    addr_per_block;
+		key->off[3] = (block % addr_per_block);
+		level = 3;
+	    } else {
+		key->off[0] = EXT2_DIND_BLOCK;
+		key->off[1] = block / addr_per_block;
+		key->off[2] = block % addr_per_block;
+		level = 2;
+	    }
+	} else {
+	    key->off[0] = EXT2_IND_BLOCK;
+	    key->off[1] = block;
+	    level = 1;
+	}
+    } else {
+	key->off[0] = block;
+	level = 0;
+    }
+
+    /*
+     *  In the second step, we load the needed buffers.
+     */
+
+    key->level = level;
+    key->inode = inode;
+
+    key->ptr[0] = (u32 *) (&(EXT2_I(inode)->i_data));
+
+    for (x = 1; x <= level; x++) {
+	u32 *ptr;
+
+	ptr = key->ptr[x - 1];
+	if (ptr == NULL)
+	    break;
+/* Paul Whittaker tweak 19 Feb 2005 */
+	block = le32_to_cpu(ptr[key->off[x - 1]]);
+	if (block == 0)
+	    continue;		// TLL 05/01/07
+	if (x - 1 != 0)
+	    block = le32_to_cpu(block);
+	if ((key->ibh[x] = __bread(inode->i_sb->s_bdev,
+				   block, inode->i_sb->s_blocksize))
+	    == NULL)
+	    goto error;
+	key->ptr[x] = (u32 *) (key->ibh[x]->b_data);
+    }
+
+    return 1;
+  error:
+    for (; x != 0; x--)
+	if (key->ibh[x] != NULL)
+	    brelse(key->ibh[x]);
+    return 0;
+}
+
+
+/*
+ *    Find the block for a given key.  Return 0 if there
+ *      is no block for this key.
+ */
+static inline u32 ext2_get_key_blkaddr(struct ext2_bkey *key)
+{
+    assert(key->inode);
+    assert(atomic_read(&(key->inode)->i_mutex.count) <= 0);
+
+/* Paul Whittaker tweak 19 Feb 2005 */
+    if (key->ptr[key->level] == NULL)
+	return 0;
+    return le32_to_cpu(key->ptr[key->level][key->off[key->level]]);
+}
+
+
+/*
+ *    Change the block for a given key.  Return 0 on success,
+ *      -errno on failure.
+ */
+static inline int ext2_set_key_blkaddr(struct ext2_bkey *key, u32 blkaddr)
+{
+    char bdn[BDEVNAME_SIZE];
+    assert(key->inode);
+    assert(atomic_read(&(key->inode)->i_mutex.count) <= 0);
+
+    if (key->ptr[key->level] == NULL) {
+	/* The reason that this "can't happen" is that this
+	   routine is only used to shuffle block numbers or by
+	   free_cluster_blocks.  Cluster sizes are such that
+	   clusters can't straddle address blocks.  So the
+	   indirect block address can't be zero.  AFAIK, ptr
+	   can only be NULL on error or on null indirect block
+	   address.  Hmm, come to think of it, I think there
+	   are still some callers that don't check for errors
+	   from ext2_get_key(), so this still can happen until
+	   those are fixed up. */
+	printk(KERN_ERR
+	       "ext2_set_key_blkaddr: can't happen: NULL parent.  "
+	       "dev=%s, ino=%lu, level=%u.\n",
+	       bdevname(key->inode->i_sb->s_bdev, bdn),
+	       key->inode->i_ino, key->level);
+	return -ENOSYS;
+    }
+    /* Paul Whittaker tweak 19 Feb 2005 */
+    key->ptr[key->level][key->off[key->level]] = le32_to_cpu(blkaddr);
+    if (key->level > 0)
+	mark_buffer_dirty(key->ibh[key->level]);
+    return 0;
+}
+
+
+/*
+ *    Increment the key.  Returns 0 if we go beyond the limits,
+ *      1 otherwise.
+ *
+ *    Precondition: -key->off[level] <= incr < addr_per_block.
+ */
+static int ext2_next_key(struct ext2_bkey *key, int incr)
+{
+    int addr_per_block = EXT2_ADDR_PER_BLOCK(key->inode->i_sb);
+    int x, level = key->level;
+    u32 tmp;
+
+    assert(key->inode);
+    assert(atomic_read(&(key->inode)->i_mutex.count) <= 0);
+
+
+    /*
+     *  Increment the key. This is done in two step: first
+     *    adjust the off array, then reload buffers that should
+     *    be reloaded (we assume level > 0).
+     */
+
+    assert(key->off[level] >= -incr);
+    assert(incr < addr_per_block);
+    key->block += incr;
+    key->off[level] += incr;
+
+    /*
+     *  First step: should be thought as the propagation
+     *    of a carry.
+     */
+
+    if (level == 0) {
+	if (key->off[0] >= EXT2_NDIR_BLOCKS) {
+	    key->off[1] = key->off[0] - EXT2_NDIR_BLOCKS;
+	    key->off[0] = EXT2_IND_BLOCK;
+	    level = 1;
+	}
+	x = 0;
+    } else {
+	for (x = level; x > 0; x--) {
+	    if (key->off[x] >= addr_per_block) {
+		key->off[x] -= addr_per_block;
+		key->off[x - 1]++;
+
+		if (x == 1) {
+		    if (++level < 4) {
+			key->off[level] = key->off[level - 1];
+			key->off[level - 1] = 0;
+		    } else
+			return 0;
+		}
+	    } else
+		break;
+	}
+    }
+
+    /*
+     *  Second step: reload the buffers that have changed.
+     */
+
+    key->level = level;
+
+    while (x++ < level) {
+	if (key->ibh[x] != NULL) {
+	    if (IS_SYNC(key->inode) && buffer_dirty(key->ibh[x])) {
+		//mw:
+		assert(buffer_mapped(key->ibh[x])
+		       && (key->ibh[x]->b_bdev != NULL));
+		ll_rw_block(WRITE, 1, &(key->ibh[x]));
+		wait_on_buffer(key->ibh[x]);
+	    }
+	    brelse(key->ibh[x]);
+	}
+/* Paul Whittaker tweak 19 Feb 2005 */
+	if ((key->ptr[x - 1] != NULL)
+	    && ((tmp = le32_to_cpu(key->ptr[x - 1][key->off[x - 1]])) !=
+		0)) {
+	    if ((key->ibh[x] =
+		 __bread(key->inode->i_sb->s_bdev, tmp,
+			 key->inode->i_sb->s_blocksize))
+		!= NULL)
+		key->ptr[x] = (u32 *) (key->ibh[x]->b_data);
+	    else
+		key->ptr[x] = NULL;
+	} else {
+	    key->ibh[x] = NULL;
+	    key->ptr[x] = NULL;
+	}
+    }
+
+    return 1;
+}
+
+
+/* Method to free the key: just release buffers.
+
+   Returns 0 on success, -errno on error.
+*/
+
+static int ext2_free_key(struct ext2_bkey *key)
+{
+    int x, n;
+    struct buffer_head *bh[4];
+
+    assert(key->inode);
+    assert(atomic_read(&(key->inode)->i_mutex.count) <= 0);
+
+
+    for (x = 0, n = 0; x <= key->level; x++) {
+	if (key->ibh[x] != NULL) {
+	    if (IS_SYNC(key->inode) && buffer_dirty(key->ibh[x]))
+		bh[n++] = key->ibh[x];
+	    else
+		brelse(key->ibh[x]);
+	}
+    }
+
+    if (n > 0) {
+	int ncopy = n;
+	while (ncopy-- > 0) {
+	    assert(buffer_mapped(bh[ncopy])
+		   && (bh[ncopy]->b_bdev != NULL));
+	}
+
+	ll_rw_block(WRITE, n, bh);
+
+	while (n-- > 0) {
+	    wait_on_buffer(bh[n]);
+	    /* TODO: Check for error. */
+	    brelse(bh[n]);
+	}
+    }
+    return 0;
+}
+
+
+/* Returns positive if specified cluster is compressed,
+   zero if not,
+   -errno if an error occurred.
+
+   If you need the result to be accurate, then down i_sem before
+   calling this, and don't raise i_sem until after you've used the
+   result. */
+int ext2_cluster_is_compressed_fn(struct inode *inode, unsigned cluster)
+{
+    unsigned block = (ext2_cluster_block0(inode, cluster)
+		      + ext2_cluster_nblocks(inode, cluster)
+		      - 1);
+    struct ext2_bkey key;
+    int result;
+
+    assert(atomic_read(&inode->i_mutex.count) <= 0);
+
+    /* impl: Not all callers of ext2_cluster_is_compressed_fn() have
+       i_sem down.  Of course it is impossible to guarantee
+       up-to-date information for such callers (someone may
+       compress or decompress between when we check and when they
+       use the information), so hopefully it won't matter if the
+       information we return is slightly inaccurate (e.g. because
+       someone is de/compressing the cluster while we check). */
+    if (!ext2_get_key(&key, inode, block))
+	return -EIO;
+
+    result = (ext2_get_key_blkaddr(&key) == EXT2_COMPRESSED_BLKADDR);
+    ext2_free_key(&key);
+    return result;
+}
+
+
+/* Support for the GETCOMPRRATIO ioctl() call.  We calculate how many
+   blocks the file would hold if it weren't compressed.  This requires
+   reading the cluster head for every compressed cluster.
+
+   Returns either -EAGAIN or the number of blocks that the file would
+   take up if uncompressed.  */
+int ext2_count_blocks(struct inode *inode)
+{
+    struct buffer_head *head_bh;
+    int count;
+    int cluster;
+    struct ext2_bkey key;
+    u32 end_blknr;
+
+    if (!(EXT2_I(inode)->i_flags & EXT2_COMPRBLK_FL))
+	return inode->i_blocks;
+
+    mutex_lock(&inode->i_mutex);
+    end_blknr = ROUNDUP_RSHIFT(inode->i_size,
+			       inode->i_sb->s_blocksize_bits);
+
+    /* inode->i_blocks is stored in units of 512-byte blocks.  It's
+       more convenient for us to work in units of s_blocksize. */
+    {
+	u32 shift = inode->i_sb->s_blocksize_bits - 9;
+
+	count = inode->i_blocks;
+	if (count & ((1 << shift) - 1))
+	    ext2_warning(inode->i_sb,
+			 "ext2_count_blocks",
+			 "i_blocks not multiple of blocksize");
+	count >>= shift;
+    }
+
+    cluster = 0;
+    if (!ext2_get_key(&key, inode, 0)) {
+	count = -EIO;
+	goto out;
+    }
+    while (key.block < end_blknr) {
+	u32 head_blkaddr = ext2_get_key_blkaddr(&key);
+
+	/* bug fix: init head_bh for each iteration TLL 2/21/07 */
+	head_bh = NULL;
+	if (head_blkaddr == EXT2_COMPRESSED_BLKADDR) {
+	    count = -EXT2_ECOMPR;
+	    break;
+	}
+	if (!ext2_next_key(&key, ext2_cluster_nblocks(inode, cluster) - 1))
+	    break;
+	if (ext2_get_key_blkaddr(&key) == EXT2_COMPRESSED_BLKADDR) {
+	    struct ext2_cluster_head *head;
+
+	    if (head_blkaddr == 0) {
+		count = -EXT2_ECOMPR;
+		break;
+	    }
+	    head_bh = __getblk(inode->i_sb->s_bdev,
+			       head_blkaddr, inode->i_sb->s_blocksize);
+	    if (head_bh == NULL) {
+		/* Hmm, EAGAIN or EIO? */
+		count = -EAGAIN;
+		break;
+	    }
+	    if (!buffer_uptodate(head_bh))
+		ll_rw_block(READ, 1, &head_bh);
+
+	    wait_on_buffer(head_bh);
+
+#ifdef CONFIG_HIGHMEM
+	    if (!page_address(head_bh->b_page)) {
+		BUG();
+	    }
+#endif
+
+	    head = (struct ext2_cluster_head *) head_bh->b_data;
+	    /* remove clen > ulen test TLL 2/21/07 */
+	    if ((head->magic != cpu_to_le16(EXT2_COMPRESS_MAGIC_04X))
+		|| (le32_to_cpu(head->ulen) > EXT2_MAX_CLUSTER_BYTES)
+		|| (head->holemap_nbytes > 4)) {
+		count = -EXT2_ECOMPR;
+		break;
+	    }
+	    assert(sizeof(struct ext2_cluster_head) == 16);
+	    count += (ROUNDUP_RSHIFT(le32_to_cpu(head->ulen),
+				     inode->i_sb->s_blocksize_bits)
+		      - ROUNDUP_RSHIFT((le32_to_cpu(head->clen)
+					+ sizeof(struct ext2_cluster_head)
+					+ head->holemap_nbytes),
+				       inode->i_sb->s_blocksize_bits));
+	    brelse(head_bh);
+	    head_bh = NULL;
+	}
+
+	if (!ext2_next_key(&key, 1))
+	    break;
+	cluster++;
+    }
+    ext2_free_key(&key);
+    if (head_bh != NULL)
+	brelse(head_bh);
+  out:
+    mutex_unlock(&inode->i_mutex);
+    if (count == -EXT2_ECOMPR) {
+	ext2_warning(inode->i_sb,
+		     "ext2_count_blocks",
+		     "invalid compressed cluster %u of inode %lu",
+		     cluster, inode->i_ino);
+	EXT2_I(inode)->i_flags |= EXT2_ECOMPR_FL;
+    }
+
+    /* The count should be in units of 512 (i.e. 1 << 9) bytes. */
+    if (count >= 0)
+	count <<= inode->i_sb->s_blocksize_bits - 9;
+    return count;
+}
+
+
+/* Decompress some blocks previously obtained from a cluster.
+   Decompressed data is stored in ext2_rd_wa.u.  Buffer heads in the bh
+   array are packed together at the begining of the array.  The ulen
+   argument is an indication of how many bytes the caller wants to
+   obtain, excluding holes.  (This can be less than head->ulen, as in the
+   case of readpage.)  No hole processing is done; we don't even look at
+   head->holemap.
+
+   Note the semantic difference between this and
+   (): the latter decompresses a cluster _and
+   stores it as such_, whereas ext2_decompress_blocks() just
+   decompresses the contents of the blocks into ext2_rd_wa.u.
+
+   The working area is supposed to be available and locked.
+ 
+   Returns a negative value on failure, the number of bytes
+   decompressed otherwise.
+ 
+   Called by :
+ 
+         ext2_decompress_cluster ()    [sem down]
+         ext2_readpage () [sem down, but only ifndef EXT2_LOCK_BUFFERS] */
+
+/* TODO: ext2_decompress_blocks() scribbles in ext2_rd_wa.c.
+   Check callers to make sure this isn't a problem. */
+size_t
+ext2_decompress_blocks(struct inode * inode,
+		       struct buffer_head ** bh,
+		       int nblk, size_t ulen, u32 cluster)
+{
+    struct ext2_cluster_head *head;
+    int count, src_ix, x;
+    unsigned char *dst;
+    unsigned meth, alg;
+    char bdn[BDEVNAME_SIZE];
+
+    assert(atomic_read(&inode->i_mutex.count) <= 0);	/* i.e. mutex_lock */
+    assert(ext2_rd_wa_lock != 0);
+
+    /*
+       We pack the buffer together before (and must take care
+       not to duplicate the buffer heads in the array).
+
+       pjm 1998-01-09: Starting from e2compr-0.4.0, they should
+       already be packed together in the blkaddr array.  TODO:
+       Insert appropriate assert() statements checking tht this is
+       the case.  TODO: Check that callers have bh[] packed. */
+#ifdef  EXT2_COMPR_REPORT
+    trace_e2c("ext2_decompress_blocks: nblk=%d\n", nblk);
+#endif
+    for (src_ix = 0, x = 0; src_ix < nblk; src_ix++) {
+	if (bh[src_ix] == NULL)
+	    printk("no_bheader()\n");
+	if ((bh[src_ix] != NULL) && (bh[src_ix]->b_blocknr != 0)) {
+
+	    if (x < src_ix) {
+		ext2_warning(inode->i_sb, "bad buffer table",
+			     "inode = %lu", inode->i_ino);
+		goto error;
+	    }
+	    x++;
+	}
+    }
+
+    nblk = x;
+#ifdef  EXT2_COMPR_REPORT_CPR
+    trace_e2c("ext2_decompress_blocks (2): nblk=%d\n", nblk);
+#endif
+    if (nblk == 0) {
+	ext2_warning(inode->i_sb, "no block in cluster", "inode = %lu",
+		     inode->i_ino);
+	goto error;
+    }
+
+    restore_b_data_himem(bh[0]);
+    head = (struct ext2_cluster_head *) (bh[0]->b_data);
+
+    /*
+     *  Do some consistency checks.
+     */
+
+    if (head->magic != cpu_to_le16(EXT2_COMPRESS_MAGIC_04X)) {
+	ext2_warning(inode->i_sb,
+		     "bad magic number",
+		     "inode = %lu, magic = %#04x",
+		     inode->i_ino, le16_to_cpu(head->magic));
+	goto error;
+    }
+#if EXT2_GRAIN_SIZE & (EXT2_GRAIN_SIZE - 1)
+# error "This code assumes EXT2_GRAIN_SIZE to be a power of two."
+#endif
+    /* The macro also assumes that _a > 0, _b > 0. */
+#define ROUNDUP_GE(_a, _b, _d) (   (  ((_a) - 1) \
+				    | ((_d) - 1)) \
+				>= (  ((_b) - 1) \
+				    | ((_d) - 1)))
+
+    //mw: following 3 just for debugging!!!
+    assert(!((le32_to_cpu(head->ulen) > EXT2_MAX_CLUSTER_BYTES)));
+    assert(!((head->clen == 0)));
+    assert(!(ROUNDUP_GE(le32_to_cpu(head->clen)
+			+ head->holemap_nbytes + sizeof(struct ext2_cluster_head), le32_to_cpu(head->ulen), EXT2_GRAIN_SIZE)));	//fails!!!
+
+    if ((le32_to_cpu(head->ulen) > EXT2_MAX_CLUSTER_BYTES)
+	|| (head->clen == 0)
+	|| ROUNDUP_GE(le32_to_cpu(head->clen)
+		      + head->holemap_nbytes
+		      + sizeof(struct ext2_cluster_head),
+		      le32_to_cpu(head->ulen), EXT2_GRAIN_SIZE)) {
+	ext2_warning(inode->i_sb,
+		     "invalid cluster len",
+		     "inode = %lu, len = %u:%u",
+		     inode->i_ino,
+		     le32_to_cpu(head->clen), le32_to_cpu(head->ulen));
+	goto error;
+    }
+#undef ROUNDUP_GE
+
+    /* TODO: Test for `nblk != 1 + ...' instead of the current
+       one-sided test.  However, first look at callers, and make
+       sure that they handle the situation properly (e.g. freeing
+       unneeded blocks) and tht they always pass a correct
+       value for nblk. */
+    if (nblk <= ((le32_to_cpu(head->clen)
+		  + head->holemap_nbytes + sizeof(struct ext2_cluster_head)
+		  - 1)
+		 / bh[0]->b_size)) {
+	int i;
+	ext2_warning(inode->i_sb,
+		     "missing blocks",
+		     "inode = %lu, blocks = %d/%u",
+		     inode->i_ino, nblk, ((le32_to_cpu(head->clen)
+					   + head->holemap_nbytes
+					   +
+					   sizeof(struct ext2_cluster_head)
+					   - 1)
+					  / bh[0]->b_size) + 1);
+	printk("i_size=%d\n", (int) inode->i_size);
+	for (i = 0; i < 12; i++)
+	    printk("i_data[%d]=%d\n", i, EXT2_I(inode)->i_data[i]);
+	printk
+	    ("cluster_head (sizeof head=%d):\n\tmagic=0x%4x\n\tmethod=%d\n\tholemap_nbytes=%d\n\tulen=%d\n\tclen=%d\n\tbh->b_size=%d\n",
+	     sizeof(struct ext2_cluster_head), head->magic,
+	     (int) head->method, (int) head->holemap_nbytes, head->ulen,
+	     head->clen, bh[0]->b_size);
+	goto error;
+    }
+
+    /* We must invalidate the cache before scribbling over ext2_rd_wa.u. */
+    memset(&ext2_rd_wa_ucontents, 0xff, sizeof(struct ext2_wa_contents_S));
+
+    /* I moved it here in case we need to load a module that
+     * needs more heap that is currently allocated.
+     * In such case "init_module" for that algorithm forces
+     * re-allocation of ext2_wa. It should be safe here b/c the
+     * first reference to ext2_wa comes just after and we have
+     * locked ext2_wa before.
+     *
+     * FIXME: Totally separate working areas for reading and writing.
+     *      Jan Rï¿½ï¿½ï¿½ï¿½ï¿½ï¿½korajski
+     */
+    meth = head->method;	/* only a byte, so no swabbing needed. */
+    if (meth >= EXT2_N_METHODS) {
+	ext2_warning(inode->i_sb,
+		     "Ass: illegal method id",
+		     "inode = %lu, id = %u", inode->i_ino, meth);
+	dump_stack();
+	goto error;
+    }
+    alg = ext2_method_table[meth].alg;
+    /* jmr 1998-10-28 This is a Bad Idea to put it here
+       if (!ext2_algorithm_table[alg].avail) {
+       sprintf(str, "ext2-compr-%s", ext2_algorithm_table[alg].name);
+       request_module(str);
+       }
+     */
+
+    /*
+     *  Adjust the length if too many bytes are requested.
+     *
+     *    TODO: Traiter les bitmaps ici, et non plus au niveau de    
+     *          l'appelant. Faire un petit cache en memorisant le    
+     *          numero du dernier noeud decompresse et du dernier    
+     *          cluster. Le pb, c'est qu'on ne peut pas savoir si    
+     *          les blocs ont ete liberes et realloue entre temps    
+     *          -> il faut etre prevenu pour invalider le buffer.    
+     *
+     *          pjm fixme tr: Take care of the bitmaps here,
+     *          instead of by the caller as we currently do.  Keep
+     *          a small cache that holds the number of the
+     *          previous <inode, cluster> to have been
+     *          decompressed.  The problem is that we have no way
+     *          of knowing whether the blocks have been freed and
+     *          reallocated in the meantime / since last time ->
+     *          we must be informed so that we can invalidate the
+     *          buffer.  */
+    if (ulen > le32_to_cpu(head->ulen)) {
+	memset(ext2_rd_wa->u + le32_to_cpu(head->ulen),
+	       0, ulen - le32_to_cpu(head->ulen));
+	ulen = le32_to_cpu(head->ulen);
+
+	assert((bh[0]->b_size & (bh[nblk - 1]->b_size - 1)) == 0);
+	if (((le32_to_cpu(head->clen)
+	      + head->holemap_nbytes + sizeof(struct ext2_cluster_head)
+	      - 1)
+	     | (bh[0]->b_size - 1))
+	    >= ((ulen - 1) | (bh[0]->b_size - 1))) {
+	    printk(KERN_WARNING
+		   "ext2_decompress_blocks: "
+		   "ulen (=%u) or clen (=%u) wrong "
+		   "in dev %s, inode %lu.\n",
+		   ulen, le32_to_cpu(head->clen),
+		   bdevname(inode->i_sb->s_bdev, bdn), inode->i_ino);
+	    goto error;
+	}
+    }
+
+    /*
+     *  Now, decompress data.
+     */
+    /* TODO: Is this (ulen == 0) possible? */
+    if (ulen == 0)
+	return 0;
+
+    assert(ext2_rd_wa_lock != 0);
+    for (x = 0, dst = ext2_rd_wa->c; x < nblk; dst += bh[x++]->b_size) {
+	restore_b_data_himem(bh[x]);
+	memcpy(dst, bh[x]->b_data, bh[x]->b_size);
+    }
+
+    //printk("De: Compressed:    %i-%lu-%lu\n", cluster, ext2_adler32 (12345, ext2_wr_wa->c, head->clen), head->clen);
+    /* TODO: Check checksum. */
+
+    if (!ext2_algorithm_table[alg].avail) {
+	ext2_warning(inode->i_sb,
+		     "ext2_decompress_blocks",
+		     "algorithm `%s' not available for inode %lu",
+		     ext2_algorithm_table[alg].name, inode->i_ino);
+	ext2_mark_algorithm_use(inode, alg);
+	goto error;
+    }
+
+    /* */
+    {
+	struct ext2_cluster_head *wa1head
+	    = (struct ext2_cluster_head *) ext2_rd_wa->c;
+	unsigned clen = le32_to_cpu(wa1head->clen);
+
+	/*mw: might not be necessary: */
+	/*unsigned long checksum;	//mw
+	checksum =
+	    cpu_to_le32(ext2_adler32
+			(le32_to_cpu(*(u32 *) ext2_rd_wa->c),
+			 ext2_rd_wa->c + 8,
+			 (sizeof(struct ext2_cluster_head) - 8 +
+			  head->holemap_nbytes + clen)));*/
+	//printk("new checksum: %lu \n", c2);
+	//assert(checksum == head->checksum);
+	//printk("UCS %i-%lu\n", cluster,checksum);
+	/*not necessaray end */
+
+	if (wa1head->checksum !=
+	    cpu_to_le32(ext2_adler32
+			(le32_to_cpu(*(u32 *) ext2_rd_wa->c),
+			 ext2_rd_wa->c + 8,
+			 (sizeof(struct ext2_cluster_head) - 8 +
+			  head->holemap_nbytes + clen)))) {
+	    ext2_warning(inode->i_sb, "ext2_decompress_blocks",
+			 "corrupted compressed data in inode %lu",
+			 inode->i_ino);
+	    goto error;
+	}
+    }
+
+    count =
+	ext2_algorithm_table[alg].decompress(ext2_rd_wa->c +
+					     sizeof(struct
+						    ext2_cluster_head) +
+					     head->holemap_nbytes,
+					     ext2_rd_wa->u,
+					     ext2_rd_wa->heap,
+					     le32_to_cpu(head->clen), ulen,
+					     ext2_method_table[meth].xarg);
+
+    //printk("De: UnCompressed:  %i-%lu-%lu\n", cluster, ext2_adler32 (12345, ext2_wr_wa->u, ulen), ulen);
+
+
+    /* If we got fewer than ulen bytes, there is a problem, since
+       we corrected the ulen value before decompressing.  Note
+       that it's OK for count to exceed ulen, because ulen can be
+       less than head->ulen. */
+    if ((count < ulen) || (count > le32_to_cpu(head->ulen))) {
+	ext2_warning(inode->i_sb,
+		     "corrupted compressed data",
+		     "inode = %lu, count = %u of %u (%u/%u)",
+		     inode->i_ino, count, ulen,
+		     le32_to_cpu(head->clen), le32_to_cpu(head->ulen));
+	goto error;
+    }
+    ext2_ensure_algorithm_use(inode, alg);
+    return count;
+
+  error:
+
+    /* Raise the ECOMPR flag for this file.  What this means is
+       that the file cannot be written to, and can only be read if
+       the user raises the NOCOMPR flag.
+
+       pjm 1997-01-16: I've changed it so that files with ECOMPR
+       still have read permission, so user can still read the rest
+       of the file but get an I/O error (errno = EXT2_ECOMPR) when
+       they try to access anything from this cluster. */
+
+    EXT2_I(inode)->i_flags |= EXT2_ECOMPR_FL;
+
+    inode->i_ctime = CURRENT_TIME;
+    mark_inode_dirty_sync(inode);
+    /* pjm 1998-02-21: We used to do `memset(ext2_rd_wa.u, 0, ulen)'
+       here because once upon a time the user could sometimes see
+       buf contents.  I believe that this can never happen any
+       more. */
+    return -EXT2_ECOMPR;
+}
+
+
+/* ext2_calc_free_ix: Calculates the position of the C_NBLK'th non-hole
+   block; equals C_NBLK plus the number of holes in the first CALC_FREE_IX()
+   block positions of the cluster.
+
+   pre: 1 =< c_nblk < EXT2_MAX_CLUSTER_BLOCKS,
+        Number of 1 bits in ,ubitmap` > ,c_nblk`.
+   post: c_nblk =< calc_free_ix() < EXT2_MAX_CLUSTER_BLOCKS
+
+   Called by:
+       ext2_decompress_cluster()
+       ext2_file_write()
+
+   TODO: Have ext2_compress_cluster() call this.
+   */
+unsigned ext2_calc_free_ix(unsigned holemap_nbytes, u8 const *holemap,
+			   unsigned c_nblk)
+{
+    unsigned i;
+
+    assert(1 <= c_nblk);
+    assert(c_nblk < EXT2_MAX_CLUSTER_BLOCKS);
+    for (i = 0; (i < holemap_nbytes * 8) && (c_nblk > 0);) {
+	assert(i < EXT2_MAX_CLUSTER_BLOCKS - 1);
+	if ((holemap[i >> 3] & (1 << (i & 7))) == 0)
+	    c_nblk--;
+	i++;
+    }
+    i += c_nblk;
+    assert(i < EXT2_MAX_CLUSTER_BLOCKS);
+    return i;
+}
+
+
+/*  (): Prepare the blkaddr[] array for
+   decompression by moving non-hole blocks to their proper positions
+   (according to ubitmap) and zeroing any other blocks.
+
+   Returns 0 on success, -errno on error.
+
+   Note: We assume tht blkaddr[i] won't change under us forall
+   clu_block0 =< i < clu_block0 + clu_nblocks.  Holding i_sem should
+   guarantee this.
+
+   Called by:
+       ext2_decompress_cluster()
+       ext2_file_write() */
+int
+ext2_unpack_blkaddrs(struct inode *inode,
+		     struct buffer_head *bh[],
+		     int mmcp,
+		     unsigned holemap_nbytes,
+		     u8 const *holemap,
+		     unsigned c_nblk,
+		     unsigned free_ix,
+		     unsigned clu_block0, unsigned clu_nblocks)
+{
+    struct ext2_bkey key;
+    u32 *blkaddr;
+    unsigned si, di;
+
+    assert(clu_nblocks <= EXT2_MAX_CLUSTER_BLOCKS);
+    assert(1 <= c_nblk);
+    assert(c_nblk <= free_ix);
+    assert(free_ix < EXT2_MAX_CLUSTER_BLOCKS);
+    if (!ext2_get_key(&key, inode, clu_block0))
+	return -EIO;
+
+    if (key.ptr[key.level] == NULL) {
+	/* TODO: Call ext2_error(). */
+	ext2_free_key(&key);
+	return -EIO;
+    }
+
+    /* impl: Note tht we're relying on clusters not straddling
+       address block boundaries. */
+    blkaddr = &key.ptr[key.level][key.off[key.level]];
+    memset(blkaddr + free_ix,
+	   0, sizeof(*blkaddr) * (clu_nblocks - free_ix));
+    si = c_nblk;
+    for (di = free_ix; di > si;) {
+	--di;
+	if (((di >> 3) < holemap_nbytes)
+	    && (holemap[di >> 3] & (1 << (di & 7)))) {
+	    blkaddr[di] = 0;
+	    bh[di]->b_blocknr = 0;
+	    clear_bit(BH_Mapped, &bh[di]->b_state);
+	} else {
+	    if (si == 0) {
+		break;
+	    }
+	    blkaddr[di] = blkaddr[--si];
+	    assert(bh[di]->b_blocknr == 0);
+	    assert(bh[si]->b_blocknr != 0);
+	    assert(buffer_mapped(bh[si]));
+#ifdef  EXT2_COMPR_REPORT_CPR
+	    trace_e2c("unpack: di=%d sts=0x%x si=%d blk=%ld sts=0x%x\n",
+		      di, (int) bh[di]->b_state, si, bh[si]->b_blocknr,
+		      (int) bh[si]->b_state);
+#endif
+	    bh[di]->b_blocknr = bh[si]->b_blocknr;
+	    set_bit(BH_Mapped, &bh[di]->b_state);
+	    bh[si]->b_blocknr = 0;
+	    clear_bit(BH_Mapped, &bh[si]->b_state);
+	    set_bit(BH_Uptodate, &bh[di]->b_state);
+	    if (mmcp) {
+		restore_b_data_himem(bh[si]);
+		restore_b_data_himem(bh[di]);
+		memcpy(bh[di]->b_data, bh[si]->b_data,
+		       inode->i_sb->s_blocksize);
+	    }
+	}
+    }
+    if (key.level > 0)
+	mark_buffer_dirty(key.ibh[key.level]);
+    return ext2_free_key(&key);
+}
+
+
+/*
+ *    Decompress one cluster.  If already compressed, the cluster
+ *      is decompressed in place, and the compress bitmap is updated.
+ *
+ *      Returns the size of decompressed data on success, a negative
+ *      value in case of failure, or 0 if the cluster was not compressed.
+ *
+ *      The inode is supposed to be writable.
+ *
+ *      Called by :
+ *
+ *        ext2_decompress_inode()      [sem down]
+ *        ext2_file_write()            [sem down]
+ *        trunc_bitmap()               [sem down]
+ */
+int ext2_decompress_cluster(struct inode *inode, u32 cluster)
+{
+    struct buffer_head *bh[EXT2_MAX_CLUSTER_BLOCKS];
+    struct buffer_head *bhc[EXT2_MAX_CLUSTER_BLOCKS];
+    struct page *pg[EXT2_MAX_CLUSTER_PAGES], *epg[EXT2_MAX_CLUSTER_PAGES];
+    int result, nbh;
+    unsigned npg, c_nblk;
+    struct ext2_cluster_head *head;
+    int i = 0;
+    unsigned free_ix, clu_block0, clu_nblocks;
+    int d_npg = -1;		/* number of decompressed page  */
+    unsigned long allpagesuptodate = 1;
+    struct buffer_head *bh_writeout[EXT2_MAX_CLUSTER_BLOCKS];
+    int bhn_writeout;
+#ifdef CONFIG_HIGHMEM
+    int kmapped = 0;
+#endif
+
+    for (i = 0; i < EXT2_MAX_CLUSTER_BLOCKS; i++) {
+	bh_writeout[i] = NULL;
+	bhn_writeout = 0;
+    }
+
+    assert(atomic_read(&inode->i_mutex.count) <= 0);	/* i.e. mutex_lock */
+    if (ext2_rd_wa == NULL)
+	return -EBUSY;
+
+    for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++)
+	epg[i] = NULL;
+
+    /*
+       Get blocks from cluster.
+       Assign to variables head, ubitmap, clu_block0, clu_nblocks.
+       Shuffle blkaddr[] array and write zero to holes.
+       Allocate new blocks.
+       Get the working area.
+       Decompress.
+       Copy to bh[]->b_data (marking buffers uptodate and dirty).
+       Release working area.
+       Release bh[].
+     */
+
+    nbh = 0;
+    npg = ext2_cluster_npages(inode, cluster);
+    result = ext2_get_cluster_pages(inode, cluster, pg, NULL, 0);
+    if (result <= 0) {
+	for (i = 0; i < npg; i++)
+	    epg[i] = NULL;
+	goto out_err;
+    }
+
+    for (i = 0; i < npg; i++) {
+	if ((pg[i]->index <= ((inode->i_size - 1) >> PAGE_CACHE_SHIFT)) &&
+	    !PageUptodate(pg[i])) {
+	    allpagesuptodate = 0;
+	}
+    }
+    if (allpagesuptodate) {
+	//printk("DecompressPages: Ino:%lu\n", inode->i_ino);
+	result = ext2_decompress_pages(inode, cluster, pg);
+	if (result != 0) {
+	    for (i = 0; i < npg; i++)
+		epg[i] = NULL;
+	    if (result > 0)
+		goto cleanup;
+	    else
+		goto out_err;
+	}
+	/*mw: if we continue here then in ext2_decompress_pages
+	 * not all pages were up-to-date 
+	 */
+    }
+    //printk("DecompressCluster: Ino:%lu\n", inode->i_ino);
+    result = ext2_get_cluster_extra_pages(inode, cluster, pg, epg);
+    if (result <= 0) {
+	goto out_err;
+    }
+#ifdef CONFIG_HIGHMEM
+    ext2_kmap_cluster_pages(NULL, pg, epg);
+    kmapped = 1;
+#endif
+
+    result = ext2_get_cluster_blocks(inode, cluster, bh, pg, epg, 0);
+    if (result <= 0) {
+	goto out_err;
+    }
+    nbh = c_nblk = result;
+
+
+#ifdef EXT2_COMPR_REPORT
+    {
+	int j;
+	printk
+	    (" > > > ext2_decompress_cluster %d: inode=%ld, size=%d nbh=%d\n",
+	     cluster, inode->i_ino, (int) inode->i_size, nbh);
+#ifdef EXT2_COMPR_REPORT_VERBOSE
+	for (j = 0; j < nbh; j++) {
+	    if (bh[j]) {
+		printk("0buffer_head[%d]: blocknr=%lu,  addr=%p \n", j,
+		       (unsigned long) bh[j]->b_blocknr, bh[j]);
+		if (bh[j]->b_page)
+		    printk("0:[page->index=%ld]\n", bh[j]->b_page->index);
+		else
+		    printk("[No page]\n");
+	    } else
+		printk("buffer_head[%d] is NULL\n", j);
+	}
+	while ((j < EXT2_MAX_CLUSTER_BLOCKS) && (bh[j] != NULL) && bh[j]->b_blocknr) {	/*Add by Yabo Ding */
+	    printk
+		("buffer_head[%d] is free but not NULL: blocknr=%lu, addr=%p\n",
+		 j, (unsigned long) bh[j]->b_blocknr, bh[j]);
+	    j++;
+	}
+#endif
+    }
+#endif
+    for (i = 0; i < nbh; i++)
+	assert(bh[i]->b_blocknr != 0);
+
+    restore_b_data_himem(bh[0]);
+
+    head = (struct ext2_cluster_head *) bh[0]->b_data;
+    if (head->magic != cpu_to_le16(EXT2_COMPRESS_MAGIC_04X)) {
+	ext2_warning(inode->i_sb,
+		     "ext2_decompress_cluster: bad magic number",
+		     "cluster %d: inode = %lu, magic = %#04x",
+		     cluster, inode->i_ino, le16_to_cpu(head->magic));
+	EXT2_I(inode)->i_flags |= EXT2_ECOMPR_FL;
+	result = -EXT2_ECOMPR;
+	goto out_err;
+    }
+    if (le32_to_cpu(head->ulen) -
+	(c_nblk << inode->i_sb->s_blocksize_bits) <= 0) {
+	ext2_error(inode->i_sb, "ext2_decompress_cluster",
+		   "ulen too small for c_nblk.  ulen=%u, c_nblk=%u, bs=%lu",
+		   le32_to_cpu(head->ulen), c_nblk,
+		   inode->i_sb->s_blocksize);
+	EXT2_I(inode)->i_flags |= EXT2_ECOMPR_FL;
+	result = -EXT2_ECOMPR;
+	goto out_err;
+    }
+    free_ix =
+	ext2_calc_free_ix(head->holemap_nbytes, (u8 const *) (&head[1]),
+			  c_nblk);
+    clu_block0 = ext2_cluster_block0(inode, cluster);
+    clu_nblocks = ext2_cluster_nblocks(inode, cluster);
+    ext2_unpack_blkaddrs(inode, bh, 1,
+			 head->holemap_nbytes, (u8 const *) (&head[1]),
+			 c_nblk, free_ix, clu_block0, clu_nblocks);
+
+    /* Allocate the extra blocks needed. */
+    {
+	int data_left = le32_to_cpu(head->ulen);
+
+	data_left -= c_nblk << inode->i_sb->s_blocksize_bits;
+	assert(data_left > 0);
+	for (i = free_ix; i < clu_nblocks; i++)
+	    if (((i >> 3) >= head->holemap_nbytes)
+		|| !(head->holemap[i >> 3] & (1 << (i & 7)))) {
+		result = ext2_get_block(inode,
+					clu_block0 + i,
+					bh[i], 1 /* create */ );
+		if (bh[i]->b_blocknr == 0)
+		    goto out_err;
+		d_npg =
+		    (i >>
+		     (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits)) +
+		    1;
+		nbh++;
+		data_left -= inode->i_sb->s_blocksize;
+		if (data_left <= 0)
+		    break;
+	    }
+    }
+
+    /* jmr 1998-10-28 Hope this is the last time I'm moving this code.
+     * Module loading must be done _before_ we lock wa, just think what
+     * can happen if we reallocate wa when somebody else uses it...
+     */
+    {
+	unsigned meth, alg;
+
+	meth = head->method;	/* only a byte, so no swabbing needed. */
+	if (meth >= EXT2_N_METHODS) {
+	    ext2_warning(inode->i_sb,
+			 "Ass.: illegal method id",
+			 "inode = %lu, id = %u", inode->i_ino, meth);
+	    result = -EXT2_ECOMPR;
+	    goto out_err;
+	}
+#ifdef CONFIG_KMOD
+	alg = ext2_method_table[meth].alg;
+	if (!ext2_algorithm_table[alg].avail) {
+	    char str[32];
+
+	    sprintf(str, "ext2-compr-%s", ext2_algorithm_table[alg].name);
+	    request_module(str);
+	}
+#endif
+    }
+
+    result = -EINTR;
+    if (ext2_lock_rd_wa() == 0)
+	goto out_err;
+
+#ifdef EXT2_COMPR_REPORT_WA
+    printk(KERN_DEBUG "pid %d locks wa\n", current->pid);
+#endif
+
+    /*
+     *  Then, decompress and copy back data.
+     */
+
+    {
+	int ic;
+
+	for (ic = 0, i = 0; i < clu_nblocks; i++) {
+	    if (bh[i]->b_blocknr != 0) {
+		bhc[ic] = bh[i];
+		ic++;
+		if (ic == c_nblk) {
+		    break;
+		}
+	    }
+	}
+    }
+    result = ext2_decompress_blocks(inode, bhc, c_nblk,
+				    le32_to_cpu(head->ulen), cluster);
+    if (result != (int) le32_to_cpu(head->ulen)) {
+	if (result >= 0) {
+	    /* I think this is impossible, as
+	       ext2_decompress_blocks() checks against
+	       head->ulen. */
+	    printk(KERN_WARNING "Unexpected return value %d "
+		   "from ext2_decompress_blocks()\n", result);
+	    result = -EXT2_ECOMPR;
+	}
+
+	ext2_unlock_rd_wa();
+#ifdef EXT2_COMPR_REPORT_WA
+	printk(KERN_DEBUG "pid %d unlocks wa\n", current->pid);
+#endif
+	goto out_err;
+    }
+    assert(ext2_rd_wa_ucontents.ino == ~0ul);
+    ext2_rd_wa_ucontents.ino = inode->i_ino;
+    ext2_rd_wa_ucontents.dev = inode->i_rdev;
+    ext2_rd_wa_ucontents.cluster = cluster;
+
+#ifdef EXT2_COMPR_REPORT
+    printk(KERN_DEBUG "ext2: %04x:%lu: cluster %d+%d [%d] "
+	   "decompressed into %d bytes\n",
+	   inode->i_rdev,
+	   inode->i_ino, clu_block0, clu_nblocks, c_nblk, result);
+#endif
+
+    /* Copy back decompressed data. */
+    {
+	int count = result;
+	unsigned char const *src;
+	int c, p;
+	int cbh;
+	int n;			/* block index in page  */
+	struct buffer_head *bp;
+	unsigned addr0, b_start, b_end;
+
+	assert(count > 0);
+	if (d_npg == -1) {
+	    d_npg = ((count - 1) >> PAGE_CACHE_SHIFT) + 1;
+	}
+#ifdef  EXT2_COMPR_REPORT_CPR
+	trace_e2c
+	    ("ext2_decompress_cluster: cnt=%d free_ix=%d d_npg=%d nbh=%d\n",
+	     count, free_ix, d_npg, nbh);
+#endif
+	result = -EXT2_ECOMPR;
+	src = ext2_rd_wa->u;
+	cbh = 0;
+	for (c = 0; c < clu_nblocks; c++) {
+
+	    if (bh[c]->b_blocknr == 0) {
+#ifdef  EXT2_COMPR_REPORT_CPR
+		trace_e2c("\t clear buf %d sts=0x%x\n", c,
+			  (int) bh[c]->b_state);
+#endif
+		restore_b_data_himem(bh[c]);
+		memset(bh[c]->b_data, 0, inode->i_sb->s_blocksize);
+		continue;
+	    }
+	    if (cbh >= (nbh - 1)) {
+		break;
+	    }
+	    if (count < inode->i_sb->s_blocksize) {
+		ext2_unlock_rd_wa();
+		goto out_err;
+	    }
+	    cbh++;
+	    count -= inode->i_sb->s_blocksize;
+	    p = c >> (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
+	    if (!PageUptodate(pg[p])) {
+		addr0 = (clu_block0 << inode->i_sb->s_blocksize_bits);
+		b_start = addr0 + (c << inode->i_sb->s_blocksize_bits);
+		b_end = b_start + inode->i_sb->s_blocksize;
+#ifdef  EXT2_COMPR_REPORT_CPR
+		trace_e2c("\t[%d] sts=0x%x e=%d s=%d sz=%d pg:%lu(%#x)\n",
+			  c, (int) bh[c]->b_state, b_end, b_start,
+			  (int) inode->i_size, pg[p]->index,
+			  (unsigned int) pg[p]);
+#endif
+		if (b_end <= inode->i_size) {
+		    /* Block is before end of file, copy data */
+		    restore_b_data_himem(bh[c]);
+		    memcpy(bh[c]->b_data, src, inode->i_sb->s_blocksize);
+
+		} else if (b_start < inode->i_size) {
+		    /* Block contains end of file, copy to end */
+		    restore_b_data_himem(bh[c]);
+		    memcpy(bh[c]->b_data, src, inode->i_size - b_start);
+
+		}
+		set_buffer_uptodate(bh[c]);
+		set_buffer_dirty(bh[c]);
+		bh_writeout[bhn_writeout] = bh[c];	//mw
+		bhn_writeout++;	//mw
+	    } else {
+		//mw: DEBUG. buffer is uptodate now. compress will not reread! an get the compressed data!!!
+		// clear flag in extra page!!!
+		// clear_bit(BH_Uptodate, &bh[c]->b_state);
+
+		n = c & ((PAGE_CACHE_SIZE - 1) >> inode->i_sb->
+			 s_blocksize_bits);
+		bp = page_buffers(pg[p]);
+		for (i = 0; i < n; i++) {
+		    bp = bp->b_this_page;
+		}
+		result = ext2_get_block(inode, clu_block0 + c, bp, 0);
+
+		//mw: needed to do a writeback of the non-epg-buffers
+		//no idea how it was done before
+		set_buffer_uptodate(bp);
+		set_buffer_dirty(bp);
+		bh_writeout[bhn_writeout] = bp;	//mw
+		bhn_writeout++;	//mw
+
+		if (bp->b_blocknr == 0) {
+		    ext2_unlock_rd_wa();
+		    goto out_err;
+		}
+		assert(bp->b_blocknr == bh[c]->b_blocknr);
+	    }
+	    src += inode->i_sb->s_blocksize;
+	}
+	if (count > inode->i_sb->s_blocksize) {
+	    ext2_unlock_rd_wa();
+	    goto out_err;
+	}
+	p = c >> (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
+	if (!PageUptodate(pg[p])) {
+	    addr0 = (clu_block0 << inode->i_sb->s_blocksize_bits);
+	    b_start = addr0 + (c << inode->i_sb->s_blocksize_bits);
+#ifdef  EXT2_COMPR_REPORT_CPR
+	    trace_e2c("\t[%d] sts=0x%x c=%d s=%d sz=%d pg:%lu(%#x)\n", c,
+		      (int) bh[c]->b_state, count, b_start,
+		      (int) inode->i_size, pg[p]->index,
+		      (unsigned int) pg[p]);
+#endif
+	    if (b_start >= inode->i_size) {
+		restore_b_data_himem(bh[c]);
+		memset(bh[c]->b_data, 0, inode->i_sb->s_blocksize);
+
+	    } else {
+		if ((inode->i_size - b_start) < count) {
+		    restore_b_data_himem(bh[c]);
+		    memcpy(bh[c]->b_data, src, inode->i_size - b_start);
+		    memset(bh[c]->b_data + (inode->i_size - b_start), 0,
+			   count - (inode->i_size - b_start));
+		} else {
+		    restore_b_data_himem(bh[c]);
+		    memcpy(bh[c]->b_data, src, count);
+		}
+	    }
+	    set_buffer_uptodate(bh[c]);
+	    set_buffer_dirty(bh[c]);
+	    bh_writeout[bhn_writeout] = bh[c];	//mw
+	    bhn_writeout++;	//mw
+	} else {
+	    assert(epg[p] != NULL);	//mw
+	    n = c & ((PAGE_CACHE_SIZE - 1) >> inode->i_sb->
+		     s_blocksize_bits);
+	    bp = page_buffers(pg[p]);
+	    for (i = 0; i < n; i++) {
+		bp = bp->b_this_page;
+	    }
+	    result = ext2_get_block(inode, clu_block0 + c, bp, 0);
+
+	    //mw: needed to do a writeback of the non-epg-buffers
+	    //no idea how it was done before
+	    set_buffer_uptodate(bp);
+	    set_buffer_dirty(bp);
+	    bh_writeout[bhn_writeout] = bp;	//mw
+	    bhn_writeout++;	//mw
+	    if (bp->b_blocknr == 0) {
+		ext2_unlock_rd_wa();
+		goto out_err;
+	    }
+	    assert(bp->b_blocknr == bh[c]->b_blocknr);
+	}
+	result = (nbh - 1) * inode->i_sb->s_blocksize + count;
+    }
+
+
+    for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+	if (pg[i] == NULL)
+	    break;
+	if (i < d_npg)
+	    SetPageUptodate(pg[i]);
+    }
+
+    inode->i_ctime = CURRENT_TIME;
+    mark_inode_dirty_sync(inode);
+    /* If needed, EXT2_DIRTY_FL is raised by the caller. */
+
+    ext2_unlock_rd_wa();
+#ifdef EXT2_COMPR_REPORT_WA
+    printk(KERN_DEBUG "pid %d unlocks wa\n", current->pid);
+#endif
+
+#if 0
+    /* TODO: SYNC */
+    if (IS_SYNC(inode)) {
+	generic_osync_inode(inode, inode->i_mapping,
+			    OSYNC_METADATA | OSYNC_DATA);
+    }
+#endif
+    assert(result >= 0);
+
+    //Sync out changes:
+    assert(bhn_writeout <= EXT2_MAX_CLUSTER_BLOCKS);
+    assert(bhn_writeout >= 0);
+
+    //mw: debug
+    for (i = 0; i < bhn_writeout; i++) {
+	if ((!buffer_mapped(bh_writeout[i]))
+	    || (bh_writeout[i]->b_bdev == NULL)) {
+	    u32 block = ext2_cluster_block0(inode, cluster);
+	    ext2_get_block(inode, block + i, bh_writeout[i], 1);
+	    //printk("ext2_get_block Block:%lu, Mapped:%i, Page:%lu, bdev: %#x\n", bh_writeout[i]->b_blocknr, (bh_writeout[i]->b_state & BH_Mapped), (bh_writeout[i]->b_page ? bh_writeout[i]->b_page->index : 0), bh_writeout[i]->b_bdev );
+	}
+	assert(buffer_mapped(bh_writeout[i]));
+	assert(bh_writeout[i]->b_bdev != NULL);
+	assert(bh_writeout[i]->b_bdev == inode->i_sb->s_bdev);
+	/*if (bh_writeout[i]->b_bdev == NULL)
+	   bh_writeout[i]->b_bdev = inode->i_sb->s_bdev; //fix bdev-bug */
+    }
+
+    ll_rw_block(WRITE, bhn_writeout, bh_writeout);
+    //mw: seems we have to wait here, otherwise: crash!
+    for (i = 0; i < bhn_writeout; i++) {
+	if (bh_writeout[i])
+	    wait_on_buffer(bh_writeout[i]);
+    }
+    goto cleanup;
+
+  out_err:
+    printk("Error in Decompressing cluster: Err=%i\n", result);
+
+  cleanup:
+
+#ifdef CONFIG_HIGHMEM
+    if (kmapped)
+	ext2_kunmap_cluster_pages(NULL, pg, epg);
+#endif
+
+    for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+	if (pg[i] == NULL)
+	    break;
+	unlock_page(pg[i]);
+	page_cache_release(pg[i]);
+    }
+
+    for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+	if (epg[i] != NULL) {
+	    ClearPageDirty(epg[i]);
+	    ClearPageUptodate(epg[i]);
+	    try_to_free_buffers(epg[i]);
+	    unlock_page(epg[i]);
+	    assert(page_count(epg[i]) == 1);
+	    page_cache_release(epg[i]);
+	}
+    }
+
+    /*
+     *  Release buffers, don't forget to unlock the locked ones.
+     *  pjm 1998-01-14: TO_DO: Locked ones?
+     */
+    assert(nbh >= 0);
+    assert(nbh <= EXT2_MAX_CLUSTER_BLOCKS);
+#ifdef EXT2_COMPR_REPORT
+    trace_e2c(" < < < ext2_decompress_cluster %d: inode=%ld, res=%i\n",
+	      cluster, inode->i_ino, result);
+#endif
+    return result;
+}
+
+
+/*
+ * Function to decompress the pages of a cluster.
+ *
+ *	Allocate buffers to pages what are not mapped on the device.
+ *
+ *      Returns the size of decompressed data on success, a negative
+ *      value in case of failure, or 0 if some pages are not uptodate.
+ *
+ *      The inode is supposed to be writable.
+ *	All the pages must be UPTODATE, 
+ */
+int ext2_decompress_pages(struct inode *inode, u32 cluster,
+			  struct page *pg[])
+{
+    struct ext2_cluster_head *head;
+    struct buffer_head *bh0;
+    struct buffer_head *bh[EXT2_MAX_CLUSTER_BLOCKS];
+    unsigned nbh, c_nblk;
+    unsigned free_ix, clu_block0, clu_nblocks;
+    int i, pagesPerCluster, data_left, size = 0;
+    long status = 0;
+    char *dp;
+    struct buffer_head *bh_writeout[EXT2_MAX_CLUSTER_BLOCKS];
+    int bhn_writeout;
+#ifdef CONFIG_HIGHMEM
+    int kmapped = 0;
+
+    ext2_kmap_cluster_pages(NULL, pg, NULL);
+    kmapped = 1;
+#endif
+
+    for (i = 0; i < EXT2_MAX_CLUSTER_BLOCKS; i++) {
+	bh_writeout[i] = NULL;
+	bhn_writeout = 0;
+    }
+
+    /* First, get cluster_head (For this, we need to re-read the first block of
+       the cluster, without overwriting the data of the page the buffer point to... */
+    /* This suppose that cluster are aligned with PAGE_SIZE... To be improved */
+
+    /* Changed by Yabo Ding<bobfree_cn@yahoo.com.cn>,<yding@wyse.com>
+       The old code cannot reread data from disk to a changed buffers data pointer in 2.6.x.
+       So, I copy memory data(decompressed) to a temporary buffer;
+       Then reread data(compressed) from disk, and copy to head;
+       Then copy back the memory data from temporary buffer.
+       It seems clumsy, but it works well.
+     */
+
+    bh0 = page_buffers(pg[0]);
+    restore_b_data_himem(bh0);
+
+    head = (struct ext2_cluster_head *) kmalloc(bh0->b_size, GFP_KERNEL);
+    if (head == NULL) {
+	ext2_warning(inode->i_sb, "no more memory", "inode = %lu",
+		     inode->i_ino);
+	status = -EIO;
+	goto out_x;
+    }
+    dp = kmalloc(bh0->b_size, GFP_KERNEL);
+    if (dp == NULL) {
+	ext2_warning(inode->i_sb, "no more memory", "inode = %lu",
+		     inode->i_ino);
+	kfree(head);
+	status = -EIO;
+	goto out_x;
+    }
+    memcpy(dp, bh0->b_data, bh0->b_size);
+    clear_bit(BH_Uptodate, &bh0->b_state);
+    if (!buffer_mapped(bh0)) {
+	status =
+	    ext2_get_block(inode, ext2_cluster_block0(inode, cluster), bh0,
+			   0);
+	if (bh0->b_blocknr == 0) {
+	    trace_e2c
+		("ext2_decompress_pages: ext2_get_block error %ld (cluster = %u)\n",
+		 status, cluster);
+	    kfree(head);
+	    memcpy(bh0->b_data, dp, bh0->b_size);
+	    kfree(dp);
+	    status = -EIO;
+	    goto out;
+	}
+    }
+    ll_rw_block(READ, 1, &bh0);
+    wait_on_buffer(bh0);
+    //printk("RE-Read: Buffer: blocknr:%lu(%#x) \n", bh0->b_blocknr, bh0);
+    if (!buffer_uptodate(bh0)) {	/* Read error ??? */
+	trace_e2c("ext2_decompress_pages: IO error (cluster = %u)\n",
+		  cluster);
+	kfree(head);
+	memcpy(bh0->b_data, dp, bh0->b_size);
+	kfree(dp);
+	status = -EIO;
+	goto out;
+    }
+    /* This suppose that cluster are aligned with PAGE_SIZE... To be improved 
+       bh0->b_data = page_address(pg[0]);                                    */
+    memcpy((char *) head, bh0->b_data, bh0->b_size);
+    memcpy(bh0->b_data, dp, bh0->b_size);
+    kfree(dp);
+
+    if (head->magic != cpu_to_le16(EXT2_COMPRESS_MAGIC_04X)) {
+	ext2_warning(inode->i_sb,
+		     "ext2_decompress_pages: bad magic number",
+		     "inode = %lu, magic = %#04x", inode->i_ino,
+		     le16_to_cpu(head->magic));
+	kfree(head);
+	status = -EIO;
+	goto out;
+    }
+#ifdef  EXT2_COMPR_REPORT
+    trace_e2c("ext2_decompress_pages: clt=%d i=%ld head=0x%x\n", cluster,
+	      inode->i_ino, (unsigned) head);
+#endif
+
+    /* Now, try to do the same as in ext2_decompress_cluster for moving/allocating blocks */
+    nbh = 0;
+    pagesPerCluster = ext2_cluster_npages(inode, cluster);
+    for (i = 0; i < pagesPerCluster && pg[i]; i++) {
+	assert(PageLocked(pg[i]));
+	//if (!(PageUptodate(pg[i]))) {                 
+	//mw: do it like ext2_decompress_cluster to handle end of a file correctly
+	if (!(PageUptodate(pg[i]))
+	    && (pg[i]->index <= ((inode->i_size - 1) >> PAGE_CACHE_SHIFT))) {
+	    kfree(head);
+	    printk("should never happen: not all pages uptodate!\n");	//mw
+	    status = 0;
+	    goto out_x;
+	}
+    }
+
+    for (i = 0; i < pagesPerCluster && pg[i]; i++) {
+	struct buffer_head *bhead, *bhx;
+	int idx = 0;
+
+	/* assert(PageUptodate(pg[i])); with ftruncate() can be false */
+	if (!page_has_buffers(pg[i])) {
+	    ClearPageUptodate(pg[i]);	/*mw */
+	    ClearPageDirty(pg[i]);	/*mw */
+	    assert(0);
+	    create_empty_buffers_e2c(pg[i], inode->i_sb->s_blocksize, 0,
+				     inode);
+	    if (unlikely(!page_has_buffers(pg[i])))
+		printk("Error: NOMEM!\n");
+	}
+	bhead = page_buffers(pg[i]);
+	for (bhx = bhead; bhx != bhead || !idx; bhx = bhx->b_this_page) {
+	    idx++;
+	    bh[nbh] = bhx;
+	    nbh++;
+	}
+    }
+
+    while ((nbh != 0) && (bh[nbh - 1]->b_blocknr == 0))
+	--nbh;
+
+    c_nblk = nbh;
+
+    free_ix =
+	ext2_calc_free_ix(head->holemap_nbytes, (u8 const *) (&head[1]),
+			  c_nblk);
+    clu_block0 = ext2_cluster_block0(inode, cluster);
+    clu_nblocks = ext2_cluster_nblocks(inode, cluster);
+    ext2_unpack_blkaddrs(inode, bh, 0, head->holemap_nbytes,
+			 (u8 const *) (&head[1]), c_nblk, free_ix,
+			 clu_block0, clu_nblocks);
+
+    /* Allocate the extra blocks needed. */
+    data_left = size = le32_to_cpu(head->ulen);
+
+    data_left -= c_nblk << inode->i_sb->s_blocksize_bits;
+    assert(data_left > 0);
+    for (i = 0; i < free_ix; i++) {
+	if (bh[i]->b_blocknr != 0) {
+#ifdef  EXT2_COMPR_REPORT_CPR
+	    trace_e2c("\t [%d] blk=%ld sts=0x%x\n", i, bh[i]->b_blocknr,
+		      (int) bh[i]->b_state);
+#endif
+	    set_buffer_dirty(bh[i]);
+	    bh_writeout[bhn_writeout] = bh[i];	//mw
+	    bhn_writeout++;	//mw
+	}
+    }
+
+    for (i = free_ix; i < clu_nblocks; i++) {
+	if (((i >> 3) >= head->holemap_nbytes)
+	    || !(head->holemap[i >> 3] & (1 << (i & 7)))) {
+	    status =
+		ext2_get_block(inode, clu_block0 + i, bh[i],
+			       1 /* create */ );
+	    if (status || bh[i]->b_blocknr == 0) {
+		status = -EIO;
+		goto out;
+	    }
+#ifdef  EXT2_COMPR_REPORT_CPR
+	    trace_e2c("\t [%d] blk=%ld sts=0x%x\n", i, bh[i]->b_blocknr,
+		      (int) bh[i]->b_state);
+#endif
+	    set_bit(BH_Uptodate, &bh[i]->b_state);
+	    set_buffer_dirty(bh[i]);
+	    bh_writeout[bhn_writeout] = bh[i];	//mw
+	    bhn_writeout++;	//mw
+	    nbh++;
+	    data_left -= inode->i_sb->s_blocksize;
+	    if (data_left <= 0)
+		break;
+	}
+    }
+
+  out:
+    kfree(head);
+
+  out_x:
+
+    for (i = 0; i < bhn_writeout; i++) {
+
+	if ((!buffer_mapped(bh_writeout[i]))
+	    || (bh_writeout[i]->b_bdev == NULL)) {
+	    u32 block = ext2_cluster_block0(inode, cluster);
+	    ext2_get_block(inode, block + i, bh_writeout[i], 1);
+	    //printk("ext2_get_block Block:%lu, Mapped:%i, Page:%lu, bdev: %#x\n", bh_writeout[i]->b_blocknr, (bh_writeout[i]->b_state & BH_Mapped), (bh_writeout[i]->b_page ? bh_writeout[i]->b_page->index : 0), bh_writeout[i]->b_bdev );
+	}
+	assert(buffer_mapped(bh_writeout[i]));
+	assert(bh_writeout[i]->b_bdev != NULL);
+	assert(bh_writeout[i]->b_bdev == inode->i_sb->s_bdev);
+	/*if (bh_writeout[i]->b_bdev == NULL)
+	   bh_writeout[i]->b_bdev = inode->i_sb->s_bdev; //fix bdev-bug */
+    }
+    //Sync out changes:
+    ll_rw_block(WRITE, bhn_writeout, bh_writeout);
+    //mw: seems we have to wait here, otherwise: crash!
+    for (i = 0; i < bhn_writeout; i++) {
+	if (bh_writeout[i])
+	    wait_on_buffer(bh_writeout[i]);
+    }
+
+
+#ifdef CONFIG_HIGHMEM
+    if (kmapped)
+	ext2_kunmap_cluster_pages(NULL, pg, NULL);
+#endif
+
+    return (status ? status : size);
+}
+
+
+/* Decompress every cluster that is still compressed.
+   We stop and return -ENOSPC if we run out of space on device.
+
+   The caller needs to check for EXT2_COMPRBLK_FL before calling.
+
+   Returns 0 on success, -errno on failure.
+
+   Called by ext2_ioctl(). */
+int ext2_decompress_inode(struct inode *inode)
+{
+    u32 cluster;
+    u32 n_clusters;
+    int err = 0;
+    struct ext2_inode_info *ei = EXT2_I(inode);
+
+    /* TODO: We could try again to allocate it. */
+    if (ext2_rd_wa == NULL)
+	return -EBUSY;
+
+    assert(ei->i_flags & EXT2_COMPRBLK_FL);
+
+    /* Quotas aren't otherwise kept if file is opened O_RDONLY. */
+    DQUOT_INIT(inode);
+
+    //mutex_lock(&inode->i_mutex); /* MW 5-16-07 */
+    assert(atomic_read(&inode->i_mutex.count) <= 0);	/* i.e. mutex_lock */
+    err = 0;
+    /* This test can succeed because down() (and I think DQUOT_INIT) can block. */
+    if (!(ei->i_flags & EXT2_COMPRBLK_FL))
+	goto out;
+
+    n_clusters = ext2_n_clusters(inode);
+    for (cluster = 0; cluster < n_clusters; cluster++) {
+	err = ext2_cluster_is_compressed_fn(inode, cluster);
+	if (err > 0) {
+	    err = ext2_decompress_cluster(inode, cluster);
+	    /* If we later get an error, we'll need to recompress. */
+	    ei->i_flags |= EXT2_DIRTY_FL;
+	    ei->i_compr_flags |= EXT2_CLEANUP_FL;
+	}
+	if (err < 0)
+	    goto error;
+    }
+    assert(err >= 0);
+    err = 0;
+    ei->i_flags &= ~(EXT2_COMPRBLK_FL | EXT2_DIRTY_FL);
+    ei->i_compr_flags &= ~EXT2_CLEANUP_FL;
+  error:
+    inode->i_ctime = CURRENT_TIME;
+    mark_inode_dirty_sync(inode);
+  out:
+//      mutex_unlock(&inode->i_mutex); /* MW 5-16-07 */
+    return err;
+}
+
+
+/*
+   TODO: SECRM_FL
+
+   TODO: Avant de liberer les blocs, regarder si le compteur
+   est a 1, et marquer le noeud si ce n'est pas le cas
+   (pour preparer la recompression immediate).        
+
+   pjm fixme translation.
+   "Before freeing the blocks, check if the counter is 1, 
+   and mark the inode if not (in order to prepare for
+   immediate recompression)." */
+
+/* This is called by ext2_compress_cluster to free the blocks now
+   available due to compression.  We free ,nb` blocks beginning with
+   block ,block`.  We set the address of each freed block to
+   EXT2_COMPRESSED_BLKADDR, thus marking the cluster as compressed.
+   N.B. It is up to the caller to adjust i_blocks. */
+
+/* TODO: ext2_truncate() is much more careful than this routine.
+   (E.g. it checks for bh->b_count > 1, and checks for things changing
+   underneath it.  It also calls bforget instead of brelse if it's
+   going to free it.)  Why?  Maybe we should copy it. */
+
+/* effic: Reduce the number of calls to ext2_free_block() the way
+   ext2_trunc_direct() does. */
+
+/* fixme: I think tht we do indeed need to check if buffers are held by
+   somebody else before freeing them. */
+static int ext2_free_cluster_blocks(struct inode *inode, u32 block,
+				    unsigned nb)
+{
+    u32 tmp;
+    struct ext2_bkey key;
+    int err;
+
+/*
+ * whitpa 04 Oct 2004: although it may be true that using e2compr in
+ * conjunction with quotas is a Bad Idea, having quotas enabled for other
+ * filesystems doesn't necessarily mean that the quota feature will actually be
+ * used in this one, so many people find the following assertion very annoying.
+ * I have therefore disabled it.
+ */
+/*	assert (!inode->i_sb->dq_op || (inode->i_flags & S_QUOTA)); */
+    if (!nb)
+	return 0;
+    if (nb > EXT2_MAX_CLU_NBLOCKS) {
+	assert((int) nb >= 0);
+	assert(nb <= EXT2_MAX_CLU_NBLOCKS);
+	return -EDOM;
+    }
+    assert(((block + nb) & 3) == 0);
+    if (!ext2_get_key(&key, inode, block))
+	return -EIO;
+
+    while (nb-- > 0) {
+	tmp = ext2_get_key_blkaddr(&key);
+	err = ext2_set_key_blkaddr(&key, EXT2_COMPRESSED_BLKADDR);
+	if (err)
+	    goto out;
+	if (tmp != 0) {
+	    assert(tmp != EXT2_COMPRESSED_BLKADDR);
+#ifdef EXT2_COMPR_REPORT_ALLOC
+	    printk(KERN_DEBUG "ext2: free %d = (%d) %d:%d:%d:%d : %d\n",
+		   key.block,
+		   key.level,
+		   key.off[0], key.off[1], key.off[2], key.off[3], tmp);
+#endif
+	    ext2_free_blocks(inode, tmp, 1);
+	}
+	if (!ext2_next_key(&key, 1))
+	    break;
+    }
+    err = 0;
+  out:
+    ext2_free_key(&key);
+    return err;
+}
+
+
+
+static unsigned count_bits(unsigned char *p, unsigned nb)
+{
+    u32 x = le32_to_cpu(*(u32 *) p);
+    unsigned n = 0;
+
+    assert(nb <= 4);
+    if (nb != 4)
+	x &= (1 << (nb * 8)) - 1;
+    while (x) {
+	x &= (x - 1);
+	n++;
+    }
+    return n;
+}
+
+/*
+ * __remove_compr_assoc_queue is used in invalidate_inode_buffers
+ * replacement code for ext2_compress_cluster(). TLL 02/21/07
+ * Yeah, it is duplicate code, but using it does not require
+ * patching fs/buffer.c/__remove_assoc_queue to export it.
+ * The buffer's backing address_space's private_lock must be held.
+ */
+/*static inline void __remove_compr_assoc_queue(struct buffer_head *bh)
+{
+	list_del_init(&bh->b_assoc_buffers);
+}*/
+
+/* Compress one cluster.  If the cluster uses fewer blocks once
+   compressed, it is stored in place of the original data.  Unused
+   blocks are freed, and the cluster is marked as compressed.
+
+   Returns a negative value on error,
+   0 if the cluster does not compress well,
+   positive if it is compressed (whether it was already compressed
+   or whether we compressed it).
+ 
+   Assume inode is writable.
+ 
+   Called by :
+ 
+         ext2_cleanup_compressed_inode () [i_sem] 
+
+   If ever we acquire new callers, make sure that quotas are
+   initialised, and COMPRBLK is handled correctly (i.e. such
+   that ioctl() can't change the cluster size on us), and that caller
+   tests for ext2_wa==NULL.
+*/
+
+int ext2_compress_cluster(struct inode *inode, u32 cluster)
+{
+    struct buffer_head *bh[EXT2_MAX_CLUSTER_BLOCKS + 1];
+    struct page *pg[EXT2_MAX_CLUSTER_PAGES];
+    int s_nblk;			/* Equals clu_nblocks less any trailing hole blocks. */
+    unsigned u_nblk = (~(unsigned) 0), c_nblk;	/* Number of blocks occupied by
+						   un/compressed data. */
+    int result, n, x;
+    int ulen, maxlen = 0, clen = 0;
+    unsigned char *dst;
+    u8 *src;
+    unsigned meth, alg;
+    int nbh = 0, npg, i;
+    unsigned char holemap_nbytes = 0;
+    unsigned last_hole_pos;
+    struct ext2_cluster_head *head;
+    unsigned r_nblk;
+    struct ext2_inode_info *ei = EXT2_I(inode);
+    unsigned long saved_isize;
+    //int dotrunc = 1; //mw
+
+#ifdef CONFIG_HIGHMEM
+    int kmapped = 0;
+#endif
+
+    /* impl: Otherwise, ioctl() could change the cluster size
+       beneath us. */
+    /* TLL say not compressed and return -1 6-15-07 */
+    if (!(ei->i_flags & EXT2_COMPRBLK_FL))
+	return -1;
+
+    //mw
+    saved_isize = inode->i_size;
+
+    assert(atomic_read(&inode->i_mutex.count) <= 0);	/* i.e. mutex_lock */
+    assert(!mapping_mapped(inode->i_mapping));
+
+    npg = ext2_cluster_npages(inode, cluster);
+
+    result = ext2_get_cluster_pages(inode, cluster, pg, NULL, 1);
+    if (result <= 0)
+	goto done;
+
+#ifdef CONFIG_HIGHMEM
+    ext2_kmap_cluster_pages(NULL, pg, NULL);
+    kmapped = 1;
+#endif
+
+    /* effic: We ought to use the page cache.  Using the page
+       cache always costs extra CPU time, but saves I/O if the
+       page is present.  We still need to detect holes, which
+       unfortunately may still cause I/O.  Testing for all-zero
+       could save us that I/O. */
+
+    nbh = ext2_get_cluster_blocks(inode, cluster, bh, pg, NULL, 1);
+
+    s_nblk = nbh;
+
+#ifdef EXT2_COMPR_REPORT
+    {
+	int i;
+	trace_e2c(" > > > ext2_compress_cluster %d: inode=%ld, size=%d\n",
+		  cluster, inode->i_ino, (int) inode->i_size);
+#ifdef EXT2_COMPR_REPORT_CPR
+	for (i = 0; i < s_nblk; i++) {
+	    if (bh[i]) {
+		printk(KERN_DEBUG
+		       "bbuffer_head[%d]: blocknr=%lu, addr=0x%p ", i,
+		       (unsigned long) bh[i]->b_blocknr, bh[i]);
+		if (bh[i]->b_page)
+		    printk(KERN_DEBUG "bgn:[page->index=%ld]\n",
+			   bh[i]->b_page->index);
+		else
+		    printk(KERN_DEBUG "[No page]\n");
+	    } else
+		printk("bbuffer_head[%d] is NULL\n", i);
+	}
+#endif
+    }
+#endif
+    /*
+     *  Did somebody else compress the cluster while we were waiting ?
+     *  This should never arise ...
+     */
+    result = ext2_cluster_is_compressed_fn(inode, cluster);
+    if (result != 0) {
+	if (result > 0) {
+	    ext2_warning(inode->i_sb,
+			 "ext2_compress_cluster",
+			 "compressing compressed cluster");
+	}
+	goto done;
+    }
+
+    /* I moved it here in case we need to load a module that
+     * needs more heap that is currently allocated.
+     * In such case "init_module" for that algorithm forces
+     * re-allocation of ext2_wa. It should be safe here b/c the
+     * first reference to ext2_wa comes just after and we have 
+     * locked ext2_wa before.
+     *
+     * I know that we may not need the compression at all
+     * (compressing 0 or 1 block) but it's better to sacrifice
+     * a bit than do make a total mess of this code.
+     *
+     * FIXME: Totally separate working areas for reading and writing.
+     *      Jan Rï¿½ï¿½ï¿½ï¿½ï¿½ï¿½korajski
+     */
+
+    meth = ei->i_compr_method;
+    assert(meth < EXT2_N_METHODS);
+    alg = ext2_method_table[meth].alg;
+#ifdef CONFIG_KMOD
+    if (!ext2_algorithm_table[alg].avail) {
+	char str[32];
+
+	sprintf(str, "ext2-compr-%s", ext2_algorithm_table[alg].name);
+	request_module(str);
+    }
+#endif
+
+    /*
+     *  Try to get the working area.
+     */
+    assert(ext2_wr_wa != NULL);	/* Should have been tested by caller. */
+    result = -EINTR;
+    if (ext2_lock_wr_wa() == 0)
+	goto done;		/* Signal pending. */
+
+#ifdef EXT2_COMPR_REPORT_WA
+    printk(KERN_DEBUG "pid %d enters critical region\n", current->pid);
+#endif
+
+    /*
+     * Now, we try to compress the cluster.  If the cluster does
+     *    not compress well, we just give up.  Otherwise, we reuse
+     *    the old blocks to store the compressed data (except that
+     *    compressed data is contiguous in the file even if the
+     *    uncompressed data had holes).
+     */
+
+    /*
+     *  Compute the block bitmap, how many bytes of data we have
+     *    in the cluster, and the maximum interesting length after
+     *    compression.  The bitmap will be used to reallocate blocks
+     *    when decompressing the cluster, so that we don't create blocks
+     *    that were previously missing.  We also pack the buffers
+     *    together.
+     */
+
+    head = (struct ext2_cluster_head *) ext2_wr_wa->c;
+#if EXT2_MAX_CLUSTER_BLOCKS > 32
+# error "We need to zero more bits than this."
+#endif
+    *(u32 *) (&head[1]) = 0;
+    last_hole_pos = (unsigned) (-1);
+    assert(head->holemap[0] == 0);
+    assert(head->holemap[1] == 0);
+    assert(head->holemap[2] == 0);
+    assert(head->holemap[3] == 0);
+    assert(*(u32 *) head->holemap == 0);
+    assert(count_bits(head->holemap, 4) == 0);
+
+    /* TODO: Check that i_size can't change beneath us.
+       do_truncate() is safe because it uses i_sem around changing
+       i_size.  For the moment, I do a runtime check. */
+
+    saved_isize = inode->i_size;
+
+#ifdef EXT2_COMPR_REPORT_VERBOSE
+    printk
+	("00 ext2_compress_cluster[%u]: i_size=%u, s_blocksize_bits=%u, s_nblk=%u\n",
+	 __LINE__, (unsigned) inode->i_size, inode->i_sb->s_blocksize_bits,
+	 s_nblk);
+#endif
+//      assert (ROUNDUP_RSHIFT(inode->i_size, inode->i_sb->s_blocksize_bits)
+//              >= s_nblk);
+    /* This initial guess at ulen doesn't take holes into account
+       unless they're at end of cluster.  We ,compensate for other
+       holes` during the loop below. */
+    ulen = MIN(s_nblk << inode->i_sb->s_blocksize_bits,
+	       inode->i_size - ext2_cluster_offset(inode, cluster));
+    r_nblk = (((ulen - 1) >> inode->i_sb->s_blocksize_bits) + 1);
+    if (r_nblk <= 1) {
+	/* MW: required to remove Z flag, otherwise compress 
+	 * is tried on each access */
+	result = 0;
+	goto no_compress;
+    }
+    /* Verify if more than 1 block to compress in the cluster       */
+    nbh = 0;
+    for (x = 0; x < s_nblk; x++) {
+	if ((bh[x] != NULL) && (bh[x]->b_blocknr != 0)) {
+	    nbh++;
+	} else {
+	    last_hole_pos = x;
+	    head->holemap[x >> 3] |= 1 << (x & 7);
+	    ulen -= inode->i_sb->s_blocksize;
+	    /* impl: We know that it's a whole block because
+	       ext2_get_cluster_blocks trims s_nblk for trailing
+	       NULL blocks, and partial blocks only come at
+	       the end, so there can't be partial NULL blocks. */
+	}
+    }
+    /* We don't try to compress cluster that only have one block
+       or no block at all.  (When fragments are implemented, this code
+       should be changed.) */
+    if (nbh <= 1) {
+	/* MW: required to remove Z flag, otherwise compress 
+	 * is tried on each access */
+	goto no_compress;
+    }
+
+    u_nblk = nbh;
+    /* Copy the data in the compression area        */
+    dst = ext2_wr_wa->u;
+    for (x = 0; x < s_nblk; x++) {
+	if ((bh[x] != NULL) && (bh[x]->b_blocknr != 0)) {
+	    restore_b_data_himem(bh[x]);
+	    memcpy(dst, bh[x]->b_data, bh[x]->b_size);
+	    dst += bh[x]->b_size;
+	}
+    }
+    ext2_wr_wa_ucontents.ino = inode->i_ino;
+    ext2_wr_wa_ucontents.dev = inode->i_rdev;
+    ext2_wr_wa_ucontents.cluster = cluster;
+    //printk("Co: UnCompressed:  %i-%lu-%lu\n", cluster, ext2_adler32 (12345, ext2_wr_wa->u, ulen), ulen);
+
+    assert(count_bits(head->holemap, 4) == s_nblk - u_nblk);
+
+#if EXT2_GRAIN_SIZE != EXT2_MIN_BLOCK_SIZE
+# error "this code ought to be changed"
+#endif
+
+    /* ,maxlen` is the maximum length that the compressed data can
+       be while still taking up fewer blocks on disk. */
+    holemap_nbytes = (last_hole_pos >> 3) + 1;
+    /* impl: Remember that ,last_hole_pos` starts off as being -1,
+       so the high 3 bits of ,last_hole_pos >> 3` can be wrong.
+       This doesn't matter if holemap_nbytes discards the high
+       bits. */
+
+    assert(sizeof(holemap_nbytes) < sizeof(unsigned));
+    assert((last_hole_pos == (unsigned) -1)
+	   == (holemap_nbytes == 0));
+    maxlen =
+	((((r_nblk <
+	    u_nblk) ? r_nblk : u_nblk) - 1) * inode->i_sb->s_blocksize -
+	 sizeof(struct ext2_cluster_head)
+	 - holemap_nbytes);
+    clen = 0;
+    /* Handling of EXT2_AUTO_METH at the moment is just that we
+       use the kernel default algorithm.  I hope that in future
+       this can be extended to the kernel deciding when to
+       compress and what algorithm to use, based on available disk
+       space, CPU time, algorithms currently used by the fs,
+       etc. */
+    if ((meth == EXT2_AUTO_METH)
+	|| !ext2_algorithm_table[alg].avail) {
+	meth = EXT2_DEFAULT_COMPR_METHOD;
+	alg = ext2_method_table[meth].alg;
+	assert(ext2_algorithm_table[alg].avail);
+    }
+    if (alg == EXT2_NONE_ALG)
+	goto no_compress;
+
+    clen = ext2_algorithm_table[alg].compress
+	(ext2_wr_wa->u,
+	 ext2_wr_wa->c + sizeof(struct ext2_cluster_head) + holemap_nbytes,
+	 ext2_wr_wa->heap, ulen, maxlen, ext2_method_table[meth].xarg);
+
+#ifdef EXT2_COMPR_REPORT_ALGORITHMS
+    printk(KERN_DEBUG "03 ext2: %lu: cluster %d+%d [%d] compressed "
+	   "into %d bytes (ulen = %d, maxlen = %d)\n",
+	   inode->i_ino,
+	   ext2_cluster_offset(inode, cluster),
+	   ext2_cluster_nblocks(inode, cluster),
+	   u_nblk, clen, ulen, maxlen);
+#endif
+
+    if ((clen == 0) || (clen > maxlen)) {
+      no_compress:
+
+	/* this chunk didn't compress. */
+	assert(inode->i_size == saved_isize);
+#ifdef EXT2_COMPR_REPORT_WA
+	printk(KERN_DEBUG
+	       "pid %d leaves critical region, nbh=%d, u_nblk=%d, "
+	       "inode->i_size=%lu, saved_isize=%lu, clen=%d, ulen=%d, maxlen=%d\n",
+	       current->pid, nbh, u_nblk,
+	       (long unsigned) inode->i_size, saved_isize, clen, ulen,
+	       maxlen);
+#endif
+
+	result = 0;
+	ext2_unlock_wr_wa();
+	goto done;
+    }
+
+#ifdef CONFIG_EXT2_VERIFY_COMPRESSION
+    /* */
+    {
+	size_t dlen;
+	size_t comp_len;
+	char bdn[BDEVNAME_SIZE];
+
+	memset(ext2_wr_wa->u, 0, ulen);
+	// [UH] length corrected to data length
+	/* FIXME: This requires that the wr_wa have at least as much
+	   heap space as rd_wa (i.e. enough space to decompress). 
+	   Alternatively, consider using rd_wa here, but that requires 
+	   gaining the lock. */
+	dlen =	    ext2_algorithm_table[alg].decompress(ext2_wr_wa->c +
+						 sizeof(struct
+							ext2_cluster_head)
+						 + holemap_nbytes,
+						 ext2_wr_wa->u,
+						 ext2_wr_wa->heap, clen,
+						 ulen,
+						 ext2_method_table[meth].
+						 xarg);
+	if (dlen != ulen) {
+	    printk(KERN_ERR "compress.c: %s/%u error detected!  "
+		   "dlen=%u, ulen=%u, pid=%d, dev=%s, ino=%lu, clu=%u.  "
+		   "Please report to e2compr list.\n",
+		   ext2_algorithm_table[alg].name,
+		   ext2_method_table[meth].xarg,
+		   dlen, ulen, current->pid, bdevname(inode->i_sb->s_bdev,
+						      bdn), inode->i_ino,
+		   cluster);
+	    goto no_compress;
+	}
+	dst = ext2_wr_wa->u;
+	x = 0;
+	comp_len = 0;
+	while (comp_len < ulen) {
+	    if ((bh[x] != NULL) && (bh[x]->b_blocknr != 0)) {
+		restore_b_data_himem(bh[x]);
+		if (memcmp
+		    (dst, bh[x]->b_data,
+		     MIN(bh[x]->b_size, ulen - comp_len)) != 0) {
+		    printk(KERN_ERR "compress.c: %s/%u error detected!  "
+			   "pid=%d, dev=%s, ino=%lu, clu=%u.  "
+			   "Please report to e2compr list.\n",
+			   ext2_algorithm_table[alg].name,
+			   ext2_method_table[meth].xarg, current->pid,
+			   bdevname(inode->i_sb->s_bdev, bdn),
+			   inode->i_ino, cluster);
+		    goto no_compress;
+		}
+		dst += bh[x]->b_size;
+		comp_len += bh[x]->b_size;
+	    }
+	    x++;
+	}
+    }
+#endif				/* verify compression */
+
+#if EXT2_MAX_CLUSTER_BLOCKS > 32
+# error "We need to zero more bits than this."
+#endif
+    assert(-1 <= (int) last_hole_pos);
+    assert((int) last_hole_pos < 32);
+    assert((le32_to_cpu(*(u32 *) head->holemap)
+	    & (~0u << (1 + last_hole_pos))
+	    & (~(~0u << (8 * holemap_nbytes))))
+	   == 0);
+    /* Don't change "~0u << (1 + last_hole_pos)" to "~1u << last_hole_pos" 
+       as I almost did, as last_hole_pos can be -1 and cannot be 32. */
+    assert(count_bits(head->holemap, holemap_nbytes) == s_nblk - u_nblk);
+
+    /* Compress the blocks at the beginning of the cluster  */
+    for (x = 0, nbh = 0; x < s_nblk; x++) {
+	if ((bh[x] != NULL) && (bh[x]->b_blocknr != 0)) {
+	    if (nbh != x) {
+		restore_b_data_himem(bh[x]);
+		bh[nbh]->b_blocknr = bh[x]->b_blocknr;
+		set_bit(BH_Mapped, &bh[nbh]->b_state);
+		bh[x]->b_blocknr = 0;
+		assert(buffer_mapped(bh[x]));
+		clear_bit(BH_Mapped, &bh[x]->b_state);
+	    }
+	    nbh++;
+	}
+    }
+    assert(nbh == u_nblk);
+    assert(count_bits(head->holemap, holemap_nbytes) == s_nblk - u_nblk);
+
+    /*
+     * Compression was successful, so add the header and copy to blocks.
+     */
+
+    /* Header. */
+    {
+	head->magic = cpu_to_le16(EXT2_COMPRESS_MAGIC_04X);
+	head->method = meth;
+	head->holemap_nbytes = holemap_nbytes;
+	head->ulen = cpu_to_le32(ulen);
+	head->clen = cpu_to_le32(clen);
+	barrier();
+	head->checksum =
+	    cpu_to_le32(ext2_adler32
+			(le32_to_cpu(*(u32 *) ext2_wr_wa->c),
+			 ext2_wr_wa->c + 8,
+			 (sizeof(struct ext2_cluster_head) - 8 +
+			  head->holemap_nbytes + clen)));
+	//printk("CCS %i-%lu\n", cluster, head->checksum);
+    }
+
+    assert((le32_to_cpu(*(u32 *) head->holemap)
+	    & (~0 << (1 + last_hole_pos))
+	    & ((1 << (8 * holemap_nbytes)) - 1)) == 0);
+    result = clen += sizeof(struct ext2_cluster_head) + holemap_nbytes;
+    c_nblk = ROUNDUP_RSHIFT(clen, inode->i_sb->s_blocksize_bits);
+
+    /* Release unneeded buffer heads.  (Freeing is done later,
+       after unlocking ext2_wr_wa.) */
+    assert(nbh == u_nblk);
+    nbh = c_nblk;
+
+#ifdef  EXT2_COMPR_REPORT
+    trace_e2c("ext2_compress_cluster: head->clen=%d, clen=%d\n", head->clen, clen);
+#endif
+    src = ext2_wr_wa->c;
+
+    //printk("Co: Compressed:    %i-%lu-%lu\n", cluster, ext2_adler32 (12345, ext2_rd_wa->c, head->clen), head->clen);
+
+    for (n = 0; (int) clen > 0; n++) {
+	restore_b_data_himem(bh[n]);
+	if (clen >= inode->i_sb->s_blocksize) {
+	    memcpy(bh[n]->b_data, src, inode->i_sb->s_blocksize);
+	} else {
+	    memcpy(bh[n]->b_data, src, clen);
+	}
+
+	/* TO_DO: OSYNC.  means: write opertions are blocking until the
+	 * the pages are written from page cache to disk */
+
+	set_buffer_uptodate(bh[n]);
+	set_buffer_dirty(bh[n]);
+	src += inode->i_sb->s_blocksize;
+	clen -= inode->i_sb->s_blocksize;
+    }
+
+    i = 0;
+    assert(n == c_nblk);
+    assert((le32_to_cpu(*(u32 *) head->holemap)
+	    & (~0 << (1 + last_hole_pos))
+	    & ((1 << (8 * holemap_nbytes)) - 1)) == 0);
+
+    /* Runtime check that no-one can change i_size while i_sem is down.
+       (See where saved_isize is set, above.) */
+    assert(inode->i_size == saved_isize);
+
+    assert(!mapping_mapped(inode->i_mapping));
+
+    /* Free the remaining blocks, and shuffle used blocks to start
+       of cluster in blkaddr array. */
+    {
+	u32 free_ix, curr;
+	int err;
+
+	/* Calculate free_ix.  There should be ,c_nblk`
+	   non-hole blocks among the first ,free_ix`
+	   blocks. */
+	{
+	    assert((le32_to_cpu(*(u32 *) head->holemap)
+		    & (~0 << (1 + last_hole_pos))
+		    & ((1 << (8 * holemap_nbytes)) - 1)) == 0);
+	    assert(n == c_nblk);
+	    for (free_ix = 0;
+		 ((int) free_ix <= (int) last_hole_pos) && (n > 0);
+		 free_ix++)
+		if (!(head->holemap[free_ix >> 3]
+		      & (1 << (free_ix & 7))))
+		    n--;
+	    free_ix += n;
+
+	    if ((free_ix < c_nblk)
+		|| (free_ix + u_nblk > s_nblk + c_nblk)
+		|| (free_ix >= ext2_cluster_nblocks(inode, cluster))
+		|| ((holemap_nbytes == 0) && (c_nblk != free_ix))) {
+		assert(free_ix >= c_nblk);
+		/*assert (free_ix - c_nblk <= s_nblk - u_nblk); */
+		assert(free_ix + u_nblk <= s_nblk + c_nblk);
+		assert(free_ix < ext2_cluster_nblocks(inode, cluster));
+		assert((holemap_nbytes != 0) || (c_nblk == free_ix));
+		assert(1 <= c_nblk);
+		assert(c_nblk < u_nblk);
+		assert(u_nblk <= s_nblk);
+		assert(s_nblk <= ext2_cluster_nblocks(inode, cluster));
+		assert(ext2_cluster_nblocks(inode, cluster) <=
+		       EXT2_MAX_CLU_NBLOCKS);
+		ext2_error(inode->i_sb, "ext2_compress_cluster",
+			   "re assertions: c=%d, u=%d, f=%d, s=%d, n=%d, "
+			   "lhp=%d, hm=%x, hnb=%d, " "ino=%lu, clu=%u",
+			   (int) c_nblk, (int) u_nblk, (int) free_ix,
+			   (int) s_nblk, (int) ext2_cluster_nblocks(inode,
+								    cluster),
+			   (int) last_hole_pos,
+			   (unsigned) le32_to_cpu(*(u32 *) head->holemap),
+			   (int) holemap_nbytes, inode->i_ino, cluster);
+	    }
+	}
+
+	/* Free unneeded blocks, and mark cluster as
+	   compressed. */
+	err = ext2_free_cluster_blocks
+	    (inode,
+	     ext2_cluster_block0(inode, cluster) + free_ix,
+	     ext2_cluster_nblocks(inode, cluster) - free_ix);
+	/* pjm 1998-06-15: This should help reduce fragmentation.
+	   Actually, we could set block to clu_block0 + clu_nbytes,
+	   and goal to the last allocated blkaddr in the compressed
+	   cluster.
+	   It would be nice if we would transfer the freed blocks
+	   to preallocation, while we're at it. */
+//              write_lock(&ei->i_meta_lock);
+	/* mw: i_next_alloc_goal and i_next_alloc_block were removed in 2.6.24.x
+	 *     so we dont need to set them to 0 (they are anyway, somehow).
+	 */
+	//ei->i_next_alloc_goal = ei->i_next_alloc_block = 0;
+//              write_unlock(&ei->i_meta_lock);
+	if (err < 0) {
+	    ext2_unlock_wr_wa();
+	    goto done;
+	}
+	/* Note that ext2_free_cluster_blocks() marks the
+	   cluster as compressed. */
+
+	/* Shuffle used blocks to beginning of block-number array. */
+	{
+	    struct ext2_bkey key;
+	    unsigned i;
+
+	    if (!ext2_get_key(&key,
+			      inode,
+			      ext2_cluster_block0(inode, cluster))) {
+		ei->i_flags |= EXT2_ECOMPR_FL;
+		result = -EIO;
+		free_ix = 0;
+	    }
+	    for (i = 0; i < free_ix; i++) {
+		curr = ext2_get_key_blkaddr(&key);
+
+		if ((c_nblk == free_ix)
+		    && (curr != bh[i]->b_blocknr)) {
+		    /* "Can't happen", yet has
+		       happened a couple of times. */
+		    ext2_error(inode->i_sb, "ext2_compress_cluster",
+			       "c_nblk=free_ix=%d, "
+			       "curr=%u, b_blocknr=%lu, "
+			       "lhp=%d, hm=%x, "
+			       "ino=%lu, blk=%u",
+			       c_nblk, curr,
+			       (unsigned long) bh[i]->b_blocknr,
+			       (int) last_hole_pos,
+			       (unsigned) le32_to_cpu(*(u32 *) head->
+						      holemap),
+			       inode->i_ino,
+			       (unsigned) ext2_cluster_block0(inode,
+							      cluster) +
+			       i);
+		}
+		err = ext2_set_key_blkaddr(&key,
+					   (i < c_nblk
+					    ? bh[i]->b_blocknr
+					    : EXT2_COMPRESSED_BLKADDR));
+		if (err)
+		    break;
+		if (!ext2_next_key(&key, 1)) {
+		    ei->i_flags |= EXT2_ECOMPR_FL;	/* sorry... */
+		    result = -EIO;
+		    break;
+		}
+	    }
+	    ext2_free_key(&key);
+	}
+    }
+
+    /*
+     *        Unlock the working area.
+     */
+
+#ifdef EXT2_COMPR_REPORT_WA
+    printk(KERN_DEBUG "pid %d leaves critical region\n", current->pid);
+#endif
+
+    ext2_unlock_wr_wa();
+    head = NULL;		/* prevent any more stupid bugs */
+
+    assert(c_nblk < u_nblk);
+    ext2_mark_algorithm_use(inode, alg);
+
+    /* TLL update b_assoc_map per 2.6.20 6-07-07 */
+    for (i = 0; i < c_nblk; i++)
+	if (bh[i] != NULL) {
+	    bh[i]->b_assoc_map = inode->i_mapping;
+	    bh[i]->b_page->mapping = inode->i_mapping;	//Andreas 5-24-07 : necessary? WRONG?
+	}
+    //mw: we must force the writeback, otherwise ext2_readpage will get confused
+    //    yaboo ding had similiar code above. but I think it makes more sense after
+    //    the block shuffeling.
+    //    Note: generic_oysnc_inode() made trouble with USB-Sticks and caused a lot
+    //    of IO, stalled system ... therefore ll_rw_block() replace it. Anyway we already operate 
+    //        with this low-level function. 
+
+    /*mw: new "hole" fix. hole == bdev bug! */
+    for (i = 0; i < c_nblk; i++) {
+
+	/* this was a hole (uncompressed)
+	 * at the beginning of the cluster.
+	 * so NO block was yet associated with it.
+	 * But now we need it, because a compressed
+	 * cluster always starts at the cluster.*/
+	if (!buffer_mapped(bh[i]) || bh[i]->b_bdev == NULL) {
+	    u32 block = ext2_cluster_block0(inode, cluster);
+	    ext2_get_block(inode, block + i, bh[i], 1);
+	    //printk("ext2_get_block Block:%lu, Mapped:%i, Page:%lu, bdev: %#x\n", bh[i]->b_blocknr, (bh[i]->b_state & BH_Mapped), (bh[i]->b_page ? bh[i]->b_page->index : 0), bh[i]->b_bdev );
+	}
+	assert(buffer_mapped(bh[i]));
+	assert(bh[i]->b_bdev != NULL);
+	assert(bh[i]->b_bdev == inode->i_sb->s_bdev);
+    }
+
+    ll_rw_block(WRITE, c_nblk, bh);
+    //mw: seems we have to wait here, otherwise: crash!
+    for (i = 0; i < c_nblk; i++) {
+	if (bh[i])
+	    wait_on_buffer(bh[i]);
+	//printk("written compressed block: Block:%lu, Mapped:%i, Page:%lu, bdev: %#x\n", bh[i]->b_blocknr, (bh[i]->b_state & BH_Mapped), (bh[i]->b_page ? bh[i]->b_page->index : 0), bh[i]->b_bdev );
+    }
+
+
+#ifdef CONFIG_HIGHMEM
+    if (kmapped)
+	ext2_kunmap_cluster_pages(NULL, pg, NULL);
+#endif
+
+    inode->i_ctime = CURRENT_TIME;	//mw: these two come always together. So I also put it here.
+    mark_inode_dirty_sync(inode);
+
+    //ext2_update_inode(inode, inode_needs_sync(inode)); //mw: might be able to fix pipe_write vs. readpage. mutex-rec-locking
+
+    /* COMPRBLK is already high, so no need to raise it. */
+    {
+	for (i = c_nblk; (i < EXT2_MAX_CLUSTER_BLOCKS) && (bh[i] != NULL);
+	     i++) {
+	    clear_buffer_dirty(bh[i]);
+	    bh[i]->b_blocknr = 0;
+	    clear_bit(BH_Mapped, &bh[i]->b_state);
+	    clear_bit(BH_Uptodate, &bh[i]->b_state);
+	}
+	for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+	    if (pg[i] == NULL) {
+		break;
+	    }
+	    assert(PageLocked(pg[i]));
+	    ClearPageUptodate(pg[i]);
+	    unlock_page(pg[i]);
+	    page_cache_release(pg[i]);
+	}
+
+	/* invalidate_inode_buffers replacement code: TLL 02/21/07
+	 * e2compr on post 2.6.10 kernels do not have an uptodate
+	 * mapping->assoc_mapping (other Vm(?) changes require it be
+	 * made explicit, 2.4 kernels have it implicit). Therefore, when
+	 * umount is called, a GPF ensues from a NULL ops pointer.
+	 * e2c on a USB thumbdrive mounted as the root fs does not
+	 * support repeated compress/uncompress cycles on a given file.
+	 * Inlined the flush list code to explicityly force update to
+	 * disk with a known valid bh list.
+	 */
+
+	/* mw: I consider this code as ... not so good! */
+	/*	  
+	  if (inode_has_buffers(inode)) {
+		//struct address_space *mapping = &inode->i_data;
+		// struct address_space *buffer_mapping = mapping->assoc_mapping;
+		// requires: inode->i_data->mapping->assoc_mapping; to be set
+		invalidate_inode_buffers(inode);	// TLL do it proper 5-25-07
+		//if (dotrunc)
+		 //ext2_truncate(inode);	// TLL file size hack 6-19-07 
+	  }
+	*/
+
+    }
+#ifdef  EXT2_COMPR_REPORT
+    trace_e2c(" < < < ext2_compress_cluster %i: [done cpr] inode=%ld\n", cluster, inode->i_ino);
+#endif
+    return result;
+
+
+  done:
+
+#ifdef CONFIG_HIGHMEM
+    if (kmapped)
+	ext2_kunmap_cluster_pages(NULL, pg, NULL);
+#endif
+
+    {
+	for (i = 0; i < EXT2_MAX_CLUSTER_PAGES; i++) {
+	    if (pg[i] == NULL) {
+		break;
+	    }
+	    unlock_page(pg[i]);
+	    page_cache_release(pg[i]);
+	}
+	/* TLL cp to compr dir bug fix 03-25-07
+	   Truncate uncompressed files to their uncompressed
+	   length, i.e. force kernel to update inode and sb */
+	//if(dotrunc)
+	ext2_truncate(inode);
+    }
+#ifdef EXT2_COMPR_REPORT_VERBOSE
+    {
+	int i;
+
+	printk(KERN_DEBUG "ext2_compress_cluster[end]: buffers kept for cluster=%d\n", cluster);
+	for (i = 0; i < nbh; i++) {
+	    if (bh[i]) {
+		printk(KERN_DEBUG "2buffer_head[%d]: blocknr=%lu, addr=0x%p ", i, (unsigned long) bh[i]->b_blocknr, bh[i]);
+		if (bh[i]->b_page)
+		    printk(KERN_DEBUG "2:[page->index=%ld]\n", bh[i]->b_page->index);
+		else
+		    printk(KERN_DEBUG "[No page]\n");
+	    } else
+		printk(KERN_DEBUG "buffer_head[%d] is NULL\n", i);
+	}
+    }
+#endif
+
+#ifdef  EXT2_COMPR_REPORT
+    trace_e2c(" < < < ext2_compress_cluster %i: [done NO cpr] inode=%ld\n", cluster, inode->i_ino);
+#endif
+    return result;
+}
+
+
+/* Go through all the clusters and compress them if not already
+   compressed.
+
+   This is called by ext2_put_inode() and ext2_release_file().  Later,
+   we may have ext2_ioctl() call it (when EXT2_COMPR_FL rises).  None
+   of the callers does any locking, so we do it here.
+
+   Neither of the current callers uses the return code, but we get ready
+   for if we start using it.
+
+   Returns 0 on "success" (whether or not we cleared EXT2_CLEANUP_FL
+   or EXT2_DIRTY_FL bits), -errno on error. */
+int ext2_cleanup_compressed_inode(struct inode *inode)
+{
+    u32 cluster;
+    u32 n_clusters;
+    int dirty = 0;
+    int err = 0;
+    u32 comprblk_mask;
+    atomic_t start_i_count = inode->i_count;
+    int retry = 0;
+    int have_downed;
+    struct ext2_inode_info *ei = EXT2_I(inode);
+#ifdef EXT2_COMPR_REPORT
+    char bdn[BDEVNAME_SIZE];
+#endif
+
+    /* impl: Actually, this assertion could fail if the kernel
+       isn't locked.  I haven't looked, but I suppose that the
+       kernel always is locked when this is called. */
+    assert(ei->i_compr_flags & EXT2_CLEANUP_FL);
+
+#ifdef EXT2_COMPR_REPORT_PUT
+    printk(KERN_DEBUG "ext2_cleanup_compressed_inode() called for pid %d; "
+	   "dev=%s, ino=%lu, i_state=0x%lx, i_count=%u\n",
+	   current->pid, bdevname(inode->i_sb->s_bdev, bdn), inode->i_ino,
+	   inode->i_state, atomic_read(&inode->i_count));
+#endif
+
+    /* Do these tests twice: once before down() and once after. */
+    for (have_downed = 0;; have_downed++) {
+	if ((ei->i_flags & (EXT2_COMPR_FL | EXT2_DIRTY_FL))
+	    != (EXT2_COMPR_FL | EXT2_DIRTY_FL)) {
+	    if (have_downed)
+		goto out;
+	    /* TLL 5-25-07 changed from a warning to trace */
+	    /*trace_e2c("ext2_cleanup_compressed_inode: trying to un/compress an "
+	       "uncompressable file.\n"
+	       "i_flags=%#x. (dev=%s, ino=%lu, down=%d)\n",
+	       ei->i_flags, bdevname(inode->i_sb->s_bdev, bdn), 
+	       inode->i_ino, have_downed); */
+	    return 0;
+	}
+
+	/* test if file is mapped by mmap */
+	if (mapping_mapped(inode->i_mapping))
+	{
+	    //trace_e2c("ext2_cleanup_compressed_inode: (dev. %s): ino=%ld: file mapped, does not compress cluster\n", bdevname(inode->i_sb->s_bdev, bdn), inode->i_ino);
+	    if (have_downed)
+		goto out;
+	    else
+		return 0;
+	}
+
+	if (IS_RDONLY(inode)
+	    || (ei->i_flags & EXT2_ECOMPR_FL)
+	    || (ext2_wr_wa == NULL)) {
+	    ei->i_compr_flags &= ~EXT2_CLEANUP_FL;
+	    if (have_downed)
+		goto out;
+	    else
+		return 0;
+	}
+	//mw            
+	if (ext2_get_dcount(inode) > 1) {
+	    err = 0;
+	    //printk("Compress: file busy (dcount: %i>1)\n", ext2_get_dcount(inode));
+	    if (have_downed)
+		goto out;
+	    else
+		return 0;
+	}
+
+	if (have_downed)
+	    break;
+
+	/* Quotas aren't otherwise kept if file is opened O_RDONLY. */
+	DQUOT_INIT(inode);
+
+	/* Check whether OSYNC of inode is acutally running */
+	//if (ei->i_compr_flags & EXT2_OSYNC_INODE)
+	//printk(KERN_DEBUG "OSYNC!\n");
+
+	/* I think:
+	 * checking these flags should prevent that one Process aquires the MUTEX again, 
+	 * e.g. in a recursive call
+	 * BUT: what happens acutally: two processes are working on this inode: pdflush and the userprogramm
+	 * SO: the check might be correct if:  ei->i_compr_flags & EXT2_OSYNC_INOD AND the same process already posesses this lock!!!
+	 */
+	//if (!(ei->i_compr_flags & EXT2_OSYNC_INODE))
+	//{
+	mutex_lock(&inode->i_mutex);
+#ifdef EXT2_COMPR_REPORT_MUTEX
+	printk(KERN_DEBUG "CLEANUP_LOCK of PID %u @ inode:%lu\n", current->pid, inode->i_ino);
+#endif
+	//}
+    }
+    n_clusters = ext2_n_clusters(inode);
+
+#ifdef EXT2_COMPR_REPORT_PUT
+    printk(KERN_DEBUG "ext2: inode:%lu: put compressed, clusters = %d, flags = %x, pid = %u\n",
+	   inode->i_ino, n_clusters, ei->i_flags, current->pid);
+#endif
+
+    assert(atomic_read(&inode->i_mutex.count) <= 0);	/* i.e. mutex_lock */
+
+    /* Try to compress the clusters.  We clear EXT2_DIRTY_FL only
+       if we looked at every cluster and if there was no error.  */
+
+    /* impl: We raise EXT2_COMPRBLK_FL now so that ext2_ioctl()
+       doesn't try to change the cluster size beneath us.  If need
+       be, we restore the bit to its original setting before
+       returning.  Note that no-one else can _change_
+       EXT2_COMPRBLK_FL while we work because i_sem is down. */
+    /* impl: Note what's happening here with comprblk_mask.  The
+       current state of COMPRBLK_FL (before we start) is that
+       (comprblk == 1) || (no compressed clusters).  At the end of
+       the procedure, comprblk == one if (at least one compressed
+       cluster, or an error occurred preventing us from finding
+       out). */
+    comprblk_mask = ~EXT2_COMPRBLK_FL | ei->i_flags;
+    ei->i_flags |= EXT2_COMPRBLK_FL;
+
+    for (cluster = 0; cluster < n_clusters; cluster++) {
+	if (atomic_read(&inode->i_count) > atomic_read(&start_i_count)) {
+	    /* This is a poor way of doing this (and doubly
+	       poor now that the only users of i_count are
+	       the dentries), but the idea is not to
+	       compress things tht are likely to be
+	       decompressed soon.  I guess a better way of
+	       doing this would be just to make sure tht
+	       the stuff is in the page cache. */
+	    retry = 1;
+	    break;
+	}
+	err = ext2_cluster_is_compressed_fn(inode, cluster);
+	if (err == 0) {
+	    //mw: ext2_compress_cluster might clean EXT2_COMPRBLK_FL, therefore raise it for every new cluster
+	    ei->i_flags |= EXT2_COMPRBLK_FL;
+
+	    err = ext2_compress_cluster(inode, cluster);
+	    if (err < 0)
+		dirty = 1;
+	    else if (err > 0)
+		comprblk_mask = ~0ul;
+	} else if (err < 0)
+	    break;
+	else {
+	    err = 0;
+	    assert(comprblk_mask == ~0ul);	/* i.e. that EXT2_COMPRBLK_FL was high. */
+	}
+    }
+
+    if ((cluster >= n_clusters) && !dirty)
+	ei->i_flags &= ~EXT2_DIRTY_FL;
+    if (!retry) {
+	ei->i_compr_flags &= ~EXT2_CLEANUP_FL;
+	ei->i_flags &= comprblk_mask;
+    }
+
+    /* We clear EXT2_CLEANUP_FL because, otherwise, we'll get
+       called again almost immediately. */
+
+    /*
+     *  The CLEANUP flag *MUST* be cleared, otherwise the iput routine
+     *  calls ext2_put_inode() again (because i_dirt is set) and there
+     *  is a loop.  The control scheme (CLEANUP + DIRTY flags) could 
+     *  probably be improved.  On the other hand, i_dirt MUST be set
+     *  because we may have sleeped, and we must force the iput routine
+     *  to look again at the i_count ...
+     */
+    /* TODO: Have a look at this cleanup scheme.  The above
+       comment sounds wrong. */
+
+    inode->i_ctime = CURRENT_TIME;
+    mark_inode_dirty_sync(inode);
+  out:
+
+#ifdef EXT2_COMPR_REPORT_MUTEX
+    printk(KERN_DEBUG "CLEANUP_UNLOCK of PID %u @ inode:%lu\n", current->pid, inode->i_ino);
+#endif
+
+//      if (!(ei->i_compr_flags & EXT2_OSYNC_INODE)) {  /* MW 5-16-07 */
+    mutex_unlock(&inode->i_mutex);
+//      }       /* MW 5-16-07 */                  
+    return err;			/* TODO: Check that ,err` is appropriate. */
+}
+
+
+int ext2_recognize_compressed(struct inode *inode, unsigned cluster)
+{
+    /* ext2_recognize_compressed(): Check tht the cluster is valid
+       in every way, and then do the EXT2_COMPRESSED_BLKADDR
+       thing. */
+    /* nyi, fixme.  All of the userspace stuff (EXT2_NOCOMPR_FL
+       etc.) needs work, so I might as well leave this.  See
+       ioctl.c for a description of what it's supposed to do. */
+    return -ENOSYS;
+}
+
+
+/* Look for compressed clusters.  If none, then clear EXT2_COMPRBLK_FL.
+
+   Called by:
+       ext2_truncate().
+       */
+void ext2_update_comprblk(struct inode *inode)
+{
+    unsigned block, last_block;
+    struct ext2_bkey key;
+    struct ext2_inode_info *ei = EXT2_I(inode);
+
+    assert(ei->i_flags & EXT2_COMPRBLK_FL);
+    if (inode->i_size == 0) {
+	ei->i_flags &= ~EXT2_COMPRBLK_FL;
+	trace_e2c("ext2_update_comprblk 1: inode: %lu removed EXT2_COMPRBLK_FL!\n", inode->i_ino);
+	return;
+    }
+    last_block = ROUNDUP_RSHIFT(inode->i_size,
+				inode->i_sb->s_blocksize_bits) - 1;
+    block = ext2_first_cluster_nblocks(inode) - 1;
+
+    assert(atomic_read(&inode->i_mutex.count) <= 0);
+
+    if (!ext2_get_key(&key, inode, block))
+	return;
+    for (;;) {
+	if (ext2_get_key_blkaddr(&key) == EXT2_COMPRESSED_BLKADDR)
+	    goto out;
+	if (block >= last_block)
+	    goto clear;
+	if (!ext2_next_key(&key, ei->i_clu_nblocks))
+	    goto out;
+	block += ei->i_clu_nblocks;
+    }
+  clear:
+    trace_e2c("ext2_update_comprblk 2: inode: %lu removed EXT2_COMPRBLK_FL!\n", inode->i_ino);
+    ei->i_flags &= ~EXT2_COMPRBLK_FL;
+  out:
+    ext2_free_key(&key);
+    assert(atomic_read(&inode->i_mutex.count) <= 0);
+
+}
--- linux-2.6.25/fs/Kconfig	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/fs/Kconfig	2008-07-31 15:13:12.000000000 +0200
@@ -68,6 +68,308 @@ config FS_XIP
 	depends on EXT2_FS_XIP
 	default y
 
+config EXT2_COMPRESS
+	bool "Ext2 file compression (DANGEROUS)"
+	depends on EXT2_FS && EXPERIMENTAL
+	select CRYPTO
+	select CRYPTO_ALGAPI
+	select CRYPTO_DEFLATE
+	select ZLIB_INFLATE
+	select ZLIB_DEFLATE
+	help
+	  Ext2 file compression allows transparent compression of files on an
+	  ext2 filesystem.  Transparent compression means that files are
+	  stored on the disk in a compressed format but they are automatically
+	  decompressed as they are read in and compressed when written out.
+	  The user is in control of how and which files are compressed, using
+	  the `chattr' utility (see chattr(1)).  For the sake of safety,
+	  administrative data (superblock, inodes, directories, etc.) are not
+	  compressed.
+
+	  Compression is very useful if you're short on disk space, and
+	  provides a better option than having lots of .gz files around.
+	  For more information, see <http://e2compr.sourceforge.net/>.
+
+	  You _need_ to have the special e2compr version of e2fsck to be able
+	  to make use of this.
+
+	  If you say Y, you will be asked which compression algorithms you wish
+	  to include.  Gzip is a good all-round algorithm, as its 1..9 parameter
+	  allows a good range of speed/compression trade-off.  Other noteworthy
+	  algorithms are LZV, which caters better to the faster/less compressing
+	  end of the scale, and bzip, which caters slightly better to the more
+	  compressing but slower end of the scale.
+
+	  Ext2 compression is still experimental, so unless you know you need
+	  it, you'd better say N.
+
+menu "Ext2 file compression options"
+	depends on EXT2_COMPRESS
+
+config EXT2_HAVE_LZO
+	tristate "LZO1X_1 algorithm"
+	depends on EXT2_COMPRESS && EXT2_FS
+	help
+	  Enable this if you want the Lempel-Ziv-Oberhumer (LZO) algorithm
+	  to be available for compression or de/&compression.  (It is actually
+	  the lzo1x_1 algorithm.)  If it is not enabled, you will not be able
+	  to read any file already stored in this format.
+
+	  This algorithm is the fastest at decompressing, but is slower than
+	  LZV1 at compressing.  LZO compresses smaller than LZV1.
+
+	  If unsure, say Y.
+
+	  If you want to compile this algorithm as a module ( = code which
+	  can be inserted in and removed from the running kernel whenever
+	  you want), say M here and read Documentation/modules.txt.  The module
+	  will be called ext2-compr-lzo.ko.  Be aware however that the default
+	  compression algorithm cannot be compiled as a module.
+
+config EXT2_HAVE_LZV1
+	tristate "LZV1 algorithm"
+	depends on EXT2_COMPRESS && EXT2_FS
+	help
+	  Enable this if you want the Lempel-Ziv-Vogt (LZV) algorithm to be
+	  available for compression or decompression.  If it is not enabled,
+	  you will not be able to read any file already stored in this format.
+
+	  This algorithm tends to be the fastest of the available algorithms
+	  (except that LZO is faster at decompressing), but tends not to
+	  compress as well as the others.  If unsure, say Y.
+
+	  If you want to compile this algorithm as a module ( = code which
+	  can be inserted in and removed from the running kernel whenever
+	  you want), say M here and read Documentation/modules.txt.  The module
+	  will be called ext2-compr-lzv1.ko.  Be aware however that the default
+	  compression algorithm cannot be compiled as a module.
+
+config EXT2_HAVE_LZRW3A
+	tristate "LZRW3A algorithm"
+	depends on EXT2_COMPRESS && EXT2_FS
+	help
+	  Enable this if you want the LZRW3A algorithm to be available for
+	  compression or decompression.  If it is not enabled, you will not be
+	  able to read any file already stored in this format.
+
+	  LZRW3A tends to be between GZIP and LZV1 in both speed and
+	  compactness.  If unsure, say Y.
+
+	  If you want to compile this algorithm as a module ( = code which
+	  can be inserted in and removed from the running kernel whenever
+	  you want), say M here and read Documentation/modules.txt.  The module
+	  will be called ext2-compr-lzrw3a.ko.  Be aware however that the
+	  default compression algorithm cannot be compiled as a module.
+
+#config EXT2_HAVE_GZIP
+	#tristate "GZIP algorithm"
+	#depends on EXT2_COMPRESS && EXT2_FS
+	#help
+	#  Enable this if you want the GZIP algorithm to be available for
+	#  compression or decompression.  If it is not enabled, you will not be
+	#  able to read any file already stored in this format.
+
+	  #This is a "good value" compression algorithm, because the 1..9
+	  #parameter allows a wide range of compression speeds and compression
+	  #amounts.  If unsure, say Y.
+
+	  #If you want to compile this algorithm as a module ( = code which
+	  #can be inserted in and removed from the running kernel whenever
+	  #you want), say M here and read Documentation/modules.txt.  The module
+	  #will be called ext2-compr-gzip.ko.  Be aware however that the
+	  #default compression algorithm cannot be compiled as a module.
+
+config EXT2_HAVE_BZIP2
+	tristate "BZIP2 algorithm"
+	depends on EXT2_COMPRESS && EXT2_FS
+	default n
+	help
+	  Enable this if you want the BZIP2 algorithm to be available for
+	  compression or decompression.  If it is not enabled, you will not be
+	  able to read any file already stored in this format.
+
+	  This algorithm is related to, but is not the same as, the algorithm
+	  used by the `bzip2' program.  Compared to gzip9, it tends to compress
+	  10-15% smaller for text files, but to about the same as gzip9 for
+	  binaries.  It is 5 times slower at compressing than gzip9, and 50%
+	  slower at decompressing.  Enabling this will enlarge your kernel by
+	  about 33kB.  If unsure, say Y.
+
+	  If you want to compile this algorithm as a module ( = code which
+	  can be inserted in and removed from the running kernel whenever
+	  you want), say M here and read Documentation/modules.txt.  The module
+	  will be called ext2-compr-bzip2.ko.  Be aware however that the
+	  default compression algorithm cannot be compiled as a module.
+
+choice
+	depends on EXT2_COMPRESS
+	prompt "Default compression algorithm"
+	default EXT2_DEFAULT_COMPR_METHOD_GZIP 
+	#default EXT2_DEFAULT_COMPR_METHOD_GZIP if EXT2_HAVE_GZIP
+	help
+	  This is the compression algorithm that is used if you do not specify
+	  any other when you set the compressed attribute of a file or
+	  directory.  The algorithm that you choose must be one of those
+	  enabled in the previous questions, and not a module.  For the
+	  relative merit of each see the help of the individual algorithms
+	  above.
+
+	  If the GZIP algorithm is eligible the default is GZIP8, which tends
+	  to produce relatively compression at the cost of being moderately
+	  slow.  Otherwise the default is deferred compression (a dummy
+	  algorithm that prepares for later compression without actually
+	  compressing anything).
+
+config EXT2_DEFAULT_COMPR_METHOD_GZIP	
+	bool "GZIP algorithm" 
+
+config EXT2_DEFAULT_COMPR_METHOD_LZO
+	bool "LZO1X_1 algorithm" if EXT2_HAVE_LZO = y
+
+config EXT2_DEFAULT_COMPR_METHOD_LZV1
+	bool "LZV1 algorithm" if EXT2_HAVE_LZV1 = y
+
+config EXT2_DEFAULT_COMPR_METHOD_LZRW3A
+	bool "LZRW3A algorithm" if EXT2_HAVE_LZRW3A = y
+
+config EXT2_DEFAULT_COMPR_METHOD_BZIP2
+	bool "BZIP2 algorithm" if EXT2_HAVE_BZIP2 = y
+	
+config EXT2_DEFAULT_COMPR_METHOD_DEFER
+	bool "None (defer compression)"
+
+
+endchoice
+
+choice
+	#depends on EXT2_DEFAULT_COMPR_METHOD_GZIP
+	prompt "Gzip parameter for default compression method"
+	default EXT2_DEFAULT_COMPR_METHOD_GZIP8
+	help
+	  You have selected `gzip' as your default compression algorithm, but
+	  I need to know whether to use `gzip -1', `gzip -9', or somewhere
+	  in between.  gzip1 is the least compressing but fastest; gzip9 is the
+	  most compressing and slowest; and the numbers in between have
+	  characteristics in between (though not on a linear scale).
+	  If unsure, say `8'.
+
+config EXT2_DEFAULT_COMPR_METHOD_GZIP1
+	bool "1"
+config EXT2_DEFAULT_COMPR_METHOD_GZIP2
+	bool "2"
+config EXT2_DEFAULT_COMPR_METHOD_GZIP3
+	bool "3"
+config EXT2_DEFAULT_COMPR_METHOD_GZIP4
+	bool "4"
+config EXT2_DEFAULT_COMPR_METHOD_GZIP5
+	bool "5"
+config EXT2_DEFAULT_COMPR_METHOD_GZIP6
+	bool "6"
+config EXT2_DEFAULT_COMPR_METHOD_GZIP7
+	bool "7"
+config EXT2_DEFAULT_COMPR_METHOD_GZIP8
+	bool "8"
+config EXT2_DEFAULT_COMPR_METHOD_GZIP9
+	bool "9"
+
+endchoice
+	
+
+config EXT2_SEPARATE_WORK_AREAS
+	bool "Extra work area for compression"
+	depends on EXT2_COMPRESS
+	default y
+	help
+	  This is an experimental feature aimed at improving response times in
+	  the case where most of your binaries are compressed and you are
+	  relatively short of memory (so there's lots of paging), and there is
+	  some compression going on.  Without enabling this option in this
+	  case, none of the compressed binaries are able to page in until the
+	  (slow) compression has finished.
+
+	  By enabling this option, compression and decompression happen in
+	  separate work areas.  Since decompression is generally faster than
+	  compression, this results in a shorter wait and hopefully better
+	  response times.  The downside is that the extra work area costs about
+	  300KB of kernel memory, which of course tends to _increase_ response
+	  times, and decrease throughput.  (In future releases, the memory cost
+	  can be decreased.)
+
+	  If you wish to compress most of your binaries, and response time is
+	  important to you, and you have enough memory that 300KB will not be
+	  missed much, then say `y'.  That said, I'd encourage most existing
+	  e2compr users to say `y' for now, and tell me whether you notice a
+	  difference.
+
+config GZ_HACK
+	bool "Exclude .gz files from automatic compression"
+	depends on EXT2_COMPRESS
+	default y
+	help
+	  If you say Y here, then files created with names ending in `.gz' or
+	  `.?gz' or `.bz2' don't inherit the `c' ("compress") attribute from
+	  their parent directory.  (However, you can still do `chattr +c FILE'
+	  if you want to try to compress it anyway.)  This means that you
+	  don't waste CPU time trying to compress a file that probably can't
+	  be compressed.  See fs/ext2/namei.c if you want to add other rules.
+	  If you have any aesthetic sensibilities then you will say N here
+	  and try to implement something better.  Most people will say Y here.
+
+config EXT2_VERIFY_COMPRESSION
+	bool "Verify compression"
+	depends on EXT2_COMPRESS
+	default n
+	help
+	  Every time we compress a cluster, immediately decompress the data
+	  and compare with original.  This takes a bit more time, but is
+	  probably worth it for the safety.  Remember, a rare bug was
+	  discovered in the gzip algorithm after it had been in world-wide use
+	  for years.  If unsure, say Y.
+	  
+
+config EXT2_COMPR_X86_CODE
+	bool "Assembly versions of compression routines [dangerous]"
+	depends on EXT2_COMPRESS && X86
+	default n
+	help
+	  For x86 kernels there are some assembler language versions of the
+	  compression routines for the lzv1 algorithms. These apparently 
+	  run "much faster" than the C ones. For newer CPUs this code might
+	  not work. If unsure, say N.
+	  
+
+choice
+	depends on EXT2_COMPRESS
+        prompt "Default cluster size (in blocks, usually 1KB each)"
+        default EXT2_DEFAULT_CLUSTER_BITS_5
+	help
+	  To make random access to compressed files reasonably fast the files
+	  are compressed in clusters.  By default, the clusters will be of the
+	  size defined here but there is a modified version of the chattr
+	  utility that can set the cluster size for each file independently.
+	  Large clusters usually result in better compression at the cost of
+	  being slower.
+
+	  Note that the answer to this question is specified in filesystem
+	  blocks rather than in kilobytes, though most filesystems have 1KB
+	  blocks anyway.  (If you have a filesystem with large blocks then
+	  you should know it, but if you want to check then "tune2fs -l
+	  /dev/xxx | grep size".)  The default is 32 blocks which is the
+	  slowest setting but gives the best compression.
+
+config EXT2_DEFAULT_CLUSTER_BITS_2
+	bool "4"
+config EXT2_DEFAULT_CLUSTER_BITS_3
+	bool "8"
+config EXT2_DEFAULT_CLUSTER_BITS_4
+	bool "16"
+config EXT2_DEFAULT_CLUSTER_BITS_5
+	bool "32"
+
+endchoice
+
+endmenu
+
 config EXT3_FS
 	tristate "Ext3 journalling file system support"
 	select JBD
--- linux-2.6.25/include/linux/mm.h	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/include/linux/mm.h	2008-07-31 15:13:12.000000000 +0200
@@ -381,6 +381,10 @@ static inline void set_compound_order(st
  *   back into memory.
  */
 
+extern wait_queue_head_t *page_waitqueue(struct page *page);
+//extern ssize_t generic_file_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
+//	loff_t offset, unsigned long nr_segs);
+
 /*
  * The zone field is never updated after free_area_init_core()
  * sets it, so none of the operations on it need to be atomic.
--- linux-2.6.25/include/linux/pagemap.h	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/include/linux/pagemap.h	2008-07-31 15:13:12.000000000 +0200
@@ -12,6 +12,8 @@
 #include <asm/uaccess.h>
 #include <linux/gfp.h>
 #include <linux/bitops.h>
+#include <linux/pagevec.h>
+
 
 /*
  * Bits in mapping->flags.  The lower __GFP_BITS_SHIFT bits are the page
--- linux-2.6.25/include/linux/ext2_fs_c.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.25-e2c-455/include/linux/ext2_fs_c.h	2008-07-31 15:13:12.000000000 +0200
@@ -0,0 +1,490 @@
+/*
+ *  Copyright (C) 2001 Alcatel Business Systems - R&D Illkirch
+ *  	(transparent compression code)
+ *  Pierre Peiffer (pierre.peiffer@sxb.bsf.alcatel.fr) - Denis Richard (denis.richard@sxb.bsf.alcatel.fr)
+ *  Adapted from patch e2compr-0.4.39-patch-2.2.18 .
+ */
+
+#ifndef EXT2_FS_C_H
+#define EXT2_FS_C_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/ext2_fs.h>
+#include "../../fs/ext2/ext2.h"
+
+#define EXT2_IOC_GETCLUSTERSIZE 	_IOR('c', 0, long)
+#define EXT2_IOC_SETCLUSTERSIZE 	_IOW('c', 0, long)
+#define EXT2_IOC_GETCOMPRMETHOD 	_IOR('c', 1, long)
+#define EXT2_IOC_SETCOMPRMETHOD 	_IOW('c', 1, long)
+#define EXT2_IOC_GETFIRSTCLUSTERSIZE 	_IOR('c', 2, long)
+#define EXT2_IOC_RECOGNIZE_COMPRESSED	_IOW('c', 2, long)
+#define EXT2_IOC_GETCLUSTERBIT		_IOR('c', 3, long)
+#define EXT2_IOC_GETCOMPRRATIO		_IOR('c', 4, long)
+/* Don't use _IOW('c', {5,6}, long), as these are used by old
+   e2compress binaries as SETCLUSTERBIT and CLRCLUSTERBIT
+   respectively. */
+
+/* EXT2_xxxx_ALG is an index into ext2_algorithm_table[] defined in
+   fs/ext2/compress.c. */
+/* N.B. Don't change these without also changing the table in
+   compress.c.  Be careful not to break binary compatibility.
+   (EXT2_NONE_ALG and EXT2_UNDEF_ALG are safe from binary
+   compatibility problems, though, so they can safely be renumbered --
+   and indeed probably should be if you do add another algorithm.) */
+#define EXT2_LZV1_ALG 0
+#define EXT2_LZRW3A_ALG 1
+#define EXT2_GZIP_ALG 2
+#define EXT2_BZIP2_ALG 3
+#define EXT2_LZO_ALG 4
+#define EXT2_NONE_ALG 5
+#define EXT2_UNDEF_ALG 6
+#define EXT2_N_ALGORITHMS 5 /* Count of "real" algorithms.  Excludes
+			       `none' and `undef'. */
+
+/* EXT2_xxxx_METH is an index into ext2_method_table[] defined in
+   fs/ext2/compress.c. */
+/* N.B. Don't change these without also changing the table in
+   compress.c. */
+#define EXT2_LZV1_METH 0
+#define EXT2_AUTO_METH 1
+#define EXT2_DEFER_METH 2
+#define EXT2_NEVER_METH 3
+#define EXT2_BZIP2_METH 4
+#define EXT2_LZRW3A_METH 8
+#define EXT2_LZO1X_1_METH 10
+#define EXT2_GZIP_1_METH 16
+#define EXT2_GZIP_2_METH 17
+#define EXT2_GZIP_3_METH 18
+#define EXT2_GZIP_4_METH 19
+#define EXT2_GZIP_5_METH 20
+#define EXT2_GZIP_6_METH 21
+#define EXT2_GZIP_7_METH 22
+#define EXT2_GZIP_8_METH 23
+#define EXT2_GZIP_9_METH 24
+
+#define EXT2_N_METHODS 32 /* Don't change this unless you know what
+			      you're doing.  In particular, it's tied
+			      to the width of the algorithm field
+			      in i_flags.*/
+
+#define EXT2_MAX_CLUSTER_BYTES		(32*1024)
+#define EXT2_LOG2_MAX_CLUSTER_BYTES	(5 + 10)
+
+#define EXT2_COMPRESS_MAGIC_04X	0x9ec7
+#define EXT2_MAX_CLUSTER_BLOCKS	32
+#define EXT2_MAX_CLUSTER_PAGES		EXT2_MAX_CLUSTER_BYTES >> PAGE_CACHE_SHIFT
+#define EXT2_ECOMPR			EIO
+/* A cluster is considered compressed iff the block number for the
+   last block of that cluster is EXT2_COMPRESSED_BLKADDR.  If this
+   changes then check if there's anywhere that needs a cpu_to_le32()
+   conversion. */
+#define EXT2_COMPRESSED_BLKADDR	0xffffffff 
+
+/* I like these names better. */
+#define EXT2_MAX_CLU_NBYTES EXT2_MAX_CLUSTER_BYTES
+#define EXT2_LOG2_MAX_CLU_NBYTES EXT2_LOG2_MAX_CLUSTER_BYTES
+#define EXT2_MAX_CLU_NBLOCKS EXT2_MAX_CLUSTER_BLOCKS
+
+
+#ifndef __KERNEL__
+
+/* Cluster head on disk, for e2compr versions before 0.4.0.  I'm
+   leaving this here so tht as I may make e2compress able to read
+   old-style e2compr files. */
+struct ext2_cluster_head_03x {
+  __u16 magic;			/* == EXT2_COMPRESS_MAGIC_03X */
+  __u16 len;			/* size of uncompressed data */
+  __u16 compr_len;		/* size of compressed data */
+  __u8  method;			/* compress method */
+  __u8  reserved_0;
+  __u32 bitmap;			/* block bitmap */
+  __u32 reserved_2;		/* 0 or adler32 checksum of
+				   _compressed_ data */
+};
+# define EXT2_COMPRESS_MAGIC_03X	0x8ec7 /* Head magic number
+						  for e2compr versions
+						  before 0.4.0. */
+#endif /* !__KERNEL__ */
+
+
+#ifdef __KERNEL__
+# ifdef  CONFIG_EXT2_COMPRESS
+
+//mw
+#define CONFIG_EXT2_HAVE_GZIP
+
+/* If defined, compress each cluster as soon as we get to the end of a
+   whole cluster, when writing.  (If undefined, we wait until
+   ext2_release_file() or the like.) */
+#define EXT2_COMPRESS_WHEN_CLU
+
+/* Some working area details are yet to be worked out, so I'm using this
+   set of wrappers. */
+/* # define ext2_rd_wa (ext2_wa[0]) */
+#  define ext2_rd_wa_lock (ext2_wa_lock[0])
+#  define ext2_lock_rd_wa() ext2_lock_wa(0)
+#  define ext2_unlock_rd_wa() ext2_unlock_wa(0)
+#  define ext2_rd_wa_ix 0
+#  ifdef CONFIG_EXT2_SEPARATE_WORK_AREAS
+#   define EXT2_N_WA 2
+#   define ext2_wr_wa_ix 1
+#   define ext2_wr_wa_lock (ext2_wa_lock[1])
+#   define ext2_lock_wr_wa() ext2_lock_wa(1)
+#   define ext2_unlock_wr_wa() ext2_unlock_wa(1)
+#  else
+#   define EXT2_N_WA 1
+#   define ext2_wr_wa_ix 0
+#   define ext2_wr_wa_lock (ext2_wa_lock[0])
+#   define ext2_lock_wr_wa() ext2_lock_wa(0)
+#   define ext2_unlock_wr_wa() ext2_unlock_wa(0)
+#  endif
+
+#  ifdef CONFIG_EXT2_DEFAULT_COMPR_METHOD_DEFER
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_DEFER_METH
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_BZIP2)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_BZIP2_METH
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_LZO1X_1_ME
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_LZO)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_LZO1X_1_METH
+#   ifndef CONFIG_EXT2_HAVE_LZO
+#    error "Default algorithm (lzo) is not compiled in."
+#   endif
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_LZV1)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_LZV1_METH
+#   ifndef CONFIG_EXT2_HAVE_LZV1
+#    error "Default algorithm (lzv1) is not compiled in."
+#   endif
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_LZRW3A)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_LZRW3A_METH
+#   ifndef CONFIG_EXT2_HAVE_LZRW3A
+#    error "Default algorithm (lzrw3a) is not compiled in."
+#   endif
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_GZIP1)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_GZIP_1_METH
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_GZIP2)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_GZIP_2_METH
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_GZIP3)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_GZIP_3_METH
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_GZIP4)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_GZIP_4_METH
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_GZIP5)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_GZIP_5_METH
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_GZIP6)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_GZIP_6_METH
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_GZIP7)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_GZIP_7_METH
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_GZIP8)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_GZIP_8_METH
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_GZIP9)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_GZIP_9_METH
+#  elif defined (CONFIG_EXT2_DEFAULT_COMPR_METHOD_BZIP2)
+#   define EXT2_DEFAULT_COMPR_METHOD	EXT2_BZIP2_METH
+#   ifndef CONFIG_EXT2_HAVE_BZIP2
+#    error "Default algorithm (bzip2) is not compiled in."
+#   endif
+#  else
+#   error "No default compression algorithm."
+#  endif
+#  if EXT2_DEFAULT_COMPR_METHOD >= EXT2_GZIP_1_METH && EXT2_DEFAULT_COMPR_METHOD <= EXT2_GZIP_9_METH
+#   ifndef CONFIG_EXT2_HAVE_GZIP
+#    error "Default algorithm (gzip) is not compiled in."
+#   endif
+#  endif
+
+#  if defined (CONFIG_EXT2_DEFAULT_CLUSTER_BITS_2)
+#   define EXT2_DEFAULT_LOG2_CLU_NBLOCKS	2
+#  elif defined (CONFIG_EXT2_DEFAULT_CLUSTER_BITS_3)
+#   define EXT2_DEFAULT_LOG2_CLU_NBLOCKS	3
+#  elif defined (CONFIG_EXT2_DEFAULT_CLUSTER_BITS_4)
+#   define EXT2_DEFAULT_LOG2_CLU_NBLOCKS	4
+#  elif defined (CONFIG_EXT2_DEFAULT_CLUSTER_BITS_5)
+#   define EXT2_DEFAULT_LOG2_CLU_NBLOCKS	5
+#  else
+#   error "No default cluster size."
+#  endif
+
+#  define EXT2_DEFAULT_CLU_NBLOCKS	(1 << EXT2_DEFAULT_LOG2_CLU_NBLOCKS)
+
+#  if (EXT2_LZV1_ALG != 0) || (EXT2_BZIP2_ALG != 3) || (EXT2_LZO_ALG != 4) || (EXT2_N_ALGORITHMS != 5)
+#   error "this code needs changing; but then, you shouldn't be messing with algorithm ids anyway unless you are very careful to protect disk format compatibility"
+#  endif
+#  ifdef CONFIG_EXT2_HAVE_LZV1
+#   define _ext2_lzv1_builtin (1 << EXT2_LZV1_ALG)
+#  else
+#   define _ext2_lzv1_builtin 0
+#  endif
+#  ifdef CONFIG_EXT2_HAVE_LZRW3A
+#   define _ext2_lzrw3a_builtin (1 << EXT2_LZRW3A_ALG)
+#  else
+#   define _ext2_lzrw3a_builtin 0
+#  endif
+#  ifdef CONFIG_EXT2_HAVE_GZIP
+#   define _ext2_gzip_builtin (1 << EXT2_GZIP_ALG)
+#  else
+#   define _ext2_gzip_builtin 0
+#  endif
+#  ifdef CONFIG_EXT2_HAVE_BZIP2
+#   define _ext2_bzip2_builtin (1 << EXT2_BZIP2_ALG)
+#  else
+#   define _ext2_bzip2_builtin 0
+#  endif
+#  ifdef CONFIG_EXT2_HAVE_LZO
+#   define _ext2_lzo_builtin (1 << EXT2_LZO_ALG)
+#  else
+#   define _ext2_lzo_builtin 0
+#  endif
+
+#  ifdef CONFIG_EXT2_HAVE_LZV1_MODULE
+#   define _ext2_lzv1_module (1 << EXT2_LZV1_ALG)
+#  else
+#   define _ext2_lzv1_module 0
+#  endif
+#  ifdef CONFIG_EXT2_HAVE_LZRW3A_MODULE
+#   define _ext2_lzrw3a_module (1 << EXT2_LZRW3A_ALG)
+#  else
+#   define _ext2_lzrw3a_module 0
+#  endif
+#  ifdef CONFIG_EXT2_HAVE_GZIP_MODULE
+#   define _ext2_gzip_module (1 << EXT2_GZIP_ALG)
+#  else
+#   define _ext2_gzip_module 0
+#  endif
+#  ifdef CONFIG_EXT2_HAVE_BZIP2_MODULE
+#   define _ext2_bzip2_module (1 << EXT2_BZIP2_ALG)
+#  else
+#   define _ext2_bzip2_module 0
+#  endif
+#  ifdef CONFIG_EXT2_HAVE_LZO_MODULE
+#   define _ext2_lzo_module (1 << EXT2_LZO_ALG)
+#  else
+#   define _ext2_lzo_module 0
+#  endif
+
+#  define EXT2_ALGORITHMS_MODULE  (_ext2_lzv1_module | _ext2_lzrw3a_module | _ext2_gzip_module | _ext2_bzip2_module | _ext2_lzo_module)
+#  define EXT2_ALGORITHMS_BUILTIN  (_ext2_lzv1_builtin | _ext2_lzrw3a_builtin | _ext2_gzip_builtin | _ext2_bzip2_builtin | _ext2_lzo_builtin)
+
+#  if EXT2_ALGORITHMS_MODULE & EXT2_ALGORITHMS_BUILTIN
+#   error "Arithmetic error?  Some algorithm appears to be both built-in and a module."
+#  endif
+
+/* EXT2_ALGORITHMS_SUPP is what we test when mounting a filesystem.
+   See fs/ext2/super.c. */
+#  define EXT2_ALGORITHMS_SUPP (EXT2_ALGORITHMS_MODULE | EXT2_ALGORITHMS_BUILTIN)
+#  if EXT2_ALGORITHMS_SUPP == 0
+#   error "You must select at least one compression algorithm."
+#  endif
+
+/* Cluster head on disk.  Little-endian. */
+struct ext2_cluster_head {
+  __u16 magic;		/* == EXT2_COMPRESS_MAGIC_04X. */
+  __u8  method;		/* compression method id. */
+  __u8  holemap_nbytes;	/* length of holemap[] array */
+  __u32 checksum;	/* adler32 checksum.  Checksum covers all fields
+			   below this one, and the compressed data. */
+  __u32 ulen;		/* size of uncompressed data */
+  __u32 clen;		/* size of compressed data (excluding cluster head) */
+  __u8  holemap[0];     /* bitmap describing where to put holes. */
+};
+
+
+
+struct ext2_wa_S {
+  __u8 u[EXT2_MAX_CLUSTER_BYTES];  /* Uncompressed data. */
+  __u8 c[EXT2_MAX_CLUSTER_BYTES];  /* Compressed data. */
+  __u8 heap[1];  /* Heap: working space for de/compression routines. */
+};
+extern struct ext2_wa_S *ext2_rd_wa;
+extern size_t ext2_rd_wa_size;
+#  ifdef CONFIG_EXT2_SEPARATE_WORK_AREAS
+extern struct ext2_wa_S *ext2_wr_wa;
+extern size_t ext2_wr_wa_size;
+#  else
+#   define ext2_wr_wa ext2_rd_wa
+#   define ext2_wr_wa_size ext2_rd_wa_size
+#  endif
+
+#  define EXT2_CLEANUP_FL		0x40 /* See Readme.e2compr */
+#  define EXT2_OSYNC_INODE		0x20 /* sync of inode running	*/
+#  define ROUNDUP_DIV(_n, _d) ((_n) ? 1 + (((_n) - 1) / (_d)) : 0)
+#  define ROUNDUP_RSHIFT(_n, _b) ((_n) ? 1 + (((_n) - 1) >> (_b)) : 0)
+
+#  if defined(EXT2_NDIR_BLOCKS) && (EXT2_NDIR_BLOCKS != 12)
+#   error "e2compr currently assumes that EXT2_NDIR_BLOCKS is 12."
+/* If EXT2_NDIR_BLOCKS changes then change the definitions of
+   ext2_first_cluster_nblocks() and friends, and search the patch for
+   anywhere where 12 is hard-coded.  (At the time of writing, it's
+   only hard-coded in ext2_first_cluster_nblocks().)  What we want to
+   achieve is for clusters not to straddle address blocks.  Apart from
+   performance, some code in compress.c (search for `straddle')
+   assumes this. */
+#  endif
+
+#  include <linux/fs.h>
+
+#  define EXT2_ALG_INIT_COMPRESS  	1
+#  define EXT2_ALG_INIT_DECOMPRESS	2
+
+extern int    ext2_get_cluster_pages (struct inode*, u32, struct page**, struct page *, int);
+extern int    ext2_get_cluster_extra_pages (struct inode*, u32, struct page**, struct page**);
+extern int    ext2_kmap_cluster_pages   (struct page *, struct page**, struct page**);
+extern int    ext2_kunmap_cluster_pages (struct page *, struct page**, struct page**);
+extern int    ext2_get_cluster_blocks (struct inode*, u32, struct buffer_head**, struct page**, struct page**, int);
+extern int    ext2_decompress_cluster (struct inode*, u32);
+extern int    ext2_decompress_pages(struct inode*, u32, struct page**);
+extern int    ext2_compress_cluster (struct inode*, u32);
+extern int    ext2_decompress_inode (struct inode*);
+extern int    ext2_cleanup_compressed_inode (struct inode*);
+extern void   ext2_update_comprblk (struct inode *);
+extern int    ext2_get_dcount(struct inode *inode);
+
+extern void   ext2_lock_rd_wa_uninterruptible(void);
+extern int    ext2_lock_wa (unsigned);
+extern void   ext2_unlock_wa (unsigned);
+
+//extern size_t ext2_decompress_blocks    (struct inode*, struct buffer_head**, int, size_t);
+extern size_t ext2_decompress_blocks    (struct inode*, struct buffer_head**, int, size_t, u32 cluster);
+extern int    ext2_count_blocks		(struct inode*);
+extern int    ext2_recognize_compressed (struct inode *, unsigned cluster);
+extern unsigned long ext2_adler32	(unsigned long, unsigned char*, int);
+
+extern size_t ext2_iLZV1   (int);
+extern size_t ext2_iLZV2   (int);
+extern size_t ext2_iNONE   (int);
+extern size_t ext2_iGZIP   (int);
+extern size_t ext2_iBZIP2  (int);
+extern size_t ext2_iLZO    (int);
+extern size_t ext2_iLZRW3A (int);
+// Andreas 20.4.2007
+extern size_t ext2_iZLIB   (int);
+
+extern size_t ext2_wLZV1   (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_wLZV2   (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_wNONE   (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_wGZIP   (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_wBZIP2  (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_wLZO    (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_wLZRW3A (__u8*, __u8*, void*, size_t, size_t, int);
+// Andreas
+extern size_t ext2_wZLIB   (__u8*, __u8*, void*, size_t, size_t, int);
+
+extern size_t ext2_rLZV1   (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_rLZV2   (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_rNONE   (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_rGZIP   (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_rBZIP2  (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_rLZO    (__u8*, __u8*, void*, size_t, size_t, int);
+extern size_t ext2_rLZRW3A (__u8*, __u8*, void*, size_t, size_t, int);
+// Andreas
+extern size_t ext2_rZLIB   (__u8*, __u8*, void*, size_t, size_t, int);
+
+struct ext2_algorithm { 
+	char	*name; 
+	int	avail;
+	size_t (*init)		(int); 
+	size_t (*compress)	(__u8*, __u8*, void*, size_t, size_t, int); 
+	size_t (*decompress)	(__u8*, __u8*, void*, size_t, size_t, int);
+};
+
+struct ext2_method {
+	unsigned  alg;
+	int	  xarg;
+};
+
+extern int ext2_register_compression_module(unsigned alg,
+				size_t compress_memory,
+				size_t decompress_memory,
+		                struct ext2_algorithm *new_algorithm);
+extern void ext2_unregister_compression_module(unsigned alg);
+
+//mw: 
+//i_clu_nblocks: 			Blocks / Cluster	here: 32, = 8 Pages
+//PAGE_CACHE_SHIFT: 		Bits for one Page	here: 12, = 4096 Byte PageSize
+//i_sb->s_blocksize_bits: 	Bits for one Block 	here: 10, = 1024 Byte BlockSize
+//i_sb->s_blocksize: 		Blocksize 			here: 1024 Byte 
+	
+
+#  define ext2_first_cluster_nblocks(_i) ((EXT2_I(_i))->i_clu_nblocks > 4 && (_i)->i_sb->s_blocksize < 4096 ? 12 : 4)
+#  define ext2_block_to_cluster(_i,_b)	((_b) < ext2_first_cluster_nblocks(_i) ? 0 : (((_b) - ext2_first_cluster_nblocks(_i)) >> (EXT2_I(_i))->i_log2_clu_nblocks) + 1)
+#  define ext2_offset_to_cluster(_i,_o)	ext2_block_to_cluster((_i), ((_o) >> (_i)->i_sb->s_blocksize_bits))
+#  define ext2_n_clusters(_i)	((_i)->i_size ? ext2_offset_to_cluster((_i), (_i)->i_size - 1) + 1 : 0)
+#  define ext2_cluster_block0(_i,_c)	((_c) ? ext2_first_cluster_nblocks(_i) + (((_c) - 1) << (EXT2_I(_i))->i_log2_clu_nblocks) : 0)
+#  define ext2_cluster_nblocks(_i,_c)	((_c) ? (EXT2_I(_i))->i_clu_nblocks : ext2_first_cluster_nblocks(_i))
+#  define ext2_cluster_offset(_i,_c)	((_c) ? ext2_cluster_block0((_i), (_c)) << (_i)->i_sb->s_blocksize_bits : 0)
+
+#  define ext2_first_cluster_npages(_i) ((EXT2_I(_i))->i_clu_nblocks > 4 && (_i)->i_sb->s_blocksize < 4096 ? 12 >> (PAGE_CACHE_SHIFT - (_i)->i_sb->s_blocksize_bits) : 4 >> (PAGE_CACHE_SHIFT - (_i)->i_sb->s_blocksize_bits))
+#  define ext2_page_to_cluster(_i,_p)	((_p) < ext2_first_cluster_npages(_i) ? 0 : (((_p) - ext2_first_cluster_npages(_i)) >> (((EXT2_I(_i))->i_log2_clu_nblocks)+(_i)->i_sb->s_blocksize_bits-PAGE_CACHE_SHIFT)) + 1)
+#  define ext2_cluster_page0(_i,_c)	((_c) ? ext2_cluster_block0(_i, _c) >> (PAGE_CACHE_SHIFT - (_i)->i_sb->s_blocksize_bits) : 0)
+#  define ext2_cluster_npages(_i,_c)	((_c) ? (EXT2_I(_i))->i_clu_nblocks >> (PAGE_CACHE_SHIFT - (_i)->i_sb->s_blocksize_bits) : ext2_first_cluster_npages(_i))
+
+extern inline int
+ext2_offset_is_clu_boundary(struct inode *inode, u32 off)
+{
+	if (off & (inode->i_sb->s_blocksize - 1))
+		return 0;
+	if (off == 0)
+		return 1;
+	off >>= inode->i_sb->s_blocksize_bits;
+	if (off < ext2_first_cluster_nblocks(inode))
+		return 0;
+	off -= ext2_first_cluster_nblocks(inode);
+	return !(off & (EXT2_I(inode)->i_clu_nblocks - 1));
+}
+
+struct ext2_wa_contents_S {
+	ino_t ino;
+	dev_t dev;
+	unsigned cluster;
+};
+extern struct ext2_wa_contents_S ext2_rd_wa_ucontents;
+#  ifdef CONFIG_EXT2_SEPARATE_WORK_AREAS
+extern struct ext2_wa_contents_S ext2_wr_wa_ucontents;
+#  else
+#   define ext2_wr_wa_ucontents ext2_rd_wa_ucontents
+#  endif
+
+extern struct ext2_algorithm ext2_algorithm_table[];
+extern struct ext2_method ext2_method_table[];
+
+/* Both of these return -errno if error, 0 if not compressed, positive
+   if compressed.  (You should use the macro unless you've already
+   tested COMPRBLK.) */
+extern int ext2_cluster_is_compressed_fn (struct inode *inode, __u32 cluster);
+extern inline int ext2_cluster_is_compressed (struct inode *inode, __u32 cluster)
+{
+	if ((EXT2_I(inode)->i_flags & EXT2_COMPRBLK_FL) == 0)
+		return 0;
+	return ext2_cluster_is_compressed_fn (inode, cluster);
+}
+extern unsigned ext2_calc_free_ix (unsigned , u8 const *, unsigned );
+extern int ext2_unpack_blkaddrs(struct inode *, struct buffer_head **, int, unsigned , u8 const *, unsigned , unsigned , unsigned , unsigned );
+
+#  define HOLE_BLKADDR(_b) \
+	(((_b) == 0) \
+	 || ((_b) == EXT2_COMPRESSED_BLKADDR))
+# else /* !CONFIG_EXT2_COMPRESS */
+#  define HOLE_BLKADDR(_b) ((_b) == 0)
+# endif
+
+/* For some reason or other, I see code like `if (le32_to_cpu(tmp) !=
+   0)' around in the kernel.  So far I haven't checked whether or not
+   the compiler knows that the swab can be dropped. */
+# if defined(EXT2_COMPRESSED_BLKADDR) && EXT2_COMPRESSED_BLKADDR != 0xffffffff
+/* This may be a false positive; the "correct" test would be `if
+   defined(CONFIG_EXT2_COMPRESS)', but if this test does succeed, then
+   there is at least cause to have a look around. */
+#  error "Next bit of code is wrong."
+# endif
+
+# define HOLE_BLKADDR_SWAB32(_b) HOLE_BLKADDR(_b)
+
+#ifdef EXT2_COMPR_REPORT
+#define trace_e2c(format, args...) printk(KERN_DEBUG format, ## args)
+#else
+#define trace_e2c(format, args...) do {} while(0)
+#endif
+
+#endif /* __KERNEL__ */
+
+
+#endif /* EXT2_FS_C_H */
--- linux-2.6.25/include/linux/ext2_fs.h	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/include/linux/ext2_fs.h	2008-07-31 15:13:12.000000000 +0200
@@ -24,6 +24,14 @@
  */
 
 /*
+ *  Copyright (C) 2001 Alcatel Business Systems - R&D Illkirch
+ *  Added by Pierre Peiffer (pierre.peiffer@sxb.bsf.alcatel.fr) (2001-08-09)
+ *  (Copied from pjm e2compr-0.4.39-patch-2.2.18 patch)
+ *      e2compress stuff.
+ */
+
+
+/*
  * Define EXT2FS_DEBUG to produce debug messages
  */
 #undef EXT2FS_DEBUG
@@ -61,11 +69,19 @@
 #define EXT2_ROOT_INO		 2	/* Root inode */
 #define EXT2_BOOT_LOADER_INO	 5	/* Boot loader inode */
 #define EXT2_UNDEL_DIR_INO	 6	/* Undelete directory inode */
+#if 0 /* Neither of these are used yet.  They might never be. */
+/* There are two copies of the ext2_compress_ino because
+   one is a backup.  The data is stored in compressed form,
+   and we don't want to keep all our eggs in the one basket. */
+# define EXT2_COMPRESS1_INO	 7	/* Extra inode info (copy 1). */
+# define EXT2_COMPRESS2_INO	 8	/* Extra inode info (copy 2). */
+#endif
 
 /* First non-reserved inode for old ext2 filesystems */
 #define EXT2_GOOD_OLD_FIRST_INO	11
 
 #ifdef __KERNEL__
+#include <linux/fs.h>
 #include <linux/ext2_fs_sb.h>
 static inline struct ext2_sb_info *EXT2_SB(struct super_block *sb)
 {
@@ -86,6 +102,10 @@ static inline struct ext2_sb_info *EXT2_
 /*
  * Macro-instructions used to manage several block sizes
  */
+#define EXT2_GRAIN_SIZE 		1024
+/* Minimum allocation unit.  This is used in fs/ext2/compress.c to
+   check compr_len validity wrt (uncompressed) len.  This definition
+   will probably need to be changed when fragments are implemented. */
 #define EXT2_MIN_BLOCK_SIZE		1024
 #define	EXT2_MAX_BLOCK_SIZE		4096
 #define EXT2_MIN_BLOCK_LOG_SIZE		  10
@@ -168,6 +188,9 @@ struct ext2_group_desc
 /*
  * Inode flags (GETFLAGS/SETFLAGS)
  */
+/* Note: if you overload any of these flags then you may need to change
+   code in ext2_read_inode() and ext2_update_inode(). */
+
 #define	EXT2_SECRM_FL			FS_SECRM_FL	/* Secure deletion */
 #define	EXT2_UNRM_FL			FS_UNRM_FL	/* Undelete */
 #define	EXT2_COMPR_FL			FS_COMPR_FL	/* Compress file */
@@ -177,9 +200,10 @@ struct ext2_group_desc
 #define EXT2_NODUMP_FL			FS_NODUMP_FL	/* do not dump file */
 #define EXT2_NOATIME_FL			FS_NOATIME_FL	/* do not update atime */
 /* Reserved for compression usage... */
-#define EXT2_DIRTY_FL			FS_DIRTY_FL
+#define EXT2_DIRTY_FL			FS_DIRTY_FL     /* Needs compressing; see Readme.e2compr */
 #define EXT2_COMPRBLK_FL		FS_COMPRBLK_FL	/* One or more compressed clusters */
 #define EXT2_NOCOMP_FL			FS_NOCOMP_FL	/* Don't compress */
+#define EXT2_NOCOMPR_FL                 FS_NOCOMP_FL    /* Access raw data */
 #define EXT2_ECOMPR_FL			FS_ECOMPR_FL	/* Compression error */
 /* End compression flags --- maybe not all used */	
 #define EXT2_BTREE_FL			FS_BTREE_FL	/* btree format dir */
@@ -194,6 +218,7 @@ struct ext2_group_desc
 #define EXT2_FL_USER_VISIBLE		FS_FL_USER_VISIBLE	/* User visible flags */
 #define EXT2_FL_USER_MODIFIABLE		FS_FL_USER_MODIFIABLE	/* User modifiable flags */
 
+#include <linux/ioctl.h>
 /*
  * ioctl commands
  */
@@ -317,6 +342,7 @@ struct ext2_inode {
 #define EXT2_MOUNT_MINIX_DF		0x000080  /* Mimics the Minix statfs */
 #define EXT2_MOUNT_NOBH			0x000100  /* No buffer_heads */
 #define EXT2_MOUNT_NO_UID32		0x000200  /* Disable 32-bit UIDs */
+#define EXT2_MOUNT_FORCE_COMPAT		0x0400  /* Mount despite incompatibilities */
 #define EXT2_MOUNT_XATTR_USER		0x004000  /* Extended user attributes */
 #define EXT2_MOUNT_POSIX_ACL		0x008000  /* POSIX Access Control Lists */
 #define EXT2_MOUNT_XIP			0x010000  /* Execute in place */
@@ -482,8 +508,14 @@ struct ext2_super_block {
 #define EXT2_FEATURE_INCOMPAT_ANY		0xffffffff
 
 #define EXT2_FEATURE_COMPAT_SUPP	EXT2_FEATURE_COMPAT_EXT_ATTR
-#define EXT2_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE| \
+#ifdef CONFIG_EXT2_COMPRESS
+# define EXT2_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_COMPRESSION| \
+					 EXT2_FEATURE_INCOMPAT_FILETYPE| \
 					 EXT2_FEATURE_INCOMPAT_META_BG)
+#else
+# define EXT2_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE| \
+					 EXT2_FEATURE_INCOMPAT_META_BG)
+#endif
 #define EXT2_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
 					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
 					 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
@@ -563,4 +595,16 @@ enum {
 					 ~EXT2_DIR_ROUND)
 #define EXT2_MAX_REC_LEN		((1<<16)-1)
 
+#ifndef __KERNEL__
+/* This simplifies things for user programs (notably e2fsprogs) that
+   must compile whether or not <linux/ext2_fs_c.h> is present, but
+   would prefer to include it.  Presumably the file is present if the
+   user has this version of ext2_fs.h. */
+
+# /* Do not remove this comment. */ include <linux/ext2_fs_c.h>
+
+/* The comment between `#' and `include' prevents mkdep from generating
+   a dependency on ext2_fs_c.h. */
+#endif
+
 #endif	/* _LINUX_EXT2_FS_H */
--- linux-2.6.25/MAINTAINERS	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/MAINTAINERS	2008-07-31 15:13:12.000000000 +0200
@@ -1424,6 +1424,13 @@ P:	Maciej W. Rozycki
 M:	macro@linux-mips.org
 S:	Maintained
 
+E2COMPR (ext2 compression patches)
+P:	Bodo Thiesen
+P:	Paul Whittaker
+M:	bothie@users.sourceforge.net
+M:	whitpa@users.sourceforge.net
+S:	Odd Fixes
+
 EATA-DMA SCSI DRIVER
 P:	Michael Neuffer
 L:	linux-eata@i-connect.net, linux-scsi@vger.kernel.org
--- linux-2.6.25/mm/page_alloc.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/mm/page_alloc.c	2008-07-31 15:13:12.000000000 +0200
@@ -1688,6 +1688,8 @@ void __pagevec_free(struct pagevec *pvec
 		free_hot_cold_page(pvec->pages[i], pvec->cold);
 }
 
+EXPORT_SYMBOL(__pagevec_free);
+
 void __free_pages(struct page *page, unsigned int order)
 {
 	if (put_page_testzero(page)) {
--- linux-2.6.25/mm/memory.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/mm/memory.c	2008-07-31 15:13:12.000000000 +0200
@@ -61,6 +61,10 @@
 #include <linux/swapops.h>
 #include <linux/elf.h>
 
+#ifdef CONFIG_EXT2_COMPRESS
+#include <linux/ext2_fs_c.h>
+#endif
+
 #ifndef CONFIG_NEED_MULTIPLE_NODES
 /* use the per-pgdat data instead for discontigmem - mbligh */
 unsigned long max_mapnr;
@@ -1967,6 +1971,11 @@ int vmtruncate(struct inode * inode, lof
 		 * unmap_mapping_range call must be made for correctness.
 		 */
 		unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
+#ifdef CONFIG_EXT3_COMPRESS
+		if ((inode->i_op && inode->i_op->truncate) &&
+		    ((strcmp(inode->i_sb->s_type->name, "ext3") != 0) ||
+		    (!(EXT3_I(inode)->i_flags & EXT3_COMPRBLK_FL))))
+#endif
 		truncate_inode_pages(mapping, offset);
 		unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
 	}
--- linux-2.6.25/mm/swapfile.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/mm/swapfile.c	2008-07-31 15:13:12.000000000 +0200
@@ -5,6 +5,13 @@
  *  Swap reorganised 29.12.95, Stephen Tweedie
  */
 
+/*
+ *  Copyright (C) 2001 Alcatel Business Systems - R&D Illkirch
+ *  Added by Pierre Peiffer  (pierre.peiffer@sxb.bsf.alcatel.fr) (2001-08-09)
+ *  (Copied from pjm e2compr-0.4.39-patch-2.2.18 patch)
+ *      For e2compress: Swapping not supported for e2compressed files.
+ */
+
 #include <linux/mm.h>
 #include <linux/hugetlb.h>
 #include <linux/mman.h>
@@ -28,6 +35,9 @@
 #include <linux/capability.h>
 #include <linux/syscalls.h>
 #include <linux/memcontrol.h>
+#ifdef CONFIG_EXT2_COMPRESS
+#include <linux/ext2_fs_c.h>
+#endif
 
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
@@ -1537,6 +1547,21 @@ asmlinkage long sys_swapon(const char __
 			goto bad_swap;
 		p->bdev = bdev;
 	} else if (S_ISREG(inode->i_mode)) {
+#ifdef CONFIG_EXT2_COMPRESS
+		/*
+		 * Swapping not supported for e2compressed files.
+		 * (Actually, this code is pretty useless because we
+		 * should get an error later anyway because of the
+		 * holes.)  Yes, this is pretty horrible code... I'll
+		 * improve it later.
+		 */
+		if ((strcmp(inode->i_sb->s_type->name, "ext2") == 0)
+		    && (EXT2_I(inode)->i_flags & EXT2_COMPRBLK_FL))
+		{
+			printk("Assertion: Error NO swap SWAP implemented!\n");
+			goto bad_swap;
+		}
+#endif
 		p->bdev = inode->i_sb->s_bdev;
 		mutex_lock(&inode->i_mutex);
 		did_down = 1;
--- linux-2.6.25/mm/filemap.c	2008-04-17 04:49:44.000000000 +0200
+++ linux-2.6.25-e2c-455/mm/filemap.c	2008-08-01 16:05:45.000000000 +0200
@@ -46,6 +46,10 @@ static ssize_t
 generic_file_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
 	loff_t offset, unsigned long nr_segs);
 
+#ifdef CONFIG_EXT2_COMPRESS
+# include <linux/ext2_fs_c.h>
+#endif
+
 /*
  * Shared mappings implemented 30.11.1994. It's not fully working yet,
  * though.
@@ -282,7 +286,19 @@ int wait_on_page_writeback_range(struct 
 			PAGECACHE_TAG_WRITEBACK,
 			min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1)) != 0) {
 		unsigned i;
+#ifdef CONFIG_EXT2_COMPRESS
+/*
+ * I'm not sure that this is right.  It has been reworked considerably since
+ * 2.6.5. - whitpa
+ */
+		struct inode *inode = mapping->host;
+        //printk("wait_on_page_writeback_range\n");
 
+		if ((strcmp(inode->i_sb->s_type->name, "ext2") != 0)
+			    || (atomic_read(&inode->i_mutex.count) > 0)
+			    || (EXT2_I(inode)->i_compr_flags &
+				EXT2_OSYNC_INODE))
+#endif
 		for (i = 0; i < nr_pages; i++) {
 			struct page *page = pvec.pages[i];
 
@@ -495,6 +511,7 @@ int add_to_page_cache_lru(struct page *p
 		lru_cache_add(page);
 	return ret;
 }
+EXPORT_SYMBOL(add_to_page_cache_lru);
 
 #ifdef CONFIG_NUMA
 struct page *__page_cache_alloc(gfp_t gfp)
@@ -524,12 +541,13 @@ static int __sleep_on_page_lock(void *wo
  * at a cost of "thundering herd" phenomena during rare hash
  * collisions.
  */
-static wait_queue_head_t *page_waitqueue(struct page *page)
+wait_queue_head_t *page_waitqueue(struct page *page)
 {
 	const struct zone *zone = page_zone(page);
 
 	return &zone->wait_table[hash_ptr(page, zone->wait_table_bits)];
 }
+EXPORT_SYMBOL(page_waitqueue);
 
 static inline void wake_up_page(struct page *page, int bit)
 {
@@ -867,7 +885,8 @@ EXPORT_SYMBOL(grab_cache_page_nowait);
  *
  * It is going insane. Fix it by quickly scaling down the readahead size.
  */
-static void shrink_readahead_size_eio(struct file *filp,
+static  
+void shrink_readahead_size_eio(struct file *filp,
 					struct file_ra_state *ra)
 {
 	if (!ra->ra_pages)
@@ -960,6 +979,15 @@ page_ok:
 		}
 		nr = nr - offset;
 
+#ifdef CONFIG_EXT2_COMPRESS
+		lock_page(page);
+		//check again: after locking still uptodate?
+		if(!PageUptodate(page)){
+			unlock_page(page);
+			goto page_not_up_to_date;
+		}				
+#endif
+
 		/* If users can be writing to this page using arbitrary
 		 * virtual addresses, take care about potential aliasing
 		 * before reading the page on the kernel side.
@@ -991,6 +1019,10 @@ page_ok:
 		offset &= ~PAGE_CACHE_MASK;
 		prev_offset = offset;
 
+#ifdef CONFIG_EXT2_COMPRESS
+		unlock_page(page);
+#endif
+
 		page_cache_release(page);
 		if (ret == nr && desc->count)
 			continue;
@@ -1008,7 +1040,7 @@ page_not_up_to_date:
 			continue;
 		}
 
-		/* Did somebody else fill it already? */
+
 		if (PageUptodate(page)) {
 			unlock_page(page);
 			goto page_ok;
@@ -1053,6 +1085,7 @@ readpage_error:
 		/* UHHUH! A synchronous read error occurred. Report it */
 		desc->error = error;
 		page_cache_release(page);
+		printk("Readpage Error\n");
 		goto out;
 
 no_cached_page:
@@ -1124,6 +1157,7 @@ success:
 	desc->arg.buf += size;
 	return size;
 }
+EXPORT_SYMBOL(file_read_actor);
 
 /*
  * Performs necessary checks before doing a write
@@ -1700,8 +1734,9 @@ static size_t __iovec_copy_from_user_ina
 	return copied - left;
 }
 
-/*
- * Copy as much as we can into the page and return the number of bytes which
+/*EXPORT_SYMBOL(__grab_cache_page); to be removed*/
+
+/* Copy as much as we can into the page and return the number of bytes which
  * were sucessfully copied.  If a fault is encountered then return the number of
  * bytes which were copied.
  */
