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

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

Support for rename().

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