source: mainline/uspace/srv/vfs/vfs_ops.c@ 7f5e070

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7f5e070 was 7f5e070, checked in by Jakub Jermar <jakub@…>, 16 years ago

Add dummy implementation of VFS_IN_UNMOUNT.

  • Property mode set to 100644
File size: 33.4 KB
Line 
1/*
2 * Copyright (c) 2008 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 fs
30 * @{
31 */
32
33/**
34 * @file vfs_ops.c
35 * @brief Operations that VFS offers to its clients.
36 */
37
38#include "vfs.h"
39#include <ipc/ipc.h>
40#include <async.h>
41#include <errno.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <bool.h>
46#include <fibril_synch.h>
47#include <adt/list.h>
48#include <unistd.h>
49#include <ctype.h>
50#include <fcntl.h>
51#include <assert.h>
52#include <vfs/canonify.h>
53
54/* Forward declarations of static functions. */
55static int vfs_truncate_internal(fs_handle_t, dev_handle_t, fs_index_t, size_t);
56
57/**
58 * This rwlock prevents the race between a triplet-to-VFS-node resolution and a
59 * concurrent VFS operation which modifies the file system namespace.
60 */
61FIBRIL_RWLOCK_INITIALIZE(namespace_rwlock);
62
63vfs_pair_t rootfs = {
64 .fs_handle = 0,
65 .dev_handle = 0
66};
67
68static void vfs_mount_internal(ipc_callid_t rid, dev_handle_t dev_handle,
69 fs_handle_t fs_handle, char *mp, char *opts)
70{
71 vfs_lookup_res_t mp_res;
72 vfs_lookup_res_t mr_res;
73 vfs_node_t *mp_node = NULL;
74 vfs_node_t *mr_node;
75 fs_index_t rindex;
76 size_t rsize;
77 unsigned rlnkcnt;
78 ipcarg_t rc;
79 int phone;
80 aid_t msg;
81 ipc_call_t answer;
82
83 /* Resolve the path to the mountpoint. */
84 fibril_rwlock_write_lock(&namespace_rwlock);
85 if (rootfs.fs_handle) {
86 /* We already have the root FS. */
87 if (str_cmp(mp, "/") == 0) {
88 /* Trying to mount root FS over root FS */
89 fibril_rwlock_write_unlock(&namespace_rwlock);
90 ipc_answer_0(rid, EBUSY);
91 return;
92 }
93
94 rc = vfs_lookup_internal(mp, L_DIRECTORY, &mp_res, NULL);
95 if (rc != EOK) {
96 /* The lookup failed for some reason. */
97 fibril_rwlock_write_unlock(&namespace_rwlock);
98 ipc_answer_0(rid, rc);
99 return;
100 }
101
102 mp_node = vfs_node_get(&mp_res);
103 if (!mp_node) {
104 fibril_rwlock_write_unlock(&namespace_rwlock);
105 ipc_answer_0(rid, ENOMEM);
106 return;
107 }
108
109 /*
110 * Now we hold a reference to mp_node.
111 * It will be dropped upon the corresponding VFS_IN_UNMOUNT.
112 * This prevents the mount point from being deleted.
113 */
114 } else {
115 /* We still don't have the root file system mounted. */
116 if (str_cmp(mp, "/") == 0) {
117 /*
118 * For this simple, but important case,
119 * we are almost done.
120 */
121
122 /* Tell the mountee that it is being mounted. */
123 phone = vfs_grab_phone(fs_handle);
124 msg = async_send_1(phone, VFS_OUT_MOUNTED,
125 (ipcarg_t) dev_handle, &answer);
126 /* send the mount options */
127 rc = async_data_write_start(phone, (void *)opts,
128 str_size(opts));
129 if (rc != EOK) {
130 async_wait_for(msg, NULL);
131 vfs_release_phone(phone);
132 fibril_rwlock_write_unlock(&namespace_rwlock);
133 ipc_answer_0(rid, rc);
134 return;
135 }
136 async_wait_for(msg, &rc);
137 vfs_release_phone(phone);
138
139 if (rc != EOK) {
140 fibril_rwlock_write_unlock(&namespace_rwlock);
141 ipc_answer_0(rid, rc);
142 return;
143 }
144
145 rindex = (fs_index_t) IPC_GET_ARG1(answer);
146 rsize = (size_t) IPC_GET_ARG2(answer);
147 rlnkcnt = (unsigned) IPC_GET_ARG3(answer);
148
149 mr_res.triplet.fs_handle = fs_handle;
150 mr_res.triplet.dev_handle = dev_handle;
151 mr_res.triplet.index = rindex;
152 mr_res.size = rsize;
153 mr_res.lnkcnt = rlnkcnt;
154 mr_res.type = VFS_NODE_DIRECTORY;
155
156 rootfs.fs_handle = fs_handle;
157 rootfs.dev_handle = dev_handle;
158
159 /* Add reference to the mounted root. */
160 mr_node = vfs_node_get(&mr_res);
161 assert(mr_node);
162
163 fibril_rwlock_write_unlock(&namespace_rwlock);
164 ipc_answer_0(rid, rc);
165 return;
166 } else {
167 /*
168 * We can't resolve this without the root filesystem
169 * being mounted first.
170 */
171 fibril_rwlock_write_unlock(&namespace_rwlock);
172 ipc_answer_0(rid, ENOENT);
173 return;
174 }
175 }
176
177 /*
178 * At this point, we have all necessary pieces: file system and device
179 * handles, and we know the mount point VFS node.
180 */
181
182 int mountee_phone = vfs_grab_phone(fs_handle);
183 assert(mountee_phone >= 0);
184
185 phone = vfs_grab_phone(mp_res.triplet.fs_handle);
186 msg = async_send_4(phone, VFS_OUT_MOUNT,
187 (ipcarg_t) mp_res.triplet.dev_handle,
188 (ipcarg_t) mp_res.triplet.index,
189 (ipcarg_t) fs_handle,
190 (ipcarg_t) dev_handle, &answer);
191
192 /* send connection */
193 rc = async_req_1_0(phone, IPC_M_CONNECTION_CLONE, mountee_phone);
194 if (rc != EOK) {
195 async_wait_for(msg, NULL);
196 vfs_release_phone(mountee_phone);
197 vfs_release_phone(phone);
198 /* Mount failed, drop reference to mp_node. */
199 if (mp_node)
200 vfs_node_put(mp_node);
201 ipc_answer_0(rid, rc);
202 fibril_rwlock_write_unlock(&namespace_rwlock);
203 return;
204 }
205
206 vfs_release_phone(mountee_phone);
207
208 /* send the mount options */
209 rc = async_data_write_start(phone, (void *)opts, str_size(opts));
210 if (rc != EOK) {
211 async_wait_for(msg, NULL);
212 vfs_release_phone(phone);
213 /* Mount failed, drop reference to mp_node. */
214 if (mp_node)
215 vfs_node_put(mp_node);
216 fibril_rwlock_write_unlock(&namespace_rwlock);
217 ipc_answer_0(rid, rc);
218 return;
219 }
220 async_wait_for(msg, &rc);
221 vfs_release_phone(phone);
222
223 if (rc == EOK) {
224 rindex = (fs_index_t) IPC_GET_ARG1(answer);
225 rsize = (size_t) IPC_GET_ARG2(answer);
226 rlnkcnt = (unsigned) IPC_GET_ARG3(answer);
227
228 mr_res.triplet.fs_handle = fs_handle;
229 mr_res.triplet.dev_handle = dev_handle;
230 mr_res.triplet.index = rindex;
231 mr_res.size = rsize;
232 mr_res.lnkcnt = rlnkcnt;
233 mr_res.type = VFS_NODE_DIRECTORY;
234
235 /* Add reference to the mounted root. */
236 mr_node = vfs_node_get(&mr_res);
237 assert(mr_node);
238 } else {
239 /* Mount failed, drop reference to mp_node. */
240 if (mp_node)
241 vfs_node_put(mp_node);
242 }
243
244 ipc_answer_0(rid, rc);
245 fibril_rwlock_write_unlock(&namespace_rwlock);
246}
247
248void vfs_mount(ipc_callid_t rid, ipc_call_t *request)
249{
250 /*
251 * We expect the library to do the device-name to device-handle
252 * translation for us, thus the device handle will arrive as ARG1
253 * in the request.
254 */
255 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
256
257 /*
258 * Mount flags are passed as ARG2.
259 */
260 unsigned int flags = (unsigned int) IPC_GET_ARG2(*request);
261
262 /*
263 * For now, don't make use of ARG3, but it can be used to
264 * carry mount options in the future.
265 */
266
267 /* We want the client to send us the mount point. */
268 ipc_callid_t callid;
269 size_t size;
270 if (!async_data_write_receive(&callid, &size)) {
271 ipc_answer_0(callid, EINVAL);
272 ipc_answer_0(rid, EINVAL);
273 return;
274 }
275
276 /* Check whether size is reasonable wrt. the mount point. */
277 if ((size < 1) || (size > MAX_PATH_LEN)) {
278 ipc_answer_0(callid, EINVAL);
279 ipc_answer_0(rid, EINVAL);
280 return;
281 }
282
283 /* Allocate buffer for the mount point data being received. */
284 char *mp = malloc(size + 1);
285 if (!mp) {
286 ipc_answer_0(callid, ENOMEM);
287 ipc_answer_0(rid, ENOMEM);
288 return;
289 }
290
291 /* Deliver the mount point. */
292 ipcarg_t retval = async_data_write_finalize(callid, mp, size);
293 if (retval != EOK) {
294 ipc_answer_0(rid, retval);
295 free(mp);
296 return;
297 }
298 mp[size] = '\0';
299
300 /* Now we expect to receive the mount options. */
301 if (!async_data_write_receive(&callid, &size)) {
302 ipc_answer_0(callid, EINVAL);
303 ipc_answer_0(rid, EINVAL);
304 free(mp);
305 return;
306 }
307
308 /* Check the offered options size. */
309 if (size > MAX_MNTOPTS_LEN) {
310 ipc_answer_0(callid, EINVAL);
311 ipc_answer_0(rid, EINVAL);
312 free(mp);
313 return;
314 }
315
316 /* Allocate buffer for the mount options. */
317 char *opts = (char *) malloc(size + 1);
318 if (!opts) {
319 ipc_answer_0(callid, ENOMEM);
320 ipc_answer_0(rid, ENOMEM);
321 free(mp);
322 return;
323 }
324
325 /* Deliver the mount options. */
326 retval = async_data_write_finalize(callid, opts, size);
327 if (retval != EOK) {
328 ipc_answer_0(rid, retval);
329 free(mp);
330 free(opts);
331 return;
332 }
333 opts[size] = '\0';
334
335 /*
336 * Now, we expect the client to send us data with the name of the file
337 * system.
338 */
339 if (!async_data_write_receive(&callid, &size)) {
340 ipc_answer_0(callid, EINVAL);
341 ipc_answer_0(rid, EINVAL);
342 free(mp);
343 free(opts);
344 return;
345 }
346
347 /*
348 * Don't receive more than is necessary for storing a full file system
349 * name.
350 */
351 if ((size < 1) || (size > FS_NAME_MAXLEN)) {
352 ipc_answer_0(callid, EINVAL);
353 ipc_answer_0(rid, EINVAL);
354 free(mp);
355 free(opts);
356 return;
357 }
358
359 /*
360 * Allocate buffer for file system name.
361 */
362 char *fs_name = (char *) malloc(size + 1);
363 if (fs_name == NULL) {
364 ipc_answer_0(callid, ENOMEM);
365 ipc_answer_0(rid, ENOMEM);
366 free(mp);
367 free(opts);
368 return;
369 }
370
371 /* Deliver the file system name. */
372 retval = async_data_write_finalize(callid, fs_name, size);
373 if (retval != EOK) {
374 ipc_answer_0(rid, retval);
375 free(mp);
376 free(opts);
377 free(fs_name);
378 return;
379 }
380 fs_name[size] = '\0';
381
382 /*
383 * Wait for IPC_M_PING so that we can return an error if we don't know
384 * fs_name.
385 */
386 ipc_call_t data;
387 callid = async_get_call(&data);
388 if (IPC_GET_METHOD(data) != IPC_M_PING) {
389 ipc_answer_0(callid, ENOTSUP);
390 ipc_answer_0(rid, ENOTSUP);
391 free(mp);
392 free(opts);
393 free(fs_name);
394 return;
395 }
396
397 /*
398 * Check if we know a file system with the same name as is in fs_name.
399 * This will also give us its file system handle.
400 */
401 fibril_mutex_lock(&fs_head_lock);
402 fs_handle_t fs_handle;
403recheck:
404 fs_handle = fs_name_to_handle(fs_name, false);
405 if (!fs_handle) {
406 if (flags & IPC_FLAG_BLOCKING) {
407 fibril_condvar_wait(&fs_head_cv, &fs_head_lock);
408 goto recheck;
409 }
410
411 fibril_mutex_unlock(&fs_head_lock);
412 ipc_answer_0(callid, ENOENT);
413 ipc_answer_0(rid, ENOENT);
414 free(mp);
415 free(fs_name);
416 free(opts);
417 return;
418 }
419 fibril_mutex_unlock(&fs_head_lock);
420
421 /* Acknowledge that we know fs_name. */
422 ipc_answer_0(callid, EOK);
423
424 /* Do the mount */
425 vfs_mount_internal(rid, dev_handle, fs_handle, mp, opts);
426 free(mp);
427 free(fs_name);
428 free(opts);
429}
430
431void vfs_unmount(ipc_callid_t rid, ipc_call_t *request)
432{
433 ipc_answer_0(rid, ENOTSUP);
434}
435
436void vfs_open(ipc_callid_t rid, ipc_call_t *request)
437{
438 if (!vfs_files_init()) {
439 ipc_answer_0(rid, ENOMEM);
440 return;
441 }
442
443 /*
444 * The POSIX interface is open(path, oflag, mode).
445 * We can receive oflags and mode along with the VFS_IN_OPEN call;
446 * the path will need to arrive in another call.
447 *
448 * We also receive one private, non-POSIX set of flags called lflag
449 * used to pass information to vfs_lookup_internal().
450 */
451 int lflag = IPC_GET_ARG1(*request);
452 int oflag = IPC_GET_ARG2(*request);
453 int mode = IPC_GET_ARG3(*request);
454 size_t len;
455
456 /* Ignore mode for now. */
457 (void) mode;
458
459 /*
460 * Make sure that we are called with exactly one of L_FILE and
461 * L_DIRECTORY. Make sure that the user does not pass L_OPEN.
462 */
463 if (((lflag & (L_FILE | L_DIRECTORY)) == 0) ||
464 ((lflag & (L_FILE | L_DIRECTORY)) == (L_FILE | L_DIRECTORY)) ||
465 ((lflag & L_OPEN) != 0)) {
466 ipc_answer_0(rid, EINVAL);
467 return;
468 }
469
470 if (oflag & O_CREAT)
471 lflag |= L_CREATE;
472 if (oflag & O_EXCL)
473 lflag |= L_EXCLUSIVE;
474
475 ipc_callid_t callid;
476 if (!async_data_write_receive(&callid, &len)) {
477 ipc_answer_0(callid, EINVAL);
478 ipc_answer_0(rid, EINVAL);
479 return;
480 }
481
482 char *path = malloc(len + 1);
483 if (!path) {
484 ipc_answer_0(callid, ENOMEM);
485 ipc_answer_0(rid, ENOMEM);
486 return;
487 }
488
489 int rc;
490 if ((rc = async_data_write_finalize(callid, path, len))) {
491 ipc_answer_0(rid, rc);
492 free(path);
493 return;
494 }
495 path[len] = '\0';
496
497 /*
498 * Avoid the race condition in which the file can be deleted before we
499 * find/create-and-lock the VFS node corresponding to the looked-up
500 * triplet.
501 */
502 if (lflag & L_CREATE)
503 fibril_rwlock_write_lock(&namespace_rwlock);
504 else
505 fibril_rwlock_read_lock(&namespace_rwlock);
506
507 /* The path is now populated and we can call vfs_lookup_internal(). */
508 vfs_lookup_res_t lr;
509 rc = vfs_lookup_internal(path, lflag | L_OPEN, &lr, NULL);
510 if (rc != EOK) {
511 if (lflag & L_CREATE)
512 fibril_rwlock_write_unlock(&namespace_rwlock);
513 else
514 fibril_rwlock_read_unlock(&namespace_rwlock);
515 ipc_answer_0(rid, rc);
516 free(path);
517 return;
518 }
519
520 /* Path is no longer needed. */
521 free(path);
522
523 vfs_node_t *node = vfs_node_get(&lr);
524 if (lflag & L_CREATE)
525 fibril_rwlock_write_unlock(&namespace_rwlock);
526 else
527 fibril_rwlock_read_unlock(&namespace_rwlock);
528
529 /* Truncate the file if requested and if necessary. */
530 if (oflag & O_TRUNC) {
531 fibril_rwlock_write_lock(&node->contents_rwlock);
532 if (node->size) {
533 rc = vfs_truncate_internal(node->fs_handle,
534 node->dev_handle, node->index, 0);
535 if (rc) {
536 fibril_rwlock_write_unlock(&node->contents_rwlock);
537 vfs_node_put(node);
538 ipc_answer_0(rid, rc);
539 return;
540 }
541 node->size = 0;
542 }
543 fibril_rwlock_write_unlock(&node->contents_rwlock);
544 }
545
546 /*
547 * Get ourselves a file descriptor and the corresponding vfs_file_t
548 * structure.
549 */
550 int fd = vfs_fd_alloc((oflag & O_DESC) != 0);
551 if (fd < 0) {
552 vfs_node_put(node);
553 ipc_answer_0(rid, fd);
554 return;
555 }
556 vfs_file_t *file = vfs_file_get(fd);
557 file->node = node;
558 if (oflag & O_APPEND)
559 file->append = true;
560
561 /*
562 * The following increase in reference count is for the fact that the
563 * file is being opened and that a file structure is pointing to it.
564 * It is necessary so that the file will not disappear when
565 * vfs_node_put() is called. The reference will be dropped by the
566 * respective VFS_IN_CLOSE.
567 */
568 vfs_node_addref(node);
569 vfs_node_put(node);
570
571 /* Success! Return the new file descriptor to the client. */
572 ipc_answer_1(rid, EOK, fd);
573}
574
575void vfs_open_node(ipc_callid_t rid, ipc_call_t *request)
576{
577 // FIXME: check for sanity of the supplied fs, dev and index
578
579 if (!vfs_files_init()) {
580 ipc_answer_0(rid, ENOMEM);
581 return;
582 }
583
584 /*
585 * The interface is open_node(fs, dev, index, oflag).
586 */
587 vfs_lookup_res_t lr;
588
589 lr.triplet.fs_handle = IPC_GET_ARG1(*request);
590 lr.triplet.dev_handle = IPC_GET_ARG2(*request);
591 lr.triplet.index = IPC_GET_ARG3(*request);
592 int oflag = IPC_GET_ARG4(*request);
593
594 fibril_rwlock_read_lock(&namespace_rwlock);
595
596 int rc = vfs_open_node_internal(&lr);
597 if (rc != EOK) {
598 fibril_rwlock_read_unlock(&namespace_rwlock);
599 ipc_answer_0(rid, rc);
600 return;
601 }
602
603 vfs_node_t *node = vfs_node_get(&lr);
604 fibril_rwlock_read_unlock(&namespace_rwlock);
605
606 /* Truncate the file if requested and if necessary. */
607 if (oflag & O_TRUNC) {
608 fibril_rwlock_write_lock(&node->contents_rwlock);
609 if (node->size) {
610 rc = vfs_truncate_internal(node->fs_handle,
611 node->dev_handle, node->index, 0);
612 if (rc) {
613 fibril_rwlock_write_unlock(&node->contents_rwlock);
614 vfs_node_put(node);
615 ipc_answer_0(rid, rc);
616 return;
617 }
618 node->size = 0;
619 }
620 fibril_rwlock_write_unlock(&node->contents_rwlock);
621 }
622
623 /*
624 * Get ourselves a file descriptor and the corresponding vfs_file_t
625 * structure.
626 */
627 int fd = vfs_fd_alloc((oflag & O_DESC) != 0);
628 if (fd < 0) {
629 vfs_node_put(node);
630 ipc_answer_0(rid, fd);
631 return;
632 }
633 vfs_file_t *file = vfs_file_get(fd);
634 file->node = node;
635 if (oflag & O_APPEND)
636 file->append = true;
637
638 /*
639 * The following increase in reference count is for the fact that the
640 * file is being opened and that a file structure is pointing to it.
641 * It is necessary so that the file will not disappear when
642 * vfs_node_put() is called. The reference will be dropped by the
643 * respective VFS_IN_CLOSE.
644 */
645 vfs_node_addref(node);
646 vfs_node_put(node);
647
648 /* Success! Return the new file descriptor to the client. */
649 ipc_answer_1(rid, EOK, fd);
650}
651
652void vfs_sync(ipc_callid_t rid, ipc_call_t *request)
653{
654 int fd = IPC_GET_ARG1(*request);
655
656 /* Lookup the file structure corresponding to the file descriptor. */
657 vfs_file_t *file = vfs_file_get(fd);
658 if (!file) {
659 ipc_answer_0(rid, ENOENT);
660 return;
661 }
662
663 /*
664 * Lock the open file structure so that no other thread can manipulate
665 * the same open file at a time.
666 */
667 fibril_mutex_lock(&file->lock);
668 int fs_phone = vfs_grab_phone(file->node->fs_handle);
669
670 /* Make a VFS_OUT_SYMC request at the destination FS server. */
671 aid_t msg;
672 ipc_call_t answer;
673 msg = async_send_2(fs_phone, VFS_OUT_SYNC, file->node->dev_handle,
674 file->node->index, &answer);
675
676 /* Wait for reply from the FS server. */
677 ipcarg_t rc;
678 async_wait_for(msg, &rc);
679
680 vfs_release_phone(fs_phone);
681 fibril_mutex_unlock(&file->lock);
682
683 ipc_answer_0(rid, rc);
684}
685
686static int vfs_close_internal(vfs_file_t *file)
687{
688 /*
689 * Lock the open file structure so that no other thread can manipulate
690 * the same open file at a time.
691 */
692 fibril_mutex_lock(&file->lock);
693
694 if (file->refcnt <= 1) {
695 /* Only close the file on the destination FS server
696 if there are no more file descriptors (except the
697 present one) pointing to this file. */
698
699 int fs_phone = vfs_grab_phone(file->node->fs_handle);
700
701 /* Make a VFS_OUT_CLOSE request at the destination FS server. */
702 aid_t msg;
703 ipc_call_t answer;
704 msg = async_send_2(fs_phone, VFS_OUT_CLOSE, file->node->dev_handle,
705 file->node->index, &answer);
706
707 /* Wait for reply from the FS server. */
708 ipcarg_t rc;
709 async_wait_for(msg, &rc);
710
711 vfs_release_phone(fs_phone);
712 fibril_mutex_unlock(&file->lock);
713
714 return IPC_GET_ARG1(answer);
715 }
716
717 fibril_mutex_unlock(&file->lock);
718 return EOK;
719}
720
721void vfs_close(ipc_callid_t rid, ipc_call_t *request)
722{
723 int fd = IPC_GET_ARG1(*request);
724
725 /* Lookup the file structure corresponding to the file descriptor. */
726 vfs_file_t *file = vfs_file_get(fd);
727 if (!file) {
728 ipc_answer_0(rid, ENOENT);
729 return;
730 }
731
732 int ret = vfs_close_internal(file);
733 if (ret != EOK)
734 ipc_answer_0(rid, ret);
735
736 ret = vfs_fd_free(fd);
737 ipc_answer_0(rid, ret);
738}
739
740static void vfs_rdwr(ipc_callid_t rid, ipc_call_t *request, bool read)
741{
742
743 /*
744 * The following code strongly depends on the fact that the files data
745 * structure can be only accessed by a single fibril and all file
746 * operations are serialized (i.e. the reads and writes cannot
747 * interleave and a file cannot be closed while it is being read).
748 *
749 * Additional synchronization needs to be added once the table of
750 * open files supports parallel access!
751 */
752
753 int fd = IPC_GET_ARG1(*request);
754
755 /* Lookup the file structure corresponding to the file descriptor. */
756 vfs_file_t *file = vfs_file_get(fd);
757 if (!file) {
758 ipc_answer_0(rid, ENOENT);
759 return;
760 }
761
762 /*
763 * Now we need to receive a call with client's
764 * IPC_M_DATA_READ/IPC_M_DATA_WRITE request.
765 */
766 ipc_callid_t callid;
767 int res;
768 if (read)
769 res = async_data_read_receive(&callid, NULL);
770 else
771 res = async_data_write_receive(&callid, NULL);
772 if (!res) {
773 ipc_answer_0(callid, EINVAL);
774 ipc_answer_0(rid, EINVAL);
775 return;
776 }
777
778 /*
779 * Lock the open file structure so that no other thread can manipulate
780 * the same open file at a time.
781 */
782 fibril_mutex_lock(&file->lock);
783
784 /*
785 * Lock the file's node so that no other client can read/write to it at
786 * the same time.
787 */
788 if (read)
789 fibril_rwlock_read_lock(&file->node->contents_rwlock);
790 else
791 fibril_rwlock_write_lock(&file->node->contents_rwlock);
792
793 if (file->node->type == VFS_NODE_DIRECTORY) {
794 /*
795 * Make sure that no one is modifying the namespace
796 * while we are in readdir().
797 */
798 assert(read);
799 fibril_rwlock_read_lock(&namespace_rwlock);
800 }
801
802 int fs_phone = vfs_grab_phone(file->node->fs_handle);
803
804 /* Make a VFS_READ/VFS_WRITE request at the destination FS server. */
805 aid_t msg;
806 ipc_call_t answer;
807 if (!read && file->append)
808 file->pos = file->node->size;
809 msg = async_send_3(fs_phone, read ? VFS_OUT_READ : VFS_OUT_WRITE,
810 file->node->dev_handle, file->node->index, file->pos, &answer);
811
812 /*
813 * Forward the IPC_M_DATA_READ/IPC_M_DATA_WRITE request to the
814 * destination FS server. The call will be routed as if sent by
815 * ourselves. Note that call arguments are immutable in this case so we
816 * don't have to bother.
817 */
818 ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
819
820 /* Wait for reply from the FS server. */
821 ipcarg_t rc;
822 async_wait_for(msg, &rc);
823
824 vfs_release_phone(fs_phone);
825
826 size_t bytes = IPC_GET_ARG1(answer);
827
828 if (file->node->type == VFS_NODE_DIRECTORY)
829 fibril_rwlock_read_unlock(&namespace_rwlock);
830
831 /* Unlock the VFS node. */
832 if (read)
833 fibril_rwlock_read_unlock(&file->node->contents_rwlock);
834 else {
835 /* Update the cached version of node's size. */
836 if (rc == EOK)
837 file->node->size = IPC_GET_ARG2(answer);
838 fibril_rwlock_write_unlock(&file->node->contents_rwlock);
839 }
840
841 /* Update the position pointer and unlock the open file. */
842 if (rc == EOK)
843 file->pos += bytes;
844 fibril_mutex_unlock(&file->lock);
845
846 /*
847 * FS server's reply is the final result of the whole operation we
848 * return to the client.
849 */
850 ipc_answer_1(rid, rc, bytes);
851}
852
853void vfs_read(ipc_callid_t rid, ipc_call_t *request)
854{
855 vfs_rdwr(rid, request, true);
856}
857
858void vfs_write(ipc_callid_t rid, ipc_call_t *request)
859{
860 vfs_rdwr(rid, request, false);
861}
862
863void vfs_seek(ipc_callid_t rid, ipc_call_t *request)
864{
865 int fd = (int) IPC_GET_ARG1(*request);
866 off_t off = (off_t) IPC_GET_ARG2(*request);
867 int whence = (int) IPC_GET_ARG3(*request);
868
869
870 /* Lookup the file structure corresponding to the file descriptor. */
871 vfs_file_t *file = vfs_file_get(fd);
872 if (!file) {
873 ipc_answer_0(rid, ENOENT);
874 return;
875 }
876
877 off_t newpos;
878 fibril_mutex_lock(&file->lock);
879 if (whence == SEEK_SET) {
880 file->pos = off;
881 fibril_mutex_unlock(&file->lock);
882 ipc_answer_1(rid, EOK, off);
883 return;
884 }
885 if (whence == SEEK_CUR) {
886 if (file->pos + off < file->pos) {
887 fibril_mutex_unlock(&file->lock);
888 ipc_answer_0(rid, EOVERFLOW);
889 return;
890 }
891 file->pos += off;
892 newpos = file->pos;
893 fibril_mutex_unlock(&file->lock);
894 ipc_answer_1(rid, EOK, newpos);
895 return;
896 }
897 if (whence == SEEK_END) {
898 fibril_rwlock_read_lock(&file->node->contents_rwlock);
899 size_t size = file->node->size;
900 fibril_rwlock_read_unlock(&file->node->contents_rwlock);
901 if (size + off < size) {
902 fibril_mutex_unlock(&file->lock);
903 ipc_answer_0(rid, EOVERFLOW);
904 return;
905 }
906 newpos = size + off;
907 file->pos = newpos;
908 fibril_mutex_unlock(&file->lock);
909 ipc_answer_1(rid, EOK, newpos);
910 return;
911 }
912 fibril_mutex_unlock(&file->lock);
913 ipc_answer_0(rid, EINVAL);
914}
915
916int
917vfs_truncate_internal(fs_handle_t fs_handle, dev_handle_t dev_handle,
918 fs_index_t index, size_t size)
919{
920 ipcarg_t rc;
921 int fs_phone;
922
923 fs_phone = vfs_grab_phone(fs_handle);
924 rc = async_req_3_0(fs_phone, VFS_OUT_TRUNCATE, (ipcarg_t)dev_handle,
925 (ipcarg_t)index, (ipcarg_t)size);
926 vfs_release_phone(fs_phone);
927 return (int)rc;
928}
929
930void vfs_truncate(ipc_callid_t rid, ipc_call_t *request)
931{
932 int fd = IPC_GET_ARG1(*request);
933 size_t size = IPC_GET_ARG2(*request);
934 int rc;
935
936 vfs_file_t *file = vfs_file_get(fd);
937 if (!file) {
938 ipc_answer_0(rid, ENOENT);
939 return;
940 }
941 fibril_mutex_lock(&file->lock);
942
943 fibril_rwlock_write_lock(&file->node->contents_rwlock);
944 rc = vfs_truncate_internal(file->node->fs_handle,
945 file->node->dev_handle, file->node->index, size);
946 if (rc == EOK)
947 file->node->size = size;
948 fibril_rwlock_write_unlock(&file->node->contents_rwlock);
949
950 fibril_mutex_unlock(&file->lock);
951 ipc_answer_0(rid, (ipcarg_t)rc);
952}
953
954void vfs_fstat(ipc_callid_t rid, ipc_call_t *request)
955{
956 int fd = IPC_GET_ARG1(*request);
957 ipcarg_t rc;
958
959 vfs_file_t *file = vfs_file_get(fd);
960 if (!file) {
961 ipc_answer_0(rid, ENOENT);
962 return;
963 }
964
965 ipc_callid_t callid;
966 if (!async_data_read_receive(&callid, NULL)) {
967 ipc_answer_0(callid, EINVAL);
968 ipc_answer_0(rid, EINVAL);
969 return;
970 }
971
972 fibril_mutex_lock(&file->lock);
973
974 int fs_phone = vfs_grab_phone(file->node->fs_handle);
975
976 aid_t msg;
977 msg = async_send_3(fs_phone, VFS_OUT_STAT, file->node->dev_handle,
978 file->node->index, true, NULL);
979 ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
980 async_wait_for(msg, &rc);
981 vfs_release_phone(fs_phone);
982
983 fibril_mutex_unlock(&file->lock);
984 ipc_answer_0(rid, rc);
985}
986
987void vfs_stat(ipc_callid_t rid, ipc_call_t *request)
988{
989 size_t len;
990 ipc_callid_t callid;
991
992 if (!async_data_write_receive(&callid, &len)) {
993 ipc_answer_0(callid, EINVAL);
994 ipc_answer_0(rid, EINVAL);
995 return;
996 }
997 char *path = malloc(len + 1);
998 if (!path) {
999 ipc_answer_0(callid, ENOMEM);
1000 ipc_answer_0(rid, ENOMEM);
1001 return;
1002 }
1003 int rc;
1004 if ((rc = async_data_write_finalize(callid, path, len))) {
1005 ipc_answer_0(rid, rc);
1006 free(path);
1007 return;
1008 }
1009 path[len] = '\0';
1010
1011 if (!async_data_read_receive(&callid, NULL)) {
1012 free(path);
1013 ipc_answer_0(callid, EINVAL);
1014 ipc_answer_0(rid, EINVAL);
1015 return;
1016 }
1017
1018 vfs_lookup_res_t lr;
1019 fibril_rwlock_read_lock(&namespace_rwlock);
1020 rc = vfs_lookup_internal(path, L_NONE, &lr, NULL);
1021 free(path);
1022 if (rc != EOK) {
1023 fibril_rwlock_read_unlock(&namespace_rwlock);
1024 ipc_answer_0(callid, rc);
1025 ipc_answer_0(rid, rc);
1026 return;
1027 }
1028 vfs_node_t *node = vfs_node_get(&lr);
1029 if (!node) {
1030 fibril_rwlock_read_unlock(&namespace_rwlock);
1031 ipc_answer_0(callid, ENOMEM);
1032 ipc_answer_0(rid, ENOMEM);
1033 return;
1034 }
1035
1036 fibril_rwlock_read_unlock(&namespace_rwlock);
1037
1038 int fs_phone = vfs_grab_phone(node->fs_handle);
1039 aid_t msg;
1040 msg = async_send_3(fs_phone, VFS_OUT_STAT, node->dev_handle,
1041 node->index, false, NULL);
1042 ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
1043
1044 ipcarg_t rv;
1045 async_wait_for(msg, &rv);
1046 vfs_release_phone(fs_phone);
1047
1048 ipc_answer_0(rid, rv);
1049
1050 vfs_node_put(node);
1051}
1052
1053void vfs_mkdir(ipc_callid_t rid, ipc_call_t *request)
1054{
1055 int mode = IPC_GET_ARG1(*request);
1056
1057 size_t len;
1058 ipc_callid_t callid;
1059
1060 if (!async_data_write_receive(&callid, &len)) {
1061 ipc_answer_0(callid, EINVAL);
1062 ipc_answer_0(rid, EINVAL);
1063 return;
1064 }
1065 char *path = malloc(len + 1);
1066 if (!path) {
1067 ipc_answer_0(callid, ENOMEM);
1068 ipc_answer_0(rid, ENOMEM);
1069 return;
1070 }
1071 int rc;
1072 if ((rc = async_data_write_finalize(callid, path, len))) {
1073 ipc_answer_0(rid, rc);
1074 free(path);
1075 return;
1076 }
1077 path[len] = '\0';
1078
1079 /* Ignore mode for now. */
1080 (void) mode;
1081
1082 fibril_rwlock_write_lock(&namespace_rwlock);
1083 int lflag = L_DIRECTORY | L_CREATE | L_EXCLUSIVE;
1084 rc = vfs_lookup_internal(path, lflag, NULL, NULL);
1085 fibril_rwlock_write_unlock(&namespace_rwlock);
1086 free(path);
1087 ipc_answer_0(rid, rc);
1088}
1089
1090void vfs_unlink(ipc_callid_t rid, ipc_call_t *request)
1091{
1092 int lflag = IPC_GET_ARG1(*request);
1093
1094 size_t len;
1095 ipc_callid_t callid;
1096
1097 if (!async_data_write_receive(&callid, &len)) {
1098 ipc_answer_0(callid, EINVAL);
1099 ipc_answer_0(rid, EINVAL);
1100 return;
1101 }
1102 char *path = malloc(len + 1);
1103 if (!path) {
1104 ipc_answer_0(callid, ENOMEM);
1105 ipc_answer_0(rid, ENOMEM);
1106 return;
1107 }
1108 int rc;
1109 if ((rc = async_data_write_finalize(callid, path, len))) {
1110 ipc_answer_0(rid, rc);
1111 free(path);
1112 return;
1113 }
1114 path[len] = '\0';
1115
1116 fibril_rwlock_write_lock(&namespace_rwlock);
1117 lflag &= L_DIRECTORY; /* sanitize lflag */
1118 vfs_lookup_res_t lr;
1119 rc = vfs_lookup_internal(path, lflag | L_UNLINK, &lr, NULL);
1120 free(path);
1121 if (rc != EOK) {
1122 fibril_rwlock_write_unlock(&namespace_rwlock);
1123 ipc_answer_0(rid, rc);
1124 return;
1125 }
1126
1127 /*
1128 * The name has already been unlinked by vfs_lookup_internal().
1129 * We have to get and put the VFS node to ensure that it is
1130 * VFS_OUT_DESTROY'ed after the last reference to it is dropped.
1131 */
1132 vfs_node_t *node = vfs_node_get(&lr);
1133 fibril_mutex_lock(&nodes_mutex);
1134 node->lnkcnt--;
1135 fibril_mutex_unlock(&nodes_mutex);
1136 fibril_rwlock_write_unlock(&namespace_rwlock);
1137 vfs_node_put(node);
1138 ipc_answer_0(rid, EOK);
1139}
1140
1141void vfs_rename(ipc_callid_t rid, ipc_call_t *request)
1142{
1143 size_t olen, nlen;
1144 ipc_callid_t callid;
1145 int rc;
1146
1147 /* Retrieve the old path. */
1148 if (!async_data_write_receive(&callid, &olen)) {
1149 ipc_answer_0(callid, EINVAL);
1150 ipc_answer_0(rid, EINVAL);
1151 return;
1152 }
1153 char *old = malloc(olen + 1);
1154 if (!old) {
1155 ipc_answer_0(callid, ENOMEM);
1156 ipc_answer_0(rid, ENOMEM);
1157 return;
1158 }
1159 if ((rc = async_data_write_finalize(callid, old, olen))) {
1160 ipc_answer_0(rid, rc);
1161 free(old);
1162 return;
1163 }
1164 old[olen] = '\0';
1165
1166 /* Retrieve the new path. */
1167 if (!async_data_write_receive(&callid, &nlen)) {
1168 ipc_answer_0(callid, EINVAL);
1169 ipc_answer_0(rid, EINVAL);
1170 free(old);
1171 return;
1172 }
1173 char *new = malloc(nlen + 1);
1174 if (!new) {
1175 ipc_answer_0(callid, ENOMEM);
1176 ipc_answer_0(rid, ENOMEM);
1177 free(old);
1178 return;
1179 }
1180 if ((rc = async_data_write_finalize(callid, new, nlen))) {
1181 ipc_answer_0(rid, rc);
1182 free(old);
1183 free(new);
1184 return;
1185 }
1186 new[nlen] = '\0';
1187
1188 char *oldc = canonify(old, &olen);
1189 char *newc = canonify(new, &nlen);
1190 if (!oldc || !newc) {
1191 ipc_answer_0(rid, EINVAL);
1192 free(old);
1193 free(new);
1194 return;
1195 }
1196 oldc[olen] = '\0';
1197 newc[nlen] = '\0';
1198 if ((!str_lcmp(newc, oldc, str_length(oldc))) &&
1199 ((newc[str_length(oldc)] == '/') ||
1200 (str_length(oldc) == 1) ||
1201 (str_length(oldc) == str_length(newc)))) {
1202 /*
1203 * oldc is a prefix of newc and either
1204 * - newc continues with a / where oldc ends, or
1205 * - oldc was / itself, or
1206 * - oldc and newc are equal.
1207 */
1208 ipc_answer_0(rid, EINVAL);
1209 free(old);
1210 free(new);
1211 return;
1212 }
1213
1214 vfs_lookup_res_t old_lr;
1215 vfs_lookup_res_t new_lr;
1216 vfs_lookup_res_t new_par_lr;
1217 fibril_rwlock_write_lock(&namespace_rwlock);
1218 /* Lookup the node belonging to the old file name. */
1219 rc = vfs_lookup_internal(oldc, L_NONE, &old_lr, NULL);
1220 if (rc != EOK) {
1221 fibril_rwlock_write_unlock(&namespace_rwlock);
1222 ipc_answer_0(rid, rc);
1223 free(old);
1224 free(new);
1225 return;
1226 }
1227 vfs_node_t *old_node = vfs_node_get(&old_lr);
1228 if (!old_node) {
1229 fibril_rwlock_write_unlock(&namespace_rwlock);
1230 ipc_answer_0(rid, ENOMEM);
1231 free(old);
1232 free(new);
1233 return;
1234 }
1235 /* Determine the path to the parent of the node with the new name. */
1236 char *parentc = str_dup(newc);
1237 if (!parentc) {
1238 fibril_rwlock_write_unlock(&namespace_rwlock);
1239 ipc_answer_0(rid, rc);
1240 free(old);
1241 free(new);
1242 return;
1243 }
1244 char *lastsl = str_rchr(parentc + 1, '/');
1245 if (lastsl)
1246 *lastsl = '\0';
1247 else
1248 parentc[1] = '\0';
1249 /* Lookup parent of the new file name. */
1250 rc = vfs_lookup_internal(parentc, L_NONE, &new_par_lr, NULL);
1251 free(parentc); /* not needed anymore */
1252 if (rc != EOK) {
1253 fibril_rwlock_write_unlock(&namespace_rwlock);
1254 ipc_answer_0(rid, rc);
1255 free(old);
1256 free(new);
1257 return;
1258 }
1259 /* Check whether linking to the same file system instance. */
1260 if ((old_node->fs_handle != new_par_lr.triplet.fs_handle) ||
1261 (old_node->dev_handle != new_par_lr.triplet.dev_handle)) {
1262 fibril_rwlock_write_unlock(&namespace_rwlock);
1263 ipc_answer_0(rid, EXDEV); /* different file systems */
1264 free(old);
1265 free(new);
1266 return;
1267 }
1268 /* Destroy the old link for the new name. */
1269 vfs_node_t *new_node = NULL;
1270 rc = vfs_lookup_internal(newc, L_UNLINK, &new_lr, NULL);
1271 switch (rc) {
1272 case ENOENT:
1273 /* simply not in our way */
1274 break;
1275 case EOK:
1276 new_node = vfs_node_get(&new_lr);
1277 if (!new_node) {
1278 fibril_rwlock_write_unlock(&namespace_rwlock);
1279 ipc_answer_0(rid, ENOMEM);
1280 free(old);
1281 free(new);
1282 return;
1283 }
1284 fibril_mutex_lock(&nodes_mutex);
1285 new_node->lnkcnt--;
1286 fibril_mutex_unlock(&nodes_mutex);
1287 break;
1288 default:
1289 fibril_rwlock_write_unlock(&namespace_rwlock);
1290 ipc_answer_0(rid, ENOTEMPTY);
1291 free(old);
1292 free(new);
1293 return;
1294 }
1295 /* Create the new link for the new name. */
1296 rc = vfs_lookup_internal(newc, L_LINK, NULL, NULL, old_node->index);
1297 if (rc != EOK) {
1298 fibril_rwlock_write_unlock(&namespace_rwlock);
1299 if (new_node)
1300 vfs_node_put(new_node);
1301 ipc_answer_0(rid, rc);
1302 free(old);
1303 free(new);
1304 return;
1305 }
1306 fibril_mutex_lock(&nodes_mutex);
1307 old_node->lnkcnt++;
1308 fibril_mutex_unlock(&nodes_mutex);
1309 /* Destroy the link for the old name. */
1310 rc = vfs_lookup_internal(oldc, L_UNLINK, NULL, NULL);
1311 if (rc != EOK) {
1312 fibril_rwlock_write_unlock(&namespace_rwlock);
1313 vfs_node_put(old_node);
1314 if (new_node)
1315 vfs_node_put(new_node);
1316 ipc_answer_0(rid, rc);
1317 free(old);
1318 free(new);
1319 return;
1320 }
1321 fibril_mutex_lock(&nodes_mutex);
1322 old_node->lnkcnt--;
1323 fibril_mutex_unlock(&nodes_mutex);
1324 fibril_rwlock_write_unlock(&namespace_rwlock);
1325 vfs_node_put(old_node);
1326 if (new_node)
1327 vfs_node_put(new_node);
1328 free(old);
1329 free(new);
1330 ipc_answer_0(rid, EOK);
1331}
1332
1333void vfs_dup(ipc_callid_t rid, ipc_call_t *request)
1334{
1335 int oldfd = IPC_GET_ARG1(*request);
1336 int newfd = IPC_GET_ARG2(*request);
1337
1338 /* Lookup the file structure corresponding to oldfd. */
1339 vfs_file_t *oldfile = vfs_file_get(oldfd);
1340 if (!oldfile) {
1341 ipc_answer_0(rid, EBADF);
1342 return;
1343 }
1344
1345 /* If the file descriptors are the same, do nothing. */
1346 if (oldfd == newfd) {
1347 ipc_answer_1(rid, EOK, newfd);
1348 return;
1349 }
1350
1351 /*
1352 * Lock the open file structure so that no other thread can manipulate
1353 * the same open file at a time.
1354 */
1355 fibril_mutex_lock(&oldfile->lock);
1356
1357 /* Lookup an open file structure possibly corresponding to newfd. */
1358 vfs_file_t *newfile = vfs_file_get(newfd);
1359 if (newfile) {
1360 /* Close the originally opened file. */
1361 int ret = vfs_close_internal(newfile);
1362 if (ret != EOK) {
1363 ipc_answer_0(rid, ret);
1364 return;
1365 }
1366
1367 ret = vfs_fd_free(newfd);
1368 if (ret != EOK) {
1369 ipc_answer_0(rid, ret);
1370 return;
1371 }
1372 }
1373
1374 /* Assign the old file to newfd. */
1375 int ret = vfs_fd_assign(oldfile, newfd);
1376 fibril_mutex_unlock(&oldfile->lock);
1377
1378 if (ret != EOK)
1379 ipc_answer_0(rid, ret);
1380 else
1381 ipc_answer_1(rid, EOK, newfd);
1382}
1383
1384/**
1385 * @}
1386 */
Note: See TracBrowser for help on using the repository browser.