Get rid of old ext2
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user