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 int yaffs_readpage(struct file *file, struct page * page);
-//static int yaffs_readpage(struct file*,struct page *
-static struct address_space_operations yaffs_address_ops = {
-// readpage: yaffs_readpage,
+static int yaffs_readlink(struct dentry *dentry, char *buffer, int buflen);
+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,
// commit_write: yaffs_commit_write
};
static struct file_operations yaffs_file_operations = {
read: yaffs_file_read,
write: yaffs_file_write,
-// mmap: generic_file_mmap,
+ mmap: generic_file_mmap,
fsync: yaffs_sync_object,
};
};
-
-static struct file_operations yaffs_dir_operations = {
- read: generic_read_dir,
- readdir: yaffs_readdir,
- fsync: yaffs_sync_object,
+struct inode_operations yaffs_symlink_inode_operations =
+{
+ readlink: yaffs_readlink,
+ follow_link: yaffs_follow_link,
+ setattr: yaffs_setattr
};
static struct inode_operations yaffs_dir_inode_operations = {
setattr: yaffs_setattr,
};
+static struct file_operations yaffs_dir_operations = {
+ read: generic_read_dir,
+ readdir: yaffs_readdir,
+ fsync: yaffs_sync_object,
+};
+
+
static struct super_operations yaffs_super_ops = {
statfs: yaffs_statfs,
read_inode: yaffs_read_inode,
};
+
+
+
+static int yaffs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ unsigned char *alias;
+ int ret;
+
+ alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
+
+ if(!alias)
+ return -ENOMEM;
+
+ ret = vfs_readlink(dentry, buffer, buflen, alias);
+ kfree(alias);
+ return ret;
+}
+
+static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ unsigned char *alias;
+ int ret;
+ alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
+
+ if(!alias)
+ return -ENOMEM;
+
+ ret = vfs_follow_link(nd,alias);
+ kfree(alias);
+ return ret;
+}
+
+
struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Object *obj);
/*
obj = yaffs_FindObjectByName(yaffs_InodeToObject(dir),dentry->d_name.name);
+ obj = yaffs_GetEquivalentObject(obj); // in case it was a hardlink
+
if(obj)
{
T((KERN_DEBUG"yaffs_lookup found %d\n",obj->objectId));
}
-#ifdef YAFFS_ADDRESS_OPS
-static int yaffs_readpage(struct file *file, struct page * page)
+
+
+static int yaffs_readpage(struct file *f, struct page * pg)
{
- T((KERN_DEBUG"yaffs_readpage\n"));
+ struct yaffs_Object *obj;
+ unsigned char *pg_buf;
+ int ret;
+
+ T((KERN_DEBUG"yaffs_readpage at %08x, size %08x\n",
+ pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE));
- // TODO
- return 0;
+ obj = yaffs_DentryToObject(f->f_dentry);
+
+ //down(obj->sem);
+
+ if (!PageLocked(pg))
+ PAGE_BUG(pg);
+
+ pg_buf = kmap(pg);
+ /* FIXME: Can kmap fail? */
+
+ ret = yaffs_ReadDataFromFile(obj,pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
+
+ if(ret >= 0) ret = 0;
+
+ if (ret) {
+ ClearPageUptodate(pg);
+ SetPageError(pg);
+ } else {
+ SetPageUptodate(pg);
+ ClearPageError(pg);
+ }
+
+ flush_dcache_page(pg);
+ kunmap(pg);
+
+ UnlockPage(pg);
+
+ //up(&obj->sem);
+
+ T((KERN_DEBUG"yaffs_readpage done\n"));
+ return ret;
}
+#ifdef YAFFS_ADDRESS_OPS
+
static int yaffs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
{
T((KERN_DEBUG"yaffs_prepare_write\n"));
inode->i_blksize = YAFFS_BYTES_PER_CHUNK;
inode->i_blocks = 0;
inode->i_rdev = NODEV;
- inode->i_mapping->a_ops = &yaffs_address_ops;
inode->i_atime = obj->st_atime;
inode->i_mtime = obj->st_mtime;
inode->i_ctime = obj->st_ctime;
switch (obj->st_mode & S_IFMT)
{
default:
- // init_special_inode(inode, mode, dev);
+ // init_special_inode(inode, mode, dev);
break;
case S_IFREG: // file
inode->i_op = &yaffs_file_inode_operations;
inode->i_fop = &yaffs_file_operations;
+ inode->i_mapping->a_ops = &yaffs_file_address_operations;
break;
case S_IFDIR: // directory
inode->i_op = &yaffs_dir_inode_operations;
inode->i_fop = &yaffs_dir_operations;
break;
case S_IFLNK: // symlink
- inode->i_op = &page_symlink_inode_operations;
+ inode->i_op = &yaffs_symlink_inode_operations;
break;
}
break;
case S_IFLNK: // symlink
T((KERN_DEBUG"yaffs_mknod: making file\n"));
- obj = NULL; // Todo
+ obj = NULL; // Do we ever get here?
break;
}
if(obj)
{
inode = yaffs_get_inode(dir->i_sb, mode, dev, obj);
-
-// did not fix dir bug if((mode & S_IFMT) == S_IFDIR) atomic_inc(&inode->i_count);
-
d_instantiate(dentry, inode);
T((KERN_DEBUG"yaffs_mknod created object %d count = %d\n",obj->objectId,atomic_read(&inode->i_count)));
error = 0;
/*
- * Link a file..
+ * Create a link...
*/
static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry)
{
struct inode *inode = old_dentry->d_inode;
+ yaffs_Object *obj = NULL;
+ yaffs_Object *link=NULL;
T((KERN_DEBUG"yaffs_link\n"));
- return -EPERM; //Todo
+ obj = yaffs_InodeToObject(inode);
-
- if (S_ISDIR(inode->i_mode))
- return -EPERM;
-
-
- return 0;
+ link = yaffs_Link(yaffs_InodeToObject(dir),dentry->d_name.name,obj);
+
+ if(link)
+ {
+ return 0;
+ }
+
+
+ return -EPERM;
}
static int yaffs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
{
- int error;
+ yaffs_Object *obj;
T((KERN_DEBUG"yaffs_symlink\n"));
-
- return -ENOMEM; //Todo
+ obj = yaffs_MknodSymLink(yaffs_InodeToObject(dir), dentry->d_name.name,
+ S_IFLNK | S_IRWXUGO, current->uid, current->gid,
+ symname);
- error = yaffs_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0);
- return error;
+ if(obj)
+ {
+ T((KERN_DEBUG"symlink created OK\n"));
+ return 0;
+ }
+ else
+ {
+ T((KERN_DEBUG"symlink not created\n"));
+
+ }
+
+ return -ENOMEM;
}
static int yaffs_sync_object(struct file * file, struct dentry *dentry, int datasync)
static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId);
static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);
static int yaffs_CheckStructures(void);
-static yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj);
loff_t yaffs_GetFileSize(yaffs_Object *obj);
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)
obj->dirty = 1;
yaffs_AddObjectToDirectory(newDir,obj);
- return yaffs_UpdateObjectHeader(obj,newName);
+ if(yaffs_UpdateObjectHeader(obj,newName) >= 0)
+ {
+ return YAFFS_OK;
+ }
}
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
return yaffs_WriteChunkToNAND(dev,chunkInNAND,buffer,&spare);
}
+#endif
+
static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_Tags *tags, int useReserve)
{
{
int block = chunkId / YAFFS_CHUNKS_PER_BLOCK;
int page = chunkId % YAFFS_CHUNKS_PER_BLOCK;
- yaffs_Tags tags;
+ yaffs_Spare spare;
+
+ yaffs_SpareInitialise(&spare);
- // Mark the deleted NAND page as deleted
- tags.chunkId = 0;
- tags.objectId = 0;
- tags.byteCount = 0;
- tags.ecc = 0;
+ spare.pageStatus = 0; // To mark it as deleted.
+
- yaffs_WriteChunkWithTagsToNAND(dev,chunkId,NULL,&tags);
+ yaffs_WriteChunkToNAND(dev,chunkId,NULL,&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)
{
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);
int chunk;
int c;
int deleted;
- int inuse;
yaffs_BlockState state;
yaffs_Object *hardList = NULL;
yaffs_Object *hl;
- __u32 pageBits;
+// int inuse;
+// __u32 pageBits;
yaffs_ObjectHeader *oh;
yaffs_Object *in;
__u8 chunkData[YAFFS_BYTES_PER_CHUNK];
+
+ // Scan all the blocks...
+
for(blk = dev->startBlock; blk <= dev->endBlock; blk++)
{
deleted = 0;
- pageBits = 0;
- inuse = 0;
- state = YAFFS_BLOCK_STATE_UNKNOWN;
+ dev->blockInfo[blk].pageBits = 0;
+ dev->blockInfo[blk].pagesInUse = 0;
+ state = YAFFS_BLOCK_STATE_SCANNING;
+
+ // Read each chunk in the block.
for(c = 0; c < YAFFS_CHUNKS_PER_BLOCK &&
- state == YAFFS_BLOCK_STATE_UNKNOWN; c++)
+ 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);
// This block looks ok, now what's in this chunk?
yaffs_GetTagsFromSpare(&spare,&tags);
- if(tags.objectId == YAFFS_UNUSED_OBJECT_ID)
+ 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
dev->nFreeChunks += (YAFFS_CHUNKS_PER_BLOCK - c);
}
- else if(tags.objectId == 0)
- {
- // A deleted chunk
- deleted++;
- dev->nFreeChunks ++;
- T((" %d %d deleted\n",blk,c));
- }
else if(tags.chunkId > 0)
{
// A data chunk.
- inuse++;
- pageBits |= ( 1 <<c);
+ dev->blockInfo[blk].pageBits |= (1 << c);
+ dev->blockInfo[blk].pagesInUse++;
+
in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
- // PutChuunkIntoFIle checks for a clash (two data chunks with
+ // PutChunkIntoFIle checks for a clash (two data chunks with
// the same chunkId).
yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1);
T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));
else
{
// chunkId == 0, so it is an ObjectHeader.
- inuse++;
- pageBits |= ( 1 <<c);
+ // 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);
+
oh = (yaffs_ObjectHeader *)chunkData;
in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
+
if(in->valid)
{
- // todo we have already filled this one. We have
- // a duplicate. Need to fix
+ // 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);
+ }
}
- // we don't have a duplicate...
+ 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->chunkId = chunk;
-
- in->sum = oh->sum;
- in->dirty = 0;
+ 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->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
- }
+ // 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);
+ 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:
- in->variant.fileVariant.fileSize = oh->fileSize;
- 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_SYMLINK: // Do nothing
- in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias);
- break;
+ // 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:
+ in->variant.fileVariant.fileSize = oh->fileSize;
+ 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_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));
}
- T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));
}
}
else
}
}
- if(state == YAFFS_BLOCK_STATE_UNKNOWN)
+ if(state == YAFFS_BLOCK_STATE_SCANNING)
{
- // If we got this far, then the block is fully allocated.
- // ie. Full or Dirty
- state = (inuse) ? YAFFS_BLOCK_STATE_FULL : YAFFS_BLOCK_STATE_DIRTY;
-
+ // If we got this far while scanning, then the block is fully allocated.
+ state = YAFFS_BLOCK_STATE_FULL;
}
- dev->blockInfo[blk].pageBits = pageBits;
- dev->blockInfo[blk].pagesInUse = inuse;
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);
+ }
+
}
- // Todo fix up the hard link chains
+ // 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;
if(in)
{
+ // Add the hardlink pointers
hl->variant.hardLinkVariant.equivalentObject=in;
list_add(&hl->hardLinks,&in->hardLinks);
}
else
{
- //Todo Need to report this better.
+ //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);
// GetEquivalentObject dereferences any hard links to get to the
// actual object.
-static yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj)
+yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj)
{
if(obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
{
yaffs_GetObjectName(obj,name,256);
- YPRINTF(("Object %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n",
- yaffs_GetObjectInode(obj), name, obj->dirty, obj->valid, obj->serial,
+ 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