source: mainline/uspace/lib/c/generic/vfs/vfs.c@ eec201d

Last change on this file since eec201d was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

  • Property mode set to 100644
File size: 32.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 libc
30 * @{
31 */
32/** @file
33 */
34
35#include <vfs/vfs.h>
36#include <vfs/canonify.h>
37#include <vfs/vfs_mtab.h>
38#include <vfs/vfs_sess.h>
39#include <macros.h>
40#include <stdlib.h>
41#include <stddef.h>
42#include <stdint.h>
43#include <ipc/services.h>
44#include <ns.h>
45#include <async.h>
46#include <fibril_synch.h>
47#include <errno.h>
48#include <assert.h>
49#include <str.h>
50#include <loc.h>
51#include <ipc/vfs.h>
52#include <ipc/loc.h>
53
54/*
55 * This file contains the implementation of the native HelenOS file system API.
56 *
57 * The API supports client-side file system roots, client-side IO cursors and
58 * uses file handles as a primary means to refer to files. In order to call the
59 * API functions, one just includes vfs/vfs.h.
60 *
61 * The API functions come in two main flavors:
62 *
63 * - functions that operate on integer file handles, such as:
64 * vfs_walk(), vfs_open(), vfs_read(), vfs_link(), ...
65 *
66 * - functions that operate on paths, such as:
67 * vfs_lookup(), vfs_link_path(), vfs_unlink_path(), vfs_rename_path(), ...
68 *
69 * There is usually a corresponding path function for each file handle function
70 * that exists mostly as a convenience wrapper, except for cases when only a
71 * path version exists due to file system consistency considerations (see
72 * vfs_rename_path()). Sometimes one of the versions does not make sense, in
73 * which case it is also omitted.
74 *
75 * Besides of that, the API provides some convenience wrappers for frequently
76 * performed pairs of operations, for example there is a combo API for
77 * vfs_lookup() and vfs_open(): vfs_lookup_open().
78 *
79 * Some of the functions here return a file handle that can be passed to other
80 * functions. Note that a file handle does not automatically represent a file
81 * from which one can read or to which one can write. In order to do so, the
82 * file handle must be opened first for reading/writing using vfs_open().
83 *
84 * All file handles, no matter whether opened or not, must be eventually
85 * returned to the system using vfs_put(). Non-returned file handles are in use
86 * and consume system resources.
87 *
88 * Functions that return errno_t return an error code on error and do not
89 * set errno. Depending on function, success is signalled by returning either
90 * EOK or a non-negative file handle.
91 *
92 * An example life-cycle of a file handle is as follows:
93 *
94 * #include <vfs/vfs.h>
95 *
96 * int file = vfs_lookup("/foo/bar/foobar", WALK_REGULAR);
97 * if (file < 0)
98 * return file;
99 * errno_t rc = vfs_open(file, MODE_READ);
100 * if (rc != EOK) {
101 * (void) vfs_put(file);
102 * return rc;
103 * }
104 * aoff64_t pos = 42;
105 * char buf[512];
106 * size_t nread;
107 * rc = vfs_read(file, &pos, buf, sizeof(buf), &nread);
108 * if (rc != EOK) {
109 * vfs_put(file);
110 * return rc;
111 * }
112 *
113 * // buf is now filled with nread bytes from file
114 *
115 * vfs_put(file);
116 */
117
118static FIBRIL_MUTEX_INITIALIZE(vfs_mutex);
119static async_sess_t *vfs_sess = NULL;
120
121static FIBRIL_MUTEX_INITIALIZE(cwd_mutex);
122
123static int cwd_fd = -1;
124static char *cwd_path = NULL;
125static size_t cwd_size = 0;
126
127static FIBRIL_MUTEX_INITIALIZE(root_mutex);
128static int root_fd = -1;
129
130static errno_t get_parent_and_child(const char *path, int *parent, char **child)
131{
132 size_t size;
133 char *apath = vfs_absolutize(path, &size);
134 if (!apath)
135 return ENOMEM;
136
137 char *slash = str_rchr(apath, L'/');
138 if (slash == apath) {
139 *parent = vfs_root();
140 if (*parent < 0) {
141 free(apath);
142 return EBADF;
143 }
144 *child = apath;
145 return EOK;
146 } else {
147 *slash = '\0';
148 errno_t rc = vfs_lookup(apath, WALK_DIRECTORY, parent);
149 if (rc != EOK) {
150 free(apath);
151 return rc;
152 }
153 *slash = '/';
154 *child = str_dup(slash);
155 free(apath);
156 if (!*child) {
157 vfs_put(*parent);
158 return ENOMEM;
159 }
160
161 return rc;
162 }
163
164}
165
166/** Make a potentially relative path absolute
167 *
168 * This function coverts a current-working-directory-relative path into a
169 * well-formed, absolute path. The caller is responsible for deallocating the
170 * returned buffer.
171 *
172 * @param[in] path Path to be absolutized
173 * @param[out] retlen Length of the absolutized path
174 *
175 * @return New buffer holding the absolutized path or NULL
176 */
177char *vfs_absolutize(const char *path, size_t *retlen)
178{
179 char *ncwd_path;
180 char *ncwd_path_nc;
181
182 fibril_mutex_lock(&cwd_mutex);
183 size_t size = str_size(path);
184 if (*path != '/') {
185 if (cwd_path == NULL) {
186 fibril_mutex_unlock(&cwd_mutex);
187 return NULL;
188 }
189 ncwd_path_nc = malloc(cwd_size + 1 + size + 1);
190 if (ncwd_path_nc == NULL) {
191 fibril_mutex_unlock(&cwd_mutex);
192 return NULL;
193 }
194 str_cpy(ncwd_path_nc, cwd_size + 1 + size + 1, cwd_path);
195 ncwd_path_nc[cwd_size] = '/';
196 ncwd_path_nc[cwd_size + 1] = '\0';
197 } else {
198 ncwd_path_nc = malloc(size + 1);
199 if (ncwd_path_nc == NULL) {
200 fibril_mutex_unlock(&cwd_mutex);
201 return NULL;
202 }
203 ncwd_path_nc[0] = '\0';
204 }
205 str_append(ncwd_path_nc, cwd_size + 1 + size + 1, path);
206 ncwd_path = canonify(ncwd_path_nc, retlen);
207 if (ncwd_path == NULL) {
208 fibril_mutex_unlock(&cwd_mutex);
209 free(ncwd_path_nc);
210 return NULL;
211 }
212 /*
213 * We need to clone ncwd_path because canonify() works in-place and thus
214 * the address in ncwd_path need not be the same as ncwd_path_nc, even
215 * though they both point into the same dynamically allocated buffer.
216 */
217 ncwd_path = str_dup(ncwd_path);
218 free(ncwd_path_nc);
219 if (ncwd_path == NULL) {
220 fibril_mutex_unlock(&cwd_mutex);
221 return NULL;
222 }
223 fibril_mutex_unlock(&cwd_mutex);
224 return ncwd_path;
225}
226
227/** Clone a file handle
228 *
229 * The caller can choose whether to clone an existing file handle into another
230 * already existing file handle (in which case it is first closed) or to a new
231 * file handle allocated either from low or high indices.
232 *
233 * @param file_from Source file handle
234 * @param file_to Destination file handle or -1
235 * @param high If file_to is -1, high controls whether the new file
236 * handle will be allocated from high indices
237 *
238 * @return New file handle on success or an error code
239 */
240errno_t vfs_clone(int file_from, int file_to, bool high, int *handle)
241{
242 assert(handle != NULL);
243
244 async_exch_t *vfs_exch = vfs_exchange_begin();
245 sysarg_t ret;
246 errno_t rc = async_req_3_1(vfs_exch, VFS_IN_CLONE, (sysarg_t) file_from,
247 (sysarg_t) file_to, (sysarg_t) high, &ret);
248 vfs_exchange_end(vfs_exch);
249
250 if (rc == EOK) {
251 *handle = ret;
252 }
253 return rc;
254}
255
256/** Get current working directory path
257 *
258 * @param[out] buf Buffer
259 * @param size Size of @a buf
260 *
261 * @return EOK on success or a non-error code
262 */
263errno_t vfs_cwd_get(char *buf, size_t size)
264{
265 fibril_mutex_lock(&cwd_mutex);
266
267 if ((cwd_size == 0) || (size < cwd_size + 1)) {
268 fibril_mutex_unlock(&cwd_mutex);
269 return ERANGE;
270 }
271
272 str_cpy(buf, size, cwd_path);
273 fibril_mutex_unlock(&cwd_mutex);
274
275 return EOK;
276}
277
278/** Change working directory
279 *
280 * @param path Path of the new working directory
281 *
282 * @return EOK on success or an error code
283 */
284errno_t vfs_cwd_set(const char *path)
285{
286 size_t abs_size;
287 char *abs_path = vfs_absolutize(path, &abs_size);
288 if (abs_path == NULL)
289 return ENOMEM;
290
291 int fd;
292 errno_t rc = vfs_lookup(abs_path, WALK_DIRECTORY, &fd);
293 if (rc != EOK) {
294 free(abs_path);
295 return rc;
296 }
297
298 fibril_mutex_lock(&cwd_mutex);
299
300 if (cwd_fd >= 0)
301 vfs_put(cwd_fd);
302
303 if (cwd_path)
304 free(cwd_path);
305
306 cwd_fd = fd;
307 cwd_path = abs_path;
308 cwd_size = abs_size;
309
310 fibril_mutex_unlock(&cwd_mutex);
311 return EOK;
312}
313
314/** Start an async exchange on the VFS session
315 *
316 * @return New exchange
317 */
318async_exch_t *vfs_exchange_begin(void)
319{
320 fibril_mutex_lock(&vfs_mutex);
321
322 while (vfs_sess == NULL) {
323 vfs_sess = service_connect_blocking(SERVICE_VFS, INTERFACE_VFS,
324 0);
325 }
326
327 fibril_mutex_unlock(&vfs_mutex);
328
329 return async_exchange_begin(vfs_sess);
330}
331
332/** Finish an async exchange on the VFS session
333 *
334 * @param exch Exchange to be finished
335 */
336void vfs_exchange_end(async_exch_t *exch)
337{
338 async_exchange_end(exch);
339}
340
341/** Open session to service represented by a special file
342 *
343 * Given that the file referred to by @a file represents a service,
344 * open a session to that service.
345 *
346 * @param file File handle representing a service
347 * @param iface Interface to connect to (XXX Should be automatic)
348 *
349 * @return Session pointer on success.
350 * @return @c NULL or error.
351 */
352async_sess_t *vfs_fd_session(int file, iface_t iface)
353{
354 vfs_stat_t stat;
355 errno_t rc = vfs_stat(file, &stat);
356 if (rc != EOK)
357 return NULL;
358
359 if (stat.service == 0)
360 return NULL;
361
362 return loc_service_connect(stat.service, iface, 0);
363}
364
365/** Determine if a device contains the specified file system type. If so,
366 * return identification information.
367 *
368 * @param fs_name File system name
369 * @param serv Service representing the mountee
370 * @param info Place to store volume identification information
371 *
372 * @return EOK on success or an error code
373 */
374errno_t vfs_fsprobe(const char *fs_name, service_id_t serv,
375 vfs_fs_probe_info_t *info)
376{
377 errno_t rc;
378
379 ipc_call_t answer;
380 async_exch_t *exch = vfs_exchange_begin();
381 aid_t req = async_send_1(exch, VFS_IN_FSPROBE, serv, &answer);
382
383 rc = async_data_write_start(exch, (void *) fs_name,
384 str_size(fs_name));
385
386 async_wait_for(req, &rc);
387
388 if (rc != EOK) {
389 vfs_exchange_end(exch);
390 return rc;
391 }
392
393 rc = async_data_read_start(exch, info, sizeof(*info));
394 vfs_exchange_end(exch);
395
396 return rc;
397}
398
399/** Return a list of currently available file system types
400 *
401 * @param fstypes Points to structure where list of filesystem types is
402 * stored. It is read as a null-terminated list of strings
403 * fstypes->fstypes[0..]. To free the list use vfs_fstypes_free().
404 *
405 * @return EOK on success or an error code
406 */
407errno_t vfs_fstypes(vfs_fstypes_t *fstypes)
408{
409 sysarg_t size;
410 char *buf;
411 char dummybuf[1];
412 size_t count, i;
413
414 async_exch_t *exch = vfs_exchange_begin();
415 errno_t rc = async_req_0_1(exch, VFS_IN_FSTYPES, &size);
416
417 if (rc != EOK) {
418 vfs_exchange_end(exch);
419 return rc;
420 }
421
422 buf = malloc(size);
423 if (buf == NULL) {
424 buf = dummybuf;
425 size = 1;
426 }
427
428 rc = async_data_read_start(exch, buf, size);
429 vfs_exchange_end(exch);
430
431 if (buf == dummybuf)
432 return ENOMEM;
433
434 /*
435 * Buffer should contain a number of null-terminated strings.
436 * Count them so that we can allocate an index
437 */
438 count = 0;
439 i = 0;
440 while (i < size) {
441 if (buf[i] == '\0')
442 ++count;
443 ++i;
444 }
445
446 if (count == 0) {
447 free(buf);
448 return EIO;
449 }
450
451 fstypes->fstypes = calloc(sizeof(char *), count + 1);
452 if (fstypes->fstypes == NULL) {
453 free(buf);
454 return ENOMEM;
455 }
456
457 /* Now fill the index */
458 if (buf[0] != '\0')
459 fstypes->fstypes[0] = &buf[0];
460 count = 0;
461 i = 0;
462 while (i < size) {
463 if (buf[i] == '\0')
464 fstypes->fstypes[++count] = &buf[i + 1];
465 ++i;
466 }
467 fstypes->fstypes[count] = NULL;
468 fstypes->buf = buf;
469 fstypes->size = size;
470
471 return rc;
472}
473
474/** Free list of file system types.
475 *
476 * @param fstypes List of file system types
477 */
478void vfs_fstypes_free(vfs_fstypes_t *fstypes)
479{
480 free(fstypes->buf);
481 fstypes->buf = NULL;
482 free(fstypes->fstypes);
483 fstypes->fstypes = NULL;
484 fstypes->size = 0;
485}
486
487/** Link a file or directory
488 *
489 * Create a new name and an empty file or an empty directory in a parent
490 * directory. If child with the same name already exists, the function returns
491 * a failure, the existing file remains untouched and no file system object
492 * is created.
493 *
494 * @param parent File handle of the parent directory node
495 * @param child New name to be linked
496 * @param kind Kind of the object to be created: KIND_FILE or
497 * KIND_DIRECTORY
498 * @param[out] linkedfd If not NULL, will receive a file handle to the linked
499 * child
500 * @return EOK on success or an error code
501 */
502errno_t vfs_link(int parent, const char *child, vfs_file_kind_t kind, int *linkedfd)
503{
504 int flags = (kind == KIND_DIRECTORY) ? WALK_DIRECTORY : WALK_REGULAR;
505 int file = -1;
506 errno_t rc = vfs_walk(parent, child, WALK_MUST_CREATE | flags, &file);
507 if (rc != EOK)
508 return rc;
509
510 if (linkedfd)
511 *linkedfd = file;
512 else
513 vfs_put(file);
514
515 return EOK;
516}
517
518/** Link a file or directory
519 *
520 * Create a new name and an empty file or an empty directory at given path.
521 * If a link with the same name already exists, the function returns
522 * a failure, the existing file remains untouched and no file system object
523 * is created.
524 *
525 * @param path New path to be linked
526 * @param kind Kind of the object to be created: KIND_FILE or
527 * KIND_DIRECTORY
528 * @param[out] linkedfd If not NULL, will receive a file handle to the linked
529 * child
530 * @return EOK on success or an error code
531 */
532errno_t vfs_link_path(const char *path, vfs_file_kind_t kind, int *linkedfd)
533{
534 char *child;
535 int parent;
536 errno_t rc = get_parent_and_child(path, &parent, &child);
537 if (rc != EOK)
538 return rc;
539
540 rc = vfs_link(parent, child, kind, linkedfd);
541
542 free(child);
543 vfs_put(parent);
544 return rc;
545}
546
547/** Lookup a path relative to the local root
548 *
549 * @param path Path to be looked up
550 * @param flags Walk flags
551 * @param[out] handle Pointer to variable where handle is to be written.
552 *
553 * @return EOK on success or an error code.
554 */
555errno_t vfs_lookup(const char *path, int flags, int *handle)
556{
557 size_t size;
558 char *p = vfs_absolutize(path, &size);
559 if (!p)
560 return ENOMEM;
561
562 int root = vfs_root();
563 if (root < 0) {
564 free(p);
565 return ENOENT;
566 }
567
568 // XXX: Workaround for GCC diagnostics.
569 *handle = -1;
570
571 errno_t rc = vfs_walk(root, p, flags, handle);
572 vfs_put(root);
573 free(p);
574 return rc;
575}
576
577/** Lookup a path relative to the local root and open the result
578 *
579 * This function is a convenience combo for vfs_lookup() and vfs_open().
580 *
581 * @param path Path to be looked up
582 * @param flags Walk flags
583 * @param mode Mode in which to open file in
584 * @param[out] handle Pointer to variable where handle is to be written.
585 *
586 * @return EOK on success or an error code
587 */
588errno_t vfs_lookup_open(const char *path, int flags, int mode, int *handle)
589{
590 int file;
591 errno_t rc = vfs_lookup(path, flags, &file);
592 if (rc != EOK)
593 return rc;
594
595 rc = vfs_open(file, mode);
596 if (rc != EOK) {
597 vfs_put(file);
598 return rc;
599 }
600
601 *handle = file;
602 return EOK;
603}
604
605/** Mount a file system
606 *
607 * @param[in] mp File handle representing the mount-point
608 * @param[in] fs_name File system name
609 * @param[in] serv Service representing the mountee
610 * @param[in] opts Mount options for the endpoint file system
611 * @param[in] flags Mount flags
612 * @param[in] instance Instance number of the file system server
613 * @param[out] mountedfd File handle of the mounted root if not NULL
614 *
615 * @return EOK on success or an error code
616 */
617errno_t vfs_mount(int mp, const char *fs_name, service_id_t serv, const char *opts,
618 unsigned int flags, unsigned int instance, int *mountedfd)
619{
620 errno_t rc, rc1;
621
622 if (!mountedfd)
623 flags |= VFS_MOUNT_NO_REF;
624 if (mp < 0)
625 flags |= VFS_MOUNT_CONNECT_ONLY;
626
627 ipc_call_t answer;
628 async_exch_t *exch = vfs_exchange_begin();
629 aid_t req = async_send_4(exch, VFS_IN_MOUNT, mp, serv, flags, instance,
630 &answer);
631
632 rc1 = async_data_write_start(exch, (void *) opts, str_size(opts));
633 if (rc1 == EOK) {
634 rc1 = async_data_write_start(exch, (void *) fs_name,
635 str_size(fs_name));
636 }
637
638 vfs_exchange_end(exch);
639
640 async_wait_for(req, &rc);
641
642 if (mountedfd)
643 *mountedfd = (int) IPC_GET_ARG1(answer);
644
645 if (rc != EOK)
646 return rc;
647 return rc1;
648}
649
650/** Mount a file system
651 *
652 * @param[in] mp Path representing the mount-point
653 * @param[in] fs_name File system name
654 * @param[in] fqsn Fully qualified service name of the mountee
655 * @param[in] opts Mount options for the endpoint file system
656 * @param[in] flags Mount flags
657 * @param[in] instance Instance number of the file system server
658 *
659 * @return EOK on success or an error code
660 */
661errno_t vfs_mount_path(const char *mp, const char *fs_name, const char *fqsn,
662 const char *opts, unsigned int flags, unsigned int instance)
663{
664 int null_id = -1;
665 char null[LOC_NAME_MAXLEN];
666
667 if (str_cmp(fqsn, "") == 0) {
668 /*
669 * No device specified, create a fresh null/%d device instead.
670 */
671 null_id = loc_null_create();
672
673 if (null_id == -1)
674 return ENOMEM;
675
676 snprintf(null, LOC_NAME_MAXLEN, "null/%d", null_id);
677 fqsn = null;
678 }
679
680 if (flags & IPC_FLAG_BLOCKING)
681 flags = VFS_MOUNT_BLOCKING;
682 else
683 flags = 0;
684
685 service_id_t service_id;
686 errno_t res = loc_service_get_id(fqsn, &service_id, flags);
687 if (res != EOK) {
688 if (null_id != -1)
689 loc_null_destroy(null_id);
690
691 return res;
692 }
693
694 size_t mpa_size;
695 char *mpa = vfs_absolutize(mp, &mpa_size);
696 if (mpa == NULL) {
697 if (null_id != -1)
698 loc_null_destroy(null_id);
699
700 return ENOMEM;
701 }
702
703 fibril_mutex_lock(&root_mutex);
704
705 errno_t rc;
706
707 if (str_cmp(mpa, "/") == 0) {
708 /* Mounting root. */
709
710 if (root_fd >= 0) {
711 fibril_mutex_unlock(&root_mutex);
712 if (null_id != -1)
713 loc_null_destroy(null_id);
714 return EBUSY;
715 }
716
717 int root;
718 rc = vfs_mount(-1, fs_name, service_id, opts, flags, instance,
719 &root);
720 if (rc == EOK)
721 root_fd = root;
722 } else {
723 if (root_fd < 0) {
724 fibril_mutex_unlock(&root_mutex);
725 if (null_id != -1)
726 loc_null_destroy(null_id);
727 return EINVAL;
728 }
729
730 int mpfd;
731 rc = vfs_walk(root_fd, mpa, WALK_DIRECTORY, &mpfd);
732 if (rc == EOK) {
733 rc = vfs_mount(mpfd, fs_name, service_id, opts, flags,
734 instance, NULL);
735 vfs_put(mpfd);
736 }
737 }
738
739 fibril_mutex_unlock(&root_mutex);
740
741 if ((rc != EOK) && (null_id != -1))
742 loc_null_destroy(null_id);
743
744 return (errno_t) rc;
745}
746
747/** Open a file handle for I/O
748 *
749 * @param file File handle to enable I/O on
750 * @param mode Mode in which to open file in
751 *
752 * @return EOK on success or an error code
753 */
754errno_t vfs_open(int file, int mode)
755{
756 async_exch_t *exch = vfs_exchange_begin();
757 errno_t rc = async_req_2_0(exch, VFS_IN_OPEN, file, mode);
758 vfs_exchange_end(exch);
759
760 return rc;
761}
762
763/** Pass a file handle to another VFS client
764 *
765 * @param vfs_exch Donor's VFS exchange
766 * @param file Donor's file handle to pass
767 * @param exch Exchange to the acceptor
768 *
769 * @return EOK on success or an error code
770 */
771errno_t vfs_pass_handle(async_exch_t *vfs_exch, int file, async_exch_t *exch)
772{
773 return async_state_change_start(exch, VFS_PASS_HANDLE, (sysarg_t) file,
774 0, vfs_exch);
775}
776
777/** Stop working with a file handle
778 *
779 * @param file File handle to put
780 *
781 * @return EOK on success or an error code
782 */
783errno_t vfs_put(int file)
784{
785 async_exch_t *exch = vfs_exchange_begin();
786 errno_t rc = async_req_1_0(exch, VFS_IN_PUT, file);
787 vfs_exchange_end(exch);
788
789 return rc;
790}
791
792/** Receive a file handle from another VFS client
793 *
794 * @param high If true, the received file handle will be allocated from high
795 * indices
796 * @param[out] handle Received handle.
797 *
798 * @return EOK on success or an error code
799 */
800errno_t vfs_receive_handle(bool high, int *handle)
801{
802 ipc_call_t call;
803 if (!async_state_change_receive(&call)) {
804 async_answer_0(&call, EINVAL);
805 return EINVAL;
806 }
807
808 async_exch_t *vfs_exch = vfs_exchange_begin();
809
810 async_state_change_finalize(&call, vfs_exch);
811
812 sysarg_t ret;
813 errno_t rc = async_req_1_1(vfs_exch, VFS_IN_WAIT_HANDLE, high, &ret);
814
815 async_exchange_end(vfs_exch);
816
817 if (rc == EOK) {
818 *handle = (int) ret;
819 }
820
821 return rc;
822}
823
824/** Read data
825 *
826 * Read up to @a nbytes bytes from file if available. This function always reads
827 * all the available bytes up to @a nbytes.
828 *
829 * @param file File handle to read from
830 * @param[inout] pos Position to read from, updated by the actual bytes read
831 * @param buf Buffer, @a nbytes bytes long
832 * @param nbytes Number of bytes to read
833 * @param nread Place to store number of bytes actually read
834 *
835 * @return On success, EOK and @a *nread is filled with number
836 * of bytes actually read.
837 * @return On failure, an error code
838 */
839errno_t vfs_read(int file, aoff64_t *pos, void *buf, size_t nbyte, size_t *nread)
840{
841 ssize_t cnt = 0;
842 size_t nr = 0;
843 uint8_t *bp = (uint8_t *) buf;
844 errno_t rc;
845
846 do {
847 bp += cnt;
848 nr += cnt;
849 *pos += cnt;
850 rc = vfs_read_short(file, *pos, bp, nbyte - nr, &cnt);
851 } while (rc == EOK && cnt > 0 && (nbyte - nr - cnt) > 0);
852
853 if (rc != EOK) {
854 *nread = nr;
855 return rc;
856 }
857
858 nr += cnt;
859 *pos += cnt;
860 *nread = nr;
861 return EOK;
862}
863
864/** Read bytes from a file
865 *
866 * Read up to @a nbyte bytes from file. The actual number of bytes read
867 * may be lower, but greater than zero if there are any bytes available.
868 * If there are no bytes available for reading, then the function will
869 * return success with zero bytes read.
870 *
871 * @param file File handle to read from
872 * @param[in] pos Position to read from
873 * @param buf Buffer to read from
874 * @param nbyte Maximum number of bytes to read
875 * @param[out] nread Actual number of bytes read (0 or more)
876 *
877 * @return EOK on success or an error code
878 */
879errno_t vfs_read_short(int file, aoff64_t pos, void *buf, size_t nbyte,
880 ssize_t *nread)
881{
882 errno_t rc;
883 ipc_call_t answer;
884 aid_t req;
885
886 if (nbyte > DATA_XFER_LIMIT)
887 nbyte = DATA_XFER_LIMIT;
888
889 async_exch_t *exch = vfs_exchange_begin();
890
891 req = async_send_3(exch, VFS_IN_READ, file, LOWER32(pos),
892 UPPER32(pos), &answer);
893 rc = async_data_read_start(exch, (void *) buf, nbyte);
894
895 vfs_exchange_end(exch);
896
897 if (rc == EOK)
898 async_wait_for(req, &rc);
899 else
900 async_forget(req);
901
902 if (rc != EOK)
903 return rc;
904
905 *nread = (ssize_t) IPC_GET_ARG1(answer);
906 return EOK;
907}
908
909/** Rename a file or directory
910 *
911 * There is no file-handle-based variant to disallow attempts to introduce loops
912 * and breakage in the directory tree when relinking eg. a node under its own
913 * descendant. The path-based variant is not susceptible because the VFS can
914 * prevent this lexically by comparing the paths.
915 *
916 * @param old Old path
917 * @param new New path
918 *
919 * @return EOK on success or an error code
920 */
921errno_t vfs_rename_path(const char *old, const char *new)
922{
923 errno_t rc;
924 errno_t rc_orig;
925 aid_t req;
926
927 size_t olda_size;
928 char *olda = vfs_absolutize(old, &olda_size);
929 if (olda == NULL)
930 return ENOMEM;
931
932 size_t newa_size;
933 char *newa = vfs_absolutize(new, &newa_size);
934 if (newa == NULL) {
935 free(olda);
936 return ENOMEM;
937 }
938
939 async_exch_t *exch = vfs_exchange_begin();
940 int root = vfs_root();
941 if (root < 0) {
942 free(olda);
943 free(newa);
944 return ENOENT;
945 }
946
947 req = async_send_1(exch, VFS_IN_RENAME, root, NULL);
948 rc = async_data_write_start(exch, olda, olda_size);
949 if (rc != EOK) {
950 vfs_exchange_end(exch);
951 free(olda);
952 free(newa);
953 vfs_put(root);
954 async_wait_for(req, &rc_orig);
955 if (rc_orig != EOK)
956 rc = rc_orig;
957 return rc;
958 }
959 rc = async_data_write_start(exch, newa, newa_size);
960 if (rc != EOK) {
961 vfs_exchange_end(exch);
962 free(olda);
963 free(newa);
964 vfs_put(root);
965 async_wait_for(req, &rc_orig);
966 if (rc_orig != EOK)
967 rc = rc_orig;
968 return rc;
969 }
970 vfs_exchange_end(exch);
971 free(olda);
972 free(newa);
973 vfs_put(root);
974 async_wait_for(req, &rc);
975
976 return rc;
977}
978
979/** Resize file to a specified length
980 *
981 * Resize file so that its size is exactly @a length.
982 *
983 * @param file File handle to resize
984 * @param length New length
985 *
986 * @return EOK on success or an error code
987 */
988errno_t vfs_resize(int file, aoff64_t length)
989{
990 async_exch_t *exch = vfs_exchange_begin();
991 errno_t rc = async_req_3_0(exch, VFS_IN_RESIZE, file, LOWER32(length),
992 UPPER32(length));
993 vfs_exchange_end(exch);
994
995 return rc;
996}
997
998/** Return a new file handle representing the local root
999 *
1000 * @return A clone of the local root file handle or -1
1001 */
1002int vfs_root(void)
1003{
1004 fibril_mutex_lock(&root_mutex);
1005 int fd;
1006 if (root_fd < 0) {
1007 fd = -1;
1008 } else {
1009 errno_t rc = vfs_clone(root_fd, -1, true, &fd);
1010 if (rc != EOK) {
1011 fd = -1;
1012 }
1013 }
1014 fibril_mutex_unlock(&root_mutex);
1015 return fd;
1016}
1017
1018/** Set a new local root
1019 *
1020 * Note that it is still possible to have file handles for other roots and pass
1021 * them to the API functions. Functions like vfs_root() and vfs_lookup() will
1022 * however consider the file set by this function to be the root.
1023 *
1024 * @param nroot The new local root file handle
1025 *
1026 * @return Error code
1027 */
1028errno_t vfs_root_set(int nroot)
1029{
1030 int new_root;
1031 errno_t rc = vfs_clone(nroot, -1, true, &new_root);
1032 if (rc != EOK) {
1033 return rc;
1034 }
1035
1036 fibril_mutex_lock(&root_mutex);
1037 if (root_fd >= 0)
1038 vfs_put(root_fd);
1039 root_fd = new_root;
1040 fibril_mutex_unlock(&root_mutex);
1041
1042 return EOK;
1043}
1044
1045/** Get file information
1046 *
1047 * @param file File handle to get information about
1048 * @param[out] stat Place to store file information
1049 *
1050 * @return EOK on success or an error code
1051 */
1052errno_t vfs_stat(int file, vfs_stat_t *stat)
1053{
1054 errno_t rc;
1055 aid_t req;
1056
1057 async_exch_t *exch = vfs_exchange_begin();
1058
1059 req = async_send_1(exch, VFS_IN_STAT, file, NULL);
1060 rc = async_data_read_start(exch, (void *) stat, sizeof(vfs_stat_t));
1061 if (rc != EOK) {
1062 vfs_exchange_end(exch);
1063
1064 errno_t rc_orig;
1065 async_wait_for(req, &rc_orig);
1066
1067 if (rc_orig != EOK)
1068 rc = rc_orig;
1069
1070 return rc;
1071 }
1072
1073 vfs_exchange_end(exch);
1074 async_wait_for(req, &rc);
1075
1076 return rc;
1077}
1078
1079/** Get file information
1080 *
1081 * @param path File path to get information about
1082 * @param[out] stat Place to store file information
1083 *
1084 * @return EOK on success or an error code
1085 */
1086errno_t vfs_stat_path(const char *path, vfs_stat_t *stat)
1087{
1088 int file;
1089 errno_t rc = vfs_lookup(path, 0, &file);
1090 if (rc != EOK)
1091 return rc;
1092
1093 rc = vfs_stat(file, stat);
1094
1095 vfs_put(file);
1096
1097 return rc;
1098}
1099
1100/** Get filesystem statistics
1101 *
1102 * @param file File located on the queried file system
1103 * @param[out] st Buffer for storing information
1104 *
1105 * @return EOK on success or an error code
1106 */
1107errno_t vfs_statfs(int file, vfs_statfs_t *st)
1108{
1109 errno_t rc, ret;
1110 aid_t req;
1111
1112 async_exch_t *exch = vfs_exchange_begin();
1113
1114 req = async_send_1(exch, VFS_IN_STATFS, file, NULL);
1115 rc = async_data_read_start(exch, (void *) st, sizeof(*st));
1116
1117 vfs_exchange_end(exch);
1118 async_wait_for(req, &ret);
1119
1120 rc = (ret != EOK ? ret : rc);
1121
1122 return rc;
1123}
1124
1125/** Get filesystem statistics
1126 *
1127 * @param file Path pointing to the queried file system
1128 * @param[out] st Buffer for storing information
1129 *
1130 * @return EOK on success or an error code
1131 */
1132errno_t vfs_statfs_path(const char *path, vfs_statfs_t *st)
1133{
1134 int file;
1135 errno_t rc = vfs_lookup(path, 0, &file);
1136 if (rc != EOK)
1137 return rc;
1138
1139 rc = vfs_statfs(file, st);
1140
1141 vfs_put(file);
1142
1143 return rc;
1144}
1145
1146/** Synchronize file
1147 *
1148 * @param file File handle to synchronize
1149 *
1150 * @return EOK on success or an error code
1151 */
1152errno_t vfs_sync(int file)
1153{
1154 async_exch_t *exch = vfs_exchange_begin();
1155 errno_t rc = async_req_1_0(exch, VFS_IN_SYNC, file);
1156 vfs_exchange_end(exch);
1157
1158 return rc;
1159}
1160
1161/** Unlink a file or directory
1162 *
1163 * Unlink a name from a parent directory. The caller can supply the file handle
1164 * of the unlinked child in order to detect a possible race with vfs_link() and
1165 * avoid unlinking a wrong file. If the last link for a file or directory is
1166 * removed, the FS implementation will deallocate its resources.
1167 *
1168 * @param parent File handle of the parent directory node
1169 * @param child Old name to be unlinked
1170 * @param expect File handle of the unlinked child
1171 *
1172 * @return EOK on success or an error code
1173 */
1174errno_t vfs_unlink(int parent, const char *child, int expect)
1175{
1176 errno_t rc;
1177 aid_t req;
1178
1179 async_exch_t *exch = vfs_exchange_begin();
1180
1181 req = async_send_2(exch, VFS_IN_UNLINK, parent, expect, NULL);
1182 rc = async_data_write_start(exch, child, str_size(child));
1183
1184 vfs_exchange_end(exch);
1185
1186 errno_t rc_orig;
1187 async_wait_for(req, &rc_orig);
1188
1189 if (rc_orig != EOK)
1190 return (errno_t) rc_orig;
1191 return rc;
1192}
1193
1194/** Unlink a file or directory
1195 *
1196 * Unlink a path. If the last link for a file or directory is removed, the FS
1197 * implementation will deallocate its resources.
1198 *
1199 * @param path Old path to be unlinked
1200 *
1201 * @return EOK on success or an error code
1202 */
1203errno_t vfs_unlink_path(const char *path)
1204{
1205 int expect;
1206 errno_t rc = vfs_lookup(path, 0, &expect);
1207 if (rc != EOK)
1208 return rc;
1209
1210 char *child;
1211 int parent;
1212 rc = get_parent_and_child(path, &parent, &child);
1213 if (rc != EOK) {
1214 vfs_put(expect);
1215 return rc;
1216 }
1217
1218 rc = vfs_unlink(parent, child, expect);
1219
1220 free(child);
1221 vfs_put(parent);
1222 vfs_put(expect);
1223 return rc;
1224}
1225
1226/** Unmount a file system
1227 *
1228 * @param mp File handle representing the mount-point
1229 *
1230 * @return EOK on success or an error code
1231 */
1232errno_t vfs_unmount(int mp)
1233{
1234 async_exch_t *exch = vfs_exchange_begin();
1235 errno_t rc = async_req_1_0(exch, VFS_IN_UNMOUNT, mp);
1236 vfs_exchange_end(exch);
1237 return rc;
1238}
1239
1240/** Unmount a file system
1241 *
1242 * @param mpp Mount-point path
1243 *
1244 * @return EOK on success or an error code
1245 */
1246errno_t vfs_unmount_path(const char *mpp)
1247{
1248 int mp;
1249 errno_t rc = vfs_lookup(mpp, WALK_MOUNT_POINT | WALK_DIRECTORY, &mp);
1250 if (rc != EOK)
1251 return rc;
1252
1253 rc = vfs_unmount(mp);
1254 vfs_put(mp);
1255 return rc;
1256}
1257
1258/** Walk a path starting in a parent node
1259 *
1260 * @param parent File handle of the parent node where the walk starts
1261 * @param path Parent-relative path to be walked
1262 * @param flags Flags influencing the walk
1263 * @param[out] handle File handle representing the result on success.
1264 *
1265 * @return Error code.
1266 */
1267errno_t vfs_walk(int parent, const char *path, int flags, int *handle)
1268{
1269 async_exch_t *exch = vfs_exchange_begin();
1270
1271 ipc_call_t answer;
1272 aid_t req = async_send_2(exch, VFS_IN_WALK, parent, flags, &answer);
1273 errno_t rc = async_data_write_start(exch, path, str_size(path));
1274 vfs_exchange_end(exch);
1275
1276 errno_t rc_orig;
1277 async_wait_for(req, &rc_orig);
1278
1279 if (rc_orig != EOK)
1280 return (errno_t) rc_orig;
1281
1282 if (rc != EOK)
1283 return (errno_t) rc;
1284
1285 *handle = (int) IPC_GET_ARG1(answer);
1286 return EOK;
1287}
1288
1289/** Write data
1290 *
1291 * This function fails if it cannot write exactly @a len bytes to the file.
1292 *
1293 * @param file File handle to write to
1294 * @param[inout] pos Position to write to, updated by the actual bytes
1295 * written
1296 * @param buf Data, @a nbytes bytes long
1297 * @param nbytes Number of bytes to write
1298 * @param nwritten Place to store number of bytes written
1299 *
1300 * @return On success, EOK, @a *nwr is filled with number
1301 * of bytes written
1302 * @return On failure, an error code
1303 */
1304errno_t vfs_write(int file, aoff64_t *pos, const void *buf, size_t nbyte,
1305 size_t *nwritten)
1306{
1307 ssize_t cnt = 0;
1308 ssize_t nwr = 0;
1309 const uint8_t *bp = (uint8_t *) buf;
1310 errno_t rc;
1311
1312 do {
1313 bp += cnt;
1314 nwr += cnt;
1315 *pos += cnt;
1316 rc = vfs_write_short(file, *pos, bp, nbyte - nwr, &cnt);
1317 } while (rc == EOK && ((ssize_t)nbyte - nwr - cnt) > 0);
1318
1319 if (rc != EOK) {
1320 *nwritten = nwr;
1321 return rc;
1322 }
1323
1324 nwr += cnt;
1325 *pos += cnt;
1326 *nwritten = nwr;
1327 return EOK;
1328}
1329
1330/** Write bytes to a file
1331 *
1332 * Write up to @a nbyte bytes from file. The actual number of bytes written
1333 * may be lower, but greater than zero.
1334 *
1335 * @param file File handle to write to
1336 * @param[in] pos Position to write to
1337 * @param buf Buffer to write to
1338 * @param nbyte Maximum number of bytes to write
1339 * @param[out] nread Actual number of bytes written (0 or more)
1340 *
1341 * @return EOK on success or an error code
1342 */
1343errno_t vfs_write_short(int file, aoff64_t pos, const void *buf, size_t nbyte,
1344 ssize_t *nwritten)
1345{
1346 errno_t rc;
1347 ipc_call_t answer;
1348 aid_t req;
1349
1350 if (nbyte > DATA_XFER_LIMIT)
1351 nbyte = DATA_XFER_LIMIT;
1352
1353 async_exch_t *exch = vfs_exchange_begin();
1354
1355 req = async_send_3(exch, VFS_IN_WRITE, file, LOWER32(pos),
1356 UPPER32(pos), &answer);
1357 rc = async_data_write_start(exch, (void *) buf, nbyte);
1358
1359 vfs_exchange_end(exch);
1360
1361 if (rc == EOK)
1362 async_wait_for(req, &rc);
1363 else
1364 async_forget(req);
1365
1366 if (rc != EOK)
1367 return rc;
1368
1369 *nwritten = (ssize_t) IPC_GET_ARG1(answer);
1370 return EOK;
1371}
1372
1373/** @}
1374 */
Note: See TracBrowser for help on using the repository browser.