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

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

Mount should be able to print the list of available file system types.

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