HelenOS sources
This source file includes following definitions.
- vfs_out_fsprobe
- vfs_out_mounted
- vfs_out_unmounted
- vfs_out_link
- vfs_out_lookup
- vfs_out_read
- vfs_out_write
- vfs_out_truncate
- vfs_out_close
- vfs_out_destroy
- vfs_out_open_node
- vfs_out_stat
- vfs_out_sync
- vfs_out_statfs
- vfs_out_is_empty
- vfs_connection
- fs_register
- fs_node_initialize
- plb_get_char
- plb_get_component
- receive_fname
- libfs_link
- libfs_lookup
- libfs_stat
- libfs_statfs
- libfs_open_node
- fs_instance_create
- fs_instance_get
- fs_instance_destroy
#include "libfs.h"
#include <macros.h>
#include <errno.h>
#include <async.h>
#include <as.h>
#include <assert.h>
#include <dirent.h>
#include <mem.h>
#include <str.h>
#include <stdlib.h>
#include <fibril_synch.h>
#include <ipc/vfs.h>
#include <vfs/vfs.h>
#define on_error(rc, action) \
do { \
if ((rc) != EOK) \
action; \
} while (0)
#define combine_rc(rc1, rc2) \
((rc1) == EOK ? (rc2) : (rc1))
#define answer_and_return(call, rc) \
do { \
async_answer_0((call), (rc)); \
return; \
} while (0)
static fs_reg_t reg;
static vfs_out_ops_t *vfs_out_ops = NULL;
static libfs_ops_t *libfs_ops = NULL;
static char fs_name[FS_NAME_MAXLEN + 1];
static void libfs_link(libfs_ops_t *, fs_handle_t, ipc_call_t *);
static void libfs_lookup(libfs_ops_t *, fs_handle_t, ipc_call_t *);
static void libfs_stat(libfs_ops_t *, fs_handle_t, ipc_call_t *);
static void libfs_open_node(libfs_ops_t *, fs_handle_t, ipc_call_t *);
static void libfs_statfs(libfs_ops_t *, fs_handle_t, ipc_call_t *);
static void vfs_out_fsprobe(ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
errno_t rc;
vfs_fs_probe_info_t info;
ipc_call_t call;
size_t size;
if ((!async_data_read_receive(&call, &size)) ||
(size != sizeof(info))) {
async_answer_0(&call, EIO);
async_answer_0(req, EIO);
return;
}
memset(&info, 0, sizeof(info));
rc = vfs_out_ops->fsprobe(service_id, &info);
if (rc != EOK) {
async_answer_0(&call, EIO);
async_answer_0(req, rc);
return;
}
async_data_read_finalize(&call, &info, sizeof(info));
async_answer_0(req, EOK);
}
static void vfs_out_mounted(ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
char *opts;
errno_t rc;
rc = async_data_write_accept((void **) &opts, true, 0, 0, 0, NULL);
if (rc != EOK) {
async_answer_0(req, rc);
return;
}
fs_index_t index;
aoff64_t size;
rc = vfs_out_ops->mounted(service_id, opts, &index, &size);
if (rc == EOK) {
async_answer_3(req, EOK, index, LOWER32(size),
UPPER32(size));
} else
async_answer_0(req, rc);
free(opts);
}
static void vfs_out_unmounted(ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
errno_t rc;
rc = vfs_out_ops->unmounted(service_id);
async_answer_0(req, rc);
}
static void vfs_out_link(ipc_call_t *req)
{
libfs_link(libfs_ops, reg.fs_handle, req);
}
static void vfs_out_lookup(ipc_call_t *req)
{
libfs_lookup(libfs_ops, reg.fs_handle, req);
}
static void vfs_out_read(ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
fs_index_t index = (fs_index_t) ipc_get_arg2(req);
aoff64_t pos = (aoff64_t) MERGE_LOUP32(ipc_get_arg3(req),
ipc_get_arg4(req));
size_t rbytes;
errno_t rc;
rc = vfs_out_ops->read(service_id, index, pos, &rbytes);
if (rc == EOK)
async_answer_1(req, EOK, rbytes);
else
async_answer_0(req, rc);
}
static void vfs_out_write(ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
fs_index_t index = (fs_index_t) ipc_get_arg2(req);
aoff64_t pos = (aoff64_t) MERGE_LOUP32(ipc_get_arg3(req),
ipc_get_arg4(req));
size_t wbytes;
aoff64_t nsize;
errno_t rc;
rc = vfs_out_ops->write(service_id, index, pos, &wbytes, &nsize);
if (rc == EOK) {
async_answer_3(req, EOK, wbytes, LOWER32(nsize),
UPPER32(nsize));
} else
async_answer_0(req, rc);
}
static void vfs_out_truncate(ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
fs_index_t index = (fs_index_t) ipc_get_arg2(req);
aoff64_t size = (aoff64_t) MERGE_LOUP32(ipc_get_arg3(req),
ipc_get_arg4(req));
errno_t rc;
rc = vfs_out_ops->truncate(service_id, index, size);
async_answer_0(req, rc);
}
static void vfs_out_close(ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
fs_index_t index = (fs_index_t) ipc_get_arg2(req);
errno_t rc;
rc = vfs_out_ops->close(service_id, index);
async_answer_0(req, rc);
}
static void vfs_out_destroy(ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
fs_index_t index = (fs_index_t) ipc_get_arg2(req);
errno_t rc;
fs_node_t *node = NULL;
rc = libfs_ops->node_get(&node, service_id, index);
if (rc == EOK && node != NULL) {
bool destroy = (libfs_ops->lnkcnt_get(node) == 0);
libfs_ops->node_put(node);
if (destroy)
rc = vfs_out_ops->destroy(service_id, index);
}
async_answer_0(req, rc);
}
static void vfs_out_open_node(ipc_call_t *req)
{
libfs_open_node(libfs_ops, reg.fs_handle, req);
}
static void vfs_out_stat(ipc_call_t *req)
{
libfs_stat(libfs_ops, reg.fs_handle, req);
}
static void vfs_out_sync(ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
fs_index_t index = (fs_index_t) ipc_get_arg2(req);
errno_t rc;
rc = vfs_out_ops->sync(service_id, index);
async_answer_0(req, rc);
}
static void vfs_out_statfs(ipc_call_t *req)
{
libfs_statfs(libfs_ops, reg.fs_handle, req);
}
static void vfs_out_is_empty(ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
fs_index_t index = (fs_index_t) ipc_get_arg2(req);
errno_t rc;
fs_node_t *node = NULL;
rc = libfs_ops->node_get(&node, service_id, index);
if (rc != EOK)
async_answer_0(req, rc);
if (node == NULL)
async_answer_0(req, EINVAL);
bool children = false;
rc = libfs_ops->has_children(&children, node);
libfs_ops->node_put(node);
if (rc != EOK)
async_answer_0(req, rc);
async_answer_0(req, children ? ENOTEMPTY : EOK);
}
static void vfs_connection(ipc_call_t *icall, void *arg)
{
if (icall->cap_handle) {
async_accept_0(icall);
}
while (true) {
ipc_call_t call;
async_get_call(&call);
if (!ipc_get_imethod(&call)) {
async_answer_0(&call, EOK);
return;
}
switch (ipc_get_imethod(&call)) {
case VFS_OUT_FSPROBE:
vfs_out_fsprobe(&call);
break;
case VFS_OUT_MOUNTED:
vfs_out_mounted(&call);
break;
case VFS_OUT_UNMOUNTED:
vfs_out_unmounted(&call);
break;
case VFS_OUT_LINK:
vfs_out_link(&call);
break;
case VFS_OUT_LOOKUP:
vfs_out_lookup(&call);
break;
case VFS_OUT_READ:
vfs_out_read(&call);
break;
case VFS_OUT_WRITE:
vfs_out_write(&call);
break;
case VFS_OUT_TRUNCATE:
vfs_out_truncate(&call);
break;
case VFS_OUT_CLOSE:
vfs_out_close(&call);
break;
case VFS_OUT_DESTROY:
vfs_out_destroy(&call);
break;
case VFS_OUT_OPEN_NODE:
vfs_out_open_node(&call);
break;
case VFS_OUT_STAT:
vfs_out_stat(&call);
break;
case VFS_OUT_SYNC:
vfs_out_sync(&call);
break;
case VFS_OUT_STATFS:
vfs_out_statfs(&call);
break;
case VFS_OUT_IS_EMPTY:
vfs_out_is_empty(&call);
break;
default:
async_answer_0(&call, ENOTSUP);
break;
}
}
}
errno_t fs_register(async_sess_t *sess, vfs_info_t *info, vfs_out_ops_t *vops,
libfs_ops_t *lops)
{
async_exch_t *exch = async_exchange_begin(sess);
ipc_call_t answer;
aid_t req = async_send_0(exch, VFS_IN_REGISTER, &answer);
errno_t rc = async_data_write_start(exch, info, sizeof(*info));
if (rc != EOK) {
async_exchange_end(exch);
async_forget(req);
return rc;
}
vfs_out_ops = vops;
libfs_ops = lops;
str_cpy(fs_name, sizeof(fs_name), info->name);
port_id_t port;
rc = async_create_callback_port(exch, INTERFACE_VFS_DRIVER_CB, 0, 0,
vfs_connection, NULL, &port);
rc = async_share_in_start_0_0(exch, PLB_SIZE, (void *) ®.plb_ro);
if (reg.plb_ro == AS_MAP_FAILED) {
async_exchange_end(exch);
async_forget(req);
return ENOMEM;
}
async_exchange_end(exch);
if (rc) {
async_forget(req);
return rc;
}
async_wait_for(req, NULL);
reg.fs_handle = (int) ipc_get_arg1(&answer);
return ipc_get_retval(&answer);
}
void fs_node_initialize(fs_node_t *fn)
{
memset(fn, 0, sizeof(fs_node_t));
}
static char plb_get_char(unsigned pos)
{
return reg.plb_ro[pos % PLB_SIZE];
}
static errno_t plb_get_component(char *dest, unsigned *sz, unsigned *ppos,
unsigned last)
{
unsigned pos = *ppos;
unsigned size = 0;
if (pos == last) {
*sz = 0;
return ERANGE;
}
char c = plb_get_char(pos);
if (c == '/')
pos++;
for (int i = 0; i <= NAME_MAX; i++) {
c = plb_get_char(pos);
if (pos == last || c == '/') {
dest[i] = 0;
*ppos = pos;
*sz = size;
return EOK;
}
dest[i] = c;
pos++;
size++;
}
return ENAMETOOLONG;
}
static errno_t receive_fname(char *buffer)
{
ipc_call_t call;
size_t size;
if (!async_data_write_receive(&call, &size))
return ENOENT;
if (size > NAME_MAX + 1) {
async_answer_0(&call, ERANGE);
return ERANGE;
}
return async_data_write_finalize(&call, buffer, size);
}
void libfs_link(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_call_t *req)
{
service_id_t parent_sid = ipc_get_arg1(req);
fs_index_t parent_index = ipc_get_arg2(req);
fs_index_t child_index = ipc_get_arg3(req);
char component[NAME_MAX + 1];
errno_t rc = receive_fname(component);
if (rc != EOK) {
async_answer_0(req, rc);
return;
}
fs_node_t *parent = NULL;
rc = ops->node_get(&parent, parent_sid, parent_index);
if (parent == NULL) {
async_answer_0(req, rc == EOK ? EBADF : rc);
return;
}
fs_node_t *child = NULL;
rc = ops->node_get(&child, parent_sid, child_index);
if (child == NULL) {
async_answer_0(req, rc == EOK ? EBADF : rc);
ops->node_put(parent);
return;
}
rc = ops->link(parent, child, component);
ops->node_put(parent);
ops->node_put(child);
async_answer_0(req, rc);
}
void libfs_lookup(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_call_t *req)
{
unsigned first = ipc_get_arg1(req);
unsigned len = ipc_get_arg2(req);
service_id_t service_id = ipc_get_arg3(req);
fs_index_t index = ipc_get_arg4(req);
int lflag = ipc_get_arg5(req);
unsigned next = first;
unsigned last = first + len;
char component[NAME_MAX + 1];
errno_t rc;
fs_node_t *par = NULL;
fs_node_t *cur = NULL;
fs_node_t *tmp = NULL;
unsigned clen = 0;
rc = ops->node_get(&cur, service_id, index);
if (rc != EOK) {
async_answer_0(req, rc);
goto out;
}
assert(cur != NULL);
unsigned last_next = 0;
while (next != last) {
if (cur == NULL) {
assert(par != NULL);
goto out1;
}
if (!ops->is_directory(cur)) {
async_answer_0(req, ENOTDIR);
goto out;
}
last_next = next;
rc = plb_get_component(component, &clen, &next, last);
assert(rc != ERANGE);
if (rc != EOK) {
async_answer_0(req, rc);
goto out;
}
if (clen == 0) {
break;
}
assert(component[clen] == 0);
rc = ops->match(&tmp, cur, component);
if (rc != EOK) {
async_answer_0(req, rc);
goto out;
}
if (par) {
rc = ops->node_put(par);
if (rc != EOK) {
async_answer_0(req, rc);
goto out;
}
}
par = cur;
cur = tmp;
tmp = NULL;
}
assert(par == NULL || ops->is_directory(par));
assert(par != NULL || cur != NULL);
if (cur && (lflag & L_FILE) && (ops->is_directory(cur))) {
async_answer_0(req, EISDIR);
goto out;
}
if (cur && (lflag & L_DIRECTORY) && (ops->is_file(cur))) {
async_answer_0(req, ENOTDIR);
goto out;
}
if (lflag & L_UNLINK) {
if (!cur) {
async_answer_0(req, ENOENT);
goto out;
}
if (!par) {
async_answer_0(req, EINVAL);
goto out;
}
rc = ops->unlink(par, cur, component);
if (rc == EOK) {
aoff64_t size = ops->size_get(cur);
async_answer_5(req, EOK, fs_handle,
ops->index_get(cur),
(ops->is_directory(cur) << 16) | last,
LOWER32(size), UPPER32(size));
} else {
async_answer_0(req, rc);
}
goto out;
}
if (lflag & L_CREATE) {
if (cur && (lflag & L_EXCLUSIVE)) {
async_answer_0(req, EEXIST);
goto out;
}
if (!cur) {
rc = ops->create(&cur, service_id,
lflag & (L_FILE | L_DIRECTORY));
if (rc != EOK) {
async_answer_0(req, rc);
goto out;
}
if (!cur) {
async_answer_0(req, ENOSPC);
goto out;
}
rc = ops->link(par, cur, component);
if (rc != EOK) {
(void) ops->destroy(cur);
cur = NULL;
async_answer_0(req, rc);
goto out;
}
}
}
out1:
if (!cur) {
async_answer_5(req, EOK, fs_handle, ops->index_get(par),
(ops->is_directory(par) << 16) | last_next,
LOWER32(ops->size_get(par)), UPPER32(ops->size_get(par)));
goto out;
}
async_answer_5(req, EOK, fs_handle, ops->index_get(cur),
(ops->is_directory(cur) << 16) | last, LOWER32(ops->size_get(cur)),
UPPER32(ops->size_get(cur)));
out:
if (par)
(void) ops->node_put(par);
if (cur)
(void) ops->node_put(cur);
if (tmp)
(void) ops->node_put(tmp);
}
void libfs_stat(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
fs_index_t index = (fs_index_t) ipc_get_arg2(req);
fs_node_t *fn;
errno_t rc = ops->node_get(&fn, service_id, index);
on_error(rc, answer_and_return(req, rc));
ipc_call_t call;
size_t size;
if ((!async_data_read_receive(&call, &size)) ||
(size != sizeof(vfs_stat_t))) {
ops->node_put(fn);
async_answer_0(&call, EINVAL);
async_answer_0(req, EINVAL);
return;
}
vfs_stat_t stat;
memset(&stat, 0, sizeof(vfs_stat_t));
stat.fs_handle = fs_handle;
stat.service_id = service_id;
stat.index = index;
stat.lnkcnt = ops->lnkcnt_get(fn);
stat.is_file = ops->is_file(fn);
stat.is_directory = ops->is_directory(fn);
stat.size = ops->size_get(fn);
stat.service = ops->service_get(fn);
ops->node_put(fn);
async_data_read_finalize(&call, &stat, sizeof(vfs_stat_t));
async_answer_0(req, EOK);
}
void libfs_statfs(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_call_t *req)
{
service_id_t service_id = (service_id_t) ipc_get_arg1(req);
fs_index_t index = (fs_index_t) ipc_get_arg2(req);
fs_node_t *fn;
errno_t rc = ops->node_get(&fn, service_id, index);
on_error(rc, answer_and_return(req, rc));
ipc_call_t call;
size_t size;
if ((!async_data_read_receive(&call, &size)) ||
(size != sizeof(vfs_statfs_t))) {
goto error;
}
vfs_statfs_t st;
memset(&st, 0, sizeof(vfs_statfs_t));
str_cpy(st.fs_name, sizeof(st.fs_name), fs_name);
if (ops->size_block != NULL) {
rc = ops->size_block(service_id, &st.f_bsize);
if (rc != EOK)
goto error;
}
if (ops->total_block_count != NULL) {
rc = ops->total_block_count(service_id, &st.f_blocks);
if (rc != EOK)
goto error;
}
if (ops->free_block_count != NULL) {
rc = ops->free_block_count(service_id, &st.f_bfree);
if (rc != EOK)
goto error;
}
ops->node_put(fn);
async_data_read_finalize(&call, &st, sizeof(vfs_statfs_t));
async_answer_0(req, EOK);
return;
error:
ops->node_put(fn);
async_answer_0(&call, EINVAL);
async_answer_0(req, EINVAL);
}
void libfs_open_node(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_call_t *req)
{
service_id_t service_id = ipc_get_arg1(req);
fs_index_t index = ipc_get_arg2(req);
fs_node_t *fn;
errno_t rc = ops->node_get(&fn, service_id, index);
on_error(rc, answer_and_return(req, rc));
if (fn == NULL) {
async_answer_0(req, ENOENT);
return;
}
rc = ops->node_open(fn);
aoff64_t size = ops->size_get(fn);
async_answer_4(req, rc, LOWER32(size), UPPER32(size),
ops->lnkcnt_get(fn),
(ops->is_file(fn) ? L_FILE : 0) |
(ops->is_directory(fn) ? L_DIRECTORY : 0));
(void) ops->node_put(fn);
}
static FIBRIL_MUTEX_INITIALIZE(instances_mutex);
static LIST_INITIALIZE(instances_list);
typedef struct {
service_id_t service_id;
link_t link;
void *data;
} fs_instance_t;
errno_t fs_instance_create(service_id_t service_id, void *data)
{
fs_instance_t *inst = malloc(sizeof(fs_instance_t));
if (!inst)
return ENOMEM;
link_initialize(&inst->link);
inst->service_id = service_id;
inst->data = data;
fibril_mutex_lock(&instances_mutex);
list_foreach(instances_list, link, fs_instance_t, cur) {
if (cur->service_id == service_id) {
fibril_mutex_unlock(&instances_mutex);
free(inst);
return EEXIST;
}
if (cur->service_id < service_id) {
list_insert_before(&inst->link, &cur->link);
fibril_mutex_unlock(&instances_mutex);
return EOK;
}
}
list_append(&inst->link, &instances_list);
fibril_mutex_unlock(&instances_mutex);
return EOK;
}
errno_t fs_instance_get(service_id_t service_id, void **idp)
{
fibril_mutex_lock(&instances_mutex);
list_foreach(instances_list, link, fs_instance_t, inst) {
if (inst->service_id == service_id) {
*idp = inst->data;
fibril_mutex_unlock(&instances_mutex);
return EOK;
}
}
fibril_mutex_unlock(&instances_mutex);
return ENOENT;
}
errno_t fs_instance_destroy(service_id_t service_id)
{
fibril_mutex_lock(&instances_mutex);
list_foreach(instances_list, link, fs_instance_t, inst) {
if (inst->service_id == service_id) {
list_remove(&inst->link);
fibril_mutex_unlock(&instances_mutex);
free(inst);
return EOK;
}
}
fibril_mutex_unlock(&instances_mutex);
return ENOENT;
}
HelenOS homepage, sources at GitHub