diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-07-10 11:31:46 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-07-10 11:31:46 +0800 |
| commit | e90b72ec3082588b296956730bd2930cf8d5d671 (patch) | |
| tree | 36ff6916af9b4176ea5a9662219d11e721f0b691 /fs/ramfs | |
| parent | 1767d390fbf3d0c65389de46caf0b6d43ef3b6ad (diff) | |
| download | lume-e90b72ec3082588b296956730bd2930cf8d5d671.tar.gz lume-e90b72ec3082588b296956730bd2930cf8d5d671.tar.xz | |
refactor: promote new architecture to repository root
Archive the deprecated implementation under lume-legacy, make the new kernel the default root-level build, and update repository entry points without changing kernel behavior.
Diffstat (limited to 'fs/ramfs')
| -rw-r--r-- | fs/ramfs/file.cc | 216 | ||||
| -rw-r--r-- | fs/ramfs/inode.cc | 164 | ||||
| -rw-r--r-- | fs/ramfs/super.cc | 154 |
3 files changed, 534 insertions, 0 deletions
diff --git a/fs/ramfs/file.cc b/fs/ramfs/file.cc new file mode 100644 index 0000000..6d9a070 --- /dev/null +++ b/fs/ramfs/file.cc @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +/* + * ramfs/file.cc — RamFS file operations (read/write via in-memory pages) + * + * Phase 1: simple flat buffer per-inode via i_private. + * Will migrate to page_cache integration later. + */ + +#include <lume/fs.h> +#include <lume/file.h> +#include <lume/dcache.h> +#include <lume/slab.h> +#include <lume/errno.h> +#include <lume/fs_types.h> + +/* + * Simplest possible in-memory file storage: + * i_private points to a dynamically grown buffer. + */ +struct ramfs_file_data { + char* buf; + uint64 capacity; +}; + +static ramfs_file_data* get_or_create_data(inode* ip) +{ + if (!ip->i_private) { + auto* data = new ramfs_file_data{}; + data->buf = nullptr; + data->capacity = 0; + ip->i_private = data; + } + return static_cast<ramfs_file_data*>(ip->i_private); +} + +static int ensure_capacity(ramfs_file_data* data, uint64 needed) +{ + if (needed <= data->capacity) return 0; + + /* Round up to page size */ + uint64 new_cap = (needed + 4095) & ~4095ULL; + char* new_buf = new char[new_cap]; + if (!new_buf) return -ENOMEM; + + /* Copy old data */ + if (data->buf && data->capacity > 0) { + for (uint64 i = 0; i < data->capacity; i++) + new_buf[i] = data->buf[i]; + } + /* Zero new region */ + for (uint64 i = data->capacity; i < new_cap; i++) + new_buf[i] = 0; + + delete[] data->buf; + data->buf = new_buf; + data->capacity = new_cap; + return 0; +} + +/* ================================================================== */ +/* ramfs_read */ +/* ================================================================== */ + +static int64 ramfs_read(file* fp, char* buf, uint64 count, int64* pos) +{ + inode* ip = file_inode(fp); + int64 offset = *pos; + + if (offset < 0) return -EINVAL; + if (offset >= ip->i_size) return 0; + + int64 avail = ip->i_size - offset; + if (static_cast<int64>(count) > avail) + count = static_cast<uint64>(avail); + + auto* data = static_cast<ramfs_file_data*>(ip->i_private); + if (!data || !data->buf) return 0; + + /* Copy to user buffer. + * TODO: use copy_to_user when called from syscall context */ + for (uint64 i = 0; i < count; i++) + buf[i] = data->buf[offset + i]; + + *pos += count; + return static_cast<int64>(count); +} + +/* ================================================================== */ +/* ramfs_write */ +/* ================================================================== */ + +static int64 ramfs_write(file* fp, const char* buf, uint64 count, int64* pos) +{ + inode* ip = file_inode(fp); + int64 offset = *pos; + + if (fp->f_flags & O_APPEND) + offset = ip->i_size; + + if (offset < 0) return -EINVAL; + + auto* data = get_or_create_data(ip); + uint64 end = static_cast<uint64>(offset) + count; + + int err = ensure_capacity(data, end); + if (err) return err; + + /* Copy from user buffer */ + for (uint64 i = 0; i < count; i++) + data->buf[offset + i] = buf[i]; + + if (static_cast<int64>(end) > ip->i_size) + ip->i_size = end; + + *pos = end; + mark_inode_dirty(ip); + return static_cast<int64>(count); +} + +/* ================================================================== */ +/* ramfs_dir_iterate — getdents64 */ +/* ================================================================== */ + +static int ramfs_dir_iterate(file* fp, dir_context* ctx) +{ + dentry* dir_de = file_dentry(fp); + inode* dir_ip = file_inode(fp); + int64 pos = ctx->pos; + int count = 0; + + /* pos 0: emit "." */ + if (pos == 0) { + if (!ctx->actor(ctx, ".", 1, pos, dir_ip->i_ino, DT_DIR)) + return 0; + ctx->pos = 1; + pos = 1; + count++; + } + + /* pos 1: emit ".." */ + if (pos == 1) { + uint64 parent_ino = dir_de->d_parent->d_inode + ? dir_de->d_parent->d_inode->i_ino : dir_ip->i_ino; + if (!ctx->actor(ctx, "..", 2, pos, parent_ino, DT_DIR)) + return 0; + ctx->pos = 2; + pos = 2; + count++; + } + + /* pos >= 2: iterate children */ + int64 child_idx = 2; + list_node* node; + list_for_each(node, &dir_de->d_children) { + dentry* child = list_entry(node, dentry, d_child); + + if (!child->d_inode) + continue; /* skip negative dentries */ + + if (child_idx >= pos) { + uint8 d_type = DT_UNKNOWN; + if (S_ISDIR(child->d_inode->i_mode)) d_type = DT_DIR; + else if (S_ISREG(child->d_inode->i_mode)) d_type = DT_REG; + else if (S_ISCHR(child->d_inode->i_mode)) d_type = DT_CHR; + else if (S_ISBLK(child->d_inode->i_mode)) d_type = DT_BLK; + else if (S_ISLNK(child->d_inode->i_mode)) d_type = DT_LNK; + else if (S_ISFIFO(child->d_inode->i_mode)) d_type = DT_FIFO; + + if (!ctx->actor(ctx, child->d_name.name, + static_cast<int>(child->d_name.len), + child_idx, child->d_inode->i_ino, d_type)) + return 0; + + ctx->pos = child_idx + 1; + count++; + } + child_idx++; + } + + return count; +} + +extern file_operations ramfs_file_operations; +extern file_operations ramfs_dir_operations; + +file_operations ramfs_file_operations; +file_operations ramfs_dir_operations; + +void ramfs_file_ops_init() +{ + ramfs_file_operations.llseek = nullptr; + ramfs_file_operations.read = ramfs_read; + ramfs_file_operations.write = ramfs_write; + ramfs_file_operations.iterate_shared = nullptr; + ramfs_file_operations.poll = nullptr; + ramfs_file_operations.unlocked_ioctl = nullptr; + ramfs_file_operations.mmap = nullptr; + ramfs_file_operations.open = nullptr; + ramfs_file_operations.flush = nullptr; + ramfs_file_operations.release = nullptr; + ramfs_file_operations.fsync = nullptr; + + ramfs_dir_operations.llseek = nullptr; + ramfs_dir_operations.read = nullptr; + ramfs_dir_operations.write = nullptr; + ramfs_dir_operations.iterate_shared = ramfs_dir_iterate; + ramfs_dir_operations.poll = nullptr; + ramfs_dir_operations.unlocked_ioctl = nullptr; + ramfs_dir_operations.mmap = nullptr; + ramfs_dir_operations.open = nullptr; + ramfs_dir_operations.flush = nullptr; + ramfs_dir_operations.release = nullptr; + ramfs_dir_operations.fsync = nullptr; +} diff --git a/fs/ramfs/inode.cc b/fs/ramfs/inode.cc new file mode 100644 index 0000000..c5e49bf --- /dev/null +++ b/fs/ramfs/inode.cc @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +/* + * ramfs/inode.cc — RamFS inode operations + * + * A simple in-memory filesystem (like Linux tmpfs without swap). + * All data lives in memory via the page cache. + */ + +#include <lume/fs.h> +#include <lume/dcache.h> +#include <lume/file.h> +#include <lume/slab.h> +#include <lume/errno.h> +#include <lume/fs_types.h> + +/* Forward declarations */ +extern file_operations ramfs_file_operations; +extern file_operations ramfs_dir_operations; +extern inode_operations ramfs_dir_inode_operations; + +/* ================================================================== */ +/* ramfs_get_inode — allocate + init an inode for ramfs */ +/* ================================================================== */ + +inode* ramfs_get_inode(super_block* sb, const inode* dir, uint32 mode, uint32 rdev) +{ + inode* ip = new_inode(sb); + if (!ip) return nullptr; + + inode_init_owner(ip, dir, mode); + /* TODO: set timestamps to current time when we have a time source */ + + switch (mode & S_IFMT) { + case S_IFREG: + ip->i_op = nullptr; /* regular files don't need inode_ops for Phase 1 */ + ip->i_fop = &ramfs_file_operations; + break; + case S_IFDIR: + ip->i_op = &ramfs_dir_inode_operations; + ip->i_fop = &ramfs_dir_operations; + inc_nlink(ip); /* directories start with nlink=2 (. and parent's link) */ + break; + case S_IFLNK: + /* Symlink: stub for Phase 1 */ + break; + default: + /* Character/block devices: i_rdev */ + ip->i_rdev = rdev; + break; + } + + return ip; +} + +/* ================================================================== */ +/* Directory inode operations */ +/* ================================================================== */ + +static dentry* ramfs_lookup(inode* dir, dentry* de, unsigned int flags) +{ + (void)dir; + (void)flags; + /* For ramfs, everything is in dcache. If we get here, it means + * d_lookup() already failed, so this is a negative dentry. + * Just return nullptr to let the caller see de->d_inode == NULL. */ + return nullptr; +} + +static int ramfs_create(inode* dir, dentry* de, uint32 mode, bool excl) +{ + (void)excl; + inode* ip = ramfs_get_inode(dir->i_sb, dir, mode | S_IFREG, 0); + if (!ip) return -ENOMEM; + + d_instantiate(de, ip); + dget(de); + dir->i_size += de->d_name.len; + mark_inode_dirty(dir); + return 0; +} + +static int ramfs_mkdir(inode* dir, dentry* de, uint32 mode) +{ + inode* ip = ramfs_get_inode(dir->i_sb, dir, mode | S_IFDIR, 0); + if (!ip) return -ENOMEM; + + d_instantiate(de, ip); + dget(de); + inc_nlink(dir); + dir->i_size += de->d_name.len; + mark_inode_dirty(dir); + return 0; +} + +static int ramfs_rmdir(inode* dir, dentry* de) +{ + /* Check if directory is empty */ + if (!list_empty(&de->d_children)) + return -ENOTEMPTY; + + drop_nlink(de->d_inode); /* remove . link */ + drop_nlink(de->d_inode); /* remove parent's link */ + drop_nlink(dir); + d_delete(de); + dir->i_size -= de->d_name.len; + mark_inode_dirty(dir); + return 0; +} + +static int ramfs_unlink(inode* dir, dentry* de) +{ + drop_nlink(de->d_inode); + d_delete(de); + dir->i_size -= de->d_name.len; + mark_inode_dirty(dir); + return 0; +} + +static int ramfs_link(dentry* old_de, inode* dir, dentry* new_de) +{ + inode* ip = old_de->d_inode; + if (!ip) return -ENOENT; + + inc_nlink(ip); + ihold(ip); + d_instantiate(new_de, ip); + dget(new_de); + dir->i_size += new_de->d_name.len; + mark_inode_dirty(dir); + return 0; +} + +static int ramfs_mknod(inode* dir, dentry* de, uint32 mode, uint32 rdev) +{ + inode* ip = ramfs_get_inode(dir->i_sb, dir, mode, rdev); + if (!ip) return -ENOMEM; + + d_instantiate(de, ip); + dget(de); + dir->i_size += de->d_name.len; + mark_inode_dirty(dir); + return 0; +} + +inode_operations ramfs_dir_inode_operations; + +void ramfs_dir_inode_ops_init() +{ + ramfs_dir_inode_operations.lookup = ramfs_lookup; + ramfs_dir_inode_operations.create = ramfs_create; + ramfs_dir_inode_operations.link = ramfs_link; + ramfs_dir_inode_operations.unlink = ramfs_unlink; + ramfs_dir_inode_operations.symlink = nullptr; + ramfs_dir_inode_operations.mkdir = ramfs_mkdir; + ramfs_dir_inode_operations.rmdir = ramfs_rmdir; + ramfs_dir_inode_operations.mknod = ramfs_mknod; + ramfs_dir_inode_operations.rename = nullptr; + ramfs_dir_inode_operations.setattr = nullptr; + ramfs_dir_inode_operations.getattr = nullptr; + ramfs_dir_inode_operations.get_link = nullptr; + ramfs_dir_inode_operations.permission = nullptr; +} diff --git a/fs/ramfs/super.cc b/fs/ramfs/super.cc new file mode 100644 index 0000000..6f929f2 --- /dev/null +++ b/fs/ramfs/super.cc @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +/* + * ramfs/super.cc — RamFS superblock: mount, fill_super, and init + */ + +#include <lume/fs.h> +#include <lume/dcache.h> +#include <lume/file.h> +#include <lume/mount.h> +#include <lume/slab.h> +#include <lume/kprintf.h> +#include <lume/errno.h> +#include <lume/fs_types.h> + +extern inode* ramfs_get_inode(super_block* sb, const inode* dir, + uint32 mode, uint32 rdev); + +/* Superblock allocation helper (defined in super.cc) */ +extern super_block* alloc_super(file_system_type* type); + +/* ================================================================== */ +/* ramfs super_operations */ +/* ================================================================== */ + +static inode* ramfs_alloc_inode(super_block* sb) +{ + return new inode{}; +} + +static void ramfs_destroy_inode(inode* ip) +{ + /* Free ramfs_file_data if any */ + if (ip->i_private) { + struct ramfs_file_data { + char* buf; + uint64 capacity; + }; + auto* data = static_cast<ramfs_file_data*>(ip->i_private); + delete[] data->buf; + delete data; + } + delete ip; +} + +static int ramfs_statfs(dentry* de, kstatfs* buf) +{ + /* Simple fake values */ + buf->f_type = 0x858458f6; /* RAMFS_MAGIC */ + buf->f_bsize = 4096; + buf->f_blocks = 0; + buf->f_bfree = 0; + buf->f_bavail = 0; + buf->f_files = 0; + buf->f_ffree = 0; + buf->f_namelen = 255; + buf->f_frsize = 4096; + buf->f_flags = 0; + return 0; +} + +static super_operations ramfs_sops; + +static void ramfs_sops_init() +{ + ramfs_sops.alloc_inode = ramfs_alloc_inode; + ramfs_sops.destroy_inode = ramfs_destroy_inode; + ramfs_sops.free_inode = nullptr; + ramfs_sops.dirty_inode = nullptr; + ramfs_sops.write_inode = nullptr; + ramfs_sops.drop_inode = nullptr; + ramfs_sops.evict_inode = nullptr; + ramfs_sops.put_super = nullptr; + ramfs_sops.sync_fs = nullptr; + ramfs_sops.statfs = ramfs_statfs; + ramfs_sops.umount_begin = nullptr; +} + +/* ================================================================== */ +/* ramfs_fill_super */ +/* ================================================================== */ + +static int ramfs_fill_super(super_block* sb, void* data, int silent) +{ + sb->s_op = &ramfs_sops; + sb->s_magic = 0x858458f6; + sb->s_blocksize = 4096; + sb->s_blocksize_bits = 12; + sb->s_maxbytes = 0x7FFFFFFFFFFFFFFFLL; + sb->s_time_gran = 1; + + /* Create root inode */ + inode* root_ip = ramfs_get_inode(sb, nullptr, S_IFDIR | 0755, 0); + if (!root_ip) return -ENOMEM; + + /* Create root dentry */ + sb->s_root = d_make_root(root_ip); + if (!sb->s_root) + return -ENOMEM; + + return 0; +} + +/* ================================================================== */ +/* ramfs_mount */ +/* ================================================================== */ + +static super_block* ramfs_mount(file_system_type* type, int flags, + const char* dev_name, void* data) +{ + super_block* sb = alloc_super(type); + if (!sb) return nullptr; + + int err = ramfs_fill_super(sb, data, flags & SB_SILENT); + if (err) { + delete sb; + return nullptr; + } + + return sb; +} + +static void ramfs_kill_sb(super_block* sb) +{ + /* Phase 1: no cleanup needed for ramfs */ + (void)sb; +} + +/* ================================================================== */ +/* Filesystem type registration */ +/* ================================================================== */ + +static file_system_type ramfs_fs_type; + +extern void ramfs_file_ops_init(); +extern void ramfs_dir_inode_ops_init(); + +void ramfs_init() +{ + /* Runtime init to avoid un-relocated absolute pointers in .data */ + ramfs_sops_init(); + ramfs_file_ops_init(); + ramfs_dir_inode_ops_init(); + + ramfs_fs_type.name = "ramfs"; + ramfs_fs_type.fs_flags = FS_NO_DEVICE; + ramfs_fs_type.mount = ramfs_mount; + ramfs_fs_type.kill_sb = ramfs_kill_sb; + list_init(&ramfs_fs_type.fs_list); + + register_filesystem(&ramfs_fs_type); +} + |
