Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev HEAD → Rev 2953

/trunk/uspace/srv/fs/fat/fat_fat.c
File deleted
/trunk/uspace/srv/fs/fat/fat_dentry.c
File deleted
/trunk/uspace/srv/fs/fat/fat_dentry.h
File deleted
/trunk/uspace/srv/fs/fat/fat_fat.h
File deleted
/trunk/uspace/srv/fs/fat/fat_ops.c
36,38 → 36,153
*/
 
#include "fat.h"
#include "fat_dentry.h"
#include "fat_fat.h"
#include "../../vfs/vfs.h"
#include <libfs.h>
#include <libblock.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 <adt/hash_table.h>
#include <adt/list.h>
#include <libadt/hash_table.h>
#include <libadt/list.h>
#include <assert.h>
#include <fibril_sync.h>
#include <sys/mman.h>
#include <align.h>
#include <futex.h>
 
#define FAT_NODE(node) ((node) ? (fat_node_t *) (node)->data : NULL)
#define FS_NODE(node) ((node) ? (node)->bp : NULL)
#define BS_BLOCK 0
 
/** Mutex protecting the list of cached free FAT nodes. */
static FIBRIL_MUTEX_INITIALIZE(ffn_mutex);
/** 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
 
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) {
buf++;
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];
}
}
 
/* TODO move somewhere else */
typedef struct {
void *data;
} block_t;
 
static block_t *block_get(dev_handle_t dev_handle, off_t offset)
{
return NULL; /* TODO */
}
 
static void block_put(block_t *block)
{
/* TODO */
}
 
#define FAT_BS(b) ((fat_bs_t *)((b)->data))
 
#define FAT_CLST_RES0 0x0000
#define FAT_CLST_RES1 0x0001 /* internally used to mark root directory */
#define FAT_CLST_FIRST 0x0002
#define FAT_CLST_BAD 0xfff7
#define FAT_CLST_LAST1 0xfff8
#define FAT_CLST_LAST8 0xffff
 
#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);
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_RES1) {
/* root directory special case */
assert(offset < rds);
b = block_get(dev_handle, rscnt + fatcnt * sf + offset);
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);
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);
 
return b;
}
 
static void fat_node_initialize(fat_node_t *node)
{
fibril_mutex_initialize(&node->lock);
node->bp = NULL;
futex_initialize(&node->lock, 1);
node->idx = NULL;
node->type = 0;
link_initialize(&node->ffn_link);
77,83 → 192,52
node->dirty = false;
}
 
static void fat_node_sync(fat_node_t *node)
static uint16_t fat_bps_get(dev_handle_t dev_handle)
{
block_t *b;
fat_bs_t *bs;
fat_dentry_t *d;
block_t *bb;
uint16_t bps;
unsigned dps;
assert(node->dirty);
bb = block_get(dev_handle, BS_BLOCK);
assert(bb != NULL);
bps = uint16_t_le2host(FAT_BS(bb)->bps);
block_put(bb);
 
bs = block_bb_get(node->idx->dev_handle);
bps = uint16_t_le2host(bs->bps);
dps = bps / sizeof(fat_dentry_t);
/* Read the block that contains the dentry of interest. */
b = _fat_block_get(bs, node->idx->dev_handle, node->idx->pfc,
(node->idx->pdi * sizeof(fat_dentry_t)) / bps, BLOCK_FLAGS_NONE);
return bps;
}
 
d = ((fat_dentry_t *)b->data) + (node->idx->pdi % dps);
typedef enum {
FAT_DENTRY_SKIP,
FAT_DENTRY_LAST,
FAT_DENTRY_VALID
} fat_dentry_clsf_t;
 
d->firstc = host2uint16_t_le(node->firstc);
if (node->type == FAT_FILE) {
d->size = host2uint32_t_le(node->size);
} else if (node->type == FAT_DIRECTORY) {
d->attr = FAT_ATTR_SUBDIR;
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;
}
/* TODO: update other fields? (e.g time fields) */
b->dirty = true; /* need to sync block */
block_put(b);
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 fat_node_t *fat_node_get_new(void)
static void fat_node_sync(fat_node_t *node)
{
fs_node_t *fn;
fat_node_t *nodep;
 
fibril_mutex_lock(&ffn_mutex);
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 (!fibril_mutex_trylock(&nodep->lock))
goto skip_cache;
idxp_tmp = nodep->idx;
if (!fibril_mutex_trylock(&idxp_tmp->lock)) {
fibril_mutex_unlock(&nodep->lock);
goto skip_cache;
}
list_remove(&nodep->ffn_link);
fibril_mutex_unlock(&ffn_mutex);
if (nodep->dirty)
fat_node_sync(nodep);
idxp_tmp->nodep = NULL;
fibril_mutex_unlock(&nodep->lock);
fibril_mutex_unlock(&idxp_tmp->lock);
fn = FS_NODE(nodep);
} else {
skip_cache:
/* Try to allocate a new node structure. */
fibril_mutex_unlock(&ffn_mutex);
fn = (fs_node_t *)malloc(sizeof(fs_node_t));
if (!fn)
return NULL;
nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
if (!nodep) {
free(fn);
return NULL;
}
}
fat_node_initialize(nodep);
fs_node_initialize(fn);
fn->data = nodep;
nodep->bp = fn;
return nodep;
/* TODO */
}
 
/** Internal version of fat_node_get().
160,14 → 244,12
*
* @param idxp Locked index structure.
*/
static fat_node_t *fat_node_get_core(fat_idx_t *idxp)
static void *fat_node_get_core(fat_idx_t *idxp)
{
block_t *b;
fat_bs_t *bs;
fat_dentry_t *d;
fat_node_t *nodep = NULL;
fat_node_t *nodep;
unsigned bps;
unsigned spc;
unsigned dps;
 
if (idxp->nodep) {
175,10 → 257,10
* We are lucky.
* The node is already instantiated in memory.
*/
fibril_mutex_lock(&idxp->nodep->lock);
futex_down(&idxp->nodep->lock);
if (!idxp->nodep->refcnt++)
list_remove(&idxp->nodep->ffn_link);
fibril_mutex_unlock(&idxp->nodep->lock);
list_remove(&nodep->ffn_link);
futex_up(&idxp->nodep->lock);
return idxp->nodep;
}
 
188,18 → 270,41
assert(idxp->pfc);
 
nodep = fat_node_get_new();
if (!nodep)
return NULL;
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);
 
bs = block_bb_get(idxp->dev_handle);
bps = uint16_t_le2host(bs->bps);
spc = bs->spc;
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(bs, idxp->dev_handle, idxp->pfc,
(idxp->pdi * sizeof(fat_dentry_t)) / bps, BLOCK_FLAGS_NONE);
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);
210,18 → 315,11
* 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 * spc * fat_clusters_get(bs, 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->size = uint32_t_le2host(d->size);
nodep->lnkcnt = 1;
nodep->refcnt = 1;
 
234,33 → 332,10
return nodep;
}
 
/*
* Forward declarations of FAT libfs operations.
*/
static fs_node_t *fat_node_get(dev_handle_t, fs_index_t);
static void fat_node_put(fs_node_t *);
static fs_node_t *fat_create_node(dev_handle_t, int);
static int fat_destroy_node(fs_node_t *);
static int fat_link(fs_node_t *, fs_node_t *, const char *);
static int fat_unlink(fs_node_t *, fs_node_t *, const char *);
static fs_node_t *fat_match(fs_node_t *, const char *);
static fs_index_t fat_index_get(fs_node_t *);
static size_t fat_size_get(fs_node_t *);
static unsigned fat_lnkcnt_get(fs_node_t *);
static bool fat_has_children(fs_node_t *);
static fs_node_t *fat_root_get(dev_handle_t);
static char fat_plb_get_char(unsigned);
static bool fat_is_directory(fs_node_t *);
static bool fat_is_file(fs_node_t *node);
 
/*
* FAT libfs operations.
*/
 
/** Instantiate a FAT in-core node. */
fs_node_t *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
static void *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
{
fat_node_t *nodep;
void *node;
fat_idx_t *idxp;
 
idxp = fat_idx_get_by_index(dev_handle, index);
267,333 → 342,47
if (!idxp)
return NULL;
/* idxp->lock held */
nodep = fat_node_get_core(idxp);
fibril_mutex_unlock(&idxp->lock);
return FS_NODE(nodep);
node = fat_node_get_core(idxp);
futex_up(&idxp->lock);
return node;
}
 
void fat_node_put(fs_node_t *fn)
static void fat_node_put(void *node)
{
fat_node_t *nodep = FAT_NODE(fn);
bool destroy = false;
fat_node_t *nodep = (fat_node_t *)node;
 
fibril_mutex_lock(&nodep->lock);
futex_down(&nodep->lock);
if (!--nodep->refcnt) {
if (nodep->idx) {
fibril_mutex_lock(&ffn_mutex);
list_append(&nodep->ffn_link, &ffn_head);
fibril_mutex_unlock(&ffn_mutex);
} else {
/*
* The node does not have any index structure associated
* with itself. This can only mean that we are releasing
* the node after a failed attempt to allocate the index
* structure for it.
*/
destroy = true;
}
futex_down(&ffn_futex);
list_append(&nodep->ffn_link, &ffn_head);
futex_up(&ffn_futex);
}
fibril_mutex_unlock(&nodep->lock);
if (destroy) {
free(nodep->bp);
free(nodep);
}
futex_up(&nodep->lock);
}
 
fs_node_t *fat_create_node(dev_handle_t dev_handle, int flags)
static void *fat_create(int flags)
{
fat_idx_t *idxp;
fat_node_t *nodep;
fat_bs_t *bs;
fat_cluster_t mcl, lcl;
uint16_t bps;
int rc;
 
bs = block_bb_get(dev_handle);
bps = uint16_t_le2host(bs->bps);
if (flags & L_DIRECTORY) {
/* allocate a cluster */
rc = fat_alloc_clusters(bs, dev_handle, 1, &mcl, &lcl);
if (rc != EOK)
return NULL;
}
 
nodep = fat_node_get_new();
if (!nodep) {
fat_free_clusters(bs, dev_handle, mcl);
return NULL;
}
idxp = fat_idx_get_new(dev_handle);
if (!idxp) {
fat_free_clusters(bs, dev_handle, mcl);
fat_node_put(FS_NODE(nodep));
return NULL;
}
/* idxp->lock held */
if (flags & L_DIRECTORY) {
int i;
block_t *b;
 
/*
* Populate the new cluster with unused dentries.
*/
for (i = 0; i < bs->spc; i++) {
b = _fat_block_get(bs, dev_handle, mcl, i,
BLOCK_FLAGS_NOREAD);
/* mark all dentries as never-used */
memset(b->data, 0, bps);
b->dirty = false;
block_put(b);
}
nodep->type = FAT_DIRECTORY;
nodep->firstc = mcl;
nodep->size = bps * bs->spc;
} else {
nodep->type = FAT_FILE;
nodep->firstc = FAT_CLST_RES0;
nodep->size = 0;
}
nodep->lnkcnt = 0; /* not linked anywhere */
nodep->refcnt = 1;
nodep->dirty = true;
 
nodep->idx = idxp;
idxp->nodep = nodep;
 
fibril_mutex_unlock(&idxp->lock);
return FS_NODE(nodep);
return NULL; /* not supported at the moment */
}
 
int fat_destroy_node(fs_node_t *fn)
static int fat_destroy(void *node)
{
fat_node_t *nodep = FAT_NODE(fn);
fat_bs_t *bs;
 
/*
* The node is not reachable from the file system. This means that the
* link count should be zero and that the index structure cannot be
* found in the position hash. Obviously, we don't need to lock the node
* nor its index structure.
*/
assert(nodep->lnkcnt == 0);
 
/*
* The node may not have any children.
*/
assert(fat_has_children(fn) == false);
 
bs = block_bb_get(nodep->idx->dev_handle);
if (nodep->firstc != FAT_CLST_RES0) {
assert(nodep->size);
/* Free all clusters allocated to the node. */
fat_free_clusters(bs, nodep->idx->dev_handle, nodep->firstc);
}
 
fat_idx_destroy(nodep->idx);
free(nodep->bp);
free(nodep);
return EOK;
return ENOTSUP; /* not supported at the moment */
}
 
int fat_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
static bool fat_link(void *prnt, void *chld, const char *name)
{
fat_node_t *parentp = FAT_NODE(pfn);
fat_node_t *childp = FAT_NODE(cfn);
fat_dentry_t *d;
fat_bs_t *bs;
block_t *b;
unsigned i, j;
uint16_t bps;
unsigned dps;
unsigned blocks;
fat_cluster_t mcl, lcl;
int rc;
 
fibril_mutex_lock(&childp->lock);
if (childp->lnkcnt == 1) {
/*
* On FAT, we don't support multiple hard links.
*/
fibril_mutex_unlock(&childp->lock);
return EMLINK;
}
assert(childp->lnkcnt == 0);
fibril_mutex_unlock(&childp->lock);
 
if (!fat_dentry_name_verify(name)) {
/*
* Attempt to create unsupported name.
*/
return ENOTSUP;
}
 
/*
* Get us an unused parent node's dentry or grow the parent and allocate
* a new one.
*/
fibril_mutex_lock(&parentp->idx->lock);
bs = block_bb_get(parentp->idx->dev_handle);
bps = uint16_t_le2host(bs->bps);
dps = bps / sizeof(fat_dentry_t);
 
blocks = parentp->size / bps;
 
for (i = 0; i < blocks; i++) {
b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NONE);
for (j = 0; j < dps; j++) {
d = ((fat_dentry_t *)b->data) + j;
switch (fat_classify_dentry(d)) {
case FAT_DENTRY_SKIP:
case FAT_DENTRY_VALID:
/* skipping used and meta entries */
continue;
case FAT_DENTRY_FREE:
case FAT_DENTRY_LAST:
/* found an empty slot */
goto hit;
}
}
block_put(b);
}
j = 0;
/*
* We need to grow the parent in order to create a new unused dentry.
*/
if (parentp->idx->pfc == FAT_CLST_ROOT) {
/* Can't grow the root directory. */
fibril_mutex_unlock(&parentp->idx->lock);
return ENOSPC;
}
rc = fat_alloc_clusters(bs, parentp->idx->dev_handle, 1, &mcl, &lcl);
if (rc != EOK) {
fibril_mutex_unlock(&parentp->idx->lock);
return rc;
}
fat_append_clusters(bs, parentp, mcl);
b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NOREAD);
d = (fat_dentry_t *)b->data;
/*
* Clear all dentries in the block except for the first one (the first
* dentry will be cleared in the next step).
*/
memset(d + 1, 0, bps - sizeof(fat_dentry_t));
 
hit:
/*
* At this point we only establish the link between the parent and the
* child. The dentry, except of the name and the extension, will remain
* uninitialized until the corresponding node is synced. Thus the valid
* dentry data is kept in the child node structure.
*/
memset(d, 0, sizeof(fat_dentry_t));
fat_dentry_name_set(d, name);
b->dirty = true; /* need to sync block */
block_put(b);
fibril_mutex_unlock(&parentp->idx->lock);
 
fibril_mutex_lock(&childp->idx->lock);
/*
* If possible, create the Sub-directory Identifier Entry and the
* Sub-directory Parent Pointer Entry (i.e. "." and ".."). These entries
* are not mandatory according to Standard ECMA-107 and HelenOS VFS does
* not use them anyway, so this is rather a sign of our good will.
*/
b = fat_block_get(bs, childp, 0, BLOCK_FLAGS_NONE);
d = (fat_dentry_t *)b->data;
if (fat_classify_dentry(d) == FAT_DENTRY_LAST ||
str_cmp(d->name, FAT_NAME_DOT) == 0) {
memset(d, 0, sizeof(fat_dentry_t));
str_cpy(d->name, 8, FAT_NAME_DOT);
str_cpy(d->ext, 3, FAT_EXT_PAD);
d->attr = FAT_ATTR_SUBDIR;
d->firstc = host2uint16_t_le(childp->firstc);
/* TODO: initialize also the date/time members. */
}
d++;
if (fat_classify_dentry(d) == FAT_DENTRY_LAST ||
str_cmp(d->name, FAT_NAME_DOT_DOT) == 0) {
memset(d, 0, sizeof(fat_dentry_t));
str_cpy(d->name, 8, FAT_NAME_DOT_DOT);
str_cpy(d->ext, 3, FAT_EXT_PAD);
d->attr = FAT_ATTR_SUBDIR;
d->firstc = (parentp->firstc == FAT_CLST_ROOT) ?
host2uint16_t_le(FAT_CLST_RES0) :
host2uint16_t_le(parentp->firstc);
/* TODO: initialize also the date/time members. */
}
b->dirty = true; /* need to sync block */
block_put(b);
 
childp->idx->pfc = parentp->firstc;
childp->idx->pdi = i * dps + j;
fibril_mutex_unlock(&childp->idx->lock);
 
fibril_mutex_lock(&childp->lock);
childp->lnkcnt = 1;
childp->dirty = true; /* need to sync node */
fibril_mutex_unlock(&childp->lock);
 
/*
* Hash in the index structure into the position hash.
*/
fat_idx_hashin(childp->idx);
 
return EOK;
return false; /* not supported at the moment */
}
 
int fat_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
static int fat_unlink(void *prnt, void *chld)
{
fat_node_t *parentp = FAT_NODE(pfn);
fat_node_t *childp = FAT_NODE(cfn);
fat_bs_t *bs;
fat_dentry_t *d;
uint16_t bps;
block_t *b;
 
if (!parentp)
return EBUSY;
if (fat_has_children(cfn))
return ENOTEMPTY;
 
fibril_mutex_lock(&parentp->lock);
fibril_mutex_lock(&childp->lock);
assert(childp->lnkcnt == 1);
fibril_mutex_lock(&childp->idx->lock);
bs = block_bb_get(childp->idx->dev_handle);
bps = uint16_t_le2host(bs->bps);
 
b = _fat_block_get(bs, childp->idx->dev_handle, childp->idx->pfc,
(childp->idx->pdi * sizeof(fat_dentry_t)) / bps,
BLOCK_FLAGS_NONE);
d = (fat_dentry_t *)b->data +
(childp->idx->pdi % (bps / sizeof(fat_dentry_t)));
/* mark the dentry as not-currently-used */
d->name[0] = FAT_DENTRY_ERASED;
b->dirty = true; /* need to sync block */
block_put(b);
 
/* remove the index structure from the position hash */
fat_idx_hashout(childp->idx);
/* clear position information */
childp->idx->pfc = FAT_CLST_RES0;
childp->idx->pdi = 0;
fibril_mutex_unlock(&childp->idx->lock);
childp->lnkcnt = 0;
childp->dirty = true;
fibril_mutex_unlock(&childp->lock);
fibril_mutex_unlock(&parentp->lock);
 
return EOK;
return ENOTSUP; /* not supported at the moment */
}
 
fs_node_t *fat_match(fs_node_t *pfn, const char *component)
static void *fat_match(void *prnt, const char *component)
{
fat_bs_t *bs;
fat_node_t *parentp = FAT_NODE(pfn);
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 */
602,31 → 391,34
fat_dentry_t *d;
block_t *b;
 
fibril_mutex_lock(&parentp->idx->lock);
bs = block_bb_get(parentp->idx->dev_handle);
bps = uint16_t_le2host(bs->bps);
futex_down(&parentp->idx->lock);
bps = fat_bps_get(parentp->idx->dev_handle);
dps = bps / sizeof(fat_dentry_t);
blocks = parentp->size / bps;
blocks = parentp->size / bps + (parentp->size % bps != 0);
for (i = 0; i < blocks; i++) {
b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NONE);
for (j = 0; j < dps; j++) {
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:
case FAT_DENTRY_FREE:
continue;
case FAT_DENTRY_LAST:
block_put(b);
fibril_mutex_unlock(&parentp->idx->lock);
futex_up(&parentp->idx->lock);
return NULL;
default:
case FAT_DENTRY_VALID:
fat_dentry_name_get(d, name);
dentry_name_canonify(d, name);
break;
}
if (fat_dentry_namecmp(name, component) == 0) {
if (strcmp(name, component) == 0) {
/* hit */
fat_node_t *nodep;
void *node;
/*
* Assume tree hierarchy for locking. We
* already have the parent and now we are going
636,7 → 428,7
fat_idx_t *idx = fat_idx_get_by_pos(
parentp->idx->dev_handle, parentp->firstc,
i * dps + j);
fibril_mutex_unlock(&parentp->idx->lock);
futex_up(&parentp->idx->lock);
if (!idx) {
/*
* Can happen if memory is low or if we
645,38 → 437,39
block_put(b);
return NULL;
}
nodep = fat_node_get_core(idx);
fibril_mutex_unlock(&idx->lock);
node = fat_node_get_core(idx);
futex_up(&idx->lock);
block_put(b);
return FS_NODE(nodep);
return node;
}
}
block_put(b);
}
 
fibril_mutex_unlock(&parentp->idx->lock);
futex_up(&parentp->idx->lock);
return NULL;
}
 
fs_index_t fat_index_get(fs_node_t *fn)
static fs_index_t fat_index_get(void *node)
{
return FAT_NODE(fn)->idx->index;
fat_node_t *fnodep = (fat_node_t *)node;
if (!fnodep)
return 0;
return fnodep->idx->index;
}
 
size_t fat_size_get(fs_node_t *fn)
static size_t fat_size_get(void *node)
{
return FAT_NODE(fn)->size;
return ((fat_node_t *)node)->size;
}
 
unsigned fat_lnkcnt_get(fs_node_t *fn)
static unsigned fat_lnkcnt_get(void *node)
{
return FAT_NODE(fn)->lnkcnt;
return ((fat_node_t *)node)->lnkcnt;
}
 
bool fat_has_children(fs_node_t *fn)
static bool fat_has_children(void *node)
{
fat_bs_t *bs;
fat_node_t *nodep = FAT_NODE(fn);
fat_node_t *nodep = (fat_node_t *)node;
unsigned bps;
unsigned dps;
unsigned blocks;
685,63 → 478,65
 
if (nodep->type != FAT_DIRECTORY)
return false;
fibril_mutex_lock(&nodep->idx->lock);
bs = block_bb_get(nodep->idx->dev_handle);
bps = uint16_t_le2host(bs->bps);
 
futex_down(&nodep->idx->lock);
bps = fat_bps_get(nodep->idx->dev_handle);
dps = bps / sizeof(fat_dentry_t);
 
blocks = nodep->size / bps;
blocks = nodep->size / bps + (nodep->size % bps != 0);
 
for (i = 0; i < blocks; i++) {
unsigned dentries;
fat_dentry_t *d;
b = fat_block_get(bs, nodep, i, BLOCK_FLAGS_NONE);
for (j = 0; j < dps; j++) {
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:
case FAT_DENTRY_FREE:
continue;
case FAT_DENTRY_LAST:
block_put(b);
fibril_mutex_unlock(&nodep->idx->lock);
futex_up(&nodep->idx->lock);
return false;
default:
case FAT_DENTRY_VALID:
block_put(b);
fibril_mutex_unlock(&nodep->idx->lock);
futex_up(&nodep->idx->lock);
return true;
}
block_put(b);
fibril_mutex_unlock(&nodep->idx->lock);
futex_up(&nodep->idx->lock);
return true;
}
block_put(b);
}
 
fibril_mutex_unlock(&nodep->idx->lock);
futex_up(&nodep->idx->lock);
return false;
}
 
fs_node_t *fat_root_get(dev_handle_t dev_handle)
static void *fat_root_get(dev_handle_t dev_handle)
{
return fat_node_get(dev_handle, 0);
return NULL; /* TODO */
}
 
char fat_plb_get_char(unsigned pos)
static char fat_plb_get_char(unsigned pos)
{
return fat_reg.plb_ro[pos % PLB_SIZE];
}
 
bool fat_is_directory(fs_node_t *fn)
static bool fat_is_directory(void *node)
{
return FAT_NODE(fn)->type == FAT_DIRECTORY;
return ((fat_node_t *)node)->type == FAT_DIRECTORY;
}
 
bool fat_is_file(fs_node_t *fn)
static bool fat_is_file(void *node)
{
return FAT_NODE(fn)->type == FAT_FILE;
return ((fat_node_t *)node)->type == FAT_FILE;
}
 
/** libfs operations */
749,8 → 544,8
.match = fat_match,
.node_get = fat_node_get,
.node_put = fat_node_put,
.create = fat_create_node,
.destroy = fat_destroy_node,
.create = fat_create,
.destroy = fat_destroy,
.link = fat_link,
.unlink = fat_unlink,
.index_get = fat_index_get,
763,456 → 558,11
.is_file = fat_is_file
};
 
/*
* VFS operations.
*/
 
void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
{
dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
enum cache_mode cmode;
fat_bs_t *bs;
uint16_t bps;
uint16_t rde;
int rc;
 
/* accept the mount options */
ipc_callid_t callid;
size_t size;
if (!ipc_data_write_receive(&callid, &size)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
char *opts = malloc(size + 1);
if (!opts) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
return;
}
ipcarg_t retval = ipc_data_write_finalize(callid, opts, size);
if (retval != EOK) {
ipc_answer_0(rid, retval);
free(opts);
return;
}
opts[size] = '\0';
 
/* Check for option enabling write through. */
if (str_cmp(opts, "wtcache") == 0)
cmode = CACHE_MODE_WT;
else
cmode = CACHE_MODE_WB;
 
/* initialize libblock */
rc = block_init(dev_handle, BS_SIZE);
if (rc != EOK) {
ipc_answer_0(rid, rc);
return;
}
 
/* prepare the boot block */
rc = block_bb_read(dev_handle, BS_BLOCK * BS_SIZE, BS_SIZE);
if (rc != EOK) {
block_fini(dev_handle);
ipc_answer_0(rid, rc);
return;
}
 
/* get the buffer with the boot sector */
bs = block_bb_get(dev_handle);
/* Read the number of root directory entries. */
bps = uint16_t_le2host(bs->bps);
rde = uint16_t_le2host(bs->root_ent_max);
 
if (bps != BS_SIZE) {
block_fini(dev_handle);
ipc_answer_0(rid, ENOTSUP);
return;
}
 
/* Initialize the block cache */
rc = block_cache_init(dev_handle, bps, 0 /* XXX */, cmode);
if (rc != EOK) {
block_fini(dev_handle);
ipc_answer_0(rid, rc);
return;
}
 
rc = fat_idx_init_by_dev_handle(dev_handle);
if (rc != EOK) {
block_fini(dev_handle);
ipc_answer_0(rid, rc);
return;
}
 
/* Initialize the root node. */
fs_node_t *rfn = (fs_node_t *)malloc(sizeof(fs_node_t));
if (!rfn) {
block_fini(dev_handle);
fat_idx_fini_by_dev_handle(dev_handle);
ipc_answer_0(rid, ENOMEM);
return;
}
fs_node_initialize(rfn);
fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
if (!rootp) {
free(rfn);
block_fini(dev_handle);
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) {
free(rfn);
free(rootp);
block_fini(dev_handle);
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;
rootp->bp = rfn;
rfn->data = rootp;
fibril_mutex_unlock(&ridxp->lock);
 
ipc_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
}
 
void fat_mount(ipc_callid_t rid, ipc_call_t *request)
{
libfs_mount(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
}
 
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);
fs_node_t *fn = fat_node_get(dev_handle, index);
fat_node_t *nodep;
fat_bs_t *bs;
uint16_t bps;
size_t bytes;
block_t *b;
 
if (!fn) {
ipc_answer_0(rid, ENOENT);
return;
}
nodep = FAT_NODE(fn);
 
ipc_callid_t callid;
size_t len;
if (!ipc_data_read_receive(&callid, &len)) {
fat_node_put(fn);
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
 
bs = block_bb_get(dev_handle);
bps = uint16_t_le2host(bs->bps);
 
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.
*/
if (pos >= nodep->size) {
/* reading beyond the EOF */
bytes = 0;
(void) ipc_data_read_finalize(callid, NULL, 0);
} else {
bytes = min(len, bps - pos % bps);
bytes = min(bytes, nodep->size - pos);
b = fat_block_get(bs, nodep, pos / bps,
BLOCK_FLAGS_NONE);
(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(bs, nodep, bnum, BLOCK_FLAGS_NONE);
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:
case FAT_DENTRY_FREE:
continue;
case FAT_DENTRY_LAST:
block_put(b);
goto miss;
default:
case FAT_DENTRY_VALID:
fat_dentry_name_get(d, name);
block_put(b);
goto hit;
}
}
block_put(b);
bnum++;
}
miss:
fat_node_put(fn);
ipc_answer_0(callid, ENOENT);
ipc_answer_1(rid, ENOENT, 0);
return;
hit:
(void) ipc_data_read_finalize(callid, name, str_size(name) + 1);
bytes = (pos - spos) + 1;
}
 
fat_node_put(fn);
ipc_answer_1(rid, EOK, (ipcarg_t)bytes);
}
 
void fat_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);
fs_node_t *fn = fat_node_get(dev_handle, index);
fat_node_t *nodep;
fat_bs_t *bs;
size_t bytes;
block_t *b;
uint16_t bps;
unsigned spc;
unsigned bpc; /* bytes per cluster */
off_t boundary;
int flags = BLOCK_FLAGS_NONE;
if (!fn) {
ipc_answer_0(rid, ENOENT);
return;
}
nodep = FAT_NODE(fn);
ipc_callid_t callid;
size_t len;
if (!ipc_data_write_receive(&callid, &len)) {
fat_node_put(fn);
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
 
bs = block_bb_get(dev_handle);
bps = uint16_t_le2host(bs->bps);
spc = bs->spc;
bpc = bps * spc;
 
/*
* In all scenarios, we will attempt to write out only one block worth
* of data at maximum. There might be some more efficient approaches,
* but this one greatly simplifies fat_write(). Note that we can afford
* to do this because the client must be ready to handle the return
* value signalizing a smaller number of bytes written.
*/
bytes = min(len, bps - pos % bps);
if (bytes == bps)
flags |= BLOCK_FLAGS_NOREAD;
boundary = ROUND_UP(nodep->size, bpc);
if (pos < boundary) {
/*
* This is the easier case - we are either overwriting already
* existing contents or writing behind the EOF, but still within
* the limits of the last cluster. The node size may grow to the
* next block size boundary.
*/
fat_fill_gap(bs, nodep, FAT_CLST_RES0, pos);
b = fat_block_get(bs, nodep, pos / bps, flags);
(void) ipc_data_write_finalize(callid, b->data + pos % bps,
bytes);
b->dirty = true; /* need to sync block */
block_put(b);
if (pos + bytes > nodep->size) {
nodep->size = pos + bytes;
nodep->dirty = true; /* need to sync node */
}
ipc_answer_2(rid, EOK, bytes, nodep->size);
fat_node_put(fn);
return;
} else {
/*
* This is the more difficult case. We must allocate new
* clusters for the node and zero them out.
*/
int status;
unsigned nclsts;
fat_cluster_t mcl, lcl;
nclsts = (ROUND_UP(pos + bytes, bpc) - boundary) / bpc;
/* create an independent chain of nclsts clusters in all FATs */
status = fat_alloc_clusters(bs, dev_handle, nclsts, &mcl, &lcl);
if (status != EOK) {
/* could not allocate a chain of nclsts clusters */
fat_node_put(fn);
ipc_answer_0(callid, status);
ipc_answer_0(rid, status);
return;
}
/* zero fill any gaps */
fat_fill_gap(bs, nodep, mcl, pos);
b = _fat_block_get(bs, dev_handle, lcl, (pos / bps) % spc,
flags);
(void) ipc_data_write_finalize(callid, b->data + pos % bps,
bytes);
b->dirty = true; /* need to sync block */
block_put(b);
/*
* Append the cluster chain starting in mcl to the end of the
* node's cluster chain.
*/
fat_append_clusters(bs, nodep, mcl);
nodep->size = pos + bytes;
nodep->dirty = true; /* need to sync node */
ipc_answer_2(rid, EOK, bytes, nodep->size);
fat_node_put(fn);
return;
}
}
 
void fat_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);
fs_node_t *fn = fat_node_get(dev_handle, index);
fat_node_t *nodep;
fat_bs_t *bs;
uint16_t bps;
uint8_t spc;
unsigned bpc; /* bytes per cluster */
int rc;
 
if (!fn) {
ipc_answer_0(rid, ENOENT);
return;
}
nodep = FAT_NODE(fn);
 
bs = block_bb_get(dev_handle);
bps = uint16_t_le2host(bs->bps);
spc = bs->spc;
bpc = bps * spc;
 
if (nodep->size == size) {
rc = EOK;
} else if (nodep->size < size) {
/*
* The standard says we have the freedom to grow the node.
* For now, we simply return an error.
*/
rc = EINVAL;
} else if (ROUND_UP(nodep->size, bpc) == ROUND_UP(size, bpc)) {
/*
* The node will be shrunk, but no clusters will be deallocated.
*/
nodep->size = size;
nodep->dirty = true; /* need to sync node */
rc = EOK;
} else {
/*
* The node will be shrunk, clusters will be deallocated.
*/
if (size == 0) {
fat_chop_clusters(bs, nodep, FAT_CLST_RES0);
} else {
fat_cluster_t lastc;
(void) fat_cluster_walk(bs, dev_handle, nodep->firstc,
&lastc, (size - 1) / bpc);
fat_chop_clusters(bs, nodep, lastc);
}
nodep->size = size;
nodep->dirty = true; /* need to sync node */
rc = EOK;
}
fat_node_put(fn);
ipc_answer_0(rid, rc);
return;
}
 
void fat_close(ipc_callid_t rid, ipc_call_t *request)
{
ipc_answer_0(rid, EOK);
}
 
void fat_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;
 
fs_node_t *fn = fat_node_get(dev_handle, index);
if (!fn) {
ipc_answer_0(rid, ENOENT);
return;
}
 
rc = fat_destroy_node(fn);
ipc_answer_0(rid, rc);
}
 
void fat_open_node(ipc_callid_t rid, ipc_call_t *request)
{
libfs_open_node(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
}
 
void fat_stat(ipc_callid_t rid, ipc_call_t *request)
{
libfs_stat(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
}
 
void fat_sync(ipc_callid_t rid, ipc_call_t *request)
{
/* Dummy implementation */
ipc_answer_0(rid, EOK);
}
 
/**
* @}
*/
*/
/trunk/uspace/srv/fs/fat/fat_idx.c
39,10 → 39,10
#include "../../vfs/vfs.h"
#include <errno.h>
#include <string.h>
#include <adt/hash_table.h>
#include <adt/list.h>
#include <libadt/hash_table.h>
#include <libadt/list.h>
#include <assert.h>
#include <fibril_sync.h>
#include <futex.h>
 
/** Each instance of this type describes one interval of freed VFS indices. */
typedef struct {
68,41 → 68,15
link_t freed_head;
} unused_t;
 
/** Mutex protecting the list of unused structures. */
static FIBRIL_MUTEX_INITIALIZE(unused_lock);
/** 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);
}
/** Futex protecting the up_hash and ui_hash. */
static futex_t used_futex = FUTEX_INITIALIZER;
 
static unused_t *unused_find(dev_handle_t dev_handle, bool lock)
{
unused_t *u;
link_t *l;
 
if (lock)
fibril_mutex_lock(&unused_lock);
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)
fibril_mutex_unlock(&unused_lock);
return NULL;
}
 
/** Mutex protecting the up_hash and ui_hash. */
static FIBRIL_MUTEX_INITIALIZE(used_lock);
 
/**
* Global hash table of all used fat_idx_t structures.
* The index structures are hashed by the dev_handle, parent node's first
214,15 → 188,24
};
 
/** Allocate a VFS index which is not currently in use. */
static bool fat_index_alloc(dev_handle_t dev_handle, fs_index_t *index)
static bool fat_idx_alloc(dev_handle_t dev_handle, fs_index_t *index)
{
link_t *l;
unused_t *u;
assert(index);
u = unused_find(dev_handle, true);
if (!u)
return false;
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)
goto hit;
}
futex_up(&unused_futex);
/* dev_handle not found */
return false;
 
hit:
if (list_empty(&u->freed_head)) {
if (u->remaining) {
/*
231,7 → 214,7
*/
*index = u->next++;
--u->remaining;
fibril_mutex_unlock(&unused_lock);
futex_up(&unused_futex);
return true;
}
} else {
244,7 → 227,7
list_remove(&f->link);
free(f);
}
fibril_mutex_unlock(&unused_lock);
futex_up(&unused_futex);
return true;
}
/*
252,7 → 235,7
* theoretically still possible (e.g. too many open unlinked nodes or
* too many zero-sized nodes).
*/
fibril_mutex_unlock(&unused_lock);
futex_up(&unused_futex);
return false;
}
 
276,13 → 259,23
}
 
/** Free a VFS index, which is no longer in use. */
static void fat_index_free(dev_handle_t dev_handle, fs_index_t index)
static void fat_idx_free(dev_handle_t dev_handle, fs_index_t index)
{
link_t *l;
unused_t *u;
 
u = unused_find(dev_handle, true);
assert(u);
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)
goto hit;
}
futex_up(&unused_futex);
 
/* should not happen */
assert(0);
 
hit:
if (u->next == index + 1) {
/* The index can be returned directly to the counter. */
u->next--;
302,7 → 295,7
if (lnk->prev != &u->freed_head)
try_coalesce_intervals(lnk->prev, lnk,
lnk);
fibril_mutex_unlock(&unused_lock);
futex_up(&unused_futex);
return;
}
if (f->last == index - 1) {
310,7 → 303,7
if (lnk->next != &u->freed_head)
try_coalesce_intervals(lnk, lnk->next,
lnk);
fibril_mutex_unlock(&unused_lock);
futex_up(&unused_futex);
return;
}
if (index > f->first) {
321,7 → 314,7
n->first = index;
n->last = index;
list_insert_before(&n->link, lnk);
fibril_mutex_unlock(&unused_lock);
futex_up(&unused_futex);
return;
}
 
335,55 → 328,9
n->last = index;
list_append(&n->link, &u->freed_head);
}
fibril_mutex_unlock(&unused_lock);
futex_up(&unused_futex);
}
 
static fat_idx_t *fat_idx_create(dev_handle_t dev_handle)
{
fat_idx_t *fidx;
 
fidx = (fat_idx_t *) malloc(sizeof(fat_idx_t));
if (!fidx)
return NULL;
if (!fat_index_alloc(dev_handle, &fidx->index)) {
free(fidx);
return NULL;
}
link_initialize(&fidx->uph_link);
link_initialize(&fidx->uih_link);
fibril_mutex_initialize(&fidx->lock);
fidx->dev_handle = dev_handle;
fidx->pfc = FAT_CLST_RES0; /* no parent yet */
fidx->pdi = 0;
fidx->nodep = NULL;
 
return fidx;
}
 
fat_idx_t *fat_idx_get_new(dev_handle_t dev_handle)
{
fat_idx_t *fidx;
 
fibril_mutex_lock(&used_lock);
fidx = fat_idx_create(dev_handle);
if (!fidx) {
fibril_mutex_unlock(&used_lock);
return NULL;
}
unsigned long ikey[] = {
[UIH_DH_KEY] = dev_handle,
[UIH_INDEX_KEY] = fidx->index,
};
hash_table_insert(&ui_hash, ikey, &fidx->uih_link);
fibril_mutex_lock(&fidx->lock);
fibril_mutex_unlock(&used_lock);
 
return fidx;
}
 
fat_idx_t *
fat_idx_get_by_pos(dev_handle_t dev_handle, fat_cluster_t pfc, unsigned pdi)
{
395,16 → 342,21
[UPH_PDI_KEY] = pdi,
};
 
fibril_mutex_lock(&used_lock);
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_create(dev_handle);
fidx = (fat_idx_t *) malloc(sizeof(fat_idx_t));
if (!fidx) {
fibril_mutex_unlock(&used_lock);
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,
411,44 → 363,23
[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);
}
fibril_mutex_lock(&fidx->lock);
fibril_mutex_unlock(&used_lock);
futex_down(&fidx->lock);
futex_up(&used_futex);
 
return fidx;
}
 
void fat_idx_hashin(fat_idx_t *idx)
{
unsigned long pkey[] = {
[UPH_DH_KEY] = idx->dev_handle,
[UPH_PFC_KEY] = idx->pfc,
[UPH_PDI_KEY] = idx->pdi,
};
 
fibril_mutex_lock(&used_lock);
hash_table_insert(&up_hash, pkey, &idx->uph_link);
fibril_mutex_unlock(&used_lock);
}
 
void fat_idx_hashout(fat_idx_t *idx)
{
unsigned long pkey[] = {
[UPH_DH_KEY] = idx->dev_handle,
[UPH_PFC_KEY] = idx->pfc,
[UPH_PDI_KEY] = idx->pdi,
};
 
fibril_mutex_lock(&used_lock);
hash_table_remove(&up_hash, pkey, 3);
fibril_mutex_unlock(&used_lock);
}
 
fat_idx_t *
fat_idx_get_by_index(dev_handle_t dev_handle, fs_index_t index)
{
459,98 → 390,14
[UIH_INDEX_KEY] = index,
};
 
fibril_mutex_lock(&used_lock);
futex_down(&used_futex);
l = hash_table_find(&ui_hash, ikey);
if (l) {
fidx = hash_table_get_instance(l, fat_idx_t, uih_link);
fibril_mutex_lock(&fidx->lock);
futex_down(&fidx->lock);
}
fibril_mutex_unlock(&used_lock);
futex_up(&used_futex);
 
return fidx;
}
 
/** Destroy the index structure.
*
* @param idx The index structure to be destroyed.
*/
void fat_idx_destroy(fat_idx_t *idx)
{
unsigned long ikey[] = {
[UIH_DH_KEY] = idx->dev_handle,
[UIH_INDEX_KEY] = idx->index,
};
 
assert(idx->pfc == FAT_CLST_RES0);
 
fibril_mutex_lock(&used_lock);
/*
* Since we can only free unlinked nodes, the index structure is not
* present in the position hash (uph). We therefore hash it out from
* the index hash only.
*/
hash_table_remove(&ui_hash, ikey, 2);
fibril_mutex_unlock(&used_lock);
/* Release the VFS index. */
fat_index_free(idx->dev_handle, idx->index);
/* Deallocate the structure. */
free(idx);
}
 
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);
fibril_mutex_lock(&unused_lock);
if (!unused_find(dev_handle, false))
list_append(&u->link, &unused_head);
else
rc = EEXIST;
fibril_mutex_unlock(&unused_lock);
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);
fibril_mutex_unlock(&unused_lock);
 
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);
}
 
/**
* @}
*/
/trunk/uspace/srv/fs/fat/fat.h
33,9 → 33,7
#ifndef FAT_FAT_H_
#define FAT_FAT_H_
 
#include "fat_fat.h"
#include <ipc/ipc.h>
#include <fibril_sync.h>
#include <libfs.h>
#include <atomic.h>
#include <sys/types.h>
42,16 → 40,9
#include <bool.h>
#include "../../vfs/vfs.h"
 
#ifndef dprintf
#define dprintf(...) printf(__VA_ARGS__)
#endif
 
#define min(a, b) ((a) < (b) ? (a) : (b))
 
#define BS_BLOCK 0
#define BS_SIZE 512
 
typedef struct fat_bs {
typedef struct {
uint8_t ji[3]; /**< Jump instruction. */
uint8_t oem_name[8];
/* BIOS Parameter Block */
122,6 → 113,34
};
} __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,
161,7 → 180,7
/** Used indices (index) hash table link. */
link_t uih_link;
 
fibril_mutex_t lock;
futex_t lock;
dev_handle_t dev_handle;
fs_index_t index;
/**
179,10 → 198,7
 
/** FAT in-core node. */
typedef struct fat_node {
/** Back pointer to the FS node. */
fs_node_t *bp;
fibril_mutex_t lock;
futex_t lock;
fat_node_type_t type;
fat_idx_t *idx;
/**
201,31 → 217,11
 
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 void fat_write(ipc_callid_t, ipc_call_t *);
extern void fat_truncate(ipc_callid_t, ipc_call_t *);
extern void fat_stat(ipc_callid_t, ipc_call_t *);
extern void fat_close(ipc_callid_t, ipc_call_t *);
extern void fat_destroy(ipc_callid_t, ipc_call_t *);
extern void fat_open_node(ipc_callid_t, ipc_call_t *);
extern void fat_stat(ipc_callid_t, ipc_call_t *);
extern void fat_sync(ipc_callid_t, ipc_call_t *);
 
extern fat_idx_t *fat_idx_get_new(dev_handle_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 void fat_idx_destroy(fat_idx_t *);
extern void fat_idx_hashin(fat_idx_t *);
extern void fat_idx_hashout(fat_idx_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
 
/**
/trunk/uspace/srv/fs/fat/Makefile
31,17 → 31,12
 
LIBC_PREFIX = ../../../lib/libc
LIBFS_PREFIX = ../../../lib/libfs
LIBBLOCK_PREFIX = ../../../lib/libblock
SOFTINT_PREFIX = ../../../lib/softint
 
include $(LIBC_PREFIX)/Makefile.toolchain
 
CFLAGS += -I $(LIBFS_PREFIX) -I $(LIBBLOCK_PREFIX)
CFLAGS += -I $(LIBFS_PREFIX)
 
LIBS = \
$(LIBFS_PREFIX)/libfs.a \
$(LIBBLOCK_PREFIX)/libblock.a \
$(LIBC_PREFIX)/libc.a
LIBS = $(LIBC_PREFIX)/libc.a $(LIBFS_PREFIX)/libfs.a
 
## Sources
#
50,32 → 45,28
SOURCES = \
fat.c \
fat_ops.c \
fat_idx.c \
fat_dentry.c \
fat_fat.c
fat_idx.c
 
OBJECTS := $(addsuffix .o,$(basename $(SOURCES)))
 
.PHONY: all clean depend disasm
 
all: $(OUTPUT) $(OUTPUT).disasm
all: $(OUTPUT) disasm
 
-include Makefile.depend
 
clean:
-rm -f $(OUTPUT) $(OUTPUT).map $(OUTPUT).disasm Makefile.depend $(OBJECTS)
-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/$(UARCH)/_link.ld $(OBJECTS) $(LIBS) $(LFLAGS) -o $@ -Map $(OUTPUT).map
$(LD) -T $(LIBC_PREFIX)/arch/$(ARCH)/_link.ld $(OBJECTS) $(LIBS) $(LFLAGS) -o $@ -Map $(OUTPUT).map
 
disasm: $(OUTPUT).disasm
disasm:
$(OBJDUMP) -d $(OUTPUT) >$(OUTPUT).disasm
 
$(OUTPUT).disasm: $(OUTPUT)
$(OBJDUMP) -d $< > $@
 
%.o: %.S
$(CC) $(DEFS) $(AFLAGS) $(CFLAGS) -D__ASM__ -c $< -o $@
 
/trunk/uspace/srv/fs/fat/fat.c
49,6 → 49,14
 
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_UNMOUNT)] = VFS_OP_NULL,
}
};
 
fs_reg_t fat_reg;
89,41 → 97,9
callid = async_get_call(&call);
switch (IPC_GET_METHOD(call)) {
case IPC_M_PHONE_HUNGUP:
return;
case VFS_OUT_MOUNTED:
fat_mounted(callid, &call);
break;
case VFS_OUT_MOUNT:
fat_mount(callid, &call);
break;
case VFS_OUT_LOOKUP:
case VFS_LOOKUP:
fat_lookup(callid, &call);
break;
case VFS_OUT_READ:
fat_read(callid, &call);
break;
case VFS_OUT_WRITE:
fat_write(callid, &call);
break;
case VFS_OUT_TRUNCATE:
fat_truncate(callid, &call);
break;
case VFS_OUT_STAT:
fat_stat(callid, &call);
break;
case VFS_OUT_CLOSE:
fat_close(callid, &call);
break;
case VFS_OUT_DESTROY:
fat_destroy(callid, &call);
break;
case VFS_OUT_OPEN_NODE:
fat_open_node(callid, &call);
break;
case VFS_OUT_SYNC:
fat_sync(callid, &call);
break;
default:
ipc_answer_0(callid, ENOTSUP);
break;
134,24 → 110,20
int main(int argc, char **argv)
{
int vfs_phone;
int rc;
 
printf("fat: HelenOS FAT file system server.\n");
printf("FAT: HelenOS FAT file system server.\n");
 
rc = fat_idx_init();
if (rc != EOK)
goto err;
 
vfs_phone = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_VFS, 0, 0);
if (vfs_phone < EOK) {
printf("fat: failed to connect to VFS\n");
return -1;
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, &fat_reg, &fat_vfs_info, fat_connection);
if (rc != EOK) {
fat_idx_fini();
goto err;
printf("Failed to register the FAT file system (%d)\n", rc);
return rc;
}
dprintf("FAT filesystem registered, fs_handle=%d.\n",
160,10 → 132,6
async_manager();
/* not reached */
return 0;
 
err:
printf("Failed to register the FAT file system (%d)\n", rc);
return rc;
}
 
/**