Get rid of old ext2

This commit is contained in:
Mark
2020-01-22 15:36:58 +02:00
parent 92fe570c38
commit f648cd2b94
8 changed files with 0 additions and 1834 deletions
-5
View File
@@ -66,11 +66,6 @@ OBJS+=$(O)/sys/debug.o \
$(O)/sys/net/in.o \
$(O)/sys/net/netdev.o \
$(O)/sys/vfs/pseudo.o \
$(O)/sys/vfs/ext2/ext2.o \
$(O)/sys/vfs/ext2/ext2vnop.o \
$(O)/sys/vfs/ext2/ext2blk.o \
$(O)/sys/vfs/ext2/ext2alloc.o \
$(O)/sys/vfs/ext2/ext2dir.o \
$(O)/sys/vfs/tar.o \
$(O)/sys/blk/ram.o \
$(O)/sys/tty.o \
-149
View File
@@ -1,149 +0,0 @@
#pragma once
#include <stdint.h>
#include "fs.h"
#define EXT2_MAGIC ((uint16_t) 0xEF53)
#define EXT2_SBSIZ 1024
#define EXT2_SBOFF 1024
#define EXT2_ROOTINO 2
#define EXT2_GOOD ((uint16_t) 1)
#define EXT2_BAD ((uint16_t) 2)
#define EXT2_EACT_IGN ((uint16_t) 1)
#define EXT2_EACT_REM ((uint16_t) 2)
#define EXT2_EACT_PAN ((uint16_t) 3)
#define EXT2_TYPE_REG ((uint16_t) 0x8000)
#define EXT2_TYPE_DIR ((uint16_t) 0x4000)
#define EXT2_TYPE_LNK ((uint16_t) 0xA000)
struct ext2_sb {
uint32_t inode_count;
uint32_t block_count;
uint32_t su_reserved;
uint32_t free_block_count;
uint32_t free_inode_count;
uint32_t sb_block_number;
uint32_t block_size_log;
uint32_t frag_size_log;
uint32_t block_group_size_blocks;
uint32_t block_group_size_frags;
uint32_t block_group_size_inodes;
uint32_t last_mount_time;
uint32_t last_mtime;
uint16_t mount_count_since_fsck;
uint16_t mount_max_before_fsck;
uint16_t magic;
uint16_t fs_state;
uint16_t error_action;
uint16_t version_minor;
uint32_t last_fsck_time;
uint32_t os_id;
uint32_t version_major;
uint16_t su_uid;
uint16_t su_gid;
uint32_t first_non_reserved;
uint16_t inode_struct_size;
uint16_t backup_group_number;
uint32_t optional_features;
uint32_t required_features;
uint32_t ro_required_features;
char fsid[16];
char volname[16];
char last_mount_path[64];
uint32_t compression;
uint8_t prealloc_file_block_number;
uint8_t prealloc_dir_block_number;
uint16_t __un0;
char journal_id[16];
uint32_t journal_inode;
uint32_t journal_dev;
uint32_t orphan_inode_head;
} __attribute__((packed));
struct ext2_info {
union {
char __sb_data[1024];
struct ext2_sb sb;
};
// driver-specific info
uint32_t block_size;
uint32_t block_group_count;
uint32_t block_group_descriptor_table_block;
uint32_t block_group_descriptor_table_size_blocks;
struct ext2_grp_desc *block_group_descriptor_table;
};
struct ext2_grp_desc {
uint32_t block_usage_bitmap_block;
uint32_t inode_usage_bitmap_block;
uint32_t inode_table_block;
uint16_t free_blocks;
uint16_t free_inodes;
uint16_t dir_count;
char __pad[14];
} __attribute__((packed));
struct ext2_inode {
uint16_t type_perm;
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_link_count;
uint32_t disk_sector_count;
uint32_t flags;
uint32_t os_value_1;
uint32_t direct_blocks[12];
uint32_t l1_indirect_block;
uint32_t l2_indirect_block;
uint32_t l3_indirect_block;
uint32_t gen_number;
uint32_t acl;
union {
uint32_t dir_acl;
uint32_t size_upper;
};
uint32_t frag_block_addr;
char os_value_2[12];
} __attribute__((packed));
struct ext2_dirent {
uint32_t ino;
uint16_t len;
uint8_t name_len;
uint8_t type_ind;
char name[];
} __attribute__((packed));
void ext2_class_init(void);
enum vnode_type ext2_inode_type(struct ext2_inode *i);
// Implemented in ext2blk.c
int ext2_write_superblock(struct fs *ext2);
int ext2_read_block(struct fs *ext2, uint32_t block_no, void *buf);
int ext2_write_block(struct fs *ext2, uint32_t block_no, const void *buf);
int ext2_read_inode_block(struct fs *ext2, struct ext2_inode *inode, uint32_t index, void *buf);
int ext2_write_inode_block(struct fs *ext2, struct ext2_inode *inode, uint32_t index, const void *buf);
int ext2_read_inode(struct fs *ext2, struct ext2_inode *inode, uint32_t ino);
int ext2_write_inode(struct fs *ext2, const struct ext2_inode *inode, uint32_t ino);
// Implemented in ext2alloc.c
int ext2_alloc_block(struct fs *ext2, uint32_t *block_no);
int ext2_free_block(struct fs *ext2, uint32_t block_no);
int ext2_inode_alloc_block(struct fs *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index);
int ext2_free_inode_block(struct fs *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index);
int ext2_free_inode(struct fs *ext2, uint32_t ino);
int ext2_alloc_inode(struct fs *ext2, uint32_t *ino);
// Implemented in ext2dir.c
int ext2_dir_add_inode(struct fs *ext2, struct vnode *dir, const char *name, uint32_t ino);
int ext2_dir_remove_inode(struct fs *ext2, struct vnode *dir, const char *name, uint32_t ino);
extern struct vnode_operations ext2_vnode_ops;
-1
View File
@@ -5,7 +5,6 @@
#include "sys/binfmt_elf.h"
#include "sys/amd64/cpu.h"
#include "sys/config.h"
#include "sys/fs/ext2.h"
#include "sys/vmalloc.h"
#include "sys/string.h"
#include "sys/assert.h"
-157
View File
@@ -1,157 +0,0 @@
#include "sys/fs/ext2.h"
#include "sys/fs/vfs.h"
#include "sys/debug.h"
#include "sys/panic.h"
#include "sys/errno.h"
#include "sys/heap.h"
enum vnode_type ext2_inode_type(struct ext2_inode *i) {
uint16_t v = i->type_perm & 0xF000;
switch (v) {
case EXT2_TYPE_DIR:
return VN_DIR;
case EXT2_TYPE_REG:
return VN_REG;
case EXT2_TYPE_LNK:
return VN_LNK;
default:
// fprintf(stderr, "Unknown file type: %x\n", v);
// abort();
panic("Unknown file type: %x\n", v);
return 0;
}
}
static int ext2_fs_init(struct fs *fs, const char *opt) {
int res;
kdebug("ext2_fs_init()\n");
struct ext2_info *info;
// ext2's private data is its superblock structure
info = kmalloc(sizeof(struct ext2_info));
fs->fs_private = info;
// Read the superblock from blkdev
if ((res = blk_read(fs->blk, info, EXT2_SBOFF, EXT2_SBSIZ)) != EXT2_SBSIZ) {
kfree(fs->fs_private);
kerror("ext2: superblock read failed\n");
return -EINVAL;
}
// Check if superblock is ext2
if (info->sb.magic != EXT2_MAGIC) {
kfree(info);
kdebug("ext2: magic mismatch\n");
return -EINVAL;
}
// Check if we have an extended ext2 sb
if (info->sb.version_major == 0) {
// Initialize params which are missing in non-extended sbs
info->sb.inode_struct_size = 128;
info->sb.first_non_reserved = 11;
}
info->block_size = 1024 << info->sb.block_size_log;
// Load block group descriptor table
// Get descriptor table size
uint32_t block_group_descriptor_table_length = info->sb.block_count / info->sb.block_group_size_blocks;
if (block_group_descriptor_table_length * info->sb.block_group_size_blocks < info->sb.block_count) {
++block_group_descriptor_table_length;
}
info->block_group_count = block_group_descriptor_table_length;
uint32_t block_group_descriptor_table_size_blocks = 32 * block_group_descriptor_table_length /
info->block_size + 1;
uint32_t block_group_descriptor_table_block = 2;
if (info->block_size > 1024) {
block_group_descriptor_table_block = 1;
}
info->block_group_descriptor_table_block = block_group_descriptor_table_block;
info->block_group_descriptor_table_size_blocks = block_group_descriptor_table_size_blocks;
// Load all block group descriptors into memory
kdebug("Allocating %u bytes for BGDT\n", info->block_group_descriptor_table_size_blocks * info->block_size);
info->block_group_descriptor_table = kmalloc(info->block_group_descriptor_table_size_blocks * info->block_size);
for (size_t i = 0; i < info->block_group_descriptor_table_size_blocks; ++i) {
ext2_read_block(fs, i + info->block_group_descriptor_table_block,
(void *) (((uintptr_t) info->block_group_descriptor_table) + i * info->block_size));
}
return 0;
}
static int ext2_fs_umount(struct fs *fs) {
struct ext2_info *info = fs->fs_private;
// Free block group descriptor table
kfree(info->block_group_descriptor_table);
// Free superblock
kfree(info);
return 0;
}
static struct vnode *ext2_fs_get_root(struct fs *fs) {
struct ext2_info *info = fs->fs_private;
kdebug("ext2_fs_get_root()\n");
struct ext2_inode *inode = kmalloc(info->sb.inode_struct_size);
// Read root inode (2)
if (ext2_read_inode(fs, inode, EXT2_ROOTINO) != 0) {
kfree(inode);
return NULL;
}
struct vnode *res = vnode_create(VN_DIR, NULL);
res->ino = EXT2_ROOTINO;
res->fs = fs;
res->fs_data = inode;
res->op = &ext2_vnode_ops;
res->type = ext2_inode_type(inode);
res->uid = inode->uid;
res->gid = inode->gid;
res->mode = inode->type_perm;
return res;
}
static int ext2_fs_statvfs(struct fs *fs, struct statvfs *st) {
struct ext2_info *info = fs->fs_private;
st->f_blocks = info->sb.block_count;
st->f_bfree = info->sb.free_block_count;
st->f_bavail = info->sb.block_count - info->sb.su_reserved;
st->f_files = info->sb.inode_count;
st->f_ffree = info->sb.free_inode_count;
st->f_favail = info->sb.inode_count - info->sb.first_non_reserved + 1;
st->f_bsize = info->block_size;
st->f_frsize = info->block_size;
// XXX: put something here
st->f_fsid = 0;
st->f_flag = 0;
st->f_namemax = 256;
return 0;
}
static struct fs_class ext2_class = {
.name = "ext2",
.get_root = ext2_fs_get_root,
.init = ext2_fs_init,
.mount = NULL,
.umount = ext2_fs_umount,
.statvfs = ext2_fs_statvfs
};
void ext2_class_init(void) {
fs_class_register(&ext2_class);
}
-345
View File
@@ -1,345 +0,0 @@
// ext2fs block/inode alloc/free
#include "sys/fs/ext2.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/debug.h"
#include "sys/errno.h"
// #include <assert.h>
// #include <stdlib.h>
// #include <errno.h>
// #include <stdio.h>
int ext2_alloc_block(struct fs *ext2, uint32_t *block_no) {
struct ext2_info *info = ext2->fs_private;
char block_buffer[info->block_size];
uint32_t res_block_no = 0;
uint32_t res_group_no = 0;
uint32_t res_block_no_in_group = 0;
int found = 0;
int res;
for (size_t i = 0; i < info->block_group_count; ++i) {
if (info->block_group_descriptor_table[i].free_blocks > 0) {
// Found a free block here
kdebug("Allocating a block in group #%u\n", i);
if ((res = ext2_read_block(ext2,
info->block_group_descriptor_table[i].block_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
for (size_t j = 0; j < info->block_size / sizeof(uint64_t); ++j) {
uint64_t qw = ((uint64_t *) block_buffer)[j];
if (qw != (uint64_t) -1) {
for (size_t k = 0; k < 64; ++k) {
if (!(qw & (1 << k))) {
res_block_no_in_group = k + j * 64;
res_group_no = i;
// XXX: had to increment the resulting block_no
// because for some reason linux's ext2
// impl hasn't marked #531 as used in one
// case, but it was actually a L1-indirect
// block. So I just had to make it allocate
// #532 instead as a workaround (though
// block numbering should start with 0)
res_block_no = res_block_no_in_group + i * info->sb.block_group_size_blocks + 1;
found = 1;
break;
}
}
if (found) {
break;
}
}
}
if (found) {
break;
}
}
}
if (!found) {
return -ENOSPC;
}
// Write block usage bitmap
((uint64_t *) block_buffer)[res_block_no_in_group / 64] |= (1 << (res_block_no_in_group % 64));
if ((res = ext2_write_block(ext2,
info->block_group_descriptor_table[res_group_no].block_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Update BGDT
--info->block_group_descriptor_table[res_group_no].free_blocks;
for (size_t i = 0; i < info->block_group_descriptor_table_size_blocks; ++i) {
void *blk_ptr = (void *) (((uintptr_t) info->block_group_descriptor_table) + i * info->block_size);
if ((res = ext2_write_block(ext2, info->block_group_descriptor_table_block + i, blk_ptr)) < 0) {
return res;
}
}
// Update global block count and flush superblock
--info->sb.free_block_count;
if ((res = ext2_write_superblock(ext2)) < 0) {
return res;
}
*block_no = res_block_no;
kdebug("Allocated block #%u\n", res_block_no);
return 0;
}
int ext2_free_block(struct fs *ext2, uint32_t block_no) {
_assert(block_no);
struct ext2_info *info = ext2->fs_private;
char block_buffer[info->block_size];
int res;
uint32_t block_group_no = (block_no - 1) / info->sb.block_group_size_blocks;
uint32_t block_no_in_group = (block_no - 1) % info->sb.block_group_size_blocks;
// Read block ussge bitmap block
if ((res = ext2_read_block(ext2,
info->block_group_descriptor_table[block_group_no].block_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Update the bitmap
_assert(((uint64_t *) block_buffer)[block_no_in_group / 64] & (1 << (block_no_in_group % 64)));
((uint64_t *) block_buffer)[block_no_in_group / 64] &= ~(1 << (block_no_in_group % 64));
if ((res = ext2_write_block(ext2,
info->block_group_descriptor_table[block_group_no].block_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Update BGDT
++info->block_group_descriptor_table[block_group_no].free_blocks;
for (size_t i = 0; i < info->block_group_descriptor_table_size_blocks; ++i) {
void *blk_ptr = (void *) (((uintptr_t) info->block_group_descriptor_table) + i * info->block_size);
if ((res = ext2_write_block(ext2, info->block_group_descriptor_table_block + i, blk_ptr)) < 0) {
return res;
}
}
// Update global block count
++info->sb.free_block_count;
if ((res = ext2_write_superblock(ext2)) < 0) {
return res;
}
kdebug("Freed block #%u\n", block_no);
return 0;
}
int ext2_inode_alloc_block(struct fs *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index) {
struct ext2_info *info = ext2->fs_private;
int res;
uint32_t block_no;
// Allocate the block itself
if ((res = ext2_alloc_block(ext2, &block_no)) < 0) {
return res;
}
if (index < 12) {
// Write direct block list entry
inode->direct_blocks[index] = block_no;
} else if (index < 12 + (info->block_size / 4)) {
// Also allocate L1 indirection block if needed
char buf[info->block_size];
if (!inode->l1_indirect_block) {
uint32_t l1_block;
if ((res = ext2_alloc_block(ext2, &l1_block)) < 0) {
return res;
}
inode->l1_indirect_block = l1_block;
_assert(inode->l1_indirect_block);
memset(buf, 0, info->block_size);
} else {
// Read indirection block
if ((res = ext2_read_block(ext2, inode->l1_indirect_block, buf)) < 0) {
return res;
}
}
((uint32_t *) buf)[index - 12] = block_no;
// Write the block back
if ((res = ext2_write_block(ext2, inode->l1_indirect_block, buf)) < 0) {
return res;
}
} else {
panic("Inode block index has too high indirection level\n");
}
// Flush changes to the device
return ext2_write_inode(ext2, inode, ino);
}
//int ext2_free_inode_block(struct fs *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index) {
// if (index >= 12) {
// panic("Not implemented\n");
// }
// // All sanity checks regarding whether the block is present
// // at all are left to the caller
//
// int res;
// uint32_t block_no;
//
// // Get block number
// block_no = inode->direct_blocks[index];
//
// // Free the block
// if ((res = ext2_free_block(ext2, block_no)) < 0) {
// return res;
// }
//
// // Write updated inode
// inode->direct_blocks[index] = 0;
// return ext2_write_inode(ext2, inode, ino);
//}
int ext2_free_inode(struct fs *ext2, uint32_t ino) {
_assert(ino);
struct ext2_info *info = ext2->fs_private;
char block_buffer[info->block_size];
uint32_t ino_block_group_number = (ino - 1) / info->sb.block_group_size_inodes;
uint32_t ino_inode_index_in_group = (ino - 1) % info->sb.block_group_size_inodes;
int res;
// Read inode usage block
if ((res = ext2_read_block(ext2,
info->block_group_descriptor_table[ino_block_group_number].inode_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Remove usage bit
_assert(((uint64_t *) block_buffer)[ino_inode_index_in_group / 64] & (1 << (ino_inode_index_in_group % 64)));
((uint64_t *) block_buffer)[ino_inode_index_in_group / 64] &= ~(1 << (ino_inode_index_in_group % 64));
// Write modified bitmap back
if ((res = ext2_write_block(ext2,
info->block_group_descriptor_table[ino_block_group_number].inode_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Increment free inode count in BGDT entry and write it back
// TODO: this code is repetitive and maybe should be moved to
// ext2_bgdt_inode_inc/_dec()
++info->block_group_descriptor_table[ino_block_group_number].free_inodes;
for (size_t i = 0; i < info->block_group_descriptor_table_size_blocks; ++i) {
void *blk_ptr = (void *) (((uintptr_t) info->block_group_descriptor_table) + i * info->block_size);
if ((res = ext2_write_block(ext2, info->block_group_descriptor_table_block + i, blk_ptr)) < 0) {
return res;
}
}
// Update global inode count
++info->sb.free_inode_count;
if ((res = ext2_write_superblock(ext2)) < 0) {
return res;
}
kdebug("Freed inode #%u\n", ino);
return 0;
}
int ext2_alloc_inode(struct fs *ext2, uint32_t *ino) {
struct ext2_info *info = ext2->fs_private;
char block_buffer[info->block_size];
uint32_t res_ino = 0;
uint32_t res_group_no = 0;
uint32_t res_ino_number_in_group = 0;
int res;
// Look through BGDT to find any block groups with free inodes
for (size_t i = 0; i < info->block_group_count; ++i) {
if (info->block_group_descriptor_table[i].free_inodes > 0) {
// Found a block group with free inodes
kdebug("Allocating an inode inside block group #%u\n", i);
// Read inode usage bitmap
if ((res = ext2_read_block(ext2,
info->block_group_descriptor_table[i].inode_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Find a free bit
// Think this should be fine on amd64
for (size_t j = 0; j < info->block_size / sizeof(uint64_t); ++j) {
// Get bitmap qword
uint64_t qw = ((uint64_t *) block_buffer)[j];
// If not all bits are set in this qword, find exactly which one
if (qw != ((uint64_t) -1)) {
for (size_t k = 0; k < 64; ++k) {
if (!(qw & (1 << k))) {
res_ino_number_in_group = k + j * 64;
res_group_no = i;
res_ino = res_ino_number_in_group + i * info->sb.block_group_size_inodes + 1;
break;
}
}
if (res_ino) {
break;
}
}
if (res_ino) {
break;
}
}
}
if (res_ino) {
break;
}
}
if (res_ino == 0) {
return -ENOSPC;
}
// Write updated bitmap
((uint64_t *) block_buffer)[res_ino_number_in_group / 64] |= (1 << (res_ino_number_in_group % 64));
if ((res = ext2_write_block(ext2,
info->block_group_descriptor_table[res_group_no].inode_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Write updated BGDT
--info->block_group_descriptor_table[res_group_no].free_inodes;
for (size_t i = 0; i < info->block_group_descriptor_table_size_blocks; ++i) {
void *blk_ptr = (void *) (((uintptr_t) info->block_group_descriptor_table) + i * info->block_size);
if ((res = ext2_write_block(ext2, info->block_group_descriptor_table_block + i, blk_ptr)) < 0) {
return res;
}
}
// Update global inode count and flush superblock
--info->sb.free_inode_count;
if ((res = ext2_write_superblock(ext2)) < 0) {
return res;
}
*ino = res_ino;
return 0;
}
-133
View File
@@ -1,133 +0,0 @@
#include "sys/fs/ext2.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/errno.h"
#include "sys/debug.h"
#define ext2_super(e) ((struct ext2_info *) (e)->fs_private)
int ext2_write_superblock(struct fs *ext2) {
struct ext2_info *info = ext2->fs_private;
return blk_write(ext2->blk, info, EXT2_SBOFF, EXT2_SBSIZ);
}
int ext2_read_block(struct fs *ext2, uint32_t block_no, void *buf) {
if (!block_no) {
return -1;
}
//printf("ext2_read_block %u\n", block_no);
int res = blk_read(ext2->blk, buf, block_no * ext2_super(ext2)->block_size, ext2_super(ext2)->block_size);
if (res < 0) {
//fprintf(stderr, "ext2: Failed to read %uth block\n", block_no);
kerror("ext2: Failed to read %uth block\n", block_no);
}
return res;
}
int ext2_write_block(struct fs *ext2, uint32_t block_no, const void *buf) {
if (!block_no) {
return -1;
}
int res = blk_write(ext2->blk, buf, block_no * ext2_super(ext2)->block_size, ext2_super(ext2)->block_size);
if (res < 0) {
//fprintf(stderr, "ext2: Failed to write %uth block\n", block_no);
kerror("ext2: Failed to write %uth block\n", block_no);
}
return res;
}
static uint32_t ext2_get_inode_block(struct fs *ext2, struct ext2_inode *inode, uint32_t index) {
if (index < 12) {
return inode->direct_blocks[index];
} else {
struct ext2_info *info = ext2->fs_private;
char buf[1024];
int res;
if (index < 12 + (info->block_size / 4)) {
if ((res = ext2_read_block(ext2, inode->l1_indirect_block, buf)) < 0) {
return res;
}
return ((uint32_t *) buf)[index - 12];
} else {
panic("Inode block index has too high indirection level\n");
}
}
}
int ext2_write_inode_block(struct fs *ext2, struct ext2_inode *inode, uint32_t index, const void *buf) {
uint32_t block_number = ext2_get_inode_block(ext2, inode, index);
return ext2_write_block(ext2, block_number, buf);
}
int ext2_read_inode_block(struct fs *ext2, struct ext2_inode *inode, uint32_t index, void *buf) {
uint32_t block_number = ext2_get_inode_block(ext2, inode, index);
return ext2_read_block(ext2, block_number, buf);
}
int ext2_read_inode(struct fs *ext2, struct ext2_inode *inode, uint32_t ino) {
struct ext2_info *info = ext2->fs_private;
//printf("ext2_read_inode %d\n", ino);
char inode_block_buffer[info->block_size];
uint32_t ino_block_group_number = (ino - 1) / info->sb.block_group_size_inodes;
//printf("inode block group number = %d\n", ino_block_group_number);
uint32_t ino_inode_table_block = info->block_group_descriptor_table[ino_block_group_number].inode_table_block;
//printf("inode table is at block %d\n", ino_inode_table_block);
uint32_t ino_inode_index_in_group = (ino - 1) % info->sb.block_group_size_inodes;
//printf("inode entry index in the group = %d\n", ino_inode_index_in_group);
uint32_t ino_inode_block_in_group = (ino_inode_index_in_group * info->sb.inode_struct_size) / info->block_size;
//printf("inode entry offset is %d blocks\n", ino_inode_block_in_group);
uint32_t ino_inode_block_number = ino_inode_block_in_group + ino_inode_table_block;
//printf("inode block number is %uth block\n", ino_inode_block_number);
//struct ext2_inode *root_inode_block_inode_table = (struct ext2_inode *) root_inode_block_buf;
if (ext2_read_block(ext2, ino_inode_block_number, inode_block_buffer) < 0) {
kerror("ext2: failed to load inode#%d block\n", ino);
return -1;
}
uint32_t ino_entry_in_block = (ino_inode_index_in_group * info->sb.inode_struct_size) % info->block_size;
memcpy(inode, &inode_block_buffer[ino_entry_in_block], info->sb.inode_struct_size);
return 0;
}
int ext2_write_inode(struct fs *ext2, const struct ext2_inode *inode, uint32_t ino) {
struct ext2_info *info = ext2->fs_private;
//printf("ext2_read_inode %d\n", ino);
char inode_block_buffer[info->block_size];
int res;
uint32_t ino_block_group_number = (ino - 1) / info->sb.block_group_size_inodes;
//printf("inode block group number = %d\n", ino_block_group_number);
uint32_t ino_inode_table_block = info->block_group_descriptor_table[ino_block_group_number].inode_table_block;
//printf("inode table is at block %d\n", ino_inode_table_block);
uint32_t ino_inode_index_in_group = (ino - 1) % info->sb.block_group_size_inodes;
//printf("inode entry index in the group = %d\n", ino_inode_index_in_group);
uint32_t ino_inode_block_in_group = (ino_inode_index_in_group * info->sb.inode_struct_size) / info->block_size;
//printf("inode entry offset is %d blocks\n", ino_inode_block_in_group);
uint32_t ino_inode_block_number = ino_inode_block_in_group + ino_inode_table_block;
//printf("inode block number is %uth block\n", ino_inode_block_number);
// Need to read the block to modify it
if ((res = ext2_read_block(ext2, ino_inode_block_number, inode_block_buffer)) < 0) {
return res;
}
uint32_t ino_entry_in_block = (ino_inode_index_in_group * info->sb.inode_struct_size) % info->block_size;
memcpy(&inode_block_buffer[ino_entry_in_block], inode, info->sb.inode_struct_size);
// Write the block back
if ((res = ext2_write_block(ext2, ino_inode_block_number, inode_block_buffer)) < 0) {
return res;
}
return 0;
}
-177
View File
@@ -1,177 +0,0 @@
// ext2fs directory content operations
#include "sys/fs/ext2.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/debug.h"
#include "sys/errno.h"
// #include <string.h>
// #include <stdlib.h>
// #include <assert.h>
// #include <errno.h>
// #include <stdio.h>
// Add an inode to directory
int ext2_dir_add_inode(struct fs *ext2, struct vnode *dir, const char *name, uint32_t ino) {
struct ext2_info *info = ext2->fs_private;
char block_buffer[info->block_size];
struct ext2_inode *dir_inode = dir->fs_data;
struct ext2_dirent *current_dirent, *result_dirent;
int res;
size_t req_free = strlen(name) + sizeof(struct ext2_dirent);
// Align up 4 bytes
req_free = (req_free + 3) & ~3;
// Try reading parent dirent blocks to see if any has
// some space to fit our file
size_t dir_size_blocks = (dir_inode->size_lower + info->block_size - 1) / info->block_size;
for (size_t i = 0; i < dir_size_blocks; ++i) {
current_dirent = NULL;
result_dirent = NULL;
size_t off = 0;
// Read directory content block
if ((res = ext2_read_inode_block(ext2, dir_inode, i, block_buffer)) < 0) {
return res;
}
// Check if any of the entries can be split to fit our entry
while (off < info->block_size) {
current_dirent = (struct ext2_dirent *) &block_buffer[off];
if (current_dirent->ino == 0) {
kwarn("ext2: found dirent with ino = 0\n");
}
// Check how much space we need to still store the entry
size_t real_len = current_dirent->name_len + sizeof(struct dirent);
real_len = (real_len + 3) & ~3;
// And check how much is left to fit our entry
if (real_len < current_dirent->len /* Sanity? */ &&
current_dirent->len - real_len >= req_free) {
// Yay, can fit our dirent in there
// Sanity check that we're aligned properly
_assert(((off + real_len) & 3) == 0);
result_dirent = (struct ext2_dirent *) &block_buffer[off + real_len];
result_dirent->len = current_dirent->len - real_len;
result_dirent->name_len = strlen(name);
result_dirent->type_ind = 0;
result_dirent->ino = ino;
strncpy(result_dirent->name, name, result_dirent->name_len);
current_dirent->len = real_len;
if ((res = ext2_write_inode_block(ext2, dir_inode, i, block_buffer)) < 0) {
return res;
}
return 0;
}
off += current_dirent->len;
}
}
dir_inode->size_lower += info->block_size;
if ((res = ext2_inode_alloc_block(ext2, dir_inode, dir->ino, dir_size_blocks)) < 0) {
dir_inode->size_lower -= info->block_size;
return res;
}
memset(block_buffer, 0, info->block_size);
current_dirent = (struct ext2_dirent *) block_buffer;
current_dirent->ino = ino;
current_dirent->len = info->block_size;
current_dirent->name_len = strlen(name);
current_dirent->type_ind = 0;
strncpy(current_dirent->name, name, current_dirent->name_len);
return ext2_write_inode_block(ext2, dir_inode, dir_size_blocks, block_buffer);
}
// Not only free the block itself, but also remove it from index list
static int ext2_free_block_index(struct fs *ext2, struct ext2_inode *inode, uint32_t index, uint32_t ino, size_t sz) {
if (index >= 12) {
// TODO: Implement this
panic("Not implemented\n");
}
int res;
uint32_t block_no = inode->direct_blocks[index];
if ((res = ext2_free_block(ext2, block_no)) < 0) {
return res;
}
// Shift direct indexed blocks
for (uint32_t i = index; i < 11; ++i) {
inode->direct_blocks[i] = inode->direct_blocks[i + 1];
}
// TODO: inode->direct_blocks[11] becomes the first block of indirect block
inode->direct_blocks[11] = 0;
inode->size_lower -= sz;
return ext2_write_inode(ext2, inode, ino);
}
int ext2_dir_remove_inode(struct fs *ext2, struct vnode *dir, const char *name, uint32_t ino) {
struct ext2_info *info = ext2->fs_private;
char block_buffer[info->block_size];
struct ext2_inode *dir_inode = dir->fs_data;
struct ext2_dirent *current_dirent, *prev_dirent;
int res;
size_t dir_size_blocks = (dir_inode->size_lower + info->block_size - 1) / info->block_size;
for (size_t i = 0; i < dir_size_blocks; ++i) {
if ((res = ext2_read_inode_block(ext2, dir_inode, i, block_buffer)) < 0) {
return res;
}
size_t off = 0;
current_dirent = NULL;
prev_dirent = NULL;
while (off < info->block_size) {
prev_dirent = current_dirent;
current_dirent = (struct ext2_dirent *) &block_buffer[off];
if (current_dirent->ino == 0) {
kwarn("ext2: found dirent with ino = 0\n");
}
if (current_dirent->name_len == strlen(name) &&
!strncmp(current_dirent->name, name, current_dirent->name_len)) {
// Found matching dirent
// Sanity
_assert(current_dirent->ino == ino);
if (current_dirent->len + off >= info->block_size) {
// It's the last node in the list
if (!prev_dirent) {
return ext2_free_block_index(ext2, dir_inode, i, dir->ino, info->block_size);
}
// Resize the previous node
prev_dirent->len += current_dirent->len;
return ext2_write_inode_block(ext2, dir_inode, i, block_buffer);
} else {
// It's not the last one - relocate the next entry
uint32_t len = current_dirent->len;
struct ext2_dirent *next_dirent = (struct ext2_dirent *) &block_buffer[off + len];
memmove(current_dirent, next_dirent, next_dirent->len);
next_dirent = current_dirent;
next_dirent->len += len;
_assert(((off + len) & 3) == 0);
return ext2_write_inode_block(ext2, dir_inode, i, block_buffer);
}
}
off += current_dirent->len;
}
}
return -EIO;
}
-867
View File
@@ -1,867 +0,0 @@
// ext2fs vnode operations
#include "sys/fs/ext2.h"
#include "sys/fs/node.h"
#include "sys/fs/ofile.h"
#include "sys/fcntl.h"
#include "sys/fs/vfs.h"
#include "sys/string.h"
#include "sys/debug.h"
#include "sys/assert.h"
#include "sys/panic.h"
#include "sys/errno.h"
#include "sys/time.h"
#include "sys/heap.h"
// #include <string.h>
// #include <stddef.h>
// #include <stdlib.h>
// #include <assert.h>
// #include <stdio.h>
// #include <errno.h>
// Forward declaration of ext2 vnode functions
static int ext2_vnode_find(struct vnode *vn, const char *name, struct vnode **resvn);
static int ext2_vnode_creat(struct vnode *at, const char *name, uid_t uid, gid_t gid, mode_t mode);
static int ext2_vnode_mkdir(struct vnode *at, const char *name, uid_t uid, gid_t gid, mode_t mode);
static int ext2_vnode_open(struct ofile *fd, int opt);
static int ext2_vnode_opendir(struct ofile *fd);
static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count);
static ssize_t ext2_vnode_write(struct ofile *fd, const void *buf, size_t count);
static int ext2_vnode_truncate(struct vnode *vn, size_t length);
static ssize_t ext2_vnode_readdir(struct ofile *fd, struct dirent *ent);
//static void ext2_vnode_destroy(vnode_t *vn);
static int ext2_vnode_stat(struct vnode *vn, struct stat *st);
static int ext2_vnode_chmod(struct vnode *vn, mode_t mode);
static int ext2_vnode_chown(struct vnode *vn, uid_t uid, gid_t gid);
static int ext2_vnode_unlink(struct vnode *vn);
//static int ext2_vnode_access(vnode_t *vn, uid_t *uid, gid_t *gid, mode_t *mode);
static int ext2_vnode_readlink(struct vnode *vn, char *dst, size_t lim);
//static int ext2_vnode_symlink(vnode_t *at, struct vfs_ioctx *ctx, const char *name, const char *dst);
static off_t ext2_vnode_lseek(struct ofile *fd, off_t offset, int whence);
struct vnode_operations ext2_vnode_ops = {
.find = ext2_vnode_find,
.creat = ext2_vnode_creat,
.lseek = ext2_vnode_lseek,
.mkdir = ext2_vnode_mkdir,
// .destroy = ext2_vnode_destroy,
//
.readlink = ext2_vnode_readlink,
// .symlink = ext2_vnode_symlink,
//
.chmod = ext2_vnode_chmod,
.chown = ext2_vnode_chown,
.stat = ext2_vnode_stat,
.unlink = ext2_vnode_unlink,
// .access = ext2_vnode_access,
//
.opendir = ext2_vnode_opendir,
.readdir = ext2_vnode_readdir,
.open = ext2_vnode_open,
.read = ext2_vnode_read,
.write = ext2_vnode_write,
.truncate = ext2_vnode_truncate,
};
//// vnode function implementation
static int ext2_vnode_find(struct vnode *vn, const char *name, struct vnode **res) {
struct fs *ext2 = vn->fs;
struct ext2_info *info = vn->fs->fs_private;
struct ext2_inode *inode = vn->fs_data;
char buffer[info->block_size];
struct ext2_dirent *dirent = NULL;
size_t block_count = (inode->size_lower + (info->block_size - 1)) / info->block_size;
// char ent_name[256];
size_t index = 0;
while (index < block_count) {
// Read directory contents block
if (ext2_read_inode_block(ext2, inode, index, buffer) < 0) {
return -EIO;
}
size_t offset = 0;
while (1) {
dirent = (struct ext2_dirent *) &buffer[offset];
if (!dirent->len) {
break;
}
if (dirent->ino) {
if (strlen(name) == dirent->name_len && !strncmp(dirent->name, name, dirent->name_len)) {
// Found the entry
inode = kmalloc(info->sb.inode_struct_size);
if (ext2_read_inode(ext2, inode, dirent->ino) != 0) {
return -EIO;
}
struct vnode *node = vnode_create(ext2_inode_type(inode), name);
node->fs = ext2;
node->fs_data = inode;
node->ino = dirent->ino;
node->op = &ext2_vnode_ops;
node->mode = inode->type_perm & VFS_MODE_MASK;
node->uid = inode->uid;
node->gid = inode->gid;
node->open_count = 0;
*res = node;
return 0;
}
}
offset += dirent->len;
if (offset >= info->block_size) {
break;
}
}
++index;
}
return -ENOENT;
}
static int ext2_vnode_opendir(struct ofile *fd) {
_assert(fd);
_assert(fd->vnode);
_assert(fd->vnode->type == VN_DIR);
fd->pos = 0;
return 0;
}
static int ext2_vnode_open(struct ofile *fd, int opt) {
_assert(fd);
_assert(fd->vnode);
_assert(fd->vnode->type == VN_REG);
if (opt & O_APPEND) {
struct ext2_inode *inode = (struct ext2_inode *) fd->vnode->fs_data;
_assert(inode);
fd->pos = inode->size_lower;
} else {
fd->pos = 0;
}
return 0;
}
static int ext2_vnode_mkdir(struct vnode *at, const char *name, uid_t uid, gid_t gid, mode_t mode) {
struct fs *ext2 = at->fs;
_assert(at->type == VN_DIR);
struct ext2_info *info = ext2->fs_private;
char block_buffer[info->block_size];
uint32_t new_ino, new_block_no;
int res;
// Allocate a new inode for the directory
if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) {
kerror("ext2: Failed to allocate an inode\n");
return res;
}
// Allocate a block for "." and ".." entries
if ((res = ext2_alloc_block(ext2, &new_block_no)) < 0) {
kerror("ext2: Failed to allocate a block\n");
return res;
}
struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(info->sb.inode_struct_size);
// Now create an entry in parents dirent list
if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) {
return res;
}
// Fill the inode
ent_inode->flags = 0;
ent_inode->dir_acl = 0;
ent_inode->frag_block_addr = 0;
ent_inode->gen_number = 0;
ent_inode->hard_link_count = 1;
ent_inode->acl = 0;
ent_inode->os_value_1 = 0;
memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2));
time_t cur_time = time();
ent_inode->atime = cur_time;
ent_inode->mtime = cur_time;
ent_inode->ctime = cur_time;
ent_inode->dtime = 0;
memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks));
ent_inode->direct_blocks[0] = new_block_no;
ent_inode->l1_indirect_block = 0;
ent_inode->l2_indirect_block = 0;
ent_inode->l3_indirect_block = 0;
ent_inode->type_perm = (mode & VFS_MODE_MASK) | EXT2_TYPE_DIR;
ent_inode->uid = uid;
ent_inode->gid = gid;
ent_inode->disk_sector_count = 0;
ent_inode->size_lower = info->block_size;
memset(block_buffer, 0, info->block_size);
// "."
struct ext2_dirent *dirent = (struct ext2_dirent *) block_buffer;
dirent->ino = new_ino;
dirent->name_len = 1;
dirent->len = (sizeof(struct ext2_dirent) + 4) & ~3;
dirent->name[0] = '.';
dirent->type_ind = 0;
// ".."
dirent = (struct ext2_dirent *) &block_buffer[dirent->len];
dirent->ino = at->ino;
dirent->name_len = 2;
dirent->len = info->block_size - ((sizeof(struct ext2_dirent) + 4) & ~3);
dirent->name[0] = '.';
dirent->name[1] = '.';
dirent->type_ind = 0;
// Write directory's first block
if ((res = ext2_write_block(ext2, new_block_no, block_buffer)) < 0) {
return res;
}
// Write directory inode
if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) {
return res;
}
return 0;
}
static int ext2_vnode_creat(struct vnode *at, const char *name, uid_t uid, gid_t gid, mode_t mode) {
struct fs *ext2 = at->fs;
_assert(at->type == VN_DIR);
_assert(/* Don't support making directories like this */ !(mode & O_DIRECTORY));
struct ext2_info *info = ext2->fs_private;
uint32_t new_ino;
int res;
// Allocate new inode number
if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) {
kerror("Failed to allocate inode\n");
return res;
}
kdebug("Allocated inode %d\n", new_ino);
// Create an inode struct in memory
struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(info->sb.inode_struct_size);
// Now create an entry in parents dirent list
if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) {
return res;
}
// Fill the inode
ent_inode->flags = 0;
ent_inode->dir_acl = 0;
ent_inode->frag_block_addr = 0;
ent_inode->gen_number = 0;
ent_inode->hard_link_count = 1;
ent_inode->acl = 0;
ent_inode->os_value_1 = 0;
memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2));
time_t cur_time = time();
ent_inode->atime = cur_time;
ent_inode->mtime = cur_time;
ent_inode->ctime = cur_time;
ent_inode->dtime = 0;
memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks));
ent_inode->l1_indirect_block = 0;
ent_inode->l2_indirect_block = 0;
ent_inode->l3_indirect_block = 0;
ent_inode->uid = uid;
ent_inode->gid = gid;
// NOTE: only regular files can be created this way now
ent_inode->type_perm = (mode & VFS_MODE_MASK) | (EXT2_TYPE_REG);
ent_inode->disk_sector_count = 0;
ent_inode->size_lower = 0;
// Write the inode
if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) {
return res;
}
kfree(ent_inode);
return 0;
}
//
static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count) {
struct vnode *vn = fd->vnode;
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
struct ext2_info *info = vn->fs->fs_private;
size_t nread = MIN(inode->size_lower - fd->pos, count);
if (nread == 0) {
return -1;
}
size_t block_number = fd->pos / info->block_size;
size_t nblocks = (nread + info->block_size - 1) / info->block_size;
char block_buffer[info->block_size];
for (size_t i = 0; i < nblocks; ++i) {
if (ext2_read_inode_block(vn->fs, inode, i + block_number, block_buffer) < 0) {
kerror("Failed to read inode %d block #%u\n", vn->ino, i + block_number);
return -EIO;
}
if (i == 0) {
size_t ncpy = MIN(info->block_size - fd->pos % info->block_size, nread);
memcpy(buf, block_buffer + fd->pos % info->block_size, ncpy);
} else {
size_t ncpy = MIN(info->block_size, nread - info->block_size * i);
memcpy((void *) (((uintptr_t) buf) + info->block_size * i), block_buffer, ncpy);
}
}
fd->pos += nread;
return nread;
}
static ssize_t ext2_vnode_write(struct ofile *fd, const void *buf, size_t count) {
struct vnode *vn = fd->vnode;
_assert(vn);
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
struct fs *ext2 = vn->fs;
struct ext2_info *info = ext2->fs_private;
char block_buffer[info->block_size];
int res;
if (fd->pos > inode->size_lower) {
// This shouldn't be possible, yeah?
return -ESPIPE;
}
// How many bytes can we write into the blocks already allocated
size_t size_blocks = (inode->size_lower + info->block_size - 1) / info->block_size;
size_t can_write = size_blocks * info->block_size - inode->size_lower;
size_t current_block = fd->pos / info->block_size;
size_t written = 0;
size_t remaining = count;
// Update mtime on writes
// TODO: something like nomtime option
inode->mtime = time();
if (can_write) {
size_t can_write_blocks = (can_write + info->block_size - 1) / info->block_size;
for (size_t i = 0; i < can_write_blocks; ++i) {
size_t block_index = current_block + i;
size_t pos_in_block = fd->pos % info->block_size;
size_t need_write = MIN(remaining, info->block_size - pos_in_block);
kdebug("Write %uB to block %u offset %u\n", need_write, block_index, pos_in_block);
if (need_write == info->block_size) {
// Can write block without reading it
// TODO: implement this
panic("Not implemented\n");
} else {
// Read the block to change its contents
// and write it back again
if ((res = ext2_read_inode_block(ext2, inode, block_index, block_buffer)) < 0) {
break;
}
memcpy(block_buffer + pos_in_block, (void *) (((uintptr_t) buf) + written), need_write);
if ((res = ext2_write_inode_block(ext2, inode, block_index, block_buffer)) < 0) {
break;
}
}
written += need_write;
fd->pos += need_write;
remaining -= need_write;
}
inode->size_lower = MAX(fd->pos, inode->size_lower);
current_block += can_write_blocks;
}
if (remaining) {
// Need to allocate additional blocks
size_t need_blocks = (remaining + info->block_size - 1) / info->block_size;
for (size_t i = 0; i < need_blocks; ++i) {
size_t block_index = current_block + i;
size_t need_write = MIN(remaining, info->block_size);
// Update the size here so it gets written when the block is allocated
// and inode struct is flushed
inode->size_lower += need_write;
// Allocate a block for the index
if ((res = ext2_inode_alloc_block(ext2, inode, vn->ino, block_index)) < 0) {
kerror("Could not allocate a block for writing\n");
break;
}
if (need_write == info->block_size) {
// TODO: implement this
panic("Not implemented\n");
} else {
// Writing the last block
memcpy(block_buffer, (void *) (((uintptr_t) buf) + written), need_write);
if ((res = ext2_write_inode_block(ext2, inode, block_index, block_buffer)) < 0) {
break;
}
}
written += need_write;
fd->pos += need_write;
remaining -= need_write;
}
}
ext2_write_inode(ext2, inode, vn->ino);
return written;
}
static int ext2_vnode_truncate(struct vnode *vn, size_t length) {
_assert(vn);
struct fs *ext2 = vn->fs;
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
struct ext2_info *info = vn->fs->fs_private;
int res;
if (length == inode->size_lower) {
// Already good
return 0;
}
size_t was_blocks = (inode->size_lower + info->block_size - 1) / info->block_size;
size_t now_blocks = (length + info->block_size - 1) / info->block_size;
ssize_t delta_blocks = now_blocks - was_blocks;
if (delta_blocks < 0) {
int ind1 = 0;
char buf1[info->block_size];
// Free truncated blocks
for (size_t i = now_blocks; i < was_blocks; ++i) {
inode->size_lower -= info->block_size;
if (i < 12) {
if ((res = ext2_free_block(ext2, inode->direct_blocks[i])) < 0) {
panic("Failed to release inode block\n");
}
inode->direct_blocks[i] = 0;
} else if (i < 12 + (info->block_size / 4)) {
if (!ind1) {
// Read indirection block only once
if ((res = ext2_read_block(ext2, inode->l1_indirect_block, buf1)) < 0) {
// TODO: rollback inode struct and return error
panic("Failed to read indirection block\n");
}
ind1 = 1;
}
if ((res = ext2_free_block(ext2, ((uint32_t *) buf1)[i - 12])) < 0) {
panic("Failed to release inode block\n");
}
((uint32_t *) buf1)[i - 12] = 0;
} else {
panic("Inode block index has too high indirection level\n");
}
}
// All the blocks were successfully freed, can set proper file length
if (inode->size_lower != length) {
// If requested size is not block-aligned, we need to write inode
// struct to disk once again
inode->size_lower = length;
if ((res = ext2_write_inode(ext2, inode, vn->ino)) < 0) {
return res;
}
}
return 0;
} else {
kerror("Not implemented: upwards truncation (ext2)\n");
return -EINVAL;
}
}
static ssize_t ext2_vnode_readdir(struct ofile *fd, struct dirent *vfsdir) {
struct vnode *vn = fd->vnode;
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
struct ext2_info *info = vn->fs->fs_private;
ssize_t res;
if (fd->pos >= inode->size_lower) {
return -1;
}
size_t block_number = fd->pos / info->block_size;
char block_buffer[info->block_size];
if ((res = ext2_read_inode_block(vn->fs, inode, block_number, block_buffer)) < 0) {
return res;
}
size_t block_offset = fd->pos % info->block_size;
struct ext2_dirent *ext2dir = (struct ext2_dirent *) &block_buffer[block_offset];
if (ext2dir->len == 0) {
// If entry size is zero, guess we're finished - align the fd->pos up to block size
fd->pos = (fd->pos + info->block_size - 1) / info->block_size;
return ext2_vnode_readdir(fd, vfsdir);
}
vfsdir->d_ino = ext2dir->ino;
strncpy(vfsdir->d_name, ext2dir->name, ext2dir->name_len);
vfsdir->d_name[ext2dir->name_len] = 0;
vfsdir->d_reclen = ext2dir->len;
if (info->sb.required_features & 2 /* Directory entries contain type field */) {
switch (ext2dir->type_ind) {
case 1:
// Regular file
vfsdir->d_type = DT_REG;
break;
case 2:
// Directory
vfsdir->d_type = DT_DIR;
break;
// XXX: Don't know if I should have ANY devices in ext2, we have devfs for that
// kind of shit
case 0:
default:
vfsdir->d_type = DT_UNKNOWN;
break;
}
} else {
// Have to read each fucking inode to sort this mess out
struct ext2_inode ent_inode_buf;
if ((res = ext2_read_inode(vn->fs, &ent_inode_buf, ext2dir->ino)) < 0) {
return res;
}
switch (ent_inode_buf.type_perm & 0xF000) {
case EXT2_TYPE_REG:
vfsdir->d_type = DT_REG;
break;
case EXT2_TYPE_DIR:
vfsdir->d_type = DT_DIR;
break;
default:
vfsdir->d_type = DT_UNKNOWN;
break;
}
}
// Not implemented, I guess
vfsdir->d_off = 0;
fd->pos += ext2dir->len;
return ext2dir->len;
}
//static void ext2_vnode_destroy(vnode_t *vn) {
// // Release inode struct
// kfree(vn->fs_data);
//}
static int ext2_vnode_stat(struct vnode *vn, struct stat *st) {
_assert(vn && vn->fs);
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
_assert(inode);
struct ext2_info *info = vn->fs->fs_private;
_assert(info);
st->st_atime = inode->atime;
st->st_ctime = inode->ctime;
st->st_mtime = inode->mtime;
st->st_dev = 0; // Not implemented
st->st_rdev = 0; // Not implemented
st->st_gid = inode->gid;
st->st_uid = inode->uid;
st->st_mode = inode->type_perm;
st->st_size = inode->size_lower;
st->st_blocks = (inode->size_lower + info->block_size - 1) / info->block_size;
st->st_blksize = info->block_size;
st->st_nlink = 0;
st->st_ino = vn->ino;
return 0;
}
static int ext2_vnode_chmod(struct vnode *vn, mode_t mode) {
_assert(vn && vn->fs && vn->fs_data);
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
// Update only access mode
inode->type_perm &= ~VFS_MODE_MASK;
inode->type_perm |= mode & VFS_MODE_MASK;
// Write the inode back
return ext2_write_inode(vn->fs, inode, vn->ino);
}
static int ext2_vnode_chown(struct vnode *vn, uid_t uid, gid_t gid) {
_assert(vn && vn->fs && vn->fs_data);
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
inode->gid = gid;
inode->uid = uid;
// Write the inode back
return ext2_write_inode(vn->fs, inode, vn->ino);
}
static int ext2_vnode_unlink(struct vnode *node) {
_assert(node);
_assert(node->parent);
struct vnode *at = node->parent;
struct ext2_inode *inode = node->fs_data;
struct ext2_inode *at_inode = at->fs_data;
struct fs *ext2 = node->fs;
struct ext2_info *info = ext2->fs_private;
uint32_t ino = node->ino;
int res;
if (node->type == VN_DIR) {
// Check if the directory we're unlinking has any entries besides
// . and ..
// Can tell this just by looking at the first block
if (inode->size_lower > info->block_size) {
// Directory size is more than one block - totally
// has something inside
return -EISDIR;
}
char block_buffer[info->block_size];
size_t off = 0;
if ((res = ext2_read_inode_block(ext2, inode, 0, block_buffer)) < 0) {
return res;
}
while (off < info->block_size) {
struct ext2_dirent *ent = (struct ext2_dirent *) &block_buffer[off];
if (!ent->ino) {
break;
}
if (ent->name_len == 1 && ent->name[0] == '.') {
off += ent->len;
continue;
}
if (ent->name_len == 2 && ent->name[1] == '.' && ent->name[0] == '.') {
off += ent->len;
continue;
}
return -EISDIR;
}
}
// Free blocks used by the inode - truncate the file to zero
size_t nblocks = (inode->size_lower + info->block_size - 1) / info->block_size;
int ind1 = 0;
char buf1[info->block_size];
for (size_t i = 0; i < nblocks; ++i) {
if (i < 12) {
if ((res = ext2_free_block(ext2, inode->direct_blocks[i])) < 0) {
panic("Failed to release inode block\n");
}
inode->direct_blocks[i] = 0;
} else if (i < 12 + (info->block_size / 4)) {
if (!ind1) {
// Read indirection block only once
if ((res = ext2_read_block(ext2, inode->l1_indirect_block, buf1)) < 0) {
// TODO: rollback inode struct and return error
panic("Failed to read indirection block\n");
}
ind1 = 1;
}
if ((res = ext2_free_block(ext2, ((uint32_t *) buf1)[i - 12])) < 0) {
panic("Failed to release inode block\n");
}
((uint32_t *) buf1)[i - 12] = 0;
} else {
panic("Inode block index has too high indirection level\n");
}
}
// Free the inode itself
if ((res = ext2_free_inode(ext2, ino)) < 0) {
return res;
}
// Now remove the entry from directory
if ((res = ext2_dir_remove_inode(ext2, at, node->name, ino)) < 0) {
return res;
}
return 0;
}
//static int ext2_vnode_access(vnode_t *vn, uid_t *uid, gid_t *gid, mode_t *mode) {
// _assert(vn && vn->fs_data);
// struct ext2_inode *inode = vn->fs_data;
//
// *uid = inode->uid;
// *gid = inode->gid;
// *mode = inode->type_perm & VFS_MODE_MASK;
//
// return 0;
//}
//
static int ext2_vnode_readlink(struct vnode *vn, char *dst, size_t lim) {
_assert(vn && vn->fs_data);
struct ext2_inode *inode = vn->fs_data;
struct fs *ext2 = vn->fs;
struct ext2_info *info = ext2->fs_private;
if (lim <= inode->size_lower) {
return -1;
}
if (inode->size_lower >= 60) {
char block_buffer[info->block_size];
int res;
if ((res = ext2_read_inode_block(ext2, inode, 0, block_buffer)) < 0) {
return res;
}
strncpy(dst, block_buffer, inode->size_lower);
dst[inode->size_lower] = 0;
} else {
const char *src = (const char *) inode->direct_blocks;
strncpy(dst, src, inode->size_lower);
dst[inode->size_lower] = 0;
}
return 0;
}
//
//static int ext2_vnode_symlink(vnode_t *at, struct vfs_ioctx *ctx, const char *name, const char *dst) {
// _assert(at && at->fs && at->fs_data);
// struct ext2_inode *inode = at->fs_data;
// fs_t *ext2 = at->fs;
// struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
//
// uint32_t new_ino;
// int res;
//
// // Allocate new inode number
// if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) {
// kerror("Failed to allocate inode\n");
// return res;
// }
//
// kdebug("Allocated inode %d\n", new_ino);
//
// // Create an inode struct in memory
// struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size);
//
// // Now create an entry in parents dirent list
// if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) {
// return res;
// }
//
// // Fill the inode
// ent_inode->flags = 0;
// ent_inode->dir_acl = 0;
// ent_inode->frag_block_addr = 0;
// ent_inode->gen_number = 0;
// ent_inode->hard_link_count = 1;
// ent_inode->acl = 0;
// ent_inode->os_value_1 = 0;
// memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2));
// // TODO: time support in kernel
// ent_inode->atime = 0;
// ent_inode->mtime = 0;
// ent_inode->ctime = 0;
// ent_inode->dtime = 0;
//
// ent_inode->size_lower = strlen(dst);
//
// if (ent_inode->size_lower <= 60) {
// char *dst_str = (char *) ent_inode->direct_blocks;
// memset(dst_str, 0, 60);
//
// strncpy(dst_str, dst, ent_inode->size_lower);
// } else {
// char block_buffer[sb->block_size];
// uint32_t block_no;
//
// if ((res = ext2_alloc_block(ext2, &block_no)) < 0) {
// return res;
// }
//
// memset(block_buffer, 0, sb->block_size);
// strncpy(block_buffer, dst, sb->block_size);
//
// if ((res = ext2_write_block(ext2, block_no, block_buffer)) < 0) {
// return res;
// }
//
// memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks));
// ent_inode->l1_indirect_block = 0;
// ent_inode->l2_indirect_block = 0;
// ent_inode->l3_indirect_block = 0;
//
// ent_inode->direct_blocks[0] = block_no;
// }
//
// ent_inode->uid = ctx->uid;
// ent_inode->gid = ctx->gid;
// ent_inode->type_perm = 0777 | EXT2_TYPE_LNK;
//
// // Write the inode
// if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) {
// return res;
// }
//
// return 0;
//}
static off_t ext2_vnode_lseek(struct ofile *fd, off_t offset, int whence) {
_assert(fd);
struct vnode *node = fd->vnode;
_assert(node);
_assert(node->type == VN_REG);
struct ext2_inode *inode = node->fs_data;
_assert(inode);
size_t max = inode->size_lower;
switch (whence) {
case SEEK_SET:
if ((size_t) offset > max) {
// Don't support seeking beyond file end
return -ENXIO;
}
fd->pos = offset;
break;
default:
panic("Unsupported whence: %d\n", whence);
}
return fd->pos;
}