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

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

Let vfs_link() and vfs_link_path() return the linked file handle

Add an output argument to both vfs_link() and vfs_link_path() so that the
client may receive the file handle of the linked file or directory. This
makes it easy to work with the new file or directory as it is ready to be
opened or in case of a directory to use it as a parent for further
operations without the need for additional vfs_lookup().

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