18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * proc/fs/generic.c --- generic routines for the proc-fs
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file contains generic proc-fs routines for handling
68c2ecf20Sopenharmony_ci * directories and files.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds.
98c2ecf20Sopenharmony_ci * Copyright (C) 1997 Theodore Ts'o
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/cache.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/time.h>
158c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
168c2ecf20Sopenharmony_ci#include <linux/stat.h>
178c2ecf20Sopenharmony_ci#include <linux/mm.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/namei.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/printk.h>
228c2ecf20Sopenharmony_ci#include <linux/mount.h>
238c2ecf20Sopenharmony_ci#include <linux/init.h>
248c2ecf20Sopenharmony_ci#include <linux/idr.h>
258c2ecf20Sopenharmony_ci#include <linux/bitops.h>
268c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
278c2ecf20Sopenharmony_ci#include <linux/completion.h>
288c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
298c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include "internal.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(proc_subdir_lock);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct kmem_cache *proc_dir_entry_cache __ro_after_init;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_civoid pde_free(struct proc_dir_entry *pde)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	if (S_ISLNK(pde->mode))
408c2ecf20Sopenharmony_ci		kfree(pde->data);
418c2ecf20Sopenharmony_ci	if (pde->name != pde->inline_name)
428c2ecf20Sopenharmony_ci		kfree(pde->name);
438c2ecf20Sopenharmony_ci	kmem_cache_free(proc_dir_entry_cache, pde);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int proc_match(const char *name, struct proc_dir_entry *de, unsigned int len)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	if (len < de->namelen)
498c2ecf20Sopenharmony_ci		return -1;
508c2ecf20Sopenharmony_ci	if (len > de->namelen)
518c2ecf20Sopenharmony_ci		return 1;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	return memcmp(name, de->name, len);
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic struct proc_dir_entry *pde_subdir_first(struct proc_dir_entry *dir)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	return rb_entry_safe(rb_first(&dir->subdir), struct proc_dir_entry,
598c2ecf20Sopenharmony_ci			     subdir_node);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	return rb_entry_safe(rb_next(&dir->subdir_node), struct proc_dir_entry,
658c2ecf20Sopenharmony_ci			     subdir_node);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir,
698c2ecf20Sopenharmony_ci					      const char *name,
708c2ecf20Sopenharmony_ci					      unsigned int len)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct rb_node *node = dir->subdir.rb_node;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	while (node) {
758c2ecf20Sopenharmony_ci		struct proc_dir_entry *de = rb_entry(node,
768c2ecf20Sopenharmony_ci						     struct proc_dir_entry,
778c2ecf20Sopenharmony_ci						     subdir_node);
788c2ecf20Sopenharmony_ci		int result = proc_match(name, de, len);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		if (result < 0)
818c2ecf20Sopenharmony_ci			node = node->rb_left;
828c2ecf20Sopenharmony_ci		else if (result > 0)
838c2ecf20Sopenharmony_ci			node = node->rb_right;
848c2ecf20Sopenharmony_ci		else
858c2ecf20Sopenharmony_ci			return de;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	return NULL;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic bool pde_subdir_insert(struct proc_dir_entry *dir,
918c2ecf20Sopenharmony_ci			      struct proc_dir_entry *de)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct rb_root *root = &dir->subdir;
948c2ecf20Sopenharmony_ci	struct rb_node **new = &root->rb_node, *parent = NULL;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/* Figure out where to put new node */
978c2ecf20Sopenharmony_ci	while (*new) {
988c2ecf20Sopenharmony_ci		struct proc_dir_entry *this = rb_entry(*new,
998c2ecf20Sopenharmony_ci						       struct proc_dir_entry,
1008c2ecf20Sopenharmony_ci						       subdir_node);
1018c2ecf20Sopenharmony_ci		int result = proc_match(de->name, this, de->namelen);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		parent = *new;
1048c2ecf20Sopenharmony_ci		if (result < 0)
1058c2ecf20Sopenharmony_ci			new = &(*new)->rb_left;
1068c2ecf20Sopenharmony_ci		else if (result > 0)
1078c2ecf20Sopenharmony_ci			new = &(*new)->rb_right;
1088c2ecf20Sopenharmony_ci		else
1098c2ecf20Sopenharmony_ci			return false;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* Add new node and rebalance tree. */
1138c2ecf20Sopenharmony_ci	rb_link_node(&de->subdir_node, parent, new);
1148c2ecf20Sopenharmony_ci	rb_insert_color(&de->subdir_node, root);
1158c2ecf20Sopenharmony_ci	return true;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(dentry);
1218c2ecf20Sopenharmony_ci	struct proc_dir_entry *de = PDE(inode);
1228c2ecf20Sopenharmony_ci	int error;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	error = setattr_prepare(dentry, iattr);
1258c2ecf20Sopenharmony_ci	if (error)
1268c2ecf20Sopenharmony_ci		return error;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	setattr_copy(inode, iattr);
1298c2ecf20Sopenharmony_ci	mark_inode_dirty(inode);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	proc_set_user(de, inode->i_uid, inode->i_gid);
1328c2ecf20Sopenharmony_ci	de->mode = inode->i_mode;
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic int proc_getattr(const struct path *path, struct kstat *stat,
1378c2ecf20Sopenharmony_ci			u32 request_mask, unsigned int query_flags)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(path->dentry);
1408c2ecf20Sopenharmony_ci	struct proc_dir_entry *de = PDE(inode);
1418c2ecf20Sopenharmony_ci	if (de) {
1428c2ecf20Sopenharmony_ci		nlink_t nlink = READ_ONCE(de->nlink);
1438c2ecf20Sopenharmony_ci		if (nlink > 0) {
1448c2ecf20Sopenharmony_ci			set_nlink(inode, nlink);
1458c2ecf20Sopenharmony_ci		}
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	generic_fillattr(inode, stat);
1498c2ecf20Sopenharmony_ci	return 0;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic const struct inode_operations proc_file_inode_operations = {
1538c2ecf20Sopenharmony_ci	.setattr	= proc_notify_change,
1548c2ecf20Sopenharmony_ci};
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/*
1578c2ecf20Sopenharmony_ci * This function parses a name such as "tty/driver/serial", and
1588c2ecf20Sopenharmony_ci * returns the struct proc_dir_entry for "/proc/tty/driver", and
1598c2ecf20Sopenharmony_ci * returns "serial" in residual.
1608c2ecf20Sopenharmony_ci */
1618c2ecf20Sopenharmony_cistatic int __xlate_proc_name(const char *name, struct proc_dir_entry **ret,
1628c2ecf20Sopenharmony_ci			     const char **residual)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	const char     		*cp = name, *next;
1658c2ecf20Sopenharmony_ci	struct proc_dir_entry	*de;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	de = *ret;
1688c2ecf20Sopenharmony_ci	if (!de)
1698c2ecf20Sopenharmony_ci		de = &proc_root;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	while (1) {
1728c2ecf20Sopenharmony_ci		next = strchr(cp, '/');
1738c2ecf20Sopenharmony_ci		if (!next)
1748c2ecf20Sopenharmony_ci			break;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		de = pde_subdir_find(de, cp, next - cp);
1778c2ecf20Sopenharmony_ci		if (!de) {
1788c2ecf20Sopenharmony_ci			WARN(1, "name '%s'\n", name);
1798c2ecf20Sopenharmony_ci			return -ENOENT;
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci		cp = next + 1;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci	*residual = cp;
1848c2ecf20Sopenharmony_ci	*ret = de;
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int xlate_proc_name(const char *name, struct proc_dir_entry **ret,
1898c2ecf20Sopenharmony_ci			   const char **residual)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	int rv;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	read_lock(&proc_subdir_lock);
1948c2ecf20Sopenharmony_ci	rv = __xlate_proc_name(name, ret, residual);
1958c2ecf20Sopenharmony_ci	read_unlock(&proc_subdir_lock);
1968c2ecf20Sopenharmony_ci	return rv;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic DEFINE_IDA(proc_inum_ida);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci#define PROC_DYNAMIC_FIRST 0xF0000000U
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/*
2048c2ecf20Sopenharmony_ci * Return an inode number between PROC_DYNAMIC_FIRST and
2058c2ecf20Sopenharmony_ci * 0xffffffff, or zero on failure.
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_ciint proc_alloc_inum(unsigned int *inum)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	int i;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1,
2128c2ecf20Sopenharmony_ci			   GFP_KERNEL);
2138c2ecf20Sopenharmony_ci	if (i < 0)
2148c2ecf20Sopenharmony_ci		return i;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	*inum = PROC_DYNAMIC_FIRST + (unsigned int)i;
2178c2ecf20Sopenharmony_ci	return 0;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_civoid proc_free_inum(unsigned int inum)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	ida_simple_remove(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int proc_misc_d_revalidate(struct dentry *dentry, unsigned int flags)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	if (flags & LOOKUP_RCU)
2288c2ecf20Sopenharmony_ci		return -ECHILD;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (atomic_read(&PDE(d_inode(dentry))->in_use) < 0)
2318c2ecf20Sopenharmony_ci		return 0; /* revalidate */
2328c2ecf20Sopenharmony_ci	return 1;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int proc_misc_d_delete(const struct dentry *dentry)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	return atomic_read(&PDE(d_inode(dentry))->in_use) < 0;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic const struct dentry_operations proc_misc_dentry_ops = {
2418c2ecf20Sopenharmony_ci	.d_revalidate	= proc_misc_d_revalidate,
2428c2ecf20Sopenharmony_ci	.d_delete	= proc_misc_d_delete,
2438c2ecf20Sopenharmony_ci};
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci/*
2468c2ecf20Sopenharmony_ci * Don't create negative dentries here, return -ENOENT by hand
2478c2ecf20Sopenharmony_ci * instead.
2488c2ecf20Sopenharmony_ci */
2498c2ecf20Sopenharmony_cistruct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,
2508c2ecf20Sopenharmony_ci			      struct proc_dir_entry *de)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct inode *inode;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	read_lock(&proc_subdir_lock);
2558c2ecf20Sopenharmony_ci	de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len);
2568c2ecf20Sopenharmony_ci	if (de) {
2578c2ecf20Sopenharmony_ci		pde_get(de);
2588c2ecf20Sopenharmony_ci		read_unlock(&proc_subdir_lock);
2598c2ecf20Sopenharmony_ci		inode = proc_get_inode(dir->i_sb, de);
2608c2ecf20Sopenharmony_ci		if (!inode)
2618c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
2628c2ecf20Sopenharmony_ci		d_set_d_op(dentry, de->proc_dops);
2638c2ecf20Sopenharmony_ci		return d_splice_alias(inode, dentry);
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci	read_unlock(&proc_subdir_lock);
2668c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOENT);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistruct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
2708c2ecf20Sopenharmony_ci		unsigned int flags)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct proc_fs_info *fs_info = proc_sb_info(dir->i_sb);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (fs_info->pidonly == PROC_PIDONLY_ON)
2758c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOENT);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	return proc_lookup_de(dir, dentry, PDE(dir));
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/*
2818c2ecf20Sopenharmony_ci * This returns non-zero if at EOF, so that the /proc
2828c2ecf20Sopenharmony_ci * root directory can use this and check if it should
2838c2ecf20Sopenharmony_ci * continue with the <pid> entries..
2848c2ecf20Sopenharmony_ci *
2858c2ecf20Sopenharmony_ci * Note that the VFS-layer doesn't care about the return
2868c2ecf20Sopenharmony_ci * value of the readdir() call, as long as it's non-negative
2878c2ecf20Sopenharmony_ci * for success..
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_ciint proc_readdir_de(struct file *file, struct dir_context *ctx,
2908c2ecf20Sopenharmony_ci		    struct proc_dir_entry *de)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	int i;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (!dir_emit_dots(file, ctx))
2958c2ecf20Sopenharmony_ci		return 0;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	i = ctx->pos - 2;
2988c2ecf20Sopenharmony_ci	read_lock(&proc_subdir_lock);
2998c2ecf20Sopenharmony_ci	de = pde_subdir_first(de);
3008c2ecf20Sopenharmony_ci	for (;;) {
3018c2ecf20Sopenharmony_ci		if (!de) {
3028c2ecf20Sopenharmony_ci			read_unlock(&proc_subdir_lock);
3038c2ecf20Sopenharmony_ci			return 0;
3048c2ecf20Sopenharmony_ci		}
3058c2ecf20Sopenharmony_ci		if (!i)
3068c2ecf20Sopenharmony_ci			break;
3078c2ecf20Sopenharmony_ci		de = pde_subdir_next(de);
3088c2ecf20Sopenharmony_ci		i--;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	do {
3128c2ecf20Sopenharmony_ci		struct proc_dir_entry *next;
3138c2ecf20Sopenharmony_ci		pde_get(de);
3148c2ecf20Sopenharmony_ci		read_unlock(&proc_subdir_lock);
3158c2ecf20Sopenharmony_ci		if (!dir_emit(ctx, de->name, de->namelen,
3168c2ecf20Sopenharmony_ci			    de->low_ino, de->mode >> 12)) {
3178c2ecf20Sopenharmony_ci			pde_put(de);
3188c2ecf20Sopenharmony_ci			return 0;
3198c2ecf20Sopenharmony_ci		}
3208c2ecf20Sopenharmony_ci		ctx->pos++;
3218c2ecf20Sopenharmony_ci		read_lock(&proc_subdir_lock);
3228c2ecf20Sopenharmony_ci		next = pde_subdir_next(de);
3238c2ecf20Sopenharmony_ci		pde_put(de);
3248c2ecf20Sopenharmony_ci		de = next;
3258c2ecf20Sopenharmony_ci	} while (de);
3268c2ecf20Sopenharmony_ci	read_unlock(&proc_subdir_lock);
3278c2ecf20Sopenharmony_ci	return 1;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ciint proc_readdir(struct file *file, struct dir_context *ctx)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
3338c2ecf20Sopenharmony_ci	struct proc_fs_info *fs_info = proc_sb_info(inode->i_sb);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (fs_info->pidonly == PROC_PIDONLY_ON)
3368c2ecf20Sopenharmony_ci		return 1;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return proc_readdir_de(file, ctx, PDE(inode));
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci/*
3428c2ecf20Sopenharmony_ci * These are the generic /proc directory operations. They
3438c2ecf20Sopenharmony_ci * use the in-memory "struct proc_dir_entry" tree to parse
3448c2ecf20Sopenharmony_ci * the /proc directory.
3458c2ecf20Sopenharmony_ci */
3468c2ecf20Sopenharmony_cistatic const struct file_operations proc_dir_operations = {
3478c2ecf20Sopenharmony_ci	.llseek			= generic_file_llseek,
3488c2ecf20Sopenharmony_ci	.read			= generic_read_dir,
3498c2ecf20Sopenharmony_ci	.iterate_shared		= proc_readdir,
3508c2ecf20Sopenharmony_ci};
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	return 0;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ciconst struct dentry_operations proc_net_dentry_ops = {
3588c2ecf20Sopenharmony_ci	.d_revalidate	= proc_net_d_revalidate,
3598c2ecf20Sopenharmony_ci	.d_delete	= always_delete_dentry,
3608c2ecf20Sopenharmony_ci};
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci/*
3638c2ecf20Sopenharmony_ci * proc directories can do almost nothing..
3648c2ecf20Sopenharmony_ci */
3658c2ecf20Sopenharmony_cistatic const struct inode_operations proc_dir_inode_operations = {
3668c2ecf20Sopenharmony_ci	.lookup		= proc_lookup,
3678c2ecf20Sopenharmony_ci	.getattr	= proc_getattr,
3688c2ecf20Sopenharmony_ci	.setattr	= proc_notify_change,
3698c2ecf20Sopenharmony_ci};
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci/* returns the registered entry, or frees dp and returns NULL on failure */
3728c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
3738c2ecf20Sopenharmony_ci		struct proc_dir_entry *dp)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	if (proc_alloc_inum(&dp->low_ino))
3768c2ecf20Sopenharmony_ci		goto out_free_entry;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	write_lock(&proc_subdir_lock);
3798c2ecf20Sopenharmony_ci	dp->parent = dir;
3808c2ecf20Sopenharmony_ci	if (pde_subdir_insert(dir, dp) == false) {
3818c2ecf20Sopenharmony_ci		WARN(1, "proc_dir_entry '%s/%s' already registered\n",
3828c2ecf20Sopenharmony_ci		     dir->name, dp->name);
3838c2ecf20Sopenharmony_ci		write_unlock(&proc_subdir_lock);
3848c2ecf20Sopenharmony_ci		goto out_free_inum;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci	dir->nlink++;
3878c2ecf20Sopenharmony_ci	write_unlock(&proc_subdir_lock);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	return dp;
3908c2ecf20Sopenharmony_ciout_free_inum:
3918c2ecf20Sopenharmony_ci	proc_free_inum(dp->low_ino);
3928c2ecf20Sopenharmony_ciout_free_entry:
3938c2ecf20Sopenharmony_ci	pde_free(dp);
3948c2ecf20Sopenharmony_ci	return NULL;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
3988c2ecf20Sopenharmony_ci					  const char *name,
3998c2ecf20Sopenharmony_ci					  umode_t mode,
4008c2ecf20Sopenharmony_ci					  nlink_t nlink)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	struct proc_dir_entry *ent = NULL;
4038c2ecf20Sopenharmony_ci	const char *fn;
4048c2ecf20Sopenharmony_ci	struct qstr qstr;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	if (xlate_proc_name(name, parent, &fn) != 0)
4078c2ecf20Sopenharmony_ci		goto out;
4088c2ecf20Sopenharmony_ci	qstr.name = fn;
4098c2ecf20Sopenharmony_ci	qstr.len = strlen(fn);
4108c2ecf20Sopenharmony_ci	if (qstr.len == 0 || qstr.len >= 256) {
4118c2ecf20Sopenharmony_ci		WARN(1, "name len %u\n", qstr.len);
4128c2ecf20Sopenharmony_ci		return NULL;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci	if (qstr.len == 1 && fn[0] == '.') {
4158c2ecf20Sopenharmony_ci		WARN(1, "name '.'\n");
4168c2ecf20Sopenharmony_ci		return NULL;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci	if (qstr.len == 2 && fn[0] == '.' && fn[1] == '.') {
4198c2ecf20Sopenharmony_ci		WARN(1, "name '..'\n");
4208c2ecf20Sopenharmony_ci		return NULL;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci	if (*parent == &proc_root && name_to_int(&qstr) != ~0U) {
4238c2ecf20Sopenharmony_ci		WARN(1, "create '/proc/%s' by hand\n", qstr.name);
4248c2ecf20Sopenharmony_ci		return NULL;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci	if (is_empty_pde(*parent)) {
4278c2ecf20Sopenharmony_ci		WARN(1, "attempt to add to permanently empty directory");
4288c2ecf20Sopenharmony_ci		return NULL;
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	ent = kmem_cache_zalloc(proc_dir_entry_cache, GFP_KERNEL);
4328c2ecf20Sopenharmony_ci	if (!ent)
4338c2ecf20Sopenharmony_ci		goto out;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (qstr.len + 1 <= SIZEOF_PDE_INLINE_NAME) {
4368c2ecf20Sopenharmony_ci		ent->name = ent->inline_name;
4378c2ecf20Sopenharmony_ci	} else {
4388c2ecf20Sopenharmony_ci		ent->name = kmalloc(qstr.len + 1, GFP_KERNEL);
4398c2ecf20Sopenharmony_ci		if (!ent->name) {
4408c2ecf20Sopenharmony_ci			pde_free(ent);
4418c2ecf20Sopenharmony_ci			return NULL;
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	memcpy(ent->name, fn, qstr.len + 1);
4468c2ecf20Sopenharmony_ci	ent->namelen = qstr.len;
4478c2ecf20Sopenharmony_ci	ent->mode = mode;
4488c2ecf20Sopenharmony_ci	ent->nlink = nlink;
4498c2ecf20Sopenharmony_ci	ent->subdir = RB_ROOT;
4508c2ecf20Sopenharmony_ci	refcount_set(&ent->refcnt, 1);
4518c2ecf20Sopenharmony_ci	spin_lock_init(&ent->pde_unload_lock);
4528c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ent->pde_openers);
4538c2ecf20Sopenharmony_ci	proc_set_user(ent, (*parent)->uid, (*parent)->gid);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	ent->proc_dops = &proc_misc_dentry_ops;
4568c2ecf20Sopenharmony_ci	/* Revalidate everything under /proc/${pid}/net */
4578c2ecf20Sopenharmony_ci	if ((*parent)->proc_dops == &proc_net_dentry_ops)
4588c2ecf20Sopenharmony_ci		pde_force_lookup(ent);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ciout:
4618c2ecf20Sopenharmony_ci	return ent;
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_symlink(const char *name,
4658c2ecf20Sopenharmony_ci		struct proc_dir_entry *parent, const char *dest)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct proc_dir_entry *ent;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	ent = __proc_create(&parent, name,
4708c2ecf20Sopenharmony_ci			  (S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO),1);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (ent) {
4738c2ecf20Sopenharmony_ci		ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL);
4748c2ecf20Sopenharmony_ci		if (ent->data) {
4758c2ecf20Sopenharmony_ci			strcpy((char*)ent->data,dest);
4768c2ecf20Sopenharmony_ci			ent->proc_iops = &proc_link_inode_operations;
4778c2ecf20Sopenharmony_ci			ent = proc_register(parent, ent);
4788c2ecf20Sopenharmony_ci		} else {
4798c2ecf20Sopenharmony_ci			pde_free(ent);
4808c2ecf20Sopenharmony_ci			ent = NULL;
4818c2ecf20Sopenharmony_ci		}
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci	return ent;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_symlink);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistruct proc_dir_entry *_proc_mkdir(const char *name, umode_t mode,
4888c2ecf20Sopenharmony_ci		struct proc_dir_entry *parent, void *data, bool force_lookup)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct proc_dir_entry *ent;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (mode == 0)
4938c2ecf20Sopenharmony_ci		mode = S_IRUGO | S_IXUGO;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
4968c2ecf20Sopenharmony_ci	if (ent) {
4978c2ecf20Sopenharmony_ci		ent->data = data;
4988c2ecf20Sopenharmony_ci		ent->proc_dir_ops = &proc_dir_operations;
4998c2ecf20Sopenharmony_ci		ent->proc_iops = &proc_dir_inode_operations;
5008c2ecf20Sopenharmony_ci		if (force_lookup) {
5018c2ecf20Sopenharmony_ci			pde_force_lookup(ent);
5028c2ecf20Sopenharmony_ci		}
5038c2ecf20Sopenharmony_ci		ent = proc_register(parent, ent);
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci	return ent;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(_proc_mkdir);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
5108c2ecf20Sopenharmony_ci		struct proc_dir_entry *parent, void *data)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	return _proc_mkdir(name, mode, parent, data, false);
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(proc_mkdir_data);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
5178c2ecf20Sopenharmony_ci				       struct proc_dir_entry *parent)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	return proc_mkdir_data(name, mode, parent, NULL);
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_mkdir_mode);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_mkdir(const char *name,
5248c2ecf20Sopenharmony_ci		struct proc_dir_entry *parent)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	return proc_mkdir_data(name, 0, parent, NULL);
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_mkdir);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create_mount_point(const char *name)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	umode_t mode = S_IFDIR | S_IRUGO | S_IXUGO;
5338c2ecf20Sopenharmony_ci	struct proc_dir_entry *ent, *parent = NULL;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	ent = __proc_create(&parent, name, mode, 2);
5368c2ecf20Sopenharmony_ci	if (ent) {
5378c2ecf20Sopenharmony_ci		ent->data = NULL;
5388c2ecf20Sopenharmony_ci		ent->proc_dir_ops = NULL;
5398c2ecf20Sopenharmony_ci		ent->proc_iops = NULL;
5408c2ecf20Sopenharmony_ci		ent = proc_register(parent, ent);
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci	return ent;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_create_mount_point);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create_reg(const char *name, umode_t mode,
5478c2ecf20Sopenharmony_ci		struct proc_dir_entry **parent, void *data)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct proc_dir_entry *p;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	if ((mode & S_IFMT) == 0)
5528c2ecf20Sopenharmony_ci		mode |= S_IFREG;
5538c2ecf20Sopenharmony_ci	if ((mode & S_IALLUGO) == 0)
5548c2ecf20Sopenharmony_ci		mode |= S_IRUGO;
5558c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!S_ISREG(mode)))
5568c2ecf20Sopenharmony_ci		return NULL;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	p = __proc_create(parent, name, mode, 1);
5598c2ecf20Sopenharmony_ci	if (p) {
5608c2ecf20Sopenharmony_ci		p->proc_iops = &proc_file_inode_operations;
5618c2ecf20Sopenharmony_ci		p->data = data;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci	return p;
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic inline void pde_set_flags(struct proc_dir_entry *pde)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
5698c2ecf20Sopenharmony_ci		pde->flags |= PROC_ENTRY_PERMANENT;
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
5738c2ecf20Sopenharmony_ci		struct proc_dir_entry *parent,
5748c2ecf20Sopenharmony_ci		const struct proc_ops *proc_ops, void *data)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct proc_dir_entry *p;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	p = proc_create_reg(name, mode, &parent, data);
5798c2ecf20Sopenharmony_ci	if (!p)
5808c2ecf20Sopenharmony_ci		return NULL;
5818c2ecf20Sopenharmony_ci	p->proc_ops = proc_ops;
5828c2ecf20Sopenharmony_ci	pde_set_flags(p);
5838c2ecf20Sopenharmony_ci	return proc_register(parent, p);
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_create_data);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create(const char *name, umode_t mode,
5888c2ecf20Sopenharmony_ci				   struct proc_dir_entry *parent,
5898c2ecf20Sopenharmony_ci				   const struct proc_ops *proc_ops)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	return proc_create_data(name, mode, parent, proc_ops, NULL);
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_create);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cistatic int proc_seq_open(struct inode *inode, struct file *file)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	struct proc_dir_entry *de = PDE(inode);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	if (de->state_size)
6008c2ecf20Sopenharmony_ci		return seq_open_private(file, de->seq_ops, de->state_size);
6018c2ecf20Sopenharmony_ci	return seq_open(file, de->seq_ops);
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic int proc_seq_release(struct inode *inode, struct file *file)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct proc_dir_entry *de = PDE(inode);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	if (de->state_size)
6098c2ecf20Sopenharmony_ci		return seq_release_private(inode, file);
6108c2ecf20Sopenharmony_ci	return seq_release(inode, file);
6118c2ecf20Sopenharmony_ci}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_cistatic const struct proc_ops proc_seq_ops = {
6148c2ecf20Sopenharmony_ci	/* not permanent -- can call into arbitrary seq_operations */
6158c2ecf20Sopenharmony_ci	.proc_open	= proc_seq_open,
6168c2ecf20Sopenharmony_ci	.proc_read_iter	= seq_read_iter,
6178c2ecf20Sopenharmony_ci	.proc_lseek	= seq_lseek,
6188c2ecf20Sopenharmony_ci	.proc_release	= proc_seq_release,
6198c2ecf20Sopenharmony_ci};
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode,
6228c2ecf20Sopenharmony_ci		struct proc_dir_entry *parent, const struct seq_operations *ops,
6238c2ecf20Sopenharmony_ci		unsigned int state_size, void *data)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	struct proc_dir_entry *p;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	p = proc_create_reg(name, mode, &parent, data);
6288c2ecf20Sopenharmony_ci	if (!p)
6298c2ecf20Sopenharmony_ci		return NULL;
6308c2ecf20Sopenharmony_ci	p->proc_ops = &proc_seq_ops;
6318c2ecf20Sopenharmony_ci	p->seq_ops = ops;
6328c2ecf20Sopenharmony_ci	p->state_size = state_size;
6338c2ecf20Sopenharmony_ci	return proc_register(parent, p);
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_create_seq_private);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_cistatic int proc_single_open(struct inode *inode, struct file *file)
6388c2ecf20Sopenharmony_ci{
6398c2ecf20Sopenharmony_ci	struct proc_dir_entry *de = PDE(inode);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	return single_open(file, de->single_show, de->data);
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic const struct proc_ops proc_single_ops = {
6458c2ecf20Sopenharmony_ci	/* not permanent -- can call into arbitrary ->single_show */
6468c2ecf20Sopenharmony_ci	.proc_open	= proc_single_open,
6478c2ecf20Sopenharmony_ci	.proc_read_iter = seq_read_iter,
6488c2ecf20Sopenharmony_ci	.proc_lseek	= seq_lseek,
6498c2ecf20Sopenharmony_ci	.proc_release	= single_release,
6508c2ecf20Sopenharmony_ci};
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,
6538c2ecf20Sopenharmony_ci		struct proc_dir_entry *parent,
6548c2ecf20Sopenharmony_ci		int (*show)(struct seq_file *, void *), void *data)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	struct proc_dir_entry *p;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	p = proc_create_reg(name, mode, &parent, data);
6598c2ecf20Sopenharmony_ci	if (!p)
6608c2ecf20Sopenharmony_ci		return NULL;
6618c2ecf20Sopenharmony_ci	p->proc_ops = &proc_single_ops;
6628c2ecf20Sopenharmony_ci	p->single_show = show;
6638c2ecf20Sopenharmony_ci	return proc_register(parent, p);
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_create_single_data);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_civoid proc_set_size(struct proc_dir_entry *de, loff_t size)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	de->size = size;
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_set_size);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_civoid proc_set_user(struct proc_dir_entry *de, kuid_t uid, kgid_t gid)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	de->uid = uid;
6768c2ecf20Sopenharmony_ci	de->gid = gid;
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_set_user);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_civoid pde_put(struct proc_dir_entry *pde)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&pde->refcnt)) {
6838c2ecf20Sopenharmony_ci		proc_free_inum(pde->low_ino);
6848c2ecf20Sopenharmony_ci		pde_free(pde);
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci/*
6898c2ecf20Sopenharmony_ci * Remove a /proc entry and free it if it's not currently in use.
6908c2ecf20Sopenharmony_ci */
6918c2ecf20Sopenharmony_civoid remove_proc_entry(const char *name, struct proc_dir_entry *parent)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	struct proc_dir_entry *de = NULL;
6948c2ecf20Sopenharmony_ci	const char *fn = name;
6958c2ecf20Sopenharmony_ci	unsigned int len;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	write_lock(&proc_subdir_lock);
6988c2ecf20Sopenharmony_ci	if (__xlate_proc_name(name, &parent, &fn) != 0) {
6998c2ecf20Sopenharmony_ci		write_unlock(&proc_subdir_lock);
7008c2ecf20Sopenharmony_ci		return;
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci	len = strlen(fn);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	de = pde_subdir_find(parent, fn, len);
7058c2ecf20Sopenharmony_ci	if (de) {
7068c2ecf20Sopenharmony_ci		if (unlikely(pde_is_permanent(de))) {
7078c2ecf20Sopenharmony_ci			WARN(1, "removing permanent /proc entry '%s'", de->name);
7088c2ecf20Sopenharmony_ci			de = NULL;
7098c2ecf20Sopenharmony_ci		} else {
7108c2ecf20Sopenharmony_ci			rb_erase(&de->subdir_node, &parent->subdir);
7118c2ecf20Sopenharmony_ci			if (S_ISDIR(de->mode))
7128c2ecf20Sopenharmony_ci				parent->nlink--;
7138c2ecf20Sopenharmony_ci		}
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci	write_unlock(&proc_subdir_lock);
7168c2ecf20Sopenharmony_ci	if (!de) {
7178c2ecf20Sopenharmony_ci		WARN(1, "name '%s'\n", name);
7188c2ecf20Sopenharmony_ci		return;
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	proc_entry_rundown(de);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	WARN(pde_subdir_first(de),
7248c2ecf20Sopenharmony_ci	     "%s: removing non-empty directory '%s/%s', leaking at least '%s'\n",
7258c2ecf20Sopenharmony_ci	     __func__, de->parent->name, de->name, pde_subdir_first(de)->name);
7268c2ecf20Sopenharmony_ci	pde_put(de);
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(remove_proc_entry);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ciint remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	struct proc_dir_entry *root = NULL, *de, *next;
7338c2ecf20Sopenharmony_ci	const char *fn = name;
7348c2ecf20Sopenharmony_ci	unsigned int len;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	write_lock(&proc_subdir_lock);
7378c2ecf20Sopenharmony_ci	if (__xlate_proc_name(name, &parent, &fn) != 0) {
7388c2ecf20Sopenharmony_ci		write_unlock(&proc_subdir_lock);
7398c2ecf20Sopenharmony_ci		return -ENOENT;
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci	len = strlen(fn);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	root = pde_subdir_find(parent, fn, len);
7448c2ecf20Sopenharmony_ci	if (!root) {
7458c2ecf20Sopenharmony_ci		write_unlock(&proc_subdir_lock);
7468c2ecf20Sopenharmony_ci		return -ENOENT;
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci	if (unlikely(pde_is_permanent(root))) {
7498c2ecf20Sopenharmony_ci		write_unlock(&proc_subdir_lock);
7508c2ecf20Sopenharmony_ci		WARN(1, "removing permanent /proc entry '%s/%s'",
7518c2ecf20Sopenharmony_ci			root->parent->name, root->name);
7528c2ecf20Sopenharmony_ci		return -EINVAL;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci	rb_erase(&root->subdir_node, &parent->subdir);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	de = root;
7578c2ecf20Sopenharmony_ci	while (1) {
7588c2ecf20Sopenharmony_ci		next = pde_subdir_first(de);
7598c2ecf20Sopenharmony_ci		if (next) {
7608c2ecf20Sopenharmony_ci			if (unlikely(pde_is_permanent(next))) {
7618c2ecf20Sopenharmony_ci				write_unlock(&proc_subdir_lock);
7628c2ecf20Sopenharmony_ci				WARN(1, "removing permanent /proc entry '%s/%s'",
7638c2ecf20Sopenharmony_ci					next->parent->name, next->name);
7648c2ecf20Sopenharmony_ci				return -EINVAL;
7658c2ecf20Sopenharmony_ci			}
7668c2ecf20Sopenharmony_ci			rb_erase(&next->subdir_node, &de->subdir);
7678c2ecf20Sopenharmony_ci			de = next;
7688c2ecf20Sopenharmony_ci			continue;
7698c2ecf20Sopenharmony_ci		}
7708c2ecf20Sopenharmony_ci		next = de->parent;
7718c2ecf20Sopenharmony_ci		if (S_ISDIR(de->mode))
7728c2ecf20Sopenharmony_ci			next->nlink--;
7738c2ecf20Sopenharmony_ci		write_unlock(&proc_subdir_lock);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci		proc_entry_rundown(de);
7768c2ecf20Sopenharmony_ci		if (de == root)
7778c2ecf20Sopenharmony_ci			break;
7788c2ecf20Sopenharmony_ci		pde_put(de);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci		write_lock(&proc_subdir_lock);
7818c2ecf20Sopenharmony_ci		de = next;
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci	pde_put(root);
7848c2ecf20Sopenharmony_ci	return 0;
7858c2ecf20Sopenharmony_ci}
7868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(remove_proc_subtree);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_civoid *proc_get_parent_data(const struct inode *inode)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	struct proc_dir_entry *de = PDE(inode);
7918c2ecf20Sopenharmony_ci	return de->parent->data;
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(proc_get_parent_data);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_civoid proc_remove(struct proc_dir_entry *de)
7968c2ecf20Sopenharmony_ci{
7978c2ecf20Sopenharmony_ci	if (de)
7988c2ecf20Sopenharmony_ci		remove_proc_subtree(de->name, de->parent);
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_remove);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_civoid *PDE_DATA(const struct inode *inode)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	return __PDE_DATA(inode);
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(PDE_DATA);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci/*
8098c2ecf20Sopenharmony_ci * Pull a user buffer into memory and pass it to the file's write handler if
8108c2ecf20Sopenharmony_ci * one is supplied.  The ->write() method is permitted to modify the
8118c2ecf20Sopenharmony_ci * kernel-side buffer.
8128c2ecf20Sopenharmony_ci */
8138c2ecf20Sopenharmony_cissize_t proc_simple_write(struct file *f, const char __user *ubuf, size_t size,
8148c2ecf20Sopenharmony_ci			  loff_t *_pos)
8158c2ecf20Sopenharmony_ci{
8168c2ecf20Sopenharmony_ci	struct proc_dir_entry *pde = PDE(file_inode(f));
8178c2ecf20Sopenharmony_ci	char *buf;
8188c2ecf20Sopenharmony_ci	int ret;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	if (!pde->write)
8218c2ecf20Sopenharmony_ci		return -EACCES;
8228c2ecf20Sopenharmony_ci	if (size == 0 || size > PAGE_SIZE - 1)
8238c2ecf20Sopenharmony_ci		return -EINVAL;
8248c2ecf20Sopenharmony_ci	buf = memdup_user_nul(ubuf, size);
8258c2ecf20Sopenharmony_ci	if (IS_ERR(buf))
8268c2ecf20Sopenharmony_ci		return PTR_ERR(buf);
8278c2ecf20Sopenharmony_ci	ret = pde->write(f, buf, size);
8288c2ecf20Sopenharmony_ci	kfree(buf);
8298c2ecf20Sopenharmony_ci	return ret == 0 ? size : ret;
8308c2ecf20Sopenharmony_ci}
831