X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_fs.c;h=86a8e8dd39dc3b52baf093dcab6ba713b6b06366;hp=f2f709b1aaa0efdf33cb769fde65391b519bedb6;hb=9d519e3129cb7bbbda465b5c9c8dcbbe24c60f8a;hpb=17b102ed84b24f9c45ebdba39cacc73eb39e8cae diff --git a/yaffs_fs.c b/yaffs_fs.c index f2f709b..86a8e8d 100644 --- a/yaffs_fs.c +++ b/yaffs_fs.c @@ -1,7 +1,7 @@ /* * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. * - * Copyright (C) 2002-2007 Aleph One Ltd. + * Copyright (C) 2002-2009 Aleph One Ltd. * for Toby Churchill Ltd and Brightstar Engineering * * Created by Charles Manning @@ -32,7 +32,7 @@ */ const char *yaffs_fs_c_version = - "$Id: yaffs_fs.c,v 1.77 2009-03-09 07:25:09 charles Exp $"; + "$Id: yaffs_fs.c,v 1.92 2010-01-19 21:16:30 charles Exp $"; extern const char *yaffs_guts_c_version; #include @@ -54,6 +54,9 @@ extern const char *yaffs_guts_c_version; #include "asm/div64.h" + +#define LOCK_TRACE 0 + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) #include /* Added NCB 15-8-2003 */ @@ -111,6 +114,7 @@ static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) #include #include "yportenv.h" +#include "yaffs_trace.h" #include "yaffs_guts.h" #include @@ -118,7 +122,7 @@ static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) #include "yaffs_mtdif1.h" #include "yaffs_mtdif2.h" -unsigned int yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS; +unsigned int yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS | YAFFS_TRACE_ALWAYS; unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; unsigned int yaffs_auto_checkpoint = 1; @@ -163,6 +167,11 @@ static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino); #define yaffs_SuperToDevice(sb) ((yaffs_Device *)sb->u.generic_sbp) #endif + +#define update_dir_time(dir) do {\ + (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ + } while(0) + static void yaffs_put_super(struct super_block *sb); static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, @@ -315,6 +324,17 @@ static const struct file_operations yaffs_file_operations = { }; #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)) +static void zero_user_segment(struct page *page, unsigned start, unsigned end) +{ + void * kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + start, 0, end - start); + kunmap_atomic(kaddr, KM_USER0); + flush_dcache_page(page); +} +#endif + + static const struct inode_operations yaffs_file_inode_operations = { .setattr = yaffs_setattr, }; @@ -362,17 +382,133 @@ static const struct super_operations yaffs_super_ops = { static void yaffs_GrossLock(yaffs_Device *dev) { - T(YAFFS_TRACE_OS, ("yaffs locking %p\n", current)); + T(LOCK_TRACE && YAFFS_TRACE_OS, ("yaffs locking %p\n", current)); down(&dev->grossLock); - T(YAFFS_TRACE_OS, ("yaffs locked %p\n", current)); + T(LOCK_TRACE && YAFFS_TRACE_OS, ("yaffs locked %p\n", current)); } static void yaffs_GrossUnlock(yaffs_Device *dev) { - T(YAFFS_TRACE_OS, ("yaffs unlocking %p\n", current)); + T(LOCK_TRACE && YAFFS_TRACE_OS, ("yaffs unlocking %p\n", current)); up(&dev->grossLock); } + +/*-----------------------------------------------------------------*/ +/* Directory search context allows us to unlock access to yaffs during + * filldir without causing problems with the directory being modified. + * This is similar to the tried and tested mechanism used in yaffs direct. + * + * A search context iterates along a doubly linked list of siblings in the + * directory. If the iterating object is deleted then this would corrupt + * the list iteration, likely causing a crash. The search context avoids + * this by using the removeObjectCallback to move the search context to the + * next object before the object is deleted. + * + * Many readdirs (and thus seach conexts) may be alive simulateously so + * each yaffs_Device has a list of these. + * + * A seach context lives for the duration of a readdir. + * + * All these functions must be called while yaffs is locked. + */ + +struct yaffs_SearchContext { + yaffs_Device *dev; + yaffs_Object *dirObj; + yaffs_Object *nextReturn; + struct ylist_head others; +}; + +/* + * yaffs_NewSearch() creates a new search context, initialises it and + * adds it to the device's search context list. + * + * Called at start of readdir. + */ +static struct yaffs_SearchContext * yaffs_NewSearch(yaffs_Object *dir) +{ + yaffs_Device *dev = dir->myDev; + struct yaffs_SearchContext *sc = YMALLOC(sizeof(struct yaffs_SearchContext)); + if(sc){ + sc->dirObj = dir; + sc->dev = dev; + if( ylist_empty(&sc->dirObj->variant.directoryVariant.children)) + sc->nextReturn = NULL; + else + sc->nextReturn = ylist_entry( + dir->variant.directoryVariant.children.next, + yaffs_Object,siblings); + YINIT_LIST_HEAD(&sc->others); + ylist_add(&sc->others,&dev->searchContexts); + } + return sc; +} + +/* + * yaffs_EndSearch() disposes of a search context and cleans up. + */ +static void yaffs_EndSearch(struct yaffs_SearchContext * sc) +{ + if(sc){ + ylist_del(&sc->others); + YFREE(sc); + } +} + +/* + * yaffs_SearchAdvance() moves a search context to the next object. + * Called when the search iterates or when an object removal causes + * the search context to be moved to the next object. + */ +static void yaffs_SearchAdvance(struct yaffs_SearchContext *sc) +{ + if(!sc) + return; + + if( sc->nextReturn == NULL || + ylist_empty(&sc->dirObj->variant.directoryVariant.children)) + sc->nextReturn = NULL; + else { + struct ylist_head *next = sc->nextReturn->siblings.next; + + if( next == &sc->dirObj->variant.directoryVariant.children) + sc->nextReturn = NULL; /* end of list */ + else + sc->nextReturn = ylist_entry(next,yaffs_Object,siblings); + } +} + +/* + * yaffs_RemoveObjectCallback() is called when an object is unlinked. + * We check open search contexts and advance any which are currently + * on the object being iterated. + */ +static void yaffs_RemoveObjectCallback(yaffs_Object *obj) +{ + + struct ylist_head *i; + struct yaffs_SearchContext *sc; + struct ylist_head *search_contexts = &obj->myDev->searchContexts; + + + /* Iterate through the directory search contexts. + * If any are currently on the object being removed, then advance + * the search context to the next object to prevent a hanging pointer. + */ + ylist_for_each(i, search_contexts) { + if (i) { + sc = ylist_entry(i, struct yaffs_SearchContext,others); + if(sc->nextReturn == obj) + yaffs_SearchAdvance(sc); + } + } + +} + + +/*-----------------------------------------------------------------*/ + static int yaffs_readlink(struct dentry *dentry, char __user *buffer, int buflen) { @@ -489,8 +625,6 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) d_add(dentry, inode); return NULL; - /* return (ERR_PTR(-EIO)); */ - } @@ -586,7 +720,7 @@ static int yaffs_file_flush(struct file *file) yaffs_GrossLock(dev); - yaffs_FlushFile(obj, 1); + yaffs_FlushFile(obj, 1, 0); yaffs_GrossUnlock(dev); @@ -603,7 +737,7 @@ static int yaffs_readpage_nolock(struct file *f, struct page *pg) yaffs_Device *dev; - T(YAFFS_TRACE_OS, ("yaffs_readpage at %08x, size %08x\n", + T(YAFFS_TRACE_OS, ("yaffs_readpage_nolock at %08x, size %08x\n", (unsigned)(pg->index << PAGE_CACHE_SHIFT), (unsigned)PAGE_CACHE_SIZE)); @@ -623,8 +757,7 @@ static int yaffs_readpage_nolock(struct file *f, struct page *pg) yaffs_GrossLock(dev); - ret = - yaffs_ReadDataFromFile(obj, pg_buf, + ret = yaffs_ReadDataFromFile(obj, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE); @@ -644,7 +777,7 @@ static int yaffs_readpage_nolock(struct file *f, struct page *pg) flush_dcache_page(pg); kunmap(pg); - T(YAFFS_TRACE_OS, ("yaffs_readpage done\n")); + T(YAFFS_TRACE_OS, ("yaffs_readpage_nolock done\n")); return ret; } @@ -657,7 +790,12 @@ static int yaffs_readpage_unlock(struct file *f, struct page *pg) static int yaffs_readpage(struct file *f, struct page *pg) { - return yaffs_readpage_unlock(f, pg); + int ret; + + T(YAFFS_TRACE_OS, ("yaffs_readpage\n")); + ret=yaffs_readpage_unlock(f, pg); + T(YAFFS_TRACE_OS, ("yaffs_readpage done\n")); + return ret; } /* writepage inspired by/stolen from smbfs */ @@ -669,38 +807,46 @@ static int yaffs_writepage(struct page *page) #endif { struct address_space *mapping = page->mapping; - loff_t offset = (loff_t) page->index << PAGE_CACHE_SHIFT; struct inode *inode; unsigned long end_index; char *buffer; yaffs_Object *obj; int nWritten = 0; unsigned nBytes; + loff_t i_size; if (!mapping) BUG(); inode = mapping->host; if (!inode) BUG(); + i_size = i_size_read(inode); - if (offset > inode->i_size) { - T(YAFFS_TRACE_OS, - ("yaffs_writepage at %08x, inode size = %08x!!!\n", - (unsigned)(page->index << PAGE_CACHE_SHIFT), - (unsigned)inode->i_size)); - T(YAFFS_TRACE_OS, - (" -> don't care!!\n")); - unlock_page(page); - return 0; - } + end_index = i_size >> PAGE_CACHE_SHIFT; - end_index = inode->i_size >> PAGE_CACHE_SHIFT; - - /* easy case */ - if (page->index < end_index) + if(page->index < end_index) nBytes = PAGE_CACHE_SIZE; - else - nBytes = inode->i_size & (PAGE_CACHE_SIZE - 1); + else { + nBytes = i_size & (PAGE_CACHE_SIZE -1); + + if (page->index > end_index || !nBytes) { + T(YAFFS_TRACE_OS, + ("yaffs_writepage at %08x, inode size = %08x!!!\n", + (unsigned)(page->index << PAGE_CACHE_SHIFT), + (unsigned)inode->i_size)); + T(YAFFS_TRACE_OS, + (" -> don't care!!\n")); + + zero_user_segment(page,0,PAGE_CACHE_SIZE); + set_page_writeback(page); + unlock_page(page); + end_page_writeback(page); + return 0; + } + } + + if(nBytes != PAGE_CACHE_SIZE) + zero_user_segment(page,nBytes,PAGE_CACHE_SIZE); get_page(page); @@ -726,8 +872,9 @@ static int yaffs_writepage(struct page *page) yaffs_GrossUnlock(obj->myDev); kunmap(page); - SetPageUptodate(page); - UnlockPage(page); + set_page_writeback(page); + unlock_page(page); + end_page_writeback(page); put_page(page); return (nWritten == nBytes) ? 0 : -ENOSPC; @@ -741,15 +888,12 @@ static int yaffs_write_begin(struct file *filp, struct address_space *mapping, { struct page *pg = NULL; pgoff_t index = pos >> PAGE_CACHE_SHIFT; - uint32_t offset = pos & (PAGE_CACHE_SIZE - 1); - uint32_t to = offset + len; int ret = 0; int space_held = 0; - T(YAFFS_TRACE_OS, ("start yaffs_write_begin\n")); /* Get a page */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) pg = grab_cache_page_write_begin(mapping, index, flags); #else pg = __grab_cache_page(mapping, index); @@ -760,6 +904,8 @@ static int yaffs_write_begin(struct file *filp, struct address_space *mapping, ret = -ENOMEM; goto out; } + T(YAFFS_TRACE_OS, ("start yaffs_write_begin index %d(%x) uptodate %d\n",(int)index,(int)index,Page_Uptodate(pg) ? 1 : 0)); + /* Get fs space */ space_held = yaffs_hold_space(filp); @@ -770,7 +916,7 @@ static int yaffs_write_begin(struct file *filp, struct address_space *mapping, /* Update page if required */ - if (!Page_Uptodate(pg) && (offset || to < PAGE_CACHE_SIZE)) + if (!Page_Uptodate(pg)) ret = yaffs_readpage_nolock(filp, pg); if (ret) @@ -799,7 +945,7 @@ static int yaffs_prepare_write(struct file *f, struct page *pg, { T(YAFFS_TRACE_OS, ("yaffs_prepair_write\n")); - if (!Page_Uptodate(pg) && (offset || to < PAGE_CACHE_SIZE)) + if (!Page_Uptodate(pg)) return yaffs_readpage_nolock(f, pg); return 0; } @@ -829,9 +975,8 @@ static int yaffs_write_end(struct file *filp, struct address_space *mapping, ("yaffs_write_end not same size ret %d copied %d\n", ret, copied)); SetPageError(pg); - ClearPageUptodate(pg); } else { - SetPageUptodate(pg); + /* Nothing */ } kunmap(pg); @@ -871,9 +1016,8 @@ static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, ("yaffs_commit_write not same size nWritten %d nBytes %d\n", nWritten, nBytes)); SetPageError(pg); - ClearPageUptodate(pg); } else { - SetPageUptodate(pg); + /* Nothing */ } kunmap(pg); @@ -1053,15 +1197,15 @@ static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, ("yaffs_file_write: hey obj is null!\n")); else T(YAFFS_TRACE_OS, - ("yaffs_file_write about to write writing %zu bytes" - "to object %d at %d\n", - n, obj->objectId, ipos)); + ("yaffs_file_write about to write writing %u(%x) bytes" + "to object %d at %d(%x)\n", + (unsigned) n, (unsigned) n, obj->objectId, ipos,ipos)); nWritten = yaffs_WriteDataToFile(obj, buf, ipos, n, 0); T(YAFFS_TRACE_OS, - ("yaffs_file_write writing %zu bytes, %d written at %d\n", - n, nWritten, ipos)); + ("yaffs_file_write: %d(%x) bytes written\n", + (unsigned )n,(unsigned)n)); if (nWritten > 0) { ipos += nWritten; @@ -1078,7 +1222,7 @@ static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, } yaffs_GrossUnlock(dev); - return nWritten == 0 ? -ENOSPC : nWritten; + return (nWritten == 0) && (n > 0) ? -ENOSPC : nWritten; } /* Space holding and freeing is done to ensure we have space available for write_begin/end */ @@ -1126,10 +1270,11 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) { yaffs_Object *obj; yaffs_Device *dev; + struct yaffs_SearchContext *sc; struct inode *inode = f->f_dentry->d_inode; unsigned long offset, curoffs; - struct ylist_head *i; yaffs_Object *l; + int retVal = 0; char name[YAFFS_MAX_NAME_LENGTH + 1]; @@ -1140,14 +1285,22 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) offset = f->f_pos; + sc = yaffs_NewSearch(obj); + if(!sc){ + retVal = -ENOMEM; + goto unlock_out; + } + T(YAFFS_TRACE_OS, ("yaffs_readdir: starting at %d\n", (int)offset)); if (offset == 0) { T(YAFFS_TRACE_OS, ("yaffs_readdir: entry . ino %d \n", (int)inode->i_ino)); + yaffs_GrossUnlock(dev); if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) goto out; + yaffs_GrossLock(dev); offset++; f->f_pos++; } @@ -1155,9 +1308,11 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) T(YAFFS_TRACE_OS, ("yaffs_readdir: entry .. ino %d \n", (int)f->f_dentry->d_parent->d_inode->i_ino)); + yaffs_GrossUnlock(dev); if (filldir(dirent, "..", 2, offset, f->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) goto out; + yaffs_GrossLock(dev); offset++; f->f_pos++; } @@ -1173,10 +1328,12 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) f->f_version = inode->i_version; } - ylist_for_each(i, &obj->variant.directoryVariant.children) { + while(sc->nextReturn){ curoffs++; + l = sc->nextReturn; if (curoffs >= offset) { - l = ylist_entry(i, yaffs_Object, siblings); + int this_inode = yaffs_GetObjectInode(l); + int this_type = yaffs_GetObjectType(l); yaffs_GetObjectName(l, name, YAFFS_MAX_NAME_LENGTH + 1); @@ -1184,24 +1341,30 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) ("yaffs_readdir: %s inode %d\n", name, yaffs_GetObjectInode(l))); + yaffs_GrossUnlock(dev); + if (filldir(dirent, name, strlen(name), offset, - yaffs_GetObjectInode(l), - yaffs_GetObjectType(l)) < 0) - goto up_and_out; + this_inode, + this_type) < 0) + goto out; + + yaffs_GrossLock(dev); offset++; f->f_pos++; } + yaffs_SearchAdvance(sc); } -up_and_out: -out: +unlock_out: yaffs_GrossUnlock(dev); +out: + yaffs_EndSearch(sc); - return 0; + return retVal; } /* @@ -1289,6 +1452,7 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, if (obj) { inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); d_instantiate(dentry, inode); + update_dir_time(dir); T(YAFFS_TRACE_OS, ("yaffs_mknod created object %d count = %d\n", obj->objectId, atomic_read(&inode->i_count))); @@ -1342,6 +1506,7 @@ static int yaffs_unlink(struct inode *dir, struct dentry *dentry) dir->i_version++; yaffs_GrossUnlock(dev); mark_inode_dirty(dentry->d_inode); + update_dir_time(dir); return 0; } yaffs_GrossUnlock(dev); @@ -1382,8 +1547,10 @@ static int yaffs_link(struct dentry *old_dentry, struct inode *dir, yaffs_GrossUnlock(dev); - if (link) + if (link){ + update_dir_time(dir); return 0; + } return -EPERM; } @@ -1409,6 +1576,7 @@ static int yaffs_symlink(struct inode *dir, struct dentry *dentry, inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); d_instantiate(dentry, inode); + update_dir_time(dir); T(YAFFS_TRACE_OS, ("symlink created OK\n")); return 0; } else { @@ -1429,9 +1597,9 @@ static int yaffs_sync_object(struct file *file, struct dentry *dentry, dev = obj->myDev; - T(YAFFS_TRACE_OS, ("yaffs_sync_object\n")); + T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_sync_object\n")); yaffs_GrossLock(dev); - yaffs_FlushFile(obj, 1); + yaffs_FlushFile(obj, 1, datasync); yaffs_GrossUnlock(dev); return 0; } @@ -1481,7 +1649,10 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, new_dentry->d_inode->i_nlink--; mark_inode_dirty(new_dentry->d_inode); } - + + update_dir_time(old_dir); + if(old_dir != new_dir) + update_dir_time(new_dir); return 0; } else { return -ENOTEMPTY; @@ -1500,18 +1671,30 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) error = inode_change_ok(inode, attr); if (error == 0) { + int result; + if (!error){ + error = inode_setattr(inode, attr); + T(YAFFS_TRACE_OS,("inode_setattr called\n")); + if (attr->ia_valid & ATTR_SIZE) + truncate_inode_pages(&inode->i_data,attr->ia_size); + } dev = yaffs_InodeToObject(inode)->myDev; + if (attr->ia_valid & ATTR_SIZE){ + T(YAFFS_TRACE_OS,("resize to %d(%x)\n",(int)(attr->ia_size),(int)(attr->ia_size))); + } yaffs_GrossLock(dev); - if (yaffs_SetAttributes(yaffs_InodeToObject(inode), attr) == - YAFFS_OK) { + result = yaffs_SetAttributes(yaffs_InodeToObject(inode), attr); + if(result == YAFFS_OK) { error = 0; } else { error = -EPERM; } yaffs_GrossUnlock(dev); - if (!error) - error = inode_setattr(inode, attr); + } + T(YAFFS_TRACE_OS, + ("yaffs_setattr done\n")); + return error; } @@ -1586,17 +1769,33 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf) } + +static void yaffs_flush_sb_inodes(struct super_block *sb) +{ + struct inode *iptr; + yaffs_Object *obj; + + list_for_each_entry(iptr,&sb->s_inodes, i_sb_list){ + obj = yaffs_InodeToObject(iptr); + if(obj){ + T(YAFFS_TRACE_OS, ("flushing obj %d\n",obj->objectId)); + yaffs_FlushFile(obj,1,0); + } + } +} + static int yaffs_do_sync_fs(struct super_block *sb) { yaffs_Device *dev = yaffs_SuperToDevice(sb); - T(YAFFS_TRACE_OS, ("yaffs_do_sync_fs\n")); + T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_do_sync_fs\n")); if (sb->s_dirt) { yaffs_GrossLock(dev); if (dev) { yaffs_FlushEntireDeviceCache(dev); + yaffs_flush_sb_inodes(sb); yaffs_CheckpointSave(dev); } @@ -1615,7 +1814,7 @@ static int yaffs_write_super(struct super_block *sb) #endif { - T(YAFFS_TRACE_OS, ("yaffs_write_super\n")); + T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_write_super\n")); if (yaffs_auto_checkpoint >= 2) yaffs_do_sync_fs(sb); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) @@ -1630,7 +1829,7 @@ static int yaffs_sync_fs(struct super_block *sb, int wait) static int yaffs_sync_fs(struct super_block *sb) #endif { - T(YAFFS_TRACE_OS, ("yaffs_sync_fs\n")); + T(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ("yaffs_sync_fs\n")); if (yaffs_auto_checkpoint >= 1) yaffs_do_sync_fs(sb); @@ -1786,9 +1985,15 @@ typedef struct { int skip_checkpoint_read; int skip_checkpoint_write; int no_cache; + int tags_ecc_on; + int tags_ecc_overridden; + int lazy_loading_enabled; + int lazy_loading_overridden; + int empty_lost_and_found; + int empty_lost_and_found_overridden; } yaffs_options; -#define MAX_OPT_LEN 20 +#define MAX_OPT_LEN 30 static int yaffs_parse_options(yaffs_options *options, const char *options_str) { char cur_opt[MAX_OPT_LEN + 1]; @@ -1801,6 +2006,9 @@ static int yaffs_parse_options(yaffs_options *options, const char *options_str) memset(cur_opt, 0, MAX_OPT_LEN + 1); p = 0; + while(*options_str == ',') + options_str++; + while (*options_str && *options_str != ',') { if (p < MAX_OPT_LEN) { cur_opt[p] = *options_str; @@ -1811,7 +2019,25 @@ static int yaffs_parse_options(yaffs_options *options, const char *options_str) if (!strcmp(cur_opt, "inband-tags")) options->inband_tags = 1; - else if (!strcmp(cur_opt, "no-cache")) + else if (!strcmp(cur_opt, "tags-ecc-off")){ + options->tags_ecc_on = 0; + options->tags_ecc_overridden=1; + } else if (!strcmp(cur_opt, "tags-ecc-on")){ + options->tags_ecc_on = 1; + options->tags_ecc_overridden = 1; + } else if (!strcmp(cur_opt, "lazy-loading-off")){ + options->lazy_loading_enabled = 0; + options->lazy_loading_overridden=1; + } else if (!strcmp(cur_opt, "lazy-loading-on")){ + options->lazy_loading_enabled = 1; + options->lazy_loading_overridden = 1; + } else if (!strcmp(cur_opt, "empty-lost-and-found-off")){ + options->empty_lost_and_found = 0; + options->empty_lost_and_found_overridden=1; + } else if (!strcmp(cur_opt, "empty-lost-and-found-on")){ + options->empty_lost_and_found = 1; + options->empty_lost_and_found_overridden=1; + } else if (!strcmp(cur_opt, "no-cache")) options->no_cache = 1; else if (!strcmp(cur_opt, "no-checkpoint-read")) options->skip_checkpoint_read = 1; @@ -2027,6 +2253,24 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, dev->nShortOpCaches = (options.no_cache) ? 0 : 10; dev->inbandTags = options.inband_tags; +#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD + dev->disableLazyLoad = 1; +#endif + if(options.lazy_loading_overridden) + dev->disableLazyLoad = !options.lazy_loading_enabled; + +#ifdef CONFIG_YAFFS_DISABLE_TAGS_ECC + dev->noTagsECC = 1; +#endif + if(options.tags_ecc_overridden) + dev->noTagsECC = !options.tags_ecc_on; + +#ifdef CONFIG_YAFFS_EMPTY_LOST_AND_FOUND + dev->emptyLostAndFound = 1; +#endif + if(options.empty_lost_and_found_overridden) + dev->emptyLostAndFound = options.empty_lost_and_found; + /* ... and the functions. */ if (yaffsVersion == 2) { dev->writeChunkWithTagsToNAND = @@ -2087,6 +2331,10 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, /* we assume this is protected by lock_kernel() in mount/umount */ ylist_add_tail(&dev->devList, &yaffs_dev_list); + /* Directory search handling...*/ + YINIT_LIST_HEAD(&dev->searchContexts); + dev->removeObjectCallback = yaffs_RemoveObjectCallback; + init_MUTEX(&dev->grossLock); yaffs_GrossLock(dev); @@ -2227,7 +2475,7 @@ static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, static struct proc_dir_entry *my_proc_entry; -static char *yaffs_dump_dev(char *buf, yaffs_Device * dev) +static char *yaffs_dump_dev_part0(char *buf, yaffs_Device * dev) { buf += sprintf(buf, "startBlock......... %d\n", dev->startBlock); buf += sprintf(buf, "endBlock........... %d\n", dev->endBlock); @@ -2262,9 +2510,19 @@ static char *yaffs_dump_dev(char *buf, yaffs_Device * dev) buf += sprintf(buf, "nUnlinkedFiles..... %d\n", dev->nUnlinkedFiles); buf += sprintf(buf, "nBackgroudDeletions %d\n", dev->nBackgroundDeletions); + + return buf; +} + + +static char *yaffs_dump_dev_part1(char *buf, yaffs_Device * dev) +{ buf += sprintf(buf, "useNANDECC......... %d\n", dev->useNANDECC); + buf += sprintf(buf, "noTagsECC.......... %d\n", dev->noTagsECC); buf += sprintf(buf, "isYaffs2........... %d\n", dev->isYaffs2); buf += sprintf(buf, "inbandTags......... %d\n", dev->inbandTags); + buf += sprintf(buf, "emptyLostAndFound.. %d\n", dev->emptyLostAndFound); + buf += sprintf(buf, "disableLazyLoad.... %d\n", dev->disableLazyLoad); return buf; } @@ -2287,27 +2545,35 @@ static int yaffs_proc_read(char *page, *(int *)start = 1; /* Print header first */ - if (step == 0) { + if (step == 0) buf += sprintf(buf, "YAFFS built:" __DATE__ " " __TIME__ "\n%s\n%s\n", yaffs_fs_c_version, yaffs_guts_c_version); - } - - /* hold lock_kernel while traversing yaffs_dev_list */ - lock_kernel(); - - /* Locate and print the Nth entry. Order N-squared but N is small. */ - ylist_for_each(item, &yaffs_dev_list) { - yaffs_Device *dev = ylist_entry(item, yaffs_Device, devList); - if (n < step) { - n++; - continue; + else if (step == 1) + buf += sprintf(buf,"\n"); + else { + step-=2; + + /* hold lock_kernel while traversing yaffs_dev_list */ + lock_kernel(); + + /* Locate and print the Nth entry. Order N-squared but N is small. */ + ylist_for_each(item, &yaffs_dev_list) { + yaffs_Device *dev = ylist_entry(item, yaffs_Device, devList); + if (n < (step & ~1)) { + n+=2; + continue; + } + if((step & 1)==0){ + buf += sprintf(buf, "\nDevice %d \"%s\"\n", n, dev->name); + buf = yaffs_dump_dev_part0(buf, dev); + } else + buf = yaffs_dump_dev_part1(buf, dev); + + break; } - buf += sprintf(buf, "\nDevice %d \"%s\"\n", n, dev->name); - buf = yaffs_dump_dev(buf, dev); - break; + unlock_kernel(); } - unlock_kernel(); return buf - page < count ? buf - page : count; } @@ -2339,6 +2605,7 @@ static struct { {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, {"scan", YAFFS_TRACE_SCAN}, {"tracing", YAFFS_TRACE_TRACING}, + {"sync", YAFFS_TRACE_SYNC}, {"verify", YAFFS_TRACE_VERIFY}, {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, @@ -2352,7 +2619,7 @@ static struct { }; #define MAX_MASK_NAME_LENGTH 40 -static int yaffs_proc_write(struct file *file, const char *buf, +static int yaffs_proc_write_trace_options(struct file *file, const char *buf, unsigned long count, void *data) { unsigned rg = 0, mask_bitfield; @@ -2444,6 +2711,13 @@ static int yaffs_proc_write(struct file *file, const char *buf, return count; } + +static int yaffs_proc_write(struct file *file, const char *buf, + unsigned long count, void *data) +{ + return yaffs_proc_write_trace_options(file, buf, count, data); +} + /* Stuff to handle installation of file systems */ struct file_system_to_install { struct file_system_type *fst;