153 lines
4.1 KiB
C
153 lines
4.1 KiB
C
#include "fs/ext2/block.h"
|
|
#include "fs/ext2/ext2.h"
|
|
#include "fs/ext2/node.h"
|
|
#include "fs/fs.h"
|
|
#include "fs/node.h"
|
|
#include "sys/assert.h"
|
|
#include "sys/attr.h"
|
|
#include "sys/block/blk.h"
|
|
#include "sys/debug.h"
|
|
#include "sys/heap.h"
|
|
#include "sys/mem/slab.h"
|
|
#include "sys/string.h"
|
|
#include "user/errno.h"
|
|
#include "user/mount.h"
|
|
|
|
static int ext2_init(struct fs *ext2, const char *opt);
|
|
static void ext2_destroy(struct fs *ext2);
|
|
static struct vnode *ext2_get_root(struct fs *ext2);
|
|
|
|
////
|
|
|
|
static struct fs_class g_ext2 = {
|
|
.name = "ext2",
|
|
.opt = 0,
|
|
.init = ext2_init,
|
|
.destroy = ext2_destroy,
|
|
.get_root = ext2_get_root
|
|
};
|
|
|
|
////
|
|
|
|
static int ext2_init(struct fs *ext2, const char *opt) {
|
|
_assert(ext2->blk);
|
|
|
|
struct ext2_data *data = kmalloc(sizeof(struct ext2_data));
|
|
_assert(data);
|
|
ext2->fs_private = data;
|
|
|
|
int res;
|
|
|
|
if (!(ext2->flags & MS_SYNCHRONOUS)) {
|
|
// 16MiB cache
|
|
blk_set_cache(ext2->blk, 4096);
|
|
} else {
|
|
kdebug("Not using cache\n");
|
|
}
|
|
|
|
// Read superblock
|
|
if ((res = ext2_read_superblock(ext2)) != 0) {
|
|
kfree(data);
|
|
ext2->fs_private = NULL;
|
|
return res;
|
|
}
|
|
|
|
// Check the signature
|
|
if (data->sb.ext2_signature != EXT2_SIGNATURE) {
|
|
kerror("Invalid ext2 signature\n");
|
|
kfree(data);
|
|
ext2->fs_private = NULL;
|
|
return -EINVAL;
|
|
}
|
|
|
|
// TODO: perform some kind of fsck?
|
|
kinfo("Volume ID: \"%s\"\n", data->sb.volume_name);
|
|
kinfo("Filesystem version: %d.%d\n", data->sb.version_major, data->sb.version_minor);
|
|
|
|
// Calculate filesystem parameters
|
|
data->block_size = 1024 << data->sb.log2_block_size;
|
|
|
|
if (data->sb.version_major != 0) {
|
|
data->inode_size = data->sb.inode_size;
|
|
} else {
|
|
data->inode_size = 128;
|
|
}
|
|
|
|
// Create a slab cache for inode objects
|
|
data->inode_cache = slab_cache_get(data->inode_size);
|
|
if (!data->inode_cache) {
|
|
// TODO: error handling
|
|
panic("Failed to allocate a slab cache of size %d for inodes\n", data->inode_size);
|
|
}
|
|
|
|
data->inodes_per_block = data->block_size / data->inode_size;
|
|
data->blkgrp_inode_blocks = data->sb.block_group_inodes / data->inodes_per_block;
|
|
|
|
kinfo("Block size: %u, inode size: %u\n", data->block_size, data->inode_size);
|
|
|
|
data->bgdt_entry_count = (data->sb.block_count + data->sb.block_group_blocks - 1) / data->sb.block_group_blocks;
|
|
uint32_t bgdt_size = data->bgdt_entry_count * sizeof(struct ext2_blkgrp_desc);
|
|
data->bgdt_size_blocks = (bgdt_size + data->block_size - 1) / data->block_size;
|
|
|
|
kinfo("Block groups: %u\n", data->bgdt_entry_count);
|
|
kinfo("BGDT occupies %u block(s)\n", data->bgdt_size_blocks);
|
|
|
|
data->bgdt = kmalloc(data->bgdt_size_blocks * data->block_size);
|
|
_assert(data->bgdt);
|
|
|
|
// BGDT starts with block 2
|
|
for (uint32_t i = 0; i < data->bgdt_size_blocks; ++i) {
|
|
if ((res = ext2_read_block(ext2, ((void *) data->bgdt) + i * data->block_size, i + 2)) != 0) {
|
|
kfree(data->bgdt);
|
|
kfree(data);
|
|
ext2->fs_private = NULL;
|
|
return res;
|
|
}
|
|
}
|
|
|
|
data->root_inode = slab_calloc(data->inode_cache);
|
|
_assert(data->root_inode);
|
|
data->root = vnode_create(VN_DIR, NULL);
|
|
_assert(data->root);
|
|
|
|
// Read root inode
|
|
if ((res = ext2_read_inode(ext2, data->root_inode, 2)) != 0) {
|
|
kfree(data->root_inode);
|
|
kfree(data->root);
|
|
kfree(data->bgdt);
|
|
kfree(data);
|
|
return res;
|
|
}
|
|
|
|
data->root->fs = ext2;
|
|
ext2_inode_to_vnode(data->root, data->root_inode, 2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ext2_destroy(struct fs *ext2) {
|
|
struct ext2_data *data = ext2->fs_private;
|
|
_assert(data);
|
|
|
|
if (!(ext2->flags & MS_SYNCHRONOUS)) {
|
|
blk_cache_release(ext2->blk);
|
|
}
|
|
|
|
kfree(data->root_inode);
|
|
kfree(data->root);
|
|
kfree(data->bgdt);
|
|
kfree(data);
|
|
}
|
|
|
|
static struct vnode *ext2_get_root(struct fs *ext2) {
|
|
struct ext2_data *data = ext2->fs_private;
|
|
_assert(data);
|
|
_assert(data->root);
|
|
|
|
return data->root;
|
|
}
|
|
|
|
__init(ext2_class_init) {
|
|
fs_class_register(&g_ext2);
|
|
}
|