summaryrefslogtreecommitdiff
path: root/fs/ramfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ramfs')
-rw-r--r--fs/ramfs/file.cc216
-rw-r--r--fs/ramfs/inode.cc164
-rw-r--r--fs/ramfs/super.cc154
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);
+}
+