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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a53ed3a was a53ed3a, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Change (rc != 0) to (rc != EOK), where appropriate.

  • Property mode set to 100644
File size: 33.0 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 = vfs_absolutize(path, &abs_size);
288 if (!abs)
289 return ENOMEM;
290
291 int fd;
292 errno_t rc = vfs_lookup(abs, WALK_DIRECTORY, &fd);
293 if (rc != EOK) {
294 free(abs);
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;
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 struct stat 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_callid_t callid;
805 if (!async_state_change_receive(&callid, NULL, NULL, NULL)) {
806 async_answer_0(callid, EINVAL);
807 return EINVAL;
808 }
809
810 async_exch_t *vfs_exch = vfs_exchange_begin();
811
812 async_state_change_finalize(callid, 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, struct stat *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(struct stat));
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, struct stat *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, struct statfs *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, struct statfs *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.