162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/proc/inode.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/cache.h> 962306a36Sopenharmony_ci#include <linux/time.h> 1062306a36Sopenharmony_ci#include <linux/proc_fs.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/pid_namespace.h> 1362306a36Sopenharmony_ci#include <linux/mm.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/stat.h> 1662306a36Sopenharmony_ci#include <linux/completion.h> 1762306a36Sopenharmony_ci#include <linux/poll.h> 1862306a36Sopenharmony_ci#include <linux/printk.h> 1962306a36Sopenharmony_ci#include <linux/file.h> 2062306a36Sopenharmony_ci#include <linux/limits.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/sysctl.h> 2462306a36Sopenharmony_ci#include <linux/seq_file.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <linux/mount.h> 2762306a36Sopenharmony_ci#include <linux/bug.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "internal.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void proc_evict_inode(struct inode *inode) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct proc_dir_entry *de; 3462306a36Sopenharmony_ci struct ctl_table_header *head; 3562306a36Sopenharmony_ci struct proc_inode *ei = PROC_I(inode); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci truncate_inode_pages_final(&inode->i_data); 3862306a36Sopenharmony_ci clear_inode(inode); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* Stop tracking associated processes */ 4162306a36Sopenharmony_ci if (ei->pid) { 4262306a36Sopenharmony_ci proc_pid_evict_inode(ei); 4362306a36Sopenharmony_ci ei->pid = NULL; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Let go of any associated proc directory entry */ 4762306a36Sopenharmony_ci de = ei->pde; 4862306a36Sopenharmony_ci if (de) { 4962306a36Sopenharmony_ci pde_put(de); 5062306a36Sopenharmony_ci ei->pde = NULL; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci head = ei->sysctl; 5462306a36Sopenharmony_ci if (head) { 5562306a36Sopenharmony_ci RCU_INIT_POINTER(ei->sysctl, NULL); 5662306a36Sopenharmony_ci proc_sys_evict_inode(inode, head); 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic struct kmem_cache *proc_inode_cachep __ro_after_init; 6162306a36Sopenharmony_cistatic struct kmem_cache *pde_opener_cache __ro_after_init; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic struct inode *proc_alloc_inode(struct super_block *sb) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct proc_inode *ei; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci ei = alloc_inode_sb(sb, proc_inode_cachep, GFP_KERNEL); 6862306a36Sopenharmony_ci if (!ei) 6962306a36Sopenharmony_ci return NULL; 7062306a36Sopenharmony_ci ei->pid = NULL; 7162306a36Sopenharmony_ci ei->fd = 0; 7262306a36Sopenharmony_ci ei->op.proc_get_link = NULL; 7362306a36Sopenharmony_ci ei->pde = NULL; 7462306a36Sopenharmony_ci ei->sysctl = NULL; 7562306a36Sopenharmony_ci ei->sysctl_entry = NULL; 7662306a36Sopenharmony_ci INIT_HLIST_NODE(&ei->sibling_inodes); 7762306a36Sopenharmony_ci ei->ns_ops = NULL; 7862306a36Sopenharmony_ci return &ei->vfs_inode; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void proc_free_inode(struct inode *inode) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci kmem_cache_free(proc_inode_cachep, PROC_I(inode)); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void init_once(void *foo) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct proc_inode *ei = (struct proc_inode *) foo; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci inode_init_once(&ei->vfs_inode); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_civoid __init proc_init_kmemcache(void) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci proc_inode_cachep = kmem_cache_create("proc_inode_cache", 9662306a36Sopenharmony_ci sizeof(struct proc_inode), 9762306a36Sopenharmony_ci 0, (SLAB_RECLAIM_ACCOUNT| 9862306a36Sopenharmony_ci SLAB_MEM_SPREAD|SLAB_ACCOUNT| 9962306a36Sopenharmony_ci SLAB_PANIC), 10062306a36Sopenharmony_ci init_once); 10162306a36Sopenharmony_ci pde_opener_cache = 10262306a36Sopenharmony_ci kmem_cache_create("pde_opener", sizeof(struct pde_opener), 0, 10362306a36Sopenharmony_ci SLAB_ACCOUNT|SLAB_PANIC, NULL); 10462306a36Sopenharmony_ci proc_dir_entry_cache = kmem_cache_create_usercopy( 10562306a36Sopenharmony_ci "proc_dir_entry", SIZEOF_PDE, 0, SLAB_PANIC, 10662306a36Sopenharmony_ci offsetof(struct proc_dir_entry, inline_name), 10762306a36Sopenharmony_ci SIZEOF_PDE_INLINE_NAME, NULL); 10862306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct proc_dir_entry) >= SIZEOF_PDE); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_civoid proc_invalidate_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct inode *inode; 11462306a36Sopenharmony_ci struct proc_inode *ei; 11562306a36Sopenharmony_ci struct hlist_node *node; 11662306a36Sopenharmony_ci struct super_block *old_sb = NULL; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci rcu_read_lock(); 11962306a36Sopenharmony_ci for (;;) { 12062306a36Sopenharmony_ci struct super_block *sb; 12162306a36Sopenharmony_ci node = hlist_first_rcu(inodes); 12262306a36Sopenharmony_ci if (!node) 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci ei = hlist_entry(node, struct proc_inode, sibling_inodes); 12562306a36Sopenharmony_ci spin_lock(lock); 12662306a36Sopenharmony_ci hlist_del_init_rcu(&ei->sibling_inodes); 12762306a36Sopenharmony_ci spin_unlock(lock); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci inode = &ei->vfs_inode; 13062306a36Sopenharmony_ci sb = inode->i_sb; 13162306a36Sopenharmony_ci if ((sb != old_sb) && !atomic_inc_not_zero(&sb->s_active)) 13262306a36Sopenharmony_ci continue; 13362306a36Sopenharmony_ci inode = igrab(inode); 13462306a36Sopenharmony_ci rcu_read_unlock(); 13562306a36Sopenharmony_ci if (sb != old_sb) { 13662306a36Sopenharmony_ci if (old_sb) 13762306a36Sopenharmony_ci deactivate_super(old_sb); 13862306a36Sopenharmony_ci old_sb = sb; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci if (unlikely(!inode)) { 14162306a36Sopenharmony_ci rcu_read_lock(); 14262306a36Sopenharmony_ci continue; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) { 14662306a36Sopenharmony_ci struct dentry *dir = d_find_any_alias(inode); 14762306a36Sopenharmony_ci if (dir) { 14862306a36Sopenharmony_ci d_invalidate(dir); 14962306a36Sopenharmony_ci dput(dir); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci } else { 15262306a36Sopenharmony_ci struct dentry *dentry; 15362306a36Sopenharmony_ci while ((dentry = d_find_alias(inode))) { 15462306a36Sopenharmony_ci d_invalidate(dentry); 15562306a36Sopenharmony_ci dput(dentry); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci iput(inode); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci rcu_read_lock(); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci rcu_read_unlock(); 16362306a36Sopenharmony_ci if (old_sb) 16462306a36Sopenharmony_ci deactivate_super(old_sb); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic inline const char *hidepid2str(enum proc_hidepid v) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci switch (v) { 17062306a36Sopenharmony_ci case HIDEPID_OFF: return "off"; 17162306a36Sopenharmony_ci case HIDEPID_NO_ACCESS: return "noaccess"; 17262306a36Sopenharmony_ci case HIDEPID_INVISIBLE: return "invisible"; 17362306a36Sopenharmony_ci case HIDEPID_NOT_PTRACEABLE: return "ptraceable"; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci WARN_ONCE(1, "bad hide_pid value: %d\n", v); 17662306a36Sopenharmony_ci return "unknown"; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int proc_show_options(struct seq_file *seq, struct dentry *root) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct proc_fs_info *fs_info = proc_sb_info(root->d_sb); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (!gid_eq(fs_info->pid_gid, GLOBAL_ROOT_GID)) 18462306a36Sopenharmony_ci seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, fs_info->pid_gid)); 18562306a36Sopenharmony_ci if (fs_info->hide_pid != HIDEPID_OFF) 18662306a36Sopenharmony_ci seq_printf(seq, ",hidepid=%s", hidepid2str(fs_info->hide_pid)); 18762306a36Sopenharmony_ci if (fs_info->pidonly != PROC_PIDONLY_OFF) 18862306a36Sopenharmony_ci seq_printf(seq, ",subset=pid"); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciconst struct super_operations proc_sops = { 19462306a36Sopenharmony_ci .alloc_inode = proc_alloc_inode, 19562306a36Sopenharmony_ci .free_inode = proc_free_inode, 19662306a36Sopenharmony_ci .drop_inode = generic_delete_inode, 19762306a36Sopenharmony_ci .evict_inode = proc_evict_inode, 19862306a36Sopenharmony_ci .statfs = simple_statfs, 19962306a36Sopenharmony_ci .show_options = proc_show_options, 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cienum {BIAS = -1U<<31}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic inline int use_pde(struct proc_dir_entry *pde) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci return likely(atomic_inc_unless_negative(&pde->in_use)); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic void unuse_pde(struct proc_dir_entry *pde) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci if (unlikely(atomic_dec_return(&pde->in_use) == BIAS)) 21262306a36Sopenharmony_ci complete(pde->pde_unload_completion); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/* 21662306a36Sopenharmony_ci * At most 2 contexts can enter this function: the one doing the last 21762306a36Sopenharmony_ci * close on the descriptor and whoever is deleting PDE itself. 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci * First to enter calls ->proc_release hook and signals its completion 22062306a36Sopenharmony_ci * to the second one which waits and then does nothing. 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * PDE is locked on entry, unlocked on exit. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_cistatic void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo) 22562306a36Sopenharmony_ci __releases(&pde->pde_unload_lock) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci /* 22862306a36Sopenharmony_ci * close() (proc_reg_release()) can't delete an entry and proceed: 22962306a36Sopenharmony_ci * ->release hook needs to be available at the right moment. 23062306a36Sopenharmony_ci * 23162306a36Sopenharmony_ci * rmmod (remove_proc_entry() et al) can't delete an entry and proceed: 23262306a36Sopenharmony_ci * "struct file" needs to be available at the right moment. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci if (pdeo->closing) { 23562306a36Sopenharmony_ci /* somebody else is doing that, just wait */ 23662306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(c); 23762306a36Sopenharmony_ci pdeo->c = &c; 23862306a36Sopenharmony_ci spin_unlock(&pde->pde_unload_lock); 23962306a36Sopenharmony_ci wait_for_completion(&c); 24062306a36Sopenharmony_ci } else { 24162306a36Sopenharmony_ci struct file *file; 24262306a36Sopenharmony_ci struct completion *c; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci pdeo->closing = true; 24562306a36Sopenharmony_ci spin_unlock(&pde->pde_unload_lock); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci file = pdeo->file; 24862306a36Sopenharmony_ci pde->proc_ops->proc_release(file_inode(file), file); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci spin_lock(&pde->pde_unload_lock); 25162306a36Sopenharmony_ci /* Strictly after ->proc_release, see above. */ 25262306a36Sopenharmony_ci list_del(&pdeo->lh); 25362306a36Sopenharmony_ci c = pdeo->c; 25462306a36Sopenharmony_ci spin_unlock(&pde->pde_unload_lock); 25562306a36Sopenharmony_ci if (unlikely(c)) 25662306a36Sopenharmony_ci complete(c); 25762306a36Sopenharmony_ci kmem_cache_free(pde_opener_cache, pdeo); 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_civoid proc_entry_rundown(struct proc_dir_entry *de) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(c); 26462306a36Sopenharmony_ci /* Wait until all existing callers into module are done. */ 26562306a36Sopenharmony_ci de->pde_unload_completion = &c; 26662306a36Sopenharmony_ci if (atomic_add_return(BIAS, &de->in_use) != BIAS) 26762306a36Sopenharmony_ci wait_for_completion(&c); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* ->pde_openers list can't grow from now on. */ 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci spin_lock(&de->pde_unload_lock); 27262306a36Sopenharmony_ci while (!list_empty(&de->pde_openers)) { 27362306a36Sopenharmony_ci struct pde_opener *pdeo; 27462306a36Sopenharmony_ci pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh); 27562306a36Sopenharmony_ci close_pdeo(de, pdeo); 27662306a36Sopenharmony_ci spin_lock(&de->pde_unload_lock); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci spin_unlock(&de->pde_unload_lock); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 28462306a36Sopenharmony_ci loff_t rv = -EINVAL; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (pde_is_permanent(pde)) { 28762306a36Sopenharmony_ci return pde->proc_ops->proc_lseek(file, offset, whence); 28862306a36Sopenharmony_ci } else if (use_pde(pde)) { 28962306a36Sopenharmony_ci rv = pde->proc_ops->proc_lseek(file, offset, whence); 29062306a36Sopenharmony_ci unuse_pde(pde); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci return rv; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic ssize_t proc_reg_read_iter(struct kiocb *iocb, struct iov_iter *iter) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(iocb->ki_filp)); 29862306a36Sopenharmony_ci ssize_t ret; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (pde_is_permanent(pde)) 30162306a36Sopenharmony_ci return pde->proc_ops->proc_read_iter(iocb, iter); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (!use_pde(pde)) 30462306a36Sopenharmony_ci return -EIO; 30562306a36Sopenharmony_ci ret = pde->proc_ops->proc_read_iter(iocb, iter); 30662306a36Sopenharmony_ci unuse_pde(pde); 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic ssize_t pde_read(struct proc_dir_entry *pde, struct file *file, char __user *buf, size_t count, loff_t *ppos) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci typeof_member(struct proc_ops, proc_read) read; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci read = pde->proc_ops->proc_read; 31562306a36Sopenharmony_ci if (read) 31662306a36Sopenharmony_ci return read(file, buf, count, ppos); 31762306a36Sopenharmony_ci return -EIO; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 32362306a36Sopenharmony_ci ssize_t rv = -EIO; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (pde_is_permanent(pde)) { 32662306a36Sopenharmony_ci return pde_read(pde, file, buf, count, ppos); 32762306a36Sopenharmony_ci } else if (use_pde(pde)) { 32862306a36Sopenharmony_ci rv = pde_read(pde, file, buf, count, ppos); 32962306a36Sopenharmony_ci unuse_pde(pde); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci return rv; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic ssize_t pde_write(struct proc_dir_entry *pde, struct file *file, const char __user *buf, size_t count, loff_t *ppos) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci typeof_member(struct proc_ops, proc_write) write; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci write = pde->proc_ops->proc_write; 33962306a36Sopenharmony_ci if (write) 34062306a36Sopenharmony_ci return write(file, buf, count, ppos); 34162306a36Sopenharmony_ci return -EIO; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 34762306a36Sopenharmony_ci ssize_t rv = -EIO; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (pde_is_permanent(pde)) { 35062306a36Sopenharmony_ci return pde_write(pde, file, buf, count, ppos); 35162306a36Sopenharmony_ci } else if (use_pde(pde)) { 35262306a36Sopenharmony_ci rv = pde_write(pde, file, buf, count, ppos); 35362306a36Sopenharmony_ci unuse_pde(pde); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci return rv; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic __poll_t pde_poll(struct proc_dir_entry *pde, struct file *file, struct poll_table_struct *pts) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci typeof_member(struct proc_ops, proc_poll) poll; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci poll = pde->proc_ops->proc_poll; 36362306a36Sopenharmony_ci if (poll) 36462306a36Sopenharmony_ci return poll(file, pts); 36562306a36Sopenharmony_ci return DEFAULT_POLLMASK; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic __poll_t proc_reg_poll(struct file *file, struct poll_table_struct *pts) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 37162306a36Sopenharmony_ci __poll_t rv = DEFAULT_POLLMASK; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (pde_is_permanent(pde)) { 37462306a36Sopenharmony_ci return pde_poll(pde, file, pts); 37562306a36Sopenharmony_ci } else if (use_pde(pde)) { 37662306a36Sopenharmony_ci rv = pde_poll(pde, file, pts); 37762306a36Sopenharmony_ci unuse_pde(pde); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci return rv; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic long pde_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci typeof_member(struct proc_ops, proc_ioctl) ioctl; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ioctl = pde->proc_ops->proc_ioctl; 38762306a36Sopenharmony_ci if (ioctl) 38862306a36Sopenharmony_ci return ioctl(file, cmd, arg); 38962306a36Sopenharmony_ci return -ENOTTY; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 39562306a36Sopenharmony_ci long rv = -ENOTTY; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (pde_is_permanent(pde)) { 39862306a36Sopenharmony_ci return pde_ioctl(pde, file, cmd, arg); 39962306a36Sopenharmony_ci } else if (use_pde(pde)) { 40062306a36Sopenharmony_ci rv = pde_ioctl(pde, file, cmd, arg); 40162306a36Sopenharmony_ci unuse_pde(pde); 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci return rv; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 40762306a36Sopenharmony_cistatic long pde_compat_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci typeof_member(struct proc_ops, proc_compat_ioctl) compat_ioctl; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci compat_ioctl = pde->proc_ops->proc_compat_ioctl; 41262306a36Sopenharmony_ci if (compat_ioctl) 41362306a36Sopenharmony_ci return compat_ioctl(file, cmd, arg); 41462306a36Sopenharmony_ci return -ENOTTY; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 42062306a36Sopenharmony_ci long rv = -ENOTTY; 42162306a36Sopenharmony_ci if (pde_is_permanent(pde)) { 42262306a36Sopenharmony_ci return pde_compat_ioctl(pde, file, cmd, arg); 42362306a36Sopenharmony_ci } else if (use_pde(pde)) { 42462306a36Sopenharmony_ci rv = pde_compat_ioctl(pde, file, cmd, arg); 42562306a36Sopenharmony_ci unuse_pde(pde); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci return rv; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci#endif 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int pde_mmap(struct proc_dir_entry *pde, struct file *file, struct vm_area_struct *vma) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci typeof_member(struct proc_ops, proc_mmap) mmap; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci mmap = pde->proc_ops->proc_mmap; 43662306a36Sopenharmony_ci if (mmap) 43762306a36Sopenharmony_ci return mmap(file, vma); 43862306a36Sopenharmony_ci return -EIO; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic int proc_reg_mmap(struct file *file, struct vm_area_struct *vma) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 44462306a36Sopenharmony_ci int rv = -EIO; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (pde_is_permanent(pde)) { 44762306a36Sopenharmony_ci return pde_mmap(pde, file, vma); 44862306a36Sopenharmony_ci } else if (use_pde(pde)) { 44962306a36Sopenharmony_ci rv = pde_mmap(pde, file, vma); 45062306a36Sopenharmony_ci unuse_pde(pde); 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci return rv; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic unsigned long 45662306a36Sopenharmony_cipde_get_unmapped_area(struct proc_dir_entry *pde, struct file *file, unsigned long orig_addr, 45762306a36Sopenharmony_ci unsigned long len, unsigned long pgoff, 45862306a36Sopenharmony_ci unsigned long flags) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci typeof_member(struct proc_ops, proc_get_unmapped_area) get_area; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci get_area = pde->proc_ops->proc_get_unmapped_area; 46362306a36Sopenharmony_ci#ifdef CONFIG_MMU 46462306a36Sopenharmony_ci if (!get_area) 46562306a36Sopenharmony_ci get_area = current->mm->get_unmapped_area; 46662306a36Sopenharmony_ci#endif 46762306a36Sopenharmony_ci if (get_area) 46862306a36Sopenharmony_ci return get_area(file, orig_addr, len, pgoff, flags); 46962306a36Sopenharmony_ci return orig_addr; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic unsigned long 47362306a36Sopenharmony_ciproc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr, 47462306a36Sopenharmony_ci unsigned long len, unsigned long pgoff, 47562306a36Sopenharmony_ci unsigned long flags) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 47862306a36Sopenharmony_ci unsigned long rv = -EIO; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (pde_is_permanent(pde)) { 48162306a36Sopenharmony_ci return pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags); 48262306a36Sopenharmony_ci } else if (use_pde(pde)) { 48362306a36Sopenharmony_ci rv = pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags); 48462306a36Sopenharmony_ci unuse_pde(pde); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci return rv; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int proc_reg_open(struct inode *inode, struct file *file) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(inode); 49262306a36Sopenharmony_ci int rv = 0; 49362306a36Sopenharmony_ci typeof_member(struct proc_ops, proc_open) open; 49462306a36Sopenharmony_ci typeof_member(struct proc_ops, proc_release) release; 49562306a36Sopenharmony_ci struct pde_opener *pdeo; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (!pde->proc_ops->proc_lseek) 49862306a36Sopenharmony_ci file->f_mode &= ~FMODE_LSEEK; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (pde_is_permanent(pde)) { 50162306a36Sopenharmony_ci open = pde->proc_ops->proc_open; 50262306a36Sopenharmony_ci if (open) 50362306a36Sopenharmony_ci rv = open(inode, file); 50462306a36Sopenharmony_ci return rv; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* 50862306a36Sopenharmony_ci * Ensure that 50962306a36Sopenharmony_ci * 1) PDE's ->release hook will be called no matter what 51062306a36Sopenharmony_ci * either normally by close()/->release, or forcefully by 51162306a36Sopenharmony_ci * rmmod/remove_proc_entry. 51262306a36Sopenharmony_ci * 51362306a36Sopenharmony_ci * 2) rmmod isn't blocked by opening file in /proc and sitting on 51462306a36Sopenharmony_ci * the descriptor (including "rmmod foo </proc/foo" scenario). 51562306a36Sopenharmony_ci * 51662306a36Sopenharmony_ci * Save every "struct file" with custom ->release hook. 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_ci if (!use_pde(pde)) 51962306a36Sopenharmony_ci return -ENOENT; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci release = pde->proc_ops->proc_release; 52262306a36Sopenharmony_ci if (release) { 52362306a36Sopenharmony_ci pdeo = kmem_cache_alloc(pde_opener_cache, GFP_KERNEL); 52462306a36Sopenharmony_ci if (!pdeo) { 52562306a36Sopenharmony_ci rv = -ENOMEM; 52662306a36Sopenharmony_ci goto out_unuse; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci open = pde->proc_ops->proc_open; 53162306a36Sopenharmony_ci if (open) 53262306a36Sopenharmony_ci rv = open(inode, file); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (release) { 53562306a36Sopenharmony_ci if (rv == 0) { 53662306a36Sopenharmony_ci /* To know what to release. */ 53762306a36Sopenharmony_ci pdeo->file = file; 53862306a36Sopenharmony_ci pdeo->closing = false; 53962306a36Sopenharmony_ci pdeo->c = NULL; 54062306a36Sopenharmony_ci spin_lock(&pde->pde_unload_lock); 54162306a36Sopenharmony_ci list_add(&pdeo->lh, &pde->pde_openers); 54262306a36Sopenharmony_ci spin_unlock(&pde->pde_unload_lock); 54362306a36Sopenharmony_ci } else 54462306a36Sopenharmony_ci kmem_cache_free(pde_opener_cache, pdeo); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ciout_unuse: 54862306a36Sopenharmony_ci unuse_pde(pde); 54962306a36Sopenharmony_ci return rv; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int proc_reg_release(struct inode *inode, struct file *file) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(inode); 55562306a36Sopenharmony_ci struct pde_opener *pdeo; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (pde_is_permanent(pde)) { 55862306a36Sopenharmony_ci typeof_member(struct proc_ops, proc_release) release; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci release = pde->proc_ops->proc_release; 56162306a36Sopenharmony_ci if (release) { 56262306a36Sopenharmony_ci return release(inode, file); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci spin_lock(&pde->pde_unload_lock); 56862306a36Sopenharmony_ci list_for_each_entry(pdeo, &pde->pde_openers, lh) { 56962306a36Sopenharmony_ci if (pdeo->file == file) { 57062306a36Sopenharmony_ci close_pdeo(pde, pdeo); 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci spin_unlock(&pde->pde_unload_lock); 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic const struct file_operations proc_reg_file_ops = { 57962306a36Sopenharmony_ci .llseek = proc_reg_llseek, 58062306a36Sopenharmony_ci .read = proc_reg_read, 58162306a36Sopenharmony_ci .write = proc_reg_write, 58262306a36Sopenharmony_ci .poll = proc_reg_poll, 58362306a36Sopenharmony_ci .unlocked_ioctl = proc_reg_unlocked_ioctl, 58462306a36Sopenharmony_ci .mmap = proc_reg_mmap, 58562306a36Sopenharmony_ci .get_unmapped_area = proc_reg_get_unmapped_area, 58662306a36Sopenharmony_ci .open = proc_reg_open, 58762306a36Sopenharmony_ci .release = proc_reg_release, 58862306a36Sopenharmony_ci}; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic const struct file_operations proc_iter_file_ops = { 59162306a36Sopenharmony_ci .llseek = proc_reg_llseek, 59262306a36Sopenharmony_ci .read_iter = proc_reg_read_iter, 59362306a36Sopenharmony_ci .write = proc_reg_write, 59462306a36Sopenharmony_ci .splice_read = copy_splice_read, 59562306a36Sopenharmony_ci .poll = proc_reg_poll, 59662306a36Sopenharmony_ci .unlocked_ioctl = proc_reg_unlocked_ioctl, 59762306a36Sopenharmony_ci .mmap = proc_reg_mmap, 59862306a36Sopenharmony_ci .get_unmapped_area = proc_reg_get_unmapped_area, 59962306a36Sopenharmony_ci .open = proc_reg_open, 60062306a36Sopenharmony_ci .release = proc_reg_release, 60162306a36Sopenharmony_ci}; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 60462306a36Sopenharmony_cistatic const struct file_operations proc_reg_file_ops_compat = { 60562306a36Sopenharmony_ci .llseek = proc_reg_llseek, 60662306a36Sopenharmony_ci .read = proc_reg_read, 60762306a36Sopenharmony_ci .write = proc_reg_write, 60862306a36Sopenharmony_ci .poll = proc_reg_poll, 60962306a36Sopenharmony_ci .unlocked_ioctl = proc_reg_unlocked_ioctl, 61062306a36Sopenharmony_ci .compat_ioctl = proc_reg_compat_ioctl, 61162306a36Sopenharmony_ci .mmap = proc_reg_mmap, 61262306a36Sopenharmony_ci .get_unmapped_area = proc_reg_get_unmapped_area, 61362306a36Sopenharmony_ci .open = proc_reg_open, 61462306a36Sopenharmony_ci .release = proc_reg_release, 61562306a36Sopenharmony_ci}; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic const struct file_operations proc_iter_file_ops_compat = { 61862306a36Sopenharmony_ci .llseek = proc_reg_llseek, 61962306a36Sopenharmony_ci .read_iter = proc_reg_read_iter, 62062306a36Sopenharmony_ci .splice_read = copy_splice_read, 62162306a36Sopenharmony_ci .write = proc_reg_write, 62262306a36Sopenharmony_ci .poll = proc_reg_poll, 62362306a36Sopenharmony_ci .unlocked_ioctl = proc_reg_unlocked_ioctl, 62462306a36Sopenharmony_ci .compat_ioctl = proc_reg_compat_ioctl, 62562306a36Sopenharmony_ci .mmap = proc_reg_mmap, 62662306a36Sopenharmony_ci .get_unmapped_area = proc_reg_get_unmapped_area, 62762306a36Sopenharmony_ci .open = proc_reg_open, 62862306a36Sopenharmony_ci .release = proc_reg_release, 62962306a36Sopenharmony_ci}; 63062306a36Sopenharmony_ci#endif 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic void proc_put_link(void *p) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci unuse_pde(p); 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic const char *proc_get_link(struct dentry *dentry, 63862306a36Sopenharmony_ci struct inode *inode, 63962306a36Sopenharmony_ci struct delayed_call *done) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct proc_dir_entry *pde = PDE(inode); 64262306a36Sopenharmony_ci if (!use_pde(pde)) 64362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 64462306a36Sopenharmony_ci set_delayed_call(done, proc_put_link, pde); 64562306a36Sopenharmony_ci return pde->data; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ciconst struct inode_operations proc_link_inode_operations = { 64962306a36Sopenharmony_ci .get_link = proc_get_link, 65062306a36Sopenharmony_ci}; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistruct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct inode *inode = new_inode(sb); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (!inode) { 65762306a36Sopenharmony_ci pde_put(de); 65862306a36Sopenharmony_ci return NULL; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci inode->i_private = de->data; 66262306a36Sopenharmony_ci inode->i_ino = de->low_ino; 66362306a36Sopenharmony_ci inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode); 66462306a36Sopenharmony_ci PROC_I(inode)->pde = de; 66562306a36Sopenharmony_ci if (is_empty_pde(de)) { 66662306a36Sopenharmony_ci make_empty_dir_inode(inode); 66762306a36Sopenharmony_ci return inode; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (de->mode) { 67162306a36Sopenharmony_ci inode->i_mode = de->mode; 67262306a36Sopenharmony_ci inode->i_uid = de->uid; 67362306a36Sopenharmony_ci inode->i_gid = de->gid; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci if (de->size) 67662306a36Sopenharmony_ci inode->i_size = de->size; 67762306a36Sopenharmony_ci if (de->nlink) 67862306a36Sopenharmony_ci set_nlink(inode, de->nlink); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) { 68162306a36Sopenharmony_ci inode->i_op = de->proc_iops; 68262306a36Sopenharmony_ci if (de->proc_ops->proc_read_iter) 68362306a36Sopenharmony_ci inode->i_fop = &proc_iter_file_ops; 68462306a36Sopenharmony_ci else 68562306a36Sopenharmony_ci inode->i_fop = &proc_reg_file_ops; 68662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 68762306a36Sopenharmony_ci if (de->proc_ops->proc_compat_ioctl) { 68862306a36Sopenharmony_ci if (de->proc_ops->proc_read_iter) 68962306a36Sopenharmony_ci inode->i_fop = &proc_iter_file_ops_compat; 69062306a36Sopenharmony_ci else 69162306a36Sopenharmony_ci inode->i_fop = &proc_reg_file_ops_compat; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci#endif 69462306a36Sopenharmony_ci } else if (S_ISDIR(inode->i_mode)) { 69562306a36Sopenharmony_ci inode->i_op = de->proc_iops; 69662306a36Sopenharmony_ci inode->i_fop = de->proc_dir_ops; 69762306a36Sopenharmony_ci } else if (S_ISLNK(inode->i_mode)) { 69862306a36Sopenharmony_ci inode->i_op = de->proc_iops; 69962306a36Sopenharmony_ci inode->i_fop = NULL; 70062306a36Sopenharmony_ci } else { 70162306a36Sopenharmony_ci BUG(); 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci return inode; 70462306a36Sopenharmony_ci} 705