FS/statsfs.c
2025-06-15 10:07:35 +00:00

203 lines
5.9 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/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/dcache.h>
#include <linux/list.h>
#include <linux/fs_context.h>
#include <linux/err.h>
#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 = NULL;
static struct vfsmount *libfs_mnt;
static const struct super_operations libfs_sops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
};
static const struct file_operations libfs_file_ops = {
.llseek = no_llseek,
.read = NULL,
.write = NULL,
};
static struct inode *libfs_get_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->i_sb = sb;
inode->i_op = &simple_dir_inode_operations;
if (S_ISDIR(mode)) {
inode->i_fop = &simple_dir_operations;
} else {
inode->i_fop = &libfs_file_ops;
}
inode->i_mode = mode;
return inode;
}
static bool is_protected_dentry(struct dentry *dentry) {
return (strcmp(dentry->d_name.name, "books") == 0 || strcmp(dentry->d_name.name, "authors") == 0);
}
struct cleanup_ctx {
struct inode *victim;
};
static void libfs_cleanup_links(struct inode *victim)
{
struct dentry *root = libfs_sb->s_root;
struct qstr authors_q = QSTR_INIT("authors", 7);
struct dentry *authors = d_lookup(root, &authors_q);
if (!authors || d_really_is_negative(authors)) {
dput(authors);
return;
}
// Здесь конкретный пример: если ты сам знаешь имя линка на victim
// То можешь через d_lookup() найти его и удалить
struct qstr filename = QSTR_INIT("some_symlink", 12);
struct dentry *maybe_link = d_lookup(authors, &filename);
if (maybe_link && maybe_link->d_inode == victim) {
inode_lock(authors->d_inode);
vfs_unlink(NULL, authors->d_inode, maybe_link, NULL);
inode_unlock(authors->d_inode);
}
dput(maybe_link);
dput(authors);
}
static int libfs_unlink(struct inode *dir, struct dentry *dentry) {
if (dir == libfs_sb->s_root->d_inode && is_protected_dentry(dentry))
return -EPERM;
struct inode *victim = dentry->d_inode;
int ret = simple_unlink(dir, dentry);
if (ret == 0 && dir == books_inode) {
libfs_cleanup_links(victim);
}
return ret;
}
static int libfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) {
if (dir == libfs_sb->s_root->d_inode) {
return -EPERM;
}
struct 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;
}
static int libfs_rmdir(struct inode *dir, struct dentry *dentry) {
if (dir == libfs_sb->s_root->d_inode && is_protected_dentry(dentry))
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_dentry(old_dentry) || is_protected_dentry(new_dentry)))
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) {
if (dir->i_sb != old_dentry->d_inode->i_sb)
return -EXDEV;
if (!S_ISREG(old_dentry->d_inode->i_mode))
return -EPERM;
return simple_link(old_dentry, dir, new_dentry);
}
static const struct inode_operations libfs_dir_iops = {
.lookup = simple_lookup,
.mkdir = libfs_mkdir,
.unlink = libfs_unlink,
.rmdir = libfs_rmdir,
.rename = libfs_rename,
.link = libfs_link,
};
static int libfs_fill_super(struct super_block *sb, void *data, int silent) {
struct inode *inode;
struct dentry *root;
libfs_sb = sb;
sb->s_magic = LIBFS_MAGIC;
sb->s_op = &libfs_sops;
inode = libfs_get_inode(sb, S_IFDIR | 0755);
if (!inode)
return -ENOMEM;
inode->i_op = &libfs_dir_iops;
inode->i_fop = &simple_dir_operations;
root = d_make_root(inode);
if (!root)
return -ENOMEM;
sb->s_root = root;
// /books
{
struct inode *books = libfs_get_inode(sb, S_IFDIR | LIBFS_BOOKS_MODE);
books_inode = books;
struct dentry *d = d_alloc_name(root, "books");
d_add(d, books);
}
// /authors
{
struct inode *authors = libfs_get_inode(sb, S_IFDIR | LIBFS_AUTHORS_MODE);
struct dentry *d = d_alloc_name(root, "authors");
d_add(d, authors);
}
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 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) {
int ret = register_filesystem(&libfs_type);
if (ret)
return ret;
libfs_mnt = kern_mount(&libfs_type);
return PTR_ERR_OR_ZERO(libfs_mnt);
}
static void __exit libfs_exit(void) {
kern_unmount(libfs_mnt);
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");