source: mainline/uspace/lib/c/generic/vfs/vfs.c@ 6ac1243d

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

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

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