Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
35
fs/jffs2/LICENCE
Normal file
35
fs/jffs2/LICENCE
Normal file
@@ -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.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
21
fs/jffs2/Makefile
Normal file
21
fs/jffs2/Makefile
Normal file
@@ -0,0 +1,21 @@
|
||||
#
|
||||
# Makefile for the Linux Journalling Flash File System v2 (JFFS2)
|
||||
#
|
||||
# $Id: Makefile,v 1.1.1.1 2007/06/12 07:27:13 eyryu 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 debug.o
|
||||
|
||||
jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o
|
||||
jffs2-$(CONFIG_JFFS2_FS_XATTR) += xattr.o xattr_trusted.o xattr_user.o
|
||||
jffs2-$(CONFIG_JFFS2_FS_SECURITY) += security.o
|
||||
jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += acl.o
|
||||
jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
|
||||
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
|
||||
jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
|
||||
jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o
|
||||
173
fs/jffs2/README.Locking
Normal file
173
fs/jffs2/README.Locking
Normal file
@@ -0,0 +1,173 @@
|
||||
$Id: README.Locking,v 1.1.1.1 2007/06/12 07:27:13 eyryu 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.
|
||||
|
||||
This spinlock also covers allocation of new inode numbers, which is
|
||||
currently just '++->highest_ino++', but might one day get more complicated
|
||||
if we need to deal with wrapping after 4 milliard inode numbers are used.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
c->xattr_sem
|
||||
------------
|
||||
|
||||
This read/write semaphore protects against concurrent access to the
|
||||
xattr related objects which include stuff in superblock and ic->xref.
|
||||
In read-only path, write-semaphore is too much exclusion. It's enough
|
||||
by read-semaphore. But you must hold write-semaphore when updating,
|
||||
creating or deleting any xattr related object.
|
||||
|
||||
Once xattr_sem released, there would be no assurance for the existence
|
||||
of those objects. Thus, a series of processes is often required to retry,
|
||||
when updating such a object is necessary under holding read semaphore.
|
||||
For example, do_jffs2_getxattr() holds read-semaphore to scan xref and
|
||||
xdatum at first. But it retries this process with holding write-semaphore
|
||||
after release read-semaphore, if it's necessary to load name/value pair
|
||||
from medium.
|
||||
|
||||
Ordering constraints:
|
||||
Lock xattr_sem last, after the alloc_sem.
|
||||
40
fs/jffs2/TODO
Normal file
40
fs/jffs2/TODO
Normal file
@@ -0,0 +1,40 @@
|
||||
$Id: TODO,v 1.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
|
||||
- support asynchronous operation -- add a per-fs 'reserved_space' count,
|
||||
let each outstanding write reserve the _maximum_ amount of physical
|
||||
space it could take. Let GC flush the outstanding writes because the
|
||||
reservations will necessarily be pessimistic. With this we could even
|
||||
do shared writable mmap, if we can have a fs hook for do_wp_page() to
|
||||
make the reservation.
|
||||
- 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:
|
||||
- almost done :)
|
||||
- use bad block check instead of the hardwired byte check
|
||||
|
||||
- Optimisations:
|
||||
- Split writes so they go to two separate blocks rather than just c->nextblock.
|
||||
By writing _new_ nodes to one block, and garbage-collected REF_PRISTINE
|
||||
nodes to a different one, we can separate clean nodes from those which
|
||||
are likely to become dirty, and end up with blocks which are each far
|
||||
closer to 100% or 0% clean, hence speeding up later GC progress dramatically.
|
||||
- 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.
|
||||
|
||||
dedekind:
|
||||
1. __jffs2_flush_wbuf() has a strange 'pad' parameter. Eliminate.
|
||||
2. get_sb()->build_fs()->scan() path... Why get_sb() removes scan()'s crap in
|
||||
case of failure? scan() does not clean everything. Fix.
|
||||
486
fs/jffs2/acl.c
Normal file
486
fs/jffs2/acl.c
Normal file
@@ -0,0 +1,486 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2006 NEC Corporation
|
||||
*
|
||||
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
static size_t jffs2_acl_size(int count)
|
||||
{
|
||||
if (count <= 4) {
|
||||
return sizeof(struct jffs2_acl_header)
|
||||
+ count * sizeof(struct jffs2_acl_entry_short);
|
||||
} else {
|
||||
return sizeof(struct jffs2_acl_header)
|
||||
+ 4 * sizeof(struct jffs2_acl_entry_short)
|
||||
+ (count - 4) * sizeof(struct jffs2_acl_entry);
|
||||
}
|
||||
}
|
||||
|
||||
static int jffs2_acl_count(size_t size)
|
||||
{
|
||||
size_t s;
|
||||
|
||||
size -= sizeof(struct jffs2_acl_header);
|
||||
s = size - 4 * sizeof(struct jffs2_acl_entry_short);
|
||||
if (s < 0) {
|
||||
if (size % sizeof(struct jffs2_acl_entry_short))
|
||||
return -1;
|
||||
return size / sizeof(struct jffs2_acl_entry_short);
|
||||
} else {
|
||||
if (s % sizeof(struct jffs2_acl_entry))
|
||||
return -1;
|
||||
return s / sizeof(struct jffs2_acl_entry) + 4;
|
||||
}
|
||||
}
|
||||
|
||||
static struct posix_acl *jffs2_acl_from_medium(void *value, size_t size)
|
||||
{
|
||||
void *end = value + size;
|
||||
struct jffs2_acl_header *header = value;
|
||||
struct jffs2_acl_entry *entry;
|
||||
struct posix_acl *acl;
|
||||
uint32_t ver;
|
||||
int i, count;
|
||||
|
||||
if (!value)
|
||||
return NULL;
|
||||
if (size < sizeof(struct jffs2_acl_header))
|
||||
return ERR_PTR(-EINVAL);
|
||||
ver = je32_to_cpu(header->a_version);
|
||||
if (ver != JFFS2_ACL_VERSION) {
|
||||
JFFS2_WARNING("Invalid ACL version. (=%u)\n", ver);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
value += sizeof(struct jffs2_acl_header);
|
||||
count = jffs2_acl_count(size);
|
||||
if (count < 0)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (count == 0)
|
||||
return NULL;
|
||||
|
||||
acl = posix_acl_alloc(count, GFP_KERNEL);
|
||||
if (!acl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i=0; i < count; i++) {
|
||||
entry = value;
|
||||
if (value + sizeof(struct jffs2_acl_entry_short) > end)
|
||||
goto fail;
|
||||
acl->a_entries[i].e_tag = je16_to_cpu(entry->e_tag);
|
||||
acl->a_entries[i].e_perm = je16_to_cpu(entry->e_perm);
|
||||
switch (acl->a_entries[i].e_tag) {
|
||||
case ACL_USER_OBJ:
|
||||
case ACL_GROUP_OBJ:
|
||||
case ACL_MASK:
|
||||
case ACL_OTHER:
|
||||
value += sizeof(struct jffs2_acl_entry_short);
|
||||
acl->a_entries[i].e_id = ACL_UNDEFINED_ID;
|
||||
break;
|
||||
|
||||
case ACL_USER:
|
||||
case ACL_GROUP:
|
||||
value += sizeof(struct jffs2_acl_entry);
|
||||
if (value > end)
|
||||
goto fail;
|
||||
acl->a_entries[i].e_id = je32_to_cpu(entry->e_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (value != end)
|
||||
goto fail;
|
||||
return acl;
|
||||
fail:
|
||||
posix_acl_release(acl);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size)
|
||||
{
|
||||
struct jffs2_acl_header *header;
|
||||
struct jffs2_acl_entry *entry;
|
||||
void *e;
|
||||
size_t i;
|
||||
|
||||
*size = jffs2_acl_size(acl->a_count);
|
||||
header = kmalloc(sizeof(*header) + acl->a_count * sizeof(*entry), GFP_KERNEL);
|
||||
if (!header)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
header->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
|
||||
e = header + 1;
|
||||
for (i=0; i < acl->a_count; i++) {
|
||||
entry = e;
|
||||
entry->e_tag = cpu_to_je16(acl->a_entries[i].e_tag);
|
||||
entry->e_perm = cpu_to_je16(acl->a_entries[i].e_perm);
|
||||
switch(acl->a_entries[i].e_tag) {
|
||||
case ACL_USER:
|
||||
case ACL_GROUP:
|
||||
entry->e_id = cpu_to_je32(acl->a_entries[i].e_id);
|
||||
e += sizeof(struct jffs2_acl_entry);
|
||||
break;
|
||||
|
||||
case ACL_USER_OBJ:
|
||||
case ACL_GROUP_OBJ:
|
||||
case ACL_MASK:
|
||||
case ACL_OTHER:
|
||||
e += sizeof(struct jffs2_acl_entry_short);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return header;
|
||||
fail:
|
||||
kfree(header);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static struct posix_acl *jffs2_iget_acl(struct inode *inode, struct posix_acl **i_acl)
|
||||
{
|
||||
struct posix_acl *acl = JFFS2_ACL_NOT_CACHED;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
if (*i_acl != JFFS2_ACL_NOT_CACHED)
|
||||
acl = posix_acl_dup(*i_acl);
|
||||
spin_unlock(&inode->i_lock);
|
||||
return acl;
|
||||
}
|
||||
|
||||
static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct posix_acl *acl)
|
||||
{
|
||||
spin_lock(&inode->i_lock);
|
||||
if (*i_acl != JFFS2_ACL_NOT_CACHED)
|
||||
posix_acl_release(*i_acl);
|
||||
*i_acl = posix_acl_dup(acl);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
static struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
struct posix_acl *acl;
|
||||
char *value = NULL;
|
||||
int rc, xprefix;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
acl = jffs2_iget_acl(inode, &f->i_acl_access);
|
||||
if (acl != JFFS2_ACL_NOT_CACHED)
|
||||
return acl;
|
||||
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
acl = jffs2_iget_acl(inode, &f->i_acl_default);
|
||||
if (acl != JFFS2_ACL_NOT_CACHED)
|
||||
return acl;
|
||||
xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
rc = do_jffs2_getxattr(inode, xprefix, "", NULL, 0);
|
||||
if (rc > 0) {
|
||||
value = kmalloc(rc, GFP_KERNEL);
|
||||
if (!value)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
rc = do_jffs2_getxattr(inode, xprefix, "", value, rc);
|
||||
}
|
||||
if (rc > 0) {
|
||||
acl = jffs2_acl_from_medium(value, rc);
|
||||
} else if (rc == -ENODATA || rc == -ENOSYS) {
|
||||
acl = NULL;
|
||||
} else {
|
||||
acl = ERR_PTR(rc);
|
||||
}
|
||||
if (value)
|
||||
kfree(value);
|
||||
if (!IS_ERR(acl)) {
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
jffs2_iset_acl(inode, &f->i_acl_access, acl);
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
jffs2_iset_acl(inode, &f->i_acl_default, acl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return acl;
|
||||
}
|
||||
|
||||
static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
size_t size = 0;
|
||||
char *value = NULL;
|
||||
int rc, xprefix;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
|
||||
if (acl) {
|
||||
mode_t mode = inode->i_mode;
|
||||
rc = posix_acl_equiv_mode(acl, &mode);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (inode->i_mode != mode) {
|
||||
inode->i_mode = mode;
|
||||
jffs2_dirty_inode(inode);
|
||||
}
|
||||
if (rc == 0)
|
||||
acl = NULL;
|
||||
}
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return acl ? -EACCES : 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (acl) {
|
||||
value = jffs2_acl_to_medium(acl, &size);
|
||||
if (IS_ERR(value))
|
||||
return PTR_ERR(value);
|
||||
}
|
||||
|
||||
rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0);
|
||||
if (!value && rc == -ENODATA)
|
||||
rc = 0;
|
||||
if (value)
|
||||
kfree(value);
|
||||
if (!rc) {
|
||||
switch(type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
jffs2_iset_acl(inode, &f->i_acl_access, acl);
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
jffs2_iset_acl(inode, &f->i_acl_default, acl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jffs2_check_acl(struct inode *inode, int mask)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int rc;
|
||||
|
||||
acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl) {
|
||||
rc = posix_acl_permission(inode, acl, mask);
|
||||
posix_acl_release(acl);
|
||||
return rc;
|
||||
}
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
|
||||
{
|
||||
return generic_permission(inode, mask, jffs2_check_acl);
|
||||
}
|
||||
|
||||
int jffs2_init_acl(struct inode *inode, struct inode *dir)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
struct posix_acl *acl = NULL, *clone;
|
||||
mode_t mode;
|
||||
int rc = 0;
|
||||
|
||||
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
|
||||
f->i_acl_default = JFFS2_ACL_NOT_CACHED;
|
||||
if (!S_ISLNK(inode->i_mode)) {
|
||||
acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (!acl)
|
||||
inode->i_mode &= ~current->fs->umask;
|
||||
}
|
||||
if (acl) {
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
|
||||
if (rc)
|
||||
goto cleanup;
|
||||
}
|
||||
clone = posix_acl_clone(acl, GFP_KERNEL);
|
||||
rc = -ENOMEM;
|
||||
if (!clone)
|
||||
goto cleanup;
|
||||
mode = inode->i_mode;
|
||||
rc = posix_acl_create_masq(clone, &mode);
|
||||
if (rc >= 0) {
|
||||
inode->i_mode = mode;
|
||||
if (rc > 0)
|
||||
rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
|
||||
}
|
||||
posix_acl_release(clone);
|
||||
}
|
||||
cleanup:
|
||||
posix_acl_release(acl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void jffs2_clear_acl(struct jffs2_inode_info *f)
|
||||
{
|
||||
if (f->i_acl_access && f->i_acl_access != JFFS2_ACL_NOT_CACHED) {
|
||||
posix_acl_release(f->i_acl_access);
|
||||
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
|
||||
}
|
||||
if (f->i_acl_default && f->i_acl_default != JFFS2_ACL_NOT_CACHED) {
|
||||
posix_acl_release(f->i_acl_default);
|
||||
f->i_acl_default = JFFS2_ACL_NOT_CACHED;
|
||||
}
|
||||
}
|
||||
|
||||
int jffs2_acl_chmod(struct inode *inode)
|
||||
{
|
||||
struct posix_acl *acl, *clone;
|
||||
int rc;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(acl) || !acl)
|
||||
return PTR_ERR(acl);
|
||||
clone = posix_acl_clone(acl, GFP_KERNEL);
|
||||
posix_acl_release(acl);
|
||||
if (!clone)
|
||||
return -ENOMEM;
|
||||
rc = posix_acl_chmod_masq(clone, inode->i_mode);
|
||||
if (!rc)
|
||||
rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
|
||||
posix_acl_release(clone);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size,
|
||||
const char *name, size_t name_len)
|
||||
{
|
||||
const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS);
|
||||
|
||||
if (list && retlen <= list_size)
|
||||
strcpy(list, POSIX_ACL_XATTR_ACCESS);
|
||||
return retlen;
|
||||
}
|
||||
|
||||
static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size,
|
||||
const char *name, size_t name_len)
|
||||
{
|
||||
const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT);
|
||||
|
||||
if (list && retlen <= list_size)
|
||||
strcpy(list, POSIX_ACL_XATTR_DEFAULT);
|
||||
return retlen;
|
||||
}
|
||||
|
||||
static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int rc;
|
||||
|
||||
acl = jffs2_get_acl(inode, type);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (!acl)
|
||||
return -ENODATA;
|
||||
rc = posix_acl_to_xattr(acl, buffer, size);
|
||||
posix_acl_release(acl);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
|
||||
{
|
||||
if (name[0] != '\0')
|
||||
return -EINVAL;
|
||||
return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size);
|
||||
}
|
||||
|
||||
static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
|
||||
{
|
||||
if (name[0] != '\0')
|
||||
return -EINVAL;
|
||||
return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
|
||||
}
|
||||
|
||||
static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int rc;
|
||||
|
||||
if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
|
||||
return -EPERM;
|
||||
|
||||
if (value) {
|
||||
acl = posix_acl_from_xattr(value, size);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl) {
|
||||
rc = posix_acl_valid(acl);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
acl = NULL;
|
||||
}
|
||||
rc = jffs2_set_acl(inode, type, acl);
|
||||
out:
|
||||
posix_acl_release(acl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jffs2_acl_access_setxattr(struct inode *inode, const char *name,
|
||||
const void *buffer, size_t size, int flags)
|
||||
{
|
||||
if (name[0] != '\0')
|
||||
return -EINVAL;
|
||||
return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size);
|
||||
}
|
||||
|
||||
static int jffs2_acl_default_setxattr(struct inode *inode, const char *name,
|
||||
const void *buffer, size_t size, int flags)
|
||||
{
|
||||
if (name[0] != '\0')
|
||||
return -EINVAL;
|
||||
return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
|
||||
}
|
||||
|
||||
struct xattr_handler jffs2_acl_access_xattr_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_ACCESS,
|
||||
.list = jffs2_acl_access_listxattr,
|
||||
.get = jffs2_acl_access_getxattr,
|
||||
.set = jffs2_acl_access_setxattr,
|
||||
};
|
||||
|
||||
struct xattr_handler jffs2_acl_default_xattr_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_DEFAULT,
|
||||
.list = jffs2_acl_default_listxattr,
|
||||
.get = jffs2_acl_default_getxattr,
|
||||
.set = jffs2_acl_default_setxattr,
|
||||
};
|
||||
45
fs/jffs2/acl.h
Normal file
45
fs/jffs2/acl.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2006 NEC Corporation
|
||||
*
|
||||
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
*/
|
||||
struct jffs2_acl_entry {
|
||||
jint16_t e_tag;
|
||||
jint16_t e_perm;
|
||||
jint32_t e_id;
|
||||
};
|
||||
|
||||
struct jffs2_acl_entry_short {
|
||||
jint16_t e_tag;
|
||||
jint16_t e_perm;
|
||||
};
|
||||
|
||||
struct jffs2_acl_header {
|
||||
jint32_t a_version;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
|
||||
|
||||
#define JFFS2_ACL_NOT_CACHED ((void *)-1)
|
||||
|
||||
extern int jffs2_permission(struct inode *, int, struct nameidata *);
|
||||
extern int jffs2_acl_chmod(struct inode *);
|
||||
extern int jffs2_init_acl(struct inode *, struct inode *);
|
||||
extern void jffs2_clear_acl(struct jffs2_inode_info *);
|
||||
|
||||
extern struct xattr_handler jffs2_acl_access_xattr_handler;
|
||||
extern struct xattr_handler jffs2_acl_default_xattr_handler;
|
||||
|
||||
#else
|
||||
|
||||
#define jffs2_permission NULL
|
||||
#define jffs2_acl_chmod(inode) (0)
|
||||
#define jffs2_init_acl(inode,dir) (0)
|
||||
#define jffs2_clear_acl(f)
|
||||
|
||||
#endif /* CONFIG_JFFS2_FS_POSIX_ACL */
|
||||
150
fs/jffs2/background.c
Normal file
150
fs/jffs2/background.c
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/freezer.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;
|
||||
|
||||
BUG_ON(c->gc_task);
|
||||
|
||||
init_completion(&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));
|
||||
wait_for_completion(&c->gc_thread_start);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
|
||||
{
|
||||
int wait = 0;
|
||||
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);
|
||||
wait = 1;
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
if (wait)
|
||||
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;
|
||||
complete(&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())
|
||||
continue;
|
||||
|
||||
/* This thread is purely an optimisation. But if it runs when
|
||||
other things could be running, it actually makes things a
|
||||
lot worse. Use yield() and put it at the back of the runqueue
|
||||
every time. Especially during boot, pulling an inode in
|
||||
with read_inode() is much preferable to having the GC thread
|
||||
get there first. */
|
||||
yield();
|
||||
|
||||
/* 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);
|
||||
}
|
||||
374
fs/jffs2/build.c
Normal file
374
fs/jffs2/build.c
Normal file
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu 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 void jffs2_build_inode_pass1(struct jffs2_sb_info *c,
|
||||
struct jffs2_inode_cache *ic)
|
||||
{
|
||||
struct jffs2_full_dirent *fd;
|
||||
|
||||
dbg_fsbuild("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;
|
||||
|
||||
/* we can get high latency here with huge directories */
|
||||
|
||||
child_ic = jffs2_get_ino_cache(c, fd->ino);
|
||||
if (!child_ic) {
|
||||
dbg_fsbuild("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) {
|
||||
JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n",
|
||||
fd->name, fd->ino, ic->ino);
|
||||
/* TODO: What do we do about it? */
|
||||
}
|
||||
dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino);
|
||||
/* Can't free scan_dents so far. 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;
|
||||
|
||||
dbg_fsbuild("build FS data structures\n");
|
||||
|
||||
/* First, scan the medium and build all the inode caches with
|
||||
lists of physical nodes */
|
||||
|
||||
c->flags |= JFFS2_SB_FLAG_SCANNING;
|
||||
ret = jffs2_scan_medium(c);
|
||||
c->flags &= ~JFFS2_SB_FLAG_SCANNING;
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
dbg_fsbuild("scanned flash completely\n");
|
||||
jffs2_dbg_dump_block_lists_nolock(c);
|
||||
|
||||
dbg_fsbuild("pass 1 starting\n");
|
||||
c->flags |= JFFS2_SB_FLAG_BUILDING;
|
||||
/* Now scan the directory tree, increasing nlink according to every dirent found. */
|
||||
for_each_inode(i, c, ic) {
|
||||
if (ic->scan_dents) {
|
||||
jffs2_build_inode_pass1(c, ic);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
dbg_fsbuild("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. */
|
||||
dbg_fsbuild("pass 2 starting\n");
|
||||
|
||||
for_each_inode(i, c, ic) {
|
||||
if (ic->nlink)
|
||||
continue;
|
||||
|
||||
jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
dbg_fsbuild("pass 2a starting\n");
|
||||
|
||||
while (dead_fds) {
|
||||
fd = dead_fds;
|
||||
dead_fds = fd->next;
|
||||
|
||||
ic = jffs2_get_ino_cache(c, fd->ino);
|
||||
|
||||
if (ic)
|
||||
jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
|
||||
jffs2_free_full_dirent(fd);
|
||||
}
|
||||
|
||||
dbg_fsbuild("pass 2a complete\n");
|
||||
dbg_fsbuild("freeing temporary data structures\n");
|
||||
|
||||
/* Finally, we can scan again and free the dirent structs */
|
||||
for_each_inode(i, c, ic) {
|
||||
while(ic->scan_dents) {
|
||||
fd = ic->scan_dents;
|
||||
ic->scan_dents = fd->next;
|
||||
jffs2_free_full_dirent(fd);
|
||||
}
|
||||
ic->scan_dents = NULL;
|
||||
cond_resched();
|
||||
}
|
||||
jffs2_build_xattr_subsystem(c);
|
||||
c->flags &= ~JFFS2_SB_FLAG_BUILDING;
|
||||
|
||||
dbg_fsbuild("FS build complete\n");
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
jffs2_clear_xattr_subsystem(c);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
dbg_fsbuild("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;
|
||||
dbg_fsbuild("obsoleting node at 0x%08x\n", ref_offset(raw));
|
||||
jffs2_mark_node_obsolete(c, raw);
|
||||
raw = next;
|
||||
}
|
||||
|
||||
if (ic->scan_dents) {
|
||||
int whinged = 0;
|
||||
dbg_fsbuild("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 */
|
||||
dbg_fsbuild("child \"%s\" is a deletion dirent, skipping...\n", fd->name);
|
||||
jffs2_free_full_dirent(fd);
|
||||
continue;
|
||||
}
|
||||
if (!whinged)
|
||||
whinged = 1;
|
||||
|
||||
dbg_fsbuild("removing child \"%s\", ino #%u\n", fd->name, fd->ino);
|
||||
|
||||
child_ic = jffs2_get_ino_cache(c, fd->ino);
|
||||
if (!child_ic) {
|
||||
dbg_fsbuild("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) {
|
||||
dbg_fsbuild("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 {
|
||||
dbg_fsbuild("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);
|
||||
|
||||
dbg_fsbuild("JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
|
||||
c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks);
|
||||
dbg_fsbuild("Blocks required to allow deletion: %d (%d KiB)\n",
|
||||
c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024);
|
||||
dbg_fsbuild("Blocks required to allow writes: %d (%d KiB)\n",
|
||||
c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024);
|
||||
dbg_fsbuild("Blocks required to quiesce GC thread: %d (%d KiB)\n",
|
||||
c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024);
|
||||
dbg_fsbuild("Blocks required to allow GC merges: %d (%d KiB)\n",
|
||||
c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024);
|
||||
dbg_fsbuild("Blocks required to GC bad blocks: %d (%d KiB)\n",
|
||||
c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024);
|
||||
dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n",
|
||||
c->nospc_dirty_size);
|
||||
}
|
||||
|
||||
int jffs2_do_mount_fs(struct jffs2_sb_info *c)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
int size;
|
||||
|
||||
c->free_size = c->flash_size;
|
||||
c->nr_blocks = c->flash_size / c->sector_size;
|
||||
size = sizeof(struct jffs2_eraseblock) * c->nr_blocks;
|
||||
#ifndef __ECOS
|
||||
if (jffs2_blocks_use_vmalloc(c))
|
||||
c->blocks = vmalloc(size);
|
||||
else
|
||||
#endif
|
||||
c->blocks = kmalloc(size, GFP_KERNEL);
|
||||
if (!c->blocks)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(c->blocks, 0, size);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
c->summary = NULL;
|
||||
|
||||
ret = jffs2_sum_init(c);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
if (jffs2_build_filesystem(c)) {
|
||||
dbg_fsbuild("build_fs failed\n");
|
||||
jffs2_free_ino_caches(c);
|
||||
jffs2_free_raw_node_refs(c);
|
||||
ret = -EIO;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
jffs2_calc_trigger_levels(c);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
#ifndef __ECOS
|
||||
if (jffs2_blocks_use_vmalloc(c))
|
||||
vfree(c->blocks);
|
||||
else
|
||||
#endif
|
||||
kfree(c->blocks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
457
fs/jffs2/compr.c
Normal file
457
fs/jffs2/compr.c
Normal file
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu 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 __init 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
|
||||
/* 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_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;
|
||||
}
|
||||
107
fs/jffs2/compr.h
Normal file
107
fs/jffs2/compr.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu 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 "jffs2_fs_i.h"
|
||||
#include "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
|
||||
|
||||
#endif /* __JFFS2_COMPR_H__ */
|
||||
132
fs/jffs2/compr_rtime.c
Normal file
132
fs/jffs2/compr_rtime.c
Normal file
@@ -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.1.1.1 2007/06/12 07:27:13 eyryu 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);
|
||||
}
|
||||
378
fs/jffs2/compr_rubin.c
Normal file
378
fs/jffs2/compr_rubin.c
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu 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
|
||||
static 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static 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
Normal file
21
fs/jffs2/compr_rubin.h
Normal file
@@ -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.1.1.1 2007/06/12 07:27:13 eyryu 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];
|
||||
};
|
||||
221
fs/jffs2/compr_zlib.c
Normal file
221
fs/jffs2/compr_zlib.c
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(__KERNEL__) && !defined(__ECOS)
|
||||
#error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
|
||||
#endif
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/zlib.h>
|
||||
#include <linux/zutil.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 DEFINE_MUTEX(deflate_mutex);
|
||||
static DEFINE_MUTEX(inflate_mutex);
|
||||
static z_stream inf_strm, def_strm;
|
||||
|
||||
#ifdef __KERNEL__ /* Linux-only */
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.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__ */
|
||||
|
||||
static 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;
|
||||
|
||||
mutex_lock(&deflate_mutex);
|
||||
|
||||
if (Z_OK != zlib_deflateInit(&def_strm, 3)) {
|
||||
printk(KERN_WARNING "deflateInit failed\n");
|
||||
mutex_unlock(&deflate_mutex);
|
||||
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);
|
||||
mutex_unlock(&deflate_mutex);
|
||||
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:
|
||||
mutex_unlock(&deflate_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static 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;
|
||||
|
||||
mutex_lock(&inflate_mutex);
|
||||
|
||||
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");
|
||||
mutex_unlock(&inflate_mutex);
|
||||
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);
|
||||
mutex_unlock(&inflate_mutex);
|
||||
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
Normal file
307
fs/jffs2/comprtest.c
Normal file
@@ -0,0 +1,307 @@
|
||||
/* $Id: comprtest.c,v 1.1.1.1 2007/06/12 07:27:13 eyryu 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;
|
||||
}
|
||||
705
fs/jffs2/debug.c
Normal file
705
fs/jffs2/debug.c
Normal file
@@ -0,0 +1,705 @@
|
||||
/*
|
||||
* 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: debug.c,v 1.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include "nodelist.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef JFFS2_DBG_SANITY_CHECKS
|
||||
|
||||
void
|
||||
__jffs2_dbg_acct_sanity_check_nolock(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
if (unlikely(jeb && jeb->used_size + jeb->dirty_size +
|
||||
jeb->free_size + jeb->wasted_size +
|
||||
jeb->unchecked_size != c->sector_size)) {
|
||||
JFFS2_ERROR("eeep, space accounting for block at 0x%08x is screwed.\n", jeb->offset);
|
||||
JFFS2_ERROR("free %#08x + dirty %#08x + used %#08x + wasted %#08x + unchecked %#08x != total %#08x.\n",
|
||||
jeb->free_size, jeb->dirty_size, jeb->used_size,
|
||||
jeb->wasted_size, jeb->unchecked_size, c->sector_size);
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (unlikely(c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size
|
||||
+ c->wasted_size + c->unchecked_size != c->flash_size)) {
|
||||
JFFS2_ERROR("eeep, space accounting superblock info is screwed.\n");
|
||||
JFFS2_ERROR("free %#08x + dirty %#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();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
__jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
jffs2_dbg_acct_sanity_check_nolock(c, jeb);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
#endif /* JFFS2_DBG_SANITY_CHECKS */
|
||||
|
||||
#ifdef JFFS2_DBG_PARANOIA_CHECKS
|
||||
/*
|
||||
* Check the fragtree.
|
||||
*/
|
||||
void
|
||||
__jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f)
|
||||
{
|
||||
down(&f->sem);
|
||||
__jffs2_dbg_fragtree_paranoia_check_nolock(f);
|
||||
up(&f->sem);
|
||||
}
|
||||
|
||||
void
|
||||
__jffs2_dbg_fragtree_paranoia_check_nolock(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) {
|
||||
JFFS2_ERROR("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) {
|
||||
JFFS2_ERROR("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) {
|
||||
JFFS2_ERROR("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) {
|
||||
JFFS2_ERROR("fragtree is corrupted.\n");
|
||||
__jffs2_dbg_dump_fragtree_nolock(f);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the flash contains all 0xFF before we start writing.
|
||||
*/
|
||||
void
|
||||
__jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c,
|
||||
uint32_t ofs, int len)
|
||||
{
|
||||
size_t retlen;
|
||||
int ret, i;
|
||||
unsigned char *buf;
|
||||
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
|
||||
if (ret || (retlen != len)) {
|
||||
JFFS2_WARNING("read %d bytes failed or short. ret %d, retlen %zd.\n",
|
||||
len, ret, retlen);
|
||||
kfree(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
for (i = 0; i < len; i++)
|
||||
if (buf[i] != 0xff)
|
||||
ret = 1;
|
||||
|
||||
if (ret) {
|
||||
JFFS2_ERROR("argh, about to write node to %#08x on flash, but there are data already there. The first corrupted byte is at %#08x offset.\n",
|
||||
ofs, ofs + i);
|
||||
__jffs2_dbg_dump_buffer(buf, len, ofs);
|
||||
kfree(buf);
|
||||
BUG();
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'.
|
||||
*/
|
||||
void
|
||||
__jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
__jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
void
|
||||
__jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
uint32_t my_used_size = 0;
|
||||
uint32_t my_unchecked_size = 0;
|
||||
uint32_t my_dirty_size = 0;
|
||||
struct jffs2_raw_node_ref *ref2 = jeb->first_node;
|
||||
|
||||
while (ref2) {
|
||||
uint32_t totlen = ref_totlen(c, jeb, ref2);
|
||||
|
||||
if (ref_offset(ref2) < jeb->offset ||
|
||||
ref_offset(ref2) > jeb->offset + c->sector_size) {
|
||||
JFFS2_ERROR("node_ref %#08x shouldn't be in block at %#08x.\n",
|
||||
ref_offset(ref2), jeb->offset);
|
||||
goto error;
|
||||
|
||||
}
|
||||
if (ref_flags(ref2) == REF_UNCHECKED)
|
||||
my_unchecked_size += totlen;
|
||||
else if (!ref_obsolete(ref2))
|
||||
my_used_size += totlen;
|
||||
else
|
||||
my_dirty_size += totlen;
|
||||
|
||||
if ((!ref_next(ref2)) != (ref2 == jeb->last_node)) {
|
||||
JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next at %#08x (mem %p), last_node is at %#08x (mem %p).\n",
|
||||
ref_offset(ref2), ref2, ref_offset(ref_next(ref2)), ref_next(ref2),
|
||||
ref_offset(jeb->last_node), jeb->last_node);
|
||||
goto error;
|
||||
}
|
||||
ref2 = ref_next(ref2);
|
||||
}
|
||||
|
||||
if (my_used_size != jeb->used_size) {
|
||||
JFFS2_ERROR("Calculated used size %#08x != stored used size %#08x.\n",
|
||||
my_used_size, jeb->used_size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (my_unchecked_size != jeb->unchecked_size) {
|
||||
JFFS2_ERROR("Calculated unchecked size %#08x != stored unchecked size %#08x.\n",
|
||||
my_unchecked_size, jeb->unchecked_size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* This should work when we implement ref->__totlen elemination */
|
||||
if (my_dirty_size != jeb->dirty_size + jeb->wasted_size) {
|
||||
JFFS2_ERROR("Calculated dirty+wasted size %#08x != stored dirty + wasted size %#08x\n",
|
||||
my_dirty_size, jeb->dirty_size + jeb->wasted_size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (jeb->free_size == 0
|
||||
&& my_used_size + my_unchecked_size + my_dirty_size != c->sector_size) {
|
||||
JFFS2_ERROR("The sum of all nodes in block (%#x) != size of block (%#x)\n",
|
||||
my_used_size + my_unchecked_size + my_dirty_size,
|
||||
c->sector_size);
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
__jffs2_dbg_dump_node_refs_nolock(c, jeb);
|
||||
__jffs2_dbg_dump_jeb_nolock(jeb);
|
||||
__jffs2_dbg_dump_block_lists_nolock(c);
|
||||
BUG();
|
||||
|
||||
}
|
||||
#endif /* JFFS2_DBG_PARANOIA_CHECKS */
|
||||
|
||||
#if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS)
|
||||
/*
|
||||
* Dump the node_refs of the 'jeb' JFFS2 eraseblock.
|
||||
*/
|
||||
void
|
||||
__jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
__jffs2_dbg_dump_node_refs_nolock(c, jeb);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
void
|
||||
__jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
struct jffs2_raw_node_ref *ref;
|
||||
int i = 0;
|
||||
|
||||
printk(JFFS2_DBG_MSG_PREFIX " Dump node_refs of the eraseblock %#08x\n", jeb->offset);
|
||||
if (!jeb->first_node) {
|
||||
printk(JFFS2_DBG_MSG_PREFIX " no nodes in the eraseblock %#08x\n", jeb->offset);
|
||||
return;
|
||||
}
|
||||
|
||||
printk(JFFS2_DBG);
|
||||
for (ref = jeb->first_node; ; ref = ref_next(ref)) {
|
||||
printk("%#08x(%#x)", ref_offset(ref), ref->__totlen);
|
||||
if (ref_next(ref))
|
||||
printk("->");
|
||||
else
|
||||
break;
|
||||
if (++i == 4) {
|
||||
i = 0;
|
||||
printk("\n" JFFS2_DBG);
|
||||
}
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump an eraseblock's space accounting.
|
||||
*/
|
||||
void
|
||||
__jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
__jffs2_dbg_dump_jeb_nolock(jeb);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
void
|
||||
__jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
if (!jeb)
|
||||
return;
|
||||
|
||||
printk(JFFS2_DBG_MSG_PREFIX " dump space accounting for the eraseblock at %#08x:\n",
|
||||
jeb->offset);
|
||||
|
||||
printk(JFFS2_DBG "used_size: %#08x\n", jeb->used_size);
|
||||
printk(JFFS2_DBG "dirty_size: %#08x\n", jeb->dirty_size);
|
||||
printk(JFFS2_DBG "wasted_size: %#08x\n", jeb->wasted_size);
|
||||
printk(JFFS2_DBG "unchecked_size: %#08x\n", jeb->unchecked_size);
|
||||
printk(JFFS2_DBG "free_size: %#08x\n", jeb->free_size);
|
||||
}
|
||||
|
||||
void
|
||||
__jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c)
|
||||
{
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
__jffs2_dbg_dump_block_lists_nolock(c);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
void
|
||||
__jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c)
|
||||
{
|
||||
printk(JFFS2_DBG_MSG_PREFIX " dump JFFS2 blocks lists:\n");
|
||||
|
||||
printk(JFFS2_DBG "flash_size: %#08x\n", c->flash_size);
|
||||
printk(JFFS2_DBG "used_size: %#08x\n", c->used_size);
|
||||
printk(JFFS2_DBG "dirty_size: %#08x\n", c->dirty_size);
|
||||
printk(JFFS2_DBG "wasted_size: %#08x\n", c->wasted_size);
|
||||
printk(JFFS2_DBG "unchecked_size: %#08x\n", c->unchecked_size);
|
||||
printk(JFFS2_DBG "free_size: %#08x\n", c->free_size);
|
||||
printk(JFFS2_DBG "erasing_size: %#08x\n", c->erasing_size);
|
||||
printk(JFFS2_DBG "bad_size: %#08x\n", c->bad_size);
|
||||
printk(JFFS2_DBG "sector_size: %#08x\n", c->sector_size);
|
||||
printk(JFFS2_DBG "jffs2_reserved_blocks size: %#08x\n",
|
||||
c->sector_size * c->resv_blocks_write);
|
||||
|
||||
if (c->nextblock)
|
||||
printk(JFFS2_DBG "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(JFFS2_DBG "nextblock: NULL\n");
|
||||
|
||||
if (c->gcblock)
|
||||
printk(JFFS2_DBG "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(JFFS2_DBG "gcblock: NULL\n");
|
||||
|
||||
if (list_empty(&c->clean_list)) {
|
||||
printk(JFFS2_DBG "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;
|
||||
if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
|
||||
printk(JFFS2_DBG "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 (JFFS2_DBG "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(JFFS2_DBG "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;
|
||||
if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
|
||||
printk(JFFS2_DBG "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 (JFFS2_DBG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
|
||||
numblocks, dirty, dirty / numblocks);
|
||||
}
|
||||
|
||||
if (list_empty(&c->dirty_list)) {
|
||||
printk(JFFS2_DBG "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;
|
||||
if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
|
||||
printk(JFFS2_DBG "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 (JFFS2_DBG "contains %d blocks with total dirty size %u, average dirty size: %u\n",
|
||||
numblocks, dirty, dirty / numblocks);
|
||||
}
|
||||
|
||||
if (list_empty(&c->erasable_list)) {
|
||||
printk(JFFS2_DBG "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);
|
||||
|
||||
if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
|
||||
printk(JFFS2_DBG "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(JFFS2_DBG "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);
|
||||
|
||||
if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
|
||||
printk(JFFS2_DBG "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(JFFS2_DBG "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);
|
||||
|
||||
if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
|
||||
printk(JFFS2_DBG "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(JFFS2_DBG "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);
|
||||
|
||||
if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
|
||||
printk(JFFS2_DBG "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(JFFS2_DBG "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);
|
||||
|
||||
if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
|
||||
printk(JFFS2_DBG "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(JFFS2_DBG "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);
|
||||
|
||||
if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
|
||||
printk(JFFS2_DBG "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(JFFS2_DBG "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);
|
||||
|
||||
if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
|
||||
printk(JFFS2_DBG "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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
__jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f)
|
||||
{
|
||||
down(&f->sem);
|
||||
jffs2_dbg_dump_fragtree_nolock(f);
|
||||
up(&f->sem);
|
||||
}
|
||||
|
||||
void
|
||||
__jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f)
|
||||
{
|
||||
struct jffs2_node_frag *this = frag_first(&f->fragtree);
|
||||
uint32_t lastofs = 0;
|
||||
int buggy = 0;
|
||||
|
||||
printk(JFFS2_DBG_MSG_PREFIX " dump fragtree of ino #%u\n", f->inocache->ino);
|
||||
while(this) {
|
||||
if (this->node)
|
||||
printk(JFFS2_DBG "frag %#04x-%#04x: %#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(JFFS2_DBG "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 (f->metadata)
|
||||
printk(JFFS2_DBG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
|
||||
|
||||
if (buggy) {
|
||||
JFFS2_ERROR("frag tree got a hole in it.\n");
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
#define JFFS2_BUFDUMP_BYTES_PER_LINE 32
|
||||
void
|
||||
__jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs)
|
||||
{
|
||||
int skip;
|
||||
int i;
|
||||
|
||||
printk(JFFS2_DBG_MSG_PREFIX " dump from offset %#08x to offset %#08x (%x bytes).\n",
|
||||
offs, offs + len, len);
|
||||
i = skip = offs % JFFS2_BUFDUMP_BYTES_PER_LINE;
|
||||
offs = offs & ~(JFFS2_BUFDUMP_BYTES_PER_LINE - 1);
|
||||
|
||||
if (skip != 0)
|
||||
printk(JFFS2_DBG "%#08x: ", offs);
|
||||
|
||||
while (skip--)
|
||||
printk(" ");
|
||||
|
||||
while (i < len) {
|
||||
if ((i % JFFS2_BUFDUMP_BYTES_PER_LINE) == 0 && i != len -1) {
|
||||
if (i != 0)
|
||||
printk("\n");
|
||||
offs += JFFS2_BUFDUMP_BYTES_PER_LINE;
|
||||
printk(JFFS2_DBG "%0#8x: ", offs);
|
||||
}
|
||||
|
||||
printk("%02x ", buf[i]);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump a JFFS2 node.
|
||||
*/
|
||||
void
|
||||
__jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs)
|
||||
{
|
||||
union jffs2_node_union node;
|
||||
int len = sizeof(union jffs2_node_union);
|
||||
size_t retlen;
|
||||
uint32_t crc;
|
||||
int ret;
|
||||
|
||||
printk(JFFS2_DBG_MSG_PREFIX " dump node at offset %#08x.\n", ofs);
|
||||
|
||||
ret = jffs2_flash_read(c, ofs, len, &retlen, (unsigned char *)&node);
|
||||
if (ret || (retlen != len)) {
|
||||
JFFS2_ERROR("read %d bytes failed or short. ret %d, retlen %zd.\n",
|
||||
len, ret, retlen);
|
||||
return;
|
||||
}
|
||||
|
||||
printk(JFFS2_DBG "magic:\t%#04x\n", je16_to_cpu(node.u.magic));
|
||||
printk(JFFS2_DBG "nodetype:\t%#04x\n", je16_to_cpu(node.u.nodetype));
|
||||
printk(JFFS2_DBG "totlen:\t%#08x\n", je32_to_cpu(node.u.totlen));
|
||||
printk(JFFS2_DBG "hdr_crc:\t%#08x\n", je32_to_cpu(node.u.hdr_crc));
|
||||
|
||||
crc = crc32(0, &node.u, sizeof(node.u) - 4);
|
||||
if (crc != je32_to_cpu(node.u.hdr_crc)) {
|
||||
JFFS2_ERROR("wrong common header CRC.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (je16_to_cpu(node.u.magic) != JFFS2_MAGIC_BITMASK &&
|
||||
je16_to_cpu(node.u.magic) != JFFS2_OLD_MAGIC_BITMASK)
|
||||
{
|
||||
JFFS2_ERROR("wrong node magic: %#04x instead of %#04x.\n",
|
||||
je16_to_cpu(node.u.magic), JFFS2_MAGIC_BITMASK);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(je16_to_cpu(node.u.nodetype)) {
|
||||
|
||||
case JFFS2_NODETYPE_INODE:
|
||||
|
||||
printk(JFFS2_DBG "the node is inode node\n");
|
||||
printk(JFFS2_DBG "ino:\t%#08x\n", je32_to_cpu(node.i.ino));
|
||||
printk(JFFS2_DBG "version:\t%#08x\n", je32_to_cpu(node.i.version));
|
||||
printk(JFFS2_DBG "mode:\t%#08x\n", node.i.mode.m);
|
||||
printk(JFFS2_DBG "uid:\t%#04x\n", je16_to_cpu(node.i.uid));
|
||||
printk(JFFS2_DBG "gid:\t%#04x\n", je16_to_cpu(node.i.gid));
|
||||
printk(JFFS2_DBG "isize:\t%#08x\n", je32_to_cpu(node.i.isize));
|
||||
printk(JFFS2_DBG "atime:\t%#08x\n", je32_to_cpu(node.i.atime));
|
||||
printk(JFFS2_DBG "mtime:\t%#08x\n", je32_to_cpu(node.i.mtime));
|
||||
printk(JFFS2_DBG "ctime:\t%#08x\n", je32_to_cpu(node.i.ctime));
|
||||
printk(JFFS2_DBG "offset:\t%#08x\n", je32_to_cpu(node.i.offset));
|
||||
printk(JFFS2_DBG "csize:\t%#08x\n", je32_to_cpu(node.i.csize));
|
||||
printk(JFFS2_DBG "dsize:\t%#08x\n", je32_to_cpu(node.i.dsize));
|
||||
printk(JFFS2_DBG "compr:\t%#02x\n", node.i.compr);
|
||||
printk(JFFS2_DBG "usercompr:\t%#02x\n", node.i.usercompr);
|
||||
printk(JFFS2_DBG "flags:\t%#04x\n", je16_to_cpu(node.i.flags));
|
||||
printk(JFFS2_DBG "data_crc:\t%#08x\n", je32_to_cpu(node.i.data_crc));
|
||||
printk(JFFS2_DBG "node_crc:\t%#08x\n", je32_to_cpu(node.i.node_crc));
|
||||
|
||||
crc = crc32(0, &node.i, sizeof(node.i) - 8);
|
||||
if (crc != je32_to_cpu(node.i.node_crc)) {
|
||||
JFFS2_ERROR("wrong node header CRC.\n");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case JFFS2_NODETYPE_DIRENT:
|
||||
|
||||
printk(JFFS2_DBG "the node is dirent node\n");
|
||||
printk(JFFS2_DBG "pino:\t%#08x\n", je32_to_cpu(node.d.pino));
|
||||
printk(JFFS2_DBG "version:\t%#08x\n", je32_to_cpu(node.d.version));
|
||||
printk(JFFS2_DBG "ino:\t%#08x\n", je32_to_cpu(node.d.ino));
|
||||
printk(JFFS2_DBG "mctime:\t%#08x\n", je32_to_cpu(node.d.mctime));
|
||||
printk(JFFS2_DBG "nsize:\t%#02x\n", node.d.nsize);
|
||||
printk(JFFS2_DBG "type:\t%#02x\n", node.d.type);
|
||||
printk(JFFS2_DBG "node_crc:\t%#08x\n", je32_to_cpu(node.d.node_crc));
|
||||
printk(JFFS2_DBG "name_crc:\t%#08x\n", je32_to_cpu(node.d.name_crc));
|
||||
|
||||
node.d.name[node.d.nsize] = '\0';
|
||||
printk(JFFS2_DBG "name:\t\"%s\"\n", node.d.name);
|
||||
|
||||
crc = crc32(0, &node.d, sizeof(node.d) - 8);
|
||||
if (crc != je32_to_cpu(node.d.node_crc)) {
|
||||
JFFS2_ERROR("wrong node header CRC.\n");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(JFFS2_DBG "node type is unknown\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* JFFS2_DBG_DUMPS || JFFS2_DBG_PARANOIA_CHECKS */
|
||||
285
fs/jffs2/debug.h
Normal file
285
fs/jffs2/debug.h
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* 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: debug.h,v 1.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
#ifndef _JFFS2_DEBUG_H_
|
||||
#define _JFFS2_DEBUG_H_
|
||||
|
||||
#include <linux/sched.h>
|
||||
|
||||
#ifndef CONFIG_JFFS2_FS_DEBUG
|
||||
#define CONFIG_JFFS2_FS_DEBUG 0
|
||||
#endif
|
||||
|
||||
#if CONFIG_JFFS2_FS_DEBUG > 0
|
||||
/* Enable "paranoia" checks and dumps */
|
||||
#define JFFS2_DBG_PARANOIA_CHECKS
|
||||
#define JFFS2_DBG_DUMPS
|
||||
|
||||
/*
|
||||
* By defining/undefining the below macros one may select debugging messages
|
||||
* fro specific JFFS2 subsystems.
|
||||
*/
|
||||
#define JFFS2_DBG_READINODE_MESSAGES
|
||||
#define JFFS2_DBG_FRAGTREE_MESSAGES
|
||||
#define JFFS2_DBG_DENTLIST_MESSAGES
|
||||
#define JFFS2_DBG_NODEREF_MESSAGES
|
||||
#define JFFS2_DBG_INOCACHE_MESSAGES
|
||||
#define JFFS2_DBG_SUMMARY_MESSAGES
|
||||
#define JFFS2_DBG_FSBUILD_MESSAGES
|
||||
#endif
|
||||
|
||||
#if CONFIG_JFFS2_FS_DEBUG > 1
|
||||
#define JFFS2_DBG_FRAGTREE2_MESSAGES
|
||||
#define JFFS2_DBG_MEMALLOC_MESSAGES
|
||||
#endif
|
||||
|
||||
/* Sanity checks are supposed to be light-weight and enabled by default */
|
||||
#define JFFS2_DBG_SANITY_CHECKS
|
||||
|
||||
/*
|
||||
* Dx() are mainly used for debugging messages, they must go away and be
|
||||
* superseded by nicer dbg_xxx() macros...
|
||||
*/
|
||||
#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
|
||||
|
||||
/* The prefixes of JFFS2 messages */
|
||||
#define JFFS2_DBG_PREFIX "[JFFS2 DBG]"
|
||||
#define JFFS2_ERR_PREFIX "JFFS2 error:"
|
||||
#define JFFS2_WARN_PREFIX "JFFS2 warning:"
|
||||
#define JFFS2_NOTICE_PREFIX "JFFS2 notice:"
|
||||
|
||||
#define JFFS2_ERR KERN_ERR
|
||||
#define JFFS2_WARN KERN_WARNING
|
||||
#define JFFS2_NOT KERN_NOTICE
|
||||
#define JFFS2_DBG KERN_DEBUG
|
||||
|
||||
#define JFFS2_DBG_MSG_PREFIX JFFS2_DBG JFFS2_DBG_PREFIX
|
||||
#define JFFS2_ERR_MSG_PREFIX JFFS2_ERR JFFS2_ERR_PREFIX
|
||||
#define JFFS2_WARN_MSG_PREFIX JFFS2_WARN JFFS2_WARN_PREFIX
|
||||
#define JFFS2_NOTICE_MSG_PREFIX JFFS2_NOT JFFS2_NOTICE_PREFIX
|
||||
|
||||
/* JFFS2 message macros */
|
||||
#define JFFS2_ERROR(fmt, ...) \
|
||||
do { \
|
||||
printk(JFFS2_ERR_MSG_PREFIX \
|
||||
" (%d) %s: " fmt, current->pid, \
|
||||
__FUNCTION__ , ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define JFFS2_WARNING(fmt, ...) \
|
||||
do { \
|
||||
printk(JFFS2_WARN_MSG_PREFIX \
|
||||
" (%d) %s: " fmt, current->pid, \
|
||||
__FUNCTION__ , ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define JFFS2_NOTICE(fmt, ...) \
|
||||
do { \
|
||||
printk(JFFS2_NOTICE_MSG_PREFIX \
|
||||
" (%d) %s: " fmt, current->pid, \
|
||||
__FUNCTION__ , ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define JFFS2_DEBUG(fmt, ...) \
|
||||
do { \
|
||||
printk(JFFS2_DBG_MSG_PREFIX \
|
||||
" (%d) %s: " fmt, current->pid, \
|
||||
__FUNCTION__ , ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* We split our debugging messages on several parts, depending on the JFFS2
|
||||
* subsystem the message belongs to.
|
||||
*/
|
||||
/* Read inode debugging messages */
|
||||
#ifdef JFFS2_DBG_READINODE_MESSAGES
|
||||
#define dbg_readinode(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_readinode(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Fragtree build debugging messages */
|
||||
#ifdef JFFS2_DBG_FRAGTREE_MESSAGES
|
||||
#define dbg_fragtree(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_fragtree(fmt, ...)
|
||||
#endif
|
||||
#ifdef JFFS2_DBG_FRAGTREE2_MESSAGES
|
||||
#define dbg_fragtree2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_fragtree2(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Directory entry list manilulation debugging messages */
|
||||
#ifdef JFFS2_DBG_DENTLIST_MESSAGES
|
||||
#define dbg_dentlist(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_dentlist(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Print the messages about manipulating node_refs */
|
||||
#ifdef JFFS2_DBG_NODEREF_MESSAGES
|
||||
#define dbg_noderef(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_noderef(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Manipulations with the list of inodes (JFFS2 inocache) */
|
||||
#ifdef JFFS2_DBG_INOCACHE_MESSAGES
|
||||
#define dbg_inocache(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_inocache(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Summary debugging messages */
|
||||
#ifdef JFFS2_DBG_SUMMARY_MESSAGES
|
||||
#define dbg_summary(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_summary(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* File system build messages */
|
||||
#ifdef JFFS2_DBG_FSBUILD_MESSAGES
|
||||
#define dbg_fsbuild(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_fsbuild(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Watch the object allocations */
|
||||
#ifdef JFFS2_DBG_MEMALLOC_MESSAGES
|
||||
#define dbg_memalloc(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_memalloc(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Watch the XATTR subsystem */
|
||||
#ifdef JFFS2_DBG_XATTR_MESSAGES
|
||||
#define dbg_xattr(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_xattr(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* "Sanity" checks */
|
||||
void
|
||||
__jffs2_dbg_acct_sanity_check_nolock(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb);
|
||||
void
|
||||
__jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb);
|
||||
|
||||
/* "Paranoia" checks */
|
||||
void
|
||||
__jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f);
|
||||
void
|
||||
__jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f);
|
||||
void
|
||||
__jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb);
|
||||
void
|
||||
__jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb);
|
||||
void
|
||||
__jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c,
|
||||
uint32_t ofs, int len);
|
||||
|
||||
/* "Dump" functions */
|
||||
void
|
||||
__jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
void
|
||||
__jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb);
|
||||
void
|
||||
__jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c);
|
||||
void
|
||||
__jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c);
|
||||
void
|
||||
__jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb);
|
||||
void
|
||||
__jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb);
|
||||
void
|
||||
__jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f);
|
||||
void
|
||||
__jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f);
|
||||
void
|
||||
__jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs);
|
||||
void
|
||||
__jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs);
|
||||
|
||||
#ifdef JFFS2_DBG_PARANOIA_CHECKS
|
||||
#define jffs2_dbg_fragtree_paranoia_check(f) \
|
||||
__jffs2_dbg_fragtree_paranoia_check(f)
|
||||
#define jffs2_dbg_fragtree_paranoia_check_nolock(f) \
|
||||
__jffs2_dbg_fragtree_paranoia_check_nolock(f)
|
||||
#define jffs2_dbg_acct_paranoia_check(c, jeb) \
|
||||
__jffs2_dbg_acct_paranoia_check(c,jeb)
|
||||
#define jffs2_dbg_acct_paranoia_check_nolock(c, jeb) \
|
||||
__jffs2_dbg_acct_paranoia_check_nolock(c,jeb)
|
||||
#define jffs2_dbg_prewrite_paranoia_check(c, ofs, len) \
|
||||
__jffs2_dbg_prewrite_paranoia_check(c, ofs, len)
|
||||
#else
|
||||
#define jffs2_dbg_fragtree_paranoia_check(f)
|
||||
#define jffs2_dbg_fragtree_paranoia_check_nolock(f)
|
||||
#define jffs2_dbg_acct_paranoia_check(c, jeb)
|
||||
#define jffs2_dbg_acct_paranoia_check_nolock(c, jeb)
|
||||
#define jffs2_dbg_prewrite_paranoia_check(c, ofs, len)
|
||||
#endif /* !JFFS2_PARANOIA_CHECKS */
|
||||
|
||||
#ifdef JFFS2_DBG_DUMPS
|
||||
#define jffs2_dbg_dump_jeb(c, jeb) \
|
||||
__jffs2_dbg_dump_jeb(c, jeb);
|
||||
#define jffs2_dbg_dump_jeb_nolock(jeb) \
|
||||
__jffs2_dbg_dump_jeb_nolock(jeb);
|
||||
#define jffs2_dbg_dump_block_lists(c) \
|
||||
__jffs2_dbg_dump_block_lists(c)
|
||||
#define jffs2_dbg_dump_block_lists_nolock(c) \
|
||||
__jffs2_dbg_dump_block_lists_nolock(c)
|
||||
#define jffs2_dbg_dump_fragtree(f) \
|
||||
__jffs2_dbg_dump_fragtree(f);
|
||||
#define jffs2_dbg_dump_fragtree_nolock(f) \
|
||||
__jffs2_dbg_dump_fragtree_nolock(f);
|
||||
#define jffs2_dbg_dump_buffer(buf, len, offs) \
|
||||
__jffs2_dbg_dump_buffer(*buf, len, offs);
|
||||
#define jffs2_dbg_dump_node(c, ofs) \
|
||||
__jffs2_dbg_dump_node(c, ofs);
|
||||
#else
|
||||
#define jffs2_dbg_dump_jeb(c, jeb)
|
||||
#define jffs2_dbg_dump_jeb_nolock(jeb)
|
||||
#define jffs2_dbg_dump_block_lists(c)
|
||||
#define jffs2_dbg_dump_block_lists_nolock(c)
|
||||
#define jffs2_dbg_dump_fragtree(f)
|
||||
#define jffs2_dbg_dump_fragtree_nolock(f)
|
||||
#define jffs2_dbg_dump_buffer(buf, len, offs)
|
||||
#define jffs2_dbg_dump_node(c, ofs)
|
||||
#endif /* !JFFS2_DBG_DUMPS */
|
||||
|
||||
#ifdef JFFS2_DBG_SANITY_CHECKS
|
||||
#define jffs2_dbg_acct_sanity_check(c, jeb) \
|
||||
__jffs2_dbg_acct_sanity_check(c, jeb)
|
||||
#define jffs2_dbg_acct_sanity_check_nolock(c, jeb) \
|
||||
__jffs2_dbg_acct_sanity_check_nolock(c, jeb)
|
||||
#else
|
||||
#define jffs2_dbg_acct_sanity_check(c, jeb)
|
||||
#define jffs2_dbg_acct_sanity_check_nolock(c, jeb)
|
||||
#endif /* !JFFS2_DBG_SANITY_CHECKS */
|
||||
|
||||
#endif /* _JFFS2_DEBUG_H_ */
|
||||
870
fs/jffs2/dir.c
Normal file
870
fs/jffs2/dir.c
Normal file
@@ -0,0 +1,870 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include "jffs2_fs_i.h"
|
||||
#include "jffs2_fs_sb.h"
|
||||
#include <linux/time.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
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,dev_t);
|
||||
static int jffs2_rename (struct inode *, struct dentry *,
|
||||
struct inode *, struct dentry *);
|
||||
|
||||
const struct file_operations jffs2_dir_operations =
|
||||
{
|
||||
.read = generic_read_dir,
|
||||
.readdir = jffs2_readdir,
|
||||
.ioctl = jffs2_ioctl,
|
||||
.fsync = jffs2_fsync
|
||||
};
|
||||
|
||||
|
||||
const struct inode_operations jffs2_dir_inode_operations =
|
||||
{
|
||||
.create = jffs2_create,
|
||||
.lookup = jffs2_lookup,
|
||||
.link = jffs2_link,
|
||||
.unlink = jffs2_unlink,
|
||||
.symlink = jffs2_symlink,
|
||||
.mkdir = jffs2_mkdir,
|
||||
.rmdir = jffs2_rmdir,
|
||||
.mknod = jffs2_mknod,
|
||||
.rename = jffs2_rename,
|
||||
.permission = jffs2_permission,
|
||||
.setattr = jffs2_setattr,
|
||||
.setxattr = jffs2_setxattr,
|
||||
.getxattr = jffs2_getxattr,
|
||||
.listxattr = jffs2_listxattr,
|
||||
.removexattr = jffs2_removexattr
|
||||
};
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
|
||||
/* 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"));
|
||||
|
||||
if (target->d_name.len > JFFS2_MAX_NAME_LEN)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
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_path.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_path.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_path.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)
|
||||
goto fail;
|
||||
|
||||
ret = jffs2_init_security(inode, dir_i);
|
||||
if (ret)
|
||||
goto fail;
|
||||
ret = jffs2_init_acl(inode, dir_i);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
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;
|
||||
|
||||
fail:
|
||||
make_bad_inode(inode);
|
||||
iput(inode);
|
||||
jffs2_free_raw_inode(ri);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
|
||||
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;
|
||||
uint32_t now = get_seconds();
|
||||
|
||||
ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name,
|
||||
dentry->d_name.len, dead_f, now);
|
||||
if (dead_f->inocache)
|
||||
dentry->d_inode->i_nlink = dead_f->inocache->nlink;
|
||||
if (!ret)
|
||||
dir_i->i_mtime = dir_i->i_ctime = ITIME(now);
|
||||
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;
|
||||
uint32_t now;
|
||||
|
||||
/* 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;
|
||||
|
||||
now = get_seconds();
|
||||
ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len, now);
|
||||
|
||||
if (!ret) {
|
||||
down(&f->sem);
|
||||
old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
|
||||
up(&f->sem);
|
||||
d_instantiate(dentry, old_dentry->d_inode);
|
||||
dir_i->i_mtime = dir_i->i_ctime = ITIME(now);
|
||||
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;
|
||||
int ret, targetlen = strlen(target);
|
||||
|
||||
/* FIXME: If you care. We'd need to use frags for the target
|
||||
if it grows much more than this */
|
||||
if (targetlen > 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) + targetlen, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
|
||||
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 = targetlen;
|
||||
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, targetlen));
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
|
||||
fn = jffs2_write_dnode(c, f, ri, target, targetlen, 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);
|
||||
}
|
||||
|
||||
/* We use f->target field to store the target path. */
|
||||
f->target = kmalloc(targetlen + 1, GFP_KERNEL);
|
||||
if (!f->target) {
|
||||
printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_clear_inode(inode);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(f->target, target, targetlen + 1);
|
||||
D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->target));
|
||||
|
||||
/* 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_init_security(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
ret = jffs2_init_acl(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
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, 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;
|
||||
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), &alloclen, ALLOC_NORMAL,
|
||||
JFFS2_SUMMARY_INODE_SIZE);
|
||||
|
||||
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, 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_init_security(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
ret = jffs2_init_acl(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
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, 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));
|
||||
inc_nlink(dir_i);
|
||||
|
||||
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)
|
||||
drop_nlink(dir_i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, dev_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;
|
||||
union jffs2_device_node dev;
|
||||
int devlen = 0;
|
||||
uint32_t alloclen;
|
||||
int ret;
|
||||
|
||||
if (!new_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))
|
||||
devlen = jffs2_encode_dev(&dev, rdev);
|
||||
|
||||
/* 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, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
|
||||
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, 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_init_security(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
ret = jffs2_init_acl(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
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, 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;
|
||||
uint32_t now;
|
||||
|
||||
/* 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;
|
||||
|
||||
now = get_seconds();
|
||||
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, now);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (victim_f) {
|
||||
/* There was a victim. Kill it off nicely */
|
||||
drop_nlink(new_dentry->d_inode);
|
||||
/* 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)
|
||||
inc_nlink(new_dir_i);
|
||||
|
||||
/* 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, now);
|
||||
|
||||
/* 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);
|
||||
inc_nlink(old_dentry->d_inode);
|
||||
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);
|
||||
new_dir_i->i_mtime = new_dir_i->i_ctime = ITIME(now);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (S_ISDIR(old_dentry->d_inode->i_mode))
|
||||
drop_nlink(old_dir_i);
|
||||
|
||||
new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
453
fs/jffs2/erase.c
Normal file
453
fs/jffs2/erase.c
Normal file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu 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_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;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#08x (range %#08x-%#08x)\n",
|
||||
jeb->offset, jeb->offset, jeb->offset + c->sector_size));
|
||||
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_move(&jeb->list, &c->erase_pending_list);
|
||||
c->erasing_size -= c->sector_size;
|
||||
c->dirty_size += c->sector_size;
|
||||
jeb->dirty_size = c->sector_size;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
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_move(&jeb->list, &c->erase_pending_list);
|
||||
c->erasing_size -= c->sector_size;
|
||||
c->dirty_size += c->sector_size;
|
||||
jeb->dirty_size = c->sector_size;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
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_jeb_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_move_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_move(&jeb->list, &c->erase_pending_list);
|
||||
c->erasing_size -= c->sector_size;
|
||||
c->dirty_size += c->sector_size;
|
||||
jeb->dirty_size = c->sector_size;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
c->erasing_size -= c->sector_size;
|
||||
c->bad_size += c->sector_size;
|
||||
list_move(&jeb->list, &c->bad_list);
|
||||
c->nr_erasing_blocks--;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
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 (SECTOR_ADDR((*prev)->flash_offset) == 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) {
|
||||
JFFS2_WARNING("inode_cache/xattr_datum/xattr_ref"
|
||||
" 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");
|
||||
});
|
||||
|
||||
switch (ic->class) {
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
case RAWNODE_CLASS_XATTR_DATUM:
|
||||
jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
|
||||
break;
|
||||
case RAWNODE_CLASS_XATTR_REF:
|
||||
jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
if (ic->nodes == (void *)ic && ic->nlink == 0)
|
||||
jffs2_del_ino_cache(c, ic);
|
||||
}
|
||||
}
|
||||
|
||||
void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
struct jffs2_raw_node_ref *block, *ref;
|
||||
D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset));
|
||||
|
||||
block = ref = jeb->first_node;
|
||||
|
||||
while (ref) {
|
||||
if (ref->flash_offset == REF_LINK_NODE) {
|
||||
ref = ref->next_in_ino;
|
||||
jffs2_free_refblock(block);
|
||||
block = ref;
|
||||
continue;
|
||||
}
|
||||
if (ref->flash_offset != REF_EMPTY_NODE && 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 */
|
||||
|
||||
ref++;
|
||||
}
|
||||
jeb->first_node = jeb->last_node = NULL;
|
||||
}
|
||||
|
||||
static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset)
|
||||
{
|
||||
void *ebuf;
|
||||
uint32_t ofs;
|
||||
size_t retlen;
|
||||
int ret = -EIO;
|
||||
|
||||
ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!ebuf) {
|
||||
printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
|
||||
|
||||
for (ofs = jeb->offset; 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 fail;
|
||||
}
|
||||
if (retlen != readlen) {
|
||||
printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen);
|
||||
goto fail;
|
||||
}
|
||||
for (i=0; i<readlen; i += sizeof(unsigned long)) {
|
||||
/* It's OK. We know it's properly aligned */
|
||||
unsigned long *datum = 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);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
ofs += readlen;
|
||||
cond_resched();
|
||||
}
|
||||
ret = 0;
|
||||
fail:
|
||||
kfree(ebuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
size_t retlen;
|
||||
int ret;
|
||||
uint32_t bad_offset;
|
||||
|
||||
switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
|
||||
case -EAGAIN: goto refile;
|
||||
case -EIO: goto filebad;
|
||||
}
|
||||
|
||||
/* Write the erase complete marker */
|
||||
D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
|
||||
bad_offset = jeb->offset;
|
||||
|
||||
/* Cleanmarker in oob area or no cleanmarker at all ? */
|
||||
if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
|
||||
|
||||
if (jffs2_cleanmarker_oob(c)) {
|
||||
if (jffs2_write_nand_cleanmarker(c, jeb))
|
||||
goto filebad;
|
||||
}
|
||||
|
||||
/* Everything else got zeroed before the erase */
|
||||
jeb->free_size = c->sector_size;
|
||||
} 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)
|
||||
};
|
||||
|
||||
jffs2_prealloc_raw_node_refs(c, jeb, 1);
|
||||
|
||||
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 || retlen != sizeof(marker)) {
|
||||
if (ret)
|
||||
printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
|
||||
jeb->offset, ret);
|
||||
else
|
||||
printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
|
||||
jeb->offset, sizeof(marker), retlen);
|
||||
|
||||
goto filebad;
|
||||
}
|
||||
|
||||
/* Everything else got zeroed before the erase */
|
||||
jeb->free_size = c->sector_size;
|
||||
/* FIXME Special case for cleanmarker in empty block */
|
||||
jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL);
|
||||
}
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
c->erasing_size -= c->sector_size;
|
||||
c->free_size += jeb->free_size;
|
||||
c->used_size += jeb->used_size;
|
||||
|
||||
jffs2_dbg_acct_sanity_check_nolock(c,jeb);
|
||||
jffs2_dbg_acct_paranoia_check_nolock(c, 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);
|
||||
return;
|
||||
|
||||
filebad:
|
||||
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;
|
||||
|
||||
refile:
|
||||
/* 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;
|
||||
}
|
||||
299
fs/jffs2/file.c
Normal file
299
fs/jffs2/file.c
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#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"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const struct file_operations jffs2_file_operations =
|
||||
{
|
||||
.llseek = generic_file_llseek,
|
||||
.open = generic_file_open,
|
||||
.read = do_sync_read,
|
||||
.aio_read = generic_file_aio_read,
|
||||
.write = do_sync_write,
|
||||
.aio_write = generic_file_aio_write,
|
||||
.ioctl = jffs2_ioctl,
|
||||
.mmap = generic_file_readonly_mmap,
|
||||
.fsync = jffs2_fsync,
|
||||
.sendfile = generic_file_sendfile
|
||||
};
|
||||
|
||||
/* jffs2_file_inode_operations */
|
||||
|
||||
const struct inode_operations jffs2_file_inode_operations =
|
||||
{
|
||||
.permission = jffs2_permission,
|
||||
.setattr = jffs2_setattr,
|
||||
.setxattr = jffs2_setxattr,
|
||||
.getxattr = jffs2_getxattr,
|
||||
.listxattr = jffs2_listxattr,
|
||||
.removexattr = jffs2_removexattr
|
||||
};
|
||||
|
||||
const 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));
|
||||
|
||||
BUG_ON(!PageLocked(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 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), &alloc_len,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
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, 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 (end == PAGE_CACHE_SIZE) {
|
||||
if (!start) {
|
||||
/* 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);
|
||||
} else {
|
||||
/* When writing out the end of a page, write out the
|
||||
_whole_ page. This helps to reduce the number of
|
||||
nodes in files which have many short writes, like
|
||||
syslog files. */
|
||||
start = aligned_start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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",start+writtenlen==end?0:ret));
|
||||
return start+writtenlen==end?0:ret;
|
||||
}
|
||||
709
fs/jffs2/fs.c
Normal file
709
fs/jffs2/fs.c
Normal file
@@ -0,0 +1,709 @@
|
||||
/*
|
||||
* 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.2 2007/12/06 04:02:01 boyko Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/capability.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;
|
||||
union jffs2_device_node dev;
|
||||
unsigned char *mdata = NULL;
|
||||
int mdatalen = 0;
|
||||
unsigned int ivalid;
|
||||
uint32_t alloclen;
|
||||
int ret;
|
||||
D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
|
||||
ret = inode_change_ok(inode, iattr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Special cases - we don't want more than one data node
|
||||
for these types on the medium at any time. So setattr
|
||||
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 */
|
||||
mdatalen = jffs2_encode_dev(&dev, inode->i_rdev);
|
||||
mdata = (char *)&dev;
|
||||
D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
|
||||
} else if (S_ISLNK(inode->i_mode)) {
|
||||
down(&f->sem);
|
||||
mdatalen = f->metadata->size;
|
||||
mdata = kmalloc(f->metadata->size, GFP_USER);
|
||||
if (!mdata) {
|
||||
up(&f->sem);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen);
|
||||
if (ret) {
|
||||
up(&f->sem);
|
||||
kfree(mdata);
|
||||
return ret;
|
||||
}
|
||||
up(&f->sem);
|
||||
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, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
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, 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_fragtree (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)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = jffs2_do_setattr(dentry->d_inode, iattr);
|
||||
if (!rc && (iattr->ia_valid & ATTR_MODE))
|
||||
rc = jffs2_acl_chmod(dentry->d_inode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int jffs2_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
{
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(dentry->d_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;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
|
||||
|
||||
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;
|
||||
union jffs2_device_node jdev;
|
||||
dev_t rdev = 0;
|
||||
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);
|
||||
down(&f->sem);
|
||||
|
||||
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_blocks = (inode->i_size + 511) >> 9;
|
||||
|
||||
switch (inode->i_mode & S_IFMT) {
|
||||
|
||||
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)
|
||||
inc_nlink(inode);
|
||||
}
|
||||
/* and '..' */
|
||||
inc_nlink(inode);
|
||||
/* Root dir gets i_nlink 3 for some reason */
|
||||
if (inode->i_ino == 1)
|
||||
inc_nlink(inode);
|
||||
|
||||
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 */
|
||||
if (f->metadata->size != sizeof(jdev.old) &&
|
||||
f->metadata->size != sizeof(jdev.new)) {
|
||||
printk(KERN_NOTICE "Device node has strange size %d\n", f->metadata->size);
|
||||
up(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
make_bad_inode(inode);
|
||||
return;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
|
||||
if (jffs2_read_dnode(c, f, f->metadata, (char *)&jdev, 0, f->metadata->size) < 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;
|
||||
}
|
||||
if (f->metadata->size == sizeof(jdev.old))
|
||||
rdev = old_decode_dev(je16_to_cpu(jdev.old));
|
||||
else
|
||||
rdev = new_decode_dev(je32_to_cpu(jdev.new));
|
||||
|
||||
case S_IFSOCK:
|
||||
case S_IFIFO:
|
||||
inode->i_op = &jffs2_file_inode_operations;
|
||||
init_special_inode(inode, inode->i_mode, 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);
|
||||
down(&f->sem);
|
||||
|
||||
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_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_WRITEBUFFER
|
||||
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;
|
||||
}
|
||||
if (c->mtd->type == MTD_DATAFLASH) {
|
||||
printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
c->flash_size = c->mtd->size;
|
||||
c->sector_size = c->mtd->erasesize;
|
||||
blocks = c->flash_size / c->sector_size;
|
||||
|
||||
/*
|
||||
* 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->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);
|
||||
|
||||
/* NAND (or other bizarre) flash... do setup accordingly */
|
||||
ret = jffs2_flash_setup(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
c->inocache_list = kcalloc(INOCACHE_HASHSIZE, sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
|
||||
if (!c->inocache_list) {
|
||||
ret = -ENOMEM;
|
||||
goto out_wbuf;
|
||||
}
|
||||
|
||||
jffs2_init_xattr_subsystem(c);
|
||||
|
||||
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_root_i;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
sb->s_maxbytes = 0xFFFFFFFF;
|
||||
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);
|
||||
jffs2_free_ino_caches(c);
|
||||
jffs2_free_raw_node_refs(c);
|
||||
if (jffs2_blocks_use_vmalloc(c))
|
||||
vfree(c->blocks);
|
||||
else
|
||||
kfree(c->blocks);
|
||||
out_inohash:
|
||||
jffs2_clear_xattr_subsystem(c);
|
||||
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;
|
||||
}
|
||||
|
||||
/* and Dataflash */
|
||||
if (jffs2_dataflash(c)) {
|
||||
ret = jffs2_dataflash_setup(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* and Intel "Sibley" flash */
|
||||
if (jffs2_nor_wbuf_flash(c)) {
|
||||
ret = jffs2_nor_wbuf_flash_setup(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This section is UBI volume */
|
||||
|
||||
if (jffs2_ubivol(c)) {
|
||||
|
||||
ret = jffs2_ubivol_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);
|
||||
}
|
||||
|
||||
/* and DataFlash */
|
||||
if (jffs2_dataflash(c)) {
|
||||
jffs2_dataflash_cleanup(c);
|
||||
}
|
||||
|
||||
/* and Intel "Sibley" flash */
|
||||
if (jffs2_nor_wbuf_flash(c)) {
|
||||
jffs2_nor_wbuf_flash_cleanup(c);
|
||||
}
|
||||
|
||||
/* This section is UBI volume */
|
||||
|
||||
if (jffs2_ubivol(c)) {
|
||||
jffs2_ubivol_cleanup(c);
|
||||
}
|
||||
}
|
||||
1292
fs/jffs2/gc.c
Normal file
1292
fs/jffs2/gc.c
Normal file
File diff suppressed because it is too large
Load Diff
2
fs/jffs2/histo_mips.h
Normal file
2
fs/jffs2/histo_mips.h
Normal file
@@ -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
Normal file
23
fs/jffs2/ioctl.c
Normal file
@@ -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.1.1.1 2007/06/12 07:27:13 eyryu 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;
|
||||
}
|
||||
|
||||
51
fs/jffs2/jffs2_fs_i.h
Normal file
51
fs/jffs2/jffs2_fs_i.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* $Id: jffs2_fs_i.h,v 1.1.1.1 2007/06/12 07:27:13 eyryu Exp $ */
|
||||
|
||||
#ifndef _JFFS2_FS_I
|
||||
#define _JFFS2_FS_I
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/posix_acl.h>
|
||||
#include <asm/semaphore.h>
|
||||
|
||||
struct jffs2_inode_info {
|
||||
/* We need an internal mutex similar to inode->i_mutex.
|
||||
Unfortunately, we can't used the existing one, because
|
||||
either the GC would deadlock, or we'd have to release it
|
||||
before letting GC proceed. Or we'd have to put ugliness
|
||||
into the GC code so it didn't attempt to obtain the i_mutex
|
||||
for the inode(s) which are already locked */
|
||||
struct semaphore sem;
|
||||
|
||||
/* The highest (datanode) version number used for this ino */
|
||||
uint32_t highest_version;
|
||||
|
||||
/* List of data fragments which make up the file */
|
||||
struct rb_root fragtree;
|
||||
|
||||
/* There may be one datanode which isn't referenced by any of the
|
||||
above fragments, if it contains a metadata update but no actual
|
||||
data - or if this is a directory inode */
|
||||
/* This also holds the _only_ dnode for symlinks/device nodes,
|
||||
etc. */
|
||||
struct jffs2_full_dnode *metadata;
|
||||
|
||||
/* Directory entries */
|
||||
struct jffs2_full_dirent *dents;
|
||||
|
||||
/* The target path if this is the inode of a symlink */
|
||||
unsigned char *target;
|
||||
|
||||
/* Some stuff we just have to keep in-core at all times, for each inode. */
|
||||
struct jffs2_inode_cache *inocache;
|
||||
|
||||
uint16_t flags;
|
||||
uint8_t usercompr;
|
||||
struct inode vfs_inode;
|
||||
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
|
||||
struct posix_acl *i_acl_access;
|
||||
struct posix_acl *i_acl_default;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* _JFFS2_FS_I */
|
||||
130
fs/jffs2/jffs2_fs_sb.h
Normal file
130
fs/jffs2/jffs2_fs_sb.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/* $Id: jffs2_fs_sb.h,v 1.1.1.1 2007/06/12 07:27:13 eyryu Exp $ */
|
||||
|
||||
#ifndef _JFFS2_FS_SB
|
||||
#define _JFFS2_FS_SB
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/completion.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
#define JFFS2_SB_FLAG_RO 1
|
||||
#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */
|
||||
#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */
|
||||
|
||||
struct jffs2_inodirty;
|
||||
|
||||
/* A struct for the overall file system control. Pointers to
|
||||
jffs2_sb_info structs are named `c' in the source code.
|
||||
Nee jffs_control
|
||||
*/
|
||||
struct jffs2_sb_info {
|
||||
struct mtd_info *mtd;
|
||||
|
||||
uint32_t highest_ino;
|
||||
uint32_t checked_ino;
|
||||
|
||||
unsigned int flags;
|
||||
|
||||
struct task_struct *gc_task; /* GC task struct */
|
||||
struct completion gc_thread_start; /* GC thread start completion */
|
||||
struct completion gc_thread_exit; /* GC thread exit completion port */
|
||||
|
||||
struct semaphore alloc_sem; /* Used to protect all the following
|
||||
fields, and also to protect against
|
||||
out-of-order writing of nodes. And GC. */
|
||||
uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER
|
||||
(i.e. zero for OOB CLEANMARKER */
|
||||
|
||||
uint32_t flash_size;
|
||||
uint32_t used_size;
|
||||
uint32_t dirty_size;
|
||||
uint32_t wasted_size;
|
||||
uint32_t free_size;
|
||||
uint32_t erasing_size;
|
||||
uint32_t bad_size;
|
||||
uint32_t sector_size;
|
||||
uint32_t unchecked_size;
|
||||
|
||||
uint32_t nr_free_blocks;
|
||||
uint32_t nr_erasing_blocks;
|
||||
|
||||
/* Number of free blocks there must be before we... */
|
||||
uint8_t resv_blocks_write; /* ... allow a normal filesystem write */
|
||||
uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */
|
||||
uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */
|
||||
uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */
|
||||
uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */
|
||||
|
||||
uint32_t nospc_dirty_size;
|
||||
|
||||
uint32_t nr_blocks;
|
||||
struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks
|
||||
* from the offset (blocks[ofs / sector_size]) */
|
||||
struct jffs2_eraseblock *nextblock; /* The block we're currently filling */
|
||||
|
||||
struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */
|
||||
|
||||
struct list_head clean_list; /* Blocks 100% full of clean data */
|
||||
struct list_head very_dirty_list; /* Blocks with lots of dirty space */
|
||||
struct list_head dirty_list; /* Blocks with some dirty space */
|
||||
struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */
|
||||
struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */
|
||||
struct list_head erasing_list; /* Blocks which are currently erasing */
|
||||
struct list_head erase_pending_list; /* Blocks which need erasing now */
|
||||
struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */
|
||||
struct list_head free_list; /* Blocks which are free and ready to be used */
|
||||
struct list_head bad_list; /* Bad blocks. */
|
||||
struct list_head bad_used_list; /* Bad blocks with valid data in. */
|
||||
|
||||
spinlock_t erase_completion_lock; /* Protect free_list and erasing_list
|
||||
against erase completion handler */
|
||||
wait_queue_head_t erase_wait; /* For waiting for erases to complete */
|
||||
|
||||
wait_queue_head_t inocache_wq;
|
||||
struct jffs2_inode_cache **inocache_list;
|
||||
spinlock_t inocache_lock;
|
||||
|
||||
/* Sem to allow jffs2_garbage_collect_deletion_dirent to
|
||||
drop the erase_completion_lock while it's holding a pointer
|
||||
to an obsoleted node. I don't like this. Alternatives welcomed. */
|
||||
struct semaphore erase_free_sem;
|
||||
|
||||
uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
unsigned char *wbuf; /* Write-behind buffer for NAND flash */
|
||||
uint32_t wbuf_ofs;
|
||||
uint32_t wbuf_len;
|
||||
struct jffs2_inodirty *wbuf_inodes;
|
||||
struct rw_semaphore wbuf_sem; /* Protects the write buffer */
|
||||
|
||||
unsigned char *oobbuf;
|
||||
int oobavail; /* How many bytes are available for JFFS2 in OOB */
|
||||
#endif
|
||||
|
||||
struct jffs2_summary *summary; /* Summary information */
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
#define XATTRINDEX_HASHSIZE (57)
|
||||
uint32_t highest_xid;
|
||||
uint32_t highest_xseqno;
|
||||
struct list_head xattrindex[XATTRINDEX_HASHSIZE];
|
||||
struct list_head xattr_unchecked;
|
||||
struct list_head xattr_dead_list;
|
||||
struct jffs2_xattr_ref *xref_dead_list;
|
||||
struct jffs2_xattr_ref *xref_temp;
|
||||
struct rw_semaphore xattr_sem;
|
||||
uint32_t xdatum_mem_usage;
|
||||
uint32_t xdatum_mem_threshold;
|
||||
#endif
|
||||
/* OS-private pointer for getting back to master superblock info */
|
||||
void *os_priv;
|
||||
};
|
||||
|
||||
#endif /* _JFFS2_FB_SB */
|
||||
322
fs/jffs2/malloc.c
Normal file
322
fs/jffs2/malloc.c
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
/* These are initialised to NULL in the kernel startup code.
|
||||
If you're porting to other operating systems, beware */
|
||||
static struct kmem_cache *full_dnode_slab;
|
||||
static struct kmem_cache *raw_dirent_slab;
|
||||
static struct kmem_cache *raw_inode_slab;
|
||||
static struct kmem_cache *tmp_dnode_info_slab;
|
||||
static struct kmem_cache *raw_node_ref_slab;
|
||||
static struct kmem_cache *node_frag_slab;
|
||||
static struct kmem_cache *inode_cache_slab;
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
static struct kmem_cache *xattr_datum_cache;
|
||||
static struct kmem_cache *xattr_ref_cache;
|
||||
#endif
|
||||
|
||||
int __init jffs2_create_slab_caches(void)
|
||||
{
|
||||
full_dnode_slab = kmem_cache_create("jffs2_full_dnode",
|
||||
sizeof(struct jffs2_full_dnode),
|
||||
0, 0, NULL, NULL);
|
||||
if (!full_dnode_slab)
|
||||
goto err;
|
||||
|
||||
raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent",
|
||||
sizeof(struct jffs2_raw_dirent),
|
||||
0, 0, NULL, NULL);
|
||||
if (!raw_dirent_slab)
|
||||
goto err;
|
||||
|
||||
raw_inode_slab = kmem_cache_create("jffs2_raw_inode",
|
||||
sizeof(struct jffs2_raw_inode),
|
||||
0, 0, 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, 0, NULL, NULL);
|
||||
if (!tmp_dnode_info_slab)
|
||||
goto err;
|
||||
|
||||
raw_node_ref_slab = kmem_cache_create("jffs2_refblock",
|
||||
sizeof(struct jffs2_raw_node_ref) * (REFS_PER_BLOCK + 1),
|
||||
0, 0, NULL, NULL);
|
||||
if (!raw_node_ref_slab)
|
||||
goto err;
|
||||
|
||||
node_frag_slab = kmem_cache_create("jffs2_node_frag",
|
||||
sizeof(struct jffs2_node_frag),
|
||||
0, 0, NULL, NULL);
|
||||
if (!node_frag_slab)
|
||||
goto err;
|
||||
|
||||
inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
|
||||
sizeof(struct jffs2_inode_cache),
|
||||
0, 0, NULL, NULL);
|
||||
if (!inode_cache_slab)
|
||||
goto err;
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
xattr_datum_cache = kmem_cache_create("jffs2_xattr_datum",
|
||||
sizeof(struct jffs2_xattr_datum),
|
||||
0, 0, NULL, NULL);
|
||||
if (!xattr_datum_cache)
|
||||
goto err;
|
||||
|
||||
xattr_ref_cache = kmem_cache_create("jffs2_xattr_ref",
|
||||
sizeof(struct jffs2_xattr_ref),
|
||||
0, 0, NULL, NULL);
|
||||
if (!xattr_ref_cache)
|
||||
goto err;
|
||||
#endif
|
||||
|
||||
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);
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
if (xattr_datum_cache)
|
||||
kmem_cache_destroy(xattr_datum_cache);
|
||||
if (xattr_ref_cache)
|
||||
kmem_cache_destroy(xattr_ref_cache);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
|
||||
{
|
||||
struct jffs2_full_dirent *ret;
|
||||
ret = kmalloc(sizeof(struct jffs2_full_dirent) + namesize, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_full_dirent(struct jffs2_full_dirent *x)
|
||||
{
|
||||
dbg_memalloc("%p\n", x);
|
||||
kfree(x);
|
||||
}
|
||||
|
||||
struct jffs2_full_dnode *jffs2_alloc_full_dnode(void)
|
||||
{
|
||||
struct jffs2_full_dnode *ret;
|
||||
ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_full_dnode(struct jffs2_full_dnode *x)
|
||||
{
|
||||
dbg_memalloc("%p\n", x);
|
||||
kmem_cache_free(full_dnode_slab, x);
|
||||
}
|
||||
|
||||
struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void)
|
||||
{
|
||||
struct jffs2_raw_dirent *ret;
|
||||
ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x)
|
||||
{
|
||||
dbg_memalloc("%p\n", x);
|
||||
kmem_cache_free(raw_dirent_slab, x);
|
||||
}
|
||||
|
||||
struct jffs2_raw_inode *jffs2_alloc_raw_inode(void)
|
||||
{
|
||||
struct jffs2_raw_inode *ret;
|
||||
ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_raw_inode(struct jffs2_raw_inode *x)
|
||||
{
|
||||
dbg_memalloc("%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;
|
||||
ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
|
||||
{
|
||||
dbg_memalloc("%p\n", x);
|
||||
kmem_cache_free(tmp_dnode_info_slab, x);
|
||||
}
|
||||
|
||||
static struct jffs2_raw_node_ref *jffs2_alloc_refblock(void)
|
||||
{
|
||||
struct jffs2_raw_node_ref *ret;
|
||||
|
||||
ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
|
||||
if (ret) {
|
||||
int i = 0;
|
||||
for (i=0; i < REFS_PER_BLOCK; i++) {
|
||||
ret[i].flash_offset = REF_EMPTY_NODE;
|
||||
ret[i].next_in_ino = NULL;
|
||||
}
|
||||
ret[i].flash_offset = REF_LINK_NODE;
|
||||
ret[i].next_in_ino = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb, int nr)
|
||||
{
|
||||
struct jffs2_raw_node_ref **p, *ref;
|
||||
int i = nr;
|
||||
|
||||
dbg_memalloc("%d\n", nr);
|
||||
|
||||
p = &jeb->last_node;
|
||||
ref = *p;
|
||||
|
||||
dbg_memalloc("Reserving %d refs for block @0x%08x\n", nr, jeb->offset);
|
||||
|
||||
/* If jeb->last_node is really a valid node then skip over it */
|
||||
if (ref && ref->flash_offset != REF_EMPTY_NODE)
|
||||
ref++;
|
||||
|
||||
while (i) {
|
||||
if (!ref) {
|
||||
dbg_memalloc("Allocating new refblock linked from %p\n", p);
|
||||
ref = *p = jffs2_alloc_refblock();
|
||||
if (!ref)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (ref->flash_offset == REF_LINK_NODE) {
|
||||
p = &ref->next_in_ino;
|
||||
ref = *p;
|
||||
continue;
|
||||
}
|
||||
i--;
|
||||
ref++;
|
||||
}
|
||||
jeb->allocated_refs = nr;
|
||||
|
||||
dbg_memalloc("Reserved %d refs for block @0x%08x, last_node is %p (%08x,%p)\n",
|
||||
nr, jeb->offset, jeb->last_node, jeb->last_node->flash_offset,
|
||||
jeb->last_node->next_in_ino);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jffs2_free_refblock(struct jffs2_raw_node_ref *x)
|
||||
{
|
||||
dbg_memalloc("%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;
|
||||
ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_node_frag(struct jffs2_node_frag *x)
|
||||
{
|
||||
dbg_memalloc("%p\n", x);
|
||||
kmem_cache_free(node_frag_slab, x);
|
||||
}
|
||||
|
||||
struct jffs2_inode_cache *jffs2_alloc_inode_cache(void)
|
||||
{
|
||||
struct jffs2_inode_cache *ret;
|
||||
ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
|
||||
{
|
||||
dbg_memalloc("%p\n", x);
|
||||
kmem_cache_free(inode_cache_slab, x);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void)
|
||||
{
|
||||
struct jffs2_xattr_datum *xd;
|
||||
xd = kmem_cache_alloc(xattr_datum_cache, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n", xd);
|
||||
|
||||
memset(xd, 0, sizeof(struct jffs2_xattr_datum));
|
||||
xd->class = RAWNODE_CLASS_XATTR_DATUM;
|
||||
xd->node = (void *)xd;
|
||||
INIT_LIST_HEAD(&xd->xindex);
|
||||
return xd;
|
||||
}
|
||||
|
||||
void jffs2_free_xattr_datum(struct jffs2_xattr_datum *xd)
|
||||
{
|
||||
dbg_memalloc("%p\n", xd);
|
||||
kmem_cache_free(xattr_datum_cache, xd);
|
||||
}
|
||||
|
||||
struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void)
|
||||
{
|
||||
struct jffs2_xattr_ref *ref;
|
||||
ref = kmem_cache_alloc(xattr_ref_cache, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n", ref);
|
||||
|
||||
memset(ref, 0, sizeof(struct jffs2_xattr_ref));
|
||||
ref->class = RAWNODE_CLASS_XATTR_REF;
|
||||
ref->node = (void *)ref;
|
||||
return ref;
|
||||
}
|
||||
|
||||
void jffs2_free_xattr_ref(struct jffs2_xattr_ref *ref)
|
||||
{
|
||||
dbg_memalloc("%p\n", ref);
|
||||
kmem_cache_free(xattr_ref_cache, ref);
|
||||
}
|
||||
#endif
|
||||
1225
fs/jffs2/nodelist.c
Normal file
1225
fs/jffs2/nodelist.c
Normal file
File diff suppressed because it is too large
Load Diff
447
fs/jffs2/nodelist.h
Normal file
447
fs/jffs2/nodelist.h
Normal file
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __JFFS2_NODELIST_H__
|
||||
#define __JFFS2_NODELIST_H__
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include "jffs2_fs_sb.h"
|
||||
#include "jffs2_fs_i.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
#include "summary.h"
|
||||
|
||||
#ifdef __ECOS
|
||||
#include "os-ecos.h"
|
||||
#else
|
||||
#include <linux/mtd/compatmac.h> /* For compatibility with older kernels */
|
||||
#include "os-linux.h"
|
||||
#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
|
||||
|
||||
/* The minimal node header size */
|
||||
#define JFFS2_MIN_NODE_HEADER sizeof(struct jffs2_raw_dirent)
|
||||
|
||||
/*
|
||||
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 object. If this _is_ the last, it points to the inode_cache,
|
||||
xattr_ref or xattr_datum instead. The common part of those structures
|
||||
has NULL in the first word. See jffs2_raw_ref_to_ic() below */
|
||||
uint32_t flash_offset;
|
||||
#define TEST_TOTLEN
|
||||
#ifdef TEST_TOTLEN
|
||||
uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
|
||||
#endif
|
||||
};
|
||||
|
||||
#define REF_LINK_NODE ((int32_t)-1)
|
||||
#define REF_EMPTY_NODE ((int32_t)-2)
|
||||
|
||||
/* Use blocks of about 256 bytes */
|
||||
#define REFS_PER_BLOCK ((255/sizeof(struct jffs2_raw_node_ref))-1)
|
||||
|
||||
static inline struct jffs2_raw_node_ref *ref_next(struct jffs2_raw_node_ref *ref)
|
||||
{
|
||||
ref++;
|
||||
|
||||
/* Link to another block of refs */
|
||||
if (ref->flash_offset == REF_LINK_NODE) {
|
||||
ref = ref->next_in_ino;
|
||||
if (!ref)
|
||||
return ref;
|
||||
}
|
||||
|
||||
/* End of chain */
|
||||
if (ref->flash_offset == REF_EMPTY_NODE)
|
||||
return NULL;
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* NB. This can be a jffs2_xattr_datum or jffs2_xattr_ref and
|
||||
not actually a jffs2_inode_cache. Check ->class */
|
||||
return ((struct jffs2_inode_cache *)raw);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
|
||||
/* NB: REF_PRISTINE for an inode-less node (ref->next_in_ino == NULL) indicates
|
||||
it is an unknown node of type JFFS2_NODETYPE_RWCOMPAT_COPY, so it'll get
|
||||
copied. If you need to do anything different to GC inode-less nodes, then
|
||||
you need to modify gc.c accordingly. */
|
||||
|
||||
/* 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 {
|
||||
/* First part of structure is shared with other objects which
|
||||
can terminate the raw node refs' next_in_ino list -- which
|
||||
currently struct jffs2_xattr_datum and struct jffs2_xattr_ref. */
|
||||
|
||||
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_raw_node_ref *nodes;
|
||||
uint8_t class; /* It's used for identification */
|
||||
|
||||
/* end of shared structure */
|
||||
|
||||
uint8_t flags;
|
||||
uint16_t state;
|
||||
uint32_t ino;
|
||||
struct jffs2_inode_cache *next;
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
struct jffs2_xattr_ref *xref;
|
||||
#endif
|
||||
int nlink;
|
||||
};
|
||||
|
||||
/* 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 INO_STATE_CLEARING 6 /* In clear_inode() */
|
||||
|
||||
#define INO_FLAGS_XATTR_CHECKED 0x01 /* has no duplicate xattr_ref */
|
||||
|
||||
#define RAWNODE_CLASS_INODE_CACHE 0
|
||||
#define RAWNODE_CLASS_XATTR_DATUM 1
|
||||
#define RAWNODE_CLASS_XATTR_REF 2
|
||||
|
||||
#define INOCACHE_HASHSIZE 128
|
||||
|
||||
#define write_ofs(c) ((c)->nextblock->offset + (c)->sector_size - (c)->nextblock->free_size)
|
||||
|
||||
/*
|
||||
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 rb_node rb;
|
||||
struct jffs2_full_dnode *fn;
|
||||
uint32_t version;
|
||||
uint32_t data_crc;
|
||||
uint32_t partial_crc;
|
||||
uint32_t csize;
|
||||
};
|
||||
|
||||
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 */
|
||||
uint32_t allocated_refs;
|
||||
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 */
|
||||
};
|
||||
|
||||
static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c)
|
||||
{
|
||||
return ((c->flash_size / c->sector_size) * sizeof (struct jffs2_eraseblock)) > (128 * 1024);
|
||||
}
|
||||
|
||||
#define ref_totlen(a, b, c) __jffs2_ref_totlen((a), (b), (c))
|
||||
|
||||
#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 int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev)
|
||||
{
|
||||
if (old_valid_dev(rdev)) {
|
||||
jdev->old = cpu_to_je16(old_encode_dev(rdev));
|
||||
return sizeof(jdev->old);
|
||||
} else {
|
||||
jdev->new = cpu_to_je32(new_encode_dev(rdev));
|
||||
return sizeof(jdev->new);
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *node = rb_first(root);
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
return rb_entry(node, struct jffs2_node_frag, rb);
|
||||
}
|
||||
|
||||
static inline struct jffs2_node_frag *frag_last(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *node = rb_last(root);
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
return rb_entry(node, struct jffs2_node_frag, rb);
|
||||
}
|
||||
|
||||
#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 */
|
||||
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
|
||||
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);
|
||||
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);
|
||||
int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
|
||||
void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
|
||||
int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn);
|
||||
struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
uint32_t ofs, uint32_t len,
|
||||
struct jffs2_inode_cache *ic);
|
||||
extern uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_node_ref *ref);
|
||||
|
||||
/* 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 *len, int prio, uint32_t sumsize);
|
||||
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
|
||||
uint32_t *len, uint32_t sumsize);
|
||||
struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
|
||||
uint32_t ofs, uint32_t len,
|
||||
struct jffs2_inode_cache *ic);
|
||||
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);
|
||||
|
||||
/* 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, 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, 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, uint32_t time);
|
||||
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, uint32_t time);
|
||||
|
||||
|
||||
/* readinode.c */
|
||||
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 *);
|
||||
int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb, int nr);
|
||||
void jffs2_free_refblock(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 *);
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void);
|
||||
void jffs2_free_xattr_datum(struct jffs2_xattr_datum *);
|
||||
struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void);
|
||||
void jffs2_free_xattr_ref(struct jffs2_xattr_ref *);
|
||||
#endif
|
||||
|
||||
/* 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);
|
||||
struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
|
||||
int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t size);
|
||||
|
||||
/* 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);
|
||||
void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
/* 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
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#endif /* __JFFS2_NODELIST_H__ */
|
||||
742
fs/jffs2/nodemgmt.c
Normal file
742
fs/jffs2/nodemgmt.c
Normal file
@@ -0,0 +1,742 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu 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"
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* jffs2_reserve_space - request physical space to write nodes to flash
|
||||
* @c: superblock info
|
||||
* @minsize: Minimum acceptable size of allocation
|
||||
* @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 'len' into the appropriate place, or returns -ENOSPC or other
|
||||
* error if appropriate. Doesn't return len since that's
|
||||
*
|
||||
* 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 *len, uint32_t sumsize);
|
||||
|
||||
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
|
||||
uint32_t *len, int prio, uint32_t sumsize)
|
||||
{
|
||||
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) {
|
||||
D1(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) {
|
||||
D1(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, len, sumsize);
|
||||
if (ret) {
|
||||
D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
|
||||
}
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
if (!ret)
|
||||
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
|
||||
if (ret)
|
||||
up(&c->alloc_sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
|
||||
uint32_t *len, uint32_t sumsize)
|
||||
{
|
||||
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, len, sumsize);
|
||||
if (ret) {
|
||||
D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
|
||||
}
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
if (!ret)
|
||||
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Classify nextblock (clean, dirty of verydirty) and force to select an other one */
|
||||
|
||||
static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
|
||||
/* 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 = NULL;
|
||||
|
||||
}
|
||||
|
||||
/* Select a new jeb for nextblock */
|
||||
|
||||
static int jffs2_find_nextblock(struct jffs2_sb_info *c)
|
||||
{
|
||||
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_move_tail(&ejeb->list, &c->erase_pending_list);
|
||||
c->nr_erasing_blocks++;
|
||||
jffs2_erase_pending_trigger(c);
|
||||
D1(printk(KERN_DEBUG "jffs2_find_nextblock: 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_find_nextblock: 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 = list_entry(next, struct jffs2_eraseblock, list);
|
||||
c->nr_free_blocks--;
|
||||
|
||||
jffs2_sum_reset_collected(c->summary); /* reset collected summary */
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_find_nextblock(): new nextblock = 0x%08x\n", c->nextblock->offset));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with alloc sem _and_ erase_completion_lock */
|
||||
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
|
||||
uint32_t *len, uint32_t sumsize)
|
||||
{
|
||||
struct jffs2_eraseblock *jeb = c->nextblock;
|
||||
uint32_t reserved_size; /* for summary information at the end of the jeb */
|
||||
int ret;
|
||||
|
||||
restart:
|
||||
reserved_size = 0;
|
||||
|
||||
if (jffs2_sum_active() && (sumsize != JFFS2_SUMMARY_NOSUM_SIZE)) {
|
||||
/* NOSUM_SIZE means not to generate summary */
|
||||
|
||||
if (jeb) {
|
||||
reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE);
|
||||
dbg_summary("minsize=%d , jeb->free=%d ,"
|
||||
"summary->size=%d , sumsize=%d\n",
|
||||
minsize, jeb->free_size,
|
||||
c->summary->sum_size, sumsize);
|
||||
}
|
||||
|
||||
/* Is there enough space for writing out the current node, or we have to
|
||||
write out summary information now, close this jeb and select new nextblock? */
|
||||
if (jeb && (PAD(minsize) + PAD(c->summary->sum_size + sumsize +
|
||||
JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size)) {
|
||||
|
||||
/* Has summary been disabled for this jeb? */
|
||||
if (jffs2_sum_is_disabled(c->summary)) {
|
||||
sumsize = JFFS2_SUMMARY_NOSUM_SIZE;
|
||||
goto restart;
|
||||
}
|
||||
|
||||
/* Writing out the collected summary information */
|
||||
dbg_summary("generating summary for 0x%08x.\n", jeb->offset);
|
||||
ret = jffs2_sum_write_sumnode(c);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (jffs2_sum_is_disabled(c->summary)) {
|
||||
/* jffs2_write_sumnode() couldn't write out the summary information
|
||||
diabling summary for this jeb and free the collected information
|
||||
*/
|
||||
sumsize = JFFS2_SUMMARY_NOSUM_SIZE;
|
||||
goto restart;
|
||||
}
|
||||
|
||||
jffs2_close_nextblock(c, jeb);
|
||||
jeb = NULL;
|
||||
/* keep always valid value in reserved_size */
|
||||
reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE);
|
||||
}
|
||||
} else {
|
||||
if (jeb && minsize > jeb->free_size) {
|
||||
uint32_t waste;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
ret = jffs2_prealloc_raw_node_refs(c, jeb, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Just lock it again and continue. Nothing much can change because
|
||||
we hold c->alloc_sem anyway. In fact, it's not entirely clear why
|
||||
we hold c->erase_completion_lock in the majority of this function...
|
||||
but that's a question for another (more caffeine-rich) day. */
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
waste = jeb->free_size;
|
||||
jffs2_link_node_ref(c, jeb,
|
||||
(jeb->offset + c->sector_size - waste) | REF_OBSOLETE,
|
||||
waste, NULL);
|
||||
/* FIXME: that made it count as dirty. Convert to wasted */
|
||||
jeb->dirty_size -= waste;
|
||||
c->dirty_size -= waste;
|
||||
jeb->wasted_size += waste;
|
||||
c->wasted_size += waste;
|
||||
|
||||
jffs2_close_nextblock(c, jeb);
|
||||
jeb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!jeb) {
|
||||
|
||||
ret = jffs2_find_nextblock(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
jeb = c->nextblock;
|
||||
|
||||
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 */
|
||||
*len = jeb->free_size - reserved_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, jeb->offset + (c->sector_size - jeb->free_size)));
|
||||
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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
|
||||
uint32_t ofs, uint32_t len,
|
||||
struct jffs2_inode_cache *ic)
|
||||
{
|
||||
struct jffs2_eraseblock *jeb;
|
||||
struct jffs2_raw_node_ref *new;
|
||||
|
||||
jeb = &c->blocks[ofs / c->sector_size];
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n",
|
||||
ofs & ~3, ofs & 3, len));
|
||||
#if 1
|
||||
/* Allow non-obsolete nodes only to be added at the end of c->nextblock,
|
||||
if c->nextblock is set. Note that wbuf.c will file obsolete nodes
|
||||
even after refiling c->nextblock */
|
||||
if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE))
|
||||
&& (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) {
|
||||
printk(KERN_WARNING "argh. node added in wrong place\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
new = jffs2_link_node_ref(c, jeb, ofs, len, ic);
|
||||
|
||||
if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_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;
|
||||
}
|
||||
jffs2_dbg_acct_sanity_check_nolock(c,jeb);
|
||||
jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
uint32_t freed_len;
|
||||
|
||||
if(unlikely(!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_SCANNING | JFFS2_SB_FLAG_BUILDING))) {
|
||||
/* 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);
|
||||
|
||||
freed_len = ref_totlen(c, jeb, ref);
|
||||
|
||||
if (ref_flags(ref) == REF_UNCHECKED) {
|
||||
D1(if (unlikely(jeb->unchecked_size < freed_len)) {
|
||||
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",
|
||||
freed_len, 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), freed_len));
|
||||
jeb->unchecked_size -= freed_len;
|
||||
c->unchecked_size -= freed_len;
|
||||
} else {
|
||||
D1(if (unlikely(jeb->used_size < freed_len)) {
|
||||
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",
|
||||
freed_len, blocknr, ref->flash_offset, jeb->used_size);
|
||||
BUG();
|
||||
})
|
||||
D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), freed_len));
|
||||
jeb->used_size -= freed_len;
|
||||
c->used_size -= freed_len;
|
||||
}
|
||||
|
||||
// Take care, that wasted size is taken into concern
|
||||
if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + freed_len)) && jeb != c->nextblock) {
|
||||
D1(printk("Dirtying\n"));
|
||||
addedsize = freed_len;
|
||||
jeb->dirty_size += freed_len;
|
||||
c->dirty_size += freed_len;
|
||||
|
||||
/* 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 += freed_len;
|
||||
c->wasted_size += freed_len;
|
||||
}
|
||||
ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
|
||||
|
||||
jffs2_dbg_acct_sanity_check_nolock(c, jeb);
|
||||
jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
|
||||
|
||||
if (c->flags & JFFS2_SB_FLAG_SCANNING) {
|
||||
/* Flash scanning is 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) ||
|
||||
(c->flags & JFFS2_SB_FLAG_BUILDING)) {
|
||||
/* 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_jeb_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(freed_len)) {
|
||||
printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), freed_len);
|
||||
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;
|
||||
|
||||
switch (ic->class) {
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
case RAWNODE_CLASS_XATTR_DATUM:
|
||||
jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
|
||||
break;
|
||||
case RAWNODE_CLASS_XATTR_REF:
|
||||
jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
if (ic->nodes == (void *)ic && ic->nlink == 0)
|
||||
jffs2_del_ino_cache(c, ic);
|
||||
break;
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
out_erase_sem:
|
||||
up(&c->erase_free_sem);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
211
fs/jffs2/os-linux.h
Normal file
211
fs/jffs2/os-linux.h
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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.2 2007/12/06 04:02:01 boyko Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __JFFS2_OS_LINUX_H__
|
||||
#define __JFFS2_OS_LINUX_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)
|
||||
|
||||
struct kstatfs;
|
||||
struct kvec;
|
||||
|
||||
#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)
|
||||
|
||||
|
||||
#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)
|
||||
#define JFFS2_F_I_RDEV(f) (OFNI_EDONI_2SFFJ(f)->i_rdev)
|
||||
|
||||
#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)
|
||||
|
||||
#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)
|
||||
{
|
||||
f->highest_version = 0;
|
||||
f->fragtree = RB_ROOT;
|
||||
f->metadata = NULL;
|
||||
f->dents = NULL;
|
||||
f->target = NULL;
|
||||
f->flags = 0;
|
||||
f->usercompr = 0;
|
||||
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
|
||||
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
|
||||
f->i_acl_default = JFFS2_ACL_NOT_CACHED;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
|
||||
|
||||
#define SECTOR_ADDR(x) ( (((unsigned long)(x) / c->sector_size) * c->sector_size) )
|
||||
#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
|
||||
|
||||
#ifdef CONFIG_JFFS2_SUMMARY
|
||||
#define jffs2_can_mark_obsolete(c) (0)
|
||||
#else
|
||||
#define jffs2_can_mark_obsolete(c) (1)
|
||||
#endif
|
||||
|
||||
#define jffs2_is_writebuffered(c) (0)
|
||||
#define jffs2_cleanmarker_oob(c) (0)
|
||||
#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
|
||||
|
||||
#define jffs2_flash_write(c, ofs, len, retlen, buf) jffs2_flash_direct_write(c, 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) ({ do{} while(0); (void)(c), 0; })
|
||||
#define jffs2_flush_wbuf_gc(c, i) ({ do{} while(0); (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_dataflash(c) (0)
|
||||
#define jffs2_dataflash_setup(c) (0)
|
||||
#define jffs2_dataflash_cleanup(c) do {} while (0)
|
||||
#define jffs2_nor_wbuf_flash(c) (0)
|
||||
#define jffs2_nor_wbuf_flash_setup(c) (0)
|
||||
#define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0)
|
||||
#define jffs2_ubivol(c) (0)
|
||||
#define jffs2_ubivol_setup(c) (0)
|
||||
#define jffs2_ubivol_cleanup(c) do {} while (0)
|
||||
|
||||
#else /* NAND and/or ECC'd NOR support present */
|
||||
|
||||
#define jffs2_is_writebuffered(c) (c->wbuf != NULL)
|
||||
|
||||
#ifdef CONFIG_JFFS2_SUMMARY
|
||||
#define jffs2_can_mark_obsolete(c) (0)
|
||||
#else
|
||||
#define jffs2_can_mark_obsolete(c) (c->mtd->flags & (MTD_BIT_WRITEABLE))
|
||||
#endif
|
||||
|
||||
#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);
|
||||
|
||||
#define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
|
||||
int jffs2_dataflash_setup(struct jffs2_sb_info *c);
|
||||
void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
|
||||
#define jffs2_ubivol(c) (c->mtd->type == MTD_UBIVOLUME)
|
||||
int jffs2_ubivol_setup(struct jffs2_sb_info *c);
|
||||
void jffs2_ubivol_cleanup(struct jffs2_sb_info *c);
|
||||
|
||||
#define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && ! (c->mtd->flags & MTD_BIT_WRITEABLE))
|
||||
int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c);
|
||||
void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c);
|
||||
|
||||
#endif /* WRITEBUFFER */
|
||||
|
||||
/* 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 const struct file_operations jffs2_dir_operations;
|
||||
extern const struct inode_operations jffs2_dir_inode_operations;
|
||||
|
||||
/* file.c */
|
||||
extern const struct file_operations jffs2_file_operations;
|
||||
extern const struct inode_operations jffs2_file_inode_operations;
|
||||
extern const 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 const 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 dentry *, 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);
|
||||
int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
|
||||
size_t *retlen, const u_char *buf);
|
||||
|
||||
#endif /* __JFFS2_OS_LINUX_H__ */
|
||||
|
||||
|
||||
72
fs/jffs2/pushpull.h
Normal file
72
fs/jffs2/pushpull.h
Normal file
@@ -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.1.1.1 2007/06/12 07:27:13 eyryu 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__ */
|
||||
215
fs/jffs2/read.c
Normal file
215
fs/jffs2/read.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu 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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
1019
fs/jffs2/readinode.c
Normal file
1019
fs/jffs2/readinode.c
Normal file
File diff suppressed because it is too large
Load Diff
1134
fs/jffs2/scan.c
Normal file
1134
fs/jffs2/scan.c
Normal file
File diff suppressed because it is too large
Load Diff
82
fs/jffs2/security.c
Normal file
82
fs/jffs2/security.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2006 NEC Corporation
|
||||
*
|
||||
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
*/
|
||||
#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 <linux/xattr.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/security.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
/* ---- Initial Security Label Attachment -------------- */
|
||||
int jffs2_init_security(struct inode *inode, struct inode *dir)
|
||||
{
|
||||
int rc;
|
||||
size_t len;
|
||||
void *value;
|
||||
char *name;
|
||||
|
||||
rc = security_inode_init_security(inode, dir, &name, &value, &len);
|
||||
if (rc) {
|
||||
if (rc == -EOPNOTSUPP)
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
|
||||
|
||||
kfree(name);
|
||||
kfree(value);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* ---- XATTR Handler for "security.*" ----------------- */
|
||||
static int jffs2_security_getxattr(struct inode *inode, const char *name,
|
||||
void *buffer, size_t size)
|
||||
{
|
||||
if (!strcmp(name, ""))
|
||||
return -EINVAL;
|
||||
|
||||
return do_jffs2_getxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size);
|
||||
}
|
||||
|
||||
static int jffs2_security_setxattr(struct inode *inode, const char *name, const void *buffer,
|
||||
size_t size, int flags)
|
||||
{
|
||||
if (!strcmp(name, ""))
|
||||
return -EINVAL;
|
||||
|
||||
return do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size, flags);
|
||||
}
|
||||
|
||||
static size_t jffs2_security_listxattr(struct inode *inode, char *list, size_t list_size,
|
||||
const char *name, size_t name_len)
|
||||
{
|
||||
size_t retlen = XATTR_SECURITY_PREFIX_LEN + name_len + 1;
|
||||
|
||||
if (list && retlen <= list_size) {
|
||||
strcpy(list, XATTR_SECURITY_PREFIX);
|
||||
strcpy(list + XATTR_SECURITY_PREFIX_LEN, name);
|
||||
}
|
||||
|
||||
return retlen;
|
||||
}
|
||||
|
||||
struct xattr_handler jffs2_security_xattr_handler = {
|
||||
.prefix = XATTR_SECURITY_PREFIX,
|
||||
.list = jffs2_security_listxattr,
|
||||
.set = jffs2_security_setxattr,
|
||||
.get = jffs2_security_getxattr
|
||||
};
|
||||
845
fs/jffs2/summary.c
Normal file
845
fs/jffs2/summary.c
Normal file
@@ -0,0 +1,845 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
|
||||
* Zoltan Sogor <weth@inf.u-szeged.hu>,
|
||||
* Patrik Kluba <pajko@halom.u-szeged.hu>,
|
||||
* University of Szeged, Hungary
|
||||
* 2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: summary.c,v 1.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include "nodelist.h"
|
||||
#include "debug.h"
|
||||
|
||||
int jffs2_sum_init(struct jffs2_sb_info *c)
|
||||
{
|
||||
c->summary = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
|
||||
|
||||
if (!c->summary) {
|
||||
JFFS2_WARNING("Can't allocate memory for summary information!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
c->summary->sum_buf = vmalloc(c->sector_size);
|
||||
|
||||
if (!c->summary->sum_buf) {
|
||||
JFFS2_WARNING("Can't allocate buffer for writing out summary information!\n");
|
||||
kfree(c->summary);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dbg_summary("returned successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jffs2_sum_exit(struct jffs2_sb_info *c)
|
||||
{
|
||||
dbg_summary("called\n");
|
||||
|
||||
jffs2_sum_disable_collecting(c->summary);
|
||||
|
||||
vfree(c->summary->sum_buf);
|
||||
c->summary->sum_buf = NULL;
|
||||
|
||||
kfree(c->summary);
|
||||
c->summary = NULL;
|
||||
}
|
||||
|
||||
static int jffs2_sum_add_mem(struct jffs2_summary *s, union jffs2_sum_mem *item)
|
||||
{
|
||||
if (!s->sum_list_head)
|
||||
s->sum_list_head = (union jffs2_sum_mem *) item;
|
||||
if (s->sum_list_tail)
|
||||
s->sum_list_tail->u.next = (union jffs2_sum_mem *) item;
|
||||
s->sum_list_tail = (union jffs2_sum_mem *) item;
|
||||
|
||||
switch (je16_to_cpu(item->u.nodetype)) {
|
||||
case JFFS2_NODETYPE_INODE:
|
||||
s->sum_size += JFFS2_SUMMARY_INODE_SIZE;
|
||||
s->sum_num++;
|
||||
dbg_summary("inode (%u) added to summary\n",
|
||||
je32_to_cpu(item->i.inode));
|
||||
break;
|
||||
case JFFS2_NODETYPE_DIRENT:
|
||||
s->sum_size += JFFS2_SUMMARY_DIRENT_SIZE(item->d.nsize);
|
||||
s->sum_num++;
|
||||
dbg_summary("dirent (%u) added to summary\n",
|
||||
je32_to_cpu(item->d.ino));
|
||||
break;
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
case JFFS2_NODETYPE_XATTR:
|
||||
s->sum_size += JFFS2_SUMMARY_XATTR_SIZE;
|
||||
s->sum_num++;
|
||||
dbg_summary("xattr (xid=%u, version=%u) added to summary\n",
|
||||
je32_to_cpu(item->x.xid), je32_to_cpu(item->x.version));
|
||||
break;
|
||||
case JFFS2_NODETYPE_XREF:
|
||||
s->sum_size += JFFS2_SUMMARY_XREF_SIZE;
|
||||
s->sum_num++;
|
||||
dbg_summary("xref added to summary\n");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
JFFS2_WARNING("UNKNOWN node type %u\n",
|
||||
je16_to_cpu(item->u.nodetype));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* The following 3 functions are called from scan.c to collect summary info for not closed jeb */
|
||||
|
||||
int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size)
|
||||
{
|
||||
dbg_summary("called with %u\n", size);
|
||||
s->sum_padded += size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri,
|
||||
uint32_t ofs)
|
||||
{
|
||||
struct jffs2_sum_inode_mem *temp = kmalloc(sizeof(struct jffs2_sum_inode_mem), GFP_KERNEL);
|
||||
|
||||
if (!temp)
|
||||
return -ENOMEM;
|
||||
|
||||
temp->nodetype = ri->nodetype;
|
||||
temp->inode = ri->ino;
|
||||
temp->version = ri->version;
|
||||
temp->offset = cpu_to_je32(ofs); /* relative offset from the begining of the jeb */
|
||||
temp->totlen = ri->totlen;
|
||||
temp->next = NULL;
|
||||
|
||||
return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
|
||||
int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd,
|
||||
uint32_t ofs)
|
||||
{
|
||||
struct jffs2_sum_dirent_mem *temp =
|
||||
kmalloc(sizeof(struct jffs2_sum_dirent_mem) + rd->nsize, GFP_KERNEL);
|
||||
|
||||
if (!temp)
|
||||
return -ENOMEM;
|
||||
|
||||
temp->nodetype = rd->nodetype;
|
||||
temp->totlen = rd->totlen;
|
||||
temp->offset = cpu_to_je32(ofs); /* relative from the begining of the jeb */
|
||||
temp->pino = rd->pino;
|
||||
temp->version = rd->version;
|
||||
temp->ino = rd->ino;
|
||||
temp->nsize = rd->nsize;
|
||||
temp->type = rd->type;
|
||||
temp->next = NULL;
|
||||
|
||||
memcpy(temp->name, rd->name, rd->nsize);
|
||||
|
||||
return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs)
|
||||
{
|
||||
struct jffs2_sum_xattr_mem *temp;
|
||||
|
||||
temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
|
||||
if (!temp)
|
||||
return -ENOMEM;
|
||||
|
||||
temp->nodetype = rx->nodetype;
|
||||
temp->xid = rx->xid;
|
||||
temp->version = rx->version;
|
||||
temp->offset = cpu_to_je32(ofs);
|
||||
temp->totlen = rx->totlen;
|
||||
temp->next = NULL;
|
||||
|
||||
return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
|
||||
int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs)
|
||||
{
|
||||
struct jffs2_sum_xref_mem *temp;
|
||||
|
||||
temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
|
||||
if (!temp)
|
||||
return -ENOMEM;
|
||||
|
||||
temp->nodetype = rr->nodetype;
|
||||
temp->offset = cpu_to_je32(ofs);
|
||||
temp->next = NULL;
|
||||
|
||||
return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
#endif
|
||||
/* Cleanup every collected summary information */
|
||||
|
||||
static void jffs2_sum_clean_collected(struct jffs2_summary *s)
|
||||
{
|
||||
union jffs2_sum_mem *temp;
|
||||
|
||||
if (!s->sum_list_head) {
|
||||
dbg_summary("already empty\n");
|
||||
}
|
||||
while (s->sum_list_head) {
|
||||
temp = s->sum_list_head;
|
||||
s->sum_list_head = s->sum_list_head->u.next;
|
||||
kfree(temp);
|
||||
}
|
||||
s->sum_list_tail = NULL;
|
||||
s->sum_padded = 0;
|
||||
s->sum_num = 0;
|
||||
}
|
||||
|
||||
void jffs2_sum_reset_collected(struct jffs2_summary *s)
|
||||
{
|
||||
dbg_summary("called\n");
|
||||
jffs2_sum_clean_collected(s);
|
||||
s->sum_size = 0;
|
||||
}
|
||||
|
||||
void jffs2_sum_disable_collecting(struct jffs2_summary *s)
|
||||
{
|
||||
dbg_summary("called\n");
|
||||
jffs2_sum_clean_collected(s);
|
||||
s->sum_size = JFFS2_SUMMARY_NOSUM_SIZE;
|
||||
}
|
||||
|
||||
int jffs2_sum_is_disabled(struct jffs2_summary *s)
|
||||
{
|
||||
return (s->sum_size == JFFS2_SUMMARY_NOSUM_SIZE);
|
||||
}
|
||||
|
||||
/* Move the collected summary information into sb (called from scan.c) */
|
||||
|
||||
void jffs2_sum_move_collected(struct jffs2_sb_info *c, struct jffs2_summary *s)
|
||||
{
|
||||
dbg_summary("oldsize=0x%x oldnum=%u => newsize=0x%x newnum=%u\n",
|
||||
c->summary->sum_size, c->summary->sum_num,
|
||||
s->sum_size, s->sum_num);
|
||||
|
||||
c->summary->sum_size = s->sum_size;
|
||||
c->summary->sum_num = s->sum_num;
|
||||
c->summary->sum_padded = s->sum_padded;
|
||||
c->summary->sum_list_head = s->sum_list_head;
|
||||
c->summary->sum_list_tail = s->sum_list_tail;
|
||||
|
||||
s->sum_list_head = s->sum_list_tail = NULL;
|
||||
}
|
||||
|
||||
/* Called from wbuf.c to collect writed node info */
|
||||
|
||||
int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
|
||||
unsigned long count, uint32_t ofs)
|
||||
{
|
||||
union jffs2_node_union *node;
|
||||
struct jffs2_eraseblock *jeb;
|
||||
|
||||
if (c->summary->sum_size == JFFS2_SUMMARY_NOSUM_SIZE) {
|
||||
dbg_summary("Summary is disabled for this jeb! Skipping summary info!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
node = invecs[0].iov_base;
|
||||
jeb = &c->blocks[ofs / c->sector_size];
|
||||
ofs -= jeb->offset;
|
||||
|
||||
switch (je16_to_cpu(node->u.nodetype)) {
|
||||
case JFFS2_NODETYPE_INODE: {
|
||||
struct jffs2_sum_inode_mem *temp =
|
||||
kmalloc(sizeof(struct jffs2_sum_inode_mem), GFP_KERNEL);
|
||||
|
||||
if (!temp)
|
||||
goto no_mem;
|
||||
|
||||
temp->nodetype = node->i.nodetype;
|
||||
temp->inode = node->i.ino;
|
||||
temp->version = node->i.version;
|
||||
temp->offset = cpu_to_je32(ofs);
|
||||
temp->totlen = node->i.totlen;
|
||||
temp->next = NULL;
|
||||
|
||||
return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
|
||||
case JFFS2_NODETYPE_DIRENT: {
|
||||
struct jffs2_sum_dirent_mem *temp =
|
||||
kmalloc(sizeof(struct jffs2_sum_dirent_mem) + node->d.nsize, GFP_KERNEL);
|
||||
|
||||
if (!temp)
|
||||
goto no_mem;
|
||||
|
||||
temp->nodetype = node->d.nodetype;
|
||||
temp->totlen = node->d.totlen;
|
||||
temp->offset = cpu_to_je32(ofs);
|
||||
temp->pino = node->d.pino;
|
||||
temp->version = node->d.version;
|
||||
temp->ino = node->d.ino;
|
||||
temp->nsize = node->d.nsize;
|
||||
temp->type = node->d.type;
|
||||
temp->next = NULL;
|
||||
|
||||
switch (count) {
|
||||
case 1:
|
||||
memcpy(temp->name,node->d.name,node->d.nsize);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
memcpy(temp->name,invecs[1].iov_base,node->d.nsize);
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG(); /* impossible count value */
|
||||
break;
|
||||
}
|
||||
|
||||
return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
case JFFS2_NODETYPE_XATTR: {
|
||||
struct jffs2_sum_xattr_mem *temp;
|
||||
temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
|
||||
if (!temp)
|
||||
goto no_mem;
|
||||
|
||||
temp->nodetype = node->x.nodetype;
|
||||
temp->xid = node->x.xid;
|
||||
temp->version = node->x.version;
|
||||
temp->totlen = node->x.totlen;
|
||||
temp->offset = cpu_to_je32(ofs);
|
||||
temp->next = NULL;
|
||||
|
||||
return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
case JFFS2_NODETYPE_XREF: {
|
||||
struct jffs2_sum_xref_mem *temp;
|
||||
temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
|
||||
if (!temp)
|
||||
goto no_mem;
|
||||
temp->nodetype = node->r.nodetype;
|
||||
temp->offset = cpu_to_je32(ofs);
|
||||
temp->next = NULL;
|
||||
|
||||
return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
#endif
|
||||
case JFFS2_NODETYPE_PADDING:
|
||||
dbg_summary("node PADDING\n");
|
||||
c->summary->sum_padded += je32_to_cpu(node->u.totlen);
|
||||
break;
|
||||
|
||||
case JFFS2_NODETYPE_CLEANMARKER:
|
||||
dbg_summary("node CLEANMARKER\n");
|
||||
break;
|
||||
|
||||
case JFFS2_NODETYPE_SUMMARY:
|
||||
dbg_summary("node SUMMARY\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
/* If you implement a new node type you should also implement
|
||||
summary support for it or disable summary.
|
||||
*/
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
no_mem:
|
||||
JFFS2_WARNING("MEMORY ALLOCATION ERROR!");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct jffs2_raw_node_ref *sum_link_node_ref(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
uint32_t ofs, uint32_t len,
|
||||
struct jffs2_inode_cache *ic)
|
||||
{
|
||||
/* If there was a gap, mark it dirty */
|
||||
if ((ofs & ~3) > c->sector_size - jeb->free_size) {
|
||||
/* Ew. Summary doesn't actually tell us explicitly about dirty space */
|
||||
jffs2_scan_dirty_space(c, jeb, (ofs & ~3) - (c->sector_size - jeb->free_size));
|
||||
}
|
||||
|
||||
return jffs2_link_node_ref(c, jeb, jeb->offset + ofs, len, ic);
|
||||
}
|
||||
|
||||
/* Process the stored summary information - helper function for jffs2_sum_scan_sumnode() */
|
||||
|
||||
static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_summary *summary, uint32_t *pseudo_random)
|
||||
{
|
||||
struct jffs2_inode_cache *ic;
|
||||
struct jffs2_full_dirent *fd;
|
||||
void *sp;
|
||||
int i, ino;
|
||||
int err;
|
||||
|
||||
sp = summary->sum;
|
||||
|
||||
for (i=0; i<je32_to_cpu(summary->sum_num); i++) {
|
||||
dbg_summary("processing summary index %d\n", i);
|
||||
|
||||
cond_resched();
|
||||
|
||||
/* Make sure there's a spare ref for dirty space */
|
||||
err = jffs2_prealloc_raw_node_refs(c, jeb, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) {
|
||||
case JFFS2_NODETYPE_INODE: {
|
||||
struct jffs2_sum_inode_flash *spi;
|
||||
spi = sp;
|
||||
|
||||
ino = je32_to_cpu(spi->inode);
|
||||
|
||||
dbg_summary("Inode at 0x%08x-0x%08x\n",
|
||||
jeb->offset + je32_to_cpu(spi->offset),
|
||||
jeb->offset + je32_to_cpu(spi->offset) + je32_to_cpu(spi->totlen));
|
||||
|
||||
ic = jffs2_scan_make_ino_cache(c, ino);
|
||||
if (!ic) {
|
||||
JFFS2_NOTICE("scan_make_ino_cache failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sum_link_node_ref(c, jeb, je32_to_cpu(spi->offset) | REF_UNCHECKED,
|
||||
PAD(je32_to_cpu(spi->totlen)), ic);
|
||||
|
||||
*pseudo_random += je32_to_cpu(spi->version);
|
||||
|
||||
sp += JFFS2_SUMMARY_INODE_SIZE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case JFFS2_NODETYPE_DIRENT: {
|
||||
struct jffs2_sum_dirent_flash *spd;
|
||||
spd = sp;
|
||||
|
||||
dbg_summary("Dirent at 0x%08x-0x%08x\n",
|
||||
jeb->offset + je32_to_cpu(spd->offset),
|
||||
jeb->offset + je32_to_cpu(spd->offset) + je32_to_cpu(spd->totlen));
|
||||
|
||||
|
||||
fd = jffs2_alloc_full_dirent(spd->nsize+1);
|
||||
if (!fd)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(&fd->name, spd->name, spd->nsize);
|
||||
fd->name[spd->nsize] = 0;
|
||||
|
||||
ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino));
|
||||
if (!ic) {
|
||||
jffs2_free_full_dirent(fd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fd->raw = sum_link_node_ref(c, jeb, je32_to_cpu(spd->offset) | REF_UNCHECKED,
|
||||
PAD(je32_to_cpu(spd->totlen)), ic);
|
||||
|
||||
fd->next = NULL;
|
||||
fd->version = je32_to_cpu(spd->version);
|
||||
fd->ino = je32_to_cpu(spd->ino);
|
||||
fd->nhash = full_name_hash(fd->name, spd->nsize);
|
||||
fd->type = spd->type;
|
||||
|
||||
jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
|
||||
|
||||
*pseudo_random += je32_to_cpu(spd->version);
|
||||
|
||||
sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize);
|
||||
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
case JFFS2_NODETYPE_XATTR: {
|
||||
struct jffs2_xattr_datum *xd;
|
||||
struct jffs2_sum_xattr_flash *spx;
|
||||
|
||||
spx = (struct jffs2_sum_xattr_flash *)sp;
|
||||
dbg_summary("xattr at %#08x-%#08x (xid=%u, version=%u)\n",
|
||||
jeb->offset + je32_to_cpu(spx->offset),
|
||||
jeb->offset + je32_to_cpu(spx->offset) + je32_to_cpu(spx->totlen),
|
||||
je32_to_cpu(spx->xid), je32_to_cpu(spx->version));
|
||||
|
||||
xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid),
|
||||
je32_to_cpu(spx->version));
|
||||
if (IS_ERR(xd))
|
||||
return PTR_ERR(xd);
|
||||
if (xd->version > je32_to_cpu(spx->version)) {
|
||||
/* node is not the newest one */
|
||||
struct jffs2_raw_node_ref *raw
|
||||
= sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
|
||||
PAD(je32_to_cpu(spx->totlen)), NULL);
|
||||
raw->next_in_ino = xd->node->next_in_ino;
|
||||
xd->node->next_in_ino = raw;
|
||||
} else {
|
||||
xd->version = je32_to_cpu(spx->version);
|
||||
sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
|
||||
PAD(je32_to_cpu(spx->totlen)), (void *)xd);
|
||||
}
|
||||
*pseudo_random += je32_to_cpu(spx->xid);
|
||||
sp += JFFS2_SUMMARY_XATTR_SIZE;
|
||||
|
||||
break;
|
||||
}
|
||||
case JFFS2_NODETYPE_XREF: {
|
||||
struct jffs2_xattr_ref *ref;
|
||||
struct jffs2_sum_xref_flash *spr;
|
||||
|
||||
spr = (struct jffs2_sum_xref_flash *)sp;
|
||||
dbg_summary("xref at %#08x-%#08x\n",
|
||||
jeb->offset + je32_to_cpu(spr->offset),
|
||||
jeb->offset + je32_to_cpu(spr->offset) +
|
||||
(uint32_t)PAD(sizeof(struct jffs2_raw_xref)));
|
||||
|
||||
ref = jffs2_alloc_xattr_ref();
|
||||
if (!ref) {
|
||||
JFFS2_NOTICE("allocation of xattr_datum failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ref->next = c->xref_temp;
|
||||
c->xref_temp = ref;
|
||||
|
||||
sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED,
|
||||
PAD(sizeof(struct jffs2_raw_xref)), (void *)ref);
|
||||
|
||||
*pseudo_random += ref->node->flash_offset;
|
||||
sp += JFFS2_SUMMARY_XREF_SIZE;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default : {
|
||||
uint16_t nodetype = je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype);
|
||||
JFFS2_WARNING("Unsupported node type %x found in summary! Exiting...\n", nodetype);
|
||||
if ((nodetype & JFFS2_COMPAT_MASK) == JFFS2_FEATURE_INCOMPAT)
|
||||
return -EIO;
|
||||
|
||||
/* For compatible node types, just fall back to the full scan */
|
||||
c->wasted_size -= jeb->wasted_size;
|
||||
c->free_size += c->sector_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 = 0;
|
||||
jeb->free_size = c->sector_size;
|
||||
|
||||
jffs2_free_jeb_node_refs(c, jeb);
|
||||
return -ENOTRECOVERABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Process the summary node - called from jffs2_scan_eraseblock() */
|
||||
int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_summary *summary, uint32_t sumsize,
|
||||
uint32_t *pseudo_random)
|
||||
{
|
||||
struct jffs2_unknown_node crcnode;
|
||||
int ret, ofs;
|
||||
uint32_t crc;
|
||||
|
||||
ofs = c->sector_size - sumsize;
|
||||
|
||||
dbg_summary("summary found for 0x%08x at 0x%08x (0x%x bytes)\n",
|
||||
jeb->offset, jeb->offset + ofs, sumsize);
|
||||
|
||||
/* OK, now check for node validity and CRC */
|
||||
crcnode.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
crcnode.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY);
|
||||
crcnode.totlen = summary->totlen;
|
||||
crc = crc32(0, &crcnode, sizeof(crcnode)-4);
|
||||
|
||||
if (je32_to_cpu(summary->hdr_crc) != crc) {
|
||||
dbg_summary("Summary node header is corrupt (bad CRC or "
|
||||
"no summary at all)\n");
|
||||
goto crc_err;
|
||||
}
|
||||
|
||||
if (je32_to_cpu(summary->totlen) != sumsize) {
|
||||
dbg_summary("Summary node is corrupt (wrong erasesize?)\n");
|
||||
goto crc_err;
|
||||
}
|
||||
|
||||
crc = crc32(0, summary, sizeof(struct jffs2_raw_summary)-8);
|
||||
|
||||
if (je32_to_cpu(summary->node_crc) != crc) {
|
||||
dbg_summary("Summary node is corrupt (bad CRC)\n");
|
||||
goto crc_err;
|
||||
}
|
||||
|
||||
crc = crc32(0, summary->sum, sumsize - sizeof(struct jffs2_raw_summary));
|
||||
|
||||
if (je32_to_cpu(summary->sum_crc) != crc) {
|
||||
dbg_summary("Summary node data is corrupt (bad CRC)\n");
|
||||
goto crc_err;
|
||||
}
|
||||
|
||||
if ( je32_to_cpu(summary->cln_mkr) ) {
|
||||
|
||||
dbg_summary("Summary : CLEANMARKER node \n");
|
||||
|
||||
ret = jffs2_prealloc_raw_node_refs(c, jeb, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (je32_to_cpu(summary->cln_mkr) != c->cleanmarker_size) {
|
||||
dbg_summary("CLEANMARKER node has totlen 0x%x != normal 0x%x\n",
|
||||
je32_to_cpu(summary->cln_mkr), c->cleanmarker_size);
|
||||
if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr)))))
|
||||
return ret;
|
||||
} else if (jeb->first_node) {
|
||||
dbg_summary("CLEANMARKER node not first node in block "
|
||||
"(0x%08x)\n", jeb->offset);
|
||||
if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr)))))
|
||||
return ret;
|
||||
} else {
|
||||
jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL,
|
||||
je32_to_cpu(summary->cln_mkr), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
ret = jffs2_sum_process_sum_data(c, jeb, summary, pseudo_random);
|
||||
/* -ENOTRECOVERABLE isn't a fatal error -- it means we should do a full
|
||||
scan of this eraseblock. So return zero */
|
||||
if (ret == -ENOTRECOVERABLE)
|
||||
return 0;
|
||||
if (ret)
|
||||
return ret; /* real error */
|
||||
|
||||
/* for PARANOIA_CHECK */
|
||||
ret = jffs2_prealloc_raw_node_refs(c, jeb, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sum_link_node_ref(c, jeb, ofs | REF_NORMAL, sumsize, NULL);
|
||||
|
||||
if (unlikely(jeb->free_size)) {
|
||||
JFFS2_WARNING("Free size 0x%x bytes in eraseblock @0x%08x with summary?\n",
|
||||
jeb->free_size, jeb->offset);
|
||||
jeb->wasted_size += jeb->free_size;
|
||||
c->wasted_size += jeb->free_size;
|
||||
c->free_size -= jeb->free_size;
|
||||
jeb->free_size = 0;
|
||||
}
|
||||
|
||||
return jffs2_scan_classify_jeb(c, jeb);
|
||||
|
||||
crc_err:
|
||||
JFFS2_WARNING("Summary node crc error, skipping summary information.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write summary data to flash - helper function for jffs2_sum_write_sumnode() */
|
||||
|
||||
static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
uint32_t infosize, uint32_t datasize, int padsize)
|
||||
{
|
||||
struct jffs2_raw_summary isum;
|
||||
union jffs2_sum_mem *temp;
|
||||
struct jffs2_sum_marker *sm;
|
||||
struct kvec vecs[2];
|
||||
uint32_t sum_ofs;
|
||||
void *wpage;
|
||||
int ret;
|
||||
size_t retlen;
|
||||
|
||||
memset(c->summary->sum_buf, 0xff, datasize);
|
||||
memset(&isum, 0, sizeof(isum));
|
||||
|
||||
isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY);
|
||||
isum.totlen = cpu_to_je32(infosize);
|
||||
isum.hdr_crc = cpu_to_je32(crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4));
|
||||
isum.padded = cpu_to_je32(c->summary->sum_padded);
|
||||
isum.cln_mkr = cpu_to_je32(c->cleanmarker_size);
|
||||
isum.sum_num = cpu_to_je32(c->summary->sum_num);
|
||||
wpage = c->summary->sum_buf;
|
||||
|
||||
while (c->summary->sum_num) {
|
||||
temp = c->summary->sum_list_head;
|
||||
|
||||
switch (je16_to_cpu(temp->u.nodetype)) {
|
||||
case JFFS2_NODETYPE_INODE: {
|
||||
struct jffs2_sum_inode_flash *sino_ptr = wpage;
|
||||
|
||||
sino_ptr->nodetype = temp->i.nodetype;
|
||||
sino_ptr->inode = temp->i.inode;
|
||||
sino_ptr->version = temp->i.version;
|
||||
sino_ptr->offset = temp->i.offset;
|
||||
sino_ptr->totlen = temp->i.totlen;
|
||||
|
||||
wpage += JFFS2_SUMMARY_INODE_SIZE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case JFFS2_NODETYPE_DIRENT: {
|
||||
struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage;
|
||||
|
||||
sdrnt_ptr->nodetype = temp->d.nodetype;
|
||||
sdrnt_ptr->totlen = temp->d.totlen;
|
||||
sdrnt_ptr->offset = temp->d.offset;
|
||||
sdrnt_ptr->pino = temp->d.pino;
|
||||
sdrnt_ptr->version = temp->d.version;
|
||||
sdrnt_ptr->ino = temp->d.ino;
|
||||
sdrnt_ptr->nsize = temp->d.nsize;
|
||||
sdrnt_ptr->type = temp->d.type;
|
||||
|
||||
memcpy(sdrnt_ptr->name, temp->d.name,
|
||||
temp->d.nsize);
|
||||
|
||||
wpage += JFFS2_SUMMARY_DIRENT_SIZE(temp->d.nsize);
|
||||
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
case JFFS2_NODETYPE_XATTR: {
|
||||
struct jffs2_sum_xattr_flash *sxattr_ptr = wpage;
|
||||
|
||||
temp = c->summary->sum_list_head;
|
||||
sxattr_ptr->nodetype = temp->x.nodetype;
|
||||
sxattr_ptr->xid = temp->x.xid;
|
||||
sxattr_ptr->version = temp->x.version;
|
||||
sxattr_ptr->offset = temp->x.offset;
|
||||
sxattr_ptr->totlen = temp->x.totlen;
|
||||
|
||||
wpage += JFFS2_SUMMARY_XATTR_SIZE;
|
||||
break;
|
||||
}
|
||||
case JFFS2_NODETYPE_XREF: {
|
||||
struct jffs2_sum_xref_flash *sxref_ptr = wpage;
|
||||
|
||||
temp = c->summary->sum_list_head;
|
||||
sxref_ptr->nodetype = temp->r.nodetype;
|
||||
sxref_ptr->offset = temp->r.offset;
|
||||
|
||||
wpage += JFFS2_SUMMARY_XREF_SIZE;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default : {
|
||||
if ((je16_to_cpu(temp->u.nodetype) & JFFS2_COMPAT_MASK)
|
||||
== JFFS2_FEATURE_RWCOMPAT_COPY) {
|
||||
dbg_summary("Writing unknown RWCOMPAT_COPY node type %x\n",
|
||||
je16_to_cpu(temp->u.nodetype));
|
||||
jffs2_sum_disable_collecting(c->summary);
|
||||
} else {
|
||||
BUG(); /* unknown node in summary information */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c->summary->sum_list_head = temp->u.next;
|
||||
kfree(temp);
|
||||
|
||||
c->summary->sum_num--;
|
||||
}
|
||||
|
||||
jffs2_sum_reset_collected(c->summary);
|
||||
|
||||
wpage += padsize;
|
||||
|
||||
sm = wpage;
|
||||
sm->offset = cpu_to_je32(c->sector_size - jeb->free_size);
|
||||
sm->magic = cpu_to_je32(JFFS2_SUM_MAGIC);
|
||||
|
||||
isum.sum_crc = cpu_to_je32(crc32(0, c->summary->sum_buf, datasize));
|
||||
isum.node_crc = cpu_to_je32(crc32(0, &isum, sizeof(isum) - 8));
|
||||
|
||||
vecs[0].iov_base = &isum;
|
||||
vecs[0].iov_len = sizeof(isum);
|
||||
vecs[1].iov_base = c->summary->sum_buf;
|
||||
vecs[1].iov_len = datasize;
|
||||
|
||||
sum_ofs = jeb->offset + c->sector_size - jeb->free_size;
|
||||
|
||||
dbg_summary("JFFS2: writing out data to flash to pos : 0x%08x\n",
|
||||
sum_ofs);
|
||||
|
||||
ret = jffs2_flash_writev(c, vecs, 2, sum_ofs, &retlen, 0);
|
||||
|
||||
if (ret || (retlen != infosize)) {
|
||||
|
||||
JFFS2_WARNING("Write of %u bytes at 0x%08x failed. returned %d, retlen %zd\n",
|
||||
infosize, sum_ofs, ret, retlen);
|
||||
|
||||
if (retlen) {
|
||||
/* Waste remaining space */
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
jffs2_link_node_ref(c, jeb, sum_ofs | REF_OBSOLETE, infosize, NULL);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
jffs2_link_node_ref(c, jeb, sum_ofs | REF_NORMAL, infosize, NULL);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write out summary information - called from jffs2_do_reserve_space */
|
||||
|
||||
int jffs2_sum_write_sumnode(struct jffs2_sb_info *c)
|
||||
{
|
||||
int datasize, infosize, padsize;
|
||||
struct jffs2_eraseblock *jeb;
|
||||
int ret;
|
||||
|
||||
dbg_summary("called\n");
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
jeb = c->nextblock;
|
||||
jffs2_prealloc_raw_node_refs(c, jeb, 1);
|
||||
|
||||
if (!c->summary->sum_num || !c->summary->sum_list_head) {
|
||||
JFFS2_WARNING("Empty summary info!!!\n");
|
||||
BUG();
|
||||
}
|
||||
|
||||
datasize = c->summary->sum_size + sizeof(struct jffs2_sum_marker);
|
||||
infosize = sizeof(struct jffs2_raw_summary) + datasize;
|
||||
padsize = jeb->free_size - infosize;
|
||||
infosize += padsize;
|
||||
datasize += padsize;
|
||||
|
||||
/* Is there enough space for summary? */
|
||||
if (padsize < 0) {
|
||||
/* don't try to write out summary for this jeb */
|
||||
jffs2_sum_disable_collecting(c->summary);
|
||||
|
||||
JFFS2_WARNING("Not enough space for summary, padsize = %d\n", padsize);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = jffs2_sum_write_data(c, jeb, infosize, datasize, padsize);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
return ret;
|
||||
}
|
||||
209
fs/jffs2/summary.h
Normal file
209
fs/jffs2/summary.h
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
|
||||
* Zoltan Sogor <weth@inf.u-szeged.hu>,
|
||||
* Patrik Kluba <pajko@halom.u-szeged.hu>,
|
||||
* University of Szeged, Hungary
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: summary.h,v 1.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef JFFS2_SUMMARY_H
|
||||
#define JFFS2_SUMMARY_H
|
||||
|
||||
#include <linux/uio.h>
|
||||
#include <linux/jffs2.h>
|
||||
|
||||
#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
|
||||
|
||||
#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
|
||||
#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
|
||||
#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
|
||||
#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash))
|
||||
#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash))
|
||||
|
||||
/* Summary structures used on flash */
|
||||
|
||||
struct jffs2_sum_unknown_flash
|
||||
{
|
||||
jint16_t nodetype; /* node type */
|
||||
};
|
||||
|
||||
struct jffs2_sum_inode_flash
|
||||
{
|
||||
jint16_t nodetype; /* node type */
|
||||
jint32_t inode; /* inode number */
|
||||
jint32_t version; /* inode version */
|
||||
jint32_t offset; /* offset on jeb */
|
||||
jint32_t totlen; /* record length */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct jffs2_sum_dirent_flash
|
||||
{
|
||||
jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */
|
||||
jint32_t totlen; /* record length */
|
||||
jint32_t offset; /* offset on jeb */
|
||||
jint32_t pino; /* parent inode */
|
||||
jint32_t version; /* dirent version */
|
||||
jint32_t ino; /* == zero for unlink */
|
||||
uint8_t nsize; /* dirent name size */
|
||||
uint8_t type; /* dirent type */
|
||||
uint8_t name[0]; /* dirent name */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct jffs2_sum_xattr_flash
|
||||
{
|
||||
jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */
|
||||
jint32_t xid; /* xattr identifier */
|
||||
jint32_t version; /* version number */
|
||||
jint32_t offset; /* offset on jeb */
|
||||
jint32_t totlen; /* node length */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct jffs2_sum_xref_flash
|
||||
{
|
||||
jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */
|
||||
jint32_t offset; /* offset on jeb */
|
||||
} __attribute__((packed));
|
||||
|
||||
union jffs2_sum_flash
|
||||
{
|
||||
struct jffs2_sum_unknown_flash u;
|
||||
struct jffs2_sum_inode_flash i;
|
||||
struct jffs2_sum_dirent_flash d;
|
||||
struct jffs2_sum_xattr_flash x;
|
||||
struct jffs2_sum_xref_flash r;
|
||||
};
|
||||
|
||||
/* Summary structures used in the memory */
|
||||
|
||||
struct jffs2_sum_unknown_mem
|
||||
{
|
||||
union jffs2_sum_mem *next;
|
||||
jint16_t nodetype; /* node type */
|
||||
};
|
||||
|
||||
struct jffs2_sum_inode_mem
|
||||
{
|
||||
union jffs2_sum_mem *next;
|
||||
jint16_t nodetype; /* node type */
|
||||
jint32_t inode; /* inode number */
|
||||
jint32_t version; /* inode version */
|
||||
jint32_t offset; /* offset on jeb */
|
||||
jint32_t totlen; /* record length */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct jffs2_sum_dirent_mem
|
||||
{
|
||||
union jffs2_sum_mem *next;
|
||||
jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */
|
||||
jint32_t totlen; /* record length */
|
||||
jint32_t offset; /* ofset on jeb */
|
||||
jint32_t pino; /* parent inode */
|
||||
jint32_t version; /* dirent version */
|
||||
jint32_t ino; /* == zero for unlink */
|
||||
uint8_t nsize; /* dirent name size */
|
||||
uint8_t type; /* dirent type */
|
||||
uint8_t name[0]; /* dirent name */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct jffs2_sum_xattr_mem
|
||||
{
|
||||
union jffs2_sum_mem *next;
|
||||
jint16_t nodetype;
|
||||
jint32_t xid;
|
||||
jint32_t version;
|
||||
jint32_t offset;
|
||||
jint32_t totlen;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct jffs2_sum_xref_mem
|
||||
{
|
||||
union jffs2_sum_mem *next;
|
||||
jint16_t nodetype;
|
||||
jint32_t offset;
|
||||
} __attribute__((packed));
|
||||
|
||||
union jffs2_sum_mem
|
||||
{
|
||||
struct jffs2_sum_unknown_mem u;
|
||||
struct jffs2_sum_inode_mem i;
|
||||
struct jffs2_sum_dirent_mem d;
|
||||
struct jffs2_sum_xattr_mem x;
|
||||
struct jffs2_sum_xref_mem r;
|
||||
};
|
||||
|
||||
/* Summary related information stored in superblock */
|
||||
|
||||
struct jffs2_summary
|
||||
{
|
||||
uint32_t sum_size; /* collected summary information for nextblock */
|
||||
uint32_t sum_num;
|
||||
uint32_t sum_padded;
|
||||
union jffs2_sum_mem *sum_list_head;
|
||||
union jffs2_sum_mem *sum_list_tail;
|
||||
|
||||
jint32_t *sum_buf; /* buffer for writing out summary */
|
||||
};
|
||||
|
||||
/* Summary marker is stored at the end of every sumarized erase block */
|
||||
|
||||
struct jffs2_sum_marker
|
||||
{
|
||||
jint32_t offset; /* offset of the summary node in the jeb */
|
||||
jint32_t magic; /* == JFFS2_SUM_MAGIC */
|
||||
};
|
||||
|
||||
#define JFFS2_SUMMARY_FRAME_SIZE (sizeof(struct jffs2_raw_summary) + sizeof(struct jffs2_sum_marker))
|
||||
|
||||
#ifdef CONFIG_JFFS2_SUMMARY /* SUMMARY SUPPORT ENABLED */
|
||||
|
||||
#define jffs2_sum_active() (1)
|
||||
int jffs2_sum_init(struct jffs2_sb_info *c);
|
||||
void jffs2_sum_exit(struct jffs2_sb_info *c);
|
||||
void jffs2_sum_disable_collecting(struct jffs2_summary *s);
|
||||
int jffs2_sum_is_disabled(struct jffs2_summary *s);
|
||||
void jffs2_sum_reset_collected(struct jffs2_summary *s);
|
||||
void jffs2_sum_move_collected(struct jffs2_sb_info *c, struct jffs2_summary *s);
|
||||
int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
|
||||
unsigned long count, uint32_t to);
|
||||
int jffs2_sum_write_sumnode(struct jffs2_sb_info *c);
|
||||
int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size);
|
||||
int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs);
|
||||
int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs);
|
||||
int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs);
|
||||
int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs);
|
||||
int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_summary *summary, uint32_t sumlen,
|
||||
uint32_t *pseudo_random);
|
||||
|
||||
#else /* SUMMARY DISABLED */
|
||||
|
||||
#define jffs2_sum_active() (0)
|
||||
#define jffs2_sum_init(a) (0)
|
||||
#define jffs2_sum_exit(a)
|
||||
#define jffs2_sum_disable_collecting(a)
|
||||
#define jffs2_sum_is_disabled(a) (0)
|
||||
#define jffs2_sum_reset_collected(a)
|
||||
#define jffs2_sum_add_kvec(a,b,c,d) (0)
|
||||
#define jffs2_sum_move_collected(a,b)
|
||||
#define jffs2_sum_write_sumnode(a) (0)
|
||||
#define jffs2_sum_add_padding_mem(a,b)
|
||||
#define jffs2_sum_add_inode_mem(a,b,c)
|
||||
#define jffs2_sum_add_dirent_mem(a,b,c)
|
||||
#define jffs2_sum_add_xattr_mem(a,b,c)
|
||||
#define jffs2_sum_add_xref_mem(a,b,c)
|
||||
#define jffs2_sum_scan_sumnode(a,b,c,d,e) (0)
|
||||
|
||||
#endif /* CONFIG_JFFS2_SUMMARY */
|
||||
|
||||
#endif /* JFFS2_SUMMARY_H */
|
||||
401
fs/jffs2/super.c
Normal file
401
fs/jffs2/super.c
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#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/err.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 struct kmem_cache *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, GFP_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, struct kmem_cache * 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(&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 const 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 int jffs2_get_sb_mtd(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *data, struct mtd_info *mtd,
|
||||
struct vfsmount *mnt)
|
||||
{
|
||||
struct super_block *sb;
|
||||
struct jffs2_sb_info *c;
|
||||
int ret;
|
||||
|
||||
c = kzalloc(sizeof(*c), GFP_KERNEL);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
c->mtd = mtd;
|
||||
|
||||
sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
|
||||
|
||||
if (IS_ERR(sb))
|
||||
goto out_error;
|
||||
|
||||
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));
|
||||
ret = simple_set_mnt(mnt, sb);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
|
||||
mtd->index, mtd->name));
|
||||
|
||||
/* Initialize JFFS2 superblock locks, the further initialization will be
|
||||
* done later */
|
||||
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);
|
||||
|
||||
sb->s_op = &jffs2_super_operations;
|
||||
sb->s_flags = flags | MS_NOATIME;
|
||||
sb->s_xattr = jffs2_xattr_handlers;
|
||||
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
|
||||
sb->s_flags |= MS_POSIXACL;
|
||||
#endif
|
||||
ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
|
||||
|
||||
if (ret) {
|
||||
/* Failure case... */
|
||||
up_write(&sb->s_umount);
|
||||
deactivate_super(sb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sb->s_flags |= MS_ACTIVE;
|
||||
return simple_set_mnt(mnt, sb);
|
||||
|
||||
out_error:
|
||||
ret = PTR_ERR(sb);
|
||||
out_put:
|
||||
kfree(c);
|
||||
put_mtd_device(mtd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jffs2_get_sb_mtdnr(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *data, int mtdnr,
|
||||
struct vfsmount *mnt)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
|
||||
mtd = get_mtd_device(NULL, mtdnr);
|
||||
if (IS_ERR(mtd)) {
|
||||
D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr));
|
||||
return PTR_ERR(mtd);
|
||||
}
|
||||
|
||||
return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt);
|
||||
}
|
||||
|
||||
static int jffs2_get_sb(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *data, struct vfsmount *mnt)
|
||||
{
|
||||
int err;
|
||||
struct nameidata nd;
|
||||
int mtdnr;
|
||||
|
||||
if (!dev_name)
|
||||
return -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 (!IS_ERR(mtd)) {
|
||||
if (!strcmp(mtd->name, dev_name+4))
|
||||
return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt);
|
||||
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, mnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
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_SILENT))
|
||||
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, mnt);
|
||||
|
||||
out:
|
||||
path_release(&nd);
|
||||
return 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"));
|
||||
|
||||
down(&c->alloc_sem);
|
||||
jffs2_flush_wbuf_pad(c);
|
||||
up(&c->alloc_sem);
|
||||
|
||||
jffs2_sum_exit(c);
|
||||
|
||||
jffs2_free_ino_caches(c);
|
||||
jffs2_free_raw_node_refs(c);
|
||||
if (jffs2_blocks_use_vmalloc(c))
|
||||
vfree(c->blocks);
|
||||
else
|
||||
kfree(c->blocks);
|
||||
jffs2_flash_cleanup(c);
|
||||
kfree(c->inocache_list);
|
||||
jffs2_clear_xattr_subsystem(c);
|
||||
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);
|
||||
if (!(sb->s_flags & MS_RDONLY))
|
||||
jffs2_stop_garbage_collect_thread(c);
|
||||
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;
|
||||
|
||||
/* Paranoia checks for on-medium structures. If we ask GCC
|
||||
to pack them with __attribute__((packed)) then it _also_
|
||||
assumes that they're not aligned -- so it emits crappy
|
||||
code on some architectures. Ideally we want an attribute
|
||||
which means just 'no padding', without the alignment
|
||||
thing. But GCC doesn't have that -- we have to just
|
||||
hope the structs are the right sizes, instead. */
|
||||
BUILD_BUG_ON(sizeof(struct jffs2_unknown_node) != 12);
|
||||
BUILD_BUG_ON(sizeof(struct jffs2_raw_dirent) != 40);
|
||||
BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68);
|
||||
BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32);
|
||||
|
||||
printk(KERN_INFO "JFFS2 version 2.2."
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
" (NAND)"
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_SUMMARY
|
||||
" (SUMMARY) "
|
||||
#endif
|
||||
" (C) 2001-2006 Red Hat, Inc.\n");
|
||||
|
||||
jffs2_inode_cachep = kmem_cache_create("jffs2_i",
|
||||
sizeof(struct jffs2_inode_info),
|
||||
0, (SLAB_RECLAIM_ACCOUNT|
|
||||
SLAB_MEM_SPREAD),
|
||||
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.
|
||||
68
fs/jffs2/symlink.c
Normal file
68
fs/jffs2/symlink.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
|
||||
|
||||
const struct inode_operations jffs2_symlink_inode_operations =
|
||||
{
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = jffs2_follow_link,
|
||||
.permission = jffs2_permission,
|
||||
.setattr = jffs2_setattr,
|
||||
.setxattr = jffs2_setxattr,
|
||||
.getxattr = jffs2_getxattr,
|
||||
.listxattr = jffs2_listxattr,
|
||||
.removexattr = jffs2_removexattr
|
||||
};
|
||||
|
||||
static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
|
||||
char *p = (char *)f->target;
|
||||
|
||||
/*
|
||||
* We don't acquire the f->sem mutex here since the only data we
|
||||
* use is f->target.
|
||||
*
|
||||
* 1. If we are here the inode has already built and f->target has
|
||||
* to point to the target path.
|
||||
* 2. Nobody uses f->target (if the inode is symlink's inode). The
|
||||
* exception is inode freeing function which frees f->target. But
|
||||
* it can't be called while we are here and before VFS has
|
||||
* stopped using our f->target string which we provide by means of
|
||||
* nd_set_link() call.
|
||||
*/
|
||||
|
||||
if (!p) {
|
||||
printk(KERN_ERR "jffs2_follow_link(): can't find symlink target\n");
|
||||
p = ERR_PTR(-EIO);
|
||||
}
|
||||
D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->target));
|
||||
|
||||
nd_set_link(nd, p);
|
||||
|
||||
/*
|
||||
* We will unlock the f->sem mutex but VFS will use the f->target string. This is safe
|
||||
* since the only way that may cause f->target to be changed is iput() operation.
|
||||
* But VFS will not use f->target after iput() has been called.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
1244
fs/jffs2/wbuf.c
Normal file
1244
fs/jffs2/wbuf.c
Normal file
File diff suppressed because it is too large
Load Diff
671
fs/jffs2/write.c
Normal file
671
fs/jffs2/write.c
Normal file
@@ -0,0 +1,671 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu 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->state = INO_STATE_PRESENT;
|
||||
|
||||
jffs2_add_ino_cache(c, f->inocache);
|
||||
D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
|
||||
ri->ino = cpu_to_je32(f->inocache->ino);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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, int alloc_mode)
|
||||
|
||||
{
|
||||
struct jffs2_full_dnode *fn;
|
||||
size_t retlen;
|
||||
uint32_t flash_ofs;
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
fn = jffs2_alloc_full_dnode();
|
||||
if (!fn)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* check number of valid vecs */
|
||||
if (!datalen || !data)
|
||||
cnt = 1;
|
||||
retry:
|
||||
flash_ofs = write_ofs(c);
|
||||
|
||||
jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len);
|
||||
|
||||
if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
|
||||
BUG_ON(!retried);
|
||||
D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, "
|
||||
"highest version %d -> updating dnode\n",
|
||||
je32_to_cpu(ri->version), f->highest_version));
|
||||
ri->version = cpu_to_je32(++f->highest_version);
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
}
|
||||
|
||||
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) {
|
||||
/* 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 */
|
||||
jffs2_add_physical_node_ref(c, flash_ofs | REF_OBSOLETE, PAD(sizeof(*ri)+datalen), NULL);
|
||||
} else {
|
||||
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", flash_ofs);
|
||||
}
|
||||
if (!retried && alloc_mode != ALLOC_NORETRY) {
|
||||
/* 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"));
|
||||
|
||||
jffs2_dbg_acct_sanity_check(c,jeb);
|
||||
jffs2_dbg_acct_paranoia_check(c, jeb);
|
||||
|
||||
if (alloc_mode == ALLOC_GC) {
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &dummy,
|
||||
JFFS2_SUMMARY_INODE_SIZE);
|
||||
} else {
|
||||
/* Locking pain */
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &dummy,
|
||||
alloc_mode, JFFS2_SUMMARY_INODE_SIZE);
|
||||
down(&f->sem);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
flash_ofs = write_ofs(c);
|
||||
D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
|
||||
|
||||
jffs2_dbg_acct_sanity_check(c,jeb);
|
||||
jffs2_dbg_acct_paranoia_check(c, jeb);
|
||||
|
||||
goto retry;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
|
||||
}
|
||||
/* 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)))) {
|
||||
flash_ofs |= REF_PRISTINE;
|
||||
} else {
|
||||
flash_ofs |= REF_NORMAL;
|
||||
}
|
||||
fn->raw = jffs2_add_physical_node_ref(c, flash_ofs, PAD(sizeof(*ri)+datalen), f->inocache);
|
||||
fn->ofs = je32_to_cpu(ri->offset);
|
||||
fn->size = je32_to_cpu(ri->dsize);
|
||||
fn->frags = 0;
|
||||
|
||||
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 & ~3, flash_ofs & 3, 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) {
|
||||
jffs2_dbg_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, int alloc_mode)
|
||||
{
|
||||
struct jffs2_full_dirent *fd;
|
||||
size_t retlen;
|
||||
struct kvec vecs[2];
|
||||
uint32_t flash_ofs;
|
||||
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(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;
|
||||
|
||||
fd = jffs2_alloc_full_dirent(namelen+1);
|
||||
if (!fd)
|
||||
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:
|
||||
flash_ofs = write_ofs(c);
|
||||
|
||||
jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len);
|
||||
|
||||
if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
|
||||
BUG_ON(!retried);
|
||||
D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, "
|
||||
"highest version %d -> updating dirent\n",
|
||||
je32_to_cpu(rd->version), f->highest_version));
|
||||
rd->version = cpu_to_je32(++f->highest_version);
|
||||
fd->version = je32_to_cpu(rd->version);
|
||||
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
|
||||
}
|
||||
|
||||
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) {
|
||||
jffs2_add_physical_node_ref(c, flash_ofs | REF_OBSOLETE, PAD(sizeof(*rd)+namelen), NULL);
|
||||
} else {
|
||||
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", flash_ofs);
|
||||
}
|
||||
if (!retried) {
|
||||
/* 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"));
|
||||
|
||||
jffs2_dbg_acct_sanity_check(c,jeb);
|
||||
jffs2_dbg_acct_paranoia_check(c, jeb);
|
||||
|
||||
if (alloc_mode == ALLOC_GC) {
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &dummy,
|
||||
JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
} else {
|
||||
/* Locking pain */
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &dummy,
|
||||
alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
down(&f->sem);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
flash_ofs = write_ofs(c);
|
||||
D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
|
||||
jffs2_dbg_acct_sanity_check(c,jeb);
|
||||
jffs2_dbg_acct_paranoia_check(c, jeb);
|
||||
goto retry;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
|
||||
}
|
||||
/* 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 */
|
||||
fd->raw = jffs2_add_physical_node_ref(c, flash_ofs | REF_PRISTINE, PAD(sizeof(*rd)+namelen), f->inocache);
|
||||
|
||||
if (retried) {
|
||||
jffs2_dbg_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 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,
|
||||
&alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
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, 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;
|
||||
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), &alloclen, ALLOC_NORMAL,
|
||||
JFFS2_SUMMARY_INODE_SIZE);
|
||||
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, 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, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
|
||||
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, 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,
|
||||
uint32_t time)
|
||||
{
|
||||
struct jffs2_raw_dirent *rd;
|
||||
struct jffs2_full_dirent *fd;
|
||||
uint32_t alloclen;
|
||||
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, &alloclen,
|
||||
ALLOC_DELETION, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
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(time);
|
||||
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, 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);
|
||||
|
||||
if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
|
||||
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, uint32_t time)
|
||||
{
|
||||
struct jffs2_raw_dirent *rd;
|
||||
struct jffs2_full_dirent *fd;
|
||||
uint32_t alloclen;
|
||||
int ret;
|
||||
|
||||
rd = jffs2_alloc_raw_dirent();
|
||||
if (!rd)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
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(time);
|
||||
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, 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;
|
||||
}
|
||||
81
fs/jffs2/writev.c
Normal file
81
fs/jffs2/writev.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.1.1.1 2007/06/12 07:27:13 eyryu 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 (!jffs2_is_writebuffered(c)) {
|
||||
if (jffs2_sum_active()) {
|
||||
int res;
|
||||
res = jffs2_sum_add_kvec(c, vecs, count, (uint32_t) to);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
int ret;
|
||||
ret = c->mtd->write(c->mtd, ofs, len, retlen, buf);
|
||||
|
||||
if (jffs2_sum_active()) {
|
||||
struct kvec vecs[1];
|
||||
int res;
|
||||
|
||||
vecs[0].iov_base = (unsigned char *) buf;
|
||||
vecs[0].iov_len = len;
|
||||
|
||||
res = jffs2_sum_add_kvec(c, vecs, 1, (uint32_t) ofs);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
1323
fs/jffs2/xattr.c
Normal file
1323
fs/jffs2/xattr.c
Normal file
File diff suppressed because it is too large
Load Diff
129
fs/jffs2/xattr.h
Normal file
129
fs/jffs2/xattr.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2006 NEC Corporation
|
||||
*
|
||||
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
*/
|
||||
#ifndef _JFFS2_FS_XATTR_H_
|
||||
#define _JFFS2_FS_XATTR_H_
|
||||
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#define JFFS2_XFLAGS_HOT (0x01) /* This datum is HOT */
|
||||
#define JFFS2_XFLAGS_BIND (0x02) /* This datum is not reclaimed */
|
||||
#define JFFS2_XFLAGS_DEAD (0x40) /* This datum is already dead */
|
||||
#define JFFS2_XFLAGS_INVALID (0x80) /* This datum contains crc error */
|
||||
|
||||
struct jffs2_xattr_datum
|
||||
{
|
||||
void *always_null;
|
||||
struct jffs2_raw_node_ref *node;
|
||||
uint8_t class;
|
||||
uint8_t flags;
|
||||
uint16_t xprefix; /* see JFFS2_XATTR_PREFIX_* */
|
||||
|
||||
struct list_head xindex; /* chained from c->xattrindex[n] */
|
||||
atomic_t refcnt; /* # of xattr_ref refers this */
|
||||
uint32_t xid;
|
||||
uint32_t version;
|
||||
|
||||
uint32_t data_crc;
|
||||
uint32_t hashkey;
|
||||
char *xname; /* XATTR name without prefix */
|
||||
uint32_t name_len; /* length of xname */
|
||||
char *xvalue; /* XATTR value */
|
||||
uint32_t value_len; /* length of xvalue */
|
||||
};
|
||||
|
||||
struct jffs2_inode_cache;
|
||||
struct jffs2_xattr_ref
|
||||
{
|
||||
void *always_null;
|
||||
struct jffs2_raw_node_ref *node;
|
||||
uint8_t class;
|
||||
uint8_t flags; /* Currently unused */
|
||||
u16 unused;
|
||||
|
||||
uint32_t xseqno;
|
||||
union {
|
||||
struct jffs2_inode_cache *ic; /* reference to jffs2_inode_cache */
|
||||
uint32_t ino; /* only used in scanning/building */
|
||||
};
|
||||
union {
|
||||
struct jffs2_xattr_datum *xd; /* reference to jffs2_xattr_datum */
|
||||
uint32_t xid; /* only used in sccanning/building */
|
||||
};
|
||||
struct jffs2_xattr_ref *next; /* chained from ic->xref_list */
|
||||
};
|
||||
|
||||
#define XREF_DELETE_MARKER (0x00000001)
|
||||
static inline int is_xattr_ref_dead(struct jffs2_xattr_ref *ref)
|
||||
{
|
||||
return ((ref->xseqno & XREF_DELETE_MARKER) != 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
|
||||
extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c);
|
||||
extern void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c);
|
||||
extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c);
|
||||
|
||||
extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
|
||||
uint32_t xid, uint32_t version);
|
||||
|
||||
extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
|
||||
extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
|
||||
|
||||
extern int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd,
|
||||
struct jffs2_raw_node_ref *raw);
|
||||
extern int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref,
|
||||
struct jffs2_raw_node_ref *raw);
|
||||
extern int jffs2_verify_xattr(struct jffs2_sb_info *c);
|
||||
extern void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd);
|
||||
extern void jffs2_release_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref);
|
||||
|
||||
extern int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname,
|
||||
char *buffer, size_t size);
|
||||
extern int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
|
||||
const char *buffer, size_t size, int flags);
|
||||
|
||||
extern struct xattr_handler *jffs2_xattr_handlers[];
|
||||
extern struct xattr_handler jffs2_user_xattr_handler;
|
||||
extern struct xattr_handler jffs2_trusted_xattr_handler;
|
||||
|
||||
extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t);
|
||||
#define jffs2_getxattr generic_getxattr
|
||||
#define jffs2_setxattr generic_setxattr
|
||||
#define jffs2_removexattr generic_removexattr
|
||||
|
||||
#else
|
||||
|
||||
#define jffs2_init_xattr_subsystem(c)
|
||||
#define jffs2_build_xattr_subsystem(c)
|
||||
#define jffs2_clear_xattr_subsystem(c)
|
||||
|
||||
#define jffs2_xattr_delete_inode(c, ic)
|
||||
#define jffs2_xattr_free_inode(c, ic)
|
||||
#define jffs2_verify_xattr(c) (1)
|
||||
|
||||
#define jffs2_xattr_handlers NULL
|
||||
#define jffs2_listxattr NULL
|
||||
#define jffs2_getxattr NULL
|
||||
#define jffs2_setxattr NULL
|
||||
#define jffs2_removexattr NULL
|
||||
|
||||
#endif /* CONFIG_JFFS2_FS_XATTR */
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_SECURITY
|
||||
extern int jffs2_init_security(struct inode *inode, struct inode *dir);
|
||||
extern struct xattr_handler jffs2_security_xattr_handler;
|
||||
#else
|
||||
#define jffs2_init_security(inode,dir) (0)
|
||||
#endif /* CONFIG_JFFS2_FS_SECURITY */
|
||||
|
||||
#endif /* _JFFS2_FS_XATTR_H_ */
|
||||
52
fs/jffs2/xattr_trusted.c
Normal file
52
fs/jffs2/xattr_trusted.c
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2006 NEC Corporation
|
||||
*
|
||||
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
static int jffs2_trusted_getxattr(struct inode *inode, const char *name,
|
||||
void *buffer, size_t size)
|
||||
{
|
||||
if (!strcmp(name, ""))
|
||||
return -EINVAL;
|
||||
return do_jffs2_getxattr(inode, JFFS2_XPREFIX_TRUSTED, name, buffer, size);
|
||||
}
|
||||
|
||||
static int jffs2_trusted_setxattr(struct inode *inode, const char *name, const void *buffer,
|
||||
size_t size, int flags)
|
||||
{
|
||||
if (!strcmp(name, ""))
|
||||
return -EINVAL;
|
||||
return do_jffs2_setxattr(inode, JFFS2_XPREFIX_TRUSTED, name, buffer, size, flags);
|
||||
}
|
||||
|
||||
static size_t jffs2_trusted_listxattr(struct inode *inode, char *list, size_t list_size,
|
||||
const char *name, size_t name_len)
|
||||
{
|
||||
size_t retlen = XATTR_TRUSTED_PREFIX_LEN + name_len + 1;
|
||||
|
||||
if (list && retlen<=list_size) {
|
||||
strcpy(list, XATTR_TRUSTED_PREFIX);
|
||||
strcpy(list + XATTR_TRUSTED_PREFIX_LEN, name);
|
||||
}
|
||||
|
||||
return retlen;
|
||||
}
|
||||
|
||||
struct xattr_handler jffs2_trusted_xattr_handler = {
|
||||
.prefix = XATTR_TRUSTED_PREFIX,
|
||||
.list = jffs2_trusted_listxattr,
|
||||
.set = jffs2_trusted_setxattr,
|
||||
.get = jffs2_trusted_getxattr
|
||||
};
|
||||
52
fs/jffs2/xattr_user.c
Normal file
52
fs/jffs2/xattr_user.c
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2006 NEC Corporation
|
||||
*
|
||||
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
static int jffs2_user_getxattr(struct inode *inode, const char *name,
|
||||
void *buffer, size_t size)
|
||||
{
|
||||
if (!strcmp(name, ""))
|
||||
return -EINVAL;
|
||||
return do_jffs2_getxattr(inode, JFFS2_XPREFIX_USER, name, buffer, size);
|
||||
}
|
||||
|
||||
static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer,
|
||||
size_t size, int flags)
|
||||
{
|
||||
if (!strcmp(name, ""))
|
||||
return -EINVAL;
|
||||
return do_jffs2_setxattr(inode, JFFS2_XPREFIX_USER, name, buffer, size, flags);
|
||||
}
|
||||
|
||||
static size_t jffs2_user_listxattr(struct inode *inode, char *list, size_t list_size,
|
||||
const char *name, size_t name_len)
|
||||
{
|
||||
size_t retlen = XATTR_USER_PREFIX_LEN + name_len + 1;
|
||||
|
||||
if (list && retlen <= list_size) {
|
||||
strcpy(list, XATTR_USER_PREFIX);
|
||||
strcpy(list + XATTR_USER_PREFIX_LEN, name);
|
||||
}
|
||||
|
||||
return retlen;
|
||||
}
|
||||
|
||||
struct xattr_handler jffs2_user_xattr_handler = {
|
||||
.prefix = XATTR_USER_PREFIX,
|
||||
.list = jffs2_user_listxattr,
|
||||
.set = jffs2_user_setxattr,
|
||||
.get = jffs2_user_getxattr
|
||||
};
|
||||
Reference in New Issue
Block a user