#include #include #include #include #include #include #include #include #include #include #include #include #define FS_NAME "customfs" #define FS_MAGIC 0x20240615 #define MODE_BOOKS 01777 #define MODE_AUTHORS 0777 #define MODE_AUTHOR_SUBDIR 01777 #define logf(fmt, ...) \ do { \ printk(KERN_INFO "customfs: " fmt "\n", ##__VA_ARGS__); \ struct file *f = filp_open("/home/ubuntu/FS/customfs.log", O_WRONLY | O_CREAT | O_APPEND, 0644); \ if (!IS_ERR(f)) { \ char __buf[256]; \ int __len = snprintf(__buf, sizeof(__buf), fmt "\n", ##__VA_ARGS__); \ kernel_write(f, __buf, __len, &f->f_pos); \ filp_close(f, NULL); \ } \ } while (0) MODULE_LICENSE("GPL"); MODULE_AUTHOR("you"); MODULE_DESCRIPTION("Custom FS with books/authors behavior"); static int custom_unlink(struct inode *dir, struct dentry *dentry); static struct super_block *global_sb; static struct inode *books_inode; static const struct inode_operations fs_dir_iops; DEFINE_HASHTABLE(link_table, 6); static const struct inode_operations custom_file_inode_operations = {}; struct link_entry { struct hlist_node node; struct dentry *link; struct inode *target; }; static ssize_t dummy_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) { return 0; } #if 0 static ssize_t dummy_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { return len; } #endif static ssize_t dummy_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct inode *inode = file_inode(file); loff_t new_end = *ppos + len; if (inode->i_size < new_end) inode->i_size = new_end; *ppos = new_end; mark_inode_dirty(inode); return len; } static const struct file_operations custom_file_ops = { .read = dummy_read, .write = dummy_write, .llseek = noop_llseek, }; static bool is_protected_dentry(struct dentry *d) { return strcmp(d->d_name.name, "books") == 0 || strcmp(d->d_name.name, "authors") == 0; } #if 0 static struct inode *get_inode(struct super_block *sb, umode_t mode) { struct inode *inode = new_inode(sb); if (!inode) return NULL; inode_init_owner(&init_user_ns, inode, NULL, mode); inode->i_ino = get_next_ino(); 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 = &custom_file_ops; } return inode; } #endif static struct inode *get_inode(struct super_block *sb, umode_t mode) { struct inode *inode = new_inode(sb); if (!inode) return NULL; inode_init_owner(&init_user_ns, inode, NULL, mode); inode->i_ino = get_next_ino(); inode->i_sb = sb; inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); inode->i_mode = mode; return inode; } #if 0 static int books_create(struct user_namespace *ns, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct inode *inode = get_inode(dir->i_sb, S_IFREG | mode); if (!inode) return -ENOMEM; inode->i_uid = current_fsuid(); d_add(dentry, inode); return 0; } #endif static int books_create(struct user_namespace *ns, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct inode *inode = get_inode(dir->i_sb, S_IFREG | mode); if (!inode) return -ENOMEM; inode->i_uid = current_fsuid(); inode->i_op = &custom_file_inode_operations; inode->i_fop = &custom_file_ops; logf("books_create: dentry addr=%px, inode=%px", dentry, inode); d_instantiate(dentry, inode); inc_nlink(inode); logf("books_create: d_instantiate done, dentry->d_inode=%px", dentry->d_inode); logf("dentry flags: %u, dentry name: %s", dentry->d_flags, dentry->d_name.name); mark_inode_dirty(inode); logf("books_create: created file %s (ino=%lu)", dentry->d_name.name, inode->i_ino); return 0; } #if 0 static struct dentry *books_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { logf("books_lookup: looking for %s in ino=%lu", dentry->d_name.name, dir->i_ino); return simple_lookup(dir, dentry, flags); } #endif static struct dentry *books_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct dentry *child; logf("books_lookup: looking for %s in ino=%lu", dentry->d_name.name, dir->i_ino); child = d_lookup(dentry->d_parent, &dentry->d_name); if (child) { d_add(dentry, d_inode(child)); // связываем найденное inode dput(child); } else { d_add(dentry, NULL); // файл не найден } return NULL; // всегда возвращается NULL в lookup } static const struct inode_operations books_dir_iops = { .lookup = books_lookup, .create = books_create, .unlink = custom_unlink, }; static void remove_links_to_inode(struct inode *inode) { int bkt; struct link_entry *entry; struct hlist_node *tmp; logf("remove_links_to_inode: start for inode %lu", inode->i_ino); hash_for_each_safe(link_table, bkt, tmp, entry, node) { if (entry->target == inode) { logf("removing link: dentry=%s", entry->link->d_name.name); d_delete(entry->link); dput(entry->link); hash_del(&entry->node); kfree(entry); } } } #if 0 static int custom_mkdir(struct user_namespace *ns, struct inode *dir, struct dentry *dentry, umode_t mode) { umode_t mode_only = dir->i_mode & 07777; kuid_t uid = current_fsuid(); logf("mkdir: parent ino=%lu, mode=%o, uid=%u, name=%s", dir->i_ino, mode_only, from_kuid(&init_user_ns, uid), dentry->d_name.name); if (dir == global_sb->s_root->d_inode) { logf("mkdir denied: attempt to create in root"); return -EPERM; } // В authors (0777): любой пользователь может создать свою папку if (mode_only == MODE_AUTHORS) { logf("mkdir in authors: allowed"); } // В подкаталогах authors (01777): только владелец каталога может создавать else if (mode_only == MODE_AUTHOR_SUBDIR && !uid_eq(uid, dir->i_uid)) { logf("mkdir denied in subdir: uid mismatch (dir uid=%u)", from_kuid(&init_user_ns, dir->i_uid)); return -EACCES; } struct inode *inode = get_inode(dir->i_sb, S_IFDIR | MODE_AUTHOR_SUBDIR); if (!inode) { logf("mkdir error: get_inode returned NULL"); return -ENOMEM; } inode->i_uid = uid; inode->i_op = &fs_dir_iops; inode->i_fop = &simple_dir_operations; d_instantiate(dentry, inode); inc_nlink(dir); logf("mkdir success: created %s in ino=%lu by uid=%u", dentry->d_name.name, dir->i_ino, from_kuid(&init_user_ns, uid)); return 0; } #endif static int custom_mkdir(struct user_namespace *ns, struct inode *dir, struct dentry *dentry, umode_t mode) { umode_t mode_only = dir->i_mode & 07777; kuid_t uid = current_fsuid(); logf("mkdir: parent ino=%lu, mode=%o, uid=%u, name=%s", dir->i_ino, mode_only, from_kuid(&init_user_ns, uid), dentry->d_name.name); if (dir == global_sb->s_root->d_inode) { logf("mkdir denied: attempt to create in root"); return -EPERM; } if (mode_only == MODE_AUTHORS) { logf("mkdir in authors: allowed"); } else if (mode_only == MODE_AUTHOR_SUBDIR && !uid_eq(uid, dir->i_uid)) { logf("mkdir denied in subdir: uid mismatch (dir uid=%u)", from_kuid(&init_user_ns, dir->i_uid)); return -EACCES; } struct inode *inode = get_inode(dir->i_sb, S_IFDIR | MODE_AUTHOR_SUBDIR); if (!inode) { logf("mkdir error: get_inode returned NULL"); return -ENOMEM; } inode->i_uid = uid; inode->i_op = &fs_dir_iops; inode->i_fop = &simple_dir_operations; d_instantiate(dentry, inode); inc_nlink(inode); inc_nlink(dir); logf("mkdir success: created %s in ino=%lu by uid=%u", dentry->d_name.name, dir->i_ino, from_kuid(&init_user_ns, uid)); return 0; } static int custom_unlink(struct inode *dir, struct dentry *dentry) { logf("custom_unlink: dir ino=%lu, file=%s", dir->i_ino, dentry->d_name.name); if (dir == global_sb->s_root->d_inode && is_protected_dentry(dentry)) return -EPERM; if (dir == books_inode) { logf("custom_unlink: removing links to inode %lu", d_inode(dentry)->i_ino); remove_links_to_inode(d_inode(dentry)); } return simple_unlink(dir, dentry); } static int custom_rmdir(struct inode *dir, struct dentry *dentry) { if (dir == global_sb->s_root->d_inode && is_protected_dentry(dentry)) return -EPERM; return simple_rmdir(dir, dentry); } static int custom_rename(struct user_namespace *ns, struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { if ((old_dir == global_sb->s_root->d_inode || new_dir == global_sb->s_root->d_inode) && (is_protected_dentry(old_dentry) || is_protected_dentry(new_dentry))) return -EPERM; return simple_rename(ns, old_dir, old_dentry, new_dir, new_dentry, flags); } static int custom_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { if (dir->i_sb != old_dentry->d_inode->i_sb) return -EXDEV; if (!S_ISREG(old_dentry->d_inode->i_mode)) return -EPERM; int r = simple_link(old_dentry, dir, new_dentry); if (r == 0) { struct link_entry *e = kmalloc(sizeof(*e), GFP_KERNEL); if (!e) return -ENOMEM; e->target = d_inode(old_dentry); e->link = dget(new_dentry); hash_add(link_table, &e->node, e->target->i_ino); } return r; } static struct dentry *fs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct dentry *found; logf("fs_lookup: looking for %s in ino=%lu", dentry->d_name.name, dir->i_ino); found = d_lookup(dentry->d_parent, &dentry->d_name); if (found) { d_add(dentry, d_inode(found)); dput(found); } else { d_add(dentry, NULL); } return NULL; } #if 0 static const struct inode_operations fs_dir_iops = { .lookup = simple_lookup, .mkdir = custom_mkdir, .unlink = custom_unlink, .rmdir = custom_rmdir, .rename = custom_rename, .link = custom_link, }; #endif static const struct inode_operations fs_dir_iops = { .lookup = fs_lookup, .mkdir = custom_mkdir, .unlink = custom_unlink, .rmdir = custom_rmdir, .rename = custom_rename, .link = custom_link, }; static void evict_inode(struct inode *inode) { clear_inode(inode); } static const struct super_operations fs_sops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, .evict_inode = evict_inode, }; #if 0 static int fill_super(struct super_block *sb, void *data, int silent) { struct inode *root_inode = get_inode(sb, S_IFDIR | 0755); if (!root_inode) return -ENOMEM; struct dentry *root_dentry = d_make_root(root_inode); if (!root_dentry) return -ENOMEM; sb->s_root = root_dentry; sb->s_magic = FS_MAGIC; sb->s_op = &fs_sops; global_sb = sb; // books books_inode = get_inode(sb, S_IFDIR | MODE_BOOKS); if (!books_inode) return -ENOMEM; books_inode->i_op = &books_dir_iops; books_inode->i_fop = &simple_dir_operations; struct dentry *books = d_alloc_name(root_dentry, "books"); d_add(books, books_inode); // authors struct inode *authors_inode = get_inode(sb, S_IFDIR | MODE_AUTHORS); if (!authors_inode) return -ENOMEM; authors_inode->i_uid = current_fsuid(); authors_inode->i_op = &fs_dir_iops; authors_inode->i_fop = &simple_dir_operations; struct dentry *authors = d_alloc_name(root_dentry, "authors"); d_add(authors, authors_inode); return 0; } #endif static int fill_super(struct super_block *sb, void *data, int silent) { struct inode *root_inode = get_inode(sb, S_IFDIR | 0755); if (!root_inode) return -ENOMEM; root_inode->i_op = &fs_dir_iops; root_inode->i_fop = &simple_dir_operations; struct dentry *root_dentry = d_make_root(root_inode); if (!root_dentry) return -ENOMEM; sb->s_root = root_dentry; sb->s_magic = FS_MAGIC; sb->s_op = &fs_sops; global_sb = sb; // books books_inode = get_inode(sb, S_IFDIR | MODE_BOOKS); if (!books_inode) return -ENOMEM; books_inode->i_op = &books_dir_iops; books_inode->i_fop = &simple_dir_operations; books_inode->i_mode = S_IFDIR | 01777; struct dentry *books = d_alloc_name(root_dentry, "books"); if (!books) return -ENOMEM; d_add(books, books_inode); inc_nlink(root_inode); inc_nlink(books_inode); logf("fill_super: books directory instantiated"); // authors struct inode *authors_inode = get_inode(sb, S_IFDIR | MODE_AUTHORS); if (!authors_inode) return -ENOMEM; authors_inode->i_uid = current_fsuid(); authors_inode->i_op = &fs_dir_iops; authors_inode->i_fop = &simple_dir_operations; struct dentry *authors = d_alloc_name(root_dentry, "authors"); if (!authors) return -ENOMEM; d_add(authors, authors_inode); inc_nlink(root_inode); inc_nlink(authors_inode); logf("fill_super: authors directory instantiated"); logf("fill_super: books and authors created"); return 0; } static struct dentry *mount_fs(struct file_system_type *type, int flags, const char *dev, void *data) { return mount_nodev(type, flags, data, fill_super); } static struct file_system_type fs_type = { .owner = THIS_MODULE, .name = FS_NAME, .mount = mount_fs, .kill_sb = kill_litter_super, }; static int __init fs_init(void) { hash_init(link_table); return register_filesystem(&fs_type); } static void __exit fs_exit(void) { unregister_filesystem(&fs_type); } module_init(fs_init); module_exit(fs_exit);