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); |
} |
|
/** |
* @} |
*/ |