source: mainline/uspace/srv/vfs/vfs_ops.c@ 64b67c3

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

Make VFS_MOUNT call even when mounting the root file system.

  • Property mode set to 100644
File size: 21.6 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 <futex.h>
47#include <rwlock.h>
48#include <libadt/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, dev_handle_t, fs_index_t, size_t);
57
58/**
59 * This rwlock prevents the race between a triplet-to-VFS-node resolution and a
60 * concurrent VFS operation which modifies the file system namespace.
61 */
62RWLOCK_INITIALIZE(namespace_rwlock);
63
64futex_t rootfs_futex = FUTEX_INITIALIZER;
65vfs_triplet_t rootfs = {
66 .fs_handle = 0,
67 .dev_handle = 0,
68 .index = 0,
69};
70
71static int
72lookup_root(fs_handle_t fs_handle, dev_handle_t dev_handle,
73 vfs_lookup_res_t *result)
74{
75 vfs_pair_t altroot = {
76 .fs_handle = fs_handle,
77 .dev_handle = dev_handle,
78 };
79
80 return vfs_lookup_internal("/", L_DIRECTORY, result, &altroot);
81}
82
83void vfs_mount(ipc_callid_t rid, ipc_call_t *request)
84{
85 dev_handle_t dev_handle;
86 vfs_node_t *mp_node = NULL;
87 int rc;
88 int phone;
89
90 /*
91 * We expect the library to do the device-name to device-handle
92 * translation for us, thus the device handle will arrive as ARG1
93 * in the request.
94 */
95 dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
96
97 /*
98 * For now, don't make use of ARG2 and ARG3, but they can be used to
99 * carry mount options in the future.
100 */
101
102 ipc_callid_t callid;
103 size_t size;
104
105 /*
106 * Now, we expect the client to send us data with the name of the file
107 * system.
108 */
109 if (!ipc_data_write_receive(&callid, &size)) {
110 ipc_answer_0(callid, EINVAL);
111 ipc_answer_0(rid, EINVAL);
112 return;
113 }
114
115 /*
116 * Don't receive more than is necessary for storing a full file system
117 * name.
118 */
119 if (size < 1 || size > FS_NAME_MAXLEN) {
120 ipc_answer_0(callid, EINVAL);
121 ipc_answer_0(rid, EINVAL);
122 return;
123 }
124
125 /* Deliver the file system name. */
126 char fs_name[FS_NAME_MAXLEN + 1];
127 (void) ipc_data_write_finalize(callid, fs_name, size);
128 fs_name[size] = '\0';
129
130 /*
131 * Check if we know a file system with the same name as is in fs_name.
132 * This will also give us its file system handle.
133 */
134 fs_handle_t fs_handle = fs_name_to_handle(fs_name, true);
135 if (!fs_handle) {
136 ipc_answer_0(rid, ENOENT);
137 return;
138 }
139
140 /* Now, we want the client to send us the mount point. */
141 if (!ipc_data_write_receive(&callid, &size)) {
142 ipc_answer_0(callid, EINVAL);
143 ipc_answer_0(rid, EINVAL);
144 return;
145 }
146
147 /* Check whether size is reasonable wrt. the mount point. */
148 if (size < 1 || size > MAX_PATH_LEN) {
149 ipc_answer_0(callid, EINVAL);
150 ipc_answer_0(rid, EINVAL);
151 return;
152 }
153 /* Allocate buffer for the mount point data being received. */
154 uint8_t *buf;
155 buf = malloc(size + 1);
156 if (!buf) {
157 ipc_answer_0(callid, ENOMEM);
158 ipc_answer_0(rid, ENOMEM);
159 return;
160 }
161
162 /* Deliver the mount point. */
163 (void) ipc_data_write_finalize(callid, buf, size);
164 buf[size] = '\0';
165
166 /*
167 * Lookup the root node of the filesystem being mounted.
168 * In this case, we don't need to take the namespace_futex as the root
169 * node cannot be removed. However, we do take a reference to it so
170 * that we can track how many times it has been mounted.
171 */
172 vfs_lookup_res_t mr_res;
173 rc = lookup_root(fs_handle, dev_handle, &mr_res);
174 if (rc != EOK) {
175 free(buf);
176 ipc_answer_0(rid, rc);
177 return;
178 }
179 vfs_node_t *mr_node = vfs_node_get(&mr_res);
180 if (!mr_node) {
181 free(buf);
182 ipc_answer_0(rid, ENOMEM);
183 return;
184 }
185
186 /* Finally, we need to resolve the path to the mountpoint. */
187 vfs_lookup_res_t mp_res;
188 futex_down(&rootfs_futex);
189 if (rootfs.fs_handle) {
190 /* We already have the root FS. */
191 rwlock_write_lock(&namespace_rwlock);
192 if ((size == 1) && (buf[0] == '/')) {
193 /* Trying to mount root FS over root FS */
194 rwlock_write_unlock(&namespace_rwlock);
195 futex_up(&rootfs_futex);
196 vfs_node_put(mr_node);
197 free(buf);
198 ipc_answer_0(rid, EBUSY);
199 return;
200 }
201 rc = vfs_lookup_internal(buf, L_DIRECTORY, &mp_res, NULL);
202 if (rc != EOK) {
203 /* The lookup failed for some reason. */
204 rwlock_write_unlock(&namespace_rwlock);
205 futex_up(&rootfs_futex);
206 vfs_node_put(mr_node); /* failed -> drop reference */
207 free(buf);
208 ipc_answer_0(rid, rc);
209 return;
210 }
211 mp_node = vfs_node_get(&mp_res);
212 if (!mp_node) {
213 rwlock_write_unlock(&namespace_rwlock);
214 futex_up(&rootfs_futex);
215 vfs_node_put(mr_node); /* failed -> drop reference */
216 free(buf);
217 ipc_answer_0(rid, ENOMEM);
218 return;
219 }
220 /*
221 * Now we hold a reference to mp_node.
222 * It will be dropped upon the corresponding VFS_UNMOUNT.
223 * This prevents the mount point from being deleted.
224 */
225 rwlock_write_unlock(&namespace_rwlock);
226 } else {
227 /* We still don't have the root file system mounted. */
228 if ((size == 1) && (buf[0] == '/')) {
229 /*
230 * For this simple, but important case,
231 * we are almost done.
232 */
233 free(buf);
234
235 /* Inform the mount point about the root mount. */
236 phone = vfs_grab_phone(mr_res.triplet.fs_handle);
237 rc = async_req_5_0(phone, VFS_MOUNT,
238 (ipcarg_t) mr_res.triplet.dev_handle,
239 (ipcarg_t) mr_res.triplet.index,
240 (ipcarg_t) mr_res.triplet.fs_handle,
241 (ipcarg_t) mr_res.triplet.dev_handle,
242 (ipcarg_t) mr_res.triplet.index);
243 vfs_release_phone(phone);
244
245 if (rc == EOK)
246 rootfs = mr_res.triplet;
247 else
248 vfs_node_put(mr_node);
249
250 futex_up(&rootfs_futex);
251 ipc_answer_0(rid, rc);
252 return;
253 } else {
254 /*
255 * We can't resolve this without the root filesystem
256 * being mounted first.
257 */
258 futex_up(&rootfs_futex);
259 free(buf);
260 vfs_node_put(mr_node); /* failed -> drop reference */
261 ipc_answer_0(rid, ENOENT);
262 return;
263 }
264 }
265 futex_up(&rootfs_futex);
266
267 free(buf); /* The buffer is not needed anymore. */
268
269 /*
270 * At this point, we have all necessary pieces: file system and device
271 * handles, and we know the mount point VFS node and also the root node
272 * of the file system being mounted.
273 */
274
275 /**
276 * @todo
277 * Add more IPC parameters so that we can send mount mode/flags.
278 */
279 phone = vfs_grab_phone(mp_res.triplet.fs_handle);
280 rc = async_req_5_0(phone, VFS_MOUNT,
281 (ipcarg_t) mp_res.triplet.dev_handle,
282 (ipcarg_t) mp_res.triplet.index,
283 (ipcarg_t) mr_res.triplet.fs_handle,
284 (ipcarg_t) mr_res.triplet.dev_handle,
285 (ipcarg_t) mr_res.triplet.index);
286 vfs_release_phone(phone);
287
288 if (rc != EOK) {
289 /* Mount failed, drop references to mr_node and mp_node. */
290 vfs_node_put(mr_node);
291 if (mp_node)
292 vfs_node_put(mp_node);
293 }
294
295 ipc_answer_0(rid, rc);
296}
297
298void vfs_open(ipc_callid_t rid, ipc_call_t *request)
299{
300 if (!vfs_files_init()) {
301 ipc_answer_0(rid, ENOMEM);
302 return;
303 }
304
305 /*
306 * The POSIX interface is open(path, oflag, mode).
307 * We can receive oflags and mode along with the VFS_OPEN call; the path
308 * will need to arrive in another call.
309 *
310 * We also receive one private, non-POSIX set of flags called lflag
311 * used to pass information to vfs_lookup_internal().
312 */
313 int lflag = IPC_GET_ARG1(*request);
314 int oflag = IPC_GET_ARG2(*request);
315 int mode = IPC_GET_ARG3(*request);
316 size_t len;
317
318 if (oflag & O_CREAT)
319 lflag |= L_CREATE;
320 if (oflag & O_EXCL)
321 lflag |= L_EXCLUSIVE;
322
323 ipc_callid_t callid;
324
325 if (!ipc_data_write_receive(&callid, &len)) {
326 ipc_answer_0(callid, EINVAL);
327 ipc_answer_0(rid, EINVAL);
328 return;
329 }
330 char *path = malloc(len + 1);
331 if (!path) {
332 ipc_answer_0(callid, ENOMEM);
333 ipc_answer_0(rid, ENOMEM);
334 return;
335 }
336 int rc;
337 if ((rc = ipc_data_write_finalize(callid, path, len))) {
338 ipc_answer_0(rid, rc);
339 free(path);
340 return;
341 }
342 path[len] = '\0';
343
344 /*
345 * Avoid the race condition in which the file can be deleted before we
346 * find/create-and-lock the VFS node corresponding to the looked-up
347 * triplet.
348 */
349 if (lflag & L_CREATE)
350 rwlock_write_lock(&namespace_rwlock);
351 else
352 rwlock_read_lock(&namespace_rwlock);
353
354 /* The path is now populated and we can call vfs_lookup_internal(). */
355 vfs_lookup_res_t lr;
356 rc = vfs_lookup_internal(path, lflag, &lr, NULL);
357 if (rc) {
358 if (lflag & L_CREATE)
359 rwlock_write_unlock(&namespace_rwlock);
360 else
361 rwlock_read_unlock(&namespace_rwlock);
362 ipc_answer_0(rid, rc);
363 free(path);
364 return;
365 }
366
367 /* Path is no longer needed. */
368 free(path);
369
370 vfs_node_t *node = vfs_node_get(&lr);
371 if (lflag & L_CREATE)
372 rwlock_write_unlock(&namespace_rwlock);
373 else
374 rwlock_read_unlock(&namespace_rwlock);
375
376 /* Truncate the file if requested and if necessary. */
377 if (oflag & O_TRUNC) {
378 rwlock_write_lock(&node->contents_rwlock);
379 if (node->size) {
380 rc = vfs_truncate_internal(node->fs_handle,
381 node->dev_handle, node->index, 0);
382 if (rc) {
383 rwlock_write_unlock(&node->contents_rwlock);
384 vfs_node_put(node);
385 ipc_answer_0(rid, rc);
386 return;
387 }
388 node->size = 0;
389 }
390 rwlock_write_unlock(&node->contents_rwlock);
391 }
392
393 /*
394 * Get ourselves a file descriptor and the corresponding vfs_file_t
395 * structure.
396 */
397 int fd = vfs_fd_alloc();
398 if (fd < 0) {
399 vfs_node_put(node);
400 ipc_answer_0(rid, fd);
401 return;
402 }
403 vfs_file_t *file = vfs_file_get(fd);
404 file->node = node;
405 if (oflag & O_APPEND)
406 file->append = true;
407
408 /*
409 * The following increase in reference count is for the fact that the
410 * file is being opened and that a file structure is pointing to it.
411 * It is necessary so that the file will not disappear when
412 * vfs_node_put() is called. The reference will be dropped by the
413 * respective VFS_CLOSE.
414 */
415 vfs_node_addref(node);
416 vfs_node_put(node);
417
418 /* Success! Return the new file descriptor to the client. */
419 ipc_answer_1(rid, EOK, fd);
420}
421
422void vfs_close(ipc_callid_t rid, ipc_call_t *request)
423{
424 int fd = IPC_GET_ARG1(*request);
425 if (fd >= MAX_OPEN_FILES) {
426 ipc_answer_0(rid, EBADF);
427 return;
428 }
429 vfs_fd_free(fd);
430 ipc_answer_0(rid, EOK);
431}
432
433static void vfs_rdwr(ipc_callid_t rid, ipc_call_t *request, bool read)
434{
435
436 /*
437 * The following code strongly depends on the fact that the files data
438 * structure can be only accessed by a single fibril and all file
439 * operations are serialized (i.e. the reads and writes cannot
440 * interleave and a file cannot be closed while it is being read).
441 *
442 * Additional synchronization needs to be added once the table of
443 * open files supports parallel access!
444 */
445
446 int fd = IPC_GET_ARG1(*request);
447
448 /* Lookup the file structure corresponding to the file descriptor. */
449 vfs_file_t *file = vfs_file_get(fd);
450 if (!file) {
451 ipc_answer_0(rid, ENOENT);
452 return;
453 }
454
455 /*
456 * Now we need to receive a call with client's
457 * IPC_M_DATA_READ/IPC_M_DATA_WRITE request.
458 */
459 ipc_callid_t callid;
460 int res;
461 if (read)
462 res = ipc_data_read_receive(&callid, NULL);
463 else
464 res = ipc_data_write_receive(&callid, NULL);
465 if (!res) {
466 ipc_answer_0(callid, EINVAL);
467 ipc_answer_0(rid, EINVAL);
468 return;
469 }
470
471 /*
472 * Lock the open file structure so that no other thread can manipulate
473 * the same open file at a time.
474 */
475 futex_down(&file->lock);
476
477 /*
478 * Lock the file's node so that no other client can read/write to it at
479 * the same time.
480 */
481 if (read)
482 rwlock_read_lock(&file->node->contents_rwlock);
483 else
484 rwlock_write_lock(&file->node->contents_rwlock);
485
486 int fs_phone = vfs_grab_phone(file->node->fs_handle);
487
488 /* Make a VFS_READ/VFS_WRITE request at the destination FS server. */
489 aid_t msg;
490 ipc_call_t answer;
491 if (!read && file->append)
492 file->pos = file->node->size;
493 msg = async_send_3(fs_phone, IPC_GET_METHOD(*request),
494 file->node->dev_handle, file->node->index, file->pos, &answer);
495
496 /*
497 * Forward the IPC_M_DATA_READ/IPC_M_DATA_WRITE request to the
498 * destination FS server. The call will be routed as if sent by
499 * ourselves. Note that call arguments are immutable in this case so we
500 * don't have to bother.
501 */
502 ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
503
504 vfs_release_phone(fs_phone);
505
506 /* Wait for reply from the FS server. */
507 ipcarg_t rc;
508 async_wait_for(msg, &rc);
509 size_t bytes = IPC_GET_ARG1(answer);
510
511 /* Unlock the VFS node. */
512 if (read)
513 rwlock_read_unlock(&file->node->contents_rwlock);
514 else {
515 /* Update the cached version of node's size. */
516 if (rc == EOK)
517 file->node->size = IPC_GET_ARG2(answer);
518 rwlock_write_unlock(&file->node->contents_rwlock);
519 }
520
521 /* Update the position pointer and unlock the open file. */
522 if (rc == EOK)
523 file->pos += bytes;
524 futex_up(&file->lock);
525
526 /*
527 * FS server's reply is the final result of the whole operation we
528 * return to the client.
529 */
530 ipc_answer_1(rid, rc, bytes);
531}
532
533void vfs_read(ipc_callid_t rid, ipc_call_t *request)
534{
535 vfs_rdwr(rid, request, true);
536}
537
538void vfs_write(ipc_callid_t rid, ipc_call_t *request)
539{
540 vfs_rdwr(rid, request, false);
541}
542
543void vfs_seek(ipc_callid_t rid, ipc_call_t *request)
544{
545 int fd = (int) IPC_GET_ARG1(*request);
546 off_t off = (off_t) IPC_GET_ARG2(*request);
547 int whence = (int) IPC_GET_ARG3(*request);
548
549
550 /* Lookup the file structure corresponding to the file descriptor. */
551 vfs_file_t *file = vfs_file_get(fd);
552 if (!file) {
553 ipc_answer_0(rid, ENOENT);
554 return;
555 }
556
557 off_t newpos;
558 futex_down(&file->lock);
559 if (whence == SEEK_SET) {
560 file->pos = off;
561 futex_up(&file->lock);
562 ipc_answer_1(rid, EOK, off);
563 return;
564 }
565 if (whence == SEEK_CUR) {
566 if (file->pos + off < file->pos) {
567 futex_up(&file->lock);
568 ipc_answer_0(rid, EOVERFLOW);
569 return;
570 }
571 file->pos += off;
572 newpos = file->pos;
573 futex_up(&file->lock);
574 ipc_answer_1(rid, EOK, newpos);
575 return;
576 }
577 if (whence == SEEK_END) {
578 rwlock_read_lock(&file->node->contents_rwlock);
579 size_t size = file->node->size;
580 rwlock_read_unlock(&file->node->contents_rwlock);
581 if (size + off < size) {
582 futex_up(&file->lock);
583 ipc_answer_0(rid, EOVERFLOW);
584 return;
585 }
586 newpos = size + off;
587 futex_up(&file->lock);
588 ipc_answer_1(rid, EOK, newpos);
589 return;
590 }
591 futex_up(&file->lock);
592 ipc_answer_0(rid, EINVAL);
593}
594
595int
596vfs_truncate_internal(fs_handle_t fs_handle, dev_handle_t dev_handle,
597 fs_index_t index, size_t size)
598{
599 ipcarg_t rc;
600 int fs_phone;
601
602 fs_phone = vfs_grab_phone(fs_handle);
603 rc = async_req_3_0(fs_phone, VFS_TRUNCATE, (ipcarg_t)dev_handle,
604 (ipcarg_t)index, (ipcarg_t)size);
605 vfs_release_phone(fs_phone);
606 return (int)rc;
607}
608
609void vfs_truncate(ipc_callid_t rid, ipc_call_t *request)
610{
611 int fd = IPC_GET_ARG1(*request);
612 size_t size = IPC_GET_ARG2(*request);
613 int rc;
614
615 vfs_file_t *file = vfs_file_get(fd);
616 if (!file) {
617 ipc_answer_0(rid, ENOENT);
618 return;
619 }
620 futex_down(&file->lock);
621
622 rwlock_write_lock(&file->node->contents_rwlock);
623 rc = vfs_truncate_internal(file->node->fs_handle,
624 file->node->dev_handle, file->node->index, size);
625 if (rc == EOK)
626 file->node->size = size;
627 rwlock_write_unlock(&file->node->contents_rwlock);
628
629 futex_up(&file->lock);
630 ipc_answer_0(rid, (ipcarg_t)rc);
631}
632
633void vfs_mkdir(ipc_callid_t rid, ipc_call_t *request)
634{
635 int mode = IPC_GET_ARG1(*request);
636
637 size_t len;
638 ipc_callid_t callid;
639
640 if (!ipc_data_write_receive(&callid, &len)) {
641 ipc_answer_0(callid, EINVAL);
642 ipc_answer_0(rid, EINVAL);
643 return;
644 }
645 char *path = malloc(len + 1);
646 if (!path) {
647 ipc_answer_0(callid, ENOMEM);
648 ipc_answer_0(rid, ENOMEM);
649 return;
650 }
651 int rc;
652 if ((rc = ipc_data_write_finalize(callid, path, len))) {
653 ipc_answer_0(rid, rc);
654 free(path);
655 return;
656 }
657 path[len] = '\0';
658
659 rwlock_write_lock(&namespace_rwlock);
660 int lflag = L_DIRECTORY | L_CREATE | L_EXCLUSIVE;
661 rc = vfs_lookup_internal(path, lflag, NULL, NULL);
662 rwlock_write_unlock(&namespace_rwlock);
663 free(path);
664 ipc_answer_0(rid, rc);
665}
666
667void vfs_unlink(ipc_callid_t rid, ipc_call_t *request)
668{
669 int lflag = IPC_GET_ARG1(*request);
670
671 size_t len;
672 ipc_callid_t callid;
673
674 if (!ipc_data_write_receive(&callid, &len)) {
675 ipc_answer_0(callid, EINVAL);
676 ipc_answer_0(rid, EINVAL);
677 return;
678 }
679 char *path = malloc(len + 1);
680 if (!path) {
681 ipc_answer_0(callid, ENOMEM);
682 ipc_answer_0(rid, ENOMEM);
683 return;
684 }
685 int rc;
686 if ((rc = ipc_data_write_finalize(callid, path, len))) {
687 ipc_answer_0(rid, rc);
688 free(path);
689 return;
690 }
691 path[len] = '\0';
692
693 rwlock_write_lock(&namespace_rwlock);
694 lflag &= L_DIRECTORY; /* sanitize lflag */
695 vfs_lookup_res_t lr;
696 rc = vfs_lookup_internal(path, lflag | L_UNLINK, &lr, NULL);
697 free(path);
698 if (rc != EOK) {
699 rwlock_write_unlock(&namespace_rwlock);
700 ipc_answer_0(rid, rc);
701 return;
702 }
703
704 /*
705 * The name has already been unlinked by vfs_lookup_internal().
706 * We have to get and put the VFS node to ensure that it is
707 * VFS_DESTROY'ed after the last reference to it is dropped.
708 */
709 vfs_node_t *node = vfs_node_get(&lr);
710 futex_down(&nodes_futex);
711 node->lnkcnt--;
712 futex_up(&nodes_futex);
713 rwlock_write_unlock(&namespace_rwlock);
714 vfs_node_put(node);
715 ipc_answer_0(rid, EOK);
716}
717
718void vfs_rename(ipc_callid_t rid, ipc_call_t *request)
719{
720 size_t len;
721 ipc_callid_t callid;
722 int rc;
723
724 /* Retrieve the old path. */
725 if (!ipc_data_write_receive(&callid, &len)) {
726 ipc_answer_0(callid, EINVAL);
727 ipc_answer_0(rid, EINVAL);
728 return;
729 }
730 char *old = malloc(len + 1);
731 if (!old) {
732 ipc_answer_0(callid, ENOMEM);
733 ipc_answer_0(rid, ENOMEM);
734 return;
735 }
736 if ((rc = ipc_data_write_finalize(callid, old, len))) {
737 ipc_answer_0(rid, rc);
738 free(old);
739 return;
740 }
741 old[len] = '\0';
742
743 /* Retrieve the new path. */
744 if (!ipc_data_write_receive(&callid, &len)) {
745 ipc_answer_0(callid, EINVAL);
746 ipc_answer_0(rid, EINVAL);
747 free(old);
748 return;
749 }
750 char *new = malloc(len + 1);
751 if (!new) {
752 ipc_answer_0(callid, ENOMEM);
753 ipc_answer_0(rid, ENOMEM);
754 free(old);
755 return;
756 }
757 if ((rc = ipc_data_write_finalize(callid, new, len))) {
758 ipc_answer_0(rid, rc);
759 free(old);
760 free(new);
761 return;
762 }
763 new[len] = '\0';
764
765 char *oldc = canonify(old, &len);
766 char *newc = canonify(new, NULL);
767 if (!oldc || !newc) {
768 ipc_answer_0(rid, EINVAL);
769 free(old);
770 free(new);
771 return;
772 }
773 if (!strncmp(newc, oldc, len)) {
774 /* oldc is a prefix of newc */
775 ipc_answer_0(rid, EINVAL);
776 free(old);
777 free(new);
778 return;
779 }
780
781 vfs_lookup_res_t old_lr;
782 vfs_lookup_res_t new_lr;
783 vfs_lookup_res_t new_par_lr;
784 rwlock_write_lock(&namespace_rwlock);
785 /* Lookup the node belonging to the old file name. */
786 rc = vfs_lookup_internal(oldc, L_NONE, &old_lr, NULL);
787 if (rc != EOK) {
788 rwlock_write_unlock(&namespace_rwlock);
789 ipc_answer_0(rid, rc);
790 free(old);
791 free(new);
792 return;
793 }
794 vfs_node_t *old_node = vfs_node_get(&old_lr);
795 if (!old_node) {
796 rwlock_write_unlock(&namespace_rwlock);
797 ipc_answer_0(rid, ENOMEM);
798 free(old);
799 free(new);
800 return;
801 }
802 /* Lookup parent of the new file name. */
803 rc = vfs_lookup_internal(newc, L_PARENT, &new_par_lr, NULL);
804 if (rc != EOK) {
805 rwlock_write_unlock(&namespace_rwlock);
806 ipc_answer_0(rid, rc);
807 free(old);
808 free(new);
809 return;
810 }
811 /* Check whether linking to the same file system instance. */
812 if ((old_node->fs_handle != new_par_lr.triplet.fs_handle) ||
813 (old_node->dev_handle != new_par_lr.triplet.dev_handle)) {
814 rwlock_write_unlock(&namespace_rwlock);
815 ipc_answer_0(rid, EXDEV); /* different file systems */
816 free(old);
817 free(new);
818 return;
819 }
820 /* Destroy the old link for the new name. */
821 vfs_node_t *new_node = NULL;
822 rc = vfs_lookup_internal(newc, L_UNLINK, &new_lr, NULL);
823 switch (rc) {
824 case ENOENT:
825 /* simply not in our way */
826 break;
827 case EOK:
828 new_node = vfs_node_get(&new_lr);
829 if (!new_node) {
830 rwlock_write_unlock(&namespace_rwlock);
831 ipc_answer_0(rid, ENOMEM);
832 free(old);
833 free(new);
834 return;
835 }
836 futex_down(&nodes_futex);
837 new_node->lnkcnt--;
838 futex_up(&nodes_futex);
839 break;
840 default:
841 rwlock_write_unlock(&namespace_rwlock);
842 ipc_answer_0(rid, ENOTEMPTY);
843 free(old);
844 free(new);
845 return;
846 }
847 /* Create the new link for the new name. */
848 rc = vfs_lookup_internal(newc, L_LINK, NULL, NULL, old_node->index);
849 if (rc != EOK) {
850 rwlock_write_unlock(&namespace_rwlock);
851 if (new_node)
852 vfs_node_put(new_node);
853 ipc_answer_0(rid, rc);
854 free(old);
855 free(new);
856 return;
857 }
858 futex_down(&nodes_futex);
859 old_node->lnkcnt++;
860 futex_up(&nodes_futex);
861 /* Destroy the link for the old name. */
862 rc = vfs_lookup_internal(oldc, L_UNLINK, NULL, NULL);
863 if (rc != EOK) {
864 rwlock_write_unlock(&namespace_rwlock);
865 vfs_node_put(old_node);
866 if (new_node)
867 vfs_node_put(new_node);
868 ipc_answer_0(rid, rc);
869 free(old);
870 free(new);
871 return;
872 }
873 futex_down(&nodes_futex);
874 old_node->lnkcnt--;
875 futex_up(&nodes_futex);
876 rwlock_write_unlock(&namespace_rwlock);
877 vfs_node_put(old_node);
878 if (new_node)
879 vfs_node_put(new_node);
880 free(old);
881 free(new);
882 ipc_answer_0(rid, EOK);
883}
884
885/**
886 * @}
887 */
Note: See TracBrowser for help on using the repository browser.