194 lines
5.8 KiB
C
194 lines
5.8 KiB
C
#include "fs/ext2/block.h"
|
|
#include "fs/ext2/ext2.h"
|
|
#include "user/errno.h"
|
|
#include "sys/block/blk.h"
|
|
#include "sys/assert.h"
|
|
#include "sys/string.h"
|
|
#include "fs/ext2/alloc.h"
|
|
#include "fs/fs.h"
|
|
#include "sys/debug.h"
|
|
|
|
int ext2_read_superblock(struct fs *fs) {
|
|
struct ext2_data *data = fs->fs_private;
|
|
_assert(data);
|
|
int res = blk_read(fs->blk, data->__data, 1024, 1024);
|
|
if (res == 1024) {
|
|
return 0;
|
|
}
|
|
kerror("Superblock read failed\n");
|
|
return res;
|
|
}
|
|
|
|
int ext2_write_superblock(struct fs *fs) {
|
|
struct ext2_data *data = fs->fs_private;
|
|
_assert(data);
|
|
int res = blk_write(fs->blk, data->__data, 1024, 1024);
|
|
if (res == 1024) {
|
|
return 0;
|
|
}
|
|
kerror("Superblock write failed\n");
|
|
return res;
|
|
}
|
|
|
|
int ext2_read_block(struct fs *fs, void *block, uint32_t no) {
|
|
struct ext2_data *data = fs->fs_private;
|
|
_assert(data);
|
|
int res = blk_read(fs->blk, block, no * data->block_size, data->block_size);
|
|
if (res == 1024) {
|
|
return 0;
|
|
}
|
|
kerror("Failed to read block %u: %s\n", no, kstrerror(res));
|
|
return res;
|
|
}
|
|
|
|
int ext2_write_block(struct fs *fs, const void *block, uint32_t no) {
|
|
struct ext2_data *data = fs->fs_private;
|
|
_assert(data);
|
|
int res = blk_write(fs->blk, block, no * data->block_size, data->block_size);
|
|
if (res == 1024) {
|
|
return 0;
|
|
}
|
|
kerror("Failed to write block %u: %s\n", no, kstrerror(res));
|
|
return res;
|
|
}
|
|
|
|
int ext2_read_inode(struct fs *fs, struct ext2_inode *inode, uint32_t ino) {
|
|
struct ext2_data *data = fs->fs_private;
|
|
_assert(data);
|
|
char block[data->block_size];
|
|
if (ino < 1 || ino >= data->sb.inode_count) {
|
|
panic("Invalid inode number: %u\n", ino);
|
|
}
|
|
int res;
|
|
|
|
--ino;
|
|
uint32_t ino_group = ino / data->sb.block_group_inodes;
|
|
uint32_t ino_in_group = ino % data->sb.block_group_inodes;
|
|
uint32_t ino_block = data->bgdt[ino_group].inode_table_no + ino_in_group / data->inodes_per_block;
|
|
uint32_t offset_in_block = (ino_in_group % data->inodes_per_block) * data->inode_size;
|
|
_assert(offset_in_block < data->block_size);
|
|
|
|
if ((res = ext2_read_block(fs, block, ino_block)) != 0) {
|
|
return res;
|
|
}
|
|
|
|
memcpy(inode, offset_in_block + block, data->inode_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ext2_write_inode(struct fs *fs, struct ext2_inode *inode, uint32_t ino) {
|
|
// Automatically update inode times
|
|
inode->atime = time();
|
|
|
|
struct ext2_data *data = fs->fs_private;
|
|
_assert(data);
|
|
char block[data->block_size];
|
|
if (ino < 1 || ino >= data->sb.inode_count) {
|
|
panic("Invalid inode number: %u\n", ino);
|
|
}
|
|
int res;
|
|
|
|
--ino;
|
|
uint32_t ino_group = ino / data->sb.block_group_inodes;
|
|
uint32_t ino_in_group = ino % data->sb.block_group_inodes;
|
|
uint32_t ino_block = data->bgdt[ino_group].inode_table_no + ino_in_group / data->inodes_per_block;
|
|
uint32_t offset_in_block = (ino_in_group % data->inodes_per_block) * data->inode_size;
|
|
_assert(offset_in_block < data->block_size);
|
|
|
|
if ((res = ext2_read_block(fs, block, ino_block)) != 0) {
|
|
return res;
|
|
}
|
|
|
|
memcpy(offset_in_block + block, inode, data->inode_size);
|
|
|
|
return ext2_write_block(fs, block, ino_block);
|
|
}
|
|
|
|
int ext2_read_inode_block(struct fs *fs, struct ext2_inode *inode, void *buf, uint32_t index) {
|
|
uint32_t block = ext2_inode_get_index(fs, inode, index);
|
|
if (!block) {
|
|
panic("Read outside of block count range\n");
|
|
}
|
|
return ext2_read_block(fs, buf, block);
|
|
}
|
|
|
|
int ext2_write_inode_block(struct fs *fs, struct ext2_inode *inode, const void *buf, uint32_t index) {
|
|
uint32_t block = ext2_inode_get_index(fs, inode, index);
|
|
if (!block) {
|
|
panic("Write outside of block count range\n");
|
|
}
|
|
return ext2_write_block(fs, buf, block);
|
|
}
|
|
|
|
uint32_t ext2_inode_get_index(struct fs *ext2, struct ext2_inode *inode, uint32_t index) {
|
|
struct ext2_data *data = ext2->fs_private;
|
|
_assert(data);
|
|
|
|
uint32_t p = data->block_size / sizeof(uint32_t);
|
|
uint32_t ptrs[p];
|
|
|
|
if (index < EXT2_DIRECT_BLOCKS) {
|
|
return inode->direct_blocks[index];
|
|
}
|
|
|
|
// Fits in L1?
|
|
index -= EXT2_DIRECT_BLOCKS;
|
|
if (index < p) {
|
|
if (!inode->indirect_block_l1) {
|
|
panic("Read beyond end of file (L1.1)\n");
|
|
}
|
|
|
|
_assert(ext2_read_block(ext2, ptrs, inode->indirect_block_l1) == 0);
|
|
return ptrs[index];
|
|
}
|
|
|
|
// Fits in L2?
|
|
index -= p;
|
|
if (index < p * p) {
|
|
uint32_t index_l1 = index / p;
|
|
uint32_t index_l0 = index % p;
|
|
if (!inode->indirect_block_l2) {
|
|
panic("Read beyond end of the file (L2.2)\n");
|
|
}
|
|
_assert(ext2_read_block(ext2, ptrs, inode->indirect_block_l2) == 0);
|
|
if (!ptrs[index_l1]) {
|
|
panic("Read beyond end of the file (L2.1)\n");
|
|
}
|
|
_assert(ext2_read_block(ext2, ptrs, ptrs[index_l1]) == 0);
|
|
return ptrs[index_l0];
|
|
}
|
|
|
|
panic("TODO: L3 support\n");
|
|
}
|
|
|
|
int ext2_inode_set_index(struct fs *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index, uint32_t value) {
|
|
struct ext2_data *data = ext2->fs_private;
|
|
_assert(data);
|
|
|
|
uint32_t p = data->block_size / sizeof(uint32_t);
|
|
uint32_t a, b, c, d, e, f, g;
|
|
uint32_t ptrs[p];
|
|
|
|
if (index < EXT2_DIRECT_BLOCKS) {
|
|
inode->direct_blocks[index] = value;
|
|
return 0;
|
|
} else if (index < EXT2_DIRECT_BLOCKS + p) {
|
|
if (!inode->indirect_block_l1) {
|
|
uint32_t block = ext2_alloc_block(ext2, data);
|
|
if (!block) {
|
|
panic("Failed to allocate a block\n");
|
|
}
|
|
inode->indirect_block_l1 = block;
|
|
}
|
|
|
|
_assert(ext2_read_block(ext2, ptrs, inode->indirect_block_l1) == 0);
|
|
ptrs[index - EXT2_DIRECT_BLOCKS] = value;
|
|
_assert(ext2_write_block(ext2, ptrs, inode->indirect_block_l1) == 0);
|
|
return 0;
|
|
} else {
|
|
panic("TODO L2+\n");
|
|
}
|
|
}
|
|
|