2 * Copyright (c) 2009 Jakub Jermar
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
34 * Glue code which is common to all FS implementations.
47 #include <fibril_synch.h>
51 #define on_error(rc, action) \
57 #define combine_rc(rc1, rc2) \
58 ((rc1) == EOK ? (rc2) : (rc1))
60 #define answer_and_return(call, rc) \
62 async_answer_0((call), (rc)); \
68 static vfs_out_ops_t
*vfs_out_ops
= NULL
;
69 static libfs_ops_t
*libfs_ops
= NULL
;
71 static char fs_name
[FS_NAME_MAXLEN
+ 1];
73 static void libfs_link(libfs_ops_t
*, fs_handle_t
, ipc_call_t
*);
74 static void libfs_lookup(libfs_ops_t
*, fs_handle_t
, ipc_call_t
*);
75 static void libfs_stat(libfs_ops_t
*, fs_handle_t
, ipc_call_t
*);
76 static void libfs_open_node(libfs_ops_t
*, fs_handle_t
, ipc_call_t
*);
77 static void libfs_statfs(libfs_ops_t
*, fs_handle_t
, ipc_call_t
*);
79 static void vfs_out_fsprobe(ipc_call_t
*req
)
81 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
83 vfs_fs_probe_info_t info
;
87 if ((!async_data_read_receive(&call
, &size
)) ||
88 (size
!= sizeof(info
))) {
89 async_answer_0(&call
, EIO
);
90 async_answer_0(req
, EIO
);
94 memset(&info
, 0, sizeof(info
));
95 rc
= vfs_out_ops
->fsprobe(service_id
, &info
);
97 async_answer_0(&call
, EIO
);
98 async_answer_0(req
, rc
);
102 async_data_read_finalize(&call
, &info
, sizeof(info
));
103 async_answer_0(req
, EOK
);
106 static void vfs_out_mounted(ipc_call_t
*req
)
108 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
112 /* Accept the mount options. */
113 rc
= async_data_write_accept((void **) &opts
, true, 0, 0, 0, NULL
);
115 async_answer_0(req
, rc
);
121 rc
= vfs_out_ops
->mounted(service_id
, opts
, &index
, &size
);
124 async_answer_3(req
, EOK
, index
, LOWER32(size
),
127 async_answer_0(req
, rc
);
132 static void vfs_out_unmounted(ipc_call_t
*req
)
134 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
137 rc
= vfs_out_ops
->unmounted(service_id
);
139 async_answer_0(req
, rc
);
142 static void vfs_out_link(ipc_call_t
*req
)
144 libfs_link(libfs_ops
, reg
.fs_handle
, req
);
147 static void vfs_out_lookup(ipc_call_t
*req
)
149 libfs_lookup(libfs_ops
, reg
.fs_handle
, req
);
152 static void vfs_out_read(ipc_call_t
*req
)
154 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
155 fs_index_t index
= (fs_index_t
) ipc_get_arg2(req
);
156 aoff64_t pos
= (aoff64_t
) MERGE_LOUP32(ipc_get_arg3(req
),
161 rc
= vfs_out_ops
->read(service_id
, index
, pos
, &rbytes
);
164 async_answer_1(req
, EOK
, rbytes
);
166 async_answer_0(req
, rc
);
169 static void vfs_out_write(ipc_call_t
*req
)
171 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
172 fs_index_t index
= (fs_index_t
) ipc_get_arg2(req
);
173 aoff64_t pos
= (aoff64_t
) MERGE_LOUP32(ipc_get_arg3(req
),
179 rc
= vfs_out_ops
->write(service_id
, index
, pos
, &wbytes
, &nsize
);
182 async_answer_3(req
, EOK
, wbytes
, LOWER32(nsize
),
185 async_answer_0(req
, rc
);
188 static void vfs_out_truncate(ipc_call_t
*req
)
190 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
191 fs_index_t index
= (fs_index_t
) ipc_get_arg2(req
);
192 aoff64_t size
= (aoff64_t
) MERGE_LOUP32(ipc_get_arg3(req
),
196 rc
= vfs_out_ops
->truncate(service_id
, index
, size
);
198 async_answer_0(req
, rc
);
201 static void vfs_out_close(ipc_call_t
*req
)
203 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
204 fs_index_t index
= (fs_index_t
) ipc_get_arg2(req
);
207 rc
= vfs_out_ops
->close(service_id
, index
);
209 async_answer_0(req
, rc
);
212 static void vfs_out_destroy(ipc_call_t
*req
)
214 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
215 fs_index_t index
= (fs_index_t
) ipc_get_arg2(req
);
218 fs_node_t
*node
= NULL
;
219 rc
= libfs_ops
->node_get(&node
, service_id
, index
);
220 if (rc
== EOK
&& node
!= NULL
) {
221 bool destroy
= (libfs_ops
->lnkcnt_get(node
) == 0);
222 libfs_ops
->node_put(node
);
224 rc
= vfs_out_ops
->destroy(service_id
, index
);
226 async_answer_0(req
, rc
);
229 static void vfs_out_open_node(ipc_call_t
*req
)
231 libfs_open_node(libfs_ops
, reg
.fs_handle
, req
);
234 static void vfs_out_stat(ipc_call_t
*req
)
236 libfs_stat(libfs_ops
, reg
.fs_handle
, req
);
239 static void vfs_out_sync(ipc_call_t
*req
)
241 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
242 fs_index_t index
= (fs_index_t
) ipc_get_arg2(req
);
245 rc
= vfs_out_ops
->sync(service_id
, index
);
247 async_answer_0(req
, rc
);
250 static void vfs_out_statfs(ipc_call_t
*req
)
252 libfs_statfs(libfs_ops
, reg
.fs_handle
, req
);
255 static void vfs_out_is_empty(ipc_call_t
*req
)
257 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
258 fs_index_t index
= (fs_index_t
) ipc_get_arg2(req
);
261 fs_node_t
*node
= NULL
;
262 rc
= libfs_ops
->node_get(&node
, service_id
, index
);
264 async_answer_0(req
, rc
);
266 async_answer_0(req
, EINVAL
);
268 bool children
= false;
269 rc
= libfs_ops
->has_children(&children
, node
);
270 libfs_ops
->node_put(node
);
273 async_answer_0(req
, rc
);
274 async_answer_0(req
, children
? ENOTEMPTY
: EOK
);
277 static void vfs_connection(ipc_call_t
*icall
, void *arg
)
279 if (icall
->cap_handle
) {
281 * This only happens for connections opened by
282 * IPC_M_CONNECT_ME_TO calls as opposed to callback connections
283 * created by IPC_M_CONNECT_TO_ME.
285 async_accept_0(icall
);
290 async_get_call(&call
);
292 if (!ipc_get_imethod(&call
)) {
293 async_answer_0(&call
, EOK
);
297 switch (ipc_get_imethod(&call
)) {
298 case VFS_OUT_FSPROBE
:
299 vfs_out_fsprobe(&call
);
301 case VFS_OUT_MOUNTED
:
302 vfs_out_mounted(&call
);
304 case VFS_OUT_UNMOUNTED
:
305 vfs_out_unmounted(&call
);
311 vfs_out_lookup(&call
);
317 vfs_out_write(&call
);
319 case VFS_OUT_TRUNCATE
:
320 vfs_out_truncate(&call
);
323 vfs_out_close(&call
);
325 case VFS_OUT_DESTROY
:
326 vfs_out_destroy(&call
);
328 case VFS_OUT_OPEN_NODE
:
329 vfs_out_open_node(&call
);
338 vfs_out_statfs(&call
);
340 case VFS_OUT_IS_EMPTY
:
341 vfs_out_is_empty(&call
);
344 async_answer_0(&call
, ENOTSUP
);
350 /** Register file system server.
352 * This function abstracts away the tedious registration protocol from
353 * file system implementations and lets them to reuse this registration glue
356 * @param sess Session for communication with VFS.
357 * @param info VFS info structure supplied by the file system
359 * @param vops Address of the vfs_out_ops_t structure.
360 * @param lops Address of the libfs_ops_t structure.
362 * @return EOK on success or a non-zero error code on errror.
365 errno_t
fs_register(async_sess_t
*sess
, vfs_info_t
*info
, vfs_out_ops_t
*vops
,
369 * Tell VFS that we are here and want to get registered.
370 * We use the async framework because VFS will answer the request
371 * out-of-order, when it knows that the operation succeeded or failed.
374 async_exch_t
*exch
= async_exchange_begin(sess
);
377 aid_t req
= async_send_0(exch
, VFS_IN_REGISTER
, &answer
);
380 * Send our VFS info structure to VFS.
382 errno_t rc
= async_data_write_start(exch
, info
, sizeof(*info
));
385 async_exchange_end(exch
);
391 * Set VFS_OUT and libfs operations.
396 str_cpy(fs_name
, sizeof(fs_name
), info
->name
);
399 * Ask VFS for callback connection.
402 rc
= async_create_callback_port(exch
, INTERFACE_VFS_DRIVER_CB
, 0, 0,
403 vfs_connection
, NULL
, &port
);
406 * Request sharing the Path Lookup Buffer with VFS.
408 rc
= async_share_in_start_0_0(exch
, PLB_SIZE
, (void *) ®
.plb_ro
);
409 if (reg
.plb_ro
== AS_MAP_FAILED
) {
410 async_exchange_end(exch
);
415 async_exchange_end(exch
);
423 * Pick up the answer for the request to the VFS_IN_REQUEST call.
425 async_wait_for(req
, NULL
);
426 reg
.fs_handle
= (int) ipc_get_arg1(&answer
);
428 return ipc_get_retval(&answer
);
431 void fs_node_initialize(fs_node_t
*fn
)
433 memset(fn
, 0, sizeof(fs_node_t
));
436 static char plb_get_char(unsigned pos
)
438 return reg
.plb_ro
[pos
% PLB_SIZE
];
441 static errno_t
plb_get_component(char *dest
, unsigned *sz
, unsigned *ppos
,
444 unsigned pos
= *ppos
;
452 char c
= plb_get_char(pos
);
456 for (int i
= 0; i
<= NAME_MAX
; i
++) {
457 c
= plb_get_char(pos
);
458 if (pos
== last
|| c
== '/') {
471 static errno_t
receive_fname(char *buffer
)
476 if (!async_data_write_receive(&call
, &size
))
479 if (size
> NAME_MAX
+ 1) {
480 async_answer_0(&call
, ERANGE
);
484 return async_data_write_finalize(&call
, buffer
, size
);
487 /** Link a file at a path.
490 void libfs_link(libfs_ops_t
*ops
, fs_handle_t fs_handle
, ipc_call_t
*req
)
492 service_id_t parent_sid
= ipc_get_arg1(req
);
493 fs_index_t parent_index
= ipc_get_arg2(req
);
494 fs_index_t child_index
= ipc_get_arg3(req
);
496 char component
[NAME_MAX
+ 1];
497 errno_t rc
= receive_fname(component
);
499 async_answer_0(req
, rc
);
503 fs_node_t
*parent
= NULL
;
504 rc
= ops
->node_get(&parent
, parent_sid
, parent_index
);
505 if (parent
== NULL
) {
506 async_answer_0(req
, rc
== EOK
? EBADF
: rc
);
510 fs_node_t
*child
= NULL
;
511 rc
= ops
->node_get(&child
, parent_sid
, child_index
);
513 async_answer_0(req
, rc
== EOK
? EBADF
: rc
);
514 ops
->node_put(parent
);
518 rc
= ops
->link(parent
, child
, component
);
519 ops
->node_put(parent
);
520 ops
->node_put(child
);
521 async_answer_0(req
, rc
);
524 /** Lookup VFS triplet by name in the file system name space.
526 * The path passed in the PLB must be in the canonical file system path format
527 * as returned by the canonify() function.
529 * @param ops libfs operations structure with function pointers to
530 * file system implementation
531 * @param fs_handle File system handle of the file system where to perform
533 * @param req VFS_OUT_LOOKUP request data itself.
536 void libfs_lookup(libfs_ops_t
*ops
, fs_handle_t fs_handle
, ipc_call_t
*req
)
538 unsigned first
= ipc_get_arg1(req
);
539 unsigned len
= ipc_get_arg2(req
);
540 service_id_t service_id
= ipc_get_arg3(req
);
541 fs_index_t index
= ipc_get_arg4(req
);
542 int lflag
= ipc_get_arg5(req
);
544 // TODO: Validate flags.
546 unsigned next
= first
;
547 unsigned last
= first
+ len
;
549 char component
[NAME_MAX
+ 1];
552 fs_node_t
*par
= NULL
;
553 fs_node_t
*cur
= NULL
;
554 fs_node_t
*tmp
= NULL
;
557 rc
= ops
->node_get(&cur
, service_id
, index
);
559 async_answer_0(req
, rc
);
565 /* Find the file and its parent. */
567 unsigned last_next
= 0;
569 while (next
!= last
) {
575 if (!ops
->is_directory(cur
)) {
576 async_answer_0(req
, ENOTDIR
);
581 /* Collect the component */
582 rc
= plb_get_component(component
, &clen
, &next
, last
);
583 assert(rc
!= ERANGE
);
585 async_answer_0(req
, rc
);
590 /* The path is just "/". */
594 assert(component
[clen
] == 0);
596 /* Match the component */
597 rc
= ops
->match(&tmp
, cur
, component
);
599 async_answer_0(req
, rc
);
603 /* Descend one level */
605 rc
= ops
->node_put(par
);
607 async_answer_0(req
, rc
);
618 * At this point, par is either NULL or a directory.
619 * If cur is NULL, the looked up file does not exist yet.
622 assert(par
== NULL
|| ops
->is_directory(par
));
623 assert(par
!= NULL
|| cur
!= NULL
);
625 /* Check for some error conditions. */
627 if (cur
&& (lflag
& L_FILE
) && (ops
->is_directory(cur
))) {
628 async_answer_0(req
, EISDIR
);
632 if (cur
&& (lflag
& L_DIRECTORY
) && (ops
->is_file(cur
))) {
633 async_answer_0(req
, ENOTDIR
);
639 if (lflag
& L_UNLINK
) {
641 async_answer_0(req
, ENOENT
);
645 async_answer_0(req
, EINVAL
);
649 rc
= ops
->unlink(par
, cur
, component
);
651 aoff64_t size
= ops
->size_get(cur
);
652 async_answer_5(req
, EOK
, fs_handle
,
654 (ops
->is_directory(cur
) << 16) | last
,
655 LOWER32(size
), UPPER32(size
));
657 async_answer_0(req
, rc
);
664 if (lflag
& L_CREATE
) {
665 if (cur
&& (lflag
& L_EXCLUSIVE
)) {
666 async_answer_0(req
, EEXIST
);
671 rc
= ops
->create(&cur
, service_id
,
672 lflag
& (L_FILE
| L_DIRECTORY
));
674 async_answer_0(req
, rc
);
678 async_answer_0(req
, ENOSPC
);
682 rc
= ops
->link(par
, cur
, component
);
684 (void) ops
->destroy(cur
);
686 async_answer_0(req
, rc
);
695 async_answer_5(req
, EOK
, fs_handle
, ops
->index_get(par
),
696 (ops
->is_directory(par
) << 16) | last_next
,
697 LOWER32(ops
->size_get(par
)), UPPER32(ops
->size_get(par
)));
701 async_answer_5(req
, EOK
, fs_handle
, ops
->index_get(cur
),
702 (ops
->is_directory(cur
) << 16) | last
, LOWER32(ops
->size_get(cur
)),
703 UPPER32(ops
->size_get(cur
)));
707 (void) ops
->node_put(par
);
710 (void) ops
->node_put(cur
);
713 (void) ops
->node_put(tmp
);
716 void libfs_stat(libfs_ops_t
*ops
, fs_handle_t fs_handle
, ipc_call_t
*req
)
718 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
719 fs_index_t index
= (fs_index_t
) ipc_get_arg2(req
);
722 errno_t rc
= ops
->node_get(&fn
, service_id
, index
);
723 on_error(rc
, answer_and_return(req
, rc
));
727 if ((!async_data_read_receive(&call
, &size
)) ||
728 (size
!= sizeof(vfs_stat_t
))) {
730 async_answer_0(&call
, EINVAL
);
731 async_answer_0(req
, EINVAL
);
736 memset(&stat
, 0, sizeof(vfs_stat_t
));
738 stat
.fs_handle
= fs_handle
;
739 stat
.service_id
= service_id
;
741 stat
.lnkcnt
= ops
->lnkcnt_get(fn
);
742 stat
.is_file
= ops
->is_file(fn
);
743 stat
.is_directory
= ops
->is_directory(fn
);
744 stat
.size
= ops
->size_get(fn
);
745 stat
.service
= ops
->service_get(fn
);
749 async_data_read_finalize(&call
, &stat
, sizeof(vfs_stat_t
));
750 async_answer_0(req
, EOK
);
753 void libfs_statfs(libfs_ops_t
*ops
, fs_handle_t fs_handle
, ipc_call_t
*req
)
755 service_id_t service_id
= (service_id_t
) ipc_get_arg1(req
);
756 fs_index_t index
= (fs_index_t
) ipc_get_arg2(req
);
759 errno_t rc
= ops
->node_get(&fn
, service_id
, index
);
760 on_error(rc
, answer_and_return(req
, rc
));
764 if ((!async_data_read_receive(&call
, &size
)) ||
765 (size
!= sizeof(vfs_statfs_t
))) {
770 memset(&st
, 0, sizeof(vfs_statfs_t
));
772 str_cpy(st
.fs_name
, sizeof(st
.fs_name
), fs_name
);
774 if (ops
->size_block
!= NULL
) {
775 rc
= ops
->size_block(service_id
, &st
.f_bsize
);
780 if (ops
->total_block_count
!= NULL
) {
781 rc
= ops
->total_block_count(service_id
, &st
.f_blocks
);
786 if (ops
->free_block_count
!= NULL
) {
787 rc
= ops
->free_block_count(service_id
, &st
.f_bfree
);
793 async_data_read_finalize(&call
, &st
, sizeof(vfs_statfs_t
));
794 async_answer_0(req
, EOK
);
799 async_answer_0(&call
, EINVAL
);
800 async_answer_0(req
, EINVAL
);
803 /** Open VFS triplet.
805 * @param ops libfs operations structure with function pointers to
806 * file system implementation
807 * @param req VFS_OUT_OPEN_NODE request data itself.
810 void libfs_open_node(libfs_ops_t
*ops
, fs_handle_t fs_handle
, ipc_call_t
*req
)
812 service_id_t service_id
= ipc_get_arg1(req
);
813 fs_index_t index
= ipc_get_arg2(req
);
816 errno_t rc
= ops
->node_get(&fn
, service_id
, index
);
817 on_error(rc
, answer_and_return(req
, rc
));
820 async_answer_0(req
, ENOENT
);
824 rc
= ops
->node_open(fn
);
825 aoff64_t size
= ops
->size_get(fn
);
826 async_answer_4(req
, rc
, LOWER32(size
), UPPER32(size
),
828 (ops
->is_file(fn
) ? L_FILE
: 0) |
829 (ops
->is_directory(fn
) ? L_DIRECTORY
: 0));
831 (void) ops
->node_put(fn
);
834 static FIBRIL_MUTEX_INITIALIZE(instances_mutex
);
835 static LIST_INITIALIZE(instances_list
);
838 service_id_t service_id
;
843 errno_t
fs_instance_create(service_id_t service_id
, void *data
)
845 fs_instance_t
*inst
= malloc(sizeof(fs_instance_t
));
849 link_initialize(&inst
->link
);
850 inst
->service_id
= service_id
;
853 fibril_mutex_lock(&instances_mutex
);
854 list_foreach(instances_list
, link
, fs_instance_t
, cur
) {
855 if (cur
->service_id
== service_id
) {
856 fibril_mutex_unlock(&instances_mutex
);
861 /* keep the list sorted */
862 if (cur
->service_id
< service_id
) {
863 list_insert_before(&inst
->link
, &cur
->link
);
864 fibril_mutex_unlock(&instances_mutex
);
868 list_append(&inst
->link
, &instances_list
);
869 fibril_mutex_unlock(&instances_mutex
);
874 errno_t
fs_instance_get(service_id_t service_id
, void **idp
)
876 fibril_mutex_lock(&instances_mutex
);
878 list_foreach(instances_list
, link
, fs_instance_t
, inst
) {
879 if (inst
->service_id
== service_id
) {
881 fibril_mutex_unlock(&instances_mutex
);
886 fibril_mutex_unlock(&instances_mutex
);
890 errno_t
fs_instance_destroy(service_id_t service_id
)
892 fibril_mutex_lock(&instances_mutex
);
894 list_foreach(instances_list
, link
, fs_instance_t
, inst
) {
895 if (inst
->service_id
== service_id
) {
896 list_remove(&inst
->link
);
897 fibril_mutex_unlock(&instances_mutex
);
903 fibril_mutex_unlock(&instances_mutex
);