Remove superfluous assert
[helenos.git] / uspace / lib / fs / libfs.c
blob7930ff76fbdfd365cdea803e8f6e141e901effcf
1 /*
2 * Copyright (c) 2009 Jakub Jermar
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
29 /** @addtogroup libfs
30 * @{
32 /**
33 * @file
34 * Glue code which is common to all FS implementations.
37 #include "libfs.h"
38 #include <macros.h>
39 #include <errno.h>
40 #include <async.h>
41 #include <as.h>
42 #include <assert.h>
43 #include <dirent.h>
44 #include <mem.h>
45 #include <str.h>
46 #include <stdlib.h>
47 #include <fibril_synch.h>
48 #include <ipc/vfs.h>
49 #include <vfs/vfs.h>
51 #define on_error(rc, action) \
52 do { \
53 if ((rc) != EOK) \
54 action; \
55 } while (0)
57 #define combine_rc(rc1, rc2) \
58 ((rc1) == EOK ? (rc2) : (rc1))
60 #define answer_and_return(rid, rc) \
61 do { \
62 async_answer_0((rid), (rc)); \
63 return; \
64 } while (0)
66 static fs_reg_t reg;
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,
74 ipc_call_t *);
75 static void libfs_lookup(libfs_ops_t *, fs_handle_t, ipc_callid_t,
76 ipc_call_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,
79 ipc_call_t *);
80 static void libfs_statfs(libfs_ops_t *, fs_handle_t, ipc_callid_t,
81 ipc_call_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);
86 char *opts;
87 int rc;
89 /* Accept the mount options. */
90 rc = async_data_write_accept((void **) &opts, true, 0, 0, 0, NULL);
91 if (rc != EOK) {
92 async_answer_0(rid, rc);
93 return;
96 fs_index_t index;
97 aoff64_t size;
98 unsigned lnkcnt;
99 rc = vfs_out_ops->mounted(service_id, opts, &index, &size, &lnkcnt);
101 if (rc == EOK)
102 async_answer_4(rid, EOK, index, LOWER32(size), UPPER32(size),
103 lnkcnt);
104 else
105 async_answer_0(rid, rc);
107 free(opts);
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);
113 int rc;
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),
135 IPC_GET_ARG4(*req));
136 size_t rbytes;
137 int rc;
139 rc = vfs_out_ops->read(service_id, index, pos, &rbytes);
141 if (rc == EOK)
142 async_answer_1(rid, EOK, rbytes);
143 else
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),
152 IPC_GET_ARG4(*req));
153 size_t wbytes;
154 aoff64_t nsize;
155 int rc;
157 rc = vfs_out_ops->write(service_id, index, pos, &wbytes, &nsize);
159 if (rc == EOK) {
160 async_answer_3(rid, EOK, wbytes, LOWER32(nsize),
161 UPPER32(nsize));
162 } else
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),
171 IPC_GET_ARG4(*req));
172 int rc;
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);
183 int rc;
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);
195 int rc;
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);
201 if (destroy)
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);
221 int rc;
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);
237 int rc;
239 fs_node_t *node = NULL;
240 rc = libfs_ops->node_get(&node, service_id, index);
241 if (rc != EOK)
242 async_answer_0(rid, rc);
243 if (node == NULL)
244 async_answer_0(rid, EINVAL);
246 bool children = false;
247 rc = libfs_ops->has_children(&children, node);
248 libfs_ops->node_put(node);
250 if (rc != EOK)
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)
257 if (iid) {
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);
266 while (true) {
267 ipc_call_t call;
268 ipc_callid_t callid = async_get_call(&call);
270 if (!IPC_GET_IMETHOD(call))
271 return;
273 switch (IPC_GET_IMETHOD(call)) {
274 case VFS_OUT_MOUNTED:
275 vfs_out_mounted(callid, &call);
276 break;
277 case VFS_OUT_UNMOUNTED:
278 vfs_out_unmounted(callid, &call);
279 break;
280 case VFS_OUT_LINK:
281 vfs_out_link(callid, &call);
282 break;
283 case VFS_OUT_LOOKUP:
284 vfs_out_lookup(callid, &call);
285 break;
286 case VFS_OUT_READ:
287 vfs_out_read(callid, &call);
288 break;
289 case VFS_OUT_WRITE:
290 vfs_out_write(callid, &call);
291 break;
292 case VFS_OUT_TRUNCATE:
293 vfs_out_truncate(callid, &call);
294 break;
295 case VFS_OUT_CLOSE:
296 vfs_out_close(callid, &call);
297 break;
298 case VFS_OUT_DESTROY:
299 vfs_out_destroy(callid, &call);
300 break;
301 case VFS_OUT_OPEN_NODE:
302 vfs_out_open_node(callid, &call);
303 break;
304 case VFS_OUT_STAT:
305 vfs_out_stat(callid, &call);
306 break;
307 case VFS_OUT_SYNC:
308 vfs_out_sync(callid, &call);
309 break;
310 case VFS_OUT_STATFS:
311 vfs_out_statfs(callid, &call);
312 break;
313 case VFS_OUT_IS_EMPTY:
314 vfs_out_is_empty(callid, &call);
315 break;
316 default:
317 async_answer_0(callid, ENOTSUP);
318 break;
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
327 * code.
329 * @param sess Session for communication with VFS.
330 * @param info VFS info structure supplied by the file system
331 * implementation.
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,
339 libfs_ops_t *lops)
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);
349 ipc_call_t answer;
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));
357 if (rc != EOK) {
358 async_exchange_end(exch);
359 async_forget(req);
360 return rc;
364 * Set VFS_OUT and libfs operations.
366 vfs_out_ops = vops;
367 libfs_ops = lops;
369 str_cpy(fs_name, sizeof(fs_name), info->name);
372 * Ask VFS for callback connection.
374 port_id_t port;
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 *) &reg.plb_ro);
382 if (reg.plb_ro == AS_MAP_FAILED) {
383 async_exchange_end(exch);
384 async_forget(req);
385 return ENOMEM;
388 async_exchange_end(exch);
390 if (rc) {
391 async_forget(req);
392 return rc;
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,
421 unsigned last)
423 unsigned pos = *ppos;
424 unsigned size = 0;
426 if (pos == last) {
427 *sz = 0;
428 return ERANGE;
431 char c = plb_get_char(pos);
432 if (c == '/')
433 pos++;
435 for (int i = 0; i <= NAME_MAX; i++) {
436 c = plb_get_char(pos);
437 if (pos == last || c == '/') {
438 dest[i] = 0;
439 *ppos = pos;
440 *sz = size;
441 return EOK;
443 dest[i] = c;
444 pos++;
445 size++;
447 return ENAMETOOLONG;
450 static int receive_fname(char *buffer)
452 size_t size;
453 ipc_callid_t wcall;
455 if (!async_data_write_receive(&wcall, &size))
456 return ENOENT;
457 if (size > NAME_MAX + 1) {
458 async_answer_0(wcall, ERANGE);
459 return 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,
467 ipc_call_t *req)
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);
475 if (rc != EOK) {
476 async_answer_0(rid, rc);
477 return;
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);
484 return;
487 fs_node_t *child = NULL;
488 rc = ops->node_get(&child, parent_sid, child_index);
489 if (child == NULL) {
490 async_answer_0(rid, rc == EOK ? EBADF : rc);
491 ops->node_put(parent);
492 return;
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
509 * the lookup.
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,
515 ipc_call_t *req)
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];
529 int rc;
531 fs_node_t *par = NULL;
532 fs_node_t *cur = NULL;
533 fs_node_t *tmp = NULL;
534 unsigned clen = 0;
536 rc = ops->node_get(&cur, service_id, index);
537 if (rc != EOK) {
538 async_answer_0(rid, rc);
539 goto out;
542 assert(cur != NULL);
544 /* Find the file and its parent. */
546 unsigned last_next = 0;
548 while (next != last) {
549 if (cur == NULL) {
550 assert(par != NULL);
551 goto out1;
554 if (!ops->is_directory(cur)) {
555 async_answer_0(rid, ENOTDIR);
556 goto out;
559 last_next = next;
560 /* Collect the component */
561 rc = plb_get_component(component, &clen, &next, last);
562 assert(rc != ERANGE);
563 if (rc != EOK) {
564 async_answer_0(rid, rc);
565 goto out;
568 if (clen == 0) {
569 /* The path is just "/". */
570 break;
573 assert(component[clen] == 0);
575 /* Match the component */
576 rc = ops->match(&tmp, cur, component);
577 if (rc != EOK) {
578 async_answer_0(rid, rc);
579 goto out;
582 /* Descend one level */
583 if (par) {
584 rc = ops->node_put(par);
585 if (rc != EOK) {
586 async_answer_0(rid, rc);
587 goto out;
591 par = cur;
592 cur = tmp;
593 tmp = NULL;
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);
607 goto out;
610 if (cur && (lflag & L_DIRECTORY) && (ops->is_file(cur))) {
611 async_answer_0(rid, ENOTDIR);
612 goto out;
615 /* Unlink. */
617 if (lflag & L_UNLINK) {
618 if (!cur) {
619 async_answer_0(rid, ENOENT);
620 goto out;
622 if (!par) {
623 async_answer_0(rid, EINVAL);
624 goto out;
627 rc = ops->unlink(par, cur, component);
628 if (rc == EOK) {
629 aoff64_t size = ops->size_get(cur);
630 async_answer_5(rid, fs_handle, service_id,
631 ops->index_get(cur),
632 (ops->is_directory(cur) << 16) | last,
633 LOWER32(size), UPPER32(size));
634 } else {
635 async_answer_0(rid, rc);
637 goto out;
640 /* Create. */
642 if (lflag & L_CREATE) {
643 if (cur && (lflag & L_EXCLUSIVE)) {
644 async_answer_0(rid, EEXIST);
645 goto out;
648 if (!cur) {
649 rc = ops->create(&cur, service_id,
650 lflag & (L_FILE | L_DIRECTORY));
651 if (rc != EOK) {
652 async_answer_0(rid, rc);
653 goto out;
655 if (!cur) {
656 async_answer_0(rid, ENOSPC);
657 goto out;
660 rc = ops->link(par, cur, component);
661 if (rc != EOK) {
662 (void) ops->destroy(cur);
663 cur = NULL;
664 async_answer_0(rid, rc);
665 goto out;
670 /* Return. */
671 out1:
672 if (!cur) {
673 async_answer_5(rid, fs_handle, service_id, ops->index_get(par),
674 last_next, -1, true);
675 goto out;
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),
681 UPPER32(size));
683 out:
684 if (par)
685 (void) ops->node_put(par);
687 if (cur)
688 (void) ops->node_put(cur);
690 if (tmp)
691 (void) ops->node_put(tmp);
694 void libfs_stat(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
695 ipc_call_t *request)
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);
700 fs_node_t *fn;
701 int rc = ops->node_get(&fn, service_id, index);
702 on_error(rc, answer_and_return(rid, rc));
704 ipc_callid_t callid;
705 size_t size;
706 if ((!async_data_read_receive(&callid, &size)) ||
707 (size != sizeof(struct stat))) {
708 ops->node_put(fn);
709 async_answer_0(callid, EINVAL);
710 async_answer_0(rid, EINVAL);
711 return;
714 struct stat stat;
715 memset(&stat, 0, sizeof(struct stat));
717 stat.fs_handle = fs_handle;
718 stat.service_id = service_id;
719 stat.index = index;
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);
726 ops->node_put(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,
734 ipc_call_t *request)
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);
739 fs_node_t *fn;
740 int rc = ops->node_get(&fn, service_id, index);
741 on_error(rc, answer_and_return(rid, rc));
743 ipc_callid_t callid;
744 size_t size;
745 if ((!async_data_read_receive(&callid, &size)) ||
746 (size != sizeof(struct statfs))) {
747 goto error;
750 struct statfs st;
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);
757 if (rc != EOK)
758 goto error;
761 if (ops->total_block_count != NULL) {
762 rc = ops->total_block_count(service_id, &st.f_blocks);
763 if (rc != EOK)
764 goto error;
767 if (ops->free_block_count != NULL) {
768 rc = ops->free_block_count(service_id, &st.f_bfree);
769 if (rc != EOK)
770 goto error;
773 ops->node_put(fn);
774 async_data_read_finalize(callid, &st, sizeof(struct statfs));
775 async_answer_0(rid, EOK);
776 return;
778 error:
779 ops->node_put(fn);
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,
794 ipc_call_t *request)
796 service_id_t service_id = IPC_GET_ARG1(*request);
797 fs_index_t index = IPC_GET_ARG2(*request);
799 fs_node_t *fn;
800 int rc = ops->node_get(&fn, service_id, index);
801 on_error(rc, answer_and_return(rid, rc));
803 if (fn == NULL) {
804 async_answer_0(rid, ENOENT);
805 return;
808 rc = ops->node_open(fn);
809 aoff64_t size = ops->size_get(fn);
810 async_answer_4(rid, rc, LOWER32(size), UPPER32(size),
811 ops->lnkcnt_get(fn),
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);
821 typedef struct {
822 service_id_t service_id;
823 link_t link;
824 void *data;
825 } fs_instance_t;
827 int fs_instance_create(service_id_t service_id, void *data)
829 fs_instance_t *inst = malloc(sizeof(fs_instance_t));
830 if (!inst)
831 return ENOMEM;
833 link_initialize(&inst->link);
834 inst->service_id = service_id;
835 inst->data = data;
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);
841 free(inst);
842 return EEXIST;
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);
849 return EOK;
852 list_append(&inst->link, &instances_list);
853 fibril_mutex_unlock(&instances_mutex);
855 return EOK;
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) {
863 *idp = inst->data;
864 fibril_mutex_unlock(&instances_mutex);
865 return EOK;
868 fibril_mutex_unlock(&instances_mutex);
869 return ENOENT;
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);
879 free(inst);
880 return EOK;
883 fibril_mutex_unlock(&instances_mutex);
884 return ENOENT;
887 /** @}