source: mainline/uspace/srv/fs/locfs/locfs_ops.c@ 9ce2202b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9ce2202b was 086290d, checked in by Jakub Jermar <jakub@…>, 14 years ago

locfs should not propagate EHANGUP to VFS.

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