/*
* Copyright (c) 2008 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @addtogroup libc
* @{
*/
/** @file
*/
#include <vfs/vfs.h>
#include <vfs/canonify.h>
#include <vfs/vfs_mtab.h>
#include <vfs/vfs_sess.h>
#include <macros.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <ipc/services.h>
#include <ns.h>
#include <async.h>
#include <fibril_synch.h>
#include <errno.h>
#include <assert.h>
#include <str.h>
#include <loc.h>
#include <ipc/vfs.h>
#include <ipc/loc.h>
/*
* This file contains the implementation of the native HelenOS file system API.
*
* The API supports client-side file system roots, client-side IO cursors and
* uses file handles as a primary means to refer to files. In order to call the
* API functions, one just includes vfs/vfs.h.
*
* The API functions come in two main flavors:
*
* - functions that operate on integer file handles, such as:
* vfs_walk(), vfs_open(), vfs_read(), vfs_link(), ...
*
* - functions that operate on paths, such as:
* vfs_lookup(), vfs_link_path(), vfs_unlink_path(), vfs_rename_path(), ...
*
* There is usually a corresponding path function for each file handle function
* that exists mostly as a convenience wrapper, except for cases when only a
* path version exists due to file system consistency considerations (see
* vfs_rename_path()). Sometimes one of the versions does not make sense, in
* which case it is also omitted.
*
* Besides of that, the API provides some convenience wrappers for frequently
* performed pairs of operations, for example there is a combo API for
* vfs_lookup() and vfs_open(): vfs_lookup_open().
*
* Some of the functions here return a file handle that can be passed to other
* functions. Note that a file handle does not automatically represent a file
* from which one can read or to which one can write. In order to do so, the
* file handle must be opened first for reading/writing using vfs_open().
*
* All file handles, no matter whether opened or not, must be eventually
* returned to the system using vfs_put(). Non-returned file handles are in use
* and consume system resources.
*
* Functions that return errno_t return an error code on error and do not
* set errno. Depending on function, success is signalled by returning either
* EOK or a non-negative file handle.
*
* An example life-cycle of a file handle is as follows:
*
* #include <vfs/vfs.h>
*
* int file = vfs_lookup("/foo/bar/foobar", WALK_REGULAR);
* if (file < 0)
* return file;
* errno_t rc = vfs_open(file, MODE_READ);
* if (rc != EOK) {
* (void) vfs_put(file);
* return rc;
* }
* aoff64_t pos = 42;
* char buf[512];
* size_t nread;
* rc = vfs_read(file, &pos, buf, sizeof(buf), &nread);
* if (rc != EOK) {
* vfs_put(file);
* return rc;
* }
*
* // buf is now filled with nread bytes from file
*
* vfs_put(file);
*/
static FIBRIL_MUTEX_INITIALIZE(vfs_mutex);
static async_sess_t *vfs_sess = NULL;
static FIBRIL_MUTEX_INITIALIZE(cwd_mutex);
static int cwd_fd = -1;
static char *cwd_path = NULL;
static size_t cwd_size = 0;
static FIBRIL_MUTEX_INITIALIZE(root_mutex);
static int root_fd = -1;
static errno_t get_parent_and_child(const char *path, int *parent, char **child)
{
size_t size;
char *apath = vfs_absolutize(path, &size);
if (!apath)
return ENOMEM;
char *slash = str_rchr(apath, L'/');
if (slash == apath) {
*parent = vfs_root();
if (*parent < 0) {
free(apath);
return EBADF;
}
*child = apath;
return EOK;
} else {
*slash = '\0';
errno_t rc = vfs_lookup(apath, WALK_DIRECTORY, parent);
if (rc != EOK) {
free(apath);
return rc;
}
*slash = '/';
*child = str_dup(slash);
free(apath);
if (!*child) {
vfs_put(*parent);
return ENOMEM;
}
return rc;
}
}
/** Make a potentially relative path absolute
*
* This function coverts a current-working-directory-relative path into a
* well-formed, absolute path. The caller is responsible for deallocating the
* returned buffer.
*
* @param[in] path Path to be absolutized
* @param[out] retlen Length of the absolutized path
*
* @return New buffer holding the absolutized path or NULL
*/
char *vfs_absolutize(const char *path, size_t *retlen)
{
char *ncwd_path;
char *ncwd_path_nc;
fibril_mutex_lock(&cwd_mutex);
size_t size = str_size(path);
if (*path != '/') {
if (cwd_path == NULL) {
fibril_mutex_unlock(&cwd_mutex);
return NULL;
}
ncwd_path_nc = malloc(cwd_size + 1 + size + 1);
if (ncwd_path_nc == NULL) {
fibril_mutex_unlock(&cwd_mutex);
return NULL;
}
str_cpy(ncwd_path_nc, cwd_size + 1 + size + 1, cwd_path);
ncwd_path_nc[cwd_size] = '/';
ncwd_path_nc[cwd_size + 1] = '\0';
} else {
ncwd_path_nc = malloc(size + 1);
if (ncwd_path_nc == NULL) {
fibril_mutex_unlock(&cwd_mutex);
return NULL;
}
ncwd_path_nc[0] = '\0';
}
str_append(ncwd_path_nc, cwd_size + 1 + size + 1, path);
ncwd_path = canonify(ncwd_path_nc, retlen);
if (ncwd_path == NULL) {
fibril_mutex_unlock(&cwd_mutex);
free(ncwd_path_nc);
return NULL;
}
/*
* We need to clone ncwd_path because canonify() works in-place and thus
* the address in ncwd_path need not be the same as ncwd_path_nc, even
* though they both point into the same dynamically allocated buffer.
*/
ncwd_path = str_dup(ncwd_path);
free(ncwd_path_nc);
if (ncwd_path == NULL) {
fibril_mutex_unlock(&cwd_mutex);
return NULL;
}
fibril_mutex_unlock(&cwd_mutex);
return ncwd_path;
}
/** Clone a file handle
*
* The caller can choose whether to clone an existing file handle into another
* already existing file handle (in which case it is first closed) or to a new
* file handle allocated either from low or high indices.
*
* @param file_from Source file handle
* @param file_to Destination file handle or -1
* @param high If file_to is -1, high controls whether the new file
* handle will be allocated from high indices
*
* @return New file handle on success or an error code
*/
errno_t vfs_clone(int file_from, int file_to, bool high, int *handle)
{
assert(handle != NULL);
async_exch_t *vfs_exch = vfs_exchange_begin();
sysarg_t ret;
errno_t rc = async_req_3_1(vfs_exch, VFS_IN_CLONE, (sysarg_t) file_from,
(sysarg_t) file_to, (sysarg_t) high, &ret);
vfs_exchange_end(vfs_exch);
if (rc == EOK) {
*handle = ret;
}
return rc;
}
/** Get current working directory path
*
* @param[out] buf Buffer
* @param size Size of @a buf
*
* @return EOK on success or a non-error code
*/
errno_t vfs_cwd_get(char *buf, size_t size)
{
fibril_mutex_lock(&cwd_mutex);
if ((cwd_size == 0) || (size < cwd_size + 1)) {
fibril_mutex_unlock(&cwd_mutex);
return ERANGE;
}
str_cpy(buf, size, cwd_path);
fibril_mutex_unlock(&cwd_mutex);
return EOK;
}
/** Change working directory
*
* @param path Path of the new working directory
*
* @return EOK on success or an error code
*/
errno_t vfs_cwd_set(const char *path)
{
size_t abs_size;
char *abs_path = vfs_absolutize(path, &abs_size);
if (abs_path == NULL)
return ENOMEM;
int fd;
errno_t rc = vfs_lookup(abs_path, WALK_DIRECTORY, &fd);
if (rc != EOK) {
free(abs_path);
return rc;
}
fibril_mutex_lock(&cwd_mutex);
if (cwd_fd >= 0)
vfs_put(cwd_fd);
if (cwd_path)
free(cwd_path);
cwd_fd = fd;
cwd_path = abs_path;
cwd_size = abs_size;
fibril_mutex_unlock(&cwd_mutex);
return EOK;
}
/** Start an async exchange on the VFS session
*
* @return New exchange
*/
async_exch_t *vfs_exchange_begin(void)
{
fibril_mutex_lock(&vfs_mutex);
while (vfs_sess == NULL) {
vfs_sess = service_connect_blocking(SERVICE_VFS, INTERFACE_VFS,
0, NULL);
}
fibril_mutex_unlock(&vfs_mutex);
return async_exchange_begin(vfs_sess);
}
/** Finish an async exchange on the VFS session
*
* @param exch Exchange to be finished
*/
void vfs_exchange_end(async_exch_t *exch)
{
async_exchange_end(exch);
}
/** Open session to service represented by a special file
*
* Given that the file referred to by @a file represents a service,
* open a session to that service.
*
* @param file File handle representing a service
* @param iface Interface to connect to (XXX Should be automatic)
*
* @return Session pointer on success.
* @return @c NULL or error.
*/
async_sess_t *vfs_fd_session(int file, iface_t iface)
{
vfs_stat_t stat;
errno_t rc = vfs_stat(file, &stat);
if (rc != EOK)
return NULL;
if (stat.service == 0)
return NULL;
return loc_service_connect(stat.service, iface, 0);
}
/** Determine if a device contains the specified file system type. If so,
* return identification information.
*
* @param fs_name File system name
* @param serv Service representing the mountee
* @param info Place to store volume identification information
*
* @return EOK on success or an error code
*/
errno_t vfs_fsprobe(const char *fs_name, service_id_t serv,
vfs_fs_probe_info_t *info)
{
errno_t rc;
ipc_call_t answer;
async_exch_t *exch = vfs_exchange_begin();
aid_t req = async_send_1(exch, VFS_IN_FSPROBE, serv, &answer);
rc = async_data_write_start(exch, (void *) fs_name,
str_size(fs_name));
async_wait_for(req, &rc);
if (rc != EOK) {
vfs_exchange_end(exch);
return rc;
}
rc = async_data_read_start(exch, info, sizeof(*info));
vfs_exchange_end(exch);
return rc;
}
/** Return a list of currently available file system types
*
* @param fstypes Points to structure where list of filesystem types is
* stored. It is read as a null-terminated list of strings
* fstypes->fstypes[0..]. To free the list use vfs_fstypes_free().
*
* @return EOK on success or an error code
*/
errno_t vfs_fstypes(vfs_fstypes_t *fstypes)
{
sysarg_t size;
char *buf;
char dummybuf[1];
size_t count, i;
async_exch_t *exch = vfs_exchange_begin();
errno_t rc = async_req_0_1(exch, VFS_IN_FSTYPES, &size);
if (rc != EOK) {
vfs_exchange_end(exch);
return rc;
}
buf = malloc(size);
if (buf == NULL) {
buf = dummybuf;
size = 1;
}
rc = async_data_read_start(exch, buf, size);
vfs_exchange_end(exch);
if (buf == dummybuf)
return ENOMEM;
/*
* Buffer should contain a number of null-terminated strings.
* Count them so that we can allocate an index
*/
count = 0;
i = 0;
while (i < size) {
if (buf[i] == '\0')
++count;
++i;
}
if (count == 0) {
free(buf);
return EIO;
}
fstypes->fstypes = calloc(sizeof(char *), count + 1);
if (fstypes->fstypes == NULL) {
free(buf);
return ENOMEM;
}
/* Now fill the index */
if (buf[0] != '\0')
fstypes->fstypes[0] = &buf[0];
count = 0;
i = 0;
while (i < size) {
if (buf[i] == '\0')
fstypes->fstypes[++count] = &buf[i + 1];
++i;
}
fstypes->fstypes[count] = NULL;
fstypes->buf = buf;
fstypes->size = size;
return rc;
}
/** Free list of file system types.
*
* @param fstypes List of file system types
*/
void vfs_fstypes_free(vfs_fstypes_t *fstypes)
{
free(fstypes->buf);
fstypes->buf = NULL;
free(fstypes->fstypes);
fstypes->fstypes = NULL;
fstypes->size = 0;
}
/** Link a file or directory
*
* Create a new name and an empty file or an empty directory in a parent
* directory. If child with the same name already exists, the function returns
* a failure, the existing file remains untouched and no file system object
* is created.
*
* @param parent File handle of the parent directory node
* @param child New name to be linked
* @param kind Kind of the object to be created: KIND_FILE or
* KIND_DIRECTORY
* @param[out] linkedfd If not NULL, will receive a file handle to the linked
* child
* @return EOK on success or an error code
*/
errno_t vfs_link(int parent, const char *child, vfs_file_kind_t kind, int *linkedfd)
{
int flags = (kind == KIND_DIRECTORY) ? WALK_DIRECTORY : WALK_REGULAR;
int file = -1;
errno_t rc = vfs_walk(parent, child, WALK_MUST_CREATE | flags, &file);
if (rc != EOK)
return rc;
if (linkedfd)
*linkedfd = file;
else
vfs_put(file);
return EOK;
}
/** Link a file or directory
*
* Create a new name and an empty file or an empty directory at given path.
* If a link with the same name already exists, the function returns
* a failure, the existing file remains untouched and no file system object
* is created.
*
* @param path New path to be linked
* @param kind Kind of the object to be created: KIND_FILE or
* KIND_DIRECTORY
* @param[out] linkedfd If not NULL, will receive a file handle to the linked
* child
* @return EOK on success or an error code
*/
errno_t vfs_link_path(const char *path, vfs_file_kind_t kind, int *linkedfd)
{
char *child;
int parent;
errno_t rc = get_parent_and_child(path, &parent, &child);
if (rc != EOK)
return rc;
rc = vfs_link(parent, child, kind, linkedfd);
free(child);
vfs_put(parent);
return rc;
}
/** Lookup a path relative to the local root
*
* @param path Path to be looked up
* @param flags Walk flags
* @param[out] handle Pointer to variable where handle is to be written.
*
* @return EOK on success or an error code.
*/
errno_t vfs_lookup(const char *path, int flags, int *handle)
{
size_t size;
char *p = vfs_absolutize(path, &size);
if (!p)
return ENOMEM;
int root = vfs_root();
if (root < 0) {
free(p);
return ENOENT;
}
// XXX: Workaround for GCC diagnostics.
*handle = -1;
errno_t rc = vfs_walk(root, p, flags, handle);
vfs_put(root);
free(p);
return rc;
}
/** Lookup a path relative to the local root and open the result
*
* This function is a convenience combo for vfs_lookup() and vfs_open().
*
* @param path Path to be looked up
* @param flags Walk flags
* @param mode Mode in which to open file in
* @param[out] handle Pointer to variable where handle is to be written.
*
* @return EOK on success or an error code
*/
errno_t vfs_lookup_open(const char *path, int flags, int mode, int *handle)
{
int file;
errno_t rc = vfs_lookup(path, flags, &file);
if (rc != EOK)
return rc;
rc = vfs_open(file, mode);
if (rc != EOK) {
vfs_put(file);
return rc;
}
*handle = file;
return EOK;
}
/** Mount a file system
*
* @param[in] mp File handle representing the mount-point
* @param[in] fs_name File system name
* @param[in] serv Service representing the mountee
* @param[in] opts Mount options for the endpoint file system
* @param[in] flags Mount flags
* @param[in] instance Instance number of the file system server
* @param[out] mountedfd File handle of the mounted root if not NULL
*
* @return EOK on success or an error code
*/
errno_t vfs_mount(int mp, const char *fs_name, service_id_t serv, const char *opts,
unsigned int flags, unsigned int instance, int *mountedfd)
{
errno_t rc, rc1;
if (!mountedfd)
flags |= VFS_MOUNT_NO_REF;
if (mp < 0)
flags |= VFS_MOUNT_CONNECT_ONLY;
ipc_call_t answer;
async_exch_t *exch = vfs_exchange_begin();
aid_t req = async_send_4(exch, VFS_IN_MOUNT, mp, serv, flags, instance,
&answer);
rc1 = async_data_write_start(exch, (void *) opts, str_size(opts));
if (rc1 == EOK) {
rc1 = async_data_write_start(exch, (void *) fs_name,
str_size(fs_name));
}
vfs_exchange_end(exch);
async_wait_for(req, &rc);
if (mountedfd)
*mountedfd = (int) ipc_get_arg1(&answer);
if (rc != EOK)
return rc;
return rc1;
}
/** Mount a file system
*
* @param[in] mp Path representing the mount-point
* @param[in] fs_name File system name
* @param[in] fqsn Fully qualified service name of the mountee
* @param[in] opts Mount options for the endpoint file system
* @param[in] flags Mount flags
* @param[in] instance Instance number of the file system server
*
* @return EOK on success or an error code
*/
errno_t vfs_mount_path(const char *mp, const char *fs_name, const char *fqsn,
const char *opts, unsigned int flags, unsigned int instance)
{
int null_id = -1;
char null[LOC_NAME_MAXLEN];
if (str_cmp(fqsn, "") == 0) {
/*
* No device specified, create a fresh null/%d device instead.
*/
null_id = loc_null_create();
if (null_id == -1)
return ENOMEM;
snprintf(null, LOC_NAME_MAXLEN, "null/%d", null_id);
fqsn = null;
}
if (flags & IPC_FLAG_BLOCKING)
flags = VFS_MOUNT_BLOCKING;
else
flags = 0;
service_id_t service_id;
errno_t res = loc_service_get_id(fqsn, &service_id, flags);
if (res != EOK) {
if (null_id != -1)
loc_null_destroy(null_id);
return res;
}
size_t mpa_size;
char *mpa = vfs_absolutize(mp, &mpa_size);
if (mpa == NULL) {
if (null_id != -1)
loc_null_destroy(null_id);
return ENOMEM;
}
fibril_mutex_lock(&root_mutex);
errno_t rc;
if (str_cmp(mpa, "/") == 0) {
/* Mounting root. */
if (root_fd >= 0) {
fibril_mutex_unlock(&root_mutex);
if (null_id != -1)
loc_null_destroy(null_id);
return EBUSY;
}
int root;
rc = vfs_mount(-1, fs_name, service_id, opts, flags, instance,
&root);
if (rc == EOK)
root_fd = root;
} else {
if (root_fd < 0) {
fibril_mutex_unlock(&root_mutex);
if (null_id != -1)
loc_null_destroy(null_id);
return EINVAL;
}
int mpfd;
rc = vfs_walk(root_fd, mpa, WALK_DIRECTORY, &mpfd);
if (rc == EOK) {
rc = vfs_mount(mpfd, fs_name, service_id, opts, flags,
instance, NULL);
vfs_put(mpfd);
}
}
fibril_mutex_unlock(&root_mutex);
if ((rc != EOK) && (null_id != -1))
loc_null_destroy(null_id);
return (errno_t) rc;
}
/** Open a file handle for I/O
*
* @param file File handle to enable I/O on
* @param mode Mode in which to open file in
*
* @return EOK on success or an error code
*/
errno_t vfs_open(int file, int mode)
{
async_exch_t *exch = vfs_exchange_begin();
errno_t rc = async_req_2_0(exch, VFS_IN_OPEN, file, mode);
vfs_exchange_end(exch);
return rc;
}
/** Pass a file handle to another VFS client
*
* @param vfs_exch Donor's VFS exchange
* @param file Donor's file handle to pass
* @param exch Exchange to the acceptor
*
* @return EOK on success or an error code
*/
errno_t vfs_pass_handle(async_exch_t *vfs_exch, int file, async_exch_t *exch)
{
return async_state_change_start(exch, VFS_PASS_HANDLE, (sysarg_t) file,
0, vfs_exch);
}
/** Stop working with a file handle
*
* @param file File handle to put
*
* @return EOK on success or an error code
*/
errno_t vfs_put(int file)
{
async_exch_t *exch = vfs_exchange_begin();
errno_t rc = async_req_1_0(exch, VFS_IN_PUT, file);
vfs_exchange_end(exch);
return rc;
}
/** Receive a file handle from another VFS client
*
* @param high If true, the received file handle will be allocated from high
* indices
* @param[out] handle Received handle.
*
* @return EOK on success or an error code
*/
errno_t vfs_receive_handle(bool high, int *handle)
{
ipc_call_t call;
if (!async_state_change_receive(&call)) {
async_answer_0(&call, EINVAL);
return EINVAL;
}
async_exch_t *vfs_exch = vfs_exchange_begin();
async_state_change_finalize(&call, vfs_exch);
sysarg_t ret;
errno_t rc = async_req_1_1(vfs_exch, VFS_IN_WAIT_HANDLE, high, &ret);
async_exchange_end(vfs_exch);
if (rc == EOK) {
*handle = (int) ret;
}
return rc;
}
/** Read data
*
* Read up to @a nbytes bytes from file if available. This function always reads
* all the available bytes up to @a nbytes.
*
* @param file File handle to read from
* @param[inout] pos Position to read from, updated by the actual bytes read
* @param buf Buffer, @a nbytes bytes long
* @param nbytes Number of bytes to read
* @param nread Place to store number of bytes actually read
*
* @return On success, EOK and @a *nread is filled with number
* of bytes actually read.
* @return On failure, an error code
*/
errno_t vfs_read(int file, aoff64_t *pos, void *buf, size_t nbyte, size_t *nread)
{
ssize_t cnt = 0;
size_t nr = 0;
uint8_t *bp = (uint8_t *) buf;
errno_t rc;
do {
bp += cnt;
nr += cnt;
*pos += cnt;
rc = vfs_read_short(file, *pos, bp, nbyte - nr, &cnt);
} while (rc == EOK && cnt > 0 && (nbyte - nr - cnt) > 0);
if (rc != EOK) {
*nread = nr;
return rc;
}
nr += cnt;
*pos += cnt;
*nread = nr;
return EOK;
}
/** Read bytes from a file
*
* Read up to @a nbyte bytes from file. The actual number of bytes read
* may be lower, but greater than zero if there are any bytes available.
* If there are no bytes available for reading, then the function will
* return success with zero bytes read.
*
* @param file File handle to read from
* @param[in] pos Position to read from
* @param buf Buffer to read from
* @param nbyte Maximum number of bytes to read
* @param[out] nread Actual number of bytes read (0 or more)
*
* @return EOK on success or an error code
*/
errno_t vfs_read_short(int file, aoff64_t pos, void *buf, size_t nbyte,
ssize_t *nread)
{
errno_t rc;
ipc_call_t answer;
aid_t req;
if (nbyte > DATA_XFER_LIMIT)
nbyte = DATA_XFER_LIMIT;
async_exch_t *exch = vfs_exchange_begin();
req = async_send_3(exch, VFS_IN_READ, file, LOWER32(pos),
UPPER32(pos), &answer);
rc = async_data_read_start(exch, (void *) buf, nbyte);
vfs_exchange_end(exch);
if (rc == EOK)
async_wait_for(req, &rc);
else
async_forget(req);
if (rc != EOK)
return rc;
*nread = (ssize_t) ipc_get_arg1(&answer);
return EOK;
}
/** Rename a file or directory
*
* There is no file-handle-based variant to disallow attempts to introduce loops
* and breakage in the directory tree when relinking eg. a node under its own
* descendant. The path-based variant is not susceptible because the VFS can
* prevent this lexically by comparing the paths.
*
* @param old Old path
* @param new New path
*
* @return EOK on success or an error code
*/
errno_t vfs_rename_path(const char *old, const char *new)
{
errno_t rc;
errno_t rc_orig;
aid_t req;
size_t olda_size;
char *olda = vfs_absolutize(old, &olda_size);
if (olda == NULL)
return ENOMEM;
size_t newa_size;
char *newa = vfs_absolutize(new, &newa_size);
if (newa == NULL) {
free(olda);
return ENOMEM;
}
async_exch_t *exch = vfs_exchange_begin();
int root = vfs_root();
if (root < 0) {
free(olda);
free(newa);
return ENOENT;
}
req = async_send_1(exch, VFS_IN_RENAME, root, NULL);
rc = async_data_write_start(exch, olda, olda_size);
if (rc != EOK) {
vfs_exchange_end(exch);
free(olda);
free(newa);
vfs_put(root);
async_wait_for(req, &rc_orig);
if (rc_orig != EOK)
rc = rc_orig;
return rc;
}
rc = async_data_write_start(exch, newa, newa_size);
if (rc != EOK) {
vfs_exchange_end(exch);
free(olda);
free(newa);
vfs_put(root);
async_wait_for(req, &rc_orig);
if (rc_orig != EOK)
rc = rc_orig;
return rc;
}
vfs_exchange_end(exch);
free(olda);
free(newa);
vfs_put(root);
async_wait_for(req, &rc);
return rc;
}
/** Resize file to a specified length
*
* Resize file so that its size is exactly @a length.
*
* @param file File handle to resize
* @param length New length
*
* @return EOK on success or an error code
*/
errno_t vfs_resize(int file, aoff64_t length)
{
async_exch_t *exch = vfs_exchange_begin();
errno_t rc = async_req_3_0(exch, VFS_IN_RESIZE, file, LOWER32(length),
UPPER32(length));
vfs_exchange_end(exch);
return rc;
}
/** Return a new file handle representing the local root
*
* @return A clone of the local root file handle or -1
*/
int vfs_root(void)
{
fibril_mutex_lock(&root_mutex);
int fd;
if (root_fd < 0) {
fd = -1;
} else {
errno_t rc = vfs_clone(root_fd, -1, true, &fd);
if (rc != EOK) {
fd = -1;
}
}
fibril_mutex_unlock(&root_mutex);
return fd;
}
/** Set a new local root
*
* Note that it is still possible to have file handles for other roots and pass
* them to the API functions. Functions like vfs_root() and vfs_lookup() will
* however consider the file set by this function to be the root.
*
* @param nroot The new local root file handle
*
* @return Error code
*/
errno_t vfs_root_set(int nroot)
{
int new_root;
errno_t rc = vfs_clone(nroot, -1, true, &new_root);
if (rc != EOK) {
return rc;
}
fibril_mutex_lock(&root_mutex);
if (root_fd >= 0)
vfs_put(root_fd);
root_fd = new_root;
fibril_mutex_unlock(&root_mutex);
return EOK;
}
/** Get file information
*
* @param file File handle to get information about
* @param[out] stat Place to store file information
*
* @return EOK on success or an error code
*/
errno_t vfs_stat(int file, vfs_stat_t *stat)
{
errno_t rc;
aid_t req;
async_exch_t *exch = vfs_exchange_begin();
req = async_send_1(exch, VFS_IN_STAT, file, NULL);
rc = async_data_read_start(exch, (void *) stat, sizeof(vfs_stat_t));
if (rc != EOK) {
vfs_exchange_end(exch);
errno_t rc_orig;
async_wait_for(req, &rc_orig);
if (rc_orig != EOK)
rc = rc_orig;
return rc;
}
vfs_exchange_end(exch);
async_wait_for(req, &rc);
return rc;
}
/** Get file information
*
* @param path File path to get information about
* @param[out] stat Place to store file information
*
* @return EOK on success or an error code
*/
errno_t vfs_stat_path(const char *path, vfs_stat_t *stat)
{
int file;
errno_t rc = vfs_lookup(path, 0, &file);
if (rc != EOK)
return rc;
rc = vfs_stat(file, stat);
vfs_put(file);
return rc;
}
/** Get filesystem statistics
*
* @param file File located on the queried file system
* @param[out] st Buffer for storing information
*
* @return EOK on success or an error code
*/
errno_t vfs_statfs(int file, vfs_statfs_t *st)
{
errno_t rc, ret;
aid_t req;
async_exch_t *exch = vfs_exchange_begin();
req = async_send_1(exch, VFS_IN_STATFS, file, NULL);
rc = async_data_read_start(exch, (void *) st, sizeof(*st));
vfs_exchange_end(exch);
async_wait_for(req, &ret);
rc = (ret != EOK ? ret : rc);
return rc;
}
/** Get filesystem statistics
*
* @param file Path pointing to the queried file system
* @param[out] st Buffer for storing information
*
* @return EOK on success or an error code
*/
errno_t vfs_statfs_path(const char *path, vfs_statfs_t *st)
{
int file;
errno_t rc = vfs_lookup(path, 0, &file);
if (rc != EOK)
return rc;
rc = vfs_statfs(file, st);
vfs_put(file);
return rc;
}
/** Synchronize file
*
* @param file File handle to synchronize
*
* @return EOK on success or an error code
*/
errno_t vfs_sync(int file)
{
async_exch_t *exch = vfs_exchange_begin();
errno_t rc = async_req_1_0(exch, VFS_IN_SYNC, file);
vfs_exchange_end(exch);
return rc;
}
/** Unlink a file or directory
*
* Unlink a name from a parent directory. The caller can supply the file handle
* of the unlinked child in order to detect a possible race with vfs_link() and
* avoid unlinking a wrong file. If the last link for a file or directory is
* removed, the FS implementation will deallocate its resources.
*
* @param parent File handle of the parent directory node
* @param child Old name to be unlinked
* @param expect File handle of the unlinked child
*
* @return EOK on success or an error code
*/
errno_t vfs_unlink(int parent, const char *child, int expect)
{
errno_t rc;
aid_t req;
async_exch_t *exch = vfs_exchange_begin();
req = async_send_2(exch, VFS_IN_UNLINK, parent, expect, NULL);
rc = async_data_write_start(exch, child, str_size(child));
vfs_exchange_end(exch);
errno_t rc_orig;
async_wait_for(req, &rc_orig);
if (rc_orig != EOK)
return (errno_t) rc_orig;
return rc;
}
/** Unlink a file or directory
*
* Unlink a path. If the last link for a file or directory is removed, the FS
* implementation will deallocate its resources.
*
* @param path Old path to be unlinked
*
* @return EOK on success or an error code
*/
errno_t vfs_unlink_path(const char *path)
{
int expect;
errno_t rc = vfs_lookup(path, 0, &expect);
if (rc != EOK)
return rc;
char *child;
int parent;
rc = get_parent_and_child(path, &parent, &child);
if (rc != EOK) {
vfs_put(expect);
return rc;
}
rc = vfs_unlink(parent, child, expect);
free(child);
vfs_put(parent);
vfs_put(expect);
return rc;
}
/** Unmount a file system
*
* @param mp File handle representing the mount-point
*
* @return EOK on success or an error code
*/
errno_t vfs_unmount(int mp)
{
async_exch_t *exch = vfs_exchange_begin();
errno_t rc = async_req_1_0(exch, VFS_IN_UNMOUNT, mp);
vfs_exchange_end(exch);
return rc;
}
/** Unmount a file system
*
* @param mpp Mount-point path
*
* @return EOK on success or an error code
*/
errno_t vfs_unmount_path(const char *mpp)
{
int mp;
errno_t rc = vfs_lookup(mpp, WALK_MOUNT_POINT | WALK_DIRECTORY, &mp);
if (rc != EOK)
return rc;
rc = vfs_unmount(mp);
vfs_put(mp);
return rc;
}
/** Walk a path starting in a parent node
*
* @param parent File handle of the parent node where the walk starts
* @param path Parent-relative path to be walked
* @param flags Flags influencing the walk
* @param[out] handle File handle representing the result on success.
*
* @return Error code.
*/
errno_t vfs_walk(int parent, const char *path, int flags, int *handle)
{
async_exch_t *exch = vfs_exchange_begin();
ipc_call_t answer;
aid_t req = async_send_2(exch, VFS_IN_WALK, parent, flags, &answer);
errno_t rc = async_data_write_start(exch, path, str_size(path));
vfs_exchange_end(exch);
errno_t rc_orig;
async_wait_for(req, &rc_orig);
if (rc_orig != EOK)
return (errno_t) rc_orig;
if (rc != EOK)
return (errno_t) rc;
*handle = (int) ipc_get_arg1(&answer);
return EOK;
}
/** Write data
*
* This function fails if it cannot write exactly @a len bytes to the file.
*
* @param file File handle to write to
* @param[inout] pos Position to write to, updated by the actual bytes
* written
* @param buf Data, @a nbytes bytes long
* @param nbytes Number of bytes to write
* @param nwritten Place to store number of bytes written
*
* @return On success, EOK, @a *nwr is filled with number
* of bytes written
* @return On failure, an error code
*/
errno_t vfs_write(int file, aoff64_t *pos, const void *buf, size_t nbyte,
size_t *nwritten)
{
ssize_t cnt = 0;
ssize_t nwr = 0;
const uint8_t *bp = (uint8_t *) buf;
errno_t rc;
do {
bp += cnt;
nwr += cnt;
*pos += cnt;
rc = vfs_write_short(file, *pos, bp, nbyte - nwr, &cnt);
} while (rc == EOK && ((ssize_t)nbyte - nwr - cnt) > 0);
if (rc != EOK) {
*nwritten = nwr;
return rc;
}
nwr += cnt;
*pos += cnt;
*nwritten = nwr;
return EOK;
}
/** Write bytes to a file
*
* Write up to @a nbyte bytes from file. The actual number of bytes written
* may be lower, but greater than zero.
*
* @param file File handle to write to
* @param[in] pos Position to write to
* @param buf Buffer to write to
* @param nbyte Maximum number of bytes to write
* @param[out] nread Actual number of bytes written (0 or more)
*
* @return EOK on success or an error code
*/
errno_t vfs_write_short(int file, aoff64_t pos, const void *buf, size_t nbyte,
ssize_t *nwritten)
{
errno_t rc;
ipc_call_t answer;
aid_t req;
if (nbyte > DATA_XFER_LIMIT)
nbyte = DATA_XFER_LIMIT;
async_exch_t *exch = vfs_exchange_begin();
req = async_send_3(exch, VFS_IN_WRITE, file, LOWER32(pos),
UPPER32(pos), &answer);
rc = async_data_write_start(exch, (void *) buf, nbyte);
vfs_exchange_end(exch);
if (rc == EOK)
async_wait_for(req, &rc);
else
async_forget(req);
if (rc != EOK)
return rc;
*nwritten = (ssize_t) ipc_get_arg1(&answer);
return EOK;
}
/** @}
*/