source: mainline/uspace/srv/vfs/vfs_ops.c@ fcab7ef

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

Merge dup2() into vfs_clone()

  • Property mode set to 100644
File size: 20.5 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
55/* Forward declarations of static functions. */
56static int vfs_truncate_internal(fs_handle_t, service_id_t, fs_index_t,
57 aoff64_t);
58
59/**
60 * This rwlock prevents the race between a triplet-to-VFS-node resolution and a
61 * concurrent VFS operation which modifies the file system namespace.
62 */
63FIBRIL_RWLOCK_INITIALIZE(namespace_rwlock);
64
65static size_t shared_path(char *a, char *b)
66{
67 size_t res = 0;
68
69 while (a[res] == b[res] && a[res] != 0)
70 res++;
71
72 if (a[res] == b[res])
73 return res;
74
75 res--;
76 while (a[res] != '/')
77 res--;
78 return res;
79}
80
81/* This call destroys the file if and only if there are no hard links left. */
82static void out_destroy(vfs_triplet_t *file)
83{
84 async_exch_t *exch = vfs_exchange_grab(file->fs_handle);
85 async_msg_2(exch, VFS_OUT_DESTROY, (sysarg_t) file->service_id,
86 (sysarg_t) file->index);
87 vfs_exchange_release(exch);
88}
89
90int vfs_op_clone(int oldfd, int newfd, bool desc)
91{
92 int rc;
93
94 /* If the file descriptors are the same, do nothing. */
95 if (oldfd == newfd)
96 return EOK;
97
98 /* Lookup the file structure corresponding to fd. */
99 vfs_file_t *oldfile = vfs_file_get(oldfd);
100 if (oldfile == NULL)
101 return EBADF;
102
103 assert(oldfile->node != NULL);
104
105 if (newfd != -1) {
106 /* Make sure newfd is closed. */
107 (void) vfs_fd_free(newfd);
108 /* Assign the old file to newfd. */
109 rc = vfs_fd_assign(oldfile, newfd);
110 } else {
111 vfs_file_t *newfile;
112 int newfd = vfs_fd_alloc(&newfile, desc);
113 if (newfd >= 0) {
114 newfile->node = oldfile->node;
115 newfile->permissions = oldfile->permissions;
116 vfs_node_addref(newfile->node);
117
118 vfs_file_put(newfile);
119 }
120 rc = newfd;
121 }
122 vfs_file_put(oldfile);
123
124 return rc;
125}
126
127int vfs_op_close(int fd)
128{
129 return vfs_fd_free(fd);
130}
131
132int vfs_op_fstat(int fd)
133{
134 vfs_file_t *file = vfs_file_get(fd);
135 if (!file)
136 return EBADF;
137
138 vfs_node_t *node = file->node;
139
140 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
141 int rc = async_data_read_forward_fast(exch, VFS_OUT_STAT,
142 node->service_id, node->index, true, 0, NULL);
143 vfs_exchange_release(exch);
144
145 vfs_file_put(file);
146 return rc;
147}
148
149static int vfs_connect_internal(service_id_t service_id, unsigned flags,
150 unsigned instance, const char *options, const char *fsname,
151 vfs_node_t **root)
152{
153 fs_handle_t fs_handle = 0;
154
155 fibril_mutex_lock(&fs_list_lock);
156 while (true) {
157 fs_handle = fs_name_to_handle(instance, fsname, false);
158
159 if (fs_handle != 0 || !(flags & VFS_MOUNT_BLOCKING))
160 break;
161
162 fibril_condvar_wait(&fs_list_cv, &fs_list_lock);
163 }
164 fibril_mutex_unlock(&fs_list_lock);
165
166 if (fs_handle == 0)
167 return ENOENT;
168
169 /* Tell the mountee that it is being mounted. */
170 ipc_call_t answer;
171 async_exch_t *exch = vfs_exchange_grab(fs_handle);
172 aid_t msg = async_send_1(exch, VFS_OUT_MOUNTED, (sysarg_t) service_id,
173 &answer);
174 /* Send the mount options */
175 sysarg_t rc = async_data_write_start(exch, options, str_size(options));
176 if (rc != EOK) {
177 async_forget(msg);
178 vfs_exchange_release(exch);
179 return rc;
180 }
181
182 async_wait_for(msg, &rc);
183 if (rc != EOK) {
184 vfs_exchange_release(exch);
185 return rc;
186 }
187
188 vfs_lookup_res_t res;
189 res.triplet.fs_handle = fs_handle;
190 res.triplet.service_id = service_id;
191 res.triplet.index = (fs_index_t) IPC_GET_ARG1(answer);
192 res.size = (int64_t) MERGE_LOUP32(IPC_GET_ARG2(answer),
193 IPC_GET_ARG3(answer));
194 res.type = VFS_NODE_DIRECTORY;
195
196 /* Add reference to the mounted root. */
197 *root = vfs_node_get(&res);
198 if (!*root) {
199 aid_t msg = async_send_1(exch, VFS_OUT_UNMOUNTED,
200 (sysarg_t) service_id, NULL);
201 async_forget(msg);
202 vfs_exchange_release(exch);
203 return ENOMEM;
204 }
205
206 vfs_exchange_release(exch);
207
208 return EOK;
209}
210
211int vfs_op_mount(int mpfd, unsigned service_id, unsigned flags,
212 unsigned instance, const char *opts, const char *fs_name, int *outfd)
213{
214 int rc;
215 vfs_file_t *mp = NULL;
216 vfs_file_t *file = NULL;
217 int fd = -1;
218
219 if (!(flags & VFS_MOUNT_CONNECT_ONLY)) {
220 mp = vfs_file_get(mpfd);
221 if (mp == NULL) {
222 rc = EBADF;
223 goto out;
224 }
225
226 if (mp->node->mount != NULL) {
227 rc = EBUSY;
228 goto out;
229 }
230
231 if (mp->node->type != VFS_NODE_DIRECTORY) {
232 rc = ENOTDIR;
233 goto out;
234 }
235
236 if (vfs_node_has_children(mp->node)) {
237 rc = ENOTEMPTY;
238 goto out;
239 }
240 }
241
242 if (!(flags & VFS_MOUNT_NO_REF)) {
243 fd = vfs_fd_alloc(&file, false);
244 if (fd < 0) {
245 rc = fd;
246 goto out;
247 }
248 }
249
250 vfs_node_t *root = NULL;
251
252 fibril_rwlock_write_lock(&namespace_rwlock);
253
254 rc = vfs_connect_internal(service_id, flags, instance, opts, fs_name,
255 &root);
256 if (rc == EOK && !(flags & VFS_MOUNT_CONNECT_ONLY)) {
257 vfs_node_addref(mp->node);
258 vfs_node_addref(root);
259 mp->node->mount = root;
260 }
261
262 fibril_rwlock_write_unlock(&namespace_rwlock);
263
264 if (rc != EOK)
265 goto out;
266
267 if (flags & VFS_MOUNT_NO_REF) {
268 vfs_node_delref(root);
269 } else {
270 assert(file != NULL);
271
272 file->node = root;
273 file->permissions = MODE_READ | MODE_WRITE | MODE_APPEND;
274 file->open_read = false;
275 file->open_write = false;
276 }
277
278out:
279 if (mp)
280 vfs_file_put(mp);
281 if (file)
282 vfs_file_put(file);
283
284 if (rc != EOK && fd >= 0) {
285 vfs_fd_free(fd);
286 fd = 0;
287 }
288
289 *outfd = fd;
290 return rc;
291}
292
293int vfs_op_open2(int fd, int flags)
294{
295 if (flags == 0)
296 return EINVAL;
297
298 vfs_file_t *file = vfs_file_get(fd);
299 if (!file)
300 return EBADF;
301
302 if ((flags & ~file->permissions) != 0) {
303 vfs_file_put(file);
304 return EPERM;
305 }
306
307 if (file->open_read || file->open_write) {
308 vfs_file_put(file);
309 return EBUSY;
310 }
311
312 file->open_read = (flags & MODE_READ) != 0;
313 file->open_write = (flags & (MODE_WRITE | MODE_APPEND)) != 0;
314 file->append = (flags & MODE_APPEND) != 0;
315
316 if (!file->open_read && !file->open_write) {
317 vfs_file_put(file);
318 return EINVAL;
319 }
320
321 if (file->node->type == VFS_NODE_DIRECTORY && file->open_write) {
322 file->open_read = file->open_write = false;
323 vfs_file_put(file);
324 return EINVAL;
325 }
326
327 int rc = vfs_open_node_remote(file->node);
328 if (rc != EOK) {
329 file->open_read = file->open_write = false;
330 vfs_file_put(file);
331 return rc;
332 }
333
334 vfs_file_put(file);
335 return EOK;
336}
337
338typedef int (* rdwr_ipc_cb_t)(async_exch_t *, vfs_file_t *, aoff64_t,
339 ipc_call_t *, bool, void *);
340
341static int rdwr_ipc_client(async_exch_t *exch, vfs_file_t *file, aoff64_t pos,
342 ipc_call_t *answer, bool read, void *data)
343{
344 size_t *bytes = (size_t *) data;
345 int rc;
346
347 /*
348 * Make a VFS_READ/VFS_WRITE request at the destination FS server
349 * and forward the IPC_M_DATA_READ/IPC_M_DATA_WRITE request to the
350 * destination FS server. The call will be routed as if sent by
351 * ourselves. Note that call arguments are immutable in this case so we
352 * don't have to bother.
353 */
354
355 if (read) {
356 rc = async_data_read_forward_4_1(exch, VFS_OUT_READ,
357 file->node->service_id, file->node->index,
358 LOWER32(pos), UPPER32(pos), answer);
359 } else {
360 rc = async_data_write_forward_4_1(exch, VFS_OUT_WRITE,
361 file->node->service_id, file->node->index,
362 LOWER32(pos), UPPER32(pos), answer);
363 }
364
365 *bytes = IPC_GET_ARG1(*answer);
366 return rc;
367}
368
369static int rdwr_ipc_internal(async_exch_t *exch, vfs_file_t *file, aoff64_t pos,
370 ipc_call_t *answer, bool read, void *data)
371{
372 rdwr_io_chunk_t *chunk = (rdwr_io_chunk_t *) data;
373
374 if (exch == NULL)
375 return ENOENT;
376
377 aid_t msg = async_send_fast(exch, read ? VFS_OUT_READ : VFS_OUT_WRITE,
378 file->node->service_id, file->node->index, LOWER32(pos),
379 UPPER32(pos), answer);
380 if (msg == 0)
381 return EINVAL;
382
383 int retval = async_data_read_start(exch, chunk->buffer, chunk->size);
384 if (retval != EOK) {
385 async_forget(msg);
386 return retval;
387 }
388
389 sysarg_t rc;
390 async_wait_for(msg, &rc);
391
392 chunk->size = IPC_GET_ARG1(*answer);
393
394 return (int) rc;
395}
396
397static int vfs_rdwr(int fd, aoff64_t pos, bool read, rdwr_ipc_cb_t ipc_cb,
398 void *ipc_cb_data)
399{
400 /*
401 * The following code strongly depends on the fact that the files data
402 * structure can be only accessed by a single fibril and all file
403 * operations are serialized (i.e. the reads and writes cannot
404 * interleave and a file cannot be closed while it is being read).
405 *
406 * Additional synchronization needs to be added once the table of
407 * open files supports parallel access!
408 */
409
410 /* Lookup the file structure corresponding to the file descriptor. */
411 vfs_file_t *file = vfs_file_get(fd);
412 if (!file)
413 return EBADF;
414
415 if ((read && !file->open_read) || (!read && !file->open_write)) {
416 vfs_file_put(file);
417 return EINVAL;
418 }
419
420 vfs_info_t *fs_info = fs_handle_to_info(file->node->fs_handle);
421 assert(fs_info);
422
423 bool rlock = read ||
424 (fs_info->concurrent_read_write && fs_info->write_retains_size);
425
426 /*
427 * Lock the file's node so that no other client can read/write to it at
428 * the same time unless the FS supports concurrent reads/writes and its
429 * write implementation does not modify the file size.
430 */
431 if (rlock)
432 fibril_rwlock_read_lock(&file->node->contents_rwlock);
433 else
434 fibril_rwlock_write_lock(&file->node->contents_rwlock);
435
436 if (file->node->type == VFS_NODE_DIRECTORY) {
437 /*
438 * Make sure that no one is modifying the namespace
439 * while we are in readdir().
440 */
441
442 if (!read) {
443 if (rlock) {
444 fibril_rwlock_read_unlock(
445 &file->node->contents_rwlock);
446 } else {
447 fibril_rwlock_write_unlock(
448 &file->node->contents_rwlock);
449 }
450 vfs_file_put(file);
451 return EINVAL;
452 }
453
454 fibril_rwlock_read_lock(&namespace_rwlock);
455 }
456
457 async_exch_t *fs_exch = vfs_exchange_grab(file->node->fs_handle);
458
459 if (!read && file->append) {
460 if (file->node->size == -1)
461 file->node->size = vfs_node_get_size(file->node);
462 pos = file->node->size;
463 }
464
465 /*
466 * Handle communication with the endpoint FS.
467 */
468 ipc_call_t answer;
469 int rc = ipc_cb(fs_exch, file, pos, &answer, read, ipc_cb_data);
470
471 vfs_exchange_release(fs_exch);
472
473 if (file->node->type == VFS_NODE_DIRECTORY)
474 fibril_rwlock_read_unlock(&namespace_rwlock);
475
476 /* Unlock the VFS node. */
477 if (rlock) {
478 fibril_rwlock_read_unlock(&file->node->contents_rwlock);
479 } else {
480 /* Update the cached version of node's size. */
481 if (rc == EOK) {
482 file->node->size = MERGE_LOUP32(IPC_GET_ARG2(answer),
483 IPC_GET_ARG3(answer));
484 }
485 fibril_rwlock_write_unlock(&file->node->contents_rwlock);
486 }
487
488 vfs_file_put(file);
489
490 return rc;
491}
492
493int vfs_rdwr_internal(int fd, aoff64_t pos, bool read, rdwr_io_chunk_t *chunk)
494{
495 return vfs_rdwr(fd, pos, read, rdwr_ipc_internal, chunk);
496}
497
498int vfs_op_read(int fd, aoff64_t pos, size_t *out_bytes)
499{
500 return vfs_rdwr(fd, pos, true, rdwr_ipc_client, out_bytes);
501}
502
503int vfs_op_rename(int basefd, char *old, char *new)
504{
505 vfs_file_t *base_file = vfs_file_get(basefd);
506 if (!base_file)
507 return EBADF;
508
509 vfs_node_t *base = base_file->node;
510 vfs_node_addref(base);
511 vfs_file_put(base_file);
512
513 vfs_lookup_res_t base_lr;
514 vfs_lookup_res_t old_lr;
515 vfs_lookup_res_t new_lr_orig;
516 bool orig_unlinked = false;
517
518 int rc;
519
520 size_t shared = shared_path(old, new);
521
522 /* Do not allow one path to be a prefix of the other. */
523 if (old[shared] == 0 || new[shared] == 0) {
524 vfs_node_put(base);
525 return EINVAL;
526 }
527 assert(old[shared] == '/');
528 assert(new[shared] == '/');
529
530 fibril_rwlock_write_lock(&namespace_rwlock);
531
532 /* Resolve the shared portion of the path first. */
533 if (shared != 0) {
534 old[shared] = 0;
535 rc = vfs_lookup_internal(base, old, L_DIRECTORY, &base_lr);
536 if (rc != EOK) {
537 vfs_node_put(base);
538 fibril_rwlock_write_unlock(&namespace_rwlock);
539 return rc;
540 }
541
542 vfs_node_put(base);
543 base = vfs_node_get(&base_lr);
544 if (!base) {
545 fibril_rwlock_write_unlock(&namespace_rwlock);
546 return ENOMEM;
547 }
548 old[shared] = '/';
549 old += shared;
550 new += shared;
551 }
552
553 rc = vfs_lookup_internal(base, old, L_DISABLE_MOUNTS, &old_lr);
554 if (rc != EOK) {
555 vfs_node_put(base);
556 fibril_rwlock_write_unlock(&namespace_rwlock);
557 return rc;
558 }
559
560 rc = vfs_lookup_internal(base, new, L_UNLINK | L_DISABLE_MOUNTS,
561 &new_lr_orig);
562 if (rc == EOK) {
563 orig_unlinked = true;
564 } else if (rc != ENOENT) {
565 vfs_node_put(base);
566 fibril_rwlock_write_unlock(&namespace_rwlock);
567 return rc;
568 }
569
570 rc = vfs_link_internal(base, new, &old_lr.triplet);
571 if (rc != EOK) {
572 vfs_link_internal(base, old, &old_lr.triplet);
573 if (orig_unlinked)
574 vfs_link_internal(base, new, &new_lr_orig.triplet);
575 vfs_node_put(base);
576 fibril_rwlock_write_unlock(&namespace_rwlock);
577 return rc;
578 }
579
580 rc = vfs_lookup_internal(base, old, L_UNLINK | L_DISABLE_MOUNTS,
581 &old_lr);
582 if (rc != EOK) {
583 if (orig_unlinked)
584 vfs_link_internal(base, new, &new_lr_orig.triplet);
585 vfs_node_put(base);
586 fibril_rwlock_write_unlock(&namespace_rwlock);
587 return rc;
588 }
589
590 /* If the node is not held by anyone, try to destroy it. */
591 if (orig_unlinked) {
592 vfs_node_t *node = vfs_node_peek(&new_lr_orig);
593 if (!node)
594 out_destroy(&new_lr_orig.triplet);
595 else
596 vfs_node_put(node);
597 }
598
599 vfs_node_put(base);
600 fibril_rwlock_write_unlock(&namespace_rwlock);
601 return EOK;
602}
603
604int vfs_op_statfs(int fd)
605{
606 vfs_file_t *file = vfs_file_get(fd);
607 if (!file)
608 return EBADF;
609
610 vfs_node_t *node = file->node;
611
612 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
613 int rc = async_data_read_forward_fast(exch, VFS_OUT_STATFS,
614 node->service_id, node->index, false, 0, NULL);
615 vfs_exchange_release(exch);
616
617 vfs_file_put(file);
618 return rc;
619}
620
621int vfs_op_sync(int fd)
622{
623 vfs_file_t *file = vfs_file_get(fd);
624 if (!file)
625 return EBADF;
626
627 async_exch_t *fs_exch = vfs_exchange_grab(file->node->fs_handle);
628
629 aid_t msg;
630 ipc_call_t answer;
631 msg = async_send_2(fs_exch, VFS_OUT_SYNC, file->node->service_id,
632 file->node->index, &answer);
633
634 vfs_exchange_release(fs_exch);
635
636 sysarg_t rc;
637 async_wait_for(msg, &rc);
638
639 vfs_file_put(file);
640 return rc;
641
642}
643
644static int vfs_truncate_internal(fs_handle_t fs_handle, service_id_t service_id,
645 fs_index_t index, aoff64_t size)
646{
647 async_exch_t *exch = vfs_exchange_grab(fs_handle);
648 sysarg_t rc = async_req_4_0(exch, VFS_OUT_TRUNCATE,
649 (sysarg_t) service_id, (sysarg_t) index, LOWER32(size),
650 UPPER32(size));
651 vfs_exchange_release(exch);
652
653 return (int) rc;
654}
655
656int vfs_op_truncate(int fd, int64_t size)
657{
658 vfs_file_t *file = vfs_file_get(fd);
659 if (!file)
660 return EBADF;
661
662 fibril_rwlock_write_lock(&file->node->contents_rwlock);
663
664 int rc = vfs_truncate_internal(file->node->fs_handle,
665 file->node->service_id, file->node->index, size);
666 if (rc == EOK)
667 file->node->size = size;
668
669 fibril_rwlock_write_unlock(&file->node->contents_rwlock);
670 vfs_file_put(file);
671 return rc;
672}
673
674int vfs_op_unlink2(int parentfd, int expectfd, int wflag, char *path)
675{
676 int rc = EOK;
677 vfs_file_t *parent = NULL;
678 vfs_file_t *expect = NULL;
679
680 if (parentfd == expectfd)
681 return EINVAL;
682
683 fibril_rwlock_write_lock(&namespace_rwlock);
684
685 int lflag = (wflag & WALK_DIRECTORY) ? L_DIRECTORY: 0;
686
687 /*
688 * Files are retrieved in order of file descriptors, to prevent
689 * deadlock.
690 */
691 if (parentfd < expectfd) {
692 parent = vfs_file_get(parentfd);
693 if (!parent) {
694 rc = EBADF;
695 goto exit;
696 }
697 }
698
699 if (expectfd >= 0) {
700 expect = vfs_file_get(expectfd);
701 if (!expect) {
702 rc = EBADF;
703 goto exit;
704 }
705 }
706
707 if (parentfd > expectfd) {
708 parent = vfs_file_get(parentfd);
709 if (!parent) {
710 rc = EBADF;
711 goto exit;
712 }
713 }
714
715 assert(parent != NULL);
716
717 if (expectfd >= 0) {
718 vfs_lookup_res_t lr;
719 rc = vfs_lookup_internal(parent->node, path, lflag, &lr);
720 if (rc != EOK)
721 goto exit;
722
723 vfs_node_t *found_node = vfs_node_peek(&lr);
724 vfs_node_put(found_node);
725 if (expect->node != found_node) {
726 rc = ENOENT;
727 goto exit;
728 }
729
730 vfs_file_put(expect);
731 expect = NULL;
732 }
733
734 vfs_lookup_res_t lr;
735 rc = vfs_lookup_internal(parent->node, path, lflag | L_UNLINK, &lr);
736 if (rc != EOK)
737 goto exit;
738
739 /* If the node is not held by anyone, try to destroy it. */
740 vfs_node_t *node = vfs_node_peek(&lr);
741 if (!node)
742 out_destroy(&lr.triplet);
743 else
744 vfs_node_put(node);
745
746exit:
747 if (path)
748 free(path);
749 if (parent)
750 vfs_file_put(parent);
751 if (expect)
752 vfs_file_put(expect);
753 fibril_rwlock_write_unlock(&namespace_rwlock);
754 return rc;
755}
756
757int vfs_op_unmount(int mpfd)
758{
759 vfs_file_t *mp = vfs_file_get(mpfd);
760 if (mp == NULL)
761 return EBADF;
762
763 if (mp->node->mount == NULL) {
764 vfs_file_put(mp);
765 return ENOENT;
766 }
767
768 fibril_rwlock_write_lock(&namespace_rwlock);
769
770 /*
771 * Count the total number of references for the mounted file system. We
772 * are expecting at least one, which is held by the mount point.
773 * If we find more, it means that
774 * the file system cannot be gracefully unmounted at the moment because
775 * someone is working with it.
776 */
777 if (vfs_nodes_refcount_sum_get(mp->node->mount->fs_handle,
778 mp->node->mount->service_id) != 1) {
779 vfs_file_put(mp);
780 fibril_rwlock_write_unlock(&namespace_rwlock);
781 return EBUSY;
782 }
783
784 async_exch_t *exch = vfs_exchange_grab(mp->node->mount->fs_handle);
785 int rc = async_req_1_0(exch, VFS_OUT_UNMOUNTED,
786 mp->node->mount->service_id);
787 vfs_exchange_release(exch);
788
789 if (rc != EOK) {
790 vfs_file_put(mp);
791 fibril_rwlock_write_unlock(&namespace_rwlock);
792 return rc;
793 }
794
795 vfs_node_forget(mp->node->mount);
796 vfs_node_put(mp->node);
797 mp->node->mount = NULL;
798
799 fibril_rwlock_write_unlock(&namespace_rwlock);
800
801 vfs_file_put(mp);
802 return EOK;
803}
804
805int vfs_op_wait_handle(bool high_fd)
806{
807 return vfs_wait_handle_internal(high_fd);
808}
809
810static inline bool walk_flags_valid(int flags)
811{
812 if ((flags & ~WALK_ALL_FLAGS) != 0)
813 return false;
814 if ((flags & WALK_MAY_CREATE) && (flags & WALK_MUST_CREATE))
815 return false;
816 if ((flags & WALK_REGULAR) && (flags & WALK_DIRECTORY))
817 return false;
818 if ((flags & WALK_MAY_CREATE) || (flags & WALK_MUST_CREATE)) {
819 if (!(flags & WALK_DIRECTORY) && !(flags & WALK_REGULAR))
820 return false;
821 }
822 return true;
823}
824
825static inline int walk_lookup_flags(int flags)
826{
827 int lflags = 0;
828 if ((flags & WALK_MAY_CREATE) || (flags & WALK_MUST_CREATE))
829 lflags |= L_CREATE;
830 if (flags & WALK_MUST_CREATE)
831 lflags |= L_EXCLUSIVE;
832 if (flags & WALK_REGULAR)
833 lflags |= L_FILE;
834 if (flags & WALK_DIRECTORY)
835 lflags |= L_DIRECTORY;
836 if (flags & WALK_MOUNT_POINT)
837 lflags |= L_MP;
838 return lflags;
839}
840
841int vfs_op_walk(int parentfd, int flags, char *path, int *out_fd)
842{
843 if (!walk_flags_valid(flags))
844 return EINVAL;
845
846 vfs_file_t *parent = vfs_file_get(parentfd);
847 if (!parent)
848 return EBADF;
849
850 fibril_rwlock_read_lock(&namespace_rwlock);
851
852 vfs_lookup_res_t lr;
853 int rc = vfs_lookup_internal(parent->node, path,
854 walk_lookup_flags(flags), &lr);
855 if (rc != EOK) {
856 fibril_rwlock_read_unlock(&namespace_rwlock);
857 vfs_file_put(parent);
858 return rc;
859 }
860
861 vfs_node_t *node = vfs_node_get(&lr);
862 if (!node) {
863 fibril_rwlock_read_unlock(&namespace_rwlock);
864 vfs_file_put(parent);
865 return ENOMEM;
866 }
867
868 vfs_file_t *file;
869 int fd = vfs_fd_alloc(&file, false);
870 if (fd < 0) {
871 vfs_node_put(node);
872 vfs_file_put(parent);
873 return fd;
874 }
875 assert(file != NULL);
876
877 file->node = node;
878 file->permissions = parent->permissions;
879 file->open_read = false;
880 file->open_write = false;
881
882 vfs_file_put(file);
883 vfs_file_put(parent);
884
885 fibril_rwlock_read_unlock(&namespace_rwlock);
886
887 *out_fd = fd;
888 return EOK;
889}
890
891int vfs_op_write(int fd, aoff64_t pos, size_t *out_bytes)
892{
893 return vfs_rdwr(fd, pos, false, rdwr_ipc_client, out_bytes);
894}
895
896/**
897 * @}
898 */
Note: See TracBrowser for help on using the repository browser.