source: mainline/uspace/srv/fs/locfs/locfs_ops.c@ 83781a22

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 83781a22 was 3e6a98c5, checked in by Jiri Svoboda <jiri@…>, 13 years ago

Standards-compliant boolean type.

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