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