diff --git a/statsfs.c b/statsfs.c index 06229c1..7bb1f87 100644 --- a/statsfs.c +++ b/statsfs.c @@ -1,20 +1,15 @@ #include #include #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 @@ -22,31 +17,16 @@ #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); +static struct inode *books_inode; +static struct inode *authors_inode; +static struct dentry *authors_dentry; struct link_entry { struct hlist_node node; - struct dentry *link; + 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 void libfs_evict_inode(struct inode *inode); 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, @@ -59,40 +39,45 @@ static int libfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, 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_file_iops = { + .setattr = simple_setattr, + .getattr = simple_getattr, +}; + static const struct inode_operations libfs_dir_iops = { .lookup = simple_lookup, - .mkdir = libfs_mkdir, - .mknod = libfs_mknod, - .symlink = libfs_symlink, + .create = libfs_create, .unlink = libfs_unlink, + .mkdir = libfs_mkdir, .rmdir = libfs_rmdir, .rename = libfs_rename, .link = libfs_link, - .create = libfs_create, + .mknod = NULL, /* запрещаем */ + .symlink = NULL, /* запрещаем */ +}; + +static const struct super_operations statsfs_sops = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, + .evict_inode = libfs_evict_inode, }; 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; - + pr_info("statsfs: libfs_get_inode(sb=%p, mode=%o)\n", sb, mode); inode = new_inode(sb); - if (!inode) + if (!inode) { + pr_err("statsfs: new_inode failed for mode %o\n", mode); return NULL; + } inode->i_ino = get_next_ino(); inode->i_sb = sb; @@ -101,18 +86,21 @@ static struct inode *libfs_get_inode(struct super_block *sb, umode_t mode) if (S_ISDIR(mode)) { inode->i_op = &libfs_dir_iops; inode->i_fop = &simple_dir_operations; + pr_info("statsfs: created dir inode #%lu\n", inode->i_ino); } else { - /* файл — просто generic_file_* */ - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &libfs_file_ops; - /* заводим список жёстких ссылок */ + inode->i_op = &libfs_file_iops; + inode->i_fop = &libfs_file_ops; + inode->i_private = kzalloc(sizeof(struct hlist_head), GFP_KERNEL); if (!inode->i_private) { + pr_err("statsfs: kzalloc for inode #%lu failed\n", inode->i_ino); iput(inode); return NULL; } INIT_HLIST_HEAD((struct hlist_head *)inode->i_private); + pr_info("statsfs: created file inode #%lu with private list\n", inode->i_ino); } + return inode; } @@ -125,30 +113,36 @@ static void cleanup_links(struct inode *victim) if (!S_ISREG(victim->i_mode) || !victim->i_private) return; + pr_info("statsfs: cleanup_links for inode #%lu\n", victim->i_ino); list = (struct hlist_head *)victim->i_private; + hlist_for_each_entry_safe(le, tmp, list, node) { struct dentry *link = le->link; + struct dentry *pd; + struct inode *parent; if (!link || d_unlinked(link)) { + pr_info("statsfs: link already gone, freeing entry\n"); 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); - } + pd = dget_parent(link); + if (pd) { + parent = d_inode(pd); + if (WARN_ON(!parent)) + goto skip_unlink; + inode_lock(parent); + vfs_unlink(NULL, parent, link, NULL); + inode_unlock(parent); + pr_info("statsfs: unlinked %pd from parent inode #%lu\n", + link, parent->i_ino); + dput(pd); } + skip_unlink: hlist_del(&le->node); dput(link); kfree(le); @@ -158,94 +152,10 @@ static void cleanup_links(struct inode *victim) victim->i_private = NULL; } -static bool is_protected(struct dentry *dentry) +static bool is_protected(const struct dentry *d) { - 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; + return !strcmp(d->d_name.name, "books") || + !strcmp(d->d_name.name, "authors"); } static int libfs_create(struct mnt_idmap *idmap, struct inode *dir, @@ -253,15 +163,113 @@ static int libfs_create(struct mnt_idmap *idmap, struct inode *dir, { struct inode *inode; + pr_info("statsfs: CREATE %pd in dir inode #%lu mode=%o\n", + dentry, dir->i_ino, mode); 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); + pr_info("statsfs: added file %pd as inode #%lu\n", dentry, inode->i_ino); + return 0; +} + +static int libfs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *victim = d_inode(dentry); + int err; + + pr_info("statsfs: UNLINK %pd from dir inode #%lu\n", + dentry, dir->i_ino); + if (dir == libfs_sb->s_root->d_inode && is_protected(dentry)) + return -EPERM; + + err = simple_unlink(dir, dentry); + if (!err && dir == books_inode) { + cleanup_links(victim); + } + return err; +} + +static int libfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode) +{ + struct inode *inode; + + pr_info("statsfs: MKDIR %pd in dir inode #%lu\n", + dentry, dir->i_ino); + if (dir == libfs_sb->s_root->d_inode || dir == books_inode) + return -EPERM; + if (dir != authors_inode) + return -EPERM; + + 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); + pr_info("statsfs: created subdir %pd as inode #%lu\n", dentry, inode->i_ino); + return 0; +} + +static int libfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + pr_info("statsfs: RMDIR %pd in dir inode #%lu\n", + dentry, dir->i_ino); + 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 *odir, + struct dentry *odent, struct inode *ndir, + struct dentry *ndent, unsigned int flags) +{ + pr_info("statsfs: RENAME %pd (ino#%lu) -> %pd (ino#%lu)\n", + odent, odir->i_ino, ndent, ndir->i_ino); + if ((odir == libfs_sb->s_root->d_inode || ndir == libfs_sb->s_root->d_inode) && + (is_protected(odent) || is_protected(ndent))) + return -EPERM; + if (odir == authors_inode || ndir == authors_inode) + return -EPERM; + return simple_rename(idmap, odir, odent, ndir, ndent, flags); +} + +static int libfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + struct inode *t = d_inode(old_dentry); + struct link_entry *le; + int err; + + pr_info("statsfs: LINK %pd -> %pd in dir inode #%lu\n", + old_dentry, new_dentry, dir->i_ino); + if (!S_ISREG(t->i_mode)) + return -EPERM; + if (!authors_dentry || + new_dentry->d_parent->d_parent != authors_dentry) + return -EPERM; + + err = simple_link(old_dentry, dir, new_dentry); + if (err) + return err; + + le = kmalloc(sizeof(*le), GFP_KERNEL); + if (!le) + return -ENOMEM; + + le->link = dget(new_dentry); + hlist_add_head(&le->node, (struct hlist_head *)t->i_private); + pr_info("statsfs: recorded link entry for inode #%lu\n", t->i_ino); return 0; } @@ -269,40 +277,42 @@ 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; + pr_info("statsfs: fill_super\n"); + libfs_sb = sb; + sb->s_magic = LIBFS_MAGIC; + sb->s_op = &statsfs_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); - } + books_inode = libfs_get_inode(sb, S_IFDIR | LIBFS_BOOKS_MODE); + d_add(d_alloc_name(sb->s_root, "books"), 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); + pr_info("statsfs: superblock initialized\n"); return 0; } -static struct dentry *libfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static struct dentry *libfs_mount(struct file_system_type *fs, + int flags, const char *dev, void *data) { - return mount_nodev(fs_type, flags, data, libfs_fill_super); + pr_info("statsfs: mounting\n"); + return mount_nodev(fs, flags, data, libfs_fill_super); } static void libfs_evict_inode(struct inode *inode) { + pr_info("statsfs: evict_inode inode #%lu\n", inode->i_ino); truncate_inode_pages_final(&inode->i_data); cleanup_links(inode); clear_inode(inode); @@ -317,24 +327,17 @@ static struct file_system_type libfs_type = { static int __init libfs_init(void) { + pr_info("statsfs: module init\n"); 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; - } + pr_info("statsfs: module exit\n"); + dput(authors_dentry); + cleanup_links(books_inode); + iput(books_inode); + iput(authors_inode); unregister_filesystem(&libfs_type); } @@ -343,4 +346,4 @@ module_exit(libfs_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("you"); -MODULE_DESCRIPTION("Custom library FS with books and authors"); \ No newline at end of file +MODULE_DESCRIPTION("Custom library FS with books and authors (with debug logs)"); diff --git a/test.bash b/test.bash index 4f0f8ec..24c7d0d 100755 --- a/test.bash +++ b/test.bash @@ -1,5 +1,7 @@ #!/bin/bash +set -e + mnt=/mnt/test sudo mount -t libfs none "$mnt"