18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/proc/inode.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/cache.h> 98c2ecf20Sopenharmony_ci#include <linux/time.h> 108c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/pid_namespace.h> 138c2ecf20Sopenharmony_ci#include <linux/mm.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/stat.h> 168c2ecf20Sopenharmony_ci#include <linux/completion.h> 178c2ecf20Sopenharmony_ci#include <linux/poll.h> 188c2ecf20Sopenharmony_ci#include <linux/printk.h> 198c2ecf20Sopenharmony_ci#include <linux/file.h> 208c2ecf20Sopenharmony_ci#include <linux/limits.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 248c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/mount.h> 278c2ecf20Sopenharmony_ci#include <linux/bug.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "internal.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void proc_evict_inode(struct inode *inode) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct proc_dir_entry *de; 368c2ecf20Sopenharmony_ci struct ctl_table_header *head; 378c2ecf20Sopenharmony_ci struct proc_inode *ei = PROC_I(inode); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci truncate_inode_pages_final(&inode->i_data); 408c2ecf20Sopenharmony_ci clear_inode(inode); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* Stop tracking associated processes */ 438c2ecf20Sopenharmony_ci if (ei->pid) { 448c2ecf20Sopenharmony_ci proc_pid_evict_inode(ei); 458c2ecf20Sopenharmony_ci ei->pid = NULL; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* Let go of any associated proc directory entry */ 498c2ecf20Sopenharmony_ci de = ei->pde; 508c2ecf20Sopenharmony_ci if (de) { 518c2ecf20Sopenharmony_ci pde_put(de); 528c2ecf20Sopenharmony_ci ei->pde = NULL; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci head = ei->sysctl; 568c2ecf20Sopenharmony_ci if (head) { 578c2ecf20Sopenharmony_ci RCU_INIT_POINTER(ei->sysctl, NULL); 588c2ecf20Sopenharmony_ci proc_sys_evict_inode(inode, head); 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct kmem_cache *proc_inode_cachep __ro_after_init; 638c2ecf20Sopenharmony_cistatic struct kmem_cache *pde_opener_cache __ro_after_init; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic struct inode *proc_alloc_inode(struct super_block *sb) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct proc_inode *ei; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci ei = kmem_cache_alloc(proc_inode_cachep, GFP_KERNEL); 708c2ecf20Sopenharmony_ci if (!ei) 718c2ecf20Sopenharmony_ci return NULL; 728c2ecf20Sopenharmony_ci ei->pid = NULL; 738c2ecf20Sopenharmony_ci ei->fd = 0; 748c2ecf20Sopenharmony_ci ei->op.proc_get_link = NULL; 758c2ecf20Sopenharmony_ci ei->pde = NULL; 768c2ecf20Sopenharmony_ci ei->sysctl = NULL; 778c2ecf20Sopenharmony_ci ei->sysctl_entry = NULL; 788c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&ei->sibling_inodes); 798c2ecf20Sopenharmony_ci ei->ns_ops = NULL; 808c2ecf20Sopenharmony_ci return &ei->vfs_inode; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void proc_free_inode(struct inode *inode) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci kmem_cache_free(proc_inode_cachep, PROC_I(inode)); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void init_once(void *foo) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct proc_inode *ei = (struct proc_inode *) foo; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci inode_init_once(&ei->vfs_inode); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_civoid __init proc_init_kmemcache(void) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci proc_inode_cachep = kmem_cache_create("proc_inode_cache", 988c2ecf20Sopenharmony_ci sizeof(struct proc_inode), 998c2ecf20Sopenharmony_ci 0, (SLAB_RECLAIM_ACCOUNT| 1008c2ecf20Sopenharmony_ci SLAB_MEM_SPREAD|SLAB_ACCOUNT| 1018c2ecf20Sopenharmony_ci SLAB_PANIC), 1028c2ecf20Sopenharmony_ci init_once); 1038c2ecf20Sopenharmony_ci pde_opener_cache = 1048c2ecf20Sopenharmony_ci kmem_cache_create("pde_opener", sizeof(struct pde_opener), 0, 1058c2ecf20Sopenharmony_ci SLAB_ACCOUNT|SLAB_PANIC, NULL); 1068c2ecf20Sopenharmony_ci proc_dir_entry_cache = kmem_cache_create_usercopy( 1078c2ecf20Sopenharmony_ci "proc_dir_entry", SIZEOF_PDE, 0, SLAB_PANIC, 1088c2ecf20Sopenharmony_ci offsetof(struct proc_dir_entry, inline_name), 1098c2ecf20Sopenharmony_ci SIZEOF_PDE_INLINE_NAME, NULL); 1108c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct proc_dir_entry) >= SIZEOF_PDE); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_civoid proc_invalidate_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct inode *inode; 1168c2ecf20Sopenharmony_ci struct proc_inode *ei; 1178c2ecf20Sopenharmony_ci struct hlist_node *node; 1188c2ecf20Sopenharmony_ci struct super_block *old_sb = NULL; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci rcu_read_lock(); 1218c2ecf20Sopenharmony_ci for (;;) { 1228c2ecf20Sopenharmony_ci struct super_block *sb; 1238c2ecf20Sopenharmony_ci node = hlist_first_rcu(inodes); 1248c2ecf20Sopenharmony_ci if (!node) 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci ei = hlist_entry(node, struct proc_inode, sibling_inodes); 1278c2ecf20Sopenharmony_ci spin_lock(lock); 1288c2ecf20Sopenharmony_ci hlist_del_init_rcu(&ei->sibling_inodes); 1298c2ecf20Sopenharmony_ci spin_unlock(lock); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci inode = &ei->vfs_inode; 1328c2ecf20Sopenharmony_ci sb = inode->i_sb; 1338c2ecf20Sopenharmony_ci if ((sb != old_sb) && !atomic_inc_not_zero(&sb->s_active)) 1348c2ecf20Sopenharmony_ci continue; 1358c2ecf20Sopenharmony_ci inode = igrab(inode); 1368c2ecf20Sopenharmony_ci rcu_read_unlock(); 1378c2ecf20Sopenharmony_ci if (sb != old_sb) { 1388c2ecf20Sopenharmony_ci if (old_sb) 1398c2ecf20Sopenharmony_ci deactivate_super(old_sb); 1408c2ecf20Sopenharmony_ci old_sb = sb; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci if (unlikely(!inode)) { 1438c2ecf20Sopenharmony_ci rcu_read_lock(); 1448c2ecf20Sopenharmony_ci continue; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (S_ISDIR(inode->i_mode)) { 1488c2ecf20Sopenharmony_ci struct dentry *dir = d_find_any_alias(inode); 1498c2ecf20Sopenharmony_ci if (dir) { 1508c2ecf20Sopenharmony_ci d_invalidate(dir); 1518c2ecf20Sopenharmony_ci dput(dir); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci } else { 1548c2ecf20Sopenharmony_ci struct dentry *dentry; 1558c2ecf20Sopenharmony_ci while ((dentry = d_find_alias(inode))) { 1568c2ecf20Sopenharmony_ci d_invalidate(dentry); 1578c2ecf20Sopenharmony_ci dput(dentry); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci iput(inode); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci rcu_read_lock(); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci rcu_read_unlock(); 1658c2ecf20Sopenharmony_ci if (old_sb) 1668c2ecf20Sopenharmony_ci deactivate_super(old_sb); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic inline const char *hidepid2str(enum proc_hidepid v) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci switch (v) { 1728c2ecf20Sopenharmony_ci case HIDEPID_OFF: return "off"; 1738c2ecf20Sopenharmony_ci case HIDEPID_NO_ACCESS: return "noaccess"; 1748c2ecf20Sopenharmony_ci case HIDEPID_INVISIBLE: return "invisible"; 1758c2ecf20Sopenharmony_ci case HIDEPID_NOT_PTRACEABLE: return "ptraceable"; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci WARN_ONCE(1, "bad hide_pid value: %d\n", v); 1788c2ecf20Sopenharmony_ci return "unknown"; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int proc_show_options(struct seq_file *seq, struct dentry *root) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct proc_fs_info *fs_info = proc_sb_info(root->d_sb); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (!gid_eq(fs_info->pid_gid, GLOBAL_ROOT_GID)) 1868c2ecf20Sopenharmony_ci seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, fs_info->pid_gid)); 1878c2ecf20Sopenharmony_ci if (fs_info->hide_pid != HIDEPID_OFF) 1888c2ecf20Sopenharmony_ci seq_printf(seq, ",hidepid=%s", hidepid2str(fs_info->hide_pid)); 1898c2ecf20Sopenharmony_ci if (fs_info->pidonly != PROC_PIDONLY_OFF) 1908c2ecf20Sopenharmony_ci seq_printf(seq, ",subset=pid"); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ciconst struct super_operations proc_sops = { 1968c2ecf20Sopenharmony_ci .alloc_inode = proc_alloc_inode, 1978c2ecf20Sopenharmony_ci .free_inode = proc_free_inode, 1988c2ecf20Sopenharmony_ci .drop_inode = generic_delete_inode, 1998c2ecf20Sopenharmony_ci .evict_inode = proc_evict_inode, 2008c2ecf20Sopenharmony_ci .statfs = simple_statfs, 2018c2ecf20Sopenharmony_ci .show_options = proc_show_options, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cienum {BIAS = -1U<<31}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic inline int use_pde(struct proc_dir_entry *pde) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci return likely(atomic_inc_unless_negative(&pde->in_use)); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void unuse_pde(struct proc_dir_entry *pde) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci if (unlikely(atomic_dec_return(&pde->in_use) == BIAS)) 2148c2ecf20Sopenharmony_ci complete(pde->pde_unload_completion); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* pde is locked on entry, unlocked on exit */ 2188c2ecf20Sopenharmony_cistatic void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo) 2198c2ecf20Sopenharmony_ci __releases(&pde->pde_unload_lock) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci /* 2228c2ecf20Sopenharmony_ci * close() (proc_reg_release()) can't delete an entry and proceed: 2238c2ecf20Sopenharmony_ci * ->release hook needs to be available at the right moment. 2248c2ecf20Sopenharmony_ci * 2258c2ecf20Sopenharmony_ci * rmmod (remove_proc_entry() et al) can't delete an entry and proceed: 2268c2ecf20Sopenharmony_ci * "struct file" needs to be available at the right moment. 2278c2ecf20Sopenharmony_ci * 2288c2ecf20Sopenharmony_ci * Therefore, first process to enter this function does ->release() and 2298c2ecf20Sopenharmony_ci * signals its completion to the other process which does nothing. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci if (pdeo->closing) { 2328c2ecf20Sopenharmony_ci /* somebody else is doing that, just wait */ 2338c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(c); 2348c2ecf20Sopenharmony_ci pdeo->c = &c; 2358c2ecf20Sopenharmony_ci spin_unlock(&pde->pde_unload_lock); 2368c2ecf20Sopenharmony_ci wait_for_completion(&c); 2378c2ecf20Sopenharmony_ci } else { 2388c2ecf20Sopenharmony_ci struct file *file; 2398c2ecf20Sopenharmony_ci struct completion *c; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci pdeo->closing = true; 2428c2ecf20Sopenharmony_ci spin_unlock(&pde->pde_unload_lock); 2438c2ecf20Sopenharmony_ci file = pdeo->file; 2448c2ecf20Sopenharmony_ci pde->proc_ops->proc_release(file_inode(file), file); 2458c2ecf20Sopenharmony_ci spin_lock(&pde->pde_unload_lock); 2468c2ecf20Sopenharmony_ci /* After ->release. */ 2478c2ecf20Sopenharmony_ci list_del(&pdeo->lh); 2488c2ecf20Sopenharmony_ci c = pdeo->c; 2498c2ecf20Sopenharmony_ci spin_unlock(&pde->pde_unload_lock); 2508c2ecf20Sopenharmony_ci if (unlikely(c)) 2518c2ecf20Sopenharmony_ci complete(c); 2528c2ecf20Sopenharmony_ci kmem_cache_free(pde_opener_cache, pdeo); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_civoid proc_entry_rundown(struct proc_dir_entry *de) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(c); 2598c2ecf20Sopenharmony_ci /* Wait until all existing callers into module are done. */ 2608c2ecf20Sopenharmony_ci de->pde_unload_completion = &c; 2618c2ecf20Sopenharmony_ci if (atomic_add_return(BIAS, &de->in_use) != BIAS) 2628c2ecf20Sopenharmony_ci wait_for_completion(&c); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* ->pde_openers list can't grow from now on. */ 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci spin_lock(&de->pde_unload_lock); 2678c2ecf20Sopenharmony_ci while (!list_empty(&de->pde_openers)) { 2688c2ecf20Sopenharmony_ci struct pde_opener *pdeo; 2698c2ecf20Sopenharmony_ci pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh); 2708c2ecf20Sopenharmony_ci close_pdeo(de, pdeo); 2718c2ecf20Sopenharmony_ci spin_lock(&de->pde_unload_lock); 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci spin_unlock(&de->pde_unload_lock); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic loff_t pde_lseek(struct proc_dir_entry *pde, struct file *file, loff_t offset, int whence) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci typeof_member(struct proc_ops, proc_lseek) lseek; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci lseek = pde->proc_ops->proc_lseek; 2818c2ecf20Sopenharmony_ci if (!lseek) 2828c2ecf20Sopenharmony_ci lseek = default_llseek; 2838c2ecf20Sopenharmony_ci return lseek(file, offset, whence); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 2898c2ecf20Sopenharmony_ci loff_t rv = -EINVAL; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (pde_is_permanent(pde)) { 2928c2ecf20Sopenharmony_ci return pde_lseek(pde, file, offset, whence); 2938c2ecf20Sopenharmony_ci } else if (use_pde(pde)) { 2948c2ecf20Sopenharmony_ci rv = pde_lseek(pde, file, offset, whence); 2958c2ecf20Sopenharmony_ci unuse_pde(pde); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci return rv; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic ssize_t proc_reg_read_iter(struct kiocb *iocb, struct iov_iter *iter) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(iocb->ki_filp)); 3038c2ecf20Sopenharmony_ci ssize_t ret; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (pde_is_permanent(pde)) 3068c2ecf20Sopenharmony_ci return pde->proc_ops->proc_read_iter(iocb, iter); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (!use_pde(pde)) 3098c2ecf20Sopenharmony_ci return -EIO; 3108c2ecf20Sopenharmony_ci ret = pde->proc_ops->proc_read_iter(iocb, iter); 3118c2ecf20Sopenharmony_ci unuse_pde(pde); 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic ssize_t pde_read(struct proc_dir_entry *pde, struct file *file, char __user *buf, size_t count, loff_t *ppos) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci typeof_member(struct proc_ops, proc_read) read; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci read = pde->proc_ops->proc_read; 3208c2ecf20Sopenharmony_ci if (read) 3218c2ecf20Sopenharmony_ci return read(file, buf, count, ppos); 3228c2ecf20Sopenharmony_ci return -EIO; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 3288c2ecf20Sopenharmony_ci ssize_t rv = -EIO; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (pde_is_permanent(pde)) { 3318c2ecf20Sopenharmony_ci return pde_read(pde, file, buf, count, ppos); 3328c2ecf20Sopenharmony_ci } else if (use_pde(pde)) { 3338c2ecf20Sopenharmony_ci rv = pde_read(pde, file, buf, count, ppos); 3348c2ecf20Sopenharmony_ci unuse_pde(pde); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci return rv; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic ssize_t pde_write(struct proc_dir_entry *pde, struct file *file, const char __user *buf, size_t count, loff_t *ppos) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci typeof_member(struct proc_ops, proc_write) write; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci write = pde->proc_ops->proc_write; 3448c2ecf20Sopenharmony_ci if (write) 3458c2ecf20Sopenharmony_ci return write(file, buf, count, ppos); 3468c2ecf20Sopenharmony_ci return -EIO; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 3528c2ecf20Sopenharmony_ci ssize_t rv = -EIO; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (pde_is_permanent(pde)) { 3558c2ecf20Sopenharmony_ci return pde_write(pde, file, buf, count, ppos); 3568c2ecf20Sopenharmony_ci } else if (use_pde(pde)) { 3578c2ecf20Sopenharmony_ci rv = pde_write(pde, file, buf, count, ppos); 3588c2ecf20Sopenharmony_ci unuse_pde(pde); 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci return rv; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic __poll_t pde_poll(struct proc_dir_entry *pde, struct file *file, struct poll_table_struct *pts) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci typeof_member(struct proc_ops, proc_poll) poll; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci poll = pde->proc_ops->proc_poll; 3688c2ecf20Sopenharmony_ci if (poll) 3698c2ecf20Sopenharmony_ci return poll(file, pts); 3708c2ecf20Sopenharmony_ci return DEFAULT_POLLMASK; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic __poll_t proc_reg_poll(struct file *file, struct poll_table_struct *pts) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 3768c2ecf20Sopenharmony_ci __poll_t rv = DEFAULT_POLLMASK; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (pde_is_permanent(pde)) { 3798c2ecf20Sopenharmony_ci return pde_poll(pde, file, pts); 3808c2ecf20Sopenharmony_ci } else if (use_pde(pde)) { 3818c2ecf20Sopenharmony_ci rv = pde_poll(pde, file, pts); 3828c2ecf20Sopenharmony_ci unuse_pde(pde); 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci return rv; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic long pde_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci typeof_member(struct proc_ops, proc_ioctl) ioctl; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci ioctl = pde->proc_ops->proc_ioctl; 3928c2ecf20Sopenharmony_ci if (ioctl) 3938c2ecf20Sopenharmony_ci return ioctl(file, cmd, arg); 3948c2ecf20Sopenharmony_ci return -ENOTTY; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 4008c2ecf20Sopenharmony_ci long rv = -ENOTTY; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (pde_is_permanent(pde)) { 4038c2ecf20Sopenharmony_ci return pde_ioctl(pde, file, cmd, arg); 4048c2ecf20Sopenharmony_ci } else if (use_pde(pde)) { 4058c2ecf20Sopenharmony_ci rv = pde_ioctl(pde, file, cmd, arg); 4068c2ecf20Sopenharmony_ci unuse_pde(pde); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci return rv; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 4128c2ecf20Sopenharmony_cistatic long pde_compat_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci typeof_member(struct proc_ops, proc_compat_ioctl) compat_ioctl; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci compat_ioctl = pde->proc_ops->proc_compat_ioctl; 4178c2ecf20Sopenharmony_ci if (compat_ioctl) 4188c2ecf20Sopenharmony_ci return compat_ioctl(file, cmd, arg); 4198c2ecf20Sopenharmony_ci return -ENOTTY; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 4258c2ecf20Sopenharmony_ci long rv = -ENOTTY; 4268c2ecf20Sopenharmony_ci if (pde_is_permanent(pde)) { 4278c2ecf20Sopenharmony_ci return pde_compat_ioctl(pde, file, cmd, arg); 4288c2ecf20Sopenharmony_ci } else if (use_pde(pde)) { 4298c2ecf20Sopenharmony_ci rv = pde_compat_ioctl(pde, file, cmd, arg); 4308c2ecf20Sopenharmony_ci unuse_pde(pde); 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci return rv; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci#endif 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int pde_mmap(struct proc_dir_entry *pde, struct file *file, struct vm_area_struct *vma) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci typeof_member(struct proc_ops, proc_mmap) mmap; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci mmap = pde->proc_ops->proc_mmap; 4418c2ecf20Sopenharmony_ci if (mmap) 4428c2ecf20Sopenharmony_ci return mmap(file, vma); 4438c2ecf20Sopenharmony_ci return -EIO; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int proc_reg_mmap(struct file *file, struct vm_area_struct *vma) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 4498c2ecf20Sopenharmony_ci int rv = -EIO; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (pde_is_permanent(pde)) { 4528c2ecf20Sopenharmony_ci return pde_mmap(pde, file, vma); 4538c2ecf20Sopenharmony_ci } else if (use_pde(pde)) { 4548c2ecf20Sopenharmony_ci rv = pde_mmap(pde, file, vma); 4558c2ecf20Sopenharmony_ci unuse_pde(pde); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci return rv; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic unsigned long 4618c2ecf20Sopenharmony_cipde_get_unmapped_area(struct proc_dir_entry *pde, struct file *file, unsigned long orig_addr, 4628c2ecf20Sopenharmony_ci unsigned long len, unsigned long pgoff, 4638c2ecf20Sopenharmony_ci unsigned long flags) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci typeof_member(struct proc_ops, proc_get_unmapped_area) get_area; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci get_area = pde->proc_ops->proc_get_unmapped_area; 4688c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 4698c2ecf20Sopenharmony_ci if (!get_area) 4708c2ecf20Sopenharmony_ci get_area = current->mm->get_unmapped_area; 4718c2ecf20Sopenharmony_ci#endif 4728c2ecf20Sopenharmony_ci if (get_area) 4738c2ecf20Sopenharmony_ci return get_area(file, orig_addr, len, pgoff, flags); 4748c2ecf20Sopenharmony_ci return orig_addr; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic unsigned long 4788c2ecf20Sopenharmony_ciproc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr, 4798c2ecf20Sopenharmony_ci unsigned long len, unsigned long pgoff, 4808c2ecf20Sopenharmony_ci unsigned long flags) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(file)); 4838c2ecf20Sopenharmony_ci unsigned long rv = -EIO; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (pde_is_permanent(pde)) { 4868c2ecf20Sopenharmony_ci return pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags); 4878c2ecf20Sopenharmony_ci } else if (use_pde(pde)) { 4888c2ecf20Sopenharmony_ci rv = pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags); 4898c2ecf20Sopenharmony_ci unuse_pde(pde); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci return rv; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic int proc_reg_open(struct inode *inode, struct file *file) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct proc_fs_info *fs_info = proc_sb_info(inode->i_sb); 4978c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(inode); 4988c2ecf20Sopenharmony_ci int rv = 0; 4998c2ecf20Sopenharmony_ci typeof_member(struct proc_ops, proc_open) open; 5008c2ecf20Sopenharmony_ci typeof_member(struct proc_ops, proc_release) release; 5018c2ecf20Sopenharmony_ci struct pde_opener *pdeo; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (pde_is_permanent(pde)) { 5048c2ecf20Sopenharmony_ci open = pde->proc_ops->proc_open; 5058c2ecf20Sopenharmony_ci if (open) 5068c2ecf20Sopenharmony_ci rv = open(inode, file); 5078c2ecf20Sopenharmony_ci return rv; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (fs_info->pidonly == PROC_PIDONLY_ON) 5118c2ecf20Sopenharmony_ci return -ENOENT; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* 5148c2ecf20Sopenharmony_ci * Ensure that 5158c2ecf20Sopenharmony_ci * 1) PDE's ->release hook will be called no matter what 5168c2ecf20Sopenharmony_ci * either normally by close()/->release, or forcefully by 5178c2ecf20Sopenharmony_ci * rmmod/remove_proc_entry. 5188c2ecf20Sopenharmony_ci * 5198c2ecf20Sopenharmony_ci * 2) rmmod isn't blocked by opening file in /proc and sitting on 5208c2ecf20Sopenharmony_ci * the descriptor (including "rmmod foo </proc/foo" scenario). 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * Save every "struct file" with custom ->release hook. 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_ci if (!use_pde(pde)) 5258c2ecf20Sopenharmony_ci return -ENOENT; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci release = pde->proc_ops->proc_release; 5288c2ecf20Sopenharmony_ci if (release) { 5298c2ecf20Sopenharmony_ci pdeo = kmem_cache_alloc(pde_opener_cache, GFP_KERNEL); 5308c2ecf20Sopenharmony_ci if (!pdeo) { 5318c2ecf20Sopenharmony_ci rv = -ENOMEM; 5328c2ecf20Sopenharmony_ci goto out_unuse; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci open = pde->proc_ops->proc_open; 5378c2ecf20Sopenharmony_ci if (open) 5388c2ecf20Sopenharmony_ci rv = open(inode, file); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (release) { 5418c2ecf20Sopenharmony_ci if (rv == 0) { 5428c2ecf20Sopenharmony_ci /* To know what to release. */ 5438c2ecf20Sopenharmony_ci pdeo->file = file; 5448c2ecf20Sopenharmony_ci pdeo->closing = false; 5458c2ecf20Sopenharmony_ci pdeo->c = NULL; 5468c2ecf20Sopenharmony_ci spin_lock(&pde->pde_unload_lock); 5478c2ecf20Sopenharmony_ci list_add(&pdeo->lh, &pde->pde_openers); 5488c2ecf20Sopenharmony_ci spin_unlock(&pde->pde_unload_lock); 5498c2ecf20Sopenharmony_ci } else 5508c2ecf20Sopenharmony_ci kmem_cache_free(pde_opener_cache, pdeo); 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ciout_unuse: 5548c2ecf20Sopenharmony_ci unuse_pde(pde); 5558c2ecf20Sopenharmony_ci return rv; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic int proc_reg_release(struct inode *inode, struct file *file) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(inode); 5618c2ecf20Sopenharmony_ci struct pde_opener *pdeo; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (pde_is_permanent(pde)) { 5648c2ecf20Sopenharmony_ci typeof_member(struct proc_ops, proc_release) release; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci release = pde->proc_ops->proc_release; 5678c2ecf20Sopenharmony_ci if (release) { 5688c2ecf20Sopenharmony_ci return release(inode, file); 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci spin_lock(&pde->pde_unload_lock); 5748c2ecf20Sopenharmony_ci list_for_each_entry(pdeo, &pde->pde_openers, lh) { 5758c2ecf20Sopenharmony_ci if (pdeo->file == file) { 5768c2ecf20Sopenharmony_ci close_pdeo(pde, pdeo); 5778c2ecf20Sopenharmony_ci return 0; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci spin_unlock(&pde->pde_unload_lock); 5818c2ecf20Sopenharmony_ci return 0; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic const struct file_operations proc_reg_file_ops = { 5858c2ecf20Sopenharmony_ci .llseek = proc_reg_llseek, 5868c2ecf20Sopenharmony_ci .read = proc_reg_read, 5878c2ecf20Sopenharmony_ci .write = proc_reg_write, 5888c2ecf20Sopenharmony_ci .poll = proc_reg_poll, 5898c2ecf20Sopenharmony_ci .unlocked_ioctl = proc_reg_unlocked_ioctl, 5908c2ecf20Sopenharmony_ci .mmap = proc_reg_mmap, 5918c2ecf20Sopenharmony_ci .get_unmapped_area = proc_reg_get_unmapped_area, 5928c2ecf20Sopenharmony_ci .open = proc_reg_open, 5938c2ecf20Sopenharmony_ci .release = proc_reg_release, 5948c2ecf20Sopenharmony_ci}; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic const struct file_operations proc_iter_file_ops = { 5978c2ecf20Sopenharmony_ci .llseek = proc_reg_llseek, 5988c2ecf20Sopenharmony_ci .read_iter = proc_reg_read_iter, 5998c2ecf20Sopenharmony_ci .write = proc_reg_write, 6008c2ecf20Sopenharmony_ci .splice_read = generic_file_splice_read, 6018c2ecf20Sopenharmony_ci .poll = proc_reg_poll, 6028c2ecf20Sopenharmony_ci .unlocked_ioctl = proc_reg_unlocked_ioctl, 6038c2ecf20Sopenharmony_ci .mmap = proc_reg_mmap, 6048c2ecf20Sopenharmony_ci .get_unmapped_area = proc_reg_get_unmapped_area, 6058c2ecf20Sopenharmony_ci .open = proc_reg_open, 6068c2ecf20Sopenharmony_ci .release = proc_reg_release, 6078c2ecf20Sopenharmony_ci}; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 6108c2ecf20Sopenharmony_cistatic const struct file_operations proc_reg_file_ops_compat = { 6118c2ecf20Sopenharmony_ci .llseek = proc_reg_llseek, 6128c2ecf20Sopenharmony_ci .read = proc_reg_read, 6138c2ecf20Sopenharmony_ci .write = proc_reg_write, 6148c2ecf20Sopenharmony_ci .poll = proc_reg_poll, 6158c2ecf20Sopenharmony_ci .unlocked_ioctl = proc_reg_unlocked_ioctl, 6168c2ecf20Sopenharmony_ci .compat_ioctl = proc_reg_compat_ioctl, 6178c2ecf20Sopenharmony_ci .mmap = proc_reg_mmap, 6188c2ecf20Sopenharmony_ci .get_unmapped_area = proc_reg_get_unmapped_area, 6198c2ecf20Sopenharmony_ci .open = proc_reg_open, 6208c2ecf20Sopenharmony_ci .release = proc_reg_release, 6218c2ecf20Sopenharmony_ci}; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic const struct file_operations proc_iter_file_ops_compat = { 6248c2ecf20Sopenharmony_ci .llseek = proc_reg_llseek, 6258c2ecf20Sopenharmony_ci .read_iter = proc_reg_read_iter, 6268c2ecf20Sopenharmony_ci .splice_read = generic_file_splice_read, 6278c2ecf20Sopenharmony_ci .write = proc_reg_write, 6288c2ecf20Sopenharmony_ci .poll = proc_reg_poll, 6298c2ecf20Sopenharmony_ci .unlocked_ioctl = proc_reg_unlocked_ioctl, 6308c2ecf20Sopenharmony_ci .compat_ioctl = proc_reg_compat_ioctl, 6318c2ecf20Sopenharmony_ci .mmap = proc_reg_mmap, 6328c2ecf20Sopenharmony_ci .get_unmapped_area = proc_reg_get_unmapped_area, 6338c2ecf20Sopenharmony_ci .open = proc_reg_open, 6348c2ecf20Sopenharmony_ci .release = proc_reg_release, 6358c2ecf20Sopenharmony_ci}; 6368c2ecf20Sopenharmony_ci#endif 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_cistatic void proc_put_link(void *p) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci unuse_pde(p); 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic const char *proc_get_link(struct dentry *dentry, 6448c2ecf20Sopenharmony_ci struct inode *inode, 6458c2ecf20Sopenharmony_ci struct delayed_call *done) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(inode); 6488c2ecf20Sopenharmony_ci if (!use_pde(pde)) 6498c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 6508c2ecf20Sopenharmony_ci set_delayed_call(done, proc_put_link, pde); 6518c2ecf20Sopenharmony_ci return pde->data; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ciconst struct inode_operations proc_link_inode_operations = { 6558c2ecf20Sopenharmony_ci .get_link = proc_get_link, 6568c2ecf20Sopenharmony_ci}; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistruct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct inode *inode = new_inode(sb); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (!inode) { 6638c2ecf20Sopenharmony_ci pde_put(de); 6648c2ecf20Sopenharmony_ci return NULL; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci inode->i_ino = de->low_ino; 6688c2ecf20Sopenharmony_ci inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); 6698c2ecf20Sopenharmony_ci PROC_I(inode)->pde = de; 6708c2ecf20Sopenharmony_ci if (is_empty_pde(de)) { 6718c2ecf20Sopenharmony_ci make_empty_dir_inode(inode); 6728c2ecf20Sopenharmony_ci return inode; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (de->mode) { 6768c2ecf20Sopenharmony_ci inode->i_mode = de->mode; 6778c2ecf20Sopenharmony_ci inode->i_uid = de->uid; 6788c2ecf20Sopenharmony_ci inode->i_gid = de->gid; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci if (de->size) 6818c2ecf20Sopenharmony_ci inode->i_size = de->size; 6828c2ecf20Sopenharmony_ci if (de->nlink) 6838c2ecf20Sopenharmony_ci set_nlink(inode, de->nlink); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (S_ISREG(inode->i_mode)) { 6868c2ecf20Sopenharmony_ci inode->i_op = de->proc_iops; 6878c2ecf20Sopenharmony_ci if (de->proc_ops->proc_read_iter) 6888c2ecf20Sopenharmony_ci inode->i_fop = &proc_iter_file_ops; 6898c2ecf20Sopenharmony_ci else 6908c2ecf20Sopenharmony_ci inode->i_fop = &proc_reg_file_ops; 6918c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 6928c2ecf20Sopenharmony_ci if (de->proc_ops->proc_compat_ioctl) { 6938c2ecf20Sopenharmony_ci if (de->proc_ops->proc_read_iter) 6948c2ecf20Sopenharmony_ci inode->i_fop = &proc_iter_file_ops_compat; 6958c2ecf20Sopenharmony_ci else 6968c2ecf20Sopenharmony_ci inode->i_fop = &proc_reg_file_ops_compat; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci#endif 6998c2ecf20Sopenharmony_ci } else if (S_ISDIR(inode->i_mode)) { 7008c2ecf20Sopenharmony_ci inode->i_op = de->proc_iops; 7018c2ecf20Sopenharmony_ci inode->i_fop = de->proc_dir_ops; 7028c2ecf20Sopenharmony_ci } else if (S_ISLNK(inode->i_mode)) { 7038c2ecf20Sopenharmony_ci inode->i_op = de->proc_iops; 7048c2ecf20Sopenharmony_ci inode->i_fop = NULL; 7058c2ecf20Sopenharmony_ci } else { 7068c2ecf20Sopenharmony_ci BUG(); 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci return inode; 7098c2ecf20Sopenharmony_ci} 710