44a3dc21b4
"kind of" == directories aren't resized and overall space handling is crappy
164 lines
5.1 KiB
C
164 lines
5.1 KiB
C
#include "fs/ext2/alloc.h"
|
|
#include "fs/ext2/block.h"
|
|
#include "fs/ext2/dir.h"
|
|
#include "fs/ext2/ext2.h"
|
|
#include "fs/fs.h"
|
|
#include "fs/node.h"
|
|
#include "sys/assert.h"
|
|
#include "sys/debug.h"
|
|
#include "sys/string.h"
|
|
#include "user/errno.h"
|
|
|
|
int ext2_dir_insert_inode(struct fs *ext2,
|
|
struct ext2_inode *at_inode,
|
|
uint32_t at_ino,
|
|
const char *name,
|
|
uint32_t ino,
|
|
enum vnode_type type) {
|
|
// Align name
|
|
// Not zero-terminated
|
|
struct ext2_data *data = ext2->fs_private;
|
|
size_t size_needed = ((strlen(name) + 3) & ~3) + sizeof(struct ext2_dirent);
|
|
size_t block_count = at_inode->size_lower / data->block_size;
|
|
struct ext2_dirent *new = NULL;
|
|
int inode_changed = 0;
|
|
uint32_t index;
|
|
char buf[data->block_size];
|
|
|
|
// Try to fit inside already-existing blocks
|
|
for (size_t i = 0; i < block_count; ++i) {
|
|
// Find out how much space is free in this dir
|
|
_assert(ext2_read_inode_block(ext2, at_inode, buf, i) == 0);
|
|
|
|
size_t offset = 0;
|
|
while (offset < data->block_size) {
|
|
struct ext2_dirent *ent = (struct ext2_dirent *) &buf[offset];
|
|
// Minimum size needed for this entry
|
|
size_t size_needed_prev = sizeof(struct ext2_dirent);
|
|
if (ent->ino) {
|
|
// A real inode dirent
|
|
size_needed_prev += ((ent->name_length_low + 3) & ~3);
|
|
}
|
|
_assert(ent->ent_size >= size_needed_prev);
|
|
size_t spare_space = ent->ent_size - size_needed_prev;
|
|
|
|
if (spare_space >= size_needed) {
|
|
// Cut off the spare space from previous entry
|
|
ent->ent_size = size_needed_prev;
|
|
|
|
// This will be the new entry
|
|
new = (struct ext2_dirent *) &buf[offset + ent->ent_size];
|
|
new->ent_size = spare_space;
|
|
index = i;
|
|
|
|
break;
|
|
}
|
|
|
|
offset += ent->ent_size;
|
|
}
|
|
|
|
if (new) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!new) {
|
|
// Grow the directory
|
|
_assert(ext2_file_resize(ext2, at_ino, at_inode, at_inode->size_lower + data->block_size) == 0);
|
|
|
|
inode_changed = 1;
|
|
memset(buf, 0, data->block_size);
|
|
|
|
// New entry takes the whole block
|
|
new = (struct ext2_dirent *) buf;
|
|
new->ent_size = data->block_size;
|
|
|
|
index = block_count;
|
|
}
|
|
|
|
new->ino = ino;
|
|
new->name_length_low = strlen(name);
|
|
strncpy(new->name, name, new->name_length_low);
|
|
// TODO
|
|
if (data->sb.required_features & EXT2_REQ_ENT_TYPE) {
|
|
switch (type) {
|
|
case VN_REG:
|
|
new->type_indicator = 1;
|
|
break;
|
|
case VN_DIR:
|
|
new->type_indicator = 2;
|
|
break;
|
|
default:
|
|
panic("Unsupported vnode type: %02x\n", type);
|
|
}
|
|
} else {
|
|
new->name_length_high = 0;
|
|
}
|
|
|
|
// Write back
|
|
_assert(ext2_write_inode_block(ext2, at_inode, buf, index) == 0);
|
|
|
|
if (inode_changed) {
|
|
_assert(ext2_write_inode(ext2, at_inode, at_ino) == 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ext2_dir_del_inode(struct fs *ext2, struct ext2_inode *at_inode, uint32_t at_ino, struct vnode *vn) {
|
|
size_t offset;
|
|
struct ext2_data *data = ext2->fs_private;
|
|
size_t block_count = at_inode->size_lower / data->block_size;
|
|
char buf[data->block_size];
|
|
struct ext2_dirent *prev, *next;
|
|
|
|
for (size_t i = 0; i < block_count; ++i) {
|
|
offset = 0;
|
|
prev = NULL;
|
|
|
|
_assert(ext2_read_inode_block(ext2, at_inode, buf, i) == 0);
|
|
|
|
while (offset < data->block_size) {
|
|
struct ext2_dirent *ent = (struct ext2_dirent *) &buf[offset];
|
|
|
|
if (ent->ino == vn->ino) {
|
|
_assert(!strncmp(ent->name, vn->name, ent->name_length_low));
|
|
|
|
ent->ino = 0;
|
|
|
|
// TODO: Free directory blocks when the last block entry
|
|
// is removed
|
|
// TODO: would be better to just "lshift" all the following
|
|
// entries to reduce fragmentation
|
|
if (ent->ent_size == data->block_size) {
|
|
kinfo("TODO: free directory blocks\n");
|
|
return ext2_write_inode_block(ext2, at_inode, buf, i);
|
|
}
|
|
|
|
if (prev) {
|
|
prev->ent_size += ent->ent_size;
|
|
return ext2_write_inode_block(ext2, at_inode, buf, i);
|
|
} else {
|
|
_assert(!offset);
|
|
_assert(ent->ent_size < data->block_size);
|
|
|
|
next = (struct ext2_dirent *) &buf[offset + ent->ent_size];
|
|
size_t old = ent->ent_size;
|
|
size_t tot = ent->ent_size + next->ent_size;
|
|
size_t len = next->ent_size;
|
|
|
|
memmove(ent, next, len);
|
|
|
|
ent->ent_size = tot;
|
|
return ext2_write_inode_block(ext2, at_inode, buf, i);
|
|
}
|
|
}
|
|
|
|
prev = ent;
|
|
offset += ent->ent_size;
|
|
}
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|