From a1d28cf77fe06fe98cea4c4e80cc1cab94ea6736 Mon Sep 17 00:00:00 2001 From: charles Date: Tue, 17 Sep 2002 04:43:07 +0000 Subject: [PATCH] *** empty log message *** --- devextras.h | 2 +- nand_ecc.c | 20 +- snMakefile | 16 +- utils/mkyaffs | Bin 15115 -> 15933 bytes utils/mkyaffs.c | 48 +- yaffs_fileem.c | 19 +- yaffs_fs.c | 125 +- yaffs_guts.c | 7878 ++++++++++++++++++++++++---------------------- yaffs_guts.h | 999 +++--- yaffs_mtdif.h | 2 +- yaffs_nandemul.h | 2 +- yaffsdev | Bin 118448 -> 123432 bytes yaffsdev.c | 27 +- yaffsdev.proj | Bin 53248 -> 53248 bytes yaffsinterface.h | 2 +- yportenv.h | 8 +- 16 files changed, 4854 insertions(+), 4294 deletions(-) diff --git a/devextras.h b/devextras.h index 24fe5be..bd749b6 100644 --- a/devextras.h +++ b/devextras.h @@ -8,7 +8,7 @@ * Created by Charles Manning * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as + * it under the terms of the GNU Lesser General Public License version 2.1 as * published by the Free Software Foundation. * * This file is just holds extra declarations used during development. diff --git a/nand_ecc.c b/nand_ecc.c index 84b270d..6e2b7cd 100644 --- a/nand_ecc.c +++ b/nand_ecc.c @@ -4,18 +4,18 @@ * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) * Toshiba America Electronics Components, Inc. * - * $Id: nand_ecc.c,v 1.1 2002-05-27 18:57:25 charles Exp $ + * $Id: nand_ecc.c,v 1.2 2002-09-17 04:45:48 charles Exp $ * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. * * This file contains an ECC algorithm from Toshiba that detects and * corrects 1 bit errors in a 256 byte block of data. - * - * - * Slightly hacked to fit in with YAFFS by Charles Manning. */ + // Minor tweak by Charles Manning to prevent exporting symbols + // when compiled in with yaffs. + #if 0 #include #include @@ -211,9 +211,11 @@ int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc) return -1; } - #if 0 EXPORT_SYMBOL(nand_calculate_ecc); EXPORT_SYMBOL(nand_correct_data); -#endif +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Steven J. Hill "); +MODULE_DESCRIPTION("Generic NAND ECC support"); +#endif diff --git a/snMakefile b/snMakefile index dd819c8..6006c69 100644 --- a/snMakefile +++ b/snMakefile @@ -1,6 +1,6 @@ ######################################################### # Makefile auto generated by Cygnus Source Navigator. -# Target: yaffsdev_disk Date: Aug 27 2002 Time: 03:17:30 PM +# Target: yaffsdev_disk_dump_disk Date: Sep 17 2002 Time: 06:47:40 AM # @@ -18,7 +18,7 @@ CPP = g++ YACC_FLAGS = LEX_FLAGS = JAVA_FLAGS = -CC_FLAGS = -g -Wall -DYAFFS_PARANOID -DYAFFS_FILEEM +CC_FLAGS = -g -Wall -DYAFFS_DUMP -DYAFFS_START=1281 -DYAFFS_END=32767 -DCONFIG_YAFFS_SHORT_OP_CACHE -DYAFFS_PARANOID -DYAFFS_FILEEM CPP_FLAGS = YACC_INCLUDES = LEX_INCLUDES = @@ -30,16 +30,16 @@ LEX_DEFINES = JAVA_DEFINES = CC_DEFINES = CPP_DEFINES = -yaffsdev_disk_LIBS = +yaffsdev_disk_dump_disk_LIBS = LINKER = gcc LINKER_FLAGS = LINKER_ENTRY = -yaffsdev_disk_OBJECTS = nand_ecc.o yaffs_fileem.o yaffs_guts.o yaffsdev.o +yaffsdev_disk_dump_disk_OBJECTS = nand_ecc.o yaffs_fileem.o yaffs_guts.o yaffsdev.o -all: yaffsdev +all: yaffsdumpdisk -yaffsdev: $(yaffsdev_disk_OBJECTS) - $(LINKER) -o yaffsdev $(LINKER_ENTRY) $(LINKER_FLAGS) $(yaffsdev_disk_OBJECTS) $(yaffsdev_disk_LIBS) +yaffsdumpdisk: $(yaffsdev_disk_dump_disk_OBJECTS) + $(LINKER) -o yaffsdumpdisk $(LINKER_ENTRY) $(LINKER_FLAGS) $(yaffsdev_disk_dump_disk_OBJECTS) $(yaffsdev_disk_dump_disk_LIBS) .y.c: $(YACC) $< $(YACC_FLAGS) $(YACC_DEFINES) $(YACC_INCLUDES) @@ -76,5 +76,5 @@ yaffsdev.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h /op clean: rm -f *.o - rm -f yaffsdev + rm -f yaffsdumpdisk diff --git a/utils/mkyaffs b/utils/mkyaffs index 3bf419059c64fda043fd7026953e7d2535aca490..94a7df35310c59073d53dd1f78cbd23f585bb5df 100755 GIT binary patch delta 3915 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 lp>4Npamb0L4`h06pH$XWmBwNZJ=(W3wrg\n", argv[0]); + printf("usage: %s [image name]\n", argv[0]); exit(1); } + + if(argc > 2 && + (img = open(argv[2],O_RDONLY)) == -1) { + perror("opening image file"); + exit(1); + } + + if(img >= 0){ + imglen = lseek(img,0,SEEK_END); + if(imglen %528){ + printf("Image not a multiple of 528 bytes\n"); + exit(1); + } + } + + lseek(img,0,SEEK_SET); /* Open the device */ if((fd = open(argv[1], O_RDWR)) == -1) { - perror("open flash"); + perror("opening flash"); exit(1); } @@ -103,7 +123,14 @@ int main(int argc, char **argv) exit(1); } + if(imglen >= 0 && + (imglen/528 +32)*512 > meminfo.size){ + printf("Image is too big for NAND\n"); + exit(1); + } + + printf("Erasing and programming NAND\n"); for(addr = 0; addr < meminfo.size; addr += meminfo.erasesize) { /* Read the OOB data to determine if the block is valid. @@ -137,11 +164,28 @@ int main(int argc, char **argv) exit(1); } + /* Do some programming, but not in the first block */ + + if(addr){ + for(offset = 0; offset #include -#define FILE_SIZE_IN_MEG 32 +#define FILE_SIZE_IN_MEG 4 + +// #define YAFFS_ERROR_TESTING #define BLOCK_SIZE (32 * 528) #define BLOCKS_PER_MEG ((1024*1024)/(32 * 512)) @@ -43,6 +45,7 @@ static int markedBadBlocks[] = { 1, 4, -1}; static int IsAMarkedBadBlock(int blk) { +#if YAFFS_ERROR_TESTING int *m = markedBadBlocks; while(*m >= 0) @@ -50,12 +53,14 @@ static int IsAMarkedBadBlock(int blk) if(*m == blk) return 1; m++; } +#endif return 0; } static __u8 yaffs_WriteFailCorruption(int chunkInNAND) { +#if YAFFS_ERROR_TESTING // Whole blocks that fail switch(chunkInNAND/YAFFS_CHUNKS_PER_BLOCK) @@ -82,28 +87,32 @@ static __u8 yaffs_WriteFailCorruption(int chunkInNAND) } - +#endif return 0; } static void yaffs_ModifyWriteData(int chunkInNAND,__u8 *data) { +#if YAFFS_ERROR_TESTING if(data) { *data ^= yaffs_WriteFailCorruption(chunkInNAND); } +#endif } static __u8 yaffs_ReadFailCorruption(int chunkInNAND) { switch(chunkInNAND) { +#if YAFFS_ERROR_TESTING case 500: return 3;// ding two bits case 700: case 750: return 1;// ding one bit +#endif default: return 0; } @@ -111,10 +120,12 @@ static __u8 yaffs_ReadFailCorruption(int chunkInNAND) static void yaffs_ModifyReadData(int chunkInNAND,__u8 *data) { +#if YAFFS_ERROR_TESTING if(data) { *data ^= yaffs_ReadFailCorruption(chunkInNAND); } +#endif } @@ -132,7 +143,11 @@ static void CheckInit(yaffs_Device *dev) { memset(ffChunk,0xFF,528); +#ifdef YAFFS_DUMP + h = open("yaffs-em-file" , O_RDONLY); +#else h = open("yaffs-em-file" , O_RDWR | O_CREAT, S_IREAD | S_IWRITE); +#endif if(h < 0) { perror("Fatal error opening yaffs emulation file"); diff --git a/yaffs_fs.c b/yaffs_fs.c index d494e83..29c8408 100644 --- a/yaffs_fs.c +++ b/yaffs_fs.c @@ -21,7 +21,7 @@ * * Acknowledgements: * * Luc van OostenRyck for numerous patches. - * * Nick Bane for patches marked NCB. + * * Nick Bane for numerous patches. * * Some code bodily lifted from JFFS2. */ @@ -90,6 +90,9 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf); static void yaffs_read_inode (struct inode *inode); static struct super_block *yaffs_read_super(struct super_block * sb, void * data, int silent); static void yaffs_put_inode (struct inode *inode); +static void yaffs_delete_inode(struct inode *); +static void yaffs_clear_inode(struct inode *); + static int yaffs_readpage(struct file *file, struct page * page); static int yaffs_prepare_write(struct file *f, struct page *pg, unsigned offset, unsigned to); @@ -101,7 +104,6 @@ static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd); - static struct address_space_operations yaffs_file_address_operations = { readpage: yaffs_readpage, prepare_write: yaffs_prepare_write, @@ -160,9 +162,9 @@ static struct super_operations yaffs_super_ops = { read_inode: yaffs_read_inode, put_inode: yaffs_put_inode, put_super: yaffs_put_super, -// read_inode: // remount_fs: -// clear_inode: + delete_inode: yaffs_delete_inode, + clear_inode: yaffs_clear_inode, }; @@ -257,13 +259,14 @@ static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry) if(inode) { - T((KERN_DEBUG"yaffs_loookup looks good\n")); - // try to fix ln -s prob dget(dentry); // try to solve directory bug + T((KERN_DEBUG"yaffs_loookup dentry \n")); + //dget(dentry); // try to solve directory bug d_add(dentry,inode); yaffs_GrossUnlock(dev); - return dentry; + // return dentry; + return NULL; } } @@ -275,18 +278,57 @@ static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry) yaffs_GrossUnlock(dev); return NULL; + // return (ERR_PTR(-EIO)); } // For now put inode is just for debugging +// Put inode is called when the inode **structure** is put. static void yaffs_put_inode(struct inode *inode) { T(("yaffs_put_inode: ino %d, count %d\n",(int)inode->i_ino, atomic_read(&inode->i_count))); - // yaffs_FlushFile(yaffs_InodeToObject(inode)); +} + +// clear is called to tell the fs to release any per-inode data it holds +static void yaffs_clear_inode(struct inode *inode) +{ + yaffs_Object *obj = yaffs_InodeToObject(inode); + + T(("yaffs_clear_inode: ino %d, count %d %s\n",(int)inode->i_ino, atomic_read(&inode->i_count), + obj ? "object exists" : "null object")); + + if(obj) + { + obj->myInode = NULL; + inode->u.generic_ip = NULL; + } + } +// delete is called when the link count is zero and the inode +// is put (ie. nobody wants to know about it anymore, time to +// delete the file). +// NB Must call clear_inode() +static void yaffs_delete_inode(struct inode *inode) +{ + yaffs_Object *obj = yaffs_InodeToObject(inode); + yaffs_Device *dev; + + T(("yaffs_delete_inode: ino %d, count %d %s\n",(int)inode->i_ino, atomic_read(&inode->i_count), + obj ? "object exists" : "null object")); + + if(obj) + { + dev = obj->myDev; + yaffs_GrossLock(dev); + yaffs_DeleteFile(obj); + yaffs_GrossUnlock(dev); + } + clear_inode(inode); +} + static int yaffs_file_flush(struct file* file) { @@ -455,6 +497,7 @@ static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object *obj) inode->u.generic_ip = obj; + obj->myInode = inode; } else @@ -473,7 +516,8 @@ struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Ob inode = iget(sb,obj->objectId); // NB Side effect: iget calls back to yaffs_read_inode(). - + // iget also increments the inode's i_count + return inode; } @@ -753,6 +797,8 @@ static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) static int yaffs_unlink(struct inode * dir, struct dentry *dentry) { int retVal; + int nlinks; + yaffs_Device *dev; @@ -761,11 +807,17 @@ static int yaffs_unlink(struct inode * dir, struct dentry *dentry) dev = yaffs_InodeToObject(dir)->myDev; yaffs_GrossLock(dev); + + nlinks = + retVal = yaffs_Unlink(yaffs_InodeToObject(dir),dentry->d_name.name); + + yaffs_GrossUnlock(dev); if( retVal == YAFFS_OK) { + dentry->d_inode->i_nlink--; return 0; } else @@ -794,10 +846,23 @@ static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dent link = yaffs_Link(yaffs_InodeToObject(dir),dentry->d_name.name,obj); + + if(link) + { + old_dentry->d_inode->i_nlink = yaffs_GetObjectLinkCount(obj); + d_instantiate(dentry, old_dentry->d_inode); + atomic_inc(&old_dentry->d_inode->i_count); + T((KERN_DEBUG"yaffs_link link count %d i_count %d\n", + old_dentry->d_inode->i_nlink,atomic_read(&old_dentry->d_inode->i_count))); + + } + yaffs_GrossUnlock(dev); + if(link) { + return 0; } @@ -853,6 +918,8 @@ static int yaffs_sync_object(struct file * file, struct dentry *dentry, int data /* * The VFS layer already does all the dentry stuff for rename. + * + * NB: POSIX says you can rename an object over an old object of the same name */ static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry) { @@ -916,7 +983,7 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf) buf->f_type = YAFFS_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_namelen = 255; - buf->f_blocks = dev->nBlocks * YAFFS_CHUNKS_PER_BLOCK/ + buf->f_blocks = (dev->endBlock - dev->startBlock + 1) * YAFFS_CHUNKS_PER_BLOCK/ (sb->s_blocksize/YAFFS_BYTES_PER_CHUNK); buf->f_files = 0; buf->f_ffree = 0; @@ -980,6 +1047,7 @@ static void yaffs_MTDPutSuper(struct super_block *sb) static struct super_block *yaffs_internal_read_super(int useRam, struct super_block * sb, void * data, int silent) { + int nBlocks; struct inode * inode; struct dentry * root; yaffs_Device *dev; @@ -1030,9 +1098,9 @@ static struct super_block *yaffs_internal_read_super(int useRam, struct super_bl memset(dev,0,sizeof(yaffs_Device)); dev->genericDevice = NULL; // Not used for RAM emulation. - dev->nBlocks = YAFFS_RAM_EMULATION_SIZE / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); + nBlocks = YAFFS_RAM_EMULATION_SIZE / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); dev->startBlock = 1; // Don't use block 0 - dev->endBlock = dev->nBlocks - 1; + dev->endBlock = nBlocks - 1; dev->writeChunkToNAND = nandemul_WriteChunkToNAND; dev->readChunkFromNAND = nandemul_ReadChunkFromNAND; @@ -1108,10 +1176,9 @@ static struct super_block *yaffs_internal_read_super(int useRam, struct super_bl // Set up the memory size parameters.... -// NCB dev->nBlocks = YAFFS_RAM_EMULATION_SIZE / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); - dev->nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); + nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); dev->startBlock = 1; // Don't use block 0 - dev->endBlock = dev->nBlocks - 1; + dev->endBlock = nBlocks - 1; // ... and the functions. dev->writeChunkToNAND = nandmtd_WriteChunkToNAND; @@ -1177,6 +1244,7 @@ static DECLARE_FSTYPE(yaffs_ram_fs_type, "yaffsram", yaffs_ram_read_super, FS_SI static struct proc_dir_entry *my_proc_entry; +static struct proc_dir_entry *my_proc_ram_write_entry; static int yaffs_proc_read( @@ -1203,6 +1271,21 @@ static int yaffs_proc_read( return strlen(my_buffer); } + +static int yaffs_proc_ram_write( + char *page, + char **start, + off_t offset, + int count, + int *eof, + void *data + ) +{ + + printk(KERN_DEBUG "yaffs write size %d\n",count); + return count; +} + static int __init init_yaffs_fs(void) { int error = 0; @@ -1228,6 +1311,18 @@ static int __init init_yaffs_fs(void) #ifdef CONFIG_YAFFS_RAM_ENABLED + my_proc_ram_write_entry = create_proc_entry("yaffs_ram", + S_IRUGO | S_IFREG, + &proc_root); + + if(!my_proc_ram_write_entry) + { + return -ENOMEM; + } + else + { + my_proc_ram_write_entry->write_proc = yaffs_proc_ram_write; + } error = register_filesystem(&yaffs_ram_fs_type); if(error) { diff --git a/yaffs_guts.c b/yaffs_guts.c index fa1c9d5..b042a9a 100644 --- a/yaffs_guts.c +++ b/yaffs_guts.c @@ -1,3770 +1,4108 @@ -/* - * YAFFS: Yet another FFS. A NAND-flash specific file system. - * yaffs_guts.c The main guts of YAFFS - * - * Copyright (C) 2002 Aleph One Ltd. - * for Toby Churchill Ltd and Brightstar Engineering - * - * Created by Charles Manning - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - //yaffs_guts.c - -#include "yportenv.h" - -#include "yaffsinterface.h" -#include "yaffs_guts.h" - - - -// External functions for ECC on data -void nand_calculate_ecc (const u_char *dat, u_char *ecc_code); -int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc); - - -// countBits is a quick way of counting the number of bits in a byte. -// ie. countBits[n] holds the number of 1 bits in a byte with the value n. - -static const char yaffs_countBitsTable[256] = -{ -0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4, -1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, -1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, -2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, -1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, -2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, -2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, -3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, -1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, -2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, -2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, -3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, -2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, -3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, -3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, -4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 -}; - -static int yaffs_CountBits(__u8 x) -{ - int retVal; - retVal = yaffs_countBitsTable[x]; - return retVal; -} - - - -// Device info -//static yaffs_Device *yaffs_device; -//yaffs_Object *yaffs_rootDir; -//yaffs_Object *yaffs_lostNFound; - - - -// Local prototypes -static int yaffs_CheckObjectHashSanity(yaffs_Device *dev); -static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr); -static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr); -static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan); - -static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type); -static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj); -static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force); -static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId); -static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj); -static int yaffs_CheckStructures(void); - -// Robustification -static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND); -static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND); -static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND); -static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare); -static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare); - -static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND); - -static int yaffs_UnlinkWorker(yaffs_Object *obj); - - -static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1); - - - -loff_t yaffs_GetFileSize(yaffs_Object *obj); - -static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted); -static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted); - - -static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve); - -#ifdef YAFFS_PARANOID -static int yaffs_CheckFileSanity(yaffs_Object *in); -#else -#define yaffs_CheckFileSanity(in) -#endif - -static int __inline__ yaffs_HashFunction(int n) -{ - return (n % YAFFS_NOBJECT_BUCKETS); -} - - -yaffs_Object *yaffs_Root(yaffs_Device *dev) -{ - return dev->rootDir; -} - -yaffs_Object *yaffs_LostNFound(yaffs_Device *dev) -{ - return dev->lostNFoundDir; -} - - -static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare) -{ - dev->nPageWrites++; - return dev->writeChunkToNAND(dev,chunkInNAND,data,spare); -} - - -int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare,int doErrorCorrection) -{ - int retVal; - __u8 calcEcc[3]; - yaffs_Spare localSpare; - int eccResult1,eccResult2; - - dev->nPageReads++; - - if(!spare && data) - { - // If we don't have a real spare, then we use a local one. - // Need this for the calculation of the ecc - spare = &localSpare; - } - - retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare); - if(data && doErrorCorrection) - { - // Do ECC correction - //Todo handle any errors - nand_calculate_ecc(data,calcEcc); - eccResult1 = nand_correct_data (data,spare->ecc1, calcEcc); - nand_calculate_ecc(&data[256],calcEcc); - eccResult2 = nand_correct_data (&data[256],spare->ecc2, calcEcc); - - if(eccResult1>0) - { - T((TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND)); - } - else if(eccResult1<0) - { - T((TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND)); - } - - if(eccResult2>0) - { - T((TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND)); - } - else if(eccResult2<0) - { - T((TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND)); - } - - if(eccResult1 || eccResult2) - { - // Hoosterman, we had a data problem on this page - yaffs_HandleReadDataError(dev,chunkInNAND); - } - } - return retVal; -} - - -static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND) -{ - - static int init = 0; - static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK]; - static __u8 data[YAFFS_BYTES_PER_CHUNK]; - static __u8 spare[16]; - - - dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare); - - - - if(!init) - { - memset(cmpbuf,0xff,YAFFS_BYTES_PER_CHUNK); - init = 1; - } - - if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) return YAFFS_FAIL; - if(memcmp(cmpbuf,spare,16)) return YAFFS_FAIL; - - - return YAFFS_OK; - -} - - - -int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND) -{ - dev->nBlockErasures++; - return dev->eraseBlockInNAND(dev,blockInNAND); -} - -int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev) -{ - return dev->initialiseNAND(dev); -} - -static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_Spare *spare,int useReserve) -{ - int chunk; - - int writeOk = 1; - int attempts = 0; - - unsigned char rbData[YAFFS_BYTES_PER_CHUNK]; - yaffs_Spare rbSpare; - - do{ - chunk = yaffs_AllocateChunk(dev,useReserve); - - if(chunk >= 0) - { - - // First check this chunk is erased... -#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK - writeOk = yaffs_CheckChunkErased(dev,chunk); -#endif - if(!writeOk) - { - T((TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk)); - } - else - { - writeOk = yaffs_WriteChunkToNAND(dev,chunk,data,spare); - } - attempts++; - if(writeOk) - { - // Readback & verify - // If verify fails, then delete this chunk and try again - // To verify we compare everything except the block and - // page status bytes. - // NB We check a raw read without ECC correction applied - yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare,0); - -#ifndef CONFIG_YAFFS_DISABLE_WRITE_VERIFY - if(!yaffs_VerifyCompare(data,rbData,spare,&rbSpare)) - { - // Didn't verify - T((TSTR("**>> yaffs write verify failed on chunk %d" TENDSTR), chunk)); - - writeOk = 0; - } -#endif - - } - if(writeOk) - { - // Copy the data into the write buffer. - // NB We do this at the end to prevent duplicates in the case of a write error. - //Todo - yaffs_HandleWriteChunkOk(dev,chunk,data,spare); - } - else - { - yaffs_HandleWriteChunkError(dev,chunk); - } - } - - } while(chunk >= 0 && ! writeOk); - - if(attempts > 1) - { - T((TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts)); - dev->nRetriedWrites+= (attempts - 1); - } - - return chunk; -} - -/// -// Functions for robustisizing -// -// - -static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND) -{ - // Ding the blockStatus in the first two pages of the block. - - yaffs_Spare spare; - - memset(&spare, 0xff,sizeof(yaffs_Spare)); - - spare.blockStatus = 0; - - yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK, NULL , &spare); - yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK + 1, NULL , &spare); - - dev->blockInfo[blockInNAND].blockState = YAFFS_BLOCK_STATE_DEAD; - dev->nRetiredBlocks++; -} - - - -static int yaffs_RewriteBufferedBlock(yaffs_Device *dev) -{ - dev->doingBufferedBlockRewrite = 1; - // - // Remove erased chunks - // Rewrite existing chunks to a new block - // Set current write block to the new block - - dev->doingBufferedBlockRewrite = 0; - - return 1; -} - - -static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND) -{ - int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK; - - // Mark the block for retirement - dev->blockInfo[blockInNAND].needsRetiring = 1; - - //TODO - // Just do a garbage collection on the affected block then retire the block - // NB recursion -} - - -static void yaffs_CheckWrittenBlock(yaffs_Device *dev,int chunkInNAND) -{ -} - -static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare) -{ -} - -static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare) -{ -} - -static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND) -{ - int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK; - - // Mark the block for retirement - dev->blockInfo[blockInNAND].needsRetiring = 1; - // Delete the chunk - yaffs_DeleteChunk(dev,chunkInNAND); -} - - - - -static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1) -{ - - - if( memcmp(d0,d1,YAFFS_BYTES_PER_CHUNK) != 0 || - s0->tagByte0 != s1->tagByte0 || - s0->tagByte1 != s1->tagByte1 || - s0->tagByte2 != s1->tagByte2 || - s0->tagByte3 != s1->tagByte3 || - s0->tagByte4 != s1->tagByte4 || - s0->tagByte5 != s1->tagByte5 || - s0->tagByte6 != s1->tagByte6 || - s0->tagByte7 != s1->tagByte7 || - s0->ecc1[0] != s1->ecc1[0] || - s0->ecc1[1] != s1->ecc1[1] || - s0->ecc1[2] != s1->ecc1[2] || - s0->ecc2[0] != s1->ecc2[0] || - s0->ecc2[1] != s1->ecc2[1] || - s0->ecc2[2] != s1->ecc2[2] ) - { - return 0; - } - - return 1; -} - - -///////////////////////// Object management ////////////////// -// List of spare objects -// The list is hooked together using the first pointer -// in the object - -// static yaffs_Object *yaffs_freeObjects = NULL; - -// static int yaffs_nFreeObjects; - -// static yaffs_ObjectList *yaffs_allocatedObjectList = NULL; - -// static yaffs_ObjectBucket yaffs_objectBucket[YAFFS_NOBJECT_BUCKETS]; - - -static __u16 yaffs_CalcNameSum(const char *name) -{ - __u16 sum = 0; - __u16 i = 1; - - __u8 *bname = (__u8 *)name; - - while (*bname) - { - sum += (*bname) * i; - i++; - bname++; - } - return sum; -} - - -void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare) -{ - nand_calculate_ecc (data , spare->ecc1); - nand_calculate_ecc (&data[256] , spare->ecc2); -} - -void yaffs_CalcTagsECC(yaffs_Tags *tags) -{ - // Calculate an ecc - - unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes; - unsigned i,j; - unsigned ecc = 0; - unsigned bit = 0; - - tags->ecc = 0; - - for(i = 0; i < 8; i++) - { - for(j = 1; j &0x7f; j<<=1) - { - bit++; - if(b[i] & j) - { - ecc ^= bit; - } - } - } - - tags->ecc = ecc; - - -} - -void yaffs_CheckECCOnTags(yaffs_Tags *tags) -{ - unsigned ecc = tags->ecc; - - yaffs_CalcTagsECC(tags); - - ecc ^= tags->ecc; - - if(ecc) - { - // Needs fixing - unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes; - - ecc--; - - b[ecc / 8] ^= (1 << (ecc & 7)); - - // Now recvalc the ecc - yaffs_CalcTagsECC(tags); - } -} - - -///////////////////////// TNODES /////////////////////// - -// List of spare tnodes -// The list is hooked together using the first pointer -// in the tnode. - -//static yaffs_Tnode *yaffs_freeTnodes = NULL; - -// static int yaffs_nFreeTnodes; - -//static yaffs_TnodeList *yaffs_allocatedTnodeList = NULL; - - - -// yaffs_CreateTnodes creates a bunch more tnodes and -// adds them to the tnode free list. -// Don't use this function directly - -static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes) -{ - int i; - yaffs_Tnode *newTnodes; - yaffs_TnodeList *tnl; - - if(nTnodes < 1) return YAFFS_OK; - - // make these things - - newTnodes = YMALLOC(nTnodes * sizeof(yaffs_Tnode)); - - if (!newTnodes) - { - YALERT("Could not malloc tnodes"); - return YAFFS_FAIL; - } - - // Hook them into the free list - for(i = 0; i < nTnodes - 1; i++) - { - newTnodes[i].internal[0] = &newTnodes[i+1]; - } - - newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes; - dev->freeTnodes = newTnodes; - dev->nFreeTnodes+= nTnodes; - dev->nTnodesCreated += nTnodes; - - // Now add this bunch of tnodes to a list for freeing up. - - tnl = YMALLOC(sizeof(yaffs_TnodeList)); - if(!tnl) - { - YALERT("Could not add tnodes to management list"); - } - else - { - tnl->tnodes = newTnodes; - tnl->next = dev->allocatedTnodeList; - dev->allocatedTnodeList = tnl; - } - - - YINFO("Tnodes created"); - - - return YAFFS_OK; -} - - -// GetTnode gets us a clean tnode. Tries to make allocate more if we run out -static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev) -{ - yaffs_Tnode *tn = NULL; - - // If there are none left make more - if(!dev->freeTnodes) - { - yaffs_CreateTnodes(dev,YAFFS_ALLOCATION_NTNODES); - } - - if(dev->freeTnodes) - { - tn = dev->freeTnodes; - dev->freeTnodes = dev->freeTnodes->internal[0]; - dev->nFreeTnodes--; - // zero out - memset(tn,0,sizeof(yaffs_Tnode)); - } - - - return tn; -} - - -// FreeTnode frees up a tnode and puts it back on the free list -static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn) -{ - tn->internal[0] = dev->freeTnodes; - dev->freeTnodes = tn; - dev->nFreeTnodes++; -} - - -static void yaffs_DeinitialiseTnodes(yaffs_Device*dev) -{ - // Free the list of allocated tnodes - - while(dev->allocatedTnodeList) - { - YFREE(dev->allocatedTnodeList->tnodes); - dev->allocatedTnodeList = dev->allocatedTnodeList->next; - } - - dev->freeTnodes = NULL; - dev->nFreeTnodes = 0; -} - -static void yaffs_InitialiseTnodes(yaffs_Device*dev) -{ - dev->allocatedTnodeList = NULL; - dev->freeTnodes = NULL; - dev->nFreeTnodes = 0; - dev->nTnodesCreated = 0; - -} - -void yaffs_TnodeTest(yaffs_Device *dev) -{ - - int i; - int j; - yaffs_Tnode *tn[1000]; - - YINFO("Testing TNodes"); - - for(j = 0; j < 50; j++) - { - for(i = 0; i < 1000; i++) - { - tn[i] = yaffs_GetTnode(dev); - if(!tn[i]) - { - YALERT("Getting tnode failed"); - } - } - for(i = 0; i < 1000; i+=3) - { - yaffs_FreeTnode(dev,tn[i]); - tn[i] = NULL; - } - - } -} - -////////////////// END OF TNODE MANIPULATION /////////////////////////// - -/////////////// Functions to manipulate the look-up tree (made up of tnodes) -// The look up tree is represented by the top tnode and the number of topLevel -// in the tree. 0 means only the level 0 tnode is in the tree. - - -// FindLevel0Tnode finds the level 0 tnode, if one exists. -// Used when reading..... -static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,yaffs_FileStructure *fStruct, __u32 chunkId) -{ - - yaffs_Tnode *tn = fStruct->top; - __u32 i; - int requiredTallness; - int level = fStruct->topLevel; - - // Check sane level and chunk Id - if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL) - { - char str[50]; - sprintf(str,"Bad level %d",level); - YALERT(str); - return NULL; - } - - if(chunkId > YAFFS_MAX_CHUNK_ID) - { - char str[50]; - sprintf(str,"Bad chunkId %d",chunkId); - YALERT(str); - return NULL; - } - - // First check we're tall enough (ie enough topLevel) - - i = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS); - requiredTallness = 0; - while(i) - { - i >>= YAFFS_TNODES_INTERNAL_BITS; - requiredTallness++; - } - - - if(requiredTallness > fStruct->topLevel) - { - // Not tall enough, so we can't find it, return NULL. - return NULL; - } - - - // Traverse down to level 0 - while (level > 0 && tn) - { - tn = tn->internal[(chunkId >>(/* dev->chunkGroupBits + */ YAFFS_TNODES_LEVEL0_BITS + (level-1) * YAFFS_TNODES_INTERNAL_BITS)) & - YAFFS_TNODES_INTERNAL_MASK]; - level--; - - } - - return tn; -} - -// AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree. -// This happens in two steps: -// 1. If the tree isn't tall enough, then make it taller. -// 2. Scan down the tree towards the level 0 tnode adding tnodes if required. -// -// Used when modifying the tree. -// -static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId) -{ - - yaffs_Tnode *tn; - - int requiredTallness; - - __u32 i; - __u32 l; - - - //T((TSTR("AddOrFind topLevel=%d, chunk=%d"),fStruct->topLevel,chunkId)); - - // Check sane level and page Id - if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL) - { - char str[50]; - sprintf(str,"Bad level %d",fStruct->topLevel); - YALERT(str); - return NULL; - } - - if(chunkId > YAFFS_MAX_CHUNK_ID) - { - char str[50]; - sprintf(str,"Bad chunkId %d",chunkId); - YALERT(str); - return NULL; - } - - // First check we're tall enough (ie enough topLevel) - - i = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS); - requiredTallness = 0; - while(i) - { - i >>= YAFFS_TNODES_INTERNAL_BITS; - requiredTallness++; - } - - //T((TSTR(" required=%d"),requiredTallness)); - - - if(requiredTallness > fStruct->topLevel) - { - // Not tall enough,gotta make the tree taller - for(i = fStruct->topLevel; i < requiredTallness; i++) - { - //T((TSTR(" add new top"))); - - tn = yaffs_GetTnode(dev); - - if(tn) - { - tn->internal[0] = fStruct->top; - fStruct->top = tn; - } - else - { - YALERT("No more tnodes"); - } - } - - fStruct->topLevel = requiredTallness; - } - - - // Traverse down to level 0, adding anything we need - - l = fStruct->topLevel; - tn = fStruct->top; - while (l > 0 && tn) - { - i = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) & - YAFFS_TNODES_INTERNAL_MASK; - - //T((TSTR(" [%d:%d]"),l,i)); - - if(!tn->internal[i]) - { - //T((TSTR(" added"))); - - tn->internal[i] = yaffs_GetTnode(dev); - } - - tn = tn->internal[i]; - l--; - - } - - //TSTR(TENDSTR))); - - return tn; -} - -// DeleteWorker scans backwards through the tnode tree and delets all the -// chunks and tnodes in the file - -static void yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset) -{ - int i; - int chunkInInode; - int theChunk; - yaffs_Tags tags; - int found; - int chunkDeleted; - - - if(tn) - { - if(level > 0) - { - - for(i = YAFFS_NTNODES_INTERNAL -1; i >= 0; i--) - { - if(tn->internal[i]) - { - yaffs_DeleteWorker(in,tn->internal[i],level - 1, - (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i ); - yaffs_FreeTnode(in->myDev,tn->internal[i]); - tn->internal[i] = NULL; - } - - } - } - else if(level == 0) - { - for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0; i--) - { - if(tn->level0[i]) - { - int j; - - chunkInInode = (chunkOffset << YAFFS_TNODES_LEVEL0_BITS ) + i; - - theChunk = tn->level0[i] << in->myDev->chunkGroupBits; - - // Now we need to search for it - for(j = 0,found = 0; theChunk && j < in->myDev->chunkGroupSize && !found; j++) - { - yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,&tags,&chunkDeleted); - if(yaffs_TagsMatch(&tags,in->objectId,chunkInInode,chunkDeleted)) - { - // found it; - found = 1; - - } - else - { - theChunk++; - } - } - - if(found) - { - yaffs_DeleteChunk(in->myDev,theChunk); - - } - - tn->level0[i] = 0; - } - - } - - } - - } - -} - - - - -// Pruning removes any part of the file structure tree that is beyond the -// bounds of the file (ie that does not point to chunks). -// -// A file should only get pruned when its size is reduced. -// -// Before pruning, the chunks must be pulled from the tree and the -// level 0 tnode entries must be zeroed out. -// Could also use this for file deletion, but that's probably better handled -// by a special case. - -// yaffs_PruneWorker should only be called by yaffs_PruneFileStructure() - -static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn, __u32 level, int del0) -{ - int i; - int hasData; - - if(tn) - { - hasData = 0; - - for(i = 0; i < YAFFS_NTNODES_INTERNAL; i++) - { - if(tn->internal[i] && level > 0) - { - tn->internal[i] = yaffs_PruneWorker(dev,tn->internal[i],level - 1, ( i == 0) ? del0 : 1); - } - - if(tn->internal[i]) - { - hasData++; - } - } - - if(hasData == 0 && del0) - { - // Free and return NULL - - yaffs_FreeTnode(dev,tn); - tn = NULL; - } - - } - - return tn; - -} - -static int yaffs_PruneFileStructure(yaffs_Device *dev, yaffs_FileStructure *fStruct) -{ - int i; - int hasData; - int done = 0; - yaffs_Tnode *tn; - - if(fStruct->topLevel > 0) - { - fStruct->top = yaffs_PruneWorker(dev,fStruct->top, fStruct->topLevel,0); - - // Now we have a tree with all the non-zero branches NULL but the height - // is the same as it was. - // Let's see if we can trim internal tnodes to shorten the tree. - // We can do this if only the 0th element in the tnode is in use - // (ie all the non-zero are NULL) - - while(fStruct->topLevel && !done) - { - tn = fStruct->top; - - hasData = 0; - for(i = 1; i internal[i]) - { - hasData++; - } - } - - if(!hasData) - { - fStruct->top = tn->internal[0]; - fStruct->topLevel--; - yaffs_FreeTnode(dev,tn); - } - else - { - done = 1; - } - } - } - - return YAFFS_OK; -} - - - - -/////////////////////// End of File Structure functions. ///////////////// - -// yaffs_CreateFreeObjects creates a bunch more objects and -// adds them to the object free list. -static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects) -{ - int i; - yaffs_Object *newObjects; - yaffs_ObjectList *list; - - if(nObjects < 1) return YAFFS_OK; - - // make these things - - newObjects = YMALLOC(nObjects * sizeof(yaffs_Object)); - - if (!newObjects) - { - YALERT("Could not allocate more objects"); - return YAFFS_FAIL; - } - - // Hook them into the free list - for(i = 0; i < nObjects - 1; i++) - { - (yaffs_Object *)newObjects[i].siblings.next = &newObjects[i+1]; - } - - newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects; - dev->freeObjects = newObjects; - dev->nFreeObjects+= nObjects; - dev->nObjectsCreated+= nObjects; - - // Now add this bunch of Objects to a list for freeing up. - - list = YMALLOC(sizeof(yaffs_ObjectList)); - if(!list) - { - YALERT("Could not add Objects to management list"); - } - else - { - list->objects = newObjects; - list->next = dev->allocatedObjectList; - dev->allocatedObjectList = list; - } - - - YINFO("Objects created"); - - - return YAFFS_OK; -} - - -// AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out -static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev) -{ - yaffs_Object *tn = NULL; - - // If there are none left make more - if(!dev->freeObjects) - { - yaffs_CreateFreeObjects(dev,YAFFS_ALLOCATION_NOBJECTS); - } - - if(dev->freeObjects) - { - tn = dev->freeObjects; - dev->freeObjects = (yaffs_Object *)(dev->freeObjects->siblings.next); - dev->nFreeObjects--; - - // Now sweeten it up... - - memset(tn,0,sizeof(yaffs_Object)); - tn->chunkId = -1; - tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN; - INIT_LIST_HEAD(&(tn->hardLinks)); - INIT_LIST_HEAD(&(tn->hashLink)); - INIT_LIST_HEAD(&tn->siblings); - - // Add it to the lost and found directory. - // NB Can't put root or lostNFound in lostNFound so - // check if lostNFound exists first - if(dev->lostNFoundDir) - { - yaffs_AddObjectToDirectory(dev->lostNFoundDir,tn); - } - } - - - return tn; -} - -static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev,int number,__u32 mode) -{ - - yaffs_Object *obj = yaffs_CreateNewObject(dev,number,YAFFS_OBJECT_TYPE_DIRECTORY); - if(obj) - { - obj->fake = 1; // it is fake so it has no NAND presence... - obj->renameAllowed= 0; // ... and we're not allowed to rename it... - obj->unlinkAllowed= 0; // ... or unlink it - obj->st_mode = mode; - obj->myDev = dev; - obj->chunkId = 0; // Not a valid chunk. - } - - return obj; - -} - - -static void yaffs_UnhashObject(yaffs_Object *tn) -{ - int bucket; - yaffs_Device *dev = tn->myDev; - - - // If it is still linked into the bucket list, free from the list - if(!list_empty(&tn->hashLink)) - { - list_del_init(&tn->hashLink); - bucket = yaffs_HashFunction(tn->objectId); - dev->objectBucket[bucket].count--; - } - -} - - -// FreeObject frees up a Object and puts it back on the free list -static void yaffs_FreeObject(yaffs_Object *tn) -{ - - yaffs_Device *dev = tn->myDev; - - yaffs_UnhashObject(tn); - - // Link into the free list. - (yaffs_Object *)(tn->siblings.next) = dev->freeObjects; - dev->freeObjects = tn; - dev->nFreeObjects++; -} - - - - -static void yaffs_DeinitialiseObjects(yaffs_Device *dev) -{ - // Free the list of allocated Objects - - while( dev->allocatedObjectList) - { - YFREE(dev->allocatedObjectList->objects); - dev->allocatedObjectList = dev->allocatedObjectList->next; - } - - dev->freeObjects = NULL; - dev->nFreeObjects = 0; -} - -static void yaffs_InitialiseObjects(yaffs_Device *dev) -{ - int i; - - dev->allocatedObjectList = NULL; - dev->freeObjects = NULL; - dev->nFreeObjects = 0; - - for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) - { - INIT_LIST_HEAD(&dev->objectBucket[i].list); - dev->objectBucket[i].count = 0; - } - -} - - - - - - -int yaffs_FindNiceObjectBucket(yaffs_Device *dev) -{ - static int x = 0; - int i; - int l = 999; - int lowest = 999999; - - - // First let's see if we can find one that's empty. - - for(i = 0; i < 10 && lowest > 0; i++) - { - x++; - x %= YAFFS_NOBJECT_BUCKETS; - if(dev->objectBucket[x].count < lowest) - { - lowest = dev->objectBucket[x].count; - l = x; - } - - } - - // If we didn't find an empty list, then try - // looking a bit further for a short one - - for(i = 0; i < 10 && lowest > 3; i++) - { - x++; - x %= YAFFS_NOBJECT_BUCKETS; - if(dev->objectBucket[x].count < lowest) - { - lowest = dev->objectBucket[x].count; - l = x; - } - - } - - return l; -} - -static int yaffs_CreateNewObjectNumber(yaffs_Device *dev) -{ - int bucket = yaffs_FindNiceObjectBucket(dev); - - // Now find an object value that has not already been taken - // by scanning the list. - - int found = 0; - struct list_head *i; - - int n = bucket; - - //yaffs_CheckObjectHashSanity(); - - while(!found) - { - found = 1; - n += YAFFS_NOBJECT_BUCKETS; - if(1 ||dev->objectBucket[bucket].count > 0) - { - list_for_each(i,&dev->objectBucket[bucket].list) - { - // If there is already one in the list - if(list_entry(i, yaffs_Object,hashLink)->objectId == n) - { - found = 0; - } - } - } - } - - //T(("bucket %d count %d inode %d\n",bucket,yaffs_objectBucket[bucket].count,n); - - return n; -} - -void yaffs_HashObject(yaffs_Object *in) -{ - int bucket = yaffs_HashFunction(in->objectId); - yaffs_Device *dev = in->myDev; - - if(!list_empty(&in->hashLink)) - { - YINFO("!!!"); - } - - - list_add(&in->hashLink,&dev->objectBucket[bucket].list); - dev->objectBucket[bucket].count++; - -} - -yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number) -{ - int bucket = yaffs_HashFunction(number); - struct list_head *i; - yaffs_Object *in; - - list_for_each(i,&dev->objectBucket[bucket].list) - { - // Look if it is in the list - in = list_entry(i, yaffs_Object,hashLink); - if(in->objectId == number) - { - return in; - } - } - - return NULL; -} - - - -yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type) -{ - - yaffs_Object *theObject; - - if(number < 0) - { - number = yaffs_CreateNewObjectNumber(dev); - } - - theObject = yaffs_AllocateEmptyObject(dev); - - if(theObject) - { - theObject->fake = 0; - theObject->renameAllowed = 1; - theObject->unlinkAllowed = 1; - theObject->objectId = number; - theObject->myDev = dev; - yaffs_HashObject(theObject); - theObject->variantType = type; - theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME; - - switch(type) - { - case YAFFS_OBJECT_TYPE_FILE: - theObject->variant.fileVariant.fileSize = 0; - theObject->variant.fileVariant.scannedFileSize = 0; - theObject->variant.fileVariant.topLevel = 0; - theObject->variant.fileVariant.top = yaffs_GetTnode(dev); - break; - case YAFFS_OBJECT_TYPE_DIRECTORY: - INIT_LIST_HEAD(&theObject->variant.directoryVariant.children); - break; - case YAFFS_OBJECT_TYPE_SYMLINK: - // No action required - break; - case YAFFS_OBJECT_TYPE_HARDLINK: - // No action required - break; - case YAFFS_OBJECT_TYPE_SPECIAL: - // No action required - break; - case YAFFS_OBJECT_TYPE_UNKNOWN: - // todo this should not happen - break; - } - } - - return theObject; -} - -yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type) -{ - yaffs_Object *theObject = NULL; - - if(number > 0) - { - theObject = yaffs_FindObjectByNumber(dev,number); - } - - if(!theObject) - { - theObject = yaffs_CreateNewObject(dev,number,type); - } - - return theObject; - -} - -char *yaffs_CloneString(const char *str) -{ - char *newStr = NULL; - - if(str && *str) - { - newStr = YMALLOC(strlen(str) + 1); - strcpy(newStr,str); - } - - return newStr; - -} - -// -// Mknod (create) a new object. -// equivalentObject only has meaning for a hard link; -// aliasString only has meaning for a sumlink. -// rdev only has meaning for devices (a subset of special objects) -yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type, - yaffs_Object *parent, - const char *name, - __u32 mode, - __u32 uid, - __u32 gid, - yaffs_Object *equivalentObject, - const char *aliasString, - __u32 rdev) -{ - yaffs_Object *in; - - yaffs_Device *dev = parent->myDev; - - // Check if the entry exists. If it does then fail the call since we don't want a dup. - if(yaffs_FindObjectByName(parent,name)) - { - return NULL; - } - - in = yaffs_CreateNewObject(dev,-1,type); - - if(in) - { - in->chunkId = -1; - in->valid = 1; - in->variantType = type; - - in->st_mode = mode; - in->st_rdev = rdev; - in->st_uid = uid; - in->st_gid = gid; - in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME; - - in->nDataChunks = 0; - - in->sum = yaffs_CalcNameSum(name); - in->dirty = 1; - - yaffs_AddObjectToDirectory(parent,in); - - in->myDev = parent->myDev; - - - switch(type) - { - case YAFFS_OBJECT_TYPE_SYMLINK: - in->variant.symLinkVariant.alias = yaffs_CloneString(aliasString); - break; - case YAFFS_OBJECT_TYPE_HARDLINK: - in->variant.hardLinkVariant.equivalentObject = equivalentObject; - in->variant.hardLinkVariant.equivalentObjectId = equivalentObject->objectId; - list_add(&in->hardLinks,&equivalentObject->hardLinks); - break; - case YAFFS_OBJECT_TYPE_FILE: // do nothing - case YAFFS_OBJECT_TYPE_DIRECTORY: // do nothing - case YAFFS_OBJECT_TYPE_SPECIAL: // do nothing - case YAFFS_OBJECT_TYPE_UNKNOWN: - break; - } - - if(yaffs_UpdateObjectHeader(in,name,0) < 0) - { - // Could not create the object header, fail the creation - yaffs_UnlinkWorker(in); - in = NULL; - } - - } - - return in; -} - -yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid) -{ - return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE,parent,name,mode,uid,gid,NULL,NULL,0); -} - -yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid) -{ - return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,0); -} - -yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev) -{ - return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,rdev); -} - -yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,const char *alias) -{ - return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK,parent,name,mode,uid,gid,NULL,alias,0); -} - -// NB yaffs_Link returns the object id of the equivalent object. -yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject) -{ - // Get the real object in case we were fed a hard link as an equivalent object - equivalentObject = yaffs_GetEquivalentObject(equivalentObject); - - if(yaffs_MknodObject(YAFFS_OBJECT_TYPE_HARDLINK,parent,name,0,0,0,equivalentObject,NULL,0)) - { - return equivalentObject; - } - else - { - return NULL; - } - -} - - -static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName) -{ - //yaffs_Device *dev = obj->myDev; - - if(newDir == NULL) - { - newDir = obj->parent; // use the old directory - } - - // Only proceed if the new name does not exist and - // if we're putting it into a directory. - if(!yaffs_FindObjectByName(newDir,newName) && - newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) - { - obj->sum = yaffs_CalcNameSum(newName); - obj->dirty = 1; - yaffs_AddObjectToDirectory(newDir,obj); - - if(yaffs_UpdateObjectHeader(obj,newName,0) >= 0) - { - return YAFFS_OK; - } - } - - return YAFFS_FAIL; -} - -int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName) -{ - yaffs_Object *obj; - - obj = yaffs_FindObjectByName(oldDir,oldName); - if(obj && obj->renameAllowed) - { - return yaffs_ChangeObjectName(obj,newDir,newName); - } - return YAFFS_FAIL; -} - - - -static int yaffs_CheckObjectHashSanity(yaffs_Device *dev) -{ - // Scan the buckets and check that the lists - // have as many members as the count says there are - int bucket; - int countEm; - struct list_head *j; - int ok = YAFFS_OK; - - for(bucket = 0; bucket < YAFFS_NOBJECT_BUCKETS; bucket++) - { - countEm = 0; - - list_for_each(j,&dev->objectBucket[bucket].list) - { - countEm++; - } - - if(countEm != dev->objectBucket[bucket].count) - { - YALERT("Inode hash inconsistency"); - ok = YAFFS_FAIL; - } - } - - return ok; -} - -void yaffs_ObjectTest(yaffs_Device *dev) -{ - yaffs_Object *in[1000]; - int inNo[1000]; - yaffs_Object *inold[1000]; - int i; - int j; - - memset(in,0,1000*sizeof(yaffs_Object *)); - memset(inold,0,1000*sizeof(yaffs_Object *)); - - yaffs_CheckObjectHashSanity(dev); - - for(j = 0; j < 10; j++) - { - //T(("%d\n",j)); - - for(i = 0; i < 1000; i++) - { - in[i] = yaffs_CreateNewObject(dev,-1,YAFFS_OBJECT_TYPE_FILE); - if(!in[i]) - { - YINFO("No more inodes"); - } - else - { - inNo[i] = in[i]->objectId; - } - } - - for(i = 0; i < 1000; i++) - { - if(yaffs_FindObjectByNumber(dev,inNo[i]) != in[i]) - { - //T(("Differnce in look up test\n")); - } - else - { - // T(("Look up ok\n")); - } - } - - yaffs_CheckObjectHashSanity(dev); - - for(i = 0; i < 1000; i+=3) - { - yaffs_FreeObject(in[i]); - in[i] = NULL; - } - - - yaffs_CheckObjectHashSanity(dev); - } - -} - - - -/////////////////////////// Block Management and Page Allocation /////////////////// - - -static void yaffs_InitialiseBlocks(yaffs_Device *dev) -{ - //Todo we're assuming the malloc will pass. - dev->blockInfo = YMALLOC(dev->nBlocks * sizeof(yaffs_BlockInfo)); - memset(dev->blockInfo,0,dev->nBlocks * sizeof(yaffs_BlockInfo)); - dev->allocationBlock = -1; // force it to get a new one -} - -static void yaffs_DeinitialiseBlocks(yaffs_Device *dev) -{ - YFREE(dev->blockInfo); -} - -// FindDiretiestBlock is used to select the dirtiest block (or close enough) -// for garbage collection. - -static int yaffs_FindDirtiestBlock(yaffs_Device *dev) -{ - - int b = dev->currentDirtyChecker; - - int i; - int dirtiest = -1; - int pagesInUse = 100; // silly big number - - for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++) - { - b++; - if (b > dev->endBlock) - { - b = dev->startBlock; - } - - if(dev->blockInfo[b].blockState == YAFFS_BLOCK_STATE_FULL && - (dev->blockInfo)[b].pagesInUse < pagesInUse) - { - dirtiest = b; - pagesInUse = (dev->blockInfo)[b].pagesInUse; - } - } - - dev->currentDirtyChecker = b; - - return dirtiest; -} - - -static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo) -{ - yaffs_BlockInfo *bi = &dev->blockInfo[blockNo]; - - int erasedOk = 0; - - // If the block is still healthy erase it and mark as clean. - // If the block has had a data failure, then retire it. - bi->blockState = YAFFS_BLOCK_STATE_DIRTY; - - if(!bi->needsRetiring) - { - erasedOk = yaffs_EraseBlockInNAND(dev,blockNo); - if(!erasedOk) - { - T((TSTR("**>> Erasure failed %d" TENDSTR),blockNo)); - } - } - - if( erasedOk ) - { - bi->blockState = YAFFS_BLOCK_STATE_EMPTY; - dev->nErasedBlocks++; - bi->pagesInUse = 0; - bi->pageBits = 0; - - T((TSTR("Erased block %d" TENDSTR),blockNo)); - } - else - { - yaffs_RetireBlock(dev,blockNo); - T((TSTR("**>> Block %d retired" TENDSTR),blockNo)); - } -} - - -static int yaffs_FindBlockForAllocation(yaffs_Device *dev) -{ - int i; - - if(dev->nErasedBlocks < 1) - { - // Hoosterman we've got a problem. - // Can't get space to gc - return -1; - } - - // Find an empty block. - - for(i = dev->startBlock; i <= dev->endBlock; i++) - { - - if(dev->blockInfo[i].blockState == YAFFS_BLOCK_STATE_EMPTY) - { - dev->blockInfo[i].blockState = YAFFS_BLOCK_STATE_ALLOCATING; - dev->nErasedBlocks--; - if(dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS) - { - dev->garbageCollectionRequired = 1; - } - - return i; - } - } - - return -1; -} - - - -static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve) -{ - int retVal; - - if(dev->allocationBlock < 0) - { - // Get next block to allocate off - dev->allocationBlock = yaffs_FindBlockForAllocation(dev); - dev->allocationPage = 0; - } - - if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS) - { - // Not enough space to allocate unless we're allowed to use the reserve. - return -1; - } - - // Next page please.... - if(dev->allocationBlock >= 0) - { - retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) + - dev->allocationPage; - dev->blockInfo[dev->allocationBlock].pagesInUse++; - dev->blockInfo[dev->allocationBlock].pageBits |= - (1 << (dev->allocationPage)); - - dev->allocationPage++; - - dev->nFreeChunks--; - - // If the block is full set the state to full - if(dev->allocationPage >= YAFFS_CHUNKS_PER_BLOCK) - { - dev->blockInfo[dev->allocationBlock].blockState = YAFFS_BLOCK_STATE_FULL; - dev->allocationBlock = -1; - } - - - return retVal; - - } - T((TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR))); - - return -1; -} - - -int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block) -{ - int oldChunk; - int newChunk; - __u32 mask; - - - yaffs_Spare spare; - yaffs_Tags tags; - __u8 buffer[YAFFS_BYTES_PER_CHUNK]; - - yaffs_BlockInfo *bi = &dev->blockInfo[block]; - - yaffs_Object *object; - - //T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits)); - - for(mask = 1,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK; - mask && bi->pageBits; - mask <<= 1, oldChunk++ ) - { - if(bi->pageBits & mask) - { - - // This page is in use and needs to be copied off - - dev->nGCCopies++; - - //T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk)); - - yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare,1); - - yaffs_GetTagsFromSpare(&spare,&tags); - tags.serialNumber++; - yaffs_LoadTagsIntoSpare(&spare,&tags); - -#if 0 - newChunk = yaffs_AllocatePage(dev,1); - if(newChunk < 0) - { - return YAFFS_FAIL; - } - - yaffs_WriteChunkToNAND(dev,newChunk, buffer, &spare); - -#else - newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1); -#endif - if(newChunk < 0) - { - return YAFFS_FAIL; - } - - object = yaffs_FindObjectByNumber(dev,tags.objectId); - - // Ok, now fix up the Tnodes etc. - - if(tags.chunkId == 0) - { - // It's a header - object->chunkId = newChunk; - } - else - { - // It's a data chunk - yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0); - - } - - yaffs_DeleteChunk(dev,oldChunk); - - } - } - - return YAFFS_OK; -} - -int yaffs_CheckGarbageCollection(yaffs_Device *dev) -{ - int block; - - if(dev->garbageCollectionRequired) - { - dev->garbageCollectionRequired = 0; - if(dev->blockSelectedForGC >= 0) - { - block = dev->blockSelectedForGC; - } - else - { - block = yaffs_FindDirtiestBlock(dev); - } - - if(block >= 0) - { - return yaffs_GarbageCollectBlock(dev,block); - } - else - { - return YAFFS_FAIL; - } - } - - return YAFFS_OK; -} - - -//////////////////////////// TAGS /////////////////////////////////////// - -static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr) -{ - yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr; - - yaffs_CalcTagsECC(tagsPtr); - - sparePtr->tagByte0 = tu->asBytes[0]; - sparePtr->tagByte1 = tu->asBytes[1]; - sparePtr->tagByte2 = tu->asBytes[2]; - sparePtr->tagByte3 = tu->asBytes[3]; - sparePtr->tagByte4 = tu->asBytes[4]; - sparePtr->tagByte5 = tu->asBytes[5]; - sparePtr->tagByte6 = tu->asBytes[6]; - sparePtr->tagByte7 = tu->asBytes[7]; -} - -static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr) -{ - yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr; - - tu->asBytes[0]= sparePtr->tagByte0; - tu->asBytes[1]= sparePtr->tagByte1; - tu->asBytes[2]= sparePtr->tagByte2; - tu->asBytes[3]= sparePtr->tagByte3; - tu->asBytes[4]= sparePtr->tagByte4; - tu->asBytes[5]= sparePtr->tagByte5; - tu->asBytes[6]= sparePtr->tagByte6; - tu->asBytes[7]= sparePtr->tagByte7; - - yaffs_CheckECCOnTags(tagsPtr); -} - -static void yaffs_SpareInitialise(yaffs_Spare *spare) -{ - memset(spare,0xFF,sizeof(yaffs_Spare)); -} - -static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted) -{ - if(tags) - { - yaffs_Spare spare; - if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,NULL,&spare,1) == YAFFS_OK) - { - *chunkDeleted = (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0; - yaffs_GetTagsFromSpare(&spare,tags); - return YAFFS_OK; - } - else - { - return YAFFS_FAIL; - } - } - - return YAFFS_OK; -} - -#if 0 -static int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_Tags *tags) -{ - // NB There must be tags, data is optional - // If there is data, then an ECC is calculated on it. - - yaffs_Spare spare; - - if(!tags) - { - return YAFFS_FAIL; - } - - yaffs_SpareInitialise(&spare); - - - if(buffer) - { - yaffs_CalcECC(buffer,&spare); - } - - yaffs_LoadTagsIntoSpare(&spare,tags); - - return yaffs_WriteChunkToNAND(dev,chunkInNAND,buffer,&spare); - -} -#endif - - -static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_Tags *tags, int useReserve) -{ - // NB There must be tags, data is optional - // If there is data, then an ECC is calculated on it. - - yaffs_Spare spare; - - if(!tags) - { - return YAFFS_FAIL; - } - - yaffs_SpareInitialise(&spare); - - - if(buffer) - { - yaffs_CalcECC(buffer,&spare); - } - - yaffs_LoadTagsIntoSpare(&spare,tags); - - return yaffs_WriteNewChunkToNAND(dev,buffer,&spare,useReserve); - -} - -static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted) -{ - return ( tags->chunkId == chunkInObject && - tags->objectId == objectId && - !chunkDeleted) ? 1 : 0; - -} - - - -int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags) -{ - //Get the Tnode, then get the level 0 offset chunk offset - yaffs_Tnode *tn; - int theChunk = -1; - yaffs_Tags localTags; - int i; - int found = 0; - int chunkDeleted; - - yaffs_Device *dev = in->myDev; - - - if(!tags) - { - // Passed a NULL, so use our own tags space - tags = &localTags; - } - - tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode); - - if(tn) - { - theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits; - - // Now we need to do the shifting etc and search for it - for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++) - { - yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted); - if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted)) - { - // found it; - found = 1; - } - else - { - theChunk++; - } - } - } - return found ? theChunk : -1; -} - -int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags) -{ - //Get the Tnode, then get the level 0 offset chunk offset - yaffs_Tnode *tn; - int theChunk = -1; - yaffs_Tags localTags; - int i; - int found = 0; - yaffs_Device *dev = in->myDev; - int chunkDeleted; - - if(!tags) - { - // Passed a NULL, so use our own tags space - tags = &localTags; - } - - tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode); - - if(tn) - { - - theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits; - - // Now we need to do the shifting etc and search for it - for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++) - { - yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted); - if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted)) - { - // found it; - found = 1; - } - else - { - theChunk++; - } - } - - // Delete the entry in the filestructure - if(found) - { - tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = 0; - } - } - else - { - //T(("No level 0 found for %d\n", chunkInInode)); - } - - if(!found) - { - //T(("Could not find %d to delete\n",chunkInInode)); - } - return found ? theChunk : -1; -} - - -#if YAFFS_PARANOID - -static int yaffs_CheckFileSanity(yaffs_Object *in) -{ - int chunk; - int nChunks; - int fSize; - int failed = 0; - int objId; - yaffs_Tnode *tn; - yaffs_Tags localTags; - yaffs_Tags *tags = &localTags; - int theChunk; - int chunkDeleted; - - - if(in->variantType != YAFFS_OBJECT_TYPE_FILE) - { - //T(("Object not a file\n")); - return YAFFS_FAIL; - } - - objId = in->objectId; - fSize = in->variant.fileVariant.fileSize; - nChunks = (fSize + YAFFS_BYTES_PER_CHUNK -1)/YAFFS_BYTES_PER_CHUNK; - - for(chunk = 1; chunk <= nChunks; chunk++) - { - tn = yaffs_FindLevel0Tnode(in->myDev,&in->variant.fileVariant, chunk); - - if(tn) - { - - theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << in->myDev->chunkGroupBits; - - - yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,tags,&chunkDeleted); - if(yaffs_TagsMatch(tags,in->objectId,chunk,chunkDeleted)) - { - // found it; - - } - else - { - //T(("File problem file [%d,%d] NAND %d tags[%d,%d]\n", - // objId,chunk,theChunk,tags->chunkId,tags->objectId); - - failed = 1; - - } - - } - else - { - //T(("No level 0 found for %d\n", chunk)); - } - } - - return failed ? YAFFS_FAIL : YAFFS_OK; -} - -#endif - -static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan) -{ - yaffs_Tnode *tn; - yaffs_Device *dev = in->myDev; - int existingChunk; - yaffs_Tags existingTags; - yaffs_Tags newTags; - unsigned existingSerial, newSerial; - - int newChunkDeleted; - - - tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode); - if(!tn) - { - return YAFFS_FAIL; - } - - existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK]; - - if(inScan) - { - // If we're scanning then we need to test for duplicates - // NB This does not need to be efficient since it should only ever - // happen when the power fails during a write, then only one - // chunk should ever be affected. - - - if(existingChunk != 0) - { - // NB Right now existing chunk will not be real chunkId if the device >= 32MB - // thus we have to do a FindChunkInFile to get the real chunk id. - // - // We have a duplicate now we need to decide which one to use - // To do this we get both sets of tags and compare serial numbers. - yaffs_ReadChunkTagsFromNAND(dev,chunkInNAND, &newTags,&newChunkDeleted); - - - // Do a proper find - existingChunk = yaffs_FindChunkInFile(in,chunkInInode, &existingTags); - - if(existingChunk <=0) - { - //Hoosterman - how did this happen? - // todo - } - - - // NB The deleted flags should be false, otherwise the chunks will - // not be loaded during a scan - - newSerial = newTags.serialNumber; - existingSerial = existingTags.serialNumber; - - if( existingChunk <= 0 || - ((existingSerial+1) & 3) == newSerial) - { - // Use new - // Delete the old one and drop through to update the tnode - yaffs_DeleteChunk(dev,existingChunk); - } - else - { - // Use existing. - // Delete the new one and return early so that the tnode isn't changed - yaffs_DeleteChunk(dev,chunkInNAND); - return YAFFS_OK; - } - } - - } - - if(existingChunk == 0) - { - in->nDataChunks++; - } - - tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = (chunkInNAND >> dev->chunkGroupBits); - - return YAFFS_OK; -} - - - -int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer) -{ - int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL); - - if(chunkInNAND >= 0) - { - return yaffs_ReadChunkFromNAND(in->myDev,chunkInNAND,buffer,NULL,1); - } - else - { - return 0; - } - -} - - -static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId) -{ - int block; - int page; - yaffs_Spare spare; - - if(chunkId <= 0) return; - - block = chunkId / YAFFS_CHUNKS_PER_BLOCK; - page = chunkId % YAFFS_CHUNKS_PER_BLOCK; - yaffs_SpareInitialise(&spare); - - spare.pageStatus = 0; // To mark it as deleted. - - - yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare); - yaffs_HandleUpdateChunk(dev,chunkId,&spare); - - - // Pull out of the management area. - // If the whole block became dirty, this will kick off an erasure. - if( dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_ALLOCATING || - dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL) - { - dev->nFreeChunks++; - - dev->blockInfo[block].pageBits &= ~(1 << page); - dev->blockInfo[block].pagesInUse--; - - if( dev->blockInfo[block].pagesInUse == 0 && - dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL) - { - yaffs_BlockBecameDirty(dev,block); - } - - } - else - { - // T(("Bad news deleting chunk %d\n",chunkId)); - } - -} - - - - -int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *buffer,int nBytes,int useReserve) -{ - // Find old chunk Need to do this to get serial number - // Write new one and patch into tree. - // Invalidate old tags. - - int prevChunkId; - yaffs_Tags prevTags; - - int newChunkId; - yaffs_Tags newTags; - - yaffs_Device *dev = in->myDev; - - yaffs_CheckGarbageCollection(dev); - - // Get the previous chunk at this location in the file if it exists - prevChunkId = yaffs_FindChunkInFile(in,chunkInInode,&prevTags); - - // Set up new tags - newTags.chunkId = chunkInInode; - newTags.objectId = in->objectId; - newTags.serialNumber = (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1; - newTags.byteCount = nBytes; - newTags.unusedStuff = 0xFFFFFFFF; - - yaffs_CalcTagsECC(&newTags); - - - #if 0 - // Create new chunk in NAND - newChunkId = yaffs_AllocatePage(dev,useReserve); - - - if(newChunkId >= 0) - { - - - yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,buffer,&newTags); - - yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId); - - - if(prevChunkId >= 0) - { - yaffs_DeleteChunk(dev,prevChunkId); - - } - - yaffs_CheckFileSanity(in); - - return newChunkId; - } - - - return -1; -#else - - newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve); - if(newChunkId >= 0) - { - yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0); - - - if(prevChunkId >= 0) - { - yaffs_DeleteChunk(dev,prevChunkId); - - } - - yaffs_CheckFileSanity(in); - } - return newChunkId; - -#endif - - - -} - - -// UpdateObjectHeader updates the header on NAND for an object. -// If name is not NULL, then that new name is used. -// -int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force) -{ - - yaffs_Device *dev = in->myDev; - - int prevChunkId; - - int newChunkId; - yaffs_Tags newTags; - __u8 bufferNew[YAFFS_BYTES_PER_CHUNK]; - __u8 bufferOld[YAFFS_BYTES_PER_CHUNK]; - - yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew; - yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld; - - if(!in->fake || force) - { - - yaffs_CheckGarbageCollection(dev); - - memset(bufferNew,0xFF,YAFFS_BYTES_PER_CHUNK); - - prevChunkId = in->chunkId; - - if(prevChunkId >= 0) - { - yaffs_ReadChunkFromNAND(dev,prevChunkId,bufferOld,NULL,1); - } - - // Header data - oh->type = in->variantType; - - oh->st_mode = in->st_mode; - oh->st_uid = in->st_uid; - oh->st_gid = in->st_gid; - oh->st_atime = in->st_atime; - oh->st_mtime = in->st_mtime; - oh->st_ctime = in->st_ctime; - oh->st_rdev = in->st_rdev; - - if(in->parent) - { - oh->parentObjectId = in->parent->objectId; - } - else - { - oh->parentObjectId = 0; - } - - oh->sum = in->sum; - if(name && *name) - { - memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1); - strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH); - } - else if(prevChunkId) - { - memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1); - } - else - { - memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1); - } - - switch(in->variantType) - { - case YAFFS_OBJECT_TYPE_UNKNOWN: - // Should not happen - break; - case YAFFS_OBJECT_TYPE_FILE: - oh->fileSize = in->variant.fileVariant.fileSize; - break; - case YAFFS_OBJECT_TYPE_HARDLINK: - oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId; - break; - case YAFFS_OBJECT_TYPE_SPECIAL: - // Do nothing - break; - case YAFFS_OBJECT_TYPE_DIRECTORY: - // Do nothing - break; - case YAFFS_OBJECT_TYPE_SYMLINK: - strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH); - oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; - break; - } - - // Tags - in->serial++; - newTags.chunkId = 0; - newTags.objectId = in->objectId; - newTags.serialNumber = in->serial; - newTags.byteCount = 0xFFFFFFFF; - newTags.unusedStuff = 0xFFFFFFFF; - - yaffs_CalcTagsECC(&newTags); - - - -#if 0 - // Create new chunk in NAND - newChunkId = yaffs_AllocatePage(dev,1); - - if(newChunkId >= 0) - { - - yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,bufferNew,&newTags); - - in->chunkId = newChunkId; - - if(prevChunkId >= 0) - { - yaffs_DeleteChunk(dev,prevChunkId); - } - - in->dirty = 0; - return newChunkId; - } - - return -1; -#else - // Create new chunk in NAND - newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1); - - if(newChunkId >= 0) - { - - in->chunkId = newChunkId; - - if(prevChunkId >= 0) - { - yaffs_DeleteChunk(dev,prevChunkId); - } - - in->dirty = 0; - } - - return newChunkId; - -#endif - } - return 0; -} - - - -///////////////////////// File read/write /////////////////////////////// -// Read and write have very similar structures. -// In general the read/write has three parts to it -// * An incomplete chunk to start with (if the read/write is not chunk-aligned) -// * Some complete chunks -// * An incomplete chunk to end off with -// -// Curve-balls: the first chunk might also be the last chunk. - -int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes) -{ - -// yaffs_Device *dev = in->myDev; - - __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; - - int chunk; - int start; - int nToCopy; - int n = nBytes; - int nDone = 0; - - while(n > 0) - { - chunk = offset / YAFFS_BYTES_PER_CHUNK + 1; // The first chunk is 1 - start = offset % YAFFS_BYTES_PER_CHUNK; - - // OK now check for the curveball where the start and end are in - // the same chunk. - if( (start + n) < YAFFS_BYTES_PER_CHUNK) - { - nToCopy = n; - } - else - { - nToCopy = YAFFS_BYTES_PER_CHUNK - start; - } - - if(nToCopy != YAFFS_BYTES_PER_CHUNK) - { - // An incomplete start or end chunk (or maybe both start and end chunk) - // Read into the local buffer then copy... - - yaffs_ReadChunkDataFromObject(in,chunk,localBuffer); - memcpy(buffer,&localBuffer[start],nToCopy); - } - else - { - // A full chunk. Read directly into the supplied buffer. - yaffs_ReadChunkDataFromObject(in,chunk,buffer); - } - - n -= nToCopy; - offset += nToCopy; - buffer += nToCopy; - nDone += nToCopy; - - } - - return nDone; -} - - -int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes) -{ - __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; - - int chunk; - int start; - int nToCopy; - int n = nBytes; - int nDone = 0; - int nToWriteBack; - int endOfWrite = offset+nBytes; - int chunkWritten = 0; - - while(n > 0 && chunkWritten >= 0) - { - chunk = offset / YAFFS_BYTES_PER_CHUNK + 1; - start = offset % YAFFS_BYTES_PER_CHUNK; - - - // OK now check for the curveball where the start and end are in - // the same chunk. - if( (start + n) < YAFFS_BYTES_PER_CHUNK) - { - nToCopy = n; - nToWriteBack = (start + n); - } - else - { - nToCopy = YAFFS_BYTES_PER_CHUNK - start; - nToWriteBack = YAFFS_BYTES_PER_CHUNK; - } - - if(nToCopy != YAFFS_BYTES_PER_CHUNK) - { - // An incomplete start or end chunk (or maybe both start and end chunk) - // Read into the local buffer then copy, then copy over and write back. - - yaffs_ReadChunkDataFromObject(in,chunk,localBuffer); - - memcpy(&localBuffer[start],buffer,nToCopy); - - chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0); - - //T(("Write with readback to chunk %d %d\n",chunk,chunkWritten)); - - } - else - { - // A full chunk. Write directly from the supplied buffer. - chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0); - //T(("Write to chunk %d %d\n",chunk,chunkWritten)); - } - - if(chunkWritten >= 0) - { - n -= nToCopy; - offset += nToCopy; - buffer += nToCopy; - nDone += nToCopy; - } - - } - - // Update file object - - if(endOfWrite > in->variant.fileVariant.fileSize) - { - in->variant.fileVariant.fileSize = endOfWrite; - } - - in->dirty = 1; - /*in->st_mtime = CURRENT_TIME; only update in flush*/ - - return nDone; -} - - -int yaffs_ResizeFile(yaffs_Object *in, int newSize) -{ - int i; - int chunkId; - int oldFileSize = in->variant.fileVariant.fileSize; - int sizeOfPartialChunk = newSize % YAFFS_BYTES_PER_CHUNK; - - yaffs_Device *dev = in->myDev; - - __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; - - if(in->variantType != YAFFS_OBJECT_TYPE_FILE) - { - return yaffs_GetFileSize(in); - } - - if(newSize < oldFileSize) - { - - int lastDel = 1 + (oldFileSize-1)/YAFFS_BYTES_PER_CHUNK; - - int startDel = 1 + (newSize + YAFFS_BYTES_PER_CHUNK - 1)/ - YAFFS_BYTES_PER_CHUNK; - - // Delete backwards so that we don't end up with holes if - // power is lost part-way through the operation. - for(i = lastDel; i >= startDel; i--) - { - // NB this could be optimised somewhat, - // eg. could retrieve the tags and write them without - // using yaffs_DeleteChunk - - chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL); - if(chunkId <= 0 || chunkId >= (dev->endBlock * 32)) - { - //T(("Found daft chunkId %d for %d\n",chunkId,i)); - } - else - { - in->nDataChunks--; - yaffs_DeleteChunk(dev,chunkId); - } - } - - - if(sizeOfPartialChunk != 0) - { - int lastChunk = 1+ newSize/YAFFS_BYTES_PER_CHUNK; - - // Got to read and rewrite the last chunk with its new size. - yaffs_ReadChunkDataFromObject(in,lastChunk,localBuffer); - - yaffs_WriteChunkDataToObject(in,lastChunk,localBuffer,sizeOfPartialChunk,1); - - } - - in->variant.fileVariant.fileSize = newSize; - - yaffs_PruneFileStructure(dev,&in->variant.fileVariant); - - return newSize; - - } - else - { - return oldFileSize; - } -} - - -loff_t yaffs_GetFileSize(yaffs_Object *obj) -{ - obj = yaffs_GetEquivalentObject(obj); - - switch(obj->variantType) - { - case YAFFS_OBJECT_TYPE_FILE: - return obj->variant.fileVariant.fileSize; - case YAFFS_OBJECT_TYPE_SYMLINK: - return strlen(obj->variant.symLinkVariant.alias); - default: - return 0; - } -} - - - -// yaffs_FlushFile() updates the file's -// objectId in NAND - -int yaffs_FlushFile(yaffs_Object *in) -{ - int retVal; - if(in->dirty) - { - //T(("flushing object header\n")); - - in->st_mtime = CURRENT_TIME; - - retVal = yaffs_UpdateObjectHeader(in,NULL,0); - } - else - { - retVal = YAFFS_OK; - } - - return retVal; - -} - - -static int yaffs_DoGenericObjectDeletion(yaffs_Object *in) -{ - yaffs_RemoveObjectFromDirectory(in); - yaffs_DeleteChunk(in->myDev,in->chunkId); - yaffs_FreeObject(in); - return YAFFS_OK; - -} - -// yaffs_DeleteFile deletes the whole file data -// and the inode associated with the file. -// It does not delete the links associated with the file. -static int yaffs_DeleteFile(yaffs_Object *in) -{ - // Delete the file data & tnodes -#if 0 - yaffs_ResizeFile(in,0); -#else - yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0); - -#endif - - yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top); - - return yaffs_DoGenericObjectDeletion(in); -} - -static int yaffs_DeleteDirectory(yaffs_Object *in) -{ - //First check that the directory is empty. - if(list_empty(&in->variant.directoryVariant.children)) - { - return yaffs_DoGenericObjectDeletion(in); - } - - return YAFFS_FAIL; - -} - -static int yaffs_DeleteSymLink(yaffs_Object *in) -{ - YFREE(in->variant.symLinkVariant.alias); - - return yaffs_DoGenericObjectDeletion(in); -} - -static int yaffs_DeleteHardLink(yaffs_Object *in) -{ - // remove this hardlink from the list assocaited with the equivalent - // object - list_del(&in->hardLinks); - return yaffs_DoGenericObjectDeletion(in); -} - - -static int yaffs_UnlinkWorker(yaffs_Object *obj) -{ - - - if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) - { - return yaffs_DeleteHardLink(obj); - } - else if(!list_empty(&obj->hardLinks)) - { -#if 0 - // Curve ball: We're unlinking an object that has a hardlink. - // Therefore we can't really delete the object. - // Instead, we do the following: - // - Select a hardlink. - // - Re-type a hardlink as the equivalent object and populate the fields, including the - // objectId. Updating the object id is important so that all the hardlinks do not need - // to be rewritten. - // - Update the equivalet object pointers. - // - Delete all object. - - yaffs_Object *hl; - struct list_head *i; - - - yaffs_RemoveObjectFromDirectory(obj); - - - - hl = list_entry(obj->hardLinks.next, yaffs_Object,hardLinks); - - hl->dirty = 1; - hl->st_mode = obj->st_mode; - hl->st_uid = obj->st_uid; - hl->st_gid = obj->st_gid; - hl->st_atime = obj->st_atime; - hl->st_mtime = obj->st_mtime; - hl->st_ctime = obj->st_ctime; - hl->st_rdev = obj->st_rdev; - - hl->variantType = obj->variantType; - - switch(hl->variantType) - { - case YAFFS_OBJECT_TYPE_FILE: - case YAFFS_OBJECT_TYPE_SYMLINK: - case YAFFS_OBJECT_TYPE_SPECIAL: - // These types are OK to just copy across. - hl->variant = obj->variant; - break; - case YAFFS_OBJECT_TYPE_DIRECTORY: - // Fix the list up - list_add(&hl->variant.directoryVariant.children, - &obj->variant.directoryVariant.children); - list_del(&obj->variant.directoryVariant.children); - - // Now change all the directory children to point to the new parent. - list_for_each(i,&hl->variant.directoryVariant.children) - { - list_entry(i,yaffs_Object,siblings)->parent = hl; - } - break; - - case YAFFS_OBJECT_TYPE_HARDLINK: - case YAFFS_OBJECT_TYPE_UNKNOWN: - // Should not be either of these types. - } - - // Now fix up the hardlink chain - list_del(&obj->hardLinks); - - list_for_each(i,&hl->hardLinks) - { - list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObject = hl; - list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObjectId = hl->objectId; - } - - // Now fix up the hash links. - yaffs_UnhashObject(hl); - hl->objectId = obj->objectId; - yaffs_HashObject(hl); - - // Update the hardlink which has become an object - yaffs_UpdateObjectHeader(hl,NULL,0); - - // Finally throw away the deleted object - yaffs_DeleteChunk(obj->myDev,obj->chunkId); - yaffs_FreeObject(obj); - - return YAFFS_OK; -#else - // Curve ball: We're unlinking an object that has a hardlink. - // - // This problem arises because we are not strictly following - // The Linux link/inode model. - // - // We can't really delete the object. - // Instead, we do the following: - // - Select a hardlink. - // - Unhook it from the hard links - // - Unhook it from its parent directory (so that the rename can work) - // - Rename the object to the hardlink's name. - // - Delete the hardlink - - - yaffs_Object *hl; - int retVal; - char name[YAFFS_MAX_NAME_LENGTH+1]; - - hl = list_entry(obj->hardLinks.next,yaffs_Object,hardLinks); - - list_del_init(&hl->hardLinks); - list_del_init(&hl->siblings); - - yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1); - - retVal = yaffs_ChangeObjectName(obj, hl->parent, name); - - if(retVal == YAFFS_OK) - { - retVal = yaffs_DoGenericObjectDeletion(hl); - } - return retVal; - -#endif - - - } - else - { - switch(obj->variantType) - { - case YAFFS_OBJECT_TYPE_FILE: - return yaffs_DeleteFile(obj); - break; - case YAFFS_OBJECT_TYPE_DIRECTORY: - return yaffs_DeleteDirectory(obj); - break; - case YAFFS_OBJECT_TYPE_SYMLINK: - return yaffs_DeleteSymLink(obj); - break; - case YAFFS_OBJECT_TYPE_HARDLINK: - case YAFFS_OBJECT_TYPE_UNKNOWN: - default: - return YAFFS_FAIL; - } - } -} - -int yaffs_Unlink(yaffs_Object *dir, const char *name) -{ - yaffs_Object *obj; - - obj = yaffs_FindObjectByName(dir,name); - - if(obj && obj->unlinkAllowed) - { - return yaffs_UnlinkWorker(obj); - } - - return YAFFS_FAIL; - -} - -//////////////// Initialisation Scanning ///////////////// - - - -// For now we use the SmartMedia check. -// We look at the blockStatus byte in the first two chunks -// These must be 0xFF to pass as OK. -// todo: this function needs to be modifyable foir different NAND types -// and different chunk sizes. Suggest make this into a per-device configurable -// function. -static int yaffs_IsBlockBad(yaffs_Device *dev, int blk) -{ - yaffs_Spare spare; - - yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK,NULL,&spare,1); -#if 1 - if(yaffs_CountBits(spare.blockStatus) < 7) - { - return 1; - } -#else - if(spare.blockStatus != 0xFF) - { - return 1; - } -#endif - yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK + 1,NULL,&spare,1); - -#if 1 - if(yaffs_CountBits(spare.blockStatus) < 7) - { - return 1; - } -#else - if(spare.blockStatus != 0xFF) - { - return 1; - } -#endif - - return 0; - -} - -static int yaffs_Scan(yaffs_Device *dev) -{ - yaffs_Spare spare; - yaffs_Tags tags; - int blk; - int chunk; - int c; - int deleted; - yaffs_BlockState state; - yaffs_Object *hardList = NULL; - yaffs_Object *hl; - - - - yaffs_ObjectHeader *oh; - yaffs_Object *in; - yaffs_Object *parent; - - __u8 chunkData[YAFFS_BYTES_PER_CHUNK]; - - - // Scan all the blocks... - - for(blk = dev->startBlock; blk <= dev->endBlock; blk++) - { - deleted = 0; - dev->blockInfo[blk].pageBits = 0; - dev->blockInfo[blk].pagesInUse = 0; - state = YAFFS_BLOCK_STATE_SCANNING; - - - if(yaffs_IsBlockBad(dev,blk)) - { - state = YAFFS_BLOCK_STATE_DEAD; - T((TSTR("block %d is bad" TENDSTR),blk)); - } - - // Read each chunk in the block. - - for(c = 0; c < YAFFS_CHUNKS_PER_BLOCK && - state == YAFFS_BLOCK_STATE_SCANNING; c++) - { - // Read the spare area and decide what to do - chunk = blk * YAFFS_CHUNKS_PER_BLOCK + c; - - yaffs_ReadChunkFromNAND(dev,chunk,NULL,&spare,1); - - - // This block looks ok, now what's in this chunk? - yaffs_GetTagsFromSpare(&spare,&tags); - - if(yaffs_CountBits(spare.pageStatus) < 6) - { - // A deleted chunk - deleted++; - dev->nFreeChunks ++; - //T((" %d %d deleted\n",blk,c)); - } - else if(tags.objectId == YAFFS_UNUSED_OBJECT_ID) - { - // An unassigned chunk in the block - // This means that either the block is empty or - // this is the one being allocated from - - if(c == 0) - { - // the block is unused - state = YAFFS_BLOCK_STATE_EMPTY; - dev->nErasedBlocks++; - } - else - { - // this is the block being allocated from - T((TSTR(" Allocating from %d %d" TENDSTR),blk,c)); - state = YAFFS_BLOCK_STATE_ALLOCATING; - dev->allocationBlock = blk; - dev->allocationPage = c; - } - - dev->nFreeChunks += (YAFFS_CHUNKS_PER_BLOCK - c); - } - else if(tags.chunkId > 0) - { - int endpos; - // A data chunk. - dev->blockInfo[blk].pageBits |= (1 << c); - dev->blockInfo[blk].pagesInUse++; - - in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE); - // PutChunkIntoFIle checks for a clash (two data chunks with - // the same chunkId). - yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1); - endpos = (tags.chunkId - 1)* YAFFS_BYTES_PER_CHUNK + tags.byteCount; - if(in->variant.fileVariant.scannedFileSize variant.fileVariant.scannedFileSize = endpos; -#ifndef CONFIG_YAFFS_USE_HEADER_FILE_SIZE - in->variant.fileVariant.fileSize = - in->variant.fileVariant.scannedFileSize; -#endif - - } - //T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId)); - } - else - { - // chunkId == 0, so it is an ObjectHeader. - // Thus, we read in the object header and make the object - dev->blockInfo[blk].pageBits |= (1 << c); - dev->blockInfo[blk].pagesInUse++; - - yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL,1); - - oh = (yaffs_ObjectHeader *)chunkData; - - in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type); - - if(in->valid) - { - // We have already filled this one. We have a duplicate and need to resolve it. - - unsigned existingSerial = in->serial; - unsigned newSerial = tags.serialNumber; - - if(((existingSerial+1) & 3) == newSerial) - { - // Use new one - destroy the exisiting one - yaffs_DeleteChunk(dev,in->chunkId); - in->valid = 0; - } - else - { - // Use existing - destroy this one. - yaffs_DeleteChunk(dev,chunk); - } - } - - if(!in->valid && - (tags.objectId == YAFFS_OBJECTID_ROOT || - tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) - { - // We only load some info, don't fiddle with directory structure - in->valid = 1; - in->variantType = oh->type; - - in->st_mode = oh->st_mode; - in->st_uid = oh->st_uid; - in->st_gid = oh->st_gid; - in->st_atime = oh->st_atime; - in->st_mtime = oh->st_mtime; - in->st_ctime = oh->st_ctime; - in->st_rdev = oh->st_rdev; - in->chunkId = chunk; - - } - else if(!in->valid) - { - // we need to load this info - - in->valid = 1; - in->variantType = oh->type; - - in->st_mode = oh->st_mode; - in->st_uid = oh->st_uid; - in->st_gid = oh->st_gid; - in->st_atime = oh->st_atime; - in->st_mtime = oh->st_mtime; - in->st_ctime = oh->st_ctime; - in->st_rdev = oh->st_rdev; - in->chunkId = chunk; - - in->sum = oh->sum; - in->dirty = 0; - - // directory stuff... - // hook up to parent - - parent = yaffs_FindOrCreateObjectByNumber(dev,oh->parentObjectId,YAFFS_OBJECT_TYPE_DIRECTORY); - if(parent->variantType == YAFFS_OBJECT_TYPE_UNKNOWN) - { - // Set up as a directory - parent->variantType = YAFFS_OBJECT_TYPE_DIRECTORY; - INIT_LIST_HEAD(&parent->variant.directoryVariant.children); - } - else if(parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) - { - // Hoosterman, another problem.... - // We're trying to use a non-directory as a directory - // Todo ... handle - } - - yaffs_AddObjectToDirectory(parent,in); - - // Note re hardlinks. - // Since we might scan a hardlink before its equivalent object is scanned - // we put them all in a list. - // After scanning is complete, we should have all the objects, so we run through this - // list and fix up all the chains. - - switch(in->variantType) - { - case YAFFS_OBJECT_TYPE_UNKNOWN: // Todo got a problem - break; - case YAFFS_OBJECT_TYPE_FILE: -#ifdef CONFIG_YAFFS_USE_HEADER_FILE_SIZE - in->variant.fileVariant.fileSize = oh->fileSize; -#endif - break; - case YAFFS_OBJECT_TYPE_HARDLINK: - in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId; - (yaffs_Object *)(in->hardLinks.next) = hardList; - hardList = in; - break; - case YAFFS_OBJECT_TYPE_DIRECTORY: // Do nothing - break; - case YAFFS_OBJECT_TYPE_SPECIAL: // Do nothing - break; - case YAFFS_OBJECT_TYPE_SYMLINK: // Do nothing - in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias); - break; - } - //T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType)); - } - } - } - - if(state == YAFFS_BLOCK_STATE_SCANNING) - { - // If we got this far while scanning, then the block is fully allocated. - state = YAFFS_BLOCK_STATE_FULL; - } - - dev->blockInfo[blk].blockState = state; - - // Now let's see if it was dirty - if( dev->blockInfo[blk].pagesInUse == 0 && - dev->blockInfo[blk].blockState == YAFFS_BLOCK_STATE_FULL) - { - yaffs_BlockBecameDirty(dev,blk); - } - - } - - // Fix up the hard link chains. - // We should now have scanned all the objects, now it's time to add these - // hardlinks. - while(hardList) - { - hl = hardList; - hardList = (yaffs_Object *)(hardList->hardLinks.next); - - in = yaffs_FindObjectByNumber(dev,hl->variant.hardLinkVariant.equivalentObjectId); - - if(in) - { - // Add the hardlink pointers - hl->variant.hardLinkVariant.equivalentObject=in; - list_add(&hl->hardLinks,&in->hardLinks); - } - else - { - //Todo Need to report/handle this better. - // Got a problem... hardlink to a non-existant object - hl->variant.hardLinkVariant.equivalentObject=NULL; - INIT_LIST_HEAD(&hl->hardLinks); - - } - - } - - - - return YAFFS_OK; -} - - -////////////////////////// Directory Functions ///////////////////////// - - -static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj) -{ - - if(obj->siblings.prev == NULL) - { - // Not initialised - INIT_LIST_HEAD(&obj->siblings); - - } - else if(!list_empty(&obj->siblings)) - { - // If it is holed up somewhere else, un hook it - list_del_init(&obj->siblings); - } - // Now add it - list_add(&obj->siblings,&directory->variant.directoryVariant.children); - obj->parent = directory; -} - -static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj) -{ - list_del_init(&obj->siblings); - obj->parent = NULL; -} - -yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory,const char *name) -{ - int sum; - - struct list_head *i; - char buffer[YAFFS_MAX_NAME_LENGTH+1]; - - yaffs_Object *l; - - sum = yaffs_CalcNameSum(name); - - list_for_each(i,&directory->variant.directoryVariant.children) - { - l = list_entry(i, yaffs_Object,siblings); - - // Special case for lost-n-found - if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND) - { - if(yaffs_strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0) - { - return l; - } - } - else if(yaffs_SumCompare(l->sum, sum)) - { - // Do a real check - yaffs_GetObjectName(l,buffer,YAFFS_MAX_NAME_LENGTH); - if(yaffs_strcmp(name,buffer) == 0) - { - return l; - } - - - } - } - - return NULL; -} - - -int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *)) -{ - struct list_head *i; - yaffs_Object *l; - - - list_for_each(i,&theDir->variant.directoryVariant.children) - { - l = list_entry(i, yaffs_Object,siblings); - if(!fn(l)) - { - return YAFFS_FAIL; - } - } - - return YAFFS_OK; - -} - - -// GetEquivalentObject dereferences any hard links to get to the -// actual object. - -yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj) -{ - if(obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) - { - // We want the object id of the equivalent object, not this one - obj = obj->variant.hardLinkVariant.equivalentObject; - } - return obj; - -} - -int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize) -{ - memset(name,0,buffSize); - - if(obj->objectId == YAFFS_OBJECTID_LOSTNFOUND) - { - strncpy(name,YAFFS_LOSTNFOUND_NAME,buffSize - 1); - } - else if(obj->chunkId <= 0) - { - char locName[20]; - // make up a name - sprintf(locName,"%s%d",YAFFS_LOSTNFOUND_PREFIX,obj->objectId); - strncpy(name,locName,buffSize - 1); - - } - else - { - __u8 buffer[YAFFS_BYTES_PER_CHUNK]; - yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer; - - memset(buffer,0,YAFFS_BYTES_PER_CHUNK); - - if(obj->chunkId >= 0) - { - yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL,1); - } - strncpy(name,oh->name,buffSize - 1); - } - - return strlen(name); -} - -int yaffs_GetObjectFileLength(yaffs_Object *obj) -{ - - // Dereference any hard linking - obj = yaffs_GetEquivalentObject(obj); - - if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) - { - return obj->variant.fileVariant.fileSize; - } - if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) - { - return strlen(obj->variant.symLinkVariant.alias); - } - else - { - // Only a directory should drop through to here - return YAFFS_BYTES_PER_CHUNK; - } -} - -int yaffs_GetObjectLinkCount(yaffs_Object *obj) -{ - int count = 1; // the object itself - struct list_head *i; - - list_for_each(i,&obj->hardLinks) - { - count++; - } - return count; - -} - - -int yaffs_GetObjectInode(yaffs_Object *obj) -{ - obj = yaffs_GetEquivalentObject(obj); - - return obj->objectId; -} - -unsigned yaffs_GetObjectType(yaffs_Object *obj) -{ - obj = yaffs_GetEquivalentObject(obj); - - switch(obj->variantType) - { - case YAFFS_OBJECT_TYPE_FILE: return DT_REG; break; - case YAFFS_OBJECT_TYPE_DIRECTORY: return DT_DIR; break; - case YAFFS_OBJECT_TYPE_SYMLINK: return DT_LNK; break; - case YAFFS_OBJECT_TYPE_HARDLINK: return DT_REG; break; - case YAFFS_OBJECT_TYPE_SPECIAL: - if(S_ISFIFO(obj->st_mode)) return DT_FIFO; - if(S_ISCHR(obj->st_mode)) return DT_CHR; - if(S_ISBLK(obj->st_mode)) return DT_BLK; - if(S_ISSOCK(obj->st_mode)) return DT_SOCK; - default: return DT_REG; break; - } -} - -char *yaffs_GetSymlinkAlias(yaffs_Object *obj) -{ - obj = yaffs_GetEquivalentObject(obj); - if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) - { - return yaffs_CloneString(obj->variant.symLinkVariant.alias); - } - else - { - return yaffs_CloneString(""); - } -} - - -int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr) -{ - unsigned int valid = attr->ia_valid; - - if(valid & ATTR_MODE) obj->st_mode = attr->ia_mode; - if(valid & ATTR_UID) obj->st_uid = attr->ia_uid; - if(valid & ATTR_GID) obj->st_gid = attr->ia_gid; - - if(valid & ATTR_ATIME) obj->st_atime = attr->ia_atime; - if(valid & ATTR_CTIME) obj->st_ctime = attr->ia_ctime; - if(valid & ATTR_MTIME) obj->st_mtime = attr->ia_mtime; - - if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size); - - yaffs_UpdateObjectHeader(obj,NULL,1); - - return YAFFS_OK; - -} -int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr) -{ - unsigned int valid = 0; - - attr->ia_mode = obj->st_mode; valid |= ATTR_MODE; - attr->ia_uid = obj->st_uid; valid |= ATTR_UID; - attr->ia_gid = obj->st_gid; valid |= ATTR_GID; - - attr->ia_atime = obj->st_atime; valid |= ATTR_ATIME; - attr->ia_ctime = obj->st_ctime; valid |= ATTR_CTIME; - attr->ia_mtime = obj->st_mtime; valid |= ATTR_MTIME; - - attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE; - - attr->ia_valid = valid; - - return YAFFS_OK; - -} - - - -int yaffs_DumpObject(yaffs_Object *obj) -{ -// __u8 buffer[YAFFS_BYTES_PER_CHUNK]; - char name[257]; -// yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer; - -// memset(buffer,0,YAFFS_BYTES_PER_CHUNK); - -// if(obj->chunkId >= 0) -// { -// yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL); -// } - - yaffs_GetObjectName(obj,name,256); - - YPRINTF(("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n", - obj->objectId,yaffs_GetObjectInode(obj), name, obj->dirty, obj->valid, obj->serial, - obj->sum, obj->chunkId, yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj))); - -#if 0 - YPRINTF(("Object %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d\n", - obj->objectId, oh->name, obj->dirty, obj->valid, obj->serial, - obj->sum, obj->chunkId)); - switch(obj->variantType) - { - case YAFFS_OBJECT_TYPE_FILE: - YPRINTF((" FILE length %d\n",obj->variant.fileVariant.fileSize)); - break; - case YAFFS_OBJECT_TYPE_DIRECTORY: - YPRINTF((" DIRECTORY\n")); - break; - case YAFFS_OBJECT_TYPE_HARDLINK: //todo - case YAFFS_OBJECT_TYPE_SYMLINK: - case YAFFS_OBJECT_TYPE_UNKNOWN: - default: - } -#endif - - return YAFFS_OK; -} - - -///////////////////////// Initialisation code /////////////////////////// - - - -int yaffs_GutsInitialise(yaffs_Device *dev) -{ - unsigned nChunks,x; - int bits; - - - - if(!yaffs_CheckStructures()) - { - return YAFFS_FAIL; - } - - - // OK now calculate a few things for the device - // Calculate chunkGroupBits. - // If there are 64k or less chunks then this is 1 - // Else it is log2(nChunks) - 16 - // - x = nChunks = YAFFS_CHUNKS_PER_BLOCK * dev->nBlocks; - - for(bits = 0, x = nChunks; (x & 1) == 0; bits++) - { - x >>= 1; - } - - if( x != 1) - { - // Not a power of 2 - YPRINTF(("nBlocks should be a power of 2 but is %u\n", - dev->nBlocks)); - return YAFFS_FAIL; - } - - if(bits <= 16) - { - dev->chunkGroupBits = 0; - dev->chunkGroupSize = 1; - } - else - { - dev->chunkGroupBits = bits - 16; - dev->chunkGroupSize = nChunks>> 16; - } - - // More device initialisation - dev->garbageCollectionRequired = 0; - dev->currentDirtyChecker = 0; - dev->bufferedBlock = -1; - dev->doingBufferedBlockRewrite = 0; - dev->blockSelectedForGC = -1; - - yaffs_InitialiseBlocks(dev); - - yaffs_InitialiseTnodes(dev); - - yaffs_InitialiseObjects(dev); - - - // Initialise the root and lost and found directories - dev->lostNFoundDir = dev->rootDir = NULL; - dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR); - dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_ROOT_MODE | S_IFDIR); - yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir); - - // Now scan the flash. - yaffs_Scan(dev); - - // Zero out stats - dev->nPageReads = 0; - dev->nPageWrites = 0; - dev->nBlockErasures = 0; - dev->nGCCopies = 0; - dev->nRetriedWrites = 0; - dev->nRetiredBlocks = 0; - - - return YAFFS_OK; - -} - -void yaffs_Deinitialise(yaffs_Device *dev) -{ - yaffs_DeinitialiseBlocks(dev); - yaffs_DeinitialiseTnodes(dev); - yaffs_DeinitialiseObjects(dev); - -} - -int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev) -{ - int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS); - - return (nFree < 0) ? 0 : nFree; - -} - - -/////////////////// YAFFS test code ////////////////////////////////// - -#define yaffs_CheckStruct(structure,syze, name) \ - if(sizeof(structure) != syze) \ - { YPRINTF(("%s should be %d but is %d\n",name,syze,sizeof(structure))); \ - return YAFFS_FAIL; \ - } - - -static int yaffs_CheckStructures(void) -{ - yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags") - yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion") - yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare") - yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode") - yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader") - - - return YAFFS_OK; -} - -void yaffs_GutsTest(yaffs_Device *dev) -{ - - if(yaffs_CheckStructures() != YAFFS_OK) - { - YPRINTF(("One or more structures malformed-- aborting\n")); - return; - } - else - { - YPRINTF(("Structures OK\n")); - } - - yaffs_TnodeTest(dev); - yaffs_ObjectTest(dev); -} - - +/* + * YAFFS: Yet another FFS. A NAND-flash specific file system. + * yaffs_guts.c The main guts of YAFFS + * + * Copyright (C) 2002 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + //yaffs_guts.c + +#include "yportenv.h" + +#include "yaffsinterface.h" +#include "yaffs_guts.h" + + +#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2 + + +// External functions for ECC on data +void nand_calculate_ecc (const u_char *dat, u_char *ecc_code); +int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc); + + +// countBits is a quick way of counting the number of bits in a byte. +// ie. countBits[n] holds the number of 1 bits in a byte with the value n. + +static const char yaffs_countBitsTable[256] = +{ +0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4, +1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, +1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, +1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, +3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, +4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 +}; + +static int yaffs_CountBits(__u8 x) +{ + int retVal; + retVal = yaffs_countBitsTable[x]; + return retVal; +} + + + +// Local prototypes +static int yaffs_CheckObjectHashSanity(yaffs_Device *dev); +static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr); +static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr); +static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan); + +static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type); +static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj); +static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force); +static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId); +static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj); +static int yaffs_CheckStructures(void); +static void yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit); +static int yaffs_DoGenericObjectDeletion(yaffs_Object *in); + +static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device *dev,int blockNo); + +// Robustification +static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND); +static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND); +static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND); +static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare); +static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare); + +static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND); + +static int yaffs_UnlinkWorker(yaffs_Object *obj); + + +static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1); + + + +loff_t yaffs_GetFileSize(yaffs_Object *obj); + +static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted); +static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted); + + +static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve); + +#ifdef YAFFS_PARANOID +static int yaffs_CheckFileSanity(yaffs_Object *in); +#else +#define yaffs_CheckFileSanity(in) +#endif + +#ifdef CONFIG_YAFFS_SHORT_OP_CACHE +static void yaffs_InvalidateChunkCache(yaffs_Object *in); +#define yaffs_INVALIDATECHUNKCACHE(in) yaffs_InvalidateChunkCache(in) +#else +#define yaffs_INVALIDATECHUNKCACHE(in) +#endif + + +static __inline__ yaffs_BlockInfo* yaffs_GetBlockInfo(yaffs_Device *dev, int blk) +{ + if(blk < dev->startBlock || blk > dev->endBlock) + { + YBUG(); + } + return &dev->blockInfo[blk - dev->startBlock]; +} + + +static __inline__ int yaffs_HashFunction(int n) +{ + return (n % YAFFS_NOBJECT_BUCKETS); +} + + +yaffs_Object *yaffs_Root(yaffs_Device *dev) +{ + return dev->rootDir; +} + +yaffs_Object *yaffs_LostNFound(yaffs_Device *dev) +{ + return dev->lostNFoundDir; +} + + +static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare) +{ + dev->nPageWrites++; + return dev->writeChunkToNAND(dev,chunkInNAND,data,spare); +} + + +int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare,int doErrorCorrection) +{ + int retVal; + __u8 calcEcc[3]; + yaffs_Spare localSpare; + int eccResult1,eccResult2; + + dev->nPageReads++; + + if(!spare && data) + { + // If we don't have a real spare, then we use a local one. + // Need this for the calculation of the ecc + spare = &localSpare; + } + + retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare); + if(data && doErrorCorrection) + { + // Do ECC correction + //Todo handle any errors + nand_calculate_ecc(data,calcEcc); + eccResult1 = nand_correct_data (data,spare->ecc1, calcEcc); + nand_calculate_ecc(&data[256],calcEcc); + eccResult2 = nand_correct_data (&data[256],spare->ecc2, calcEcc); + + if(eccResult1>0) + { + T((TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND)); + } + else if(eccResult1<0) + { + T((TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND)); + } + + if(eccResult2>0) + { + T((TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND)); + } + else if(eccResult2<0) + { + T((TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND)); + } + + if(eccResult1 || eccResult2) + { + // Hoosterman, we had a data problem on this page + yaffs_HandleReadDataError(dev,chunkInNAND); + } + } + return retVal; +} + + +static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND) +{ + + static int init = 0; + static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK]; + static __u8 data[YAFFS_BYTES_PER_CHUNK]; + static __u8 spare[16]; + + + dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare); + + + + if(!init) + { + memset(cmpbuf,0xff,YAFFS_BYTES_PER_CHUNK); + init = 1; + } + + if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) return YAFFS_FAIL; + if(memcmp(cmpbuf,spare,16)) return YAFFS_FAIL; + + + return YAFFS_OK; + +} + + + +int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND) +{ + dev->nBlockErasures++; + return dev->eraseBlockInNAND(dev,blockInNAND); +} + +int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev) +{ + return dev->initialiseNAND(dev); +} + +static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_Spare *spare,int useReserve) +{ + int chunk; + + int writeOk = 1; + int attempts = 0; + + unsigned char rbData[YAFFS_BYTES_PER_CHUNK]; + yaffs_Spare rbSpare; + + do{ + chunk = yaffs_AllocateChunk(dev,useReserve); + + if(chunk >= 0) + { + + // First check this chunk is erased... +#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK + writeOk = yaffs_CheckChunkErased(dev,chunk); +#endif + if(!writeOk) + { + T((TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk)); + } + else + { + writeOk = yaffs_WriteChunkToNAND(dev,chunk,data,spare); + } + attempts++; + if(writeOk) + { + // Readback & verify + // If verify fails, then delete this chunk and try again + // To verify we compare everything except the block and + // page status bytes. + // NB We check a raw read without ECC correction applied + yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare,0); + +#ifndef CONFIG_YAFFS_DISABLE_WRITE_VERIFY + if(!yaffs_VerifyCompare(data,rbData,spare,&rbSpare)) + { + // Didn't verify + T((TSTR("**>> yaffs write verify failed on chunk %d" TENDSTR), chunk)); + + writeOk = 0; + } +#endif + + } + if(writeOk) + { + // Copy the data into the write buffer. + // NB We do this at the end to prevent duplicates in the case of a write error. + //Todo + yaffs_HandleWriteChunkOk(dev,chunk,data,spare); + } + else + { + yaffs_HandleWriteChunkError(dev,chunk); + } + } + + } while(chunk >= 0 && ! writeOk); + + if(attempts > 1) + { + T((TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts)); + dev->nRetriedWrites+= (attempts - 1); + } + + return chunk; +} + +/// +// Functions for robustisizing +// +// + +static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND) +{ + // Ding the blockStatus in the first two pages of the block. + + yaffs_Spare spare; + + memset(&spare, 0xff,sizeof(yaffs_Spare)); + + spare.blockStatus = 0; + + yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK, NULL , &spare); + yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK + 1, NULL , &spare); + + yaffs_GetBlockInfo(dev,blockInNAND)->blockState = YAFFS_BLOCK_STATE_DEAD; + dev->nRetiredBlocks++; +} + + + +static int yaffs_RewriteBufferedBlock(yaffs_Device *dev) +{ + dev->doingBufferedBlockRewrite = 1; + // + // Remove erased chunks + // Rewrite existing chunks to a new block + // Set current write block to the new block + + dev->doingBufferedBlockRewrite = 0; + + return 1; +} + + +static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND) +{ + int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK; + + // Mark the block for retirement + yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1; + T((TSTR("**>>Block %d marked for retirement" TENDSTR),blockInNAND)); + + + //TODO + // Just do a garbage collection on the affected block then retire the block + // NB recursion +} + + +static void yaffs_CheckWrittenBlock(yaffs_Device *dev,int chunkInNAND) +{ +} + +static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare) +{ +} + +static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare) +{ +} + +static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND) +{ + int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK; + + // Mark the block for retirement + yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1; + // Delete the chunk + yaffs_DeleteChunk(dev,chunkInNAND); +} + + + + +static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1) +{ + + + if( memcmp(d0,d1,YAFFS_BYTES_PER_CHUNK) != 0 || + s0->tagByte0 != s1->tagByte0 || + s0->tagByte1 != s1->tagByte1 || + s0->tagByte2 != s1->tagByte2 || + s0->tagByte3 != s1->tagByte3 || + s0->tagByte4 != s1->tagByte4 || + s0->tagByte5 != s1->tagByte5 || + s0->tagByte6 != s1->tagByte6 || + s0->tagByte7 != s1->tagByte7 || + s0->ecc1[0] != s1->ecc1[0] || + s0->ecc1[1] != s1->ecc1[1] || + s0->ecc1[2] != s1->ecc1[2] || + s0->ecc2[0] != s1->ecc2[0] || + s0->ecc2[1] != s1->ecc2[1] || + s0->ecc2[2] != s1->ecc2[2] ) + { + return 0; + } + + return 1; +} + + +///////////////////////// Object management ////////////////// +// List of spare objects +// The list is hooked together using the first pointer +// in the object + +// static yaffs_Object *yaffs_freeObjects = NULL; + +// static int yaffs_nFreeObjects; + +// static yaffs_ObjectList *yaffs_allocatedObjectList = NULL; + +// static yaffs_ObjectBucket yaffs_objectBucket[YAFFS_NOBJECT_BUCKETS]; + + +static __u16 yaffs_CalcNameSum(const char *name) +{ + __u16 sum = 0; + __u16 i = 1; + + __u8 *bname = (__u8 *)name; + if(bname) + { + while (*bname) + { + sum += (*bname) * i; + i++; + bname++; + } + } + return sum; +} + + +void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare) +{ + nand_calculate_ecc (data , spare->ecc1); + nand_calculate_ecc (&data[256] , spare->ecc2); +} + +void yaffs_CalcTagsECC(yaffs_Tags *tags) +{ + // Calculate an ecc + + unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes; + unsigned i,j; + unsigned ecc = 0; + unsigned bit = 0; + + tags->ecc = 0; + + for(i = 0; i < 8; i++) + { + for(j = 1; j &0xff; j<<=1) + { + bit++; + if(b[i] & j) + { + ecc ^= bit; + } + } + } + + tags->ecc = ecc; + + +} + +void yaffs_CheckECCOnTags(yaffs_Tags *tags) +{ + unsigned ecc = tags->ecc; + + yaffs_CalcTagsECC(tags); + + ecc ^= tags->ecc; + + if(ecc) + { + // Needs fixing + unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes; + + ecc--; + + b[ecc / 8] ^= (1 << (ecc & 7)); + + // Now recvalc the ecc + yaffs_CalcTagsECC(tags); + } +} + + +///////////////////////// TNODES /////////////////////// + +// List of spare tnodes +// The list is hooked together using the first pointer +// in the tnode. + +//static yaffs_Tnode *yaffs_freeTnodes = NULL; + +// static int yaffs_nFreeTnodes; + +//static yaffs_TnodeList *yaffs_allocatedTnodeList = NULL; + + + +// yaffs_CreateTnodes creates a bunch more tnodes and +// adds them to the tnode free list. +// Don't use this function directly + +static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes) +{ + int i; + yaffs_Tnode *newTnodes; + yaffs_TnodeList *tnl; + + if(nTnodes < 1) return YAFFS_OK; + + // make these things + + newTnodes = YMALLOC(nTnodes * sizeof(yaffs_Tnode)); + + if (!newTnodes) + { + YALERT("Could not malloc tnodes"); + return YAFFS_FAIL; + } + + // Hook them into the free list + for(i = 0; i < nTnodes - 1; i++) + { + newTnodes[i].internal[0] = &newTnodes[i+1]; + } + + newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes; + dev->freeTnodes = newTnodes; + dev->nFreeTnodes+= nTnodes; + dev->nTnodesCreated += nTnodes; + + // Now add this bunch of tnodes to a list for freeing up. + + tnl = YMALLOC(sizeof(yaffs_TnodeList)); + if(!tnl) + { + YALERT("Could not add tnodes to management list"); + } + else + { + tnl->tnodes = newTnodes; + tnl->next = dev->allocatedTnodeList; + dev->allocatedTnodeList = tnl; + } + + + YINFO("Tnodes created"); + + + return YAFFS_OK; +} + + +// GetTnode gets us a clean tnode. Tries to make allocate more if we run out +static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev) +{ + yaffs_Tnode *tn = NULL; + + // If there are none left make more + if(!dev->freeTnodes) + { + yaffs_CreateTnodes(dev,YAFFS_ALLOCATION_NTNODES); + } + + if(dev->freeTnodes) + { + tn = dev->freeTnodes; + dev->freeTnodes = dev->freeTnodes->internal[0]; + dev->nFreeTnodes--; + // zero out + memset(tn,0,sizeof(yaffs_Tnode)); + } + + + return tn; +} + + +// FreeTnode frees up a tnode and puts it back on the free list +static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn) +{ + tn->internal[0] = dev->freeTnodes; + dev->freeTnodes = tn; + dev->nFreeTnodes++; +} + + +static void yaffs_DeinitialiseTnodes(yaffs_Device*dev) +{ + // Free the list of allocated tnodes + + while(dev->allocatedTnodeList) + { + YFREE(dev->allocatedTnodeList->tnodes); + dev->allocatedTnodeList = dev->allocatedTnodeList->next; + } + + dev->freeTnodes = NULL; + dev->nFreeTnodes = 0; +} + +static void yaffs_InitialiseTnodes(yaffs_Device*dev) +{ + dev->allocatedTnodeList = NULL; + dev->freeTnodes = NULL; + dev->nFreeTnodes = 0; + dev->nTnodesCreated = 0; + +} + +void yaffs_TnodeTest(yaffs_Device *dev) +{ + + int i; + int j; + yaffs_Tnode *tn[1000]; + + YINFO("Testing TNodes"); + + for(j = 0; j < 50; j++) + { + for(i = 0; i < 1000; i++) + { + tn[i] = yaffs_GetTnode(dev); + if(!tn[i]) + { + YALERT("Getting tnode failed"); + } + } + for(i = 0; i < 1000; i+=3) + { + yaffs_FreeTnode(dev,tn[i]); + tn[i] = NULL; + } + + } +} + +////////////////// END OF TNODE MANIPULATION /////////////////////////// + +/////////////// Functions to manipulate the look-up tree (made up of tnodes) +// The look up tree is represented by the top tnode and the number of topLevel +// in the tree. 0 means only the level 0 tnode is in the tree. + + +// FindLevel0Tnode finds the level 0 tnode, if one exists. +// Used when reading..... +static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,yaffs_FileStructure *fStruct, __u32 chunkId) +{ + + yaffs_Tnode *tn = fStruct->top; + __u32 i; + int requiredTallness; + int level = fStruct->topLevel; + + // Check sane level and chunk Id + if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL) + { + char str[50]; + sprintf(str,"Bad level %d",level); + YALERT(str); + return NULL; + } + + if(chunkId > YAFFS_MAX_CHUNK_ID) + { + char str[50]; + sprintf(str,"Bad chunkId %d",chunkId); + YALERT(str); + return NULL; + } + + // First check we're tall enough (ie enough topLevel) + + i = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS); + requiredTallness = 0; + while(i) + { + i >>= YAFFS_TNODES_INTERNAL_BITS; + requiredTallness++; + } + + + if(requiredTallness > fStruct->topLevel) + { + // Not tall enough, so we can't find it, return NULL. + return NULL; + } + + + // Traverse down to level 0 + while (level > 0 && tn) + { + tn = tn->internal[(chunkId >>(/* dev->chunkGroupBits + */ YAFFS_TNODES_LEVEL0_BITS + (level-1) * YAFFS_TNODES_INTERNAL_BITS)) & + YAFFS_TNODES_INTERNAL_MASK]; + level--; + + } + + return tn; +} + +// AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree. +// This happens in two steps: +// 1. If the tree isn't tall enough, then make it taller. +// 2. Scan down the tree towards the level 0 tnode adding tnodes if required. +// +// Used when modifying the tree. +// +static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId) +{ + + yaffs_Tnode *tn; + + int requiredTallness; + + __u32 i; + __u32 l; + + + //T((TSTR("AddOrFind topLevel=%d, chunk=%d"),fStruct->topLevel,chunkId)); + + // Check sane level and page Id + if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL) + { + char str[50]; + sprintf(str,"Bad level %d",fStruct->topLevel); + YALERT(str); + return NULL; + } + + if(chunkId > YAFFS_MAX_CHUNK_ID) + { + char str[50]; + sprintf(str,"Bad chunkId %d",chunkId); + YALERT(str); + return NULL; + } + + // First check we're tall enough (ie enough topLevel) + + i = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS); + requiredTallness = 0; + while(i) + { + i >>= YAFFS_TNODES_INTERNAL_BITS; + requiredTallness++; + } + + //T((TSTR(" required=%d"),requiredTallness)); + + + if(requiredTallness > fStruct->topLevel) + { + // Not tall enough,gotta make the tree taller + for(i = fStruct->topLevel; i < requiredTallness; i++) + { + //T((TSTR(" add new top"))); + + tn = yaffs_GetTnode(dev); + + if(tn) + { + tn->internal[0] = fStruct->top; + fStruct->top = tn; + } + else + { + YALERT("No more tnodes"); + } + } + + fStruct->topLevel = requiredTallness; + } + + + // Traverse down to level 0, adding anything we need + + l = fStruct->topLevel; + tn = fStruct->top; + while (l > 0 && tn) + { + i = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) & + YAFFS_TNODES_INTERNAL_MASK; + + //T((TSTR(" [%d:%d]"),l,i)); + + if(!tn->internal[i]) + { + //T((TSTR(" added"))); + + tn->internal[i] = yaffs_GetTnode(dev); + } + + tn = tn->internal[i]; + l--; + + } + + //TSTR(TENDSTR))); + + return tn; +} + +// DeleteWorker scans backwards through the tnode tree and deletes all the +// chunks and tnodes in the file + +static void yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit) +{ + int i; + int chunkInInode; + int theChunk; + yaffs_Tags tags; + int found; + int chunkDeleted; + + + if(tn) + { + if(level > 0) + { + + for(i = YAFFS_NTNODES_INTERNAL -1; i >= 0 && (!limit || *limit > 0); i--) + { + if(tn->internal[i]) + { + yaffs_DeleteWorker(in,tn->internal[i],level - 1, + (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i ,limit); + yaffs_FreeTnode(in->myDev,tn->internal[i]); + tn->internal[i] = NULL; + } + + } + } + else if(level == 0) + { + for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0; i--) //NB Don't apply the limit here, always delete a whole level0 + { + if(tn->level0[i]) + { + int j; + + chunkInInode = (chunkOffset << YAFFS_TNODES_LEVEL0_BITS ) + i; + + theChunk = tn->level0[i] << in->myDev->chunkGroupBits; + + // Now we need to search for it + for(j = 0,found = 0; theChunk && j < in->myDev->chunkGroupSize && !found; j++) + { + yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,&tags,&chunkDeleted); + if(yaffs_TagsMatch(&tags,in->objectId,chunkInInode,chunkDeleted)) + { + // found it; + found = 1; + + } + else + { + theChunk++; + } + } + + if(found) + { + yaffs_DeleteChunk(in->myDev,theChunk); + in->nDataChunks--; + if(limit) + { + *limit = *limit-1; + } + + } + + tn->level0[i] = 0; + } + + } + + } + + } + +} + + + + +// Pruning removes any part of the file structure tree that is beyond the +// bounds of the file (ie that does not point to chunks). +// +// A file should only get pruned when its size is reduced. +// +// Before pruning, the chunks must be pulled from the tree and the +// level 0 tnode entries must be zeroed out. +// Could also use this for file deletion, but that's probably better handled +// by a special case. + +// yaffs_PruneWorker should only be called by yaffs_PruneFileStructure() + +static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn, __u32 level, int del0) +{ + int i; + int hasData; + + if(tn) + { + hasData = 0; + + for(i = 0; i < YAFFS_NTNODES_INTERNAL; i++) + { + if(tn->internal[i] && level > 0) + { + tn->internal[i] = yaffs_PruneWorker(dev,tn->internal[i],level - 1, ( i == 0) ? del0 : 1); + } + + if(tn->internal[i]) + { + hasData++; + } + } + + if(hasData == 0 && del0) + { + // Free and return NULL + + yaffs_FreeTnode(dev,tn); + tn = NULL; + } + + } + + return tn; + +} + +static int yaffs_PruneFileStructure(yaffs_Device *dev, yaffs_FileStructure *fStruct) +{ + int i; + int hasData; + int done = 0; + yaffs_Tnode *tn; + + if(fStruct->topLevel > 0) + { + fStruct->top = yaffs_PruneWorker(dev,fStruct->top, fStruct->topLevel,0); + + // Now we have a tree with all the non-zero branches NULL but the height + // is the same as it was. + // Let's see if we can trim internal tnodes to shorten the tree. + // We can do this if only the 0th element in the tnode is in use + // (ie all the non-zero are NULL) + + while(fStruct->topLevel && !done) + { + tn = fStruct->top; + + hasData = 0; + for(i = 1; i internal[i]) + { + hasData++; + } + } + + if(!hasData) + { + fStruct->top = tn->internal[0]; + fStruct->topLevel--; + yaffs_FreeTnode(dev,tn); + } + else + { + done = 1; + } + } + } + + return YAFFS_OK; +} + + + + +/////////////////////// End of File Structure functions. ///////////////// + +// yaffs_CreateFreeObjects creates a bunch more objects and +// adds them to the object free list. +static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects) +{ + int i; + yaffs_Object *newObjects; + yaffs_ObjectList *list; + + if(nObjects < 1) return YAFFS_OK; + + // make these things + + newObjects = YMALLOC(nObjects * sizeof(yaffs_Object)); + + if (!newObjects) + { + YALERT("Could not allocate more objects"); + return YAFFS_FAIL; + } + + // Hook them into the free list + for(i = 0; i < nObjects - 1; i++) + { + (yaffs_Object *)newObjects[i].siblings.next = &newObjects[i+1]; + } + + newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects; + dev->freeObjects = newObjects; + dev->nFreeObjects+= nObjects; + dev->nObjectsCreated+= nObjects; + + // Now add this bunch of Objects to a list for freeing up. + + list = YMALLOC(sizeof(yaffs_ObjectList)); + if(!list) + { + YALERT("Could not add Objects to management list"); + } + else + { + list->objects = newObjects; + list->next = dev->allocatedObjectList; + dev->allocatedObjectList = list; + } + + + YINFO("Objects created"); + + + return YAFFS_OK; +} + + +// AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out +static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev) +{ + yaffs_Object *tn = NULL; + + // If there are none left make more + if(!dev->freeObjects) + { + yaffs_CreateFreeObjects(dev,YAFFS_ALLOCATION_NOBJECTS); + } + + if(dev->freeObjects) + { + tn = dev->freeObjects; + dev->freeObjects = (yaffs_Object *)(dev->freeObjects->siblings.next); + dev->nFreeObjects--; + + // Now sweeten it up... + + memset(tn,0,sizeof(yaffs_Object)); + tn->myDev = dev; + tn->chunkId = -1; + tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN; + INIT_LIST_HEAD(&(tn->hardLinks)); + INIT_LIST_HEAD(&(tn->hashLink)); + INIT_LIST_HEAD(&tn->siblings); + + // Add it to the lost and found directory. + // NB Can't put root or lostNFound in lostNFound so + // check if lostNFound exists first + if(dev->lostNFoundDir) + { + yaffs_AddObjectToDirectory(dev->lostNFoundDir,tn); + } + } + + + return tn; +} + +static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev,int number,__u32 mode) +{ + + yaffs_Object *obj = yaffs_CreateNewObject(dev,number,YAFFS_OBJECT_TYPE_DIRECTORY); + if(obj) + { + obj->fake = 1; // it is fake so it has no NAND presence... + obj->renameAllowed= 0; // ... and we're not allowed to rename it... + obj->unlinkAllowed= 0; // ... or unlink it + obj->deleted = 0; + obj->unlinked = 0; + obj->st_mode = mode; + obj->myDev = dev; + obj->chunkId = 0; // Not a valid chunk. + } + + return obj; + +} + + +static void yaffs_UnhashObject(yaffs_Object *tn) +{ + int bucket; + yaffs_Device *dev = tn->myDev; + + + // If it is still linked into the bucket list, free from the list + if(!list_empty(&tn->hashLink)) + { + list_del_init(&tn->hashLink); + bucket = yaffs_HashFunction(tn->objectId); + dev->objectBucket[bucket].count--; + } + +} + + +// FreeObject frees up a Object and puts it back on the free list +static void yaffs_FreeObject(yaffs_Object *tn) +{ + + yaffs_Device *dev = tn->myDev; + + yaffs_UnhashObject(tn); + + // Link into the free list. + (yaffs_Object *)(tn->siblings.next) = dev->freeObjects; + dev->freeObjects = tn; + dev->nFreeObjects++; +} + + + + +static void yaffs_DeinitialiseObjects(yaffs_Device *dev) +{ + // Free the list of allocated Objects + + while( dev->allocatedObjectList) + { + YFREE(dev->allocatedObjectList->objects); + dev->allocatedObjectList = dev->allocatedObjectList->next; + } + + dev->freeObjects = NULL; + dev->nFreeObjects = 0; +} + +static void yaffs_InitialiseObjects(yaffs_Device *dev) +{ + int i; + + dev->allocatedObjectList = NULL; + dev->freeObjects = NULL; + dev->nFreeObjects = 0; + + for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) + { + INIT_LIST_HEAD(&dev->objectBucket[i].list); + dev->objectBucket[i].count = 0; + } + +} + + + + + + +int yaffs_FindNiceObjectBucket(yaffs_Device *dev) +{ + static int x = 0; + int i; + int l = 999; + int lowest = 999999; + + + // First let's see if we can find one that's empty. + + for(i = 0; i < 10 && lowest > 0; i++) + { + x++; + x %= YAFFS_NOBJECT_BUCKETS; + if(dev->objectBucket[x].count < lowest) + { + lowest = dev->objectBucket[x].count; + l = x; + } + + } + + // If we didn't find an empty list, then try + // looking a bit further for a short one + + for(i = 0; i < 10 && lowest > 3; i++) + { + x++; + x %= YAFFS_NOBJECT_BUCKETS; + if(dev->objectBucket[x].count < lowest) + { + lowest = dev->objectBucket[x].count; + l = x; + } + + } + + return l; +} + +static int yaffs_CreateNewObjectNumber(yaffs_Device *dev) +{ + int bucket = yaffs_FindNiceObjectBucket(dev); + + // Now find an object value that has not already been taken + // by scanning the list. + + int found = 0; + struct list_head *i; + + int n = bucket; + + //yaffs_CheckObjectHashSanity(); + + while(!found) + { + found = 1; + n += YAFFS_NOBJECT_BUCKETS; + if(1 ||dev->objectBucket[bucket].count > 0) + { + list_for_each(i,&dev->objectBucket[bucket].list) + { + // If there is already one in the list + if(list_entry(i, yaffs_Object,hashLink)->objectId == n) + { + found = 0; + } + } + } + } + + //T(("bucket %d count %d inode %d\n",bucket,yaffs_objectBucket[bucket].count,n); + + return n; +} + +void yaffs_HashObject(yaffs_Object *in) +{ + int bucket = yaffs_HashFunction(in->objectId); + yaffs_Device *dev = in->myDev; + + if(!list_empty(&in->hashLink)) + { + YINFO("!!!"); + } + + + list_add(&in->hashLink,&dev->objectBucket[bucket].list); + dev->objectBucket[bucket].count++; + +} + +yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number) +{ + int bucket = yaffs_HashFunction(number); + struct list_head *i; + yaffs_Object *in; + + list_for_each(i,&dev->objectBucket[bucket].list) + { + // Look if it is in the list + in = list_entry(i, yaffs_Object,hashLink); + if(in->objectId == number) + { + return in; + } + } + + return NULL; +} + + + +yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type) +{ + + yaffs_Object *theObject; + + if(number < 0) + { + number = yaffs_CreateNewObjectNumber(dev); + } + + theObject = yaffs_AllocateEmptyObject(dev); + + if(theObject) + { + theObject->fake = 0; + theObject->renameAllowed = 1; + theObject->unlinkAllowed = 1; + theObject->objectId = number; + yaffs_HashObject(theObject); + theObject->variantType = type; + theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME; + + switch(type) + { + case YAFFS_OBJECT_TYPE_FILE: + theObject->variant.fileVariant.fileSize = 0; + theObject->variant.fileVariant.scannedFileSize = 0; + theObject->variant.fileVariant.topLevel = 0; + theObject->variant.fileVariant.top = yaffs_GetTnode(dev); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + INIT_LIST_HEAD(&theObject->variant.directoryVariant.children); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + // No action required + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + // No action required + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + // No action required + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + // todo this should not happen + break; + } + } + + return theObject; +} + +yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type) +{ + yaffs_Object *theObject = NULL; + + if(number > 0) + { + theObject = yaffs_FindObjectByNumber(dev,number); + } + + if(!theObject) + { + theObject = yaffs_CreateNewObject(dev,number,type); + } + + return theObject; + +} + +char *yaffs_CloneString(const char *str) +{ + char *newStr = NULL; + + if(str && *str) + { + newStr = YMALLOC(strlen(str) + 1); + strcpy(newStr,str); + } + + return newStr; + +} + +// +// Mknod (create) a new object. +// equivalentObject only has meaning for a hard link; +// aliasString only has meaning for a sumlink. +// rdev only has meaning for devices (a subset of special objects) +yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type, + yaffs_Object *parent, + const char *name, + __u32 mode, + __u32 uid, + __u32 gid, + yaffs_Object *equivalentObject, + const char *aliasString, + __u32 rdev) +{ + yaffs_Object *in; + + yaffs_Device *dev = parent->myDev; + + // Check if the entry exists. If it does then fail the call since we don't want a dup. + if(yaffs_FindObjectByName(parent,name)) + { + return NULL; + } + + in = yaffs_CreateNewObject(dev,-1,type); + + if(in) + { + in->chunkId = -1; + in->valid = 1; + in->variantType = type; + + in->st_mode = mode; + in->st_rdev = rdev; + in->st_uid = uid; + in->st_gid = gid; + in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME; + + in->nDataChunks = 0; + + in->sum = yaffs_CalcNameSum(name); + in->dirty = 1; + + yaffs_AddObjectToDirectory(parent,in); + + in->myDev = parent->myDev; + + + switch(type) + { + case YAFFS_OBJECT_TYPE_SYMLINK: + in->variant.symLinkVariant.alias = yaffs_CloneString(aliasString); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant.hardLinkVariant.equivalentObject = equivalentObject; + in->variant.hardLinkVariant.equivalentObjectId = equivalentObject->objectId; + list_add(&in->hardLinks,&equivalentObject->hardLinks); + break; + case YAFFS_OBJECT_TYPE_FILE: // do nothing + case YAFFS_OBJECT_TYPE_DIRECTORY: // do nothing + case YAFFS_OBJECT_TYPE_SPECIAL: // do nothing + case YAFFS_OBJECT_TYPE_UNKNOWN: + break; + } + + if(yaffs_GetNumberOfFreeChunks(dev) <= 0 || + yaffs_UpdateObjectHeader(in,name,0) < 0) + { + // Could not create the object header, fail the creation + yaffs_UnlinkWorker(in); + in = NULL; + } + + } + + return in; +} + +yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE,parent,name,mode,uid,gid,NULL,NULL,0); +} + +yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,0); +} + +yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,rdev); +} + +yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,const char *alias) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK,parent,name,mode,uid,gid,NULL,alias,0); +} + +// NB yaffs_Link returns the object id of the equivalent object. +yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject) +{ + // Get the real object in case we were fed a hard link as an equivalent object + equivalentObject = yaffs_GetEquivalentObject(equivalentObject); + + if(yaffs_MknodObject(YAFFS_OBJECT_TYPE_HARDLINK,parent,name,0,0,0,equivalentObject,NULL,0)) + { + return equivalentObject; + } + else + { + return NULL; + } + +} + + +static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName) +{ + int unlinkOp; + + if(newDir == NULL) + { + newDir = obj->parent; // use the old directory + } + + unlinkOp = (newDir == obj->myDev->unlinkedDir && obj->variantType == YAFFS_OBJECT_TYPE_FILE); + + // If the object is a file going into the unlinked directory, then it is OK to just stuff it in since + // duplicate names are allowed. + // Otherwise only proceed if the new name does not exist and if we're putting it into a directory. + if( unlinkOp|| + (!yaffs_FindObjectByName(newDir,newName) && + newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)) + { + obj->sum = yaffs_CalcNameSum(newName); + obj->dirty = 1; + + yaffs_AddObjectToDirectory(newDir,obj); + + if(unlinkOp) obj->unlinked = 1; + + + if(yaffs_UpdateObjectHeader(obj,newName,0) >= 0) + { + return YAFFS_OK; + } + } + + return YAFFS_FAIL; +} + +int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName) +{ + yaffs_Object *obj; + + obj = yaffs_FindObjectByName(oldDir,oldName); + if(obj && obj->renameAllowed) + { + return yaffs_ChangeObjectName(obj,newDir,newName); + } + return YAFFS_FAIL; +} + + + +static int yaffs_CheckObjectHashSanity(yaffs_Device *dev) +{ + // Scan the buckets and check that the lists + // have as many members as the count says there are + int bucket; + int countEm; + struct list_head *j; + int ok = YAFFS_OK; + + for(bucket = 0; bucket < YAFFS_NOBJECT_BUCKETS; bucket++) + { + countEm = 0; + + list_for_each(j,&dev->objectBucket[bucket].list) + { + countEm++; + } + + if(countEm != dev->objectBucket[bucket].count) + { + YALERT("Inode hash inconsistency"); + ok = YAFFS_FAIL; + } + } + + return ok; +} + +void yaffs_ObjectTest(yaffs_Device *dev) +{ + yaffs_Object *in[1000]; + int inNo[1000]; + yaffs_Object *inold[1000]; + int i; + int j; + + memset(in,0,1000*sizeof(yaffs_Object *)); + memset(inold,0,1000*sizeof(yaffs_Object *)); + + yaffs_CheckObjectHashSanity(dev); + + for(j = 0; j < 10; j++) + { + //T(("%d\n",j)); + + for(i = 0; i < 1000; i++) + { + in[i] = yaffs_CreateNewObject(dev,-1,YAFFS_OBJECT_TYPE_FILE); + if(!in[i]) + { + YINFO("No more inodes"); + } + else + { + inNo[i] = in[i]->objectId; + } + } + + for(i = 0; i < 1000; i++) + { + if(yaffs_FindObjectByNumber(dev,inNo[i]) != in[i]) + { + //T(("Differnce in look up test\n")); + } + else + { + // T(("Look up ok\n")); + } + } + + yaffs_CheckObjectHashSanity(dev); + + for(i = 0; i < 1000; i+=3) + { + yaffs_FreeObject(in[i]); + in[i] = NULL; + } + + + yaffs_CheckObjectHashSanity(dev); + } + +} + + + +/////////////////////////// Block Management and Page Allocation /////////////////// + + +static int yaffs_InitialiseBlocks(yaffs_Device *dev,int nBlocks) +{ + dev->allocationBlock = -1; // force it to get a new one + //Todo we're assuming the malloc will pass. + dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo)); + if(dev->blockInfo) + { + memset(dev->blockInfo,0,nBlocks * sizeof(yaffs_BlockInfo)); + return YAFFS_OK; + } + return YAFFS_FAIL; + +} + +static void yaffs_DeinitialiseBlocks(yaffs_Device *dev) +{ + YFREE(dev->blockInfo); +} + +// FindDiretiestBlock is used to select the dirtiest block (or close enough) +// for garbage collection. + +static int yaffs_FindDirtiestBlock(yaffs_Device *dev) +{ + + int b = dev->currentDirtyChecker; + + int i; + int dirtiest = -1; + int pagesInUse = 100; // silly big number + yaffs_BlockInfo *bi; + + for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++) + { + b++; + if (b > dev->endBlock) + { + b = dev->startBlock; + } + + bi = yaffs_GetBlockInfo(dev,b); + + if(bi->blockState == YAFFS_BLOCK_STATE_FULL && + bi->pagesInUse < pagesInUse) + { + dirtiest = b; + pagesInUse = bi->pagesInUse; + } + } + + dev->currentDirtyChecker = b; + + return dirtiest; +} + + +static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo) +{ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,blockNo); + + int erasedOk = 0; + + // If the block is still healthy erase it and mark as clean. + // If the block has had a data failure, then retire it. + bi->blockState = YAFFS_BLOCK_STATE_DIRTY; + + if(!bi->needsRetiring) + { + erasedOk = yaffs_EraseBlockInNAND(dev,blockNo); + if(!erasedOk) + { + T((TSTR("**>> Erasure failed %d" TENDSTR),blockNo)); + } + } + + if( erasedOk ) + { + bi->blockState = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + bi->pagesInUse = 0; + bi->pageBits = 0; + + T((TSTR("Erased block %d" TENDSTR),blockNo)); + } + else + { + yaffs_RetireBlock(dev,blockNo); + T((TSTR("**>> Block %d retired" TENDSTR),blockNo)); + } +} + + +static int yaffs_FindBlockForAllocation(yaffs_Device *dev) +{ + int i; + yaffs_BlockInfo *bi; + + if(dev->nErasedBlocks < 1) + { + // Hoosterman we've got a problem. + // Can't get space to gc + return -1; + } + + // Find an empty block. + + for(i = dev->startBlock; i <= dev->endBlock; i++) + { + bi = yaffs_GetBlockInfo(dev,i); + if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY) + { + bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING; + dev->nErasedBlocks--; + return i; + } + } + + return -1; +} + + + +static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve) +{ + int retVal; + yaffs_BlockInfo *bi; + + if(dev->allocationBlock < 0) + { + // Get next block to allocate off + dev->allocationBlock = yaffs_FindBlockForAllocation(dev); + dev->allocationPage = 0; + } + + if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS) + { + // Not enough space to allocate unless we're allowed to use the reserve. + return -1; + } + + // Next page please.... + if(dev->allocationBlock >= 0) + { + bi = yaffs_GetBlockInfo(dev,dev->allocationBlock); + + retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) + + dev->allocationPage; + bi->pagesInUse++; + bi->pageBits |= (1 << (dev->allocationPage)); + + dev->allocationPage++; + + dev->nFreeChunks--; + + // If the block is full set the state to full + if(dev->allocationPage >= YAFFS_CHUNKS_PER_BLOCK) + { + bi->blockState = YAFFS_BLOCK_STATE_FULL; + dev->allocationBlock = -1; + } + + + return retVal; + + } + T((TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR))); + + return -1; +} + + +int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block) +{ + int oldChunk; + int newChunk; + __u32 mask; + + + yaffs_Spare spare; + yaffs_Tags tags; + __u8 buffer[YAFFS_BYTES_PER_CHUNK]; + + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block); + + yaffs_Object *object; + + //T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits)); + + for(mask = 1,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK; + mask && bi->pageBits; + mask <<= 1, oldChunk++ ) + { + if(bi->pageBits & mask) + { + + // This page is in use and needs to be copied off + + dev->nGCCopies++; + + //T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk)); + + yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare,1); + + yaffs_GetTagsFromSpare(&spare,&tags); + tags.serialNumber++; + yaffs_LoadTagsIntoSpare(&spare,&tags); + +#if 0 + newChunk = yaffs_AllocatePage(dev,1); + if(newChunk < 0) + { + return YAFFS_FAIL; + } + + yaffs_WriteChunkToNAND(dev,newChunk, buffer, &spare); + +#else + newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1); +#endif + if(newChunk < 0) + { + return YAFFS_FAIL; + } + + object = yaffs_FindObjectByNumber(dev,tags.objectId); + + // Ok, now fix up the Tnodes etc. + + if(tags.chunkId == 0) + { + // It's a header + object->chunkId = newChunk; + } + else + { + // It's a data chunk + yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0); + + } + + yaffs_DeleteChunk(dev,oldChunk); + + } + } + + return YAFFS_OK; +} + + +static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev) +{ + // todo find a file to delete + struct list_head *i; + yaffs_Object *l; + + + // To the free chunks add the chunks that are in the deleted unlinked files. + list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children) + { + l = list_entry(i, yaffs_Object,siblings); + if(l->deleted) + { + return l; + } + } + return NULL; +} + +static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev) +{ + // This does background deletion on unlinked files.. only deleted ones. + // If we don't have a file we're working on then find one + if(!dev->unlinkedDeletion && dev->nDeletedFiles > 0) + { + dev->unlinkedDeletion = yaffs_FindDeletedUnlinkedFile(dev); + } + + // OK, we're working on a file... + if(dev->unlinkedDeletion) + { + yaffs_Object *obj = dev->unlinkedDeletion; + int limit; + limit = 50; // Max number of chunks to delete in a file. NB this can be exceeded, but not by much. + yaffs_DeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0,&limit); + if(obj->nDataChunks == 0) + { + // Done all the deleting of data chunks. + // Now dump the header and clean up + yaffs_DoGenericObjectDeletion(dev->unlinkedDeletion); + dev->nDeletedFiles--; + dev->unlinkedDeletion = NULL; + } + } +} + +static int yaffs_CheckGarbageCollection(yaffs_Device *dev) +{ + int block; + + yaffs_DoUnlinkedFileDeletion(dev); + + if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER)) + { + dev->garbageCollectionRequired = 1; + } + + if(dev->garbageCollectionRequired) + { + dev->garbageCollections++; + dev->garbageCollectionRequired = 0; + if(dev->blockSelectedForGC >= 0) + { + block = dev->blockSelectedForGC; + } + else + { + block = yaffs_FindDirtiestBlock(dev); + } + + if(block >= 0) + { + return yaffs_GarbageCollectBlock(dev,block); + } + else + { + return YAFFS_FAIL; + } + } + + return YAFFS_OK; +} + + +//////////////////////////// TAGS /////////////////////////////////////// + +static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr) +{ + yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr; + + yaffs_CalcTagsECC(tagsPtr); + + sparePtr->tagByte0 = tu->asBytes[0]; + sparePtr->tagByte1 = tu->asBytes[1]; + sparePtr->tagByte2 = tu->asBytes[2]; + sparePtr->tagByte3 = tu->asBytes[3]; + sparePtr->tagByte4 = tu->asBytes[4]; + sparePtr->tagByte5 = tu->asBytes[5]; + sparePtr->tagByte6 = tu->asBytes[6]; + sparePtr->tagByte7 = tu->asBytes[7]; +} + +static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr) +{ + yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr; + + tu->asBytes[0]= sparePtr->tagByte0; + tu->asBytes[1]= sparePtr->tagByte1; + tu->asBytes[2]= sparePtr->tagByte2; + tu->asBytes[3]= sparePtr->tagByte3; + tu->asBytes[4]= sparePtr->tagByte4; + tu->asBytes[5]= sparePtr->tagByte5; + tu->asBytes[6]= sparePtr->tagByte6; + tu->asBytes[7]= sparePtr->tagByte7; + + yaffs_CheckECCOnTags(tagsPtr); +} + +static void yaffs_SpareInitialise(yaffs_Spare *spare) +{ + memset(spare,0xFF,sizeof(yaffs_Spare)); +} + +static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted) +{ + if(tags) + { + yaffs_Spare spare; + if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,NULL,&spare,1) == YAFFS_OK) + { + *chunkDeleted = (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0; + yaffs_GetTagsFromSpare(&spare,tags); + return YAFFS_OK; + } + else + { + return YAFFS_FAIL; + } + } + + return YAFFS_OK; +} + +#if 0 +static int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_Tags *tags) +{ + // NB There must be tags, data is optional + // If there is data, then an ECC is calculated on it. + + yaffs_Spare spare; + + if(!tags) + { + return YAFFS_FAIL; + } + + yaffs_SpareInitialise(&spare); + + + if(buffer) + { + yaffs_CalcECC(buffer,&spare); + } + + yaffs_LoadTagsIntoSpare(&spare,tags); + + return yaffs_WriteChunkToNAND(dev,chunkInNAND,buffer,&spare); + +} +#endif + + +static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_Tags *tags, int useReserve) +{ + // NB There must be tags, data is optional + // If there is data, then an ECC is calculated on it. + + yaffs_Spare spare; + + if(!tags) + { + return YAFFS_FAIL; + } + + yaffs_SpareInitialise(&spare); + + + if(buffer) + { + yaffs_CalcECC(buffer,&spare); + } + + yaffs_LoadTagsIntoSpare(&spare,tags); + + return yaffs_WriteNewChunkToNAND(dev,buffer,&spare,useReserve); + +} + +static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted) +{ + return ( tags->chunkId == chunkInObject && + tags->objectId == objectId && + !chunkDeleted) ? 1 : 0; + +} + + + +int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags) +{ + //Get the Tnode, then get the level 0 offset chunk offset + yaffs_Tnode *tn; + int theChunk = -1; + yaffs_Tags localTags; + int i; + int found = 0; + int chunkDeleted; + + yaffs_Device *dev = in->myDev; + + + if(!tags) + { + // Passed a NULL, so use our own tags space + tags = &localTags; + } + + tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode); + + if(tn) + { + theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits; + + // Now we need to do the shifting etc and search for it + for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++) + { + yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted); + if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted)) + { + // found it; + found = 1; + } + else + { + theChunk++; + } + } + } + return found ? theChunk : -1; +} + +int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags) +{ + //Get the Tnode, then get the level 0 offset chunk offset + yaffs_Tnode *tn; + int theChunk = -1; + yaffs_Tags localTags; + int i; + int found = 0; + yaffs_Device *dev = in->myDev; + int chunkDeleted; + + if(!tags) + { + // Passed a NULL, so use our own tags space + tags = &localTags; + } + + tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode); + + if(tn) + { + + theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits; + + // Now we need to do the shifting etc and search for it + for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++) + { + yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted); + if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted)) + { + // found it; + found = 1; + } + else + { + theChunk++; + } + } + + // Delete the entry in the filestructure + if(found) + { + tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = 0; + } + } + else + { + //T(("No level 0 found for %d\n", chunkInInode)); + } + + if(!found) + { + //T(("Could not find %d to delete\n",chunkInInode)); + } + return found ? theChunk : -1; +} + + +#if YAFFS_PARANOID + +static int yaffs_CheckFileSanity(yaffs_Object *in) +{ + int chunk; + int nChunks; + int fSize; + int failed = 0; + int objId; + yaffs_Tnode *tn; + yaffs_Tags localTags; + yaffs_Tags *tags = &localTags; + int theChunk; + int chunkDeleted; + + + if(in->variantType != YAFFS_OBJECT_TYPE_FILE) + { + //T(("Object not a file\n")); + return YAFFS_FAIL; + } + + objId = in->objectId; + fSize = in->variant.fileVariant.fileSize; + nChunks = (fSize + YAFFS_BYTES_PER_CHUNK -1)/YAFFS_BYTES_PER_CHUNK; + + for(chunk = 1; chunk <= nChunks; chunk++) + { + tn = yaffs_FindLevel0Tnode(in->myDev,&in->variant.fileVariant, chunk); + + if(tn) + { + + theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << in->myDev->chunkGroupBits; + + + yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,tags,&chunkDeleted); + if(yaffs_TagsMatch(tags,in->objectId,chunk,chunkDeleted)) + { + // found it; + + } + else + { + //T(("File problem file [%d,%d] NAND %d tags[%d,%d]\n", + // objId,chunk,theChunk,tags->chunkId,tags->objectId); + + failed = 1; + + } + + } + else + { + //T(("No level 0 found for %d\n", chunk)); + } + } + + return failed ? YAFFS_FAIL : YAFFS_OK; +} + +#endif + +static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan) +{ + yaffs_Tnode *tn; + yaffs_Device *dev = in->myDev; + int existingChunk; + yaffs_Tags existingTags; + yaffs_Tags newTags; + unsigned existingSerial, newSerial; + + int newChunkDeleted; + + + tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode); + if(!tn) + { + return YAFFS_FAIL; + } + + existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK]; + + if(inScan) + { + // If we're scanning then we need to test for duplicates + // NB This does not need to be efficient since it should only ever + // happen when the power fails during a write, then only one + // chunk should ever be affected. + + + if(existingChunk != 0) + { + // NB Right now existing chunk will not be real chunkId if the device >= 32MB + // thus we have to do a FindChunkInFile to get the real chunk id. + // + // We have a duplicate now we need to decide which one to use + // To do this we get both sets of tags and compare serial numbers. + yaffs_ReadChunkTagsFromNAND(dev,chunkInNAND, &newTags,&newChunkDeleted); + + + // Do a proper find + existingChunk = yaffs_FindChunkInFile(in,chunkInInode, &existingTags); + + if(existingChunk <=0) + { + //Hoosterman - how did this happen? + // todo + } + + + // NB The deleted flags should be false, otherwise the chunks will + // not be loaded during a scan + + newSerial = newTags.serialNumber; + existingSerial = existingTags.serialNumber; + + if( existingChunk <= 0 || + ((existingSerial+1) & 3) == newSerial) + { + // Use new + // Delete the old one and drop through to update the tnode + yaffs_DeleteChunk(dev,existingChunk); + } + else + { + // Use existing. + // Delete the new one and return early so that the tnode isn't changed + yaffs_DeleteChunk(dev,chunkInNAND); + return YAFFS_OK; + } + } + + } + + if(existingChunk == 0) + { + in->nDataChunks++; + } + + tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = (chunkInNAND >> dev->chunkGroupBits); + + return YAFFS_OK; +} + + + +int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer) +{ + int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL); + + if(chunkInNAND >= 0) + { + return yaffs_ReadChunkFromNAND(in->myDev,chunkInNAND,buffer,NULL,1); + } + else + { + return 0; + } + +} + + +static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId) +{ + int block; + int page; + yaffs_Spare spare; + yaffs_BlockInfo *bi; + + if(chunkId <= 0) return; + + block = chunkId / YAFFS_CHUNKS_PER_BLOCK; + page = chunkId % YAFFS_CHUNKS_PER_BLOCK; + yaffs_SpareInitialise(&spare); + + spare.pageStatus = 0; // To mark it as deleted. + + + yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare); + yaffs_HandleUpdateChunk(dev,chunkId,&spare); + bi = yaffs_GetBlockInfo(dev,block); + + + // Pull out of the management area. + // If the whole block became dirty, this will kick off an erasure. + if( bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || + bi->blockState == YAFFS_BLOCK_STATE_FULL) + { + dev->nFreeChunks++; + + bi->pageBits &= ~(1 << page); + bi->pagesInUse--; + + if(bi->pagesInUse == 0 && + bi->blockState == YAFFS_BLOCK_STATE_FULL) + { + yaffs_BlockBecameDirty(dev,block); + } + + } + else + { + // T(("Bad news deleting chunk %d\n",chunkId)); + } + +} + + + + +int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *buffer,int nBytes,int useReserve) +{ + // Find old chunk Need to do this to get serial number + // Write new one and patch into tree. + // Invalidate old tags. + + int prevChunkId; + yaffs_Tags prevTags; + + int newChunkId; + yaffs_Tags newTags; + + yaffs_Device *dev = in->myDev; + + yaffs_CheckGarbageCollection(dev); + + // Get the previous chunk at this location in the file if it exists + prevChunkId = yaffs_FindChunkInFile(in,chunkInInode,&prevTags); + + // Set up new tags + newTags.chunkId = chunkInInode; + newTags.objectId = in->objectId; + newTags.serialNumber = (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1; + newTags.byteCount = nBytes; + newTags.unusedStuff = 0xFFFFFFFF; + + yaffs_CalcTagsECC(&newTags); + + + #if 0 + // Create new chunk in NAND + newChunkId = yaffs_AllocatePage(dev,useReserve); + + + if(newChunkId >= 0) + { + + + yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,buffer,&newTags); + + yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId); + + + if(prevChunkId >= 0) + { + yaffs_DeleteChunk(dev,prevChunkId); + + } + + yaffs_CheckFileSanity(in); + + return newChunkId; + } + + + return -1; +#else + + newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve); + if(newChunkId >= 0) + { + yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0); + + + if(prevChunkId >= 0) + { + yaffs_DeleteChunk(dev,prevChunkId); + + } + + yaffs_CheckFileSanity(in); + } + return newChunkId; + +#endif + + + +} + + +// UpdateObjectHeader updates the header on NAND for an object. +// If name is not NULL, then that new name is used. +// +int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force) +{ + + yaffs_Device *dev = in->myDev; + + int prevChunkId; + + int newChunkId; + yaffs_Tags newTags; + __u8 bufferNew[YAFFS_BYTES_PER_CHUNK]; + __u8 bufferOld[YAFFS_BYTES_PER_CHUNK]; + + yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew; + yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld; + + yaffs_INVALIDATECHUNKCACHE(in); + + if(!in->fake || force) + { + + yaffs_CheckGarbageCollection(dev); + + memset(bufferNew,0xFF,YAFFS_BYTES_PER_CHUNK); + + prevChunkId = in->chunkId; + + if(prevChunkId >= 0) + { + yaffs_ReadChunkFromNAND(dev,prevChunkId,bufferOld,NULL,1); + } + + // Header data + oh->type = in->variantType; + + oh->st_mode = in->st_mode; + oh->st_uid = in->st_uid; + oh->st_gid = in->st_gid; + oh->st_atime = in->st_atime; + oh->st_mtime = in->st_mtime; + oh->st_ctime = in->st_ctime; + oh->st_rdev = in->st_rdev; + + if(in->parent) + { + oh->parentObjectId = in->parent->objectId; + } + else + { + oh->parentObjectId = 0; + } + + oh->sum = in->sum; + if(name && *name) + { + memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1); + strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH); + } + else if(prevChunkId) + { + memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1); + } + else + { + memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1); + } + + switch(in->variantType) + { + case YAFFS_OBJECT_TYPE_UNKNOWN: + // Should not happen + break; + case YAFFS_OBJECT_TYPE_FILE: + oh->fileSize = in->variant.fileVariant.fileSize; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId; + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + // Do nothing + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + // Do nothing + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH); + oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; + break; + } + + // Tags + in->serial++; + newTags.chunkId = 0; + newTags.objectId = in->objectId; + newTags.serialNumber = in->serial; + newTags.byteCount = 0xFFFFFFFF; + newTags.unusedStuff = 0xFFFFFFFF; + + yaffs_CalcTagsECC(&newTags); + + + +#if 0 + // Create new chunk in NAND + newChunkId = yaffs_AllocatePage(dev,1); + + if(newChunkId >= 0) + { + + yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,bufferNew,&newTags); + + in->chunkId = newChunkId; + + if(prevChunkId >= 0) + { + yaffs_DeleteChunk(dev,prevChunkId); + } + + in->dirty = 0; + return newChunkId; + } + + return -1; +#else + // Create new chunk in NAND + newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1); + + if(newChunkId >= 0) + { + + in->chunkId = newChunkId; + + if(prevChunkId >= 0) + { + yaffs_DeleteChunk(dev,prevChunkId); + } + + in->dirty = 0; + } + + return newChunkId; + +#endif + } + return 0; +} + + +/////////////////////// Short Read Cache //////////////////////////////// +// In many siturations where there is no high level buffering a lot of +// reads might be short sequential reads. eg. scanning a jpeg file. +// In these cases, a shoprt read cache can provide a huge perfomance benefit +// with dumb-as-a-rock code. +// There are a limited number (~10) of cache chunks per device + + +#ifdef CONFIG_YAFFS_SHORT_OP_CACHE + +// Invalidate all the cache pages associated with this object +// Do this whenever ther file is modified... dumb as a rock remember! +static void yaffs_InvalidateChunkCache(yaffs_Object *in) +{ + int i; + yaffs_Device *dev = in->myDev; + int id = in->objectId; + + for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++) + { + if(dev->srCache[i].objectId == id) + { + dev->srCache[i].objectId = 0; + } + } +} + + +// Grab us a chunk for use. +// First look for an empty one. +// Then look for the least recently used one. +static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev) +{ + int i; + int usage; + int theOne; + + for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++) + { + if(dev->srCache[i].objectId == 0) + { + //T(("Grabbing empty %d\n",i)); + + return &dev->srCache[i]; + } + } + + + usage = dev->srCache[i].lastUse; + theOne = 0; + + for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++) + { + if(dev->srCache[i].lastUse < usage) + { + usage = dev->srCache[i].lastUse; + theOne = i; + } + } + + //T(("Grabbing non-empty %d\n",theOne)); + return &dev->srCache[theOne]; + +} + + +// Find a cached chunk +static yaffs_ChunkCache *yaffs_FindChunkCache(yaffs_Device *dev, int objectId, int chunkId) +{ + int i; +; + + for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++) + { + if(dev->srCache[i].objectId == objectId && + dev->srCache[i].chunkId == chunkId) + { + dev->cacheHits++; + + return &dev->srCache[i]; + } + } + + return NULL; +} + +// Mark the chunk for the least recently used algorithym +static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache) +{ + if( dev->srLastUse < 0 || + dev->srLastUse > 1000000) + { + // Reset the cache usages + int i; + for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++) + { + dev->srCache[i].lastUse = 0; + } + dev->srLastUse = 0; + } + + dev->srLastUse++; + + cache->lastUse = dev->srLastUse; + +} + + + +#endif + + + +///////////////////////// File read/write /////////////////////////////// +// Read and write have very similar structures. +// In general the read/write has three parts to it +// * An incomplete chunk to start with (if the read/write is not chunk-aligned) +// * Some complete chunks +// * An incomplete chunk to end off with +// +// Curve-balls: the first chunk might also be the last chunk. + +int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes) +{ + +// yaffs_Device *dev = in->myDev; + +#ifdef CONFIG_YAFFS_SHORT_OP_CACHE + yaffs_ChunkCache *cache; +#else + __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; +#endif + + int chunk; + int start; + int nToCopy; + int n = nBytes; + int nDone = 0; + + while(n > 0) + { + chunk = offset / YAFFS_BYTES_PER_CHUNK + 1; // The first chunk is 1 + start = offset % YAFFS_BYTES_PER_CHUNK; + + // OK now check for the curveball where the start and end are in + // the same chunk. + if( (start + n) < YAFFS_BYTES_PER_CHUNK) + { + nToCopy = n; + } + else + { + nToCopy = YAFFS_BYTES_PER_CHUNK - start; + } + + if(nToCopy != YAFFS_BYTES_PER_CHUNK) + { + // An incomplete start or end chunk (or maybe both start and end chunk) +#ifdef CONFIG_YAFFS_SHORT_OP_CACHE + // If we can't find the data in the cache, then load it up. + cache = yaffs_FindChunkCache(in->myDev,in->objectId,chunk); + if(!cache) + { + cache = yaffs_GrabChunkCache(in->myDev); + cache->objectId = in->objectId; + cache->chunkId = chunk; + yaffs_ReadChunkDataFromObject(in,chunk,cache->data); + } + + yaffs_UseChunkCache(in->myDev,cache); + + memcpy(buffer,&cache->data[start],nToCopy); +#else + // Read into the local buffer then copy... + yaffs_ReadChunkDataFromObject(in,chunk,localBuffer); + memcpy(buffer,&localBuffer[start],nToCopy); +#endif + } + else + { + // A full chunk. Read directly into the supplied buffer. + yaffs_ReadChunkDataFromObject(in,chunk,buffer); + } + + n -= nToCopy; + offset += nToCopy; + buffer += nToCopy; + nDone += nToCopy; + + } + + return nDone; +} + + +int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes) +{ + __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; + + int chunk; + int start; + int nToCopy; + int n = nBytes; + int nDone = 0; + int nToWriteBack; + int endOfWrite = offset+nBytes; + int chunkWritten = 0; + + yaffs_INVALIDATECHUNKCACHE(in); + + while(n > 0 && chunkWritten >= 0) + { + chunk = offset / YAFFS_BYTES_PER_CHUNK + 1; + start = offset % YAFFS_BYTES_PER_CHUNK; + + + // OK now check for the curveball where the start and end are in + // the same chunk. + if( (start + n) < YAFFS_BYTES_PER_CHUNK) + { + nToCopy = n; + nToWriteBack = (start + n); + } + else + { + nToCopy = YAFFS_BYTES_PER_CHUNK - start; + nToWriteBack = YAFFS_BYTES_PER_CHUNK; + } + + if(nToCopy != YAFFS_BYTES_PER_CHUNK) + { + // An incomplete start or end chunk (or maybe both start and end chunk) + // Read into the local buffer then copy, then copy over and write back. + + yaffs_ReadChunkDataFromObject(in,chunk,localBuffer); + + memcpy(&localBuffer[start],buffer,nToCopy); + + chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0); + + //T(("Write with readback to chunk %d %d\n",chunk,chunkWritten)); + + } + else + { + // A full chunk. Write directly from the supplied buffer. + chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0); + //T(("Write to chunk %d %d\n",chunk,chunkWritten)); + } + + if(chunkWritten >= 0) + { + n -= nToCopy; + offset += nToCopy; + buffer += nToCopy; + nDone += nToCopy; + } + + } + + // Update file object + + if(endOfWrite > in->variant.fileVariant.fileSize) + { + in->variant.fileVariant.fileSize = endOfWrite; + } + + in->dirty = 1; + /*in->st_mtime = CURRENT_TIME; only update in flush*/ + + return nDone; +} + + +int yaffs_ResizeFile(yaffs_Object *in, int newSize) +{ + int i; + int chunkId; + int oldFileSize = in->variant.fileVariant.fileSize; + int sizeOfPartialChunk = newSize % YAFFS_BYTES_PER_CHUNK; + + yaffs_Device *dev = in->myDev; + + __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; + + yaffs_INVALIDATECHUNKCACHE(in); + + if(in->variantType != YAFFS_OBJECT_TYPE_FILE) + { + return yaffs_GetFileSize(in); + } + + if(newSize < oldFileSize) + { + + int lastDel = 1 + (oldFileSize-1)/YAFFS_BYTES_PER_CHUNK; + + int startDel = 1 + (newSize + YAFFS_BYTES_PER_CHUNK - 1)/ + YAFFS_BYTES_PER_CHUNK; + + // Delete backwards so that we don't end up with holes if + // power is lost part-way through the operation. + for(i = lastDel; i >= startDel; i--) + { + // NB this could be optimised somewhat, + // eg. could retrieve the tags and write them without + // using yaffs_DeleteChunk + + chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL); + if(chunkId <= 0 || chunkId >= (dev->endBlock * 32)) + { + //T(("Found daft chunkId %d for %d\n",chunkId,i)); + } + else + { + in->nDataChunks--; + yaffs_DeleteChunk(dev,chunkId); + } + } + + + if(sizeOfPartialChunk != 0) + { + int lastChunk = 1+ newSize/YAFFS_BYTES_PER_CHUNK; + + // Got to read and rewrite the last chunk with its new size. + yaffs_ReadChunkDataFromObject(in,lastChunk,localBuffer); + + yaffs_WriteChunkDataToObject(in,lastChunk,localBuffer,sizeOfPartialChunk,1); + + } + + in->variant.fileVariant.fileSize = newSize; + + yaffs_PruneFileStructure(dev,&in->variant.fileVariant); + + return newSize; + + } + else + { + return oldFileSize; + } +} + + +loff_t yaffs_GetFileSize(yaffs_Object *obj) +{ + obj = yaffs_GetEquivalentObject(obj); + + switch(obj->variantType) + { + case YAFFS_OBJECT_TYPE_FILE: + return obj->variant.fileVariant.fileSize; + case YAFFS_OBJECT_TYPE_SYMLINK: + return strlen(obj->variant.symLinkVariant.alias); + default: + return 0; + } +} + + + +// yaffs_FlushFile() updates the file's +// objectId in NAND + +int yaffs_FlushFile(yaffs_Object *in) +{ + int retVal; + if(in->dirty) + { + //T(("flushing object header\n")); + + in->st_mtime = CURRENT_TIME; + + retVal = yaffs_UpdateObjectHeader(in,NULL,0); + } + else + { + retVal = YAFFS_OK; + } + + return retVal; + +} + + +static int yaffs_DoGenericObjectDeletion(yaffs_Object *in) +{ + yaffs_INVALIDATECHUNKCACHE(in); + yaffs_RemoveObjectFromDirectory(in); + yaffs_DeleteChunk(in->myDev,in->chunkId); +#if __KERNEL__ + if(in->myInode) + { + in->myInode->u.generic_ip = NULL; + in->myInode = 0; + } +#endif + yaffs_FreeObject(in); + return YAFFS_OK; + +} + +// yaffs_DeleteFile deletes the whole file data +// and the inode associated with the file. +// It does not delete the links associated with the file. +static int yaffs_UnlinkFile(yaffs_Object *in) +{ + +#ifdef CONFIG_YAFFS_DISABLE_BACKGROUND_DELETION + // Delete the file data & tnodes +#if 0 + yaffs_ResizeFile(in,0); +#else + yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL); + +#endif + + yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top); + + return yaffs_DoGenericObjectDeletion(in); +#else + int retVal; + retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL); + if(retVal == YAFFS_OK) + { + in->unlinked = 1; + in->renameAllowed = 0; + } + return retVal; + + +#endif +} + +int yaffs_DeleteFile(yaffs_Object *in) +{ + int retVal = YAFFS_OK; + if(!in->unlinked) + { + retVal = yaffs_UnlinkFile(in); + } + if(retVal == YAFFS_OK && in->unlinked) + { + in->deleted = 1; + } + return in->deleted ? YAFFS_OK : YAFFS_FAIL; +} + +static int yaffs_DeleteDirectory(yaffs_Object *in) +{ + //First check that the directory is empty. + if(list_empty(&in->variant.directoryVariant.children)) + { + return yaffs_DoGenericObjectDeletion(in); + } + + return YAFFS_FAIL; + +} + +static int yaffs_DeleteSymLink(yaffs_Object *in) +{ + YFREE(in->variant.symLinkVariant.alias); + + return yaffs_DoGenericObjectDeletion(in); +} + +static int yaffs_DeleteHardLink(yaffs_Object *in) +{ + // remove this hardlink from the list assocaited with the equivalent + // object + list_del(&in->hardLinks); + return yaffs_DoGenericObjectDeletion(in); +} + + +static int yaffs_UnlinkWorker(yaffs_Object *obj) +{ + + + if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) + { + return yaffs_DeleteHardLink(obj); + } + else if(!list_empty(&obj->hardLinks)) + { +#if 0 + // Curve ball: We're unlinking an object that has a hardlink. + // Therefore we can't really delete the object. + // Instead, we do the following: + // - Select a hardlink. + // - Re-type a hardlink as the equivalent object and populate the fields, including the + // objectId. Updating the object id is important so that all the hardlinks do not need + // to be rewritten. + // - Update the equivalet object pointers. + // - Delete all object. + + yaffs_Object *hl; + struct list_head *i; + + + yaffs_RemoveObjectFromDirectory(obj); + + + + hl = list_entry(obj->hardLinks.next, yaffs_Object,hardLinks); + + hl->dirty = 1; + hl->st_mode = obj->st_mode; + hl->st_uid = obj->st_uid; + hl->st_gid = obj->st_gid; + hl->st_atime = obj->st_atime; + hl->st_mtime = obj->st_mtime; + hl->st_ctime = obj->st_ctime; + hl->st_rdev = obj->st_rdev; + + hl->variantType = obj->variantType; + + switch(hl->variantType) + { + case YAFFS_OBJECT_TYPE_FILE: + case YAFFS_OBJECT_TYPE_SYMLINK: + case YAFFS_OBJECT_TYPE_SPECIAL: + // These types are OK to just copy across. + hl->variant = obj->variant; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + // Fix the list up + list_add(&hl->variant.directoryVariant.children, + &obj->variant.directoryVariant.children); + list_del(&obj->variant.directoryVariant.children); + + // Now change all the directory children to point to the new parent. + list_for_each(i,&hl->variant.directoryVariant.children) + { + list_entry(i,yaffs_Object,siblings)->parent = hl; + } + break; + + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_UNKNOWN: + // Should not be either of these types. + } + + // Now fix up the hardlink chain + list_del(&obj->hardLinks); + + list_for_each(i,&hl->hardLinks) + { + list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObject = hl; + list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObjectId = hl->objectId; + } + + // Now fix up the hash links. + yaffs_UnhashObject(hl); + hl->objectId = obj->objectId; + yaffs_HashObject(hl); + + // Update the hardlink which has become an object + yaffs_UpdateObjectHeader(hl,NULL,0); + + // Finally throw away the deleted object + yaffs_DeleteChunk(obj->myDev,obj->chunkId); + yaffs_FreeObject(obj); + + return YAFFS_OK; +#else + // Curve ball: We're unlinking an object that has a hardlink. + // + // This problem arises because we are not strictly following + // The Linux link/inode model. + // + // We can't really delete the object. + // Instead, we do the following: + // - Select a hardlink. + // - Unhook it from the hard links + // - Unhook it from its parent directory (so that the rename can work) + // - Rename the object to the hardlink's name. + // - Delete the hardlink + + + yaffs_Object *hl; + int retVal; + char name[YAFFS_MAX_NAME_LENGTH+1]; + + hl = list_entry(obj->hardLinks.next,yaffs_Object,hardLinks); + + list_del_init(&hl->hardLinks); + list_del_init(&hl->siblings); + + yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1); + + retVal = yaffs_ChangeObjectName(obj, hl->parent, name); + + if(retVal == YAFFS_OK) + { + retVal = yaffs_DoGenericObjectDeletion(hl); + } + return retVal; + +#endif + + + } + else + { + switch(obj->variantType) + { + case YAFFS_OBJECT_TYPE_FILE: + return yaffs_UnlinkFile(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + return yaffs_DeleteDirectory(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + return yaffs_DeleteSymLink(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_UNKNOWN: + default: + return YAFFS_FAIL; + } + } +} + +int yaffs_Unlink(yaffs_Object *dir, const char *name) +{ + yaffs_Object *obj; + + obj = yaffs_FindObjectByName(dir,name); + + if(obj && obj->unlinkAllowed) + { + return yaffs_UnlinkWorker(obj); + } + + return YAFFS_FAIL; + +} + +//////////////// Initialisation Scanning ///////////////// + + + +// For now we use the SmartMedia check. +// We look at the blockStatus byte in the first two chunks +// These must be 0xFF to pass as OK. +// todo: this function needs to be modifyable foir different NAND types +// and different chunk sizes. Suggest make this into a per-device configurable +// function. +static int yaffs_IsBlockBad(yaffs_Device *dev, int blk) +{ + yaffs_Spare spare; + + yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK,NULL,&spare,1); +#if 1 + if(yaffs_CountBits(spare.blockStatus) < 7) + { + return 1; + } +#else + if(spare.blockStatus != 0xFF) + { + return 1; + } +#endif + yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK + 1,NULL,&spare,1); + +#if 1 + if(yaffs_CountBits(spare.blockStatus) < 7) + { + return 1; + } +#else + if(spare.blockStatus != 0xFF) + { + return 1; + } +#endif + + return 0; + +} + +static int yaffs_Scan(yaffs_Device *dev) +{ + yaffs_Spare spare; + yaffs_Tags tags; + int blk; + int chunk; + int c; + int deleted; + yaffs_BlockState state; + yaffs_Object *hardList = NULL; + yaffs_Object *hl; + yaffs_BlockInfo *bi; + + + yaffs_ObjectHeader *oh; + yaffs_Object *in; + yaffs_Object *parent; + + __u8 chunkData[YAFFS_BYTES_PER_CHUNK]; + + + // Scan all the blocks... + + for(blk = dev->startBlock; blk <= dev->endBlock; blk++) + { + deleted = 0; + bi = yaffs_GetBlockInfo(dev,blk); + bi->pageBits = 0; + bi->pagesInUse = 0; + state = YAFFS_BLOCK_STATE_SCANNING; + + + if(yaffs_IsBlockBad(dev,blk)) + { + state = YAFFS_BLOCK_STATE_DEAD; + T((TSTR("block %d is bad" TENDSTR),blk)); + } + + // Read each chunk in the block. + + for(c = 0; c < YAFFS_CHUNKS_PER_BLOCK && + state == YAFFS_BLOCK_STATE_SCANNING; c++) + { + // Read the spare area and decide what to do + chunk = blk * YAFFS_CHUNKS_PER_BLOCK + c; + + yaffs_ReadChunkFromNAND(dev,chunk,NULL,&spare,1); + + + // This block looks ok, now what's in this chunk? + yaffs_GetTagsFromSpare(&spare,&tags); + + if(yaffs_CountBits(spare.pageStatus) < 6) + { + // A deleted chunk + deleted++; + dev->nFreeChunks ++; + //T((" %d %d deleted\n",blk,c)); + } + else if(tags.objectId == YAFFS_UNUSED_OBJECT_ID) + { + // An unassigned chunk in the block + // This means that either the block is empty or + // this is the one being allocated from + + if(c == 0) + { + // the block is unused + state = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + } + else + { + // this is the block being allocated from + T((TSTR(" Allocating from %d %d" TENDSTR),blk,c)); + state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->allocationBlock = blk; + dev->allocationPage = c; + } + + dev->nFreeChunks += (YAFFS_CHUNKS_PER_BLOCK - c); + } + else if(tags.chunkId > 0) + { + int endpos; + // A data chunk. + bi->pageBits |= (1 << c); + bi->pagesInUse++; + + in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE); + // PutChunkIntoFIle checks for a clash (two data chunks with + // the same chunkId). + yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1); + endpos = (tags.chunkId - 1)* YAFFS_BYTES_PER_CHUNK + tags.byteCount; + if(in->variant.fileVariant.scannedFileSize variant.fileVariant.scannedFileSize = endpos; +#ifndef CONFIG_YAFFS_USE_HEADER_FILE_SIZE + in->variant.fileVariant.fileSize = + in->variant.fileVariant.scannedFileSize; +#endif + + } + //T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId)); + } + else + { + // chunkId == 0, so it is an ObjectHeader. + // Thus, we read in the object header and make the object + bi->pageBits |= (1 << c); + bi->pagesInUse++; + + yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL,1); + + oh = (yaffs_ObjectHeader *)chunkData; + + in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type); + + if(in->valid) + { + // We have already filled this one. We have a duplicate and need to resolve it. + + unsigned existingSerial = in->serial; + unsigned newSerial = tags.serialNumber; + + if(((existingSerial+1) & 3) == newSerial) + { + // Use new one - destroy the exisiting one + yaffs_DeleteChunk(dev,in->chunkId); + in->valid = 0; + } + else + { + // Use existing - destroy this one. + yaffs_DeleteChunk(dev,chunk); + } + } + + if(!in->valid && + (tags.objectId == YAFFS_OBJECTID_ROOT || + tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) + { + // We only load some info, don't fiddle with directory structure + in->valid = 1; + in->variantType = oh->type; + + in->st_mode = oh->st_mode; + in->st_uid = oh->st_uid; + in->st_gid = oh->st_gid; + in->st_atime = oh->st_atime; + in->st_mtime = oh->st_mtime; + in->st_ctime = oh->st_ctime; + in->st_rdev = oh->st_rdev; + in->chunkId = chunk; + + } + else if(!in->valid) + { + // we need to load this info + + in->valid = 1; + in->variantType = oh->type; + + in->st_mode = oh->st_mode; + in->st_uid = oh->st_uid; + in->st_gid = oh->st_gid; + in->st_atime = oh->st_atime; + in->st_mtime = oh->st_mtime; + in->st_ctime = oh->st_ctime; + in->st_rdev = oh->st_rdev; + in->chunkId = chunk; + + in->sum = oh->sum; + in->dirty = 0; + + // directory stuff... + // hook up to parent + + parent = yaffs_FindOrCreateObjectByNumber(dev,oh->parentObjectId,YAFFS_OBJECT_TYPE_DIRECTORY); + if(parent->variantType == YAFFS_OBJECT_TYPE_UNKNOWN) + { + // Set up as a directory + parent->variantType = YAFFS_OBJECT_TYPE_DIRECTORY; + INIT_LIST_HEAD(&parent->variant.directoryVariant.children); + } + else if(parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) + { + // Hoosterman, another problem.... + // We're trying to use a non-directory as a directory + // Todo ... handle + } + + yaffs_AddObjectToDirectory(parent,in); + if(parent == dev->unlinkedDir) + { + in->deleted = 1; // If it is unlinked at start up then it wants deleting + dev->nDeletedFiles++; + } + + // Note re hardlinks. + // Since we might scan a hardlink before its equivalent object is scanned + // we put them all in a list. + // After scanning is complete, we should have all the objects, so we run through this + // list and fix up all the chains. + + switch(in->variantType) + { + case YAFFS_OBJECT_TYPE_UNKNOWN: // Todo got a problem + break; + case YAFFS_OBJECT_TYPE_FILE: +#ifdef CONFIG_YAFFS_USE_HEADER_FILE_SIZE + in->variant.fileVariant.fileSize = oh->fileSize; +#endif + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId; + (yaffs_Object *)(in->hardLinks.next) = hardList; + hardList = in; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: // Do nothing + break; + case YAFFS_OBJECT_TYPE_SPECIAL: // Do nothing + break; + case YAFFS_OBJECT_TYPE_SYMLINK: // Do nothing + in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias); + break; + } + //T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType)); + } + } + } + + if(state == YAFFS_BLOCK_STATE_SCANNING) + { + // If we got this far while scanning, then the block is fully allocated. + state = YAFFS_BLOCK_STATE_FULL; + } + + bi->blockState = state; + + // Now let's see if it was dirty + if( bi->pagesInUse == 0 && + bi->blockState == YAFFS_BLOCK_STATE_FULL) + { + yaffs_BlockBecameDirty(dev,blk); + } + + } + + // Fix up the hard link chains. + // We should now have scanned all the objects, now it's time to add these + // hardlinks. + while(hardList) + { + hl = hardList; + hardList = (yaffs_Object *)(hardList->hardLinks.next); + + in = yaffs_FindObjectByNumber(dev,hl->variant.hardLinkVariant.equivalentObjectId); + + if(in) + { + // Add the hardlink pointers + hl->variant.hardLinkVariant.equivalentObject=in; + list_add(&hl->hardLinks,&in->hardLinks); + } + else + { + //Todo Need to report/handle this better. + // Got a problem... hardlink to a non-existant object + hl->variant.hardLinkVariant.equivalentObject=NULL; + INIT_LIST_HEAD(&hl->hardLinks); + + } + + } + + + + return YAFFS_OK; +} + + +////////////////////////// Directory Functions ///////////////////////// + + +static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj) +{ + + if(obj->siblings.prev == NULL) + { + // Not initialised + INIT_LIST_HEAD(&obj->siblings); + + } + else if(!list_empty(&obj->siblings)) + { + // If it is holed up somewhere else, un hook it + list_del_init(&obj->siblings); + } + // Now add it + list_add(&obj->siblings,&directory->variant.directoryVariant.children); + obj->parent = directory; + + if(directory == obj->myDev->unlinkedDir) + { + obj->unlinked = 1; + obj->renameAllowed = 0; + } +} + +static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj) +{ + list_del_init(&obj->siblings); + obj->parent = NULL; +} + +yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory,const char *name) +{ + int sum; + + struct list_head *i; + char buffer[YAFFS_MAX_NAME_LENGTH+1]; + + yaffs_Object *l; + + sum = yaffs_CalcNameSum(name); + + list_for_each(i,&directory->variant.directoryVariant.children) + { + l = list_entry(i, yaffs_Object,siblings); + + // Special case for lost-n-found + if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND) + { + if(yaffs_strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0) + { + return l; + } + } + else if(yaffs_SumCompare(l->sum, sum)) + { + // Do a real check + yaffs_GetObjectName(l,buffer,YAFFS_MAX_NAME_LENGTH); + if(yaffs_strcmp(name,buffer) == 0) + { + return l; + } + + + } + } + + return NULL; +} + + +int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *)) +{ + struct list_head *i; + yaffs_Object *l; + + + list_for_each(i,&theDir->variant.directoryVariant.children) + { + l = list_entry(i, yaffs_Object,siblings); + if(!fn(l)) + { + return YAFFS_FAIL; + } + } + + return YAFFS_OK; + +} + + +// GetEquivalentObject dereferences any hard links to get to the +// actual object. + +yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj) +{ + if(obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) + { + // We want the object id of the equivalent object, not this one + obj = obj->variant.hardLinkVariant.equivalentObject; + } + return obj; + +} + +int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize) +{ + memset(name,0,buffSize); + + if(obj->objectId == YAFFS_OBJECTID_LOSTNFOUND) + { + strncpy(name,YAFFS_LOSTNFOUND_NAME,buffSize - 1); + } + else if(obj->chunkId <= 0) + { + char locName[20]; + // make up a name + sprintf(locName,"%s%d",YAFFS_LOSTNFOUND_PREFIX,obj->objectId); + strncpy(name,locName,buffSize - 1); + + } + else + { + __u8 buffer[YAFFS_BYTES_PER_CHUNK]; + yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer; + + memset(buffer,0,YAFFS_BYTES_PER_CHUNK); + + if(obj->chunkId >= 0) + { + yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL,1); + } + strncpy(name,oh->name,buffSize - 1); + } + + return strlen(name); +} + +int yaffs_GetObjectFileLength(yaffs_Object *obj) +{ + + // Dereference any hard linking + obj = yaffs_GetEquivalentObject(obj); + + if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) + { + return obj->variant.fileVariant.fileSize; + } + if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) + { + return strlen(obj->variant.symLinkVariant.alias); + } + else + { + // Only a directory should drop through to here + return YAFFS_BYTES_PER_CHUNK; + } +} + +int yaffs_GetObjectLinkCount(yaffs_Object *obj) +{ + int count = 0; + struct list_head *i; + + if(!obj->unlinked) + { + count++; // the object itself + } + list_for_each(i,&obj->hardLinks) + { + count++; // add the hard links; + } + return count; + +} + + +int yaffs_GetObjectInode(yaffs_Object *obj) +{ + obj = yaffs_GetEquivalentObject(obj); + + return obj->objectId; +} + +unsigned yaffs_GetObjectType(yaffs_Object *obj) +{ + obj = yaffs_GetEquivalentObject(obj); + + switch(obj->variantType) + { + case YAFFS_OBJECT_TYPE_FILE: return DT_REG; break; + case YAFFS_OBJECT_TYPE_DIRECTORY: return DT_DIR; break; + case YAFFS_OBJECT_TYPE_SYMLINK: return DT_LNK; break; + case YAFFS_OBJECT_TYPE_HARDLINK: return DT_REG; break; + case YAFFS_OBJECT_TYPE_SPECIAL: + if(S_ISFIFO(obj->st_mode)) return DT_FIFO; + if(S_ISCHR(obj->st_mode)) return DT_CHR; + if(S_ISBLK(obj->st_mode)) return DT_BLK; + if(S_ISSOCK(obj->st_mode)) return DT_SOCK; + default: return DT_REG; break; + } +} + +char *yaffs_GetSymlinkAlias(yaffs_Object *obj) +{ + obj = yaffs_GetEquivalentObject(obj); + if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) + { + return yaffs_CloneString(obj->variant.symLinkVariant.alias); + } + else + { + return yaffs_CloneString(""); + } +} + + +int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr) +{ + unsigned int valid = attr->ia_valid; + + if(valid & ATTR_MODE) obj->st_mode = attr->ia_mode; + if(valid & ATTR_UID) obj->st_uid = attr->ia_uid; + if(valid & ATTR_GID) obj->st_gid = attr->ia_gid; + + if(valid & ATTR_ATIME) obj->st_atime = attr->ia_atime; + if(valid & ATTR_CTIME) obj->st_ctime = attr->ia_ctime; + if(valid & ATTR_MTIME) obj->st_mtime = attr->ia_mtime; + + if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size); + + yaffs_UpdateObjectHeader(obj,NULL,1); + + return YAFFS_OK; + +} +int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr) +{ + unsigned int valid = 0; + + attr->ia_mode = obj->st_mode; valid |= ATTR_MODE; + attr->ia_uid = obj->st_uid; valid |= ATTR_UID; + attr->ia_gid = obj->st_gid; valid |= ATTR_GID; + + attr->ia_atime = obj->st_atime; valid |= ATTR_ATIME; + attr->ia_ctime = obj->st_ctime; valid |= ATTR_CTIME; + attr->ia_mtime = obj->st_mtime; valid |= ATTR_MTIME; + + attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE; + + attr->ia_valid = valid; + + return YAFFS_OK; + +} + + + +int yaffs_DumpObject(yaffs_Object *obj) +{ +// __u8 buffer[YAFFS_BYTES_PER_CHUNK]; + char name[257]; +// yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer; + +// memset(buffer,0,YAFFS_BYTES_PER_CHUNK); + +// if(obj->chunkId >= 0) +// { +// yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL); +// } + + yaffs_GetObjectName(obj,name,256); + + YPRINTF(("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n", + obj->objectId,yaffs_GetObjectInode(obj), name, obj->dirty, obj->valid, obj->serial, + obj->sum, obj->chunkId, yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj))); + +#if 0 + YPRINTF(("Object %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d\n", + obj->objectId, oh->name, obj->dirty, obj->valid, obj->serial, + obj->sum, obj->chunkId)); + switch(obj->variantType) + { + case YAFFS_OBJECT_TYPE_FILE: + YPRINTF((" FILE length %d\n",obj->variant.fileVariant.fileSize)); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + YPRINTF((" DIRECTORY\n")); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: //todo + case YAFFS_OBJECT_TYPE_SYMLINK: + case YAFFS_OBJECT_TYPE_UNKNOWN: + default: + } +#endif + + return YAFFS_OK; +} + + +///////////////////////// Initialisation code /////////////////////////// + + + +int yaffs_GutsInitialise(yaffs_Device *dev) +{ + unsigned x; + int bits; + int extraBits; + int nBlocks; + + + + if(!yaffs_CheckStructures()) + { + YPRINTF(("yaffs_CheckStructures failed\n")); + return YAFFS_FAIL; + } + + if(dev->startBlock <= 0 || + (dev->endBlock - dev->startBlock) < 10) + { + YPRINTF(("startBlock %d or endBlock %d invalid\n", + dev->startBlock, dev->endBlock)); + return YAFFS_FAIL; + } + + nBlocks = dev->endBlock - dev->startBlock + 1; + + + + // OK now calculate a few things for the device + // Calculate chunkGroupBits. + // We need to find the next power of 2 > than endBlock + + x = YAFFS_CHUNKS_PER_BLOCK * (dev->endBlock+1); + + for(bits = extraBits = 0; x > 1; bits++) + { + if(x & 1) extraBits++; + x >>= 1; + } + + if(extraBits > 0) bits++; + + + // Level0 Tnodes are 16 bits, so if the bitwidth of the + // chunk range we're using is greater than 16 we need + // to figure out chunk shift and chunkGroupSize + if(bits <= 16) + { + dev->chunkGroupBits = 0; + } + else + { + dev->chunkGroupBits = bits - 16; + } + dev->chunkGroupSize = 1 << dev->chunkGroupBits; + + + // More device initialisation + dev->garbageCollectionRequired = 0; + dev->garbageCollections = 0; + dev->currentDirtyChecker = 0; + dev->bufferedBlock = -1; + dev->doingBufferedBlockRewrite = 0; + dev->blockSelectedForGC = -1; + dev->nDeletedFiles = 0; + + yaffs_InitialiseBlocks(dev,nBlocks); + + yaffs_InitialiseTnodes(dev); + + yaffs_InitialiseObjects(dev); + +#ifdef CONFIG_YAFFS_SHORT_OP_CACHE + { + int i; + for(i=0; i < YAFFS_N_CACHE_CHUNKS; i++) + { + dev->srCache[i].objectId = 0; + dev->srCache[i].lastUse = 0; + } + dev->srLastUse = 0; + } +#endif + + dev->cacheHits = 0; + + + // Initialise the unlinked, root and lost and found directories + dev->lostNFoundDir = dev->rootDir = dev->unlinkedDir = NULL; + + dev->unlinkedDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_UNLINKED, S_IFDIR); + + dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR); + dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_LOSTNFOUND_MODE | S_IFDIR); + yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir); + + + // Now scan the flash. + yaffs_Scan(dev); + + // Zero out stats + dev->nPageReads = 0; + dev->nPageWrites = 0; + dev->nBlockErasures = 0; + dev->nGCCopies = 0; + dev->nRetriedWrites = 0; + dev->nRetiredBlocks = 0; + + + return YAFFS_OK; + +} + +void yaffs_Deinitialise(yaffs_Device *dev) +{ + yaffs_DeinitialiseBlocks(dev); + yaffs_DeinitialiseTnodes(dev); + yaffs_DeinitialiseObjects(dev); + +} + +int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev) +{ + int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS); + + struct list_head *i; + yaffs_Object *l; + + + // To the free chunks add the chunks that are in the deleted unlinked files. + list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children) + { + l = list_entry(i, yaffs_Object,siblings); + if(l->deleted) + { + nFree++; + nFree += l->nDataChunks; + } + } + + + return (nFree < 0) ? 0 : nFree; + +} + + +/////////////////// YAFFS test code ////////////////////////////////// + +#define yaffs_CheckStruct(structure,syze, name) \ + if(sizeof(structure) != syze) \ + { YPRINTF(("%s should be %d but is %d\n",name,syze,sizeof(structure))); \ + return YAFFS_FAIL; \ + } + + +static int yaffs_CheckStructures(void) +{ + yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags") + yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion") + yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare") + yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode") + yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader") + + + return YAFFS_OK; +} + +void yaffs_GutsTest(yaffs_Device *dev) +{ + + if(yaffs_CheckStructures() != YAFFS_OK) + { + YPRINTF(("One or more structures malformed-- aborting\n")); + return; + } + else + { + YPRINTF(("Structures OK\n")); + } + + yaffs_TnodeTest(dev); + yaffs_ObjectTest(dev); +} + + diff --git a/yaffs_guts.h b/yaffs_guts.h index aadfa3f..8553542 100644 --- a/yaffs_guts.h +++ b/yaffs_guts.h @@ -1,478 +1,521 @@ -/* - * YAFFS: Yet another FFS. A NAND-flash specific file system. - * yaffs_guts.h: Configuration etc for yaffs_guts - * - * Copyright (C) 2002 Aleph One Ltd. - * for Toby Churchill Ltd and Brightstar Engineering - * - * Created by Charles Manning - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#ifndef __YAFFS_GUTS_H__ -#define __YAFFS_GUTS_H__ - -#include "devextras.h" - - -#define YAFFS_OK 1 -#define YAFFS_FAIL 0 - -// Y=0x59, A=0x41, S=0x53 -#define YAFFS_MAGIC 0x5941FF53 - -#define YAFFS_NTNODES_LEVEL0 16 -#define YAFFS_TNODES_LEVEL0_BITS 4 -#define YAFFS_TNODES_LEVEL0_MASK 0xf - -#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) -#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) -#define YAFFS_TNODES_INTERNAL_MASK 0x7 -#define YAFFS_TNODES_MAX_LEVEL 6 - -#define YAFFS_BYTES_PER_CHUNK 512 -#define YAFFS_CHUNK_SIZE_SHIFT 9 - -#define YAFFS_BYTES_PER_SPARE 16 - -#define YAFFS_CHUNKS_PER_BLOCK 32 -#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) - -#define YAFFS_MAX_CHUNK_ID 0x000FFFFF - -#define YAFFS_UNUSED_OBJECT_ID 0x0003FFFF - -#define YAFFS_ALLOCATION_NOBJECTS 100 -#define YAFFS_ALLOCATION_NTNODES 100 -#define YAFFS_ALLOCATION_NLINKS 100 - -#define YAFFS_NOBJECT_BUCKETS 256 - - -#define YAFFS_RESERVED_BLOCKS 5 - -#define YAFFS_OBJECT_SPACE 0x40000 -#define YAFFS_MAX_NAME_LENGTH 255 - -#define YAFFS_MAX_ALIAS_LENGTH 159 - -#define YAFFS_OBJECTID_ROOT 1 -#define YAFFS_OBJECTID_LOSTNFOUND 2 - -// Tags structures in RAM -// NB This uses bitfield. Bitfields should not stradle a u32 boundary otherwise -// the structure size will get blown out. - -typedef struct -{ - unsigned chunkId:20; - unsigned serialNumber:2; - unsigned byteCount:10; - unsigned objectId:18; - unsigned ecc:12; - unsigned unusedStuff:2; -} yaffs_Tags; - -typedef union -{ - yaffs_Tags asTags; - __u8 asBytes[8]; -} yaffs_TagsUnion; - - -// Spare structure -typedef struct -{ - __u8 tagByte0; - __u8 tagByte1; - __u8 tagByte2; - __u8 tagByte3; - __u8 pageStatus; // Currently unused, but sort of set aside to distinguish - // unused - vs- used -vs- deleted chunks. We achieve this by - // using the objectId tags. - __u8 blockStatus; - __u8 tagByte4; - __u8 tagByte5; - __u8 ecc1[3]; - __u8 tagByte6; - __u8 tagByte7; - __u8 ecc2[3]; -} yaffs_Spare; - -// Block data in RAM - -typedef enum { - YAFFS_BLOCK_STATE_UddNKNOWN = 0, - YAFFS_BLOCK_STATE_SCANNING, // Used while the block is being scanned. - // NB Don't erase blocks while they're being scanned - - YAFFS_BLOCK_STATE_EMPTY, // This block is empty - - YAFFS_BLOCK_STATE_ALLOCATING, // This block is partially allocated. - // This is the one currently being used for page - // allocation. Should never be more than one of these - - - YAFFS_BLOCK_STATE_FULL, // All the pages in this block have been allocated. - // At least one page holds valid data. - - YAFFS_BLOCK_STATE_DIRTY, // All pages have been allocated and deleted. - // Erase me, reuse me. - - YAFFS_BLOCK_STATE_DEAD = 0x77 // This block has failed and is not in use - -} yaffs_BlockState; - - - - -typedef struct -{ - __u32 pageBits; // bitmap of pages in use - __u8 blockState; // One of the above block states - __u8 pagesInUse; // number of pages in use - __u8 needsRetiring:1; // Data has failed on this block, need to get valid data off - // and retire the block. -} yaffs_BlockInfo; - - -//////////////////// Object structure /////////////////////////// -// This is the object structure as stored on NAND - -typedef enum -{ - YAFFS_OBJECT_TYPE_UNKNOWN, - YAFFS_OBJECT_TYPE_FILE, - YAFFS_OBJECT_TYPE_SYMLINK, - YAFFS_OBJECT_TYPE_DIRECTORY, - YAFFS_OBJECT_TYPE_HARDLINK, - YAFFS_OBJECT_TYPE_SPECIAL -} yaffs_ObjectType; - -typedef struct -{ - yaffs_ObjectType type; - - // Apply to everything - int parentObjectId; - __u16 sum; // checksum of name - char name[YAFFS_MAX_NAME_LENGTH + 1]; - - // Thes following apply to directories, files, symlinks - not hard links - __u32 st_mode; // protection - __u32 st_uid; // user ID of owner - __u32 st_gid; // group ID of owner - __u32 st_atime; // time of last access - __u32 st_mtime; // time of last modification - __u32 st_ctime; // time of last change - - // File size applies to files only - int fileSize; - - // Equivalent object id applies to hard links only. - int equivalentObjectId; - - // Alias is for symlinks only. - char alias[YAFFS_MAX_ALIAS_LENGTH + 1]; - - __u32 st_rdev; // device stuff for block and char devices (maj/min) - // Roowm to grow... - __u32 roomToGrow[12]; - -} yaffs_ObjectHeader; - - - -//////////////////// Tnode /////////////////////////// - -union yaffs_Tnode_union -{ - union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL]; - __u16 level0[YAFFS_NTNODES_LEVEL0]; - -}; - -typedef union yaffs_Tnode_union yaffs_Tnode; - -struct yaffs_TnodeList_struct -{ - struct yaffs_TnodeList_struct *next; - yaffs_Tnode *tnodes; -}; - -typedef struct yaffs_TnodeList_struct yaffs_TnodeList; - - - -/////////////////// Object //////////////////////////////// -// An object can be one of: -// - a directory (no data, has children links -// - a regular file (data.... not prunes :->). -// - a symlink [symbolic link] (the alias). -// - a hard link - - -typedef struct -{ - __u32 fileSize; - __u32 scannedFileSize; - __u32 topLevel; - yaffs_Tnode *top; -} yaffs_FileStructure; - -typedef struct -{ - struct list_head children; // list of child links -} yaffs_DirectoryStructure; - -typedef struct -{ - char *alias; -} yaffs_SymLinkStructure; - -typedef struct -{ - struct yaffs_ObjectStruct *equivalentObject; - __u32 equivalentObjectId; -} yaffs_HardLinkStructure; - -typedef union -{ - yaffs_FileStructure fileVariant; - yaffs_DirectoryStructure directoryVariant; - yaffs_SymLinkStructure symLinkVariant; - yaffs_HardLinkStructure hardLinkVariant; -} yaffs_ObjectVariant; - - -struct yaffs_ObjectStruct -{ - __u8 fake:1; // A fake object has no presence on NAND. - __u8 renameAllowed:1; - __u8 unlinkAllowed:1; - __u8 dirty:1; // the object needs to be written to flash - __u8 valid:1; // When the file system is being loaded up, this - // object might be created before the data - // is available (ie. file data records appear before the header). - __u8 serial; // serial number of chunk in NAND. Store here so we don't have to - // read back the old one to update. - __u16 sum; // sum of the name to speed searching - - struct yaffs_DeviceStruct *myDev; // The device I'm on - - - struct list_head hashLink; // list of objects in this hash bucket - - - struct list_head hardLinks; // all the equivalent hard linked objects - // live on this list - // directory structure stuff - struct yaffs_ObjectStruct *parent; //my parent directory - struct list_head siblings; // siblings in a directory - // also used for linking up the free list - - // Where's my data in NAND? - int chunkId; // where it lives - - int nDataChunks; // really only for debugging. - - __u32 objectId; // the object id value - - - __u32 st_mode; // protection - __u32 st_uid; // user ID of owner - __u32 st_gid; // group ID of owner - __u32 st_atime; // time of last access - __u32 st_mtime; // time of last modification - __u32 st_ctime; // time of last change - __u32 st_rdev; // device stuff for block and char devices - -#ifdef WIN32 - __u32 inUse; -#endif - - - yaffs_ObjectType variantType; - - yaffs_ObjectVariant variant; - -}; - - - -typedef struct yaffs_ObjectStruct yaffs_Object; - - -struct yaffs_ObjectList_struct -{ - yaffs_Object *objects; - struct yaffs_ObjectList_struct *next; -}; - -typedef struct yaffs_ObjectList_struct yaffs_ObjectList; - -typedef struct -{ - struct list_head list; - __u32 count; -} yaffs_ObjectBucket; - - -//////////////////// Device //////////////////////////////// - -struct yaffs_DeviceStruct -{ - // Entry parameters set up way early. Yaffs sets up the rest. - __u32 nBlocks; // Size of whole device in blocks - __u32 startBlock; // Start block we're allowed to use - __u32 endBlock; // End block we're allowed to use - __u16 chunkGroupBits; // 0 for devices <= 32MB. else log2(nchunks) - 16 - __u16 chunkGroupSize; // == 2^^chunkGroupBits - - - void *genericDevice; // Pointer to device context - // On an mtd this holds the mtd pointer. - -#ifdef __KERNEL__ - - struct semaphore sem;// Semaphore for waiting on erasure. - struct semaphore grossLock; // Gross locking semaphore - -#endif - - - // NAND access functions (Must be set before calling YAFFS) - - int (*writeChunkToNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare); - int (*readChunkFromNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare); - int (*eraseBlockInNAND)(struct yaffs_DeviceStruct *dev,int blockInNAND); - int (*initialiseNAND)(struct yaffs_DeviceStruct *dev); - -#ifdef __KERNEL__ - void (*putSuperFunc)(struct super_block *sb); -#endif - - // Runtime parameters. - yaffs_BlockInfo *blockInfo; - int nErasedBlocks; - int allocationBlock; - __u32 allocationPage; - - // Runtime state - int nTnodesCreated; - yaffs_Tnode *freeTnodes; - int nFreeTnodes; - yaffs_TnodeList *allocatedTnodeList; - - - int nObjectsCreated; - yaffs_Object *freeObjects; - int nFreeObjects; - - yaffs_ObjectList *allocatedObjectList; - - yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS]; - - int nFreeChunks; - - int currentDirtyChecker; // Used to find current dirtiest block - - int garbageCollectionRequired; - - // Operations since mount - int nPageWrites; - int nPageReads; - int nBlockErasures; - int nGCCopies; - int nRetriedWrites; - int nRetiredBlocks; - - yaffs_Object *rootDir; - yaffs_Object *lostNFoundDir; - - // Buffer areas for storing data to recover from write failures - __u8 bufferedData[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK]; - yaffs_Spare bufferedSpare[YAFFS_CHUNKS_PER_BLOCK]; - int bufferedBlock; // Which block is buffered here? - int doingBufferedBlockRewrite; - - int blockSelectedForGC; - - -}; - -typedef struct yaffs_DeviceStruct yaffs_Device; - - - -//////////// YAFFS Functions ////////////////// - -int yaffs_GutsInitialise(yaffs_Device *dev); -void yaffs_Deinitialise(yaffs_Device *dev); - -int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev); - - -// Rename -int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName); - -// generic Object functions -int yaffs_Unlink(yaffs_Object *dir, const char *name); - -// Object access functions. -int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize); -int yaffs_GetObjectFileLength(yaffs_Object *obj); -int yaffs_GetObjectInode(yaffs_Object *obj); -unsigned yaffs_GetObjectType(yaffs_Object *obj); -int yaffs_GetObjectLinkCount(yaffs_Object *obj); - -// Change inode attributes -int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr); -int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr); - -// File operations -int yaffs_ReadDataFromFile(yaffs_Object *obj, __u8 *buffer, __u32 offset, int nBytes); -int yaffs_WriteDataToFile(yaffs_Object *obj, const __u8 *buffer, __u32 offset, int nBytes); -int yaffs_ResizeFile(yaffs_Object *obj, int newSize); - -yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid); -int yaffs_FlushFile(yaffs_Object *obj); - - -// Directory operations -yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid); -yaffs_Object *yaffs_FindObjectByName(yaffs_Object *theDir,const char *name); -int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *)); - -yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number); - -// Link operations -yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject); - -yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj); - -// Symlink operations -yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent, const char *name, __u32 mode, __u32 uid, __u32 gid, const char *alias); -char *yaffs_GetSymlinkAlias(yaffs_Object *obj); - -// Special inodes (fifos, sockets and devices) -yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,__u32 rdev); - - -// Special directories -yaffs_Object *yaffs_Root(yaffs_Device *dev); -yaffs_Object *yaffs_LostNFound(yaffs_Device *dev); - - -// Debug dump -int yaffs_DumpObject(yaffs_Object *obj); - - -void yaffs_GutsTest(yaffs_Device *dev); - - -#endif +/* + * YAFFS: Yet another FFS. A NAND-flash specific file system. + * yaffs_guts.h: Configuration etc for yaffs_guts + * + * Copyright (C) 2002 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + */ + +#ifndef __YAFFS_GUTS_H__ +#define __YAFFS_GUTS_H__ + +#include "devextras.h" + + +#define YAFFS_OK 1 +#define YAFFS_FAIL 0 + +// Give us a Y=0x59, +// Give us an A=0x41, +// Give us an FF=0xFF +// Give us an S=0x53 +// And what have we got... +#define YAFFS_MAGIC 0x5941FF53 + +#define YAFFS_NTNODES_LEVEL0 16 +#define YAFFS_TNODES_LEVEL0_BITS 4 +#define YAFFS_TNODES_LEVEL0_MASK 0xf + +#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) +#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) +#define YAFFS_TNODES_INTERNAL_MASK 0x7 +#define YAFFS_TNODES_MAX_LEVEL 6 + +#define YAFFS_BYTES_PER_CHUNK 512 +#define YAFFS_CHUNK_SIZE_SHIFT 9 + +#define YAFFS_BYTES_PER_SPARE 16 + +#define YAFFS_CHUNKS_PER_BLOCK 32 +#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) + +#define YAFFS_MAX_CHUNK_ID 0x000FFFFF + +#define YAFFS_UNUSED_OBJECT_ID 0x0003FFFF + +#define YAFFS_ALLOCATION_NOBJECTS 100 +#define YAFFS_ALLOCATION_NTNODES 100 +#define YAFFS_ALLOCATION_NLINKS 100 + +#define YAFFS_NOBJECT_BUCKETS 256 + + +#define YAFFS_RESERVED_BLOCKS 5 + +#define YAFFS_OBJECT_SPACE 0x40000 +#define YAFFS_MAX_NAME_LENGTH 255 + +#define YAFFS_MAX_ALIAS_LENGTH 159 + +#define YAFFS_OBJECTID_ROOT 1 +#define YAFFS_OBJECTID_LOSTNFOUND 2 +#define YAFFS_OBJECTID_UNLINKED 3 + +#define YAFFS_N_CACHE_CHUNKS 10 + +#ifdef WIN32 + +// Force the short operation cache on for WinCE + +#define CONFIG_YAFFS_SHORT_OP_CACHE +#endif + + +// ChunkCache is used for short read/write operations. +typedef struct +{ + int objectId; + int chunkId; + int lastUse; + int dirty; + __u8 data[YAFFS_BYTES_PER_CHUNK]; +} yaffs_ChunkCache; + +// Tags structures in RAM +// NB This uses bitfield. Bitfields should not stradle a u32 boundary otherwise +// the structure size will get blown out. + +typedef struct +{ + unsigned chunkId:20; + unsigned serialNumber:2; + unsigned byteCount:10; + unsigned objectId:18; + unsigned ecc:12; + unsigned unusedStuff:2; +} yaffs_Tags; + +typedef union +{ + yaffs_Tags asTags; + __u8 asBytes[8]; +} yaffs_TagsUnion; + + +// Spare structure +typedef struct +{ + __u8 tagByte0; + __u8 tagByte1; + __u8 tagByte2; + __u8 tagByte3; + __u8 pageStatus; // set to 0 to delete the chunk + __u8 blockStatus; + __u8 tagByte4; + __u8 tagByte5; + __u8 ecc1[3]; + __u8 tagByte6; + __u8 tagByte7; + __u8 ecc2[3]; +} yaffs_Spare; + +// Block data in RAM + +typedef enum { + YAFFS_BLOCK_STATE_UddNKNOWN = 0, + YAFFS_BLOCK_STATE_SCANNING, // Used while the block is being scanned. + // NB Don't erase blocks while they're being scanned + + YAFFS_BLOCK_STATE_EMPTY, // This block is empty + + YAFFS_BLOCK_STATE_ALLOCATING, // This block is partially allocated. + // This is the one currently being used for page + // allocation. Should never be more than one of these + + + YAFFS_BLOCK_STATE_FULL, // All the pages in this block have been allocated. + // At least one page holds valid data. + + YAFFS_BLOCK_STATE_DIRTY, // All pages have been allocated and deleted. + // Erase me, reuse me. + + YAFFS_BLOCK_STATE_DEAD = 0x77 // This block has failed and is not in use + +} yaffs_BlockState; + + + + +typedef struct +{ + __u32 pageBits; // bitmap of pages in use + __u8 blockState; // One of the above block states + __u8 pagesInUse; // number of pages in use + __u8 needsRetiring:1; // Data has failed on this block, need to get valid data off + // and retire the block. +} yaffs_BlockInfo; + + +//////////////////// Object structure /////////////////////////// +// This is the object structure as stored on NAND + +typedef enum +{ + YAFFS_OBJECT_TYPE_UNKNOWN, + YAFFS_OBJECT_TYPE_FILE, + YAFFS_OBJECT_TYPE_SYMLINK, + YAFFS_OBJECT_TYPE_DIRECTORY, + YAFFS_OBJECT_TYPE_HARDLINK, + YAFFS_OBJECT_TYPE_SPECIAL +} yaffs_ObjectType; + +typedef struct +{ + yaffs_ObjectType type; + + // Apply to everything + int parentObjectId; + __u16 sum; // checksum of name + char name[YAFFS_MAX_NAME_LENGTH + 1]; + + // Thes following apply to directories, files, symlinks - not hard links + __u32 st_mode; // protection + __u32 st_uid; // user ID of owner + __u32 st_gid; // group ID of owner + __u32 st_atime; // time of last access + __u32 st_mtime; // time of last modification + __u32 st_ctime; // time of last change + + // File size applies to files only + int fileSize; + + // Equivalent object id applies to hard links only. + int equivalentObjectId; + + // Alias is for symlinks only. + char alias[YAFFS_MAX_ALIAS_LENGTH + 1]; + + __u32 st_rdev; // device stuff for block and char devices (maj/min) + // Roowm to grow... + __u32 roomToGrow[12]; + +} yaffs_ObjectHeader; + + + +//////////////////// Tnode /////////////////////////// + +union yaffs_Tnode_union +{ + union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL]; + __u16 level0[YAFFS_NTNODES_LEVEL0]; + +}; + +typedef union yaffs_Tnode_union yaffs_Tnode; + +struct yaffs_TnodeList_struct +{ + struct yaffs_TnodeList_struct *next; + yaffs_Tnode *tnodes; +}; + +typedef struct yaffs_TnodeList_struct yaffs_TnodeList; + + + +/////////////////// Object //////////////////////////////// +// An object can be one of: +// - a directory (no data, has children links +// - a regular file (data.... not prunes :->). +// - a symlink [symbolic link] (the alias). +// - a hard link + + +typedef struct +{ + __u32 fileSize; + __u32 scannedFileSize; + __u32 topLevel; + yaffs_Tnode *top; +} yaffs_FileStructure; + +typedef struct +{ + struct list_head children; // list of child links +} yaffs_DirectoryStructure; + +typedef struct +{ + char *alias; +} yaffs_SymLinkStructure; + +typedef struct +{ + struct yaffs_ObjectStruct *equivalentObject; + __u32 equivalentObjectId; +} yaffs_HardLinkStructure; + +typedef union +{ + yaffs_FileStructure fileVariant; + yaffs_DirectoryStructure directoryVariant; + yaffs_SymLinkStructure symLinkVariant; + yaffs_HardLinkStructure hardLinkVariant; +} yaffs_ObjectVariant; + + +struct yaffs_ObjectStruct +{ + __u8 deleted: 1; // This should only apply to unlinked files. + __u8 unlinked: 1; // An unlinked file. The file should be in the unlinked pseudo directory. + __u8 fake:1; // A fake object has no presence on NAND. + __u8 renameAllowed:1; + __u8 unlinkAllowed:1; + __u8 dirty:1; // the object needs to be written to flash + __u8 valid:1; // When the file system is being loaded up, this + // object might be created before the data + // is available (ie. file data records appear before the header). + __u8 serial; // serial number of chunk in NAND. Store here so we don't have to + // read back the old one to update. + __u16 sum; // sum of the name to speed searching + + struct yaffs_DeviceStruct *myDev; // The device I'm on + + + struct list_head hashLink; // list of objects in this hash bucket + + + struct list_head hardLinks; // all the equivalent hard linked objects + // live on this list + // directory structure stuff + struct yaffs_ObjectStruct *parent; //my parent directory + struct list_head siblings; // siblings in a directory + // also used for linking up the free list + + // Where's my data in NAND? + int chunkId; // where it lives + + int nDataChunks; + + __u32 objectId; // the object id value + + + __u32 st_mode; // protection + __u32 st_uid; // user ID of owner + __u32 st_gid; // group ID of owner + __u32 st_atime; // time of last access + __u32 st_mtime; // time of last modification + __u32 st_ctime; // time of last change + __u32 st_rdev; // device stuff for block and char devices + +#ifdef WIN32 + __u32 inUse; +#endif + +#ifdef __KERNEL__ + struct inode *myInode; +#endif + + + + yaffs_ObjectType variantType; + + yaffs_ObjectVariant variant; + +}; + + + +typedef struct yaffs_ObjectStruct yaffs_Object; + + +struct yaffs_ObjectList_struct +{ + yaffs_Object *objects; + struct yaffs_ObjectList_struct *next; +}; + +typedef struct yaffs_ObjectList_struct yaffs_ObjectList; + +typedef struct +{ + struct list_head list; + __u32 count; +} yaffs_ObjectBucket; + + +//////////////////// Device //////////////////////////////// + +struct yaffs_DeviceStruct +{ + // Entry parameters set up way early. Yaffs sets up the rest. + // __u32 nBlocks; // Size of whole device in blocks + __u32 startBlock; // Start block we're allowed to use + __u32 endBlock; // End block we're allowed to use + __u16 chunkGroupBits; // 0 for devices <= 32MB. else log2(nchunks) - 16 + __u16 chunkGroupSize; // == 2^^chunkGroupBits + + + void *genericDevice; // Pointer to device context + // On an mtd this holds the mtd pointer. + +#ifdef __KERNEL__ + + struct semaphore sem;// Semaphore for waiting on erasure. + struct semaphore grossLock; // Gross locking semaphore + +#endif + + + // NAND access functions (Must be set before calling YAFFS) + + int (*writeChunkToNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare); + int (*readChunkFromNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare); + int (*eraseBlockInNAND)(struct yaffs_DeviceStruct *dev,int blockInNAND); + int (*initialiseNAND)(struct yaffs_DeviceStruct *dev); + +#ifdef __KERNEL__ + void (*putSuperFunc)(struct super_block *sb); +#endif + + // Runtime parameters. + yaffs_BlockInfo *blockInfo; + int nErasedBlocks; + int allocationBlock; + __u32 allocationPage; + + // Runtime state + int nTnodesCreated; + yaffs_Tnode *freeTnodes; + int nFreeTnodes; + yaffs_TnodeList *allocatedTnodeList; + + + int nObjectsCreated; + yaffs_Object *freeObjects; + int nFreeObjects; + + yaffs_ObjectList *allocatedObjectList; + + yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS]; + + int nFreeChunks; + + int currentDirtyChecker; // Used to find current dirtiest block + + int garbageCollectionRequired; + + // Operations since mount + int nPageWrites; + int nPageReads; + int nBlockErasures; + int nGCCopies; + int garbageCollections; + int nRetriedWrites; + int nRetiredBlocks; + + yaffs_Object *rootDir; + yaffs_Object *lostNFoundDir; + + // Buffer areas for storing data to recover from write failures + __u8 bufferedData[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK]; + yaffs_Spare bufferedSpare[YAFFS_CHUNKS_PER_BLOCK]; + int bufferedBlock; // Which block is buffered here? + int doingBufferedBlockRewrite; + + int blockSelectedForGC; + +#ifdef CONFIG_YAFFS_SHORT_OP_CACHE + yaffs_ChunkCache srCache[YAFFS_N_CACHE_CHUNKS]; + int srLastUse; +#endif + int cacheHits; + + // Stuff for background deletion and unlinked files. + yaffs_Object *unlinkedDir; // Directory where unlinked and deleted files live. + yaffs_Object *unlinkedDeletion; // Current file being background deleted. + int nDeletedFiles; // Count of files awaiting deletion; + + +}; + +typedef struct yaffs_DeviceStruct yaffs_Device; + + + +//////////// YAFFS Functions ////////////////// + +int yaffs_GutsInitialise(yaffs_Device *dev); +void yaffs_Deinitialise(yaffs_Device *dev); + +int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev); + + +// Rename +int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName); + +// generic Object functions +int yaffs_Unlink(yaffs_Object *dir, const char *name); +int yaffs_DeleteFile(yaffs_Object *obj); + +// Object access functions. +int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize); +int yaffs_GetObjectFileLength(yaffs_Object *obj); +int yaffs_GetObjectInode(yaffs_Object *obj); +unsigned yaffs_GetObjectType(yaffs_Object *obj); +int yaffs_GetObjectLinkCount(yaffs_Object *obj); + +// Change inode attributes +int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr); +int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr); + +// File operations +int yaffs_ReadDataFromFile(yaffs_Object *obj, __u8 *buffer, __u32 offset, int nBytes); +int yaffs_WriteDataToFile(yaffs_Object *obj, const __u8 *buffer, __u32 offset, int nBytes); +int yaffs_ResizeFile(yaffs_Object *obj, int newSize); + +yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid); +int yaffs_FlushFile(yaffs_Object *obj); + + +// Directory operations +yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid); +yaffs_Object *yaffs_FindObjectByName(yaffs_Object *theDir,const char *name); +int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *)); + +yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number); + +// Link operations +yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject); + +yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj); + +// Symlink operations +yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent, const char *name, __u32 mode, __u32 uid, __u32 gid, const char *alias); +char *yaffs_GetSymlinkAlias(yaffs_Object *obj); + +// Special inodes (fifos, sockets and devices) +yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,__u32 rdev); + + +// Special directories +yaffs_Object *yaffs_Root(yaffs_Device *dev); +yaffs_Object *yaffs_LostNFound(yaffs_Device *dev); + + +// Debug dump +int yaffs_DumpObject(yaffs_Object *obj); + + +void yaffs_GutsTest(yaffs_Device *dev); + + +#endif diff --git a/yaffs_mtdif.h b/yaffs_mtdif.h index 6dc2e1b..42b65e2 100644 --- a/yaffs_mtdif.h +++ b/yaffs_mtdif.h @@ -8,7 +8,7 @@ * Created by Charles Manning * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as + * it under the terms of the GNU Lesser General Public License version 2.1 as * published by the Free Software Foundation. * */ diff --git a/yaffs_nandemul.h b/yaffs_nandemul.h index f233408..880d350 100644 --- a/yaffs_nandemul.h +++ b/yaffs_nandemul.h @@ -7,7 +7,7 @@ * Created by Charles Manning * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as + * it under the terms of the GNU Lesser General Public License version 2.1 as * published by the Free Software Foundation. * * yaffs_nandemul.h: Interface to emulated NAND functions diff --git a/yaffsdev b/yaffsdev index b217ad6fd7c425f2717d4fa0b826d8ff227d6deb..3a7e43e15266f58ed026ccec3094a6ffda448bf8 100755 GIT binary patch literal 123432 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 bl-Hf8bXx1{U6?SVQRTxt&%(A@R5kN|-<`pB literal 118448 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%0Atyq=Jr|^`_`v< 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 diff --git a/yaffsdev.c b/yaffsdev.c index ff4b7e6..afb9354 100644 --- a/yaffsdev.c +++ b/yaffsdev.c @@ -218,7 +218,13 @@ void TestTime(yaffs_Device *dev) else written = yaffs_WriteDataToFile(f,testStr2,i,strlen(testStr2)); } + // some short reads + for(i = 1000; i < 50000; i+=2) + { + yaffs_ReadDataFromFile(f,data,i,20); + } + yaffs_ReadDataFromFile(f,data,1000,50); data[50] = 0; @@ -354,7 +360,10 @@ void TestTime(yaffs_Device *dev) yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject); f = yaffs_MknodFile(yaffs_Root(dev),"pfile",0,0,0); - yaffs_WriteDataToFile(f,testStr,0,strlen(testStr)); + if(f) + { + yaffs_WriteDataToFile(f,testStr,0,strlen(testStr)); + } yaffs_Link(yaffs_Root(dev),"phl4",f); } @@ -469,8 +478,10 @@ void TestTimeTnodeFocussed(yaffs_Device *dev) int main(int argc,char *argv[]) { + int nBlocks; + #if YAFFS_FILEEM - device.nBlocks = (64 * 1024 * 1024) / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); + nBlocks =(32 * 1024 * 1024) / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK) ; device.writeChunkToNAND = yaffs_FEWriteChunkToNAND; device.readChunkFromNAND = yaffs_FEReadChunkFromNAND; device.eraseBlockInNAND = yaffs_FEEraseBlockInNAND; @@ -478,7 +489,7 @@ int main(int argc,char *argv[]) printf("Testing on file emulation\n"); #else - device.nBlocks = (2 * 1024 * 1024) / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); + nBlocks = (2 * 1024 * 1024) / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); device.writeChunkToNAND = nandemul_WriteChunkToNAND; device.readChunkFromNAND = nandemul_ReadChunkFromNAND; device.eraseBlockInNAND = nandemul_EraseBlockInNAND; @@ -487,8 +498,13 @@ int main(int argc,char *argv[]) printf("Testing on RAM emulation\n"); #endif +#ifdef YAFFS_START + device.startBlock = YAFFS_START; // Don't use block 0 + device.endBlock = YAFFS_END; +#else device.startBlock = 1; // Don't use block 0 - device.endBlock = device.nBlocks - 1; + device.endBlock = nBlocks; +#endif yaffs_GutsInitialise(&device); @@ -496,5 +512,8 @@ int main(int argc,char *argv[]) TestTime(&device); + printf("Cache hits %d\n",device.cacheHits); + printf("Retired blocks %d\n",device.nRetiredBlocks); + exit(0); } diff --git a/yaffsdev.proj b/yaffsdev.proj index 116b3a99258e5b7bde62d6261cb07fd3cef01b6c..38db4124e717da6c403c951ab7712de8bd506e56 100644 GIT binary patch delta 7061 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 ewxQemu;C+ZrOtqBB(-M1ZiW+28J} * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as + * it under the terms of the GNU Lesser General Public License version 2.1 as * published by the Free Software Foundation. * */ diff --git a/yportenv.h b/yportenv.h index 94e72fa..100d8a3 100644 --- a/yportenv.h +++ b/yportenv.h @@ -9,7 +9,7 @@ * Created by Charles Manning * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as + * it under the terms of the GNU Lesser General Public License version 2.1 as * published by the Free Software Foundation. * */ @@ -34,7 +34,7 @@ #include -#define YAFFS_LOSTNFOUND_NAME "LOST-N-FOUND" +#define YAFFS_LOSTNFOUND_NAME "LOST_CLUSTERS" #define YAFFS_LOSTNFOUND_PREFIX "OBJ" #define YPRINTF(x) @@ -57,6 +57,7 @@ extern unsigned yfsd_U32FileTimeNow(void); #define CURRENT_TIME yfsd_U32FileTimeNow() #define YAFFS_ROOT_MODE FILE_ATTRIBUTE_ARCHIVE +#define YAFFS_LOSTNFOUND_MODE (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN) #define TENDSTR "\r\n" @@ -81,6 +82,7 @@ extern unsigned yfsd_U32FileTimeNow(void); #define YFREE(x) kfree(x) #define YAFFS_ROOT_MODE 0666 +#define YAFFS_LOSTNFOUND_MODE 0666 #define yaffs_SumCompare(x,y) ((x) == (y)) @@ -117,6 +119,7 @@ extern unsigned yfsd_U32FileTimeNow(void); #define CURRENT_TIME 0 #define YAFFS_ROOT_MODE 0666 +#define YAFFS_LOSTNFOUND_MODE 0666 #define yaffs_SumCompare(x,y) ((x) == (y)) #define yaffs_strcmp(a,b) strcmp(a,b) @@ -131,6 +134,7 @@ extern unsigned yfsd_U32FileTimeNow(void); #define YINFO(s) YPRINTF((KERN_DEBUG __FILE__ " %d %s\n",__LINE__,s)) #define YALERT(s) YINFO(s) +#define YBUG() do{YINFO("bug");} while(0) #endif -- 2.30.2