source: mainline/uspace/srv/vfs/vfs_ops.c@ 7fe1f75

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

Support for O_TRUNC.

  • Property mode set to 100644
File size: 17.8 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 <ipc/ipc.h>
39#include <async.h>
40#include <errno.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <bool.h>
45#include <futex.h>
46#include <rwlock.h>
47#include <libadt/list.h>
48#include <unistd.h>
49#include <ctype.h>
50#include <fcntl.h>
51#include <assert.h>
52#include <atomic.h>
53#include "vfs.h"
54
55/* Forward declarations of static functions. */
56static int vfs_truncate_internal(int, int, unsigned long, 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
64atomic_t rootfs_futex = FUTEX_INITIALIZER;
65vfs_triplet_t rootfs = {
66 .fs_handle = 0,
67 .dev_handle = 0,
68 .index = 0,
69};
70
71static int lookup_root(int fs_handle, int dev_handle, vfs_lookup_res_t *result)
72{
73 vfs_pair_t altroot = {
74 .fs_handle = fs_handle,
75 .dev_handle = dev_handle,
76 };
77
78 return vfs_lookup_internal("/", strlen("/"), L_DIRECTORY, result,
79 &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);
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
162 /*
163 * Lookup the root node of the filesystem being mounted.
164 * In this case, we don't need to take the namespace_futex as the root
165 * node cannot be removed. However, we do take a reference to it so
166 * that we can track how many times it has been mounted.
167 */
168 int rc;
169 vfs_lookup_res_t mr_res;
170 rc = lookup_root(fs_handle, dev_handle, &mr_res);
171 if (rc != EOK) {
172 free(buf);
173 ipc_answer_0(rid, rc);
174 return;
175 }
176 vfs_node_t *mr_node = vfs_node_get(&mr_res);
177 if (!mr_node) {
178 free(buf);
179 ipc_answer_0(rid, ENOMEM);
180 return;
181 }
182
183 /* Finally, we need to resolve the path to the mountpoint. */
184 vfs_lookup_res_t mp_res;
185 futex_down(&rootfs_futex);
186 if (rootfs.fs_handle) {
187 /* We already have the root FS. */
188 rwlock_write_lock(&namespace_rwlock);
189 rc = vfs_lookup_internal(buf, size, L_DIRECTORY, &mp_res,
190 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
311 /*
312 * Now we are on the verge of accepting the path.
313 *
314 * There is one optimization we could do in the future: copy the path
315 * directly into the PLB using some kind of a callback.
316 */
317 char *path = malloc(len);
318
319 if (!path) {
320 ipc_answer_0(callid, ENOMEM);
321 ipc_answer_0(rid, ENOMEM);
322 return;
323 }
324
325 int rc;
326 if ((rc = ipc_data_write_finalize(callid, path, len))) {
327 ipc_answer_0(rid, rc);
328 free(path);
329 return;
330 }
331
332 /*
333 * Avoid the race condition in which the file can be deleted before we
334 * find/create-and-lock the VFS node corresponding to the looked-up
335 * triplet.
336 */
337 if (lflag & L_CREATE)
338 rwlock_write_lock(&namespace_rwlock);
339 else
340 rwlock_read_lock(&namespace_rwlock);
341
342 /* The path is now populated and we can call vfs_lookup_internal(). */
343 vfs_lookup_res_t lr;
344 rc = vfs_lookup_internal(path, len, lflag, &lr, NULL);
345 if (rc) {
346 if (lflag & L_CREATE)
347 rwlock_write_unlock(&namespace_rwlock);
348 else
349 rwlock_read_unlock(&namespace_rwlock);
350 ipc_answer_0(rid, rc);
351 free(path);
352 return;
353 }
354
355 /* Path is no longer needed. */
356 free(path);
357
358 vfs_node_t *node = vfs_node_get(&lr);
359 if (lflag & L_CREATE)
360 rwlock_write_unlock(&namespace_rwlock);
361 else
362 rwlock_read_unlock(&namespace_rwlock);
363
364 /* Truncate the file if requested and if necessary. */
365 if (oflag & O_TRUNC) {
366 futex_down(&node->contents_rwlock);
367 if (node->size) {
368 rc = vfs_truncate_internal(node->fs_handle,
369 node->dev_handle, node->index, 0);
370 if (rc) {
371 futex_up(&node->contents_rwlock);
372 vfs_node_put(node);
373 ipc_answer_0(rid, rc);
374 return;
375 }
376 node->size = 0;
377 }
378 futex_up(&node->contents_rwlock);
379 }
380
381 /*
382 * Get ourselves a file descriptor and the corresponding vfs_file_t
383 * structure.
384 */
385 int fd = vfs_fd_alloc();
386 if (fd < 0) {
387 vfs_node_put(node);
388 ipc_answer_0(rid, fd);
389 return;
390 }
391 vfs_file_t *file = vfs_file_get(fd);
392 file->node = node;
393 if (oflag & O_APPEND)
394 file->append = true;
395
396 /*
397 * The following increase in reference count is for the fact that the
398 * file is being opened and that a file structure is pointing to it.
399 * It is necessary so that the file will not disappear when
400 * vfs_node_put() is called. The reference will be dropped by the
401 * respective VFS_CLOSE.
402 */
403 vfs_node_addref(node);
404 vfs_node_put(node);
405
406 /* Success! Return the new file descriptor to the client. */
407 ipc_answer_1(rid, EOK, fd);
408}
409
410void vfs_close(ipc_callid_t rid, ipc_call_t *request)
411{
412 int fd = IPC_GET_ARG1(*request);
413 if (fd >= MAX_OPEN_FILES) {
414 ipc_answer_0(rid, EBADF);
415 return;
416 }
417 vfs_fd_free(fd);
418 ipc_answer_0(rid, EOK);
419}
420
421static void vfs_rdwr(ipc_callid_t rid, ipc_call_t *request, bool read)
422{
423
424 /*
425 * The following code strongly depends on the fact that the files data
426 * structure can be only accessed by a single fibril and all file
427 * operations are serialized (i.e. the reads and writes cannot
428 * interleave and a file cannot be closed while it is being read).
429 *
430 * Additional synchronization needs to be added once the table of
431 * open files supports parallel access!
432 */
433
434 int fd = IPC_GET_ARG1(*request);
435
436 /* Lookup the file structure corresponding to the file descriptor. */
437 vfs_file_t *file = vfs_file_get(fd);
438 if (!file) {
439 ipc_answer_0(rid, ENOENT);
440 return;
441 }
442
443 /*
444 * Now we need to receive a call with client's
445 * IPC_M_DATA_READ/IPC_M_DATA_WRITE request.
446 */
447 ipc_callid_t callid;
448 int res;
449 if (read)
450 res = ipc_data_read_receive(&callid, NULL);
451 else
452 res = ipc_data_write_receive(&callid, NULL);
453 if (!res) {
454 ipc_answer_0(callid, EINVAL);
455 ipc_answer_0(rid, EINVAL);
456 return;
457 }
458
459 /*
460 * Lock the open file structure so that no other thread can manipulate
461 * the same open file at a time.
462 */
463 futex_down(&file->lock);
464
465 /*
466 * Lock the file's node so that no other client can read/write to it at
467 * the same time.
468 */
469 if (read)
470 rwlock_read_lock(&file->node->contents_rwlock);
471 else
472 rwlock_write_lock(&file->node->contents_rwlock);
473
474 int fs_phone = vfs_grab_phone(file->node->fs_handle);
475
476 /* Make a VFS_READ/VFS_WRITE request at the destination FS server. */
477 aid_t msg;
478 ipc_call_t answer;
479 if (!read && file->append)
480 file->pos = file->node->size;
481 msg = async_send_3(fs_phone, IPC_GET_METHOD(*request),
482 file->node->dev_handle, file->node->index, file->pos, &answer);
483
484 /*
485 * Forward the IPC_M_DATA_READ/IPC_M_DATA_WRITE request to the
486 * destination FS server. The call will be routed as if sent by
487 * ourselves. Note that call arguments are immutable in this case so we
488 * don't have to bother.
489 */
490 ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
491
492 vfs_release_phone(fs_phone);
493
494 /* Wait for reply from the FS server. */
495 ipcarg_t rc;
496 async_wait_for(msg, &rc);
497 size_t bytes = IPC_GET_ARG1(answer);
498
499 /* Unlock the VFS node. */
500 if (read)
501 rwlock_read_unlock(&file->node->contents_rwlock);
502 else {
503 /* Update the cached version of node's size. */
504 if (rc == EOK)
505 file->node->size = IPC_GET_ARG2(answer);
506 rwlock_write_unlock(&file->node->contents_rwlock);
507 }
508
509 /* Update the position pointer and unlock the open file. */
510 if (rc == EOK)
511 file->pos += bytes;
512 futex_up(&file->lock);
513
514 /*
515 * FS server's reply is the final result of the whole operation we
516 * return to the client.
517 */
518 ipc_answer_1(rid, rc, bytes);
519}
520
521void vfs_read(ipc_callid_t rid, ipc_call_t *request)
522{
523 vfs_rdwr(rid, request, true);
524}
525
526void vfs_write(ipc_callid_t rid, ipc_call_t *request)
527{
528 vfs_rdwr(rid, request, false);
529}
530
531void vfs_seek(ipc_callid_t rid, ipc_call_t *request)
532{
533 int fd = (int) IPC_GET_ARG1(*request);
534 off_t off = (off_t) IPC_GET_ARG2(*request);
535 int whence = (int) IPC_GET_ARG3(*request);
536
537
538 /* Lookup the file structure corresponding to the file descriptor. */
539 vfs_file_t *file = vfs_file_get(fd);
540 if (!file) {
541 ipc_answer_0(rid, ENOENT);
542 return;
543 }
544
545 off_t newpos;
546 futex_down(&file->lock);
547 if (whence == SEEK_SET) {
548 file->pos = off;
549 futex_up(&file->lock);
550 ipc_answer_1(rid, EOK, off);
551 return;
552 }
553 if (whence == SEEK_CUR) {
554 if (file->pos + off < file->pos) {
555 futex_up(&file->lock);
556 ipc_answer_0(rid, EOVERFLOW);
557 return;
558 }
559 file->pos += off;
560 newpos = file->pos;
561 futex_up(&file->lock);
562 ipc_answer_1(rid, EOK, newpos);
563 return;
564 }
565 if (whence == SEEK_END) {
566 rwlock_read_lock(&file->node->contents_rwlock);
567 size_t size = file->node->size;
568 rwlock_read_unlock(&file->node->contents_rwlock);
569 if (size + off < size) {
570 futex_up(&file->lock);
571 ipc_answer_0(rid, EOVERFLOW);
572 return;
573 }
574 newpos = size + off;
575 futex_up(&file->lock);
576 ipc_answer_1(rid, EOK, newpos);
577 return;
578 }
579 futex_up(&file->lock);
580 ipc_answer_0(rid, EINVAL);
581}
582
583int vfs_truncate_internal(int fs_handle, int dev_handle, unsigned long index,
584 size_t size)
585{
586 ipcarg_t rc;
587 int fs_phone;
588
589 fs_phone = vfs_grab_phone(fs_handle);
590 rc = async_req_3_0(fs_phone, VFS_TRUNCATE, (ipcarg_t)dev_handle,
591 (ipcarg_t)index, (ipcarg_t)size);
592 vfs_release_phone(fs_phone);
593 return (int)rc;
594}
595
596void vfs_truncate(ipc_callid_t rid, ipc_call_t *request)
597{
598 int fd = IPC_GET_ARG1(*request);
599 size_t size = IPC_GET_ARG2(*request);
600 int rc;
601
602 vfs_file_t *file = vfs_file_get(fd);
603 if (!file) {
604 ipc_answer_0(rid, ENOENT);
605 return;
606 }
607 futex_down(&file->lock);
608
609 rwlock_write_lock(&file->node->contents_rwlock);
610 rc = vfs_truncate_internal(file->node->fs_handle,
611 file->node->dev_handle, file->node->index, size);
612 if (rc == EOK)
613 file->node->size = size;
614 rwlock_write_unlock(&file->node->contents_rwlock);
615
616 futex_up(&file->lock);
617 ipc_answer_0(rid, (ipcarg_t)rc);
618}
619
620void vfs_mkdir(ipc_callid_t rid, ipc_call_t *request)
621{
622 int mode = IPC_GET_ARG1(*request);
623
624 size_t len;
625 ipc_callid_t callid;
626
627 if (!ipc_data_write_receive(&callid, &len)) {
628 ipc_answer_0(callid, EINVAL);
629 ipc_answer_0(rid, EINVAL);
630 return;
631 }
632
633 /*
634 * Now we are on the verge of accepting the path.
635 *
636 * There is one optimization we could do in the future: copy the path
637 * directly into the PLB using some kind of a callback.
638 */
639 char *path = malloc(len);
640
641 if (!path) {
642 ipc_answer_0(callid, ENOMEM);
643 ipc_answer_0(rid, ENOMEM);
644 return;
645 }
646
647 int rc;
648 if ((rc = ipc_data_write_finalize(callid, path, len))) {
649 ipc_answer_0(rid, rc);
650 free(path);
651 return;
652 }
653
654 rwlock_write_lock(&namespace_rwlock);
655 int lflag = L_DIRECTORY | L_CREATE | L_EXCLUSIVE;
656 rc = vfs_lookup_internal(path, len, lflag, NULL, NULL);
657 rwlock_write_unlock(&namespace_rwlock);
658 free(path);
659 ipc_answer_0(rid, rc);
660}
661
662void vfs_unlink(ipc_callid_t rid, ipc_call_t *request)
663{
664 int lflag = IPC_GET_ARG1(*request);
665
666 size_t len;
667 ipc_callid_t callid;
668
669 if (!ipc_data_write_receive(&callid, &len)) {
670 ipc_answer_0(callid, EINVAL);
671 ipc_answer_0(rid, EINVAL);
672 return;
673 }
674
675 /*
676 * Now we are on the verge of accepting the path.
677 *
678 * There is one optimization we could do in the future: copy the path
679 * directly into the PLB using some kind of a callback.
680 */
681 char *path = malloc(len);
682
683 if (!path) {
684 ipc_answer_0(callid, ENOMEM);
685 ipc_answer_0(rid, ENOMEM);
686 return;
687 }
688
689 int rc;
690 if ((rc = ipc_data_write_finalize(callid, path, len))) {
691 ipc_answer_0(rid, rc);
692 free(path);
693 return;
694 }
695
696 rwlock_write_lock(&namespace_rwlock);
697 lflag &= L_DIRECTORY; /* sanitize lflag */
698 vfs_lookup_res_t lr;
699 rc = vfs_lookup_internal(path, len, lflag | L_DESTROY, &lr, NULL);
700 free(path);
701 if (rc != EOK) {
702 rwlock_write_unlock(&namespace_rwlock);
703 ipc_answer_0(rid, rc);
704 return;
705 }
706
707 /*
708 * The name has already been unlinked by vfs_lookup_internal().
709 * We have to get and put the VFS node to ensure that it is
710 * VFS_DESTROY'ed after the last reference to it is dropped.
711 */
712 vfs_node_t *node = vfs_node_get(&lr);
713 node->lnkcnt--;
714 rwlock_write_unlock(&namespace_rwlock);
715 vfs_node_put(node);
716 ipc_answer_0(rid, EOK);
717}
718
719/**
720 * @}
721 */
Note: See TracBrowser for help on using the repository browser.