*** empty log message ***
[yaffs/.git] / yaffs_fs.c
index da3b7f923daa288db1bca8efa4eaf0d635abb940..637f7fd4b2940c3589a3b9f46ca5db931a39746b 100644 (file)
  * Acknowledgements:
  * * Luc van OostenRyck for numerous patches.
  * * Nick Bane for numerous patches.
+ * * Nick Bane for 2.5/2.6 integration.
  * * Andras Toth for mknod rdev issue.
  * * Some code bodily lifted from JFFS2.
  */
 
 
-const char *yaffs_fs_c_version = "$Id: yaffs_fs.c,v 1.20 2002-11-26 01:15:37 charles Exp $";
+const char *yaffs_fs_c_version = "$Id: yaffs_fs.c,v 1.28 2003-08-20 03:53:39 charles Exp $";
 extern const char *yaffs_guts_c_version;
 
 
@@ -44,7 +45,18 @@ extern const char *yaffs_guts_c_version;
 #include <linux/mtd/mtd.h>
 #include <linux/interrupt.h>
 #include <linux/string.h>
+
+
+#if defined(CONFIG_KERNEL_2_5)
+#include <linux/statfs.h>      /* Added NCB 15-8-2003 */
+#include <asm/statfs.h>
+#define UnlockPage(p) unlock_page(p)
+#define Page_Uptodate(page)    test_bit(PG_uptodate, &(page)->flags)
+#define kdevname(x) cdevname(to_kdev_t(x))
+#else
 #include <linux/locks.h>
+#endif
+
 
 #include <asm/uaccess.h>
 
@@ -73,8 +85,13 @@ unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | YAFFS_TRACE_BAD_BLOCKS;
 
 #define yaffs_InodeToObject(iptr) ((yaffs_Object *)((iptr)->u.generic_ip))
 #define yaffs_DentryToObject(dptr) yaffs_InodeToObject((dptr)->d_inode)
-#define yaffs_SuperToDevice(sb)        ((yaffs_Device *)sb->u.generic_sbp)
+//NCB #define yaffs_SuperToDevice(sb)  ((yaffs_Device *)sb->u.generic_sbp)
 
+#if defined(CONFIG_KERNEL_2_5)
+#define yaffs_SuperToDevice(sb)        ((yaffs_Device *)sb->s_fs_info)
+#else
+#define yaffs_SuperToDevice(sb)        ((yaffs_Device *)sb->u.generic_sbp)
+#endif
 
 
 static void yaffs_put_super(struct super_block *sb);
@@ -87,19 +104,39 @@ static int yaffs_sync_object(struct file * file, struct dentry *dentry, int data
 
 static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir);
 
+#if defined(CONFIG_KERNEL_2_5) /* Added NCB 185-8-2003 */
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *n);
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *n);
+#else
 static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode);
 static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry);
+#endif
 static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry);
 static int yaffs_unlink(struct inode * dir, struct dentry *dentry);
 static int yaffs_symlink(struct inode * dir, struct dentry *dentry, const char * symname);
 static int yaffs_mkdir(struct inode * dir, struct dentry * dentry, int mode);
+
+#if defined(CONFIG_KERNEL_2_5)
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
+#else
 static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev);
+#endif
 static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry);
 static int yaffs_setattr(struct dentry *dentry, struct iattr *attr);
 
+#if defined(CONFIG_KERNEL_2_5) /* Added NCB 185-8-2003 */
+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf);
+#else
 static int yaffs_statfs(struct super_block *sb, struct statfs *buf);
+#endif
 static void yaffs_read_inode (struct inode *inode);
+
+#if defined(CONFIG_KERNEL_2_5)
+static struct super_block *yaffs_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data);
+#else
 static struct super_block *yaffs_read_super(struct super_block * sb, void * data, int silent);
+#endif
+
 static void yaffs_put_inode (struct inode *inode);
 static void yaffs_delete_inode(struct inode *);
 static void yaffs_clear_inode(struct inode *);
@@ -243,10 +280,14 @@ struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Ob
 /*
  * Lookup is used to find objects in the fs
  */
+#if defined(CONFIG_KERNEL_2_5) /* Added NCB 185-8-2003 */
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *n)
+#else
 static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry)
+#endif
 {
        yaffs_Object *obj;
-       struct inode *inode;
+       struct inode *inode = NULL; // NCB 2.5/2.6 needs NULL here
        
        yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev;
 
@@ -271,6 +312,8 @@ static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry)
                if(inode)
                {
                        T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_loookup dentry \n"));
+/* #if 0 asserted by NCB for 2.5/6 compatability - falls through to d_add even if NULL inode */        
+#if 0
                        //dget(dentry); // try to solve directory bug
                        d_add(dentry,inode);
                        
@@ -278,6 +321,7 @@ static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry)
 
                        // return dentry;
                        return NULL;
+#endif
                }
 
        }
@@ -287,6 +331,9 @@ static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry)
                
        }
        yaffs_GrossUnlock(dev);
+
+/* added NCB for 2.5/6 compatability - forces add even if inode is NULL which creates dentry hash*/    
+       d_add(dentry,inode);
        
        return NULL;
        //      return (ERR_PTR(-EIO));
@@ -352,7 +399,7 @@ static int yaffs_file_flush(struct file* file)
 
        yaffs_GrossLock(dev);
        
-    yaffs_FlushFile(obj);
+    yaffs_FlushFile(obj,1);
 
        yaffs_GrossUnlock(dev);
 
@@ -476,12 +523,23 @@ static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object *obj)
                inode->i_uid = obj->st_uid;
                inode->i_gid = obj->st_gid;
                inode->i_blksize = inode->i_sb->s_blocksize;
-               inode->i_blocks = 0;
-               inode->i_rdev = obj->st_rdev;;
+#if defined(CONFIG_KERNEL_2_5)
+               inode->i_rdev = to_kdev_t(obj->st_rdev);
+               inode->i_atime.tv_sec = (time_t)(obj->st_atime);
+               inode->i_atime.tv_nsec = 0;
+               inode->i_mtime.tv_sec = (time_t)obj->st_mtime;
+               inode->i_mtime.tv_nsec =0;
+               inode->i_ctime.tv_sec = (time_t)obj->st_ctime;
+               inode->i_ctime.tv_nsec = 0;
+#else
+               inode->i_rdev = obj->st_rdev;
                inode->i_atime = obj->st_atime;
                inode->i_mtime = obj->st_mtime;
                inode->i_ctime = obj->st_ctime;
+#endif
                inode->i_size = yaffs_GetObjectFileLength(obj);
+               inode->i_blocks = (inode->i_size + inode->i_blksize - 1) >> inode->i_sb->s_blocksize_bits;
+
                inode->i_nlink = yaffs_GetObjectLinkCount(obj);
                
                T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_FillInode mode %x uid %d gid %d size %d count %d\n",
@@ -490,7 +548,7 @@ static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object *obj)
                switch (obj->st_mode & S_IFMT) 
                {
                        default: // fifo, device or socket
-                               init_special_inode(inode, obj->st_mode, obj->st_rdev);
+                               init_special_inode(inode, obj->st_mode,(dev_t)(obj->st_rdev));
                                break;
                        case S_IFREG:   // file         
                                inode->i_op = &yaffs_file_inode_operations;
@@ -715,7 +773,11 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
 /*
  * File creation. Allocate an inode, and we're done..
  */
+#if defined(CONFIG_KERNEL_2_5)
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
+#else
 static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+#endif
 {
        struct inode *inode;
        
@@ -798,7 +860,11 @@ static int yaffs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
        return retVal;
 }
 
+#if defined(CONFIG_KERNEL_2_5) /* Added NCB 185-8-2003 */
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *n)
+#else
 static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode)
+#endif
 {
        T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_create\n"));
        return yaffs_mknod(dir, dentry, mode | S_IFREG, 0);
@@ -853,8 +919,11 @@ static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dent
        dev = obj->myDev;
        
        yaffs_GrossLock(dev);
-       
-       link = yaffs_Link(yaffs_InodeToObject(dir),dentry->d_name.name,obj);
+
+       if (!S_ISDIR(inode->i_mode))    // Don't link directories
+       {
+               link = yaffs_Link(yaffs_InodeToObject(dir),dentry->d_name.name,obj);
+       }
        
 
        if(link)
@@ -926,7 +995,7 @@ static int yaffs_sync_object(struct file * file, struct dentry *dentry, int data
        
        T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_sync_object\n"));
        yaffs_GrossLock(dev);
-       yaffs_FlushFile(obj);
+       yaffs_FlushFile(obj,1);
        yaffs_GrossUnlock(dev);
        return 0;
 }
@@ -939,22 +1008,44 @@ static int yaffs_sync_object(struct file * file, struct dentry *dentry, int data
 static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry)
 {
        yaffs_Device *dev;
-       int retVal;
+       int retVal = YAFFS_FAIL;
+       int removed = 0;
+       yaffs_Object *target;
        
        dev = yaffs_InodeToObject(old_dir)->myDev;
 
        yaffs_GrossLock(dev);
        
-       // Unlink the target if it exists
-       yaffs_Unlink(yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
+       // Check if the target is an existing directory that is not empty.
+       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))
+       {
+               retVal = YAFFS_FAIL;
+       }
+       else
+       {
+          
+               // Unlink the target if it exists
+               removed = yaffs_Unlink(yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
 
        
-       retVal =  yaffs_RenameObject(yaffs_InodeToObject(old_dir),old_dentry->d_name.name,
-                                        yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
+               retVal =  yaffs_RenameObject(yaffs_InodeToObject(old_dir),old_dentry->d_name.name,
+                                                                       yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
+                                                                       
+       }
        yaffs_GrossUnlock(dev);
        
        if(retVal == YAFFS_OK)
        {
+               if(removed == YAFFS_OK)
+               {
+                       new_dentry->d_inode->i_nlink--;
+                       mark_inode_dirty(new_dentry->d_inode);
+               }
+               
                return 0;
        }
        else
@@ -992,7 +1083,11 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)
        return error;
 }
 
+#if defined(CONFIG_KERNEL_2_5) /* Added NCB 185-8-2003 */
+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf)
+#else
 static int yaffs_statfs(struct super_block *sb, struct statfs *buf)
+#endif
 {
        yaffs_Device *dev = yaffs_SuperToDevice(sb);
        T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_statfs\n"));
@@ -1105,7 +1200,7 @@ static struct super_block *yaffs_internal_read_super(int useRam, struct super_bl
        T(YAFFS_TRACE_OS,("yaffs_read_super: %s block size %d\n", useRam ? "RAM" : "MTD",(int)(sb->s_blocksize)));
 
 #ifdef CONFIG_YAFFS_DISABLE_WRITE_VERIFY
-       T(YAFFS_TRACE_OS,("yaffs: Write verification disabled. All guarantees null and void\n");
+       T(YAFFS_TRACE_OS,("yaffs: Write verification disabled. All guarantees null and void\n"));
 #endif
 
 
@@ -1130,6 +1225,9 @@ static struct super_block *yaffs_internal_read_super(int useRam, struct super_bl
                nBlocks = YAFFS_RAM_EMULATION_SIZE / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
                dev->startBlock = 1;  // Don't use block 0
                dev->endBlock = nBlocks - 1;
+               dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
+               dev->nBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
+               dev->nReservedBlocks = 5;
 
                dev->writeChunkToNAND = nandemul_WriteChunkToNAND;
                dev->readChunkFromNAND = nandemul_ReadChunkFromNAND;
@@ -1175,8 +1273,12 @@ static struct super_block *yaffs_internal_read_super(int useRam, struct super_bl
                if(!mtd->erase ||
                   !mtd->read  ||
                   !mtd->write ||
+#ifndef CONFIG_YAFFS_USE_OLD_MTD
+                  !mtd->write_ecc ||
+                  !mtd->read_ecc ||
+#endif
                   !mtd->read_oob ||
-                  !mtd->write_oob)
+                  !mtd->write_oob )
                {
                        printk(KERN_DEBUG "yaffs: MTD device does not support required functions\n");
                        return NULL;
@@ -1194,7 +1296,11 @@ static struct super_block *yaffs_internal_read_super(int useRam, struct super_bl
                // like it has the right capabilities
                // Set the yaffs_Device up for ram emulation
 
+#if defined(CONFIG_KERNEL_2_5)
+               sb->s_fs_info = dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
+#else
                sb->u.generic_sbp =     dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
+#endif
                if(!dev)
                {
                        // Deep shit could not allocate device structure
@@ -1210,6 +1316,11 @@ static struct super_block *yaffs_internal_read_super(int useRam, struct super_bl
                nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
                dev->startBlock = 1;  // Don't use block 0
                dev->endBlock = nBlocks - 1;
+               dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
+               dev->nBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
+               dev->nReservedBlocks = 5;
+               dev->nShortOpCaches = 10; // Enable short op caching
+               
 
                // ... and the functions.
                dev->writeChunkToNAND = nandmtd_WriteChunkToNAND;
@@ -1218,6 +1329,10 @@ static struct super_block *yaffs_internal_read_super(int useRam, struct super_bl
                dev->initialiseNAND = nandmtd_InitialiseNAND;
                                
                dev->putSuperFunc = yaffs_MTDPutSuper;
+               
+#ifdef CONFIG_YAFFS_USE_NANDECC
+               dev->useNANDECC = 1;
+#endif
 
                yaffs_dev = dev;
                
@@ -1239,6 +1354,10 @@ static struct super_block *yaffs_internal_read_super(int useRam, struct super_bl
 
        if (!inode)
                return NULL;
+               
+// added NCB
+       inode->i_op = & yaffs_dir_inode_operations;
+       inode->i_fop = & yaffs_dir_operations;
 
        T(YAFFS_TRACE_OS,("yaffs_read_super: got root inode\n"));
                
@@ -1257,7 +1376,35 @@ static struct super_block *yaffs_internal_read_super(int useRam, struct super_bl
        return sb;
 }
 
+static int yaffs_internal_read_super_ram(struct super_block * sb, void * data, int silent)
+{
+        return yaffs_internal_read_super(1,sb,data,silent) ? 0 : -1;
+}
+static int yaffs_internal_read_super_mtd(struct super_block * sb, void * data, int silent)
+{
+        return yaffs_internal_read_super(0,sb,data,silent) ? 0 : -1;
+}
+
+
 #ifdef CONFIG_YAFFS_MTD_ENABLED
+#if defined(CONFIG_KERNEL_2_5)
+static struct super_block *yaffs_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data)
+{
+
+    return get_sb_bdev(fs, flags, dev_name, data, yaffs_internal_read_super_mtd);
+}
+
+/* changes NCB 2.5.70 */
+//static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV);
+static struct file_system_type yaffs_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "yaffs",
+       .get_sb         = yaffs_read_super,
+//     .kill_sb        = kill_block_super,
+       .kill_sb        = kill_litter_super,
+       .fs_flags       = FS_REQUIRES_DEV,
+};
+#else
 static struct super_block *yaffs_read_super(struct super_block * sb, void * data, int silent)
 {
        return yaffs_internal_read_super(0,sb,data,silent);
@@ -1265,15 +1412,35 @@ static struct super_block *yaffs_read_super(struct super_block * sb, void * data
 
 static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV);
 #endif
+#endif
 
 #ifdef CONFIG_YAFFS_RAM_ENABLED
 
+#if defined(CONFIG_KERNEL_2_5)
+static struct super_block *yaffs_ram_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data)
+{
+
+    return get_sb_bdev(fs, flags, dev_name, data, yaffs_internal_read_super_ram);
+}
+
+/* changes NCB 2.5.70 */
+//static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV);
+static struct file_system_type yaffs_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "yaffsram",
+       .get_sb         = yaffs_ram_read_super,
+//     .kill_sb        = kill_block_super,
+       .kill_sb        = kill_litter_super,
+       .fs_flags       = FS_SINGLE,
+};
+#else
 static struct super_block *yaffs_ram_read_super(struct super_block * sb, void * data, int silent)
 {
        return yaffs_internal_read_super(1,sb,data,silent);
 }
 
 static DECLARE_FSTYPE(yaffs_ram_fs_type, "yaffsram", yaffs_ram_read_super, FS_SINGLE);
+#endif
 #endif // CONFIG_YAFFS_RAM_ENABLED
 
 
@@ -1298,6 +1465,7 @@ static char * yaffs_dump_dev(char *buf,yaffs_Device *dev,char *name)
        buf +=sprintf(buf,"nBlockErasures..... %d\n",dev->nBlockErasures);
        buf +=sprintf(buf,"nGCCopies.......... %d\n",dev->nGCCopies);
        buf +=sprintf(buf,"garbageCollections. %d\n",dev->garbageCollections);
+       buf +=sprintf(buf,"passiveGCs......... %d\n",dev->passiveGarbageCollections);
        buf +=sprintf(buf,"nRetriedWrites..... %d\n",dev->nRetriedWrites);
        buf +=sprintf(buf,"nRetireBlocks...... %d\n",dev->nRetiredBlocks);
        buf +=sprintf(buf,"eccFixed........... %d\n",dev->eccFixed);
@@ -1308,6 +1476,8 @@ static char * yaffs_dump_dev(char *buf,yaffs_Device *dev,char *name)
        buf +=sprintf(buf,"nDeletedFiles...... %d\n",dev->nDeletedFiles);
        buf +=sprintf(buf,"nUnlinkedFiles..... %d\n",dev->nUnlinkedFiles);
        buf +=sprintf(buf,"nBackgroudDeletions %d\n",dev->nBackgroundDeletions);
+       buf +=sprintf(buf,"useNANDECC......... %d\n",dev->useNANDECC);
+
        
        return buf;     
 }