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(rid, rc) \
62 async_answer_0((rid), (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_callid_t
,
75 static void libfs_lookup(libfs_ops_t
*, fs_handle_t
, ipc_callid_t
,
77 static void libfs_stat(libfs_ops_t
*, fs_handle_t
, ipc_callid_t
, ipc_call_t
*);
78 static void libfs_open_node(libfs_ops_t
*, fs_handle_t
, ipc_callid_t
,
80 static void libfs_statfs(libfs_ops_t
*, fs_handle_t
, ipc_callid_t
,
83 static void vfs_out_mounted(ipc_callid_t rid
, ipc_call_t
*req
)
85 service_id_t service_id
= (service_id_t
) IPC_GET_ARG1(*req
);
89 /* Accept the mount options. */
90 rc
= async_data_write_accept((void **) &opts
, true, 0, 0, 0, NULL
);
92 async_answer_0(rid
, rc
);
99 rc
= vfs_out_ops
->mounted(service_id
, opts
, &index
, &size
, &lnkcnt
);
102 async_answer_4(rid
, EOK
, index
, LOWER32(size
), UPPER32(size
),
105 async_answer_0(rid
, rc
);
110 static void vfs_out_unmounted(ipc_callid_t rid
, ipc_call_t
*req
)
112 service_id_t service_id
= (service_id_t
) IPC_GET_ARG1(*req
);
115 rc
= vfs_out_ops
->unmounted(service_id
);
117 async_answer_0(rid
, rc
);
120 static void vfs_out_link(ipc_callid_t rid
, ipc_call_t
*req
)
122 libfs_link(libfs_ops
, reg
.fs_handle
, rid
, req
);
125 static void vfs_out_lookup(ipc_callid_t rid
, ipc_call_t
*req
)
127 libfs_lookup(libfs_ops
, reg
.fs_handle
, rid
, req
);
130 static void vfs_out_read(ipc_callid_t rid
, ipc_call_t
*req
)
132 service_id_t service_id
= (service_id_t
) IPC_GET_ARG1(*req
);
133 fs_index_t index
= (fs_index_t
) IPC_GET_ARG2(*req
);
134 aoff64_t pos
= (aoff64_t
) MERGE_LOUP32(IPC_GET_ARG3(*req
),
139 rc
= vfs_out_ops
->read(service_id
, index
, pos
, &rbytes
);
142 async_answer_1(rid
, EOK
, rbytes
);
144 async_answer_0(rid
, rc
);
147 static void vfs_out_write(ipc_callid_t rid
, ipc_call_t
*req
)
149 service_id_t service_id
= (service_id_t
) IPC_GET_ARG1(*req
);
150 fs_index_t index
= (fs_index_t
) IPC_GET_ARG2(*req
);
151 aoff64_t pos
= (aoff64_t
) MERGE_LOUP32(IPC_GET_ARG3(*req
),
157 rc
= vfs_out_ops
->write(service_id
, index
, pos
, &wbytes
, &nsize
);
160 async_answer_3(rid
, EOK
, wbytes
, LOWER32(nsize
),
163 async_answer_0(rid
, rc
);
166 static void vfs_out_truncate(ipc_callid_t rid
, ipc_call_t
*req
)
168 service_id_t service_id
= (service_id_t
) IPC_GET_ARG1(*req
);
169 fs_index_t index
= (fs_index_t
) IPC_GET_ARG2(*req
);
170 aoff64_t size
= (aoff64_t
) MERGE_LOUP32(IPC_GET_ARG3(*req
),
174 rc
= vfs_out_ops
->truncate(service_id
, index
, size
);
176 async_answer_0(rid
, rc
);
179 static void vfs_out_close(ipc_callid_t rid
, ipc_call_t
*req
)
181 service_id_t service_id
= (service_id_t
) IPC_GET_ARG1(*req
);
182 fs_index_t index
= (fs_index_t
) IPC_GET_ARG2(*req
);
185 rc
= vfs_out_ops
->close(service_id
, index
);
187 async_answer_0(rid
, rc
);
190 static void vfs_out_destroy(ipc_callid_t rid
, ipc_call_t
*req
)
192 service_id_t service_id
= (service_id_t
) IPC_GET_ARG1(*req
);
193 fs_index_t index
= (fs_index_t
) IPC_GET_ARG2(*req
);
196 fs_node_t
*node
= NULL
;
197 rc
= libfs_ops
->node_get(&node
, service_id
, index
);
198 if (rc
== EOK
&& node
!= NULL
) {
199 bool destroy
= (libfs_ops
->lnkcnt_get(node
) == 0);
200 libfs_ops
->node_put(node
);
202 rc
= vfs_out_ops
->destroy(service_id
, index
);
204 async_answer_0(rid
, rc
);
207 static void vfs_out_open_node(ipc_callid_t rid
, ipc_call_t
*req
)
209 libfs_open_node(libfs_ops
, reg
.fs_handle
, rid
, req
);
212 static void vfs_out_stat(ipc_callid_t rid
, ipc_call_t
*req
)
214 libfs_stat(libfs_ops
, reg
.fs_handle
, rid
, req
);
217 static void vfs_out_sync(ipc_callid_t rid
, ipc_call_t
*req
)
219 service_id_t service_id
= (service_id_t
) IPC_GET_ARG1(*req
);
220 fs_index_t index
= (fs_index_t
) IPC_GET_ARG2(*req
);
223 rc
= vfs_out_ops
->sync(service_id
, index
);
225 async_answer_0(rid
, rc
);
228 static void vfs_out_statfs(ipc_callid_t rid
, ipc_call_t
*req
)
230 libfs_statfs(libfs_ops
, reg
.fs_handle
, rid
, req
);
233 static void vfs_out_is_empty(ipc_callid_t rid
, ipc_call_t
*req
)
235 service_id_t service_id
= (service_id_t
) IPC_GET_ARG1(*req
);
236 fs_index_t index
= (fs_index_t
) IPC_GET_ARG2(*req
);
239 fs_node_t
*node
= NULL
;
240 rc
= libfs_ops
->node_get(&node
, service_id
, index
);
242 async_answer_0(rid
, rc
);
244 async_answer_0(rid
, EINVAL
);
246 bool children
= false;
247 rc
= libfs_ops
->has_children(&children
, node
);
248 libfs_ops
->node_put(node
);
251 async_answer_0(rid
, rc
);
252 async_answer_0(rid
, children
? ENOTEMPTY
: EOK
);
255 static void vfs_connection(ipc_callid_t iid
, ipc_call_t
*icall
, void *arg
)
259 * This only happens for connections opened by
260 * IPC_M_CONNECT_ME_TO calls as opposed to callback connections
261 * created by IPC_M_CONNECT_TO_ME.
263 async_answer_0(iid
, EOK
);
268 ipc_callid_t callid
= async_get_call(&call
);
270 if (!IPC_GET_IMETHOD(call
))
273 switch (IPC_GET_IMETHOD(call
)) {
274 case VFS_OUT_MOUNTED
:
275 vfs_out_mounted(callid
, &call
);
277 case VFS_OUT_UNMOUNTED
:
278 vfs_out_unmounted(callid
, &call
);
281 vfs_out_link(callid
, &call
);
284 vfs_out_lookup(callid
, &call
);
287 vfs_out_read(callid
, &call
);
290 vfs_out_write(callid
, &call
);
292 case VFS_OUT_TRUNCATE
:
293 vfs_out_truncate(callid
, &call
);
296 vfs_out_close(callid
, &call
);
298 case VFS_OUT_DESTROY
:
299 vfs_out_destroy(callid
, &call
);
301 case VFS_OUT_OPEN_NODE
:
302 vfs_out_open_node(callid
, &call
);
305 vfs_out_stat(callid
, &call
);
308 vfs_out_sync(callid
, &call
);
311 vfs_out_statfs(callid
, &call
);
313 case VFS_OUT_IS_EMPTY
:
314 vfs_out_is_empty(callid
, &call
);
317 async_answer_0(callid
, ENOTSUP
);
323 /** Register file system server.
325 * This function abstracts away the tedious registration protocol from
326 * file system implementations and lets them to reuse this registration glue
329 * @param sess Session for communication with VFS.
330 * @param info VFS info structure supplied by the file system
332 * @param vops Address of the vfs_out_ops_t structure.
333 * @param lops Address of the libfs_ops_t structure.
335 * @return EOK on success or a non-zero error code on errror.
338 int fs_register(async_sess_t
*sess
, vfs_info_t
*info
, vfs_out_ops_t
*vops
,
342 * Tell VFS that we are here and want to get registered.
343 * We use the async framework because VFS will answer the request
344 * out-of-order, when it knows that the operation succeeded or failed.
347 async_exch_t
*exch
= async_exchange_begin(sess
);
350 aid_t req
= async_send_0(exch
, VFS_IN_REGISTER
, &answer
);
353 * Send our VFS info structure to VFS.
355 int rc
= async_data_write_start(exch
, info
, sizeof(*info
));
358 async_exchange_end(exch
);
364 * Set VFS_OUT and libfs operations.
369 str_cpy(fs_name
, sizeof(fs_name
), info
->name
);
372 * Ask VFS for callback connection.
375 rc
= async_create_callback_port(exch
, INTERFACE_VFS_DRIVER_CB
, 0, 0,
376 vfs_connection
, NULL
, &port
);
379 * Request sharing the Path Lookup Buffer with VFS.
381 rc
= async_share_in_start_0_0(exch
, PLB_SIZE
, (void *) ®
.plb_ro
);
382 if (reg
.plb_ro
== AS_MAP_FAILED
) {
383 async_exchange_end(exch
);
388 async_exchange_end(exch
);
396 * Pick up the answer for the request to the VFS_IN_REQUEST call.
398 async_wait_for(req
, NULL
);
399 reg
.fs_handle
= (int) IPC_GET_ARG1(answer
);
402 * Tell the async framework that other connections are to be handled by
403 * the same connection fibril as well.
405 async_set_fallback_port_handler(vfs_connection
, NULL
);
407 return IPC_GET_RETVAL(answer
);
410 void fs_node_initialize(fs_node_t
*fn
)
412 memset(fn
, 0, sizeof(fs_node_t
));
415 static char plb_get_char(unsigned pos
)
417 return reg
.plb_ro
[pos
% PLB_SIZE
];
420 static int plb_get_component(char *dest
, unsigned *sz
, unsigned *ppos
,
423 unsigned pos
= *ppos
;
431 char c
= plb_get_char(pos
);
435 for (int i
= 0; i
<= NAME_MAX
; i
++) {
436 c
= plb_get_char(pos
);
437 if (pos
== last
|| c
== '/') {
450 static int receive_fname(char *buffer
)
455 if (!async_data_write_receive(&wcall
, &size
))
457 if (size
> NAME_MAX
+ 1) {
458 async_answer_0(wcall
, ERANGE
);
461 return async_data_write_finalize(wcall
, buffer
, size
);
464 /** Link a file at a path.
466 void libfs_link(libfs_ops_t
*ops
, fs_handle_t fs_handle
, ipc_callid_t rid
,
469 service_id_t parent_sid
= IPC_GET_ARG1(*req
);
470 fs_index_t parent_index
= IPC_GET_ARG2(*req
);
471 fs_index_t child_index
= IPC_GET_ARG3(*req
);
473 char component
[NAME_MAX
+ 1];
474 int rc
= receive_fname(component
);
476 async_answer_0(rid
, rc
);
480 fs_node_t
*parent
= NULL
;
481 rc
= ops
->node_get(&parent
, parent_sid
, parent_index
);
482 if (parent
== NULL
) {
483 async_answer_0(rid
, rc
== EOK
? EBADF
: rc
);
487 fs_node_t
*child
= NULL
;
488 rc
= ops
->node_get(&child
, parent_sid
, child_index
);
490 async_answer_0(rid
, rc
== EOK
? EBADF
: rc
);
491 ops
->node_put(parent
);
495 rc
= ops
->link(parent
, child
, component
);
496 ops
->node_put(parent
);
497 ops
->node_put(child
);
498 async_answer_0(rid
, rc
);
501 /** Lookup VFS triplet by name in the file system name space.
503 * The path passed in the PLB must be in the canonical file system path format
504 * as returned by the canonify() function.
506 * @param ops libfs operations structure with function pointers to
507 * file system implementation
508 * @param fs_handle File system handle of the file system where to perform
510 * @param rid Request ID of the VFS_OUT_LOOKUP request.
511 * @param request VFS_OUT_LOOKUP request data itself.
514 void libfs_lookup(libfs_ops_t
*ops
, fs_handle_t fs_handle
, ipc_callid_t rid
,
517 unsigned first
= IPC_GET_ARG1(*req
);
518 unsigned len
= IPC_GET_ARG2(*req
);
519 service_id_t service_id
= IPC_GET_ARG3(*req
);
520 fs_index_t index
= IPC_GET_ARG4(*req
);
521 int lflag
= IPC_GET_ARG5(*req
);
523 // TODO: Validate flags.
525 unsigned next
= first
;
526 unsigned last
= first
+ len
;
528 char component
[NAME_MAX
+ 1];
531 fs_node_t
*par
= NULL
;
532 fs_node_t
*cur
= NULL
;
533 fs_node_t
*tmp
= NULL
;
536 rc
= ops
->node_get(&cur
, service_id
, index
);
538 async_answer_0(rid
, rc
);
544 /* Find the file and its parent. */
546 unsigned last_next
= 0;
548 while (next
!= last
) {
554 if (!ops
->is_directory(cur
)) {
555 async_answer_0(rid
, ENOTDIR
);
560 /* Collect the component */
561 rc
= plb_get_component(component
, &clen
, &next
, last
);
562 assert(rc
!= ERANGE
);
564 async_answer_0(rid
, rc
);
569 /* The path is just "/". */
573 assert(component
[clen
] == 0);
575 /* Match the component */
576 rc
= ops
->match(&tmp
, cur
, component
);
578 async_answer_0(rid
, rc
);
582 /* Descend one level */
584 rc
= ops
->node_put(par
);
586 async_answer_0(rid
, rc
);
596 /* At this point, par is either NULL or a directory.
597 * If cur is NULL, the looked up file does not exist yet.
600 assert(par
== NULL
|| ops
->is_directory(par
));
601 assert(par
!= NULL
|| cur
!= NULL
);
603 /* Check for some error conditions. */
605 if (cur
&& (lflag
& L_FILE
) && (ops
->is_directory(cur
))) {
606 async_answer_0(rid
, EISDIR
);
610 if (cur
&& (lflag
& L_DIRECTORY
) && (ops
->is_file(cur
))) {
611 async_answer_0(rid
, ENOTDIR
);
617 if (lflag
& L_UNLINK
) {
619 async_answer_0(rid
, ENOENT
);
623 async_answer_0(rid
, EINVAL
);
627 rc
= ops
->unlink(par
, cur
, component
);
629 aoff64_t size
= ops
->size_get(cur
);
630 async_answer_5(rid
, fs_handle
, service_id
,
632 (ops
->is_directory(cur
) << 16) | last
,
633 LOWER32(size
), UPPER32(size
));
635 async_answer_0(rid
, rc
);
642 if (lflag
& L_CREATE
) {
643 if (cur
&& (lflag
& L_EXCLUSIVE
)) {
644 async_answer_0(rid
, EEXIST
);
649 rc
= ops
->create(&cur
, service_id
,
650 lflag
& (L_FILE
| L_DIRECTORY
));
652 async_answer_0(rid
, rc
);
656 async_answer_0(rid
, ENOSPC
);
660 rc
= ops
->link(par
, cur
, component
);
662 (void) ops
->destroy(cur
);
664 async_answer_0(rid
, rc
);
673 async_answer_5(rid
, fs_handle
, service_id
, ops
->index_get(par
),
674 last_next
, -1, true);
678 aoff64_t size
= ops
->size_get(cur
);
679 async_answer_5(rid
, fs_handle
, service_id
, ops
->index_get(cur
),
680 (ops
->is_directory(cur
) << 16) | last
, LOWER32(size
),
685 (void) ops
->node_put(par
);
688 (void) ops
->node_put(cur
);
691 (void) ops
->node_put(tmp
);
694 void libfs_stat(libfs_ops_t
*ops
, fs_handle_t fs_handle
, ipc_callid_t rid
,
697 service_id_t service_id
= (service_id_t
) IPC_GET_ARG1(*request
);
698 fs_index_t index
= (fs_index_t
) IPC_GET_ARG2(*request
);
701 int rc
= ops
->node_get(&fn
, service_id
, index
);
702 on_error(rc
, answer_and_return(rid
, rc
));
706 if ((!async_data_read_receive(&callid
, &size
)) ||
707 (size
!= sizeof(struct stat
))) {
709 async_answer_0(callid
, EINVAL
);
710 async_answer_0(rid
, EINVAL
);
715 memset(&stat
, 0, sizeof(struct stat
));
717 stat
.fs_handle
= fs_handle
;
718 stat
.service_id
= service_id
;
720 stat
.lnkcnt
= ops
->lnkcnt_get(fn
);
721 stat
.is_file
= ops
->is_file(fn
);
722 stat
.is_directory
= ops
->is_directory(fn
);
723 stat
.size
= ops
->size_get(fn
);
724 stat
.service
= ops
->service_get(fn
);
729 async_data_read_finalize(callid
, &stat
, sizeof(struct stat
));
730 async_answer_0(rid
, EOK
);
733 void libfs_statfs(libfs_ops_t
*ops
, fs_handle_t fs_handle
, ipc_callid_t rid
,
736 service_id_t service_id
= (service_id_t
) IPC_GET_ARG1(*request
);
737 fs_index_t index
= (fs_index_t
) IPC_GET_ARG2(*request
);
740 int rc
= ops
->node_get(&fn
, service_id
, index
);
741 on_error(rc
, answer_and_return(rid
, rc
));
745 if ((!async_data_read_receive(&callid
, &size
)) ||
746 (size
!= sizeof(struct statfs
))) {
751 memset(&st
, 0, sizeof(struct statfs
));
753 str_cpy(st
.fs_name
, sizeof(st
.fs_name
), fs_name
);
755 if (ops
->size_block
!= NULL
) {
756 rc
= ops
->size_block(service_id
, &st
.f_bsize
);
761 if (ops
->total_block_count
!= NULL
) {
762 rc
= ops
->total_block_count(service_id
, &st
.f_blocks
);
767 if (ops
->free_block_count
!= NULL
) {
768 rc
= ops
->free_block_count(service_id
, &st
.f_bfree
);
774 async_data_read_finalize(callid
, &st
, sizeof(struct statfs
));
775 async_answer_0(rid
, EOK
);
780 async_answer_0(callid
, EINVAL
);
781 async_answer_0(rid
, EINVAL
);
785 /** Open VFS triplet.
787 * @param ops libfs operations structure with function pointers to
788 * file system implementation
789 * @param rid Request ID of the VFS_OUT_OPEN_NODE request.
790 * @param request VFS_OUT_OPEN_NODE request data itself.
793 void libfs_open_node(libfs_ops_t
*ops
, fs_handle_t fs_handle
, ipc_callid_t rid
,
796 service_id_t service_id
= IPC_GET_ARG1(*request
);
797 fs_index_t index
= IPC_GET_ARG2(*request
);
800 int rc
= ops
->node_get(&fn
, service_id
, index
);
801 on_error(rc
, answer_and_return(rid
, rc
));
804 async_answer_0(rid
, ENOENT
);
808 rc
= ops
->node_open(fn
);
809 aoff64_t size
= ops
->size_get(fn
);
810 async_answer_4(rid
, rc
, LOWER32(size
), UPPER32(size
),
812 (ops
->is_file(fn
) ? L_FILE
: 0) |
813 (ops
->is_directory(fn
) ? L_DIRECTORY
: 0));
815 (void) ops
->node_put(fn
);
818 static FIBRIL_MUTEX_INITIALIZE(instances_mutex
);
819 static LIST_INITIALIZE(instances_list
);
822 service_id_t service_id
;
827 int fs_instance_create(service_id_t service_id
, void *data
)
829 fs_instance_t
*inst
= malloc(sizeof(fs_instance_t
));
833 link_initialize(&inst
->link
);
834 inst
->service_id
= service_id
;
837 fibril_mutex_lock(&instances_mutex
);
838 list_foreach(instances_list
, link
, fs_instance_t
, cur
) {
839 if (cur
->service_id
== service_id
) {
840 fibril_mutex_unlock(&instances_mutex
);
845 /* keep the list sorted */
846 if (cur
->service_id
< service_id
) {
847 list_insert_before(&inst
->link
, &cur
->link
);
848 fibril_mutex_unlock(&instances_mutex
);
852 list_append(&inst
->link
, &instances_list
);
853 fibril_mutex_unlock(&instances_mutex
);
858 int fs_instance_get(service_id_t service_id
, void **idp
)
860 fibril_mutex_lock(&instances_mutex
);
861 list_foreach(instances_list
, link
, fs_instance_t
, inst
) {
862 if (inst
->service_id
== service_id
) {
864 fibril_mutex_unlock(&instances_mutex
);
868 fibril_mutex_unlock(&instances_mutex
);
872 int fs_instance_destroy(service_id_t service_id
)
874 fibril_mutex_lock(&instances_mutex
);
875 list_foreach(instances_list
, link
, fs_instance_t
, inst
) {
876 if (inst
->service_id
== service_id
) {
877 list_remove(&inst
->link
);
878 fibril_mutex_unlock(&instances_mutex
);
883 fibril_mutex_unlock(&instances_mutex
);