commit e087715248de26b51be0c4aa9c28e6e62fc555ea Author: nganhkhoa Date: Tue Oct 29 13:01:19 2024 +0700 init diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9e71f15 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +obj-m += voidfs.o + +all: voidfs + +voidfs: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules diff --git a/README b/README new file mode 100644 index 0000000..667bb86 --- /dev/null +++ b/README @@ -0,0 +1,21 @@ +# Void Filesystem + +Just a dummy filesystem for learning. + +## Virtual Filesystem (VFS) + +Linux VFS allows us to write customize filesystem. The filesystem must be able to mount, other operations such as file read/write or folder creation/deletion can be customized. + +A filesystem can be virtual, without a backed-up device. This is the case for, I guess, network file descriptors. For filesystem with a backed-up device, usually a block device, the underlying block device can be accessed by sb_bread to read a block. + +Internally, a filesystem must provide a superblock, that will be used as a pointer to read files. + +When a file is read, the inode of the file entry can be accessed. If we also keep the internal data of the file in `inode->i_private`, we can fetch that to read the file system through `sb_read(inode->sb)`. + +## References + +[simplefs](https://github.com/sysprog21/simplefs) + +[Linux Kernel Labs - File system drivers](https://linux-kernel-labs.github.io/refs/heads/master/labs/filesystems_part1.html) + +[Linux Kernel Documentation](https://docs.kernel.org/filesystems/vfs.html) diff --git a/voidfs.c b/voidfs.c new file mode 100644 index 0000000..05981d8 --- /dev/null +++ b/voidfs.c @@ -0,0 +1,194 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct dentry *voidfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data); + +static int voidfs_fill_super(struct super_block *sb, void *data, int silent); +void voidfs_kill_superblock(struct super_block *sb); + + +ssize_t voidfs_read(struct file *fp, char __user *buf, size_t len, loff_t *ppos); +ssize_t voidfs_write(struct file *fp, const char __user *buf, size_t len, loff_t *ppos); + +struct file_system_type voidfs_fs_type = { + .owner = THIS_MODULE, + .name = "voidfs", + .mount = voidfs_mount, + .kill_sb = voidfs_kill_superblock, + .fs_flags = FS_REQUIRES_DEV, +}; + +struct super_operations voidfs_super_ops = { + // null for now +} + +struct super_operations voidfs_inode_ops = { + // null for now +} + +struct file_operations voidfs_dir_ops = { + .iterate_shared = voidfs_readdir, +} + +struct file_operations voidfs_file_ops = { + .read = voidfs_read, + .write = voidfs_write, +} + +int voidfs_readdir(struct file *fp, struct dir_context *ctx) { + // populate directory entry maybe + struct inode *inode; + struct super_block *sb; + + printk(KERN_INFO "readdir"); + + inode = file_inode(file); + sb = inode->i_sb; + + // if (inode && + // !dir_emit(ctx, f->filename, SIMPLEFS_FILENAME_LEN, f->inode, + // DT_UNKNOWN)) + // break; + + return 0; +} + +ssize_t voidfs_read(struct file *fp, char __user *buf, size_t len, loff_t *ppos) { + struct inode *inode; + struct super_block *sb; + + inode = file_inode(file); + if (!inode) { + return -EFAULT; + } + sb = inode->i_sb; + + // we don't read from the block device for now + // so return a dummy string + char* buffer = "this is a voidfs file"; + ssize_t nbytes = 21; + + // get file buffer + // move the offset + // get as much as we can + + if (copy_to_user(buf, buffer, nbytes)) { + brelse(bh); + printk(KERN_ERR + "Error copying file content to userspace buffer\n"); + return -EFAULT; + } +} + +ssize_t voidfs_write(struct file *fp, const char __user *buf, size_t len, loff_t *ppos) { +} + +struct dentry *voidfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { + printk(KERN_INFO "voidfs try to mount %s\n", dev_name); + + struct dentry *ret; + ret = mount_bdev(fs_type, flags, dev_name, data, voidfs_fill_super); + + if (unlikely(IS_ERR(ret))) { + printk(KERN_ERR "Error mounting voidfs.\n"); + } else { + printk(KERN_INFO "voidfs is succesfully mounted on: %s\n", + dev_name); + } + + return ret; +} + +// initialize super block metadata +static int voidfs_fill_super(struct super_block *sb, void *data, int silent) { + struct inode *root_inode; + struct inode *dummy_body; + + sb->s_magic = 0x44445555; + sb->s_op = &voidfs_super_ops; + sb_set_blocksize(sb, 1 << 12 /* 4 kilobyte */); + sb->s_maxbytes = (1 << 12) * 10; + + // we don't get a read on file for now, because we still have + // to define the internal format + + // create a dummy root node that we can access + root_inode = new_inode(sb); + if (!root_inode) { + return -ENOMEM; + } + inode_init_owner(root_inode, NULL, root_inode->i_mode); + + root_inode->i_sb = sb; + root_inode->i_op = &voidfs_inode_ops; + root_inode->i_atime = root_inode->i_mtime + = root_inode->i_ctime + = CURRENT_TIME; + + root_inode->i_fop = &voidfs_dir_ops; + + // create a dummy file entry point + root_inode->i_fop = &voidfs_file_ops; + + sb->s_root = d_make_root(root_inode); + + return 0; +} + +void voidfs_kill_superblock(struct super_block *sb) { + printk(KERN_INFO "voidfs try to unmount\n"); +} + +static int __init voidfs_init(void) +{ + int ret; + + // hellofs_inode_cache = kmem_cache_create("hellofs_inode_cache", + // sizeof(struct hellofs_inode), + // 0, + // (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), + // NULL); + // if (!hellofs_inode_cache) { + // return -ENOMEM; + // } + + ret = register_filesystem(&voidfs_fs_type); + if (likely(0 == ret)) { + printk(KERN_INFO "Sucessfully registered voidfs\n"); + } else { + printk(KERN_ERR "Failed to register voidfs. Error code: %d\n", ret); + } + + return ret; +} + +static void __exit voidfs_exit(void) +{ + int ret; + + ret = unregister_filesystem(&voidfs_fs_type); + // kmem_cache_destroy(voidfs_inode_cache); + + if (likely(ret == 0)) { + printk(KERN_INFO "Sucessfully unregistered voidfs\n"); + } else { + printk(KERN_ERR "Failed to unregister voidfs. Error code: %d\n", + ret); + } +} + +module_init(voidfs_init); +module_exit(voidfs_exit); + +MODULE_LICENSE("MIT"); +MODULE_AUTHOR("nganhkhoa");