source: mainline/uspace/lib/c/generic/vfs/vfs.c@ 25a179e

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

IPC return values are always errno constants. Adjust types to reflect that.

In principle, IPC server is not allowed to return non-errno values via
the "main" return value, because kernel interprets it (e.g. EHANGUP).
It's still possible to return arbitrary additional return values alongside EOK,
which are not interpreted in normal communication.

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