HelenOS sources
This source file includes following definitions.
- services_key_hash
- services_hash
- services_key_equal
- services_remove_callback
- locfs_node_get_internal
- locfs_root_get
- locfs_match
- locfs_node_get
- locfs_node_open
- locfs_node_put
- locfs_create_node
- locfs_destroy_node
- locfs_link_node
- locfs_unlink_node
- locfs_has_children
- locfs_index_get
- locfs_size_get
- locfs_lnkcnt_get
- locfs_is_directory
- locfs_is_file
- locfs_service_get
- locfs_init
- locfs_fsprobe
- locfs_mounted
- locfs_unmounted
- locfs_read
- locfs_write
- locfs_truncate
- locfs_close
- locfs_sync
- locfs_destroy
#include <macros.h>
#include <stdbool.h>
#include <errno.h>
#include <stdlib.h>
#include <str.h>
#include <libfs.h>
#include <fibril_synch.h>
#include <adt/hash_table.h>
#include <ipc/loc.h>
#include <assert.h>
#include "locfs.h"
#include "locfs_ops.h"
typedef struct {
loc_object_type_t type;
service_id_t service_id;
} locfs_node_t;
typedef struct {
service_id_t service_id;
async_sess_t *sess;
size_t refcount;
ht_link_t link;
fibril_condvar_t cv;
} service_t;
static hash_table_t services;
static FIBRIL_MUTEX_INITIALIZE(services_mutex);
static size_t services_key_hash(const void *key)
{
const service_id_t *k = key;
return *k;
}
static size_t services_hash(const ht_link_t *item)
{
service_t *dev = hash_table_get_inst(item, service_t, link);
return dev->service_id;
}
static bool services_key_equal(const void *key, const ht_link_t *item)
{
const service_id_t *k = key;
service_t *dev = hash_table_get_inst(item, service_t, link);
return (dev->service_id == *k);
}
static void services_remove_callback(ht_link_t *item)
{
free(hash_table_get_inst(item, service_t, link));
}
static const hash_table_ops_t services_ops = {
.hash = services_hash,
.key_hash = services_key_hash,
.key_equal = services_key_equal,
.equal = NULL,
.remove_callback = services_remove_callback
};
static errno_t locfs_node_get_internal(fs_node_t **rfn, loc_object_type_t type,
service_id_t service_id)
{
locfs_node_t *node = (locfs_node_t *) malloc(sizeof(locfs_node_t));
if (node == NULL) {
*rfn = NULL;
return ENOMEM;
}
*rfn = (fs_node_t *) malloc(sizeof(fs_node_t));
if (*rfn == NULL) {
free(node);
*rfn = NULL;
return ENOMEM;
}
fs_node_initialize(*rfn);
node->type = type;
node->service_id = service_id;
(*rfn)->data = node;
return EOK;
}
static errno_t locfs_root_get(fs_node_t **rfn, service_id_t service_id)
{
return locfs_node_get_internal(rfn, LOC_OBJECT_NONE, 0);
}
static errno_t locfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
{
locfs_node_t *node = (locfs_node_t *) pfn->data;
errno_t ret;
if (node->service_id == 0) {
loc_sdesc_t *nspaces;
size_t count = loc_get_namespaces(&nspaces);
if (count > 0) {
size_t pos;
for (pos = 0; pos < count; pos++) {
if (str_cmp(nspaces[pos].name, "") == 0)
continue;
if (str_cmp(nspaces[pos].name, component) == 0) {
ret = locfs_node_get_internal(rfn, LOC_OBJECT_NAMESPACE, nspaces[pos].id);
free(nspaces);
return ret;
}
}
free(nspaces);
}
service_id_t namespace;
loc_sdesc_t *svcs;
if (loc_namespace_get_id("", &namespace, 0) == EOK) {
count = loc_get_services(namespace, &svcs);
if (count > 0) {
size_t pos;
for (pos = 0; pos < count; pos++) {
if (str_cmp(svcs[pos].name, component) == 0) {
ret = locfs_node_get_internal(rfn, LOC_OBJECT_SERVICE, svcs[pos].id);
free(svcs);
return ret;
}
}
free(svcs);
}
}
*rfn = NULL;
return EOK;
}
if (node->type == LOC_OBJECT_NAMESPACE) {
loc_sdesc_t *svcs;
size_t count = loc_get_services(node->service_id, &svcs);
if (count > 0) {
size_t pos;
for (pos = 0; pos < count; pos++) {
if (str_cmp(svcs[pos].name, component) == 0) {
ret = locfs_node_get_internal(rfn, LOC_OBJECT_SERVICE, svcs[pos].id);
free(svcs);
return ret;
}
}
free(svcs);
}
*rfn = NULL;
return EOK;
}
*rfn = NULL;
return EOK;
}
static errno_t locfs_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
{
return locfs_node_get_internal(rfn, loc_id_probe(index), index);
}
static errno_t locfs_node_open(fs_node_t *fn)
{
locfs_node_t *node = (locfs_node_t *) fn->data;
if (node->service_id == 0) {
return EOK;
}
loc_object_type_t type = loc_id_probe(node->service_id);
if (type == LOC_OBJECT_NAMESPACE) {
return EOK;
}
if (type == LOC_OBJECT_SERVICE) {
fibril_mutex_lock(&services_mutex);
ht_link_t *lnk;
restart:
lnk = hash_table_find(&services, &node->service_id);
if (lnk == NULL) {
service_t *dev = (service_t *) malloc(sizeof(service_t));
if (dev == NULL) {
fibril_mutex_unlock(&services_mutex);
return ENOMEM;
}
dev->service_id = node->service_id;
dev->sess = NULL;
dev->refcount = 1;
fibril_condvar_initialize(&dev->cv);
hash_table_insert(&services, &dev->link);
fibril_mutex_unlock(&services_mutex);
async_sess_t *sess = loc_service_connect(node->service_id,
INTERFACE_FS, 0);
fibril_mutex_lock(&services_mutex);
fibril_condvar_broadcast(&dev->cv);
if (!sess) {
hash_table_remove(&services, &node->service_id);
fibril_mutex_unlock(&services_mutex);
return ENOENT;
}
dev->sess = sess;
} else {
service_t *dev = hash_table_get_inst(lnk, service_t, link);
if (!dev->sess) {
fibril_condvar_wait(&dev->cv, &services_mutex);
goto restart;
}
dev->refcount++;
}
fibril_mutex_unlock(&services_mutex);
return EOK;
}
return ENOENT;
}
static errno_t locfs_node_put(fs_node_t *fn)
{
free(fn->data);
free(fn);
return EOK;
}
static errno_t locfs_create_node(fs_node_t **rfn, service_id_t service_id, int lflag)
{
assert((lflag & L_FILE) ^ (lflag & L_DIRECTORY));
*rfn = NULL;
return ENOTSUP;
}
static errno_t locfs_destroy_node(fs_node_t *fn)
{
return ENOTSUP;
}
static errno_t locfs_link_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
{
return ENOTSUP;
}
static errno_t locfs_unlink_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
{
return ENOTSUP;
}
static errno_t locfs_has_children(bool *has_children, fs_node_t *fn)
{
locfs_node_t *node = (locfs_node_t *) fn->data;
if (node->service_id == 0) {
size_t count = loc_count_namespaces();
if (count > 0) {
*has_children = true;
return EOK;
}
service_id_t namespace;
if (loc_namespace_get_id("", &namespace, 0) == EOK) {
count = loc_count_services(namespace);
if (count > 0) {
*has_children = true;
return EOK;
}
}
*has_children = false;
return EOK;
}
if (node->type == LOC_OBJECT_NAMESPACE) {
size_t count = loc_count_services(node->service_id);
if (count > 0) {
*has_children = true;
return EOK;
}
*has_children = false;
return EOK;
}
*has_children = false;
return EOK;
}
static fs_index_t locfs_index_get(fs_node_t *fn)
{
locfs_node_t *node = (locfs_node_t *) fn->data;
return node->service_id;
}
static aoff64_t locfs_size_get(fs_node_t *fn)
{
return 0;
}
static unsigned int locfs_lnkcnt_get(fs_node_t *fn)
{
locfs_node_t *node = (locfs_node_t *) fn->data;
if (node->service_id == 0)
return 0;
return 1;
}
static bool locfs_is_directory(fs_node_t *fn)
{
locfs_node_t *node = (locfs_node_t *) fn->data;
return ((node->type == LOC_OBJECT_NONE) || (node->type == LOC_OBJECT_NAMESPACE));
}
static bool locfs_is_file(fs_node_t *fn)
{
locfs_node_t *node = (locfs_node_t *) fn->data;
return (node->type == LOC_OBJECT_SERVICE);
}
static service_id_t locfs_service_get(fs_node_t *fn)
{
locfs_node_t *node = (locfs_node_t *) fn->data;
if (node->type == LOC_OBJECT_SERVICE)
return node->service_id;
return 0;
}
libfs_ops_t locfs_libfs_ops = {
.root_get = locfs_root_get,
.match = locfs_match,
.node_get = locfs_node_get,
.node_open = locfs_node_open,
.node_put = locfs_node_put,
.create = locfs_create_node,
.destroy = locfs_destroy_node,
.link = locfs_link_node,
.unlink = locfs_unlink_node,
.has_children = locfs_has_children,
.index_get = locfs_index_get,
.size_get = locfs_size_get,
.lnkcnt_get = locfs_lnkcnt_get,
.is_directory = locfs_is_directory,
.is_file = locfs_is_file,
.service_get = locfs_service_get
};
bool locfs_init(void)
{
if (!hash_table_create(&services, 0, 0, &services_ops))
return false;
return true;
}
static errno_t locfs_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
{
return ENOTSUP;
}
static errno_t locfs_mounted(service_id_t service_id, const char *opts,
fs_index_t *index, aoff64_t *size)
{
*index = 0;
*size = 0;
return EOK;
}
static errno_t locfs_unmounted(service_id_t service_id)
{
return ENOTSUP;
}
static errno_t
locfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
size_t *rbytes)
{
if (index == 0) {
ipc_call_t call;
size_t size;
if (!async_data_read_receive(&call, &size)) {
async_answer_0(&call, EINVAL);
return EINVAL;
}
loc_sdesc_t *desc;
size_t count = loc_get_namespaces(&desc);
size_t i;
for (i = 0; i < count; i++) {
if (str_cmp(desc[i].name, "") == 0) {
if (pos >= i)
pos++;
break;
}
}
if (pos < count) {
async_data_read_finalize(&call, desc[pos].name, str_size(desc[pos].name) + 1);
free(desc);
*rbytes = 1;
return EOK;
}
free(desc);
pos -= count;
service_id_t namespace;
if (loc_namespace_get_id("", &namespace, 0) == EOK) {
count = loc_get_services(namespace, &desc);
if (pos < count) {
async_data_read_finalize(&call, desc[pos].name, str_size(desc[pos].name) + 1);
free(desc);
*rbytes = 1;
return EOK;
}
free(desc);
}
async_answer_0(&call, ENOENT);
return ENOENT;
}
loc_object_type_t type = loc_id_probe(index);
if (type == LOC_OBJECT_NAMESPACE) {
ipc_call_t call;
size_t size;
if (!async_data_read_receive(&call, &size)) {
async_answer_0(&call, EINVAL);
return EINVAL;
}
loc_sdesc_t *desc;
size_t count = loc_get_services(index, &desc);
if (pos < count) {
async_data_read_finalize(&call, desc[pos].name, str_size(desc[pos].name) + 1);
free(desc);
*rbytes = 1;
return EOK;
}
free(desc);
async_answer_0(&call, ENOENT);
return ENOENT;
}
if (type == LOC_OBJECT_SERVICE) {
fibril_mutex_lock(&services_mutex);
service_id_t service_index = index;
ht_link_t *lnk = hash_table_find(&services, &service_index);
if (lnk == NULL) {
fibril_mutex_unlock(&services_mutex);
return ENOENT;
}
service_t *dev = hash_table_get_inst(lnk, service_t, link);
assert(dev->sess);
ipc_call_t call;
if (!async_data_read_receive(&call, NULL)) {
fibril_mutex_unlock(&services_mutex);
async_answer_0(&call, EINVAL);
return EINVAL;
}
async_exch_t *exch = async_exchange_begin(dev->sess);
ipc_call_t answer;
aid_t msg = async_send_4(exch, VFS_OUT_READ, service_id,
index, LOWER32(pos), UPPER32(pos), &answer);
async_forward_0(&call, exch, 0, IPC_FF_ROUTE_FROM_ME);
async_exchange_end(exch);
fibril_mutex_unlock(&services_mutex);
errno_t rc;
async_wait_for(msg, &rc);
if ((errno_t) rc == EHANGUP)
rc = ENOTSUP;
*rbytes = ipc_get_arg1(&answer);
return rc;
}
return ENOENT;
}
static errno_t
locfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
size_t *wbytes, aoff64_t *nsize)
{
if (index == 0)
return ENOTSUP;
loc_object_type_t type = loc_id_probe(index);
if (type == LOC_OBJECT_NAMESPACE) {
return ENOTSUP;
}
if (type == LOC_OBJECT_SERVICE) {
fibril_mutex_lock(&services_mutex);
service_id_t service_index = index;
ht_link_t *lnk = hash_table_find(&services, &service_index);
if (lnk == NULL) {
fibril_mutex_unlock(&services_mutex);
return ENOENT;
}
service_t *dev = hash_table_get_inst(lnk, service_t, link);
assert(dev->sess);
ipc_call_t call;
if (!async_data_write_receive(&call, NULL)) {
fibril_mutex_unlock(&services_mutex);
async_answer_0(&call, EINVAL);
return EINVAL;
}
async_exch_t *exch = async_exchange_begin(dev->sess);
ipc_call_t answer;
aid_t msg = async_send_4(exch, VFS_OUT_WRITE, service_id,
index, LOWER32(pos), UPPER32(pos), &answer);
async_forward_0(&call, exch, 0, IPC_FF_ROUTE_FROM_ME);
async_exchange_end(exch);
fibril_mutex_unlock(&services_mutex);
errno_t rc;
async_wait_for(msg, &rc);
if ((errno_t) rc == EHANGUP)
rc = ENOTSUP;
*wbytes = ipc_get_arg1(&answer);
*nsize = 0;
return rc;
}
return ENOENT;
}
static errno_t
locfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
{
return ENOTSUP;
}
static errno_t locfs_close(service_id_t service_id, fs_index_t index)
{
if (index == 0)
return EOK;
loc_object_type_t type = loc_id_probe(index);
if (type == LOC_OBJECT_NAMESPACE) {
return EOK;
}
if (type == LOC_OBJECT_SERVICE) {
fibril_mutex_lock(&services_mutex);
service_id_t service_index = index;
ht_link_t *lnk = hash_table_find(&services, &service_index);
if (lnk == NULL) {
fibril_mutex_unlock(&services_mutex);
return ENOENT;
}
service_t *dev = hash_table_get_inst(lnk, service_t, link);
assert(dev->sess);
dev->refcount--;
if (dev->refcount == 0) {
async_hangup(dev->sess);
service_id_t service_index = index;
hash_table_remove(&services, &service_index);
}
fibril_mutex_unlock(&services_mutex);
return EOK;
}
return ENOENT;
}
static errno_t locfs_sync(service_id_t service_id, fs_index_t index)
{
if (index == 0)
return EOK;
loc_object_type_t type = loc_id_probe(index);
if (type == LOC_OBJECT_NAMESPACE) {
return EOK;
}
if (type == LOC_OBJECT_SERVICE) {
fibril_mutex_lock(&services_mutex);
service_id_t service_index = index;
ht_link_t *lnk = hash_table_find(&services, &service_index);
if (lnk == NULL) {
fibril_mutex_unlock(&services_mutex);
return ENOENT;
}
service_t *dev = hash_table_get_inst(lnk, service_t, link);
assert(dev->sess);
async_exch_t *exch = async_exchange_begin(dev->sess);
ipc_call_t answer;
aid_t msg = async_send_2(exch, VFS_OUT_SYNC, service_id,
index, &answer);
async_exchange_end(exch);
fibril_mutex_unlock(&services_mutex);
errno_t rc;
async_wait_for(msg, &rc);
return rc;
}
return ENOENT;
}
static errno_t locfs_destroy(service_id_t service_id, fs_index_t index)
{
return ENOTSUP;
}
vfs_out_ops_t locfs_ops = {
.fsprobe = locfs_fsprobe,
.mounted = locfs_mounted,
.unmounted = locfs_unmounted,
.read = locfs_read,
.write = locfs_write,
.truncate = locfs_truncate,
.close = locfs_close,
.destroy = locfs_destroy,
.sync = locfs_sync,
};
HelenOS homepage, sources at GitHub