[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

model based compressor support



Hi David,

I finished the implementation of model based compressor support for JFFS2.

David, I would like to ask you: may I commit it to CVS? (certainly after 
it you can modify there anything you like/dont't like)

We had some discussion about it some month ago, I tried to follow that: 
model files now aren't "normal" files. They have no dentry only inode. I 
introduced a new kind of node:

#define JFFS2_NODETYPE_MODELINFO (JFFS2_FEATURE_INCOMPAT | 
JFFS2_NODE_ACCURATE | 5)

This node only stores an array with the inode numbers (and sizes) of the 
model files. After scaning of the flash the nlink of the inodes of model 
files are setted to 1. At the end of the mount process model files will 
be loaded "by hand". (the method is simililar the method of 
jffs2_read_dnode()).

Model files have an 8 bytes header: the first 4 is a magic 'J2MF', the 
next two is the compr and serial (usercompr), next two is reserved to 0. 
After loading the model file the corresponding compressor is called to 
initilize it (if that wants).

I modified gc to handle this MODELINFO correctly (just copy if 
neccesarry). I found a "FIXME" to handle JFFS2_NODETYPE_RWCOMPAT_COPY 
nodes, too, I implemented it in the same function.

I exended the Kconfig with the ability for users to set the priority of 
the compressor modules, too. Certainly this model support is also only 
an option, and if not enabled everything works as before.

I moved the JFFS2_COMPR_XXX defines from jffs2.h to compr.h. I think it 
is better place for them - if you not agree we can move back them.

In mkfs.jffs2 I intoduced a "compression configuration" concept. It is a 
string with comma separated words. Every word can be a name of the 
compressor (with priority, optoinally), or a compression mode. The 
default value of it is "priority,zlib:70,rtime:60". It equals the 
behavior of the original mkfs.jffs2. New switches of the mkfs.jffs2:

-C, --default-compr-config=CONFIG: Set the default compression 
configuration.

-F, --compressor-config-file=FILENAME: Specifies a compression config 
file. In this config  file  every line must be in format 
"FILENAME;TYPE;COMPRESSION_CONFIG", where FILENAME is a relative file 
name, TYPE can be only  one  of  the file  or  model keywords, and 
COMPRESSOIN_CONFIG must be a valid compression configuration. This 
specified  file will  be  compressed the specified compressor 
configuration. All of the non specified files will be  compressed  by 
the  default compression  configuration.   For  example a line of this 
config file can  be:  "boot/zImage;file;priority,zlib:60,rtime:50",  an 
other example: "model.armlib;model;zlib:60".

Certainly it is documented in its man page and if you don't use the new 
options mkfs.jffs2 will produce exactly the same image than the "old" 
mkfs.jffs2.

The attached patch contains the fix of the problem I wrote about 
yesterday but does not contains compr_armlib.c and modelgen_armlib.c 
(CVS will contain). Armlib is a special model based ARM code compressor 
designed by us.

I also modified jffs2dump.c to handle the new modelinfo node type.

Regards,
Ferenc
diff --unified --recursive --new-file mtd2/fs/Kconfig mtd/fs/Kconfig
--- mtd2/fs/Kconfig	2004-07-17 00:00:11.000000000 +0200
+++ mtd/fs/Kconfig	2004-10-06 10:56:20.000000000 +0200
@@ -80,6 +80,43 @@
 
 	  If unsure, you should _definitely_ say 'N'.
 
+choice
+        prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS
+        default JFFS2_CMODE_PRIORITY
+        depends on JFFS2_FS
+        help
+          You can set here the default compression mode of JFFS2 from 
+          the avaiable compression modes. Don't touch if unsure.
+
+config JFFS2_CMODE_NONE
+        bool "no compression"
+        help
+          Uses no compression.
+
+config JFFS2_CMODE_PRIORITY
+        bool "priority"
+        help
+          Tries the compressors in a predefinied order and chooses the first 
+          successful one.
+
+config JFFS2_CMODE_SIZE
+        bool "size (EXPERIMENTAL)"
+        help
+          Tries all compressors and chooses the one which has the smallest 
+          result.
+
+endchoice
+
+config JFFS2_MODEL
+	bool "JFFS2 model based compression support" if JFFS2_COMPRESSION_OPTIONS
+	depends on JFFS2_FS
+	default y
+        help
+          This feature makes it possible to use model based compressors
+          such as armib.
+          
+          Say 'Y' if unsure.
+
 config JFFS2_ZLIB
 	bool "JFFS2 ZLIB compression support" if JFFS2_COMPRESSION_OPTIONS
 	select ZLIB_INFLATE
@@ -94,6 +131,12 @@
           
           Say 'Y' if unsure.
 
+config JFFS2_ZLIB_PRIORITY
+	int "JFFS2 ZLIB compressor priority" if JFFS2_ZLIB
+	default "70"
+	help
+	  This prirority used by priority compression mode.
+
 config JFFS2_RTIME
 	bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS
 	depends on JFFS2_FS
@@ -101,6 +144,12 @@
         help
           Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
 
+config JFFS2_RTIME_PRIORITY
+	int "JFFS2 RTIME compressor priority" if JFFS2_RTIME
+	default "60"
+	help
+	  This prirority used by priority compression mode.
+
 config JFFS2_RUBIN
 	bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS
 	depends on JFFS2_FS
@@ -108,29 +157,24 @@
         help
           RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
 
-choice
-        prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS
-        default JFFS2_CMODE_PRIORITY
-        depends on JFFS2_FS
-        help
-          You can set here the default compression mode of JFFS2 from 
-          the avaiable compression modes. Don't touch if unsure.
-
-config JFFS2_CMODE_NONE
-        bool "no compression"
-        help
-          Uses no compression.
-
-config JFFS2_CMODE_PRIORITY
-        bool "priority"
+config JFFS2_RUBIN_PRIORITY
+	int "JFFS2 RUBIN compressor priority" if JFFS2_RUBIN
+	default "20"
+	help
+	  This prirority used by priority compression mode.
+
+config JFFS2_ARMLIB
+	bool "JFFS2 ARMLIB compression support" if JFFS2_COMPRESSION_OPTIONS
+	depends on JFFS2_MODEL
+	default y
         help
-          Tries the compressors in a predefinied order and chooses the first 
-          successful one.
+          ARMLIB is a model based compressor. It is designed for code compression.
+          Read its manual for more information!
 
-config JFFS2_CMODE_SIZE
-        bool "size (EXPERIMENTAL)"
-        help
-          Tries all compressors and chooses the one which has the smallest 
-          result.
+config JFFS2_ARMLIB_PRIORITY
+	int "JFFS2 ARMLIB compressor priority" if JFFS2_ARMLIB
+	default "50"
+	help
+	  This prirority used by priority compression mode.
 
-endchoice
+	  
diff --unified --recursive --new-file mtd2/fs/jffs2/Makefile.common mtd/fs/jffs2/Makefile.common
--- mtd2/fs/jffs2/Makefile.common	2004-07-17 00:00:11.000000000 +0200
+++ mtd/fs/jffs2/Makefile.common	2004-10-06 11:36:26.000000000 +0200
@@ -9,9 +9,10 @@
 jffs2-y	:= compr.o dir.o file.o ioctl.o nodelist.o malloc.o
 jffs2-y	+= read.o nodemgmt.o readinode.o write.o scan.o gc.o
 jffs2-y	+= symlink.o build.o erase.o background.o fs.o writev.o
-jffs2-y	+= super.o
+jffs2-y	+= super.o 
 
 jffs2-$(CONFIG_JFFS2_FS_NAND)	+= wbuf.o
 jffs2-$(CONFIG_JFFS2_RUBIN)	+= compr_rubin.o
 jffs2-$(CONFIG_JFFS2_RTIME)	+= compr_rtime.o
 jffs2-$(CONFIG_JFFS2_ZLIB)	+= compr_zlib.o
+jffs2-$(CONFIG_JFFS2_ARMLIB)	+= compr_armlib.o
diff --unified --recursive --new-file mtd2/fs/jffs2/build.c mtd/fs/jffs2/build.c
--- mtd2/fs/jffs2/build.c	2003-10-29 00:00:34.000000000 +0100
+++ mtd/fs/jffs2/build.c	2004-10-06 10:52:09.000000000 +0200
@@ -15,6 +15,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include "nodelist.h"
+#include "compr.h"
 
 static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
 
@@ -95,6 +96,9 @@
 
 	c->flags |= JFFS2_SB_FLAG_MOUNTING;
 	ret = jffs2_scan_medium(c);
+#ifdef CONFIG_JFFS2_MODEL
+        jffs2_link_model_files(c);     /* sets their nlink to 1 */
+#endif
 	c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
 
 	if (ret)
diff --unified --recursive --new-file mtd2/fs/jffs2/compr.c mtd/fs/jffs2/compr.c
--- mtd2/fs/jffs2/compr.c	2004-08-08 00:00:11.000000000 +0200
+++ mtd/fs/jffs2/compr.c	2004-10-06 11:28:52.000000000 +0200
@@ -13,15 +13,94 @@
  *
  */
 
+#include <linux/crc32.h>
 #include "compr.h"
 
-static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED;
-
+/* Actual compression mode */
+static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
 /* Available compressors are on this list */
 static LIST_HEAD(jffs2_compressor_list);
+/* Available models are on this list */
+static LIST_HEAD(jffs2_model_list);
+/* Lock for compressor list */
+static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED;
+/* Lock for model list */
+static spinlock_t jffs2_model_list_lock = SPIN_LOCK_UNLOCKED;
+/* Statistics for blocks stored without compression */
+static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
 
-/* Actual compression mode */
-static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+/* Init and exit function headers for compressors */
+int jffs2_rubinmips_init(void); void jffs2_rubinmips_exit(void);
+int jffs2_dynrubin_init(void);  void jffs2_dynrubin_exit(void);
+int jffs2_rtime_init(void);     void jffs2_rtime_exit(void);
+int jffs2_zlib_init(void);      void jffs2_zlib_exit(void);
+int jffs2_armlib_init(void);    void jffs2_armlib_exit(void);
+int jffs2_lzari_init(void);     void jffs2_lzari_exit(void);
+int jffs2_lzo_init(void);       void jffs2_lzo_exit(void);
+
+/* Registering compressors and setting the default compression mode */
+
+int jffs2_compressors_init(void) 
+{
+#ifdef CONFIG_JFFS2_ZLIB
+        jffs2_zlib_init();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+        jffs2_rtime_init();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+        jffs2_rubinmips_init();
+        jffs2_dynrubin_init();
+#endif
+#ifdef CONFIG_JFFS2_ARMLIB
+        jffs2_armlib_init();
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+        jffs2_lzari_init();
+#endif
+#ifdef CONFIG_JFFS2_LZO
+        jffs2_lzo_init();
+#endif
+/* Setting default compression mode */
+#ifdef CONFIG_JFFS2_CMODE_NONE
+        jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+        D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
+#else
+#ifdef CONFIG_JFFS2_CMODE_SIZE
+        jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+        D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
+#else
+        D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
+#endif
+#endif
+        return 0;
+}
+
+/* Unregistering compressors */
+
+int jffs2_compressors_exit(void) 
+{
+#ifdef CONFIG_JFFS2_LZO
+        jffs2_lzo_exit();
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+        jffs2_lzari_exit();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+        jffs2_dynrubin_exit();
+        jffs2_rubinmips_exit();
+#endif
+#ifdef CONFIG_JFFS2_ARMLIB
+        jffs2_armlib_exit();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+        jffs2_rtime_exit();
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+        jffs2_zlib_exit();
+#endif
+        return 0;
+}
 
 void jffs2_set_compression_mode(int mode) 
 {
@@ -33,12 +112,278 @@
         return jffs2_compression_mode;
 }
 
-/* Statistics for blocks stored without compression */
-static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
+#ifdef CONFIG_JFFS2_MODEL
+
+int jffs2_model_add(struct jffs2_sb_info *c, uint32_t inode, uint32_t size) 
+{
+        struct jffs2_model *m;
+
+        m = kmalloc(sizeof(*m),GFP_KERNEL);
+        if (!m) {
+                printk(KERN_WARNING "JFFS2: No memory for model allocation. Failed.\n");
+                return 1;
+        }
+        m->sb_info = c;
+        m->ino     = inode;
+	m->size    = size;
+        m->model   = NULL;
+        m->compr   = 0;     /* mark as "not asociated yet" */
+        spin_lock(&jffs2_model_list_lock);
+        list_add_tail(&m->list, &jffs2_model_list);
+        spin_unlock(&jffs2_model_list_lock);
+        return 0;
+}
+
+static int jffs2_model_init(struct jffs2_model *m) 
+{
+        struct jffs2_compressor *this;
+        unsigned char *p;
+	int ret;
+
+        p = m->model;
+        if ((p[0]!='J')||(p[1]!='2')||(p[2]!='M')||(p[3]!='F')) {
+                printk(KERN_WARNING "JFFS2: Bad magic (%c%c%c%c) in model file ino %d.\n",p[0],p[1],p[2],p[3],m->ino);
+                return 1;
+        }
+        m->compr  = p[4];
+        m->serial = p[5];
+	printk(KERN_DEBUG "JFFS2 model: compr=%d serial=%d\n",(int)m->compr,(int)m->serial);
+        spin_lock(&jffs2_compressor_list_lock);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (m->compr == this->compr) {
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        if (!this->model_init) return 0;
+			ret = this->model_init(&(m->model));
+                        return ret;
+                }
+        }
+	spin_unlock(&jffs2_compressor_list_lock);
+        printk(KERN_WARNING "JFFS2: compressor (%d) not found for ino %d.\n", m->compr, m->ino);
+        return 1;
+}
+
+static char *jffs2_model_load_inode(struct jffs2_sb_info *c, int ino, int size) 
+{
+	struct jffs2_inode_cache *ic;
+	struct jffs2_raw_node_ref *raw;
+	struct jffs2_raw_inode *ri;
+        char *buf = NULL, *readbuf, *decomprbuf;
+	size_t readlen;
+	int ret;
+	uint32_t offset,clen,dlen,crc;
+	
+	ic = jffs2_get_ino_cache(c, ino);
+	if (ic) {
+		buf = vmalloc(size);
+		raw = ic->nodes;
+		while (raw&&((char*)raw!=(char*)ic)) {
+			ri = jffs2_alloc_raw_inode();
+			ret = jffs2_flash_read(c,ref_offset(raw),sizeof(*ri),&readlen,(char*)ri);
+			if (ret) {
+				jffs2_free_raw_inode(ri);
+				vfree(buf);
+				return NULL;
+			}
+			offset = je32_to_cpu(ri->offset);
+			clen = je32_to_cpu(ri->csize);
+			dlen = je32_to_cpu(ri->dsize);
+			
+			if (ri->compr == JFFS2_COMPR_ZERO) {
+				memset(buf+offset, 0, dlen);
+				goto out_ri;
+			}
+			readbuf = NULL;
+			if (ri->compr == JFFS2_COMPR_NONE)
+				readbuf = buf+offset;
+			else    readbuf = kmalloc(clen, GFP_KERNEL);
+			if (!readbuf) goto out_ri;
+			
+			decomprbuf = buf+offset;
+			
+			ret = jffs2_flash_read(c, (ref_offset(raw)) + sizeof(*ri),
+			       clen, &readlen, readbuf);
+
+			if (!ret && readlen != clen) {
+				printk("JFFS2: model: cannot read enough data!\n");
+				goto out_readbuf;
+			}
+			if (ret) goto out_readbuf;
+
+			crc = crc32(0, readbuf, clen);
+			if (crc != je32_to_cpu(ri->data_crc)) {
+				printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n",
+		    		    je32_to_cpu(ri->data_crc), crc, ref_offset(raw));
+				goto out_readbuf;
+			}
+			if (ri->compr & 0x80) {
+				printk(KERN_WARNING "JFFS2: model files must be comperessed by non model based compressors!\n");
+				goto out_readbuf;
+			}
+			if (ri->compr != JFFS2_COMPR_NONE) {
+				D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
+				    clen, readbuf, dlen, decomprbuf)); 
+				ret = jffs2_decompress(c, NULL, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, clen, dlen);
+				if (ret) {
+					printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
+					goto out_readbuf;
+				}
+			}
+
+			if(readbuf != buf+offset)
+				kfree(readbuf);
+			jffs2_free_raw_inode(ri);
+			raw = raw->next_in_ino;
+		}
+	}
+	else printk(KERN_WARNING "JFFS2: cannot find model in inode cache.\n");
+	return buf;
+out_readbuf:
+	if(readbuf != buf+offset)
+		kfree(readbuf);
+out_ri:
+	jffs2_free_raw_inode(ri);
+	vfree(buf);
+	return NULL;
+}
+
+void jffs2_model_load(struct super_block *sb) 
+{
+        struct jffs2_sb_info *c;
+        struct jffs2_model *this;
+
+        c = JFFS2_SB_INFO(sb);
+        printk(KERN_DEBUG "JFFS2: loading model files for %x %x\n", (int)sb, (int)c);
+	spin_lock(&jffs2_model_list_lock);
+        list_for_each_entry(this, &jffs2_model_list, list) {
+                if ((this->sb_info == c) && (this->compr == 0)) {
+	                spin_unlock(&jffs2_model_list_lock);
+                        printk(KERN_INFO "JFFS2: loading model files from ino %d, size %d.\n", (int)(this->ino),(int)(this->size));
+			this->model = jffs2_model_load_inode(c, this->ino, this->size);
+			if (this->model) {
+				if (jffs2_model_init(this)) {
+					printk(KERN_WARNING "JFFS2: Unable to init file ino %d\n",this->ino);
+				}
+				else {
+					printk(KERN_DEBUG "JFFS2: model loaded and inited correctly.\n");
+				}
+			}
+			else {
+				printk(KERN_WARNING "JFFS2: Unable to load model file ino %d\n",this->ino);
+			}
+	                spin_lock(&jffs2_model_list_lock);
+                }
+        }
+	spin_unlock(&jffs2_model_list_lock);
+}
+
+void jffs2_model_release(struct super_block *sb) 
+{
+        struct jffs2_sb_info *c;
+        struct jffs2_model *this, *m;
+        struct jffs2_compressor *comp;
+
+        c = JFFS2_SB_INFO(sb);
+        printk(KERN_DEBUG "JFFS2: releasing model files for %x %x\n", (int)sb, (int)c);
+
+        do {
+                m = NULL;
+	        spin_lock(&jffs2_model_list_lock);
+                list_for_each_entry(this, &jffs2_model_list, list) {
+                        if ((this->sb_info == c)) {
+                                m = this;
+                        }
+                }
+                spin_unlock(&jffs2_model_list_lock);
+                if (m) {
+                        /* free m->model */
+                        spin_lock(&jffs2_compressor_list_lock);
+                        list_for_each_entry(comp, &jffs2_compressor_list, list) {
+                                if (comp->compr == m->compr) {
+                                        spin_unlock(&jffs2_compressor_list_lock);
+                                        if (comp->model_destroy) comp->model_destroy(&m->model);
+                                        spin_lock(&jffs2_compressor_list_lock);
+                                }
+                        }
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        if (m->model) {
+                                /* if model_destroy did not do its job let's do it */
+                                vfree(m->model);
+                                m->model = NULL;
+                        }
+	                spin_lock(&jffs2_model_list_lock);
+                        list_del(&m->list);
+	                spin_unlock(&jffs2_model_list_lock);
+                        kfree(m);
+                }
+        } while (m);
+}
+
+static struct jffs2_model *jffs2_model_get(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                                           unsigned char compr, unsigned char min_serial) 
+{
+        struct jffs2_model *this;
+
+	spin_lock(&jffs2_model_list_lock);
+        list_for_each_entry(this, &jffs2_model_list, list) {
+                if ((this->sb_info == c) && (this->compr == compr) && (this->serial >= min_serial)) {
+	                spin_unlock(&jffs2_model_list_lock);
+                        return this;
+                }
+        }
+	spin_unlock(&jffs2_model_list_lock);
+        return NULL;
+}
+
+void jffs2_link_model_files(struct jffs2_sb_info *c)
+{
+        struct jffs2_model *this;
+	struct jffs2_inode_cache *ic;
+
+	spin_lock(&jffs2_model_list_lock);
+        list_for_each_entry(this, &jffs2_model_list, list) {
+                if (this->sb_info == c) {
+	                ic = jffs2_get_ino_cache(c, this->ino);
+                        if (ic) {
+                                ic->nlink = 1;    /* mark it not to delete */
+                        }
+                        else {
+				printk(KERN_WARNING "JFFS2: Unable to file model %d in inode cache.\n",this->ino);
+                        }
+                }
+        }
+	spin_unlock(&jffs2_model_list_lock);
+}
+
+int jffs2_is_model_ino(struct jffs2_sb_info *c, uint32_t ino) 
+{
+        struct jffs2_model *this;
+
+	spin_lock(&jffs2_model_list_lock);
+        list_for_each_entry(this, &jffs2_model_list, list) {
+                if ((this->sb_info == c) && (this->ino == ino)) {
+	                spin_unlock(&jffs2_model_list_lock);
+                        return 1;
+                }
+        }
+        spin_unlock(&jffs2_model_list_lock);
+        return 0;
+}
+
+#else  /* CONFIG_JFFS2_MODEL */
+
+int jffs2_is_model_ino(struct jffs2_sb_info *c, uint32_t inode) 
+{
+        return 0;
+}
+
+#endif /* CONFIG_JFFS2_MODEL */
+
 
 /* jffs2_compress:
- * @data: Pointer to uncompressed data
- * @cdata: Pointer to returned pointer to buffer for compressed data
+ * @c: sb_info
+ * @f: inode_info
+ * @data_in: Pointer to uncompressed data
+ * @cpage_out: Pointer to returned pointer to buffer for compressed data
  * @xxxxxxx.
  *	On exit, expected to hold the amount of data actually compressed.
  * @cdatalen: On entry, holds the amount of space available for compressed
@@ -64,6 +409,7 @@
         unsigned char *output_buf = NULL, *tmp_buf;
         uint32_t orig_slen, orig_dlen;
         uint32_t best_slen=0, best_dlen=0;
+        int best_serial=0;
 
         switch (jffs2_compression_mode) {
         case JFFS2_COMPR_MODE_NONE:
@@ -73,7 +419,7 @@
                 if (!output_buf) {
                         printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
                         goto out;
-                }
+                }                
                 orig_slen = *datalen;
                 orig_dlen = *cdatalen;
                 spin_lock(&jffs2_compressor_list_lock);
@@ -82,20 +428,49 @@
                         if ((!this->compress)||(this->disabled))
                                 continue;
 
-                        this->usecount++;
-                        spin_unlock(&jffs2_compressor_list_lock);
-                        *datalen  = orig_slen;
-                        *cdatalen = orig_dlen;
-                        compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
-                        spin_lock(&jffs2_compressor_list_lock);
-                        this->usecount--;
-                        if (!compr_ret) {
-                                ret = this->compr;
-                                this->stat_compr_blocks++;
-                                this->stat_compr_orig_size += *datalen;
-                                this->stat_compr_new_size  += *cdatalen;
-                                break;
+                        if (!(this->compr & 0x80)) {
+                                /* OK, this is a non-model-based compressor */
+                                *datalen  = orig_slen;
+                                *cdatalen = orig_dlen;
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                this->usecount++;
+                                compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
+                                this->usecount--;
+                                spin_lock(&jffs2_compressor_list_lock);
+                                if (!compr_ret) {
+                                        ret = this->compr;
+                                        this->stat_compr_blocks++;
+                                        this->stat_compr_orig_size += *datalen;
+                                        this->stat_compr_new_size  += *cdatalen;
+                                        break;
+                                }
                         }
+#ifdef CONFIG_JFFS2_MODEL
+                        else {
+                                /* It is model-based compressor, let's search model for it */
+                                int serial = 0;
+                                struct jffs2_model *m= jffs2_model_get(c,f,this->compr,serial);
+                                while (m) {
+                                        *datalen  = orig_slen;
+                                        *cdatalen = orig_dlen;
+                                        spin_unlock(&jffs2_compressor_list_lock);
+                                        this->usecount++;
+                                        compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, m->model);
+                                        this->usecount--;
+                                        spin_lock(&jffs2_compressor_list_lock);
+                                        if (!compr_ret) {
+                                                ret = (uint16_t)this->compr | ((uint16_t)m->serial << 8);
+                                                this->stat_compr_blocks++;
+                                                this->stat_compr_orig_size += *datalen;
+                                                this->stat_compr_new_size  += *cdatalen;
+                                                break;
+                                        }
+                                        serial = m->serial+1;
+                                        m = jffs2_model_get(c, f, this->compr, serial);
+                                }
+                                if (ret!=JFFS2_COMPR_NONE) break;
+                        }
+#endif
                 }
                 spin_unlock(&jffs2_compressor_list_lock);
                 if (ret == JFFS2_COMPR_NONE) kfree(output_buf);
@@ -129,20 +504,49 @@
                                         this->compr_buf_size = orig_dlen;
                                 }
                         }
-                        this->usecount++;
-                        spin_unlock(&jffs2_compressor_list_lock);
-                        *datalen  = orig_slen;
-                        *cdatalen = orig_dlen;
-                        compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
-                        spin_lock(&jffs2_compressor_list_lock);
-                        this->usecount--;
-                        if (!compr_ret) {
-                                if ((!best_dlen)||(best_dlen>*cdatalen)) {
-                                        best_dlen = *cdatalen;
-                                        best_slen = *datalen;
-                                        best = this;
+                        if (!(this->compr & 0x80)) {
+                                /* OK, this is a non-model-based compressor */
+                                *datalen  = orig_slen;
+                                *cdatalen = orig_dlen;
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                this->usecount++;
+                                compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
+                                this->usecount--;
+                                spin_lock(&jffs2_compressor_list_lock);
+                                if (!compr_ret) {
+                                        if ((!best_dlen)||(best_dlen>*cdatalen)) {
+                                                best_dlen = *cdatalen;
+                                                best_slen = *datalen;
+                                                best = this;
+                                        }
+                                }
+                        }
+#ifdef CONFIG_JFFS2_MODEL
+                        else {
+                                /* It is model-based compressor, let's search model for it */
+                                int serial = 0;
+                                struct jffs2_model *m = jffs2_model_get(c, f, this->compr,serial);
+                                while (m) {
+                                        *datalen  = orig_slen;
+                                        *cdatalen = orig_dlen;
+                                        spin_unlock(&jffs2_compressor_list_lock);
+                                        this->usecount++;
+                                        compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, m->model);
+                                        this->usecount--;
+                                        spin_lock(&jffs2_compressor_list_lock);
+                                        if (!compr_ret) {
+                                                if ((!best_dlen)||(best_dlen>*cdatalen)) {
+                                                        best_dlen = *cdatalen;
+                                                        best_slen = *datalen;
+                                                        best = this;
+                                                        best_serial = m->serial;
+                                                }
+                                        }
+                                        serial = m->serial+1;
+                                        m = jffs2_model_get(c, f, this->compr, serial);
                                 }
                         }
+#endif
                 }
                 if (best_dlen) {
                         *cdatalen = best_dlen;
@@ -153,7 +557,7 @@
                         best->stat_compr_blocks++;
                         best->stat_compr_orig_size += best_slen;
                         best->stat_compr_new_size  += best_dlen;
-                        ret = best->compr;
+                        ret = (uint16_t)best->compr | ((uint16_t)best_serial << 8);
                 }
                 spin_unlock(&jffs2_compressor_list_lock);
                 break;
@@ -179,13 +583,16 @@
 {
         struct jffs2_compressor *this;
         int ret;
+	unsigned char compr, model_serial;
+        void *model = NULL;
+#ifdef CONFIG_JFFS2_MODEL
+        struct jffs2_model *m;
+#endif
 
-	/* Older code had a bug where it would write non-zero 'usercompr'
-	   fields. Deal with it. */
-	if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB)
-		comprtype &= 0xff;
-
-	switch (comprtype & 0xff) {
+        compr = comprtype & 0xff;
+        model_serial = (comprtype >> 8) & 0xff;
+	
+	switch (compr) {
 	case JFFS2_COMPR_NONE:
 		/* This should be special-cased elsewhere, but we might as well deal with it */
 		memcpy(data_out, cdata_in, datalen);
@@ -197,24 +604,37 @@
 	default:
                 spin_lock(&jffs2_compressor_list_lock);
                 list_for_each_entry(this, &jffs2_compressor_list, list) {
-                        if (comprtype == this->compr) {
-                                this->usecount++;
+                        if (compr == this->compr) {
+                                if (compr & 0x80) { /* it is a model based compressor? */
+#ifdef CONFIG_JFFS2_MODEL
+                                        m = jffs2_model_get(c,f,compr,model_serial);
+                                        if ((m == NULL) || (m->compr != compr) || (m->serial != model_serial)) {
+                                                spin_unlock(&jffs2_compressor_list_lock);
+		                                printk(KERN_WARNING "JFFS2 model 0x%02x,0x%02x not available.\n", compr, model_serial);
+		                                return -EIO;
+                                        }
+                                        model = m->model;
+#else
+                                        spin_unlock(&jffs2_compressor_list_lock);
+		                        printk(KERN_WARNING "JFFS2 model support (0x%02x,0x%02x) not available.\n", compr, model_serial);
+		                        return -EIO;
+#endif
+                                }
                                 spin_unlock(&jffs2_compressor_list_lock);
-                                ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
-                                spin_lock(&jffs2_compressor_list_lock);
+                                this->usecount++;
+                                ret = this->decompress(cdata_in, data_out, cdatalen, datalen, model);
+                                this->usecount--;
                                 if (ret) {
                                         printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
                                 }
                                 else {
                                         this->stat_decompr_blocks++;
                                 }
-                                this->usecount--;
-                                spin_unlock(&jffs2_compressor_list_lock);
                                 return ret;
                         }
                 }
-		printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype);
                 spin_unlock(&jffs2_compressor_list_lock);
+		printk(KERN_WARNING "JFFS2 compression type 0x%02x,0x%02x not available.\n", compr, model_serial);
 		return -EIO;
 	}
 	return 0;
@@ -225,9 +645,15 @@
         struct jffs2_compressor *this;
 
         if (!comp->name) {
-                printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
+                printk(KERN_WARNING "JFFS2: NULL compressor name at registering compressor. Failed.\n");
+                return -1;
+        }
+#ifndef CONFIG_JFFS2_MODEL
+        if ((comp->compr & 0x80)) {
+                printk(KERN_WARNING "JFFS2: model support is not active (%s). Recompile!\n", comp->name);
                 return -1;
         }
+#endif
         comp->compr_buf_size=0;
         comp->compr_buf=NULL;
         comp->usecount=0;
@@ -278,6 +704,12 @@
         return 0;
 }
 
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
+{
+        if (orig != comprbuf)
+                kfree(comprbuf);
+}
+
 #ifdef CONFIG_JFFS2_PROC
 
 #define JFFS2_STAT_BUF_SIZE 16000
@@ -414,66 +846,4 @@
         return 0;
 }
 
-#endif
-
-void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
-{
-        if (orig != comprbuf)
-                kfree(comprbuf);
-}
-
-int jffs2_compressors_init(void) 
-{
-/* Registering compressors */
-#ifdef CONFIG_JFFS2_ZLIB
-        jffs2_zlib_init();
-#endif
-#ifdef CONFIG_JFFS2_RTIME
-        jffs2_rtime_init();
-#endif
-#ifdef CONFIG_JFFS2_RUBIN
-        jffs2_rubinmips_init();
-        jffs2_dynrubin_init();
-#endif
-#ifdef CONFIG_JFFS2_LZARI
-        jffs2_lzari_init();
-#endif
-#ifdef CONFIG_JFFS2_LZO
-        jffs2_lzo_init();
-#endif
-/* Setting default compression mode */
-#ifdef CONFIG_JFFS2_CMODE_NONE
-        jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
-        D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
-#else
-#ifdef CONFIG_JFFS2_CMODE_SIZE
-        jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
-        D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
-#else
-        D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
-#endif
-#endif
-        return 0;
-}
-
-int jffs2_compressors_exit(void) 
-{
-/* Unregistering compressors */
-#ifdef CONFIG_JFFS2_LZO
-        jffs2_lzo_exit();
-#endif
-#ifdef CONFIG_JFFS2_LZARI
-        jffs2_lzari_exit();
-#endif
-#ifdef CONFIG_JFFS2_RUBIN
-        jffs2_dynrubin_exit();
-        jffs2_rubinmips_exit();
-#endif
-#ifdef CONFIG_JFFS2_RTIME
-        jffs2_rtime_exit();
-#endif
-#ifdef CONFIG_JFFS2_ZLIB
-        jffs2_zlib_exit();
-#endif
-        return 0;
-}
+#endif /* CONFIG_JFFS2_PROC */
diff --unified --recursive --new-file mtd2/fs/jffs2/compr.h mtd/fs/jffs2/compr.h
--- mtd2/fs/jffs2/compr.h	2004-07-17 00:00:11.000000000 +0200
+++ mtd/fs/jffs2/compr.h	2004-10-06 10:56:35.000000000 +0200
@@ -27,12 +27,18 @@
 #include <linux/jffs2_fs_sb.h>
 #include "nodelist.h"
 
-#define JFFS2_RUBINMIPS_PRIORITY 10
-#define JFFS2_DYNRUBIN_PRIORITY  20
-#define JFFS2_LZARI_PRIORITY     30
-#define JFFS2_LZO_PRIORITY       40
-#define JFFS2_RTIME_PRIORITY     50
-#define JFFS2_ZLIB_PRIORITY      60
+/* Non-model-based compressors */
+#define JFFS2_COMPR_NONE	0x00
+#define JFFS2_COMPR_ZERO	0x01
+#define JFFS2_COMPR_RTIME	0x02
+#define JFFS2_COMPR_RUBINMIPS	0x03
+#define JFFS2_COMPR_COPY	0x04
+#define JFFS2_COMPR_DYNRUBIN	0x05
+#define JFFS2_COMPR_ZLIB	0x06
+#define JFFS2_COMPR_LZO         0x07
+#define JFFS2_COMPR_LZARI       0x08
+/* Model-based compressors */
+#define JFFS2_COMPR_ARMLIB      0x81
 
 #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
 #define JFFS2_DYNRUBIN_DISABLED  /*        for decompression */
@@ -48,11 +54,16 @@
         struct list_head list;
         int priority;              /* used by prirority comr. mode */
         char *name;
-        char compr;                /* JFFS2_COMPR_XXX */
+        unsigned char compr;       /* JFFS2_COMPR_XXX */
         int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
                         uint32_t *srclen, uint32_t *destlen, void *model);
         int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
                         uint32_t cdatalen, uint32_t datalen, void *model);
+        int (*model_init)(void **model);
+                                   /* called to init model data */
+        int (*model_destroy)(void **model);
+                                   /* called to free model data, 
+                                      it should set *model to NULL */
         int usecount;
         int disabled;              /* if seted the compressor won't compress */
         unsigned char *compr_buf;  /* used by size compr. mode */
@@ -63,6 +74,17 @@
         uint32_t stat_decompr_blocks;
 };
 
+struct jffs2_model {
+        struct list_head list;
+        struct jffs2_sb_info *sb_info; /* jffs2 sb_info */
+        void *sb;                      /* real super block */
+        uint32_t ino;
+	uint32_t size;
+        void *model;                   /* model data */
+        unsigned char compr;           /* JFFS2_COMPR_XXX */
+        unsigned char serial;
+};
+
 int jffs2_register_compressor(struct jffs2_compressor *comp);
 int jffs2_unregister_compressor(struct jffs2_compressor *comp);
 
@@ -79,6 +101,14 @@
 
 void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
 
+#ifdef CONFIG_JFFS2_MODEL
+int jffs2_model_add(struct jffs2_sb_info *c, uint32_t inode, uint32_t size);
+void jffs2_model_load(struct super_block *sb);
+void jffs2_model_release(struct super_block *sb);
+void jffs2_link_model_files(struct jffs2_sb_info *c);
+#endif
+int jffs2_is_model_ino(struct jffs2_sb_info *c, uint32_t ino);
+
 #ifdef CONFIG_JFFS2_PROC
 int jffs2_enable_compressor_name(const char *name);
 int jffs2_disable_compressor_name(const char *name);
@@ -89,30 +119,4 @@
 char *jffs2_stats(void);
 #endif
 
-/* Compressor modules */
-/* These functions will be called by jffs2_compressors_init/exit */
-
-#ifdef CONFIG_JFFS2_RUBIN
-int jffs2_rubinmips_init(void);
-void jffs2_rubinmips_exit(void);
-int jffs2_dynrubin_init(void);
-void jffs2_dynrubin_exit(void);
-#endif
-#ifdef CONFIG_JFFS2_RTIME
-int jffs2_rtime_init(void);
-void jffs2_rtime_exit(void);
-#endif
-#ifdef CONFIG_JFFS2_ZLIB
-int jffs2_zlib_init(void);
-void jffs2_zlib_exit(void);
-#endif
-#ifdef CONFIG_JFFS2_LZARI
-int jffs2_lzari_init(void);
-void jffs2_lzari_exit(void);
-#endif
-#ifdef CONFIG_JFFS2_LZO
-int jffs2_lzo_init(void);
-void jffs2_lzo_exit(void);
-#endif
-
 #endif /* __JFFS2_COMPR_H__ */
diff --unified --recursive --new-file mtd2/fs/jffs2/compr_lzari.c mtd/fs/jffs2/compr_lzari.c
--- mtd2/fs/jffs2/compr_lzari.c	2004-06-24 00:00:12.000000000 +0200
+++ mtd/fs/jffs2/compr_lzari.c	2004-10-06 10:52:09.000000000 +0200
@@ -680,7 +680,7 @@
 			  uint32_t dstlen, void *model);
 
 struct jffs2_compressor jffs2_lzari_comp = {
-	.priority = JFFS2_LZARI_PRIORITY,
+	.priority = CONFIG_JFFS2_LZARI_PRIORITY,
 	.name = "lzari",
 	.compr = JFFS2_COMPR_LZARI,
 	.compress = &jffs2_lzari_compress,
diff --unified --recursive --new-file mtd2/fs/jffs2/compr_lzo.c mtd/fs/jffs2/compr_lzo.c
--- mtd2/fs/jffs2/compr_lzo.c	2004-06-24 00:00:12.000000000 +0200
+++ mtd/fs/jffs2/compr_lzo.c	2004-10-06 10:52:09.000000000 +0200
@@ -2220,7 +2220,7 @@
 			  uint32_t dstlen, void *model);
 
 static struct jffs2_compressor jffs2_lzo_comp = {
-	.priority = JFFS2_LZO_PRIORITY,
+	.priority = CONFIG_JFFS2_LZO_PRIORITY,
 	.name = "lzo",
 	.compr = JFFS2_COMPR_LZO,
 	.compress = &jffs2_lzo_compress,
diff --unified --recursive --new-file mtd2/fs/jffs2/compr_rtime.c mtd/fs/jffs2/compr_rtime.c
--- mtd2/fs/jffs2/compr_rtime.c	2004-06-24 00:00:12.000000000 +0200
+++ mtd/fs/jffs2/compr_rtime.c	2004-10-06 10:52:09.000000000 +0200
@@ -105,7 +105,7 @@
 }		   
 
 static struct jffs2_compressor jffs2_rtime_comp = {
-    .priority = JFFS2_RTIME_PRIORITY,
+    .priority = CONFIG_JFFS2_RTIME_PRIORITY,
     .name = "rtime",
     .compr = JFFS2_COMPR_RTIME,
     .compress = &jffs2_rtime_compress,
diff --unified --recursive --new-file mtd2/fs/jffs2/compr_rubin.c mtd/fs/jffs2/compr_rubin.c
--- mtd2/fs/jffs2/compr_rubin.c	2004-06-24 00:00:12.000000000 +0200
+++ mtd/fs/jffs2/compr_rubin.c	2004-10-06 10:52:09.000000000 +0200
@@ -327,7 +327,7 @@
 }
 
 static struct jffs2_compressor jffs2_rubinmips_comp = {
-    .priority = JFFS2_RUBINMIPS_PRIORITY,
+    .priority = CONFIG_JFFS2_RUBIN_PRIORITY,
     .name = "rubinmips",
     .compr = JFFS2_COMPR_DYNRUBIN,
     .compress = NULL, /*&jffs2_rubinmips_compress,*/
@@ -350,7 +350,7 @@
 }
 
 static struct jffs2_compressor jffs2_dynrubin_comp = {
-    .priority = JFFS2_DYNRUBIN_PRIORITY,
+    .priority = CONFIG_JFFS2_RUBIN_PRIORITY,
     .name = "dynrubin",
     .compr = JFFS2_COMPR_RUBINMIPS,
     .compress = jffs2_dynrubin_compress,
diff --unified --recursive --new-file mtd2/fs/jffs2/compr_zlib.c mtd/fs/jffs2/compr_zlib.c
--- mtd2/fs/jffs2/compr_zlib.c	2004-06-24 00:00:12.000000000 +0200
+++ mtd/fs/jffs2/compr_zlib.c	2004-10-06 10:52:09.000000000 +0200
@@ -184,7 +184,7 @@
 }
 
 static struct jffs2_compressor jffs2_zlib_comp = {
-    .priority = JFFS2_ZLIB_PRIORITY,
+    .priority = CONFIG_JFFS2_ZLIB_PRIORITY,
     .name = "zlib",
     .compr = JFFS2_COMPR_ZLIB,
     .compress = &jffs2_zlib_compress,
diff --unified --recursive --new-file mtd2/fs/jffs2/dir.c mtd/fs/jffs2/dir.c
--- mtd2/fs/jffs2/dir.c	2003-10-12 00:00:05.000000000 +0200
+++ mtd/fs/jffs2/dir.c	2004-10-06 10:52:09.000000000 +0200
@@ -21,6 +21,7 @@
 #include <linux/jffs2_fs_sb.h>
 #include <linux/time.h>
 #include "nodelist.h"
+#include "compr.h"
 
 /* Urgh. Please tell me there's a nicer way of doing these. */
 #include <linux/version.h>
diff --unified --recursive --new-file mtd2/fs/jffs2/ecos/src/fs-ecos.c mtd/fs/jffs2/ecos/src/fs-ecos.c
--- mtd2/fs/jffs2/ecos/src/fs-ecos.c	2004-04-22 00:00:32.000000000 +0200
+++ mtd/fs/jffs2/ecos/src/fs-ecos.c	2004-10-06 10:52:09.000000000 +0200
@@ -21,6 +21,7 @@
 #include <linux/pagemap.h>
 #include <linux/crc32.h>
 #include "nodelist.h"
+#include "compr.h"
 
 #include <errno.h>
 #include <string.h>
diff --unified --recursive --new-file mtd2/fs/jffs2/file.c mtd/fs/jffs2/file.c
--- mtd2/fs/jffs2/file.c	2004-03-20 00:00:06.000000000 +0100
+++ mtd/fs/jffs2/file.c	2004-10-06 10:52:09.000000000 +0200
@@ -21,6 +21,7 @@
 #include <linux/crc32.h>
 #include <linux/jffs2.h>
 #include "nodelist.h"
+#include "compr.h"
 
 extern int generic_file_open(struct inode *, struct file *) __attribute__((weak));
 extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) __attribute__((weak));
diff --unified --recursive --new-file mtd2/fs/jffs2/fs.c mtd/fs/jffs2/fs.c
--- mtd2/fs/jffs2/fs.c	2004-07-14 00:00:17.000000000 +0200
+++ mtd/fs/jffs2/fs.c	2004-10-06 10:52:09.000000000 +0200
@@ -23,7 +23,7 @@
 #include <linux/vfs.h>
 #include <linux/crc32.h>
 #include "nodelist.h"
-
+#include "compr.h"
 
 static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
 {
diff --unified --recursive --new-file mtd2/fs/jffs2/gc.c mtd/fs/jffs2/gc.c
--- mtd2/fs/jffs2/gc.c	2004-07-21 00:00:13.000000000 +0200
+++ mtd/fs/jffs2/gc.c	2004-10-06 15:40:36.000000000 +0200
@@ -21,6 +21,8 @@
 #include "nodelist.h"
 #include "compr.h"
 
+static int jffs2_garbage_handle_inodeless(struct jffs2_sb_info *c, 
+					  struct jffs2_raw_node_ref *raw);
 static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
 					  struct jffs2_inode_cache *ic,
 					  struct jffs2_raw_node_ref *raw);
@@ -240,12 +242,11 @@
 	D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw)));
 
 	if (!raw->next_in_ino) {
-		/* Inode-less node. Clean marker, snapshot or something like that */
-		/* FIXME: If it's something that needs to be copied, including something
-		   we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */
+		/* Inode-less node. Clean marker, snapshot, modelinfo or something like that */
 		spin_unlock(&c->erase_completion_lock);
-		jffs2_mark_node_obsolete(c, raw);
 		up(&c->alloc_sem);
+		jffs2_garbage_handle_inodeless(c, raw);
+		jffs2_mark_node_obsolete(c, raw);
 		goto eraseit_lock;
 	}
 
@@ -486,6 +487,153 @@
 	return ret;
 }
 
+static int jffs2_garbage_handle_inodeless(struct jffs2_sb_info *c, 
+					  struct jffs2_raw_node_ref *raw) 
+{
+	union jffs2_node_union *node;
+	struct jffs2_raw_node_ref *nraw;
+	size_t retlen;
+	int ret;
+	uint32_t phys_ofs, alloclen;
+	uint32_t crc, rawlen;
+	int retried = 0;
+
+	D1(printk(KERN_DEBUG "Going to GC and inode-less node at 0x%08x\n", ref_offset(raw)));
+
+	rawlen = ref_totlen(c, c->gcblock, raw);
+
+	/* Ask for a small amount of space (or the totlen if smaller) because we
+	   don't want to force wastage of the end of a block if splitting would
+	   work. */
+	ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, 
+					      rawlen), &phys_ofs, &alloclen);
+	if (ret)
+		return ret;
+
+	if (alloclen < rawlen) {
+		/* Doesn't fit untouched. We'll go the old route and split it */
+		return -EBADFD;
+	}
+
+	node = kmalloc(rawlen, GFP_KERNEL);
+	if (!node)
+               return -ENOMEM;
+
+	ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
+	
+	if (!ret && retlen != rawlen)
+		ret = -EIO;
+	if (ret)
+		goto il_out_node;
+
+	crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4);
+	if (je32_to_cpu(node->u.hdr_crc) != crc) {
+		printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+		       ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc);
+		goto il_bail;
+	}
+
+	if (je16_to_cpu(node->u.nodetype)==JFFS2_NODETYPE_MODELINFO) {
+		crc = crc32(0, node, sizeof(node->m)-8);
+		if (je32_to_cpu(node->m.node_crc) != crc) {
+			printk(KERN_WARNING "Node CRC failed on modelinfo node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+			       ref_offset(raw), je32_to_cpu(node->m.node_crc), crc);
+			goto il_bail;
+		}
+		if (je32_to_cpu(node->m.model_num)) {
+			crc = crc32(0, node->m.models, je32_to_cpu(node->m.model_num) * 2 * sizeof(jint32_t));
+			if (je32_to_cpu(node->m.models_crc) != crc) {
+				printk(KERN_WARNING "Name CRC failed on modelinfo node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+				       ref_offset(raw), je32_to_cpu(node->m.models_crc), crc);
+				goto il_bail;
+			}
+		}
+	}
+	else if ((je16_to_cpu(node->u.nodetype))&JFFS2_FEATURE_RWCOMPAT_COPY) {
+		printk(KERN_DEBUG "Unknown but RWCOMPAT_COPY node at 0x%08x: 0x%04x\n",
+		       ref_offset(raw), je16_to_cpu(node->u.nodetype));
+		printk("Unknown but RWCOMPAT_COPY node at 0x%08x: 0x%04x\n",
+		       ref_offset(raw), je16_to_cpu(node->u.nodetype));
+	}
+	else {
+		printk(KERN_DEBUG "Not important inodeless node at 0x%08x: 0x%04x\n", 
+		       ref_offset(raw), je16_to_cpu(node->u.nodetype));
+		goto il_bail;
+	}
+	
+	nraw = jffs2_alloc_raw_node_ref();
+	if (!nraw) {
+		ret = -ENOMEM;
+		goto il_out_node;
+	}
+
+	/* OK, all the CRCs are good; this node can just be copied as-is. */
+ il_retry:
+	nraw->flash_offset = phys_ofs;
+	nraw->__totlen = rawlen;
+	nraw->next_phys = NULL;
+
+	ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
+
+	if (ret || (retlen != rawlen)) {
+		printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
+                       rawlen, phys_ofs, ret, retlen);
+		if (retlen) {
+                        /* Doesn't belong to any inode */
+			nraw->next_in_ino = NULL;
+
+			nraw->flash_offset |= REF_OBSOLETE;
+			jffs2_add_physical_node_ref(c, nraw);
+			jffs2_mark_node_obsolete(c, nraw);
+		} else {
+			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset);
+                        jffs2_free_raw_node_ref(nraw);
+		}
+		if (!retried && (nraw == jffs2_alloc_raw_node_ref())) {
+			/* Try to reallocate space and retry */
+			uint32_t dummy;
+			struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
+
+			retried = 1;
+
+			D1(printk(KERN_DEBUG "Retrying failed write of inode-less node.\n"));
+			
+			ACCT_SANITY_CHECK(c,jeb);
+			D1(ACCT_PARANOIA_CHECK(jeb));
+
+			ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy);
+
+			if (!ret) {
+				D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
+
+				ACCT_SANITY_CHECK(c,jeb);
+				D1(ACCT_PARANOIA_CHECK(jeb));
+
+				goto il_retry;
+			}
+			D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
+			jffs2_free_raw_node_ref(nraw);
+		}
+
+		if (!ret)
+			ret = -EIO;
+		goto il_out_node;
+	}
+	nraw->flash_offset |= REF_PRISTINE;
+	jffs2_add_physical_node_ref(c, nraw);
+
+	D1(printk(KERN_DEBUG "WHEEE! GC inodless node at 0x%08x succeeded\n", ref_offset(raw)));
+
+ il_out_node:
+	kfree(node);
+	return ret;
+ il_bail:
+	ret = -EBADFD;
+	goto il_out_node;
+	return 0;
+}
+
+
 static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
 					  struct jffs2_inode_cache *ic,
 					  struct jffs2_raw_node_ref *raw)
@@ -520,6 +668,8 @@
                return -ENOMEM;
 
 	ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
+	
+	if (je16_to_cpu(node->u.nodetype)==JFFS2_NODETYPE_MODELINFO) printk("YES!!!\n");
 	if (!ret && retlen != rawlen)
 		ret = -EIO;
 	if (ret)
@@ -558,7 +708,7 @@
 			       ref_offset(raw), je32_to_cpu(node->d.node_crc), crc);
 			goto bail;
 		}
-
+		
 		if (node->d.nsize) {
 			crc = crc32(0, node->d.name, node->d.nsize);
 			if (je32_to_cpu(node->d.name_crc) != crc) {
diff --unified --recursive --new-file mtd2/fs/jffs2/nodelist.c mtd/fs/jffs2/nodelist.c
--- mtd2/fs/jffs2/nodelist.c	2003-11-01 00:00:35.000000000 +0100
+++ mtd/fs/jffs2/nodelist.c	2004-10-06 10:52:09.000000000 +0200
@@ -20,6 +20,7 @@
 #include <linux/slab.h>
 #include <linux/pagemap.h>
 #include "nodelist.h"
+#include "compr.h"
 
 void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
 {
diff --unified --recursive --new-file mtd2/fs/jffs2/nodelist.h mtd/fs/jffs2/nodelist.h
--- mtd2/fs/jffs2/nodelist.h	2004-05-28 12:51:05.000000000 +0200
+++ mtd/fs/jffs2/nodelist.h	2004-10-07 12:36:23.000000000 +0200
@@ -281,9 +281,14 @@
 			else if (!ref_obsolete(ref2)) \
 				my_used_size += ref_totlen(c, jeb, ref2); \
 			if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \
-				printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
-				       ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
-				       jeb->last_node, ref_offset(jeb->last_node)); \
+                                if (!ref2->next_phys) \
+				       printk("ref for node at %p (phys %08x) has next_phys->%p (----), last_node->%p (phys %08x)\n", \
+				             ref2, ref_offset(ref2), ref2->next_phys, \
+				             jeb->last_node, ref_offset(jeb->last_node)); \
+                                else \
+                                       printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
+				             ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
+				             jeb->last_node, ref_offset(jeb->last_node)); \
 				paranoia_failed_dump(jeb); \
 				BUG(); \
 			} \
diff --unified --recursive --new-file mtd2/fs/jffs2/nodemgmt.c mtd/fs/jffs2/nodemgmt.c
--- mtd2/fs/jffs2/nodemgmt.c	2004-09-22 00:00:12.000000000 +0200
+++ mtd/fs/jffs2/nodemgmt.c	2004-10-06 17:06:56.000000000 +0200
@@ -573,6 +573,7 @@
 		ref->__totlen += n->__totlen;
 		/* we don't need to check jeb->last_node */
 		ref->next_phys = n->next_phys;
+                if (jeb->last_node == n) jeb->last_node=ref;
 		if (jeb->gc_node == n) {
 			/* gc will be happy continuing gc on this node */
 			jeb->gc_node=ref;
diff --unified --recursive --new-file mtd2/fs/jffs2/readinode.c mtd/fs/jffs2/readinode.c
--- mtd2/fs/jffs2/readinode.c	2003-11-04 00:00:36.000000000 +0100
+++ mtd/fs/jffs2/readinode.c	2004-10-06 10:52:09.000000000 +0200
@@ -19,6 +19,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/compiler.h>
 #include "nodelist.h"
+#include "compr.h"
 
 static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
 
@@ -561,7 +562,7 @@
 
 	if (!fn) {
 		/* No data nodes for this inode. */
-		if (f->inocache->ino != 1) {
+		if ((f->inocache->ino != 1) && (!jffs2_is_model_ino(c, f->inocache->ino))) {
 			printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino);
 			if (!fd_list) {
 				if (f->inocache->state == INO_STATE_READING)
diff --unified --recursive --new-file mtd2/fs/jffs2/scan.c mtd/fs/jffs2/scan.c
--- mtd2/fs/jffs2/scan.c	2004-09-13 00:00:12.000000000 +0200
+++ mtd/fs/jffs2/scan.c	2004-10-06 10:52:09.000000000 +0200
@@ -18,6 +18,7 @@
 #include <linux/crc32.h>
 #include <linux/compiler.h>
 #include "nodelist.h"
+#include "compr.h"
 
 #define EMPTY_SCAN_SIZE 1024
 
@@ -58,6 +59,11 @@
 static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
 				 struct jffs2_raw_dirent *rd, uint32_t ofs);
 
+#ifdef CONFIG_JFFS2_MODEL
+static int jffs2_scan_modelinfo_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+			  	     struct jffs2_raw_modelinfo *rd, uint32_t ofs);
+#endif
+
 #define BLK_STATE_ALLFF		0
 #define BLK_STATE_CLEAN		1
 #define BLK_STATE_PARTDIRTY	2
@@ -578,6 +584,26 @@
 			ofs += PAD(je32_to_cpu(node->totlen));
 			break;
 
+		case JFFS2_NODETYPE_MODELINFO:
+#ifdef CONFIG_JFFS2_MODEL
+			if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %d bytes (modelinfo node) left to end of buf. Reading 0x%x at 0x%08x\n",
+					  je32_to_cpu(node->totlen), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			err = jffs2_scan_modelinfo_node(c, jeb, (void *)node, ofs);
+			if (err) return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
+			break;
+#else
+                        printk(KERN_WARNING "JFFS2: you should recompile jffs2 with model file support!\n");
+#endif
+
 		default:
 			switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
 			case JFFS2_FEATURE_ROCOMPAT:
@@ -812,6 +838,61 @@
 	return 0;
 }
 
+#ifdef CONFIG_JFFS2_MODEL
+static int jffs2_scan_modelinfo_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+			  	     struct jffs2_raw_modelinfo *rm, uint32_t ofs)
+{
+	struct jffs2_raw_node_ref *raw;
+	uint32_t crc;
+        int model_num, ino, i, size;
+
+	D1(printk(KERN_DEBUG "jffs2_scan_modelinfo_node(): Node at 0x%08x\n", ofs));
+
+	/* We don't get here unless the node is still valid, so we don't have to
+	   mask in the ACCURATE bit any more. */
+	crc = crc32(0, rm, sizeof(*rm)-8);
+
+	if (crc != je32_to_cpu(rm->node_crc)) {
+		printk(KERN_NOTICE "jffs2_scan_modelinfo_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+		       ofs, je32_to_cpu(rm->node_crc), crc);
+		/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
+		DIRTY_SPACE(PAD(je32_to_cpu(rm->totlen)));
+		return 0;
+	}
+        model_num = je32_to_cpu(rm->model_num);
+	printk(KERN_DEBUG "jffs2_scan_modelinfo_node(): Found %d model inode number.\n", model_num);
+	crc = crc32(0, rm->models, model_num * 2 * sizeof(jint32_t));
+	if (crc != je32_to_cpu(rm->models_crc)) {
+		printk(KERN_NOTICE "jffs2_scan_modelinfo_node(): Models CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+		       ofs, je32_to_cpu(rm->models_crc), crc);	
+		DIRTY_SPACE(PAD(je32_to_cpu(rm->totlen)));
+		return 0;
+	}
+        for (i=0;i<model_num;i++) {
+                ino  = je32_to_cpu(rm->models[i*2]);
+		size = je32_to_cpu(rm->models[i*2+1]);
+	        printk(KERN_INFO "JFFS2 adding ino %d to modellist.\n", ino);
+                jffs2_model_add(c, ino, size);
+        }
+	raw = jffs2_alloc_raw_node_ref();
+	if (!raw) {
+		printk(KERN_NOTICE "jffs2_scan_modelinfo_node(): allocation of node reference failed\n");
+		return -ENOMEM;
+	}
+	raw->__totlen = PAD(je32_to_cpu(rm->totlen));
+	raw->flash_offset = ofs | REF_PRISTINE;
+	raw->next_phys = NULL;
+	raw->next_in_ino = NULL;
+	if (!jeb->first_node)
+		jeb->first_node = raw;
+	if (jeb->last_node)
+		jeb->last_node->next_phys = raw;
+	jeb->last_node = raw;
+	USED_SPACE(PAD(je32_to_cpu(rm->totlen)));
+	return 0;
+}
+#endif
+
 static int count_list(struct list_head *l)
 {
 	uint32_t count = 0;
diff --unified --recursive --new-file mtd2/fs/jffs2/super-v24.c mtd/fs/jffs2/super-v24.c
--- mtd2/fs/jffs2/super-v24.c	2004-07-17 00:00:11.000000000 +0200
+++ mtd/fs/jffs2/super-v24.c	2004-10-06 10:52:09.000000000 +0200
@@ -72,7 +72,11 @@
 		put_mtd_device(c->mtd);
 		return NULL;
 	}
-
+#ifdef CONFIG_JFFS2_MODEL
+        else {
+                jffs2_model_load(sb);
+        }
+#endif
 	return sb;
 }
 
@@ -82,6 +86,9 @@
 
 	D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
 
+#ifdef CONFIG_JFFS2_MODEL
+        jffs2_model_release(sb);
+#endif
 
 	if (!(sb->s_flags & MS_RDONLY))
 		jffs2_stop_garbage_collect_thread(c);
diff --unified --recursive --new-file mtd2/fs/jffs2/super.c mtd/fs/jffs2/super.c
--- mtd2/fs/jffs2/super.c	2004-08-25 00:00:14.000000000 +0200
+++ mtd/fs/jffs2/super.c	2004-10-06 10:52:09.000000000 +0200
@@ -142,6 +142,11 @@
 	}
 
 	sb->s_flags |= MS_ACTIVE;
+
+#ifdef CONFIG_JFFS2_MODEL
+        jffs2_model_load(sb);
+#endif
+
 	return sb;
 
  out_put:
@@ -259,6 +264,10 @@
 
 	D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
 
+#ifdef CONFIG_JFFS2_MODEL
+        jffs2_model_release(sb);
+#endif
+
 	if (!(sb->s_flags & MS_RDONLY))
 		jffs2_stop_garbage_collect_thread(c);
 	down(&c->alloc_sem);
diff --unified --recursive --new-file mtd2/include/linux/jffs2.h mtd/include/linux/jffs2.h
--- mtd2/include/linux/jffs2.h	2004-05-28 12:51:10.000000000 +0200
+++ mtd/include/linux/jffs2.h	2004-10-06 10:52:09.000000000 +0200
@@ -36,15 +36,6 @@
 /* How small can we sensibly write nodes? */
 #define JFFS2_MIN_DATA_LEN 128
 
-#define JFFS2_COMPR_NONE	0x00
-#define JFFS2_COMPR_ZERO	0x01
-#define JFFS2_COMPR_RTIME	0x02
-#define JFFS2_COMPR_RUBINMIPS	0x03
-#define JFFS2_COMPR_COPY	0x04
-#define JFFS2_COMPR_DYNRUBIN	0x05
-#define JFFS2_COMPR_ZLIB	0x06
-#define JFFS2_COMPR_LZO         0x07
-#define JFFS2_COMPR_LZARI       0x08
 /* Compatibility flags. */
 #define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
 #define JFFS2_NODE_ACCURATE 0x2000
@@ -61,12 +52,12 @@
 #define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
 #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 #define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
+#define JFFS2_NODETYPE_MODELINFO (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 5)
 
 // Maybe later...
 //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
 
-
 #define JFFS2_INO_FLAG_PREREAD	  1	/* Do read_inode() for this one at 
 					   mount time, don't wait for it to 
 					   happen later */
@@ -148,7 +139,22 @@
 	uint8_t data[0];
 } __attribute__((packed));
 
+
+struct jffs2_raw_modelinfo
+{
+	jint16_t magic;
+	jint16_t nodetype;	/* == JFFS_NODETYPE_MODELINFO */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t model_num;     /* number of model files */
+	jint32_t node_crc;
+	jint32_t models_crc;
+	jint32_t models[0];     /* inode and size list of the model files */
+} __attribute__((packed));
+
+
 union jffs2_node_union {
+	struct jffs2_raw_modelinfo m;
 	struct jffs2_raw_inode i;
 	struct jffs2_raw_dirent d;
 	struct jffs2_unknown_node u;
diff --unified --recursive --new-file mtd2/util/Makefile mtd/util/Makefile
--- mtd2/util/Makefile	2004-07-14 00:00:18.000000000 +0200
+++ mtd/util/Makefile	2004-10-06 10:55:01.000000000 +0200
@@ -13,9 +13,9 @@
 TARGETS = ftl_format flash_erase flash_eraseall nanddump doc_loadbios \
 	mkfs.jffs ftl_check mkfs.jffs2 flash_lock flash_unlock \
 	flash_info mtd_debug flashcp nandwrite jffs2dump \
-	nftldump nftl_format docfdisk #jffs2reader
+	nftldump nftl_format docfdisk modelgen.armlib #jffs2reader
 
-SYMLINKS = compr_lzari.c compr_lzo.c
+SYMLINKS = compr_lzari.c compr_lzo.c compr_armlib.c
 
 %: %.o
 	$(CC) $(LDFLAGS) -g -o $@ $^
@@ -36,9 +36,12 @@
 $(SYMLINKS):
 	ln -sf ../fs/jffs2/$@ $@
 
-mkfs.jffs2: crc32.o compr_rtime.o mkfs.jffs2.o compr_zlib.o compr.o compr_lzari.o compr_lzo.o
+mkfs.jffs2: crc32.o compr_rtime.o mkfs.jffs2.o compr_zlib.o compr.o compr_lzari.o compr_lzo.o compr_armlib.o
 	$(CC) $(LDFLAGS) -o $@ $^ -lz
 
+modelgen.armlib: modelgen.armlib.o
+	$(CC) $(LDFLAGS) -o $@ $^ -lm
+
 flash_eraseall: crc32.o flash_eraseall.o
 	$(CC) $(LDFLAGS) -o $@ $^
 
diff --unified --recursive --new-file mtd2/util/compr.c mtd/util/compr.c
--- mtd2/util/compr.c	2004-06-25 00:00:13.000000000 +0200
+++ mtd/util/compr.c	2004-10-06 11:26:42.000000000 +0200
@@ -15,13 +15,43 @@
 #include <string.h>
 #include <stdlib.h>
 #include <linux/jffs2.h>
+#include <sys/stat.h>
+
+/* COMPRESSOR INIT AND EXIT */
+
+/* Init and exit function headers for compressors */
+int jffs2_zlib_init(void);   void jffs2_zlib_exit(void);
+int jffs2_rtime_init(void);  void jffs2_rtime_exit(void);
+int jffs2_armlib_init(void); void jffs2_armlib_exit(void);
+int jffs2_lzari_init(void);  void jffs2_lzari_exit(void);
+int jffs2_lzo_init(void);    void jffs2_lzo_exit(void);
+
+int jffs2_compressors_init(void)
+{
+        jffs2_zlib_init();
+        jffs2_rtime_init();
+        jffs2_armlib_init();
+        jffs2_lzari_init();
+        jffs2_lzo_init();
+        return 0;
+}
+
+int jffs2_compressors_exit(void)
+{
+        jffs2_lzo_exit();
+        jffs2_lzari_exit();
+        jffs2_armlib_exit();
+        jffs2_rtime_exit();
+        jffs2_zlib_exit();
+        return 0;
+}
+
+/* END OF COMPRESSOR INIT AND EXIT */
 
-extern int page_size;
 
 /* LIST IMPLEMENTATION (from linux/list.h) */
 
 #define LIST_HEAD_INIT(name) { &(name), &(name) }
-
 #define LIST_HEAD(name) \
         struct list_head name = LIST_HEAD_INIT(name)
 
@@ -66,32 +96,31 @@
              &pos->member != (head);                                    \
              pos = list_entry(pos->member.next, typeof(*pos), member))
 
+/* END OF LIST IMPLEMENTATION (from linux/list.h) */
+
+/* GLOBAL VARIABLES */
 
+extern int page_size;
 /* Available compressors are on this list */
 static LIST_HEAD(jffs2_compressor_list);
-
+/* Available models are on this list */
+static LIST_HEAD(jffs2_model_list);
 /* Actual compression mode */
 static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
-
-void jffs2_set_compression_mode(int mode) 
-{
-        jffs2_compression_mode = mode;
-}
-
-int jffs2_get_compression_mode(void)
-{
-        return jffs2_compression_mode;
-}
-
+/* Default comprssion configuration */
+static char *jffs2_default_compression_config = DEFAULT_COMPRESSION_CONFIG;
+/* Compressor configs for files */
+static LIST_HEAD(jffs2_compression_configs);
 /* Statistics for blocks stored without compression */
 static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
-
 /* Compression test stuffs */
-
 static int jffs2_compression_check = 0;
-
 static unsigned char *jffs2_compression_check_buf = NULL;
 
+/* END OF GLOBAL VARIABLES */
+
+/* COMPRESSION TEST FUNCTIONS */
+
 void jffs2_compression_check_set(int yesno)
 {
         jffs2_compression_check = yesno;
@@ -121,7 +150,7 @@
 /* Called after compression (if compression_check is setted) to test the result */
 static void jffs2_decompression_test(struct jffs2_compressor *compr,
                                      unsigned char *data_in, unsigned char *output_buf,
-                                     uint32_t cdatalen, uint32_t datalen, uint32_t buf_size)
+                                     uint32_t cdatalen, uint32_t datalen, void *model, uint32_t buf_size)
 {
         uint32_t i;
 
@@ -150,7 +179,7 @@
                 jffs2_error_cnt++;
                 return;
         }
-        if (compr->decompress(output_buf,jffs2_compression_check_buf,cdatalen,datalen,NULL)) {
+        if (compr->decompress(output_buf,jffs2_compression_check_buf,cdatalen,datalen,model)) {
                 fprintf(stderr,"JFFS2 compression check: decompression failed at %s.\n", compr->name);
                 jffs2_error_cnt++;
         }
@@ -166,6 +195,341 @@
         }
 }
 
+/* END OF COMPRESSION TEST FUNCTIONS */
+
+/* COMPRESSOR CONFIG AND MODEL PROCESSING */
+
+static char *config_str_find_next(char *from, char d) 
+{
+        if (from==NULL) return NULL;
+        while (1) {
+                if (*from==0) return NULL;       /* end of string */
+                if ((*from==13)||(*from==10)) {  /* end of line */
+                        *from=0;
+                        return NULL;
+                }
+                if (*from==d) {                  /* delimiter found */
+                        *from=0; from++;
+                        return from;
+                }
+                from++;
+        }
+}
+
+static int jffs2_set_compression_mode_name(const char *name) 
+{
+        if (!strcmp("none",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+                return 0;
+        }
+        if (!strcmp("priority",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+                return 0;
+        }
+        if (!strcmp("size",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+                return 0;
+        }
+        return 1;
+}
+
+static int jffs2_compressor_Xable(const char *name, int disabled)
+{
+        struct jffs2_compressor *this;
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (!strcmp(this->name, name)) {
+                        this->disabled = disabled;
+                        return 0;
+                }
+        }
+        return 1;
+}
+
+static int jffs2_set_compressor_priority(const char *name, int priority)
+{
+        struct jffs2_compressor *this,*comp;
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (!strcmp(this->name, name)) {
+                        this->priority = priority;
+                        comp = this;
+                        goto reinsert;
+                }
+        }
+        fprintf(stderr,"mkfs.jffs2: compressor %s not found.\n",name);
+        return 1;
+reinsert:
+        /* list is sorted in the order of priority, so if
+           we change it we have to reinsert it into the
+           good place */
+        list_del(&comp->list);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (this->priority < comp->priority) {
+                        list_add(&comp->list, this->list.prev);
+                        return 0;
+                }
+        }
+        list_add_tail(&comp->list, &jffs2_compressor_list);
+        return 0;
+}
+
+static void jffs2_disable_compressors(void) {
+        struct jffs2_compressor *this;
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                this->disabled = 1;
+        }
+}
+
+int jffs2_activate_compression_config(const char *compr_config) 
+{
+        char *str_dup, *compr, *next_compr, *prio_str;
+        int prio;
+
+        if (compr_config==NULL) {
+                fprintf(stderr, "Compression config not found.\n");
+                return 1;
+        }
+        str_dup = (char*)malloc(strlen(compr_config)+1);
+        strcpy(str_dup,compr_config);
+        compr = str_dup;
+        jffs2_disable_compressors();
+        while (compr != NULL) {
+                next_compr = config_str_find_next(compr, ',');
+                if (jffs2_set_compression_mode_name(compr)) {
+                        prio_str = config_str_find_next(compr, ':');
+                        prio = -1; if (prio_str != NULL) prio = atoi(prio_str);
+                        if (jffs2_compressor_Xable(compr,0)) {  /* enable */
+                                fprintf(stderr,"Unknow compressor of mode %s.\n",compr);
+                                return 1;
+                        }
+                        if (prio>=0) jffs2_set_compressor_priority(compr,prio);
+                }
+                compr = next_compr;
+        }
+        free(str_dup);
+        return 0;
+}
+
+void jffs2_activate_default_compression_config(void) 
+{
+        jffs2_activate_compression_config(jffs2_default_compression_config);
+}
+
+int jffs2_set_default_compression_config(const char *config)
+{
+        if (jffs2_activate_compression_config(config)) {
+                return 1;
+        }
+        jffs2_default_compression_config = strdup(config);
+        return 0;
+}
+
+int jffs2_compression_read_config_file(const char *config_filename)
+{
+        FILE *f;
+        char line[501];
+        char *filename=NULL,*type,*compr_config;
+        struct jffs2_compression_config_for_file *c;
+        int is_model=-1;
+
+        f = fopen(config_filename,"r");
+        if (f==NULL) {
+                fprintf(stderr, "Cannot open file: %s\n",filename);
+                return 1;
+        }
+        while (!feof(f)) {
+                if (fgets(line,500,f)==NULL) break;
+                type = compr_config = NULL;
+                filename=line;
+                type = config_str_find_next(filename,';');
+                if (type!=NULL) {
+                        compr_config = config_str_find_next(type,';');
+                        if (compr_config!=NULL) config_str_find_next(compr_config,';'); /* for later compatibilty */
+                }
+                else break;
+                if (!strcmp(type, "file"))  is_model=0;
+                if (!strcmp(type, "model")) is_model=1;
+                if (is_model<0) {
+                        fprintf(stderr, "In %s type must be file or model (not %s).\n", filename, type);
+                        return 1;
+                }
+                if (jffs2_activate_compression_config(compr_config)) {
+                        fprintf(stderr,"Error processing config for file %s.\n", filename);
+                        return 1;
+                }
+                c = malloc(sizeof(*c));
+                c->compr_config = strdup(compr_config);
+                c->filename = strdup(filename);
+		c->path     = NULL;
+                c->is_model = is_model;
+                c->ino      = 0;
+		c->len      = 0;
+                list_add_tail(&c->list, &jffs2_compression_configs);
+        }
+        fclose(f);
+        jffs2_activate_default_compression_config();
+        return 0;
+}
+
+struct jffs2_compression_config_for_file *jffs2_get_compression_config_for_file(const char *name, const char *path)
+{
+        struct jffs2_compression_config_for_file *this;
+        
+        if (*name=='/') name++;
+        list_for_each_entry(this, &jffs2_compression_configs, list) {
+                if (!strcmp(this->filename, name)) {
+                        this->path = strdup(path);
+                        return this;
+                }
+        }
+        return NULL;
+}
+
+static int jffs2_model_init(struct jffs2_model *m)
+{
+        struct jffs2_compressor *this;
+        unsigned char *p;
+
+        p = m->model;
+        if ((p[0]!='J')||(p[1]!='2')||(p[2]!='M')||(p[3]!='F')) {
+                fprintf(stderr, "Bad magic (%c%c%c%c) in model file %s.\n",p[0],p[1],p[2],p[3],m->filename);
+                return 1;
+        }
+        m->compr  = p[4];
+        m->serial = p[5];
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (m->compr == this->compr) {
+                        if (!this->model_init) return 0;
+                        return this->model_init(&(m->model));
+                }
+        }
+        fprintf(stderr, "Compressor (%d) not found for model %s.\n", m->compr, m->filename);
+        return 1;
+}
+
+static int jffs2_do_load_model(const char *path, const char *filename) 
+{
+        struct stat st;
+        FILE *f;
+        char *buff;
+        struct jffs2_model *m;
+        int ret=0;
+
+        if (stat(path,&st)) return 1;
+        buff = malloc(st.st_size); if (!buff) return 2;
+        f = fopen(path,"r");       if (!f) return 3;
+        if (fread(buff,1,st.st_size,f)!=st.st_size) return 4;
+        fclose(f);
+        m = malloc(sizeof(*m));
+        if (!m) return 5;
+        m->model = buff;
+        m->filename = filename;
+        if ((ret=jffs2_model_init(m))!=0) {
+                fprintf(stderr,"Error %d at init model file %s.\n",ret,filename);
+                return ret;
+        }
+        list_add_tail(&m->list, &jffs2_model_list);
+        return 0;
+}
+
+int jffs2_model_load(void)
+{
+        int ret = 0;
+        struct jffs2_compression_config_for_file *this;
+        list_for_each_entry(this, &jffs2_compression_configs, list) {
+                if ((this->is_model) && (this->path)) {
+                        fprintf(stderr,"Load model file %s %s\n",this->path,this->filename);
+                        ret = jffs2_do_load_model(this->path,this->filename);
+                        if (ret) return ret;
+                }
+        }
+        return 0;
+}
+
+int jffs2_compressors_before_file(struct jffs2_compression_config_for_file *conf, int ino, int len)
+{
+        conf->ino = ino;
+	conf->len = len;
+        jffs2_activate_compression_config(conf->compr_config);
+        return conf->is_model;
+}
+
+/* 
+   Returns the array of inode number of the model files.
+   The first element of the array is the size of it. 
+*/
+int *jffs2_get_model_ino_array(void) 
+{
+        struct jffs2_compression_config_for_file *this;
+        int model_num = 0;
+        int *ino_array,i;
+
+        list_for_each_entry(this, &jffs2_compression_configs, list) {
+                if (this->is_model) {
+                        if (this->ino) model_num++;
+                        else fprintf(stderr,"Warning: model file %s not found.\n",this->filename);
+                }
+                else {
+                        if (!this->ino) fprintf(stderr,"Warning: file %s not found.\n",this->filename);
+                }
+        }
+        ino_array = malloc(sizeof(int)*model_num*2+1);
+        if (!ino_array) {
+                fprintf(stderr,"jffs2_get_model_ino_array(): cannot allocate memory\n");
+                return NULL;
+        }
+        i=0;
+        ino_array[i++] = model_num; 
+        list_for_each_entry(this, &jffs2_compression_configs, list) {
+                if ((this->is_model)&&(this->ino)) {
+			ino_array[i++]=this->ino;
+			ino_array[i++]=this->len;
+		}
+        }
+        return ino_array;
+}
+
+void jffs2_model_release(void)
+{
+        struct jffs2_model *this, *m;
+        struct jffs2_compressor *comp;
+
+        do {
+                m = NULL;
+                /* not the most optimal but analog with kernel */
+                list_for_each_entry(this, &jffs2_model_list, list) {
+                        m = this;
+                }
+                if (m) {
+                        list_for_each_entry(comp, &jffs2_compressor_list, list) {
+                                if (comp->compr == m->compr) {
+                                        if (comp->model_destroy) comp->model_destroy(&m->model);
+                                }
+                        }
+                        if (m->model) {
+                                /* if model_destroy did not do its job let's do it */
+                                vfree(m->model);
+                                m->model = NULL;
+                        }
+                        list_del(&m->list);
+                        kfree(m);
+                }
+        } while (m);
+}
+
+static struct jffs2_model *jffs2_model_get(unsigned char compr, unsigned char min_serial)
+{
+        struct jffs2_model *this;
+
+        list_for_each_entry(this, &jffs2_model_list, list) {
+                if ((this->compr == compr) && (this->serial >= min_serial)) {
+                        return this;
+                }
+        }
+        return NULL;
+}
+
+/* END OF COMPRESSOR CONFIG AND MODEL PROCESSING */
+
 /* jffs2_compress:
  * @data: Pointer to uncompressed data
  * @cdata: Pointer to returned pointer to buffer for compressed data
@@ -193,6 +557,7 @@
         unsigned char *output_buf = NULL, *tmp_buf;
         uint32_t orig_slen, orig_dlen;
         uint32_t best_slen=0, best_dlen=0;
+        struct jffs2_model *m; int serial, best_serial=0;
 
         switch (jffs2_compression_mode) {
         case JFFS2_COMPR_MODE_NONE:
@@ -210,23 +575,52 @@
                         if ((!this->compress)||(this->disabled))
                                 continue;
 
-                        this->usecount++;
-
-                        if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */
-                                jffs2_decompression_test_prepare(output_buf, orig_dlen);
-
-                        *datalen  = orig_slen;
-                        *cdatalen = orig_dlen;
-                        compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
-                        this->usecount--;
-                        if (!compr_ret) {
-                                ret = this->compr;
-                                this->stat_compr_blocks++;
-                                this->stat_compr_orig_size += *datalen;
-                                this->stat_compr_new_size  += *cdatalen;
-                                if (jffs2_compression_check)
-                                        jffs2_decompression_test(this, data_in, output_buf, *cdatalen, *datalen, orig_dlen);
-                                break;
+                        if (!(this->compr & 0x80)) {  
+                                /* OK, this is a non-model-based compressor */
+                                if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */
+                                        jffs2_decompression_test_prepare(output_buf, orig_dlen);
+
+                                *datalen  = orig_slen;
+                                *cdatalen = orig_dlen;
+                                this->usecount++;
+                                compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
+                                this->usecount--;
+                                if (!compr_ret) {
+                                        ret = this->compr;
+                                        this->stat_compr_blocks++;
+                                        this->stat_compr_orig_size += *datalen;
+                                        this->stat_compr_new_size  += *cdatalen;
+                                        if (jffs2_compression_check)
+                                                jffs2_decompression_test(this, data_in, output_buf, *cdatalen, *datalen, NULL, orig_dlen);
+                                        break;
+                                }
+                        }
+                        else {  
+                                /* It is model-based compressor, let's search model for it */
+                                serial = 0;
+                                m = jffs2_model_get(this->compr,serial);
+                                while (m) {
+                                        if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */
+                                                jffs2_decompression_test_prepare(output_buf, orig_dlen);
+
+                                        *datalen  = orig_slen;
+                                        *cdatalen = orig_dlen;
+                                        this->usecount++;
+                                        compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, m->model);
+                                        this->usecount--;
+                                        if (!compr_ret) {
+                                                ret = (uint16_t)this->compr | ((uint16_t)m->serial << 8);
+                                                this->stat_compr_blocks++;
+                                                this->stat_compr_orig_size += *datalen;
+                                                this->stat_compr_new_size  += *cdatalen;
+                                                if (jffs2_compression_check)
+                                                        jffs2_decompression_test(this, data_in, output_buf, *cdatalen, *datalen, m->model, orig_dlen);
+                                                break;
+                                        }
+                                        serial = m->serial+1;
+                                        m = jffs2_model_get(this->compr,serial);
+                                }
+                                if (ret!=JFFS2_COMPR_NONE) break;
                         }
                 }
                 if (ret == JFFS2_COMPR_NONE) free(output_buf);
@@ -255,20 +649,50 @@
                                         this->compr_buf_size = orig_dlen;
                                 }
                         }
-                        this->usecount++;
-                        if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */
-                                jffs2_decompression_test_prepare(this->compr_buf,this->compr_buf_size);
-                        *datalen  = orig_slen;
-                        *cdatalen = orig_dlen;
-                        compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
-                        this->usecount--;
-                        if (!compr_ret) {
-                                if (jffs2_compression_check)
-                                    jffs2_decompression_test(this, data_in, this->compr_buf, *cdatalen, *datalen, this->compr_buf_size);
-                                if ((!best_dlen)||(best_dlen>*cdatalen)) {
-                                        best_dlen = *cdatalen;
-                                        best_slen = *datalen;
-                                        best = this;
+                        
+                        if (!(this->compr & 0x80)) {
+                                /* OK, this is a non-model-based compressor */
+                                if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */
+                                        jffs2_decompression_test_prepare(this->compr_buf,this->compr_buf_size);
+                                *datalen  = orig_slen;
+                                *cdatalen = orig_dlen;
+                                this->usecount++;
+                                compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
+                                this->usecount--;
+                                if (!compr_ret) {
+                                        if (jffs2_compression_check)
+                                            jffs2_decompression_test(this, data_in, this->compr_buf, *cdatalen, *datalen, NULL, this->compr_buf_size);
+                                        if ((!best_dlen)||(best_dlen>*cdatalen)) {
+                                                best_dlen = *cdatalen;
+                                                best_slen = *datalen;
+                                                best = this;
+                                        }
+                                }
+                        }
+                        else {
+                                /* It is model-based compressor, let's search model for it */
+                                serial = 0;
+                                m  = jffs2_model_get(this->compr,serial);
+                                while (m) {                                        
+                                        if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */
+                                                jffs2_decompression_test_prepare(this->compr_buf,this->compr_buf_size);
+                                        *datalen  = orig_slen;
+                                        *cdatalen = orig_dlen;
+                                        this->usecount++;
+                                        compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, m->model);
+                                        this->usecount--;
+                                        if (!compr_ret) {
+                                                if (jffs2_compression_check)
+                                                    jffs2_decompression_test(this, data_in, this->compr_buf, *cdatalen, *datalen, m->model, this->compr_buf_size);
+                                                if ((!best_dlen)||(best_dlen>*cdatalen)) {
+                                                        best_dlen = *cdatalen;
+                                                        best_slen = *datalen;
+                                                        best = this;
+                                                        best_serial = m->serial;
+                                                }
+                                        }
+                                        serial = m->serial+1;
+                                        m = jffs2_model_get(this->compr,serial);
                                 }
                         }
                 }
@@ -281,7 +705,7 @@
                         best->stat_compr_blocks++;
                         best->stat_compr_orig_size += best_slen;
                         best->stat_compr_new_size  += best_dlen;
-                        ret = best->compr;
+                        ret = (uint16_t)best->compr | ((uint16_t)best_serial << 8);
                 }
                 break;
         default:
@@ -300,6 +724,7 @@
 	return ret;
 }
 
+/* COMPRESSOR (UN)REGISTRATION */
 
 int jffs2_register_compressor(struct jffs2_compressor *comp)
 {
@@ -332,7 +757,7 @@
 {
 
         if (comp->usecount) {
-                fprintf(stderr,"mkfs.jffs2: Compressor modul is in use. Unregister failed.\n");
+                fprintf(stderr,"mkfs.jffs2: Compressor modul %s is in use. Unregister failed.\n",comp->name);
                 return -1;
         }
         list_del(&comp->list);
@@ -340,6 +765,10 @@
         return 0;
 }
 
+/* END OF COMPRESSOR (UN)REGISTRATION */
+
+/* COMPRESSOR STATISTICS */
+
 #define JFFS2_STAT_BUF_SIZE 16000
 
 char *jffs2_list_compressors(void)
@@ -366,21 +795,7 @@
 
         act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE);
 
-        act_buf += sprintf(act_buf,"Compression mode: ");
-        switch (jffs2_compression_mode) {
-        case JFFS2_COMPR_MODE_NONE:
-                act_buf += sprintf(act_buf,"none");
-                break;
-        case JFFS2_COMPR_MODE_PRIORITY:
-                act_buf += sprintf(act_buf,"priority");
-                break;
-        case JFFS2_COMPR_MODE_SIZE:
-                act_buf += sprintf(act_buf,"size");
-                break;
-        default:
-                act_buf += sprintf(act_buf,"unkown");
-                break;
-        }
+        act_buf += sprintf(act_buf,"Default compression config:\n    %s\n",jffs2_default_compression_config);
         act_buf += sprintf(act_buf,"\nCompressors:\n");
         act_buf += sprintf(act_buf,"%10s             ","none");
         act_buf += sprintf(act_buf,"compr: %d blocks (%d)  decompr: %d blocks\n", none_stat_compr_blocks, 
@@ -399,103 +814,4 @@
         return buf;
 }
 
-int jffs2_set_compression_mode_name(const char *name) 
-{
-        if (!strcmp("none",name)) {
-                jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
-                return 0;
-        }
-        if (!strcmp("priority",name)) {
-                jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
-                return 0;
-        }
-        if (!strcmp("size",name)) {
-                jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
-                return 0;
-        }
-        return 1;
-}
-
-static int jffs2_compressor_Xable(const char *name, int disabled)
-{
-        struct jffs2_compressor *this;
-        list_for_each_entry(this, &jffs2_compressor_list, list) {
-                if (!strcmp(this->name, name)) {
-                        this->disabled = disabled;
-                        return 0;
-                }
-        }
-        return 1;
-}
-
-int jffs2_enable_compressor_name(const char *name)
-{
-        return jffs2_compressor_Xable(name, 0);
-}
-
-int jffs2_disable_compressor_name(const char *name)
-{
-        return jffs2_compressor_Xable(name, 1);
-}
-
-int jffs2_set_compressor_priority(const char *name, int priority)
-{
-        struct jffs2_compressor *this,*comp;
-        list_for_each_entry(this, &jffs2_compressor_list, list) {
-                if (!strcmp(this->name, name)) {
-                        this->priority = priority;
-                        comp = this;
-                        goto reinsert;
-                }
-        }
-        fprintf(stderr,"mkfs.jffs2: compressor %s not found.\n",name);
-        return 1;
-reinsert:
-        /* list is sorted in the order of priority, so if
-           we change it we have to reinsert it into the
-           good place */
-        list_del(&comp->list);
-        list_for_each_entry(this, &jffs2_compressor_list, list) {
-                if (this->priority < comp->priority) {
-                        list_add(&comp->list, this->list.prev);
-                        return 0;
-                }
-        }
-        list_add_tail(&comp->list, &jffs2_compressor_list);
-        return 0;
-}
-
-
-int jffs2_compressors_init(void)
-{
-#ifdef CONFIG_JFFS2_ZLIB
-        jffs2_zlib_init();
-#endif
-#ifdef CONFIG_JFFS2_RTIME
-        jffs2_rtime_init();
-#endif
-#ifdef CONFIG_JFFS2_LZARI
-        jffs2_lzari_init();
-#endif
-#ifdef CONFIG_JFFS2_LZO
-        jffs2_lzo_init();
-#endif
-        return 0;
-}
-
-int jffs2_compressors_exit(void)
-{
-#ifdef CONFIG_JFFS2_LZO
-        jffs2_lzo_exit();
-#endif
-#ifdef CONFIG_JFFS2_LZARI
-        jffs2_lzari_exit();
-#endif
-#ifdef CONFIG_JFFS2_RTIME
-        jffs2_rtime_exit();
-#endif
-#ifdef CONFIG_JFFS2_ZLIB
-        jffs2_zlib_exit();
-#endif
-        return 0;
-}
+/* END OF COMPRESSOR STATISTICS */
diff --unified --recursive --new-file mtd2/util/compr.h mtd/util/compr.h
--- mtd2/util/compr.h	2004-06-25 00:00:13.000000000 +0200
+++ mtd/util/compr.h	2004-10-06 10:56:02.000000000 +0200
@@ -17,21 +17,28 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
-#include "linux/jffs2.h"
 
-#define CONFIG_JFFS2_ZLIB
-#define CONFIG_JFFS2_RTIME
-#define CONFIG_JFFS2_LZARI
-#define CONFIG_JFFS2_LZO
-#define JFFS2_LZARI_DISABLED
-#define JFFS2_LZO_DISABLED
-
-#define JFFS2_RUBINMIPS_PRIORITY 10
-#define JFFS2_DYNRUBIN_PRIORITY  20
-#define JFFS2_LZARI_PRIORITY     30
-#define JFFS2_LZO_PRIORITY       40
-#define JFFS2_RTIME_PRIORITY     50
-#define JFFS2_ZLIB_PRIORITY      60
+#define JFFS2_COMPR_NONE	0x00
+#define JFFS2_COMPR_ZERO	0x01
+#define JFFS2_COMPR_RTIME	0x02
+#define JFFS2_COMPR_RUBINMIPS	0x03
+#define JFFS2_COMPR_COPY	0x04
+#define JFFS2_COMPR_DYNRUBIN	0x05
+#define JFFS2_COMPR_ZLIB	0x06
+#define JFFS2_COMPR_LZO         0x07
+#define JFFS2_COMPR_LZARI       0x08
+/* Model based compressors */
+#define JFFS2_COMPR_ARMLIB      0x81
+
+#define DEFAULT_COMPRESSION_CONFIG     "priority,zlib:70,rtime:60"
+
+/* Default compressor priorities (compression config overrides them) */
+#define CONFIG_JFFS2_RUBIN_PRIORITY     20
+#define CONFIG_JFFS2_LZARI_PRIORITY     30
+#define CONFIG_JFFS2_LZO_PRIORITY       40
+#define CONFIG_JFFS2_ARMLIB_PRIORITY    50
+#define CONFIG_JFFS2_RTIME_PRIORITY     60
+#define CONFIG_JFFS2_ZLIB_PRIORITY      70
 
 #define JFFS2_COMPR_MODE_NONE       0
 #define JFFS2_COMPR_MODE_PRIORITY   1
@@ -61,24 +68,20 @@
         struct list_head *next, *prev;
 };
 
-void jffs2_set_compression_mode(int mode);
-int jffs2_get_compression_mode(void);
-int jffs2_set_compression_mode_name(const char *mode_name);
-
-int jffs2_enable_compressor_name(const char *name);
-int jffs2_disable_compressor_name(const char *name);
-
-int jffs2_set_compressor_priority(const char *name, int priority);
-
 struct jffs2_compressor {
         struct list_head list;
         int priority;             /* used by prirority comr. mode */
         char *name;
-        char compr;               /* JFFS2_COMPR_XXX */
+        unsigned char compr;      /* JFFS2_COMPR_XXX */
         int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
                         uint32_t *srclen, uint32_t *destlen, void *model);
         int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
                         uint32_t cdatalen, uint32_t datalen, void *model);
+        int (*model_init)(void **model);
+                                   /* called to init model data */
+	int (*model_destroy)(void **model);
+                                   /* called to free model data,
+                                      it should set *model to NULL */
         int usecount;
         int disabled;             /* if seted the compressor won't compress */
         unsigned char *compr_buf; /* used by size compr. mode */
@@ -89,6 +92,26 @@
         uint32_t stat_decompr_blocks;
 };
 
+/* Structure for model files and regular files which will compressed 
+   by not the default compressor config */
+struct jffs2_compression_config_for_file {
+        struct list_head list;
+        char *compr_config;
+        const char *filename;
+	const char *path;           /* filled by jffs2_get_compression_config_for_file() */
+        int is_model;
+        int ino;              /* filled by jffs2_compressors_before_file() */
+        int len;              /* filled by jffs2_compressors_before_file() */
+};
+
+struct jffs2_model {
+        struct list_head list;
+        const char *filename;
+        void *model;                   /* model information */
+        unsigned char compr;           /* JFFS2_COMPR_XXX */
+        unsigned char serial;
+};
+
 int jffs2_register_compressor(struct jffs2_compressor *comp);
 int jffs2_unregister_compressor(struct jffs2_compressor *comp);
 
@@ -98,6 +121,21 @@
 uint16_t jffs2_compress(unsigned char *data_in, unsigned char **cpage_out,
                              uint32_t *datalen, uint32_t *cdatalen);
 
+int jffs2_compression_read_config_file(const char *filename);
+int jffs2_set_default_compression_config(const char *config);
+
+int jffs2_activate_compression_config(const char *config);
+void jffs2_activate_default_compression_config(void);
+
+struct jffs2_compression_config_for_file *jffs2_get_compression_config_for_file(const char *name, const char *path);
+int jffs2_model_load(void);
+void jffs2_model_release(void);
+/* Sets the corresponding compressor config and fill the ino field 
+   Returns 1 if the file is a model file, otherwise 0 */
+int jffs2_compressors_before_file(struct jffs2_compression_config_for_file *conf, int ino, int len);
+/* Returns the inodes of the model files */
+int *jffs2_get_model_ino_array(void);
+
 /* If it is setted, a decompress will be called after every compress */
 void jffs2_compression_check_set(int yesno);
 int jffs2_compression_check_get(void);
@@ -106,24 +144,4 @@
 char *jffs2_list_compressors(void);
 char *jffs2_stats(void);
 
-/* Compressor modules */
-
-/* These functions will be called by jffs2_compressors_init/exit */
-#ifdef CONFIG_JFFS2_ZLIB
-int jffs2_zlib_init(void);
-void jffs2_zlib_exit(void);
-#endif
-#ifdef CONFIG_JFFS2_RTIME
-int jffs2_rtime_init(void);
-void jffs2_rtime_exit(void);
-#endif
-#ifdef CONFIG_JFFS2_LZARI
-int jffs2_lzari_init(void);
-void jffs2_lzari_exit(void);
-#endif
-#ifdef CONFIG_JFFS2_LZO
-int jffs2_lzo_init(void);
-void jffs2_lzo_exit(void);
-#endif
-
 #endif /* __JFFS2_COMPR_H__ */
diff --unified --recursive --new-file mtd2/util/compr_rtime.c mtd/util/compr_rtime.c
--- mtd2/util/compr_rtime.c	2004-06-24 00:00:14.000000000 +0200
+++ mtd/util/compr_rtime.c	2004-10-06 10:52:09.000000000 +0200
@@ -103,7 +103,7 @@
 
 
 static struct jffs2_compressor jffs2_rtime_comp = {
-    .priority = JFFS2_RTIME_PRIORITY,
+    .priority = CONFIG_JFFS2_RTIME_PRIORITY,
     .name = "rtime",
     .disabled = 0,
     .compr = JFFS2_COMPR_RTIME,
diff --unified --recursive --new-file mtd2/util/compr_zlib.c mtd/util/compr_zlib.c
--- mtd2/util/compr_zlib.c	2004-06-24 00:00:14.000000000 +0200
+++ mtd/util/compr_zlib.c	2004-10-06 10:52:09.000000000 +0200
@@ -129,7 +129,7 @@
 }
 
 static struct jffs2_compressor jffs2_zlib_comp = {
-    .priority = JFFS2_ZLIB_PRIORITY,
+    .priority = CONFIG_JFFS2_ZLIB_PRIORITY,
     .name = "zlib",
     .disabled = 0,
     .compr = JFFS2_COMPR_ZLIB,
diff --unified --recursive --new-file mtd2/util/config mtd/util/config
--- mtd2/util/config	1970-01-01 01:00:00.000000000 +0100
+++ mtd/util/config	2004-10-06 12:11:01.000000000 +0200
@@ -0,0 +1 @@
+model.armlib;model;zlib:60,rtime:50
diff --unified --recursive --new-file mtd2/util/jffs2dump.c mtd/util/jffs2dump.c
--- mtd2/util/jffs2dump.c	2004-06-20 00:00:57.000000000 +0200
+++ mtd/util/jffs2dump.c	2004-10-06 10:52:09.000000000 +0200
@@ -185,6 +185,7 @@
 	uint16_t		type;
 	int			bitchbitmask = 0;
 	int			obsolete;
+        uint32_t                i;
 
 	while ( p < (data + imglen)) {
 		node = (union jffs2_node_union*) p;
@@ -277,6 +278,37 @@
 
 			p += PAD(je32_to_cpu (node->d.totlen));						
 			break;
+
+		case JFFS2_NODETYPE_MODELINFO:
+			printf ("%8s Modelinfo  node at 0x%08x, totlen 0x%08x, model_num %2d,  models: ",
+					obsolete ? "Obsolete" : "",
+					p - data, je32_to_cpu (node->m.totlen),
+					je32_to_cpu(node->m.model_num));
+                        for (i=0 ; i<je32_to_cpu(node->m.model_num) ; i++) {
+                                printf("ino=%d,size=%d ",
+                                        je32_to_cpu(node->m.models[i*2]),
+                                        je32_to_cpu(node->m.models[i*2+1]));
+                        }
+                        printf("\n");
+
+			crc = crc32 (0, node, sizeof (struct jffs2_raw_modelinfo) - 8);
+			if (crc != je32_to_cpu (node->m.node_crc)) {
+				printf ("Wrong node_crc at  0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->m.node_crc), crc);
+				p += PAD(je32_to_cpu (node->m.totlen));
+				dirty += PAD(je32_to_cpu (node->m.totlen));;
+				continue;
+			}
+			
+			crc = crc32(0, p + sizeof (struct jffs2_raw_modelinfo), je32_to_cpu(node->m.model_num)*2*sizeof(jint32_t));
+			if (crc != je32_to_cpu(node->m.models_crc)) {
+				printf ("Wrong name_crc at  0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->m.models_crc), crc);
+				p += PAD(je32_to_cpu (node->m.totlen));
+				dirty += PAD(je32_to_cpu (node->m.totlen));;
+				continue;
+			}
+
+			p += PAD(je32_to_cpu (node->m.totlen));
+			break;
 	
 		case JFFS2_NODETYPE_CLEANMARKER:
 			if (verbose) {
@@ -325,8 +357,8 @@
 	char			*p = data;
 	union jffs2_node_union 	*node, newnode;
 	int			fd, len;
-	jint32_t		mode;
-	uint32_t		crc;
+	jint32_t		mode, m_ino, m_size;
+	uint32_t		crc, i;
 	
 	fd = open (cnvfile, O_WRONLY | O_CREAT, 0644);
 	if (fd < 0) {
@@ -418,9 +450,29 @@
 
 			write (fd, &newnode, sizeof (struct jffs2_raw_dirent));
 			write (fd, p + sizeof (struct jffs2_raw_dirent), PAD (je32_to_cpu (node->d.totlen) -  sizeof (struct jffs2_raw_dirent)));
-			p += PAD(je32_to_cpu (node->d.totlen));						
+			p += PAD(je32_to_cpu (node->d.totlen));
 			break;
 	
+		case JFFS2_NODETYPE_MODELINFO:
+			newnode.m.magic = cnv_e16 (node->m.magic);
+			newnode.m.nodetype = cnv_e16 (node->m.nodetype);
+			newnode.m.totlen = cnv_e32 (node->m.totlen);
+			newnode.m.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));
+			newnode.m.model_num = cnv_e32 (node->m.model_num);
+			if (recalccrc)
+				newnode.m.models_crc = cpu_to_e32 ( crc32(0, p + sizeof (struct jffs2_raw_modelinfo), 2 * je32_to_cpu(node->m.model_num) * sizeof(jint32_t)));
+			else
+				newnode.m.models_crc = cnv_e32 (node->m.models_crc);
+			write (fd, &newnode, sizeof (struct jffs2_raw_modelinfo));
+                        for (i=0 ; i < je32_to_cpu(node->m.model_num) ; i++) {
+                                m_ino  = cnv_e32 (node->m.models[i*2]);
+                                m_size = cnv_e32 (node->m.models[i*2+1]);
+			        write (fd, &m_ino, sizeof(m_ino));
+			        write (fd, &m_size, sizeof(m_size));
+                        }
+			p += PAD(je32_to_cpu (node->m.totlen));
+			break;
+                        
 		case JFFS2_NODETYPE_CLEANMARKER:
 		case JFFS2_NODETYPE_PADDING:
 			newnode.u.magic = cnv_e16 (node->u.magic);
diff --unified --recursive --new-file mtd2/util/mkfs.jffs2.1 mtd/util/mkfs.jffs2.1
--- mtd2/util/mkfs.jffs2.1	2004-05-28 12:51:11.000000000 +0200
+++ mtd/util/mkfs.jffs2.1	2004-10-06 10:52:09.000000000 +0200
@@ -49,16 +49,10 @@
 .B -P,--squash-perms
 ]
 [
-.B -m,--compression-mode=MODE
+.B -C,--default-compr-config=CONFIG
 ]
 [
-.B -x,--disable-compressor=NAME
-]
-[
-.B -X,--enable-compressor=NAME
-]
-[
-.B -y,--compressor-priority=PRIORITY:NAME
+.B -F,--compressor-config-file=FILENAME
 ]
 [
 .B -L,--list-compressors
@@ -173,31 +167,45 @@
 .B -P, --squash-perms
 Squash permissions, removing write permission for \'group\' and \'other\'.
 .TP
-.B -m, --compression-mode=MODE
-Set the default compression mode. The default mode is 
-.B priority 
-which tries the compressors in a predefinied order and chooses the first
-successful one. The alternatives are:
+.B -C, --default-compr-config=CONFIG
+Set the default compression configuration. The CONFIG is a comma separated
+string. Every word in this string can be the name of a compressor
+or the name of a compression mode:
 .B none
-(mkfs will not compress) and
+(mkfs will not compress),
+.B priority 
+(which tries the compressors in priority order and chooses the first
+successful one) or
 .B size
-(mkfs will try all compressor and chooses the one which have the smallest result).
-.TP
-.B -x, --disable-compressor=NAME
-Disable a compressor. Use
-.B -L
-to see the list of the avaiable compressors and their default states.
-.TP
-.B -X, --enable-compressor=NAME
-Enable a compressor. Use
-.B -L
-to see the list of the avaiable compressors and their default states.
-.TP
-.B -y, --compressor-priority=PRIORITY:NAME
-Set the priority of a compressor. Use
-.B -L
-to see the list of the avaiable compressors and their default priority.
-Priorities are used by priority compression mode.
+(mkfs will try all compressors and chooses the one which have the smallest result).
+The name of a compressor can be in format 
+.B compressor_name 
+or
+.B compressor_name:PRIORITY
+, where PRIORITY is an integer.
+All none declarated compressors will be disabled.
+The default config is "priority,zlib:70,rtime:60".
+.TP
+.B -F, --compressor-config-file=FILENAME
+Specifies a compression config file. In this config file every line must be
+in format "FILENAME;TYPE;COMPRESSION_CONFIG", where
+.B FILENAME
+is a relative file name, 
+.B TYPE
+can be only one of the 
+.B file
+or
+.B model
+keywords, and 
+.B COMPRESSOIN_CONFIG
+must be a valid compression configuration (see 
+.B -C 
+option!). This specified file will be compressed the specified compressor
+configuration. All of the non specified files will be compressed by
+the default compression configuration.
+For example a line of this config file can be: 
+"boot/zImage;file;priority,zlib:60,rtime:50", an other 
+example: "model.armlib;model;zlib:60".
 .TP
 .B -L, --list-compressors
 Show the list of the avaiable compressors and their states.
diff --unified --recursive --new-file mtd2/util/mkfs.jffs2.c mtd/util/mkfs.jffs2.c
--- mtd2/util/mkfs.jffs2.c	2004-05-28 12:51:11.000000000 +0200
+++ mtd/util/mkfs.jffs2.c	2004-10-06 11:32:23.000000000 +0200
@@ -6,6 +6,8 @@
  *           2001 David A. Schleef <ds@xxxxxxx.com>
  *           2002 Axis Communications AB
  *           2001, 2002 Erik Andersen <andersen@xxxxxxx.org>
+ *           2004 Ferenc Havasi <havasi@xxxxxxx.hu>,
+ *                University of Szeged, Hungary
  *
  * 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
@@ -76,6 +78,8 @@
 #define mkfs_debug_msg(a...)	{ }
 #define min(x,y) ({ typeof((x)) _x = (x); typeof((y)) _y = (y); (_x>_y)?_y:_x; })
 
+#include "compr.h"
+
 struct filesystem_entry {
 	char *name;					/* Name of this directory (think basename) */
 	char *path;					/* Path of this directory (think dirname) */
@@ -87,6 +91,7 @@
 	struct filesystem_entry *prev;	/* Only relevant to non-directories */
 	struct filesystem_entry *next;	/* Only relevant to non-directories */
 	struct filesystem_entry *files;	/* Only relevant to directories */
+        struct jffs2_compression_config_for_file *compr_config; /* If it is NULL the default compressor config will be used */
 };
 
 
@@ -337,6 +342,8 @@
 		entry->sb.st_size = strlen(entry->link);
 	}
 
+        entry->compr_config = jffs2_get_compression_config_for_file(name, path);
+
 	/* This happens only for root */
 	if (!parent)
 		return (entry);
@@ -666,7 +673,6 @@
  * 64-bit arches and whatnot, use the --pagesize=SIZE option */
 int page_size = 4096;
 
-#include "compr.h"
 
 static void full_write(int fd, const void *buf, int len)
 {
@@ -791,7 +797,17 @@
 	mkfs_debug_msg("writing file '%s'  ino=%lu  parent_ino=%lu", 
 			e->name, (unsigned long) statbuf->st_ino, 
 			(unsigned long) e->parent->sb.st_ino);
-	write_dirent(e);
+
+        if (e->compr_config) {
+                /* It sets the special compression config if specified for this file.
+                   If it is a model file we don't store its dirent. */
+                if (!jffs2_compressors_before_file(e->compr_config, statbuf->st_ino, statbuf->st_size)) {
+	                write_dirent(e);
+                }
+        }
+        else {
+	        write_dirent(e);
+        }
 
 	buf = xmalloc(page_size);
 	cbuf = NULL;
@@ -881,6 +897,8 @@
 	}
 	free(buf);
 	close(fd);
+
+        if (e->compr_config) jffs2_activate_default_compression_config();
 }
 
 static void write_symlink(struct filesystem_entry *e)
@@ -1105,6 +1123,54 @@
 	}
 }
 
+static void write_modelinfo()
+{
+        int *model_inodes;
+	struct jffs2_raw_modelinfo *rm;
+        unsigned int model_num, fullsize, i;
+
+        model_inodes = jffs2_get_model_ino_array();
+        if (!model_inodes) return;
+        if (model_inodes[0] == 0) {
+                free(model_inodes);
+                return;
+        }
+
+        model_num = model_inodes[0];
+        fullsize = sizeof(*rm) + model_num * sizeof(int) * 2;
+
+        rm = malloc(fullsize);
+
+	memset(rm, 0, fullsize);
+
+	rm->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rm->nodetype = cpu_to_je16(JFFS2_NODETYPE_MODELINFO);
+	rm->totlen = cpu_to_je32(fullsize);
+
+	rm->hdr_crc = cpu_to_je32(crc32(0, rm, 
+				sizeof(struct jffs2_unknown_node) - 4));
+	
+        rm->model_num = cpu_to_je32(model_num);
+	
+	rm->node_crc = cpu_to_je32(crc32(0, rm, sizeof(*rm) - 8));
+        
+        if (verbose) fprintf(stderr,"\nModel file inodes: ");
+        for (i=0;i<model_num;i++) {
+                if (verbose) fprintf(stderr,"%d ",model_inodes[i*2+1]);
+                rm->models[i*2] = cpu_to_je32(model_inodes[i*2+1]);
+                rm->models[i*2+1] = cpu_to_je32(model_inodes[i*2+2]);
+        }
+        if (verbose) fprintf(stderr,"\n");
+
+	rm->models_crc = cpu_to_je32(crc32(0, &(rm->models[0]), 2 * model_num * sizeof(int)));
+
+	pad_block_if_less_than(fullsize);
+	full_write(out_fd, rm, fullsize);
+	free(rm);
+	padword();
+        free(model_inodes);
+}
+
 static void create_target_filesystem(struct filesystem_entry *root)
 {
 	cleanmarker.magic    = cpu_to_je16(JFFS2_MAGIC_BITMASK);
@@ -1115,6 +1181,8 @@
 	ino = root->sb.st_ino = 1;
 	recursive_populate_directory(root);
 
+        write_modelinfo();
+
 	if (pad_fs_size == -1) {
 		padblock();
 	} else {
@@ -1152,8 +1220,8 @@
 	{"squash-perms", 0, NULL, 'P'},
 	{"faketime", 0, NULL, 'f'},
 	{"devtable", 1, NULL, 'D'},
-	{"compression-mode", 1, NULL, 'm'},
-	{"disable-compressor", 1, NULL, 'x'},
+	{"default-compr-config", 1, NULL, 'C'},
+	{"compressor-config-file", 1, NULL, 'F'},
 	{"test-compression", 0, NULL, 't'},
 	{"compressor-priority", 1, NULL, 'y'},
 	{NULL, 0, NULL, 0}
@@ -1170,13 +1238,13 @@
 	"  -s, --pagesize=SIZE     Use page size (max data node size) SIZE (default: 4KiB)\n"
 	"  -e, --eraseblock=SIZE   Use erase block size SIZE (default: 64KiB)\n"
 	"  -c, --cleanmarker=SIZE  Size of cleanmarker (default 12)\n"
-	"  -m, --compr-mode=MODE   Select compression mode (default: priortiry)\n"
-        "  -x, --disable-compressor=COMPRESSOR_NAME\n"
-        "                          Disable a compressor\n"
-        "  -X, --enable-compressor=COMPRESSOR_NAME\n"
-        "                          Enable a compressor\n"
-        "  -y, --compressor-priority=PRIORITY:COMPRESSOR_NAME\n"
-        "                          Set the priority of a compressor\n"
+	"  -C, --default-compr-config=MODE\n"
+        "                          Select default compression mode & active compressor\n"
+        "                          Default value: \"priority,zlib:60,rtime:50\"\n"
+        "  -F, --compressor-config-file=FILE\n"
+        "                          The name of the compressor config file to define\n"
+        "                          different compressor configuration for files.\n"
+        "                          Default: not used.\n"
         "  -L, --list-compressors  Show the list of the avaiable compressors\n"
         "  -t, --test-compression  Call decompress and compare with the original (for test)\n"
 	"  -n, --no-cleanmarkers   Don't add a cleanmarker to every eraseblock\n"
@@ -1193,7 +1261,7 @@
 	"  -V, --version           Display version information\n\n";
 
 
-static char *revtext = "$Revision: 1.42 $";
+static char *revtext = "$Revision: 1.43 $";
 
 int main(int argc, char **argv)
 {
@@ -1202,13 +1270,11 @@
 	struct stat sb;
 	FILE *devtable = NULL;
 	struct filesystem_entry *root;
-        char *compr_name = NULL;
-        int compr_prior  = -1;
 
         jffs2_compressors_init();
 
 	while ((opt = getopt_long(argc, argv, 
-					"D:d:r:s:o:qUPfh?vVe:lbp::nc:m:x:X:Lty:", long_options, &c)) >= 0) 
+					"D:d:r:s:o:qUPfh?vVe:lbp::nc:C:F:Lt", long_options, &c)) >= 0) 
 	{
 		switch (opt) {
 			case 'D':
@@ -1316,9 +1382,11 @@
 				else
 					pad_fs_size = -1;
 				break;
+
 			case 'n':
 				add_cleanmarkers = 0;
 				break;
+
 			case 'c':
 				cleanmarker_size = strtol(optarg, NULL, 0);
 				if (cleanmarker_size < sizeof(cleanmarker)) {
@@ -1328,39 +1396,26 @@
 					error_msg_and_die("cleanmarker size must be < eraseblock size");
 				}
 				break;
-                        case 'm':
-                                if (jffs2_set_compression_mode_name(optarg)) {
-					error_msg_and_die("Unknown compression mode %s", optarg);
+                        case 'C':
+                                if (jffs2_set_default_compression_config(optarg)) {
+					error_msg_and_die("Invalid compression configuration \"%s\"", optarg);
 				}
                                 break;
-                        case 'x':
-                                if (jffs2_disable_compressor_name(optarg)) {
-                                        error_msg_and_die("Unknown compressor name %s",optarg);
-                                }
-                                break;
-                        case 'X':
-                                if (jffs2_enable_compressor_name(optarg)) {
-                                        error_msg_and_die("Unknown compressor name %s",optarg);
-                                }
-                                break;
+
                         case 'L':
                                 error_msg_and_die("\n%s",jffs2_list_compressors());
                                 break;
+
                         case 't':
                                 jffs2_compression_check_set(1);
                                 break;
-                        case 'y':
-                                compr_name = malloc(strlen(optarg));
-                                sscanf(optarg,"%d:%s",&compr_prior,compr_name);
-                                if ((compr_prior>=0)&&(compr_name)) {
-                                        if (jffs2_set_compressor_priority(compr_name, compr_prior))
-	                                        exit(EXIT_FAILURE);
-                                }
-                                else {
-                                        error_msg_and_die("Cannot parse %s",optarg);
+
+                        case 'F':
+                                if (jffs2_compression_read_config_file(optarg)) {
+                                        error_msg_and_die("Unable to read or process compressor config file %s.\n",optarg);
                                 }
-                                free(compr_name);
                                 break;
+
 		}
 	}
 	if (out_fd == -1) {
@@ -1383,6 +1438,8 @@
 	if (devtable)
 		parse_device_table(root, devtable);
 
+        jffs2_model_load();
+
 	create_target_filesystem(root);
 
 	cleanup(root);
@@ -1394,13 +1451,14 @@
 
         if (verbose) {
                 char *s = jffs2_stats();
-                fprintf(stderr,"\n\n%s",s);
+                fprintf(stderr,"\n%s",s);
                 free(s);
         }
         if ((verbose)||(jffs2_compression_check_get()&&(jffs2_compression_check_errorcnt_get()))) {
                 fprintf(stderr,"Compression errors: %d\n",jffs2_compression_check_errorcnt_get());
         }
 
+        jffs2_model_release();
         jffs2_compressors_exit();
 
 	return 0;