Commit 79023d0e authored by David Woodhouse's avatar David Woodhouse

Update to 2002-03-12 JFFS2 development tree. Main features:

 - Preliminary version of NAND flash support.
 - Locking documentation and fixes (including BKL removal because it's superfluous).
 - Performance improvements - especially for mount time. 
 - Annoying stuff like i_nlink on directories fixed.
 - Portability cleanups.
parent ace5d474
......@@ -44,7 +44,8 @@ if [ "$CONFIG_JFFS_FS" = "y" -o "$CONFIG_JFFS_FS" = "m" ] ; then
fi
dep_tristate 'Journalling Flash File System v2 (JFFS2) support' CONFIG_JFFS2_FS $CONFIG_MTD
if [ "$CONFIG_JFFS2_FS" = "y" -o "$CONFIG_JFFS2_FS" = "m" ] ; then
int 'JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0
int ' JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0
dep_bool ' JFFS2 support for NAND flash' CONFIG_JFFS2_FS_NAND $CONFIG_EXPERIMENTAL
fi
tristate 'Compressed ROM file system support' CONFIG_CRAMFS
bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS
......
#
# Makefile for the linux Journalling Flash FileSystem (JFFS) routines.
#
# $Id: Makefile,v 1.25 2001/09/25 20:59:41 dwmw2 Exp $
# $Id: Makefile,v 1.34 2002/03/08 11:27:59 dwmw2 Exp $
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
......@@ -10,15 +10,20 @@
# Note 2! The CFLAGS definitions are now in the main makefile...
COMPR_OBJS := compr.o compr_rubin.o compr_rtime.o pushpull.o \
compr_zlib.o
COMPR_OBJS := compr.o compr_rubin.o compr_rtime.o compr_zlib.o
JFFS2_OBJS := dir.o file.o ioctl.o nodelist.o malloc.o \
read.o nodemgmt.o readinode.o super.o write.o scan.o gc.o \
symlink.o build.o erase.o background.o
read.o nodemgmt.o readinode.o write.o scan.o gc.o \
symlink.o build.o erase.o background.o fs.o writev.o
LINUX_OBJS-24 := super-v24.o crc32.o
LINUX_OBJS-25 := super.o
NAND_OBJS-$(CONFIG_JFFS2_FS_NAND) := wbuf.o
O_TARGET := jffs2.o
obj-y := $(COMPR_OBJS) $(JFFS2_OBJS)
obj-y := $(COMPR_OBJS) $(JFFS2_OBJS) $(VERS_OBJS) $(NAND_OBJS-y) \
$(LINUX_OBJS-$(VERSION)$(PATCHLEVEL))
obj-m := $(O_TARGET)
include $(TOPDIR)/Rules.make
......
$Id: README.Locking,v 1.4 2002/03/08 16:20:06 dwmw2 Exp $
JFFS2 LOCKING DOCUMENTATION
---------------------------
At least theoretically, JFFS2 does not require the Big Kernel Lock
(BKL), which was always helpfully obtained for it by Linux 2.4 VFS
code. It has its own locking, as described below.
This document attempts to describe the existing locking rules for
JFFS2. It is not expected to remain perfectly up to date, but ought to
be fairly close.
alloc_sem
---------
The alloc_sem is a per-filesystem semaphore, used primarily to ensure
contiguous allocation of space on the medium. It is automatically
obtained during space allocations (jffs2_reserve_space()) and freed
upon write completion (jffs2_complete_reservation()). Note that
the garbage collector will obtain this right at the beginning of
jffs2_garbage_collect_pass() and release it at the end, thereby
preventing any other write activity on the file system during a
garbage collect pass.
When writing new nodes, the alloc_sem must be held until the new nodes
have been properly linked into the data structures for the inode to
which they belong. This is for the benefit of NAND flash - adding new
nodes to an inode may obsolete old ones, and by holding the alloc_sem
until this happens we ensure that any data in the write-buffer at the
time this happens are part of the new node, not just something that
was written afterwards. Hence, we can ensure the newly-obsoleted nodes
don't actually get erased until the write-buffer has been flushed to
the medium.
With the introduction of NAND flash support and the write-buffer,
the alloc_sem is also used to protect the wbuf-related members of the
jffs2_sb_info structure. Atomically reading the wbuf_len member to see
if the wbuf is currently holding any data is permitted, though.
Ordering constraints: See f->sem.
File Semaphore f->sem
---------------------
This is the JFFS2-internal equivalent of the inode semaphore i->i_sem.
It protects the contents of the jffs2_inode_info private inode data,
including the linked list of node fragments (but see the notes below on
erase_completion_lock), etc.
The reason that the i_sem itself isn't used for this purpose is to
avoid deadlocks with garbage collection -- the VFS will lock the i_sem
before calling a function which may need to allocate space. The
allocation may trigger garbage-collection, which may need to move a
node belonging to the inode which was locked in the first place by the
VFS. If the garbage collection code were to attempt to lock the i_sem
of the inode from which it's garbage-collecting a physical node, this
lead to deadlock, unless we played games with unlocking the i_sem
before calling the space allocation functions.
Instead of playing such games, we just have an extra internal
semaphore, which is obtained by the garbage collection code and also
by the normal file system code _after_ allocation of space.
Ordering constraints:
1. Never attempt to allocate space or lock alloc_sem with
any f->sem held.
2. Never attempt to lock two file semaphores in one thread.
No ordering rules have been made for doing so.
erase_completion_lock spinlock
------------------------------
This is used to serialise access to the eraseblock lists, to the
per-eraseblock lists of physical jffs2_raw_node_ref structures, and
(NB) the per-inode list of physical nodes. The latter is a special
case - see below.
As the MTD API permits erase-completion callback functions to be
called from bottom-half (timer) context, and these functions access
the data structures protected by this lock, it must be locked with
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.
node_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.
$Id: TODO,v 1.3 2001/03/01 23:26:48 dwmw2 Exp $
$Id: TODO,v 1.7 2002/03/11 12:36:59 dwmw2 Exp $
- disable compression in commit_write()? Or at least optimise the 'always write
whole page' bit.
- fix zlib. It's ugly as hell and there are at least three copies in the kernel tree
- Locking audit. Even more so now 2.5 took away the BKL.
- 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)
......@@ -10,11 +9,16 @@ $Id: TODO,v 1.3 2001/03/01 23:26:48 dwmw2 Exp $
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.
- stop it depending on a block device. mount(8) needs a change for this.
- make it work on NAND flash. We need to know when we can GC
deletion dirents, etc. And think about holes/truncation. It can
all be done reasonably simply, but it need implementing.
- NAND flash will require new dirent/dnode structures on the medium with
ECC data in rather than just the CRC we're using ATM.
- stop it depending on a block device.
- test, test, test
- NAND flash support:
- flush_wbuf using GC to fill it, don't just pad.
- Deal with write errors. Data don't get lost - we just have to write
the affected node(s) out again somewhere else.
- make fsync flush only if actually required
- make sys_sync() work.
- reboot notifier
- timed flush of old wbuf
- fix magical second arg of jffs2_flush_wbuf(). Split into two or more functions instead.
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,19 +31,19 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: background.c,v 1.16 2001/10/08 09:22:38 dwmw2 Exp $
* $Id: background.c,v 1.23 2002/03/06 12:37:08 dwmw2 Exp $
*
*/
#define __KERNEL_SYSCALLS__
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/unistd.h>
#include <linux/jffs2.h>
#include <linux/mtd/mtd.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/mtd/compatmac.h> /* recalc_sigpending() */
#include "nodelist.h"
......@@ -106,10 +106,7 @@ static int jffs2_garbage_collect_thread(void *_c)
sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
/* FIXME in the 2.2 backport */
current->nice = 10;
#endif
set_user_nice(current, 10);
for (;;) {
spin_lock_irq(&current->sigmask_lock);
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,12 +31,11 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: build.c,v 1.16 2001/03/15 15:38:23 dwmw2 Exp $
* $Id: build.c,v 1.32 2002/03/08 15:11:24 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/jffs2.h>
#include <linux/slab.h>
#include "nodelist.h"
......@@ -51,7 +50,7 @@ int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode
- Scan directory tree from top down, setting nlink in inocaches
- Scan inocaches for inodes with nlink==0
*/
int jffs2_build_filesystem(struct jffs2_sb_info *c)
static int jffs2_build_filesystem(struct jffs2_sb_info *c)
{
int ret;
int i;
......@@ -59,7 +58,11 @@ int jffs2_build_filesystem(struct jffs2_sb_info *c)
/* First, scan the medium and build all the inode caches with
lists of physical nodes */
c->flags |= JFFS2_SB_FLAG_MOUNTING;
ret = jffs2_scan_medium(c);
c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
if (ret)
return ret;
......@@ -147,6 +150,7 @@ int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *i
D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring old metadata at 0x%08x\n",
metadata->fn->raw->flash_offset &~3));
jffs2_mark_node_obsolete(c, metadata->fn->raw);
jffs2_free_full_dnode(metadata->fn);
jffs2_free_tmp_dnode_info(metadata);
metadata = NULL;
......@@ -159,15 +163,25 @@ int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *i
if (!metadata) {
metadata = tn;
} else {
/* This will only happen if it has the _same_ version
number as the existing metadata node. */
D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring new metadata at 0x%08x\n",
tn->fn->raw->flash_offset &~3));
jffs2_mark_node_obsolete(c, tn->fn->raw);
jffs2_free_full_dnode(tn->fn);
jffs2_free_tmp_dnode_info(tn);
}
}
}
if (ic->scan->version) {
/* It's a regular file, so truncate it to the last known
i_size, if necessary */
D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 truncating fraglist to 0x%08x\n", ic->scan->isize));
jffs2_truncate_fraglist(c, &fraglist, ic->scan->isize);
}
/* OK. Now clear up */
if (metadata) {
jffs2_free_full_dnode(metadata->fn);
......@@ -201,6 +215,10 @@ int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *i
if (child_ic->nlink++ && fd->type == DT_DIR) {
printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
if (fd->ino == 1 && ic->ino == 1) {
printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
}
/* What do we do about it? */
}
D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
......@@ -251,7 +269,58 @@ int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inod
}
kfree(ic->scan);
ic->scan = NULL;
// jffs2_del_ino_cache(c, ic);
// jffs2_free_inode_cache(ic);
/*
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.
*/
return ret;
}
int jffs2_do_mount_fs(struct jffs2_sb_info *c)
{
int i;
c->free_size = c->flash_size;
c->nr_blocks = c->flash_size / c->sector_size;
c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
if (!c->blocks)
return -ENOMEM;
for (i=0; i<c->nr_blocks; i++) {
INIT_LIST_HEAD(&c->blocks[i].list);
c->blocks[i].offset = i * c->sector_size;
c->blocks[i].free_size = c->sector_size;
c->blocks[i].dirty_size = 0;
c->blocks[i].used_size = 0;
c->blocks[i].first_node = NULL;
c->blocks[i].last_node = NULL;
}
init_MUTEX(&c->alloc_sem);
init_MUTEX(&c->erase_free_sem);
init_waitqueue_head(&c->erase_wait);
spin_lock_init(&c->erase_completion_lock);
spin_lock_init(&c->inocache_lock);
INIT_LIST_HEAD(&c->clean_list);
INIT_LIST_HEAD(&c->dirty_list);
INIT_LIST_HEAD(&c->erasable_list);
INIT_LIST_HEAD(&c->erasing_list);
INIT_LIST_HEAD(&c->erase_pending_list);
INIT_LIST_HEAD(&c->erasable_pending_wbuf_list);
INIT_LIST_HEAD(&c->erase_complete_list);
INIT_LIST_HEAD(&c->free_list);
INIT_LIST_HEAD(&c->bad_list);
INIT_LIST_HEAD(&c->bad_used_list);
c->highest_ino = 1;
if (jffs2_build_filesystem(c)) {
D1(printk(KERN_DEBUG "build_fs failed\n"));
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
kfree(c->blocks);
return -EIO;
}
return 0;
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by Arjan van de Ven <arjanv@redhat.com>
*
......@@ -31,24 +31,34 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: compr.c,v 1.17 2001/09/23 09:56:46 dwmw2 Exp $
* $Id: compr.c,v 1.23 2002/01/25 01:49:26 dwmw2 Exp $
*
*/
#ifdef __KERNEL__
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/errno.h>
#else
#define KERN_DEBUG
#define KERN_NOTICE
#define KERN_WARNING
#define printk printf
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#endif
#include <linux/jffs2.h>
int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
/* jffs2_compress:
......@@ -69,28 +79,28 @@ void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32
* *datalen accordingly to show the amount of data which were compressed.
*/
unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
__u32 *datalen, __u32 *cdatalen)
uint32_t *datalen, uint32_t *cdatalen)
{
int ret;
ret = zlib_compress(data_in, cpage_out, datalen, cdatalen);
ret = jffs2_zlib_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_ZLIB;
}
#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
ret = dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
ret = jffs2_dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_DYNRUBIN;
}
#endif
#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */
ret = rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
ret = jffs2_rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_RUBINMIPS;
}
#endif
/* rtime does manage to recompress already-compressed data */
ret = rtime_compress(data_in, cpage_out, datalen, cdatalen);
ret = jffs2_rtime_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_RTIME;
}
......@@ -108,7 +118,7 @@ unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
unsigned char *data_out, __u32 cdatalen, __u32 datalen)
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
{
switch (comprtype) {
case JFFS2_COMPR_NONE:
......@@ -121,23 +131,23 @@ int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
break;
case JFFS2_COMPR_ZLIB:
zlib_decompress(cdata_in, data_out, cdatalen, datalen);
jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen);
break;
case JFFS2_COMPR_RTIME:
rtime_decompress(cdata_in, data_out, cdatalen, datalen);
jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen);
break;
case JFFS2_COMPR_RUBINMIPS:
#if 0 /* Disabled 23/9/1 */
rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
#else
printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n");
#endif
break;
case JFFS2_COMPR_DYNRUBIN:
#if 1 /* Phase this one out */
dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
#else
printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
#endif
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by Arjan van de Ven <arjanv@redhat.com>
*
......@@ -31,7 +31,7 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: compr_rtime.c,v 1.5 2001/03/15 15:38:23 dwmw2 Exp $
* $Id: compr_rtime.c,v 1.8 2002/01/25 01:49:26 dwmw2 Exp $
*
*
* Very simple lz77-ish encoder.
......@@ -51,8 +51,8 @@
#include <linux/string.h>
/* _compress returns the compressed size, -1 if bigger */
int rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
__u32 *sourcelen, __u32 *dstlen)
int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
{
int positions[256];
int outpos = 0;
......@@ -91,8 +91,8 @@ int rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
}
void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
__u32 srclen, __u32 destlen)
void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen)
{
int positions[256];
int outpos = 0;
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by Arjan van de Ven <arjanv@redhat.com>
*
......@@ -31,7 +31,7 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: compr_rubin.c,v 1.13 2001/09/23 10:06:05 rmk Exp $
* $Id: compr_rubin.c,v 1.16 2002/01/25 01:49:26 dwmw2 Exp $
*
*/
......@@ -43,7 +43,7 @@
void init_rubin(struct rubin_state *rs, int div, int *bits)
static void init_rubin(struct rubin_state *rs, int div, int *bits)
{
int c;
......@@ -56,7 +56,7 @@ void init_rubin(struct rubin_state *rs, int div, int *bits)
}
int encode(struct rubin_state *rs, long A, long B, int symbol)
static int encode(struct rubin_state *rs, long A, long B, int symbol)
{
long i0, i1;
......@@ -91,7 +91,7 @@ int encode(struct rubin_state *rs, long A, long B, int symbol)
}
void end_rubin(struct rubin_state *rs)
static void end_rubin(struct rubin_state *rs)
{
int i;
......@@ -104,7 +104,7 @@ void end_rubin(struct rubin_state *rs)
}
void init_decode(struct rubin_state *rs, int div, int *bits)
static void init_decode(struct rubin_state *rs, int div, int *bits)
{
init_rubin(rs, div, bits);
......@@ -151,7 +151,7 @@ static void __do_decode(struct rubin_state *rs, unsigned long p, unsigned long q
rs->rec_q = rec_q;
}
int decode(struct rubin_state *rs, long A, long B)
static int decode(struct rubin_state *rs, long A, long B)
{
unsigned long p = rs->p, q = rs->q;
long i0, threshold;
......@@ -212,8 +212,8 @@ static int in_byte(struct rubin_state *rs)
int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen)
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;
......@@ -246,20 +246,20 @@ int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
}
#if 0
/* _compress returns the compressed size, -1 if bigger */
int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
__u32 *sourcelen, __u32 *dstlen)
int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
{
return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
}
#endif
int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
__u32 *sourcelen, __u32 *dstlen)
int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
{
int bits[8];
unsigned char histo[256];
int i;
int ret;
__u32 mysrclen, mydstlen;
uint32_t mysrclen, mydstlen;
mysrclen = *sourcelen;
mydstlen = *dstlen - 8;
......@@ -315,8 +315,8 @@ int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
return 0;
}
void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in,
unsigned char *page_out, __u32 srclen, __u32 destlen)
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;
......@@ -330,14 +330,14 @@ void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in,
}
void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
__u32 sourcelen, __u32 dstlen)
void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t sourcelen, uint32_t dstlen)
{
rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
}
void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
__u32 sourcelen, __u32 dstlen)
void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t sourcelen, uint32_t dstlen)
{
int bits[8];
int c;
......
/* Rubin encoder/decoder header */
/* work started at : aug 3, 1994 */
/* last modification : aug 15, 1994 */
/* $Id: compr_rubin.h,v 1.5 2001/02/26 13:50:01 dwmw2 Exp $ */
/* $Id: compr_rubin.h,v 1.6 2002/01/25 01:49:26 dwmw2 Exp $ */
#include "pushpull.h"
......@@ -19,10 +19,3 @@ struct rubin_state {
int bit_divider;
int bits[8];
};
void init_rubin (struct rubin_state *rs, int div, int *bits);
int encode (struct rubin_state *, long, long, int);
void end_rubin (struct rubin_state *);
void init_decode (struct rubin_state *, int div, int *bits);
int decode (struct rubin_state *, long, long);
......@@ -85,7 +85,7 @@ void __exit jffs2_zlib_exit(void)
vfree(inflate_workspace);
}
int zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
{
z_stream strm;
......@@ -145,7 +145,7 @@ int zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
return 0;
}
void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen)
{
z_stream strm;
......
/* $Id: comprtest.c,v 1.4 2001/02/21 14:03:20 dwmw2 Exp $ */
/* $Id: comprtest.c,v 1.5 2002/01/03 15:20:44 dwmw2 Exp $ */
#include <linux/kernel.h>
#include <linux/string.h>
......@@ -266,13 +266,13 @@ 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, __u32 cdatalen, __u32 datalen);
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
__u32 *datalen, __u32 *cdatalen);
uint32_t *datalen, uint32_t *cdatalen);
int init_module(void ) {
unsigned char comprtype;
__u32 c, d;
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",
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,12 +31,13 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: dir.c,v 1.45.2.5 2002/02/23 14:31:09 dwmw2 Exp $
* $Id: dir.c,v 1.68 2002/03/11 12:36:59 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/crc32.h>
#include <linux/mtd/compatmac.h> /* For completion */
......@@ -44,7 +45,6 @@
#include <linux/jffs2_fs_i.h>
#include <linux/jffs2_fs_sb.h>
#include <linux/time.h>
#include <linux/smp_lock.h>
#include "nodelist.h"
static int jffs2_readdir (struct file *, void *, filldir_t);
......@@ -65,7 +65,7 @@ struct file_operations jffs2_dir_operations =
read: generic_read_dir,
readdir: jffs2_readdir,
ioctl: jffs2_ioctl,
fsync: jffs2_null_fsync
fsync: jffs2_fsync
};
......@@ -95,12 +95,11 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target)
struct jffs2_inode_info *dir_f;
struct jffs2_sb_info *c;
struct jffs2_full_dirent *fd = NULL, *fd_list;
__u32 ino = 0;
uint32_t ino = 0;
struct inode *inode = NULL;
D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
lock_kernel();
dir_f = JFFS2_INODE_INFO(dir_i);
c = JFFS2_SB_INFO(dir_i->i_sb);
......@@ -121,12 +120,10 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target)
if (ino) {
inode = iget(dir_i->i_sb, ino);
if (!inode) {
unlock_kernel();
printk(KERN_WARNING "iget() failed for ino #%u\n", ino);
return (ERR_PTR(-EIO));
}
}
unlock_kernel();
d_add(target, inode);
......@@ -158,8 +155,9 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir)
offset++;
}
if (offset == 1) {
D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", parent_ino(filp->f_dentry)));
if (filldir(dirent, "..", 2, 1, parent_ino(filp->f_dentry), DT_DIR) < 0)
unsigned long pino = parent_ino(filp->f_dentry);
D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", pino));
if (filldir(dirent, "..", 2, 1, pino, DT_DIR) < 0)
goto out;
offset++;
}
......@@ -193,50 +191,28 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir)
/***********************************************************************/
static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode)
{
struct jffs2_raw_inode *ri;
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;
__u32 alloclen, phys_ofs;
__u32 writtenlen;
int ret;
lock_kernel();
ri = jffs2_alloc_raw_inode();
if (!ri) {
unlock_kernel();
if (!ri)
return -ENOMEM;
}
c = JFFS2_SB_INFO(dir_i->i_sb);
D1(printk(KERN_DEBUG "jffs2_create()\n"));
/* Try to reserve enough space for both node and dirent.
* Just the node will do for now, though
*/
namelen = dentry->d_name.len;
ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
D1(printk(KERN_DEBUG "jffs2_create(): reserved 0x%x bytes\n", alloclen));
if (ret) {
jffs2_free_raw_inode(ri);
unlock_kernel();
return ret;
}
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);
jffs2_complete_reservation(c);
unlock_kernel();
return PTR_ERR(inode);
}
......@@ -246,278 +222,71 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode)
inode->i_mapping->nrpages = 0;
f = JFFS2_INODE_INFO(inode);
dir_f = JFFS2_INODE_INFO(dir_i);
ri->data_crc = 0;
ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
D1(printk(KERN_DEBUG "jffs2_create created file with mode 0x%x\n", ri->mode));
jffs2_free_raw_inode(ri);
if (IS_ERR(fn)) {
D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
/* Eeek. Wave bye bye */
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
unlock_kernel();
return PTR_ERR(fn);
}
/* No data here. Only a metadata node, which will be
obsoleted by the first data write
*/
f->metadata = fn;
/* Work out where to put the dirent node now. */
writtenlen = PAD(writtenlen);
phys_ofs += writtenlen;
alloclen -= writtenlen;
up(&f->sem);
if (alloclen < sizeof(*rd)+namelen) {
/* Not enough space left in this chunk. Get some more */
jffs2_complete_reservation(c);
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
ret = jffs2_do_create(c, dir_f, f, ri,
dentry->d_name.name, dentry->d_name.len);
if (ret) {
/* Eep. */
D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
jffs2_clear_inode(inode);
unlock_kernel();
make_bad_inode(inode);
iput(inode);
jffs2_free_raw_inode(ri);
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);
unlock_kernel();
return -ENOMEM;
}
dir_f = JFFS2_INODE_INFO(dir_i);
down(&dir_f->sem);
rd->magic = JFFS2_MAGIC_BITMASK;
rd->nodetype = JFFS2_NODETYPE_DIRENT;
rd->totlen = sizeof(*rd) + namelen;
rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
rd->pino = dir_i->i_ino;
rd->version = ++dir_f->highest_version;
rd->ino = inode->i_ino;
rd->mctime = CURRENT_TIME;
rd->nsize = namelen;
rd->type = DT_REG;
rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
rd->name_crc = crc32(0, dentry->d_name.name, namelen);
fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
jffs2_complete_reservation(c);
if (IS_ERR(fd)) {
/* dirent failed to write. Delete the inode normally
as if it were the final unlink() */
jffs2_free_raw_dirent(rd);
up(&dir_f->sem);
jffs2_clear_inode(inode);
unlock_kernel();
return PTR_ERR(fd);
}
dir_i->i_mtime = dir_i->i_ctime = 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);
dir_i->i_mtime = dir_i->i_ctime = 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));
unlock_kernel();
return 0;
}
/***********************************************************************/
static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int rename)
{
struct jffs2_inode_info *dir_f, *f;
struct jffs2_sb_info *c;
struct jffs2_raw_dirent *rd;
struct jffs2_full_dirent *fd;
__u32 alloclen, phys_ofs;
int ret;
c = JFFS2_SB_INFO(dir_i->i_sb);
rd = jffs2_alloc_raw_dirent();
if (!rd)
return -ENOMEM;
ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_DELETION);
if (ret) {
jffs2_free_raw_dirent(rd);
return ret;
}
dir_f = JFFS2_INODE_INFO(dir_i);
down(&dir_f->sem);
/* Build a deletion node */
rd->magic = JFFS2_MAGIC_BITMASK;
rd->nodetype = JFFS2_NODETYPE_DIRENT;
rd->totlen = sizeof(*rd) + dentry->d_name.len;
rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
rd->pino = dir_i->i_ino;
rd->version = ++dir_f->highest_version;
rd->ino = 0;
rd->mctime = CURRENT_TIME;
rd->nsize = dentry->d_name.len;
rd->type = DT_UNKNOWN;
rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
jffs2_complete_reservation(c);
jffs2_free_raw_dirent(rd);
if (IS_ERR(fd)) {
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);
if (!rename) {
f = JFFS2_INODE_INFO(dentry->d_inode);
down(&f->sem);
while (f->dents) {
/* There can be only deleted ones */
fd = f->dents;
f->dents = fd->next;
if (fd->ino) {
printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
f->inocache->ino, fd->name, fd->ino);
} else {
D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, f->inocache->ino));
}
jffs2_mark_node_obsolete(c, fd->raw);
jffs2_free_full_dirent(fd);
}
f->inocache->nlink--;
dentry->d_inode->i_nlink--;
up(&f->sem);
}
return 0;
}
static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry)
{
int res;
lock_kernel();
res = jffs2_do_unlink(dir_i, dentry, 0);
unlock_kernel();
return res;
}
/***********************************************************************/
static int jffs2_do_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry, int rename)
{
struct jffs2_inode_info *dir_f, *f;
struct jffs2_sb_info *c;
struct jffs2_raw_dirent *rd;
struct jffs2_full_dirent *fd;
__u32 alloclen, phys_ofs;
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;
c = JFFS2_SB_INFO(dir_i->i_sb);
rd = jffs2_alloc_raw_dirent();
if (!rd)
return -ENOMEM;
ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
jffs2_free_raw_dirent(rd);
ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name,
dentry->d_name.len, dead_f);
dentry->d_inode->i_nlink = dead_f->inocache->nlink;
return ret;
}
dir_f = JFFS2_INODE_INFO(dir_i);
down(&dir_f->sem);
/* Build a deletion node */
rd->magic = JFFS2_MAGIC_BITMASK;
rd->nodetype = JFFS2_NODETYPE_DIRENT;
rd->totlen = sizeof(*rd) + dentry->d_name.len;
rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
rd->pino = dir_i->i_ino;
rd->version = ++dir_f->highest_version;
rd->ino = old_dentry->d_inode->i_ino;
rd->mctime = CURRENT_TIME;
rd->nsize = dentry->d_name.len;
/* XXX: This is ugly. */
rd->type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
if (!rd->type) rd->type = DT_REG;
rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
jffs2_complete_reservation(c);
jffs2_free_raw_dirent(rd);
if (IS_ERR(fd)) {
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);
if (!rename) {
f = JFFS2_INODE_INFO(old_dentry->d_inode);
down(&f->sem);
old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
up(&f->sem);
}
return 0;
}
/***********************************************************************/
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;
if (S_ISDIR(old_dentry->d_inode->i_mode))
return -EPERM;
lock_kernel();
ret = jffs2_do_link(old_dentry, dir_i, dentry, 0);
/* XXX: This is ugly */
type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
if (!type) type = DT_REG;
ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len);
if (!ret) {
down(&f->sem);
old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
up(&f->sem);
d_instantiate(dentry, old_dentry->d_inode);
atomic_inc(&old_dentry->d_inode->i_count);
}
unlock_kernel();
return ret;
}
......@@ -533,8 +302,8 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
struct jffs2_full_dnode *fn;
struct jffs2_full_dirent *fd;
int namelen;
__u32 alloclen, phys_ofs;
__u32 writtenlen;
uint32_t alloclen, phys_ofs;
uint32_t writtenlen;
int ret;
/* FIXME: If you care. We'd need to use frags for the target
......@@ -542,7 +311,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
if (strlen(target) > 254)
return -EINVAL;
lock_kernel();
ri = jffs2_alloc_raw_inode();
if (!ri)
......@@ -558,7 +326,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
if (ret) {
jffs2_free_raw_inode(ri);
unlock_kernel();
return ret;
}
......@@ -567,7 +334,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
if (IS_ERR(inode)) {
jffs2_free_raw_inode(ri);
jffs2_complete_reservation(c);
unlock_kernel();
return PTR_ERR(inode);
}
......@@ -583,7 +349,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
ri->data_crc = crc32(0, target, strlen(target));
ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
fn = jffs2_write_dnode(inode, ri, target, strlen(target), phys_ofs, &writtenlen);
fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, &writtenlen);
jffs2_free_raw_inode(ri);
......@@ -592,7 +358,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
unlock_kernel();
return PTR_ERR(fn);
}
/* No data here. Only a metadata node, which will be
......@@ -613,7 +378,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
if (ret) {
/* Eep. */
jffs2_clear_inode(inode);
unlock_kernel();
return ret;
}
}
......@@ -623,7 +387,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
/* Argh. Now we treat it like a normal delete */
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
unlock_kernel();
return -ENOMEM;
}
......@@ -644,19 +407,18 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
rd->name_crc = crc32(0, dentry->d_name.name, namelen);
fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
jffs2_complete_reservation(c);
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
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);
unlock_kernel();
return PTR_ERR(fd);
}
dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
jffs2_free_raw_dirent(rd);
......@@ -664,10 +426,11 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
/* 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);
unlock_kernel();
return 0;
}
......@@ -682,18 +445,15 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
struct jffs2_full_dnode *fn;
struct jffs2_full_dirent *fd;
int namelen;
__u32 alloclen, phys_ofs;
__u32 writtenlen;
uint32_t alloclen, phys_ofs;
uint32_t writtenlen;
int ret;
mode |= S_IFDIR;
lock_kernel();
ri = jffs2_alloc_raw_inode();
if (!ri) {
unlock_kernel();
if (!ri)
return -ENOMEM;
}
c = JFFS2_SB_INFO(dir_i->i_sb);
......@@ -705,7 +465,6 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
if (ret) {
jffs2_free_raw_inode(ri);
unlock_kernel();
return ret;
}
......@@ -714,19 +473,20 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
if (IS_ERR(inode)) {
jffs2_free_raw_inode(ri);
jffs2_complete_reservation(c);
unlock_kernel();
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 = 0;
ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen);
jffs2_free_raw_inode(ri);
......@@ -735,7 +495,6 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
unlock_kernel();
return PTR_ERR(fn);
}
/* No data here. Only a metadata node, which will be
......@@ -756,7 +515,6 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
if (ret) {
/* Eep. */
jffs2_clear_inode(inode);
unlock_kernel();
return ret;
}
}
......@@ -766,7 +524,6 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
/* Argh. Now we treat it like a normal delete */
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
unlock_kernel();
return -ENOMEM;
}
......@@ -787,31 +544,31 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
rd->name_crc = crc32(0, dentry->d_name.name, namelen);
fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
jffs2_complete_reservation(c);
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
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);
unlock_kernel();
return PTR_ERR(fd);
}
dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
dir_i->i_nlink++;
jffs2_free_raw_dirent(rd);
/* Link the fd into the inode's list, obsoleting an old
one if necessary. */
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
up(&dir_f->sem);
jffs2_complete_reservation(c);
d_instantiate(dentry, inode);
unlock_kernel();
return 0;
}
......@@ -819,16 +576,16 @@ 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;
lock_kernel();
for (fd = f->dents ; fd; fd = fd->next) {
if (fd->ino) {
unlock_kernel();
if (fd->ino)
return -ENOTEMPTY;
}
}
unlock_kernel();
return jffs2_unlink(dir_i, dentry);
ret = jffs2_unlink(dir_i, dentry);
if (!ret)
dir_i->i_nlink--;
return ret;
}
static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, int rdev)
......@@ -843,16 +600,13 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in
int namelen;
unsigned short dev;
int devlen = 0;
__u32 alloclen, phys_ofs;
__u32 writtenlen;
uint32_t alloclen, phys_ofs;
uint32_t writtenlen;
int ret;
lock_kernel();
ri = jffs2_alloc_raw_inode();
if (!ri) {
unlock_kernel();
if (!ri)
return -ENOMEM;
}
c = JFFS2_SB_INFO(dir_i->i_sb);
......@@ -869,7 +623,6 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in
if (ret) {
jffs2_free_raw_inode(ri);
unlock_kernel();
return ret;
}
......@@ -878,7 +631,6 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in
if (IS_ERR(inode)) {
jffs2_free_raw_inode(ri);
jffs2_complete_reservation(c);
unlock_kernel();
return PTR_ERR(inode);
}
inode->i_op = &jffs2_file_inode_operations;
......@@ -894,7 +646,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in
ri->data_crc = crc32(0, &dev, devlen);
ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
fn = jffs2_write_dnode(inode, ri, (char *)&dev, devlen, phys_ofs, &writtenlen);
fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, &writtenlen);
jffs2_free_raw_inode(ri);
......@@ -903,7 +655,6 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
unlock_kernel();
return PTR_ERR(fn);
}
/* No data here. Only a metadata node, which will be
......@@ -924,7 +675,6 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in
if (ret) {
/* Eep. */
jffs2_clear_inode(inode);
unlock_kernel();
return ret;
}
}
......@@ -934,7 +684,6 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in
/* Argh. Now we treat it like a normal delete */
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
unlock_kernel();
return -ENOMEM;
}
......@@ -958,17 +707,15 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in
rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
rd->name_crc = crc32(0, dentry->d_name.name, namelen);
fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
jffs2_complete_reservation(c);
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
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);
unlock_kernel();
return PTR_ERR(fd);
}
......@@ -979,8 +726,9 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in
/* 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);
unlock_kernel();
jffs2_complete_reservation(c);
d_instantiate(dentry, inode);
......@@ -991,35 +739,54 @@ 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);
uint8_t type;
/* 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 */
lock_kernel();
ret = jffs2_do_link(old_dentry, new_dir_i, new_dentry, 1);
if (ret) {
unlock_kernel();
/* XXX: This is ugly */
type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
if (!type) type = DT_REG;
ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i),
old_dentry->d_inode->i_ino, type,
new_dentry->d_name.name, new_dentry->d_name.len);
if (ret)
return ret;
}
if (S_ISDIR(old_dentry->d_inode->i_mode))
new_dir_i->i_nlink++;
/* Unlink the original */
ret = jffs2_do_unlink(old_dir_i, old_dentry, 1);
ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
old_dentry->d_name.name, old_dentry->d_name.len, NULL);
/* We don't touch inode->i_nlink */
if (ret) {
/* Oh shit. We really ought to make a single node which can do both atomically */
struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
down(&f->sem);
old_dentry->d_inode->i_nlink = f->inocache->nlink++;
old_dentry->d_inode->i_nlink++;
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);
}
unlock_kernel();
return ret;
}
if (S_ISDIR(old_dentry->d_inode->i_mode))
old_dir_i->i_nlink--;
return 0;
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,14 +31,15 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: erase.c,v 1.24 2001/12/06 16:38:38 dwmw2 Exp $
* $Id: erase.c,v 1.35 2002/03/08 15:11:24 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/jffs2.h>
#include <linux/interrupt.h>
#include <linux/compiler.h>
#include <linux/crc32.h>
#include "nodelist.h"
......@@ -48,12 +49,13 @@ struct erase_priv_struct {
};
static void jffs2_erase_callback(struct erase_info *);
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct erase_info *instr;
int ret;
struct erase_info *instr;
instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
if (!instr) {
......@@ -77,10 +79,16 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
((struct erase_priv_struct *)instr->priv)->jeb = jeb;
((struct erase_priv_struct *)instr->priv)->c = c;
/* NAND , read out the fail counter, if possible */
if (!jffs2_can_mark_obsolete(c))
jffs2_nand_read_failcnt(c,jeb);
ret = c->mtd->erase(c->mtd, instr);
if (!ret) {
if (!ret)
return;
}
kfree(instr);
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));
......@@ -89,7 +97,6 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
list_add(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
spin_unlock_bh(&c->erase_completion_lock);
kfree(instr);
return;
}
......@@ -97,6 +104,7 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
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);
spin_lock_bh(&c->erase_completion_lock);
list_del(&jeb->list);
list_add(&jeb->list, &c->bad_list);
......@@ -105,13 +113,14 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
c->erasing_size -= c->sector_size;
spin_unlock_bh(&c->erase_completion_lock);
wake_up(&c->erase_wait);
kfree(instr);
}
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
{
struct jffs2_eraseblock *jeb;
down(&c->erase_free_sem);
spin_lock_bh(&c->erase_completion_lock);
while (!list_empty(&c->erase_pending_list)) {
......@@ -130,35 +139,50 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
spin_unlock_bh(&c->erase_completion_lock);
jffs2_erase_block(c, jeb);
/* Be nice */
cond_resched();
spin_lock_bh(&c->erase_completion_lock);
}
spin_unlock_bh(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
up(&c->erase_free_sem);
}
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
spin_lock(&c->erase_completion_lock);
list_del(&jeb->list);
list_add_tail(&jeb->list, &c->erase_complete_list);
spin_unlock(&c->erase_completion_lock);
}
static inline void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->bad_size += c->sector_size;
list_del(&jeb->list);
list_add(&jeb->list, &c->bad_list);
c->nr_erasing_blocks--;
spin_unlock(&c->erase_completion_lock);
wake_up(&c->erase_wait);
}
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);
spin_lock(&priv->c->erase_completion_lock);
priv->c->erasing_size -= priv->c->sector_size;
priv->c->bad_size += priv->c->sector_size;
list_del(&priv->jeb->list);
list_add(&priv->jeb->list, &priv->c->bad_list);
priv->c->nr_erasing_blocks--;
spin_unlock(&priv->c->erase_completion_lock);
wake_up(&priv->c->erase_wait);
jffs2_erase_failed(priv->c, priv->jeb);
} else {
D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", instr->addr));
spin_lock(&priv->c->erase_completion_lock);
list_del(&priv->jeb->list);
list_add_tail(&priv->jeb->list, &priv->c->erase_complete_list);
spin_unlock(&priv->c->erase_completion_lock);
jffs2_erase_succeeded(priv->c, priv->jeb);
}
/* Make sure someone picks up the block off the erase_complete list */
OFNI_BS_2SFFJ(priv->c)->s_dirt = 1;
......@@ -268,11 +292,12 @@ void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
totlen: sizeof(struct jffs2_unknown_node)
};
struct jffs2_eraseblock *jeb;
struct jffs2_raw_node_ref *marker_ref;
struct jffs2_raw_node_ref *marker_ref = NULL;
unsigned char *ebuf;
ssize_t retlen;
size_t retlen;
int ret;
if (unlikely(!marker.hdr_crc))
marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4);
spin_lock_bh(&c->erase_completion_lock);
......@@ -281,6 +306,7 @@ void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
list_del(&jeb->list);
spin_unlock_bh(&c->erase_completion_lock);
if (!jffs2_cleanmarker_oob(c)) {
marker_ref = jffs2_alloc_raw_node_ref();
if (!marker_ref) {
printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
......@@ -288,20 +314,20 @@ void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
jffs2_erase_pending_trigger(c);
return;
}
}
ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!ebuf) {
printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
} else {
__u32 ofs = jeb->offset;
uint32_t ofs = jeb->offset;
D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
while(ofs < jeb->offset + c->sector_size) {
__u32 readlen = min((__u32)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
int i;
ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
if (ret < 0) {
ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
if (ret) {
printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
goto bad;
}
......@@ -315,7 +341,10 @@ void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
if (datum + 1) {
printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
bad:
if (!jffs2_cleanmarker_oob(c))
jffs2_free_raw_node_ref(marker_ref);
else
jffs2_write_nand_badblock( c ,jeb );
kfree(ebuf);
bad2:
spin_lock_bh(&c->erase_completion_lock);
......@@ -336,7 +365,18 @@ void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
/* Write the erase complete marker */
D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
ret = c->mtd->write(c->mtd, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
if (jffs2_cleanmarker_oob(c)) {
if (jffs2_write_nand_cleanmarker(c, jeb))
goto bad2;
jeb->first_node = jeb->last_node = NULL;
jeb->free_size = c->sector_size;
jeb->used_size = 0;
jeb->dirty_size = 0;
} else {
ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
if (ret) {
printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
jeb->offset, ret);
......@@ -358,6 +398,7 @@ void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
jeb->free_size = c->sector_size - marker_ref->totlen;
jeb->used_size = marker_ref->totlen;
jeb->dirty_size = 0;
}
spin_lock_bh(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,7 +31,7 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: file.c,v 1.58.2.1 2002/02/23 14:25:36 dwmw2 Exp $
* $Id: file.c,v 1.70 2002/03/05 09:55:07 dwmw2 Exp $
*
*/
......@@ -39,6 +39,7 @@
#include <linux/mtd/compatmac.h> /* for min() */
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include <linux/jffs2.h>
......@@ -48,9 +49,26 @@ extern int generic_file_open(struct inode *, struct file *) __attribute__((weak)
extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) __attribute__((weak));
int jffs2_null_fsync(struct file *filp, struct dentry *dentry, int datasync)
int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
{
/* Move along. Nothing to see here */
struct inode *inode = dentry->d_inode;
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
if (!c->wbuf || !c->wbuf_len)
return 0;
/* flush write buffer and update c->nextblock */
/* FIXME NAND */
/* At the moment we flush the buffer, to make sure
* that every thing is on the flash.
* maybe we have to think about it to find a smarter
* solution.
*/
down(&f->sem);
jffs2_flush_wbuf(c,2);
up(&f->sem);
return 0;
}
......@@ -62,7 +80,7 @@ struct file_operations jffs2_file_operations =
write: generic_file_write,
ioctl: jffs2_ioctl,
mmap: generic_file_mmap,
fsync: jffs2_null_fsync
fsync: jffs2_fsync
};
/* jffs2_file_inode_operations */
......@@ -90,7 +108,7 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
unsigned char *mdata = NULL;
int mdatalen = 0;
unsigned int ivalid;
__u32 phys_ofs, alloclen;
uint32_t phys_ofs, alloclen;
int ret;
D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
ret = inode_change_ok(inode, iattr);
......@@ -132,7 +150,7 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
jffs2_free_raw_inode(ri);
if (S_ISLNK(inode->i_mode))
if (S_ISLNK(inode->i_mode & S_IFMT))
kfree(mdata);
return ret;
}
......@@ -175,13 +193,12 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
else
ri->data_crc = 0;
new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL);
new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, NULL);
if (S_ISLNK(inode->i_mode))
kfree(mdata);
jffs2_complete_reservation(c);
if (IS_ERR(new_metadata)) {
jffs2_complete_reservation(c);
jffs2_free_raw_inode(ri);
up(&f->sem);
return PTR_ERR(new_metadata);
......@@ -214,7 +231,10 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
jffs2_free_full_dnode(old_metadata);
}
jffs2_free_raw_inode(ri);
up(&f->sem);
jffs2_complete_reservation(c);
return 0;
}
......@@ -222,85 +242,30 @@ 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);
struct jffs2_node_frag *frag = f->fraglist;
__u32 offset = pg->index << PAGE_CACHE_SHIFT;
__u32 end = offset + PAGE_CACHE_SIZE;
unsigned char *pg_buf;
int ret;
D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%x\n", inode->i_ino, offset));
D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
if (!PageLocked(pg))
PAGE_BUG(pg);
while(frag && frag->ofs + frag->size <= offset) {
// D1(printk(KERN_DEBUG "skipping frag %d-%d; before the region we care about\n", frag->ofs, frag->ofs + frag->size));
frag = frag->next;
}
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);
/* 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_readpage: offset %d, end %d\n", offset, end));
if (!frag || frag->ofs > offset) {
__u32 holesize = end - offset;
if (frag) {
D1(printk(KERN_NOTICE "Eep. Hole in ino %ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", inode->i_ino, frag->ofs, offset));
holesize = min(holesize, frag->ofs - offset);
D1(jffs2_print_frag_list(f));
}
D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
memset(pg_buf, 0, holesize);
pg_buf += holesize;
offset += holesize;
continue;
} else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) {
D1(printk(KERN_NOTICE "Eep. Overlap in ino #%ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
inode->i_ino, frag->ofs, offset));
D1(jffs2_print_frag_list(f));
memset(pg_buf, 0, end - offset);
ClearPageUptodate(pg);
SetPageError(pg);
kunmap(pg);
return -EIO;
} else if (!frag->node) {
__u32 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(pg_buf, 0, holeend - offset);
pg_buf += holeend - offset;
offset = holeend;
frag = frag->next;
continue;
} else {
__u32 readlen;
readlen = min(frag->size, end - offset);
D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs, frag->ofs+readlen, frag->node->raw->flash_offset & ~3));
ret = jffs2_read_dnode(c, frag->node, pg_buf, frag->ofs - frag->node->ofs, readlen);
D2(printk(KERN_DEBUG "node read done\n"));
if (ret) {
D1(printk(KERN_DEBUG"jffs2_readpage error %d\n",ret));
memset(pg_buf, 0, frag->size);
ClearPageUptodate(pg);
SetPageError(pg);
kunmap(pg);
return ret;
}
}
pg_buf += frag->size;
offset += frag->size;
frag = frag->next;
D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
}
D2(printk(KERN_DEBUG "readpage finishing\n"));
} else {
SetPageUptodate(pg);
ClearPageError(pg);
}
flush_dcache_page(pg);
kunmap(pg);
D1(printk(KERN_DEBUG "readpage finished\n"));
return 0;
}
......@@ -328,18 +293,18 @@ int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, uns
{
struct inode *inode = filp->f_dentry->d_inode;
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
__u32 pageofs = pg->index << PAGE_CACHE_SHIFT;
uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT;
int ret = 0;
down(&f->sem);
D1(printk(KERN_DEBUG "jffs2_prepare_write() nrpages %ld\n", inode->i_mapping->nrpages));
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;
__u32 phys_ofs, alloc_len;
uint32_t phys_ofs, alloc_len;
D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
(unsigned int)inode->i_size, pageofs));
......@@ -361,7 +326,7 @@ int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, uns
ri.mode = inode->i_mode;
ri.uid = inode->i_uid;
ri.gid = inode->i_gid;
ri.isize = max((__u32)inode->i_size, pageofs);
ri.isize = max((uint32_t)inode->i_size, pageofs);
ri.atime = ri.ctime = ri.mtime = CURRENT_TIME;
ri.offset = inode->i_size;
ri.dsize = pageofs - inode->i_size;
......@@ -370,10 +335,11 @@ int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, uns
ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
ri.data_crc = 0;
fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
jffs2_complete_reservation(c);
fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, NULL);
if (IS_ERR(fn)) {
ret = PTR_ERR(fn);
jffs2_complete_reservation(c);
up(&f->sem);
return ret;
}
......@@ -387,9 +353,11 @@ int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, uns
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;
}
......@@ -397,7 +365,7 @@ int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, uns
/* Read in the page if it wasn't already present */
if (!Page_Uptodate(pg) && (start || end < PAGE_SIZE))
ret = jffs2_do_readpage_nolock(inode, pg);
D1(printk(KERN_DEBUG "end prepare_write(). nrpages %ld\n", inode->i_mapping->nrpages));
D1(printk(KERN_DEBUG "end prepare_write()\n"));
up(&f->sem);
return ret;
}
......@@ -410,119 +378,49 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi
struct inode *inode = filp->f_dentry->d_inode;
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
__u32 newsize = max_t(__u32, filp->f_dentry->d_inode->i_size, (pg->index << PAGE_CACHE_SHIFT) + end);
__u32 file_ofs = (pg->index << PAGE_CACHE_SHIFT);
__u32 writelen = min((__u32)PAGE_CACHE_SIZE, newsize - file_ofs);
struct jffs2_raw_inode *ri;
int ret = 0;
ssize_t writtenlen = 0;
uint32_t writtenlen = 0;
D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, nrpages %ld\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, filp->f_dentry->d_inode->i_mapping->nrpages));
D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d\n",
inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end));
ri = jffs2_alloc_raw_inode();
if (!ri)
return -ENOMEM;
while(writelen) {
struct jffs2_full_dnode *fn;
unsigned char *comprbuf = NULL;
unsigned char comprtype = JFFS2_COMPR_NONE;
__u32 phys_ofs, alloclen;
__u32 datalen, cdatalen;
D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, file_ofs));
ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
SetPageError(pg);
D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
break;
}
down(&f->sem);
datalen = writelen;
cdatalen = min(alloclen - sizeof(*ri), writelen);
comprbuf = kmalloc(cdatalen, GFP_KERNEL);
if (comprbuf) {
comprtype = jffs2_compress(page_address(pg)+ (file_ofs & (PAGE_CACHE_SIZE-1)), comprbuf, &datalen, &cdatalen);
}
if (comprtype == JFFS2_COMPR_NONE) {
/* Either compression failed, or the allocation of comprbuf failed */
if (comprbuf)
kfree(comprbuf);
comprbuf = page_address(pg) + (file_ofs & (PAGE_CACHE_SIZE -1));
datalen = cdatalen;
if (!ri) {
D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
return -ENOMEM;
}
/* Now comprbuf points to the data to be written, be it compressed or not.
comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means
that the comprbuf doesn't need to be kfree()d.
*/
ri->magic = JFFS2_MAGIC_BITMASK;
ri->nodetype = JFFS2_NODETYPE_INODE;
ri->totlen = sizeof(*ri) + cdatalen;
ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
/* Set the fields that the generic jffs2_write_inode_range() code can't find */
ri->ino = inode->i_ino;
ri->version = ++f->highest_version;
ri->mode = inode->i_mode;
ri->uid = inode->i_uid;
ri->gid = inode->i_gid;
ri->isize = max((__u32)inode->i_size, file_ofs + datalen);
ri->isize = (uint32_t)inode->i_size;
ri->atime = ri->ctime = ri->mtime = CURRENT_TIME;
ri->offset = file_ofs;
ri->csize = cdatalen;
ri->dsize = datalen;
ri->compr = comprtype;
ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
ri->data_crc = crc32(0, comprbuf, cdatalen);
fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL);
jffs2_complete_reservation(c);
if (comprtype != JFFS2_COMPR_NONE)
kfree(comprbuf);
/* We rely on the fact that generic_file_write() currently kmaps the page for us. */
ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start,
(pg->index << PAGE_CACHE_SHIFT) + start, end - start, &writtenlen);
if (IS_ERR(fn)) {
ret = PTR_ERR(fn);
up(&f->sem);
SetPageError(pg);
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;
}
up(&f->sem);
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);
/* There was an error writing. */
SetPageError(pg);
break;
}
inode->i_size = ri->isize;
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 = ri->ctime;
if (!datalen) {
printk(KERN_WARNING "Eep. We didn't actually write any bloody data\n");
ret = -EIO;
SetPageError(pg);
break;
}
D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
writtenlen += datalen;
file_ofs += datalen;
writelen -= datalen;
}
jffs2_free_raw_inode(ri);
if (writtenlen < end) {
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 */
......@@ -530,13 +428,7 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi
SetPageError(pg);
ClearPageUptodate(pg);
}
if (writtenlen <= start) {
/* We didn't even get to the start of the affected part */
ret = ret?ret:-ENOSPC;
D1(printk(KERN_DEBUG "jffs2_commit_write(): Only %x bytes written to page. start (%x) not reached, returning %d\n", writtenlen, start, ret));
}
writtenlen = min(end-start, writtenlen-start);
D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d. nrpages is %ld\n",writtenlen?writtenlen:ret, inode->i_mapping->nrpages));
D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",writtenlen?writtenlen:ret));
return writtenlen?writtenlen:ret;
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* The original JFFS, from which the design for JFFS2 was derived,
* was designed and implemented by Axis Communications AB.
*
* The contents of this file are subject to the Red Hat eCos Public
* License Version 1.1 (the "Licence"); you may not use this file
* except in compliance with the Licence. You may obtain a copy of
* the Licence at http://www.redhat.com/
*
* Software distributed under the Licence is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the Licence for the specific language governing rights and
* limitations under the Licence.
*
* The Original Code is JFFS2 - Journalling Flash File System, version 2
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in
* which case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the RHEPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: fs.c,v 1.4 2002/03/11 12:36:59 dwmw2 Exp $
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include "nodelist.h"
int jffs2_statfs(struct super_block *sb, struct statfs *buf)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
unsigned long avail;
buf->f_type = JFFS2_SUPER_MAGIC;
buf->f_bsize = 1 << PAGE_SHIFT;
buf->f_blocks = c->flash_size >> PAGE_SHIFT;
buf->f_files = 0;
buf->f_ffree = 0;
buf->f_namelen = JFFS2_MAX_NAME_LEN;
spin_lock_bh(&c->erase_completion_lock);
avail = c->dirty_size + c->free_size;
if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE)
avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE;
else
avail = 0;
buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
#if CONFIG_JFFS2_FS_DEBUG > 0
printk(KERN_DEBUG "STATFS:\n");
printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE);
if (c->nextblock) {
printk(KERN_DEBUG "nextblock: 0x%08x\n", c->nextblock->offset);
} else {
printk(KERN_DEBUG "nextblock: NULL\n");
}
if (c->gcblock) {
printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->offset);
} else {
printk(KERN_DEBUG "gcblock: NULL\n");
}
if (list_empty(&c->clean_list)) {
printk(KERN_DEBUG "clean_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->clean_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "clean_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->dirty_list)) {
printk(KERN_DEBUG "dirty_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->dirty_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "dirty_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->erasable_list)) {
printk(KERN_DEBUG "erasable_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->erasable_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "erasable_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->erasing_list)) {
printk(KERN_DEBUG "erasing_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->erasing_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "erasing_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->erase_pending_list)) {
printk(KERN_DEBUG "erase_pending_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->erase_pending_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "erase_pending_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->erasable_pending_wbuf_list)) {
printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->erasable_pending_wbuf_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "erase_pending_wbuf_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->free_list)) {
printk(KERN_DEBUG "free_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->free_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "free_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->bad_list)) {
printk(KERN_DEBUG "bad_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->bad_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "bad_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->bad_used_list)) {
printk(KERN_DEBUG "bad_used_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->bad_used_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "bad_used_list: %08x\n", jeb->offset);
}
}
#endif /* CONFIG_JFFS2_FS_DEBUG */
spin_unlock_bh(&c->erase_completion_lock);
return 0;
}
void jffs2_clear_inode (struct inode *inode)
{
/* We can forget about this inode for now - drop all
* the nodelists associated with it, etc.
*/
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
jffs2_do_clear_inode(c, f);
}
void jffs2_read_inode (struct inode *inode)
{
struct jffs2_inode_info *f;
struct jffs2_sb_info *c;
struct jffs2_raw_inode latest_node;
int ret;
D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
f = JFFS2_INODE_INFO(inode);
c = JFFS2_SB_INFO(inode->i_sb);
jffs2_init_inode_info(f);
ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
if (ret) {
make_bad_inode(inode);
up(&f->sem);
return;
}
inode->i_mode = latest_node.mode;
inode->i_uid = latest_node.uid;
inode->i_gid = latest_node.gid;
inode->i_size = latest_node.isize;
inode->i_atime = latest_node.atime;
inode->i_mtime = latest_node.mtime;
inode->i_ctime = latest_node.ctime;
inode->i_nlink = f->inocache->nlink;
inode->i_blksize = PAGE_SIZE;
inode->i_blocks = (inode->i_size + 511) >> 9;
switch (inode->i_mode & S_IFMT) {
unsigned short rdev;
case S_IFLNK:
inode->i_op = &jffs2_symlink_inode_operations;
break;
case S_IFDIR:
{
struct jffs2_full_dirent *fd;
for (fd=f->dents; fd; fd = fd->next) {
if (fd->type == DT_DIR && fd->ino)
inode->i_nlink++;
}
/* and '..' */
inode->i_nlink++;
/* Root dir gets i_nlink 3 for some reason */
if (inode->i_ino == 1)
inode->i_nlink++;
inode->i_op = &jffs2_dir_inode_operations;
inode->i_fop = &jffs2_dir_operations;
break;
}
case S_IFREG:
inode->i_op = &jffs2_file_inode_operations;
inode->i_fop = &jffs2_file_operations;
inode->i_mapping->a_ops = &jffs2_file_address_operations;
inode->i_mapping->nrpages = 0;
break;
case S_IFBLK:
case S_IFCHR:
/* Read the device numbers from the media */
D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
/* Eep */
printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
jffs2_do_clear_inode(c, f);
make_bad_inode(inode);
up(&f->sem);
return;
}
case S_IFSOCK:
case S_IFIFO:
inode->i_op = &jffs2_file_inode_operations;
init_special_inode(inode, inode->i_mode, kdev_t_to_nr(mk_kdev(rdev>>8, rdev&0xff)));
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"));
}
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 */
if (!(sb->s_flags & MS_RDONLY))
jffs2_stop_garbage_collect_thread(c);
if (!(*flags & MS_RDONLY))
jffs2_start_garbage_collect_thread(c);
sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
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("jffs2_write_super(): flush_wbuf before gc-trigger\n"));
jffs2_flush_wbuf(c, 2);
jffs2_garbage_collect_trigger(c);
jffs2_erase_pending_blocks(c);
jffs2_mark_erased_blocks(c);
}
/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
fill in the raw_inode while you're at it. */
struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
{
struct inode *inode;
struct super_block *sb = dir_i->i_sb;
struct jffs2_sb_info *c;
struct jffs2_inode_info *f;
int ret;
D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
c = JFFS2_SB_INFO(sb);
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
f = JFFS2_INODE_INFO(inode);
jffs2_init_inode_info(f);
memset(ri, 0, sizeof(*ri));
/* Set OS-specific defaults for new inodes */
ri->uid = current->fsuid;
if (dir_i->i_mode & S_ISGID) {
ri->gid = dir_i->i_gid;
if (S_ISDIR(mode))
ri->mode |= S_ISGID;
} else {
ri->gid = current->fsgid;
}
ri->mode = 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 = ri->ino;
inode->i_mode = ri->mode;
inode->i_gid = ri->gid;
inode->i_uid = ri->uid;
inode->i_atime = inode->i_ctime = inode->i_mtime =
ri->atime = ri->mtime = ri->ctime = CURRENT_TIME;
inode->i_blksize = PAGE_SIZE;
inode->i_blocks = 0;
inode->i_size = 0;
insert_inode_hash(inode);
return inode;
}
int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
{
struct jffs2_sb_info *c;
struct inode *root_i;
c = JFFS2_SB_INFO(sb);
c->sector_size = c->mtd->erasesize;
c->flash_size = c->mtd->size;
if (c->sector_size < 0x10000) {
printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using 64KiB instead\n",
c->sector_size / 1024);
c->sector_size = 0x10000;
}
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;
}
if (c->mtd->type == MTD_NANDFLASH) {
/* Initialise write buffer */
c->wbuf_pagesize = c->mtd->oobblock;
c->wbuf_ofs = 0xFFFFFFFF;
c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
if (!c->wbuf)
goto out_mtd;
}
if (jffs2_do_mount_fs(c))
goto out_mtd;
D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n"));
root_i = iget(sb, 1);
if (is_bad_inode(root_i)) {
D1(printk(KERN_WARNING "get root inode failed\n"));
goto out_nodes;
}
D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n"));
sb->s_root = d_alloc_root(root_i);
if (!sb->s_root)
goto out_root_i;
#if LINUX_VERSION_CODE >= 0x20403
sb->s_maxbytes = 0xFFFFFFFF;
#endif
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = JFFS2_SUPER_MAGIC;
if (!(sb->s_flags & MS_RDONLY))
jffs2_start_garbage_collect_thread(c);
return 0;
out_root_i:
iput(root_i);
out_nodes:
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
kfree(c->blocks);
out_mtd:
return -EINVAL;
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,45 +31,47 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: gc.c,v 1.52.2.2 2002/02/23 14:25:36 dwmw2 Exp $
* $Id: gc.c,v 1.68 2002/03/08 15:11:24 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/slab.h>
#include <linux/jffs2.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include "nodelist.h"
static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct inode *inode, struct jffs2_full_dnode *fd);
struct jffs2_inode_info *f, struct jffs2_full_dnode *fd);
static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct inode *inode, struct jffs2_full_dirent *fd);
struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct inode *inode, struct jffs2_full_dirent *fd);
struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct inode *indeo, struct jffs2_full_dnode *fn,
__u32 start, __u32 end);
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end);
static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct inode *inode, struct jffs2_full_dnode *fn,
__u32 start, __u32 end);
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end);
/* Called with erase_completion_lock held */
static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
{
struct jffs2_eraseblock *ret;
struct list_head *nextlist = NULL;
int n = jiffies % 128;
/* Pick an eraseblock to garbage collect next. This is where we'll
put the clever wear-levelling algorithms. Eventually. */
if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) {
D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
nextlist = &c->bad_used_list;
} else if (jiffies % 100 && !list_empty(&c->dirty_list)) {
} else if (n < 100 && !list_empty(&c->erasable_list)) {
D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n"));
nextlist = &c->erasable_list;
} else if (n < 126 && !list_empty(&c->dirty_list)) {
/* Most of the time, pick one off the dirty list */
D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n"));
nextlist = &c->dirty_list;
......@@ -80,9 +82,13 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n"));
nextlist = &c->dirty_list;
} else if (!list_empty(&c->erasable_list)) {
D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and dirty_list were empty)\n"));
nextlist = &c->erasable_list;
} else {
/* Eep. Both were empty */
printk(KERN_NOTICE "jffs2: No clean _or_ dirty blocks to GC from! Where are they all?\n");
printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n");
return NULL;
}
......@@ -109,8 +115,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
struct jffs2_node_frag *frag;
struct jffs2_full_dnode *fn = NULL;
struct jffs2_full_dirent *fd;
__u32 start = 0, end = 0, nrfrags = 0;
__u32 inum;
uint32_t start = 0, end = 0, nrfrags = 0;
uint32_t inum;
struct inode *inode;
int ret = 0;
......@@ -154,6 +160,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", raw->flash_offset &~3));
if (!raw->next_in_ino) {
/* Inode-less node. Clean marker, snapshot or something like that */
/* FIXME: If it's something that needs to be copied, including something
we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */
spin_unlock_bh(&c->erase_completion_lock);
jffs2_mark_node_obsolete(c, raw);
goto eraseit_lock;
......@@ -177,6 +185,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
f = JFFS2_INODE_INFO(inode);
down(&f->sem);
/* Now we have the lock for this inode. Check that it's still the one at the head
of the list. */
......@@ -188,7 +197,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
/* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */
if (f->metadata && f->metadata->raw == raw) {
fn = f->metadata;
ret = jffs2_garbage_collect_metadata(c, jeb, inode, fn);
ret = jffs2_garbage_collect_metadata(c, jeb, f, fn);
goto upnout;
}
......@@ -206,10 +215,10 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
/* We found a datanode. Do the GC */
if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) {
/* It crosses a page boundary. Therefore, it must be a hole. */
ret = jffs2_garbage_collect_hole(c, jeb, inode, fn, start, end);
ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end);
} else {
/* It could still be a hole. But we GC the page this way anyway */
ret = jffs2_garbage_collect_dnode(c, jeb, inode, fn, start, end);
ret = jffs2_garbage_collect_dnode(c, jeb, f, fn, start, end);
}
goto upnout;
}
......@@ -221,11 +230,12 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
}
if (fd && fd->ino) {
ret = jffs2_garbage_collect_dirent(c, jeb, inode, fd);
ret = jffs2_garbage_collect_dirent(c, jeb, f, fd);
} else if (fd) {
ret = jffs2_garbage_collect_deletion_dirent(c, jeb, inode, fd);
ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd);
} else {
printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%lu\n", raw->flash_offset&~3, inode->i_ino);
printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n",
raw->flash_offset&~3, f->inocache->ino);
if (raw->flash_offset & 1) {
printk(KERN_WARNING "But it's obsolete so we don't mind too much\n");
} else {
......@@ -256,24 +266,25 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
}
static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct inode *inode, struct jffs2_full_dnode *fn)
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_full_dnode *new_fn;
struct jffs2_raw_inode ri;
unsigned short dev;
char *mdata = NULL, mdatalen = 0;
__u32 alloclen, phys_ofs;
uint32_t alloclen, phys_ofs;
int ret;
if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
S_ISCHR(JFFS2_F_I_MODE(f)) ) {
/* For these, we don't actually need to read the old node */
dev = (major(inode->i_rdev) << 8) |
minor(inode->i_rdev);
/* FIXME: for minor or major > 255. */
dev = ((JFFS2_F_I_RDEV_MAJ(f) << 8) |
JFFS2_F_I_RDEV_MIN(f));
mdata = (char *)&dev;
mdatalen = sizeof(dev);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
} else if (S_ISLNK(inode->i_mode)) {
} else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
mdatalen = fn->size;
mdata = kmalloc(fn->size, GFP_KERNEL);
if (!mdata) {
......@@ -303,15 +314,15 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
ri.totlen = sizeof(ri) + mdatalen;
ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
ri.ino = inode->i_ino;
ri.ino = f->inocache->ino;
ri.version = ++f->highest_version;
ri.mode = inode->i_mode;
ri.uid = inode->i_uid;
ri.gid = inode->i_gid;
ri.isize = inode->i_size;
ri.atime = inode->i_atime;
ri.ctime = inode->i_ctime;
ri.mtime = inode->i_mtime;
ri.mode = JFFS2_F_I_MODE(f);
ri.uid = JFFS2_F_I_UID(f);
ri.gid = JFFS2_F_I_GID(f);
ri.isize = JFFS2_F_I_SIZE(f);
ri.atime = JFFS2_F_I_ATIME(f);
ri.ctime = JFFS2_F_I_CTIME(f);
ri.mtime = JFFS2_F_I_MTIME(f);
ri.offset = 0;
ri.csize = mdatalen;
ri.dsize = mdatalen;
......@@ -319,7 +330,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
ri.data_crc = crc32(0, mdata, mdatalen);
new_fn = jffs2_write_dnode(inode, &ri, mdata, mdatalen, phys_ofs, NULL);
new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, NULL);
if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
......@@ -330,18 +341,17 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
jffs2_free_full_dnode(fn);
f->metadata = new_fn;
out:
if (S_ISLNK(inode->i_mode))
if (S_ISLNK(JFFS2_F_I_MODE(f)))
kfree(mdata);
return ret;
}
static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct inode *inode, struct jffs2_full_dirent *fd)
struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_full_dirent *new_fd;
struct jffs2_raw_dirent rd;
__u32 alloclen, phys_ofs;
uint32_t alloclen, phys_ofs;
int ret;
rd.magic = JFFS2_MAGIC_BITMASK;
......@@ -350,10 +360,10 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er
rd.totlen = sizeof(rd) + rd.nsize;
rd.hdr_crc = crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4);
rd.pino = inode->i_ino;
rd.pino = f->inocache->ino;
rd.version = ++f->highest_version;
rd.ino = fd->ino;
rd.mctime = max(inode->i_mtime, inode->i_ctime);
rd.mctime = max(JFFS2_F_I_MTIME(f), JFFS2_F_I_CTIME(f));
rd.type = fd->type;
rd.node_crc = crc32(0, &rd, sizeof(rd)-8);
rd.name_crc = crc32(0, fd->name, rd.nsize);
......@@ -364,7 +374,7 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er
sizeof(rd)+rd.nsize, ret);
return ret;
}
new_fd = jffs2_write_dirent(inode, &rd, fd->name, rd.nsize, phys_ofs, NULL);
new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, NULL);
if (IS_ERR(new_fd)) {
printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
......@@ -375,19 +385,119 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er
}
static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct inode *inode, struct jffs2_full_dirent *fd)
struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_full_dirent **fdp = &f->dents;
int found = 0;
/* FIXME: When we run on NAND flash, we need to work out whether
this deletion dirent is still needed to actively delete a
'real' dirent with the same name that's still somewhere else
on the flash. For now, we know that we've actually obliterated
all the older dirents when they became obsolete, so we didn't
really need to write the deletion to flash in the first place.
*/
/* On a medium where we can't actually mark nodes obsolete
pernamently, such as NAND flash, we need to work out
whether this deletion dirent is still needed to actively
delete a 'real' dirent with the same name that's still
somewhere else on the flash. */
if (!jffs2_can_mark_obsolete(c)) {
struct jffs2_raw_dirent rd;
struct jffs2_raw_node_ref *raw;
int ret;
size_t retlen;
int name_len = strlen(fd->name);
uint32_t name_crc = crc32(0, fd->name, name_len);
char *namebuf = NULL;
/* Prevent the erase code from nicking the obsolete node refs while
we're looking at them. I really don't like this extra lock but
can't see any alternative. Suggestions on a postcard to... */
down(&c->erase_free_sem);
for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
/* We only care about obsolete ones */
if (!(raw->flash_offset & 1))
continue;
/* Doesn't matter if there's one in the same erase block. We're going to
delete it too at the same time. */
if ((raw->flash_offset & ~(c->sector_size-1)) ==
(fd->raw->flash_offset & ~(c->sector_size-1)))
continue;
/* This is an obsolete node belonging to the same directory */
ret = jffs2_flash_read(c, raw->flash_offset & ~3, sizeof(struct jffs2_unknown_node), &retlen, (char *)&rd);
if (ret) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading header from obsolete node at %08x\n", ret, raw->flash_offset & ~3);
/* If we can't read it, we don't need to continune to obsolete it. Continue */
continue;
}
if (retlen != sizeof(struct jffs2_unknown_node)) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading header from obsolete node at %08x\n",
retlen, sizeof(struct jffs2_unknown_node), raw->flash_offset & ~3);
continue;
}
if (rd.nodetype != JFFS2_NODETYPE_DIRENT ||
PAD(rd.totlen) != PAD(sizeof(rd) + name_len))
continue;
/* OK, it's a dirent node, it's the right length. We have to take a
closer look at it... */
ret = jffs2_flash_read(c, raw->flash_offset & ~3, sizeof(rd), &retlen, (char *)&rd);
if (ret) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading from obsolete node at %08x\n", ret, raw->flash_offset & ~3);
/* If we can't read it, we don't need to continune to obsolete it. Continue */
continue;
}
if (retlen != sizeof(struct jffs2_unknown_node)) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading from obsolete node at %08x\n",
retlen, sizeof(struct jffs2_unknown_node), raw->flash_offset & ~3);
continue;
}
/* If the name CRC doesn't match, skip */
if (rd.name_crc != name_crc)
continue;
/* If the name length doesn't match, or it's another deletion dirent, skip */
if (rd.nsize != name_len || !rd.ino)
continue;
/* OK, check the actual name now */
if (!namebuf) {
namebuf = kmalloc(name_len + 1, GFP_KERNEL);
if (!namebuf) {
up(&c->erase_free_sem);
return -ENOMEM;
}
}
/* We read the extra byte before it so it's a word-aligned read */
ret = jffs2_flash_read(c, (raw->flash_offset & ~3)+sizeof(rd)-1, name_len+1, &retlen, namebuf);
if (ret) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading name from obsolete node at %08x\n", ret, raw->flash_offset & ~3);
/* If we can't read it, we don't need to continune to obsolete it. Continue */
continue;
}
if (retlen != sizeof(rd)) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading name from obsolete node at %08x\n",
retlen, name_len, raw->flash_offset & ~3);
continue;
}
if (memcmp(namebuf+1, fd->name, name_len))
continue;
/* OK. The name really does match. There really is still an older node on
the flash which our deletion dirent obsoletes. So we have to write out
a new deletion dirent to replace it */
if (namebuf)
kfree(namebuf);
up(&c->erase_free_sem);
return jffs2_garbage_collect_dirent(c, jeb, f, fd);
}
up(&c->erase_free_sem);
if (namebuf)
kfree(namebuf);
}
/* No need for it any more. Just mark it obsolete and remove it from the list */
while (*fdp) {
if ((*fdp) == fd) {
found = 1;
......@@ -397,7 +507,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
fdp = &(*fdp)->next;
}
if (!found) {
printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%lu\n", fd->name, inode->i_ino);
printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino);
}
jffs2_mark_node_obsolete(c, fd->raw);
jffs2_free_full_dirent(fd);
......@@ -405,27 +515,26 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
}
static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct inode *inode, struct jffs2_full_dnode *fn,
__u32 start, __u32 end)
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_raw_inode ri;
struct jffs2_node_frag *frag;
struct jffs2_full_dnode *new_fn;
__u32 alloclen, phys_ofs;
uint32_t alloclen, phys_ofs;
int ret;
D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%lu from offset 0x%x to 0x%x\n",
inode->i_ino, start, end));
D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
f->inocache->ino, start, end));
memset(&ri, 0, sizeof(ri));
if(fn->frags > 1) {
size_t readlen;
__u32 crc;
uint32_t crc;
/* It's partially obsoleted by a later write. So we have to
write it out again with the _same_ version as before */
ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(ri), &readlen, (char *)&ri);
ret = jffs2_flash_read(c, fn->raw->flash_offset & ~3, sizeof(ri), &readlen, (char *)&ri);
if (readlen != sizeof(ri) || ret) {
printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %d. Data will be lost by writing new hold node\n", ret, readlen);
goto fill;
......@@ -445,14 +554,14 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
fn->raw->flash_offset & ~3, ri.node_crc, crc);
/* FIXME: We could possibly deal with this by writing new holes for each frag */
printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n",
start, end, inode->i_ino);
printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
start, end, f->inocache->ino);
goto fill;
}
if (ri.compr != JFFS2_COMPR_ZERO) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", fn->raw->flash_offset & ~3);
printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n",
start, end, inode->i_ino);
printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
start, end, f->inocache->ino);
goto fill;
}
} else {
......@@ -462,20 +571,20 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
ri.totlen = sizeof(ri);
ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
ri.ino = inode->i_ino;
ri.ino = f->inocache->ino;
ri.version = ++f->highest_version;
ri.offset = start;
ri.dsize = end - start;
ri.csize = 0;
ri.compr = JFFS2_COMPR_ZERO;
}
ri.mode = inode->i_mode;
ri.uid = inode->i_uid;
ri.gid = inode->i_gid;
ri.isize = inode->i_size;
ri.atime = inode->i_atime;
ri.ctime = inode->i_ctime;
ri.mtime = inode->i_mtime;
ri.mode = JFFS2_F_I_MODE(f);
ri.uid = JFFS2_F_I_UID(f);
ri.gid = JFFS2_F_I_GID(f);
ri.isize = JFFS2_F_I_SIZE(f);
ri.atime = JFFS2_F_I_ATIME(f);
ri.ctime = JFFS2_F_I_CTIME(f);
ri.mtime = JFFS2_F_I_MTIME(f);
ri.data_crc = 0;
ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
......@@ -485,7 +594,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
sizeof(ri), ret);
return ret;
}
new_fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, NULL);
if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
......@@ -525,23 +634,22 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
}
static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct inode *inode, struct jffs2_full_dnode *fn,
__u32 start, __u32 end)
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_full_dnode *new_fn;
struct jffs2_raw_inode ri;
__u32 alloclen, phys_ofs, offset, orig_end;
uint32_t alloclen, phys_ofs, offset, orig_end;
int ret = 0;
unsigned char *comprbuf = NULL, *writebuf;
struct page *pg;
unsigned char *pg_ptr;
/* FIXME: */ struct inode *inode = OFNI_EDONI_2SFFJ(f);
memset(&ri, 0, sizeof(ri));
D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%lu from offset 0x%x to 0x%x\n",
inode->i_ino, start, end));
D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
f->inocache->ino, start, end));
orig_end = end;
......@@ -561,7 +669,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
/* Shitloads of space */
/* FIXME: Integrate this properly with GC calculations */
start &= ~(PAGE_CACHE_SIZE-1);
end = min_t(__u32, start + PAGE_CACHE_SIZE, inode->i_size);
end = min_t(uint32_t, start + PAGE_CACHE_SIZE, JFFS2_F_I_SIZE(f));
D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n",
start, end));
if (end < orig_end) {
......@@ -588,8 +696,8 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
offset = start;
while(offset < orig_end) {
__u32 datalen;
__u32 cdatalen;
uint32_t datalen;
uint32_t cdatalen;
char comprtype = JFFS2_COMPR_NONE;
ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
......@@ -617,15 +725,15 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
ri.totlen = sizeof(ri) + cdatalen;
ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
ri.ino = inode->i_ino;
ri.ino = f->inocache->ino;
ri.version = ++f->highest_version;
ri.mode = inode->i_mode;
ri.uid = inode->i_uid;
ri.gid = inode->i_gid;
ri.isize = inode->i_size;
ri.atime = inode->i_atime;
ri.ctime = inode->i_ctime;
ri.mtime = inode->i_mtime;
ri.mode = JFFS2_F_I_MODE(f);
ri.uid = JFFS2_F_I_UID(f);
ri.gid = JFFS2_F_I_GID(f);
ri.isize = JFFS2_F_I_SIZE(f);
ri.atime = JFFS2_F_I_ATIME(f);
ri.ctime = JFFS2_F_I_CTIME(f);
ri.mtime = JFFS2_F_I_MTIME(f);
ri.offset = offset;
ri.csize = cdatalen;
ri.dsize = datalen;
......@@ -633,7 +741,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
ri.data_crc = crc32(0, writebuf, cdatalen);
new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL);
new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, NULL);
if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,7 +31,7 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: malloc.c,v 1.16 2001/03/15 15:38:24 dwmw2 Exp $
* $Id: malloc.c,v 1.21 2002/03/12 17:36:55 dwmw2 Exp $
*
*/
......@@ -49,7 +49,6 @@
/* These are initialised to NULL in the kernel startup code.
If you're porting to other operating systems, beware */
static kmem_cache_t *jffs2_inode_cachep;
static kmem_cache_t *full_dnode_slab;
static kmem_cache_t *raw_dirent_slab;
static kmem_cache_t *raw_inode_slab;
......@@ -58,89 +57,47 @@ static kmem_cache_t *raw_node_ref_slab;
static kmem_cache_t *node_frag_slab;
static kmem_cache_t *inode_cache_slab;
void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
{
struct jffs2_tmp_dnode_info *next;
while (tn) {
next = tn;
tn = tn->next;
jffs2_free_full_dnode(next->fn);
jffs2_free_tmp_dnode_info(next);
}
}
void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
{
struct jffs2_full_dirent *next;
while (fd) {
next = fd->next;
jffs2_free_full_dirent(fd);
fd = next;
}
}
struct inode *jffs2_alloc_inode(struct super_block *sb)
{
struct jffs2_inode_info *ei;
ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL);
if (!ei)
return NULL;
return &ei->vfs_inode;
}
void jffs2_destroy_inode(struct inode *inode)
{
kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
}
static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
{
struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo;
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR) {
init_MUTEX(&ei->sem);
inode_init_once(&ei->vfs_inode);
}
}
int __init jffs2_create_slab_caches(void)
{
full_dnode_slab = kmem_cache_create("jffs2_full_dnode", sizeof(struct jffs2_full_dnode), 0, JFFS2_SLAB_POISON, NULL, NULL);
full_dnode_slab = kmem_cache_create("jffs2_full_dnode",
sizeof(struct jffs2_full_dnode),
0, JFFS2_SLAB_POISON, NULL, NULL);
if (!full_dnode_slab)
goto err;
raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", sizeof(struct jffs2_raw_dirent), 0, JFFS2_SLAB_POISON, NULL, NULL);
raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent",
sizeof(struct jffs2_raw_dirent),
0, JFFS2_SLAB_POISON, NULL, NULL);
if (!raw_dirent_slab)
goto err;
raw_inode_slab = kmem_cache_create("jffs2_raw_inode", sizeof(struct jffs2_raw_inode), 0, JFFS2_SLAB_POISON, NULL, NULL);
raw_inode_slab = kmem_cache_create("jffs2_raw_inode",
sizeof(struct jffs2_raw_inode),
0, JFFS2_SLAB_POISON, NULL, NULL);
if (!raw_inode_slab)
goto err;
tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", sizeof(struct jffs2_tmp_dnode_info), 0, JFFS2_SLAB_POISON, NULL, NULL);
tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode",
sizeof(struct jffs2_tmp_dnode_info),
0, JFFS2_SLAB_POISON, NULL, NULL);
if (!tmp_dnode_info_slab)
goto err;
raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", sizeof(struct jffs2_raw_node_ref), 0, JFFS2_SLAB_POISON, NULL, NULL);
raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref",
sizeof(struct jffs2_raw_node_ref),
0, JFFS2_SLAB_POISON, NULL, NULL);
if (!raw_node_ref_slab)
goto err;
node_frag_slab = kmem_cache_create("jffs2_node_frag", sizeof(struct jffs2_node_frag), 0, JFFS2_SLAB_POISON, NULL, NULL);
node_frag_slab = kmem_cache_create("jffs2_node_frag",
sizeof(struct jffs2_node_frag),
0, JFFS2_SLAB_POISON, NULL, NULL);
if (!node_frag_slab)
goto err;
jffs2_inode_cachep = kmem_cache_create("jffs2_inode_cache",
sizeof(struct jffs2_inode_info),
0, SLAB_HWCACHE_ALIGN,
init_once, NULL);
if (!jffs2_inode_cachep)
goto err;
inode_cache_slab = kmem_cache_create("jffs2_inode", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL);
inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
sizeof(struct jffs2_inode_cache),
0, JFFS2_SLAB_POISON, NULL, NULL);
if (inode_cache_slab)
return 0;
err:
......@@ -164,9 +121,6 @@ void jffs2_destroy_slab_caches(void)
kmem_cache_destroy(node_frag_slab);
if(inode_cache_slab)
kmem_cache_destroy(inode_cache_slab);
if(jffs2_inode_cachep)
kmem_cache_destroy(jffs2_inode_cachep);
}
struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
......
......@@ -31,14 +31,14 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: nodelist.c,v 1.30.2.3 2002/02/23 14:04:44 dwmw2 Exp $
* $Id: nodelist.c,v 1.42 2002/03/11 11:17:29 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/jffs2.h>
#include <linux/fs.h>
#include <linux/mtd/mtd.h>
#include <linux/interrupt.h>
#include "nodelist.h"
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
......@@ -89,13 +89,37 @@ void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnod
*prev = tn;
}
static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
{
struct jffs2_tmp_dnode_info *next;
while (tn) {
next = tn;
tn = tn->next;
jffs2_free_full_dnode(next->fn);
jffs2_free_tmp_dnode_info(next);
}
}
static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
{
struct jffs2_full_dirent *next;
while (fd) {
next = fd->next;
jffs2_free_full_dirent(fd);
fd = next;
}
}
/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
with this ino, returning the former in order of version */
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
__u32 *highest_version, __u32 *latest_mctime,
__u32 *mctime_ver)
uint32_t *highest_version, uint32_t *latest_mctime,
uint32_t *mctime_ver)
{
struct jffs2_raw_node_ref *ref = f->inocache->nodes;
struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
......@@ -111,6 +135,9 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
if (!f->inocache->nodes) {
printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino);
}
spin_lock_bh(&c->erase_completion_lock);
for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) {
/* Work out whether it's a data node or a dirent node */
if (ref->flash_offset & 1) {
......@@ -118,7 +145,12 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref->flash_offset &~3));
continue;
}
err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
/* We can hold a pointer to a non-obsolete node without the spinlock,
but _obsolete_ nodes may disappear at any time, if the block
they're in gets erased */
spin_unlock_bh(&c->erase_completion_lock);
err = jffs2_flash_read(c, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
if (err) {
printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, (ref->flash_offset) & ~3);
goto free_out;
......@@ -143,8 +175,10 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
if (node.d.version > *highest_version)
*highest_version = node.d.version;
if (ref->flash_offset & 1) {
/* Obsoleted */
continue;
/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n",
ref->flash_offset & ~3);
BUG();
}
fd = jffs2_alloc_full_dirent(node.d.nsize+1);
if (!fd) {
......@@ -167,7 +201,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
dirent we've already read from the flash
*/
if (retlen > sizeof(struct jffs2_raw_dirent))
memcpy(&fd->name[0], &node.d.name[0], min((__u32)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
memcpy(&fd->name[0], &node.d.name[0], min((uint32_t)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
/* Do we need to copy any more of the name directly
from the flash?
......@@ -175,7 +209,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) {
int already = retlen - sizeof(struct jffs2_raw_dirent);
err = c->mtd->read(c->mtd, (ref->flash_offset & ~3) + retlen,
err = jffs2_flash_read(c, (ref->flash_offset & ~3) + retlen,
node.d.nsize - already, &retlen, &fd->name[already]);
if (!err && retlen != node.d.nsize - already)
err = -EIO;
......@@ -207,9 +241,10 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", node.d.version, *highest_version));
if (ref->flash_offset & 1) {
D1(printk(KERN_DEBUG "obsoleted\n"));
/* Obsoleted */
continue;
/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n",
ref->flash_offset & ~3);
BUG();
}
tn = jffs2_alloc_tmp_dnode_info();
if (!tn) {
......@@ -254,7 +289,10 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
break;
}
}
spin_lock_bh(&c->erase_completion_lock);
}
spin_unlock_bh(&c->erase_completion_lock);
*tnp = ret_tn;
*fdp = ret_fd;
......@@ -272,15 +310,19 @@ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino)
D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
spin_lock (&c->inocache_lock);
if (c->inocache_last && c->inocache_last->ino == ino) {
ret = c->inocache_last;
} else {
ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
while (ret && ret->ino < ino) {
ret = ret->next;
}
spin_unlock(&c->inocache_lock);
if (ret && ret->ino != ino)
ret = NULL;
}
spin_unlock(&c->inocache_lock);
D2(printk(KERN_DEBUG "jffs2_get_ino_cache found %p for ino %u\n", ret, ino));
return ret;
......@@ -299,6 +341,9 @@ void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new
}
new->next = *prev;
*prev = new;
c->inocache_last = new;
spin_unlock(&c->inocache_lock);
}
......@@ -316,6 +361,9 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
if ((*prev) == old) {
*prev = old->next;
}
if (c->inocache_last == old)
c->inocache_last = NULL;
spin_unlock(&c->inocache_lock);
}
......@@ -334,6 +382,7 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c)
}
c->inocache_list[i] = NULL;
}
c->inocache_last = NULL;
}
void jffs2_free_raw_node_refs(struct jffs2_sb_info *c)
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,16 +31,21 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: nodelist.h,v 1.46.2.1 2002/02/23 14:04:44 dwmw2 Exp $
* + zlib_init calls from v1.65
* $Id: nodelist.h,v 1.68 2002/03/08 11:27:19 dwmw2 Exp $
*
*/
#ifndef __JFFS2_NODELIST_H__
#define __JFFS2_NODELIST_H__
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
#include <linux/jffs2.h>
#include <linux/jffs2_fs_sb.h>
#include <linux/jffs2_fs_i.h>
#include "os-linux.h"
#ifndef CONFIG_JFFS2_FS_DEBUG
#define CONFIG_JFFS2_FS_DEBUG 2
......@@ -72,10 +77,10 @@ struct jffs2_raw_node_ref
for this inode instead. The inode_cache will have NULL in the first
word so you know when you've got there :) */
struct jffs2_raw_node_ref *next_phys;
// __u32 ino;
__u32 flash_offset;
__u32 totlen;
// __u16 nodetype;
// uint32_t ino;
uint32_t flash_offset;
uint32_t totlen;
// uint16_t nodetype;
/* flash_offset & 3 always has to be zero, because nodes are
always aligned at 4 bytes. So we have a couple of extra bits
......@@ -108,13 +113,16 @@ struct jffs2_inode_cache {
chain. */
struct jffs2_inode_cache *next;
struct jffs2_raw_node_ref *nodes;
__u32 ino;
uint32_t ino;
int nlink;
};
struct jffs2_scan_info {
struct jffs2_full_dirent *dents;
struct jffs2_tmp_dnode_info *tmpnodes;
/* Latest i_size info */
uint32_t version;
uint32_t isize;
};
/*
Larger representation of a raw node, kept in-core only when the
......@@ -124,9 +132,9 @@ struct jffs2_scan_info {
struct jffs2_full_dnode
{
struct jffs2_raw_node_ref *raw;
__u32 ofs; /* Don't really need this, but optimisation */
__u32 size;
__u32 frags; /* Number of fragments which currently refer
uint32_t ofs; /* Don't really need this, but optimisation */
uint32_t size;
uint32_t frags; /* Number of fragments which currently refer
to this node. When this reaches zero,
the node is obsolete.
*/
......@@ -141,15 +149,15 @@ struct jffs2_tmp_dnode_info
{
struct jffs2_tmp_dnode_info *next;
struct jffs2_full_dnode *fn;
__u32 version;
uint32_t version;
};
struct jffs2_full_dirent
{
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dirent *next;
__u32 version;
__u32 ino; /* == zero for unlink */
uint32_t version;
uint32_t ino; /* == zero for unlink */
unsigned int nhash;
unsigned char type;
unsigned char name[0];
......@@ -162,20 +170,20 @@ struct jffs2_node_frag
{
struct jffs2_node_frag *next;
struct jffs2_full_dnode *node; /* NULL for holes */
__u32 size;
__u32 ofs; /* Don't really need this, but optimisation */
__u32 node_ofs; /* offset within the physical node */
uint32_t size;
uint32_t ofs; /* Don't really need this, but optimisation */
uint32_t node_ofs; /* offset within the physical node */
};
struct jffs2_eraseblock
{
struct list_head list;
int bad_count;
__u32 offset; /* of this block in the MTD */
uint32_t offset; /* of this block in the MTD */
__u32 used_size;
__u32 dirty_size;
__u32 free_size; /* Note that sector_size - free_size
uint32_t used_size;
uint32_t dirty_size;
uint32_t free_size; /* Note that sector_size - free_size
is the address of the first free space */
struct jffs2_raw_node_ref *first_node;
struct jffs2_raw_node_ref *last_node;
......@@ -207,7 +215,7 @@ struct jffs2_eraseblock
} while(0)
#define ACCT_PARANOIA_CHECK(jeb) do { \
__u32 my_used_size = 0; \
uint32_t my_used_size = 0; \
struct jffs2_raw_node_ref *ref2 = jeb->first_node; \
while (ref2) { \
if (!(ref2->flash_offset & 1)) \
......@@ -249,8 +257,8 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new
void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list);
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
__u32 *highest_version, __u32 *latest_mctime,
__u32 *mctime_ver);
uint32_t *highest_version, uint32_t *latest_mctime,
uint32_t *mctime_ver);
struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int 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);
......@@ -258,28 +266,33 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c);
void jffs2_free_raw_node_refs(struct jffs2_sb_info *c);
/* nodemgmt.c */
int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio);
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len);
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty);
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio);
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, uint32_t len, int dirty);
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 */
struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri);
struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen);
struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen);
int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, uint32_t *writelen);
struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, uint32_t *writelen);
int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
struct jffs2_raw_inode *ri, unsigned char *buf,
uint32_t offset, uint32_t writelen, uint32_t *retlen);
int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen);
int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f);
int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen);
/* readinode.c */
void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size);
void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, uint32_t size);
int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn);
int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
void jffs2_read_inode (struct inode *);
void jffs2_clear_inode (struct inode *);
int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
uint32_t ino, struct jffs2_raw_inode *latest_node);
void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
/* malloc.c */
void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn);
void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd);
int jffs2_create_slab_caches(void);
void jffs2_destroy_slab_caches(void);
......@@ -303,47 +316,24 @@ void jffs2_free_inode_cache(struct jffs2_inode_cache *);
/* gc.c */
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
/* background.c */
int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
/* dir.c */
extern struct file_operations jffs2_dir_operations;
extern struct inode_operations jffs2_dir_inode_operations;
/* file.c */
extern struct file_operations jffs2_file_operations;
extern struct inode_operations jffs2_file_inode_operations;
extern struct address_space_operations jffs2_file_address_operations;
int jffs2_null_fsync(struct file *, struct dentry *, int);
int jffs2_setattr (struct dentry *dentry, struct iattr *iattr);
int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
int jffs2_readpage (struct file *, struct page *);
int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
/* ioctl.c */
int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
/* read.c */
int jffs2_read_dnode(struct jffs2_sb_info *c, 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);
/* compr.c */
unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
__u32 *datalen, __u32 *cdatalen);
uint32_t *datalen, uint32_t *cdatalen);
int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
unsigned char *data_out, __u32 cdatalen, __u32 datalen);
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
/* scan.c */
int jffs2_scan_medium(struct jffs2_sb_info *c);
/* build.c */
int jffs2_build_filesystem(struct jffs2_sb_info *c);
/* symlink.c */
extern struct inode_operations jffs2_symlink_inode_operations;
int jffs2_do_mount_fs(struct jffs2_sb_info *c);
/* erase.c */
void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
......@@ -351,6 +341,16 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c);
void jffs2_mark_erased_blocks(struct jffs2_sb_info *c);
void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
#ifdef CONFIG_JFFS2_FS_NAND
/* wbuf.c */
int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad);
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_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
#endif
/* compr_zlib.c */
int jffs2_zlib_init(void);
void jffs2_zlib_exit(void);
#endif /* __JFFS2_NODELIST_H__ */
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,13 +31,12 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: nodemgmt.c,v 1.45.2.1 2002/02/23 14:13:34 dwmw2 Exp $
* $Id: nodemgmt.c,v 1.63 2002/03/08 14:54:09 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/jffs2.h>
#include <linux/mtd/mtd.h>
#include <linux/interrupt.h>
#include "nodelist.h"
......@@ -62,9 +61,9 @@
* for the requested allocation.
*/
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len);
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio)
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio)
{
int ret = -EAGAIN;
int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
......@@ -121,7 +120,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u3
return ret;
}
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len)
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
{
int ret = -EAGAIN;
minsize = PAD(minsize);
......@@ -140,13 +139,21 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, _
}
/* Called with alloc sem _and_ erase_completion_lock */
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len)
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
{
struct jffs2_eraseblock *jeb = c->nextblock;
restart:
if (jeb && minsize > jeb->free_size) {
/* Skip the end of this block and file it as having some dirty space */
/* If there's a pending write to it, flush now */
if (c->wbuf_len) {
spin_unlock_bh(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
jffs2_flush_wbuf(c, 1);
spin_lock_bh(&c->erase_completion_lock);
/* We know nobody's going to have changed nextblock. Just continue */
}
c->dirty_size += jeb->free_size;
c->free_size -= jeb->free_size;
jeb->dirty_size += jeb->free_size;
......@@ -165,20 +172,44 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32
DECLARE_WAITQUEUE(wait, current);
if (!c->nr_erasing_blocks &&
!list_empty(&c->erasable_list)) {
struct jffs2_eraseblock *ejeb;
ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list);
list_del(&ejeb->list);
list_add_tail(&ejeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n",
ejeb->offset));
}
if (!c->nr_erasing_blocks &&
!list_empty(&c->erasable_pending_wbuf_list)) {
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
/* c->nextblock is NULL, no update to c->nextblock allowed */
spin_unlock_bh(&c->erase_completion_lock);
jffs2_flush_wbuf(c, 1);
spin_lock_bh(&c->erase_completion_lock);
/* Have another go. It'll be on the erasable_list now */
return -EAGAIN;
}
if (!c->nr_erasing_blocks) {
// if (list_empty(&c->erasing_list) && list_empty(&c->erase_pending_list) && list_empty(c->erase_complete_list)) {
/* 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. (erasingempty: %s, erasependingempty: %s)\n",
c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
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;
}
/* Make sure this can't deadlock. Someone has to start the erases
of erase_pending blocks */
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&c->erase_wait, &wait);
D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n",
c->nr_erasing_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
c->nr_erasing_blocks, list_empty(&c->erasable_list)?"yes":"no",
list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
if (!list_empty(&c->erase_pending_list)) {
D1(printk(KERN_DEBUG "Triggering pending erases\n"));
jffs2_erase_pending_trigger(c);
......@@ -200,7 +231,11 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32
list_del(next);
c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
c->nr_free_blocks--;
if (jeb->free_size != c->sector_size - sizeof(struct jffs2_unknown_node)) {
/* On NAND free_size == sector_size, cleanmarker is in spare area !*/
if (jeb->free_size != c->sector_size -
(jffs2_cleanmarker_oob(c)) ? 0 : sizeof(struct jffs2_unknown_node)) {
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;
}
......@@ -209,6 +244,20 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32
enough space */
*ofs = jeb->offset + (c->sector_size - jeb->free_size);
*len = jeb->free_size;
if (jeb->used_size == PAD(sizeof(struct jffs2_unknown_node)) &&
!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_bh(&c->erase_completion_lock);
jffs2_mark_node_obsolete(c, jeb->first_node);
spin_lock_bh(&c->erase_completion_lock);
}
D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
return 0;
}
......@@ -216,9 +265,9 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32
/**
* jffs2_add_physical_node_ref - add a physical node reference to the list
* @c: superblock info
* @ofs: physical location of this physical node
* @new: new node reference to add
* @len: length of this physical node
* @ino: inode number with which this physical node is associated
* @dirty: dirty flag for new node
*
* Should only be used to report nodes for which space has been allocated
* by jffs2_reserve_space.
......@@ -226,12 +275,12 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32
* Must be called with the alloc_sem held.
*/
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty)
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, uint32_t len, int dirty)
{
struct jffs2_eraseblock *jeb;
len = PAD(len);
jeb = &c->blocks[(new->flash_offset & ~3) / c->sector_size];
jeb = &c->blocks[new->flash_offset / c->sector_size];
D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x, size 0x%x\n", new->flash_offset & ~3, len));
#if 1
if (jeb != c->nextblock || (new->flash_offset & ~3) != jeb->offset + (c->sector_size - jeb->free_size)) {
......@@ -240,13 +289,14 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
return -EINVAL;
}
#endif
spin_lock_bh(&c->erase_completion_lock);
if (!jeb->first_node)
jeb->first_node = new;
if (jeb->last_node)
jeb->last_node->next_phys = new;
jeb->last_node = new;
spin_lock_bh(&c->erase_completion_lock);
jeb->free_size -= len;
c->free_size -= len;
if (dirty) {
......@@ -257,17 +307,26 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
jeb->used_size += len;
c->used_size += len;
}
spin_unlock_bh(&c->erase_completion_lock);
if (!jeb->free_size && !jeb->dirty_size) {
/* If it lives on the dirty_list, jffs2_reserve_space will put it there */
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
if (c->wbuf_len) {
/* Flush the last write in the block if it's outstanding */
spin_unlock_bh(&c->erase_completion_lock);
jffs2_flush_wbuf(c, 1);
spin_lock_bh(&c->erase_completion_lock);
}
list_add_tail(&jeb->list, &c->clean_list);
c->nextblock = NULL;
}
ACCT_SANITY_CHECK(c,jeb);
ACCT_PARANOIA_CHECK(jeb);
spin_unlock_bh(&c->erase_completion_lock);
return 0;
}
......@@ -285,7 +344,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
int blocknr;
struct jffs2_unknown_node n;
int ret;
ssize_t retlen;
size_t retlen;
if(!ref) {
printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
......@@ -327,41 +386,62 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
spin_unlock_bh(&c->erase_completion_lock);
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 == c->gcblock) {
D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
#if 0 /* We no longer do this here. It can screw the wear levelling. If you have a lot of static
data and a few blocks free, and you just create new files and keep deleting/overwriting
them, then you'd keep erasing and reusing those blocks without ever moving stuff around.
So we leave completely obsoleted blocks on the dirty_list and let the GC delete them
when it finds them there. That way, we still get the 'once in a while, take a clean block'
to spread out the flash usage */
} else if (!jeb->used_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);
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);
// OFNI_BS_2SFFJ(c)->s_dirt = 1;
}
if (c->wbuf_len) {
D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
/* We've changed the rules slightly. After
writing a node you now mustn't drop the
alloc_sem before you've finished all the
list management - this is so that when we
get here, we know that no other nodes have
been written, and the above check on wbuf
is valid - wbuf_len is nonzero IFF the node
which obsoletes this node is still in the
wbuf.
So we BUG() if that new rule is broken, to
make sure we catch it and fix it.
*/
if (!down_trylock(&c->alloc_sem)) {
up(&c->alloc_sem);
printk(KERN_CRIT "jffs2_mark_node_obsolete() called with wbuf active but alloc_sem not locked!\n");
BUG();
}
} else {
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"));
#endif
} else if (jeb == c->gcblock) {
D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset));
} else if (jeb->dirty_size == ref->totlen) {
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);
}
spin_unlock_bh(&c->erase_completion_lock);
if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM)
if (!jffs2_can_mark_obsolete(c))
return;
if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
if (jffs2_is_readonly(c))
return;
D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref->flash_offset &~3));
ret = c->mtd->read(c->mtd, ref->flash_offset &~3, sizeof(n), &retlen, (char *)&n);
ret = jffs2_flash_read(c, ref->flash_offset &~3, sizeof(n), &retlen, (char *)&n);
if (ret) {
printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
return;
......@@ -379,7 +459,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
return;
}
n.nodetype &= ~JFFS2_NODE_ACCURATE;
ret = c->mtd->write(c->mtd, ref->flash_offset&~3, sizeof(n), &retlen, (char *)&n);
ret = jffs2_flash_write(c, ref->flash_offset&~3, sizeof(n), &retlen, (char *)&n);
if (ret) {
printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
return;
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* The original JFFS, from which the design for JFFS2 was derived,
* was designed and implemented by Axis Communications AB.
*
* The contents of this file are subject to the Red Hat eCos Public
* License Version 1.1 (the "Licence"); you may not use this file
* except in compliance with the Licence. You may obtain a copy of
* the Licence at http://www.redhat.com/
*
* Software distributed under the Licence is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the Licence for the specific language governing rights and
* limitations under the Licence.
*
* The Original Code is JFFS2 - Journalling Flash File System, version 2
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in
* which case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the RHEPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: os-linux.h,v 1.15 2002/03/08 11:31:48 dwmw2 Exp $
*
*/
#ifndef __JFFS2_OS_LINUX_H__
#define __JFFS2_OS_LINUX_H__
#include <linux/version.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode)
#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
#elif defined(JFFS2_OUT_OF_KERNEL)
#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
#else
#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
#endif
#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size)
#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime)
#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime)
#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1)
#define JFFS2_F_I_RDEV_MIN(f) (minor(OFNI_EDONI_2SFFJ(f)->i_rdev))
#define JFFS2_F_I_RDEV_MAJ(f) (major(OFNI_EDONI_2SFFJ(f)->i_rdev))
#else
#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
#endif
static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
{
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
f->highest_version = 0;
f->fraglist = NULL;
f->metadata = NULL;
f->dents = NULL;
f->flags = 0;
f->usercompr = 0;
#else
memset(f, 0, sizeof(*f));
init_MUTEX_LOCKED(&f->sem);
#endif
}
#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
#ifndef CONFIG_JFFS2_FS_NAND
#define jffs2_can_mark_obsolete(c) (1)
#define jffs2_cleanmarker_oob(c) (0)
#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf))
#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf))
#define jffs2_flush_wbuf(c, flag) do { ; } while(0)
#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0)
#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0)
#define jffs2_flash_writev jffs2_flash_direct_writev
#else /* NAND support present */
#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM)
#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf))
/* wbuf.c */
int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
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);
#endif /* NAND */
/* background.c */
int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
/* dir.c */
extern struct file_operations jffs2_dir_operations;
extern struct inode_operations jffs2_dir_inode_operations;
/* file.c */
extern struct file_operations jffs2_file_operations;
extern struct inode_operations jffs2_file_inode_operations;
extern struct address_space_operations jffs2_file_address_operations;
int jffs2_fsync(struct file *, struct dentry *, int);
int jffs2_setattr (struct dentry *dentry, struct iattr *iattr);
int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
int jffs2_readpage (struct file *, struct page *);
int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
/* ioctl.c */
int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
/* symlink.c */
extern struct inode_operations jffs2_symlink_inode_operations;
/* fs.c */
void jffs2_read_inode (struct inode *);
void jffs2_clear_inode (struct inode *);
struct inode *jffs2_new_inode (struct inode *dir_i, int mode,
struct jffs2_raw_inode *ri);
int jffs2_statfs (struct super_block *, struct statfs *);
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);
/* writev.c */
int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
unsigned long count, loff_t to, size_t *retlen);
/* super.c */
#endif /* __JFFS2_OS_LINUX_H__ */
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,12 +31,15 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: pushpull.h,v 1.5 2001/09/23 10:04:15 rmk Exp $
* $Id: pushpull.h,v 1.7 2002/03/06 12:37:08 dwmw2 Exp $
*
*/
#ifndef __PUSHPULL_H__
#define __PUSHPULL_H__
#include <linux/errno.h>
struct pushpull {
unsigned char *buf;
unsigned int buflen;
......@@ -44,9 +47,36 @@ struct pushpull {
unsigned int reserve;
};
void init_pushpull(struct pushpull *, char *, unsigned, unsigned, unsigned);
int pushbit(struct pushpull *pp, int bit, int use_reserved);
int pushedbits(struct pushpull *pp);
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)
{
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,14 +31,14 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: read.c,v 1.13.2.1 2002/02/01 23:32:33 dwmw2 Exp $
* $Id: read.c,v 1.22 2002/03/02 22:08:27 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/crc32.h>
#include <linux/jffs2.h>
#include <linux/pagemap.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
......@@ -46,7 +46,7 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsig
{
struct jffs2_raw_inode *ri;
size_t readlen;
__u32 crc;
uint32_t crc;
unsigned char *decomprbuf = NULL;
unsigned char *readbuf = NULL;
int ret = 0;
......@@ -55,7 +55,7 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsig
if (!ri)
return -ENOMEM;
ret = c->mtd->read(c->mtd, fd->raw->flash_offset & ~3, sizeof(*ri), &readlen, (char *)ri);
ret = jffs2_flash_read(c, fd->raw->flash_offset & ~3, sizeof(*ri), &readlen, (char *)ri);
if (ret) {
jffs2_free_raw_inode(ri);
printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", fd->raw->flash_offset & ~3, ret);
......@@ -124,7 +124,7 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsig
}
D2(printk(KERN_DEBUG "Read %d bytes to %p\n", ri->csize, readbuf));
ret = c->mtd->read(c->mtd, (fd->raw->flash_offset &~3) + sizeof(*ri), ri->csize, &readlen, readbuf);
ret = jffs2_flash_read(c, (fd->raw->flash_offset &~3) + sizeof(*ri), ri->csize, &readlen, readbuf);
if (!ret && readlen != ri->csize)
ret = -EIO;
......@@ -161,3 +161,99 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsig
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 = f->fraglist;
int ret;
D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n",
f->inocache->ino, offset, offset+len));
while(frag && frag->ofs + frag->size <= offset) {
D2(printk(KERN_DEBUG "skipping frag %d-%d; before the region we care about\n", frag->ofs, frag->ofs + frag->size));
frag = frag->next;
}
/* 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 (!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(jffs2_print_frag_list(f));
}
D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
memset(buf, 0, holesize);
buf += holesize;
offset += holesize;
continue;
} else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) {
D1(printk(KERN_NOTICE "Eep. Overlap in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
f->inocache->ino, frag->ofs, offset));
D1(jffs2_print_frag_list(f));
memset(buf, 0, end - offset);
return -EIO;
} else if (!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;
continue;
} else {
uint32_t readlen;
readlen = min(frag->size, end - offset);
D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs, frag->ofs+readlen, frag->node->raw->flash_offset & ~3));
ret = jffs2_read_dnode(c, frag->node, buf, 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, frag->size);
return ret;
}
}
buf += frag->size;
offset += frag->size;
frag = frag->next;
D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
}
return 0;
}
/* Core function to read symlink target. */
char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
{
char *buf;
int ret;
down(&f->sem);
if (!f->metadata) {
printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino);
up(&f->sem);
return ERR_PTR(-EINVAL);
}
buf = kmalloc(f->metadata->size+1, GFP_USER);
if (!buf) {
up(&f->sem);
return ERR_PTR(-ENOMEM);
}
buf[f->metadata->size]=0;
ret = jffs2_read_dnode(c, f->metadata, buf, 0, f->metadata->size);
up(&f->sem);
if (ret) {
kfree(buf);
return ERR_PTR(ret);
}
return buf;
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,19 +31,16 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: readinode.c,v 1.58.2.2 2002/02/23 14:25:37 dwmw2 Exp $
* $Id: readinode.c,v 1.71 2002/03/06 12:25:59 dwmw2 Exp $
*
*/
/* Given an inode, probably with existing list of fragments, add the new node
* to the fragment list.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/crc32.h>
#include <linux/mtd/mtd.h>
#include <linux/jffs2.h>
#include <linux/interrupt.h>
#include "nodelist.h"
......@@ -64,6 +61,9 @@ D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)
})
/* Given an inode, probably with existing list of fragments, add the new node
* to the fragment list.
*/
int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
{
int ret;
......@@ -101,7 +101,7 @@ int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_
struct jffs2_node_frag *this, **prev, *old;
struct jffs2_node_frag *newfrag, *newfrag2;
__u32 lastend = 0;
uint32_t lastend = 0;
newfrag = jffs2_alloc_node_frag();
......@@ -222,7 +222,7 @@ int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_
return 0;
}
void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size)
void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, uint32_t size)
{
D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
......@@ -243,68 +243,53 @@ void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **
/* Scan the list of all nodes present for this ino, build map of versions, etc. */
void jffs2_read_inode (struct inode *inode)
int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
uint32_t ino, struct jffs2_raw_inode *latest_node)
{
struct jffs2_tmp_dnode_info *tn_list, *tn;
struct jffs2_full_dirent *fd_list;
struct jffs2_inode_info *f;
struct jffs2_full_dnode *fn = NULL;
struct jffs2_sb_info *c;
struct jffs2_raw_inode latest_node;
__u32 latest_mctime, mctime_ver;
uint32_t crc;
uint32_t latest_mctime, mctime_ver;
uint32_t mdata_ver = 0;
size_t retlen;
int ret;
ssize_t retlen;
D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
f = JFFS2_INODE_INFO(inode);
c = JFFS2_SB_INFO(inode->i_sb);
f->inocache = jffs2_get_ino_cache(c, ino);
f->highest_version = 0;
f->fraglist = NULL;
f->metadata = NULL;
f->dents = NULL;
f->flags = 0;
f->usercompr = 0;
D2(printk(KERN_DEBUG "getting inocache\n"));
f->inocache = jffs2_get_ino_cache(c, inode->i_ino);
D2(printk(KERN_DEBUG "jffs2_read_inode(): Got inocache at %p\n", f->inocache));
if (!f->inocache && inode->i_ino == 1) {
D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
if (!f->inocache && ino == 1) {
/* Special case - no root inode on medium */
f->inocache = jffs2_alloc_inode_cache();
if (!f->inocache) {
printk(KERN_CRIT "jffs2_read_inode(): Cannot allocate inocache for root inode\n");
make_bad_inode(inode);
return;
printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n");
return -ENOMEM;
}
D1(printk(KERN_DEBUG "jffs2_read_inode(): Creating inocache for root inode\n"));
D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n"));
memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
f->inocache->ino = f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
jffs2_add_ino_cache(c, f->inocache);
}
if (!f->inocache) {
printk(KERN_WARNING "jffs2_read_inode() on nonexistent ino %lu\n", (unsigned long)inode->i_ino);
make_bad_inode(inode);
return;
printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
return -ENOENT;
}
D1(printk(KERN_DEBUG "jffs2_read_inode(): ino #%lu nlink is %d\n", (unsigned long)inode->i_ino, f->inocache->nlink));
inode->i_nlink = f->inocache->nlink;
D1(printk(KERN_DEBUG "jffs2_do_read_inode(): ino #%u nlink is %d\n", ino, f->inocache->nlink));
/* Grab all nodes relevant to this ino */
ret = jffs2_get_inode_nodes(c, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
ret = jffs2_get_inode_nodes(c, ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
if (ret) {
printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %lu returned %d\n", inode->i_ino, ret);
make_bad_inode(inode);
return;
printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", ino, ret);
return ret;
}
f->dents = fd_list;
while (tn_list) {
static __u32 mdata_ver;
tn = tn_list;
fn = tn->fn;
......@@ -331,150 +316,106 @@ void jffs2_read_inode (struct inode *inode)
}
if (!fn) {
/* No data nodes for this inode. */
if (inode->i_ino != 1) {
printk(KERN_WARNING "jffs2_read_inode(): No data nodes found for ino #%lu\n", inode->i_ino);
if (ino != 1) {
printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", ino);
if (!fd_list) {
make_bad_inode(inode);
return;
}
printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n");
}
inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
latest_node.version = 0;
inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
inode->i_nlink = f->inocache->nlink;
inode->i_size = 0;
} else {
__u32 crc;
ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(latest_node), &retlen, (void *)&latest_node);
if (ret || retlen != sizeof(latest_node)) {
printk(KERN_NOTICE "MTD read in jffs2_read_inode() failed: Returned %d, %ld of %d bytes read\n",
ret, (long)retlen, sizeof(latest_node));
jffs2_clear_inode(inode);
make_bad_inode(inode);
return;
}
crc = crc32(0, &latest_node, sizeof(latest_node)-8);
if (crc != latest_node.node_crc) {
printk(KERN_NOTICE "CRC failed for read_inode of inode %ld at physical location 0x%x\n", inode->i_ino, fn->raw->flash_offset & ~3);
jffs2_clear_inode(inode);
make_bad_inode(inode);
return;
}
inode->i_mode = latest_node.mode;
inode->i_uid = latest_node.uid;
inode->i_gid = latest_node.gid;
inode->i_size = latest_node.isize;
if (S_ISREG(inode->i_mode))
jffs2_truncate_fraglist(c, &f->fraglist, latest_node.isize);
inode->i_atime = latest_node.atime;
inode->i_mtime = latest_node.mtime;
inode->i_ctime = latest_node.ctime;
}
/* OK, now the special cases. Certain inode types should
have only one data node, and it's kept as the metadata
node */
if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode) ||
S_ISLNK(inode->i_mode)) {
if (f->metadata) {
printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had metadata node\n", inode->i_ino, inode->i_mode);
jffs2_clear_inode(inode);
make_bad_inode(inode);
return;
return -EIO;
}
if (!f->fraglist) {
printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o has no fragments\n", inode->i_ino, inode->i_mode);
jffs2_clear_inode(inode);
make_bad_inode(inode);
return;
}
/* ASSERT: f->fraglist != NULL */
if (f->fraglist->next) {
printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had more than one node\n", inode->i_ino, inode->i_mode);
/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
jffs2_clear_inode(inode);
make_bad_inode(inode);
return;
printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
}
/* OK. We're happy */
f->metadata = f->fraglist->node;
jffs2_free_node_frag(f->fraglist);
f->fraglist = NULL;
latest_node->mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
latest_node->version = 0;
latest_node->atime = latest_node->ctime = latest_node->mtime = 0;
latest_node->isize = 0;
latest_node->gid = 0;
latest_node->uid = 0;
return 0;
}
inode->i_blksize = PAGE_SIZE;
inode->i_blocks = (inode->i_size + 511) >> 9;
switch (inode->i_mode & S_IFMT) {
unsigned short rdev;
ret = jffs2_flash_read(c, fn->raw->flash_offset & ~3, sizeof(*latest_node), &retlen, (void *)latest_node);
if (ret || retlen != sizeof(*latest_node)) {
printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %ld of %d bytes read\n",
ret, (long)retlen, sizeof(*latest_node));
/* FIXME: If this fails, there seems to be a memory leak. Find it. */
jffs2_do_clear_inode(c, f);
return ret?ret:-EIO;
}
case S_IFLNK:
inode->i_op = &jffs2_symlink_inode_operations;
/* Hack to work around broken isize in old symlink code.
Remove this when dwmw2 comes to his senses and stops
symlinks from being an entirely gratuitous special
case. */
if (!inode->i_size)
inode->i_size = latest_node.dsize;
break;
crc = crc32(0, latest_node, sizeof(*latest_node)-8);
if (crc != latest_node->node_crc) {
printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", ino, fn->raw->flash_offset & ~3);
jffs2_do_clear_inode(c, f);
return -EIO;
}
switch(latest_node->mode & S_IFMT) {
case S_IFDIR:
if (mctime_ver > latest_node.version) {
if (mctime_ver > latest_node->version) {
/* The times in the latest_node are actually older than
mctime in the latest dirent. Cheat. */
inode->i_mtime = inode->i_ctime = inode->i_atime =
latest_mctime;
latest_node->ctime = latest_node->mtime = latest_mctime;
}
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;
/* If it was a regular file, truncate it to the latest node's isize */
jffs2_truncate_fraglist(c, &f->fraglist, latest_node->isize);
break;
case S_IFLNK:
/* Hack to work around broken isize in old symlink code.
Remove this when dwmw2 comes to his senses and stops
symlinks from being an entirely gratuitous special
case. */
if (!latest_node->isize)
latest_node->isize = latest_node->dsize;
/* fall through... */
case S_IFBLK:
case S_IFCHR:
/* Read the device numbers from the media */
D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
/* Eep */
printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
jffs2_clear_inode(inode);
make_bad_inode(inode);
return;
}
case S_IFSOCK:
case S_IFIFO:
inode->i_op = &jffs2_file_inode_operations;
init_special_inode(inode, inode->i_mode, kdev_t_to_nr(mk_kdev(rdev>>8, rdev&0xff)));
/* Xertain inode types should have only one data node, and it's
kept as the metadata node */
if (f->metadata) {
printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n", ino, latest_node->mode);
jffs2_do_clear_inode(c, f);
return -EIO;
}
if (!f->fraglist) {
printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n", ino, latest_node->mode);
jffs2_do_clear_inode(c, f);
return -EIO;
}
/* ASSERT: f->fraglist != NULL */
if (f->fraglist->next) {
printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had more than one node\n", ino, latest_node->mode);
/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
jffs2_do_clear_inode(c, f);
return -EIO;
}
/* OK. We're happy */
f->metadata = f->fraglist->node;
jffs2_free_node_frag(f->fraglist);
f->fraglist = NULL;
break;
default:
printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu", inode->i_mode, (unsigned long)inode->i_ino);
}
D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
return 0;
}
void jffs2_clear_inode (struct inode *inode)
void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
{
/* 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_node_frag *frag, *frags;
struct jffs2_full_dirent *fd, *fds;
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));
/* If it's a deleted inode, grab the alloc_sem to keep the
(maybe temporary) BUG() in jffs2_mark_node_obsolete()
from triggering */
if(!f->inocache->nlink)
down(&c->alloc_sem);
down(&f->sem);
frags = f->fraglist;
fds = f->dents;
......@@ -487,7 +428,7 @@ void jffs2_clear_inode (struct inode *inode)
while (frags) {
frag = frags;
frags = frag->next;
D2(printk(KERN_DEBUG "jffs2_clear_inode: frag at 0x%x-0x%x: node %p, frags %d--\n", frag->ofs, frag->ofs+frag->size, frag->node, frag->node?frag->node->frags:0));
D2(printk(KERN_DEBUG "jffs2_do_clear_inode: frag at 0x%x-0x%x: node %p, frags %d--\n", frag->ofs, frag->ofs+frag->size, frag->node, frag->node?frag->node->frags:0));
if (frag->node && !(--frag->node->frags)) {
/* Not a hole, and it's the final remaining frag of this node. Free the node */
......@@ -503,10 +444,12 @@ void jffs2_clear_inode (struct inode *inode)
fds = fd->next;
jffs2_free_full_dirent(fd);
}
// if (!f->inocache->nlink) {
// D1(printk(KERN_DEBUG "jffs2_clear_inode() deleting inode #%lu\n", inode->i_ino));
// jffs2_del_ino_cache(JFFS2_SB_INFO(inode->i_sb), f->inocache);
// jffs2_free_inode_cache(f->inocache);
// }
};
/* Urgh. Is there a nicer way to do this? */
if(!f->inocache->nlink) {
up(&f->sem);
up(&c->alloc_sem);
} else {
up(&f->sem);
}
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,12 +31,11 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: scan.c,v 1.51.2.2 2002/02/23 13:34:31 dwmw2 Exp $
* $Id: scan.c,v 1.69 2002/03/08 11:03:23 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/jffs2.h>
#include <linux/mtd/mtd.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
......@@ -71,15 +70,21 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
* Returning an error will abort the mount - bad checksums etc. should just mark the space
* as dirty.
*/
static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs, int *noise);
static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs, int *noise);
static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs);
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs);
#define BLK_STATE_ALLFF 0
#define BLK_STATE_CLEAN 1
#define BLK_STATE_PARTDIRTY 2
#define BLK_STATE_CLEANMARKER 3
#define BLK_STATE_ALLDIRTY 4
#define BLK_STATE_BADBLOCK 5
int jffs2_scan_medium(struct jffs2_sb_info *c)
{
int i, ret;
__u32 empty_blocks = 0;
uint32_t empty_blocks = 0, bad_blocks = 0;
if (!c->blocks) {
printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
......@@ -95,7 +100,8 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
ACCT_PARANOIA_CHECK(jeb);
/* Now decide which list to put it on */
if (ret == 1) {
switch(ret) {
case BLK_STATE_ALLFF:
/*
* Empty block. Since we can't be sure it
* was entirely erased, we just queue it for erase
......@@ -103,10 +109,12 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
* is complete. Meanwhile we still count it as empty
* for later checks.
*/
list_add(&jeb->list, &c->erase_pending_list);
empty_blocks++;
list_add(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
} else if (jeb->used_size == PAD(sizeof(struct jffs2_unknown_node)) && !jeb->first_node->next_in_ino) {
break;
case BLK_STATE_CLEANMARKER:
/* Only a CLEANMARKER node is valid */
if (!jeb->dirty_size) {
/* It's actually free */
......@@ -118,10 +126,14 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
list_add(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
}
} else if (jeb->used_size > c->sector_size - (2*sizeof(struct jffs2_raw_inode))) {
break;
case BLK_STATE_CLEAN:
/* Full (or almost full) of clean data. Clean list */
list_add(&jeb->list, &c->clean_list);
} else if (jeb->used_size) {
break;
case BLK_STATE_PARTDIRTY:
/* Some data, but not full. Dirty list. */
/* Except that we want to remember the block with most free space,
and stick it in the 'nextblock' position to start writing to it.
......@@ -137,20 +149,36 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
} else {
list_add(&jeb->list, &c->dirty_list);
}
} else {
break;
case BLK_STATE_ALLDIRTY:
/* Nothing valid - not even a clean marker. Needs erasing. */
/* For now we just put it on the erasing list. We'll start the erases later */
printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset);
D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset));
list_add(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
break;
case BLK_STATE_BADBLOCK:
D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset));
list_add(&jeb->list, &c->bad_list);
c->bad_size += c->sector_size;
c->free_size -= c->sector_size;
bad_blocks++;
break;
default:
printk("jffs2_scan_medium(): unknown block state\n");
BUG();
}
}
/* Rotate the lists by some number to ensure wear levelling */
jffs2_rotate_lists(c);
if (c->nr_erasing_blocks) {
if (!c->used_size && empty_blocks != c->nr_blocks) {
if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
return -EIO;
}
jffs2_erase_pending_trigger(c);
......@@ -160,27 +188,62 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
struct jffs2_unknown_node node;
__u32 ofs, prevofs;
__u32 hdr_crc, nodetype;
uint32_t ofs, prevofs;
uint32_t hdr_crc, nodetype;
int err;
int noise = 0;
#ifdef CONFIG_JFFS2_FS_NAND
int cleanmarkerfound=0;
#endif
ofs = jeb->offset;
prevofs = jeb->offset - 1;
D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
#ifdef CONFIG_JFFS2_FS_NAND
if (jffs2_cleanmarker_oob(c)) {
int ret = jffs2_check_nand_cleanmarker(c, jeb);
D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
/* Even if it's not found, we still scan to see
if the block is empty. We use this information
to decide whether to erase it or not. */
switch (ret) {
case 0: cleanmarkerfound = 1; break;
case 1: break;
case 2: return BLK_STATE_BADBLOCK;
case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
default: return ret;
}
}
#endif
err = jffs2_scan_empty(c, jeb, &ofs, &noise);
if (err) return err;
if (err)
return err;
if (ofs == jeb->offset + c->sector_size) {
#ifdef CONFIG_JFFS2_FS_NAND
if (jffs2_cleanmarker_oob(c)) {
/* scan oob, take care of cleanmarker */
int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
D2(printk(KERN_NOTICE "jffs_check_oob_empty returned %d\n",ret));
switch (ret) {
case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
case 1: return BLK_STATE_ALLDIRTY;
case 2: return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */
case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
default: return ret;
}
}
#endif
D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
return 1; /* special return code */
return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
}
noise = 10;
while(ofs < jeb->offset + c->sector_size) {
ssize_t retlen;
size_t retlen;
ACCT_PARANOIA_CHECK(jeb);
if (ofs & 3) {
......@@ -202,8 +265,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
break;
}
err = c->mtd->read(c->mtd, ofs, sizeof(node), &retlen, (char *)&node);
err = jffs2_flash_read(c, ofs, sizeof(node), &retlen, (char *)&node);
if (err) {
D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", sizeof(node), ofs, err));
return err;
......@@ -261,7 +323,15 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
continue;
}
switch(node.nodetype | JFFS2_NODE_ACCURATE) {
if (!(node.nodetype & JFFS2_NODE_ACCURATE)) {
/* Wheee. This is an obsoleted node */
D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
DIRTY_SPACE(PAD(node.totlen));
ofs += PAD(node.totlen);
continue;
}
switch(node.nodetype) {
case JFFS2_NODETYPE_INODE:
err = jffs2_scan_inode_node(c, jeb, &ofs);
if (err) return err;
......@@ -304,7 +374,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
case JFFS2_FEATURE_ROCOMPAT:
printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
c->flags |= JFFS2_SB_FLAG_RO;
if (!(OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY))
if (!(jffs2_is_readonly(c)))
return -EROFS;
DIRTY_SPACE(PAD(node.totlen));
ofs += PAD(node.totlen);
......@@ -315,43 +385,54 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
return -EINVAL;
case JFFS2_FEATURE_RWCOMPAT_DELETE:
printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs));
DIRTY_SPACE(PAD(node.totlen));
ofs += PAD(node.totlen);
break;
case JFFS2_FEATURE_RWCOMPAT_COPY:
printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs));
USED_SPACE(PAD(node.totlen));
ofs += PAD(node.totlen);
break;
}
}
}
D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset,
jeb->free_size, jeb->dirty_size, jeb->used_size));
return 0;
if (jeb->used_size == PAD(sizeof(struct jffs2_unknown_node)) &&
!jeb->first_node->next_in_ino && !jeb->dirty_size)
return BLK_STATE_CLEANMARKER;
else if (jeb->used_size > c->sector_size - (2*sizeof(struct jffs2_raw_inode)))
return BLK_STATE_CLEAN;
else if (jeb->used_size)
return BLK_STATE_PARTDIRTY;
else
return BLK_STATE_ALLDIRTY;
}
/* We're pointing at the first empty word on the flash. Scan and account for the whole dirty region */
static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *startofs, int *noise)
static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *startofs, int *noise)
{
__u32 *buf;
__u32 scanlen = (jeb->offset + c->sector_size) - *startofs;
__u32 curofs = *startofs;
uint32_t *buf;
uint32_t scanlen = (jeb->offset + c->sector_size) - *startofs;
uint32_t curofs = *startofs;
buf = kmalloc(min((__u32)PAGE_SIZE, scanlen), GFP_KERNEL);
buf = kmalloc(min((uint32_t)PAGE_SIZE, scanlen), GFP_KERNEL);
if (!buf) {
printk(KERN_WARNING "Scan buffer allocation failed\n");
return -ENOMEM;
}
while(scanlen) {
ssize_t retlen;
size_t retlen;
int ret, i;
ret = c->mtd->read(c->mtd, curofs, min((__u32)PAGE_SIZE, scanlen), &retlen, (char *)buf);
if(ret) {
D1(printk(KERN_WARNING "jffs2_scan_empty(): Read 0x%x bytes at 0x%08x returned %d\n", min((__u32)PAGE_SIZE, scanlen), curofs, ret));
ret = jffs2_flash_read(c, curofs, min((uint32_t)PAGE_SIZE, scanlen), &retlen, (char *)buf);
if (ret) {
D1(printk(KERN_WARNING "jffs2_scan_empty(): Read 0x%x bytes at 0x%08x returned %d\n", min((uint32_t)PAGE_SIZE, scanlen), curofs, ret));
kfree(buf);
return ret;
}
......@@ -363,7 +444,6 @@ static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
for (i=0; i<(retlen / 4); i++) {
if (buf[i] != 0xffffffff) {
curofs += i*4;
noisy_printk(noise, "jffs2_scan_empty(): Empty block at 0x%08x ends at 0x%08x (with 0x%08x)! Marking dirty\n", *startofs, curofs, buf[i]);
DIRTY_SPACE(curofs - (*startofs));
*startofs = curofs;
......@@ -381,7 +461,7 @@ static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
return 0;
}
static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, __u32 ino)
static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
{
struct jffs2_inode_cache *ic;
......@@ -410,21 +490,21 @@ static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info
return ic;
}
static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs)
{
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dnode *fn;
struct jffs2_tmp_dnode_info *tn, **tn_list;
struct jffs2_inode_cache *ic;
struct jffs2_raw_inode ri;
__u32 crc;
__u16 oldnodetype;
uint32_t crc;
uint16_t oldnodetype;
int ret;
ssize_t retlen;
size_t retlen;
D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", *ofs));
ret = c->mtd->read(c->mtd, *ofs, sizeof(ri), &retlen, (char *)&ri);
ret = jffs2_flash_read(c, *ofs, sizeof(ri), &retlen, (char *)&ri);
if (ret) {
printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs, ret);
return ret;
......@@ -460,14 +540,14 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
if (ri.csize) {
/* Check data CRC too */
unsigned char *dbuf;
__u32 crc;
uint32_t crc;
dbuf = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
if (!dbuf) {
printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of temporary data buffer for CRC check failed\n");
return -ENOMEM;
}
ret = c->mtd->read(c->mtd, *ofs+sizeof(ri), ri.csize, &retlen, dbuf);
ret = jffs2_flash_read(c, *ofs+sizeof(ri), ri.csize, &retlen, dbuf);
if (ret) {
printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs+sizeof(ri), ret);
kfree(dbuf);
......@@ -551,6 +631,13 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
}
if (ri.nodetype & JFFS2_NODE_ACCURATE) {
/* Only do fraglist truncation in pass1 for S_IFREG inodes */
if (S_ISREG(ri.mode) && ic->scan->version < ri.version) {
ic->scan->version = ri.version;
ic->scan->isize = ri.isize;
}
memset(fn,0,sizeof(*fn));
fn->ofs = ri.offset;
......@@ -587,20 +674,20 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
return 0;
}
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs)
{
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dirent *fd;
struct jffs2_inode_cache *ic;
struct jffs2_raw_dirent rd;
__u16 oldnodetype;
uint16_t oldnodetype;
int ret;
__u32 crc;
ssize_t retlen;
uint32_t crc;
size_t retlen;
D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs));
ret = c->mtd->read(c->mtd, *ofs, sizeof(rd), &retlen, (char *)&rd);
ret = jffs2_flash_read(c, *ofs, sizeof(rd), &retlen, (char *)&rd);
if (ret) {
printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", *ofs, ret);
return ret;
......@@ -632,8 +719,8 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
fd = jffs2_alloc_full_dirent(rd.nsize+1);
if (!fd) {
return -ENOMEM;
}
ret = c->mtd->read(c->mtd, *ofs + sizeof(rd), rd.nsize, &retlen, &fd->name[0]);
}
ret = jffs2_flash_read(c, *ofs + sizeof(rd), rd.nsize, &retlen, &fd->name[0]);
if (ret) {
jffs2_free_full_dirent(fd);
printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n",
......@@ -646,6 +733,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
retlen, *ofs + sizeof(rd), rd.nsize);
return -EIO;
}
crc = crc32(0, fd->name, rd.nsize);
if (crc != rd.name_crc) {
printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
......@@ -690,13 +778,11 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
fd->name[rd.nsize]=0;
fd->nhash = full_name_hash(fd->name, rd.nsize);
fd->type = rd.type;
USED_SPACE(PAD(rd.totlen));
jffs2_add_fd_to_list(c, fd, &ic->scan->dents);
} else {
raw->flash_offset |= 1;
jffs2_free_full_dirent(fd);
DIRTY_SPACE(PAD(rd.totlen));
}
*ofs += PAD(rd.totlen);
......@@ -721,8 +807,9 @@ static void rotate_list(struct list_head *head, uint32_t count)
struct list_head *n = head->next;
list_del(head);
while(count--)
while(count--) {
n = n->next;
}
list_add(head, n);
}
......@@ -743,4 +830,5 @@ static void jffs2_rotate_lists(struct jffs2_sb_info *c)
if (c->nr_free_blocks) /* Not that it should ever be zero */
rotate_list((&c->free_list), pseudo_random % c->nr_free_blocks);
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,8 +31,7 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: super.c,v 1.48.2.1 2002/02/23 14:13:34 dwmw2 Exp $
* + zlib_init calls from v1.56
* $Id: super.c,v 1.62 2002/03/12 16:23:41 dwmw2 Exp $
*
*/
......@@ -50,25 +49,41 @@
#include <linux/interrupt.h>
#include "nodelist.h"
#ifndef MTD_BLOCK_MAJOR
#define MTD_BLOCK_MAJOR 31
#endif
extern void jffs2_read_inode (struct inode *);
void jffs2_put_super (struct super_block *);
void jffs2_write_super (struct super_block *);
static int jffs2_statfs (struct super_block *, struct statfs *);
int jffs2_remount_fs (struct super_block *, int *, char *);
extern void jffs2_clear_inode (struct inode *);
extern void jffs2_destroy_inode (struct inode *);
extern struct inode *jffs2_alloc_inode (struct super_block *);
static kmem_cache_t *jffs2_inode_cachep;
static struct inode *jffs2_alloc_inode(struct super_block *sb)
{
struct jffs2_inode_info *ei;
ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL);
if (!ei)
return NULL;
return &ei->vfs_inode;
}
static void jffs2_destroy_inode(struct inode *inode)
{
kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
}
static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
{
struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo;
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR) {
init_MUTEX(&ei->sem);
inode_init_once(&ei->vfs_inode);
}
}
static struct super_operations jffs2_super_operations =
{
alloc_inode: jffs2_alloc_inode,
destroy_inode: jffs2_destroy_inode,
read_inode: jffs2_read_inode,
// delete_inode: jffs2_delete_inode,
put_super: jffs2_put_super,
write_super: jffs2_write_super,
statfs: jffs2_statfs,
......@@ -76,222 +91,37 @@ static struct super_operations jffs2_super_operations =
clear_inode: jffs2_clear_inode
};
static int jffs2_statfs(struct super_block *sb, struct statfs *buf)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
unsigned long avail;
buf->f_type = JFFS2_SUPER_MAGIC;
buf->f_bsize = 1 << PAGE_SHIFT;
buf->f_blocks = c->flash_size >> PAGE_SHIFT;
buf->f_files = 0;
buf->f_ffree = 0;
buf->f_namelen = JFFS2_MAX_NAME_LEN;
spin_lock_bh(&c->erase_completion_lock);
avail = c->dirty_size + c->free_size;
if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE)
avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE;
else
avail = 0;
buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
#if CONFIG_JFFS2_FS_DEBUG > 0
printk(KERN_DEBUG "STATFS:\n");
printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
if (c->nextblock) {
printk(KERN_DEBUG "nextblock: 0x%08x\n", c->nextblock->offset);
} else {
printk(KERN_DEBUG "nextblock: NULL\n");
}
if (c->gcblock) {
printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->offset);
} else {
printk(KERN_DEBUG "gcblock: NULL\n");
}
if (list_empty(&c->clean_list)) {
printk(KERN_DEBUG "clean_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->clean_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "clean_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->dirty_list)) {
printk(KERN_DEBUG "dirty_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->dirty_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "dirty_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->erasing_list)) {
printk(KERN_DEBUG "erasing_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->erasing_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "erasing_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->erase_pending_list)) {
printk(KERN_DEBUG "erase_pending_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->erase_pending_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "erase_pending_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->free_list)) {
printk(KERN_DEBUG "free_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->free_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "free_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->bad_list)) {
printk(KERN_DEBUG "bad_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->bad_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "bad_list: %08x\n", jeb->offset);
}
}
if (list_empty(&c->bad_used_list)) {
printk(KERN_DEBUG "bad_used_list: empty\n");
} else {
struct list_head *this;
list_for_each(this, &c->bad_used_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "bad_used_list: %08x\n", jeb->offset);
}
}
#endif /* CONFIG_JFFS2_FS_DEBUG */
spin_unlock_bh(&c->erase_completion_lock);
return 0;
}
static int jffs2_fill_super(struct super_block *sb, void *data, int silent)
static int jffs2_blk_fill_super(struct super_block *sb, void *data, int silent)
{
struct jffs2_sb_info *c;
struct inode *root_i;
int i;
int ret;
D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", sb->s_id));
D1(printk(KERN_DEBUG "jffs2: blk_read_super for device %s\n", sb->s_id));
if (major(sb->s_dev) != MTD_BLOCK_MAJOR) {
if (!silent)
printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev));
printk(KERN_NOTICE "jffs2: attempt to mount non-MTD device %s\n",
sb->s_id);
return -EINVAL;
}
c = JFFS2_SB_INFO(sb);
memset(c, 0, sizeof(*c));
sb->s_op = &jffs2_super_operations;
c->mtd = get_mtd_device(NULL, minor(sb->s_dev));
if (!c->mtd) {
D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", minor(sb->s_dev)));
return -EINVAL;
}
c->sector_size = c->mtd->erasesize;
c->free_size = c->flash_size = c->mtd->size;
c->nr_blocks = c->mtd->size / c->mtd->erasesize;
c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
if (!c->blocks)
goto out_mtd;
for (i=0; i<c->nr_blocks; i++) {
INIT_LIST_HEAD(&c->blocks[i].list);
c->blocks[i].offset = i * c->sector_size;
c->blocks[i].free_size = c->sector_size;
c->blocks[i].dirty_size = 0;
c->blocks[i].used_size = 0;
c->blocks[i].first_node = NULL;
c->blocks[i].last_node = NULL;
}
spin_lock_init(&c->nodelist_lock);
init_MUTEX(&c->alloc_sem);
init_waitqueue_head(&c->erase_wait);
spin_lock_init(&c->erase_completion_lock);
spin_lock_init(&c->inocache_lock);
INIT_LIST_HEAD(&c->clean_list);
INIT_LIST_HEAD(&c->dirty_list);
INIT_LIST_HEAD(&c->erasing_list);
INIT_LIST_HEAD(&c->erase_pending_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->flags |= JFFS2_SB_FLAG_MOUNTING;
if (jffs2_build_filesystem(c)) {
D1(printk(KERN_DEBUG "build_fs failed\n"));
goto out_nodes;
}
c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
sb->s_op = &jffs2_super_operations;
D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
root_i = iget(sb, 1);
if (is_bad_inode(root_i)) {
D1(printk(KERN_WARNING "get root inode failed\n"));
goto out_nodes;
}
D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n"));
sb->s_root = d_alloc_root(root_i);
if (!sb->s_root)
goto out_root_i;
#if LINUX_VERSION_CODE >= 0x20403
sb->s_maxbytes = 0xFFFFFFFF;
#endif
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = JFFS2_SUPER_MAGIC;
if (!(sb->s_flags & MS_RDONLY))
jffs2_start_garbage_collect_thread(c);
return 0;
out_root_i:
iput(root_i);
out_nodes:
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
kfree(c->blocks);
out_mtd:
ret = jffs2_do_fill_super(sb, data, silent);
if (ret)
put_mtd_device(c->mtd);
return -EINVAL;
return ret;
}
void jffs2_put_super (struct super_block *sb)
......@@ -300,8 +130,10 @@ void jffs2_put_super (struct super_block *sb)
D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
if (!(sb->s_flags & MS_RDONLY))
jffs2_stop_garbage_collect_thread(c);
jffs2_flush_wbuf(c, 1);
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
kfree(c->blocks);
......@@ -312,44 +144,10 @@ void jffs2_put_super (struct super_block *sb)
D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
}
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 */
if (!(sb->s_flags & MS_RDONLY))
jffs2_stop_garbage_collect_thread(c);
if (!(*flags & MS_RDONLY))
jffs2_start_garbage_collect_thread(c);
sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
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;
jffs2_garbage_collect_trigger(c);
jffs2_erase_pending_blocks(c);
jffs2_mark_erased_blocks(c);
}
static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
int flags, char *dev_name, void *data)
{
return get_sb_bdev(fs_type, flags, dev_name, data, jffs2_fill_super);
return get_sb_bdev(fs_type, flags, dev_name, data, jffs2_blk_fill_super);
}
static struct file_system_type jffs2_fs_type = {
......@@ -360,21 +158,22 @@ static struct file_system_type jffs2_fs_type = {
fs_flags: FS_REQUIRES_DEV,
};
static int __init init_jffs2_fs(void)
{
int ret;
printk(KERN_NOTICE "JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB.\n");
printk(KERN_INFO "JFFS2 version 2.1. (C) 2001, 2002 Red Hat, Inc.\n");
#ifdef JFFS2_OUT_OF_KERNEL
/* sanity checks. Could we do these at compile time? */
if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) {
printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n",
sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u));
return -EIO;
jffs2_inode_cachep = kmem_cache_create("jffs2_i",
sizeof(struct jffs2_inode_info),
0, SLAB_HWCACHE_ALIGN,
jffs2_i_init_once, NULL);
if (!jffs2_inode_cachep) {
printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
return -ENOMEM;
}
#endif
ret = jffs2_zlib_init();
if (ret) {
printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
......@@ -395,9 +194,10 @@ static int __init init_jffs2_fs(void)
static void __exit exit_jffs2_fs(void)
{
unregister_filesystem(&jffs2_fs_type);
jffs2_destroy_slab_caches();
jffs2_zlib_exit();
unregister_filesystem(&jffs2_fs_type);
kmem_cache_destroy(jffs2_inode_cachep);
}
module_init(init_jffs2_fs);
......
......@@ -31,7 +31,7 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: symlink.c,v 1.5.2.1 2002/01/15 10:39:06 dwmw2 Exp $
* $Id: symlink.c,v 1.9 2002/01/10 09:29:53 dwmw2 Exp $
*
*/
......@@ -39,7 +39,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/jffs2.h>
#include "nodelist.h"
int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
......@@ -52,40 +51,12 @@ struct inode_operations jffs2_symlink_inode_operations =
setattr: jffs2_setattr
};
static char *jffs2_getlink(struct dentry *dentry)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
char *buf;
int ret;
down(&f->sem);
if (!f->metadata) {
up(&f->sem);
printk(KERN_NOTICE "No metadata for symlink inode #%lu\n", dentry->d_inode->i_ino);
return ERR_PTR(-EINVAL);
}
buf = kmalloc(f->metadata->size+1, GFP_USER);
if (!buf) {
up(&f->sem);
return ERR_PTR(-ENOMEM);
}
buf[f->metadata->size]=0;
ret = jffs2_read_dnode(JFFS2_SB_INFO(dentry->d_inode->i_sb), f->metadata, buf, 0, f->metadata->size);
up(&f->sem);
if (ret) {
kfree(buf);
return ERR_PTR(ret);
}
return buf;
}
int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
{
unsigned char *kbuf;
int ret;
kbuf = jffs2_getlink(dentry);
kbuf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
if (IS_ERR(kbuf))
return PTR_ERR(kbuf);
......@@ -99,7 +70,7 @@ int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
unsigned char *buf;
int ret;
buf = jffs2_getlink(dentry);
buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
if (IS_ERR(buf))
return PTR_ERR(buf);
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
* The original JFFS, from which the design for JFFS2 was derived,
* was designed and implemented by Axis Communications AB.
*
* The contents of this file are subject to the Red Hat eCos Public
* License Version 1.1 (the "Licence"); you may not use this file
* except in compliance with the Licence. You may obtain a copy of
* the Licence at http://www.redhat.com/
*
* Software distributed under the Licence is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the Licence for the specific language governing rights and
* limitations under the Licence.
*
* The Original Code is JFFS2 - Journalling Flash File System, version 2
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in
* which case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the RHEPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: wbuf.c,v 1.7 2002/03/08 11:27:59 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/interrupt.h>
#include <linux/crc32.h>
#include "nodelist.h"
/* FIXME duplicated defines in wbuf.c and nand.c
* Constants for out of band layout
*/
#define NAND_JFFS2_OOB_BADBPOS 5
#define NAND_JFFS2_OOB8_FSDAPOS 6
#define NAND_JFFS2_OOB16_FSDAPOS 8
#define NAND_JFFS2_OOB8_FSDALEN 2
#define NAND_JFFS2_OOB16_FSDALEN 8
#define MAX_ERASE_FAILURES 5
static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
{
struct list_head *this, *next;
if (list_empty(&c->erasable_pending_wbuf_list))
return;
list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
list_del(this);
list_add_tail(this, &c->erasable_list);
}
}
int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
{
int ret;
size_t retlen;
if(!c->wbuf || !c->wbuf_len)
return 0;
/* claim remaining space on the page
this happens, if we have a change to a new block,
or if fsync forces us to flush the writebuffer.
if we have a switch to next page, we will not have
enough remaining space for this.
*/
if (pad) {
c->wbuf_len = PAD(c->wbuf_len);
if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) {
struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len);
padnode->magic = JFFS2_MAGIC_BITMASK;
padnode->nodetype = JFFS2_NODETYPE_PADDING;
padnode->totlen = c->wbuf_pagesize - c->wbuf_len;
padnode->hdr_crc = crc32(0, padnode, sizeof(*padnode)-4);
}
}
/* else jffs2_flash_writev has actually filled in the rest of the
buffer for us, and will deal with the node refs etc. later. */
ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
if (ret || retlen != c->wbuf_pagesize) {
if (ret)
printk(KERN_CRIT "jffs2_flush_wbuf(): Write failed with %d\n",ret);
else
printk(KERN_CRIT "jffs2_flush_wbuf(): Write was short %d instead of %d\n",retlen,c->wbuf_pagesize);
ret = -EIO;
/* CHECKME NAND
So that the caller knows what happened. If
we were called from jffs2_flash_writev(), it'll
know to return failure and _its_ caller will
try again. writev gives back to jffs2_write_xxx
in write.c. There are the real fixme's
*/
/* FIXME NAND
If we were called from GC or fsync, there's no repair kit yet
*/
return ret;
}
/* Adjusting free size of next block only, if it's called from fsync ! */
if (pad == 2) {
D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n"));
spin_lock_bh(&c->erase_completion_lock);
if (!c->nextblock)
BUG();
if (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len))
BUG();
c->nextblock->free_size -= (c->wbuf_pagesize - c->wbuf_len);
c->nextblock->dirty_size += (c->wbuf_pagesize - c->wbuf_len);
spin_unlock_bh(&c->erase_completion_lock);
}
/* Stick any now-obsoleted blocks on the erase_pending_list */
spin_lock_bh(&c->erase_completion_lock);
jffs2_refile_wbuf_blocks(c);
spin_unlock_bh(&c->erase_completion_lock);
memset(c->wbuf,0xff,c->wbuf_pagesize);
/* adjust write buffer offset, else we get a non contigous write bug */
c->wbuf_ofs+= c->wbuf_pagesize;
c->wbuf_len = 0;
return 0;
}
#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsigned long count, loff_t to, size_t *retlen)
{
struct iovec outvecs[3];
uint32_t totlen = 0;
uint32_t split_ofs = 0;
uint32_t old_totlen;
int ret, splitvec = -1;
int invec, outvec;
size_t wbuf_retlen;
unsigned char *wbuf_ptr;
size_t donelen = 0;
uint32_t outvec_to = to;
/* If not NAND flash, don't bother */
if (!c->wbuf)
return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
/* If wbuf_ofs is not initialized, set it to target adress */
if (c->wbuf_ofs == 0xFFFFFFFF) {
c->wbuf_ofs = PAGE_DIV(to);
c->wbuf_len = PAGE_MOD(to);
memset(c->wbuf,0xff,c->wbuf_pagesize);
}
/* Sanity checks on target address.
It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs),
and it's permitted to write at the beginning of a new
erase block. Anything else, and you die.
New block starts at xxx000c (0-b = block header)
*/
if ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) {
/* It's a write to a new block */
if (c->wbuf_len) {
D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs));
ret = jffs2_flush_wbuf(c, 1);
if (ret) {
/* the underlying layer has to check wbuf_len to do the cleanup */
D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
*retlen = 0;
return ret;
}
}
/* set pointer to new block */
c->wbuf_ofs = PAGE_DIV(to);
c->wbuf_len = PAGE_MOD(to);
}
if (to != PAD(c->wbuf_ofs + c->wbuf_len)) {
/* We're not writing immediately after the writebuffer. Bad. */
printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to);
if (c->wbuf_len)
printk(KERN_CRIT "wbuf was previously %08x-%08x\n",
c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len);
BUG();
}
/* Note outvecs[3] above. We know count is never greater than 2 */
if (count > 2) {
printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count);
BUG();
}
invec = 0;
outvec = 0;
/* Fill writebuffer first, if already in use */
if (c->wbuf_len) {
uint32_t invec_ofs = 0;
/* adjust alignment offset */
if (c->wbuf_len != PAGE_MOD(to)) {
c->wbuf_len = PAGE_MOD(to);
/* take care of alignment to next page */
if (!c->wbuf_len)
c->wbuf_len = c->wbuf_pagesize;
}
while(c->wbuf_len < c->wbuf_pagesize) {
uint32_t thislen;
if (invec == count)
goto alldone;
thislen = c->wbuf_pagesize - c->wbuf_len;
if (thislen >= invecs[invec].iov_len)
thislen = invecs[invec].iov_len;
invec_ofs = thislen;
memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen);
c->wbuf_len += thislen;
donelen += thislen;
/* Get next invec, if actual did not fill the buffer */
if (c->wbuf_len < c->wbuf_pagesize)
invec++;
}
/* write buffer is full, flush buffer */
ret = jffs2_flush_wbuf(c, 0);
if (ret) {
/* the underlying layer has to check wbuf_len to do the cleanup */
D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
*retlen = 0;
return ret;
}
outvec_to += donelen;
c->wbuf_ofs = outvec_to;
/* All invecs done ? */
if (invec == count)
goto alldone;
/* Set up the first outvec, containing the remainder of the
invec we partially used */
if (invecs[invec].iov_len > invec_ofs) {
outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs;
totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs;
if (totlen > c->wbuf_pagesize) {
splitvec = outvec;
split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen);
}
outvec++;
}
invec++;
}
/* OK, now we've flushed the wbuf and the start of the bits
we have been asked to write, now to write the rest.... */
/* totlen holds the amount of data still to be written */
old_totlen = totlen;
for ( ; invec < count; invec++,outvec++ ) {
outvecs[outvec].iov_base = invecs[invec].iov_base;
totlen += outvecs[outvec].iov_len = invecs[invec].iov_len;
if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) {
splitvec = outvec;
split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen);
old_totlen = totlen;
}
}
/* Now the outvecs array holds all the remaining data to write */
/* Up to splitvec,split_ofs is to be written immediately. The rest
goes into the (now-empty) wbuf */
if (splitvec != -1) {
uint32_t remainder;
int ret;
remainder = outvecs[splitvec].iov_len - split_ofs;
outvecs[splitvec].iov_len = split_ofs;
/* We did cross a page boundary, so we write some now */
ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen);
if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
/* At this point we have no problem,
c->wbuf is empty.
*/
*retlen = donelen;
return ret;
}
donelen += wbuf_retlen;
c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen);
if (remainder) {
outvecs[splitvec].iov_base += split_ofs;
outvecs[splitvec].iov_len = remainder;
} else {
splitvec++;
}
} else {
splitvec = 0;
}
/* Now splitvec points to the start of the bits we have to copy
into the wbuf */
wbuf_ptr = c->wbuf;
for ( ; splitvec < outvec; splitvec++) {
/* Don't copy the wbuf into itself */
if (outvecs[splitvec].iov_base == c->wbuf)
continue;
memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len);
wbuf_ptr += outvecs[splitvec].iov_len;
donelen += outvecs[splitvec].iov_len;
}
c->wbuf_len = wbuf_ptr - c->wbuf;
alldone:
*retlen = donelen;
return 0;
}
/*
This is the entry for NOR-Flash. We use it also for NAND to flush wbuf
*/
int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf)
{
return c->mtd->write(c->mtd, ofs, len, retlen, buf);
}
/*
Handle readback from writebuffer and ECC failure return
*/
int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf)
{
loff_t orbf = 0, owbf = 0, lwbf = 0;
int ret;
/* Read flash */
ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
if (!jffs2_can_mark_obsolete(c) && (ret == -EIO) && (*retlen == len) ) {
printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%llx) returned ECC error\n", len, ofs);
/*
* We have the raw data without ECC correction in the buffer, maybe
* we are lucky and all data or parts are correct. We check the node.
* If data are corrupted node check will sort it out.
* We keep this block, it will fail on write or erase and the we
* mark it bad. Or should we do that now? But we should give him a chance.
* Maybe we had a system crash or power loss before the ecc write or
* a erase was completed.
* So we return success. :)
*/
ret = 0;
}
/* if no writebuffer available or write buffer empty, return */
if (!c->wbuf_pagesize || !c->wbuf_len)
return ret;
/* if we read in a different block, return */
if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) )
return ret;
if (ofs >= c->wbuf_ofs) {
owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */
if (owbf > c->wbuf_len) /* is read beyond write buffer ? */
return ret;
lwbf = c->wbuf_len - owbf; /* number of bytes to copy */
if (lwbf > len)
lwbf = len;
} else {
orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */
if (orbf > len) /* is write beyond write buffer ? */
return ret;
lwbf = len - orbf; /* number of bytes to copy */
if (lwbf > c->wbuf_len)
lwbf = c->wbuf_len;
}
if (lwbf > 0)
memcpy(buf+orbf,c->wbuf+owbf,lwbf);
return ret;
}
/*
* Check, if the out of band area is empty
*/
int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode)
{
unsigned char *buf;
int ret = 0;
int i,len,cnt,page;
size_t retlen;
int fsdata_pos,badblock_pos,oob_size;
oob_size = c->mtd->oobsize;
switch(c->mtd->ecctype) {
case MTD_ECC_SW:
fsdata_pos = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDAPOS : NAND_JFFS2_OOB16_FSDAPOS;
badblock_pos = NAND_JFFS2_OOB_BADBPOS;
break;
default:
D1(printk(KERN_WARNING "jffs2_write_oob_empty(): Invalid ECC type\n"));
return -EINVAL;
}
/* allocate a buffer for all oob data in this sector */
len = oob_size * (c->sector_size/c->mtd->oobblock);
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n");
return -ENOMEM;
}
/*
* if mode = 0, we scan for a total empty oob area, else we have
* to take care of the cleanmarker in the first page of the block
*/
ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf);
if (ret) {
D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
goto out;
}
if (retlen < len) {
D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read "
"(%d bytes not %d) for block at %08x\n", retlen, len, jeb->offset));
ret = -EIO;
goto out;
}
/* Special check for first two pages */
for (page = 0; page < 2; page += oob_size) {
/* Check for bad block marker */
if (buf[page+badblock_pos] != 0xff) {
D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset));
/* Return 2 for bad and 3 for failed block
bad goes to list_bad and failed to list_erase */
ret = (!page) ? 2 : 3;
goto out;
}
cnt = oob_size;
if (mode)
cnt -= fsdata_pos;
for(i = 0; i < cnt ; i+=sizeof(unsigned short)) {
unsigned short dat = *(unsigned short *)(&buf[page+i]);
if(dat != 0xffff) {
ret = 1;
goto out;
}
}
/* only the first page can contain a cleanmarker !*/
mode = 0;
}
/* we know, we are aligned :) */
for (; page < len; page += sizeof(long)) {
unsigned long dat = *(unsigned long *)(&buf[page]);
if(dat != -1) {
ret = 1;
goto out;
}
}
out:
kfree(buf);
return ret;
}
int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct jffs2_unknown_node n;
unsigned char buf[32];
unsigned char *p;
int ret,i;
size_t retlen;
int fsdata_pos,fsdata_len, oob_size, badblock_pos;
oob_size = c->mtd->oobsize;
switch(c->mtd->ecctype) {
case MTD_ECC_SW:
fsdata_pos = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDAPOS : NAND_JFFS2_OOB16_FSDAPOS;
fsdata_len = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDALEN : NAND_JFFS2_OOB16_FSDALEN;
badblock_pos = NAND_JFFS2_OOB_BADBPOS;
break;
default:
D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Invalid ECC type\n"));
return -EINVAL;
}
/*
* We read oob data from page 0 and 1 of the block.
* page 0 contains cleanmarker and badblock info
* page 2 contains failure count of this block
*/
ret = c->mtd->read_oob(c->mtd, jeb->offset, oob_size << 1 , &retlen, buf);
if (ret) {
D1(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
return ret;
}
if (retlen < (oob_size << 1) ) {
D1(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%d bytes not %d) for block at %08x\n", retlen, oob_size << 1 , jeb->offset));
return -EIO;
}
/* Check for bad block marker */
if (buf[badblock_pos] != 0xff) {
D1(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n",jeb->offset));
return 2;
}
/* Check for failure counter in the second page */
if (buf[badblock_pos+oob_size] != 0xff) {
D1(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n",jeb->offset,buf[badblock_pos+oob_size]));
return 3;
}
n.magic = JFFS2_MAGIC_BITMASK;
n.nodetype = JFFS2_NODETYPE_CLEANMARKER;
n.totlen = 8;
p = (unsigned char *) &n;
for (i = 0; i < fsdata_len; i++) {
if (buf[fsdata_pos+i] != p[i]) {
D2(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset));
return 1;
}
}
return 0;
}
int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct jffs2_unknown_node n;
int ret;
int fsdata_pos,fsdata_len;
size_t retlen;
switch(c->mtd->ecctype) {
case MTD_ECC_SW:
fsdata_pos = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDAPOS : NAND_JFFS2_OOB16_FSDAPOS;
fsdata_len = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDALEN : NAND_JFFS2_OOB16_FSDALEN;
break;
default:
D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Invalid ECC type\n"));
return -EINVAL;
}
n.magic = JFFS2_MAGIC_BITMASK;
n.nodetype = JFFS2_NODETYPE_CLEANMARKER;
n.totlen = 8;
ret = jffs2_flash_write_oob(c, jeb->offset + fsdata_pos, fsdata_len, &retlen, (unsigned char *)&n);
if (ret) {
D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
return ret;
}
if (retlen != fsdata_len) {
D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %d not %d\n", jeb->offset, retlen, fsdata_len));
return ret;
}
return 0;
}
/*
* We try to get the failure count of this block.
*/
int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
unsigned char buf[16];
int ret;
size_t retlen;
int oob_size, badblock_pos;
oob_size = c->mtd->oobsize;
switch(c->mtd->ecctype) {
case MTD_ECC_SW:
badblock_pos = NAND_JFFS2_OOB_BADBPOS;
break;
default:
D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Invalid ECC type\n"));
return -EINVAL;
}
ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf);
if (ret) {
D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
return ret;
}
if (retlen < oob_size) {
D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%d bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset));
return -EIO;
}
jeb->bad_count = buf[badblock_pos];
return 0;
}
/*
* On NAND we try to mark this block bad. We try to write how often
* the block was erased and mark it finaly bad, if the count
* is > MAX_ERASE_FAILURES. We read this information on mount !
* jeb->bad_count contains the count before this erase.
* Don't care about failures. This block remains on the erase-pending
* or badblock list as long as nobody manipulates the flash with
* a bootloader or something like that.
*/
int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
unsigned char buf = 0x0;
int ret,pos;
size_t retlen;
switch(c->mtd->ecctype) {
case MTD_ECC_SW:
pos = NAND_JFFS2_OOB_BADBPOS;
break;
default:
D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Invalid ECC type\n"));
return -EINVAL;
}
/* if the count is < max, we try to write the counter to the 2nd page oob area */
if( ++jeb->bad_count < MAX_ERASE_FAILURES) {
buf = (unsigned char)jeb->bad_count;
pos += c->mtd->oobblock;
}
ret = jffs2_flash_write_oob(c, jeb->offset + pos, 1, &retlen, &buf);
if (ret) {
D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
return ret;
}
if (retlen != 1) {
D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %d not 1\n", jeb->offset, retlen));
return ret;
}
return 0;
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,60 +31,35 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: write.c,v 1.30 2001/12/30 16:01:11 dwmw2 Exp $
* $Id: write.c,v 1.52 2002/03/08 11:01:43 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/crc32.h>
#include <linux/jffs2.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
/* 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)
int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
{
struct inode *inode;
struct super_block *sb = dir_i->i_sb;
struct jffs2_inode_cache *ic;
struct jffs2_sb_info *c;
struct jffs2_inode_info *f;
D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
c = JFFS2_SB_INFO(sb);
memset(ri, 0, sizeof(*ri));
ic = jffs2_alloc_inode_cache();
if (!ic) {
return ERR_PTR(-ENOMEM);
}
memset(ic, 0, sizeof(*ic));
inode = new_inode(sb);
if (!inode) {
jffs2_free_inode_cache(ic);
return ERR_PTR(-ENOMEM);
return -ENOMEM;
}
/* Alloc jffs2_inode_info when that's split in 2.5 */
memset(ic, 0, sizeof(*ic));
f = JFFS2_INODE_INFO(inode);
down(&f->sem);
f->highest_version = 0;
f->fraglist = NULL;
f->metadata = NULL;
f->dents = NULL;
f->flags = 0;
f->usercompr = 0;
init_MUTEX_LOCKED(&f->sem);
f->inocache = ic;
inode->i_nlink = f->inocache->nlink = 1;
f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
f->inocache->ino = ri->ino = inode->i_ino = ++c->highest_ino;
D1(printk(KERN_DEBUG "jffs2_new_inode(): Assigned ino# %d\n", ri->ino));
f->inocache->ino = ri->ino = ++c->highest_ino;
D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", ri->ino));
jffs2_add_ino_cache(c, f->inocache);
ri->magic = JFFS2_MAGIC_BITMASK;
......@@ -93,65 +68,18 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i
ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
ri->mode = mode;
f->highest_version = ri->version = 1;
ri->uid = current->fsuid;
if (dir_i->i_mode & S_ISGID) {
ri->gid = dir_i->i_gid;
if (S_ISDIR(mode))
ri->mode |= S_ISGID;
} else {
ri->gid = current->fsgid;
}
inode->i_mode = ri->mode;
inode->i_gid = ri->gid;
inode->i_uid = ri->uid;
inode->i_atime = inode->i_ctime = inode->i_mtime =
ri->atime = ri->mtime = ri->ctime = CURRENT_TIME;
inode->i_blksize = PAGE_SIZE;
inode->i_blocks = 0;
inode->i_size = 0;
insert_inode_hash(inode);
return inode;
return 0;
}
/* 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 int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *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++) {
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;
}
static inline int mtd_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
{
if (mtd->writev)
return mtd->writev(mtd,vecs,count,to,retlen);
else
return mtd_fake_writev(mtd, vecs, count, to, retlen);
}
static void writecheck(struct mtd_info *mtd, __u32 ofs)
static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
{
unsigned char buf[16];
ssize_t retlen;
size_t retlen;
int ret, i;
ret = mtd->read(mtd, ofs, 16, &retlen, buf);
if (ret && retlen != 16) {
ret = jffs2_flash_read(c, ofs, 16, &retlen, buf);
if (ret || (retlen != 16)) {
D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %d\n", ret, retlen));
return;
}
......@@ -171,20 +99,18 @@ static void writecheck(struct mtd_info *mtd, __u32 ofs)
/* 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 inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen)
struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, uint32_t *writelen)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dnode *fn;
ssize_t retlen;
size_t retlen;
struct iovec vecs[2];
int ret;
unsigned long cnt = 2;
D1(if(ri->hdr_crc != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n");
......@@ -196,7 +122,7 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw
vecs[1].iov_base = (unsigned char *)data;
vecs[1].iov_len = datalen;
writecheck(c->mtd, flash_ofs);
writecheck(c, flash_ofs);
if (ri->totlen != sizeof(*ri) + datalen) {
printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08x) + datalen (0x%08x)\n", ri->totlen, sizeof(*ri), datalen);
......@@ -219,7 +145,12 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw
fn->frags = 0;
fn->raw = raw;
ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
/* check number of valid vecs */
if (!datalen || !data)
cnt = 1;
ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen);
if (ret || (retlen != sizeof(*ri) + datalen)) {
printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n",
sizeof(*ri)+datalen, flash_ofs, ret, retlen);
......@@ -261,18 +192,16 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw
return fn;
}
struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen)
struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, uint32_t *writelen)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dirent *fd;
ssize_t retlen;
size_t retlen;
struct iovec vecs[2];
int ret;
D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", rd->pino, name, name, rd->ino, rd->name_crc));
writecheck(c->mtd, flash_ofs);
writecheck(c, flash_ofs);
D1(if(rd->hdr_crc != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
......@@ -309,7 +238,7 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_r
fd->name[namelen]=0;
fd->raw = raw;
ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen);
if (ret || (retlen != sizeof(*rd) + namelen)) {
printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n",
sizeof(*rd)+namelen, flash_ofs, ret, retlen);
......@@ -336,3 +265,343 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_r
f->inocache->nodes = raw;
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;
unsigned char comprtype = JFFS2_COMPR_NONE;
uint32_t phys_ofs, alloclen;
uint32_t datalen, cdatalen;
D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
break;
}
down(&f->sem);
datalen = writelen;
cdatalen = min(alloclen - sizeof(*ri), writelen);
comprbuf = kmalloc(cdatalen, GFP_KERNEL);
if (comprbuf) {
comprtype = jffs2_compress(buf, comprbuf, &datalen, &cdatalen);
}
if (comprtype == JFFS2_COMPR_NONE) {
/* Either compression failed, or the allocation of comprbuf failed */
if (comprbuf)
kfree(comprbuf);
comprbuf = buf;
datalen = cdatalen;
}
/* Now comprbuf points to the data to be written, be it compressed or not.
comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means
that the comprbuf doesn't need to be kfree()d.
*/
ri->magic = JFFS2_MAGIC_BITMASK;
ri->nodetype = JFFS2_NODETYPE_INODE;
ri->totlen = sizeof(*ri) + cdatalen;
ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
ri->ino = f->inocache->ino;
ri->version = ++f->highest_version;
ri->isize = max(ri->isize, offset + datalen);
ri->offset = offset;
ri->csize = cdatalen;
ri->dsize = datalen;
ri->compr = comprtype;
ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
ri->data_crc = crc32(0, comprbuf, cdatalen);
fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, NULL);
if (comprtype != JFFS2_COMPR_NONE)
kfree(comprbuf);
if (IS_ERR(fn)) {
ret = PTR_ERR(fn);
up(&f->sem);
jffs2_complete_reservation(c);
break;
}
ret = jffs2_add_full_dnode_to_inode(c, f, fn);
if (f->metadata) {
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
f->metadata = NULL;
}
if (ret) {
/* Eep */
D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
jffs2_mark_node_obsolete(c, fn->raw);
jffs2_free_full_dnode(fn);
up(&f->sem);
jffs2_complete_reservation(c);
break;
}
up(&f->sem);
jffs2_complete_reservation(c);
if (!datalen) {
printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
ret = -EIO;
break;
}
D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
writtenlen += datalen;
offset += datalen;
writelen -= datalen;
buf += datalen;
}
*retlen = writtenlen;
return ret;
}
int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen)
{
struct jffs2_raw_dirent *rd;
struct jffs2_full_dnode *fn;
struct jffs2_full_dirent *fd;
uint32_t alloclen, phys_ofs;
uint32_t writtenlen;
int ret;
/* Try to reserve enough space for both node and dirent.
* Just the node will do for now, though
*/
ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen));
if (ret)
return ret;
ri->data_crc = 0;
ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen);
D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", 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;
/* Work out where to put the dirent node now. */
writtenlen = PAD(writtenlen);
phys_ofs += writtenlen;
alloclen -= writtenlen;
up(&f->sem);
if (alloclen < sizeof(*rd)+namelen) {
/* Not enough space left in this chunk. Get some more */
jffs2_complete_reservation(c);
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
/* Eep. */
D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
return ret;
}
}
rd = jffs2_alloc_raw_dirent();
if (!rd) {
/* Argh. Now we treat it like a normal delete */
jffs2_complete_reservation(c);
return -ENOMEM;
}
down(&dir_f->sem);
rd->magic = JFFS2_MAGIC_BITMASK;
rd->nodetype = JFFS2_NODETYPE_DIRENT;
rd->totlen = sizeof(*rd) + namelen;
rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
rd->pino = dir_f->inocache->ino;
rd->version = ++dir_f->highest_version;
rd->ino = ri->ino;
rd->mctime = ri->ctime;
rd->nsize = namelen;
rd->type = DT_REG;
rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
rd->name_crc = crc32(0, name, namelen);
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, &writtenlen);
jffs2_free_raw_dirent(rd);
if (IS_ERR(fd)) {
/* dirent failed to write. Delete the inode normally
as if it were the final unlink() */
jffs2_complete_reservation(c);
up(&dir_f->sem);
return PTR_ERR(fd);
}
/* Link the fd into the inode's list, obsoleting an old
one if necessary. */
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
jffs2_complete_reservation(c);
up(&dir_f->sem);
return 0;
}
int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f)
{
struct jffs2_raw_dirent *rd;
struct jffs2_full_dirent *fd;
uint32_t alloclen, phys_ofs;
int ret;
rd = jffs2_alloc_raw_dirent();
if (!rd)
return -ENOMEM;
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
if (ret) {
jffs2_free_raw_dirent(rd);
return ret;
}
down(&dir_f->sem);
/* Build a deletion node */
rd->magic = JFFS2_MAGIC_BITMASK;
rd->nodetype = JFFS2_NODETYPE_DIRENT;
rd->totlen = sizeof(*rd) + namelen;
rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
rd->pino = dir_f->inocache->ino;
rd->version = ++dir_f->highest_version;
rd->ino = 0;
rd->mctime = CURRENT_TIME;
rd->nsize = namelen;
rd->type = DT_UNKNOWN;
rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
rd->name_crc = crc32(0, name, namelen);
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL);
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);
if (dead_f) { /* Null if this was a rename not a real unlink */
down(&dead_f->sem);
while (dead_f->dents) {
/* There can be only deleted ones */
fd = dead_f->dents;
dead_f->dents = fd->next;
if (fd->ino) {
printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
dead_f->inocache->ino, fd->name, fd->ino);
} else {
D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino));
}
jffs2_mark_node_obsolete(c, fd->raw);
jffs2_free_full_dirent(fd);
}
dead_f->inocache->nlink--;
/* NB: Caller must set inode nlink if appropriate */
up(&dead_f->sem);
}
return 0;
}
int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen)
{
struct jffs2_raw_dirent *rd;
struct jffs2_full_dirent *fd;
uint32_t alloclen, phys_ofs;
int ret;
rd = jffs2_alloc_raw_dirent();
if (!rd)
return -ENOMEM;
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
jffs2_free_raw_dirent(rd);
return ret;
}
down(&dir_f->sem);
/* Build a deletion node */
rd->magic = JFFS2_MAGIC_BITMASK;
rd->nodetype = JFFS2_NODETYPE_DIRENT;
rd->totlen = sizeof(*rd) + namelen;
rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
rd->pino = dir_f->inocache->ino;
rd->version = ++dir_f->highest_version;
rd->ino = ino;
rd->mctime = CURRENT_TIME;
rd->nsize = namelen;
rd->type = type;
rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
rd->name_crc = crc32(0, name, namelen);
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL);
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;
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,41 +31,44 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: pushpull.c,v 1.7 2001/09/23 10:04:15 rmk Exp $
* $Id: writev.c,v 1.1 2002/03/08 11:27:59 dwmw2 Exp $
*
*/
#include <linux/string.h>
#include "pushpull.h"
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve)
/* 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 iovec *vecs,
unsigned long count, loff_t to, size_t *retlen)
{
pp->buf = buf;
pp->buflen = buflen;
pp->ofs = ofs;
pp->reserve = reserve;
}
int pushbit(struct pushpull *pp, int bit, int use_reserved)
{
if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) {
return -ENOSPC;
}
unsigned long i;
size_t totlen = 0, thislen;
int ret = 0;
if (bit) {
pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7)));
}
else {
pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7)));
for (i=0; i<count; i++) {
if (!vecs[i].iov_len)
continue;
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;
}
pp->ofs++;
return 0;
if (retlen)
*retlen = totlen;
return ret;
}
int pushedbits(struct pushpull *pp)
int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
unsigned long count, loff_t to, size_t *retlen)
{
return pp->ofs;
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);
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
*
......@@ -31,14 +31,13 @@
* provisions above, a recipient may use your version of this file
* under either the RHEPL or the GPL.
*
* $Id: jffs2.h,v 1.19 2001/10/09 13:20:23 dwmw2 Exp $
* $Id: jffs2.h,v 1.23 2002/02/21 17:03:45 dwmw2 Exp $
*
*/
#ifndef __LINUX_JFFS2_H__
#define __LINUX_JFFS2_H__
#include <asm/types.h>
#define JFFS2_SUPER_MAGIC 0x72b6
/* Values we may expect to find in the 'magic' field */
......@@ -78,16 +77,12 @@
#define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
// Maybe later...
//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
/* Same as the non_ECC versions, but with extra space for real
* ECC instead of just the checksum. For use on NAND flash
*/
//#define JFFS2_NODETYPE_DIRENT_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 5)
//#define JFFS2_NODETYPE_INODE_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 6)
#define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at
mount time, don't wait for it to
......@@ -99,28 +94,28 @@
struct jffs2_unknown_node
{
/* All start like this */
__u16 magic;
__u16 nodetype;
__u32 totlen; /* So we can skip over nodes we don't grok */
__u32 hdr_crc;
uint16_t magic;
uint16_t nodetype;
uint32_t totlen; /* So we can skip over nodes we don't grok */
uint32_t hdr_crc;
} __attribute__((packed));
struct jffs2_raw_dirent
{
__u16 magic;
__u16 nodetype; /* == JFFS_NODETYPE_DIRENT */
__u32 totlen;
__u32 hdr_crc;
__u32 pino;
__u32 version;
__u32 ino; /* == zero for unlink */
__u32 mctime;
__u8 nsize;
__u8 type;
__u8 unused[2];
__u32 node_crc;
__u32 name_crc;
__u8 name[0];
uint16_t magic;
uint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */
uint32_t totlen;
uint32_t hdr_crc;
uint32_t pino;
uint32_t version;
uint32_t ino; /* == zero for unlink */
uint32_t mctime;
uint8_t nsize;
uint8_t type;
uint8_t unused[2];
uint32_t node_crc;
uint32_t name_crc;
uint8_t name[0];
} __attribute__((packed));
/* The JFFS2 raw inode structure: Used for storage on physical media. */
......@@ -131,28 +126,28 @@ struct jffs2_raw_dirent
*/
struct jffs2_raw_inode
{
__u16 magic; /* A constant magic number. */
__u16 nodetype; /* == JFFS_NODETYPE_INODE */
__u32 totlen; /* Total length of this node (inc data, etc.) */
__u32 hdr_crc;
__u32 ino; /* Inode number. */
__u32 version; /* Version number. */
__u32 mode; /* The file's type or mode. */
__u16 uid; /* The file's owner. */
__u16 gid; /* The file's group. */
__u32 isize; /* Total resultant size of this inode (used for truncations) */
__u32 atime; /* Last access time. */
__u32 mtime; /* Last modification time. */
__u32 ctime; /* Change time. */
__u32 offset; /* Where to begin to write. */
__u32 csize; /* (Compressed) data size */
__u32 dsize; /* Size of the node's data. (after decompression) */
__u8 compr; /* Compression algorithm used */
__u8 usercompr; /* Compression algorithm requested by the user */
__u16 flags; /* See JFFS2_INO_FLAG_* */
__u32 data_crc; /* CRC for the (compressed) data. */
__u32 node_crc; /* CRC for the raw inode (excluding data) */
// __u8 data[dsize];
uint16_t magic; /* A constant magic number. */
uint16_t nodetype; /* == JFFS_NODETYPE_INODE */
uint32_t totlen; /* Total length of this node (inc data, etc.) */
uint32_t hdr_crc;
uint32_t ino; /* Inode number. */
uint32_t version; /* Version number. */
uint32_t mode; /* The file's type or mode. */
uint16_t uid; /* The file's owner. */
uint16_t gid; /* The file's group. */
uint32_t isize; /* Total resultant size of this inode (used for truncations) */
uint32_t atime; /* Last access time. */
uint32_t mtime; /* Last modification time. */
uint32_t ctime; /* Change time. */
uint32_t offset; /* Where to begin to write. */
uint32_t csize; /* (Compressed) data size */
uint32_t dsize; /* Size of the node's data. (after decompression) */
uint8_t compr; /* Compression algorithm used */
uint8_t usercompr; /* Compression algorithm requested by the user */
uint16_t flags; /* See JFFS2_INO_FLAG_* */
uint32_t data_crc; /* CRC for the (compressed) data. */
uint32_t node_crc; /* CRC for the raw inode (excluding data) */
// uint8_t data[dsize];
} __attribute__((packed));
union jffs2_node_union {
......
/* $Id: jffs2_fs_i.h,v 1.8 2001/04/18 13:05:28 dwmw2 Exp $ */
/* $Id: jffs2_fs_i.h,v 1.12 2002/03/06 13:59:21 dwmw2 Exp $ */
#ifndef _JFFS2_FS_I
#define _JFFS2_FS_I
/* Include the pipe_inode_info at the beginning so that we can still
use the storage space in the inode when we have a pipe inode.
This sucks.
*/
#undef THISSUCKS /* Only for 2.2 */
#ifdef THISSUCKS
#include <linux/pipe_fs_i.h>
#endif
#include <linux/version.h>
struct jffs2_inode_info {
#ifdef THISSUCKS
struct pipe_inode_info pipecrap;
#endif
/* We need an internal semaphore similar to inode->i_sem.
Unfortunately, we can't used the existing one, because
either the GC would deadlock, or we'd have to release it
......@@ -26,7 +15,7 @@ struct jffs2_inode_info {
struct semaphore sem;
/* The highest (datanode) version number used for this ino */
__u32 highest_version;
uint32_t highest_version;
/* List of data fragments which make up the file */
struct jffs2_node_frag *fraglist;
......@@ -44,23 +33,11 @@ struct jffs2_inode_info {
/* Some stuff we just have to keep in-core at all times, for each inode. */
struct jffs2_inode_cache *inocache;
/* Keep a pointer to the last physical node in the list. We don't
use the doubly-linked lists because we don't want to increase
the memory usage that much. This is simpler */
// struct jffs2_raw_node_ref *lastnode;
__u16 flags;
__u8 usercompr;
uint16_t flags;
uint8_t usercompr;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
struct inode vfs_inode;
};
#ifdef JFFS2_OUT_OF_KERNEL
#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
#else
static inline struct jffs2_inode_info *JFFS2_INODE_INFO(struct inode *inode)
{
return list_entry(inode, struct jffs2_inode_info, vfs_inode);
}
#endif
};
#endif /* _JFFS2_FS_I */
/* $Id: jffs2_fs_sb.h,v 1.16.2.1 2002/02/23 14:13:34 dwmw2 Exp $ */
/* $Id: jffs2_fs_sb.h,v 1.25 2002/03/08 15:11:24 dwmw2 Exp $ */
#ifndef _JFFS2_FS_SB
#define _JFFS2_FS_SB
......@@ -9,7 +9,7 @@
#include <asm/semaphore.h>
#include <linux/list.h>
#define INOCACHE_HASHSIZE 1
#define INOCACHE_HASHSIZE 14
#define JFFS2_SB_FLAG_RO 1
#define JFFS2_SB_FLAG_MOUNTING 2
......@@ -21,36 +21,30 @@
struct jffs2_sb_info {
struct mtd_info *mtd;
__u32 highest_ino;
uint32_t highest_ino;
unsigned int flags;
spinlock_t nodelist_lock;
// pid_t thread_pid; /* GC thread's PID */
struct task_struct *gc_task; /* GC task struct */
struct semaphore gc_thread_start; /* GC thread start mutex */
struct completion gc_thread_exit; /* GC thread exit completion port */
// __u32 gc_minfree_threshold; /* GC trigger thresholds */
// __u32 gc_maxdirty_threshold;
struct semaphore alloc_sem; /* Used to protect all the following
fields, and also to protect against
out-of-order writing of nodes.
And GC.
*/
__u32 flash_size;
__u32 used_size;
__u32 dirty_size;
__u32 free_size;
__u32 erasing_size;
__u32 bad_size;
__u32 sector_size;
// __u32 min_free_size;
// __u32 max_chunk_size;
__u32 nr_free_blocks;
__u32 nr_erasing_blocks;
__u32 nr_blocks;
uint32_t flash_size;
uint32_t used_size;
uint32_t dirty_size;
uint32_t free_size;
uint32_t erasing_size;
uint32_t bad_size;
uint32_t sector_size;
uint32_t nr_free_blocks;
uint32_t nr_erasing_blocks;
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 */
......@@ -59,8 +53,10 @@ struct jffs2_sb_info {
struct list_head clean_list; /* Blocks 100% full of clean data */
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 */
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. */
......@@ -69,16 +65,22 @@ struct jffs2_sb_info {
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 */
struct jffs2_inode_cache *inocache_list[INOCACHE_HASHSIZE];
spinlock_t inocache_lock;
/* This _really_ speeds up mounts. */
struct jffs2_inode_cache *inocache_last;
/* 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;
/* Write-behind buffer for NAND flash */
unsigned char *wbuf;
uint32_t wbuf_ofs;
uint32_t wbuf_len;
uint32_t wbuf_pagesize;
};
#ifdef JFFS2_OUT_OF_KERNEL
#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
#else
#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
#endif
#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
#endif /* _JFFS2_FB_SB */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment