source: mainline/uspace/srv/fs/fat/fat_idx.c@ c21d4d6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c21d4d6 was 5e801dc, checked in by GitHub <noreply@…>, 6 years ago

Indicate and enforce constness of hash table key in certain functions (#158)

The assumption here is that modifying key in the hash/equal functions in something completely unexpected, and not something you would ever want to do intentionally, so it makes sense to disallow it entirely to get that extra level of checking.

  • Property mode set to 100644
File size: 13.3 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup fat
30 * @{
31 */
32
33/**
34 * @file fat_idx.c
35 * @brief Layer for translating FAT entities to VFS node indices.
36 */
37
38#include "fat.h"
39#include "../../vfs/vfs.h"
40#include <errno.h>
41#include <str.h>
42#include <adt/hash_table.h>
43#include <adt/hash.h>
44#include <adt/list.h>
45#include <assert.h>
46#include <fibril_synch.h>
47#include <stdlib.h>
48
49/** Each instance of this type describes one interval of freed VFS indices. */
50typedef struct {
51 link_t link;
52 fs_index_t first;
53 fs_index_t last;
54} freed_t;
55
56/**
57 * Each instance of this type describes state of all VFS indices that
58 * are currently unused.
59 */
60typedef struct {
61 link_t link;
62 service_id_t service_id;
63
64 /** Next unassigned index. */
65 fs_index_t next;
66 /** Number of remaining unassigned indices. */
67 uint64_t remaining;
68
69 /** Sorted list of intervals of freed indices. */
70 list_t freed_list;
71} unused_t;
72
73/** Mutex protecting the list of unused structures. */
74static FIBRIL_MUTEX_INITIALIZE(unused_lock);
75
76/** List of unused structures. */
77static LIST_INITIALIZE(unused_list);
78
79static void unused_initialize(unused_t *u, service_id_t service_id)
80{
81 link_initialize(&u->link);
82 u->service_id = service_id;
83 u->next = 0;
84 u->remaining = ((uint64_t)((fs_index_t)-1)) + 1;
85 list_initialize(&u->freed_list);
86}
87
88static unused_t *unused_find(service_id_t service_id, bool lock)
89{
90 if (lock)
91 fibril_mutex_lock(&unused_lock);
92
93 list_foreach(unused_list, link, unused_t, u) {
94 if (u->service_id == service_id)
95 return u;
96 }
97
98 if (lock)
99 fibril_mutex_unlock(&unused_lock);
100 return NULL;
101}
102
103/** Mutex protecting the up_hash and ui_hash. */
104static FIBRIL_MUTEX_INITIALIZE(used_lock);
105
106/**
107 * Global hash table of all used fat_idx_t structures.
108 * The index structures are hashed by the service_id, parent node's first
109 * cluster and index within the parent directory.
110 */
111static hash_table_t up_hash;
112
113typedef struct {
114 service_id_t service_id;
115 fat_cluster_t pfc;
116 unsigned pdi;
117} pos_key_t;
118
119static inline size_t pos_key_hash(const void *key)
120{
121 const pos_key_t *pos = key;
122
123 size_t hash = 0;
124 hash = hash_combine(pos->pfc, pos->pdi);
125 return hash_combine(hash, pos->service_id);
126}
127
128static size_t pos_hash(const ht_link_t *item)
129{
130 fat_idx_t *fidx = hash_table_get_inst(item, fat_idx_t, uph_link);
131
132 pos_key_t pkey = {
133 .service_id = fidx->service_id,
134 .pfc = fidx->pfc,
135 .pdi = fidx->pdi,
136 };
137
138 return pos_key_hash(&pkey);
139}
140
141static bool pos_key_equal(const void *key, const ht_link_t *item)
142{
143 const pos_key_t *pos = key;
144 fat_idx_t *fidx = hash_table_get_inst(item, fat_idx_t, uph_link);
145
146 return pos->service_id == fidx->service_id &&
147 pos->pdi == fidx->pdi &&
148 pos->pfc == fidx->pfc;
149}
150
151static hash_table_ops_t uph_ops = {
152 .hash = pos_hash,
153 .key_hash = pos_key_hash,
154 .key_equal = pos_key_equal,
155 .equal = NULL,
156 .remove_callback = NULL,
157};
158
159/**
160 * Global hash table of all used fat_idx_t structures.
161 * The index structures are hashed by the service_id and index.
162 */
163static hash_table_t ui_hash;
164
165typedef struct {
166 service_id_t service_id;
167 fs_index_t index;
168} idx_key_t;
169
170static size_t idx_key_hash(const void *key_arg)
171{
172 const idx_key_t *key = key_arg;
173 return hash_combine(key->service_id, key->index);
174}
175
176static size_t idx_hash(const ht_link_t *item)
177{
178 fat_idx_t *fidx = hash_table_get_inst(item, fat_idx_t, uih_link);
179 return hash_combine(fidx->service_id, fidx->index);
180}
181
182static bool idx_key_equal(const void *key_arg, const ht_link_t *item)
183{
184 fat_idx_t *fidx = hash_table_get_inst(item, fat_idx_t, uih_link);
185 const idx_key_t *key = key_arg;
186
187 return key->index == fidx->index && key->service_id == fidx->service_id;
188}
189
190static void idx_remove_callback(ht_link_t *item)
191{
192 fat_idx_t *fidx = hash_table_get_inst(item, fat_idx_t, uih_link);
193
194 free(fidx);
195}
196
197static hash_table_ops_t uih_ops = {
198 .hash = idx_hash,
199 .key_hash = idx_key_hash,
200 .key_equal = idx_key_equal,
201 .equal = NULL,
202 .remove_callback = idx_remove_callback,
203};
204
205/** Allocate a VFS index which is not currently in use. */
206static bool fat_index_alloc(service_id_t service_id, fs_index_t *index)
207{
208 unused_t *u;
209
210 assert(index);
211 u = unused_find(service_id, true);
212 if (!u)
213 return false;
214
215 if (list_empty(&u->freed_list)) {
216 if (u->remaining) {
217 /*
218 * There are no freed indices, allocate one directly
219 * from the counter.
220 */
221 *index = u->next++;
222 --u->remaining;
223 fibril_mutex_unlock(&unused_lock);
224 return true;
225 }
226 } else {
227 /* There are some freed indices which we can reuse. */
228 freed_t *f = list_get_instance(list_first(&u->freed_list),
229 freed_t, link);
230 *index = f->first;
231 if (f->first++ == f->last) {
232 /* Destroy the interval. */
233 list_remove(&f->link);
234 free(f);
235 }
236 fibril_mutex_unlock(&unused_lock);
237 return true;
238 }
239 /*
240 * We ran out of indices, which is extremely unlikely with FAT16, but
241 * theoretically still possible (e.g. too many open unlinked nodes or
242 * too many zero-sized nodes).
243 */
244 fibril_mutex_unlock(&unused_lock);
245 return false;
246}
247
248/** If possible, coalesce two intervals of freed indices. */
249static void try_coalesce_intervals(link_t *l, link_t *r, link_t *cur)
250{
251 freed_t *fl = list_get_instance(l, freed_t, link);
252 freed_t *fr = list_get_instance(r, freed_t, link);
253
254 if (fl->last + 1 == fr->first) {
255 if (cur == l) {
256 fl->last = fr->last;
257 list_remove(r);
258 free(r);
259 } else {
260 fr->first = fl->first;
261 list_remove(l);
262 free(l);
263 }
264 }
265}
266
267/** Free a VFS index, which is no longer in use. */
268static void fat_index_free(service_id_t service_id, fs_index_t index)
269{
270 unused_t *u;
271
272 u = unused_find(service_id, true);
273 assert(u);
274
275 if (u->next == index + 1) {
276 /* The index can be returned directly to the counter. */
277 u->next--;
278 u->remaining++;
279 } else {
280 /*
281 * The index must be returned either to an existing freed
282 * interval or a new interval must be created.
283 */
284 link_t *lnk;
285 freed_t *n;
286 for (lnk = u->freed_list.head.next; lnk != &u->freed_list.head;
287 lnk = lnk->next) {
288 freed_t *f = list_get_instance(lnk, freed_t, link);
289 if (f->first == index + 1) {
290 f->first--;
291 if (lnk->prev != &u->freed_list.head)
292 try_coalesce_intervals(lnk->prev, lnk,
293 lnk);
294 fibril_mutex_unlock(&unused_lock);
295 return;
296 }
297 if (f->last == index - 1) {
298 f->last++;
299 if (lnk->next != &u->freed_list.head)
300 try_coalesce_intervals(lnk, lnk->next,
301 lnk);
302 fibril_mutex_unlock(&unused_lock);
303 return;
304 }
305 if (index > f->first) {
306 n = malloc(sizeof(freed_t));
307 /* TODO: sleep until allocation succeeds */
308 assert(n);
309 link_initialize(&n->link);
310 n->first = index;
311 n->last = index;
312 list_insert_before(&n->link, lnk);
313 fibril_mutex_unlock(&unused_lock);
314 return;
315 }
316
317 }
318 /* The index will form the last interval. */
319 n = malloc(sizeof(freed_t));
320 /* TODO: sleep until allocation succeeds */
321 assert(n);
322 link_initialize(&n->link);
323 n->first = index;
324 n->last = index;
325 list_append(&n->link, &u->freed_list);
326 }
327 fibril_mutex_unlock(&unused_lock);
328}
329
330static errno_t fat_idx_create(fat_idx_t **fidxp, service_id_t service_id)
331{
332 fat_idx_t *fidx;
333
334 fidx = (fat_idx_t *) malloc(sizeof(fat_idx_t));
335 if (!fidx)
336 return ENOMEM;
337 if (!fat_index_alloc(service_id, &fidx->index)) {
338 free(fidx);
339 return ENOSPC;
340 }
341
342 fibril_mutex_initialize(&fidx->lock);
343 fidx->service_id = service_id;
344 fidx->pfc = FAT_CLST_RES0; /* no parent yet */
345 fidx->pdi = 0;
346 fidx->nodep = NULL;
347
348 *fidxp = fidx;
349 return EOK;
350}
351
352errno_t fat_idx_get_new(fat_idx_t **fidxp, service_id_t service_id)
353{
354 fat_idx_t *fidx;
355 errno_t rc;
356
357 fibril_mutex_lock(&used_lock);
358 rc = fat_idx_create(&fidx, service_id);
359 if (rc != EOK) {
360 fibril_mutex_unlock(&used_lock);
361 return rc;
362 }
363
364 hash_table_insert(&ui_hash, &fidx->uih_link);
365 fibril_mutex_lock(&fidx->lock);
366 fibril_mutex_unlock(&used_lock);
367
368 *fidxp = fidx;
369 return EOK;
370}
371
372fat_idx_t *
373fat_idx_get_by_pos(service_id_t service_id, fat_cluster_t pfc, unsigned pdi)
374{
375 fat_idx_t *fidx;
376
377 pos_key_t pos_key = {
378 .service_id = service_id,
379 .pfc = pfc,
380 .pdi = pdi,
381 };
382
383 fibril_mutex_lock(&used_lock);
384 ht_link_t *l = hash_table_find(&up_hash, &pos_key);
385 if (l) {
386 fidx = hash_table_get_inst(l, fat_idx_t, uph_link);
387 } else {
388 errno_t rc;
389
390 rc = fat_idx_create(&fidx, service_id);
391 if (rc != EOK) {
392 fibril_mutex_unlock(&used_lock);
393 return NULL;
394 }
395
396 fidx->pfc = pfc;
397 fidx->pdi = pdi;
398
399 hash_table_insert(&up_hash, &fidx->uph_link);
400 hash_table_insert(&ui_hash, &fidx->uih_link);
401 }
402 fibril_mutex_lock(&fidx->lock);
403 fibril_mutex_unlock(&used_lock);
404
405 return fidx;
406}
407
408void fat_idx_hashin(fat_idx_t *idx)
409{
410 fibril_mutex_lock(&used_lock);
411 hash_table_insert(&up_hash, &idx->uph_link);
412 fibril_mutex_unlock(&used_lock);
413}
414
415void fat_idx_hashout(fat_idx_t *idx)
416{
417 fibril_mutex_lock(&used_lock);
418 hash_table_remove_item(&up_hash, &idx->uph_link);
419 fibril_mutex_unlock(&used_lock);
420}
421
422fat_idx_t *
423fat_idx_get_by_index(service_id_t service_id, fs_index_t index)
424{
425 fat_idx_t *fidx = NULL;
426
427 idx_key_t idx_key = {
428 .service_id = service_id,
429 .index = index,
430 };
431
432 fibril_mutex_lock(&used_lock);
433 ht_link_t *l = hash_table_find(&ui_hash, &idx_key);
434 if (l) {
435 fidx = hash_table_get_inst(l, fat_idx_t, uih_link);
436 fibril_mutex_lock(&fidx->lock);
437 }
438 fibril_mutex_unlock(&used_lock);
439
440 return fidx;
441}
442
443/** Destroy the index structure.
444 *
445 * @param idx The index structure to be destroyed.
446 */
447void fat_idx_destroy(fat_idx_t *idx)
448{
449 idx_key_t idx_key = {
450 .service_id = idx->service_id,
451 .index = idx->index,
452 };
453
454 assert(idx->pfc == FAT_CLST_RES0);
455
456 fibril_mutex_lock(&used_lock);
457 /*
458 * Since we can only free unlinked nodes, the index structure is not
459 * present in the position hash (uph). We therefore hash it out from
460 * the index hash only.
461 */
462 hash_table_remove(&ui_hash, &idx_key);
463 fibril_mutex_unlock(&used_lock);
464 /* Release the VFS index. */
465 fat_index_free(idx_key.service_id, idx_key.index);
466 /* The index structure itself is freed in idx_remove_callback(). */
467}
468
469errno_t fat_idx_init(void)
470{
471 if (!hash_table_create(&up_hash, 0, 0, &uph_ops))
472 return ENOMEM;
473 if (!hash_table_create(&ui_hash, 0, 0, &uih_ops)) {
474 hash_table_destroy(&up_hash);
475 return ENOMEM;
476 }
477 return EOK;
478}
479
480void fat_idx_fini(void)
481{
482 /* We assume the hash tables are empty. */
483 assert(hash_table_empty(&up_hash) && hash_table_empty(&ui_hash));
484 hash_table_destroy(&up_hash);
485 hash_table_destroy(&ui_hash);
486}
487
488errno_t fat_idx_init_by_service_id(service_id_t service_id)
489{
490 unused_t *u;
491 errno_t rc = EOK;
492
493 u = (unused_t *) malloc(sizeof(unused_t));
494 if (!u)
495 return ENOMEM;
496 unused_initialize(u, service_id);
497 fibril_mutex_lock(&unused_lock);
498 if (!unused_find(service_id, false)) {
499 list_append(&u->link, &unused_list);
500 } else {
501 free(u);
502 rc = EEXIST;
503 }
504 fibril_mutex_unlock(&unused_lock);
505 return rc;
506}
507
508static bool rm_pos_service_id(ht_link_t *item, void *arg)
509{
510 service_id_t service_id = *(service_id_t *)arg;
511 fat_idx_t *fidx = hash_table_get_inst(item, fat_idx_t, uph_link);
512
513 if (fidx->service_id == service_id) {
514 hash_table_remove_item(&up_hash, item);
515 }
516
517 return true;
518}
519
520static bool rm_idx_service_id(ht_link_t *item, void *arg)
521{
522 service_id_t service_id = *(service_id_t *)arg;
523 fat_idx_t *fidx = hash_table_get_inst(item, fat_idx_t, uih_link);
524
525 if (fidx->service_id == service_id) {
526 hash_table_remove_item(&ui_hash, item);
527 }
528
529 return true;
530}
531
532void fat_idx_fini_by_service_id(service_id_t service_id)
533{
534 /*
535 * Remove this instance's index structure from up_hash and ui_hash.
536 * Process up_hash first and ui_hash second because the index structure
537 * is actually removed in idx_remove_callback().
538 */
539 fibril_mutex_lock(&used_lock);
540 hash_table_apply(&up_hash, rm_pos_service_id, &service_id);
541 hash_table_apply(&ui_hash, rm_idx_service_id, &service_id);
542 fibril_mutex_unlock(&used_lock);
543
544 /*
545 * Free the unused and freed structures for this instance.
546 */
547 unused_t *u = unused_find(service_id, true);
548 assert(u);
549 list_remove(&u->link);
550 fibril_mutex_unlock(&unused_lock);
551
552 while (!list_empty(&u->freed_list)) {
553 freed_t *f;
554 f = list_get_instance(list_first(&u->freed_list), freed_t, link);
555 list_remove(&f->link);
556 free(f);
557 }
558 free(u);
559}
560
561/**
562 * @}
563 */
Note: See TracBrowser for help on using the repository browser.