summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/Kconfig81
-rw-r--r--fs/jffs2/Makefile1
-rw-r--r--fs/jffs2/acl.c23
-rw-r--r--fs/jffs2/acl.h4
-rw-r--r--fs/jffs2/background.c4
-rw-r--r--fs/jffs2/build.c10
-rw-r--r--fs/jffs2/compr.c422
-rw-r--r--fs/jffs2/compr.h54
-rw-r--r--fs/jffs2/compr_lzo.c108
-rw-r--r--fs/jffs2/compr_rtime.c2
-rw-r--r--fs/jffs2/compr_rubin.c4
-rw-r--r--fs/jffs2/compr_zlib.c6
-rw-r--r--fs/jffs2/dir.c37
-rw-r--r--fs/jffs2/erase.c57
-rw-r--r--fs/jffs2/fs.c32
-rw-r--r--fs/jffs2/gc.c23
-rw-r--r--fs/jffs2/jffs2_fs_sb.h5
-rw-r--r--fs/jffs2/nodelist.h2
-rw-r--r--fs/jffs2/nodemgmt.c25
-rw-r--r--fs/jffs2/os-linux.h5
-rw-r--r--fs/jffs2/readinode.c8
-rw-r--r--fs/jffs2/scan.c19
-rw-r--r--fs/jffs2/security.c6
-rw-r--r--fs/jffs2/summary.c30
-rw-r--r--fs/jffs2/summary.h6
-rw-r--r--fs/jffs2/wbuf.c81
-rw-r--r--fs/jffs2/write.c13
-rw-r--r--fs/jffs2/xattr.h2
-rw-r--r--fs/jffs2/xattr_user.c4
29 files changed, 736 insertions, 338 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index f9eed6d79066..bb02b39380a3 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1225,6 +1225,14 @@ config JFFS2_FS_WRITEBUFFER
- NOR flash with transparent ECC
- DataFlash
+config JFFS2_FS_WBUF_VERIFY
+ bool "Verify JFFS2 write-buffer reads"
+ depends on JFFS2_FS_WRITEBUFFER
+ default n
+ help
+ This causes JFFS2 to read back every page written through the
+ write-buffer, and check for errors.
+
config JFFS2_SUMMARY
bool "JFFS2 summary support (EXPERIMENTAL)"
depends on JFFS2_FS && EXPERIMENTAL
@@ -1295,52 +1303,71 @@ config JFFS2_ZLIB
select ZLIB_DEFLATE
depends on JFFS2_FS
default y
- help
- Zlib is designed to be a free, general-purpose, legally unencumbered,
- lossless data-compression library for use on virtually any computer
- hardware and operating system. See <http://www.gzip.org/zlib/> for
- further information.
+ help
+ Zlib is designed to be a free, general-purpose, legally unencumbered,
+ lossless data-compression library for use on virtually any computer
+ hardware and operating system. See <http://www.gzip.org/zlib/> for
+ further information.
- Say 'Y' if unsure.
+ Say 'Y' if unsure.
+
+config JFFS2_LZO
+ bool "JFFS2 LZO compression support" if JFFS2_COMPRESSION_OPTIONS
+ select LZO_COMPRESS
+ select LZO_DECOMPRESS
+ depends on JFFS2_FS
+ default n
+ help
+ minilzo-based compression. Generally works better than Zlib.
+
+ This feature was added in July, 2007. Say 'N' if you need
+ compatibility with older bootloaders or kernels.
config JFFS2_RTIME
bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS
depends on JFFS2_FS
default y
- help
- Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
+ help
+ Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
config JFFS2_RUBIN
bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS
depends on JFFS2_FS
default n
- help
- RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
+ 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 available compression modes. Don't touch if unsure.
+ 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 available compression modes. Don't touch if unsure.
config JFFS2_CMODE_NONE
- bool "no compression"
- help
- Uses no compression.
+ bool "no compression"
+ help
+ Uses no compression.
config JFFS2_CMODE_PRIORITY
- bool "priority"
- help
- Tries the compressors in a predefined order and chooses the first
- successful one.
+ bool "priority"
+ help
+ Tries the compressors in a predefined 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.
+ bool "size (EXPERIMENTAL)"
+ help
+ Tries all compressors and chooses the one which has the smallest
+ result.
+
+config JFFS2_CMODE_FAVOURLZO
+ bool "Favour LZO"
+ help
+ Tries all compressors and chooses the one which has the smallest
+ result but gives some preference to LZO (which has faster
+ decompression) at the expense of size.
endchoice
diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile
index c32b241e3d91..60e5d49ca03e 100644
--- a/fs/jffs2/Makefile
+++ b/fs/jffs2/Makefile
@@ -17,4 +17,5 @@ jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += acl.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_LZO) += compr_lzo.o
jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 65b3a1b5b88d..8ec9323e830a 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -176,7 +176,7 @@ static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct
spin_unlock(&inode->i_lock);
}
-static struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
+struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct posix_acl *acl;
@@ -247,8 +247,13 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
if (rc < 0)
return rc;
if (inode->i_mode != mode) {
- inode->i_mode = mode;
- jffs2_dirty_inode(inode);
+ struct iattr attr;
+
+ attr.ia_valid = ATTR_MODE;
+ attr.ia_mode = mode;
+ rc = jffs2_do_setattr(inode, &attr);
+ if (rc < 0)
+ return rc;
}
if (rc == 0)
acl = NULL;
@@ -307,22 +312,16 @@ int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
return generic_permission(inode, mask, jffs2_check_acl);
}
-int jffs2_init_acl(struct inode *inode, struct inode *dir)
+int jffs2_init_acl(struct inode *inode, struct posix_acl *acl)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
- struct posix_acl *acl = NULL, *clone;
+ struct posix_acl *clone;
mode_t mode;
int rc = 0;
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
f->i_acl_default = JFFS2_ACL_NOT_CACHED;
- if (!S_ISLNK(inode->i_mode)) {
- acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT);
- if (IS_ERR(acl))
- return PTR_ERR(acl);
- if (!acl)
- inode->i_mode &= ~current->fs->umask;
- }
+
if (acl) {
if (S_ISDIR(inode->i_mode)) {
rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h
index c84378cee82a..90a2dbf59051 100644
--- a/fs/jffs2/acl.h
+++ b/fs/jffs2/acl.h
@@ -28,9 +28,10 @@ struct jffs2_acl_header {
#define JFFS2_ACL_NOT_CACHED ((void *)-1)
+extern struct posix_acl *jffs2_get_acl(struct inode *inode, int type);
extern int jffs2_permission(struct inode *, int, struct nameidata *);
extern int jffs2_acl_chmod(struct inode *);
-extern int jffs2_init_acl(struct inode *, struct inode *);
+extern int jffs2_init_acl(struct inode *, struct posix_acl *);
extern void jffs2_clear_acl(struct jffs2_inode_info *);
extern struct xattr_handler jffs2_acl_access_xattr_handler;
@@ -38,6 +39,7 @@ extern struct xattr_handler jffs2_acl_default_xattr_handler;
#else
+#define jffs2_get_acl(inode, type) (NULL)
#define jffs2_permission NULL
#define jffs2_acl_chmod(inode) (0)
#define jffs2_init_acl(inode,dir) (0)
diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c
index 504643f2e98b..d568ae846741 100644
--- a/fs/jffs2/background.c
+++ b/fs/jffs2/background.c
@@ -23,8 +23,8 @@ static int jffs2_garbage_collect_thread(void *);
void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
{
spin_lock(&c->erase_completion_lock);
- if (c->gc_task && jffs2_thread_should_wake(c))
- send_sig(SIGHUP, c->gc_task, 1);
+ if (c->gc_task && jffs2_thread_should_wake(c))
+ send_sig(SIGHUP, c->gc_task, 1);
spin_unlock(&c->erase_completion_lock);
}
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c
index 0ca2fff2617f..722a6b682951 100644
--- a/fs/jffs2/build.c
+++ b/fs/jffs2/build.c
@@ -285,6 +285,14 @@ static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
than actually making progress? */
c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
+ /* What number of 'very dirty' eraseblocks do we allow before we
+ trigger the GC thread even if we don't _need_ the space. When we
+ can't mark nodes obsolete on the medium, the old dirty nodes cause
+ performance problems because we have to inspect and discard them. */
+ c->vdirty_blocks_gctrigger = c->resv_blocks_gctrigger;
+ if (jffs2_can_mark_obsolete(c))
+ c->vdirty_blocks_gctrigger *= 10;
+
/* If there's less than this amount of dirty space, don't bother
trying to GC to make more space. It'll be a fruitless task */
c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
@@ -303,6 +311,8 @@ static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024);
dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n",
c->nospc_dirty_size);
+ dbg_fsbuild("Very dirty blocks before GC triggered: %d\n",
+ c->vdirty_blocks_gctrigger);
}
int jffs2_do_mount_fs(struct jffs2_sb_info *c)
diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c
index 485d065de41f..86739ee53b37 100644
--- a/fs/jffs2/compr.c
+++ b/fs/jffs2/compr.c
@@ -5,7 +5,7 @@
* Created by Arjan van de Ven <arjanv@redhat.com>
*
* Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
- * University of Szeged, Hungary
+ * University of Szeged, Hungary
*
* For licensing information, see the file 'LICENCE' in this directory.
*
@@ -24,6 +24,34 @@ static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
/* Statistics for blocks stored without compression */
static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
+
+/*
+ * Return 1 to use this compression
+ */
+static int jffs2_is_best_compression(struct jffs2_compressor *this,
+ struct jffs2_compressor *best, uint32_t size, uint32_t bestsize)
+{
+ switch (jffs2_compression_mode) {
+ case JFFS2_COMPR_MODE_SIZE:
+ if (bestsize > size)
+ return 1;
+ return 0;
+ case JFFS2_COMPR_MODE_FAVOURLZO:
+ if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size))
+ return 1;
+ if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size))
+ return 1;
+ if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100)))
+ return 1;
+ if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size)
+ return 1;
+
+ return 0;
+ }
+ /* Shouldn't happen */
+ return 0;
+}
+
/* jffs2_compress:
* @data: Pointer to uncompressed data
* @cdata: Pointer to returned pointer to buffer for compressed data
@@ -43,121 +71,124 @@ static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_co
* *datalen accordingly to show the amount of data which were compressed.
*/
uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
- unsigned char *data_in, unsigned char **cpage_out,
- uint32_t *datalen, uint32_t *cdatalen)
+ unsigned char *data_in, unsigned char **cpage_out,
+ uint32_t *datalen, uint32_t *cdatalen)
{
int ret = JFFS2_COMPR_NONE;
- int compr_ret;
- struct jffs2_compressor *this, *best=NULL;
- unsigned char *output_buf = NULL, *tmp_buf;
- uint32_t orig_slen, orig_dlen;
- uint32_t best_slen=0, best_dlen=0;
+ int compr_ret;
+ struct jffs2_compressor *this, *best=NULL;
+ unsigned char *output_buf = NULL, *tmp_buf;
+ uint32_t orig_slen, orig_dlen;
+ uint32_t best_slen=0, best_dlen=0;
- switch (jffs2_compression_mode) {
- case JFFS2_COMPR_MODE_NONE:
- break;
- case JFFS2_COMPR_MODE_PRIORITY:
- output_buf = kmalloc(*cdatalen,GFP_KERNEL);
- 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);
- list_for_each_entry(this, &jffs2_compressor_list, list) {
- /* Skip decompress-only backwards-compatibility and disabled modules */
- if ((!this->compress)||(this->disabled))
- continue;
+ switch (jffs2_compression_mode) {
+ case JFFS2_COMPR_MODE_NONE:
+ break;
+ case JFFS2_COMPR_MODE_PRIORITY:
+ output_buf = kmalloc(*cdatalen,GFP_KERNEL);
+ 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);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ /* Skip decompress-only backwards-compatibility and disabled modules */
+ 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;
- }
- }
- spin_unlock(&jffs2_compressor_list_lock);
- if (ret == JFFS2_COMPR_NONE) kfree(output_buf);
- break;
- case JFFS2_COMPR_MODE_SIZE:
- orig_slen = *datalen;
- orig_dlen = *cdatalen;
- spin_lock(&jffs2_compressor_list_lock);
- list_for_each_entry(this, &jffs2_compressor_list, list) {
- /* Skip decompress-only backwards-compatibility and disabled modules */
- if ((!this->compress)||(this->disabled))
- continue;
- /* Allocating memory for output buffer if necessary */
- if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) {
- spin_unlock(&jffs2_compressor_list_lock);
- kfree(this->compr_buf);
- spin_lock(&jffs2_compressor_list_lock);
- this->compr_buf_size=0;
- this->compr_buf=NULL;
- }
- if (!this->compr_buf) {
- spin_unlock(&jffs2_compressor_list_lock);
- tmp_buf = kmalloc(orig_dlen,GFP_KERNEL);
- spin_lock(&jffs2_compressor_list_lock);
- if (!tmp_buf) {
- printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen);
- continue;
- }
- else {
- this->compr_buf = tmp_buf;
- 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 (best_dlen) {
- *cdatalen = best_dlen;
- *datalen = best_slen;
- output_buf = best->compr_buf;
- best->compr_buf = NULL;
- best->compr_buf_size = 0;
- best->stat_compr_blocks++;
- best->stat_compr_orig_size += best_slen;
- best->stat_compr_new_size += best_dlen;
- ret = best->compr;
- }
- spin_unlock(&jffs2_compressor_list_lock);
- break;
- default:
- printk(KERN_ERR "JFFS2: unknow compression mode.\n");
- }
+ 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;
+ }
+ }
+ spin_unlock(&jffs2_compressor_list_lock);
+ if (ret == JFFS2_COMPR_NONE)
+ kfree(output_buf);
+ break;
+ case JFFS2_COMPR_MODE_SIZE:
+ case JFFS2_COMPR_MODE_FAVOURLZO:
+ orig_slen = *datalen;
+ orig_dlen = *cdatalen;
+ spin_lock(&jffs2_compressor_list_lock);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ /* Skip decompress-only backwards-compatibility and disabled modules */
+ if ((!this->compress)||(this->disabled))
+ continue;
+ /* Allocating memory for output buffer if necessary */
+ if ((this->compr_buf_size < orig_slen) && (this->compr_buf)) {
+ spin_unlock(&jffs2_compressor_list_lock);
+ kfree(this->compr_buf);
+ spin_lock(&jffs2_compressor_list_lock);
+ this->compr_buf_size=0;
+ this->compr_buf=NULL;
+ }
+ if (!this->compr_buf) {
+ spin_unlock(&jffs2_compressor_list_lock);
+ tmp_buf = kmalloc(orig_slen, GFP_KERNEL);
+ spin_lock(&jffs2_compressor_list_lock);
+ if (!tmp_buf) {
+ printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n", orig_slen);
+ continue;
+ }
+ else {
+ this->compr_buf = tmp_buf;
+ this->compr_buf_size = orig_slen;
+ }
+ }
+ 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) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen))
+ && (*cdatalen < *datalen)) {
+ best_dlen = *cdatalen;
+ best_slen = *datalen;
+ best = this;
+ }
+ }
+ }
+ if (best_dlen) {
+ *cdatalen = best_dlen;
+ *datalen = best_slen;
+ output_buf = best->compr_buf;
+ best->compr_buf = NULL;
+ best->compr_buf_size = 0;
+ best->stat_compr_blocks++;
+ best->stat_compr_orig_size += best_slen;
+ best->stat_compr_new_size += best_dlen;
+ ret = best->compr;
+ }
+ spin_unlock(&jffs2_compressor_list_lock);
+ break;
+ default:
+ printk(KERN_ERR "JFFS2: unknow compression mode.\n");
+ }
out:
- if (ret == JFFS2_COMPR_NONE) {
- *cpage_out = data_in;
- *datalen = *cdatalen;
- none_stat_compr_blocks++;
- none_stat_compr_size += *datalen;
- }
- else {
- *cpage_out = output_buf;
- }
+ if (ret == JFFS2_COMPR_NONE) {
+ *cpage_out = data_in;
+ *datalen = *cdatalen;
+ none_stat_compr_blocks++;
+ none_stat_compr_size += *datalen;
+ }
+ else {
+ *cpage_out = output_buf;
+ }
return ret;
}
@@ -165,8 +196,8 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
uint16_t comprtype, unsigned char *cdata_in,
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
{
- struct jffs2_compressor *this;
- int ret;
+ struct jffs2_compressor *this;
+ int ret;
/* Older code had a bug where it would write non-zero 'usercompr'
fields. Deal with it. */
@@ -177,32 +208,32 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
case JFFS2_COMPR_NONE:
/* This should be special-cased elsewhere, but we might as well deal with it */
memcpy(data_out, cdata_in, datalen);
- none_stat_decompr_blocks++;
+ none_stat_decompr_blocks++;
break;
case JFFS2_COMPR_ZERO:
memset(data_out, 0, datalen);
break;
default:
- spin_lock(&jffs2_compressor_list_lock);
- list_for_each_entry(this, &jffs2_compressor_list, list) {
- if (comprtype == this->compr) {
- this->usecount++;
- spin_unlock(&jffs2_compressor_list_lock);
- ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
- spin_lock(&jffs2_compressor_list_lock);
- 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;
- }
- }
+ spin_lock(&jffs2_compressor_list_lock);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (comprtype == this->compr) {
+ this->usecount++;
+ spin_unlock(&jffs2_compressor_list_lock);
+ ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
+ spin_lock(&jffs2_compressor_list_lock);
+ 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);
+ spin_unlock(&jffs2_compressor_list_lock);
return -EIO;
}
return 0;
@@ -210,108 +241,119 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
int jffs2_register_compressor(struct jffs2_compressor *comp)
{
- struct jffs2_compressor *this;
+ struct jffs2_compressor *this;
- if (!comp->name) {
- printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
- return -1;
- }
- comp->compr_buf_size=0;
- comp->compr_buf=NULL;
- comp->usecount=0;
- comp->stat_compr_orig_size=0;
- comp->stat_compr_new_size=0;
- comp->stat_compr_blocks=0;
- comp->stat_decompr_blocks=0;
- D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
+ if (!comp->name) {
+ printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
+ return -1;
+ }
+ comp->compr_buf_size=0;
+ comp->compr_buf=NULL;
+ comp->usecount=0;
+ comp->stat_compr_orig_size=0;
+ comp->stat_compr_new_size=0;
+ comp->stat_compr_blocks=0;
+ comp->stat_decompr_blocks=0;
+ D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
- spin_lock(&jffs2_compressor_list_lock);
+ spin_lock(&jffs2_compressor_list_lock);
- list_for_each_entry(this, &jffs2_compressor_list, list) {
- if (this->priority < comp->priority) {
- list_add(&comp->list, this->list.prev);
- goto out;
- }
- }
- list_add_tail(&comp->list, &jffs2_compressor_list);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (this->priority < comp->priority) {
+ list_add(&comp->list, this->list.prev);
+ goto out;
+ }
+ }
+ list_add_tail(&comp->list, &jffs2_compressor_list);
out:
- D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
- printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
- })
+ D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+ printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+ })
- spin_unlock(&jffs2_compressor_list_lock);
+ spin_unlock(&jffs2_compressor_list_lock);
- return 0;
+ return 0;
}
int jffs2_unregister_compressor(struct jffs2_compressor *comp)
{
- D2(struct jffs2_compressor *this;)
+ D2(struct jffs2_compressor *this;)
- D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
+ D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
- spin_lock(&jffs2_compressor_list_lock);
+ spin_lock(&jffs2_compressor_list_lock);
- if (comp->usecount) {
- spin_unlock(&jffs2_compressor_list_lock);
- printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
- return -1;
- }
- list_del(&comp->list);
+ if (comp->usecount) {
+ spin_unlock(&jffs2_compressor_list_lock);
+ printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
+ return -1;
+ }
+ list_del(&comp->list);
- D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
- printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
- })
- spin_unlock(&jffs2_compressor_list_lock);
- return 0;
+ D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+ printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+ })
+ spin_unlock(&jffs2_compressor_list_lock);
+ return 0;
}
void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
{
- if (orig != comprbuf)
- kfree(comprbuf);
+ if (orig != comprbuf)
+ kfree(comprbuf);
}
int __init jffs2_compressors_init(void)
{
/* Registering compressors */
#ifdef CONFIG_JFFS2_ZLIB
- jffs2_zlib_init();
+ jffs2_zlib_init();
#endif
#ifdef CONFIG_JFFS2_RTIME
- jffs2_rtime_init();
+ jffs2_rtime_init();
#endif
#ifdef CONFIG_JFFS2_RUBIN
- jffs2_rubinmips_init();
- jffs2_dynrubin_init();
+ jffs2_rubinmips_init();
+ jffs2_dynrubin_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");)
+ 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");)
+ jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+ D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
+#else
+#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO
+ jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO;
+ D1(printk(KERN_INFO "JFFS2: default compression mode: favourlzo\n");)
#else
- D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
+ D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
+#endif
#endif
#endif
- return 0;
+ return 0;
}
int jffs2_compressors_exit(void)
{
/* Unregistering compressors */
+#ifdef CONFIG_JFFS2_LZO
+ jffs2_lzo_exit();
+#endif
#ifdef CONFIG_JFFS2_RUBIN
- jffs2_dynrubin_exit();
- jffs2_rubinmips_exit();
+ jffs2_dynrubin_exit();
+ jffs2_rubinmips_exit();
#endif
#ifdef CONFIG_JFFS2_RTIME
- jffs2_rtime_exit();
+ jffs2_rtime_exit();
#endif
#ifdef CONFIG_JFFS2_ZLIB
- jffs2_zlib_exit();
+ jffs2_zlib_exit();
#endif
- return 0;
+ return 0;
}
diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h
index 68cc7010dbdf..7d1d72faa774 100644
--- a/fs/jffs2/compr.h
+++ b/fs/jffs2/compr.h
@@ -2,7 +2,7 @@
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
- * University of Szeged, Hungary
+ * University of Szeged, Hungary
*
* For licensing information, see the file 'LICENCE' in this directory.
*
@@ -27,34 +27,38 @@
#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_LZO_PRIORITY 80
+
#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
-#define JFFS2_DYNRUBIN_DISABLED /* for decompression */
+#define JFFS2_DYNRUBIN_DISABLED /* for decompression */
#define JFFS2_COMPR_MODE_NONE 0
#define JFFS2_COMPR_MODE_PRIORITY 1
#define JFFS2_COMPR_MODE_SIZE 2
+#define JFFS2_COMPR_MODE_FAVOURLZO 3
+
+#define FAVOUR_LZO_PERCENT 80
struct jffs2_compressor {
- struct list_head list;
- int priority; /* used by prirority comr. mode */
- char *name;
- 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 usecount;
- int disabled; /* if seted the compressor won't compress */
- unsigned char *compr_buf; /* used by size compr. mode */
- uint32_t compr_buf_size; /* used by size compr. mode */
- uint32_t stat_compr_orig_size;
- uint32_t stat_compr_new_size;
- uint32_t stat_compr_blocks;
- uint32_t stat_decompr_blocks;
+ struct list_head list;
+ int priority; /* used by prirority comr. mode */
+ char *name;
+ 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 usecount;
+ int disabled; /* if set the compressor won't compress */
+ unsigned char *compr_buf; /* used by size compr. mode */
+ uint32_t compr_buf_size; /* used by size compr. mode */
+ uint32_t stat_compr_orig_size;
+ uint32_t stat_compr_new_size;
+ uint32_t stat_compr_blocks;
+ uint32_t stat_decompr_blocks;
};
int jffs2_register_compressor(struct jffs2_compressor *comp);
@@ -64,12 +68,12 @@ int jffs2_compressors_init(void);
int jffs2_compressors_exit(void);
uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
- unsigned char *data_in, unsigned char **cpage_out,
- uint32_t *datalen, uint32_t *cdatalen);
+ unsigned char *data_in, unsigned char **cpage_out,
+ uint32_t *datalen, uint32_t *cdatalen);
int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
- uint16_t comprtype, unsigned char *cdata_in,
- unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
+ uint16_t comprtype, unsigned char *cdata_in,
+ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
@@ -90,5 +94,9 @@ void jffs2_rtime_exit(void);
int jffs2_zlib_init(void);
void jffs2_zlib_exit(void);
#endif
+#ifdef CONFIG_JFFS2_LZO
+int jffs2_lzo_init(void);
+void jffs2_lzo_exit(void);
+#endif
#endif /* __JFFS2_COMPR_H__ */
diff --git a/fs/jffs2/compr_lzo.c b/fs/jffs2/compr_lzo.c
new file mode 100644
index 000000000000..47b045797e42
--- /dev/null
+++ b/fs/jffs2/compr_lzo.c
@@ -0,0 +1,108 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright © 2007 Nokia Corporation. All rights reserved.
+ *
+ * Created by Richard Purdie <rpurdie@openedhand.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/lzo.h>
+#include "compr.h"
+
+static void *lzo_mem;
+static void *lzo_compress_buf;
+static DEFINE_MUTEX(deflate_mutex);
+
+static void free_workspace(void)
+{
+ vfree(lzo_mem);
+ vfree(lzo_compress_buf);
+}
+
+static int __init alloc_workspace(void)
+{
+ lzo_mem = vmalloc(LZO1X_MEM_COMPRESS);
+ lzo_compress_buf = vmalloc(lzo1x_worst_compress(PAGE_SIZE));
+
+ if (!lzo_mem || !lzo_compress_buf) {
+ printk(KERN_WARNING "Failed to allocate lzo deflate workspace\n");
+ free_workspace();
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t *sourcelen, uint32_t *dstlen, void *model)
+{
+ size_t compress_size;
+ int ret;
+
+ mutex_lock(&deflate_mutex);
+ ret = lzo1x_1_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem);
+ mutex_unlock(&deflate_mutex);
+
+ if (ret != LZO_E_OK)
+ return -1;
+
+ if (compress_size > *dstlen)
+ return -1;
+
+ memcpy(cpage_out, lzo_compress_buf, compress_size);
+ *dstlen = compress_size;
+
+ return 0;
+}
+
+static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t srclen, uint32_t destlen, void *model)
+{
+ size_t dl = destlen;
+ int ret;
+
+ ret = lzo1x_decompress_safe(data_in, srclen, cpage_out, &dl);
+
+ if (ret != LZO_E_OK || dl != destlen)
+ return -1;
+
+ return 0;
+}
+
+static struct jffs2_compressor jffs2_lzo_comp = {
+ .priority = JFFS2_LZO_PRIORITY,
+ .name = "lzo",
+ .compr = JFFS2_COMPR_LZO,
+ .compress = &jffs2_lzo_compress,
+ .decompress = &jffs2_lzo_decompress,
+ .disabled = 0,
+};
+
+int __init jffs2_lzo_init(void)
+{
+ int ret;
+
+ ret = alloc_workspace();
+ if (ret < 0)
+ return ret;
+
+ ret = jffs2_register_compressor(&jffs2_lzo_comp);
+ if (ret)
+ free_workspace();
+
+ return ret;
+}
+
+void jffs2_lzo_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_lzo_comp);
+ free_workspace();
+}
diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c
index 0d0bfd2e4e0d..546d1538d076 100644
--- a/fs/jffs2/compr_rtime.c
+++ b/fs/jffs2/compr_rtime.c
@@ -104,7 +104,7 @@ static int jffs2_rtime_decompress(unsigned char *data_in,
}
}
}
- return 0;
+ return 0;
}
static struct jffs2_compressor jffs2_rtime_comp = {
diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c
index ea0431e047d5..c73fa89b5f8a 100644
--- a/fs/jffs2/compr_rubin.c
+++ b/fs/jffs2/compr_rubin.c
@@ -384,7 +384,7 @@ static int jffs2_rubinmips_decompress(unsigned char *data_in,
void *model)
{
rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
- return 0;
+ return 0;
}
static int jffs2_dynrubin_decompress(unsigned char *data_in,
@@ -399,7 +399,7 @@ static int jffs2_dynrubin_decompress(unsigned char *data_in,
bits[c] = data_in[c];
rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
- return 0;
+ return 0;
}
static struct jffs2_compressor jffs2_rubinmips_comp = {
diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c
index 2b87fccc1557..cfd301a5edfc 100644
--- a/fs/jffs2/compr_zlib.c
+++ b/fs/jffs2/compr_zlib.c
@@ -181,7 +181,7 @@ static int jffs2_zlib_decompress(unsigned char *data_in,
}
zlib_inflateEnd(&inf_strm);
mutex_unlock(&inflate_mutex);
- return 0;
+ return 0;
}
static struct jffs2_compressor jffs2_zlib_comp = {
@@ -203,11 +203,11 @@ int __init jffs2_zlib_init(void)
ret = alloc_workspaces();
if (ret)
- return ret;
+ return ret;
ret = jffs2_register_compressor(&jffs2_zlib_comp);
if (ret)
- free_workspaces();
+ free_workspaces();
return ret;
}
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index c1dfca310dd6..8353eb9c1799 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -32,7 +32,7 @@ static int jffs2_mkdir (struct inode *,struct dentry *,int);
static int jffs2_rmdir (struct inode *,struct dentry *);
static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t);
static int jffs2_rename (struct inode *, struct dentry *,
- struct inode *, struct dentry *);
+ struct inode *, struct dentry *);
const struct file_operations jffs2_dir_operations =
{
@@ -182,6 +182,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
struct jffs2_inode_info *f, *dir_f;
struct jffs2_sb_info *c;
struct inode *inode;
+ struct posix_acl *acl;
int ret;
ri = jffs2_alloc_raw_inode();
@@ -192,7 +193,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
D1(printk(KERN_DEBUG "jffs2_create()\n"));
- inode = jffs2_new_inode(dir_i, mode, ri);
+ inode = jffs2_new_inode(dir_i, mode, ri, &acl);
if (IS_ERR(inode)) {
D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n"));
@@ -212,12 +213,12 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
dentry->d_name.name, dentry->d_name.len);
if (ret)
- goto fail;
+ goto fail_acl;
ret = jffs2_init_security(inode, dir_i);
if (ret)
- goto fail;
- ret = jffs2_init_acl(inode, dir_i);
+ goto fail_acl;
+ ret = jffs2_init_acl(inode, acl);
if (ret)
goto fail;
@@ -230,6 +231,8 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages));
return 0;
+ fail_acl:
+ posix_acl_release(acl);
fail:
make_bad_inode(inode);
iput(inode);
@@ -306,6 +309,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
struct jffs2_full_dirent *fd;
int namelen;
uint32_t alloclen;
+ struct posix_acl *acl;
int ret, targetlen = strlen(target);
/* FIXME: If you care. We'd need to use frags for the target
@@ -332,7 +336,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
return ret;
}
- inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri);
+ inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri, &acl);
if (IS_ERR(inode)) {
jffs2_free_raw_inode(ri);
@@ -362,6 +366,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
+ posix_acl_release(acl);
return PTR_ERR(fn);
}
@@ -372,6 +377,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
+ posix_acl_release(acl);
return -ENOMEM;
}
@@ -389,9 +395,10 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
ret = jffs2_init_security(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
+ posix_acl_release(acl);
return ret;
}
- ret = jffs2_init_acl(inode, dir_i);
+ ret = jffs2_init_acl(inode, acl);
if (ret) {
jffs2_clear_inode(inode);
return ret;
@@ -469,6 +476,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
struct jffs2_full_dirent *fd;
int namelen;
uint32_t alloclen;
+ struct posix_acl *acl;
int ret;
mode |= S_IFDIR;
@@ -491,7 +499,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
return ret;
}
- inode = jffs2_new_inode(dir_i, mode, ri);
+ inode = jffs2_new_inode(dir_i, mode, ri, &acl);
if (IS_ERR(inode)) {
jffs2_free_raw_inode(ri);
@@ -518,6 +526,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
+ posix_acl_release(acl);
return PTR_ERR(fn);
}
/* No data here. Only a metadata node, which will be
@@ -531,9 +540,10 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
ret = jffs2_init_security(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
+ posix_acl_release(acl);
return ret;
}
- ret = jffs2_init_acl(inode, dir_i);
+ ret = jffs2_init_acl(inode, acl);
if (ret) {
jffs2_clear_inode(inode);
return ret;
@@ -629,6 +639,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
union jffs2_device_node dev;
int devlen = 0;
uint32_t alloclen;
+ struct posix_acl *acl;
int ret;
if (!new_valid_dev(rdev))
@@ -655,7 +666,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
return ret;
}
- inode = jffs2_new_inode(dir_i, mode, ri);
+ inode = jffs2_new_inode(dir_i, mode, ri, &acl);
if (IS_ERR(inode)) {
jffs2_free_raw_inode(ri);
@@ -684,6 +695,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
+ posix_acl_release(acl);
return PTR_ERR(fn);
}
/* No data here. Only a metadata node, which will be
@@ -697,9 +709,10 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
ret = jffs2_init_security(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
+ posix_acl_release(acl);
return ret;
}
- ret = jffs2_init_acl(inode, dir_i);
+ ret = jffs2_init_acl(inode, acl);
if (ret) {
jffs2_clear_inode(inode);
return ret;
@@ -770,7 +783,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
}
static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
- struct inode *new_dir_i, struct dentry *new_dentry)
+ struct inode *new_dir_i, struct dentry *new_dentry)
{
int ret;
struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c
index 66e7c2f1e644..a1db9180633f 100644
--- a/fs/jffs2/erase.c
+++ b/fs/jffs2/erase.c
@@ -38,8 +38,8 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
#ifdef __ECOS
ret = jffs2_flash_erase(c, jeb);
if (!ret) {
- jffs2_erase_succeeded(c, jeb);
- return;
+ jffs2_erase_succeeded(c, jeb);
+ return;
}
bad_offset = jeb->offset;
#else /* Linux */
@@ -50,12 +50,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
if (!instr) {
printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_move(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
c->dirty_size += c->sector_size;
jeb->dirty_size = c->sector_size;
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
return;
}
@@ -82,12 +84,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
if (ret == -ENOMEM || ret == -EAGAIN) {
/* Erase failed immediately. Refile it on the list */
D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_move(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
c->dirty_size += c->sector_size;
jeb->dirty_size = c->sector_size;
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
return;
}
@@ -114,6 +118,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
list_del(&jeb->list);
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
jffs2_mark_erased_block(c, jeb);
if (!--count) {
@@ -134,6 +139,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
jffs2_free_jeb_node_refs(c, jeb);
list_add(&jeb->list, &c->erasing_list);
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
jffs2_erase_block(c, jeb);
@@ -142,23 +148,25 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
}
/* Be nice */
- cond_resched();
+ yield();
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
}
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
done:
D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
-
- up(&c->erase_free_sem);
}
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_move_tail(&jeb->list, &c->erase_complete_list);
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
/* Ensure that kupdated calls us again to mark them clean */
jffs2_erase_pending_trigger(c);
}
@@ -172,22 +180,26 @@ static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock
failed too many times. */
if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
/* We'd like to give this block another try. */
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_move(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
c->dirty_size += c->sector_size;
jeb->dirty_size = c->sector_size;
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
return;
}
}
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->bad_size += c->sector_size;
list_move(&jeb->list, &c->bad_list);
c->nr_erasing_blocks--;
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
wake_up(&c->erase_wait);
}
@@ -317,6 +329,33 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl
size_t retlen;
int ret = -EIO;
+ if (c->mtd->point) {
+ unsigned long *wordebuf;
+
+ ret = c->mtd->point(c->mtd, jeb->offset, c->sector_size, &retlen, (unsigned char **)&ebuf);
+ if (ret) {
+ D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
+ goto do_flash_read;
+ }
+ if (retlen < c->sector_size) {
+ /* Don't muck about if it won't let us point to the whole erase sector */
+ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
+ c->mtd->unpoint(c->mtd, ebuf, jeb->offset, retlen);
+ goto do_flash_read;
+ }
+ wordebuf = ebuf-sizeof(*wordebuf);
+ retlen /= sizeof(*wordebuf);
+ do {
+ if (*++wordebuf != ~0)
+ break;
+ } while(--retlen);
+ c->mtd->unpoint(c->mtd, ebuf, jeb->offset, c->sector_size);
+ if (retlen)
+ printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08tx\n",
+ *wordebuf, jeb->offset + c->sector_size-retlen*sizeof(*wordebuf));
+ return 0;
+ }
+ do_flash_read:
ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!ebuf) {
printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset);
@@ -362,7 +401,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
{
size_t retlen;
int ret;
- uint32_t bad_offset;
+ uint32_t uninitialized_var(bad_offset);
switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
case -EAGAIN: goto refile;
@@ -417,6 +456,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL);
}
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->free_size += jeb->free_size;
@@ -429,23 +469,28 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
c->nr_erasing_blocks--;
c->nr_free_blocks++;
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
wake_up(&c->erase_wait);
return;
filebad:
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
/* Stick it on a list (any list) so erase_failed can take it
right off again. Silly, but shouldn't happen often. */
list_add(&jeb->list, &c->erasing_list);
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
jffs2_erase_failed(c, jeb, bad_offset);
return;
refile:
/* Stick it back on the list from whence it came and come back later */
jffs2_erase_pending_trigger(c);
+ down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_add(&jeb->list, &c->erase_complete_list);
spin_unlock(&c->erase_completion_lock);
+ up(&c->erase_free_sem);
return;
}
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index 8bc727b71696..ed85f9afdbc8 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -24,7 +24,7 @@
static int jffs2_flash_setup(struct jffs2_sb_info *c);
-static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
+int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
{
struct jffs2_full_dnode *old_metadata, *new_metadata;
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
@@ -36,10 +36,8 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
unsigned int ivalid;
uint32_t alloclen;
int ret;
+
D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
- ret = inode_change_ok(inode, iattr);
- if (ret)
- return ret;
/* Special cases - we don't want more than one data node
for these types on the medium at any time. So setattr
@@ -183,9 +181,14 @@ int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
{
int rc;
+ rc = inode_change_ok(dentry->d_inode, iattr);
+ if (rc)
+ return rc;
+
rc = jffs2_do_setattr(dentry->d_inode, iattr);
if (!rc && (iattr->ia_valid & ATTR_MODE))
rc = jffs2_acl_chmod(dentry->d_inode);
+
return rc;
}
@@ -399,7 +402,8 @@ void jffs2_write_super (struct super_block *sb)
/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
fill in the raw_inode while you're at it. */
-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
+struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri,
+ struct posix_acl **acl)
{
struct inode *inode;
struct super_block *sb = dir_i->i_sb;
@@ -431,7 +435,23 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i
} else {
ri->gid = cpu_to_je16(current->fsgid);
}
- ri->mode = cpu_to_jemode(mode);
+
+ /* POSIX ACLs have to be processed now, at least partly.
+ The umask is only applied if there's no default ACL */
+ if (!S_ISLNK(mode)) {
+ *acl = jffs2_get_acl(dir_i, ACL_TYPE_DEFAULT);
+ if (IS_ERR(*acl)) {
+ make_bad_inode(inode);
+ iput(inode);
+ inode = (void *)*acl;
+ *acl = NULL;
+ return inode;
+ }
+ if (!(*acl))
+ mode &= ~current->fs->umask;
+ } else {
+ *acl = NULL;
+ }
ret = jffs2_do_new_inode (c, f, mode, ri);
if (ret) {
make_bad_inode(inode);
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c
index 2d99e06ab223..32ff0373aa04 100644
--- a/fs/jffs2/gc.c
+++ b/fs/jffs2/gc.c
@@ -122,6 +122,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
struct jffs2_inode_cache *ic;
struct jffs2_eraseblock *jeb;
struct jffs2_raw_node_ref *raw;
+ uint32_t gcblock_dirty;
int ret = 0, inum, nlink;
int xattr = 0;
@@ -236,6 +237,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
}
raw = jeb->gc_node;
+ gcblock_dirty = jeb->dirty_size;
while(ref_obsolete(raw)) {
D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
@@ -282,7 +284,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
} else {
ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw);
}
- goto release_sem;
+ goto test_gcnode;
}
#endif
@@ -376,7 +378,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
if (ret != -EBADFD) {
spin_unlock(&c->inocache_lock);
- goto release_sem;
+ goto test_gcnode;
}
/* Fall through if it wanted us to, with inocache_lock held */
@@ -407,6 +409,12 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
jffs2_gc_release_inode(c, f);
+ test_gcnode:
+ if (jeb->dirty_size == gcblock_dirty && !ref_obsolete(jeb->gc_node)) {
+ /* Eep. This really should never happen. GC is broken */
+ printk(KERN_ERR "Error garbage collecting node at %08x!\n", ref_offset(jeb->gc_node));
+ ret = -ENOSPC;
+ }
release_sem:
up(&c->alloc_sem);
@@ -556,7 +564,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
node = kmalloc(rawlen, GFP_KERNEL);
if (!node)
- return -ENOMEM;
+ return -ENOMEM;
ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
if (!ret && retlen != rawlen)
@@ -598,10 +606,15 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
goto bail;
}
+ if (strnlen(node->d.name, node->d.nsize) != node->d.nsize) {
+ printk(KERN_WARNING "Name in dirent node at 0x%08x contains zeroes\n", ref_offset(raw));
+ goto bail;
+ }
+
if (node->d.nsize) {
crc = crc32(0, node->d.name, node->d.nsize);
if (je32_to_cpu(node->d.name_crc) != crc) {
- printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
goto bail;
}
@@ -624,7 +637,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
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);
+ rawlen, phys_ofs, ret, retlen);
if (retlen) {
jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL);
} else {
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h
index b13298a824ed..3a2197f3c812 100644
--- a/fs/jffs2/jffs2_fs_sb.h
+++ b/fs/jffs2/jffs2_fs_sb.h
@@ -69,6 +69,8 @@ struct jffs2_sb_info {
uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */
uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */
uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */
+ /* Number of 'very dirty' blocks before we trigger immediate GC */
+ uint8_t vdirty_blocks_gctrigger;
uint32_t nospc_dirty_size;
@@ -106,6 +108,9 @@ struct jffs2_sb_info {
uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+ unsigned char *wbuf_verify; /* read-back buffer for verification */
+#endif
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
unsigned char *wbuf; /* Write-behind buffer for NAND flash */
uint32_t wbuf_ofs;
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
index bc5509fe577b..ec1aae9e695e 100644
--- a/fs/jffs2/nodelist.h
+++ b/fs/jffs2/nodelist.h
@@ -127,7 +127,7 @@ static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_nod
return ((struct jffs2_inode_cache *)raw);
}
- /* flash_offset & 3 always has to be zero, because nodes are
+ /* flash_offset & 3 always has to be zero, because nodes are
always aligned at 4 bytes. So we have a couple of extra bits
to play with, which indicate the node's status; see below: */
#define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index dbc908ad622b..a0313fa8748e 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -154,7 +154,7 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
while(ret == -EAGAIN) {
ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
if (ret) {
- D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
+ D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
}
}
spin_unlock(&c->erase_completion_lock);
@@ -423,7 +423,12 @@ struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
even after refiling c->nextblock */
if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE))
&& (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) {
- printk(KERN_WARNING "argh. node added in wrong place\n");
+ printk(KERN_WARNING "argh. node added in wrong place at 0x%08x(%d)\n", ofs & ~3, ofs & 3);
+ if (c->nextblock)
+ printk(KERN_WARNING "nextblock 0x%08x", c->nextblock->offset);
+ else
+ printk(KERN_WARNING "No nextblock");
+ printk(", expected at %08x\n", jeb->offset + (c->sector_size - jeb->free_size));
return ERR_PTR(-EINVAL);
}
#endif
@@ -717,6 +722,8 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c)
{
int ret = 0;
uint32_t dirty;
+ int nr_very_dirty = 0;
+ struct jffs2_eraseblock *jeb;
if (c->unchecked_size) {
D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
@@ -738,8 +745,18 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c)
(dirty > c->nospc_dirty_size))
ret = 1;
- D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
- c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
+ list_for_each_entry(jeb, &c->very_dirty_list, list) {
+ nr_very_dirty++;
+ if (nr_very_dirty == c->vdirty_blocks_gctrigger) {
+ ret = 1;
+ /* In debug mode, actually go through and count them all */
+ D1(continue);
+ break;
+ }
+ }
+
+ D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x, vdirty_blocks %d: %s\n",
+ c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, nr_very_dirty, ret?"yes":"no"));
return ret;
}
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index 80daea96bbc2..f6743a915cf3 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -173,12 +173,15 @@ int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
extern const struct inode_operations jffs2_symlink_inode_operations;
/* fs.c */
+struct posix_acl;
+
int jffs2_setattr (struct dentry *, struct iattr *);
+int jffs2_do_setattr (struct inode *, struct iattr *);
void jffs2_read_inode (struct inode *);
void jffs2_clear_inode (struct inode *);
void jffs2_dirty_inode(struct inode *inode);
struct inode *jffs2_new_inode (struct inode *dir_i, int mode,
- struct jffs2_raw_inode *ri);
+ struct jffs2_raw_inode *ri, struct posix_acl **acl);
int jffs2_statfs (struct dentry *, struct kstatfs *);
void jffs2_write_super (struct super_block *);
int jffs2_remount_fs (struct super_block *, int *, char *);
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index b5baa356fed2..2eae5d2dbebe 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -65,7 +65,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer);
if (!err && retlen < tn->csize) {
JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize);
- c->mtd->unpoint(c->mtd, buffer, ofs, len);
+ c->mtd->unpoint(c->mtd, buffer, ofs, retlen);
} else if (err)
JFFS2_WARNING("MTD point failed: error code %d.\n", err);
else
@@ -211,7 +211,7 @@ static void jffs2_kill_tn(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *
* ordering.
*
* Returns 0 if the node was handled (including marking it obsolete)
- * < 0 an if error occurred
+ * < 0 an if error occurred
*/
static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
struct jffs2_readinode_info *rii,
@@ -862,8 +862,8 @@ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_re
JFFS2_ERROR("REF_UNCHECKED but unknown node at %#08x\n",
ref_offset(ref));
JFFS2_ERROR("Node is {%04x,%04x,%08x,%08x}. Please report this error.\n",
- je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
- je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc));
+ je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
+ je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc));
jffs2_mark_node_obsolete(c, ref);
return 0;
}
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index 6c75cd433342..272872d27fd5 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -101,7 +101,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
if (!ret && pointlen < c->mtd->size) {
/* Don't muck about if it won't let us point to the whole flash */
D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
- c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
+ c->mtd->unpoint(c->mtd, flashbuf, 0, pointlen);
flashbuf = NULL;
}
if (ret)
@@ -863,7 +863,7 @@ scan_more:
switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
case JFFS2_FEATURE_ROCOMPAT:
printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
- c->flags |= JFFS2_SB_FLAG_RO;
+ c->flags |= JFFS2_SB_FLAG_RO;
if (!(jffs2_is_readonly(c)))
return -EROFS;
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
@@ -1004,6 +1004,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
{
struct jffs2_full_dirent *fd;
struct jffs2_inode_cache *ic;
+ uint32_t checkedlen;
uint32_t crc;
int err;
@@ -1024,12 +1025,18 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
pseudo_random += je32_to_cpu(rd->version);
- fd = jffs2_alloc_full_dirent(rd->nsize+1);
+ /* Should never happen. Did. (OLPC trac #4184)*/
+ checkedlen = strnlen(rd->name, rd->nsize);
+ if (checkedlen < rd->nsize) {
+ printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n",
+ ofs, checkedlen);
+ }
+ fd = jffs2_alloc_full_dirent(checkedlen+1);
if (!fd) {
return -ENOMEM;
}
- memcpy(&fd->name, rd->name, rd->nsize);
- fd->name[rd->nsize] = 0;
+ memcpy(&fd->name, rd->name, checkedlen);
+ fd->name[checkedlen] = 0;
crc = crc32(0, fd->name, rd->nsize);
if (crc != je32_to_cpu(rd->name_crc)) {
@@ -1055,7 +1062,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
fd->next = NULL;
fd->version = je32_to_cpu(rd->version);
fd->ino = je32_to_cpu(rd->ino);
- fd->nhash = full_name_hash(fd->name, rd->nsize);
+ fd->nhash = full_name_hash(fd->name, checkedlen);
fd->type = rd->type;
jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
diff --git a/fs/jffs2/security.c b/fs/jffs2/security.c
index bc9f6ba10823..02c39c64ecb3 100644
--- a/fs/jffs2/security.c
+++ b/fs/jffs2/security.c
@@ -38,9 +38,9 @@ int jffs2_init_security(struct inode *inode, struct inode *dir)
}
rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
- kfree(name);
- kfree(value);
- return rc;
+ kfree(name);
+ kfree(value);
+ return rc;
}
/* ---- XATTR Handler for "security.*" ----------------- */
diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c
index d828b296392a..629af01e5ade 100644
--- a/fs/jffs2/summary.c
+++ b/fs/jffs2/summary.c
@@ -2,10 +2,10 @@
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
- * Zoltan Sogor <weth@inf.u-szeged.hu>,
- * Patrik Kluba <pajko@halom.u-szeged.hu>,
- * University of Szeged, Hungary
- * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
+ * Zoltan Sogor <weth@inf.u-szeged.hu>,
+ * Patrik Kluba <pajko@halom.u-szeged.hu>,
+ * University of Szeged, Hungary
+ * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
@@ -429,6 +429,7 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
case JFFS2_NODETYPE_DIRENT: {
struct jffs2_sum_dirent_flash *spd;
+ int checkedlen;
spd = sp;
dbg_summary("Dirent at 0x%08x-0x%08x\n",
@@ -436,12 +437,25 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
jeb->offset + je32_to_cpu(spd->offset) + je32_to_cpu(spd->totlen));
- fd = jffs2_alloc_full_dirent(spd->nsize+1);
+ /* This should never happen, but https://dev.laptop.org/ticket/4184 */
+ checkedlen = strnlen(spd->name, spd->nsize);
+ if (!checkedlen) {
+ printk(KERN_ERR "Dirent at %08x has zero at start of name. Aborting mount.\n",
+ jeb->offset + je32_to_cpu(spd->offset));
+ return -EIO;
+ }
+ if (checkedlen < spd->nsize) {
+ printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n",
+ jeb->offset + je32_to_cpu(spd->offset), checkedlen);
+ }
+
+
+ fd = jffs2_alloc_full_dirent(checkedlen+1);
if (!fd)
return -ENOMEM;
- memcpy(&fd->name, spd->name, spd->nsize);
- fd->name[spd->nsize] = 0;
+ memcpy(&fd->name, spd->name, checkedlen);
+ fd->name[checkedlen] = 0;
ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino));
if (!ic) {
@@ -455,7 +469,7 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
fd->next = NULL;
fd->version = je32_to_cpu(spd->version);
fd->ino = je32_to_cpu(spd->ino);
- fd->nhash = full_name_hash(fd->name, spd->nsize);
+ fd->nhash = full_name_hash(fd->name, checkedlen);
fd->type = spd->type;
jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
diff --git a/fs/jffs2/summary.h b/fs/jffs2/summary.h
index 0c6669e21390..8bf34f2fa5ce 100644
--- a/fs/jffs2/summary.h
+++ b/fs/jffs2/summary.h
@@ -2,9 +2,9 @@
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
- * Zoltan Sogor <weth@inf.u-szeged.hu>,
- * Patrik Kluba <pajko@halom.u-szeged.hu>,
- * University of Szeged, Hungary
+ * Zoltan Sogor <weth@inf.u-szeged.hu>,
+ * Patrik Kluba <pajko@halom.u-szeged.hu>,
+ * University of Szeged, Hungary
*
* For licensing information, see the file 'LICENCE' in this directory.
*
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
index 91d1d0f1c66c..d1d4f27464ba 100644
--- a/fs/jffs2/wbuf.c
+++ b/fs/jffs2/wbuf.c
@@ -220,6 +220,47 @@ static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info
return NULL;
}
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf,
+ uint32_t ofs)
+{
+ int ret;
+ size_t retlen;
+ char *eccstr;
+
+ ret = c->mtd->read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify);
+ if (ret && ret != -EUCLEAN && ret != -EBADMSG) {
+ printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x failed: %d\n", c->wbuf_ofs, ret);
+ return ret;
+ } else if (retlen != c->wbuf_pagesize) {
+ printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x gave short read: %zd not %d.\n", ofs, retlen, c->wbuf_pagesize);
+ return -EIO;
+ }
+ if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize))
+ return 0;
+
+ if (ret == -EUCLEAN)
+ eccstr = "corrected";
+ else if (ret == -EBADMSG)
+ eccstr = "correction failed";
+ else
+ eccstr = "OK or unused";
+
+ printk(KERN_WARNING "Write verify error (ECC %s) at %08x. Wrote:\n",
+ eccstr, c->wbuf_ofs);
+ print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
+ c->wbuf, c->wbuf_pagesize, 0);
+
+ printk(KERN_WARNING "Read back:\n");
+ print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
+ c->wbuf_verify, c->wbuf_pagesize, 0);
+
+ return -EIO;
+}
+#else
+#define jffs2_verify_write(c,b,o) (0)
+#endif
+
/* Recover from failure to write wbuf. Recover the nodes up to the
* wbuf, not the one which we were starting to try to write. */
@@ -380,7 +421,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
ret = c->mtd->write(c->mtd, ofs, towrite, &retlen,
rewrite_buf);
- if (ret || retlen != towrite) {
+ if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) {
/* Argh. We tried. Really we did. */
printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
kfree(buf);
@@ -587,15 +628,16 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
- if (ret || retlen != c->wbuf_pagesize) {
- if (ret)
- printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret);
- else {
- printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
- retlen, c->wbuf_pagesize);
- ret = -EIO;
- }
-
+ if (ret) {
+ printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n", ret);
+ goto wfail;
+ } else if (retlen != c->wbuf_pagesize) {
+ printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
+ retlen, c->wbuf_pagesize);
+ ret = -EIO;
+ goto wfail;
+ } else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) {
+ wfail:
jffs2_wbuf_recover(c);
return ret;
@@ -966,8 +1008,8 @@ exit:
#define NR_OOB_SCAN_PAGES 4
-/* For historical reasons we use only 12 bytes for OOB clean marker */
-#define OOB_CM_SIZE 12
+/* For historical reasons we use only 8 bytes for OOB clean marker */
+#define OOB_CM_SIZE 8
static const struct jffs2_unknown_node oob_cleanmarker =
{
@@ -1021,8 +1063,8 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
/*
* Check for a valid cleanmarker.
* Returns: 0 if a valid cleanmarker was found
- * 1 if no cleanmarker was found
- * negative error code if an error occurred
+ * 1 if no cleanmarker was found
+ * negative error code if an error occurred
*/
int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
struct jffs2_eraseblock *jeb)
@@ -1138,11 +1180,22 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
return -ENOMEM;
}
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+ c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+ if (!c->wbuf_verify) {
+ kfree(c->oobbuf);
+ kfree(c->wbuf);
+ return -ENOMEM;
+ }
+#endif
return 0;
}
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
{
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+ kfree(c->wbuf_verify);
+#endif
kfree(c->wbuf);
kfree(c->oobbuf);
}
diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c
index 664c164aa67c..2f5695446d0f 100644
--- a/fs/jffs2/write.c
+++ b/fs/jffs2/write.c
@@ -215,6 +215,17 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
BUG();
});
+ if (strnlen(name, namelen) != namelen) {
+ /* This should never happen, but seems to have done on at least one
+ occasion: https://dev.laptop.org/ticket/4184 */
+ printk(KERN_CRIT "Error in jffs2_write_dirent() -- name contains zero bytes!\n");
+ printk(KERN_CRIT "Directory inode #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x\n",
+ je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
+ je32_to_cpu(rd->name_crc));
+ WARN_ON(1);
+ return ERR_PTR(-EIO);
+ }
+
vecs[0].iov_base = rd;
vecs[0].iov_len = sizeof(*rd);
vecs[1].iov_base = (unsigned char *)name;
@@ -226,7 +237,7 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
fd->version = je32_to_cpu(rd->version);
fd->ino = je32_to_cpu(rd->ino);
- fd->nhash = full_name_hash(name, strlen(name));
+ fd->nhash = full_name_hash(name, namelen);
fd->type = rd->type;
memcpy(fd->name, name, namelen);
fd->name[namelen]=0;
diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h
index 3b0ff2925937..6e3b5ddfb7ab 100644
--- a/fs/jffs2/xattr.h
+++ b/fs/jffs2/xattr.h
@@ -75,7 +75,7 @@ extern void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c);
extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c);
extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
- uint32_t xid, uint32_t version);
+ uint32_t xid, uint32_t version);
extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
diff --git a/fs/jffs2/xattr_user.c b/fs/jffs2/xattr_user.c
index 40942bc516bb..8bbeab90ada1 100644
--- a/fs/jffs2/xattr_user.c
+++ b/fs/jffs2/xattr_user.c
@@ -17,7 +17,7 @@
#include "nodelist.h"
static int jffs2_user_getxattr(struct inode *inode, const char *name,
- void *buffer, size_t size)
+ void *buffer, size_t size)
{
if (!strcmp(name, ""))
return -EINVAL;
@@ -25,7 +25,7 @@ static int jffs2_user_getxattr(struct inode *inode, const char *name,
}
static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer,
- size_t size, int flags)
+ size_t size, int flags)
{
if (!strcmp(name, ""))
return -EINVAL;