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

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

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

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