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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5e801dc was 5e801dc, checked in by GitHub <noreply@…>, 6 years ago

Indicate and enforce constness of hash table key in certain functions (#158)

The assumption here is that modifying key in the hash/equal functions in something completely unexpected, and not something you would ever want to do intentionally, so it makes sense to disallow it entirely to get that extra level of checking.

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