]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/fs/fat/v2_0/src/fatfs_ncache.c
Initial revision
[karo-tx-redboot.git] / packages / fs / fat / v2_0 / src / fatfs_ncache.c
1 //==========================================================================
2 //
3 //      fatfs_ncache.c
4 //
5 //      FAT file system node cache functions
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // -------------------------------------------
37 //####ECOSGPLCOPYRIGHTEND####
38 //==========================================================================
39 //#####DESCRIPTIONBEGIN####
40 //
41 // Author(s):           Savin Zlobec <savin@elatec.si> 
42 // Date:                2003-06-26
43 //
44 //####DESCRIPTIONEND####
45 //
46 //==========================================================================
47
48 #include <pkgconf/fs_fat.h>
49 #include <pkgconf/infra.h>
50 #include <cyg/infra/cyg_type.h>
51 #include <cyg/infra/cyg_ass.h>
52 #include <cyg/infra/cyg_trac.h>
53 #include <cyg/infra/diag.h>
54 #include <sys/types.h>
55 #include <ctype.h>
56
57 #include "fatfs.h"
58
59 //==========================================================================
60 // Defines & macros 
61
62 #ifdef CYGDBG_USE_ASSERTS
63 # define USE_ASSERTS 1
64 #endif
65
66 #ifdef FATFS_NODE_CACHE_EXTRA_CHECKS
67 # define USE_XCHECKS 1
68 #endif
69
70 #ifdef FATFS_TRACE_NODE_CACHE
71 # define TNC 1
72 #else
73 # define TNC 0
74 #endif
75
76 // This defines how many nodes should always be kept in dead list - 
77 // it should be >= 2 or the file finding code may not work correctly!
78 #define DLIST_KEEP_NUM 2 
79
80 //==========================================================================
81 // Node list functions 
82
83 static void
84 node_list_init(fatfs_node_list_t *list)
85 {
86     list->size  = 0;
87     list->first = list->last = NULL;
88 }
89
90 static __inline__ int 
91 node_list_get_size(fatfs_node_list_t *list)
92 {
93     return (list->size);
94 }
95
96 static __inline__ fatfs_node_t*
97 node_list_get_head(fatfs_node_list_t *list)
98 {
99     return (list->first);
100 }
101
102 static __inline__ fatfs_node_t*
103 node_list_get_tail(fatfs_node_list_t *list)
104 {
105     return (list->last);
106 }
107
108 static __inline__ fatfs_node_t*
109 node_list_get_next(fatfs_node_t *node)
110 {
111     return (node->list_next);
112 }
113
114 static __inline__ fatfs_node_t*
115 node_list_get_prev(fatfs_node_t *node)
116 {
117     return (node->list_prev);
118 }
119
120 static __inline__ bool
121 node_list_is_last(fatfs_node_t *node)
122 {
123     return (NULL == node->list_next);
124 }
125
126 static __inline__ bool
127 node_list_is_first(fatfs_node_t *node)
128 {
129     return (NULL == node->list_prev);
130 }
131
132 static void
133 node_list_head_add(fatfs_node_list_t *list, fatfs_node_t *node)
134 {
135     node->list_prev = NULL;
136     node->list_next = list->first;
137     
138     if (NULL == list->first)
139     {
140         CYG_ASSERTC(list->size == 0);
141         list->last = node;
142     }
143     else
144     {
145         list->first->list_prev = node;
146     }
147     
148     list->first = node;
149     list->size++;
150 }
151
152 #if 0
153 static __inline__ fatfs_node_t*
154 node_list_head_get(fatfs_node_list_t *list)
155 {
156     return list->first;
157 }
158
159 static void
160 node_list_tail_add(fatfs_node_list_t *list, fatfs_node_t *node)
161 {
162     node->list_prev = list->last;
163     node->list_next = NULL;
164     
165     if (NULL == list->last)
166     {
167         CYG_ASSERTC(list->size == 0);
168         list->first = node;
169     }
170     else
171     {
172         list->last->list_next = node;
173     }
174     
175     list->last = node;
176     list->size++;
177 }
178 #endif
179
180 static __inline__ fatfs_node_t*
181 node_list_tail_get(fatfs_node_list_t *list)
182 {
183     return list->last;
184 }
185
186 static void
187 node_list_remove(fatfs_node_list_t *list, fatfs_node_t *node)
188 {
189     if (list->first == list->last)
190     {
191         if (list->first == node)
192         {
193             CYG_ASSERTC(list->size == 1);
194             list->first = list->last = NULL;
195         }
196         else
197         {
198             CYG_ASSERT(false, "chain node not in list!");
199         }
200     }
201     else if (list->first == node)
202     {
203         CYG_ASSERTC(node->list_prev == NULL); 
204         list->first = node->list_next;
205         list->first->list_prev = NULL;
206     }
207     else if (list->last == node)
208     {
209         CYG_ASSERTC(node->list_next == NULL); 
210         list->last = node->list_prev;
211         list->last->list_next = NULL;
212     }
213     else
214     {
215         CYG_ASSERTC(node->list_prev != NULL && node->list_next != NULL); 
216         node->list_prev->list_next = node->list_next;
217         node->list_next->list_prev = node->list_prev;        
218     }
219     list->size--;
220 }
221
222 #ifdef USE_XCHECKS
223 static void
224 node_list_check(fatfs_node_list_t *list, int min_ref, int max_ref)
225 {
226     int i;
227     fatfs_node_t *node, *pnode;
228
229     CYG_ASSERT((list->last == NULL && list->first == NULL) ||
230                (list->last != NULL && list->first != NULL), 
231                "list->first and list->last broken!");
232
233     if (list->first == NULL)
234     {
235         CYG_ASSERTC(list->size == 0);
236         return;
237     }
238     
239     CYG_ASSERTC(list->first->list_prev == NULL);
240     CYG_ASSERTC(list->last->list_next == NULL);
241
242     CYG_ASSERTC(list->first->refcnt >= min_ref && 
243                 list->first->refcnt <= max_ref);
244     CYG_ASSERTC(list->last->refcnt >= min_ref && 
245                 list->last->refcnt <= max_ref);
246     
247     i = 1;
248     node = list->first; 
249     pnode = NULL;
250     while (node->list_next != NULL)
251     {
252         i++;
253         CYG_ASSERTC(node->list_prev == pnode);
254         CYG_ASSERTC(node->refcnt >= min_ref && node->refcnt <= max_ref);
255         pnode = node;
256         node = node->list_next;
257     }
258     CYG_ASSERTC(node->list_prev == pnode);
259     CYG_ASSERTC(list->size == i);
260     CYG_ASSERTC(node == list->last);
261 }
262
263 static void
264 node_lists_check(fatfs_disk_t* disk)
265 {
266     node_list_check(&disk->live_nlist, 1, 99999);                        
267     node_list_check(&disk->dead_nlist, 0, 0);                           
268 }
269 #endif // USE_XCHECKS
270
271 //==========================================================================
272 // Node hash functions 
273
274 static unsigned int 
275 hash_fn(const char *str, unsigned int strlen)
276 {
277     unsigned int i = 0, val = 0;
278
279     while (i++ < strlen)
280         val = (val ^ (int)toupper(*str++)) << 1;
281     return(val);
282 }
283
284 static void
285 node_hash_init(fatfs_hash_table_t *tbl)
286 {
287     int i;
288
289     // Set size and clear all slots
290     tbl->size = FATFS_HASH_TABLE_SIZE;
291     for (i = 0; i < tbl->size; i++)
292         tbl->nodes[i] = NULL;
293     tbl->n = 0;
294 }
295
296 static bool
297 node_hash_add(fatfs_hash_table_t *tbl, fatfs_node_t *node)
298 {
299     unsigned int hval;
300    
301     // Calculate hash of given node filename
302     hval = hash_fn(node->dentry.filename, 
303                    strlen(node->dentry.filename)) % tbl->size;
304
305     CYG_TRACE2(TNC, "name='%s' hval=%d", node->dentry.filename, hval);
306
307     if (tbl->nodes[hval] == NULL)
308     {
309         // First node in this slot
310         node->hash_next  = NULL;
311         tbl->nodes[hval] = node;
312         tbl->n++; 
313         return true;
314     }
315     else
316     { 
317         // More nodes in this slot
318         fatfs_node_t *lnode, *pnode;
319        
320         pnode = NULL;
321         lnode = tbl->nodes[hval];
322
323         // Insert node into list so that it is sorted by filename        
324         while (lnode != NULL)
325         {
326             if (lnode == node)
327                 return false;
328             
329             if (strcasecmp(lnode->dentry.filename, node->dentry.filename) > 0)
330             {
331                 if (pnode != NULL)
332                     pnode->hash_next = node; // Insert in the middle
333                 else
334                     tbl->nodes[hval] = node; // Insert at the beginning
335                 node->hash_next = lnode;
336                 tbl->n++; 
337                 return true;
338             }    
339             
340             pnode = lnode;    
341             lnode = lnode->hash_next;
342         }
343         // Insert at the end
344         pnode->hash_next = node;
345         node->hash_next  = NULL;
346         tbl->n++; 
347         return true;
348     }
349 }
350
351 static fatfs_node_t*
352 node_hash_find(fatfs_hash_table_t *tbl, 
353                const char         *name,
354                unsigned int        namelen,
355                unsigned int        parent_cluster)
356 {   
357     unsigned int  hval; 
358     fatfs_node_t *node;
359
360     // Calculate hash of name and get the first node in slot
361     hval = hash_fn(name, namelen) % tbl->size;
362     node = tbl->nodes[hval];
363
364     CYG_TRACE2(TNC, "name='%s' hval=%d\n", name, hval);
365  
366     // Find the node in list wich matches the 
367     // given name and parent_cluster
368     while (node != NULL)
369     {
370         // First compare the parent cluster number and 
371         // check filename length since it is faster than 
372         // comparing filenames
373         if (parent_cluster == node->dentry.parent_cluster &&
374             '\0' == node->dentry.filename[namelen])
375         {
376             int i = strncasecmp(node->dentry.filename, name, namelen);
377
378             if (i == 0)
379                 return node;
380             else if (i > 0)
381                 return NULL; // Stop searching - we have a 
382                              // sorted list so there can't be
383                              // any matching filename further on
384                              // if i > 0 - look at node_hash_add
385         }
386         node = node->hash_next;
387     }
388     
389     // No such node found
390     return NULL; 
391 }
392
393 static bool
394 node_hash_remove_keyless(fatfs_hash_table_t *tbl, fatfs_node_t *node)
395 {
396     int i;
397     fatfs_node_t *lnode, *pnode;
398
399     // Look in all slots, since we have no key
400     for (i = 0; i < tbl->size; i++)
401     {
402         // Look in list and remove it if there
403         lnode = tbl->nodes[i];
404         pnode = NULL;
405         while (lnode != NULL)
406         {
407             if (lnode == node)
408             {
409                 if (pnode == NULL)
410                     tbl->nodes[i]    = lnode->hash_next;
411                 else
412                     pnode->hash_next = lnode->hash_next;
413                 node->hash_next = NULL;
414                 tbl->n--;
415             
416                 // All done
417                 return true;
418             }
419             pnode = lnode;
420             lnode = lnode->hash_next;
421         }    
422     }
423     return false;
424 }
425
426 static bool 
427 node_hash_remove(fatfs_hash_table_t *tbl, fatfs_node_t *node)
428 {
429     unsigned int hval;
430     fatfs_node_t *lnode, *pnode;
431    
432     // Calculate hash of name and get the first node in slot
433     hval = hash_fn(node->dentry.filename, 
434                    strlen(node->dentry.filename)) % tbl->size;
435     lnode = tbl->nodes[hval];
436
437     // Now find the node in list and remove it
438     pnode = NULL;
439     while (lnode != NULL)
440     {
441         if (lnode == node)
442         {
443             if (pnode == NULL)
444                 tbl->nodes[hval] = lnode->hash_next;
445             else
446                 pnode->hash_next = lnode->hash_next;
447             node->hash_next = NULL;
448             tbl->n--;
449             
450             // All done
451             return true;
452         }
453         pnode = lnode;
454         lnode = lnode->hash_next;
455     } 
456
457     // Node not in list 
458     return false;
459 }
460
461 #ifdef USE_XCHECKS
462 static void
463 node_hash_check(fatfs_hash_table_t *tbl)
464 {
465     int i, n;
466
467     n = 0;
468     for (i = 0; i < tbl->size; i++)
469     {
470         fatfs_node_t *lnode, *pnode;
471
472         pnode = NULL;
473         lnode = tbl->nodes[i];
474
475         while (lnode != NULL)
476         {
477             if (pnode != NULL)
478             {
479                 int c = strcasecmp(pnode->dentry.filename, lnode->dentry.filename);
480                 CYG_ASSERT(c <= 0, "hash table not sorted");
481                 CYG_ASSERT(pnode->dentry.parent_cluster != lnode->dentry.parent_cluster ||
482                            0 != c, "duplicated node in hash table");
483             }
484             n++;
485             pnode = lnode;
486             lnode = lnode->hash_next;
487         }
488     }
489     CYG_ASSERTC(tbl->n == n);
490 }
491
492 static void
493 node_hash_not_found_check(fatfs_disk_t *disk, 
494                           const char   *name,
495                           unsigned int  namelen,
496                           unsigned int  parent_cluster)
497 {
498     fatfs_node_t* node;
499
500     node = node_list_get_head(&disk->live_nlist);
501     while (NULL != node)
502     {
503         if (node->dentry.parent_cluster == parent_cluster          &&
504             namelen == strlen(node->dentry.filename)               &&
505             0 == strncasecmp(name, node->dentry.filename, namelen))
506             CYG_ASSERT(false, "node not found in hash, "
507                               "but exists in live list");
508         node = node_list_get_next(node);
509     }
510
511     node = node_list_get_head(&disk->dead_nlist);
512     while (NULL != node)
513     {
514         if (node->dentry.parent_cluster == parent_cluster          &&
515             namelen == strlen(node->dentry.filename)               &&
516             0 == strncasecmp(name, node->dentry.filename, namelen))
517             CYG_ASSERT(false, "node not found in hash, "
518                               "but exists in dead list");
519         node = node_list_get_next(node);
520     }   
521 }
522
523 static void
524 node_hash_found_check(fatfs_disk_t *disk, 
525                       const char   *name,
526                       unsigned int  namelen,
527                       unsigned int  parent_cluster,
528                       fatfs_node_t* node)
529 {
530     fatfs_node_t *n;
531
532     n = node_list_get_head(&disk->live_nlist);
533     while (NULL != n)
534     {
535         if (n == node) 
536         {
537             if (node->dentry.parent_cluster != parent_cluster ||
538                 namelen != strlen(node->dentry.filename)      ||
539                 0 != strncasecmp(name, node->dentry.filename, namelen))
540                 CYG_ASSERT(false, "node_hash_find returned wrong node");
541             return;
542         }
543         n = node_list_get_next(n);
544     }
545
546     n = node_list_get_head(&disk->dead_nlist);
547     while (NULL != n)
548     {
549         if (n == node)
550         {
551             if (node->dentry.parent_cluster != parent_cluster ||
552                 namelen != strlen(node->dentry.filename)      ||
553                 0 != strncasecmp(name, node->dentry.filename, namelen))
554                 CYG_ASSERT(false, "node_hash_find returned wrong node");
555             return;
556         } 
557         n = node_list_get_next(n);
558     }
559
560     CYG_ASSERT(false, "node not found in dead or live lists, "
561                       "but exists in hash.");
562 }
563 #endif // USE_XCHECKS
564     
565 //--------------------------------------------------------------------------
566
567 #ifdef USE_XCHECKS
568
569 # define SANITY_CHECK()                                                     \
570     CYG_MACRO_START                                                         \
571         node_lists_check(disk);                                             \
572         node_hash_check(&disk->node_hash);                                  \
573         CYG_ASSERTC((disk->live_nlist.size + disk->dead_nlist.size) ==      \
574                     disk->node_hash.n);                                     \
575     CYG_MACRO_END    
576
577 # define NODE_FIND_CHECK()                                                    \
578     CYG_MACRO_START                                                           \
579         if (NULL == node)                                                     \
580             node_hash_not_found_check(disk, name, namelen, parent_cluster);   \
581         else                                                                  \
582             node_hash_found_check(disk, name, namelen, parent_cluster, node); \
583     CYG_MACRO_END
584
585 #else // USE_XCHECKS
586 # define SANITY_CHECK()    CYG_EMPTY_STATEMENT
587 # define NODE_FIND_CHECK() CYG_EMPTY_STATEMENT
588 #endif // not USE_XCHECKS
589
590 //==========================================================================
591 // Node pool allocation functions
592
593 static void
594 node_pool_init(fatfs_disk_t *disk)
595 {
596     int i;
597
598     for (i = 0; i < FATFS_NODE_POOL_SIZE; i++)
599         disk->node_pool[i] = &disk->node_pool_base[i];
600
601     disk->node_pool_free_cnt = i;
602 }
603
604 static fatfs_node_t *
605 node_pool_alloc(fatfs_disk_t *disk)
606 {
607     fatfs_node_t *node = NULL;
608
609     if (disk->node_pool_free_cnt > 0)
610         node = disk->node_pool[--disk->node_pool_free_cnt];
611
612     return node;
613 }
614
615 static void
616 node_pool_free(fatfs_disk_t *disk, fatfs_node_t *node)
617 {
618     disk->node_pool[disk->node_pool_free_cnt++] = node;
619 }
620     
621 //==========================================================================
622 //==========================================================================
623 // Exported functions 
624
625 //--------------------------------------------------------------------------
626 // fatfs_node_cache_init()
627 // Initializes node cache of given disk.
628
629 void
630 fatfs_node_cache_init(fatfs_disk_t *disk)
631 {
632     CYG_CHECK_DATA_PTRC(disk);
633     
634     node_list_init(&disk->live_nlist);
635     node_list_init(&disk->dead_nlist);
636     node_hash_init(&disk->node_hash);
637     node_pool_init(disk);
638     
639     SANITY_CHECK();
640 }
641
642 //--------------------------------------------------------------------------
643 // fatfs_node_cache_flush()
644 // Frees all node cache of given disk.
645
646 void
647 fatfs_node_cache_flush(fatfs_disk_t *disk)
648 {
649     fatfs_node_t *node, *next_node;
650  
651     node = node_list_get_head(&disk->live_nlist);
652
653     while (NULL != node)
654     {
655         next_node = node_list_get_next(node);
656         node_list_remove(&disk->live_nlist, node);
657         if (!node_hash_remove(&disk->node_hash, node))
658             CYG_ASSERT(false, "Node not in hash");
659         node_pool_free(disk, node);
660         node = next_node;
661     }
662
663     node = node_list_get_head(&disk->dead_nlist);
664
665     while (NULL != node)
666     {
667         next_node = node_list_get_next(node);
668         node_list_remove(&disk->dead_nlist, node);
669         if (!node_hash_remove(&disk->node_hash, node))
670             CYG_ASSERT(false, "Node not in hash");
671         node_pool_free(disk, node);
672         node = next_node;
673     }
674
675     SANITY_CHECK();
676 }
677
678 //--------------------------------------------------------------------------
679 // fatfs_node_alloc()
680 // Allocates a new node.  
681
682 fatfs_node_t*
683 fatfs_node_alloc(fatfs_disk_t *disk, fatfs_dir_entry_t *dentry)
684 {
685     fatfs_node_t *node;
686
687     CYG_CHECK_DATA_PTRC(disk);
688     CYG_CHECK_DATA_PTRC(dentry);
689  
690     node = node_pool_alloc(disk);
691         
692     if (NULL == node)
693     {
694         CYG_TRACE2(TNC, "getting node from dead list (size=%d keep=%d)",
695                         node_list_get_size(&disk->dead_nlist), DLIST_KEEP_NUM);
696         
697         if (node_list_get_size(&disk->dead_nlist) <= DLIST_KEEP_NUM)
698             return NULL;
699         
700         node = node_list_tail_get(&disk->dead_nlist);
701         if (NULL == node)
702             return NULL;
703         
704         CYG_TRACE1(TNC, "recycling node='%s'", node->dentry.filename); 
705
706         node_list_remove(&disk->dead_nlist, node);
707         if (!node_hash_remove(&disk->node_hash, node))
708             CYG_ASSERT(false, "node not in hash");
709     }     
710
711     // Init new node    
712     node->dentry = *dentry;
713     node->refcnt = 0;
714
715     node_list_head_add(&disk->dead_nlist, node);
716     if (!node_hash_add(&disk->node_hash, node))
717         CYG_ASSERT(false, "node already in hash");
718
719     SANITY_CHECK();
720
721     return node;
722 }
723
724 //--------------------------------------------------------------------------
725 // fatfs_node_touch()
726 // Moves given node to top of its list.
727 // (When reusing dead node it is always taken from the bottom of list) 
728
729 void
730 fatfs_node_touch(fatfs_disk_t *disk, fatfs_node_t *node)
731 {
732     CYG_CHECK_DATA_PTRC(disk);
733     CYG_CHECK_DATA_PTRC(node);
734     CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->dentry.filename, node->refcnt);
735
736     if (node->refcnt == 0)
737     {
738         node_list_remove(&disk->dead_nlist, node);
739         node_list_head_add(&disk->dead_nlist, node);
740     }
741     else
742     {
743         node_list_remove(&disk->live_nlist, node);
744         node_list_head_add(&disk->live_nlist, node);
745     } 
746
747     SANITY_CHECK();
748 }
749
750 //--------------------------------------------------------------------------
751 // fatfs_node_ref()
752 // Increases the given node reference count.
753 // If the reference goes from 0 to 1, than the node
754 // is moved from dead list to live list.
755
756 void
757 fatfs_node_ref(fatfs_disk_t *disk, fatfs_node_t *node)
758 {
759     CYG_CHECK_DATA_PTRC(disk);
760     CYG_CHECK_DATA_PTRC(node);
761     CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->dentry.filename, node->refcnt);
762     
763     node->refcnt++;    
764     if (1 == node->refcnt)
765     {
766         // First reference - move node from dead to live list
767
768         CYG_TRACE1(TNC, "node='%s' to live list", node->dentry.filename);
769         
770         node_list_remove(&disk->dead_nlist, node);
771         node_list_head_add(&disk->live_nlist, node);
772     }
773
774     SANITY_CHECK();
775 }
776
777 //--------------------------------------------------------------------------
778 // fatfs_node_unref()
779 // Decreases the given node reference count.
780 // If the reference goes from 1 to 0, than the node
781 // is moved from live list to top of dead list.
782 // (When reusing dead node it is always taken from the bottom of list)
783
784 void
785 fatfs_node_unref(fatfs_disk_t *disk, fatfs_node_t *node)
786 {
787     CYG_CHECK_DATA_PTRC(disk);
788     CYG_CHECK_DATA_PTRC(node);
789     CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->dentry.filename, node->refcnt);
790     CYG_ASSERT(node->refcnt > 0, "node->refcnt <= 0");
791
792     node->refcnt--;
793     if (0 == node->refcnt)
794     {
795         // No more references - move node from live to dead list
796
797         CYG_TRACE1(TNC, "node='%s' to dead list", node->dentry.filename);
798
799         node_list_remove(&disk->live_nlist, node);
800         node_list_head_add(&disk->dead_nlist, node);
801     }
802
803     SANITY_CHECK();
804 }
805
806 //--------------------------------------------------------------------------
807 // fatfs_node_free()
808 // Frees node memory and removes it from its list and hash. 
809 // This function must not be called on a referenced node.
810
811 void
812 fatfs_node_free(fatfs_disk_t *disk, fatfs_node_t *node)
813 {
814     CYG_CHECK_DATA_PTRC(disk);
815     CYG_CHECK_DATA_PTRC(node);
816     CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->dentry.filename, node->refcnt);
817     CYG_ASSERTC(node->refcnt == 0);
818     CYG_ASSERTC(node != disk->root);
819
820     // Remove from dead list, from hash and free ptr
821
822     node_list_remove(&disk->dead_nlist, node);
823     if (!node_hash_remove(&disk->node_hash, node))
824         CYG_ASSERT(false, "node not in hash");   
825     
826     node_pool_free(disk, node);
827
828     SANITY_CHECK();
829 }
830
831 //--------------------------------------------------------------------------
832 // fatfs_node_rehash()
833 // Recalculates given node hash key. 
834
835 void
836 fatfs_node_rehash(fatfs_disk_t *disk, fatfs_node_t *node)
837 {
838     CYG_CHECK_DATA_PTRC(disk);
839     CYG_CHECK_DATA_PTRC(node);
840     
841     if (!node_hash_remove_keyless(&disk->node_hash, node))
842         CYG_ASSERT(false, "node not in hash");
843     
844     if (!node_hash_add(&disk->node_hash, node))
845         CYG_ASSERT(false, "node already in hash");
846     
847     SANITY_CHECK();
848 }
849
850 //--------------------------------------------------------------------------
851 // fatfs_node_find()
852 // Finds node in hash by its name and parent cluster. 
853 // If no node found NULL is returned.
854
855 fatfs_node_t*
856 fatfs_node_find(fatfs_disk_t *disk, 
857                 const char   *name, 
858                 unsigned int  namelen,
859                 unsigned int  parent_cluster)
860 {
861     fatfs_node_t *node;
862
863     CYG_CHECK_DATA_PTRC(disk);
864     CYG_CHECK_DATA_PTRC(name);
865  
866     node = node_hash_find(&disk->node_hash, name, namelen, parent_cluster);
867
868     NODE_FIND_CHECK();
869     
870     CYG_TRACE3(TNC, "node name=%s pcluster=%d %s found in cache\n",
871         name, parent_cluster, ((node != NULL) ? "" : "not"));
872     
873     return node;
874 }
875
876 //--------------------------------------------------------------------------
877 // fatfs_get_live_node_count()
878 // Gets the number of live nodes.
879
880 int
881 fatfs_get_live_node_count(fatfs_disk_t *disk)
882 {
883     return node_list_get_size(&disk->live_nlist);
884 }
885
886 //--------------------------------------------------------------------------
887 // fatfs_get_dead_node_count()
888 // Gets the number of dead nodes.
889
890 int
891 fatfs_get_dead_node_count(fatfs_disk_t *disk)
892 {
893     return node_list_get_size(&disk->dead_nlist);
894 }
895
896 // -------------------------------------------------------------------------
897 // EOF fatfs_ncache.c