linux 4.14内核jffs2文件系统不自动释放空间的bug
前段时间在做spi-nor flash项目的时候,使用jffs2文件系统,发现在4.14内核下存在无法释放空间的bug,后来进行了修复,修复后功能正常,现将修复patch公开,供后来者学习:
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 7ebacf148..093ffbd82 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -133,7 +133,8 @@ static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size)size_t i;*size = jffs2_acl_size(acl->a_count);
- header = kmalloc(sizeof(*header) + acl->a_count * sizeof(*entry), GFP_KERNEL);
+ header = kmalloc(struct_size(header, a_entries, acl->a_count),
+ GFP_KERNEL);if (!header)return ERR_PTR(-ENOMEM);header->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h
index 2e2b5745c..12d0271bd 100644
--- a/fs/jffs2/acl.h
+++ b/fs/jffs2/acl.h
@@ -22,6 +22,7 @@ struct jffs2_acl_entry_short {struct jffs2_acl_header {jint32_t a_version;
+ struct jffs2_acl_entry a_entries[];};#ifdef CONFIG_JFFS2_FS_POSIX_ACL
diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c
index 79e771ab6..406d9cc84 100644
--- a/fs/jffs2/compr_rtime.c
+++ b/fs/jffs2/compr_rtime.c
@@ -37,9 +37,6 @@ static int jffs2_rtime_compress(unsigned char *data_in,int outpos = 0;int pos=0;- if (*dstlen <= 3)
- return -1;
-memset(positions,0,sizeof(positions));while (pos < (*sourcelen) && outpos <= (*dstlen)-2) {
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index f4a5ec92f..f20cff119 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -227,7 +227,7 @@ static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry)struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(d_inode(dentry));int ret;
- uint32_t now = get_seconds();
+ uint32_t now = JFFS2_NOW();ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name,dentry->d_name.len, dead_f, now);
@@ -260,7 +260,7 @@ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct detype = (d_inode(old_dentry)->i_mode & S_IFMT) >> 12;if (!type) type = DT_REG;- now = get_seconds();
+ now = JFFS2_NOW();ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len, now);if (!ret) {
@@ -400,7 +400,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const charrd->pino = cpu_to_je32(dir_i->i_ino);rd->version = cpu_to_je32(++dir_f->highest_version);rd->ino = cpu_to_je32(inode->i_ino);
- rd->mctime = cpu_to_je32(get_seconds());
+ rd->mctime = cpu_to_je32(JFFS2_NOW());rd->nsize = namelen;rd->type = DT_LNK;rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
@@ -543,7 +543,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, umode_t moderd->pino = cpu_to_je32(dir_i->i_ino);rd->version = cpu_to_je32(++dir_f->highest_version);rd->ino = cpu_to_je32(inode->i_ino);
- rd->mctime = cpu_to_je32(get_seconds());
+ rd->mctime = cpu_to_je32(JFFS2_NOW());rd->nsize = namelen;rd->type = DT_DIR;rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
@@ -588,16 +588,12 @@ static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry)struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode(dentry));struct jffs2_full_dirent *fd;int ret;
- uint32_t now = get_seconds();
+ uint32_t now = JFFS2_NOW();- mutex_lock(&f->sem);for (fd = f->dents ; fd; fd = fd->next) {
- if (fd->ino) {
- mutex_unlock(&f->sem);
+ if (fd->ino)return -ENOTEMPTY;
- }}
- mutex_unlock(&f->sem);ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name,dentry->d_name.len, f, now);
@@ -716,7 +712,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, umode_t moderd->pino = cpu_to_je32(dir_i->i_ino);rd->version = cpu_to_je32(++dir_f->highest_version);rd->ino = cpu_to_je32(inode->i_ino);
- rd->mctime = cpu_to_je32(get_seconds());
+ rd->mctime = cpu_to_je32(JFFS2_NOW());rd->nsize = namelen;/* XXX: This is ugly. */
@@ -801,7 +797,7 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,type = (d_inode(old_dentry)->i_mode & S_IFMT) >> 12;if (!type) type = DT_REG;- now = get_seconds();
+ now = JFFS2_NOW();ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i),d_inode(old_dentry)->i_ino, type,new_dentry->d_name.name, new_dentry->d_name.len, now);
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c
index 4a6cf289b..83b8f06b4 100644
--- a/fs/jffs2/erase.c
+++ b/fs/jffs2/erase.c
@@ -21,14 +21,6 @@#include <linux/pagemap.h>#include "nodelist.h"-struct erase_priv_struct {
- struct jffs2_eraseblock *jeb;
- struct jffs2_sb_info *c;
-};
-
-#ifndef __ECOS
-static void jffs2_erase_callback(struct erase_info *);
-#endifstatic void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
@@ -51,7 +43,7 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,jffs2_dbg(1, "%s(): erase block %#08x (range %#08x-%#08x)\n",__func__,jeb->offset, jeb->offset, jeb->offset + c->sector_size);
- instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
+ instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL);if (!instr) {pr_warn("kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");mutex_lock(&c->erase_free_sem);
@@ -67,18 +59,15 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,memset(instr, 0, sizeof(*instr));- instr->mtd = c->mtd;instr->addr = jeb->offset;instr->len = c->sector_size;
- instr->callback = jffs2_erase_callback;
- instr->priv = (unsigned long)(&instr[1]);
-
- ((struct erase_priv_struct *)instr->priv)->jeb = jeb;
- ((struct erase_priv_struct *)instr->priv)->c = c;ret = mtd_erase(c->mtd, instr);
- if (!ret)
+ if (!ret) {
+ jffs2_erase_succeeded(c, jeb);
+ kfree(instr);return;
+ }bad_offset = instr->fail_addr;kfree(instr);
@@ -214,22 +203,6 @@ static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblockwake_up(&c->erase_wait);}-#ifndef __ECOS
-static void jffs2_erase_callback(struct erase_info *instr)
-{
- struct erase_priv_struct *priv = (void *)instr->priv;
-
- if(instr->state != MTD_ERASE_DONE) {
- pr_warn("Erase at 0x%08llx finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n",
- (unsigned long long)instr->addr, instr->state);
- jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
- } else {
- jffs2_erase_succeeded(priv->c, priv->jeb);
- }
- kfree(instr);
-}
-#endif /* !__ECOS */
-/* Hmmm. Maybe we should accept the extra space it takes and makethis a standard doubly-linked list? */static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c
index bd0428beb..7d8654a14 100644
--- a/fs/jffs2/file.c
+++ b/fs/jffs2/file.c
@@ -175,7 +175,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,ri.uid = cpu_to_je16(i_uid_read(inode));ri.gid = cpu_to_je16(i_gid_read(inode));ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs));
- ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds());
+ ri.atime = ri.ctime = ri.mtime = cpu_to_je32(JFFS2_NOW());ri.offset = cpu_to_je32(inode->i_size);ri.dsize = cpu_to_je32(pageofs - inode->i_size);ri.csize = cpu_to_je32(0);
@@ -283,7 +283,7 @@ static int jffs2_write_end(struct file *filp, struct address_space *mapping,ri->uid = cpu_to_je16(i_uid_read(inode));ri->gid = cpu_to_je16(i_gid_read(inode));ri->isize = cpu_to_je32((uint32_t)inode->i_size);
- ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds());
+ ri->atime = ri->ctime = ri->mtime = cpu_to_je32(JFFS2_NOW());/* In 2.4, it was already kmapped by generic_file_write(). Doesn'thurt to do it again. The alternative is ifdefs, which are ugly. */
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index 3c96f4bdc..eab04eca9 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -408,10 +408,10 @@ int jffs2_do_remount_fs(struct super_block *sb, int *flags, char *data)mutex_unlock(&c->alloc_sem);}- if (!(*flags & MS_RDONLY))
+ if (!(*flags & SB_RDONLY))jffs2_start_garbage_collect_thread(c);- *flags |= MS_NOATIME;
+ *flags |= SB_NOATIME;return 0;}diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index 824e61ede..0a2691fcd 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -31,12 +31,19 @@ struct kvec;#define JFFS2_F_I_GID(f) (i_gid_read(OFNI_EDONI_2SFFJ(f)))#define JFFS2_F_I_RDEV(f) (OFNI_EDONI_2SFFJ(f)->i_rdev)+#define JFFS2_CLAMP_TIME(t) ((uint32_t)clamp_t(time64_t, (t), 0, U32_MAX))
+
+#if BITS_PER_LONG == 32#define ITIME(sec) ((struct timespec){sec, 0})
-#define I_SEC(tv) ((tv).tv_sec)
-#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec)
-#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec)
-#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec)
+#else
+#define ITIME(sec) ((struct timespec64){sec, 0})
+#endif+#define JFFS2_NOW() JFFS2_CLAMP_TIME(ktime_get_real_seconds())
+#define I_SEC(tv) JFFS2_CLAMP_TIME((tv).tv_sec)
+#define JFFS2_F_I_CTIME(f) I_SEC(OFNI_EDONI_2SFFJ(f)->i_ctime)
+#define JFFS2_F_I_MTIME(f) I_SEC(OFNI_EDONI_2SFFJ(f)->i_mtime)
+#define JFFS2_F_I_ATIME(f) I_SEC(OFNI_EDONI_2SFFJ(f)->i_atime)#define sleep_on_spinunlock(wq, s) \do { \DECLARE_WAITQUEUE(__wait, current); \
@@ -59,7 +66,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)}-#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
+#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & SB_RDONLY)#define SECTOR_ADDR(x) ( (((unsigned long)(x) / c->sector_size) * c->sector_size) )#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index d19483fa1..389ea53ea 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -672,22 +672,6 @@ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_rjffs2_free_full_dirent(fd);return -EIO;}
-
-#ifdef CONFIG_JFFS2_SUMMARY
- /*
- * we use CONFIG_JFFS2_SUMMARY because without it, we
- * have checked it while mounting
- */
- crc = crc32(0, fd->name, rd->nsize);
- if (unlikely(crc != je32_to_cpu(rd->name_crc))) {
- JFFS2_NOTICE("name CRC failed on dirent node at"
- "%#08x: read %#08x,calculated %#08x\n",
- ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
- jffs2_mark_node_obsolete(c, ref);
- jffs2_free_full_dirent(fd);
- return 0;
- }
-#endif}fd->nhash = full_name_hash(NULL, fd->name, rd->nsize);
@@ -1430,6 +1414,11 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);+ if (f->target) {
+ kfree(f->target);
+ f->target = NULL;
+ }
+fds = f->dents;while(fds) {fd = fds;
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index 08813789f..90431dd61 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -1075,7 +1075,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblomemcpy(&fd->name, rd->name, checkedlen);fd->name[checkedlen] = 0;- crc = crc32(0, fd->name, checkedlen);
+ crc = crc32(0, fd->name, rd->nsize);if (crc != je32_to_cpu(rd->name_crc)) {pr_notice("%s(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",__func__, ofs, je32_to_cpu(rd->name_crc), crc);
diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c
index 4fe645198..be7c8a6a5 100644
--- a/fs/jffs2/summary.c
+++ b/fs/jffs2/summary.c
@@ -783,8 +783,6 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblockdbg_summary("Writing unknown RWCOMPAT_COPY node type %x\n",je16_to_cpu(temp->u.nodetype));jffs2_sum_disable_collecting(c->summary);
- /* The above call removes the list, nothing more to do */
- goto bail_rwcompat;} else {BUG(); /* unknown node in summary information */}
@@ -796,7 +794,6 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblockc->summary->sum_num--;}
- bail_rwcompat:jffs2_sum_reset_collected(c->summary);diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index 9a9f30edd..902a7dd10 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -47,10 +47,7 @@ static struct inode *jffs2_alloc_inode(struct super_block *sb)static void jffs2_i_callback(struct rcu_head *head){struct inode *inode = container_of(head, struct inode, i_rcu);
- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
-
- kfree(f->target);
- kmem_cache_free(jffs2_inode_cachep, f);
+ kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));}static void jffs2_destroy_inode(struct inode *inode)
@@ -104,8 +101,7 @@ static int jffs2_sync_fs(struct super_block *sb, int wait)struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
- if (jffs2_is_writebuffered(c))
- cancel_delayed_work_sync(&c->wbuf_dwork);
+ cancel_delayed_work_sync(&c->wbuf_dwork);#endifmutex_lock(&c->alloc_sem);
@@ -303,10 +299,10 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent)sb->s_op = &jffs2_super_operations;sb->s_export_op = &jffs2_export_ops;
- sb->s_flags = sb->s_flags | MS_NOATIME;
+ sb->s_flags = sb->s_flags | SB_NOATIME;sb->s_xattr = jffs2_xattr_handlers;#ifdef CONFIG_JFFS2_FS_POSIX_ACL
- sb->s_flags |= MS_POSIXACL;
+ sb->s_flags |= SB_POSIXACL;#endifret = jffs2_do_fill_super(sb, data, silent);return ret;
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
index 2cfe48770..c6821a509 100644
--- a/fs/jffs2/wbuf.c
+++ b/fs/jffs2/wbuf.c
@@ -1208,7 +1208,7 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)if (!c->wbuf)return -ENOMEM;- c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->oobavail, GFP_KERNEL);
+ c->oobbuf = kmalloc_array(NR_OOB_SCAN_PAGES, c->oobavail, GFP_KERNEL);if (!c->oobbuf) {kfree(c->wbuf);return -ENOMEM;
diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c
index cda9a3613..a0eb5f117 100644
--- a/fs/jffs2/write.c
+++ b/fs/jffs2/write.c
@@ -596,6 +596,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,/* File it. This will mark the old one obsolete. */jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+ //jffs2_complete_reservation(c);mutex_unlock(&dir_f->sem);} else {uint32_t nhash = full_name_hash(NULL, name, namelen);
@@ -660,7 +661,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,mutex_unlock(&dead_f->sem);}- jffs2_complete_reservation(c);
+ jffs2_complete_reservation(c); //return 0;}