blob: d5c8d3f2f5236ff1c93e0d85a3ae7da30bc1df65 [file] [log] [blame]
#include <dri/fs/ext2/ext2.h>
#include <dri/fs/ext2/ext2_vfs.h>
#include <sync.h>
#include <alloc.h>
#include <log.h>
#include <io.h>
struct ext2_fs_dirent
{
struct fs_node *node;
char name[256];
uint name_len;
uint inode;
struct ext2_fs_dirent *next;
};
struct ext2_fs_data
{
struct ext2_superblock *sb;
/// Cached inode
struct ext2_inode *in;
/// Reference count for the inode cache. Once this hits 0 we free
/// in. Only used for **files**, directories **always** cache
/// their inode.
uint rc;
semaphore_t lock;
/// Entries (if this is a directory)
struct ext2_fs_dirent *dirent;
};
uint ext2_file_read(struct fs_node *node, size_t ofs, size_t size,
uchar *buffer)
{
struct ext2_fs_data *d = node->dri_res_p;
const uint block_size = ext2_block_size(d->sb);
// temporary buffer
uchar *tmp = malloc(block_size);
uint num_read = 0;
if (!buffer)
{
kprintf(WARN "ext2_file_read out of memory!\n");
return 0;
}
uint block_start = ofs / block_size;
uint block_offset = ofs % block_size;
int to_read;
do
{
// How much from this block to read
to_read = MIN(block_size - block_offset, size);
ext2_read_inode_block(d->sb, d->in, tmp, block_start);
// Copy that to the user buffer
memcpy(buffer, tmp + block_offset, to_read);
buffer += to_read;
size -= to_read;
num_read += to_read;
// Move to the next block
block_start++;
block_offset = 0;
}
while (to_read > 0);
free(tmp);
return num_read;
}
void ext2_file_open(struct fs_node *node)
{
struct ext2_fs_data *d = node->dri_res_p;
sm_wait(d->lock);
if (d->rc++ == 0)
{
d->in = malloc(sizeof(struct ext2_inode));
if (d->in == NULL)
{
// lol
kpanic("ext2_file_open: ENOMEM");
}
if (!ext2_find_inode(d->sb, node->inode, d->in))
{
kprintf(ERROR "ext2_file_open: can't find inode %d\n",
node->inode);
free(d->in);
goto done;
}
}
done:
sm_signal(d->lock);
}
void ext2_file_close(struct fs_node *node)
{
struct ext2_fs_data *d = node->dri_res_p;
sm_wait(d->lock);
if (--d->rc == 0)
{
free(d->in);
d->in = NULL;
}
sm_signal(d->lock);
}
struct fs_vtable ext2_file_vfs_vt =
{
.read = ext2_file_read,
.write = NULL,
.open = ext2_file_open,
.close = ext2_file_close,
.readdir = NULL,
.finddir = NULL,
};
bool ext2_dir_readdir(struct fs_node *node, uint index, struct fs_dirent *dirent)
{
struct ext2_fs_data *d = node->dri_res_p;
struct ext2_fs_dirent *dent = d->dirent;
uint i;
for (i = 0; i < index && dent; i++, dent = dent->next)
{
}
if (i == index)
{
memcpy(dirent->name, dent->name, 256);
dirent->name_len = dent->name_len;
dirent->inode = dent->inode;
return true;
}
return false;
}
struct fs_node *ext2_dir_finddir(struct fs_node *node, char *name, uint name_len)
{
name_len = MIN(name_len, 256);
struct ext2_fs_data *d = node->dri_res_p;
struct ext2_fs_dirent *dent = d->dirent;
for (; dent; dent = dent->next)
{
if (strncmp(dent->name, name, name_len) == 0)
{
return dent->node;
}
}
return NULL;
}
struct fs_vtable ext2_dir_vfs_vt =
{
.read = NULL,
.write = NULL,
.open = NULL,
.close = NULL,
.readdir = ext2_dir_readdir,
.finddir = ext2_dir_finddir,
};
struct ext2_fs_dirent_to_fs_data
{
struct ext2_fs_data *d;
struct ext2_fs_dirent *last;
struct ext2_superblock *sb;
};
static bool ext2_dirent_to_fs_node_cb(uint inode, const char *name,
uint name_len, void *data)
{
struct ext2_fs_dirent_to_fs_data *d = data;
struct ext2_fs_dirent *dent =
malloc(sizeof(struct ext2_fs_dirent));
if (strncmp(".", name, name_len) == 0 ||
strncmp("..", name, name_len) == 0)
{
return true;
}
dent->node = ext2_inode2vfs(d->sb, inode, (char *)name, name_len);
dent->name_len = name_len;
memcpy(dent->name, name, MIN(name_len, 256));
dent->inode = inode;
if (d->last)
{
d->last->next = dent;
d->last = dent;
}
else
{
d->last = d->d->dirent = dent;
}
return true;
}
struct fs_node *ext2_inode2vfs(struct ext2_superblock *sb, uint inode,
char *name, uint name_len)
{
struct ext2_inode in;
if (!ext2_find_inode(sb, inode, &in))
{
// Something has gone terribly wrong!
return NULL;
}
struct ext2_fs_data *d = malloc(sizeof(struct ext2_fs_data));
d->sb = sb;
d->rc = 0;
d->lock = sm_new();
d->in = NULL;
d->dirent = NULL;
struct fs_node *n = malloc(sizeof(struct fs_node));
n->name_len = MIN(name_len, FS_MAX_NAME_LEN);
memcpy(n->name, name, n->name_len);
n->inode = inode;
n->dri_res_p = d;
n->mask = in.mode & 0xfff;
n->gid = in.gid;
n->uid = in.uid;
n->size = in.size;
switch (in.mode & EXT2_F_TYPE)
{
case EXT2_S_IFREG:
{
n->flags = FS_FILE;
n->vtable = &ext2_file_vfs_vt;
break;
}
case EXT2_S_IFDIR:
{
n->flags = FS_DIRECTORY;
n->vtable = &ext2_dir_vfs_vt;
struct ext2_fs_dirent_to_fs_data data;
data.d = d;
data.last = NULL;
data.sb = sb;
ext2_dir_ls(sb, &in, ext2_dirent_to_fs_node_cb, &data);
break;
}
default:
{
kprintf(ERROR "ext2_inode2vfs: unimplemented for dir type%d\n",
EXT2_S_TO_FT(in.mode));
kpanic("Unimplemented");
}
}
return n;
}