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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0239846 was 8d2dd7f2, checked in by Jakub Jermar <jakub@…>, 8 years ago

Reduce the number of files that include <sys/types.h>

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