# HG changeset patch # User gthomas # Date 1044403240 0 # Node ID a7947d436e858ccbb4154a5ddb75479e1f51e1bd # Parent 0d98f58e80cbf87673d72fdf888d92bc7f660d99 Update to latest public JFFS2 sources diff --git a/packages/fs/jffs2/current/ChangeLog b/packages/fs/jffs2/current/ChangeLog --- a/packages/fs/jffs2/current/ChangeLog +++ b/packages/fs/jffs2/current/ChangeLog @@ -1,3 +1,41 @@ +2003-02-04 Gary Thomas on behalf of +2003-02-04 David Woodhouse + + * src/write.c: + * src/scan.c: + * src/readinode.c: + * src/read.c: + * src/pushpull.h: + * src/os-ecos.h: + * src/nodemgmt.c: + * src/nodelist.h: + * src/nodelist.c: + * src/malloc-ecos.c: + * src/jffs2port.h: + * src/gc.c: + * src/file-ecos.c: + * src/erase.c: + * src/dir-ecos.c: + * src/compr_zlib.c: + * src/compr_rubin.c: + * src/compr_rtime.c: + * src/compr.c: + * src/build.c: + * cdl/jffs2.cdl: Update to latest public JFFS2 codebase. + + * src/list.h: + * src/jffs2_fs_sb.h: + * src/jffs2_fs_i.h: + * src/jffs2.h: + * src/jffs2.c: + * src/crc32.h: + * src/background.c: Removed/renamed file(s). + + * src/fs-ecos.c: + * include/linux/jffs2_fs_sb.h: + * include/linux/jffs2_fs_i.h: + * include/linux/jffs2.h: New file(s). + 2002-12-06 Andrew Lunn * cdl/jffs2.cdl: Implements the CYGINT_IO_FILEIO_FS interface. diff --git a/packages/fs/jffs2/current/cdl/jffs2.cdl b/packages/fs/jffs2/current/cdl/jffs2.cdl --- a/packages/fs/jffs2/current/cdl/jffs2.cdl +++ b/packages/fs/jffs2/current/cdl/jffs2.cdl @@ -40,7 +40,7 @@ # ==================================================================== ######DESCRIPTIONBEGIN#### # -# Author(s): dominic ostrowski +# Author(s): David Woodhouse, Dominic Ostrowski # Original data: ported from JFFS2 by David Woodhouse # Contributors: dominic.ostrowski@3glab.com # Date: 2000-08-28 @@ -52,13 +52,12 @@ cdl_package CYGPKG_FS_JFFS2 { display "JFFS2 filesystem" doc doc/index.html - include_dir cyg/jffs2 + include_dir "" requires CYGPKG_IO_FILEIO requires CYGPKG_IO_FLASH requires CYGPKG_COMPRESS_ZLIB requires CYGINT_ISO_MALLOC - requires CYGPKG_CRC requires CYGPKG_ISOINFRA requires CYGPKG_ERROR @@ -66,10 +65,12 @@ cdl_package CYGPKG_FS_JFFS2 { requires CYGINT_ISO_ERRNO_CODES requires CYGPKG_IO_FLASH_BLOCK_DEVICE requires CYGPKG_IO_FILEIO_INODE + requires CYGPKG_LINUX_COMPAT + requires CYGPKG_CRC implements CYGINT_IO_FILEIO_FS - compile -library=libextras.a jffs2.c + compile -library=libextras.a fs-ecos.c compile build.c scan.c malloc-ecos.c nodelist.c nodemgmt.c readinode.c erase.c dir-ecos.c write.c gc.c read.c compr.c compr_zlib.c compr_rtime.c compr_rubin.c file-ecos.c cdl_option CYGPKG_FS_JFFS2_CFLAGS_ADD { @@ -78,7 +79,7 @@ cdl_package CYGPKG_FS_JFFS2 { no_define # We add -D__ECOS to trigger eCos-specific code in places. # We add -Werror because I find it useful. - default_value { "-D__ECOS -Werror" } + default_value { "-D__ECOS -nostdinc -iwithprefix include -Werror" } description " This option modifies the set of compiler flags for building the JFFS2 package. @@ -100,6 +101,31 @@ cdl_package CYGPKG_FS_JFFS2 { the set of global flags if present." } + cdl_option CYGNUM_FS_JFFS2_SECTOR_SIZE { + display "Physical sector size of the flash devices" + flavor data + default_value { 0x10000 } + description "64 KiB for synthetic target." + } + + cdl_option CYGNUM_FS_JFFS2_FLASH_SIZE { + display "Amount of flash committed to JFFS2" + flavor data + default_value { 0x100000 } + description "1 MiB (giving 16 sectors, realistic minimum!)" + } + + cdl_option CYGNUM_FS_JFFS2_BASE_ADDRESS { + display "Base of the JFFS2 filesystem in memory" + flavor data + default_value { 0x40000000 } + description "Set this value to the base address of the JFFS2 + filesystem in memory. By coincidence, this matches the + address at which the synthetic target will mmap its + backing store. Perhaps we should make it work through + something other than coincidence?" + } + # ---------------------------------------------------------------- # Tests diff --git a/packages/fs/jffs2/current/include/linux/jffs2.h b/packages/fs/jffs2/current/include/linux/jffs2.h new file mode 100644 --- /dev/null +++ b/packages/fs/jffs2/current/include/linux/jffs2.h @@ -0,0 +1,188 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001, 2002 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + * + * $Id: jffs2.h,v 1.29 2003/01/22 11:59:23 dwmw2 Exp $ + * + */ + +#ifndef __LINUX_JFFS2_H__ +#define __LINUX_JFFS2_H__ + +/* You must include something which defines the C99 uintXX_t types. + We don't do it from here because this file is used in too many + different environments. */ + +#define JFFS2_SUPER_MAGIC 0x72b6 + +/* Values we may expect to find in the 'magic' field */ +#define JFFS2_OLD_MAGIC_BITMASK 0x1984 +#define JFFS2_MAGIC_BITMASK 0x1985 +#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */ +#define JFFS2_EMPTY_BITMASK 0xffff +#define JFFS2_DIRTY_BITMASK 0x0000 + +/* We only allow a single char for length, and 0xFF is empty flash so + we don't want it confused with a real length. Hence max 254. +*/ +#define JFFS2_MAX_NAME_LEN 254 + +/* How small can we sensibly write nodes? */ +#define JFFS2_MIN_DATA_LEN 128 + +#define JFFS2_COMPR_NONE 0x00 +#define JFFS2_COMPR_ZERO 0x01 +#define JFFS2_COMPR_RTIME 0x02 +#define JFFS2_COMPR_RUBINMIPS 0x03 +#define JFFS2_COMPR_COPY 0x04 +#define JFFS2_COMPR_DYNRUBIN 0x05 +#define JFFS2_COMPR_ZLIB 0x06 +/* Compatibility flags. */ +#define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ +#define JFFS2_NODE_ACCURATE 0x2000 +/* INCOMPAT: Fail to mount the filesystem */ +#define JFFS2_FEATURE_INCOMPAT 0xc000 +/* ROCOMPAT: Mount read-only */ +#define JFFS2_FEATURE_ROCOMPAT 0x8000 +/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */ +#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000 +/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */ +#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000 + +#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) + + +#define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at + mount time, don't wait for it to + happen later */ +#define JFFS2_INO_FLAG_USERCOMPR 2 /* User has requested a specific + compression type */ + + +/* These can go once we've made sure we've caught all uses without + byteswapping */ + +typedef struct { + uint32_t v32; +} __attribute__((packed)) jint32_t; + +typedef struct { + uint32_t m; +} __attribute__((packed)) jmode_t; + +typedef struct { + uint16_t v16; +} __attribute__((packed)) jint16_t; + +#define JFFS2_NATIVE_ENDIAN + +/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from + whatever OS we're actually running on here too. */ + +#if defined(JFFS2_NATIVE_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){x}) +#define cpu_to_je32(x) ((jint32_t){x}) +#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)}) + +#define je16_to_cpu(x) ((x).v16) +#define je32_to_cpu(x) ((x).v32) +#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m)) +#elif defined(JFFS2_BIG_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)}) +#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)}) +#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))}) + +#define je16_to_cpu(x) (be16_to_cpu(x.v16)) +#define je32_to_cpu(x) (be32_to_cpu(x.v32)) +#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m))) +#elif defined(JFFS2_LITTLE_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)}) +#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)}) +#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))}) + +#define je16_to_cpu(x) (le16_to_cpu(x.v16)) +#define je32_to_cpu(x) (le32_to_cpu(x.v32)) +#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m))) +#else +#error wibble +#endif + +struct jffs2_unknown_node +{ + /* All start like this */ + jint16_t magic; + jint16_t nodetype; + jint32_t totlen; /* So we can skip over nodes we don't grok */ + jint32_t hdr_crc; +} __attribute__((packed)); + +struct jffs2_raw_dirent +{ + jint16_t magic; + jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t pino; + jint32_t version; + jint32_t ino; /* == zero for unlink */ + jint32_t mctime; + uint8_t nsize; + uint8_t type; + uint8_t unused[2]; + jint32_t node_crc; + jint32_t name_crc; + uint8_t name[0]; +} __attribute__((packed)); + +/* The JFFS2 raw inode structure: Used for storage on physical media. */ +/* The uid, gid, atime, mtime and ctime members could be longer, but + are left like this for space efficiency. If and when people decide + they really need them extended, it's simple enough to add support for + a new type of raw node. +*/ +struct jffs2_raw_inode +{ + jint16_t magic; /* A constant magic number. */ + jint16_t nodetype; /* == JFFS_NODETYPE_INODE */ + jint32_t totlen; /* Total length of this node (inc data, etc.) */ + jint32_t hdr_crc; + jint32_t ino; /* Inode number. */ + jint32_t version; /* Version number. */ + jmode_t mode; /* The file's type or mode. */ + jint16_t uid; /* The file's owner. */ + jint16_t gid; /* The file's group. */ + jint32_t isize; /* Total resultant size of this inode (used for truncations) */ + jint32_t atime; /* Last access time. */ + jint32_t mtime; /* Last modification time. */ + jint32_t ctime; /* Change time. */ + jint32_t offset; /* Where to begin to write. */ + jint32_t csize; /* (Compressed) data size */ + jint32_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 */ + jint16_t flags; /* See JFFS2_INO_FLAG_* */ + jint32_t data_crc; /* CRC for the (compressed) data. */ + jint32_t node_crc; /* CRC for the raw inode (excluding data) */ + uint8_t data[0]; +} __attribute__((packed)); + +union jffs2_node_union { + struct jffs2_raw_inode i; + struct jffs2_raw_dirent d; + struct jffs2_unknown_node u; +}; + +#endif /* __LINUX_JFFS2_H__ */ diff --git a/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h b/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h new file mode 100644 --- /dev/null +++ b/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h @@ -0,0 +1,46 @@ +/* $Id: jffs2_fs_i.h,v 1.16 2003/01/09 14:03:21 dwmw2 Exp $ */ + +#ifndef _JFFS2_FS_I +#define _JFFS2_FS_I + +#include +#include + +struct jffs2_inode_info { + /* 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 + before letting GC proceed. Or we'd have to put ugliness + into the GC code so it didn't attempt to obtain the i_sem + for the inode(s) which are already locked */ + struct semaphore sem; + + /* The highest (datanode) version number used for this ino */ + uint32_t highest_version; + + /* List of data fragments which make up the file */ + struct rb_root fragtree; + + /* There may be one datanode which isn't referenced by any of the + above fragments, if it contains a metadata update but no actual + data - or if this is a directory inode */ + /* This also holds the _only_ dnode for symlinks/device nodes, + etc. */ + struct jffs2_full_dnode *metadata; + + /* Directory entries */ + struct jffs2_full_dirent *dents; + + /* Some stuff we just have to keep in-core at all times, for each inode. */ + struct jffs2_inode_cache *inocache; + + uint16_t flags; + uint8_t usercompr; +#if !defined (__ECOS) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) + struct inode vfs_inode; +#endif +#endif +}; + +#endif /* _JFFS2_FS_I */ diff --git a/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h b/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h new file mode 100644 --- /dev/null +++ b/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h @@ -0,0 +1,99 @@ +/* $Id: jffs2_fs_sb.h,v 1.37 2003/01/17 16:04:44 dwmw2 Exp $ */ + +#ifndef _JFFS2_FS_SB +#define _JFFS2_FS_SB + +#include +#include +#include +#include +#include +#include +#include +#include + +#define JFFS2_SB_FLAG_RO 1 +#define JFFS2_SB_FLAG_MOUNTING 2 + +/* A struct for the overall file system control. Pointers to + jffs2_sb_info structs are named `c' in the source code. + Nee jffs_control +*/ +struct jffs2_sb_info { + struct mtd_info *mtd; + + uint32_t highest_ino; + uint32_t checked_ino; + + unsigned int flags; + + struct task_struct *gc_task; /* GC task struct */ + struct semaphore gc_thread_start; /* GC thread start mutex */ + struct completion gc_thread_exit; /* GC thread exit completion port */ + + struct semaphore alloc_sem; /* Used to protect all the following + fields, and also to protect against + out-of-order writing of nodes. + And GC. + */ + uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER + (i.e. zero for OOB CLEANMARKER */ + + uint32_t flash_size; + uint32_t used_size; + uint32_t dirty_size; + uint32_t wasted_size; + uint32_t free_size; + uint32_t erasing_size; + uint32_t bad_size; + uint32_t sector_size; + uint32_t unchecked_size; + + uint32_t nr_free_blocks; + uint32_t nr_erasing_blocks; + + uint32_t nr_blocks; + struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks + * from the offset (blocks[ofs / sector_size]) */ + struct jffs2_eraseblock *nextblock; /* The block we're currently filling */ + + struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */ + + struct list_head clean_list; /* Blocks 100% full of clean data */ + struct list_head very_dirty_list; /* Blocks with lots of dirty space */ + struct list_head dirty_list; /* Blocks with some dirty space */ + struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */ + struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */ + struct list_head erasing_list; /* Blocks which are currently erasing */ + struct list_head erase_pending_list; /* Blocks which need erasing now */ + struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */ + struct list_head free_list; /* Blocks which are free and ready to be used */ + struct list_head bad_list; /* Bad blocks. */ + struct list_head bad_used_list; /* Bad blocks with valid data in. */ + + spinlock_t erase_completion_lock; /* Protect free_list and erasing_list + against erase completion handler */ + wait_queue_head_t erase_wait; /* For waiting for erases to complete */ + + wait_queue_head_t inocache_wq; + struct jffs2_inode_cache **inocache_list; + spinlock_t inocache_lock; + + /* Sem to allow jffs2_garbage_collect_deletion_dirent to + drop the erase_completion_lock while it's holding a pointer + to an obsoleted node. I don't like this. Alternatives welcomed. */ + struct semaphore erase_free_sem; + + /* Write-behind buffer for NAND flash */ + unsigned char *wbuf; + uint32_t wbuf_ofs; + uint32_t wbuf_len; + uint32_t wbuf_pagesize; + struct work_struct wbuf_task; /* task for timed wbuf flush */ + struct timer_list wbuf_timer; /* timer for flushing wbuf */ + + /* OS-private pointer for getting back to master superblock info */ + void *os_priv; +}; + +#endif /* _JFFS2_FB_SB */ diff --git a/packages/fs/jffs2/current/src/background.c b/packages/fs/jffs2/current/src/background.c deleted file mode 100644 --- a/packages/fs/jffs2/current/src/background.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * JFFS2 -- Journalling Flash File System, Version 2. - * - * Copyright (C) 2001 Red Hat, Inc. - * - * Created by David Woodhouse - * - * For licensing information, see the file 'LICENCE' in this directory. - * - * $Id: background.c,v 1.16 2001/10/08 09:22:38 dwmw2 Exp $ - * - */ - -#define __KERNEL_SYSCALLS__ - -#include -#include -#include -#include -#include -#include -#include -#include "nodelist.h" - - -static int jffs2_garbage_collect_thread(void *); -static int thread_should_wake(struct jffs2_sb_info *c); - -void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) -{ - spin_lock_bh(&c->erase_completion_lock); - if (c->gc_task && thread_should_wake(c)) - send_sig(SIGHUP, c->gc_task, 1); - spin_unlock_bh(&c->erase_completion_lock); -} - -/* This must only ever be called when no GC thread is currently running */ -int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) -{ - pid_t pid; - int ret = 0; - - if (c->gc_task) - BUG(); - - init_MUTEX_LOCKED(&c->gc_thread_start); - init_completion(&c->gc_thread_exit); - - pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES); - if (pid < 0) { - printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %d\n", -pid); - complete(&c->gc_thread_exit); - ret = pid; - } else { - /* Wait for it... */ - D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid)); - down(&c->gc_thread_start); - } - - return ret; -} - -void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) -{ - spin_lock_bh(&c->erase_completion_lock); - if (c->gc_task) { - D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid)); - send_sig(SIGKILL, c->gc_task, 1); - } - spin_unlock_bh(&c->erase_completion_lock); - wait_for_completion(&c->gc_thread_exit); -} - -static int jffs2_garbage_collect_thread(void *_c) -{ - struct jffs2_sb_info *c = _c; - - daemonize(); - current->tty = NULL; - c->gc_task = current; - up(&c->gc_thread_start); - - sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index); - - /* FIXME in the 2.2 backport */ - current->nice = 10; - - for (;;) { - spin_lock_irq(¤t->sigmask_lock); - siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); - - if (!thread_should_wake(c)) { - set_current_state (TASK_INTERRUPTIBLE); - D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n")); - /* Yes, there's a race here; we checked thread_should_wake() before - setting current->state to TASK_INTERRUPTIBLE. But it doesn't - matter - We don't care if we miss a wakeup, because the GC thread - is only an optimisation anyway. */ - schedule(); - } - - if (current->need_resched) - schedule(); - - /* Put_super will send a SIGKILL and then wait on the sem. - */ - while (signal_pending(current)) { - siginfo_t info; - unsigned long signr; - - spin_lock_irq(¤t->sigmask_lock); - signr = dequeue_signal(¤t->blocked, &info); - spin_unlock_irq(¤t->sigmask_lock); - - switch(signr) { - case SIGSTOP: - D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n")); - set_current_state(TASK_STOPPED); - schedule(); - break; - - case SIGKILL: - D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n")); - spin_lock_bh(&c->erase_completion_lock); - c->gc_task = NULL; - spin_unlock_bh(&c->erase_completion_lock); - complete_and_exit(&c->gc_thread_exit, 0); - - case SIGHUP: - D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n")); - break; - default: - D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr)); - - } - } - /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ - spin_lock_irq(¤t->sigmask_lock); - siginitsetinv (¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); - - D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n")); - jffs2_garbage_collect_pass(c); - } -} - -static int thread_should_wake(struct jffs2_sb_info *c) -{ - D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x\n", - c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size)); - if (c->nr_free_blocks + c->nr_erasing_blocks < JFFS2_RESERVED_BLOCKS_GCTRIGGER && - c->dirty_size > c->sector_size) - return 1; - else - return 0; -} diff --git a/packages/fs/jffs2/current/src/build.c b/packages/fs/jffs2/current/src/build.c --- a/packages/fs/jffs2/current/src/build.c +++ b/packages/fs/jffs2/current/src/build.c @@ -7,14 +7,13 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.22 2002/01/09 16:30:57 dwmw2 Exp $ + * $Id: build.c,v 1.44 2003/01/17 16:04:16 dwmw2 Exp $ * */ -#ifndef __ECOS #include +#include #include -#endif #include "nodelist.h" int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *); @@ -36,13 +35,18 @@ static int jffs2_build_filesystem(struct /* 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; D1(printk(KERN_DEBUG "Scanned flash completely\n")); - /* Now build the data map for each inode, marking obsoleted nodes - as such, and also increase nlink of any children. */ + D1(jffs2_dump_block_lists(c)); + + /* Now scan the directory tree, increasing nlink according to every dirent found. */ for_each_inode(i, c, ic) { D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino)); ret = jffs2_build_inode_pass1(c, ic); @@ -50,8 +54,10 @@ static int jffs2_build_filesystem(struct D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret)); return ret; } + cond_resched(); } D1(printk(KERN_DEBUG "Pass 1 complete\n")); + D1(jffs2_dump_block_lists(c)); /* Next, scan for inodes with nlink == 0 and remove them. If they were directories, then decrement the nlink of their @@ -66,6 +72,8 @@ static int jffs2_build_filesystem(struct if (ic->nlink) continue; + /* XXX: Can get high latency here. Move the cond_resched() from the end of the loop? */ + ret = jffs2_build_remove_unlinked_inode(c, ic); if (ret) break; @@ -73,101 +81,52 @@ static int jffs2_build_filesystem(struct and furthermore that it had children and their nlink has now gone to zero too. So we have to restart the scan. */ } + D1(jffs2_dump_block_lists(c)); + + cond_resched(); + } while(ret == -EAGAIN); - + D1(printk(KERN_DEBUG "Pass 2 complete\n")); /* Finally, we can scan again and free the dirent nodes and scan_info structs */ for_each_inode(i, c, ic) { - struct jffs2_scan_info *scan = ic->scan; struct jffs2_full_dirent *fd; D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes)); - if (!scan) { - if (ic->nlink) { - D1(printk(KERN_WARNING "Why no scan struct for ino #%u which has nlink %d?\n", ic->ino, ic->nlink)); - } - continue; - } - ic->scan = NULL; - while(scan->dents) { - fd = scan->dents; - scan->dents = fd->next; + + while(ic->scan_dents) { + fd = ic->scan_dents; + ic->scan_dents = fd->next; jffs2_free_full_dirent(fd); } - kfree(scan); + ic->scan_dents = NULL; + cond_resched(); } D1(printk(KERN_DEBUG "Pass 3 complete\n")); + D1(jffs2_dump_block_lists(c)); + + /* Rotate the lists by some number to ensure wear levelling */ + jffs2_rotate_lists(c); return ret; } - + int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) { - struct jffs2_tmp_dnode_info *tn; struct jffs2_full_dirent *fd; - struct jffs2_node_frag *fraglist = NULL; - struct jffs2_tmp_dnode_info *metadata = NULL; D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino)); + if (ic->ino > c->highest_ino) c->highest_ino = ic->ino; - if (!ic->scan->tmpnodes && ic->ino != 1) { - D1(printk(KERN_DEBUG "jffs2_build_inode: ino #%u has no data nodes!\n", ic->ino)); - } - /* Build the list to make sure any obsolete nodes are marked as such */ - while(ic->scan->tmpnodes) { - tn = ic->scan->tmpnodes; - ic->scan->tmpnodes = tn->next; - - if (metadata && tn->version > metadata->version) { - D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring old metadata at 0x%08x\n", - metadata->fn->raw->flash_offset &~3)); - - jffs2_free_full_dnode(metadata->fn); - jffs2_free_tmp_dnode_info(metadata); - metadata = NULL; - } - - if (tn->fn->size) { - jffs2_add_full_dnode_to_fraglist (c, &fraglist, tn->fn); - jffs2_free_tmp_dnode_info(tn); - } else { - if (!metadata) { - metadata = tn; - } else { - D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring new metadata at 0x%08x\n", - tn->fn->raw->flash_offset &~3)); - - jffs2_free_full_dnode(tn->fn); - jffs2_free_tmp_dnode_info(tn); - } - } - } - - /* OK. Now clear up */ - if (metadata) { - jffs2_free_full_dnode(metadata->fn); - jffs2_free_tmp_dnode_info(metadata); - } - metadata = NULL; - - while (fraglist) { - struct jffs2_node_frag *frag; - frag = fraglist; - fraglist = fraglist->next; - - if (frag->node && !(--frag->node->frags)) { - jffs2_free_full_dnode(frag->node); - } - jffs2_free_node_frag(frag); - } - - /* Now for each child, increase nlink */ - for(fd=ic->scan->dents; fd; fd = fd->next) { + /* For each child, increase nlink */ + for(fd=ic->scan_dents; fd; fd = fd->next) { struct jffs2_inode_cache *child_ic; if (!fd->ino) continue; + + /* XXX: Can get high latency here with huge directories */ child_ic = jffs2_get_ino_cache(c, fd->ino); if (!child_ic) { @@ -178,6 +137,10 @@ int jffs2_build_inode_pass1(struct jffs2 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)); @@ -192,26 +155,33 @@ int jffs2_build_remove_unlinked_inode(st struct jffs2_full_dirent *fd; int ret = 0; - if(!ic->scan) { - D1(printk(KERN_DEBUG "ino #%u was already removed\n", ic->ino)); - return 0; - } - D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) { - D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", raw->flash_offset&~3)); + D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw))); jffs2_mark_node_obsolete(c, raw); } - if (ic->scan->dents) { - printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino); - - while(ic->scan->dents) { + if (ic->scan_dents) { + int whinged = 0; + D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino)); + + while(ic->scan_dents) { struct jffs2_inode_cache *child_ic; - fd = ic->scan->dents; - ic->scan->dents = fd->next; + fd = ic->scan_dents; + ic->scan_dents = fd->next; + + if (!fd->ino) { + /* It's a deletion dirent. Ignore it */ + D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name)); + jffs2_free_full_dirent(fd); + continue; + } + if (!whinged) { + whinged = 1; + printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino); + } D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n", fd->name, fd->ino)); @@ -219,6 +189,7 @@ int jffs2_build_remove_unlinked_inode(st child_ic = jffs2_get_ino_cache(c, fd->ino); if (!child_ic) { printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino); + jffs2_free_full_dirent(fd); continue; } jffs2_free_full_dirent(fd); @@ -226,8 +197,6 @@ int jffs2_build_remove_unlinked_inode(st } ret = -EAGAIN; } - kfree(ic->scan); - ic->scan = NULL; /* We don't delete the inocache from the hash list and free it yet. @@ -251,21 +220,27 @@ int jffs2_do_mount_fs(struct jffs2_sb_in c->blocks[i].offset = i * c->sector_size; c->blocks[i].free_size = c->sector_size; c->blocks[i].dirty_size = 0; + c->blocks[i].wasted_size = 0; + c->blocks[i].unchecked_size = 0; c->blocks[i].used_size = 0; c->blocks[i].first_node = NULL; c->blocks[i].last_node = NULL; } - spin_lock_init(&c->nodelist_lock); init_MUTEX(&c->alloc_sem); + init_MUTEX(&c->erase_free_sem); init_waitqueue_head(&c->erase_wait); + init_waitqueue_head(&c->inocache_wq); spin_lock_init(&c->erase_completion_lock); spin_lock_init(&c->inocache_lock); INIT_LIST_HEAD(&c->clean_list); + INIT_LIST_HEAD(&c->very_dirty_list); INIT_LIST_HEAD(&c->dirty_list); + INIT_LIST_HEAD(&c->erasable_list); INIT_LIST_HEAD(&c->erasing_list); INIT_LIST_HEAD(&c->erase_pending_list); + INIT_LIST_HEAD(&c->erasable_pending_wbuf_list); INIT_LIST_HEAD(&c->erase_complete_list); INIT_LIST_HEAD(&c->free_list); INIT_LIST_HEAD(&c->bad_list); diff --git a/packages/fs/jffs2/current/src/compr.c b/packages/fs/jffs2/current/src/compr.c --- a/packages/fs/jffs2/current/src/compr.c +++ b/packages/fs/jffs2/current/src/compr.c @@ -7,18 +7,16 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.23 2002/01/25 01:49:26 dwmw2 Exp $ + * $Id: compr.c,v 1.26 2003/01/12 13:21:28 dwmw2 Exp $ * */ -#ifdef __KERNEL__ +#if defined(__KERNEL__) || defined (__ECOS) #include #include #include -#include -#elif defined(__ECOS) -#include "jffs2.h" -#else /* Userland */ +#include +#else #define KERN_DEBUG #define KERN_NOTICE #define KERN_WARNING @@ -26,8 +24,9 @@ #include #include #include +#endif + #include -#endif 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); diff --git a/packages/fs/jffs2/current/src/compr_rtime.c b/packages/fs/jffs2/current/src/compr_rtime.c --- a/packages/fs/jffs2/current/src/compr_rtime.c +++ b/packages/fs/jffs2/current/src/compr_rtime.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rtime.c,v 1.8 2002/01/25 01:49:26 dwmw2 Exp $ + * $Id: compr_rtime.c,v 1.9 2002/05/20 14:56:37 dwmw2 Exp $ * * * Very simple lz77-ish encoder. @@ -21,14 +21,10 @@ * */ -#ifdef __ECOS -#include "jffs2port.h" -#else #include #include #include #include -#endif /* _compress returns the compressed size, -1 if bigger */ int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, diff --git a/packages/fs/jffs2/current/src/compr_rubin.c b/packages/fs/jffs2/current/src/compr_rubin.c --- a/packages/fs/jffs2/current/src/compr_rubin.c +++ b/packages/fs/jffs2/current/src/compr_rubin.c @@ -7,17 +7,13 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rubin.c,v 1.16 2002/01/25 01:49:26 dwmw2 Exp $ + * $Id: compr_rubin.c,v 1.17 2002/05/20 14:56:37 dwmw2 Exp $ * */ -#ifdef __ECOS -#include "jffs2port.h" -#else #include #include -#endif #include "compr_rubin.h" #include "histo_mips.h" diff --git a/packages/fs/jffs2/current/src/compr_zlib.c b/packages/fs/jffs2/current/src/compr_zlib.c --- a/packages/fs/jffs2/current/src/compr_zlib.c +++ b/packages/fs/jffs2/current/src/compr_zlib.c @@ -7,62 +7,23 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.12 2002/01/25 01:49:26 dwmw2 Exp $ + * $Id: compr_zlib.c,v 1.22 2003/01/12 13:21:28 dwmw2 Exp $ * */ -/* Be careful - there are three environments are supported by this file. - * - Linux kernel. - * - eCos kernel. - * - Userspace (mkfs.jffs2). - */ +#if !defined(__KERNEL__) && !defined(__ECOS) +#error "The userspace support got too messy and was removed. Update your mkfs.jffs2" +#endif -#ifdef __ECOS - /* Compiling in eCos (or RedBoot) */ -#include -#include "jffs2port.h" -#include "nodelist.h" - -#elif defined(__KERNEL__) - /* Compiling in Linux kernel */ #include #include #include /* for min() */ #include -#include +#include +#include +#include #include "nodelist.h" -static void *zalloc(void *opaque, unsigned nr, unsigned size) -{ - /* How much does it request? Should we use vmalloc? Or be dynamic? */ - return kmalloc(nr * size, GFP_KERNEL); -} - -static void zfree(void *opaque, void *addr) -{ - kfree(addr); -} -#ifdef CONFIG_ZLIB /* 2.5 kernels will have a shared zlib, at last. */ -#include -#else -#include "zlib.h" -#endif - -#else - /* Compiling in Linux(ish) userspace */ -#define min(x,y) ((x)<(y)?(x):(y)) -#ifndef D1 -#define D1(x) -#endif -#define KERN_DEBUG -#define KERN_NOTICE -#define KERN_WARNING -#define printk printf -#include -#include -#include -#endif - /* Plan: call deflate() with avail_in == *sourcelen, avail_out = *dstlen - 12 and flush == Z_FINISH. If it doesn't manage to finish, call it again with @@ -72,98 +33,145 @@ static void zfree(void *opaque, void *ad */ #define STREAM_END_SPACE 12 +static DECLARE_MUTEX(deflate_sem); +static DECLARE_MUTEX(inflate_sem); +static z_stream inf_strm, def_strm; + +#ifdef __KERNEL__ /* Linux-only */ +int __init jffs2_zlib_init(void) +{ + def_strm.workspace = vmalloc(zlib_deflate_workspacesize()); + if (!def_strm.workspace) { + printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize()); + return -ENOMEM; + } + D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize())); + inf_strm.workspace = vmalloc(zlib_inflate_workspacesize()); + if (!inf_strm.workspace) { + printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize()); + vfree(def_strm.workspace); + return -ENOMEM; + } + D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize())); + return 0; +} + +void jffs2_zlib_exit(void) +{ + vfree(def_strm.workspace); + vfree(inf_strm.workspace); +} +#endif /* __KERNEL__ */ + int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen) { - z_stream strm; int ret; if (*dstlen <= STREAM_END_SPACE) return -1; -#ifdef __KERNEL__ /* Linux needs own alloc/free funcs */ - strm.zalloc = zalloc; - strm.zfree = zfree; -#else /* eCos and userspace don't */ - strm.zalloc = (void *)0; - strm.zfree = (void *)0; -#endif + down(&deflate_sem); - if (Z_OK != deflateInit(&strm, 3)) { + if (Z_OK != zlib_deflateInit(&def_strm, 3)) { printk(KERN_WARNING "deflateInit failed\n"); + up(&deflate_sem); return -1; } - strm.next_in = data_in; - strm.total_in = 0; + + def_strm.next_in = data_in; + def_strm.total_in = 0; - strm.next_out = cpage_out; - strm.total_out = 0; + def_strm.next_out = cpage_out; + def_strm.total_out = 0; - while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) { - strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE); - strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out); + while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) { + def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE); + def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out); D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n", - strm.avail_in, strm.avail_out)); - ret = deflate(&strm, Z_PARTIAL_FLUSH); + def_strm.avail_in, def_strm.avail_out)); + ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH); D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", - strm.avail_in, strm.avail_out, strm.total_in, strm.total_out)); + def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out)); if (ret != Z_OK) { D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret)); - deflateEnd(&strm); + zlib_deflateEnd(&def_strm); + up(&deflate_sem); return -1; } } - strm.avail_out += STREAM_END_SPACE; - strm.avail_in = 0; - ret = deflate(&strm, Z_FINISH); + def_strm.avail_out += STREAM_END_SPACE; + def_strm.avail_in = 0; + ret = zlib_deflate(&def_strm, Z_FINISH); + zlib_deflateEnd(&def_strm); + if (ret != Z_STREAM_END) { D1(printk(KERN_DEBUG "final deflate returned %d\n", ret)); - deflateEnd(&strm); - return -1; + ret = -1; + goto out; } - deflateEnd(&strm); - - D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n", strm.total_in, strm.total_out)); - if (strm.total_out >= strm.total_in) - return -1; - + if (def_strm.total_out >= def_strm.total_in) { + D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n", + def_strm.total_in, def_strm.total_out)); + ret = -1; + goto out; + } - *dstlen = strm.total_out; - *sourcelen = strm.total_in; - return 0; + D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n", + def_strm.total_in, def_strm.total_out)); + + *dstlen = def_strm.total_out; + *sourcelen = def_strm.total_in; + ret = 0; + out: + up(&deflate_sem); + return ret; } void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen) { - z_stream strm; int ret; + int wbits = MAX_WBITS; + + down(&inflate_sem); + + inf_strm.next_in = data_in; + inf_strm.avail_in = srclen; + inf_strm.total_in = 0; + + inf_strm.next_out = cpage_out; + inf_strm.avail_out = destlen; + inf_strm.total_out = 0; -#ifdef __KERNEL__ - strm.zalloc = zalloc; - strm.zfree = zfree; -#else - strm.zalloc = (void *)0; - strm.zfree = (void *)0; -#endif + /* If it's deflate, and it's got no preset dictionary, then + we can tell zlib to skip the adler32 check. */ + if (srclen > 2 && !(data_in[1] & PRESET_DICT) && + ((data_in[0] & 0x0f) == Z_DEFLATED) && + !(((data_in[0]<<8) + data_in[1]) % 31)) { - if (Z_OK != inflateInit(&strm)) { + D2(printk(KERN_DEBUG "inflate skipping adler32\n")); + wbits = -((data_in[0] >> 4) + 8); + inf_strm.next_in += 2; + inf_strm.avail_in -= 2; + } else { + /* Let this remain D1 for now -- it should never happen */ + D1(printk(KERN_DEBUG "inflate not skipping adler32\n")); + } + + + if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) { printk(KERN_WARNING "inflateInit failed\n"); + up(&inflate_sem); return; } - strm.next_in = data_in; - strm.avail_in = srclen; - strm.total_in = 0; - - strm.next_out = cpage_out; - strm.avail_out = destlen; - strm.total_out = 0; - while((ret = inflate(&strm, Z_FINISH)) == Z_OK) + while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK) ; if (ret != Z_STREAM_END) { printk(KERN_NOTICE "inflate returned %d\n", ret); } - inflateEnd(&strm); + zlib_inflateEnd(&inf_strm); + up(&inflate_sem); } diff --git a/packages/fs/jffs2/current/src/crc32.h b/packages/fs/jffs2/current/src/crc32.h deleted file mode 100644 --- a/packages/fs/jffs2/current/src/crc32.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef CRC32_H -#define CRC32_H - -/* $Id: crc32.h,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */ - -#ifdef __ECOS -#include "jffs2port.h" -#else -#include -#endif - -extern const uint32_t crc32_table[256]; - -/* Return a 32-bit CRC of the contents of the buffer. */ - -static inline uint32_t -crc32(uint32_t val, const void *ss, int len) -{ - return cyg_crc32_accumulate(val,(void *)ss,len); -} - -#endif diff --git a/packages/fs/jffs2/current/src/dir-ecos.c b/packages/fs/jffs2/current/src/dir-ecos.c --- a/packages/fs/jffs2/current/src/dir-ecos.c +++ b/packages/fs/jffs2/current/src/dir-ecos.c @@ -7,13 +7,13 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * In sync with: - * $Id: dir.c,v 1.57 2002/01/28 22:25:07 dwmw2 Exp $ + * $Id: dir-ecos.c,v 1.3 2003/01/22 00:04:13 dwmw2 Exp $ * */ +#include +#include #include "nodelist.h" -#include "crc32.h" /***********************************************************************/ @@ -30,7 +30,7 @@ struct inode *jffs2_lookup(struct inode uint32_t ino = 0; struct inode *inode = NULL; - D1(printk(KERN_DEBUG "jffs2_lookup()\n")); + D1(printk("jffs2_lookup()\n")); dir_f = JFFS2_INODE_INFO(dir_i); c = JFFS2_SB_INFO(dir_i->i_sb); @@ -52,7 +52,7 @@ struct inode *jffs2_lookup(struct inode if (ino) { inode = iget(dir_i->i_sb, ino); if (!inode) { - printk(KERN_WARNING "iget() failed for ino #%u\n", ino); + printk("iget() failed for ino #%u\n", ino); return (ERR_PTR(-EIO)); } } @@ -123,7 +123,8 @@ int jffs2_unlink(struct inode *dir_i, st ret = jffs2_do_unlink(c, dir_f, d_name->name, d_name->len, dead_f); - d_inode->i_nlink = dead_f->inocache->nlink; + if (dead_f->inocache) + d_inode->i_nlink = dead_f->inocache->nlink; return ret; } /***********************************************************************/ @@ -193,8 +194,8 @@ int jffs2_mkdir (struct inode *dir_i, st f = JFFS2_INODE_INFO(inode); - ri->data_crc = 0; - ri->node_crc = crc32(0, ri, sizeof(*ri)-8); + ri->data_crc = cpu_to_je32(0); + ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen); @@ -240,19 +241,19 @@ int jffs2_mkdir (struct inode *dir_i, st 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->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); + rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); + rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); - rd->pino = dir_i->i_ino; - rd->version = ++dir_f->highest_version; - rd->ino = inode->i_ino; - rd->mctime = CURRENT_TIME; + rd->pino = cpu_to_je32(dir_i->i_ino); + rd->version = cpu_to_je32(++dir_f->highest_version); + rd->ino = cpu_to_je32(inode->i_ino); + rd->mctime = cpu_to_je32(cyg_timestamp()); rd->nsize = namelen; rd->type = DT_DIR; - rd->node_crc = crc32(0, rd, sizeof(*rd)-8); - rd->name_crc = crc32(0, d_name->name, namelen); + rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); + rd->name_crc = cpu_to_je32(crc32(0, d_name->name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, d_name->name, namelen, phys_ofs, &writtenlen); @@ -293,8 +294,39 @@ int jffs2_rename (struct inode *old_dir_ { int ret; struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); + struct jffs2_inode_info *victim_f = NULL; uint8_t type; +#if 0 /* FIXME -- this really doesn't belong in individual file systems. + The fileio code ought to do this for us, or at least part of it */ + if (new_dentry->d_inode) { + if (S_ISDIR(d_inode->i_mode) && + !S_ISDIR(new_dentry->d_inode->i_mode)) { + /* Cannot rename directory over non-directory */ + return -EINVAL; + } + + victim_f = JFFS2_INODE_INFO(new_dentry->d_inode); + + if (S_ISDIR(new_dentry->d_inode->i_mode)) { + struct jffs2_full_dirent *fd; + + if (!S_ISDIR(d_inode->i_mode)) { + /* Cannot rename non-directory over directory */ + return -EINVAL; + } + down(&victim_f->sem); + for (fd = victim_f->dents; fd; fd = fd->next) { + if (fd->ino) { + up(&victim_f->sem); + return -ENOTEMPTY; + } + } + up(&victim_f->sem); + } + } +#endif + /* 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 :) @@ -313,20 +345,30 @@ int jffs2_rename (struct inode *old_dir_ if (ret) return ret; + if (victim_f) { + /* There was a victim. Kill it off nicely */ + /* Don't oops if the victim was a dirent pointing to an + inode which didn't exist. */ + if (victim_f->inocache) { + down(&victim_f->sem); + victim_f->inocache->nlink--; + up(&victim_f->sem); + } + } + /* Unlink the original */ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), old_d_name->name, old_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(d_inode); down(&f->sem); - d_inode->i_nlink = f->inocache->nlink++; + if (f->inocache) + 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); + printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret); } return ret; } diff --git a/packages/fs/jffs2/current/src/erase.c b/packages/fs/jffs2/current/src/erase.c --- a/packages/fs/jffs2/current/src/erase.c +++ b/packages/fs/jffs2/current/src/erase.c @@ -7,18 +7,18 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.30 2002/01/09 13:25:57 dwmw2 Exp $ + * $Id: erase.c,v 1.49 2003/01/21 18:11:28 dwmw2 Exp $ * */ -#ifndef __ECOS #include #include #include -#include -#endif +#include +#include +#include +#include #include "nodelist.h" -#include "crc32.h" struct erase_priv_struct { struct jffs2_eraseblock *jeb; @@ -28,29 +28,31 @@ struct erase_priv_struct { #ifndef __ECOS static void jffs2_erase_callback(struct erase_info *); #endif +static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { int ret; #ifdef __ECOS - ret = jffs2_flash_erase(c, jeb); - if (!ret) { - jffs2_erase_succeeded(c, jeb); - return; - } + ret = jffs2_flash_erase(c, jeb); + if (!ret) { + jffs2_erase_succeeded(c, jeb); + return; + } #else /* Linux */ struct erase_info *instr; instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); if (!instr) { printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); list_del(&jeb->list); list_add(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); return; } @@ -65,20 +67,25 @@ void jffs2_erase_block(struct jffs2_sb_i ((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) return; kfree(instr); -#endif +#endif /* __ECOS */ + if (ret == -ENOMEM || ret == -EAGAIN) { /* Erase failed immediately. Refile it on the list */ D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); list_del(&jeb->list); list_add(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); return; } @@ -87,50 +94,62 @@ void jffs2_erase_block(struct jffs2_sb_i 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); - c->nr_erasing_blocks--; - c->bad_size += c->sector_size; - c->erasing_size -= c->sector_size; - spin_unlock_bh(&c->erase_completion_lock); - wake_up(&c->erase_wait); + /* Note: This is almost identical to jffs2_erase_failed() except + for the fact that we used spin_lock() not spin_lock(). If + we could use spin_lock() from a BH, we could merge them. + Or if we abandon the idea that MTD drivers may call the erase + callback from a BH, I suppose :) + */ + jffs2_erase_failed(c, jeb); } void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) { struct jffs2_eraseblock *jeb; - spin_lock_bh(&c->erase_completion_lock); - while (!list_empty(&c->erase_pending_list)) { + down(&c->erase_free_sem); + + spin_lock(&c->erase_completion_lock); - jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list); + while (!list_empty(&c->erase_complete_list) || + !list_empty(&c->erase_pending_list)) { - D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset)); + if (!list_empty(&c->erase_complete_list)) { + jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); + list_del(&jeb->list); + spin_unlock(&c->erase_completion_lock); + jffs2_mark_erased_block(c, jeb); - list_del(&jeb->list); - c->erasing_size += c->sector_size; - c->free_size -= jeb->free_size; - c->used_size -= jeb->used_size; - c->dirty_size -= jeb->dirty_size; - jeb->used_size = jeb->dirty_size = jeb->free_size = 0; - jffs2_free_all_node_refs(c, jeb); - list_add(&jeb->list, &c->erasing_list); - spin_unlock_bh(&c->erase_completion_lock); - - jffs2_erase_block(c, jeb); -#ifndef __ECOS + } else if (!list_empty(&c->erase_pending_list)) { + jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list); + D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset)); + list_del(&jeb->list); + c->erasing_size += c->sector_size; + c->free_size -= jeb->free_size; + c->used_size -= jeb->used_size; + c->dirty_size -= jeb->dirty_size; + jeb->used_size = jeb->dirty_size = jeb->free_size = 0; + jffs2_free_all_node_refs(c, jeb); + list_add(&jeb->list, &c->erasing_list); + spin_unlock(&c->erase_completion_lock); + + jffs2_erase_block(c, jeb); + + } else { + BUG(); + } + /* Be nice */ - if (current->need_resched) - schedule(); -#endif - spin_lock_bh(&c->erase_completion_lock); + cond_resched(); + spin_lock(&c->erase_completion_lock); } - spin_unlock_bh(&c->erase_completion_lock); + + spin_unlock(&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)); @@ -138,10 +157,11 @@ static void jffs2_erase_succeeded(struct list_del(&jeb->list); list_add_tail(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); + /* Ensure that kupdated calls us again to mark them clean */ + jffs2_erase_pending_trigger(c); } -#ifndef __ECOS -static inline void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +static 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; @@ -153,6 +173,7 @@ static inline void jffs2_erase_failed(st wake_up(&c->erase_wait); } +#ifndef __ECOS static void jffs2_erase_callback(struct erase_info *instr) { struct erase_priv_struct *priv = (void *)instr->priv; @@ -163,11 +184,9 @@ static void jffs2_erase_callback(struct } else { 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; kfree(instr); } -#endif +#endif /* !__ECOS */ /* Hmmm. Maybe we should accept the extra space it takes and make this a standard doubly-linked list? */ @@ -224,7 +243,7 @@ static inline void jffs2_remove_node_ref this = ic->nodes; while(this) { - printk( "0x%08x(%d)->", this->flash_offset & ~3, this->flash_offset &3); + printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this)); if (++i == 5) { printk("\n" KERN_DEBUG); i=0; @@ -264,113 +283,133 @@ void jffs2_erase_pending_trigger(struct OFNI_BS_2SFFJ(c)->s_dirt = 1; } -void jffs2_mark_erased_blocks(struct jffs2_sb_info *c) +static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, 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; size_t retlen; int ret; - marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4); - - spin_lock_bh(&c->erase_completion_lock); - while (!list_empty(&c->erase_complete_list)) { - jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); - 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"); - /* Come back later */ + /* Stick it back on the list from whence it came and come back later */ jffs2_erase_pending_trigger(c); + spin_lock(&c->erase_completion_lock); + list_add(&jeb->list, &c->erase_complete_list); + spin_unlock(&c->erase_completion_lock); return; } + } + ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!ebuf) { + printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset); + } else { + uint32_t ofs = jeb->offset; - ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!ebuf) { - printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset); - } else { - uint32_t ofs = jeb->offset; + D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset)); + while(ofs < jeb->offset + c->sector_size) { + uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); + int i; - D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset)); - while(ofs < jeb->offset + c->sector_size) { - uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); - int i; + ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); + if (ret) { + printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); + goto bad; + } + if (retlen != readlen) { + printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); + goto bad; + } + for (i=0; ierase_completion_lock); + c->erasing_size -= c->sector_size; + c->bad_size += c->sector_size; - ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); - if (ret < 0) { - printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); - goto bad; - } - if (retlen != readlen) { - printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen); - goto bad; + list_add_tail(&jeb->list, &c->bad_list); + c->nr_erasing_blocks--; + spin_unlock(&c->erase_completion_lock); + wake_up(&c->erase_wait); + return; } - for (i=0; ierase_completion_lock); - c->erasing_size -= c->sector_size; - c->bad_size += c->sector_size; + } + ofs += readlen; + cond_resched(); + } + kfree(ebuf); + } + + /* Write the erase complete marker */ + D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset)); + if (jffs2_cleanmarker_oob(c)) { - list_add_tail(&jeb->list, &c->bad_list); - c->nr_erasing_blocks--; - spin_unlock_bh(&c->erase_completion_lock); - wake_up(&c->erase_wait); - return; - } - } - ofs += readlen; - } - kfree(ebuf); - } - - /* Write the erase complete marker */ - D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset)); - ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker); + if (jffs2_write_nand_cleanmarker(c, jeb)) + goto bad2; + + jeb->first_node = jeb->last_node = NULL; + + jeb->free_size = c->sector_size; + jeb->used_size = 0; + jeb->dirty_size = 0; + jeb->wasted_size = 0; + } else { + struct jffs2_unknown_node marker = { + .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), + .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), + .totlen = cpu_to_je32(c->cleanmarker_size) + }; + + marker.hdr_crc = cpu_to_je32(crc32(0, &marker, je32_to_cpu(marker.totlen) - 4)); + + ret = jffs2_flash_write(c, jeb->offset, je32_to_cpu(marker.totlen), &retlen, (char *)&marker); if (ret) { printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", jeb->offset, ret); goto bad2; } - if (retlen != sizeof(marker)) { - printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %d\n", - jeb->offset, sizeof(marker), retlen); + if (retlen != je32_to_cpu(marker.totlen)) { + printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n", + jeb->offset, je32_to_cpu(marker.totlen), retlen); goto bad2; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; - marker_ref->flash_offset = jeb->offset; - marker_ref->totlen = PAD(sizeof(marker)); - + marker_ref->flash_offset = jeb->offset | REF_NORMAL; + marker_ref->totlen = PAD(je32_to_cpu(marker.totlen)); + jeb->first_node = jeb->last_node = marker_ref; - + jeb->free_size = c->sector_size - marker_ref->totlen; jeb->used_size = marker_ref->totlen; jeb->dirty_size = 0; + jeb->wasted_size = 0; + } - spin_lock_bh(&c->erase_completion_lock); - c->erasing_size -= c->sector_size; - c->free_size += jeb->free_size; - c->used_size += jeb->used_size; + spin_lock(&c->erase_completion_lock); + c->erasing_size -= c->sector_size; + c->free_size += jeb->free_size; + c->used_size += jeb->used_size; - ACCT_SANITY_CHECK(c,jeb); - ACCT_PARANOIA_CHECK(jeb); + ACCT_SANITY_CHECK(c,jeb); + D1(ACCT_PARANOIA_CHECK(jeb)); - list_add_tail(&jeb->list, &c->free_list); - c->nr_erasing_blocks--; - c->nr_free_blocks++; - wake_up(&c->erase_wait); - } - spin_unlock_bh(&c->erase_completion_lock); + list_add_tail(&jeb->list, &c->free_list); + c->nr_erasing_blocks--; + c->nr_free_blocks++; + spin_unlock(&c->erase_completion_lock); + wake_up(&c->erase_wait); } + diff --git a/packages/fs/jffs2/current/src/file-ecos.c b/packages/fs/jffs2/current/src/file-ecos.c --- a/packages/fs/jffs2/current/src/file-ecos.c +++ b/packages/fs/jffs2/current/src/file-ecos.c @@ -7,18 +7,14 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: file.c,v 1.63 2002/01/07 11:18:05 dwmw2 Exp $ + * $Id: file-ecos.c,v 1.5 2003/01/22 00:04:13 dwmw2 Exp $ * */ -//#include -//#include /* for min() */ -//#include -//#include -//#include -#include "jffs2.h" +#include +#include +#include #include "nodelist.h" -#include "crc32.h" int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) { @@ -80,8 +76,6 @@ int jffs2_prepare_write (struct inode *d 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) { @@ -95,30 +89,31 @@ int jffs2_prepare_write (struct inode *d (unsigned int)inode->i_size, pageofs)); ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL); - if (ret) { - up(&f->sem); + if (ret) return ret; - } + + down(&f->sem); memset(&ri, 0, sizeof(ri)); - ri.magic = JFFS2_MAGIC_BITMASK; - ri.nodetype = JFFS2_NODETYPE_INODE; - ri.totlen = sizeof(ri); - ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4); + ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + ri.totlen = cpu_to_je32(sizeof(ri)); + ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); - ri.ino = 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 = 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; - ri.csize = 0; + ri.ino = cpu_to_je32(f->inocache->ino); + ri.version = cpu_to_je32(++f->highest_version); + ri.mode = cpu_to_jemode(inode->i_mode); + ri.uid = cpu_to_je16(inode->i_uid); + ri.gid = cpu_to_je16(inode->i_gid); + + ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs)); + ri.atime = ri.ctime = ri.mtime = cpu_to_je32(cyg_timestamp()); + ri.offset = cpu_to_je32(inode->i_size); + ri.dsize = cpu_to_je32(pageofs - inode->i_size); + ri.csize = cpu_to_je32(0); ri.compr = JFFS2_COMPR_ZERO; - ri.node_crc = crc32(0, &ri, sizeof(ri)-8); - ri.data_crc = 0; + ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); + ri.data_crc = cpu_to_je32(0); fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, NULL); jffs2_complete_reservation(c); @@ -141,16 +136,18 @@ int jffs2_prepare_write (struct inode *d return ret; } inode->i_size = pageofs; + up(&f->sem); } - /* Read in the page if it wasn't already present */ + /* Read in the page if it wasn't already present, unless it's a whole page */ // eCos has no concept of uptodate and by default always reads pages afresh - if (!Page_Uptodate(pg) && (start || end < PAGE_SIZE)) + if (!Page_Uptodate(pg) && (start || end < PAGE_CACHE_SIZE)) { + down(&f->sem); ret = jffs2_do_readpage_nolock(inode, pg); - //D1(printk(KERN_DEBUG "end prepare_write(). nrpages %ld\n", inode->i_mapping->nrpages)); + up(&f->sem); + } D1(printk(KERN_DEBUG "end prepare_write()\n")); - up(&f->sem); return ret; } @@ -178,16 +175,16 @@ int jffs2_commit_write (struct inode *d_ } /* Set the fields that the generic jffs2_write_inode_range() code can't find */ - ri->ino = inode->i_ino; - ri->mode = inode->i_mode; - ri->uid = inode->i_uid; - ri->gid = inode->i_gid; - ri->isize = (uint32_t)inode->i_size; - ri->atime = ri->ctime = ri->mtime = CURRENT_TIME; + ri->ino = cpu_to_je32(inode->i_ino); + ri->mode = cpu_to_jemode(inode->i_mode); + ri->uid = cpu_to_je16(inode->i_uid); + ri->gid = cpu_to_je16(inode->i_gid); + ri->isize = cpu_to_je32((uint32_t)inode->i_size); + ri->atime = ri->ctime = ri->mtime = cpu_to_je32(cyg_timestamp()); - /* 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); + (pg->index << PAGE_CACHE_SHIFT) + start, + end - start, &writtenlen); if (ret) { /* There was an error writing. */ @@ -197,7 +194,7 @@ int jffs2_commit_write (struct inode *d_ 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_ctime = inode->i_mtime = ri->ctime; + inode->i_ctime = inode->i_mtime = je32_to_cpu(ri->ctime); } } diff --git a/packages/fs/jffs2/current/src/fs-ecos.c b/packages/fs/jffs2/current/src/fs-ecos.c new file mode 100644 --- /dev/null +++ b/packages/fs/jffs2/current/src/fs-ecos.c @@ -0,0 +1,2050 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001, 2002 Red Hat, Inc. + * + * Created by Dominic Ostrowski + * Contributors: David Woodhouse, Nick Garnett, Richard Panton. + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: fs-ecos.c,v 1.8 2003/01/21 18:13:01 dwmw2 Exp $ + * + */ + +#include +#include +#include +#include "jffs2port.h" +#include +#include +#include +#include +#include "nodelist.h" + +#include +#include +#include +#include +#include + +//========================================================================== +// Forward definitions + +// Filesystem operations +static int jffs2_mount(cyg_fstab_entry * fste, cyg_mtab_entry * mte); +static int jffs2_umount(cyg_mtab_entry * mte); +static int jffs2_open(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + int mode, cyg_file * fte); +static int jffs2_ops_unlink(cyg_mtab_entry * mte, cyg_dir dir, + const char *name); +static int jffs2_ops_mkdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name); +static int jffs2_ops_rmdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name); +static int jffs2_ops_rename(cyg_mtab_entry * mte, cyg_dir dir1, + const char *name1, cyg_dir dir2, const char *name2); +static int jffs2_ops_link(cyg_mtab_entry * mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2, int type); +static int jffs2_opendir(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + cyg_file * fte); +static int jffs2_chdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + cyg_dir * dir_out); +static int jffs2_stat(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + struct stat *buf); +static int jffs2_getinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + int key, void *buf, int len); +static int jffs2_setinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + int key, void *buf, int len); + +// File operations +static int jffs2_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int jffs2_fo_lseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence); +static int jffs2_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data); +static int jffs2_fo_fsync(struct CYG_FILE_TAG *fp, int mode); +static int jffs2_fo_close(struct CYG_FILE_TAG *fp); +static int jffs2_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf); +static int jffs2_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf, + int len); +static int jffs2_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf, + int len); + +// Directory operations +static int jffs2_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int jffs2_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence); + +//========================================================================== +// Filesystem table entries + +// ------------------------------------------------------------------------- +// Fstab entry. +// This defines the entry in the filesystem table. +// For simplicity we use _FILESYSTEM synchronization for all accesses since +// we should never block in any filesystem operations. + +FSTAB_ENTRY(jffs2_fste, "jffs2", 0, + CYG_SYNCMODE_FILE_FILESYSTEM | CYG_SYNCMODE_IO_FILESYSTEM, + jffs2_mount, + jffs2_umount, + jffs2_open, + jffs2_ops_unlink, + jffs2_ops_mkdir, + jffs2_ops_rmdir, + jffs2_ops_rename, + jffs2_ops_link, + jffs2_opendir, + jffs2_chdir, jffs2_stat, jffs2_getinfo, jffs2_setinfo); + +// ------------------------------------------------------------------------- +// File operations. +// This set of file operations are used for normal open files. + +static cyg_fileops jffs2_fileops = { + jffs2_fo_read, + jffs2_fo_write, + jffs2_fo_lseek, + jffs2_fo_ioctl, + cyg_fileio_seltrue, + jffs2_fo_fsync, + jffs2_fo_close, + jffs2_fo_fstat, + jffs2_fo_getinfo, + jffs2_fo_setinfo +}; + +// ------------------------------------------------------------------------- +// Directory file operations. +// This set of operations are used for open directories. Most entries +// point to error-returning stub functions. Only the read, lseek and +// close entries are functional. + +static cyg_fileops jffs2_dirops = { + jffs2_fo_dirread, + (cyg_fileop_write *) cyg_fileio_enosys, + jffs2_fo_dirlseek, + (cyg_fileop_ioctl *) cyg_fileio_enosys, + cyg_fileio_seltrue, + (cyg_fileop_fsync *) cyg_fileio_enosys, + jffs2_fo_close, + (cyg_fileop_fstat *) cyg_fileio_enosys, + (cyg_fileop_getinfo *) cyg_fileio_enosys, + (cyg_fileop_setinfo *) cyg_fileio_enosys +}; + +//========================================================================== +// STATIC VARIABLES !!! + +static char read_write_buffer[PAGE_CACHE_SIZE]; //avoids malloc when user may be under memory pressure +static char gc_buffer[PAGE_CACHE_SIZE]; //avoids malloc when user may be under memory pressure + +//========================================================================== +// Directory operations + +struct jffs2_dirsearch { + struct inode *dir; // directory to search + const char *path; // path to follow + struct inode *node; // Node found + const char *name; // last name fragment used + int namelen; // name fragment length + cyg_bool last; // last name in path? +}; + +typedef struct jffs2_dirsearch jffs2_dirsearch; + +//========================================================================== +// Ref count and nlink management + +// ------------------------------------------------------------------------- +// dec_refcnt() +// Decrment the reference count on an inode. If this makes the ref count +// zero, then this inode can be freed. + +static int dec_refcnt(struct inode *node) +{ + int err = ENOERR; + node->i_count--; + + // In JFFS2 inode's are temporary in ram structures that are free'd when the usage i_count drops to 0 + // The i_nlink however is managed by JFFS2 and is unrelated to usage + if (node->i_count == 0) { + // This inode is not in use, so delete it. + iput(node); + } + + return err; +} + +// FIXME: This seems like real cruft. Wouldn't it be better just to do the +// right thing? +static void icache_evict(struct inode *root_i, struct inode *i) +{ + struct inode *cached_inode; + struct inode *next_inode; + + D2(printf("icache_evict\n")); + // If this is an absolute search path from the root, + // remove all cached inodes with i_count of zero (these are only + // held where needed for dotdot filepaths) + if (i == root_i) { + for (cached_inode = root_i; cached_inode != NULL; + cached_inode = next_inode) { + next_inode = cached_inode->i_cache_next; + if (cached_inode->i_count == 0) { + cached_inode->i_cache_prev->i_cache_next = cached_inode->i_cache_next; // Prveious entry points ahead of us + if (cached_inode->i_cache_next != NULL) + cached_inode->i_cache_next->i_cache_prev = cached_inode->i_cache_prev; // Next entry points behind us + jffs2_clear_inode(cached_inode); + D2(printf + ("free icache_evict inode %x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n", + cached_inode)); + free(cached_inode); + } + } + } +} + +//========================================================================== +// Directory search + +// ------------------------------------------------------------------------- +// init_dirsearch() +// Initialize a dirsearch object to start a search + +static void init_dirsearch(jffs2_dirsearch * ds, + struct inode *dir, const char *name) +{ + D2(printf("init_dirsearch name = %s\n", name)); + D2(printf("init_dirsearch dir = %x\n", dir)); + ds->dir = dir; + ds->path = name; + ds->node = dir; + ds->name = name; + ds->namelen = 0; + ds->last = false; +} + +// ------------------------------------------------------------------------- +// find_entry() +// Search a single directory for the next name in a path and update the +// dirsearch object appropriately. + +static int find_entry(jffs2_dirsearch * ds) +{ + unsigned long hash; + struct qstr this; + unsigned int c; + const char *hashname; + + struct inode *dir = ds->dir; + const char *name = ds->path; + const char *n = name; + char namelen = 0; + struct inode *d; + + D2(printf("find_entry\n")); + + // check that we really have a directory + if (!S_ISDIR(dir->i_mode)) + return ENOTDIR; + + // Isolate the next element of the path name. + while (*n != '\0' && *n != '/') + n++, namelen++; + + // If we terminated on a NUL, set last flag. + if (*n == '\0') + ds->last = true; + + // update name in dirsearch object + ds->name = name; + ds->namelen = namelen; + + if (name[0] == '.') + switch (namelen) { + default: + break; + case 2: + // Dot followed by not Dot, treat as any other name + if (name[1] != '.') + break; + // Dot Dot + // Move back up the search path + D2(printf("find_entry found ..\n")); + ds->node = ds->dir->i_parent; + if (ds->dir->i_count == 0) { + iput(ds->dir); // This inode may be evicted + ds->dir = NULL; + } + return ENOERR; + case 1: + // Dot is consumed + D2(printf("find_entry found .\n")); + ds->node = ds->dir; + return ENOERR; + } + // Here we have the name and its length set up. + // Search the directory for a matching entry + + hashname = name; + this.name = hashname; + c = *(const unsigned char *) hashname; + + hash = init_name_hash(); + do { + hashname++; + hash = partial_name_hash(c, hash); + c = *(const unsigned char *) hashname; + } while (c && (c != '/')); + this.len = hashname - (const char *) this.name; + this.hash = end_name_hash(hash); + + D2(printf("find_entry for name = %s\n", ds->path)); + d = jffs2_lookup(dir, &this); + D2(printf("find_entry got dir = %x\n", d)); + + if (d == NULL) + return ENOENT; + + // The back path for dotdot to follow + d->i_parent = dir; + // pass back the node we have found + ds->node = d; + + return ENOERR; + +} + +// ------------------------------------------------------------------------- +// jffs2_find() +// Main interface to directory search code. This is used in all file +// level operations to locate the object named by the pathname. + +static int jffs2_find(jffs2_dirsearch * d) +{ + int err; + + D2(printf("jffs2_find for path =%s\n", d->path)); + // Short circuit empty paths + if (*(d->path) == '\0') + return ENOERR; + + // iterate down directory tree until we find the object + // we want. + for (;;) { + err = find_entry(d); + + if (err != ENOERR) + return err; + + if (d->last) + return ENOERR; + + // every inode traversed in the find is temporary and should be free'd + //iput(d->dir); + + // Update dirsearch object to search next directory. + d->dir = d->node; + d->path += d->namelen; + if (*(d->path) == '/') + d->path++; // skip dirname separators + } +} + +//========================================================================== +// Pathconf support +// This function provides support for pathconf() and fpathconf(). + +static int jffs2_pathconf(struct inode *node, struct cyg_pathconf_info *info) +{ + int err = ENOERR; + D2(printf("jffs2_pathconf\n")); + + switch (info->name) { + case _PC_LINK_MAX: + info->value = LINK_MAX; + break; + + case _PC_MAX_CANON: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_MAX_INPUT: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_NAME_MAX: + info->value = NAME_MAX; + break; + + case _PC_PATH_MAX: + info->value = PATH_MAX; + break; + + case _PC_PIPE_BUF: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_ASYNC_IO: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_CHOWN_RESTRICTED: + info->value = -1; // not supported + err = EINVAL; + break; + + case _PC_NO_TRUNC: + info->value = 0; + break; + + case _PC_PRIO_IO: + info->value = 0; + break; + + case _PC_SYNC_IO: + info->value = 0; + break; + + case _PC_VDISABLE: + info->value = -1; // not supported + err = EINVAL; + break; + + default: + err = EINVAL; + break; + } + + return err; +} + +//========================================================================== +// Filesystem operations + +// ------------------------------------------------------------------------- +// jffs2_mount() +// Process a mount request. This mainly creates a root for the +// filesystem. +static int jffs2_read_super(struct super_block *sb) +{ + struct jffs2_sb_info *c; + struct inode *root_i; + Cyg_ErrNo err; + cyg_uint32 len; + cyg_io_flash_getconfig_devsize_t ds; + cyg_io_flash_getconfig_blocksize_t bs; + + D1(printk(KERN_DEBUG "jffs2: read_super\n")); + + c = JFFS2_SB_INFO(sb); + + len = sizeof (ds); + err = cyg_io_get_config(sb->s_dev, + CYG_IO_GET_CONFIG_FLASH_DEVSIZE, &ds, &len); + if (err != ENOERR) { + D1(printf + ("jffs2: cyg_io_get_config failed to get dev size: %d\n", + err)); + return err; + } + len = sizeof (bs); + bs.offset = 0; + err = cyg_io_get_config(sb->s_dev, + CYG_IO_GET_CONFIG_FLASH_BLOCKSIZE, &bs, &len); + if (err != ENOERR) { + D1(printf + ("jffs2: cyg_io_get_config failed to get block size: %d\n", + err)); + return err; + } + + c->sector_size = bs.block_size; + c->flash_size = ds.dev_size; + c->cleanmarker_size = sizeof(struct jffs2_unknown_node); + + err = jffs2_do_mount_fs(c); + if (err) + return -err; + + 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")); + err = EIO; + 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) { + err = ENOMEM; + goto out_root_i; + } + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = JFFS2_SUPER_MAGIC; + + return 0; + + out_root_i: + iput(root_i); + out_nodes: + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + free(c->blocks); + + return err; +} + +static int jffs2_mount(cyg_fstab_entry * fste, cyg_mtab_entry * mte) +{ + extern cyg_mtab_entry mtab[], mtab_end; + struct super_block *jffs2_sb = NULL; + struct jffs2_sb_info *c; + cyg_mtab_entry *m; + cyg_io_handle_t t; + Cyg_ErrNo err; + + D2(printf("jffs2_mount\n")); + + err = cyg_io_lookup(mte->devname, &t); + if (err != ENOERR) + return -err; + + // Iterate through the mount table to see if we're mounted + // FIXME: this should be done better - perhaps if the superblock + // can be stored as an inode in the icache. + for (m = &mtab[0]; m != &mtab_end; m++) { + // stop if there are more than the configured maximum + if (m - &mtab[0] >= CYGNUM_FILEIO_MTAB_MAX) { + m = &mtab_end; + break; + } + if (m->valid && strcmp(m->fsname, "jffs2") == 0 && + strcmp(m->devname, mte->devname) == 0) { + jffs2_sb = (struct super_block *) m->data; + } + } + + if (jffs2_sb == NULL) { + jffs2_sb = malloc(sizeof (struct super_block)); + + if (jffs2_sb == NULL) + return ENOMEM; + + c = JFFS2_SB_INFO(jffs2_sb); + memset(jffs2_sb, 0, sizeof (struct super_block)); + jffs2_sb->s_dev = t; + + c->inocache_list = malloc(sizeof(struct jffs2_inode_cache *) * INOCACHE_HASHSIZE); + if (!c->inocache_list) { + free(jffs2_sb); + return ENOMEM; + } + memset(c->inocache_list, 0, sizeof(struct jffs2_inode_cache *) * INOCACHE_HASHSIZE); + + err = jffs2_read_super(jffs2_sb); + + if (err) { + free(jffs2_sb); + free(c->inocache_list); + return err; + } + + jffs2_sb->s_root->i_parent = jffs2_sb->s_root; // points to itself, no dotdot paths above mountpoint + jffs2_sb->s_root->i_cache_prev = NULL; // root inode, so always null + jffs2_sb->s_root->i_cache_next = NULL; + jffs2_sb->s_root->i_count = 1; // Ensures the root inode is always in ram until umount + + D2(printf("jffs2_mount erasing pending blocks\n")); + jffs2_erase_pending_blocks(c); + } + mte->data = (CYG_ADDRWORD) jffs2_sb; + + jffs2_sb->s_mount_count++; + mte->root = (cyg_dir) jffs2_sb->s_root; + D2(printf("jffs2_mounted superblock at %x\n", mte->root)); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_umount() +// Unmount the filesystem. + +static int jffs2_umount(cyg_mtab_entry * mte) +{ + struct inode *root = (struct inode *) mte->root; + struct super_block *jffs2_sb = root->i_sb; + struct jffs2_sb_info *c = JFFS2_SB_INFO(jffs2_sb); + + D2(printf("jffs2_umount\n")); + + // Decrement the mount count + jffs2_sb->s_mount_count--; + + // Only really umount if this is the only mount + if (jffs2_sb->s_mount_count == 0) { + + // Check for open/inuse root or any cached inodes +//if( root->i_count != 1 || root->i_cache_next != NULL) // root icount was set to 1 on mount + if (root->i_cache_next != NULL) // root icount was set to 1 on mount + return EBUSY; + + dec_refcnt(root); // Time to free the root inode + + //Clear root inode + //root_i = NULL; + + // Clean up the super block and root inode + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + free(c->blocks); + free(c->inocache_list); + free(jffs2_sb); + // Clear root pointer + mte->root = CYG_DIR_NULL; + mte->fs->data = 0; // fstab entry, visible to all mounts. No current mount + // That's all folks. + D2(printf("jffs2_umount No current mounts\n")); + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_open() +// Open a file for reading or writing. + +static int jffs2_open(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + int mode, cyg_file * file) +{ + + jffs2_dirsearch ds; + struct inode *node = NULL; + int err; + + D2(printf("jffs2_open\n")); + + icache_evict((struct inode *) mte->root, (struct inode *) dir); + + init_dirsearch(&ds, (struct inode *) dir, name); + + err = jffs2_find(&ds); + + if (err == ENOENT) { + if (ds.last && (mode & O_CREAT)) { + unsigned long hash; + struct qstr this; + unsigned int c; + const char *hashname; + + // No node there, if the O_CREAT bit is set then we must + // create a new one. The dir and name fields of the dirsearch + // object will have been updated so we know where to put it. + + hashname = ds.name; + this.name = hashname; + c = *(const unsigned char *) hashname; + + hash = init_name_hash(); + do { + hashname++; + hash = partial_name_hash(c, hash); + c = *(const unsigned char *) hashname; + } while (c && (c != '/')); + this.len = hashname - (const char *) this.name; + this.hash = end_name_hash(hash); + + err = jffs2_create(ds.dir, &this, S_IRUGO|S_IXUGO|S_IWUSR|S_IFREG, &node); + + if (err != 0) { + //Possible orphaned inode on the flash - but will be gc'd + return err; + } + + err = ENOERR; + } + } else if (err == ENOERR) { + // The node exists. If the O_CREAT and O_EXCL bits are set, we + // must fail the open. + + if ((mode & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + err = EEXIST; + else + node = ds.node; + } + + if (err == ENOERR && (mode & O_TRUNC)) { + struct jffs2_inode_info *f = JFFS2_INODE_INFO(node); + struct jffs2_sb_info *c = JFFS2_SB_INFO(node->i_sb); + // If the O_TRUNC bit is set we must clean out the file data. + + node->i_size = 0; + jffs2_truncate_fraglist(c, &f->fragtree, 0); + // Update file times + node->i_ctime = node->i_mtime = cyg_timestamp(); + } + + if (err != ENOERR) + return err; + + // Check that we actually have a file here + if (S_ISDIR(node->i_mode)) + return EISDIR; + + node->i_count++; // Count successful open + + // Initialize the file object + + file->f_flag |= mode & CYG_FILE_MODE_MASK; + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &jffs2_fileops; + file->f_offset = (mode & O_APPEND) ? node->i_size : 0; + file->f_data = (CYG_ADDRWORD) node; + file->f_xops = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_ops_unlink() +// Remove a file link from its directory. + +static int jffs2_ops_unlink(cyg_mtab_entry * mte, cyg_dir dir, const char *name) +{ + unsigned long hash; + struct qstr this; + unsigned int c; + const char *hashname; + jffs2_dirsearch ds; + int err; + + D2(printf("jffs2_ops_unlink\n")); + + icache_evict((struct inode *) mte->root, (struct inode *) dir); + + init_dirsearch(&ds, (struct inode *) dir, name); + + err = jffs2_find(&ds); + + if (err != ENOERR) + return err; + + // Cannot unlink directories, use rmdir() instead + if (S_ISDIR(ds.node->i_mode)) + return EPERM; + + // Delete it from its directory + + hashname = ds.name; + this.name = hashname; + c = *(const unsigned char *) hashname; + + hash = init_name_hash(); + do { + hashname++; + hash = partial_name_hash(c, hash); + c = *(const unsigned char *) hashname; + } while (c && (c != '/')); + this.len = hashname - (const char *) this.name; + this.hash = end_name_hash(hash); + + err = jffs2_unlink(ds.dir, ds.node, &this); + + return err; +} + +// ------------------------------------------------------------------------- +// jffs2_ops_mkdir() +// Create a new directory. + +static int jffs2_ops_mkdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name) +{ + jffs2_dirsearch ds; + struct inode *node = NULL; + int err; + + D2(printf("jffs2_ops_mkdir\n")); + + icache_evict((struct inode *) mte->root, (struct inode *) dir); + + init_dirsearch(&ds, (struct inode *) dir, name); + + err = jffs2_find(&ds); + + if (err == ENOENT) { + if (ds.last) { + unsigned long hash; + struct qstr this; + unsigned int c; + const char *hashname; + // The entry does not exist, and it is the last element in + // the pathname, so we can create it here. + + hashname = ds.name; + this.name = hashname; + c = *(const unsigned char *) hashname; + + hash = init_name_hash(); + do { + hashname++; + hash = partial_name_hash(c, hash); + c = *(const unsigned char *) hashname; + } while (c && (c != '/')); + this.len = hashname - (const char *) this.name; + this.hash = end_name_hash(hash); + + err = jffs2_mkdir(ds.dir, &this, 0, &node); + + if (err != 0) + return ENOSPC; + + } + // If this was not the last element, then and intermediate + // directory does not exist. + } else { + // If there we no error, something already exists with that + // name, so we cannot create another one. + + if (err == ENOERR) + err = EEXIST; + } + + return err; +} + +// ------------------------------------------------------------------------- +// jffs2_ops_rmdir() +// Remove a directory. + +static int jffs2_ops_rmdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name) +{ + unsigned long hash; + struct qstr this; + unsigned int c; + const char *hashname; + jffs2_dirsearch ds; + int err; + + D2(printf("jffs2_ops_rmdir\n")); + + icache_evict((struct inode *) mte->root, (struct inode *) dir); + + init_dirsearch(&ds, (struct inode *) dir, name); + + err = jffs2_find(&ds); + + if (err != ENOERR) + return err; + + // Check that this is actually a directory. + if (!S_ISDIR(ds.node->i_mode)) + return EPERM; + + // Delete the entry. + hashname = ds.name; + this.name = hashname; + c = *(const unsigned char *) hashname; + + hash = init_name_hash(); + do { + hashname++; + hash = partial_name_hash(c, hash); + c = *(const unsigned char *) hashname; + } while (c && (c != '/')); + this.len = hashname - (const char *) this.name; + this.hash = end_name_hash(hash); + + err = jffs2_rmdir(ds.dir, ds.node, &this); + + return err; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_ops_rename() +// Rename a file/dir. + +static int jffs2_ops_rename(cyg_mtab_entry * mte, cyg_dir dir1, + const char *name1, cyg_dir dir2, const char *name2) +{ + unsigned long hash; + struct qstr this1, this2; + unsigned int c; + const char *hashname; + jffs2_dirsearch ds1, ds2; + int err; + + D2(printf("jffs2_ops_rename\n")); + + init_dirsearch(&ds1, (struct inode *) dir1, name1); + + err = jffs2_find(&ds1); + + if (err != ENOERR) + return err; + + init_dirsearch(&ds2, (struct inode *) dir2, name2); + + err = jffs2_find(&ds2); + + // Allow through renames to non-existent objects. + if (ds2.last && err == ENOENT) + ds2.node = NULL, err = ENOERR; + + if (err != ENOERR) + return err; + + // Null rename, just return + if (ds1.node == ds2.node) + return ENOERR; + + hashname = ds1.name; + this1.name = hashname; + c = *(const unsigned char *) hashname; + + hash = init_name_hash(); + do { + hashname++; + hash = partial_name_hash(c, hash); + c = *(const unsigned char *) hashname; + } while (c && (c != '/')); + this1.len = hashname - (const char *) this1.name; + this1.hash = end_name_hash(hash); + + hashname = ds2.name; + this2.name = hashname; + c = *(const unsigned char *) hashname; + + hash = init_name_hash(); + do { + hashname++; + hash = partial_name_hash(c, hash); + c = *(const unsigned char *) hashname; + } while (c && (c != '/')); + this2.len = hashname - (const char *) this2.name; + this2.hash = end_name_hash(hash); + + // First deal with any entry that is at the destination + if (ds2.node) { + // Check that we are renaming like-for-like + + if (!S_ISDIR(ds1.node->i_mode) && S_ISDIR(ds2.node->i_mode)) + return EISDIR; + + if (S_ISDIR(ds1.node->i_mode) && !S_ISDIR(ds2.node->i_mode)) + return ENOTDIR; + + // Now delete the destination directory entry + + err = jffs2_unlink(ds2.dir, ds2.node, &this2); + + if (err != 0) + return err; + + } + // Now we know that there is no clashing node at the destination, + // make a new direntry at the destination and delete the old entry + // at the source. + + err = jffs2_rename(ds1.dir, ds1.node, &this1, ds2.dir, &this2); + + // Update directory times + if (err == 0) + ds1.dir->i_ctime = + ds1.dir->i_mtime = + ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp(); + + return err; +} + +// ------------------------------------------------------------------------- +// jffs2_ops_link() +// Make a new directory entry for a file. + +static int jffs2_ops_link(cyg_mtab_entry * mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2, int type) +{ + unsigned long hash; + struct qstr this; + unsigned int c; + const char *hashname; + jffs2_dirsearch ds1, ds2; + int err; + + D2(printf("jffs2_ops_link\n")); + + // Only do hard links for now in this filesystem + if (type != CYG_FSLINK_HARD) + return EINVAL; + + init_dirsearch(&ds1, (struct inode *) dir1, name1); + + err = jffs2_find(&ds1); + + if (err != ENOERR) + return err; + + init_dirsearch(&ds2, (struct inode *) dir2, name2); + + err = jffs2_find(&ds2); + + // Don't allow links to existing objects + if (err == ENOERR) + return EEXIST; + + // Allow through links to non-existing terminal objects + if (ds2.last && err == ENOENT) + ds2.node = NULL, err = ENOERR; + + if (err != ENOERR) + return err; + + // Now we know that there is no existing node at the destination, + // make a new direntry at the destination. + + hashname = ds2.name; + this.name = hashname; + c = *(const unsigned char *) hashname; + + hash = init_name_hash(); + do { + hashname++; + hash = partial_name_hash(c, hash); + c = *(const unsigned char *) hashname; + } while (c && (c != '/')); + this.len = hashname - (const char *) this.name; + this.hash = end_name_hash(hash); + + err = jffs2_link(ds2.dir, ds1.node, &this); + + if (err == 0) + ds1.node->i_ctime = + ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp(); + + return err; +} + +// ------------------------------------------------------------------------- +// jffs2_opendir() +// Open a directory for reading. + +static int jffs2_opendir(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + cyg_file * file) +{ + jffs2_dirsearch ds; + int err; + + D2(printf("jffs2_opendir\n")); + + icache_evict((struct inode *) mte->root, (struct inode *) dir); + + init_dirsearch(&ds, (struct inode *) dir, name); + + err = jffs2_find(&ds); + + if (err != ENOERR) + return err; + + // check it is really a directory. + if (!S_ISDIR(ds.node->i_mode)) + return ENOTDIR; + + ds.node->i_count++; // Count successful open + + // Initialize the file object, setting the f_ops field to a + // special set of file ops. + + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &jffs2_dirops; + file->f_offset = 0; + file->f_data = (CYG_ADDRWORD) ds.node; + file->f_xops = 0; + + return ENOERR; + +} + +// ------------------------------------------------------------------------- +// jffs2_chdir() +// Change directory support. + +static int jffs2_chdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + cyg_dir * dir_out) +{ + D2(printf("jffs2_chdir\n")); + + if (dir_out != NULL) { + // This is a request to get a new directory pointer in + // *dir_out. + + jffs2_dirsearch ds; + int err; + + icache_evict((struct inode *) mte->root, (struct inode *) dir); + + init_dirsearch(&ds, (struct inode *) dir, name); + + err = jffs2_find(&ds); + + if (err != ENOERR) + return err; + + // check it is a directory + if (!S_ISDIR(ds.node->i_mode)) + return ENOTDIR; + + // Increment ref count to keep this directory in existance + // while it is the current cdir. + ds.node->i_count++; + + // Pass it out + *dir_out = (cyg_dir) ds.node; + } else { + // If no output dir is required, this means that the mte and + // dir arguments are the current cdir setting and we should + // forget this fact. + + struct inode *node = (struct inode *) dir; + + // Just decrement directory reference count. + dec_refcnt(node); + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_stat() +// Get struct stat info for named object. + +static int jffs2_stat(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + struct stat *buf) +{ + jffs2_dirsearch ds; + int err; + + D2(printf("jffs2_stat\n")); + + icache_evict((struct inode *) mte->root, (struct inode *) dir); + + init_dirsearch(&ds, (struct inode *) dir, name); + + err = jffs2_find(&ds); + + if (err != ENOERR) + return err; + + // Fill in the status + buf->st_mode = ds.node->i_mode; + buf->st_ino = (ino_t) ds.node; + buf->st_dev = 0; + buf->st_nlink = ds.node->i_nlink; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = ds.node->i_size; + buf->st_atime = ds.node->i_atime; + buf->st_mtime = ds.node->i_mtime; + buf->st_ctime = ds.node->i_ctime; + + return err; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_getinfo() +// Getinfo. Currently only support pathconf(). + +static int jffs2_getinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + int key, void *buf, int len) +{ + jffs2_dirsearch ds; + int err; + + D2(printf("jffs2_getinfo\n")); + + icache_evict((struct inode *) mte->root, (struct inode *) dir); + + init_dirsearch(&ds, (struct inode *) dir, name); + + err = jffs2_find(&ds); + + if (err != ENOERR) + return err; + + switch (key) { + case FS_INFO_CONF: + err = jffs2_pathconf(ds.node, (struct cyg_pathconf_info *) buf); + break; + + default: + err = EINVAL; + } + return err; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_setinfo() +// Setinfo. Nothing to support here at present. + +static int jffs2_setinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name, + int key, void *buf, int len) +{ + // No setinfo keys supported at present + + D2(printf("jffs2_setinfo\n")); + + return EINVAL; +} + +//========================================================================== +// File operations + +// ------------------------------------------------------------------------- +// jffs2_fo_read() +// Read data from the file. + +static int jffs2_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + struct inode *inode = (struct inode *) fp->f_data; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + int i; + ssize_t resid = uio->uio_resid; + off_t pos = fp->f_offset; + + down(&f->sem); + + // Loop over the io vectors until there are none left + for (i = 0; i < uio->uio_iovcnt && pos < inode->i_size; i++) { + int ret; + cyg_iovec *iov = &uio->uio_iov[i]; + off_t len = min(iov->iov_len, inode->i_size - pos); + + D2(printf("jffs2_fo_read inode size %d\n", inode->i_size)); + + ret = + jffs2_read_inode_range(c, f, + (unsigned char *) iov->iov_base, pos, + len); + if (ret) { + D1(printf + ("jffs2_fo_read(): read_inode_range failed %d\n", + ret)); + uio->uio_resid = resid; + up(&f->sem); + return -ret; + } + resid -= len; + pos += len; + } + + // We successfully read some data, update the node's access time + // and update the file offset and transfer residue. + + inode->i_atime = cyg_timestamp(); + + uio->uio_resid = resid; + fp->f_offset = pos; + + up(&f->sem); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_fo_write() +// Write data to file. + +static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + struct page write_page; + off_t page_start_pos; + struct inode *node = (struct inode *) fp->f_data; + off_t pos = fp->f_offset; + ssize_t resid = uio->uio_resid; + int i; + + memset(&read_write_buffer, 0, PAGE_CACHE_SIZE); + write_page.virtual = &read_write_buffer; + + // If the APPEND mode bit was supplied, force all writes to + // the end of the file. + if (fp->f_flag & CYG_FAPPEND) + pos = fp->f_offset = node->i_size; + + // Check that pos is within current file size, or at the very end. + if (pos < 0 || pos > node->i_size) + return EINVAL; + + // Now loop over the iovecs until they are all done, or + // we get an error. + for (i = 0; i < uio->uio_iovcnt; i++) { + cyg_iovec *iov = &uio->uio_iov[i]; + char *buf = (char *) iov->iov_base; + off_t len = iov->iov_len; + + // loop over the vector writing it to the file until it has + // all been done. + while (len > 0) { + //cyg_uint8 *fbuf; + //size_t bsize; + size_t writtenlen; + off_t l = len; + int err; + + write_page.index = 0; + + page_start_pos = pos; + while (page_start_pos >= (PAGE_CACHE_SIZE)) { + write_page.index++; + page_start_pos -= PAGE_CACHE_SIZE; + } + + if (l > PAGE_CACHE_SIZE - page_start_pos) + l = PAGE_CACHE_SIZE - page_start_pos; + + D2(printf + ("jffs2_fo_write write_page.index %d\n", + write_page.index)); + D2(printf + ("jffs2_fo_write page_start_pos %d\n", + page_start_pos)); + D2(printf("jffs2_fo_write transfer size %d\n", l)); + + err = + jffs2_prepare_write(node, &write_page, + page_start_pos, + page_start_pos + l); + + if (err != 0) + return err; + + // copy data in + memcpy(&read_write_buffer[page_start_pos], buf, l); + + writtenlen = + jffs2_commit_write(node, &write_page, + page_start_pos, + page_start_pos + l); + + if (writtenlen != l) + return ENOSPC; + + // Update working vars + len -= l; + buf += l; + pos += l; + resid -= l; + } + } + + // We wrote some data successfully, update the modified and access + // times of the node, increase its size appropriately, and update + // the file offset and transfer residue. + node->i_mtime = node->i_ctime = cyg_timestamp(); + if (pos > node->i_size) + node->i_size = pos; + + uio->uio_resid = resid; + fp->f_offset = pos; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_fo_lseek() +// Seek to a new file position. + +static int jffs2_fo_lseek(struct CYG_FILE_TAG *fp, off_t * apos, int whence) +{ + struct inode *node = (struct inode *) fp->f_data; + off_t pos = *apos; + + D2(printf("jffs2_fo_lseek\n")); + + switch (whence) { + case SEEK_SET: + // Pos is already where we want to be. + break; + + case SEEK_CUR: + // Add pos to current offset. + pos += fp->f_offset; + break; + + case SEEK_END: + // Add pos to file size. + pos += node->i_size; + break; + + default: + return EINVAL; + } + + // Check that pos is still within current file size, or at the + // very end. + if (pos < 0 || pos > node->i_size) + return EINVAL; + + // All OK, set fp offset and return new position. + *apos = fp->f_offset = pos; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_fo_ioctl() +// Handle ioctls. Currently none are defined. + +static int jffs2_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data) +{ + // No Ioctls currenly defined. + + D2(printf("jffs2_fo_ioctl\n")); + + return EINVAL; +} + +// ------------------------------------------------------------------------- +// jffs2_fo_fsync(). +// Force the file out to data storage. + +static int jffs2_fo_fsync(struct CYG_FILE_TAG *fp, int mode) +{ + // Data is always permanently where it belongs, nothing to do + // here. + + D2(printf("jffs2_fo_fsync\n")); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_fo_close() +// Close a file. We just decrement the refcnt and let it go away if +// that is all that is keeping it here. + +static int jffs2_fo_close(struct CYG_FILE_TAG *fp) +{ + struct inode *node = (struct inode *) fp->f_data; + + D2(printf("jffs2_fo_close\n")); + + dec_refcnt(node); + + fp->f_data = 0; // zero data pointer + + return ENOERR; +} + +// ------------------------------------------------------------------------- +//jffs2_fo_fstat() +// Get file status. + +static int jffs2_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf) +{ + struct inode *node = (struct inode *) fp->f_data; + + D2(printf("jffs2_fo_fstat\n")); + + // Fill in the status + buf->st_mode = node->i_mode; + buf->st_ino = (ino_t) node; + buf->st_dev = 0; + buf->st_nlink = node->i_nlink; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = node->i_size; + buf->st_atime = node->i_atime; + buf->st_mtime = node->i_mtime; + buf->st_ctime = node->i_ctime; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_fo_getinfo() +// Get info. Currently only supports fpathconf(). + +static int jffs2_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf, + int len) +{ + struct inode *node = (struct inode *) fp->f_data; + int err; + + D2(printf("jffs2_fo_getinfo\n")); + + switch (key) { + case FS_INFO_CONF: + err = jffs2_pathconf(node, (struct cyg_pathconf_info *) buf); + break; + + default: + err = EINVAL; + } + return err; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_fo_setinfo() +// Set info. Nothing supported here. + +static int jffs2_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf, + int len) +{ + // No setinfo key supported at present + + D2(printf("jffs2_fo_setinfo\n")); + + return ENOERR; +} + +//========================================================================== +// Directory operations + +// ------------------------------------------------------------------------- +// jffs2_fo_dirread() +// Read a single directory entry from a file. + +static __inline void filldir(char *nbuf, int nlen, const char *name, int namlen) +{ + int len = nlen < namlen ? nlen : namlen; + memcpy(nbuf, name, len); + nbuf[len] = '\0'; +} + +static int jffs2_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + struct inode *d_inode = (struct inode *) fp->f_data; + struct dirent *ent = (struct dirent *) uio->uio_iov[0].iov_base; + char *nbuf = ent->d_name; + int nlen = sizeof (ent->d_name) - 1; + off_t len = uio->uio_iov[0].iov_len; + struct jffs2_inode_info *f; + struct jffs2_sb_info *c; + struct inode *inode = d_inode; + struct jffs2_full_dirent *fd; + unsigned long offset, curofs; + int found = 1; + + if (len < sizeof (struct dirent)) + return EINVAL; + + D1(printk + (KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", d_inode->i_ino)); + + f = JFFS2_INODE_INFO(inode); + c = JFFS2_SB_INFO(inode->i_sb); + + offset = fp->f_offset; + + if (offset == 0) { + D1(printk + (KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino)); + filldir(nbuf, nlen, ".", 1); + goto out; + } + if (offset == 1) { + filldir(nbuf, nlen, "..", 2); + goto out; + } + + curofs = 1; + down(&f->sem); + for (fd = f->dents; fd; fd = fd->next) { + + curofs++; + /* First loop: curofs = 2; offset = 2 */ + if (curofs < offset) { + D2(printk + (KERN_DEBUG + "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n", + fd->name, fd->ino, fd->type, curofs, offset)); + continue; + } + if (!fd->ino) { + D2(printk + (KERN_DEBUG "Skipping deletion dirent \"%s\"\n", + fd->name)); + offset++; + continue; + } + D2(printk + (KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset, + fd->name, fd->ino, fd->type)); + filldir(nbuf, nlen, fd->name, strlen(fd->name)); + goto out_sem; + } + /* Reached the end of the directory */ + found = 0; + out_sem: + up(&f->sem); + out: + fp->f_offset = ++offset; + if (found) { + uio->uio_resid -= sizeof (struct dirent); + } + return ENOERR; +} + +// ------------------------------------------------------------------------- +// jffs2_fo_dirlseek() +// Seek directory to start. + +static int jffs2_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence) +{ + // Only allow SEEK_SET to zero + + D2(printf("jffs2_fo_dirlseek\n")); + + if (whence != SEEK_SET || *pos != 0) + return EINVAL; + + *pos = fp->f_offset = 0; + + return ENOERR; +} + +//========================================================================== +// +// Called by JFFS2 +// =============== +// +// +//========================================================================== + +struct page *read_cache_page(unsigned long index, + int (*filler) (void *, struct page *), void *data) +{ + // Only called in gc.c jffs2_garbage_collect_dnode + // but gets a real page for the specified inode + + int err; + struct page *gc_page = malloc(sizeof (struct page)); + + printf("read_cache_page\n"); + memset(&gc_buffer, 0, PAGE_CACHE_SIZE); + + if (gc_page != NULL) { + gc_page->virtual = &gc_buffer; + gc_page->index = index; + + err = filler(data, gc_page); + if (err < 0) { + free(gc_page); + gc_page = NULL; + } + } + + return gc_page; +} + +void page_cache_release(struct page *pg) +{ + + // Only called in gc.c jffs2_garbage_collect_dnode + // but should free the page malloc'd by read_cache_page + + printf("page_cache_release\n"); + free(pg); +} + +struct inode *new_inode(struct super_block *sb) +{ + + // Only called in write.c jffs2_new_inode + // Always adds itself to inode cache + + struct inode *inode; + struct inode *cached_inode; + + inode = malloc(sizeof (struct inode)); + if (inode == NULL) + return 0; + + D2(printf + ("malloc new_inode %x ####################################\n", + inode)); + + memset(inode, 0, sizeof (struct inode)); + inode->i_sb = sb; + inode->i_ino = 1; + inode->i_count = 0; //1; // Let ecos manage the open count + + inode->i_nlink = 1; // Let JFFS2 manage the link count + inode->i_size = 0; + + inode->i_cache_next = NULL; // Newest inode, about to be cached + + // Add to the icache + for (cached_inode = sb->s_root; cached_inode != NULL; + cached_inode = cached_inode->i_cache_next) { + if (cached_inode->i_cache_next == NULL) { + cached_inode->i_cache_next = inode; // Current last in cache points to newcomer + inode->i_cache_prev = cached_inode; // Newcomer points back to last + break; + } + } + + return inode; +} + +struct inode *iget(struct super_block *sb, cyg_uint32 ino) +{ + + // Substitute for iget drops straight through to reading the + // inode from disk if it is not in the inode cache + + // Called in super.c jffs2_read_super, dir.c jffs2_lookup, + // and gc.c jffs2_garbage_collect_pass + + // Must first check for cached inode + // If this fails let new_inode create one + + struct inode *inode; + + D2(printf("iget\n")); + + // Check for this inode in the cache + for (inode = sb->s_root; inode != NULL; inode = inode->i_cache_next) { + if (inode->i_ino == ino) + return inode; + } + inode = NULL; + + // Not cached, so malloc it + inode = new_inode(sb); + if (inode == NULL) + return 0; + + inode->i_ino = ino; + jffs2_read_inode(inode); + + return inode; +} + +void iput(struct inode *i) +{ + + // Called in dec_refcnt, jffs2_find + // (and jffs2_open and jffs2_ops_mkdir?) + // super.c jffs2_read_super, + // and gc.c jffs2_garbage_collect_pass + + struct inode *cached_inode; + + D2(printf + ("free iput inode %x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n", i)); + if (i && i->i_count) { + /* Added by dwmw2. iget/iput in Linux track the use count, + don't just unconditionally free it */ + printf("iput called for used inode\n"); + return; + } + if (i != NULL) { + // Remove from the icache + for (cached_inode = i->i_sb->s_root; cached_inode != NULL; + cached_inode = cached_inode->i_cache_next) { + if (cached_inode == i) { + cached_inode->i_cache_prev->i_cache_next = cached_inode->i_cache_next; // Prveious entry points ahead of us + if (cached_inode->i_cache_next != NULL) + cached_inode->i_cache_next->i_cache_prev = cached_inode->i_cache_prev; // Next entry points behind us + break; + } + } + // inode has been seperated from the cache + jffs2_clear_inode(i); + free(i); + } +} + +static int return_EIO(void) +{ + return -EIO; +} + +#define EIO_ERROR ((void *) (return_EIO)) + +void make_bad_inode(struct inode *inode) +{ + + // In readinode.c JFFS2 checks whether the inode has appropriate + // content for its marked type + + D2(printf("make_bad_inode\n")); + + inode->i_mode = S_IFREG; + inode->i_atime = inode->i_mtime = inode->i_ctime = cyg_timestamp(); + inode->i_op = EIO_ERROR; + inode->i_fop = EIO_ERROR; +} + +int is_bad_inode(struct inode *inode) +{ + + // Called in super.c jffs2_read_super, + // and gc.c jffs2_garbage_collect_pass + + D2(printf("is_bad_inode\n")); + + return (inode->i_op == EIO_ERROR); + /*if(i == NULL) + return 1; + return 0; */ +} + +cyg_bool jffs2_flash_read(struct jffs2_sb_info * c, + cyg_uint32 read_buffer_offset, const size_t size, + size_t * return_size, char *write_buffer) +{ + Cyg_ErrNo err; + cyg_uint32 len = size; + struct super_block *sb = OFNI_BS_2SFFJ(c); + + //D2(printf("FLASH READ\n")); + //D2(printf("read address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + read_buffer_offset)); + //D2(printf("write address = %x\n", write_buffer)); + //D2(printf("size = %x\n", size)); + err = cyg_io_bread(sb->s_dev, write_buffer, &len, read_buffer_offset); + + *return_size = (size_t) len; + return (err != ENOERR); +} + +cyg_bool jffs2_flash_write(struct jffs2_sb_info * c, + cyg_uint32 write_buffer_offset, const size_t size, + size_t * return_size, char *read_buffer) +{ + + Cyg_ErrNo err; + cyg_uint32 len = size; + struct super_block *sb = OFNI_BS_2SFFJ(c); + + // D2(printf("FLASH WRITE ENABLED!!!\n")); + // D2(printf("write address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + write_buffer_offset)); + // D2(printf("read address = %x\n", read_buffer)); + // D2(printf("size = %x\n", size)); + + err = cyg_io_bwrite(sb->s_dev, read_buffer, &len, write_buffer_offset); + *return_size = (size_t) len; + + return (err != ENOERR); +} + +int +jffs2_flash_writev(struct jffs2_sb_info *c, 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++) { + // writes need to be aligned but the data we're passed may not be + // Observation suggests most unaligned writes are small, so we + // optimize for that case. + + if (((vecs[i].iov_len & (sizeof (int) - 1))) || + (((unsigned long) vecs[i]. + iov_base & (sizeof (unsigned long) - 1)))) { + // are there iov's after this one? Or is it so much we'd need + // to do multiple writes anyway? + if ((i + 1) < count || vecs[i].iov_len > 256) { + // cop out and malloc + unsigned long j; + ssize_t sizetomalloc = 0, totvecsize = 0; + char *cbuf, *cbufptr; + + for (j = i; j < count; j++) + totvecsize += vecs[j].iov_len; + + // pad up in case unaligned + sizetomalloc = totvecsize + sizeof (int) - 1; + sizetomalloc &= ~(sizeof (int) - 1); + cbuf = (char *) malloc(sizetomalloc); + // malloc returns aligned memory + if (!cbuf) { + ret = -ENOMEM; + goto writev_out; + } + cbufptr = cbuf; + for (j = i; j < count; j++) { + memcpy(cbufptr, vecs[j].iov_base, + vecs[j].iov_len); + cbufptr += vecs[j].iov_len; + } + ret = + jffs2_flash_write(c, to, sizetomalloc, + &thislen, cbuf); + if (thislen > totvecsize) // in case it was aligned up + thislen = totvecsize; + totlen += thislen; + free(cbuf); + goto writev_out; + } else { + // otherwise optimize for the common case + int buf[256 / sizeof (int)]; // int, so int aligned + size_t lentowrite; + + lentowrite = vecs[i].iov_len; + // pad up in case its unaligned + lentowrite += sizeof (int) - 1; + lentowrite &= ~(sizeof (int) - 1); + memcpy(buf, vecs[i].iov_base, lentowrite); + + ret = + jffs2_flash_write(c, to, lentowrite, + &thislen, (char *) &buf); + if (thislen > vecs[i].iov_len) + thislen = vecs[i].iov_len; + } // else + } else + ret = + jffs2_flash_write(c, 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; + } + writev_out: + if (retlen) + *retlen = totlen; + + return ret; +} + +cyg_bool jffs2_flash_erase(struct jffs2_sb_info * c, + struct jffs2_eraseblock * jeb) +{ + cyg_io_flash_getconfig_erase_t e; + void *err_addr; + Cyg_ErrNo err; + cyg_uint32 len = sizeof (e); + struct super_block *sb = OFNI_BS_2SFFJ(c); + + e.offset = jeb->offset; + e.len = c->sector_size; + e.err_address = &err_addr; + + // D2(printf("FLASH ERASE ENABLED!!!\n")); + // D2(printf("erase address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + jeb->offset)); + // D2(printf("size = %x\n", c->sector_size)); + + err = cyg_io_get_config(sb->s_dev, CYG_IO_GET_CONFIG_FLASH_ERASE, + &e, &len); + + return (err != ENOERR || e.flasherr != 0); +} + +// ------------------------------------------------------------------------- +// EOF jffs2.c +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); +} + + +/* 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 = ri->gid = cpu_to_je16(0); + ri->mode = cpu_to_jemode(mode); + ret = jffs2_do_new_inode (c, f, mode, ri); + if (ret) { + make_bad_inode(inode); + iput(inode); + return ERR_PTR(ret); + } + inode->i_nlink = 1; + inode->i_ino = je32_to_cpu(ri->ino); + inode->i_mode = jemode_to_cpu(ri->mode); + inode->i_gid = je16_to_cpu(ri->gid); + inode->i_uid = je16_to_cpu(ri->uid); + inode->i_atime = inode->i_ctime = inode->i_mtime = cyg_timestamp(); + ri->atime = ri->mtime = ri->ctime = cpu_to_je32(inode->i_mtime); + + inode->i_size = 0; + + insert_inode_hash(inode); + + return inode; +} + + +void jffs2_read_inode (struct inode *inode) +{ + struct jffs2_inode_info *f; + struct jffs2_sb_info *c; + struct jffs2_raw_inode latest_node; + int ret; + + D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); + + f = JFFS2_INODE_INFO(inode); + c = JFFS2_SB_INFO(inode->i_sb); + + jffs2_init_inode_info(f); + + ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); + + if (ret) { + make_bad_inode(inode); + up(&f->sem); + return; + } + inode->i_mode = jemode_to_cpu(latest_node.mode); + inode->i_uid = je16_to_cpu(latest_node.uid); + inode->i_gid = je16_to_cpu(latest_node.gid); + inode->i_size = je32_to_cpu(latest_node.isize); + inode->i_atime = je32_to_cpu(latest_node.atime); + inode->i_mtime = je32_to_cpu(latest_node.mtime); + inode->i_ctime = je32_to_cpu(latest_node.ctime); + + inode->i_nlink = f->inocache->nlink; + up(&f->sem); + + D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); +} diff --git a/packages/fs/jffs2/current/src/gc.c b/packages/fs/jffs2/current/src/gc.c --- a/packages/fs/jffs2/current/src/gc.c +++ b/packages/fs/jffs2/current/src/gc.c @@ -7,21 +7,22 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.60 2002/01/20 16:55:52 dwmw2 Exp $ + * $Id: gc.c,v 1.100 2003/01/22 14:43:51 dwmw2 Exp $ * */ -#ifndef __ECOS #include #include #include -#include -#include #include -#endif +#include +#include +#include #include "nodelist.h" -#include "crc32.h" +static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, + struct jffs2_inode_cache *ic, + struct jffs2_raw_node_ref *raw); static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 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, @@ -34,20 +35,33 @@ static int jffs2_garbage_collect_hole(st static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end); +static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic); /* 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. */ + /* We possibly want to favour the dirtier blocks more when the + number of free blocks is low. */ 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)) { - /* Most of the time, pick one off the dirty list */ + } else if (n < 50 && !list_empty(&c->erasable_list)) { + /* Note that most of them will have gone directly to be erased. + So don't favour the erasable_list _too_ much. */ + D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n")); + nextlist = &c->erasable_list; + } else if (n < 110 && !list_empty(&c->very_dirty_list)) { + /* Most of the time, pick one off the very_dirty list */ + D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n")); + nextlist = &c->very_dirty_list; + } else if (n < 126 && !list_empty(&c->dirty_list)) { D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n")); nextlist = &c->dirty_list; } else if (!list_empty(&c->clean_list)) { @@ -57,9 +71,16 @@ static struct jffs2_eraseblock *jffs2_fi 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->very_dirty_list)) { + D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n")); + nextlist = &c->very_dirty_list; + } else if (!list_empty(&c->erasable_list)) { + D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}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"); + /* Eep. All were empty */ + printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"); return NULL; } @@ -71,6 +92,17 @@ static struct jffs2_eraseblock *jffs2_fi printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset); BUG(); } + + /* Have we accidentally picked a clean block with wasted space ? */ + if (ret->wasted_size) { + D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size)); + ret->dirty_size += ret->wasted_size; + c->wasted_size -= ret->wasted_size; + c->dirty_size += ret->wasted_size; + ret->wasted_size = 0; + } + + D1(jffs2_dump_block_lists(c)); return ret; } @@ -80,21 +112,90 @@ static struct jffs2_eraseblock *jffs2_fi */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) { + struct jffs2_inode_cache *ic; struct jffs2_eraseblock *jeb; - struct jffs2_inode_info *f; struct jffs2_raw_node_ref *raw; - struct jffs2_node_frag *frag; - struct jffs2_full_dnode *fn = NULL; - struct jffs2_full_dirent *fd; - uint32_t start = 0, end = 0, nrfrags = 0; uint32_t inum; - struct inode *inode; int ret = 0; if (down_interruptible(&c->alloc_sem)) return -EINTR; - spin_lock_bh(&c->erase_completion_lock); + for (;;) { + struct jffs2_inode_cache *ic; + + spin_lock(&c->erase_completion_lock); + if (!c->unchecked_size) + break; + + /* We can't start doing GC yet. We haven't finished checking + the node CRCs etc. Do it now. */ + + /* checked_ino is protected by the alloc_sem */ + if (c->checked_ino > c->highest_ino) { + printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", + c->unchecked_size); + D1(jffs2_dump_block_lists(c)); + spin_unlock(&c->erase_completion_lock); + BUG(); + } + + spin_unlock(&c->erase_completion_lock); + + spin_lock(&c->inocache_lock); + + ic = jffs2_get_ino_cache(c, c->checked_ino++); + + if (!ic) { + spin_unlock(&c->inocache_lock); + continue; + } + + if (!ic->nlink) { + D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n", + ic->ino)); + spin_unlock(&c->inocache_lock); + continue; + } + switch(ic->state) { + case INO_STATE_CHECKEDABSENT: + case INO_STATE_PRESENT: + D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino)); + spin_unlock(&c->inocache_lock); + continue; + + case INO_STATE_GC: + case INO_STATE_CHECKING: + printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state); + spin_unlock(&c->inocache_lock); + BUG(); + + case INO_STATE_READING: + /* We need to wait for it to finish, lest we move on + and trigger the BUG() above while we haven't yet + finished checking all its nodes */ + D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino)); + up(&c->alloc_sem); + sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); + return 0; + + default: + BUG(); + + case INO_STATE_UNCHECKED: + ; + } + ic->state = INO_STATE_CHECKING; + spin_unlock(&c->inocache_lock); + + D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%d\n", ic->ino)); + + ret = jffs2_do_crccheck_inode(c, ic); + if (!ret) + jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); + up(&c->alloc_sem); + return ret; + } /* First, work out which block we're garbage-collecting */ jeb = c->gcblock; @@ -104,51 +205,181 @@ int jffs2_garbage_collect_pass(struct jf if (!jeb) { printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"); - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -EIO; } - D1(printk(KERN_DEBUG "garbage collect from block at phys 0x%08x\n", jeb->offset)); + D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size)); + D1(if (c->nextblock) + printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size)); - if (!jeb->used_size) + if (!jeb->used_size) { + up(&c->alloc_sem); goto eraseit; + } raw = jeb->gc_node; - while(raw->flash_offset & 1) { - D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", raw->flash_offset &~3)); + while(ref_obsolete(raw)) { + D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw))); jeb->gc_node = raw = raw->next_phys; if (!raw) { printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n"); printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size); - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); BUG(); } } - D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", raw->flash_offset &~3)); + D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw))); if (!raw->next_in_ino) { /* Inode-less node. Clean marker, snapshot or something like that */ - spin_unlock_bh(&c->erase_completion_lock); + /* 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(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, raw); + up(&c->alloc_sem); goto eraseit_lock; } inum = jffs2_raw_ref_to_inum(raw); D1(printk(KERN_DEBUG "Inode number is #%u\n", inum)); - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); + + D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), inum)); + + /* Three possibilities: + 1. Inode is already in-core. We must iget it and do proper + updating to its fragtree, etc. + 2. Inode is not in-core, node is REF_PRISTINE. We lock the + inocache to prevent a read_inode(), copy the node intact. + 3. Inode is not in-core, node is not pristine. We must iget() + and take the slow path. + */ + spin_lock(&c->inocache_lock); + ic = jffs2_get_ino_cache(c, inum); + + /* This should never fail unless I'm particularly stupid. + So we don't check before dereferencing it */ + + switch(ic->state) { + case INO_STATE_CHECKEDABSENT: + /* It's been checked, but it's not currently in-core. + We can just copy any pristine nodes, but have + to prevent anyone else from doing read_inode() while + we're at it, so we set the state accordingly */ + if (ref_flags(raw) == REF_PRISTINE) + ic->state = INO_STATE_GC; + else { + D1(printk("Ino #%u is absent but node not REF_PRISTINE. Reading.\n", + inum)); + } + break; - D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, raw->flash_offset&~3, inum)); + case INO_STATE_PRESENT: + case INO_STATE_UNCHECKED: + /* It's in-core or hasn't been checked. GC must iget() it. */ + break; + + case INO_STATE_CHECKING: + /* Should never happen. We should have finished checking + by the time we actually start doing any GC. */ + BUG(); + + + case INO_STATE_GC: + /* Should never happen. We are holding the alloc_sem, + no other garbage collection can happen. Note that we + do depend on this later when deciding to do a simple + node copy */ + BUG(); + + case INO_STATE_READING: + /* Someone's currently trying to read it. We must wait for + them to finish and then go through the full iget() route + to do the GC. However, sometimes read_inode() needs to get + the alloc_sem() (for marking nodes invalid) so we must + drop the alloc_sem before sleeping. */ + + up(&c->alloc_sem); + D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n", + inum, ic->state)); + sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); + /* And because we dropped the alloc_sem we must start again from the + beginning. Ponder chance of livelock here -- we're returning success + without actually making any progress. - inode = iget(OFNI_BS_2SFFJ(c), inum); + Q: What are the chances that the inode is back in INO_STATE_READING + again by the time we next enter this function? And that this happens + enough times to cause a real delay? + + A: Small enough that I don't care :) + */ + return 0; + + } + + spin_unlock(&c->inocache_lock); + + /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the + node intact, and we don't have to muck about with the fragtree etc. + because we know it's not in-core. If it _was_ in-core, we go through + all the iget() crap anyway */ + + if (ic->state == INO_STATE_GC) { + ret = jffs2_garbage_collect_pristine(c, ic, raw); + jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); + + if (ret != -EBADFD) + goto release_sem; + + /* Fall through if it wanted us to */ + } + + ret = jffs2_garbage_collect_live(c, jeb, raw, ic); + + release_sem: + up(&c->alloc_sem); + + eraseit_lock: + /* If we've finished this block, start it erasing */ + spin_lock(&c->erase_completion_lock); + + eraseit: + if (c->gcblock && !c->gcblock->used_size) { + D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset)); + /* We're GC'ing an empty block? */ + list_add_tail(&c->gcblock->list, &c->erase_pending_list); + c->gcblock = NULL; + c->nr_erasing_blocks++; + jffs2_erase_pending_trigger(c); + } + spin_unlock(&c->erase_completion_lock); + + return ret; +} + + +static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic) +{ + struct jffs2_inode_info *f; + struct jffs2_node_frag *frag; + struct jffs2_full_dnode *fn = NULL; + struct jffs2_full_dirent *fd; + uint32_t start = 0, end = 0, nrfrags = 0; + struct inode *inode; + int ret = 0; + + inode = iget(OFNI_BS_2SFFJ(c), ic->ino); if (is_bad_inode(inode)) { - printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", inum); + printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino); /* NB. This will happen again. We need to do something appropriate here. */ + up(&c->alloc_sem); iput(inode); - up(&c->alloc_sem); return -EIO; } @@ -158,7 +389,7 @@ int jffs2_garbage_collect_pass(struct jf /* Now we have the lock for this inode. Check that it's still the one at the head of the list. */ - if (raw->flash_offset & 1) { + if (ref_obsolete(raw)) { D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n")); /* They'll call again */ goto upnout; @@ -169,11 +400,36 @@ int jffs2_garbage_collect_pass(struct jf ret = jffs2_garbage_collect_metadata(c, jeb, f, fn); goto upnout; } - - for (frag = f->fraglist; frag; frag = frag->next) { + + /* FIXME. Read node and do lookup? */ + for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { if (frag->node && frag->node->raw == raw) { fn = frag->node; end = frag->ofs + frag->size; +#if 1 /* Temporary debugging sanity checks, till we're ready to _trust_ the REF_PRISTINE flag stuff */ + if (!nrfrags && ref_flags(fn->raw) == REF_PRISTINE) { + if (fn->frags > 1) { + printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(raw), fn->frags); + mark_ref_normal(raw); + } + /* A hole node which isn't multi-page should be garbage-collected + and merged anyway, so we just check for the frag size here, + rather than mucking around with actually reading the node + and checking the compression type, which is the real way + to tell a hole node. */ + if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE) { + printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n", + ref_offset(raw)); + mark_ref_normal(raw); + } + + if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE) { + printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n", + ref_offset(raw), frag->ofs, frag->ofs+frag->size); + mark_ref_normal(raw); + } + } +#endif if (!nrfrags++) start = frag->ofs; if (nrfrags == frag->node->frags) @@ -181,6 +437,15 @@ int jffs2_garbage_collect_pass(struct jf } } if (fn) { + if (ref_flags(raw) == REF_PRISTINE) { + ret = jffs2_garbage_collect_pristine(c, ic, raw); + if (!ret) { + /* Urgh. Return it sensibly. */ + frag->node->raw = ic->nodes; + } + if (ret != -EBADFD) + goto upnout; + } /* 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. */ @@ -204,8 +469,8 @@ int jffs2_garbage_collect_pass(struct jf 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 #%u\n", - raw->flash_offset&~3, f->inocache->ino); - if (raw->flash_offset & 1) { + ref_offset(raw), f->inocache->ino); + if (ref_obsolete(raw)) { printk(KERN_WARNING "But it's obsolete so we don't mind too much\n"); } else { ret = -EIO; @@ -215,23 +480,143 @@ int jffs2_garbage_collect_pass(struct jf up(&f->sem); iput(inode); - eraseit_lock: - /* If we've finished this block, start it erasing */ - spin_lock_bh(&c->erase_completion_lock); + return ret; +} + +static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, + struct jffs2_inode_cache *ic, + struct jffs2_raw_node_ref *raw) +{ + union jffs2_node_union *node; + struct jffs2_raw_node_ref *nraw; + size_t retlen; + int ret; + uint32_t phys_ofs, alloclen; + uint32_t crc; + + D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw))); + + /* Ask for a small amount of space (or the totlen if smaller) because we + don't want to force wastage of the end of a block if splitting would + work. */ + ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, raw->totlen), + &phys_ofs, &alloclen); + if (ret) + return ret; + + if (alloclen < raw->totlen) { + /* Doesn't fit untouched. We'll go the old route and split it */ + return -EBADFD; + } + + node = kmalloc(raw->totlen, GFP_KERNEL); + if (!node) + return -ENOMEM; + + ret = jffs2_flash_read(c, ref_offset(raw), raw->totlen, &retlen, (char *)node); + if (!ret && retlen != raw->totlen) + ret = -EIO; + if (ret) + goto out_node; + + crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4); + if (je32_to_cpu(node->u.hdr_crc) != crc) { + printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc); + goto bail; + } + + switch(je16_to_cpu(node->u.nodetype)) { + case JFFS2_NODETYPE_INODE: + crc = crc32(0, node, sizeof(node->i)-8); + if (je32_to_cpu(node->i.node_crc) != crc) { + printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ref_offset(raw), je32_to_cpu(node->i.node_crc), crc); + goto bail; + } + + if (je32_to_cpu(node->i.dsize)) { + crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize)); + if (je32_to_cpu(node->i.data_crc) != crc) { + printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ref_offset(raw), je32_to_cpu(node->i.data_crc), crc); + goto bail; + } + } + break; - eraseit: - if (c->gcblock && !c->gcblock->used_size) { - D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset)); - /* We're GC'ing an empty block? */ - list_add_tail(&c->gcblock->list, &c->erase_pending_list); - c->gcblock = NULL; - c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + case JFFS2_NODETYPE_DIRENT: + crc = crc32(0, node, sizeof(node->d)-8); + if (je32_to_cpu(node->d.node_crc) != crc) { + printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ref_offset(raw), je32_to_cpu(node->d.node_crc), crc); + goto bail; + } + + if (node->d.nsize) { + crc = crc32(0, node->d.name, node->d.nsize); + if (je32_to_cpu(node->d.name_crc) != crc) { + printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ref_offset(raw), je32_to_cpu(node->d.name_crc), crc); + goto bail; + } + } + break; + default: + printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n", + ref_offset(raw), je16_to_cpu(node->u.nodetype)); + goto bail; + } + + nraw = jffs2_alloc_raw_node_ref(); + if (!nraw) { + ret = -ENOMEM; + goto out_node; } - spin_unlock_bh(&c->erase_completion_lock); - up(&c->alloc_sem); + nraw->flash_offset = phys_ofs; + nraw->totlen = raw->totlen; + nraw->next_phys = NULL; + + /* OK, all the CRCs are good; this node can just be copied as-is. */ + + ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node); + if (ret || (retlen != raw->totlen)) { + printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", + raw->totlen, phys_ofs, ret, retlen); + if (retlen) { + /* Doesn't belong to any inode */ + nraw->next_in_ino = NULL; + nraw->flash_offset |= REF_OBSOLETE; + jffs2_add_physical_node_ref(c, nraw); + jffs2_mark_node_obsolete(c, nraw); + } else { + printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset); + jffs2_free_raw_node_ref(raw); + } + if (!ret) + ret = -EIO; + goto out_node; + } + nraw->flash_offset |= REF_PRISTINE; + jffs2_add_physical_node_ref(c, nraw); + + /* Link into per-inode list. This is safe because of the ic + state being INO_STATE_GC. Note that if we're doing this + for an inode which is in-code, the 'nraw' pointer is then + going to be fetched from ic->nodes by our caller. */ + nraw->next_in_ino = ic->nodes; + ic->nodes = nraw; + + jffs2_mark_node_obsolete(c, raw); + D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw))); + + out_node: + kfree(node); return ret; + bail: + ret = -EBADFD; + goto out_node; } static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, @@ -244,8 +629,8 @@ static int jffs2_garbage_collect_metadat uint32_t alloclen, phys_ofs; int ret; - if ((JFFS2_F_I_MODE(f) & S_IFMT) == S_IFBLK || - (JFFS2_F_I_MODE(f) & S_IFMT) == S_IFCHR) { + 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 */ /* FIXME: for minor or major > 255. */ dev = ((JFFS2_F_I_RDEV_MAJ(f) << 8) | @@ -253,7 +638,7 @@ static int jffs2_garbage_collect_metadat mdata = (char *)&dev; mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen)); - } else if ((JFFS2_F_I_MODE(f) & S_IFMT) == S_IFLNK) { + } else if (S_ISLNK(JFFS2_F_I_MODE(f))) { mdatalen = fn->size; mdata = kmalloc(fn->size, GFP_KERNEL); if (!mdata) { @@ -272,32 +657,32 @@ static int jffs2_garbage_collect_metadat ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen); if (ret) { - printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_metadata failed: %d\n", + printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n", sizeof(ri)+ mdatalen, ret); goto out; } memset(&ri, 0, sizeof(ri)); - ri.magic = JFFS2_MAGIC_BITMASK; - ri.nodetype = JFFS2_NODETYPE_INODE; - ri.totlen = sizeof(ri) + mdatalen; - ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4); + ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + ri.totlen = cpu_to_je32(sizeof(ri) + mdatalen); + ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); - ri.ino = f->inocache->ino; - ri.version = ++f->highest_version; - 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; + ri.ino = cpu_to_je32(f->inocache->ino); + ri.version = cpu_to_je32(++f->highest_version); + ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); + ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); + ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); + ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); + ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); + ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); + ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); + ri.offset = cpu_to_je32(0); + ri.csize = cpu_to_je32(mdatalen); + ri.dsize = cpu_to_je32(mdatalen); ri.compr = JFFS2_COMPR_NONE; - ri.node_crc = crc32(0, &ri, sizeof(ri)-8); - ri.data_crc = crc32(0, mdata, mdatalen); + ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); + ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, NULL); @@ -310,7 +695,7 @@ static int jffs2_garbage_collect_metadat jffs2_free_full_dnode(fn); f->metadata = new_fn; out: - if ((JFFS2_F_I_MODE(f) & S_IFMT) == S_IFLNK) + if (S_ISLNK(JFFS2_F_I_MODE(f))) kfree(mdata); return ret; } @@ -323,23 +708,23 @@ static int jffs2_garbage_collect_dirent( uint32_t alloclen, phys_ofs; int ret; - rd.magic = JFFS2_MAGIC_BITMASK; - rd.nodetype = JFFS2_NODETYPE_DIRENT; + rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd.nsize = strlen(fd->name); - rd.totlen = sizeof(rd) + rd.nsize; - rd.hdr_crc = crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4); + rd.totlen = cpu_to_je32(sizeof(rd) + rd.nsize); + rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4)); - rd.pino = f->inocache->ino; - rd.version = ++f->highest_version; - rd.ino = fd->ino; - rd.mctime = max(JFFS2_F_I_MTIME(f), JFFS2_F_I_CTIME(f)); + rd.pino = cpu_to_je32(f->inocache->ino); + rd.version = cpu_to_je32(++f->highest_version); + rd.ino = cpu_to_je32(fd->ino); + rd.mctime = cpu_to_je32(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); + rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8)); + rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize)); ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen); if (ret) { - printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dirent failed: %d\n", + printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n", sizeof(rd)+rd.nsize, ret); return ret; } @@ -373,9 +758,14 @@ static int jffs2_garbage_collect_deletio 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)) + if (!(ref_obsolete(raw))) continue; /* Doesn't matter if there's one in the same erase block. We're going to @@ -385,58 +775,60 @@ static int jffs2_garbage_collect_deletio 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); + ret = jffs2_flash_read(c, ref_offset(raw), 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 */ + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading header from obsolete node at %08x\n", ret, ref_offset(raw)); + /* If we can't read it, we don't need to continue 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); + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n", + retlen, sizeof(struct jffs2_unknown_node), ref_offset(raw)); continue; } - if (rd.nodetype != JFFS2_NODETYPE_DIRENT || - PAD(rd.totlen) != PAD(sizeof(rd) + name_len)) + if (je16_to_cpu(rd.nodetype) != JFFS2_NODETYPE_DIRENT || + PAD(je32_to_cpu(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); + ret = jffs2_flash_read(c, ref_offset(raw), 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); + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading from obsolete node at %08x\n", ret, ref_offset(raw)); /* 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); + if (retlen != sizeof(rd)) { + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading from obsolete node at %08x\n", + retlen, sizeof(rd), ref_offset(raw)); continue; } /* If the name CRC doesn't match, skip */ - if (rd.name_crc != name_crc) + if (je32_to_cpu(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) + if (rd.nsize != name_len || !je32_to_cpu(rd.ino)) continue; /* OK, check the actual name now */ if (!namebuf) { namebuf = kmalloc(name_len + 1, GFP_KERNEL); - if (!namebuf) + 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); + ret = jffs2_flash_read(c, (ref_offset(raw))+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); + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading name from obsolete node at %08x\n", ret, ref_offset(raw)); /* 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); + if (retlen != name_len+1) { + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %d) reading name from obsolete node at %08x\n", + retlen, name_len+1, ref_offset(raw)); continue; } if (memcmp(namebuf+1, fd->name, name_len)) @@ -449,8 +841,12 @@ static int jffs2_garbage_collect_deletio 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); } @@ -492,63 +888,66 @@ static int jffs2_garbage_collect_hole(st 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 = jffs2_flash_read(c, fn->raw->flash_offset & ~3, sizeof(ri), &readlen, (char *)&ri); + ret = jffs2_flash_read(c, ref_offset(fn->raw), 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); + printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen); goto fill; } - if (ri.nodetype != JFFS2_NODETYPE_INODE) { + if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) { printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n", - fn->raw->flash_offset & ~3, ri.nodetype, JFFS2_NODETYPE_INODE); + ref_offset(fn->raw), + je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE); return -EIO; } - if (ri.totlen != sizeof(ri)) { - printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%x\n", - fn->raw->flash_offset & ~3, ri.totlen, sizeof(ri)); + if (je32_to_cpu(ri.totlen) != sizeof(ri)) { + printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n", + ref_offset(fn->raw), + je32_to_cpu(ri.totlen), sizeof(ri)); return -EIO; } crc = crc32(0, &ri, sizeof(ri)-8); - if (crc != ri.node_crc) { + if (crc != je32_to_cpu(ri.node_crc)) { 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); + ref_offset(fn->raw), + je32_to_cpu(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 #%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 "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw)); 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 { fill: - ri.magic = JFFS2_MAGIC_BITMASK; - ri.nodetype = JFFS2_NODETYPE_INODE; - ri.totlen = sizeof(ri); - ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4); + ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + ri.totlen = cpu_to_je32(sizeof(ri)); + ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); - ri.ino = f->inocache->ino; - ri.version = ++f->highest_version; - ri.offset = start; - ri.csize = end - start; - ri.dsize = 0; + ri.ino = cpu_to_je32(f->inocache->ino); + ri.version = cpu_to_je32(++f->highest_version); + ri.offset = cpu_to_je32(start); + ri.dsize = cpu_to_je32(end - start); + ri.csize = cpu_to_je32(0); ri.compr = JFFS2_COMPR_ZERO; } - 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); + ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); + ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); + ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); + ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); + ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); + ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); + ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); + ri.data_crc = cpu_to_je32(0); + ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen); if (ret) { - printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_hole failed: %d\n", + printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n", sizeof(ri), ret); return ret; } @@ -558,16 +957,30 @@ static int jffs2_garbage_collect_hole(st printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn)); return PTR_ERR(new_fn); } - if (ri.version == f->highest_version) { + if (je32_to_cpu(ri.version) == f->highest_version) { jffs2_add_full_dnode_to_inode(c, f, new_fn); if (f->metadata) { jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; - return 0; } + return 0; } - for (frag = f->fraglist; frag; frag = frag->next) { + + /* + * We should only get here in the case where the node we are + * replacing had more than one frag, so we kept the same version + * number as before. (Except in case of error -- see 'goto fill;' + * above.) + */ + D1(if(unlikely(fn->frags <= 1)) { + printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n", + fn->frags, je32_to_cpu(ri.version), f->highest_version, + je32_to_cpu(ri.ino)); + }); + + for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs); + frag; frag = frag_next(frag)) { if (frag->ofs > fn->size + fn->ofs) break; if (frag->node == fn) { @@ -611,7 +1024,6 @@ static int jffs2_garbage_collect_dnode(s orig_end = end; - /* If we're looking at the last node in the block we're garbage-collecting, we allow ourselves to merge as if the block was already erasing. We're likely to be GC'ing a @@ -643,9 +1055,11 @@ static int jffs2_garbage_collect_dnode(s * page OK. We'll actually write it out again in commit_write, which is a little * suboptimal, but at least we're correct. */ - //pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); +#ifdef __ECOS pg = read_cache_page(start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); - +#else + pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); +#endif if (IS_ERR(pg)) { printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg)); return PTR_ERR(pg); @@ -662,11 +1076,11 @@ static int jffs2_garbage_collect_dnode(s ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen); if (ret) { - printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dnode failed: %d\n", + printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n", sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret); break; } - cdatalen = min(alloclen - sizeof(ri), end - offset); + cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset); datalen = end - offset; writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1)); @@ -679,26 +1093,26 @@ static int jffs2_garbage_collect_dnode(s } else { datalen = cdatalen; } - 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.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen); + ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); - ri.ino = f->inocache->ino; - ri.version = ++f->highest_version; - 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; + ri.ino = cpu_to_je32(f->inocache->ino); + ri.version = cpu_to_je32(++f->highest_version); + ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); + ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); + ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); + ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); + ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); + ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); + ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); + ri.offset = cpu_to_je32(offset); + ri.csize = cpu_to_je32(cdatalen); + ri.dsize = cpu_to_je32(datalen); ri.compr = comprtype; - ri.node_crc = crc32(0, &ri, sizeof(ri)-8); - ri.data_crc = crc32(0, writebuf, cdatalen); + ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); + ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen)); new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, NULL); diff --git a/packages/fs/jffs2/current/src/jffs2.c b/packages/fs/jffs2/current/src/jffs2.c deleted file mode 100644 --- a/packages/fs/jffs2/current/src/jffs2.c +++ /dev/null @@ -1,1968 +0,0 @@ -//========================================================================== -// -// jffs2.c -// -// JFFS2 file system -// -//========================================================================== -//####ECOSGPLCOPYRIGHTBEGIN#### -// ------------------------------------------- -// This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. -// -// eCos is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 or (at your option) any later version. -// -// eCos is distributed in the hope that it will be useful, but WITHOUT ANY -// WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. -// -// You should have received a copy of the GNU General Public License along -// with eCos; if not, write to the Free Software Foundation, Inc., -// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -// -// As a special exception, if other files instantiate templates or use macros -// or inline functions from this file, or you compile this file and link it -// with other works to produce a work based on this file, this file does not -// by itself cause the resulting work to be covered by the GNU General Public -// License. However the source code for this file must still be made available -// in accordance with section (3) of the GNU General Public License. -// -// This exception does not invalidate any other reasons why a work based on -// this file might be covered by the GNU General Public License. -// -// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. -// at http://sources.redhat.com/ecos/ecos-license/ -// ------------------------------------------- -//####ECOSGPLCOPYRIGHTEND#### -//========================================================================== -//#####DESCRIPTIONBEGIN#### -// -// Author(s): dominic.ostrowski@3glab.com -// Contributors: nickg, richard.panton@3glab.com -// Date: 2000-07-25 -// Purpose: JFFS2 file system -// Description: This is the JFFS2 flash filesystem for eCos. -// -//####DESCRIPTIONEND#### -// -//========================================================================== -// -// General Description -// =================== -// -// -//========================================================================== - -#include "jffs2port.h" -#include "jffs2.h" -#include "jffs2_fs_sb.h" -#include "jffs2_fs_i.h" -#include "nodelist.h" - -#include -#include -#include -#include -#include - -//========================================================================== -// Forward definitions - -// Filesystem operations -static int jffs2_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte ); -static int jffs2_umount ( cyg_mtab_entry *mte ); -static int jffs2_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - int mode, cyg_file *fte ); -static int jffs2_ops_unlink ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ); -static int jffs2_ops_mkdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ); -static int jffs2_ops_rmdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ); -static int jffs2_ops_rename ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, - cyg_dir dir2, const char *name2 ); -static int jffs2_ops_link ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, - cyg_dir dir2, const char *name2, int type ); -static int jffs2_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - cyg_file *fte ); -static int jffs2_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - cyg_dir *dir_out ); -static int jffs2_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - struct stat *buf); -static int jffs2_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - int key, void *buf, int len ); -static int jffs2_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - int key, void *buf, int len ); - -// File operations -static int jffs2_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); -static int jffs2_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); -static int jffs2_fo_lseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ); -static int jffs2_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, - CYG_ADDRWORD data); -static int jffs2_fo_fsync (struct CYG_FILE_TAG *fp, int mode ); -static int jffs2_fo_close (struct CYG_FILE_TAG *fp); -static int jffs2_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ); -static int jffs2_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ); -static int jffs2_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ); - -// Directory operations -static int jffs2_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); -static int jffs2_fo_dirlseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ); - - -//========================================================================== -// Filesystem table entries - -// ------------------------------------------------------------------------- -// Fstab entry. -// This defines the entry in the filesystem table. -// For simplicity we use _FILESYSTEM synchronization for all accesses since -// we should never block in any filesystem operations. - -FSTAB_ENTRY( jffs2_fste, "jffs2", 0, - CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM, - jffs2_mount, - jffs2_umount, - jffs2_open, - jffs2_ops_unlink, - jffs2_ops_mkdir, - jffs2_ops_rmdir, - jffs2_ops_rename, - jffs2_ops_link, - jffs2_opendir, - jffs2_chdir, - jffs2_stat, - jffs2_getinfo, - jffs2_setinfo); - -// ------------------------------------------------------------------------- -// File operations. -// This set of file operations are used for normal open files. - -static cyg_fileops jffs2_fileops = -{ - jffs2_fo_read, - jffs2_fo_write, - jffs2_fo_lseek, - jffs2_fo_ioctl, - cyg_fileio_seltrue, - jffs2_fo_fsync, - jffs2_fo_close, - jffs2_fo_fstat, - jffs2_fo_getinfo, - jffs2_fo_setinfo -}; - -// ------------------------------------------------------------------------- -// Directory file operations. -// This set of operations are used for open directories. Most entries -// point to error-returning stub functions. Only the read, lseek and -// close entries are functional. - -static cyg_fileops jffs2_dirops = -{ - jffs2_fo_dirread, - (cyg_fileop_write *)cyg_fileio_enosys, - jffs2_fo_dirlseek, - (cyg_fileop_ioctl *)cyg_fileio_enosys, - cyg_fileio_seltrue, - (cyg_fileop_fsync *)cyg_fileio_enosys, - jffs2_fo_close, - (cyg_fileop_fstat *)cyg_fileio_enosys, - (cyg_fileop_getinfo *)cyg_fileio_enosys, - (cyg_fileop_setinfo *)cyg_fileio_enosys -}; - - -//========================================================================== -// STATIC VARIABLES !!! - -static char read_write_buffer[PAGE_CACHE_SIZE]; //avoids malloc when user may be under memory pressure -static char gc_buffer[PAGE_CACHE_SIZE]; //avoids malloc when user may be under memory pressure - -//========================================================================== -// Directory operations - -struct jffs2_dirsearch -{ - struct inode *dir; // directory to search - const char *path; // path to follow - struct inode *node; // Node found - const char *name; // last name fragment used - int namelen; // name fragment length - cyg_bool last; // last name in path? -}; - -typedef struct jffs2_dirsearch jffs2_dirsearch; - -//========================================================================== -// Ref count and nlink management - -// ------------------------------------------------------------------------- -// dec_refcnt() -// Decrment the reference count on an inode. If this makes the ref count -// zero, then this inode can be freed. - -static int dec_refcnt( struct inode *node ) -{ - int err = ENOERR; - node->i_count--; - - // In JFFS2 inode's are temporary in ram structures that are free'd when the usage i_count drops to 0 - // The i_nlink however is managed by JFFS2 and is unrelated to usage - if( node->i_count == 0) - { - // This inode is not in use, so delete it. - iput(node); - } - - return err; -} - -// FIXME: This seems like real cruft. Wouldn't it be better just to do the -// right thing? -static void icache_evict(struct inode *root_i, struct inode *i) { - struct inode *cached_inode; - struct inode *next_inode; - - D2(printf("icache_evict\n")); - // If this is an absolute search path from the root, - // remove all cached inodes with i_count of zero (these are only - // held where needed for dotdot filepaths) - if(i == root_i) { - for(cached_inode = root_i; cached_inode != NULL; cached_inode = next_inode) { - next_inode = cached_inode->i_cache_next; - if (cached_inode->i_count == 0) { - cached_inode->i_cache_prev->i_cache_next = cached_inode->i_cache_next; // Prveious entry points ahead of us - if (cached_inode->i_cache_next != NULL) - cached_inode->i_cache_next->i_cache_prev = cached_inode->i_cache_prev; // Next entry points behind us - jffs2_clear_inode(cached_inode); - D2(printf("free icache_evict inode %x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n", cached_inode)); - free(cached_inode); - } - } - } -} - -//========================================================================== -// Directory search - -// ------------------------------------------------------------------------- -// init_dirsearch() -// Initialize a dirsearch object to start a search - -static void init_dirsearch( jffs2_dirsearch *ds, - struct inode *dir, - const char *name) -{ - D2(printf("init_dirsearch name = %s\n", name)); - D2(printf("init_dirsearch dir = %x\n", dir)); - ds->dir = dir; - ds->path = name; - ds->node = dir; - ds->name = name; - ds->namelen = 0; - ds->last = false; -} - - -// ------------------------------------------------------------------------- -// find_entry() -// Search a single directory for the next name in a path and update the -// dirsearch object appropriately. - -static int find_entry( jffs2_dirsearch *ds ) -{ - unsigned long hash; - struct qstr this; - unsigned int c; - const char * hashname; - - struct inode *dir = ds->dir; - const char *name = ds->path; - const char *n = name; - char namelen = 0; - struct inode *d; - - D2(printf("find_entry\n")); - - // check that we really have a directory - if( !S_ISDIR(dir->i_mode) ) - return ENOTDIR; - - // Isolate the next element of the path name. - while( *n != '\0' && *n != '/' ) - n++, namelen++; - - // If we terminated on a NUL, set last flag. - if( *n == '\0' ) - ds->last = true; - - // update name in dirsearch object - ds->name = name; - ds->namelen = namelen; - - if (name[0] == '.') switch (namelen) { - default: - break; - case 2: - // Dot followed by not Dot, treat as any other name - if (name[1] != '.') - break; - // Dot Dot - // Move back up the search path - D2(printf("find_entry found ..\n")); - ds->node = ds->dir->i_parent; - if(ds->dir->i_count == 0) { - iput(ds->dir); // This inode may be evicted - ds->dir = NULL; - } - return ENOERR; - case 1: - // Dot is consumed - D2(printf("find_entry found .\n")); - ds->node = ds->dir; - return ENOERR; - } - - // Here we have the name and its length set up. - // Search the directory for a matching entry - - hashname = name; - this.name = hashname; - c = *(const unsigned char *)hashname; - - hash = init_name_hash(); - do { - hashname++; - hash = partial_name_hash(c, hash); - c = *(const unsigned char *)hashname; - } while (c && (c != '/')); - this.len = hashname - (const char *) this.name; - this.hash = end_name_hash(hash); - - D2(printf("find_entry for name = %s\n", ds->path)); - d = jffs2_lookup(dir, &this); - D2(printf("find_entry got dir = %x\n", d)); - - if( d == NULL ) - return ENOENT; - - // The back path for dotdot to follow - d->i_parent = dir; - // pass back the node we have found - ds->node = d; - - return ENOERR; - -} - -// ------------------------------------------------------------------------- -// jffs2_find() -// Main interface to directory search code. This is used in all file -// level operations to locate the object named by the pathname. - -static int jffs2_find( jffs2_dirsearch *d ) -{ - int err; - - D2(printf("jffs2_find for path =%s\n", d->path)); - // Short circuit empty paths - if( *(d->path) == '\0' ) - return ENOERR; - - // iterate down directory tree until we find the object - // we want. - for(;;) - { - err = find_entry( d ); - - if( err != ENOERR ) - return err; - - if( d->last ) - return ENOERR; - - // every inode traversed in the find is temporary and should be free'd - //iput(d->dir); - - // Update dirsearch object to search next directory. - d->dir = d->node; - d->path += d->namelen; - if( *(d->path) == '/' ) d->path++; // skip dirname separators - } -} - -//========================================================================== -// Pathconf support -// This function provides support for pathconf() and fpathconf(). - -static int jffs2_pathconf( struct inode *node, struct cyg_pathconf_info *info ) -{ - int err = ENOERR; - D2(printf("jffs2_pathconf\n")); - - switch( info->name ) - { - case _PC_LINK_MAX: - info->value = LINK_MAX; - break; - - case _PC_MAX_CANON: - info->value = -1; // not supported - err = EINVAL; - break; - - case _PC_MAX_INPUT: - info->value = -1; // not supported - err = EINVAL; - break; - - case _PC_NAME_MAX: - info->value = NAME_MAX; - break; - - case _PC_PATH_MAX: - info->value = PATH_MAX; - break; - - case _PC_PIPE_BUF: - info->value = -1; // not supported - err = EINVAL; - break; - - - case _PC_ASYNC_IO: - info->value = -1; // not supported - err = EINVAL; - break; - - case _PC_CHOWN_RESTRICTED: - info->value = -1; // not supported - err = EINVAL; - break; - - case _PC_NO_TRUNC: - info->value = 0; - break; - - case _PC_PRIO_IO: - info->value = 0; - break; - - case _PC_SYNC_IO: - info->value = 0; - break; - - case _PC_VDISABLE: - info->value = -1; // not supported - err = EINVAL; - break; - - default: - err = EINVAL; - break; - } - - return err; -} - -//========================================================================== -// Filesystem operations - -// ------------------------------------------------------------------------- -// jffs2_mount() -// Process a mount request. This mainly creates a root for the -// filesystem. -static int jffs2_read_super(struct super_block *sb) -{ - struct jffs2_sb_info *c; - struct inode *root_i; - Cyg_ErrNo err; - cyg_uint32 len; - cyg_io_flash_getconfig_devsize_t ds; - cyg_io_flash_getconfig_blocksize_t bs; - - D1(printk(KERN_DEBUG "jffs2: read_super\n")); - - c = JFFS2_SB_INFO(sb); - memset(c, 0, sizeof(*c)); - - len = sizeof(ds); - err = cyg_io_get_config( sb->s_dev, - CYG_IO_GET_CONFIG_FLASH_DEVSIZE, - &ds, &len ); - if ( err != ENOERR ) { - D1(printf("jffs2: cyg_io_get_config failed to get dev size: %d\n", - err)); - return err; - } - len = sizeof(bs); - bs.offset = 0; - err = cyg_io_get_config( sb->s_dev, - CYG_IO_GET_CONFIG_FLASH_BLOCKSIZE, - &bs, &len ); - if ( err != ENOERR ) { - D1(printf("jffs2: cyg_io_get_config failed to get block size: %d\n", - err)); - return err; - } - - c->sector_size = bs.block_size; - c->flash_size = ds.dev_size; - - err = jffs2_do_mount_fs(c); - if (err) - return -err; - - 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")); - err = EIO; - 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) { - err = ENOMEM; - goto out_root_i; - } - sb->s_blocksize = PAGE_CACHE_SIZE; - sb->s_blocksize_bits = PAGE_CACHE_SHIFT; - sb->s_magic = JFFS2_SUPER_MAGIC; - - return 0; - - out_root_i: - iput(root_i); - out_nodes: - jffs2_free_ino_caches(c); - jffs2_free_raw_node_refs(c); - free(c->blocks); - - return err; -} - -static int jffs2_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte ) -{ - extern cyg_mtab_entry mtab[], mtab_end; - struct super_block *jffs2_sb = NULL; - struct jffs2_sb_info *c; - cyg_mtab_entry *m; - cyg_io_handle_t t; - Cyg_ErrNo err; - - D2(printf("jffs2_mount\n")); - - err = cyg_io_lookup( mte->devname, &t ); - if( err != ENOERR ) - return -err; - - // Iterate through the mount table to see if we're mounted - // FIXME: this should be done better - perhaps if the superblock - // can be stored as an inode in the icache. - for( m = &mtab[0]; m != &mtab_end; m++ ) - { - // stop if there are more than the configured maximum - if( m-&mtab[0] >= CYGNUM_FILEIO_MTAB_MAX ) - { - m = &mtab_end; - break; - } - if ( m->valid && strcmp( m->fsname, "jffs2" ) == 0 && - strcmp( m->devname, mte->devname) == 0 ) { - jffs2_sb = (struct super_block *)m->data; - } - } - - if ( jffs2_sb == NULL) { - jffs2_sb = malloc(sizeof(struct super_block)); - - if(jffs2_sb == NULL) - return ENOSPC; - - c = JFFS2_SB_INFO(jffs2_sb); - memset(jffs2_sb, 0, sizeof(struct super_block)); - jffs2_sb->s_dev = t; - - err = jffs2_read_super(jffs2_sb); - - if (err) { - free(jffs2_sb); - return err; - } - - jffs2_sb->s_root->i_parent = jffs2_sb->s_root; // points to itself, no dotdot paths above mountpoint - jffs2_sb->s_root->i_cache_prev = NULL; // root inode, so always null - jffs2_sb->s_root->i_cache_next = NULL; - jffs2_sb->s_root->i_count = 1; // Ensures the root inode is always in ram until umount - - D2(printf("jffs2_mount erasing pending blocks\n")); - jffs2_erase_pending_blocks(c); - jffs2_mark_erased_blocks(c); - } - mte->data = (CYG_ADDRWORD)jffs2_sb; - - jffs2_sb->s_mount_count++; - mte->root = (cyg_dir)jffs2_sb->s_root; - D2(printf("jffs2_mounted superblock at %x\n", mte->root)); - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_umount() -// Unmount the filesystem. - -static int jffs2_umount ( cyg_mtab_entry *mte ) -{ - struct inode *root = (struct inode *)mte->root; - struct super_block *jffs2_sb = root->i_sb; - struct jffs2_sb_info *c = JFFS2_SB_INFO(jffs2_sb); - - D2(printf("jffs2_umount\n")); - - // Decrement the mount count - jffs2_sb->s_mount_count--; - - // Only really umount if this is the only mount - if (jffs2_sb->s_mount_count == 0) { - - // Check for open/inuse root or any cached inodes -//if( root->i_count != 1 || root->i_cache_next != NULL) // root icount was set to 1 on mount - if( root->i_cache_next != NULL) // root icount was set to 1 on mount - return EBUSY; - - dec_refcnt(root); // Time to free the root inode - - //Clear root inode - //root_i = NULL; - - // Clean up the super block and root inode - jffs2_free_ino_caches(c); - jffs2_free_raw_node_refs(c); - free(c->blocks); - - // Clear root pointer - mte->root = CYG_DIR_NULL; - - mte->fs->data = 0; // fstab entry, visible to all mounts. No current mount - // That's all folks. - D2(printf("jffs2_umount No current mounts\n")); - } - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_open() -// Open a file for reading or writing. - -static int jffs2_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - int mode, cyg_file *file ) -{ - - jffs2_dirsearch ds; - struct inode *node = NULL; - int err; - - D2(printf("jffs2_open\n")); - - icache_evict((struct inode *)mte->root, (struct inode *)dir); - - init_dirsearch( &ds, (struct inode *)dir, name ); - - err = jffs2_find( &ds ); - - if( err == ENOENT ) - { - if( ds.last && (mode & O_CREAT) ) - { - unsigned long hash; - struct qstr this; - unsigned int c; - const char * hashname; - - // No node there, if the O_CREAT bit is set then we must - // create a new one. The dir and name fields of the dirsearch - // object will have been updated so we know where to put it. - - hashname = ds.name; - this.name = hashname; - c = *(const unsigned char *)hashname; - - hash = init_name_hash(); - do { - hashname++; - hash = partial_name_hash(c, hash); - c = *(const unsigned char *)hashname; - } while (c && (c != '/')); - this.len = hashname - (const char *) this.name; - this.hash = end_name_hash(hash); - - err = jffs2_create(ds.dir, &this, 0644, &node); - - if( err != 0 ) - { - //Possible orphaned inode on the flash - but will be gc'd - return err; - } - - err = ENOERR; - } - } - else if( err == ENOERR ) - { - // The node exists. If the O_CREAT and O_EXCL bits are set, we - // must fail the open. - - if( (mode & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL) ) - err = EEXIST; - else node = ds.node; - } - - if( err == ENOERR && (mode & O_TRUNC ) ) - { - // If the O_TRUNC bit is set we must clean out the file data. - - node->i_size = 0; - - // Update file times - node->i_ctime = - node->i_mtime = cyg_timestamp(); - } - - if( err != ENOERR ) return err; - - // Check that we actually have a file here - if( S_ISDIR(node->i_mode) ) return EISDIR; - - node->i_count++; // Count successful open - - // Initialize the file object - - file->f_flag |= mode & CYG_FILE_MODE_MASK; - file->f_type = CYG_FILE_TYPE_FILE; - file->f_ops = &jffs2_fileops; - file->f_offset = (mode&O_APPEND) ? node->i_size : 0; - file->f_data = (CYG_ADDRWORD)node; - file->f_xops = 0; - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_ops_unlink() -// Remove a file link from its directory. - -static int jffs2_ops_unlink ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ) -{ - unsigned long hash; - struct qstr this; - unsigned int c; - const char * hashname; - jffs2_dirsearch ds; - int err; - - D2(printf("jffs2_ops_unlink\n")); - - icache_evict((struct inode *)mte->root, (struct inode *)dir); - - init_dirsearch( &ds, (struct inode *)dir, name ); - - err = jffs2_find( &ds ); - - if( err != ENOERR ) return err; - - // Cannot unlink directories, use rmdir() instead - if( S_ISDIR(ds.node->i_mode) ) - return EPERM; - - // Delete it from its directory - - hashname = ds.name; - this.name = hashname; - c = *(const unsigned char *)hashname; - - hash = init_name_hash(); - do { - hashname++; - hash = partial_name_hash(c, hash); - c = *(const unsigned char *)hashname; - } while (c && (c != '/')); - this.len = hashname - (const char *) this.name; - this.hash = end_name_hash(hash); - - err = jffs2_unlink(ds.dir, ds.node, &this); - - return err; -} - -// ------------------------------------------------------------------------- -// jffs2_ops_mkdir() -// Create a new directory. - -static int jffs2_ops_mkdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ) -{ - jffs2_dirsearch ds; - struct inode *node = NULL; - int err; - - D2(printf("jffs2_ops_mkdir\n")); - - icache_evict((struct inode *)mte->root, (struct inode *)dir); - - init_dirsearch( &ds, (struct inode *)dir, name ); - - err = jffs2_find( &ds ); - - if( err == ENOENT ) - { - if( ds.last ) - { - unsigned long hash; - struct qstr this; - unsigned int c; - const char * hashname; - // The entry does not exist, and it is the last element in - // the pathname, so we can create it here. - - hashname = ds.name; - this.name = hashname; - c = *(const unsigned char *)hashname; - - hash = init_name_hash(); - do { - hashname++; - hash = partial_name_hash(c, hash); - c = *(const unsigned char *)hashname; - } while (c && (c != '/')); - this.len = hashname - (const char *) this.name; - this.hash = end_name_hash(hash); - - err = jffs2_mkdir(ds.dir, &this, 0, &node); - - if( err != 0 ) - return ENOSPC; - - } - // If this was not the last element, then and intermediate - // directory does not exist. - } - else - { - // If there we no error, something already exists with that - // name, so we cannot create another one. - - if( err == ENOERR ) - err = EEXIST; - } - - return err; -} - -// ------------------------------------------------------------------------- -// jffs2_ops_rmdir() -// Remove a directory. - -static int jffs2_ops_rmdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name ) -{ - unsigned long hash; - struct qstr this; - unsigned int c; - const char * hashname; - jffs2_dirsearch ds; - int err; - - D2(printf("jffs2_ops_rmdir\n")); - - icache_evict((struct inode *)mte->root, (struct inode *)dir); - - init_dirsearch( &ds, (struct inode *)dir, name ); - - err = jffs2_find( &ds ); - - if( err != ENOERR ) return err; - - // Check that this is actually a directory. - if( !S_ISDIR(ds.node->i_mode) ) - return EPERM; - - // Delete the entry. - hashname = ds.name; - this.name = hashname; - c = *(const unsigned char *)hashname; - - hash = init_name_hash(); - do { - hashname++; - hash = partial_name_hash(c, hash); - c = *(const unsigned char *)hashname; - } while (c && (c != '/')); - this.len = hashname - (const char *) this.name; - this.hash = end_name_hash(hash); - - err = jffs2_rmdir(ds.dir, ds.node, &this); - - return err; - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_ops_rename() -// Rename a file/dir. - -static int jffs2_ops_rename ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, - cyg_dir dir2, const char *name2 ) -{ - unsigned long hash; - struct qstr this1,this2; - unsigned int c; - const char * hashname; - jffs2_dirsearch ds1, ds2; - int err; - - D2(printf("jffs2_ops_rename\n")); - - init_dirsearch( &ds1, (struct inode *)dir1, name1 ); - - err = jffs2_find( &ds1 ); - - if( err != ENOERR ) return err; - - init_dirsearch( &ds2, (struct inode *)dir2, name2 ); - - err = jffs2_find( &ds2 ); - - // Allow through renames to non-existent objects. - if( ds2.last && err == ENOENT ) - ds2.node = NULL, err = ENOERR; - - if( err != ENOERR ) return err; - - // Null rename, just return - if( ds1.node == ds2.node ) - return ENOERR; - - hashname = ds1.name; - this1.name = hashname; - c = *(const unsigned char *)hashname; - - hash = init_name_hash(); - do { - hashname++; - hash = partial_name_hash(c, hash); - c = *(const unsigned char *)hashname; - } while (c && (c != '/')); - this1.len = hashname - (const char *) this1.name; - this1.hash = end_name_hash(hash); - - hashname = ds2.name; - this2.name = hashname; - c = *(const unsigned char *)hashname; - - hash = init_name_hash(); - do { - hashname++; - hash = partial_name_hash(c, hash); - c = *(const unsigned char *)hashname; - } while (c && (c != '/')); - this2.len = hashname - (const char *) this2.name; - this2.hash = end_name_hash(hash); - - // First deal with any entry that is at the destination - if( ds2.node ) - { - // Check that we are renaming like-for-like - - if( !S_ISDIR(ds1.node->i_mode) && S_ISDIR(ds2.node->i_mode) ) - return EISDIR; - - if( S_ISDIR(ds1.node->i_mode) && !S_ISDIR(ds2.node->i_mode) ) - return ENOTDIR; - - // Now delete the destination directory entry - - err = jffs2_unlink(ds2.dir, ds2.node, &this2); - - if( err != 0 ) return err; - - } - - // Now we know that there is no clashing node at the destination, - // make a new direntry at the destination and delete the old entry - // at the source. - - err = jffs2_rename(ds1.dir, ds1.node, &this1, ds2.dir, &this2); - - // Update directory times - if( err == 0 ) - ds1.dir->i_ctime = - ds1.dir->i_mtime = - ds2.dir->i_ctime = - ds2.dir->i_mtime = cyg_timestamp(); - - return err; -} - -// ------------------------------------------------------------------------- -// jffs2_ops_link() -// Make a new directory entry for a file. - -static int jffs2_ops_link ( cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, - cyg_dir dir2, const char *name2, int type ) -{ - unsigned long hash; - struct qstr this; - unsigned int c; - const char * hashname; - jffs2_dirsearch ds1, ds2; - int err; - - D2(printf("jffs2_ops_link\n")); - - // Only do hard links for now in this filesystem - if( type != CYG_FSLINK_HARD ) - return EINVAL; - - init_dirsearch( &ds1, (struct inode *)dir1, name1 ); - - err = jffs2_find( &ds1 ); - - if( err != ENOERR ) return err; - - init_dirsearch( &ds2, (struct inode *)dir2, name2 ); - - err = jffs2_find( &ds2 ); - - // Don't allow links to existing objects - if( err == ENOERR ) return EEXIST; - - // Allow through links to non-existing terminal objects - if( ds2.last && err == ENOENT ) - ds2.node = NULL, err = ENOERR; - - if( err != ENOERR ) return err; - - // Now we know that there is no existing node at the destination, - // make a new direntry at the destination. - - hashname = ds2.name; - this.name = hashname; - c = *(const unsigned char *)hashname; - - hash = init_name_hash(); - do { - hashname++; - hash = partial_name_hash(c, hash); - c = *(const unsigned char *)hashname; - } while (c && (c != '/')); - this.len = hashname - (const char *) this.name; - this.hash = end_name_hash(hash); - - err = jffs2_link(ds2.dir, ds1.node, &this); - - if( err == 0 ) - ds1.node->i_ctime = - ds2.dir->i_ctime = - ds2.dir->i_mtime = cyg_timestamp(); - - return err; -} - -// ------------------------------------------------------------------------- -// jffs2_opendir() -// Open a directory for reading. - -static int jffs2_opendir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - cyg_file *file ) -{ - jffs2_dirsearch ds; - int err; - - D2(printf("jffs2_opendir\n")); - - icache_evict((struct inode *)mte->root, (struct inode *)dir); - - init_dirsearch( &ds, (struct inode *)dir, name ); - - err = jffs2_find( &ds ); - - if( err != ENOERR ) return err; - - // check it is really a directory. - if( !S_ISDIR(ds.node->i_mode) ) return ENOTDIR; - - ds.node->i_count++; // Count successful open - - // Initialize the file object, setting the f_ops field to a - // special set of file ops. - - file->f_type = CYG_FILE_TYPE_FILE; - file->f_ops = &jffs2_dirops; - file->f_offset = 0; - file->f_data = (CYG_ADDRWORD)ds.node; - file->f_xops = 0; - - - return ENOERR; - -} - -// ------------------------------------------------------------------------- -// jffs2_chdir() -// Change directory support. - -static int jffs2_chdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - cyg_dir *dir_out ) -{ - D2(printf("jffs2_chdir\n")); - - if( dir_out != NULL ) - { - // This is a request to get a new directory pointer in - // *dir_out. - - jffs2_dirsearch ds; - int err; - - icache_evict((struct inode *)mte->root, (struct inode *)dir); - - init_dirsearch( &ds, (struct inode *)dir, name ); - - err = jffs2_find( &ds ); - - if( err != ENOERR ) return err; - - // check it is a directory - if( !S_ISDIR(ds.node->i_mode) ) - return ENOTDIR; - - // Increment ref count to keep this directory in existance - // while it is the current cdir. - ds.node->i_count++; - - // Pass it out - *dir_out = (cyg_dir)ds.node; - } - else - { - // If no output dir is required, this means that the mte and - // dir arguments are the current cdir setting and we should - // forget this fact. - - struct inode *node = (struct inode *)dir; - - // Just decrement directory reference count. - dec_refcnt( node ); - } - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_stat() -// Get struct stat info for named object. - -static int jffs2_stat ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - struct stat *buf) -{ - jffs2_dirsearch ds; - int err; - - D2(printf("jffs2_stat\n")); - - icache_evict((struct inode *)mte->root, (struct inode *)dir); - - init_dirsearch( &ds, (struct inode *)dir, name ); - - err = jffs2_find( &ds ); - - if( err != ENOERR ) return err; - - // Fill in the status - buf->st_mode = ds.node->i_mode; - buf->st_ino = (ino_t)ds.node; - buf->st_dev = 0; - buf->st_nlink = ds.node->i_nlink; - buf->st_uid = 0; - buf->st_gid = 0; - buf->st_size = ds.node->i_size; - buf->st_atime = ds.node->i_atime; - buf->st_mtime = ds.node->i_mtime; - buf->st_ctime = ds.node->i_ctime; - - return err; - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_getinfo() -// Getinfo. Currently only support pathconf(). - -static int jffs2_getinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - int key, void *buf, int len ) -{ - jffs2_dirsearch ds; - int err; - - D2(printf("jffs2_getinfo\n")); - - icache_evict((struct inode *)mte->root, (struct inode *)dir); - - init_dirsearch( &ds, (struct inode *)dir, name ); - - err = jffs2_find( &ds ); - - if( err != ENOERR ) return err; - - switch( key ) - { - case FS_INFO_CONF: - err = jffs2_pathconf( ds.node, (struct cyg_pathconf_info *)buf ); - break; - - default: - err = EINVAL; - } - return err; - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_setinfo() -// Setinfo. Nothing to support here at present. - -static int jffs2_setinfo ( cyg_mtab_entry *mte, cyg_dir dir, const char *name, - int key, void *buf, int len ) -{ - // No setinfo keys supported at present - - D2(printf("jffs2_setinfo\n")); - - return EINVAL; -} - - -//========================================================================== -// File operations - -// ------------------------------------------------------------------------- -// jffs2_fo_read() -// Read data from the file. - -static int jffs2_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) -{ - struct inode *inode = (struct inode *)fp->f_data; - struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); - struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); - int i; - ssize_t resid = uio->uio_resid; - off_t pos = fp->f_offset; - - down(&f->sem); - - // Loop over the io vectors until there are none left - for( i = 0; i < uio->uio_iovcnt && pos < inode->i_size; i++ ) - { - int ret; - cyg_iovec *iov = &uio->uio_iov[i]; - off_t len = min(iov->iov_len, inode->i_size - pos); - - D2(printf("jffs2_fo_read inode size %d\n", inode->i_size)); - - ret = jffs2_read_inode_range(c, f, (unsigned char *)iov->iov_base, - pos, len); - if (ret) { - D1(printf("jffs2_fo_read(): read_inode_range failed %d\n", - ret)); - uio->uio_resid = resid; - up(&f->sem); - return -ret; - } - resid -= len; - pos += len; - } - - // We successfully read some data, update the node's access time - // and update the file offset and transfer residue. - - inode->i_atime = cyg_timestamp(); - - uio->uio_resid = resid; - fp->f_offset = pos; - - up(&f->sem); - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_fo_write() -// Write data to file. - -static int jffs2_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) -{ - struct page write_page; - off_t page_start_pos; - struct inode *node = (struct inode *)fp->f_data; - off_t pos = fp->f_offset; - ssize_t resid = uio->uio_resid; - int i; - - memset(&read_write_buffer, 0, PAGE_CACHE_SIZE); - write_page.virtual = &read_write_buffer; - - // If the APPEND mode bit was supplied, force all writes to - // the end of the file. - if( fp->f_flag & CYG_FAPPEND ) - pos = fp->f_offset = node->i_size; - - // Check that pos is within current file size, or at the very end. - if( pos < 0 || pos > node->i_size ) - return EINVAL; - - // Now loop over the iovecs until they are all done, or - // we get an error. - for( i = 0; i < uio->uio_iovcnt; i++ ) - { - cyg_iovec *iov = &uio->uio_iov[i]; - char *buf = (char *)iov->iov_base; - off_t len = iov->iov_len; - - // loop over the vector writing it to the file until it has - // all been done. - while( len > 0 ) - { - //cyg_uint8 *fbuf; - //size_t bsize; - size_t writtenlen; - off_t l = len; - int err; - - write_page.index = 0; - - page_start_pos = pos; - while(page_start_pos >= (PAGE_CACHE_SIZE)) { - write_page.index++; - page_start_pos -= PAGE_CACHE_SIZE; - } - - if( l > PAGE_CACHE_SIZE - page_start_pos) - l = PAGE_CACHE_SIZE - page_start_pos; - - D2(printf("jffs2_fo_write write_page.index %d\n", write_page.index)); - D2(printf("jffs2_fo_write page_start_pos %d\n", page_start_pos)); - D2(printf("jffs2_fo_write transfer size %d\n", l)); - - err = jffs2_prepare_write(node, &write_page, page_start_pos, page_start_pos + l); - - if( err != 0 ) - return err; - - // copy data in - memcpy(&read_write_buffer[page_start_pos], buf, l); - - writtenlen = jffs2_commit_write(node, &write_page, page_start_pos, page_start_pos + l); - - if( writtenlen != l ) - return ENOSPC; - - // Update working vars - len -= l; - buf += l; - pos += l; - resid -= l; - } - } - - // We wrote some data successfully, update the modified and access - // times of the node, increase its size appropriately, and update - // the file offset and transfer residue. - node->i_mtime = - node->i_ctime = cyg_timestamp(); - if( pos > node->i_size ) - node->i_size = pos; - - uio->uio_resid = resid; - fp->f_offset = pos; - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_fo_lseek() -// Seek to a new file position. - -static int jffs2_fo_lseek (struct CYG_FILE_TAG *fp, off_t *apos, int whence ) -{ - struct inode *node = (struct inode *)fp->f_data; - off_t pos = *apos; - - D2(printf("jffs2_fo_lseek\n")); - - switch( whence ) - { - case SEEK_SET: - // Pos is already where we want to be. - break; - - case SEEK_CUR: - // Add pos to current offset. - pos += fp->f_offset; - break; - - case SEEK_END: - // Add pos to file size. - pos += node->i_size; - break; - - default: - return EINVAL; - } - - // Check that pos is still within current file size, or at the - // very end. - if( pos < 0 || pos > node->i_size ) - return EINVAL; - - // All OK, set fp offset and return new position. - *apos = fp->f_offset = pos; - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_fo_ioctl() -// Handle ioctls. Currently none are defined. - -static int jffs2_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, - CYG_ADDRWORD data) -{ - // No Ioctls currenly defined. - - D2(printf("jffs2_fo_ioctl\n")); - - return EINVAL; -} - -// ------------------------------------------------------------------------- -// jffs2_fo_fsync(). -// Force the file out to data storage. - -static int jffs2_fo_fsync (struct CYG_FILE_TAG *fp, int mode ) -{ - // Data is always permanently where it belongs, nothing to do - // here. - - D2(printf("jffs2_fo_fsync\n")); - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_fo_close() -// Close a file. We just decrement the refcnt and let it go away if -// that is all that is keeping it here. - -static int jffs2_fo_close (struct CYG_FILE_TAG *fp) -{ - struct inode *node = (struct inode *)fp->f_data; - - D2(printf("jffs2_fo_close\n")); - - dec_refcnt( node ); - - fp->f_data = 0; // zero data pointer - - return ENOERR; -} - -// ------------------------------------------------------------------------- -//jffs2_fo_fstat() -// Get file status. - -static int jffs2_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ) -{ - struct inode *node = (struct inode *)fp->f_data; - - D2(printf("jffs2_fo_fstat\n")); - - // Fill in the status - buf->st_mode = node->i_mode; - buf->st_ino = (ino_t)node; - buf->st_dev = 0; - buf->st_nlink = node->i_nlink; - buf->st_uid = 0; - buf->st_gid = 0; - buf->st_size = node->i_size; - buf->st_atime = node->i_atime; - buf->st_mtime = node->i_mtime; - buf->st_ctime = node->i_ctime; - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_fo_getinfo() -// Get info. Currently only supports fpathconf(). - -static int jffs2_fo_getinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ) -{ - struct inode *node = (struct inode *)fp->f_data; - int err; - - D2(printf("jffs2_fo_getinfo\n")); - - switch( key ) - { - case FS_INFO_CONF: - err = jffs2_pathconf( node, (struct cyg_pathconf_info *)buf ); - break; - - default: - err = EINVAL; - } - return err; - - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_fo_setinfo() -// Set info. Nothing supported here. - -static int jffs2_fo_setinfo (struct CYG_FILE_TAG *fp, int key, void *buf, int len ) -{ - // No setinfo key supported at present - - D2(printf("jffs2_fo_setinfo\n")); - - return ENOERR; -} - - -//========================================================================== -// Directory operations - -// ------------------------------------------------------------------------- -// jffs2_fo_dirread() -// Read a single directory entry from a file. - -static __inline void -filldir(char *nbuf, int nlen, const char * name, int namlen) -{ - int len = nlen < namlen ? nlen : namlen; - memcpy(nbuf, name, len); - nbuf[len] = '\0'; -} - -static int jffs2_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) -{ - struct inode *d_inode = (struct inode *)fp->f_data; - struct dirent *ent = (struct dirent *)uio->uio_iov[0].iov_base; - char *nbuf = ent->d_name; - int nlen = sizeof(ent->d_name)-1; - off_t len = uio->uio_iov[0].iov_len; - struct jffs2_inode_info *f; - struct jffs2_sb_info *c; - struct inode *inode = d_inode; - struct jffs2_full_dirent *fd; - unsigned long offset, curofs; - int found = 1; - - if( len < sizeof(struct dirent) ) - return EINVAL; - - D1(printk(KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", d_inode->i_ino)); - - f = JFFS2_INODE_INFO(inode); - c = JFFS2_SB_INFO(inode->i_sb); - - offset = fp->f_offset; - - if (offset == 0) { - D1(printk(KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino)); - filldir(nbuf, nlen, ".", 1); - goto out; - } - if (offset == 1) { - filldir(nbuf, nlen, "..", 2); - goto out; - } - - curofs=1; - down(&f->sem); - for (fd = f->dents; fd; fd = fd->next) { - - curofs++; - /* First loop: curofs = 2; offset = 2 */ - if (curofs < offset) { - D2(printk(KERN_DEBUG "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n", - fd->name, fd->ino, fd->type, curofs, offset)); - continue; - } - if (!fd->ino) { - D2(printk(KERN_DEBUG "Skipping deletion dirent \"%s\"\n", fd->name)); - offset++; - continue; - } - D2(printk(KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset, fd->name, fd->ino, fd->type)); - filldir(nbuf, nlen, fd->name, strlen(fd->name)); - goto out_sem; - } - /* Reached the end of the directory */ - found = 0; - out_sem: - up(&f->sem); - out: - fp->f_offset = ++offset; - if (found) { - uio->uio_resid -= sizeof(struct dirent); - } - return ENOERR; -} - -// ------------------------------------------------------------------------- -// jffs2_fo_dirlseek() -// Seek directory to start. - -static int jffs2_fo_dirlseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ) -{ - // Only allow SEEK_SET to zero - - D2(printf("jffs2_fo_dirlseek\n")); - - if( whence != SEEK_SET || *pos != 0) - return EINVAL; - - *pos = fp->f_offset = 0; - - return ENOERR; -} - - -//========================================================================== -// -// Called by JFFS2 -// =============== -// -// -//========================================================================== - - -struct page *read_cache_page(unsigned long index, - int (*filler)(void *,struct page*), - void *data) -{ - // Only called in gc.c jffs2_garbage_collect_dnode - // but gets a real page for the specified inode - - int err; - struct page *gc_page = malloc(sizeof(struct page)); - -printf("read_cache_page\n"); - memset(&gc_buffer, 0, PAGE_CACHE_SIZE); - - if(gc_page != NULL) { - gc_page->virtual = &gc_buffer; - gc_page->index = index; - - err = filler(data, gc_page); - if (err < 0) { - free(gc_page); - gc_page = NULL; - } - } - - return gc_page; -} - -void page_cache_release(struct page * pg) { - - // Only called in gc.c jffs2_garbage_collect_dnode - // but should free the page malloc'd by read_cache_page - -printf("page_cache_release\n"); - free(pg); -} - -struct inode * new_inode(struct super_block *sb) { - - // Only called in write.c jffs2_new_inode - // Always adds itself to inode cache - - struct inode * inode; - struct inode * cached_inode; - - inode = malloc(sizeof(struct inode)); - if (inode == NULL) - return 0; - - D2(printf("malloc new_inode %x ####################################\n", inode)); - - memset(inode, 0, sizeof(struct inode)); - inode->i_sb = sb; - inode->i_ino = 1; - inode->i_count = 0;//1; // Let ecos manage the open count - - inode->i_nlink = 1; // Let JFFS2 manage the link count - inode->i_size = 0; - - inode->i_cache_next = NULL; // Newest inode, about to be cached - - // Add to the icache - for(cached_inode = sb->s_root; cached_inode != NULL; cached_inode = cached_inode->i_cache_next) { - if (cached_inode->i_cache_next == NULL) { - cached_inode->i_cache_next = inode; // Current last in cache points to newcomer - inode->i_cache_prev = cached_inode; // Newcomer points back to last - break; - } - } - - return inode; -} - -struct inode * iget(struct super_block *sb, cyg_uint32 ino) { - - // Substitute for iget drops straight through to reading the - // inode from disk if it is not in the inode cache - - // Called in super.c jffs2_read_super, dir.c jffs2_lookup, - // and gc.c jffs2_garbage_collect_pass - - // Must first check for cached inode - // If this fails let new_inode create one - - struct inode * inode; - - D2(printf("iget\n")); - - // Check for this inode in the cache - for(inode = sb->s_root; inode != NULL; inode = inode->i_cache_next) { - if (inode->i_ino == ino) - return inode; - } - inode = NULL; - - // Not cached, so malloc it - inode = new_inode(sb); - if (inode == NULL) - return 0; - - inode->i_ino = ino; - jffs2_read_inode(inode); - - return inode; -} - -void iput(struct inode * i) { - - // Called in dec_refcnt, jffs2_find - // (and jffs2_open and jffs2_ops_mkdir?) - // super.c jffs2_read_super, - // and gc.c jffs2_garbage_collect_pass - - struct inode *cached_inode; - - D2(printf("free iput inode %x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n", i)); - if (i && i->i_count) { - /* Added by dwmw2. iget/iput in Linux track the use count, - don't just unconditionally free it */ - printf("iput called for used inode\n"); - return; - } - if (i != NULL) { - // Remove from the icache - for(cached_inode = i->i_sb->s_root; cached_inode != NULL; cached_inode = cached_inode->i_cache_next) { - if (cached_inode == i) { - cached_inode->i_cache_prev->i_cache_next = cached_inode->i_cache_next; // Prveious entry points ahead of us - if (cached_inode->i_cache_next != NULL) - cached_inode->i_cache_next->i_cache_prev = cached_inode->i_cache_prev; // Next entry points behind us - break; - } - } - // inode has been seperated from the cache - jffs2_clear_inode(i); - free(i); - } -} - -static int return_EIO(void) -{ - return -EIO; -} - -#define EIO_ERROR ((void *) (return_EIO)) - -void make_bad_inode(struct inode * inode) { - - // In readinode.c JFFS2 checks whether the inode has appropriate - // content for its marked type - - D2(printf("make_bad_inode\n")); - - inode->i_mode = S_IFREG; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_op = EIO_ERROR; - inode->i_fop = EIO_ERROR; -} - -int is_bad_inode(struct inode * inode) { - - // Called in super.c jffs2_read_super, - // and gc.c jffs2_garbage_collect_pass - - D2(printf("is_bad_inode\n")); - - return (inode->i_op == EIO_ERROR); - /*if(i == NULL) - return 1; - return 0;*/ -} - -cyg_bool jffs2_flash_read(struct jffs2_sb_info *c, cyg_uint32 read_buffer_offset, const size_t size, size_t * return_size, char * write_buffer) { - Cyg_ErrNo err; - cyg_uint32 len = size; - struct super_block *sb = OFNI_BS_2SFFJ( c ); - - //D2(printf("FLASH READ\n")); - //D2(printf("read address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + read_buffer_offset)); - //D2(printf("write address = %x\n", write_buffer)); - //D2(printf("size = %x\n", size)); - err = cyg_io_bread( sb->s_dev, write_buffer, &len, read_buffer_offset ); - - *return_size = (size_t)len; - return ( err != ENOERR ); -} - -cyg_bool jffs2_flash_write(struct jffs2_sb_info *c, cyg_uint32 write_buffer_offset, const size_t size, size_t * return_size, char * read_buffer) { - - Cyg_ErrNo err; - cyg_uint32 len = size; - struct super_block *sb = OFNI_BS_2SFFJ( c ); - - // D2(printf("FLASH WRITE ENABLED!!!\n")); - // D2(printf("write address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + write_buffer_offset)); - // D2(printf("read address = %x\n", read_buffer)); - // D2(printf("size = %x\n", size)); - - err = cyg_io_bwrite( sb->s_dev, read_buffer, &len, write_buffer_offset ); - *return_size = (size_t)len; - - return ( err != ENOERR ); -} - -int -jffs2_flash_writev(struct jffs2_sb_info *c, 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 256 ) - { - // cop out and malloc - unsigned long j; - ssize_t sizetomalloc=0, totvecsize=0; - char *cbuf, *cbufptr; - - for (j=i;j totvecsize ) // in case it was aligned up - thislen = totvecsize; - totlen += thislen; - free(cbuf); - goto writev_out; - } - else - { - // otherwise optimize for the common case - int buf[256/sizeof(int)]; // int, so int aligned - size_t lentowrite; - - lentowrite = vecs[i].iov_len; - // pad up in case its unaligned - lentowrite += sizeof(int)-1; - lentowrite &= ~(sizeof(int)-1); - memcpy( buf, vecs[i].iov_base, lentowrite ); - - ret = jffs2_flash_write(c, to, lentowrite, &thislen, - (char *)&buf ); - if ( thislen > vecs[i].iov_len ) - thislen = vecs[i].iov_len; - } // else - } - else - ret = jffs2_flash_write(c, 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; - } -writev_out: - if (retlen) - *retlen = totlen; - - return ret; -} - -cyg_bool jffs2_flash_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - cyg_io_flash_getconfig_erase_t e; - void *err_addr; - Cyg_ErrNo err; - cyg_uint32 len = sizeof(e); - struct super_block *sb = OFNI_BS_2SFFJ( c ); - - e.offset = jeb->offset; - e.len = c->sector_size; - e.err_address = &err_addr; - - // D2(printf("FLASH ERASE ENABLED!!!\n")); - // D2(printf("erase address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + jeb->offset)); - // D2(printf("size = %x\n", c->sector_size)); - - err = cyg_io_get_config( sb->s_dev, CYG_IO_GET_CONFIG_FLASH_ERASE, - &e, &len ); - - return ( err != ENOERR || e.flasherr != 0 ); -} - -// ------------------------------------------------------------------------- -// EOF jffs2.c diff --git a/packages/fs/jffs2/current/src/jffs2.h b/packages/fs/jffs2/current/src/jffs2.h deleted file mode 100644 --- a/packages/fs/jffs2/current/src/jffs2.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * JFFS2 -- Journalling Flash File System, Version 2. - * - * Copyright (C) 2001, 2002 Red Hat, Inc. - * - * Created by David Woodhouse - * - * For licensing information, see the file 'LICENCE' in this directory. - * - * $Id: jffs2.h,v 1.22 2002/01/09 13:27:58 dwmw2 Exp $ - * - */ - -#ifndef __LINUX_JFFS2_H__ -#define __LINUX_JFFS2_H__ - -#ifdef __ECOS -#include "jffs2port.h" -#else -#include -#endif - -#define JFFS2_SUPER_MAGIC 0x72b6 - -/* Values we may expect to find in the 'magic' field */ -#define JFFS2_OLD_MAGIC_BITMASK 0x1984 -#define JFFS2_MAGIC_BITMASK 0x1985 -#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */ -#define JFFS2_EMPTY_BITMASK 0xffff -#define JFFS2_DIRTY_BITMASK 0x0000 - -/* We only allow a single char for length, and 0xFF is empty flash so - we don't want it confused with a real length. Hence max 254. -*/ -#define JFFS2_MAX_NAME_LEN 254 - -/* How small can we sensibly write nodes? */ -#define JFFS2_MIN_DATA_LEN 128 - -#define JFFS2_COMPR_NONE 0x00 -#define JFFS2_COMPR_ZERO 0x01 -#define JFFS2_COMPR_RTIME 0x02 -#define JFFS2_COMPR_RUBINMIPS 0x03 -#define JFFS2_COMPR_COPY 0x04 -#define JFFS2_COMPR_DYNRUBIN 0x05 -#define JFFS2_COMPR_ZLIB 0x06 -/* Compatibility flags. */ -#define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ -#define JFFS2_NODE_ACCURATE 0x2000 -/* INCOMPAT: Fail to mount the filesystem */ -#define JFFS2_FEATURE_INCOMPAT 0xc000 -/* ROCOMPAT: Mount read-only */ -#define JFFS2_FEATURE_ROCOMPAT 0x8000 -/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */ -#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000 -/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */ -#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000 - -#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) - -// 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 - happen later */ -#define JFFS2_INO_FLAG_USERCOMPR 2 /* User has requested a specific - compression type */ - - -struct jffs2_unknown_node -{ - /* All start like this */ - 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 -{ - 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. */ -/* The uid, gid, atime, mtime and ctime members could be longer, but - are left like this for space efficiency. If and when people decide - they really need them extended, it's simple enough to add support for - a new type of raw node. -*/ -struct jffs2_raw_inode -{ - 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 { - struct jffs2_raw_inode i; - struct jffs2_raw_dirent d; - struct jffs2_unknown_node u; -}; - -#endif /* __LINUX_JFFS2_H__ */ diff --git a/packages/fs/jffs2/current/src/jffs2_fs_i.h b/packages/fs/jffs2/current/src/jffs2_fs_i.h deleted file mode 100644 --- a/packages/fs/jffs2/current/src/jffs2_fs_i.h +++ /dev/null @@ -1,55 +0,0 @@ -/* $Id: jffs2_fs_i.h,v 1.10 2002/01/09 11:44:23 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 -#endif - -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 - before letting GC proceed. Or we'd have to put ugliness - into the GC code so it didn't attempt to obtain the i_sem - for the inode(s) which are already locked */ - struct semaphore sem; - - /* The highest (datanode) version number used for this ino */ - uint32_t highest_version; - - /* List of data fragments which make up the file */ - struct jffs2_node_frag *fraglist; - - /* There may be one datanode which isn't referenced by any of the - above fragments, if it contains a metadata update but no actual - data - or if this is a directory inode */ - /* This also holds the _only_ dnode for symlinks/device nodes, - etc. */ - struct jffs2_full_dnode *metadata; - - /* Directory entries */ - struct jffs2_full_dirent *dents; - - /* 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; - uint16_t flags; - uint8_t usercompr; -}; - -#endif /* _JFFS2_FS_I */ diff --git a/packages/fs/jffs2/current/src/jffs2_fs_sb.h b/packages/fs/jffs2/current/src/jffs2_fs_sb.h deleted file mode 100644 --- a/packages/fs/jffs2/current/src/jffs2_fs_sb.h +++ /dev/null @@ -1,81 +0,0 @@ -/* $Id: jffs2_fs_sb.h,v 1.18 2002/01/09 11:44:23 dwmw2 Exp $ */ - -#ifndef _JFFS2_FS_SB -#define _JFFS2_FS_SB - -#ifdef __ECOS -#include "list.h" -#else -#include -#include -#include -#include -#include -#endif - -#define INOCACHE_HASHSIZE 1 - -#define JFFS2_SB_FLAG_RO 1 - -/* A struct for the overall file system control. Pointers to - jffs2_sb_info structs are named `c' in the source code. - Nee jffs_control -*/ -struct jffs2_sb_info { - struct mtd_info *mtd; - - uint32_t highest_ino; - unsigned int flags; - spinlock_t nodelist_lock; - - // pid_t thread_pid; /* GC thread's PID */ - struct task_struct *gc_task; /* GC task struct */ -#ifndef __ECOS - struct semaphore gc_thread_start; /* GC thread start mutex */ - struct completion gc_thread_exit; /* GC thread exit completion port */ -#endif - // uint32_t gc_minfree_threshold; /* GC trigger thresholds */ - // uint32_t 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. - */ - 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 min_free_size; - // uint32_t max_chunk_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 */ - - struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */ - - struct list_head clean_list; /* Blocks 100% full of clean data */ - struct list_head dirty_list; /* Blocks with some dirty space */ - struct list_head erasing_list; /* Blocks which are currently erasing */ - struct list_head erase_pending_list; /* Blocks which need erasing */ - struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */ - struct list_head free_list; /* Blocks which are free and ready to be used */ - struct list_head bad_list; /* Bad blocks. */ - struct list_head bad_used_list; /* Bad blocks with valid data in. */ - - spinlock_t erase_completion_lock; /* Protect free_list and erasing_list - against erase completion handler */ - wait_queue_head_t erase_wait; /* For waiting for erases to complete */ - struct jffs2_inode_cache *inocache_list[INOCACHE_HASHSIZE]; - spinlock_t inocache_lock; -}; - -#endif /* _JFFS2_FB_SB */ diff --git a/packages/fs/jffs2/current/src/jffs2port.h b/packages/fs/jffs2/current/src/jffs2port.h --- a/packages/fs/jffs2/current/src/jffs2port.h +++ b/packages/fs/jffs2/current/src/jffs2port.h @@ -1,6 +1,8 @@ #ifndef __LINUX_JFFS2PORT_H__ #define __LINUX_JFFS2PORT_H__ +/* $Id: jffs2port.h,v 1.3 2003/01/21 18:13:22 dwmw2 Exp $ */ + #include #include //#include @@ -27,92 +29,12 @@ #include -#include - #include - -#include "list.h" - -// ARM related constants -#define PAGE_SHIFT 0xC -#define PAGE_SIZE (0x1 << PAGE_SHIFT) -#define PAGE_CACHE_SHIFT PAGE_SHIFT -#define PAGE_CACHE_SIZE PAGE_SIZE - -//#define BITS_PER_LONG 32 - +#include +#include +#include // Linux types #define printf diag_printf -#define printk diag_printf -#define BUG() diag_printf("BUG()\n") - -#define uint8_t cyg_uint8 -#define uint16_t cyg_uint16 -#define uint32_t cyg_uint32 -#define loff_t off_t - -#define min(x,y) (x" // system is unusable -#define KERN_ALERT "<1>" // action must be taken immediately -#define KERN_CRIT "<2>" // critical conditions -#define KERN_ERR "<3>" // error conditions -#define KERN_WARNING "<4>" // warning conditions -#define KERN_NOTICE "<5>" // normal but significant condition -#define KERN_INFO "<6>" // informational -#define KERN_DEBUG "<7>" // debug-level messages // Structures used by VFS @@ -127,7 +49,7 @@ struct qstr { #define DNAME_INLINE_LEN 16 struct dentry { - atomic_t d_count; + // atomic_t d_count; unsigned int d_flags; struct inode * d_inode; /* Where the name belongs to - NULL is negative */ struct dentry * d_parent; /* parent directory */ @@ -225,33 +147,12 @@ struct inode_operations { //int (*getattr) (struct dentry *, struct iattr *); }; -/* - * File types - */ -#define DT_UNKNOWN 0 -#define DT_DIR 4 -#define DT_REG 8 struct iovec { void *iov_base; ssize_t iov_len; }; -// FIXME: we must provide some translation so that Linux-created jffs2 -// partitions can be read on eCos and vice versa - -#define S_IFMT 0xF9 // Quick fix for clash on ecos / linux mode bits -#define S_IFDIR __stat_mode_DIR -#define S_IFREG __stat_mode_REG -#define S_IFBLK __stat_mode_BLK -#define S_IFCHR __stat_mode_CHR -#define S_IFLNK 0x101 -#define S_IFSOCK 0x102 -#define S_IFIFO __stat_mode_FIFO -#define S_IRUGO 0x103 -#define S_IXUGO 0x104 -#define S_IRWXUGO 0x105 - // called by JFFS2 @@ -291,8 +192,6 @@ cyg_bool jffs2_flash_write(struct jffs2_ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen); cyg_bool jffs2_flash_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); -static inline void wake_up(wait_queue_head_t *erase_wait) -{ /* Only used for waking up threads blocks on erases. Not used in eCos */ } // calls to JFFS2 diff --git a/packages/fs/jffs2/current/src/list.h b/packages/fs/jffs2/current/src/list.h deleted file mode 100644 --- a/packages/fs/jffs2/current/src/list.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef CYGONCE_FS_JFFS2_LIST_H -#define CYGONCE_FS_JFFS2_LIST_H - -/* - * JFFS2 -- Journalling Flash File System, Version 2. - * - * Copyright (C) 2002 Red Hat, Inc. - * - * Created by Jonathan Larmour - * - * For licensing information, see the file 'LICENCE' in this directory. - * - * $Id: $ - * - */ - -/* -----------------------------------------------------------------------*/ - -/* Doubly linked list implementation to replace the GPL'd one used in - the Linux kernel. */ - -#include -#include - -/* TYPES */ - -struct list_head { - struct list_head *next; - struct list_head *prev; -}; - -/* MACROS */ - -#define INIT_LIST_HEAD( _list_ ) \ -CYG_MACRO_START \ -(_list_)->next = (_list_)->prev = (_list_); \ -CYG_MACRO_END - -/* FUNCTIONS */ - -/* Insert an entry _after_ the specified entry */ -static __inline__ void -list_add( struct list_head *newent, struct list_head *afterthisent ) -{ - struct list_head *next = afterthisent->next; - newent->next = next; - newent->prev = afterthisent; - afterthisent->next = newent; - next->prev = newent; -} /* list_add() */ - -/* Insert an entry _before_ the specified entry */ -static __inline__ void -list_add_tail( struct list_head *newent, struct list_head *beforethisent ) -{ - struct list_head *prev = beforethisent->prev; - newent->prev = prev; - newent->next = beforethisent; - beforethisent->prev = newent; - prev->next = newent; -} /* list_add_tail() */ - -/* Delete the specified entry */ -static __inline__ void -list_del( struct list_head *ent ) -{ - ent->prev->next = ent->next; - ent->next->prev = ent->prev; -} /* list_del() */ - -/* Is this list empty? */ -static __inline__ int -list_empty( struct list_head *list ) -{ - return ( list->next == list ); -} /* list_empty() */ - -/* list_entry - Assuming you have a struct of type _type_ that contains a - list which has the name _member_ in that struct type, then given the - address of that list in the struct, _list_, this returns the address - of the container structure */ - -#define list_entry( _list_, _type_, _member_ ) \ - ((_type_ *)((char *)(_list_)-(char *)(offsetof(_type_,_member_)))) - -/* list_for_each - using _ent_, iterate through list _list_ */ - -#define list_for_each( _ent_, _list_ ) \ - for ( (_ent_) = (_list_)->next; \ - (_ent_) != (_list_); \ - (_ent_) = (_ent_)->next ) - -/* -----------------------------------------------------------------------*/ -#endif /* #ifndef CYGONCE_FS_JFFS2_LIST_H */ -/* EOF list.h */ diff --git a/packages/fs/jffs2/current/src/malloc-ecos.c b/packages/fs/jffs2/current/src/malloc-ecos.c --- a/packages/fs/jffs2/current/src/malloc-ecos.c +++ b/packages/fs/jffs2/current/src/malloc-ecos.c @@ -7,10 +7,11 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id$ + * $Id: malloc-ecos.c,v 1.2 2003/01/09 13:53:44 dwmw2 Exp $ * */ +#include #include "nodelist.h" struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) diff --git a/packages/fs/jffs2/current/src/nodelist.c b/packages/fs/jffs2/current/src/nodelist.c --- a/packages/fs/jffs2/current/src/nodelist.c +++ b/packages/fs/jffs2/current/src/nodelist.c @@ -7,15 +7,18 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.36 2002/01/09 16:52:59 dwmw2 Exp $ + * $Id: nodelist.c,v 1.75 2003/01/21 18:11:28 dwmw2 Exp $ * */ -#ifndef __ECOS #include +#include #include #include -#endif +#include +#include +#include +#include #include "nodelist.h" void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list) @@ -46,7 +49,7 @@ void jffs2_add_fd_to_list(struct jffs2_s *prev = new; out: - D1(while(*list) { + D2(while(*list) { printk(KERN_DEBUG "Dirent \"%s\" (hash 0x%08x, ino #%u\n", (*list)->name, (*list)->nhash, (*list)->ino); list = &(*list)->next; }); @@ -95,7 +98,8 @@ static void jffs2_free_full_dirent_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, - uint32_t *highest_version) + 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; @@ -105,44 +109,63 @@ int jffs2_get_inode_nodes(struct jffs2_s size_t retlen; int err; + *mctime_ver = 0; + D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino)); if (!f->inocache->nodes) { - printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino); + printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)ino); } + + spin_lock(&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) { + if (ref_obsolete(ref)) { /* FIXME: On NAND flash we may need to read these */ - D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref->flash_offset &~3)); + D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref))); continue; } - err = jffs2_flash_read(c, (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(&c->erase_completion_lock); + + cond_resched(); + + /* FIXME: point() */ + err = jffs2_flash_read(c, (ref_offset(ref)), min_t(uint32_t, 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); + printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref)); goto free_out; } /* Check we've managed to read at least the common node header */ - if (retlen < min(ref->totlen, sizeof(node.u))) { + if (retlen < min_t(uint32_t, ref->totlen, sizeof(node.u))) { printk(KERN_WARNING "short read in get_inode_nodes()\n"); err = -EIO; goto free_out; } - switch (node.u.nodetype) { + switch (je16_to_cpu(node.u.nodetype)) { case JFFS2_NODETYPE_DIRENT: - D1(printk(KERN_DEBUG "Node at %08x is a dirent node\n", ref->flash_offset &~3)); + D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref))); + if (ref_flags(ref) == REF_UNCHECKED) { + printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref)); + BUG(); + } if (retlen < sizeof(node.d)) { printk(KERN_WARNING "short read in get_inode_nodes()\n"); err = -EIO; goto free_out; } - if (node.d.version > *highest_version) - *highest_version = node.d.version; - if (ref->flash_offset & 1) { - /* Obsoleted */ - continue; + if (je32_to_cpu(node.d.version) > *highest_version) + *highest_version = je32_to_cpu(node.d.version); + if (ref_obsolete(ref)) { + /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ + printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n", + ref_offset(ref)); + BUG(); } fd = jffs2_alloc_full_dirent(node.d.nsize+1); if (!fd) { @@ -151,22 +174,30 @@ int jffs2_get_inode_nodes(struct jffs2_s } memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1); fd->raw = ref; - fd->version = node.d.version; - fd->ino = node.d.ino; + fd->version = je32_to_cpu(node.d.version); + fd->ino = je32_to_cpu(node.d.ino); fd->type = node.d.type; - /* memcpy as much of the name as possible from the raw - dirent we've already read from the flash - */ + + /* Pick out the mctime of the latest dirent */ + if(fd->version > *mctime_ver) { + *mctime_ver = fd->version; + *latest_mctime = je32_to_cpu(node.d.mctime); + } + + /* memcpy as much of the name as possible from the raw + dirent we've already read from the flash + */ if (retlen > sizeof(struct jffs2_raw_dirent)) - memcpy(&fd->name[0], &node.d.name[0], min((uint32_t)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent)))); + memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent)))); - /* Do we need to copy any more of the name directly - from the flash? - */ + /* Do we need to copy any more of the name directly + from the flash? + */ if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) { + /* FIXME: point() */ int already = retlen - sizeof(struct jffs2_raw_dirent); - err = jffs2_flash_read(c, (ref->flash_offset & ~3) + retlen, + err = jffs2_flash_read(c, (ref_offset(ref)) + retlen, node.d.nsize - already, &retlen, &fd->name[already]); if (!err && retlen != node.d.nsize - already) err = -EIO; @@ -187,21 +218,112 @@ int jffs2_get_inode_nodes(struct jffs2_s break; case JFFS2_NODETYPE_INODE: - D1(printk(KERN_DEBUG "Node at %08x is a data node\n", ref->flash_offset &~3)); + D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref))); if (retlen < sizeof(node.i)) { printk(KERN_WARNING "read too short for dnode\n"); err = -EIO; goto free_out; } - if (node.d.version > *highest_version) - *highest_version = node.i.version; - D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", node.d.version, *highest_version)); + if (je32_to_cpu(node.i.version) > *highest_version) + *highest_version = je32_to_cpu(node.i.version); + D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version)); + + if (ref_obsolete(ref)) { + /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ + printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n", + ref_offset(ref)); + BUG(); + } + + /* If we've never checked the CRCs on this node, check them now. */ + if (ref_flags(ref) == REF_UNCHECKED) { + uint32_t crc; + struct jffs2_eraseblock *jeb; + + crc = crc32(0, &node, sizeof(node.i)-8); + if (crc != je32_to_cpu(node.i.node_crc)) { + printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ref_offset(ref), je32_to_cpu(node.i.node_crc), crc); + jffs2_mark_node_obsolete(c, ref); + spin_lock(&c->erase_completion_lock); + continue; + } - if (ref->flash_offset & 1) { - D1(printk(KERN_DEBUG "obsoleted\n")); - /* Obsoleted */ - continue; + if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) { + unsigned char *buf=NULL; + uint32_t pointed = 0; +#ifndef __ECOS + if (c->mtd->point) { + err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize), + &retlen, &buf); + if (!err && retlen < je32_to_cpu(node.i.csize)) { + D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen)); + c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize)); + } else if (err){ + D1(printk(KERN_DEBUG "MTD point failed %d\n", err)); + } else + pointed = 1; /* succefully pointed to device */ + } +#endif + if(!pointed){ + buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize), + &retlen, buf); + if (!err && retlen != je32_to_cpu(node.i.csize)) + err = -EIO; + if (err) { + kfree(buf); + return err; + } + } + crc = crc32(0, buf, je32_to_cpu(node.i.csize)); + if(!pointed) + kfree(buf); +#ifndef __ECOS + else + c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize)); +#endif + + if (crc != je32_to_cpu(node.i.data_crc)) { + printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ref_offset(ref), je32_to_cpu(node.i.data_crc), crc); + jffs2_mark_node_obsolete(c, ref); + spin_lock(&c->erase_completion_lock); + continue; + } + + } + + /* Mark the node as having been checked and fix the accounting accordingly */ + spin_lock(&c->erase_completion_lock); + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + jeb->used_size += ref->totlen; + jeb->unchecked_size -= ref->totlen; + c->used_size += ref->totlen; + c->unchecked_size -= ref->totlen; + + /* If node covers at least a whole page, or if it starts at the + beginning of a page and runs to the end of the file, or if + it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. + + If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) + when the overlapping node(s) get added to the tree anyway. + */ + if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) || + ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) && + (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) == je32_to_cpu(node.i.isize)))) { + D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref))); + ref->flash_offset = ref_offset(ref) | REF_PRISTINE; + } else { + D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref))); + ref->flash_offset = ref_offset(ref) | REF_NORMAL; + } + spin_unlock(&c->erase_completion_lock); } + tn = jffs2_alloc_tmp_dnode_info(); if (!tn) { D1(printk(KERN_DEBUG "alloc tn failed\n")); @@ -216,33 +338,76 @@ int jffs2_get_inode_nodes(struct jffs2_s jffs2_free_tmp_dnode_info(tn); goto free_out; } - tn->version = node.i.version; - tn->fn->ofs = node.i.offset; - tn->fn->size = node.i.dsize; + tn->version = je32_to_cpu(node.i.version); + tn->fn->ofs = je32_to_cpu(node.i.offset); + /* There was a bug where we wrote hole nodes out with + csize/dsize swapped. Deal with it */ + if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize)) + tn->fn->size = je32_to_cpu(node.i.csize); + else // normal case... + tn->fn->size = je32_to_cpu(node.i.dsize); tn->fn->raw = ref; - D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ref->flash_offset &~3, node.i.version, node.i.offset, node.i.dsize)); + D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", + ref_offset(ref), je32_to_cpu(node.i.version), + je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize))); jffs2_add_tn_to_list(tn, &ret_tn); break; default: - switch(node.u.nodetype & JFFS2_COMPAT_MASK) { + if (ref_flags(ref) == REF_UNCHECKED) { + struct jffs2_eraseblock *jeb; + + printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n", + je16_to_cpu(node.u.nodetype), ref_offset(ref)); + + /* Mark the node as having been checked and fix the accounting accordingly */ + spin_lock(&c->erase_completion_lock); + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + jeb->used_size += ref->totlen; + jeb->unchecked_size -= ref->totlen; + c->used_size += ref->totlen; + c->unchecked_size -= ref->totlen; + + mark_ref_normal(ref); + spin_unlock(&c->erase_completion_lock); + } + node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype)); + if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) { + /* Hmmm. This should have been caught at scan time. */ + printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n", + ref_offset(ref)); + printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n", + je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen), + je32_to_cpu(node.u.hdr_crc)); + jffs2_mark_node_obsolete(c, ref); + } else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_INCOMPAT: - printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); + printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); + /* EEP */ + BUG(); break; case JFFS2_FEATURE_ROCOMPAT: - printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); + printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); + if (!(c->flags & JFFS2_SB_FLAG_RO)) + BUG(); break; case JFFS2_FEATURE_RWCOMPAT_COPY: - printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); + printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); break; case JFFS2_FEATURE_RWCOMPAT_DELETE: - printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); + printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); + jffs2_mark_node_obsolete(c, ref); break; } + } + spin_lock(&c->erase_completion_lock); + } + spin_unlock(&c->erase_completion_lock); *tnp = ret_tn; *fdp = ret_fd; + return 0; free_out: @@ -251,19 +416,30 @@ int jffs2_get_inode_nodes(struct jffs2_s return err; } +void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state) +{ + spin_lock(&c->inocache_lock); + ic->state = state; + wake_up(&c->inocache_wq); + spin_unlock(&c->inocache_lock); +} + +/* During mount, this needs no locking. During normal operation, its + callers want to do other stuff while still holding the inocache_lock. + Rather than introducing special case get_ino_cache functions or + callbacks, we just let the caller do the locking itself. */ + struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino) { struct jffs2_inode_cache *ret; D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino)); - spin_lock (&c->inocache_lock); + 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; @@ -284,6 +460,7 @@ void jffs2_add_ino_cache (struct jffs2_s } new->next = *prev; *prev = new; + spin_unlock(&c->inocache_lock); } @@ -301,6 +478,7 @@ void jffs2_del_ino_cache(struct jffs2_sb if ((*prev) == old) { *prev = old->next; } + spin_unlock(&c->inocache_lock); } @@ -337,3 +515,126 @@ void jffs2_free_raw_node_refs(struct jff } } +struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset) +{ + /* The common case in lookup is that there will be a node + which precisely matches. So we go looking for that first */ + struct rb_node *next; + struct jffs2_node_frag *prev = NULL; + struct jffs2_node_frag *frag = NULL; + + D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset)); + + next = fragtree->rb_node; + + while(next) { + frag = rb_entry(next, struct jffs2_node_frag, rb); + + D2(printk(KERN_DEBUG "Considering frag %d-%d (%p). left %p, right %p\n", + frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right)); + if (frag->ofs + frag->size <= offset) { + D2(printk(KERN_DEBUG "Going right from frag %d-%d, before the region we care about\n", + frag->ofs, frag->ofs+frag->size)); + /* Remember the closest smaller match on the way down */ + if (!prev || frag->ofs > prev->ofs) + prev = frag; + next = frag->rb.rb_right; + } else if (frag->ofs > offset) { + D2(printk(KERN_DEBUG "Going left from frag %d-%d, after the region we care about\n", + frag->ofs, frag->ofs+frag->size)); + next = frag->rb.rb_left; + } else { + D2(printk(KERN_DEBUG "Returning frag %d,%d, matched\n", + frag->ofs, frag->ofs+frag->size)); + return frag; + } + } + + /* Exact match not found. Go back up looking at each parent, + and return the closest smaller one */ + + if (prev) + D2(printk(KERN_DEBUG "No match. Returning frag %d,%d, closest previous\n", + prev->ofs, prev->ofs+prev->size)); + else + D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n")); + + return prev; +} + +/* Pass 'c' argument to indicate that nodes should be marked obsolete as + they're killed. */ +void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c) +{ + struct jffs2_node_frag *frag; + struct jffs2_node_frag *parent; + + if (!root->rb_node) + return; + + frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb)); + + while(frag) { + if (frag->rb.rb_left) { + D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n", + frag, frag->ofs, frag->ofs+frag->size)); + frag = frag_left(frag); + continue; + } + if (frag->rb.rb_right) { + D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n", + frag, frag->ofs, frag->ofs+frag->size)); + frag = frag_right(frag); + continue; + } + + D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n", + frag->ofs, frag->ofs+frag->size, frag->node, + frag->node?frag->node->frags:0)); + + if (frag->node && !(--frag->node->frags)) { + /* Not a hole, and it's the final remaining frag + of this node. Free the node */ + if (c) + jffs2_mark_node_obsolete(c, frag->node->raw); + + jffs2_free_full_dnode(frag->node); + } + parent = frag_parent(frag); + if (parent) { + if (frag_left(parent) == frag) + parent->rb.rb_left = NULL; + else + parent->rb.rb_right = NULL; + } + + jffs2_free_node_frag(frag); + frag = parent; + } +} + +void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base) +{ + struct rb_node *parent = &base->rb; + struct rb_node **link = &parent; + + D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag, + newfrag->ofs, newfrag->ofs+newfrag->size, base)); + + while (*link) { + parent = *link; + base = rb_entry(parent, struct jffs2_node_frag, rb); + + D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs)); + if (newfrag->ofs > base->ofs) + link = &base->rb.rb_right; + else if (newfrag->ofs < base->ofs) + link = &base->rb.rb_left; + else { + printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base); + BUG(); + } + } + + rb_link_node(&newfrag->rb, &base->rb, link); +} diff --git a/packages/fs/jffs2/current/src/nodelist.h b/packages/fs/jffs2/current/src/nodelist.h --- a/packages/fs/jffs2/current/src/nodelist.h +++ b/packages/fs/jffs2/current/src/nodelist.h @@ -7,31 +7,27 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.60 2002/01/24 00:45:11 dwmw2 Exp $ + * $Id: nodelist.h,v 1.92 2003/01/22 14:43:29 dwmw2 Exp $ * */ #ifndef __JFFS2_NODELIST_H__ #define __JFFS2_NODELIST_H__ -#ifdef __ECOS -#include "jffs2port.h" -#include "jffs2.h" -#include "jffs2_fs_sb.h" -#include "jffs2_fs_i.h" -#include "os-ecos.h" -#else #include #include - -#include /* For min/max in older kernels */ +#include #include #include #include + +#ifdef __ECOS +#include "os-ecos.h" +#else +#include /* For min/max in older kernels */ #include "os-linux.h" #endif - #ifndef CONFIG_JFFS2_FS_DEBUG #define CONFIG_JFFS2_FS_DEBUG 2 #endif @@ -62,16 +58,22 @@ 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; - // 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 to play with. So we set the least significant bit to 1 to signify that the node is obsoleted by later nodes. */ +#define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */ +#define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */ +#define REF_PRISTINE 2 /* Completely clean. GC without looking */ +#define REF_NORMAL 3 /* Possibly overlapped. Read the page and write again on GC */ +#define ref_flags(ref) ((ref)->flash_offset & 3) +#define ref_offset(ref) ((ref)->flash_offset & ~3) +#define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE) +#define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0) }; /* @@ -92,19 +94,35 @@ struct jffs2_raw_node_ref_list { a pointer to the first physical node which is part of this inode, too. */ struct jffs2_inode_cache { - struct jffs2_scan_info *scan; /* Used during scan to hold - temporary lists of nodes, and later must be set to + struct jffs2_full_dirent *scan_dents; /* Used during scan to hold + temporary lists of dirents, and later must be set to NULL to mark the end of the raw_node_ref->next_in_ino chain. */ struct jffs2_inode_cache *next; struct jffs2_raw_node_ref *nodes; uint32_t ino; int nlink; + int state; }; +/* Inode states for 'state' above. We need the 'GC' state to prevent + someone from doing a read_inode() while we're moving a 'REF_PRISTINE' + node without going through all the iget() nonsense */ +#define INO_STATE_UNCHECKED 0 /* CRC checks not yet done */ +#define INO_STATE_CHECKING 1 /* CRC checks in progress */ +#define INO_STATE_PRESENT 2 /* In core */ +#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */ +#define INO_STATE_GC 4 /* GCing a 'pristine' node */ +#define INO_STATE_READING 5 /* In read_inode() */ + +#define INOCACHE_HASHSIZE 128 + 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 @@ -150,11 +168,10 @@ struct jffs2_full_dirent */ struct jffs2_node_frag { - struct jffs2_node_frag *next; + struct rb_node rb; struct jffs2_full_dnode *node; /* NULL for holes */ 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 @@ -163,8 +180,10 @@ struct jffs2_eraseblock int bad_count; uint32_t offset; /* of this block in the MTD */ + uint32_t unchecked_size; uint32_t used_size; uint32_t dirty_size; + uint32_t wasted_size; uint32_t free_size; /* Note that sector_size - free_size is the address of the first free space */ struct jffs2_raw_node_ref *first_node; @@ -182,25 +201,28 @@ struct jffs2_eraseblock }; #define ACCT_SANITY_CHECK(c, jeb) do { \ - if (jeb->used_size + jeb->dirty_size + jeb->free_size != c->sector_size) { \ + if (jeb->used_size + jeb->dirty_size + jeb->free_size + jeb->wasted_size + jeb->unchecked_size != c->sector_size) { \ printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \ - printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x != total %08x\n", \ - jeb->free_size, jeb->dirty_size, jeb->used_size, c->sector_size); \ + printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \ + jeb->free_size, jeb->dirty_size, jeb->used_size, jeb->wasted_size, jeb->unchecked_size, c->sector_size); \ BUG(); \ } \ - if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size != c->flash_size) { \ + if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \ printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \ - printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x != total %08x\n", \ - c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->flash_size); \ + printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x + wasted %08x + unchecked %08x != total %08x\n", \ + c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \ BUG(); \ } \ } while(0) #define ACCT_PARANOIA_CHECK(jeb) do { \ uint32_t my_used_size = 0; \ + uint32_t my_unchecked_size = 0; \ struct jffs2_raw_node_ref *ref2 = jeb->first_node; \ while (ref2) { \ - if (!(ref2->flash_offset & 1)) \ + if (ref_flags(ref2) == REF_UNCHECKED) \ + my_unchecked_size += ref2->totlen; \ + else if (!ref_obsolete(ref2)) \ my_used_size += ref2->totlen; \ ref2 = ref2->next_phys; \ } \ @@ -208,6 +230,10 @@ struct jffs2_eraseblock printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \ BUG(); \ } \ + if (my_unchecked_size != jeb->unchecked_size) { \ + printk(KERN_NOTICE "Calculated unchecked size %08x != stored unchecked size %08x\n", my_unchecked_size, jeb->unchecked_size); \ + BUG(); \ + } \ } while(0) #define ALLOC_NORMAL 0 /* Normal allocation */ @@ -216,12 +242,18 @@ struct jffs2_eraseblock #define JFFS2_RESERVED_BLOCKS_BASE 3 /* Number of free blocks there must be before we... */ #define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2) /* ... allow a normal filesystem write */ -#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... allow a normal filesystem deletion */ +#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE) /* ... allow a normal filesystem deletion */ #define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3) /* ... wake up the GC thread */ #define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... pick a block from the bad_list to GC */ #define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE) /* ... merge pages when garbage collecting */ +/* How much dirty space before it goes on the very_dirty_list */ +#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2)) + +/* check if dirty space is more than 255 Byte */ +#define ISDIRTY(size) ((size) > sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN) + #define PAD(x) (((x)+3)&~3) static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw) @@ -233,28 +265,55 @@ static inline int jffs2_raw_ref_to_inum( return ((struct jffs2_inode_cache *)raw)->ino; } +static inline struct jffs2_node_frag *frag_first(struct rb_root *root) +{ + struct rb_node *node = root->rb_node; + + if (!node) + return NULL; + while(node->rb_left) + node = node->rb_left; + return rb_entry(node, struct jffs2_node_frag, rb); +} +#define rb_parent(rb) ((rb)->rb_parent) +#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb) +#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb) +#define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb) +#define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb) +#define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb) +#define frag_erase(frag, list) rb_erase(&frag->rb, list); + /* nodelist.c */ D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)); void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); 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, - uint32_t *highest_version); + uint32_t *highest_version, uint32_t *latest_mctime, + uint32_t *mctime_ver); +void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state); struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, 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); void jffs2_free_ino_caches(struct jffs2_sb_info *c); void jffs2_free_raw_node_refs(struct jffs2_sb_info *c); +struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset); +void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete); +void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base); +struct rb_node *rb_next(struct rb_node *); +struct rb_node *rb_prev(struct rb_node *); +void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); /* nodemgmt.c */ int jffs2_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); +int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new); void jffs2_complete_reservation(struct jffs2_sb_info *c); void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw); +void jffs2_dump_block_lists(struct jffs2_sb_info *c); /* write.c */ -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 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, @@ -266,13 +325,11 @@ int jffs2_do_link (struct jffs2_sb_info /* readinode.c */ -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); +void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); -void jffs2_read_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_clear_inode (struct inode *); +int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); /* malloc.c */ @@ -314,6 +371,7 @@ int jffs2_decompress(unsigned char compr /* scan.c */ int jffs2_scan_medium(struct jffs2_sb_info *c); +void jffs2_rotate_lists(struct jffs2_sb_info *c); /* build.c */ int jffs2_do_mount_fs(struct jffs2_sb_info *c); @@ -321,7 +379,18 @@ int jffs2_do_mount_fs(struct jffs2_sb_in /* erase.c */ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); 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__ */ diff --git a/packages/fs/jffs2/current/src/nodemgmt.c b/packages/fs/jffs2/current/src/nodemgmt.c --- a/packages/fs/jffs2/current/src/nodemgmt.c +++ b/packages/fs/jffs2/current/src/nodemgmt.c @@ -7,16 +7,15 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.52 2002/01/19 23:16:41 dwmw2 Exp $ + * $Id: nodemgmt.c,v 1.92 2003/01/22 14:40:26 dwmw2 Exp $ * */ -#ifndef __ECOS #include #include #include -#include -#endif +#include +#include /* For cond_resched() */ #include "nodelist.h" /** @@ -56,7 +55,7 @@ int jffs2_reserve_space(struct jffs2_sb_ D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); /* this needs a little more thought */ while(ret == -EAGAIN) { @@ -64,28 +63,30 @@ int jffs2_reserve_space(struct jffs2_sb_ int ret; up(&c->alloc_sem); - if (c->dirty_size < c->sector_size) { - D1(printk(KERN_DEBUG "Short on space, but total dirty size 0x%08x < sector size 0x%08x, so -ENOSPC\n", c->dirty_size, c->sector_size)); - spin_unlock_bh(&c->erase_completion_lock); + + if (c->dirty_size + c->unchecked_size < c->sector_size) { + D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < sector size 0x%08x, returning -ENOSPC\n", + c->dirty_size, c->unchecked_size, c->sector_size)); + spin_unlock(&c->erase_completion_lock); return -ENOSPC; } - D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", - c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, - c->free_size + c->dirty_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); - spin_unlock_bh(&c->erase_completion_lock); + + D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", + c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size, + c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); + spin_unlock(&c->erase_completion_lock); ret = jffs2_garbage_collect_pass(c); if (ret) return ret; -#ifndef __ECOS - if (current->need_resched) - schedule(); + + cond_resched(); if (signal_pending(current)) return -EINTR; -#endif + down(&c->alloc_sem); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); } ret = jffs2_do_reserve_space(c, minsize, ofs, len); @@ -93,7 +94,7 @@ int jffs2_reserve_space(struct jffs2_sb_ D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); } } - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); if (ret) up(&c->alloc_sem); return ret; @@ -106,14 +107,14 @@ int jffs2_reserve_space_gc(struct jffs2_ D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize)); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); while(ret == -EAGAIN) { ret = jffs2_do_reserve_space(c, minsize, ofs, len); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); } } - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); return ret; } @@ -125,13 +126,39 @@ static int jffs2_do_reserve_space(struct restart: if (jeb && minsize > jeb->free_size) { /* Skip the end of this block and file it as having some dirty space */ - c->dirty_size += jeb->free_size; + /* If there's a pending write to it, flush now */ + if (c->wbuf_len) { + spin_unlock(&c->erase_completion_lock); + D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); + jffs2_flush_wbuf(c, 1); + spin_lock(&c->erase_completion_lock); + /* We know nobody's going to have changed nextblock. Just continue */ + } + c->wasted_size += jeb->free_size; c->free_size -= jeb->free_size; - jeb->dirty_size += jeb->free_size; + jeb->wasted_size += jeb->free_size; jeb->free_size = 0; - D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", + + /* Check, if we have a dirty block now, or if it was dirty already */ + if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { + c->dirty_size += jeb->wasted_size; + c->wasted_size -= jeb->wasted_size; + jeb->dirty_size += jeb->wasted_size; + jeb->wasted_size = 0; + if (VERYDIRTY(c, jeb->dirty_size)) { + D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); + list_add_tail(&jeb->list, &c->very_dirty_list); + } else { + D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); + list_add_tail(&jeb->list, &c->dirty_list); + } + } else { + D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); - list_add_tail(&jeb->list, &c->dirty_list); + list_add_tail(&jeb->list, &c->clean_list); + } c->nextblock = jeb = NULL; } @@ -143,12 +170,36 @@ static int jffs2_do_reserve_space(struct 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++; + jffs2_erase_pending_trigger(c); + D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n", + ejeb->offset)); + } + + if (!c->nr_erasing_blocks && + !list_empty(&c->erasable_pending_wbuf_list)) { + D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); + /* c->nextblock is NULL, no update to c->nextblock allowed */ + spin_unlock(&c->erase_completion_lock); + jffs2_flush_wbuf(c, 1); + spin_lock(&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 @@ -157,20 +208,20 @@ static int jffs2_do_reserve_space(struct /* In eCos, we don't have a handy kernel thread doing the erases for us. We do them ourselves right now. */ jffs2_erase_pending_blocks(c); - jffs2_mark_erased_blocks(c); #else 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); } - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); schedule(); remove_wait_queue(&c->erase_wait, &wait); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); if (signal_pending(current)) { return -EINTR; } @@ -185,7 +236,8 @@ static int jffs2_do_reserve_space(struct 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)) { + + if (jeb->free_size != c->sector_size - c->cleanmarker_size) { printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size); goto restart; } @@ -194,6 +246,20 @@ static int jffs2_do_reserve_space(struct enough space */ *ofs = jeb->offset + (c->sector_size - jeb->free_size); *len = jeb->free_size; + + if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size && + !jeb->first_node->next_in_ino) { + /* Only node in it beforehand was a CLEANMARKER node (we think). + So mark it obsolete now that there's going to be another node + in the block. This will reduce used_size to zero but We've + already set c->nextblock so that jffs2_mark_node_obsolete() + won't try to refile it to the dirty_list. + */ + spin_unlock(&c->erase_completion_lock); + jffs2_mark_node_obsolete(c, jeb->first_node); + spin_lock(&c->erase_completion_lock); + } + D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs)); return 0; } @@ -201,9 +267,9 @@ static int jffs2_do_reserve_space(struct /** * 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. @@ -211,47 +277,56 @@ static int jffs2_do_reserve_space(struct * 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, uint32_t len, int dirty) +int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new) { struct jffs2_eraseblock *jeb; + uint32_t len = new->totlen; - len = PAD(len); - jeb = &c->blocks[(new->flash_offset & ~3) / 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)); + jeb = &c->blocks[new->flash_offset / c->sector_size]; + D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len)); #if 1 - if (jeb != c->nextblock || (new->flash_offset & ~3) != jeb->offset + (c->sector_size - jeb->free_size)) { + if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) { printk(KERN_WARNING "argh. node added in wrong place\n"); jffs2_free_raw_node_ref(new); return -EINVAL; } #endif + spin_lock(&c->erase_completion_lock); + if (!jeb->first_node) jeb->first_node = new; if (jeb->last_node) jeb->last_node->next_phys = new; jeb->last_node = new; - spin_lock_bh(&c->erase_completion_lock); jeb->free_size -= len; c->free_size -= len; - if (dirty) { - new->flash_offset |= 1; + if (ref_obsolete(new)) { jeb->dirty_size += len; c->dirty_size += len; } else { 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(&c->erase_completion_lock); + jffs2_flush_wbuf(c, 1); + spin_lock(&c->erase_completion_lock); + } + list_add_tail(&jeb->list, &c->clean_list); c->nextblock = NULL; } ACCT_SANITY_CHECK(c,jeb); - ACCT_PARANOIA_CHECK(jeb); + D1(ACCT_PARANOIA_CHECK(jeb)); + + spin_unlock(&c->erase_completion_lock); return 0; } @@ -276,8 +351,8 @@ void jffs2_mark_node_obsolete(struct jff printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n"); return; } - if (ref->flash_offset & 1) { - D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref->flash_offset &~3)); + if (ref_obsolete(ref)) { + D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref))); return; } blocknr = ref->flash_offset / c->sector_size; @@ -286,82 +361,319 @@ void jffs2_mark_node_obsolete(struct jff BUG(); } jeb = &c->blocks[blocknr]; - if (jeb->used_size < ref->totlen) { - printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n", - ref->totlen, blocknr, ref->flash_offset, jeb->used_size); - BUG(); + + spin_lock(&c->erase_completion_lock); + + if (ref_flags(ref) == REF_UNCHECKED) { + D1(if (unlikely(jeb->unchecked_size < ref->totlen)) { + printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n", + ref->totlen, blocknr, ref->flash_offset, jeb->used_size); + BUG(); + }) + D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen)); + jeb->unchecked_size -= ref->totlen; + c->unchecked_size -= ref->totlen; + } else { + D1(if (unlikely(jeb->used_size < ref->totlen)) { + printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n", + ref->totlen, blocknr, ref->flash_offset, jeb->used_size); + BUG(); + }) + D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen)); + jeb->used_size -= ref->totlen; + c->used_size -= ref->totlen; } - spin_lock_bh(&c->erase_completion_lock); - jeb->used_size -= ref->totlen; - jeb->dirty_size += ref->totlen; - c->used_size -= ref->totlen; - c->dirty_size += ref->totlen; - ref->flash_offset |= 1; + if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) { + D1(printk("Dirtying\n")); + jeb->dirty_size += ref->totlen + jeb->wasted_size; + c->dirty_size += ref->totlen + jeb->wasted_size; + c->wasted_size -= jeb->wasted_size; + jeb->wasted_size = 0; + } else { + D1(printk("Wasting\n")); + jeb->wasted_size += ref->totlen; + c->wasted_size += ref->totlen; + } + ref->flash_offset = ref_offset(ref) | REF_OBSOLETE; ACCT_SANITY_CHECK(c, jeb); - ACCT_PARANOIA_CHECK(jeb); + D1(ACCT_PARANOIA_CHECK(jeb)); + + if (c->flags & JFFS2_SB_FLAG_MOUNTING) { + /* Mount in progress. Don't muck about with the block + lists because they're not ready yet, and don't actually + obliterate nodes that look obsolete. If they weren't + marked obsolete on the flash at the time they _became_ + obsolete, there was probably a reason for that. */ + spin_unlock(&c->erase_completion_lock); + return; + } if (jeb == c->nextblock) { D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset)); + } else if (!jeb->used_size && !jeb->unchecked_size) { + if (jeb == c->gcblock) { + D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset)); + c->gcblock = NULL; + } else { + D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset)); + list_del(&jeb->list); + } + if (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); +#if 0 /* This check was added to allow us to find places where we added nodes to the lists + after dropping the alloc_sem, and it did that just fine. But it also caused us to + lock the alloc_sem in other places, like clear_inode(), when we wouldn't otherwise + have needed to. So I suspect it's outlived its usefulness. Thomas? */ + + /* 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(); + } +#endif + } else { + if (jiffies & 127) { + /* Most of the time, we just erase it immediately. Otherwise we + spend ages scanning it on mount, etc. */ + D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); + list_add_tail(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_erase_pending_trigger(c); + } else { + /* Sometimes, however, we leave it elsewhere so it doesn't get + immediately reused, and we spread the load a bit. */ + D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); + list_add_tail(&jeb->list, &c->erasable_list); + } + } + D1(printk(KERN_DEBUG "Done OK\n")); } else if (jeb == c->gcblock) { - D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty/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) { - 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; - D1(printk(KERN_DEBUG "Done OK\n")); -#endif - } else if (jeb->dirty_size == ref->totlen) { + D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset)); + } else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - 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); + } else if (VERYDIRTY(c, jeb->dirty_size) && + !VERYDIRTY(c, jeb->dirty_size - ref->totlen)) { + D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset)); + list_del(&jeb->list); + D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n")); + list_add_tail(&jeb->list, &c->very_dirty_list); + } else { + D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n", + jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); + } + + spin_unlock(&c->erase_completion_lock); if (!jffs2_can_mark_obsolete(c)) return; if (jffs2_is_readonly(c)) return; - D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref->flash_offset &~3)); - ret = jffs2_flash_read(c, ref->flash_offset &~3, sizeof(n), &retlen, (char *)&n); + D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref))); + ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); if (ret) { - printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret); + printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); + return; + } + if (retlen != sizeof(n)) { + printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); + return; + } + if (PAD(je32_to_cpu(n.totlen)) != PAD(ref->totlen)) { + printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref->totlen); + return; + } + if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) { + D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref_offset(ref), je16_to_cpu(n.nodetype))); + return; + } + /* XXX FIXME: This is ugly now */ + n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE); + ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); + if (ret) { + printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); return; } if (retlen != sizeof(n)) { - printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen); - return; - } - if (PAD(n.totlen) != PAD(ref->totlen)) { - printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", n.totlen, ref->totlen); - return; - } - if (!(n.nodetype & JFFS2_NODE_ACCURATE)) { - D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref->flash_offset &~3, n.nodetype)); - return; - } - n.nodetype &= ~JFFS2_NODE_ACCURATE; - 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; - } - if (retlen != sizeof(n)) { - printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen); + printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); return; } } + +#if CONFIG_JFFS2_FS_DEBUG > 0 +void jffs2_dump_block_lists(struct jffs2_sb_info *c) +{ + + + printk(KERN_DEBUG "jffs2_dump_block_lists:\n"); + printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size); + printk(KERN_DEBUG "used_size: %08x\n", c->used_size); + printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size); + printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size); + printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size); + printk(KERN_DEBUG "free_size: %08x\n", c->free_size); + printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size); + printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size); + printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size); + printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE); + + if (c->nextblock) { + printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size); + } else { + printk(KERN_DEBUG "nextblock: NULL\n"); + } + if (c->gcblock) { + printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size); + } else { + printk(KERN_DEBUG "gcblock: NULL\n"); + } + if (list_empty(&c->clean_list)) { + printk(KERN_DEBUG "clean_list: empty\n"); + } else { + struct list_head *this; + int numblocks = 0; + uint32_t dirty = 0; + + list_for_each(this, &c->clean_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + numblocks ++; + dirty += jeb->wasted_size; + printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); + } + printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks); + } + if (list_empty(&c->very_dirty_list)) { + printk(KERN_DEBUG "very_dirty_list: empty\n"); + } else { + struct list_head *this; + int numblocks = 0; + uint32_t dirty = 0; + + list_for_each(this, &c->very_dirty_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + numblocks ++; + dirty += jeb->dirty_size; + printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); + } + printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", + numblocks, dirty, dirty / numblocks); + } + if (list_empty(&c->dirty_list)) { + printk(KERN_DEBUG "dirty_list: empty\n"); + } else { + struct list_head *this; + int numblocks = 0; + uint32_t dirty = 0; + + list_for_each(this, &c->dirty_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + numblocks ++; + dirty += jeb->dirty_size; + printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); + } + printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", + numblocks, dirty, dirty / numblocks); + } + if (list_empty(&c->erasable_list)) { + printk(KERN_DEBUG "erasable_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erasable_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); + } + } + if (list_empty(&c->erasing_list)) { + printk(KERN_DEBUG "erasing_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erasing_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); + } + } + if (list_empty(&c->erase_pending_list)) { + printk(KERN_DEBUG "erase_pending_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erase_pending_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); + } + } + if (list_empty(&c->erasable_pending_wbuf_list)) { + printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erasable_pending_wbuf_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "erase_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); + } + } + if (list_empty(&c->free_list)) { + printk(KERN_DEBUG "free_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->free_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); + } + } + if (list_empty(&c->bad_list)) { + printk(KERN_DEBUG "bad_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->bad_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); + } + } + if (list_empty(&c->bad_used_list)) { + printk(KERN_DEBUG "bad_used_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->bad_used_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); + } + } +} +#endif /* CONFIG_JFFS2_FS_DEBUG */ diff --git a/packages/fs/jffs2/current/src/os-ecos.h b/packages/fs/jffs2/current/src/os-ecos.h --- a/packages/fs/jffs2/current/src/os-ecos.h +++ b/packages/fs/jffs2/current/src/os-ecos.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id$ + * $Id: os-ecos.h,v 1.5 2003/01/21 18:14:27 dwmw2 Exp $ * */ @@ -15,9 +15,95 @@ #define __JFFS2_OS_ECOS_H__ #include +#include +#include +#include +#include "jffs2port.h" #define CONFIG_JFFS2_FS_DEBUG 0 +static inline uint32_t os_to_jffs2_mode(uint32_t osmode) +{ + uint32_t jmode = ((osmode & S_IRUSR)?00400:0) | + ((osmode & S_IWUSR)?00200:0) | + ((osmode & S_IXUSR)?00100:0) | + ((osmode & S_IRGRP)?00040:0) | + ((osmode & S_IWGRP)?00020:0) | + ((osmode & S_IXGRP)?00010:0) | + ((osmode & S_IROTH)?00004:0) | + ((osmode & S_IWOTH)?00002:0) | + ((osmode & S_IXOTH)?00001:0); + + switch (osmode & S_IFMT) { + case S_IFSOCK: + return jmode | 0140000; + case S_IFLNK: + return jmode | 0120000; + case S_IFREG: + return jmode | 0100000; + case S_IFBLK: + return jmode | 0060000; + case S_IFDIR: + return jmode | 0040000; + case S_IFCHR: + return jmode | 0020000; + case S_IFIFO: + return jmode | 0010000; + case S_ISUID: + return jmode | 0004000; + case S_ISGID: + return jmode | 0002000; +#ifdef S_ISVTX + case S_ISVTX: + return jmode | 0001000; +#endif + } + printf("os_to_jffs2_mode() cannot convert 0x%x\n", osmode); + BUG(); + return 0; +} + +static inline uint32_t jffs2_to_os_mode (uint32_t jmode) +{ + uint32_t osmode = ((jmode & 00400)?S_IRUSR:0) | + ((jmode & 00200)?S_IWUSR:0) | + ((jmode & 00100)?S_IXUSR:0) | + ((jmode & 00040)?S_IRGRP:0) | + ((jmode & 00020)?S_IWGRP:0) | + ((jmode & 00010)?S_IXGRP:0) | + ((jmode & 00004)?S_IROTH:0) | + ((jmode & 00002)?S_IWOTH:0) | + ((jmode & 00001)?S_IXOTH:0); + + switch(jmode & 00170000) { + case 0140000: + return osmode | S_IFSOCK; + case 0120000: + return osmode | S_IFLNK; + case 0100000: + return osmode | S_IFREG; + case 0060000: + return osmode | S_IFBLK; + case 0040000: + return osmode | S_IFDIR; + case 0020000: + return osmode | S_IFCHR; + case 0010000: + return osmode | S_IFIFO; + case 0004000: + return osmode | S_ISUID; + case 0002000: + return osmode | S_ISGID; +#ifdef S_ISVTX + case 0001000: + return osmode | S_ISVTX; +#endif + } + printf("jffs2_to_os_mode() cannot convert 0x%x\n", osmode); + BUG(); + return 0; +} + /* Read-only operation not currently implemented on eCos */ #define jffs2_is_readonly(c) (0) @@ -39,6 +125,11 @@ #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))) + +//#define ITIME(sec) (sec) +//#define I_SEC(x) (x) +#define get_seconds cyg_timestamp + struct inode { //struct list_head i_hash; //struct list_head i_list; @@ -106,6 +197,9 @@ struct super_block { cyg_io_handle_t s_dev; }; +#define sleep_on_spinunlock(wq, sl) do { ; } while(0) +#define EBADFD 32767 + /* background.c */ static inline void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) { @@ -131,5 +225,28 @@ int jffs2_prepare_write (struct inode *d //int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned); int jffs2_commit_write (struct inode *d_inode, struct page *pg, unsigned start, unsigned end); +#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_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_writev +#define jffs2_wbuf_timeout NULL +#define jffs2_wbuf_process NULL +#else +#error no nand yet +#endif +struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri); +void jffs2_clear_inode (struct inode *inode); +void jffs2_read_inode (struct inode *inode); + +static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) +{ + memset(f, 0, sizeof(*f)); + init_MUTEX_LOCKED(&f->sem); +} #endif /* __JFFS2_OS_ECOS_H__ */ diff --git a/packages/fs/jffs2/current/src/pushpull.h b/packages/fs/jffs2/current/src/pushpull.h --- a/packages/fs/jffs2/current/src/pushpull.h +++ b/packages/fs/jffs2/current/src/pushpull.h @@ -1,24 +1,20 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001 Red Hat, Inc. + * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: pushpull.h,v 1.6 2002/01/25 01:49:26 dwmw2 Exp $ + * $Id: pushpull.h,v 1.8 2002/05/20 14:56:38 dwmw2 Exp $ * */ #ifndef __PUSHPULL_H__ #define __PUSHPULL_H__ -#ifndef __ECOS #include -#else -#include -#endif struct pushpull { unsigned char *buf; diff --git a/packages/fs/jffs2/current/src/read.c b/packages/fs/jffs2/current/src/read.c --- a/packages/fs/jffs2/current/src/read.c +++ b/packages/fs/jffs2/current/src/read.c @@ -7,17 +7,16 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: read.c,v 1.19 2002/01/14 23:30:26 dwmw2 Exp $ + * $Id: read.c,v 1.31 2003/01/14 14:06:22 dwmw2 Exp $ * */ -#ifndef __ECOS #include #include +#include +#include #include -#endif #include "nodelist.h" -#include "crc32.h" int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len) { @@ -32,29 +31,41 @@ int jffs2_read_dnode(struct jffs2_sb_inf if (!ri) return -ENOMEM; - ret = jffs2_flash_read(c, fd->raw->flash_offset & ~3, sizeof(*ri), &readlen, (char *)ri); + ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri); if (ret) { jffs2_free_raw_inode(ri); - printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", fd->raw->flash_offset & ~3, ret); + printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret); return ret; } if (readlen != sizeof(*ri)) { jffs2_free_raw_inode(ri); - printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%x bytes, got 0x%x\n", - fd->raw->flash_offset & ~3, sizeof(*ri), readlen); + printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n", + ref_offset(fd->raw), sizeof(*ri), readlen); return -EIO; } crc = crc32(0, ri, sizeof(*ri)-8); - D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", fd->raw->flash_offset & ~3, ri->node_crc, crc, ri->dsize, ri->csize, ri->offset, buf)); - if (crc != ri->node_crc) { - printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ri->node_crc, crc, fd->raw->flash_offset & ~3); + D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", + ref_offset(fd->raw), je32_to_cpu(ri->node_crc), + crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize), + je32_to_cpu(ri->offset), buf)); + if (crc != je32_to_cpu(ri->node_crc)) { + printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", + je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw)); ret = -EIO; goto out_ri; } + /* There was a bug where we wrote hole nodes out with csize/dsize + swapped. Deal with it */ + if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) && + je32_to_cpu(ri->csize)) { + ri->dsize = ri->csize; + ri->csize = cpu_to_je32(0); + } - D1(if(ofs + len > ri->dsize) { - printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", len, ofs, ri->dsize); + D1(if(ofs + len > je32_to_cpu(ri->dsize)) { + printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", + len, ofs, je32_to_cpu(ri->dsize)); ret = -EINVAL; goto out_ri; }); @@ -71,18 +82,18 @@ int jffs2_read_dnode(struct jffs2_sb_inf Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy */ - if (ri->compr == JFFS2_COMPR_NONE && len == ri->dsize) { + if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) { readbuf = buf; } else { - readbuf = kmalloc(ri->csize, GFP_KERNEL); + readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL); if (!readbuf) { ret = -ENOMEM; goto out_ri; } } if (ri->compr != JFFS2_COMPR_NONE) { - if (len < ri->dsize) { - decomprbuf = kmalloc(ri->dsize, GFP_KERNEL); + if (len < je32_to_cpu(ri->dsize)) { + decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL); if (!decomprbuf) { ret = -ENOMEM; goto out_readbuf; @@ -94,31 +105,35 @@ int jffs2_read_dnode(struct jffs2_sb_inf decomprbuf = readbuf; } - D2(printk(KERN_DEBUG "Read %d bytes to %p\n", ri->csize, readbuf)); - ret = jffs2_flash_read(c, (fd->raw->flash_offset &~3) + sizeof(*ri), ri->csize, &readlen, readbuf); + D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize), + readbuf)); + ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri), + je32_to_cpu(ri->csize), &readlen, readbuf); - if (!ret && readlen != ri->csize) + if (!ret && readlen != je32_to_cpu(ri->csize)) ret = -EIO; if (ret) goto out_decomprbuf; - crc = crc32(0, readbuf, ri->csize); - if (crc != ri->data_crc) { - printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", ri->data_crc, crc, fd->raw->flash_offset & ~3); + crc = crc32(0, readbuf, je32_to_cpu(ri->csize)); + if (crc != je32_to_cpu(ri->data_crc)) { + printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", + je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw)); ret = -EIO; goto out_decomprbuf; } D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc)); if (ri->compr != JFFS2_COMPR_NONE) { - D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", ri->csize, readbuf, ri->dsize, decomprbuf)); - ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, ri->csize, ri->dsize); + D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", + je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); + ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize)); if (ret) { printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret); goto out_decomprbuf; } } - if (len < ri->dsize) { + if (len < je32_to_cpu(ri->dsize)) { memcpy(buf, decomprbuf+ofs, len); } out_decomprbuf: @@ -137,16 +152,14 @@ int jffs2_read_inode_range(struct jffs2_ unsigned char *buf, uint32_t offset, uint32_t len) { uint32_t end = offset + len; - struct jffs2_node_frag *frag = f->fraglist; + struct jffs2_node_frag *frag; int ret; D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n", f->inocache->ino, offset, offset+len)); - 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; - } + frag = jffs2_lookup_node_frag(&f->fragtree, offset); + /* XXX FIXME: Where a single physical node actually shows up in two frags, we read it twice. Don't do that. */ /* Now we're pointing at the first frag which overlaps our page */ @@ -164,30 +177,39 @@ int jffs2_read_inode_range(struct jffs2_ 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; + frag = frag_next(frag); continue; } else { uint32_t readlen; - uint32_t frag_ofs = offset - frag->ofs; /* Offset within this frag from which we want to start reading */ - readlen = min(frag->size - frag_ofs, end - offset); - D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs+frag_ofs, frag->ofs+frag_ofs+readlen, frag->node->raw->flash_offset & ~3)); - ret = jffs2_read_dnode(c, frag->node, buf, frag->ofs + frag_ofs - frag->node->ofs, readlen); + uint32_t fragofs; /* offset within the frag to start reading */ + + fragofs = offset - frag->ofs; + readlen = min(frag->size - fragofs, end - offset); + D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n", + frag->ofs+fragofs, frag->ofs+fragofs+readlen, + ref_offset(frag->node->raw), ref_flags(frag->node->raw))); + ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen); D2(printk(KERN_DEBUG "node read done\n")); if (ret) { D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret)); - memset(buf, 0, frag->size - frag_ofs); + memset(buf, 0, readlen); return ret; } - - buf += frag->size - frag_ofs; - offset += frag->size - frag_ofs; - frag = frag->next; + buf += readlen; + offset += readlen; + frag = frag_next(frag); D2(printk(KERN_DEBUG "node read was OK. Looping\n")); } } diff --git a/packages/fs/jffs2/current/src/readinode.c b/packages/fs/jffs2/current/src/readinode.c --- a/packages/fs/jffs2/current/src/readinode.c +++ b/packages/fs/jffs2/current/src/readinode.c @@ -7,51 +7,56 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.64 2002/01/09 13:25:58 dwmw2 Exp $ + * $Id: readinode.c,v 1.103 2003/01/22 14:43:29 dwmw2 Exp $ * */ -/* Given an inode, probably with existing list of fragments, add the new node - * to the fragment list. - */ - -#ifndef __ECOS #include #include #include +#include +#include #include -#endif +#include #include "nodelist.h" -#include "crc32.h" +static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag); -D1(void jffs2_print_frag_list(struct jffs2_inode_info *f) +#if CONFIG_JFFS2_FS_DEBUG >= 1 +static void jffs2_print_fragtree(struct rb_root *list, int permitbug) { - struct jffs2_node_frag *this = f->fraglist; + struct jffs2_node_frag *this = frag_first(list); + uint32_t lastofs = 0; + int buggy = 0; while(this) { if (this->node) - printk(KERN_DEBUG "frag %04x-%04x: 0x%08x on flash (*%p->%p)\n", this->ofs, this->ofs+this->size, this->node->raw->flash_offset &~3, this, this->next); + printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n", + this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw), + this, frag_left(this), frag_right(this), frag_parent(this)); else - printk(KERN_DEBUG "frag %04x-%04x: hole (*%p->%p)\n", this->ofs, this->ofs+this->size, this, this->next); - this = this->next; + printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs, + this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this)); + if (this->ofs != lastofs) + buggy = 1; + lastofs = this->ofs+this->size; + this = frag_next(this); } - if (f->metadata) { - printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3); + if (buggy && !permitbug) { + printk(KERN_CRIT "Frag tree got a hole in it\n"); + BUG(); } -}) - +} -int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) +void jffs2_print_frag_list(struct jffs2_inode_info *f) { - int ret; - D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn)); + jffs2_print_fragtree(&f->fragtree, 0); - ret = jffs2_add_full_dnode_to_fraglist(c, &f->fraglist, fn); - - D2(jffs2_print_frag_list(f)); - return ret; + if (f->metadata) { + printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); + } } +#endif /* D1 */ static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this) { @@ -60,42 +65,38 @@ static void jffs2_obsolete_node_frag(str if (!this->node->frags) { /* The node has no valid frags left. It's totally obsoleted */ D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n", - this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size)); + ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size)); jffs2_mark_node_obsolete(c, this->node->raw); jffs2_free_full_dnode(this->node); } else { - D2(printk(KERN_DEBUG "Not marking old node @0x%08x (0x%04x-0x%04x) obsolete. frags is %d\n", - this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size, + D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n", + ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size, this->node->frags)); + mark_ref_normal(this->node->raw); } } jffs2_free_node_frag(this); } -/* Doesn't set inode->i_size */ -int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn) +/* 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) { - - struct jffs2_node_frag *this, **prev, *old; - struct jffs2_node_frag *newfrag, *newfrag2; - uint32_t lastend = 0; + int ret; + struct jffs2_node_frag *newfrag; + D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn)); newfrag = jffs2_alloc_node_frag(); - if (!newfrag) { + if (unlikely(!newfrag)) return -ENOMEM; - } - D2(if (fn->raw) - printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, fn->raw->flash_offset &~3, newfrag); - else - printk(KERN_DEBUG "adding hole node %04x-%04x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, newfrag)); + D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", + fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag)); - prev = list; - this = *list; - - if (!fn->size) { + if (unlikely(!fn->size)) { jffs2_free_node_frag(newfrag); return 0; } @@ -104,141 +105,283 @@ int jffs2_add_full_dnode_to_fraglist(str newfrag->size = fn->size; newfrag->node = fn; newfrag->node->frags = 1; - newfrag->next = (void *)0xdeadbeef; + + ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag); + if (ret) + return ret; + + /* If we now share a page with other nodes, mark either previous + or next node REF_NORMAL, as appropriate. */ + if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) { + struct jffs2_node_frag *prev = frag_prev(newfrag); + + mark_ref_normal(fn->raw); + /* If we don't start at zero there's _always_ a previous */ + if (prev->node) + mark_ref_normal(prev->node->raw); + } + + if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) { + struct jffs2_node_frag *next = frag_next(newfrag); + + if (next) { + mark_ref_normal(fn->raw); + if (next->node) + mark_ref_normal(next->node->raw); + } + } + D2(jffs2_print_frag_list(f)); + return 0; +} + +/* Doesn't set inode->i_size */ +static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag) +{ + struct jffs2_node_frag *this; + uint32_t lastend; /* Skip all the nodes which are completed before this one starts */ - while(this && fn->ofs >= this->ofs+this->size) { - lastend = this->ofs + this->size; + this = jffs2_lookup_node_frag(list, newfrag->node->ofs); - D2(printk(KERN_DEBUG "j_a_f_d_t_f: skipping frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", - this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next)); - prev = &this->next; - this = this->next; + if (this) { + D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", + this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this)); + lastend = this->ofs + this->size; + } else { + D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n")); + lastend = 0; } - + /* See if we ran off the end of the list */ - if (!this) { + if (lastend <= newfrag->ofs) { /* We did */ - if (lastend < fn->ofs) { + + /* Check if 'this' node was on the same page as the new node. + If so, both 'this' and the new node get marked REF_NORMAL so + the GC can take a look. + */ + if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { + if (this->node) + mark_ref_normal(this->node->raw); + mark_ref_normal(newfrag->node->raw); + } + + if (lastend < newfrag->node->ofs) { /* ... and we need to put a hole in before the new node */ struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag(); if (!holefrag) return -ENOMEM; holefrag->ofs = lastend; - holefrag->size = fn->ofs - lastend; - holefrag->next = NULL; + holefrag->size = newfrag->node->ofs - lastend; holefrag->node = NULL; - *prev = holefrag; - prev = &holefrag->next; + if (this) { + /* By definition, the 'this' node has no right-hand child, + because there are no frags with offset greater than it. + So that's where we want to put the hole */ + D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this)); + rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right); + } else { + D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag)); + rb_link_node(&holefrag->rb, NULL, &list->rb_node); + } + rb_insert_color(&holefrag->rb, list); + this = holefrag; } - newfrag->next = NULL; - *prev = newfrag; + if (this) { + /* By definition, the 'this' node has no right-hand child, + because there are no frags with offset greater than it. + So that's where we want to put the hole */ + D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this)); + rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); + } else { + D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag)); + rb_link_node(&newfrag->rb, NULL, &list->rb_node); + } + rb_insert_color(&newfrag->rb, list); return 0; } - D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", - this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next)); + D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", + this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this)); - /* OK. 'this' is pointing at the first frag that fn->ofs at least partially obsoletes, - * - i.e. fn->ofs < this->ofs+this->size && fn->ofs >= this->ofs + /* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes, + * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs */ - if (fn->ofs > this->ofs) { + if (newfrag->ofs > this->ofs) { /* This node isn't completely obsoleted. The start of it remains valid */ - if (this->ofs + this->size > fn->ofs + fn->size) { + + /* Mark the new node and the partially covered node REF_NORMAL -- let + the GC take a look at them */ + mark_ref_normal(newfrag->node->raw); + if (this->node) + mark_ref_normal(this->node->raw); + + if (this->ofs + this->size > newfrag->ofs + newfrag->size) { /* The new node splits 'this' frag into two */ - newfrag2 = jffs2_alloc_node_frag(); + struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag(); if (!newfrag2) { jffs2_free_node_frag(newfrag); return -ENOMEM; } - D1(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size); + D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size); if (this->node) - printk("phys 0x%08x\n", this->node->raw->flash_offset &~3); + printk("phys 0x%08x\n", ref_offset(this->node->raw)); else printk("hole\n"); ) - newfrag2->ofs = fn->ofs + fn->size; + + /* New second frag pointing to this's node */ + newfrag2->ofs = newfrag->ofs + newfrag->size; newfrag2->size = (this->ofs+this->size) - newfrag2->ofs; - newfrag2->next = this->next; newfrag2->node = this->node; if (this->node) this->node->frags++; - newfrag->next = newfrag2; - this->next = newfrag; + + /* Adjust size of original 'this' */ this->size = newfrag->ofs - this->ofs; + + /* Now, we know there's no node with offset + greater than this->ofs but smaller than + newfrag2->ofs or newfrag->ofs, for obvious + reasons. So we can do a tree insert from + 'this' to insert newfrag, and a tree insert + from newfrag to insert newfrag2. */ + jffs2_fragtree_insert(newfrag, this); + rb_insert_color(&newfrag->rb, list); + + jffs2_fragtree_insert(newfrag2, newfrag); + rb_insert_color(&newfrag2->rb, list); + return 0; } /* New node just reduces 'this' frag in size, doesn't split it */ - this->size = fn->ofs - this->ofs; - newfrag->next = this->next; - this->next = newfrag; - this = newfrag->next; + this->size = newfrag->ofs - this->ofs; + + /* Again, we know it lives down here in the tree */ + jffs2_fragtree_insert(newfrag, this); + rb_insert_color(&newfrag->rb, list); } else { - D2(printk(KERN_DEBUG "Inserting newfrag (*%p) in before 'this' (*%p)\n", newfrag, this)); - *prev = newfrag; - newfrag->next = this; + /* New frag starts at the same point as 'this' used to. Replace + it in the tree without doing a delete and insertion */ + D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n", + newfrag, newfrag->ofs, newfrag->ofs+newfrag->size, + this, this->ofs, this->ofs+this->size)); + + rb_replace_node(&this->rb, &newfrag->rb, list); + + if (newfrag->ofs + newfrag->size >= this->ofs+this->size) { + D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size)); + jffs2_obsolete_node_frag(c, this); + } else { + this->ofs += newfrag->size; + this->size -= newfrag->size; + + jffs2_fragtree_insert(this, newfrag); + rb_insert_color(&this->rb, list); + return 0; + } } - /* OK, now we have newfrag added in the correct place in the list, but - newfrag->next points to a fragment which may be overlapping it + /* OK, now we have newfrag added in the correct place in the tree, but + frag_next(newfrag) may be a fragment which is overlapped by it */ - while (this && newfrag->ofs + newfrag->size >= this->ofs + this->size) { - /* 'this' frag is obsoleted. */ - old = this; - this = old->next; - jffs2_obsolete_node_frag(c, old); + while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) { + /* 'this' frag is obsoleted completely. */ + D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size)); + rb_erase(&this->rb, list); + jffs2_obsolete_node_frag(c, this); } /* Now we're pointing at the first frag which isn't totally obsoleted by the new frag */ - newfrag->next = this; if (!this || newfrag->ofs + newfrag->size == this->ofs) { return 0; } - /* Still some overlap */ + /* Still some overlap but we don't need to move it in the tree */ this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size); this->ofs = newfrag->ofs + newfrag->size; + + /* And mark them REF_NORMAL so the GC takes a look at them */ + if (this->node) + mark_ref_normal(this->node->raw); + mark_ref_normal(newfrag->node->raw); + return 0; } -void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, uint32_t size) +void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size) { + struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size); + D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size)); - while (*list) { - if ((*list)->ofs >= size) { - struct jffs2_node_frag *this = *list; - *list = this->next; - D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", this->ofs, this->ofs+this->size)); - jffs2_obsolete_node_frag(c, this); - continue; - } else if ((*list)->ofs + (*list)->size > size) { - D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", (*list)->ofs, (*list)->ofs + (*list)->size)); - (*list)->size = size - (*list)->ofs; + /* We know frag->ofs <= size. That's what lookup does for us */ + if (frag && frag->ofs != size) { + if (frag->ofs+frag->size >= size) { + D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size)); + frag->size = size - frag->ofs; } - list = &(*list)->next; + frag = frag_next(frag); + } + while (frag && frag->ofs >= size) { + struct jffs2_node_frag *next = frag_next(frag); + + D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size)); + frag_erase(frag, list); + jffs2_obsolete_node_frag(c, frag); + frag = next; } } /* Scan the list of all nodes present for this ino, build map of versions, etc. */ +static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + struct jffs2_raw_inode *latest_node); + int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t ino, struct jffs2_raw_inode *latest_node) { - struct jffs2_tmp_dnode_info *tn_list, *tn; - struct jffs2_full_dirent *fd_list; - struct jffs2_full_dnode *fn = NULL; - uint32_t crc; - size_t retlen; - int ret; - - init_MUTEX(&f->sem); - D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n")); + retry_inocache: + spin_lock(&c->inocache_lock); f->inocache = jffs2_get_ino_cache(c, ino); D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache)); + if (f->inocache) { + /* Check its state. We may need to wait before we can use it */ + switch(f->inocache->state) { + case INO_STATE_UNCHECKED: + case INO_STATE_CHECKEDABSENT: + f->inocache->state = INO_STATE_READING; + break; + + case INO_STATE_CHECKING: + case INO_STATE_GC: + /* If it's in either of these states, we need + to wait for whoever's got it to finish and + put it back. */ + D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n", + ino, f->inocache->state)); + sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); + goto retry_inocache; + + case INO_STATE_READING: + case INO_STATE_PRESENT: + /* Eep. This should never happen. It can + happen if Linux calls read_inode() again + before clear_inode() has finished though. */ + printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state); + /* Fail. That's probably better than allowing it to succeed */ + f->inocache = NULL; + break; + + default: + BUG(); + } + } + spin_unlock(&c->inocache_lock); if (!f->inocache && ino == 1) { /* Special case - no root inode on medium */ f->inocache = jffs2_alloc_inode_cache(); @@ -250,32 +393,65 @@ int jffs2_do_read_inode(struct jffs2_sb_ memset(f->inocache, 0, sizeof(struct jffs2_inode_cache)); f->inocache->ino = f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; + f->inocache->state = INO_STATE_READING; jffs2_add_ino_cache(c, f->inocache); } if (!f->inocache) { printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino); return -ENOENT; } - D1(printk(KERN_DEBUG "jffs2_do_read_inode(): ino #%u nlink is %d\n", ino, f->inocache->nlink)); + + return jffs2_do_read_inode_internal(c, f, latest_node); +} + +int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + struct jffs2_raw_inode n; + struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL); + + if (!f) + return -ENOMEM; + + memset(f, 0, sizeof(*f)); + init_MUTEX_LOCKED(&f->sem); + f->inocache = ic; + + return jffs2_do_read_inode_internal(c, f, &n); +} + +static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + struct jffs2_raw_inode *latest_node) +{ + struct jffs2_tmp_dnode_info *tn_list, *tn; + struct jffs2_full_dirent *fd_list; + struct jffs2_full_dnode *fn = NULL; + uint32_t crc; + uint32_t latest_mctime, mctime_ver; + uint32_t mdata_ver = 0; + size_t retlen; + int ret; + + D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink)); /* Grab all nodes relevant to this ino */ - ret = jffs2_get_inode_nodes(c, ino, f, &tn_list, &fd_list, &f->highest_version); + ret = jffs2_get_inode_nodes(c, f->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); if (ret) { - printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", ino, ret); + printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret); + if (f->inocache->state == INO_STATE_READING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); return ret; } f->dents = fd_list; while (tn_list) { - static uint32_t mdata_ver = 0; - tn = tn_list; fn = tn->fn; if (f->metadata && tn->version > mdata_ver) { - D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3)); + D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw))); jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; @@ -287,7 +463,7 @@ int jffs2_do_read_inode(struct jffs2_sb_ jffs2_add_full_dnode_to_inode(c, f, fn); } else { /* Zero-sized node at end of version list. Just a metadata update */ - D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", fn->raw->flash_offset &~3, tn->version)); + D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version)); f->metadata = fn; mdata_ver = tn->version; } @@ -296,41 +472,57 @@ int jffs2_do_read_inode(struct jffs2_sb_ } if (!fn) { /* No data nodes for this inode. */ - if (ino != 1) { - printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", ino); + if (f->inocache->ino != 1) { + printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino); if (!fd_list) { + if (f->inocache->state == INO_STATE_READING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); return -EIO; } printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n"); } - latest_node->mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO; - latest_node->atime = latest_node->ctime = latest_node->mtime = 0; - latest_node->isize = 0; - latest_node->gid = 0; - latest_node->uid = 0; + latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO); + latest_node->version = cpu_to_je32(0); + latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0); + latest_node->isize = cpu_to_je32(0); + latest_node->gid = cpu_to_je16(0); + latest_node->uid = cpu_to_je16(0); + if (f->inocache->state == INO_STATE_READING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); return 0; } - ret = jffs2_flash_read(c, fn->raw->flash_offset & ~3, sizeof(*latest_node), &retlen, (void *)latest_node); + ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node); if (ret || retlen != sizeof(*latest_node)) { - printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %ld of %d bytes read\n", - ret, (long)retlen, sizeof(*latest_node)); + printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n", + ret, retlen, sizeof(*latest_node)); /* FIXME: If this fails, there seems to be a memory leak. Find it. */ + up(&f->sem); jffs2_do_clear_inode(c, f); return ret?ret:-EIO; } crc = crc32(0, latest_node, sizeof(*latest_node)-8); - if (crc != 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); + if (crc != je32_to_cpu(latest_node->node_crc)) { + printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw)); + up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } - switch(latest_node->mode & S_IFMT) { + switch(jemode_to_cpu(latest_node->mode) & S_IFMT) { + case S_IFDIR: + if (mctime_ver > je32_to_cpu(latest_node->version)) { + /* The times in the latest_node are actually older than + mctime in the latest dirent. Cheat. */ + latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime); + } + break; + + case S_IFREG: /* If it was a regular file, truncate it to the latest node's isize */ - jffs2_truncate_fraglist(c, &f->fraglist, latest_node->isize); + jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize)); break; case S_IFLNK: @@ -338,166 +530,92 @@ int jffs2_do_read_inode(struct jffs2_sb_ Remove this when dwmw2 comes to his senses and stops symlinks from being an entirely gratuitous special case. */ - if (!latest_node->isize) + if (!je32_to_cpu(latest_node->isize)) latest_node->isize = latest_node->dsize; /* fall through... */ case S_IFBLK: case S_IFCHR: - /* Xertain inode types should have only one data node, and it's + /* Certain inode types should have only one data node, and it's kept as the metadata node */ if (f->metadata) { - printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n", ino, latest_node->mode); + printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n", + f->inocache->ino, jemode_to_cpu(latest_node->mode)); + up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } - if (!f->fraglist) { - printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n", ino, latest_node->mode); + if (!frag_first(&f->fragtree)) { + printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n", + f->inocache->ino, jemode_to_cpu(latest_node->mode)); + up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } /* ASSERT: f->fraglist != NULL */ - if (f->fraglist->next) { - printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had more than one node\n", ino, latest_node->mode); + if (frag_next(frag_first(&f->fragtree))) { + printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n", + f->inocache->ino, jemode_to_cpu(latest_node->mode)); /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ + up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } /* OK. We're happy */ - f->metadata = f->fraglist->node; - jffs2_free_node_frag(f->fraglist); - f->fraglist = NULL; + f->metadata = frag_first(&f->fragtree)->node; + jffs2_free_node_frag(frag_first(&f->fragtree)); + f->fragtree = RB_ROOT; break; } + if (f->inocache->state == INO_STATE_READING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); return 0; } -void jffs2_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); - - memset(f, 0, sizeof(*f)); - - ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); - - if (ret) { - 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; - inode->i_atime = latest_node.atime; - inode->i_mtime = latest_node.mtime; - inode->i_ctime = latest_node.ctime; - - inode->i_nlink = f->inocache->nlink; - -#ifndef __ECOS - inode->i_blksize = PAGE_SIZE; - inode->i_blocks = (inode->i_size + 511) >> 9; - -/* ifdef ugliness - when we sanify the eCos version of inode cache this whole - routine will probably be replaced */ - - switch (inode->i_mode & S_IFMT) { - unsigned short rdev; - - case S_IFLNK: - inode->i_op = &jffs2_symlink_inode_operations; - break; - - case S_IFDIR: - 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); - 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(MKDEV(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); - } -#endif /* !__ECOS */ - D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); -} - void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) { - struct jffs2_node_frag *frag, *frags; struct jffs2_full_dirent *fd, *fds; + /* I don't think we care about the potential race due to reading this + without f->sem. It can never get undeleted. */ + int deleted = f->inocache && !f->inocache->nlink; - frags = f->fraglist; - fds = f->dents; + /* If it's a deleted inode, grab the alloc_sem. This prevents + jffs2_garbage_collect_pass() from deciding that it wants to + garbage collect one of the nodes we're just about to mark + obsolete -- by the time we drop alloc_sem and return, all + the nodes are marked obsolete, and jffs2_g_c_pass() won't + call iget() for the inode in question. + + We also used to do this to keep the temporary BUG() in + jffs2_mark_node_obsolete() from triggering. + */ + if(deleted) + down(&c->alloc_sem); + + down(&f->sem); + if (f->metadata) { - if (!f->inocache->nlink) + if (deleted) jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); } - while (frags) { - frag = frags; - frags = frag->next; - 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)); + jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL); - if (frag->node && !(--frag->node->frags)) { - /* Not a hole, and it's the final remaining frag of this node. Free the node */ - if (!f->inocache->nlink) - jffs2_mark_node_obsolete(c, frag->node->raw); + fds = f->dents; - jffs2_free_full_dnode(frag->node); - } - jffs2_free_node_frag(frag); - } while(fds) { fd = fds; fds = fd->next; jffs2_free_full_dirent(fd); } -} + + if (f->inocache && f->inocache->state != INO_STATE_CHECKING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); -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)); + up(&f->sem); - jffs2_do_clear_inode(c, f); + if(deleted) + up(&c->alloc_sem); } - diff --git a/packages/fs/jffs2/current/src/scan.c b/packages/fs/jffs2/current/src/scan.c --- a/packages/fs/jffs2/current/src/scan.c +++ b/packages/fs/jffs2/current/src/scan.c @@ -7,19 +7,19 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.57 2002/01/09 13:25:58 dwmw2 Exp $ + * $Id: scan.c,v 1.98 2003/01/22 16:33:39 dwmw2 Exp $ * */ - -#ifndef __ECOS #include +#include #include #include #include -#endif +#include +#include #include "nodelist.h" -#include "crc32.h" +#define EMPTY_SCAN_SIZE 1024 #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \ c->free_size -= _x; c->dirty_size += _x; \ @@ -29,6 +29,10 @@ c->free_size -= _x; c->used_size += _x; \ jeb->free_size -= _x ; jeb->used_size += _x; \ }while(0) +#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \ + c->free_size -= _x; c->unchecked_size += _x; \ + jeb->free_size -= _x ; jeb->unchecked_size += _x; \ + }while(0) #define noisy_printk(noise, args...) do { \ if (*(noise)) { \ @@ -40,37 +44,75 @@ } \ } while(0) -static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +static uint32_t pseudo_random; + +static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + unsigned char *buf, uint32_t buf_size); /* These helper functions _must_ increase ofs and also do the dirty/used space accounting. * Returning an error will abort the mount - bad checksums etc. should just mark the space * as dirty. */ -static int jffs2_scan_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); +static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_inode *ri, uint32_t ofs); +static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_dirent *rd, uint32_t ofs); +#define BLK_STATE_ALLFF 0 +#define BLK_STATE_CLEAN 1 +#define BLK_STATE_PARTDIRTY 2 +#define BLK_STATE_CLEANMARKER 3 +#define BLK_STATE_ALLDIRTY 4 +#define BLK_STATE_BADBLOCK 5 int jffs2_scan_medium(struct jffs2_sb_info *c) { int i, ret; - uint32_t empty_blocks = 0; + uint32_t empty_blocks = 0, bad_blocks = 0; + unsigned char *flashbuf = NULL; + uint32_t buf_size = 0; +#ifndef __ECOS + size_t pointlen; - if (!c->blocks) { - printk(KERN_WARNING "EEEK! c->blocks is NULL!\n"); - return -EINVAL; + if (c->mtd->point) { + ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf); + if (!ret && pointlen < c->mtd->size) { + /* Don't muck about if it won't let us point to the whole flash */ + D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen)); + c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); + flashbuf = NULL; + } + if (ret) + D1(printk(KERN_DEBUG "MTD point failed %d\n", ret)); } +#endif + if (!flashbuf) { + /* For NAND it's quicker to read a whole eraseblock at a time, + apparently */ + if (jffs2_cleanmarker_oob(c)) + buf_size = c->sector_size; + else + buf_size = PAGE_SIZE; + + D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size)); + flashbuf = kmalloc(buf_size, GFP_KERNEL); + if (!flashbuf) + return -ENOMEM; + } + for (i=0; inr_blocks; i++) { struct jffs2_eraseblock *jeb = &c->blocks[i]; - ret = jffs2_scan_eraseblock(c, jeb); + ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size); + if (ret < 0) return ret; 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 @@ -78,10 +120,12 @@ int jffs2_scan_medium(struct jffs2_sb_in * is complete. Meanwhile we still count it as empty * for later checks. */ + empty_blocks++; list_add(&jeb->list, &c->erase_pending_list); - empty_blocks++; 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 */ @@ -93,10 +137,14 @@ int jffs2_scan_medium(struct jffs2_sb_in 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. @@ -104,57 +152,206 @@ int jffs2_scan_medium(struct jffs2_sb_in not the one with most free space. */ if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) && - (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { + (jffs2_can_mark_obsolete(c) || jeb->free_size > c->wbuf_pagesize) && + (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { /* Better candidate for the next writes to go to */ - if (c->nextblock) - list_add(&c->nextblock->list, &c->dirty_list); + if (c->nextblock) { + c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; + c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; + c->free_size -= c->nextblock->free_size; + c->wasted_size -= c->nextblock->wasted_size; + c->nextblock->free_size = c->nextblock->wasted_size = 0; + if (VERYDIRTY(c, c->nextblock->dirty_size)) { + list_add(&c->nextblock->list, &c->very_dirty_list); + } else { + list_add(&c->nextblock->list, &c->dirty_list); + } + } c->nextblock = jeb; } else { - list_add(&jeb->list, &c->dirty_list); + jeb->dirty_size += jeb->free_size + jeb->wasted_size; + c->dirty_size += jeb->free_size + jeb->wasted_size; + c->free_size -= jeb->free_size; + c->wasted_size -= jeb->wasted_size; + jeb->free_size = jeb->wasted_size = 0; + if (VERYDIRTY(c, jeb->dirty_size)) { + list_add(&jeb->list, &c->very_dirty_list); + } else { + list_add(&jeb->list, &c->dirty_list); + } } - } 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(KERN_WARNING "jffs2_scan_medium(): unknown block state\n"); + BUG(); } } + + /* Nextblock dirty is always seen as wasted, because we cannot recycle it now */ + if (c->nextblock && (c->nextblock->dirty_size)) { + c->nextblock->wasted_size += c->nextblock->dirty_size; + c->wasted_size += c->nextblock->dirty_size; + c->dirty_size -= c->nextblock->dirty_size; + c->nextblock->dirty_size = 0; + } + + if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) { + /* If we're going to start writing into a block which already + contains data, and the end of the data isn't page-aligned, + skip a little and align it. */ + + uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1); + + D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n", + skip)); + c->nextblock->wasted_size += skip; + c->wasted_size += skip; + + c->nextblock->free_size -= skip; + c->free_size -= skip; + } 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); } + if (buf_size) + kfree(flashbuf); +#ifndef __ECOS + else + c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); +#endif return 0; } -static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - struct jffs2_unknown_node node; +static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf, + uint32_t ofs, uint32_t len) +{ + int ret; + size_t retlen; + + ret = jffs2_flash_read(c, ofs, len, &retlen, buf); + if (ret) { + D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret)); + return ret; + } + if (retlen < len) { + D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen)); + return -EIO; + } + D2(printk(KERN_DEBUG "Read 0x%x bytes from 0x%08x into buf\n", len, ofs)); + D2(printk(KERN_DEBUG "000: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15])); + return 0; +} + +static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + unsigned char *buf, uint32_t buf_size) { + struct jffs2_unknown_node *node; + struct jffs2_unknown_node crcnode; uint32_t ofs, prevofs; - uint32_t hdr_crc, nodetype; + uint32_t hdr_crc, buf_ofs, buf_len; int err; int noise = 0; + int wasempty = 0; + uint32_t empty_start = 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)); - err = jffs2_scan_empty(c, jeb, &ofs, &noise); - if (err) return err; - if (ofs == jeb->offset + c->sector_size) { - D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset)); - return 1; /* special return code */ +#ifdef CONFIG_JFFS2_FS_NAND + if (jffs2_cleanmarker_oob(c)) { + int ret = jffs2_check_nand_cleanmarker(c, jeb); + D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); + /* Even if it's not found, we still scan to see + if the block is empty. We use this information + to decide whether to erase it or not. */ + switch (ret) { + case 0: cleanmarkerfound = 1; break; + case 1: break; + case 2: return BLK_STATE_BADBLOCK; + case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ + default: return ret; + } + } +#endif + buf_ofs = jeb->offset; + + if (!buf_size) { + buf_len = c->sector_size; + } else { + buf_len = EMPTY_SCAN_SIZE; + err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); + if (err) + return err; } + /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ + ofs = 0; + + /* Scan only 4KiB of 0xFF before declaring it's empty */ + while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) + ofs += 4; + + if (ofs == EMPTY_SCAN_SIZE) { +#ifdef CONFIG_JFFS2_FS_NAND + if (jffs2_cleanmarker_oob(c)) { + /* scan oob, take care of cleanmarker */ + int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound); + D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret)); + switch (ret) { + case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF; + case 1: return BLK_STATE_ALLDIRTY; + 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 BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */ + } + if (ofs) { + D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset, + jeb->offset + ofs)); + DIRTY_SPACE(ofs); + } + + /* Now ofs is a complete physical flash offset as it always was... */ + ofs += jeb->offset; + noise = 10; while(ofs < jeb->offset + c->sector_size) { - size_t retlen; - ACCT_PARANOIA_CHECK(jeb); - + + D1(ACCT_PARANOIA_CHECK(jeb)); + + cond_resched(); + if (ofs & 3) { printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs); ofs = (ofs+3)&~3; @@ -167,93 +364,174 @@ static int jffs2_scan_eraseblock (struct continue; } prevofs = ofs; - - if (jeb->offset + c->sector_size < ofs + sizeof(node)) { - D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. Not reading\n", sizeof(struct jffs2_unknown_node))); + + if (jeb->offset + c->sector_size < ofs + sizeof(*node)) { + D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node), + jeb->offset, c->sector_size, ofs, sizeof(*node))); DIRTY_SPACE((jeb->offset + c->sector_size)-ofs); break; } - 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; + if (buf_ofs + buf_len < ofs + sizeof(*node)) { + buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); + D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n", + sizeof(struct jffs2_unknown_node), buf_len, ofs)); + err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); + if (err) + return err; + buf_ofs = ofs; } - if (retlen < sizeof(node)) { - D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen)); - DIRTY_SPACE(retlen); - ofs += retlen; + + node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs]; + + if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) { + uint32_t inbuf_ofs = ofs - buf_ofs + 4; + uint32_t scanend; + + empty_start = ofs; + ofs += 4; + + /* If scanning empty space after only a cleanmarker, don't + bother scanning the whole block */ + if (unlikely(empty_start == jeb->offset + c->cleanmarker_size && + jeb->offset + EMPTY_SCAN_SIZE < buf_ofs + buf_len)) + scanend = jeb->offset + EMPTY_SCAN_SIZE - buf_ofs; + else + scanend = buf_len; + + D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs)); + while (inbuf_ofs < scanend) { + if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) + goto emptyends; + + inbuf_ofs+=4; + ofs += 4; + } + /* Ran off end. */ + D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs)); + + if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && + !jeb->first_node->next_in_ino && !jeb->dirty_size) + return BLK_STATE_CLEANMARKER; + wasempty = 1; + continue; + } else if (wasempty) { + emptyends: + printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs); + DIRTY_SPACE(ofs-empty_start); + wasempty = 0; continue; } - if (node.magic == JFFS2_EMPTY_BITMASK && node.nodetype == JFFS2_EMPTY_BITMASK) { - D1(printk(KERN_DEBUG "Found empty flash at 0x%x\n", ofs)); - err = jffs2_scan_empty(c, jeb, &ofs, &noise); - if (err) return err; - continue; - } - - if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) { + if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs); DIRTY_SPACE(4); ofs += 4; continue; } - if (node.magic == JFFS2_DIRTY_BITMASK) { + if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) { D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs)); DIRTY_SPACE(4); ofs += 4; continue; } - if (node.magic == JFFS2_OLD_MAGIC_BITMASK) { + if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) { printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs); printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n"); DIRTY_SPACE(4); ofs += 4; continue; } - if (node.magic != JFFS2_MAGIC_BITMASK) { + if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) { /* OK. We're out of possibilities. Whinge and move on */ - noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, node.magic); + noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", + JFFS2_MAGIC_BITMASK, ofs, + je16_to_cpu(node->magic)); DIRTY_SPACE(4); ofs += 4; continue; } /* We seem to have a node of sorts. Check the CRC */ - nodetype = node.nodetype; - node.nodetype |= JFFS2_NODE_ACCURATE; - hdr_crc = crc32(0, &node, sizeof(node)-4); - node.nodetype = nodetype; - if (hdr_crc != node.hdr_crc) { + crcnode.magic = node->magic; + crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE); + crcnode.totlen = node->totlen; + hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4); + + if (hdr_crc != je32_to_cpu(node->hdr_crc)) { noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n", - ofs, node.magic, node.nodetype, node.totlen, node.hdr_crc, hdr_crc); + ofs, je16_to_cpu(node->magic), + je16_to_cpu(node->nodetype), + je32_to_cpu(node->totlen), + je32_to_cpu(node->hdr_crc), + hdr_crc); + DIRTY_SPACE(4); + ofs += 4; + continue; + } + + if (ofs + je32_to_cpu(node->totlen) > + jeb->offset + c->sector_size) { + /* Eep. Node goes over the end of the erase block. */ + printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n", + ofs, je32_to_cpu(node->totlen)); + printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n"); DIRTY_SPACE(4); ofs += 4; continue; } - switch(node.nodetype | JFFS2_NODE_ACCURATE) { + if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) { + /* Wheee. This is an obsoleted node */ + D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs)); + DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); + ofs += PAD(je32_to_cpu(node->totlen)); + continue; + } + + switch(je16_to_cpu(node->nodetype)) { case JFFS2_NODETYPE_INODE: - err = jffs2_scan_inode_node(c, jeb, &ofs); + if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) { + buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); + D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n", + sizeof(struct jffs2_raw_inode), buf_len, ofs)); + err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); + if (err) + return err; + buf_ofs = ofs; + node = (void *)buf; + } + err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs); if (err) return err; + ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_NODETYPE_DIRENT: - err = jffs2_scan_dirent_node(c, jeb, &ofs); + if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { + buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); + D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n", + je32_to_cpu(node->totlen), buf_len, ofs)); + err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); + if (err) + return err; + buf_ofs = ofs; + node = (void *)buf; + } + err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs); if (err) return err; + ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_NODETYPE_CLEANMARKER: - if (node.totlen != sizeof(struct jffs2_unknown_node)) { + D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs)); + if (je32_to_cpu(node->totlen) != c->cleanmarker_size) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", - ofs, node.totlen, sizeof(struct jffs2_unknown_node)); + ofs, je32_to_cpu(node->totlen), c->cleanmarker_size); DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); + ofs += PAD(sizeof(struct jffs2_unknown_node)); } else if (jeb->first_node) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset); DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); ofs += PAD(sizeof(struct jffs2_unknown_node)); - continue; } else { struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref(); if (!marker_ref) { @@ -262,95 +540,77 @@ static int jffs2_scan_eraseblock (struct } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; - marker_ref->flash_offset = ofs; - marker_ref->totlen = sizeof(struct jffs2_unknown_node); + marker_ref->flash_offset = ofs | REF_NORMAL; + marker_ref->totlen = c->cleanmarker_size; jeb->first_node = jeb->last_node = marker_ref; - USED_SPACE(PAD(sizeof(struct jffs2_unknown_node))); + USED_SPACE(PAD(c->cleanmarker_size)); + ofs += PAD(c->cleanmarker_size); } - ofs += PAD(sizeof(struct jffs2_unknown_node)); + break; + + case JFFS2_NODETYPE_PADDING: + DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); + ofs += PAD(je32_to_cpu(node->totlen)); break; default: - switch (node.nodetype & JFFS2_COMPAT_MASK) { + switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_ROCOMPAT: - printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); + printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); c->flags |= JFFS2_SB_FLAG_RO; if (!(jffs2_is_readonly(c))) return -EROFS; - DIRTY_SPACE(PAD(node.totlen)); - ofs += PAD(node.totlen); - continue; + DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); + ofs += PAD(je32_to_cpu(node->totlen)); + break; case JFFS2_FEATURE_INCOMPAT: - printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); + printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); return -EINVAL; case JFFS2_FEATURE_RWCOMPAT_DELETE: - 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); + D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); + DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); + ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_FEATURE_RWCOMPAT_COPY: - 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); + D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); + USED_SPACE(PAD(je32_to_cpu(node->totlen))); + ofs += PAD(je32_to_cpu(node->totlen)); break; } } } - D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, - jeb->free_size, jeb->dirty_size, jeb->used_size)); - return 0; -} + + + D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset, + jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size)); -/* 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, uint32_t *startofs, int *noise) -{ - uint32_t *buf; - uint32_t scanlen = (jeb->offset + c->sector_size) - *startofs; - uint32_t curofs = *startofs; - - buf = kmalloc(min((uint32_t)PAGE_SIZE, scanlen), GFP_KERNEL); - if (!buf) { - printk(KERN_WARNING "Scan buffer allocation failed\n"); - return -ENOMEM; - } - while(scanlen) { - size_t retlen; - int ret, i; - - 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; - } - if (retlen < 4) { - D1(printk(KERN_WARNING "Eep. too few bytes read in scan_empty()\n")); - kfree(buf); - return -EIO; - } - 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; - kfree(buf); - return 0; - } - } - scanlen -= retlen&~3; - curofs += retlen&~3; + /* mark_node_obsolete can add to wasted !! */ + if (jeb->wasted_size) { + jeb->dirty_size += jeb->wasted_size; + c->dirty_size += jeb->wasted_size; + c->wasted_size -= jeb->wasted_size; + jeb->wasted_size = 0; } - D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs)); - kfree(buf); - *startofs = curofs; - return 0; + if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size + && (!jeb->first_node || jeb->first_node->next_in_ino) ) + return BLK_STATE_CLEANMARKER; + + /* move blocks with max 4 byte dirty space to cleanlist */ + else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) { + c->dirty_size -= jeb->dirty_size; + c->wasted_size += jeb->dirty_size; + jeb->wasted_size += jeb->dirty_size; + jeb->dirty_size = 0; + return BLK_STATE_CLEAN; + } else if (jeb->used_size || jeb->unchecked_size) + return BLK_STATE_PARTDIRTY; + else + return BLK_STATE_ALLDIRTY; } static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino) @@ -367,13 +627,7 @@ static struct jffs2_inode_cache *jffs2_s return NULL; } memset(ic, 0, sizeof(*ic)); - ic->scan = kmalloc(sizeof(struct jffs2_scan_info), GFP_KERNEL); - if (!ic->scan) { - printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of scan info for inode cache failed\n"); - jffs2_free_inode_cache(ic); - return NULL; - } - memset(ic->scan, 0, sizeof(*ic->scan)); + ic->ino = ino; ic->nodes = (void *)ic; jffs2_add_ino_cache(c, ic); @@ -382,108 +636,133 @@ static struct jffs2_inode_cache *jffs2_s return ic; } -static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs) +static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_inode *ri, uint32_t ofs) { struct jffs2_raw_node_ref *raw; - struct jffs2_full_dnode *fn; - struct jffs2_tmp_dnode_info *tn, **tn_list; struct jffs2_inode_cache *ic; - struct jffs2_raw_inode ri; - uint32_t crc; - uint16_t oldnodetype; - int ret; - size_t retlen; - - D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", *ofs)); + uint32_t ino = je32_to_cpu(ri->ino); - 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; - } - if (retlen != sizeof(ri)) { - printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", - retlen, *ofs, sizeof(ri)); - return -EIO; - } - - /* We sort of assume that the node was accurate when it was - first written to the medium :) */ - oldnodetype = ri.nodetype; - ri.nodetype |= JFFS2_NODE_ACCURATE; - crc = crc32(0, &ri, sizeof(ri)-8); - ri.nodetype = oldnodetype; + D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs)); - if(crc != ri.node_crc) { - printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", - *ofs, ri.node_crc, crc); - /* FIXME: Why do we believe totlen? */ - DIRTY_SPACE(4); - *ofs += 4; - return 0; - } - - if (ri.csize) { - /* Check data CRC too */ - unsigned char *dbuf; - uint32_t crc; + /* We do very little here now. Just check the ino# to which we should attribute + this node; we can do all the CRC checking etc. later. There's a tradeoff here -- + we used to scan the flash once only, reading everything we want from it into + memory, then building all our in-core data structures and freeing the extra + information. Now we allow the first part of the mount to complete a lot quicker, + but we have to go _back_ to the flash in order to finish the CRC checking, etc. + Which means that the _full_ amount of time to get to proper write mode with GC + operational may actually be _longer_ than before. Sucks to be me. */ - 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 = 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); - return ret; - } - if (retlen != ri.csize) { - printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", - retlen, *ofs+ sizeof(ri), ri.csize); - kfree(dbuf); - return -EIO; - } - crc = crc32(0, dbuf, ri.csize); - kfree(dbuf); - if (crc != ri.data_crc) { - printk(KERN_NOTICE "jffs2_scan_inode_node(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", - *ofs, ri.data_crc, crc); - DIRTY_SPACE(PAD(ri.totlen)); - *ofs += PAD(ri.totlen); - return -0; - } - } - - /* Wheee. It worked */ raw = jffs2_alloc_raw_node_ref(); if (!raw) { printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n"); return -ENOMEM; } - tn = jffs2_alloc_tmp_dnode_info(); - if (!tn) { - jffs2_free_raw_node_ref(raw); + + ic = jffs2_get_ino_cache(c, ino); + if (!ic) { + /* Inocache get failed. Either we read a bogus ino# or it's just genuinely the + first node we found for this inode. Do a CRC check to protect against the former + case */ + uint32_t crc = crc32(0, ri, sizeof(*ri)-8); + + if (crc != je32_to_cpu(ri->node_crc)) { + printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ofs, je32_to_cpu(ri->node_crc), crc); + /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ + DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen))); + return 0; + } + ic = jffs2_scan_make_ino_cache(c, ino); + if (!ic) { + jffs2_free_raw_node_ref(raw); + return -ENOMEM; + } + } + + /* Wheee. It worked */ + + raw->flash_offset = ofs | REF_UNCHECKED; + raw->totlen = PAD(je32_to_cpu(ri->totlen)); + raw->next_phys = NULL; + raw->next_in_ino = ic->nodes; + + ic->nodes = raw; + if (!jeb->first_node) + jeb->first_node = raw; + if (jeb->last_node) + jeb->last_node->next_phys = raw; + jeb->last_node = raw; + + D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", + je32_to_cpu(ri->ino), je32_to_cpu(ri->version), + je32_to_cpu(ri->offset), + je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize))); + + pseudo_random += je32_to_cpu(ri->version); + + UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen))); + return 0; +} + +static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_dirent *rd, uint32_t ofs) +{ + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *fd; + struct jffs2_inode_cache *ic; + uint32_t crc; + + D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs)); + + /* We don't get here unless the node is still valid, so we don't have to + mask in the ACCURATE bit any more. */ + crc = crc32(0, rd, sizeof(*rd)-8); + + if (crc != je32_to_cpu(rd->node_crc)) { + printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ofs, je32_to_cpu(rd->node_crc), crc); + /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ + DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); + return 0; + } + + pseudo_random += je32_to_cpu(rd->version); + + fd = jffs2_alloc_full_dirent(rd->nsize+1); + if (!fd) { return -ENOMEM; } - fn = jffs2_alloc_full_dnode(); - if (!fn) { - jffs2_free_tmp_dnode_info(tn); + memcpy(&fd->name, rd->name, rd->nsize); + fd->name[rd->nsize] = 0; + + crc = crc32(0, fd->name, rd->nsize); + if (crc != je32_to_cpu(rd->name_crc)) { + printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ofs, je32_to_cpu(rd->name_crc), crc); + D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino))); + jffs2_free_full_dirent(fd); + /* FIXME: Why do we believe totlen? */ + /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */ + DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); + return 0; + } + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + jffs2_free_full_dirent(fd); + printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n"); + return -ENOMEM; + } + ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino)); + if (!ic) { + jffs2_free_full_dirent(fd); jffs2_free_raw_node_ref(raw); return -ENOMEM; } - ic = jffs2_scan_make_ino_cache(c, ri.ino); - if (!ic) { - jffs2_free_full_dnode(fn); - jffs2_free_tmp_dnode_info(tn); - jffs2_free_raw_node_ref(raw); - return -ENOMEM; - } - - /* Build the data structures and file them for later */ - raw->flash_offset = *ofs; - raw->totlen = PAD(ri.totlen); + + raw->totlen = PAD(je32_to_cpu(rd->totlen)); + raw->flash_offset = ofs | REF_PRISTINE; raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; @@ -493,174 +772,120 @@ static int jffs2_scan_inode_node(struct jeb->last_node->next_phys = raw; jeb->last_node = raw; - D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", - ri.ino, ri.version, ri.offset, ri.offset+ri.dsize)); - - for (tn_list = &ic->scan->tmpnodes; *tn_list; tn_list = &((*tn_list)->next)) { - if ((*tn_list)->version < ri.version) - continue; - if ((*tn_list)->version > ri.version) - break; - /* Wheee. We've found another instance of the same version number. - We should obsolete one of them. - */ - D1(printk(KERN_DEBUG "Duplicate version %d found in ino #%u. Previous one is at 0x%08x\n", ri.version, ic->ino, (*tn_list)->fn->raw->flash_offset &~3)); - if (!jeb->used_size) { - D1(printk(KERN_DEBUG "No valid nodes yet found in this eraseblock 0x%08x, so obsoleting the new instance at 0x%08x\n", - jeb->offset, raw->flash_offset & ~3)); - ri.nodetype &= ~JFFS2_NODE_ACCURATE; - /* Perhaps we could also mark it as such on the medium. Maybe later */ - } - break; - } - - if (ri.nodetype & JFFS2_NODE_ACCURATE) { - memset(fn,0,sizeof(*fn)); + fd->raw = raw; + fd->next = NULL; + fd->version = je32_to_cpu(rd->version); + fd->ino = je32_to_cpu(rd->ino); + fd->nhash = full_name_hash(fd->name, rd->nsize); + fd->type = rd->type; + USED_SPACE(PAD(je32_to_cpu(rd->totlen))); + jffs2_add_fd_to_list(c, fd, &ic->scan_dents); - fn->ofs = ri.offset; - fn->size = ri.dsize; - fn->frags = 0; - fn->raw = raw; - - tn->next = NULL; - tn->fn = fn; - tn->version = ri.version; - - USED_SPACE(PAD(ri.totlen)); - jffs2_add_tn_to_list(tn, &ic->scan->tmpnodes); - /* Make sure the one we just added is the _last_ in the list - with this version number, so the older ones get obsoleted */ - while (tn->next && tn->next->version == tn->version) { - - D1(printk(KERN_DEBUG "Shifting new node at 0x%08x after other node at 0x%08x for version %d in list\n", - fn->raw->flash_offset&~3, tn->next->fn->raw->flash_offset &~3, ri.version)); - - if(tn->fn != fn) - BUG(); - tn->fn = tn->next->fn; - tn->next->fn = fn; - tn = tn->next; - } - } else { - jffs2_free_full_dnode(fn); - jffs2_free_tmp_dnode_info(tn); - raw->flash_offset |= 1; - DIRTY_SPACE(PAD(ri.totlen)); - } - *ofs += PAD(ri.totlen); return 0; } -static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *ofs) +static int count_list(struct list_head *l) { - struct jffs2_raw_node_ref *raw; - struct jffs2_full_dirent *fd; - struct jffs2_inode_cache *ic; - struct jffs2_raw_dirent rd; - uint16_t oldnodetype; - int ret; - uint32_t crc; - size_t retlen; + uint32_t count = 0; + struct list_head *tmp; - D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs)); - - 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; + list_for_each(tmp, l) { + count++; } - if (retlen != sizeof(rd)) { - printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", - retlen, *ofs, sizeof(rd)); - return -EIO; + return count; +} + +/* Note: This breaks if list_empty(head). I don't care. You + might, if you copy this code and use it elsewhere :) */ +static void rotate_list(struct list_head *head, uint32_t count) +{ + struct list_head *n = head->next; + + list_del(head); + while(count--) { + n = n->next; } + list_add(head, n); +} - /* We sort of assume that the node was accurate when it was - first written to the medium :) */ - oldnodetype = rd.nodetype; - rd.nodetype |= JFFS2_NODE_ACCURATE; - crc = crc32(0, &rd, sizeof(rd)-8); - rd.nodetype = oldnodetype; +void jffs2_rotate_lists(struct jffs2_sb_info *c) +{ + uint32_t x; + uint32_t rotateby; - if (crc != rd.node_crc) { - printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", - *ofs, rd.node_crc, crc); - /* FIXME: Why do we believe totlen? */ - DIRTY_SPACE(4); - *ofs += 4; - return 0; + x = count_list(&c->clean_list); + if (x) { + rotateby = pseudo_random % x; + D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby)); + + rotate_list((&c->clean_list), rotateby); + + D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n", + list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty clean_list\n")); } - fd = jffs2_alloc_full_dirent(rd.nsize+1); - if (!fd) { - return -ENOMEM; -} - 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", - *ofs + sizeof(rd), ret); - return ret; - } - if (retlen != rd.nsize) { - jffs2_free_full_dirent(fd); - printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", - retlen, *ofs + sizeof(rd), rd.nsize); - return -EIO; + x = count_list(&c->very_dirty_list); + if (x) { + rotateby = pseudo_random % x; + D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby)); + + rotate_list((&c->very_dirty_list), rotateby); + + D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n", + list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n")); } - 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", - *ofs, rd.name_crc, crc); - fd->name[rd.nsize]=0; - D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, rd.ino)); - jffs2_free_full_dirent(fd); - /* FIXME: Why do we believe totlen? */ - DIRTY_SPACE(PAD(rd.totlen)); - *ofs += PAD(rd.totlen); - return 0; - } - raw = jffs2_alloc_raw_node_ref(); - if (!raw) { - jffs2_free_full_dirent(fd); - printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n"); - return -ENOMEM; + + x = count_list(&c->dirty_list); + if (x) { + rotateby = pseudo_random % x; + D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby)); + + rotate_list((&c->dirty_list), rotateby); + + D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n", + list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n")); } - ic = jffs2_scan_make_ino_cache(c, rd.pino); - if (!ic) { - jffs2_free_full_dirent(fd); - jffs2_free_raw_node_ref(raw); - return -ENOMEM; + + x = count_list(&c->erasable_list); + if (x) { + rotateby = pseudo_random % x; + D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby)); + + rotate_list((&c->erasable_list), rotateby); + + D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n", + list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n")); } - - raw->totlen = PAD(rd.totlen); - raw->flash_offset = *ofs; - raw->next_phys = NULL; - raw->next_in_ino = ic->nodes; - ic->nodes = raw; - if (!jeb->first_node) - jeb->first_node = raw; - if (jeb->last_node) - jeb->last_node->next_phys = raw; - jeb->last_node = raw; + + if (c->nr_erasing_blocks) { + rotateby = pseudo_random % c->nr_erasing_blocks; + D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby)); - if (rd.nodetype & JFFS2_NODE_ACCURATE) { - fd->raw = raw; - fd->next = NULL; - fd->version = rd.version; - fd->ino = rd.ino; - fd->name[rd.nsize]=0; - fd->nhash = full_name_hash(fd->name, rd.nsize); - fd->type = rd.type; + rotate_list((&c->erase_pending_list), rotateby); + + D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n", + list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n")); + } - 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); + if (c->nr_free_blocks) { + rotateby = pseudo_random % c->nr_free_blocks; + D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby)); + + rotate_list((&c->free_list), rotateby); - DIRTY_SPACE(PAD(rd.totlen)); - } - *ofs += PAD(rd.totlen); - return 0; + D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n", + list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty free_list\n")); + } } diff --git a/packages/fs/jffs2/current/src/write.c b/packages/fs/jffs2/current/src/write.c --- a/packages/fs/jffs2/current/src/write.c +++ b/packages/fs/jffs2/current/src/write.c @@ -7,18 +7,17 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.40 2002/01/28 22:25:08 dwmw2 Exp $ + * $Id: write.c,v 1.65 2003/01/21 18:11:29 dwmw2 Exp $ * */ -#ifndef __ECOS #include #include +#include #include +#include #include -#endif #include "nodelist.h" -#include "crc32.h" int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri) @@ -29,121 +28,34 @@ int jffs2_do_new_inode(struct jffs2_sb_i if (!ic) { return -ENOMEM; } + memset(ic, 0, sizeof(*ic)); init_MUTEX_LOCKED(&f->sem); f->inocache = ic; f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; - f->inocache->ino = ri->ino = ++c->highest_ino; - D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", ri->ino)); + f->inocache->ino = ++c->highest_ino; + f->inocache->state = INO_STATE_PRESENT; + + ri->ino = cpu_to_je32(f->inocache->ino); + + D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); jffs2_add_ino_cache(c, f->inocache); - ri->magic = JFFS2_MAGIC_BITMASK; - ri->nodetype = JFFS2_NODETYPE_INODE; - ri->totlen = PAD(sizeof(*ri)); - ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); - ri->mode = mode; - f->highest_version = ri->version = 1; + ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + ri->totlen = cpu_to_je32(PAD(sizeof(*ri))); + ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); + ri->mode = cpu_to_jemode(mode); + + f->highest_version = 1; + ri->version = cpu_to_je32(f->highest_version); return 0; } -/* jffs2_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); - - /* Alloc jffs2_inode_info when that's split in 2.5 */ - - f = JFFS2_INODE_INFO(inode); - memset(f, 0, sizeof(*f)); - - memset(ri, 0, sizeof(*ri)); - /* Set OS-specific defaults for new inodes */ -#ifdef __ECOS - ri->uid = ri->gid = 0; -#else - 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; - } -#endif - 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; -#ifndef __ECOS - inode->i_blksize = PAGE_SIZE; - inode->i_blocks = 0; -#endif - inode->i_size = 0; - - insert_inode_hash(inode); - - return inode; -} - -#ifndef __ECOS -/* 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; iwrite(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 jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen) -{ - if (c->mtd->writev) - return c->mtd->writev(c->mtd, vecs, count, to, retlen); - else - return mtd_fake_writev(c->mtd, vecs, count, to, retlen); -} -#endif - +#if CONFIG_JFFS2_FS_DEBUG > 0 static void writecheck(struct jffs2_sb_info *c, uint32_t ofs) { unsigned char buf[16]; @@ -151,8 +63,8 @@ static void writecheck(struct jffs2_sb_i int ret, i; ret = jffs2_flash_read(c, ofs, 16, &retlen, buf); - if (ret && retlen != 16) { - D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %d\n", ret, retlen)); + if (ret || (retlen != 16)) { + D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen)); return; } ret = 0; @@ -161,16 +73,15 @@ static void writecheck(struct jffs2_sb_i ret = 1; } if (ret) { - printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there's data already there:\n", ofs); + printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs); printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", ofs, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); } } +#endif - - /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, write it to the flash, link it into the existing inode/fragment list */ @@ -183,8 +94,9 @@ struct jffs2_full_dnode *jffs2_write_dno 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)) { + D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) { printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n"); BUG(); } @@ -194,10 +106,10 @@ struct jffs2_full_dnode *jffs2_write_dno vecs[1].iov_base = (unsigned char *)data; vecs[1].iov_len = datalen; - writecheck(c, flash_ofs); + D1(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); + if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) { + printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen); } raw = jffs2_alloc_raw_node_ref(); if (!raw) @@ -209,17 +121,22 @@ struct jffs2_full_dnode *jffs2_write_dno return ERR_PTR(-ENOMEM); } raw->flash_offset = flash_ofs; - raw->totlen = PAD(ri->totlen); + raw->totlen = PAD(sizeof(*ri)+datalen); raw->next_phys = NULL; - fn->ofs = ri->offset; - fn->size = ri->dsize; + fn->ofs = je32_to_cpu(ri->offset); + fn->size = je32_to_cpu(ri->dsize); fn->frags = 0; fn->raw = raw; - ret = jffs2_flash_writev(c, vecs, datalen?2:1, 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", + printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", sizeof(*ri)+datalen, flash_ofs, ret, retlen); /* Mark the space as dirtied */ if (retlen) { @@ -231,7 +148,8 @@ struct jffs2_full_dnode *jffs2_write_dno seem corrupted, in which case the scan would skip over any node we write before the original intended end of this node */ - jffs2_add_physical_node_ref(c, raw, sizeof(*ri)+datalen, 1); + raw->flash_offset |= REF_OBSOLETE; + jffs2_add_physical_node_ref(c, raw); jffs2_mark_node_obsolete(c, raw); } else { printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); @@ -245,13 +163,27 @@ struct jffs2_full_dnode *jffs2_write_dno return ERR_PTR(ret?ret:-EIO); } /* Mark the space used */ - jffs2_add_physical_node_ref(c, raw, retlen, 0); + /* If node covers at least a whole page, or if it starts at the + beginning of a page and runs to the end of the file, or if + it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. + */ + if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) || + ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) && + (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) { + raw->flash_offset |= REF_PRISTINE; + } else { + raw->flash_offset |= REF_NORMAL; + } + jffs2_add_physical_node_ref(c, raw); /* Link into per-inode list */ raw->next_in_ino = f->inocache->nodes; f->inocache->nodes = raw; - D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ri->dsize, ri->csize, ri->node_crc, ri->data_crc, ri->totlen)); + D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", + flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), + je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc), + je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen))); if (writelen) *writelen = retlen; @@ -267,10 +199,12 @@ struct jffs2_full_dirent *jffs2_write_di 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, flash_ofs); + D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", + je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino), + je32_to_cpu(rd->name_crc))); + D1(writecheck(c, flash_ofs)); - D1(if(rd->hdr_crc != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) { + D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) { printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n"); BUG(); } @@ -292,13 +226,13 @@ struct jffs2_full_dirent *jffs2_write_di return ERR_PTR(-ENOMEM); } raw->flash_offset = flash_ofs; - raw->totlen = PAD(rd->totlen); + raw->totlen = PAD(sizeof(*rd)+namelen); raw->next_in_ino = f->inocache->nodes; f->inocache->nodes = raw; raw->next_phys = NULL; - fd->version = rd->version; - fd->ino = rd->ino; + fd->version = je32_to_cpu(rd->version); + fd->ino = je32_to_cpu(rd->ino); fd->nhash = full_name_hash(name, strlen(name)); fd->type = rd->type; memcpy(fd->name, name, namelen); @@ -306,17 +240,18 @@ struct jffs2_full_dirent *jffs2_write_di fd->raw = raw; 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", + if (ret || (retlen != sizeof(*rd) + namelen)) { + printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", sizeof(*rd)+namelen, flash_ofs, ret, retlen); /* Mark the space as dirtied */ - if (retlen) { - jffs2_add_physical_node_ref(c, raw, sizeof(*rd)+namelen, 1); - jffs2_mark_node_obsolete(c, raw); - } else { - printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); - jffs2_free_raw_node_ref(raw); - } + if (retlen) { + raw->flash_offset |= REF_OBSOLETE; + jffs2_add_physical_node_ref(c, raw); + jffs2_mark_node_obsolete(c, raw); + } else { + printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); + jffs2_free_raw_node_ref(raw); + } /* Release the full_dnode which is now useless, and return */ jffs2_free_full_dirent(fd); @@ -325,7 +260,8 @@ struct jffs2_full_dirent *jffs2_write_di return ERR_PTR(ret?ret:-EIO); } /* Mark the space used */ - jffs2_add_physical_node_ref(c, raw, retlen, 0); + raw->flash_offset |= REF_PRISTINE; + jffs2_add_physical_node_ref(c, raw); if (writelen) *writelen = retlen; @@ -362,7 +298,7 @@ int jffs2_write_inode_range(struct jffs2 } down(&f->sem); datalen = writelen; - cdatalen = min(alloclen - sizeof(*ri), writelen); + cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen); comprbuf = kmalloc(cdatalen, GFP_KERNEL); if (comprbuf) { @@ -380,31 +316,30 @@ int jffs2_write_inode_range(struct jffs2 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->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen); + ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); - ri->ino = 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->ino = cpu_to_je32(f->inocache->ino); + ri->version = cpu_to_je32(++f->highest_version); + ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen)); + ri->offset = cpu_to_je32(offset); + ri->csize = cpu_to_je32(cdatalen); + ri->dsize = cpu_to_je32(datalen); ri->compr = comprtype; - ri->node_crc = crc32(0, ri, sizeof(*ri)-8); - ri->data_crc = crc32(0, comprbuf, cdatalen); + ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, NULL); - jffs2_complete_reservation(c); - 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); @@ -413,14 +348,18 @@ int jffs2_write_inode_range(struct jffs2 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); + + 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; @@ -450,15 +389,18 @@ int jffs2_do_create(struct jffs2_sb_info */ 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) + if (ret) { + up(&f->sem); return ret; + } - ri->data_crc = 0; - ri->node_crc = crc32(0, ri, sizeof(*ri)-8); + ri->data_crc = cpu_to_je32(0); + ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen); - D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", ri->mode)); + D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", + jemode_to_cpu(ri->mode))); if (IS_ERR(fn)) { D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n")); @@ -499,28 +441,28 @@ int jffs2_do_create(struct jffs2_sb_info 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->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); + rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); + rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); - rd->pino = dir_f->inocache->ino; - rd->version = ++dir_f->highest_version; + rd->pino = cpu_to_je32(dir_f->inocache->ino); + rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = ri->ino; - rd->mctime = CURRENT_TIME; + 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); + rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); + rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, &writtenlen); - jffs2_complete_reservation(c); 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); } @@ -528,13 +470,16 @@ int jffs2_do_create(struct jffs2_sb_info /* 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) +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; @@ -554,35 +499,39 @@ int jffs2_do_unlink(struct jffs2_sb_info 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->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); + rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); + rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); - rd->pino = dir_f->inocache->ino; - rd->version = ++dir_f->highest_version; - rd->ino = 0; - rd->mctime = CURRENT_TIME; + rd->pino = cpu_to_je32(dir_f->inocache->ino); + rd->version = cpu_to_je32(++dir_f->highest_version); + rd->ino = cpu_to_je32(0); + rd->mctime = cpu_to_je32(get_seconds()); rd->nsize = namelen; rd->type = DT_UNKNOWN; - rd->node_crc = crc32(0, rd, sizeof(*rd)-8); - rd->name_crc = crc32(0, name, namelen); + rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); + rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL); - jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { + jffs2_complete_reservation(c); up(&dir_f->sem); return PTR_ERR(fd); } /* File it. This will mark the old one obsolete. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); + up(&dir_f->sem); - if (dead_f) { /* Null if this was a rename not a real unlink */ + /* dead_f is NULL if this was a rename not a real unlink */ + /* Also catch the !f->inocache case, where there was a dirent + pointing to an inode which didn't exist. */ + if (dead_f && dead_f->inocache) { down(&dead_f->sem); @@ -607,6 +556,8 @@ int jffs2_do_unlink(struct jffs2_sb_info up(&dead_f->sem); } + jffs2_complete_reservation(c); + return 0; } @@ -631,34 +582,36 @@ int jffs2_do_link (struct jffs2_sb_info 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->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); + rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); + rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); - rd->pino = dir_f->inocache->ino; - rd->version = ++dir_f->highest_version; - rd->ino = ino; - rd->mctime = CURRENT_TIME; + rd->pino = cpu_to_je32(dir_f->inocache->ino); + rd->version = cpu_to_je32(++dir_f->highest_version); + rd->ino = cpu_to_je32(ino); + rd->mctime = cpu_to_je32(get_seconds()); rd->nsize = namelen; rd->type = type; - rd->node_crc = crc32(0, rd, sizeof(*rd)-8); - rd->name_crc = crc32(0, name, namelen); + rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); + rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL); - jffs2_complete_reservation(c); 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;