source: mainline/uspace/lib/c/generic/vfs/vfs.c@ 3ba431a

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

Sort functions in vfs/vfs.[ch] alphabetically

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