source: mainline/uspace/srv/fs/locfs/locfs_ops.c@ 0ca7286

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0ca7286 was 0ca7286, checked in by Adam Hraska <adam.hraska+hos@…>, 13 years ago

Added resizing to user space (single-threaded) hash_table. Resizes in a way to mitigate effects of bad hash functions. Change of interface affected many files.

  • Property mode set to 100644
File size: 17.6 KB
Line 
1/*
2 * Copyright (c) 2009 Martin Decky
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 fs
30 * @{
31 */
32
33/**
34 * @file locfs_ops.c
35 * @brief Implementation of VFS operations for the locfs file system server.
36 */
37
38#include <macros.h>
39#include <bool.h>
40#include <errno.h>
41#include <malloc.h>
42#include <str.h>
43#include <libfs.h>
44#include <fibril_synch.h>
45#include <adt/hash_table.h>
46#include <ipc/loc.h>
47#include <sys/stat.h>
48#include <libfs.h>
49#include <assert.h>
50#include "locfs.h"
51#include "locfs_ops.h"
52
53typedef struct {
54 loc_object_type_t type;
55 service_id_t service_id;
56} locfs_node_t;
57
58/** Opened services structure */
59typedef struct {
60 service_id_t service_id;
61 async_sess_t *sess; /**< If NULL, the structure is incomplete. */
62 size_t refcount;
63 link_t link;
64 fibril_condvar_t cv; /**< Broadcast when completed. */
65} service_t;
66
67/** Hash table of opened services */
68static hash_table_t services;
69
70/** Hash table mutex */
71static FIBRIL_MUTEX_INITIALIZE(services_mutex);
72
73#define SERVICES_KEYS 1
74#define SERVICES_KEY_HANDLE 0
75
76/* Implementation of hash table interface for the nodes hash table. */
77
78static size_t services_key_hash(unsigned long key[])
79{
80 return key[SERVICES_KEY_HANDLE];
81}
82
83static size_t services_hash(const link_t *item)
84{
85 service_t *dev = hash_table_get_instance(item, service_t, link);
86 unsigned long key[] = {
87 [SERVICES_KEY_HANDLE] = dev->service_id
88 };
89
90 return services_key_hash(key);
91}
92
93static bool services_match(unsigned long key[], size_t keys, const link_t *item)
94{
95 assert(keys == 1);
96 service_t *dev = hash_table_get_instance(item, service_t, link);
97 return (dev->service_id == (service_id_t) key[SERVICES_KEY_HANDLE]);
98}
99
100static void services_remove_callback(link_t *item)
101{
102 free(hash_table_get_instance(item, service_t, link));
103}
104
105static hash_table_ops_t services_ops = {
106 .hash = services_hash,
107 .key_hash = services_key_hash,
108 .match = services_match,
109 .equal = 0,
110 .remove_callback = services_remove_callback
111};
112
113static int locfs_node_get_internal(fs_node_t **rfn, loc_object_type_t type,
114 service_id_t service_id)
115{
116 locfs_node_t *node = (locfs_node_t *) malloc(sizeof(locfs_node_t));
117 if (node == NULL) {
118 *rfn = NULL;
119 return ENOMEM;
120 }
121
122 *rfn = (fs_node_t *) malloc(sizeof(fs_node_t));
123 if (*rfn == NULL) {
124 free(node);
125 *rfn = NULL;
126 return ENOMEM;
127 }
128
129 fs_node_initialize(*rfn);
130 node->type = type;
131 node->service_id = service_id;
132
133 (*rfn)->data = node;
134 return EOK;
135}
136
137static int locfs_root_get(fs_node_t **rfn, service_id_t service_id)
138{
139 return locfs_node_get_internal(rfn, LOC_OBJECT_NONE, 0);
140}
141
142static int locfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
143{
144 locfs_node_t *node = (locfs_node_t *) pfn->data;
145 int ret;
146
147 if (node->service_id == 0) {
148 /* Root directory */
149
150 loc_sdesc_t *nspaces;
151 size_t count = loc_get_namespaces(&nspaces);
152
153 if (count > 0) {
154 size_t pos;
155 for (pos = 0; pos < count; pos++) {
156 /* Ignore root namespace */
157 if (str_cmp(nspaces[pos].name, "") == 0)
158 continue;
159
160 if (str_cmp(nspaces[pos].name, component) == 0) {
161 ret = locfs_node_get_internal(rfn, LOC_OBJECT_NAMESPACE, nspaces[pos].id);
162 free(nspaces);
163 return ret;
164 }
165 }
166
167 free(nspaces);
168 }
169
170 /* Search root namespace */
171 service_id_t namespace;
172 loc_sdesc_t *svcs;
173 if (loc_namespace_get_id("", &namespace, 0) == EOK) {
174 count = loc_get_services(namespace, &svcs);
175
176 if (count > 0) {
177 size_t pos;
178 for (pos = 0; pos < count; pos++) {
179 if (str_cmp(svcs[pos].name, component) == 0) {
180 ret = locfs_node_get_internal(rfn, LOC_OBJECT_SERVICE, svcs[pos].id);
181 free(svcs);
182 return ret;
183 }
184 }
185
186 free(svcs);
187 }
188 }
189
190 *rfn = NULL;
191 return EOK;
192 }
193
194 if (node->type == LOC_OBJECT_NAMESPACE) {
195 /* Namespace directory */
196
197 loc_sdesc_t *svcs;
198 size_t count = loc_get_services(node->service_id, &svcs);
199 if (count > 0) {
200 size_t pos;
201 for (pos = 0; pos < count; pos++) {
202 if (str_cmp(svcs[pos].name, component) == 0) {
203 ret = locfs_node_get_internal(rfn, LOC_OBJECT_SERVICE, svcs[pos].id);
204 free(svcs);
205 return ret;
206 }
207 }
208
209 free(svcs);
210 }
211
212 *rfn = NULL;
213 return EOK;
214 }
215
216 *rfn = NULL;
217 return EOK;
218}
219
220static int locfs_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
221{
222 return locfs_node_get_internal(rfn, loc_id_probe(index), index);
223}
224
225static int locfs_node_open(fs_node_t *fn)
226{
227 locfs_node_t *node = (locfs_node_t *) fn->data;
228
229 if (node->service_id == 0) {
230 /* Root directory */
231 return EOK;
232 }
233
234 loc_object_type_t type = loc_id_probe(node->service_id);
235
236 if (type == LOC_OBJECT_NAMESPACE) {
237 /* Namespace directory */
238 return EOK;
239 }
240
241 if (type == LOC_OBJECT_SERVICE) {
242 /* Device node */
243
244 unsigned long key[] = {
245 [SERVICES_KEY_HANDLE] = (unsigned long) node->service_id
246 };
247 link_t *lnk;
248
249 fibril_mutex_lock(&services_mutex);
250restart:
251 lnk = hash_table_find(&services, key);
252 if (lnk == NULL) {
253 service_t *dev = (service_t *) malloc(sizeof(service_t));
254 if (dev == NULL) {
255 fibril_mutex_unlock(&services_mutex);
256 return ENOMEM;
257 }
258
259 dev->service_id = node->service_id;
260
261 /* Mark as incomplete */
262 dev->sess = NULL;
263 dev->refcount = 1;
264 fibril_condvar_initialize(&dev->cv);
265
266 /*
267 * Insert the incomplete device structure so that other
268 * fibrils will not race with us when we drop the mutex
269 * below.
270 */
271 hash_table_insert(&services, &dev->link);
272
273 /*
274 * Drop the mutex to allow recursive locfs requests.
275 */
276 fibril_mutex_unlock(&services_mutex);
277
278 async_sess_t *sess = loc_service_connect(
279 EXCHANGE_SERIALIZE, node->service_id, 0);
280
281 fibril_mutex_lock(&services_mutex);
282
283 /*
284 * Notify possible waiters about this device structure
285 * being completed (or destroyed).
286 */
287 fibril_condvar_broadcast(&dev->cv);
288
289 if (!sess) {
290 /*
291 * Connecting failed, need to remove the
292 * entry and free the device structure.
293 */
294 hash_table_remove(&services, key, SERVICES_KEYS);
295 fibril_mutex_unlock(&services_mutex);
296
297 return ENOENT;
298 }
299
300 /* Set the correct session. */
301 dev->sess = sess;
302 } else {
303 service_t *dev = hash_table_get_instance(lnk, service_t, link);
304
305 if (!dev->sess) {
306 /*
307 * Wait until the device structure is completed
308 * and start from the beginning as the device
309 * structure might have entirely disappeared
310 * while we were not holding the mutex in
311 * fibril_condvar_wait().
312 */
313 fibril_condvar_wait(&dev->cv, &services_mutex);
314 goto restart;
315 }
316
317 dev->refcount++;
318 }
319
320 fibril_mutex_unlock(&services_mutex);
321
322 return EOK;
323 }
324
325 return ENOENT;
326}
327
328static int locfs_node_put(fs_node_t *fn)
329{
330 free(fn->data);
331 free(fn);
332 return EOK;
333}
334
335static int locfs_create_node(fs_node_t **rfn, service_id_t service_id, int lflag)
336{
337 assert((lflag & L_FILE) ^ (lflag & L_DIRECTORY));
338
339 *rfn = NULL;
340 return ENOTSUP;
341}
342
343static int locfs_destroy_node(fs_node_t *fn)
344{
345 return ENOTSUP;
346}
347
348static int locfs_link_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
349{
350 return ENOTSUP;
351}
352
353static int locfs_unlink_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
354{
355 return ENOTSUP;
356}
357
358static int locfs_has_children(bool *has_children, fs_node_t *fn)
359{
360 locfs_node_t *node = (locfs_node_t *) fn->data;
361
362 if (node->service_id == 0) {
363 size_t count = loc_count_namespaces();
364 if (count > 0) {
365 *has_children = true;
366 return EOK;
367 }
368
369 /* Root namespace */
370 service_id_t namespace;
371 if (loc_namespace_get_id("", &namespace, 0) == EOK) {
372 count = loc_count_services(namespace);
373 if (count > 0) {
374 *has_children = true;
375 return EOK;
376 }
377 }
378
379 *has_children = false;
380 return EOK;
381 }
382
383 if (node->type == LOC_OBJECT_NAMESPACE) {
384 size_t count = loc_count_services(node->service_id);
385 if (count > 0) {
386 *has_children = true;
387 return EOK;
388 }
389
390 *has_children = false;
391 return EOK;
392 }
393
394 *has_children = false;
395 return EOK;
396}
397
398static fs_index_t locfs_index_get(fs_node_t *fn)
399{
400 locfs_node_t *node = (locfs_node_t *) fn->data;
401 return node->service_id;
402}
403
404static aoff64_t locfs_size_get(fs_node_t *fn)
405{
406 return 0;
407}
408
409static unsigned int locfs_lnkcnt_get(fs_node_t *fn)
410{
411 locfs_node_t *node = (locfs_node_t *) fn->data;
412
413 if (node->service_id == 0)
414 return 0;
415
416 return 1;
417}
418
419static bool locfs_is_directory(fs_node_t *fn)
420{
421 locfs_node_t *node = (locfs_node_t *) fn->data;
422
423 return ((node->type == LOC_OBJECT_NONE) || (node->type == LOC_OBJECT_NAMESPACE));
424}
425
426static bool locfs_is_file(fs_node_t *fn)
427{
428 locfs_node_t *node = (locfs_node_t *) fn->data;
429
430 return (node->type == LOC_OBJECT_SERVICE);
431}
432
433static service_id_t locfs_service_get(fs_node_t *fn)
434{
435 locfs_node_t *node = (locfs_node_t *) fn->data;
436
437 if (node->type == LOC_OBJECT_SERVICE)
438 return node->service_id;
439
440 return 0;
441}
442
443/** libfs operations */
444libfs_ops_t locfs_libfs_ops = {
445 .root_get = locfs_root_get,
446 .match = locfs_match,
447 .node_get = locfs_node_get,
448 .node_open = locfs_node_open,
449 .node_put = locfs_node_put,
450 .create = locfs_create_node,
451 .destroy = locfs_destroy_node,
452 .link = locfs_link_node,
453 .unlink = locfs_unlink_node,
454 .has_children = locfs_has_children,
455 .index_get = locfs_index_get,
456 .size_get = locfs_size_get,
457 .lnkcnt_get = locfs_lnkcnt_get,
458 .is_directory = locfs_is_directory,
459 .is_file = locfs_is_file,
460 .service_get = locfs_service_get
461};
462
463bool locfs_init(void)
464{
465 if (!hash_table_create(&services, 0, SERVICES_KEYS, &services_ops))
466 return false;
467
468 return true;
469}
470
471static int locfs_mounted(service_id_t service_id, const char *opts,
472 fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
473{
474 *index = 0;
475 *size = 0;
476 *lnkcnt = 0;
477 return EOK;
478}
479
480static int locfs_unmounted(service_id_t service_id)
481{
482 return ENOTSUP;
483}
484
485static int
486locfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
487 size_t *rbytes)
488{
489 if (index == 0) {
490 ipc_callid_t callid;
491 size_t size;
492 if (!async_data_read_receive(&callid, &size)) {
493 async_answer_0(callid, EINVAL);
494 return EINVAL;
495 }
496
497 loc_sdesc_t *desc;
498 size_t count = loc_get_namespaces(&desc);
499
500 /* Get rid of root namespace */
501 size_t i;
502 for (i = 0; i < count; i++) {
503 if (str_cmp(desc[i].name, "") == 0) {
504 if (pos >= i)
505 pos++;
506
507 break;
508 }
509 }
510
511 if (pos < count) {
512 async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
513 free(desc);
514 *rbytes = 1;
515 return EOK;
516 }
517
518 free(desc);
519 pos -= count;
520
521 /* Search root namespace */
522 service_id_t namespace;
523 if (loc_namespace_get_id("", &namespace, 0) == EOK) {
524 count = loc_get_services(namespace, &desc);
525
526 if (pos < count) {
527 async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
528 free(desc);
529 *rbytes = 1;
530 return EOK;
531 }
532
533 free(desc);
534 }
535
536 async_answer_0(callid, ENOENT);
537 return ENOENT;
538 }
539
540 loc_object_type_t type = loc_id_probe(index);
541
542 if (type == LOC_OBJECT_NAMESPACE) {
543 /* Namespace directory */
544 ipc_callid_t callid;
545 size_t size;
546 if (!async_data_read_receive(&callid, &size)) {
547 async_answer_0(callid, EINVAL);
548 return EINVAL;
549 }
550
551 loc_sdesc_t *desc;
552 size_t count = loc_get_services(index, &desc);
553
554 if (pos < count) {
555 async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
556 free(desc);
557 *rbytes = 1;
558 return EOK;
559 }
560
561 free(desc);
562 async_answer_0(callid, ENOENT);
563 return ENOENT;
564 }
565
566 if (type == LOC_OBJECT_SERVICE) {
567 /* Device node */
568
569 unsigned long key[] = {
570 [SERVICES_KEY_HANDLE] = (unsigned long) index
571 };
572
573 fibril_mutex_lock(&services_mutex);
574 link_t *lnk = hash_table_find(&services, key);
575 if (lnk == NULL) {
576 fibril_mutex_unlock(&services_mutex);
577 return ENOENT;
578 }
579
580 service_t *dev = hash_table_get_instance(lnk, service_t, link);
581 assert(dev->sess);
582
583 ipc_callid_t callid;
584 if (!async_data_read_receive(&callid, NULL)) {
585 fibril_mutex_unlock(&services_mutex);
586 async_answer_0(callid, EINVAL);
587 return EINVAL;
588 }
589
590 /* Make a request at the driver */
591 async_exch_t *exch = async_exchange_begin(dev->sess);
592
593 ipc_call_t answer;
594 aid_t msg = async_send_4(exch, VFS_OUT_READ, service_id,
595 index, LOWER32(pos), UPPER32(pos), &answer);
596
597 /* Forward the IPC_M_DATA_READ request to the driver */
598 async_forward_fast(callid, exch, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
599
600 async_exchange_end(exch);
601
602 fibril_mutex_unlock(&services_mutex);
603
604 /* Wait for reply from the driver. */
605 sysarg_t rc;
606 async_wait_for(msg, &rc);
607
608 /* Do not propagate EHANGUP back to VFS. */
609 if ((int) rc == EHANGUP)
610 rc = ENOTSUP;
611
612 *rbytes = IPC_GET_ARG1(answer);
613 return rc;
614 }
615
616 return ENOENT;
617}
618
619static int
620locfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
621 size_t *wbytes, aoff64_t *nsize)
622{
623 if (index == 0)
624 return ENOTSUP;
625
626 loc_object_type_t type = loc_id_probe(index);
627
628 if (type == LOC_OBJECT_NAMESPACE) {
629 /* Namespace directory */
630 return ENOTSUP;
631 }
632
633 if (type == LOC_OBJECT_SERVICE) {
634 /* Device node */
635 unsigned long key[] = {
636 [SERVICES_KEY_HANDLE] = (unsigned long) index
637 };
638
639 fibril_mutex_lock(&services_mutex);
640 link_t *lnk = hash_table_find(&services, key);
641 if (lnk == NULL) {
642 fibril_mutex_unlock(&services_mutex);
643 return ENOENT;
644 }
645
646 service_t *dev = hash_table_get_instance(lnk, service_t, link);
647 assert(dev->sess);
648
649 ipc_callid_t callid;
650 if (!async_data_write_receive(&callid, NULL)) {
651 fibril_mutex_unlock(&services_mutex);
652 async_answer_0(callid, EINVAL);
653 return EINVAL;
654 }
655
656 /* Make a request at the driver */
657 async_exch_t *exch = async_exchange_begin(dev->sess);
658
659 ipc_call_t answer;
660 aid_t msg = async_send_4(exch, VFS_OUT_WRITE, service_id,
661 index, LOWER32(pos), UPPER32(pos), &answer);
662
663 /* Forward the IPC_M_DATA_WRITE request to the driver */
664 async_forward_fast(callid, exch, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
665
666 async_exchange_end(exch);
667
668 fibril_mutex_unlock(&services_mutex);
669
670 /* Wait for reply from the driver. */
671 sysarg_t rc;
672 async_wait_for(msg, &rc);
673
674 /* Do not propagate EHANGUP back to VFS. */
675 if ((int) rc == EHANGUP)
676 rc = ENOTSUP;
677
678 *wbytes = IPC_GET_ARG1(answer);
679 *nsize = 0;
680 return rc;
681 }
682
683 return ENOENT;
684}
685
686static int
687locfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
688{
689 return ENOTSUP;
690}
691
692static int locfs_close(service_id_t service_id, fs_index_t index)
693{
694 if (index == 0)
695 return EOK;
696
697 loc_object_type_t type = loc_id_probe(index);
698
699 if (type == LOC_OBJECT_NAMESPACE) {
700 /* Namespace directory */
701 return EOK;
702 }
703
704 if (type == LOC_OBJECT_SERVICE) {
705 unsigned long key[] = {
706 [SERVICES_KEY_HANDLE] = (unsigned long) index
707 };
708
709 fibril_mutex_lock(&services_mutex);
710 link_t *lnk = hash_table_find(&services, key);
711 if (lnk == NULL) {
712 fibril_mutex_unlock(&services_mutex);
713 return ENOENT;
714 }
715
716 service_t *dev = hash_table_get_instance(lnk, service_t, link);
717 assert(dev->sess);
718 dev->refcount--;
719
720 if (dev->refcount == 0) {
721 async_hangup(dev->sess);
722 hash_table_remove(&services, key, SERVICES_KEYS);
723 }
724
725 fibril_mutex_unlock(&services_mutex);
726
727 return EOK;
728 }
729
730 return ENOENT;
731}
732
733static int locfs_sync(service_id_t service_id, fs_index_t index)
734{
735 if (index == 0)
736 return EOK;
737
738 loc_object_type_t type = loc_id_probe(index);
739
740 if (type == LOC_OBJECT_NAMESPACE) {
741 /* Namespace directory */
742 return EOK;
743 }
744
745 if (type == LOC_OBJECT_SERVICE) {
746 unsigned long key[] = {
747 [SERVICES_KEY_HANDLE] = (unsigned long) index
748 };
749
750 fibril_mutex_lock(&services_mutex);
751 link_t *lnk = hash_table_find(&services, key);
752 if (lnk == NULL) {
753 fibril_mutex_unlock(&services_mutex);
754 return ENOENT;
755 }
756
757 service_t *dev = hash_table_get_instance(lnk, service_t, link);
758 assert(dev->sess);
759
760 /* Make a request at the driver */
761 async_exch_t *exch = async_exchange_begin(dev->sess);
762
763 ipc_call_t answer;
764 aid_t msg = async_send_2(exch, VFS_OUT_SYNC, service_id,
765 index, &answer);
766
767 async_exchange_end(exch);
768
769 fibril_mutex_unlock(&services_mutex);
770
771 /* Wait for reply from the driver */
772 sysarg_t rc;
773 async_wait_for(msg, &rc);
774
775 return rc;
776 }
777
778 return ENOENT;
779}
780
781static int locfs_destroy(service_id_t service_id, fs_index_t index)
782{
783 return ENOTSUP;
784}
785
786vfs_out_ops_t locfs_ops = {
787 .mounted = locfs_mounted,
788 .unmounted = locfs_unmounted,
789 .read = locfs_read,
790 .write = locfs_write,
791 .truncate = locfs_truncate,
792 .close = locfs_close,
793 .destroy = locfs_destroy,
794 .sync = locfs_sync,
795};
796
797/**
798 * @}
799 */
Note: See TracBrowser for help on using the repository browser.