Secure deletion under JFFS2 is complicated by the log of dirty nodes
that may contain obsoleted versions of sensitive data.  There is an
existing mechanism to trigger a single gc step via -HUP signal;
extend this to trigger gc collection of all currently-dirty data via
a -POLL signal.

On receipt of -POLL, the gc will retire the current nextblock, store
the last dirty list entry, and keep continuously cycling collection
until that entry has been cleaned.

Signed-off-by: Theuns Verwoerd <theuns.verwo...@alliedtelesis.co.nz>
---
 fs/jffs2/background.c  | 31 ++++++++++++++++++++++++++++++-
 fs/jffs2/build.c       |  1 +
 fs/jffs2/jffs2_fs_sb.h |  2 ++
 fs/jffs2/nodelist.h    |  1 +
 fs/jffs2/nodemgmt.c    |  6 +++++-
 5 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c
index 453a6a1fff34..4c29e2d323c4 100644
--- a/fs/jffs2/background.c
+++ b/fs/jffs2/background.c
@@ -72,15 +72,27 @@ void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info 
*c)
                wait_for_completion(&c->gc_thread_exit);
 }
 
+static int list_contains(struct list_head *list, struct list_head *entry)
+{
+       struct list_head *ptr;
+
+       list_for_each(ptr, list) {
+               if (ptr == entry)
+                       return 1;
+       }
+       return 0;
+}
+
 static int jffs2_garbage_collect_thread(void *_c)
 {
        struct jffs2_sb_info *c = _c;
        sigset_t hupmask;
 
-       siginitset(&hupmask, sigmask(SIGHUP));
+       siginitset(&hupmask, sigmask(SIGHUP) | sigmask(SIGPOLL));
        allow_signal(SIGKILL);
        allow_signal(SIGSTOP);
        allow_signal(SIGHUP);
+       allow_signal(SIGPOLL);
 
        c->gc_task = current;
        complete(&c->gc_thread_start);
@@ -143,6 +155,15 @@ static int jffs2_garbage_collect_thread(void *_c)
                                jffs2_dbg(1, "%s(): SIGHUP received\n",
                                          __func__);
                                break;
+                       case SIGPOLL:
+                               if (!c->tidemark) {
+                                       /* Force retire current half-used block 
*/
+                                       if (c->nextblock)
+                                               jffs2_close_nextblock(c, 
c->nextblock);
+                                       /* Keep going until we hit the last 
element in the (now) current dirty list */
+                                       c->tidemark = c->dirty_list.prev;
+                               }
+                               break;
                        default:
                                jffs2_dbg(1, "%s(): signal %ld received\n",
                                          __func__, signr);
@@ -156,6 +177,14 @@ static int jffs2_garbage_collect_thread(void *_c)
                        pr_notice("No space for garbage collection. Aborting GC 
thread\n");
                        goto die;
                }
+               /* If we're working towards a tidemark, keep going until it's 
clean */
+               if (c->tidemark && (
+                       !list_empty(&c->very_dirty_list) ||
+                       list_contains(&c->dirty_list, c->tidemark) ||
+                       (&c->gcblock->list) == c->tidemark))
+                       goto again;
+               else if (c->tidemark)
+                       c->tidemark = NULL;
        }
  die:
        spin_lock(&c->erase_completion_lock);
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c
index b288c8ae1236..7d128705648f 100644
--- a/fs/jffs2/build.c
+++ b/fs/jffs2/build.c
@@ -405,6 +405,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c)
        INIT_LIST_HEAD(&c->bad_used_list);
        c->highest_ino = 1;
        c->summary = NULL;
+       c->tidemark = NULL;
 
        ret = jffs2_sum_init(c);
        if (ret)
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h
index 778275f48a87..25960589e3c0 100644
--- a/fs/jffs2/jffs2_fs_sb.h
+++ b/fs/jffs2/jffs2_fs_sb.h
@@ -87,6 +87,8 @@ struct jffs2_sb_info {
 
        uint32_t nospc_dirty_size;
 
+       struct list_head *tidemark;             /* Last dirty block at the time 
a full sync started */
+
        uint32_t nr_blocks;
        struct jffs2_eraseblock *blocks;        /* The whole array of blocks. 
Used for getting blocks
                                                 * from the offset (blocks[ofs 
/ sector_size]) */
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
index 0637271f3770..73348bc7f545 100644
--- a/fs/jffs2/nodelist.h
+++ b/fs/jffs2/nodelist.h
@@ -386,6 +386,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t 
minsize,
                        uint32_t *len, int prio, uint32_t sumsize);
 int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
                        uint32_t *len, uint32_t sumsize);
+void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock 
*jeb);
 struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info 
*c, 
                                                       uint32_t ofs, uint32_t 
len,
                                                       struct jffs2_inode_cache 
*ic);
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index a7bbe879cfc3..c07c53f69516 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -240,7 +240,7 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, 
uint32_t minsize,
 
 /* Classify nextblock (clean, dirty of verydirty) and force to select an other 
one */
 
-static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct 
jffs2_eraseblock *jeb)
+void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock 
*jeb)
 {
 
        if (c->nextblock == NULL) {
@@ -875,6 +875,10 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c)
                }
        }
 
+       /* Pending cleanup, always wake */
+       if (c->tidemark)
+               ret = 1;
+
        jffs2_dbg(1, "%s(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 
0x%x, vdirty_blocks %d: %s\n",
                  __func__, c->nr_free_blocks, c->nr_erasing_blocks,
                  c->dirty_size, nr_very_dirty, ret ? "yes" : "no");
-- 
2.18.0

Reply via email to