source: mainline/uspace/srv/fs/locfs/locfs_ops.c@ 802a8c8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 802a8c8 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

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