1 //==========================================================================
5 // FAT file system node cache functions
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.
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.
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
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.
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.
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.
36 // -------------------------------------------
37 //####ECOSGPLCOPYRIGHTEND####
38 //==========================================================================
39 //#####DESCRIPTIONBEGIN####
41 // Author(s): Savin Zlobec <savin@elatec.si>
44 //####DESCRIPTIONEND####
46 //==========================================================================
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>
59 //==========================================================================
62 #ifdef CYGDBG_USE_ASSERTS
63 # define USE_ASSERTS 1
66 #ifdef FATFS_NODE_CACHE_EXTRA_CHECKS
67 # define USE_XCHECKS 1
70 #ifdef FATFS_TRACE_NODE_CACHE
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
80 //==========================================================================
81 // Node list functions
84 node_list_init(fatfs_node_list_t *list)
87 list->first = list->last = NULL;
91 node_list_get_size(fatfs_node_list_t *list)
96 static __inline__ fatfs_node_t*
97 node_list_get_head(fatfs_node_list_t *list)
102 static __inline__ fatfs_node_t*
103 node_list_get_tail(fatfs_node_list_t *list)
108 static __inline__ fatfs_node_t*
109 node_list_get_next(fatfs_node_t *node)
111 return (node->list_next);
114 static __inline__ fatfs_node_t*
115 node_list_get_prev(fatfs_node_t *node)
117 return (node->list_prev);
120 static __inline__ bool
121 node_list_is_last(fatfs_node_t *node)
123 return (NULL == node->list_next);
126 static __inline__ bool
127 node_list_is_first(fatfs_node_t *node)
129 return (NULL == node->list_prev);
133 node_list_head_add(fatfs_node_list_t *list, fatfs_node_t *node)
135 node->list_prev = NULL;
136 node->list_next = list->first;
138 if (NULL == list->first)
140 CYG_ASSERTC(list->size == 0);
145 list->first->list_prev = node;
153 static __inline__ fatfs_node_t*
154 node_list_head_get(fatfs_node_list_t *list)
160 node_list_tail_add(fatfs_node_list_t *list, fatfs_node_t *node)
162 node->list_prev = list->last;
163 node->list_next = NULL;
165 if (NULL == list->last)
167 CYG_ASSERTC(list->size == 0);
172 list->last->list_next = node;
180 static __inline__ fatfs_node_t*
181 node_list_tail_get(fatfs_node_list_t *list)
187 node_list_remove(fatfs_node_list_t *list, fatfs_node_t *node)
189 if (list->first == list->last)
191 if (list->first == node)
193 CYG_ASSERTC(list->size == 1);
194 list->first = list->last = NULL;
198 CYG_ASSERT(false, "chain node not in list!");
201 else if (list->first == node)
203 CYG_ASSERTC(node->list_prev == NULL);
204 list->first = node->list_next;
205 list->first->list_prev = NULL;
207 else if (list->last == node)
209 CYG_ASSERTC(node->list_next == NULL);
210 list->last = node->list_prev;
211 list->last->list_next = NULL;
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;
224 node_list_check(fatfs_node_list_t *list, int min_ref, int max_ref)
227 fatfs_node_t *node, *pnode;
229 CYG_ASSERT((list->last == NULL && list->first == NULL) ||
230 (list->last != NULL && list->first != NULL),
231 "list->first and list->last broken!");
233 if (list->first == NULL)
235 CYG_ASSERTC(list->size == 0);
239 CYG_ASSERTC(list->first->list_prev == NULL);
240 CYG_ASSERTC(list->last->list_next == NULL);
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);
250 while (node->list_next != NULL)
253 CYG_ASSERTC(node->list_prev == pnode);
254 CYG_ASSERTC(node->refcnt >= min_ref && node->refcnt <= max_ref);
256 node = node->list_next;
258 CYG_ASSERTC(node->list_prev == pnode);
259 CYG_ASSERTC(list->size == i);
260 CYG_ASSERTC(node == list->last);
264 node_lists_check(fatfs_disk_t* disk)
266 node_list_check(&disk->live_nlist, 1, 99999);
267 node_list_check(&disk->dead_nlist, 0, 0);
269 #endif // USE_XCHECKS
271 //==========================================================================
272 // Node hash functions
275 hash_fn(const char *str, unsigned int strlen)
277 unsigned int i = 0, val = 0;
280 val = (val ^ (int)toupper(*str++)) << 1;
285 node_hash_init(fatfs_hash_table_t *tbl)
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;
297 node_hash_add(fatfs_hash_table_t *tbl, fatfs_node_t *node)
301 // Calculate hash of given node filename
302 hval = hash_fn(node->dentry.filename,
303 strlen(node->dentry.filename)) % tbl->size;
305 CYG_TRACE2(TNC, "name='%s' hval=%d", node->dentry.filename, hval);
307 if (tbl->nodes[hval] == NULL)
309 // First node in this slot
310 node->hash_next = NULL;
311 tbl->nodes[hval] = node;
317 // More nodes in this slot
318 fatfs_node_t *lnode, *pnode;
321 lnode = tbl->nodes[hval];
323 // Insert node into list so that it is sorted by filename
324 while (lnode != NULL)
329 if (strcasecmp(lnode->dentry.filename, node->dentry.filename) > 0)
332 pnode->hash_next = node; // Insert in the middle
334 tbl->nodes[hval] = node; // Insert at the beginning
335 node->hash_next = lnode;
341 lnode = lnode->hash_next;
344 pnode->hash_next = node;
345 node->hash_next = NULL;
352 node_hash_find(fatfs_hash_table_t *tbl,
354 unsigned int namelen,
355 unsigned int parent_cluster)
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];
364 CYG_TRACE2(TNC, "name='%s' hval=%d\n", name, hval);
366 // Find the node in list wich matches the
367 // given name and parent_cluster
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])
376 int i = strncasecmp(node->dentry.filename, name, namelen);
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
386 node = node->hash_next;
389 // No such node found
394 node_hash_remove_keyless(fatfs_hash_table_t *tbl, fatfs_node_t *node)
397 fatfs_node_t *lnode, *pnode;
399 // Look in all slots, since we have no key
400 for (i = 0; i < tbl->size; i++)
402 // Look in list and remove it if there
403 lnode = tbl->nodes[i];
405 while (lnode != NULL)
410 tbl->nodes[i] = lnode->hash_next;
412 pnode->hash_next = lnode->hash_next;
413 node->hash_next = NULL;
420 lnode = lnode->hash_next;
427 node_hash_remove(fatfs_hash_table_t *tbl, fatfs_node_t *node)
430 fatfs_node_t *lnode, *pnode;
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];
437 // Now find the node in list and remove it
439 while (lnode != NULL)
444 tbl->nodes[hval] = lnode->hash_next;
446 pnode->hash_next = lnode->hash_next;
447 node->hash_next = NULL;
454 lnode = lnode->hash_next;
463 node_hash_check(fatfs_hash_table_t *tbl)
468 for (i = 0; i < tbl->size; i++)
470 fatfs_node_t *lnode, *pnode;
473 lnode = tbl->nodes[i];
475 while (lnode != NULL)
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");
486 lnode = lnode->hash_next;
489 CYG_ASSERTC(tbl->n == n);
493 node_hash_not_found_check(fatfs_disk_t *disk,
495 unsigned int namelen,
496 unsigned int parent_cluster)
500 node = node_list_get_head(&disk->live_nlist);
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);
511 node = node_list_get_head(&disk->dead_nlist);
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);
524 node_hash_found_check(fatfs_disk_t *disk,
526 unsigned int namelen,
527 unsigned int parent_cluster,
532 n = node_list_get_head(&disk->live_nlist);
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");
543 n = node_list_get_next(n);
546 n = node_list_get_head(&disk->dead_nlist);
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");
557 n = node_list_get_next(n);
560 CYG_ASSERT(false, "node not found in dead or live lists, "
561 "but exists in hash.");
563 #endif // USE_XCHECKS
565 //--------------------------------------------------------------------------
569 # define SANITY_CHECK() \
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); \
577 # define NODE_FIND_CHECK() \
580 node_hash_not_found_check(disk, name, namelen, parent_cluster); \
582 node_hash_found_check(disk, name, namelen, parent_cluster, node); \
586 # define SANITY_CHECK() CYG_EMPTY_STATEMENT
587 # define NODE_FIND_CHECK() CYG_EMPTY_STATEMENT
588 #endif // not USE_XCHECKS
590 //==========================================================================
591 // Node pool allocation functions
594 node_pool_init(fatfs_disk_t *disk)
598 for (i = 0; i < FATFS_NODE_POOL_SIZE; i++)
599 disk->node_pool[i] = &disk->node_pool_base[i];
601 disk->node_pool_free_cnt = i;
604 static fatfs_node_t *
605 node_pool_alloc(fatfs_disk_t *disk)
607 fatfs_node_t *node = NULL;
609 if (disk->node_pool_free_cnt > 0)
610 node = disk->node_pool[--disk->node_pool_free_cnt];
616 node_pool_free(fatfs_disk_t *disk, fatfs_node_t *node)
618 disk->node_pool[disk->node_pool_free_cnt++] = node;
621 //==========================================================================
622 //==========================================================================
623 // Exported functions
625 //--------------------------------------------------------------------------
626 // fatfs_node_cache_init()
627 // Initializes node cache of given disk.
630 fatfs_node_cache_init(fatfs_disk_t *disk)
632 CYG_CHECK_DATA_PTRC(disk);
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);
642 //--------------------------------------------------------------------------
643 // fatfs_node_cache_flush()
644 // Frees all node cache of given disk.
647 fatfs_node_cache_flush(fatfs_disk_t *disk)
649 fatfs_node_t *node, *next_node;
651 node = node_list_get_head(&disk->live_nlist);
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);
663 node = node_list_get_head(&disk->dead_nlist);
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);
678 //--------------------------------------------------------------------------
679 // fatfs_node_alloc()
680 // Allocates a new node.
683 fatfs_node_alloc(fatfs_disk_t *disk, fatfs_dir_entry_t *dentry)
687 CYG_CHECK_DATA_PTRC(disk);
688 CYG_CHECK_DATA_PTRC(dentry);
690 node = node_pool_alloc(disk);
694 CYG_TRACE2(TNC, "getting node from dead list (size=%d keep=%d)",
695 node_list_get_size(&disk->dead_nlist), DLIST_KEEP_NUM);
697 if (node_list_get_size(&disk->dead_nlist) <= DLIST_KEEP_NUM)
700 node = node_list_tail_get(&disk->dead_nlist);
704 CYG_TRACE1(TNC, "recycling node='%s'", node->dentry.filename);
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");
712 node->dentry = *dentry;
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");
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)
730 fatfs_node_touch(fatfs_disk_t *disk, fatfs_node_t *node)
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);
736 if (node->refcnt == 0)
738 node_list_remove(&disk->dead_nlist, node);
739 node_list_head_add(&disk->dead_nlist, node);
743 node_list_remove(&disk->live_nlist, node);
744 node_list_head_add(&disk->live_nlist, node);
750 //--------------------------------------------------------------------------
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.
757 fatfs_node_ref(fatfs_disk_t *disk, fatfs_node_t *node)
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);
764 if (1 == node->refcnt)
766 // First reference - move node from dead to live list
768 CYG_TRACE1(TNC, "node='%s' to live list", node->dentry.filename);
770 node_list_remove(&disk->dead_nlist, node);
771 node_list_head_add(&disk->live_nlist, node);
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)
785 fatfs_node_unref(fatfs_disk_t *disk, fatfs_node_t *node)
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");
793 if (0 == node->refcnt)
795 // No more references - move node from live to dead list
797 CYG_TRACE1(TNC, "node='%s' to dead list", node->dentry.filename);
799 node_list_remove(&disk->live_nlist, node);
800 node_list_head_add(&disk->dead_nlist, node);
806 //--------------------------------------------------------------------------
808 // Frees node memory and removes it from its list and hash.
809 // This function must not be called on a referenced node.
812 fatfs_node_free(fatfs_disk_t *disk, fatfs_node_t *node)
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);
820 // Remove from dead list, from hash and free ptr
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");
826 node_pool_free(disk, node);
831 //--------------------------------------------------------------------------
832 // fatfs_node_rehash()
833 // Recalculates given node hash key.
836 fatfs_node_rehash(fatfs_disk_t *disk, fatfs_node_t *node)
838 CYG_CHECK_DATA_PTRC(disk);
839 CYG_CHECK_DATA_PTRC(node);
841 if (!node_hash_remove_keyless(&disk->node_hash, node))
842 CYG_ASSERT(false, "node not in hash");
844 if (!node_hash_add(&disk->node_hash, node))
845 CYG_ASSERT(false, "node already in hash");
850 //--------------------------------------------------------------------------
852 // Finds node in hash by its name and parent cluster.
853 // If no node found NULL is returned.
856 fatfs_node_find(fatfs_disk_t *disk,
858 unsigned int namelen,
859 unsigned int parent_cluster)
863 CYG_CHECK_DATA_PTRC(disk);
864 CYG_CHECK_DATA_PTRC(name);
866 node = node_hash_find(&disk->node_hash, name, namelen, parent_cluster);
870 CYG_TRACE3(TNC, "node name=%s pcluster=%d %s found in cache\n",
871 name, parent_cluster, ((node != NULL) ? "" : "not"));
876 //--------------------------------------------------------------------------
877 // fatfs_get_live_node_count()
878 // Gets the number of live nodes.
881 fatfs_get_live_node_count(fatfs_disk_t *disk)
883 return node_list_get_size(&disk->live_nlist);
886 //--------------------------------------------------------------------------
887 // fatfs_get_dead_node_count()
888 // Gets the number of dead nodes.
891 fatfs_get_dead_node_count(fatfs_disk_t *disk)
893 return node_list_get_size(&disk->dead_nlist);
896 // -------------------------------------------------------------------------
897 // EOF fatfs_ncache.c