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

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

Sort functions in vfs/vfs.[ch] alphabetically

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