X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_fs.c;h=09591117f4f50a91ed323402d8c90ac23d6838bd;hp=574eda6f989522ab413314b6121e36cbe6f2f91b;hb=e1b8e63260986ab7afec3c379e7a320677c95846;hpb=9f361d87ca8c3d331ed1a4ffea53dd9a8d55ee88 diff --git a/yaffs_fs.c b/yaffs_fs.c index 574eda6..0959111 100644 --- a/yaffs_fs.c +++ b/yaffs_fs.c @@ -31,7 +31,7 @@ */ const char *yaffs_fs_c_version = - "$Id: yaffs_fs.c,v 1.29 2005-08-11 01:07:43 marty Exp $"; + "$Id: yaffs_fs.c,v 1.46 2006-05-08 10:13:34 charles Exp $"; extern const char *yaffs_guts_c_version; #include @@ -48,6 +48,7 @@ extern const char *yaffs_guts_c_version; #include #include #include +#include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) @@ -72,14 +73,14 @@ extern const char *yaffs_guts_c_version; #include "yportenv.h" #include "yaffs_guts.h" -unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | YAFFS_TRACE_BAD_BLOCKS; -/*unsigned yaffs_traceMask = 0xFFFFFFFF; */ +unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | + YAFFS_TRACE_BAD_BLOCKS | + YAFFS_TRACE_CHECKPOINT + /* | 0xFFFFFFFF */; -#ifdef CONFIG_YAFFS_YAFFS1 #include #include "yaffs_mtdif.h" #include "yaffs_mtdif2.h" -#endif /*CONFIG_YAFFS_YAFFS1 */ /*#define T(x) printk x */ @@ -155,7 +156,11 @@ static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, static int yaffs_readlink(struct dentry *dentry, char __user * buffer, int buflen); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) +static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd); +#else static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd); +#endif static struct address_space_operations yaffs_file_address_operations = { .readpage = yaffs_readpage, @@ -170,6 +175,10 @@ static struct file_operations yaffs_file_operations = { .mmap = generic_file_mmap, .flush = yaffs_file_flush, .fsync = yaffs_sync_object, +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + .sendfile = generic_file_sendfile, +#endif + }; static struct inode_operations yaffs_file_inode_operations = { @@ -246,7 +255,11 @@ static int yaffs_readlink(struct dentry *dentry, char __user * buffer, return ret; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) +static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) +#else static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) +#endif { unsigned char *alias; int ret; @@ -259,11 +272,19 @@ static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) yaffs_GrossUnlock(dev); if (!alias) - return -ENOMEM; + { + ret = -ENOMEM; + goto out; + } ret = vfs_follow_link(nd, alias); kfree(alias); +out: +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) + return ERR_PTR (ret); +#else return ret; +#endif } struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, @@ -296,6 +317,9 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) dentry->d_name.name); obj = yaffs_GetEquivalentObject(obj); /* in case it was a hardlink */ + + /* Can't hold gross lock when calling yaffs_get_inode() */ + yaffs_GrossUnlock(dev); if (obj) { T(YAFFS_TRACE_OS, @@ -312,8 +336,6 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) /*dget(dentry); // try to solve directory bug */ d_add(dentry, inode); - yaffs_GrossUnlock(dev); - /* return dentry; */ return NULL; #endif @@ -323,7 +345,6 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_lookup not found\n")); } - yaffs_GrossUnlock(dev); /* added NCB for 2.5/6 compatability - forces add even if inode is * NULL which creates dentry hash */ @@ -401,6 +422,9 @@ static void yaffs_delete_inode(struct inode *inode) yaffs_DeleteFile(obj); yaffs_GrossUnlock(dev); } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) + truncate_inode_pages (&inode->i_data, 0); +#endif clear_inode(inode); } @@ -607,9 +631,10 @@ static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, } T(YAFFS_TRACE_OS, - (KERN_DEBUG "yaffs_commit_write returning %d\n", nWritten)); + (KERN_DEBUG "yaffs_commit_write returning %d\n", + nWritten == nBytes ? 0 : nWritten)); - return nWritten; + return nWritten == nBytes ? 0 : nWritten; } @@ -708,6 +733,7 @@ struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, /* NB Side effect: iget calls back to yaffs_read_inode(). */ /* iget also increments the inode's i_count */ + /* NB You can't be holding grossLock or deadlock will happen! */ return inode; } @@ -766,8 +792,7 @@ static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, } yaffs_GrossUnlock(dev); - - return nWritten != n ? -ENOSPC : nWritten; + return nWritten == 0 ? -ENOSPC : nWritten; } static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) @@ -816,6 +841,15 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) curoffs = 1; + /* If the directory has changed since the open or last call to + readdir, rewind to after the 2 canned entries. */ + + if (f->f_version != inode->i_version) { + offset = 2; + f->f_pos = offset; + f->f_version = inode->i_version; + } + list_for_each(i, &obj->variant.directoryVariant.children) { curoffs++; if (curoffs >= offset) { @@ -923,6 +957,9 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, obj = NULL; /* Do we ever get here? */ break; } + + /* Can not call yaffs_get_inode() with gross lock held */ + yaffs_GrossUnlock(dev); if (obj) { inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); @@ -937,8 +974,6 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, error = -ENOMEM; } - yaffs_GrossUnlock(dev); - return error; } @@ -983,15 +1018,15 @@ static int yaffs_unlink(struct inode *dir, struct dentry *dentry) retVal = yaffs_Unlink(yaffs_InodeToObject(dir), dentry->d_name.name); - yaffs_GrossUnlock(dev); - if (retVal == YAFFS_OK) { dentry->d_inode->i_nlink--; + dir->i_version++; + yaffs_GrossUnlock(dev); mark_inode_dirty(dentry->d_inode); return 0; - } else { - return -ENOTEMPTY; } + yaffs_GrossUnlock(dev); + return -ENOTEMPTY; } /* @@ -1100,9 +1135,9 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, { yaffs_Device *dev; int retVal = YAFFS_FAIL; - int removed = 0; yaffs_Object *target; + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_rename\n")); dev = yaffs_InodeToObject(old_dir)->myDev; yaffs_GrossLock(dev); @@ -1111,15 +1146,21 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, target = yaffs_FindObjectByName(yaffs_InodeToObject(new_dir), new_dentry->d_name.name); + + if (target && target->variantType == YAFFS_OBJECT_TYPE_DIRECTORY && !list_empty(&target->variant.directoryVariant.children)) { + + T(YAFFS_TRACE_OS, (KERN_DEBUG "target is non-empty dir\n")); + retVal = YAFFS_FAIL; } else { /* Now does unlinking internally using shadowing mechanism */ - + T(YAFFS_TRACE_OS, (KERN_DEBUG "calling yaffs_RenameObject\n")); + retVal = yaffs_RenameObject(yaffs_InodeToObject(old_dir), old_dentry->d_name.name, @@ -1130,7 +1171,7 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, yaffs_GrossUnlock(dev); if (retVal == YAFFS_OK) { - if (removed == YAFFS_OK) { + if(target) { new_dentry->d_inode->i_nlink--; mark_inode_dirty(new_dentry->d_inode); } @@ -1213,8 +1254,9 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf) static void yaffs_read_inode(struct inode *inode) { - /* NB This is called as a side effect of other functions and - * thus gross locking should always be in place already. + /* NB This is called as a side effect of other functions, but + * we had to release the lock to prevent deadlocks, so + * need to lock again. */ yaffs_Object *obj; @@ -1223,10 +1265,13 @@ static void yaffs_read_inode(struct inode *inode) T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_read_inode for %d\n", (int)inode->i_ino)); + yaffs_GrossLock(dev); + obj = yaffs_FindObjectByNumber(dev, inode->i_ino); yaffs_FillInodeFromObject(inode, obj); + yaffs_GrossUnlock(dev); } static LIST_HEAD(yaffs_dev_list); @@ -1236,19 +1281,29 @@ static void yaffs_put_super(struct super_block *sb) yaffs_Device *dev = yaffs_SuperToDevice(sb); yaffs_GrossLock(dev); + + yaffs_FlushEntireDeviceCache(dev); + if (dev->putSuperFunc) { dev->putSuperFunc(sb); } + + yaffs_CheckpointSave(dev); yaffs_Deinitialise(dev); + yaffs_GrossUnlock(dev); /* we assume this is protected by lock_kernel() in mount/umount */ list_del(&dev->devList); + + if(dev->spareBuffer){ + YFREE(dev->spareBuffer); + dev->spareBuffer = NULL; + } kfree(dev); } -#ifdef CONFIG_YAFFS_YAFFS1 static void yaffs_MTDPutSuper(struct super_block *sb) { @@ -1262,7 +1317,6 @@ static void yaffs_MTDPutSuper(struct super_block *sb) put_mtd_device(mtd); } -#endif static struct super_block *yaffs_internal_read_super(int yaffsVersion, struct super_block *sb, @@ -1337,6 +1391,15 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, T(YAFFS_TRACE_OS, (" oobsize %d\n", mtd->oobsize)); T(YAFFS_TRACE_OS, (" erasesize %d\n", mtd->erasesize)); T(YAFFS_TRACE_OS, (" size %d\n", mtd->size)); + +#ifdef CONFIG_YAFFS_AUTO_YAFFS2 + + if (yaffsVersion == 1 && + mtd->oobblock >= 2048) { + T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs2\n")); + yaffsVersion = 2; + } +#endif if (yaffsVersion == 2) { /* Check for version 2 style functions */ @@ -1356,7 +1419,7 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, if (mtd->oobblock < YAFFS_MIN_YAFFS2_CHUNK_SIZE || mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) { T(YAFFS_TRACE_ALWAYS, - ("yaffs: MTD device does not support have the " + ("yaffs: MTD device does not have the " "right page sizes\n")); return NULL; } @@ -1427,7 +1490,9 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, dev->nBytesPerChunk = mtd->oobblock; dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock; nBlocks = mtd->size / mtd->erasesize; - dev->startBlock = 0; + dev->checkpointStartBlock = 0; + dev->checkpointEndBlock = 20; + dev->startBlock = dev->checkpointEndBlock + 1; dev->endBlock = nBlocks - 1; } else { dev->writeChunkToNAND = nandmtd_WriteChunkToNAND; @@ -1444,6 +1509,10 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, dev->useNANDECC = 1; #endif +#ifdef CONFIG_YAFFS_DISABLE_WIDE_TNODES + dev->wideTnodesDisabled = 1; +#endif + /* we assume this is protected by lock_kernel() in mount/umount */ list_add_tail(&dev->devList, &yaffs_dev_list); @@ -1456,14 +1525,15 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, T(YAFFS_TRACE_OS, ("yaffs_read_super: guts initialised %s\n", (err == YAFFS_OK) ? "OK" : "FAILED")); + + /* Release lock before yaffs_get_inode() */ + yaffs_GrossUnlock(dev); /* Create root inode */ if (err == YAFFS_OK) inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_Root(dev)); - yaffs_GrossUnlock(dev); - if (!inode) return NULL; @@ -1486,13 +1556,12 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, return sb; } -#ifdef CONFIG_YAFFS_YAFFS1 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, int silent) { - return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -1; + return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; } static struct super_block *yaffs_read_super(struct file_system_type *fs, @@ -1522,7 +1591,6 @@ static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV); #endif -#endif /* CONFIG_YAFFS_YAFFS1 */ #ifdef CONFIG_YAFFS_YAFFS2 @@ -1530,7 +1598,7 @@ static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, int silent) { - return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -1; + return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; } static struct super_block *yaffs2_read_super(struct file_system_type *fs, @@ -1645,6 +1713,118 @@ static int yaffs_proc_read(char *page, return buf - page < count ? buf - page : count; } +/** + * Set the verbosity of the warnings and error messages. + * + */ + +static struct { + char *mask_name; + unsigned mask_bitfield; +} mask_flags[] = { + {"allocate", YAFFS_TRACE_ALLOCATE}, + {"always", YAFFS_TRACE_ALWAYS}, + {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, + {"buffers", YAFFS_TRACE_BUFFERS}, + {"bug", YAFFS_TRACE_BUG}, + {"deletion", YAFFS_TRACE_DELETION}, + {"erase", YAFFS_TRACE_ERASE}, + {"error", YAFFS_TRACE_ERROR}, + {"gc_detail", YAFFS_TRACE_GC_DETAIL}, + {"gc", YAFFS_TRACE_GC}, + {"mtd", YAFFS_TRACE_MTD}, + {"nandaccess", YAFFS_TRACE_NANDACCESS}, + {"os", YAFFS_TRACE_OS}, + {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, + {"scan", YAFFS_TRACE_SCAN}, + {"tracing", YAFFS_TRACE_TRACING}, + {"write", YAFFS_TRACE_WRITE}, + {"all", 0xffffffff}, + {"none", 0}, + {NULL, 0}, +}; + +static int yaffs_proc_write(struct file *file, const char *buf, + unsigned long count, void *data) +{ + unsigned rg = 0, mask_bitfield; + char *end, *mask_name; + int i; + int done = 0; + int add, len; + int pos = 0; + + rg = yaffs_traceMask; + + while (!done && (pos < count)) { + done = 1; + while ((pos < count) && isspace(buf[pos])) { + pos++; + } + + switch (buf[pos]) { + case '+': + case '-': + case '=': + add = buf[pos]; + pos++; + break; + + default: + add = ' '; + break; + } + mask_name = NULL; + mask_bitfield = simple_strtoul(buf + pos, &end, 0); + if (end > buf + pos) { + mask_name = "numeral"; + len = end - (buf + pos); + done = 0; + } else { + + for (i = 0; mask_flags[i].mask_name != NULL; i++) { + len = strlen(mask_flags[i].mask_name); + if (strncmp(buf + pos, mask_flags[i].mask_name, len) == 0) { + mask_name = mask_flags[i].mask_name; + mask_bitfield = mask_flags[i].mask_bitfield; + done = 0; + break; + } + } + } + + if (mask_name != NULL) { + pos += len; + done = 0; + switch(add) { + case '-': + rg &= ~mask_bitfield; + break; + case '+': + rg |= mask_bitfield; + break; + case '=': + rg = mask_bitfield; + break; + default: + rg |= mask_bitfield; + break; + } + } + } + + yaffs_traceMask = rg; + if (rg & YAFFS_TRACE_ALWAYS) { + for (i = 0; mask_flags[i].mask_name != NULL; i++) { + char flag; + flag = ((rg & mask_flags[i].mask_bitfield) == mask_flags[i].mask_bitfield) ? '+' : '-'; + printk("%c%s\n", flag, mask_flags[i].mask_name); + } + } + + return count; +} + /* Stuff to handle installation of file systems */ struct file_system_to_install { struct file_system_type *fst; @@ -1652,12 +1832,12 @@ struct file_system_to_install { }; static struct file_system_to_install fs_to_install[] = { -#ifdef CONFIG_YAFFS_YAFFS1 +//#ifdef CONFIG_YAFFS_YAFFS1 {&yaffs_fs_type, 0}, -#endif -#ifdef CONFIG_YAFFS_YAFFS2 +//#endif +//#ifdef CONFIG_YAFFS_YAFFS2 {&yaffs2_fs_type, 0}, -#endif +//#endif {NULL, 0} }; @@ -1670,11 +1850,15 @@ static int __init init_yaffs_fs(void) ("yaffs " __DATE__ " " __TIME__ " Installing. \n")); /* Install the proc_fs entry */ - my_proc_entry = create_proc_read_entry("yaffs", + my_proc_entry = create_proc_entry("yaffs", S_IRUGO | S_IFREG, - &proc_root, - yaffs_proc_read, NULL); - if (!my_proc_entry) { + &proc_root); + + if (my_proc_entry) { + my_proc_entry->write_proc = yaffs_proc_write; + my_proc_entry->read_proc = yaffs_proc_read; + my_proc_entry->data = NULL; + } else { return -ENOMEM; } @@ -1732,5 +1916,5 @@ module_init(init_yaffs_fs) module_exit(exit_yaffs_fs) MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); -MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002,2003,2004"); +MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2006"); MODULE_LICENSE("GPL");