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

Last change on this file was 01900b6, checked in by Martin Decky <martin@…>, 5 years ago

Use an optional output argument instead of errno to propagate the error

The use of errno is troublesome in all other than top-level library
functions since the value in errno might get overwritten by subsequent
inner calls on the error path (e.g. cleanup, deallocation, etc.). The
optional output argument makes it possible to explicitly ignore the
error code if it is not needed, but still to pass it reliably back to
the original caller.

This change affecs async_connect_me_to(),
async_connect_me_to_blocking(), async_connect_kbox(), service_connect(),
service_connect_blocking() and loader_connect().

  • 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, NULL);
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.