Mercurial > nand-ecoscentric
changeset 1443:cd7d2afbc7dd
Import latest JFFS2 code from David Woodhouse
line wrap: on
line diff
--- a/packages/fs/jffs2/current/ChangeLog +++ b/packages/fs/jffs2/current/ChangeLog @@ -1,3 +1,18 @@ +2003-11-26 David Woodhouse <dwmw2@redhat.com> + + JFFS2 cleanup and import of newer code. Remove last vestiges of + Linuxisms such as 'struct inode' from the core code, leaving eCos + with no excuse, and renaming the eCos 'struct inode' to make that + point. Fix i_count handling throughout. Clean up remaining Linuxisms + such as 'struct qstr' to the point where jffs2port.h can be removed. + Add skeleton for background garbage-collect thread. Fix compression + so that it's actually called, and even with the right pointers. Turn + on -Werror again. Zero tolerance is a good thing. Make the i_mode + conversion functions non-inline to avoid warnings. Fix namespace + pollution (of all but ^jffs2_* at least). Move physical flash I/O + functions into separate file flashio.c for relatively easy + substitution. Various other cruftectomy. + 2003-11-25 Andrew Lunn <andrew.lunn@ascom.ch> * src/fs-ecos.c: ARM gcc 3.2.3 is also broken. Complain with any
--- a/packages/fs/jffs2/current/cdl/jffs2.cdl +++ b/packages/fs/jffs2/current/cdl/jffs2.cdl @@ -4,7 +4,7 @@ # # JFFS2 Filesystem configuration data # -# $Id: jffs2.cdl,v 1.6 2003/11/20 16:41:57 dwmw2 Exp $ +# $Id: jffs2.cdl,v 1.13 2003/11/29 00:30:30 dwmw2 Exp $ # # ==================================================================== #####ECOSGPLCOPYRIGHTBEGIN#### @@ -72,7 +72,30 @@ cdl_package CYGPKG_FS_JFFS2 { implements CYGINT_IO_FILEIO_FS 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 file-ecos.c + compile build.c scan.c malloc-ecos.c nodelist.c nodemgmt.c readinode.c dir-ecos.c read.c compr.c + # This could be overridden by an alternative direct I/O method. + compile flashio.c + + cdl_option CYGOPT_FS_JFFS2_GCTHREAD { + display "Support garbage-collection background thread" + flavor bool + compile gcthread.c +# Leave this off till it's been implemented. And don't implement it till +# icache locking has been made thread-safe. + requires CYGPKG_KERNEL && 0 + description " + Enable background garbage collection thread, for making + free space ahead of time." + } + + cdl_option CYGOPT_FS_JFFS2_WRITE { + display "Include write support for JFFS2" + flavor bool + compile gc.c write.c erase.c + default_value 1 + description " + Enable writing to JFFS2 file systems; not only reading." + } cdl_option CYGOPT_FS_JFFS2_NAND { display "Support for NAND flash" @@ -166,8 +189,9 @@ cdl_package CYGPKG_FS_JFFS2 { display "Additional compiler flags" flavor data no_define - # We add -D__ECOS to trigger eCos-specific code in places. - # We add -Werror because I find it useful. + # We add '-D__ECOS' to trigger eCos-specific code in places. + # We add '-nostdinc -iwithprefix include' to avoid picking up + # native <linux/*.h> include files when building on Linux. default_value { "-D__ECOS -nostdinc -iwithprefix include" } description " This option modifies the set of compiler flags for @@ -176,20 +200,6 @@ cdl_package CYGPKG_FS_JFFS2 { to the set of global flags." } - cdl_option CYGPKG_FS_JFFS2_CFLAGS_REMOVE { - display "Suppressed compiler flags" - flavor data - no_define - # We remove -Wpointer-arith so that some of the hacky Linux-compat code - # (in file.c) compiled. We can probably remove it when that's replaced - # properly. - default_value { "-Wpointer-arith" } - description " - This option modifies the set of compiler flags for - building the JFFS2 package. These flags are removed from - the set of global flags if present." - } - # ---------------------------------------------------------------- # Tests
new file mode 100644 --- /dev/null +++ b/packages/fs/jffs2/current/doc/README.Locking @@ -0,0 +1,116 @@ + $Id: README.Locking,v 1.6 2003/11/02 19:27:54 dwmw2 Exp $ + + JFFS2 LOCKING DOCUMENTATION + --------------------------- + +At least theoretically, JFFS2 does not require the Big Kernel Lock +(BKL), which was always helpfully obtained for it by Linux 2.4 VFS +code. It has its own locking, as described below. + +This document attempts to describe the existing locking rules for +JFFS2. It is not expected to remain perfectly up to date, but ought to +be fairly close. + + + alloc_sem + --------- + +The alloc_sem is a per-filesystem semaphore, used primarily to ensure +contiguous allocation of space on the medium. It is automatically +obtained during space allocations (jffs2_reserve_space()) and freed +upon write completion (jffs2_complete_reservation()). Note that +the garbage collector will obtain this right at the beginning of +jffs2_garbage_collect_pass() and release it at the end, thereby +preventing any other write activity on the file system during a +garbage collect pass. + +When writing new nodes, the alloc_sem must be held until the new nodes +have been properly linked into the data structures for the inode to +which they belong. This is for the benefit of NAND flash - adding new +nodes to an inode may obsolete old ones, and by holding the alloc_sem +until this happens we ensure that any data in the write-buffer at the +time this happens are part of the new node, not just something that +was written afterwards. Hence, we can ensure the newly-obsoleted nodes +don't actually get erased until the write-buffer has been flushed to +the medium. + +With the introduction of NAND flash support and the write-buffer, +the alloc_sem is also used to protect the wbuf-related members of the +jffs2_sb_info structure. Atomically reading the wbuf_len member to see +if the wbuf is currently holding any data is permitted, though. + +Ordering constraints: See f->sem. + + + File Semaphore f->sem + --------------------- + +This is the JFFS2-internal equivalent of the inode semaphore i->i_sem. +It protects the contents of the jffs2_inode_info private inode data, +including the linked list of node fragments (but see the notes below on +erase_completion_lock), etc. + +The reason that the i_sem itself isn't used for this purpose is to +avoid deadlocks with garbage collection -- the VFS will lock the i_sem +before calling a function which may need to allocate space. The +allocation may trigger garbage-collection, which may need to move a +node belonging to the inode which was locked in the first place by the +VFS. If the garbage collection code were to attempt to lock the i_sem +of the inode from which it's garbage-collecting a physical node, this +lead to deadlock, unless we played games with unlocking the i_sem +before calling the space allocation functions. + +Instead of playing such games, we just have an extra internal +semaphore, which is obtained by the garbage collection code and also +by the normal file system code _after_ allocation of space. + +Ordering constraints: + + 1. Never attempt to allocate space or lock alloc_sem with + any f->sem held. + 2. Never attempt to lock two file semaphores in one thread. + No ordering rules have been made for doing so. + + + erase_completion_lock spinlock + ------------------------------ + +This is used to serialise access to the eraseblock lists, to the +per-eraseblock lists of physical jffs2_raw_node_ref structures, and +(NB) the per-inode list of physical nodes. The latter is a special +case - see below. + +As the MTD API no longer permits erase-completion callback functions +to be called from bottom-half (timer) context (on the basis that nobody +ever actually implemented such a thing), it's now sufficient to use +a simple spin_lock() rather than spin_lock_bh(). + +Note that the per-inode list of physical nodes (f->nodes) is a special +case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in +the list are protected by the file semaphore f->sem. But the erase +code may remove _obsolete_ nodes from the list while holding only the +erase_completion_lock. So you can walk the list only while holding the +erase_completion_lock, and can drop the lock temporarily mid-walk as +long as the pointer you're holding is to a _valid_ node, not an +obsolete one. + +The erase_completion_lock is also used to protect the c->gc_task +pointer when the garbage collection thread exits. The code to kill the +GC thread locks it, sends the signal, then unlocks it - while the GC +thread itself locks it, zeroes c->gc_task, then unlocks on the exit path. + + erase_free_sem + -------------- + +This semaphore is only used by the erase code which frees obsolete +node references and the jffs2_garbage_collect_deletion_dirent() +function. The latter function on NAND flash must read _obsolete_ nodes +to determine whether the 'deletion dirent' under consideration can be +discarded or whether it is still required to show that an inode has +been unlinked. Because reading from the flash may sleep, the +erase_completion_lock cannot be held, so an alternative, more +heavyweight lock was required to prevent the erase code from freeing +the jffs2_raw_node_ref structures in question while the garbage +collection code is looking at them. + +Suggestions for alternative solutions to this problem would be welcomed.
new file mode 100644 --- /dev/null +++ b/packages/fs/jffs2/current/doc/TODO @@ -0,0 +1,28 @@ +$Id: TODO,v 1.15 2003/10/06 14:50:37 dwmw2 Exp $ + + - disable compression in commit_write()? + - fine-tune the allocation / GC thresholds + - chattr support - turning on/off and tuning compression per-inode + - checkpointing (do we need this? scan is quite fast) + - make the scan code populate real inodes so read_inode just after + mount doesn't have to read the flash twice for large files. + Make this a per-inode option, changable with chattr, so you can + decide which inodes should be in-core immediately after mount. + - test, test, test + + - NAND flash support: + - done :) + + - Optimisations: + - Split writes so they go to two separate blocks rather than just c->nextblock. + By writing _new_ nodes to one block, and garbage-collected REF_PRISTINE + nodes to a different one, we can separate clean nodes from those which + are likely to become dirty, and end up with blocks which are each far + closer to 100% or 0% clean, hence speeding up later GC progress dramatically. + - Stop keeping name in-core with struct jffs2_full_dirent. If we keep the hash in + the full dirent, we only need to go to the flash in lookup() when we think we've + got a match, and in readdir(). + - Doubly-linked next_in_ino list to allow us to free obsoleted raw_node_refs immediately? + - Remove totlen from jffs2_raw_node_ref? Need to have totlen passed into + jffs2_mark_node_obsolete(). Can all callers work it out? + - Remove size from jffs2_raw_node_frag.
new file mode 100644 --- /dev/null +++ b/packages/fs/jffs2/current/doc/TODO.eCos @@ -0,0 +1,25 @@ +$Id: TODO.eCos,v 1.2 2003/11/28 11:15:56 dwmw2 Exp $ + + - Make symlinks work properly. + + - Fill in the skeleton gcthread.c so it actually does something. + + - Check and possibly fix locking of icache mangling in fs-ecos.c + + - Check that os-ecos.h defines 'spin_lock()' to something appropriate. + + - Fix unmount of root file system after chdir(). + + - Fix atomicity of renames. Why was the unlink added before rename? + + - Further cleanup -- should the functions in dir-ecos.c take 'struct + dirsearch' instead of various components thereof, or should each of + those functions just be moved inside its only caller in fs-ecos.c? + + - Improve mount time by using pointer directly into flash chip instead + of jffs2_flash_read() for the initial scan -- look at the #ifdef + __ECOS bit in scan.c for details. + + - Reduce memory usage. There are fields marked for possible removal in + struct _inode, and there's the __totlen field in struct + jffs2_raw_node_ref as discussed recently.
--- a/packages/fs/jffs2/current/src/compr.c +++ b/packages/fs/jffs2/current/src/compr.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.29 2003/11/20 16:40:35 dwmw2 Exp $ + * $Id: compr.c,v 1.33 2003/11/28 17:22:54 dwmw2 Exp $ * */ @@ -17,6 +17,7 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/jffs2.h> +#include "nodelist.h" 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); @@ -54,42 +55,43 @@ unsigned char jffs2_compress(unsigned ch *cpage_out = kmalloc(*cdatalen, GFP_KERNEL); if (!*cpage_out) { printk(KERN_WARNING "No memory for compressor allocation. Compression failed\n"); - return JFFS2_COMPR_NONE; + goto out; } #ifdef JFFS2_USE_ZLIB - ret = jffs2_zlib_compress(data_in, cpage_out, datalen, cdatalen); + ret = jffs2_zlib_compress(data_in, *cpage_out, datalen, cdatalen); if (!ret) { return JFFS2_COMPR_ZLIB; } #endif #ifdef JFFS2_USE_DYNRUBIN - ret = jffs2_dynrubin_compress(data_in, cpage_out, datalen, cdatalen); + ret = jffs2_dynrubin_compress(data_in, *cpage_out, datalen, cdatalen); if (!ret) { return JFFS2_COMPR_DYNRUBIN; } #endif #ifdef JFFS2_USE_RUBINMIPS - ret = jffs2_rubinmips_compress(data_in, cpage_out, datalen, cdatalen); + ret = jffs2_rubinmips_compress(data_in, *cpage_out, datalen, cdatalen); if (!ret) { return JFFS2_COMPR_RUBINMIPS; } #endif #ifdef JFFS2_USE_RTIME /* rtime does manage to recompress already-compressed data */ - ret = jffs2_rtime_compress(data_in, cpage_out, datalen, cdatalen); + ret = jffs2_rtime_compress(data_in, *cpage_out, datalen, cdatalen); if (!ret) { return JFFS2_COMPR_RTIME; } #endif kfree(*cpage_out); #endif /* Compression */ + out: *cpage_out = data_in; *datalen = *cdatalen; return JFFS2_COMPR_NONE; /* We failed to compress */ } -void jffs2_free_comprbuf(unsigned char *orig, unsigned char *comprbuf) +void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) { if (orig != comprbuf) kfree(comprbuf);
--- a/packages/fs/jffs2/current/src/compr_zlib.c +++ b/packages/fs/jffs2/current/src/compr_zlib.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.24 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: compr_zlib.c,v 1.25 2003/12/03 09:25:43 dwmw2 Exp $ * */ @@ -17,8 +17,6 @@ #include <linux/config.h> #include <linux/kernel.h> -#include <linux/vmalloc.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/zlib.h> #include <linux/zutil.h> @@ -39,6 +37,9 @@ static DECLARE_MUTEX(inflate_sem); static z_stream inf_strm, def_strm; #ifdef __KERNEL__ /* Linux-only */ +#include <linux/vmalloc.h> +#include <linux/init.h> + int __init jffs2_zlib_init(void) { def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
--- a/packages/fs/jffs2/current/src/dir-ecos.c +++ b/packages/fs/jffs2/current/src/dir-ecos.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse <dwmw2@cambridge.redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: dir-ecos.c,v 1.4 2003/11/20 16:41:58 dwmw2 Exp $ + * $Id: dir-ecos.c,v 1.10 2003/11/26 15:55:35 dwmw2 Exp $ * */ @@ -17,18 +17,15 @@ /***********************************************************************/ - -/* We keep the dirent list sorted in increasing order of name hash, - and we use the same hash function as the dentries. Makes this - nice and simple -*/ -struct inode *jffs2_lookup(struct inode *dir_i, struct qstr *d_name) +/* Takes length argument because it can be either NUL-terminated or '/'-terminated */ +struct _inode *jffs2_lookup(struct _inode *dir_i, const unsigned char *d_name, int namelen) { struct jffs2_inode_info *dir_f; struct jffs2_sb_info *c; struct jffs2_full_dirent *fd = NULL, *fd_list; uint32_t ino = 0; - struct inode *inode = NULL; + uint32_t hash = full_name_hash(d_name, namelen); + struct _inode *inode = NULL; D1(printk("jffs2_lookup()\n")); @@ -38,11 +35,11 @@ struct inode *jffs2_lookup(struct inode down(&dir_f->sem); /* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */ - for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= d_name->hash; fd_list = fd_list->next) { - if (fd_list->nhash == d_name->hash && + for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= hash; fd_list = fd_list->next) { + if (fd_list->nhash == hash && (!fd || fd_list->version > fd->version) && - strlen(fd_list->name) == d_name->len && - !strncmp(fd_list->name, d_name->name, d_name->len)) { + strlen(fd_list->name) == namelen && + !strncmp(fd_list->name, d_name, namelen)) { fd = fd_list; } } @@ -50,9 +47,9 @@ struct inode *jffs2_lookup(struct inode ino = fd->ino; up(&dir_f->sem); if (ino) { - inode = iget(dir_i->i_sb, ino); + inode = jffs2_iget(dir_i->i_sb, ino); if (!inode) { - printk("iget() failed for ino #%u\n", ino); + printk("jffs2_iget() failed for ino #%u\n", ino); return (ERR_PTR(-EIO)); } } @@ -64,13 +61,13 @@ struct inode *jffs2_lookup(struct inode -int jffs2_create(struct inode *dir_i, struct qstr *d_name, int mode, - struct inode **new_i) +int jffs2_create(struct _inode *dir_i, const unsigned char *d_name, int mode, + struct _inode **new_i) { struct jffs2_raw_inode *ri; struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; - struct inode *inode; + struct _inode *inode; int ret; ri = jffs2_alloc_raw_inode(); @@ -93,12 +90,11 @@ int jffs2_create(struct inode *dir_i, st dir_f = JFFS2_INODE_INFO(dir_i); ret = jffs2_do_create(c, dir_f, f, ri, - d_name->name, d_name->len); + d_name, strlen(d_name)); if (ret) { - jffs2_clear_inode(inode); - make_bad_inode(inode); - iput(inode); + inode->i_nlink = 0; + jffs2_iput(inode); jffs2_free_raw_inode(ri); return ret; } @@ -114,15 +110,15 @@ int jffs2_create(struct inode *dir_i, st /***********************************************************************/ -int jffs2_unlink(struct inode *dir_i, struct inode *d_inode, struct qstr *d_name) +int jffs2_unlink(struct _inode *dir_i, struct _inode *d_inode, const unsigned char *d_name) { struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb); struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(d_inode); int ret; - ret = jffs2_do_unlink(c, dir_f, d_name->name, - d_name->len, dead_f); + ret = jffs2_do_unlink(c, dir_f, d_name, + strlen(d_name), dead_f); if (dead_f->inocache) d_inode->i_nlink = dead_f->inocache->nlink; return ret; @@ -130,7 +126,7 @@ int jffs2_unlink(struct inode *dir_i, st /***********************************************************************/ -int jffs2_link (struct inode *old_d_inode, struct inode *dir_i, struct qstr *d_name) +int jffs2_link (struct _inode *old_d_inode, struct _inode *dir_i, const unsigned char *d_name) { struct jffs2_sb_info *c = JFFS2_SB_INFO(old_d_inode->i_sb); struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_d_inode); @@ -141,7 +137,7 @@ int jffs2_link (struct inode *old_d_inod uint8_t type = (old_d_inode->i_mode & S_IFMT) >> 12; if (!type) type = DT_REG; - ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, d_name->name, d_name->len); + ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, d_name, strlen(d_name)); if (!ret) { down(&f->sem); @@ -151,11 +147,11 @@ int jffs2_link (struct inode *old_d_inod return ret; } -int jffs2_mkdir (struct inode *dir_i, struct qstr *d_name, int mode, struct inode **new_i) +int jffs2_mkdir (struct _inode *dir_i, const unsigned char *d_name, int mode) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; - struct inode *inode; + struct _inode *inode; struct jffs2_raw_inode *ri; struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; @@ -175,7 +171,7 @@ int jffs2_mkdir (struct inode *dir_i, st /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ - namelen = d_name->len; + namelen = strlen(d_name); ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL); if (ret) { @@ -204,7 +200,8 @@ int jffs2_mkdir (struct inode *dir_i, st /* Eeek. Wave bye bye */ up(&f->sem); jffs2_complete_reservation(c); - jffs2_clear_inode(inode); + inode->i_nlink = 0; + jffs2_iput(inode); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be @@ -217,7 +214,8 @@ int jffs2_mkdir (struct inode *dir_i, st ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); if (ret) { /* Eep. */ - jffs2_clear_inode(inode); + inode->i_nlink = 0; + jffs2_iput(inode); return ret; } @@ -225,7 +223,8 @@ int jffs2_mkdir (struct inode *dir_i, st if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); - jffs2_clear_inode(inode); + inode->i_nlink = 0; + jffs2_iput(inode); return -ENOMEM; } @@ -244,9 +243,9 @@ int jffs2_mkdir (struct inode *dir_i, st rd->nsize = namelen; rd->type = DT_DIR; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); - rd->name_crc = cpu_to_je32(crc32(0, d_name->name, namelen)); + rd->name_crc = cpu_to_je32(crc32(0, d_name, namelen)); - fd = jffs2_write_dirent(c, dir_f, rd, d_name->name, namelen, phys_ofs, ALLOC_NORMAL); + fd = jffs2_write_dirent(c, dir_f, rd, d_name, namelen, phys_ofs, ALLOC_NORMAL); jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); @@ -255,7 +254,8 @@ int jffs2_mkdir (struct inode *dir_i, st /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ up(&dir_f->sem); - jffs2_clear_inode(inode); + inode->i_nlink = 0; + jffs2_iput(inode); return PTR_ERR(fd); } @@ -264,11 +264,11 @@ int jffs2_mkdir (struct inode *dir_i, st jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); - *new_i = inode; + jffs2_iput(inode); return 0; } -int jffs2_rmdir (struct inode *dir_i, struct inode *d_inode, struct qstr *d_name) +int jffs2_rmdir (struct _inode *dir_i, struct _inode *d_inode, const unsigned char *d_name) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode); struct jffs2_full_dirent *fd; @@ -280,8 +280,8 @@ int jffs2_rmdir (struct inode *dir_i, st return jffs2_unlink(dir_i, d_inode, d_name); } -int jffs2_rename (struct inode *old_dir_i, struct inode *d_inode, struct qstr *old_d_name, - struct inode *new_dir_i, struct qstr *new_d_name) +int jffs2_rename (struct _inode *old_dir_i, struct _inode *d_inode, const unsigned char *old_d_name, + struct _inode *new_dir_i, const unsigned char *new_d_name) { int ret; struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); @@ -331,7 +331,7 @@ int jffs2_rename (struct inode *old_dir_ ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i), d_inode->i_ino, type, - new_d_name->name, new_d_name->len); + new_d_name, strlen(new_d_name)); if (ret) return ret; @@ -349,7 +349,7 @@ int jffs2_rename (struct inode *old_dir_ /* Unlink the original */ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), - old_d_name->name, old_d_name->len, NULL); + old_d_name, strlen(old_d_name), NULL); if (ret) { /* Oh shit. We really ought to make a single node which can do both atomically */
--- a/packages/fs/jffs2/current/src/erase.c +++ b/packages/fs/jffs2/current/src/erase.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.57 2003/11/04 14:46:13 dwmw2 Exp $ + * $Id: erase.c,v 1.58 2003/11/26 13:02:46 dwmw2 Exp $ * */ @@ -283,11 +283,6 @@ static void jffs2_free_all_node_refs(str jeb->last_node = NULL; } -void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) -{ - OFNI_BS_2SFFJ(c)->s_dirt = 1; -} - static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_raw_node_ref *marker_ref = NULL;
new file mode 100644 --- /dev/null +++ b/packages/fs/jffs2/current/src/flashio.c @@ -0,0 +1,165 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by Dominic Ostrowski <dominic.ostrowski@3glab.com> + * Contributors: David Woodhouse, Nick Garnett, Richard Panton. + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: flashio.c,v 1.1 2003/11/26 14:09:29 dwmw2 Exp $ + * + */ + +#include <linux/kernel.h> +#include "nodelist.h" + +#include <cyg/io/io.h> +#include <cyg/io/config_keys.h> +#include <cyg/io/flash.h> + +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) ? ENOERR : -EIO); +} + +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) ? ENOERR : -EIO); +} + +int +jffs2_flash_direct_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); +} +
--- a/packages/fs/jffs2/current/src/fs-ecos.c +++ b/packages/fs/jffs2/current/src/fs-ecos.c @@ -1,32 +1,31 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by Dominic Ostrowski <dominic.ostrowski@3glab.com> * Contributors: David Woodhouse, Nick Garnett, Richard Panton. * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: fs-ecos.c,v 1.11 2003/11/20 16:41:58 dwmw2 Exp $ + * $Id: fs-ecos.c,v 1.33 2003/12/02 10:43:03 dwmw2 Exp $ * */ #include <linux/types.h> #include <linux/stat.h> #include <linux/kernel.h> -#include "jffs2port.h" #include <linux/jffs2.h> #include <linux/jffs2_fs_sb.h> #include <linux/jffs2_fs_i.h> #include <linux/pagemap.h> +#include <linux/crc32.h> #include "nodelist.h" #include <errno.h> #include <string.h> #include <cyg/io/io.h> #include <cyg/io/config_keys.h> -#include <cyg/io/flash.h> #if (__GNUC__ == 3) && (__GNUC_MINOR__ == 2) && defined (__ARM_ARCH_4__) #error This compiler is known to be broken. Please see: @@ -40,44 +39,52 @@ 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); + int mode, cyg_file * fte); +#ifdef CYGOPT_FS_JFFS2_WRITE static int jffs2_ops_unlink(cyg_mtab_entry * mte, cyg_dir dir, - const char *name); + 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); + 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); + cyg_dir dir2, const char *name2, int type); +#endif static int jffs2_opendir(cyg_mtab_entry * mte, cyg_dir dir, const char *name, - cyg_file * fte); + cyg_file * fte); static int jffs2_chdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name, - cyg_dir * dir_out); + cyg_dir * dir_out); static int jffs2_stat(cyg_mtab_entry * mte, cyg_dir dir, const char *name, - struct stat *buf); + struct stat *buf); static int jffs2_getinfo(cyg_mtab_entry * mte, cyg_dir dir, const char *name, - int key, void *buf, int len); + 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); + int key, void *buf, int len); // File operations static int jffs2_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +#ifdef CYGOPT_FS_JFFS2_WRITE static int jffs2_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +#endif 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); + 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); + int len); static int jffs2_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf, - int len); + 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); + +static int jffs2_read_inode (struct _inode *inode); +static void jffs2_clear_inode (struct _inode *inode); + //========================================================================== // Filesystem table entries @@ -87,34 +94,53 @@ static int jffs2_fo_dirlseek(struct CYG_ // For simplicity we use _FILESYSTEM synchronization for all accesses since // we should never block in any filesystem operations. +#ifdef CYGOPT_FS_JFFS2_WRITE 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); + CYG_SYNCMODE_FILE_FILESYSTEM | CYG_SYNCMODE_IO_FILE, + 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); +#else +FSTAB_ENTRY(jffs2_fste, "jffs2", 0, + CYG_SYNCMODE_FILE_FILESYSTEM | CYG_SYNCMODE_IO_FILE, + jffs2_mount, + jffs2_umount, + jffs2_open, + (cyg_fsop_unlink *)cyg_fileio_erofs, + (cyg_fsop_mkdir *)cyg_fileio_erofs, + (cyg_fsop_rmdir *)cyg_fileio_erofs, + (cyg_fsop_rename *)cyg_fileio_erofs, + (cyg_fsop_link *)cyg_fileio_erofs, + jffs2_opendir, + jffs2_chdir, jffs2_stat, jffs2_getinfo, jffs2_setinfo); +#endif // ------------------------------------------------------------------------- // 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 + jffs2_fo_read, +#ifdef CYGOPT_FS_JFFS2_WRITE + jffs2_fo_write, +#else + (cyg_fileop_write *) cyg_fileio_erofs, +#endif + jffs2_fo_lseek, + jffs2_fo_ioctl, + cyg_fileio_seltrue, + jffs2_fo_fsync, + jffs2_fo_close, + jffs2_fo_fstat, + jffs2_fo_getinfo, + jffs2_fo_setinfo }; // ------------------------------------------------------------------------- @@ -124,35 +150,34 @@ static cyg_fileops jffs2_fileops = { // 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 + 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 +static unsigned char gc_buffer[PAGE_CACHE_SIZE]; //avoids malloc when user may be under memory pressure static unsigned char n_fs_mounted = 0; // a counter to track the number of jffs2 instances mounted //========================================================================== // 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? + 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; @@ -160,53 +185,37 @@ typedef struct jffs2_dirsearch jffs2_dir //========================================================================== // 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) +static void icache_evict(struct _inode *root_i, struct _inode *i) { - struct inode *cached_inode; - struct inode *next_inode; + struct _inode *this = root_i, *next; - 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; // Previous 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); - } - } - } + restart: + 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) + while (this) { + next = this->i_cache_next; + if (this != i && this->i_count == 0) { + struct _inode *parent = this->i_parent; + if (this->i_cache_next) + this->i_cache_next->i_cache_prev = this->i_cache_prev; + if (this->i_cache_prev) + this->i_cache_prev->i_cache_next = this->i_cache_next; + jffs2_clear_inode(this); + memset(this, 0x5a, sizeof(*this)); + free(this); + if (parent && parent != this) { + parent->i_count--; + this = root_i; + goto restart; + } + } + this = next; + } } //========================================================================== @@ -217,16 +226,18 @@ static void icache_evict(struct inode *r // Initialize a dirsearch object to start a search static void init_dirsearch(jffs2_dirsearch * ds, - struct inode *dir, const char *name) + 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; + D2(printf("init_dirsearch name = %s\n", name)); + D2(printf("init_dirsearch dir = %x\n", dir)); + + dir->i_count++; + ds->dir = dir; + ds->path = name; + ds->node = dir; + ds->name = name; + ds->namelen = 0; + ds->last = false; } // ------------------------------------------------------------------------- @@ -236,87 +247,72 @@ static void init_dirsearch(jffs2_dirsear 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; - 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")); - D2(printf("find_entry\n")); + // check that we really have a directory + if (!S_ISDIR(dir->i_mode)) + return ENOTDIR; - // 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++; + // 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; + // 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; + // 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 + 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->dir = ds->node; + ds->node = ds->dir->i_parent; + ds->node->i_count++; + return ENOERR; + case 1: + // Dot is consumed + D2(printf("find_entry found .\n")); + ds->node = ds->dir; + ds->dir->i_count++; + return ENOERR; + } - hashname = name; - this.name = hashname; - c = *(const unsigned char *) hashname; + // Here we have the name and its length set up. + // Search the directory for a matching entry - 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, name, namelen); + D2(printf("find_entry got dir = %x\n", d)); + + if (d == NULL) + return ENOENT; - 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; + // If it's a new directory inode, increase refcount on its parent + if (S_ISDIR(d->i_mode) && !d->i_parent) { + d->i_parent = dir; + dir->i_count++; + } - // The back path for dotdot to follow - d->i_parent = dir; - // pass back the node we have found - ds->node = d; - - return ENOERR; + // pass back the node we have found + ds->node = d; + return ENOERR; } @@ -325,107 +321,113 @@ static int find_entry(jffs2_dirsearch * // Main interface to directory search code. This is used in all file // level operations to locate the object named by the pathname. +// Returns with use count incremented on both the sought object and +// the directory it was found in static int jffs2_find(jffs2_dirsearch * d) { - int err; + int err; + + D2(printf("jffs2_find for path =%s\n", d->path)); - D2(printf("jffs2_find for path =%s\n", d->path)); - // Short circuit empty paths - if (*(d->path) == '\0') - return ENOERR; + // Short circuit empty paths + if (*(d->path) == '\0') { + d->node->i_count++; + return ENOERR; + } - // iterate down directory tree until we find the object - // we want. - for (;;) { - err = find_entry(d); + // iterate down directory tree until we find the object + // we want. + for (;;) { + err = find_entry(d); - if (err != ENOERR) - return err; + if (err != ENOERR) + return err; - if (d->last) - return ENOERR; + if (d->last) + return ENOERR; - // every inode traversed in the find is temporary and should be free'd - //iput(d->dir); + /* We're done with it, although it we found a subdir that + will have caused the refcount to have been increased */ + jffs2_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 - } + // 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) +static int jffs2_pathconf(struct _inode *node, struct cyg_pathconf_info *info) { - int err = ENOERR; - D2(printf("jffs2_pathconf\n")); + int err = ENOERR; + D2(printf("jffs2_pathconf\n")); - switch (info->name) { - case _PC_LINK_MAX: - info->value = LINK_MAX; - break; + 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_CANON: + info->value = -1; // not supported + err = EINVAL; + break; - case _PC_MAX_INPUT: - 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_NAME_MAX: + info->value = NAME_MAX; + break; - case _PC_PATH_MAX: - info->value = PATH_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_PIPE_BUF: + info->value = -1; // not supported + err = EINVAL; + break; - case _PC_ASYNC_IO: - 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_CHOWN_RESTRICTED: + info->value = -1; // not supported + err = EINVAL; + break; - case _PC_NO_TRUNC: - info->value = 0; - break; + case _PC_NO_TRUNC: + info->value = 0; + break; - case _PC_PRIO_IO: - info->value = 0; - break; + case _PC_PRIO_IO: + info->value = 0; + break; - case _PC_SYNC_IO: - info->value = 0; - break; + case _PC_SYNC_IO: + info->value = 0; + break; - case _PC_VDISABLE: - info->value = -1; // not supported - err = EINVAL; - break; + case _PC_VDISABLE: + info->value = -1; // not supported + err = EINVAL; + break; - default: - err = EINVAL; - break; - } + default: + err = EINVAL; + break; + } - return err; + return err; } //========================================================================== @@ -437,193 +439,223 @@ static int jffs2_pathconf(struct inode * // 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); + struct jffs2_sb_info *c; + Cyg_ErrNo err; + cyg_uint32 len; + cyg_io_flash_getconfig_devsize_t ds; + cyg_io_flash_getconfig_blocksize_t bs; - 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; - } + D1(printk(KERN_DEBUG "jffs2: read_super\n")); + + c = JFFS2_SB_INFO(sb); - 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; - } + 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; + } - 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; + 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; - return 0; + D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n")); + sb->s_root = jffs2_iget(sb, 1); + if (IS_ERR(sb->s_root)) { + D1(printk(KERN_WARNING "get root inode failed\n")); + err = PTR_ERR(sb->s_root); + sb->s_root = NULL; + goto out_nodes; + } - out_root_i: - iput(root_i); + return 0; + out_nodes: - jffs2_free_ino_caches(c); - jffs2_free_raw_node_refs(c); - free(c->blocks); + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + free(c->blocks); - return err; + return err; } static int jffs2_mount(cyg_fstab_entry * fste, cyg_mtab_entry * mte) { - extern cyg_mtab_entry cyg_mtab[], cyg_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; + extern cyg_mtab_entry cyg_mtab[], cyg_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")); + D2(printf("jffs2_mount\n")); - err = cyg_io_lookup(mte->devname, &t); - if (err != ENOERR) - return -err; + 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 = &cyg_mtab[0]; m != &cyg_mtab_end; m++) { - // stop if there are more than the configured maximum - if (m - &cyg_mtab[0] >= CYGNUM_FILEIO_MTAB_MAX) { - m = &cyg_mtab_end; - break; - } - if (m->valid && strcmp(m->fsname, "jffs2") == 0 && - strcmp(m->devname, mte->devname) == 0) { - jffs2_sb = (struct super_block *) m->data; - } - } + // 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 = &cyg_mtab[0]; m != &cyg_mtab_end; m++) { + // stop if there are more than the configured maximum + if (m - &cyg_mtab[0] >= CYGNUM_FILEIO_MTAB_MAX) { + m = &cyg_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) { + jffs2_sb = malloc(sizeof (struct super_block)); - if (jffs2_sb == NULL) - return ENOMEM; + 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 = 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); + 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); if (n_fs_mounted++ == 0) jffs2_create_slab_caches(); // No error check, cannot fail - err = jffs2_read_super(jffs2_sb); + err = jffs2_read_super(jffs2_sb); - if (err) { + if (err) { if (--n_fs_mounted == 0) jffs2_destroy_slab_caches(); - free(jffs2_sb); - free(c->inocache_list); - return 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 + 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,0); - } - mte->data = (CYG_ADDRWORD) jffs2_sb; + D2(printf("jffs2_mount erasing pending blocks\n")); +#ifdef CYGOPT_FS_JFFS2_WRITE + if (!jffs2_is_readonly(c)) + jffs2_erase_pending_blocks(c,0); +#endif +#ifdef CYGOPT_FS_JFFS2_GCTHREAD + jffs2_start_garbage_collect_thread(c); +#endif + } + 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)); + 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; + return ENOERR; } +extern cyg_dir cyg_cdir_dir; +extern cyg_mtab_entry *cyg_cdir_mtab_entry; + // ------------------------------------------------------------------------- // 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")); + 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); - // Only really umount if this is the only mount - if (jffs2_sb->s_mount_count == 1) { - - 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 + D2(printf("jffs2_umount\n")); - //Clear root inode - //root_i = NULL; + // Only really umount if this is the only mount + if (jffs2_sb->s_mount_count == 1) { + icache_evict(root, NULL); + if (root->i_cache_next != NULL) { + struct _inode *inode = root; + printf("Refuse to unmount.\n"); + while (inode) { + printf("Ino #%u has use count %d\n", + inode->i_ino, inode->i_count); + inode = inode->i_cache_next; + } + // root icount was set to 1 on mount + return EBUSY; + } + if (root->i_count == 2 && + cyg_cdir_mtab_entry == mte && + cyg_cdir_dir == (cyg_dir)root && + !strcmp(mte->name, "/")) { + /* If we were mounted on root, there's no + way for the cwd to change out and free + the file system for unmounting. So we hack + it -- if cwd is '/' we unset it. Perhaps + we should allow chdir(NULL) to unset + cyg_cdir_dir? */ + cyg_cdir_dir = CYG_DIR_NULL; + jffs2_iput(root); + } + /* Argh. The fileio code sets this; never clears it */ + if (cyg_cdir_mtab_entry == mte) + cyg_cdir_mtab_entry = 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 superblock & root pointer - mte->root = CYG_DIR_NULL; + if (root->i_count != 1) { + printf("Ino #1 has use count %d\n", + root->i_count); + return EBUSY; + } +#ifdef CYGOPT_FS_JFFS2_GCTHREAD + jffs2_stop_garbage_collect_thread(c); +#endif + jffs2_iput(root); // Time to free the root inode + free(root); + //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 superblock & root pointer + mte->root = CYG_DIR_NULL; mte->data = 0; - 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")); - } else { - jffs2_sb->s_mount_count--; + 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")); + } else { + jffs2_sb->s_mount_count--; } if (--n_fs_mounted == 0) jffs2_destroy_slab_caches(); - return ENOERR; + return ENOERR; } // ------------------------------------------------------------------------- @@ -631,142 +663,127 @@ static int jffs2_umount(cyg_mtab_entry * // 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) + int mode, cyg_file * file) { - jffs2_dirsearch ds; - struct inode *node = NULL; - int err; + jffs2_dirsearch ds; + struct _inode *node = NULL; + int err; - D2(printf("jffs2_open\n")); - - icache_evict((struct inode *) mte->root, (struct inode *) dir); + D2(printf("jffs2_open\n")); - init_dirsearch(&ds, (struct inode *) dir, name); - - err = jffs2_find(&ds); + /* If no chdir has been called and we were the first file system + mounted, we get called with dir == NULL. Deal with it */ + if (!dir) + dir = mte->root; - if (err == ENOENT) { - if (ds.last && (mode & O_CREAT)) { - unsigned long hash; - struct qstr this; - unsigned int c; - const char *hashname; +#ifndef CYGOPT_FS_JFFS2_WRITE + if (mode & (O_CREAT|O_TRUNC|O_WRONLY)) + return EROFS; +#endif + init_dirsearch(&ds, (struct _inode *) dir, name); + + err = jffs2_find(&ds); - // 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. + if (err == ENOENT) { +#ifdef CYGOPT_FS_JFFS2_WRITE + if (ds.last && (mode & O_CREAT)) { - hashname = ds.name; - this.name = hashname; - c = *(const unsigned 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. + + err = jffs2_create(ds.dir, ds.name, S_IRUGO|S_IXUGO|S_IWUSR|S_IFREG, &node); - 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); + if (err != 0) { + //Possible orphaned inode on the flash - but will be gc'd + return err; + } - 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; + } +#endif + } else if (err == ENOERR) { + // The node exists. If the O_CREAT and O_EXCL bits are set, we + // must fail the open. - 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)) { + jffs2_iput(ds.node); + err = EEXIST; + } else + node = ds.node; + } - if ((mode & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) - err = EEXIST; - else - node = ds.node; - } + // Finished with the directory now + jffs2_iput(ds.dir); + + if (err != ENOERR) + return err; - 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. + // Check that we actually have a file here + if (S_ISDIR(node->i_mode)) { + jffs2_iput(node); + return EISDIR; + } - 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; +#ifndef CYGOPT_FS_JFFS2_WRITE + if (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_count++; // Count successful open - - // Initialize the file object + node->i_size = 0; + jffs2_truncate_fraglist(c, &f->fragtree, 0); + // Update file times + node->i_ctime = node->i_mtime = cyg_timestamp(); + } +#endif + // Initialise 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; - 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; + return ENOERR; } +#ifdef CYGOPT_FS_JFFS2_WRITE // ------------------------------------------------------------------------- // 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; + jffs2_dirsearch ds; + int err; - D2(printf("jffs2_ops_unlink\n")); + D2(printf("jffs2_ops_unlink\n")); + + init_dirsearch(&ds, (struct _inode *) dir, name); - icache_evict((struct inode *) mte->root, (struct inode *) dir); - - init_dirsearch(&ds, (struct inode *) dir, name); + err = jffs2_find(&ds); - err = jffs2_find(&ds); - - if (err != ENOERR) - return err; + if (err != ENOERR) { + jffs2_iput(ds.dir); + 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; + // Cannot unlink directories, use rmdir() instead + if (S_ISDIR(ds.node->i_mode)) { + jffs2_iput(ds.dir); + jffs2_iput(ds.node); + return EPERM; + } - 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); + // Delete it from its directory - err = jffs2_unlink(ds.dir, ds.node, &this); + err = jffs2_unlink(ds.dir, ds.node, ds.name); + jffs2_iput(ds.dir); + jffs2_iput(ds.node); - return err; + return -err; } // ------------------------------------------------------------------------- @@ -775,57 +792,33 @@ static int jffs2_ops_unlink(cyg_mtab_ent 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")); + jffs2_dirsearch ds; + int err; - icache_evict((struct inode *) mte->root, (struct inode *) dir); + D2(printf("jffs2_ops_mkdir\n")); - init_dirsearch(&ds, (struct inode *) dir, name); - - err = jffs2_find(&ds); + init_dirsearch(&ds, (struct _inode *) dir, name); - 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. + err = jffs2_find(&ds); - hashname = ds.name; - this.name = hashname; - c = *(const unsigned char *) hashname; + if (err == ENOENT) { + if (ds.last) { + // The entry does not exist, and it is the last element in + // the pathname, so we can create it here. - 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; + err = -jffs2_mkdir(ds.dir, ds.name, S_IRUGO|S_IXUGO|S_IWUSR); + } + // If this was not the last element, then an intermediate + // directory does not exist. + } else { + // If there we no error, something already exists with that + // name, so we cannot create another one. + jffs2_iput(ds.node); + if (err == ENOERR) + err = EEXIST; + } + jffs2_iput(ds.dir); + return err; } // ------------------------------------------------------------------------- @@ -834,47 +827,32 @@ static int jffs2_ops_mkdir(cyg_mtab_entr 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")); + jffs2_dirsearch ds; + int err; - icache_evict((struct inode *) mte->root, (struct inode *) dir); - - init_dirsearch(&ds, (struct inode *) dir, name); + D2(printf("jffs2_ops_rmdir\n")); - err = jffs2_find(&ds); + init_dirsearch(&ds, (struct _inode *) dir, name); - if (err != ENOERR) - return err; + err = jffs2_find(&ds); - // 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; + if (err != ENOERR) { + jffs2_iput(ds.dir); + return err; + } - 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); + // Check that this is actually a directory. + if (!S_ISDIR(ds.node->i_mode)) { + jffs2_iput(ds.dir); + jffs2_iput(ds.node); + return EPERM; + } - err = jffs2_rmdir(ds.dir, ds.node, &this); + err = jffs2_rmdir(ds.dir, ds.node, ds.name); - return err; - - return ENOERR; + jffs2_iput(ds.dir); + jffs2_iput(ds.node); + return -err; } // ------------------------------------------------------------------------- @@ -882,96 +860,95 @@ static int jffs2_ops_rmdir(cyg_mtab_entr // 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) + 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; + jffs2_dirsearch ds1, ds2; + int err; + + D2(printf("jffs2_ops_rename\n")); + + init_dirsearch(&ds1, (struct _inode *) dir1, name1); - D2(printf("jffs2_ops_rename\n")); - - init_dirsearch(&ds1, (struct inode *) dir1, name1); + err = jffs2_find(&ds1); - err = jffs2_find(&ds1); + if (err != ENOERR) { + jffs2_iput(ds1.dir); + return err; + } - if (err != ENOERR) - return err; + init_dirsearch(&ds2, (struct _inode *) dir2, name2); + + err = jffs2_find(&ds2); - 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; + // Allow through renames to non-existent objects. + if (ds2.last && err == ENOENT) { + ds2.node = NULL; + err = ENOERR; + } - if (err != ENOERR) - return err; + if (err != ENOERR) { + jffs2_iput(ds1.dir); + jffs2_iput(ds1.node); + jffs2_iput(ds2.dir); + return err; + } - // Null rename, just return - if (ds1.node == ds2.node) - return ENOERR; + // Null rename, just return + if (ds1.node == ds2.node) { + err = ENOERR; + goto out; + } - hashname = ds1.name; - this1.name = hashname; - c = *(const unsigned char *) hashname; + // First deal with any entry that is at the destination + if (ds2.node) { + // Check that we are renaming like-for-like - 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); + if (!S_ISDIR(ds1.node->i_mode) && S_ISDIR(ds2.node->i_mode)) { + err = EISDIR; + goto out; + } - hashname = ds2.name; - this2.name = hashname; - c = *(const unsigned char *) hashname; + if (S_ISDIR(ds1.node->i_mode) && !S_ISDIR(ds2.node->i_mode)) { + err = ENOTDIR; + goto out; + } - 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); + // Now delete the destination directory entry + /* Er, what happened to atomicity of rename()? */ + err = -jffs2_unlink(ds2.dir, ds2.node, ds2.name); - // First deal with any entry that is at the destination - if (ds2.node) { - // Check that we are renaming like-for-like + if (err != 0) + goto out; + + } + // 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. - 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; + err = -jffs2_rename(ds1.dir, ds1.node, ds1.name, ds2.dir, ds2.name); - } - // 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; + // Update directory times + if (!err) + ds1.dir->i_ctime = + ds1.dir->i_mtime = + ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp(); + out: + jffs2_iput(ds1.dir); + jffs2_iput(ds1.node); + if (S_ISDIR(ds1.node->i_mode)) { + /* Renamed a directory to elsewhere... so fix up its + i_parent pointer and the i_counts of its old and + new parents. */ + jffs2_iput(ds1.node->i_parent); + ds1.node->i_parent = ds2.dir; + /* We effectively increase its use count by not... */ + } else { + jffs2_iput(ds2.dir); /* ... doing this */ + } + if (ds2.node) + jffs2_iput(ds2.node); + + return -err; } // ------------------------------------------------------------------------- @@ -979,105 +956,106 @@ static int jffs2_ops_rename(cyg_mtab_ent // 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) + 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; + jffs2_dirsearch ds1, ds2; + int err; - D2(printf("jffs2_ops_link\n")); + D2(printf("jffs2_ops_link\n")); - // Only do hard links for now in this filesystem - if (type != CYG_FSLINK_HARD) - return EINVAL; + // Only do hard links for now in this filesystem + if (type != CYG_FSLINK_HARD) + return EINVAL; - init_dirsearch(&ds1, (struct inode *) dir1, name1); + init_dirsearch(&ds1, (struct _inode *) dir1, name1); - err = jffs2_find(&ds1); - - if (err != ENOERR) - return err; + err = jffs2_find(&ds1); - init_dirsearch(&ds2, (struct inode *) dir2, name2); - - err = jffs2_find(&ds2); + if (err != ENOERR) { + jffs2_iput(ds1.dir); + return err; + } - // Don't allow links to existing objects - if (err == ENOERR) - return EEXIST; + init_dirsearch(&ds2, (struct _inode *) dir2, name2); + + err = jffs2_find(&ds2); - // Allow through links to non-existing terminal objects - if (ds2.last && err == ENOENT) - ds2.node = NULL, err = ENOERR; - - if (err != ENOERR) - return err; + // Don't allow links to existing objects + if (err == ENOERR) { + jffs2_iput(ds1.dir); + jffs2_iput(ds1.node); + jffs2_iput(ds2.dir); + jffs2_iput(ds2.node); + return EEXIST; + } - // 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; + // Allow through links to non-existing terminal objects + if (ds2.last && err == ENOENT) { + jffs2_iput(ds2.node); + ds2.node = NULL; + err = ENOERR; + } - 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); + if (err != ENOERR) { + jffs2_iput(ds1.dir); + jffs2_iput(ds1.node); + jffs2_iput(ds2.dir); + return err; + } + + // Now we know that there is no existing node at the destination, + // make a new direntry at the destination. + + err = jffs2_link(ds1.node, ds2.dir, ds2.name); - err = jffs2_link(ds1.node, ds2.dir, &this); + if (err == 0) + ds1.node->i_ctime = + ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp(); - if (err == 0) - ds1.node->i_ctime = - ds2.dir->i_ctime = ds2.dir->i_mtime = cyg_timestamp(); + jffs2_iput(ds1.dir); + jffs2_iput(ds1.node); + jffs2_iput(ds2.dir); - return err; + return -err; } - +#endif /* CYGOPT_FS_JFFS2_WRITE */ // ------------------------------------------------------------------------- // jffs2_opendir() // Open a directory for reading. static int jffs2_opendir(cyg_mtab_entry * mte, cyg_dir dir, const char *name, - cyg_file * file) + cyg_file * file) { - jffs2_dirsearch ds; - int err; + jffs2_dirsearch ds; + int err; - D2(printf("jffs2_opendir\n")); + D2(printf("jffs2_opendir\n")); - icache_evict((struct inode *) mte->root, (struct inode *) dir); + init_dirsearch(&ds, (struct _inode *) dir, name); - init_dirsearch(&ds, (struct inode *) dir, name); + err = jffs2_find(&ds); - err = jffs2_find(&ds); + jffs2_iput(ds.dir); - if (err != ENOERR) - return err; + 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 + // check it is really a directory. + if (!S_ISDIR(ds.node->i_mode)) { + jffs2_iput(ds.node); + return ENOTDIR; + } - // Initialize the file object, setting the f_ops field to a - // special set of file ops. + // 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; + 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; + return ENOERR; } @@ -1086,48 +1064,43 @@ static int jffs2_opendir(cyg_mtab_entry // Change directory support. static int jffs2_chdir(cyg_mtab_entry * mte, cyg_dir dir, const char *name, - cyg_dir * dir_out) + cyg_dir * dir_out) { - D2(printf("jffs2_chdir\n")); + D2(printf("jffs2_chdir\n")); - if (dir_out != NULL) { - // This is a request to get a new directory pointer in - // *dir_out. + if (dir_out != NULL) { + // This is a request to get a new directory pointer in + // *dir_out. - jffs2_dirsearch ds; - int err; + jffs2_dirsearch ds; + int err; - icache_evict((struct inode *) mte->root, (struct inode *) dir); + init_dirsearch(&ds, (struct _inode *) dir, name); - init_dirsearch(&ds, (struct inode *) dir, name); + err = jffs2_find(&ds); + jffs2_iput(ds.dir); - err = jffs2_find(&ds); - - if (err != ENOERR) - return err; + 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++; + // check it is a directory + if (!S_ISDIR(ds.node->i_mode)) + return ENOTDIR; - // 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. + // 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; + struct _inode *node = (struct _inode *) dir; - // Just decrement directory reference count. - dec_refcnt(node); - } + // Just decrement directory reference count. + jffs2_iput(node); + } - return ENOERR; + return ENOERR; } // ------------------------------------------------------------------------- @@ -1135,37 +1108,36 @@ static int jffs2_chdir(cyg_mtab_entry * // Get struct stat info for named object. static int jffs2_stat(cyg_mtab_entry * mte, cyg_dir dir, const char *name, - struct stat *buf) + struct stat *buf) { - jffs2_dirsearch ds; - int err; + jffs2_dirsearch ds; + int err; - D2(printf("jffs2_stat\n")); + D2(printf("jffs2_stat\n")); - icache_evict((struct inode *) mte->root, (struct inode *) dir); - - init_dirsearch(&ds, (struct inode *) dir, name); + init_dirsearch(&ds, (struct _inode *) dir, name); - err = jffs2_find(&ds); + err = jffs2_find(&ds); + jffs2_iput(ds.dir); - if (err != ENOERR) - return err; + if (err != ENOERR) + return err; - // Fill in the status - buf->st_mode = ds.node->i_mode; - buf->st_ino = ds.node->i_ino; - 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; + // Fill in the status + buf->st_mode = ds.node->i_mode; + buf->st_ino = ds.node->i_ino; + buf->st_dev = 0; + buf->st_nlink = ds.node->i_nlink; + buf->st_uid = ds.node->i_uid; + buf->st_gid = ds.node->i_gid; + 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; + jffs2_iput(ds.node); - return ENOERR; + return ENOERR; } // ------------------------------------------------------------------------- @@ -1173,33 +1145,32 @@ static int jffs2_stat(cyg_mtab_entry * m // 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) + int key, void *buf, int len) { - jffs2_dirsearch ds; - int err; + jffs2_dirsearch ds; + int err; - D2(printf("jffs2_getinfo\n")); - - icache_evict((struct inode *) mte->root, (struct inode *) dir); + D2(printf("jffs2_getinfo\n")); - init_dirsearch(&ds, (struct inode *) dir, name); + init_dirsearch(&ds, (struct _inode *) dir, name); - err = jffs2_find(&ds); + err = jffs2_find(&ds); + jffs2_iput(ds.dir); - if (err != ENOERR) - return err; + if (err != ENOERR) + return err; - switch (key) { - case FS_INFO_CONF: - err = jffs2_pathconf(ds.node, (struct cyg_pathconf_info *) buf); - break; + switch (key) { + case FS_INFO_CONF: + err = jffs2_pathconf(ds.node, (struct cyg_pathconf_info *) buf); + break; - default: - err = EINVAL; - } - return err; + default: + err = EINVAL; + } - return ENOERR; + jffs2_iput(ds.node); + return err; } // ------------------------------------------------------------------------- @@ -1207,13 +1178,13 @@ static int jffs2_getinfo(cyg_mtab_entry // 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) + int key, void *buf, int len) { - // No setinfo keys supported at present + // No setinfo keys supported at present - D2(printf("jffs2_setinfo\n")); + D2(printf("jffs2_setinfo\n")); - return EINVAL; + return EINVAL; } //========================================================================== @@ -1225,151 +1196,188 @@ static int jffs2_setinfo(cyg_mtab_entry 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; + 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); + 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); + // 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)); + 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; - } + 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. + // We successfully read some data, update the node's access time + // and update the file offset and transfer residue. - inode->i_atime = cyg_timestamp(); + inode->i_atime = cyg_timestamp(); - uio->uio_resid = resid; - fp->f_offset = pos; + uio->uio_resid = resid; + fp->f_offset = pos; - up(&f->sem); + up(&f->sem); - return ENOERR; + return ENOERR; } + +#ifdef CYGOPT_FS_JFFS2_WRITE // ------------------------------------------------------------------------- // jffs2_fo_write() // Write data to file. +static int jffs2_extend_file (struct _inode *inode, struct jffs2_raw_inode *ri, + unsigned long offset) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_full_dnode *fn; + uint32_t phys_ofs, alloc_len; + int ret = 0; + + /* Make new hole frag from old EOF to new page */ + D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", + (unsigned int)inode->i_size, offset)); + + ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL); + if (ret) + return ret; + + down(&f->sem); + + 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->version = cpu_to_je32(++f->highest_version); + ri->isize = cpu_to_je32(max((uint32_t)inode->i_size, offset)); + + ri->offset = cpu_to_je32(inode->i_size); + ri->dsize = cpu_to_je32(offset - inode->i_size); + ri->csize = cpu_to_je32(0); + ri->compr = JFFS2_COMPR_ZERO; + ri->node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); + ri->data_crc = cpu_to_je32(0); + + fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); + jffs2_complete_reservation(c); + if (IS_ERR(fn)) { + ret = PTR_ERR(fn); + up(&f->sem); + return ret; + } + ret = jffs2_add_full_dnode_to_inode(c, f, fn); + if (f->metadata) { + jffs2_mark_node_obsolete(c, f->metadata->raw); + jffs2_free_full_dnode(f->metadata); + f->metadata = NULL; + } + if (ret) { + D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret)); + jffs2_mark_node_obsolete(c, fn->raw); + jffs2_free_full_dnode(fn); + up(&f->sem); + return ret; + } + inode->i_size = offset; + up(&f->sem); + return 0; +} 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; + struct _inode *inode = (struct _inode *) fp->f_data; + off_t pos = fp->f_offset; + ssize_t resid = uio->uio_resid; + struct jffs2_raw_inode ri; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + 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 = inode->i_size; - // 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; + if (pos < 0) + 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; + memset(&ri, 0, sizeof(ri)); + + ri.ino = cpu_to_je32(f->inocache->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.atime = ri.ctime = ri.mtime = cpu_to_je32(cyg_timestamp()); - // 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; + if (pos > inode->i_size) { + int err; + ri.version = cpu_to_je32(++f->highest_version); + err = jffs2_extend_file(inode, &ri, pos); + if (err) + return -err; + } - 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)); + // 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; - err = - jffs2_prepare_write(node, &write_page, - page_start_pos, - page_start_pos + l); - - if (err != 0) - return err; + uint32_t writtenlen; + int 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); + D2(printf("jffs2_fo_write page_start_pos %d\n", pos)); + D2(printf("jffs2_fo_write transfer size %d\n", l)); - if (writtenlen != l) - return ENOSPC; + err = jffs2_write_inode_range(c, f, &ri, buf, + pos, len, &writtenlen); + if (err) + return -err; + + if (writtenlen != len) + return ENOSPC; - // Update working vars - len -= l; - buf += l; - pos += l; - resid -= l; - } - } + pos += len; + resid -= len; + } - // 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; + // We wrote some data successfully, update the modified and access + // times of the inode, increase its size appropriately, and update + // the file offset and transfer residue. + inode->i_mtime = inode->i_ctime = je32_to_cpu(ri.mtime); + if (pos > inode->i_size) + inode->i_size = pos; - uio->uio_resid = resid; - fp->f_offset = pos; + uio->uio_resid = resid; + fp->f_offset = pos; - return ENOERR; + return ENOERR; } +#endif /* CYGOPT_FS_JFFS2_WRITE */ // ------------------------------------------------------------------------- // jffs2_fo_lseek() @@ -1377,39 +1385,39 @@ static int jffs2_fo_write(struct CYG_FIL 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; + struct _inode *node = (struct _inode *) fp->f_data; + off_t pos = *apos; - D2(printf("jffs2_fo_lseek\n")); + D2(printf("jffs2_fo_lseek\n")); - switch (whence) { - case SEEK_SET: - // Pos is already where we want to be. - break; + 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_CUR: + // Add pos to current offset. + pos += fp->f_offset; + break; - case SEEK_END: - // Add pos to file size. - pos += node->i_size; - break; + case SEEK_END: + // Add pos to file size. + pos += node->i_size; + break; - default: - return EINVAL; - } + 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; + // 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; + // All OK, set fp offset and return new position. + *apos = fp->f_offset = pos; - return ENOERR; + return ENOERR; } // ------------------------------------------------------------------------- @@ -1417,13 +1425,13 @@ static int jffs2_fo_lseek(struct CYG_FIL // Handle ioctls. Currently none are defined. static int jffs2_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, - CYG_ADDRWORD data) + CYG_ADDRWORD data) { - // No Ioctls currenly defined. + // No Ioctls currenly defined. - D2(printf("jffs2_fo_ioctl\n")); + D2(printf("jffs2_fo_ioctl\n")); - return EINVAL; + return EINVAL; } // ------------------------------------------------------------------------- @@ -1432,12 +1440,12 @@ static int jffs2_fo_ioctl(struct CYG_FIL static int jffs2_fo_fsync(struct CYG_FILE_TAG *fp, int mode) { - // Data is always permanently where it belongs, nothing to do - // here. + // Data is always permanently where it belongs, nothing to do + // here. - D2(printf("jffs2_fo_fsync\n")); + D2(printf("jffs2_fo_fsync\n")); - return ENOERR; + return ENOERR; } // ------------------------------------------------------------------------- @@ -1447,15 +1455,15 @@ static int jffs2_fo_fsync(struct CYG_FIL static int jffs2_fo_close(struct CYG_FILE_TAG *fp) { - struct inode *node = (struct inode *) fp->f_data; + struct _inode *node = (struct _inode *) fp->f_data; - D2(printf("jffs2_fo_close\n")); + D2(printf("jffs2_fo_close\n")); - dec_refcnt(node); + jffs2_iput(node); - fp->f_data = 0; // zero data pointer + fp->f_data = 0; // zero data pointer - return ENOERR; + return ENOERR; } // ------------------------------------------------------------------------- @@ -1464,23 +1472,23 @@ static int jffs2_fo_close(struct CYG_FIL static int jffs2_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf) { - struct inode *node = (struct inode *) fp->f_data; + struct _inode *node = (struct _inode *) fp->f_data; - D2(printf("jffs2_fo_fstat\n")); + D2(printf("jffs2_fo_fstat\n")); - // Fill in the status - buf->st_mode = node->i_mode; - buf->st_ino = node->i_ino; - 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; + // Fill in the status + buf->st_mode = node->i_mode; + buf->st_ino = node->i_ino; + buf->st_dev = 0; + buf->st_nlink = node->i_nlink; + buf->st_uid = node->i_uid; + buf->st_gid = node->i_gid; + 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; + return ENOERR; } // ------------------------------------------------------------------------- @@ -1488,24 +1496,24 @@ static int jffs2_fo_fstat(struct CYG_FIL // Get info. Currently only supports fpathconf(). static int jffs2_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf, - int len) + int len) { - struct inode *node = (struct inode *) fp->f_data; - int err; + struct _inode *node = (struct _inode *) fp->f_data; + int err; - D2(printf("jffs2_fo_getinfo\n")); + D2(printf("jffs2_fo_getinfo\n")); - switch (key) { - case FS_INFO_CONF: - err = jffs2_pathconf(node, (struct cyg_pathconf_info *) buf); - break; + switch (key) { + case FS_INFO_CONF: + err = jffs2_pathconf(node, (struct cyg_pathconf_info *) buf); + break; - default: - err = EINVAL; - } - return err; + default: + err = EINVAL; + } + return err; - return ENOERR; + return ENOERR; } // ------------------------------------------------------------------------- @@ -1513,13 +1521,13 @@ static int jffs2_fo_getinfo(struct CYG_F // Set info. Nothing supported here. static int jffs2_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf, - int len) + int len) { - // No setinfo key supported at present + // No setinfo key supported at present - D2(printf("jffs2_fo_setinfo\n")); + D2(printf("jffs2_fo_setinfo\n")); - return ENOERR; + return ENOERR; } //========================================================================== @@ -1531,83 +1539,83 @@ static int jffs2_fo_setinfo(struct CYG_F 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'; + 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; + 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; + if (len < sizeof (struct dirent)) + return EINVAL; - D1(printk - (KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", d_inode->i_ino)); + 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); + f = JFFS2_INODE_INFO(inode); + c = JFFS2_SB_INFO(inode->i_sb); - offset = fp->f_offset; + 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; - } + 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 = 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; + 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); + up(&f->sem); out: - fp->f_offset = ++offset; - if (found) { - uio->uio_resid -= sizeof (struct dirent); - } - return ENOERR; + fp->f_offset = ++offset; + if (found) { + uio->uio_resid -= sizeof (struct dirent); + } + return ENOERR; } // ------------------------------------------------------------------------- @@ -1616,16 +1624,16 @@ static int jffs2_fo_dirread(struct CYG_F static int jffs2_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t * pos, int whence) { - // Only allow SEEK_SET to zero + // Only allow SEEK_SET to zero - D2(printf("jffs2_fo_dirlseek\n")); + D2(printf("jffs2_fo_dirlseek\n")); - if (whence != SEEK_SET || *pos != 0) - return EINVAL; + if (whence != SEEK_SET || *pos != 0) + return EINVAL; - *pos = fp->f_offset = 0; + *pos = fp->f_offset = 0; - return ENOERR; + return ENOERR; } //========================================================================== @@ -1636,347 +1644,179 @@ static int jffs2_fo_dirlseek(struct CYG_ // //========================================================================== -struct page *read_cache_page(unsigned long index, - int (*filler) (void *, struct page *), void *data) +unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + unsigned long offset, + unsigned long *priv) { - // 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; + /* FIXME: This works only with one file system mounted at a time */ + int ret; - err = filler(data, gc_page); - if (err < 0) { - free(gc_page); - gc_page = NULL; - } - } + ret = jffs2_read_inode_range(c, f, gc_buffer, offset, PAGE_CACHE_SIZE); + if (ret) + return ERR_PTR(ret); - 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); + return gc_buffer; } -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 *ilookup(struct super_block *sb, cyg_uint32 ino) -{ - struct inode *inode = NULL; - - D2(printf("ilookup\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) { - inode->i_count++; - break; - } - } - - return inode; -} - -struct inode *iget(struct super_block *sb, cyg_uint32 ino) +void jffs2_gc_release_page(struct jffs2_sb_info *c, + unsigned char *ptr, + unsigned long *priv) { - - // 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")); - - inode = ilookup(sb, ino); - if (inode) - return inode; - - // Not cached, so malloc it - inode = new_inode(sb); - if (inode == NULL) - return 0; - - inode->i_ino = ino; - jffs2_read_inode(inode); - inode->i_count = 1; - 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) { - if(cached_inode->i_cache_prev != NULL) { - cached_inode->i_cache_prev->i_cache_next = cached_inode->i_cache_next; // Previous 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; + /* Do nothing */ } -#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) +static struct _inode *new_inode(struct super_block *sb) { - // Called in super.c jffs2_read_super, - // and gc.c jffs2_garbage_collect_pass + // Only called in write.c jffs2_new_inode + // Always adds itself to inode cache - D2(printf("is_bad_inode\n")); + struct _inode *inode; + struct _inode *cached_inode; - return (inode->i_op == EIO_ERROR); - /*if(i == NULL) - return 1; - return 0; */ -} + inode = malloc(sizeof (struct _inode)); + if (inode == NULL) + 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 + ("malloc new_inode %x ####################################\n", + inode)); - //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); + memset(inode, 0, sizeof (struct _inode)); + inode->i_sb = sb; + inode->i_ino = 1; + inode->i_count = 1; + inode->i_nlink = 1; // Let JFFS2 manage the link count + inode->i_size = 0; - *return_size = (size_t) len; - return ((err == ENOERR) ? ENOERR : -EIO); -} + inode->i_cache_next = NULL; // Newest inode, about to be cached -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); + // 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; + } + } - // 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) ? ENOERR : -EIO); + return inode; } -int -jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, - unsigned long count, loff_t to, size_t * retlen) +static struct _inode *ilookup(struct super_block *sb, cyg_uint32 ino) { - 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; + struct _inode *inode = NULL; - // 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; + D2(printf("ilookup\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) { + inode->i_count++; + break; + } + } - 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; + return inode; } -cyg_bool jffs2_flash_erase(struct jffs2_sb_info * c, - struct jffs2_eraseblock * jeb) +struct _inode *jffs2_iget(struct super_block *sb, cyg_uint32 ino) { - 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); + // 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; + int err; + + D2(printf("jffs2_iget\n")); - e.offset = jeb->offset; - e.len = c->sector_size; - e.err_address = &err_addr; + inode = ilookup(sb, ino); + if (inode) + return inode; + + // Not cached, so malloc it + inode = new_inode(sb); + if (inode == NULL) + return 0; - // 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)); + inode->i_ino = ino; - err = cyg_io_get_config(sb->s_dev, CYG_IO_GET_CONFIG_FLASH_ERASE, - &e, &len); - - return (err != ENOERR || e.flasherr != 0); + err = jffs2_read_inode(inode); + if (err) { + printf("jffs2_read_inode() failed\n"); + jffs2_iput(inode); + inode = NULL; + return ERR_PTR(err); + } + return inode; } // ------------------------------------------------------------------------- +// Decrement the reference count on an inode. If this makes the ref count +// zero, then this inode can be freed. + +void jffs2_iput(struct _inode *i) +{ + // Called in jffs2_find + // (and jffs2_open and jffs2_ops_mkdir?) + // super.c jffs2_read_super, + // and gc.c jffs2_garbage_collect_pass + recurse: + if (!i) { + printf("jffs2_iput() called with NULL inode\n"); + // and let it fault... + } + + i->i_count--; + + if (i->i_count < 0) + BUG(); + + if (i->i_count) + return; + + if (!i->i_nlink) { + struct _inode *parent; + + // Remove from the icache linked list and free immediately + if (i->i_cache_prev) + i->i_cache_prev->i_cache_next = i->i_cache_next; + if (i->i_cache_next) + i->i_cache_next->i_cache_prev = i->i_cache_prev; + + parent = i->i_parent; + jffs2_clear_inode(i); + memset(i, 0x5a, sizeof(*i)); + free(i); + + if (parent && parent != i) { + i = parent; + goto recurse; + } + + } else { + // Evict some _other_ inode with i_count zero, leaving + // this latest one in the cache for a while + icache_evict(i->i_sb->s_root, i); + } +} + + +// ------------------------------------------------------------------------- // EOF jffs2.c -void jffs2_clear_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); +} + +static void jffs2_clear_inode (struct _inode *inode) { /* We can forget about this inode for now - drop all * the nodelists associated with it, etc. @@ -1992,83 +1832,227 @@ void jffs2_clear_inode (struct inode *in /* 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 *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; + 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)); + 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); - 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); - 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) { + jffs2_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); - 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; + + return inode; +} + + +static int 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); - inode->i_size = 0; + jffs2_init_inode_info(f); + + ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); - insert_inode_hash(inode); + if (ret) { + up(&f->sem); + return ret; + } + 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); - return inode; + inode->i_nlink = f->inocache->nlink; + up(&f->sem); + + D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); + return 0; } -void jffs2_read_inode (struct inode *inode) +void jffs2_gc_release_inode(struct jffs2_sb_info *c, + struct jffs2_inode_info *f) +{ + jffs2_iput(OFNI_EDONI_2SFFJ(f)); +} + +struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, + int inum, int nlink) { - struct jffs2_inode_info *f; - struct jffs2_sb_info *c; - struct jffs2_raw_inode latest_node; - int ret; + struct _inode *inode; + struct jffs2_inode_cache *ic; + if (!nlink) { + /* The inode has zero nlink but its nodes weren't yet marked + obsolete. This has to be because we're still waiting for + the final (close() and) jffs2_iput() to happen. + + There's a possibility that the final jffs2_iput() could have + happened while we were contemplating. In order to ensure + that we don't cause a new read_inode() (which would fail) + for the inode in question, we use ilookup() in this case + instead of jffs2_iget(). + + The nlink can't _become_ zero at this point because we're + holding the alloc_sem, and jffs2_do_unlink() would also + need that while decrementing nlink on any inode. + */ + inode = ilookup(OFNI_BS_2SFFJ(c), inum); + if (!inode) { + D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", + inum)); - D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); + spin_lock(&c->inocache_lock); + ic = jffs2_get_ino_cache(c, inum); + if (!ic) { + D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum)); + spin_unlock(&c->inocache_lock); + return NULL; + } + if (ic->state != INO_STATE_CHECKEDABSENT) { + /* Wait for progress. Don't just loop */ + D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n", + ic->ino, ic->state)); + sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); + } else { + spin_unlock(&c->inocache_lock); + } - f = JFFS2_INODE_INFO(inode); - c = JFFS2_SB_INFO(inode->i_sb); + return NULL; + } + } else { + /* Inode has links to it still; they're not going away because + jffs2_do_unlink() would need the alloc_sem and we have it. + Just jffs2_iget() it, and if read_inode() is necessary that's OK. + */ + inode = jffs2_iget(OFNI_BS_2SFFJ(c), inum); + if (IS_ERR(inode)) + return (void *)inode; + } - jffs2_init_inode_info(f); - - ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); + return JFFS2_INODE_INFO(inode); +} + + - 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); +uint32_t jffs2_from_os_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; +} - inode->i_nlink = f->inocache->nlink; - up(&f->sem); +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); - D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); + 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; }
--- a/packages/fs/jffs2/current/src/gc.c +++ b/packages/fs/jffs2/current/src/gc.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.129 2003/11/20 16:40:14 dwmw2 Exp $ + * $Id: gc.c,v 1.132 2003/12/01 11:32:11 dwmw2 Exp $ * */ @@ -36,7 +36,7 @@ static int jffs2_garbage_collect_dnode(s 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); + struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f); /* Called with erase_completion_lock held */ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) @@ -112,10 +112,11 @@ static struct jffs2_eraseblock *jffs2_fi */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) { + struct jffs2_inode_info *f; struct jffs2_inode_cache *ic; struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *raw; - int ret = 0; + int ret = 0, inum, nlink; if (down_interruptible(&c->alloc_sem)) return -EINTR; @@ -249,7 +250,9 @@ int jffs2_garbage_collect_pass(struct jf ic = jffs2_raw_ref_to_ic(raw); - /* We need to hold the inocache */ + /* We need to hold the inocache. Either the erase_completion_lock or + the inocache_lock are sufficient; we trade down since the inocache_lock + causes less contention. */ spin_lock(&c->inocache_lock); spin_unlock(&c->erase_completion_lock); @@ -343,14 +346,26 @@ int jffs2_garbage_collect_pass(struct jf /* Fall through if it wanted us to, with inocache_lock held */ } - /* FIXME: We still have the inocache_lock held. This is ugly. - It's done to prevent the fairly unlikely race where the - gcblock is entirely obsoleted by the final close of a file - which had the only valid nodes in the block, followed by - erasure, followed by freeing of the ic because the erased - block(s) held _all_ the nodes of that inode.... never been - seen but it's vaguely possible. */ - ret = jffs2_garbage_collect_live(c, jeb, raw, ic); + /* Prevent the fairly unlikely race where the gcblock is + entirely obsoleted by the final close of a file which had + the only valid nodes in the block, followed by erasure, + followed by freeing of the ic because the erased block(s) + held _all_ the nodes of that inode.... never been seen but + it's vaguely possible. */ + + inum = ic->ino; + nlink = ic->nlink; + spin_unlock(&c->inocache_lock); + + f = jffs2_gc_fetch_inode(c, inum, nlink); + if (IS_ERR(f)) + return PTR_ERR(f); + if (!f) + return 0; + + ret = jffs2_garbage_collect_live(c, jeb, raw, f); + + jffs2_gc_release_inode(c, f); release_sem: up(&c->alloc_sem); @@ -374,78 +389,14 @@ int jffs2_garbage_collect_pass(struct jf } 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_raw_node_ref *raw, struct jffs2_inode_info *f) { - 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; - if (!ic->nlink) { - int inum = ic->ino; - - spin_unlock(&c->inocache_lock); - - /* The inode has zero nlink but its nodes weren't yet marked - obsolete. This has to be because we're still waiting for - the final (close() and) iput() to happen. - - There's a possibility that the final iput() could have - happened while we were contemplating. In order to ensure - that we don't cause a new read_inode() (which would fail) - for the inode in question, we use ilookup() in this case - instead of iget(). - - The nlink can't _become_ zero at this point because we're - holding the alloc_sem, and jffs2_do_unlink() would also - need that while decrementing nlink on any inode. - */ - inode = ilookup(OFNI_BS_2SFFJ(c), inum); - if (!inode) { - D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", - inum)); - - spin_lock(&c->inocache_lock); - ic = jffs2_get_ino_cache(c, inum); - if (!ic) { - D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum)); - spin_unlock(&c->inocache_lock); - return 0; - } - if (ic->state != INO_STATE_CHECKEDABSENT) { - /* Wait for progress. Don't just loop */ - D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n", - ic->ino, ic->state)); - sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); - } else { - spin_unlock(&c->inocache_lock); - } - - return 0; - } - } else { - spin_unlock(&c->inocache_lock); - - /* Inode has links to it still; they're not going away because - jffs2_do_unlink() would need the alloc_sem and we have it. - Just iget() it, and if read_inode() is necessary that's OK. - */ - inode = iget(OFNI_BS_2SFFJ(c), ic->ino); - if (!inode) - return -ENOMEM; - } - if (is_bad_inode(inode)) { - printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d, state %d\n", - ic->ino, ic->nlink, ic->state); - /* NB. This will happen again. We need to do something appropriate here. */ - ret = -EIO; - goto put_out; - } - - f = JFFS2_INODE_INFO(inode); down(&f->sem); /* Now we have the lock for this inode. Check that it's still the one at the head @@ -486,10 +437,10 @@ static int jffs2_garbage_collect_live(st } if (fn) { if (ref_flags(raw) == REF_PRISTINE) { - ret = jffs2_garbage_collect_pristine(c, ic, raw); + ret = jffs2_garbage_collect_pristine(c, f->inocache, raw); if (!ret) { /* Urgh. Return it sensibly. */ - frag->node->raw = ic->nodes; + frag->node->raw = f->inocache->nodes; } if (ret != -EBADFD) goto upnout; @@ -526,8 +477,6 @@ static int jffs2_garbage_collect_live(st } upnout: up(&f->sem); - put_out: - iput(inode); return ret; } @@ -1075,10 +1024,9 @@ static int jffs2_garbage_collect_dnode(s uint32_t alloclen, phys_ofs, offset, orig_end, orig_start; int ret = 0; unsigned char *comprbuf = NULL, *writebuf; - struct page *pg; + unsigned long pg; unsigned char *pg_ptr; - /* FIXME: */ struct inode *inode = OFNI_EDONI_2SFFJ(f); - + memset(&ri, 0, sizeof(ri)); D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n", @@ -1217,16 +1165,12 @@ 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. */ -#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); + pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); + + if (IS_ERR(pg_ptr)) { + printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr)); + return PTR_ERR(pg_ptr); } - pg_ptr = (char *)kmap(pg); offset = start; while(offset < orig_end) { @@ -1267,7 +1211,7 @@ static int jffs2_garbage_collect_dnode(s ri.dsize = cpu_to_je32(datalen); ri.compr = comprtype; ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); - ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen)); + ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC); @@ -1287,10 +1231,7 @@ static int jffs2_garbage_collect_dnode(s } } - kunmap(pg); - /* XXX: Does the page get freed automatically? */ - /* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */ - page_cache_release(pg); + jffs2_gc_release_page(c, pg_ptr, &pg); return ret; }
new file mode 100644 --- /dev/null +++ b/packages/fs/jffs2/current/src/gcthread.c @@ -0,0 +1,52 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@redhat.com> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: gcthread.c,v 1.1 2003/11/26 15:55:35 dwmw2 Exp $ + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/jffs2.h> +#include "nodelist.h" + + +static void jffs2_garbage_collect_thread(struct jffs2_sb_info *c); + +void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) +{ + /* Wake up the thread */ + (void)&jffs2_garbage_collect_thread; +} + +void jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) +{ + /* Start the thread. Doesn't matter if it fails -- it's only an optimisation anyway */ +} + +void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) +{ + /* Stop the thread and wait for it if necessary */ +} + + +static void jffs2_garbage_collect_thread(struct jffs2_sb_info *c) +{ +#define this_thread_should_die() 0 + while(!this_thread_should_die()) { + while(!jffs2_thread_should_wake(c)) { + /* Sleep.... */ + continue; + } + if (jffs2_garbage_collect_pass(c) == -ENOSPC) { + printf("No space for garbage collection. Aborting JFFS2 GC thread\n"); + break; + } + } +}
--- a/packages/fs/jffs2/current/src/malloc-ecos.c +++ b/packages/fs/jffs2/current/src/malloc-ecos.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse <dwmw2@cambridge.redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: malloc-ecos.c,v 1.2 2003/01/09 13:53:44 dwmw2 Exp $ + * $Id: malloc-ecos.c,v 1.4 2003/11/26 15:55:35 dwmw2 Exp $ * */
--- a/packages/fs/jffs2/current/src/nodelist.h +++ b/packages/fs/jffs2/current/src/nodelist.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.113 2003/11/20 16:40:14 dwmw2 Exp $ + * $Id: nodelist.h,v 1.115 2003/11/26 15:30:58 dwmw2 Exp $ * */ @@ -378,6 +378,7 @@ struct rb_node *rb_prev(struct rb_node * void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); /* nodemgmt.c */ +int jffs2_thread_should_wake(struct jffs2_sb_info *c); int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio); int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len); int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new); @@ -453,7 +454,6 @@ 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, int count); -void jffs2_erase_pending_trigger(struct jffs2_sb_info *c); #ifdef CONFIG_JFFS2_FS_NAND /* wbuf.c */
--- a/packages/fs/jffs2/current/src/nodemgmt.c +++ b/packages/fs/jffs2/current/src/nodemgmt.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.106 2003/11/03 17:33:54 dwmw2 Exp $ + * $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $ * */ @@ -708,3 +708,34 @@ void jffs2_dump_block_lists(struct jffs2 } } #endif /* CONFIG_JFFS2_FS_DEBUG */ + +int jffs2_thread_should_wake(struct jffs2_sb_info *c) +{ + int ret = 0; + uint32_t dirty; + + if (c->unchecked_size) { + D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", + c->unchecked_size, c->checked_ino)); + return 1; + } + + /* dirty_size contains blocks on erase_pending_list + * those blocks are counted in c->nr_erasing_blocks. + * If one block is actually erased, it is not longer counted as dirty_space + * but it is counted in c->nr_erasing_blocks, so we add it and subtract it + * with c->nr_erasing_blocks * c->sector_size again. + * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks + * This helps us to force gc and pick eventually a clean block to spread the load. + */ + dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size; + + if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && + (dirty > c->nospc_dirty_size)) + ret = 1; + + D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", + c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); + + return ret; +}
--- a/packages/fs/jffs2/current/src/os-ecos.h +++ b/packages/fs/jffs2/current/src/os-ecos.h @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2002 Red Hat, Inc. + * Copyright (C) 2002-2003 Red Hat, Inc. * * Created by David Woodhouse <dwmw2@cambridge.redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: os-ecos.h,v 1.8 2003/11/20 16:41:58 dwmw2 Exp $ + * $Id: os-ecos.h,v 1.19 2003/11/28 11:38:45 dwmw2 Exp $ * */ @@ -20,102 +20,66 @@ #include <asm/atomic.h> #include <linux/stat.h> #include <linux/compiler.h> -#include "jffs2port.h" -#ifndef CONFIG_JFFS2_FS_DEBUG -# define CONFIG_JFFS2_FS_DEBUG 0 -#endif +#include <pkgconf/system.h> +#include <pkgconf/hal.h> +#include <pkgconf/io_fileio.h> + +#include <cyg/infra/cyg_trac.h> // tracing macros +#include <cyg/infra/cyg_ass.h> // assertion macros -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); +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <dirent.h> + +#include <stdlib.h> +#include <string.h> + +#include <cyg/fileio/fileio.h> + +#include <cyg/hal/drv_api.h> +#include <cyg/infra/diag.h> - 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 +#include <cyg/io/flash.h> + +#include <linux/types.h> +#include <linux/list.h> +#include <asm/bug.h> + +#define printf diag_printf + +struct _inode; +struct super_block; + +struct iovec { + void *iov_base; + ssize_t iov_len; +}; + +static inline unsigned int full_name_hash(const unsigned char * name, unsigned int len) { + + unsigned hash = 0; + while (len--) { + hash = (hash << 4) | (hash >> 28); + hash ^= *(name++); } - printf("os_to_jffs2_mode() cannot convert 0x%x\n", osmode); - BUG(); - return 0; + return hash; } -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; +#ifdef CYGOPT_FS_JFFS2_WRITE +#define jffs2_is_readonly(c) (0) +#else +#define jffs2_is_readonly(c) (1) #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) /* NAND flash not currently supported on eCos */ #define jffs2_can_mark_obsolete(c) (1) #define JFFS2_INODE_INFO(i) (&(i)->jffs2_i) -#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->jffs2_i)) ) ) +#define OFNI_EDONI_2SFFJ(f) ((struct _inode *) ( ((char *)f) - ((char *)(&((struct _inode *)NULL)->jffs2_i)) ) ) #define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size) #define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode) @@ -126,108 +90,98 @@ static inline uint32_t jffs2_to_os_mode #define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime) /* FIXME: eCos doesn't hav a concept of device major/minor numbers */ -#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 JFFS2_F_I_RDEV_MIN(f) ((OFNI_EDONI_2SFFJ(f)->i_rdev)&0xff) +#define JFFS2_F_I_RDEV_MAJ(f) ((OFNI_EDONI_2SFFJ(f)->i_rdev)>>8) - -//#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; - struct list_head i_dentry; +struct _inode { + cyg_uint32 i_ino; - cyg_uint32 i_ino; - atomic_t i_count; - //kdev_t i_dev; + int i_count; mode_t i_mode; - nlink_t i_nlink; + nlink_t i_nlink; // Could we dispense with this? uid_t i_uid; gid_t i_gid; - kdev_t i_rdev; - off_t i_size; time_t i_atime; time_t i_mtime; time_t i_ctime; - unsigned long i_blksize; - unsigned long i_blocks; - //unsigned long i_version; - //struct semaphore i_sem; - //struct semaphore i_zombie; - struct inode_operations *i_op; - struct file_operations *i_fop; // former ->i_op->default_file_ops - struct super_block *i_sb; - //wait_queue_head_t i_wait; - //struct file_lock *i_flock; - //struct address_space *i_mapping; - //struct address_space i_data; - //struct dquot *i_dquot[MAXQUOTAS]; - //struct pipe_inode_info *i_pipe; - //struct block_device *i_bdev; +// union { + unsigned short i_rdev; // For devices only + struct _inode * i_parent; // For directories only + off_t i_size; // For files only +// }; + struct super_block * i_sb; - //unsigned long i_state; - - unsigned int i_flags; - //unsigned char i_sock; - - atomic_t i_writecount; - //unsigned int i_attr_flags; - //uint32_t i_generation; struct jffs2_inode_info jffs2_i; - struct inode *i_parent; - - struct inode *i_cache_prev; - struct inode *i_cache_next; + struct _inode * i_cache_prev; // We need doubly-linked? + struct _inode * i_cache_next; }; #define JFFS2_SB_INFO(sb) (&(sb)->jffs2_sb) - #define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->jffs2_sb)) ) ) struct super_block { - unsigned long s_blocksize; - unsigned char s_blocksize_bits; - unsigned char s_dirt; - //struct super_operations *s_op; - unsigned long s_flags; - unsigned long s_magic; - //struct dentry *s_root; - struct inode *s_root; - struct jffs2_sb_info jffs2_sb; - unsigned long s_mount_count; - cyg_io_handle_t s_dev; + struct jffs2_sb_info jffs2_sb; + struct _inode * s_root; + unsigned long s_mount_count; + cyg_io_handle_t s_dev; }; -#define sleep_on_spinunlock(wq, sl) do { ; } while(0) +#define sleep_on_spinunlock(wq, sl) spin_unlock(sl) #define EBADFD 32767 /* background.c */ +#ifdef CYGOPT_FS_JFFS2_GCTHREAD +void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c); +void jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); +void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); +#else static inline void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) { /* We don't have a GC thread in eCos (yet) */ } +#endif -/* dir.c */ -extern struct file_operations jffs2_dir_operations; -extern struct inode_operations jffs2_dir_inode_operations; +/* fs-ecos.c */ +struct _inode *jffs2_new_inode (struct _inode *dir_i, int mode, struct jffs2_raw_inode *ri); +struct _inode *jffs2_iget(struct super_block *sb, cyg_uint32 ino); +void jffs2_iput(struct _inode * i); +void jffs2_gc_release_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); +struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, int inum, int nlink); +unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + unsigned long offset, unsigned long *priv); +void jffs2_gc_release_page(struct jffs2_sb_info *c, unsigned char *pg, unsigned long *priv); + +/* Avoid polluting eCos namespace with names not starting in jffs2_ */ +#define os_to_jffs2_mode(x) jffs2_from_os_mode(x) +uint32_t jffs2_from_os_mode(uint32_t osmode); +uint32_t jffs2_to_os_mode (uint32_t jmode); + -/* file.c */ -extern struct file_operations jffs2_file_operations; -extern struct inode_operations jffs2_file_inode_operations; -extern struct address_space_operations jffs2_file_address_operations; -int jffs2_null_fsync(struct file *, struct dentry *, int); -int jffs2_setattr (struct dentry *dentry, struct iattr *iattr); -int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg); -int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg); -//int jffs2_readpage (struct file *, struct page *); -int jffs2_readpage (struct inode *d_inode, struct page *pg); -//int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned); -int jffs2_prepare_write (struct inode *d_inode, struct page *pg, unsigned start, unsigned end); -//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); +/* flashio.c */ +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_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); +int jffs2_flash_direct_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); + +// dir-ecos.c +struct _inode *jffs2_lookup(struct _inode *dir_i, const unsigned char *name, int namelen); +int jffs2_create(struct _inode *dir_i, const unsigned char *d_name, int mode, struct _inode **new_i); +int jffs2_mkdir (struct _inode *dir_i, const unsigned char *d_name, int mode); +int jffs2_link (struct _inode *old_d_inode, struct _inode *dir_i, const unsigned char *d_name); +int jffs2_unlink(struct _inode *dir_i, struct _inode *d_inode, const unsigned char *d_name); +int jffs2_rmdir (struct _inode *dir_i, struct _inode *d_inode, const unsigned char *d_name); +int jffs2_rename (struct _inode *old_dir_i, struct _inode *d_inode, const unsigned char *old_d_name, + struct _inode *new_dir_i, const unsigned char *new_d_name); + +/* erase.c */ +static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) +{ } #ifndef CONFIG_JFFS2_FS_NAND #define jffs2_can_mark_obsolete(c) (1) @@ -247,16 +201,9 @@ int jffs2_commit_write (struct inode *d_ #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); -} - +#ifndef BUG_ON #define BUG_ON(x) do { if (unlikely(x)) BUG(); } while(0) +#endif #endif /* __JFFS2_OS_ECOS_H__ */
--- a/packages/fs/jffs2/current/src/write.c +++ b/packages/fs/jffs2/current/src/write.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.77 2003/11/18 21:14:02 dwmw2 Exp $ + * $Id: write.c,v 1.79 2003/12/03 09:41:03 dwmw2 Exp $ * */ @@ -373,8 +373,8 @@ int jffs2_write_inode_range(struct jffs2 break; } down(&f->sem); - datalen = writelen; - cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen); + datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); + cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); comprtype = jffs2_compress(buf, &comprbuf, &datalen, &cdatalen);
--- a/packages/fs/jffs2/current/tests/fileio1.c +++ b/packages/fs/jffs2/current/tests/fileio1.c @@ -97,7 +97,7 @@ diag_printf("<FAIL>: " #_fn "() returned //========================================================================== -#define IOSIZE 100 +#define IOSIZE 1000 #define LONGNAME1 "long_file_name_that_should_take_up_more_than_one_directory_entry_1" #define LONGNAME2 "long_file_name_that_should_take_up_more_than_one_directory_entry_2" @@ -182,7 +182,7 @@ static void listdir( char *name, int sta static void createfile( char *name, size_t size ) { - char buf[IOSIZE]; + unsigned char buf[IOSIZE]; int fd; ssize_t wrote; int i; @@ -255,7 +255,7 @@ static void maxfile( char *name ) static void checkfile( char *name ) { - char buf[IOSIZE]; + unsigned char buf[IOSIZE]; int fd; ssize_t done; int i;
