source: mainline/uspace/lib/c/generic/vfs/vfs.c@ 3345b86

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3345b86 was d2c8533, checked in by Jiri Svoboda <jiri@…>, 9 years ago

File system probing groundwork. Only MFS can do it for now.

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