/** * anon_inode_getfile - creates a new file instance by hooking it up to an * anonymous inode, and a dentry that describe the "class" * of the file * * @name: [in] name of the "class" of the new file * @fops: [in] file operations for the new file * @priv: [in] private data for the new file (will be file's private_data) * @flags: [in] flags * * Creates a new file by hooking it on a single inode. This is useful for files * that do not need to have a full-fledged inode in order to operate correctly. * All the files created with anon_inode_getfile() will share a single inode, * hence saving memory and avoiding code duplication for the file/inode/dentry * setup. Returns the newly created file* or an error pointer. */ struct file *anon_inode_getfile(const char *name, const struct file_operations *fops, void *priv, int flags) { struct qstr this; struct path path; struct file *file; int error; if (IS_ERR(anon_inode_inode)) return ERR_PTR(-ENODEV); if (fops->owner && !try_module_get(fops->owner)) return ERR_PTR(-ENOENT); /* * Link the inode to a directory entry by creating a unique name * using the inode sequence number. */ error = -ENOMEM; this.name = name; this.len = strlen(name); this.hash = 0; path.dentry = d_alloc_pseudo(anon_inode_mnt->mnt_sb, &this); if (!path.dentry) goto err_module; path.mnt = mntget(anon_inode_mnt); /* * We know the anon_inode inode count is always greater than zero, * so ihold() is safe. */ ihold(anon_inode_inode); d_instantiate(path.dentry, anon_inode_inode); error = -ENFILE; file = alloc_file(&path, OPEN_FMODE(flags), fops); if (!file) goto err_dput; file->f_mapping = anon_inode_inode->i_mapping; file->f_pos = 0; file->f_flags = flags & (O_ACCMODE | O_NONBLOCK); file->f_version = 0; file->private_data = priv; return file; err_dput: path_put(&path); err_module: module_put(fops->owner); return ERR_PTR(error); }
struct file *anon_inode_getfile(const char *name, const struct file_operations *fops, void *priv, int flags) { struct qstr this; struct path path; struct file *file; int error; if (IS_ERR(anon_inode_inode)) return ERR_PTR(-ENODEV); if (fops->owner && !try_module_get(fops->owner)) return ERR_PTR(-ENOENT); error = -ENOMEM; this.name = name; this.len = strlen(name); this.hash = 0; path.dentry = d_alloc_pseudo(anon_inode_mnt->mnt_sb, &this); if (!path.dentry) goto err_module; path.mnt = mntget(anon_inode_mnt); ihold(anon_inode_inode); d_instantiate(path.dentry, anon_inode_inode); error = -ENFILE; file = alloc_file(&path, OPEN_FMODE(flags), fops); if (!file) goto err_dput; file->f_mapping = anon_inode_inode->i_mapping; file->f_pos = 0; file->f_flags = flags & (O_ACCMODE | O_NONBLOCK); file->f_version = 0; file->private_data = priv; return file; err_dput: path_put(&path); err_module: module_put(fops->owner); return ERR_PTR(error); }
static void *__ns_get_path(struct path *path, struct ns_common *ns) { struct vfsmount *mnt = nsfs_mnt; struct qstr qname = { .name = "", }; struct dentry *dentry; struct inode *inode; unsigned long d; rcu_read_lock(); d = atomic_long_read(&ns->stashed); if (!d) goto slow; dentry = (struct dentry *)d; if (!lockref_get_not_dead(&dentry->d_lockref)) goto slow; rcu_read_unlock(); ns->ops->put(ns); got_it: path->mnt = mntget(mnt); path->dentry = dentry; return NULL; slow: rcu_read_unlock(); inode = new_inode_pseudo(mnt->mnt_sb); if (!inode) { ns->ops->put(ns); return ERR_PTR(-ENOMEM); } inode->i_ino = ns->inum; inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); inode->i_flags |= S_IMMUTABLE; inode->i_mode = S_IFREG | S_IRUGO; inode->i_fop = &ns_file_operations; inode->i_private = ns; dentry = d_alloc_pseudo(mnt->mnt_sb, &qname); if (!dentry) { iput(inode); return ERR_PTR(-ENOMEM); } d_instantiate(dentry, inode); dentry->d_flags |= DCACHE_RCUACCESS; dentry->d_fsdata = (void *)ns->ops; d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry); if (d) { d_delete(dentry); /* make sure ->d_prune() does nothing */ dput(dentry); cpu_relax(); return ERR_PTR(-EAGAIN); } goto got_it; } void *ns_get_path(struct path *path, struct task_struct *task, const struct proc_ns_operations *ns_ops) { struct ns_common *ns; void *ret; again: ns = ns_ops->get(task); if (!ns) return ERR_PTR(-ENOENT); ret = __ns_get_path(path, ns); if (IS_ERR(ret) && PTR_ERR(ret) == -EAGAIN) goto again; return ret; } int open_related_ns(struct ns_common *ns, struct ns_common *(*get_ns)(struct ns_common *ns)) { struct path path = {}; struct file *f; void *err; int fd; fd = get_unused_fd_flags(O_CLOEXEC); if (fd < 0) return fd; while (1) { struct ns_common *relative; relative = get_ns(ns); if (IS_ERR(relative)) { put_unused_fd(fd); return PTR_ERR(relative); } err = __ns_get_path(&path, relative); if (IS_ERR(err) && PTR_ERR(err) == -EAGAIN) continue; break; } if (IS_ERR(err)) { put_unused_fd(fd); return PTR_ERR(err); } f = dentry_open(&path, O_RDONLY, current_cred()); path_put(&path); if (IS_ERR(f)) { put_unused_fd(fd); fd = PTR_ERR(f); } else fd_install(fd, f); return fd; } static long ns_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { struct user_namespace *user_ns; struct ns_common *ns = get_proc_ns(file_inode(filp)); uid_t __user *argp; uid_t uid; switch (ioctl) { case NS_GET_USERNS: return open_related_ns(ns, ns_get_owner); case NS_GET_PARENT: if (!ns->ops->get_parent) return -EINVAL; return open_related_ns(ns, ns->ops->get_parent); case NS_GET_NSTYPE: return ns->ops->type; case NS_GET_OWNER_UID: if (ns->ops->type != CLONE_NEWUSER) return -EINVAL; user_ns = container_of(ns, struct user_namespace, ns); argp = (uid_t __user *) arg; uid = from_kuid_munged(current_user_ns(), user_ns->owner); return put_user(uid, argp); default: return -ENOTTY; } } int ns_get_name(char *buf, size_t size, struct task_struct *task, const struct proc_ns_operations *ns_ops) { struct ns_common *ns; int res = -ENOENT; ns = ns_ops->get(task); if (ns) { res = snprintf(buf, size, "%s:[%u]", ns_ops->name, ns->inum); ns_ops->put(ns); } return res; } struct file *proc_ns_fget(int fd) { struct file *file; file = fget(fd); if (!file) return ERR_PTR(-EBADF); if (file->f_op != &ns_file_operations) goto out_invalid; return file; out_invalid: fput(file); return ERR_PTR(-EINVAL); } static int nsfs_show_path(struct seq_file *seq, struct dentry *dentry) { struct inode *inode = d_inode(dentry); const struct proc_ns_operations *ns_ops = dentry->d_fsdata; seq_printf(seq, "%s:[%lu]", ns_ops->name, inode->i_ino); return 0; } static const struct super_operations nsfs_ops = { .statfs = simple_statfs, .evict_inode = nsfs_evict, .show_path = nsfs_show_path, }; static struct dentry *nsfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_pseudo(fs_type, "nsfs:", &nsfs_ops, &ns_dentry_operations, NSFS_MAGIC); } static struct file_system_type nsfs = { .name = "nsfs", .mount = nsfs_mount, .kill_sb = kill_anon_super, }; void __init nsfs_init(void) { nsfs_mnt = kern_mount(&nsfs); if (IS_ERR(nsfs_mnt)) panic("can't set nsfs up\n"); nsfs_mnt->mnt_sb->s_flags &= ~MS_NOUSER; }
void *ns_get_path(struct path *path, struct task_struct *task, const struct proc_ns_operations *ns_ops) { struct vfsmount *mnt = mntget(nsfs_mnt); struct qstr qname = { .name = "", }; struct dentry *dentry; struct inode *inode; struct ns_common *ns; unsigned long d; again: ns = ns_ops->get(task); if (!ns) { mntput(mnt); return ERR_PTR(-ENOENT); } rcu_read_lock(); d = atomic_long_read(&ns->stashed); if (!d) goto slow; dentry = (struct dentry *)d; if (!lockref_get_not_dead(&dentry->d_lockref)) goto slow; rcu_read_unlock(); ns_ops->put(ns); got_it: path->mnt = mnt; path->dentry = dentry; return NULL; slow: rcu_read_unlock(); inode = new_inode_pseudo(mnt->mnt_sb); if (!inode) { ns_ops->put(ns); mntput(mnt); return ERR_PTR(-ENOMEM); } inode->i_ino = ns->inum; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_flags |= S_IMMUTABLE; inode->i_mode = S_IFREG | S_IRUGO; inode->i_fop = &ns_file_operations; inode->i_private = ns; dentry = d_alloc_pseudo(mnt->mnt_sb, &qname); if (!dentry) { iput(inode); mntput(mnt); return ERR_PTR(-ENOMEM); } d_instantiate(dentry, inode); dentry->d_fsdata = (void *)ns_ops; d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry); if (d) { d_delete(dentry); /* make sure ->d_prune() does nothing */ dput(dentry); cpu_relax(); goto again; } goto got_it; } int ns_get_name(char *buf, size_t size, struct task_struct *task, const struct proc_ns_operations *ns_ops) { struct ns_common *ns; int res = -ENOENT; ns = ns_ops->get(task); if (ns) { res = snprintf(buf, size, "%s:[%u]", ns_ops->name, ns->inum); ns_ops->put(ns); } return res; } struct file *proc_ns_fget(int fd) { struct file *file; file = fget(fd); if (!file) return ERR_PTR(-EBADF); if (file->f_op != &ns_file_operations) goto out_invalid; return file; out_invalid: fput(file); return ERR_PTR(-EINVAL); } static const struct super_operations nsfs_ops = { .statfs = simple_statfs, .evict_inode = nsfs_evict, }; static struct dentry *nsfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_pseudo(fs_type, "nsfs:", &nsfs_ops, &ns_dentry_operations, NSFS_MAGIC); } static struct file_system_type nsfs = { .name = "nsfs", .mount = nsfs_mount, .kill_sb = kill_anon_super, }; void __init nsfs_init(void) { nsfs_mnt = kern_mount(&nsfs); if (IS_ERR(nsfs_mnt)) panic("can't set nsfs up\n"); nsfs_mnt->mnt_sb->s_flags &= ~MS_NOUSER; }
static int onload_alloc_file(tcp_helper_resource_t *thr, oo_sp ep_id, int flags, int fd_type) { struct qstr name = { .name = "" }; #ifdef EFX_HAVE_STRUCT_PATH struct path path; #define my_dentry path.dentry #else struct dentry *dentry; #define my_dentry dentry #endif struct file *file; int fd; struct inode *inode; ci_private_t *priv; struct file_operations *fops; fops = oo_fops_by_type(fd_type); if( fops == NULL ) return -EINVAL; ci_assert_equal(fops->owner, THIS_MODULE); inode = new_inode(onload_mnt->mnt_sb); if( inode == NULL ) return -ENOMEM; #ifdef EFX_FSTYPE_HAS_MOUNT inode->i_ino = get_next_ino(); #endif if( fd_type == CI_PRIV_TYPE_NETIF ) inode->i_mode = S_IRWXUGO; if( fd_type == CI_PRIV_TYPE_TCP_EP || fd_type == CI_PRIV_TYPE_UDP_EP ) inode->i_mode = #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) /* in 2.6.18 this flag makes us "socket" and sendmsg crashes; * see sock_from_file() */ S_IFSOCK | #endif S_IRWXUGO; else inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); priv = &container_of(inode, struct onload_inode, vfs_inode)->priv; priv->thr = thr; priv->sock_id = ep_id; priv->fd_type = fd_type; fd = get_unused_fd(); if( fd < 0 ) { iput(inode); return fd; } /*ci_log("[%d]%s(%d:%d) return %d priv=%p", current->pid, __func__, thr->id, ep_id, fd, priv);*/ #ifdef EFX_FSTYPE_HAS_MOUNT #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,37) path.dentry = d_alloc(onload_mnt->mnt_sb->s_root, &name); if( path.dentry != NULL ) path.dentry->d_op = &onloadfs_dentry_operations; #else path.dentry = d_alloc_pseudo(onload_mnt->mnt_sb, &name); #endif #else /* EFX_FSTYPE_HAS_MOUNT */ #ifdef EFX_HAVE_D_DNAME my_dentry = d_alloc(onload_mnt->mnt_sb->s_root, &name); #else { char str[32]; name.len = onloadfs_name(&container_of(inode, struct onload_inode, vfs_inode)->priv, str, sizeof(str)); name.name = str; name.hash = inode->i_ino; my_dentry = d_alloc(onload_mnt->mnt_sb->s_root, &name); } #endif #endif /* EFX_FSTYPE_HAS_MOUNT */ if( my_dentry == NULL ) { put_unused_fd(fd); iput(inode); return -ENOMEM; } #if !defined(EFX_FSTYPE_HAS_MOUNT) || defined(EFX_OLD_MOUNT_PSEUDO) my_dentry->d_op = &onloadfs_dentry_operations; #if !defined(EFX_HAVE_STRUCT_PATH) && defined(EFX_HAVE_D_DNAME) my_dentry->d_flags &= ~DCACHE_UNHASHED; #endif #endif d_instantiate(my_dentry, inode); #ifndef EFX_HAVE_D_DNAME d_rehash(my_dentry); #endif inode->i_fop = fops; #ifdef EFX_HAVE_STRUCT_PATH path.mnt = mntget(onload_mnt); file = alloc_file(&path, FMODE_READ | FMODE_WRITE, fops); #else file = alloc_file(onload_mnt, dentry, FMODE_READ | FMODE_WRITE, fops); #endif if( file == NULL) { #ifdef EFX_HAVE_STRUCT_PATH path_put(&path); #else dput(dentry); iput(inode); #endif put_unused_fd(fd); return -ENFILE; } priv->_filp = file; file->f_flags = O_RDWR | (flags & O_NONBLOCK); file->f_pos = 0; file->private_data = priv; if( flags & O_CLOEXEC ) { struct files_struct *files = current->files; struct fdtable *fdt; spin_lock(&files->file_lock); fdt = files_fdtable(files); rcu_assign_pointer(fdt->fd[fd], file); efx_set_close_on_exec(fd, fdt); spin_unlock(&files->file_lock); } else fd_install(fd, file); try_module_get(THIS_MODULE); ci_assert_equal(file->f_op, fops); return fd; } void onload_priv_free(ci_private_t *priv) { if( priv->_filp->f_vfsmnt != onload_mnt) ci_free(priv); /* inode will free the priv automatically */ } int oo_create_fd(tcp_helper_endpoint_t* ep, int flags, int fd_type) { int fd; tcp_helper_resource_t *trs = ep->thr; citp_waitable_obj *wo = SP_TO_WAITABLE_OBJ(&trs->netif, ep->id); efab_thr_ref(trs); fd = onload_alloc_file(trs, ep->id, flags, fd_type); if( fd < 0 ) { efab_thr_release(trs); OO_DEBUG_ERR(ci_log("%s: onload_alloc_file failed (%d)", __FUNCTION__, fd)); return fd; } ci_atomic32_and(&wo-> waitable.sb_aflags, ~(CI_SB_AFLAG_ORPHAN | CI_SB_AFLAG_TCP_IN_ACCEPTQ)); return fd; }
/* * We have a vnode/inode, now get the dentry. If there isn't * one already pointing to the inode, we must fabricate a * disconnected entry for NFS to connect up to the known * dcache tree (in the paranoia/export directory checking * cases--but performance will be better if * NFSEXP_NOSUBTREECHECK is present in the export options--but * will this break our use of dentry->d_parent->d_inode in * dentry_to_fh()? XXX). * Returns a valid dentry pointer or a negative error code. * If the returned dentry is a new one, the inode's refcount is * incremented. */ STATIC struct dentry * vnlayer_find_dentry(VNODE_T *vp) { static const struct qstr this_is_anon = { .name = ""}; struct dentry *dp; struct dentry *dnew; INODE_T *ip; ip = VTOI(vp); /* * We create an anonymous dentry for NFS, but it will be used * only if we don't find a suitable one for this inode. * d_alloc is here because before 2.6.38 it acquires dcache_lock. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) dnew = d_alloc(NULL, &this_is_anon); #else dnew = d_alloc_pseudo(ip->i_sb, &this_is_anon); #endif if (dnew == NULL) { dp = ERR_PTR(-ENOMEM); } else { dnew->d_parent = dnew; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) spin_lock(&dcache_lock); #else spin_lock(&ip->i_lock); #endif /* * equivalent of d_splice_alias, * we only want view-extended dentries */ dp = vnlayer_inode2dentry_internal_no_lock(ip, NULL, NULL, &vnode_dentry_ops); if (dp == NULL) { /* new dentry, increase the refcount for this v/inode */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) VN_HOLD(vp); #else /* can't igrab because the spinlock is acquired */ ihold(ip); #endif /* found no suitable dentry, add a new one */ MDKI_SET_DOPS(dnew, &vnode_dentry_ops); dnew->d_sb = ip->i_sb; dnew->d_inode = ip; #if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) dnew->d_flags |= NFSD_DCACHE_DISCON; dnew->d_flags &= ~DCACHE_UNHASHED; #else /* LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) */ /* it is not clear if it needs DCACHE_RCUACCESS */ dnew->d_flags |= NFSD_DCACHE_DISCON; #endif /* else LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) hlist_add_head(&dnew->d_alias, &ip->i_dentry); #else list_add(&dnew->d_alias, &ip->i_dentry); #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) */ #if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) hlist_add_head(&dnew->d_hash, &ip->i_sb->s_anon); #else /* LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) */ hlist_bl_lock(&ip->i_sb->s_anon); hlist_bl_add_head_rcu(&dnew->d_hash, &ip->i_sb->s_anon); hlist_bl_unlock(&ip->i_sb->s_anon); #endif /* else LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) */ dp = dnew; /* skip the dput call for dnew, we will need it */ dnew = NULL; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) spin_unlock(&dcache_lock); #else spin_unlock(&ip->i_lock); #endif } /* dput dnew if we don't use it */ if (dnew != NULL) dput(dnew); return dp; } static const char vnode_verid_mvfs_linux_sops_c[] = "$Id: c9a2d7b9.e2bd11e3.8cd7.00:11:25:27:c4:b4 $";
static struct dentry *proc_ns_get_dentry(struct super_block *sb, struct task_struct *task, const struct proc_ns_operations *ns_ops) { struct dentry *dentry, *result; struct inode *inode; struct proc_inode *ei; struct qstr qname = { .name = "", }; void *ns; ns = ns_ops->get(task); if (!ns) return ERR_PTR(-ENOENT); dentry = d_alloc_pseudo(sb, &qname); if (!dentry) { ns_ops->put(ns); return ERR_PTR(-ENOMEM); } inode = iget_locked(sb, ns_ops->inum(ns)); if (!inode) { dput(dentry); ns_ops->put(ns); return ERR_PTR(-ENOMEM); } ei = PROC_I(inode); if (inode->i_state & I_NEW) { inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_op = &ns_inode_operations; inode->i_mode = S_IFREG | S_IRUGO; inode->i_fop = &ns_file_operations; ei->ns.ns_ops = ns_ops; ei->ns.ns = ns; unlock_new_inode(inode); } else { ns_ops->put(ns); } d_set_d_op(dentry, &ns_dentry_operations); result = d_instantiate_unique(dentry, inode); if (result) { dput(dentry); dentry = result; } return dentry; } static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd) { struct inode *inode = dentry->d_inode; struct super_block *sb = inode->i_sb; struct proc_inode *ei = PROC_I(inode); struct task_struct *task; struct path ns_path; void *error = ERR_PTR(-EACCES); task = get_proc_task(inode); if (!task) goto out; if (!ptrace_may_access(task, PTRACE_MODE_READ)) goto out_put_task; ns_path.dentry = proc_ns_get_dentry(sb, task, ei->ns.ns_ops); if (IS_ERR(ns_path.dentry)) { error = ERR_CAST(ns_path.dentry); goto out_put_task; } ns_path.mnt = mntget(nd->path.mnt); nd_jump_link(nd, &ns_path); error = NULL; out_put_task: put_task_struct(task); out: return error; } static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen) { struct inode *inode = dentry->d_inode; struct proc_inode *ei = PROC_I(inode); const struct proc_ns_operations *ns_ops = ei->ns.ns_ops; struct task_struct *task; void *ns; char name[50]; int len = -EACCES; task = get_proc_task(inode); if (!task) goto out; if (!ptrace_may_access(task, PTRACE_MODE_READ)) goto out_put_task; len = -ENOENT; ns = ns_ops->get(task); if (!ns) goto out_put_task; snprintf(name, sizeof(name), "%s:[%u]", ns_ops->name, ns_ops->inum(ns)); len = strlen(name); if (len > buflen) len = buflen; if (copy_to_user(buffer, name, len)) len = -EFAULT; ns_ops->put(ns); out_put_task: put_task_struct(task); out: return len; } static const struct inode_operations proc_ns_link_inode_operations = { .readlink = proc_ns_readlink, .follow_link = proc_ns_follow_link, .setattr = proc_setattr, }; static int proc_ns_instantiate(struct inode *dir, struct dentry *dentry, struct task_struct *task, const void *ptr) { const struct proc_ns_operations *ns_ops = ptr; struct inode *inode; struct proc_inode *ei; inode = proc_pid_make_inode(dir->i_sb, task); if (!inode) goto out; ei = PROC_I(inode); inode->i_mode = S_IFLNK|S_IRWXUGO; inode->i_op = &proc_ns_link_inode_operations; ei->ns.ns_ops = ns_ops; d_set_d_op(dentry, &pid_dentry_operations); d_add(dentry, inode); /* Close the race of the process dying before we return the dentry */ if (pid_revalidate(dentry, 0)) return 0; out: return -ENOENT; } static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx) { struct task_struct *task = get_proc_task(file_inode(file)); const struct proc_ns_operations **entry, **last; if (!task) return -ENOENT; if (!dir_emit_dots(file, ctx)) goto out; if (ctx->pos >= 2 + ARRAY_SIZE(ns_entries)) goto out; entry = ns_entries + (ctx->pos - 2); last = &ns_entries[ARRAY_SIZE(ns_entries) - 1]; while (entry <= last) { const struct proc_ns_operations *ops = *entry; if (!proc_fill_cache(file, ctx, ops->name, strlen(ops->name), proc_ns_instantiate, task, ops)) break; ctx->pos++; entry++; } out: put_task_struct(task); return 0; } const struct file_operations proc_ns_dir_operations = { .read = generic_read_dir, .iterate = proc_ns_dir_readdir, }; static struct dentry *proc_ns_dir_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { int error; struct task_struct *task = get_proc_task(dir); const struct proc_ns_operations **entry, **last; unsigned int len = dentry->d_name.len; error = -ENOENT; if (!task) goto out_no_task; last = &ns_entries[ARRAY_SIZE(ns_entries)]; for (entry = ns_entries; entry < last; entry++) { if (strlen((*entry)->name) != len) continue; if (!memcmp(dentry->d_name.name, (*entry)->name, len)) break; } if (entry == last) goto out; error = proc_ns_instantiate(dir, dentry, task, *entry); out: put_task_struct(task); out_no_task: return ERR_PTR(error); }