Rev 2876 | Rev 2884 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 2876 | Rev 2881 | ||
---|---|---|---|
Line 34... | Line 34... | ||
34 | * @file fat_idx.c |
34 | * @file fat_idx.c |
35 | * @brief Layer for translating FAT entities to VFS node indices. |
35 | * @brief Layer for translating FAT entities to VFS node indices. |
36 | */ |
36 | */ |
37 | 37 | ||
38 | #include "fat.h" |
38 | #include "fat.h" |
- | 39 | #include "../../vfs/vfs.h" |
|
39 | #include <errno.h> |
40 | #include <errno.h> |
40 | #include <string.h> |
41 | #include <string.h> |
41 | #include <libadt/hash_table.h> |
42 | #include <libadt/hash_table.h> |
42 | #include <libadt/list.h> |
43 | #include <libadt/list.h> |
43 | #include <assert.h> |
44 | #include <assert.h> |
44 | #include <futex.h> |
45 | #include <futex.h> |
45 | 46 | ||
- | 47 | /** Each instance of this type describes one interval of freed VFS indices. */ |
|
- | 48 | typedef struct { |
|
- | 49 | link_t link; |
|
- | 50 | fs_index_t first; |
|
- | 51 | fs_index_t last; |
|
- | 52 | } freed_t; |
|
- | 53 | ||
- | 54 | /** |
|
- | 55 | * Each instance of this type describes state of all VFS indices that |
|
- | 56 | * are currently unused. |
|
- | 57 | */ |
|
- | 58 | typedef struct { |
|
- | 59 | link_t link; |
|
- | 60 | dev_handle_t dev_handle; |
|
- | 61 | ||
- | 62 | /** Next unassigned index. */ |
|
- | 63 | fs_index_t next; |
|
- | 64 | /** Number of remaining unassigned indices. */ |
|
- | 65 | uint64_t remaining; |
|
- | 66 | ||
- | 67 | /** Sorted list of intervals of freed indices. */ |
|
- | 68 | link_t freed_head; |
|
- | 69 | } unused_t; |
|
- | 70 | ||
- | 71 | static LIST_INITIALIZE(unused_head); |
|
- | 72 | ||
- | 73 | /** Allocate a VFS index which is not currently in use. */ |
|
- | 74 | static bool fat_idx_alloc(dev_handle_t dev_handle, fs_index_t *index) |
|
- | 75 | { |
|
- | 76 | link_t *l; |
|
- | 77 | unused_t *u; |
|
- | 78 | ||
- | 79 | assert(index); |
|
- | 80 | for (l = unused_head.next; l != &unused_head; l = l->next) { |
|
- | 81 | u = list_get_instance(l, unused_t, link); |
|
- | 82 | if (u->dev_handle == dev_handle) |
|
- | 83 | goto hit; |
|
- | 84 | } |
|
- | 85 | ||
- | 86 | /* dev_handle not found */ |
|
- | 87 | return false; |
|
- | 88 | ||
- | 89 | hit: |
|
- | 90 | if (list_empty(&u->freed_head)) { |
|
- | 91 | if (u->remaining) { |
|
- | 92 | /* |
|
- | 93 | * There are no freed indices, allocate one directly |
|
- | 94 | * from the counter. |
|
- | 95 | */ |
|
- | 96 | *index = u->next++; |
|
- | 97 | --u->remaining; |
|
- | 98 | return true; |
|
- | 99 | } |
|
- | 100 | } else { |
|
- | 101 | /* There are some freed indices which we can reuse. */ |
|
- | 102 | freed_t *f = list_get_instance(u->freed_head.next, freed_t, |
|
- | 103 | link); |
|
- | 104 | *index = f->first; |
|
- | 105 | if (f->first++ == f->last) { |
|
- | 106 | /* Destroy the interval. */ |
|
- | 107 | list_remove(&f->link); |
|
- | 108 | free(f); |
|
- | 109 | } |
|
- | 110 | return true; |
|
- | 111 | } |
|
- | 112 | /* |
|
- | 113 | * We ran out of indices, which is extremely unlikely with FAT16, but |
|
- | 114 | * theoretically still possible (e.g. too many open unlinked nodes or |
|
- | 115 | * too many zero-sized nodes). |
|
- | 116 | */ |
|
- | 117 | return false; |
|
- | 118 | } |
|
- | 119 | ||
- | 120 | /** If possible, merge two intervals of freed indices. */ |
|
- | 121 | static void try_merge_intervals(link_t *l, link_t *r, link_t *cur) |
|
- | 122 | { |
|
- | 123 | freed_t *fl = list_get_instance(l, freed_t, link); |
|
- | 124 | freed_t *fr = list_get_instance(r, freed_t, link); |
|
- | 125 | ||
- | 126 | if (fl->last + 1 == fr->first) { |
|
- | 127 | if (cur == l) { |
|
- | 128 | fl->last = fr->last; |
|
- | 129 | list_remove(r); |
|
- | 130 | free(r); |
|
- | 131 | } else { |
|
- | 132 | fr->first = fl->first; |
|
- | 133 | list_remove(l); |
|
- | 134 | free(l); |
|
- | 135 | } |
|
- | 136 | } |
|
- | 137 | } |
|
- | 138 | ||
- | 139 | /** Free a VFS index, which is no longer in use. */ |
|
- | 140 | static void fat_idx_free(dev_handle_t dev_handle, fs_index_t index) |
|
- | 141 | { |
|
- | 142 | link_t *l; |
|
- | 143 | unused_t *u; |
|
- | 144 | ||
- | 145 | for (l = unused_head.next; l != &unused_head; l = l->next) { |
|
- | 146 | u = list_get_instance(l, unused_t, link); |
|
- | 147 | if (u->dev_handle == dev_handle) |
|
- | 148 | goto hit; |
|
- | 149 | } |
|
- | 150 | ||
- | 151 | /* should not happen */ |
|
- | 152 | assert(0); |
|
- | 153 | ||
- | 154 | hit: |
|
- | 155 | if (u->next == index + 1) { |
|
- | 156 | /* The index can be returned directly to the counter. */ |
|
- | 157 | u->next--; |
|
- | 158 | u->remaining++; |
|
- | 159 | return; |
|
- | 160 | } else { |
|
- | 161 | /* |
|
- | 162 | * The index must be returned either to an existing freed |
|
- | 163 | * interval or a new interval must be created. |
|
- | 164 | */ |
|
- | 165 | link_t *lnk; |
|
- | 166 | freed_t *n; |
|
- | 167 | for (lnk = u->freed_head.next; lnk != &u->freed_head; |
|
- | 168 | lnk = lnk->next) { |
|
- | 169 | freed_t *f = list_get_instance(lnk, freed_t, link); |
|
- | 170 | if (f->first == index + 1) { |
|
- | 171 | f->first--; |
|
- | 172 | if (lnk->prev != &u->freed_head) |
|
- | 173 | try_merge_intervals(lnk->prev, lnk, |
|
- | 174 | lnk); |
|
- | 175 | return; |
|
- | 176 | } |
|
- | 177 | if (f->last == index - 1) { |
|
- | 178 | f->last++; |
|
- | 179 | if (lnk->next != &u->freed_head) |
|
- | 180 | try_merge_intervals(lnk, lnk->next, |
|
- | 181 | lnk); |
|
- | 182 | return; |
|
- | 183 | } |
|
- | 184 | if (index > f->first) { |
|
- | 185 | n = malloc(sizeof(freed_t)); |
|
- | 186 | /* TODO: sleep until allocation succeeds */ |
|
- | 187 | assert(n); |
|
- | 188 | link_initialize(&n->link); |
|
- | 189 | n->first = index; |
|
- | 190 | n->last = index; |
|
- | 191 | list_insert_before(&n->link, lnk); |
|
- | 192 | return; |
|
- | 193 | } |
|
- | 194 | ||
- | 195 | } |
|
- | 196 | /* The index will form the last interval. */ |
|
- | 197 | n = malloc(sizeof(freed_t)); |
|
- | 198 | /* TODO: sleep until allocation succeeds */ |
|
- | 199 | assert(n); |
|
- | 200 | link_initialize(&n->link); |
|
- | 201 | n->first = index; |
|
- | 202 | n->last = index; |
|
- | 203 | list_append(&n->link, &u->freed_head); |
|
- | 204 | } |
|
- | 205 | } |
|
- | 206 | ||
46 | fat_idx_t *fat_idx_map(fat_cluster_t pfc, unsigned pdi) |
207 | fat_idx_t *fat_idx_map(dev_handle_t dev_handle, fat_cluster_t pfc, unsigned pdi) |
47 | { |
208 | { |
48 | return NULL; /* TODO */ |
209 | return NULL; /* TODO */ |
49 | } |
210 | } |