source: mainline/uspace/lib/fs/libfs.c@ 778d26d

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

Relativize rename().

  • 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 unsigned int old_lnkcnt = ops->lnkcnt_get(cur);
767 rc = ops->unlink(par, cur, component);
768 if (rc == EOK) {
769 aoff64_t size = ops->size_get(cur);
770 async_answer_5(rid, fs_handle, service_id,
771 ops->index_get(cur), LOWER32(size), UPPER32(size),
772 old_lnkcnt);
773 LOG_EXIT(EOK);
774 } else {
775 async_answer_0(rid, rc);
776 LOG_EXIT(rc);
777 }
778 goto out;
779 }
780
781 /* Create. */
782
783 if (lflag & L_CREATE) {
784 if (cur && (lflag & L_EXCLUSIVE)) {
785 async_answer_0(rid, EEXIST);
786 LOG_EXIT(EEXIST);
787 goto out;
788 }
789
790 if (!cur) {
791 rc = ops->create(&cur, service_id, lflag & (L_FILE|L_DIRECTORY));
792 if (rc != EOK) {
793 async_answer_0(rid, rc);
794 LOG_EXIT(rc);
795 goto out;
796 }
797 if (!cur) {
798 async_answer_0(rid, ENOSPC);
799 LOG_EXIT(ENOSPC);
800 goto out;
801 }
802
803 rc = ops->link(par, cur, component);
804 if (rc != EOK) {
805 (void) ops->destroy(cur);
806 cur = NULL;
807 async_answer_0(rid, rc);
808 LOG_EXIT(rc);
809 goto out;
810 }
811 }
812 }
813
814 /* Return. */
815
816 if (!cur) {
817 async_answer_0(rid, ENOENT);
818 LOG_EXIT(ENOENT);
819 goto out;
820 }
821
822 if (lflag & L_OPEN) {
823 rc = ops->node_open(cur);
824 if (rc != EOK) {
825 async_answer_0(rid, rc);
826 LOG_EXIT(rc);
827 goto out;
828 }
829 }
830
831 aoff64_t size = ops->size_get(cur);
832 async_answer_5(rid, fs_handle, service_id,
833 ops->index_get(cur), LOWER32(size), UPPER32(size),
834 ops->lnkcnt_get(cur));
835
836 LOG_EXIT(EOK);
837out:
838 if (par) {
839 (void) ops->node_put(par);
840 }
841
842 if (cur) {
843 (void) ops->node_put(cur);
844 }
845
846 if (tmp) {
847 (void) ops->node_put(tmp);
848 }
849}
850
851void libfs_stat(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
852 ipc_call_t *request)
853{
854 service_id_t service_id = (service_id_t) IPC_GET_ARG1(*request);
855 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
856
857 fs_node_t *fn;
858 int rc = ops->node_get(&fn, service_id, index);
859 on_error(rc, answer_and_return(rid, rc));
860
861 ipc_callid_t callid;
862 size_t size;
863 if ((!async_data_read_receive(&callid, &size)) ||
864 (size != sizeof(struct stat))) {
865 ops->node_put(fn);
866 async_answer_0(callid, EINVAL);
867 async_answer_0(rid, EINVAL);
868 return;
869 }
870
871 struct stat stat;
872 memset(&stat, 0, sizeof(struct stat));
873
874 stat.fs_handle = fs_handle;
875 stat.service_id = service_id;
876 stat.index = index;
877 stat.lnkcnt = ops->lnkcnt_get(fn);
878 stat.is_file = ops->is_file(fn);
879 stat.is_directory = ops->is_directory(fn);
880 stat.size = ops->size_get(fn);
881 stat.service = ops->service_get(fn);
882
883 ops->node_put(fn);
884
885 async_data_read_finalize(callid, &stat, sizeof(struct stat));
886 async_answer_0(rid, EOK);
887}
888
889/** Open VFS triplet.
890 *
891 * @param ops libfs operations structure with function pointers to
892 * file system implementation
893 * @param rid Request ID of the VFS_OUT_OPEN_NODE request.
894 * @param request VFS_OUT_OPEN_NODE request data itself.
895 *
896 */
897void libfs_open_node(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
898 ipc_call_t *request)
899{
900 service_id_t service_id = IPC_GET_ARG1(*request);
901 fs_index_t index = IPC_GET_ARG2(*request);
902
903 fs_node_t *fn;
904 int rc = ops->node_get(&fn, service_id, index);
905 on_error(rc, answer_and_return(rid, rc));
906
907 if (fn == NULL) {
908 async_answer_0(rid, ENOENT);
909 return;
910 }
911
912 rc = ops->node_open(fn);
913 aoff64_t size = ops->size_get(fn);
914 async_answer_4(rid, rc, LOWER32(size), UPPER32(size),
915 ops->lnkcnt_get(fn),
916 (ops->is_file(fn) ? L_FILE : 0) | (ops->is_directory(fn) ? L_DIRECTORY : 0));
917
918 (void) ops->node_put(fn);
919}
920
921static FIBRIL_MUTEX_INITIALIZE(instances_mutex);
922static LIST_INITIALIZE(instances_list);
923
924typedef struct {
925 service_id_t service_id;
926 link_t link;
927 void *data;
928} fs_instance_t;
929
930int fs_instance_create(service_id_t service_id, void *data)
931{
932 fs_instance_t *inst = malloc(sizeof(fs_instance_t));
933 if (!inst)
934 return ENOMEM;
935
936 link_initialize(&inst->link);
937 inst->service_id = service_id;
938 inst->data = data;
939
940 fibril_mutex_lock(&instances_mutex);
941 list_foreach(instances_list, link) {
942 fs_instance_t *cur = list_get_instance(link, fs_instance_t,
943 link);
944
945 if (cur->service_id == service_id) {
946 fibril_mutex_unlock(&instances_mutex);
947 free(inst);
948 return EEXIST;
949 }
950
951 /* keep the list sorted */
952 if (cur->service_id < service_id) {
953 list_insert_before(&inst->link, &cur->link);
954 fibril_mutex_unlock(&instances_mutex);
955 return EOK;
956 }
957 }
958 list_append(&inst->link, &instances_list);
959 fibril_mutex_unlock(&instances_mutex);
960
961 return EOK;
962}
963
964int fs_instance_get(service_id_t service_id, void **idp)
965{
966 fibril_mutex_lock(&instances_mutex);
967 list_foreach(instances_list, link) {
968 fs_instance_t *inst = list_get_instance(link, fs_instance_t,
969 link);
970
971 if (inst->service_id == service_id) {
972 *idp = inst->data;
973 fibril_mutex_unlock(&instances_mutex);
974 return EOK;
975 }
976 }
977 fibril_mutex_unlock(&instances_mutex);
978 return ENOENT;
979}
980
981int fs_instance_destroy(service_id_t service_id)
982{
983 fibril_mutex_lock(&instances_mutex);
984 list_foreach(instances_list, link) {
985 fs_instance_t *inst = list_get_instance(link, fs_instance_t,
986 link);
987
988 if (inst->service_id == service_id) {
989 list_remove(&inst->link);
990 fibril_mutex_unlock(&instances_mutex);
991 free(inst);
992 return EOK;
993 }
994 }
995 fibril_mutex_unlock(&instances_mutex);
996 return ENOENT;
997}
998
999/** @}
1000 */
Note: See TracBrowser for help on using the repository browser.