FS/libfs.c
2025-06-20 20:38:26 +00:00

410 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/file.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/dcache.h>
#include <linux/slab.h>
#include <linux/hashtable.h>
#include <linux/version.h>
#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 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;
}
static ssize_t dummy_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) {
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);
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 = &simple_file_inode_operations;
inode->i_op = &custom_file_inode_operations;
inode->i_fop = &custom_file_ops;
d_instantiate(dentry, inode);
mark_inode_dirty(inode);
logf("books_create: created file %s (ino=%lu)", dentry->d_name.name, inode->i_ino);
return 0;
}
static const struct inode_operations books_dir_iops = {
.lookup = simple_lookup,
.create = books_create,
};
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(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 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,
};
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;
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);