source: mainline/uspace/srv/vfs/vfs_ops.c@ 63d46341

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 63d46341 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

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