source: mainline/uspace/lib/fs/libfs.c@ 9e9b168

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9e9b168 was 9e9b168, checked in by Jiri Zarevucky <zarevucky.jiri@…>, 12 years ago

Fix unlinking.

  • Property mode set to 100644
File size: 23.5 KB
Line 
1/*
2 * Copyright (c) 2009 Jakub Jermar
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 libfs
30 * @{
31 */
32/**
33 * @file
34 * Glue code which is common to all FS implementations.
35 */
36
37#include "libfs.h"
38#include "../../srv/vfs/vfs.h"
39#include <macros.h>
40#include <errno.h>
41#include <async.h>
42#include <as.h>
43#include <assert.h>
44#include <dirent.h>
45#include <mem.h>
46#include <sys/stat.h>
47#include <stdlib.h>
48
49#define on_error(rc, action) \
50 do { \
51 if ((rc) != EOK) \
52 action; \
53 } while (0)
54
55#define combine_rc(rc1, rc2) \
56 ((rc1) == EOK ? (rc2) : (rc1))
57
58#define answer_and_return(rid, rc) \
59 do { \
60 async_answer_0((rid), (rc)); \
61 return; \
62 } while (0)
63
64#define DPRINTF(...)
65
66#define LOG_EXIT(rc) \
67 DPRINTF("Exiting %s() with rc = %d at line %d\n", __FUNC__, rc, __LINE__);
68
69static fs_reg_t reg;
70
71static vfs_out_ops_t *vfs_out_ops = NULL;
72static libfs_ops_t *libfs_ops = NULL;
73
74static void libfs_mount(libfs_ops_t *, fs_handle_t, ipc_callid_t, ipc_call_t *);
75static void libfs_unmount(libfs_ops_t *, ipc_callid_t, ipc_call_t *);
76static void libfs_link(libfs_ops_t *, fs_handle_t, ipc_callid_t,
77 ipc_call_t *);
78static void libfs_lookup(libfs_ops_t *, fs_handle_t, ipc_callid_t,
79 ipc_call_t *);
80static void libfs_stat(libfs_ops_t *, fs_handle_t, ipc_callid_t, ipc_call_t *);
81static void libfs_open_node(libfs_ops_t *, fs_handle_t, ipc_callid_t,
82 ipc_call_t *);
83
84static void vfs_out_mounted(ipc_callid_t rid, ipc_call_t *req)
85{
86 service_id_t service_id = (service_id_t) IPC_GET_ARG1(*req);
87 char *opts;
88 int rc;
89
90 /* Accept the mount options. */
91 rc = async_data_write_accept((void **) &opts, true, 0, 0, 0, NULL);
92 if (rc != EOK) {
93 async_answer_0(rid, rc);
94 return;
95 }
96
97 fs_index_t index;
98 aoff64_t size;
99 unsigned lnkcnt;
100 rc = vfs_out_ops->mounted(service_id, opts, &index, &size, &lnkcnt);
101
102 if (rc == EOK)
103 async_answer_4(rid, EOK, index, LOWER32(size), UPPER32(size),
104 lnkcnt);
105 else
106 async_answer_0(rid, rc);
107
108 free(opts);
109}
110
111static void vfs_out_mount(ipc_callid_t rid, ipc_call_t *req)
112{
113 libfs_mount(libfs_ops, reg.fs_handle, rid, req);
114}
115
116static void vfs_out_unmounted(ipc_callid_t rid, ipc_call_t *req)
117{
118 service_id_t service_id = (service_id_t) IPC_GET_ARG1(*req);
119 int rc;
120
121 rc = vfs_out_ops->unmounted(service_id);
122
123 async_answer_0(rid, rc);
124}
125
126static void vfs_out_unmount(ipc_callid_t rid, ipc_call_t *req)
127{
128
129 libfs_unmount(libfs_ops, rid, req);
130}
131
132static void vfs_out_link(ipc_callid_t rid, ipc_call_t *req)
133{
134 libfs_link(libfs_ops, reg.fs_handle, rid, req);
135}
136
137static void vfs_out_lookup(ipc_callid_t rid, ipc_call_t *req)
138{
139 libfs_lookup(libfs_ops, reg.fs_handle, rid, req);
140}
141
142static void vfs_out_read(ipc_callid_t rid, ipc_call_t *req)
143{
144 service_id_t service_id = (service_id_t) IPC_GET_ARG1(*req);
145 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
146 aoff64_t pos = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*req),
147 IPC_GET_ARG4(*req));
148 size_t rbytes;
149 int rc;
150
151 rc = vfs_out_ops->read(service_id, index, pos, &rbytes);
152
153 if (rc == EOK)
154 async_answer_1(rid, EOK, rbytes);
155 else
156 async_answer_0(rid, rc);
157}
158
159static void vfs_out_write(ipc_callid_t rid, ipc_call_t *req)
160{
161 service_id_t service_id = (service_id_t) IPC_GET_ARG1(*req);
162 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
163 aoff64_t pos = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*req),
164 IPC_GET_ARG4(*req));
165 size_t wbytes;
166 aoff64_t nsize;
167 int rc;
168
169 rc = vfs_out_ops->write(service_id, index, pos, &wbytes, &nsize);
170
171 if (rc == EOK)
172 async_answer_3(rid, EOK, wbytes, LOWER32(nsize), UPPER32(nsize));
173 else
174 async_answer_0(rid, rc);
175}
176
177static void vfs_out_truncate(ipc_callid_t rid, ipc_call_t *req)
178{
179 service_id_t service_id = (service_id_t) IPC_GET_ARG1(*req);
180 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
181 aoff64_t size = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*req),
182 IPC_GET_ARG4(*req));
183 int rc;
184
185 rc = vfs_out_ops->truncate(service_id, index, size);
186
187 async_answer_0(rid, rc);
188}
189
190static void vfs_out_close(ipc_callid_t rid, ipc_call_t *req)
191{
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);
194 int rc;
195
196 rc = vfs_out_ops->close(service_id, index);
197
198 async_answer_0(rid, rc);
199}
200
201static void vfs_out_destroy(ipc_callid_t rid, ipc_call_t *req)
202{
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);
205 int rc;
206
207 rc = vfs_out_ops->destroy(service_id, index);
208
209 async_answer_0(rid, rc);
210}
211
212static void vfs_out_open_node(ipc_callid_t rid, ipc_call_t *req)
213{
214 libfs_open_node(libfs_ops, reg.fs_handle, rid, req);
215}
216
217static void vfs_out_stat(ipc_callid_t rid, ipc_call_t *req)
218{
219 libfs_stat(libfs_ops, reg.fs_handle, rid, req);
220}
221
222static void vfs_out_sync(ipc_callid_t rid, ipc_call_t *req)
223{
224 service_id_t service_id = (service_id_t) IPC_GET_ARG1(*req);
225 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
226 int rc;
227
228 rc = vfs_out_ops->sync(service_id, index);
229
230 async_answer_0(rid, rc);
231}
232
233static void vfs_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
234{
235 if (iid) {
236 /*
237 * This only happens for connections opened by
238 * IPC_M_CONNECT_ME_TO calls as opposed to callback connections
239 * created by IPC_M_CONNECT_TO_ME.
240 */
241 async_answer_0(iid, EOK);
242 }
243
244 while (true) {
245 ipc_call_t call;
246 ipc_callid_t callid = async_get_call(&call);
247
248 if (!IPC_GET_IMETHOD(call))
249 return;
250
251 switch (IPC_GET_IMETHOD(call)) {
252 case VFS_OUT_MOUNTED:
253 vfs_out_mounted(callid, &call);
254 break;
255 case VFS_OUT_MOUNT:
256 vfs_out_mount(callid, &call);
257 break;
258 case VFS_OUT_UNMOUNTED:
259 vfs_out_unmounted(callid, &call);
260 break;
261 case VFS_OUT_UNMOUNT:
262 vfs_out_unmount(callid, &call);
263 break;
264 case VFS_OUT_LINK:
265 vfs_out_link(callid, &call);
266 break;
267 case VFS_OUT_LOOKUP:
268 vfs_out_lookup(callid, &call);
269 break;
270 case VFS_OUT_READ:
271 vfs_out_read(callid, &call);
272 break;
273 case VFS_OUT_WRITE:
274 vfs_out_write(callid, &call);
275 break;
276 case VFS_OUT_TRUNCATE:
277 vfs_out_truncate(callid, &call);
278 break;
279 case VFS_OUT_CLOSE:
280 vfs_out_close(callid, &call);
281 break;
282 case VFS_OUT_DESTROY:
283 vfs_out_destroy(callid, &call);
284 break;
285 case VFS_OUT_OPEN_NODE:
286 vfs_out_open_node(callid, &call);
287 break;
288 case VFS_OUT_STAT:
289 vfs_out_stat(callid, &call);
290 break;
291 case VFS_OUT_SYNC:
292 vfs_out_sync(callid, &call);
293 break;
294 default:
295 async_answer_0(callid, ENOTSUP);
296 break;
297 }
298 }
299}
300
301/** Register file system server.
302 *
303 * This function abstracts away the tedious registration protocol from
304 * file system implementations and lets them to reuse this registration glue
305 * code.
306 *
307 * @param sess Session for communication with VFS.
308 * @param info VFS info structure supplied by the file system
309 * implementation.
310 * @param vops Address of the vfs_out_ops_t structure.
311 * @param lops Address of the libfs_ops_t structure.
312 *
313 * @return EOK on success or a non-zero error code on errror.
314 *
315 */
316int fs_register(async_sess_t *sess, vfs_info_t *info, vfs_out_ops_t *vops,
317 libfs_ops_t *lops)
318{
319 /*
320 * Tell VFS that we are here and want to get registered.
321 * We use the async framework because VFS will answer the request
322 * out-of-order, when it knows that the operation succeeded or failed.
323 */
324
325 async_exch_t *exch = async_exchange_begin(sess);
326
327 ipc_call_t answer;
328 aid_t req = async_send_0(exch, VFS_IN_REGISTER, &answer);
329
330 /*
331 * Send our VFS info structure to VFS.
332 */
333 int rc = async_data_write_start(exch, info, sizeof(*info));
334
335 if (rc != EOK) {
336 async_exchange_end(exch);
337 async_forget(req);
338 return rc;
339 }
340
341 /*
342 * Set VFS_OUT and libfs operations.
343 */
344 vfs_out_ops = vops;
345 libfs_ops = lops;
346
347 /*
348 * Ask VFS for callback connection.
349 */
350 async_connect_to_me(exch, 0, 0, 0, vfs_connection, NULL);
351
352 /*
353 * Request sharing the Path Lookup Buffer with VFS.
354 */
355 rc = async_share_in_start_0_0(exch, PLB_SIZE, (void *) &reg.plb_ro);
356 if (reg.plb_ro == AS_MAP_FAILED) {
357 async_exchange_end(exch);
358 async_forget(req);
359 return ENOMEM;
360 }
361
362 async_exchange_end(exch);
363
364 if (rc) {
365 async_forget(req);
366 return rc;
367 }
368
369 /*
370 * Pick up the answer for the request to the VFS_IN_REQUEST call.
371 */
372 async_wait_for(req, NULL);
373 reg.fs_handle = (int) IPC_GET_ARG1(answer);
374
375 /*
376 * Tell the async framework that other connections are to be handled by
377 * the same connection fibril as well.
378 */
379 async_set_client_connection(vfs_connection);
380
381 return IPC_GET_RETVAL(answer);
382}
383
384void fs_node_initialize(fs_node_t *fn)
385{
386 memset(fn, 0, sizeof(fs_node_t));
387}
388
389void libfs_mount(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
390 ipc_call_t *req)
391{
392 service_id_t mp_service_id = (service_id_t) IPC_GET_ARG1(*req);
393 fs_index_t mp_fs_index = (fs_index_t) IPC_GET_ARG2(*req);
394 fs_handle_t mr_fs_handle = (fs_handle_t) IPC_GET_ARG3(*req);
395 service_id_t mr_service_id = (service_id_t) IPC_GET_ARG4(*req);
396
397 async_sess_t *mountee_sess = async_clone_receive(EXCHANGE_PARALLEL);
398 if (mountee_sess == NULL) {
399 async_answer_0(rid, EINVAL);
400 return;
401 }
402
403 fs_node_t *fn;
404 int res = ops->node_get(&fn, mp_service_id, mp_fs_index);
405 if ((res != EOK) || (!fn)) {
406 async_hangup(mountee_sess);
407 async_data_write_void(combine_rc(res, ENOENT));
408 async_answer_0(rid, combine_rc(res, ENOENT));
409 return;
410 }
411
412 if (fn->mp_data.mp_active) {
413 async_hangup(mountee_sess);
414 (void) ops->node_put(fn);
415 async_data_write_void(EBUSY);
416 async_answer_0(rid, EBUSY);
417 return;
418 }
419
420 async_exch_t *exch = async_exchange_begin(mountee_sess);
421 async_sess_t *sess = async_clone_establish(EXCHANGE_PARALLEL, exch);
422
423 if (!sess) {
424 async_exchange_end(exch);
425 async_hangup(mountee_sess);
426 (void) ops->node_put(fn);
427 async_data_write_void(errno);
428 async_answer_0(rid, errno);
429 return;
430 }
431
432 ipc_call_t answer;
433 int rc = async_data_write_forward_1_1(exch, VFS_OUT_MOUNTED,
434 mr_service_id, &answer);
435 async_exchange_end(exch);
436
437 if (rc == EOK) {
438 fn->mp_data.mp_active = true;
439 fn->mp_data.fs_handle = mr_fs_handle;
440 fn->mp_data.service_id = mr_service_id;
441 fn->mp_data.sess = mountee_sess;
442 }
443
444 /*
445 * Do not release the FS node so that it stays in memory.
446 */
447 async_answer_4(rid, rc, IPC_GET_ARG1(answer), IPC_GET_ARG2(answer),
448 IPC_GET_ARG3(answer), IPC_GET_ARG4(answer));
449}
450
451void libfs_unmount(libfs_ops_t *ops, ipc_callid_t rid, ipc_call_t *req)
452{
453 service_id_t mp_service_id = (service_id_t) IPC_GET_ARG1(*req);
454 fs_index_t mp_fs_index = (fs_index_t) IPC_GET_ARG2(*req);
455 fs_node_t *fn;
456 int res;
457
458 res = ops->node_get(&fn, mp_service_id, mp_fs_index);
459 if ((res != EOK) || (!fn)) {
460 async_answer_0(rid, combine_rc(res, ENOENT));
461 return;
462 }
463
464 /*
465 * We are clearly expecting to find the mount point active.
466 */
467 if (!fn->mp_data.mp_active) {
468 (void) ops->node_put(fn);
469 async_answer_0(rid, EINVAL);
470 return;
471 }
472
473 /*
474 * Tell the mounted file system to unmount.
475 */
476 async_exch_t *exch = async_exchange_begin(fn->mp_data.sess);
477 res = async_req_1_0(exch, VFS_OUT_UNMOUNTED, fn->mp_data.service_id);
478 async_exchange_end(exch);
479
480 /*
481 * If everything went well, perform the clean-up on our side.
482 */
483 if (res == EOK) {
484 async_hangup(fn->mp_data.sess);
485 fn->mp_data.mp_active = false;
486 fn->mp_data.fs_handle = 0;
487 fn->mp_data.service_id = 0;
488 fn->mp_data.sess = NULL;
489
490 /* Drop the reference created in libfs_mount(). */
491 (void) ops->node_put(fn);
492 }
493
494 (void) ops->node_put(fn);
495 async_answer_0(rid, res);
496}
497
498static char plb_get_char(unsigned pos)
499{
500 return reg.plb_ro[pos % PLB_SIZE];
501}
502
503static int plb_get_component(char *dest, unsigned *sz, unsigned *ppos, unsigned last)
504{
505 unsigned pos = *ppos;
506 unsigned size = 0;
507
508 if (pos == last) {
509 *sz = 0;
510 return ERANGE;
511 }
512
513 char c = plb_get_char(pos);
514 if (c == '/') {
515 pos++;
516 }
517
518 for (int i = 0; i <= NAME_MAX; i++) {
519 c = plb_get_char(pos);
520 if (pos == last || c == '/') {
521 dest[i] = 0;
522 *ppos = pos;
523 *sz = size;
524 return EOK;
525 }
526 dest[i] = c;
527 pos++;
528 size++;
529 }
530 return ENAMETOOLONG;
531}
532
533static int receive_fname(char *buffer)
534{
535 size_t size;
536 ipc_callid_t wcall;
537
538 if (!async_data_write_receive(&wcall, &size)) {
539 return ENOENT;
540 }
541 if (size > NAME_MAX + 1) {
542 async_answer_0(wcall, ERANGE);
543 return ERANGE;
544 }
545 return async_data_write_finalize(wcall, buffer, size);
546}
547
548/** Link a file at a path.
549 */
550void libfs_link(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid, ipc_call_t *req)
551{
552 service_id_t parent_sid = IPC_GET_ARG1(*req);
553 fs_index_t parent_index = IPC_GET_ARG2(*req);
554 fs_index_t child_index = IPC_GET_ARG3(*req);
555
556 char component[NAME_MAX + 1];
557 int rc = receive_fname(component);
558 if (rc != EOK) {
559 async_answer_0(rid, rc);
560 return;
561 }
562
563 fs_node_t *parent = NULL;
564 rc = ops->node_get(&parent, parent_sid, parent_index);
565 if (parent == NULL) {
566 async_answer_0(rid, rc == EOK ? EBADF : rc);
567 return;
568 }
569
570 fs_node_t *child = NULL;
571 rc = ops->node_get(&child, parent_sid, child_index);
572 if (child == NULL) {
573 async_answer_0(rid, rc == EOK ? EBADF : rc);
574 ops->node_put(parent);
575 return;
576 }
577
578 rc = ops->link(parent, child, component);
579 ops->node_put(parent);
580 ops->node_put(child);
581 async_answer_0(rid, rc);
582}
583
584/** Lookup VFS triplet by name in the file system name space.
585 *
586 * The path passed in the PLB must be in the canonical file system path format
587 * as returned by the canonify() function.
588 *
589 * @param ops libfs operations structure with function pointers to
590 * file system implementation
591 * @param fs_handle File system handle of the file system where to perform
592 * the lookup.
593 * @param rid Request ID of the VFS_OUT_LOOKUP request.
594 * @param request VFS_OUT_LOOKUP request data itself.
595 *
596 */
597void libfs_lookup(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid, ipc_call_t *req)
598{
599 unsigned first = IPC_GET_ARG1(*req);
600 unsigned len = IPC_GET_ARG2(*req);
601 service_id_t service_id = IPC_GET_ARG3(*req);
602 fs_index_t index = IPC_GET_ARG4(*req);
603 int lflag = IPC_GET_ARG5(*req);
604
605 DPRINTF("Entered libfs_lookup()\n");
606
607 // TODO: Validate flags.
608
609 unsigned next = first;
610 unsigned last = first + len;
611
612 char component[NAME_MAX + 1];
613 int rc;
614
615 fs_node_t *par = NULL;
616 fs_node_t *cur = NULL;
617 fs_node_t *tmp = NULL;
618 unsigned clen = 0;
619
620 if (index == (fs_index_t)-1) {
621 rc = ops->root_get(&cur, service_id);
622 } else {
623 rc = ops->node_get(&cur, service_id, index);
624 }
625 if (rc != EOK) {
626 async_answer_0(rid, rc);
627 LOG_EXIT(rc);
628 goto out;
629 }
630
631 assert(cur != NULL);
632
633 if (cur->mp_data.mp_active) {
634 if (lflag & L_DISABLE_MOUNTS) {
635 async_answer_0(rid, EXDEV);
636 LOG_EXIT(EXDEV);
637 goto out;
638 }
639
640 async_exch_t *exch = async_exchange_begin(cur->mp_data.sess);
641 async_forward_slow(rid, exch, VFS_OUT_LOOKUP, next, last - next,
642 cur->mp_data.service_id, (fs_index_t) -1, lflag, IPC_FF_ROUTE_FROM_ME);
643 async_exchange_end(exch);
644
645 (void) ops->node_put(cur);
646 DPRINTF("Passing to another filesystem instance.\n");
647 return;
648 }
649
650 /* Find the file and its parent. */
651
652 while (next != last) {
653 if (cur == NULL) {
654 async_answer_0(rid, ENOENT);
655 LOG_EXIT(ENOENT);
656 goto out;
657 }
658 if (!ops->is_directory(cur)) {
659 async_answer_0(rid, ENOTDIR);
660 LOG_EXIT(ENOTDIR);
661 goto out;
662 }
663
664 /* Collect the component */
665 rc = plb_get_component(component, &clen, &next, last);
666 assert(rc != ERANGE);
667 if (rc != EOK) {
668 async_answer_0(rid, rc);
669 LOG_EXIT(rc);
670 goto out;
671 }
672
673 if (clen == 0) {
674 /* The path is just "/". */
675 break;
676 }
677
678 assert(component[clen] == 0);
679
680 /* Match the component */
681 rc = ops->match(&tmp, cur, component);
682 if (rc != EOK) {
683 async_answer_0(rid, rc);
684 LOG_EXIT(rc);
685 goto out;
686 }
687
688 /*
689 * If the matching component is a mount point, there are two
690 * legitimate semantics of the lookup operation. The first is
691 * the commonly used one in which the lookup crosses each mount
692 * point into the mounted file system. The second semantics is
693 * used mostly during unmount() and differs from the first one
694 * only in that the last mount point in the looked up path,
695 * which is also its last component, is not crossed.
696 */
697
698 if ((tmp) && (tmp->mp_data.mp_active) &&
699 (!(lflag & L_MP) || (next < last))) {
700
701 if (lflag & L_DISABLE_MOUNTS) {
702 async_answer_0(rid, EXDEV);
703 LOG_EXIT(EXDEV);
704 goto out;
705 }
706
707 async_exch_t *exch = async_exchange_begin(tmp->mp_data.sess);
708 async_forward_slow(rid, exch, VFS_OUT_LOOKUP, next,
709 last - next, tmp->mp_data.service_id, (fs_index_t) -1, lflag,
710 IPC_FF_ROUTE_FROM_ME);
711 async_exchange_end(exch);
712 DPRINTF("Passing to another filesystem instance.\n");
713 goto out;
714 }
715
716 /* Descend one level */
717 if (par) {
718 rc = ops->node_put(par);
719 if (rc != EOK) {
720 async_answer_0(rid, rc);
721 LOG_EXIT(rc);
722 goto out;
723 }
724 }
725
726 par = cur;
727 cur = tmp;
728 tmp = NULL;
729 }
730
731 /* At this point, par is either NULL or a directory.
732 * If cur is NULL, the looked up file does not exist yet.
733 */
734
735 assert(par == NULL || ops->is_directory(par));
736 assert(par != NULL || cur != NULL);
737
738 /* Check for some error conditions. */
739
740 if (cur && (lflag & L_FILE) && (ops->is_directory(cur))) {
741 async_answer_0(rid, EISDIR);
742 LOG_EXIT(EISDIR);
743 goto out;
744 }
745
746 if (cur && (lflag & L_DIRECTORY) && (ops->is_file(cur))) {
747 async_answer_0(rid, ENOTDIR);
748 LOG_EXIT(ENOTDIR);
749 goto out;
750 }
751
752 /* Unlink. */
753
754 if (lflag & L_UNLINK) {
755 if (!cur) {
756 async_answer_0(rid, ENOENT);
757 LOG_EXIT(ENOENT);
758 goto out;
759 }
760 if (!par) {
761 async_answer_0(rid, EINVAL);
762 LOG_EXIT(EINVAL);
763 goto out;
764 }
765
766 rc = ops->unlink(par, cur, component);
767 if (rc == EOK) {
768 aoff64_t size = ops->size_get(cur);
769 async_answer_5(rid, fs_handle, service_id,
770 ops->index_get(cur), LOWER32(size), UPPER32(size),
771 ops->lnkcnt_get(cur));
772 LOG_EXIT(EOK);
773 } else {
774 async_answer_0(rid, rc);
775 LOG_EXIT(rc);
776 }
777 goto out;
778 }
779
780 /* Create. */
781
782 if (lflag & L_CREATE) {
783 if (cur && (lflag & L_EXCLUSIVE)) {
784 async_answer_0(rid, EEXIST);
785 LOG_EXIT(EEXIST);
786 goto out;
787 }
788
789 if (!cur) {
790 rc = ops->create(&cur, service_id, lflag & (L_FILE|L_DIRECTORY));
791 if (rc != EOK) {
792 async_answer_0(rid, rc);
793 LOG_EXIT(rc);
794 goto out;
795 }
796 if (!cur) {
797 async_answer_0(rid, ENOSPC);
798 LOG_EXIT(ENOSPC);
799 goto out;
800 }
801
802 rc = ops->link(par, cur, component);
803 if (rc != EOK) {
804 (void) ops->destroy(cur);
805 cur = NULL;
806 async_answer_0(rid, rc);
807 LOG_EXIT(rc);
808 goto out;
809 }
810 }
811 }
812
813 /* Return. */
814
815 if (!cur) {
816 async_answer_0(rid, ENOENT);
817 LOG_EXIT(ENOENT);
818 goto out;
819 }
820
821 if (lflag & L_OPEN) {
822 rc = ops->node_open(cur);
823 if (rc != EOK) {
824 async_answer_0(rid, rc);
825 LOG_EXIT(rc);
826 goto out;
827 }
828 }
829
830 aoff64_t size = ops->size_get(cur);
831 async_answer_5(rid, fs_handle, service_id,
832 ops->index_get(cur), LOWER32(size), UPPER32(size),
833 ops->lnkcnt_get(cur));
834
835 LOG_EXIT(EOK);
836out:
837 if (par) {
838 (void) ops->node_put(par);
839 }
840
841 if (cur) {
842 (void) ops->node_put(cur);
843 }
844
845 if (tmp) {
846 (void) ops->node_put(tmp);
847 }
848}
849
850void libfs_stat(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
851 ipc_call_t *request)
852{
853 service_id_t service_id = (service_id_t) IPC_GET_ARG1(*request);
854 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
855
856 fs_node_t *fn;
857 int rc = ops->node_get(&fn, service_id, index);
858 on_error(rc, answer_and_return(rid, rc));
859
860 ipc_callid_t callid;
861 size_t size;
862 if ((!async_data_read_receive(&callid, &size)) ||
863 (size != sizeof(struct stat))) {
864 ops->node_put(fn);
865 async_answer_0(callid, EINVAL);
866 async_answer_0(rid, EINVAL);
867 return;
868 }
869
870 struct stat stat;
871 memset(&stat, 0, sizeof(struct stat));
872
873 stat.fs_handle = fs_handle;
874 stat.service_id = service_id;
875 stat.index = index;
876 stat.lnkcnt = ops->lnkcnt_get(fn);
877 stat.is_file = ops->is_file(fn);
878 stat.is_directory = ops->is_directory(fn);
879 stat.size = ops->size_get(fn);
880 stat.service = ops->service_get(fn);
881
882 ops->node_put(fn);
883
884 async_data_read_finalize(callid, &stat, sizeof(struct stat));
885 async_answer_0(rid, EOK);
886}
887
888/** Open VFS triplet.
889 *
890 * @param ops libfs operations structure with function pointers to
891 * file system implementation
892 * @param rid Request ID of the VFS_OUT_OPEN_NODE request.
893 * @param request VFS_OUT_OPEN_NODE request data itself.
894 *
895 */
896void libfs_open_node(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
897 ipc_call_t *request)
898{
899 service_id_t service_id = IPC_GET_ARG1(*request);
900 fs_index_t index = IPC_GET_ARG2(*request);
901
902 fs_node_t *fn;
903 int rc = ops->node_get(&fn, service_id, index);
904 on_error(rc, answer_and_return(rid, rc));
905
906 if (fn == NULL) {
907 async_answer_0(rid, ENOENT);
908 return;
909 }
910
911 rc = ops->node_open(fn);
912 aoff64_t size = ops->size_get(fn);
913 async_answer_4(rid, rc, LOWER32(size), UPPER32(size),
914 ops->lnkcnt_get(fn),
915 (ops->is_file(fn) ? L_FILE : 0) | (ops->is_directory(fn) ? L_DIRECTORY : 0));
916
917 (void) ops->node_put(fn);
918}
919
920static FIBRIL_MUTEX_INITIALIZE(instances_mutex);
921static LIST_INITIALIZE(instances_list);
922
923typedef struct {
924 service_id_t service_id;
925 link_t link;
926 void *data;
927} fs_instance_t;
928
929int fs_instance_create(service_id_t service_id, void *data)
930{
931 fs_instance_t *inst = malloc(sizeof(fs_instance_t));
932 if (!inst)
933 return ENOMEM;
934
935 link_initialize(&inst->link);
936 inst->service_id = service_id;
937 inst->data = data;
938
939 fibril_mutex_lock(&instances_mutex);
940 list_foreach(instances_list, link) {
941 fs_instance_t *cur = list_get_instance(link, fs_instance_t,
942 link);
943
944 if (cur->service_id == service_id) {
945 fibril_mutex_unlock(&instances_mutex);
946 free(inst);
947 return EEXIST;
948 }
949
950 /* keep the list sorted */
951 if (cur->service_id < service_id) {
952 list_insert_before(&inst->link, &cur->link);
953 fibril_mutex_unlock(&instances_mutex);
954 return EOK;
955 }
956 }
957 list_append(&inst->link, &instances_list);
958 fibril_mutex_unlock(&instances_mutex);
959
960 return EOK;
961}
962
963int fs_instance_get(service_id_t service_id, void **idp)
964{
965 fibril_mutex_lock(&instances_mutex);
966 list_foreach(instances_list, link) {
967 fs_instance_t *inst = list_get_instance(link, fs_instance_t,
968 link);
969
970 if (inst->service_id == service_id) {
971 *idp = inst->data;
972 fibril_mutex_unlock(&instances_mutex);
973 return EOK;
974 }
975 }
976 fibril_mutex_unlock(&instances_mutex);
977 return ENOENT;
978}
979
980int fs_instance_destroy(service_id_t service_id)
981{
982 fibril_mutex_lock(&instances_mutex);
983 list_foreach(instances_list, link) {
984 fs_instance_t *inst = list_get_instance(link, fs_instance_t,
985 link);
986
987 if (inst->service_id == service_id) {
988 list_remove(&inst->link);
989 fibril_mutex_unlock(&instances_mutex);
990 free(inst);
991 return EOK;
992 }
993 }
994 fibril_mutex_unlock(&instances_mutex);
995 return ENOENT;
996}
997
998/** @}
999 */
Note: See TracBrowser for help on using the repository browser.