Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 3402 → Rev 3403

/branches/dynload/uspace/srv/fs/fat/fat_ops.c
39,6 → 39,8
#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>
47,8 → 49,10
#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;
66,15 → 70,15
#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) {
buf++;
if (d->name[i] == FAT_PAD)
break;
}
if (d->name[i] == FAT_DENTRY_E5_ESC)
*buf++ = 0xe5;
else
92,21 → 96,55
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)
static block_t *block_get(dev_handle_t dev_handle, off_t offset, size_t bs)
{
return NULL; /* TODO */
/* 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)
{
/* TODO */
/* FIXME */
free(block->data);
free(block);
}
 
#define FAT_BS(b) ((fat_bs_t *)((b)->data))
143,7 → 181,7
fat_cluster_t clst = firstc;
unsigned i;
 
bb = block_get(dev_handle, BS_BLOCK);
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);
159,7 → 197,7
if (firstc == FAT_CLST_ROOT) {
/* root directory special case */
assert(offset < rds);
b = block_get(dev_handle, rscnt + fatcnt * sf + offset);
b = block_get(dev_handle, rscnt + fatcnt * sf + offset, bps);
return b;
}
 
172,7 → 210,7
fsec = (clst * sizeof(fat_cluster_t)) / bps;
fidx = clst % (bps / sizeof(fat_cluster_t));
/* read FAT1 */
b = block_get(dev_handle, rscnt + fsec);
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);
180,11 → 218,58
}
 
b = block_get(dev_handle, ssa + (clst - FAT_CLST_FIRST) * spc +
offset % 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);
202,7 → 287,7
block_t *bb;
uint16_t bps;
bb = block_get(dev_handle, BS_BLOCK);
bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
assert(bb != NULL);
bps = uint16_t_le2host(FAT_BS(bb)->bps);
block_put(bb);
253,7 → 338,7
{
block_t *b;
fat_dentry_t *d;
fat_node_t *nodep;
fat_node_t *nodep = NULL;
unsigned bps;
unsigned dps;
 
264,7 → 349,7
*/
futex_down(&idxp->nodep->lock);
if (!idxp->nodep->refcnt++)
list_remove(&nodep->ffn_link);
list_remove(&idxp->nodep->ffn_link);
futex_up(&idxp->nodep->lock);
return idxp->nodep;
}
320,11 → 405,18
* 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->size = uint32_t_le2host(d->size);
nodep->lnkcnt = 1;
nodep->refcnt = 1;
 
421,7 → 513,7
dentry_name_canonify(d, name);
break;
}
if (strcmp(name, component) == 0) {
if (stricmp(name, component) == 0) {
/* hit */
void *node;
/*
567,16 → 659,56
{
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);
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;
}
584,6 → 716,7
/* 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;
592,6 → 725,7
 
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);
603,6 → 737,7
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;
609,7 → 744,7
futex_up(&ridxp->lock);
 
ipc_answer_0(rid, EOK);
ipc_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
}
 
void fat_mount(ipc_callid_t rid, ipc_call_t *request)
622,6 → 757,96
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);
}
 
/**
* @}
*/