Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 3385 → Rev 3386

/branches/network/uspace/srv/fs/tmpfs/tmpfs_ops.c
0,0 → 1,625
/*
* 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 fs
* @{
*/
 
/**
* @file tmpfs_ops.c
* @brief Implementation of VFS operations for the TMPFS file system
* server.
*/
 
#include "tmpfs.h"
#include "../../vfs/vfs.h"
#include <ipc/ipc.h>
#include <async.h>
#include <errno.h>
#include <atomic.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include <libadt/hash_table.h>
#include <as.h>
#include <libfs.h>
 
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
 
#define DENTRIES_BUCKETS 256
 
#define NAMES_BUCKETS 4
 
/*
* For now, we don't distinguish between different dev_handles/instances. All
* requests resolve to the only instance, rooted in the following variable.
*/
static tmpfs_dentry_t *root;
 
/*
* Implementation of the libfs interface.
*/
 
/* Forward declarations of static functions. */
static void *tmpfs_match(void *, const char *);
static void *tmpfs_node_get(dev_handle_t, fs_index_t);
static void tmpfs_node_put(void *);
static void *tmpfs_create_node(int);
static bool tmpfs_link_node(void *, void *, const char *);
static int tmpfs_unlink_node(void *, void *);
static int tmpfs_destroy_node(void *);
 
/* Implementation of helper functions. */
static fs_index_t tmpfs_index_get(void *nodep)
{
return ((tmpfs_dentry_t *) nodep)->index;
}
 
static size_t tmpfs_size_get(void *nodep)
{
return ((tmpfs_dentry_t *) nodep)->size;
}
 
static unsigned tmpfs_lnkcnt_get(void *nodep)
{
return ((tmpfs_dentry_t *) nodep)->lnkcnt;
}
 
static bool tmpfs_has_children(void *nodep)
{
return ((tmpfs_dentry_t *) nodep)->child != NULL;
}
 
static void *tmpfs_root_get(dev_handle_t dev_handle)
{
return root;
}
 
static char tmpfs_plb_get_char(unsigned pos)
{
return tmpfs_reg.plb_ro[pos % PLB_SIZE];
}
 
static bool tmpfs_is_directory(void *nodep)
{
return ((tmpfs_dentry_t *) nodep)->type == TMPFS_DIRECTORY;
}
 
static bool tmpfs_is_file(void *nodep)
{
return ((tmpfs_dentry_t *) nodep)->type == TMPFS_FILE;
}
 
/** libfs operations */
libfs_ops_t tmpfs_libfs_ops = {
.match = tmpfs_match,
.node_get = tmpfs_node_get,
.node_put = tmpfs_node_put,
.create = tmpfs_create_node,
.destroy = tmpfs_destroy_node,
.link = tmpfs_link_node,
.unlink = tmpfs_unlink_node,
.index_get = tmpfs_index_get,
.size_get = tmpfs_size_get,
.lnkcnt_get = tmpfs_lnkcnt_get,
.has_children = tmpfs_has_children,
.root_get = tmpfs_root_get,
.plb_get_char = tmpfs_plb_get_char,
.is_directory = tmpfs_is_directory,
.is_file = tmpfs_is_file
};
 
/** Hash table of all directory entries. */
hash_table_t dentries;
 
/* Implementation of hash table interface for the dentries hash table. */
static hash_index_t dentries_hash(unsigned long *key)
{
return *key % DENTRIES_BUCKETS;
}
 
static int dentries_compare(unsigned long *key, hash_count_t keys,
link_t *item)
{
tmpfs_dentry_t *dentry = hash_table_get_instance(item, tmpfs_dentry_t,
dh_link);
return dentry->index == *key;
}
 
static void dentries_remove_callback(link_t *item)
{
}
 
/** TMPFS dentries hash table operations. */
hash_table_operations_t dentries_ops = {
.hash = dentries_hash,
.compare = dentries_compare,
.remove_callback = dentries_remove_callback
};
 
fs_index_t tmpfs_next_index = 1;
 
typedef struct {
char *name;
tmpfs_dentry_t *parent;
link_t link;
} tmpfs_name_t;
 
/* Implementation of hash table interface for the names hash table. */
static hash_index_t names_hash(unsigned long *key)
{
tmpfs_dentry_t *dentry = (tmpfs_dentry_t *) *key;
return dentry->index % NAMES_BUCKETS;
}
 
static int names_compare(unsigned long *key, hash_count_t keys, link_t *item)
{
tmpfs_dentry_t *dentry = (tmpfs_dentry_t *) *key;
tmpfs_name_t *namep = hash_table_get_instance(item, tmpfs_name_t,
link);
return dentry == namep->parent;
}
 
static void names_remove_callback(link_t *item)
{
tmpfs_name_t *namep = hash_table_get_instance(item, tmpfs_name_t,
link);
free(namep->name);
free(namep);
}
 
/** TMPFS node names hash table operations. */
static hash_table_operations_t names_ops = {
.hash = names_hash,
.compare = names_compare,
.remove_callback = names_remove_callback
};
 
static void tmpfs_name_initialize(tmpfs_name_t *namep)
{
namep->name = NULL;
namep->parent = NULL;
link_initialize(&namep->link);
}
 
static bool tmpfs_dentry_initialize(tmpfs_dentry_t *dentry)
{
dentry->index = 0;
dentry->sibling = NULL;
dentry->child = NULL;
dentry->type = TMPFS_NONE;
dentry->lnkcnt = 0;
dentry->size = 0;
dentry->data = NULL;
link_initialize(&dentry->dh_link);
return (bool)hash_table_create(&dentry->names, NAMES_BUCKETS, 1,
&names_ops);
}
 
static bool tmpfs_init(void)
{
if (!hash_table_create(&dentries, DENTRIES_BUCKETS, 1, &dentries_ops))
return false;
root = (tmpfs_dentry_t *) tmpfs_create_node(L_DIRECTORY);
if (!root) {
hash_table_destroy(&dentries);
return false;
}
root->lnkcnt = 0; /* FS root is not linked */
return true;
}
 
/** Compare one component of path to a directory entry.
*
* @param parentp Pointer to node from which we descended.
* @param childp Pointer to node to compare the path component with.
* @param component Array of characters holding component name.
*
* @return True on match, false otherwise.
*/
static bool
tmpfs_match_one(tmpfs_dentry_t *parentp, tmpfs_dentry_t *childp,
const char *component)
{
unsigned long key = (unsigned long) parentp;
link_t *hlp = hash_table_find(&childp->names, &key);
assert(hlp);
tmpfs_name_t *namep = hash_table_get_instance(hlp, tmpfs_name_t, link);
return !strcmp(namep->name, component);
}
 
void *tmpfs_match(void *prnt, const char *component)
{
tmpfs_dentry_t *parentp = (tmpfs_dentry_t *) prnt;
tmpfs_dentry_t *childp = parentp->child;
 
while (childp && !tmpfs_match_one(parentp, childp, component))
childp = childp->sibling;
 
return (void *) childp;
}
 
void *
tmpfs_node_get(dev_handle_t dev_handle, fs_index_t index)
{
unsigned long key = index;
link_t *lnk = hash_table_find(&dentries, &key);
if (!lnk)
return NULL;
return hash_table_get_instance(lnk, tmpfs_dentry_t, dh_link);
}
 
void tmpfs_node_put(void *node)
{
/* nothing to do */
}
 
void *tmpfs_create_node(int lflag)
{
assert((lflag & L_FILE) ^ (lflag & L_DIRECTORY));
 
tmpfs_dentry_t *node = malloc(sizeof(tmpfs_dentry_t));
if (!node)
return NULL;
 
if (!tmpfs_dentry_initialize(node)) {
free(node);
return NULL;
}
node->index = tmpfs_next_index++;
if (lflag & L_DIRECTORY)
node->type = TMPFS_DIRECTORY;
else
node->type = TMPFS_FILE;
 
/* Insert the new node into the dentry hash table. */
unsigned long key = node->index;
hash_table_insert(&dentries, &key, &node->dh_link);
return (void *) node;
}
 
bool tmpfs_link_node(void *prnt, void *chld, const char *nm)
{
tmpfs_dentry_t *parentp = (tmpfs_dentry_t *) prnt;
tmpfs_dentry_t *childp = (tmpfs_dentry_t *) chld;
 
assert(parentp->type == TMPFS_DIRECTORY);
 
tmpfs_name_t *namep = malloc(sizeof(tmpfs_name_t));
if (!namep)
return false;
tmpfs_name_initialize(namep);
size_t len = strlen(nm);
namep->name = malloc(len + 1);
if (!namep->name) {
free(namep);
return false;
}
strcpy(namep->name, nm);
namep->parent = parentp;
childp->lnkcnt++;
 
unsigned long key = (unsigned long) parentp;
hash_table_insert(&childp->names, &key, &namep->link);
 
/* Insert the new node into the namespace. */
if (parentp->child) {
tmpfs_dentry_t *tmp = parentp->child;
while (tmp->sibling)
tmp = tmp->sibling;
tmp->sibling = childp;
} else {
parentp->child = childp;
}
 
return true;
}
 
int tmpfs_unlink_node(void *prnt, void *chld)
{
tmpfs_dentry_t *parentp = (tmpfs_dentry_t *)prnt;
tmpfs_dentry_t *childp = (tmpfs_dentry_t *)chld;
 
if (!parentp)
return EBUSY;
 
if (childp->child)
return ENOTEMPTY;
 
if (parentp->child == childp) {
parentp->child = childp->sibling;
} else {
/* TODO: consider doubly linked list for organizing siblings. */
tmpfs_dentry_t *tmp = parentp->child;
while (tmp->sibling != childp)
tmp = tmp->sibling;
tmp->sibling = childp->sibling;
}
childp->sibling = NULL;
 
unsigned long key = (unsigned long) parentp;
hash_table_remove(&childp->names, &key, 1);
 
childp->lnkcnt--;
 
return EOK;
}
 
int tmpfs_destroy_node(void *nodep)
{
tmpfs_dentry_t *dentry = (tmpfs_dentry_t *) nodep;
assert(!dentry->lnkcnt);
assert(!dentry->child);
assert(!dentry->sibling);
 
unsigned long key = dentry->index;
hash_table_remove(&dentries, &key, 1);
 
hash_table_destroy(&dentry->names);
 
if (dentry->type == TMPFS_FILE)
free(dentry->data);
free(dentry);
return EOK;
}
 
void tmpfs_mounted(ipc_callid_t rid, ipc_call_t *request)
{
dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
 
/* Initialize TMPFS. */
if (!root && !tmpfs_init()) {
ipc_answer_0(rid, ENOMEM);
return;
}
 
if (dev_handle >= 0) {
if (tmpfs_restore(dev_handle))
ipc_answer_3(rid, EOK, root->index, root->size,
root->lnkcnt);
else
ipc_answer_0(rid, ELIMIT);
} else {
ipc_answer_3(rid, EOK, root->index, root->size, root->lnkcnt);
}
}
 
void tmpfs_mount(ipc_callid_t rid, ipc_call_t *request)
{
dev_handle_t mp_dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
fs_index_t mp_index = (fs_index_t) IPC_GET_ARG2(*request);
fs_handle_t mr_fs_handle = (fs_handle_t) IPC_GET_ARG3(*request);
dev_handle_t mr_dev_handle = (dev_handle_t) IPC_GET_ARG4(*request);
ipc_answer_0(rid, ENOTSUP);
}
 
void tmpfs_lookup(ipc_callid_t rid, ipc_call_t *request)
{
libfs_lookup(&tmpfs_libfs_ops, tmpfs_reg.fs_handle, rid, request);
}
 
void tmpfs_read(ipc_callid_t rid, ipc_call_t *request)
{
dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
off_t pos = (off_t)IPC_GET_ARG3(*request);
 
/*
* Lookup the respective dentry.
*/
link_t *hlp;
unsigned long key = index;
hlp = hash_table_find(&dentries, &key);
if (!hlp) {
ipc_answer_0(rid, ENOENT);
return;
}
tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
dh_link);
 
/*
* Receive the read request.
*/
ipc_callid_t callid;
size_t len;
if (!ipc_data_read_receive(&callid, &len)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
 
size_t bytes;
if (dentry->type == TMPFS_FILE) {
bytes = max(0, min(dentry->size - pos, len));
(void) ipc_data_read_finalize(callid, dentry->data + pos,
bytes);
} else {
int i;
tmpfs_dentry_t *cur;
assert(dentry->type == TMPFS_DIRECTORY);
/*
* Yes, we really use O(n) algorithm here.
* If it bothers someone, it could be fixed by introducing a
* hash table.
*/
for (i = 0, cur = dentry->child; i < pos && cur; i++,
cur = cur->sibling)
;
 
if (!cur) {
ipc_answer_0(callid, ENOENT);
ipc_answer_1(rid, ENOENT, 0);
return;
}
 
unsigned long key = (unsigned long) dentry;
link_t *hlp = hash_table_find(&cur->names, &key);
assert(hlp);
tmpfs_name_t *namep = hash_table_get_instance(hlp, tmpfs_name_t,
link);
 
(void) ipc_data_read_finalize(callid, namep->name,
strlen(namep->name) + 1);
bytes = 1;
}
 
/*
* Answer the VFS_READ call.
*/
ipc_answer_1(rid, EOK, bytes);
}
 
void tmpfs_write(ipc_callid_t rid, ipc_call_t *request)
{
dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
off_t pos = (off_t)IPC_GET_ARG3(*request);
 
/*
* Lookup the respective dentry.
*/
link_t *hlp;
unsigned long key = index;
hlp = hash_table_find(&dentries, &key);
if (!hlp) {
ipc_answer_0(rid, ENOENT);
return;
}
tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
dh_link);
 
/*
* Receive the write request.
*/
ipc_callid_t callid;
size_t len;
if (!ipc_data_write_receive(&callid, &len)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
 
/*
* Check whether the file needs to grow.
*/
if (pos + len <= dentry->size) {
/* The file size is not changing. */
(void) ipc_data_write_finalize(callid, dentry->data + pos, len);
ipc_answer_2(rid, EOK, len, dentry->size);
return;
}
size_t delta = (pos + len) - dentry->size;
/*
* At this point, we are deliberately extremely straightforward and
* simply realloc the contents of the file on every write that grows the
* file. In the end, the situation might not be as bad as it may look:
* our heap allocator can save us and just grow the block whenever
* possible.
*/
void *newdata = realloc(dentry->data, dentry->size + delta);
if (!newdata) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_2(rid, EOK, 0, dentry->size);
return;
}
/* Clear any newly allocated memory in order to emulate gaps. */
memset(newdata + dentry->size, 0, delta);
dentry->size += delta;
dentry->data = newdata;
(void) ipc_data_write_finalize(callid, dentry->data + pos, len);
ipc_answer_2(rid, EOK, len, dentry->size);
}
 
void tmpfs_truncate(ipc_callid_t rid, ipc_call_t *request)
{
dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
size_t size = (off_t)IPC_GET_ARG3(*request);
 
/*
* Lookup the respective dentry.
*/
link_t *hlp;
unsigned long key = index;
hlp = hash_table_find(&dentries, &key);
if (!hlp) {
ipc_answer_0(rid, ENOENT);
return;
}
tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
dh_link);
 
if (size == dentry->size) {
ipc_answer_0(rid, EOK);
return;
}
 
void *newdata = realloc(dentry->data, size);
if (!newdata) {
ipc_answer_0(rid, ENOMEM);
return;
}
if (size > dentry->size) {
size_t delta = size - dentry->size;
memset(newdata + dentry->size, 0, delta);
}
dentry->size = size;
dentry->data = newdata;
ipc_answer_0(rid, EOK);
}
 
void tmpfs_destroy(ipc_callid_t rid, ipc_call_t *request)
{
dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
int rc;
 
link_t *hlp;
unsigned long key = index;
hlp = hash_table_find(&dentries, &key);
if (!hlp) {
ipc_answer_0(rid, ENOENT);
return;
}
tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
dh_link);
rc = tmpfs_destroy_node(dentry);
ipc_answer_0(rid, rc);
}
 
/**
* @}
*/
/branches/network/uspace/srv/fs/tmpfs/tmpfs_dump.c
0,0 → 1,214
/*
* Copyright (c) 2008 Jakub Jermar
* Copyright (c) 2008 Martin Decky
* 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 fs
* @{
*/
 
/**
* @file tmpfs_dump.c
* @brief Support for loading TMPFS file system dump.
*/
 
#include "tmpfs.h"
#include "../../vfs/vfs.h"
#include <ipc/ipc.h>
#include <async.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <as.h>
#include <libfs.h>
#include <ipc/services.h>
#include <ipc/devmap.h>
#include <sys/mman.h>
#include <byteorder.h>
 
#define TMPFS_BLOCK_SIZE 1024
 
struct rdentry {
uint8_t type;
uint32_t len;
} __attribute__((packed));
 
static bool
tmpfs_restore_recursion(int phone, void *block, off_t *bufpos, size_t *buflen,
off_t *pos, tmpfs_dentry_t *parent)
{
struct rdentry entry;
libfs_ops_t *ops = &tmpfs_libfs_ops;
do {
char *fname;
tmpfs_dentry_t *node;
uint32_t size;
if (!libfs_blockread(phone, block, bufpos, buflen, pos, &entry,
sizeof(entry), TMPFS_BLOCK_SIZE))
return false;
entry.len = uint32_t_le2host(entry.len);
switch (entry.type) {
case TMPFS_NONE:
break;
case TMPFS_FILE:
fname = malloc(entry.len + 1);
if (fname == NULL)
return false;
node = (tmpfs_dentry_t *) ops->create(L_FILE);
if (node == NULL) {
free(fname);
return false;
}
if (!libfs_blockread(phone, block, bufpos, buflen, pos,
fname, entry.len, TMPFS_BLOCK_SIZE)) {
ops->destroy((void *) node);
free(fname);
return false;
}
fname[entry.len] = 0;
if (!ops->link((void *) parent, (void *) node, fname)) {
ops->destroy((void *) node);
free(fname);
return false;
}
free(fname);
if (!libfs_blockread(phone, block, bufpos, buflen, pos,
&size, sizeof(size), TMPFS_BLOCK_SIZE))
return false;
size = uint32_t_le2host(size);
node->data = malloc(size);
if (node->data == NULL)
return false;
node->size = size;
if (!libfs_blockread(phone, block, bufpos, buflen, pos,
node->data, size, TMPFS_BLOCK_SIZE))
return false;
break;
case TMPFS_DIRECTORY:
fname = malloc(entry.len + 1);
if (fname == NULL)
return false;
node = (tmpfs_dentry_t *) ops->create(L_DIRECTORY);
if (node == NULL) {
free(fname);
return false;
}
if (!libfs_blockread(phone, block, bufpos, buflen, pos,
fname, entry.len, TMPFS_BLOCK_SIZE)) {
ops->destroy((void *) node);
free(fname);
return false;
}
fname[entry.len] = 0;
if (!ops->link((void *) parent, (void *) node, fname)) {
ops->destroy((void *) node);
free(fname);
return false;
}
free(fname);
if (!tmpfs_restore_recursion(phone, block, bufpos,
buflen, pos, node))
return false;
break;
default:
return false;
}
} while (entry.type != TMPFS_NONE);
return true;
}
 
bool tmpfs_restore(dev_handle_t dev)
{
libfs_ops_t *ops = &tmpfs_libfs_ops;
 
void *block = mmap(NULL, TMPFS_BLOCK_SIZE,
PROTO_READ | PROTO_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (block == NULL)
return false;
int phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP,
DEVMAP_CONNECT_TO_DEVICE, dev);
 
if (phone < 0) {
munmap(block, TMPFS_BLOCK_SIZE);
return false;
}
if (ipc_share_out_start(phone, block, AS_AREA_READ | AS_AREA_WRITE) !=
EOK)
goto error;
off_t bufpos = 0;
size_t buflen = 0;
off_t pos = 0;
char tag[6];
if (!libfs_blockread(phone, block, &bufpos, &buflen, &pos, tag, 5,
TMPFS_BLOCK_SIZE))
goto error;
tag[5] = 0;
if (strcmp(tag, "TMPFS") != 0)
goto error;
if (!tmpfs_restore_recursion(phone, block, &bufpos, &buflen, &pos,
ops->root_get(dev)))
goto error;
ipc_hangup(phone);
munmap(block, TMPFS_BLOCK_SIZE);
return true;
error:
ipc_hangup(phone);
munmap(block, TMPFS_BLOCK_SIZE);
return false;
}
 
/**
* @}
*/
/branches/network/uspace/srv/fs/tmpfs/tmpfs.h
0,0 → 1,83
/*
* 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 fs
* @{
*/
 
#ifndef TMPFS_TMPFS_H_
#define TMPFS_TMPFS_H_
 
#include <ipc/ipc.h>
#include <libfs.h>
#include <atomic.h>
#include <sys/types.h>
#include <bool.h>
#include <libadt/hash_table.h>
 
#ifndef dprintf
#define dprintf(...) printf(__VA_ARGS__)
#endif
 
typedef enum {
TMPFS_NONE,
TMPFS_FILE,
TMPFS_DIRECTORY
} tmpfs_dentry_type_t;
 
typedef struct tmpfs_dentry {
fs_index_t index; /**< TMPFS node index. */
link_t dh_link; /**< Dentries hash table link. */
struct tmpfs_dentry *sibling;
struct tmpfs_dentry *child;
hash_table_t names; /**< All names linking to this TMPFS node. */
tmpfs_dentry_type_t type;
unsigned lnkcnt; /**< Link count. */
size_t size; /**< File size if type is TMPFS_FILE. */
void *data; /**< File content's if type is TMPFS_FILE. */
} tmpfs_dentry_t;
 
extern fs_reg_t tmpfs_reg;
 
extern libfs_ops_t tmpfs_libfs_ops;
 
extern void tmpfs_mounted(ipc_callid_t, ipc_call_t *);
extern void tmpfs_mount(ipc_callid_t, ipc_call_t *);
extern void tmpfs_lookup(ipc_callid_t, ipc_call_t *);
extern void tmpfs_read(ipc_callid_t, ipc_call_t *);
extern void tmpfs_write(ipc_callid_t, ipc_call_t *);
extern void tmpfs_truncate(ipc_callid_t, ipc_call_t *);
extern void tmpfs_destroy(ipc_callid_t, ipc_call_t *);
 
extern bool tmpfs_restore(dev_handle_t);
 
#endif
 
/**
* @}
*/
/branches/network/uspace/srv/fs/tmpfs/Makefile
0,0 → 1,79
#
# Copyright (c) 2006 Martin Decky
# 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.
#
 
## Setup toolchain
#
 
LIBC_PREFIX = ../../../lib/libc
LIBFS_PREFIX = ../../../lib/libfs
SOFTINT_PREFIX = ../../../lib/softint
include $(LIBC_PREFIX)/Makefile.toolchain
 
CFLAGS += -I $(LIBFS_PREFIX)
 
LIBS = $(LIBC_PREFIX)/libc.a $(LIBFS_PREFIX)/libfs.a
 
## Sources
#
 
OUTPUT = tmpfs
SOURCES = \
tmpfs.c \
tmpfs_ops.c \
tmpfs_dump.c
 
OBJECTS := $(addsuffix .o,$(basename $(SOURCES)))
 
.PHONY: all clean depend disasm
 
all: $(OUTPUT) $(OUTPUT).disasm
 
-include Makefile.depend
 
clean:
-rm -f $(OUTPUT) $(OUTPUT).map $(OUTPUT).disasm Makefile.depend
 
depend:
$(CC) $(DEFS) $(CFLAGS) -M $(SOURCES) > Makefile.depend
 
$(OUTPUT): $(OBJECTS) $(LIBS)
$(LD) -T $(LIBC_PREFIX)/arch/$(ARCH)/_link.ld $(OBJECTS) $(LIBS) $(LFLAGS) -o $@ -Map $(OUTPUT).map
 
disasm: $(OUTPUT).disasm
 
$(OUTPUT).disasm: $(OUTPUT)
$(OBJDUMP) -d $< >$@
 
%.o: %.S
$(CC) $(DEFS) $(AFLAGS) $(CFLAGS) -D__ASM__ -c $< -o $@
 
%.o: %.s
$(AS) $(AFLAGS) $< -o $@
 
%.o: %.c
$(CC) $(DEFS) $(CFLAGS) -c $< -o $@
/branches/network/uspace/srv/fs/tmpfs/tmpfs.c
0,0 → 1,165
/*
* Copyright (c) 2006 Martin Decky
* 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 fs
* @{
*/
 
/**
* @file tmpfs.c
* @brief File system driver for in-memory file system.
*
* Every instance of tmpfs exists purely in memory and has neither a disk layout
* nor any permanent storage (e.g. disk blocks). With each system reboot, data
* stored in a tmpfs file system is lost.
*/
 
#include "tmpfs.h"
#include <ipc/ipc.h>
#include <ipc/services.h>
#include <async.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <libfs.h>
#include "../../vfs/vfs.h"
 
#define NAME "tmpfs"
 
 
vfs_info_t tmpfs_vfs_info = {
.name = "tmpfs",
.ops = {
[IPC_METHOD_TO_VFS_OP(VFS_LOOKUP)] = VFS_OP_DEFINED,
[IPC_METHOD_TO_VFS_OP(VFS_READ)] = VFS_OP_DEFINED,
[IPC_METHOD_TO_VFS_OP(VFS_WRITE)] = VFS_OP_DEFINED,
[IPC_METHOD_TO_VFS_OP(VFS_TRUNCATE)] = VFS_OP_DEFINED,
[IPC_METHOD_TO_VFS_OP(VFS_MOUNT)] = VFS_OP_DEFINED,
[IPC_METHOD_TO_VFS_OP(VFS_MOUNTED)] = VFS_OP_DEFINED,
[IPC_METHOD_TO_VFS_OP(VFS_UNMOUNT)] = VFS_OP_NULL,
[IPC_METHOD_TO_VFS_OP(VFS_DESTROY)] = VFS_OP_DEFINED,
}
};
 
fs_reg_t tmpfs_reg;
 
/**
* This connection fibril processes VFS requests from VFS.
*
* In order to support simultaneous VFS requests, our design is as follows.
* The connection fibril accepts VFS requests from VFS. If there is only one
* instance of the fibril, VFS will need to serialize all VFS requests it sends
* to FAT. To overcome this bottleneck, VFS can send TMPFS the
* IPC_M_CONNECT_ME_TO call. In that case, a new connection fibril will be
* created, which in turn will accept the call. Thus, a new phone will be
* opened for VFS.
*
* There are few issues with this arrangement. First, VFS can run out of
* available phones. In that case, VFS can close some other phones or use one
* phone for more serialized requests. Similarily, TMPFS can refuse to duplicate
* the connection. VFS should then just make use of already existing phones and
* route its requests through them. To avoid paying the fibril creation price
* upon each request, TMPFS might want to keep the connections open after the
* request has been completed.
*/
static void tmpfs_connection(ipc_callid_t iid, ipc_call_t *icall)
{
if (iid) {
/*
* This only happens for connections opened by
* IPC_M_CONNECT_ME_TO calls as opposed to callback connections
* created by IPC_M_CONNECT_TO_ME.
*/
ipc_answer_0(iid, EOK);
}
dprintf("VFS-TMPFS connection established.\n");
while (1) {
ipc_callid_t callid;
ipc_call_t call;
callid = async_get_call(&call);
switch (IPC_GET_METHOD(call)) {
case VFS_MOUNTED:
tmpfs_mounted(callid, &call);
break;
case VFS_MOUNT:
tmpfs_mount(callid, &call);
break;
case VFS_LOOKUP:
tmpfs_lookup(callid, &call);
break;
case VFS_READ:
tmpfs_read(callid, &call);
break;
case VFS_WRITE:
tmpfs_write(callid, &call);
break;
case VFS_TRUNCATE:
tmpfs_truncate(callid, &call);
break;
case VFS_DESTROY:
tmpfs_destroy(callid, &call);
break;
default:
ipc_answer_0(callid, ENOTSUP);
break;
}
}
}
 
int main(int argc, char **argv)
{
int vfs_phone;
 
printf(NAME ": HelenOS TMPFS file system server\n");
 
vfs_phone = ipc_connect_me_to(PHONE_NS, SERVICE_VFS, 0, 0);
while (vfs_phone < EOK) {
usleep(10000);
vfs_phone = ipc_connect_me_to(PHONE_NS, SERVICE_VFS, 0, 0);
}
int rc;
rc = fs_register(vfs_phone, &tmpfs_reg, &tmpfs_vfs_info,
tmpfs_connection);
if (rc != EOK) {
printf(NAME ": Failed to register file system (%d)\n", rc);
return rc;
}
printf(NAME ": Accepting connections\n");
async_manager();
/* not reached */
return 0;
}
 
/**
* @}
*/
/branches/network/uspace/srv/fs/fat/fat_ops.c
0,0 → 1,852
/*
* 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 fs
* @{
*/
 
/**
* @file fat_ops.c
* @brief Implementation of VFS operations for the FAT file system server.
*/
 
#include "fat.h"
#include "../../vfs/vfs.h"
#include <libfs.h>
#include <ipc/ipc.h>
#include <ipc/services.h>
#include <ipc/devmap.h>
#include <async.h>
#include <errno.h>
#include <string.h>
#include <byteorder.h>
#include <libadt/hash_table.h>
#include <libadt/list.h>
#include <assert.h>
#include <futex.h>
#include <sys/mman.h>
 
#define BS_BLOCK 0
#define BS_SIZE 512
 
/** Futex protecting the list of cached free FAT nodes. */
static futex_t ffn_futex = FUTEX_INITIALIZER;
 
/** List of cached free FAT nodes. */
static LIST_INITIALIZE(ffn_head);
 
#define FAT_NAME_LEN 8
#define FAT_EXT_LEN 3
 
#define FAT_PAD ' '
 
#define FAT_DENTRY_UNUSED 0x00
#define FAT_DENTRY_E5_ESC 0x05
#define FAT_DENTRY_DOT 0x2e
#define FAT_DENTRY_ERASED 0xe5
 
#define min(a, b) ((a) < (b) ? (a) : (b))
 
static void dentry_name_canonify(fat_dentry_t *d, char *buf)
{
int i;
 
for (i = 0; i < FAT_NAME_LEN; i++) {
if (d->name[i] == FAT_PAD)
break;
if (d->name[i] == FAT_DENTRY_E5_ESC)
*buf++ = 0xe5;
else
*buf++ = d->name[i];
}
if (d->ext[0] != FAT_PAD)
*buf++ = '.';
for (i = 0; i < FAT_EXT_LEN; i++) {
if (d->ext[i] == FAT_PAD) {
*buf = '\0';
return;
}
if (d->ext[i] == FAT_DENTRY_E5_ESC)
*buf++ = 0xe5;
else
*buf++ = d->ext[i];
}
*buf = '\0';
}
 
static int dev_phone = -1; /* FIXME */
static void *dev_buffer = NULL; /* FIXME */
 
/* TODO move somewhere else */
typedef struct {
void *data;
size_t size;
} block_t;
 
static block_t *block_get(dev_handle_t dev_handle, off_t offset, size_t bs)
{
/* FIXME */
block_t *b;
off_t bufpos = 0;
size_t buflen = 0;
off_t pos = offset * bs;
 
assert(dev_phone != -1);
assert(dev_buffer);
 
b = malloc(sizeof(block_t));
if (!b)
return NULL;
b->data = malloc(bs);
if (!b->data) {
free(b);
return NULL;
}
b->size = bs;
 
if (!libfs_blockread(dev_phone, dev_buffer, &bufpos, &buflen, &pos,
b->data, bs, bs)) {
free(b->data);
free(b);
return NULL;
}
 
return b;
}
 
static void block_put(block_t *block)
{
/* FIXME */
free(block->data);
free(block);
}
 
#define FAT_BS(b) ((fat_bs_t *)((b)->data))
 
#define FAT_CLST_RES0 0x0000
#define FAT_CLST_RES1 0x0001
#define FAT_CLST_FIRST 0x0002
#define FAT_CLST_BAD 0xfff7
#define FAT_CLST_LAST1 0xfff8
#define FAT_CLST_LAST8 0xffff
 
/* internally used to mark root directory's parent */
#define FAT_CLST_ROOTPAR FAT_CLST_RES0
/* internally used to mark root directory */
#define FAT_CLST_ROOT FAT_CLST_RES1
 
#define fat_block_get(np, off) \
_fat_block_get((np)->idx->dev_handle, (np)->firstc, (off))
 
static block_t *
_fat_block_get(dev_handle_t dev_handle, fat_cluster_t firstc, off_t offset)
{
block_t *bb;
block_t *b;
unsigned bps;
unsigned spc;
unsigned rscnt; /* block address of the first FAT */
unsigned fatcnt;
unsigned rde;
unsigned rds; /* root directory size */
unsigned sf;
unsigned ssa; /* size of the system area */
unsigned clusters;
fat_cluster_t clst = firstc;
unsigned i;
 
bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
bps = uint16_t_le2host(FAT_BS(bb)->bps);
spc = FAT_BS(bb)->spc;
rscnt = uint16_t_le2host(FAT_BS(bb)->rscnt);
fatcnt = FAT_BS(bb)->fatcnt;
rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
sf = uint16_t_le2host(FAT_BS(bb)->sec_per_fat);
block_put(bb);
 
rds = (sizeof(fat_dentry_t) * rde) / bps;
rds += ((sizeof(fat_dentry_t) * rde) % bps != 0);
ssa = rscnt + fatcnt * sf + rds;
 
if (firstc == FAT_CLST_ROOT) {
/* root directory special case */
assert(offset < rds);
b = block_get(dev_handle, rscnt + fatcnt * sf + offset, bps);
return b;
}
 
clusters = offset / spc;
for (i = 0; i < clusters; i++) {
unsigned fsec; /* sector offset relative to FAT1 */
unsigned fidx; /* FAT1 entry index */
 
assert(clst >= FAT_CLST_FIRST && clst < FAT_CLST_BAD);
fsec = (clst * sizeof(fat_cluster_t)) / bps;
fidx = clst % (bps / sizeof(fat_cluster_t));
/* read FAT1 */
b = block_get(dev_handle, rscnt + fsec, bps);
clst = uint16_t_le2host(((fat_cluster_t *)b->data)[fidx]);
assert(clst != FAT_CLST_BAD);
assert(clst < FAT_CLST_LAST1);
block_put(b);
}
 
b = block_get(dev_handle, ssa + (clst - FAT_CLST_FIRST) * spc +
offset % spc, bps);
 
return b;
}
 
/** Return number of blocks allocated to a file.
*
* @param dev_handle Device handle of the device with the file.
* @param firstc First cluster of the file.
*
* @return Number of blocks allocated to the file.
*/
static uint16_t
_fat_blcks_get(dev_handle_t dev_handle, fat_cluster_t firstc)
{
block_t *bb;
block_t *b;
unsigned bps;
unsigned spc;
unsigned rscnt; /* block address of the first FAT */
unsigned clusters = 0;
fat_cluster_t clst = firstc;
 
bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
bps = uint16_t_le2host(FAT_BS(bb)->bps);
spc = FAT_BS(bb)->spc;
rscnt = uint16_t_le2host(FAT_BS(bb)->rscnt);
block_put(bb);
 
if (firstc == FAT_CLST_RES0) {
/* No space allocated to the file. */
return 0;
}
 
while (clst < FAT_CLST_LAST1) {
unsigned fsec; /* sector offset relative to FAT1 */
unsigned fidx; /* FAT1 entry index */
 
assert(clst >= FAT_CLST_FIRST);
fsec = (clst * sizeof(fat_cluster_t)) / bps;
fidx = clst % (bps / sizeof(fat_cluster_t));
/* read FAT1 */
b = block_get(dev_handle, rscnt + fsec, bps);
clst = uint16_t_le2host(((fat_cluster_t *)b->data)[fidx]);
assert(clst != FAT_CLST_BAD);
block_put(b);
clusters++;
}
 
return clusters * spc;
}
 
static void fat_node_initialize(fat_node_t *node)
{
futex_initialize(&node->lock, 1);
node->idx = NULL;
node->type = 0;
link_initialize(&node->ffn_link);
node->size = 0;
node->lnkcnt = 0;
node->refcnt = 0;
node->dirty = false;
}
 
static uint16_t fat_bps_get(dev_handle_t dev_handle)
{
block_t *bb;
uint16_t bps;
bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
assert(bb != NULL);
bps = uint16_t_le2host(FAT_BS(bb)->bps);
block_put(bb);
 
return bps;
}
 
typedef enum {
FAT_DENTRY_SKIP,
FAT_DENTRY_LAST,
FAT_DENTRY_VALID
} fat_dentry_clsf_t;
 
static fat_dentry_clsf_t fat_classify_dentry(fat_dentry_t *d)
{
if (d->attr & FAT_ATTR_VOLLABEL) {
/* volume label entry */
return FAT_DENTRY_SKIP;
}
if (d->name[0] == FAT_DENTRY_ERASED) {
/* not-currently-used entry */
return FAT_DENTRY_SKIP;
}
if (d->name[0] == FAT_DENTRY_UNUSED) {
/* never used entry */
return FAT_DENTRY_LAST;
}
if (d->name[0] == FAT_DENTRY_DOT) {
/*
* Most likely '.' or '..'.
* It cannot occur in a regular file name.
*/
return FAT_DENTRY_SKIP;
}
return FAT_DENTRY_VALID;
}
 
static void fat_node_sync(fat_node_t *node)
{
/* TODO */
}
 
/** Internal version of fat_node_get().
*
* @param idxp Locked index structure.
*/
static void *fat_node_get_core(fat_idx_t *idxp)
{
block_t *b;
fat_dentry_t *d;
fat_node_t *nodep = NULL;
unsigned bps;
unsigned dps;
 
if (idxp->nodep) {
/*
* We are lucky.
* The node is already instantiated in memory.
*/
futex_down(&idxp->nodep->lock);
if (!idxp->nodep->refcnt++)
list_remove(&idxp->nodep->ffn_link);
futex_up(&idxp->nodep->lock);
return idxp->nodep;
}
 
/*
* We must instantiate the node from the file system.
*/
assert(idxp->pfc);
 
futex_down(&ffn_futex);
if (!list_empty(&ffn_head)) {
/* Try to use a cached free node structure. */
fat_idx_t *idxp_tmp;
nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
if (futex_trydown(&nodep->lock) == ESYNCH_WOULD_BLOCK)
goto skip_cache;
idxp_tmp = nodep->idx;
if (futex_trydown(&idxp_tmp->lock) == ESYNCH_WOULD_BLOCK) {
futex_up(&nodep->lock);
goto skip_cache;
}
list_remove(&nodep->ffn_link);
futex_up(&ffn_futex);
if (nodep->dirty)
fat_node_sync(nodep);
idxp_tmp->nodep = NULL;
futex_up(&nodep->lock);
futex_up(&idxp_tmp->lock);
} else {
skip_cache:
/* Try to allocate a new node structure. */
futex_up(&ffn_futex);
nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
if (!nodep)
return NULL;
}
fat_node_initialize(nodep);
 
bps = fat_bps_get(idxp->dev_handle);
dps = bps / sizeof(fat_dentry_t);
 
/* Read the block that contains the dentry of interest. */
b = _fat_block_get(idxp->dev_handle, idxp->pfc,
(idxp->pdi * sizeof(fat_dentry_t)) / bps);
assert(b);
 
d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
if (d->attr & FAT_ATTR_SUBDIR) {
/*
* The only directory which does not have this bit set is the
* root directory itself. The root directory node is handled
* and initialized elsewhere.
*/
nodep->type = FAT_DIRECTORY;
/*
* Unfortunately, the 'size' field of the FAT dentry is not
* defined for the directory entry type. We must determine the
* size of the directory by walking the FAT.
*/
nodep->size = bps * _fat_blcks_get(idxp->dev_handle,
uint16_t_le2host(d->firstc));
} else {
nodep->type = FAT_FILE;
nodep->size = uint32_t_le2host(d->size);
}
nodep->firstc = uint16_t_le2host(d->firstc);
nodep->lnkcnt = 1;
nodep->refcnt = 1;
 
block_put(b);
 
/* Link the idx structure with the node structure. */
nodep->idx = idxp;
idxp->nodep = nodep;
 
return nodep;
}
 
/** Instantiate a FAT in-core node. */
static void *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
{
void *node;
fat_idx_t *idxp;
 
idxp = fat_idx_get_by_index(dev_handle, index);
if (!idxp)
return NULL;
/* idxp->lock held */
node = fat_node_get_core(idxp);
futex_up(&idxp->lock);
return node;
}
 
static void fat_node_put(void *node)
{
fat_node_t *nodep = (fat_node_t *)node;
 
futex_down(&nodep->lock);
if (!--nodep->refcnt) {
futex_down(&ffn_futex);
list_append(&nodep->ffn_link, &ffn_head);
futex_up(&ffn_futex);
}
futex_up(&nodep->lock);
}
 
static void *fat_create(int flags)
{
return NULL; /* not supported at the moment */
}
 
static int fat_destroy(void *node)
{
return ENOTSUP; /* not supported at the moment */
}
 
static bool fat_link(void *prnt, void *chld, const char *name)
{
return false; /* not supported at the moment */
}
 
static int fat_unlink(void *prnt, void *chld)
{
return ENOTSUP; /* not supported at the moment */
}
 
static void *fat_match(void *prnt, const char *component)
{
fat_node_t *parentp = (fat_node_t *)prnt;
char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
unsigned i, j;
unsigned bps; /* bytes per sector */
unsigned dps; /* dentries per sector */
unsigned blocks;
fat_dentry_t *d;
block_t *b;
 
futex_down(&parentp->idx->lock);
bps = fat_bps_get(parentp->idx->dev_handle);
dps = bps / sizeof(fat_dentry_t);
blocks = parentp->size / bps + (parentp->size % bps != 0);
for (i = 0; i < blocks; i++) {
unsigned dentries;
b = fat_block_get(parentp, i);
dentries = (i == blocks - 1) ?
parentp->size % sizeof(fat_dentry_t) :
dps;
for (j = 0; j < dentries; j++) {
d = ((fat_dentry_t *)b->data) + j;
switch (fat_classify_dentry(d)) {
case FAT_DENTRY_SKIP:
continue;
case FAT_DENTRY_LAST:
block_put(b);
futex_up(&parentp->idx->lock);
return NULL;
default:
case FAT_DENTRY_VALID:
dentry_name_canonify(d, name);
break;
}
if (stricmp(name, component) == 0) {
/* hit */
void *node;
/*
* Assume tree hierarchy for locking. We
* already have the parent and now we are going
* to lock the child. Never lock in the oposite
* order.
*/
fat_idx_t *idx = fat_idx_get_by_pos(
parentp->idx->dev_handle, parentp->firstc,
i * dps + j);
futex_up(&parentp->idx->lock);
if (!idx) {
/*
* Can happen if memory is low or if we
* run out of 32-bit indices.
*/
block_put(b);
return NULL;
}
node = fat_node_get_core(idx);
futex_up(&idx->lock);
block_put(b);
return node;
}
}
block_put(b);
}
futex_up(&parentp->idx->lock);
return NULL;
}
 
static fs_index_t fat_index_get(void *node)
{
fat_node_t *fnodep = (fat_node_t *)node;
if (!fnodep)
return 0;
return fnodep->idx->index;
}
 
static size_t fat_size_get(void *node)
{
return ((fat_node_t *)node)->size;
}
 
static unsigned fat_lnkcnt_get(void *node)
{
return ((fat_node_t *)node)->lnkcnt;
}
 
static bool fat_has_children(void *node)
{
fat_node_t *nodep = (fat_node_t *)node;
unsigned bps;
unsigned dps;
unsigned blocks;
block_t *b;
unsigned i, j;
 
if (nodep->type != FAT_DIRECTORY)
return false;
 
futex_down(&nodep->idx->lock);
bps = fat_bps_get(nodep->idx->dev_handle);
dps = bps / sizeof(fat_dentry_t);
 
blocks = nodep->size / bps + (nodep->size % bps != 0);
 
for (i = 0; i < blocks; i++) {
unsigned dentries;
fat_dentry_t *d;
b = fat_block_get(nodep, i);
dentries = (i == blocks - 1) ?
nodep->size % sizeof(fat_dentry_t) :
dps;
for (j = 0; j < dentries; j++) {
d = ((fat_dentry_t *)b->data) + j;
switch (fat_classify_dentry(d)) {
case FAT_DENTRY_SKIP:
continue;
case FAT_DENTRY_LAST:
block_put(b);
futex_up(&nodep->idx->lock);
return false;
default:
case FAT_DENTRY_VALID:
block_put(b);
futex_up(&nodep->idx->lock);
return true;
}
block_put(b);
futex_up(&nodep->idx->lock);
return true;
}
block_put(b);
}
 
futex_up(&nodep->idx->lock);
return false;
}
 
static void *fat_root_get(dev_handle_t dev_handle)
{
return fat_node_get(dev_handle, 0);
}
 
static char fat_plb_get_char(unsigned pos)
{
return fat_reg.plb_ro[pos % PLB_SIZE];
}
 
static bool fat_is_directory(void *node)
{
return ((fat_node_t *)node)->type == FAT_DIRECTORY;
}
 
static bool fat_is_file(void *node)
{
return ((fat_node_t *)node)->type == FAT_FILE;
}
 
/** libfs operations */
libfs_ops_t fat_libfs_ops = {
.match = fat_match,
.node_get = fat_node_get,
.node_put = fat_node_put,
.create = fat_create,
.destroy = fat_destroy,
.link = fat_link,
.unlink = fat_unlink,
.index_get = fat_index_get,
.size_get = fat_size_get,
.lnkcnt_get = fat_lnkcnt_get,
.has_children = fat_has_children,
.root_get = fat_root_get,
.plb_get_char = fat_plb_get_char,
.is_directory = fat_is_directory,
.is_file = fat_is_file
};
 
void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
{
dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
block_t *bb;
uint16_t bps;
uint16_t rde;
int rc;
 
/*
* For now, we don't bother to remember dev_handle, dev_phone or
* dev_buffer in some data structure. We use global variables because we
* know there will be at most one mount on this file system.
* Of course, this is a huge TODO item.
*/
dev_buffer = mmap(NULL, BS_SIZE, PROTO_READ | PROTO_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (!dev_buffer) {
ipc_answer_0(rid, ENOMEM);
return;
}
 
dev_phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP,
DEVMAP_CONNECT_TO_DEVICE, dev_handle);
 
if (dev_phone < 0) {
munmap(dev_buffer, BS_SIZE);
ipc_answer_0(rid, dev_phone);
return;
}
 
rc = ipc_share_out_start(dev_phone, dev_buffer,
AS_AREA_READ | AS_AREA_WRITE);
if (rc != EOK) {
munmap(dev_buffer, BS_SIZE);
ipc_answer_0(rid, rc);
return;
}
 
/* Read the number of root directory entries. */
bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
bps = uint16_t_le2host(FAT_BS(bb)->bps);
rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
block_put(bb);
 
if (bps != BS_SIZE) {
munmap(dev_buffer, BS_SIZE);
ipc_answer_0(rid, ENOTSUP);
return;
}
 
rc = fat_idx_init_by_dev_handle(dev_handle);
if (rc != EOK) {
munmap(dev_buffer, BS_SIZE);
ipc_answer_0(rid, rc);
return;
}
 
/* Initialize the root node. */
fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
if (!rootp) {
munmap(dev_buffer, BS_SIZE);
fat_idx_fini_by_dev_handle(dev_handle);
ipc_answer_0(rid, ENOMEM);
return;
}
fat_node_initialize(rootp);
 
fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
if (!ridxp) {
munmap(dev_buffer, BS_SIZE);
free(rootp);
fat_idx_fini_by_dev_handle(dev_handle);
ipc_answer_0(rid, ENOMEM);
return;
}
assert(ridxp->index == 0);
/* ridxp->lock held */
 
rootp->type = FAT_DIRECTORY;
rootp->firstc = FAT_CLST_ROOT;
rootp->refcnt = 1;
rootp->lnkcnt = 0; /* FS root is not linked */
rootp->size = rde * sizeof(fat_dentry_t);
rootp->idx = ridxp;
ridxp->nodep = rootp;
futex_up(&ridxp->lock);
 
ipc_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
}
 
void fat_mount(ipc_callid_t rid, ipc_call_t *request)
{
ipc_answer_0(rid, ENOTSUP);
}
 
void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
{
libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
}
 
void fat_read(ipc_callid_t rid, ipc_call_t *request)
{
dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
off_t pos = (off_t)IPC_GET_ARG3(*request);
fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
uint16_t bps = fat_bps_get(dev_handle);
size_t bytes;
block_t *b;
 
if (!nodep) {
ipc_answer_0(rid, ENOENT);
return;
}
 
ipc_callid_t callid;
size_t len;
if (!ipc_data_read_receive(&callid, &len)) {
fat_node_put(nodep);
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
 
if (nodep->type == FAT_FILE) {
/*
* Our strategy for regular file reads is to read one block at
* most and make use of the possibility to return less data than
* requested. This keeps the code very simple.
*/
bytes = min(len, bps - pos % bps);
b = fat_block_get(nodep, pos / bps);
(void) ipc_data_read_finalize(callid, b->data + pos % bps,
bytes);
block_put(b);
} else {
unsigned bnum;
off_t spos = pos;
char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
fat_dentry_t *d;
 
assert(nodep->type == FAT_DIRECTORY);
assert(nodep->size % bps == 0);
assert(bps % sizeof(fat_dentry_t) == 0);
 
/*
* Our strategy for readdir() is to use the position pointer as
* an index into the array of all dentries. On entry, it points
* to the first unread dentry. If we skip any dentries, we bump
* the position pointer accordingly.
*/
bnum = (pos * sizeof(fat_dentry_t)) / bps;
while (bnum < nodep->size / bps) {
off_t o;
 
b = fat_block_get(nodep, bnum);
for (o = pos % (bps / sizeof(fat_dentry_t));
o < bps / sizeof(fat_dentry_t);
o++, pos++) {
d = ((fat_dentry_t *)b->data) + o;
switch (fat_classify_dentry(d)) {
case FAT_DENTRY_SKIP:
continue;
case FAT_DENTRY_LAST:
block_put(b);
goto miss;
default:
case FAT_DENTRY_VALID:
dentry_name_canonify(d, name);
block_put(b);
goto hit;
}
}
block_put(b);
bnum++;
}
miss:
fat_node_put(nodep);
ipc_answer_0(callid, ENOENT);
ipc_answer_1(rid, ENOENT, 0);
return;
hit:
(void) ipc_data_read_finalize(callid, name, strlen(name) + 1);
bytes = (pos - spos) + 1;
}
 
fat_node_put(nodep);
ipc_answer_1(rid, EOK, (ipcarg_t)bytes);
}
 
/**
* @}
*/
/branches/network/uspace/srv/fs/fat/fat.h
0,0 → 1,239
/*
* 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 fs
* @{
*/
 
#ifndef FAT_FAT_H_
#define FAT_FAT_H_
 
#include <ipc/ipc.h>
#include <libfs.h>
#include <atomic.h>
#include <sys/types.h>
#include <bool.h>
#include "../../vfs/vfs.h"
 
#ifndef dprintf
#define dprintf(...) printf(__VA_ARGS__)
#endif
 
typedef struct {
uint8_t ji[3]; /**< Jump instruction. */
uint8_t oem_name[8];
/* BIOS Parameter Block */
uint16_t bps; /**< Bytes per sector. */
uint8_t spc; /**< Sectors per cluster. */
uint16_t rscnt; /**< Reserved sector count. */
uint8_t fatcnt; /**< Number of FATs. */
uint16_t root_ent_max; /**< Maximum number of root directory
entries. */
uint16_t totsec16; /**< Total sectors. 16-bit version. */
uint8_t mdesc; /**< Media descriptor. */
uint16_t sec_per_fat; /**< Sectors per FAT12/FAT16. */
uint16_t sec_per_track; /**< Sectors per track. */
uint16_t headcnt; /**< Number of heads. */
uint32_t hidden_sec; /**< Hidden sectors. */
uint32_t totsec32; /**< Total sectors. 32-bit version. */
 
union {
struct {
/* FAT12/FAT16 only: Extended BIOS Parameter Block */
/** Physical drive number. */
uint8_t pdn;
uint8_t reserved;
/** Extended boot signature. */
uint8_t ebs;
/** Serial number. */
uint32_t id;
/** Volume label. */
uint8_t label[11];
/** FAT type. */
uint8_t type[8];
/** Boot code. */
uint8_t boot_code[448];
/** Boot sector signature. */
uint16_t signature;
} __attribute__ ((packed));
struct {
/* FAT32 only */
/** Sectors per FAT. */
uint32_t sectors_per_fat;
/** FAT flags. */
uint16_t flags;
/** Version. */
uint16_t version;
/** Cluster number of root directory. */
uint32_t root_cluster;
/** Sector number of file system information sector. */
uint16_t fsinfo_sec;
/** Sector number of boot sector copy. */
uint16_t bscopy_sec;
uint8_t reserved1[12];
/** Physical drive number. */
uint8_t pdn;
uint8_t reserved2;
/** Extended boot signature. */
uint8_t ebs;
/** Serial number. */
uint32_t id;
/** Volume label. */
uint8_t label[11];
/** FAT type. */
uint8_t type[8];
/** Boot code. */
uint8_t boot_code[420];
/** Signature. */
uint16_t signature;
} __attribute__ ((packed));
};
} __attribute__ ((packed)) fat_bs_t;
 
#define FAT_ATTR_RDONLY (1 << 0)
#define FAT_ATTR_VOLLABEL (1 << 3)
#define FAT_ATTR_SUBDIR (1 << 4)
 
typedef struct {
uint8_t name[8];
uint8_t ext[3];
uint8_t attr;
uint8_t reserved;
uint8_t ctime_fine;
uint16_t ctime;
uint16_t cdate;
uint16_t adate;
union {
uint16_t eaidx; /* FAT12/FAT16 */
uint16_t firstc_hi; /* FAT32 */
};
uint16_t mtime;
uint16_t mdate;
union {
uint16_t firstc; /* FAT12/FAT16 */
uint16_t firstc_lo; /* FAT32 */
};
uint32_t size;
} __attribute__ ((packed)) fat_dentry_t;
 
typedef uint16_t fat_cluster_t;
 
typedef enum {
FAT_INVALID,
FAT_DIRECTORY,
FAT_FILE
} fat_node_type_t;
 
struct fat_node;
 
/** FAT index structure.
*
* This structure exists to help us to overcome certain limitations of the FAT
* file system design. The problem with FAT is that it is hard to find
* an entity which could represent a VFS index. There are two candidates:
*
* a) number of the node's first cluster
* b) the pair of the parent directory's first cluster and the dentry index
* within the parent directory
*
* We need VFS indices to be:
* A) unique
* B) stable in time, at least until the next mount
*
* Unfortunately a) does not meet the A) criterion because zero-length files
* will have the first cluster field cleared. And b) does not meet the B)
* criterion because unlink() and rename() will both free up the original
* dentry, which contains all the essential info about the file.
*
* Therefore, a completely opaque indices are used and the FAT server maintains
* a mapping between them and otherwise nice b) variant. On rename(), the VFS
* index stays unaltered, while the internal FAT "physical tree address"
* changes. The unlink case is also handled this way thanks to an in-core node
* pointer embedded in the index structure.
*/
typedef struct {
/** Used indices (position) hash table link. */
link_t uph_link;
/** Used indices (index) hash table link. */
link_t uih_link;
 
futex_t lock;
dev_handle_t dev_handle;
fs_index_t index;
/**
* Parent node's first cluster.
* Zero is used if this node is not linked, in which case nodep must
* contain a pointer to the in-core node structure.
* One is used when the parent is the root directory.
*/
fat_cluster_t pfc;
/** Directory entry index within the parent node. */
unsigned pdi;
/** Pointer to in-core node instance. */
struct fat_node *nodep;
} fat_idx_t;
 
/** FAT in-core node. */
typedef struct fat_node {
futex_t lock;
fat_node_type_t type;
fat_idx_t *idx;
/**
* Node's first cluster.
* Zero is used for zero-length nodes.
* One is used to mark root directory.
*/
fat_cluster_t firstc;
/** FAT in-core node free list link. */
link_t ffn_link;
size_t size;
unsigned lnkcnt;
unsigned refcnt;
bool dirty;
} fat_node_t;
 
extern fs_reg_t fat_reg;
 
extern void fat_mounted(ipc_callid_t, ipc_call_t *);
extern void fat_mount(ipc_callid_t, ipc_call_t *);
extern void fat_lookup(ipc_callid_t, ipc_call_t *);
extern void fat_read(ipc_callid_t, ipc_call_t *);
 
extern fat_idx_t *fat_idx_get_by_pos(dev_handle_t, fat_cluster_t, unsigned);
extern fat_idx_t *fat_idx_get_by_index(dev_handle_t, fs_index_t);
 
extern int fat_idx_init(void);
extern void fat_idx_fini(void);
extern int fat_idx_init_by_dev_handle(dev_handle_t);
extern void fat_idx_fini_by_dev_handle(dev_handle_t);
 
#endif
 
/**
* @}
*/
/branches/network/uspace/srv/fs/fat/fat.c
0,0 → 1,157
/*
* Copyright (c) 2006 Martin Decky
* 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 fs
* @{
*/
 
/**
* @file fat.c
* @brief FAT file system driver for HelenOS.
*/
 
#include "fat.h"
#include <ipc/ipc.h>
#include <ipc/services.h>
#include <async.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <libfs.h>
#include "../../vfs/vfs.h"
 
 
vfs_info_t fat_vfs_info = {
.name = "fat",
.ops = {
[IPC_METHOD_TO_VFS_OP(VFS_LOOKUP)] = VFS_OP_DEFINED,
[IPC_METHOD_TO_VFS_OP(VFS_READ)] = VFS_OP_DEFINED,
[IPC_METHOD_TO_VFS_OP(VFS_WRITE)] = VFS_OP_NULL,
[IPC_METHOD_TO_VFS_OP(VFS_TRUNCATE)] = VFS_OP_NULL,
[IPC_METHOD_TO_VFS_OP(VFS_MOUNT)] = VFS_OP_NULL,
[IPC_METHOD_TO_VFS_OP(VFS_MOUNTED)] = VFS_OP_DEFINED,
[IPC_METHOD_TO_VFS_OP(VFS_UNMOUNT)] = VFS_OP_NULL,
}
};
 
fs_reg_t fat_reg;
 
/**
* This connection fibril processes VFS requests from VFS.
*
* In order to support simultaneous VFS requests, our design is as follows.
* The connection fibril accepts VFS requests from VFS. If there is only one
* instance of the fibril, VFS will need to serialize all VFS requests it sends
* to FAT. To overcome this bottleneck, VFS can send FAT the IPC_M_CONNECT_ME_TO
* call. In that case, a new connection fibril will be created, which in turn
* will accept the call. Thus, a new phone will be opened for VFS.
*
* There are few issues with this arrangement. First, VFS can run out of
* available phones. In that case, VFS can close some other phones or use one
* phone for more serialized requests. Similarily, FAT can refuse to duplicate
* the connection. VFS should then just make use of already existing phones and
* route its requests through them. To avoid paying the fibril creation price
* upon each request, FAT might want to keep the connections open after the
* request has been completed.
*/
static void fat_connection(ipc_callid_t iid, ipc_call_t *icall)
{
if (iid) {
/*
* This only happens for connections opened by
* IPC_M_CONNECT_ME_TO calls as opposed to callback connections
* created by IPC_M_CONNECT_TO_ME.
*/
ipc_answer_0(iid, EOK);
}
dprintf("VFS-FAT connection established.\n");
while (1) {
ipc_callid_t callid;
ipc_call_t call;
callid = async_get_call(&call);
switch (IPC_GET_METHOD(call)) {
case VFS_MOUNTED:
fat_mounted(callid, &call);
break;
case VFS_MOUNT:
fat_mount(callid, &call);
break;
case VFS_LOOKUP:
fat_lookup(callid, &call);
break;
case VFS_READ:
fat_read(callid, &call);
break;
default:
ipc_answer_0(callid, ENOTSUP);
break;
}
}
}
 
int main(int argc, char **argv)
{
int vfs_phone;
int rc;
 
printf("FAT: HelenOS FAT file system server.\n");
 
rc = fat_idx_init();
if (rc != EOK)
goto err;
 
vfs_phone = ipc_connect_me_to(PHONE_NS, SERVICE_VFS, 0, 0);
while (vfs_phone < EOK) {
usleep(10000);
vfs_phone = ipc_connect_me_to(PHONE_NS, SERVICE_VFS, 0, 0);
}
rc = fs_register(vfs_phone, &fat_reg, &fat_vfs_info, fat_connection);
if (rc != EOK) {
fat_idx_fini();
goto err;
}
dprintf("FAT filesystem registered, fs_handle=%d.\n",
fat_reg.fs_handle);
 
async_manager();
/* not reached */
return 0;
 
err:
printf("Failed to register the FAT file system (%d)\n", rc);
return rc;
}
 
/**
* @}
*/
/branches/network/uspace/srv/fs/fat/Makefile
0,0 → 1,79
#
# Copyright (c) 2006 Martin Decky
# 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.
#
 
## Setup toolchain
#
 
LIBC_PREFIX = ../../../lib/libc
LIBFS_PREFIX = ../../../lib/libfs
SOFTINT_PREFIX = ../../../lib/softint
include $(LIBC_PREFIX)/Makefile.toolchain
 
CFLAGS += -I $(LIBFS_PREFIX)
 
LIBS = $(LIBC_PREFIX)/libc.a $(LIBFS_PREFIX)/libfs.a
 
## Sources
#
 
OUTPUT = fat
SOURCES = \
fat.c \
fat_ops.c \
fat_idx.c
 
OBJECTS := $(addsuffix .o,$(basename $(SOURCES)))
 
.PHONY: all clean depend disasm
 
all: $(OUTPUT) $(OUTPUT).disasm
 
-include Makefile.depend
 
clean:
-rm -f $(OUTPUT) $(OUTPUT).map $(OUTPUT).disasm Makefile.depend
 
depend:
$(CC) $(DEFS) $(CFLAGS) -M $(SOURCES) > Makefile.depend
 
$(OUTPUT): $(OBJECTS) $(LIBS)
$(LD) -T $(LIBC_PREFIX)/arch/$(ARCH)/_link.ld $(OBJECTS) $(LIBS) $(LFLAGS) -o $@ -Map $(OUTPUT).map
 
disasm: $(OUTPUT).disasm
 
$(OUTPUT).disasm: $(OUTPUT)
$(OBJDUMP) -d $< >$@
 
%.o: %.S
$(CC) $(DEFS) $(AFLAGS) $(CFLAGS) -D__ASM__ -c $< -o $@
 
%.o: %.s
$(AS) $(AFLAGS) $< -o $@
 
%.o: %.c
$(CC) $(DEFS) $(CFLAGS) -c $< -o $@
/branches/network/uspace/srv/fs/fat/fat_idx.c
0,0 → 1,467
/*
* 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 fs
* @{
*/
 
/**
* @file fat_idx.c
* @brief Layer for translating FAT entities to VFS node indices.
*/
 
#include "fat.h"
#include "../../vfs/vfs.h"
#include <errno.h>
#include <string.h>
#include <libadt/hash_table.h>
#include <libadt/list.h>
#include <assert.h>
#include <futex.h>
 
/** Each instance of this type describes one interval of freed VFS indices. */
typedef struct {
link_t link;
fs_index_t first;
fs_index_t last;
} freed_t;
 
/**
* Each instance of this type describes state of all VFS indices that
* are currently unused.
*/
typedef struct {
link_t link;
dev_handle_t dev_handle;
 
/** Next unassigned index. */
fs_index_t next;
/** Number of remaining unassigned indices. */
uint64_t remaining;
 
/** Sorted list of intervals of freed indices. */
link_t freed_head;
} unused_t;
 
/** Futex protecting the list of unused structures. */
static futex_t unused_futex = FUTEX_INITIALIZER;
 
/** List of unused structures. */
static LIST_INITIALIZE(unused_head);
 
static void unused_initialize(unused_t *u, dev_handle_t dev_handle)
{
link_initialize(&u->link);
u->dev_handle = dev_handle;
u->next = 0;
u->remaining = ((uint64_t)((fs_index_t)-1)) + 1;
list_initialize(&u->freed_head);
}
 
static unused_t *unused_find(dev_handle_t dev_handle, bool lock)
{
unused_t *u;
link_t *l;
 
if (lock)
futex_down(&unused_futex);
for (l = unused_head.next; l != &unused_head; l = l->next) {
u = list_get_instance(l, unused_t, link);
if (u->dev_handle == dev_handle)
return u;
}
if (lock)
futex_up(&unused_futex);
return NULL;
}
 
/** Futex protecting the up_hash and ui_hash. */
static futex_t used_futex = FUTEX_INITIALIZER;
 
/**
* Global hash table of all used fat_idx_t structures.
* The index structures are hashed by the dev_handle, parent node's first
* cluster and index within the parent directory.
*/
static hash_table_t up_hash;
 
#define UPH_BUCKETS_LOG 12
#define UPH_BUCKETS (1 << UPH_BUCKETS_LOG)
 
#define UPH_DH_KEY 0
#define UPH_PFC_KEY 1
#define UPH_PDI_KEY 2
 
static hash_index_t pos_hash(unsigned long key[])
{
dev_handle_t dev_handle = (dev_handle_t)key[UPH_DH_KEY];
fat_cluster_t pfc = (fat_cluster_t)key[UPH_PFC_KEY];
unsigned pdi = (unsigned)key[UPH_PDI_KEY];
 
hash_index_t h;
 
/*
* The least significant half of all bits are the least significant bits
* of the parent node's first cluster.
*
* The least significant half of the most significant half of all bits
* are the least significant bits of the node's dentry index within the
* parent directory node.
*
* The most significant half of the most significant half of all bits
* are the least significant bits of the device handle.
*/
h = pfc & ((1 << (UPH_BUCKETS_LOG / 2)) - 1);
h |= (pdi & ((1 << (UPH_BUCKETS_LOG / 4)) - 1)) <<
(UPH_BUCKETS_LOG / 2);
h |= (dev_handle & ((1 << (UPH_BUCKETS_LOG / 4)) - 1)) <<
(3 * (UPH_BUCKETS_LOG / 4));
 
return h;
}
 
static int pos_compare(unsigned long key[], hash_count_t keys, link_t *item)
{
dev_handle_t dev_handle = (dev_handle_t)key[UPH_DH_KEY];
fat_cluster_t pfc = (fat_cluster_t)key[UPH_PFC_KEY];
unsigned pdi = (unsigned)key[UPH_PDI_KEY];
fat_idx_t *fidx = list_get_instance(item, fat_idx_t, uph_link);
 
return (dev_handle == fidx->dev_handle) && (pfc == fidx->pfc) &&
(pdi == fidx->pdi);
}
 
static void pos_remove_callback(link_t *item)
{
/* nothing to do */
}
 
static hash_table_operations_t uph_ops = {
.hash = pos_hash,
.compare = pos_compare,
.remove_callback = pos_remove_callback,
};
 
/**
* Global hash table of all used fat_idx_t structures.
* The index structures are hashed by the dev_handle and index.
*/
static hash_table_t ui_hash;
 
#define UIH_BUCKETS_LOG 12
#define UIH_BUCKETS (1 << UIH_BUCKETS_LOG)
 
#define UIH_DH_KEY 0
#define UIH_INDEX_KEY 1
 
static hash_index_t idx_hash(unsigned long key[])
{
dev_handle_t dev_handle = (dev_handle_t)key[UIH_DH_KEY];
fs_index_t index = (fs_index_t)key[UIH_INDEX_KEY];
 
hash_index_t h;
 
h = dev_handle & ((1 << (UIH_BUCKETS_LOG / 2)) - 1);
h |= (index & ((1 << (UIH_BUCKETS_LOG / 2)) - 1)) <<
(UIH_BUCKETS_LOG / 2);
 
return h;
}
 
static int idx_compare(unsigned long key[], hash_count_t keys, link_t *item)
{
dev_handle_t dev_handle = (dev_handle_t)key[UIH_DH_KEY];
fs_index_t index = (fs_index_t)key[UIH_INDEX_KEY];
fat_idx_t *fidx = list_get_instance(item, fat_idx_t, uih_link);
 
return (dev_handle == fidx->dev_handle) && (index == fidx->index);
}
 
static void idx_remove_callback(link_t *item)
{
/* nothing to do */
}
 
static hash_table_operations_t uih_ops = {
.hash = idx_hash,
.compare = idx_compare,
.remove_callback = idx_remove_callback,
};
 
/** Allocate a VFS index which is not currently in use. */
static bool fat_idx_alloc(dev_handle_t dev_handle, fs_index_t *index)
{
unused_t *u;
assert(index);
u = unused_find(dev_handle, true);
if (!u)
return false;
 
if (list_empty(&u->freed_head)) {
if (u->remaining) {
/*
* There are no freed indices, allocate one directly
* from the counter.
*/
*index = u->next++;
--u->remaining;
futex_up(&unused_futex);
return true;
}
} else {
/* There are some freed indices which we can reuse. */
freed_t *f = list_get_instance(u->freed_head.next, freed_t,
link);
*index = f->first;
if (f->first++ == f->last) {
/* Destroy the interval. */
list_remove(&f->link);
free(f);
}
futex_up(&unused_futex);
return true;
}
/*
* We ran out of indices, which is extremely unlikely with FAT16, but
* theoretically still possible (e.g. too many open unlinked nodes or
* too many zero-sized nodes).
*/
futex_up(&unused_futex);
return false;
}
 
/** If possible, coalesce two intervals of freed indices. */
static void try_coalesce_intervals(link_t *l, link_t *r, link_t *cur)
{
freed_t *fl = list_get_instance(l, freed_t, link);
freed_t *fr = list_get_instance(r, freed_t, link);
 
if (fl->last + 1 == fr->first) {
if (cur == l) {
fl->last = fr->last;
list_remove(r);
free(r);
} else {
fr->first = fl->first;
list_remove(l);
free(l);
}
}
}
 
/** Free a VFS index, which is no longer in use. */
static void fat_idx_free(dev_handle_t dev_handle, fs_index_t index)
{
unused_t *u;
 
u = unused_find(dev_handle, true);
assert(u);
 
if (u->next == index + 1) {
/* The index can be returned directly to the counter. */
u->next--;
u->remaining++;
} else {
/*
* The index must be returned either to an existing freed
* interval or a new interval must be created.
*/
link_t *lnk;
freed_t *n;
for (lnk = u->freed_head.next; lnk != &u->freed_head;
lnk = lnk->next) {
freed_t *f = list_get_instance(lnk, freed_t, link);
if (f->first == index + 1) {
f->first--;
if (lnk->prev != &u->freed_head)
try_coalesce_intervals(lnk->prev, lnk,
lnk);
futex_up(&unused_futex);
return;
}
if (f->last == index - 1) {
f->last++;
if (lnk->next != &u->freed_head)
try_coalesce_intervals(lnk, lnk->next,
lnk);
futex_up(&unused_futex);
return;
}
if (index > f->first) {
n = malloc(sizeof(freed_t));
/* TODO: sleep until allocation succeeds */
assert(n);
link_initialize(&n->link);
n->first = index;
n->last = index;
list_insert_before(&n->link, lnk);
futex_up(&unused_futex);
return;
}
 
}
/* The index will form the last interval. */
n = malloc(sizeof(freed_t));
/* TODO: sleep until allocation succeeds */
assert(n);
link_initialize(&n->link);
n->first = index;
n->last = index;
list_append(&n->link, &u->freed_head);
}
futex_up(&unused_futex);
}
 
fat_idx_t *
fat_idx_get_by_pos(dev_handle_t dev_handle, fat_cluster_t pfc, unsigned pdi)
{
fat_idx_t *fidx;
link_t *l;
unsigned long pkey[] = {
[UPH_DH_KEY] = dev_handle,
[UPH_PFC_KEY] = pfc,
[UPH_PDI_KEY] = pdi,
};
 
futex_down(&used_futex);
l = hash_table_find(&up_hash, pkey);
if (l) {
fidx = hash_table_get_instance(l, fat_idx_t, uph_link);
} else {
fidx = (fat_idx_t *) malloc(sizeof(fat_idx_t));
if (!fidx) {
futex_up(&used_futex);
return NULL;
}
if (!fat_idx_alloc(dev_handle, &fidx->index)) {
free(fidx);
futex_up(&used_futex);
return NULL;
}
unsigned long ikey[] = {
[UIH_DH_KEY] = dev_handle,
[UIH_INDEX_KEY] = fidx->index,
};
link_initialize(&fidx->uph_link);
link_initialize(&fidx->uih_link);
futex_initialize(&fidx->lock, 1);
fidx->dev_handle = dev_handle;
fidx->pfc = pfc;
fidx->pdi = pdi;
fidx->nodep = NULL;
 
hash_table_insert(&up_hash, pkey, &fidx->uph_link);
hash_table_insert(&ui_hash, ikey, &fidx->uih_link);
}
futex_down(&fidx->lock);
futex_up(&used_futex);
 
return fidx;
}
 
fat_idx_t *
fat_idx_get_by_index(dev_handle_t dev_handle, fs_index_t index)
{
fat_idx_t *fidx = NULL;
link_t *l;
unsigned long ikey[] = {
[UIH_DH_KEY] = dev_handle,
[UIH_INDEX_KEY] = index,
};
 
futex_down(&used_futex);
l = hash_table_find(&ui_hash, ikey);
if (l) {
fidx = hash_table_get_instance(l, fat_idx_t, uih_link);
futex_down(&fidx->lock);
}
futex_up(&used_futex);
 
return fidx;
}
 
int fat_idx_init(void)
{
if (!hash_table_create(&up_hash, UPH_BUCKETS, 3, &uph_ops))
return ENOMEM;
if (!hash_table_create(&ui_hash, UIH_BUCKETS, 2, &uih_ops)) {
hash_table_destroy(&up_hash);
return ENOMEM;
}
return EOK;
}
 
void fat_idx_fini(void)
{
/* We assume the hash tables are empty. */
hash_table_destroy(&up_hash);
hash_table_destroy(&ui_hash);
}
 
int fat_idx_init_by_dev_handle(dev_handle_t dev_handle)
{
unused_t *u;
int rc = EOK;
 
u = (unused_t *) malloc(sizeof(unused_t));
if (!u)
return ENOMEM;
unused_initialize(u, dev_handle);
futex_down(&unused_futex);
if (!unused_find(dev_handle, false))
list_append(&u->link, &unused_head);
else
rc = EEXIST;
futex_up(&unused_futex);
return rc;
}
 
void fat_idx_fini_by_dev_handle(dev_handle_t dev_handle)
{
unused_t *u;
 
u = unused_find(dev_handle, true);
assert(u);
list_remove(&u->link);
futex_up(&unused_futex);
 
while (!list_empty(&u->freed_head)) {
freed_t *f;
f = list_get_instance(u->freed_head.next, freed_t, link);
list_remove(&f->link);
free(f);
}
free(u);
}
 
/**
* @}
*/