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