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

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

Split the 'mount another filesystem here' and 'you are being mounted and the
device is this' mount semantics. Add VFS_MOUNTED VFS operation that corresponds
to the latter and reserve VFS_MOUNT only for the former. Because of this
change, the VFS server does not maintain the mr_node VFS node for the name space
root anymore and the VFS_LOOKUP operation is now not meant to be used on
unmounted file system, not even for looking up the root node of unmounted file
systems. In the light of these changes, TMPFS is now initialized from
tmpfs_mounted() function.

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