*** empty log message ***
[yaffs/.git] / yaffs_guts.c
1 /*\r
2  * YAFFS: Yet another FFS. A NAND-flash specific file system.\r
3  * yaffs_guts.c  The main guts of YAFFS\r
4  *\r
5  * Copyright (C) 2002 Aleph One Ltd.\r
6  *   for Toby Churchill Ltd and Brightstar Engineering\r
7  *\r
8  * Created by Charles Manning <charles@aleph1.co.uk>\r
9  *\r
10  * This program is free software; you can redistribute it and/or modify\r
11  * it under the terms of the GNU General Public License version 2 as\r
12  * published by the Free Software Foundation.\r
13  *\r
14  */\r
15  //yaffs_guts.c\r
16 \r
17 const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.12 2002-11-08 07:19:41 charles Exp $";\r
18 \r
19 #include "yportenv.h"\r
20 \r
21 #include "yaffsinterface.h"\r
22 #include "yaffs_guts.h"\r
23 \r
24 \r
25 #define YAFFS_GARBAGE_COLLECT_LOW_WATER 2\r
26 \r
27 \r
28 \r
29 // External functions for ECC on data\r
30 void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);\r
31 int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);\r
32 \r
33 \r
34 // countBits is a quick way of counting the number of bits in a byte.\r
35 // ie. countBits[n] holds the number of 1 bits in a byte with the value n.\r
36 \r
37 static const char yaffs_countBitsTable[256] =\r
38 {\r
39 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,\r
40 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,\r
41 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,\r
42 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
43 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,\r
44 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
45 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
46 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,\r
47 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,\r
48 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
49 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
50 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,\r
51 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
52 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,\r
53 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,\r
54 4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8\r
55 };\r
56 \r
57 static int yaffs_CountBits(__u8 x)\r
58 {\r
59         int retVal;\r
60         retVal = yaffs_countBitsTable[x];\r
61         return retVal;\r
62 }\r
63 \r
64 \r
65 \r
66 // Local prototypes\r
67 static int yaffs_CheckObjectHashSanity(yaffs_Device *dev);\r
68 static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr);\r
69 static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr);\r
70 static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan);\r
71 \r
72 static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type);\r
73 static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj);\r
74 static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force);\r
75 static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId);\r
76 static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);\r
77 static int yaffs_CheckStructures(void);\r
78 static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit);\r
79 static int yaffs_DoGenericObjectDeletion(yaffs_Object *in);\r
80 \r
81 static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device *dev,int blockNo);\r
82 \r
83 // Robustification\r
84 static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND);\r
85 static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND);\r
86 static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND);\r
87 static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare);\r
88 static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare);\r
89 \r
90 static int  yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND);\r
91 \r
92 static int yaffs_UnlinkWorker(yaffs_Object *obj);\r
93 \r
94 \r
95 static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1);\r
96 \r
97 \r
98 \r
99 loff_t yaffs_GetFileSize(yaffs_Object *obj);\r
100 \r
101 static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted);\r
102 static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted);\r
103 \r
104 \r
105 static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve);\r
106 \r
107 #ifdef YAFFS_PARANOID\r
108 static int yaffs_CheckFileSanity(yaffs_Object *in);\r
109 #else\r
110 #define yaffs_CheckFileSanity(in)\r
111 #endif\r
112 \r
113 static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in);\r
114 static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId);\r
115 \r
116 \r
117 static  __inline__ yaffs_BlockInfo* yaffs_GetBlockInfo(yaffs_Device *dev, int blk)\r
118 {\r
119         if(blk < dev->startBlock || blk > dev->endBlock)\r
120         {\r
121                 T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: block %d is not valid" TENDSTR),blk));\r
122                 YBUG();\r
123         }\r
124         return &dev->blockInfo[blk - dev->startBlock];\r
125 }\r
126 \r
127 \r
128 static  __inline__ int yaffs_HashFunction(int n)\r
129 {\r
130         return (n % YAFFS_NOBJECT_BUCKETS);\r
131 }\r
132 \r
133 \r
134 yaffs_Object *yaffs_Root(yaffs_Device *dev)\r
135 {\r
136         return dev->rootDir;\r
137 }\r
138 \r
139 yaffs_Object *yaffs_LostNFound(yaffs_Device *dev)\r
140 {\r
141         return dev->lostNFoundDir;\r
142 }\r
143 \r
144 \r
145 static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)\r
146 {\r
147         if(chunkInNAND < dev->startBlock * YAFFS_CHUNKS_PER_BLOCK)\r
148         {\r
149                 T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),chunkInNAND));\r
150                 return YAFFS_FAIL;\r
151         }\r
152 \r
153         dev->nPageWrites++;\r
154         return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);\r
155 }\r
156 \r
157 \r
158 int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,\r
159                                                         int chunkInNAND, \r
160                                                         __u8 *data, \r
161                                                         yaffs_Spare *spare, \r
162                                                         int doErrorCorrection)\r
163 {\r
164         int retVal;\r
165         __u8 calcEcc[3];\r
166         yaffs_Spare localSpare;\r
167         int eccResult1,eccResult2;\r
168         \r
169         dev->nPageReads++;\r
170         \r
171         \r
172 \r
173         \r
174         if(!spare && data)\r
175         {\r
176                 // If we don't have a real spare, then we use a local one.\r
177                 // Need this for the calculation of the ecc\r
178                 spare = &localSpare;\r
179         }\r
180         \r
181         retVal  = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);\r
182         if(data && doErrorCorrection)\r
183         {\r
184                 // Do ECC correction\r
185                 //Todo handle any errors\r
186                  nand_calculate_ecc(data,calcEcc);\r
187                  eccResult1 = nand_correct_data (data,spare->ecc1, calcEcc);\r
188                  nand_calculate_ecc(&data[256],calcEcc);\r
189                  eccResult2 = nand_correct_data (&data[256],spare->ecc2, calcEcc);\r
190                  \r
191                  if(eccResult1>0)\r
192                  {\r
193                         T(YAFFS_TRACE_ERROR, (TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));\r
194                         dev->eccFixed++;\r
195                  }\r
196                  else if(eccResult1<0)\r
197                  {\r
198                         T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));\r
199                         dev->eccUnfixed++;\r
200                  }\r
201                  \r
202                  if(eccResult2>0)\r
203                  {\r
204                         T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));\r
205                         dev->eccFixed++;\r
206                  }\r
207                  else if(eccResult2<0)\r
208                  {\r
209                         T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));\r
210                         dev->eccUnfixed++;\r
211                  }\r
212                  \r
213                  if(eccResult1 || eccResult2)\r
214                  {\r
215                         // Hoosterman, we had a data problem on this page\r
216                         yaffs_HandleReadDataError(dev,chunkInNAND);\r
217                  }\r
218         }\r
219 \r
220         return retVal;\r
221 }\r
222 \r
223 \r
224 static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)\r
225 {\r
226 \r
227         static int init = 0;\r
228         static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];\r
229         static __u8 data[YAFFS_BYTES_PER_CHUNK];\r
230         static __u8 spare[16];\r
231         \r
232         \r
233         dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare);\r
234         \r
235         \r
236         \r
237         if(!init)\r
238         {\r
239                 memset(cmpbuf,0xff,YAFFS_BYTES_PER_CHUNK);\r
240                 init = 1;\r
241         }\r
242         \r
243         if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) return  YAFFS_FAIL;\r
244         if(memcmp(cmpbuf,spare,16)) return YAFFS_FAIL;\r
245 \r
246         \r
247         return YAFFS_OK;\r
248         \r
249 }\r
250 \r
251 \r
252 \r
253 int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)\r
254 {\r
255         dev->nBlockErasures++;\r
256         return dev->eraseBlockInNAND(dev,blockInNAND);\r
257 }\r
258 \r
259 int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)\r
260 {\r
261         return dev->initialiseNAND(dev);\r
262 }\r
263 \r
264 static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_Spare *spare,int useReserve)\r
265 {\r
266         int chunk;\r
267         \r
268         int writeOk = 1;\r
269         int attempts = 0;\r
270         \r
271         unsigned char rbData[YAFFS_BYTES_PER_CHUNK];\r
272         yaffs_Spare rbSpare;\r
273         \r
274         do{\r
275                 chunk = yaffs_AllocateChunk(dev,useReserve);\r
276         \r
277                 if(chunk >= 0)\r
278                 {\r
279 \r
280                         // First check this chunk is erased...\r
281 #ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK\r
282                         writeOk = yaffs_CheckChunkErased(dev,chunk);\r
283 #endif          \r
284                         if(!writeOk)\r
285                         {\r
286                                 T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk));\r
287                         }\r
288                         else\r
289                         {\r
290                                 writeOk =  yaffs_WriteChunkToNAND(dev,chunk,data,spare);\r
291                         }\r
292                         attempts++;\r
293                         if(writeOk)\r
294                         {\r
295                                 // Readback & verify\r
296                                 // If verify fails, then delete this chunk and try again\r
297                                 // To verify we compare everything except the block and \r
298                                 // page status bytes.\r
299                                 // NB We check a raw read without ECC correction applied\r
300                                 yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare,0);\r
301                                 \r
302 #ifndef CONFIG_YAFFS_DISABLE_WRITE_VERIFY\r
303                                 if(!yaffs_VerifyCompare(data,rbData,spare,&rbSpare))\r
304                                 {\r
305                                         // Didn't verify\r
306                                         T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write verify failed on chunk %d" TENDSTR), chunk));\r
307 \r
308                                         writeOk = 0;\r
309                                 }       \r
310 #endif                          \r
311                                 \r
312                         }\r
313                         if(writeOk)\r
314                         {\r
315                                 // Copy the data into the write buffer.\r
316                                 // NB We do this at the end to prevent duplicates in the case of a write error.\r
317                                 //Todo\r
318                                 yaffs_HandleWriteChunkOk(dev,chunk,data,spare);\r
319                         }\r
320                         else\r
321                         {\r
322                                 yaffs_HandleWriteChunkError(dev,chunk);\r
323                         }\r
324                 }\r
325                 \r
326         } while(chunk >= 0 && ! writeOk);\r
327         \r
328         if(attempts > 1)\r
329         {\r
330                 T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));\r
331                 dev->nRetriedWrites+= (attempts - 1);   \r
332         }\r
333         \r
334         return chunk;\r
335 }\r
336 \r
337 ///\r
338 // Functions for robustisizing\r
339 //\r
340 //\r
341 \r
342 static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND)\r
343 {\r
344         // Ding the blockStatus in the first two pages of the block.\r
345         \r
346         yaffs_Spare spare;\r
347 \r
348         memset(&spare, 0xff,sizeof(yaffs_Spare));\r
349 \r
350         spare.blockStatus = 0;\r
351         \r
352         yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK, NULL , &spare);\r
353         yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK + 1, NULL , &spare);\r
354         \r
355         yaffs_GetBlockInfo(dev,blockInNAND)->blockState = YAFFS_BLOCK_STATE_DEAD;\r
356         dev->nRetiredBlocks++;\r
357 }\r
358 \r
359 \r
360 \r
361 static int yaffs_RewriteBufferedBlock(yaffs_Device *dev)\r
362 {\r
363         dev->doingBufferedBlockRewrite = 1;\r
364         //\r
365         //      Remove erased chunks\r
366         //  Rewrite existing chunks to a new block\r
367         //      Set current write block to the new block\r
368         \r
369         dev->doingBufferedBlockRewrite = 0;\r
370         \r
371         return 1;\r
372 }\r
373 \r
374 \r
375 static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND)\r
376 {\r
377         int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK;\r
378 \r
379         // Mark the block for retirement\r
380         yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;\r
381         T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>>Block %d marked for retirement" TENDSTR),blockInNAND));\r
382 \r
383 \r
384         //TODO  \r
385         // Just do a garbage collection on the affected block then retire the block\r
386         // NB recursion\r
387 }\r
388 \r
389 \r
390 static void yaffs_CheckWrittenBlock(yaffs_Device *dev,int chunkInNAND)\r
391 {\r
392 }\r
393 \r
394 static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare)\r
395 {\r
396 }\r
397 \r
398 static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare)\r
399 {\r
400 }\r
401 \r
402 static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND)\r
403 {\r
404         int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK;\r
405 \r
406         // Mark the block for retirement\r
407         yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;\r
408         // Delete the chunk\r
409         yaffs_DeleteChunk(dev,chunkInNAND);\r
410 }\r
411 \r
412 \r
413 \r
414 \r
415 static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1)\r
416 {\r
417 \r
418 \r
419         if( memcmp(d0,d1,YAFFS_BYTES_PER_CHUNK) != 0 ||\r
420                 s0->tagByte0 != s1->tagByte0 ||\r
421                 s0->tagByte1 != s1->tagByte1 ||\r
422                 s0->tagByte2 != s1->tagByte2 ||\r
423                 s0->tagByte3 != s1->tagByte3 ||\r
424                 s0->tagByte4 != s1->tagByte4 ||\r
425                 s0->tagByte5 != s1->tagByte5 ||\r
426                 s0->tagByte6 != s1->tagByte6 ||\r
427                 s0->tagByte7 != s1->tagByte7 ||\r
428                 s0->ecc1[0]  != s1->ecc1[0]  ||\r
429                 s0->ecc1[1]  != s1->ecc1[1]  ||\r
430                 s0->ecc1[2]  != s1->ecc1[2]  ||\r
431                 s0->ecc2[0]  != s1->ecc2[0]  ||\r
432                 s0->ecc2[1]  != s1->ecc2[1]  ||\r
433                 s0->ecc2[2]  != s1->ecc2[2] )\r
434                 {\r
435                         return 0;\r
436                 }\r
437         \r
438         return 1;\r
439 }\r
440 \r
441 \r
442 ///////////////////////// Object management //////////////////\r
443 // List of spare objects\r
444 // The list is hooked together using the first pointer\r
445 // in the object\r
446 \r
447 // static yaffs_Object *yaffs_freeObjects = NULL;\r
448 \r
449 // static int yaffs_nFreeObjects;\r
450 \r
451 // static yaffs_ObjectList *yaffs_allocatedObjectList = NULL;\r
452 \r
453 // static yaffs_ObjectBucket yaffs_objectBucket[YAFFS_NOBJECT_BUCKETS];\r
454 \r
455 \r
456 static __u16 yaffs_CalcNameSum(const char *name)\r
457 {\r
458         __u16 sum = 0;\r
459         __u16 i = 1;\r
460         \r
461         __u8 *bname = (__u8 *)name;\r
462         if(bname)\r
463         {\r
464                 while ((*bname) && (i <=YAFFS_MAX_NAME_LENGTH))\r
465                 {\r
466 #ifdef CONFIG_YAFFS_WINCE\r
467                         sum += toupper(*bname) * i;\r
468 #else\r
469                         sum += (*bname) * i;\r
470 #endif\r
471                         i++;\r
472                         bname++;\r
473                 }\r
474         }\r
475         return sum;\r
476 }\r
477 \r
478 void yaffs_SetObjectName(yaffs_Object *obj, const char *name)\r
479 {\r
480 #ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM\r
481                                         if(name && strlen(name) <= YAFFS_SHORT_NAME_LENGTH)\r
482                                         {\r
483                                                 strcpy(obj->shortName,name);\r
484                                         }\r
485                                         else\r
486                                         {\r
487                                                 obj->shortName[0]='\0';\r
488                                         }\r
489 #endif\r
490                                         obj->sum = yaffs_CalcNameSum(name);\r
491 }\r
492 \r
493 void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)\r
494 {\r
495         nand_calculate_ecc (data , spare->ecc1);\r
496         nand_calculate_ecc (&data[256] , spare->ecc2);\r
497 }\r
498 \r
499 void yaffs_CalcTagsECC(yaffs_Tags *tags)\r
500 {\r
501         // Calculate an ecc\r
502         \r
503         unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;\r
504         unsigned  i,j;\r
505         unsigned  ecc = 0;\r
506         unsigned bit = 0;\r
507 \r
508         tags->ecc = 0;\r
509         \r
510         for(i = 0; i < 8; i++)\r
511         {\r
512                 for(j = 1; j &0xff; j<<=1)\r
513                 {\r
514                         bit++;\r
515                         if(b[i] & j)\r
516                         {\r
517                                 ecc ^= bit;\r
518                         }\r
519                 }\r
520         }\r
521         \r
522         tags->ecc = ecc;\r
523         \r
524         \r
525 }\r
526 \r
527 int  yaffs_CheckECCOnTags(yaffs_Tags *tags)\r
528 {\r
529         unsigned ecc = tags->ecc;\r
530         \r
531         yaffs_CalcTagsECC(tags);\r
532         \r
533         ecc ^= tags->ecc;\r
534         \r
535         if(ecc && ecc <= 64)\r
536         {\r
537                 // TODO: Handle the failure better. Retire?\r
538                 unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;\r
539 \r
540                 ecc--;\r
541                                 \r
542                 b[ecc / 8] ^= (1 << (ecc & 7));\r
543                 \r
544                 // Now recvalc the ecc\r
545                 yaffs_CalcTagsECC(tags);\r
546                 \r
547                 return 1; // recovered error\r
548         }\r
549         else if(ecc)\r
550         {\r
551                 // Wierd ecc failure value\r
552                 // TODO Need to do somethiong here\r
553                 return -1; //unrecovered error\r
554         }\r
555         \r
556         return 0;\r
557 }\r
558 \r
559 \r
560 ///////////////////////// TNODES ///////////////////////\r
561 \r
562 // List of spare tnodes\r
563 // The list is hooked together using the first pointer\r
564 // in the tnode.\r
565 \r
566 //static yaffs_Tnode *yaffs_freeTnodes = NULL;\r
567 \r
568 // static int yaffs_nFreeTnodes;\r
569 \r
570 //static yaffs_TnodeList *yaffs_allocatedTnodeList = NULL;\r
571 \r
572 \r
573 \r
574 // yaffs_CreateTnodes creates a bunch more tnodes and\r
575 // adds them to the tnode free list.\r
576 // Don't use this function directly\r
577 \r
578 static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes)\r
579 {\r
580     int i;\r
581     yaffs_Tnode *newTnodes;\r
582     yaffs_TnodeList *tnl;\r
583     \r
584     if(nTnodes < 1) return YAFFS_OK;\r
585    \r
586         // make these things\r
587         \r
588     newTnodes = YMALLOC(nTnodes * sizeof(yaffs_Tnode));\r
589    \r
590     if (!newTnodes)\r
591     {\r
592                 T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not allocate Tnodes"TENDSTR)));\r
593                 return YAFFS_FAIL;\r
594     }\r
595     \r
596     // Hook them into the free list\r
597     for(i = 0; i < nTnodes - 1; i++)\r
598     {\r
599         newTnodes[i].internal[0] = &newTnodes[i+1];\r
600     }\r
601         \r
602         newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;\r
603         dev->freeTnodes = newTnodes;\r
604         dev->nFreeTnodes+= nTnodes;\r
605         dev->nTnodesCreated += nTnodes;\r
606 \r
607         // Now add this bunch of tnodes to a list for freeing up.\r
608         // NB If we can't add this to the management list it isn't fatal\r
609         // but it just means we can't free this bunch of tnodes later.\r
610         tnl = YMALLOC(sizeof(yaffs_TnodeList));\r
611         if(!tnl)\r
612         {\r
613                 T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not add tnodes to management list" TENDSTR)));\r
614                 \r
615         }\r
616         else\r
617         {\r
618                 tnl->tnodes = newTnodes;\r
619                 tnl->next = dev->allocatedTnodeList;\r
620                 dev->allocatedTnodeList = tnl;\r
621         }\r
622 \r
623 \r
624         T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Tnodes added" TENDSTR)));\r
625 \r
626 \r
627         return YAFFS_OK;\r
628 }\r
629 \r
630 \r
631 // GetTnode gets us a clean tnode. Tries to make allocate more if we run out\r
632 static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)\r
633 {\r
634         yaffs_Tnode *tn = NULL;\r
635         \r
636         // If there are none left make more\r
637         if(!dev->freeTnodes)\r
638         {\r
639                 yaffs_CreateTnodes(dev,YAFFS_ALLOCATION_NTNODES);\r
640         }\r
641         \r
642         if(dev->freeTnodes)\r
643         {\r
644                 tn = dev->freeTnodes;\r
645                 dev->freeTnodes = dev->freeTnodes->internal[0];\r
646                 dev->nFreeTnodes--;\r
647                 // zero out\r
648                 memset(tn,0,sizeof(yaffs_Tnode));\r
649         }\r
650         \r
651 \r
652         return tn;\r
653 }\r
654 \r
655 \r
656 // FreeTnode frees up a tnode and puts it back on the free list\r
657 static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn)\r
658 {\r
659         tn->internal[0] = dev->freeTnodes;\r
660         dev->freeTnodes = tn;\r
661         dev->nFreeTnodes++;\r
662 }\r
663 \r
664 \r
665 static void yaffs_DeinitialiseTnodes(yaffs_Device*dev)\r
666 {\r
667         // Free the list of allocated tnodes\r
668         \r
669         while(dev->allocatedTnodeList)\r
670         {\r
671                 YFREE(dev->allocatedTnodeList->tnodes);\r
672                 dev->allocatedTnodeList =  dev->allocatedTnodeList->next;\r
673         }\r
674         \r
675         dev->freeTnodes = NULL;\r
676         dev->nFreeTnodes = 0;\r
677 }\r
678 \r
679 static void yaffs_InitialiseTnodes(yaffs_Device*dev)\r
680 {\r
681         dev->allocatedTnodeList = NULL;\r
682         dev->freeTnodes = NULL;\r
683         dev->nFreeTnodes = 0;\r
684         dev->nTnodesCreated = 0;\r
685 \r
686 }\r
687 \r
688 #if 0\r
689 void yaffs_TnodeTest(yaffs_Device *dev)\r
690 {\r
691 \r
692         int i;\r
693         int j;\r
694         yaffs_Tnode *tn[1000];\r
695         \r
696         YINFO("Testing TNodes");\r
697         \r
698         for(j = 0; j < 50; j++)\r
699         {\r
700                 for(i = 0; i < 1000; i++)\r
701                 {\r
702                         tn[i] = yaffs_GetTnode(dev);\r
703                         if(!tn[i])\r
704                         {\r
705                                 YALERT("Getting tnode failed");\r
706                         }\r
707                 }\r
708                 for(i = 0; i < 1000; i+=3)\r
709                 {\r
710                         yaffs_FreeTnode(dev,tn[i]);\r
711                         tn[i] = NULL;\r
712                 }\r
713                 \r
714         }\r
715 }\r
716 #endif\r
717 \r
718 \r
719 ////////////////// END OF TNODE MANIPULATION ///////////////////////////\r
720 \r
721 /////////////// Functions to manipulate the look-up tree (made up of tnodes)\r
722 // The look up tree is represented by the top tnode and the number of topLevel\r
723 // in the tree. 0 means only the level 0 tnode is in the tree.\r
724 \r
725 \r
726 // FindLevel0Tnode finds the level 0 tnode, if one exists.\r
727 // Used when reading.....\r
728 static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,yaffs_FileStructure *fStruct, __u32 chunkId)\r
729 {\r
730         \r
731         yaffs_Tnode *tn = fStruct->top;\r
732         __u32 i;\r
733         int requiredTallness;   \r
734         int level = fStruct->topLevel;\r
735         \r
736         // Check sane level and chunk Id\r
737         if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL)\r
738         {\r
739 //              char str[50];\r
740 //              sprintf(str,"Bad level %d",level);\r
741 //              YALERT(str);\r
742                 return NULL;\r
743         }\r
744         \r
745         if(chunkId > YAFFS_MAX_CHUNK_ID)\r
746         {\r
747 //              char str[50];\r
748 //              sprintf(str,"Bad chunkId %d",chunkId);\r
749 //              YALERT(str);\r
750                 return NULL;\r
751         }\r
752 \r
753         // First check we're tall enough (ie enough topLevel)\r
754         \r
755         i = chunkId >> (/*dev->chunkGroupBits  + */YAFFS_TNODES_LEVEL0_BITS);\r
756         requiredTallness = 0;\r
757         while(i)\r
758         {\r
759                 i >>= YAFFS_TNODES_INTERNAL_BITS;\r
760                 requiredTallness++;\r
761         }\r
762         \r
763         \r
764         if(requiredTallness > fStruct->topLevel)\r
765         {\r
766                 // Not tall enough, so we can't find it, return NULL.\r
767                 return NULL;\r
768         }\r
769                 \r
770         \r
771         // Traverse down to level 0\r
772         while (level > 0 && tn)\r
773         {\r
774             tn = tn->internal[(chunkId >>(/* dev->chunkGroupBits + */ YAFFS_TNODES_LEVEL0_BITS + (level-1) * YAFFS_TNODES_INTERNAL_BITS)) & \r
775                                YAFFS_TNODES_INTERNAL_MASK]; \r
776                 level--;\r
777         \r
778         }\r
779         \r
780         return tn;              \r
781 }\r
782 \r
783 // AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree.\r
784 // This happens in two steps:\r
785 //  1. If the tree isn't tall enough, then make it taller.\r
786 //  2. Scan down the tree towards the level 0 tnode adding tnodes if required.\r
787 //\r
788 // Used when modifying the tree.\r
789 //\r
790 static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId)\r
791 {\r
792         \r
793         yaffs_Tnode *tn; \r
794         \r
795         int requiredTallness;\r
796         int i;\r
797         int l;\r
798         \r
799         __u32 x;\r
800                 \r
801         \r
802         //T((TSTR("AddOrFind topLevel=%d, chunk=%d"),fStruct->topLevel,chunkId));\r
803         \r
804         // Check sane level and page Id\r
805         if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL)\r
806         {\r
807 //              char str[50];\r
808 //              sprintf(str,"Bad level %d",fStruct->topLevel);\r
809 //              YALERT(str);\r
810                 return NULL;\r
811         }\r
812         \r
813         if(chunkId > YAFFS_MAX_CHUNK_ID)\r
814         {\r
815 //              char str[50];\r
816 //              sprintf(str,"Bad chunkId %d",chunkId);\r
817 //              YALERT(str);\r
818                 return NULL;\r
819         }\r
820         \r
821         // First check we're tall enough (ie enough topLevel)\r
822         \r
823         x = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS);\r
824         requiredTallness = 0;\r
825         while(x)\r
826         {\r
827                 x >>= YAFFS_TNODES_INTERNAL_BITS;\r
828                 requiredTallness++;\r
829         }\r
830         \r
831         //T((TSTR(" required=%d"),requiredTallness));\r
832         \r
833         \r
834         if(requiredTallness > fStruct->topLevel)\r
835         {\r
836                 // Not tall enough,gotta make the tree taller\r
837                 for(i = fStruct->topLevel; i < requiredTallness; i++)\r
838                 {\r
839                         //T((TSTR(" add new top")));\r
840                         \r
841                         tn = yaffs_GetTnode(dev);\r
842                         \r
843                         if(tn)\r
844                         {\r
845                                 tn->internal[0] = fStruct->top;\r
846                                 fStruct->top = tn;\r
847                         }\r
848                         else\r
849                         {\r
850                                         T(YAFFS_TRACE_ERROR,(TSTR("yaffs: no more tnodes" TENDSTR)));\r
851                         }\r
852                 }\r
853                 \r
854                 fStruct->topLevel = requiredTallness;\r
855         }\r
856         \r
857         \r
858         // Traverse down to level 0, adding anything we need\r
859         \r
860         l = fStruct->topLevel;\r
861         tn = fStruct->top;\r
862         while (l > 0 && tn)\r
863         {\r
864                 x = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) & \r
865                                YAFFS_TNODES_INTERNAL_MASK;\r
866                                \r
867                 //T((TSTR(" [%d:%d]"),l,i));\r
868                 \r
869             if(!tn->internal[x])\r
870             {\r
871                 //T((TSTR(" added")));\r
872                 \r
873                 tn->internal[x] = yaffs_GetTnode(dev);\r
874             }\r
875             \r
876             tn =        tn->internal[x];\r
877                 l--;\r
878         \r
879         }\r
880         \r
881         //TSTR(TENDSTR)));\r
882         \r
883         return tn;              \r
884 }\r
885 \r
886 // DeleteWorker scans backwards through the tnode tree and deletes all the\r
887 // chunks and tnodes in the file\r
888 // Returns 1 if the tree was deleted. Returns 0 if it stopped early due to hitting the limit and the delete is incomplete.\r
889 \r
890 static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit)\r
891 {\r
892         int i;\r
893         int chunkInInode;\r
894         int theChunk;\r
895         yaffs_Tags tags;\r
896         int found;\r
897         int chunkDeleted;\r
898         int allDone = 1;\r
899         \r
900         \r
901         if(tn)\r
902         {\r
903                 if(level > 0)\r
904                 {\r
905                 \r
906                         for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)\r
907                         {\r
908                             if(tn->internal[i])\r
909                         {\r
910                                         if(limit && (*limit) < 0)\r
911                                         {\r
912                                                 allDone = 0;\r
913                                         }\r
914                                         else\r
915                                         {\r
916                                                 allDone = yaffs_DeleteWorker(in,tn->internal[i],level - 1,\r
917                                                                                 (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i ,limit);\r
918                                         }\r
919                                         if(allDone)\r
920                                         {\r
921                                                 yaffs_FreeTnode(in->myDev,tn->internal[i]);\r
922                                         tn->internal[i] = NULL;\r
923                                         }\r
924                             }\r
925                     \r
926                         }\r
927                         return (allDone) ? 1 : 0;\r
928                 }\r
929                 else if(level == 0)\r
930                 {\r
931                         for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0; i--) //NB Don't apply the limit here, always delete a whole level0\r
932                         {\r
933                             if(tn->level0[i])\r
934                         {\r
935                                         int j;\r
936                                         \r
937                                         chunkInInode = (chunkOffset << YAFFS_TNODES_LEVEL0_BITS ) + i;\r
938                                         \r
939                                         theChunk =  tn->level0[i] << in->myDev->chunkGroupBits;\r
940 \r
941                                         // Now we need to search for it\r
942                                         for(j = 0,found = 0; theChunk && j < in->myDev->chunkGroupSize && !found; j++)\r
943                                         {\r
944                                                 yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,&tags,&chunkDeleted);\r
945                                                 if(yaffs_TagsMatch(&tags,in->objectId,chunkInInode,chunkDeleted))\r
946                                                 {\r
947                                                         // found it;\r
948                                                         found = 1;\r
949                                         \r
950                                                 }\r
951                                                 else\r
952                                                 {\r
953                                                         theChunk++;\r
954                                                 }\r
955                                         }\r
956                                         \r
957                                         if(found)\r
958                                         {\r
959                                                 yaffs_DeleteChunk(in->myDev,theChunk);\r
960                                                 in->nDataChunks--;\r
961                                                 if(limit)\r
962                                                 { \r
963                                                         *limit = *limit-1;\r
964                                                 }\r
965                                         \r
966                                         }\r
967                                         \r
968                                 tn->level0[i] = 0;\r
969                             }\r
970                     \r
971                         }\r
972                         return 1;\r
973 \r
974                         \r
975                 }\r
976                 \r
977         }\r
978         \r
979         return 1;\r
980         \r
981 }\r
982 \r
983 \r
984 \r
985 \r
986 // Pruning removes any part of the file structure tree that is beyond the\r
987 // bounds of the file (ie that does not point to chunks).\r
988 //\r
989 // A file should only get pruned when its size is reduced.\r
990 //\r
991 // Before pruning, the chunks must be pulled from the tree and the\r
992 // level 0 tnode entries must be zeroed out.\r
993 // Could also use this for file deletion, but that's probably better handled\r
994 // by a special case.\r
995 \r
996 // yaffs_PruneWorker should only be called by yaffs_PruneFileStructure()\r
997 \r
998 static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn, __u32 level, int del0)\r
999 {\r
1000         int i;\r
1001         int hasData;\r
1002         \r
1003         if(tn)\r
1004         {\r
1005                 hasData = 0;\r
1006                 \r
1007                 for(i = 0; i < YAFFS_NTNODES_INTERNAL; i++)\r
1008                 {\r
1009                     if(tn->internal[i] && level > 0)\r
1010                     {\r
1011                         tn->internal[i] = yaffs_PruneWorker(dev,tn->internal[i],level - 1, ( i == 0) ? del0 : 1);\r
1012                     }\r
1013                     \r
1014                     if(tn->internal[i])\r
1015                     {\r
1016                         hasData++;\r
1017                         }\r
1018                 }\r
1019                 \r
1020                 if(hasData == 0 && del0)\r
1021                 {\r
1022                         // Free and return NULL\r
1023                         \r
1024                         yaffs_FreeTnode(dev,tn);\r
1025                         tn = NULL;\r
1026                 }\r
1027                 \r
1028         }\r
1029 \r
1030         return tn;\r
1031         \r
1032 }\r
1033 \r
1034 static int yaffs_PruneFileStructure(yaffs_Device *dev, yaffs_FileStructure *fStruct)\r
1035 {\r
1036         int i;\r
1037         int hasData;\r
1038         int done = 0;\r
1039         yaffs_Tnode *tn;\r
1040         \r
1041         if(fStruct->topLevel > 0)\r
1042         {\r
1043                 fStruct->top = yaffs_PruneWorker(dev,fStruct->top, fStruct->topLevel,0);\r
1044                 \r
1045                 // Now we have a tree with all the non-zero branches NULL but the height\r
1046                 // is the same as it was.\r
1047                 // Let's see if we can trim internal tnodes to shorten the tree.\r
1048                 // We can do this if only the 0th element in the tnode is in use \r
1049                 // (ie all the non-zero are NULL)\r
1050                 \r
1051                 while(fStruct->topLevel && !done)\r
1052                 {\r
1053                         tn = fStruct->top;\r
1054                         \r
1055                         hasData = 0;\r
1056                         for(i = 1; i <YAFFS_NTNODES_INTERNAL; i++)\r
1057                         {\r
1058                                 if(tn->internal[i])\r
1059                         {\r
1060                                 hasData++;\r
1061                                 }\r
1062                         }\r
1063                         \r
1064                         if(!hasData)\r
1065                         {\r
1066                                 fStruct->top = tn->internal[0];\r
1067                                 fStruct->topLevel--;\r
1068                                 yaffs_FreeTnode(dev,tn);\r
1069                         }\r
1070                         else\r
1071                         {\r
1072                                 done = 1;\r
1073                         }\r
1074                 }\r
1075         }\r
1076         \r
1077         return YAFFS_OK;\r
1078 }\r
1079 \r
1080 \r
1081 \r
1082 \r
1083 \r
1084 /////////////////////// End of File Structure functions. /////////////////\r
1085 \r
1086 // yaffs_CreateFreeObjects creates a bunch more objects and\r
1087 // adds them to the object free list.\r
1088 static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects)\r
1089 {\r
1090     int i;\r
1091     yaffs_Object *newObjects;\r
1092     yaffs_ObjectList *list;\r
1093     \r
1094     if(nObjects < 1) return YAFFS_OK;\r
1095    \r
1096         // make these things\r
1097         \r
1098     newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));\r
1099    \r
1100     if (!newObjects)\r
1101     {\r
1102                 T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Could not allocate more objects" TENDSTR)));\r
1103                 return YAFFS_FAIL;\r
1104     }\r
1105     \r
1106     // Hook them into the free list\r
1107     for(i = 0; i < nObjects - 1; i++)\r
1108     {\r
1109         (yaffs_Object *)newObjects[i].siblings.next = &newObjects[i+1];\r
1110     }\r
1111         \r
1112         newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;\r
1113         dev->freeObjects = newObjects;\r
1114         dev->nFreeObjects+= nObjects;\r
1115         dev->nObjectsCreated+= nObjects;\r
1116         \r
1117         // Now add this bunch of Objects to a list for freeing up.\r
1118         \r
1119         list = YMALLOC(sizeof(yaffs_ObjectList));\r
1120         if(!list)\r
1121         {\r
1122                 T(YAFFS_TRACE_ALLOCATE,(TSTR("Could not add objects to management list" TENDSTR)));\r
1123         }\r
1124         else\r
1125         {\r
1126                 list->objects = newObjects;\r
1127                 list->next = dev->allocatedObjectList;\r
1128                 dev->allocatedObjectList = list;\r
1129         }\r
1130         \r
1131         \r
1132         \r
1133         return YAFFS_OK;\r
1134 }\r
1135 \r
1136 \r
1137 // AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out\r
1138 static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev)\r
1139 {\r
1140         yaffs_Object *tn = NULL;\r
1141         \r
1142         // If there are none left make more\r
1143         if(!dev->freeObjects)\r
1144         {\r
1145                 yaffs_CreateFreeObjects(dev,YAFFS_ALLOCATION_NOBJECTS);\r
1146         }\r
1147         \r
1148         if(dev->freeObjects)\r
1149         {\r
1150                 tn = dev->freeObjects;\r
1151                 dev->freeObjects = (yaffs_Object *)(dev->freeObjects->siblings.next);\r
1152                 dev->nFreeObjects--;\r
1153                 \r
1154                 // Now sweeten it up...\r
1155         \r
1156                 memset(tn,0,sizeof(yaffs_Object));\r
1157                 tn->myDev = dev;\r
1158                 tn->chunkId = -1;\r
1159                 tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;\r
1160                 INIT_LIST_HEAD(&(tn->hardLinks));\r
1161                 INIT_LIST_HEAD(&(tn->hashLink));\r
1162                 INIT_LIST_HEAD(&tn->siblings);\r
1163                 \r
1164                 // Add it to the lost and found directory.\r
1165                 // NB Can't put root or lostNFound in lostNFound so\r
1166                 // check if lostNFound exists first\r
1167                 if(dev->lostNFoundDir)\r
1168                 {\r
1169                         yaffs_AddObjectToDirectory(dev->lostNFoundDir,tn);      \r
1170                 }\r
1171         }\r
1172         \r
1173 \r
1174         return tn;\r
1175 }\r
1176 \r
1177 static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev,int number,__u32 mode)\r
1178 {\r
1179 \r
1180         yaffs_Object *obj = yaffs_CreateNewObject(dev,number,YAFFS_OBJECT_TYPE_DIRECTORY);              \r
1181         if(obj)\r
1182         {\r
1183                 obj->fake = 1;                  // it is fake so it has no NAND presence...\r
1184                 obj->renameAllowed= 0;  // ... and we're not allowed to rename it...\r
1185                 obj->unlinkAllowed= 0;  // ... or unlink it\r
1186                 obj->deleted = 0;\r
1187                 obj->unlinked = 0;\r
1188                 obj->st_mode = mode;\r
1189                 obj->myDev = dev;\r
1190                 obj->chunkId = 0; // Not a valid chunk.\r
1191         }\r
1192         \r
1193         return obj;\r
1194         \r
1195 }\r
1196 \r
1197 \r
1198 static void yaffs_UnhashObject(yaffs_Object *tn)\r
1199 {\r
1200         int bucket;\r
1201         yaffs_Device *dev = tn->myDev;\r
1202         \r
1203         \r
1204         // If it is still linked into the bucket list, free from the list\r
1205         if(!list_empty(&tn->hashLink))\r
1206         {\r
1207                 list_del_init(&tn->hashLink);\r
1208                 bucket =  yaffs_HashFunction(tn->objectId);\r
1209                 dev->objectBucket[bucket].count--;\r
1210         }\r
1211         \r
1212 }\r
1213 \r
1214 \r
1215 // FreeObject frees up a Object and puts it back on the free list\r
1216 static void yaffs_FreeObject(yaffs_Object *tn)\r
1217 {\r
1218 \r
1219         yaffs_Device *dev = tn->myDev;\r
1220         \r
1221         yaffs_UnhashObject(tn);\r
1222         \r
1223         // Link into the free list.\r
1224         (yaffs_Object *)(tn->siblings.next) = dev->freeObjects;\r
1225         dev->freeObjects = tn;\r
1226         dev->nFreeObjects++;\r
1227 }\r
1228 \r
1229 \r
1230 \r
1231 \r
1232 static void yaffs_DeinitialiseObjects(yaffs_Device *dev)\r
1233 {\r
1234         // Free the list of allocated Objects\r
1235         \r
1236         while( dev->allocatedObjectList)\r
1237         {\r
1238                 YFREE(dev->allocatedObjectList->objects);\r
1239                 dev->allocatedObjectList =  dev->allocatedObjectList->next;\r
1240         }\r
1241         \r
1242         dev->freeObjects = NULL;\r
1243         dev->nFreeObjects = 0;\r
1244 }\r
1245 \r
1246 static void yaffs_InitialiseObjects(yaffs_Device *dev)\r
1247 {\r
1248         int i;\r
1249         \r
1250         dev->allocatedObjectList = NULL;\r
1251         dev->freeObjects = NULL;\r
1252         dev->nFreeObjects = 0;\r
1253         \r
1254         for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++)\r
1255         {\r
1256                 INIT_LIST_HEAD(&dev->objectBucket[i].list);\r
1257                 dev->objectBucket[i].count = 0; \r
1258         }\r
1259 \r
1260 }\r
1261 \r
1262 \r
1263 \r
1264 \r
1265 \r
1266 \r
1267 int yaffs_FindNiceObjectBucket(yaffs_Device *dev)\r
1268 {\r
1269         static int x = 0;\r
1270         int i;\r
1271         int l = 999;\r
1272         int lowest = 999999;\r
1273 \r
1274                 \r
1275         // First let's see if we can find one that's empty.\r
1276         \r
1277         for(i = 0; i < 10 && lowest > 0; i++)\r
1278          {\r
1279                 x++;\r
1280                 x %=  YAFFS_NOBJECT_BUCKETS;\r
1281                 if(dev->objectBucket[x].count < lowest)\r
1282                 {\r
1283                         lowest = dev->objectBucket[x].count;\r
1284                         l = x;\r
1285                 }\r
1286                 \r
1287         }\r
1288         \r
1289         // If we didn't find an empty list, then try\r
1290         // looking a bit further for a short one\r
1291         \r
1292         for(i = 0; i < 10 && lowest > 3; i++)\r
1293          {\r
1294                 x++;\r
1295                 x %=  YAFFS_NOBJECT_BUCKETS;\r
1296                 if(dev->objectBucket[x].count < lowest)\r
1297                 {\r
1298                         lowest = dev->objectBucket[x].count;\r
1299                         l = x;\r
1300                 }\r
1301                 \r
1302         }\r
1303         \r
1304         return l;\r
1305 }\r
1306 \r
1307 static int yaffs_CreateNewObjectNumber(yaffs_Device *dev)\r
1308 {\r
1309         int bucket = yaffs_FindNiceObjectBucket(dev);\r
1310         \r
1311         // Now find an object value that has not already been taken\r
1312         // by scanning the list.\r
1313         \r
1314         int found = 0;\r
1315         struct list_head *i;\r
1316         \r
1317         __u32 n = (__u32)bucket;\r
1318 \r
1319         //yaffs_CheckObjectHashSanity();        \r
1320         \r
1321         while(!found)\r
1322         {\r
1323                 found = 1;\r
1324                 n +=  YAFFS_NOBJECT_BUCKETS;\r
1325                 if(1 ||dev->objectBucket[bucket].count > 0)\r
1326                 {\r
1327                         list_for_each(i,&dev->objectBucket[bucket].list)\r
1328                         {\r
1329                                 // If there is already one in the list\r
1330                                 if(list_entry(i, yaffs_Object,hashLink)->objectId == n)\r
1331                                 {\r
1332                                         found = 0;\r
1333                                 }\r
1334                         }\r
1335                 }\r
1336         }\r
1337         \r
1338         //T(("bucket %d count %d inode %d\n",bucket,yaffs_objectBucket[bucket].count,n);\r
1339         \r
1340         return n;       \r
1341 }\r
1342 \r
1343 void yaffs_HashObject(yaffs_Object *in)\r
1344 {\r
1345         int bucket = yaffs_HashFunction(in->objectId);\r
1346         yaffs_Device *dev = in->myDev;\r
1347         \r
1348         if(!list_empty(&in->hashLink))\r
1349         {\r
1350                 //YINFO("!!!");\r
1351         }\r
1352 \r
1353         \r
1354         list_add(&in->hashLink,&dev->objectBucket[bucket].list);\r
1355         dev->objectBucket[bucket].count++;\r
1356 \r
1357 }\r
1358 \r
1359 yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,__u32 number)\r
1360 {\r
1361         int bucket = yaffs_HashFunction(number);\r
1362         struct list_head *i;\r
1363         yaffs_Object *in;\r
1364         \r
1365         list_for_each(i,&dev->objectBucket[bucket].list)\r
1366         {\r
1367                 // Look if it is in the list\r
1368                 in = list_entry(i, yaffs_Object,hashLink);\r
1369                 if(in->objectId == number)\r
1370                 {\r
1371                         return in;\r
1372                 }\r
1373         }\r
1374         \r
1375         return NULL;\r
1376 }\r
1377 \r
1378 \r
1379 \r
1380 yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type)\r
1381 {\r
1382                 \r
1383         yaffs_Object *theObject;\r
1384 \r
1385         if(number < 0)\r
1386         {\r
1387                 number = yaffs_CreateNewObjectNumber(dev);\r
1388         }\r
1389         \r
1390         theObject = yaffs_AllocateEmptyObject(dev);\r
1391         \r
1392         if(theObject)\r
1393         {\r
1394                 theObject->fake = 0;\r
1395                 theObject->renameAllowed = 1;\r
1396                 theObject->unlinkAllowed = 1;\r
1397                 theObject->objectId = number;\r
1398                 yaffs_HashObject(theObject);\r
1399                 theObject->variantType = type;\r
1400 #ifdef CONFIG_YAFFS_WINCE\r
1401                 yfsd_WinFileTimeNow(theObject->win_atime);\r
1402                 theObject->win_ctime[0] = theObject->win_mtime[0] = theObject->win_atime[0];\r
1403                 theObject->win_ctime[1] = theObject->win_mtime[1] = theObject->win_atime[1];\r
1404 \r
1405 #else\r
1406                 theObject->st_atime = theObject->st_mtime =     theObject->st_ctime = CURRENT_TIME;\r
1407 #endif\r
1408                 switch(type)\r
1409                 {\r
1410                         case YAFFS_OBJECT_TYPE_FILE: \r
1411                                 theObject->variant.fileVariant.fileSize = 0;\r
1412                                 theObject->variant.fileVariant.scannedFileSize = 0;\r
1413                                 theObject->variant.fileVariant.topLevel = 0;\r
1414                                 theObject->variant.fileVariant.top  = yaffs_GetTnode(dev);\r
1415                                 break;\r
1416                         case YAFFS_OBJECT_TYPE_DIRECTORY:\r
1417                                 INIT_LIST_HEAD(&theObject->variant.directoryVariant.children);\r
1418                                 break;\r
1419                         case YAFFS_OBJECT_TYPE_SYMLINK:\r
1420                                 // No action required\r
1421                                 break;\r
1422                         case YAFFS_OBJECT_TYPE_HARDLINK:\r
1423                                 // No action required\r
1424                                 break;\r
1425                         case YAFFS_OBJECT_TYPE_SPECIAL:\r
1426                                 // No action required\r
1427                                 break;\r
1428                         case YAFFS_OBJECT_TYPE_UNKNOWN:\r
1429                                 // todo this should not happen\r
1430                                 break;\r
1431                 }\r
1432         }\r
1433         \r
1434         return theObject;\r
1435 }\r
1436 \r
1437 yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type)\r
1438 {\r
1439         yaffs_Object *theObject = NULL;\r
1440         \r
1441         if(number > 0)\r
1442         {\r
1443                 theObject = yaffs_FindObjectByNumber(dev,number);\r
1444         }\r
1445         \r
1446         if(!theObject)\r
1447         {\r
1448                 theObject = yaffs_CreateNewObject(dev,number,type);\r
1449         }\r
1450         \r
1451         return theObject;\r
1452 \r
1453 }\r
1454 \r
1455 char *yaffs_CloneString(const char *str)\r
1456 {\r
1457         char *newStr = NULL;\r
1458         \r
1459         if(str && *str)\r
1460         {\r
1461                 newStr = YMALLOC(strlen(str) + 1);\r
1462                 strcpy(newStr,str);\r
1463         }\r
1464 \r
1465         return newStr;\r
1466         \r
1467 }\r
1468 \r
1469 //\r
1470 // Mknod (create) a new object.\r
1471 // equivalentObject only has meaning for a hard link;\r
1472 // aliasString only has meaning for a sumlink.\r
1473 // rdev only has meaning for devices (a subset of special objects)\r
1474 yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,\r
1475                                                                  yaffs_Object *parent,\r
1476                                                                  const char *name, \r
1477                                                                  __u32 mode,\r
1478                                                                  __u32 uid,\r
1479                                                                  __u32 gid,\r
1480                                                                  yaffs_Object *equivalentObject,\r
1481                                                                  const char *aliasString,\r
1482                                                                  __u32 rdev)\r
1483 {\r
1484         yaffs_Object *in;\r
1485 \r
1486         yaffs_Device *dev = parent->myDev;\r
1487         \r
1488         // Check if the entry exists. If it does then fail the call since we don't want a dup.\r
1489         if(yaffs_FindObjectByName(parent,name))\r
1490         {\r
1491                 return NULL;\r
1492         }\r
1493         \r
1494         in = yaffs_CreateNewObject(dev,-1,type);\r
1495         \r
1496         if(in)\r
1497         {\r
1498                 in->chunkId = -1;\r
1499                 in->valid = 1;\r
1500                 in->variantType = type;\r
1501 \r
1502                 in->st_mode  = mode;\r
1503                 \r
1504 #ifdef CONFIG_YAFFS_WINCE\r
1505                 yfsd_WinFileTimeNow(in->win_atime);\r
1506                 in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];\r
1507                 in->win_ctime[1] = in->win_mtime[1] = in->win_atime[0];\r
1508                 \r
1509 #else\r
1510                 in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME;\r
1511                 in->st_rdev  = rdev;\r
1512                 in->st_uid   = uid;\r
1513                 in->st_gid   = gid;\r
1514 #endif          \r
1515                 in->nDataChunks = 0;\r
1516 \r
1517                 yaffs_SetObjectName(in,name);\r
1518                 in->dirty = 1;\r
1519                 \r
1520                 yaffs_AddObjectToDirectory(parent,in);\r
1521                 \r
1522                 in->myDev = parent->myDev;\r
1523                 \r
1524                                 \r
1525                 switch(type)\r
1526                 {\r
1527                         case YAFFS_OBJECT_TYPE_SYMLINK:\r
1528                                 in->variant.symLinkVariant.alias = yaffs_CloneString(aliasString);\r
1529                                 break;\r
1530                         case YAFFS_OBJECT_TYPE_HARDLINK:\r
1531                                 in->variant.hardLinkVariant.equivalentObject = equivalentObject;\r
1532                                 in->variant.hardLinkVariant.equivalentObjectId = equivalentObject->objectId;\r
1533                                 list_add(&in->hardLinks,&equivalentObject->hardLinks);\r
1534                                 break;\r
1535                         case YAFFS_OBJECT_TYPE_FILE: // do nothing\r
1536                         case YAFFS_OBJECT_TYPE_DIRECTORY: // do nothing\r
1537                         case YAFFS_OBJECT_TYPE_SPECIAL: // do nothing\r
1538                         case YAFFS_OBJECT_TYPE_UNKNOWN:\r
1539                                 break;\r
1540                 }\r
1541 \r
1542                 if(yaffs_GetNumberOfFreeChunks(dev) <= 0 ||\r
1543                    yaffs_UpdateObjectHeader(in,name,0) < 0)\r
1544                 {\r
1545                         // Could not create the object header, fail the creation\r
1546                         yaffs_UnlinkWorker(in);\r
1547                         in = NULL;\r
1548                 }\r
1549 \r
1550         }\r
1551         \r
1552         return in;\r
1553 }\r
1554 \r
1555 yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)\r
1556 {\r
1557         return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE,parent,name,mode,uid,gid,NULL,NULL,0);\r
1558 }\r
1559 \r
1560 yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)\r
1561 {\r
1562         return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,0);\r
1563 }\r
1564 \r
1565 yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev)\r
1566 {\r
1567         return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,rdev);\r
1568 }\r
1569 \r
1570 yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,const char *alias)\r
1571 {\r
1572         return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK,parent,name,mode,uid,gid,NULL,alias,0);\r
1573 }\r
1574 \r
1575 // NB yaffs_Link returns the object id of the equivalent object.\r
1576 yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject)\r
1577 {\r
1578         // Get the real object in case we were fed a hard link as an equivalent object\r
1579         equivalentObject = yaffs_GetEquivalentObject(equivalentObject);\r
1580         \r
1581         if(yaffs_MknodObject(YAFFS_OBJECT_TYPE_HARDLINK,parent,name,0,0,0,equivalentObject,NULL,0))\r
1582         {\r
1583                 return equivalentObject;\r
1584         }\r
1585         else\r
1586         {\r
1587                 return NULL;\r
1588         }\r
1589         \r
1590 }\r
1591 \r
1592 \r
1593 static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName,int force)\r
1594 {\r
1595         int unlinkOp;\r
1596 \r
1597         if(newDir == NULL)\r
1598         {\r
1599                 newDir = obj->parent; // use the old directory\r
1600         }\r
1601 \r
1602         unlinkOp = (newDir == obj->myDev->unlinkedDir && obj->variantType == YAFFS_OBJECT_TYPE_FILE);\r
1603         \r
1604         // If the object is a file going into the unlinked directory, then it is OK to just stuff it in since\r
1605         // duplicate names are allowed.\r
1606         // Otherwise only proceed if the new name does not exist and if we're putting it into a directory.\r
1607         if( (unlinkOp|| \r
1608                  force || \r
1609                  !yaffs_FindObjectByName(newDir,newName))  &&\r
1610              newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)\r
1611         {\r
1612                 yaffs_SetObjectName(obj,newName);\r
1613                 obj->dirty = 1;\r
1614                 \r
1615                 yaffs_AddObjectToDirectory(newDir,obj);\r
1616                 \r
1617                 if(unlinkOp) obj->unlinked = 1;\r
1618                 \r
1619                 \r
1620                 if(yaffs_UpdateObjectHeader(obj,newName,0) >= 0)\r
1621                 {\r
1622                         return YAFFS_OK;\r
1623                 }\r
1624         }\r
1625         \r
1626         return YAFFS_FAIL;\r
1627 }\r
1628 \r
1629 \r
1630 \r
1631 int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName)\r
1632 {\r
1633         yaffs_Object *obj;\r
1634         int force = 0;\r
1635         \r
1636 #ifdef CONFIG_YAFFS_WINCE\r
1637         // Special case for WinCE.\r
1638         // While look-up is case insensitive, the name isn't.\r
1639         // THerefore we might want to change x.txt to X.txt\r
1640         if(oldDir == newDir && _stricmp(oldName,newName) == 0)\r
1641         {\r
1642                 force = 1;\r
1643         }       \r
1644 #endif\r
1645         \r
1646         obj = yaffs_FindObjectByName(oldDir,oldName);\r
1647         if(obj && obj->renameAllowed)\r
1648         {\r
1649                 return yaffs_ChangeObjectName(obj,newDir,newName,force);\r
1650         }\r
1651         return YAFFS_FAIL;\r
1652 }\r
1653 \r
1654 \r
1655 \r
1656 static int yaffs_CheckObjectHashSanity(yaffs_Device *dev)\r
1657 {\r
1658         // Scan the buckets and check that the lists \r
1659         // have as many members as the count says there are\r
1660         int bucket;\r
1661         int countEm;\r
1662         struct list_head *j;\r
1663         int ok = YAFFS_OK;\r
1664         \r
1665         for(bucket = 0; bucket < YAFFS_NOBJECT_BUCKETS; bucket++)\r
1666         {\r
1667                 countEm = 0;\r
1668                 \r
1669                 list_for_each(j,&dev->objectBucket[bucket].list)\r
1670                 {\r
1671                         countEm++;\r
1672                 }\r
1673                 \r
1674                 if(countEm != dev->objectBucket[bucket].count)\r
1675                 {\r
1676                         T(YAFFS_TRACE_ERROR,(TSTR("Inode hash inconsistency" TENDSTR)));\r
1677                         ok = YAFFS_FAIL;\r
1678                 }\r
1679         }\r
1680 \r
1681         return ok;\r
1682 }\r
1683 \r
1684 #if 0\r
1685 void yaffs_ObjectTest(yaffs_Device *dev)\r
1686 {\r
1687         yaffs_Object *in[1000];\r
1688         int inNo[1000];\r
1689         yaffs_Object *inold[1000];\r
1690         int i;\r
1691         int j;\r
1692         \r
1693         memset(in,0,1000*sizeof(yaffs_Object *));\r
1694         memset(inold,0,1000*sizeof(yaffs_Object *));\r
1695         \r
1696         yaffs_CheckObjectHashSanity(dev);\r
1697         \r
1698         for(j = 0; j < 10; j++)\r
1699         {\r
1700                 //T(("%d\n",j));\r
1701                 \r
1702                 for(i = 0; i < 1000; i++)\r
1703                 {\r
1704                         in[i] = yaffs_CreateNewObject(dev,-1,YAFFS_OBJECT_TYPE_FILE);\r
1705                         if(!in[i])\r
1706                         {\r
1707                                 YINFO("No more inodes");\r
1708                         }\r
1709                         else\r
1710                         {\r
1711                                 inNo[i] = in[i]->objectId;\r
1712                         }\r
1713                 }\r
1714                 \r
1715                 for(i = 0; i < 1000; i++)\r
1716                 {\r
1717                         if(yaffs_FindObjectByNumber(dev,inNo[i]) != in[i])\r
1718                         {\r
1719                                 //T(("Differnce in look up test\n"));\r
1720                         }\r
1721                         else\r
1722                         {\r
1723                                 // T(("Look up ok\n"));\r
1724                         }\r
1725                 }\r
1726                 \r
1727                 yaffs_CheckObjectHashSanity(dev);\r
1728         \r
1729                 for(i = 0; i < 1000; i+=3)\r
1730                 {\r
1731                         yaffs_FreeObject(in[i]);        \r
1732                         in[i] = NULL;\r
1733                 }\r
1734                 \r
1735         \r
1736                 yaffs_CheckObjectHashSanity(dev);\r
1737         }\r
1738                 \r
1739 }\r
1740 \r
1741 #endif\r
1742 \r
1743 /////////////////////////// Block Management and Page Allocation ///////////////////\r
1744 \r
1745 \r
1746 static int yaffs_InitialiseBlocks(yaffs_Device *dev,int nBlocks)\r
1747 {\r
1748         dev->allocationBlock = -1; // force it to get a new one\r
1749         //Todo we're assuming the malloc will pass.\r
1750         dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));\r
1751         if(dev->blockInfo)\r
1752         {\r
1753                 memset(dev->blockInfo,0,nBlocks * sizeof(yaffs_BlockInfo));\r
1754                 return YAFFS_OK;\r
1755         }\r
1756         return YAFFS_FAIL;\r
1757         \r
1758 }\r
1759 \r
1760 static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)\r
1761 {\r
1762         YFREE(dev->blockInfo);\r
1763 }\r
1764 \r
1765 // FindDiretiestBlock is used to select the dirtiest block (or close enough)\r
1766 // for garbage collection.\r
1767 \r
1768 static int yaffs_FindDirtiestBlock(yaffs_Device *dev)\r
1769 {\r
1770 \r
1771         int b = dev->currentDirtyChecker;\r
1772         \r
1773         int i;\r
1774         int dirtiest = -1;\r
1775         int pagesInUse = 100; // silly big number\r
1776         yaffs_BlockInfo *bi;\r
1777         \r
1778         for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++)\r
1779         {\r
1780                 b++;\r
1781                 if ( b < dev->startBlock || b > dev->endBlock)\r
1782                 {\r
1783                         b =  dev->startBlock;\r
1784                 }\r
1785 \r
1786                 if(b < dev->startBlock || b > dev->endBlock)\r
1787                 {\r
1788                         T(YAFFS_TRACE_ERROR,(TSTR("**>> Block %d is not valid" TENDSTR),b));\r
1789                         YBUG();\r
1790                 }\r
1791                 \r
1792                 bi = yaffs_GetBlockInfo(dev,b);\r
1793                 \r
1794                 if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&\r
1795                    bi->pagesInUse < pagesInUse)\r
1796                 {\r
1797                         dirtiest = b;\r
1798                         pagesInUse = bi->pagesInUse;\r
1799                 }\r
1800         }\r
1801         \r
1802         dev->currentDirtyChecker = b;\r
1803         \r
1804         return dirtiest;\r
1805 }\r
1806 \r
1807 \r
1808 static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)\r
1809 {\r
1810         yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,blockNo);\r
1811         \r
1812         int erasedOk = 0;\r
1813         \r
1814         // If the block is still healthy erase it and mark as clean.\r
1815         // If the block has had a data failure, then retire it.\r
1816         bi->blockState = YAFFS_BLOCK_STATE_DIRTY;\r
1817 \r
1818         if(!bi->needsRetiring)\r
1819         {\r
1820                 erasedOk = yaffs_EraseBlockInNAND(dev,blockNo);\r
1821                 if(!erasedOk)\r
1822                 {\r
1823                         T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Erasure failed %d" TENDSTR),blockNo));\r
1824                 }\r
1825         }\r
1826         \r
1827         if( erasedOk )\r
1828         {\r
1829                 bi->blockState = YAFFS_BLOCK_STATE_EMPTY;\r
1830                 dev->nErasedBlocks++;\r
1831                 bi->pagesInUse = 0;\r
1832                 bi->pageBits = 0;\r
1833         \r
1834                 T(YAFFS_TRACE_ERASE,(TSTR("Erased block %d" TENDSTR),blockNo));\r
1835         }\r
1836         else\r
1837         {\r
1838                 yaffs_RetireBlock(dev,blockNo);\r
1839                 T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Block %d retired" TENDSTR),blockNo));\r
1840         }\r
1841 }\r
1842 \r
1843 \r
1844 static int yaffs_FindBlockForAllocation(yaffs_Device *dev)\r
1845 {\r
1846         int i;\r
1847         yaffs_BlockInfo *bi;\r
1848         \r
1849         if(dev->nErasedBlocks < 1)\r
1850         {\r
1851                 // Hoosterman we've got a problem.\r
1852                 // Can't get space to gc\r
1853                 T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: no space during gc" TENDSTR)));\r
1854 \r
1855                 return -1;\r
1856         }\r
1857         \r
1858         // Find an empty block.\r
1859         \r
1860         for(i = dev->startBlock; i <= dev->endBlock; i++)\r
1861         {\r
1862                 bi = yaffs_GetBlockInfo(dev,i);\r
1863                 if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)\r
1864                 {\r
1865                         bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;\r
1866                         dev->nErasedBlocks--;                   \r
1867                         return i;\r
1868                 }\r
1869         }\r
1870         \r
1871         return -1;      \r
1872 }\r
1873 \r
1874 \r
1875 \r
1876 static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)\r
1877 {\r
1878         int retVal;\r
1879         yaffs_BlockInfo *bi;\r
1880         \r
1881         if(dev->allocationBlock < 0)\r
1882         {\r
1883                 // Get next block to allocate off\r
1884                 dev->allocationBlock = yaffs_FindBlockForAllocation(dev);\r
1885                 dev->allocationPage = 0;\r
1886         }\r
1887         \r
1888         if(!useReserve &&  dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)\r
1889         {\r
1890                 // Not enough space to allocate unless we're allowed to use the reserve.\r
1891                 return -1;\r
1892         }\r
1893         \r
1894         // Next page please....\r
1895         if(dev->allocationBlock >= 0)\r
1896         {\r
1897                 bi = yaffs_GetBlockInfo(dev,dev->allocationBlock);\r
1898                 \r
1899                 retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) + \r
1900                                   dev->allocationPage;\r
1901                 bi->pagesInUse++;\r
1902                 bi->pageBits |= (1 << (dev->allocationPage));\r
1903 \r
1904                 dev->allocationPage++;\r
1905                 \r
1906                 dev->nFreeChunks--;\r
1907                 \r
1908                 // If the block is full set the state to full\r
1909                 if(dev->allocationPage >= YAFFS_CHUNKS_PER_BLOCK)\r
1910                 {\r
1911                         bi->blockState = YAFFS_BLOCK_STATE_FULL;\r
1912                         dev->allocationBlock = -1;\r
1913                 }\r
1914 \r
1915 \r
1916                 return retVal;\r
1917                 \r
1918         }\r
1919         T(YAFFS_TRACE_ERROR,(TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));\r
1920 \r
1921         return -1;      \r
1922 }\r
1923 \r
1924 // To determine if we have enough space we just look at the \r
1925 // number of erased blocks.\r
1926 // The cache is allowed to use reserved blocks.\r
1927 \r
1928 int yaffs_CheckSpaceForChunkCache(yaffs_Device *dev)\r
1929 {\r
1930         return (dev->nErasedBlocks >= YAFFS_RESERVED_BLOCKS);\r
1931 }\r
1932 \r
1933 \r
1934 int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)\r
1935 {\r
1936         int oldChunk;\r
1937         int newChunk;\r
1938         __u32 mask;\r
1939         \r
1940         \r
1941         yaffs_Spare spare;\r
1942         yaffs_Tags  tags;\r
1943                 __u8  buffer[YAFFS_BYTES_PER_CHUNK];\r
1944         \r
1945         yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block);\r
1946         \r
1947         yaffs_Object *object;\r
1948 \r
1949         //T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));        \r
1950         \r
1951         for(mask = 1,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK; \r
1952             mask && bi->pageBits;\r
1953             mask <<= 1, oldChunk++ )\r
1954         {\r
1955                 if(bi->pageBits & mask)\r
1956                 {\r
1957                         \r
1958                         // This page is in use and needs to be copied off\r
1959                         \r
1960                         dev->nGCCopies++;\r
1961                         \r
1962                         //T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk));\r
1963                         \r
1964                         yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare,1);\r
1965                         \r
1966                         yaffs_GetTagsFromSpare(dev,&spare,&tags);\r
1967 \r
1968                         object = yaffs_FindObjectByNumber(dev,tags.objectId);\r
1969                         \r
1970                         if(object && object->deleted && tags.chunkId != 0)\r
1971                         {\r
1972                                 // Data chunk in a deleted file, throw it away\r
1973                                 // It's a deleted data chunk,\r
1974                                 // No need to copy this, just forget about it and fix up the\r
1975                                 // object.\r
1976                                 \r
1977                                 yaffs_PutChunkIntoFile(object, tags.chunkId, 0,0); \r
1978                                 object->nDataChunks--;\r
1979                         }\r
1980                         else if( 0 /* Todo object && object->deleted && object->nDataChunks == 0 */)\r
1981                         {\r
1982                                 // Deleted object header with no data chunks.\r
1983                                 // Can be discarded\r
1984                                 object->chunkId = 0;\r
1985                                 //Todo some clean up\r
1986                                 \r
1987                         }\r
1988                         else if(object)\r
1989                         {\r
1990                                 // It's either a data chunk in a live file or\r
1991                                 // an ObjectHeader, so we're interested in it.\r
1992                                 // NB Need to keep the ObjectHeaders of deleted files\r
1993                                 // until the whole file has been deleted off\r
1994                                 tags.serialNumber++;\r
1995                                 yaffs_LoadTagsIntoSpare(&spare,&tags);\r
1996 \r
1997 \r
1998                                 newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);\r
1999                         \r
2000                                 if(newChunk < 0)\r
2001                                 {\r
2002                                         return YAFFS_FAIL;\r
2003                                 }\r
2004                         \r
2005                                 // Ok, now fix up the Tnodes etc.\r
2006                         \r
2007                                 if(tags.chunkId == 0)\r
2008                                 {\r
2009                                         // It's a header\r
2010                                         object->chunkId = newChunk;\r
2011                                 }\r
2012                                 else\r
2013                                 {\r
2014                                         // It's a data chunk\r
2015                                         yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);\r
2016                                 }\r
2017                         }\r
2018                         \r
2019                         yaffs_DeleteChunk(dev,oldChunk);                        \r
2020                         \r
2021                 }\r
2022         }\r
2023 \r
2024         return YAFFS_OK;\r
2025 }\r
2026 \r
2027 \r
2028 static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev)\r
2029 {\r
2030         // todo find a file to delete\r
2031         struct list_head *i;    \r
2032         yaffs_Object *l;\r
2033 \r
2034 \r
2035         // To the free chunks add the chunks that are in the deleted unlinked files.\r
2036         list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)\r
2037         {\r
2038                 l = list_entry(i, yaffs_Object,siblings);\r
2039                 if(l->deleted)\r
2040                 {\r
2041                         return l;                       \r
2042                 }\r
2043         }       \r
2044         return NULL;\r
2045 }\r
2046 \r
2047 static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev)\r
2048 {\r
2049         // This does background deletion on unlinked files.. only deleted ones.\r
2050         // If we don't have a file we're working on then find one\r
2051         if(!dev->unlinkedDeletion && dev->nDeletedFiles > 0)\r
2052         {\r
2053                 dev->unlinkedDeletion = yaffs_FindDeletedUnlinkedFile(dev);\r
2054         }\r
2055         \r
2056         // OK, we're working on a file...\r
2057         if(dev->unlinkedDeletion)\r
2058         {\r
2059                 yaffs_Object *obj = dev->unlinkedDeletion;\r
2060                 int delresult;\r
2061                 int limit; // Number of chunks to delete in a file.\r
2062                                    // NB this can be exceeded, but not by much.\r
2063                                    \r
2064                 limit = 5;\r
2065 #if 0                              \r
2066                 if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER))\r
2067                 {\r
2068                         limit = 50; // Doing GC soon, so dig deeper     \r
2069                 }\r
2070                 else\r
2071                 {\r
2072                         limit = 5;\r
2073                 }\r
2074 #endif\r
2075         \r
2076                 delresult = yaffs_DeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0,&limit);\r
2077                 \r
2078                 if(obj->nDataChunks == 0)\r
2079                 {\r
2080                         // Done all the deleting of data chunks.\r
2081                         // Now dump the header and clean up\r
2082                         yaffs_FreeTnode(dev,obj->variant.fileVariant.top);\r
2083                         yaffs_DoGenericObjectDeletion(obj);\r
2084                         dev->nDeletedFiles--;\r
2085                         dev->nUnlinkedFiles--;\r
2086                         dev->nBackgroundDeletions++;\r
2087                         dev->unlinkedDeletion = NULL;   \r
2088                 }\r
2089         }\r
2090 }\r
2091 \r
2092 \r
2093 \r
2094 static int yaffs_CheckGarbageCollection(yaffs_Device *dev)\r
2095 {\r
2096         int block;\r
2097         \r
2098         yaffs_DoUnlinkedFileDeletion(dev);\r
2099         \r
2100         if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER))\r
2101         {\r
2102                 dev->garbageCollectionRequired = 1;\r
2103         }       \r
2104         \r
2105         if(dev->garbageCollectionRequired)\r
2106         {\r
2107                 dev->garbageCollections++;\r
2108                 dev->garbageCollectionRequired = 0;\r
2109                 if(dev->blockSelectedForGC >= 0)\r
2110                 {\r
2111                         block = dev->blockSelectedForGC;\r
2112                 }\r
2113                 else\r
2114                 {\r
2115                         block = yaffs_FindDirtiestBlock(dev);\r
2116                 }\r
2117                 \r
2118                 if(block >= 0)\r
2119                 {\r
2120                         return yaffs_GarbageCollectBlock(dev,block);\r
2121                 }       \r
2122                 else\r
2123                 {\r
2124                         return YAFFS_FAIL;\r
2125                 }\r
2126         }\r
2127 \r
2128         return YAFFS_OK;\r
2129 }\r
2130 \r
2131 \r
2132 //////////////////////////// TAGS ///////////////////////////////////////\r
2133 \r
2134 static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr)\r
2135 {\r
2136         yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;\r
2137         \r
2138         yaffs_CalcTagsECC(tagsPtr);\r
2139         \r
2140         sparePtr->tagByte0 = tu->asBytes[0];\r
2141         sparePtr->tagByte1 = tu->asBytes[1];\r
2142         sparePtr->tagByte2 = tu->asBytes[2];\r
2143         sparePtr->tagByte3 = tu->asBytes[3];\r
2144         sparePtr->tagByte4 = tu->asBytes[4];\r
2145         sparePtr->tagByte5 = tu->asBytes[5];\r
2146         sparePtr->tagByte6 = tu->asBytes[6];\r
2147         sparePtr->tagByte7 = tu->asBytes[7];\r
2148 }\r
2149 \r
2150 static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)\r
2151 {\r
2152         yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;\r
2153         int result;\r
2154 \r
2155         tu->asBytes[0]= sparePtr->tagByte0;\r
2156         tu->asBytes[1]= sparePtr->tagByte1;\r
2157         tu->asBytes[2]= sparePtr->tagByte2;\r
2158         tu->asBytes[3]= sparePtr->tagByte3;\r
2159         tu->asBytes[4]= sparePtr->tagByte4;\r
2160         tu->asBytes[5]= sparePtr->tagByte5;\r
2161         tu->asBytes[6]= sparePtr->tagByte6;\r
2162         tu->asBytes[7]= sparePtr->tagByte7;\r
2163         \r
2164         result =  yaffs_CheckECCOnTags(tagsPtr);\r
2165         if(result> 0)\r
2166         {\r
2167                 dev->tagsEccFixed++;\r
2168         }\r
2169         else if(result <0)\r
2170         {\r
2171                 dev->tagsEccUnfixed++;\r
2172         }\r
2173 }\r
2174 \r
2175 static void yaffs_SpareInitialise(yaffs_Spare *spare)\r
2176 {\r
2177         memset(spare,0xFF,sizeof(yaffs_Spare));\r
2178 }\r
2179 \r
2180 static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted)\r
2181 {\r
2182         if(tags)\r
2183         {\r
2184                 yaffs_Spare spare;\r
2185                 if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,NULL,&spare,1) == YAFFS_OK)\r
2186                 {\r
2187                         *chunkDeleted = (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0;\r
2188                         yaffs_GetTagsFromSpare(dev,&spare,tags);\r
2189                         return YAFFS_OK;\r
2190                 }\r
2191                 else\r
2192                 {\r
2193                         return YAFFS_FAIL;\r
2194                 }\r
2195         }\r
2196         \r
2197         return YAFFS_OK;\r
2198 }\r
2199 \r
2200 #if 0\r
2201 static int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_Tags *tags)\r
2202 {\r
2203         // NB There must be tags, data is optional\r
2204         // If there is data, then an ECC is calculated on it.\r
2205         \r
2206         yaffs_Spare spare;\r
2207         \r
2208         if(!tags)\r
2209         {\r
2210                 return YAFFS_FAIL;\r
2211         }\r
2212         \r
2213         yaffs_SpareInitialise(&spare);\r
2214         \r
2215 \r
2216         if(buffer)\r
2217         {\r
2218                 yaffs_CalcECC(buffer,&spare);\r
2219         }\r
2220         \r
2221         yaffs_LoadTagsIntoSpare(&spare,tags);\r
2222         \r
2223         return yaffs_WriteChunkToNAND(dev,chunkInNAND,buffer,&spare);\r
2224         \r
2225 }\r
2226 #endif\r
2227 \r
2228 \r
2229 static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_Tags *tags, int useReserve)\r
2230 {\r
2231         // NB There must be tags, data is optional\r
2232         // If there is data, then an ECC is calculated on it.\r
2233         \r
2234         yaffs_Spare spare;\r
2235         \r
2236         if(!tags)\r
2237         {\r
2238                 return YAFFS_FAIL;\r
2239         }\r
2240         \r
2241         yaffs_SpareInitialise(&spare);\r
2242         \r
2243 \r
2244         if(buffer)\r
2245         {\r
2246                 yaffs_CalcECC(buffer,&spare);\r
2247         }\r
2248         \r
2249         yaffs_LoadTagsIntoSpare(&spare,tags);\r
2250         \r
2251         return yaffs_WriteNewChunkToNAND(dev,buffer,&spare,useReserve);\r
2252         \r
2253 }\r
2254 \r
2255 static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted)\r
2256 {\r
2257         return  (  tags->chunkId == chunkInObject &&\r
2258                            tags->objectId == objectId &&\r
2259                            !chunkDeleted) ? 1 : 0;\r
2260         \r
2261 }\r
2262 \r
2263 \r
2264 \r
2265 int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)\r
2266 {\r
2267         //Get the Tnode, then get the level 0 offset chunk offset\r
2268     yaffs_Tnode *tn;     \r
2269     int theChunk = -1;\r
2270     yaffs_Tags localTags;\r
2271     int i;\r
2272     int found = 0;\r
2273     int chunkDeleted;\r
2274     \r
2275     yaffs_Device *dev = in->myDev;\r
2276     \r
2277     \r
2278     if(!tags)\r
2279     {\r
2280         // Passed a NULL, so use our own tags space\r
2281         tags = &localTags;\r
2282     }\r
2283     \r
2284     tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);\r
2285     \r
2286     if(tn)\r
2287     {\r
2288                 theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;\r
2289 \r
2290                 // Now we need to do the shifting etc and search for it\r
2291                 for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)\r
2292                 {\r
2293                         yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted);\r
2294                         if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted))\r
2295                         {\r
2296                                 // found it;\r
2297                                 found = 1;\r
2298                         }\r
2299                         else\r
2300                         {\r
2301                                 theChunk++;\r
2302                         }\r
2303                 }\r
2304     }\r
2305     return found ? theChunk : -1;\r
2306 }\r
2307 \r
2308 int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)\r
2309 {\r
2310         //Get the Tnode, then get the level 0 offset chunk offset\r
2311     yaffs_Tnode *tn;     \r
2312     int theChunk = -1;\r
2313     yaffs_Tags localTags;\r
2314     int i;\r
2315     int found = 0;\r
2316     yaffs_Device *dev = in->myDev;\r
2317     int chunkDeleted;\r
2318     \r
2319     if(!tags)\r
2320     {\r
2321         // Passed a NULL, so use our own tags space\r
2322         tags = &localTags;\r
2323     }\r
2324     \r
2325     tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);\r
2326     \r
2327     if(tn)\r
2328     {\r
2329     \r
2330                 theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;\r
2331     \r
2332                 // Now we need to do the shifting etc and search for it\r
2333                 for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)\r
2334                 {\r
2335                         yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted);\r
2336                         if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted))\r
2337                         {\r
2338                                 // found it;\r
2339                                 found = 1;\r
2340                         }\r
2341                         else\r
2342                         {\r
2343                                 theChunk++;\r
2344                         }\r
2345                 }\r
2346     \r
2347                 // Delete the entry in the filestructure\r
2348                 if(found)\r
2349                 {\r
2350                         tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = 0;\r
2351                 }\r
2352     }\r
2353     else\r
2354     {\r
2355         //T(("No level 0 found for %d\n", chunkInInode));\r
2356     }\r
2357     \r
2358     if(!found)\r
2359     {\r
2360         //T(("Could not find %d to delete\n",chunkInInode));\r
2361     }\r
2362     return found ? theChunk : -1;\r
2363 }\r
2364 \r
2365 \r
2366 #ifdef YAFFS_PARANOID\r
2367 \r
2368 static int yaffs_CheckFileSanity(yaffs_Object *in)\r
2369 {\r
2370         int chunk;\r
2371         int nChunks;\r
2372         int fSize;\r
2373         int failed = 0;\r
2374         int objId;\r
2375         yaffs_Tnode *tn;\r
2376     yaffs_Tags localTags;\r
2377     yaffs_Tags *tags = &localTags;\r
2378     int theChunk;\r
2379     int chunkDeleted;\r
2380     \r
2381         \r
2382         if(in->variantType != YAFFS_OBJECT_TYPE_FILE)\r
2383         {\r
2384                 //T(("Object not a file\n"));\r
2385                 return YAFFS_FAIL;\r
2386         }\r
2387         \r
2388         objId = in->objectId;\r
2389         fSize  = in->variant.fileVariant.fileSize;\r
2390         nChunks = (fSize + YAFFS_BYTES_PER_CHUNK -1)/YAFFS_BYTES_PER_CHUNK;\r
2391         \r
2392         for(chunk = 1; chunk <= nChunks; chunk++)\r
2393         {\r
2394                 tn = yaffs_FindLevel0Tnode(in->myDev,&in->variant.fileVariant, chunk);\r
2395     \r
2396                 if(tn)\r
2397                 {\r
2398     \r
2399                         theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << in->myDev->chunkGroupBits;\r
2400     \r
2401 \r
2402                                 yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,tags,&chunkDeleted);\r
2403                                 if(yaffs_TagsMatch(tags,in->objectId,chunk,chunkDeleted))\r
2404                                 {\r
2405                                         // found it;\r
2406                                 \r
2407                                 }\r
2408                                 else\r
2409                                 {\r
2410                                         //T(("File problem file [%d,%d] NAND %d  tags[%d,%d]\n",\r
2411                                         //              objId,chunk,theChunk,tags->chunkId,tags->objectId);\r
2412                                                         \r
2413                                         failed = 1;\r
2414                                                                 \r
2415                                 }\r
2416     \r
2417                 }\r
2418                 else\r
2419                 {\r
2420                         //T(("No level 0 found for %d\n", chunk));\r
2421                 }\r
2422         }\r
2423         \r
2424         return failed ? YAFFS_FAIL : YAFFS_OK;\r
2425 }\r
2426 \r
2427 #endif\r
2428 \r
2429 static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan)\r
2430 {\r
2431         yaffs_Tnode *tn;\r
2432         yaffs_Device *dev = in->myDev;\r
2433         int existingChunk;\r
2434         yaffs_Tags existingTags;\r
2435         yaffs_Tags newTags;\r
2436         unsigned existingSerial, newSerial;\r
2437         \r
2438         int newChunkDeleted;\r
2439         \r
2440         \r
2441         tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);\r
2442         if(!tn)\r
2443         {\r
2444                 return YAFFS_FAIL;\r
2445         }\r
2446 \r
2447         existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK];            \r
2448         \r
2449         if(inScan)\r
2450         {\r
2451                 // If we're scanning then we need to test for duplicates\r
2452                 // NB This does not need to be efficient since it should only ever \r
2453                 // happen when the power fails during a write, then only one\r
2454                 // chunk should ever be affected.\r
2455         \r
2456                 \r
2457                 if(existingChunk != 0)\r
2458                 {\r
2459                         // NB Right now existing chunk will not be real chunkId if the device >= 32MB\r
2460                         //    thus we have to do a FindChunkInFile to get the real chunk id.\r
2461                         //\r
2462                         // We have a duplicate now we need to decide which one to use\r
2463                         // To do this we get both sets of tags and compare serial numbers.\r
2464                         yaffs_ReadChunkTagsFromNAND(dev,chunkInNAND, &newTags,&newChunkDeleted);\r
2465                         \r
2466                         \r
2467                         // Do a proper find\r
2468                         existingChunk = yaffs_FindChunkInFile(in,chunkInInode, &existingTags);\r
2469 \r
2470                         if(existingChunk <=0)\r
2471                         {\r
2472                                 //Hoosterman - how did this happen?\r
2473                                 \r
2474                                 T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: existing chunk < 0 in scan" TENDSTR)));\r
2475 \r
2476                         }\r
2477 \r
2478                         \r
2479                         // NB The deleted flags should be false, otherwise the chunks will \r
2480                         // not be loaded during a scan\r
2481                         \r
2482                         newSerial = newTags.serialNumber;\r
2483                         existingSerial = existingTags.serialNumber;\r
2484                         \r
2485                         if( existingChunk <= 0 ||\r
2486                             ((existingSerial+1) & 3) == newSerial)\r
2487                         {\r
2488                                 // Use new\r
2489                                 // Delete the old one and drop through to update the tnode\r
2490                                 yaffs_DeleteChunk(dev,existingChunk);\r
2491                         }\r
2492                         else\r
2493                         {\r
2494                                 // Use existing.\r
2495                                 // Delete the new one and return early so that the tnode isn't changed\r
2496                                 yaffs_DeleteChunk(dev,chunkInNAND);\r
2497                                 return YAFFS_OK;\r
2498                         }\r
2499                 }\r
2500 \r
2501         }\r
2502                 \r
2503         if(existingChunk == 0)\r
2504         {\r
2505                 in->nDataChunks++;\r
2506         }\r
2507         \r
2508         tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = (chunkInNAND >> dev->chunkGroupBits);\r
2509         \r
2510         return YAFFS_OK;\r
2511 }\r
2512 \r
2513 \r
2514 \r
2515 int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer)\r
2516 {\r
2517     int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL);\r
2518     \r
2519     if(chunkInNAND >= 0)\r
2520     {\r
2521                 return yaffs_ReadChunkFromNAND(in->myDev,chunkInNAND,buffer,NULL,1);\r
2522         }\r
2523         else\r
2524         {\r
2525                 return 0;\r
2526         }\r
2527 \r
2528 }\r
2529 \r
2530 \r
2531 static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId)\r
2532 {\r
2533         int block;\r
2534         int page;\r
2535         yaffs_Spare spare;\r
2536         yaffs_BlockInfo *bi;\r
2537         \r
2538         if(chunkId <= 0) return;        \r
2539                 \r
2540         block = chunkId / YAFFS_CHUNKS_PER_BLOCK;\r
2541         page = chunkId % YAFFS_CHUNKS_PER_BLOCK;\r
2542         yaffs_SpareInitialise(&spare);\r
2543         \r
2544         spare.pageStatus = 0; // To mark it as deleted.\r
2545 \r
2546         \r
2547         yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);\r
2548         yaffs_HandleUpdateChunk(dev,chunkId,&spare);\r
2549         bi = yaffs_GetBlockInfo(dev,block);\r
2550                         \r
2551         \r
2552         // Pull out of the management area.\r
2553         // If the whole block became dirty, this will kick off an erasure.\r
2554         if(     bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING ||\r
2555             bi->blockState == YAFFS_BLOCK_STATE_FULL)\r
2556         {\r
2557                 dev->nFreeChunks++;\r
2558 \r
2559                 bi->pageBits &= ~(1 << page);\r
2560                 bi->pagesInUse--;\r
2561                 \r
2562                 if(bi->pagesInUse == 0 &&\r
2563                bi->blockState == YAFFS_BLOCK_STATE_FULL)\r
2564             {\r
2565                 yaffs_BlockBecameDirty(dev,block);\r
2566             }\r
2567 \r
2568         }\r
2569         else\r
2570         {\r
2571                 // T(("Bad news deleting chunk %d\n",chunkId));\r
2572         }\r
2573         \r
2574 }\r
2575 \r
2576 \r
2577 \r
2578 \r
2579 int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *buffer,int nBytes,int useReserve)\r
2580 {\r
2581         // Find old chunk Need to do this to get serial number\r
2582         // Write new one and patch into tree.\r
2583         // Invalidate old tags.\r
2584 \r
2585     int prevChunkId;\r
2586     yaffs_Tags prevTags;\r
2587     \r
2588     int newChunkId;\r
2589     yaffs_Tags newTags;\r
2590 \r
2591     yaffs_Device *dev = in->myDev;    \r
2592 \r
2593         yaffs_CheckGarbageCollection(dev);\r
2594 \r
2595         // Get the previous chunk at this location in the file if it exists\r
2596     prevChunkId  = yaffs_FindChunkInFile(in,chunkInInode,&prevTags);\r
2597     \r
2598     // Set up new tags\r
2599         newTags.chunkId = chunkInInode;\r
2600         newTags.objectId = in->objectId;\r
2601         newTags.serialNumber = (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;\r
2602         newTags.byteCount = nBytes;\r
2603         newTags.unusedStuff = 0xFFFFFFFF;\r
2604                 \r
2605         yaffs_CalcTagsECC(&newTags);\r
2606 \r
2607         newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);\r
2608         if(newChunkId >= 0)\r
2609         {\r
2610                 yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0);\r
2611                 \r
2612                 \r
2613                 if(prevChunkId >= 0)\r
2614                 {\r
2615                         yaffs_DeleteChunk(dev,prevChunkId);\r
2616         \r
2617                 }\r
2618                 \r
2619                 yaffs_CheckFileSanity(in);\r
2620         }\r
2621         return newChunkId;\r
2622 \r
2623 \r
2624 \r
2625 \r
2626 \r
2627 }\r
2628 \r
2629 \r
2630 // UpdateObjectHeader updates the header on NAND for an object.\r
2631 // If name is not NULL, then that new name is used.\r
2632 //\r
2633 int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force)\r
2634 {\r
2635 \r
2636         yaffs_Device *dev = in->myDev;\r
2637         \r
2638     int prevChunkId;\r
2639     \r
2640     int newChunkId;\r
2641     yaffs_Tags newTags;\r
2642     __u8 bufferNew[YAFFS_BYTES_PER_CHUNK];\r
2643     __u8 bufferOld[YAFFS_BYTES_PER_CHUNK];\r
2644     \r
2645     yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew;\r
2646     yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;\r
2647 \r
2648     \r
2649     if(!in->fake || force)\r
2650     {\r
2651   \r
2652                 yaffs_CheckGarbageCollection(dev);              \r
2653     \r
2654                 memset(bufferNew,0xFF,YAFFS_BYTES_PER_CHUNK);\r
2655     \r
2656                 prevChunkId = in->chunkId;\r
2657     \r
2658                 if(prevChunkId >= 0)\r
2659                 {\r
2660                         yaffs_ReadChunkFromNAND(dev,prevChunkId,bufferOld,NULL,1);      \r
2661                 }\r
2662 \r
2663                 // Header data\r
2664                 oh->type = in->variantType;\r
2665                 \r
2666                 oh->st_mode = in->st_mode;\r
2667 \r
2668 #ifdef CONFIG_YAFFS_WINCE\r
2669                 oh->win_atime[0] = in->win_atime[0];\r
2670                 oh->win_ctime[0] = in->win_ctime[0];\r
2671                 oh->win_mtime[0] = in->win_mtime[0];\r
2672                 oh->win_atime[1] = in->win_atime[1];\r
2673                 oh->win_ctime[1] = in->win_ctime[1];\r
2674                 oh->win_mtime[1] = in->win_mtime[1];\r
2675 #else\r
2676                 oh->st_uid = in->st_uid;\r
2677                 oh->st_gid = in->st_gid;\r
2678                 oh->st_atime = in->st_atime;\r
2679                 oh->st_mtime = in->st_mtime;\r
2680                 oh->st_ctime = in->st_ctime;\r
2681                 oh->st_rdev = in->st_rdev;\r
2682 #endif  \r
2683                 if(in->parent)\r
2684                 {\r
2685                         oh->parentObjectId = in->parent->objectId;\r
2686                 }\r
2687                 else\r
2688                 {\r
2689                         oh->parentObjectId = 0;\r
2690                 }\r
2691                 \r
2692                 //oh->sum = in->sum;\r
2693                 if(name && *name)\r
2694                 {\r
2695                         memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);\r
2696                         strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);\r
2697                 }\r
2698                 else if(prevChunkId)\r
2699                 {       \r
2700                         memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1);\r
2701                 }\r
2702                 else\r
2703                 {\r
2704                         memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);   \r
2705                 }\r
2706         \r
2707                 switch(in->variantType)\r
2708                 {\r
2709                         case YAFFS_OBJECT_TYPE_UNKNOWN:         \r
2710                                 // Should not happen\r
2711                                 break;\r
2712                         case YAFFS_OBJECT_TYPE_FILE:\r
2713                                 oh->fileSize = in->variant.fileVariant.fileSize;\r
2714                                 break;\r
2715                         case YAFFS_OBJECT_TYPE_HARDLINK:\r
2716                                 oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId;\r
2717                                 break;\r
2718                         case YAFFS_OBJECT_TYPE_SPECIAL: \r
2719                                 // Do nothing\r
2720                                 break;\r
2721                         case YAFFS_OBJECT_TYPE_DIRECTORY:       \r
2722                                 // Do nothing\r
2723                                 break;\r
2724                         case YAFFS_OBJECT_TYPE_SYMLINK:\r
2725                                 strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH);\r
2726                                 oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;\r
2727                                 break;\r
2728                 }\r
2729 \r
2730                 // Tags\r
2731                 in->serial++;\r
2732                 newTags.chunkId = 0;\r
2733                 newTags.objectId = in->objectId;\r
2734                 newTags.serialNumber = in->serial;\r
2735                 newTags.byteCount =   0xFFFFFFFF;\r
2736                 newTags.unusedStuff = 0xFFFFFFFF;\r
2737         \r
2738                 yaffs_CalcTagsECC(&newTags);\r
2739         \r
2740 \r
2741                 // Create new chunk in NAND\r
2742                 newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1);\r
2743     \r
2744                 if(newChunkId >= 0)\r
2745                 {\r
2746                 \r
2747                         in->chunkId = newChunkId;               \r
2748                 \r
2749                         if(prevChunkId >= 0)\r
2750                         {\r
2751                                 yaffs_DeleteChunk(dev,prevChunkId);\r
2752                         }\r
2753                 \r
2754                         in->dirty = 0;\r
2755                 }\r
2756                 \r
2757                 return newChunkId;\r
2758 \r
2759     }\r
2760     return 0;\r
2761 }\r
2762 \r
2763 \r
2764 /////////////////////// Short Operations Cache ////////////////////////////////\r
2765 //      In many siturations where there is no high level buffering (eg WinCE) a lot of\r
2766 //      reads might be short sequential reads, and a lot of writes may be short \r
2767 //  sequential writes. eg. scanning/writing a jpeg file.\r
2768 //      In these cases, a short read/write cache can provide a huge perfomance benefit \r
2769 //  with dumb-as-a-rock code.\r
2770 //  There are a limited number (~10) of cache chunks per device so that we don't\r
2771 //  need a very intelligent search.\r
2772 \r
2773 \r
2774 #ifdef CONFIG_YAFFS_SHORT_OP_CACHE\r
2775 \r
2776 \r
2777 static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)\r
2778 {\r
2779         yaffs_Device *dev = obj->myDev;\r
2780         int lowest;\r
2781         int i;\r
2782         yaffs_ChunkCache *cache;\r
2783         int chunkWritten;\r
2784         int nBytes;\r
2785         \r
2786         do{\r
2787                 cache = NULL;\r
2788                 \r
2789                 // Find the dirty cache for this object with the lowest chunk id.\r
2790                 for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
2791                 {\r
2792                         if(dev->srCache[i].object == obj &&\r
2793                            dev->srCache[i].dirty)\r
2794                         {\r
2795                                 if(!cache ||  dev->srCache[i].chunkId < lowest)\r
2796                                 {\r
2797                                         cache = &dev->srCache[i];\r
2798                                         lowest = cache->chunkId;\r
2799                                 }\r
2800                         }\r
2801                 }\r
2802                 \r
2803                 if(cache)\r
2804                 {\r
2805                         //Write it out\r
2806 \r
2807                         nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK);\r
2808                         \r
2809                         if(nBytes > YAFFS_BYTES_PER_CHUNK)\r
2810                         {\r
2811                                 nBytes= YAFFS_BYTES_PER_CHUNK;\r
2812                         }\r
2813                         \r
2814                         chunkWritten = yaffs_WriteChunkDataToObject(cache->object,\r
2815                                                                                                                 cache->chunkId,\r
2816                                                                                                                 cache->data,\r
2817                                                                                                                 nBytes,1);\r
2818 \r
2819                         cache->dirty = 0;\r
2820                 }\r
2821                 \r
2822         } while(cache && chunkWritten > 0);\r
2823         \r
2824         if(cache)\r
2825         {\r
2826                 //Hoosterman, disk full while writing cache out.\r
2827                 T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: no space during caceh write" TENDSTR)));\r
2828 \r
2829         }       \r
2830                 \r
2831 }\r
2832 \r
2833 \r
2834 // Grab us a chunk for use.\r
2835 // First look for an empty one. \r
2836 // Then look for the least recently used non-dirty one.\r
2837 // Then look for the least recently used dirty one...., flush and look again.\r
2838 static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device *dev)\r
2839 {\r
2840         int i;\r
2841         int usage;\r
2842         int theOne;\r
2843         \r
2844         for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
2845         {\r
2846                 if(!dev->srCache[i].object)\r
2847                 {\r
2848                         //T(("Grabbing empty %d\n",i));\r
2849                         \r
2850                         return &dev->srCache[i];\r
2851                 }\r
2852         }\r
2853         \r
2854         theOne = -1; \r
2855         usage = 0; // just to stop the compiler grizzling\r
2856         \r
2857         for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
2858         {\r
2859                 if(!dev->srCache[i].dirty &&\r
2860                    ((dev->srCache[i].lastUse < usage  && theOne >= 0)|| \r
2861                      theOne < 0))\r
2862                 {\r
2863                         usage = dev->srCache[i].lastUse;\r
2864                         theOne = i;\r
2865                 }\r
2866         }\r
2867         \r
2868         //T(("Grabbing non-empty %d\n",theOne));\r
2869         return  theOne >= 0 ?  &dev->srCache[theOne] : NULL;\r
2870         \r
2871 }\r
2872 \r
2873 \r
2874 static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)\r
2875 {\r
2876         yaffs_ChunkCache *cache;\r
2877         yaffs_Object *theObj;\r
2878         int usage;\r
2879         int i;\r
2880         \r
2881         // Try find a non-dirty one...\r
2882         \r
2883         cache = yaffs_GrabChunkCacheWorker(dev);\r
2884         \r
2885         if(!cache)\r
2886         {\r
2887                 // They were all dirty, find the last recently used object and flush\r
2888                 // its cache, then  find again.\r
2889                 // NB what's here is not very accurate, we actually flush the object\r
2890                 // the last recently used page.\r
2891                 \r
2892                 theObj = dev->srCache[0].object;\r
2893                 usage = dev->srCache[0].lastUse;\r
2894         \r
2895                 for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++)\r
2896                 {\r
2897                         if( dev->srCache[i].object && \r
2898                             dev->srCache[i].lastUse < usage)\r
2899                         {\r
2900                                 usage  = dev->srCache[i].lastUse;\r
2901                                 theObj = dev->srCache[i].object;\r
2902                         }\r
2903                 }\r
2904                 \r
2905                 yaffs_FlushFilesChunkCache(theObj);\r
2906                 \r
2907                 // Try again\r
2908                 cache = yaffs_GrabChunkCacheWorker(dev);\r
2909         }\r
2910         \r
2911         return cache;\r
2912 \r
2913 }\r
2914 \r
2915 \r
2916 // Find a cached chunk\r
2917 static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object *obj, int chunkId)\r
2918 {\r
2919         yaffs_Device *dev = obj->myDev;\r
2920         int i;\r
2921         \r
2922         for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
2923         {\r
2924                 if(dev->srCache[i].object == obj && \r
2925                    dev->srCache[i].chunkId == chunkId)\r
2926                 {\r
2927                         dev->cacheHits++;\r
2928                         \r
2929                         return &dev->srCache[i];\r
2930                 }\r
2931         }\r
2932         \r
2933         return NULL;\r
2934 }\r
2935 \r
2936 // Mark the chunk for the least recently used algorithym\r
2937 static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache, int isAWrite)\r
2938 {\r
2939         if( dev->srLastUse < 0 || \r
2940                 dev->srLastUse > 100000000)\r
2941         {\r
2942                 // Reset the cache usages\r
2943                 int i;\r
2944                 for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++)\r
2945                 {\r
2946                         dev->srCache[i].lastUse = 0;\r
2947                 }\r
2948                 dev->srLastUse = 0;\r
2949         }\r
2950 \r
2951         dev->srLastUse++;\r
2952         \r
2953         cache->lastUse = dev->srLastUse;\r
2954 \r
2955         if(isAWrite)\r
2956         {\r
2957                 cache->dirty = 1;\r
2958         }\r
2959 }\r
2960 \r
2961 // Invalidate a single cache page.\r
2962 // Do this when a whole page gets written,\r
2963 // ie the short cache for this page is no longer valid.\r
2964 static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId)\r
2965 {\r
2966         yaffs_ChunkCache *cache = yaffs_FindChunkCache(object,chunkId);\r
2967 \r
2968         if(cache)\r
2969         {\r
2970                 cache->object = NULL;\r
2971         }\r
2972 }\r
2973 \r
2974 \r
2975 // Invalidate all the cache pages associated with this object\r
2976 // Do this whenever ther file is deleted or resized.\r
2977 static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in)\r
2978 {\r
2979         int i;\r
2980         yaffs_Device *dev = in->myDev;\r
2981         \r
2982         // Now invalidate it.\r
2983         for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
2984         {\r
2985                 if(dev->srCache[i].object == in)\r
2986                 {\r
2987                         dev->srCache[i].object = NULL;\r
2988                 }\r
2989         }\r
2990 }\r
2991 \r
2992 \r
2993 #else\r
2994 \r
2995 static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)\r
2996 {\r
2997         return NULL;    \r
2998 }\r
2999 \r
3000 \r
3001 static yaffs_ChunkCache *yaffs_FindChunkCache(yaffs_Device *dev, int objectId, int chunkId)\r
3002 {\r
3003         return NULL;\r
3004 }\r
3005 \r
3006 static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache)\r
3007 {\r
3008 }\r
3009 \r
3010 static void yaffs_InvalidateChunkCache(yaffs_Object *obj, int chunkId)\r
3011 {\r
3012 }\r
3013 \r
3014 static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in)\r
3015 {\r
3016 }\r
3017 \r
3018 \r
3019 \r
3020 #endif\r
3021 \r
3022 \r
3023 \r
3024 ///////////////////////// File read/write ///////////////////////////////\r
3025 // Read and write have very similar structures.\r
3026 // In general the read/write has three parts to it\r
3027 // * An incomplete chunk to start with (if the read/write is not chunk-aligned)\r
3028 // * Some complete chunks\r
3029 // * An incomplete chunk to end off with\r
3030 //\r
3031 // Curve-balls: the first chunk might also be the last chunk.\r
3032 \r
3033 int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes)\r
3034 {\r
3035         \r
3036         \r
3037         int chunk;\r
3038         int start;\r
3039         int nToCopy;\r
3040         int n = nBytes;\r
3041         int nDone = 0;\r
3042         \r
3043         while(n > 0)\r
3044         {\r
3045                 chunk = offset / YAFFS_BYTES_PER_CHUNK + 1; // The first chunk is 1\r
3046                 start = offset % YAFFS_BYTES_PER_CHUNK;\r
3047 \r
3048                 // OK now check for the curveball where the start and end are in\r
3049                 // the same chunk.      \r
3050                 if(     (start + n) < YAFFS_BYTES_PER_CHUNK)\r
3051                 {\r
3052                         nToCopy = n;\r
3053                 }\r
3054                 else\r
3055                 {\r
3056                         nToCopy = YAFFS_BYTES_PER_CHUNK - start;\r
3057                 }\r
3058         \r
3059                 if(nToCopy != YAFFS_BYTES_PER_CHUNK)\r
3060                 {\r
3061                         // An incomplete start or end chunk (or maybe both start and end chunk)\r
3062 #ifdef CONFIG_YAFFS_SHORT_OP_CACHE\r
3063                         yaffs_ChunkCache *cache;\r
3064                         // If we can't find the data in the cache, then load it up.\r
3065                         cache = yaffs_FindChunkCache(in,chunk);\r
3066                         if(!cache)\r
3067                         {\r
3068                                 cache = yaffs_GrabChunkCache(in->myDev);\r
3069                                 cache->object = in;\r
3070                                 cache->chunkId = chunk;\r
3071                                 cache->dirty = 0;\r
3072                                 yaffs_ReadChunkDataFromObject(in,chunk,cache->data);            \r
3073                         }\r
3074                         \r
3075                         yaffs_UseChunkCache(in->myDev,cache,0);\r
3076 \r
3077                         memcpy(buffer,&cache->data[start],nToCopy);\r
3078 #else\r
3079                         __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
3080                         // Read into the local buffer then copy...\r
3081                         yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);            \r
3082                         memcpy(buffer,&localBuffer[start],nToCopy);\r
3083 #endif\r
3084                 }\r
3085                 else\r
3086                 {\r
3087 #ifdef CONFIG_YAFFS_WINCE\r
3088                         __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
3089                         \r
3090                         // Under WinCE can't do direct transfer. Need to use a local buffer.\r
3091                         // This is because we otherwise screw up WinCE's memory mapper\r
3092                         yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);\r
3093                         memcpy(buffer,localBuffer,YAFFS_BYTES_PER_CHUNK);\r
3094 #else\r
3095                         // A full chunk. Read directly into the supplied buffer.\r
3096                         yaffs_ReadChunkDataFromObject(in,chunk,buffer);\r
3097 #endif\r
3098                 }\r
3099                 \r
3100                 n -= nToCopy;\r
3101                 offset += nToCopy;\r
3102                 buffer += nToCopy;\r
3103                 nDone += nToCopy;\r
3104                 \r
3105         }\r
3106         \r
3107         return nDone;\r
3108 }\r
3109 \r
3110 \r
3111 \r
3112 int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes)\r
3113 {       \r
3114         \r
3115         int chunk;\r
3116         int start;\r
3117         int nToCopy;\r
3118         int n = nBytes;\r
3119         int nDone = 0;\r
3120         int nToWriteBack;\r
3121         int startOfWrite = offset;\r
3122         int chunkWritten = 0;\r
3123         int nBytesRead;\r
3124         \r
3125         \r
3126         \r
3127         while(n > 0 && chunkWritten >= 0)\r
3128         {\r
3129                 chunk = offset / YAFFS_BYTES_PER_CHUNK + 1;\r
3130                 start = offset % YAFFS_BYTES_PER_CHUNK;\r
3131                 \r
3132 \r
3133                 // OK now check for the curveball where the start and end are in\r
3134                 // the same chunk.\r
3135                 \r
3136                 if(     (start + n) < YAFFS_BYTES_PER_CHUNK)\r
3137                 {\r
3138                         nToCopy = n;\r
3139                         \r
3140                         // Now folks, to calculate how many bytes to write back....\r
3141                         // If we're overwriting and not writing to then end of file then\r
3142                         // we need to write back as much as was there before.\r
3143                         \r
3144                         nBytesRead = in->variant.fileVariant.fileSize - ((chunk -1) * YAFFS_BYTES_PER_CHUNK);\r
3145                         \r
3146                         if(nBytesRead > YAFFS_BYTES_PER_CHUNK)\r
3147                         {\r
3148                                 nBytesRead = YAFFS_BYTES_PER_CHUNK;\r
3149                         }\r
3150                         \r
3151                         nToWriteBack = (nBytesRead > (start + n)) ? nBytesRead : (start +n);\r
3152                         \r
3153                 }\r
3154                 else\r
3155                 {\r
3156                         nToCopy = YAFFS_BYTES_PER_CHUNK - start;\r
3157                         nToWriteBack = YAFFS_BYTES_PER_CHUNK;\r
3158                 }\r
3159         \r
3160                 if(nToCopy != YAFFS_BYTES_PER_CHUNK)\r
3161                 {\r
3162                         // An incomplete start or end chunk (or maybe both start and end chunk)\r
3163 #ifdef CONFIG_YAFFS_SHORT_OP_CACHE\r
3164                         yaffs_ChunkCache *cache;\r
3165                         // If we can't find the data in the cache, then load it up.\r
3166                         cache = yaffs_FindChunkCache(in,chunk);\r
3167                         if(!cache && yaffs_CheckSpaceForChunkCache(in->myDev))\r
3168                         {\r
3169                                 cache = yaffs_GrabChunkCache(in->myDev);\r
3170                                 cache->object = in;\r
3171                                 cache->chunkId = chunk;\r
3172                                 cache->dirty = 0;\r
3173                                 yaffs_ReadChunkDataFromObject(in,chunk,cache->data);            \r
3174                         }\r
3175                         \r
3176                         if(cache)\r
3177                         {       \r
3178                                 yaffs_UseChunkCache(in->myDev,cache,1);\r
3179                                 memcpy(&cache->data[start],buffer,nToCopy);\r
3180                         }\r
3181                         else\r
3182                         {\r
3183                                 chunkWritten = -1; // fail the write\r
3184                         }\r
3185 #else\r
3186                         __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
3187 \r
3188                         // An incomplete start or end chunk (or maybe both start and end chunk)\r
3189                         // Read into the local buffer then copy, then copy over and write back.\r
3190                 \r
3191                         yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);\r
3192                                 \r
3193                         memcpy(&localBuffer[start],buffer,nToCopy);\r
3194                         \r
3195                         chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0);\r
3196                         \r
3197                         //T(("Write with readback to chunk %d %d  start %d  copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack));\r
3198 #endif\r
3199                         \r
3200                 }\r
3201                 else\r
3202                 {\r
3203                         \r
3204 #ifdef CONFIG_YAFFS_WINCE\r
3205                         __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
3206                         // Under WinCE can't do direct transfer. Need to use a local buffer.\r
3207                         // This is because we otherwise screw up WinCE's memory mapper\r
3208                         memcpy(localBuffer,buffer,YAFFS_BYTES_PER_CHUNK);\r
3209                         chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,YAFFS_BYTES_PER_CHUNK,0);\r
3210 #else\r
3211                         // A full chunk. Write directly from the supplied buffer.\r
3212                         chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0);\r
3213 #endif\r
3214                         // Since we've overwritten the cached data, we better invalidate it.\r
3215                         yaffs_InvalidateChunkCache(in,chunk);\r
3216                         //T(("Write to chunk %d %d\n",chunk,chunkWritten));\r
3217                 }\r
3218                 \r
3219                 if(chunkWritten >= 0)\r
3220                 {\r
3221                         n -= nToCopy;\r
3222                         offset += nToCopy;\r
3223                         buffer += nToCopy;\r
3224                         nDone += nToCopy;\r
3225                 }\r
3226                 \r
3227         }\r
3228         \r
3229         // Update file object\r
3230         \r
3231         if((startOfWrite + nDone) > in->variant.fileVariant.fileSize)\r
3232         {\r
3233                 in->variant.fileVariant.fileSize = (startOfWrite + nDone);\r
3234         }\r
3235         \r
3236         in->dirty = 1;\r
3237         \r
3238         return nDone;\r
3239 }\r
3240 \r
3241 \r
3242 int yaffs_ResizeFile(yaffs_Object *in, int newSize)\r
3243 {\r
3244         int i;\r
3245         int chunkId;\r
3246         int oldFileSize = in->variant.fileVariant.fileSize;\r
3247         int sizeOfPartialChunk = newSize % YAFFS_BYTES_PER_CHUNK;\r
3248         \r
3249         yaffs_Device *dev = in->myDev;\r
3250         \r
3251         __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
3252 \r
3253         yaffs_FlushFilesChunkCache(in); \r
3254         yaffs_InvalidateWholeChunkCache(in);\r
3255         \r
3256         if(in->variantType != YAFFS_OBJECT_TYPE_FILE)\r
3257         {\r
3258                 return yaffs_GetFileSize(in);\r
3259         }\r
3260         \r
3261         if(newSize < oldFileSize)\r
3262         {\r
3263                 \r
3264                 int lastDel = 1 + (oldFileSize-1)/YAFFS_BYTES_PER_CHUNK;\r
3265                 \r
3266                 int startDel = 1 + (newSize + YAFFS_BYTES_PER_CHUNK - 1)/\r
3267                                                         YAFFS_BYTES_PER_CHUNK;\r
3268 \r
3269                 // Delete backwards so that we don't end up with holes if\r
3270                 // power is lost part-way through the operation.\r
3271                 for(i = lastDel; i >= startDel; i--)\r
3272                 {\r
3273                         // NB this could be optimised somewhat,\r
3274                         // eg. could retrieve the tags and write them without\r
3275                         // using yaffs_DeleteChunk\r
3276 \r
3277                         chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL);\r
3278                         if(chunkId <= 0 || chunkId >= (dev->endBlock * 32))\r
3279                         {\r
3280                                 //T(("Found daft chunkId %d for %d\n",chunkId,i));\r
3281                         }\r
3282                         else\r
3283                         {\r
3284                                 in->nDataChunks--;\r
3285                                 yaffs_DeleteChunk(dev,chunkId);\r
3286                         }\r
3287                 }\r
3288                 \r
3289                 \r
3290                 if(sizeOfPartialChunk != 0)\r
3291                 {\r
3292                         int lastChunk = 1+ newSize/YAFFS_BYTES_PER_CHUNK;\r
3293                         \r
3294                         // Got to read and rewrite the last chunk with its new size.\r
3295                         yaffs_ReadChunkDataFromObject(in,lastChunk,localBuffer);\r
3296                         \r
3297                         yaffs_WriteChunkDataToObject(in,lastChunk,localBuffer,sizeOfPartialChunk,1);\r
3298                                 \r
3299                 }\r
3300                 \r
3301                 in->variant.fileVariant.fileSize = newSize;\r
3302                 \r
3303                 yaffs_PruneFileStructure(dev,&in->variant.fileVariant);\r
3304                 \r
3305                 return newSize;\r
3306                 \r
3307         }\r
3308         else\r
3309         {\r
3310                 return oldFileSize;\r
3311         }\r
3312 }\r
3313 \r
3314 \r
3315 loff_t yaffs_GetFileSize(yaffs_Object *obj)\r
3316 {\r
3317         obj = yaffs_GetEquivalentObject(obj);\r
3318         \r
3319         switch(obj->variantType)\r
3320         {\r
3321                 case YAFFS_OBJECT_TYPE_FILE: \r
3322                         return obj->variant.fileVariant.fileSize;\r
3323                 case YAFFS_OBJECT_TYPE_SYMLINK:\r
3324                         return strlen(obj->variant.symLinkVariant.alias);\r
3325                 default:\r
3326                         return 0;\r
3327         }\r
3328 }\r
3329 \r
3330 \r
3331 \r
3332 // yaffs_FlushFile() updates the file's\r
3333 // objectId in NAND\r
3334 \r
3335 int yaffs_FlushFile(yaffs_Object *in)\r
3336 {\r
3337         int retVal;\r
3338         if(in->dirty)\r
3339         {\r
3340                 //T(("flushing object header\n"));\r
3341                 \r
3342                 yaffs_FlushFilesChunkCache(in);\r
3343         \r
3344 #ifdef CONFIG_YAFFS_WINCE\r
3345                 yfsd_WinFileTimeNow(in->win_mtime);\r
3346 #else\r
3347                 in->st_mtime = CURRENT_TIME;\r
3348 #endif\r
3349 \r
3350                 retVal = yaffs_UpdateObjectHeader(in,NULL,0);\r
3351         }\r
3352         else\r
3353         {\r
3354                 retVal = YAFFS_OK;\r
3355         }\r
3356         \r
3357         return retVal;\r
3358         \r
3359 }\r
3360 \r
3361 \r
3362 static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)\r
3363 {\r
3364 \r
3365         // First off, invalidate the file's data in the cache, without flushing.\r
3366         yaffs_InvalidateWholeChunkCache(in);\r
3367         \r
3368         yaffs_RemoveObjectFromDirectory(in);\r
3369         yaffs_DeleteChunk(in->myDev,in->chunkId);\r
3370 #ifdef __KERNEL__\r
3371         if(in->myInode)\r
3372         {\r
3373                 in->myInode->u.generic_ip = NULL;\r
3374                 in->myInode = 0;\r
3375         }\r
3376 #endif\r
3377         yaffs_FreeObject(in);\r
3378         return YAFFS_OK;\r
3379 \r
3380 }\r
3381 \r
3382 // yaffs_DeleteFile deletes the whole file data\r
3383 // and the inode associated with the file.\r
3384 // It does not delete the links associated with the file.\r
3385 static int yaffs_UnlinkFile(yaffs_Object *in)\r
3386 {\r
3387 \r
3388 #ifdef CONFIG_YAFFS_DISABLE_BACKGROUND_DELETION\r
3389         // Delete the file data & tnodes\r
3390 \r
3391          yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL);\r
3392          \r
3393 \r
3394         yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);\r
3395         \r
3396         return  yaffs_DoGenericObjectDeletion(in);\r
3397 #else\r
3398         int retVal;\r
3399         int immediateDeletion=0;\r
3400         retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0);\r
3401         if(retVal == YAFFS_OK)\r
3402         {\r
3403                 //in->unlinked = 1;\r
3404                 //in->myDev->nUnlinkedFiles++;\r
3405                 //in->renameAllowed = 0;\r
3406 #ifdef __KERNEL__\r
3407                 if(in->myInode)\r
3408                 {\r
3409                         immediateDeletion = 1;\r
3410 \r
3411                 }\r
3412 #endif\r
3413 #ifdef CONFIG_YAFFS_WINCE\r
3414                 if(in->inUse <= 0)\r
3415                 {\r
3416                         immediateDeletion = 1;\r
3417 \r
3418                 }\r
3419 #endif\r
3420                 \r
3421                 if(immediateDeletion)\r
3422                 {\r
3423                         T(YAFFS_TRACE_TRACING,(TSTR("yaffs: immediate deletion of file %d" TENDSTR),in->objectId));\r
3424                         in->deleted=1;\r
3425                         in->myDev->nDeletedFiles++;\r
3426                 }\r
3427         \r
3428         }\r
3429         return retVal;\r
3430 \r
3431         \r
3432 #endif\r
3433 }\r
3434 \r
3435 int yaffs_DeleteFile(yaffs_Object *in)\r
3436 {\r
3437         int retVal = YAFFS_OK;\r
3438         if(!in->unlinked)\r
3439         {\r
3440                 retVal = yaffs_UnlinkFile(in);\r
3441         }\r
3442         if(retVal == YAFFS_OK && \r
3443            in->unlinked &&\r
3444            !in->deleted)\r
3445         {\r
3446                 in->deleted = 1;\r
3447                 in->myDev->nDeletedFiles++;\r
3448         }\r
3449         return in->deleted ? YAFFS_OK : YAFFS_FAIL;     \r
3450 }\r
3451 \r
3452 static int yaffs_DeleteDirectory(yaffs_Object *in)\r
3453 {\r
3454         //First check that the directory is empty.\r
3455         if(list_empty(&in->variant.directoryVariant.children))\r
3456         {\r
3457                 return  yaffs_DoGenericObjectDeletion(in);\r
3458         }\r
3459         \r
3460         return YAFFS_FAIL;\r
3461         \r
3462 }\r
3463 \r
3464 static int yaffs_DeleteSymLink(yaffs_Object *in)\r
3465 {\r
3466         YFREE(in->variant.symLinkVariant.alias);\r
3467 \r
3468         return  yaffs_DoGenericObjectDeletion(in);\r
3469 }\r
3470 \r
3471 static int yaffs_DeleteHardLink(yaffs_Object *in)\r
3472 {\r
3473         // remove this hardlink from the list assocaited with the equivalent\r
3474         // object\r
3475         list_del(&in->hardLinks);\r
3476         return  yaffs_DoGenericObjectDeletion(in);      \r
3477 }\r
3478 \r
3479 \r
3480 static int yaffs_UnlinkWorker(yaffs_Object *obj)\r
3481 {\r
3482 \r
3483         \r
3484         if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)\r
3485         {\r
3486                 return  yaffs_DeleteHardLink(obj);\r
3487         }\r
3488         else if(!list_empty(&obj->hardLinks))\r
3489         {       \r
3490                 // Curve ball: We're unlinking an object that has a hardlink.\r
3491                 //\r
3492                 //      This problem arises because we are not strictly following\r
3493                 //  The Linux link/inode model.\r
3494                 //\r
3495                 // We can't really delete the object.\r
3496                 // Instead, we do the following:\r
3497                 // - Select a hardlink.\r
3498                 // - Unhook it from the hard links\r
3499                 // - Unhook it from its parent directory (so that the rename can work)\r
3500                 // - Rename the object to the hardlink's name.\r
3501                 // - Delete the hardlink\r
3502                 \r
3503                 \r
3504                 yaffs_Object *hl;\r
3505                 int retVal;\r
3506                 char name[YAFFS_MAX_NAME_LENGTH+1];\r
3507                 \r
3508                 hl = list_entry(obj->hardLinks.next,yaffs_Object,hardLinks);\r
3509                 \r
3510                 list_del_init(&hl->hardLinks);\r
3511                 list_del_init(&hl->siblings);\r
3512                 \r
3513                 yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1);\r
3514                 \r
3515                 retVal = yaffs_ChangeObjectName(obj, hl->parent, name,0);\r
3516                 \r
3517                 if(retVal == YAFFS_OK)\r
3518                 {\r
3519                         retVal = yaffs_DoGenericObjectDeletion(hl);\r
3520                 }\r
3521                 return retVal;\r
3522                                 \r
3523         }\r
3524         else\r
3525         {\r
3526                 switch(obj->variantType)\r
3527                 {\r
3528                         case YAFFS_OBJECT_TYPE_FILE:\r
3529                                 return yaffs_UnlinkFile(obj);\r
3530                                 break;\r
3531                         case YAFFS_OBJECT_TYPE_DIRECTORY:\r
3532                                 return yaffs_DeleteDirectory(obj);\r
3533                                 break;\r
3534                         case YAFFS_OBJECT_TYPE_SYMLINK:\r
3535                                 return yaffs_DeleteSymLink(obj);\r
3536                                 break;\r
3537                         case YAFFS_OBJECT_TYPE_HARDLINK:\r
3538                         case YAFFS_OBJECT_TYPE_UNKNOWN:\r
3539                         default:\r
3540                                 return YAFFS_FAIL;\r
3541                 }\r
3542         }\r
3543 }\r
3544 \r
3545 int yaffs_Unlink(yaffs_Object *dir, const char *name)\r
3546 {\r
3547         yaffs_Object *obj;\r
3548         \r
3549          obj = yaffs_FindObjectByName(dir,name);\r
3550          \r
3551          if(obj && obj->unlinkAllowed)\r
3552          {\r
3553                 return yaffs_UnlinkWorker(obj);\r
3554          }\r
3555          \r
3556          return YAFFS_FAIL;\r
3557         \r
3558 }\r
3559 \r
3560 //////////////// Initialisation Scanning /////////////////\r
3561 \r
3562 \r
3563 \r
3564 // For now we use the SmartMedia check.\r
3565 // We look at the blockStatus byte in the first two chunks\r
3566 // These must be 0xFF to pass as OK.\r
3567 // todo: this function needs to be modifyable foir different NAND types\r
3568 // and different chunk sizes.  Suggest make this into a per-device configurable\r
3569 // function.\r
3570 static int yaffs_IsBlockBad(yaffs_Device *dev, int blk)\r
3571 {\r
3572         yaffs_Spare spare;\r
3573         \r
3574         yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK,NULL,&spare,1);\r
3575 #if 1\r
3576         if(yaffs_CountBits(spare.blockStatus) < 7)\r
3577         {\r
3578                 return 1;\r
3579         }\r
3580 #else\r
3581         if(spare.blockStatus != 0xFF)\r
3582         {\r
3583                 return 1;\r
3584         }\r
3585 #endif\r
3586         yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK + 1,NULL,&spare,1);\r
3587 \r
3588 #if 1\r
3589         if(yaffs_CountBits(spare.blockStatus) < 7)\r
3590         {\r
3591                 return 1;\r
3592         }\r
3593 #else\r
3594         if(spare.blockStatus != 0xFF)\r
3595         {\r
3596                 return 1;\r
3597         }\r
3598 #endif\r
3599         \r
3600         return 0;\r
3601         \r
3602 }\r
3603 \r
3604 static int yaffs_Scan(yaffs_Device *dev)\r
3605 {\r
3606         yaffs_Spare spare;\r
3607         yaffs_Tags tags;\r
3608         int blk;\r
3609         int chunk;\r
3610         int c;\r
3611         int deleted;\r
3612         yaffs_BlockState state;\r
3613         yaffs_Object *hardList = NULL;\r
3614         yaffs_Object *hl;\r
3615         yaffs_BlockInfo *bi;\r
3616 \r
3617         \r
3618         yaffs_ObjectHeader *oh;\r
3619         yaffs_Object *in;\r
3620         yaffs_Object *parent;\r
3621         \r
3622         __u8 chunkData[YAFFS_BYTES_PER_CHUNK];\r
3623         \r
3624         \r
3625         // Scan all the blocks...\r
3626         \r
3627         for(blk = dev->startBlock; blk <= dev->endBlock; blk++)\r
3628         {\r
3629                 deleted = 0;\r
3630                 bi = yaffs_GetBlockInfo(dev,blk);\r
3631                 bi->pageBits = 0;\r
3632                 bi->pagesInUse = 0;\r
3633                 state = YAFFS_BLOCK_STATE_SCANNING;\r
3634                 \r
3635                 \r
3636                 if(yaffs_IsBlockBad(dev,blk))\r
3637                 {\r
3638                         state = YAFFS_BLOCK_STATE_DEAD;\r
3639                         T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk));\r
3640                 }\r
3641                 \r
3642                 // Read each chunk in the block.\r
3643                 \r
3644                 for(c = 0; c < YAFFS_CHUNKS_PER_BLOCK && \r
3645                                    state == YAFFS_BLOCK_STATE_SCANNING; c++)\r
3646                 {\r
3647                         // Read the spare area and decide what to do\r
3648                         chunk = blk * YAFFS_CHUNKS_PER_BLOCK + c;\r
3649                         \r
3650                         yaffs_ReadChunkFromNAND(dev,chunk,NULL,&spare,1);\r
3651 \r
3652                         \r
3653                         // This block looks ok, now what's in this chunk?\r
3654                         yaffs_GetTagsFromSpare(dev,&spare,&tags);\r
3655                         \r
3656                         if(yaffs_CountBits(spare.pageStatus) < 6)\r
3657                         {\r
3658                                 // A deleted chunk\r
3659                                 deleted++;\r
3660                                 dev->nFreeChunks ++;\r
3661                                 //T((" %d %d deleted\n",blk,c));\r
3662                         }\r
3663                         else if(tags.objectId == YAFFS_UNUSED_OBJECT_ID)\r
3664                         {\r
3665                                 // An unassigned chunk in the block\r
3666                                 // This means that either the block is empty or \r
3667                                 // this is the one being allocated from\r
3668                                 \r
3669                                 if(c == 0)\r
3670                                 {\r
3671                                         // the block is unused\r
3672                                         state = YAFFS_BLOCK_STATE_EMPTY;\r
3673                                         dev->nErasedBlocks++;\r
3674                                 }\r
3675                                 else\r
3676                                 {\r
3677                                         // this is the block being allocated from\r
3678                                         T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c));\r
3679                                         state = YAFFS_BLOCK_STATE_ALLOCATING;\r
3680                                         dev->allocationBlock = blk;\r
3681                                         dev->allocationPage = c;\r
3682                                 }\r
3683 \r
3684                                 dev->nFreeChunks += (YAFFS_CHUNKS_PER_BLOCK - c);\r
3685                         }\r
3686                         else if(tags.chunkId > 0)\r
3687                         {\r
3688                                 int endpos;\r
3689                                 // A data chunk.\r
3690                                 bi->pageBits |= (1 << c);\r
3691                                 bi->pagesInUse++;\r
3692                                                                 \r
3693                                 in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);\r
3694                                 // PutChunkIntoFIle checks for a clash (two data chunks with\r
3695                                 // the same chunkId).\r
3696                                 yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1);\r
3697                                 endpos = (tags.chunkId - 1)* YAFFS_BYTES_PER_CHUNK + tags.byteCount;\r
3698                                 if(in->variant.fileVariant.scannedFileSize <endpos)\r
3699                                 {\r
3700                                         in->variant.fileVariant.scannedFileSize = endpos;\r
3701 #ifndef CONFIG_YAFFS_USE_HEADER_FILE_SIZE\r
3702                                                 in->variant.fileVariant.fileSize =      \r
3703                                                         in->variant.fileVariant.scannedFileSize;\r
3704 #endif\r
3705 \r
3706                                 }\r
3707                                 //T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));  \r
3708                         }\r
3709                         else\r
3710                         {\r
3711                                 // chunkId == 0, so it is an ObjectHeader.\r
3712                                 // Thus, we read in the object header and make the object\r
3713                                 bi->pageBits |= (1 << c);\r
3714                                 bi->pagesInUse++;\r
3715                                                         \r
3716                                 yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL,1);\r
3717                                 \r
3718                                 oh = (yaffs_ObjectHeader *)chunkData;\r
3719                                 \r
3720                                 in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);\r
3721                                 \r
3722                                 if(in->valid)\r
3723                                 {\r
3724                                         // We have already filled this one. We have a duplicate and need to resolve it.\r
3725                                         \r
3726                                         unsigned existingSerial = in->serial;\r
3727                                         unsigned newSerial = tags.serialNumber;\r
3728                                         \r
3729                                         if(((existingSerial+1) & 3) == newSerial)\r
3730                                         {\r
3731                                                 // Use new one - destroy the exisiting one\r
3732                                                 yaffs_DeleteChunk(dev,in->chunkId);\r
3733                                                 in->valid = 0;\r
3734                                         }\r
3735                                         else\r
3736                                         {\r
3737                                                 // Use existing - destroy this one.\r
3738                                                 yaffs_DeleteChunk(dev,chunk);\r
3739                                         }\r
3740                                 }\r
3741                                 \r
3742                                 if(!in->valid &&\r
3743                                    (tags.objectId == YAFFS_OBJECTID_ROOT ||\r
3744                                     tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))\r
3745                                 {\r
3746                                         // We only load some info, don't fiddle with directory structure\r
3747                                         in->valid = 1;\r
3748                                         in->variantType = oh->type;\r
3749         \r
3750                                         in->st_mode  = oh->st_mode;\r
3751 #ifdef CONFIG_YAFFS_WINCE\r
3752                                         in->win_atime[0] = oh->win_atime[0];\r
3753                                         in->win_ctime[0] = oh->win_ctime[0];\r
3754                                         in->win_mtime[0] = oh->win_mtime[0];\r
3755                                         in->win_atime[1] = oh->win_atime[1];\r
3756                                         in->win_ctime[1] = oh->win_ctime[1];\r
3757                                         in->win_mtime[1] = oh->win_mtime[1];\r
3758 #else\r
3759                                         in->st_uid   = oh->st_uid;\r
3760                                         in->st_gid   = oh->st_gid;\r
3761                                         in->st_atime = oh->st_atime;\r
3762                                         in->st_mtime = oh->st_mtime;\r
3763                                         in->st_ctime = oh->st_ctime;\r
3764                                         in->st_rdev = oh->st_rdev;\r
3765 #endif\r
3766                                         in->chunkId  = chunk;\r
3767 \r
3768                                 }\r
3769                                 else if(!in->valid)\r
3770                                 {\r
3771                                         // we need to load this info\r
3772                                 \r
3773                                         in->valid = 1;\r
3774                                         in->variantType = oh->type;\r
3775         \r
3776                                         in->st_mode  = oh->st_mode;\r
3777 #ifdef CONFIG_YAFFS_WINCE\r
3778                                         in->win_atime[0] = oh->win_atime[0];\r
3779                                         in->win_ctime[0] = oh->win_ctime[0];\r
3780                                         in->win_mtime[0] = oh->win_mtime[0];\r
3781                                         in->win_atime[1] = oh->win_atime[1];\r
3782                                         in->win_ctime[1] = oh->win_ctime[1];\r
3783                                         in->win_mtime[1] = oh->win_mtime[1];\r
3784 #else\r
3785                                         in->st_uid   = oh->st_uid;\r
3786                                         in->st_gid   = oh->st_gid;\r
3787                                         in->st_atime = oh->st_atime;\r
3788                                         in->st_mtime = oh->st_mtime;\r
3789                                         in->st_ctime = oh->st_ctime;\r
3790                                         in->st_rdev = oh->st_rdev;\r
3791 #endif\r
3792                                         in->chunkId  = chunk;\r
3793 \r
3794                                         yaffs_SetObjectName(in,oh->name);\r
3795                                         in->dirty = 0;\r
3796                                                         \r
3797                                         // directory stuff...\r
3798                                         // hook up to parent\r
3799         \r
3800                                         parent = yaffs_FindOrCreateObjectByNumber(dev,oh->parentObjectId,YAFFS_OBJECT_TYPE_DIRECTORY);\r
3801                                         if(parent->variantType == YAFFS_OBJECT_TYPE_UNKNOWN)\r
3802                                         {\r
3803                                                 // Set up as a directory\r
3804                                                 parent->variantType = YAFFS_OBJECT_TYPE_DIRECTORY;\r
3805                                                 INIT_LIST_HEAD(&parent->variant.directoryVariant.children);\r
3806                                         }\r
3807                                         else if(parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)\r
3808                                         {\r
3809                                                 // Hoosterman, another problem....\r
3810                                                 // We're trying to use a non-directory as a directory\r
3811                                                 // Todo ... handle\r
3812                                                 T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: attempting to use non-directory as a directory in scan" TENDSTR)));\r
3813 \r
3814                                         }\r
3815                                 \r
3816                                         yaffs_AddObjectToDirectory(parent,in);\r
3817                                         if(parent == dev->unlinkedDir)\r
3818                                         {\r
3819                                                 in->deleted = 1; // If it is unlinked at start up then it wants deleting\r
3820                                                 dev->nDeletedFiles++;\r
3821                                         }\r
3822                                 \r
3823                                         // Note re hardlinks.\r
3824                                         // Since we might scan a hardlink before its equivalent object is scanned\r
3825                                         // we put them all in a list.\r
3826                                         // After scanning is complete, we should have all the objects, so we run through this\r
3827                                         // list and fix up all the chains.              \r
3828         \r
3829                                         switch(in->variantType)\r
3830                                         {\r
3831                                                 case YAFFS_OBJECT_TYPE_UNKNOWN:         // Todo got a problem\r
3832                                                         break;\r
3833                                                 case YAFFS_OBJECT_TYPE_FILE:\r
3834 #ifdef CONFIG_YAFFS_USE_HEADER_FILE_SIZE\r
3835                                                         in->variant.fileVariant.fileSize = oh->fileSize;\r
3836 #endif\r
3837                                                         break;\r
3838                                                 case YAFFS_OBJECT_TYPE_HARDLINK:\r
3839                                                         in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;\r
3840                                                         (yaffs_Object *)(in->hardLinks.next) = hardList;\r
3841                                                         hardList = in;\r
3842                                                         break;\r
3843                                                 case YAFFS_OBJECT_TYPE_DIRECTORY:       // Do nothing\r
3844                                                         break;\r
3845                                                 case YAFFS_OBJECT_TYPE_SPECIAL: // Do nothing\r
3846                                                         break;\r
3847                                                 case YAFFS_OBJECT_TYPE_SYMLINK:         // Do nothing\r
3848                                                         in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias);\r
3849                                                         break;\r
3850                                         }\r
3851                                         //T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));        \r
3852                                 }\r
3853                         }\r
3854                 }\r
3855                 \r
3856                 if(state == YAFFS_BLOCK_STATE_SCANNING)\r
3857                 {\r
3858                         // If we got this far while scanning, then the block is fully allocated.\r
3859                         state = YAFFS_BLOCK_STATE_FULL; \r
3860                 }\r
3861                 \r
3862                 bi->blockState = state;\r
3863                 \r
3864                 // Now let's see if it was dirty\r
3865                 if(     bi->pagesInUse == 0 &&\r
3866                 bi->blockState == YAFFS_BLOCK_STATE_FULL)\r
3867             {\r
3868                 yaffs_BlockBecameDirty(dev,blk);\r
3869             }\r
3870 \r
3871         }\r
3872         \r
3873         // Fix up the hard link chains.\r
3874         // We should now have scanned all the objects, now it's time to add these \r
3875         // hardlinks.\r
3876         while(hardList)\r
3877         {\r
3878                 hl = hardList;\r
3879                 hardList = (yaffs_Object *)(hardList->hardLinks.next);\r
3880                 \r
3881                 in = yaffs_FindObjectByNumber(dev,hl->variant.hardLinkVariant.equivalentObjectId);\r
3882                 \r
3883                 if(in)\r
3884                 {\r
3885                         // Add the hardlink pointers\r
3886                         hl->variant.hardLinkVariant.equivalentObject=in;\r
3887                         list_add(&hl->hardLinks,&in->hardLinks);\r
3888                 }\r
3889                 else\r
3890                 {\r
3891                         //Todo Need to report/handle this better.\r
3892                         // Got a problem... hardlink to a non-existant object\r
3893                         hl->variant.hardLinkVariant.equivalentObject=NULL;\r
3894                         INIT_LIST_HEAD(&hl->hardLinks);\r
3895                         \r
3896                 }\r
3897                 \r
3898         }\r
3899         \r
3900         \r
3901         \r
3902         return YAFFS_OK;\r
3903 }\r
3904 \r
3905 \r
3906 ////////////////////////// Directory Functions /////////////////////////\r
3907 \r
3908 \r
3909 static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj)\r
3910 {\r
3911 \r
3912         if(obj->siblings.prev == NULL)\r
3913         {\r
3914                 // Not initialised\r
3915                 INIT_LIST_HEAD(&obj->siblings);\r
3916                 \r
3917         }\r
3918         else if(!list_empty(&obj->siblings))\r
3919         {\r
3920                 // If it is holed up somewhere else, un hook it\r
3921                 list_del_init(&obj->siblings);\r
3922         }\r
3923         // Now add it\r
3924         list_add(&obj->siblings,&directory->variant.directoryVariant.children);\r
3925         obj->parent = directory;\r
3926         \r
3927         if(directory == obj->myDev->unlinkedDir)\r
3928         {\r
3929                 obj->unlinked = 1;\r
3930                 obj->myDev->nUnlinkedFiles++;\r
3931                 obj->renameAllowed = 0;\r
3932         }\r
3933 }\r
3934 \r
3935 static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)\r
3936 {\r
3937         list_del_init(&obj->siblings);\r
3938         obj->parent = NULL;\r
3939 }\r
3940 \r
3941 yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory,const char *name)\r
3942 {\r
3943         int sum;\r
3944         \r
3945         struct list_head *i;\r
3946         char buffer[YAFFS_MAX_NAME_LENGTH+1];\r
3947         \r
3948         yaffs_Object *l;\r
3949         \r
3950         sum = yaffs_CalcNameSum(name);\r
3951         \r
3952         list_for_each(i,&directory->variant.directoryVariant.children)\r
3953         {\r
3954                 l = list_entry(i, yaffs_Object,siblings);\r
3955                 \r
3956                 // Special case for lost-n-found\r
3957                 if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND)\r
3958                 {\r
3959                         if(yaffs_strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0)\r
3960                         {\r
3961                                 return l;\r
3962                         }\r
3963                 }\r
3964                 else if(yaffs_SumCompare(l->sum, sum))\r
3965                 {\r
3966                         // Do a real check\r
3967                         yaffs_GetObjectName(l,buffer,YAFFS_MAX_NAME_LENGTH);\r
3968                         if(yaffs_strcmp(name,buffer) == 0)\r
3969                         {\r
3970                                 return l;\r
3971                         }\r
3972                         \r
3973                         \r
3974                 }\r
3975         }\r
3976         \r
3977         return NULL;\r
3978 }\r
3979 \r
3980 \r
3981 int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *))\r
3982 {\r
3983         struct list_head *i;    \r
3984         yaffs_Object *l;\r
3985         \r
3986         \r
3987         list_for_each(i,&theDir->variant.directoryVariant.children)\r
3988         {\r
3989                 l = list_entry(i, yaffs_Object,siblings);\r
3990                 if(!fn(l))\r
3991                 {\r
3992                         return YAFFS_FAIL;\r
3993                 }\r
3994         }\r
3995         \r
3996         return YAFFS_OK;\r
3997 \r
3998 }\r
3999 \r
4000 \r
4001 // GetEquivalentObject dereferences any hard links to get to the\r
4002 // actual object.\r
4003 \r
4004 yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj)\r
4005 {\r
4006         if(obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)\r
4007         {\r
4008                 // We want the object id of the equivalent object, not this one\r
4009                 obj = obj->variant.hardLinkVariant.equivalentObject;\r
4010         }\r
4011         return obj;\r
4012 \r
4013 }\r
4014 \r
4015 int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize)\r
4016 {\r
4017         memset(name,0,buffSize);\r
4018         \r
4019         if(obj->objectId == YAFFS_OBJECTID_LOSTNFOUND)\r
4020         {\r
4021                 strncpy(name,YAFFS_LOSTNFOUND_NAME,buffSize - 1);\r
4022         }\r
4023         else if(obj->chunkId <= 0)\r
4024         {\r
4025                 char locName[20];\r
4026                 // make up a name\r
4027                 sprintf(locName,"%s%d",YAFFS_LOSTNFOUND_PREFIX,obj->objectId);\r
4028                 strncpy(name,locName,buffSize - 1);\r
4029 \r
4030         }\r
4031 #ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM\r
4032         else if(obj->shortName[0])\r
4033         {\r
4034                 strcpy(name,obj->shortName);\r
4035         }\r
4036 #endif\r
4037         else\r
4038         {\r
4039                 __u8 buffer[YAFFS_BYTES_PER_CHUNK];\r
4040                 yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;\r
4041 \r
4042                 memset(buffer,0,YAFFS_BYTES_PER_CHUNK);\r
4043         \r
4044                 if(obj->chunkId >= 0)\r
4045                 {\r
4046                         yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL,1);\r
4047                 }\r
4048                 strncpy(name,oh->name,buffSize - 1);\r
4049         }\r
4050         \r
4051         return strlen(name);\r
4052 }\r
4053 \r
4054 int yaffs_GetObjectFileLength(yaffs_Object *obj)\r
4055 {\r
4056         \r
4057         // Dereference any hard linking\r
4058         obj = yaffs_GetEquivalentObject(obj);\r
4059         \r
4060         if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)\r
4061         {\r
4062                 return obj->variant.fileVariant.fileSize;\r
4063         }\r
4064         if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)\r
4065         {\r
4066                 return strlen(obj->variant.symLinkVariant.alias);\r
4067         }\r
4068         else\r
4069         {\r
4070                 // Only a directory should drop through to here\r
4071                 return YAFFS_BYTES_PER_CHUNK;\r
4072         }       \r
4073 }\r
4074 \r
4075 int yaffs_GetObjectLinkCount(yaffs_Object *obj)\r
4076 {\r
4077         int count = 0; \r
4078         struct list_head *i;\r
4079         \r
4080         if(!obj->unlinked)\r
4081         {\r
4082                 count++;        // the object itself\r
4083         }\r
4084         list_for_each(i,&obj->hardLinks)\r
4085         {\r
4086                 count++;        // add the hard links;\r
4087         }\r
4088         return count;\r
4089         \r
4090 }\r
4091 \r
4092 \r
4093 int yaffs_GetObjectInode(yaffs_Object *obj)\r
4094 {\r
4095         obj = yaffs_GetEquivalentObject(obj);\r
4096         \r
4097         return obj->objectId;\r
4098 }\r
4099 \r
4100 unsigned yaffs_GetObjectType(yaffs_Object *obj)\r
4101 {\r
4102         obj = yaffs_GetEquivalentObject(obj);\r
4103         \r
4104         switch(obj->variantType)\r
4105         {\r
4106                 case YAFFS_OBJECT_TYPE_FILE:            return DT_REG; break;\r
4107                 case YAFFS_OBJECT_TYPE_DIRECTORY:       return DT_DIR; break;\r
4108                 case YAFFS_OBJECT_TYPE_SYMLINK:         return DT_LNK; break;\r
4109                 case YAFFS_OBJECT_TYPE_HARDLINK:        return DT_REG; break;\r
4110                 case YAFFS_OBJECT_TYPE_SPECIAL:         \r
4111                         if(S_ISFIFO(obj->st_mode)) return DT_FIFO;\r
4112                         if(S_ISCHR(obj->st_mode)) return DT_CHR;\r
4113                         if(S_ISBLK(obj->st_mode)) return DT_BLK;\r
4114                         if(S_ISSOCK(obj->st_mode)) return DT_SOCK;\r
4115                 default: return DT_REG; break;\r
4116         }\r
4117 }\r
4118 \r
4119 char *yaffs_GetSymlinkAlias(yaffs_Object *obj)\r
4120 {\r
4121         obj = yaffs_GetEquivalentObject(obj);\r
4122         if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)\r
4123         {\r
4124                 return yaffs_CloneString(obj->variant.symLinkVariant.alias);\r
4125         }\r
4126         else\r
4127         {\r
4128                 return yaffs_CloneString("");\r
4129         }\r
4130 }\r
4131 \r
4132 #ifndef CONFIG_YAFFS_WINCE\r
4133 \r
4134 int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr)\r
4135 {\r
4136         unsigned int valid = attr->ia_valid;\r
4137         \r
4138         if(valid & ATTR_MODE) obj->st_mode = attr->ia_mode;\r
4139         if(valid & ATTR_UID) obj->st_uid = attr->ia_uid;\r
4140         if(valid & ATTR_GID) obj->st_gid = attr->ia_gid;\r
4141         \r
4142         if(valid & ATTR_ATIME) obj->st_atime = attr->ia_atime;\r
4143         if(valid & ATTR_CTIME) obj->st_ctime = attr->ia_ctime;\r
4144         if(valid & ATTR_MTIME) obj->st_mtime = attr->ia_mtime;\r
4145         \r
4146         if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);\r
4147         \r
4148         yaffs_UpdateObjectHeader(obj,NULL,1);\r
4149         \r
4150         return YAFFS_OK;\r
4151         \r
4152 }\r
4153 int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr)\r
4154 {\r
4155         unsigned int valid = 0;\r
4156         \r
4157         attr->ia_mode = obj->st_mode;   valid |= ATTR_MODE;\r
4158         attr->ia_uid = obj->st_uid;             valid |= ATTR_UID;\r
4159         attr->ia_gid = obj->st_gid;             valid |= ATTR_GID;\r
4160         \r
4161         attr->ia_atime = obj->st_atime; valid |= ATTR_ATIME;\r
4162         attr->ia_ctime = obj->st_ctime; valid |= ATTR_CTIME;\r
4163         attr->ia_mtime = obj->st_mtime; valid |= ATTR_MTIME;\r
4164         \r
4165         attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE;\r
4166         \r
4167         attr->ia_valid = valid;\r
4168         \r
4169         return YAFFS_OK;\r
4170         \r
4171 }\r
4172 \r
4173 #endif\r
4174 \r
4175 int yaffs_DumpObject(yaffs_Object *obj)\r
4176 {\r
4177 //      __u8 buffer[YAFFS_BYTES_PER_CHUNK];\r
4178         char name[257];\r
4179 //      yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;\r
4180 \r
4181 //      memset(buffer,0,YAFFS_BYTES_PER_CHUNK);\r
4182         \r
4183 //      if(obj->chunkId >= 0)\r
4184 //      {\r
4185 //              yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL);\r
4186 //      }\r
4187         \r
4188         yaffs_GetObjectName(obj,name,256);\r
4189         \r
4190         T(YAFFS_TRACE_ALWAYS,(TSTR("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n" TENDSTR),\r
4191                         obj->objectId,yaffs_GetObjectInode(obj), name, obj->dirty, obj->valid, obj->serial, \r
4192                         obj->sum, obj->chunkId, yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj)));\r
4193 \r
4194 #if 0\r
4195         YPRINTF(("Object %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d\n",\r
4196                         obj->objectId, oh->name, obj->dirty, obj->valid, obj->serial, \r
4197                         obj->sum, obj->chunkId));\r
4198                 switch(obj->variantType)\r
4199         {\r
4200                 case YAFFS_OBJECT_TYPE_FILE: \r
4201                         YPRINTF((" FILE length %d\n",obj->variant.fileVariant.fileSize));\r
4202                         break;\r
4203                 case YAFFS_OBJECT_TYPE_DIRECTORY:\r
4204                         YPRINTF((" DIRECTORY\n"));\r
4205                         break;\r
4206                 case YAFFS_OBJECT_TYPE_HARDLINK: //todo\r
4207                 case YAFFS_OBJECT_TYPE_SYMLINK:\r
4208                 case YAFFS_OBJECT_TYPE_UNKNOWN:\r
4209                 default:\r
4210         }\r
4211 #endif\r
4212         \r
4213         return YAFFS_OK;\r
4214 }\r
4215 \r
4216 \r
4217 ///////////////////////// Initialisation code ///////////////////////////\r
4218 \r
4219 \r
4220 \r
4221 int yaffs_GutsInitialise(yaffs_Device *dev)\r
4222 {\r
4223         unsigned x;\r
4224         int bits;\r
4225         int extraBits;\r
4226         int nBlocks;\r
4227 \r
4228 \r
4229         \r
4230         if(!yaffs_CheckStructures())\r
4231         {\r
4232                 T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs_CheckStructures failed\n" TENDSTR)));\r
4233                 return YAFFS_FAIL;\r
4234         }\r
4235         \r
4236         if(dev->startBlock <= 0 ||\r
4237            (dev->endBlock - dev->startBlock) < 10)\r
4238         {\r
4239                 T(YAFFS_TRACE_ALWAYS,(TSTR("startBlock %d or endBlock %d invalid\n" TENDSTR),\r
4240                                 dev->startBlock, dev->endBlock));\r
4241                 return YAFFS_FAIL;\r
4242         }\r
4243         \r
4244         nBlocks = dev->endBlock - dev->startBlock + 1;\r
4245         \r
4246 \r
4247                 \r
4248         // OK now calculate a few things for the device\r
4249         // Calculate chunkGroupBits. \r
4250         // We need to find the next power of 2 > than endBlock\r
4251         \r
4252         x = YAFFS_CHUNKS_PER_BLOCK * (dev->endBlock+1);\r
4253         \r
4254         for(bits = extraBits = 0; x > 1; bits++)\r
4255         {\r
4256                 if(x & 1) extraBits++;\r
4257                 x >>= 1;\r
4258         }\r
4259 \r
4260         if(extraBits > 0) bits++;\r
4261         \r
4262         \r
4263         // Level0 Tnodes are 16 bits, so if the bitwidth of the\r
4264         // chunk range we're using is greater than 16 we need \r
4265         // to figure out chunk shift and chunkGroupSize\r
4266         if(bits <= 16) \r
4267         {\r
4268                 dev->chunkGroupBits = 0;\r
4269         }\r
4270         else\r
4271         {\r
4272                 dev->chunkGroupBits = bits - 16;\r
4273         }\r
4274         dev->chunkGroupSize = 1 << dev->chunkGroupBits;\r
4275         \r
4276         \r
4277         // More device initialisation\r
4278         dev->garbageCollectionRequired  = 0;\r
4279         dev->garbageCollections = 0;\r
4280         dev->currentDirtyChecker = 0;\r
4281         dev->bufferedBlock = -1;\r
4282         dev->doingBufferedBlockRewrite = 0;\r
4283         dev->blockSelectedForGC = -1;\r
4284         dev->nDeletedFiles = 0;\r
4285         dev->nBackgroundDeletions=0;\r
4286         dev->nUnlinkedFiles = 0;\r
4287         dev->eccFixed=0;\r
4288         dev->eccUnfixed=0;\r
4289         dev->tagsEccFixed=0;\r
4290         dev->tagsEccUnfixed=0;\r
4291         \r
4292         yaffs_InitialiseBlocks(dev,nBlocks);\r
4293         \r
4294         yaffs_InitialiseTnodes(dev);\r
4295 \r
4296         yaffs_InitialiseObjects(dev);\r
4297         \r
4298 #ifdef CONFIG_YAFFS_SHORT_OP_CACHE\r
4299         { \r
4300                 int i;\r
4301                 for(i=0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
4302                 {\r
4303                         dev->srCache[i].object = NULL;\r
4304                         dev->srCache[i].lastUse = 0;\r
4305                         dev->srCache[i].dirty = 0;\r
4306                 }\r
4307                 dev->srLastUse = 0;\r
4308         }\r
4309 #endif\r
4310 \r
4311         dev->cacheHits = 0;\r
4312         \r
4313         \r
4314         // Initialise the unlinked, root and lost and found directories\r
4315         dev->lostNFoundDir = dev->rootDir = dev->unlinkedDir = NULL;\r
4316         \r
4317         dev->unlinkedDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_UNLINKED, S_IFDIR);\r
4318 \r
4319         dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR);\r
4320         dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_LOSTNFOUND_MODE | S_IFDIR);\r
4321         yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir);\r
4322         \r
4323                 \r
4324         // Now scan the flash.  \r
4325         yaffs_Scan(dev);\r
4326         \r
4327         // Zero out stats\r
4328         dev->nPageReads = 0;\r
4329     dev->nPageWrites =  0;\r
4330         dev->nBlockErasures = 0;\r
4331         dev->nGCCopies = 0;\r
4332         dev->nRetriedWrites = 0;\r
4333         dev->nRetiredBlocks = 0;\r
4334 \r
4335         \r
4336         return YAFFS_OK;\r
4337                 \r
4338 }\r
4339 \r
4340 void yaffs_Deinitialise(yaffs_Device *dev)\r
4341 {\r
4342         yaffs_DeinitialiseBlocks(dev);\r
4343         yaffs_DeinitialiseTnodes(dev);\r
4344         yaffs_DeinitialiseObjects(dev);\r
4345         \r
4346 }\r
4347 \r
4348 int  yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)\r
4349 {\r
4350         int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS);\r
4351         \r
4352         struct list_head *i;    \r
4353         yaffs_Object *l;\r
4354         \r
4355         \r
4356         // To the free chunks add the chunks that are in the deleted unlinked files.\r
4357         list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)\r
4358         {\r
4359                 l = list_entry(i, yaffs_Object,siblings);\r
4360                 if(l->deleted)\r
4361                 {\r
4362                         nFree++;\r
4363                         nFree += l->nDataChunks;\r
4364                 }\r
4365         }\r
4366 \r
4367 \r
4368         return (nFree < 0) ? 0 : nFree; \r
4369         \r
4370 }\r
4371 \r
4372 \r
4373 /////////////////// YAFFS test code //////////////////////////////////\r
4374 \r
4375 #define yaffs_CheckStruct(structure,syze, name) \\r
4376            if(sizeof(structure) != syze) \\r
4377                { \\r
4378                  T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),name,syze,sizeof(structure))); \\r
4379                  return YAFFS_FAIL; \\r
4380                    }\r
4381                  \r
4382                  \r
4383 static int yaffs_CheckStructures(void)\r
4384 {\r
4385         yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags")\r
4386         yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion")\r
4387         yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare")\r
4388         yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode")\r
4389         yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader")\r
4390         \r
4391         \r
4392         return YAFFS_OK;\r
4393 }\r
4394 \r
4395 #if 0\r
4396 void yaffs_GutsTest(yaffs_Device *dev)\r
4397 {\r
4398         \r
4399         if(yaffs_CheckStructures() != YAFFS_OK)\r
4400         {\r
4401                 T(YAFFS_TRACE_ALWAYS,(TSTR("One or more structures malformed-- aborting\n" TENDSTR)));\r
4402                 return;\r
4403         }\r
4404         \r
4405         yaffs_TnodeTest(dev);\r
4406         yaffs_ObjectTest(dev);  \r
4407 }\r
4408 #endif\r
4409 \r
4410 \r
4411 \r