#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LIBFS_MAGIC 0xA0B0C0D0 #define LIBFS_BOOKS_MODE 01777 #define LIBFS_AUTHORS_MODE 0777 #define LIBFS_SUBDIR_MODE 01777 static struct super_block *libfs_sb; static struct inode *books_inode; static struct inode *authors_inode; static struct dentry *authors_dentry; static void libfs_evict_inode(struct inode *inode); struct link_entry { struct hlist_node node; struct dentry *link; }; /* запрет создания FIFO/устройств */ static int libfs_mknod(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { return -EPERM; } /* запрет симв. ссылок */ static int libfs_symlink(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, const char *target) { return -EPERM; } static int libfs_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl); static int libfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode); static int libfs_unlink(struct inode *dir, struct dentry *dentry); static int libfs_rmdir(struct inode *dir, struct dentry *dentry); static int libfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags); static int libfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry); static const struct super_operations libfs_sops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, .evict_inode = libfs_evict_inode, }; static const struct file_operations libfs_file_ops = { .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .llseek = generic_file_llseek, }; static const struct inode_operations libfs_dir_iops = { .lookup = simple_lookup, .mkdir = libfs_mkdir, .mknod = libfs_mknod, .symlink = libfs_symlink, .unlink = libfs_unlink, .rmdir = libfs_rmdir, .rename = libfs_rename, .link = libfs_link, .create = libfs_create, }; static struct inode *libfs_get_inode(struct super_block *sb, umode_t mode) { struct inode *inode; if (!S_ISREG(mode) && !S_ISDIR(mode)) return NULL; inode = new_inode(sb); if (!inode) return NULL; inode->i_ino = get_next_ino(); inode->i_sb = sb; inode->i_mode = mode; if (S_ISDIR(mode)) { inode->i_op = &libfs_dir_iops; inode->i_fop = &simple_dir_operations; } else { /* файл — просто generic_file_* */ inode->i_op = &simple_dir_inode_operations; inode->i_fop = &libfs_file_ops; /* заводим список жёстких ссылок */ inode->i_private = kzalloc(sizeof(struct hlist_head), GFP_KERNEL); if (!inode->i_private) { iput(inode); return NULL; } INIT_HLIST_HEAD((struct hlist_head *)inode->i_private); } return inode; } static void cleanup_links(struct inode *victim) { struct hlist_head *list; struct link_entry *le; struct hlist_node *tmp; if (!S_ISREG(victim->i_mode) || !victim->i_private) return; list = (struct hlist_head *)victim->i_private; hlist_for_each_entry_safe(le, tmp, list, node) { struct dentry *link = le->link; if (!link || d_unlinked(link)) { hlist_del(&le->node); dput(link); kfree(le); continue; } { struct dentry *pd = dget_parent(link); if (pd) { struct inode *parent = d_inode(pd); if (parent) { inode_lock(parent); vfs_unlink(NULL, parent, link, NULL); inode_unlock(parent); } dput(pd); } } hlist_del(&le->node); dput(link); kfree(le); } kfree(victim->i_private); victim->i_private = NULL; } static bool is_protected(struct dentry *dentry) { return strcmp(dentry->d_name.name, "books") == 0 || strcmp(dentry->d_name.name, "authors") == 0; } static int libfs_unlink(struct inode *dir, struct dentry *dentry) { struct inode *victim = d_inode(dentry); int ret; if (dir == libfs_sb->s_root->d_inode && is_protected(dentry)) return -EPERM; ret = simple_unlink(dir, dentry); if (ret == 0 && dir == books_inode) cleanup_links(victim); return ret; } static int libfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { struct inode *inode; if (dir == libfs_sb->s_root->d_inode || dir == books_inode) return -EPERM; if (dir == authors_inode) { inode = libfs_get_inode(dir->i_sb, S_IFDIR | LIBFS_SUBDIR_MODE); if (!inode) return -ENOMEM; inode->i_uid = current_fsuid(); d_add(dentry, inode); inc_nlink(dir); return 0; } return -EPERM; } static int libfs_rmdir(struct inode *dir, struct dentry *dentry) { if (dir == libfs_sb->s_root->d_inode && is_protected(dentry)) return -EPERM; if (dir == authors_inode) return -EPERM; return simple_rmdir(dir, dentry); } static int libfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { if ((old_dir == libfs_sb->s_root->d_inode || new_dir == libfs_sb->s_root->d_inode) && (is_protected(old_dentry) || is_protected(new_dentry))) return -EPERM; if (old_dir == authors_inode || new_dir == authors_inode) return -EPERM; return simple_rename(idmap, old_dir, old_dentry, new_dir, new_dentry, flags); } static int libfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { struct inode *target = d_inode(old_dentry); struct hlist_head *list; struct link_entry *le; int ret; if (!S_ISREG(target->i_mode)) return -EPERM; if (!authors_dentry || new_dentry->d_parent->d_parent != authors_dentry) return -EPERM; ret = simple_link(old_dentry, dir, new_dentry); if (ret) return ret; list = (struct hlist_head *)target->i_private; le = kmalloc(sizeof(*le), GFP_KERNEL); if (!le) return -ENOMEM; le->link = dget(new_dentry); hlist_add_head(&le->node, list); return 0; } static int libfs_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct inode *inode; if (dir != books_inode) return -EPERM; inode = libfs_get_inode(dir->i_sb, S_IFREG | mode); if (!inode) return -ENOMEM; inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); d_add(dentry, inode); return 0; } static int libfs_fill_super(struct super_block *sb, void *data, int silent) { struct inode *root; libfs_sb = sb; sb->s_magic = LIBFS_MAGIC; sb->s_op = &libfs_sops; root = libfs_get_inode(sb, S_IFDIR | 0755); if (!root) return -ENOMEM; root->i_op = &libfs_dir_iops; root->i_fop = &simple_dir_operations; sb->s_root = d_make_root(root); if (!sb->s_root) return -ENOMEM; books_inode = libfs_get_inode(sb, S_IFDIR | LIBFS_BOOKS_MODE); { struct dentry *d = d_alloc_name(sb->s_root, "books"); d_add(d, books_inode); } authors_inode = libfs_get_inode(sb, S_IFDIR | LIBFS_AUTHORS_MODE); authors_dentry = d_alloc_name(sb->s_root, "authors"); d_add(authors_dentry, authors_inode); return 0; } static struct dentry *libfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_nodev(fs_type, flags, data, libfs_fill_super); } static void libfs_evict_inode(struct inode *inode) { truncate_inode_pages_final(&inode->i_data); cleanup_links(inode); clear_inode(inode); } static struct file_system_type libfs_type = { .owner = THIS_MODULE, .name = "libfs", .mount = libfs_mount, .kill_sb = kill_litter_super, }; static int __init libfs_init(void) { return register_filesystem(&libfs_type); } static void __exit libfs_exit(void) { if (authors_dentry) { dput(authors_dentry); authors_dentry = NULL; } if (books_inode) { cleanup_links(books_inode); iput(books_inode); books_inode = NULL; } if (authors_inode) { iput(authors_inode); authors_inode = NULL; } unregister_filesystem(&libfs_type); } module_init(libfs_init); module_exit(libfs_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("you"); MODULE_DESCRIPTION("Custom library FS with books and authors");