source: mainline/uspace/srv/vfs/vfs_ops.c@ 5126f80

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

Merge from lp:~zarevucky-jiri/helenos/vfs-2.5/ revision 1946

Original commit messages:

1946: Jiri Zarevucky 2013-08-06 Relativize mount, add root handle to libc and remove root from VFS server. This wraps up the "relativization" phase.

Breakage:

  • Dynamic builds broken
  • Mount table lookups by name
  • Property mode set to 100644
File size: 29.3 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 <macros.h>
40#include <stdint.h>
41#include <async.h>
42#include <errno.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <str.h>
46#include <stdbool.h>
47#include <fibril_synch.h>
48#include <adt/list.h>
49#include <unistd.h>
50#include <ctype.h>
51#include <fcntl.h>
52#include <assert.h>
53#include <vfs/canonify.h>
54#include <vfs/vfs_mtab.h>
55
56FIBRIL_MUTEX_INITIALIZE(mtab_list_lock);
57LIST_INITIALIZE(mtab_list);
58static size_t mtab_size = 0;
59
60/* Forward declarations of static functions. */
61static int vfs_truncate_internal(fs_handle_t, service_id_t, fs_index_t,
62 aoff64_t);
63
64/**
65 * This rwlock prevents the race between a triplet-to-VFS-node resolution and a
66 * concurrent VFS operation which modifies the file system namespace.
67 */
68FIBRIL_RWLOCK_INITIALIZE(namespace_rwlock);
69
70static int vfs_connect_internal(service_id_t service_id, unsigned flags, unsigned instance,
71 char *options, char *fsname, vfs_node_t **root)
72{
73 fs_handle_t fs_handle = 0;
74
75 fibril_mutex_lock(&fs_list_lock);
76 while (1) {
77 fs_handle = fs_name_to_handle(instance, fsname, false);
78
79 if (fs_handle != 0 || !(flags & VFS_MOUNT_BLOCKING)) {
80 break;
81 }
82
83 fibril_condvar_wait(&fs_list_cv, &fs_list_lock);
84 }
85 fibril_mutex_unlock(&fs_list_lock);
86
87 if (fs_handle == 0) {
88 return ENOENT;
89 }
90
91 /* Tell the mountee that it is being mounted. */
92 ipc_call_t answer;
93 async_exch_t *exch = vfs_exchange_grab(fs_handle);
94 aid_t msg = async_send_1(exch, VFS_OUT_MOUNTED, (sysarg_t) service_id, &answer);
95 /* Send the mount options */
96 sysarg_t rc = async_data_write_start(exch, options, str_size(options));
97 if (rc != EOK) {
98 async_forget(msg);
99 vfs_exchange_release(exch);
100 return rc;
101 }
102 async_wait_for(msg, &rc);
103 vfs_exchange_release(exch);
104
105 if (rc != EOK) {
106 return rc;
107 }
108
109 vfs_lookup_res_t res;
110 res.triplet.fs_handle = fs_handle;
111 res.triplet.service_id = service_id;
112 res.triplet.index = (fs_index_t) IPC_GET_ARG1(answer);
113 res.size = (int64_t) MERGE_LOUP32(IPC_GET_ARG2(answer), IPC_GET_ARG3(answer));
114 res.type = VFS_NODE_DIRECTORY;
115
116 /* Add reference to the mounted root. */
117 *root = vfs_node_get(&res);
118 assert(*root);
119
120 return EOK;
121}
122
123void vfs_mount_srv(ipc_callid_t rid, ipc_call_t *request)
124{
125 int mpfd = IPC_GET_ARG1(*request);
126
127 /*
128 * We expect the library to do the device-name to device-handle
129 * translation for us, thus the device handle will arrive as ARG1
130 * in the request.
131 */
132 service_id_t service_id = (service_id_t) IPC_GET_ARG2(*request);
133
134 /*
135 * Mount flags are passed as ARG2.
136 */
137 unsigned int flags = (unsigned int) IPC_GET_ARG3(*request);
138
139 /*
140 * Instance number is passed as ARG3.
141 */
142 unsigned int instance = IPC_GET_ARG4(*request);
143
144 char *opts = NULL;
145 char *fs_name = NULL;
146 vfs_file_t *mp = NULL;
147 vfs_file_t *file = NULL;
148 int fd = -1;
149 mtab_ent_t *mtab_ent = NULL;
150
151 /* Now we expect to receive the mount options. */
152 int rc = async_data_write_accept((void **) &opts, true, 0, MAX_MNTOPTS_LEN,
153 0, NULL);
154 if (rc != EOK) {
155 async_data_write_void(rc);
156 goto out;
157 }
158
159 /*
160 * Now, we expect the client to send us data with the name of the file
161 * system.
162 */
163 rc = async_data_write_accept((void **) &fs_name, true, 0,
164 FS_NAME_MAXLEN, 0, NULL);
165 if (rc != EOK) {
166 goto out;
167 }
168
169 if (!(flags & VFS_MOUNT_CONNECT_ONLY)) {
170 mp = vfs_file_get(mpfd);
171 if (mp == NULL) {
172 rc = EBADF;
173 goto out;
174 }
175
176 if (mp->node->mount != NULL) {
177 rc = EBUSY;
178 goto out;
179 }
180
181 if (mp->node->type != VFS_NODE_DIRECTORY) {
182 rc = ENOTDIR;
183 goto out;
184 }
185
186 if (vfs_node_has_children(mp->node)) {
187 rc = ENOTEMPTY;
188 goto out;
189 }
190 }
191
192 if (!(flags & VFS_MOUNT_NO_REF)) {
193 fd = vfs_fd_alloc(&file, false);
194 if (fd < 0) {
195 rc = fd;
196 goto out;
197 }
198 }
199
200 /* Add the filesystem info to the list of mounted filesystems */
201 mtab_ent = malloc(sizeof(mtab_ent_t));
202 if (!mtab_ent) {
203 rc = ENOMEM;
204 goto out;
205 }
206
207 vfs_node_t *root = NULL;
208
209 fibril_rwlock_write_lock(&namespace_rwlock);
210
211 rc = vfs_connect_internal(service_id, flags, instance, opts, fs_name, &root);
212 if (rc == EOK && !(flags & VFS_MOUNT_CONNECT_ONLY)) {
213 vfs_node_addref(mp->node);
214 vfs_node_addref(root);
215 mp->node->mount = root;
216 }
217
218 fibril_rwlock_write_unlock(&namespace_rwlock);
219
220 if (rc != EOK) {
221 goto out;
222 }
223
224
225 if (flags & VFS_MOUNT_NO_REF) {
226 vfs_node_delref(root);
227 } else {
228 assert(file != NULL);
229
230 file->node = root;
231 file->permissions = MODE_READ | MODE_WRITE | MODE_APPEND;
232 file->open_read = false;
233 file->open_write = false;
234 }
235
236 /* Add the filesystem info to the list of mounted filesystems */
237 if (rc == EOK) {
238 str_cpy(mtab_ent->mp, MAX_PATH_LEN, "fixme");
239 str_cpy(mtab_ent->fs_name, FS_NAME_MAXLEN, fs_name);
240 str_cpy(mtab_ent->opts, MAX_MNTOPTS_LEN, opts);
241 mtab_ent->instance = instance;
242 mtab_ent->service_id = service_id;
243
244 link_initialize(&mtab_ent->link);
245
246 fibril_mutex_lock(&mtab_list_lock);
247 list_append(&mtab_ent->link, &mtab_list);
248 mtab_size++;
249 fibril_mutex_unlock(&mtab_list_lock);
250 }
251
252 rc = EOK;
253
254out:
255 async_answer_1(rid, rc, rc == EOK ? fd : 0);
256
257 if (opts) {
258 free(opts);
259 }
260 if (fs_name) {
261 free(fs_name);
262 }
263 if (mp) {
264 vfs_file_put(mp);
265 }
266 if (file) {
267 vfs_file_put(file);
268 }
269 if (rc != EOK && fd >= 0) {
270 vfs_fd_free(fd);
271 }
272}
273
274void vfs_unmount_srv(ipc_callid_t rid, ipc_call_t *request)
275{
276 int mpfd = IPC_GET_ARG1(*request);
277
278 vfs_file_t *mp = vfs_file_get(mpfd);
279 if (mp == NULL) {
280 async_answer_0(rid, EBADF);
281 return;
282 }
283
284 if (mp->node->mount == NULL) {
285 async_answer_0(rid, ENOENT);
286 vfs_file_put(mp);
287 return;
288 }
289
290 fibril_rwlock_write_lock(&namespace_rwlock);
291
292 /*
293 * Count the total number of references for the mounted file system. We
294 * are expecting at least one, which is held by the mount point.
295 * If we find more, it means that
296 * the file system cannot be gracefully unmounted at the moment because
297 * someone is working with it.
298 */
299 if (vfs_nodes_refcount_sum_get(mp->node->mount->fs_handle, mp->node->mount->service_id) != 1) {
300 async_answer_0(rid, EBUSY);
301 vfs_file_put(mp);
302 fibril_rwlock_write_unlock(&namespace_rwlock);
303 return;
304 }
305
306 async_exch_t *exch = vfs_exchange_grab(mp->node->mount->fs_handle);
307 int rc = async_req_1_0(exch, VFS_OUT_UNMOUNTED, mp->node->mount->service_id);
308 vfs_exchange_release(exch);
309
310 if (rc != EOK) {
311 async_answer_0(rid, rc);
312 vfs_file_put(mp);
313 fibril_rwlock_write_unlock(&namespace_rwlock);
314 return;
315 }
316
317 vfs_node_forget(mp->node->mount);
318 vfs_node_put(mp->node);
319 mp->node->mount = NULL;
320
321 fibril_rwlock_write_unlock(&namespace_rwlock);
322
323 fibril_mutex_lock(&mtab_list_lock);
324 int found = 0;
325
326 list_foreach(mtab_list, link, mtab_ent_t, mtab_ent) {
327 // FIXME: mp name
328 if (str_cmp(mtab_ent->mp, "fixme") == 0) {
329 list_remove(&mtab_ent->link);
330 mtab_size--;
331 free(mtab_ent);
332 found = 1;
333 break;
334 }
335 }
336 assert(found);
337 fibril_mutex_unlock(&mtab_list_lock);
338
339 vfs_file_put(mp);
340 async_answer_0(rid, EOK);
341}
342
343static inline bool walk_flags_valid(int flags)
344{
345 if ((flags&~WALK_ALL_FLAGS) != 0) {
346 return false;
347 }
348 if ((flags&WALK_MAY_CREATE) && (flags&WALK_MUST_CREATE)) {
349 return false;
350 }
351 if ((flags&WALK_REGULAR) && (flags&WALK_DIRECTORY)) {
352 return false;
353 }
354 if ((flags&WALK_MAY_CREATE) || (flags&WALK_MUST_CREATE)) {
355 if (!(flags&WALK_DIRECTORY) && !(flags&WALK_REGULAR)) {
356 return false;
357 }
358 }
359 return true;
360}
361
362static inline int walk_lookup_flags(int flags)
363{
364 int lflags = 0;
365 if (flags&WALK_MAY_CREATE || flags&WALK_MUST_CREATE) {
366 lflags |= L_CREATE;
367 }
368 if (flags&WALK_MUST_CREATE) {
369 lflags |= L_EXCLUSIVE;
370 }
371 if (flags&WALK_REGULAR) {
372 lflags |= L_FILE;
373 }
374 if (flags&WALK_DIRECTORY) {
375 lflags |= L_DIRECTORY;
376 }
377 if (flags&WALK_MOUNT_POINT) {
378 lflags |= L_MP;
379 }
380 return lflags;
381}
382
383void vfs_walk(ipc_callid_t rid, ipc_call_t *request)
384{
385 /*
386 * Parent is our relative root for file lookup.
387 * For defined flags, see <ipc/vfs.h>.
388 */
389 int parentfd = IPC_GET_ARG1(*request);
390 int flags = IPC_GET_ARG2(*request);
391
392 if (!walk_flags_valid(flags)) {
393 async_answer_0(rid, EINVAL);
394 return;
395 }
396
397 char *path;
398 int rc = async_data_write_accept((void **)&path, true, 0, 0, 0, NULL);
399
400 /* Lookup the file structure corresponding to the file descriptor. */
401 vfs_file_t *parent = vfs_file_get(parentfd);
402 if (!parent) {
403 free(path);
404 async_answer_0(rid, EBADF);
405 return;
406 }
407
408 fibril_rwlock_read_lock(&namespace_rwlock);
409
410 vfs_lookup_res_t lr;
411 rc = vfs_lookup_internal(parent->node, path, walk_lookup_flags(flags), &lr);
412 free(path);
413
414 if (rc != EOK) {
415 fibril_rwlock_read_unlock(&namespace_rwlock);
416 if (parent) {
417 vfs_file_put(parent);
418 }
419 async_answer_0(rid, rc);
420 return;
421 }
422
423 vfs_node_t *node = vfs_node_get(&lr);
424
425 vfs_file_t *file;
426 int fd = vfs_fd_alloc(&file, false);
427 if (fd < 0) {
428 vfs_node_put(node);
429 if (parent) {
430 vfs_file_put(parent);
431 }
432 async_answer_0(rid, fd);
433 return;
434 }
435 assert(file != NULL);
436
437 file->node = node;
438 if (parent) {
439 file->permissions = parent->permissions;
440 } else {
441 file->permissions = MODE_READ | MODE_WRITE | MODE_APPEND;
442 }
443 file->open_read = false;
444 file->open_write = false;
445
446 vfs_file_put(file);
447 if (parent) {
448 vfs_file_put(parent);
449 }
450
451 fibril_rwlock_read_unlock(&namespace_rwlock);
452
453 async_answer_1(rid, EOK, fd);
454}
455
456void vfs_open2(ipc_callid_t rid, ipc_call_t *request)
457{
458 int fd = IPC_GET_ARG1(*request);
459 int flags = IPC_GET_ARG2(*request);
460
461 if (flags == 0) {
462 async_answer_0(rid, EINVAL);
463 return;
464 }
465
466 vfs_file_t *file = vfs_file_get(fd);
467 if (!file) {
468 async_answer_0(rid, EBADF);
469 return;
470 }
471
472 if ((flags & ~file->permissions) != 0) {
473 vfs_file_put(file);
474 async_answer_0(rid, EPERM);
475 return;
476 }
477
478 file->open_read = (flags & MODE_READ) != 0;
479 file->open_write = (flags & (MODE_WRITE | MODE_APPEND)) != 0;
480 file->append = (flags & MODE_APPEND) != 0;
481
482 if (!file->open_read && !file->open_write) {
483 vfs_file_put(file);
484 async_answer_0(rid, EINVAL);
485 return;
486 }
487
488 if (file->node->type == VFS_NODE_DIRECTORY && file->open_write) {
489 file->open_read = file->open_write = false;
490 vfs_file_put(file);
491 async_answer_0(rid, EINVAL);
492 return;
493 }
494
495 int rc = vfs_open_node_remote(file->node);
496 if (rc != EOK) {
497 file->open_read = file->open_write = false;
498 vfs_file_put(file);
499 async_answer_0(rid, rc);
500 return;
501 }
502
503 vfs_file_put(file);
504 async_answer_0(rid, EOK);
505}
506
507void vfs_sync(ipc_callid_t rid, ipc_call_t *request)
508{
509 int fd = IPC_GET_ARG1(*request);
510
511 /* Lookup the file structure corresponding to the file descriptor. */
512 vfs_file_t *file = vfs_file_get(fd);
513 if (!file) {
514 async_answer_0(rid, ENOENT);
515 return;
516 }
517
518 /*
519 * Lock the open file structure so that no other thread can manipulate
520 * the same open file at a time.
521 */
522 async_exch_t *fs_exch = vfs_exchange_grab(file->node->fs_handle);
523
524 /* Make a VFS_OUT_SYMC request at the destination FS server. */
525 aid_t msg;
526 ipc_call_t answer;
527 msg = async_send_2(fs_exch, VFS_OUT_SYNC, file->node->service_id,
528 file->node->index, &answer);
529
530 vfs_exchange_release(fs_exch);
531
532 /* Wait for reply from the FS server. */
533 sysarg_t rc;
534 async_wait_for(msg, &rc);
535
536 vfs_file_put(file);
537 async_answer_0(rid, rc);
538}
539
540void vfs_close(ipc_callid_t rid, ipc_call_t *request)
541{
542 int fd = IPC_GET_ARG1(*request);
543 int ret = vfs_fd_free(fd);
544 async_answer_0(rid, ret);
545}
546
547typedef int (* rdwr_ipc_cb_t)(async_exch_t *, vfs_file_t *, ipc_call_t *,
548 bool, void *);
549
550static int rdwr_ipc_client(async_exch_t *exch, vfs_file_t *file,
551 ipc_call_t *answer, bool read, void *data)
552{
553 size_t *bytes = (size_t *) data;
554 int rc;
555
556 /*
557 * Make a VFS_READ/VFS_WRITE request at the destination FS server
558 * and forward the IPC_M_DATA_READ/IPC_M_DATA_WRITE request to the
559 * destination FS server. The call will be routed as if sent by
560 * ourselves. Note that call arguments are immutable in this case so we
561 * don't have to bother.
562 */
563
564 if (read) {
565 rc = async_data_read_forward_4_1(exch, VFS_OUT_READ,
566 file->node->service_id, file->node->index,
567 LOWER32(file->pos), UPPER32(file->pos), answer);
568 } else {
569 rc = async_data_write_forward_4_1(exch, VFS_OUT_WRITE,
570 file->node->service_id, file->node->index,
571 LOWER32(file->pos), UPPER32(file->pos), answer);
572 }
573
574 *bytes = IPC_GET_ARG1(*answer);
575 return rc;
576}
577
578static int rdwr_ipc_internal(async_exch_t *exch, vfs_file_t *file,
579 ipc_call_t *answer, bool read, void *data)
580{
581 rdwr_io_chunk_t *chunk = (rdwr_io_chunk_t *) data;
582
583 if (exch == NULL)
584 return ENOENT;
585
586 aid_t msg = async_send_fast(exch, read ? VFS_OUT_READ : VFS_OUT_WRITE,
587 file->node->service_id, file->node->index, LOWER32(file->pos),
588 UPPER32(file->pos), answer);
589 if (msg == 0)
590 return EINVAL;
591
592 int retval = async_data_read_start(exch, chunk->buffer, chunk->size);
593 if (retval != EOK) {
594 async_forget(msg);
595 return retval;
596 }
597
598 sysarg_t rc;
599 async_wait_for(msg, &rc);
600
601 chunk->size = IPC_GET_ARG1(*answer);
602
603 return (int) rc;
604}
605
606static int vfs_rdwr(int fd, bool read, rdwr_ipc_cb_t ipc_cb, void *ipc_cb_data)
607{
608 /*
609 * The following code strongly depends on the fact that the files data
610 * structure can be only accessed by a single fibril and all file
611 * operations are serialized (i.e. the reads and writes cannot
612 * interleave and a file cannot be closed while it is being read).
613 *
614 * Additional synchronization needs to be added once the table of
615 * open files supports parallel access!
616 */
617
618 /* Lookup the file structure corresponding to the file descriptor. */
619 vfs_file_t *file = vfs_file_get(fd);
620 if (!file)
621 return EBADF;
622
623 if ((read && !file->open_read) || (!read && !file->open_write)) {
624 vfs_file_put(file);
625 return EINVAL;
626 }
627
628 vfs_info_t *fs_info = fs_handle_to_info(file->node->fs_handle);
629 assert(fs_info);
630
631 bool rlock = read || ((fs_info->concurrent_read_write) && (fs_info->write_retains_size));
632
633 /*
634 * Lock the file's node so that no other client can read/write to it at
635 * the same time unless the FS supports concurrent reads/writes and its
636 * write implementation does not modify the file size.
637 */
638 if (rlock) {
639 fibril_rwlock_read_lock(&file->node->contents_rwlock);
640 } else {
641 fibril_rwlock_write_lock(&file->node->contents_rwlock);
642 }
643
644 if (file->node->type == VFS_NODE_DIRECTORY) {
645 /*
646 * Make sure that no one is modifying the namespace
647 * while we are in readdir().
648 */
649
650 if (!read) {
651 if (rlock) {
652 fibril_rwlock_read_unlock(&file->node->contents_rwlock);
653 } else {
654 fibril_rwlock_write_unlock(&file->node->contents_rwlock);
655 }
656 vfs_file_put(file);
657 return EINVAL;
658 }
659
660 fibril_rwlock_read_lock(&namespace_rwlock);
661 }
662
663 async_exch_t *fs_exch = vfs_exchange_grab(file->node->fs_handle);
664
665 if (!read && file->append)
666 file->pos = file->node->size;
667
668 /*
669 * Handle communication with the endpoint FS.
670 */
671 ipc_call_t answer;
672 int rc = ipc_cb(fs_exch, file, &answer, read, ipc_cb_data);
673
674 vfs_exchange_release(fs_exch);
675
676 size_t bytes = IPC_GET_ARG1(answer);
677
678 if (file->node->type == VFS_NODE_DIRECTORY) {
679 fibril_rwlock_read_unlock(&namespace_rwlock);
680 }
681
682 /* Unlock the VFS node. */
683 if (rlock) {
684 fibril_rwlock_read_unlock(&file->node->contents_rwlock);
685 } else {
686 /* Update the cached version of node's size. */
687 if (rc == EOK) {
688 file->node->size = MERGE_LOUP32(IPC_GET_ARG2(answer),
689 IPC_GET_ARG3(answer));
690 }
691 fibril_rwlock_write_unlock(&file->node->contents_rwlock);
692 }
693
694 /* Update the position pointer and unlock the open file. */
695 if (rc == EOK) {
696 file->pos += bytes;
697 }
698 vfs_file_put(file);
699
700 return rc;
701}
702
703static void vfs_rdwr_client(ipc_callid_t rid, ipc_call_t *request, bool read)
704{
705 size_t bytes = 0;
706 int rc = vfs_rdwr(IPC_GET_ARG1(*request), read, rdwr_ipc_client,
707 &bytes);
708 async_answer_1(rid, rc, bytes);
709}
710
711int vfs_rdwr_internal(int fd, bool read, rdwr_io_chunk_t *chunk)
712{
713 return vfs_rdwr(fd, read, rdwr_ipc_internal, chunk);
714}
715
716void vfs_read(ipc_callid_t rid, ipc_call_t *request)
717{
718 vfs_rdwr_client(rid, request, true);
719}
720
721void vfs_write(ipc_callid_t rid, ipc_call_t *request)
722{
723 vfs_rdwr_client(rid, request, false);
724}
725
726void vfs_seek(ipc_callid_t rid, ipc_call_t *request)
727{
728 int fd = (int) IPC_GET_ARG1(*request);
729 off64_t off = (off64_t) MERGE_LOUP32(IPC_GET_ARG2(*request),
730 IPC_GET_ARG3(*request));
731 int whence = (int) IPC_GET_ARG4(*request);
732
733 /* Lookup the file structure corresponding to the file descriptor. */
734 vfs_file_t *file = vfs_file_get(fd);
735 if (!file) {
736 async_answer_0(rid, ENOENT);
737 return;
738 }
739
740 off64_t newoff;
741 switch (whence) {
742 case SEEK_SET:
743 if (off >= 0) {
744 file->pos = (aoff64_t) off;
745 vfs_file_put(file);
746 async_answer_1(rid, EOK, off);
747 return;
748 }
749 break;
750 case SEEK_CUR:
751 if ((off >= 0) && (file->pos + off < file->pos)) {
752 vfs_file_put(file);
753 async_answer_0(rid, EOVERFLOW);
754 return;
755 }
756
757 if ((off < 0) && (file->pos < (aoff64_t) -off)) {
758 vfs_file_put(file);
759 async_answer_0(rid, EOVERFLOW);
760 return;
761 }
762
763 file->pos += off;
764 newoff = (file->pos > OFF64_MAX) ? OFF64_MAX : file->pos;
765
766 vfs_file_put(file);
767 async_answer_2(rid, EOK, LOWER32(newoff),
768 UPPER32(newoff));
769 return;
770 case SEEK_END:
771 fibril_rwlock_read_lock(&file->node->contents_rwlock);
772 aoff64_t size = vfs_node_get_size(file->node);
773
774 if ((off >= 0) && (size + off < size)) {
775 fibril_rwlock_read_unlock(&file->node->contents_rwlock);
776 vfs_file_put(file);
777 async_answer_0(rid, EOVERFLOW);
778 return;
779 }
780
781 if ((off < 0) && (size < (aoff64_t) -off)) {
782 fibril_rwlock_read_unlock(&file->node->contents_rwlock);
783 vfs_file_put(file);
784 async_answer_0(rid, EOVERFLOW);
785 return;
786 }
787
788 file->pos = size + off;
789 newoff = (file->pos > OFF64_MAX) ? OFF64_MAX : file->pos;
790
791 fibril_rwlock_read_unlock(&file->node->contents_rwlock);
792 vfs_file_put(file);
793 async_answer_2(rid, EOK, LOWER32(newoff), UPPER32(newoff));
794 return;
795 }
796
797 vfs_file_put(file);
798 async_answer_0(rid, EINVAL);
799}
800
801int vfs_truncate_internal(fs_handle_t fs_handle, service_id_t service_id,
802 fs_index_t index, aoff64_t size)
803{
804 async_exch_t *exch = vfs_exchange_grab(fs_handle);
805 sysarg_t rc = async_req_4_0(exch, VFS_OUT_TRUNCATE,
806 (sysarg_t) service_id, (sysarg_t) index, LOWER32(size),
807 UPPER32(size));
808 vfs_exchange_release(exch);
809
810 return (int) rc;
811}
812
813void vfs_truncate(ipc_callid_t rid, ipc_call_t *request)
814{
815 int fd = IPC_GET_ARG1(*request);
816 aoff64_t size = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG2(*request),
817 IPC_GET_ARG3(*request));
818 int rc;
819
820 vfs_file_t *file = vfs_file_get(fd);
821 if (!file) {
822 async_answer_0(rid, ENOENT);
823 return;
824 }
825
826 fibril_rwlock_write_lock(&file->node->contents_rwlock);
827 rc = vfs_truncate_internal(file->node->fs_handle,
828 file->node->service_id, file->node->index, size);
829 if (rc == EOK)
830 file->node->size = size;
831 fibril_rwlock_write_unlock(&file->node->contents_rwlock);
832
833 vfs_file_put(file);
834 async_answer_0(rid, (sysarg_t)rc);
835}
836
837void vfs_fstat(ipc_callid_t rid, ipc_call_t *request)
838{
839 int fd = IPC_GET_ARG1(*request);
840 sysarg_t rc;
841
842 vfs_file_t *file = vfs_file_get(fd);
843 if (!file) {
844 async_answer_0(rid, ENOENT);
845 return;
846 }
847 assert(file->node);
848
849 ipc_callid_t callid;
850 if (!async_data_read_receive(&callid, NULL)) {
851 vfs_file_put(file);
852 async_answer_0(callid, EINVAL);
853 async_answer_0(rid, EINVAL);
854 return;
855 }
856
857 async_exch_t *exch = vfs_exchange_grab(file->node->fs_handle);
858 assert(exch);
859
860 aid_t msg;
861 msg = async_send_3(exch, VFS_OUT_STAT, file->node->service_id,
862 file->node->index, true, NULL);
863 assert(msg);
864 async_forward_fast(callid, exch, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
865
866 vfs_exchange_release(exch);
867
868 async_wait_for(msg, &rc);
869
870 vfs_file_put(file);
871 async_answer_0(rid, rc);
872}
873
874static void out_destroy(vfs_triplet_t *file)
875{
876 async_exch_t *exch = vfs_exchange_grab(file->fs_handle);
877 async_msg_2(exch, VFS_OUT_DESTROY,
878 (sysarg_t) file->service_id, (sysarg_t) file->index);
879 vfs_exchange_release(exch);
880}
881
882void vfs_unlink2(ipc_callid_t rid, ipc_call_t *request)
883{
884 int rc;
885 char *path;
886 vfs_file_t *parent = NULL;
887 vfs_file_t *expect = NULL;
888
889 int parentfd = IPC_GET_ARG1(*request);
890 int expectfd = IPC_GET_ARG2(*request);
891 int wflag = IPC_GET_ARG3(*request);
892
893 rc = async_data_write_accept((void **) &path, true, 0, 0, 0, NULL);
894 if (rc != EOK) {
895 async_answer_0(rid, rc);
896 return;
897 }
898 if (parentfd == expectfd) {
899 async_answer_0(rid, EINVAL);
900 return;
901 }
902
903 fibril_rwlock_write_lock(&namespace_rwlock);
904
905 int lflag = (wflag&WALK_DIRECTORY) ? L_DIRECTORY: 0;
906
907 /* Files are retrieved in order of file descriptors, to prevent deadlock. */
908 if (parentfd < expectfd) {
909 parent = vfs_file_get(parentfd);
910 if (!parent) {
911 rc = EBADF;
912 goto exit;
913 }
914 }
915
916 if (expectfd >= 0) {
917 expect = vfs_file_get(expectfd);
918 if (!expect) {
919 rc = ENOENT;
920 goto exit;
921 }
922 }
923
924 if (parentfd > expectfd) {
925 parent = vfs_file_get(parentfd);
926 if (!parent) {
927 rc = EBADF;
928 goto exit;
929 }
930 }
931
932 assert(parent != NULL);
933
934 if (expectfd >= 0) {
935 vfs_lookup_res_t lr;
936 rc = vfs_lookup_internal(parent->node, path, lflag, &lr);
937 if (rc != EOK) {
938 goto exit;
939 }
940
941 vfs_node_t *found_node = vfs_node_peek(&lr);
942 if (expect->node != found_node) {
943 rc = ENOENT;
944 goto exit;
945 }
946
947 vfs_file_put(expect);
948 expect = NULL;
949 }
950
951 vfs_lookup_res_t lr;
952 rc = vfs_lookup_internal(parent->node, path, lflag | L_UNLINK, &lr);
953 if (rc != EOK) {
954 goto exit;
955 }
956
957 /* If the node is not held by anyone, try to destroy it. */
958 if (vfs_node_peek(&lr) == NULL) {
959 out_destroy(&lr.triplet);
960 }
961
962exit:
963 if (path) {
964 free(path);
965 }
966 if (parent) {
967 vfs_file_put(parent);
968 }
969 if (expect) {
970 vfs_file_put(expect);
971 }
972 fibril_rwlock_write_unlock(&namespace_rwlock);
973 async_answer_0(rid, rc);
974}
975
976static size_t shared_path(char *a, char *b)
977{
978 size_t res = 0;
979
980 while (a[res] == b[res] && a[res] != 0) {
981 res++;
982 }
983
984 if (a[res] == b[res]) {
985 return res;
986 }
987
988 res--;
989 while (a[res] != '/') {
990 res--;
991 }
992 return res;
993}
994
995static int vfs_rename_internal(vfs_node_t *base, char *old, char *new)
996{
997 assert(base != NULL);
998 assert(old != NULL);
999 assert(new != NULL);
1000
1001 vfs_lookup_res_t base_lr;
1002 vfs_lookup_res_t old_lr;
1003 vfs_lookup_res_t new_lr_orig;
1004 bool orig_unlinked = false;
1005
1006 int rc;
1007
1008 size_t shared = shared_path(old, new);
1009
1010 /* Do not allow one path to be a prefix of the other. */
1011 if (old[shared] == 0 || new[shared] == 0) {
1012 return EINVAL;
1013 }
1014 assert(old[shared] == '/');
1015 assert(new[shared] == '/');
1016
1017 fibril_rwlock_write_lock(&namespace_rwlock);
1018
1019 /* Resolve the shared portion of the path first. */
1020 if (shared != 0) {
1021 old[shared] = 0;
1022 rc = vfs_lookup_internal(base, old, L_DIRECTORY, &base_lr);
1023 if (rc != EOK) {
1024 fibril_rwlock_write_unlock(&namespace_rwlock);
1025 return rc;
1026 }
1027
1028 base = vfs_node_get(&base_lr);
1029 old[shared] = '/';
1030 old += shared;
1031 new += shared;
1032 } else {
1033 vfs_node_addref(base);
1034 }
1035
1036
1037 rc = vfs_lookup_internal(base, new, L_UNLINK | L_DISABLE_MOUNTS, &new_lr_orig);
1038 if (rc == EOK) {
1039 orig_unlinked = true;
1040 } else if (rc != ENOENT) {
1041 vfs_node_put(base);
1042 fibril_rwlock_write_unlock(&namespace_rwlock);
1043 return rc;
1044 }
1045
1046 rc = vfs_lookup_internal(base, old, L_UNLINK | L_DISABLE_MOUNTS, &old_lr);
1047 if (rc != EOK) {
1048 if (orig_unlinked) {
1049 vfs_link_internal(base, new, &new_lr_orig.triplet);
1050 }
1051 vfs_node_put(base);
1052 fibril_rwlock_write_unlock(&namespace_rwlock);
1053 return rc;
1054 }
1055
1056 rc = vfs_link_internal(base, new, &old_lr.triplet);
1057 if (rc != EOK) {
1058 vfs_link_internal(base, old, &old_lr.triplet);
1059 if (orig_unlinked) {
1060 vfs_link_internal(base, new, &new_lr_orig.triplet);
1061 }
1062 vfs_node_put(base);
1063 fibril_rwlock_write_unlock(&namespace_rwlock);
1064 return rc;
1065 }
1066
1067 /* If the node is not held by anyone, try to destroy it. */
1068 if (orig_unlinked && vfs_node_peek(&new_lr_orig) == NULL) {
1069 out_destroy(&new_lr_orig.triplet);
1070 }
1071
1072 vfs_node_put(base);
1073 fibril_rwlock_write_unlock(&namespace_rwlock);
1074 return EOK;
1075}
1076
1077void vfs_rename(ipc_callid_t rid, ipc_call_t *request)
1078{
1079 /* The common base directory. */
1080 int basefd;
1081 char *old = NULL;
1082 char *new = NULL;
1083 vfs_file_t *base = NULL;
1084 int rc;
1085
1086 basefd = IPC_GET_ARG1(*request);
1087
1088 /* Retrieve the old path. */
1089 rc = async_data_write_accept((void **) &old, true, 0, 0, 0, NULL);
1090 if (rc != EOK) {
1091 goto out;
1092 }
1093
1094 /* Retrieve the new path. */
1095 rc = async_data_write_accept((void **) &new, true, 0, 0, 0, NULL);
1096 if (rc != EOK) {
1097 goto out;
1098 }
1099
1100 size_t olen;
1101 size_t nlen;
1102 char *oldc = canonify(old, &olen);
1103 char *newc = canonify(new, &nlen);
1104
1105 if ((!oldc) || (!newc)) {
1106 rc = EINVAL;
1107 goto out;
1108 }
1109
1110 assert(oldc[olen] == '\0');
1111 assert(newc[nlen] == '\0');
1112
1113 /* Lookup the file structure corresponding to the file descriptor. */
1114 base = vfs_file_get(basefd);
1115 if (!base) {
1116 rc = EBADF;
1117 goto out;
1118 }
1119
1120 rc = vfs_rename_internal(base->node, oldc, newc);
1121
1122out:
1123 async_answer_0(rid, rc);
1124
1125 if (old) {
1126 free(old);
1127 }
1128 if (new) {
1129 free(new);
1130 }
1131 if (base) {
1132 vfs_file_put(base);
1133 }
1134}
1135
1136void vfs_dup(ipc_callid_t rid, ipc_call_t *request)
1137{
1138 int oldfd = IPC_GET_ARG1(*request);
1139 int newfd = IPC_GET_ARG2(*request);
1140
1141 /* If the file descriptors are the same, do nothing. */
1142 if (oldfd == newfd) {
1143 async_answer_1(rid, EOK, newfd);
1144 return;
1145 }
1146
1147 /* Lookup the file structure corresponding to oldfd. */
1148 vfs_file_t *oldfile = vfs_file_get(oldfd);
1149 if (!oldfile) {
1150 async_answer_0(rid, EBADF);
1151 return;
1152 }
1153
1154 /* Make sure newfd is closed. */
1155 (void) vfs_fd_free(newfd);
1156
1157 /* Assign the old file to newfd. */
1158 int ret = vfs_fd_assign(oldfile, newfd);
1159 vfs_file_put(oldfile);
1160
1161 if (ret != EOK)
1162 async_answer_0(rid, ret);
1163 else
1164 async_answer_1(rid, EOK, newfd);
1165}
1166
1167void vfs_wait_handle(ipc_callid_t rid, ipc_call_t *request)
1168{
1169 bool high_fd = IPC_GET_ARG1(*request);
1170 int fd = vfs_wait_handle_internal(high_fd);
1171 async_answer_1(rid, EOK, fd);
1172}
1173
1174void vfs_get_mtab(ipc_callid_t rid, ipc_call_t *request)
1175{
1176 ipc_callid_t callid;
1177 ipc_call_t data;
1178 sysarg_t rc = EOK;
1179 size_t len;
1180
1181 fibril_mutex_lock(&mtab_list_lock);
1182
1183 /* Send to the caller the number of mounted filesystems */
1184 callid = async_get_call(&data);
1185 if (IPC_GET_IMETHOD(data) != VFS_IN_PING) {
1186 rc = ENOTSUP;
1187 async_answer_0(callid, rc);
1188 goto exit;
1189 }
1190 async_answer_1(callid, EOK, mtab_size);
1191
1192 list_foreach(mtab_list, link, mtab_ent_t, mtab_ent) {
1193 rc = ENOTSUP;
1194
1195 if (!async_data_read_receive(&callid, &len)) {
1196 async_answer_0(callid, rc);
1197 goto exit;
1198 }
1199
1200 (void) async_data_read_finalize(callid, mtab_ent->mp,
1201 str_size(mtab_ent->mp));
1202
1203 if (!async_data_read_receive(&callid, &len)) {
1204 async_answer_0(callid, rc);
1205 goto exit;
1206 }
1207
1208 (void) async_data_read_finalize(callid, mtab_ent->opts,
1209 str_size(mtab_ent->opts));
1210
1211 if (!async_data_read_receive(&callid, &len)) {
1212 async_answer_0(callid, rc);
1213 goto exit;
1214 }
1215
1216 (void) async_data_read_finalize(callid, mtab_ent->fs_name,
1217 str_size(mtab_ent->fs_name));
1218
1219 callid = async_get_call(&data);
1220
1221 if (IPC_GET_IMETHOD(data) != VFS_IN_PING) {
1222 async_answer_0(callid, rc);
1223 goto exit;
1224 }
1225
1226 rc = EOK;
1227 async_answer_2(callid, rc, mtab_ent->instance,
1228 mtab_ent->service_id);
1229 }
1230
1231exit:
1232 fibril_mutex_unlock(&mtab_list_lock);
1233 async_answer_0(rid, rc);
1234}
1235
1236void vfs_statfs(ipc_callid_t rid, ipc_call_t *request)
1237{
1238 int fd = IPC_GET_ARG1(*request);
1239
1240 ipc_callid_t callid;
1241 if (!async_data_read_receive(&callid, NULL)) {
1242 async_answer_0(callid, EINVAL);
1243 async_answer_0(rid, EINVAL);
1244 return;
1245 }
1246
1247 vfs_file_t *file = vfs_file_get(fd);
1248 if (!file) {
1249 async_answer_0(callid, EBADF);
1250 async_answer_0(rid, EBADF);
1251 }
1252
1253 vfs_node_t *node = file->node;
1254
1255 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
1256
1257 aid_t msg;
1258 msg = async_send_3(exch, VFS_OUT_STATFS, node->service_id,
1259 node->index, false, NULL);
1260 async_forward_fast(callid, exch, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
1261
1262 vfs_exchange_release(exch);
1263
1264 sysarg_t rv;
1265 async_wait_for(msg, &rv);
1266
1267 vfs_file_put(file);
1268
1269 async_answer_0(rid, rv);
1270}
1271
1272void vfs_op_clone(ipc_callid_t rid, ipc_call_t *request)
1273{
1274 int oldfd = IPC_GET_ARG1(*request);
1275 bool desc = IPC_GET_ARG2(*request);
1276
1277 /* Lookup the file structure corresponding to fd. */
1278 vfs_file_t *oldfile = vfs_file_get(oldfd);
1279 if (oldfile == NULL) {
1280 async_answer_0(rid, EBADF);
1281 return;
1282 }
1283
1284 vfs_file_t *newfile;
1285 int newfd = vfs_fd_alloc(&newfile, desc);
1286 async_answer_0(rid, newfd);
1287
1288 if (newfd < 0) {
1289 vfs_file_put(oldfile);
1290 return;
1291 }
1292
1293 assert(oldfile->node != NULL);
1294
1295 newfile->node = oldfile->node;
1296 newfile->permissions = oldfile->permissions;
1297 vfs_node_addref(newfile->node);
1298
1299 vfs_file_put(oldfile);
1300 vfs_file_put(newfile);
1301}
1302
1303/**
1304 * @}
1305 */
Note: See TracBrowser for help on using the repository browser.