push
This commit is contained in:
@@ -0,0 +1,935 @@
|
||||
#include "../../include/fs/ext2.h"
|
||||
#include "../../include/fs/vfs.h"
|
||||
#include "../../include/drivers/blkdev.h"
|
||||
#include "../../include/memory/pmm.h"
|
||||
#include "../../include/io/serial.h"
|
||||
#include "../../include/syscall/errno.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int block_read(ext2_t *fs, uint32_t block, void *buf) {
|
||||
return blkdev_read(fs->dev, (uint64_t)block * fs->block_size, buf, fs->block_size);
|
||||
}
|
||||
|
||||
static int block_write(ext2_t *fs, uint32_t block, const void *buf) {
|
||||
return blkdev_write(fs->dev, (uint64_t)block * fs->block_size, buf, fs->block_size);
|
||||
}
|
||||
|
||||
static int sb_flush(ext2_t *fs) {
|
||||
return blkdev_write(fs->dev, EXT2_SUPER_OFFSET, &fs->sb, sizeof(fs->sb));
|
||||
}
|
||||
|
||||
static int gdt_flush(ext2_t *fs) {
|
||||
uint32_t gdt_block = (fs->block_size == 1024) ? 2 : 1;
|
||||
return blkdev_write(fs->dev, (uint64_t)gdt_block * fs->block_size,
|
||||
fs->gdt, fs->groups_count * sizeof(ext2_group_desc_t));
|
||||
}
|
||||
|
||||
static int inode_read(ext2_t *fs, uint32_t ino, ext2_inode_t *out) {
|
||||
if (ino == 0) return -EINVAL;
|
||||
uint32_t idx = ino - 1;
|
||||
uint32_t group = idx / fs->sb.s_inodes_per_group;
|
||||
uint32_t local = idx % fs->sb.s_inodes_per_group;
|
||||
if (group >= fs->groups_count) return -EINVAL;
|
||||
uint64_t off = (uint64_t)fs->gdt[group].bg_inode_table * fs->block_size
|
||||
+ (uint64_t)local * fs->inode_size;
|
||||
return blkdev_read(fs->dev, off, out, sizeof(*out));
|
||||
}
|
||||
|
||||
static int inode_write(ext2_t *fs, uint32_t ino, const ext2_inode_t *in) {
|
||||
if (ino == 0) return -EINVAL;
|
||||
uint32_t idx = ino - 1;
|
||||
uint32_t group = idx / fs->sb.s_inodes_per_group;
|
||||
uint32_t local = idx % fs->sb.s_inodes_per_group;
|
||||
if (group >= fs->groups_count) return -EINVAL;
|
||||
uint64_t off = (uint64_t)fs->gdt[group].bg_inode_table * fs->block_size
|
||||
+ (uint64_t)local * fs->inode_size;
|
||||
return blkdev_write(fs->dev, off, in, sizeof(*in));
|
||||
}
|
||||
|
||||
static bool bmp_test(const uint8_t *bmp, uint32_t bit) {
|
||||
return (bmp[bit / 8] >> (bit % 8)) & 1;
|
||||
}
|
||||
static void bmp_set(uint8_t *bmp, uint32_t bit) {
|
||||
bmp[bit / 8] |= (uint8_t)(1 << (bit % 8));
|
||||
}
|
||||
static void bmp_clear(uint8_t *bmp, uint32_t bit) {
|
||||
bmp[bit / 8] &= (uint8_t)~(1 << (bit % 8));
|
||||
}
|
||||
|
||||
static int32_t alloc_inode(ext2_t *fs) {
|
||||
uint8_t *bmp = kmalloc(fs->block_size);
|
||||
if (!bmp) return -ENOMEM;
|
||||
for (uint32_t g = 0; g < fs->groups_count; g++) {
|
||||
if (fs->gdt[g].bg_free_inodes_count == 0) continue;
|
||||
block_read(fs, fs->gdt[g].bg_inode_bitmap, bmp);
|
||||
for (uint32_t i = 0; i < fs->sb.s_inodes_per_group; i++) {
|
||||
if (!bmp_test(bmp, i)) {
|
||||
bmp_set(bmp, i);
|
||||
block_write(fs, fs->gdt[g].bg_inode_bitmap, bmp);
|
||||
fs->gdt[g].bg_free_inodes_count--;
|
||||
fs->sb.s_free_inodes_count--;
|
||||
fs->dirty = true;
|
||||
kfree(bmp);
|
||||
return (int32_t)(g * fs->sb.s_inodes_per_group + i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
kfree(bmp);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static void free_inode(ext2_t *fs, uint32_t ino) {
|
||||
uint32_t idx = ino - 1;
|
||||
uint32_t group = idx / fs->sb.s_inodes_per_group;
|
||||
uint32_t local = idx % fs->sb.s_inodes_per_group;
|
||||
uint8_t *bmp = kmalloc(fs->block_size);
|
||||
if (!bmp) return;
|
||||
block_read(fs, fs->gdt[group].bg_inode_bitmap, bmp);
|
||||
bmp_clear(bmp, local);
|
||||
block_write(fs, fs->gdt[group].bg_inode_bitmap, bmp);
|
||||
fs->gdt[group].bg_free_inodes_count++;
|
||||
fs->sb.s_free_inodes_count++;
|
||||
fs->dirty = true;
|
||||
kfree(bmp);
|
||||
}
|
||||
|
||||
static int32_t alloc_block(ext2_t *fs) {
|
||||
uint8_t *bmp = kmalloc(fs->block_size);
|
||||
if (!bmp) return -ENOMEM;
|
||||
for (uint32_t g = 0; g < fs->groups_count; g++) {
|
||||
if (fs->gdt[g].bg_free_blocks_count == 0) continue;
|
||||
block_read(fs, fs->gdt[g].bg_block_bitmap, bmp);
|
||||
for (uint32_t i = 0; i < fs->sb.s_blocks_per_group; i++) {
|
||||
uint32_t abs_block = g * fs->sb.s_blocks_per_group + i + fs->sb.s_first_data_block;
|
||||
if (abs_block >= fs->sb.s_blocks_count) break;
|
||||
if (!bmp_test(bmp, i)) {
|
||||
bmp_set(bmp, i);
|
||||
block_write(fs, fs->gdt[g].bg_block_bitmap, bmp);
|
||||
fs->gdt[g].bg_free_blocks_count--;
|
||||
fs->sb.s_free_blocks_count--;
|
||||
fs->dirty = true;
|
||||
kfree(bmp);
|
||||
return (int32_t)abs_block;
|
||||
}
|
||||
}
|
||||
}
|
||||
kfree(bmp);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static void free_block(ext2_t *fs, uint32_t blk) {
|
||||
if (blk < fs->sb.s_first_data_block) return;
|
||||
uint32_t adj = blk - fs->sb.s_first_data_block;
|
||||
uint32_t group = adj / fs->sb.s_blocks_per_group;
|
||||
uint32_t local = adj % fs->sb.s_blocks_per_group;
|
||||
if (group >= fs->groups_count) return;
|
||||
uint8_t *bmp = kmalloc(fs->block_size);
|
||||
if (!bmp) return;
|
||||
block_read(fs, fs->gdt[group].bg_block_bitmap, bmp);
|
||||
bmp_clear(bmp, local);
|
||||
block_write(fs, fs->gdt[group].bg_block_bitmap, bmp);
|
||||
fs->gdt[group].bg_free_blocks_count++;
|
||||
fs->sb.s_free_blocks_count++;
|
||||
fs->dirty = true;
|
||||
kfree(bmp);
|
||||
}
|
||||
|
||||
static int32_t get_block_num(ext2_t *fs, ext2_inode_t *di, uint32_t file_block) {
|
||||
if (file_block < EXT2_NDIR_BLOCKS)
|
||||
return (int32_t)di->i_block[file_block];
|
||||
uint32_t ppb = fs->ptrs_per_block;
|
||||
file_block -= EXT2_NDIR_BLOCKS;
|
||||
if (file_block < ppb) {
|
||||
if (di->i_block[EXT2_IND_BLOCK] == 0) return 0;
|
||||
uint32_t *ind = kmalloc(fs->block_size);
|
||||
if (!ind) return -ENOMEM;
|
||||
block_read(fs, di->i_block[EXT2_IND_BLOCK], ind);
|
||||
int32_t ret = (int32_t)ind[file_block];
|
||||
kfree(ind);
|
||||
return ret;
|
||||
}
|
||||
file_block -= ppb;
|
||||
if (file_block < ppb * ppb) {
|
||||
if (di->i_block[EXT2_DIND_BLOCK] == 0) return 0;
|
||||
uint32_t *dind = kmalloc(fs->block_size);
|
||||
if (!dind) return -ENOMEM;
|
||||
block_read(fs, di->i_block[EXT2_DIND_BLOCK], dind);
|
||||
uint32_t i1 = file_block / ppb;
|
||||
uint32_t i2 = file_block % ppb;
|
||||
if (dind[i1] == 0) { kfree(dind); return 0; }
|
||||
uint32_t ib = dind[i1];
|
||||
kfree(dind);
|
||||
uint32_t *ind = kmalloc(fs->block_size);
|
||||
if (!ind) return -ENOMEM;
|
||||
block_read(fs, ib, ind);
|
||||
int32_t ret = (int32_t)ind[i2];
|
||||
kfree(ind);
|
||||
return ret;
|
||||
}
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
static int set_block_num(ext2_t *fs, ext2_inode_t *di, uint32_t file_block, uint32_t disk_block) {
|
||||
if (file_block < EXT2_NDIR_BLOCKS) {
|
||||
di->i_block[file_block] = disk_block;
|
||||
return 0;
|
||||
}
|
||||
uint32_t ppb = fs->ptrs_per_block;
|
||||
file_block -= EXT2_NDIR_BLOCKS;
|
||||
if (file_block < ppb) {
|
||||
if (di->i_block[EXT2_IND_BLOCK] == 0) {
|
||||
int32_t nb = alloc_block(fs);
|
||||
if (nb < 0) return nb;
|
||||
di->i_block[EXT2_IND_BLOCK] = (uint32_t)nb;
|
||||
uint8_t *z = kzalloc(fs->block_size);
|
||||
block_write(fs, (uint32_t)nb, z);
|
||||
kfree(z);
|
||||
}
|
||||
uint32_t *ind = kmalloc(fs->block_size);
|
||||
if (!ind) return -ENOMEM;
|
||||
block_read(fs, di->i_block[EXT2_IND_BLOCK], ind);
|
||||
ind[file_block] = disk_block;
|
||||
block_write(fs, di->i_block[EXT2_IND_BLOCK], ind);
|
||||
kfree(ind);
|
||||
return 0;
|
||||
}
|
||||
file_block -= ppb;
|
||||
if (file_block < ppb * ppb) {
|
||||
if (di->i_block[EXT2_DIND_BLOCK] == 0) {
|
||||
int32_t nb = alloc_block(fs);
|
||||
if (nb < 0) return nb;
|
||||
di->i_block[EXT2_DIND_BLOCK] = (uint32_t)nb;
|
||||
uint8_t *z = kzalloc(fs->block_size);
|
||||
block_write(fs, (uint32_t)nb, z);
|
||||
kfree(z);
|
||||
}
|
||||
uint32_t *dind = kmalloc(fs->block_size);
|
||||
if (!dind) return -ENOMEM;
|
||||
block_read(fs, di->i_block[EXT2_DIND_BLOCK], dind);
|
||||
uint32_t i1 = file_block / ppb;
|
||||
uint32_t i2 = file_block % ppb;
|
||||
if (dind[i1] == 0) {
|
||||
int32_t nb = alloc_block(fs);
|
||||
if (nb < 0) { kfree(dind); return nb; }
|
||||
dind[i1] = (uint32_t)nb;
|
||||
block_write(fs, di->i_block[EXT2_DIND_BLOCK], dind);
|
||||
uint8_t *z = kzalloc(fs->block_size);
|
||||
block_write(fs, (uint32_t)nb, z);
|
||||
kfree(z);
|
||||
}
|
||||
uint32_t ib = dind[i1];
|
||||
kfree(dind);
|
||||
uint32_t *ind = kmalloc(fs->block_size);
|
||||
if (!ind) return -ENOMEM;
|
||||
block_read(fs, ib, ind);
|
||||
ind[i2] = disk_block;
|
||||
block_write(fs, ib, ind);
|
||||
kfree(ind);
|
||||
return 0;
|
||||
}
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
static const vnode_ops_t ext2_file_ops;
|
||||
static const vnode_ops_t ext2_dir_ops;
|
||||
|
||||
static vnode_t *ext2_make_vnode(ext2_t *fs, uint32_t ino, ext2_inode_t *di) {
|
||||
vnode_t *v = kzalloc(sizeof(vnode_t));
|
||||
if (!v) return NULL;
|
||||
ext2_vdata_t *vd = kzalloc(sizeof(ext2_vdata_t));
|
||||
if (!vd) { kfree(v); return NULL; }
|
||||
vd->fs = fs;
|
||||
vd->ino = ino;
|
||||
if ((di->i_mode & 0xF000) == EXT2_S_IFDIR) {
|
||||
v->type = VFS_NODE_DIR;
|
||||
v->ops = &ext2_dir_ops;
|
||||
} else {
|
||||
v->type = VFS_NODE_FILE;
|
||||
v->ops = &ext2_file_ops;
|
||||
}
|
||||
v->mode = di->i_mode & 0x0FFF;
|
||||
v->uid = di->i_uid;
|
||||
v->gid = di->i_gid;
|
||||
v->ino = ino;
|
||||
v->size = di->i_size;
|
||||
v->fs_data = vd;
|
||||
v->refcount = 1;
|
||||
return v;
|
||||
}
|
||||
|
||||
static int64_t ext2_file_read(vnode_t *node, void *buf, size_t len, uint64_t offset) {
|
||||
ext2_vdata_t *vd = node->fs_data;
|
||||
ext2_t *fs = vd->fs;
|
||||
ext2_inode_t di;
|
||||
int r = inode_read(fs, vd->ino, &di);
|
||||
if (r < 0) return r;
|
||||
if (offset >= di.i_size) return 0;
|
||||
if (offset + len > di.i_size) len = di.i_size - (size_t)offset;
|
||||
if (len == 0) return 0;
|
||||
uint8_t *dst = (uint8_t *)buf;
|
||||
size_t done = 0;
|
||||
uint8_t *bb = kmalloc(fs->block_size);
|
||||
if (!bb) return -ENOMEM;
|
||||
while (done < len) {
|
||||
uint32_t co = (uint32_t)(offset + done);
|
||||
uint32_t fb = co / fs->block_size;
|
||||
uint32_t bo = co % fs->block_size;
|
||||
int32_t db = get_block_num(fs, &di, fb);
|
||||
if (db <= 0) {
|
||||
if (db < 0) { kfree(bb); return db; }
|
||||
memset(bb, 0, fs->block_size);
|
||||
} else {
|
||||
r = block_read(fs, (uint32_t)db, bb);
|
||||
if (r < 0) { kfree(bb); return r; }
|
||||
}
|
||||
size_t ch = fs->block_size - bo;
|
||||
if (ch > len - done) ch = len - done;
|
||||
memcpy(dst + done, bb + bo, ch);
|
||||
done += ch;
|
||||
}
|
||||
kfree(bb);
|
||||
return (int64_t)done;
|
||||
}
|
||||
|
||||
static int64_t ext2_file_write(vnode_t *node, const void *buf, size_t len, uint64_t offset) {
|
||||
ext2_vdata_t *vd = node->fs_data;
|
||||
ext2_t *fs = vd->fs;
|
||||
ext2_inode_t di;
|
||||
int r = inode_read(fs, vd->ino, &di);
|
||||
if (r < 0) return r;
|
||||
const uint8_t *src = (const uint8_t *)buf;
|
||||
size_t done = 0;
|
||||
uint8_t *bb = kmalloc(fs->block_size);
|
||||
if (!bb) return -ENOMEM;
|
||||
while (done < len) {
|
||||
uint32_t co = (uint32_t)(offset + done);
|
||||
uint32_t fb = co / fs->block_size;
|
||||
uint32_t bo = co % fs->block_size;
|
||||
int32_t db = get_block_num(fs, &di, fb);
|
||||
if (db == 0) {
|
||||
int32_t nb = alloc_block(fs);
|
||||
if (nb < 0) { kfree(bb); return (done > 0) ? (int64_t)done : nb; }
|
||||
set_block_num(fs, &di, fb, (uint32_t)nb);
|
||||
db = nb;
|
||||
memset(bb, 0, fs->block_size);
|
||||
di.i_blocks += fs->block_size / 512;
|
||||
} else if (db < 0) {
|
||||
kfree(bb); return db;
|
||||
} else {
|
||||
if (bo != 0 || (len - done) < fs->block_size)
|
||||
block_read(fs, (uint32_t)db, bb);
|
||||
}
|
||||
size_t ch = fs->block_size - bo;
|
||||
if (ch > len - done) ch = len - done;
|
||||
memcpy(bb + bo, src + done, ch);
|
||||
block_write(fs, (uint32_t)db, bb);
|
||||
done += ch;
|
||||
}
|
||||
uint32_t ne = (uint32_t)(offset + done);
|
||||
if (ne > di.i_size) { di.i_size = ne; node->size = ne; }
|
||||
inode_write(fs, vd->ino, &di);
|
||||
fs->dirty = true;
|
||||
kfree(bb);
|
||||
return (int64_t)done;
|
||||
}
|
||||
|
||||
static void free_all_blocks(ext2_t *fs, ext2_inode_t *di) {
|
||||
for (int i = 0; i < EXT2_NDIR_BLOCKS; i++) {
|
||||
if (di->i_block[i]) { free_block(fs, di->i_block[i]); di->i_block[i] = 0; }
|
||||
}
|
||||
if (di->i_block[EXT2_IND_BLOCK]) {
|
||||
uint32_t *ind = kmalloc(fs->block_size);
|
||||
if (ind) {
|
||||
block_read(fs, di->i_block[EXT2_IND_BLOCK], ind);
|
||||
for (uint32_t i = 0; i < fs->ptrs_per_block; i++)
|
||||
if (ind[i]) free_block(fs, ind[i]);
|
||||
kfree(ind);
|
||||
}
|
||||
free_block(fs, di->i_block[EXT2_IND_BLOCK]);
|
||||
di->i_block[EXT2_IND_BLOCK] = 0;
|
||||
}
|
||||
if (di->i_block[EXT2_DIND_BLOCK]) {
|
||||
uint32_t *dind = kmalloc(fs->block_size);
|
||||
if (dind) {
|
||||
block_read(fs, di->i_block[EXT2_DIND_BLOCK], dind);
|
||||
for (uint32_t i = 0; i < fs->ptrs_per_block; i++) {
|
||||
if (dind[i]) {
|
||||
uint32_t *ind = kmalloc(fs->block_size);
|
||||
if (ind) {
|
||||
block_read(fs, dind[i], ind);
|
||||
for (uint32_t j = 0; j < fs->ptrs_per_block; j++)
|
||||
if (ind[j]) free_block(fs, ind[j]);
|
||||
kfree(ind);
|
||||
}
|
||||
free_block(fs, dind[i]);
|
||||
}
|
||||
}
|
||||
kfree(dind);
|
||||
}
|
||||
free_block(fs, di->i_block[EXT2_DIND_BLOCK]);
|
||||
di->i_block[EXT2_DIND_BLOCK] = 0;
|
||||
}
|
||||
di->i_blocks = 0;
|
||||
}
|
||||
|
||||
static int ext2_file_truncate(vnode_t *node, uint64_t new_size) {
|
||||
ext2_vdata_t *vd = node->fs_data;
|
||||
ext2_t *fs = vd->fs;
|
||||
ext2_inode_t di;
|
||||
int r = inode_read(fs, vd->ino, &di);
|
||||
if (r < 0) return r;
|
||||
if (new_size == 0) free_all_blocks(fs, &di);
|
||||
di.i_size = (uint32_t)new_size;
|
||||
node->size = new_size;
|
||||
inode_write(fs, vd->ino, &di);
|
||||
fs->dirty = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext2_stat(vnode_t *node, vfs_stat_t *out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->st_ino = node->ino;
|
||||
out->st_type = node->type;
|
||||
out->st_mode = node->mode;
|
||||
out->st_uid = node->uid;
|
||||
out->st_gid = node->gid;
|
||||
out->st_size = node->size;
|
||||
out->st_blocks = (node->size + 511) / 512;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ext2_vnode_ref(vnode_t *node) { (void)node; }
|
||||
static void ext2_vnode_unref(vnode_t *node) {
|
||||
if (node->fs_data) kfree(node->fs_data);
|
||||
kfree(node);
|
||||
}
|
||||
|
||||
static const vnode_ops_t ext2_file_ops = {
|
||||
.read = ext2_file_read,
|
||||
.write = ext2_file_write,
|
||||
.truncate = ext2_file_truncate,
|
||||
.stat = ext2_stat,
|
||||
.ref = ext2_vnode_ref,
|
||||
.unref = ext2_vnode_unref,
|
||||
};
|
||||
|
||||
static int ext2_dir_lookup(vnode_t *dir, const char *name, vnode_t **out) {
|
||||
ext2_vdata_t *vd = dir->fs_data;
|
||||
ext2_t *fs = vd->fs;
|
||||
ext2_inode_t di;
|
||||
int r = inode_read(fs, vd->ino, &di);
|
||||
if (r < 0) return r;
|
||||
uint8_t *bb = kmalloc(fs->block_size);
|
||||
if (!bb) return -ENOMEM;
|
||||
size_t nl = strlen(name);
|
||||
uint32_t ds = di.i_size;
|
||||
uint32_t pos = 0, fb = 0;
|
||||
while (pos < ds) {
|
||||
int32_t db = get_block_num(fs, &di, fb);
|
||||
if (db <= 0) { fb++; pos += fs->block_size; continue; }
|
||||
block_read(fs, (uint32_t)db, bb);
|
||||
uint32_t off = 0;
|
||||
while (off < fs->block_size && (pos + off) < ds) {
|
||||
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
|
||||
if (de->rec_len == 0) break;
|
||||
if (de->inode != 0 && de->name_len == nl && memcmp(de->name, name, nl) == 0) {
|
||||
ext2_inode_t ci;
|
||||
inode_read(fs, de->inode, &ci);
|
||||
*out = ext2_make_vnode(fs, de->inode, &ci);
|
||||
kfree(bb);
|
||||
return *out ? 0 : -ENOMEM;
|
||||
}
|
||||
off += de->rec_len;
|
||||
}
|
||||
fb++; pos += fs->block_size;
|
||||
}
|
||||
kfree(bb);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int ext2_dir_readdir(vnode_t *dir, uint64_t index, vfs_dirent_t *out) {
|
||||
ext2_vdata_t *vd = dir->fs_data;
|
||||
ext2_t *fs = vd->fs;
|
||||
ext2_inode_t di;
|
||||
int r = inode_read(fs, vd->ino, &di);
|
||||
if (r < 0) return r;
|
||||
uint8_t *bb = kmalloc(fs->block_size);
|
||||
if (!bb) return -ENOMEM;
|
||||
uint32_t ds = di.i_size, pos = 0, fb = 0;
|
||||
uint64_t cur = 0;
|
||||
while (pos < ds) {
|
||||
int32_t db = get_block_num(fs, &di, fb);
|
||||
if (db <= 0) { fb++; pos += fs->block_size; continue; }
|
||||
block_read(fs, (uint32_t)db, bb);
|
||||
uint32_t off = 0;
|
||||
while (off < fs->block_size && (pos + off) < ds) {
|
||||
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
|
||||
if (de->rec_len == 0) break;
|
||||
if (de->inode != 0 && de->name_len > 0) {
|
||||
bool skip = (de->name_len == 1 && de->name[0] == '.') ||
|
||||
(de->name_len == 2 && de->name[0] == '.' && de->name[1] == '.');
|
||||
if (!skip) {
|
||||
if (cur == index) {
|
||||
out->d_ino = de->inode;
|
||||
out->d_type = (de->file_type == EXT2_FT_DIR) ? VFS_NODE_DIR : VFS_NODE_FILE;
|
||||
size_t n = de->name_len;
|
||||
if (n >= VFS_MAX_NAME) n = VFS_MAX_NAME - 1;
|
||||
memcpy(out->d_name, de->name, n);
|
||||
out->d_name[n] = '\0';
|
||||
kfree(bb);
|
||||
return 0;
|
||||
}
|
||||
cur++;
|
||||
}
|
||||
}
|
||||
off += de->rec_len;
|
||||
}
|
||||
fb++; pos += fs->block_size;
|
||||
}
|
||||
kfree(bb);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int ext2_dir_add_entry(ext2_t *fs, uint32_t dir_ino, uint32_t child_ino,
|
||||
uint8_t file_type, const char *name) {
|
||||
if (!name || !name[0]) return -EINVAL;
|
||||
ext2_inode_t di;
|
||||
int r = inode_read(fs, dir_ino, &di);
|
||||
if (r < 0) return r;
|
||||
uint8_t nl = (uint8_t)strlen(name);
|
||||
uint16_t need = (uint16_t)((8 + nl + 3) & ~3);
|
||||
uint8_t *bb = kmalloc(fs->block_size);
|
||||
if (!bb) return -ENOMEM;
|
||||
uint32_t nb = (di.i_size + fs->block_size - 1) / fs->block_size;
|
||||
for (uint32_t fb = 0; fb < nb; fb++) {
|
||||
int32_t db = get_block_num(fs, &di, fb);
|
||||
if (db <= 0) continue;
|
||||
block_read(fs, (uint32_t)db, bb);
|
||||
uint32_t off = 0;
|
||||
while (off < fs->block_size) {
|
||||
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
|
||||
if (de->rec_len == 0) break;
|
||||
if (de->inode == 0 && de->rec_len >= need) {
|
||||
de->inode = child_ino;
|
||||
de->name_len = nl;
|
||||
de->file_type = file_type;
|
||||
memcpy(de->name, name, nl);
|
||||
block_write(fs, (uint32_t)db, bb);
|
||||
kfree(bb);
|
||||
return 0;
|
||||
}
|
||||
uint16_t actual = (uint16_t)((8 + de->name_len + 3) & ~3);
|
||||
uint16_t slack = de->rec_len - actual;
|
||||
if (slack >= need) {
|
||||
de->rec_len = actual;
|
||||
ext2_dir_entry_t *ne = (ext2_dir_entry_t *)(bb + off + actual);
|
||||
ne->inode = child_ino;
|
||||
ne->rec_len = slack;
|
||||
ne->name_len = nl;
|
||||
ne->file_type = file_type;
|
||||
memcpy(ne->name, name, nl);
|
||||
block_write(fs, (uint32_t)db, bb);
|
||||
kfree(bb);
|
||||
return 0;
|
||||
}
|
||||
off += de->rec_len;
|
||||
}
|
||||
}
|
||||
int32_t new_blk = alloc_block(fs);
|
||||
if (new_blk < 0) { kfree(bb); return (int)new_blk; }
|
||||
set_block_num(fs, &di, nb, (uint32_t)new_blk);
|
||||
di.i_size += fs->block_size;
|
||||
di.i_blocks += fs->block_size / 512;
|
||||
memset(bb, 0, fs->block_size);
|
||||
ext2_dir_entry_t *de = (ext2_dir_entry_t *)bb;
|
||||
de->inode = child_ino;
|
||||
de->rec_len = (uint16_t)fs->block_size;
|
||||
de->name_len = nl;
|
||||
de->file_type = file_type;
|
||||
memcpy(de->name, name, nl);
|
||||
block_write(fs, (uint32_t)new_blk, bb);
|
||||
inode_write(fs, dir_ino, &di);
|
||||
kfree(bb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext2_dir_mkdir(vnode_t *dir, const char *name, uint32_t mode) {
|
||||
if (!name || !name[0]) return -EINVAL;
|
||||
ext2_vdata_t *vd = dir->fs_data;
|
||||
ext2_t *fs = vd->fs;
|
||||
vnode_t *ex = NULL;
|
||||
if (ext2_dir_lookup(dir, name, &ex) == 0) { vnode_unref(ex); return -EEXIST; }
|
||||
int32_t ino = alloc_inode(fs);
|
||||
if (ino < 0) return (int)ino;
|
||||
ext2_inode_t ndi;
|
||||
memset(&ndi, 0, sizeof(ndi));
|
||||
ndi.i_mode = EXT2_S_IFDIR | (uint16_t)(mode ? mode : 0755);
|
||||
ndi.i_links_count = 2;
|
||||
int32_t blk = alloc_block(fs);
|
||||
if (blk < 0) { free_inode(fs, (uint32_t)ino); return (int)blk; }
|
||||
ndi.i_block[0] = (uint32_t)blk;
|
||||
ndi.i_size = fs->block_size;
|
||||
ndi.i_blocks = fs->block_size / 512;
|
||||
uint8_t *bb = kzalloc(fs->block_size);
|
||||
if (!bb) { free_block(fs, (uint32_t)blk); free_inode(fs, (uint32_t)ino); return -ENOMEM; }
|
||||
ext2_dir_entry_t *dot = (ext2_dir_entry_t *)bb;
|
||||
dot->inode = (uint32_t)ino; dot->rec_len = 12; dot->name_len = 1;
|
||||
dot->file_type = EXT2_FT_DIR; dot->name[0] = '.';
|
||||
ext2_dir_entry_t *dotdot = (ext2_dir_entry_t *)(bb + 12);
|
||||
dotdot->inode = vd->ino; dotdot->rec_len = (uint16_t)(fs->block_size - 12);
|
||||
dotdot->name_len = 2; dotdot->file_type = EXT2_FT_DIR;
|
||||
dotdot->name[0] = '.'; dotdot->name[1] = '.';
|
||||
block_write(fs, (uint32_t)blk, bb);
|
||||
kfree(bb);
|
||||
inode_write(fs, (uint32_t)ino, &ndi);
|
||||
int r = ext2_dir_add_entry(fs, vd->ino, (uint32_t)ino, EXT2_FT_DIR, name);
|
||||
if (r < 0) { free_block(fs, (uint32_t)blk); free_inode(fs, (uint32_t)ino); return r; }
|
||||
ext2_inode_t pdi;
|
||||
inode_read(fs, vd->ino, &pdi);
|
||||
pdi.i_links_count++;
|
||||
inode_write(fs, vd->ino, &pdi);
|
||||
uint32_t grp = ((uint32_t)ino - 1) / fs->sb.s_inodes_per_group;
|
||||
fs->gdt[grp].bg_used_dirs_count++;
|
||||
fs->dirty = true;
|
||||
dir->size++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext2_dir_create(vnode_t *dir, const char *name, uint32_t mode, vnode_t **out) {
|
||||
if (!name || !name[0]) return -EINVAL;
|
||||
ext2_vdata_t *vd = dir->fs_data;
|
||||
ext2_t *fs = vd->fs;
|
||||
if (ext2_dir_lookup(dir, name, out) == 0) return 0;
|
||||
int32_t ino = alloc_inode(fs);
|
||||
if (ino < 0) return (int)ino;
|
||||
ext2_inode_t ndi;
|
||||
memset(&ndi, 0, sizeof(ndi));
|
||||
ndi.i_mode = EXT2_S_IFREG | (uint16_t)(mode ? mode : 0644);
|
||||
ndi.i_links_count = 1;
|
||||
inode_write(fs, (uint32_t)ino, &ndi);
|
||||
int r = ext2_dir_add_entry(fs, vd->ino, (uint32_t)ino, EXT2_FT_REG_FILE, name);
|
||||
if (r < 0) { free_inode(fs, (uint32_t)ino); return r; }
|
||||
*out = ext2_make_vnode(fs, (uint32_t)ino, &ndi);
|
||||
if (!*out) return -ENOMEM;
|
||||
fs->dirty = true;
|
||||
dir->size++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext2_dir_unlink(vnode_t *dir, const char *name) {
|
||||
ext2_vdata_t *vd = dir->fs_data;
|
||||
ext2_t *fs = vd->fs;
|
||||
ext2_inode_t di;
|
||||
int r = inode_read(fs, vd->ino, &di);
|
||||
if (r < 0) return r;
|
||||
uint8_t *bb = kmalloc(fs->block_size);
|
||||
if (!bb) return -ENOMEM;
|
||||
size_t nl = strlen(name);
|
||||
uint32_t nblocks = (di.i_size + fs->block_size - 1) / fs->block_size;
|
||||
for (uint32_t fb = 0; fb < nblocks; fb++) {
|
||||
int32_t db = get_block_num(fs, &di, fb);
|
||||
if (db <= 0) continue;
|
||||
block_read(fs, (uint32_t)db, bb);
|
||||
uint32_t off = 0;
|
||||
ext2_dir_entry_t *prev = NULL;
|
||||
while (off < fs->block_size) {
|
||||
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
|
||||
if (de->rec_len == 0) break;
|
||||
if (de->inode != 0 && de->name_len == nl && memcmp(de->name, name, nl) == 0) {
|
||||
uint32_t cino = de->inode;
|
||||
ext2_inode_t ci;
|
||||
inode_read(fs, cino, &ci);
|
||||
bool is_dir = ((ci.i_mode & 0xF000) == EXT2_S_IFDIR);
|
||||
free_all_blocks(fs, &ci);
|
||||
free_inode(fs, cino);
|
||||
if (prev) prev->rec_len += de->rec_len;
|
||||
else de->inode = 0;
|
||||
block_write(fs, (uint32_t)db, bb);
|
||||
if (is_dir) {
|
||||
ext2_inode_t pdi;
|
||||
inode_read(fs, vd->ino, &pdi);
|
||||
if (pdi.i_links_count > 1) pdi.i_links_count--;
|
||||
inode_write(fs, vd->ino, &pdi);
|
||||
uint32_t grp = (cino - 1) / fs->sb.s_inodes_per_group;
|
||||
if (fs->gdt[grp].bg_used_dirs_count > 0)
|
||||
fs->gdt[grp].bg_used_dirs_count--;
|
||||
}
|
||||
dir->size--;
|
||||
fs->dirty = true;
|
||||
kfree(bb);
|
||||
return 0;
|
||||
}
|
||||
prev = de;
|
||||
off += de->rec_len;
|
||||
}
|
||||
}
|
||||
kfree(bb);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int ext2_dir_rename(vnode_t *src_dir, const char *src_name,
|
||||
vnode_t *dst_dir, const char *dst_name) {
|
||||
ext2_vdata_t *svd = src_dir->fs_data;
|
||||
ext2_t *fs = svd->fs;
|
||||
vnode_t *child = NULL;
|
||||
int r = ext2_dir_lookup(src_dir, src_name, &child);
|
||||
if (r < 0) return r;
|
||||
uint8_t ft = (child->type == VFS_NODE_DIR) ? EXT2_FT_DIR : EXT2_FT_REG_FILE;
|
||||
uint32_t cino = (uint32_t)child->ino;
|
||||
vnode_unref(child);
|
||||
vnode_t *ex = NULL;
|
||||
if (ext2_dir_lookup(dst_dir, dst_name, &ex) == 0) {
|
||||
vnode_unref(ex);
|
||||
ext2_dir_unlink(dst_dir, dst_name);
|
||||
}
|
||||
ext2_vdata_t *dvd = dst_dir->fs_data;
|
||||
r = ext2_dir_add_entry(fs, dvd->ino, cino, ft, dst_name);
|
||||
if (r < 0) return r;
|
||||
ext2_inode_t sdi;
|
||||
inode_read(fs, svd->ino, &sdi);
|
||||
uint8_t *bb = kmalloc(fs->block_size);
|
||||
if (!bb) return -ENOMEM;
|
||||
size_t snl = strlen(src_name);
|
||||
uint32_t nblocks = (sdi.i_size + fs->block_size - 1) / fs->block_size;
|
||||
for (uint32_t fb = 0; fb < nblocks; fb++) {
|
||||
int32_t db = get_block_num(fs, &sdi, fb);
|
||||
if (db <= 0) continue;
|
||||
block_read(fs, (uint32_t)db, bb);
|
||||
uint32_t off = 0;
|
||||
ext2_dir_entry_t *prev = NULL;
|
||||
while (off < fs->block_size) {
|
||||
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
|
||||
if (de->rec_len == 0) break;
|
||||
if (de->inode == cino && de->name_len == snl &&
|
||||
memcmp(de->name, src_name, snl) == 0) {
|
||||
if (prev) prev->rec_len += de->rec_len;
|
||||
else de->inode = 0;
|
||||
block_write(fs, (uint32_t)db, bb);
|
||||
kfree(bb);
|
||||
fs->dirty = true;
|
||||
return 0;
|
||||
}
|
||||
prev = de;
|
||||
off += de->rec_len;
|
||||
}
|
||||
}
|
||||
kfree(bb);
|
||||
fs->dirty = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const vnode_ops_t ext2_dir_ops = {
|
||||
.lookup = ext2_dir_lookup,
|
||||
.readdir = ext2_dir_readdir,
|
||||
.mkdir = ext2_dir_mkdir,
|
||||
.create = ext2_dir_create,
|
||||
.unlink = ext2_dir_unlink,
|
||||
.rename = ext2_dir_rename,
|
||||
.stat = ext2_stat,
|
||||
.ref = ext2_vnode_ref,
|
||||
.unref = ext2_vnode_unref,
|
||||
};
|
||||
|
||||
int ext2_format(blkdev_t *dev, const char *label) {
|
||||
if (!dev) return -EINVAL;
|
||||
uint64_t disk_bytes = dev->size_bytes;
|
||||
if (disk_bytes < 64 * 1024) return -ENOSPC;
|
||||
uint32_t block_size = 1024;
|
||||
uint32_t total_blocks = (uint32_t)(disk_bytes / block_size);
|
||||
uint32_t blocks_per_group = block_size * 8;
|
||||
uint32_t inodes_per_group = (total_blocks < 8192) ? 128 : 256;
|
||||
uint32_t groups_count = (total_blocks + blocks_per_group - 1) / blocks_per_group;
|
||||
if (groups_count == 0) groups_count = 1;
|
||||
uint32_t total_inodes = inodes_per_group * groups_count;
|
||||
uint32_t inode_size = 128;
|
||||
uint32_t it_blocks_pg = (inodes_per_group * inode_size + block_size - 1) / block_size;
|
||||
uint32_t first_data_block = 1;
|
||||
uint8_t *zb = kzalloc(block_size);
|
||||
if (!zb) return -ENOMEM;
|
||||
for (uint32_t b = 0; b < total_blocks && b < 16; b++)
|
||||
blkdev_write(dev, (uint64_t)b * block_size, zb, block_size);
|
||||
ext2_superblock_t sb;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.s_inodes_count = total_inodes;
|
||||
sb.s_blocks_count = total_blocks;
|
||||
sb.s_r_blocks_count = total_blocks / 20;
|
||||
sb.s_free_blocks_count = total_blocks;
|
||||
sb.s_free_inodes_count = total_inodes;
|
||||
sb.s_first_data_block = first_data_block;
|
||||
sb.s_log_block_size = 0;
|
||||
sb.s_blocks_per_group = blocks_per_group;
|
||||
sb.s_frags_per_group = blocks_per_group;
|
||||
sb.s_inodes_per_group = inodes_per_group;
|
||||
sb.s_magic = EXT2_SUPER_MAGIC;
|
||||
sb.s_state = EXT2_VALID_FS;
|
||||
sb.s_errors = 1;
|
||||
sb.s_rev_level = EXT2_DYNAMIC_REV;
|
||||
sb.s_first_ino = 11;
|
||||
sb.s_inode_size = (uint16_t)inode_size;
|
||||
sb.s_max_mnt_count = 20;
|
||||
if (label) strncpy(sb.s_volume_name, label, 15);
|
||||
uint32_t gdt_block = first_data_block + 1;
|
||||
uint32_t gdt_blocks = (groups_count * sizeof(ext2_group_desc_t) + block_size - 1) / block_size;
|
||||
ext2_group_desc_t *gdt = kzalloc(gdt_blocks * block_size);
|
||||
if (!gdt) { kfree(zb); return -ENOMEM; }
|
||||
uint32_t used_total = first_data_block;
|
||||
printf(" ext2: ");
|
||||
uint32_t last_pct_ext2 = 999;
|
||||
uint32_t spinner_ext2 = 0;
|
||||
for (uint32_t g = 0; g < groups_count; g++) {
|
||||
uint32_t gs = g * blocks_per_group + first_data_block;
|
||||
uint32_t cb = (g == 0) ? (gdt_block + gdt_blocks) : gs;
|
||||
gdt[g].bg_block_bitmap = cb++;
|
||||
gdt[g].bg_inode_bitmap = cb++;
|
||||
gdt[g].bg_inode_table = cb;
|
||||
cb += it_blocks_pg;
|
||||
uint32_t meta = cb - gs;
|
||||
if (g == 0) meta = cb - first_data_block;
|
||||
uint32_t gb = blocks_per_group;
|
||||
if (g == groups_count - 1) gb = total_blocks - gs;
|
||||
gdt[g].bg_free_blocks_count = (gb > meta) ? (uint16_t)(gb - meta) : 0;
|
||||
gdt[g].bg_free_inodes_count = (uint16_t)inodes_per_group;
|
||||
used_total += meta;
|
||||
for (uint32_t t = 0; t < it_blocks_pg; t++)
|
||||
blkdev_write(dev, (uint64_t)(gdt[g].bg_inode_table + t) * block_size, zb, block_size);
|
||||
uint8_t *bbmp = kzalloc(block_size);
|
||||
for (uint32_t i = 0; i < meta; i++) bmp_set(bbmp, i);
|
||||
if (g == 0)
|
||||
for (uint32_t i = 0; i < (gdt_block + gdt_blocks - first_data_block); i++)
|
||||
bmp_set(bbmp, i);
|
||||
for (uint32_t i = gb; i < blocks_per_group; i++) bmp_set(bbmp, i);
|
||||
blkdev_write(dev, (uint64_t)gdt[g].bg_block_bitmap * block_size, bbmp, block_size);
|
||||
kfree(bbmp);
|
||||
uint8_t *ibmp = kzalloc(block_size);
|
||||
if (g == 0) {
|
||||
for (uint32_t i = 0; i < 11; i++) { bmp_set(ibmp, i); gdt[g].bg_free_inodes_count--; }
|
||||
}
|
||||
for (uint32_t i = inodes_per_group; i < block_size * 8; i++) bmp_set(ibmp, i);
|
||||
blkdev_write(dev, (uint64_t)gdt[g].bg_inode_bitmap * block_size, ibmp, block_size);
|
||||
kfree(ibmp);
|
||||
|
||||
uint32_t pct = ((g + 1) * 100) / groups_count;
|
||||
if (pct != last_pct_ext2) {
|
||||
static const char glyphs[4] = { '|', '/', '-', '\\' };
|
||||
printf("\r \r %c ext2: %u%% (group %u/%u)",
|
||||
glyphs[spinner_ext2 & 3], pct, g + 1, groups_count);
|
||||
spinner_ext2++;
|
||||
last_pct_ext2 = pct;
|
||||
}
|
||||
}
|
||||
printf("\r \r ext2: done\n");
|
||||
sb.s_free_blocks_count = total_blocks - used_total;
|
||||
sb.s_free_inodes_count = total_inodes - 11;
|
||||
int32_t root_blk = -1;
|
||||
{
|
||||
uint8_t *bbmp = kmalloc(block_size);
|
||||
blkdev_read(dev, (uint64_t)gdt[0].bg_block_bitmap * block_size, bbmp, block_size);
|
||||
for (uint32_t i = 0; i < blocks_per_group; i++) {
|
||||
if (!bmp_test(bbmp, i)) {
|
||||
bmp_set(bbmp, i);
|
||||
blkdev_write(dev, (uint64_t)gdt[0].bg_block_bitmap * block_size, bbmp, block_size);
|
||||
root_blk = (int32_t)(i + first_data_block);
|
||||
gdt[0].bg_free_blocks_count--;
|
||||
sb.s_free_blocks_count--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
kfree(bbmp);
|
||||
}
|
||||
if (root_blk < 0) { kfree(gdt); kfree(zb); return -ENOSPC; }
|
||||
ext2_inode_t ri;
|
||||
memset(&ri, 0, sizeof(ri));
|
||||
ri.i_mode = EXT2_S_IFDIR | 0755;
|
||||
ri.i_links_count = 2;
|
||||
ri.i_block[0] = (uint32_t)root_blk;
|
||||
ri.i_size = block_size;
|
||||
ri.i_blocks = block_size / 512;
|
||||
uint64_t roff = (uint64_t)gdt[0].bg_inode_table * block_size + (uint64_t)(EXT2_ROOT_INO - 1) * inode_size;
|
||||
blkdev_write(dev, roff, &ri, sizeof(ri));
|
||||
uint8_t *rdb = kzalloc(block_size);
|
||||
ext2_dir_entry_t *dot = (ext2_dir_entry_t *)rdb;
|
||||
dot->inode = EXT2_ROOT_INO; dot->rec_len = 12; dot->name_len = 1;
|
||||
dot->file_type = EXT2_FT_DIR; dot->name[0] = '.';
|
||||
ext2_dir_entry_t *dotdot = (ext2_dir_entry_t *)(rdb + 12);
|
||||
dotdot->inode = EXT2_ROOT_INO; dotdot->rec_len = (uint16_t)(block_size - 12);
|
||||
dotdot->name_len = 2; dotdot->file_type = EXT2_FT_DIR;
|
||||
dotdot->name[0] = '.'; dotdot->name[1] = '.';
|
||||
blkdev_write(dev, (uint64_t)root_blk * block_size, rdb, block_size);
|
||||
kfree(rdb);
|
||||
gdt[0].bg_used_dirs_count = 1;
|
||||
blkdev_write(dev, EXT2_SUPER_OFFSET, &sb, sizeof(sb));
|
||||
blkdev_write(dev, (uint64_t)gdt_block * block_size, gdt, gdt_blocks * block_size);
|
||||
kfree(gdt);
|
||||
kfree(zb);
|
||||
serial_printf("[ext2] formatted: %u blocks (%u KiB), %u inodes, bs=%u, label='%s'\n",
|
||||
total_blocks, total_blocks * block_size / 1024, total_inodes, block_size,
|
||||
label ? label : "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
vnode_t *ext2_mount(blkdev_t *dev) {
|
||||
if (!dev) return NULL;
|
||||
ext2_t *fs = kzalloc(sizeof(ext2_t));
|
||||
if (!fs) return NULL;
|
||||
fs->dev = dev;
|
||||
if (blkdev_read(dev, EXT2_SUPER_OFFSET, &fs->sb, sizeof(fs->sb)) < 0) { kfree(fs); return NULL; }
|
||||
if (fs->sb.s_magic != EXT2_SUPER_MAGIC) {
|
||||
serial_printf("[ext2] bad magic: 0x%x (expected 0x%x)\n", fs->sb.s_magic, EXT2_SUPER_MAGIC);
|
||||
kfree(fs);
|
||||
return NULL;
|
||||
}
|
||||
fs->block_size = 1024 << fs->sb.s_log_block_size;
|
||||
fs->inode_size = (fs->sb.s_rev_level >= EXT2_DYNAMIC_REV) ? fs->sb.s_inode_size : 128;
|
||||
fs->groups_count = (fs->sb.s_blocks_count + fs->sb.s_blocks_per_group - 1) / fs->sb.s_blocks_per_group;
|
||||
fs->inodes_per_block = fs->block_size / fs->inode_size;
|
||||
fs->ptrs_per_block = fs->block_size / 4;
|
||||
uint32_t gdt_block = (fs->block_size == 1024) ? 2 : 1;
|
||||
uint32_t gdt_sz = fs->groups_count * sizeof(ext2_group_desc_t);
|
||||
uint32_t gdt_blocks = (gdt_sz + fs->block_size - 1) / fs->block_size;
|
||||
fs->gdt = kmalloc(gdt_blocks * fs->block_size);
|
||||
if (!fs->gdt) { kfree(fs); return NULL; }
|
||||
if (blkdev_read(dev, (uint64_t)gdt_block * fs->block_size, fs->gdt, gdt_blocks * fs->block_size) < 0) {
|
||||
kfree(fs->gdt); kfree(fs); return NULL;
|
||||
}
|
||||
ext2_inode_t root_di;
|
||||
if (inode_read(fs, EXT2_ROOT_INO, &root_di) < 0) { kfree(fs->gdt); kfree(fs); return NULL; }
|
||||
vnode_t *root = ext2_make_vnode(fs, EXT2_ROOT_INO, &root_di);
|
||||
if (!root) { kfree(fs->gdt); kfree(fs); return NULL; }
|
||||
serial_printf("[ext2] mounted '%s': %u blocks (%u free), %u inodes (%u free), bs=%u\n",
|
||||
fs->sb.s_volume_name, fs->sb.s_blocks_count, fs->sb.s_free_blocks_count,
|
||||
fs->sb.s_inodes_count, fs->sb.s_free_inodes_count, fs->block_size);
|
||||
return root;
|
||||
}
|
||||
|
||||
void ext2_sync(ext2_t *fs) {
|
||||
if (!fs || !fs->dirty) return;
|
||||
sb_flush(fs);
|
||||
gdt_flush(fs);
|
||||
if (fs->dev->ops && fs->dev->ops->flush) fs->dev->ops->flush(fs->dev);
|
||||
fs->dirty = false;
|
||||
}
|
||||
|
||||
void ext2_unmount(ext2_t *fs) {
|
||||
if (!fs) return;
|
||||
ext2_sync(fs);
|
||||
if (fs->gdt) kfree(fs->gdt);
|
||||
kfree(fs);
|
||||
}
|
||||
|
||||
int ext2_statvfs(vnode_t *root, vfs_statvfs_t *out) {
|
||||
if (!root || !root->fs_data || !out) return -EINVAL;
|
||||
ext2_vdata_t *vd = (ext2_vdata_t *)root->fs_data;
|
||||
ext2_t *fs = vd->fs;
|
||||
if (!fs) return -EINVAL;
|
||||
|
||||
out->f_bsize = fs->block_size;
|
||||
out->f_blocks = fs->sb.s_blocks_count;
|
||||
out->f_bfree = fs->sb.s_free_blocks_count;
|
||||
out->f_bavail = fs->sb.s_free_blocks_count;
|
||||
out->f_files = fs->sb.s_inodes_count;
|
||||
out->f_ffree = fs->sb.s_free_inodes_count;
|
||||
out->f_flag = 0;
|
||||
out->f_namemax = 255;
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user