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

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

File system probing groundwork. Only MFS can do it for now.

  • 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 <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 int 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 int 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 int 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 int 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 int 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 int 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;
236restart:
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 int locfs_node_put(fs_node_t *fn)
315{
316 free(fn->data);
317 free(fn);
318 return EOK;
319}
320
321static int 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 int locfs_destroy_node(fs_node_t *fn)
330{
331 return ENOTSUP;
332}
333
334static int locfs_link_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
335{
336 return ENOTSUP;
337}
338
339static int locfs_unlink_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
340{
341 return ENOTSUP;
342}
343
344static int 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 int locfs_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
458{
459 return ENOTSUP;
460}
461
462static int locfs_mounted(service_id_t service_id, const char *opts,
463 fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
464{
465 *index = 0;
466 *size = 0;
467 *lnkcnt = 0;
468 return EOK;
469}
470
471static int locfs_unmounted(service_id_t service_id)
472{
473 return ENOTSUP;
474}
475
476static int
477locfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
478 size_t *rbytes)
479{
480 if (index == 0) {
481 ipc_callid_t callid;
482 size_t size;
483 if (!async_data_read_receive(&callid, &size)) {
484 async_answer_0(callid, EINVAL);
485 return EINVAL;
486 }
487
488 loc_sdesc_t *desc;
489 size_t count = loc_get_namespaces(&desc);
490
491 /* Get rid of root namespace */
492 size_t i;
493 for (i = 0; i < count; i++) {
494 if (str_cmp(desc[i].name, "") == 0) {
495 if (pos >= i)
496 pos++;
497
498 break;
499 }
500 }
501
502 if (pos < count) {
503 async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
504 free(desc);
505 *rbytes = 1;
506 return EOK;
507 }
508
509 free(desc);
510 pos -= count;
511
512 /* Search root namespace */
513 service_id_t namespace;
514 if (loc_namespace_get_id("", &namespace, 0) == EOK) {
515 count = loc_get_services(namespace, &desc);
516
517 if (pos < count) {
518 async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
519 free(desc);
520 *rbytes = 1;
521 return EOK;
522 }
523
524 free(desc);
525 }
526
527 async_answer_0(callid, ENOENT);
528 return ENOENT;
529 }
530
531 loc_object_type_t type = loc_id_probe(index);
532
533 if (type == LOC_OBJECT_NAMESPACE) {
534 /* Namespace directory */
535 ipc_callid_t callid;
536 size_t size;
537 if (!async_data_read_receive(&callid, &size)) {
538 async_answer_0(callid, EINVAL);
539 return EINVAL;
540 }
541
542 loc_sdesc_t *desc;
543 size_t count = loc_get_services(index, &desc);
544
545 if (pos < count) {
546 async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
547 free(desc);
548 *rbytes = 1;
549 return EOK;
550 }
551
552 free(desc);
553 async_answer_0(callid, ENOENT);
554 return ENOENT;
555 }
556
557 if (type == LOC_OBJECT_SERVICE) {
558 /* Device node */
559
560 fibril_mutex_lock(&services_mutex);
561 service_id_t service_index = index;
562 ht_link_t *lnk = hash_table_find(&services, &service_index);
563 if (lnk == NULL) {
564 fibril_mutex_unlock(&services_mutex);
565 return ENOENT;
566 }
567
568 service_t *dev = hash_table_get_inst(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
624 fibril_mutex_lock(&services_mutex);
625 service_id_t service_index = index;
626 ht_link_t *lnk = hash_table_find(&services, &service_index);
627 if (lnk == NULL) {
628 fibril_mutex_unlock(&services_mutex);
629 return ENOENT;
630 }
631
632 service_t *dev = hash_table_get_inst(lnk, service_t, link);
633 assert(dev->sess);
634
635 ipc_callid_t callid;
636 if (!async_data_write_receive(&callid, NULL)) {
637 fibril_mutex_unlock(&services_mutex);
638 async_answer_0(callid, EINVAL);
639 return EINVAL;
640 }
641
642 /* Make a request at the driver */
643 async_exch_t *exch = async_exchange_begin(dev->sess);
644
645 ipc_call_t answer;
646 aid_t msg = async_send_4(exch, VFS_OUT_WRITE, service_id,
647 index, LOWER32(pos), UPPER32(pos), &answer);
648
649 /* Forward the IPC_M_DATA_WRITE request to the driver */
650 async_forward_fast(callid, exch, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
651
652 async_exchange_end(exch);
653
654 fibril_mutex_unlock(&services_mutex);
655
656 /* Wait for reply from the driver. */
657 sysarg_t rc;
658 async_wait_for(msg, &rc);
659
660 /* Do not propagate EHANGUP back to VFS. */
661 if ((int) rc == EHANGUP)
662 rc = ENOTSUP;
663
664 *wbytes = IPC_GET_ARG1(answer);
665 *nsize = 0;
666 return rc;
667 }
668
669 return ENOENT;
670}
671
672static int
673locfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
674{
675 return ENOTSUP;
676}
677
678static int locfs_close(service_id_t service_id, fs_index_t index)
679{
680 if (index == 0)
681 return EOK;
682
683 loc_object_type_t type = loc_id_probe(index);
684
685 if (type == LOC_OBJECT_NAMESPACE) {
686 /* Namespace directory */
687 return EOK;
688 }
689
690 if (type == LOC_OBJECT_SERVICE) {
691
692 fibril_mutex_lock(&services_mutex);
693 service_id_t service_index = index;
694 ht_link_t *lnk = hash_table_find(&services, &service_index);
695 if (lnk == NULL) {
696 fibril_mutex_unlock(&services_mutex);
697 return ENOENT;
698 }
699
700 service_t *dev = hash_table_get_inst(lnk, service_t, link);
701 assert(dev->sess);
702 dev->refcount--;
703
704 if (dev->refcount == 0) {
705 async_hangup(dev->sess);
706 service_id_t service_index = index;
707 hash_table_remove(&services, &service_index);
708 }
709
710 fibril_mutex_unlock(&services_mutex);
711
712 return EOK;
713 }
714
715 return ENOENT;
716}
717
718static int locfs_sync(service_id_t service_id, fs_index_t index)
719{
720 if (index == 0)
721 return EOK;
722
723 loc_object_type_t type = loc_id_probe(index);
724
725 if (type == LOC_OBJECT_NAMESPACE) {
726 /* Namespace directory */
727 return EOK;
728 }
729
730 if (type == LOC_OBJECT_SERVICE) {
731
732 fibril_mutex_lock(&services_mutex);
733 service_id_t service_index = index;
734 ht_link_t *lnk = hash_table_find(&services, &service_index);
735 if (lnk == NULL) {
736 fibril_mutex_unlock(&services_mutex);
737 return ENOENT;
738 }
739
740 service_t *dev = hash_table_get_inst(lnk, service_t, link);
741 assert(dev->sess);
742
743 /* Make a request at the driver */
744 async_exch_t *exch = async_exchange_begin(dev->sess);
745
746 ipc_call_t answer;
747 aid_t msg = async_send_2(exch, VFS_OUT_SYNC, service_id,
748 index, &answer);
749
750 async_exchange_end(exch);
751
752 fibril_mutex_unlock(&services_mutex);
753
754 /* Wait for reply from the driver */
755 sysarg_t rc;
756 async_wait_for(msg, &rc);
757
758 return rc;
759 }
760
761 return ENOENT;
762}
763
764static int locfs_destroy(service_id_t service_id, fs_index_t index)
765{
766 return ENOTSUP;
767}
768
769vfs_out_ops_t locfs_ops = {
770 .fsprobe = locfs_fsprobe,
771 .mounted = locfs_mounted,
772 .unmounted = locfs_unmounted,
773 .read = locfs_read,
774 .write = locfs_write,
775 .truncate = locfs_truncate,
776 .close = locfs_close,
777 .destroy = locfs_destroy,
778 .sync = locfs_sync,
779};
780
781/**
782 * @}
783 */
Note: See TracBrowser for help on using the repository browser.