Backport ext2

This commit is contained in:
Mark
2020-02-03 12:56:53 +02:00
parent bd023a1873
commit fbdb44b71d
10 changed files with 941 additions and 72 deletions
+5
View File
@@ -43,6 +43,7 @@ OBJS+=$(O)/sys/debug.o \
$(O)/sys/char/tty.o \
$(O)/sys/char/chr.o \
$(O)/sys/block/pseudo.o \
$(O)/sys/block/part_gpt.o \
$(O)/sys/block/ram.o \
$(O)/sys/block/blk.o \
$(O)/sys/dev.o \
@@ -53,6 +54,9 @@ OBJS+=$(O)/sys/debug.o \
$(O)/sys/fs/node.o \
$(O)/sys/fs/tar.o \
$(O)/sys/fs/sysfs.o \
$(O)/sys/fs/ext2/block.o \
$(O)/sys/fs/ext2/ext2.o \
$(O)/sys/fs/ext2/node.o \
$(O)/sys/time.o \
$(O)/sys/sys_file.o \
$(O)/sys/sys_sys.o \
@@ -65,6 +69,7 @@ OBJS+=$(O)/sys/driver/pci/pci.o \
$(O)/sys/driver/ata/ahci.o
DIRS+=$(O)/sys/fs \
$(O)/sys/fs/ext2 \
$(O)/sys/char \
$(O)/sys/block \
$(O)/sys/driver/pci \
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "sys/types.h"
struct blkdev;
struct vnode;
int blk_enumerate_gpt(struct blkdev *dev, struct vnode *of, uint8_t *head);
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include "sys/types.h"
struct ext2_inode;
struct fs;
int ext2_read_superblock(struct fs *ext2);
int ext2_write_superblock(struct fs *ext2);
int ext2_read_block(struct fs *ext2, void *blk, uint32_t no);
int ext2_read_inode_block(struct fs *ext2, struct ext2_inode *inode, void *block, uint32_t index);
int ext2_read_inode(struct fs *ext2, struct ext2_inode *dst, uint32_t ino);
// NOTE: this function automatically updates access and modification time
// on the inode written
int ext2_write_inode(struct fs *ext2, struct ext2_inode *node, uint32_t ino);
+146
View File
@@ -0,0 +1,146 @@
#pragma once
#include "sys/types.h"
#define EXT2_FLAG_RO (1 << 0)
#define EXT2_STATE_CLEAN 1
#define EXT2_STATE_DIRTY 2
#define EXT2_ERROR_IGNORE 1
#define EXT2_ERROR_READONLY 2
#define EXT2_ERROR_PANIC 3
#define EXT2_REQ_COMP (1 << 0)
#define EXT2_REQ_ENT_TYPE (1 << 1)
#define EXT2_REQ_REPLAY_JOURNAL (1 << 2)
#define EXT2_REQ_JOURNAL_DEV (1 << 3)
#define EXT2_RO_SPARSE_SB (1 << 0)
#define EXT2_RO_64BIT_SIZE (1 << 1)
#define EXT2_RO_DIR_BTREE (1 << 2)
#define EXT2_SIGNATURE 0xEF53
#define EXT2_IFIFO 0x1000
#define EXT2_IFCHR 0x2000
#define EXT2_IFDIR 0x4000
#define EXT2_IFBLK 0x6000
#define EXT2_IFREG 0x8000
#define EXT2_IFLNK 0xA000
#define EXT2_IFSOCK 0xC000
struct vnode;
struct ext2_superblock {
// Base superblock
uint32_t inode_count;
uint32_t block_count;
uint32_t reserved_blocks;
uint32_t free_blocks;
uint32_t free_inodes;
uint32_t superblock_no;
uint32_t log2_block_size;
uint32_t log2_frag_size;
uint32_t block_group_blocks;
uint32_t block_group_frags;
uint32_t block_group_inodes;
uint32_t mount_time;
uint32_t write_time;
uint16_t mount_count;
uint16_t fsck_mount_count;
uint16_t ext2_signature;
uint16_t state;
uint16_t error_behavior;
uint16_t version_minor;
uint32_t fsck_time;
uint32_t fsck_interval;
uint32_t os_id;
uint32_t version_major;
uint16_t reserved_uid;
uint16_t reserved_gid;
// Extended superblock
uint32_t first_inode; // == 11 in < 1.0
uint16_t inode_size; // == 128 in < 1.0
uint16_t superblock_block_group; // For backup
uint32_t optional_features; // Don't matter
uint32_t required_features; // Cannot read without these
uint32_t write_required_features; // Can read without these
char filesystem_id[16];
char volume_name[16];
char last_mount_path[64];
uint32_t compression;
uint8_t prealloc_file_blocks;
uint8_t prealloc_dir_blocks;
uint16_t __res0;
char journal_id[16];
uint32_t journal_inode;
uint32_t journal_device;
uint32_t orphan_inode_list_head;
} __attribute__((packed));
struct ext2_blkgrp_desc {
uint32_t block_bitmap_no;
uint32_t inode_bitmap_no;
uint32_t inode_table_no;
uint16_t free_blocks;
uint16_t free_inodes;
uint16_t directory_count;
char __pad[14];
} __attribute__((packed));
struct ext2_inode {
uint16_t mode;
uint16_t uid;
uint32_t size_lower;
uint32_t atime;
uint32_t ctime;
uint32_t mtime;
uint32_t dtime;
uint16_t gid;
uint16_t hard_links;
uint32_t sector_count;
uint32_t flags;
uint32_t os_val1;
uint32_t direct_blocks[12];
uint32_t indirect_block_l1;
uint32_t indirect_block_l2;
uint32_t indirect_block_l3;
uint32_t generation;
uint32_t facl;
union {
uint32_t size_upper;
uint32_t dir_acl;
};
uint32_t frag_block_no;
uint32_t os_val2;
} __attribute__((packed));
struct ext2_dirent {
uint32_t ino;
uint16_t ent_size;
uint8_t name_length_low;
union {
uint8_t type_indicator;
uint8_t name_length_high;
};
char name[0];
} __attribute__((packed));
struct ext2_data {
union {
struct ext2_superblock sb;
char __data[1024];
};
struct ext2_blkgrp_desc *bgdt;
struct vnode *root;
struct ext2_inode *root_inode;
uint32_t flags;
uint32_t block_size;
uint32_t inode_size;
uint32_t inodes_per_block;
uint32_t blkgrp_inode_blocks;
uint32_t bgdt_entry_count;
uint32_t bgdt_size_blocks;
};
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "sys/types.h"
struct vnode_operations;
struct ext2_inode;
struct vnode;
extern struct vnode_operations g_ext2_vnode_ops;
void ext2_inode_to_vnode(struct vnode *vnode, struct ext2_inode *inode, uint32_t ino);
+73 -72
View File
@@ -1,12 +1,14 @@
//#include "sys/blk/part_gpt.h"
#include "sys/block/part_gpt.h"
#include "sys/user/errno.h"
#include "sys/block/blk.h"
#include "sys/fs/node.h"
#include "sys/assert.h"
#include "sys/fs/vfs.h"
#include "sys/string.h"
#include "sys/fs/fs.h"
#include "sys/debug.h"
#include "sys/heap.h"
#include "sys/dev.h"
struct blk_part {
struct blkdev *device;
@@ -65,77 +67,76 @@ int blk_mount_auto(struct vnode *at, struct blkdev *blk, const char *opt) {
return -EINVAL;
}
//static ssize_t blk_part_write(struct blkdev *dev, const void *buf, size_t off, size_t count) {
// struct blk_part *part = (struct blk_part *) dev->dev_data;
// size_t part_size_bytes = part->size * 512;
// size_t part_off_bytes = part->lba_start * 512;
//
// if (off >= part_size_bytes) {
// return -1;
// }
//
// size_t l = MIN(part_size_bytes - off, count);
//
// return blk_write(part->device, buf, off + part_off_bytes, l);
//}
//
//static ssize_t blk_part_read(struct blkdev *dev, void *buf, size_t off, size_t count) {
// struct blk_part *part = (struct blk_part *) dev->dev_data;
// size_t part_size_bytes = part->size * 512;
// size_t part_off_bytes = part->lba_start * 512;
//
// if (off >= part_size_bytes) {
// return -1;
// }
//
// size_t l = MIN(part_size_bytes - off, count);
//
// return blk_read(part->device, buf, off + part_off_bytes, l);
//}
//
//int blk_add_part(struct vnode *of, int n, uint64_t lba, uint64_t size) {
// // Allocate both a device and partition details
// char *buf = kmalloc(sizeof(struct blk_part) + sizeof(struct blkdev));
// _assert(buf);
// struct blk_part *part = (struct blk_part *) (buf + sizeof(struct blkdev));
// struct blkdev *dev = (struct blkdev *) buf;
//
// dev->dev_data = part;
// dev->read = blk_part_read;
// dev->write = blk_part_write;
// dev->flags = 0;
//
// part->device = of->dev;
// part->lba_start = lba;
// part->size = size;
//
// char name[16];
// size_t len = strlen(of->name);
// _assert(len < sizeof(name) - 2);
// strncpy(name, of->name, len);
// name[len] = '1' + n;
// name[len + 1] = 0;
//
// dev_add(DEV_CLASS_BLOCK, DEV_BLOCK_PART, dev, name);
//
// return 0;
//}
static ssize_t blk_part_write(struct blkdev *dev, const void *buf, size_t off, size_t count) {
struct blk_part *part = (struct blk_part *) dev->dev_data;
size_t part_size_bytes = part->size * 512;
size_t part_off_bytes = part->lba_start * 512;
if (off >= part_size_bytes) {
return -1;
}
size_t l = MIN(part_size_bytes - off, count);
return blk_write(part->device, buf, off + part_off_bytes, l);
}
static ssize_t blk_part_read(struct blkdev *dev, void *buf, size_t off, size_t count) {
struct blk_part *part = (struct blk_part *) dev->dev_data;
size_t part_size_bytes = part->size * 512;
size_t part_off_bytes = part->lba_start * 512;
if (off >= part_size_bytes) {
return -1;
}
size_t l = MIN(part_size_bytes - off, count);
return blk_read(part->device, buf, off + part_off_bytes, l);
}
int blk_add_part(struct vnode *of, int n, uint64_t lba, uint64_t size) {
// Allocate both a device and partition details
char *buf = kmalloc(sizeof(struct blk_part) + sizeof(struct blkdev));
_assert(buf);
struct blk_part *part = (struct blk_part *) (buf + sizeof(struct blkdev));
struct blkdev *dev = (struct blkdev *) buf;
dev->dev_data = part;
dev->read = blk_part_read;
dev->write = blk_part_write;
dev->flags = 0;
part->device = of->dev;
part->lba_start = lba;
part->size = size;
char name[16];
size_t len = strlen(of->name);
_assert(len < sizeof(name) - 2);
strncpy(name, of->name, len);
name[len] = '1' + n;
name[len + 1] = 0;
dev_add(DEV_CLASS_BLOCK, DEV_BLOCK_PART, dev, name);
int blk_enumerate_partitions(struct blkdev *blk, struct vnode *node) {
// // TODO: better differentiation for CD/DVD-drives than just assuming every
// // drive with block size of 2048 is one
// ssize_t res;
// uint8_t buf[1024];
//
// if (blk->block_size == 512) {
// if ((res = blk_read(blk, buf, 0, 1024)) < 0) {
// return res;
// }
//
// if (!strncmp((const char *) (buf + 512), "EFI PART", 8)) {
// return blk_enumerate_gpt(blk, node, buf);
// }
// }
//
return 0;
}
int blk_enumerate_partitions(struct blkdev *blk, struct vnode *node) {
// TODO: better differentiation for CD/DVD-drives than just assuming every
// drive with block size of 2048 is one
ssize_t res;
uint8_t buf[1024];
if (blk->block_size == 512) {
if ((res = blk_read(blk, buf, 0, 1024)) < 0) {
return res;
}
if (!strncmp((const char *) (buf + 512), "EFI PART", 8)) {
return blk_enumerate_gpt(blk, node, buf);
}
}
return 0;
}
+129
View File
@@ -0,0 +1,129 @@
#include "sys/user/errno.h"
#include "sys/block/blk.h"
#include "sys/fs/node.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/debug.h"
// Align to some nice boundary (it's 36)
#define GUID_STRLEN 40
const char GUID_EMPTY[16] = {0};
const struct {
const char *name;
const char *guid;
} guiddb[] = {
// A bunch of known GUID so I can pretty-print stuff
{ "EFI System Partition", "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" },
{ "Linux Filesystem", "0FC63DAF-8483-4772-8E79-3D69D8477DE4" },
{ "Microsoft Basic Data", "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7" },
};
struct gpt_header {
char signature[8];
uint32_t revision;
uint32_t header_size;
uint32_t header_crc32;
uint32_t __res0;
uint64_t my_lba;
uint64_t alt_lba;
uint64_t first_lba;
uint64_t last_lba;
char disk_guid[16];
uint64_t partition_entry_lba;
uint32_t partition_count;
uint32_t partition_entry_size;
uint32_t partition_array_crc32;
} __attribute__((packed));
struct gpt_part_entry {
char type_guid[16];
char part_guid[16];
uint64_t start_lba;
uint64_t end_lba;
uint64_t attr;
char part_name[72];
};
static void strguid(char *str, const char *guid) {
static const char *const hexc = "0123456789ABCDEF";
// TODO: snprintf
int off = 0;
for (int i = 3; i >= 0; --i) {
str[off++] = hexc[(guid[i] >> 4) & 0xF];
str[off++] = hexc[guid[i] & 0xF];
}
str[off++] = '-';
for (int i = 1; i >= 0; --i) {
str[off++] = hexc[(guid[i + 4] >> 4) & 0xF];
str[off++] = hexc[guid[i + 4] & 0xF];
}
str[off++] = '-';
for (int i = 1; i >= 0; --i) {
str[off++] = hexc[(guid[i + 6] >> 4) & 0xF];
str[off++] = hexc[guid[i + 6] & 0xF];
}
str[off++] = '-';
for (int i = 0; i < 2; ++i) {
str[off++] = hexc[(guid[i + 8] >> 4) & 0xF];
str[off++] = hexc[guid[i + 8] & 0xF];
}
str[off++] = '-';
for (int i = 0; i < 6; ++i) {
str[off++] = hexc[(guid[i + 10] >> 4) & 0xF];
str[off++] = hexc[guid[i + 10] & 0xF];
}
str[off] = 0;
}
static void gpt_describe_entry(struct vnode *of, size_t i, struct gpt_part_entry *ent) {
char type_guid_str[GUID_STRLEN];
char part_guid_str[GUID_STRLEN];
kdebug("%s partition %d:\n", of->name, i);
strguid(type_guid_str, ent->type_guid);
strguid(part_guid_str, ent->part_guid);
kdebug("Type: %s\n", type_guid_str);
for (size_t j = 0; j < sizeof(guiddb) / sizeof(guiddb[0]); ++j) {
if (!strncmp(guiddb[j].guid, type_guid_str, GUID_STRLEN)) {
kdebug(" %s\n", guiddb[j].name);
break;
}
}
kdebug("Part: %s\n", part_guid_str);
}
int blk_enumerate_gpt(struct blkdev *dev, struct vnode *of, uint8_t *head) {
// TODO: support other devices
_assert(dev->block_size == 512);
// LBA1 - GPT header
struct gpt_header *hdr = (struct gpt_header *) &head[512];
// Read a single block for partition table entries
char buf[512];
_assert(blk_read(dev, buf, hdr->partition_entry_lba * 512, 512) >= 0);
size_t offset = 0;
size_t part_n = 0;
int res;
for (size_t i = 0; i < hdr->partition_count; ++i) {
struct gpt_part_entry *ent = (struct gpt_part_entry *) &buf[offset];
if (memcmp(ent->type_guid, GUID_EMPTY, 16)) {
// Maybe some known filesystem here, ignore partition type guid
if ((res = blk_add_part(of, i, ent->start_lba, ent->end_lba - ent->start_lba)) != 0) {
kerror("Failed to add partition: %s\n", kstrerror(res));
} else {
gpt_describe_entry(of, i, ent);
}
}
offset += hdr->partition_entry_size;
if (offset >= 512) {
break;
}
}
return 0;
}
+105
View File
@@ -0,0 +1,105 @@
#include "sys/fs/ext2/block.h"
#include "sys/fs/ext2/ext2.h"
#include "sys/user/errno.h"
#include "sys/block/blk.h"
#include "sys/assert.h"
#include "sys/string.h"
#include "sys/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_read_block(struct fs *fs, void *block, uint32_t no) {
struct ext2_data *data = fs->fs_private;
_assert(data);
kdebug("Read block %u: %p\n", no, no * data->block_size);
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);
kdebug("Write block %u: %p\n", no, no * data->block_size);
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->mtime = time();
inode->atime = inode->mtime;
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) {
if (index < 12) {
return ext2_read_block(fs, buf, inode->direct_blocks[index]);
} else {
panic("Not implemented\n");
}
}
+119
View File
@@ -0,0 +1,119 @@
#include "sys/fs/ext2/block.h"
#include "sys/fs/ext2/node.h"
#include "sys/fs/ext2/ext2.h"
#include "sys/user/errno.h"
#include "sys/fs/node.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/debug.h"
#include "sys/fs/fs.h"
#include "sys/heap.h"
#include "sys/attr.h"
static int ext2_init(struct fs *ext2, const char *opt);
static struct vnode *ext2_get_root(struct fs *ext2);
////
static struct fs_class g_ext2 = {
.name = "ext2",
.opt = 0,
.init = ext2_init,
.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;
// 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;
}
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 = kmalloc(data->inode_size);
_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 struct vnode *ext2_get_root(struct fs *ext2) {
struct ext2_data *data = ext2->fs_private;
_assert(data);
_assert(data->root);
return data->root;
}
static void __init ext2_class_init(void) {
fs_class_register(&g_ext2);
}
+330
View File
@@ -0,0 +1,330 @@
#include "sys/fs/ext2/block.h"
#include "sys/fs/ext2/node.h"
#include "sys/fs/ext2/ext2.h"
#include "sys/user/fcntl.h"
#include "sys/user/errno.h"
#include "sys/fs/ofile.h"
#include "sys/fs/node.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/fs/fs.h"
#include "sys/panic.h"
#include "sys/debug.h"
#include "sys/heap.h"
static int ext2_vnode_find(struct vnode *at, const char *name, struct vnode **res);
static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count);
static int ext2_vnode_open(struct ofile *fd, int opt);
static int ext2_vnode_opendir(struct ofile *fd);
static int ext2_vnode_chmod(struct vnode *vn, mode_t new_mode);
static int ext2_vnode_chown(struct vnode *node, uid_t new_uid, gid_t new_gid);
static ssize_t ext2_vnode_readdir(struct ofile *fd, struct dirent *ent);
static int ext2_vnode_stat(struct vnode *at, struct stat *st);
////
struct vnode_operations g_ext2_vnode_ops = {
.find = ext2_vnode_find,
.opendir = ext2_vnode_opendir,
.readdir = ext2_vnode_readdir,
.stat = ext2_vnode_stat,
.chmod = ext2_vnode_chmod,
.chown = ext2_vnode_chown,
.open = ext2_vnode_open,
.read = ext2_vnode_read,
};
////
static int ext2_vnode_find(struct vnode *at, const char *name, struct vnode **result) {
_assert(at && at->type == VN_DIR);
_assert(name);
_assert(result);
struct ext2_inode *at_inode = at->fs_data;
_assert(at_inode);
struct fs *ext2 = at->fs;
_assert(ext2);
struct ext2_data *data = ext2->fs_private;
_assert(data);
char block_buffer[data->block_size];
uint32_t block_offset;
uint32_t dir_blocks = at_inode->size_lower / data->block_size;
size_t name_length = strlen(name);
int res;
// Because
_assert(name_length < 255);
for (uint32_t block_index = 0; block_index < dir_blocks; ++block_index) {
block_offset = 0;
if ((res = ext2_read_inode_block(ext2, at_inode, block_buffer, block_index)) != 0) {
return res;
}
while (block_offset < data->block_size) {
struct ext2_dirent *dirent = (struct ext2_dirent *) (block_buffer + block_offset);
if (dirent->ino && (dirent->name_length_low == name_length)) {
if (!strncmp(dirent->name, name, name_length)) {
// Found the dirent
struct ext2_inode *res_inode = kmalloc(data->inode_size);
_assert(res_inode);
struct vnode *node = vnode_create(VN_DIR, name);
if ((res = ext2_read_inode(ext2, res_inode, dirent->ino)) != 0) {
kfree(res_inode);
return res;
}
node->fs = ext2;
ext2_inode_to_vnode(node, res_inode, dirent->ino);
*result = node;
return 0;
}
}
block_offset += dirent->ent_size;
}
}
return -ENOENT;
}
static int ext2_vnode_open(struct ofile *fd, int opt) {
if ((opt & O_APPEND) && (opt & O_ACCMODE) == O_RDONLY) {
// Impossible, I guess
return -EINVAL;
}
if ((opt & O_ACCMODE) != O_RDONLY) {
// Not implemented
return -EROFS;
}
_assert(!(opt & O_DIRECTORY));
fd->pos = 0;
return 0;
}
static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count) {
_assert(fd);
_assert(buf);
struct vnode *node = fd->vnode;
_assert(node);
struct ext2_inode *inode = node->fs_data;
_assert(inode);
struct fs *ext2 = node->fs;
_assert(ext2);
struct ext2_data *data = ext2->fs_private;
_assert(data);
// TODO
_assert(!inode->size_upper);
if (fd->pos >= inode->size_lower) {
return 0;
}
char block_buffer[data->block_size];
size_t rem = MIN(count, inode->size_lower - fd->pos);
size_t bread = 0;
int res;
while (rem) {
size_t block_offset = fd->pos % data->block_size;
size_t can_read = MIN(rem, data->block_size - block_offset);
uint32_t block_index = fd->pos / data->block_size;
if ((res = ext2_read_inode_block(ext2, inode, block_buffer, block_index)) != 0) {
return res;
}
memcpy(buf, block_buffer + block_offset, can_read);
buf += can_read;
fd->pos += can_read;
bread += can_read;
rem -= can_read;
}
return bread;
}
static int ext2_vnode_opendir(struct ofile *fd) {
_assert(fd && fd->vnode);
_assert(fd->vnode->type == VN_DIR);
fd->pos = 0;
return 0;
}
static ssize_t ext2_vnode_readdir(struct ofile *fd, struct dirent *ent) {
_assert(fd);
_assert(ent);
struct vnode *node = fd->vnode;
_assert(node);
struct ext2_inode *inode = node->fs_data;
_assert(inode);
struct fs *ext2 = node->fs;
_assert(ext2);
struct ext2_data *data = ext2->fs_private;
_assert(data);
if (fd->pos >= inode->size_lower) {
return 0;
}
char block_buffer[data->block_size];
uint32_t block_index = fd->pos / data->block_size;
struct ext2_dirent *dirent = (struct ext2_dirent *) (block_buffer + fd->pos % data->block_size);
int res;
if ((res = ext2_read_inode_block(ext2, inode, block_buffer, block_index)) != 0) {
return res;
}
if (!dirent->ino) {
fd->pos += dirent->ent_size;
if (fd->pos / data->block_size != block_index) {
// Dirent reclen cannot point beyond block size
_assert(!(fd->pos % data->block_size));
// Requires reading a next block for dirent
return ext2_vnode_readdir(fd, ent);
}
// Don't think the scenario of two empty dirents following each other is likely
dirent = (struct ext2_dirent *) (block_buffer + fd->pos % data->block_size);
_assert(dirent->ino);
}
uint32_t name_length = dirent->name_length_low;
ent->d_type = DT_UNKNOWN;
if (data->sb.required_features & EXT2_REQ_ENT_TYPE) {
switch (dirent->type_indicator) {
case 1:
ent->d_type = DT_REG;
break;
case 2:
ent->d_type = DT_DIR;
break;
case 7:
ent->d_type = DT_LNK;
break;
default:
panic("Unsupported dirent type: %u\n", dirent->type_indicator);
}
} else {
name_length += (uint16_t) dirent->name_length_high << 8;
}
strncpy(ent->d_name, dirent->name, name_length);
ent->d_name[name_length] = 0;
ent->d_off = fd->pos;
ent->d_reclen = dirent->ent_size;
// Calculate next dirent position
fd->pos += dirent->ent_size;
return ent->d_reclen;
}
static int ext2_vnode_chmod(struct vnode *node, mode_t new_mode) {
_assert(node);
struct ext2_inode *inode = node->fs_data;
_assert(inode);
struct fs *ext2 = node->fs;
_assert(ext2);
// Only rewrite inode if something really changed
if ((inode->mode & 0xFFF) != (new_mode & 0xFFF)) {
// chmod() only updates file mode, don't touch file type
inode->mode &= ~0xFFF;
inode->mode |= new_mode & 0xFFF;
node->mode = new_mode & 0xFFF;
return ext2_write_inode(ext2, inode, node->ino);
}
return 0;
}
static int ext2_vnode_chown(struct vnode *node, uid_t new_uid, gid_t new_gid) {
_assert(node);
struct ext2_inode *inode = node->fs_data;
_assert(inode);
struct fs *ext2 = node->fs;
_assert(ext2);
if (new_uid != inode->uid || new_gid != inode->gid) {
inode->uid = new_uid;
inode->gid = new_gid;
node->uid = new_uid;
node->gid = new_gid;
return ext2_write_inode(ext2, inode, node->ino);
}
return 0;
}
static int ext2_vnode_stat(struct vnode *node, struct stat *st) {
_assert(node && st);
struct ext2_inode *inode = node->fs_data;
_assert(inode);
struct fs *ext2 = node->fs;
_assert(ext2);
struct ext2_data *data = ext2->fs_private;
_assert(data);
_assert(inode->uid == node->uid);
_assert(inode->gid == node->gid);
_assert((inode->mode & 0xFFF) == node->mode);
st->st_mode = inode->mode;
st->st_uid = inode->uid;
st->st_gid = inode->gid;
st->st_size = inode->size_lower;
_assert(!inode->size_upper);
st->st_blksize = data->block_size;
st->st_blocks = (st->st_size + data->block_size - 1) / data->block_size;
st->st_dev = 0;
st->st_rdev = 0;
st->st_mtime = inode->mtime;
st->st_atime = inode->atime;
st->st_ctime = inode->ctime;
st->st_nlink = inode->hard_links;
return 0;
}
////
// Map inode parameters to vnode fields
void ext2_inode_to_vnode(struct vnode *vnode, struct ext2_inode *inode, uint32_t ino) {
vnode->ino = ino;
vnode->uid = inode->uid;
vnode->gid = inode->gid;
vnode->mode = inode->mode & 0xFFF;
vnode->fs_data = inode;
vnode->op = &g_ext2_vnode_ops;
switch (inode->mode & 0xF000) {
case EXT2_IFDIR:
vnode->type = VN_DIR;
break;
case EXT2_IFREG:
vnode->type = VN_REG;
break;
default:
panic("Unsupported inode type: %04x\n", inode->mode & 0xF000);
}
}