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

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

Refactored any users of hash_table to use opaque void* keys instead of the cumbersome unsigned long[] keys. Switched from the ad hoc computations of hashes of multiple values to hash_combine().

  • Property mode set to 100644
File size: 16.9 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 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 = 0,
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 ht_link_t *lnk = hash_table_find(&services, &index);
559 if (lnk == NULL) {
560 fibril_mutex_unlock(&services_mutex);
561 return ENOENT;
562 }
563
564 service_t *dev = hash_table_get_inst(lnk, service_t, link);
565 assert(dev->sess);
566
567 ipc_callid_t callid;
568 if (!async_data_read_receive(&callid, NULL)) {
569 fibril_mutex_unlock(&services_mutex);
570 async_answer_0(callid, EINVAL);
571 return EINVAL;
572 }
573
574 /* Make a request at the driver */
575 async_exch_t *exch = async_exchange_begin(dev->sess);
576
577 ipc_call_t answer;
578 aid_t msg = async_send_4(exch, VFS_OUT_READ, service_id,
579 index, LOWER32(pos), UPPER32(pos), &answer);
580
581 /* Forward the IPC_M_DATA_READ request to the driver */
582 async_forward_fast(callid, exch, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
583
584 async_exchange_end(exch);
585
586 fibril_mutex_unlock(&services_mutex);
587
588 /* Wait for reply from the driver. */
589 sysarg_t rc;
590 async_wait_for(msg, &rc);
591
592 /* Do not propagate EHANGUP back to VFS. */
593 if ((int) rc == EHANGUP)
594 rc = ENOTSUP;
595
596 *rbytes = IPC_GET_ARG1(answer);
597 return rc;
598 }
599
600 return ENOENT;
601}
602
603static int
604locfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
605 size_t *wbytes, aoff64_t *nsize)
606{
607 if (index == 0)
608 return ENOTSUP;
609
610 loc_object_type_t type = loc_id_probe(index);
611
612 if (type == LOC_OBJECT_NAMESPACE) {
613 /* Namespace directory */
614 return ENOTSUP;
615 }
616
617 if (type == LOC_OBJECT_SERVICE) {
618 /* Device node */
619
620 fibril_mutex_lock(&services_mutex);
621 ht_link_t *lnk = hash_table_find(&services, &index);
622 if (lnk == NULL) {
623 fibril_mutex_unlock(&services_mutex);
624 return ENOENT;
625 }
626
627 service_t *dev = hash_table_get_inst(lnk, service_t, link);
628 assert(dev->sess);
629
630 ipc_callid_t callid;
631 if (!async_data_write_receive(&callid, NULL)) {
632 fibril_mutex_unlock(&services_mutex);
633 async_answer_0(callid, EINVAL);
634 return EINVAL;
635 }
636
637 /* Make a request at the driver */
638 async_exch_t *exch = async_exchange_begin(dev->sess);
639
640 ipc_call_t answer;
641 aid_t msg = async_send_4(exch, VFS_OUT_WRITE, service_id,
642 index, LOWER32(pos), UPPER32(pos), &answer);
643
644 /* Forward the IPC_M_DATA_WRITE request to the driver */
645 async_forward_fast(callid, exch, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
646
647 async_exchange_end(exch);
648
649 fibril_mutex_unlock(&services_mutex);
650
651 /* Wait for reply from the driver. */
652 sysarg_t rc;
653 async_wait_for(msg, &rc);
654
655 /* Do not propagate EHANGUP back to VFS. */
656 if ((int) rc == EHANGUP)
657 rc = ENOTSUP;
658
659 *wbytes = IPC_GET_ARG1(answer);
660 *nsize = 0;
661 return rc;
662 }
663
664 return ENOENT;
665}
666
667static int
668locfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
669{
670 return ENOTSUP;
671}
672
673static int locfs_close(service_id_t service_id, fs_index_t index)
674{
675 if (index == 0)
676 return EOK;
677
678 loc_object_type_t type = loc_id_probe(index);
679
680 if (type == LOC_OBJECT_NAMESPACE) {
681 /* Namespace directory */
682 return EOK;
683 }
684
685 if (type == LOC_OBJECT_SERVICE) {
686
687 fibril_mutex_lock(&services_mutex);
688 ht_link_t *lnk = hash_table_find(&services, &index);
689 if (lnk == NULL) {
690 fibril_mutex_unlock(&services_mutex);
691 return ENOENT;
692 }
693
694 service_t *dev = hash_table_get_inst(lnk, service_t, link);
695 assert(dev->sess);
696 dev->refcount--;
697
698 if (dev->refcount == 0) {
699 async_hangup(dev->sess);
700 hash_table_remove(&services, &index);
701 }
702
703 fibril_mutex_unlock(&services_mutex);
704
705 return EOK;
706 }
707
708 return ENOENT;
709}
710
711static int locfs_sync(service_id_t service_id, fs_index_t index)
712{
713 if (index == 0)
714 return EOK;
715
716 loc_object_type_t type = loc_id_probe(index);
717
718 if (type == LOC_OBJECT_NAMESPACE) {
719 /* Namespace directory */
720 return EOK;
721 }
722
723 if (type == LOC_OBJECT_SERVICE) {
724
725 fibril_mutex_lock(&services_mutex);
726 ht_link_t *lnk = hash_table_find(&services, &index);
727 if (lnk == NULL) {
728 fibril_mutex_unlock(&services_mutex);
729 return ENOENT;
730 }
731
732 service_t *dev = hash_table_get_inst(lnk, service_t, link);
733 assert(dev->sess);
734
735 /* Make a request at the driver */
736 async_exch_t *exch = async_exchange_begin(dev->sess);
737
738 ipc_call_t answer;
739 aid_t msg = async_send_2(exch, VFS_OUT_SYNC, service_id,
740 index, &answer);
741
742 async_exchange_end(exch);
743
744 fibril_mutex_unlock(&services_mutex);
745
746 /* Wait for reply from the driver */
747 sysarg_t rc;
748 async_wait_for(msg, &rc);
749
750 return rc;
751 }
752
753 return ENOENT;
754}
755
756static int locfs_destroy(service_id_t service_id, fs_index_t index)
757{
758 return ENOTSUP;
759}
760
761vfs_out_ops_t locfs_ops = {
762 .mounted = locfs_mounted,
763 .unmounted = locfs_unmounted,
764 .read = locfs_read,
765 .write = locfs_write,
766 .truncate = locfs_truncate,
767 .close = locfs_close,
768 .destroy = locfs_destroy,
769 .sync = locfs_sync,
770};
771
772/**
773 * @}
774 */
Note: See TracBrowser for help on using the repository browser.