diff options
author | mchourasia <mchourasia@nvidia.com> | 2011-07-07 17:18:44 +0530 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-07-13 16:39:18 -0700 |
commit | 26e3664f64b49c7353150ce5dc7750fcc27b8438 (patch) | |
tree | 28ea9d8ee14cb6b3dd245117dccfb5080d31e24a /fs | |
parent | e20be38acc75fc69d6f2dae65bb9ec57bf1235dd (diff) |
fs:yaffs2: filesystem with stable YAFFS2 release
Yaffs2 update is required for making it build with
linux-2.6.36 kernel.
(cherry picked from commit 801807856be210442c88812b357f4b83fe79b344)
Change-Id: I120b262b203c751e1730914ce3b5e6656930bd20
Reviewed-on: http://git-master/r/39783
Reviewed-by: Venu Byravarasu <vbyravarasu@nvidia.com>
Tested-by: Manoj Chourasia <mchourasia@nvidia.com>
Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>
Diffstat (limited to 'fs')
30 files changed, 5068 insertions, 3445 deletions
diff --git a/fs/yaffs2/Kconfig b/fs/yaffs2/Kconfig index d4363edcdbfe..7b3988c3a014 100644 --- a/fs/yaffs2/Kconfig +++ b/fs/yaffs2/Kconfig @@ -178,3 +178,13 @@ config YAFFS_DISABLE_BACKGROUND Background processing makes many foreground activities faster. If unsure, say N. + +config YAFFS_XATTR + bool "Enable yaffs2 xattr support" + depends on YAFFS_FS + default y + help + If this is set then yaffs2 will provide xattr support. + If unsure, say Y. + + diff --git a/fs/yaffs2/Makefile b/fs/yaffs2/Makefile index 382ee6142cf6..fbdbd4a019d3 100644 --- a/fs/yaffs2/Makefile +++ b/fs/yaffs2/Makefile @@ -4,7 +4,14 @@ obj-$(CONFIG_YAFFS_FS) += yaffs.o -yaffs-y := yaffs_ecc.o yaffs_fs.o yaffs_guts.o yaffs_checkptrw.o -yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o yaffs_qsort.o +yaffs-y := yaffs_ecc.o yaffs_vfs_glue.o yaffs_guts.o yaffs_checkptrw.o +yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o yaffs-y += yaffs_tagscompat.o yaffs_tagsvalidity.o yaffs-y += yaffs_mtdif.o yaffs_mtdif1.o yaffs_mtdif2.o +yaffs-y += yaffs_nameval.o +yaffs-y += yaffs_allocator.o +yaffs-y += yaffs_yaffs1.o +yaffs-y += yaffs_yaffs2.o +yaffs-y += yaffs_bitmap.o +yaffs-y += yaffs_verify.o + diff --git a/fs/yaffs2/devextras.h b/fs/yaffs2/devextras.h index 215caa50f178..ce30c820d938 100644 --- a/fs/yaffs2/devextras.h +++ b/fs/yaffs2/devextras.h @@ -24,6 +24,8 @@ #define __EXTRAS_H__ +#include "yportenv.h" + #if !(defined __KERNEL__) /* Definition of types */ @@ -33,103 +35,6 @@ typedef unsigned __u32; #endif -/* - * This is a simple doubly linked list implementation that matches the - * way the Linux kernel doubly linked list implementation works. - */ - -struct ylist_head { - struct ylist_head *next; /* next in chain */ - struct ylist_head *prev; /* previous in chain */ -}; - - -/* Initialise a static list */ -#define YLIST_HEAD(name) \ -struct ylist_head name = { &(name), &(name)} - - - -/* Initialise a list head to an empty list */ -#define YINIT_LIST_HEAD(p) \ -do { \ - (p)->next = (p);\ - (p)->prev = (p); \ -} while (0) - - -/* Add an element to a list */ -static __inline__ void ylist_add(struct ylist_head *newEntry, - struct ylist_head *list) -{ - struct ylist_head *listNext = list->next; - - list->next = newEntry; - newEntry->prev = list; - newEntry->next = listNext; - listNext->prev = newEntry; - -} - -static __inline__ void ylist_add_tail(struct ylist_head *newEntry, - struct ylist_head *list) -{ - struct ylist_head *listPrev = list->prev; - - list->prev = newEntry; - newEntry->next = list; - newEntry->prev = listPrev; - listPrev->next = newEntry; - -} - - -/* Take an element out of its current list, with or without - * reinitialising the links.of the entry*/ -static __inline__ void ylist_del(struct ylist_head *entry) -{ - struct ylist_head *listNext = entry->next; - struct ylist_head *listPrev = entry->prev; - - listNext->prev = listPrev; - listPrev->next = listNext; - -} - -static __inline__ void ylist_del_init(struct ylist_head *entry) -{ - ylist_del(entry); - entry->next = entry->prev = entry; -} - - -/* Test if the list is empty */ -static __inline__ int ylist_empty(struct ylist_head *entry) -{ - return (entry->next == entry); -} - - -/* ylist_entry takes a pointer to a list entry and offsets it to that - * we can find a pointer to the object it is embedded in. - */ - - -#define ylist_entry(entry, type, member) \ - ((type *)((char *)(entry)-(unsigned long)(&((type *)NULL)->member))) - - -/* ylist_for_each and list_for_each_safe iterate over lists. - * ylist_for_each_safe uses temporary storage to make the list delete safe - */ - -#define ylist_for_each(itervar, list) \ - for (itervar = (list)->next; itervar != (list); itervar = itervar->next) - -#define ylist_for_each_safe(itervar, saveVar, list) \ - for (itervar = (list)->next, saveVar = (list)->next->next; \ - itervar != (list); itervar = saveVar, saveVar = saveVar->next) - #if !(defined __KERNEL__) diff --git a/fs/yaffs2/moduleconfig.h b/fs/yaffs2/moduleconfig.h index e8efd67586a7..43f11447601d 100644 --- a/fs/yaffs2/moduleconfig.h +++ b/fs/yaffs2/moduleconfig.h @@ -63,6 +63,10 @@ /* #define CONFIG_DISABLE_BACKGROUND */ +/* Default: Selected */ +/* Meaning: Enable XATTR support */ +#define CONFIG_YAFFS_XATTR + /* Older-style on-NAND data format has a "pageStatus" byte to record chunk/page state. This byte is zeroed when the page is discarded. diff --git a/fs/yaffs2/yaffs_allocator.c b/fs/yaffs2/yaffs_allocator.c new file mode 100644 index 000000000000..a284439cfa62 --- /dev/null +++ b/fs/yaffs2/yaffs_allocator.c @@ -0,0 +1,408 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + */ + + + +#include "yaffs_allocator.h" +#include "yaffs_guts.h" +#include "yaffs_trace.h" +#include "yportenv.h" + +#ifdef CONFIG_YAFFS_YMALLOC_ALLOCATOR + +void yaffs_DeinitialiseRawTnodesAndObjects(yaffs_Device *dev) +{ + dev = dev; +} + +void yaffs_InitialiseRawTnodesAndObjects(yaffs_Device *dev) +{ + dev = dev; +} + +yaffs_Tnode *yaffs_AllocateRawTnode(yaffs_Device *dev) +{ + return (yaffs_Tnode *)YMALLOC(dev->tnodeSize); +} + +void yaffs_FreeRawTnode(yaffs_Device *dev, yaffs_Tnode *tn) +{ + dev = dev; + YFREE(tn); +} + +void yaffs_InitialiseRawObjects(yaffs_Device *dev) +{ + dev = dev; +} + +void yaffs_DeinitialiseRawObjects(yaffs_Device *dev) +{ + dev = dev; +} + +yaffs_Object *yaffs_AllocateRawObject(yaffs_Device *dev) +{ + dev = dev; + return (yaffs_Object *) YMALLOC(sizeof(yaffs_Object)); +} + + +void yaffs_FreeRawObject(yaffs_Device *dev, yaffs_Object *obj) +{ + + dev = dev; + YFREE(obj); +} + +#else + +struct yaffs_TnodeList_struct { + struct yaffs_TnodeList_struct *next; + yaffs_Tnode *tnodes; +}; + +typedef struct yaffs_TnodeList_struct yaffs_TnodeList; + +struct yaffs_ObjectList_struct { + yaffs_Object *objects; + struct yaffs_ObjectList_struct *next; +}; + +typedef struct yaffs_ObjectList_struct yaffs_ObjectList; + + +struct yaffs_AllocatorStruct { + int nTnodesCreated; + yaffs_Tnode *freeTnodes; + int nFreeTnodes; + yaffs_TnodeList *allocatedTnodeList; + + int nObjectsCreated; + yaffs_Object *freeObjects; + int nFreeObjects; + + yaffs_ObjectList *allocatedObjectList; +}; + +typedef struct yaffs_AllocatorStruct yaffs_Allocator; + + +static void yaffs_DeinitialiseRawTnodes(yaffs_Device *dev) +{ + + yaffs_Allocator *allocator = (yaffs_Allocator *)dev->allocator; + + yaffs_TnodeList *tmp; + + if(!allocator){ + YBUG(); + return; + } + + while (allocator->allocatedTnodeList) { + tmp = allocator->allocatedTnodeList->next; + + YFREE(allocator->allocatedTnodeList->tnodes); + YFREE(allocator->allocatedTnodeList); + allocator->allocatedTnodeList = tmp; + + } + + allocator->freeTnodes = NULL; + allocator->nFreeTnodes = 0; + allocator->nTnodesCreated = 0; +} + +static void yaffs_InitialiseRawTnodes(yaffs_Device *dev) +{ + yaffs_Allocator *allocator = dev->allocator; + + if(allocator){ + allocator->allocatedTnodeList = NULL; + allocator->freeTnodes = NULL; + allocator->nFreeTnodes = 0; + allocator->nTnodesCreated = 0; + } else + YBUG(); +} + +static int yaffs_CreateTnodes(yaffs_Device *dev, int nTnodes) +{ + yaffs_Allocator *allocator = (yaffs_Allocator *)dev->allocator; + int i; + yaffs_Tnode *newTnodes; + __u8 *mem; + yaffs_Tnode *curr; + yaffs_Tnode *next; + yaffs_TnodeList *tnl; + + if(!allocator){ + YBUG(); + return YAFFS_FAIL; + } + + if (nTnodes < 1) + return YAFFS_OK; + + + /* make these things */ + + newTnodes = YMALLOC(nTnodes * dev->tnodeSize); + mem = (__u8 *)newTnodes; + + if (!newTnodes) { + T(YAFFS_TRACE_ERROR, + (TSTR("yaffs: Could not allocate Tnodes" TENDSTR))); + return YAFFS_FAIL; + } + + /* New hookup for wide tnodes */ + for (i = 0; i < nTnodes - 1; i++) { + curr = (yaffs_Tnode *) &mem[i * dev->tnodeSize]; + next = (yaffs_Tnode *) &mem[(i+1) * dev->tnodeSize]; + curr->internal[0] = next; + } + + curr = (yaffs_Tnode *) &mem[(nTnodes - 1) * dev->tnodeSize]; + curr->internal[0] = allocator->freeTnodes; + allocator->freeTnodes = (yaffs_Tnode *)mem; + + allocator->nFreeTnodes += nTnodes; + allocator->nTnodesCreated += nTnodes; + + /* Now add this bunch of tnodes to a list for freeing up. + * NB If we can't add this to the management list it isn't fatal + * but it just means we can't free this bunch of tnodes later. + */ + + tnl = YMALLOC(sizeof(yaffs_TnodeList)); + if (!tnl) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs: Could not add tnodes to management list" TENDSTR))); + return YAFFS_FAIL; + } else { + tnl->tnodes = newTnodes; + tnl->next = allocator->allocatedTnodeList; + allocator->allocatedTnodeList = tnl; + } + + T(YAFFS_TRACE_ALLOCATE, (TSTR("yaffs: Tnodes added" TENDSTR))); + + return YAFFS_OK; +} + + +yaffs_Tnode *yaffs_AllocateRawTnode(yaffs_Device *dev) +{ + yaffs_Allocator *allocator = (yaffs_Allocator *)dev->allocator; + yaffs_Tnode *tn = NULL; + + if(!allocator){ + YBUG(); + return NULL; + } + + /* If there are none left make more */ + if (!allocator->freeTnodes) + yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES); + + if (allocator->freeTnodes) { + tn = allocator->freeTnodes; + allocator->freeTnodes = allocator->freeTnodes->internal[0]; + allocator->nFreeTnodes--; + } + + return tn; +} + +/* FreeTnode frees up a tnode and puts it back on the free list */ +void yaffs_FreeRawTnode(yaffs_Device *dev, yaffs_Tnode *tn) +{ + yaffs_Allocator *allocator = dev->allocator; + + if(!allocator){ + YBUG(); + return; + } + + if (tn) { + tn->internal[0] = allocator->freeTnodes; + allocator->freeTnodes = tn; + allocator->nFreeTnodes++; + } + dev->nCheckpointBlocksRequired = 0; /* force recalculation*/ +} + + + +static void yaffs_InitialiseRawObjects(yaffs_Device *dev) +{ + yaffs_Allocator *allocator = dev->allocator; + + if(allocator) { + allocator->allocatedObjectList = NULL; + allocator->freeObjects = NULL; + allocator->nFreeObjects = 0; + } else + YBUG(); +} + +static void yaffs_DeinitialiseRawObjects(yaffs_Device *dev) +{ + yaffs_Allocator *allocator = dev->allocator; + yaffs_ObjectList *tmp; + + if(!allocator){ + YBUG(); + return; + } + + while (allocator->allocatedObjectList) { + tmp = allocator->allocatedObjectList->next; + YFREE(allocator->allocatedObjectList->objects); + YFREE(allocator->allocatedObjectList); + + allocator->allocatedObjectList = tmp; + } + + allocator->freeObjects = NULL; + allocator->nFreeObjects = 0; + allocator->nObjectsCreated = 0; +} + + +static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects) +{ + yaffs_Allocator *allocator = dev->allocator; + + int i; + yaffs_Object *newObjects; + yaffs_ObjectList *list; + + if(!allocator){ + YBUG(); + return YAFFS_FAIL; + } + + if (nObjects < 1) + return YAFFS_OK; + + /* make these things */ + newObjects = YMALLOC(nObjects * sizeof(yaffs_Object)); + list = YMALLOC(sizeof(yaffs_ObjectList)); + + if (!newObjects || !list) { + if (newObjects){ + YFREE(newObjects); + newObjects = NULL; + } + if (list){ + YFREE(list); + list = NULL; + } + T(YAFFS_TRACE_ALLOCATE, + (TSTR("yaffs: Could not allocate more objects" TENDSTR))); + return YAFFS_FAIL; + } + + /* Hook them into the free list */ + for (i = 0; i < nObjects - 1; i++) { + newObjects[i].siblings.next = + (struct ylist_head *)(&newObjects[i + 1]); + } + + newObjects[nObjects - 1].siblings.next = (void *)allocator->freeObjects; + allocator->freeObjects = newObjects; + allocator->nFreeObjects += nObjects; + allocator->nObjectsCreated += nObjects; + + /* Now add this bunch of Objects to a list for freeing up. */ + + list->objects = newObjects; + list->next = allocator->allocatedObjectList; + allocator->allocatedObjectList = list; + + return YAFFS_OK; +} + +yaffs_Object *yaffs_AllocateRawObject(yaffs_Device *dev) +{ + yaffs_Object *obj = NULL; + yaffs_Allocator *allocator = dev->allocator; + + if(!allocator) { + YBUG(); + return obj; + } + + /* If there are none left make more */ + if (!allocator->freeObjects) + yaffs_CreateFreeObjects(dev, YAFFS_ALLOCATION_NOBJECTS); + + if (allocator->freeObjects) { + obj = allocator->freeObjects; + allocator->freeObjects = + (yaffs_Object *) (allocator->freeObjects->siblings.next); + allocator->nFreeObjects--; + } + + return obj; +} + + +void yaffs_FreeRawObject(yaffs_Device *dev, yaffs_Object *obj) +{ + + yaffs_Allocator *allocator = dev->allocator; + + if(!allocator) + YBUG(); + else { + /* Link into the free list. */ + obj->siblings.next = (struct ylist_head *)(allocator->freeObjects); + allocator->freeObjects = obj; + allocator->nFreeObjects++; + } +} + +void yaffs_DeinitialiseRawTnodesAndObjects(yaffs_Device *dev) +{ + if(dev->allocator){ + yaffs_DeinitialiseRawTnodes(dev); + yaffs_DeinitialiseRawObjects(dev); + + YFREE(dev->allocator); + dev->allocator=NULL; + } else + YBUG(); +} + +void yaffs_InitialiseRawTnodesAndObjects(yaffs_Device *dev) +{ + yaffs_Allocator *allocator; + + if(!dev->allocator){ + allocator = YMALLOC(sizeof(yaffs_Allocator)); + if(allocator){ + dev->allocator = allocator; + yaffs_InitialiseRawTnodes(dev); + yaffs_InitialiseRawObjects(dev); + } + } else + YBUG(); +} + + +#endif diff --git a/fs/yaffs2/yaffs_allocator.h b/fs/yaffs2/yaffs_allocator.h new file mode 100644 index 000000000000..b0a5d11b411b --- /dev/null +++ b/fs/yaffs2/yaffs_allocator.h @@ -0,0 +1,30 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_ALLOCATOR_H__ +#define __YAFFS_ALLOCATOR_H__ + +#include "yaffs_guts.h" + +void yaffs_InitialiseRawTnodesAndObjects(yaffs_Device *dev); +void yaffs_DeinitialiseRawTnodesAndObjects(yaffs_Device *dev); + +yaffs_Tnode *yaffs_AllocateRawTnode(yaffs_Device *dev); +void yaffs_FreeRawTnode(yaffs_Device *dev, yaffs_Tnode *tn); + +yaffs_Object *yaffs_AllocateRawObject(yaffs_Device *dev); +void yaffs_FreeRawObject(yaffs_Device *dev, yaffs_Object *obj); + +#endif diff --git a/fs/yaffs2/yaffs_bitmap.c b/fs/yaffs2/yaffs_bitmap.c new file mode 100644 index 000000000000..c7e9e8c4d8e8 --- /dev/null +++ b/fs/yaffs2/yaffs_bitmap.c @@ -0,0 +1,105 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + */ + +#include "yaffs_bitmap.h" +#include "yaffs_trace.h" +/* + * Chunk bitmap manipulations + */ + +static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device *dev, int blk) +{ + if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR), + blk)); + YBUG(); + } + return dev->chunkBits + + (dev->chunkBitmapStride * (blk - dev->internalStartBlock)); +} + +void yaffs_VerifyChunkBitId(yaffs_Device *dev, int blk, int chunk) +{ + if (blk < dev->internalStartBlock || blk > dev->internalEndBlock || + chunk < 0 || chunk >= dev->param.nChunksPerBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> yaffs: Chunk Id (%d:%d) invalid"TENDSTR), + blk, chunk)); + YBUG(); + } +} + +void yaffs_ClearChunkBits(yaffs_Device *dev, int blk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + + memset(blkBits, 0, dev->chunkBitmapStride); +} + +void yaffs_ClearChunkBit(yaffs_Device *dev, int blk, int chunk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + + yaffs_VerifyChunkBitId(dev, blk, chunk); + + blkBits[chunk / 8] &= ~(1 << (chunk & 7)); +} + +void yaffs_SetChunkBit(yaffs_Device *dev, int blk, int chunk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + + yaffs_VerifyChunkBitId(dev, blk, chunk); + + blkBits[chunk / 8] |= (1 << (chunk & 7)); +} + +int yaffs_CheckChunkBit(yaffs_Device *dev, int blk, int chunk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + yaffs_VerifyChunkBitId(dev, blk, chunk); + + return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; +} + +int yaffs_StillSomeChunkBits(yaffs_Device *dev, int blk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + int i; + for (i = 0; i < dev->chunkBitmapStride; i++) { + if (*blkBits) + return 1; + blkBits++; + } + return 0; +} + +int yaffs_CountChunkBits(yaffs_Device *dev, int blk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + int i; + int n = 0; + for (i = 0; i < dev->chunkBitmapStride; i++) { + __u8 x = *blkBits; + while (x) { + if (x & 1) + n++; + x >>= 1; + } + + blkBits++; + } + return n; +} + diff --git a/fs/yaffs2/yaffs_bitmap.h b/fs/yaffs2/yaffs_bitmap.h new file mode 100644 index 000000000000..00cca2c0a360 --- /dev/null +++ b/fs/yaffs2/yaffs_bitmap.h @@ -0,0 +1,33 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * Chunk bitmap manipulations + */ + +#ifndef __YAFFS_BITMAP_H__ +#define __YAFFS_BITMAP_H__ + +#include "yaffs_guts.h" + +void yaffs_VerifyChunkBitId(yaffs_Device *dev, int blk, int chunk); +void yaffs_ClearChunkBits(yaffs_Device *dev, int blk); +void yaffs_ClearChunkBit(yaffs_Device *dev, int blk, int chunk); +void yaffs_SetChunkBit(yaffs_Device *dev, int blk, int chunk); +int yaffs_CheckChunkBit(yaffs_Device *dev, int blk, int chunk); +int yaffs_StillSomeChunkBits(yaffs_Device *dev, int blk); +int yaffs_CountChunkBits(yaffs_Device *dev, int blk); + +#endif diff --git a/fs/yaffs2/yaffs_checkptrw.c b/fs/yaffs2/yaffs_checkptrw.c index 1304c19c185a..1b68e9dc4e58 100644 --- a/fs/yaffs2/yaffs_checkptrw.c +++ b/fs/yaffs2/yaffs_checkptrw.c @@ -14,7 +14,7 @@ #include "yaffs_checkptrw.h" #include "yaffs_getblockinfo.h" -static int yaffs_CheckpointSpaceOk(yaffs_Device *dev) +static int yaffs2_CheckpointSpaceOk(yaffs_Device *dev) { int blocksAvailable = dev->nErasedBlocks - dev->param.nReservedBlocks; @@ -26,7 +26,7 @@ static int yaffs_CheckpointSpaceOk(yaffs_Device *dev) } -static int yaffs_CheckpointErase(yaffs_Device *dev) +static int yaffs2_CheckpointErase(yaffs_Device *dev) { int i; @@ -59,7 +59,7 @@ static int yaffs_CheckpointErase(yaffs_Device *dev) } -static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev) +static void yaffs2_CheckpointFindNextErasedBlock(yaffs_Device *dev) { int i; int blocksAvailable = dev->nErasedBlocks - dev->param.nReservedBlocks; @@ -87,7 +87,7 @@ static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev) dev->checkpointCurrentBlock = -1; } -static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev) +static void yaffs2_CheckpointFindNextCheckpointBlock(yaffs_Device *dev) { int i; yaffs_ExtendedTags tags; @@ -123,7 +123,7 @@ static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev) } -int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting) +int yaffs2_CheckpointOpen(yaffs_Device *dev, int forWriting) { @@ -136,7 +136,7 @@ int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting) !dev->param.markNANDBlockBad) return 0; - if (forWriting && !yaffs_CheckpointSpaceOk(dev)) + if (forWriting && !yaffs2_CheckpointSpaceOk(dev)) return 0; if (!dev->checkpointBuffer) @@ -157,7 +157,7 @@ int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting) if (forWriting) { memset(dev->checkpointBuffer, 0, dev->nDataBytesPerChunk); dev->checkpointByteOffset = 0; - return yaffs_CheckpointErase(dev); + return yaffs2_CheckpointErase(dev); } else { int i; /* Set to a value that will kick off a read */ @@ -177,7 +177,7 @@ int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting) return 1; } -int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum) +int yaffs2_GetCheckpointSum(yaffs_Device *dev, __u32 *sum) { __u32 compositeSum; compositeSum = (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF); @@ -185,7 +185,7 @@ int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum) return 1; } -static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev) +static int yaffs2_CheckpointFlushBuffer(yaffs_Device *dev) { int chunk; int realignedChunk; @@ -193,7 +193,7 @@ static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev) yaffs_ExtendedTags tags; if (dev->checkpointCurrentBlock < 0) { - yaffs_CheckpointFindNextErasedBlock(dev); + yaffs2_CheckpointFindNextErasedBlock(dev); dev->checkpointCurrentChunk = 0; } @@ -238,7 +238,7 @@ static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev) } -int yaffs_CheckpointWrite(yaffs_Device *dev, const void *data, int nBytes) +int yaffs2_CheckpointWrite(yaffs_Device *dev, const void *data, int nBytes) { int i = 0; int ok = 1; @@ -267,13 +267,13 @@ int yaffs_CheckpointWrite(yaffs_Device *dev, const void *data, int nBytes) if (dev->checkpointByteOffset < 0 || dev->checkpointByteOffset >= dev->nDataBytesPerChunk) - ok = yaffs_CheckpointFlushBuffer(dev); + ok = yaffs2_CheckpointFlushBuffer(dev); } return i; } -int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes) +int yaffs2_CheckpointRead(yaffs_Device *dev, void *data, int nBytes) { int i = 0; int ok = 1; @@ -298,7 +298,7 @@ int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes) dev->checkpointByteOffset >= dev->nDataBytesPerChunk) { if (dev->checkpointCurrentBlock < 0) { - yaffs_CheckpointFindNextCheckpointBlock(dev); + yaffs2_CheckpointFindNextCheckpointBlock(dev); dev->checkpointCurrentChunk = 0; } @@ -310,7 +310,7 @@ int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes) dev->checkpointCurrentChunk; realignedChunk = chunk - dev->chunkOffset; - + dev->nPageReads++; /* read in the next chunk */ @@ -348,12 +348,12 @@ int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes) return i; } -int yaffs_CheckpointClose(yaffs_Device *dev) +int yaffs2_CheckpointClose(yaffs_Device *dev) { if (dev->checkpointOpenForWrite) { if (dev->checkpointByteOffset != 0) - yaffs_CheckpointFlushBuffer(dev); + yaffs2_CheckpointFlushBuffer(dev); } else if(dev->checkpointBlockList){ int i; for (i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++) { @@ -387,14 +387,14 @@ int yaffs_CheckpointClose(yaffs_Device *dev) return 0; } -int yaffs_CheckpointInvalidateStream(yaffs_Device *dev) +int yaffs2_CheckpointInvalidateStream(yaffs_Device *dev) { /* Erase the checkpoint data */ T(YAFFS_TRACE_CHECKPOINT, (TSTR("checkpoint invalidate of %d blocks"TENDSTR), dev->blocksInCheckpoint)); - return yaffs_CheckpointErase(dev); + return yaffs2_CheckpointErase(dev); } diff --git a/fs/yaffs2/yaffs_checkptrw.h b/fs/yaffs2/yaffs_checkptrw.h index 881c3659c8a9..ea4f0276f2b0 100644 --- a/fs/yaffs2/yaffs_checkptrw.h +++ b/fs/yaffs2/yaffs_checkptrw.h @@ -18,17 +18,17 @@ #include "yaffs_guts.h" -int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting); +int yaffs2_CheckpointOpen(yaffs_Device *dev, int forWriting); -int yaffs_CheckpointWrite(yaffs_Device *dev, const void *data, int nBytes); +int yaffs2_CheckpointWrite(yaffs_Device *dev, const void *data, int nBytes); -int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes); +int yaffs2_CheckpointRead(yaffs_Device *dev, void *data, int nBytes); -int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum); +int yaffs2_GetCheckpointSum(yaffs_Device *dev, __u32 *sum); -int yaffs_CheckpointClose(yaffs_Device *dev); +int yaffs2_CheckpointClose(yaffs_Device *dev); -int yaffs_CheckpointInvalidateStream(yaffs_Device *dev); +int yaffs2_CheckpointInvalidateStream(yaffs_Device *dev); #endif diff --git a/fs/yaffs2/yaffs_guts.c b/fs/yaffs2/yaffs_guts.c index 9509dd401a33..661f9167113d 100644 --- a/fs/yaffs2/yaffs_guts.c +++ b/fs/yaffs2/yaffs_guts.c @@ -10,6 +10,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + #include "yportenv.h" #include "yaffs_trace.h" @@ -19,34 +20,28 @@ #include "yaffs_getblockinfo.h" #include "yaffs_tagscompat.h" -#ifndef CONFIG_YAFFS_USE_OWN_SORT -#include "yaffs_qsort.h" -#endif + #include "yaffs_nand.h" -#include "yaffs_checkptrw.h" +#include "yaffs_yaffs1.h" +#include "yaffs_yaffs2.h" +#include "yaffs_bitmap.h" +#include "yaffs_verify.h" #include "yaffs_nand.h" #include "yaffs_packedtags2.h" +#include "yaffs_nameval.h" +#include "yaffs_allocator.h" /* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ #define YAFFS_GC_GOOD_ENOUGH 2 #define YAFFS_GC_PASSIVE_THRESHOLD 4 -#define YAFFS_SMALL_HOLE_THRESHOLD 3 - -/* - * Checkpoints are really no benefit on very small partitions. - * - * To save space on small partitions don't bother with checkpoints unless - * the partition is at least this big. - */ -#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 - #include "yaffs_ecc.h" + /* Robustification (if it ever comes about...) */ static void yaffs_RetireBlock(yaffs_Device *dev, int blockInNAND); static void yaffs_HandleWriteChunkError(yaffs_Device *dev, int chunkInNAND, @@ -62,28 +57,22 @@ static void yaffs_UpdateParent(yaffs_Object *obj); static int yaffs_UnlinkObject(yaffs_Object *obj); static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj); -static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList); - static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_ExtendedTags *tags, int useReserve); -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 YCHAR *name, - int force, int isShrink, int shadows); + + +static int yaffs_ApplyXMod(yaffs_Object *obj, char *buffer, yaffs_XAttrMod *xmod); + static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj); static int yaffs_CheckStructures(void); static int yaffs_DoGenericObjectDeletion(yaffs_Object *in); -static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device *dev, int blockNo); - - static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, int chunkInNAND); @@ -95,38 +84,24 @@ static int yaffs_TagsMatch(const yaffs_ExtendedTags *tags, int objectId, static int yaffs_AllocateChunk(yaffs_Device *dev, int useReserve, yaffs_BlockInfo **blockUsedPtr); -static void yaffs_VerifyFreeChunks(yaffs_Device *dev); - static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in); -static void yaffs_VerifyDirectory(yaffs_Object *directory); -#ifdef YAFFS_PARANOID -static int yaffs_CheckFileSanity(yaffs_Object *in); -#else -#define yaffs_CheckFileSanity(in) -#endif - static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in); static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId); -static void yaffs_InvalidateCheckpoint(yaffs_Device *dev); - static int yaffs_FindChunkInFile(yaffs_Object *in, int chunkInInode, yaffs_ExtendedTags *tags); -static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, - unsigned pos); -static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev, - yaffs_FileStructure *fStruct, - __u32 chunkId); - -static int yaffs_HandleHole(yaffs_Object *obj, loff_t newSize); -static void yaffs_SkipRestOfBlock(yaffs_Device *dev); static int yaffs_VerifyChunkWritten(yaffs_Device *dev, int chunkInNAND, const __u8 *data, yaffs_ExtendedTags *tags); + +static void yaffs_LoadNameFromObjectHeader(yaffs_Device *dev,YCHAR *name, const YCHAR *ohName, int bufferSize); +static void yaffs_LoadObjectHeaderFromName(yaffs_Device *dev,YCHAR *ohName, const YCHAR *name); + + /* Function to calculate chunk and offset */ static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, int *chunkOut, @@ -313,556 +288,11 @@ int yaffs_IsManagedTempBuffer(yaffs_Device *dev, const __u8 *buffer) return 0; } - - -/* - * Chunk bitmap manipulations - */ - -static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device *dev, int blk) -{ - if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) { - T(YAFFS_TRACE_ERROR, - (TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR), - blk)); - YBUG(); - } - return dev->chunkBits + - (dev->chunkBitmapStride * (blk - dev->internalStartBlock)); -} - -static Y_INLINE void yaffs_VerifyChunkBitId(yaffs_Device *dev, int blk, int chunk) -{ - if (blk < dev->internalStartBlock || blk > dev->internalEndBlock || - chunk < 0 || chunk >= dev->param.nChunksPerBlock) { - T(YAFFS_TRACE_ERROR, - (TSTR("**>> yaffs: Chunk Id (%d:%d) invalid"TENDSTR), - blk, chunk)); - YBUG(); - } -} - -static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device *dev, int blk) -{ - __u8 *blkBits = yaffs_BlockBits(dev, blk); - - memset(blkBits, 0, dev->chunkBitmapStride); -} - -static Y_INLINE void yaffs_ClearChunkBit(yaffs_Device *dev, int blk, int chunk) -{ - __u8 *blkBits = yaffs_BlockBits(dev, blk); - - yaffs_VerifyChunkBitId(dev, blk, chunk); - - blkBits[chunk / 8] &= ~(1 << (chunk & 7)); -} - -static Y_INLINE void yaffs_SetChunkBit(yaffs_Device *dev, int blk, int chunk) -{ - __u8 *blkBits = yaffs_BlockBits(dev, blk); - - yaffs_VerifyChunkBitId(dev, blk, chunk); - - blkBits[chunk / 8] |= (1 << (chunk & 7)); -} - -static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device *dev, int blk, int chunk) -{ - __u8 *blkBits = yaffs_BlockBits(dev, blk); - yaffs_VerifyChunkBitId(dev, blk, chunk); - - return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; -} - -static Y_INLINE int yaffs_StillSomeChunkBits(yaffs_Device *dev, int blk) -{ - __u8 *blkBits = yaffs_BlockBits(dev, blk); - int i; - for (i = 0; i < dev->chunkBitmapStride; i++) { - if (*blkBits) - return 1; - blkBits++; - } - return 0; -} - -static int yaffs_CountChunkBits(yaffs_Device *dev, int blk) -{ - __u8 *blkBits = yaffs_BlockBits(dev, blk); - int i; - int n = 0; - for (i = 0; i < dev->chunkBitmapStride; i++) { - __u8 x = *blkBits; - while (x) { - if (x & 1) - n++; - x >>= 1; - } - - blkBits++; - } - return n; -} - /* * Verification code */ -static int yaffs_SkipVerification(yaffs_Device *dev) -{ - dev=dev; - return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); -} - -static int yaffs_SkipFullVerification(yaffs_Device *dev) -{ - dev=dev; - return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL)); -} - -static int yaffs_SkipNANDVerification(yaffs_Device *dev) -{ - dev=dev; - return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_NAND)); -} - -static const char *blockStateName[] = { -"Unknown", -"Needs scanning", -"Scanning", -"Empty", -"Allocating", -"Full", -"Dirty", -"Checkpoint", -"Collecting", -"Dead" -}; - -static void yaffs_VerifyBlock(yaffs_Device *dev, yaffs_BlockInfo *bi, int n) -{ - int actuallyUsed; - int inUse; - - if (yaffs_SkipVerification(dev)) - return; - - /* Report illegal runtime states */ - if (bi->blockState >= YAFFS_NUMBER_OF_BLOCK_STATES) - T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has undefined state %d"TENDSTR), n, bi->blockState)); - - switch (bi->blockState) { - case YAFFS_BLOCK_STATE_UNKNOWN: - case YAFFS_BLOCK_STATE_SCANNING: - case YAFFS_BLOCK_STATE_NEEDS_SCANNING: - T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has bad run-state %s"TENDSTR), - n, blockStateName[bi->blockState])); - } - - /* Check pages in use and soft deletions are legal */ - - actuallyUsed = bi->pagesInUse - bi->softDeletions; - - if (bi->pagesInUse < 0 || bi->pagesInUse > dev->param.nChunksPerBlock || - bi->softDeletions < 0 || bi->softDeletions > dev->param.nChunksPerBlock || - actuallyUsed < 0 || actuallyUsed > dev->param.nChunksPerBlock) - T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has illegal values pagesInUsed %d softDeletions %d"TENDSTR), - n, bi->pagesInUse, bi->softDeletions)); - - - /* Check chunk bitmap legal */ - inUse = yaffs_CountChunkBits(dev, n); - if (inUse != bi->pagesInUse) - T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has inconsistent values pagesInUse %d counted chunk bits %d"TENDSTR), - n, bi->pagesInUse, inUse)); - - /* Check that the sequence number is valid. - * Ten million is legal, but is very unlikely - */ - if (dev->param.isYaffs2 && - (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || bi->blockState == YAFFS_BLOCK_STATE_FULL) && - (bi->sequenceNumber < YAFFS_LOWEST_SEQUENCE_NUMBER || bi->sequenceNumber > 10000000)) - T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has suspect sequence number of %d"TENDSTR), - n, bi->sequenceNumber)); -} - -static void yaffs_VerifyCollectedBlock(yaffs_Device *dev, yaffs_BlockInfo *bi, - int n) -{ - yaffs_VerifyBlock(dev, bi, n); - - /* After collection the block should be in the erased state */ - /* This will need to change if we do partial gc */ - - if (bi->blockState != YAFFS_BLOCK_STATE_COLLECTING && - bi->blockState != YAFFS_BLOCK_STATE_EMPTY) { - T(YAFFS_TRACE_ERROR, (TSTR("Block %d is in state %d after gc, should be erased"TENDSTR), - n, bi->blockState)); - } -} - -static void yaffs_VerifyBlocks(yaffs_Device *dev) -{ - int i; - int nBlocksPerState[YAFFS_NUMBER_OF_BLOCK_STATES]; - int nIllegalBlockStates = 0; - - if (yaffs_SkipVerification(dev)) - return; - - memset(nBlocksPerState, 0, sizeof(nBlocksPerState)); - - for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { - yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, i); - yaffs_VerifyBlock(dev, bi, i); - - if (bi->blockState < YAFFS_NUMBER_OF_BLOCK_STATES) - nBlocksPerState[bi->blockState]++; - else - nIllegalBlockStates++; - } - - T(YAFFS_TRACE_VERIFY, (TSTR(""TENDSTR))); - T(YAFFS_TRACE_VERIFY, (TSTR("Block summary"TENDSTR))); - - T(YAFFS_TRACE_VERIFY, (TSTR("%d blocks have illegal states"TENDSTR), nIllegalBlockStates)); - if (nBlocksPerState[YAFFS_BLOCK_STATE_ALLOCATING] > 1) - T(YAFFS_TRACE_VERIFY, (TSTR("Too many allocating blocks"TENDSTR))); - - for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) - T(YAFFS_TRACE_VERIFY, - (TSTR("%s %d blocks"TENDSTR), - blockStateName[i], nBlocksPerState[i])); - - if (dev->blocksInCheckpoint != nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT]) - T(YAFFS_TRACE_VERIFY, - (TSTR("Checkpoint block count wrong dev %d count %d"TENDSTR), - dev->blocksInCheckpoint, nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT])); - - if (dev->nErasedBlocks != nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY]) - T(YAFFS_TRACE_VERIFY, - (TSTR("Erased block count wrong dev %d count %d"TENDSTR), - dev->nErasedBlocks, nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY])); - - if (nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING] > 1) - T(YAFFS_TRACE_VERIFY, - (TSTR("Too many collecting blocks %d (max is 1)"TENDSTR), - nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING])); - - T(YAFFS_TRACE_VERIFY, (TSTR(""TENDSTR))); - -} - -/* - * Verify the object header. oh must be valid, but obj and tags may be NULL in which - * case those tests will not be performed. - */ -static void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh, yaffs_ExtendedTags *tags, int parentCheck) -{ - if (obj && yaffs_SkipVerification(obj->myDev)) - return; - - if (!(tags && obj && oh)) { - T(YAFFS_TRACE_VERIFY, - (TSTR("Verifying object header tags %p obj %p oh %p"TENDSTR), - tags, obj, oh)); - return; - } - - if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || - oh->type > YAFFS_OBJECT_TYPE_MAX) - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d header type is illegal value 0x%x"TENDSTR), - tags->objectId, oh->type)); - - if (tags->objectId != obj->objectId) - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d header mismatch objectId %d"TENDSTR), - tags->objectId, obj->objectId)); - - - /* - * Check that the object's parent ids match if parentCheck requested. - * - * Tests do not apply to the root object. - */ - - if (parentCheck && tags->objectId > 1 && !obj->parent) - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d header mismatch parentId %d obj->parent is NULL"TENDSTR), - tags->objectId, oh->parentObjectId)); - - if (parentCheck && obj->parent && - oh->parentObjectId != obj->parent->objectId && - (oh->parentObjectId != YAFFS_OBJECTID_UNLINKED || - obj->parent->objectId != YAFFS_OBJECTID_DELETED)) - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d header mismatch parentId %d parentObjectId %d"TENDSTR), - tags->objectId, oh->parentObjectId, obj->parent->objectId)); - - if (tags->objectId > 1 && oh->name[0] == 0) /* Null name */ - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d header name is NULL"TENDSTR), - obj->objectId)); - - if (tags->objectId > 1 && ((__u8)(oh->name[0])) == 0xff) /* Trashed name */ - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d header name is 0xFF"TENDSTR), - obj->objectId)); -} - - -#if 0 -/* Not being used, but don't want to throw away yet */ -static int yaffs_VerifyTnodeWorker(yaffs_Object *obj, yaffs_Tnode *tn, - __u32 level, int chunkOffset) -{ - int i; - yaffs_Device *dev = obj->myDev; - int ok = 1; - - if (tn) { - if (level > 0) { - - for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { - if (tn->internal[i]) { - ok = yaffs_VerifyTnodeWorker(obj, - tn->internal[i], - level - 1, - (chunkOffset<<YAFFS_TNODES_INTERNAL_BITS) + i); - } - } - } else if (level == 0) { - yaffs_ExtendedTags tags; - __u32 objectId = obj->objectId; - - chunkOffset <<= YAFFS_TNODES_LEVEL0_BITS; - - for (i = 0; i < YAFFS_NTNODES_LEVEL0; i++) { - __u32 theChunk = yaffs_GetChunkGroupBase(dev, tn, i); - - if (theChunk > 0) { - /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),tags.objectId,tags.chunkId,theChunk)); */ - yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, &tags); - if (tags.objectId != objectId || tags.chunkId != chunkOffset) { - T(~0, (TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), - objectId, chunkOffset, theChunk, - tags.objectId, tags.chunkId)); - } - } - chunkOffset++; - } - } - } - - return ok; - -} - -#endif - -static void yaffs_VerifyFile(yaffs_Object *obj) -{ - int requiredTallness; - int actualTallness; - __u32 lastChunk; - __u32 x; - __u32 i; - yaffs_Device *dev; - yaffs_ExtendedTags tags; - yaffs_Tnode *tn; - __u32 objectId; - - if (!obj) - return; - - if (yaffs_SkipVerification(obj->myDev)) - return; - - dev = obj->myDev; - objectId = obj->objectId; - - /* Check file size is consistent with tnode depth */ - lastChunk = obj->variant.fileVariant.fileSize / dev->nDataBytesPerChunk + 1; - x = lastChunk >> YAFFS_TNODES_LEVEL0_BITS; - requiredTallness = 0; - while (x > 0) { - x >>= YAFFS_TNODES_INTERNAL_BITS; - requiredTallness++; - } - - actualTallness = obj->variant.fileVariant.topLevel; - /* Check that the chunks in the tnode tree are all correct. - * We do this by scanning through the tnode tree and - * checking the tags for every chunk match. - */ - - if (yaffs_SkipNANDVerification(dev)) - return; - - for (i = 1; i <= lastChunk; i++) { - tn = yaffs_FindLevel0Tnode(dev, &obj->variant.fileVariant, i); - - if (tn) { - __u32 theChunk = yaffs_GetChunkGroupBase(dev, tn, i); - if (theChunk > 0) { - /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),objectId,i,theChunk)); */ - yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, &tags); - if (tags.objectId != objectId || tags.chunkId != i) { - T(~0, (TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), - objectId, i, theChunk, - tags.objectId, tags.chunkId)); - } - } - } - } -} - - -static void yaffs_VerifyHardLink(yaffs_Object *obj) -{ - if (obj && yaffs_SkipVerification(obj->myDev)) - return; - - /* Verify sane equivalent object */ -} - -static void yaffs_VerifySymlink(yaffs_Object *obj) -{ - if (obj && yaffs_SkipVerification(obj->myDev)) - return; - - /* Verify symlink string */ -} - -static void yaffs_VerifySpecial(yaffs_Object *obj) -{ - if (obj && yaffs_SkipVerification(obj->myDev)) - return; -} - -static void yaffs_VerifyObject(yaffs_Object *obj) -{ - yaffs_Device *dev; - - __u32 chunkMin; - __u32 chunkMax; - - __u32 chunkIdOk; - __u32 chunkInRange; - __u32 chunkShouldNotBeDeleted; - __u32 chunkValid; - - if (!obj) - return; - - if (obj->beingCreated) - return; - - dev = obj->myDev; - - if (yaffs_SkipVerification(dev)) - return; - - /* Check sane object header chunk */ - - chunkMin = dev->internalStartBlock * dev->param.nChunksPerBlock; - chunkMax = (dev->internalEndBlock+1) * dev->param.nChunksPerBlock - 1; - - chunkInRange = (((unsigned)(obj->hdrChunk)) >= chunkMin && ((unsigned)(obj->hdrChunk)) <= chunkMax); - chunkIdOk = chunkInRange || (obj->hdrChunk == 0); - chunkValid = chunkInRange && - yaffs_CheckChunkBit(dev, - obj->hdrChunk / dev->param.nChunksPerBlock, - obj->hdrChunk % dev->param.nChunksPerBlock); - chunkShouldNotBeDeleted = chunkInRange && !chunkValid; - - if (!obj->fake && - (!chunkIdOk || chunkShouldNotBeDeleted)) { - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d has chunkId %d %s %s"TENDSTR), - obj->objectId, obj->hdrChunk, - chunkIdOk ? "" : ",out of range", - chunkShouldNotBeDeleted ? ",marked as deleted" : "")); - } - - if (chunkValid && !yaffs_SkipNANDVerification(dev)) { - yaffs_ExtendedTags tags; - yaffs_ObjectHeader *oh; - __u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__); - - oh = (yaffs_ObjectHeader *)buffer; - - yaffs_ReadChunkWithTagsFromNAND(dev, obj->hdrChunk, buffer, - &tags); - - yaffs_VerifyObjectHeader(obj, oh, &tags, 1); - - yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); - } - - /* Verify it has a parent */ - if (obj && !obj->fake && - (!obj->parent || obj->parent->myDev != dev)) { - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d has parent pointer %p which does not look like an object"TENDSTR), - obj->objectId, obj->parent)); - } - - /* Verify parent is a directory */ - if (obj->parent && obj->parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d's parent is not a directory (type %d)"TENDSTR), - obj->objectId, obj->parent->variantType)); - } - - switch (obj->variantType) { - case YAFFS_OBJECT_TYPE_FILE: - yaffs_VerifyFile(obj); - break; - case YAFFS_OBJECT_TYPE_SYMLINK: - yaffs_VerifySymlink(obj); - break; - case YAFFS_OBJECT_TYPE_DIRECTORY: - yaffs_VerifyDirectory(obj); - break; - case YAFFS_OBJECT_TYPE_HARDLINK: - yaffs_VerifyHardLink(obj); - break; - case YAFFS_OBJECT_TYPE_SPECIAL: - yaffs_VerifySpecial(obj); - break; - case YAFFS_OBJECT_TYPE_UNKNOWN: - default: - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d has illegaltype %d"TENDSTR), - obj->objectId, obj->variantType)); - break; - } -} - -static void yaffs_VerifyObjects(yaffs_Device *dev) -{ - yaffs_Object *obj; - int i; - struct ylist_head *lh; - - if (yaffs_SkipVerification(dev)) - return; - - /* Iterate through the objects in each hash entry */ - - for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { - ylist_for_each(lh, &dev->objectBucket[i].list) { - if (lh) { - obj = ylist_entry(lh, yaffs_Object, hashLink); - yaffs_VerifyObject(obj); - } - } - } -} /* @@ -941,7 +371,7 @@ static int yaffs_VerifyChunkWritten(yaffs_Device *dev, yaffs_ExtendedTags tempTags; __u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__); int result; - + result = yaffs_ReadChunkWithTagsFromNAND(dev,chunkInNAND,buffer,&tempTags); if(memcmp(buffer,data,dev->nDataBytesPerChunk) || tempTags.objectId != tags->objectId || @@ -963,7 +393,7 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, int writeOk = 0; int chunk; - yaffs_InvalidateCheckpoint(dev); + yaffs2_InvalidateCheckpoint(dev); do { yaffs_BlockInfo *bi = 0; @@ -995,16 +425,16 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, * lot of checks that are most likely not needed. * * Mods to the above - * If an erase check fails or the write fails we skip the + * If an erase check fails or the write fails we skip the * rest of the block. */ /* let's give it a try */ attempts++; -#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED - bi->skipErasedCheck = 0; -#endif + if(dev->param.alwaysCheckErased) + bi->skipErasedCheck = 0; + if (!bi->skipErasedCheck) { erasedOk = yaffs_CheckChunkErased(dev, chunk); if (erasedOk != YAFFS_OK) { @@ -1057,85 +487,8 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, } -/* - * Oldest Dirty Sequence Number handling. - */ - -/* yaffs_CalcOldestDirtySequence() - * yaffs_FindOldestDirtySequence() - * Calculate the oldest dirty sequence number if we don't know it. - */ -static void yaffs_CalcOldestDirtySequence(yaffs_Device *dev) -{ - int i; - unsigned seq; - unsigned blockNo = 0; - yaffs_BlockInfo *b; - - if(!dev->param.isYaffs2) - return; - - /* Find the oldest dirty sequence number. */ - seq = dev->sequenceNumber + 1; - b = dev->blockInfo; - for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { - if (b->blockState == YAFFS_BLOCK_STATE_FULL && - (b->pagesInUse - b->softDeletions) < dev->param.nChunksPerBlock && - b->sequenceNumber < seq) { - seq = b->sequenceNumber; - blockNo = i; - } - b++; - } - - if(blockNo){ - dev->oldestDirtySequence = seq; - dev->oldestDirtyBlock = blockNo; - } - -} - - -static void yaffs_FindOldestDirtySequence(yaffs_Device *dev) -{ - if(dev->param.isYaffs2 && !dev->oldestDirtySequence) - yaffs_CalcOldestDirtySequence(dev); -} /* - * yaffs_ClearOldestDirtySequence() - * Called when a block is erased or marked bad. (ie. when its sequenceNumber - * becomes invalid). If the value matches the oldest then we clear - * dev->oldestDirtySequence to force its recomputation. - */ -static void yaffs_ClearOldestDirtySequence(yaffs_Device *dev, yaffs_BlockInfo *bi) -{ - - if(!dev->param.isYaffs2) - return; - - if(!bi || bi->sequenceNumber == dev->oldestDirtySequence){ - dev->oldestDirtySequence = 0; - dev->oldestDirtyBlock = 0; - } -} - -/* - * yaffs_UpdateOldestDirtySequence() - * Update the oldest dirty sequence number whenever we dirty a block. - * Only do this if the oldestDirtySequence is actually being tracked. - */ -static void yaffs_UpdateOldestDirtySequence(yaffs_Device *dev, unsigned blockNo, yaffs_BlockInfo *bi) -{ - if(dev->param.isYaffs2 && dev->oldestDirtySequence){ - if(dev->oldestDirtySequence > bi->sequenceNumber){ - dev->oldestDirtySequence = bi->sequenceNumber; - dev->oldestDirtyBlock = blockNo; - } - } -} - -/* * Block retiring for handling a broken block. */ @@ -1143,9 +496,9 @@ static void yaffs_RetireBlock(yaffs_Device *dev, int blockInNAND) { yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); - yaffs_InvalidateCheckpoint(dev); - - yaffs_ClearOldestDirtySequence(dev,bi); + yaffs2_InvalidateCheckpoint(dev); + + yaffs2_ClearOldestDirtySequence(dev,bi); if (yaffs_MarkBlockBad(dev, blockInNAND) != YAFFS_OK) { if (yaffs_EraseBlockInNAND(dev, blockInNAND) != YAFFS_OK) { @@ -1260,7 +613,7 @@ static __u16 yaffs_CalcNameSum(const YCHAR *name) return sum; } -static void yaffs_SetObjectName(yaffs_Object *obj, const YCHAR *name) +void yaffs_SetObjectName(yaffs_Object *obj, const YCHAR *name) { #ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM memset(obj->shortName, 0, sizeof(YCHAR) * (YAFFS_SHORT_NAME_LENGTH+1)); @@ -1272,6 +625,18 @@ static void yaffs_SetObjectName(yaffs_Object *obj, const YCHAR *name) obj->sum = yaffs_CalcNameSum(name); } +void yaffs_SetObjectNameFromOH(yaffs_Object *obj, const yaffs_ObjectHeader *oh) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE + YCHAR tmpName[YAFFS_MAX_NAME_LENGTH+1]; + memset(tmpName,0,sizeof(tmpName)); + yaffs_LoadNameFromObjectHeader(obj->myDev,tmpName,oh->name,YAFFS_MAX_NAME_LENGTH+1); + yaffs_SetObjectName(obj,tmpName); +#else + yaffs_SetObjectName(obj,oh->name); +#endif +} + /*-------------------- TNODES ------------------- * List of spare tnodes @@ -1279,194 +644,33 @@ static void yaffs_SetObjectName(yaffs_Object *obj, const YCHAR *name) * in the tnode. */ -/* yaffs_CreateTnodes creates a bunch more tnodes and - * adds them to the tnode free list. - * Don't use this function directly - */ -static Y_INLINE int yaffs_CalcTnodeSize(yaffs_Device *dev) -{ - int tnodeSize; - /* Calculate the tnode size in bytes for variable width tnode support. - * Must be a multiple of 32-bits */ - tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; - - if (tnodeSize < sizeof(yaffs_Tnode)) - tnodeSize = sizeof(yaffs_Tnode); - return tnodeSize; -} -static int yaffs_CreateTnodes(yaffs_Device *dev, int nTnodes) +yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev) { - int i; - int tnodeSize = yaffs_CalcTnodeSize(dev); - yaffs_Tnode *newTnodes; - __u8 *mem; - yaffs_Tnode *curr; - yaffs_Tnode *next; - yaffs_TnodeList *tnl; - - if (nTnodes < 1) - return YAFFS_OK; - - - /* make these things */ - - newTnodes = YMALLOC(nTnodes * tnodeSize); - mem = (__u8 *)newTnodes; - - if (!newTnodes) { - T(YAFFS_TRACE_ERROR, - (TSTR("yaffs: Could not allocate Tnodes" TENDSTR))); - return YAFFS_FAIL; - } - - /* Hook them into the free list */ -#if 0 - for (i = 0; i < nTnodes - 1; i++) { - newTnodes[i].internal[0] = &newTnodes[i + 1]; -#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG - newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1; -#endif - } - - newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes; -#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG - newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1; -#endif - dev->freeTnodes = newTnodes; -#else - /* New hookup for wide tnodes */ - for (i = 0; i < nTnodes - 1; i++) { - curr = (yaffs_Tnode *) &mem[i * tnodeSize]; - next = (yaffs_Tnode *) &mem[(i+1) * tnodeSize]; - curr->internal[0] = next; - } - - curr = (yaffs_Tnode *) &mem[(nTnodes - 1) * tnodeSize]; - curr->internal[0] = dev->freeTnodes; - dev->freeTnodes = (yaffs_Tnode *)mem; - -#endif - - - dev->nFreeTnodes += nTnodes; - dev->nTnodesCreated += nTnodes; - - /* Now add this bunch of tnodes to a list for freeing up. - * NB If we can't add this to the management list it isn't fatal - * but it just means we can't free this bunch of tnodes later. - */ - - tnl = YMALLOC(sizeof(yaffs_TnodeList)); - if (!tnl) { - T(YAFFS_TRACE_ERROR, - (TSTR - ("yaffs: Could not add tnodes to management list" TENDSTR))); - return YAFFS_FAIL; - } else { - tnl->tnodes = newTnodes; - tnl->next = dev->allocatedTnodeList; - dev->allocatedTnodeList = tnl; + yaffs_Tnode *tn = yaffs_AllocateRawTnode(dev); + if (tn){ + memset(tn, 0, dev->tnodeSize); + dev->nTnodes++; } - T(YAFFS_TRACE_ALLOCATE, (TSTR("yaffs: Tnodes added" TENDSTR))); - - return YAFFS_OK; -} - -/* GetTnode gets us a clean tnode. Tries to make allocate more if we run out */ - -static yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device *dev) -{ - yaffs_Tnode *tn = NULL; - -#ifdef CONFIG_YAFFS_VALGRIND_TEST - tn = YMALLOC(yaffs_CalcTnodeSize(dev)); - if(tn) - dev->nTnodesCreated++; -#else - /* If there are none left make more */ - if (!dev->freeTnodes) - yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES); - - if (dev->freeTnodes) { - tn = dev->freeTnodes; -#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG - if (tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1) { - /* Hoosterman, this thing looks like it isn't in the list */ - T(YAFFS_TRACE_ALWAYS, - (TSTR("yaffs: Tnode list bug 1" TENDSTR))); - } -#endif - dev->freeTnodes = dev->freeTnodes->internal[0]; - dev->nFreeTnodes--; - } -#endif dev->nCheckpointBlocksRequired = 0; /* force recalculation*/ return tn; } -static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev) -{ - yaffs_Tnode *tn = yaffs_GetTnodeRaw(dev); - int tnodeSize = yaffs_CalcTnodeSize(dev); - - if (tn) - memset(tn, 0, tnodeSize); - - 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) { - if (tn) { -#ifdef CONFIG_YAFFS_VALGRIND_TEST - YFREE(tn); - dev->nTnodesCreated--; -#else -#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG - if (tn->internal[YAFFS_NTNODES_INTERNAL] != 0) { - /* Hoosterman, this thing looks like it is already in the list */ - T(YAFFS_TRACE_ALWAYS, - (TSTR("yaffs: Tnode list bug 2" TENDSTR))); - } - tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1; -#endif - tn->internal[0] = dev->freeTnodes; - dev->freeTnodes = tn; - dev->nFreeTnodes++; -#endif - } + yaffs_FreeRawTnode(dev,tn); + dev->nTnodes--; dev->nCheckpointBlocksRequired = 0; /* force recalculation*/ } -static void yaffs_DeinitialiseTnodes(yaffs_Device *dev) +static void yaffs_DeinitialiseTnodesAndObjects(yaffs_Device *dev) { - /* Free the list of allocated tnodes */ - yaffs_TnodeList *tmp; - - while (dev->allocatedTnodeList) { - tmp = dev->allocatedTnodeList->next; - - YFREE(dev->allocatedTnodeList->tnodes); - YFREE(dev->allocatedTnodeList); - dev->allocatedTnodeList = tmp; - - } - - dev->freeTnodes = NULL; - dev->nFreeTnodes = 0; - dev->nTnodesCreated = 0; -} - -static void yaffs_InitialiseTnodes(yaffs_Device *dev) -{ - dev->allocatedTnodeList = NULL; - dev->freeTnodes = NULL; - dev->nFreeTnodes = 0; - dev->nTnodesCreated = 0; + yaffs_DeinitialiseRawTnodesAndObjects(dev); + dev->nObjects = 0; + dev->nTnodes = 0; } @@ -1500,7 +704,7 @@ void yaffs_LoadLevel0Tnode(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos, } } -static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, +__u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos) { __u32 *map = (__u32 *)tn; @@ -1537,7 +741,7 @@ static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, */ /* FindLevel0Tnode finds the level 0 tnode, if one exists. */ -static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev, +yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId) { @@ -1591,7 +795,7 @@ static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev, * be plugged into the ttree. */ -static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, +yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId, yaffs_Tnode *passedTn) @@ -1696,7 +900,7 @@ static int yaffs_FindChunkInGroup(yaffs_Device *dev, int theChunk, for (j = 0; theChunk && j < dev->chunkGroupSize; j++) { if (yaffs_CheckChunkBit(dev, theChunk / dev->param.nChunksPerBlock, theChunk % dev->param.nChunksPerBlock)) { - + if(dev->chunkGroupSize == 1) return theChunk; else { @@ -1822,7 +1026,7 @@ static void yaffs_SoftDeleteChunk(yaffs_Device *dev, int chunk) if (theBlock) { theBlock->softDeletions++; dev->nFreeChunks++; - yaffs_UpdateOldestDirtySequence(dev, blockNo, theBlock); + yaffs2_UpdateOldestDirtySequence(dev, blockNo, theBlock); } } @@ -1950,7 +1154,7 @@ static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn, hasData++; } } else { - int tnodeSize_u32 = yaffs_CalcTnodeSize(dev)/sizeof(__u32); + int tnodeSize_u32 = dev->tnodeSize/sizeof(__u32); __u32 *map = (__u32 *)tn; for(i = 0; !hasData && i < tnodeSize_u32; i++){ @@ -2015,96 +1219,32 @@ static int yaffs_PruneFileStructure(yaffs_Device *dev, /*-------------------- 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)); - list = YMALLOC(sizeof(yaffs_ObjectList)); - - if (!newObjects || !list) { - if (newObjects){ - YFREE(newObjects); - newObjects = NULL; - } - if (list){ - YFREE(list); - list = NULL; - } - T(YAFFS_TRACE_ALLOCATE, - (TSTR("yaffs: Could not allocate more objects" TENDSTR))); - return YAFFS_FAIL; - } - - /* Hook them into the free list */ - for (i = 0; i < nObjects - 1; i++) { - newObjects[i].siblings.next = - (struct ylist_head *)(&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->objects = newObjects; - list->next = dev->allocatedObjectList; - dev->allocatedObjectList = list; - - 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; + yaffs_Object *obj = yaffs_AllocateRawObject(dev); -#ifdef CONFIG_YAFFS_VALGRIND_TEST - tn = YMALLOC(sizeof(yaffs_Object)); - if(tn) - dev->nObjectsCreated++; -#else - /* If there are none left make more */ - if (!dev->freeObjects) - yaffs_CreateFreeObjects(dev, YAFFS_ALLOCATION_NOBJECTS); + if (obj) { + dev->nObjects++; - if (dev->freeObjects) { - tn = dev->freeObjects; - dev->freeObjects = - (yaffs_Object *) (dev->freeObjects->siblings.next); - dev->nFreeObjects--; - } -#endif - if (tn) { /* Now sweeten it up... */ - memset(tn, 0, sizeof(yaffs_Object)); - tn->beingCreated = 1; + memset(obj, 0, sizeof(yaffs_Object)); + obj->beingCreated = 1; - tn->myDev = dev; - tn->hdrChunk = 0; - tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN; - YINIT_LIST_HEAD(&(tn->hardLinks)); - YINIT_LIST_HEAD(&(tn->hashLink)); - YINIT_LIST_HEAD(&tn->siblings); + obj->myDev = dev; + obj->hdrChunk = 0; + obj->variantType = YAFFS_OBJECT_TYPE_UNKNOWN; + YINIT_LIST_HEAD(&(obj->hardLinks)); + YINIT_LIST_HEAD(&(obj->hashLink)); + YINIT_LIST_HEAD(&obj->siblings); /* Now make the directory sane */ if (dev->rootDir) { - tn->parent = dev->rootDir; - ylist_add(&(tn->siblings), &dev->rootDir->variant.directoryVariant.children); + obj->parent = dev->rootDir; + ylist_add(&(obj->siblings), &dev->rootDir->variant.directoryVariant.children); } /* Add it to the lost and found directory. @@ -2112,14 +1252,14 @@ static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev) * check if lostNFound exists first */ if (dev->lostNFoundDir) - yaffs_AddObjectToDirectory(dev->lostNFoundDir, tn); + yaffs_AddObjectToDirectory(dev->lostNFoundDir, obj); - tn->beingCreated = 0; + obj->beingCreated = 0; } dev->nCheckpointBlocksRequired = 0; /* force recalculation*/ - return tn; + return obj; } static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev, int number, @@ -2143,54 +1283,46 @@ static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev, int number, } -static void yaffs_UnhashObject(yaffs_Object *tn) +static void yaffs_UnhashObject(yaffs_Object *obj) { int bucket; - yaffs_Device *dev = tn->myDev; + yaffs_Device *dev = obj->myDev; /* If it is still linked into the bucket list, free from the list */ - if (!ylist_empty(&tn->hashLink)) { - ylist_del_init(&tn->hashLink); - bucket = yaffs_HashFunction(tn->objectId); + if (!ylist_empty(&obj->hashLink)) { + ylist_del_init(&obj->hashLink); + bucket = yaffs_HashFunction(obj->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) +static void yaffs_FreeObject(yaffs_Object *obj) { - yaffs_Device *dev = tn->myDev; + yaffs_Device *dev = obj->myDev; - T(YAFFS_TRACE_OS, (TSTR("FreeObject %p inode %p"TENDSTR), tn, tn->myInode)); + T(YAFFS_TRACE_OS, (TSTR("FreeObject %p inode %p"TENDSTR), obj, obj->myInode)); - if (!tn) + if (!obj) YBUG(); - if (tn->parent) + if (obj->parent) YBUG(); - if (!ylist_empty(&tn->siblings)) + if (!ylist_empty(&obj->siblings)) YBUG(); - if (tn->myInode) { + if (obj->myInode) { /* We're still hooked up to a cached inode. * Don't delete now, but mark for later deletion */ - tn->deferedFree = 1; + obj->deferedFree = 1; return; } - yaffs_UnhashObject(tn); + yaffs_UnhashObject(obj); -#ifdef CONFIG_YAFFS_VALGRIND_TEST - YFREE(tn); - dev->nObjectsCreated--; - tn = NULL; -#else - /* Link into the free list. */ - tn->siblings.next = (struct ylist_head *)(dev->freeObjects); - dev->freeObjects = tn; - dev->nFreeObjects++; -#endif + yaffs_FreeRawObject(dev,obj); + dev->nObjects--; dev->nCheckpointBlocksRequired = 0; /* force recalculation*/ } @@ -2201,33 +1333,14 @@ void yaffs_HandleDeferedFree(yaffs_Object *obj) yaffs_FreeObject(obj); } - -static void yaffs_DeinitialiseObjects(yaffs_Device *dev) -{ - /* Free the list of allocated Objects */ - - yaffs_ObjectList *tmp; - - while (dev->allocatedObjectList) { - tmp = dev->allocatedObjectList->next; - YFREE(dev->allocatedObjectList->objects); - YFREE(dev->allocatedObjectList); - - dev->allocatedObjectList = tmp; - } - - dev->freeObjects = NULL; - dev->nFreeObjects = 0; - dev->nObjectsCreated = 0; -} - -static void yaffs_InitialiseObjects(yaffs_Device *dev) +static void yaffs_InitialiseTnodesAndObjects(yaffs_Device *dev) { int i; - dev->allocatedObjectList = NULL; - dev->freeObjects = NULL; - dev->nFreeObjects = 0; + dev->nObjects = 0; + dev->nTnodes = 0; + + yaffs_InitialiseRawTnodesAndObjects(dev); for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { YINIT_LIST_HEAD(&dev->objectBucket[i].list); @@ -2394,9 +1507,9 @@ yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev, int number, return theObject; } -static yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, - int number, - yaffs_ObjectType type) +yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, + int number, + yaffs_ObjectType type) { yaffs_Object *theObject = NULL; @@ -2411,7 +1524,7 @@ static yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, } -static YCHAR *yaffs_CloneString(const YCHAR *str) +YCHAR *yaffs_CloneString(const YCHAR *str) { YCHAR *newStr = NULL; int len; @@ -2519,7 +1632,7 @@ static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, break; } - if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0) < 0) { + if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0, NULL) < 0) { /* Could not create the object header, fail the creation */ yaffs_DeleteObject(in); in = NULL; @@ -2627,7 +1740,7 @@ static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, obj->unlinked = 1; /* If it is a deletion then we mark it as a shrink for gc purposes. */ - if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows) >= 0) + if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows, NULL) >= 0) return YAFFS_OK; } @@ -2696,7 +1809,7 @@ int yaffs_RenameObject(yaffs_Object *oldDir, const YCHAR *oldName, yaffs_UpdateParent(oldDir); if(newDir != oldDir) yaffs_UpdateParent(newDir); - + return result; } return YAFFS_FAIL; @@ -2760,86 +1873,7 @@ static void yaffs_DeinitialiseBlocks(yaffs_Device *dev) dev->chunkBits = NULL; } -static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev, - yaffs_BlockInfo *bi) -{ - - if (!dev->param.isYaffs2) - return 1; /* disqualification only applies to yaffs2. */ - - if (!bi->hasShrinkHeader) - return 1; /* can gc */ - - yaffs_FindOldestDirtySequence(dev); - - /* Can't do gc of this block if there are any blocks older than this one that have - * discarded pages. - */ - return (bi->sequenceNumber <= dev->oldestDirtySequence); -} - -/* - * yaffs_FindRefreshBlock() - * periodically finds the oldest full block by sequence number for refreshing. - * Only for yaffs2. - */ -static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev) -{ - __u32 b ; - - __u32 oldest = 0; - __u32 oldestSequence = 0; - - yaffs_BlockInfo *bi; - - /* - * If refresh period < 10 then refreshing is disabled. - */ - if(dev->param.refreshPeriod < 10 || - !dev->param.isYaffs2) - return oldest; - - /* - * Fix broken values. - */ - if(dev->refreshSkip > dev->param.refreshPeriod) - dev->refreshSkip = dev->param.refreshPeriod; - - if(dev->refreshSkip > 0) - return oldest; - - /* - * Refresh skip is now zero. - * We'll do a refresh this time around.... - * Update the refresh skip and find the oldest block. - */ - dev->refreshSkip = dev->param.refreshPeriod; - dev->refreshCount++; - bi = dev->blockInfo; - for (b = dev->internalStartBlock; b <=dev->internalEndBlock; b++){ - - if (bi->blockState == YAFFS_BLOCK_STATE_FULL){ - - if(oldest < 1 || - bi->sequenceNumber < oldestSequence){ - oldest = b; - oldestSequence = bi->sequenceNumber; - } - } - bi++; - } - - if (oldest > 0) { - T(YAFFS_TRACE_GC, - (TSTR("GC refresh count %d selected block %d with sequenceNumber %d" TENDSTR), - dev->refreshCount, oldest, oldestSequence)); - } - - return oldest; -} - - -static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo) +void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo) { yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockNo); @@ -2853,7 +1887,7 @@ static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo) (TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR), blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : "")); - yaffs_ClearOldestDirtySequence(dev,bi); + yaffs2_ClearOldestDirtySequence(dev,bi); bi->blockState = YAFFS_BLOCK_STATE_DIRTY; @@ -2868,7 +1902,7 @@ static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo) } if (!bi->needsRetiring) { - yaffs_InvalidateCheckpoint(dev); + yaffs2_InvalidateCheckpoint(dev); erasedOk = yaffs_EraseBlockInNAND(dev, blockNo); if (!erasedOk) { dev->nErasureFailures++; @@ -2963,61 +1997,17 @@ static int yaffs_FindBlockForAllocation(yaffs_Device *dev) } - -static int yaffs_CheckpointRequired(yaffs_Device *dev) -{ - int nblocks = dev->internalEndBlock - dev->internalStartBlock + 1 ; - return dev->param.isYaffs2 && - !dev->param.skipCheckpointWrite && - (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); -} -static int yaffs_CalcCheckpointBlocksRequired(yaffs_Device *dev) -{ - if (!dev->nCheckpointBlocksRequired && - yaffs_CheckpointRequired(dev)){ - /* Not a valid value so recalculate */ - int nBytes = 0; - int nBlocks; - int devBlocks = (dev->param.endBlock - dev->param.startBlock + 1); - int tnodeSize = yaffs_CalcTnodeSize(dev); - - nBytes += sizeof(yaffs_CheckpointValidity); - nBytes += sizeof(yaffs_CheckpointDevice); - nBytes += devBlocks * sizeof(yaffs_BlockInfo); - nBytes += devBlocks * dev->chunkBitmapStride; - nBytes += (sizeof(yaffs_CheckpointObject) + sizeof(__u32)) * (dev->nObjectsCreated - dev->nFreeObjects); - nBytes += (tnodeSize + sizeof(__u32)) * (dev->nTnodesCreated - dev->nFreeTnodes); - nBytes += sizeof(yaffs_CheckpointValidity); - nBytes += sizeof(__u32); /* checksum*/ - - /* Round up and add 2 blocks to allow for some bad blocks, so add 3 */ - - nBlocks = (nBytes/(dev->nDataBytesPerChunk * dev->param.nChunksPerBlock)) + 3; - - dev->nCheckpointBlocksRequired = nBlocks; - } - - return dev->nCheckpointBlocksRequired; -} - /* * Check if there's space to allocate... * Thinks.... do we need top make this ths same as yaffs_GetFreeChunks()? */ -static int yaffs_CheckSpaceForAllocation(yaffs_Device *dev, int nChunks) +int yaffs_CheckSpaceForAllocation(yaffs_Device *dev, int nChunks) { int reservedChunks; int reservedBlocks = dev->param.nReservedBlocks; int checkpointBlocks; - if (dev->param.isYaffs2) { - checkpointBlocks = yaffs_CalcCheckpointBlocksRequired(dev) - - dev->blocksInCheckpoint; - if (checkpointBlocks < 0) - checkpointBlocks = 0; - } else { - checkpointBlocks = 0; - } + checkpointBlocks = yaffs2_CalcCheckpointBlocksRequired(dev); reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->param.nChunksPerBlock); @@ -3095,7 +2085,7 @@ static int yaffs_GetErasedChunks(yaffs_Device *dev) * yaffs_SkipRestOfBlock() skips over the rest of the allocation block * if we don't want to write to it. */ -static void yaffs_SkipRestOfBlock(yaffs_Device *dev) +void yaffs_SkipRestOfBlock(yaffs_Device *dev) { if(dev->allocationBlock > 0){ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, dev->allocationBlock); @@ -3114,7 +2104,6 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, int newChunk; int markNAND; int retVal = YAFFS_OK; - int cleanups = 0; int i; int isCheckpointBlock; int matchingChunk; @@ -3143,7 +2132,7 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, if(bi->blockState == YAFFS_BLOCK_STATE_FULL) bi->blockState = YAFFS_BLOCK_STATE_COLLECTING; - + bi->hasShrinkHeader = 0; /* clear the flag so that the block can erase */ dev->gcDisable = 1; @@ -3225,21 +2214,22 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, * No need to copy this, just forget about it and * fix up the object. */ - + /* Free chunks already includes softdeleted chunks. * How ever this chunk is going to soon be really deleted * which will increment free chunks. * We have to decrement free chunks so this works out properly. */ dev->nFreeChunks--; + bi->softDeletions--; object->nDataChunks--; if (object->nDataChunks <= 0) { /* remeber to clean up the object */ - dev->gcCleanupList[cleanups] = + dev->gcCleanupList[dev->nCleanups] = tags.objectId; - cleanups++; + dev->nCleanups++; } markNAND = 0; } else if (0) { @@ -3325,8 +2315,23 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); + + } + + yaffs_VerifyCollectedBlock(dev, bi, block); + + + + if (bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) { + /* + * The gc did not complete. Set block state back to FULL + * because checkpointing does not restore gc. + */ + bi->blockState = YAFFS_BLOCK_STATE_FULL; + } else { + /* The gc completed. */ /* Do any required cleanups */ - for (i = 0; i < cleanups; i++) { + for (i = 0; i < dev->nCleanups; i++) { /* Time to delete the file too */ object = yaffs_FindObjectByNumber(dev, @@ -3346,20 +2351,7 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, } - } - - yaffs_VerifyCollectedBlock(dev, bi, block); - - - if (bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) { - /* - * The gc did not complete. Set block state back to FULL - * because checkpointing does not restore gc. - */ - bi->blockState = YAFFS_BLOCK_STATE_FULL; - } else { - /* The gc completed. */ chunksAfter = yaffs_GetErasedChunks(dev); if (chunksBefore >= chunksAfter) { T(YAFFS_TRACE_GC, @@ -3369,6 +2361,7 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, } dev->gcBlock = 0; dev->gcChunk = 0; + dev->nCleanups = 0; } dev->gcDisable = 0; @@ -3404,7 +2397,7 @@ static unsigned yaffs_FindBlockForGarbageCollection(yaffs_Device *dev, if (bi->gcPrioritise) { prioritisedExists = 1; if (bi->blockState == YAFFS_BLOCK_STATE_FULL && - yaffs_BlockNotDisqualifiedFromGC(dev, bi)) { + yaffs2_BlockNotDisqualifiedFromGC(dev, bi)) { selected = i; prioritised = 1; } @@ -3440,7 +2433,16 @@ static unsigned yaffs_FindBlockForGarbageCollection(yaffs_Device *dev, threshold = dev->param.nChunksPerBlock; iterations = nBlocks; } else { - int maxThreshold = dev->param.nChunksPerBlock/2; + int maxThreshold; + + if(background) + maxThreshold = dev->param.nChunksPerBlock/2; + else + maxThreshold = dev->param.nChunksPerBlock/8; + + if(maxThreshold < YAFFS_GC_PASSIVE_THRESHOLD) + maxThreshold = YAFFS_GC_PASSIVE_THRESHOLD; + threshold = background ? (dev->gcNotDone + 2) * 2 : 0; if(threshold <YAFFS_GC_PASSIVE_THRESHOLD) @@ -3470,7 +2472,7 @@ static unsigned yaffs_FindBlockForGarbageCollection(yaffs_Device *dev, if (bi->blockState == YAFFS_BLOCK_STATE_FULL && pagesUsed < dev->param.nChunksPerBlock && (dev->gcDirtiest < 1 || pagesUsed < dev->gcPagesInUse) && - yaffs_BlockNotDisqualifiedFromGC(dev, bi)) { + yaffs2_BlockNotDisqualifiedFromGC(dev, bi)) { dev->gcDirtiest = dev->gcBlockFinder; dev->gcPagesInUse = pagesUsed; } @@ -3487,7 +2489,7 @@ static unsigned yaffs_FindBlockForGarbageCollection(yaffs_Device *dev, if(!selected && dev->param.isYaffs2 && dev->gcNotDone >= ( background ? 10 : 20)){ - yaffs_FindOldestDirtySequence(dev); + yaffs2_FindOldestDirtySequence(dev); if(dev->oldestDirtyBlock > 0) { selected = dev->oldestDirtyBlock; dev->gcDirtiest = selected; @@ -3505,8 +2507,10 @@ static unsigned yaffs_FindBlockForGarbageCollection(yaffs_Device *dev, dev->param.nChunksPerBlock - dev->gcPagesInUse, prioritised)); + dev->nGCBlocks++; if(background) dev->backgroundGCs++; + dev->gcDirtiest = 0; dev->gcPagesInUse = 0; dev->gcNotDone = 0; @@ -3540,10 +2544,8 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev, int background) int aggressive = 0; int gcOk = YAFFS_OK; int maxTries = 0; - int minErased; int erasedChunks; - int checkpointBlockAdjust; if(dev->param.gcControl && @@ -3562,9 +2564,7 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev, int background) do { maxTries++; - checkpointBlockAdjust = yaffs_CalcCheckpointBlocksRequired(dev) - dev->blocksInCheckpoint; - if (checkpointBlockAdjust < 0) - checkpointBlockAdjust = 0; + checkpointBlockAdjust = yaffs2_CalcCheckpointBlocksRequired(dev); minErased = dev->param.nReservedBlocks + checkpointBlockAdjust + 1; erasedChunks = dev->nErasedBlocks * dev->param.nChunksPerBlock; @@ -3573,6 +2573,9 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev, int background) if (dev->nErasedBlocks < minErased) aggressive = 1; else { + if(!background && erasedChunks > (dev->nFreeChunks / 4)) + break; + if(dev->gcSkip > 20) dev->gcSkip = 20; if(erasedChunks < dev->nFreeChunks/2 || @@ -3590,12 +2593,14 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev, int background) /* If we don't already have a block being gc'd then see if we should start another */ if (dev->gcBlock < 1 && !aggressive) { - dev->gcBlock = yaffs_FindRefreshBlock(dev); + dev->gcBlock = yaffs2_FindRefreshBlock(dev); dev->gcChunk = 0; + dev->nCleanups=0; } if (dev->gcBlock < 1) { dev->gcBlock = yaffs_FindBlockForGarbageCollection(dev, aggressive, background); dev->gcChunk = 0; + dev->nCleanups=0; } if (dev->gcBlock > 0) { @@ -3714,66 +2719,9 @@ static int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in, int chunkInInode, return retVal; } -#ifdef 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) - return YAFFS_FAIL; - - objId = in->objectId; - fSize = in->variant.fileVariant.fileSize; - nChunks = - (fSize + in->myDev->nDataBytesPerChunk - 1) / in->myDev->nDataBytesPerChunk; - - for (chunk = 1; chunk <= nChunks; chunk++) { - tn = yaffs_FindLevel0Tnode(in->myDev, &in->variant.fileVariant, - chunk); - - if (tn) { - - theChunk = yaffs_GetChunkGroupBase(dev, tn, chunk); - - if (yaffs_CheckChunkBits - (dev, theChunk / dev->param.nChunksPerBlock, - theChunk % dev->param.nChunksPerBlock)) { - - yaffs_ReadChunkTagsFromNAND(in->myDev, theChunk, - tags, - &chunkDeleted); - if (yaffs_TagsMatch - (tags, in->objectId, chunk, chunkDeleted)) { - /* found it; */ - - } - } else { - - 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) +int yaffs_PutChunkIntoFile(yaffs_Object *in, int chunkInInode, + int chunkInNAND, int inScan) { /* NB inScan is zero unless scanning. * For forward scanning, inScan is > 0; @@ -3811,7 +2759,7 @@ static int yaffs_PutChunkIntoFile(yaffs_Object *in, int chunkInInode, NULL); if (!tn) return YAFFS_FAIL; - + if(!chunkInNAND) /* Dummy insert, bail now */ return YAFFS_OK; @@ -3829,7 +2777,7 @@ static int yaffs_PutChunkIntoFile(yaffs_Object *in, int chunkInInode, */ if (existingChunk > 0) { - /* NB Right now existing chunk will not be real chunkId if the device >= 32MB + /* NB Right now existing chunk will not be real chunkId if the chunk group size > 1 * 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: @@ -3871,8 +2819,7 @@ static int yaffs_PutChunkIntoFile(yaffs_Object *in, int chunkInInode, } if ((inScan > 0) && - (in->myDev->param.isYaffs2 || - existingChunk <= 0 || + (existingChunk <= 0 || ((existingSerial + 1) & 3) == newSerial)) { /* Forward scanning. * Use new @@ -3941,14 +2888,14 @@ void yaffs_DeleteChunk(yaffs_Device *dev, int chunkId, int markNAND, int lyn) chunkId)); bi = yaffs_GetBlockInfo(dev, block); - - yaffs_UpdateOldestDirtySequence(dev, block, bi); + + yaffs2_UpdateOldestDirtySequence(dev, block, bi); T(YAFFS_TRACE_DELETION, (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId)); - if (markNAND && - bi->blockState != YAFFS_BLOCK_STATE_COLLECTING && !dev->param.isYaffs2) { + if (!dev->param.isYaffs2 && markNAND && + bi->blockState != YAFFS_BLOCK_STATE_COLLECTING) { yaffs_InitialiseTags(&tags); @@ -4026,8 +2973,8 @@ static int yaffs_WriteChunkDataToObject(yaffs_Object *in, int chunkInInode, (TSTR("Writing %d bytes to chunk!!!!!!!!!" TENDSTR), nBytes)); YBUG(); } - - + + newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, useReserve); @@ -4038,7 +2985,7 @@ static int yaffs_WriteChunkDataToObject(yaffs_Object *in, int chunkInInode, if (prevChunkId > 0) yaffs_DeleteChunk(dev, prevChunkId, 1, __LINE__); - yaffs_CheckFileSanity(in); + yaffs_VerifyFileSanity(in); } return newChunkId; @@ -4048,7 +2995,7 @@ static int yaffs_WriteChunkDataToObject(yaffs_Object *in, int chunkInInode, * If name is not NULL, then that new name is used. */ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force, - int isShrink, int shadows) + int isShrink, int shadows, yaffs_XAttrMod *xmod) { yaffs_BlockInfo *bi; @@ -4062,7 +3009,7 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force, int newChunkId; yaffs_ExtendedTags newTags; yaffs_ExtendedTags oldTags; - YCHAR *alias = NULL; + const YCHAR *alias = NULL; __u8 *buffer = NULL; YCHAR oldName[YAFFS_MAX_NAME_LENGTH + 1]; @@ -4074,7 +3021,7 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force, if (!in->fake || in == dev->rootDir || /* The rootDir should also be saved */ - force) { + force || xmod) { yaffs_CheckGarbageCollection(dev,0); yaffs_CheckObjectDetailsLoaded(in); @@ -4091,9 +3038,9 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force, yaffs_VerifyObjectHeader(in, oh, &oldTags, 0); memcpy(oldName, oh->name, sizeof(oh->name)); - } - - memset(buffer, 0xFF, dev->nDataBytesPerChunk); + memset(buffer, 0xFF, sizeof(yaffs_ObjectHeader)); + } else + memset(buffer, 0xFF, dev->nDataBytesPerChunk); oh->type = in->variantType; oh->yst_mode = in->yst_mode; @@ -4121,7 +3068,7 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force, if (name && *name) { memset(oh->name, 0, sizeof(oh->name)); - yaffs_strncpy(oh->name, name, YAFFS_MAX_NAME_LENGTH); + yaffs_LoadObjectHeaderFromName(dev,oh->name,name); } else if (prevChunkId > 0) memcpy(oh->name, oldName, sizeof(oh->name)); else @@ -4161,6 +3108,11 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force, break; } + /* process any xattrib modifications */ + if(xmod) + yaffs_ApplyXMod(in, (char *)buffer, xmod); + + /* Tags */ yaffs_InitialiseTags(&newTags); in->serial++; @@ -4472,562 +3424,6 @@ static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in) } } -/*--------------------- Checkpointing --------------------*/ - - -static int yaffs_WriteCheckpointValidityMarker(yaffs_Device *dev, int head) -{ - yaffs_CheckpointValidity cp; - - memset(&cp, 0, sizeof(cp)); - - cp.structType = sizeof(cp); - cp.magic = YAFFS_MAGIC; - cp.version = YAFFS_CHECKPOINT_VERSION; - cp.head = (head) ? 1 : 0; - - return (yaffs_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp)) ? - 1 : 0; -} - -static int yaffs_ReadCheckpointValidityMarker(yaffs_Device *dev, int head) -{ - yaffs_CheckpointValidity cp; - int ok; - - ok = (yaffs_CheckpointRead(dev, &cp, sizeof(cp)) == sizeof(cp)); - - if (ok) - ok = (cp.structType == sizeof(cp)) && - (cp.magic == YAFFS_MAGIC) && - (cp.version == YAFFS_CHECKPOINT_VERSION) && - (cp.head == ((head) ? 1 : 0)); - return ok ? 1 : 0; -} - -static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp, - yaffs_Device *dev) -{ - cp->nErasedBlocks = dev->nErasedBlocks; - cp->allocationBlock = dev->allocationBlock; - cp->allocationPage = dev->allocationPage; - cp->nFreeChunks = dev->nFreeChunks; - - cp->nDeletedFiles = dev->nDeletedFiles; - cp->nUnlinkedFiles = dev->nUnlinkedFiles; - cp->nBackgroundDeletions = dev->nBackgroundDeletions; - cp->sequenceNumber = dev->sequenceNumber; - -} - -static void yaffs_CheckpointDeviceToDevice(yaffs_Device *dev, - yaffs_CheckpointDevice *cp) -{ - dev->nErasedBlocks = cp->nErasedBlocks; - dev->allocationBlock = cp->allocationBlock; - dev->allocationPage = cp->allocationPage; - dev->nFreeChunks = cp->nFreeChunks; - - dev->nDeletedFiles = cp->nDeletedFiles; - dev->nUnlinkedFiles = cp->nUnlinkedFiles; - dev->nBackgroundDeletions = cp->nBackgroundDeletions; - dev->sequenceNumber = cp->sequenceNumber; -} - - -static int yaffs_WriteCheckpointDevice(yaffs_Device *dev) -{ - yaffs_CheckpointDevice cp; - __u32 nBytes; - __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); - - int ok; - - /* Write device runtime values*/ - yaffs_DeviceToCheckpointDevice(&cp, dev); - cp.structType = sizeof(cp); - - ok = (yaffs_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp)); - - /* Write block info */ - if (ok) { - nBytes = nBlocks * sizeof(yaffs_BlockInfo); - ok = (yaffs_CheckpointWrite(dev, dev->blockInfo, nBytes) == nBytes); - } - - /* Write chunk bits */ - if (ok) { - nBytes = nBlocks * dev->chunkBitmapStride; - ok = (yaffs_CheckpointWrite(dev, dev->chunkBits, nBytes) == nBytes); - } - return ok ? 1 : 0; - -} - -static int yaffs_ReadCheckpointDevice(yaffs_Device *dev) -{ - yaffs_CheckpointDevice cp; - __u32 nBytes; - __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); - - int ok; - - ok = (yaffs_CheckpointRead(dev, &cp, sizeof(cp)) == sizeof(cp)); - if (!ok) - return 0; - - if (cp.structType != sizeof(cp)) - return 0; - - - yaffs_CheckpointDeviceToDevice(dev, &cp); - - nBytes = nBlocks * sizeof(yaffs_BlockInfo); - - ok = (yaffs_CheckpointRead(dev, dev->blockInfo, nBytes) == nBytes); - - if (!ok) - return 0; - nBytes = nBlocks * dev->chunkBitmapStride; - - ok = (yaffs_CheckpointRead(dev, dev->chunkBits, nBytes) == nBytes); - - return ok ? 1 : 0; -} - -static void yaffs_ObjectToCheckpointObject(yaffs_CheckpointObject *cp, - yaffs_Object *obj) -{ - - cp->objectId = obj->objectId; - cp->parentId = (obj->parent) ? obj->parent->objectId : 0; - cp->hdrChunk = obj->hdrChunk; - cp->variantType = obj->variantType; - cp->deleted = obj->deleted; - cp->softDeleted = obj->softDeleted; - cp->unlinked = obj->unlinked; - cp->fake = obj->fake; - cp->renameAllowed = obj->renameAllowed; - cp->unlinkAllowed = obj->unlinkAllowed; - cp->serial = obj->serial; - cp->nDataChunks = obj->nDataChunks; - - if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) - cp->fileSizeOrEquivalentObjectId = obj->variant.fileVariant.fileSize; - else if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) - cp->fileSizeOrEquivalentObjectId = obj->variant.hardLinkVariant.equivalentObjectId; -} - -static int yaffs_CheckpointObjectToObject(yaffs_Object *obj, yaffs_CheckpointObject *cp) -{ - - yaffs_Object *parent; - - if (obj->variantType != cp->variantType) { - T(YAFFS_TRACE_ERROR, (TSTR("Checkpoint read object %d type %d " - TCONT("chunk %d does not match existing object type %d") - TENDSTR), cp->objectId, cp->variantType, cp->hdrChunk, - obj->variantType)); - return 0; - } - - obj->objectId = cp->objectId; - - if (cp->parentId) - parent = yaffs_FindOrCreateObjectByNumber( - obj->myDev, - cp->parentId, - YAFFS_OBJECT_TYPE_DIRECTORY); - else - parent = NULL; - - if (parent) { - if (parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { - T(YAFFS_TRACE_ALWAYS, (TSTR("Checkpoint read object %d parent %d type %d" - TCONT(" chunk %d Parent type, %d, not directory") - TENDSTR), - cp->objectId, cp->parentId, cp->variantType, - cp->hdrChunk, parent->variantType)); - return 0; - } - yaffs_AddObjectToDirectory(parent, obj); - } - - obj->hdrChunk = cp->hdrChunk; - obj->variantType = cp->variantType; - obj->deleted = cp->deleted; - obj->softDeleted = cp->softDeleted; - obj->unlinked = cp->unlinked; - obj->fake = cp->fake; - obj->renameAllowed = cp->renameAllowed; - obj->unlinkAllowed = cp->unlinkAllowed; - obj->serial = cp->serial; - obj->nDataChunks = cp->nDataChunks; - - if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) - obj->variant.fileVariant.fileSize = cp->fileSizeOrEquivalentObjectId; - else if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) - obj->variant.hardLinkVariant.equivalentObjectId = cp->fileSizeOrEquivalentObjectId; - - if (obj->hdrChunk > 0) - obj->lazyLoaded = 1; - return 1; -} - - - -static int yaffs_CheckpointTnodeWorker(yaffs_Object *in, yaffs_Tnode *tn, - __u32 level, int chunkOffset) -{ - int i; - yaffs_Device *dev = in->myDev; - int ok = 1; - int tnodeSize = yaffs_CalcTnodeSize(dev); - - if (tn) { - if (level > 0) { - - for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { - if (tn->internal[i]) { - ok = yaffs_CheckpointTnodeWorker(in, - tn->internal[i], - level - 1, - (chunkOffset<<YAFFS_TNODES_INTERNAL_BITS) + i); - } - } - } else if (level == 0) { - __u32 baseOffset = chunkOffset << YAFFS_TNODES_LEVEL0_BITS; - ok = (yaffs_CheckpointWrite(dev, &baseOffset, sizeof(baseOffset)) == sizeof(baseOffset)); - if (ok) - ok = (yaffs_CheckpointWrite(dev, tn, tnodeSize) == tnodeSize); - } - } - - return ok; - -} - -static int yaffs_WriteCheckpointTnodes(yaffs_Object *obj) -{ - __u32 endMarker = ~0; - int ok = 1; - - if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) { - ok = yaffs_CheckpointTnodeWorker(obj, - obj->variant.fileVariant.top, - obj->variant.fileVariant.topLevel, - 0); - if (ok) - ok = (yaffs_CheckpointWrite(obj->myDev, &endMarker, sizeof(endMarker)) == - sizeof(endMarker)); - } - - return ok ? 1 : 0; -} - -static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj) -{ - __u32 baseChunk; - int ok = 1; - yaffs_Device *dev = obj->myDev; - yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant; - yaffs_Tnode *tn; - int nread = 0; - int tnodeSize = yaffs_CalcTnodeSize(dev); - - ok = (yaffs_CheckpointRead(dev, &baseChunk, sizeof(baseChunk)) == sizeof(baseChunk)); - - while (ok && (~baseChunk)) { - nread++; - /* Read level 0 tnode */ - - - tn = yaffs_GetTnodeRaw(dev); - if (tn) - ok = (yaffs_CheckpointRead(dev, tn, tnodeSize) == tnodeSize); - else - ok = 0; - - if (tn && ok) - ok = yaffs_AddOrFindLevel0Tnode(dev, - fileStructPtr, - baseChunk, - tn) ? 1 : 0; - - if (ok) - ok = (yaffs_CheckpointRead(dev, &baseChunk, sizeof(baseChunk)) == sizeof(baseChunk)); - - } - - T(YAFFS_TRACE_CHECKPOINT, ( - TSTR("Checkpoint read tnodes %d records, last %d. ok %d" TENDSTR), - nread, baseChunk, ok)); - - return ok ? 1 : 0; -} - - -static int yaffs_WriteCheckpointObjects(yaffs_Device *dev) -{ - yaffs_Object *obj; - yaffs_CheckpointObject cp; - int i; - int ok = 1; - struct ylist_head *lh; - - - /* Iterate through the objects in each hash entry, - * dumping them to the checkpointing stream. - */ - - for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { - ylist_for_each(lh, &dev->objectBucket[i].list) { - if (lh) { - obj = ylist_entry(lh, yaffs_Object, hashLink); - if (!obj->deferedFree) { - yaffs_ObjectToCheckpointObject(&cp, obj); - cp.structType = sizeof(cp); - - T(YAFFS_TRACE_CHECKPOINT, ( - TSTR("Checkpoint write object %d parent %d type %d chunk %d obj addr %p" TENDSTR), - cp.objectId, cp.parentId, cp.variantType, cp.hdrChunk, obj)); - - ok = (yaffs_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp)); - - if (ok && obj->variantType == YAFFS_OBJECT_TYPE_FILE) - ok = yaffs_WriteCheckpointTnodes(obj); - } - } - } - } - - /* Dump end of list */ - memset(&cp, 0xFF, sizeof(yaffs_CheckpointObject)); - cp.structType = sizeof(cp); - - if (ok) - ok = (yaffs_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp)); - - return ok ? 1 : 0; -} - -static int yaffs_ReadCheckpointObjects(yaffs_Device *dev) -{ - yaffs_Object *obj; - yaffs_CheckpointObject cp; - int ok = 1; - int done = 0; - yaffs_Object *hardList = NULL; - - while (ok && !done) { - ok = (yaffs_CheckpointRead(dev, &cp, sizeof(cp)) == sizeof(cp)); - if (cp.structType != sizeof(cp)) { - T(YAFFS_TRACE_CHECKPOINT, (TSTR("struct size %d instead of %d ok %d"TENDSTR), - cp.structType, (int)sizeof(cp), ok)); - ok = 0; - } - - T(YAFFS_TRACE_CHECKPOINT, (TSTR("Checkpoint read object %d parent %d type %d chunk %d " TENDSTR), - cp.objectId, cp.parentId, cp.variantType, cp.hdrChunk)); - - if (ok && cp.objectId == ~0) - done = 1; - else if (ok) { - obj = yaffs_FindOrCreateObjectByNumber(dev, cp.objectId, cp.variantType); - if (obj) { - ok = yaffs_CheckpointObjectToObject(obj, &cp); - if (!ok) - break; - if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) { - ok = yaffs_ReadCheckpointTnodes(obj); - } else if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { - obj->hardLinks.next = - (struct ylist_head *) hardList; - hardList = obj; - } - } else - ok = 0; - } - } - - if (ok) - yaffs_HardlinkFixup(dev, hardList); - - return ok ? 1 : 0; -} - -static int yaffs_WriteCheckpointSum(yaffs_Device *dev) -{ - __u32 checkpointSum; - int ok; - - yaffs_GetCheckpointSum(dev, &checkpointSum); - - ok = (yaffs_CheckpointWrite(dev, &checkpointSum, sizeof(checkpointSum)) == sizeof(checkpointSum)); - - if (!ok) - return 0; - - return 1; -} - -static int yaffs_ReadCheckpointSum(yaffs_Device *dev) -{ - __u32 checkpointSum0; - __u32 checkpointSum1; - int ok; - - yaffs_GetCheckpointSum(dev, &checkpointSum0); - - ok = (yaffs_CheckpointRead(dev, &checkpointSum1, sizeof(checkpointSum1)) == sizeof(checkpointSum1)); - - if (!ok) - return 0; - - if (checkpointSum0 != checkpointSum1) - return 0; - - return 1; -} - - -static int yaffs_WriteCheckpointData(yaffs_Device *dev) -{ - int ok = 1; - - if (!yaffs_CheckpointRequired(dev)) { - T(YAFFS_TRACE_CHECKPOINT, (TSTR("skipping checkpoint write" TENDSTR))); - ok = 0; - } - - if (ok) - ok = yaffs_CheckpointOpen(dev, 1); - - if (ok) { - T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint validity" TENDSTR))); - ok = yaffs_WriteCheckpointValidityMarker(dev, 1); - } - if (ok) { - T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint device" TENDSTR))); - ok = yaffs_WriteCheckpointDevice(dev); - } - if (ok) { - T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint objects" TENDSTR))); - ok = yaffs_WriteCheckpointObjects(dev); - } - if (ok) { - T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint validity" TENDSTR))); - ok = yaffs_WriteCheckpointValidityMarker(dev, 0); - } - - if (ok) - ok = yaffs_WriteCheckpointSum(dev); - - if (!yaffs_CheckpointClose(dev)) - ok = 0; - - if (ok) - dev->isCheckpointed = 1; - else - dev->isCheckpointed = 0; - - return dev->isCheckpointed; -} - -static int yaffs_ReadCheckpointData(yaffs_Device *dev) -{ - int ok = 1; - - if (dev->param.skipCheckpointRead || !dev->param.isYaffs2) { - T(YAFFS_TRACE_CHECKPOINT, (TSTR("skipping checkpoint read" TENDSTR))); - ok = 0; - } - - if (ok) - ok = yaffs_CheckpointOpen(dev, 0); /* open for read */ - - if (ok) { - T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint validity" TENDSTR))); - ok = yaffs_ReadCheckpointValidityMarker(dev, 1); - } - if (ok) { - T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint device" TENDSTR))); - ok = yaffs_ReadCheckpointDevice(dev); - } - if (ok) { - T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint objects" TENDSTR))); - ok = yaffs_ReadCheckpointObjects(dev); - } - if (ok) { - T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint validity" TENDSTR))); - ok = yaffs_ReadCheckpointValidityMarker(dev, 0); - } - - if (ok) { - ok = yaffs_ReadCheckpointSum(dev); - T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint checksum %d" TENDSTR), ok)); - } - - if (!yaffs_CheckpointClose(dev)) - ok = 0; - - if (ok) - dev->isCheckpointed = 1; - else - dev->isCheckpointed = 0; - - return ok ? 1 : 0; - -} - -static void yaffs_InvalidateCheckpoint(yaffs_Device *dev) -{ - if (dev->isCheckpointed || - dev->blocksInCheckpoint > 0) { - dev->isCheckpointed = 0; - yaffs_CheckpointInvalidateStream(dev); - } - if (dev->param.markSuperBlockDirty) - dev->param.markSuperBlockDirty(dev); -} - - -int yaffs_CheckpointSave(yaffs_Device *dev) -{ - - T(YAFFS_TRACE_CHECKPOINT, (TSTR("save entry: isCheckpointed %d"TENDSTR), dev->isCheckpointed)); - - yaffs_VerifyObjects(dev); - yaffs_VerifyBlocks(dev); - yaffs_VerifyFreeChunks(dev); - - if (!dev->isCheckpointed) { - yaffs_InvalidateCheckpoint(dev); - yaffs_WriteCheckpointData(dev); - } - - T(YAFFS_TRACE_ALWAYS, (TSTR("save exit: isCheckpointed %d"TENDSTR), dev->isCheckpointed)); - - return dev->isCheckpointed; -} - -int yaffs_CheckpointRestore(yaffs_Device *dev) -{ - int retval; - T(YAFFS_TRACE_CHECKPOINT, (TSTR("restore entry: isCheckpointed %d"TENDSTR), dev->isCheckpointed)); - - retval = yaffs_ReadCheckpointData(dev); - - if (dev->isCheckpointed) { - yaffs_VerifyObjects(dev); - yaffs_VerifyBlocks(dev); - yaffs_VerifyFreeChunks(dev); - } - - T(YAFFS_TRACE_CHECKPOINT, (TSTR("restore exit: isCheckpointed %d"TENDSTR), dev->isCheckpointed)); - - return retval; -} /*--------------------- File read/write ------------------------ * Read and write have very similar structures. @@ -5310,7 +3706,7 @@ int yaffs_DoWriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset, int yaffs_WriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset, int nBytes, int writeThrough) { - yaffs_HandleHole(in,offset); + yaffs2_HandleHole(in,offset); return yaffs_DoWriteDataToFile(in,buffer,offset,nBytes,writeThrough); } @@ -5360,7 +3756,7 @@ static void yaffs_PruneResizedChunks(yaffs_Object *in, int newSize) } -static void yaffs_ResizeDown( yaffs_Object *obj, loff_t newSize) +void yaffs_ResizeDown( yaffs_Object *obj, loff_t newSize) { int newFullChunks; __u32 newSizeOfPartialChunk; @@ -5391,91 +3787,6 @@ static void yaffs_ResizeDown( yaffs_Object *obj, loff_t newSize) } -static int yaffs_HandleHole(yaffs_Object *obj, loff_t newSize) -{ - /* if newsSize > oldFileSize. - * We're going to be writing a hole. - * If the hole is small then write zeros otherwise write a start of hole marker. - */ - - - loff_t oldFileSize; - int increase; - int smallHole ; - int result = YAFFS_OK; - yaffs_Device *dev = NULL; - - __u8 *localBuffer = NULL; - - int smallIncreaseOk = 0; - - if(!obj) - return YAFFS_FAIL; - - if(obj->variantType != YAFFS_OBJECT_TYPE_FILE) - return YAFFS_FAIL; - - dev = obj->myDev; - - /* Bail out if not yaffs2 mode */ - if(!dev->param.isYaffs2) - return YAFFS_OK; - - oldFileSize = obj->variant.fileVariant.fileSize; - - if (newSize <= oldFileSize) - return YAFFS_OK; - - increase = newSize - oldFileSize; - - if(increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->nDataBytesPerChunk && - yaffs_CheckSpaceForAllocation(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) - smallHole = 1; - else - smallHole = 0; - - if(smallHole) - localBuffer= yaffs_GetTempBuffer(dev, __LINE__); - - if(localBuffer){ - /* fill hole with zero bytes */ - int pos = oldFileSize; - int thisWrite; - int written; - memset(localBuffer,0,dev->nDataBytesPerChunk); - smallIncreaseOk = 1; - - while(increase > 0 && smallIncreaseOk){ - thisWrite = increase; - if(thisWrite > dev->nDataBytesPerChunk) - thisWrite = dev->nDataBytesPerChunk; - written = yaffs_DoWriteDataToFile(obj,localBuffer,pos,thisWrite,0); - if(written == thisWrite){ - pos += thisWrite; - increase -= thisWrite; - } else - smallIncreaseOk = 0; - } - - yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__); - - /* If we were out of space then reverse any chunks we've added */ - if(!smallIncreaseOk) - yaffs_ResizeDown(obj, oldFileSize); - } - - if (!smallIncreaseOk && - obj->parent && - obj->parent->objectId != YAFFS_OBJECTID_UNLINKED && - obj->parent->objectId != YAFFS_OBJECTID_DELETED){ - /* Write a hole start header with the old file size */ - yaffs_UpdateObjectHeader(obj, NULL, 0,1,0); - } - - return result; - -} - int yaffs_ResizeFile(yaffs_Object *in, loff_t newSize) { yaffs_Device *dev = in->myDev; @@ -5491,14 +3802,14 @@ int yaffs_ResizeFile(yaffs_Object *in, loff_t newSize) if (newSize == oldFileSize) return YAFFS_OK; - + if(newSize > oldFileSize){ - yaffs_HandleHole(in,newSize); + yaffs2_HandleHole(in,newSize); in->variant.fileVariant.fileSize = newSize; } else { - /* newSize < oldFileSize */ + /* newSize < oldFileSize */ yaffs_ResizeDown(in, newSize); - } + } /* Write a new object header to reflect the resize. * show we've shrunk the file, if need be @@ -5509,7 +3820,7 @@ int yaffs_ResizeFile(yaffs_Object *in, loff_t newSize) !in->isShadowed && in->parent->objectId != YAFFS_OBJECTID_UNLINKED && in->parent->objectId != YAFFS_OBJECTID_DELETED) - yaffs_UpdateObjectHeader(in, NULL, 0,0,0); + yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0, NULL); return YAFFS_OK; @@ -5553,7 +3864,7 @@ int yaffs_FlushFile(yaffs_Object *in, int updateTime, int dataSync) #endif } - retVal = (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0) >= + retVal = (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0, NULL) >= 0) ? YAFFS_OK : YAFFS_FAIL; } } else { @@ -5816,7 +4127,7 @@ int yaffs_Unlink(yaffs_Object *dir, const YCHAR *name) /*----------------------- Initialisation Scanning ---------------------- */ -static void yaffs_HandleShadowedObject(yaffs_Device *dev, int objId, +void yaffs_HandleShadowedObject(yaffs_Device *dev, int objId, int backwardScanning) { yaffs_Object *obj; @@ -5850,13 +4161,8 @@ static void yaffs_HandleShadowedObject(yaffs_Device *dev, int objId, } -typedef struct { - int seq; - int block; -} yaffs_BlockIndex; - -static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList) +void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList) { yaffs_Object *hl; yaffs_Object *in; @@ -5885,29 +4191,6 @@ static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList) } - - - -static int ybicmp(const void *a, const void *b) -{ - register int aseq = ((yaffs_BlockIndex *)a)->seq; - register int bseq = ((yaffs_BlockIndex *)b)->seq; - register int ablock = ((yaffs_BlockIndex *)a)->block; - register int bblock = ((yaffs_BlockIndex *)b)->block; - if (aseq == bseq) - return ablock - bblock; - else - return aseq - bseq; -} - - -struct yaffs_ShadowFixerStruct { - int objectId; - int shadowedId; - struct yaffs_ShadowFixerStruct *next; -}; - - static void yaffs_StripDeletedObjects(yaffs_Device *dev) { /* @@ -5917,6 +4200,9 @@ static void yaffs_StripDeletedObjects(yaffs_Device *dev) struct ylist_head *n; yaffs_Object *l; + if (dev->readOnly) + return; + /* Soft delete all the unlinked files */ ylist_for_each_safe(i, n, &dev->unlinkedDir->variant.directoryVariant.children) { @@ -5952,7 +4238,7 @@ static void yaffs_StripDeletedObjects(yaffs_Device *dev) * This fixes the problem where directories might have inadvertently been deleted * leaving the object "hanging" without being rooted in the directory tree. */ - + static int yaffs_HasNULLParent(yaffs_Device *dev, yaffs_Object *obj) { return (obj == dev->deletedDir || @@ -5970,6 +4256,8 @@ static void yaffs_FixHangingObjects(yaffs_Device *dev) int depthLimit; int hanging; + if (dev->readOnly) + return; /* Iterate through the objects in each hash entry, * looking at each object. @@ -5981,7 +4269,7 @@ static void yaffs_FixHangingObjects(yaffs_Device *dev) if (lh) { obj = ylist_entry(lh, yaffs_Object, hashLink); parent= obj->parent; - + if(yaffs_HasNULLParent(dev,obj)){ /* These directories are not hanging */ hanging = 0; @@ -6030,7 +4318,7 @@ static void yaffs_DeleteDirectoryContents(yaffs_Object *dir) if(dir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) YBUG(); - + ylist_for_each_safe(lh, n, &dir->variant.directoryVariant.children) { if (lh) { obj = ylist_entry(lh, yaffs_Object, siblings); @@ -6044,10 +4332,10 @@ static void yaffs_DeleteDirectoryContents(yaffs_Object *dir) /* Need to use UnlinkObject since Delete would not handle * hardlinked objects correctly. */ - yaffs_UnlinkObject(obj); + yaffs_UnlinkObject(obj); } } - + } static void yaffs_EmptyLostAndFound(yaffs_Device *dev) @@ -6055,446 +4343,6 @@ static void yaffs_EmptyLostAndFound(yaffs_Device *dev) yaffs_DeleteDirectoryContents(dev->lostNFoundDir); } -static int yaffs_Scan(yaffs_Device *dev) -{ - yaffs_ExtendedTags tags; - int blk; - int blockIterator; - int startIterator; - int endIterator; - int result; - - int chunk; - int c; - int deleted; - yaffs_BlockState state; - yaffs_Object *hardList = NULL; - yaffs_BlockInfo *bi; - __u32 sequenceNumber; - yaffs_ObjectHeader *oh; - yaffs_Object *in; - yaffs_Object *parent; - - int alloc_failed = 0; - - struct yaffs_ShadowFixerStruct *shadowFixerList = NULL; - - - __u8 *chunkData; - - - - T(YAFFS_TRACE_SCAN, - (TSTR("yaffs_Scan starts intstartblk %d intendblk %d..." TENDSTR), - dev->internalStartBlock, dev->internalEndBlock)); - - chunkData = yaffs_GetTempBuffer(dev, __LINE__); - - dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; - - /* Scan all the blocks to determine their state */ - bi = dev->blockInfo; - for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { - yaffs_ClearChunkBits(dev, blk); - bi->pagesInUse = 0; - bi->softDeletions = 0; - - yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); - - bi->blockState = state; - bi->sequenceNumber = sequenceNumber; - - if (bi->sequenceNumber == YAFFS_SEQUENCE_BAD_BLOCK) - bi->blockState = state = YAFFS_BLOCK_STATE_DEAD; - - T(YAFFS_TRACE_SCAN_DEBUG, - (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, - state, sequenceNumber)); - - if (state == YAFFS_BLOCK_STATE_DEAD) { - T(YAFFS_TRACE_BAD_BLOCKS, - (TSTR("block %d is bad" TENDSTR), blk)); - } else if (state == YAFFS_BLOCK_STATE_EMPTY) { - T(YAFFS_TRACE_SCAN_DEBUG, - (TSTR("Block empty " TENDSTR))); - dev->nErasedBlocks++; - dev->nFreeChunks += dev->param.nChunksPerBlock; - } - bi++; - } - - startIterator = dev->internalStartBlock; - endIterator = dev->internalEndBlock; - - /* For each block.... */ - for (blockIterator = startIterator; !alloc_failed && blockIterator <= endIterator; - blockIterator++) { - - YYIELD(); - - YYIELD(); - - blk = blockIterator; - - bi = yaffs_GetBlockInfo(dev, blk); - state = bi->blockState; - - deleted = 0; - - /* For each chunk in each block that needs scanning....*/ - for (c = 0; !alloc_failed && c < dev->param.nChunksPerBlock && - state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++) { - /* Read the tags and decide what to do */ - chunk = blk * dev->param.nChunksPerBlock + c; - - result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, - &tags); - - /* Let's have a good look at this chunk... */ - - if (tags.eccResult == YAFFS_ECC_RESULT_UNFIXED || tags.chunkDeleted) { - /* YAFFS1 only... - * A deleted chunk - */ - deleted++; - dev->nFreeChunks++; - /*T((" %d %d deleted\n",blk,c)); */ - } else if (!tags.chunkUsed) { - /* 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) { - /* We're looking at the first chunk in the block so the block is unused */ - state = YAFFS_BLOCK_STATE_EMPTY; - dev->nErasedBlocks++; - } else { - /* this is the block being allocated from */ - T(YAFFS_TRACE_SCAN, - (TSTR - (" Allocating from %d %d" TENDSTR), - blk, c)); - state = YAFFS_BLOCK_STATE_ALLOCATING; - dev->allocationBlock = blk; - dev->allocationPage = c; - dev->allocationBlockFinder = blk; - /* Set block finder here to encourage the allocator to go forth from here. */ - - } - - dev->nFreeChunks += (dev->param.nChunksPerBlock - c); - } else if (tags.chunkId > 0) { - /* chunkId > 0 so it is a data chunk... */ - unsigned int endpos; - - yaffs_SetChunkBit(dev, blk, 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). - */ - - if (!in) - alloc_failed = 1; - - if (in) { - if (!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk, 1)) - alloc_failed = 1; - } - - endpos = - (tags.chunkId - 1) * dev->nDataBytesPerChunk + - tags.byteCount; - if (in && - in->variantType == YAFFS_OBJECT_TYPE_FILE - && in->variant.fileVariant.scannedFileSize < - endpos) { - in->variant.fileVariant. - scannedFileSize = endpos; - if (!dev->param.useHeaderFileSize) { - in->variant.fileVariant. - fileSize = - in->variant.fileVariant. - scannedFileSize; - } - - } - /* 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 - */ - yaffs_SetChunkBit(dev, blk, c); - bi->pagesInUse++; - - result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, - chunkData, - NULL); - - oh = (yaffs_ObjectHeader *) chunkData; - - in = yaffs_FindObjectByNumber(dev, - tags.objectId); - if (in && in->variantType != oh->type) { - /* This should not happen, but somehow - * Wev'e ended up with an objectId that has been reused but not yet - * deleted, and worse still it has changed type. Delete the old object. - */ - - yaffs_DeleteObject(in); - - in = 0; - } - - in = yaffs_FindOrCreateObjectByNumber(dev, - tags. - objectId, - oh->type); - - if (!in) - alloc_failed = 1; - - if (in && oh->shadowsObject > 0) { - - struct yaffs_ShadowFixerStruct *fixer; - fixer = YMALLOC(sizeof(struct yaffs_ShadowFixerStruct)); - if (fixer) { - fixer->next = shadowFixerList; - shadowFixerList = fixer; - fixer->objectId = tags.objectId; - fixer->shadowedId = oh->shadowsObject; - } - - } - - if (in && 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->hdrChunk, - 1, __LINE__); - in->valid = 0; - } else { - /* Use existing - destroy this one. */ - yaffs_DeleteChunk(dev, chunk, 1, - __LINE__); - } - } - - if (in && !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->yst_mode = oh->yst_mode; -#ifdef CONFIG_YAFFS_WINCE - in->win_atime[0] = oh->win_atime[0]; - in->win_ctime[0] = oh->win_ctime[0]; - in->win_mtime[0] = oh->win_mtime[0]; - in->win_atime[1] = oh->win_atime[1]; - in->win_ctime[1] = oh->win_ctime[1]; - in->win_mtime[1] = oh->win_mtime[1]; -#else - in->yst_uid = oh->yst_uid; - in->yst_gid = oh->yst_gid; - in->yst_atime = oh->yst_atime; - in->yst_mtime = oh->yst_mtime; - in->yst_ctime = oh->yst_ctime; - in->yst_rdev = oh->yst_rdev; -#endif - in->hdrChunk = chunk; - in->serial = tags.serialNumber; - - } else if (in && !in->valid) { - /* we need to load this info */ - - in->valid = 1; - in->variantType = oh->type; - - in->yst_mode = oh->yst_mode; -#ifdef CONFIG_YAFFS_WINCE - in->win_atime[0] = oh->win_atime[0]; - in->win_ctime[0] = oh->win_ctime[0]; - in->win_mtime[0] = oh->win_mtime[0]; - in->win_atime[1] = oh->win_atime[1]; - in->win_ctime[1] = oh->win_ctime[1]; - in->win_mtime[1] = oh->win_mtime[1]; -#else - in->yst_uid = oh->yst_uid; - in->yst_gid = oh->yst_gid; - in->yst_atime = oh->yst_atime; - in->yst_mtime = oh->yst_mtime; - in->yst_ctime = oh->yst_ctime; - in->yst_rdev = oh->yst_rdev; -#endif - in->hdrChunk = chunk; - in->serial = tags.serialNumber; - - yaffs_SetObjectName(in, oh->name); - in->dirty = 0; - - /* directory stuff... - * hook up to parent - */ - - parent = - yaffs_FindOrCreateObjectByNumber - (dev, oh->parentObjectId, - YAFFS_OBJECT_TYPE_DIRECTORY); - if (!parent) - alloc_failed = 1; - if (parent && parent->variantType == - YAFFS_OBJECT_TYPE_UNKNOWN) { - /* Set up as a directory */ - parent->variantType = - YAFFS_OBJECT_TYPE_DIRECTORY; - YINIT_LIST_HEAD(&parent->variant. - directoryVariant. - children); - } else if (!parent || parent->variantType != - YAFFS_OBJECT_TYPE_DIRECTORY) { - /* Hoosterman, another problem.... - * We're trying to use a non-directory as a directory - */ - - T(YAFFS_TRACE_ERROR, - (TSTR - ("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." - TENDSTR))); - parent = dev->lostNFoundDir; - } - - yaffs_AddObjectToDirectory(parent, in); - - if (0 && (parent == dev->deletedDir || - 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: - if (dev->param.useHeaderFileSize) - - in->variant.fileVariant. - fileSize = - oh->fileSize; - - break; - case YAFFS_OBJECT_TYPE_HARDLINK: - in->variant.hardLinkVariant. - equivalentObjectId = - oh->equivalentObjectId; - in->hardLinks.next = - (struct ylist_head *) - 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: - in->variant.symLinkVariant.alias = - yaffs_CloneString(oh->alias); - if (!in->variant.symLinkVariant.alias) - alloc_failed = 1; - break; - } - - } - } - } - - if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { - /* If we got this far while scanning, then the block is fully allocated.*/ - state = YAFFS_BLOCK_STATE_FULL; - } - - if (state == YAFFS_BLOCK_STATE_ALLOCATING) { - /* If the block was partially allocated then treat it as fully allocated.*/ - state = YAFFS_BLOCK_STATE_FULL; - dev->allocationBlock = -1; - } - - bi->blockState = state; - - /* Now let's see if it was dirty */ - if (bi->pagesInUse == 0 && - !bi->hasShrinkHeader && - bi->blockState == YAFFS_BLOCK_STATE_FULL) { - yaffs_BlockBecameDirty(dev, blk); - } - - } - - - /* Ok, we've done all the scanning. - * Fix up the hard link chains. - * We should now have scanned all the objects, now it's time to add these - * hardlinks. - */ - - yaffs_HardlinkFixup(dev, hardList); - - /* Fix up any shadowed objects */ - { - struct yaffs_ShadowFixerStruct *fixer; - yaffs_Object *obj; - - while (shadowFixerList) { - fixer = shadowFixerList; - shadowFixerList = fixer->next; - /* Complete the rename transaction by deleting the shadowed object - * then setting the object header to unshadowed. - */ - obj = yaffs_FindObjectByNumber(dev, fixer->shadowedId); - if (obj) - yaffs_DeleteObject(obj); - - obj = yaffs_FindObjectByNumber(dev, fixer->objectId); - - if (obj) - yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0); - - YFREE(fixer); - } - } - - yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); - - if (alloc_failed) - return YAFFS_FAIL; - - T(YAFFS_TRACE_SCAN, (TSTR("yaffs_Scan ends" TENDSTR))); - - - return YAFFS_OK; -} - static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in) { __u8 *chunkData; @@ -6539,7 +4387,7 @@ static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in) in->yst_rdev = oh->yst_rdev; #endif - yaffs_SetObjectName(in, oh->name); + yaffs_SetObjectNameFromOH(in, oh); if (in->variantType == YAFFS_OBJECT_TYPE_SYMLINK) { in->variant.symLinkVariant.alias = @@ -6552,732 +4400,8 @@ static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in) } } -static int yaffs_ScanBackwards(yaffs_Device *dev) -{ - yaffs_ExtendedTags tags; - int blk; - int blockIterator; - int startIterator; - int endIterator; - int nBlocksToScan = 0; - - int chunk; - int result; - int c; - int deleted; - yaffs_BlockState state; - yaffs_Object *hardList = NULL; - yaffs_BlockInfo *bi; - __u32 sequenceNumber; - yaffs_ObjectHeader *oh; - yaffs_Object *in; - yaffs_Object *parent; - int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; - int itsUnlinked; - __u8 *chunkData; - - int fileSize; - int isShrink; - int foundChunksInBlock; - int equivalentObjectId; - int alloc_failed = 0; - - - yaffs_BlockIndex *blockIndex = NULL; - int altBlockIndex = 0; - - if (!dev->param.isYaffs2) { - T(YAFFS_TRACE_SCAN, - (TSTR("yaffs_ScanBackwards is only for YAFFS2!" TENDSTR))); - return YAFFS_FAIL; - } - - T(YAFFS_TRACE_SCAN, - (TSTR - ("yaffs_ScanBackwards starts intstartblk %d intendblk %d..." - TENDSTR), dev->internalStartBlock, dev->internalEndBlock)); - - - dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; - - blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); - - if (!blockIndex) { - blockIndex = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockIndex)); - altBlockIndex = 1; - } - - if (!blockIndex) { - T(YAFFS_TRACE_SCAN, - (TSTR("yaffs_Scan() could not allocate block index!" TENDSTR))); - return YAFFS_FAIL; - } - - dev->blocksInCheckpoint = 0; - - chunkData = yaffs_GetTempBuffer(dev, __LINE__); - - /* Scan all the blocks to determine their state */ - bi = dev->blockInfo; - for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { - yaffs_ClearChunkBits(dev, blk); - bi->pagesInUse = 0; - bi->softDeletions = 0; - - yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); - - bi->blockState = state; - bi->sequenceNumber = sequenceNumber; - - if (bi->sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA) - bi->blockState = state = YAFFS_BLOCK_STATE_CHECKPOINT; - if (bi->sequenceNumber == YAFFS_SEQUENCE_BAD_BLOCK) - bi->blockState = state = YAFFS_BLOCK_STATE_DEAD; - - T(YAFFS_TRACE_SCAN_DEBUG, - (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, - state, sequenceNumber)); - - - if (state == YAFFS_BLOCK_STATE_CHECKPOINT) { - dev->blocksInCheckpoint++; - - } else if (state == YAFFS_BLOCK_STATE_DEAD) { - T(YAFFS_TRACE_BAD_BLOCKS, - (TSTR("block %d is bad" TENDSTR), blk)); - } else if (state == YAFFS_BLOCK_STATE_EMPTY) { - T(YAFFS_TRACE_SCAN_DEBUG, - (TSTR("Block empty " TENDSTR))); - dev->nErasedBlocks++; - dev->nFreeChunks += dev->param.nChunksPerBlock; - } else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { - - /* Determine the highest sequence number */ - if (sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER && - sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) { - - blockIndex[nBlocksToScan].seq = sequenceNumber; - blockIndex[nBlocksToScan].block = blk; - - nBlocksToScan++; - - if (sequenceNumber >= dev->sequenceNumber) - dev->sequenceNumber = sequenceNumber; - } else { - /* TODO: Nasty sequence number! */ - T(YAFFS_TRACE_SCAN, - (TSTR - ("Block scanning block %d has bad sequence number %d" - TENDSTR), blk, sequenceNumber)); - - } - } - bi++; - } - - T(YAFFS_TRACE_SCAN, - (TSTR("%d blocks to be sorted..." TENDSTR), nBlocksToScan)); - - - - YYIELD(); - - /* Sort the blocks */ -#ifndef CONFIG_YAFFS_USE_OWN_SORT - { - /* Use qsort now. */ - yaffs_qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), ybicmp); - } -#else - { - /* Dungy old bubble sort... */ - - yaffs_BlockIndex temp; - int i; - int j; - - for (i = 0; i < nBlocksToScan; i++) - for (j = i + 1; j < nBlocksToScan; j++) - if (blockIndex[i].seq > blockIndex[j].seq) { - temp = blockIndex[j]; - blockIndex[j] = blockIndex[i]; - blockIndex[i] = temp; - } - } -#endif - - YYIELD(); - - T(YAFFS_TRACE_SCAN, (TSTR("...done" TENDSTR))); - - /* Now scan the blocks looking at the data. */ - startIterator = 0; - endIterator = nBlocksToScan - 1; - T(YAFFS_TRACE_SCAN_DEBUG, - (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); - - /* For each block.... backwards */ - for (blockIterator = endIterator; !alloc_failed && blockIterator >= startIterator; - blockIterator--) { - /* Cooperative multitasking! This loop can run for so - long that watchdog timers expire. */ - YYIELD(); - - /* get the block to scan in the correct order */ - blk = blockIndex[blockIterator].block; - - bi = yaffs_GetBlockInfo(dev, blk); - - - state = bi->blockState; - - deleted = 0; - - /* For each chunk in each block that needs scanning.... */ - foundChunksInBlock = 0; - for (c = dev->param.nChunksPerBlock - 1; - !alloc_failed && c >= 0 && - (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || - state == YAFFS_BLOCK_STATE_ALLOCATING); c--) { - /* Scan backwards... - * Read the tags and decide what to do - */ - - chunk = blk * dev->param.nChunksPerBlock + c; - - result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, - &tags); - - /* Let's have a good look at this chunk... */ - - if (!tags.chunkUsed) { - /* An unassigned chunk in the block. - * If there are used chunks after this one, then - * it is a chunk that was skipped due to failing the erased - * check. Just skip it so that it can be deleted. - * But, more typically, We get here when this is an unallocated - * chunk and his means that either the block is empty or - * this is the one being allocated from - */ - - if (foundChunksInBlock) { - /* This is a chunk that was skipped due to failing the erased check */ - } else if (c == 0) { - /* We're looking at the first chunk in the block so the block is unused */ - state = YAFFS_BLOCK_STATE_EMPTY; - dev->nErasedBlocks++; - } else { - if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || - state == YAFFS_BLOCK_STATE_ALLOCATING) { - if (dev->sequenceNumber == bi->sequenceNumber) { - /* this is the block being allocated from */ - - T(YAFFS_TRACE_SCAN, - (TSTR - (" Allocating from %d %d" - TENDSTR), blk, c)); - - state = YAFFS_BLOCK_STATE_ALLOCATING; - dev->allocationBlock = blk; - dev->allocationPage = c; - dev->allocationBlockFinder = blk; - } else { - /* This is a partially written block that is not - * the current allocation block. - */ - - T(YAFFS_TRACE_SCAN, - (TSTR("Partially written block %d detected" TENDSTR), - blk)); - } - } - } - - dev->nFreeChunks++; - - } else if (tags.eccResult == YAFFS_ECC_RESULT_UNFIXED) { - T(YAFFS_TRACE_SCAN, - (TSTR(" Unfixed ECC in chunk(%d:%d), chunk ignored"TENDSTR), - blk, c)); - - dev->nFreeChunks++; - - } else if (tags.chunkId > 0) { - /* chunkId > 0 so it is a data chunk... */ - unsigned int endpos; - __u32 chunkBase = - (tags.chunkId - 1) * dev->nDataBytesPerChunk; - - foundChunksInBlock = 1; - - - yaffs_SetChunkBit(dev, blk, c); - bi->pagesInUse++; - - in = yaffs_FindOrCreateObjectByNumber(dev, - tags. - objectId, - YAFFS_OBJECT_TYPE_FILE); - if (!in) { - /* Out of memory */ - alloc_failed = 1; - } - - if (in && - in->variantType == YAFFS_OBJECT_TYPE_FILE - && chunkBase < in->variant.fileVariant.shrinkSize) { - /* This has not been invalidated by a resize */ - if (!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk, -1)) { - alloc_failed = 1; - } - - /* File size is calculated by looking at the data chunks if we have not - * seen an object header yet. Stop this practice once we find an object header. - */ - endpos = chunkBase + tags.byteCount; - - if (!in->valid && /* have not got an object header yet */ - in->variant.fileVariant.scannedFileSize < endpos) { - in->variant.fileVariant.scannedFileSize = endpos; - in->variant.fileVariant.fileSize = endpos; - } - - } else if (in) { - /* This chunk has been invalidated by a resize, or a past file deletion - * so delete the chunk*/ - yaffs_DeleteChunk(dev, chunk, 1, __LINE__); - - } - } else { - /* chunkId == 0, so it is an ObjectHeader. - * Thus, we read in the object header and make the object - */ - foundChunksInBlock = 1; - - yaffs_SetChunkBit(dev, blk, c); - bi->pagesInUse++; - - oh = NULL; - in = NULL; - - if (tags.extraHeaderInfoAvailable) { - in = yaffs_FindOrCreateObjectByNumber(dev, - tags.objectId, - tags.extraObjectType); - if (!in) - alloc_failed = 1; - } - - if (!in || - (!in->valid && dev->param.disableLazyLoad) || - tags.extraShadows || - (!in->valid && - (tags.objectId == YAFFS_OBJECTID_ROOT || - tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))) { - - /* If we don't have valid info then we need to read the chunk - * TODO In future we can probably defer reading the chunk and - * living with invalid data until needed. - */ - - result = yaffs_ReadChunkWithTagsFromNAND(dev, - chunk, - chunkData, - NULL); - - oh = (yaffs_ObjectHeader *) chunkData; - - if (dev->param.inbandTags) { - /* Fix up the header if they got corrupted by inband tags */ - oh->shadowsObject = oh->inbandShadowsObject; - oh->isShrink = oh->inbandIsShrink; - } - - if (!in) { - in = yaffs_FindOrCreateObjectByNumber(dev, tags.objectId, oh->type); - if (!in) - alloc_failed = 1; - } - - } - - if (!in) { - /* TODO Hoosterman we have a problem! */ - T(YAFFS_TRACE_ERROR, - (TSTR - ("yaffs tragedy: Could not make object for object %d at chunk %d during scan" - TENDSTR), tags.objectId, chunk)); - continue; - } - - if (in->valid) { - /* We have already filled this one. - * We have a duplicate that will be discarded, but - * we first have to suck out resize info if it is a file. - */ - - if ((in->variantType == YAFFS_OBJECT_TYPE_FILE) && - ((oh && - oh->type == YAFFS_OBJECT_TYPE_FILE) || - (tags.extraHeaderInfoAvailable && - tags.extraObjectType == YAFFS_OBJECT_TYPE_FILE))) { - __u32 thisSize = - (oh) ? oh->fileSize : tags. - extraFileLength; - __u32 parentObjectId = - (oh) ? oh-> - parentObjectId : tags. - extraParentObjectId; - - - isShrink = - (oh) ? oh->isShrink : tags. - extraIsShrinkHeader; - - /* If it is deleted (unlinked at start also means deleted) - * we treat the file size as being zeroed at this point. - */ - if (parentObjectId == - YAFFS_OBJECTID_DELETED - || parentObjectId == - YAFFS_OBJECTID_UNLINKED) { - thisSize = 0; - isShrink = 1; - } - - if (isShrink && in->variant.fileVariant.shrinkSize > thisSize) - in->variant.fileVariant.shrinkSize = thisSize; - - if (isShrink) - bi->hasShrinkHeader = 1; - - } - /* Use existing - destroy this one. */ - yaffs_DeleteChunk(dev, chunk, 1, __LINE__); - - } - - if (!in->valid && in->variantType != - (oh ? oh->type : tags.extraObjectType)) - T(YAFFS_TRACE_ERROR, ( - TSTR("yaffs tragedy: Bad object type, " - TCONT("%d != %d, for object %d at chunk ") - TCONT("%d during scan") - TENDSTR), oh ? - oh->type : tags.extraObjectType, - in->variantType, tags.objectId, - 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; - - if (oh) { - in->variantType = oh->type; - - in->yst_mode = oh->yst_mode; -#ifdef CONFIG_YAFFS_WINCE - in->win_atime[0] = oh->win_atime[0]; - in->win_ctime[0] = oh->win_ctime[0]; - in->win_mtime[0] = oh->win_mtime[0]; - in->win_atime[1] = oh->win_atime[1]; - in->win_ctime[1] = oh->win_ctime[1]; - in->win_mtime[1] = oh->win_mtime[1]; -#else - in->yst_uid = oh->yst_uid; - in->yst_gid = oh->yst_gid; - in->yst_atime = oh->yst_atime; - in->yst_mtime = oh->yst_mtime; - in->yst_ctime = oh->yst_ctime; - in->yst_rdev = oh->yst_rdev; - -#endif - } else { - in->variantType = tags.extraObjectType; - in->lazyLoaded = 1; - } - - in->hdrChunk = chunk; - - } else if (!in->valid) { - /* we need to load this info */ - - in->valid = 1; - in->hdrChunk = chunk; - - if (oh) { - in->variantType = oh->type; - - in->yst_mode = oh->yst_mode; -#ifdef CONFIG_YAFFS_WINCE - in->win_atime[0] = oh->win_atime[0]; - in->win_ctime[0] = oh->win_ctime[0]; - in->win_mtime[0] = oh->win_mtime[0]; - in->win_atime[1] = oh->win_atime[1]; - in->win_ctime[1] = oh->win_ctime[1]; - in->win_mtime[1] = oh->win_mtime[1]; -#else - in->yst_uid = oh->yst_uid; - in->yst_gid = oh->yst_gid; - in->yst_atime = oh->yst_atime; - in->yst_mtime = oh->yst_mtime; - in->yst_ctime = oh->yst_ctime; - in->yst_rdev = oh->yst_rdev; -#endif - - if (oh->shadowsObject > 0) - yaffs_HandleShadowedObject(dev, - oh-> - shadowsObject, - 1); - - - - yaffs_SetObjectName(in, oh->name); - parent = - yaffs_FindOrCreateObjectByNumber - (dev, oh->parentObjectId, - YAFFS_OBJECT_TYPE_DIRECTORY); - - fileSize = oh->fileSize; - isShrink = oh->isShrink; - equivalentObjectId = oh->equivalentObjectId; - - } else { - in->variantType = tags.extraObjectType; - parent = - yaffs_FindOrCreateObjectByNumber - (dev, tags.extraParentObjectId, - YAFFS_OBJECT_TYPE_DIRECTORY); - fileSize = tags.extraFileLength; - isShrink = tags.extraIsShrinkHeader; - equivalentObjectId = tags.extraEquivalentObjectId; - in->lazyLoaded = 1; - - } - in->dirty = 0; - - if (!parent) - alloc_failed = 1; - - /* directory stuff... - * hook up to parent - */ - - if (parent && parent->variantType == - YAFFS_OBJECT_TYPE_UNKNOWN) { - /* Set up as a directory */ - parent->variantType = - YAFFS_OBJECT_TYPE_DIRECTORY; - YINIT_LIST_HEAD(&parent->variant. - directoryVariant. - children); - } else if (!parent || parent->variantType != - YAFFS_OBJECT_TYPE_DIRECTORY) { - /* Hoosterman, another problem.... - * We're trying to use a non-directory as a directory - */ - - T(YAFFS_TRACE_ERROR, - (TSTR - ("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." - TENDSTR))); - parent = dev->lostNFoundDir; - } - - yaffs_AddObjectToDirectory(parent, in); - - itsUnlinked = (parent == dev->deletedDir) || - (parent == dev->unlinkedDir); - - if (isShrink) { - /* Mark the block as having a shrinkHeader */ - bi->hasShrinkHeader = 1; - } - - /* 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: - - if (in->variant.fileVariant. - scannedFileSize < fileSize) { - /* This covers the case where the file size is greater - * than where the data is - * This will happen if the file is resized to be larger - * than its current data extents. - */ - in->variant.fileVariant.fileSize = fileSize; - in->variant.fileVariant.scannedFileSize = fileSize; - } - - if (in->variant.fileVariant.shrinkSize > fileSize) - in->variant.fileVariant.shrinkSize = fileSize; - - - break; - case YAFFS_OBJECT_TYPE_HARDLINK: - if (!itsUnlinked) { - in->variant.hardLinkVariant.equivalentObjectId = - equivalentObjectId; - in->hardLinks.next = - (struct ylist_head *) 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: - if (oh) { - in->variant.symLinkVariant.alias = - yaffs_CloneString(oh->alias); - if (!in->variant.symLinkVariant.alias) - alloc_failed = 1; - } - break; - } - - } - - } - - } /* End of scanning for each chunk */ - - if (state == YAFFS_BLOCK_STATE_NEEDS_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->hasShrinkHeader && - bi->blockState == YAFFS_BLOCK_STATE_FULL) { - yaffs_BlockBecameDirty(dev, blk); - } - - } - - yaffs_SkipRestOfBlock(dev); - - if (altBlockIndex) - YFREE_ALT(blockIndex); - else - YFREE(blockIndex); - - /* Ok, we've done all the scanning. - * Fix up the hard link chains. - * We should now have scanned all the objects, now it's time to add these - * hardlinks. - */ - yaffs_HardlinkFixup(dev, hardList); - - - yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); - - if (alloc_failed) - return YAFFS_FAIL; - - T(YAFFS_TRACE_SCAN, (TSTR("yaffs_ScanBackwards ends" TENDSTR))); - - return YAFFS_OK; -} - /*------------------------------ Directory Functions ----------------------------- */ -static void yaffs_VerifyObjectInDirectory(yaffs_Object *obj) -{ - struct ylist_head *lh; - yaffs_Object *listObj; - - int count = 0; - - if (!obj) { - T(YAFFS_TRACE_ALWAYS, (TSTR("No object to verify" TENDSTR))); - YBUG(); - return; - } - - if (yaffs_SkipVerification(obj->myDev)) - return; - - if (!obj->parent) { - T(YAFFS_TRACE_ALWAYS, (TSTR("Object does not have parent" TENDSTR))); - YBUG(); - return; - } - - if (obj->parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { - T(YAFFS_TRACE_ALWAYS, (TSTR("Parent is not directory" TENDSTR))); - YBUG(); - } - - /* Iterate through the objects in each hash entry */ - - ylist_for_each(lh, &obj->parent->variant.directoryVariant.children) { - if (lh) { - listObj = ylist_entry(lh, yaffs_Object, siblings); - yaffs_VerifyObject(listObj); - if (obj == listObj) - count++; - } - } - - if (count != 1) { - T(YAFFS_TRACE_ALWAYS, (TSTR("Object in directory %d times" TENDSTR), count)); - YBUG(); - } -} - -static void yaffs_VerifyDirectory(yaffs_Object *directory) -{ - struct ylist_head *lh; - yaffs_Object *listObj; - - if (!directory) { - YBUG(); - return; - } - - if (yaffs_SkipFullVerification(directory->myDev)) - return; - - if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { - T(YAFFS_TRACE_ALWAYS, (TSTR("Directory has wrong type: %d" TENDSTR), directory->variantType)); - YBUG(); - } - - /* Iterate through the objects in each hash entry */ - - ylist_for_each(lh, &directory->variant.directoryVariant.children) { - if (lh) { - listObj = ylist_entry(lh, yaffs_Object, siblings); - if (listObj->parent != directory) { - T(YAFFS_TRACE_ALWAYS, (TSTR("Object in directory list has wrong parent %p" TENDSTR), listObj->parent)); - YBUG(); - } - yaffs_VerifyObjectInDirectory(listObj); - } - } -} - /* *yaffs_UpdateParent() handles fixing a directories mtime and ctime when a new * link (ie. name) is created or deleted in the directory. @@ -7293,26 +4417,28 @@ static void yaffs_VerifyDirectory(yaffs_Object *directory) * If the directory updating is defered then yaffs_UpdateDirtyDirecories must be * called periodically. */ - + static void yaffs_UpdateParent(yaffs_Object *obj) { yaffs_Device *dev; if(!obj) return; +#ifndef CONFIG_YAFFS_WINCE dev = obj->myDev; obj->dirty = 1; obj->yst_mtime = obj->yst_ctime = Y_CURRENT_TIME; if(dev->param.deferDirectoryUpdate){ - struct ylist_head *link = &obj->variant.directoryVariant.dirty; - + struct ylist_head *link = &obj->variant.directoryVariant.dirty; + if(ylist_empty(link)){ ylist_add(link,&dev->dirtyDirectories); T(YAFFS_TRACE_BACKGROUND, (TSTR("Added object %d to dirty directories" TENDSTR),obj->objectId)); } } else - yaffs_UpdateObjectHeader(obj,NULL,0,0,0); + yaffs_UpdateObjectHeader(obj, NULL, 0, 0, 0, NULL); +#endif } void yaffs_UpdateDirtyDirectories(yaffs_Device *dev) @@ -7327,7 +4453,7 @@ void yaffs_UpdateDirtyDirectories(yaffs_Device *dev) while(!ylist_empty(&dev->dirtyDirectories)){ link = dev->dirtyDirectories.next; ylist_del_init(link); - + dS=ylist_entry(link,yaffs_DirectoryStructure,dirty); oV = ylist_entry(dS,yaffs_ObjectVariant,directoryVariant); obj = ylist_entry(oV,yaffs_Object,variant); @@ -7335,7 +4461,7 @@ void yaffs_UpdateDirtyDirectories(yaffs_Device *dev) T(YAFFS_TRACE_BACKGROUND, (TSTR("Update directory %d" TENDSTR), obj->objectId)); if(obj->dirty) - yaffs_UpdateObjectHeader(obj,NULL,0,0,0); + yaffs_UpdateObjectHeader(obj, NULL, 0, 0, 0, NULL); } } @@ -7355,11 +4481,11 @@ static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj) ylist_del_init(&obj->siblings); obj->parent = NULL; - + yaffs_VerifyDirectory(parent); } -static void yaffs_AddObjectToDirectory(yaffs_Object *directory, +void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj) { if (!directory) { @@ -7513,36 +4639,124 @@ yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj) return obj; } -int yaffs_GetObjectName(yaffs_Object *obj, YCHAR *name, int buffSize) -{ - memset(name, 0, buffSize * sizeof(YCHAR)); - - yaffs_CheckObjectDetailsLoaded(obj); +/* + * A note or two on object names. + * * If the object name is missing, we then make one up in the form objnnn + * + * * ASCII names are stored in the object header's name field from byte zero + * * Unicode names are historically stored starting from byte zero. + * + * Then there are automatic Unicode names... + * The purpose of these is to save names in a way that can be read as + * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII + * system to share files. + * + * These automatic unicode are stored slightly differently... + * - If the name can fit in the ASCII character space then they are saved as + * ascii names as per above. + * - If the name needs Unicode then the name is saved in Unicode + * starting at oh->name[1]. - if (obj->objectId == YAFFS_OBJECTID_LOSTNFOUND) { - yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffSize - 1); - } else if (obj->hdrChunk <= 0) { + */ +static void yaffs_FixNullName(yaffs_Object * obj,YCHAR * name, int buffSize) +{ + /* Create an object name if we could not find one. */ + if(yaffs_strnlen(name,YAFFS_MAX_NAME_LENGTH) == 0){ YCHAR locName[20]; YCHAR numString[20]; YCHAR *x = &numString[19]; unsigned v = obj->objectId; numString[19] = 0; - while (v > 0) { + while(v>0){ x--; *x = '0' + (v % 10); v /= 10; } /* make up a name */ yaffs_strcpy(locName, YAFFS_LOSTNFOUND_PREFIX); - yaffs_strcat(locName, x); + yaffs_strcat(locName,x); yaffs_strncpy(name, locName, buffSize - 1); + } +} + +static void yaffs_LoadNameFromObjectHeader(yaffs_Device *dev,YCHAR *name, const YCHAR *ohName, int bufferSize) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE + if(dev->param.autoUnicode){ + if(*ohName){ + /* It is an ASCII name, so do an ASCII to unicode conversion */ + const char *asciiOhName = (const char *)ohName; + int n = bufferSize - 1; + while(n > 0 && *asciiOhName){ + *name = *asciiOhName; + name++; + asciiOhName++; + n--; + } + } else + yaffs_strncpy(name,ohName+1, bufferSize -1); + } else +#endif + yaffs_strncpy(name, ohName, bufferSize - 1); +} + + +static void yaffs_LoadObjectHeaderFromName(yaffs_Device *dev, YCHAR *ohName, const YCHAR *name) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE + + int isAscii; + YCHAR *w; + + if(dev->param.autoUnicode){ + + isAscii = 1; + w = name; + + /* Figure out if the name will fit in ascii character set */ + while(isAscii && *w){ + if((*w) & 0xff00) + isAscii = 0; + w++; + } + + if(isAscii){ + /* It is an ASCII name, so do a unicode to ascii conversion */ + char *asciiOhName = (char *)ohName; + int n = YAFFS_MAX_NAME_LENGTH - 1; + while(n > 0 && *name){ + *asciiOhName= *name; + name++; + asciiOhName++; + n--; + } + } else{ + /* It is a unicode name, so save starting at the second YCHAR */ + *ohName = 0; + yaffs_strncpy(ohName+1,name, YAFFS_MAX_NAME_LENGTH -2); + } + } + else +#endif + yaffs_strncpy(ohName,name, YAFFS_MAX_NAME_LENGTH - 1); +} + +int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize) +{ + memset(name, 0, buffSize * sizeof(YCHAR)); + + yaffs_CheckObjectDetailsLoaded(obj); + + if (obj->objectId == YAFFS_OBJECTID_LOSTNFOUND) { + yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffSize - 1); } #ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM - else if (obj->shortName[0]) - yaffs_strncpy(name, obj->shortName,YAFFS_SHORT_NAME_LENGTH+1); + else if (obj->shortName[0]) { + yaffs_strcpy(name, obj->shortName); + } #endif - else { + else if(obj->hdrChunk > 0) { int result; __u8 *buffer = yaffs_GetTempBuffer(obj->myDev, __LINE__); @@ -7555,15 +4769,17 @@ int yaffs_GetObjectName(yaffs_Object *obj, YCHAR *name, int buffSize) obj->hdrChunk, buffer, NULL); } - yaffs_strncpy(name, oh->name, buffSize - 1); - name[buffSize-1]=0; + yaffs_LoadNameFromObjectHeader(obj->myDev,name,oh->name,buffSize); yaffs_ReleaseTempBuffer(obj->myDev, buffer, __LINE__); } - return yaffs_strnlen(name,buffSize-1); + yaffs_FixNullName(obj,name,buffSize); + + return yaffs_strnlen(name,YAFFS_MAX_NAME_LENGTH); } + int yaffs_GetObjectFileLength(yaffs_Object *obj) { /* Dereference any hard linking */ @@ -7666,7 +4882,7 @@ int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr) if (valid & ATTR_SIZE) yaffs_ResizeFile(obj, attr->ia_size); - yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0); + yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0, NULL); return YAFFS_OK; @@ -7699,6 +4915,123 @@ int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr) #endif + +static int yaffs_DoXMod(yaffs_Object *obj, int set, const YCHAR *name, const void *value, int size, int flags) +{ + yaffs_XAttrMod xmod; + + int result; + + xmod.set = set; + xmod.name = name; + xmod.data = value; + xmod.size = size; + xmod.flags = flags; + xmod.result = -ENOSPC; + + result = yaffs_UpdateObjectHeader(obj, NULL, 0, 0, 0, &xmod); + + if(result > 0) + return xmod.result; + else + return -ENOSPC; +} + +static int yaffs_ApplyXMod(yaffs_Object *obj, char *buffer, yaffs_XAttrMod *xmod) +{ + int retval = 0; + int x_offs = sizeof(yaffs_ObjectHeader); + yaffs_Device *dev = obj->myDev; + int x_size = dev->nDataBytesPerChunk - sizeof(yaffs_ObjectHeader); + + char * x_buffer = buffer + x_offs; + + if(xmod->set) + retval = nval_set(x_buffer, x_size, xmod->name, xmod->data, xmod->size, xmod->flags); + else + retval = nval_del(x_buffer, x_size, xmod->name); + + obj->hasXattr = nval_hasvalues(x_buffer, x_size); + obj->xattrKnown = 1; + + xmod->result = retval; + + return retval; +} + +static int yaffs_DoXFetch(yaffs_Object *obj, const YCHAR *name, void *value, int size) +{ + char *buffer = NULL; + int result; + yaffs_ExtendedTags tags; + yaffs_Device *dev = obj->myDev; + int x_offs = sizeof(yaffs_ObjectHeader); + int x_size = dev->nDataBytesPerChunk - sizeof(yaffs_ObjectHeader); + + char * x_buffer; + + int retval = 0; + + if(obj->hdrChunk < 1) + return -ENODATA; + + /* If we know that the object has no xattribs then don't do all the + * reading and parsing. + */ + if(obj->xattrKnown && !obj->hasXattr){ + if(name) + return -ENODATA; + else + return 0; + } + + buffer = (char *) yaffs_GetTempBuffer(dev, __LINE__); + if(!buffer) + return -ENOMEM; + + result = yaffs_ReadChunkWithTagsFromNAND(dev,obj->hdrChunk, (__u8 *)buffer, &tags); + + if(result != YAFFS_OK) + retval = -ENOENT; + else{ + x_buffer = buffer + x_offs; + + if (!obj->xattrKnown){ + obj->hasXattr = nval_hasvalues(x_buffer, x_size); + obj->xattrKnown = 1; + } + + if(name) + retval = nval_get(x_buffer, x_size, name, value, size); + else + retval = nval_list(x_buffer, x_size, value,size); + } + yaffs_ReleaseTempBuffer(dev,(__u8 *)buffer,__LINE__); + return retval; +} + +int yaffs_SetXAttribute(yaffs_Object *obj, const YCHAR *name, const void * value, int size, int flags) +{ + return yaffs_DoXMod(obj, 1, name, value, size, flags); +} + +int yaffs_RemoveXAttribute(yaffs_Object *obj, const YCHAR *name) +{ + return yaffs_DoXMod(obj, 0, name, NULL, 0, 0); +} + +int yaffs_GetXAttribute(yaffs_Object *obj, const YCHAR *name, void *value, int size) +{ + return yaffs_DoXFetch(obj, name, value, size); +} + +int yaffs_ListXAttributes(yaffs_Object *obj, char *buffer, int size) +{ + return yaffs_DoXFetch(obj, NULL, buffer,size); +} + + + #if 0 int yaffs_DumpObject(yaffs_Object *obj) { @@ -7914,6 +5247,9 @@ int yaffs_GutsInitialise(yaffs_Device *dev) else dev->chunkGroupBits = bits - dev->tnodeWidth; + dev->tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; + if(dev->tnodeSize < sizeof(yaffs_Tnode)) + dev->tnodeSize = sizeof(yaffs_Tnode); dev->chunkGroupSize = 1 << dev->chunkGroupBits; @@ -8003,8 +5339,7 @@ int yaffs_GutsInitialise(yaffs_Device *dev) if (!init_failed && !yaffs_InitialiseBlocks(dev)) init_failed = 1; - yaffs_InitialiseTnodes(dev); - yaffs_InitialiseObjects(dev); + yaffs_InitialiseTnodesAndObjects(dev); if (!init_failed && !yaffs_CreateInitialDirectories(dev)) init_failed = 1; @@ -8013,7 +5348,7 @@ int yaffs_GutsInitialise(yaffs_Device *dev) if (!init_failed) { /* Now scan the flash. */ if (dev->param.isYaffs2) { - if (yaffs_CheckpointRestore(dev)) { + if (yaffs2_CheckpointRestore(dev)) { yaffs_CheckObjectDetailsLoaded(dev->rootDir); T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: restored from checkpoint" TENDSTR))); @@ -8023,9 +5358,8 @@ int yaffs_GutsInitialise(yaffs_Device *dev) * and scan backwards. */ yaffs_DeinitialiseBlocks(dev); - yaffs_DeinitialiseTnodes(dev); - yaffs_DeinitialiseObjects(dev); + yaffs_DeinitialiseTnodesAndObjects(dev); dev->nErasedBlocks = 0; dev->nFreeChunks = 0; @@ -8038,16 +5372,15 @@ int yaffs_GutsInitialise(yaffs_Device *dev) if (!init_failed && !yaffs_InitialiseBlocks(dev)) init_failed = 1; - yaffs_InitialiseTnodes(dev); - yaffs_InitialiseObjects(dev); + yaffs_InitialiseTnodesAndObjects(dev); if (!init_failed && !yaffs_CreateInitialDirectories(dev)) init_failed = 1; - if (!init_failed && !yaffs_ScanBackwards(dev)) + if (!init_failed && !yaffs2_ScanBackwards(dev)) init_failed = 1; } - } else if (!yaffs_Scan(dev)) + } else if (!yaffs1_Scan(dev)) init_failed = 1; yaffs_StripDeletedObjects(dev); @@ -8079,7 +5412,7 @@ int yaffs_GutsInitialise(yaffs_Device *dev) /* Clean up any aborted checkpoint data */ if(!dev->isCheckpointed && dev->blocksInCheckpoint > 0) - yaffs_InvalidateCheckpoint(dev); + yaffs2_InvalidateCheckpoint(dev); T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR))); @@ -8093,8 +5426,7 @@ void yaffs_Deinitialise(yaffs_Device *dev) int i; yaffs_DeinitialiseBlocks(dev); - yaffs_DeinitialiseTnodes(dev); - yaffs_DeinitialiseObjects(dev); + yaffs_DeinitialiseTnodesAndObjects(dev); if (dev->param.nShortOpCaches > 0 && dev->srCache) { @@ -8120,7 +5452,7 @@ void yaffs_Deinitialise(yaffs_Device *dev) } } -static int yaffs_CountFreeChunks(yaffs_Device *dev) +int yaffs_CountFreeChunks(yaffs_Device *dev) { int nFree=0; int b; @@ -8176,9 +5508,7 @@ int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev) nFree -= ((dev->param.nReservedBlocks + 1) * dev->param.nChunksPerBlock); /* Now we figure out how much to reserve for the checkpoint and report that... */ - blocksForCheckpoint = yaffs_CalcCheckpointBlocksRequired(dev) - dev->blocksInCheckpoint; - if (blocksForCheckpoint < 0) - blocksForCheckpoint = 0; + blocksForCheckpoint = yaffs2_CalcCheckpointBlocksRequired(dev); nFree -= (blocksForCheckpoint * dev->param.nChunksPerBlock); @@ -8189,27 +5519,6 @@ int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev) } -static int yaffs_freeVerificationFailures; - -static void yaffs_VerifyFreeChunks(yaffs_Device *dev) -{ - int counted; - int difference; - - if (yaffs_SkipVerification(dev)) - return; - - counted = yaffs_CountFreeChunks(dev); - - difference = dev->nFreeChunks - counted; - - if (difference) { - T(YAFFS_TRACE_ALWAYS, - (TSTR("Freechunks verification failure %d %d %d" TENDSTR), - dev->nFreeChunks, counted, difference)); - yaffs_freeVerificationFailures++; - } -} /*---------------------------------------- YAFFS test code ----------------------*/ @@ -8227,9 +5536,8 @@ 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"); */ -#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG /* yaffs_CheckStruct(yaffs_Tnode, 2 * YAFFS_NTNODES_LEVEL0, "yaffs_Tnode"); */ -#endif + #ifndef CONFIG_YAFFS_WINCE yaffs_CheckStruct(yaffs_ObjectHeader, 512, "yaffs_ObjectHeader"); #endif diff --git a/fs/yaffs2/yaffs_guts.h b/fs/yaffs2/yaffs_guts.h index 3647d6bce6cd..caf1e589b7c6 100644 --- a/fs/yaffs2/yaffs_guts.h +++ b/fs/yaffs2/yaffs_guts.h @@ -16,8 +16,9 @@ #ifndef __YAFFS_GUTS_H__ #define __YAFFS_GUTS_H__ -#include "devextras.h" #include "yportenv.h" +#include "devextras.h" +#include "yaffs_list.h" #define YAFFS_OK 1 #define YAFFS_FAIL 0 @@ -52,7 +53,6 @@ #define YAFFS_MAX_CHUNK_ID 0x000FFFFF -#define YAFFS_UNUSED_OBJECT_ID 0x0003FFFF #define YAFFS_ALLOCATION_NOBJECTS 100 #define YAFFS_ALLOCATION_NTNODES 100 @@ -62,6 +62,7 @@ #define YAFFS_OBJECT_SPACE 0x40000 +#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE -1) #define YAFFS_CHECKPOINT_VERSION 4 @@ -350,23 +351,12 @@ typedef struct { /*--------------------------- Tnode -------------------------- */ union yaffs_Tnode_union { -#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG - union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL + 1]; -#else union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL]; -#endif -/* __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: @@ -428,6 +418,9 @@ struct yaffs_ObjectStruct { __u8 beingCreated:1; /* This object is still being created so skip some checks. */ __u8 isShadowed:1; /* This object is shadowed on the way to being renamed. */ + __u8 xattrKnown:1; /* We know if this has object has xattribs or not. */ + __u8 hasXattr:1; /* This object has xattribs. Valid if xattrKnown. */ + __u8 serial; /* serial number of chunk in NAND. Cached here */ __u16 sum; /* sum of the name to speed searching */ @@ -479,13 +472,6 @@ struct yaffs_ObjectStruct { 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 ylist_head list; int count; @@ -529,7 +515,7 @@ typedef struct { struct yaffs_DeviceParamStruct { - const char *name; + const YCHAR *name; /* * Entry parameters set up way early. Yaffs sets up the rest. @@ -552,7 +538,7 @@ struct yaffs_DeviceParamStruct { * 10 to 20 is a good bet. */ int useNANDECC; /* Flag to decide whether or not to use NANDECC on data (yaffs1) */ - int noTagsECC; /* Flag to decide whether or not to do ECC on packed tags (yaffs2) */ + int noTagsECC; /* Flag to decide whether or not to do ECC on packed tags (yaffs2) */ int isYaffs2; /* Use yaffs2 mode on this device */ @@ -564,6 +550,8 @@ struct yaffs_DeviceParamStruct { __u8 skipCheckpointRead; __u8 skipCheckpointWrite; + int enableXattr; /* Enable xattribs */ + /* NAND access functions (Must be set before calling YAFFS)*/ int (*writeChunkToNAND) (struct yaffs_DeviceStruct *dev, @@ -598,7 +586,7 @@ struct yaffs_DeviceParamStruct { /* Callback to mark the superblock dirty */ void (*markSuperBlockDirty)(struct yaffs_DeviceStruct *dev); - + /* Callback to control garbage collection. */ unsigned (*gcControl)(struct yaffs_DeviceStruct *dev); @@ -607,9 +595,13 @@ struct yaffs_DeviceParamStruct { int disableLazyLoad; /* Disable lazy loading on this device */ int wideTnodesDisabled; /* Set to disable wide tnodes */ int disableSoftDelete; /* yaffs 1 only: Set to disable the use of softdeletion. */ - + int deferDirectoryUpdate; /* Set to defer directory updates */ - + +#ifdef CONFIG_YAFFS_AUTO_UNICODE + int autoUnicode; +#endif + int alwaysCheckErased; /* Force chunk erased check always on */ }; typedef struct yaffs_DeviceParamStruct yaffs_DeviceParam; @@ -619,10 +611,13 @@ struct yaffs_DeviceStruct { /* Context storage. Holds extra OS specific data for this device */ - void *context; + void *osContext; + void *driverContext; + + struct ylist_head devList; /* Runtime parameters. Set up by YAFFS. */ - int nDataBytesPerChunk; + int nDataBytesPerChunk; /* Non-wide tnode stuff */ __u16 chunkGroupBits; /* Number of bits that need to be resolved if @@ -633,6 +628,7 @@ struct yaffs_DeviceStruct { /* Stuff to support wide tnodes */ __u32 tnodeWidth; __u32 tnodeMask; + __u32 tnodeSize; /* Stuff for figuring out file offset to chunk conversions */ __u32 chunkShift; /* Shift value */ @@ -684,19 +680,13 @@ struct yaffs_DeviceStruct { __u32 allocationPage; int allocationBlockFinder; /* Used to search for next allocation block */ - int nTnodesCreated; - yaffs_Tnode *freeTnodes; - int nFreeTnodes; - yaffs_TnodeList *allocatedTnodeList; - - int nObjectsCreated; - yaffs_Object *freeObjects; - int nFreeObjects; + /* Object and Tnode memory management */ + void *allocator; + int nObjects; + int nTnodes; int nHardLinks; - yaffs_ObjectList *allocatedObjectList; - yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS]; __u32 bucketFinder; @@ -704,6 +694,7 @@ struct yaffs_DeviceStruct { /* Garbage collection control */ __u32 *gcCleanupList; /* objects to delete at the end of a GC. */ + __u32 nCleanups; unsigned hasPendingPrioritisedGCs; /* We think this device might have pending prioritised gcs */ unsigned gcDisable; @@ -766,6 +757,7 @@ struct yaffs_DeviceStruct { __u32 allGCs; __u32 passiveGCs; __u32 oldestDirtyGCs; + __u32 nGCBlocks; __u32 backgroundGCs; __u32 nRetriedWrites; __u32 nRetiredBlocks; @@ -821,6 +813,23 @@ typedef struct { } yaffs_CheckpointValidity; +struct yaffs_ShadowFixerStruct { + int objectId; + int shadowedId; + struct yaffs_ShadowFixerStruct *next; +}; + +/* Structure for doing xattr modifications */ +typedef struct { + int set; /* If 0 then this is a deletion */ + const YCHAR *name; + const void *data; + int size; + int flags; + int result; +}yaffs_XAttrMod; + + /*----------------------- YAFFS Functions -----------------------*/ int yaffs_GutsInitialise(yaffs_Device *dev); @@ -886,6 +895,12 @@ YCHAR *yaffs_GetSymlinkAlias(yaffs_Object *obj); yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent, const YCHAR *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev); + +int yaffs_SetXAttribute(yaffs_Object *obj, const YCHAR *name, const void * value, int size, int flags); +int yaffs_GetXAttribute(yaffs_Object *obj, const YCHAR *name, void *value, int size); +int yaffs_ListXAttributes(yaffs_Object *obj, char *buffer, int size); +int yaffs_RemoveXAttribute(yaffs_Object *obj, const YCHAR *name); + /* Special directories */ yaffs_Object *yaffs_Root(yaffs_Device *dev); yaffs_Object *yaffs_LostNFound(yaffs_Device *dev); @@ -906,8 +921,7 @@ int yaffs_DumpObject(yaffs_Object *obj); void yaffs_GutsTest(yaffs_Device *dev); -/* A few useful functions */ -void yaffs_InitialiseTags(yaffs_ExtendedTags *tags); +/* A few useful functions to be used within the core files*/ void yaffs_DeleteChunk(yaffs_Device *dev, int chunkId, int markNAND, int lyn); int yaffs_CheckFF(__u8 *buffer, int nBytes); void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi); @@ -915,5 +929,41 @@ void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi); __u8 *yaffs_GetTempBuffer(yaffs_Device *dev, int lineNo); void yaffs_ReleaseTempBuffer(yaffs_Device *dev, __u8 *buffer, int lineNo); +yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, + int number, + yaffs_ObjectType type); +int yaffs_PutChunkIntoFile(yaffs_Object *in, int chunkInInode, + int chunkInNAND, int inScan); +void yaffs_SetObjectName(yaffs_Object *obj, const YCHAR *name); +void yaffs_SetObjectNameFromOH(yaffs_Object *obj, const yaffs_ObjectHeader *oh); +void yaffs_AddObjectToDirectory(yaffs_Object *directory, + yaffs_Object *obj); +YCHAR *yaffs_CloneString(const YCHAR *str); +void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList); +void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo); +int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, + int force, int isShrink, int shadows, + yaffs_XAttrMod *xop); +void yaffs_HandleShadowedObject(yaffs_Device *dev, int objId, + int backwardScanning); +int yaffs_CheckSpaceForAllocation(yaffs_Device *dev, int nChunks); +yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev); +yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, + yaffs_FileStructure *fStruct, + __u32 chunkId, + yaffs_Tnode *passedTn); + +int yaffs_DoWriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset, + int nBytes, int writeThrough); +void yaffs_ResizeDown( yaffs_Object *obj, loff_t newSize); +void yaffs_SkipRestOfBlock(yaffs_Device *dev); + +int yaffs_CountFreeChunks(yaffs_Device *dev); + +yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev, + yaffs_FileStructure *fStruct, + __u32 chunkId); + +__u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos); #endif diff --git a/fs/yaffs2/yaffs_linux.h b/fs/yaffs2/yaffs_linux.h index 4434e6c497b9..ce059fd23b7e 100644 --- a/fs/yaffs2/yaffs_linux.h +++ b/fs/yaffs2/yaffs_linux.h @@ -29,14 +29,15 @@ struct yaffs_LinuxContext { __u8 *spareBuffer; /* For mtdif2 use. Don't know the size of the buffer * at compile time so we have to allocate it. */ - struct mtd_info *mtd; struct ylist_head searchContexts; void (*putSuperFunc)(struct super_block *sb); struct task_struct *readdirProcess; + unsigned mount_id; }; -#define yaffs_DeviceToContext(dev) ((struct yaffs_LinuxContext *)((dev)->context)) +#define yaffs_DeviceToLC(dev) ((struct yaffs_LinuxContext *)((dev)->osContext)) +#define yaffs_DeviceToMtd(dev) ((struct mtd_info *)((dev)->driverContext)) #endif diff --git a/fs/yaffs2/yaffs_linux_allocator.c b/fs/yaffs2/yaffs_linux_allocator.c new file mode 100644 index 000000000000..41281f33c1ac --- /dev/null +++ b/fs/yaffs2/yaffs_linux_allocator.c @@ -0,0 +1,202 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * Note: Tis code is currently unused. Being checked in in case it becomes useful. + */ + + +#include "yaffs_allocator.h" +#include "yaffs_guts.h" +#include "yaffs_trace.h" +#include "yportenv.h" +#include "yaffs_linux.h" +/* + * Start out with the same allocator as yaffs direct. + * Todo: Change to Linux slab allocator. + */ + + + +#define NAMELEN 20 +struct yaffs_AllocatorStruct { + char tnode_name[NAMELEN+1]; + char object_name[NAMELEN+1]; + struct kmem_cache *tnode_cache; + struct kmem_cache *object_cache; +}; + +typedef struct yaffs_AllocatorStruct yaffs_Allocator; + +int mount_id; + +void yaffs_DeinitialiseRawTnodesAndObjects(yaffs_Device *dev) +{ + yaffs_Allocator *allocator = (yaffs_Allocator *)dev->allocator; + + T(YAFFS_TRACE_ALLOCATE,(TSTR("Deinitialising yaffs allocator\n"))); + + if(allocator){ + if(allocator->tnode_cache){ + kmem_cache_destroy(allocator->tnode_cache); + allocator->tnode_cache = NULL; + } else { + T(YAFFS_TRACE_ALWAYS, + (TSTR("NULL tnode cache\n"))); + YBUG(); + } + + if(allocator->object_cache){ + kmem_cache_destroy(allocator->object_cache); + allocator->object_cache = NULL; + } else { + T(YAFFS_TRACE_ALWAYS, + (TSTR("NULL object cache\n"))); + YBUG(); + } + + YFREE(allocator); + + } else { + T(YAFFS_TRACE_ALWAYS, + (TSTR("Deinitialising NULL allocator\n"))); + YBUG(); + } + dev->allocator = NULL; +} + + +static void fake_ctor0(void *data){data = data;} +static void fake_ctor1(void *data){data = data;} +static void fake_ctor2(void *data){data = data;} +static void fake_ctor3(void *data){data = data;} +static void fake_ctor4(void *data){data = data;} +static void fake_ctor5(void *data){data = data;} +static void fake_ctor6(void *data){data = data;} +static void fake_ctor7(void *data){data = data;} +static void fake_ctor8(void *data){data = data;} +static void fake_ctor9(void *data){data = data;} + +static void (*fake_ctor_list[10]) (void *) = { + fake_ctor0, + fake_ctor1, + fake_ctor2, + fake_ctor3, + fake_ctor4, + fake_ctor5, + fake_ctor6, + fake_ctor7, + fake_ctor8, + fake_ctor9, +}; + +void yaffs_InitialiseRawTnodesAndObjects(yaffs_Device *dev) +{ + yaffs_Allocator *allocator; + unsigned mount_id = yaffs_DeviceToLC(dev)->mount_id; + + T(YAFFS_TRACE_ALLOCATE,(TSTR("Initialising yaffs allocator\n"))); + + if(dev->allocator) + YBUG(); + else if(mount_id >= 10){ + T(YAFFS_TRACE_ALWAYS,(TSTR("Bad mount_id %u\n"),mount_id)); + } else { + allocator = YMALLOC(sizeof(yaffs_Allocator)); + memset(allocator,0,sizeof(yaffs_Allocator)); + dev->allocator = allocator; + + if(!dev->allocator){ + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs allocator creation failed\n"))); + YBUG(); + return; + + } + + sprintf(allocator->tnode_name,"yaffs_t_%u",mount_id); + sprintf(allocator->object_name,"yaffs_o_%u",mount_id); + + allocator->tnode_cache = + kmem_cache_create(allocator->tnode_name, + dev->tnodeSize, + 0, 0, + fake_ctor_list[mount_id]); + if(allocator->tnode_cache) + T(YAFFS_TRACE_ALLOCATE, + (TSTR("tnode cache \"%s\" %p\n"), + allocator->tnode_name,allocator->tnode_cache)); + else { + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs cache creation failed\n"))); + YBUG(); + } + + + allocator->object_cache = + kmem_cache_create(allocator->object_name, + sizeof(yaffs_Object), + 0, 0, + fake_ctor_list[mount_id]); + + if(allocator->object_cache) + T(YAFFS_TRACE_ALLOCATE, + (TSTR("object cache \"%s\" %p\n"), + allocator->object_name,allocator->object_cache)); + + else { + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs cache creation failed\n"))); + YBUG(); + } + } +} + + +yaffs_Tnode *yaffs_AllocateRawTnode(yaffs_Device *dev) +{ + yaffs_Allocator *allocator = dev->allocator; + if(!allocator || !allocator->tnode_cache){ + YBUG(); + return NULL; + } + return kmem_cache_alloc(allocator->tnode_cache, GFP_NOFS); +} + +void yaffs_FreeRawTnode(yaffs_Device *dev, yaffs_Tnode *tn) +{ + yaffs_Allocator *allocator = dev->allocator; + kmem_cache_free(allocator->tnode_cache,tn); +} + +yaffs_Object *yaffs_AllocateRawObject(yaffs_Device *dev) +{ + yaffs_Allocator *allocator = dev->allocator; + if(!allocator){ + YBUG(); + return NULL; + } + if(!allocator->object_cache){ + YBUG(); + return NULL; + } + return kmem_cache_alloc(allocator->object_cache, GFP_NOFS); +} + +void yaffs_FreeRawObject(yaffs_Device *dev, yaffs_Object *obj) +{ + yaffs_Allocator *allocator = dev->allocator; + kmem_cache_free(allocator->object_cache,obj); +} diff --git a/fs/yaffs2/yaffs_list.h b/fs/yaffs2/yaffs_list.h new file mode 100644 index 000000000000..09d80b856224 --- /dev/null +++ b/fs/yaffs2/yaffs_list.h @@ -0,0 +1,127 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * This file is just holds extra declarations of macros that would normally + * be providesd in the Linux kernel. These macros have been written from + * scratch but are functionally equivalent to the Linux ones. + * + */ + +#ifndef __YAFFS_LIST_H__ +#define __YAFFS_LIST_H__ + + +#include "yportenv.h" + +/* + * This is a simple doubly linked list implementation that matches the + * way the Linux kernel doubly linked list implementation works. + */ + +struct ylist_head { + struct ylist_head *next; /* next in chain */ + struct ylist_head *prev; /* previous in chain */ +}; + + +/* Initialise a static list */ +#define YLIST_HEAD(name) \ +struct ylist_head name = { &(name), &(name)} + + + +/* Initialise a list head to an empty list */ +#define YINIT_LIST_HEAD(p) \ +do { \ + (p)->next = (p);\ + (p)->prev = (p); \ +} while (0) + + +/* Add an element to a list */ +static Y_INLINE void ylist_add(struct ylist_head *newEntry, + struct ylist_head *list) +{ + struct ylist_head *listNext = list->next; + + list->next = newEntry; + newEntry->prev = list; + newEntry->next = listNext; + listNext->prev = newEntry; + +} + +static Y_INLINE void ylist_add_tail(struct ylist_head *newEntry, + struct ylist_head *list) +{ + struct ylist_head *listPrev = list->prev; + + list->prev = newEntry; + newEntry->next = list; + newEntry->prev = listPrev; + listPrev->next = newEntry; + +} + + +/* Take an element out of its current list, with or without + * reinitialising the links.of the entry*/ +static Y_INLINE void ylist_del(struct ylist_head *entry) +{ + struct ylist_head *listNext = entry->next; + struct ylist_head *listPrev = entry->prev; + + listNext->prev = listPrev; + listPrev->next = listNext; + +} + +static Y_INLINE void ylist_del_init(struct ylist_head *entry) +{ + ylist_del(entry); + entry->next = entry->prev = entry; +} + + +/* Test if the list is empty */ +static Y_INLINE int ylist_empty(struct ylist_head *entry) +{ + return (entry->next == entry); +} + + +/* ylist_entry takes a pointer to a list entry and offsets it to that + * we can find a pointer to the object it is embedded in. + */ + + +#define ylist_entry(entry, type, member) \ + ((type *)((char *)(entry)-(unsigned long)(&((type *)NULL)->member))) + + +/* ylist_for_each and list_for_each_safe iterate over lists. + * ylist_for_each_safe uses temporary storage to make the list delete safe + */ + +#define ylist_for_each(itervar, list) \ + for (itervar = (list)->next; itervar != (list); itervar = itervar->next) + +#define ylist_for_each_safe(itervar, saveVar, list) \ + for (itervar = (list)->next, saveVar = (list)->next->next; \ + itervar != (list); itervar = saveVar, saveVar = saveVar->next) + + +#endif diff --git a/fs/yaffs2/yaffs_mtdif.c b/fs/yaffs2/yaffs_mtdif.c index 5de3193ea2b9..9ed50bad234a 100644 --- a/fs/yaffs2/yaffs_mtdif.c +++ b/fs/yaffs2/yaffs_mtdif.c @@ -25,12 +25,12 @@ int nandmtd_EraseBlockInNAND(yaffs_Device *dev, int blockNumber) { - struct mtd_info *mtd = yaffs_DeviceToContext(dev)->mtd; + struct mtd_info *mtd = yaffs_DeviceToMtd(dev); __u32 addr = ((loff_t) blockNumber) * dev->param.totalBytesPerChunk * dev->param.nChunksPerBlock; struct erase_info ei; - + int retval = 0; ei.mtd = mtd; diff --git a/fs/yaffs2/yaffs_mtdif1.c b/fs/yaffs2/yaffs_mtdif1.c index aaa02db2f665..3e2a10a62d67 100644 --- a/fs/yaffs2/yaffs_mtdif1.c +++ b/fs/yaffs2/yaffs_mtdif1.c @@ -1,10 +1,11 @@ /* * YAFFS: Yet another FFS. A NAND-flash specific file system. - * yaffs_mtdif1.c NAND mtd interface functions for small-page NAND. * * Copyright (C) 2002-2010 Aleph One Ltd. * for Toby Churchill Ltd and Brightstar Engineering * + * Created by Charles Manning <charles@aleph1.co.uk> + * * 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. @@ -91,7 +92,7 @@ static struct nand_ecclayout nand_oob_16 = { int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device *dev, int chunkInNAND, const __u8 *data, const yaffs_ExtendedTags *etags) { - struct mtd_info *mtd = yaffs_DeviceToContext(dev)->mtd; + struct mtd_info *mtd = yaffs_DeviceToMtd(dev); int chunkBytes = dev->nDataBytesPerChunk; loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; struct mtd_oob_ops ops; @@ -169,7 +170,7 @@ static int rettags(yaffs_ExtendedTags *etags, int eccResult, int retval) int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device *dev, int chunkInNAND, __u8 *data, yaffs_ExtendedTags *etags) { - struct mtd_info *mtd = yaffs_DeviceToContext(dev)->mtd; + struct mtd_info *mtd = yaffs_DeviceToMtd(dev); int chunkBytes = dev->nDataBytesPerChunk; loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; int eccres = YAFFS_ECC_RESULT_NO_ERROR; @@ -280,7 +281,7 @@ int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device *dev, */ int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) { - struct mtd_info *mtd = yaffs_DeviceToContext(dev)->mtd; + struct mtd_info *mtd = yaffs_DeviceToMtd(dev); int blocksize = dev->param.nChunksPerBlock * dev->nDataBytesPerChunk; int retval; @@ -321,7 +322,7 @@ static int nandmtd1_TestPrerequists(struct mtd_info *mtd) int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState *pState, __u32 *pSequenceNumber) { - struct mtd_info *mtd = yaffs_DeviceToContext(dev)->mtd; + struct mtd_info *mtd = yaffs_DeviceToMtd(dev); int chunkNo = blockNo * dev->param.nChunksPerBlock; loff_t addr = (loff_t)chunkNo * dev->nDataBytesPerChunk; yaffs_ExtendedTags etags; diff --git a/fs/yaffs2/yaffs_mtdif2.c b/fs/yaffs2/yaffs_mtdif2.c index c1d447819970..2b0a601fb0c8 100644 --- a/fs/yaffs2/yaffs_mtdif2.c +++ b/fs/yaffs2/yaffs_mtdif2.c @@ -34,7 +34,7 @@ int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device *dev, int chunkInNAND, const __u8 *data, const yaffs_ExtendedTags *tags) { - struct mtd_info *mtd = yaffs_DeviceToContext(dev)->mtd; + struct mtd_info *mtd = yaffs_DeviceToMtd(dev); #if (MTD_VERSION_CODE > MTD_VERSION(2, 6, 17)) struct mtd_oob_ops ops; #else @@ -100,7 +100,7 @@ int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device *dev, int chunkInNAND, int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device *dev, int chunkInNAND, __u8 *data, yaffs_ExtendedTags *tags) { - struct mtd_info *mtd = yaffs_DeviceToContext(dev)->mtd; + struct mtd_info *mtd = yaffs_DeviceToMtd(dev); #if (MTD_VERSION_CODE > MTD_VERSION(2, 6, 17)) struct mtd_oob_ops ops; #endif @@ -141,7 +141,7 @@ int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device *dev, int chunkInNAND, ops.len = data ? dev->nDataBytesPerChunk : packed_tags_size; ops.ooboffs = 0; ops.datbuf = data; - ops.oobbuf = yaffs_DeviceToContext(dev)->spareBuffer; + ops.oobbuf = yaffs_DeviceToLC(dev)->spareBuffer; retval = mtd->read_oob(mtd, addr, &ops); } #else @@ -171,7 +171,7 @@ int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device *dev, int chunkInNAND, } } else { if (tags) { - memcpy(packed_tags_ptr, yaffs_DeviceToContext(dev)->spareBuffer, packed_tags_size); + memcpy(packed_tags_ptr, yaffs_DeviceToLC(dev)->spareBuffer, packed_tags_size); yaffs_UnpackTags2(tags, &pt, !dev->param.noTagsECC); } } @@ -195,7 +195,7 @@ int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device *dev, int chunkInNAND, int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) { - struct mtd_info *mtd = yaffs_DeviceToContext(dev)->mtd; + struct mtd_info *mtd = yaffs_DeviceToMtd(dev); int retval; T(YAFFS_TRACE_MTD, (TSTR("nandmtd2_MarkNANDBlockBad %d" TENDSTR), blockNo)); @@ -215,7 +215,7 @@ int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState *state, __u32 *sequenceNumber) { - struct mtd_info *mtd = yaffs_DeviceToContext(dev)->mtd; + struct mtd_info *mtd = yaffs_DeviceToMtd(dev); int retval; T(YAFFS_TRACE_MTD, diff --git a/fs/yaffs2/yaffs_nameval.c b/fs/yaffs2/yaffs_nameval.c new file mode 100644 index 000000000000..80a47609bab8 --- /dev/null +++ b/fs/yaffs2/yaffs_nameval.c @@ -0,0 +1,197 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 simple implementation of a name-value store assumes a small number of values and fits + * into a small finite buffer. + * + * Each attribute is stored as a record: + * sizeof(int) bytes record size. + * strnlen+1 bytes name null terminated. + * nbytes value. + * ---------- + * total size stored in record size + * + * This code has not been tested with unicode yet. + */ + + +#include "yaffs_nameval.h" + +#include "yportenv.h" + +static int nval_find(const char *xb, int xb_size, const YCHAR *name, + int *exist_size) +{ + int pos=0; + int size; + + memcpy(&size,xb,sizeof(int)); + while(size > 0 && (size < xb_size) && (pos + size < xb_size)){ + if(yaffs_strncmp((YCHAR *)(xb+pos+sizeof(int)),name,size) == 0){ + if(exist_size) + *exist_size = size; + return pos; + } + pos += size; + if(pos < xb_size -sizeof(int)) + memcpy(&size,xb + pos,sizeof(int)); + else + size = 0; + } + if(exist_size) + *exist_size = 0; + return -1; +} + +static int nval_used(const char *xb, int xb_size) +{ + int pos=0; + int size; + + memcpy(&size,xb + pos,sizeof(int)); + while(size > 0 && (size < xb_size) && (pos + size < xb_size)){ + pos += size; + if(pos < xb_size -sizeof(int)) + memcpy(&size,xb + pos,sizeof(int)); + else + size = 0; + } + return pos; +} + +int nval_del(char *xb, int xb_size, const YCHAR *name) +{ + int pos = nval_find(xb, xb_size, name, NULL); + int size; + + if(pos >= 0 && pos < xb_size){ + /* Find size, shift rest over this record, then zero out the rest of buffer */ + memcpy(&size,xb+pos,sizeof(int)); + memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); + memset(xb + (xb_size - size),0,size); + return 0; + } else + return -ENODATA; +} + +int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, int bsize, int flags) +{ + int pos; + int namelen = yaffs_strnlen(name,xb_size); + int reclen; + int size_exist = 0; + int space; + int start; + + pos = nval_find(xb,xb_size,name, &size_exist); + + if(flags & XATTR_CREATE && pos >= 0) + return -EEXIST; + if(flags & XATTR_REPLACE && pos < 0) + return -ENODATA; + + start = nval_used(xb,xb_size); + space = xb_size - start + size_exist; + + reclen = (sizeof(int) + namelen + 1 + bsize); + + if(reclen > space) + return -ENOSPC; + + if(pos >= 0){ + nval_del(xb,xb_size,name); + start = nval_used(xb, xb_size); + } + + pos = start; + + memcpy(xb + pos,&reclen,sizeof(int)); + pos +=sizeof(int); + yaffs_strncpy((YCHAR *)(xb + pos), name, reclen); + pos+= (namelen+1); + memcpy(xb + pos,buf,bsize); + return 0; +} + +int nval_get(const char *xb, int xb_size, const YCHAR *name, char *buf, int bsize) +{ + int pos = nval_find(xb,xb_size,name,NULL); + int size; + + if(pos >= 0 && pos< xb_size){ + + memcpy(&size,xb +pos,sizeof(int)); + pos+=sizeof(int); /* advance past record length */ + size -= sizeof(int); + + /* Advance over name string */ + while(xb[pos] && size > 0 && pos < xb_size){ + pos++; + size--; + } + /*Advance over NUL */ + pos++; + size--; + + if(size <= bsize){ + memcpy(buf,xb + pos,size); + return size; + } + + } + if(pos >= 0) + return -ERANGE; + else + return -ENODATA; +} + +int nval_list(const char *xb, int xb_size, char *buf, int bsize) +{ + int pos = 0; + int size; + int name_len; + int ncopied = 0; + int filled = 0; + + memcpy(&size,xb + pos,sizeof(int)); + while(size > sizeof(int) && size <= xb_size && (pos + size) < xb_size && !filled){ + pos+= sizeof(int); + size-=sizeof(int); + name_len = yaffs_strnlen((YCHAR *)(xb + pos), size); + if(ncopied + name_len + 1 < bsize){ + memcpy(buf,xb+pos,name_len * sizeof(YCHAR)); + buf+= name_len; + *buf = '\0'; + buf++; + if(sizeof(YCHAR) > 1){ + *buf = '\0'; + buf++; + } + ncopied += (name_len+1); + } else + filled = 1; + pos+=size; + if(pos < xb_size -sizeof(int)) + memcpy(&size,xb + pos,sizeof(int)); + else + size = 0; + } + return ncopied; +} + + +int nval_hasvalues(const char *xb, int xb_size) +{ + return nval_used(xb, xb_size) > 0; +} diff --git a/fs/yaffs2/yaffs_nameval.h b/fs/yaffs2/yaffs_nameval.h new file mode 100644 index 000000000000..bd8085937eb3 --- /dev/null +++ b/fs/yaffs2/yaffs_nameval.h @@ -0,0 +1,26 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __NAMEVAL_H__ +#define __NAMEVAL_H__ + +#include "yportenv.h" + +int nval_del(char *xb, int xb_size, const YCHAR *name); +int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, int bsize, int flags); +int nval_get(const char *xb, int xb_size, const YCHAR *name, char *buf, int bsize); +int nval_list(const char *xb, int xb_size, char *buf, int bsize); +int nval_hasvalues(const char *xb, int xb_size); +#endif diff --git a/fs/yaffs2/yaffs_qsort.h b/fs/yaffs2/yaffs_qsort.h index a24d58e37583..4a4981b3f9ad 100644 --- a/fs/yaffs2/yaffs_qsort.h +++ b/fs/yaffs2/yaffs_qsort.h @@ -17,7 +17,18 @@ #ifndef __YAFFS_QSORT_H__ #define __YAFFS_QSORT_H__ +#ifdef __KERNEL__ +#include <linux/sort.h> + +extern void yaffs_qsort(void *const base, size_t total_elems, size_t size, + int (*cmp)(const void *, const void *)){ + sort(base, total_elems, size, cmp, NULL); +} + +#else + extern void yaffs_qsort(void *const base, size_t total_elems, size_t size, int (*cmp)(const void *, const void *)); #endif +#endif diff --git a/fs/yaffs2/yaffs_trace.h b/fs/yaffs2/yaffs_trace.h index 8b848edba05e..94395a9a9547 100644 --- a/fs/yaffs2/yaffs_trace.h +++ b/fs/yaffs2/yaffs_trace.h @@ -54,6 +54,9 @@ extern unsigned int yaffs_wr_attempts; #define YAFFS_TRACE_BUG 0x80000000 #define YAFFS_TRACE_ALWAYS 0xF0000000 +#ifdef T +#undef T +#endif #define T(mask, p) do { if ((mask) & (yaffs_traceMask | YAFFS_TRACE_ALWAYS)) TOUT(p); } while (0) diff --git a/fs/yaffs2/yaffs_verify.c b/fs/yaffs2/yaffs_verify.c new file mode 100644 index 000000000000..a600aa6b1e6b --- /dev/null +++ b/fs/yaffs2/yaffs_verify.c @@ -0,0 +1,626 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + */ + + +#include "yaffs_verify.h" +#include "yaffs_trace.h" +#include "yaffs_bitmap.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_nand.h" + +int yaffs_SkipVerification(yaffs_Device *dev) +{ + dev=dev; + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_SkipFullVerification(yaffs_Device *dev) +{ + dev=dev; + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_SkipNANDVerification(yaffs_Device *dev) +{ + dev=dev; + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_NAND)); +} + + +static const char *blockStateName[] = { +"Unknown", +"Needs scanning", +"Scanning", +"Empty", +"Allocating", +"Full", +"Dirty", +"Checkpoint", +"Collecting", +"Dead" +}; + + +void yaffs_VerifyBlock(yaffs_Device *dev, yaffs_BlockInfo *bi, int n) +{ + int actuallyUsed; + int inUse; + + if (yaffs_SkipVerification(dev)) + return; + + /* Report illegal runtime states */ + if (bi->blockState >= YAFFS_NUMBER_OF_BLOCK_STATES) + T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has undefined state %d"TENDSTR), n, bi->blockState)); + + switch (bi->blockState) { + case YAFFS_BLOCK_STATE_UNKNOWN: + case YAFFS_BLOCK_STATE_SCANNING: + case YAFFS_BLOCK_STATE_NEEDS_SCANNING: + T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has bad run-state %s"TENDSTR), + n, blockStateName[bi->blockState])); + } + + /* Check pages in use and soft deletions are legal */ + + actuallyUsed = bi->pagesInUse - bi->softDeletions; + + if (bi->pagesInUse < 0 || bi->pagesInUse > dev->param.nChunksPerBlock || + bi->softDeletions < 0 || bi->softDeletions > dev->param.nChunksPerBlock || + actuallyUsed < 0 || actuallyUsed > dev->param.nChunksPerBlock) + T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has illegal values pagesInUsed %d softDeletions %d"TENDSTR), + n, bi->pagesInUse, bi->softDeletions)); + + + /* Check chunk bitmap legal */ + inUse = yaffs_CountChunkBits(dev, n); + if (inUse != bi->pagesInUse) + T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has inconsistent values pagesInUse %d counted chunk bits %d"TENDSTR), + n, bi->pagesInUse, inUse)); + +} + + + +void yaffs_VerifyCollectedBlock(yaffs_Device *dev, yaffs_BlockInfo *bi, int n) +{ + yaffs_VerifyBlock(dev, bi, n); + + /* After collection the block should be in the erased state */ + + if (bi->blockState != YAFFS_BLOCK_STATE_COLLECTING && + bi->blockState != YAFFS_BLOCK_STATE_EMPTY) { + T(YAFFS_TRACE_ERROR, (TSTR("Block %d is in state %d after gc, should be erased"TENDSTR), + n, bi->blockState)); + } +} + +void yaffs_VerifyBlocks(yaffs_Device *dev) +{ + int i; + int nBlocksPerState[YAFFS_NUMBER_OF_BLOCK_STATES]; + int nIllegalBlockStates = 0; + + if (yaffs_SkipVerification(dev)) + return; + + memset(nBlocksPerState, 0, sizeof(nBlocksPerState)); + + for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, i); + yaffs_VerifyBlock(dev, bi, i); + + if (bi->blockState < YAFFS_NUMBER_OF_BLOCK_STATES) + nBlocksPerState[bi->blockState]++; + else + nIllegalBlockStates++; + } + + T(YAFFS_TRACE_VERIFY, (TSTR(""TENDSTR))); + T(YAFFS_TRACE_VERIFY, (TSTR("Block summary"TENDSTR))); + + T(YAFFS_TRACE_VERIFY, (TSTR("%d blocks have illegal states"TENDSTR), nIllegalBlockStates)); + if (nBlocksPerState[YAFFS_BLOCK_STATE_ALLOCATING] > 1) + T(YAFFS_TRACE_VERIFY, (TSTR("Too many allocating blocks"TENDSTR))); + + for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) + T(YAFFS_TRACE_VERIFY, + (TSTR("%s %d blocks"TENDSTR), + blockStateName[i], nBlocksPerState[i])); + + if (dev->blocksInCheckpoint != nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT]) + T(YAFFS_TRACE_VERIFY, + (TSTR("Checkpoint block count wrong dev %d count %d"TENDSTR), + dev->blocksInCheckpoint, nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT])); + + if (dev->nErasedBlocks != nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY]) + T(YAFFS_TRACE_VERIFY, + (TSTR("Erased block count wrong dev %d count %d"TENDSTR), + dev->nErasedBlocks, nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY])); + + if (nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING] > 1) + T(YAFFS_TRACE_VERIFY, + (TSTR("Too many collecting blocks %d (max is 1)"TENDSTR), + nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING])); + + T(YAFFS_TRACE_VERIFY, (TSTR(""TENDSTR))); + +} + +/* + * Verify the object header. oh must be valid, but obj and tags may be NULL in which + * case those tests will not be performed. + */ +void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh, yaffs_ExtendedTags *tags, int parentCheck) +{ + if (obj && yaffs_SkipVerification(obj->myDev)) + return; + + if (!(tags && obj && oh)) { + T(YAFFS_TRACE_VERIFY, + (TSTR("Verifying object header tags %p obj %p oh %p"TENDSTR), + tags, obj, oh)); + return; + } + + if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || + oh->type > YAFFS_OBJECT_TYPE_MAX) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header type is illegal value 0x%x"TENDSTR), + tags->objectId, oh->type)); + + if (tags->objectId != obj->objectId) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header mismatch objectId %d"TENDSTR), + tags->objectId, obj->objectId)); + + + /* + * Check that the object's parent ids match if parentCheck requested. + * + * Tests do not apply to the root object. + */ + + if (parentCheck && tags->objectId > 1 && !obj->parent) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header mismatch parentId %d obj->parent is NULL"TENDSTR), + tags->objectId, oh->parentObjectId)); + + if (parentCheck && obj->parent && + oh->parentObjectId != obj->parent->objectId && + (oh->parentObjectId != YAFFS_OBJECTID_UNLINKED || + obj->parent->objectId != YAFFS_OBJECTID_DELETED)) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header mismatch parentId %d parentObjectId %d"TENDSTR), + tags->objectId, oh->parentObjectId, obj->parent->objectId)); + + if (tags->objectId > 1 && oh->name[0] == 0) /* Null name */ + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header name is NULL"TENDSTR), + obj->objectId)); + + if (tags->objectId > 1 && ((__u8)(oh->name[0])) == 0xff) /* Trashed name */ + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header name is 0xFF"TENDSTR), + obj->objectId)); +} + + +#if 0 +/* Not being used, but don't want to throw away yet */ +int yaffs_VerifyTnodeWorker(yaffs_Object *obj, yaffs_Tnode *tn, + __u32 level, int chunkOffset) +{ + int i; + yaffs_Device *dev = obj->myDev; + int ok = 1; + + if (tn) { + if (level > 0) { + + for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { + if (tn->internal[i]) { + ok = yaffs_VerifyTnodeWorker(obj, + tn->internal[i], + level - 1, + (chunkOffset<<YAFFS_TNODES_INTERNAL_BITS) + i); + } + } + } else if (level == 0) { + yaffs_ExtendedTags tags; + __u32 objectId = obj->objectId; + + chunkOffset <<= YAFFS_TNODES_LEVEL0_BITS; + + for (i = 0; i < YAFFS_NTNODES_LEVEL0; i++) { + __u32 theChunk = yaffs_GetChunkGroupBase(dev, tn, i); + + if (theChunk > 0) { + /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),tags.objectId,tags.chunkId,theChunk)); */ + yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, &tags); + if (tags.objectId != objectId || tags.chunkId != chunkOffset) { + T(~0, (TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), + objectId, chunkOffset, theChunk, + tags.objectId, tags.chunkId)); + } + } + chunkOffset++; + } + } + } + + return ok; + +} + +#endif + +void yaffs_VerifyFile(yaffs_Object *obj) +{ + int requiredTallness; + int actualTallness; + __u32 lastChunk; + __u32 x; + __u32 i; + yaffs_Device *dev; + yaffs_ExtendedTags tags; + yaffs_Tnode *tn; + __u32 objectId; + + if (!obj) + return; + + if (yaffs_SkipVerification(obj->myDev)) + return; + + dev = obj->myDev; + objectId = obj->objectId; + + /* Check file size is consistent with tnode depth */ + lastChunk = obj->variant.fileVariant.fileSize / dev->nDataBytesPerChunk + 1; + x = lastChunk >> YAFFS_TNODES_LEVEL0_BITS; + requiredTallness = 0; + while (x > 0) { + x >>= YAFFS_TNODES_INTERNAL_BITS; + requiredTallness++; + } + + actualTallness = obj->variant.fileVariant.topLevel; + + /* Check that the chunks in the tnode tree are all correct. + * We do this by scanning through the tnode tree and + * checking the tags for every chunk match. + */ + + if (yaffs_SkipNANDVerification(dev)) + return; + + for (i = 1; i <= lastChunk; i++) { + tn = yaffs_FindLevel0Tnode(dev, &obj->variant.fileVariant, i); + + if (tn) { + __u32 theChunk = yaffs_GetChunkGroupBase(dev, tn, i); + if (theChunk > 0) { + /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),objectId,i,theChunk)); */ + yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, &tags); + if (tags.objectId != objectId || tags.chunkId != i) { + T(~0, (TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), + objectId, i, theChunk, + tags.objectId, tags.chunkId)); + } + } + } + } +} + + +void yaffs_VerifyHardLink(yaffs_Object *obj) +{ + if (obj && yaffs_SkipVerification(obj->myDev)) + return; + + /* Verify sane equivalent object */ +} + +void yaffs_VerifySymlink(yaffs_Object *obj) +{ + if (obj && yaffs_SkipVerification(obj->myDev)) + return; + + /* Verify symlink string */ +} + +void yaffs_VerifySpecial(yaffs_Object *obj) +{ + if (obj && yaffs_SkipVerification(obj->myDev)) + return; +} + +void yaffs_VerifyObject(yaffs_Object *obj) +{ + yaffs_Device *dev; + + __u32 chunkMin; + __u32 chunkMax; + + __u32 chunkIdOk; + __u32 chunkInRange; + __u32 chunkShouldNotBeDeleted; + __u32 chunkValid; + + if (!obj) + return; + + if (obj->beingCreated) + return; + + dev = obj->myDev; + + if (yaffs_SkipVerification(dev)) + return; + + /* Check sane object header chunk */ + + chunkMin = dev->internalStartBlock * dev->param.nChunksPerBlock; + chunkMax = (dev->internalEndBlock+1) * dev->param.nChunksPerBlock - 1; + + chunkInRange = (((unsigned)(obj->hdrChunk)) >= chunkMin && ((unsigned)(obj->hdrChunk)) <= chunkMax); + chunkIdOk = chunkInRange || (obj->hdrChunk == 0); + chunkValid = chunkInRange && + yaffs_CheckChunkBit(dev, + obj->hdrChunk / dev->param.nChunksPerBlock, + obj->hdrChunk % dev->param.nChunksPerBlock); + chunkShouldNotBeDeleted = chunkInRange && !chunkValid; + + if (!obj->fake && + (!chunkIdOk || chunkShouldNotBeDeleted)) { + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d has chunkId %d %s %s"TENDSTR), + obj->objectId, obj->hdrChunk, + chunkIdOk ? "" : ",out of range", + chunkShouldNotBeDeleted ? ",marked as deleted" : "")); + } + + if (chunkValid && !yaffs_SkipNANDVerification(dev)) { + yaffs_ExtendedTags tags; + yaffs_ObjectHeader *oh; + __u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__); + + oh = (yaffs_ObjectHeader *)buffer; + + yaffs_ReadChunkWithTagsFromNAND(dev, obj->hdrChunk, buffer, + &tags); + + yaffs_VerifyObjectHeader(obj, oh, &tags, 1); + + yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); + } + + /* Verify it has a parent */ + if (obj && !obj->fake && + (!obj->parent || obj->parent->myDev != dev)) { + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d has parent pointer %p which does not look like an object"TENDSTR), + obj->objectId, obj->parent)); + } + + /* Verify parent is a directory */ + if (obj->parent && obj->parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d's parent is not a directory (type %d)"TENDSTR), + obj->objectId, obj->parent->variantType)); + } + + switch (obj->variantType) { + case YAFFS_OBJECT_TYPE_FILE: + yaffs_VerifyFile(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + yaffs_VerifySymlink(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + yaffs_VerifyDirectory(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + yaffs_VerifyHardLink(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + yaffs_VerifySpecial(obj); + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + default: + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d has illegaltype %d"TENDSTR), + obj->objectId, obj->variantType)); + break; + } +} + +void yaffs_VerifyObjects(yaffs_Device *dev) +{ + yaffs_Object *obj; + int i; + struct ylist_head *lh; + + if (yaffs_SkipVerification(dev)) + return; + + /* Iterate through the objects in each hash entry */ + + for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { + ylist_for_each(lh, &dev->objectBucket[i].list) { + if (lh) { + obj = ylist_entry(lh, yaffs_Object, hashLink); + yaffs_VerifyObject(obj); + } + } + } +} + + +void yaffs_VerifyObjectInDirectory(yaffs_Object *obj) +{ + struct ylist_head *lh; + yaffs_Object *listObj; + + int count = 0; + + if (!obj) { + T(YAFFS_TRACE_ALWAYS, (TSTR("No object to verify" TENDSTR))); + YBUG(); + return; + } + + if (yaffs_SkipVerification(obj->myDev)) + return; + + if (!obj->parent) { + T(YAFFS_TRACE_ALWAYS, (TSTR("Object does not have parent" TENDSTR))); + YBUG(); + return; + } + + if (obj->parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_ALWAYS, (TSTR("Parent is not directory" TENDSTR))); + YBUG(); + } + + /* Iterate through the objects in each hash entry */ + + ylist_for_each(lh, &obj->parent->variant.directoryVariant.children) { + if (lh) { + listObj = ylist_entry(lh, yaffs_Object, siblings); + yaffs_VerifyObject(listObj); + if (obj == listObj) + count++; + } + } + + if (count != 1) { + T(YAFFS_TRACE_ALWAYS, (TSTR("Object in directory %d times" TENDSTR), count)); + YBUG(); + } +} + +void yaffs_VerifyDirectory(yaffs_Object *directory) +{ + struct ylist_head *lh; + yaffs_Object *listObj; + + if (!directory) { + YBUG(); + return; + } + + if (yaffs_SkipFullVerification(directory->myDev)) + return; + + if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_ALWAYS, (TSTR("Directory has wrong type: %d" TENDSTR), directory->variantType)); + YBUG(); + } + + /* Iterate through the objects in each hash entry */ + + ylist_for_each(lh, &directory->variant.directoryVariant.children) { + if (lh) { + listObj = ylist_entry(lh, yaffs_Object, siblings); + if (listObj->parent != directory) { + T(YAFFS_TRACE_ALWAYS, (TSTR("Object in directory list has wrong parent %p" TENDSTR), listObj->parent)); + YBUG(); + } + yaffs_VerifyObjectInDirectory(listObj); + } + } +} + +static int yaffs_freeVerificationFailures; + +void yaffs_VerifyFreeChunks(yaffs_Device *dev) +{ + int counted; + int difference; + + if (yaffs_SkipVerification(dev)) + return; + + counted = yaffs_CountFreeChunks(dev); + + difference = dev->nFreeChunks - counted; + + if (difference) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("Freechunks verification failure %d %d %d" TENDSTR), + dev->nFreeChunks, counted, difference)); + yaffs_freeVerificationFailures++; + } +} + +int yaffs_VerifyFileSanity(yaffs_Object *in) +{ +#if 0 + 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) + return YAFFS_FAIL; + + objId = in->objectId; + fSize = in->variant.fileVariant.fileSize; + nChunks = + (fSize + in->myDev->nDataBytesPerChunk - 1) / in->myDev->nDataBytesPerChunk; + + for (chunk = 1; chunk <= nChunks; chunk++) { + tn = yaffs_FindLevel0Tnode(in->myDev, &in->variant.fileVariant, + chunk); + + if (tn) { + + theChunk = yaffs_GetChunkGroupBase(dev, tn, chunk); + + if (yaffs_CheckChunkBits + (dev, theChunk / dev->param.nChunksPerBlock, + theChunk % dev->param.nChunksPerBlock)) { + + yaffs_ReadChunkTagsFromNAND(in->myDev, theChunk, + tags, + &chunkDeleted); + if (yaffs_TagsMatch + (tags, in->objectId, chunk, chunkDeleted)) { + /* found it; */ + + } + } else { + + failed = 1; + } + + } else { + /* T(("No level 0 found for %d\n", chunk)); */ + } + } + + return failed ? YAFFS_FAIL : YAFFS_OK; +#else + in=in; + return YAFFS_OK; +#endif +} diff --git a/fs/yaffs2/yaffs_verify.h b/fs/yaffs2/yaffs_verify.h new file mode 100644 index 000000000000..3dbcefd16644 --- /dev/null +++ b/fs/yaffs2/yaffs_verify.h @@ -0,0 +1,41 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_VERIFY_H__ +#define __YAFFS_VERIFY_H__ + +#include "yaffs_guts.h" + +void yaffs_VerifyBlock(yaffs_Device *dev, yaffs_BlockInfo *bi, int n); +void yaffs_VerifyCollectedBlock(yaffs_Device *dev, yaffs_BlockInfo *bi, int n); +void yaffs_VerifyBlocks(yaffs_Device *dev); + +void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh, yaffs_ExtendedTags *tags, int parentCheck); +void yaffs_VerifyFile(yaffs_Object *obj); +void yaffs_VerifyHardLink(yaffs_Object *obj); +void yaffs_VerifySymlink(yaffs_Object *obj); +void yaffs_VerifySpecial(yaffs_Object *obj); +void yaffs_VerifyObject(yaffs_Object *obj); +void yaffs_VerifyObjects(yaffs_Device *dev); +void yaffs_VerifyObjectInDirectory(yaffs_Object *obj); +void yaffs_VerifyDirectory(yaffs_Object *directory); +void yaffs_VerifyFreeChunks(yaffs_Device *dev); + +int yaffs_VerifyFileSanity(yaffs_Object *obj); + +int yaffs_SkipVerification(yaffs_Device *dev); + +#endif + diff --git a/fs/yaffs2/yaffs_fs.c b/fs/yaffs2/yaffs_vfs_glue.c index 11eca48d064f..e6dc6787f6a9 100644 --- a/fs/yaffs2/yaffs_fs.c +++ b/fs/yaffs2/yaffs_vfs_glue.c @@ -31,16 +31,36 @@ * >> inode->u.generic_ip points to the associated yaffs_Object. */ +/* + * There are two variants of the VFS glue code. This variant should compile + * for any version of Linux. + */ #include <linux/version.h> #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) #define YAFFS_COMPILE_BACKGROUND +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6, 23)) +#define YAFFS_COMPILE_FREEZER +#endif #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)) #define YAFFS_COMPILE_EXPORTFS #endif +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)) +#define YAFFS_USE_SETATTR_COPY +#define YAFFS_USE_TRUNCATE_SETSIZE +#endif +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)) +#define YAFFS_HAS_EVICT_INODE +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) +#define YAFFS_NEW_FOLLOW_LINK 1 +#else +#define YAFFS_NEW_FOLLOW_LINK 0 +#endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) #include <linux/config.h> @@ -59,6 +79,10 @@ #include <linux/string.h> #include <linux/ctype.h> +#if (YAFFS_NEW_FOLLOW_LINK == 1) +#include <linux/namei.h> +#endif + #ifdef YAFFS_COMPILE_EXPORTFS #include <linux/exportfs.h> #endif @@ -66,6 +90,8 @@ #ifdef YAFFS_COMPILE_BACKGROUND #include <linux/kthread.h> #include <linux/delay.h> +#endif +#ifdef YAFFS_COMPILE_FREEZER #include <linux/freezer.h> #endif @@ -100,6 +126,12 @@ #define YPROC_ROOT NULL #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) +#define Y_INIT_TIMER(a) init_timer(a) +#else +#define Y_INIT_TIMER(a) init_timer_on_stack(a) +#endif + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) #define WRITE_SIZE_STR "writesize" #define WRITE_SIZE(mtd) ((mtd)->writesize) @@ -142,6 +174,7 @@ unsigned int yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS | YAFFS_TRACE_ALWAYS; unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; unsigned int yaffs_auto_checkpoint = 1; unsigned int yaffs_gc_control = 1; +unsigned int yaffs_bg_enable = 1; /* Module Parameters */ #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) @@ -149,6 +182,7 @@ module_param(yaffs_traceMask, uint, 0644); module_param(yaffs_wr_attempts, uint, 0644); module_param(yaffs_auto_checkpoint, uint, 0644); module_param(yaffs_gc_control, uint, 0644); +module_param(yaffs_bg_enable, uint, 0644); #else MODULE_PARM(yaffs_traceMask, "i"); MODULE_PARM(yaffs_wr_attempts, "i"); @@ -188,7 +222,7 @@ static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino); #define update_dir_time(dir) do {\ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ } while(0) - + static void yaffs_put_super(struct super_block *sb); static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, @@ -258,8 +292,12 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf); static void yaffs_put_inode(struct inode *inode); #endif +#ifdef YAFFS_HAS_EVICT_INODE +static void yaffs_evict_inode(struct inode *); +#else static void yaffs_delete_inode(struct inode *); static void yaffs_clear_inode(struct inode *); +#endif static int yaffs_readpage(struct file *file, struct page *page); #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) @@ -268,6 +306,15 @@ static int yaffs_writepage(struct page *page, struct writeback_control *wbc); static int yaffs_writepage(struct page *page); #endif +#ifdef CONFIG_YAFFS_XATTR +int yaffs_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags); +ssize_t yaffs_getxattr(struct dentry *dentry, const char *name, void *buff, + size_t size); +int yaffs_removexattr(struct dentry *dentry, const char *name); +ssize_t yaffs_listxattr(struct dentry *dentry, char *buff, size_t size); +#endif + #if (YAFFS_USE_WRITE_BEGIN_END != 0) static int yaffs_write_begin(struct file *filp, struct address_space *mapping, @@ -286,13 +333,20 @@ static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, static int yaffs_readlink(struct dentry *dentry, char __user *buffer, int buflen); -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) +#if (YAFFS_NEW_FOLLOW_LINK == 1) +void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias); static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd); #else static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd); #endif + +static void yaffs_MarkSuperBlockDirty(yaffs_Device *dev); + static loff_t yaffs_dir_llseek(struct file *file, loff_t offset, int origin); +static int yaffs_vfs_setattr(struct inode *, struct iattr *); + + static struct address_space_operations yaffs_file_address_operations = { .readpage = yaffs_readpage, .writepage = yaffs_writepage, @@ -305,6 +359,7 @@ static struct address_space_operations yaffs_file_address_operations = { #endif }; + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) static const struct file_operations yaffs_file_operations = { .read = do_sync_read, @@ -359,12 +414,27 @@ static void zero_user_segment(struct page *page, unsigned start, unsigned end) static const struct inode_operations yaffs_file_inode_operations = { .setattr = yaffs_setattr, +#ifdef CONFIG_YAFFS_XATTR + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +#endif }; static const struct inode_operations yaffs_symlink_inode_operations = { .readlink = yaffs_readlink, .follow_link = yaffs_follow_link, +#if (YAFFS_NEW_FOLLOW_LINK == 1) + .put_link = yaffs_put_link, +#endif .setattr = yaffs_setattr, +#ifdef CONFIG_YAFFS_XATTR + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +#endif }; static const struct inode_operations yaffs_dir_inode_operations = { @@ -378,6 +448,12 @@ static const struct inode_operations yaffs_dir_inode_operations = { .mknod = yaffs_mknod, .rename = yaffs_rename, .setattr = yaffs_setattr, +#ifdef CONFIG_YAFFS_XATTR + .setxattr = yaffs_setxattr, + .getxattr = yaffs_getxattr, + .listxattr = yaffs_listxattr, + .removexattr = yaffs_removexattr, +#endif }; static const struct file_operations yaffs_dir_operations = { @@ -397,28 +473,56 @@ static const struct super_operations yaffs_super_ops = { .put_inode = yaffs_put_inode, #endif .put_super = yaffs_put_super, +#ifdef YAFFS_HAS_EVICT_INODE + .evict_inode = yaffs_evict_inode, +#else .delete_inode = yaffs_delete_inode, .clear_inode = yaffs_clear_inode, +#endif .sync_fs = yaffs_sync_fs, .write_super = yaffs_write_super, }; + +static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) +{ +#ifdef YAFFS_USE_SETATTR_COPY + setattr_copy(inode,attr); + return 0; +#else + return inode_setattr(inode, attr); +#endif + +} + +static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) +{ +#ifdef YAFFS_USE_TRUNCATE_SETSIZE + truncate_setsize(inode,newsize); + return 0; +#else + truncate_inode_pages(&inode->i_data,newsize); + return 0; +#endif + +} + static unsigned yaffs_gc_control_callback(yaffs_Device *dev) { return yaffs_gc_control; } - + static void yaffs_GrossLock(yaffs_Device *dev) { T(YAFFS_TRACE_LOCK, (TSTR("yaffs locking %p\n"), current)); - down(&(yaffs_DeviceToContext(dev)->grossLock)); + down(&(yaffs_DeviceToLC(dev)->grossLock)); T(YAFFS_TRACE_LOCK, (TSTR("yaffs locked %p\n"), current)); } static void yaffs_GrossUnlock(yaffs_Device *dev) { T(YAFFS_TRACE_LOCK, (TSTR("yaffs unlocking %p\n"), current)); - up(&(yaffs_DeviceToContext(dev)->grossLock)); + up(&(yaffs_DeviceToLC(dev)->grossLock)); } #ifdef YAFFS_COMPILE_EXPORTFS @@ -533,7 +637,7 @@ static struct yaffs_SearchContext * yaffs_NewSearch(yaffs_Object *dir) dir->variant.directoryVariant.children.next, yaffs_Object,siblings); YINIT_LIST_HEAD(&sc->others); - ylist_add(&sc->others,&(yaffs_DeviceToContext(dev)->searchContexts)); + ylist_add(&sc->others,&(yaffs_DeviceToLC(dev)->searchContexts)); } return sc; } @@ -582,7 +686,7 @@ static void yaffs_RemoveObjectCallback(yaffs_Object *obj) struct ylist_head *i; struct yaffs_SearchContext *sc; - struct ylist_head *search_contexts = &(yaffs_DeviceToContext(obj->myDev)->searchContexts); + struct ylist_head *search_contexts = &(yaffs_DeviceToLC(obj->myDev)->searchContexts); /* Iterate through the directory search contexts. @@ -624,7 +728,7 @@ static int yaffs_readlink(struct dentry *dentry, char __user *buffer, return ret; } -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) +#if (YAFFS_NEW_FOLLOW_LINK == 1) static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) #else static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) @@ -637,7 +741,6 @@ static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) yaffs_GrossLock(dev); alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry)); - yaffs_GrossUnlock(dev); if (!alias) { @@ -645,16 +748,25 @@ static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) goto out; } - ret = vfs_follow_link(nd, alias); - kfree(alias); +#if (YAFFS_NEW_FOLLOW_LINK == 1) + nd_set_link(nd, alias); + ret = (int)alias; out: -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) return ERR_PTR(ret); #else + ret = vfs_follow_link(nd, alias); + kfree(alias); +out: return ret; #endif } +#if (YAFFS_NEW_FOLLOW_LINK == 1) +void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) { + kfree(alias); +} +#endif + struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, yaffs_Object *obj); @@ -674,7 +786,7 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev; - if(current != yaffs_DeviceToContext(dev)->readdirProcess) + if(current != yaffs_DeviceToLC(dev)->readdirProcess) yaffs_GrossLock(dev); T(YAFFS_TRACE_OS, @@ -687,7 +799,7 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) obj = yaffs_GetEquivalentObject(obj); /* in case it was a hardlink */ /* Can't hold gross lock when calling yaffs_get_inode() */ - if(current != yaffs_DeviceToContext(dev)->readdirProcess) + if(current != yaffs_DeviceToLC(dev)->readdirProcess) yaffs_GrossUnlock(dev); if (obj) { @@ -737,36 +849,85 @@ static void yaffs_put_inode(struct inode *inode) } #endif -/* clear is called to tell the fs to release any per-inode data it holds */ -static void yaffs_clear_inode(struct inode *inode) + +static void yaffs_UnstitchObject(struct inode *inode, yaffs_Object *obj) +{ + /* Clear the association between the inode and + * the yaffs_Object. + */ + obj->myInode = NULL; + yaffs_InodeToObjectLV(inode) = NULL; + + /* If the object freeing was deferred, then the real + * free happens now. + * This should fix the inode inconsistency problem. + */ + yaffs_HandleDeferedFree(obj); +} + +#ifdef YAFFS_HAS_EVICT_INODE +/* yaffs_evict_inode combines into one operation what was previously done in + * yaffs_clear_inode() and yaffs_delete_inode() + * + */ +static void yaffs_evict_inode( struct inode *inode) { yaffs_Object *obj; yaffs_Device *dev; + int deleteme = 0; obj = yaffs_InodeToObject(inode); T(YAFFS_TRACE_OS, - (TSTR("yaffs_clear_inode: ino %d, count %d %s\n"), (int)inode->i_ino, + (TSTR("yaffs_evict_inode: ino %d, count %d %s\n"), (int)inode->i_ino, atomic_read(&inode->i_count), obj ? "object exists" : "null object")); + if (!inode->i_nlink && !is_bad_inode(inode)) + deleteme = 1; + truncate_inode_pages(&inode->i_data,0); + end_writeback(inode); + + if(deleteme && obj){ + dev = obj->myDev; + yaffs_GrossLock(dev); + yaffs_DeleteObject(obj); + yaffs_GrossUnlock(dev); + } if (obj) { dev = obj->myDev; yaffs_GrossLock(dev); + yaffs_UnstitchObject(inode,obj); + yaffs_GrossUnlock(dev); + } - /* Clear the association between the inode and - * the yaffs_Object. - */ - obj->myInode = NULL; - yaffs_InodeToObjectLV(inode) = NULL; - /* If the object freeing was deferred, then the real - * free happens now. - * This should fix the inode inconsistency problem. - */ +} +#else - yaffs_HandleDeferedFree(obj); +/* clear is called to tell the fs to release any per-inode data it holds. + * The object might still exist on disk and is just being thrown out of the cache + * or else the object has actually been deleted and we're being called via + * the chain + * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() + */ +static void yaffs_clear_inode(struct inode *inode) +{ + yaffs_Object *obj; + yaffs_Device *dev; + + obj = yaffs_InodeToObject(inode); + + T(YAFFS_TRACE_OS, + (TSTR("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) { + dev = obj->myDev; + yaffs_GrossLock(dev); + yaffs_UnstitchObject(inode,obj); yaffs_GrossUnlock(dev); } @@ -798,6 +959,8 @@ static void yaffs_delete_inode(struct inode *inode) #endif clear_inode(inode); } +#endif + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) static int yaffs_file_flush(struct file *file, fl_owner_t id) @@ -902,6 +1065,7 @@ static int yaffs_writepage(struct page *page, struct writeback_control *wbc) static int yaffs_writepage(struct page *page) #endif { + yaffs_Device *dev; struct address_space *mapping = page->mapping; struct inode *inode; unsigned long end_index; @@ -949,7 +1113,8 @@ static int yaffs_writepage(struct page *page) buffer = kmap(page); obj = yaffs_InodeToObject(inode); - yaffs_GrossLock(obj->myDev); + dev = obj->myDev; + yaffs_GrossLock(dev); T(YAFFS_TRACE_OS, (TSTR("yaffs_writepage at %08x, size %08x\n"), @@ -961,11 +1126,13 @@ static int yaffs_writepage(struct page *page) nWritten = yaffs_WriteDataToFile(obj, buffer, page->index << PAGE_CACHE_SHIFT, nBytes, 0); + yaffs_MarkSuperBlockDirty(dev); + T(YAFFS_TRACE_OS, (TSTR("writepag1: obj = %05x, ino = %05x\n"), (int)obj->variant.fileVariant.fileSize, (int)inode->i_size)); - yaffs_GrossUnlock(obj->myDev); + yaffs_GrossUnlock(dev); kunmap(page); set_page_writeback(page); @@ -1301,6 +1468,8 @@ static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, nWritten = yaffs_WriteDataToFile(obj, buf, ipos, n, 0); + yaffs_MarkSuperBlockDirty(dev); + T(YAFFS_TRACE_OS, (TSTR("yaffs_file_write: %d(%x) bytes written\n"), (unsigned )n,(unsigned)n)); @@ -1408,14 +1577,14 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) yaffs_GrossLock(dev); - yaffs_DeviceToContext(dev)->readdirProcess = current; + yaffs_DeviceToLC(dev)->readdirProcess = current; offset = f->f_pos; sc = yaffs_NewSearch(obj); if(!sc){ retVal = -ENOMEM; - goto unlock_out; + goto out; } T(YAFFS_TRACE_OS, (TSTR("yaffs_readdir: starting at %d\n"), (int)offset)); @@ -1425,8 +1594,10 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) (TSTR("yaffs_readdir: entry . ino %d \n"), (int)inode->i_ino)); yaffs_GrossUnlock(dev); - if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) + if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0){ + yaffs_GrossLock(dev); goto out; + } yaffs_GrossLock(dev); offset++; f->f_pos++; @@ -1437,8 +1608,10 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) (int)f->f_dentry->d_parent->d_inode->i_ino)); yaffs_GrossUnlock(dev); if (filldir(dirent, "..", 2, offset, - f->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) + f->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0){ + yaffs_GrossLock(dev); goto out; + } yaffs_GrossLock(dev); offset++; f->f_pos++; @@ -1474,8 +1647,10 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) strlen(name), offset, this_inode, - this_type) < 0) + this_type) < 0){ + yaffs_GrossLock(dev); goto out; + } yaffs_GrossLock(dev); @@ -1485,12 +1660,10 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) yaffs_SearchAdvance(sc); } -unlock_out: - yaffs_DeviceToContext(dev)->readdirProcess = NULL; - - yaffs_GrossUnlock(dev); out: - yaffs_EndSearch(sc); + yaffs_EndSearch(sc); + yaffs_DeviceToLC(dev)->readdirProcess = NULL; + yaffs_GrossUnlock(dev); return retVal; } @@ -1790,7 +1963,7 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, new_dentry->d_inode->i_nlink--; mark_inode_dirty(new_dentry->d_inode); } - + update_dir_time(old_dir); if(old_dir != new_dir) update_dir_time(new_dir); @@ -1810,7 +1983,7 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) (TSTR("yaffs_setattr of object %d\n"), yaffs_InodeToObject(inode)->objectId)); - /* Fail if a requested resize >= 2GB */ + /* Fail if a requested resize >= 2GB */ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) error = -EINVAL; @@ -1820,10 +1993,12 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) if (error == 0) { int result; if (!error){ - error = inode_setattr(inode, attr); + error = yaffs_vfs_setattr(inode, attr); T(YAFFS_TRACE_OS,(TSTR("inode_setattr called\n"))); - if (attr->ia_valid & ATTR_SIZE) - truncate_inode_pages(&inode->i_data,attr->ia_size); + if (attr->ia_valid & ATTR_SIZE){ + yaffs_vfs_setsize(inode,attr->ia_size); + inode->i_blocks = (inode->i_size + 511) >> 9; + } } dev = yaffs_InodeToObject(inode)->myDev; if (attr->ia_valid & ATTR_SIZE){ @@ -1847,6 +2022,122 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) return error; } +#ifdef CONFIG_YAFFS_XATTR +int yaffs_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + yaffs_Device *dev; + yaffs_Object *obj = yaffs_InodeToObject(inode); + + T(YAFFS_TRACE_OS, + (TSTR("yaffs_setxattr of object %d\n"), + obj->objectId)); + + + if (error == 0) { + int result; + dev = obj->myDev; + yaffs_GrossLock(dev); + result = yaffs_SetXAttribute(obj, name, value, size, flags); + if(result == YAFFS_OK) + error = 0; + else if(result < 0) + error = result; + yaffs_GrossUnlock(dev); + + } + T(YAFFS_TRACE_OS, + (TSTR("yaffs_setxattr done returning %d\n"),error)); + + return error; +} + + +ssize_t yaffs_getxattr(struct dentry *dentry, const char *name, void *buff, + size_t size) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + yaffs_Device *dev; + yaffs_Object *obj = yaffs_InodeToObject(inode); + + T(YAFFS_TRACE_OS, + (TSTR("yaffs_getxattr \"%s\" from object %d\n"), + name, obj->objectId)); + + if (error == 0) { + dev = obj->myDev; + yaffs_GrossLock(dev); + error = yaffs_GetXAttribute(obj, name, buff, size); + yaffs_GrossUnlock(dev); + + } + T(YAFFS_TRACE_OS, + (TSTR("yaffs_getxattr done returning %d\n"),error)); + + return error; +} + +int yaffs_removexattr(struct dentry *dentry, const char *name) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + yaffs_Device *dev; + yaffs_Object *obj = yaffs_InodeToObject(inode); + + T(YAFFS_TRACE_OS, + (TSTR("yaffs_removexattr of object %d\n"), + obj->objectId)); + + + if (error == 0) { + int result; + dev = obj->myDev; + yaffs_GrossLock(dev); + result = yaffs_RemoveXAttribute(obj, name); + if(result == YAFFS_OK) + error = 0; + else if(result < 0) + error = result; + yaffs_GrossUnlock(dev); + + } + T(YAFFS_TRACE_OS, + (TSTR("yaffs_removexattr done returning %d\n"),error)); + + return error; +} + +ssize_t yaffs_listxattr(struct dentry *dentry, char *buff, size_t size) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + yaffs_Device *dev; + yaffs_Object *obj = yaffs_InodeToObject(inode); + + T(YAFFS_TRACE_OS, + (TSTR("yaffs_listxattr of object %d\n"), + obj->objectId)); + + + if (error == 0) { + dev = obj->myDev; + yaffs_GrossLock(dev); + error = yaffs_ListXAttributes(obj, buff, size); + yaffs_GrossUnlock(dev); + + } + T(YAFFS_TRACE_OS, + (TSTR("yaffs_listxattr done returning %d\n"),error)); + + return error; +} + +#endif + + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) { @@ -1923,7 +2214,7 @@ static void yaffs_FlushInodes(struct super_block *sb) { struct inode *iptr; yaffs_Object *obj; - + list_for_each_entry(iptr,&sb->s_inodes, i_sb_list){ obj = yaffs_InodeToObject(iptr); if(obj){ @@ -1937,10 +2228,10 @@ static void yaffs_FlushInodes(struct super_block *sb) static void yaffs_FlushSuperBlock(struct super_block *sb, int do_checkpoint) { - yaffs_Device *dev = yaffs_SuperToDevice(sb); + yaffs_Device *dev = yaffs_SuperToDevice(sb); if(!dev) return; - + yaffs_FlushInodes(sb); yaffs_UpdateDirtyDirectories(dev); yaffs_FlushEntireDeviceCache(dev); @@ -1952,7 +2243,7 @@ static void yaffs_FlushSuperBlock(struct super_block *sb, int do_checkpoint) static unsigned yaffs_bg_gc_urgency(yaffs_Device *dev) { unsigned erasedChunks = dev->nErasedBlocks * dev->param.nChunksPerBlock; - struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev); + struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev); unsigned scatteredFree = 0; /* Free chunks not in an erased block */ if(erasedChunks < dev->nFreeChunks) @@ -2008,7 +2299,7 @@ static int yaffs_do_sync_fs(struct super_block *sb, * yaffs_BackgroundStart() launches the background thread. * yaffs_BackgroundStop() cleans up the background thread. * - * NB: + * NB: * The thread should only run after the yaffs is initialised * The thread should be stopped before yaffs is unmounted. * The thread should not do any writing while the fs is in read only. @@ -2024,7 +2315,7 @@ void yaffs_background_waker(unsigned long data) static int yaffs_BackgroundThread(void *data) { yaffs_Device *dev = (yaffs_Device *)data; - struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev); + struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev); unsigned long now = jiffies; unsigned long next_dir_update = now; unsigned long next_gc = now; @@ -2038,8 +2329,9 @@ static int yaffs_BackgroundThread(void *data) (TSTR("yaffs_background starting for dev %p\n"), (void *)dev)); +#ifdef YAFFS_COMPILE_FREEZER set_freezable(); - +#endif while(context->bgRunning){ T(YAFFS_TRACE_BACKGROUND, (TSTR("yaffs_background\n"))); @@ -2047,19 +2339,20 @@ static int yaffs_BackgroundThread(void *data) if(kthread_should_stop()) break; +#ifdef YAFFS_COMPILE_FREEZER if(try_to_freeze()) continue; - +#endif yaffs_GrossLock(dev); now = jiffies; - if(time_after(now, next_dir_update)){ + if(time_after(now, next_dir_update) && yaffs_bg_enable){ yaffs_UpdateDirtyDirectories(dev); next_dir_update = now + HZ; } - if(time_after(now,next_gc)){ + if(time_after(now,next_gc) && yaffs_bg_enable){ if(!dev->isCheckpointed){ urgency = yaffs_bg_gc_urgency(dev); gcResult = yaffs_BackgroundGarbageCollect(dev, urgency); @@ -2083,7 +2376,7 @@ static int yaffs_BackgroundThread(void *data) if(time_before(expires,now)) expires = now + HZ; - init_timer_on_stack(&timer); + Y_INIT_TIMER(&timer); timer.expires = expires+1; timer.data = (unsigned long) current; timer.function = yaffs_background_waker; @@ -2103,12 +2396,15 @@ static int yaffs_BackgroundThread(void *data) static int yaffs_BackgroundStart(yaffs_Device *dev) { int retval = 0; - struct yaffs_LinuxContext *context = yaffs_DeviceToContext(dev); + struct yaffs_LinuxContext *context = yaffs_DeviceToLC(dev); + + if(dev->readOnly) + return -1; context->bgRunning = 1; context->bgThread = kthread_run(yaffs_BackgroundThread, - (void *)dev,"yaffs-bg"); + (void *)dev,"yaffs-bg-%d",context->mount_id); if(IS_ERR(context->bgThread)){ retval = PTR_ERR(context->bgThread); @@ -2120,7 +2416,7 @@ static int yaffs_BackgroundStart(yaffs_Device *dev) static void yaffs_BackgroundStop(yaffs_Device *dev) { - struct yaffs_LinuxContext *ctxt = yaffs_DeviceToContext(dev); + struct yaffs_LinuxContext *ctxt = yaffs_DeviceToLC(dev); ctxt->bgRunning = 0; @@ -2232,14 +2528,14 @@ static void yaffs_read_inode(struct inode *inode) T(YAFFS_TRACE_OS, (TSTR("yaffs_read_inode for %d\n"), (int)inode->i_ino)); - if(current != yaffs_DeviceToContext(dev)->readdirProcess) + if(current != yaffs_DeviceToLC(dev)->readdirProcess) yaffs_GrossLock(dev); obj = yaffs_FindObjectByNumber(dev, inode->i_ino); yaffs_FillInodeFromObject(inode, obj); - if(current != yaffs_DeviceToContext(dev)->readdirProcess) + if(current != yaffs_DeviceToLC(dev)->readdirProcess) yaffs_GrossUnlock(dev); } @@ -2248,34 +2544,6 @@ static void yaffs_read_inode(struct inode *inode) static YLIST_HEAD(yaffs_context_list); struct semaphore yaffs_context_lock; -#if 0 /* not used */ -static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) -{ - yaffs_Device *dev = yaffs_SuperToDevice(sb); - - if (*flags & MS_RDONLY) { - struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice; - - T(YAFFS_TRACE_OS, - (TSTR("yaffs_remount_fs: %s: RO\n"), dev->name)); - - yaffs_GrossLock(dev); - - yaffs_FlushSuperBlock(sb,1); - - if (mtd->sync) - mtd->sync(mtd); - - yaffs_GrossUnlock(dev); - } else { - T(YAFFS_TRACE_OS, - (TSTR("yaffs_remount_fs: %s: RW\n"), dev->name)); - } - - return 0; -} -#endif - static void yaffs_put_super(struct super_block *sb) { yaffs_Device *dev = yaffs_SuperToDevice(sb); @@ -2292,8 +2560,8 @@ static void yaffs_put_super(struct super_block *sb) yaffs_FlushSuperBlock(sb,1); - if (yaffs_DeviceToContext(dev)->putSuperFunc) - yaffs_DeviceToContext(dev)->putSuperFunc(sb); + if (yaffs_DeviceToLC(dev)->putSuperFunc) + yaffs_DeviceToLC(dev)->putSuperFunc(sb); yaffs_Deinitialise(dev); @@ -2301,12 +2569,12 @@ static void yaffs_put_super(struct super_block *sb) yaffs_GrossUnlock(dev); down(&yaffs_context_lock); - ylist_del_init(&(yaffs_DeviceToContext(dev)->contextList)); + ylist_del_init(&(yaffs_DeviceToLC(dev)->contextList)); up(&yaffs_context_lock); - if (yaffs_DeviceToContext(dev)->spareBuffer) { - YFREE(yaffs_DeviceToContext(dev)->spareBuffer); - yaffs_DeviceToContext(dev)->spareBuffer = NULL; + if (yaffs_DeviceToLC(dev)->spareBuffer) { + YFREE(yaffs_DeviceToLC(dev)->spareBuffer); + yaffs_DeviceToLC(dev)->spareBuffer = NULL; } kfree(dev); @@ -2315,7 +2583,7 @@ static void yaffs_put_super(struct super_block *sb) static void yaffs_MTDPutSuper(struct super_block *sb) { - struct mtd_info *mtd = yaffs_DeviceToContext(yaffs_SuperToDevice(sb))->mtd; + struct mtd_info *mtd = yaffs_DeviceToMtd(yaffs_SuperToDevice(sb)); if (mtd->sync) mtd->sync(mtd); @@ -2326,7 +2594,7 @@ static void yaffs_MTDPutSuper(struct super_block *sb) static void yaffs_MarkSuperBlockDirty(yaffs_Device *dev) { - struct super_block *sb = yaffs_DeviceToContext(dev)->superBlock; + struct super_block *sb = yaffs_DeviceToLC(dev)->superBlock; T(YAFFS_TRACE_OS, (TSTR("yaffs_MarkSuperBlockDirty() sb = %p\n"), sb)); if (sb) @@ -2424,12 +2692,22 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, struct yaffs_LinuxContext *context = NULL; yaffs_DeviceParam *param; + int readOnly = 0; + yaffs_options options; + unsigned mount_id; + int found; + struct yaffs_LinuxContext *context_iterator; + struct ylist_head *l; + sb->s_magic = YAFFS_MAGIC; sb->s_op = &yaffs_super_ops; sb->s_flags |= MS_NOATIME; + readOnly =((sb->s_flags & MS_RDONLY) != 0); + + #ifdef YAFFS_COMPILE_EXPORTFS sb->s_export_op = &yaffs_export_ops; #endif @@ -2441,9 +2719,10 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, else if (!yaffs_devname(sb, devname_buf)) printk(KERN_INFO "yaffs: devname is NULL\n"); else - printk(KERN_INFO "yaffs: dev is %d name is \"%s\"\n", + printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", sb->s_dev, - yaffs_devname(sb, devname_buf)); + yaffs_devname(sb, devname_buf), + readOnly ? "ro" : "rw"); if (!data_str) data_str = ""; @@ -2583,9 +2862,15 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, * Set the yaffs_Device up for mtd */ + if (!readOnly && !(mtd->flags & MTD_WRITEABLE)){ + readOnly = 1; + printk(KERN_INFO "yaffs: mtd is read only, setting superblock read only"); + sb->s_flags |= MS_RDONLY; + } + dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL); context = kmalloc(sizeof(struct yaffs_LinuxContext),GFP_KERNEL); - + if(!dev || !context ){ if(dev) kfree(dev); @@ -2606,20 +2891,20 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, param = &(dev->param); memset(context,0,sizeof(struct yaffs_LinuxContext)); - dev->context = context; + dev->osContext = context; YINIT_LIST_HEAD(&(context->contextList)); context->dev = dev; context->superBlock = sb; - + dev->readOnly = readOnly; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) sb->s_fs_info = dev; #else sb->u.generic_sbp = dev; #endif - - yaffs_DeviceToContext(dev)->mtd = mtd; + + dev->driverContext = mtd; param->name = mtd->name; /* Set up the memory size parameters.... */ @@ -2637,6 +2922,9 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, #ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD param->disableLazyLoad = 1; #endif +#ifdef CONFIG_YAFFS_XATTR + param->enableXattr = 1; +#endif if(options.lazy_loading_overridden) param->disableLazyLoad = !options.lazy_loading_enabled; @@ -2662,6 +2950,10 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, param->refreshPeriod = 500; #endif +#ifdef CONFIG_YAFFS__ALWAYS_CHECK_CHUNK_ERASED + param->alwaysCheckErased = 1; +#endif + if(options.empty_lost_and_found_overridden) param->emptyLostAndFound = options.empty_lost_and_found; @@ -2673,7 +2965,7 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, nandmtd2_ReadChunkWithTagsFromNAND; param->markNANDBlockBad = nandmtd2_MarkNANDBlockBad; param->queryNANDBlock = nandmtd2_QueryNANDBlock; - yaffs_DeviceToContext(dev)->spareBuffer = YMALLOC(mtd->oobsize); + yaffs_DeviceToLC(dev)->spareBuffer = YMALLOC(mtd->oobsize); param->isYaffs2 = 1; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) param->totalBytesPerChunk = mtd->writesize; @@ -2705,13 +2997,13 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, param->eraseBlockInNAND = nandmtd_EraseBlockInNAND; param->initialiseNAND = nandmtd_InitialiseNAND; - yaffs_DeviceToContext(dev)->putSuperFunc = yaffs_MTDPutSuper; + yaffs_DeviceToLC(dev)->putSuperFunc = yaffs_MTDPutSuper; param->markSuperBlockDirty = yaffs_MarkSuperBlockDirty; param->gcControl = yaffs_gc_control_callback; - yaffs_DeviceToContext(dev)->superBlock= sb; - + yaffs_DeviceToLC(dev)->superBlock= sb; + #ifndef CONFIG_YAFFS_DOES_ECC param->useNANDECC = 1; @@ -2724,16 +3016,27 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, param->skipCheckpointRead = options.skip_checkpoint_read; param->skipCheckpointWrite = options.skip_checkpoint_write; - /* we assume this is protected by lock_kernel() in mount/umount */ down(&yaffs_context_lock); - ylist_add_tail(&(yaffs_DeviceToContext(dev)->contextList), &yaffs_context_list); + /* Get a mount id */ + found = 0; + for(mount_id=0; ! found; mount_id++){ + found = 1; + ylist_for_each(l,&yaffs_context_list){ + context_iterator = ylist_entry(l,struct yaffs_LinuxContext,contextList); + if(context_iterator->mount_id == mount_id) + found = 0; + } + } + context->mount_id = mount_id; + + ylist_add_tail(&(yaffs_DeviceToLC(dev)->contextList), &yaffs_context_list); up(&yaffs_context_lock); /* Directory search handling...*/ - YINIT_LIST_HEAD(&(yaffs_DeviceToContext(dev)->searchContexts)); + YINIT_LIST_HEAD(&(yaffs_DeviceToLC(dev)->searchContexts)); param->removeObjectCallback = yaffs_RemoveObjectCallback; - init_MUTEX(&(yaffs_DeviceToContext(dev)->grossLock)); + init_MUTEX(&(yaffs_DeviceToLC(dev)->grossLock)); yaffs_GrossLock(dev); @@ -2742,10 +3045,10 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, T(YAFFS_TRACE_OS, (TSTR("yaffs_read_super: guts initialised %s\n"), (err == YAFFS_OK) ? "OK" : "FAILED")); - + if(err == YAFFS_OK) yaffs_BackgroundStart(dev); - + if(!context->bgThread) param->deferDirectoryUpdate = 0; @@ -2896,6 +3199,7 @@ static char *yaffs_dump_dev_part0(char *buf, yaffs_Device * dev) buf += sprintf(buf, "refreshPeriod...... %d\n", dev->param.refreshPeriod); buf += sprintf(buf, "nShortOpCaches..... %d\n", dev->param.nShortOpCaches); buf += sprintf(buf, "nReservedBlocks.... %d\n", dev->param.nReservedBlocks); + buf += sprintf(buf, "alwaysCheckErased.. %d\n", dev->param.alwaysCheckErased); buf += sprintf(buf, "\n"); @@ -2911,10 +3215,8 @@ static char *yaffs_dump_dev_part1(char *buf, yaffs_Device * dev) buf += sprintf(buf, "nErasedBlocks...... %d\n", dev->nErasedBlocks); buf += sprintf(buf, "blocksInCheckpoint. %d\n", dev->blocksInCheckpoint); buf += sprintf(buf, "\n"); - buf += sprintf(buf, "nTnodesCreated..... %d\n", dev->nTnodesCreated); - buf += sprintf(buf, "nFreeTnodes........ %d\n", dev->nFreeTnodes); - buf += sprintf(buf, "nObjectsCreated.... %d\n", dev->nObjectsCreated); - buf += sprintf(buf, "nFreeObjects....... %d\n", dev->nFreeObjects); + buf += sprintf(buf, "nTnodes............ %d\n", dev->nTnodes); + buf += sprintf(buf, "nObjects........... %d\n", dev->nObjects); buf += sprintf(buf, "nFreeChunks........ %d\n", dev->nFreeChunks); buf += sprintf(buf, "\n"); buf += sprintf(buf, "nPageWrites........ %u\n", dev->nPageWrites); @@ -2924,6 +3226,7 @@ static char *yaffs_dump_dev_part1(char *buf, yaffs_Device * dev) buf += sprintf(buf, "allGCs............. %u\n", dev->allGCs); buf += sprintf(buf, "passiveGCs......... %u\n", dev->passiveGCs); buf += sprintf(buf, "oldestDirtyGCs..... %u\n", dev->oldestDirtyGCs); + buf += sprintf(buf, "nGCBlocks.......... %u\n", dev->nGCBlocks); buf += sprintf(buf, "backgroundGCs...... %u\n", dev->backgroundGCs); buf += sprintf(buf, "nRetriedWrites..... %u\n", dev->nRetriedWrites); buf += sprintf(buf, "nRetireBlocks...... %u\n", dev->nRetiredBlocks); @@ -2960,12 +3263,12 @@ static int yaffs_proc_read(char *page, /* Print header first */ if (step == 0) - buf += sprintf(buf, "YAFFS built:" __DATE__ " " __TIME__"\n"); + buf += sprintf(buf, "Multi-version YAFFS built:" __DATE__ " " __TIME__"\n"); else if (step == 1) buf += sprintf(buf,"\n"); else { step-=2; - + down(&yaffs_context_lock); /* Locate and print the Nth entry. Order N-squared but N is small. */ @@ -2982,7 +3285,7 @@ static int yaffs_proc_read(char *page, buf = yaffs_dump_dev_part0(buf, dev); } else buf = yaffs_dump_dev_part1(buf, dev); - + break; } up(&yaffs_context_lock); @@ -3007,18 +3310,13 @@ static int yaffs_stats_proc_read(char *page, yaffs_Device *dev = dc->dev; int erasedChunks; - int nObjects; - int nTnodes; erasedChunks = dev->nErasedBlocks * dev->param.nChunksPerBlock; - nObjects = dev->nObjectsCreated -dev->nFreeObjects; - nTnodes = dev->nTnodesCreated - dev->nFreeTnodes; - - - buf += sprintf(buf,"%d, %d, %d, %u, %u, %d, %d\n", + + buf += sprintf(buf,"%d, %d, %d, %u, %u, %u, %u\n", n, dev->nFreeChunks, erasedChunks, dev->backgroundGCs, dev->oldestDirtyGCs, - nObjects, nTnodes); + dev->nObjects, dev->nTnodes); } up(&yaffs_context_lock); diff --git a/fs/yaffs2/yaffs_yaffs1.c b/fs/yaffs2/yaffs_yaffs1.c new file mode 100644 index 000000000000..642fd5a4d54e --- /dev/null +++ b/fs/yaffs2/yaffs_yaffs1.c @@ -0,0 +1,466 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + */ + +#include "yaffs_yaffs1.h" +#include "yportenv.h" +#include "yaffs_trace.h" +#include "yaffs_bitmap.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_nand.h" + + +int yaffs1_Scan(yaffs_Device *dev) +{ + yaffs_ExtendedTags tags; + int blk; + int blockIterator; + int startIterator; + int endIterator; + int result; + + int chunk; + int c; + int deleted; + yaffs_BlockState state; + yaffs_Object *hardList = NULL; + yaffs_BlockInfo *bi; + __u32 sequenceNumber; + yaffs_ObjectHeader *oh; + yaffs_Object *in; + yaffs_Object *parent; + + int alloc_failed = 0; + + struct yaffs_ShadowFixerStruct *shadowFixerList = NULL; + + + __u8 *chunkData; + + + + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs1_Scan starts intstartblk %d intendblk %d..." TENDSTR), + dev->internalStartBlock, dev->internalEndBlock)); + + chunkData = yaffs_GetTempBuffer(dev, __LINE__); + + dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; + + /* Scan all the blocks to determine their state */ + bi = dev->blockInfo; + for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { + yaffs_ClearChunkBits(dev, blk); + bi->pagesInUse = 0; + bi->softDeletions = 0; + + yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); + + bi->blockState = state; + bi->sequenceNumber = sequenceNumber; + + if (bi->sequenceNumber == YAFFS_SEQUENCE_BAD_BLOCK) + bi->blockState = state = YAFFS_BLOCK_STATE_DEAD; + + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, + state, sequenceNumber)); + + if (state == YAFFS_BLOCK_STATE_DEAD) { + T(YAFFS_TRACE_BAD_BLOCKS, + (TSTR("block %d is bad" TENDSTR), blk)); + } else if (state == YAFFS_BLOCK_STATE_EMPTY) { + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block empty " TENDSTR))); + dev->nErasedBlocks++; + dev->nFreeChunks += dev->param.nChunksPerBlock; + } + bi++; + } + + startIterator = dev->internalStartBlock; + endIterator = dev->internalEndBlock; + + /* For each block.... */ + for (blockIterator = startIterator; !alloc_failed && blockIterator <= endIterator; + blockIterator++) { + + YYIELD(); + + YYIELD(); + + blk = blockIterator; + + bi = yaffs_GetBlockInfo(dev, blk); + state = bi->blockState; + + deleted = 0; + + /* For each chunk in each block that needs scanning....*/ + for (c = 0; !alloc_failed && c < dev->param.nChunksPerBlock && + state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++) { + /* Read the tags and decide what to do */ + chunk = blk * dev->param.nChunksPerBlock + c; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, + &tags); + + /* Let's have a good look at this chunk... */ + + if (tags.eccResult == YAFFS_ECC_RESULT_UNFIXED || tags.chunkDeleted) { + /* YAFFS1 only... + * A deleted chunk + */ + deleted++; + dev->nFreeChunks++; + /*T((" %d %d deleted\n",blk,c)); */ + } else if (!tags.chunkUsed) { + /* 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) { + /* We're looking at the first chunk in the block so the block is unused */ + state = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + } else { + /* this is the block being allocated from */ + T(YAFFS_TRACE_SCAN, + (TSTR + (" Allocating from %d %d" TENDSTR), + blk, c)); + state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->allocationBlock = blk; + dev->allocationPage = c; + dev->allocationBlockFinder = blk; + /* Set block finder here to encourage the allocator to go forth from here. */ + + } + + dev->nFreeChunks += (dev->param.nChunksPerBlock - c); + } else if (tags.chunkId > 0) { + /* chunkId > 0 so it is a data chunk... */ + unsigned int endpos; + + yaffs_SetChunkBit(dev, blk, 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). + */ + + if (!in) + alloc_failed = 1; + + if (in) { + if (!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk, 1)) + alloc_failed = 1; + } + + endpos = + (tags.chunkId - 1) * dev->nDataBytesPerChunk + + tags.byteCount; + if (in && + in->variantType == YAFFS_OBJECT_TYPE_FILE + && in->variant.fileVariant.scannedFileSize < + endpos) { + in->variant.fileVariant. + scannedFileSize = endpos; + if (!dev->param.useHeaderFileSize) { + in->variant.fileVariant. + fileSize = + in->variant.fileVariant. + scannedFileSize; + } + + } + /* 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 + */ + yaffs_SetChunkBit(dev, blk, c); + bi->pagesInUse++; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, + chunkData, + NULL); + + oh = (yaffs_ObjectHeader *) chunkData; + + in = yaffs_FindObjectByNumber(dev, + tags.objectId); + if (in && in->variantType != oh->type) { + /* This should not happen, but somehow + * Wev'e ended up with an objectId that has been reused but not yet + * deleted, and worse still it has changed type. Delete the old object. + */ + + yaffs_DeleteObject(in); + + in = 0; + } + + in = yaffs_FindOrCreateObjectByNumber(dev, + tags. + objectId, + oh->type); + + if (!in) + alloc_failed = 1; + + if (in && oh->shadowsObject > 0) { + + struct yaffs_ShadowFixerStruct *fixer; + fixer = YMALLOC(sizeof(struct yaffs_ShadowFixerStruct)); + if (fixer) { + fixer->next = shadowFixerList; + shadowFixerList = fixer; + fixer->objectId = tags.objectId; + fixer->shadowedId = oh->shadowsObject; + T(YAFFS_TRACE_SCAN, + (TSTR + (" Shadow fixer: %d shadows %d" TENDSTR), + fixer->objectId, fixer->shadowedId)); + + } + + } + + if (in && 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->hdrChunk, + 1, __LINE__); + in->valid = 0; + } else { + /* Use existing - destroy this one. */ + yaffs_DeleteChunk(dev, chunk, 1, + __LINE__); + } + } + + if (in && !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->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; +#endif + in->hdrChunk = chunk; + in->serial = tags.serialNumber; + + } else if (in && !in->valid) { + /* we need to load this info */ + + in->valid = 1; + in->variantType = oh->type; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; +#endif + in->hdrChunk = chunk; + in->serial = tags.serialNumber; + + yaffs_SetObjectNameFromOH(in, oh); + in->dirty = 0; + + /* directory stuff... + * hook up to parent + */ + + parent = + yaffs_FindOrCreateObjectByNumber + (dev, oh->parentObjectId, + YAFFS_OBJECT_TYPE_DIRECTORY); + if (!parent) + alloc_failed = 1; + if (parent && parent->variantType == + YAFFS_OBJECT_TYPE_UNKNOWN) { + /* Set up as a directory */ + parent->variantType = + YAFFS_OBJECT_TYPE_DIRECTORY; + YINIT_LIST_HEAD(&parent->variant. + directoryVariant. + children); + } else if (!parent || parent->variantType != + YAFFS_OBJECT_TYPE_DIRECTORY) { + /* Hoosterman, another problem.... + * We're trying to use a non-directory as a directory + */ + + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." + TENDSTR))); + parent = dev->lostNFoundDir; + } + + yaffs_AddObjectToDirectory(parent, in); + + if (0 && (parent == dev->deletedDir || + 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: + if (dev->param.useHeaderFileSize) + + in->variant.fileVariant. + fileSize = + oh->fileSize; + + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant.hardLinkVariant. + equivalentObjectId = + oh->equivalentObjectId; + in->hardLinks.next = + (struct ylist_head *) + 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: + in->variant.symLinkVariant.alias = + yaffs_CloneString(oh->alias); + if (!in->variant.symLinkVariant.alias) + alloc_failed = 1; + break; + } + + } + } + } + + if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + /* If we got this far while scanning, then the block is fully allocated.*/ + state = YAFFS_BLOCK_STATE_FULL; + } + + if (state == YAFFS_BLOCK_STATE_ALLOCATING) { + /* If the block was partially allocated then treat it as fully allocated.*/ + state = YAFFS_BLOCK_STATE_FULL; + dev->allocationBlock = -1; + } + + bi->blockState = state; + + /* Now let's see if it was dirty */ + if (bi->pagesInUse == 0 && + !bi->hasShrinkHeader && + bi->blockState == YAFFS_BLOCK_STATE_FULL) { + yaffs_BlockBecameDirty(dev, blk); + } + + } + + + /* Ok, we've done all the scanning. + * Fix up the hard link chains. + * We should now have scanned all the objects, now it's time to add these + * hardlinks. + */ + + yaffs_HardlinkFixup(dev, hardList); + + /* Fix up any shadowed objects */ + { + struct yaffs_ShadowFixerStruct *fixer; + yaffs_Object *obj; + + while (shadowFixerList) { + fixer = shadowFixerList; + shadowFixerList = fixer->next; + /* Complete the rename transaction by deleting the shadowed object + * then setting the object header to unshadowed. + */ + obj = yaffs_FindObjectByNumber(dev, fixer->shadowedId); + if (obj) + yaffs_DeleteObject(obj); + + obj = yaffs_FindObjectByNumber(dev, fixer->objectId); + + if (obj) + yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0, NULL); + + YFREE(fixer); + } + } + + yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); + + if (alloc_failed) + return YAFFS_FAIL; + + T(YAFFS_TRACE_SCAN, (TSTR("yaffs1_Scan ends" TENDSTR))); + + + return YAFFS_OK; +} + diff --git a/fs/yaffs2/yaffs_yaffs1.h b/fs/yaffs2/yaffs_yaffs1.h new file mode 100644 index 000000000000..9769eed35ebd --- /dev/null +++ b/fs/yaffs2/yaffs_yaffs1.h @@ -0,0 +1,22 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_YAFFS1_H__ +#define __YAFFS_YAFFS1_H__ + +#include "yaffs_guts.h" +int yaffs1_Scan(yaffs_Device *dev); + +#endif diff --git a/fs/yaffs2/yaffs_yaffs2.c b/fs/yaffs2/yaffs_yaffs2.c new file mode 100644 index 000000000000..29b8fd078acb --- /dev/null +++ b/fs/yaffs2/yaffs_yaffs2.c @@ -0,0 +1,1540 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + */ + + +#include "yaffs_guts.h" +#include "yaffs_trace.h" +#include "yaffs_yaffs2.h" +#include "yaffs_checkptrw.h" +#include "yaffs_bitmap.h" +#include "yaffs_qsort.h" +#include "yaffs_nand.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_verify.h" + +/* + * Checkpoints are really no benefit on very small partitions. + * + * To save space on small partitions don't bother with checkpoints unless + * the partition is at least this big. + */ +#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 + +#define YAFFS_SMALL_HOLE_THRESHOLD 4 + + +/* + * Oldest Dirty Sequence Number handling. + */ + +/* yaffs2_CalcOldestDirtySequence() + * yaffs2_FindOldestDirtySequence() + * Calculate the oldest dirty sequence number if we don't know it. + */ +void yaffs2_CalcOldestDirtySequence(yaffs_Device *dev) +{ + int i; + unsigned seq; + unsigned blockNo = 0; + yaffs_BlockInfo *b; + + if(!dev->param.isYaffs2) + return; + + /* Find the oldest dirty sequence number. */ + seq = dev->sequenceNumber + 1; + b = dev->blockInfo; + for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { + if (b->blockState == YAFFS_BLOCK_STATE_FULL && + (b->pagesInUse - b->softDeletions) < dev->param.nChunksPerBlock && + b->sequenceNumber < seq) { + seq = b->sequenceNumber; + blockNo = i; + } + b++; + } + + if(blockNo){ + dev->oldestDirtySequence = seq; + dev->oldestDirtyBlock = blockNo; + } + +} + + +void yaffs2_FindOldestDirtySequence(yaffs_Device *dev) +{ + if(!dev->param.isYaffs2) + return; + + if(!dev->oldestDirtySequence) + yaffs2_CalcOldestDirtySequence(dev); +} + +/* + * yaffs_ClearOldestDirtySequence() + * Called when a block is erased or marked bad. (ie. when its sequenceNumber + * becomes invalid). If the value matches the oldest then we clear + * dev->oldestDirtySequence to force its recomputation. + */ +void yaffs2_ClearOldestDirtySequence(yaffs_Device *dev, yaffs_BlockInfo *bi) +{ + + if(!dev->param.isYaffs2) + return; + + if(!bi || bi->sequenceNumber == dev->oldestDirtySequence){ + dev->oldestDirtySequence = 0; + dev->oldestDirtyBlock = 0; + } +} + +/* + * yaffs2_UpdateOldestDirtySequence() + * Update the oldest dirty sequence number whenever we dirty a block. + * Only do this if the oldestDirtySequence is actually being tracked. + */ +void yaffs2_UpdateOldestDirtySequence(yaffs_Device *dev, unsigned blockNo, yaffs_BlockInfo *bi) +{ + if(!dev->param.isYaffs2) + return; + + if(dev->oldestDirtySequence){ + if(dev->oldestDirtySequence > bi->sequenceNumber){ + dev->oldestDirtySequence = bi->sequenceNumber; + dev->oldestDirtyBlock = blockNo; + } + } +} + +int yaffs2_BlockNotDisqualifiedFromGC(yaffs_Device *dev, + yaffs_BlockInfo *bi) +{ + + if (!dev->param.isYaffs2) + return 1; /* disqualification only applies to yaffs2. */ + + if (!bi->hasShrinkHeader) + return 1; /* can gc */ + + yaffs2_FindOldestDirtySequence(dev); + + /* Can't do gc of this block if there are any blocks older than this one that have + * discarded pages. + */ + return (bi->sequenceNumber <= dev->oldestDirtySequence); +} + +/* + * yaffs2_FindRefreshBlock() + * periodically finds the oldest full block by sequence number for refreshing. + * Only for yaffs2. + */ +__u32 yaffs2_FindRefreshBlock(yaffs_Device *dev) +{ + __u32 b ; + + __u32 oldest = 0; + __u32 oldestSequence = 0; + + yaffs_BlockInfo *bi; + + if(!dev->param.isYaffs2) + return oldest; + + /* + * If refresh period < 10 then refreshing is disabled. + */ + if(dev->param.refreshPeriod < 10) + return oldest; + + /* + * Fix broken values. + */ + if(dev->refreshSkip > dev->param.refreshPeriod) + dev->refreshSkip = dev->param.refreshPeriod; + + if(dev->refreshSkip > 0) + return oldest; + + /* + * Refresh skip is now zero. + * We'll do a refresh this time around.... + * Update the refresh skip and find the oldest block. + */ + dev->refreshSkip = dev->param.refreshPeriod; + dev->refreshCount++; + bi = dev->blockInfo; + for (b = dev->internalStartBlock; b <=dev->internalEndBlock; b++){ + + if (bi->blockState == YAFFS_BLOCK_STATE_FULL){ + + if(oldest < 1 || + bi->sequenceNumber < oldestSequence){ + oldest = b; + oldestSequence = bi->sequenceNumber; + } + } + bi++; + } + + if (oldest > 0) { + T(YAFFS_TRACE_GC, + (TSTR("GC refresh count %d selected block %d with sequenceNumber %d" TENDSTR), + dev->refreshCount, oldest, oldestSequence)); + } + + return oldest; +} + +int yaffs2_CheckpointRequired(yaffs_Device *dev) +{ + int nblocks; + + if(!dev->param.isYaffs2) + return 0; + + nblocks = dev->internalEndBlock - dev->internalStartBlock + 1 ; + + return !dev->param.skipCheckpointWrite && + !dev->readOnly && + (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); +} + +int yaffs2_CalcCheckpointBlocksRequired(yaffs_Device *dev) +{ + int retval; + + if(!dev->param.isYaffs2) + return 0; + + if (!dev->nCheckpointBlocksRequired && + yaffs2_CheckpointRequired(dev)){ + /* Not a valid value so recalculate */ + int nBytes = 0; + int nBlocks; + int devBlocks = (dev->param.endBlock - dev->param.startBlock + 1); + + nBytes += sizeof(yaffs_CheckpointValidity); + nBytes += sizeof(yaffs_CheckpointDevice); + nBytes += devBlocks * sizeof(yaffs_BlockInfo); + nBytes += devBlocks * dev->chunkBitmapStride; + nBytes += (sizeof(yaffs_CheckpointObject) + sizeof(__u32)) * (dev->nObjects); + nBytes += (dev->tnodeSize + sizeof(__u32)) * (dev->nTnodes); + nBytes += sizeof(yaffs_CheckpointValidity); + nBytes += sizeof(__u32); /* checksum*/ + + /* Round up and add 2 blocks to allow for some bad blocks, so add 3 */ + + nBlocks = (nBytes/(dev->nDataBytesPerChunk * dev->param.nChunksPerBlock)) + 3; + + dev->nCheckpointBlocksRequired = nBlocks; + } + + retval = dev->nCheckpointBlocksRequired - dev->blocksInCheckpoint; + if(retval < 0) + retval = 0; + return retval; +} + +/*--------------------- Checkpointing --------------------*/ + + +static int yaffs2_WriteCheckpointValidityMarker(yaffs_Device *dev, int head) +{ + yaffs_CheckpointValidity cp; + + memset(&cp, 0, sizeof(cp)); + + cp.structType = sizeof(cp); + cp.magic = YAFFS_MAGIC; + cp.version = YAFFS_CHECKPOINT_VERSION; + cp.head = (head) ? 1 : 0; + + return (yaffs2_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp)) ? + 1 : 0; +} + +static int yaffs2_ReadCheckpointValidityMarker(yaffs_Device *dev, int head) +{ + yaffs_CheckpointValidity cp; + int ok; + + ok = (yaffs2_CheckpointRead(dev, &cp, sizeof(cp)) == sizeof(cp)); + + if (ok) + ok = (cp.structType == sizeof(cp)) && + (cp.magic == YAFFS_MAGIC) && + (cp.version == YAFFS_CHECKPOINT_VERSION) && + (cp.head == ((head) ? 1 : 0)); + return ok ? 1 : 0; +} + +static void yaffs2_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp, + yaffs_Device *dev) +{ + cp->nErasedBlocks = dev->nErasedBlocks; + cp->allocationBlock = dev->allocationBlock; + cp->allocationPage = dev->allocationPage; + cp->nFreeChunks = dev->nFreeChunks; + + cp->nDeletedFiles = dev->nDeletedFiles; + cp->nUnlinkedFiles = dev->nUnlinkedFiles; + cp->nBackgroundDeletions = dev->nBackgroundDeletions; + cp->sequenceNumber = dev->sequenceNumber; + +} + +static void yaffs2_CheckpointDeviceToDevice(yaffs_Device *dev, + yaffs_CheckpointDevice *cp) +{ + dev->nErasedBlocks = cp->nErasedBlocks; + dev->allocationBlock = cp->allocationBlock; + dev->allocationPage = cp->allocationPage; + dev->nFreeChunks = cp->nFreeChunks; + + dev->nDeletedFiles = cp->nDeletedFiles; + dev->nUnlinkedFiles = cp->nUnlinkedFiles; + dev->nBackgroundDeletions = cp->nBackgroundDeletions; + dev->sequenceNumber = cp->sequenceNumber; +} + + +static int yaffs2_WriteCheckpointDevice(yaffs_Device *dev) +{ + yaffs_CheckpointDevice cp; + __u32 nBytes; + __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); + + int ok; + + /* Write device runtime values*/ + yaffs2_DeviceToCheckpointDevice(&cp, dev); + cp.structType = sizeof(cp); + + ok = (yaffs2_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp)); + + /* Write block info */ + if (ok) { + nBytes = nBlocks * sizeof(yaffs_BlockInfo); + ok = (yaffs2_CheckpointWrite(dev, dev->blockInfo, nBytes) == nBytes); + } + + /* Write chunk bits */ + if (ok) { + nBytes = nBlocks * dev->chunkBitmapStride; + ok = (yaffs2_CheckpointWrite(dev, dev->chunkBits, nBytes) == nBytes); + } + return ok ? 1 : 0; + +} + +static int yaffs2_ReadCheckpointDevice(yaffs_Device *dev) +{ + yaffs_CheckpointDevice cp; + __u32 nBytes; + __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); + + int ok; + + ok = (yaffs2_CheckpointRead(dev, &cp, sizeof(cp)) == sizeof(cp)); + if (!ok) + return 0; + + if (cp.structType != sizeof(cp)) + return 0; + + + yaffs2_CheckpointDeviceToDevice(dev, &cp); + + nBytes = nBlocks * sizeof(yaffs_BlockInfo); + + ok = (yaffs2_CheckpointRead(dev, dev->blockInfo, nBytes) == nBytes); + + if (!ok) + return 0; + nBytes = nBlocks * dev->chunkBitmapStride; + + ok = (yaffs2_CheckpointRead(dev, dev->chunkBits, nBytes) == nBytes); + + return ok ? 1 : 0; +} + +static void yaffs2_ObjectToCheckpointObject(yaffs_CheckpointObject *cp, + yaffs_Object *obj) +{ + + cp->objectId = obj->objectId; + cp->parentId = (obj->parent) ? obj->parent->objectId : 0; + cp->hdrChunk = obj->hdrChunk; + cp->variantType = obj->variantType; + cp->deleted = obj->deleted; + cp->softDeleted = obj->softDeleted; + cp->unlinked = obj->unlinked; + cp->fake = obj->fake; + cp->renameAllowed = obj->renameAllowed; + cp->unlinkAllowed = obj->unlinkAllowed; + cp->serial = obj->serial; + cp->nDataChunks = obj->nDataChunks; + + if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) + cp->fileSizeOrEquivalentObjectId = obj->variant.fileVariant.fileSize; + else if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) + cp->fileSizeOrEquivalentObjectId = obj->variant.hardLinkVariant.equivalentObjectId; +} + +static int yaffs2_CheckpointObjectToObject(yaffs_Object *obj, yaffs_CheckpointObject *cp) +{ + + yaffs_Object *parent; + + if (obj->variantType != cp->variantType) { + T(YAFFS_TRACE_ERROR, (TSTR("Checkpoint read object %d type %d " + TCONT("chunk %d does not match existing object type %d") + TENDSTR), cp->objectId, cp->variantType, cp->hdrChunk, + obj->variantType)); + return 0; + } + + obj->objectId = cp->objectId; + + if (cp->parentId) + parent = yaffs_FindOrCreateObjectByNumber( + obj->myDev, + cp->parentId, + YAFFS_OBJECT_TYPE_DIRECTORY); + else + parent = NULL; + + if (parent) { + if (parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_ALWAYS, (TSTR("Checkpoint read object %d parent %d type %d" + TCONT(" chunk %d Parent type, %d, not directory") + TENDSTR), + cp->objectId, cp->parentId, cp->variantType, + cp->hdrChunk, parent->variantType)); + return 0; + } + yaffs_AddObjectToDirectory(parent, obj); + } + + obj->hdrChunk = cp->hdrChunk; + obj->variantType = cp->variantType; + obj->deleted = cp->deleted; + obj->softDeleted = cp->softDeleted; + obj->unlinked = cp->unlinked; + obj->fake = cp->fake; + obj->renameAllowed = cp->renameAllowed; + obj->unlinkAllowed = cp->unlinkAllowed; + obj->serial = cp->serial; + obj->nDataChunks = cp->nDataChunks; + + if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) + obj->variant.fileVariant.fileSize = cp->fileSizeOrEquivalentObjectId; + else if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) + obj->variant.hardLinkVariant.equivalentObjectId = cp->fileSizeOrEquivalentObjectId; + + if (obj->hdrChunk > 0) + obj->lazyLoaded = 1; + return 1; +} + + + +static int yaffs2_CheckpointTnodeWorker(yaffs_Object *in, yaffs_Tnode *tn, + __u32 level, int chunkOffset) +{ + int i; + yaffs_Device *dev = in->myDev; + int ok = 1; + + if (tn) { + if (level > 0) { + + for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { + if (tn->internal[i]) { + ok = yaffs2_CheckpointTnodeWorker(in, + tn->internal[i], + level - 1, + (chunkOffset<<YAFFS_TNODES_INTERNAL_BITS) + i); + } + } + } else if (level == 0) { + __u32 baseOffset = chunkOffset << YAFFS_TNODES_LEVEL0_BITS; + ok = (yaffs2_CheckpointWrite(dev, &baseOffset, sizeof(baseOffset)) == sizeof(baseOffset)); + if (ok) + ok = (yaffs2_CheckpointWrite(dev, tn, dev->tnodeSize) == dev->tnodeSize); + } + } + + return ok; + +} + +static int yaffs2_WriteCheckpointTnodes(yaffs_Object *obj) +{ + __u32 endMarker = ~0; + int ok = 1; + + if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) { + ok = yaffs2_CheckpointTnodeWorker(obj, + obj->variant.fileVariant.top, + obj->variant.fileVariant.topLevel, + 0); + if (ok) + ok = (yaffs2_CheckpointWrite(obj->myDev, &endMarker, sizeof(endMarker)) == + sizeof(endMarker)); + } + + return ok ? 1 : 0; +} + +static int yaffs2_ReadCheckpointTnodes(yaffs_Object *obj) +{ + __u32 baseChunk; + int ok = 1; + yaffs_Device *dev = obj->myDev; + yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant; + yaffs_Tnode *tn; + int nread = 0; + + ok = (yaffs2_CheckpointRead(dev, &baseChunk, sizeof(baseChunk)) == sizeof(baseChunk)); + + while (ok && (~baseChunk)) { + nread++; + /* Read level 0 tnode */ + + + tn = yaffs_GetTnode(dev); + if (tn){ + ok = (yaffs2_CheckpointRead(dev, tn, dev->tnodeSize) == dev->tnodeSize); + } else + ok = 0; + + if (tn && ok) + ok = yaffs_AddOrFindLevel0Tnode(dev, + fileStructPtr, + baseChunk, + tn) ? 1 : 0; + + if (ok) + ok = (yaffs2_CheckpointRead(dev, &baseChunk, sizeof(baseChunk)) == sizeof(baseChunk)); + + } + + T(YAFFS_TRACE_CHECKPOINT, ( + TSTR("Checkpoint read tnodes %d records, last %d. ok %d" TENDSTR), + nread, baseChunk, ok)); + + return ok ? 1 : 0; +} + + +static int yaffs2_WriteCheckpointObjects(yaffs_Device *dev) +{ + yaffs_Object *obj; + yaffs_CheckpointObject cp; + int i; + int ok = 1; + struct ylist_head *lh; + + + /* Iterate through the objects in each hash entry, + * dumping them to the checkpointing stream. + */ + + for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { + ylist_for_each(lh, &dev->objectBucket[i].list) { + if (lh) { + obj = ylist_entry(lh, yaffs_Object, hashLink); + if (!obj->deferedFree) { + yaffs2_ObjectToCheckpointObject(&cp, obj); + cp.structType = sizeof(cp); + + T(YAFFS_TRACE_CHECKPOINT, ( + TSTR("Checkpoint write object %d parent %d type %d chunk %d obj addr %p" TENDSTR), + cp.objectId, cp.parentId, cp.variantType, cp.hdrChunk, obj)); + + ok = (yaffs2_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp)); + + if (ok && obj->variantType == YAFFS_OBJECT_TYPE_FILE) + ok = yaffs2_WriteCheckpointTnodes(obj); + } + } + } + } + + /* Dump end of list */ + memset(&cp, 0xFF, sizeof(yaffs_CheckpointObject)); + cp.structType = sizeof(cp); + + if (ok) + ok = (yaffs2_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp)); + + return ok ? 1 : 0; +} + +static int yaffs2_ReadCheckpointObjects(yaffs_Device *dev) +{ + yaffs_Object *obj; + yaffs_CheckpointObject cp; + int ok = 1; + int done = 0; + yaffs_Object *hardList = NULL; + + while (ok && !done) { + ok = (yaffs2_CheckpointRead(dev, &cp, sizeof(cp)) == sizeof(cp)); + if (cp.structType != sizeof(cp)) { + T(YAFFS_TRACE_CHECKPOINT, (TSTR("struct size %d instead of %d ok %d"TENDSTR), + cp.structType, (int)sizeof(cp), ok)); + ok = 0; + } + + T(YAFFS_TRACE_CHECKPOINT, (TSTR("Checkpoint read object %d parent %d type %d chunk %d " TENDSTR), + cp.objectId, cp.parentId, cp.variantType, cp.hdrChunk)); + + if (ok && cp.objectId == ~0) + done = 1; + else if (ok) { + obj = yaffs_FindOrCreateObjectByNumber(dev, cp.objectId, cp.variantType); + if (obj) { + ok = yaffs2_CheckpointObjectToObject(obj, &cp); + if (!ok) + break; + if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) { + ok = yaffs2_ReadCheckpointTnodes(obj); + } else if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { + obj->hardLinks.next = + (struct ylist_head *) hardList; + hardList = obj; + } + } else + ok = 0; + } + } + + if (ok) + yaffs_HardlinkFixup(dev, hardList); + + return ok ? 1 : 0; +} + +static int yaffs2_WriteCheckpointSum(yaffs_Device *dev) +{ + __u32 checkpointSum; + int ok; + + yaffs2_GetCheckpointSum(dev, &checkpointSum); + + ok = (yaffs2_CheckpointWrite(dev, &checkpointSum, sizeof(checkpointSum)) == sizeof(checkpointSum)); + + if (!ok) + return 0; + + return 1; +} + +static int yaffs2_ReadCheckpointSum(yaffs_Device *dev) +{ + __u32 checkpointSum0; + __u32 checkpointSum1; + int ok; + + yaffs2_GetCheckpointSum(dev, &checkpointSum0); + + ok = (yaffs2_CheckpointRead(dev, &checkpointSum1, sizeof(checkpointSum1)) == sizeof(checkpointSum1)); + + if (!ok) + return 0; + + if (checkpointSum0 != checkpointSum1) + return 0; + + return 1; +} + + +static int yaffs2_WriteCheckpointData(yaffs_Device *dev) +{ + int ok = 1; + + if (!yaffs2_CheckpointRequired(dev)) { + T(YAFFS_TRACE_CHECKPOINT, (TSTR("skipping checkpoint write" TENDSTR))); + ok = 0; + } + + if (ok) + ok = yaffs2_CheckpointOpen(dev, 1); + + if (ok) { + T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint validity" TENDSTR))); + ok = yaffs2_WriteCheckpointValidityMarker(dev, 1); + } + if (ok) { + T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint device" TENDSTR))); + ok = yaffs2_WriteCheckpointDevice(dev); + } + if (ok) { + T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint objects" TENDSTR))); + ok = yaffs2_WriteCheckpointObjects(dev); + } + if (ok) { + T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint validity" TENDSTR))); + ok = yaffs2_WriteCheckpointValidityMarker(dev, 0); + } + + if (ok) + ok = yaffs2_WriteCheckpointSum(dev); + + if (!yaffs2_CheckpointClose(dev)) + ok = 0; + + if (ok) + dev->isCheckpointed = 1; + else + dev->isCheckpointed = 0; + + return dev->isCheckpointed; +} + +static int yaffs2_ReadCheckpointData(yaffs_Device *dev) +{ + int ok = 1; + + if(!dev->param.isYaffs2) + ok = 0; + + if (ok && dev->param.skipCheckpointRead) { + T(YAFFS_TRACE_CHECKPOINT, (TSTR("skipping checkpoint read" TENDSTR))); + ok = 0; + } + + if (ok) + ok = yaffs2_CheckpointOpen(dev, 0); /* open for read */ + + if (ok) { + T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint validity" TENDSTR))); + ok = yaffs2_ReadCheckpointValidityMarker(dev, 1); + } + if (ok) { + T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint device" TENDSTR))); + ok = yaffs2_ReadCheckpointDevice(dev); + } + if (ok) { + T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint objects" TENDSTR))); + ok = yaffs2_ReadCheckpointObjects(dev); + } + if (ok) { + T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint validity" TENDSTR))); + ok = yaffs2_ReadCheckpointValidityMarker(dev, 0); + } + + if (ok) { + ok = yaffs2_ReadCheckpointSum(dev); + T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint checksum %d" TENDSTR), ok)); + } + + if (!yaffs2_CheckpointClose(dev)) + ok = 0; + + if (ok) + dev->isCheckpointed = 1; + else + dev->isCheckpointed = 0; + + return ok ? 1 : 0; + +} + +void yaffs2_InvalidateCheckpoint(yaffs_Device *dev) +{ + if (dev->isCheckpointed || + dev->blocksInCheckpoint > 0) { + dev->isCheckpointed = 0; + yaffs2_CheckpointInvalidateStream(dev); + } + if (dev->param.markSuperBlockDirty) + dev->param.markSuperBlockDirty(dev); +} + + +int yaffs_CheckpointSave(yaffs_Device *dev) +{ + + T(YAFFS_TRACE_CHECKPOINT, (TSTR("save entry: isCheckpointed %d"TENDSTR), dev->isCheckpointed)); + + yaffs_VerifyObjects(dev); + yaffs_VerifyBlocks(dev); + yaffs_VerifyFreeChunks(dev); + + if (!dev->isCheckpointed) { + yaffs2_InvalidateCheckpoint(dev); + yaffs2_WriteCheckpointData(dev); + } + + T(YAFFS_TRACE_ALWAYS, (TSTR("save exit: isCheckpointed %d"TENDSTR), dev->isCheckpointed)); + + return dev->isCheckpointed; +} + +int yaffs2_CheckpointRestore(yaffs_Device *dev) +{ + int retval; + T(YAFFS_TRACE_CHECKPOINT, (TSTR("restore entry: isCheckpointed %d"TENDSTR), dev->isCheckpointed)); + + retval = yaffs2_ReadCheckpointData(dev); + + if (dev->isCheckpointed) { + yaffs_VerifyObjects(dev); + yaffs_VerifyBlocks(dev); + yaffs_VerifyFreeChunks(dev); + } + + T(YAFFS_TRACE_CHECKPOINT, (TSTR("restore exit: isCheckpointed %d"TENDSTR), dev->isCheckpointed)); + + return retval; +} + +int yaffs2_HandleHole(yaffs_Object *obj, loff_t newSize) +{ + /* if newsSize > oldFileSize. + * We're going to be writing a hole. + * If the hole is small then write zeros otherwise write a start of hole marker. + */ + + + loff_t oldFileSize; + int increase; + int smallHole ; + int result = YAFFS_OK; + yaffs_Device *dev = NULL; + + __u8 *localBuffer = NULL; + + int smallIncreaseOk = 0; + + if(!obj) + return YAFFS_FAIL; + + if(obj->variantType != YAFFS_OBJECT_TYPE_FILE) + return YAFFS_FAIL; + + dev = obj->myDev; + + /* Bail out if not yaffs2 mode */ + if(!dev->param.isYaffs2) + return YAFFS_OK; + + oldFileSize = obj->variant.fileVariant.fileSize; + + if (newSize <= oldFileSize) + return YAFFS_OK; + + increase = newSize - oldFileSize; + + if(increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->nDataBytesPerChunk && + yaffs_CheckSpaceForAllocation(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) + smallHole = 1; + else + smallHole = 0; + + if(smallHole) + localBuffer= yaffs_GetTempBuffer(dev, __LINE__); + + if(localBuffer){ + /* fill hole with zero bytes */ + int pos = oldFileSize; + int thisWrite; + int written; + memset(localBuffer,0,dev->nDataBytesPerChunk); + smallIncreaseOk = 1; + + while(increase > 0 && smallIncreaseOk){ + thisWrite = increase; + if(thisWrite > dev->nDataBytesPerChunk) + thisWrite = dev->nDataBytesPerChunk; + written = yaffs_DoWriteDataToFile(obj,localBuffer,pos,thisWrite,0); + if(written == thisWrite){ + pos += thisWrite; + increase -= thisWrite; + } else + smallIncreaseOk = 0; + } + + yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__); + + /* If we were out of space then reverse any chunks we've added */ + if(!smallIncreaseOk) + yaffs_ResizeDown(obj, oldFileSize); + } + + if (!smallIncreaseOk && + obj->parent && + obj->parent->objectId != YAFFS_OBJECTID_UNLINKED && + obj->parent->objectId != YAFFS_OBJECTID_DELETED){ + /* Write a hole start header with the old file size */ + yaffs_UpdateObjectHeader(obj, NULL, 0, 1, 0, NULL); + } + + return result; + +} + + +typedef struct { + int seq; + int block; +} yaffs_BlockIndex; + + +static int yaffs2_ybicmp(const void *a, const void *b) +{ + register int aseq = ((yaffs_BlockIndex *)a)->seq; + register int bseq = ((yaffs_BlockIndex *)b)->seq; + register int ablock = ((yaffs_BlockIndex *)a)->block; + register int bblock = ((yaffs_BlockIndex *)b)->block; + if (aseq == bseq) + return ablock - bblock; + else + return aseq - bseq; +} + +int yaffs2_ScanBackwards(yaffs_Device *dev) +{ + yaffs_ExtendedTags tags; + int blk; + int blockIterator; + int startIterator; + int endIterator; + int nBlocksToScan = 0; + + int chunk; + int result; + int c; + int deleted; + yaffs_BlockState state; + yaffs_Object *hardList = NULL; + yaffs_BlockInfo *bi; + __u32 sequenceNumber; + yaffs_ObjectHeader *oh; + yaffs_Object *in; + yaffs_Object *parent; + int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; + int itsUnlinked; + __u8 *chunkData; + + int fileSize; + int isShrink; + int foundChunksInBlock; + int equivalentObjectId; + int alloc_failed = 0; + + + yaffs_BlockIndex *blockIndex = NULL; + int altBlockIndex = 0; + + T(YAFFS_TRACE_SCAN, + (TSTR + ("yaffs2_ScanBackwards starts intstartblk %d intendblk %d..." + TENDSTR), dev->internalStartBlock, dev->internalEndBlock)); + + + dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; + + blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); + + if (!blockIndex) { + blockIndex = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockIndex)); + altBlockIndex = 1; + } + + if (!blockIndex) { + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs2_ScanBackwards() could not allocate block index!" TENDSTR))); + return YAFFS_FAIL; + } + + dev->blocksInCheckpoint = 0; + + chunkData = yaffs_GetTempBuffer(dev, __LINE__); + + /* Scan all the blocks to determine their state */ + bi = dev->blockInfo; + for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { + yaffs_ClearChunkBits(dev, blk); + bi->pagesInUse = 0; + bi->softDeletions = 0; + + yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); + + bi->blockState = state; + bi->sequenceNumber = sequenceNumber; + + if (bi->sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA) + bi->blockState = state = YAFFS_BLOCK_STATE_CHECKPOINT; + if (bi->sequenceNumber == YAFFS_SEQUENCE_BAD_BLOCK) + bi->blockState = state = YAFFS_BLOCK_STATE_DEAD; + + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, + state, sequenceNumber)); + + + if (state == YAFFS_BLOCK_STATE_CHECKPOINT) { + dev->blocksInCheckpoint++; + + } else if (state == YAFFS_BLOCK_STATE_DEAD) { + T(YAFFS_TRACE_BAD_BLOCKS, + (TSTR("block %d is bad" TENDSTR), blk)); + } else if (state == YAFFS_BLOCK_STATE_EMPTY) { + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block empty " TENDSTR))); + dev->nErasedBlocks++; + dev->nFreeChunks += dev->param.nChunksPerBlock; + } else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + + /* Determine the highest sequence number */ + if (sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER && + sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) { + + blockIndex[nBlocksToScan].seq = sequenceNumber; + blockIndex[nBlocksToScan].block = blk; + + nBlocksToScan++; + + if (sequenceNumber >= dev->sequenceNumber) + dev->sequenceNumber = sequenceNumber; + } else { + /* TODO: Nasty sequence number! */ + T(YAFFS_TRACE_SCAN, + (TSTR + ("Block scanning block %d has bad sequence number %d" + TENDSTR), blk, sequenceNumber)); + + } + } + bi++; + } + + T(YAFFS_TRACE_SCAN, + (TSTR("%d blocks to be sorted..." TENDSTR), nBlocksToScan)); + + + + YYIELD(); + + /* Sort the blocks by sequence number*/ + yaffs_qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), yaffs2_ybicmp); + + YYIELD(); + + T(YAFFS_TRACE_SCAN, (TSTR("...done" TENDSTR))); + + /* Now scan the blocks looking at the data. */ + startIterator = 0; + endIterator = nBlocksToScan - 1; + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); + + /* For each block.... backwards */ + for (blockIterator = endIterator; !alloc_failed && blockIterator >= startIterator; + blockIterator--) { + /* Cooperative multitasking! This loop can run for so + long that watchdog timers expire. */ + YYIELD(); + + /* get the block to scan in the correct order */ + blk = blockIndex[blockIterator].block; + + bi = yaffs_GetBlockInfo(dev, blk); + + + state = bi->blockState; + + deleted = 0; + + /* For each chunk in each block that needs scanning.... */ + foundChunksInBlock = 0; + for (c = dev->param.nChunksPerBlock - 1; + !alloc_failed && c >= 0 && + (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || + state == YAFFS_BLOCK_STATE_ALLOCATING); c--) { + /* Scan backwards... + * Read the tags and decide what to do + */ + + chunk = blk * dev->param.nChunksPerBlock + c; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, + &tags); + + /* Let's have a good look at this chunk... */ + + if (!tags.chunkUsed) { + /* An unassigned chunk in the block. + * If there are used chunks after this one, then + * it is a chunk that was skipped due to failing the erased + * check. Just skip it so that it can be deleted. + * But, more typically, We get here when this is an unallocated + * chunk and his means that either the block is empty or + * this is the one being allocated from + */ + + if (foundChunksInBlock) { + /* This is a chunk that was skipped due to failing the erased check */ + } else if (c == 0) { + /* We're looking at the first chunk in the block so the block is unused */ + state = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + } else { + if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || + state == YAFFS_BLOCK_STATE_ALLOCATING) { + if (dev->sequenceNumber == bi->sequenceNumber) { + /* this is the block being allocated from */ + + T(YAFFS_TRACE_SCAN, + (TSTR + (" Allocating from %d %d" + TENDSTR), blk, c)); + + state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->allocationBlock = blk; + dev->allocationPage = c; + dev->allocationBlockFinder = blk; + } else { + /* This is a partially written block that is not + * the current allocation block. + */ + + T(YAFFS_TRACE_SCAN, + (TSTR("Partially written block %d detected" TENDSTR), + blk)); + } + } + } + + dev->nFreeChunks++; + + } else if (tags.eccResult == YAFFS_ECC_RESULT_UNFIXED) { + T(YAFFS_TRACE_SCAN, + (TSTR(" Unfixed ECC in chunk(%d:%d), chunk ignored"TENDSTR), + blk, c)); + + dev->nFreeChunks++; + + } else if (tags.objectId > YAFFS_MAX_OBJECT_ID || + tags.chunkId > YAFFS_MAX_CHUNK_ID || + (tags.chunkId > 0 && tags.byteCount > dev->nDataBytesPerChunk) || + tags.sequenceNumber != bi->sequenceNumber ) { + T(YAFFS_TRACE_SCAN, + (TSTR("Chunk (%d:%d) with bad tags:obj = %d, chunkId = %d, byteCount = %d, ignored"TENDSTR), + blk, c,tags.objectId, tags.chunkId, tags.byteCount)); + + dev->nFreeChunks++; + + } else if (tags.chunkId > 0) { + /* chunkId > 0 so it is a data chunk... */ + unsigned int endpos; + __u32 chunkBase = + (tags.chunkId - 1) * dev->nDataBytesPerChunk; + + foundChunksInBlock = 1; + + + yaffs_SetChunkBit(dev, blk, c); + bi->pagesInUse++; + + in = yaffs_FindOrCreateObjectByNumber(dev, + tags. + objectId, + YAFFS_OBJECT_TYPE_FILE); + if (!in) { + /* Out of memory */ + alloc_failed = 1; + } + + if (in && + in->variantType == YAFFS_OBJECT_TYPE_FILE + && chunkBase < in->variant.fileVariant.shrinkSize) { + /* This has not been invalidated by a resize */ + if (!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk, -1)) { + alloc_failed = 1; + } + + /* File size is calculated by looking at the data chunks if we have not + * seen an object header yet. Stop this practice once we find an object header. + */ + endpos = chunkBase + tags.byteCount; + + if (!in->valid && /* have not got an object header yet */ + in->variant.fileVariant.scannedFileSize < endpos) { + in->variant.fileVariant.scannedFileSize = endpos; + in->variant.fileVariant.fileSize = endpos; + } + + } else if (in) { + /* This chunk has been invalidated by a resize, or a past file deletion + * so delete the chunk*/ + yaffs_DeleteChunk(dev, chunk, 1, __LINE__); + + } + } else { + /* chunkId == 0, so it is an ObjectHeader. + * Thus, we read in the object header and make the object + */ + foundChunksInBlock = 1; + + yaffs_SetChunkBit(dev, blk, c); + bi->pagesInUse++; + + oh = NULL; + in = NULL; + + if (tags.extraHeaderInfoAvailable) { + in = yaffs_FindOrCreateObjectByNumber(dev, + tags.objectId, + tags.extraObjectType); + if (!in) + alloc_failed = 1; + } + + if (!in || + (!in->valid && dev->param.disableLazyLoad) || + tags.extraShadows || + (!in->valid && + (tags.objectId == YAFFS_OBJECTID_ROOT || + tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))) { + + /* If we don't have valid info then we need to read the chunk + * TODO In future we can probably defer reading the chunk and + * living with invalid data until needed. + */ + + result = yaffs_ReadChunkWithTagsFromNAND(dev, + chunk, + chunkData, + NULL); + + oh = (yaffs_ObjectHeader *) chunkData; + + if (dev->param.inbandTags) { + /* Fix up the header if they got corrupted by inband tags */ + oh->shadowsObject = oh->inbandShadowsObject; + oh->isShrink = oh->inbandIsShrink; + } + + if (!in) { + in = yaffs_FindOrCreateObjectByNumber(dev, tags.objectId, oh->type); + if (!in) + alloc_failed = 1; + } + + } + + if (!in) { + /* TODO Hoosterman we have a problem! */ + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy: Could not make object for object %d at chunk %d during scan" + TENDSTR), tags.objectId, chunk)); + continue; + } + + if (in->valid) { + /* We have already filled this one. + * We have a duplicate that will be discarded, but + * we first have to suck out resize info if it is a file. + */ + + if ((in->variantType == YAFFS_OBJECT_TYPE_FILE) && + ((oh && + oh->type == YAFFS_OBJECT_TYPE_FILE) || + (tags.extraHeaderInfoAvailable && + tags.extraObjectType == YAFFS_OBJECT_TYPE_FILE))) { + __u32 thisSize = + (oh) ? oh->fileSize : tags. + extraFileLength; + __u32 parentObjectId = + (oh) ? oh-> + parentObjectId : tags. + extraParentObjectId; + + + isShrink = + (oh) ? oh->isShrink : tags. + extraIsShrinkHeader; + + /* If it is deleted (unlinked at start also means deleted) + * we treat the file size as being zeroed at this point. + */ + if (parentObjectId == + YAFFS_OBJECTID_DELETED + || parentObjectId == + YAFFS_OBJECTID_UNLINKED) { + thisSize = 0; + isShrink = 1; + } + + if (isShrink && in->variant.fileVariant.shrinkSize > thisSize) + in->variant.fileVariant.shrinkSize = thisSize; + + if (isShrink) + bi->hasShrinkHeader = 1; + + } + /* Use existing - destroy this one. */ + yaffs_DeleteChunk(dev, chunk, 1, __LINE__); + + } + + if (!in->valid && in->variantType != + (oh ? oh->type : tags.extraObjectType)) + T(YAFFS_TRACE_ERROR, ( + TSTR("yaffs tragedy: Bad object type, " + TCONT("%d != %d, for object %d at chunk ") + TCONT("%d during scan") + TENDSTR), oh ? + oh->type : tags.extraObjectType, + in->variantType, tags.objectId, + 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; + + if (oh) { + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; + + in->lazyLoaded = 0; + +#endif + } else + in->lazyLoaded = 1; + + in->hdrChunk = chunk; + + } else if (!in->valid) { + /* we need to load this info */ + + in->valid = 1; + in->hdrChunk = chunk; + + if (oh) { + in->variantType = oh->type; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; +#endif + + if (oh->shadowsObject > 0) + yaffs_HandleShadowedObject(dev, + oh-> + shadowsObject, + 1); + + + + yaffs_SetObjectNameFromOH(in, oh); + parent = + yaffs_FindOrCreateObjectByNumber + (dev, oh->parentObjectId, + YAFFS_OBJECT_TYPE_DIRECTORY); + + fileSize = oh->fileSize; + isShrink = oh->isShrink; + equivalentObjectId = oh->equivalentObjectId; + + } else { + in->variantType = tags.extraObjectType; + parent = + yaffs_FindOrCreateObjectByNumber + (dev, tags.extraParentObjectId, + YAFFS_OBJECT_TYPE_DIRECTORY); + fileSize = tags.extraFileLength; + isShrink = tags.extraIsShrinkHeader; + equivalentObjectId = tags.extraEquivalentObjectId; + in->lazyLoaded = 1; + + } + in->dirty = 0; + + if (!parent) + alloc_failed = 1; + + /* directory stuff... + * hook up to parent + */ + + if (parent && parent->variantType == + YAFFS_OBJECT_TYPE_UNKNOWN) { + /* Set up as a directory */ + parent->variantType = + YAFFS_OBJECT_TYPE_DIRECTORY; + YINIT_LIST_HEAD(&parent->variant. + directoryVariant. + children); + } else if (!parent || parent->variantType != + YAFFS_OBJECT_TYPE_DIRECTORY) { + /* Hoosterman, another problem.... + * We're trying to use a non-directory as a directory + */ + + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." + TENDSTR))); + parent = dev->lostNFoundDir; + } + + yaffs_AddObjectToDirectory(parent, in); + + itsUnlinked = (parent == dev->deletedDir) || + (parent == dev->unlinkedDir); + + if (isShrink) { + /* Mark the block as having a shrinkHeader */ + bi->hasShrinkHeader = 1; + } + + /* 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: + + if (in->variant.fileVariant. + scannedFileSize < fileSize) { + /* This covers the case where the file size is greater + * than where the data is + * This will happen if the file is resized to be larger + * than its current data extents. + */ + in->variant.fileVariant.fileSize = fileSize; + in->variant.fileVariant.scannedFileSize = fileSize; + } + + if (in->variant.fileVariant.shrinkSize > fileSize) + in->variant.fileVariant.shrinkSize = fileSize; + + + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + if (!itsUnlinked) { + in->variant.hardLinkVariant.equivalentObjectId = + equivalentObjectId; + in->hardLinks.next = + (struct ylist_head *) 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: + if (oh) { + in->variant.symLinkVariant.alias = + yaffs_CloneString(oh->alias); + if (!in->variant.symLinkVariant.alias) + alloc_failed = 1; + } + break; + } + + } + + } + + } /* End of scanning for each chunk */ + + if (state == YAFFS_BLOCK_STATE_NEEDS_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->hasShrinkHeader && + bi->blockState == YAFFS_BLOCK_STATE_FULL) { + yaffs_BlockBecameDirty(dev, blk); + } + + } + + yaffs_SkipRestOfBlock(dev); + + if (altBlockIndex) + YFREE_ALT(blockIndex); + else + YFREE(blockIndex); + + /* Ok, we've done all the scanning. + * Fix up the hard link chains. + * We should now have scanned all the objects, now it's time to add these + * hardlinks. + */ + yaffs_HardlinkFixup(dev, hardList); + + + yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); + + if (alloc_failed) + return YAFFS_FAIL; + + T(YAFFS_TRACE_SCAN, (TSTR("yaffs2_ScanBackwards ends" TENDSTR))); + + return YAFFS_OK; +} diff --git a/fs/yaffs2/yaffs_yaffs2.h b/fs/yaffs2/yaffs_yaffs2.h new file mode 100644 index 000000000000..08ba36300cb0 --- /dev/null +++ b/fs/yaffs2/yaffs_yaffs2.h @@ -0,0 +1,38 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2010 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_YAFFS2_H__ +#define __YAFFS_YAFFS2_H__ + +#include "yaffs_guts.h" + +void yaffs2_CalcOldestDirtySequence(yaffs_Device *dev); +void yaffs2_FindOldestDirtySequence(yaffs_Device *dev); +void yaffs2_ClearOldestDirtySequence(yaffs_Device *dev, yaffs_BlockInfo *bi); +void yaffs2_UpdateOldestDirtySequence(yaffs_Device *dev, unsigned blockNo, yaffs_BlockInfo *bi); +int yaffs2_BlockNotDisqualifiedFromGC(yaffs_Device *dev, yaffs_BlockInfo *bi); +__u32 yaffs2_FindRefreshBlock(yaffs_Device *dev); +int yaffs2_CheckpointRequired(yaffs_Device *dev); +int yaffs2_CalcCheckpointBlocksRequired(yaffs_Device *dev); + + +void yaffs2_InvalidateCheckpoint(yaffs_Device *dev); +int yaffs2_CheckpointSave(yaffs_Device *dev); +int yaffs2_CheckpointRestore(yaffs_Device *dev); + +int yaffs2_HandleHole(yaffs_Object *obj, loff_t newSize); +int yaffs2_ScanBackwards(yaffs_Device *dev); + +#endif diff --git a/fs/yaffs2/yportenv.h b/fs/yaffs2/yportenv.h index 13cc1eb62178..3dc677d17faf 100644 --- a/fs/yaffs2/yportenv.h +++ b/fs/yaffs2/yportenv.h @@ -41,12 +41,14 @@ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) #include <linux/config.h> #endif + #include <linux/kernel.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/vmalloc.h> +#include <linux/xattr.h> #define YCHAR char #define YUCHAR unsigned char @@ -59,7 +61,7 @@ #define yaffs_sprintf sprintf #define yaffs_toupper(a) toupper(a) -#define Y_INLINE inline +#define Y_INLINE __inline__ #define YAFFS_LOSTNFOUND_NAME "lost+found" #define YAFFS_LOSTNFOUND_PREFIX "obj" @@ -111,7 +113,6 @@ #include "stdio.h" #include "string.h" -#include "devextras.h" #define YMALLOC(x) malloc(x) #define YFREE(x) free(x) @@ -153,6 +154,168 @@ #endif +#if defined(CONFIG_YAFFS_DIRECT) || defined(CONFIG_YAFFS_WINCE) + +#ifdef CONFIG_YAFFSFS_PROVIDE_VALUES + +#ifndef O_RDONLY +#define O_RDONLY 00 +#endif + +#ifndef O_WRONLY +#define O_WRONLY 01 +#endif + +#ifndef O_RDWR +#define O_RDWR 02 +#endif + +#ifndef O_CREAT +#define O_CREAT 0100 +#endif + +#ifndef O_EXCL +#define O_EXCL 0200 +#endif + +#ifndef O_TRUNC +#define O_TRUNC 01000 +#endif + +#ifndef O_APPEND +#define O_APPEND 02000 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef EBUSY +#define EBUSY 16 +#endif + +#ifndef ENODEV +#define ENODEV 19 +#endif + +#ifndef EINVAL +#define EINVAL 22 +#endif + +#ifndef EBADF +#define EBADF 9 +#endif + +#ifndef EACCES +#define EACCES 13 +#endif + +#ifndef EXDEV +#define EXDEV 18 +#endif + +#ifndef ENOENT +#define ENOENT 2 +#endif + +#ifndef ENOSPC +#define ENOSPC 28 +#endif + +#ifndef ERANGE +#define ERANGE 34 +#endif + +#ifndef ENODATA +#define ENODATA 61 +#endif + +#ifndef ENOTEMPTY +#define ENOTEMPTY 39 +#endif + +#ifndef ENAMETOOLONG +#define ENAMETOOLONG 36 +#endif + +#ifndef ENOMEM +#define ENOMEM 12 +#endif + +#ifndef EEXIST +#define EEXIST 17 +#endif + +#ifndef ENOTDIR +#define ENOTDIR 20 +#endif + +#ifndef EISDIR +#define EISDIR 21 +#endif + + +// Mode flags + +#ifndef S_IFMT +#define S_IFMT 0170000 +#endif + +#ifndef S_IFLNK +#define S_IFLNK 0120000 +#endif + +#ifndef S_IFDIR +#define S_IFDIR 0040000 +#endif + +#ifndef S_IFREG +#define S_IFREG 0100000 +#endif + +#ifndef S_IREAD +#define S_IREAD 0000400 +#endif + +#ifndef S_IWRITE +#define S_IWRITE 0000200 +#endif + +#ifndef S_IEXEC +#define S_IEXEC 0000100 +#endif + +#ifndef XATTR_CREATE +#define XATTR_CREATE 1 +#endif + +#ifndef XATTR_REPLACE +#define XATTR_REPLACE 2 +#endif + +#ifndef R_OK +#define R_OK 4 +#define W_OK 2 +#define X_OK 1 +#define F_OK 0 +#endif + +#else +#include <errno.h> +#include <sys/stat.h> +#include <fcntl.h> +#endif + +#endif + #ifndef Y_DUMP_STACK #define Y_DUMP_STACK() do { } while (0) #endif @@ -166,4 +329,5 @@ } while (0) #endif + #endif |