*/
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 <linux/config.h>
#include <linux/mtd/mtd.h>
#include <linux/interrupt.h>
#include <linux/string.h>
+#include <linux/ctype.h>
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
#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 <linux/mtd/mtd.h>
#include "yaffs_mtdif.h"
#include "yaffs_mtdif2.h"
-#endif /*CONFIG_YAFFS_YAFFS1 */
/*#define T(x) printk x */
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,
.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 = {
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;
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,
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,
/*dget(dentry); // try to solve directory bug */
d_add(dentry, inode);
- yaffs_GrossUnlock(dev);
-
/* return dentry; */
return NULL;
#endif
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 */
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);
}
}
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;
}
/* 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;
}
}
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)
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) {
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);
error = -ENOMEM;
}
- yaffs_GrossUnlock(dev);
-
return error;
}
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;
}
/*
{
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);
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,
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);
}
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;
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);
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)
{
put_mtd_device(mtd);
}
-#endif
static struct super_block *yaffs_internal_read_super(int yaffsVersion,
struct super_block *sb,
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 */
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;
}
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;
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);
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;
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,
FS_REQUIRES_DEV);
#endif
-#endif /* CONFIG_YAFFS_YAFFS1 */
#ifdef CONFIG_YAFFS_YAFFS2
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,
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;
};
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}
};
("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;
}
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");