commit 85951ee78f457bc7e5de747655802797ed8e685d Author: nganhkhoa Date: Tue Oct 29 13:01:19 2024 +0700 working vfs hardcoded structure with support for - mount - list directory - read a file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..99915c0 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +obj-m += voidfs.o + +all: voidfs + +voidfs: + make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..41a3420 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# 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)`. + +## Run + +``` +sudo make voidfs +sudo insmod voidfs.ko + +# create a dummy file block device +dd if=/dev/zero of=dummy.iso bs=4096 count=1 +mkdir -p drive +sudo mount -t voidfs -o loop dummy.iso drive +sudo ls drive +sudo cat drive/dummyfile +``` + +## Goal + +- Fully working filesystem +- Read from block device +- Encrypted filesystem + +## Design + +TO BE UPDATED + +## 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..51a145c --- /dev/null +++ b/voidfs.c @@ -0,0 +1,234 @@ +#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); + + +struct dentry *voidfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int); +int voidfs_readdir(struct file *fp, struct dir_context *ctx); + +int voidfs_open(struct inode *inode, struct file *fp); +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 inode_operations voidfs_inode_ops = { + .lookup = voidfs_lookup, +}; + +struct file_operations voidfs_dir_ops = { + .iterate_shared = voidfs_readdir, +}; + +struct file_operations voidfs_file_ops = { + .open = voidfs_open, + .read = voidfs_read, + .write = voidfs_write, +}; + +struct dentry *voidfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int) { + struct inode* inode = NULL; + + printk(KERN_INFO "listing directory %s %s", dentry->d_parent->d_name.name, dentry->d_name.name); + printk(KERN_INFO " %p %p", dentry->d_inode, dir->i_sb); + + // parse an entry path + // right now, let's just create a dummy path + if (strcmp(dentry->d_name.name, "dummyfile") == 0) { + // inode->i_sb = sb; + inode = new_inode(dir->i_sb); + inode->i_op = &voidfs_inode_ops; + inode->i_fop = &voidfs_file_ops; + inode->i_mode = S_IFREG | 0644; + inode->i_ino = 3; + // inode->i_private = (void*)1; + } + + d_add(dentry, inode); + return NULL; +} + +int voidfs_readdir(struct file *dir, struct dir_context *ctx) { + // populate directory entry maybe + struct inode *inode; + struct super_block *sb; + + printk(KERN_INFO "readdir"); + + if (ctx->pos > 3) { + return -ENOENT; + } + + inode = file_inode(dir); + sb = inode->i_sb; + + dir_emit_dots(dir, ctx); + + dir_emit(ctx, "dummydir", 8, 2, DT_DIR); + ctx->pos += 1; + dir_emit(ctx, "dummyfile", 9, 3, DT_REG); + ctx->pos += 1; + + return 0; +} + +int voidfs_open(struct inode *inode, struct file *fp) { + printk(KERN_INFO "voidfs open"); + 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; + + printk(KERN_INFO "voidfs read"); + + // we only allow read 1 time, should be at the start + if (*ppos > 0) { + return 0; + } + + inode = file_inode(fp); + 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)) { + printk(KERN_ERR + "Error copying file content to userspace buffer\n"); + return -EFAULT; + } + *ppos += nbytes; + return nbytes; +} + +ssize_t voidfs_write(struct file *fp, const char __user *buf, size_t len, loff_t *ppos) { + return 0; +} + +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; + } + root_inode->i_mode = S_IFDIR | 0644; + inode_init_owner(&nop_mnt_idmap, root_inode, NULL, root_inode->i_mode); + + root_inode->i_sb = sb; + root_inode->i_op = &voidfs_inode_ops; + root_inode->i_fop = &voidfs_dir_ops; + + sb->s_root = d_make_root(root_inode); + + // create a dummy file entry point + // root_inode->i_fop = &voidfs_file_ops; + + // create a child file + + + return 0; +} + +void voidfs_kill_superblock(struct super_block *sb) { + printk(KERN_INFO "voidfs try to unmount\n"); + + kill_block_super(sb); +} + +static int __init voidfs_init(void) +{ + int ret; + + 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("GPL"); +MODULE_AUTHOR("nganhkhoa");