Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Tento commit je obsažen v:
35
fs/jffs2/LICENCE
Normální soubor
35
fs/jffs2/LICENCE
Normální soubor
@@ -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 $
|
18
fs/jffs2/Makefile
Normální soubor
18
fs/jffs2/Makefile
Normální soubor
@@ -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
|
148
fs/jffs2/README.Locking
Normální soubor
148
fs/jffs2/README.Locking
Normální soubor
@@ -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.
|
40
fs/jffs2/TODO
Normální soubor
40
fs/jffs2/TODO
Normální soubor
@@ -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.
|
140
fs/jffs2/background.c
Normální soubor
140
fs/jffs2/background.c
Normální soubor
@@ -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, ¤t->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);
|
||||
}
|
371
fs/jffs2/build.c
Normální soubor
371
fs/jffs2/build.c
Normální soubor
@@ -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;
|
||||
}
|
469
fs/jffs2/compr.c
Normální soubor
469
fs/jffs2/compr.c
Normální soubor
@@ -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.
|
||||
*
|
||||
* $Id: compr.c,v 1.42 2004/08/07 21:56:08 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include "compr.h"
|
||||
|
||||
static DEFINE_SPINLOCK(jffs2_compressor_list_lock);
|
||||
|
||||
/* Available compressors are on this list */
|
||||
static LIST_HEAD(jffs2_compressor_list);
|
||||
|
||||
/* Actual compression mode */
|
||||
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;
|
||||
|
||||
/* jffs2_compress:
|
||||
* @data: Pointer to uncompressed data
|
||||
* @cdata: Pointer to returned pointer to buffer for compressed data
|
||||
* @datalen: On entry, holds the amount of data available for compression.
|
||||
* On exit, expected to hold the amount of data actually compressed.
|
||||
* @cdatalen: On entry, holds the amount of space available for compressed
|
||||
* data. On exit, expected to hold the actual size of the compressed
|
||||
* data.
|
||||
*
|
||||
* Returns: Lower byte to be stored with data indicating compression type used.
|
||||
* Zero is used to show that the data could not be compressed - the
|
||||
* compressed version was actually larger than the original.
|
||||
* Upper byte will be used later. (soon)
|
||||
*
|
||||
* If the cdata buffer isn't large enough to hold all the uncompressed data,
|
||||
* jffs2_compress should compress as much as will fit, and should set
|
||||
* *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)
|
||||
{
|
||||
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;
|
||||
|
||||
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");
|
||||
}
|
||||
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;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* Older code had a bug where it would write non-zero 'usercompr'
|
||||
fields. Deal with it. */
|
||||
if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB)
|
||||
comprtype &= 0xff;
|
||||
|
||||
switch (comprtype & 0xff) {
|
||||
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++;
|
||||
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;
|
||||
}
|
||||
}
|
||||
printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype);
|
||||
spin_unlock(&jffs2_compressor_list_lock);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jffs2_register_compressor(struct jffs2_compressor *comp)
|
||||
{
|
||||
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));
|
||||
|
||||
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);
|
||||
out:
|
||||
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;
|
||||
}
|
||||
|
||||
int jffs2_unregister_compressor(struct jffs2_compressor *comp)
|
||||
{
|
||||
D2(struct jffs2_compressor *this;)
|
||||
|
||||
D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_JFFS2_PROC
|
||||
|
||||
#define JFFS2_STAT_BUF_SIZE 16000
|
||||
|
||||
char *jffs2_list_compressors(void)
|
||||
{
|
||||
struct jffs2_compressor *this;
|
||||
char *buf, *act_buf;
|
||||
|
||||
act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
|
||||
list_for_each_entry(this, &jffs2_compressor_list, list) {
|
||||
act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
|
||||
if ((this->disabled)||(!this->compress))
|
||||
act_buf += sprintf(act_buf,"disabled");
|
||||
else
|
||||
act_buf += sprintf(act_buf,"enabled");
|
||||
act_buf += sprintf(act_buf,"\n");
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *jffs2_stats(void)
|
||||
{
|
||||
struct jffs2_compressor *this;
|
||||
char *buf, *act_buf;
|
||||
|
||||
act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
|
||||
|
||||
act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n");
|
||||
act_buf += sprintf(act_buf,"%10s ","none");
|
||||
act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks,
|
||||
none_stat_compr_size, none_stat_decompr_blocks);
|
||||
spin_lock(&jffs2_compressor_list_lock);
|
||||
list_for_each_entry(this, &jffs2_compressor_list, list) {
|
||||
act_buf += sprintf(act_buf,"%10s ",this->name);
|
||||
if ((this->disabled)||(!this->compress))
|
||||
act_buf += sprintf(act_buf,"- ");
|
||||
else
|
||||
act_buf += sprintf(act_buf,"+ ");
|
||||
act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks,
|
||||
this->stat_compr_new_size, this->stat_compr_orig_size,
|
||||
this->stat_decompr_blocks);
|
||||
act_buf += sprintf(act_buf,"\n");
|
||||
}
|
||||
spin_unlock(&jffs2_compressor_list_lock);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *jffs2_get_compression_mode_name(void)
|
||||
{
|
||||
switch (jffs2_compression_mode) {
|
||||
case JFFS2_COMPR_MODE_NONE:
|
||||
return "none";
|
||||
case JFFS2_COMPR_MODE_PRIORITY:
|
||||
return "priority";
|
||||
case JFFS2_COMPR_MODE_SIZE:
|
||||
return "size";
|
||||
}
|
||||
return "unkown";
|
||||
}
|
||||
|
||||
int jffs2_set_compression_mode_name(const char *name)
|
||||
{
|
||||
if (!strcmp("none",name)) {
|
||||
jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp("priority",name)) {
|
||||
jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp("size",name)) {
|
||||
jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int jffs2_compressor_Xable(const char *name, int disabled)
|
||||
{
|
||||
struct jffs2_compressor *this;
|
||||
spin_lock(&jffs2_compressor_list_lock);
|
||||
list_for_each_entry(this, &jffs2_compressor_list, list) {
|
||||
if (!strcmp(this->name, name)) {
|
||||
this->disabled = disabled;
|
||||
spin_unlock(&jffs2_compressor_list_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
spin_unlock(&jffs2_compressor_list_lock);
|
||||
printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int jffs2_enable_compressor_name(const char *name)
|
||||
{
|
||||
return jffs2_compressor_Xable(name, 0);
|
||||
}
|
||||
|
||||
int jffs2_disable_compressor_name(const char *name)
|
||||
{
|
||||
return jffs2_compressor_Xable(name, 1);
|
||||
}
|
||||
|
||||
int jffs2_set_compressor_priority(const char *name, int priority)
|
||||
{
|
||||
struct jffs2_compressor *this,*comp;
|
||||
spin_lock(&jffs2_compressor_list_lock);
|
||||
list_for_each_entry(this, &jffs2_compressor_list, list) {
|
||||
if (!strcmp(this->name, name)) {
|
||||
this->priority = priority;
|
||||
comp = this;
|
||||
goto reinsert;
|
||||
}
|
||||
}
|
||||
spin_unlock(&jffs2_compressor_list_lock);
|
||||
printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
|
||||
return 1;
|
||||
reinsert:
|
||||
/* list is sorted in the order of priority, so if
|
||||
we change it we have to reinsert it into the
|
||||
good place */
|
||||
list_del(&comp->list);
|
||||
list_for_each_entry(this, &jffs2_compressor_list, list) {
|
||||
if (this->priority < comp->priority) {
|
||||
list_add(&comp->list, this->list.prev);
|
||||
spin_unlock(&jffs2_compressor_list_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
list_add_tail(&comp->list, &jffs2_compressor_list);
|
||||
spin_unlock(&jffs2_compressor_list_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
|
||||
{
|
||||
if (orig != comprbuf)
|
||||
kfree(comprbuf);
|
||||
}
|
||||
|
||||
int jffs2_compressors_init(void)
|
||||
{
|
||||
/* Registering compressors */
|
||||
#ifdef CONFIG_JFFS2_ZLIB
|
||||
jffs2_zlib_init();
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_RTIME
|
||||
jffs2_rtime_init();
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_RUBIN
|
||||
jffs2_rubinmips_init();
|
||||
jffs2_dynrubin_init();
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_LZARI
|
||||
jffs2_lzari_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");)
|
||||
#else
|
||||
#ifdef CONFIG_JFFS2_CMODE_SIZE
|
||||
jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
|
||||
D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
|
||||
#else
|
||||
D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jffs2_compressors_exit(void)
|
||||
{
|
||||
/* Unregistering compressors */
|
||||
#ifdef CONFIG_JFFS2_LZO
|
||||
jffs2_lzo_exit();
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_LZARI
|
||||
jffs2_lzari_exit();
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_RUBIN
|
||||
jffs2_dynrubin_exit();
|
||||
jffs2_rubinmips_exit();
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_RTIME
|
||||
jffs2_rtime_exit();
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_ZLIB
|
||||
jffs2_zlib_exit();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
115
fs/jffs2/compr.h
Normální soubor
115
fs/jffs2/compr.h
Normální soubor
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
|
||||
* University of Szeged, Hungary
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in the
|
||||
* jffs2 directory.
|
||||
*
|
||||
* $Id: compr.h,v 1.6 2004/07/16 15:17:57 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __JFFS2_COMPR_H__
|
||||
#define __JFFS2_COMPR_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/jffs2_fs_i.h>
|
||||
#include <linux/jffs2_fs_sb.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
#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_RUBINMIPS_DISABLED /* RUBINs will be used only */
|
||||
#define JFFS2_DYNRUBIN_DISABLED /* for decompression */
|
||||
|
||||
#define JFFS2_COMPR_MODE_NONE 0
|
||||
#define JFFS2_COMPR_MODE_PRIORITY 1
|
||||
#define JFFS2_COMPR_MODE_SIZE 2
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
int jffs2_register_compressor(struct jffs2_compressor *comp);
|
||||
int jffs2_unregister_compressor(struct jffs2_compressor *comp);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
|
||||
|
||||
#ifdef CONFIG_JFFS2_PROC
|
||||
int jffs2_enable_compressor_name(const char *name);
|
||||
int jffs2_disable_compressor_name(const char *name);
|
||||
int jffs2_set_compression_mode_name(const char *mode_name);
|
||||
char *jffs2_get_compression_mode_name(void);
|
||||
int jffs2_set_compressor_priority(const char *mode_name, int priority);
|
||||
char *jffs2_list_compressors(void);
|
||||
char *jffs2_stats(void);
|
||||
#endif
|
||||
|
||||
/* Compressor modules */
|
||||
/* These functions will be called by jffs2_compressors_init/exit */
|
||||
|
||||
#ifdef CONFIG_JFFS2_RUBIN
|
||||
int jffs2_rubinmips_init(void);
|
||||
void jffs2_rubinmips_exit(void);
|
||||
int jffs2_dynrubin_init(void);
|
||||
void jffs2_dynrubin_exit(void);
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_RTIME
|
||||
int jffs2_rtime_init(void);
|
||||
void jffs2_rtime_exit(void);
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_ZLIB
|
||||
int jffs2_zlib_init(void);
|
||||
void jffs2_zlib_exit(void);
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_LZARI
|
||||
int jffs2_lzari_init(void);
|
||||
void jffs2_lzari_exit(void);
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_LZO
|
||||
int jffs2_lzo_init(void);
|
||||
void jffs2_lzo_exit(void);
|
||||
#endif
|
||||
|
||||
#endif /* __JFFS2_COMPR_H__ */
|
132
fs/jffs2/compr_rtime.c
Normální soubor
132
fs/jffs2/compr_rtime.c
Normální soubor
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2001-2003 Red Hat, Inc.
|
||||
*
|
||||
* Created by Arjan van de Ven <arjanv@redhat.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $
|
||||
*
|
||||
*
|
||||
* Very simple lz77-ish encoder.
|
||||
*
|
||||
* Theory of operation: Both encoder and decoder have a list of "last
|
||||
* occurrences" for every possible source-value; after sending the
|
||||
* first source-byte, the second byte indicated the "run" length of
|
||||
* matches
|
||||
*
|
||||
* The algorithm is intended to only send "whole bytes", no bit-messing.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include "compr.h"
|
||||
|
||||
/* _compress returns the compressed size, -1 if bigger */
|
||||
static int jffs2_rtime_compress(unsigned char *data_in,
|
||||
unsigned char *cpage_out,
|
||||
uint32_t *sourcelen, uint32_t *dstlen,
|
||||
void *model)
|
||||
{
|
||||
short positions[256];
|
||||
int outpos = 0;
|
||||
int pos=0;
|
||||
|
||||
memset(positions,0,sizeof(positions));
|
||||
|
||||
while (pos < (*sourcelen) && outpos <= (*dstlen)-2) {
|
||||
int backpos, runlen=0;
|
||||
unsigned char value;
|
||||
|
||||
value = data_in[pos];
|
||||
|
||||
cpage_out[outpos++] = data_in[pos++];
|
||||
|
||||
backpos = positions[value];
|
||||
positions[value]=pos;
|
||||
|
||||
while ((backpos < pos) && (pos < (*sourcelen)) &&
|
||||
(data_in[pos]==data_in[backpos++]) && (runlen<255)) {
|
||||
pos++;
|
||||
runlen++;
|
||||
}
|
||||
cpage_out[outpos++] = runlen;
|
||||
}
|
||||
|
||||
if (outpos >= pos) {
|
||||
/* We failed */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Tell the caller how much we managed to compress, and how much space it took */
|
||||
*sourcelen = pos;
|
||||
*dstlen = outpos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int jffs2_rtime_decompress(unsigned char *data_in,
|
||||
unsigned char *cpage_out,
|
||||
uint32_t srclen, uint32_t destlen,
|
||||
void *model)
|
||||
{
|
||||
short positions[256];
|
||||
int outpos = 0;
|
||||
int pos=0;
|
||||
|
||||
memset(positions,0,sizeof(positions));
|
||||
|
||||
while (outpos<destlen) {
|
||||
unsigned char value;
|
||||
int backoffs;
|
||||
int repeat;
|
||||
|
||||
value = data_in[pos++];
|
||||
cpage_out[outpos++] = value; /* first the verbatim copied byte */
|
||||
repeat = data_in[pos++];
|
||||
backoffs = positions[value];
|
||||
|
||||
positions[value]=outpos;
|
||||
if (repeat) {
|
||||
if (backoffs + repeat >= outpos) {
|
||||
while(repeat) {
|
||||
cpage_out[outpos++] = cpage_out[backoffs++];
|
||||
repeat--;
|
||||
}
|
||||
} else {
|
||||
memcpy(&cpage_out[outpos],&cpage_out[backoffs],repeat);
|
||||
outpos+=repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct jffs2_compressor jffs2_rtime_comp = {
|
||||
.priority = JFFS2_RTIME_PRIORITY,
|
||||
.name = "rtime",
|
||||
.compr = JFFS2_COMPR_RTIME,
|
||||
.compress = &jffs2_rtime_compress,
|
||||
.decompress = &jffs2_rtime_decompress,
|
||||
#ifdef JFFS2_RTIME_DISABLED
|
||||
.disabled = 1,
|
||||
#else
|
||||
.disabled = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
int jffs2_rtime_init(void)
|
||||
{
|
||||
return jffs2_register_compressor(&jffs2_rtime_comp);
|
||||
}
|
||||
|
||||
void jffs2_rtime_exit(void)
|
||||
{
|
||||
jffs2_unregister_compressor(&jffs2_rtime_comp);
|
||||
}
|
373
fs/jffs2/compr_rubin.c
Normální soubor
373
fs/jffs2/compr_rubin.c
Normální soubor
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2001, 2002 Red Hat, Inc.
|
||||
*
|
||||
* Created by Arjan van de Ven <arjanv@redhat.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include "compr_rubin.h"
|
||||
#include "histo_mips.h"
|
||||
#include "compr.h"
|
||||
|
||||
static void init_rubin(struct rubin_state *rs, int div, int *bits)
|
||||
{
|
||||
int c;
|
||||
|
||||
rs->q = 0;
|
||||
rs->p = (long) (2 * UPPER_BIT_RUBIN);
|
||||
rs->bit_number = (long) 0;
|
||||
rs->bit_divider = div;
|
||||
for (c=0; c<8; c++)
|
||||
rs->bits[c] = bits[c];
|
||||
}
|
||||
|
||||
|
||||
static int encode(struct rubin_state *rs, long A, long B, int symbol)
|
||||
{
|
||||
|
||||
long i0, i1;
|
||||
int ret;
|
||||
|
||||
while ((rs->q >= UPPER_BIT_RUBIN) || ((rs->p + rs->q) <= UPPER_BIT_RUBIN)) {
|
||||
rs->bit_number++;
|
||||
|
||||
ret = pushbit(&rs->pp, (rs->q & UPPER_BIT_RUBIN) ? 1 : 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
rs->q &= LOWER_BITS_RUBIN;
|
||||
rs->q <<= 1;
|
||||
rs->p <<= 1;
|
||||
}
|
||||
i0 = A * rs->p / (A + B);
|
||||
if (i0 <= 0) {
|
||||
i0 = 1;
|
||||
}
|
||||
if (i0 >= rs->p) {
|
||||
i0 = rs->p - 1;
|
||||
}
|
||||
i1 = rs->p - i0;
|
||||
|
||||
if (symbol == 0)
|
||||
rs->p = i0;
|
||||
else {
|
||||
rs->p = i1;
|
||||
rs->q += i0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void end_rubin(struct rubin_state *rs)
|
||||
{
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RUBIN_REG_SIZE; i++) {
|
||||
pushbit(&rs->pp, (UPPER_BIT_RUBIN & rs->q) ? 1 : 0, 1);
|
||||
rs->q &= LOWER_BITS_RUBIN;
|
||||
rs->q <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void init_decode(struct rubin_state *rs, int div, int *bits)
|
||||
{
|
||||
init_rubin(rs, div, bits);
|
||||
|
||||
/* behalve lower */
|
||||
rs->rec_q = 0;
|
||||
|
||||
for (rs->bit_number = 0; rs->bit_number++ < RUBIN_REG_SIZE; rs->rec_q = rs->rec_q * 2 + (long) (pullbit(&rs->pp)))
|
||||
;
|
||||
}
|
||||
|
||||
static void __do_decode(struct rubin_state *rs, unsigned long p, unsigned long q)
|
||||
{
|
||||
register unsigned long lower_bits_rubin = LOWER_BITS_RUBIN;
|
||||
unsigned long rec_q;
|
||||
int c, bits = 0;
|
||||
|
||||
/*
|
||||
* First, work out how many bits we need from the input stream.
|
||||
* Note that we have already done the initial check on this
|
||||
* loop prior to calling this function.
|
||||
*/
|
||||
do {
|
||||
bits++;
|
||||
q &= lower_bits_rubin;
|
||||
q <<= 1;
|
||||
p <<= 1;
|
||||
} while ((q >= UPPER_BIT_RUBIN) || ((p + q) <= UPPER_BIT_RUBIN));
|
||||
|
||||
rs->p = p;
|
||||
rs->q = q;
|
||||
|
||||
rs->bit_number += bits;
|
||||
|
||||
/*
|
||||
* Now get the bits. We really want this to be "get n bits".
|
||||
*/
|
||||
rec_q = rs->rec_q;
|
||||
do {
|
||||
c = pullbit(&rs->pp);
|
||||
rec_q &= lower_bits_rubin;
|
||||
rec_q <<= 1;
|
||||
rec_q += c;
|
||||
} while (--bits);
|
||||
rs->rec_q = rec_q;
|
||||
}
|
||||
|
||||
static int decode(struct rubin_state *rs, long A, long B)
|
||||
{
|
||||
unsigned long p = rs->p, q = rs->q;
|
||||
long i0, threshold;
|
||||
int symbol;
|
||||
|
||||
if (q >= UPPER_BIT_RUBIN || ((p + q) <= UPPER_BIT_RUBIN))
|
||||
__do_decode(rs, p, q);
|
||||
|
||||
i0 = A * rs->p / (A + B);
|
||||
if (i0 <= 0) {
|
||||
i0 = 1;
|
||||
}
|
||||
if (i0 >= rs->p) {
|
||||
i0 = rs->p - 1;
|
||||
}
|
||||
|
||||
threshold = rs->q + i0;
|
||||
symbol = rs->rec_q >= threshold;
|
||||
if (rs->rec_q >= threshold) {
|
||||
rs->q += i0;
|
||||
i0 = rs->p - i0;
|
||||
}
|
||||
|
||||
rs->p = i0;
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int out_byte(struct rubin_state *rs, unsigned char byte)
|
||||
{
|
||||
int i, ret;
|
||||
struct rubin_state rs_copy;
|
||||
rs_copy = *rs;
|
||||
|
||||
for (i=0;i<8;i++) {
|
||||
ret = encode(rs, rs->bit_divider-rs->bits[i],rs->bits[i],byte&1);
|
||||
if (ret) {
|
||||
/* Failed. Restore old state */
|
||||
*rs = rs_copy;
|
||||
return ret;
|
||||
}
|
||||
byte=byte>>1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int in_byte(struct rubin_state *rs)
|
||||
{
|
||||
int i, result = 0, bit_divider = rs->bit_divider;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
result |= decode(rs, bit_divider - rs->bits[i], rs->bits[i]) << i;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
|
||||
unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen)
|
||||
{
|
||||
int outpos = 0;
|
||||
int pos=0;
|
||||
struct rubin_state rs;
|
||||
|
||||
init_pushpull(&rs.pp, cpage_out, *dstlen * 8, 0, 32);
|
||||
|
||||
init_rubin(&rs, bit_divider, bits);
|
||||
|
||||
while (pos < (*sourcelen) && !out_byte(&rs, data_in[pos]))
|
||||
pos++;
|
||||
|
||||
end_rubin(&rs);
|
||||
|
||||
if (outpos > pos) {
|
||||
/* We failed */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Tell the caller how much we managed to compress,
|
||||
* and how much space it took */
|
||||
|
||||
outpos = (pushedbits(&rs.pp)+7)/8;
|
||||
|
||||
if (outpos >= pos)
|
||||
return -1; /* We didn't actually compress */
|
||||
*sourcelen = pos;
|
||||
*dstlen = outpos;
|
||||
return 0;
|
||||
}
|
||||
#if 0
|
||||
/* _compress returns the compressed size, -1 if bigger */
|
||||
int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t *sourcelen, uint32_t *dstlen, void *model)
|
||||
{
|
||||
return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
|
||||
}
|
||||
#endif
|
||||
int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t *sourcelen, uint32_t *dstlen, void *model)
|
||||
{
|
||||
int bits[8];
|
||||
unsigned char histo[256];
|
||||
int i;
|
||||
int ret;
|
||||
uint32_t mysrclen, mydstlen;
|
||||
|
||||
mysrclen = *sourcelen;
|
||||
mydstlen = *dstlen - 8;
|
||||
|
||||
if (*dstlen <= 12)
|
||||
return -1;
|
||||
|
||||
memset(histo, 0, 256);
|
||||
for (i=0; i<mysrclen; i++) {
|
||||
histo[data_in[i]]++;
|
||||
}
|
||||
memset(bits, 0, sizeof(int)*8);
|
||||
for (i=0; i<256; i++) {
|
||||
if (i&128)
|
||||
bits[7] += histo[i];
|
||||
if (i&64)
|
||||
bits[6] += histo[i];
|
||||
if (i&32)
|
||||
bits[5] += histo[i];
|
||||
if (i&16)
|
||||
bits[4] += histo[i];
|
||||
if (i&8)
|
||||
bits[3] += histo[i];
|
||||
if (i&4)
|
||||
bits[2] += histo[i];
|
||||
if (i&2)
|
||||
bits[1] += histo[i];
|
||||
if (i&1)
|
||||
bits[0] += histo[i];
|
||||
}
|
||||
|
||||
for (i=0; i<8; i++) {
|
||||
bits[i] = (bits[i] * 256) / mysrclen;
|
||||
if (!bits[i]) bits[i] = 1;
|
||||
if (bits[i] > 255) bits[i] = 255;
|
||||
cpage_out[i] = bits[i];
|
||||
}
|
||||
|
||||
ret = rubin_do_compress(256, bits, data_in, cpage_out+8, &mysrclen, &mydstlen);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Add back the 8 bytes we took for the probabilities */
|
||||
mydstlen += 8;
|
||||
|
||||
if (mysrclen <= mydstlen) {
|
||||
/* We compressed */
|
||||
return -1;
|
||||
}
|
||||
|
||||
*sourcelen = mysrclen;
|
||||
*dstlen = mydstlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in,
|
||||
unsigned char *page_out, uint32_t srclen, uint32_t destlen)
|
||||
{
|
||||
int outpos = 0;
|
||||
struct rubin_state rs;
|
||||
|
||||
init_pushpull(&rs.pp, cdata_in, srclen, 0, 0);
|
||||
init_decode(&rs, bit_divider, bits);
|
||||
|
||||
while (outpos < destlen) {
|
||||
page_out[outpos++] = in_byte(&rs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t sourcelen, uint32_t dstlen, void *model)
|
||||
{
|
||||
rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t sourcelen, uint32_t dstlen, void *model)
|
||||
{
|
||||
int bits[8];
|
||||
int c;
|
||||
|
||||
for (c=0; c<8; c++)
|
||||
bits[c] = data_in[c];
|
||||
|
||||
rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct jffs2_compressor jffs2_rubinmips_comp = {
|
||||
.priority = JFFS2_RUBINMIPS_PRIORITY,
|
||||
.name = "rubinmips",
|
||||
.compr = JFFS2_COMPR_DYNRUBIN,
|
||||
.compress = NULL, /*&jffs2_rubinmips_compress,*/
|
||||
.decompress = &jffs2_rubinmips_decompress,
|
||||
#ifdef JFFS2_RUBINMIPS_DISABLED
|
||||
.disabled = 1,
|
||||
#else
|
||||
.disabled = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
int jffs2_rubinmips_init(void)
|
||||
{
|
||||
return jffs2_register_compressor(&jffs2_rubinmips_comp);
|
||||
}
|
||||
|
||||
void jffs2_rubinmips_exit(void)
|
||||
{
|
||||
jffs2_unregister_compressor(&jffs2_rubinmips_comp);
|
||||
}
|
||||
|
||||
static struct jffs2_compressor jffs2_dynrubin_comp = {
|
||||
.priority = JFFS2_DYNRUBIN_PRIORITY,
|
||||
.name = "dynrubin",
|
||||
.compr = JFFS2_COMPR_RUBINMIPS,
|
||||
.compress = jffs2_dynrubin_compress,
|
||||
.decompress = &jffs2_dynrubin_decompress,
|
||||
#ifdef JFFS2_DYNRUBIN_DISABLED
|
||||
.disabled = 1,
|
||||
#else
|
||||
.disabled = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
int jffs2_dynrubin_init(void)
|
||||
{
|
||||
return jffs2_register_compressor(&jffs2_dynrubin_comp);
|
||||
}
|
||||
|
||||
void jffs2_dynrubin_exit(void)
|
||||
{
|
||||
jffs2_unregister_compressor(&jffs2_dynrubin_comp);
|
||||
}
|
21
fs/jffs2/compr_rubin.h
Normální soubor
21
fs/jffs2/compr_rubin.h
Normální soubor
@@ -0,0 +1,21 @@
|
||||
/* Rubin encoder/decoder header */
|
||||
/* work started at : aug 3, 1994 */
|
||||
/* last modification : aug 15, 1994 */
|
||||
/* $Id: compr_rubin.h,v 1.6 2002/01/25 01:49:26 dwmw2 Exp $ */
|
||||
|
||||
#include "pushpull.h"
|
||||
|
||||
#define RUBIN_REG_SIZE 16
|
||||
#define UPPER_BIT_RUBIN (((long) 1)<<(RUBIN_REG_SIZE-1))
|
||||
#define LOWER_BITS_RUBIN ((((long) 1)<<(RUBIN_REG_SIZE-1))-1)
|
||||
|
||||
|
||||
struct rubin_state {
|
||||
unsigned long p;
|
||||
unsigned long q;
|
||||
unsigned long rec_q;
|
||||
long bit_number;
|
||||
struct pushpull pp;
|
||||
int bit_divider;
|
||||
int bits[8];
|
||||
};
|
218
fs/jffs2/compr_zlib.c
Normální soubor
218
fs/jffs2/compr_zlib.c
Normální soubor
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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: compr_zlib.c,v 1.29 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(__KERNEL__) && !defined(__ECOS)
|
||||
#error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
|
||||
#endif
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/zlib.h>
|
||||
#include <linux/zutil.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include "nodelist.h"
|
||||
#include "compr.h"
|
||||
|
||||
/* Plan: call deflate() with avail_in == *sourcelen,
|
||||
avail_out = *dstlen - 12 and flush == Z_FINISH.
|
||||
If it doesn't manage to finish, call it again with
|
||||
avail_in == 0 and avail_out set to the remaining 12
|
||||
bytes for it to clean up.
|
||||
Q: Is 12 bytes sufficient?
|
||||
*/
|
||||
#define STREAM_END_SPACE 12
|
||||
|
||||
static DECLARE_MUTEX(deflate_sem);
|
||||
static DECLARE_MUTEX(inflate_sem);
|
||||
static z_stream inf_strm, def_strm;
|
||||
|
||||
#ifdef __KERNEL__ /* Linux-only */
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
static int __init alloc_workspaces(void)
|
||||
{
|
||||
def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
|
||||
if (!def_strm.workspace) {
|
||||
printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize());
|
||||
return -ENOMEM;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize()));
|
||||
inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
|
||||
if (!inf_strm.workspace) {
|
||||
printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize());
|
||||
vfree(def_strm.workspace);
|
||||
return -ENOMEM;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_workspaces(void)
|
||||
{
|
||||
vfree(def_strm.workspace);
|
||||
vfree(inf_strm.workspace);
|
||||
}
|
||||
#else
|
||||
#define alloc_workspaces() (0)
|
||||
#define free_workspaces() do { } while(0)
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t *sourcelen, uint32_t *dstlen, void *model)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (*dstlen <= STREAM_END_SPACE)
|
||||
return -1;
|
||||
|
||||
down(&deflate_sem);
|
||||
|
||||
if (Z_OK != zlib_deflateInit(&def_strm, 3)) {
|
||||
printk(KERN_WARNING "deflateInit failed\n");
|
||||
up(&deflate_sem);
|
||||
return -1;
|
||||
}
|
||||
|
||||
def_strm.next_in = data_in;
|
||||
def_strm.total_in = 0;
|
||||
|
||||
def_strm.next_out = cpage_out;
|
||||
def_strm.total_out = 0;
|
||||
|
||||
while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) {
|
||||
def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE);
|
||||
def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out);
|
||||
D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n",
|
||||
def_strm.avail_in, def_strm.avail_out));
|
||||
ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH);
|
||||
D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n",
|
||||
def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out));
|
||||
if (ret != Z_OK) {
|
||||
D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret));
|
||||
zlib_deflateEnd(&def_strm);
|
||||
up(&deflate_sem);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
def_strm.avail_out += STREAM_END_SPACE;
|
||||
def_strm.avail_in = 0;
|
||||
ret = zlib_deflate(&def_strm, Z_FINISH);
|
||||
zlib_deflateEnd(&def_strm);
|
||||
|
||||
if (ret != Z_STREAM_END) {
|
||||
D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (def_strm.total_out >= def_strm.total_in) {
|
||||
D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n",
|
||||
def_strm.total_in, def_strm.total_out));
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
|
||||
def_strm.total_in, def_strm.total_out));
|
||||
|
||||
*dstlen = def_strm.total_out;
|
||||
*sourcelen = def_strm.total_in;
|
||||
ret = 0;
|
||||
out:
|
||||
up(&deflate_sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t srclen, uint32_t destlen, void *model)
|
||||
{
|
||||
int ret;
|
||||
int wbits = MAX_WBITS;
|
||||
|
||||
down(&inflate_sem);
|
||||
|
||||
inf_strm.next_in = data_in;
|
||||
inf_strm.avail_in = srclen;
|
||||
inf_strm.total_in = 0;
|
||||
|
||||
inf_strm.next_out = cpage_out;
|
||||
inf_strm.avail_out = destlen;
|
||||
inf_strm.total_out = 0;
|
||||
|
||||
/* If it's deflate, and it's got no preset dictionary, then
|
||||
we can tell zlib to skip the adler32 check. */
|
||||
if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
|
||||
((data_in[0] & 0x0f) == Z_DEFLATED) &&
|
||||
!(((data_in[0]<<8) + data_in[1]) % 31)) {
|
||||
|
||||
D2(printk(KERN_DEBUG "inflate skipping adler32\n"));
|
||||
wbits = -((data_in[0] >> 4) + 8);
|
||||
inf_strm.next_in += 2;
|
||||
inf_strm.avail_in -= 2;
|
||||
} else {
|
||||
/* Let this remain D1 for now -- it should never happen */
|
||||
D1(printk(KERN_DEBUG "inflate not skipping adler32\n"));
|
||||
}
|
||||
|
||||
|
||||
if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
|
||||
printk(KERN_WARNING "inflateInit failed\n");
|
||||
up(&inflate_sem);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
|
||||
;
|
||||
if (ret != Z_STREAM_END) {
|
||||
printk(KERN_NOTICE "inflate returned %d\n", ret);
|
||||
}
|
||||
zlib_inflateEnd(&inf_strm);
|
||||
up(&inflate_sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct jffs2_compressor jffs2_zlib_comp = {
|
||||
.priority = JFFS2_ZLIB_PRIORITY,
|
||||
.name = "zlib",
|
||||
.compr = JFFS2_COMPR_ZLIB,
|
||||
.compress = &jffs2_zlib_compress,
|
||||
.decompress = &jffs2_zlib_decompress,
|
||||
#ifdef JFFS2_ZLIB_DISABLED
|
||||
.disabled = 1,
|
||||
#else
|
||||
.disabled = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
int __init jffs2_zlib_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = alloc_workspaces();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = jffs2_register_compressor(&jffs2_zlib_comp);
|
||||
if (ret)
|
||||
free_workspaces();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_zlib_exit(void)
|
||||
{
|
||||
jffs2_unregister_compressor(&jffs2_zlib_comp);
|
||||
free_workspaces();
|
||||
}
|
307
fs/jffs2/comprtest.c
Normální soubor
307
fs/jffs2/comprtest.c
Normální soubor
@@ -0,0 +1,307 @@
|
||||
/* $Id: comprtest.c,v 1.5 2002/01/03 15:20:44 dwmw2 Exp $ */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/types.h>
|
||||
#if 0
|
||||
#define TESTDATA_LEN 512
|
||||
static unsigned char testdata[TESTDATA_LEN] = {
|
||||
0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x83, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00,
|
||||
0xb0, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x06, 0x00, 0x28, 0x00,
|
||||
0x1e, 0x00, 0x1b, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x80, 0x04, 0x08,
|
||||
0x34, 0x80, 0x04, 0x08, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xf4, 0x80, 0x04, 0x08,
|
||||
0xf4, 0x80, 0x04, 0x08, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x08,
|
||||
0x00, 0x80, 0x04, 0x08, 0x0d, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
|
||||
0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x10, 0x95, 0x04, 0x08,
|
||||
0x10, 0x95, 0x04, 0x08, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||
0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x58, 0x95, 0x04, 0x08,
|
||||
0x58, 0x95, 0x04, 0x08, 0xa0, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x08, 0x81, 0x04, 0x08,
|
||||
0x08, 0x81, 0x04, 0x08, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x2d, 0x6c, 0x69, 0x6e, 0x75,
|
||||
0x78, 0x2e, 0x73, 0x6f, 0x2e, 0x32, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
|
||||
0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00,
|
||||
0x0c, 0x83, 0x04, 0x08, 0x81, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
|
||||
0x1c, 0x83, 0x04, 0x08, 0xac, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00,
|
||||
0x2c, 0x83, 0x04, 0x08, 0xdd, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
|
||||
0x3c, 0x83, 0x04, 0x08, 0x2e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
|
||||
0x4c, 0x83, 0x04, 0x08, 0x7d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
|
||||
0x00, 0x85, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x67,
|
||||
0x6d, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x5f, 0x00, 0x6c, 0x69, 0x62, 0x63,
|
||||
0x2e, 0x73, 0x6f, 0x2e, 0x36, 0x00, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x00, 0x5f, 0x5f, 0x63};
|
||||
#else
|
||||
#define TESTDATA_LEN 3481
|
||||
static unsigned char testdata[TESTDATA_LEN] = {
|
||||
0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x22, 0x64, 0x62, 0x65, 0x6e, 0x63, 0x68,
|
||||
0x2e, 0x68, 0x22, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4d, 0x41, 0x58,
|
||||
0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x20, 0x31, 0x30, 0x30, 0x30, 0x0a, 0x0a, 0x73, 0x74, 0x61,
|
||||
0x74, 0x69, 0x63, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x62, 0x75, 0x66, 0x5b, 0x37, 0x30, 0x30,
|
||||
0x30, 0x30, 0x5d, 0x3b, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x20,
|
||||
0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x61,
|
||||
0x74, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x7b, 0x0a, 0x09, 0x69, 0x6e,
|
||||
0x74, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c,
|
||||
0x65, 0x3b, 0x0a, 0x7d, 0x20, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x4d, 0x41, 0x58, 0x5f,
|
||||
0x46, 0x49, 0x4c, 0x45, 0x53, 0x5d, 0x3b, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f,
|
||||
0x5f, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72,
|
||||
0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x75,
|
||||
0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20,
|
||||
0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28,
|
||||
0x25, 0x64, 0x29, 0x20, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61,
|
||||
0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09,
|
||||
0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72,
|
||||
0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a,
|
||||
0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66,
|
||||
0x69, 0x6c, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20,
|
||||
0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x3b, 0x0a,
|
||||
0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a,
|
||||
0x09, 0x09, 0x73, 0x20, 0x3d, 0x20, 0x4d, 0x49, 0x4e, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66,
|
||||
0x28, 0x62, 0x75, 0x66, 0x29, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09,
|
||||
0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73,
|
||||
0x29, 0x3b, 0x0a, 0x09, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x3d, 0x20, 0x73, 0x3b, 0x0a,
|
||||
0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6f, 0x70,
|
||||
0x65, 0x6e, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20,
|
||||
0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20,
|
||||
0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c,
|
||||
0x20, 0x69, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x3d,
|
||||
0x20, 0x4f, 0x5f, 0x52, 0x44, 0x57, 0x52, 0x7c, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x3b,
|
||||
0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74,
|
||||
0x3b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28,
|
||||
0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69,
|
||||
0x7a, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x7c,
|
||||
0x3d, 0x20, 0x4f, 0x5f, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x64, 0x20,
|
||||
0x3d, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x66, 0x6c,
|
||||
0x61, 0x67, 0x73, 0x2c, 0x20, 0x30, 0x36, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20,
|
||||
0x28, 0x66, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x2d, 0x31, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70,
|
||||
0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x6f, 0x70, 0x65, 0x6e,
|
||||
0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x68,
|
||||
0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22,
|
||||
0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65,
|
||||
0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x68,
|
||||
0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28,
|
||||
0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72,
|
||||
0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x64, 0x2c,
|
||||
0x20, 0x26, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65,
|
||||
0x20, 0x3e, 0x20, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b,
|
||||
0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69,
|
||||
0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64,
|
||||
0x69, 0x6e, 0x67, 0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f,
|
||||
0x6d, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74,
|
||||
0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x23, 0x65,
|
||||
0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66, 0x69,
|
||||
0x6c, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x74,
|
||||
0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x20, 0x65, 0x6c,
|
||||
0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3c, 0x20, 0x73, 0x74,
|
||||
0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72,
|
||||
0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67,
|
||||
0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x25,
|
||||
0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e,
|
||||
0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09,
|
||||
0x09, 0x66, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73,
|
||||
0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69,
|
||||
0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69,
|
||||
0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20,
|
||||
0x30, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66,
|
||||
0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53,
|
||||
0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x66, 0x69,
|
||||
0x6c, 0x65, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x66, 0x6f,
|
||||
0x72, 0x20, 0x25, 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b,
|
||||
0x0a, 0x09, 0x09, 0x65, 0x78, 0x69, 0x74, 0x28, 0x31, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09,
|
||||
0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65,
|
||||
0x20, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09,
|
||||
0x69, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2b, 0x2b, 0x20, 0x25, 0x20, 0x31, 0x30,
|
||||
0x30, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e,
|
||||
0x74, 0x66, 0x28, 0x22, 0x2e, 0x22, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76,
|
||||
0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x69, 0x6e, 0x74,
|
||||
0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a,
|
||||
0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x0a, 0x7b,
|
||||
0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x62,
|
||||
0x75, 0x66, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x6d, 0x65, 0x6d, 0x73,
|
||||
0x65, 0x74, 0x28, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f,
|
||||
0x66, 0x28, 0x62, 0x75, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28,
|
||||
0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b,
|
||||
0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d,
|
||||
0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a,
|
||||
0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58,
|
||||
0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x31, 0x0a,
|
||||
0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64,
|
||||
0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20,
|
||||
0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20,
|
||||
0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e,
|
||||
0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e,
|
||||
0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c,
|
||||
0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a,
|
||||
0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b,
|
||||
0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c,
|
||||
0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20,
|
||||
0x28, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d,
|
||||
0x2e, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20,
|
||||
0x21, 0x3d, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69,
|
||||
0x6e, 0x74, 0x66, 0x28, 0x22, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65,
|
||||
0x64, 0x20, 0x6f, 0x6e, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x5c, 0x6e,
|
||||
0x22, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d,
|
||||
0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x69,
|
||||
0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73,
|
||||
0x69, 0x7a, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29,
|
||||
0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20,
|
||||
0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53,
|
||||
0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d,
|
||||
0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b,
|
||||
0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41,
|
||||
0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69,
|
||||
0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61,
|
||||
0x64, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73,
|
||||
0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25,
|
||||
0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c,
|
||||
0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75,
|
||||
0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73,
|
||||
0x65, 0x74, 0x2c, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09,
|
||||
0x72, 0x65, 0x61, 0x64, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66,
|
||||
0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x7d,
|
||||
0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28,
|
||||
0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69,
|
||||
0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b,
|
||||
0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29,
|
||||
0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b,
|
||||
0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x68, 0x61, 0x6e,
|
||||
0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09,
|
||||
0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c,
|
||||
0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22,
|
||||
0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x3a, 0x20, 0x68,
|
||||
0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74,
|
||||
0x20, 0x6f, 0x70, 0x65, 0x6e, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20,
|
||||
0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72,
|
||||
0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x66, 0x74, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x29, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x20,
|
||||
0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6d, 0x6b,
|
||||
0x64, 0x69, 0x72, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29,
|
||||
0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x6d, 0x6b, 0x64, 0x69, 0x72,
|
||||
0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x30, 0x37, 0x30, 0x30, 0x29, 0x20, 0x21, 0x3d,
|
||||
0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a,
|
||||
0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x20,
|
||||
0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e,
|
||||
0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72,
|
||||
0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x7d, 0x0a,
|
||||
0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x6d, 0x64, 0x69, 0x72,
|
||||
0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a,
|
||||
0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29,
|
||||
0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x28, 0x66, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70,
|
||||
0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x20, 0x25, 0x73, 0x20,
|
||||
0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20,
|
||||
0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c,
|
||||
0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29,
|
||||
0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f,
|
||||
0x5f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6f, 0x6c,
|
||||
0x64, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6e, 0x65, 0x77, 0x29, 0x0a, 0x7b, 0x0a,
|
||||
0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6f, 0x6c, 0x64, 0x29, 0x3b, 0x0a,
|
||||
0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6e, 0x65, 0x77, 0x29, 0x3b, 0x0a,
|
||||
0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x6f, 0x6c, 0x64,
|
||||
0x2c, 0x20, 0x6e, 0x65, 0x77, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09,
|
||||
0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20,
|
||||
0x25, 0x73, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73,
|
||||
0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x6f, 0x6c, 0x64, 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72,
|
||||
0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d,
|
||||
0x0a, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x28,
|
||||
0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74,
|
||||
0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
|
||||
0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75,
|
||||
0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69,
|
||||
0x66, 0x20, 0x28, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x26,
|
||||
0x73, 0x74, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72,
|
||||
0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74,
|
||||
0x61, 0x74, 0x3a, 0x20, 0x25, 0x73, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x25,
|
||||
0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f,
|
||||
0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74,
|
||||
0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x53, 0x5f, 0x49,
|
||||
0x53, 0x44, 0x49, 0x52, 0x28, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x29,
|
||||
0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28,
|
||||
0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x21, 0x3d, 0x20, 0x73, 0x69,
|
||||
0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22,
|
||||
0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x3a, 0x20, 0x25, 0x73,
|
||||
0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x25, 0x64, 0x20, 0x25,
|
||||
0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69,
|
||||
0x7a, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a,
|
||||
0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28,
|
||||
0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74,
|
||||
0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x6f, 0x70, 0x65,
|
||||
0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x35, 0x30, 0x30, 0x30, 0x2c, 0x20, 0x73,
|
||||
0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28,
|
||||
0x35, 0x30, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x7d, 0x0a
|
||||
};
|
||||
#endif
|
||||
static unsigned char comprbuf[TESTDATA_LEN];
|
||||
static unsigned char decomprbuf[TESTDATA_LEN];
|
||||
|
||||
int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
|
||||
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
|
||||
unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t *datalen, uint32_t *cdatalen);
|
||||
|
||||
int init_module(void ) {
|
||||
unsigned char comprtype;
|
||||
uint32_t c, d;
|
||||
int ret;
|
||||
|
||||
printk("Original data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
testdata[0],testdata[1],testdata[2],testdata[3],
|
||||
testdata[4],testdata[5],testdata[6],testdata[7],
|
||||
testdata[8],testdata[9],testdata[10],testdata[11],
|
||||
testdata[12],testdata[13],testdata[14],testdata[15]);
|
||||
d = TESTDATA_LEN;
|
||||
c = TESTDATA_LEN;
|
||||
comprtype = jffs2_compress(testdata, comprbuf, &d, &c);
|
||||
|
||||
printk("jffs2_compress used compression type %d. Compressed size %d, uncompressed size %d\n",
|
||||
comprtype, c, d);
|
||||
printk("Compressed data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
comprbuf[0],comprbuf[1],comprbuf[2],comprbuf[3],
|
||||
comprbuf[4],comprbuf[5],comprbuf[6],comprbuf[7],
|
||||
comprbuf[8],comprbuf[9],comprbuf[10],comprbuf[11],
|
||||
comprbuf[12],comprbuf[13],comprbuf[14],comprbuf[15]);
|
||||
|
||||
ret = jffs2_decompress(comprtype, comprbuf, decomprbuf, c, d);
|
||||
printk("jffs2_decompress returned %d\n", ret);
|
||||
printk("Decompressed data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
decomprbuf[0],decomprbuf[1],decomprbuf[2],decomprbuf[3],
|
||||
decomprbuf[4],decomprbuf[5],decomprbuf[6],decomprbuf[7],
|
||||
decomprbuf[8],decomprbuf[9],decomprbuf[10],decomprbuf[11],
|
||||
decomprbuf[12],decomprbuf[13],decomprbuf[14],decomprbuf[15]);
|
||||
if (memcmp(decomprbuf, testdata, d))
|
||||
printk("Compression and decompression corrupted data\n");
|
||||
else
|
||||
printk("Compression good for %d bytes\n", d);
|
||||
return 1;
|
||||
}
|
799
fs/jffs2/dir.c
Normální soubor
799
fs/jffs2/dir.c
Normální soubor
@@ -0,0 +1,799 @@
|
||||
/*
|
||||
* 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: dir.c,v 1.84 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/jffs2_fs_i.h>
|
||||
#include <linux/jffs2_fs_sb.h>
|
||||
#include <linux/time.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
/* Urgh. Please tell me there's a nicer way of doing these. */
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
|
||||
typedef int mknod_arg_t;
|
||||
#define NAMEI_COMPAT(x) ((void *)x)
|
||||
#else
|
||||
typedef dev_t mknod_arg_t;
|
||||
#define NAMEI_COMPAT(x) (x)
|
||||
#endif
|
||||
|
||||
static int jffs2_readdir (struct file *, void *, filldir_t);
|
||||
|
||||
static int jffs2_create (struct inode *,struct dentry *,int,
|
||||
struct nameidata *);
|
||||
static struct dentry *jffs2_lookup (struct inode *,struct dentry *,
|
||||
struct nameidata *);
|
||||
static int jffs2_link (struct dentry *,struct inode *,struct dentry *);
|
||||
static int jffs2_unlink (struct inode *,struct dentry *);
|
||||
static int jffs2_symlink (struct inode *,struct dentry *,const char *);
|
||||
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,mknod_arg_t);
|
||||
static int jffs2_rename (struct inode *, struct dentry *,
|
||||
struct inode *, struct dentry *);
|
||||
|
||||
struct file_operations jffs2_dir_operations =
|
||||
{
|
||||
.read = generic_read_dir,
|
||||
.readdir = jffs2_readdir,
|
||||
.ioctl = jffs2_ioctl,
|
||||
.fsync = jffs2_fsync
|
||||
};
|
||||
|
||||
|
||||
struct inode_operations jffs2_dir_inode_operations =
|
||||
{
|
||||
.create = NAMEI_COMPAT(jffs2_create),
|
||||
.lookup = NAMEI_COMPAT(jffs2_lookup),
|
||||
.link = jffs2_link,
|
||||
.unlink = jffs2_unlink,
|
||||
.symlink = jffs2_symlink,
|
||||
.mkdir = jffs2_mkdir,
|
||||
.rmdir = jffs2_rmdir,
|
||||
.mknod = jffs2_mknod,
|
||||
.rename = jffs2_rename,
|
||||
.setattr = jffs2_setattr,
|
||||
};
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
|
||||
/* We keep the dirent list sorted in increasing order of name hash,
|
||||
and we use the same hash function as the dentries. Makes this
|
||||
nice and simple
|
||||
*/
|
||||
static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
|
||||
struct nameidata *nd)
|
||||
{
|
||||
struct jffs2_inode_info *dir_f;
|
||||
struct jffs2_sb_info *c;
|
||||
struct jffs2_full_dirent *fd = NULL, *fd_list;
|
||||
uint32_t ino = 0;
|
||||
struct inode *inode = NULL;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
|
||||
|
||||
dir_f = JFFS2_INODE_INFO(dir_i);
|
||||
c = JFFS2_SB_INFO(dir_i->i_sb);
|
||||
|
||||
down(&dir_f->sem);
|
||||
|
||||
/* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */
|
||||
for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= target->d_name.hash; fd_list = fd_list->next) {
|
||||
if (fd_list->nhash == target->d_name.hash &&
|
||||
(!fd || fd_list->version > fd->version) &&
|
||||
strlen(fd_list->name) == target->d_name.len &&
|
||||
!strncmp(fd_list->name, target->d_name.name, target->d_name.len)) {
|
||||
fd = fd_list;
|
||||
}
|
||||
}
|
||||
if (fd)
|
||||
ino = fd->ino;
|
||||
up(&dir_f->sem);
|
||||
if (ino) {
|
||||
inode = iget(dir_i->i_sb, ino);
|
||||
if (!inode) {
|
||||
printk(KERN_WARNING "iget() failed for ino #%u\n", ino);
|
||||
return (ERR_PTR(-EIO));
|
||||
}
|
||||
}
|
||||
|
||||
d_add(target, inode);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
|
||||
static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||
{
|
||||
struct jffs2_inode_info *f;
|
||||
struct jffs2_sb_info *c;
|
||||
struct inode *inode = filp->f_dentry->d_inode;
|
||||
struct jffs2_full_dirent *fd;
|
||||
unsigned long offset, curofs;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", filp->f_dentry->d_inode->i_ino));
|
||||
|
||||
f = JFFS2_INODE_INFO(inode);
|
||||
c = JFFS2_SB_INFO(inode->i_sb);
|
||||
|
||||
offset = filp->f_pos;
|
||||
|
||||
if (offset == 0) {
|
||||
D1(printk(KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino));
|
||||
if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0)
|
||||
goto out;
|
||||
offset++;
|
||||
}
|
||||
if (offset == 1) {
|
||||
unsigned long pino = parent_ino(filp->f_dentry);
|
||||
D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", pino));
|
||||
if (filldir(dirent, "..", 2, 1, pino, DT_DIR) < 0)
|
||||
goto out;
|
||||
offset++;
|
||||
}
|
||||
|
||||
curofs=1;
|
||||
down(&f->sem);
|
||||
for (fd = f->dents; fd; fd = fd->next) {
|
||||
|
||||
curofs++;
|
||||
/* First loop: curofs = 2; offset = 2 */
|
||||
if (curofs < offset) {
|
||||
D2(printk(KERN_DEBUG "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n",
|
||||
fd->name, fd->ino, fd->type, curofs, offset));
|
||||
continue;
|
||||
}
|
||||
if (!fd->ino) {
|
||||
D2(printk(KERN_DEBUG "Skipping deletion dirent \"%s\"\n", fd->name));
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
D2(printk(KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset, fd->name, fd->ino, fd->type));
|
||||
if (filldir(dirent, fd->name, strlen(fd->name), offset, fd->ino, fd->type) < 0)
|
||||
break;
|
||||
offset++;
|
||||
}
|
||||
up(&f->sem);
|
||||
out:
|
||||
filp->f_pos = offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
|
||||
static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
|
||||
struct nameidata *nd)
|
||||
{
|
||||
struct jffs2_raw_inode *ri;
|
||||
struct jffs2_inode_info *f, *dir_f;
|
||||
struct jffs2_sb_info *c;
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
ri = jffs2_alloc_raw_inode();
|
||||
if (!ri)
|
||||
return -ENOMEM;
|
||||
|
||||
c = JFFS2_SB_INFO(dir_i->i_sb);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_create()\n"));
|
||||
|
||||
inode = jffs2_new_inode(dir_i, mode, ri);
|
||||
|
||||
if (IS_ERR(inode)) {
|
||||
D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n"));
|
||||
jffs2_free_raw_inode(ri);
|
||||
return PTR_ERR(inode);
|
||||
}
|
||||
|
||||
inode->i_op = &jffs2_file_inode_operations;
|
||||
inode->i_fop = &jffs2_file_operations;
|
||||
inode->i_mapping->a_ops = &jffs2_file_address_operations;
|
||||
inode->i_mapping->nrpages = 0;
|
||||
|
||||
f = JFFS2_INODE_INFO(inode);
|
||||
dir_f = JFFS2_INODE_INFO(dir_i);
|
||||
|
||||
ret = jffs2_do_create(c, dir_f, f, ri,
|
||||
dentry->d_name.name, dentry->d_name.len);
|
||||
|
||||
if (ret) {
|
||||
make_bad_inode(inode);
|
||||
iput(inode);
|
||||
jffs2_free_raw_inode(ri);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
|
||||
|
||||
jffs2_free_raw_inode(ri);
|
||||
d_instantiate(dentry, inode);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
|
||||
inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
|
||||
static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry)
|
||||
{
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb);
|
||||
struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
|
||||
struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(dentry->d_inode);
|
||||
int ret;
|
||||
|
||||
ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name,
|
||||
dentry->d_name.len, dead_f);
|
||||
if (dead_f->inocache)
|
||||
dentry->d_inode->i_nlink = dead_f->inocache->nlink;
|
||||
return ret;
|
||||
}
|
||||
/***********************************************************************/
|
||||
|
||||
|
||||
static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry)
|
||||
{
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dentry->d_inode->i_sb);
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
|
||||
struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
|
||||
int ret;
|
||||
uint8_t type;
|
||||
|
||||
/* Don't let people make hard links to bad inodes. */
|
||||
if (!f->inocache)
|
||||
return -EIO;
|
||||
|
||||
if (S_ISDIR(old_dentry->d_inode->i_mode))
|
||||
return -EPERM;
|
||||
|
||||
/* XXX: This is ugly */
|
||||
type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
|
||||
if (!type) type = DT_REG;
|
||||
|
||||
ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len);
|
||||
|
||||
if (!ret) {
|
||||
down(&f->sem);
|
||||
old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
|
||||
up(&f->sem);
|
||||
d_instantiate(dentry, old_dentry->d_inode);
|
||||
atomic_inc(&old_dentry->d_inode->i_count);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char *target)
|
||||
{
|
||||
struct jffs2_inode_info *f, *dir_f;
|
||||
struct jffs2_sb_info *c;
|
||||
struct inode *inode;
|
||||
struct jffs2_raw_inode *ri;
|
||||
struct jffs2_raw_dirent *rd;
|
||||
struct jffs2_full_dnode *fn;
|
||||
struct jffs2_full_dirent *fd;
|
||||
int namelen;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
int ret;
|
||||
|
||||
/* FIXME: If you care. We'd need to use frags for the target
|
||||
if it grows much more than this */
|
||||
if (strlen(target) > 254)
|
||||
return -EINVAL;
|
||||
|
||||
ri = jffs2_alloc_raw_inode();
|
||||
|
||||
if (!ri)
|
||||
return -ENOMEM;
|
||||
|
||||
c = JFFS2_SB_INFO(dir_i->i_sb);
|
||||
|
||||
/* Try to reserve enough space for both node and dirent.
|
||||
* Just the node will do for now, though
|
||||
*/
|
||||
namelen = dentry->d_name.len;
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
|
||||
if (ret) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri);
|
||||
|
||||
if (IS_ERR(inode)) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
jffs2_complete_reservation(c);
|
||||
return PTR_ERR(inode);
|
||||
}
|
||||
|
||||
inode->i_op = &jffs2_symlink_inode_operations;
|
||||
|
||||
f = JFFS2_INODE_INFO(inode);
|
||||
|
||||
inode->i_size = strlen(target);
|
||||
ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size);
|
||||
ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size);
|
||||
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
ri->compr = JFFS2_COMPR_NONE;
|
||||
ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target)));
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
|
||||
fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL);
|
||||
|
||||
jffs2_free_raw_inode(ri);
|
||||
|
||||
if (IS_ERR(fn)) {
|
||||
/* Eeek. Wave bye bye */
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_clear_inode(inode);
|
||||
return PTR_ERR(fn);
|
||||
}
|
||||
/* No data here. Only a metadata node, which will be
|
||||
obsoleted by the first data write
|
||||
*/
|
||||
f->metadata = fn;
|
||||
up(&f->sem);
|
||||
|
||||
jffs2_complete_reservation(c);
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
if (ret) {
|
||||
/* Eep. */
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rd = jffs2_alloc_raw_dirent();
|
||||
if (!rd) {
|
||||
/* Argh. Now we treat it like a normal delete */
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_clear_inode(inode);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dir_f = JFFS2_INODE_INFO(dir_i);
|
||||
down(&dir_f->sem);
|
||||
|
||||
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
|
||||
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
|
||||
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
rd->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->nsize = namelen;
|
||||
rd->type = DT_LNK;
|
||||
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
|
||||
rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
|
||||
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
|
||||
|
||||
if (IS_ERR(fd)) {
|
||||
/* dirent failed to write. Delete the inode normally
|
||||
as if it were the final unlink() */
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_free_raw_dirent(rd);
|
||||
up(&dir_f->sem);
|
||||
jffs2_clear_inode(inode);
|
||||
return PTR_ERR(fd);
|
||||
}
|
||||
|
||||
dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
|
||||
|
||||
jffs2_free_raw_dirent(rd);
|
||||
|
||||
/* Link the fd into the inode's list, obsoleting an old
|
||||
one if necessary. */
|
||||
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
|
||||
|
||||
up(&dir_f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int jffs2_mkdir (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 jffs2_raw_inode *ri;
|
||||
struct jffs2_raw_dirent *rd;
|
||||
struct jffs2_full_dnode *fn;
|
||||
struct jffs2_full_dirent *fd;
|
||||
int namelen;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
int ret;
|
||||
|
||||
mode |= S_IFDIR;
|
||||
|
||||
ri = jffs2_alloc_raw_inode();
|
||||
if (!ri)
|
||||
return -ENOMEM;
|
||||
|
||||
c = JFFS2_SB_INFO(dir_i->i_sb);
|
||||
|
||||
/* Try to reserve enough space for both node and dirent.
|
||||
* Just the node will do for now, though
|
||||
*/
|
||||
namelen = dentry->d_name.len;
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
|
||||
if (ret) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inode = jffs2_new_inode(dir_i, mode, ri);
|
||||
|
||||
if (IS_ERR(inode)) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
jffs2_complete_reservation(c);
|
||||
return PTR_ERR(inode);
|
||||
}
|
||||
|
||||
inode->i_op = &jffs2_dir_inode_operations;
|
||||
inode->i_fop = &jffs2_dir_operations;
|
||||
/* Directories get nlink 2 at start */
|
||||
inode->i_nlink = 2;
|
||||
|
||||
f = JFFS2_INODE_INFO(inode);
|
||||
|
||||
ri->data_crc = cpu_to_je32(0);
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
|
||||
fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
|
||||
|
||||
jffs2_free_raw_inode(ri);
|
||||
|
||||
if (IS_ERR(fn)) {
|
||||
/* Eeek. Wave bye bye */
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_clear_inode(inode);
|
||||
return PTR_ERR(fn);
|
||||
}
|
||||
/* No data here. Only a metadata node, which will be
|
||||
obsoleted by the first data write
|
||||
*/
|
||||
f->metadata = fn;
|
||||
up(&f->sem);
|
||||
|
||||
jffs2_complete_reservation(c);
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
if (ret) {
|
||||
/* Eep. */
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rd = jffs2_alloc_raw_dirent();
|
||||
if (!rd) {
|
||||
/* Argh. Now we treat it like a normal delete */
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_clear_inode(inode);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dir_f = JFFS2_INODE_INFO(dir_i);
|
||||
down(&dir_f->sem);
|
||||
|
||||
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
|
||||
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
|
||||
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
rd->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->nsize = namelen;
|
||||
rd->type = DT_DIR;
|
||||
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
|
||||
rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
|
||||
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
|
||||
|
||||
if (IS_ERR(fd)) {
|
||||
/* dirent failed to write. Delete the inode normally
|
||||
as if it were the final unlink() */
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_free_raw_dirent(rd);
|
||||
up(&dir_f->sem);
|
||||
jffs2_clear_inode(inode);
|
||||
return PTR_ERR(fd);
|
||||
}
|
||||
|
||||
dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
|
||||
dir_i->i_nlink++;
|
||||
|
||||
jffs2_free_raw_dirent(rd);
|
||||
|
||||
/* Link the fd into the inode's list, obsoleting an old
|
||||
one if necessary. */
|
||||
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
|
||||
|
||||
up(&dir_f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
|
||||
struct jffs2_full_dirent *fd;
|
||||
int ret;
|
||||
|
||||
for (fd = f->dents ; fd; fd = fd->next) {
|
||||
if (fd->ino)
|
||||
return -ENOTEMPTY;
|
||||
}
|
||||
ret = jffs2_unlink(dir_i, dentry);
|
||||
if (!ret)
|
||||
dir_i->i_nlink--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev)
|
||||
{
|
||||
struct jffs2_inode_info *f, *dir_f;
|
||||
struct jffs2_sb_info *c;
|
||||
struct inode *inode;
|
||||
struct jffs2_raw_inode *ri;
|
||||
struct jffs2_raw_dirent *rd;
|
||||
struct jffs2_full_dnode *fn;
|
||||
struct jffs2_full_dirent *fd;
|
||||
int namelen;
|
||||
jint16_t dev;
|
||||
int devlen = 0;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
int ret;
|
||||
|
||||
if (!old_valid_dev(rdev))
|
||||
return -EINVAL;
|
||||
|
||||
ri = jffs2_alloc_raw_inode();
|
||||
if (!ri)
|
||||
return -ENOMEM;
|
||||
|
||||
c = JFFS2_SB_INFO(dir_i->i_sb);
|
||||
|
||||
if (S_ISBLK(mode) || S_ISCHR(mode)) {
|
||||
dev = cpu_to_je16(old_encode_dev(rdev));
|
||||
devlen = sizeof(dev);
|
||||
}
|
||||
|
||||
/* Try to reserve enough space for both node and dirent.
|
||||
* Just the node will do for now, though
|
||||
*/
|
||||
namelen = dentry->d_name.len;
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
|
||||
if (ret) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inode = jffs2_new_inode(dir_i, mode, ri);
|
||||
|
||||
if (IS_ERR(inode)) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
jffs2_complete_reservation(c);
|
||||
return PTR_ERR(inode);
|
||||
}
|
||||
inode->i_op = &jffs2_file_inode_operations;
|
||||
init_special_inode(inode, inode->i_mode, rdev);
|
||||
|
||||
f = JFFS2_INODE_INFO(inode);
|
||||
|
||||
ri->dsize = ri->csize = cpu_to_je32(devlen);
|
||||
ri->totlen = cpu_to_je32(sizeof(*ri) + devlen);
|
||||
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
ri->compr = JFFS2_COMPR_NONE;
|
||||
ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen));
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
|
||||
fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL);
|
||||
|
||||
jffs2_free_raw_inode(ri);
|
||||
|
||||
if (IS_ERR(fn)) {
|
||||
/* Eeek. Wave bye bye */
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_clear_inode(inode);
|
||||
return PTR_ERR(fn);
|
||||
}
|
||||
/* No data here. Only a metadata node, which will be
|
||||
obsoleted by the first data write
|
||||
*/
|
||||
f->metadata = fn;
|
||||
up(&f->sem);
|
||||
|
||||
jffs2_complete_reservation(c);
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
if (ret) {
|
||||
/* Eep. */
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rd = jffs2_alloc_raw_dirent();
|
||||
if (!rd) {
|
||||
/* Argh. Now we treat it like a normal delete */
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_clear_inode(inode);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dir_f = JFFS2_INODE_INFO(dir_i);
|
||||
down(&dir_f->sem);
|
||||
|
||||
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
|
||||
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
|
||||
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
rd->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->nsize = namelen;
|
||||
|
||||
/* XXX: This is ugly. */
|
||||
rd->type = (mode & S_IFMT) >> 12;
|
||||
|
||||
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
|
||||
rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
|
||||
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
|
||||
|
||||
if (IS_ERR(fd)) {
|
||||
/* dirent failed to write. Delete the inode normally
|
||||
as if it were the final unlink() */
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_free_raw_dirent(rd);
|
||||
up(&dir_f->sem);
|
||||
jffs2_clear_inode(inode);
|
||||
return PTR_ERR(fd);
|
||||
}
|
||||
|
||||
dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
|
||||
|
||||
jffs2_free_raw_dirent(rd);
|
||||
|
||||
/* Link the fd into the inode's list, obsoleting an old
|
||||
one if necessary. */
|
||||
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
|
||||
|
||||
up(&dir_f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_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);
|
||||
struct jffs2_inode_info *victim_f = NULL;
|
||||
uint8_t type;
|
||||
|
||||
/* The VFS will check for us and prevent trying to rename a
|
||||
* file over a directory and vice versa, but if it's a directory,
|
||||
* the VFS can't check whether the victim is empty. The filesystem
|
||||
* needs to do that for itself.
|
||||
*/
|
||||
if (new_dentry->d_inode) {
|
||||
victim_f = JFFS2_INODE_INFO(new_dentry->d_inode);
|
||||
if (S_ISDIR(new_dentry->d_inode->i_mode)) {
|
||||
struct jffs2_full_dirent *fd;
|
||||
|
||||
down(&victim_f->sem);
|
||||
for (fd = victim_f->dents; fd; fd = fd->next) {
|
||||
if (fd->ino) {
|
||||
up(&victim_f->sem);
|
||||
return -ENOTEMPTY;
|
||||
}
|
||||
}
|
||||
up(&victim_f->sem);
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: We probably ought to alloc enough space for
|
||||
both nodes at the same time. Writing the new link,
|
||||
then getting -ENOSPC, is quite bad :)
|
||||
*/
|
||||
|
||||
/* Make a hard link */
|
||||
|
||||
/* XXX: This is ugly */
|
||||
type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
|
||||
if (!type) type = DT_REG;
|
||||
|
||||
ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i),
|
||||
old_dentry->d_inode->i_ino, type,
|
||||
new_dentry->d_name.name, new_dentry->d_name.len);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (victim_f) {
|
||||
/* There was a victim. Kill it off nicely */
|
||||
new_dentry->d_inode->i_nlink--;
|
||||
/* Don't oops if the victim was a dirent pointing to an
|
||||
inode which didn't exist. */
|
||||
if (victim_f->inocache) {
|
||||
down(&victim_f->sem);
|
||||
victim_f->inocache->nlink--;
|
||||
up(&victim_f->sem);
|
||||
}
|
||||
}
|
||||
|
||||
/* If it was a directory we moved, and there was no victim,
|
||||
increase i_nlink on its new parent */
|
||||
if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f)
|
||||
new_dir_i->i_nlink++;
|
||||
|
||||
/* Unlink the original */
|
||||
ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
|
||||
old_dentry->d_name.name, old_dentry->d_name.len, NULL);
|
||||
|
||||
/* We don't touch inode->i_nlink */
|
||||
|
||||
if (ret) {
|
||||
/* Oh shit. We really ought to make a single node which can do both atomically */
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
|
||||
down(&f->sem);
|
||||
old_dentry->d_inode->i_nlink++;
|
||||
if (f->inocache)
|
||||
f->inocache->nlink++;
|
||||
up(&f->sem);
|
||||
|
||||
printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
|
||||
/* Might as well let the VFS know */
|
||||
d_instantiate(new_dentry, old_dentry->d_inode);
|
||||
atomic_inc(&old_dentry->d_inode->i_count);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (S_ISDIR(old_dentry->d_inode->i_mode))
|
||||
old_dir_i->i_nlink--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
442
fs/jffs2/erase.c
Normální soubor
442
fs/jffs2/erase.c
Normální soubor
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* 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: erase.c,v 1.66 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/sched.h>
|
||||
#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 *);
|
||||
#endif
|
||||
static 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_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
|
||||
static void jffs2_erase_block(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
int ret;
|
||||
uint32_t bad_offset;
|
||||
#ifdef __ECOS
|
||||
ret = jffs2_flash_erase(c, jeb);
|
||||
if (!ret) {
|
||||
jffs2_erase_succeeded(c, jeb);
|
||||
return;
|
||||
}
|
||||
bad_offset = jeb->offset;
|
||||
#else /* Linux */
|
||||
struct erase_info *instr;
|
||||
|
||||
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");
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
list_del(&jeb->list);
|
||||
list_add(&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);
|
||||
return;
|
||||
}
|
||||
|
||||
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]);
|
||||
instr->fail_addr = 0xffffffff;
|
||||
|
||||
((struct erase_priv_struct *)instr->priv)->jeb = jeb;
|
||||
((struct erase_priv_struct *)instr->priv)->c = c;
|
||||
|
||||
ret = c->mtd->erase(c->mtd, instr);
|
||||
if (!ret)
|
||||
return;
|
||||
|
||||
bad_offset = instr->fail_addr;
|
||||
kfree(instr);
|
||||
#endif /* __ECOS */
|
||||
|
||||
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));
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
list_del(&jeb->list);
|
||||
list_add(&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);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret == -EROFS)
|
||||
printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset);
|
||||
else
|
||||
printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
|
||||
|
||||
jffs2_erase_failed(c, jeb, bad_offset);
|
||||
}
|
||||
|
||||
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
|
||||
{
|
||||
struct jffs2_eraseblock *jeb;
|
||||
|
||||
down(&c->erase_free_sem);
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
while (!list_empty(&c->erase_complete_list) ||
|
||||
!list_empty(&c->erase_pending_list)) {
|
||||
|
||||
if (!list_empty(&c->erase_complete_list)) {
|
||||
jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
|
||||
list_del(&jeb->list);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
jffs2_mark_erased_block(c, jeb);
|
||||
|
||||
if (!--count) {
|
||||
D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
|
||||
goto done;
|
||||
}
|
||||
|
||||
} else if (!list_empty(&c->erase_pending_list)) {
|
||||
jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
|
||||
D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
|
||||
list_del(&jeb->list);
|
||||
c->erasing_size += c->sector_size;
|
||||
c->wasted_size -= jeb->wasted_size;
|
||||
c->free_size -= jeb->free_size;
|
||||
c->used_size -= jeb->used_size;
|
||||
c->dirty_size -= jeb->dirty_size;
|
||||
jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
|
||||
jffs2_free_all_node_refs(c, jeb);
|
||||
list_add(&jeb->list, &c->erasing_list);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
jffs2_erase_block(c, jeb);
|
||||
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Be nice */
|
||||
cond_resched();
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
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));
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
list_del(&jeb->list);
|
||||
list_add_tail(&jeb->list, &c->erase_complete_list);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
/* Ensure that kupdated calls us again to mark them clean */
|
||||
jffs2_erase_pending_trigger(c);
|
||||
}
|
||||
|
||||
static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
|
||||
{
|
||||
/* For NAND, if the failure did not occur at the device level for a
|
||||
specific physical page, don't bother updating the bad block table. */
|
||||
if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) {
|
||||
/* We had a device-level failure to erase. Let's see if we've
|
||||
failed too many times. */
|
||||
if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
|
||||
/* We'd like to give this block another try. */
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
list_del(&jeb->list);
|
||||
list_add(&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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
c->erasing_size -= c->sector_size;
|
||||
c->bad_size += c->sector_size;
|
||||
list_del(&jeb->list);
|
||||
list_add(&jeb->list, &c->bad_list);
|
||||
c->nr_erasing_blocks--;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
wake_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) {
|
||||
printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", 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 make
|
||||
this a standard doubly-linked list? */
|
||||
static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
|
||||
struct jffs2_raw_node_ref *ref, struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
struct jffs2_inode_cache *ic = NULL;
|
||||
struct jffs2_raw_node_ref **prev;
|
||||
|
||||
prev = &ref->next_in_ino;
|
||||
|
||||
/* Walk the inode's list once, removing any nodes from this eraseblock */
|
||||
while (1) {
|
||||
if (!(*prev)->next_in_ino) {
|
||||
/* We're looking at the jffs2_inode_cache, which is
|
||||
at the end of the linked list. Stash it and continue
|
||||
from the beginning of the list */
|
||||
ic = (struct jffs2_inode_cache *)(*prev);
|
||||
prev = &ic->nodes;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
|
||||
/* It's in the block we're erasing */
|
||||
struct jffs2_raw_node_ref *this;
|
||||
|
||||
this = *prev;
|
||||
*prev = this->next_in_ino;
|
||||
this->next_in_ino = NULL;
|
||||
|
||||
if (this == ref)
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
/* Not to be deleted. Skip */
|
||||
prev = &((*prev)->next_in_ino);
|
||||
}
|
||||
|
||||
/* PARANOIA */
|
||||
if (!ic) {
|
||||
printk(KERN_WARNING "inode_cache not found in remove_node_refs()!!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "Removed nodes in range 0x%08x-0x%08x from ino #%u\n",
|
||||
jeb->offset, jeb->offset + c->sector_size, ic->ino));
|
||||
|
||||
D2({
|
||||
int i=0;
|
||||
struct jffs2_raw_node_ref *this;
|
||||
printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n" KERN_DEBUG);
|
||||
|
||||
this = ic->nodes;
|
||||
|
||||
while(this) {
|
||||
printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this));
|
||||
if (++i == 5) {
|
||||
printk("\n" KERN_DEBUG);
|
||||
i=0;
|
||||
}
|
||||
this = this->next_in_ino;
|
||||
}
|
||||
printk("\n");
|
||||
});
|
||||
|
||||
if (ic->nodes == (void *)ic) {
|
||||
D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
|
||||
jffs2_del_ino_cache(c, ic);
|
||||
jffs2_free_inode_cache(ic);
|
||||
}
|
||||
}
|
||||
|
||||
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
struct jffs2_raw_node_ref *ref;
|
||||
D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset));
|
||||
while(jeb->first_node) {
|
||||
ref = jeb->first_node;
|
||||
jeb->first_node = ref->next_phys;
|
||||
|
||||
/* Remove from the inode-list */
|
||||
if (ref->next_in_ino)
|
||||
jffs2_remove_node_refs_from_ino_list(c, ref, jeb);
|
||||
/* else it was a non-inode node or already removed, so don't bother */
|
||||
|
||||
jffs2_free_raw_node_ref(ref);
|
||||
}
|
||||
jeb->last_node = NULL;
|
||||
}
|
||||
|
||||
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
struct jffs2_raw_node_ref *marker_ref = NULL;
|
||||
unsigned char *ebuf;
|
||||
size_t retlen;
|
||||
int ret;
|
||||
uint32_t bad_offset;
|
||||
|
||||
if (!jffs2_cleanmarker_oob(c)) {
|
||||
marker_ref = jffs2_alloc_raw_node_ref();
|
||||
if (!marker_ref) {
|
||||
printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
|
||||
/* Stick it back on the list from whence it came and come back later */
|
||||
jffs2_erase_pending_trigger(c);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
list_add(&jeb->list, &c->erase_complete_list);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!ebuf) {
|
||||
printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
|
||||
} else {
|
||||
uint32_t ofs = jeb->offset;
|
||||
|
||||
D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
|
||||
while(ofs < jeb->offset + c->sector_size) {
|
||||
uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
|
||||
int i;
|
||||
|
||||
bad_offset = ofs;
|
||||
|
||||
ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
|
||||
goto bad;
|
||||
}
|
||||
if (retlen != readlen) {
|
||||
printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen);
|
||||
goto bad;
|
||||
}
|
||||
for (i=0; i<readlen; i += sizeof(unsigned long)) {
|
||||
/* It's OK. We know it's properly aligned */
|
||||
unsigned long datum = *(unsigned long *)(&ebuf[i]);
|
||||
if (datum + 1) {
|
||||
bad_offset += i;
|
||||
printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
|
||||
bad:
|
||||
if (!jffs2_cleanmarker_oob(c))
|
||||
jffs2_free_raw_node_ref(marker_ref);
|
||||
kfree(ebuf);
|
||||
bad2:
|
||||
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);
|
||||
jffs2_erase_failed(c, jeb, bad_offset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ofs += readlen;
|
||||
cond_resched();
|
||||
}
|
||||
kfree(ebuf);
|
||||
}
|
||||
|
||||
bad_offset = jeb->offset;
|
||||
|
||||
/* Write the erase complete marker */
|
||||
D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
|
||||
if (jffs2_cleanmarker_oob(c)) {
|
||||
|
||||
if (jffs2_write_nand_cleanmarker(c, jeb))
|
||||
goto bad2;
|
||||
|
||||
jeb->first_node = jeb->last_node = NULL;
|
||||
|
||||
jeb->free_size = c->sector_size;
|
||||
jeb->used_size = 0;
|
||||
jeb->dirty_size = 0;
|
||||
jeb->wasted_size = 0;
|
||||
} else {
|
||||
struct kvec vecs[1];
|
||||
struct jffs2_unknown_node marker = {
|
||||
.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
|
||||
.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
|
||||
.totlen = cpu_to_je32(c->cleanmarker_size)
|
||||
};
|
||||
|
||||
marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
vecs[0].iov_base = (unsigned char *) ▮
|
||||
vecs[0].iov_len = sizeof(marker);
|
||||
ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
|
||||
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
|
||||
jeb->offset, ret);
|
||||
goto bad2;
|
||||
}
|
||||
if (retlen != sizeof(marker)) {
|
||||
printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
|
||||
jeb->offset, sizeof(marker), retlen);
|
||||
goto bad2;
|
||||
}
|
||||
|
||||
marker_ref->next_in_ino = NULL;
|
||||
marker_ref->next_phys = NULL;
|
||||
marker_ref->flash_offset = jeb->offset | REF_NORMAL;
|
||||
marker_ref->__totlen = c->cleanmarker_size;
|
||||
|
||||
jeb->first_node = jeb->last_node = marker_ref;
|
||||
|
||||
jeb->free_size = c->sector_size - c->cleanmarker_size;
|
||||
jeb->used_size = c->cleanmarker_size;
|
||||
jeb->dirty_size = 0;
|
||||
jeb->wasted_size = 0;
|
||||
}
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
c->erasing_size -= c->sector_size;
|
||||
c->free_size += jeb->free_size;
|
||||
c->used_size += jeb->used_size;
|
||||
|
||||
ACCT_SANITY_CHECK(c,jeb);
|
||||
D1(ACCT_PARANOIA_CHECK(jeb));
|
||||
|
||||
list_add_tail(&jeb->list, &c->free_list);
|
||||
c->nr_erasing_blocks--;
|
||||
c->nr_free_blocks++;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
wake_up(&c->erase_wait);
|
||||
}
|
||||
|
290
fs/jffs2/file.c
Normální soubor
290
fs/jffs2/file.c
Normální soubor
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* 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: file.c,v 1.99 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
extern int generic_file_open(struct inode *, struct file *) __attribute__((weak));
|
||||
extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) __attribute__((weak));
|
||||
|
||||
static int jffs2_commit_write (struct file *filp, struct page *pg,
|
||||
unsigned start, unsigned end);
|
||||
static int jffs2_prepare_write (struct file *filp, struct page *pg,
|
||||
unsigned start, unsigned end);
|
||||
static int jffs2_readpage (struct file *filp, struct page *pg);
|
||||
|
||||
int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
|
||||
|
||||
/* Trigger GC to flush any pending writes for this inode */
|
||||
jffs2_flush_wbuf_gc(c, inode->i_ino);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct file_operations jffs2_file_operations =
|
||||
{
|
||||
.llseek = generic_file_llseek,
|
||||
.open = generic_file_open,
|
||||
.read = generic_file_read,
|
||||
.write = generic_file_write,
|
||||
.ioctl = jffs2_ioctl,
|
||||
.mmap = generic_file_readonly_mmap,
|
||||
.fsync = jffs2_fsync,
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29)
|
||||
.sendfile = generic_file_sendfile
|
||||
#endif
|
||||
};
|
||||
|
||||
/* jffs2_file_inode_operations */
|
||||
|
||||
struct inode_operations jffs2_file_inode_operations =
|
||||
{
|
||||
.setattr = jffs2_setattr
|
||||
};
|
||||
|
||||
struct address_space_operations jffs2_file_address_operations =
|
||||
{
|
||||
.readpage = jffs2_readpage,
|
||||
.prepare_write =jffs2_prepare_write,
|
||||
.commit_write = jffs2_commit_write
|
||||
};
|
||||
|
||||
static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
|
||||
unsigned char *pg_buf;
|
||||
int ret;
|
||||
|
||||
D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
|
||||
|
||||
if (!PageLocked(pg))
|
||||
PAGE_BUG(pg);
|
||||
|
||||
pg_buf = kmap(pg);
|
||||
/* FIXME: Can kmap fail? */
|
||||
|
||||
ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
|
||||
|
||||
if (ret) {
|
||||
ClearPageUptodate(pg);
|
||||
SetPageError(pg);
|
||||
} else {
|
||||
SetPageUptodate(pg);
|
||||
ClearPageError(pg);
|
||||
}
|
||||
|
||||
flush_dcache_page(pg);
|
||||
kunmap(pg);
|
||||
|
||||
D2(printk(KERN_DEBUG "readpage finished\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg)
|
||||
{
|
||||
int ret = jffs2_do_readpage_nolock(inode, pg);
|
||||
unlock_page(pg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int jffs2_readpage (struct file *filp, struct page *pg)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host);
|
||||
int ret;
|
||||
|
||||
down(&f->sem);
|
||||
ret = jffs2_do_readpage_unlock(pg->mapping->host, pg);
|
||||
up(&f->sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jffs2_prepare_write (struct file *filp, struct page *pg,
|
||||
unsigned start, unsigned end)
|
||||
{
|
||||
struct inode *inode = pg->mapping->host;
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT;
|
||||
int ret = 0;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_prepare_write()\n"));
|
||||
|
||||
if (pageofs > inode->i_size) {
|
||||
/* Make new hole frag from old EOF to new page */
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
|
||||
struct jffs2_raw_inode ri;
|
||||
struct jffs2_full_dnode *fn;
|
||||
uint32_t phys_ofs, alloc_len;
|
||||
|
||||
D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
|
||||
(unsigned int)inode->i_size, pageofs));
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
down(&f->sem);
|
||||
memset(&ri, 0, sizeof(ri));
|
||||
|
||||
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
|
||||
ri.totlen = cpu_to_je32(sizeof(ri));
|
||||
ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
ri.ino = cpu_to_je32(f->inocache->ino);
|
||||
ri.version = cpu_to_je32(++f->highest_version);
|
||||
ri.mode = cpu_to_jemode(inode->i_mode);
|
||||
ri.uid = cpu_to_je16(inode->i_uid);
|
||||
ri.gid = cpu_to_je16(inode->i_gid);
|
||||
ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs));
|
||||
ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds());
|
||||
ri.offset = cpu_to_je32(inode->i_size);
|
||||
ri.dsize = cpu_to_je32(pageofs - inode->i_size);
|
||||
ri.csize = cpu_to_je32(0);
|
||||
ri.compr = JFFS2_COMPR_ZERO;
|
||||
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
|
||||
ri.data_crc = cpu_to_je32(0);
|
||||
|
||||
fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
|
||||
|
||||
if (IS_ERR(fn)) {
|
||||
ret = PTR_ERR(fn);
|
||||
jffs2_complete_reservation(c);
|
||||
up(&f->sem);
|
||||
return ret;
|
||||
}
|
||||
ret = jffs2_add_full_dnode_to_inode(c, f, fn);
|
||||
if (f->metadata) {
|
||||
jffs2_mark_node_obsolete(c, f->metadata->raw);
|
||||
jffs2_free_full_dnode(f->metadata);
|
||||
f->metadata = NULL;
|
||||
}
|
||||
if (ret) {
|
||||
D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret));
|
||||
jffs2_mark_node_obsolete(c, fn->raw);
|
||||
jffs2_free_full_dnode(fn);
|
||||
jffs2_complete_reservation(c);
|
||||
up(&f->sem);
|
||||
return ret;
|
||||
}
|
||||
jffs2_complete_reservation(c);
|
||||
inode->i_size = pageofs;
|
||||
up(&f->sem);
|
||||
}
|
||||
|
||||
/* Read in the page if it wasn't already present, unless it's a whole page */
|
||||
if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
|
||||
down(&f->sem);
|
||||
ret = jffs2_do_readpage_nolock(inode, pg);
|
||||
up(&f->sem);
|
||||
}
|
||||
D1(printk(KERN_DEBUG "end prepare_write(). pg->flags %lx\n", pg->flags));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jffs2_commit_write (struct file *filp, struct page *pg,
|
||||
unsigned start, unsigned end)
|
||||
{
|
||||
/* Actually commit the write from the page cache page we're looking at.
|
||||
* For now, we write the full page out each time. It sucks, but it's simple
|
||||
*/
|
||||
struct inode *inode = pg->mapping->host;
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
|
||||
struct jffs2_raw_inode *ri;
|
||||
unsigned aligned_start = start & ~3;
|
||||
int ret = 0;
|
||||
uint32_t writtenlen = 0;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
|
||||
inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
|
||||
|
||||
if (!start && end == PAGE_CACHE_SIZE) {
|
||||
/* We need to avoid deadlock with page_cache_read() in
|
||||
jffs2_garbage_collect_pass(). So we have to mark the
|
||||
page up to date, to prevent page_cache_read() from
|
||||
trying to re-lock it. */
|
||||
SetPageUptodate(pg);
|
||||
}
|
||||
|
||||
ri = jffs2_alloc_raw_inode();
|
||||
|
||||
if (!ri) {
|
||||
D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Set the fields that the generic jffs2_write_inode_range() code can't find */
|
||||
ri->ino = cpu_to_je32(inode->i_ino);
|
||||
ri->mode = cpu_to_jemode(inode->i_mode);
|
||||
ri->uid = cpu_to_je16(inode->i_uid);
|
||||
ri->gid = cpu_to_je16(inode->i_gid);
|
||||
ri->isize = cpu_to_je32((uint32_t)inode->i_size);
|
||||
ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds());
|
||||
|
||||
/* In 2.4, it was already kmapped by generic_file_write(). Doesn't
|
||||
hurt to do it again. The alternative is ifdefs, which are ugly. */
|
||||
kmap(pg);
|
||||
|
||||
ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
|
||||
(pg->index << PAGE_CACHE_SHIFT) + aligned_start,
|
||||
end - aligned_start, &writtenlen);
|
||||
|
||||
kunmap(pg);
|
||||
|
||||
if (ret) {
|
||||
/* There was an error writing. */
|
||||
SetPageError(pg);
|
||||
}
|
||||
|
||||
/* Adjust writtenlen for the padding we did, so we don't confuse our caller */
|
||||
if (writtenlen < (start&3))
|
||||
writtenlen = 0;
|
||||
else
|
||||
writtenlen -= (start&3);
|
||||
|
||||
if (writtenlen) {
|
||||
if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) {
|
||||
inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen;
|
||||
inode->i_blocks = (inode->i_size + 511) >> 9;
|
||||
|
||||
inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime));
|
||||
}
|
||||
}
|
||||
|
||||
jffs2_free_raw_inode(ri);
|
||||
|
||||
if (start+writtenlen < end) {
|
||||
/* generic_file_write has written more to the page cache than we've
|
||||
actually written to the medium. Mark the page !Uptodate so that
|
||||
it gets reread */
|
||||
D1(printk(KERN_DEBUG "jffs2_commit_write(): Not all bytes written. Marking page !uptodate\n"));
|
||||
SetPageError(pg);
|
||||
ClearPageUptodate(pg);
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",writtenlen?writtenlen:ret));
|
||||
return writtenlen?writtenlen:ret;
|
||||
}
|
677
fs/jffs2/fs.c
Normální soubor
677
fs/jffs2/fs.c
Normální soubor
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
* 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: fs.c,v 1.51 2004/11/28 12:19:37 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/vfs.h>
|
||||
#include <linux/crc32.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
static int jffs2_flash_setup(struct jffs2_sb_info *c);
|
||||
|
||||
static 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);
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
|
||||
struct jffs2_raw_inode *ri;
|
||||
unsigned short dev;
|
||||
unsigned char *mdata = NULL;
|
||||
int mdatalen = 0;
|
||||
unsigned int ivalid;
|
||||
uint32_t phys_ofs, 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
|
||||
must read the original data associated with the node
|
||||
(i.e. the device numbers or the target name) and write
|
||||
it out again with the appropriate data attached */
|
||||
if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
|
||||
/* For these, we don't actually need to read the old node */
|
||||
dev = old_encode_dev(inode->i_rdev);
|
||||
mdata = (char *)&dev;
|
||||
mdatalen = sizeof(dev);
|
||||
D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
|
||||
} else if (S_ISLNK(inode->i_mode)) {
|
||||
mdatalen = f->metadata->size;
|
||||
mdata = kmalloc(f->metadata->size, GFP_USER);
|
||||
if (!mdata)
|
||||
return -ENOMEM;
|
||||
ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen);
|
||||
if (ret) {
|
||||
kfree(mdata);
|
||||
return ret;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
|
||||
}
|
||||
|
||||
ri = jffs2_alloc_raw_inode();
|
||||
if (!ri) {
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
kfree(mdata);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
if (ret) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
if (S_ISLNK(inode->i_mode & S_IFMT))
|
||||
kfree(mdata);
|
||||
return ret;
|
||||
}
|
||||
down(&f->sem);
|
||||
ivalid = iattr->ia_valid;
|
||||
|
||||
ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
|
||||
ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen);
|
||||
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
ri->ino = cpu_to_je32(inode->i_ino);
|
||||
ri->version = cpu_to_je32(++f->highest_version);
|
||||
|
||||
ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid);
|
||||
ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid);
|
||||
|
||||
if (ivalid & ATTR_MODE)
|
||||
if (iattr->ia_mode & S_ISGID &&
|
||||
!in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID))
|
||||
ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID);
|
||||
else
|
||||
ri->mode = cpu_to_jemode(iattr->ia_mode);
|
||||
else
|
||||
ri->mode = cpu_to_jemode(inode->i_mode);
|
||||
|
||||
|
||||
ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size);
|
||||
ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime));
|
||||
ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime));
|
||||
ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime));
|
||||
|
||||
ri->offset = cpu_to_je32(0);
|
||||
ri->csize = ri->dsize = cpu_to_je32(mdatalen);
|
||||
ri->compr = JFFS2_COMPR_NONE;
|
||||
if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
|
||||
/* It's an extension. Make it a hole node */
|
||||
ri->compr = JFFS2_COMPR_ZERO;
|
||||
ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size);
|
||||
ri->offset = cpu_to_je32(inode->i_size);
|
||||
}
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
if (mdatalen)
|
||||
ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
|
||||
else
|
||||
ri->data_crc = cpu_to_je32(0);
|
||||
|
||||
new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL);
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
kfree(mdata);
|
||||
|
||||
if (IS_ERR(new_metadata)) {
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_free_raw_inode(ri);
|
||||
up(&f->sem);
|
||||
return PTR_ERR(new_metadata);
|
||||
}
|
||||
/* It worked. Update the inode */
|
||||
inode->i_atime = ITIME(je32_to_cpu(ri->atime));
|
||||
inode->i_ctime = ITIME(je32_to_cpu(ri->ctime));
|
||||
inode->i_mtime = ITIME(je32_to_cpu(ri->mtime));
|
||||
inode->i_mode = jemode_to_cpu(ri->mode);
|
||||
inode->i_uid = je16_to_cpu(ri->uid);
|
||||
inode->i_gid = je16_to_cpu(ri->gid);
|
||||
|
||||
|
||||
old_metadata = f->metadata;
|
||||
|
||||
if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
|
||||
jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size);
|
||||
|
||||
if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
|
||||
jffs2_add_full_dnode_to_inode(c, f, new_metadata);
|
||||
inode->i_size = iattr->ia_size;
|
||||
f->metadata = NULL;
|
||||
} else {
|
||||
f->metadata = new_metadata;
|
||||
}
|
||||
if (old_metadata) {
|
||||
jffs2_mark_node_obsolete(c, old_metadata->raw);
|
||||
jffs2_free_full_dnode(old_metadata);
|
||||
}
|
||||
jffs2_free_raw_inode(ri);
|
||||
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
|
||||
/* We have to do the vmtruncate() without f->sem held, since
|
||||
some pages may be locked and waiting for it in readpage().
|
||||
We are protected from a simultaneous write() extending i_size
|
||||
back past iattr->ia_size, because do_truncate() holds the
|
||||
generic inode semaphore. */
|
||||
if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
|
||||
vmtruncate(inode, iattr->ia_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||
{
|
||||
return jffs2_do_setattr(dentry->d_inode, iattr);
|
||||
}
|
||||
|
||||
int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
|
||||
{
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
|
||||
unsigned long avail;
|
||||
|
||||
buf->f_type = JFFS2_SUPER_MAGIC;
|
||||
buf->f_bsize = 1 << PAGE_SHIFT;
|
||||
buf->f_blocks = c->flash_size >> PAGE_SHIFT;
|
||||
buf->f_files = 0;
|
||||
buf->f_ffree = 0;
|
||||
buf->f_namelen = JFFS2_MAX_NAME_LEN;
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
avail = c->dirty_size + c->free_size;
|
||||
if (avail > c->sector_size * c->resv_blocks_write)
|
||||
avail -= c->sector_size * c->resv_blocks_write;
|
||||
else
|
||||
avail = 0;
|
||||
|
||||
buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
|
||||
|
||||
D2(jffs2_dump_block_lists(c));
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void jffs2_clear_inode (struct inode *inode)
|
||||
{
|
||||
/* We can forget about this inode for now - drop all
|
||||
* the nodelists associated with it, etc.
|
||||
*/
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
|
||||
|
||||
jffs2_do_clear_inode(c, f);
|
||||
}
|
||||
|
||||
void jffs2_read_inode (struct inode *inode)
|
||||
{
|
||||
struct jffs2_inode_info *f;
|
||||
struct jffs2_sb_info *c;
|
||||
struct jffs2_raw_inode latest_node;
|
||||
int ret;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
|
||||
|
||||
f = JFFS2_INODE_INFO(inode);
|
||||
c = JFFS2_SB_INFO(inode->i_sb);
|
||||
|
||||
jffs2_init_inode_info(f);
|
||||
|
||||
ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
|
||||
|
||||
if (ret) {
|
||||
make_bad_inode(inode);
|
||||
up(&f->sem);
|
||||
return;
|
||||
}
|
||||
inode->i_mode = jemode_to_cpu(latest_node.mode);
|
||||
inode->i_uid = je16_to_cpu(latest_node.uid);
|
||||
inode->i_gid = je16_to_cpu(latest_node.gid);
|
||||
inode->i_size = je32_to_cpu(latest_node.isize);
|
||||
inode->i_atime = ITIME(je32_to_cpu(latest_node.atime));
|
||||
inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime));
|
||||
inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime));
|
||||
|
||||
inode->i_nlink = f->inocache->nlink;
|
||||
|
||||
inode->i_blksize = PAGE_SIZE;
|
||||
inode->i_blocks = (inode->i_size + 511) >> 9;
|
||||
|
||||
switch (inode->i_mode & S_IFMT) {
|
||||
jint16_t rdev;
|
||||
|
||||
case S_IFLNK:
|
||||
inode->i_op = &jffs2_symlink_inode_operations;
|
||||
break;
|
||||
|
||||
case S_IFDIR:
|
||||
{
|
||||
struct jffs2_full_dirent *fd;
|
||||
|
||||
for (fd=f->dents; fd; fd = fd->next) {
|
||||
if (fd->type == DT_DIR && fd->ino)
|
||||
inode->i_nlink++;
|
||||
}
|
||||
/* and '..' */
|
||||
inode->i_nlink++;
|
||||
/* Root dir gets i_nlink 3 for some reason */
|
||||
if (inode->i_ino == 1)
|
||||
inode->i_nlink++;
|
||||
|
||||
inode->i_op = &jffs2_dir_inode_operations;
|
||||
inode->i_fop = &jffs2_dir_operations;
|
||||
break;
|
||||
}
|
||||
case S_IFREG:
|
||||
inode->i_op = &jffs2_file_inode_operations;
|
||||
inode->i_fop = &jffs2_file_operations;
|
||||
inode->i_mapping->a_ops = &jffs2_file_address_operations;
|
||||
inode->i_mapping->nrpages = 0;
|
||||
break;
|
||||
|
||||
case S_IFBLK:
|
||||
case S_IFCHR:
|
||||
/* Read the device numbers from the media */
|
||||
D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
|
||||
if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
|
||||
/* Eep */
|
||||
printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
|
||||
up(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
make_bad_inode(inode);
|
||||
return;
|
||||
}
|
||||
|
||||
case S_IFSOCK:
|
||||
case S_IFIFO:
|
||||
inode->i_op = &jffs2_file_inode_operations;
|
||||
init_special_inode(inode, inode->i_mode,
|
||||
old_decode_dev((je16_to_cpu(rdev))));
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino);
|
||||
}
|
||||
|
||||
up(&f->sem);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
|
||||
}
|
||||
|
||||
void jffs2_dirty_inode(struct inode *inode)
|
||||
{
|
||||
struct iattr iattr;
|
||||
|
||||
if (!(inode->i_state & I_DIRTY_DATASYNC)) {
|
||||
D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino));
|
||||
return;
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino));
|
||||
|
||||
iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME;
|
||||
iattr.ia_mode = inode->i_mode;
|
||||
iattr.ia_uid = inode->i_uid;
|
||||
iattr.ia_gid = inode->i_gid;
|
||||
iattr.ia_atime = inode->i_atime;
|
||||
iattr.ia_mtime = inode->i_mtime;
|
||||
iattr.ia_ctime = inode->i_ctime;
|
||||
|
||||
jffs2_do_setattr(inode, &iattr);
|
||||
}
|
||||
|
||||
int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
|
||||
|
||||
if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
|
||||
return -EROFS;
|
||||
|
||||
/* We stop if it was running, then restart if it needs to.
|
||||
This also catches the case where it was stopped and this
|
||||
is just a remount to restart it.
|
||||
Flush the writebuffer, if neccecary, else we loose it */
|
||||
if (!(sb->s_flags & MS_RDONLY)) {
|
||||
jffs2_stop_garbage_collect_thread(c);
|
||||
down(&c->alloc_sem);
|
||||
jffs2_flush_wbuf_pad(c);
|
||||
up(&c->alloc_sem);
|
||||
}
|
||||
|
||||
if (!(*flags & MS_RDONLY))
|
||||
jffs2_start_garbage_collect_thread(c);
|
||||
|
||||
*flags |= MS_NOATIME;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jffs2_write_super (struct super_block *sb)
|
||||
{
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
|
||||
sb->s_dirt = 0;
|
||||
|
||||
if (sb->s_flags & MS_RDONLY)
|
||||
return;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_write_super()\n"));
|
||||
jffs2_garbage_collect_trigger(c);
|
||||
jffs2_erase_pending_blocks(c, 0);
|
||||
jffs2_flush_wbuf_gc(c, 0);
|
||||
}
|
||||
|
||||
|
||||
/* 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 *inode;
|
||||
struct super_block *sb = dir_i->i_sb;
|
||||
struct jffs2_sb_info *c;
|
||||
struct jffs2_inode_info *f;
|
||||
int ret;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
|
||||
|
||||
c = JFFS2_SB_INFO(sb);
|
||||
|
||||
inode = new_inode(sb);
|
||||
|
||||
if (!inode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
f = JFFS2_INODE_INFO(inode);
|
||||
jffs2_init_inode_info(f);
|
||||
|
||||
memset(ri, 0, sizeof(*ri));
|
||||
/* Set OS-specific defaults for new inodes */
|
||||
ri->uid = cpu_to_je16(current->fsuid);
|
||||
|
||||
if (dir_i->i_mode & S_ISGID) {
|
||||
ri->gid = cpu_to_je16(dir_i->i_gid);
|
||||
if (S_ISDIR(mode))
|
||||
mode |= S_ISGID;
|
||||
} else {
|
||||
ri->gid = cpu_to_je16(current->fsgid);
|
||||
}
|
||||
ri->mode = cpu_to_jemode(mode);
|
||||
ret = jffs2_do_new_inode (c, f, mode, ri);
|
||||
if (ret) {
|
||||
make_bad_inode(inode);
|
||||
iput(inode);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
inode->i_nlink = 1;
|
||||
inode->i_ino = je32_to_cpu(ri->ino);
|
||||
inode->i_mode = jemode_to_cpu(ri->mode);
|
||||
inode->i_gid = je16_to_cpu(ri->gid);
|
||||
inode->i_uid = je16_to_cpu(ri->uid);
|
||||
inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
|
||||
ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime));
|
||||
|
||||
inode->i_blksize = PAGE_SIZE;
|
||||
inode->i_blocks = 0;
|
||||
inode->i_size = 0;
|
||||
|
||||
insert_inode_hash(inode);
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
|
||||
int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct jffs2_sb_info *c;
|
||||
struct inode *root_i;
|
||||
int ret;
|
||||
size_t blocks;
|
||||
|
||||
c = JFFS2_SB_INFO(sb);
|
||||
|
||||
#ifndef CONFIG_JFFS2_FS_NAND
|
||||
if (c->mtd->type == MTD_NANDFLASH) {
|
||||
printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
c->flash_size = c->mtd->size;
|
||||
|
||||
/*
|
||||
* Check, if we have to concatenate physical blocks to larger virtual blocks
|
||||
* to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation)
|
||||
*/
|
||||
c->sector_size = c->mtd->erasesize;
|
||||
blocks = c->flash_size / c->sector_size;
|
||||
if (!(c->mtd->flags & MTD_NO_VIRTBLOCKS)) {
|
||||
while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) {
|
||||
blocks >>= 1;
|
||||
c->sector_size <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Size alignment check
|
||||
*/
|
||||
if ((c->sector_size * blocks) != c->flash_size) {
|
||||
c->flash_size = c->sector_size * blocks;
|
||||
printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n",
|
||||
c->flash_size / 1024);
|
||||
}
|
||||
|
||||
if (c->sector_size != c->mtd->erasesize)
|
||||
printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n",
|
||||
c->mtd->erasesize / 1024, c->sector_size / 1024);
|
||||
|
||||
if (c->flash_size < 5*c->sector_size) {
|
||||
printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
|
||||
/* Joern -- stick alignment for weird 8-byte-page flash here */
|
||||
|
||||
/* NAND (or other bizarre) flash... do setup accordingly */
|
||||
ret = jffs2_flash_setup(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
|
||||
if (!c->inocache_list) {
|
||||
ret = -ENOMEM;
|
||||
goto out_wbuf;
|
||||
}
|
||||
memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
|
||||
|
||||
if ((ret = jffs2_do_mount_fs(c)))
|
||||
goto out_inohash;
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n"));
|
||||
root_i = iget(sb, 1);
|
||||
if (is_bad_inode(root_i)) {
|
||||
D1(printk(KERN_WARNING "get root inode failed\n"));
|
||||
goto out_nodes;
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n"));
|
||||
sb->s_root = d_alloc_root(root_i);
|
||||
if (!sb->s_root)
|
||||
goto out_root_i;
|
||||
|
||||
#if LINUX_VERSION_CODE >= 0x20403
|
||||
sb->s_maxbytes = 0xFFFFFFFF;
|
||||
#endif
|
||||
sb->s_blocksize = PAGE_CACHE_SIZE;
|
||||
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
||||
sb->s_magic = JFFS2_SUPER_MAGIC;
|
||||
if (!(sb->s_flags & MS_RDONLY))
|
||||
jffs2_start_garbage_collect_thread(c);
|
||||
return 0;
|
||||
|
||||
out_root_i:
|
||||
iput(root_i);
|
||||
out_nodes:
|
||||
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);
|
||||
out_inohash:
|
||||
kfree(c->inocache_list);
|
||||
out_wbuf:
|
||||
jffs2_flash_cleanup(c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_gc_release_inode(struct jffs2_sb_info *c,
|
||||
struct jffs2_inode_info *f)
|
||||
{
|
||||
iput(OFNI_EDONI_2SFFJ(f));
|
||||
}
|
||||
|
||||
struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
|
||||
int inum, int nlink)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct jffs2_inode_cache *ic;
|
||||
if (!nlink) {
|
||||
/* The inode has zero nlink but its nodes weren't yet marked
|
||||
obsolete. This has to be because we're still waiting for
|
||||
the final (close() and) iput() to happen.
|
||||
|
||||
There's a possibility that the final iput() could have
|
||||
happened while we were contemplating. In order to ensure
|
||||
that we don't cause a new read_inode() (which would fail)
|
||||
for the inode in question, we use ilookup() in this case
|
||||
instead of iget().
|
||||
|
||||
The nlink can't _become_ zero at this point because we're
|
||||
holding the alloc_sem, and jffs2_do_unlink() would also
|
||||
need that while decrementing nlink on any inode.
|
||||
*/
|
||||
inode = ilookup(OFNI_BS_2SFFJ(c), inum);
|
||||
if (!inode) {
|
||||
D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
|
||||
inum));
|
||||
|
||||
spin_lock(&c->inocache_lock);
|
||||
ic = jffs2_get_ino_cache(c, inum);
|
||||
if (!ic) {
|
||||
D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
|
||||
spin_unlock(&c->inocache_lock);
|
||||
return NULL;
|
||||
}
|
||||
if (ic->state != INO_STATE_CHECKEDABSENT) {
|
||||
/* Wait for progress. Don't just loop */
|
||||
D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
|
||||
ic->ino, ic->state));
|
||||
sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
|
||||
} else {
|
||||
spin_unlock(&c->inocache_lock);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
/* Inode has links to it still; they're not going away because
|
||||
jffs2_do_unlink() would need the alloc_sem and we have it.
|
||||
Just iget() it, and if read_inode() is necessary that's OK.
|
||||
*/
|
||||
inode = iget(OFNI_BS_2SFFJ(c), inum);
|
||||
if (!inode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
if (is_bad_inode(inode)) {
|
||||
printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n",
|
||||
inum, nlink);
|
||||
/* NB. This will happen again. We need to do something appropriate here. */
|
||||
iput(inode);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
return JFFS2_INODE_INFO(inode);
|
||||
}
|
||||
|
||||
unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
|
||||
struct jffs2_inode_info *f,
|
||||
unsigned long offset,
|
||||
unsigned long *priv)
|
||||
{
|
||||
struct inode *inode = OFNI_EDONI_2SFFJ(f);
|
||||
struct page *pg;
|
||||
|
||||
pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT,
|
||||
(void *)jffs2_do_readpage_unlock, inode);
|
||||
if (IS_ERR(pg))
|
||||
return (void *)pg;
|
||||
|
||||
*priv = (unsigned long)pg;
|
||||
return kmap(pg);
|
||||
}
|
||||
|
||||
void jffs2_gc_release_page(struct jffs2_sb_info *c,
|
||||
unsigned char *ptr,
|
||||
unsigned long *priv)
|
||||
{
|
||||
struct page *pg = (void *)*priv;
|
||||
|
||||
kunmap(pg);
|
||||
page_cache_release(pg);
|
||||
}
|
||||
|
||||
static int jffs2_flash_setup(struct jffs2_sb_info *c) {
|
||||
int ret = 0;
|
||||
|
||||
if (jffs2_cleanmarker_oob(c)) {
|
||||
/* NAND flash... do setup accordingly */
|
||||
ret = jffs2_nand_flash_setup(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* add setups for other bizarre flashes here... */
|
||||
if (jffs2_nor_ecc(c)) {
|
||||
ret = jffs2_nor_ecc_flash_setup(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
|
||||
|
||||
if (jffs2_cleanmarker_oob(c)) {
|
||||
jffs2_nand_flash_cleanup(c);
|
||||
}
|
||||
|
||||
/* add cleanups for other bizarre flashes here... */
|
||||
if (jffs2_nor_ecc(c)) {
|
||||
jffs2_nor_ecc_flash_cleanup(c);
|
||||
}
|
||||
}
|
1246
fs/jffs2/gc.c
Normální soubor
1246
fs/jffs2/gc.c
Normální soubor
Rozdílový obsah nebyl zobrazen, protože je příliš veliký
Načíst rozdílové porovnání
3
fs/jffs2/histo.h
Normální soubor
3
fs/jffs2/histo.h
Normální soubor
@@ -0,0 +1,3 @@
|
||||
/* This file provides the bit-probabilities for the input file */
|
||||
#define BIT_DIVIDER 629
|
||||
static int bits[9] = { 179,167,183,165,159,198,178,119,}; /* ia32 .so files */
|
2
fs/jffs2/histo_mips.h
Normální soubor
2
fs/jffs2/histo_mips.h
Normální soubor
@@ -0,0 +1,2 @@
|
||||
#define BIT_DIVIDER_MIPS 1043
|
||||
static int bits_mips[8] = { 277,249,290,267,229,341,212,241}; /* mips32 */
|
23
fs/jffs2/ioctl.c
Normální soubor
23
fs/jffs2/ioctl.c
Normální soubor
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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: ioctl.c,v 1.9 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
/* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
|
||||
will include compression support etc. */
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
205
fs/jffs2/malloc.c
Normální soubor
205
fs/jffs2/malloc.c
Normální soubor
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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: malloc.c,v 1.28 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
#if 0
|
||||
#define JFFS2_SLAB_POISON SLAB_POISON
|
||||
#else
|
||||
#define JFFS2_SLAB_POISON 0
|
||||
#endif
|
||||
|
||||
// replace this by #define D3 (x) x for cache debugging
|
||||
#define D3(x)
|
||||
|
||||
/* These are initialised to NULL in the kernel startup code.
|
||||
If you're porting to other operating systems, beware */
|
||||
static kmem_cache_t *full_dnode_slab;
|
||||
static kmem_cache_t *raw_dirent_slab;
|
||||
static kmem_cache_t *raw_inode_slab;
|
||||
static kmem_cache_t *tmp_dnode_info_slab;
|
||||
static kmem_cache_t *raw_node_ref_slab;
|
||||
static kmem_cache_t *node_frag_slab;
|
||||
static kmem_cache_t *inode_cache_slab;
|
||||
|
||||
int __init jffs2_create_slab_caches(void)
|
||||
{
|
||||
full_dnode_slab = kmem_cache_create("jffs2_full_dnode",
|
||||
sizeof(struct jffs2_full_dnode),
|
||||
0, JFFS2_SLAB_POISON, NULL, NULL);
|
||||
if (!full_dnode_slab)
|
||||
goto err;
|
||||
|
||||
raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent",
|
||||
sizeof(struct jffs2_raw_dirent),
|
||||
0, JFFS2_SLAB_POISON, NULL, NULL);
|
||||
if (!raw_dirent_slab)
|
||||
goto err;
|
||||
|
||||
raw_inode_slab = kmem_cache_create("jffs2_raw_inode",
|
||||
sizeof(struct jffs2_raw_inode),
|
||||
0, JFFS2_SLAB_POISON, NULL, NULL);
|
||||
if (!raw_inode_slab)
|
||||
goto err;
|
||||
|
||||
tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode",
|
||||
sizeof(struct jffs2_tmp_dnode_info),
|
||||
0, JFFS2_SLAB_POISON, NULL, NULL);
|
||||
if (!tmp_dnode_info_slab)
|
||||
goto err;
|
||||
|
||||
raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref",
|
||||
sizeof(struct jffs2_raw_node_ref),
|
||||
0, JFFS2_SLAB_POISON, NULL, NULL);
|
||||
if (!raw_node_ref_slab)
|
||||
goto err;
|
||||
|
||||
node_frag_slab = kmem_cache_create("jffs2_node_frag",
|
||||
sizeof(struct jffs2_node_frag),
|
||||
0, JFFS2_SLAB_POISON, NULL, NULL);
|
||||
if (!node_frag_slab)
|
||||
goto err;
|
||||
|
||||
inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
|
||||
sizeof(struct jffs2_inode_cache),
|
||||
0, JFFS2_SLAB_POISON, NULL, NULL);
|
||||
if (inode_cache_slab)
|
||||
return 0;
|
||||
err:
|
||||
jffs2_destroy_slab_caches();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void jffs2_destroy_slab_caches(void)
|
||||
{
|
||||
if(full_dnode_slab)
|
||||
kmem_cache_destroy(full_dnode_slab);
|
||||
if(raw_dirent_slab)
|
||||
kmem_cache_destroy(raw_dirent_slab);
|
||||
if(raw_inode_slab)
|
||||
kmem_cache_destroy(raw_inode_slab);
|
||||
if(tmp_dnode_info_slab)
|
||||
kmem_cache_destroy(tmp_dnode_info_slab);
|
||||
if(raw_node_ref_slab)
|
||||
kmem_cache_destroy(raw_node_ref_slab);
|
||||
if(node_frag_slab)
|
||||
kmem_cache_destroy(node_frag_slab);
|
||||
if(inode_cache_slab)
|
||||
kmem_cache_destroy(inode_cache_slab);
|
||||
}
|
||||
|
||||
struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
|
||||
{
|
||||
return kmalloc(sizeof(struct jffs2_full_dirent) + namesize, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void jffs2_free_full_dirent(struct jffs2_full_dirent *x)
|
||||
{
|
||||
kfree(x);
|
||||
}
|
||||
|
||||
struct jffs2_full_dnode *jffs2_alloc_full_dnode(void)
|
||||
{
|
||||
struct jffs2_full_dnode *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
|
||||
D3 (printk (KERN_DEBUG "alloc_full_dnode at %p\n", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_full_dnode(struct jffs2_full_dnode *x)
|
||||
{
|
||||
D3 (printk (KERN_DEBUG "free full_dnode at %p\n", x));
|
||||
kmem_cache_free(full_dnode_slab, x);
|
||||
}
|
||||
|
||||
struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void)
|
||||
{
|
||||
struct jffs2_raw_dirent *ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
|
||||
D3 (printk (KERN_DEBUG "alloc_raw_dirent\n", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x)
|
||||
{
|
||||
D3 (printk (KERN_DEBUG "free_raw_dirent at %p\n", x));
|
||||
kmem_cache_free(raw_dirent_slab, x);
|
||||
}
|
||||
|
||||
struct jffs2_raw_inode *jffs2_alloc_raw_inode(void)
|
||||
{
|
||||
struct jffs2_raw_inode *ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
|
||||
D3 (printk (KERN_DEBUG "alloc_raw_inode at %p\n", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_raw_inode(struct jffs2_raw_inode *x)
|
||||
{
|
||||
D3 (printk (KERN_DEBUG "free_raw_inode at %p\n", x));
|
||||
kmem_cache_free(raw_inode_slab, x);
|
||||
}
|
||||
|
||||
struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void)
|
||||
{
|
||||
struct jffs2_tmp_dnode_info *ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
|
||||
D3 (printk (KERN_DEBUG "alloc_tmp_dnode_info at %p\n", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
|
||||
{
|
||||
D3 (printk (KERN_DEBUG "free_tmp_dnode_info at %p\n", x));
|
||||
kmem_cache_free(tmp_dnode_info_slab, x);
|
||||
}
|
||||
|
||||
struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void)
|
||||
{
|
||||
struct jffs2_raw_node_ref *ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
|
||||
D3 (printk (KERN_DEBUG "alloc_raw_node_ref at %p\n", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x)
|
||||
{
|
||||
D3 (printk (KERN_DEBUG "free_raw_node_ref at %p\n", x));
|
||||
kmem_cache_free(raw_node_ref_slab, x);
|
||||
}
|
||||
|
||||
struct jffs2_node_frag *jffs2_alloc_node_frag(void)
|
||||
{
|
||||
struct jffs2_node_frag *ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
|
||||
D3 (printk (KERN_DEBUG "alloc_node_frag at %p\n", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_node_frag(struct jffs2_node_frag *x)
|
||||
{
|
||||
D3 (printk (KERN_DEBUG "free_node_frag at %p\n", x));
|
||||
kmem_cache_free(node_frag_slab, x);
|
||||
}
|
||||
|
||||
struct jffs2_inode_cache *jffs2_alloc_inode_cache(void)
|
||||
{
|
||||
struct jffs2_inode_cache *ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL);
|
||||
D3 (printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
|
||||
{
|
||||
D3 (printk(KERN_DEBUG "Freeing inocache at %p\n", x));
|
||||
kmem_cache_free(inode_cache_slab, x);
|
||||
}
|
||||
|
681
fs/jffs2/nodelist.c
Normální soubor
681
fs/jffs2/nodelist.c
Normální soubor
@@ -0,0 +1,681 @@
|
||||
/*
|
||||
* 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: nodelist.c,v 1.90 2004/12/08 17:59:20 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
|
||||
{
|
||||
struct jffs2_full_dirent **prev = list;
|
||||
D1(printk(KERN_DEBUG "jffs2_add_fd_to_list( %p, %p (->%p))\n", new, list, *list));
|
||||
|
||||
while ((*prev) && (*prev)->nhash <= new->nhash) {
|
||||
if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) {
|
||||
/* Duplicate. Free one */
|
||||
if (new->version < (*prev)->version) {
|
||||
D1(printk(KERN_DEBUG "Eep! Marking new dirent node obsolete\n"));
|
||||
D1(printk(KERN_DEBUG "New dirent is \"%s\"->ino #%u. Old is \"%s\"->ino #%u\n", new->name, new->ino, (*prev)->name, (*prev)->ino));
|
||||
jffs2_mark_node_obsolete(c, new->raw);
|
||||
jffs2_free_full_dirent(new);
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) obsolete\n", (*prev)->ino));
|
||||
new->next = (*prev)->next;
|
||||
jffs2_mark_node_obsolete(c, ((*prev)->raw));
|
||||
jffs2_free_full_dirent(*prev);
|
||||
*prev = new;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
prev = &((*prev)->next);
|
||||
}
|
||||
new->next = *prev;
|
||||
*prev = new;
|
||||
|
||||
out:
|
||||
D2(while(*list) {
|
||||
printk(KERN_DEBUG "Dirent \"%s\" (hash 0x%08x, ino #%u\n", (*list)->name, (*list)->nhash, (*list)->ino);
|
||||
list = &(*list)->next;
|
||||
});
|
||||
}
|
||||
|
||||
/* Put a new tmp_dnode_info into the list, keeping the list in
|
||||
order of increasing version
|
||||
*/
|
||||
static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
|
||||
{
|
||||
struct jffs2_tmp_dnode_info **prev = list;
|
||||
|
||||
while ((*prev) && (*prev)->version < tn->version) {
|
||||
prev = &((*prev)->next);
|
||||
}
|
||||
tn->next = (*prev);
|
||||
*prev = tn;
|
||||
}
|
||||
|
||||
static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
|
||||
{
|
||||
struct jffs2_tmp_dnode_info *next;
|
||||
|
||||
while (tn) {
|
||||
next = tn;
|
||||
tn = tn->next;
|
||||
jffs2_free_full_dnode(next->fn);
|
||||
jffs2_free_tmp_dnode_info(next);
|
||||
}
|
||||
}
|
||||
|
||||
static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
|
||||
{
|
||||
struct jffs2_full_dirent *next;
|
||||
|
||||
while (fd) {
|
||||
next = fd->next;
|
||||
jffs2_free_full_dirent(fd);
|
||||
fd = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns first valid node after 'ref'. May return 'ref' */
|
||||
static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref)
|
||||
{
|
||||
while (ref && ref->next_in_ino) {
|
||||
if (!ref_obsolete(ref))
|
||||
return ref;
|
||||
D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
|
||||
ref = ref->next_in_ino;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
|
||||
with this ino, returning the former in order of version */
|
||||
|
||||
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
|
||||
uint32_t *highest_version, uint32_t *latest_mctime,
|
||||
uint32_t *mctime_ver)
|
||||
{
|
||||
struct jffs2_raw_node_ref *ref, *valid_ref;
|
||||
struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
|
||||
struct jffs2_full_dirent *fd, *ret_fd = NULL;
|
||||
union jffs2_node_union node;
|
||||
size_t retlen;
|
||||
int err;
|
||||
|
||||
*mctime_ver = 0;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino));
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
valid_ref = jffs2_first_valid_node(f->inocache->nodes);
|
||||
|
||||
if (!valid_ref)
|
||||
printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
|
||||
|
||||
while (valid_ref) {
|
||||
/* We can hold a pointer to a non-obsolete node without the spinlock,
|
||||
but _obsolete_ nodes may disappear at any time, if the block
|
||||
they're in gets erased. So if we mark 'ref' obsolete while we're
|
||||
not holding the lock, it can go away immediately. For that reason,
|
||||
we find the next valid node first, before processing 'ref'.
|
||||
*/
|
||||
ref = valid_ref;
|
||||
valid_ref = jffs2_first_valid_node(ref->next_in_ino);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
cond_resched();
|
||||
|
||||
/* FIXME: point() */
|
||||
err = jffs2_flash_read(c, (ref_offset(ref)),
|
||||
min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)),
|
||||
&retlen, (void *)&node);
|
||||
if (err) {
|
||||
printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
|
||||
/* Check we've managed to read at least the common node header */
|
||||
if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) {
|
||||
printk(KERN_WARNING "short read in get_inode_nodes()\n");
|
||||
err = -EIO;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
switch (je16_to_cpu(node.u.nodetype)) {
|
||||
case JFFS2_NODETYPE_DIRENT:
|
||||
D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)));
|
||||
if (ref_flags(ref) == REF_UNCHECKED) {
|
||||
printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref));
|
||||
BUG();
|
||||
}
|
||||
if (retlen < sizeof(node.d)) {
|
||||
printk(KERN_WARNING "short read in get_inode_nodes()\n");
|
||||
err = -EIO;
|
||||
goto free_out;
|
||||
}
|
||||
/* sanity check */
|
||||
if (PAD((node.d.nsize + sizeof (node.d))) != PAD(je32_to_cpu (node.d.totlen))) {
|
||||
printk(KERN_NOTICE "jffs2_get_inode_nodes(): Illegal nsize in node at 0x%08x: nsize 0x%02x, totlen %04x\n",
|
||||
ref_offset(ref), node.d.nsize, je32_to_cpu(node.d.totlen));
|
||||
jffs2_mark_node_obsolete(c, ref);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
continue;
|
||||
}
|
||||
if (je32_to_cpu(node.d.version) > *highest_version)
|
||||
*highest_version = je32_to_cpu(node.d.version);
|
||||
if (ref_obsolete(ref)) {
|
||||
/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
|
||||
printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n",
|
||||
ref_offset(ref));
|
||||
BUG();
|
||||
}
|
||||
|
||||
fd = jffs2_alloc_full_dirent(node.d.nsize+1);
|
||||
if (!fd) {
|
||||
err = -ENOMEM;
|
||||
goto free_out;
|
||||
}
|
||||
fd->raw = ref;
|
||||
fd->version = je32_to_cpu(node.d.version);
|
||||
fd->ino = je32_to_cpu(node.d.ino);
|
||||
fd->type = node.d.type;
|
||||
|
||||
/* Pick out the mctime of the latest dirent */
|
||||
if(fd->version > *mctime_ver) {
|
||||
*mctime_ver = fd->version;
|
||||
*latest_mctime = je32_to_cpu(node.d.mctime);
|
||||
}
|
||||
|
||||
/* memcpy as much of the name as possible from the raw
|
||||
dirent we've already read from the flash
|
||||
*/
|
||||
if (retlen > sizeof(struct jffs2_raw_dirent))
|
||||
memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
|
||||
|
||||
/* Do we need to copy any more of the name directly
|
||||
from the flash?
|
||||
*/
|
||||
if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) {
|
||||
/* FIXME: point() */
|
||||
int already = retlen - sizeof(struct jffs2_raw_dirent);
|
||||
|
||||
err = jffs2_flash_read(c, (ref_offset(ref)) + retlen,
|
||||
node.d.nsize - already, &retlen, &fd->name[already]);
|
||||
if (!err && retlen != node.d.nsize - already)
|
||||
err = -EIO;
|
||||
|
||||
if (err) {
|
||||
printk(KERN_WARNING "Read remainder of name in jffs2_get_inode_nodes(): error %d\n", err);
|
||||
jffs2_free_full_dirent(fd);
|
||||
goto free_out;
|
||||
}
|
||||
}
|
||||
fd->nhash = full_name_hash(fd->name, node.d.nsize);
|
||||
fd->next = NULL;
|
||||
fd->name[node.d.nsize] = '\0';
|
||||
/* Wheee. We now have a complete jffs2_full_dirent structure, with
|
||||
the name in it and everything. Link it into the list
|
||||
*/
|
||||
D1(printk(KERN_DEBUG "Adding fd \"%s\", ino #%u\n", fd->name, fd->ino));
|
||||
jffs2_add_fd_to_list(c, fd, &ret_fd);
|
||||
break;
|
||||
|
||||
case JFFS2_NODETYPE_INODE:
|
||||
D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)));
|
||||
if (retlen < sizeof(node.i)) {
|
||||
printk(KERN_WARNING "read too short for dnode\n");
|
||||
err = -EIO;
|
||||
goto free_out;
|
||||
}
|
||||
if (je32_to_cpu(node.i.version) > *highest_version)
|
||||
*highest_version = je32_to_cpu(node.i.version);
|
||||
D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version));
|
||||
|
||||
if (ref_obsolete(ref)) {
|
||||
/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
|
||||
printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n",
|
||||
ref_offset(ref));
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* If we've never checked the CRCs on this node, check them now. */
|
||||
if (ref_flags(ref) == REF_UNCHECKED) {
|
||||
uint32_t crc, len;
|
||||
struct jffs2_eraseblock *jeb;
|
||||
|
||||
crc = crc32(0, &node, sizeof(node.i)-8);
|
||||
if (crc != je32_to_cpu(node.i.node_crc)) {
|
||||
printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
|
||||
ref_offset(ref), je32_to_cpu(node.i.node_crc), crc);
|
||||
jffs2_mark_node_obsolete(c, ref);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* sanity checks */
|
||||
if ( je32_to_cpu(node.i.offset) > je32_to_cpu(node.i.isize) ||
|
||||
PAD(je32_to_cpu(node.i.csize) + sizeof (node.i)) != PAD(je32_to_cpu(node.i.totlen))) {
|
||||
printk(KERN_NOTICE "jffs2_get_inode_nodes(): Inode corrupted at 0x%08x, totlen %d, #ino %d, version %d, isize %d, csize %d, dsize %d \n",
|
||||
ref_offset(ref), je32_to_cpu(node.i.totlen), je32_to_cpu(node.i.ino),
|
||||
je32_to_cpu(node.i.version), je32_to_cpu(node.i.isize),
|
||||
je32_to_cpu(node.i.csize), je32_to_cpu(node.i.dsize));
|
||||
jffs2_mark_node_obsolete(c, ref);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) {
|
||||
unsigned char *buf=NULL;
|
||||
uint32_t pointed = 0;
|
||||
#ifndef __ECOS
|
||||
if (c->mtd->point) {
|
||||
err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
|
||||
&retlen, &buf);
|
||||
if (!err && retlen < je32_to_cpu(node.i.csize)) {
|
||||
D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
|
||||
c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
|
||||
} else if (err){
|
||||
D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
|
||||
} else
|
||||
pointed = 1; /* succefully pointed to device */
|
||||
}
|
||||
#endif
|
||||
if(!pointed){
|
||||
buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
|
||||
&retlen, buf);
|
||||
if (!err && retlen != je32_to_cpu(node.i.csize))
|
||||
err = -EIO;
|
||||
if (err) {
|
||||
kfree(buf);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
crc = crc32(0, buf, je32_to_cpu(node.i.csize));
|
||||
if(!pointed)
|
||||
kfree(buf);
|
||||
#ifndef __ECOS
|
||||
else
|
||||
c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
|
||||
#endif
|
||||
|
||||
if (crc != je32_to_cpu(node.i.data_crc)) {
|
||||
printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
|
||||
ref_offset(ref), je32_to_cpu(node.i.data_crc), crc);
|
||||
jffs2_mark_node_obsolete(c, ref);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Mark the node as having been checked and fix the accounting accordingly */
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
jeb = &c->blocks[ref->flash_offset / c->sector_size];
|
||||
len = ref_totlen(c, jeb, ref);
|
||||
|
||||
jeb->used_size += len;
|
||||
jeb->unchecked_size -= len;
|
||||
c->used_size += len;
|
||||
c->unchecked_size -= len;
|
||||
|
||||
/* If node covers at least a whole page, or if it starts at the
|
||||
beginning of a page and runs to the end of the file, or if
|
||||
it's a hole node, mark it REF_PRISTINE, else REF_NORMAL.
|
||||
|
||||
If it's actually overlapped, it'll get made NORMAL (or OBSOLETE)
|
||||
when the overlapping node(s) get added to the tree anyway.
|
||||
*/
|
||||
if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) ||
|
||||
( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) &&
|
||||
(je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) == je32_to_cpu(node.i.isize)))) {
|
||||
D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref)));
|
||||
ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref)));
|
||||
ref->flash_offset = ref_offset(ref) | REF_NORMAL;
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
tn = jffs2_alloc_tmp_dnode_info();
|
||||
if (!tn) {
|
||||
D1(printk(KERN_DEBUG "alloc tn failed\n"));
|
||||
err = -ENOMEM;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
tn->fn = jffs2_alloc_full_dnode();
|
||||
if (!tn->fn) {
|
||||
D1(printk(KERN_DEBUG "alloc fn failed\n"));
|
||||
err = -ENOMEM;
|
||||
jffs2_free_tmp_dnode_info(tn);
|
||||
goto free_out;
|
||||
}
|
||||
tn->version = je32_to_cpu(node.i.version);
|
||||
tn->fn->ofs = je32_to_cpu(node.i.offset);
|
||||
/* There was a bug where we wrote hole nodes out with
|
||||
csize/dsize swapped. Deal with it */
|
||||
if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize))
|
||||
tn->fn->size = je32_to_cpu(node.i.csize);
|
||||
else // normal case...
|
||||
tn->fn->size = je32_to_cpu(node.i.dsize);
|
||||
tn->fn->raw = ref;
|
||||
D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n",
|
||||
ref_offset(ref), je32_to_cpu(node.i.version),
|
||||
je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize)));
|
||||
jffs2_add_tn_to_list(tn, &ret_tn);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ref_flags(ref) == REF_UNCHECKED) {
|
||||
struct jffs2_eraseblock *jeb;
|
||||
uint32_t len;
|
||||
|
||||
printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
|
||||
je16_to_cpu(node.u.nodetype), ref_offset(ref));
|
||||
|
||||
/* Mark the node as having been checked and fix the accounting accordingly */
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
jeb = &c->blocks[ref->flash_offset / c->sector_size];
|
||||
len = ref_totlen(c, jeb, ref);
|
||||
|
||||
jeb->used_size += len;
|
||||
jeb->unchecked_size -= len;
|
||||
c->used_size += len;
|
||||
c->unchecked_size -= len;
|
||||
|
||||
mark_ref_normal(ref);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype));
|
||||
if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) {
|
||||
/* Hmmm. This should have been caught at scan time. */
|
||||
printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n",
|
||||
ref_offset(ref));
|
||||
printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n",
|
||||
je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen),
|
||||
je32_to_cpu(node.u.hdr_crc));
|
||||
jffs2_mark_node_obsolete(c, ref);
|
||||
} else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) {
|
||||
case JFFS2_FEATURE_INCOMPAT:
|
||||
printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
|
||||
/* EEP */
|
||||
BUG();
|
||||
break;
|
||||
case JFFS2_FEATURE_ROCOMPAT:
|
||||
printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
|
||||
if (!(c->flags & JFFS2_SB_FLAG_RO))
|
||||
BUG();
|
||||
break;
|
||||
case JFFS2_FEATURE_RWCOMPAT_COPY:
|
||||
printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
|
||||
break;
|
||||
case JFFS2_FEATURE_RWCOMPAT_DELETE:
|
||||
printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
|
||||
jffs2_mark_node_obsolete(c, ref);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
*tnp = ret_tn;
|
||||
*fdp = ret_fd;
|
||||
|
||||
return 0;
|
||||
|
||||
free_out:
|
||||
jffs2_free_tmp_dnode_info_list(ret_tn);
|
||||
jffs2_free_full_dirent_list(ret_fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
|
||||
{
|
||||
spin_lock(&c->inocache_lock);
|
||||
ic->state = state;
|
||||
wake_up(&c->inocache_wq);
|
||||
spin_unlock(&c->inocache_lock);
|
||||
}
|
||||
|
||||
/* During mount, this needs no locking. During normal operation, its
|
||||
callers want to do other stuff while still holding the inocache_lock.
|
||||
Rather than introducing special case get_ino_cache functions or
|
||||
callbacks, we just let the caller do the locking itself. */
|
||||
|
||||
struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
|
||||
{
|
||||
struct jffs2_inode_cache *ret;
|
||||
|
||||
D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
|
||||
|
||||
ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
|
||||
while (ret && ret->ino < ino) {
|
||||
ret = ret->next;
|
||||
}
|
||||
|
||||
if (ret && ret->ino != ino)
|
||||
ret = NULL;
|
||||
|
||||
D2(printk(KERN_DEBUG "jffs2_get_ino_cache found %p for ino %u\n", ret, ino));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new)
|
||||
{
|
||||
struct jffs2_inode_cache **prev;
|
||||
D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
|
||||
spin_lock(&c->inocache_lock);
|
||||
|
||||
prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
|
||||
|
||||
while ((*prev) && (*prev)->ino < new->ino) {
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
new->next = *prev;
|
||||
*prev = new;
|
||||
|
||||
spin_unlock(&c->inocache_lock);
|
||||
}
|
||||
|
||||
void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
|
||||
{
|
||||
struct jffs2_inode_cache **prev;
|
||||
D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
|
||||
spin_lock(&c->inocache_lock);
|
||||
|
||||
prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
|
||||
|
||||
while ((*prev) && (*prev)->ino < old->ino) {
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
if ((*prev) == old) {
|
||||
*prev = old->next;
|
||||
}
|
||||
|
||||
spin_unlock(&c->inocache_lock);
|
||||
}
|
||||
|
||||
void jffs2_free_ino_caches(struct jffs2_sb_info *c)
|
||||
{
|
||||
int i;
|
||||
struct jffs2_inode_cache *this, *next;
|
||||
|
||||
for (i=0; i<INOCACHE_HASHSIZE; i++) {
|
||||
this = c->inocache_list[i];
|
||||
while (this) {
|
||||
next = this->next;
|
||||
D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
|
||||
jffs2_free_inode_cache(this);
|
||||
this = next;
|
||||
}
|
||||
c->inocache_list[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void jffs2_free_raw_node_refs(struct jffs2_sb_info *c)
|
||||
{
|
||||
int i;
|
||||
struct jffs2_raw_node_ref *this, *next;
|
||||
|
||||
for (i=0; i<c->nr_blocks; i++) {
|
||||
this = c->blocks[i].first_node;
|
||||
while(this) {
|
||||
next = this->next_phys;
|
||||
jffs2_free_raw_node_ref(this);
|
||||
this = next;
|
||||
}
|
||||
c->blocks[i].first_node = c->blocks[i].last_node = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset)
|
||||
{
|
||||
/* The common case in lookup is that there will be a node
|
||||
which precisely matches. So we go looking for that first */
|
||||
struct rb_node *next;
|
||||
struct jffs2_node_frag *prev = NULL;
|
||||
struct jffs2_node_frag *frag = NULL;
|
||||
|
||||
D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset));
|
||||
|
||||
next = fragtree->rb_node;
|
||||
|
||||
while(next) {
|
||||
frag = rb_entry(next, struct jffs2_node_frag, rb);
|
||||
|
||||
D2(printk(KERN_DEBUG "Considering frag %d-%d (%p). left %p, right %p\n",
|
||||
frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right));
|
||||
if (frag->ofs + frag->size <= offset) {
|
||||
D2(printk(KERN_DEBUG "Going right from frag %d-%d, before the region we care about\n",
|
||||
frag->ofs, frag->ofs+frag->size));
|
||||
/* Remember the closest smaller match on the way down */
|
||||
if (!prev || frag->ofs > prev->ofs)
|
||||
prev = frag;
|
||||
next = frag->rb.rb_right;
|
||||
} else if (frag->ofs > offset) {
|
||||
D2(printk(KERN_DEBUG "Going left from frag %d-%d, after the region we care about\n",
|
||||
frag->ofs, frag->ofs+frag->size));
|
||||
next = frag->rb.rb_left;
|
||||
} else {
|
||||
D2(printk(KERN_DEBUG "Returning frag %d,%d, matched\n",
|
||||
frag->ofs, frag->ofs+frag->size));
|
||||
return frag;
|
||||
}
|
||||
}
|
||||
|
||||
/* Exact match not found. Go back up looking at each parent,
|
||||
and return the closest smaller one */
|
||||
|
||||
if (prev)
|
||||
D2(printk(KERN_DEBUG "No match. Returning frag %d,%d, closest previous\n",
|
||||
prev->ofs, prev->ofs+prev->size));
|
||||
else
|
||||
D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n"));
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
/* Pass 'c' argument to indicate that nodes should be marked obsolete as
|
||||
they're killed. */
|
||||
void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c)
|
||||
{
|
||||
struct jffs2_node_frag *frag;
|
||||
struct jffs2_node_frag *parent;
|
||||
|
||||
if (!root->rb_node)
|
||||
return;
|
||||
|
||||
frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
|
||||
|
||||
while(frag) {
|
||||
if (frag->rb.rb_left) {
|
||||
D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n",
|
||||
frag, frag->ofs, frag->ofs+frag->size));
|
||||
frag = frag_left(frag);
|
||||
continue;
|
||||
}
|
||||
if (frag->rb.rb_right) {
|
||||
D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n",
|
||||
frag, frag->ofs, frag->ofs+frag->size));
|
||||
frag = frag_right(frag);
|
||||
continue;
|
||||
}
|
||||
|
||||
D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n",
|
||||
frag->ofs, frag->ofs+frag->size, frag->node,
|
||||
frag->node?frag->node->frags:0));
|
||||
|
||||
if (frag->node && !(--frag->node->frags)) {
|
||||
/* Not a hole, and it's the final remaining frag
|
||||
of this node. Free the node */
|
||||
if (c)
|
||||
jffs2_mark_node_obsolete(c, frag->node->raw);
|
||||
|
||||
jffs2_free_full_dnode(frag->node);
|
||||
}
|
||||
parent = frag_parent(frag);
|
||||
if (parent) {
|
||||
if (frag_left(parent) == frag)
|
||||
parent->rb.rb_left = NULL;
|
||||
else
|
||||
parent->rb.rb_right = NULL;
|
||||
}
|
||||
|
||||
jffs2_free_node_frag(frag);
|
||||
frag = parent;
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base)
|
||||
{
|
||||
struct rb_node *parent = &base->rb;
|
||||
struct rb_node **link = &parent;
|
||||
|
||||
D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag,
|
||||
newfrag->ofs, newfrag->ofs+newfrag->size, base));
|
||||
|
||||
while (*link) {
|
||||
parent = *link;
|
||||
base = rb_entry(parent, struct jffs2_node_frag, rb);
|
||||
|
||||
D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs));
|
||||
if (newfrag->ofs > base->ofs)
|
||||
link = &base->rb.rb_right;
|
||||
else if (newfrag->ofs < base->ofs)
|
||||
link = &base->rb.rb_left;
|
||||
else {
|
||||
printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
rb_link_node(&newfrag->rb, &base->rb, link);
|
||||
}
|
473
fs/jffs2/nodelist.h
Normální soubor
473
fs/jffs2/nodelist.h
Normální soubor
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* 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: nodelist.h,v 1.126 2004/11/19 15:06:29 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __JFFS2_NODELIST_H__
|
||||
#define __JFFS2_NODELIST_H__
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/jffs2_fs_sb.h>
|
||||
#include <linux/jffs2_fs_i.h>
|
||||
|
||||
#ifdef __ECOS
|
||||
#include "os-ecos.h"
|
||||
#else
|
||||
#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
|
||||
#include "os-linux.h"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_JFFS2_FS_DEBUG
|
||||
#define CONFIG_JFFS2_FS_DEBUG 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_JFFS2_FS_DEBUG > 0
|
||||
#define D1(x) x
|
||||
#else
|
||||
#define D1(x)
|
||||
#endif
|
||||
|
||||
#if CONFIG_JFFS2_FS_DEBUG > 1
|
||||
#define D2(x) x
|
||||
#else
|
||||
#define D2(x)
|
||||
#endif
|
||||
|
||||
#define JFFS2_NATIVE_ENDIAN
|
||||
|
||||
/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
|
||||
whatever OS we're actually running on here too. */
|
||||
|
||||
#if defined(JFFS2_NATIVE_ENDIAN)
|
||||
#define cpu_to_je16(x) ((jint16_t){x})
|
||||
#define cpu_to_je32(x) ((jint32_t){x})
|
||||
#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
|
||||
|
||||
#define je16_to_cpu(x) ((x).v16)
|
||||
#define je32_to_cpu(x) ((x).v32)
|
||||
#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
|
||||
#elif defined(JFFS2_BIG_ENDIAN)
|
||||
#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)})
|
||||
#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
|
||||
#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
|
||||
|
||||
#define je16_to_cpu(x) (be16_to_cpu(x.v16))
|
||||
#define je32_to_cpu(x) (be32_to_cpu(x.v32))
|
||||
#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
|
||||
#elif defined(JFFS2_LITTLE_ENDIAN)
|
||||
#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)})
|
||||
#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
|
||||
#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
|
||||
|
||||
#define je16_to_cpu(x) (le16_to_cpu(x.v16))
|
||||
#define je32_to_cpu(x) (le32_to_cpu(x.v32))
|
||||
#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
|
||||
#else
|
||||
#error wibble
|
||||
#endif
|
||||
|
||||
/*
|
||||
This is all we need to keep in-core for each raw node during normal
|
||||
operation. As and when we do read_inode on a particular inode, we can
|
||||
scan the nodes which are listed for it and build up a proper map of
|
||||
which nodes are currently valid. JFFSv1 always used to keep that whole
|
||||
map in core for each inode.
|
||||
*/
|
||||
struct jffs2_raw_node_ref
|
||||
{
|
||||
struct jffs2_raw_node_ref *next_in_ino; /* Points to the next raw_node_ref
|
||||
for this inode. If this is the last, it points to the inode_cache
|
||||
for this inode instead. The inode_cache will have NULL in the first
|
||||
word so you know when you've got there :) */
|
||||
struct jffs2_raw_node_ref *next_phys;
|
||||
uint32_t flash_offset;
|
||||
uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
|
||||
};
|
||||
|
||||
/* 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 */
|
||||
#define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */
|
||||
#define REF_PRISTINE 2 /* Completely clean. GC without looking */
|
||||
#define REF_NORMAL 3 /* Possibly overlapped. Read the page and write again on GC */
|
||||
#define ref_flags(ref) ((ref)->flash_offset & 3)
|
||||
#define ref_offset(ref) ((ref)->flash_offset & ~3)
|
||||
#define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE)
|
||||
#define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
|
||||
|
||||
/* For each inode in the filesystem, we need to keep a record of
|
||||
nlink, because it would be a PITA to scan the whole directory tree
|
||||
at read_inode() time to calculate it, and to keep sufficient information
|
||||
in the raw_node_ref (basically both parent and child inode number for
|
||||
dirent nodes) would take more space than this does. We also keep
|
||||
a pointer to the first physical node which is part of this inode, too.
|
||||
*/
|
||||
struct jffs2_inode_cache {
|
||||
struct jffs2_full_dirent *scan_dents; /* Used during scan to hold
|
||||
temporary lists of dirents, and later must be set to
|
||||
NULL to mark the end of the raw_node_ref->next_in_ino
|
||||
chain. */
|
||||
struct jffs2_inode_cache *next;
|
||||
struct jffs2_raw_node_ref *nodes;
|
||||
uint32_t ino;
|
||||
int nlink;
|
||||
int state;
|
||||
};
|
||||
|
||||
/* Inode states for 'state' above. We need the 'GC' state to prevent
|
||||
someone from doing a read_inode() while we're moving a 'REF_PRISTINE'
|
||||
node without going through all the iget() nonsense */
|
||||
#define INO_STATE_UNCHECKED 0 /* CRC checks not yet done */
|
||||
#define INO_STATE_CHECKING 1 /* CRC checks in progress */
|
||||
#define INO_STATE_PRESENT 2 /* In core */
|
||||
#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */
|
||||
#define INO_STATE_GC 4 /* GCing a 'pristine' node */
|
||||
#define INO_STATE_READING 5 /* In read_inode() */
|
||||
|
||||
#define INOCACHE_HASHSIZE 128
|
||||
|
||||
/*
|
||||
Larger representation of a raw node, kept in-core only when the
|
||||
struct inode for this particular ino is instantiated.
|
||||
*/
|
||||
|
||||
struct jffs2_full_dnode
|
||||
{
|
||||
struct jffs2_raw_node_ref *raw;
|
||||
uint32_t ofs; /* The offset to which the data of this node belongs */
|
||||
uint32_t size;
|
||||
uint32_t frags; /* Number of fragments which currently refer
|
||||
to this node. When this reaches zero,
|
||||
the node is obsolete. */
|
||||
};
|
||||
|
||||
/*
|
||||
Even larger representation of a raw node, kept in-core only while
|
||||
we're actually building up the original map of which nodes go where,
|
||||
in read_inode()
|
||||
*/
|
||||
struct jffs2_tmp_dnode_info
|
||||
{
|
||||
struct jffs2_tmp_dnode_info *next;
|
||||
struct jffs2_full_dnode *fn;
|
||||
uint32_t version;
|
||||
};
|
||||
|
||||
struct jffs2_full_dirent
|
||||
{
|
||||
struct jffs2_raw_node_ref *raw;
|
||||
struct jffs2_full_dirent *next;
|
||||
uint32_t version;
|
||||
uint32_t ino; /* == zero for unlink */
|
||||
unsigned int nhash;
|
||||
unsigned char type;
|
||||
unsigned char name[0];
|
||||
};
|
||||
|
||||
/*
|
||||
Fragments - used to build a map of which raw node to obtain
|
||||
data from for each part of the ino
|
||||
*/
|
||||
struct jffs2_node_frag
|
||||
{
|
||||
struct rb_node rb;
|
||||
struct jffs2_full_dnode *node; /* NULL for holes */
|
||||
uint32_t size;
|
||||
uint32_t ofs; /* The offset to which this fragment belongs */
|
||||
};
|
||||
|
||||
struct jffs2_eraseblock
|
||||
{
|
||||
struct list_head list;
|
||||
int bad_count;
|
||||
uint32_t offset; /* of this block in the MTD */
|
||||
|
||||
uint32_t unchecked_size;
|
||||
uint32_t used_size;
|
||||
uint32_t dirty_size;
|
||||
uint32_t wasted_size;
|
||||
uint32_t free_size; /* Note that sector_size - free_size
|
||||
is the address of the first free space */
|
||||
struct jffs2_raw_node_ref *first_node;
|
||||
struct jffs2_raw_node_ref *last_node;
|
||||
|
||||
struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */
|
||||
};
|
||||
|
||||
#define ACCT_SANITY_CHECK(c, jeb) do { \
|
||||
struct jffs2_eraseblock *___j = jeb; \
|
||||
if ((___j) && ___j->used_size + ___j->dirty_size + ___j->free_size + ___j->wasted_size + ___j->unchecked_size != c->sector_size) { \
|
||||
printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", ___j->offset); \
|
||||
printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \
|
||||
___j->free_size, ___j->dirty_size, ___j->used_size, ___j->wasted_size, ___j->unchecked_size, c->sector_size); \
|
||||
BUG(); \
|
||||
} \
|
||||
if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \
|
||||
printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \
|
||||
printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x + wasted %08x + unchecked %08x != total %08x\n", \
|
||||
c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \
|
||||
BUG(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
struct jffs2_raw_node_ref *ref;
|
||||
int i=0;
|
||||
|
||||
printk(KERN_NOTICE);
|
||||
for (ref = jeb->first_node; ref; ref = ref->next_phys) {
|
||||
printk("%08x->", ref_offset(ref));
|
||||
if (++i == 8) {
|
||||
i = 0;
|
||||
printk("\n" KERN_NOTICE);
|
||||
}
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
|
||||
#define ACCT_PARANOIA_CHECK(jeb) do { \
|
||||
uint32_t my_used_size = 0; \
|
||||
uint32_t my_unchecked_size = 0; \
|
||||
struct jffs2_raw_node_ref *ref2 = jeb->first_node; \
|
||||
while (ref2) { \
|
||||
if (unlikely(ref2->flash_offset < jeb->offset || \
|
||||
ref2->flash_offset > jeb->offset + c->sector_size)) { \
|
||||
printk(KERN_NOTICE "Node %08x shouldn't be in block at %08x!\n", \
|
||||
ref_offset(ref2), jeb->offset); \
|
||||
paranoia_failed_dump(jeb); \
|
||||
BUG(); \
|
||||
} \
|
||||
if (ref_flags(ref2) == REF_UNCHECKED) \
|
||||
my_unchecked_size += ref_totlen(c, jeb, ref2); \
|
||||
else if (!ref_obsolete(ref2)) \
|
||||
my_used_size += ref_totlen(c, jeb, ref2); \
|
||||
if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \
|
||||
if (!ref2->next_phys) \
|
||||
printk("ref for node at %p (phys %08x) has next_phys->%p (----), last_node->%p (phys %08x)\n", \
|
||||
ref2, ref_offset(ref2), ref2->next_phys, \
|
||||
jeb->last_node, ref_offset(jeb->last_node)); \
|
||||
else \
|
||||
printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
|
||||
ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
|
||||
jeb->last_node, ref_offset(jeb->last_node)); \
|
||||
paranoia_failed_dump(jeb); \
|
||||
BUG(); \
|
||||
} \
|
||||
ref2 = ref2->next_phys; \
|
||||
} \
|
||||
if (my_used_size != jeb->used_size) { \
|
||||
printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \
|
||||
BUG(); \
|
||||
} \
|
||||
if (my_unchecked_size != jeb->unchecked_size) { \
|
||||
printk(KERN_NOTICE "Calculated unchecked size %08x != stored unchecked size %08x\n", my_unchecked_size, jeb->unchecked_size); \
|
||||
BUG(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* Calculate totlen from surrounding nodes or eraseblock */
|
||||
static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_node_ref *ref)
|
||||
{
|
||||
uint32_t ref_end;
|
||||
|
||||
if (ref->next_phys)
|
||||
ref_end = ref_offset(ref->next_phys);
|
||||
else {
|
||||
if (!jeb)
|
||||
jeb = &c->blocks[ref->flash_offset / c->sector_size];
|
||||
|
||||
/* Last node in block. Use free_space */
|
||||
BUG_ON(ref != jeb->last_node);
|
||||
ref_end = jeb->offset + c->sector_size - jeb->free_size;
|
||||
}
|
||||
return ref_end - ref_offset(ref);
|
||||
}
|
||||
|
||||
static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_node_ref *ref)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) {
|
||||
printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n",
|
||||
jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref));
|
||||
BUG();
|
||||
})
|
||||
|
||||
#if 1
|
||||
ret = ref->__totlen;
|
||||
#else
|
||||
/* This doesn't actually work yet */
|
||||
ret = __ref_totlen(c, jeb, ref);
|
||||
if (ret != ref->__totlen) {
|
||||
printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
|
||||
ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
|
||||
ret, ref->__totlen);
|
||||
if (!jeb)
|
||||
jeb = &c->blocks[ref->flash_offset / c->sector_size];
|
||||
paranoia_failed_dump(jeb);
|
||||
BUG();
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#define ALLOC_NORMAL 0 /* Normal allocation */
|
||||
#define ALLOC_DELETION 1 /* Deletion node. Best to allow it */
|
||||
#define ALLOC_GC 2 /* Space requested for GC. Give it or die */
|
||||
#define ALLOC_NORETRY 3 /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */
|
||||
|
||||
/* How much dirty space before it goes on the very_dirty_list */
|
||||
#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2))
|
||||
|
||||
/* check if dirty space is more than 255 Byte */
|
||||
#define ISDIRTY(size) ((size) > sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN)
|
||||
|
||||
#define PAD(x) (((x)+3)&~3)
|
||||
|
||||
static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
|
||||
{
|
||||
while(raw->next_in_ino) {
|
||||
raw = raw->next_in_ino;
|
||||
}
|
||||
|
||||
return ((struct jffs2_inode_cache *)raw);
|
||||
}
|
||||
|
||||
static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *node = root->rb_node;
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
while(node->rb_left)
|
||||
node = node->rb_left;
|
||||
return rb_entry(node, struct jffs2_node_frag, rb);
|
||||
}
|
||||
#define rb_parent(rb) ((rb)->rb_parent)
|
||||
#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb)
|
||||
#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb)
|
||||
#define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb)
|
||||
#define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb)
|
||||
#define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb)
|
||||
#define frag_erase(frag, list) rb_erase(&frag->rb, list);
|
||||
|
||||
/* nodelist.c */
|
||||
D2(void jffs2_print_frag_list(struct jffs2_inode_info *f));
|
||||
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
|
||||
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
|
||||
uint32_t *highest_version, uint32_t *latest_mctime,
|
||||
uint32_t *mctime_ver);
|
||||
void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
|
||||
struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
|
||||
void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
|
||||
void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old);
|
||||
void jffs2_free_ino_caches(struct jffs2_sb_info *c);
|
||||
void jffs2_free_raw_node_refs(struct jffs2_sb_info *c);
|
||||
struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset);
|
||||
void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete);
|
||||
void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base);
|
||||
struct rb_node *rb_next(struct rb_node *);
|
||||
struct rb_node *rb_prev(struct rb_node *);
|
||||
void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
|
||||
|
||||
/* nodemgmt.c */
|
||||
int jffs2_thread_should_wake(struct jffs2_sb_info *c);
|
||||
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio);
|
||||
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
|
||||
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
|
||||
void jffs2_complete_reservation(struct jffs2_sb_info *c);
|
||||
void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
|
||||
void jffs2_dump_block_lists(struct jffs2_sb_info *c);
|
||||
|
||||
/* write.c */
|
||||
int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
|
||||
|
||||
struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode);
|
||||
struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode);
|
||||
int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
struct jffs2_raw_inode *ri, unsigned char *buf,
|
||||
uint32_t offset, uint32_t writelen, uint32_t *retlen);
|
||||
int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen);
|
||||
int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f);
|
||||
int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen);
|
||||
|
||||
|
||||
/* readinode.c */
|
||||
void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
|
||||
int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
|
||||
int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
uint32_t ino, struct jffs2_raw_inode *latest_node);
|
||||
int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
|
||||
void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
|
||||
|
||||
/* malloc.c */
|
||||
int jffs2_create_slab_caches(void);
|
||||
void jffs2_destroy_slab_caches(void);
|
||||
|
||||
struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize);
|
||||
void jffs2_free_full_dirent(struct jffs2_full_dirent *);
|
||||
struct jffs2_full_dnode *jffs2_alloc_full_dnode(void);
|
||||
void jffs2_free_full_dnode(struct jffs2_full_dnode *);
|
||||
struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void);
|
||||
void jffs2_free_raw_dirent(struct jffs2_raw_dirent *);
|
||||
struct jffs2_raw_inode *jffs2_alloc_raw_inode(void);
|
||||
void jffs2_free_raw_inode(struct jffs2_raw_inode *);
|
||||
struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void);
|
||||
void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *);
|
||||
struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void);
|
||||
void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *);
|
||||
struct jffs2_node_frag *jffs2_alloc_node_frag(void);
|
||||
void jffs2_free_node_frag(struct jffs2_node_frag *);
|
||||
struct jffs2_inode_cache *jffs2_alloc_inode_cache(void);
|
||||
void jffs2_free_inode_cache(struct jffs2_inode_cache *);
|
||||
|
||||
/* gc.c */
|
||||
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
|
||||
|
||||
/* read.c */
|
||||
int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
struct jffs2_full_dnode *fd, unsigned char *buf,
|
||||
int ofs, int len);
|
||||
int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
unsigned char *buf, uint32_t offset, uint32_t len);
|
||||
char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
|
||||
|
||||
/* scan.c */
|
||||
int jffs2_scan_medium(struct jffs2_sb_info *c);
|
||||
void jffs2_rotate_lists(struct jffs2_sb_info *c);
|
||||
|
||||
/* build.c */
|
||||
int jffs2_do_mount_fs(struct jffs2_sb_info *c);
|
||||
|
||||
/* erase.c */
|
||||
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_NAND
|
||||
/* wbuf.c */
|
||||
int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
|
||||
int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
|
||||
int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
#endif
|
||||
|
||||
#endif /* __JFFS2_NODELIST_H__ */
|
838
fs/jffs2/nodemgmt.c
Normální soubor
838
fs/jffs2/nodemgmt.c
Normální soubor
@@ -0,0 +1,838 @@
|
||||
/*
|
||||
* 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: nodemgmt.c,v 1.115 2004/11/22 11:07:21 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/sched.h> /* For cond_resched() */
|
||||
#include "nodelist.h"
|
||||
|
||||
/**
|
||||
* jffs2_reserve_space - request physical space to write nodes to flash
|
||||
* @c: superblock info
|
||||
* @minsize: Minimum acceptable size of allocation
|
||||
* @ofs: Returned value of node offset
|
||||
* @len: Returned value of allocation length
|
||||
* @prio: Allocation type - ALLOC_{NORMAL,DELETION}
|
||||
*
|
||||
* Requests a block of physical space on the flash. Returns zero for success
|
||||
* and puts 'ofs' and 'len' into the appriopriate place, or returns -ENOSPC
|
||||
* or other error if appropriate.
|
||||
*
|
||||
* If it returns zero, jffs2_reserve_space() also downs the per-filesystem
|
||||
* allocation semaphore, to prevent more than one allocation from being
|
||||
* active at any time. The semaphore is later released by jffs2_commit_allocation()
|
||||
*
|
||||
* jffs2_reserve_space() may trigger garbage collection in order to make room
|
||||
* for the requested allocation.
|
||||
*/
|
||||
|
||||
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
|
||||
|
||||
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio)
|
||||
{
|
||||
int ret = -EAGAIN;
|
||||
int blocksneeded = c->resv_blocks_write;
|
||||
/* align it */
|
||||
minsize = PAD(minsize);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize));
|
||||
down(&c->alloc_sem);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
/* this needs a little more thought (true <tglx> :)) */
|
||||
while(ret == -EAGAIN) {
|
||||
while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
|
||||
int ret;
|
||||
uint32_t dirty, avail;
|
||||
|
||||
/* calculate real dirty size
|
||||
* dirty_size contains blocks on erase_pending_list
|
||||
* those blocks are counted in c->nr_erasing_blocks.
|
||||
* If one block is actually erased, it is not longer counted as dirty_space
|
||||
* but it is counted in c->nr_erasing_blocks, so we add it and subtract it
|
||||
* with c->nr_erasing_blocks * c->sector_size again.
|
||||
* Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
|
||||
* This helps us to force gc and pick eventually a clean block to spread the load.
|
||||
* We add unchecked_size here, as we hopefully will find some space to use.
|
||||
* This will affect the sum only once, as gc first finishes checking
|
||||
* of nodes.
|
||||
*/
|
||||
dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
|
||||
if (dirty < c->nospc_dirty_size) {
|
||||
if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
|
||||
printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n");
|
||||
break;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
|
||||
dirty, c->unchecked_size, c->sector_size));
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
up(&c->alloc_sem);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* Calc possibly available space. Possibly available means that we
|
||||
* don't know, if unchecked size contains obsoleted nodes, which could give us some
|
||||
* more usable space. This will affect the sum only once, as gc first finishes checking
|
||||
* of nodes.
|
||||
+ Return -ENOSPC, if the maximum possibly available space is less or equal than
|
||||
* blocksneeded * sector_size.
|
||||
* This blocks endless gc looping on a filesystem, which is nearly full, even if
|
||||
* the check above passes.
|
||||
*/
|
||||
avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
|
||||
if ( (avail / c->sector_size) <= blocksneeded) {
|
||||
if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
|
||||
printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n");
|
||||
break;
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n",
|
||||
avail, blocksneeded * c->sector_size));
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
up(&c->alloc_sem);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
up(&c->alloc_sem);
|
||||
|
||||
D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
|
||||
c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size,
|
||||
c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
ret = jffs2_garbage_collect_pass(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cond_resched();
|
||||
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
|
||||
down(&c->alloc_sem);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
ret = jffs2_do_reserve_space(c, minsize, ofs, len);
|
||||
if (ret) {
|
||||
D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
|
||||
}
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
if (ret)
|
||||
up(&c->alloc_sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
|
||||
{
|
||||
int ret = -EAGAIN;
|
||||
minsize = PAD(minsize);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize));
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
while(ret == -EAGAIN) {
|
||||
ret = jffs2_do_reserve_space(c, minsize, ofs, len);
|
||||
if (ret) {
|
||||
D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
|
||||
}
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Called with alloc sem _and_ erase_completion_lock */
|
||||
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
|
||||
{
|
||||
struct jffs2_eraseblock *jeb = c->nextblock;
|
||||
|
||||
restart:
|
||||
if (jeb && minsize > jeb->free_size) {
|
||||
/* Skip the end of this block and file it as having some dirty space */
|
||||
/* If there's a pending write to it, flush now */
|
||||
if (jffs2_wbuf_dirty(c)) {
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
|
||||
jffs2_flush_wbuf_pad(c);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
jeb = c->nextblock;
|
||||
goto restart;
|
||||
}
|
||||
c->wasted_size += jeb->free_size;
|
||||
c->free_size -= jeb->free_size;
|
||||
jeb->wasted_size += jeb->free_size;
|
||||
jeb->free_size = 0;
|
||||
|
||||
/* Check, if we have a dirty block now, or if it was dirty already */
|
||||
if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) {
|
||||
c->dirty_size += jeb->wasted_size;
|
||||
c->wasted_size -= jeb->wasted_size;
|
||||
jeb->dirty_size += jeb->wasted_size;
|
||||
jeb->wasted_size = 0;
|
||||
if (VERYDIRTY(c, jeb->dirty_size)) {
|
||||
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
|
||||
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
|
||||
list_add_tail(&jeb->list, &c->very_dirty_list);
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
|
||||
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
|
||||
list_add_tail(&jeb->list, &c->dirty_list);
|
||||
}
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
|
||||
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
|
||||
list_add_tail(&jeb->list, &c->clean_list);
|
||||
}
|
||||
c->nextblock = jeb = NULL;
|
||||
}
|
||||
|
||||
if (!jeb) {
|
||||
struct list_head *next;
|
||||
/* Take the next block off the 'free' list */
|
||||
|
||||
if (list_empty(&c->free_list)) {
|
||||
|
||||
if (!c->nr_erasing_blocks &&
|
||||
!list_empty(&c->erasable_list)) {
|
||||
struct jffs2_eraseblock *ejeb;
|
||||
|
||||
ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list);
|
||||
list_del(&ejeb->list);
|
||||
list_add_tail(&ejeb->list, &c->erase_pending_list);
|
||||
c->nr_erasing_blocks++;
|
||||
jffs2_erase_pending_trigger(c);
|
||||
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n",
|
||||
ejeb->offset));
|
||||
}
|
||||
|
||||
if (!c->nr_erasing_blocks &&
|
||||
!list_empty(&c->erasable_pending_wbuf_list)) {
|
||||
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
|
||||
/* c->nextblock is NULL, no update to c->nextblock allowed */
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
jffs2_flush_wbuf_pad(c);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
/* Have another go. It'll be on the erasable_list now */
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (!c->nr_erasing_blocks) {
|
||||
/* Ouch. We're in GC, or we wouldn't have got here.
|
||||
And there's no space left. At all. */
|
||||
printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
|
||||
c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no",
|
||||
list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
/* Don't wait for it; just erase one right now */
|
||||
jffs2_erase_pending_blocks(c, 1);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
/* An erase may have failed, decreasing the
|
||||
amount of free space available. So we must
|
||||
restart from the beginning */
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
next = c->free_list.next;
|
||||
list_del(next);
|
||||
c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
|
||||
c->nr_free_blocks--;
|
||||
|
||||
if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
|
||||
printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
/* OK, jeb (==c->nextblock) is now pointing at a block which definitely has
|
||||
enough space */
|
||||
*ofs = jeb->offset + (c->sector_size - jeb->free_size);
|
||||
*len = jeb->free_size;
|
||||
|
||||
if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
|
||||
!jeb->first_node->next_in_ino) {
|
||||
/* Only node in it beforehand was a CLEANMARKER node (we think).
|
||||
So mark it obsolete now that there's going to be another node
|
||||
in the block. This will reduce used_size to zero but We've
|
||||
already set c->nextblock so that jffs2_mark_node_obsolete()
|
||||
won't try to refile it to the dirty_list.
|
||||
*/
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
jffs2_mark_node_obsolete(c, jeb->first_node);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* jffs2_add_physical_node_ref - add a physical node reference to the list
|
||||
* @c: superblock info
|
||||
* @new: new node reference to add
|
||||
* @len: length of this physical node
|
||||
* @dirty: dirty flag for new node
|
||||
*
|
||||
* Should only be used to report nodes for which space has been allocated
|
||||
* by jffs2_reserve_space.
|
||||
*
|
||||
* Must be called with the alloc_sem held.
|
||||
*/
|
||||
|
||||
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
|
||||
{
|
||||
struct jffs2_eraseblock *jeb;
|
||||
uint32_t len;
|
||||
|
||||
jeb = &c->blocks[new->flash_offset / c->sector_size];
|
||||
len = ref_totlen(c, jeb, new);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
|
||||
#if 1
|
||||
if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
|
||||
printk(KERN_WARNING "argh. node added in wrong place\n");
|
||||
jffs2_free_raw_node_ref(new);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
if (!jeb->first_node)
|
||||
jeb->first_node = new;
|
||||
if (jeb->last_node)
|
||||
jeb->last_node->next_phys = new;
|
||||
jeb->last_node = new;
|
||||
|
||||
jeb->free_size -= len;
|
||||
c->free_size -= len;
|
||||
if (ref_obsolete(new)) {
|
||||
jeb->dirty_size += len;
|
||||
c->dirty_size += len;
|
||||
} else {
|
||||
jeb->used_size += len;
|
||||
c->used_size += len;
|
||||
}
|
||||
|
||||
if (!jeb->free_size && !jeb->dirty_size) {
|
||||
/* If it lives on the dirty_list, jffs2_reserve_space will put it there */
|
||||
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
|
||||
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
|
||||
if (jffs2_wbuf_dirty(c)) {
|
||||
/* Flush the last write in the block if it's outstanding */
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
jffs2_flush_wbuf_pad(c);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
list_add_tail(&jeb->list, &c->clean_list);
|
||||
c->nextblock = NULL;
|
||||
}
|
||||
ACCT_SANITY_CHECK(c,jeb);
|
||||
D1(ACCT_PARANOIA_CHECK(jeb));
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void jffs2_complete_reservation(struct jffs2_sb_info *c)
|
||||
{
|
||||
D1(printk(KERN_DEBUG "jffs2_complete_reservation()\n"));
|
||||
jffs2_garbage_collect_trigger(c);
|
||||
up(&c->alloc_sem);
|
||||
}
|
||||
|
||||
static inline int on_list(struct list_head *obj, struct list_head *head)
|
||||
{
|
||||
struct list_head *this;
|
||||
|
||||
list_for_each(this, head) {
|
||||
if (this == obj) {
|
||||
D1(printk("%p is on list at %p\n", obj, head));
|
||||
return 1;
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref)
|
||||
{
|
||||
struct jffs2_eraseblock *jeb;
|
||||
int blocknr;
|
||||
struct jffs2_unknown_node n;
|
||||
int ret, addedsize;
|
||||
size_t retlen;
|
||||
|
||||
if(!ref) {
|
||||
printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
|
||||
return;
|
||||
}
|
||||
if (ref_obsolete(ref)) {
|
||||
D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref)));
|
||||
return;
|
||||
}
|
||||
blocknr = ref->flash_offset / c->sector_size;
|
||||
if (blocknr >= c->nr_blocks) {
|
||||
printk(KERN_NOTICE "raw node at 0x%08x is off the end of device!\n", ref->flash_offset);
|
||||
BUG();
|
||||
}
|
||||
jeb = &c->blocks[blocknr];
|
||||
|
||||
if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) &&
|
||||
!(c->flags & JFFS2_SB_FLAG_MOUNTING)) {
|
||||
/* Hm. This may confuse static lock analysis. If any of the above
|
||||
three conditions is false, we're going to return from this
|
||||
function without actually obliterating any nodes or freeing
|
||||
any jffs2_raw_node_refs. So we don't need to stop erases from
|
||||
happening, or protect against people holding an obsolete
|
||||
jffs2_raw_node_ref without the erase_completion_lock. */
|
||||
down(&c->erase_free_sem);
|
||||
}
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
if (ref_flags(ref) == REF_UNCHECKED) {
|
||||
D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
|
||||
printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
|
||||
ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
|
||||
BUG();
|
||||
})
|
||||
D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
|
||||
jeb->unchecked_size -= ref_totlen(c, jeb, ref);
|
||||
c->unchecked_size -= ref_totlen(c, jeb, ref);
|
||||
} else {
|
||||
D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
|
||||
printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
|
||||
ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
|
||||
BUG();
|
||||
})
|
||||
D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
|
||||
jeb->used_size -= ref_totlen(c, jeb, ref);
|
||||
c->used_size -= ref_totlen(c, jeb, ref);
|
||||
}
|
||||
|
||||
// Take care, that wasted size is taken into concern
|
||||
if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
|
||||
D1(printk("Dirtying\n"));
|
||||
addedsize = ref_totlen(c, jeb, ref);
|
||||
jeb->dirty_size += ref_totlen(c, jeb, ref);
|
||||
c->dirty_size += ref_totlen(c, jeb, ref);
|
||||
|
||||
/* Convert wasted space to dirty, if not a bad block */
|
||||
if (jeb->wasted_size) {
|
||||
if (on_list(&jeb->list, &c->bad_used_list)) {
|
||||
D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n",
|
||||
jeb->offset));
|
||||
addedsize = 0; /* To fool the refiling code later */
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n",
|
||||
jeb->wasted_size, jeb->offset));
|
||||
addedsize += jeb->wasted_size;
|
||||
jeb->dirty_size += jeb->wasted_size;
|
||||
c->dirty_size += jeb->wasted_size;
|
||||
c->wasted_size -= jeb->wasted_size;
|
||||
jeb->wasted_size = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
D1(printk("Wasting\n"));
|
||||
addedsize = 0;
|
||||
jeb->wasted_size += ref_totlen(c, jeb, ref);
|
||||
c->wasted_size += ref_totlen(c, jeb, ref);
|
||||
}
|
||||
ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
|
||||
|
||||
ACCT_SANITY_CHECK(c, jeb);
|
||||
|
||||
D1(ACCT_PARANOIA_CHECK(jeb));
|
||||
|
||||
if (c->flags & JFFS2_SB_FLAG_MOUNTING) {
|
||||
/* Mount in progress. Don't muck about with the block
|
||||
lists because they're not ready yet, and don't actually
|
||||
obliterate nodes that look obsolete. If they weren't
|
||||
marked obsolete on the flash at the time they _became_
|
||||
obsolete, there was probably a reason for that. */
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
/* We didn't lock the erase_free_sem */
|
||||
return;
|
||||
}
|
||||
|
||||
if (jeb == c->nextblock) {
|
||||
D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
|
||||
} else if (!jeb->used_size && !jeb->unchecked_size) {
|
||||
if (jeb == c->gcblock) {
|
||||
D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset));
|
||||
c->gcblock = NULL;
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
|
||||
list_del(&jeb->list);
|
||||
}
|
||||
if (jffs2_wbuf_dirty(c)) {
|
||||
D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
|
||||
list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
|
||||
} else {
|
||||
if (jiffies & 127) {
|
||||
/* Most of the time, we just erase it immediately. Otherwise we
|
||||
spend ages scanning it on mount, etc. */
|
||||
D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
|
||||
list_add_tail(&jeb->list, &c->erase_pending_list);
|
||||
c->nr_erasing_blocks++;
|
||||
jffs2_erase_pending_trigger(c);
|
||||
} else {
|
||||
/* Sometimes, however, we leave it elsewhere so it doesn't get
|
||||
immediately reused, and we spread the load a bit. */
|
||||
D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
|
||||
list_add_tail(&jeb->list, &c->erasable_list);
|
||||
}
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Done OK\n"));
|
||||
} else if (jeb == c->gcblock) {
|
||||
D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset));
|
||||
} else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) {
|
||||
D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset));
|
||||
list_del(&jeb->list);
|
||||
D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
|
||||
list_add_tail(&jeb->list, &c->dirty_list);
|
||||
} else if (VERYDIRTY(c, jeb->dirty_size) &&
|
||||
!VERYDIRTY(c, jeb->dirty_size - addedsize)) {
|
||||
D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset));
|
||||
list_del(&jeb->list);
|
||||
D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n"));
|
||||
list_add_tail(&jeb->list, &c->very_dirty_list);
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n",
|
||||
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
|
||||
}
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c)) {
|
||||
/* We didn't lock the erase_free_sem */
|
||||
return;
|
||||
}
|
||||
|
||||
/* The erase_free_sem is locked, and has been since before we marked the node obsolete
|
||||
and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
|
||||
the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
|
||||
by jffs2_free_all_node_refs() in erase.c. Which is nice. */
|
||||
|
||||
D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
|
||||
ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
|
||||
goto out_erase_sem;
|
||||
}
|
||||
if (retlen != sizeof(n)) {
|
||||
printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
|
||||
goto out_erase_sem;
|
||||
}
|
||||
if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) {
|
||||
printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref));
|
||||
goto out_erase_sem;
|
||||
}
|
||||
if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
|
||||
D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
|
||||
goto out_erase_sem;
|
||||
}
|
||||
/* XXX FIXME: This is ugly now */
|
||||
n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE);
|
||||
ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
|
||||
goto out_erase_sem;
|
||||
}
|
||||
if (retlen != sizeof(n)) {
|
||||
printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
|
||||
goto out_erase_sem;
|
||||
}
|
||||
|
||||
/* Nodes which have been marked obsolete no longer need to be
|
||||
associated with any inode. Remove them from the per-inode list.
|
||||
|
||||
Note we can't do this for NAND at the moment because we need
|
||||
obsolete dirent nodes to stay on the lists, because of the
|
||||
horridness in jffs2_garbage_collect_deletion_dirent(). Also
|
||||
because we delete the inocache, and on NAND we need that to
|
||||
stay around until all the nodes are actually erased, in order
|
||||
to stop us from giving the same inode number to another newly
|
||||
created inode. */
|
||||
if (ref->next_in_ino) {
|
||||
struct jffs2_inode_cache *ic;
|
||||
struct jffs2_raw_node_ref **p;
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
ic = jffs2_raw_ref_to_ic(ref);
|
||||
for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
|
||||
;
|
||||
|
||||
*p = ref->next_in_ino;
|
||||
ref->next_in_ino = NULL;
|
||||
|
||||
if (ic->nodes == (void *)ic) {
|
||||
D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
|
||||
jffs2_del_ino_cache(c, ic);
|
||||
jffs2_free_inode_cache(ic);
|
||||
}
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
|
||||
/* Merge with the next node in the physical list, if there is one
|
||||
and if it's also obsolete and if it doesn't belong to any inode */
|
||||
if (ref->next_phys && ref_obsolete(ref->next_phys) &&
|
||||
!ref->next_phys->next_in_ino) {
|
||||
struct jffs2_raw_node_ref *n = ref->next_phys;
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
ref->__totlen += n->__totlen;
|
||||
ref->next_phys = n->next_phys;
|
||||
if (jeb->last_node == n) jeb->last_node = ref;
|
||||
if (jeb->gc_node == n) {
|
||||
/* gc will be happy continuing gc on this node */
|
||||
jeb->gc_node=ref;
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
jffs2_free_raw_node_ref(n);
|
||||
}
|
||||
|
||||
/* Also merge with the previous node in the list, if there is one
|
||||
and that one is obsolete */
|
||||
if (ref != jeb->first_node ) {
|
||||
struct jffs2_raw_node_ref *p = jeb->first_node;
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
while (p->next_phys != ref)
|
||||
p = p->next_phys;
|
||||
|
||||
if (ref_obsolete(p) && !ref->next_in_ino) {
|
||||
p->__totlen += ref->__totlen;
|
||||
if (jeb->last_node == ref) {
|
||||
jeb->last_node = p;
|
||||
}
|
||||
if (jeb->gc_node == ref) {
|
||||
/* gc will be happy continuing gc on this node */
|
||||
jeb->gc_node=p;
|
||||
}
|
||||
p->next_phys = ref->next_phys;
|
||||
jffs2_free_raw_node_ref(ref);
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
out_erase_sem:
|
||||
up(&c->erase_free_sem);
|
||||
}
|
||||
|
||||
#if CONFIG_JFFS2_FS_DEBUG >= 2
|
||||
void jffs2_dump_block_lists(struct jffs2_sb_info *c)
|
||||
{
|
||||
|
||||
|
||||
printk(KERN_DEBUG "jffs2_dump_block_lists:\n");
|
||||
printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
|
||||
printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
|
||||
printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
|
||||
printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size);
|
||||
printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size);
|
||||
printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
|
||||
printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
|
||||
printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
|
||||
printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
|
||||
printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * c->resv_blocks_write);
|
||||
|
||||
if (c->nextblock) {
|
||||
printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
|
||||
c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size);
|
||||
} else {
|
||||
printk(KERN_DEBUG "nextblock: NULL\n");
|
||||
}
|
||||
if (c->gcblock) {
|
||||
printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
|
||||
c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size);
|
||||
} else {
|
||||
printk(KERN_DEBUG "gcblock: NULL\n");
|
||||
}
|
||||
if (list_empty(&c->clean_list)) {
|
||||
printk(KERN_DEBUG "clean_list: empty\n");
|
||||
} else {
|
||||
struct list_head *this;
|
||||
int numblocks = 0;
|
||||
uint32_t dirty = 0;
|
||||
|
||||
list_for_each(this, &c->clean_list) {
|
||||
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
|
||||
numblocks ++;
|
||||
dirty += jeb->wasted_size;
|
||||
printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
|
||||
}
|
||||
printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks);
|
||||
}
|
||||
if (list_empty(&c->very_dirty_list)) {
|
||||
printk(KERN_DEBUG "very_dirty_list: empty\n");
|
||||
} else {
|
||||
struct list_head *this;
|
||||
int numblocks = 0;
|
||||
uint32_t dirty = 0;
|
||||
|
||||
list_for_each(this, &c->very_dirty_list) {
|
||||
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
|
||||
numblocks ++;
|
||||
dirty += jeb->dirty_size;
|
||||
printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
|
||||
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
|
||||
}
|
||||
printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
|
||||
numblocks, dirty, dirty / numblocks);
|
||||
}
|
||||
if (list_empty(&c->dirty_list)) {
|
||||
printk(KERN_DEBUG "dirty_list: empty\n");
|
||||
} else {
|
||||
struct list_head *this;
|
||||
int numblocks = 0;
|
||||
uint32_t dirty = 0;
|
||||
|
||||
list_for_each(this, &c->dirty_list) {
|
||||
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
|
||||
numblocks ++;
|
||||
dirty += jeb->dirty_size;
|
||||
printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
|
||||
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
|
||||
}
|
||||
printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
|
||||
numblocks, dirty, dirty / numblocks);
|
||||
}
|
||||
if (list_empty(&c->erasable_list)) {
|
||||
printk(KERN_DEBUG "erasable_list: empty\n");
|
||||
} else {
|
||||
struct list_head *this;
|
||||
|
||||
list_for_each(this, &c->erasable_list) {
|
||||
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
|
||||
printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
|
||||
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
|
||||
}
|
||||
}
|
||||
if (list_empty(&c->erasing_list)) {
|
||||
printk(KERN_DEBUG "erasing_list: empty\n");
|
||||
} else {
|
||||
struct list_head *this;
|
||||
|
||||
list_for_each(this, &c->erasing_list) {
|
||||
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
|
||||
printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
|
||||
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
|
||||
}
|
||||
}
|
||||
if (list_empty(&c->erase_pending_list)) {
|
||||
printk(KERN_DEBUG "erase_pending_list: empty\n");
|
||||
} else {
|
||||
struct list_head *this;
|
||||
|
||||
list_for_each(this, &c->erase_pending_list) {
|
||||
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
|
||||
printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
|
||||
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
|
||||
}
|
||||
}
|
||||
if (list_empty(&c->erasable_pending_wbuf_list)) {
|
||||
printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n");
|
||||
} else {
|
||||
struct list_head *this;
|
||||
|
||||
list_for_each(this, &c->erasable_pending_wbuf_list) {
|
||||
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
|
||||
printk(KERN_DEBUG "erasable_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
|
||||
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
|
||||
}
|
||||
}
|
||||
if (list_empty(&c->free_list)) {
|
||||
printk(KERN_DEBUG "free_list: empty\n");
|
||||
} else {
|
||||
struct list_head *this;
|
||||
|
||||
list_for_each(this, &c->free_list) {
|
||||
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
|
||||
printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
|
||||
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
|
||||
}
|
||||
}
|
||||
if (list_empty(&c->bad_list)) {
|
||||
printk(KERN_DEBUG "bad_list: empty\n");
|
||||
} else {
|
||||
struct list_head *this;
|
||||
|
||||
list_for_each(this, &c->bad_list) {
|
||||
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
|
||||
printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
|
||||
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
|
||||
}
|
||||
}
|
||||
if (list_empty(&c->bad_used_list)) {
|
||||
printk(KERN_DEBUG "bad_used_list: empty\n");
|
||||
} else {
|
||||
struct list_head *this;
|
||||
|
||||
list_for_each(this, &c->bad_used_list) {
|
||||
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
|
||||
printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
|
||||
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_JFFS2_FS_DEBUG */
|
||||
|
||||
int jffs2_thread_should_wake(struct jffs2_sb_info *c)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t dirty;
|
||||
|
||||
if (c->unchecked_size) {
|
||||
D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
|
||||
c->unchecked_size, c->checked_ino));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* dirty_size contains blocks on erase_pending_list
|
||||
* those blocks are counted in c->nr_erasing_blocks.
|
||||
* If one block is actually erased, it is not longer counted as dirty_space
|
||||
* but it is counted in c->nr_erasing_blocks, so we add it and subtract it
|
||||
* with c->nr_erasing_blocks * c->sector_size again.
|
||||
* Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
|
||||
* This helps us to force gc and pick eventually a clean block to spread the load.
|
||||
*/
|
||||
dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
|
||||
|
||||
if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
|
||||
(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"));
|
||||
|
||||
return ret;
|
||||
}
|
217
fs/jffs2/os-linux.h
Normální soubor
217
fs/jffs2/os-linux.h
Normální soubor
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2002-2003 Red Hat, Inc.
|
||||
*
|
||||
* Created by David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: os-linux.h,v 1.51 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __JFFS2_OS_LINUX_H__
|
||||
#define __JFFS2_OS_LINUX_H__
|
||||
#include <linux/version.h>
|
||||
|
||||
/* JFFS2 uses Linux mode bits natively -- no need for conversion */
|
||||
#define os_to_jffs2_mode(x) (x)
|
||||
#define jffs2_to_os_mode(x) (x)
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
|
||||
#define kstatfs statfs
|
||||
#endif
|
||||
|
||||
struct kstatfs;
|
||||
struct kvec;
|
||||
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
|
||||
#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
|
||||
#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode)
|
||||
#define JFFS2_SB_INFO(sb) (sb->s_fs_info)
|
||||
#define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv)
|
||||
#elif defined(JFFS2_OUT_OF_KERNEL)
|
||||
#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
|
||||
#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
|
||||
#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
|
||||
#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
|
||||
#else
|
||||
#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
|
||||
#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
|
||||
#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
|
||||
#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
|
||||
#endif
|
||||
|
||||
|
||||
#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size)
|
||||
#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
|
||||
#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
|
||||
#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
|
||||
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1)
|
||||
#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f)))
|
||||
#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f)))
|
||||
#else
|
||||
#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
|
||||
#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
|
||||
#endif
|
||||
|
||||
/* Urgh. The things we do to keep the 2.4 build working */
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47)
|
||||
#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(x) (x)
|
||||
#define I_SEC(x) (x)
|
||||
#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime)
|
||||
#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime)
|
||||
#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
|
||||
#endif
|
||||
|
||||
#define sleep_on_spinunlock(wq, s) \
|
||||
do { \
|
||||
DECLARE_WAITQUEUE(__wait, current); \
|
||||
add_wait_queue((wq), &__wait); \
|
||||
set_current_state(TASK_UNINTERRUPTIBLE); \
|
||||
spin_unlock(s); \
|
||||
schedule(); \
|
||||
remove_wait_queue((wq), &__wait); \
|
||||
} while(0)
|
||||
|
||||
static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
|
||||
{
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
|
||||
f->highest_version = 0;
|
||||
f->fragtree = RB_ROOT;
|
||||
f->metadata = NULL;
|
||||
f->dents = NULL;
|
||||
f->flags = 0;
|
||||
f->usercompr = 0;
|
||||
#else
|
||||
memset(f, 0, sizeof(*f));
|
||||
init_MUTEX_LOCKED(&f->sem);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
|
||||
|
||||
#if (!defined CONFIG_JFFS2_FS_NAND && !defined CONFIG_JFFS2_FS_NOR_ECC)
|
||||
#define jffs2_can_mark_obsolete(c) (1)
|
||||
#define jffs2_cleanmarker_oob(c) (0)
|
||||
#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
|
||||
|
||||
#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf))
|
||||
#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf))
|
||||
#define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; })
|
||||
#define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; })
|
||||
#define jffs2_write_nand_badblock(c,jeb,bad_offset) (1)
|
||||
#define jffs2_nand_flash_setup(c) (0)
|
||||
#define jffs2_nand_flash_cleanup(c) do {} while(0)
|
||||
#define jffs2_wbuf_dirty(c) (0)
|
||||
#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
|
||||
#define jffs2_wbuf_timeout NULL
|
||||
#define jffs2_wbuf_process NULL
|
||||
#define jffs2_nor_ecc(c) (0)
|
||||
#define jffs2_nor_ecc_flash_setup(c) (0)
|
||||
#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
|
||||
|
||||
#else /* NAND and/or ECC'd NOR support present */
|
||||
|
||||
#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM)
|
||||
#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
|
||||
|
||||
#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
|
||||
#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf))
|
||||
#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len)
|
||||
|
||||
/* wbuf.c */
|
||||
int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino);
|
||||
int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
|
||||
int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf);
|
||||
int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode);
|
||||
int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
|
||||
void jffs2_wbuf_timeout(unsigned long data);
|
||||
void jffs2_wbuf_process(void *data);
|
||||
int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
|
||||
int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
|
||||
int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
|
||||
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
|
||||
#ifdef CONFIG_JFFS2_FS_NOR_ECC
|
||||
#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
|
||||
int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
|
||||
void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
|
||||
#else
|
||||
#define jffs2_nor_ecc(c) (0)
|
||||
#define jffs2_nor_ecc_flash_setup(c) (0)
|
||||
#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
|
||||
#endif /* NOR ECC */
|
||||
#endif /* NAND */
|
||||
|
||||
/* erase.c */
|
||||
static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
|
||||
{
|
||||
OFNI_BS_2SFFJ(c)->s_dirt = 1;
|
||||
}
|
||||
|
||||
/* background.c */
|
||||
int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
|
||||
void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
|
||||
void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
|
||||
|
||||
/* dir.c */
|
||||
extern struct file_operations jffs2_dir_operations;
|
||||
extern struct inode_operations jffs2_dir_inode_operations;
|
||||
|
||||
/* file.c */
|
||||
extern struct file_operations jffs2_file_operations;
|
||||
extern struct inode_operations jffs2_file_inode_operations;
|
||||
extern struct address_space_operations jffs2_file_address_operations;
|
||||
int jffs2_fsync(struct file *, struct dentry *, int);
|
||||
int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
|
||||
|
||||
/* ioctl.c */
|
||||
int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
|
||||
|
||||
/* symlink.c */
|
||||
extern struct inode_operations jffs2_symlink_inode_operations;
|
||||
|
||||
/* fs.c */
|
||||
int jffs2_setattr (struct dentry *, 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);
|
||||
int jffs2_statfs (struct super_block *, struct kstatfs *);
|
||||
void jffs2_write_super (struct super_block *);
|
||||
int jffs2_remount_fs (struct super_block *, int *, char *);
|
||||
int jffs2_do_fill_super(struct super_block *sb, void *data, int silent);
|
||||
void jffs2_gc_release_inode(struct jffs2_sb_info *c,
|
||||
struct jffs2_inode_info *f);
|
||||
struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
|
||||
int inum, int nlink);
|
||||
|
||||
unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
|
||||
struct jffs2_inode_info *f,
|
||||
unsigned long offset,
|
||||
unsigned long *priv);
|
||||
void jffs2_gc_release_page(struct jffs2_sb_info *c,
|
||||
unsigned char *pg,
|
||||
unsigned long *priv);
|
||||
void jffs2_flash_cleanup(struct jffs2_sb_info *c);
|
||||
|
||||
|
||||
/* writev.c */
|
||||
int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen);
|
||||
|
||||
|
||||
#endif /* __JFFS2_OS_LINUX_H__ */
|
||||
|
||||
|
72
fs/jffs2/pushpull.h
Normální soubor
72
fs/jffs2/pushpull.h
Normální soubor
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2001, 2002 Red Hat, Inc.
|
||||
*
|
||||
* Created by David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __PUSHPULL_H__
|
||||
#define __PUSHPULL_H__
|
||||
|
||||
#include <linux/errno.h>
|
||||
|
||||
struct pushpull {
|
||||
unsigned char *buf;
|
||||
unsigned int buflen;
|
||||
unsigned int ofs;
|
||||
unsigned int reserve;
|
||||
};
|
||||
|
||||
|
||||
static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve)
|
||||
{
|
||||
pp->buf = buf;
|
||||
pp->buflen = buflen;
|
||||
pp->ofs = ofs;
|
||||
pp->reserve = reserve;
|
||||
}
|
||||
|
||||
static inline int pushbit(struct pushpull *pp, int bit, int use_reserved)
|
||||
{
|
||||
if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (bit) {
|
||||
pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7)));
|
||||
}
|
||||
else {
|
||||
pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7)));
|
||||
}
|
||||
pp->ofs++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int pushedbits(struct pushpull *pp)
|
||||
{
|
||||
return pp->ofs;
|
||||
}
|
||||
|
||||
static inline int pullbit(struct pushpull *pp)
|
||||
{
|
||||
int bit;
|
||||
|
||||
bit = (pp->buf[pp->ofs >> 3] >> (7-(pp->ofs & 7))) & 1;
|
||||
|
||||
pp->ofs++;
|
||||
return bit;
|
||||
}
|
||||
|
||||
static inline int pulledbits(struct pushpull *pp)
|
||||
{
|
||||
return pp->ofs;
|
||||
}
|
||||
|
||||
#endif /* __PUSHPULL_H__ */
|
246
fs/jffs2/read.c
Normální soubor
246
fs/jffs2/read.c
Normální soubor
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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: read.c,v 1.38 2004/11/16 20:36:12 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/compiler.h>
|
||||
#include "nodelist.h"
|
||||
#include "compr.h"
|
||||
|
||||
int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
struct jffs2_full_dnode *fd, unsigned char *buf,
|
||||
int ofs, int len)
|
||||
{
|
||||
struct jffs2_raw_inode *ri;
|
||||
size_t readlen;
|
||||
uint32_t crc;
|
||||
unsigned char *decomprbuf = NULL;
|
||||
unsigned char *readbuf = NULL;
|
||||
int ret = 0;
|
||||
|
||||
ri = jffs2_alloc_raw_inode();
|
||||
if (!ri)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri);
|
||||
if (ret) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret);
|
||||
return ret;
|
||||
}
|
||||
if (readlen != sizeof(*ri)) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n",
|
||||
ref_offset(fd->raw), sizeof(*ri), readlen);
|
||||
return -EIO;
|
||||
}
|
||||
crc = crc32(0, ri, sizeof(*ri)-8);
|
||||
|
||||
D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n",
|
||||
ref_offset(fd->raw), je32_to_cpu(ri->node_crc),
|
||||
crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize),
|
||||
je32_to_cpu(ri->offset), buf));
|
||||
if (crc != je32_to_cpu(ri->node_crc)) {
|
||||
printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n",
|
||||
je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw));
|
||||
ret = -EIO;
|
||||
goto out_ri;
|
||||
}
|
||||
/* There was a bug where we wrote hole nodes out with csize/dsize
|
||||
swapped. Deal with it */
|
||||
if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) &&
|
||||
je32_to_cpu(ri->csize)) {
|
||||
ri->dsize = ri->csize;
|
||||
ri->csize = cpu_to_je32(0);
|
||||
}
|
||||
|
||||
D1(if(ofs + len > je32_to_cpu(ri->dsize)) {
|
||||
printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n",
|
||||
len, ofs, je32_to_cpu(ri->dsize));
|
||||
ret = -EINVAL;
|
||||
goto out_ri;
|
||||
});
|
||||
|
||||
|
||||
if (ri->compr == JFFS2_COMPR_ZERO) {
|
||||
memset(buf, 0, len);
|
||||
goto out_ri;
|
||||
}
|
||||
|
||||
/* Cases:
|
||||
Reading whole node and it's uncompressed - read directly to buffer provided, check CRC.
|
||||
Reading whole node and it's compressed - read into comprbuf, check CRC and decompress to buffer provided
|
||||
Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy
|
||||
Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy
|
||||
*/
|
||||
if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) {
|
||||
readbuf = buf;
|
||||
} else {
|
||||
readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL);
|
||||
if (!readbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto out_ri;
|
||||
}
|
||||
}
|
||||
if (ri->compr != JFFS2_COMPR_NONE) {
|
||||
if (len < je32_to_cpu(ri->dsize)) {
|
||||
decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL);
|
||||
if (!decomprbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto out_readbuf;
|
||||
}
|
||||
} else {
|
||||
decomprbuf = buf;
|
||||
}
|
||||
} else {
|
||||
decomprbuf = readbuf;
|
||||
}
|
||||
|
||||
D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize),
|
||||
readbuf));
|
||||
ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri),
|
||||
je32_to_cpu(ri->csize), &readlen, readbuf);
|
||||
|
||||
if (!ret && readlen != je32_to_cpu(ri->csize))
|
||||
ret = -EIO;
|
||||
if (ret)
|
||||
goto out_decomprbuf;
|
||||
|
||||
crc = crc32(0, readbuf, je32_to_cpu(ri->csize));
|
||||
if (crc != je32_to_cpu(ri->data_crc)) {
|
||||
printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n",
|
||||
je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw));
|
||||
ret = -EIO;
|
||||
goto out_decomprbuf;
|
||||
}
|
||||
D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc));
|
||||
if (ri->compr != JFFS2_COMPR_NONE) {
|
||||
D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
|
||||
je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf));
|
||||
ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
|
||||
goto out_decomprbuf;
|
||||
}
|
||||
}
|
||||
|
||||
if (len < je32_to_cpu(ri->dsize)) {
|
||||
memcpy(buf, decomprbuf+ofs, len);
|
||||
}
|
||||
out_decomprbuf:
|
||||
if(decomprbuf != buf && decomprbuf != readbuf)
|
||||
kfree(decomprbuf);
|
||||
out_readbuf:
|
||||
if(readbuf != buf)
|
||||
kfree(readbuf);
|
||||
out_ri:
|
||||
jffs2_free_raw_inode(ri);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
unsigned char *buf, uint32_t offset, uint32_t len)
|
||||
{
|
||||
uint32_t end = offset + len;
|
||||
struct jffs2_node_frag *frag;
|
||||
int ret;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n",
|
||||
f->inocache->ino, offset, offset+len));
|
||||
|
||||
frag = jffs2_lookup_node_frag(&f->fragtree, offset);
|
||||
|
||||
/* XXX FIXME: Where a single physical node actually shows up in two
|
||||
frags, we read it twice. Don't do that. */
|
||||
/* Now we're pointing at the first frag which overlaps our page */
|
||||
while(offset < end) {
|
||||
D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end));
|
||||
if (unlikely(!frag || frag->ofs > offset)) {
|
||||
uint32_t holesize = end - offset;
|
||||
if (frag) {
|
||||
D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset));
|
||||
holesize = min(holesize, frag->ofs - offset);
|
||||
D2(jffs2_print_frag_list(f));
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
|
||||
memset(buf, 0, holesize);
|
||||
buf += holesize;
|
||||
offset += holesize;
|
||||
continue;
|
||||
} else if (unlikely(!frag->node)) {
|
||||
uint32_t holeend = min(end, frag->ofs + frag->size);
|
||||
D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
|
||||
memset(buf, 0, holeend - offset);
|
||||
buf += holeend - offset;
|
||||
offset = holeend;
|
||||
frag = frag_next(frag);
|
||||
continue;
|
||||
} else {
|
||||
uint32_t readlen;
|
||||
uint32_t fragofs; /* offset within the frag to start reading */
|
||||
|
||||
fragofs = offset - frag->ofs;
|
||||
readlen = min(frag->size - fragofs, end - offset);
|
||||
D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n",
|
||||
frag->ofs+fragofs, frag->ofs+fragofs+readlen,
|
||||
ref_offset(frag->node->raw), ref_flags(frag->node->raw)));
|
||||
ret = jffs2_read_dnode(c, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
|
||||
D2(printk(KERN_DEBUG "node read done\n"));
|
||||
if (ret) {
|
||||
D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret));
|
||||
memset(buf, 0, readlen);
|
||||
return ret;
|
||||
}
|
||||
buf += readlen;
|
||||
offset += readlen;
|
||||
frag = frag_next(frag);
|
||||
D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Core function to read symlink target. */
|
||||
char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
|
||||
{
|
||||
char *buf;
|
||||
int ret;
|
||||
|
||||
down(&f->sem);
|
||||
|
||||
if (!f->metadata) {
|
||||
printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino);
|
||||
up(&f->sem);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
buf = kmalloc(f->metadata->size+1, GFP_USER);
|
||||
if (!buf) {
|
||||
up(&f->sem);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
buf[f->metadata->size]=0;
|
||||
|
||||
ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size);
|
||||
|
||||
up(&f->sem);
|
||||
|
||||
if (ret) {
|
||||
kfree(buf);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
return buf;
|
||||
}
|
695
fs/jffs2/readinode.c
Normální soubor
695
fs/jffs2/readinode.c
Normální soubor
@@ -0,0 +1,695 @@
|
||||
/*
|
||||
* 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: readinode.c,v 1.117 2004/11/20 18:06:54 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/compiler.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
|
||||
|
||||
#if CONFIG_JFFS2_FS_DEBUG >= 2
|
||||
static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
|
||||
{
|
||||
struct jffs2_node_frag *this = frag_first(list);
|
||||
uint32_t lastofs = 0;
|
||||
int buggy = 0;
|
||||
|
||||
while(this) {
|
||||
if (this->node)
|
||||
printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n",
|
||||
this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw),
|
||||
this, frag_left(this), frag_right(this), frag_parent(this));
|
||||
else
|
||||
printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs,
|
||||
this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this));
|
||||
if (this->ofs != lastofs)
|
||||
buggy = 1;
|
||||
lastofs = this->ofs+this->size;
|
||||
this = frag_next(this);
|
||||
}
|
||||
if (buggy && !permitbug) {
|
||||
printk(KERN_CRIT "Frag tree got a hole in it\n");
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
void jffs2_print_frag_list(struct jffs2_inode_info *f)
|
||||
{
|
||||
jffs2_print_fragtree(&f->fragtree, 0);
|
||||
|
||||
if (f->metadata) {
|
||||
printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_JFFS2_FS_DEBUG >= 1
|
||||
static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f)
|
||||
{
|
||||
struct jffs2_node_frag *frag;
|
||||
int bitched = 0;
|
||||
|
||||
for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
|
||||
|
||||
struct jffs2_full_dnode *fn = frag->node;
|
||||
if (!fn || !fn->raw)
|
||||
continue;
|
||||
|
||||
if (ref_flags(fn->raw) == REF_PRISTINE) {
|
||||
|
||||
if (fn->frags > 1) {
|
||||
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags);
|
||||
bitched = 1;
|
||||
}
|
||||
/* A hole node which isn't multi-page should be garbage-collected
|
||||
and merged anyway, so we just check for the frag size here,
|
||||
rather than mucking around with actually reading the node
|
||||
and checking the compression type, which is the real way
|
||||
to tell a hole node. */
|
||||
if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) {
|
||||
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
|
||||
ref_offset(fn->raw));
|
||||
bitched = 1;
|
||||
}
|
||||
|
||||
if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) {
|
||||
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
|
||||
ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size);
|
||||
bitched = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bitched) {
|
||||
struct jffs2_node_frag *thisfrag;
|
||||
|
||||
printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino);
|
||||
thisfrag = frag_first(&f->fragtree);
|
||||
while (thisfrag) {
|
||||
if (!thisfrag->node) {
|
||||
printk("Frag @0x%x-0x%x; node-less hole\n",
|
||||
thisfrag->ofs, thisfrag->size + thisfrag->ofs);
|
||||
} else if (!thisfrag->node->raw) {
|
||||
printk("Frag @0x%x-0x%x; raw-less hole\n",
|
||||
thisfrag->ofs, thisfrag->size + thisfrag->ofs);
|
||||
} else {
|
||||
printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n",
|
||||
thisfrag->ofs, thisfrag->size + thisfrag->ofs,
|
||||
ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw),
|
||||
thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size);
|
||||
}
|
||||
thisfrag = frag_next(thisfrag);
|
||||
}
|
||||
}
|
||||
return bitched;
|
||||
}
|
||||
#endif /* D1 */
|
||||
|
||||
static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
|
||||
{
|
||||
if (this->node) {
|
||||
this->node->frags--;
|
||||
if (!this->node->frags) {
|
||||
/* The node has no valid frags left. It's totally obsoleted */
|
||||
D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n",
|
||||
ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size));
|
||||
jffs2_mark_node_obsolete(c, this->node->raw);
|
||||
jffs2_free_full_dnode(this->node);
|
||||
} else {
|
||||
D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n",
|
||||
ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size,
|
||||
this->node->frags));
|
||||
mark_ref_normal(this->node->raw);
|
||||
}
|
||||
|
||||
}
|
||||
jffs2_free_node_frag(this);
|
||||
}
|
||||
|
||||
/* Given an inode, probably with existing list of fragments, add the new node
|
||||
* to the fragment list.
|
||||
*/
|
||||
int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
|
||||
{
|
||||
int ret;
|
||||
struct jffs2_node_frag *newfrag;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
|
||||
|
||||
newfrag = jffs2_alloc_node_frag();
|
||||
if (unlikely(!newfrag))
|
||||
return -ENOMEM;
|
||||
|
||||
D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
|
||||
fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
|
||||
|
||||
if (unlikely(!fn->size)) {
|
||||
jffs2_free_node_frag(newfrag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
newfrag->ofs = fn->ofs;
|
||||
newfrag->size = fn->size;
|
||||
newfrag->node = fn;
|
||||
newfrag->node->frags = 1;
|
||||
|
||||
ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If we now share a page with other nodes, mark either previous
|
||||
or next node REF_NORMAL, as appropriate. */
|
||||
if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) {
|
||||
struct jffs2_node_frag *prev = frag_prev(newfrag);
|
||||
|
||||
mark_ref_normal(fn->raw);
|
||||
/* If we don't start at zero there's _always_ a previous */
|
||||
if (prev->node)
|
||||
mark_ref_normal(prev->node->raw);
|
||||
}
|
||||
|
||||
if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) {
|
||||
struct jffs2_node_frag *next = frag_next(newfrag);
|
||||
|
||||
if (next) {
|
||||
mark_ref_normal(fn->raw);
|
||||
if (next->node)
|
||||
mark_ref_normal(next->node->raw);
|
||||
}
|
||||
}
|
||||
D2(if (jffs2_sanitycheck_fragtree(f)) {
|
||||
printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n",
|
||||
fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag);
|
||||
return 0;
|
||||
})
|
||||
D2(jffs2_print_frag_list(f));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Doesn't set inode->i_size */
|
||||
static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
|
||||
{
|
||||
struct jffs2_node_frag *this;
|
||||
uint32_t lastend;
|
||||
|
||||
/* Skip all the nodes which are completed before this one starts */
|
||||
this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
|
||||
|
||||
if (this) {
|
||||
D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
|
||||
this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
|
||||
lastend = this->ofs + this->size;
|
||||
} else {
|
||||
D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n"));
|
||||
lastend = 0;
|
||||
}
|
||||
|
||||
/* See if we ran off the end of the list */
|
||||
if (lastend <= newfrag->ofs) {
|
||||
/* We did */
|
||||
|
||||
/* Check if 'this' node was on the same page as the new node.
|
||||
If so, both 'this' and the new node get marked REF_NORMAL so
|
||||
the GC can take a look.
|
||||
*/
|
||||
if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
|
||||
if (this->node)
|
||||
mark_ref_normal(this->node->raw);
|
||||
mark_ref_normal(newfrag->node->raw);
|
||||
}
|
||||
|
||||
if (lastend < newfrag->node->ofs) {
|
||||
/* ... and we need to put a hole in before the new node */
|
||||
struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
|
||||
if (!holefrag) {
|
||||
jffs2_free_node_frag(newfrag);
|
||||
return -ENOMEM;
|
||||
}
|
||||
holefrag->ofs = lastend;
|
||||
holefrag->size = newfrag->node->ofs - lastend;
|
||||
holefrag->node = NULL;
|
||||
if (this) {
|
||||
/* By definition, the 'this' node has no right-hand child,
|
||||
because there are no frags with offset greater than it.
|
||||
So that's where we want to put the hole */
|
||||
D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this));
|
||||
rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
|
||||
} else {
|
||||
D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag));
|
||||
rb_link_node(&holefrag->rb, NULL, &list->rb_node);
|
||||
}
|
||||
rb_insert_color(&holefrag->rb, list);
|
||||
this = holefrag;
|
||||
}
|
||||
if (this) {
|
||||
/* By definition, the 'this' node has no right-hand child,
|
||||
because there are no frags with offset greater than it.
|
||||
So that's where we want to put the hole */
|
||||
D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this));
|
||||
rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);
|
||||
} else {
|
||||
D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag));
|
||||
rb_link_node(&newfrag->rb, NULL, &list->rb_node);
|
||||
}
|
||||
rb_insert_color(&newfrag->rb, list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
|
||||
this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
|
||||
|
||||
/* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes,
|
||||
* - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs
|
||||
*/
|
||||
if (newfrag->ofs > this->ofs) {
|
||||
/* This node isn't completely obsoleted. The start of it remains valid */
|
||||
|
||||
/* Mark the new node and the partially covered node REF_NORMAL -- let
|
||||
the GC take a look at them */
|
||||
mark_ref_normal(newfrag->node->raw);
|
||||
if (this->node)
|
||||
mark_ref_normal(this->node->raw);
|
||||
|
||||
if (this->ofs + this->size > newfrag->ofs + newfrag->size) {
|
||||
/* The new node splits 'this' frag into two */
|
||||
struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag();
|
||||
if (!newfrag2) {
|
||||
jffs2_free_node_frag(newfrag);
|
||||
return -ENOMEM;
|
||||
}
|
||||
D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
|
||||
if (this->node)
|
||||
printk("phys 0x%08x\n", ref_offset(this->node->raw));
|
||||
else
|
||||
printk("hole\n");
|
||||
)
|
||||
|
||||
/* New second frag pointing to this's node */
|
||||
newfrag2->ofs = newfrag->ofs + newfrag->size;
|
||||
newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
|
||||
newfrag2->node = this->node;
|
||||
if (this->node)
|
||||
this->node->frags++;
|
||||
|
||||
/* Adjust size of original 'this' */
|
||||
this->size = newfrag->ofs - this->ofs;
|
||||
|
||||
/* Now, we know there's no node with offset
|
||||
greater than this->ofs but smaller than
|
||||
newfrag2->ofs or newfrag->ofs, for obvious
|
||||
reasons. So we can do a tree insert from
|
||||
'this' to insert newfrag, and a tree insert
|
||||
from newfrag to insert newfrag2. */
|
||||
jffs2_fragtree_insert(newfrag, this);
|
||||
rb_insert_color(&newfrag->rb, list);
|
||||
|
||||
jffs2_fragtree_insert(newfrag2, newfrag);
|
||||
rb_insert_color(&newfrag2->rb, list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* New node just reduces 'this' frag in size, doesn't split it */
|
||||
this->size = newfrag->ofs - this->ofs;
|
||||
|
||||
/* Again, we know it lives down here in the tree */
|
||||
jffs2_fragtree_insert(newfrag, this);
|
||||
rb_insert_color(&newfrag->rb, list);
|
||||
} else {
|
||||
/* New frag starts at the same point as 'this' used to. Replace
|
||||
it in the tree without doing a delete and insertion */
|
||||
D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n",
|
||||
newfrag, newfrag->ofs, newfrag->ofs+newfrag->size,
|
||||
this, this->ofs, this->ofs+this->size));
|
||||
|
||||
rb_replace_node(&this->rb, &newfrag->rb, list);
|
||||
|
||||
if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
|
||||
D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size));
|
||||
jffs2_obsolete_node_frag(c, this);
|
||||
} else {
|
||||
this->ofs += newfrag->size;
|
||||
this->size -= newfrag->size;
|
||||
|
||||
jffs2_fragtree_insert(this, newfrag);
|
||||
rb_insert_color(&this->rb, list);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* OK, now we have newfrag added in the correct place in the tree, but
|
||||
frag_next(newfrag) may be a fragment which is overlapped by it
|
||||
*/
|
||||
while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
|
||||
/* 'this' frag is obsoleted completely. */
|
||||
D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size));
|
||||
rb_erase(&this->rb, list);
|
||||
jffs2_obsolete_node_frag(c, this);
|
||||
}
|
||||
/* Now we're pointing at the first frag which isn't totally obsoleted by
|
||||
the new frag */
|
||||
|
||||
if (!this || newfrag->ofs + newfrag->size == this->ofs) {
|
||||
return 0;
|
||||
}
|
||||
/* Still some overlap but we don't need to move it in the tree */
|
||||
this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size);
|
||||
this->ofs = newfrag->ofs + newfrag->size;
|
||||
|
||||
/* And mark them REF_NORMAL so the GC takes a look at them */
|
||||
if (this->node)
|
||||
mark_ref_normal(this->node->raw);
|
||||
mark_ref_normal(newfrag->node->raw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
|
||||
{
|
||||
struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
|
||||
|
||||
D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
|
||||
|
||||
/* We know frag->ofs <= size. That's what lookup does for us */
|
||||
if (frag && frag->ofs != size) {
|
||||
if (frag->ofs+frag->size >= size) {
|
||||
D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
|
||||
frag->size = size - frag->ofs;
|
||||
}
|
||||
frag = frag_next(frag);
|
||||
}
|
||||
while (frag && frag->ofs >= size) {
|
||||
struct jffs2_node_frag *next = frag_next(frag);
|
||||
|
||||
D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
|
||||
frag_erase(frag, list);
|
||||
jffs2_obsolete_node_frag(c, frag);
|
||||
frag = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Scan the list of all nodes present for this ino, build map of versions, etc. */
|
||||
|
||||
static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||
struct jffs2_inode_info *f,
|
||||
struct jffs2_raw_inode *latest_node);
|
||||
|
||||
int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
uint32_t ino, struct jffs2_raw_inode *latest_node)
|
||||
{
|
||||
D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
|
||||
|
||||
retry_inocache:
|
||||
spin_lock(&c->inocache_lock);
|
||||
f->inocache = jffs2_get_ino_cache(c, ino);
|
||||
|
||||
D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
|
||||
|
||||
if (f->inocache) {
|
||||
/* Check its state. We may need to wait before we can use it */
|
||||
switch(f->inocache->state) {
|
||||
case INO_STATE_UNCHECKED:
|
||||
case INO_STATE_CHECKEDABSENT:
|
||||
f->inocache->state = INO_STATE_READING;
|
||||
break;
|
||||
|
||||
case INO_STATE_CHECKING:
|
||||
case INO_STATE_GC:
|
||||
/* If it's in either of these states, we need
|
||||
to wait for whoever's got it to finish and
|
||||
put it back. */
|
||||
D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
|
||||
ino, f->inocache->state));
|
||||
sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
|
||||
goto retry_inocache;
|
||||
|
||||
case INO_STATE_READING:
|
||||
case INO_STATE_PRESENT:
|
||||
/* Eep. This should never happen. It can
|
||||
happen if Linux calls read_inode() again
|
||||
before clear_inode() has finished though. */
|
||||
printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
|
||||
/* Fail. That's probably better than allowing it to succeed */
|
||||
f->inocache = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
spin_unlock(&c->inocache_lock);
|
||||
|
||||
if (!f->inocache && ino == 1) {
|
||||
/* Special case - no root inode on medium */
|
||||
f->inocache = jffs2_alloc_inode_cache();
|
||||
if (!f->inocache) {
|
||||
printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n"));
|
||||
memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
|
||||
f->inocache->ino = f->inocache->nlink = 1;
|
||||
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
|
||||
f->inocache->state = INO_STATE_READING;
|
||||
jffs2_add_ino_cache(c, f->inocache);
|
||||
}
|
||||
if (!f->inocache) {
|
||||
printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return jffs2_do_read_inode_internal(c, f, latest_node);
|
||||
}
|
||||
|
||||
int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
|
||||
{
|
||||
struct jffs2_raw_inode n;
|
||||
struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
|
||||
int ret;
|
||||
|
||||
if (!f)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(f, 0, sizeof(*f));
|
||||
init_MUTEX_LOCKED(&f->sem);
|
||||
f->inocache = ic;
|
||||
|
||||
ret = jffs2_do_read_inode_internal(c, f, &n);
|
||||
if (!ret) {
|
||||
up(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
}
|
||||
kfree (f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||
struct jffs2_inode_info *f,
|
||||
struct jffs2_raw_inode *latest_node)
|
||||
{
|
||||
struct jffs2_tmp_dnode_info *tn_list, *tn;
|
||||
struct jffs2_full_dirent *fd_list;
|
||||
struct jffs2_full_dnode *fn = NULL;
|
||||
uint32_t crc;
|
||||
uint32_t latest_mctime, mctime_ver;
|
||||
uint32_t mdata_ver = 0;
|
||||
size_t retlen;
|
||||
int ret;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
|
||||
|
||||
/* Grab all nodes relevant to this ino */
|
||||
ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
|
||||
|
||||
if (ret) {
|
||||
printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret);
|
||||
if (f->inocache->state == INO_STATE_READING)
|
||||
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
|
||||
return ret;
|
||||
}
|
||||
f->dents = fd_list;
|
||||
|
||||
while (tn_list) {
|
||||
tn = tn_list;
|
||||
|
||||
fn = tn->fn;
|
||||
|
||||
if (f->metadata) {
|
||||
if (likely(tn->version >= mdata_ver)) {
|
||||
D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
|
||||
jffs2_mark_node_obsolete(c, f->metadata->raw);
|
||||
jffs2_free_full_dnode(f->metadata);
|
||||
f->metadata = NULL;
|
||||
|
||||
mdata_ver = 0;
|
||||
} else {
|
||||
/* This should never happen. */
|
||||
printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n",
|
||||
ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw));
|
||||
jffs2_mark_node_obsolete(c, fn->raw);
|
||||
jffs2_free_full_dnode(fn);
|
||||
/* Fill in latest_node from the metadata, not this one we're about to free... */
|
||||
fn = f->metadata;
|
||||
goto next_tn;
|
||||
}
|
||||
}
|
||||
|
||||
if (fn->size) {
|
||||
jffs2_add_full_dnode_to_inode(c, f, fn);
|
||||
} else {
|
||||
/* Zero-sized node at end of version list. Just a metadata update */
|
||||
D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version));
|
||||
f->metadata = fn;
|
||||
mdata_ver = tn->version;
|
||||
}
|
||||
next_tn:
|
||||
tn_list = tn->next;
|
||||
jffs2_free_tmp_dnode_info(tn);
|
||||
}
|
||||
D1(jffs2_sanitycheck_fragtree(f));
|
||||
|
||||
if (!fn) {
|
||||
/* No data nodes for this inode. */
|
||||
if (f->inocache->ino != 1) {
|
||||
printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino);
|
||||
if (!fd_list) {
|
||||
if (f->inocache->state == INO_STATE_READING)
|
||||
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
|
||||
return -EIO;
|
||||
}
|
||||
printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
|
||||
}
|
||||
latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);
|
||||
latest_node->version = cpu_to_je32(0);
|
||||
latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0);
|
||||
latest_node->isize = cpu_to_je32(0);
|
||||
latest_node->gid = cpu_to_je16(0);
|
||||
latest_node->uid = cpu_to_je16(0);
|
||||
if (f->inocache->state == INO_STATE_READING)
|
||||
jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
|
||||
if (ret || retlen != sizeof(*latest_node)) {
|
||||
printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n",
|
||||
ret, retlen, sizeof(*latest_node));
|
||||
/* FIXME: If this fails, there seems to be a memory leak. Find it. */
|
||||
up(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return ret?ret:-EIO;
|
||||
}
|
||||
|
||||
crc = crc32(0, latest_node, sizeof(*latest_node)-8);
|
||||
if (crc != je32_to_cpu(latest_node->node_crc)) {
|
||||
printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw));
|
||||
up(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
|
||||
case S_IFDIR:
|
||||
if (mctime_ver > je32_to_cpu(latest_node->version)) {
|
||||
/* The times in the latest_node are actually older than
|
||||
mctime in the latest dirent. Cheat. */
|
||||
latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case S_IFREG:
|
||||
/* If it was a regular file, truncate it to the latest node's isize */
|
||||
jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize));
|
||||
break;
|
||||
|
||||
case S_IFLNK:
|
||||
/* Hack to work around broken isize in old symlink code.
|
||||
Remove this when dwmw2 comes to his senses and stops
|
||||
symlinks from being an entirely gratuitous special
|
||||
case. */
|
||||
if (!je32_to_cpu(latest_node->isize))
|
||||
latest_node->isize = latest_node->dsize;
|
||||
/* fall through... */
|
||||
|
||||
case S_IFBLK:
|
||||
case S_IFCHR:
|
||||
/* Certain inode types should have only one data node, and it's
|
||||
kept as the metadata node */
|
||||
if (f->metadata) {
|
||||
printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n",
|
||||
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
||||
up(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return -EIO;
|
||||
}
|
||||
if (!frag_first(&f->fragtree)) {
|
||||
printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n",
|
||||
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
||||
up(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return -EIO;
|
||||
}
|
||||
/* ASSERT: f->fraglist != NULL */
|
||||
if (frag_next(frag_first(&f->fragtree))) {
|
||||
printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n",
|
||||
f->inocache->ino, jemode_to_cpu(latest_node->mode));
|
||||
/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
|
||||
up(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return -EIO;
|
||||
}
|
||||
/* OK. We're happy */
|
||||
f->metadata = frag_first(&f->fragtree)->node;
|
||||
jffs2_free_node_frag(frag_first(&f->fragtree));
|
||||
f->fragtree = RB_ROOT;
|
||||
break;
|
||||
}
|
||||
if (f->inocache->state == INO_STATE_READING)
|
||||
jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
|
||||
{
|
||||
struct jffs2_full_dirent *fd, *fds;
|
||||
int deleted;
|
||||
|
||||
down(&f->sem);
|
||||
deleted = f->inocache && !f->inocache->nlink;
|
||||
|
||||
if (f->metadata) {
|
||||
if (deleted)
|
||||
jffs2_mark_node_obsolete(c, f->metadata->raw);
|
||||
jffs2_free_full_dnode(f->metadata);
|
||||
}
|
||||
|
||||
jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
|
||||
|
||||
fds = f->dents;
|
||||
|
||||
while(fds) {
|
||||
fd = fds;
|
||||
fds = fd->next;
|
||||
jffs2_free_full_dirent(fd);
|
||||
}
|
||||
|
||||
if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
|
||||
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
|
||||
|
||||
up(&f->sem);
|
||||
}
|
916
fs/jffs2/scan.c
Normální soubor
916
fs/jffs2/scan.c
Normální soubor
@@ -0,0 +1,916 @@
|
||||
/*
|
||||
* 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: scan.c,v 1.115 2004/11/17 12:59:08 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/compiler.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
#define EMPTY_SCAN_SIZE 1024
|
||||
|
||||
#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
|
||||
c->free_size -= _x; c->dirty_size += _x; \
|
||||
jeb->free_size -= _x ; jeb->dirty_size += _x; \
|
||||
}while(0)
|
||||
#define USED_SPACE(x) do { typeof(x) _x = (x); \
|
||||
c->free_size -= _x; c->used_size += _x; \
|
||||
jeb->free_size -= _x ; jeb->used_size += _x; \
|
||||
}while(0)
|
||||
#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
|
||||
c->free_size -= _x; c->unchecked_size += _x; \
|
||||
jeb->free_size -= _x ; jeb->unchecked_size += _x; \
|
||||
}while(0)
|
||||
|
||||
#define noisy_printk(noise, args...) do { \
|
||||
if (*(noise)) { \
|
||||
printk(KERN_NOTICE args); \
|
||||
(*(noise))--; \
|
||||
if (!(*(noise))) { \
|
||||
printk(KERN_NOTICE "Further such events for this erase block will not be printed\n"); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static uint32_t pseudo_random;
|
||||
|
||||
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
unsigned char *buf, uint32_t buf_size);
|
||||
|
||||
/* These helper functions _must_ increase ofs and also do the dirty/used space accounting.
|
||||
* Returning an error will abort the mount - bad checksums etc. should just mark the space
|
||||
* as dirty.
|
||||
*/
|
||||
static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_inode *ri, uint32_t ofs);
|
||||
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_dirent *rd, uint32_t ofs);
|
||||
|
||||
#define BLK_STATE_ALLFF 0
|
||||
#define BLK_STATE_CLEAN 1
|
||||
#define BLK_STATE_PARTDIRTY 2
|
||||
#define BLK_STATE_CLEANMARKER 3
|
||||
#define BLK_STATE_ALLDIRTY 4
|
||||
#define BLK_STATE_BADBLOCK 5
|
||||
|
||||
static inline int min_free(struct jffs2_sb_info *c)
|
||||
{
|
||||
uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
|
||||
#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
|
||||
if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
|
||||
return c->wbuf_pagesize;
|
||||
#endif
|
||||
return min;
|
||||
|
||||
}
|
||||
int jffs2_scan_medium(struct jffs2_sb_info *c)
|
||||
{
|
||||
int i, ret;
|
||||
uint32_t empty_blocks = 0, bad_blocks = 0;
|
||||
unsigned char *flashbuf = NULL;
|
||||
uint32_t buf_size = 0;
|
||||
#ifndef __ECOS
|
||||
size_t pointlen;
|
||||
|
||||
if (c->mtd->point) {
|
||||
ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf);
|
||||
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);
|
||||
flashbuf = NULL;
|
||||
}
|
||||
if (ret)
|
||||
D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
|
||||
}
|
||||
#endif
|
||||
if (!flashbuf) {
|
||||
/* For NAND it's quicker to read a whole eraseblock at a time,
|
||||
apparently */
|
||||
if (jffs2_cleanmarker_oob(c))
|
||||
buf_size = c->sector_size;
|
||||
else
|
||||
buf_size = PAGE_SIZE;
|
||||
|
||||
/* Respect kmalloc limitations */
|
||||
if (buf_size > 128*1024)
|
||||
buf_size = 128*1024;
|
||||
|
||||
D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
|
||||
flashbuf = kmalloc(buf_size, GFP_KERNEL);
|
||||
if (!flashbuf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i=0; i<c->nr_blocks; i++) {
|
||||
struct jffs2_eraseblock *jeb = &c->blocks[i];
|
||||
|
||||
ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size);
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ACCT_PARANOIA_CHECK(jeb);
|
||||
|
||||
/* Now decide which list to put it on */
|
||||
switch(ret) {
|
||||
case BLK_STATE_ALLFF:
|
||||
/*
|
||||
* Empty block. Since we can't be sure it
|
||||
* was entirely erased, we just queue it for erase
|
||||
* again. It will be marked as such when the erase
|
||||
* is complete. Meanwhile we still count it as empty
|
||||
* for later checks.
|
||||
*/
|
||||
empty_blocks++;
|
||||
list_add(&jeb->list, &c->erase_pending_list);
|
||||
c->nr_erasing_blocks++;
|
||||
break;
|
||||
|
||||
case BLK_STATE_CLEANMARKER:
|
||||
/* Only a CLEANMARKER node is valid */
|
||||
if (!jeb->dirty_size) {
|
||||
/* It's actually free */
|
||||
list_add(&jeb->list, &c->free_list);
|
||||
c->nr_free_blocks++;
|
||||
} else {
|
||||
/* Dirt */
|
||||
D1(printk(KERN_DEBUG "Adding all-dirty block at 0x%08x to erase_pending_list\n", jeb->offset));
|
||||
list_add(&jeb->list, &c->erase_pending_list);
|
||||
c->nr_erasing_blocks++;
|
||||
}
|
||||
break;
|
||||
|
||||
case BLK_STATE_CLEAN:
|
||||
/* Full (or almost full) of clean data. Clean list */
|
||||
list_add(&jeb->list, &c->clean_list);
|
||||
break;
|
||||
|
||||
case BLK_STATE_PARTDIRTY:
|
||||
/* Some data, but not full. Dirty list. */
|
||||
/* We want to remember the block with most free space
|
||||
and stick it in the 'nextblock' position to start writing to it. */
|
||||
if (jeb->free_size > min_free(c) &&
|
||||
(!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
|
||||
/* Better candidate for the next writes to go to */
|
||||
if (c->nextblock) {
|
||||
c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
|
||||
c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
|
||||
c->free_size -= c->nextblock->free_size;
|
||||
c->wasted_size -= c->nextblock->wasted_size;
|
||||
c->nextblock->free_size = c->nextblock->wasted_size = 0;
|
||||
if (VERYDIRTY(c, c->nextblock->dirty_size)) {
|
||||
list_add(&c->nextblock->list, &c->very_dirty_list);
|
||||
} else {
|
||||
list_add(&c->nextblock->list, &c->dirty_list);
|
||||
}
|
||||
}
|
||||
c->nextblock = jeb;
|
||||
} else {
|
||||
jeb->dirty_size += jeb->free_size + jeb->wasted_size;
|
||||
c->dirty_size += jeb->free_size + jeb->wasted_size;
|
||||
c->free_size -= jeb->free_size;
|
||||
c->wasted_size -= jeb->wasted_size;
|
||||
jeb->free_size = jeb->wasted_size = 0;
|
||||
if (VERYDIRTY(c, jeb->dirty_size)) {
|
||||
list_add(&jeb->list, &c->very_dirty_list);
|
||||
} else {
|
||||
list_add(&jeb->list, &c->dirty_list);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BLK_STATE_ALLDIRTY:
|
||||
/* Nothing valid - not even a clean marker. Needs erasing. */
|
||||
/* For now we just put it on the erasing list. We'll start the erases later */
|
||||
D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset));
|
||||
list_add(&jeb->list, &c->erase_pending_list);
|
||||
c->nr_erasing_blocks++;
|
||||
break;
|
||||
|
||||
case BLK_STATE_BADBLOCK:
|
||||
D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset));
|
||||
list_add(&jeb->list, &c->bad_list);
|
||||
c->bad_size += c->sector_size;
|
||||
c->free_size -= c->sector_size;
|
||||
bad_blocks++;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n");
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
/* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
|
||||
if (c->nextblock && (c->nextblock->dirty_size)) {
|
||||
c->nextblock->wasted_size += c->nextblock->dirty_size;
|
||||
c->wasted_size += c->nextblock->dirty_size;
|
||||
c->dirty_size -= c->nextblock->dirty_size;
|
||||
c->nextblock->dirty_size = 0;
|
||||
}
|
||||
#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
|
||||
if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) {
|
||||
/* If we're going to start writing into a block which already
|
||||
contains data, and the end of the data isn't page-aligned,
|
||||
skip a little and align it. */
|
||||
|
||||
uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
|
||||
skip));
|
||||
c->nextblock->wasted_size += skip;
|
||||
c->wasted_size += skip;
|
||||
|
||||
c->nextblock->free_size -= skip;
|
||||
c->free_size -= skip;
|
||||
}
|
||||
#endif
|
||||
if (c->nr_erasing_blocks) {
|
||||
if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
|
||||
printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
|
||||
printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
jffs2_erase_pending_trigger(c);
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
if (buf_size)
|
||||
kfree(flashbuf);
|
||||
#ifndef __ECOS
|
||||
else
|
||||
c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
|
||||
uint32_t ofs, uint32_t len)
|
||||
{
|
||||
int ret;
|
||||
size_t retlen;
|
||||
|
||||
ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
|
||||
if (ret) {
|
||||
D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret));
|
||||
return ret;
|
||||
}
|
||||
if (retlen < len) {
|
||||
D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen));
|
||||
return -EIO;
|
||||
}
|
||||
D2(printk(KERN_DEBUG "Read 0x%x bytes from 0x%08x into buf\n", len, ofs));
|
||||
D2(printk(KERN_DEBUG "000: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
unsigned char *buf, uint32_t buf_size) {
|
||||
struct jffs2_unknown_node *node;
|
||||
struct jffs2_unknown_node crcnode;
|
||||
uint32_t ofs, prevofs;
|
||||
uint32_t hdr_crc, buf_ofs, buf_len;
|
||||
int err;
|
||||
int noise = 0;
|
||||
#ifdef CONFIG_JFFS2_FS_NAND
|
||||
int cleanmarkerfound = 0;
|
||||
#endif
|
||||
|
||||
ofs = jeb->offset;
|
||||
prevofs = jeb->offset - 1;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_NAND
|
||||
if (jffs2_cleanmarker_oob(c)) {
|
||||
int ret = jffs2_check_nand_cleanmarker(c, jeb);
|
||||
D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
|
||||
/* Even if it's not found, we still scan to see
|
||||
if the block is empty. We use this information
|
||||
to decide whether to erase it or not. */
|
||||
switch (ret) {
|
||||
case 0: cleanmarkerfound = 1; break;
|
||||
case 1: break;
|
||||
case 2: return BLK_STATE_BADBLOCK;
|
||||
case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
|
||||
default: return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
buf_ofs = jeb->offset;
|
||||
|
||||
if (!buf_size) {
|
||||
buf_len = c->sector_size;
|
||||
} else {
|
||||
buf_len = EMPTY_SCAN_SIZE;
|
||||
err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* We temporarily use 'ofs' as a pointer into the buffer/jeb */
|
||||
ofs = 0;
|
||||
|
||||
/* Scan only 4KiB of 0xFF before declaring it's empty */
|
||||
while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
|
||||
ofs += 4;
|
||||
|
||||
if (ofs == EMPTY_SCAN_SIZE) {
|
||||
#ifdef CONFIG_JFFS2_FS_NAND
|
||||
if (jffs2_cleanmarker_oob(c)) {
|
||||
/* scan oob, take care of cleanmarker */
|
||||
int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
|
||||
D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret));
|
||||
switch (ret) {
|
||||
case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
|
||||
case 1: return BLK_STATE_ALLDIRTY;
|
||||
default: return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
|
||||
return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
|
||||
}
|
||||
if (ofs) {
|
||||
D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
|
||||
jeb->offset + ofs));
|
||||
DIRTY_SPACE(ofs);
|
||||
}
|
||||
|
||||
/* Now ofs is a complete physical flash offset as it always was... */
|
||||
ofs += jeb->offset;
|
||||
|
||||
noise = 10;
|
||||
|
||||
scan_more:
|
||||
while(ofs < jeb->offset + c->sector_size) {
|
||||
|
||||
D1(ACCT_PARANOIA_CHECK(jeb));
|
||||
|
||||
cond_resched();
|
||||
|
||||
if (ofs & 3) {
|
||||
printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs);
|
||||
ofs = PAD(ofs);
|
||||
continue;
|
||||
}
|
||||
if (ofs == prevofs) {
|
||||
printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs);
|
||||
DIRTY_SPACE(4);
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
prevofs = ofs;
|
||||
|
||||
if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
|
||||
D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
|
||||
jeb->offset, c->sector_size, ofs, sizeof(*node)));
|
||||
DIRTY_SPACE((jeb->offset + c->sector_size)-ofs);
|
||||
break;
|
||||
}
|
||||
|
||||
if (buf_ofs + buf_len < ofs + sizeof(*node)) {
|
||||
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
|
||||
D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
|
||||
sizeof(struct jffs2_unknown_node), buf_len, ofs));
|
||||
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
|
||||
if (err)
|
||||
return err;
|
||||
buf_ofs = ofs;
|
||||
}
|
||||
|
||||
node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
|
||||
|
||||
if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
|
||||
uint32_t inbuf_ofs;
|
||||
uint32_t empty_start;
|
||||
|
||||
empty_start = ofs;
|
||||
ofs += 4;
|
||||
|
||||
D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
|
||||
more_empty:
|
||||
inbuf_ofs = ofs - buf_ofs;
|
||||
while (inbuf_ofs < buf_len) {
|
||||
if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
|
||||
printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
|
||||
empty_start, ofs);
|
||||
DIRTY_SPACE(ofs-empty_start);
|
||||
goto scan_more;
|
||||
}
|
||||
|
||||
inbuf_ofs+=4;
|
||||
ofs += 4;
|
||||
}
|
||||
/* Ran off end. */
|
||||
D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs));
|
||||
|
||||
/* If we're only checking the beginning of a block with a cleanmarker,
|
||||
bail now */
|
||||
if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
|
||||
c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_in_ino) {
|
||||
D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE));
|
||||
return BLK_STATE_CLEANMARKER;
|
||||
}
|
||||
|
||||
/* See how much more there is to read in this eraseblock... */
|
||||
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
|
||||
if (!buf_len) {
|
||||
/* No more to read. Break out of main loop without marking
|
||||
this range of empty space as dirty (because it's not) */
|
||||
D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n",
|
||||
empty_start));
|
||||
break;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs));
|
||||
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
|
||||
if (err)
|
||||
return err;
|
||||
buf_ofs = ofs;
|
||||
goto more_empty;
|
||||
}
|
||||
|
||||
if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
|
||||
printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
|
||||
DIRTY_SPACE(4);
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
|
||||
D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs));
|
||||
DIRTY_SPACE(4);
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
|
||||
printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
|
||||
printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
|
||||
DIRTY_SPACE(4);
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) {
|
||||
/* OK. We're out of possibilities. Whinge and move on */
|
||||
noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n",
|
||||
JFFS2_MAGIC_BITMASK, ofs,
|
||||
je16_to_cpu(node->magic));
|
||||
DIRTY_SPACE(4);
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
/* We seem to have a node of sorts. Check the CRC */
|
||||
crcnode.magic = node->magic;
|
||||
crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE);
|
||||
crcnode.totlen = node->totlen;
|
||||
hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4);
|
||||
|
||||
if (hdr_crc != je32_to_cpu(node->hdr_crc)) {
|
||||
noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
|
||||
ofs, je16_to_cpu(node->magic),
|
||||
je16_to_cpu(node->nodetype),
|
||||
je32_to_cpu(node->totlen),
|
||||
je32_to_cpu(node->hdr_crc),
|
||||
hdr_crc);
|
||||
DIRTY_SPACE(4);
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ofs + je32_to_cpu(node->totlen) >
|
||||
jeb->offset + c->sector_size) {
|
||||
/* Eep. Node goes over the end of the erase block. */
|
||||
printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
|
||||
ofs, je32_to_cpu(node->totlen));
|
||||
printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
|
||||
DIRTY_SPACE(4);
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
|
||||
/* Wheee. This is an obsoleted node */
|
||||
D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(je16_to_cpu(node->nodetype)) {
|
||||
case JFFS2_NODETYPE_INODE:
|
||||
if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) {
|
||||
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
|
||||
D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
|
||||
sizeof(struct jffs2_raw_inode), buf_len, ofs));
|
||||
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
|
||||
if (err)
|
||||
return err;
|
||||
buf_ofs = ofs;
|
||||
node = (void *)buf;
|
||||
}
|
||||
err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs);
|
||||
if (err) return err;
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
|
||||
case JFFS2_NODETYPE_DIRENT:
|
||||
if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
|
||||
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
|
||||
D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n",
|
||||
je32_to_cpu(node->totlen), buf_len, ofs));
|
||||
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
|
||||
if (err)
|
||||
return err;
|
||||
buf_ofs = ofs;
|
||||
node = (void *)buf;
|
||||
}
|
||||
err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs);
|
||||
if (err) return err;
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
|
||||
case JFFS2_NODETYPE_CLEANMARKER:
|
||||
D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
|
||||
if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
|
||||
printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
|
||||
ofs, je32_to_cpu(node->totlen), c->cleanmarker_size);
|
||||
DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
|
||||
ofs += PAD(sizeof(struct jffs2_unknown_node));
|
||||
} else if (jeb->first_node) {
|
||||
printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
|
||||
DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
|
||||
ofs += PAD(sizeof(struct jffs2_unknown_node));
|
||||
} else {
|
||||
struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
|
||||
if (!marker_ref) {
|
||||
printk(KERN_NOTICE "Failed to allocate node ref for clean marker\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
marker_ref->next_in_ino = NULL;
|
||||
marker_ref->next_phys = NULL;
|
||||
marker_ref->flash_offset = ofs | REF_NORMAL;
|
||||
marker_ref->__totlen = c->cleanmarker_size;
|
||||
jeb->first_node = jeb->last_node = marker_ref;
|
||||
|
||||
USED_SPACE(PAD(c->cleanmarker_size));
|
||||
ofs += PAD(c->cleanmarker_size);
|
||||
}
|
||||
break;
|
||||
|
||||
case JFFS2_NODETYPE_PADDING:
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
|
||||
default:
|
||||
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;
|
||||
if (!(jffs2_is_readonly(c)))
|
||||
return -EROFS;
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
|
||||
case JFFS2_FEATURE_INCOMPAT:
|
||||
printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
|
||||
return -EINVAL;
|
||||
|
||||
case JFFS2_FEATURE_RWCOMPAT_DELETE:
|
||||
D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
|
||||
case JFFS2_FEATURE_RWCOMPAT_COPY:
|
||||
D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
|
||||
USED_SPACE(PAD(je32_to_cpu(node->totlen)));
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset,
|
||||
jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size));
|
||||
|
||||
/* mark_node_obsolete can add to wasted !! */
|
||||
if (jeb->wasted_size) {
|
||||
jeb->dirty_size += jeb->wasted_size;
|
||||
c->dirty_size += jeb->wasted_size;
|
||||
c->wasted_size -= jeb->wasted_size;
|
||||
jeb->wasted_size = 0;
|
||||
}
|
||||
|
||||
if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
|
||||
&& (!jeb->first_node || !jeb->first_node->next_in_ino) )
|
||||
return BLK_STATE_CLEANMARKER;
|
||||
|
||||
/* move blocks with max 4 byte dirty space to cleanlist */
|
||||
else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) {
|
||||
c->dirty_size -= jeb->dirty_size;
|
||||
c->wasted_size += jeb->dirty_size;
|
||||
jeb->wasted_size += jeb->dirty_size;
|
||||
jeb->dirty_size = 0;
|
||||
return BLK_STATE_CLEAN;
|
||||
} else if (jeb->used_size || jeb->unchecked_size)
|
||||
return BLK_STATE_PARTDIRTY;
|
||||
else
|
||||
return BLK_STATE_ALLDIRTY;
|
||||
}
|
||||
|
||||
static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
|
||||
{
|
||||
struct jffs2_inode_cache *ic;
|
||||
|
||||
ic = jffs2_get_ino_cache(c, ino);
|
||||
if (ic)
|
||||
return ic;
|
||||
|
||||
if (ino > c->highest_ino)
|
||||
c->highest_ino = ino;
|
||||
|
||||
ic = jffs2_alloc_inode_cache();
|
||||
if (!ic) {
|
||||
printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
|
||||
return NULL;
|
||||
}
|
||||
memset(ic, 0, sizeof(*ic));
|
||||
|
||||
ic->ino = ino;
|
||||
ic->nodes = (void *)ic;
|
||||
jffs2_add_ino_cache(c, ic);
|
||||
if (ino == 1)
|
||||
ic->nlink = 1;
|
||||
return ic;
|
||||
}
|
||||
|
||||
static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_inode *ri, uint32_t ofs)
|
||||
{
|
||||
struct jffs2_raw_node_ref *raw;
|
||||
struct jffs2_inode_cache *ic;
|
||||
uint32_t ino = je32_to_cpu(ri->ino);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
|
||||
|
||||
/* We do very little here now. Just check the ino# to which we should attribute
|
||||
this node; we can do all the CRC checking etc. later. There's a tradeoff here --
|
||||
we used to scan the flash once only, reading everything we want from it into
|
||||
memory, then building all our in-core data structures and freeing the extra
|
||||
information. Now we allow the first part of the mount to complete a lot quicker,
|
||||
but we have to go _back_ to the flash in order to finish the CRC checking, etc.
|
||||
Which means that the _full_ amount of time to get to proper write mode with GC
|
||||
operational may actually be _longer_ than before. Sucks to be me. */
|
||||
|
||||
raw = jffs2_alloc_raw_node_ref();
|
||||
if (!raw) {
|
||||
printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ic = jffs2_get_ino_cache(c, ino);
|
||||
if (!ic) {
|
||||
/* Inocache get failed. Either we read a bogus ino# or it's just genuinely the
|
||||
first node we found for this inode. Do a CRC check to protect against the former
|
||||
case */
|
||||
uint32_t crc = crc32(0, ri, sizeof(*ri)-8);
|
||||
|
||||
if (crc != je32_to_cpu(ri->node_crc)) {
|
||||
printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
|
||||
ofs, je32_to_cpu(ri->node_crc), crc);
|
||||
/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen)));
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
return 0;
|
||||
}
|
||||
ic = jffs2_scan_make_ino_cache(c, ino);
|
||||
if (!ic) {
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wheee. It worked */
|
||||
|
||||
raw->flash_offset = ofs | REF_UNCHECKED;
|
||||
raw->__totlen = PAD(je32_to_cpu(ri->totlen));
|
||||
raw->next_phys = NULL;
|
||||
raw->next_in_ino = ic->nodes;
|
||||
|
||||
ic->nodes = raw;
|
||||
if (!jeb->first_node)
|
||||
jeb->first_node = raw;
|
||||
if (jeb->last_node)
|
||||
jeb->last_node->next_phys = raw;
|
||||
jeb->last_node = raw;
|
||||
|
||||
D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n",
|
||||
je32_to_cpu(ri->ino), je32_to_cpu(ri->version),
|
||||
je32_to_cpu(ri->offset),
|
||||
je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize)));
|
||||
|
||||
pseudo_random += je32_to_cpu(ri->version);
|
||||
|
||||
UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_dirent *rd, uint32_t ofs)
|
||||
{
|
||||
struct jffs2_raw_node_ref *raw;
|
||||
struct jffs2_full_dirent *fd;
|
||||
struct jffs2_inode_cache *ic;
|
||||
uint32_t crc;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
|
||||
|
||||
/* We don't get here unless the node is still valid, so we don't have to
|
||||
mask in the ACCURATE bit any more. */
|
||||
crc = crc32(0, rd, sizeof(*rd)-8);
|
||||
|
||||
if (crc != je32_to_cpu(rd->node_crc)) {
|
||||
printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
|
||||
ofs, je32_to_cpu(rd->node_crc), crc);
|
||||
/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
pseudo_random += je32_to_cpu(rd->version);
|
||||
|
||||
fd = jffs2_alloc_full_dirent(rd->nsize+1);
|
||||
if (!fd) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(&fd->name, rd->name, rd->nsize);
|
||||
fd->name[rd->nsize] = 0;
|
||||
|
||||
crc = crc32(0, fd->name, rd->nsize);
|
||||
if (crc != je32_to_cpu(rd->name_crc)) {
|
||||
printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
|
||||
ofs, je32_to_cpu(rd->name_crc), crc);
|
||||
D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino)));
|
||||
jffs2_free_full_dirent(fd);
|
||||
/* FIXME: Why do we believe totlen? */
|
||||
/* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
|
||||
return 0;
|
||||
}
|
||||
raw = jffs2_alloc_raw_node_ref();
|
||||
if (!raw) {
|
||||
jffs2_free_full_dirent(fd);
|
||||
printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino));
|
||||
if (!ic) {
|
||||
jffs2_free_full_dirent(fd);
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
raw->__totlen = PAD(je32_to_cpu(rd->totlen));
|
||||
raw->flash_offset = ofs | REF_PRISTINE;
|
||||
raw->next_phys = NULL;
|
||||
raw->next_in_ino = ic->nodes;
|
||||
ic->nodes = raw;
|
||||
if (!jeb->first_node)
|
||||
jeb->first_node = raw;
|
||||
if (jeb->last_node)
|
||||
jeb->last_node->next_phys = raw;
|
||||
jeb->last_node = raw;
|
||||
|
||||
fd->raw = raw;
|
||||
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->type = rd->type;
|
||||
USED_SPACE(PAD(je32_to_cpu(rd->totlen)));
|
||||
jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int count_list(struct list_head *l)
|
||||
{
|
||||
uint32_t count = 0;
|
||||
struct list_head *tmp;
|
||||
|
||||
list_for_each(tmp, l) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Note: This breaks if list_empty(head). I don't care. You
|
||||
might, if you copy this code and use it elsewhere :) */
|
||||
static void rotate_list(struct list_head *head, uint32_t count)
|
||||
{
|
||||
struct list_head *n = head->next;
|
||||
|
||||
list_del(head);
|
||||
while(count--) {
|
||||
n = n->next;
|
||||
}
|
||||
list_add(head, n);
|
||||
}
|
||||
|
||||
void jffs2_rotate_lists(struct jffs2_sb_info *c)
|
||||
{
|
||||
uint32_t x;
|
||||
uint32_t rotateby;
|
||||
|
||||
x = count_list(&c->clean_list);
|
||||
if (x) {
|
||||
rotateby = pseudo_random % x;
|
||||
D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby));
|
||||
|
||||
rotate_list((&c->clean_list), rotateby);
|
||||
|
||||
D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n",
|
||||
list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset));
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Not rotating empty clean_list\n"));
|
||||
}
|
||||
|
||||
x = count_list(&c->very_dirty_list);
|
||||
if (x) {
|
||||
rotateby = pseudo_random % x;
|
||||
D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby));
|
||||
|
||||
rotate_list((&c->very_dirty_list), rotateby);
|
||||
|
||||
D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n",
|
||||
list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset));
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n"));
|
||||
}
|
||||
|
||||
x = count_list(&c->dirty_list);
|
||||
if (x) {
|
||||
rotateby = pseudo_random % x;
|
||||
D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby));
|
||||
|
||||
rotate_list((&c->dirty_list), rotateby);
|
||||
|
||||
D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n",
|
||||
list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset));
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n"));
|
||||
}
|
||||
|
||||
x = count_list(&c->erasable_list);
|
||||
if (x) {
|
||||
rotateby = pseudo_random % x;
|
||||
D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby));
|
||||
|
||||
rotate_list((&c->erasable_list), rotateby);
|
||||
|
||||
D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n",
|
||||
list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset));
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n"));
|
||||
}
|
||||
|
||||
if (c->nr_erasing_blocks) {
|
||||
rotateby = pseudo_random % c->nr_erasing_blocks;
|
||||
D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby));
|
||||
|
||||
rotate_list((&c->erase_pending_list), rotateby);
|
||||
|
||||
D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n",
|
||||
list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset));
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n"));
|
||||
}
|
||||
|
||||
if (c->nr_free_blocks) {
|
||||
rotateby = pseudo_random % c->nr_free_blocks;
|
||||
D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby));
|
||||
|
||||
rotate_list((&c->free_list), rotateby);
|
||||
|
||||
D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n",
|
||||
list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset));
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Not rotating empty free_list\n"));
|
||||
}
|
||||
}
|
365
fs/jffs2/super.c
Normální soubor
365
fs/jffs2/super.c
Normální soubor
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* 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: super.c,v 1.104 2004/11/23 15:37:31 gleixner Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/namei.h>
|
||||
#include "compr.h"
|
||||
#include "nodelist.h"
|
||||
|
||||
static void jffs2_put_super(struct super_block *);
|
||||
|
||||
static kmem_cache_t *jffs2_inode_cachep;
|
||||
|
||||
static struct inode *jffs2_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct jffs2_inode_info *ei;
|
||||
ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL);
|
||||
if (!ei)
|
||||
return NULL;
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void jffs2_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
|
||||
}
|
||||
|
||||
static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
|
||||
{
|
||||
struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo;
|
||||
|
||||
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
|
||||
SLAB_CTOR_CONSTRUCTOR) {
|
||||
init_MUTEX_LOCKED(&ei->sem);
|
||||
inode_init_once(&ei->vfs_inode);
|
||||
}
|
||||
}
|
||||
|
||||
static int jffs2_sync_fs(struct super_block *sb, int wait)
|
||||
{
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
|
||||
|
||||
down(&c->alloc_sem);
|
||||
jffs2_flush_wbuf_pad(c);
|
||||
up(&c->alloc_sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct super_operations jffs2_super_operations =
|
||||
{
|
||||
.alloc_inode = jffs2_alloc_inode,
|
||||
.destroy_inode =jffs2_destroy_inode,
|
||||
.read_inode = jffs2_read_inode,
|
||||
.put_super = jffs2_put_super,
|
||||
.write_super = jffs2_write_super,
|
||||
.statfs = jffs2_statfs,
|
||||
.remount_fs = jffs2_remount_fs,
|
||||
.clear_inode = jffs2_clear_inode,
|
||||
.dirty_inode = jffs2_dirty_inode,
|
||||
.sync_fs = jffs2_sync_fs,
|
||||
};
|
||||
|
||||
static int jffs2_sb_compare(struct super_block *sb, void *data)
|
||||
{
|
||||
struct jffs2_sb_info *p = data;
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
|
||||
|
||||
/* The superblocks are considered to be equivalent if the underlying MTD
|
||||
device is the same one */
|
||||
if (c->mtd == p->mtd) {
|
||||
D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name));
|
||||
return 1;
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n",
|
||||
c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int jffs2_sb_set(struct super_block *sb, void *data)
|
||||
{
|
||||
struct jffs2_sb_info *p = data;
|
||||
|
||||
/* For persistence of NFS exports etc. we use the same s_dev
|
||||
each time we mount the device, don't just use an anonymous
|
||||
device */
|
||||
sb->s_fs_info = p;
|
||||
p->os_priv = sb;
|
||||
sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *data, struct mtd_info *mtd)
|
||||
{
|
||||
struct super_block *sb;
|
||||
struct jffs2_sb_info *c;
|
||||
int ret;
|
||||
|
||||
c = kmalloc(sizeof(*c), GFP_KERNEL);
|
||||
if (!c)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
memset(c, 0, sizeof(*c));
|
||||
c->mtd = mtd;
|
||||
|
||||
sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
|
||||
|
||||
if (IS_ERR(sb))
|
||||
goto out_put;
|
||||
|
||||
if (sb->s_root) {
|
||||
/* New mountpoint for JFFS2 which is already mounted */
|
||||
D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n",
|
||||
mtd->index, mtd->name));
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
|
||||
mtd->index, mtd->name));
|
||||
|
||||
sb->s_op = &jffs2_super_operations;
|
||||
sb->s_flags = flags | MS_NOATIME;
|
||||
|
||||
ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
|
||||
|
||||
if (ret) {
|
||||
/* Failure case... */
|
||||
up_write(&sb->s_umount);
|
||||
deactivate_super(sb);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
sb->s_flags |= MS_ACTIVE;
|
||||
return sb;
|
||||
|
||||
out_put:
|
||||
kfree(c);
|
||||
put_mtd_device(mtd);
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *data, int mtdnr)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
|
||||
mtd = get_mtd_device(NULL, mtdnr);
|
||||
if (!mtd) {
|
||||
D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr));
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
|
||||
}
|
||||
|
||||
static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *data)
|
||||
{
|
||||
int err;
|
||||
struct nameidata nd;
|
||||
int mtdnr;
|
||||
|
||||
if (!dev_name)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name));
|
||||
|
||||
/* The preferred way of mounting in future; especially when
|
||||
CONFIG_BLK_DEV is implemented - we specify the underlying
|
||||
MTD device by number or by name, so that we don't require
|
||||
block device support to be present in the kernel. */
|
||||
|
||||
/* FIXME: How to do the root fs this way? */
|
||||
|
||||
if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') {
|
||||
/* Probably mounting without the blkdev crap */
|
||||
if (dev_name[3] == ':') {
|
||||
struct mtd_info *mtd;
|
||||
|
||||
/* Mount by MTD device name */
|
||||
D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4));
|
||||
for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) {
|
||||
mtd = get_mtd_device(NULL, mtdnr);
|
||||
if (mtd) {
|
||||
if (!strcmp(mtd->name, dev_name+4))
|
||||
return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
|
||||
put_mtd_device(mtd);
|
||||
}
|
||||
}
|
||||
printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4);
|
||||
} else if (isdigit(dev_name[3])) {
|
||||
/* Mount by MTD device number name */
|
||||
char *endptr;
|
||||
|
||||
mtdnr = simple_strtoul(dev_name+3, &endptr, 0);
|
||||
if (!*endptr) {
|
||||
/* It was a valid number */
|
||||
D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr));
|
||||
return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Try the old way - the hack where we allowed users to mount
|
||||
/dev/mtdblock$(n) but didn't actually _use_ the blkdev */
|
||||
|
||||
err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n",
|
||||
err, nd.dentry->d_inode));
|
||||
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
err = -EINVAL;
|
||||
|
||||
if (!S_ISBLK(nd.dentry->d_inode->i_mode))
|
||||
goto out;
|
||||
|
||||
if (nd.mnt->mnt_flags & MNT_NODEV) {
|
||||
err = -EACCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) {
|
||||
if (!(flags & MS_VERBOSE)) /* Yes I mean this. Strangely */
|
||||
printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n",
|
||||
dev_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mtdnr = iminor(nd.dentry->d_inode);
|
||||
path_release(&nd);
|
||||
|
||||
return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
|
||||
|
||||
out:
|
||||
path_release(&nd);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void jffs2_put_super (struct super_block *sb)
|
||||
{
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
|
||||
|
||||
D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
|
||||
|
||||
if (!(sb->s_flags & MS_RDONLY))
|
||||
jffs2_stop_garbage_collect_thread(c);
|
||||
down(&c->alloc_sem);
|
||||
jffs2_flush_wbuf_pad(c);
|
||||
up(&c->alloc_sem);
|
||||
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);
|
||||
jffs2_flash_cleanup(c);
|
||||
kfree(c->inocache_list);
|
||||
if (c->mtd->sync)
|
||||
c->mtd->sync(c->mtd);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
|
||||
}
|
||||
|
||||
static void jffs2_kill_sb(struct super_block *sb)
|
||||
{
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
|
||||
generic_shutdown_super(sb);
|
||||
put_mtd_device(c->mtd);
|
||||
kfree(c);
|
||||
}
|
||||
|
||||
static struct file_system_type jffs2_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "jffs2",
|
||||
.get_sb = jffs2_get_sb,
|
||||
.kill_sb = jffs2_kill_sb,
|
||||
};
|
||||
|
||||
static int __init init_jffs2_fs(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk(KERN_INFO "JFFS2 version 2.2."
|
||||
#ifdef CONFIG_JFFS2_FS_NAND
|
||||
" (NAND)"
|
||||
#endif
|
||||
" (C) 2001-2003 Red Hat, Inc.\n");
|
||||
|
||||
jffs2_inode_cachep = kmem_cache_create("jffs2_i",
|
||||
sizeof(struct jffs2_inode_info),
|
||||
0, SLAB_RECLAIM_ACCOUNT,
|
||||
jffs2_i_init_once, NULL);
|
||||
if (!jffs2_inode_cachep) {
|
||||
printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = jffs2_compressors_init();
|
||||
if (ret) {
|
||||
printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
|
||||
goto out;
|
||||
}
|
||||
ret = jffs2_create_slab_caches();
|
||||
if (ret) {
|
||||
printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
|
||||
goto out_compressors;
|
||||
}
|
||||
ret = register_filesystem(&jffs2_fs_type);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
|
||||
goto out_slab;
|
||||
}
|
||||
return 0;
|
||||
|
||||
out_slab:
|
||||
jffs2_destroy_slab_caches();
|
||||
out_compressors:
|
||||
jffs2_compressors_exit();
|
||||
out:
|
||||
kmem_cache_destroy(jffs2_inode_cachep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit exit_jffs2_fs(void)
|
||||
{
|
||||
unregister_filesystem(&jffs2_fs_type);
|
||||
jffs2_destroy_slab_caches();
|
||||
jffs2_compressors_exit();
|
||||
kmem_cache_destroy(jffs2_inode_cachep);
|
||||
}
|
||||
|
||||
module_init(init_jffs2_fs);
|
||||
module_exit(exit_jffs2_fs);
|
||||
|
||||
MODULE_DESCRIPTION("The Journalling Flash File System, v2");
|
||||
MODULE_AUTHOR("Red Hat, Inc.");
|
||||
MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for
|
||||
// the sake of this tag. It's Free Software.
|
45
fs/jffs2/symlink.c
Normální soubor
45
fs/jffs2/symlink.c
Normální soubor
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2001, 2002 Red Hat, Inc.
|
||||
*
|
||||
* Created by David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: symlink.c,v 1.14 2004/11/16 20:36:12 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
|
||||
static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd);
|
||||
|
||||
struct inode_operations jffs2_symlink_inode_operations =
|
||||
{
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = jffs2_follow_link,
|
||||
.put_link = jffs2_put_link,
|
||||
.setattr = jffs2_setattr
|
||||
};
|
||||
|
||||
static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
unsigned char *buf;
|
||||
buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
|
||||
nd_set_link(nd, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
char *s = nd_get_link(nd);
|
||||
if (!IS_ERR(s))
|
||||
kfree(s);
|
||||
}
|
1184
fs/jffs2/wbuf.c
Normální soubor
1184
fs/jffs2/wbuf.c
Normální soubor
Rozdílový obsah nebyl zobrazen, protože je příliš veliký
Načíst rozdílové porovnání
708
fs/jffs2/write.c
Normální soubor
708
fs/jffs2/write.c
Normální soubor
@@ -0,0 +1,708 @@
|
||||
/*
|
||||
* 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: write.c,v 1.87 2004/11/16 20:36:12 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include "nodelist.h"
|
||||
#include "compr.h"
|
||||
|
||||
|
||||
int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
|
||||
{
|
||||
struct jffs2_inode_cache *ic;
|
||||
|
||||
ic = jffs2_alloc_inode_cache();
|
||||
if (!ic) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(ic, 0, sizeof(*ic));
|
||||
|
||||
f->inocache = ic;
|
||||
f->inocache->nlink = 1;
|
||||
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
|
||||
f->inocache->ino = ++c->highest_ino;
|
||||
f->inocache->state = INO_STATE_PRESENT;
|
||||
|
||||
ri->ino = cpu_to_je32(f->inocache->ino);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
|
||||
jffs2_add_ino_cache(c, f->inocache);
|
||||
|
||||
ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
|
||||
ri->totlen = cpu_to_je32(PAD(sizeof(*ri)));
|
||||
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
|
||||
ri->mode = cpu_to_jemode(mode);
|
||||
|
||||
f->highest_version = 1;
|
||||
ri->version = cpu_to_je32(f->highest_version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_JFFS2_FS_DEBUG > 0
|
||||
static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
|
||||
{
|
||||
unsigned char buf[16];
|
||||
size_t retlen;
|
||||
int ret, i;
|
||||
|
||||
ret = jffs2_flash_read(c, ofs, 16, &retlen, buf);
|
||||
if (ret || (retlen != 16)) {
|
||||
D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen));
|
||||
return;
|
||||
}
|
||||
ret = 0;
|
||||
for (i=0; i<16; i++) {
|
||||
if (buf[i] != 0xff)
|
||||
ret = 1;
|
||||
}
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs);
|
||||
printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
ofs,
|
||||
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
|
||||
buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it,
|
||||
write it to the flash, link it into the existing inode/fragment list */
|
||||
|
||||
struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode)
|
||||
|
||||
{
|
||||
struct jffs2_raw_node_ref *raw;
|
||||
struct jffs2_full_dnode *fn;
|
||||
size_t retlen;
|
||||
struct kvec vecs[2];
|
||||
int ret;
|
||||
int retried = 0;
|
||||
unsigned long cnt = 2;
|
||||
|
||||
D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
|
||||
printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n");
|
||||
BUG();
|
||||
}
|
||||
);
|
||||
vecs[0].iov_base = ri;
|
||||
vecs[0].iov_len = sizeof(*ri);
|
||||
vecs[1].iov_base = (unsigned char *)data;
|
||||
vecs[1].iov_len = datalen;
|
||||
|
||||
D1(writecheck(c, flash_ofs));
|
||||
|
||||
if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) {
|
||||
printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen);
|
||||
}
|
||||
raw = jffs2_alloc_raw_node_ref();
|
||||
if (!raw)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fn = jffs2_alloc_full_dnode();
|
||||
if (!fn) {
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
fn->ofs = je32_to_cpu(ri->offset);
|
||||
fn->size = je32_to_cpu(ri->dsize);
|
||||
fn->frags = 0;
|
||||
|
||||
/* check number of valid vecs */
|
||||
if (!datalen || !data)
|
||||
cnt = 1;
|
||||
retry:
|
||||
fn->raw = raw;
|
||||
|
||||
raw->flash_offset = flash_ofs;
|
||||
raw->__totlen = PAD(sizeof(*ri)+datalen);
|
||||
raw->next_phys = NULL;
|
||||
|
||||
ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
|
||||
(alloc_mode==ALLOC_GC)?0:f->inocache->ino);
|
||||
|
||||
if (ret || (retlen != sizeof(*ri) + datalen)) {
|
||||
printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
|
||||
sizeof(*ri)+datalen, flash_ofs, ret, retlen);
|
||||
|
||||
/* Mark the space as dirtied */
|
||||
if (retlen) {
|
||||
/* Doesn't belong to any inode */
|
||||
raw->next_in_ino = NULL;
|
||||
|
||||
/* Don't change raw->size to match retlen. We may have
|
||||
written the node header already, and only the data will
|
||||
seem corrupted, in which case the scan would skip over
|
||||
any node we write before the original intended end of
|
||||
this node */
|
||||
raw->flash_offset |= REF_OBSOLETE;
|
||||
jffs2_add_physical_node_ref(c, raw);
|
||||
jffs2_mark_node_obsolete(c, raw);
|
||||
} else {
|
||||
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
}
|
||||
if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) {
|
||||
/* Try to reallocate space and retry */
|
||||
uint32_t dummy;
|
||||
struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
|
||||
|
||||
retried = 1;
|
||||
|
||||
D1(printk(KERN_DEBUG "Retrying failed write.\n"));
|
||||
|
||||
ACCT_SANITY_CHECK(c,jeb);
|
||||
D1(ACCT_PARANOIA_CHECK(jeb));
|
||||
|
||||
if (alloc_mode == ALLOC_GC) {
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy);
|
||||
} else {
|
||||
/* Locking pain */
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode);
|
||||
down(&f->sem);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
|
||||
|
||||
ACCT_SANITY_CHECK(c,jeb);
|
||||
D1(ACCT_PARANOIA_CHECK(jeb));
|
||||
|
||||
goto retry;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
}
|
||||
/* Release the full_dnode which is now useless, and return */
|
||||
jffs2_free_full_dnode(fn);
|
||||
return ERR_PTR(ret?ret:-EIO);
|
||||
}
|
||||
/* Mark the space used */
|
||||
/* If node covers at least a whole page, or if it starts at the
|
||||
beginning of a page and runs to the end of the file, or if
|
||||
it's a hole node, mark it REF_PRISTINE, else REF_NORMAL.
|
||||
*/
|
||||
if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) ||
|
||||
( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) &&
|
||||
(je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) {
|
||||
raw->flash_offset |= REF_PRISTINE;
|
||||
} else {
|
||||
raw->flash_offset |= REF_NORMAL;
|
||||
}
|
||||
jffs2_add_physical_node_ref(c, raw);
|
||||
|
||||
/* Link into per-inode list */
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
raw->next_in_ino = f->inocache->nodes;
|
||||
f->inocache->nodes = raw;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
|
||||
flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize),
|
||||
je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc),
|
||||
je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen)));
|
||||
|
||||
if (retried) {
|
||||
ACCT_SANITY_CHECK(c,NULL);
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode)
|
||||
{
|
||||
struct jffs2_raw_node_ref *raw;
|
||||
struct jffs2_full_dirent *fd;
|
||||
size_t retlen;
|
||||
struct kvec vecs[2];
|
||||
int retried = 0;
|
||||
int ret;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%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)));
|
||||
D1(writecheck(c, flash_ofs));
|
||||
|
||||
D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
|
||||
printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
|
||||
BUG();
|
||||
}
|
||||
);
|
||||
|
||||
vecs[0].iov_base = rd;
|
||||
vecs[0].iov_len = sizeof(*rd);
|
||||
vecs[1].iov_base = (unsigned char *)name;
|
||||
vecs[1].iov_len = namelen;
|
||||
|
||||
raw = jffs2_alloc_raw_node_ref();
|
||||
|
||||
if (!raw)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fd = jffs2_alloc_full_dirent(namelen+1);
|
||||
if (!fd) {
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
fd->version = je32_to_cpu(rd->version);
|
||||
fd->ino = je32_to_cpu(rd->ino);
|
||||
fd->nhash = full_name_hash(name, strlen(name));
|
||||
fd->type = rd->type;
|
||||
memcpy(fd->name, name, namelen);
|
||||
fd->name[namelen]=0;
|
||||
|
||||
retry:
|
||||
fd->raw = raw;
|
||||
|
||||
raw->flash_offset = flash_ofs;
|
||||
raw->__totlen = PAD(sizeof(*rd)+namelen);
|
||||
raw->next_phys = NULL;
|
||||
|
||||
ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
|
||||
(alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino));
|
||||
if (ret || (retlen != sizeof(*rd) + namelen)) {
|
||||
printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
|
||||
sizeof(*rd)+namelen, flash_ofs, ret, retlen);
|
||||
/* Mark the space as dirtied */
|
||||
if (retlen) {
|
||||
raw->next_in_ino = NULL;
|
||||
raw->flash_offset |= REF_OBSOLETE;
|
||||
jffs2_add_physical_node_ref(c, raw);
|
||||
jffs2_mark_node_obsolete(c, raw);
|
||||
} else {
|
||||
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
}
|
||||
if (!retried && (raw = jffs2_alloc_raw_node_ref())) {
|
||||
/* Try to reallocate space and retry */
|
||||
uint32_t dummy;
|
||||
struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
|
||||
|
||||
retried = 1;
|
||||
|
||||
D1(printk(KERN_DEBUG "Retrying failed write.\n"));
|
||||
|
||||
ACCT_SANITY_CHECK(c,jeb);
|
||||
D1(ACCT_PARANOIA_CHECK(jeb));
|
||||
|
||||
if (alloc_mode == ALLOC_GC) {
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy);
|
||||
} else {
|
||||
/* Locking pain */
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode);
|
||||
down(&f->sem);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
|
||||
ACCT_SANITY_CHECK(c,jeb);
|
||||
D1(ACCT_PARANOIA_CHECK(jeb));
|
||||
goto retry;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
}
|
||||
/* Release the full_dnode which is now useless, and return */
|
||||
jffs2_free_full_dirent(fd);
|
||||
return ERR_PTR(ret?ret:-EIO);
|
||||
}
|
||||
/* Mark the space used */
|
||||
raw->flash_offset |= REF_PRISTINE;
|
||||
jffs2_add_physical_node_ref(c, raw);
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
raw->next_in_ino = f->inocache->nodes;
|
||||
f->inocache->nodes = raw;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
if (retried) {
|
||||
ACCT_SANITY_CHECK(c,NULL);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that
|
||||
we don't have to go digging in struct inode or its equivalent. It should set:
|
||||
mode, uid, gid, (starting)isize, atime, ctime, mtime */
|
||||
int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
struct jffs2_raw_inode *ri, unsigned char *buf,
|
||||
uint32_t offset, uint32_t writelen, uint32_t *retlen)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t writtenlen = 0;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_write_inode_range(): Ino #%u, ofs 0x%x, len 0x%x\n",
|
||||
f->inocache->ino, offset, writelen));
|
||||
|
||||
while(writelen) {
|
||||
struct jffs2_full_dnode *fn;
|
||||
unsigned char *comprbuf = NULL;
|
||||
uint16_t comprtype = JFFS2_COMPR_NONE;
|
||||
uint32_t phys_ofs, alloclen;
|
||||
uint32_t datalen, cdatalen;
|
||||
int retried = 0;
|
||||
|
||||
retry:
|
||||
D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
if (ret) {
|
||||
D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
|
||||
break;
|
||||
}
|
||||
down(&f->sem);
|
||||
datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));
|
||||
cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);
|
||||
|
||||
comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen);
|
||||
|
||||
ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
|
||||
ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen);
|
||||
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
ri->ino = cpu_to_je32(f->inocache->ino);
|
||||
ri->version = cpu_to_je32(++f->highest_version);
|
||||
ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen));
|
||||
ri->offset = cpu_to_je32(offset);
|
||||
ri->csize = cpu_to_je32(cdatalen);
|
||||
ri->dsize = cpu_to_je32(datalen);
|
||||
ri->compr = comprtype & 0xff;
|
||||
ri->usercompr = (comprtype >> 8 ) & 0xff;
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
|
||||
|
||||
fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
|
||||
|
||||
jffs2_free_comprbuf(comprbuf, buf);
|
||||
|
||||
if (IS_ERR(fn)) {
|
||||
ret = PTR_ERR(fn);
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
if (!retried) {
|
||||
/* Write error to be retried */
|
||||
retried = 1;
|
||||
D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n"));
|
||||
goto retry;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ret = jffs2_add_full_dnode_to_inode(c, f, fn);
|
||||
if (f->metadata) {
|
||||
jffs2_mark_node_obsolete(c, f->metadata->raw);
|
||||
jffs2_free_full_dnode(f->metadata);
|
||||
f->metadata = NULL;
|
||||
}
|
||||
if (ret) {
|
||||
/* Eep */
|
||||
D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
|
||||
jffs2_mark_node_obsolete(c, fn->raw);
|
||||
jffs2_free_full_dnode(fn);
|
||||
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
break;
|
||||
}
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
if (!datalen) {
|
||||
printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
|
||||
writtenlen += datalen;
|
||||
offset += datalen;
|
||||
writelen -= datalen;
|
||||
buf += datalen;
|
||||
}
|
||||
*retlen = writtenlen;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen)
|
||||
{
|
||||
struct jffs2_raw_dirent *rd;
|
||||
struct jffs2_full_dnode *fn;
|
||||
struct jffs2_full_dirent *fd;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
int ret;
|
||||
|
||||
/* Try to reserve enough space for both node and dirent.
|
||||
* Just the node will do for now, though
|
||||
*/
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen));
|
||||
if (ret) {
|
||||
up(&f->sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ri->data_crc = cpu_to_je32(0);
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
|
||||
fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
|
||||
jemode_to_cpu(ri->mode)));
|
||||
|
||||
if (IS_ERR(fn)) {
|
||||
D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
|
||||
/* Eeek. Wave bye bye */
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
return PTR_ERR(fn);
|
||||
}
|
||||
/* No data here. Only a metadata node, which will be
|
||||
obsoleted by the first data write
|
||||
*/
|
||||
f->metadata = fn;
|
||||
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
|
||||
if (ret) {
|
||||
/* Eep. */
|
||||
D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
rd = jffs2_alloc_raw_dirent();
|
||||
if (!rd) {
|
||||
/* Argh. Now we treat it like a normal delete */
|
||||
jffs2_complete_reservation(c);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
down(&dir_f->sem);
|
||||
|
||||
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
|
||||
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
|
||||
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
rd->pino = cpu_to_je32(dir_f->inocache->ino);
|
||||
rd->version = cpu_to_je32(++dir_f->highest_version);
|
||||
rd->ino = ri->ino;
|
||||
rd->mctime = ri->ctime;
|
||||
rd->nsize = namelen;
|
||||
rd->type = DT_REG;
|
||||
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
|
||||
rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
|
||||
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
|
||||
|
||||
jffs2_free_raw_dirent(rd);
|
||||
|
||||
if (IS_ERR(fd)) {
|
||||
/* dirent failed to write. Delete the inode normally
|
||||
as if it were the final unlink() */
|
||||
jffs2_complete_reservation(c);
|
||||
up(&dir_f->sem);
|
||||
return PTR_ERR(fd);
|
||||
}
|
||||
|
||||
/* Link the fd into the inode's list, obsoleting an old
|
||||
one if necessary. */
|
||||
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
|
||||
|
||||
jffs2_complete_reservation(c);
|
||||
up(&dir_f->sem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
|
||||
const char *name, int namelen, struct jffs2_inode_info *dead_f)
|
||||
{
|
||||
struct jffs2_raw_dirent *rd;
|
||||
struct jffs2_full_dirent *fd;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
int ret;
|
||||
|
||||
if (1 /* alternative branch needs testing */ ||
|
||||
!jffs2_can_mark_obsolete(c)) {
|
||||
/* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
|
||||
|
||||
rd = jffs2_alloc_raw_dirent();
|
||||
if (!rd)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
|
||||
if (ret) {
|
||||
jffs2_free_raw_dirent(rd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
down(&dir_f->sem);
|
||||
|
||||
/* Build a deletion node */
|
||||
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
|
||||
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
|
||||
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
rd->pino = cpu_to_je32(dir_f->inocache->ino);
|
||||
rd->version = cpu_to_je32(++dir_f->highest_version);
|
||||
rd->ino = cpu_to_je32(0);
|
||||
rd->mctime = cpu_to_je32(get_seconds());
|
||||
rd->nsize = namelen;
|
||||
rd->type = DT_UNKNOWN;
|
||||
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
|
||||
rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
|
||||
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
|
||||
|
||||
jffs2_free_raw_dirent(rd);
|
||||
|
||||
if (IS_ERR(fd)) {
|
||||
jffs2_complete_reservation(c);
|
||||
up(&dir_f->sem);
|
||||
return PTR_ERR(fd);
|
||||
}
|
||||
|
||||
/* File it. This will mark the old one obsolete. */
|
||||
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
|
||||
up(&dir_f->sem);
|
||||
} else {
|
||||
struct jffs2_full_dirent **prev = &dir_f->dents;
|
||||
uint32_t nhash = full_name_hash(name, namelen);
|
||||
|
||||
down(&dir_f->sem);
|
||||
|
||||
while ((*prev) && (*prev)->nhash <= nhash) {
|
||||
if ((*prev)->nhash == nhash &&
|
||||
!memcmp((*prev)->name, name, namelen) &&
|
||||
!(*prev)->name[namelen]) {
|
||||
struct jffs2_full_dirent *this = *prev;
|
||||
|
||||
D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n",
|
||||
this->ino, ref_offset(this->raw)));
|
||||
|
||||
*prev = this->next;
|
||||
jffs2_mark_node_obsolete(c, (this->raw));
|
||||
jffs2_free_full_dirent(this);
|
||||
break;
|
||||
}
|
||||
prev = &((*prev)->next);
|
||||
}
|
||||
up(&dir_f->sem);
|
||||
}
|
||||
|
||||
/* dead_f is NULL if this was a rename not a real unlink */
|
||||
/* Also catch the !f->inocache case, where there was a dirent
|
||||
pointing to an inode which didn't exist. */
|
||||
if (dead_f && dead_f->inocache) {
|
||||
|
||||
down(&dead_f->sem);
|
||||
|
||||
while (dead_f->dents) {
|
||||
/* There can be only deleted ones */
|
||||
fd = dead_f->dents;
|
||||
|
||||
dead_f->dents = fd->next;
|
||||
|
||||
if (fd->ino) {
|
||||
printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
|
||||
dead_f->inocache->ino, fd->name, fd->ino);
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino));
|
||||
}
|
||||
jffs2_mark_node_obsolete(c, fd->raw);
|
||||
jffs2_free_full_dirent(fd);
|
||||
}
|
||||
|
||||
dead_f->inocache->nlink--;
|
||||
/* NB: Caller must set inode nlink if appropriate */
|
||||
up(&dead_f->sem);
|
||||
}
|
||||
|
||||
jffs2_complete_reservation(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen)
|
||||
{
|
||||
struct jffs2_raw_dirent *rd;
|
||||
struct jffs2_full_dirent *fd;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
int ret;
|
||||
|
||||
rd = jffs2_alloc_raw_dirent();
|
||||
if (!rd)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
if (ret) {
|
||||
jffs2_free_raw_dirent(rd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
down(&dir_f->sem);
|
||||
|
||||
/* Build a deletion node */
|
||||
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
|
||||
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
|
||||
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
rd->pino = cpu_to_je32(dir_f->inocache->ino);
|
||||
rd->version = cpu_to_je32(++dir_f->highest_version);
|
||||
rd->ino = cpu_to_je32(ino);
|
||||
rd->mctime = cpu_to_je32(get_seconds());
|
||||
rd->nsize = namelen;
|
||||
|
||||
rd->type = type;
|
||||
|
||||
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
|
||||
rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
|
||||
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
|
||||
|
||||
jffs2_free_raw_dirent(rd);
|
||||
|
||||
if (IS_ERR(fd)) {
|
||||
jffs2_complete_reservation(c);
|
||||
up(&dir_f->sem);
|
||||
return PTR_ERR(fd);
|
||||
}
|
||||
|
||||
/* File it. This will mark the old one obsolete. */
|
||||
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
|
||||
|
||||
jffs2_complete_reservation(c);
|
||||
up(&dir_f->sem);
|
||||
|
||||
return 0;
|
||||
}
|
50
fs/jffs2/writev.c
Normální soubor
50
fs/jffs2/writev.c
Normální soubor
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2001, 2002 Red Hat, Inc.
|
||||
*
|
||||
* Created by David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: writev.c,v 1.6 2004/11/16 20:36:12 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
/* This ought to be in core MTD code. All registered MTD devices
|
||||
without writev should have this put in place. Bug the MTD
|
||||
maintainer */
|
||||
static inline int mtd_fake_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen)
|
||||
{
|
||||
unsigned long i;
|
||||
size_t totlen = 0, thislen;
|
||||
int ret = 0;
|
||||
|
||||
for (i=0; i<count; i++) {
|
||||
if (!vecs[i].iov_len)
|
||||
continue;
|
||||
ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
|
||||
totlen += thislen;
|
||||
if (ret || thislen != vecs[i].iov_len)
|
||||
break;
|
||||
to += vecs[i].iov_len;
|
||||
}
|
||||
if (retlen)
|
||||
*retlen = totlen;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen)
|
||||
{
|
||||
if (c->mtd->writev)
|
||||
return c->mtd->writev(c->mtd, vecs, count, to, retlen);
|
||||
else
|
||||
return mtd_fake_writev(c->mtd, vecs, count, to, retlen);
|
||||
}
|
||||
|
Odkázat v novém úkolu
Zablokovat Uživatele