#include #include #include #include #include #include #include #include #include #include #include #include #define FS_MAGIC 0x20240615 #define BOOKS_MODE 01777 #define AUTHORS_MODE 0777 #define AUTHOR_SUBDIR_MODE 01777 MODULE_LICENSE("GPL"); MODULE_AUTHOR("user"); MODULE_DESCRIPTION("Custom FS with books and authors directories"); static struct inode *books_inode; static struct inode *authors_inode; struct link_entry { struct hlist_node node; struct dentry *link; struct inode *target; }; DEFINE_HASHTABLE(link_table, 6); static void remove_links_to_inode(struct inode *inode) { struct link_entry *entry; struct hlist_node *tmp; int bkt; hash_for_each_safe(link_table, bkt, tmp, entry, node) { if (entry->target == inode) { printk(KERN_INFO "customfs: removing hardlink to inode %lu\n", inode->i_ino); d_delete(entry->link); hash_del(&entry->node); kfree(entry); } } } static void fs_evict_inode(struct inode *inode) { if (inode->i_sb && inode->i_sb->s_magic == FS_MAGIC) { if (S_ISREG(inode->i_mode)) { remove_links_to_inode(inode); } } truncate_inode_pages_final(&inode->i_data); clear_inode(inode); } static int fs_statfs(struct dentry *dentry, struct kstatfs *buf) { buf->f_type = FS_MAGIC; return 0; } static const struct super_operations fs_ops = { .evict_inode = fs_evict_inode, .statfs = fs_statfs, }; static ssize_t books_file_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) { return simple_read_from_buffer(buf, len, ppos, file->private_data, PAGE_SIZE); } static ssize_t books_file_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { return simple_write_to_buffer(file->private_data, PAGE_SIZE, ppos, buf, len); } static const struct file_operations books_file_fops = { .read = books_file_read, .write = books_file_write, .llseek = generic_file_llseek, }; static const struct inode_operations books_file_iops = { .getattr = simple_getattr, .setattr = simple_setattr, }; static const struct inode_operations author_subdir_iops; static int author_mkdir(struct user_namespace *ns, struct inode *dir, struct dentry *dentry, umode_t mode) { struct inode *inode = new_inode(dir->i_sb); if (!inode) return -ENOMEM; inode->i_ino = get_next_ino(); inode_init_owner(&init_user_ns, inode, dir, S_IFDIR | AUTHOR_SUBDIR_MODE); inode->i_op = &author_subdir_iops; inode->i_fop = &simple_dir_operations; inc_nlink(inode); inc_nlink(dir); d_add(dentry, inode); printk(KERN_INFO "customfs: created author subdir '%s'\n", dentry->d_name.name); return 0; } static int author_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { int err; struct inode *inode = d_inode(old_dentry); if (!S_ISREG(inode->i_mode)) return -EPERM; err = simple_link(old_dentry, dir, new_dentry); if (err == 0) { struct link_entry *entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (entry) { entry->link = dget(new_dentry); entry->target = inode; hash_add(link_table, &entry->node, inode->i_ino); printk(KERN_INFO "customfs: created hardlink '%s' to inode %lu\n", new_dentry->d_name.name, inode->i_ino); } } return err; } static const struct inode_operations author_subdir_iops = { .lookup = simple_lookup, .mkdir = author_mkdir, .rmdir = simple_rmdir, .link = author_link, }; static struct inode *fs_make_inode(struct super_block *sb, umode_t mode) { struct inode *inode = new_inode(sb); if (!inode) return NULL; inode->i_ino = get_next_ino(); inode_init_owner(&init_user_ns, inode, NULL, mode); inode->i_sb = sb; inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); if (S_ISDIR(mode)) { inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; inc_nlink(inode); } else if (S_ISREG(mode)) { inode->i_fop = &books_file_fops; inode->i_op = &books_file_iops; inode->i_private = kzalloc(PAGE_SIZE, GFP_KERNEL); } return inode; } static int fs_fill_super(struct super_block *sb, void *data, int silent) { struct inode *root_inode; struct dentry *root_dentry; struct dentry *books_dentry; struct dentry *authors_dentry; sb->s_magic = FS_MAGIC; sb->s_op = &fs_ops; root_inode = fs_make_inode(sb, S_IFDIR | 0755); if (!root_inode) return -ENOMEM; root_dentry = d_make_root(root_inode); if (!root_dentry) return -ENOMEM; books_inode = fs_make_inode(sb, S_IFDIR | BOOKS_MODE); if (!books_inode) goto out_root; books_inode->i_op = &simple_dir_inode_operations; books_inode->i_fop = &simple_dir_operations; inc_nlink(books_inode); books_dentry = d_alloc_name(root_dentry, "books"); if (!books_dentry) goto out_books; d_add(books_dentry, books_inode); d_rehash(books_dentry); printk(KERN_INFO "customfs: 'books' directory created\n"); authors_inode = fs_make_inode(sb, S_IFDIR | AUTHORS_MODE); if (!authors_inode) goto out_books_dentry; authors_inode->i_op = &author_subdir_iops; inc_nlink(authors_inode); authors_dentry = d_alloc_name(root_dentry, "authors"); if (!authors_dentry) goto out_authors; d_add(authors_dentry, authors_inode); d_rehash(authors_dentry); printk(KERN_INFO "customfs: 'authors' directory created\n"); sb->s_root = root_dentry; printk(KERN_INFO "customfs: fill_super completed\n"); printk(KERN_INFO "customfs: root inode=%lu\n", root_inode->i_ino); printk(KERN_INFO "customfs: books inode=%lu\n", books_inode->i_ino); printk(KERN_INFO "customfs: authors inode=%lu\n", authors_inode->i_ino); return 0; out_authors: iput(authors_inode); out_books_dentry: dput(books_dentry); out_books: iput(books_inode); out_root: dput(root_dentry); iput(root_inode); return -ENOMEM; } static struct dentry *fs_mount(struct file_system_type *type, int flags, const char *dev, void *data) { printk(KERN_INFO "customfs: mounting\n"); return mount_nodev(type, flags, data, fs_fill_super); } static struct file_system_type fs_type = { .owner = THIS_MODULE, .name = "customfs", .mount = fs_mount, .kill_sb = kill_litter_super, }; static int __init fs_init(void) { hash_init(link_table); printk(KERN_INFO "customfs: module loaded\n"); return register_filesystem(&fs_type); } static void __exit fs_exit(void) { unregister_filesystem(&fs_type); printk(KERN_INFO "customfs: module unloaded\n"); } module_init(fs_init); module_exit(fs_exit);