aboutsummaryrefslogtreecommitdiff
path: root/fs/jffs2
diff options
context:
space:
mode:
Diffstat (limited to 'fs/jffs2')
-rw-r--r--fs/jffs2/LICENCE35
-rw-r--r--fs/jffs2/Makefile18
-rw-r--r--fs/jffs2/README.Locking148
-rw-r--r--fs/jffs2/TODO40
-rw-r--r--fs/jffs2/background.c140
-rw-r--r--fs/jffs2/build.c371
-rw-r--r--fs/jffs2/compr.c469
-rw-r--r--fs/jffs2/compr.h115
-rw-r--r--fs/jffs2/compr_rtime.c132
-rw-r--r--fs/jffs2/compr_rubin.c373
-rw-r--r--fs/jffs2/compr_rubin.h21
-rw-r--r--fs/jffs2/compr_zlib.c218
-rw-r--r--fs/jffs2/comprtest.c307
-rw-r--r--fs/jffs2/dir.c799
-rw-r--r--fs/jffs2/erase.c442
-rw-r--r--fs/jffs2/file.c290
-rw-r--r--fs/jffs2/fs.c677
-rw-r--r--fs/jffs2/gc.c1246
-rw-r--r--fs/jffs2/histo.h3
-rw-r--r--fs/jffs2/histo_mips.h2
-rw-r--r--fs/jffs2/ioctl.c23
-rw-r--r--fs/jffs2/malloc.c205
-rw-r--r--fs/jffs2/nodelist.c681
-rw-r--r--fs/jffs2/nodelist.h473
-rw-r--r--fs/jffs2/nodemgmt.c838
-rw-r--r--fs/jffs2/os-linux.h217
-rw-r--r--fs/jffs2/pushpull.h72
-rw-r--r--fs/jffs2/read.c246
-rw-r--r--fs/jffs2/readinode.c695
-rw-r--r--fs/jffs2/scan.c916
-rw-r--r--fs/jffs2/super.c365
-rw-r--r--fs/jffs2/symlink.c45
-rw-r--r--fs/jffs2/wbuf.c1184
-rw-r--r--fs/jffs2/write.c708
-rw-r--r--fs/jffs2/writev.c50
35 files changed, 12564 insertions, 0 deletions
diff --git a/fs/jffs2/LICENCE b/fs/jffs2/LICENCE
new file mode 100644
index 00000000000..cd81d83e4ad
--- /dev/null
+++ b/fs/jffs2/LICENCE
@@ -0,0 +1,35 @@
+The files in this directory and elsewhere which refer to this LICENCE
+file are part of JFFS2, the Journalling Flash File System v2.
+
+ Copyright (C) 2001, 2002 Red Hat, Inc.
+
+JFFS2 is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 or (at your option) any later
+version.
+
+JFFS2 is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with JFFS2; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+As a special exception, if other files instantiate templates or use
+macros or inline functions from these files, or you compile these
+files and link them with other works to produce a work based on these
+files, these files do not by themselves cause the resulting work to be
+covered by the GNU General Public License. However the source code for
+these files must still be made available in accordance with section (3)
+of the GNU General Public License.
+
+This exception does not invalidate any other reasons why a work based on
+this file might be covered by the GNU General Public License.
+
+For information on obtaining alternative licences for JFFS2, see
+http://sources.redhat.com/jffs2/jffs2-licence.html
+
+
+ $Id: LICENCE,v 1.1 2002/05/20 14:56:37 dwmw2 Exp $
diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile
new file mode 100644
index 00000000000..e3c38ccf9c7
--- /dev/null
+++ b/fs/jffs2/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the Linux Journalling Flash File System v2 (JFFS2)
+#
+# $Id: Makefile.common,v 1.7 2004/11/03 12:57:38 jwboyer Exp $
+#
+
+obj-$(CONFIG_JFFS2_FS) += jffs2.o
+
+jffs2-y := compr.o dir.o file.o ioctl.o nodelist.o malloc.o
+jffs2-y += read.o nodemgmt.o readinode.o write.o scan.o gc.o
+jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
+jffs2-y += super.o
+
+jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o
+jffs2-$(CONFIG_JFFS2_FS_NOR_ECC) += wbuf.o
+jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
+jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
+jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
diff --git a/fs/jffs2/README.Locking b/fs/jffs2/README.Locking
new file mode 100644
index 00000000000..49771cf8513
--- /dev/null
+++ b/fs/jffs2/README.Locking
@@ -0,0 +1,148 @@
+ $Id: README.Locking,v 1.9 2004/11/20 10:35:40 dwmw2 Exp $
+
+ JFFS2 LOCKING DOCUMENTATION
+ ---------------------------
+
+At least theoretically, JFFS2 does not require the Big Kernel Lock
+(BKL), which was always helpfully obtained for it by Linux 2.4 VFS
+code. It has its own locking, as described below.
+
+This document attempts to describe the existing locking rules for
+JFFS2. It is not expected to remain perfectly up to date, but ought to
+be fairly close.
+
+
+ alloc_sem
+ ---------
+
+The alloc_sem is a per-filesystem semaphore, used primarily to ensure
+contiguous allocation of space on the medium. It is automatically
+obtained during space allocations (jffs2_reserve_space()) and freed
+upon write completion (jffs2_complete_reservation()). Note that
+the garbage collector will obtain this right at the beginning of
+jffs2_garbage_collect_pass() and release it at the end, thereby
+preventing any other write activity on the file system during a
+garbage collect pass.
+
+When writing new nodes, the alloc_sem must be held until the new nodes
+have been properly linked into the data structures for the inode to
+which they belong. This is for the benefit of NAND flash - adding new
+nodes to an inode may obsolete old ones, and by holding the alloc_sem
+until this happens we ensure that any data in the write-buffer at the
+time this happens are part of the new node, not just something that
+was written afterwards. Hence, we can ensure the newly-obsoleted nodes
+don't actually get erased until the write-buffer has been flushed to
+the medium.
+
+With the introduction of NAND flash support and the write-buffer,
+the alloc_sem is also used to protect the wbuf-related members of the
+jffs2_sb_info structure. Atomically reading the wbuf_len member to see
+if the wbuf is currently holding any data is permitted, though.
+
+Ordering constraints: See f->sem.
+
+
+ File Semaphore f->sem
+ ---------------------
+
+This is the JFFS2-internal equivalent of the inode semaphore i->i_sem.
+It protects the contents of the jffs2_inode_info private inode data,
+including the linked list of node fragments (but see the notes below on
+erase_completion_lock), etc.
+
+The reason that the i_sem itself isn't used for this purpose is to
+avoid deadlocks with garbage collection -- the VFS will lock the i_sem
+before calling a function which may need to allocate space. The
+allocation may trigger garbage-collection, which may need to move a
+node belonging to the inode which was locked in the first place by the
+VFS. If the garbage collection code were to attempt to lock the i_sem
+of the inode from which it's garbage-collecting a physical node, this
+lead to deadlock, unless we played games with unlocking the i_sem
+before calling the space allocation functions.
+
+Instead of playing such games, we just have an extra internal
+semaphore, which is obtained by the garbage collection code and also
+by the normal file system code _after_ allocation of space.
+
+Ordering constraints:
+
+ 1. Never attempt to allocate space or lock alloc_sem with
+ any f->sem held.
+ 2. Never attempt to lock two file semaphores in one thread.
+ No ordering rules have been made for doing so.
+
+
+ erase_completion_lock spinlock
+ ------------------------------
+
+This is used to serialise access to the eraseblock lists, to the
+per-eraseblock lists of physical jffs2_raw_node_ref structures, and
+(NB) the per-inode list of physical nodes. The latter is a special
+case - see below.
+
+As the MTD API no longer permits erase-completion callback functions
+to be called from bottom-half (timer) context (on the basis that nobody
+ever actually implemented such a thing), it's now sufficient to use
+a simple spin_lock() rather than spin_lock_bh().
+
+Note that the per-inode list of physical nodes (f->nodes) is a special
+case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in
+the list are protected by the file semaphore f->sem. But the erase
+code may remove _obsolete_ nodes from the list while holding only the
+erase_completion_lock. So you can walk the list only while holding the
+erase_completion_lock, and can drop the lock temporarily mid-walk as
+long as the pointer you're holding is to a _valid_ node, not an
+obsolete one.
+
+The erase_completion_lock is also used to protect the c->gc_task
+pointer when the garbage collection thread exits. The code to kill the
+GC thread locks it, sends the signal, then unlocks it - while the GC
+thread itself locks it, zeroes c->gc_task, then unlocks on the exit path.
+
+
+ inocache_lock spinlock
+ ----------------------
+
+This spinlock protects the hashed list (c->inocache_list) of the
+in-core jffs2_inode_cache objects (each inode in JFFS2 has the
+correspondent jffs2_inode_cache object). So, the inocache_lock
+has to be locked while walking the c->inocache_list hash buckets.
+
+Note, the f->sem guarantees that the correspondent jffs2_inode_cache
+will not be removed. So, it is allowed to access it without locking
+the inocache_lock spinlock.
+
+Ordering constraints:
+
+ If both erase_completion_lock and inocache_lock are needed, the
+ c->erase_completion has to be acquired first.
+
+
+ erase_free_sem
+ --------------
+
+This semaphore is only used by the erase code which frees obsolete
+node references and the jffs2_garbage_collect_deletion_dirent()
+function. The latter function on NAND flash must read _obsolete_ nodes
+to determine whether the 'deletion dirent' under consideration can be
+discarded or whether it is still required to show that an inode has
+been unlinked. Because reading from the flash may sleep, the
+erase_completion_lock cannot be held, so an alternative, more
+heavyweight lock was required to prevent the erase code from freeing
+the jffs2_raw_node_ref structures in question while the garbage
+collection code is looking at them.
+
+Suggestions for alternative solutions to this problem would be welcomed.
+
+
+ wbuf_sem
+ --------
+
+This read/write semaphore protects against concurrent access to the
+write-behind buffer ('wbuf') used for flash chips where we must write
+in blocks. It protects both the contents of the wbuf and the metadata
+which indicates which flash region (if any) is currently covered by
+the buffer.
+
+Ordering constraints:
+ Lock wbuf_sem last, after the alloc_sem or and f->sem.
diff --git a/fs/jffs2/TODO b/fs/jffs2/TODO
new file mode 100644
index 00000000000..2bff82fd221
--- /dev/null
+++ b/fs/jffs2/TODO
@@ -0,0 +1,40 @@
+$Id: TODO,v 1.10 2002/09/09 16:31:21 dwmw2 Exp $
+
+ - disable compression in commit_write()?
+ - fine-tune the allocation / GC thresholds
+ - chattr support - turning on/off and tuning compression per-inode
+ - checkpointing (do we need this? scan is quite fast)
+ - make the scan code populate real inodes so read_inode just after
+ mount doesn't have to read the flash twice for large files.
+ Make this a per-inode option, changable with chattr, so you can
+ decide which inodes should be in-core immediately after mount.
+ - test, test, test
+
+ - NAND flash support:
+ - flush_wbuf using GC to fill it, don't just pad.
+ - Deal with write errors. Data don't get lost - we just have to write
+ the affected node(s) out again somewhere else.
+ - make fsync flush only if actually required
+ - make sys_sync() work.
+ - reboot notifier
+ - timed flush of old wbuf
+ - fix magical second arg of jffs2_flush_wbuf(). Split into two or more functions instead.
+
+
+ - Optimisations:
+ - Stop GC from decompressing and immediately recompressing nodes which could
+ just be copied intact. (We now keep track of REF_PRISTINE flag. Easy now.)
+ - Furthermore, in the case where it could be copied intact we don't even need
+ to call iget() for it -- if we use (raw_node_raw->flash_offset & 2) as a flag
+ to show a node can be copied intact and it's _not_ in icache, we could just do
+ it, fix up the next_in_ino list and move on. We would need a way to find out
+ _whether_ it's in icache though -- if it's in icache we also need to do the
+ fragment lists, etc. P'raps a flag or pointer in the jffs2_inode_cache could
+ help. (We have half of this now.)
+ - Stop keeping name in-core with struct jffs2_full_dirent. If we keep the hash in
+ the full dirent, we only need to go to the flash in lookup() when we think we've
+ got a match, and in readdir().
+ - Doubly-linked next_in_ino list to allow us to free obsoleted raw_node_refs immediately?
+ - Remove totlen from jffs2_raw_node_ref? Need to have totlen passed into
+ jffs2_mark_node_obsolete(). Can all callers work it out?
+ - Remove size from jffs2_raw_node_frag.
diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c
new file mode 100644
index 00000000000..1be6de27dd8
--- /dev/null
+++ b/fs/jffs2/background.c
@@ -0,0 +1,140 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: background.c,v 1.50 2004/11/16 20:36:10 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/jffs2.h>
+#include <linux/mtd/mtd.h>
+#include <linux/completion.h>
+#include "nodelist.h"
+
+
+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);
+ spin_unlock(&c->erase_completion_lock);
+}
+
+/* This must only ever be called when no GC thread is currently running */
+int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
+{
+ pid_t pid;
+ int ret = 0;
+
+ if (c->gc_task)
+ BUG();
+
+ init_MUTEX_LOCKED(&c->gc_thread_start);
+ init_completion(&c->gc_thread_exit);
+
+ pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES);
+ if (pid < 0) {
+ printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %d\n", -pid);
+ complete(&c->gc_thread_exit);
+ ret = pid;
+ } else {
+ /* Wait for it... */
+ D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid));
+ down(&c->gc_thread_start);
+ }
+
+ return ret;
+}
+
+void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
+{
+ spin_lock(&c->erase_completion_lock);
+ if (c->gc_task) {
+ D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
+ send_sig(SIGKILL, c->gc_task, 1);
+ }
+ spin_unlock(&c->erase_completion_lock);
+ wait_for_completion(&c->gc_thread_exit);
+}
+
+static int jffs2_garbage_collect_thread(void *_c)
+{
+ struct jffs2_sb_info *c = _c;
+
+ daemonize("jffs2_gcd_mtd%d", c->mtd->index);
+ allow_signal(SIGKILL);
+ allow_signal(SIGSTOP);
+ allow_signal(SIGCONT);
+
+ c->gc_task = current;
+ up(&c->gc_thread_start);
+
+ set_user_nice(current, 10);
+
+ for (;;) {
+ allow_signal(SIGHUP);
+
+ if (!jffs2_thread_should_wake(c)) {
+ set_current_state (TASK_INTERRUPTIBLE);
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n"));
+ /* Yes, there's a race here; we checked jffs2_thread_should_wake()
+ before setting current->state to TASK_INTERRUPTIBLE. But it doesn't
+ matter - We don't care if we miss a wakeup, because the GC thread
+ is only an optimisation anyway. */
+ schedule();
+ }
+
+ if (try_to_freeze(0))
+ continue;
+
+ cond_resched();
+
+ /* Put_super will send a SIGKILL and then wait on the sem.
+ */
+ while (signal_pending(current)) {
+ siginfo_t info;
+ unsigned long signr;
+
+ signr = dequeue_signal_lock(current, &current->blocked, &info);
+
+ switch(signr) {
+ case SIGSTOP:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n"));
+ set_current_state(TASK_STOPPED);
+ schedule();
+ break;
+
+ case SIGKILL:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n"));
+ goto die;
+
+ case SIGHUP:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n"));
+ break;
+ default:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr));
+ }
+ }
+ /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */
+ disallow_signal(SIGHUP);
+
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n"));
+ if (jffs2_garbage_collect_pass(c) == -ENOSPC) {
+ printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n");
+ goto die;
+ }
+ }
+ die:
+ spin_lock(&c->erase_completion_lock);
+ c->gc_task = NULL;
+ spin_unlock(&c->erase_completion_lock);
+ complete_and_exit(&c->gc_thread_exit, 0);
+}
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c
new file mode 100644
index 00000000000..a01dd5fdbb9
--- /dev/null
+++ b/fs/jffs2/build.c
@@ -0,0 +1,371 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: build.c,v 1.69 2004/12/16 20:22:18 dmarlin Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+
+static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
+
+static inline struct jffs2_inode_cache *
+first_inode_chain(int *i, struct jffs2_sb_info *c)
+{
+ for (; *i < INOCACHE_HASHSIZE; (*i)++) {
+ if (c->inocache_list[*i])
+ return c->inocache_list[*i];
+ }
+ return NULL;
+}
+
+static inline struct jffs2_inode_cache *
+next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c)
+{
+ /* More in this chain? */
+ if (ic->next)
+ return ic->next;
+ (*i)++;
+ return first_inode_chain(i, c);
+}
+
+#define for_each_inode(i, c, ic) \
+ for (i = 0, ic = first_inode_chain(&i, (c)); \
+ ic; \
+ ic = next_inode(&i, ic, (c)))
+
+
+static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ struct jffs2_full_dirent *fd;
+
+ D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino));
+
+ /* For each child, increase nlink */
+ for(fd = ic->scan_dents; fd; fd = fd->next) {
+ struct jffs2_inode_cache *child_ic;
+ if (!fd->ino)
+ continue;
+
+ /* XXX: Can get high latency here with huge directories */
+
+ child_ic = jffs2_get_ino_cache(c, fd->ino);
+ if (!child_ic) {
+ printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
+ fd->name, fd->ino, ic->ino);
+ jffs2_mark_node_obsolete(c, fd->raw);
+ continue;
+ }
+
+ if (child_ic->nlink++ && fd->type == DT_DIR) {
+ printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
+ if (fd->ino == 1 && ic->ino == 1) {
+ printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
+ printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
+ }
+ /* What do we do about it? */
+ }
+ D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
+ /* Can't free them. We might need them in pass 2 */
+ }
+}
+
+/* Scan plan:
+ - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
+ - Scan directory tree from top down, setting nlink in inocaches
+ - Scan inocaches for inodes with nlink==0
+*/
+static int jffs2_build_filesystem(struct jffs2_sb_info *c)
+{
+ int ret;
+ int i;
+ struct jffs2_inode_cache *ic;
+ struct jffs2_full_dirent *fd;
+ struct jffs2_full_dirent *dead_fds = NULL;
+
+ /* First, scan the medium and build all the inode caches with
+ lists of physical nodes */
+
+ c->flags |= JFFS2_SB_FLAG_MOUNTING;
+ ret = jffs2_scan_medium(c);
+ if (ret)
+ goto exit;
+
+ D1(printk(KERN_DEBUG "Scanned flash completely\n"));
+ D2(jffs2_dump_block_lists(c));
+
+ /* Now scan the directory tree, increasing nlink according to every dirent found. */
+ for_each_inode(i, c, ic) {
+ D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
+
+ D1(BUG_ON(ic->ino > c->highest_ino));
+
+ if (ic->scan_dents) {
+ jffs2_build_inode_pass1(c, ic);
+ cond_resched();
+ }
+ }
+ c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
+
+ D1(printk(KERN_DEBUG "Pass 1 complete\n"));
+
+ /* Next, scan for inodes with nlink == 0 and remove them. If
+ they were directories, then decrement the nlink of their
+ children too, and repeat the scan. As that's going to be
+ a fairly uncommon occurrence, it's not so evil to do it this
+ way. Recursion bad. */
+ D1(printk(KERN_DEBUG "Pass 2 starting\n"));
+
+ for_each_inode(i, c, ic) {
+ D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
+ if (ic->nlink)
+ continue;
+
+ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
+ cond_resched();
+ }
+
+ D1(printk(KERN_DEBUG "Pass 2a starting\n"));
+
+ while (dead_fds) {
+ fd = dead_fds;
+ dead_fds = fd->next;
+
+ ic = jffs2_get_ino_cache(c, fd->ino);
+ D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic));
+
+ if (ic)
+ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
+ jffs2_free_full_dirent(fd);
+ }
+
+ D1(printk(KERN_DEBUG "Pass 2 complete\n"));
+
+ /* Finally, we can scan again and free the dirent structs */
+ for_each_inode(i, c, ic) {
+ D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
+
+ while(ic->scan_dents) {
+ fd = ic->scan_dents;
+ ic->scan_dents = fd->next;
+ jffs2_free_full_dirent(fd);
+ }
+ ic->scan_dents = NULL;
+ cond_resched();
+ }
+ D1(printk(KERN_DEBUG "Pass 3 complete\n"));
+ D2(jffs2_dump_block_lists(c));
+
+ /* Rotate the lists by some number to ensure wear levelling */
+ jffs2_rotate_lists(c);
+
+ ret = 0;
+
+exit:
+ if (ret) {
+ for_each_inode(i, c, ic) {
+ while(ic->scan_dents) {
+ fd = ic->scan_dents;
+ ic->scan_dents = fd->next;
+ jffs2_free_full_dirent(fd);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds)
+{
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *fd;
+
+ D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
+
+ raw = ic->nodes;
+ while (raw != (void *)ic) {
+ struct jffs2_raw_node_ref *next = raw->next_in_ino;
+ D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw)));
+ jffs2_mark_node_obsolete(c, raw);
+ raw = next;
+ }
+
+ if (ic->scan_dents) {
+ int whinged = 0;
+ D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino));
+
+ while(ic->scan_dents) {
+ struct jffs2_inode_cache *child_ic;
+
+ fd = ic->scan_dents;
+ ic->scan_dents = fd->next;
+
+ if (!fd->ino) {
+ /* It's a deletion dirent. Ignore it */
+ D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name));
+ jffs2_free_full_dirent(fd);
+ continue;
+ }
+ if (!whinged) {
+ whinged = 1;
+ printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
+ }
+
+ D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
+ fd->name, fd->ino));
+
+ child_ic = jffs2_get_ino_cache(c, fd->ino);
+ if (!child_ic) {
+ printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
+ jffs2_free_full_dirent(fd);
+ continue;
+ }
+
+ /* Reduce nlink of the child. If it's now zero, stick it on the
+ dead_fds list to be cleaned up later. Else just free the fd */
+
+ child_ic->nlink--;
+
+ if (!child_ic->nlink) {
+ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n",
+ fd->ino, fd->name));
+ fd->next = *dead_fds;
+ *dead_fds = fd;
+ } else {
+ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
+ fd->ino, fd->name, child_ic->nlink));
+ jffs2_free_full_dirent(fd);
+ }
+ }
+ }
+
+ /*
+ We don't delete the inocache from the hash list and free it yet.
+ The erase code will do that, when all the nodes are completely gone.
+ */
+}
+
+static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
+{
+ uint32_t size;
+
+ /* Deletion should almost _always_ be allowed. We're fairly
+ buggered once we stop allowing people to delete stuff
+ because there's not enough free space... */
+ c->resv_blocks_deletion = 2;
+
+ /* Be conservative about how much space we need before we allow writes.
+ On top of that which is required for deletia, require an extra 2%
+ of the medium to be available, for overhead caused by nodes being
+ split across blocks, etc. */
+
+ size = c->flash_size / 50; /* 2% of flash size */
+ size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */
+ size += c->sector_size - 1; /* ... and round up */
+
+ c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size);
+
+ /* When do we let the GC thread run in the background */
+
+ c->resv_blocks_gctrigger = c->resv_blocks_write + 1;
+
+ /* When do we allow garbage collection to merge nodes to make
+ long-term progress at the expense of short-term space exhaustion? */
+ c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1;
+
+ /* When do we allow garbage collection to eat from bad blocks rather
+ than actually making progress? */
+ c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
+
+ /* 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);
+
+ D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
+ c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks));
+ D1(printk(KERN_DEBUG "Blocks required to allow deletion: %d (%d KiB)\n",
+ c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024));
+ D1(printk(KERN_DEBUG "Blocks required to allow writes: %d (%d KiB)\n",
+ c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024));
+ D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n",
+ c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024));
+ D1(printk(KERN_DEBUG "Blocks required to allow GC merges: %d (%d KiB)\n",
+ c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024));
+ D1(printk(KERN_DEBUG "Blocks required to GC bad blocks: %d (%d KiB)\n",
+ c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024));
+ D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n",
+ c->nospc_dirty_size));
+}
+
+int jffs2_do_mount_fs(struct jffs2_sb_info *c)
+{
+ int i;
+
+ c->free_size = c->flash_size;
+ c->nr_blocks = c->flash_size / c->sector_size;
+ if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
+ c->blocks = vmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks);
+ else
+ c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
+ if (!c->blocks)
+ return -ENOMEM;
+ for (i=0; i<c->nr_blocks; i++) {
+ INIT_LIST_HEAD(&c->blocks[i].list);
+ c->blocks[i].offset = i * c->sector_size;
+ c->blocks[i].free_size = c->sector_size;
+ c->blocks[i].dirty_size = 0;
+ c->blocks[i].wasted_size = 0;
+ c->blocks[i].unchecked_size = 0;
+ c->blocks[i].used_size = 0;
+ c->blocks[i].first_node = NULL;
+ c->blocks[i].last_node = NULL;
+ c->blocks[i].bad_count = 0;
+ }
+
+ init_MUTEX(&c->alloc_sem);
+ init_MUTEX(&c->erase_free_sem);
+ init_waitqueue_head(&c->erase_wait);
+ init_waitqueue_head(&c->inocache_wq);
+ spin_lock_init(&c->erase_completion_lock);
+ spin_lock_init(&c->inocache_lock);
+
+ INIT_LIST_HEAD(&c->clean_list);
+ INIT_LIST_HEAD(&c->very_dirty_list);
+ INIT_LIST_HEAD(&c->dirty_list);
+ INIT_LIST_HEAD(&c->erasable_list);
+ INIT_LIST_HEAD(&c->erasing_list);
+ INIT_LIST_HEAD(&c->erase_pending_list);
+ INIT_LIST_HEAD(&c->erasable_pending_wbuf_list);
+ INIT_LIST_HEAD(&c->erase_complete_list);
+ INIT_LIST_HEAD(&c->free_list);
+ INIT_LIST_HEAD(&c->bad_list);
+ INIT_LIST_HEAD(&c->bad_used_list);
+ c->highest_ino = 1;
+
+ if (jffs2_build_filesystem(c)) {
+ D1(printk(KERN_DEBUG "build_fs failed\n"));
+ jffs2_free_ino_caches(c);
+ jffs2_free_raw_node_refs(c);
+ if (c->mtd->flags & MTD_NO_VIRTBLOCKS) {
+ vfree(c->blocks);
+ } else {
+ kfree(c->blocks);
+ }
+ return -EIO;
+ }
+
+ jffs2_calc_trigger_levels(c);
+
+ return 0;
+}
diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c
new file mode 100644
index 00000000000..af922a9618a
--- /dev/null
+++ b/fs/jffs2/compr.c
@@ -0,0 +1,469 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ * University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.