11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * JFFS2 -- Journalling Flash File System, Version 2. 31da177e4SLinus Torvalds * 4c00c310eSDavid Woodhouse * Copyright © 2001-2007 Red Hat, Inc. 51da177e4SLinus Torvalds * Created by Arjan van de Ven <arjanv@redhat.com> 61da177e4SLinus Torvalds * 7c00c310eSDavid Woodhouse * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, 81da177e4SLinus Torvalds * University of Szeged, Hungary 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * For licensing information, see the file 'LICENCE' in this directory. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include "compr.h" 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds static DEFINE_SPINLOCK(jffs2_compressor_list_lock); 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds /* Available compressors are on this list */ 191da177e4SLinus Torvalds static LIST_HEAD(jffs2_compressor_list); 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds /* Actual compression mode */ 221da177e4SLinus Torvalds static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds /* Statistics for blocks stored without compression */ 251da177e4SLinus Torvalds static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds /* jffs2_compress: 281da177e4SLinus Torvalds * @data: Pointer to uncompressed data 291da177e4SLinus Torvalds * @cdata: Pointer to returned pointer to buffer for compressed data 301da177e4SLinus Torvalds * @datalen: On entry, holds the amount of data available for compression. 311da177e4SLinus Torvalds * On exit, expected to hold the amount of data actually compressed. 321da177e4SLinus Torvalds * @cdatalen: On entry, holds the amount of space available for compressed 331da177e4SLinus Torvalds * data. On exit, expected to hold the actual size of the compressed 341da177e4SLinus Torvalds * data. 351da177e4SLinus Torvalds * 361da177e4SLinus Torvalds * Returns: Lower byte to be stored with data indicating compression type used. 371da177e4SLinus Torvalds * Zero is used to show that the data could not be compressed - the 381da177e4SLinus Torvalds * compressed version was actually larger than the original. 391da177e4SLinus Torvalds * Upper byte will be used later. (soon) 401da177e4SLinus Torvalds * 411da177e4SLinus Torvalds * If the cdata buffer isn't large enough to hold all the uncompressed data, 421da177e4SLinus Torvalds * jffs2_compress should compress as much as will fit, and should set 431da177e4SLinus Torvalds * *datalen accordingly to show the amount of data which were compressed. 441da177e4SLinus Torvalds */ 451da177e4SLinus Torvalds uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 461da177e4SLinus Torvalds unsigned char *data_in, unsigned char **cpage_out, 471da177e4SLinus Torvalds uint32_t *datalen, uint32_t *cdatalen) 481da177e4SLinus Torvalds { 491da177e4SLinus Torvalds int ret = JFFS2_COMPR_NONE; 501da177e4SLinus Torvalds int compr_ret; 511da177e4SLinus Torvalds struct jffs2_compressor *this, *best=NULL; 521da177e4SLinus Torvalds unsigned char *output_buf = NULL, *tmp_buf; 531da177e4SLinus Torvalds uint32_t orig_slen, orig_dlen; 541da177e4SLinus Torvalds uint32_t best_slen=0, best_dlen=0; 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds switch (jffs2_compression_mode) { 571da177e4SLinus Torvalds case JFFS2_COMPR_MODE_NONE: 581da177e4SLinus Torvalds break; 591da177e4SLinus Torvalds case JFFS2_COMPR_MODE_PRIORITY: 601da177e4SLinus Torvalds output_buf = kmalloc(*cdatalen,GFP_KERNEL); 611da177e4SLinus Torvalds if (!output_buf) { 621da177e4SLinus Torvalds printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n"); 631da177e4SLinus Torvalds goto out; 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds orig_slen = *datalen; 661da177e4SLinus Torvalds orig_dlen = *cdatalen; 671da177e4SLinus Torvalds spin_lock(&jffs2_compressor_list_lock); 681da177e4SLinus Torvalds list_for_each_entry(this, &jffs2_compressor_list, list) { 691da177e4SLinus Torvalds /* Skip decompress-only backwards-compatibility and disabled modules */ 701da177e4SLinus Torvalds if ((!this->compress)||(this->disabled)) 711da177e4SLinus Torvalds continue; 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds this->usecount++; 741da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 751da177e4SLinus Torvalds *datalen = orig_slen; 761da177e4SLinus Torvalds *cdatalen = orig_dlen; 771da177e4SLinus Torvalds compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); 781da177e4SLinus Torvalds spin_lock(&jffs2_compressor_list_lock); 791da177e4SLinus Torvalds this->usecount--; 801da177e4SLinus Torvalds if (!compr_ret) { 811da177e4SLinus Torvalds ret = this->compr; 821da177e4SLinus Torvalds this->stat_compr_blocks++; 831da177e4SLinus Torvalds this->stat_compr_orig_size += *datalen; 841da177e4SLinus Torvalds this->stat_compr_new_size += *cdatalen; 851da177e4SLinus Torvalds break; 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 891da177e4SLinus Torvalds if (ret == JFFS2_COMPR_NONE) kfree(output_buf); 901da177e4SLinus Torvalds break; 911da177e4SLinus Torvalds case JFFS2_COMPR_MODE_SIZE: 921da177e4SLinus Torvalds orig_slen = *datalen; 931da177e4SLinus Torvalds orig_dlen = *cdatalen; 941da177e4SLinus Torvalds spin_lock(&jffs2_compressor_list_lock); 951da177e4SLinus Torvalds list_for_each_entry(this, &jffs2_compressor_list, list) { 961da177e4SLinus Torvalds /* Skip decompress-only backwards-compatibility and disabled modules */ 971da177e4SLinus Torvalds if ((!this->compress)||(this->disabled)) 981da177e4SLinus Torvalds continue; 991da177e4SLinus Torvalds /* Allocating memory for output buffer if necessary */ 1001da177e4SLinus Torvalds if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) { 1011da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 1021da177e4SLinus Torvalds kfree(this->compr_buf); 1031da177e4SLinus Torvalds spin_lock(&jffs2_compressor_list_lock); 1041da177e4SLinus Torvalds this->compr_buf_size=0; 1051da177e4SLinus Torvalds this->compr_buf=NULL; 1061da177e4SLinus Torvalds } 1071da177e4SLinus Torvalds if (!this->compr_buf) { 1081da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 1091da177e4SLinus Torvalds tmp_buf = kmalloc(orig_dlen,GFP_KERNEL); 1101da177e4SLinus Torvalds spin_lock(&jffs2_compressor_list_lock); 1111da177e4SLinus Torvalds if (!tmp_buf) { 1121da177e4SLinus Torvalds printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); 1131da177e4SLinus Torvalds continue; 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds else { 1161da177e4SLinus Torvalds this->compr_buf = tmp_buf; 1171da177e4SLinus Torvalds this->compr_buf_size = orig_dlen; 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds this->usecount++; 1211da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 1221da177e4SLinus Torvalds *datalen = orig_slen; 1231da177e4SLinus Torvalds *cdatalen = orig_dlen; 1241da177e4SLinus Torvalds compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); 1251da177e4SLinus Torvalds spin_lock(&jffs2_compressor_list_lock); 1261da177e4SLinus Torvalds this->usecount--; 1271da177e4SLinus Torvalds if (!compr_ret) { 1281da177e4SLinus Torvalds if ((!best_dlen)||(best_dlen>*cdatalen)) { 1291da177e4SLinus Torvalds best_dlen = *cdatalen; 1301da177e4SLinus Torvalds best_slen = *datalen; 1311da177e4SLinus Torvalds best = this; 1321da177e4SLinus Torvalds } 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds } 1351da177e4SLinus Torvalds if (best_dlen) { 1361da177e4SLinus Torvalds *cdatalen = best_dlen; 1371da177e4SLinus Torvalds *datalen = best_slen; 1381da177e4SLinus Torvalds output_buf = best->compr_buf; 1391da177e4SLinus Torvalds best->compr_buf = NULL; 1401da177e4SLinus Torvalds best->compr_buf_size = 0; 1411da177e4SLinus Torvalds best->stat_compr_blocks++; 1421da177e4SLinus Torvalds best->stat_compr_orig_size += best_slen; 1431da177e4SLinus Torvalds best->stat_compr_new_size += best_dlen; 1441da177e4SLinus Torvalds ret = best->compr; 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 1471da177e4SLinus Torvalds break; 1481da177e4SLinus Torvalds default: 1491da177e4SLinus Torvalds printk(KERN_ERR "JFFS2: unknow compression mode.\n"); 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds out: 1521da177e4SLinus Torvalds if (ret == JFFS2_COMPR_NONE) { 1531da177e4SLinus Torvalds *cpage_out = data_in; 1541da177e4SLinus Torvalds *datalen = *cdatalen; 1551da177e4SLinus Torvalds none_stat_compr_blocks++; 1561da177e4SLinus Torvalds none_stat_compr_size += *datalen; 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds else { 1591da177e4SLinus Torvalds *cpage_out = output_buf; 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds return ret; 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 1651da177e4SLinus Torvalds uint16_t comprtype, unsigned char *cdata_in, 1661da177e4SLinus Torvalds unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) 1671da177e4SLinus Torvalds { 1681da177e4SLinus Torvalds struct jffs2_compressor *this; 1691da177e4SLinus Torvalds int ret; 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds /* Older code had a bug where it would write non-zero 'usercompr' 1721da177e4SLinus Torvalds fields. Deal with it. */ 1731da177e4SLinus Torvalds if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB) 1741da177e4SLinus Torvalds comprtype &= 0xff; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds switch (comprtype & 0xff) { 1771da177e4SLinus Torvalds case JFFS2_COMPR_NONE: 1781da177e4SLinus Torvalds /* This should be special-cased elsewhere, but we might as well deal with it */ 1791da177e4SLinus Torvalds memcpy(data_out, cdata_in, datalen); 1801da177e4SLinus Torvalds none_stat_decompr_blocks++; 1811da177e4SLinus Torvalds break; 1821da177e4SLinus Torvalds case JFFS2_COMPR_ZERO: 1831da177e4SLinus Torvalds memset(data_out, 0, datalen); 1841da177e4SLinus Torvalds break; 1851da177e4SLinus Torvalds default: 1861da177e4SLinus Torvalds spin_lock(&jffs2_compressor_list_lock); 1871da177e4SLinus Torvalds list_for_each_entry(this, &jffs2_compressor_list, list) { 1881da177e4SLinus Torvalds if (comprtype == this->compr) { 1891da177e4SLinus Torvalds this->usecount++; 1901da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 1911da177e4SLinus Torvalds ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL); 1921da177e4SLinus Torvalds spin_lock(&jffs2_compressor_list_lock); 1931da177e4SLinus Torvalds if (ret) { 1941da177e4SLinus Torvalds printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds else { 1971da177e4SLinus Torvalds this->stat_decompr_blocks++; 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds this->usecount--; 2001da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 2011da177e4SLinus Torvalds return ret; 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype); 2051da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 2061da177e4SLinus Torvalds return -EIO; 2071da177e4SLinus Torvalds } 2081da177e4SLinus Torvalds return 0; 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds int jffs2_register_compressor(struct jffs2_compressor *comp) 2121da177e4SLinus Torvalds { 2131da177e4SLinus Torvalds struct jffs2_compressor *this; 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds if (!comp->name) { 2161da177e4SLinus Torvalds printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n"); 2171da177e4SLinus Torvalds return -1; 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds comp->compr_buf_size=0; 2201da177e4SLinus Torvalds comp->compr_buf=NULL; 2211da177e4SLinus Torvalds comp->usecount=0; 2221da177e4SLinus Torvalds comp->stat_compr_orig_size=0; 2231da177e4SLinus Torvalds comp->stat_compr_new_size=0; 2241da177e4SLinus Torvalds comp->stat_compr_blocks=0; 2251da177e4SLinus Torvalds comp->stat_decompr_blocks=0; 2261da177e4SLinus Torvalds D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name)); 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds spin_lock(&jffs2_compressor_list_lock); 2291da177e4SLinus Torvalds 2301da177e4SLinus Torvalds list_for_each_entry(this, &jffs2_compressor_list, list) { 2311da177e4SLinus Torvalds if (this->priority < comp->priority) { 2321da177e4SLinus Torvalds list_add(&comp->list, this->list.prev); 2331da177e4SLinus Torvalds goto out; 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds } 2361da177e4SLinus Torvalds list_add_tail(&comp->list, &jffs2_compressor_list); 2371da177e4SLinus Torvalds out: 2381da177e4SLinus Torvalds D2(list_for_each_entry(this, &jffs2_compressor_list, list) { 2391da177e4SLinus Torvalds printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); 2401da177e4SLinus Torvalds }) 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds return 0; 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds int jffs2_unregister_compressor(struct jffs2_compressor *comp) 2481da177e4SLinus Torvalds { 2491da177e4SLinus Torvalds D2(struct jffs2_compressor *this;) 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name)); 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds spin_lock(&jffs2_compressor_list_lock); 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds if (comp->usecount) { 2561da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 2571da177e4SLinus Torvalds printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n"); 2581da177e4SLinus Torvalds return -1; 2591da177e4SLinus Torvalds } 2601da177e4SLinus Torvalds list_del(&comp->list); 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds D2(list_for_each_entry(this, &jffs2_compressor_list, list) { 2631da177e4SLinus Torvalds printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); 2641da177e4SLinus Torvalds }) 2651da177e4SLinus Torvalds spin_unlock(&jffs2_compressor_list_lock); 2661da177e4SLinus Torvalds return 0; 2671da177e4SLinus Torvalds } 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) 2701da177e4SLinus Torvalds { 2711da177e4SLinus Torvalds if (orig != comprbuf) 2721da177e4SLinus Torvalds kfree(comprbuf); 2731da177e4SLinus Torvalds } 2741da177e4SLinus Torvalds 2757d2beb13SDavid Brownell int __init jffs2_compressors_init(void) 2761da177e4SLinus Torvalds { 2771da177e4SLinus Torvalds /* Registering compressors */ 2781da177e4SLinus Torvalds #ifdef CONFIG_JFFS2_ZLIB 2791da177e4SLinus Torvalds jffs2_zlib_init(); 2801da177e4SLinus Torvalds #endif 2811da177e4SLinus Torvalds #ifdef CONFIG_JFFS2_RTIME 2821da177e4SLinus Torvalds jffs2_rtime_init(); 2831da177e4SLinus Torvalds #endif 2841da177e4SLinus Torvalds #ifdef CONFIG_JFFS2_RUBIN 2851da177e4SLinus Torvalds jffs2_rubinmips_init(); 2861da177e4SLinus Torvalds jffs2_dynrubin_init(); 2871da177e4SLinus Torvalds #endif 2881da177e4SLinus Torvalds /* Setting default compression mode */ 2891da177e4SLinus Torvalds #ifdef CONFIG_JFFS2_CMODE_NONE 2901da177e4SLinus Torvalds jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; 2911da177e4SLinus Torvalds D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");) 2921da177e4SLinus Torvalds #else 2931da177e4SLinus Torvalds #ifdef CONFIG_JFFS2_CMODE_SIZE 2941da177e4SLinus Torvalds jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; 2951da177e4SLinus Torvalds D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");) 2961da177e4SLinus Torvalds #else 2971da177e4SLinus Torvalds D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");) 2981da177e4SLinus Torvalds #endif 2991da177e4SLinus Torvalds #endif 3001da177e4SLinus Torvalds return 0; 3011da177e4SLinus Torvalds } 3021da177e4SLinus Torvalds 3033bcc86f5SDavid Woodhouse int jffs2_compressors_exit(void) 3041da177e4SLinus Torvalds { 3051da177e4SLinus Torvalds /* Unregistering compressors */ 3061da177e4SLinus Torvalds #ifdef CONFIG_JFFS2_RUBIN 3071da177e4SLinus Torvalds jffs2_dynrubin_exit(); 3081da177e4SLinus Torvalds jffs2_rubinmips_exit(); 3091da177e4SLinus Torvalds #endif 3101da177e4SLinus Torvalds #ifdef CONFIG_JFFS2_RTIME 3111da177e4SLinus Torvalds jffs2_rtime_exit(); 3121da177e4SLinus Torvalds #endif 3131da177e4SLinus Torvalds #ifdef CONFIG_JFFS2_ZLIB 3141da177e4SLinus Torvalds jffs2_zlib_exit(); 3151da177e4SLinus Torvalds #endif 3161da177e4SLinus Torvalds return 0; 3171da177e4SLinus Torvalds } 318