Signed-off-by: Gabor Juhos <juhosg@openwrt.org> SVN-Revision: 34013master
parent
56190ed0fc
commit
449f88df0f
@ -1,4 +1,4 @@ |
||||
The yaffs2 source has been fetched from the yaffs2 CVS tree. |
||||
The yaffs2 source has been fetched from the yaffs2 GIT tree. |
||||
|
||||
URL: cvs.aleph1.co.uk |
||||
Version: 2009-09-24 |
||||
URL: git://www.aleph1.co.uk/yaffs2 |
||||
Version: 7396445d7d0d13469b9505791114b9dc6b76ffe4 (2010-10-20) |
||||
|
@ -0,0 +1,409 @@ |
||||
/*
|
||||
* 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. |
||||
*/ |
||||
|
||||
|
||||
#include "yaffs_allocator.h" |
||||
#include "yaffs_guts.h" |
||||
#include "yaffs_trace.h" |
||||
#include "yportenv.h" |
||||
|
||||
#ifdef CONFIG_YAFFS_YMALLOC_ALLOCATOR |
||||
|
||||
void yaffs_deinit_raw_tnodes_and_objs(yaffs_dev_t *dev) |
||||
{ |
||||
dev = dev; |
||||
} |
||||
|
||||
void yaffs_init_raw_tnodes_and_objs(yaffs_dev_t *dev) |
||||
{ |
||||
dev = dev; |
||||
} |
||||
|
||||
yaffs_tnode_t *yaffs_alloc_raw_tnode(yaffs_dev_t *dev) |
||||
{ |
||||
return (yaffs_tnode_t *)YMALLOC(dev->tnode_size); |
||||
} |
||||
|
||||
void yaffs_free_raw_tnode(yaffs_dev_t *dev, yaffs_tnode_t *tn) |
||||
{ |
||||
dev = dev; |
||||
YFREE(tn); |
||||
} |
||||
|
||||
void yaffs_init_raw_objs(yaffs_dev_t *dev) |
||||
{ |
||||
dev = dev; |
||||
} |
||||
|
||||
void yaffs_deinit_raw_objs(yaffs_dev_t *dev) |
||||
{ |
||||
dev = dev; |
||||
} |
||||
|
||||
yaffs_obj_t *yaffs_alloc_raw_obj(yaffs_dev_t *dev) |
||||
{ |
||||
dev = dev; |
||||
return (yaffs_obj_t *) YMALLOC(sizeof(yaffs_obj_t)); |
||||
} |
||||
|
||||
|
||||
void yaffs_free_raw_obj(yaffs_dev_t *dev, yaffs_obj_t *obj) |
||||
{ |
||||
|
||||
dev = dev; |
||||
YFREE(obj); |
||||
} |
||||
|
||||
#else |
||||
|
||||
struct yaffs_tnode_list { |
||||
struct yaffs_tnode_list *next; |
||||
yaffs_tnode_t *tnodes; |
||||
}; |
||||
|
||||
typedef struct yaffs_tnode_list yaffs_tnodelist_t; |
||||
|
||||
struct yaffs_obj_tList_struct { |
||||
yaffs_obj_t *objects; |
||||
struct yaffs_obj_tList_struct *next; |
||||
}; |
||||
|
||||
typedef struct yaffs_obj_tList_struct yaffs_obj_tList; |
||||
|
||||
|
||||
struct yaffs_AllocatorStruct { |
||||
int n_tnodesCreated; |
||||
yaffs_tnode_t *freeTnodes; |
||||
int nFreeTnodes; |
||||
yaffs_tnodelist_t *allocatedTnodeList; |
||||
|
||||
int n_objCreated; |
||||
yaffs_obj_t *freeObjects; |
||||
int nFreeObjects; |
||||
|
||||
yaffs_obj_tList *allocatedObjectList; |
||||
}; |
||||
|
||||
typedef struct yaffs_AllocatorStruct yaffs_Allocator; |
||||
|
||||
|
||||
static void yaffs_deinit_raw_tnodes(yaffs_dev_t *dev) |
||||
{ |
||||
|
||||
yaffs_Allocator *allocator = (yaffs_Allocator *)dev->allocator; |
||||
|
||||
yaffs_tnodelist_t *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->n_tnodesCreated = 0; |
||||
} |
||||
|
||||
static void yaffs_init_raw_tnodes(yaffs_dev_t *dev) |
||||
{ |
||||
yaffs_Allocator *allocator = dev->allocator; |
||||
|
||||
if(allocator){ |
||||
allocator->allocatedTnodeList = NULL; |
||||
allocator->freeTnodes = NULL; |
||||
allocator->nFreeTnodes = 0; |
||||
allocator->n_tnodesCreated = 0; |
||||
} else |
||||
YBUG(); |
||||
} |
||||
|
||||
static int yaffs_create_tnodes(yaffs_dev_t *dev, int n_tnodes) |
||||
{ |
||||
yaffs_Allocator *allocator = (yaffs_Allocator *)dev->allocator; |
||||
int i; |
||||
yaffs_tnode_t *newTnodes; |
||||
__u8 *mem; |
||||
yaffs_tnode_t *curr; |
||||
yaffs_tnode_t *next; |
||||
yaffs_tnodelist_t *tnl; |
||||
|
||||
if(!allocator){ |
||||
YBUG(); |
||||
return YAFFS_FAIL; |
||||
} |
||||
|
||||
if (n_tnodes < 1) |
||||
return YAFFS_OK; |
||||
|
||||
|
||||
/* make these things */ |
||||
|
||||
newTnodes = YMALLOC(n_tnodes * dev->tnode_size); |
||||
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 < n_tnodes - 1; i++) { |
||||
curr = (yaffs_tnode_t *) &mem[i * dev->tnode_size]; |
||||
next = (yaffs_tnode_t *) &mem[(i+1) * dev->tnode_size]; |
||||
curr->internal[0] = next; |
||||
} |
||||
|
||||
curr = (yaffs_tnode_t *) &mem[(n_tnodes - 1) * dev->tnode_size]; |
||||
curr->internal[0] = allocator->freeTnodes; |
||||
allocator->freeTnodes = (yaffs_tnode_t *)mem; |
||||
|
||||
allocator->nFreeTnodes += n_tnodes; |
||||
allocator->n_tnodesCreated += n_tnodes; |
||||
|
||||
/* 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_t)); |
||||
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_t *yaffs_alloc_raw_tnode(yaffs_dev_t *dev) |
||||
{ |
||||
yaffs_Allocator *allocator = (yaffs_Allocator *)dev->allocator; |
||||
yaffs_tnode_t *tn = NULL; |
||||
|
||||
if(!allocator){ |
||||
YBUG(); |
||||
return NULL; |
||||
} |
||||
|
||||
/* If there are none left make more */ |
||||
if (!allocator->freeTnodes) |
||||
yaffs_create_tnodes(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_free_raw_tnode(yaffs_dev_t *dev, yaffs_tnode_t *tn) |
||||
{ |
||||
yaffs_Allocator *allocator = dev->allocator; |
||||
|
||||
if(!allocator){ |
||||
YBUG(); |
||||
return; |
||||
} |
||||
|
||||
if (tn) { |
||||
tn->internal[0] = allocator->freeTnodes; |
||||
allocator->freeTnodes = tn; |
||||
allocator->nFreeTnodes++; |
||||
} |
||||
dev->checkpoint_blocks_required = 0; /* force recalculation*/ |
||||
} |
||||
|
||||
|
||||
|
||||
static void yaffs_init_raw_objs(yaffs_dev_t *dev) |
||||
{ |
||||
yaffs_Allocator *allocator = dev->allocator; |
||||
|
||||
if(allocator) { |
||||
allocator->allocatedObjectList = NULL; |
||||
allocator->freeObjects = NULL; |
||||
allocator->nFreeObjects = 0; |
||||
} else |
||||
YBUG(); |
||||
} |
||||
|
||||
static void yaffs_deinit_raw_objs(yaffs_dev_t *dev) |
||||
{ |
||||
yaffs_Allocator *allocator = dev->allocator; |
||||
yaffs_obj_tList *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->n_objCreated = 0; |
||||
} |
||||
|
||||
|
||||
static int yaffs_create_free_objs(yaffs_dev_t *dev, int n_obj) |
||||
{ |
||||
yaffs_Allocator *allocator = dev->allocator; |
||||
|
||||
int i; |
||||
yaffs_obj_t *newObjects; |
||||
yaffs_obj_tList *list; |
||||
|
||||
if(!allocator){ |
||||
YBUG(); |
||||
return YAFFS_FAIL; |
||||
} |
||||
|
||||
if (n_obj < 1) |
||||
return YAFFS_OK; |
||||
|
||||
/* make these things */ |
||||
newObjects = YMALLOC(n_obj * sizeof(yaffs_obj_t)); |
||||
list = YMALLOC(sizeof(yaffs_obj_tList)); |
||||
|
||||
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 < n_obj - 1; i++) { |
||||
newObjects[i].siblings.next = |
||||
(struct ylist_head *)(&newObjects[i + 1]); |
||||
} |
||||
|
||||
newObjects[n_obj - 1].siblings.next = (void *)allocator->freeObjects; |
||||
allocator->freeObjects = newObjects; |
||||
allocator->nFreeObjects += n_obj; |
||||
allocator->n_objCreated += n_obj; |
||||
|
||||
/* 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_obj_t *yaffs_alloc_raw_obj(yaffs_dev_t *dev) |
||||
{ |
||||
yaffs_obj_t *obj = NULL; |
||||
yaffs_Allocator *allocator = dev->allocator; |
||||
|
||||
if(!allocator) { |
||||
YBUG(); |
||||
return obj; |
||||
} |
||||
|
||||
/* If there are none left make more */ |
||||
if (!allocator->freeObjects) |
||||
yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); |
||||
|
||||
if (allocator->freeObjects) { |
||||
obj = allocator->freeObjects; |
||||
allocator->freeObjects = |
||||
(yaffs_obj_t *) (allocator->freeObjects->siblings.next); |
||||
allocator->nFreeObjects--; |
||||
} |
||||
|
||||
return obj; |
||||
} |
||||
|
||||
|
||||
void yaffs_free_raw_obj(yaffs_dev_t *dev, yaffs_obj_t *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_deinit_raw_tnodes_and_objs(yaffs_dev_t *dev) |
||||
{ |
||||
if(dev->allocator){ |
||||
yaffs_deinit_raw_tnodes(dev); |
||||
yaffs_deinit_raw_objs(dev); |
||||
|
||||
YFREE(dev->allocator); |
||||
dev->allocator=NULL; |
||||
} else |
||||
YBUG(); |
||||
} |
||||
|
||||
void yaffs_init_raw_tnodes_and_objs(yaffs_dev_t *dev) |
||||
{ |
||||
yaffs_Allocator *allocator; |
||||
|
||||
if(!dev->allocator){ |
||||
allocator = YMALLOC(sizeof(yaffs_Allocator)); |
||||
if(allocator){ |
||||
dev->allocator = allocator; |
||||
yaffs_init_raw_tnodes(dev); |
||||
yaffs_init_raw_objs(dev); |
||||
} |
||||
} else |
||||
YBUG(); |
||||
} |
||||
|
||||
|
||||
#endif |
@ -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_init_raw_tnodes_and_objs(yaffs_dev_t *dev); |
||||
void yaffs_deinit_raw_tnodes_and_objs(yaffs_dev_t *dev); |
||||
|
||||
yaffs_tnode_t *yaffs_alloc_raw_tnode(yaffs_dev_t *dev); |
||||
void yaffs_free_raw_tnode(yaffs_dev_t *dev, yaffs_tnode_t *tn); |
||||
|
||||
yaffs_obj_t *yaffs_alloc_raw_obj(yaffs_dev_t *dev); |
||||
void yaffs_free_raw_obj(yaffs_dev_t *dev, yaffs_obj_t *obj); |
||||
|
||||
#endif |
@ -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_dev_t *dev, int blk) |
||||
{ |
||||
if (blk < dev->internal_start_block || blk > dev->internal_end_block) { |
||||
T(YAFFS_TRACE_ERROR, |
||||
(TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR), |
||||
blk)); |
||||
YBUG(); |
||||
} |
||||
return dev->chunk_bits + |
||||
(dev->chunk_bit_stride * (blk - dev->internal_start_block)); |
||||
} |
||||
|
||||
void yaffs_verify_chunk_bit_id(yaffs_dev_t *dev, int blk, int chunk) |
||||
{ |
||||
if (blk < dev->internal_start_block || blk > dev->internal_end_block || |
||||
chunk < 0 || chunk >= dev->param.chunks_per_block) { |
||||
T(YAFFS_TRACE_ERROR, |
||||
(TSTR("**>> yaffs: Chunk Id (%d:%d) invalid"TENDSTR), |
||||
blk, chunk)); |
||||
YBUG(); |
||||
} |
||||
} |
||||
|
||||
void yaffs_clear_chunk_bits(yaffs_dev_t *dev, int blk) |
||||
{ |
||||
__u8 *blkBits = yaffs_BlockBits(dev, blk); |
||||
|
||||
memset(blkBits, 0, dev->chunk_bit_stride); |
||||
} |
||||
|
||||
void yaffs_clear_chunk_bit(yaffs_dev_t *dev, int blk, int chunk) |
||||
{ |
||||
__u8 *blkBits = yaffs_BlockBits(dev, blk); |
||||
|
||||
yaffs_verify_chunk_bit_id(dev, blk, chunk); |
||||
|
||||
blkBits[chunk / 8] &= ~(1 << (chunk & 7)); |
||||
} |
||||
|
||||
void yaffs_set_chunk_bit(yaffs_dev_t *dev, int blk, int chunk) |
||||
{ |
||||
__u8 *blkBits = yaffs_BlockBits(dev, blk); |
||||
|
||||
yaffs_verify_chunk_bit_id(dev, blk, chunk); |
||||
|
||||
blkBits[chunk / 8] |= (1 << (chunk & 7)); |
||||
} |
||||
|
||||
int yaffs_check_chunk_bit(yaffs_dev_t *dev, int blk, int chunk) |
||||
{ |
||||
__u8 *blkBits = yaffs_BlockBits(dev, blk); |
||||
yaffs_verify_chunk_bit_id(dev, blk, chunk); |
||||
|
||||
return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; |
||||
} |
||||
|
||||
int yaffs_still_some_chunks(yaffs_dev_t *dev, int blk) |
||||
{ |
||||
__u8 *blkBits = yaffs_BlockBits(dev, blk); |
||||
int i; |
||||
for (i = 0; i < dev->chunk_bit_stride; i++) { |
||||
if (*blkBits) |
||||
return 1; |
||||
blkBits++; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int yaffs_count_chunk_bits(yaffs_dev_t *dev, int blk) |
||||
{ |
||||
__u8 *blkBits = yaffs_BlockBits(dev, blk); |
||||
int i; |
||||
int n = 0; |
||||
for (i = 0; i < dev->chunk_bit_stride; i++) { |
||||
__u8 x = *blkBits; |
||||
while (x) { |
||||
if (x & 1) |
||||
n++; |
||||
x >>= 1; |
||||
} |
||||
|
||||
blkBits++; |
||||
} |
||||
return n; |
||||
} |
||||
|
@ -0,0 +1,31 @@ |
||||
/*
|
||||
* 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. |
||||
*/ |
||||
|
||||
/*
|
||||
* Chunk bitmap manipulations |
||||
*/ |
||||
|
||||
#ifndef __YAFFS_BITMAP_H__ |
||||
#define __YAFFS_BITMAP_H__ |
||||
|
||||
#include "yaffs_guts.h" |
||||
|
||||
void yaffs_verify_chunk_bit_id(yaffs_dev_t *dev, int blk, int chunk); |
||||
void yaffs_clear_chunk_bits(yaffs_dev_t *dev, int blk); |
||||
void yaffs_clear_chunk_bit(yaffs_dev_t *dev, int blk, int chunk); |
||||
void yaffs_set_chunk_bit(yaffs_dev_t *dev, int blk, int chunk); |
||||
int yaffs_check_chunk_bit(yaffs_dev_t *dev, int blk, int chunk); |
||||
int yaffs_still_some_chunks(yaffs_dev_t *dev, int blk); |
||||
int yaffs_count_chunk_bits(yaffs_dev_t *dev, int blk); |
||||
|
||||
#endif |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,43 @@ |
||||
/*
|
||||
* 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_LINUX_H__ |
||||
#define __YAFFS_LINUX_H__ |
||||
|
||||
#include "devextras.h" |
||||
#include "yportenv.h" |
||||
|
||||
struct yaffs_LinuxContext { |
||||
struct ylist_head contextList; /* List of these we have mounted */ |
||||
struct yaffs_dev_s *dev; |
||||
struct super_block * superBlock; |
||||
struct task_struct *bgThread; /* Background thread for this device */ |
||||
int bgRunning; |
||||
struct semaphore grossLock; /* Gross locking semaphore */ |
||||
__u8 *spareBuffer; /* For mtdif2 use. Don't know the size of the buffer
|
||||
* at compile time so we have to allocate it. |
||||
*/ |
||||
struct ylist_head searchContexts; |
||||
void (*putSuperFunc)(struct super_block *sb); |
||||
|
||||
struct task_struct *readdirProcess; |
||||
unsigned mount_id; |
||||
}; |
||||
|
||||
#define yaffs_dev_to_lc(dev) ((struct yaffs_LinuxContext *)((dev)->os_context)) |
||||
#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) |
||||
|
||||
#endif |
||||
|
@ -0,0 +1,200 @@ |
||||
/*
|
||||
* 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_deinit_raw_tnodes_and_objs(yaffs_dev_t *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_init_raw_tnodes_and_objs(yaffs_dev_t *dev) |
||||
{ |
||||
yaffs_Allocator *allocator; |
||||
unsigned mount_id = yaffs_dev_to_lc(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->tnode_size, |
||||
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_obj_t), |
||||
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_t *yaffs_alloc_raw_tnode(yaffs_dev_t *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_free_raw_tnode(yaffs_dev_t *dev, yaffs_tnode_t *tn) |
||||
{ |
||||
yaffs_Allocator *allocator = dev->allocator; |
||||
kmem_cache_free(allocator->tnode_cache,tn); |
||||
} |
||||
|
||||
yaffs_obj_t *yaffs_alloc_raw_obj(yaffs_dev_t *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_free_raw_obj(yaffs_dev_t *dev, yaffs_obj_t *obj) |
||||
{ |
||||
yaffs_Allocator *allocator = dev->allocator; |
||||
kmem_cache_free(allocator->object_cache,obj); |
||||
} |
@ -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 |
@ -1,434 +0,0 @@ |
||||
From ian@brightstareng.com Fri May 18 15:06:49 2007 |
||||
From ian@brightstareng.com Fri May 18 15:08:21 2007 |
||||
Received: from 206.173.66.57.ptr.us.xo.net ([206.173.66.57] helo=zebra.brightstareng.com) |
||||
by apollo.linkchoose.co.uk with esmtp (Exim 4.60) |
||||
(envelope-from <ian@brightstareng.com>) |
||||
id 1Hp380-00011e-T6 |
||||
for david.goodenough@linkchoose.co.uk; Fri, 18 May 2007 15:08:21 +0100 |
||||
Received: from localhost (localhost.localdomain [127.0.0.1]) |
||||
by zebra.brightstareng.com (Postfix) with ESMTP |
||||
id 4819F28C004; Fri, 18 May 2007 10:07:49 -0400 (EDT) |
||||
Received: from zebra.brightstareng.com ([127.0.0.1]) |
||||
by localhost (zebra [127.0.0.1]) (amavisd-new, port 10024) with ESMTP |
||||
id 05328-06; Fri, 18 May 2007 10:07:16 -0400 (EDT) |
||||
Received: from pippin (unknown [192.168.1.25]) |
||||
by zebra.brightstareng.com (Postfix) with ESMTP |
||||
id 8BEF528C1BC; Fri, 18 May 2007 10:06:53 -0400 (EDT) |
||||
From: Ian McDonnell <ian@brightstareng.com> |
||||
To: David Goodenough <david.goodenough@linkchoose.co.uk> |
||||
Subject: Re: something tested this time -- yaffs_mtdif1-compat.c |
||||
Date: Fri, 18 May 2007 10:06:49 -0400 |
||||
User-Agent: KMail/1.9.1 |
||||
References: <200705142207.06909.ian@brightstareng.com> <200705171131.53536.ian@brightstareng.com> <200705181334.32166.david.goodenough@linkchoose.co.uk> |
||||
In-Reply-To: <200705181334.32166.david.goodenough@linkchoose.co.uk> |
||||
Cc: Andrea Conti <alyf@alyf.net>, |
||||
Charles Manning <manningc2@actrix.gen.nz> |
||||
MIME-Version: 1.0 |
||||
Content-Type: Multipart/Mixed; |
||||
boundary="Boundary-00=_5LbTGmt62YoutxM" |
||||
Message-Id: <200705181006.49860.ian@brightstareng.com> |
||||
X-Virus-Scanned: by amavisd-new at brightstareng.com |
||||
Status: R |
||||
X-Status: NT |
||||
X-KMail-EncryptionState: |
||||
X-KMail-SignatureState: |
||||
X-KMail-MDN-Sent: |
||||
|
||||
--Boundary-00=_5LbTGmt62YoutxM |
||||
Content-Type: text/plain; |
||||
charset="iso-8859-15" |
||||
Content-Transfer-Encoding: 7bit |
||||
Content-Disposition: inline |
||||
|
||||
David, Andrea, |
||||
|
||||
On Friday 18 May 2007 08:34, you wrote: |
||||
> Yea team. With this fix in place (I put it in the wrong place |
||||
> at first) I can now mount and ls the Yaffs partition without |
||||
> an error messages! |
||||
|
||||
Good news! |
||||
|
||||
Attached is a newer yaffs_mtdif1.c with a bandaid to help the |
||||
2.6.18 and 2.6.19 versions of MTD not trip on the oob read. |
||||
See the LINUX_VERSION_CODE conditional in |
||||
nandmtd1_ReadChunkWithTagsFromNAND. |
||||
|
||||
-imcd |
||||
|
||||
--Boundary-00=_5LbTGmt62YoutxM |
||||
Content-Type: text/x-csrc; |
||||
charset="iso-8859-15"; |
||||
name="yaffs_mtdif1.c" |
||||
Content-Transfer-Encoding: 7bit |
||||
Content-Disposition: attachment; |
||||
filename="yaffs_mtdif1.c" |
||||
|
||||
/*
|
||||
* YAFFS: Yet another FFS. A NAND-flash specific file system. |
||||
* yaffs_mtdif1.c NAND mtd interface functions for small-page NAND. |
||||
* |
||||
* Copyright (C) 2002 Aleph One Ltd. |
||||
* for Toby Churchill Ltd and Brightstar Engineering |
||||
* |
||||
* 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 module provides the interface between yaffs_nand.c and the |
||||
* MTD API. This version is used when the MTD interface supports the |
||||
* 'mtd_oob_ops' style calls to read_oob and write_oob, circa 2.6.17, |
||||
* and we have small-page NAND device. |
||||
* |
||||
* These functions are invoked via function pointers in yaffs_nand.c. |
||||
* This replaces functionality provided by functions in yaffs_mtdif.c |
||||
* and the yaffs_TagsCompatability functions in yaffs_tagscompat.c that are |
||||
* called in yaffs_mtdif.c when the function pointers are NULL. |
||||
* We assume the MTD layer is performing ECC (useNANDECC is true). |
||||
*/ |
||||
|
||||
#include "yportenv.h" |
||||
#include "yaffs_guts.h" |
||||
#include "yaffs_packedtags1.h" |
||||
#include "yaffs_tagscompat.h" // for yaffs_CalcTagsECC |
||||
|
||||
#include "linux/kernel.h" |
||||
#include "linux/version.h" |
||||
#include "linux/types.h" |
||||
#include "linux/mtd/mtd.h" |
||||
|
||||
/* Don't compile this module if we don't have MTD's mtd_oob_ops interface */ |
||||
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) |
||||
|
||||
const char *yaffs_mtdif1_c_version = "$Id$"; |
||||
|
||||
#ifndef CONFIG_YAFFS_9BYTE_TAGS |
||||
# define YTAG1_SIZE 8 |
||||
#else |
||||
# define YTAG1_SIZE 9 |
||||
#endif |
||||
|
||||
#if 0 |
||||
/* Use the following nand_ecclayout with MTD when using
|
||||
* CONFIG_YAFFS_9BYTE_TAGS and the older on-NAND tags layout. |
||||
* If you have existing Yaffs images and the byte order differs from this, |
||||
* adjust 'oobfree' to match your existing Yaffs data. |
||||
* |
||||
* This nand_ecclayout scatters/gathers to/from the old-yaffs layout with the |
||||
* pageStatus byte (at NAND spare offset 4) scattered/gathered from/to |
||||
* the 9th byte. |
||||
* |
||||
* Old-style on-NAND format: T0,T1,T2,T3,P,B,T4,T5,E0,E1,E2,T6,T7,E3,E4,E5 |
||||
* We have/need PackedTags1 plus pageStatus: T0,T1,T2,T3,T4,T5,T6,T7,P |
||||
* where Tn are the tag bytes, En are MTD's ECC bytes, P is the pageStatus |
||||
* byte and B is the small-page bad-block indicator byte. |
||||
*/ |
||||
static struct nand_ecclayout nand_oob_16 = { |
||||
.eccbytes = 6, |
||||
.eccpos = { 8, 9, 10, 13, 14, 15 }, |
||||
.oobavail = 9, |
||||
.oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } |
||||
}; |
||||
#endif |
||||
|
||||
/* Write a chunk (page) of data to NAND.
|
||||
* |
||||
* Caller always provides ExtendedTags data which are converted to a more |
||||
* compact (packed) form for storage in NAND. A mini-ECC runs over the |
||||
* contents of the tags meta-data; used to valid the tags when read. |
||||
* |
||||
* - Pack ExtendedTags to PackedTags1 form |
||||
* - Compute mini-ECC for PackedTags1 |
||||
* - Write data and packed tags to NAND. |
||||
* |
||||
* Note: Due to the use of the PackedTags1 meta-data which does not include |
||||
* a full sequence number (as found in the larger PackedTags2 form) it is |
||||
* necessary for Yaffs to re-write a chunk/page (just once) to mark it as |
||||
* discarded and dirty. This is not ideal: newer NAND parts are supposed |
||||
* to be written just once. When Yaffs performs this operation, this |
||||
* function is called with a NULL data pointer -- calling MTD write_oob |
||||
* without data is valid usage (2.6.17). |
||||
* |
||||
* Any underlying MTD error results in YAFFS_FAIL. |
||||
* Returns YAFFS_OK or YAFFS_FAIL. |
||||
*/ |
||||
int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device *dev, |
||||
int chunkInNAND, const __u8 * data, const yaffs_ExtendedTags * etags) |
||||
{ |
||||
struct mtd_info * mtd = dev->genericDevice; |
||||
int chunkBytes = dev->nDataBytesPerChunk; |
||||
loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; |
||||
struct mtd_oob_ops ops; |
||||
yaffs_PackedTags1 pt1; |
||||
int retval; |
||||
|
||||
/* we assume that PackedTags1 and yaffs_Tags are compatible */ |
||||
compile_time_assertion(sizeof(yaffs_PackedTags1) == 12); |
||||
compile_time_assertion(sizeof(yaffs_Tags) == 8); |
||||
|
||||
yaffs_PackTags1(&pt1, etags); |
||||
yaffs_CalcTagsECC((yaffs_Tags *)&pt1); |
||||
|
||||
/* When deleting a chunk, the upper layer provides only skeletal
|
||||
* etags, one with chunkDeleted set. However, we need to update the |
||||
* tags, not erase them completely. So we use the NAND write property |
||||
* that only zeroed-bits stick and set tag bytes to all-ones and |
||||
* zero just the (not) deleted bit. |
||||
*/ |
||||
#ifndef CONFIG_YAFFS_9BYTE_TAGS |
||||
if (etags->chunkDeleted) { |
||||
memset(&pt1, 0xff, 8); |
||||
/* clear delete status bit to indicate deleted */ |
||||
pt1.deleted = 0; |
||||
} |
||||
#else |
||||
((__u8 *)&pt1)[8] = 0xff; |
||||
if (etags->chunkDeleted) { |
||||
memset(&pt1, 0xff, 8); |
||||
/* zero pageStatus byte to indicate deleted */ |
||||
((__u8 *)&pt1)[8] = 0; |
||||
} |
||||
#endif |
||||
|
||||
memset(&ops, 0, sizeof(ops)); |
||||
ops.mode = MTD_OOB_AUTO; |
||||
ops.len = (data) ? chunkBytes : 0; |
||||
ops.ooblen = YTAG1_SIZE; |
||||
ops.datbuf = (__u8 *)data; |
||||
ops.oobbuf = (__u8 *)&pt1; |
||||
|
||||
retval = mtd->write_oob(mtd, addr, &ops); |
||||
if (retval) { |
||||
yaffs_trace(YAFFS_TRACE_MTD, |
||||
"write_oob failed, chunk %d, mtd error %d\n", |
||||
chunkInNAND, retval); |
||||
} |
||||
return retval ? YAFFS_FAIL : YAFFS_OK; |
||||
} |
||||
|
||||
/* Return with empty ExtendedTags but add eccResult.
|
||||
*/ |
||||
static int rettags(yaffs_ExtendedTags * etags, int eccResult, int retval) |
||||
{ |
||||
if (etags) { |
||||
memset(etags, 0, sizeof(*etags)); |
||||
etags->eccResult = eccResult; |
||||
} |
||||
return retval; |
||||
} |
||||
|
||||
/* Read a chunk (page) from NAND.
|
||||
* |
||||
* Caller expects ExtendedTags data to be usable even on error; that is, |
||||
* all members except eccResult and blockBad are zeroed. |
||||
* |
||||
* - Check ECC results for data (if applicable) |
||||
* - Check for blank/erased block (return empty ExtendedTags if blank) |
||||
* - Check the PackedTags1 mini-ECC (correct if necessary/possible) |
||||
* - Convert PackedTags1 to ExtendedTags |
||||
* - Update eccResult and blockBad members to refect state. |
||||
* |
||||
* Returns YAFFS_OK or YAFFS_FAIL. |
||||
*/ |
||||
int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device *dev, |
||||
int chunkInNAND, __u8 * data, yaffs_ExtendedTags * etags) |
||||
{ |
||||
struct mtd_info * mtd = dev->genericDevice; |
||||
int chunkBytes = dev->nDataBytesPerChunk; |
||||
loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; |
||||
int eccres = YAFFS_ECC_RESULT_NO_ERROR; |
||||
struct mtd_oob_ops ops; |
||||
yaffs_PackedTags1 pt1; |
||||
int retval; |
||||
int deleted; |
||||
|
||||
memset(&ops, 0, sizeof(ops)); |
||||
ops.mode = MTD_OOB_AUTO; |
||||
ops.len = (data) ? chunkBytes : 0; |
||||
ops.ooblen = YTAG1_SIZE; |
||||
ops.datbuf = data; |
||||
ops.oobbuf = (__u8 *)&pt1; |
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) |
||||
/* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;
|
||||
* help it out with ops.len = ops.ooblen when ops.datbuf == NULL. |
||||
*/ |
||||
ops.len = (ops.datbuf) ? ops.len : ops.ooblen; |
||||
#endif |
||||
/* Read page and oob using MTD.
|
||||
* Check status and determine ECC result. |
||||
*/ |
||||
retval = mtd->read_oob(mtd, addr, &ops); |
||||
if (retval) { |
||||
yaffs_trace(YAFFS_TRACE_MTD, |
||||
"read_oob failed, chunk %d, mtd error %d\n", |
||||
chunkInNAND, retval); |
||||
} |
||||
|
||||
switch (retval) { |
||||
case 0: |
||||
/* no error */ |
||||
break; |
||||
|
||||
case -EUCLEAN: |
||||
/* MTD's ECC fixed the data */ |
||||
eccres = YAFFS_ECC_RESULT_FIXED; |
||||
dev->eccFixed++; |
||||
break; |
||||
|
||||
case -EBADMSG: |
||||
/* MTD's ECC could not fix the data */ |
||||
dev->eccUnfixed++; |
||||
/* fall into... */ |
||||
default: |
||||
rettags(etags, YAFFS_ECC_RESULT_UNFIXED, 0); |
||||
etags->blockBad = (mtd->block_isbad)(mtd, addr); |
||||
return YAFFS_FAIL; |
||||
} |
||||
|
||||
/* Check for a blank/erased chunk.
|
||||
*/ |
||||
if (yaffs_CheckFF((__u8 *)&pt1, 8)) { |
||||
/* when blank, upper layers want eccResult to be <= NO_ERROR */ |
||||
return rettags(etags, YAFFS_ECC_RESULT_NO_ERROR, YAFFS_OK); |
||||
} |
||||
|
||||
#ifndef CONFIG_YAFFS_9BYTE_TAGS |
||||
/* Read deleted status (bit) then return it to it's non-deleted
|
||||
* state before performing tags mini-ECC check. pt1.deleted is |
||||
* inverted. |
||||
*/ |
||||
deleted = !pt1.deleted; |
||||
pt1.deleted = 1; |
||||
#else |
||||
(void) deleted; /* not used */ |
||||
#endif |
||||
|
||||
/* Check the packed tags mini-ECC and correct if necessary/possible.
|
||||
*/ |
||||
retval = yaffs_CheckECCOnTags((yaffs_Tags *)&pt1); |
||||
switch (retval) { |
||||
case 0: |
||||
/* no tags error, use MTD result */ |
||||
break; |
||||
case 1: |
||||
/* recovered tags-ECC error */ |
||||
dev->tagsEccFixed++; |
||||
eccres = YAFFS_ECC_RESULT_FIXED; |
||||
break; |
||||
default: |
||||
/* unrecovered tags-ECC error */ |
||||
dev->tagsEccUnfixed++; |
||||
return rettags(etags, YAFFS_ECC_RESULT_UNFIXED, YAFFS_FAIL); |
||||
} |
||||
|
||||
/* Unpack the tags to extended form and set ECC result.
|
||||
* [set shouldBeFF just to keep yaffs_UnpackTags1 happy] |
||||
*/ |
||||
pt1.shouldBeFF = 0xFFFFFFFF; |
||||
yaffs_UnpackTags1(etags, &pt1); |
||||
etags->eccResult = eccres; |
||||
|
||||
/* Set deleted state.
|
||||
*/ |
||||
#ifndef CONFIG_YAFFS_9BYTE_TAGS |
||||
etags->chunkDeleted = deleted; |
||||
#else |
||||
etags->chunkDeleted = (yaffs_CountBits(((__u8 *)&pt1)[8]) < 7); |
||||
#endif |
||||
return YAFFS_OK; |
||||
} |
||||
|
||||
/* Mark a block bad.
|
||||
* |
||||
* This is a persistant state. |
||||
* Use of this function should be rare. |
||||
* |
||||
* Returns YAFFS_OK or YAFFS_FAIL. |
||||
*/ |
||||
int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) |
||||
{ |
||||
struct mtd_info * mtd = dev->genericDevice; |
||||
int blocksize = dev->nChunksPerBlock * dev->nDataBytesPerChunk; |
||||
int retval; |
||||
|
||||
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", blockNo); |
||||
|
||||
retval = mtd->block_markbad(mtd, (loff_t)blocksize * blockNo); |
||||
return (retval) ? YAFFS_FAIL : YAFFS_OK; |
||||
} |
||||
|
||||
/* Check any MTD prerequists.
|
||||
* |
||||
* Returns YAFFS_OK or YAFFS_FAIL. |
||||
*/ |
||||
static int nandmtd1_TestPrerequists(struct mtd_info * mtd) |
||||
{ |
||||
/* 2.6.18 has mtd->ecclayout->oobavail */ |
||||
/* 2.6.21 has mtd->ecclayout->oobavail and mtd->oobavail */ |
||||
int oobavail = mtd->ecclayout->oobavail; |
||||
|
||||
if (oobavail < YTAG1_SIZE) { |
||||
yaffs_trace(YAFFS_TRACE_ERROR, |
||||
"mtd device has only %d bytes for tags, need %d", |
||||
oobavail, YTAG1_SIZE); |
||||
return YAFFS_FAIL; |
||||
} |
||||
return YAFFS_OK; |
||||
} |
||||
|
||||
/* Query for the current state of a specific block.
|
||||
* |
||||
* Examine the tags of the first chunk of the block and return the state: |
||||
* - YAFFS_BLOCK_STATE_DEAD, the block is marked bad |
||||
* - YAFFS_BLOCK_STATE_NEEDS_SCANNING, the block is in use |
||||
* - YAFFS_BLOCK_STATE_EMPTY, the block is clean |
||||
* |
||||
* Always returns YAFFS_OK. |
||||
*/ |
||||
int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, |
||||
yaffs_BlockState * pState, int *pSequenceNumber) |
||||
{ |
||||
struct mtd_info * mtd = dev->genericDevice; |
||||
int chunkNo = blockNo * dev->nChunksPerBlock; |
||||
yaffs_ExtendedTags etags; |
||||
int state = YAFFS_BLOCK_STATE_DEAD; |
||||
int seqnum = 0; |
||||
int retval; |
||||
|
||||
/* We don't yet have a good place to test for MTD config prerequists.
|
||||
* Do it here as we are called during the initial scan. |
||||
*/ |
||||
if (nandmtd1_TestPrerequists(mtd) != YAFFS_OK) { |
||||
return YAFFS_FAIL; |
||||
} |
||||
|
||||
retval = nandmtd1_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &etags); |
||||
if (etags.blockBad) { |
||||
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, |
||||
"block %d is marked bad", blockNo); |
||||
state = YAFFS_BLOCK_STATE_DEAD; |
||||
} |
||||
else if (etags.chunkUsed) { |
||||
state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; |
||||
seqnum = etags.sequenceNumber; |
||||
} |
||||
else { |
||||
state = YAFFS_BLOCK_STATE_EMPTY; |
||||
} |
||||
|
||||
*pState = state; |
||||
*pSequenceNumber = seqnum; |
||||
|
||||
/* query always succeeds */ |
||||
return YAFFS_OK; |
||||
} |
||||
|
||||
#endif /*KERNEL_VERSION*/ |
||||
|
||||
--Boundary-00=_5LbTGmt62YoutxM-- |
||||
|
||||
|
||||
|
@ -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; |
||||
} |
@ -0,0 +1,25 @@ |
||||
/*
|
||||
* 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 |
@ -0,0 +1,60 @@ |
||||
/*
|
||||
* 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 __YTRACE_H__ |
||||
#define __YTRACE_H__ |
||||
|
||||
extern unsigned int yaffs_trace_mask; |
||||
extern unsigned int yaffs_wr_attempts; |
||||
|
||||
/*
|
||||
* Tracing flags. |
||||
* The flags masked in YAFFS_TRACE_ALWAYS are always traced. |
||||
*/ |
||||
|
||||
#define YAFFS_TRACE_OS 0x00000002 |
||||
#define YAFFS_TRACE_ALLOCATE 0x00000004 |
||||
#define YAFFS_TRACE_SCAN 0x00000008 |
||||
#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 |
||||
#define YAFFS_TRACE_ERASE 0x00000020 |
||||
#define YAFFS_TRACE_GC 0x00000040 |
||||
#define YAFFS_TRACE_WRITE 0x00000080 |
||||
#define YAFFS_TRACE_TRACING 0x00000100 |
||||
#define YAFFS_TRACE_DELETION 0x00000200 |
||||
#define YAFFS_TRACE_BUFFERS 0x00000400 |
||||
#define YAFFS_TRACE_NANDACCESS 0x00000800 |
||||
#define YAFFS_TRACE_GC_DETAIL 0x00001000 |
||||
#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 |
||||
#define YAFFS_TRACE_MTD 0x00004000 |
||||
#define YAFFS_TRACE_CHECKPOINT 0x00008000 |
||||
|
||||
#define YAFFS_TRACE_VERIFY 0x00010000 |
||||
#define YAFFS_TRACE_VERIFY_NAND 0x00020000 |
||||
#define YAFFS_TRACE_VERIFY_FULL 0x00040000 |
||||
#define YAFFS_TRACE_VERIFY_ALL 0x000F0000 |
||||
|
||||
#define YAFFS_TRACE_SYNC 0x00100000 |
||||
#define YAFFS_TRACE_BACKGROUND 0x00200000 |
||||
#define YAFFS_TRACE_LOCK 0x00400000 |
||||
|
||||
#define YAFFS_TRACE_ERROR 0x40000000 |
||||
#define YAFFS_TRACE_BUG 0x80000000 |
||||
#define YAFFS_TRACE_ALWAYS 0xF0000000 |
||||
|
||||
|
||||
#define T(mask, p) do { if ((mask) & (yaffs_trace_mask | YAFFS_TRACE_ALWAYS)) TOUT(p); } while (0) |
||||
|
||||
#endif |
@ -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_skip_verification(yaffs_dev_t *dev) |
||||
{ |
||||
dev=dev; |
||||
return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); |
||||
} |
||||
|
||||
static int yaffs_skip_full_verification(yaffs_dev_t *dev) |
||||
{ |
||||
dev=dev; |
||||
return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); |
||||
} |
||||
|
||||
static int yaffs_skip_nand_verification(yaffs_dev_t *dev) |
||||
{ |
||||
dev=dev; |
||||
return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); |
||||
} |
||||
|
||||
|
||||
static const char *block_stateName[] = { |
||||
"Unknown", |
||||
"Needs scanning", |
||||
"Scanning", |
||||
"Empty", |
||||
"Allocating", |
||||
"Full", |
||||
"Dirty", |
||||
"Checkpoint", |
||||
"Collecting", |
||||
"Dead" |
||||
}; |
||||
|
||||
|
||||
void yaffs_verify_blk(yaffs_dev_t *dev, yaffs_block_info_t *bi, int n) |
||||
{ |
||||
int actuallyUsed; |
||||
int inUse; |
||||
|
||||
if (yaffs_skip_verification(dev)) |
||||
return; |
||||
|
||||
/* Report illegal runtime states */ |
||||
if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) |
||||
T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has undefined state %d"TENDSTR), n, bi->block_state)); |
||||
|
||||
switch (bi->block_state) { |
||||
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, block_stateName[bi->block_state])); |
||||
} |
||||
|
||||
/* Check pages in use and soft deletions are legal */ |
||||
|
||||
actuallyUsed = bi->pages_in_use - bi->soft_del_pages; |
||||
|
||||
if (bi->pages_in_use < 0 || bi->pages_in_use > dev->param.chunks_per_block || |
||||
bi->soft_del_pages < 0 || bi->soft_del_pages > dev->param.chunks_per_block || |
||||
actuallyUsed < 0 || actuallyUsed > dev->param.chunks_per_block) |
||||
T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has illegal values pages_in_used %d soft_del_pages %d"TENDSTR), |
||||
n, bi->pages_in_use, bi->soft_del_pages)); |
||||
|
||||
|
||||
/* Check chunk bitmap legal */ |
||||
inUse = yaffs_count_chunk_bits(dev, n); |
||||
if (inUse != bi->pages_in_use) |
||||
T(YAFFS_TRACE_VERIFY, (TSTR("Block %d has inconsistent values pages_in_use %d counted chunk bits %d"TENDSTR), |
||||
n, bi->pages_in_use, inUse)); |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
void yaffs_verify_collected_blk(yaffs_dev_t *dev, yaffs_block_info_t *bi, int n) |
||||
{ |
||||
yaffs_verify_blk(dev, bi, n); |
||||
|
||||
/* After collection the block should be in the erased state */ |
||||
|
||||
if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && |
||||
bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { |
||||
T(YAFFS_TRACE_ERROR, (TSTR("Block %d is in state %d after gc, should be erased"TENDSTR), |
||||
n, bi->block_state)); |
||||
} |
||||
} |
||||
|
||||
void yaffs_verify_blocks(yaffs_dev_t *dev) |
||||
{ |
||||
int i; |
||||
int nBlocksPerState[YAFFS_NUMBER_OF_BLOCK_STATES]; |
||||
int nIllegalBlockStates = 0; |
||||
|
||||
if (yaffs_skip_verification(dev)) |
||||
return; |
||||
|
||||
memset(nBlocksPerState, 0, sizeof(nBlocksPerState)); |
||||
|
||||
for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { |
||||
yaffs_block_info_t *bi = yaffs_get_block_info(dev, i); |
||||
yaffs_verify_blk(dev, bi, i); |
||||
|
||||
if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) |
||||
nBlocksPerState[bi->block_state]++; |
||||
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), |
||||
block_stateName[i], nBlocksPerState[i])); |
||||
|
||||
if (dev->blocks_in_checkpt != nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT]) |
||||
T(YAFFS_TRACE_VERIFY, |
||||
(TSTR("Checkpoint block count wrong dev %d count %d"TENDSTR), |
||||
dev->blocks_in_checkpt, nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT])); |
||||
|
||||
if (dev->n_erased_blocks != nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY]) |
||||
T(YAFFS_TRACE_VERIFY, |
||||
(TSTR("Erased block count wrong dev %d count %d"TENDSTR), |
||||
dev->n_erased_blocks, 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_verify_oh(yaffs_obj_t *obj, yaffs_obj_header *oh, yaffs_ext_tags *tags, int parentCheck) |
||||
{ |
||||
if (obj && yaffs_skip_verification(obj->my_dev)) |
||||
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->obj_id, oh->type)); |
||||
|
||||
if (tags->obj_id != obj->obj_id) |
||||
T(YAFFS_TRACE_VERIFY, |
||||
(TSTR("Obj %d header mismatch obj_id %d"TENDSTR), |
||||
tags->obj_id, obj->obj_id)); |
||||
|
||||
|
||||
/*
|
||||
* Check that the object's parent ids match if parentCheck requested. |
||||
* |
||||
* Tests do not apply to the root object. |
||||
*/ |
||||
|
||||
if (parentCheck && tags->obj_id > 1 && !obj->parent) |
||||
T(YAFFS_TRACE_VERIFY, |
||||
(TSTR("Obj %d header mismatch parent_id %d obj->parent is NULL"TENDSTR), |
||||
tags->obj_id, oh->parent_obj_id)); |
||||
|
||||
if (parentCheck && obj->parent && |
||||
oh->parent_obj_id != obj->parent->obj_id && |
||||
(oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || |
||||
obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) |
||||
T(YAFFS_TRACE_VERIFY, |
||||
(TSTR("Obj %d header mismatch parent_id %d parent_obj_id %d"TENDSTR), |
||||
tags->obj_id, oh->parent_obj_id, obj->parent->obj_id)); |
||||
|
||||
if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ |
||||
T(YAFFS_TRACE_VERIFY, |
||||
(TSTR("Obj %d header name is NULL"TENDSTR), |
||||
obj->obj_id)); |
||||
|
||||
if (tags->obj_id > 1 && ((__u8)(oh->name[0])) == 0xff) /* Trashed name */ |
||||
T(YAFFS_TRACE_VERIFY, |
||||
(TSTR("Obj %d header name is 0xFF"TENDSTR), |
||||
obj->obj_id)); |
||||
} |
||||
|
||||
|
||||
#if 0 |
||||
/* Not being used, but don't want to throw away yet */ |
||||
int yaffs_verify_tnode_worker(yaffs_obj_t *obj, yaffs_tnode_t *tn, |
||||
__u32 level, int chunk_offset) |
||||
{ |
||||
int i; |
||||
yaffs_dev_t *dev = obj->my_dev; |
||||
int ok = 1; |
||||
|
||||
if (tn) { |
||||
if (level > 0) { |
||||
|
||||
for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { |
||||
if (tn->internal[i]) { |
||||
ok = yaffs_verify_tnode_worker(obj, |
||||
tn->internal[i], |
||||
level - 1, |
||||
(chunk_offset<<YAFFS_TNODES_INTERNAL_BITS) + i); |
||||
} |
||||
} |
||||
} else if (level == 0) { |
||||
yaffs_ext_tags tags; |
||||
__u32 obj_id = obj->obj_id; |
||||
|
||||
chunk_offset <<= YAFFS_TNODES_LEVEL0_BITS; |
||||
|
||||
for (i = 0; i < YAFFS_NTNODES_LEVEL0; i++) { |
||||
__u32 theChunk = yaffs_get_group_base(dev, tn, i); |
||||
|
||||
if (theChunk > 0) { |
||||
/* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),tags.obj_id,tags.chunk_id,theChunk)); */ |
||||
yaffs_rd_chunk_tags_nand(dev, theChunk, NULL, &tags); |
||||
if (tags.obj_id != obj_id || tags.chunk_id != chunk_offset) { |
||||
T(~0, (TSTR("Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), |
||||
obj_id, chunk_offset, theChunk, |
||||
tags.obj_id, tags.chunk_id)); |
||||
} |
||||
} |
||||
chunk_offset++; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return ok; |
||||
|
||||
} |
||||
|
||||
#endif |
||||
|
||||
void yaffs_verify_file(yaffs_obj_t *obj) |
||||
{ |
||||
int requiredTallness; |
||||
int actualTallness; |
||||
__u32 lastChunk; |
||||
__u32 x; |
||||
__u32 i; |
||||
yaffs_dev_t *dev; |
||||
yaffs_ext_tags tags; |
||||
yaffs_tnode_t *tn; |
||||
__u32 obj_id; |
||||
|
||||
if (!obj) |
||||
return; |
||||
|
||||
if (yaffs_skip_verification(obj->my_dev)) |
||||
return; |
||||
|
||||
dev = obj->my_dev; |
||||
obj_id = obj->obj_id; |
||||
|
||||
/* Check file size is consistent with tnode depth */ |
||||
lastChunk = obj->variant.file_variant.file_size / dev->data_bytes_per_chunk + 1; |
||||
x = lastChunk >> YAFFS_TNODES_LEVEL0_BITS; |
||||
requiredTallness = 0; |
||||
while (x > 0) { |
||||
x >>= YAFFS_TNODES_INTERNAL_BITS; |
||||
requiredTallness++; |
||||
} |
||||
|
||||
actualTallness = obj->variant.file_variant.top_level; |
||||
|
||||
/* 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_skip_nand_verification(dev)) |
||||
return; |
||||
|
||||
for (i = 1; i <= lastChunk; i++) { |
||||
tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); |
||||
|
||||
if (tn) { |
||||
__u32 theChunk = yaffs_get_group_base(dev, tn, i); |
||||
if (theChunk > 0) { |
||||
/* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),obj_id,i,theChunk)); */ |
||||
yaffs_rd_chunk_tags_nand(dev, theChunk, NULL, &tags); |
||||
if (tags.obj_id != obj_id || tags.chunk_id != i) { |
||||
T(~0, (TSTR("Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), |
||||
obj_id, i, theChunk, |
||||
tags.obj_id, tags.chunk_id)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
void yaffs_verify_link(yaffs_obj_t *obj) |
||||
{ |
||||
if (obj && yaffs_skip_verification(obj->my_dev)) |
||||
return; |
||||
|
||||
/* Verify sane equivalent object */ |
||||
} |
||||
|
||||
void yaffs_verify_symlink(yaffs_obj_t *obj) |
||||
{ |
||||
if (obj && yaffs_skip_verification(obj->my_dev)) |
||||
return; |
||||
|
||||
/* Verify symlink string */ |
||||
} |
||||
|
||||
void yaffs_verify_special(yaffs_obj_t *obj) |
||||
{ |
||||
if (obj && yaffs_skip_verification(obj->my_dev)) |
||||
return; |
||||
} |
||||
|
||||
void yaffs_verify_obj(yaffs_obj_t *obj) |
||||
{ |
||||
yaffs_dev_t *dev; |
||||
|
||||
__u32 chunkMin; |
||||
__u32 chunkMax; |
||||
|
||||
__u32 chunk_idOk; |
||||
__u32 chunkInRange; |
||||
__u32 chunkShouldNotBeDeleted; |
||||
__u32 chunkValid; |
||||
|
||||
if (!obj) |
||||
return; |
||||
|
||||
if (obj->being_created) |
||||
return; |
||||
|
||||
dev = obj->my_dev; |
||||
|
||||
if (yaffs_skip_verification(dev)) |
||||
return; |
||||
|
||||
/* Check sane object header chunk */ |
||||
|
||||
chunkMin = dev->internal_start_block * dev->param.chunks_per_block; |
||||
chunkMax = (dev->internal_end_block+1) * dev->param.chunks_per_block - 1; |
||||
|
||||
chunkInRange = (((unsigned)(obj->hdr_chunk)) >= chunkMin && ((unsigned)(obj->hdr_chunk)) <= chunkMax); |
||||
chunk_idOk = chunkInRange || (obj->hdr_chunk == 0); |
||||
chunkValid = chunkInRange && |
||||
yaffs_check_chunk_bit(dev, |
||||
obj->hdr_chunk / dev->param.chunks_per_block, |
||||
obj->hdr_chunk % dev->param.chunks_per_block); |
||||
chunkShouldNotBeDeleted = chunkInRange && !chunkValid; |
||||
|
||||
if (!obj->fake && |
||||
(!chunk_idOk || chunkShouldNotBeDeleted)) { |
||||
T(YAFFS_TRACE_VERIFY, |
||||
(TSTR("Obj %d has chunk_id %d %s %s"TENDSTR), |
||||
obj->obj_id, obj->hdr_chunk, |
||||
chunk_idOk ? "" : ",out of range", |
||||
chunkShouldNotBeDeleted ? ",marked as deleted" : "")); |
||||
} |
||||
|
||||
if (chunkValid && !yaffs_skip_nand_verification(dev)) { |
||||
yaffs_ext_tags tags; |
||||
yaffs_obj_header *oh; |
||||
__u8 *buffer = yaffs_get_temp_buffer(dev, __LINE__); |
||||
|
||||
oh = (yaffs_obj_header *)buffer; |
||||
|
||||
yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, |
||||
&tags); |
||||
|
||||
yaffs_verify_oh(obj, oh, &tags, 1); |
||||
|
||||
yaffs_release_temp_buffer(dev, buffer, __LINE__); |
||||
} |
||||
|
||||
/* Verify it has a parent */ |
||||
if (obj && !obj->fake && |
||||
(!obj->parent || obj->parent->my_dev != dev)) { |
||||
T(YAFFS_TRACE_VERIFY, |
||||
(TSTR("Obj %d has parent pointer %p which does not look like an object"TENDSTR), |
||||
obj->obj_id, obj->parent)); |
||||
} |
||||
|
||||
/* Verify parent is a directory */ |
||||
if (obj->parent && obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { |
||||
T(YAFFS_TRACE_VERIFY, |
||||
(TSTR("Obj %d's parent is not a directory (type %d)"TENDSTR), |
||||
obj->obj_id, obj->parent->variant_type)); |
||||
} |
||||
|
||||
switch (obj->variant_type) { |
||||
case YAFFS_OBJECT_TYPE_FILE: |
||||
yaffs_verify_file(obj); |
||||
break; |
||||
case YAFFS_OBJECT_TYPE_SYMLINK: |
||||
yaffs_verify_symlink(obj); |
||||
break; |
||||
case YAFFS_OBJECT_TYPE_DIRECTORY: |
||||
yaffs_verify_dir(obj); |
||||
break; |
||||
case YAFFS_OBJECT_TYPE_HARDLINK: |
||||
yaffs_verify_link(obj); |
||||
break; |
||||
case YAFFS_OBJECT_TYPE_SPECIAL: |
||||
yaffs_verify_special(obj); |
||||
break; |
||||
case YAFFS_OBJECT_TYPE_UNKNOWN: |
||||
default: |
||||
T(YAFFS_TRACE_VERIFY, |
||||
(TSTR("Obj %d has illegaltype %d"TENDSTR), |
||||
obj->obj_id, obj->variant_type)); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
void yaffs_verify_objects(yaffs_dev_t *dev) |
||||
{ |
||||
yaffs_obj_t *obj; |
||||
int i; |
||||
struct ylist_head *lh; |
||||
|
||||
if (yaffs_skip_verification(dev)) |
||||
return; |
||||
|
||||
/* Iterate through the objects in each hash entry */ |
||||
|
||||
for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { |
||||
ylist_for_each(lh, &dev->obj_bucket[i].list) { |
||||
if (lh) { |
||||
obj = ylist_entry(lh, yaffs_obj_t, hash_link); |
||||
yaffs_verify_obj(obj); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
void yaffs_verify_obj_in_dir(yaffs_obj_t *obj) |
||||
{ |
||||
struct ylist_head *lh; |
||||
yaffs_obj_t *listObj; |
||||
|
||||
int count = 0; |
||||
|
||||
if (!obj) { |
||||
T(YAFFS_TRACE_ALWAYS, (TSTR("No object to verify" TENDSTR))); |
||||
YBUG(); |
||||
return; |
||||
} |
||||
|
||||
if (yaffs_skip_verification(obj->my_dev)) |
||||
return; |
||||
|
||||
if (!obj->parent) { |
||||
T(YAFFS_TRACE_ALWAYS, (TSTR("Object does not have parent" TENDSTR))); |
||||
YBUG(); |
||||
return; |
||||
} |
||||
|
||||
if (obj->parent->variant_type != 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.dir_variant.children) { |
||||
if (lh) { |
||||
listObj = ylist_entry(lh, yaffs_obj_t, siblings); |
||||
yaffs_verify_obj(listObj); |
||||
if (obj == listObj) |
||||
count++; |
||||
} |
||||
} |
||||
|
||||
if (count != 1) { |
||||
T(YAFFS_TRACE_ALWAYS, (TSTR("Object in directory %d times" TENDSTR), count)); |
||||
YBUG(); |
||||
} |
||||
} |
||||
|
||||
void yaffs_verify_dir(yaffs_obj_t *directory) |
||||
{ |
||||
struct ylist_head *lh; |
||||
yaffs_obj_t *listObj; |
||||
|
||||
if (!directory) { |
||||
YBUG(); |
||||
return; |
||||
} |
||||
|
||||
if (yaffs_skip_full_verification(directory->my_dev)) |
||||
return; |
||||
|
||||
if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { |
||||
T(YAFFS_TRACE_ALWAYS, (TSTR("Directory has wrong type: %d" TENDSTR), directory->variant_type)); |
||||
YBUG(); |
||||
} |
||||
|
||||
/* Iterate through the objects in each hash entry */ |
||||
|
||||
ylist_for_each(lh, &directory->variant.dir_variant.children) { |
||||
if (lh) { |
||||
listObj = ylist_entry(lh, yaffs_obj_t, siblings); |
||||
if (listObj->parent != directory) { |
||||
T(YAFFS_TRACE_ALWAYS, (TSTR("Object in directory list has wrong parent %p" TENDSTR), listObj->parent)); |
||||
YBUG(); |
||||
} |
||||
yaffs_verify_obj_in_dir(listObj); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static int yaffs_free_verification_failures; |
||||
|
||||
void yaffs_verify_free_chunks(yaffs_dev_t *dev) |
||||
{ |
||||
int counted; |
||||
int difference; |
||||
|
||||
if (yaffs_skip_verification(dev)) |
||||
return; |
||||
|
||||
counted = yaffs_count_free_chunks(dev); |
||||
|
||||
difference = dev->n_free_chunks - counted; |
||||
|
||||
if (difference) { |
||||
T(YAFFS_TRACE_ALWAYS, |
||||
(TSTR("Freechunks verification failure %d %d %d" TENDSTR), |
||||
dev->n_free_chunks, counted, difference)); |
||||
yaffs_free_verification_failures++; |
||||
} |
||||
} |
||||
|
||||
int yaffs_verify_file_sane(yaffs_obj_t *in) |
||||
{ |
||||
#if 0 |
||||
int chunk; |
||||
int n_chunks; |
||||
int fSize; |
||||
int failed = 0; |
||||
int obj_id; |
||||
yaffs_tnode_t *tn; |
||||
yaffs_tags_t localTags; |
||||
yaffs_tags_t *tags = &localTags; |
||||
int theChunk; |
||||
int is_deleted; |
||||
|
||||
if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) |
||||
return YAFFS_FAIL; |
||||
|
||||
obj_id = in->obj_id; |
||||
fSize = in->variant.file_variant.file_size; |
||||
n_chunks = |
||||
(fSize + in->my_dev->data_bytes_per_chunk - 1) / in->my_dev->data_bytes_per_chunk; |
||||
|
||||
for (chunk = 1; chunk <= n_chunks; chunk++) { |
||||
tn = yaffs_find_tnode_0(in->my_dev, &in->variant.file_variant, |
||||
chunk); |
||||
|
||||
if (tn) { |
||||
|
||||
theChunk = yaffs_get_group_base(dev, tn, chunk); |
||||
|
||||
if (yaffs_check_chunk_bits |
||||
(dev, theChunk / dev->param.chunks_per_block, |
||||
theChunk % dev->param.chunks_per_block)) { |
||||
|
||||
yaffs_rd_chunk_tags_nand(in->my_dev, theChunk, |
||||
tags, |
||||
&is_deleted); |
||||
if (yaffs_tags_match |
||||
(tags, in->obj_id, chunk, is_deleted)) { |
||||
/* 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 |
||||
} |
@ -0,0 +1,39 @@ |
||||
/*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#ifndef __YAFFS_VERIFY_H__ |
||||
#define __YAFFS_VERIFY_H__ |
||||
|
||||
#include "yaffs_guts.h" |
||||
|
||||
void yaffs_verify_blk(yaffs_dev_t *dev, yaffs_block_info_t *bi, int n); |
||||
void yaffs_verify_collected_blk(yaffs_dev_t *dev, yaffs_block_info_t *bi, int n); |
||||
void yaffs_verify_blocks(yaffs_dev_t *dev); |
||||
|
||||
void yaffs_verify_oh(yaffs_obj_t *obj, yaffs_obj_header *oh, yaffs_ext_tags *tags, int parentCheck); |
||||
void yaffs_verify_file(yaffs_obj_t *obj); |
||||
void yaffs_verify_link(yaffs_obj_t *obj); |
||||
void yaffs_verify_symlink(yaffs_obj_t *obj); |
||||
void yaffs_verify_special(yaffs_obj_t *obj); |
||||
void yaffs_verify_obj(yaffs_obj_t *obj); |
||||
void yaffs_verify_objects(yaffs_dev_t *dev); |
||||
void yaffs_verify_obj_in_dir(yaffs_obj_t *obj); |
||||
void yaffs_verify_dir(yaffs_obj_t *directory); |
||||
void yaffs_verify_free_chunks(yaffs_dev_t *dev); |
||||
|
||||
int yaffs_verify_file_sane(yaffs_obj_t *obj); |
||||
|
||||
int yaffs_skip_verification(yaffs_dev_t *dev); |
||||
|
||||
#endif |
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,465 @@ |
||||
/*
|
||||
* 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_dev_t *dev) |
||||
{ |
||||
yaffs_ext_tags tags; |
||||
int blk; |
||||
int blockIterator; |
||||
int startIterator; |
||||
int endIterator; |
||||
int result; |
||||
|
||||
int chunk; |
||||
int c; |
||||
int deleted; |
||||
yaffs_block_state_t state; |
||||
yaffs_obj_t *hard_list = NULL; |
||||
yaffs_block_info_t *bi; |
||||
__u32 seq_number; |
||||
yaffs_obj_header *oh; |
||||
yaffs_obj_t *in; |
||||
yaffs_obj_t *parent; |
||||
|
||||
int alloc_failed = 0; |
||||
|
||||
struct yaffs_shadow_fixer_s *shadowFixerList = NULL; |
||||
|
||||
|
||||
__u8 *chunkData; |
||||
|
||||
|
||||
|
||||
T(YAFFS_TRACE_SCAN, |
||||
(TSTR("yaffs1_scan starts intstartblk %d intendblk %d..." TENDSTR), |
||||
dev->internal_start_block, dev->internal_end_block)); |
||||
|
||||
chunkData = yaffs_get_temp_buffer(dev, __LINE__); |
||||
|
||||
dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; |
||||
|
||||
/* Scan all the blocks to determine their state */ |
||||
bi = dev->block_info; |
||||
for (blk = dev->internal_start_block; blk <= dev->internal_end_block; blk++) { |
||||
yaffs_clear_chunk_bits(dev, blk); |
||||
bi->pages_in_use = 0; |
||||
bi->soft_del_pages = 0; |
||||
|
||||
yaffs_query_init_block_state(dev, blk, &state, &seq_number); |
||||
|
||||
bi->block_state = state; |
||||
bi->seq_number = seq_number; |
||||
|
||||
if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) |
||||
bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; |
||||
|
||||
T(YAFFS_TRACE_SCAN_DEBUG, |
||||
(TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, |
||||
state, seq_number)); |
||||
|
||||
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->n_erased_blocks++; |
||||
dev->n_free_chunks += dev->param.chunks_per_block; |
||||
} |
||||
bi++; |
||||
} |
||||
|
||||
startIterator = dev->internal_start_block; |
||||
endIterator = dev->internal_end_block; |
||||
|
||||
/* For each block.... */ |
||||
for (blockIterator = startIterator; !alloc_failed && blockIterator <= endIterator; |
||||
blockIterator++) { |
||||
|
||||
YYIELD(); |
||||
|
||||
YYIELD(); |
||||
|
||||
blk = blockIterator; |
||||
|
||||
bi = yaffs_get_block_info(dev, blk); |
||||
state = bi->block_state; |
||||
|
||||
deleted = 0; |
||||
|
||||
/* For each chunk in each block that needs scanning....*/ |
||||
for (c = 0; !alloc_failed && c < dev->param.chunks_per_block && |
||||
state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++) { |
||||
/* Read the tags and decide what to do */ |
||||
chunk = blk * dev->param.chunks_per_block + c; |
||||
|
||||
result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, |
||||
&tags); |
||||
|
||||
/* Let's have a good look at this chunk... */ |
||||
|
||||
if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || tags.is_deleted) { |
||||
/* YAFFS1 only...
|
||||
* A deleted chunk |
||||
*/ |
||||
deleted++; |
||||
dev->n_free_chunks++; |
||||
/*T((" %d %d deleted\n",blk,c)); */ |
||||
} else if (!tags.chunk_used) { |
||||
/* 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->n_erased_blocks++; |
||||
} 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->alloc_block = blk; |
||||
dev->alloc_page = c; |
||||
dev->alloc_block_finder = blk; |
||||
/* Set block finder here to encourage the allocator to go forth from here. */ |
||||
|
||||
} |
||||
|
||||
dev->n_free_chunks += (dev->param.chunks_per_block - c); |
||||
} else if (tags.chunk_id > 0) { |
||||
/* chunk_id > 0 so it is a data chunk... */ |
||||
unsigned int endpos; |
||||
|
||||
yaffs_set_chunk_bit(dev, blk, c); |
||||
bi->pages_in_use++; |
||||
|
||||
in = yaffs_find_or_create_by_number(dev, |
||||
tags. |
||||
obj_id, |
||||
YAFFS_OBJECT_TYPE_FILE); |
||||
/* PutChunkIntoFile checks for a clash (two data chunks with
|
||||
* the same chunk_id). |
||||
*/ |
||||
|
||||
if (!in) |
||||
alloc_failed = 1; |
||||
|
||||
if (in) { |
||||
if (!yaffs_put_chunk_in_file(in, tags.chunk_id, chunk, 1)) |
||||
alloc_failed = 1; |
||||
} |
||||
|
||||
endpos = |
||||
(tags.chunk_id - 1) * dev->data_bytes_per_chunk + |
||||
tags.n_bytes; |
||||
if (in && |
||||
in->variant_type == YAFFS_OBJECT_TYPE_FILE |
||||
&& in->variant.file_variant.scanned_size < |
||||
endpos) { |
||||
in->variant.file_variant. |
||||
scanned_size = endpos; |
||||
if (!dev->param.use_header_file_size) { |
||||
in->variant.file_variant. |
||||
file_size = |
||||
in->variant.file_variant. |
||||
scanned_size; |
||||
} |
||||
|
||||
} |
||||
/* T((" %d %d data %d %d\n",blk,c,tags.obj_id,tags.chunk_id)); */ |
||||
} else { |
||||
/* chunk_id == 0, so it is an ObjectHeader.
|
||||
* Thus, we read in the object header and make the object |
||||
*/ |
||||
yaffs_set_chunk_bit(dev, blk, c); |
||||
bi->pages_in_use++; |
||||
|
||||
result = yaffs_rd_chunk_tags_nand(dev, chunk, |
||||
chunkData, |
||||
NULL); |
||||
|
||||
oh = (yaffs_obj_header *) chunkData; |
||||
|
||||
in = yaffs_find_by_number(dev, |
||||
tags.obj_id); |
||||
if (in && in->variant_type != oh->type) { |
||||
/* This should not happen, but somehow
|
||||
* Wev'e ended up with an obj_id that has been reused but not yet |
||||
* deleted, and worse still it has changed type. Delete the old object. |
||||
*/ |
||||
|
||||
yaffs_del_obj(in); |
||||
|
||||
in = 0; |
||||
} |
||||
|
||||
in = yaffs_find_or_create_by_number(dev, |
||||
tags. |
||||
obj_id, |
||||
oh->type); |
||||
|
||||
if (!in) |
||||
alloc_failed = 1; |
||||
|
||||
if (in && oh->shadows_obj > 0) { |
||||
|
||||
struct yaffs_shadow_fixer_s *fixer; |
||||
fixer = YMALLOC(sizeof(struct yaffs_shadow_fixer_s)); |
||||
if (fixer) { |
||||
fixer->next = shadowFixerList; |
||||
shadowFixerList = fixer; |
||||
fixer->obj_id = tags.obj_id; |
||||
fixer->shadowed_id = oh->shadows_obj; |
||||
T(YAFFS_TRACE_SCAN, |
||||
(TSTR |
||||
(" Shadow fixer: %d shadows %d" TENDSTR), |
||||
fixer->obj_id, fixer->shadowed_id)); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
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.serial_number; |
||||
|
||||
if (((existingSerial + 1) & 3) == newSerial) { |
||||
/* Use new one - destroy the exisiting one */ |
||||
yaffs_chunk_del(dev, |
||||
in->hdr_chunk, |
||||
1, __LINE__); |
||||
in->valid = 0; |
||||
} else { |
||||
/* Use existing - destroy this one. */ |
||||
yaffs_chunk_del(dev, chunk, 1, |
||||
__LINE__); |
||||
} |
||||
} |
||||
|
||||
if (in && !in->valid && |
||||
(tags.obj_id == YAFFS_OBJECTID_ROOT || |
||||
tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { |
||||
/* We only load some info, don't fiddle with directory structure */ |
||||
in->valid = 1; |
||||
in->variant_type = 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->hdr_chunk = chunk; |
||||
in->serial = tags.serial_number; |
||||
|
||||
} else if (in && !in->valid) { |
||||
/* we need to load this info */ |
||||
|
||||
in->valid = 1; |
||||
in->variant_type = 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->hdr_chunk = chunk; |
||||
in->serial = tags.serial_number; |
||||
|
||||
yaffs_set_obj_name_from_oh(in, oh); |
||||
in->dirty = 0; |
||||
|
||||
/* directory stuff...
|
||||
* hook up to parent |
||||
*/ |
||||
|
||||
parent = |
||||
yaffs_find_or_create_by_number |
||||
(dev, oh->parent_obj_id, |
||||
YAFFS_OBJECT_TYPE_DIRECTORY); |
||||
if (!parent) |
||||
alloc_failed = 1; |
||||
if (parent && parent->variant_type == |
||||
YAFFS_OBJECT_TYPE_UNKNOWN) { |
||||
/* Set up as a directory */ |
||||
parent->variant_type = |
||||
YAFFS_OBJECT_TYPE_DIRECTORY; |
||||
YINIT_LIST_HEAD(&parent->variant. |
||||
dir_variant. |
||||
children); |
||||
} else if (!parent || parent->variant_type != |
||||
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->lost_n_found; |
||||
} |
||||
|
||||
yaffs_add_obj_to_dir(parent, in); |
||||
|
||||
if (0 && (parent == dev->del_dir || |
||||
parent == dev->unlinked_dir)) { |
||||
in->deleted = 1; /* If it is unlinked at start up then it wants deleting */ |
||||
dev->n_deleted_files++; |
||||
} |
||||
/* 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->variant_type) { |
||||
case YAFFS_OBJECT_TYPE_UNKNOWN: |
||||
/* Todo got a problem */ |
||||
break; |
||||
case YAFFS_OBJECT_TYPE_FILE: |
||||
if (dev->param.use_header_file_size) |
||||
|
||||
in->variant.file_variant. |
||||
file_size = |
||||
oh->file_size; |
||||
|
||||
break; |
||||
case YAFFS_OBJECT_TYPE_HARDLINK: |
||||
in->variant.hardlink_variant. |
||||
equiv_id = |
||||
oh->equiv_id; |
||||
in->hard_links.next = |
||||
(struct ylist_head *) |
||||
hard_list; |
||||
hard_list = 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.symlink_variant.alias = |
||||
yaffs_clone_str(oh->alias); |
||||
if (!in->variant.symlink_variant.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->alloc_block = -1; |
||||
} |
||||
|
||||
bi->block_state = state; |
||||
|
||||
/* Now let's see if it was dirty */ |
||||
if (bi->pages_in_use == 0 && |
||||
!bi->has_shrink_hdr && |
||||
bi->block_state == YAFFS_BLOCK_STATE_FULL) { |
||||
yaffs_block_became_dirty(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_link_fixup(dev, hard_list); |
||||
|
||||
/* Fix up any shadowed objects */ |
||||
{ |
||||
struct yaffs_shadow_fixer_s *fixer; |
||||
yaffs_obj_t *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_find_by_number(dev, fixer->shadowed_id); |
||||
if (obj) |
||||
yaffs_del_obj(obj); |
||||
|
||||
obj = yaffs_find_by_number(dev, fixer->obj_id); |
||||
|
||||
if (obj) |
||||
yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); |
||||
|
||||
YFREE(fixer); |
||||
} |
||||
} |
||||
|
||||
yaffs_release_temp_buffer(dev, chunkData, __LINE__); |
||||
|
||||
if (alloc_failed) |
||||
return YAFFS_FAIL; |
||||
|
||||
T(YAFFS_TRACE_SCAN, (TSTR("yaffs1_scan ends" TENDSTR))); |
||||
|
||||
|
||||
return YAFFS_OK; |
||||
} |
||||
|
@ -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_dev_t *dev); |
||||
|
||||
#endif |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,36 @@ |
||||
/*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#ifndef __YAFFS_YAFFS2_H__ |
||||
#define __YAFFS_YAFFS2_H__ |
||||
|
||||
#include "yaffs_guts.h" |
||||
|
||||
void yaffs_calc_oldest_dirty_seq(yaffs_dev_t *dev); |
||||
void yaffs2_find_oldest_dirty_seq(yaffs_dev_t *dev); |
||||
void yaffs2_clear_oldest_dirty_seq(yaffs_dev_t *dev, yaffs_block_info_t *bi); |
||||
void yaffs2_update_oldest_dirty_seq(yaffs_dev_t *dev, unsigned block_no, yaffs_block_info_t *bi); |
||||
int yaffs_block_ok_for_gc(yaffs_dev_t *dev, yaffs_block_info_t *bi); |
||||
__u32 yaffs2_find_refresh_block(yaffs_dev_t *dev); |
||||
int yaffs2_checkpt_required(yaffs_dev_t *dev); |
||||
int yaffs_calc_checkpt_blocks_required(yaffs_dev_t *dev); |
||||
|
||||
|
||||
void yaffs2_checkpt_invalidate(yaffs_dev_t *dev); |
||||
int yaffs2_checkpt_save(yaffs_dev_t *dev); |
||||
int yaffs2_checkpt_restore(yaffs_dev_t *dev); |
||||
|
||||
int yaffs2_handle_hole(yaffs_obj_t *obj, loff_t new_size); |
||||
int yaffs2_scan_backwards(yaffs_dev_t *dev); |
||||
|
||||
#endif |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue