diff options
Diffstat (limited to 'ipc/kdbus/fs.c')
-rw-r--r-- | ipc/kdbus/fs.c | 508 |
1 files changed, 0 insertions, 508 deletions
diff --git a/ipc/kdbus/fs.c b/ipc/kdbus/fs.c deleted file mode 100644 index 6330c61e5..000000000 --- a/ipc/kdbus/fs.c +++ /dev/null @@ -1,508 +0,0 @@ -/* - * Copyright (C) 2013-2015 Kay Sievers - * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> - * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> - * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> - * Copyright (C) 2013-2015 Linux Foundation - * - * kdbus is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - */ - -#include <linux/dcache.h> -#include <linux/fs.h> -#include <linux/fsnotify.h> -#include <linux/init.h> -#include <linux/ipc_namespace.h> -#include <linux/magic.h> -#include <linux/module.h> -#include <linux/mount.h> -#include <linux/mutex.h> -#include <linux/namei.h> -#include <linux/pagemap.h> -#include <linux/sched.h> -#include <linux/slab.h> - -#include "bus.h" -#include "domain.h" -#include "endpoint.h" -#include "fs.h" -#include "handle.h" -#include "node.h" - -#define kdbus_node_from_dentry(_dentry) \ - ((struct kdbus_node *)(_dentry)->d_fsdata) - -static struct inode *fs_inode_get(struct super_block *sb, - struct kdbus_node *node); - -/* - * Directory Management - */ - -static inline unsigned char kdbus_dt_type(struct kdbus_node *node) -{ - switch (node->type) { - case KDBUS_NODE_DOMAIN: - case KDBUS_NODE_BUS: - return DT_DIR; - case KDBUS_NODE_CONTROL: - case KDBUS_NODE_ENDPOINT: - return DT_REG; - } - - return DT_UNKNOWN; -} - -static int fs_dir_fop_iterate(struct file *file, struct dir_context *ctx) -{ - struct dentry *dentry = file->f_path.dentry; - struct kdbus_node *parent = kdbus_node_from_dentry(dentry); - struct kdbus_node *old, *next = file->private_data; - - /* - * kdbusfs directory iterator (modelled after sysfs/kernfs) - * When iterating kdbusfs directories, we iterate all children of the - * parent kdbus_node object. We use ctx->pos to store the hash of the - * child and file->private_data to store a reference to the next node - * object. If ctx->pos is not modified via llseek while you iterate a - * directory, then we use the file->private_data node pointer to - * directly access the next node in the tree. - * However, if you directly seek on the directory, we have to find the - * closest node to that position and cannot use our node pointer. This - * means iterating the rb-tree to find the closest match and start over - * from there. - * Note that hash values are not necessarily unique. Therefore, llseek - * is not guaranteed to seek to the same node that you got when you - * retrieved the position. Seeking to 0, 1, 2 and >=INT_MAX is safe, - * though. We could use the inode-number as position, but this would - * require another rb-tree for fast access. Kernfs and others already - * ignore those conflicts, so we should be fine, too. - */ - - if (!dir_emit_dots(file, ctx)) - return 0; - - /* acquire @next; if deactivated, or seek detected, find next node */ - old = next; - if (next && ctx->pos == next->hash) { - if (kdbus_node_acquire(next)) - kdbus_node_ref(next); - else - next = kdbus_node_next_child(parent, next); - } else { - next = kdbus_node_find_closest(parent, ctx->pos); - } - kdbus_node_unref(old); - - while (next) { - /* emit @next */ - file->private_data = next; - ctx->pos = next->hash; - - kdbus_node_release(next); - - if (!dir_emit(ctx, next->name, strlen(next->name), next->id, - kdbus_dt_type(next))) - return 0; - - /* find next node after @next */ - old = next; - next = kdbus_node_next_child(parent, next); - kdbus_node_unref(old); - } - - file->private_data = NULL; - ctx->pos = INT_MAX; - - return 0; -} - -static loff_t fs_dir_fop_llseek(struct file *file, loff_t offset, int whence) -{ - struct inode *inode = file_inode(file); - loff_t ret; - - /* protect f_off against fop_iterate */ - mutex_lock(&inode->i_mutex); - ret = generic_file_llseek(file, offset, whence); - mutex_unlock(&inode->i_mutex); - - return ret; -} - -static int fs_dir_fop_release(struct inode *inode, struct file *file) -{ - kdbus_node_unref(file->private_data); - return 0; -} - -static const struct file_operations fs_dir_fops = { - .read = generic_read_dir, - .iterate = fs_dir_fop_iterate, - .llseek = fs_dir_fop_llseek, - .release = fs_dir_fop_release, -}; - -static struct dentry *fs_dir_iop_lookup(struct inode *dir, - struct dentry *dentry, - unsigned int flags) -{ - struct dentry *dnew = NULL; - struct kdbus_node *parent; - struct kdbus_node *node; - struct inode *inode; - - parent = kdbus_node_from_dentry(dentry->d_parent); - if (!kdbus_node_acquire(parent)) - return NULL; - - /* returns reference to _acquired_ child node */ - node = kdbus_node_find_child(parent, dentry->d_name.name); - if (node) { - dentry->d_fsdata = node; - inode = fs_inode_get(dir->i_sb, node); - if (IS_ERR(inode)) - dnew = ERR_CAST(inode); - else - dnew = d_splice_alias(inode, dentry); - - kdbus_node_release(node); - } - - kdbus_node_release(parent); - return dnew; -} - -static const struct inode_operations fs_dir_iops = { - .permission = generic_permission, - .lookup = fs_dir_iop_lookup, -}; - -/* - * Inode Management - */ - -static const struct inode_operations fs_inode_iops = { - .permission = generic_permission, -}; - -static struct inode *fs_inode_get(struct super_block *sb, - struct kdbus_node *node) -{ - struct inode *inode; - - inode = iget_locked(sb, node->id); - if (!inode) - return ERR_PTR(-ENOMEM); - if (!(inode->i_state & I_NEW)) - return inode; - - inode->i_private = kdbus_node_ref(node); - inode->i_mapping->a_ops = &empty_aops; - inode->i_mode = node->mode & S_IALLUGO; - inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; - inode->i_uid = node->uid; - inode->i_gid = node->gid; - - switch (node->type) { - case KDBUS_NODE_DOMAIN: - case KDBUS_NODE_BUS: - inode->i_mode |= S_IFDIR; - inode->i_op = &fs_dir_iops; - inode->i_fop = &fs_dir_fops; - set_nlink(inode, 2); - break; - case KDBUS_NODE_CONTROL: - case KDBUS_NODE_ENDPOINT: - inode->i_mode |= S_IFREG; - inode->i_op = &fs_inode_iops; - inode->i_fop = &kdbus_handle_ops; - break; - } - - unlock_new_inode(inode); - - return inode; -} - -/* - * Superblock Management - */ - -static int fs_super_dop_revalidate(struct dentry *dentry, unsigned int flags) -{ - struct kdbus_node *node; - - /* Force lookup on negatives */ - if (!dentry->d_inode) - return 0; - - node = kdbus_node_from_dentry(dentry); - - /* see whether the node has been removed */ - if (!kdbus_node_is_active(node)) - return 0; - - return 1; -} - -static void fs_super_dop_release(struct dentry *dentry) -{ - kdbus_node_unref(dentry->d_fsdata); -} - -static const struct dentry_operations fs_super_dops = { - .d_revalidate = fs_super_dop_revalidate, - .d_release = fs_super_dop_release, -}; - -static void fs_super_sop_evict_inode(struct inode *inode) -{ - struct kdbus_node *node = kdbus_node_from_inode(inode); - - truncate_inode_pages_final(&inode->i_data); - clear_inode(inode); - kdbus_node_unref(node); -} - -static const struct super_operations fs_super_sops = { - .statfs = simple_statfs, - .drop_inode = generic_delete_inode, - .evict_inode = fs_super_sop_evict_inode, -}; - -static int fs_super_fill(struct super_block *sb) -{ - struct kdbus_domain *domain = sb->s_fs_info; - struct inode *inode; - int ret; - - sb->s_blocksize = PAGE_CACHE_SIZE; - sb->s_blocksize_bits = PAGE_CACHE_SHIFT; - sb->s_magic = KDBUS_SUPER_MAGIC; - sb->s_maxbytes = MAX_LFS_FILESIZE; - sb->s_op = &fs_super_sops; - sb->s_time_gran = 1; - - inode = fs_inode_get(sb, &domain->node); - if (IS_ERR(inode)) - return PTR_ERR(inode); - - sb->s_root = d_make_root(inode); - if (!sb->s_root) { - /* d_make_root iput()s the inode on failure */ - return -ENOMEM; - } - - /* sb holds domain reference */ - sb->s_root->d_fsdata = &domain->node; - sb->s_d_op = &fs_super_dops; - - /* sb holds root reference */ - domain->dentry = sb->s_root; - - if (!kdbus_node_activate(&domain->node)) - return -ESHUTDOWN; - - ret = kdbus_domain_populate(domain, KDBUS_MAKE_ACCESS_WORLD); - if (ret < 0) - return ret; - - sb->s_flags |= MS_ACTIVE; - return 0; -} - -static void fs_super_kill(struct super_block *sb) -{ - struct kdbus_domain *domain = sb->s_fs_info; - - if (domain) { - kdbus_node_drain(&domain->node); - domain->dentry = NULL; - } - - kill_anon_super(sb); - kdbus_domain_unref(domain); -} - -static int fs_super_set(struct super_block *sb, void *data) -{ - int ret; - - ret = set_anon_super(sb, data); - if (!ret) - sb->s_fs_info = data; - - return ret; -} - -static struct dentry *fs_super_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) -{ - struct kdbus_domain *domain; - struct super_block *sb; - int ret; - - domain = kdbus_domain_new(KDBUS_MAKE_ACCESS_WORLD); - if (IS_ERR(domain)) - return ERR_CAST(domain); - - sb = sget(fs_type, NULL, fs_super_set, flags, domain); - if (IS_ERR(sb)) { - kdbus_node_drain(&domain->node); - kdbus_domain_unref(domain); - return ERR_CAST(sb); - } - - WARN_ON(sb->s_fs_info != domain); - WARN_ON(sb->s_root); - - ret = fs_super_fill(sb); - if (ret < 0) { - /* calls into ->kill_sb() when done */ - deactivate_locked_super(sb); - return ERR_PTR(ret); - } - - return dget(sb->s_root); -} - -static struct file_system_type fs_type = { - .name = KBUILD_MODNAME "fs", - .owner = THIS_MODULE, - .mount = fs_super_mount, - .kill_sb = fs_super_kill, - .fs_flags = FS_USERNS_MOUNT, -}; - -/** - * kdbus_fs_init() - register kdbus filesystem - * - * This registers a filesystem with the VFS layer. The filesystem is called - * `KBUILD_MODNAME "fs"', which usually resolves to `kdbusfs'. The nameing - * scheme allows to set KBUILD_MODNAME to "kdbus2" and you will get an - * independent filesystem for developers. - * - * Each mount of the kdbusfs filesystem has an kdbus_domain attached. - * Operations on this mount will only affect the attached domain. On each mount - * a new domain is automatically created and used for this mount exclusively. - * If you want to share a domain across multiple mounts, you need to bind-mount - * it. - * - * Mounts of kdbusfs (with a different domain each) are unrelated to each other - * and will never have any effect on any domain but their own. - * - * Return: 0 on success, negative error otherwise. - */ -int kdbus_fs_init(void) -{ - return register_filesystem(&fs_type); -} - -/** - * kdbus_fs_exit() - unregister kdbus filesystem - * - * This does the reverse to kdbus_fs_init(). It unregisters the kdbusfs - * filesystem from VFS and cleans up any allocated resources. - */ -void kdbus_fs_exit(void) -{ - unregister_filesystem(&fs_type); -} - -/* acquire domain of @node, making sure all ancestors are active */ -static struct kdbus_domain *fs_acquire_domain(struct kdbus_node *node) -{ - struct kdbus_domain *domain; - struct kdbus_node *iter; - - /* caller must guarantee that @node is linked */ - for (iter = node; iter->parent; iter = iter->parent) - if (!kdbus_node_is_active(iter->parent)) - return NULL; - - /* root nodes are always domains */ - if (WARN_ON(iter->type != KDBUS_NODE_DOMAIN)) - return NULL; - - domain = kdbus_domain_from_node(iter); - if (!kdbus_node_acquire(&domain->node)) - return NULL; - - return domain; -} - -/** - * kdbus_fs_flush() - flush dcache entries of a node - * @node: Node to flush entries of - * - * This flushes all VFS filesystem cache entries for a node and all its - * children. This should be called whenever a node is destroyed during - * runtime. It will flush the cache entries so the linked objects can be - * deallocated. - * - * This is a no-op if you call it on active nodes (they really should stay in - * cache) or on nodes with deactivated parents (flushing the parent is enough). - * Furthermore, there is no need to call it on nodes whose lifetime is bound to - * their parents'. In those cases, the parent-flush will always also flush the - * children. - */ -void kdbus_fs_flush(struct kdbus_node *node) -{ - struct dentry *dentry, *parent_dentry = NULL; - struct kdbus_domain *domain; - struct qstr name; - - /* active nodes should remain in cache */ - if (!kdbus_node_is_deactivated(node)) - return; - - /* nodes that were never linked were never instantiated */ - if (!node->parent) - return; - - /* acquire domain and verify all ancestors are active */ - domain = fs_acquire_domain(node); - if (!domain) - return; - - switch (node->type) { - case KDBUS_NODE_ENDPOINT: - if (WARN_ON(!node->parent || !node->parent->name)) - goto exit; - - name.name = node->parent->name; - name.len = strlen(node->parent->name); - parent_dentry = d_hash_and_lookup(domain->dentry, &name); - if (IS_ERR_OR_NULL(parent_dentry)) - goto exit; - - /* fallthrough */ - case KDBUS_NODE_BUS: - if (WARN_ON(!node->name)) - goto exit; - - name.name = node->name; - name.len = strlen(node->name); - dentry = d_hash_and_lookup(parent_dentry ? : domain->dentry, - &name); - if (!IS_ERR_OR_NULL(dentry)) { - d_invalidate(dentry); - dput(dentry); - } - - dput(parent_dentry); - break; - - default: - /* all other types are bound to their parent lifetime */ - break; - } - -exit: - kdbus_node_release(&domain->node); -} |