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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since db51219f was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • 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
400/** Return a list of currently available file system types
401 *
402 * @param fstypes Points to structure where list of filesystem types is
403 * stored. It is read as a null-terminated list of strings
404 * fstypes->fstypes[0..]. To free the list use vfs_fstypes_free().
405 *
406 * @return EOK on success or an error code
407 */
408errno_t vfs_fstypes(vfs_fstypes_t *fstypes)
409{
410 sysarg_t size;
411 char *buf;
412 char dummybuf[1];
413 size_t count, i;
414
415 async_exch_t *exch = vfs_exchange_begin();
416 errno_t rc = async_req_0_1(exch, VFS_IN_FSTYPES, &size);
417
418 if (rc != EOK) {
419 vfs_exchange_end(exch);
420 return rc;
421 }
422
423 buf = malloc(size);
424 if (buf == NULL) {
425 buf = dummybuf;
426 size = 1;
427 }
428
429 rc = async_data_read_start(exch, buf, size);
430 vfs_exchange_end(exch);
431
432 if (buf == dummybuf)
433 return ENOMEM;
434
435 /*
436 * Buffer should contain a number of null-terminated strings.
437 * Count them so that we can allocate an index
438 */
439 count = 0;
440 i = 0;
441 while (i < size) {
442 if (buf[i] == '\0')
443 ++count;
444 ++i;
445 }
446
447 if (count == 0) {
448 free(buf);
449 return EIO;
450 }
451
452 fstypes->fstypes = calloc(sizeof(char *), count + 1);
453 if (fstypes->fstypes == NULL) {
454 free(buf);
455 return ENOMEM;
456 }
457
458 /* Now fill the index */
459 if (buf[0] != '\0')
460 fstypes->fstypes[0] = &buf[0];
461 count = 0;
462 i = 0;
463 while (i < size) {
464 if (buf[i] == '\0')
465 fstypes->fstypes[++count] = &buf[i + 1];
466 ++i;
467 }
468 fstypes->fstypes[count] = NULL;
469 fstypes->buf = buf;
470 fstypes->size = size;
471
472 return rc;
473}
474
475/** Free list of file system types.
476 *
477 * @param fstypes List of file system types
478 */
479void vfs_fstypes_free(vfs_fstypes_t *fstypes)
480{
481 free(fstypes->buf);
482 fstypes->buf = NULL;
483 free(fstypes->fstypes);
484 fstypes->fstypes = NULL;
485 fstypes->size = 0;
486}
487
488/** Link a file or directory
489 *
490 * Create a new name and an empty file or an empty directory in a parent
491 * directory. If child with the same name already exists, the function returns
492 * a failure, the existing file remains untouched and no file system object
493 * is created.
494 *
495 * @param parent File handle of the parent directory node
496 * @param child New name to be linked
497 * @param kind Kind of the object to be created: KIND_FILE or
498 * KIND_DIRECTORY
499 * @param[out] linkedfd If not NULL, will receive a file handle to the linked
500 * child
501 * @return EOK on success or an error code
502 */
503errno_t vfs_link(int parent, const char *child, vfs_file_kind_t kind, int *linkedfd)
504{
505 int flags = (kind == KIND_DIRECTORY) ? WALK_DIRECTORY : WALK_REGULAR;
506 int file = -1;
507 errno_t rc = vfs_walk(parent, child, WALK_MUST_CREATE | flags, &file);
508 if (rc != EOK)
509 return rc;
510
511 if (linkedfd)
512 *linkedfd = file;
513 else
514 vfs_put(file);
515
516 return EOK;
517}
518
519/** Link a file or directory
520 *
521 * Create a new name and an empty file or an empty directory at given path.
522 * If a link with the same name already exists, the function returns
523 * a failure, the existing file remains untouched and no file system object
524 * is created.
525 *
526 * @param path New path to be linked
527 * @param kind Kind of the object to be created: KIND_FILE or
528 * KIND_DIRECTORY
529 * @param[out] linkedfd If not NULL, will receive a file handle to the linked
530 * child
531 * @return EOK on success or an error code
532 */
533errno_t vfs_link_path(const char *path, vfs_file_kind_t kind, int *linkedfd)
534{
535 char *child;
536 int parent;
537 errno_t rc = get_parent_and_child(path, &parent, &child);
538 if (rc != EOK)
539 return rc;
540
541 rc = vfs_link(parent, child, kind, linkedfd);
542
543 free(child);
544 vfs_put(parent);
545 return rc;
546}
547
548/** Lookup a path relative to the local root
549 *
550 * @param path Path to be looked up
551 * @param flags Walk flags
552 * @param[out] handle Pointer to variable where handle is to be written.
553 *
554 * @return EOK on success or an error code.
555 */
556errno_t vfs_lookup(const char *path, int flags, int *handle)
557{
558 size_t size;
559 char *p = vfs_absolutize(path, &size);
560 if (!p)
561 return ENOMEM;
562
563 int root = vfs_root();
564 if (root < 0) {
565 free(p);
566 return ENOENT;
567 }
568
569 // XXX: Workaround for GCC diagnostics.
570 *handle = -1;
571
572 errno_t rc = vfs_walk(root, p, flags, handle);
573 vfs_put(root);
574 free(p);
575 return rc;
576}
577
578/** Lookup a path relative to the local root and open the result
579 *
580 * This function is a convenience combo for vfs_lookup() and vfs_open().
581 *
582 * @param path Path to be looked up
583 * @param flags Walk flags
584 * @param mode Mode in which to open file in
585 * @param[out] handle Pointer to variable where handle is to be written.
586 *
587 * @return EOK on success or an error code
588 */
589errno_t vfs_lookup_open(const char *path, int flags, int mode, int *handle)
590{
591 int file;
592 errno_t rc = vfs_lookup(path, flags, &file);
593 if (rc != EOK)
594 return rc;
595
596 rc = vfs_open(file, mode);
597 if (rc != EOK) {
598 vfs_put(file);
599 return rc;
600 }
601
602 *handle = file;
603 return EOK;
604}
605
606/** Mount a file system
607 *
608 * @param[in] mp File handle representing the mount-point
609 * @param[in] fs_name File system name
610 * @param[in] serv Service representing the mountee
611 * @param[in] opts Mount options for the endpoint file system
612 * @param[in] flags Mount flags
613 * @param[in] instance Instance number of the file system server
614 * @param[out] mountedfd File handle of the mounted root if not NULL
615 *
616 * @return EOK on success or an error code
617 */
618errno_t vfs_mount(int mp, const char *fs_name, service_id_t serv, const char *opts,
619 unsigned int flags, unsigned int instance, int *mountedfd)
620{
621 errno_t rc, rc1;
622
623 if (!mountedfd)
624 flags |= VFS_MOUNT_NO_REF;
625 if (mp < 0)
626 flags |= VFS_MOUNT_CONNECT_ONLY;
627
628 ipc_call_t answer;
629 async_exch_t *exch = vfs_exchange_begin();
630 aid_t req = async_send_4(exch, VFS_IN_MOUNT, mp, serv, flags, instance,
631 &answer);
632
633 rc1 = async_data_write_start(exch, (void *) opts, str_size(opts));
634 if (rc1 == EOK) {
635 rc1 = async_data_write_start(exch, (void *) fs_name,
636 str_size(fs_name));
637 }
638
639 vfs_exchange_end(exch);
640
641 async_wait_for(req, &rc);
642
643 if (mountedfd)
644 *mountedfd = (int) IPC_GET_ARG1(answer);
645
646 if (rc != EOK)
647 return rc;
648 return rc1;
649}
650
651/** Mount a file system
652 *
653 * @param[in] mp Path representing the mount-point
654 * @param[in] fs_name File system name
655 * @param[in] fqsn Fully qualified service name of the mountee
656 * @param[in] opts Mount options for the endpoint file system
657 * @param[in] flags Mount flags
658 * @param[in] instance Instance number of the file system server
659 *
660 * @return EOK on success or an error code
661 */
662errno_t vfs_mount_path(const char *mp, const char *fs_name, const char *fqsn,
663 const char *opts, unsigned int flags, unsigned int instance)
664{
665 int null_id = -1;
666 char null[LOC_NAME_MAXLEN];
667
668 if (str_cmp(fqsn, "") == 0) {
669 /*
670 * No device specified, create a fresh null/%d device instead.
671 */
672 null_id = loc_null_create();
673
674 if (null_id == -1)
675 return ENOMEM;
676
677 snprintf(null, LOC_NAME_MAXLEN, "null/%d", null_id);
678 fqsn = null;
679 }
680
681 if (flags & IPC_FLAG_BLOCKING)
682 flags = VFS_MOUNT_BLOCKING;
683 else
684 flags = 0;
685
686 service_id_t service_id;
687 errno_t res = loc_service_get_id(fqsn, &service_id, flags);
688 if (res != EOK) {
689 if (null_id != -1)
690 loc_null_destroy(null_id);
691
692 return res;
693 }
694
695 size_t mpa_size;
696 char *mpa = vfs_absolutize(mp, &mpa_size);
697 if (mpa == NULL) {
698 if (null_id != -1)
699 loc_null_destroy(null_id);
700
701 return ENOMEM;
702 }
703
704 fibril_mutex_lock(&root_mutex);
705
706 errno_t rc;
707
708 if (str_cmp(mpa, "/") == 0) {
709 /* Mounting root. */
710
711 if (root_fd >= 0) {
712 fibril_mutex_unlock(&root_mutex);
713 if (null_id != -1)
714 loc_null_destroy(null_id);
715 return EBUSY;
716 }
717
718 int root;
719 rc = vfs_mount(-1, fs_name, service_id, opts, flags, instance,
720 &root);
721 if (rc == EOK)
722 root_fd = root;
723 } else {
724 if (root_fd < 0) {
725 fibril_mutex_unlock(&root_mutex);
726 if (null_id != -1)
727 loc_null_destroy(null_id);
728 return EINVAL;
729 }
730
731 int mpfd;
732 rc = vfs_walk(root_fd, mpa, WALK_DIRECTORY, &mpfd);
733 if (rc == EOK) {
734 rc = vfs_mount(mpfd, fs_name, service_id, opts, flags,
735 instance, NULL);
736 vfs_put(mpfd);
737 }
738 }
739
740 fibril_mutex_unlock(&root_mutex);
741
742 if ((rc != EOK) && (null_id != -1))
743 loc_null_destroy(null_id);
744
745 return (errno_t) rc;
746}
747
748
749/** Open a file handle for I/O
750 *
751 * @param file File handle to enable I/O on
752 * @param mode Mode in which to open file in
753 *
754 * @return EOK on success or an error code
755 */
756errno_t vfs_open(int file, int mode)
757{
758 async_exch_t *exch = vfs_exchange_begin();
759 errno_t rc = async_req_2_0(exch, VFS_IN_OPEN, file, mode);
760 vfs_exchange_end(exch);
761
762 return rc;
763}
764
765/** Pass a file handle to another VFS client
766 *
767 * @param vfs_exch Donor's VFS exchange
768 * @param file Donor's file handle to pass
769 * @param exch Exchange to the acceptor
770 *
771 * @return EOK on success or an error code
772 */
773errno_t vfs_pass_handle(async_exch_t *vfs_exch, int file, async_exch_t *exch)
774{
775 return async_state_change_start(exch, VFS_PASS_HANDLE, (sysarg_t) file,
776 0, vfs_exch);
777}
778
779/** Stop working with a file handle
780 *
781 * @param file File handle to put
782 *
783 * @return EOK on success or an error code
784 */
785errno_t vfs_put(int file)
786{
787 async_exch_t *exch = vfs_exchange_begin();
788 errno_t rc = async_req_1_0(exch, VFS_IN_PUT, file);
789 vfs_exchange_end(exch);
790
791 return rc;
792}
793
794/** Receive a file handle from another VFS client
795 *
796 * @param high If true, the received file handle will be allocated from high
797 * indices
798 * @param[out] handle Received handle.
799 *
800 * @return EOK on success or an error code
801 */
802errno_t vfs_receive_handle(bool high, int *handle)
803{
804 ipc_call_t call;
805 if (!async_state_change_receive(&call)) {
806 async_answer_0(&call, EINVAL);
807 return EINVAL;
808 }
809
810 async_exch_t *vfs_exch = vfs_exchange_begin();
811
812 async_state_change_finalize(&call, vfs_exch);
813
814 sysarg_t ret;
815 errno_t rc = async_req_1_1(vfs_exch, VFS_IN_WAIT_HANDLE, high, &ret);
816
817 async_exchange_end(vfs_exch);
818
819 if (rc == EOK) {
820 *handle = (int) ret;
821 }
822
823 return rc;
824}
825
826/** Read data
827 *
828 * Read up to @a nbytes bytes from file if available. This function always reads
829 * all the available bytes up to @a nbytes.
830 *
831 * @param file File handle to read from
832 * @param[inout] pos Position to read from, updated by the actual bytes read
833 * @param buf Buffer, @a nbytes bytes long
834 * @param nbytes Number of bytes to read
835 * @param nread Place to store number of bytes actually read
836 *
837 * @return On success, EOK and @a *nread is filled with number
838 * of bytes actually read.
839 * @return On failure, an error code
840 */
841errno_t vfs_read(int file, aoff64_t *pos, void *buf, size_t nbyte, size_t *nread)
842{
843 ssize_t cnt = 0;
844 size_t nr = 0;
845 uint8_t *bp = (uint8_t *) buf;
846 errno_t rc;
847
848 do {
849 bp += cnt;
850 nr += cnt;
851 *pos += cnt;
852 rc = vfs_read_short(file, *pos, bp, nbyte - nr, &cnt);
853 } while (rc == EOK && cnt > 0 && (nbyte - nr - cnt) > 0);
854
855 if (rc != EOK) {
856 *nread = nr;
857 return rc;
858 }
859
860 nr += cnt;
861 *pos += cnt;
862 *nread = nr;
863 return EOK;
864}
865
866/** Read bytes from a file
867 *
868 * Read up to @a nbyte bytes from file. The actual number of bytes read
869 * may be lower, but greater than zero if there are any bytes available.
870 * If there are no bytes available for reading, then the function will
871 * return success with zero bytes read.
872 *
873 * @param file File handle to read from
874 * @param[in] pos Position to read from
875 * @param buf Buffer to read from
876 * @param nbyte Maximum number of bytes to read
877 * @param[out] nread Actual number of bytes read (0 or more)
878 *
879 * @return EOK on success or an error code
880 */
881errno_t vfs_read_short(int file, aoff64_t pos, void *buf, size_t nbyte,
882 ssize_t *nread)
883{
884 errno_t rc;
885 ipc_call_t answer;
886 aid_t req;
887
888 if (nbyte > DATA_XFER_LIMIT)
889 nbyte = DATA_XFER_LIMIT;
890
891 async_exch_t *exch = vfs_exchange_begin();
892
893 req = async_send_3(exch, VFS_IN_READ, file, LOWER32(pos),
894 UPPER32(pos), &answer);
895 rc = async_data_read_start(exch, (void *) buf, nbyte);
896
897 vfs_exchange_end(exch);
898
899 if (rc == EOK)
900 async_wait_for(req, &rc);
901 else
902 async_forget(req);
903
904 if (rc != EOK)
905 return rc;
906
907 *nread = (ssize_t) IPC_GET_ARG1(answer);
908 return EOK;
909}
910
911/** Rename a file or directory
912 *
913 * There is no file-handle-based variant to disallow attempts to introduce loops
914 * and breakage in the directory tree when relinking eg. a node under its own
915 * descendant. The path-based variant is not susceptible because the VFS can
916 * prevent this lexically by comparing the paths.
917 *
918 * @param old Old path
919 * @param new New path
920 *
921 * @return EOK on success or an error code
922 */
923errno_t vfs_rename_path(const char *old, const char *new)
924{
925 errno_t rc;
926 errno_t rc_orig;
927 aid_t req;
928
929 size_t olda_size;
930 char *olda = vfs_absolutize(old, &olda_size);
931 if (olda == NULL)
932 return ENOMEM;
933
934 size_t newa_size;
935 char *newa = vfs_absolutize(new, &newa_size);
936 if (newa == NULL) {
937 free(olda);
938 return ENOMEM;
939 }
940
941 async_exch_t *exch = vfs_exchange_begin();
942 int root = vfs_root();
943 if (root < 0) {
944 free(olda);
945 free(newa);
946 return ENOENT;
947 }
948
949 req = async_send_1(exch, VFS_IN_RENAME, root, NULL);
950 rc = async_data_write_start(exch, olda, olda_size);
951 if (rc != EOK) {
952 vfs_exchange_end(exch);
953 free(olda);
954 free(newa);
955 vfs_put(root);
956 async_wait_for(req, &rc_orig);
957 if (rc_orig != EOK)
958 rc = rc_orig;
959 return rc;
960 }
961 rc = async_data_write_start(exch, newa, newa_size);
962 if (rc != EOK) {
963 vfs_exchange_end(exch);
964 free(olda);
965 free(newa);
966 vfs_put(root);
967 async_wait_for(req, &rc_orig);
968 if (rc_orig != EOK)
969 rc = rc_orig;
970 return rc;
971 }
972 vfs_exchange_end(exch);
973 free(olda);
974 free(newa);
975 vfs_put(root);
976 async_wait_for(req, &rc);
977
978 return rc;
979}
980
981/** Resize file to a specified length
982 *
983 * Resize file so that its size is exactly @a length.
984 *
985 * @param file File handle to resize
986 * @param length New length
987 *
988 * @return EOK on success or an error code
989 */
990errno_t vfs_resize(int file, aoff64_t length)
991{
992 async_exch_t *exch = vfs_exchange_begin();
993 errno_t rc = async_req_3_0(exch, VFS_IN_RESIZE, file, LOWER32(length),
994 UPPER32(length));
995 vfs_exchange_end(exch);
996
997 return rc;
998}
999
1000/** Return a new file handle representing the local root
1001 *
1002 * @return A clone of the local root file handle or -1
1003 */
1004int vfs_root(void)
1005{
1006 fibril_mutex_lock(&root_mutex);
1007 int fd;
1008 if (root_fd < 0) {
1009 fd = -1;
1010 } else {
1011 errno_t rc = vfs_clone(root_fd, -1, true, &fd);
1012 if (rc != EOK) {
1013 fd = -1;
1014 }
1015 }
1016 fibril_mutex_unlock(&root_mutex);
1017 return fd;
1018}
1019
1020/** Set a new local root
1021 *
1022 * Note that it is still possible to have file handles for other roots and pass
1023 * them to the API functions. Functions like vfs_root() and vfs_lookup() will
1024 * however consider the file set by this function to be the root.
1025 *
1026 * @param nroot The new local root file handle
1027 *
1028 * @return Error code
1029 */
1030errno_t vfs_root_set(int nroot)
1031{
1032 int new_root;
1033 errno_t rc = vfs_clone(nroot, -1, true, &new_root);
1034 if (rc != EOK) {
1035 return rc;
1036 }
1037
1038 fibril_mutex_lock(&root_mutex);
1039 if (root_fd >= 0)
1040 vfs_put(root_fd);
1041 root_fd = new_root;
1042 fibril_mutex_unlock(&root_mutex);
1043
1044 return EOK;
1045}
1046
1047/** Get file information
1048 *
1049 * @param file File handle to get information about
1050 * @param[out] stat Place to store file information
1051 *
1052 * @return EOK on success or an error code
1053 */
1054errno_t vfs_stat(int file, vfs_stat_t *stat)
1055{
1056 errno_t rc;
1057 aid_t req;
1058
1059 async_exch_t *exch = vfs_exchange_begin();
1060
1061 req = async_send_1(exch, VFS_IN_STAT, file, NULL);
1062 rc = async_data_read_start(exch, (void *) stat, sizeof(vfs_stat_t));
1063 if (rc != EOK) {
1064 vfs_exchange_end(exch);
1065
1066 errno_t rc_orig;
1067 async_wait_for(req, &rc_orig);
1068
1069 if (rc_orig != EOK)
1070 rc = rc_orig;
1071
1072 return rc;
1073 }
1074
1075 vfs_exchange_end(exch);
1076 async_wait_for(req, &rc);
1077
1078 return rc;
1079}
1080
1081/** Get file information
1082 *
1083 * @param path File path to get information about
1084 * @param[out] stat Place to store file information
1085 *
1086 * @return EOK on success or an error code
1087 */
1088errno_t vfs_stat_path(const char *path, vfs_stat_t *stat)
1089{
1090 int file;
1091 errno_t rc = vfs_lookup(path, 0, &file);
1092 if (rc != EOK)
1093 return rc;
1094
1095 rc = vfs_stat(file, stat);
1096
1097 vfs_put(file);
1098
1099 return rc;
1100}
1101
1102/** Get filesystem statistics
1103 *
1104 * @param file File located on the queried file system
1105 * @param[out] st Buffer for storing information
1106 *
1107 * @return EOK on success or an error code
1108 */
1109errno_t vfs_statfs(int file, vfs_statfs_t *st)
1110{
1111 errno_t rc, ret;
1112 aid_t req;
1113
1114 async_exch_t *exch = vfs_exchange_begin();
1115
1116 req = async_send_1(exch, VFS_IN_STATFS, file, NULL);
1117 rc = async_data_read_start(exch, (void *) st, sizeof(*st));
1118
1119 vfs_exchange_end(exch);
1120 async_wait_for(req, &ret);
1121
1122 rc = (ret != EOK ? ret : rc);
1123
1124 return rc;
1125}
1126
1127/** Get filesystem statistics
1128 *
1129 * @param file Path pointing to the queried file system
1130 * @param[out] st Buffer for storing information
1131 *
1132 * @return EOK on success or an error code
1133 */
1134errno_t vfs_statfs_path(const char *path, vfs_statfs_t *st)
1135{
1136 int file;
1137 errno_t rc = vfs_lookup(path, 0, &file);
1138 if (rc != EOK)
1139 return rc;
1140
1141 rc = vfs_statfs(file, st);
1142
1143 vfs_put(file);
1144
1145 return rc;
1146}
1147
1148/** Synchronize file
1149 *
1150 * @param file File handle to synchronize
1151 *
1152 * @return EOK on success or an error code
1153 */
1154errno_t vfs_sync(int file)
1155{
1156 async_exch_t *exch = vfs_exchange_begin();
1157 errno_t rc = async_req_1_0(exch, VFS_IN_SYNC, file);
1158 vfs_exchange_end(exch);
1159
1160 return rc;
1161}
1162
1163/** Unlink a file or directory
1164 *
1165 * Unlink a name from a parent directory. The caller can supply the file handle
1166 * of the unlinked child in order to detect a possible race with vfs_link() and
1167 * avoid unlinking a wrong file. If the last link for a file or directory is
1168 * removed, the FS implementation will deallocate its resources.
1169 *
1170 * @param parent File handle of the parent directory node
1171 * @param child Old name to be unlinked
1172 * @param expect File handle of the unlinked child
1173 *
1174 * @return EOK on success or an error code
1175 */
1176errno_t vfs_unlink(int parent, const char *child, int expect)
1177{
1178 errno_t rc;
1179 aid_t req;
1180
1181 async_exch_t *exch = vfs_exchange_begin();
1182
1183 req = async_send_2(exch, VFS_IN_UNLINK, parent, expect, NULL);
1184 rc = async_data_write_start(exch, child, str_size(child));
1185
1186 vfs_exchange_end(exch);
1187
1188 errno_t rc_orig;
1189 async_wait_for(req, &rc_orig);
1190
1191 if (rc_orig != EOK)
1192 return (errno_t) rc_orig;
1193 return rc;
1194}
1195
1196/** Unlink a file or directory
1197 *
1198 * Unlink a path. If the last link for a file or directory is removed, the FS
1199 * implementation will deallocate its resources.
1200 *
1201 * @param path Old path to be unlinked
1202 *
1203 * @return EOK on success or an error code
1204 */
1205errno_t vfs_unlink_path(const char *path)
1206{
1207 int expect;
1208 errno_t rc = vfs_lookup(path, 0, &expect);
1209 if (rc != EOK)
1210 return rc;
1211
1212 char *child;
1213 int parent;
1214 rc = get_parent_and_child(path, &parent, &child);
1215 if (rc != EOK) {
1216 vfs_put(expect);
1217 return rc;
1218 }
1219
1220 rc = vfs_unlink(parent, child, expect);
1221
1222 free(child);
1223 vfs_put(parent);
1224 vfs_put(expect);
1225 return rc;
1226}
1227
1228/** Unmount a file system
1229 *
1230 * @param mp File handle representing the mount-point
1231 *
1232 * @return EOK on success or an error code
1233 */
1234errno_t vfs_unmount(int mp)
1235{
1236 async_exch_t *exch = vfs_exchange_begin();
1237 errno_t rc = async_req_1_0(exch, VFS_IN_UNMOUNT, mp);
1238 vfs_exchange_end(exch);
1239 return rc;
1240}
1241
1242/** Unmount a file system
1243 *
1244 * @param mpp Mount-point path
1245 *
1246 * @return EOK on success or an error code
1247 */
1248errno_t vfs_unmount_path(const char *mpp)
1249{
1250 int mp;
1251 errno_t rc = vfs_lookup(mpp, WALK_MOUNT_POINT | WALK_DIRECTORY, &mp);
1252 if (rc != EOK)
1253 return rc;
1254
1255 rc = vfs_unmount(mp);
1256 vfs_put(mp);
1257 return rc;
1258}
1259
1260/** Walk a path starting in a parent node
1261 *
1262 * @param parent File handle of the parent node where the walk starts
1263 * @param path Parent-relative path to be walked
1264 * @param flags Flags influencing the walk
1265 * @param[out] handle File handle representing the result on success.
1266 *
1267 * @return Error code.
1268 */
1269errno_t vfs_walk(int parent, const char *path, int flags, int *handle)
1270{
1271 async_exch_t *exch = vfs_exchange_begin();
1272
1273 ipc_call_t answer;
1274 aid_t req = async_send_2(exch, VFS_IN_WALK, parent, flags, &answer);
1275 errno_t rc = async_data_write_start(exch, path, str_size(path));
1276 vfs_exchange_end(exch);
1277
1278 errno_t rc_orig;
1279 async_wait_for(req, &rc_orig);
1280
1281 if (rc_orig != EOK)
1282 return (errno_t) rc_orig;
1283
1284 if (rc != EOK)
1285 return (errno_t) rc;
1286
1287 *handle = (int) IPC_GET_ARG1(answer);
1288 return EOK;
1289}
1290
1291/** Write data
1292 *
1293 * This function fails if it cannot write exactly @a len bytes to the file.
1294 *
1295 * @param file File handle to write to
1296 * @param[inout] pos Position to write to, updated by the actual bytes
1297 * written
1298 * @param buf Data, @a nbytes bytes long
1299 * @param nbytes Number of bytes to write
1300 * @param nwritten Place to store number of bytes written
1301 *
1302 * @return On success, EOK, @a *nwr is filled with number
1303 * of bytes written
1304 * @return On failure, an error code
1305 */
1306errno_t vfs_write(int file, aoff64_t *pos, const void *buf, size_t nbyte,
1307 size_t *nwritten)
1308{
1309 ssize_t cnt = 0;
1310 ssize_t nwr = 0;
1311 const uint8_t *bp = (uint8_t *) buf;
1312 errno_t rc;
1313
1314 do {
1315 bp += cnt;
1316 nwr += cnt;
1317 *pos += cnt;
1318 rc = vfs_write_short(file, *pos, bp, nbyte - nwr, &cnt);
1319 } while (rc == EOK && ((ssize_t)nbyte - nwr - cnt) > 0);
1320
1321 if (rc != EOK) {
1322 *nwritten = nwr;
1323 return rc;
1324 }
1325
1326 nwr += cnt;
1327 *pos += cnt;
1328 *nwritten = nwr;
1329 return EOK;
1330}
1331
1332/** Write bytes to a file
1333 *
1334 * Write up to @a nbyte bytes from file. The actual number of bytes written
1335 * may be lower, but greater than zero.
1336 *
1337 * @param file File handle to write to
1338 * @param[in] pos Position to write to
1339 * @param buf Buffer to write to
1340 * @param nbyte Maximum number of bytes to write
1341 * @param[out] nread Actual number of bytes written (0 or more)
1342 *
1343 * @return EOK on success or an error code
1344 */
1345errno_t vfs_write_short(int file, aoff64_t pos, const void *buf, size_t nbyte,
1346 ssize_t *nwritten)
1347{
1348 errno_t rc;
1349 ipc_call_t answer;
1350 aid_t req;
1351
1352 if (nbyte > DATA_XFER_LIMIT)
1353 nbyte = DATA_XFER_LIMIT;
1354
1355 async_exch_t *exch = vfs_exchange_begin();
1356
1357 req = async_send_3(exch, VFS_IN_WRITE, file, LOWER32(pos),
1358 UPPER32(pos), &answer);
1359 rc = async_data_write_start(exch, (void *) buf, nbyte);
1360
1361 vfs_exchange_end(exch);
1362
1363 if (rc == EOK)
1364 async_wait_for(req, &rc);
1365 else
1366 async_forget(req);
1367
1368 if (rc != EOK)
1369 return rc;
1370
1371 *nwritten = (ssize_t) IPC_GET_ARG1(answer);
1372 return EOK;
1373}
1374
1375/** @}
1376 */
Note: See TracBrowser for help on using the repository browser.