xref: /openbmc/linux/fs/proc/generic.c (revision 70551977)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * proc/fs/generic.c --- generic routines for the proc-fs
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * This file contains generic proc-fs routines for handling
61da177e4SLinus Torvalds  * directories and files.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * Copyright (C) 1991, 1992 Linus Torvalds.
91da177e4SLinus Torvalds  * Copyright (C) 1997 Theodore Ts'o
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
12b4884f23SAlexey Dobriyan #include <linux/cache.h>
131da177e4SLinus Torvalds #include <linux/errno.h>
141da177e4SLinus Torvalds #include <linux/time.h>
151da177e4SLinus Torvalds #include <linux/proc_fs.h>
161da177e4SLinus Torvalds #include <linux/stat.h>
171025774cSChristoph Hellwig #include <linux/mm.h>
181da177e4SLinus Torvalds #include <linux/module.h>
191da4d377SAlexey Dobriyan #include <linux/namei.h>
205a0e3ad6STejun Heo #include <linux/slab.h>
2187ebdc00SAndrew Morton #include <linux/printk.h>
221da177e4SLinus Torvalds #include <linux/mount.h>
231da177e4SLinus Torvalds #include <linux/init.h>
241da177e4SLinus Torvalds #include <linux/idr.h>
251da177e4SLinus Torvalds #include <linux/bitops.h>
2664a07bd8SSteven Rostedt #include <linux/spinlock.h>
27786d7e16SAlexey Dobriyan #include <linux/completion.h>
287c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
29fddda2b7SChristoph Hellwig #include <linux/seq_file.h>
301da177e4SLinus Torvalds 
31fee781e6SAdrian Bunk #include "internal.h"
32fee781e6SAdrian Bunk 
33ecf1a3dfSWaiman Long static DEFINE_RWLOCK(proc_subdir_lock);
3464a07bd8SSteven Rostedt 
35b4884f23SAlexey Dobriyan struct kmem_cache *proc_dir_entry_cache __ro_after_init;
36b4884f23SAlexey Dobriyan 
37b4884f23SAlexey Dobriyan void pde_free(struct proc_dir_entry *pde)
38b4884f23SAlexey Dobriyan {
39b4884f23SAlexey Dobriyan 	if (S_ISLNK(pde->mode))
40b4884f23SAlexey Dobriyan 		kfree(pde->data);
41b4884f23SAlexey Dobriyan 	if (pde->name != pde->inline_name)
42b4884f23SAlexey Dobriyan 		kfree(pde->name);
43b4884f23SAlexey Dobriyan 	kmem_cache_free(proc_dir_entry_cache, pde);
44b4884f23SAlexey Dobriyan }
45b4884f23SAlexey Dobriyan 
4693ad5bc6SAlexey Dobriyan static int proc_match(const char *name, struct proc_dir_entry *de, unsigned int len)
471da177e4SLinus Torvalds {
48710585d4SNicolas Dichtel 	if (len < de->namelen)
49710585d4SNicolas Dichtel 		return -1;
50710585d4SNicolas Dichtel 	if (len > de->namelen)
51710585d4SNicolas Dichtel 		return 1;
52710585d4SNicolas Dichtel 
53710585d4SNicolas Dichtel 	return memcmp(name, de->name, len);
54710585d4SNicolas Dichtel }
55710585d4SNicolas Dichtel 
56710585d4SNicolas Dichtel static struct proc_dir_entry *pde_subdir_first(struct proc_dir_entry *dir)
57710585d4SNicolas Dichtel {
584f113437SAlexey Dobriyan 	return rb_entry_safe(rb_first(&dir->subdir), struct proc_dir_entry,
594f113437SAlexey Dobriyan 			     subdir_node);
60710585d4SNicolas Dichtel }
61710585d4SNicolas Dichtel 
62710585d4SNicolas Dichtel static struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir)
63710585d4SNicolas Dichtel {
642fc1e948SNicolas Dichtel 	return rb_entry_safe(rb_next(&dir->subdir_node), struct proc_dir_entry,
652fc1e948SNicolas Dichtel 			     subdir_node);
66710585d4SNicolas Dichtel }
67710585d4SNicolas Dichtel 
68710585d4SNicolas Dichtel static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir,
69710585d4SNicolas Dichtel 					      const char *name,
70710585d4SNicolas Dichtel 					      unsigned int len)
71710585d4SNicolas Dichtel {
724f113437SAlexey Dobriyan 	struct rb_node *node = dir->subdir.rb_node;
73710585d4SNicolas Dichtel 
74710585d4SNicolas Dichtel 	while (node) {
754e4a7fb7SGeliang Tang 		struct proc_dir_entry *de = rb_entry(node,
76710585d4SNicolas Dichtel 						     struct proc_dir_entry,
77710585d4SNicolas Dichtel 						     subdir_node);
7893ad5bc6SAlexey Dobriyan 		int result = proc_match(name, de, len);
79710585d4SNicolas Dichtel 
80710585d4SNicolas Dichtel 		if (result < 0)
81710585d4SNicolas Dichtel 			node = node->rb_left;
82710585d4SNicolas Dichtel 		else if (result > 0)
83710585d4SNicolas Dichtel 			node = node->rb_right;
84710585d4SNicolas Dichtel 		else
85710585d4SNicolas Dichtel 			return de;
86710585d4SNicolas Dichtel 	}
87710585d4SNicolas Dichtel 	return NULL;
88710585d4SNicolas Dichtel }
89710585d4SNicolas Dichtel 
90710585d4SNicolas Dichtel static bool pde_subdir_insert(struct proc_dir_entry *dir,
91710585d4SNicolas Dichtel 			      struct proc_dir_entry *de)
92710585d4SNicolas Dichtel {
934f113437SAlexey Dobriyan 	struct rb_root *root = &dir->subdir;
944f113437SAlexey Dobriyan 	struct rb_node **new = &root->rb_node, *parent = NULL;
95710585d4SNicolas Dichtel 
96710585d4SNicolas Dichtel 	/* Figure out where to put new node */
97710585d4SNicolas Dichtel 	while (*new) {
984e4a7fb7SGeliang Tang 		struct proc_dir_entry *this = rb_entry(*new,
994e4a7fb7SGeliang Tang 						       struct proc_dir_entry,
1004e4a7fb7SGeliang Tang 						       subdir_node);
10193ad5bc6SAlexey Dobriyan 		int result = proc_match(de->name, this, de->namelen);
102710585d4SNicolas Dichtel 
103710585d4SNicolas Dichtel 		parent = *new;
104710585d4SNicolas Dichtel 		if (result < 0)
105710585d4SNicolas Dichtel 			new = &(*new)->rb_left;
1064f113437SAlexey Dobriyan 		else if (result > 0)
107710585d4SNicolas Dichtel 			new = &(*new)->rb_right;
1084f113437SAlexey Dobriyan 		else
109710585d4SNicolas Dichtel 			return false;
110710585d4SNicolas Dichtel 	}
111710585d4SNicolas Dichtel 
112710585d4SNicolas Dichtel 	/* Add new node and rebalance tree. */
113710585d4SNicolas Dichtel 	rb_link_node(&de->subdir_node, parent, new);
1144f113437SAlexey Dobriyan 	rb_insert_color(&de->subdir_node, root);
115710585d4SNicolas Dichtel 	return true;
1161da177e4SLinus Torvalds }
1171da177e4SLinus Torvalds 
118549c7297SChristian Brauner static int proc_notify_change(struct user_namespace *mnt_userns,
119549c7297SChristian Brauner 			      struct dentry *dentry, struct iattr *iattr)
1201da177e4SLinus Torvalds {
1212b0143b5SDavid Howells 	struct inode *inode = d_inode(dentry);
1221da177e4SLinus Torvalds 	struct proc_dir_entry *de = PDE(inode);
1231da177e4SLinus Torvalds 	int error;
1241da177e4SLinus Torvalds 
1252f221d6fSChristian Brauner 	error = setattr_prepare(&init_user_ns, dentry, iattr);
1261da177e4SLinus Torvalds 	if (error)
1271025774cSChristoph Hellwig 		return error;
1281da177e4SLinus Torvalds 
1292f221d6fSChristian Brauner 	setattr_copy(&init_user_ns, inode, iattr);
1301025774cSChristoph Hellwig 	mark_inode_dirty(inode);
1311da177e4SLinus Torvalds 
132cdf7e8ddSRui Xiang 	proc_set_user(de, inode->i_uid, inode->i_gid);
1331da177e4SLinus Torvalds 	de->mode = inode->i_mode;
1341025774cSChristoph Hellwig 	return 0;
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds 
137549c7297SChristian Brauner static int proc_getattr(struct user_namespace *mnt_userns,
138549c7297SChristian Brauner 			const struct path *path, struct kstat *stat,
139a528d35eSDavid Howells 			u32 request_mask, unsigned int query_flags)
1402b579beeSMiklos Szeredi {
141a528d35eSDavid Howells 	struct inode *inode = d_inode(path->dentry);
1426bee55f9SAlexander Kuleshov 	struct proc_dir_entry *de = PDE(inode);
143e06689bfSAlexey Dobriyan 	if (de) {
144e06689bfSAlexey Dobriyan 		nlink_t nlink = READ_ONCE(de->nlink);
145e06689bfSAlexey Dobriyan 		if (nlink > 0) {
146e06689bfSAlexey Dobriyan 			set_nlink(inode, nlink);
147e06689bfSAlexey Dobriyan 		}
148e06689bfSAlexey Dobriyan 	}
1492b579beeSMiklos Szeredi 
1500d56a451SChristian Brauner 	generic_fillattr(&init_user_ns, inode, stat);
1512b579beeSMiklos Szeredi 	return 0;
1522b579beeSMiklos Szeredi }
1532b579beeSMiklos Szeredi 
154c5ef1c42SArjan van de Ven static const struct inode_operations proc_file_inode_operations = {
1551da177e4SLinus Torvalds 	.setattr	= proc_notify_change,
1561da177e4SLinus Torvalds };
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds /*
1591da177e4SLinus Torvalds  * This function parses a name such as "tty/driver/serial", and
1601da177e4SLinus Torvalds  * returns the struct proc_dir_entry for "/proc/tty/driver", and
1611da177e4SLinus Torvalds  * returns "serial" in residual.
1621da177e4SLinus Torvalds  */
163e17a5765SAlexey Dobriyan static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret,
164e17a5765SAlexey Dobriyan 			     const char **residual)
1651da177e4SLinus Torvalds {
1661da177e4SLinus Torvalds 	const char     		*cp = name, *next;
1671da177e4SLinus Torvalds 	struct proc_dir_entry	*de;
1681da177e4SLinus Torvalds 
169b793cd9aSAlexey Dobriyan 	de = *ret ?: &proc_root;
170b793cd9aSAlexey Dobriyan 	while ((next = strchr(cp, '/')) != NULL) {
1715f6354eaSAlexey Dobriyan 		de = pde_subdir_find(de, cp, next - cp);
17212bac0d9SAlexey Dobriyan 		if (!de) {
17312bac0d9SAlexey Dobriyan 			WARN(1, "name '%s'\n", name);
174e17a5765SAlexey Dobriyan 			return -ENOENT;
17512bac0d9SAlexey Dobriyan 		}
1765f6354eaSAlexey Dobriyan 		cp = next + 1;
1771da177e4SLinus Torvalds 	}
1781da177e4SLinus Torvalds 	*residual = cp;
1791da177e4SLinus Torvalds 	*ret = de;
180e17a5765SAlexey Dobriyan 	return 0;
181e17a5765SAlexey Dobriyan }
182e17a5765SAlexey Dobriyan 
183e17a5765SAlexey Dobriyan static int xlate_proc_name(const char *name, struct proc_dir_entry **ret,
184e17a5765SAlexey Dobriyan 			   const char **residual)
185e17a5765SAlexey Dobriyan {
186e17a5765SAlexey Dobriyan 	int rv;
187e17a5765SAlexey Dobriyan 
188ecf1a3dfSWaiman Long 	read_lock(&proc_subdir_lock);
189e17a5765SAlexey Dobriyan 	rv = __xlate_proc_name(name, ret, residual);
190ecf1a3dfSWaiman Long 	read_unlock(&proc_subdir_lock);
191e17a5765SAlexey Dobriyan 	return rv;
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds 
1949a185409SAlexey Dobriyan static DEFINE_IDA(proc_inum_ida);
1951da177e4SLinus Torvalds 
19667935df4SAlexey Dobriyan #define PROC_DYNAMIC_FIRST 0xF0000000U
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds /*
1991da177e4SLinus Torvalds  * Return an inode number between PROC_DYNAMIC_FIRST and
2001da177e4SLinus Torvalds  * 0xffffffff, or zero on failure.
2011da177e4SLinus Torvalds  */
20233d6dce6SEric W. Biederman int proc_alloc_inum(unsigned int *inum)
2031da177e4SLinus Torvalds {
204cde1b693SHeiner Kallweit 	int i;
2051da177e4SLinus Torvalds 
206cde1b693SHeiner Kallweit 	i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1,
207cde1b693SHeiner Kallweit 			   GFP_KERNEL);
208cde1b693SHeiner Kallweit 	if (i < 0)
209cde1b693SHeiner Kallweit 		return i;
2101da177e4SLinus Torvalds 
211cde1b693SHeiner Kallweit 	*inum = PROC_DYNAMIC_FIRST + (unsigned int)i;
212cc996099SAlexey Dobriyan 	return 0;
21367935df4SAlexey Dobriyan }
2141da177e4SLinus Torvalds 
21533d6dce6SEric W. Biederman void proc_free_inum(unsigned int inum)
2161da177e4SLinus Torvalds {
217cde1b693SHeiner Kallweit 	ida_simple_remove(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST);
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds 
2201da4d377SAlexey Dobriyan static int proc_misc_d_revalidate(struct dentry *dentry, unsigned int flags)
2211da4d377SAlexey Dobriyan {
2221da4d377SAlexey Dobriyan 	if (flags & LOOKUP_RCU)
2231da4d377SAlexey Dobriyan 		return -ECHILD;
2241da4d377SAlexey Dobriyan 
2251da4d377SAlexey Dobriyan 	if (atomic_read(&PDE(d_inode(dentry))->in_use) < 0)
2261da4d377SAlexey Dobriyan 		return 0; /* revalidate */
2271da4d377SAlexey Dobriyan 	return 1;
2281da4d377SAlexey Dobriyan }
2291da4d377SAlexey Dobriyan 
2301da4d377SAlexey Dobriyan static int proc_misc_d_delete(const struct dentry *dentry)
2311da4d377SAlexey Dobriyan {
2321da4d377SAlexey Dobriyan 	return atomic_read(&PDE(d_inode(dentry))->in_use) < 0;
2331da4d377SAlexey Dobriyan }
2341da4d377SAlexey Dobriyan 
2351da4d377SAlexey Dobriyan static const struct dentry_operations proc_misc_dentry_ops = {
2361da4d377SAlexey Dobriyan 	.d_revalidate	= proc_misc_d_revalidate,
2371da4d377SAlexey Dobriyan 	.d_delete	= proc_misc_d_delete,
2381da4d377SAlexey Dobriyan };
2391da4d377SAlexey Dobriyan 
2401da177e4SLinus Torvalds /*
2411da177e4SLinus Torvalds  * Don't create negative dentries here, return -ENOENT by hand
2421da177e4SLinus Torvalds  * instead.
2431da177e4SLinus Torvalds  */
24493ad5bc6SAlexey Dobriyan struct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,
24593ad5bc6SAlexey Dobriyan 			      struct proc_dir_entry *de)
2461da177e4SLinus Torvalds {
247d3d009cbSAl Viro 	struct inode *inode;
2481da177e4SLinus Torvalds 
249ecf1a3dfSWaiman Long 	read_lock(&proc_subdir_lock);
250710585d4SNicolas Dichtel 	de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len);
251710585d4SNicolas Dichtel 	if (de) {
252135d5655SAlexey Dobriyan 		pde_get(de);
253ecf1a3dfSWaiman Long 		read_unlock(&proc_subdir_lock);
2546d1b6e4eSAlexey Dobriyan 		inode = proc_get_inode(dir->i_sb, de);
255d3d009cbSAl Viro 		if (!inode)
256d3d009cbSAl Viro 			return ERR_PTR(-ENOMEM);
2571fde6f21SAlexey Dobriyan 		d_set_d_op(dentry, de->proc_dops);
258888e2b03SAl Viro 		return d_splice_alias(inode, dentry);
2591da177e4SLinus Torvalds 	}
260ecf1a3dfSWaiman Long 	read_unlock(&proc_subdir_lock);
261d3d009cbSAl Viro 	return ERR_PTR(-ENOENT);
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds 
264e9720acdSPavel Emelyanov struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
26500cd8dd3SAl Viro 		unsigned int flags)
266e9720acdSPavel Emelyanov {
2676814ef2dSAlexey Gladkov 	struct proc_fs_info *fs_info = proc_sb_info(dir->i_sb);
2686814ef2dSAlexey Gladkov 
2696814ef2dSAlexey Gladkov 	if (fs_info->pidonly == PROC_PIDONLY_ON)
2706814ef2dSAlexey Gladkov 		return ERR_PTR(-ENOENT);
2716814ef2dSAlexey Gladkov 
27293ad5bc6SAlexey Dobriyan 	return proc_lookup_de(dir, dentry, PDE(dir));
273e9720acdSPavel Emelyanov }
274e9720acdSPavel Emelyanov 
2751da177e4SLinus Torvalds /*
2761da177e4SLinus Torvalds  * This returns non-zero if at EOF, so that the /proc
2771da177e4SLinus Torvalds  * root directory can use this and check if it should
2781da177e4SLinus Torvalds  * continue with the <pid> entries..
2791da177e4SLinus Torvalds  *
2801da177e4SLinus Torvalds  * Note that the VFS-layer doesn't care about the return
2811da177e4SLinus Torvalds  * value of the readdir() call, as long as it's non-negative
2821da177e4SLinus Torvalds  * for success..
2831da177e4SLinus Torvalds  */
28493ad5bc6SAlexey Dobriyan int proc_readdir_de(struct file *file, struct dir_context *ctx,
28593ad5bc6SAlexey Dobriyan 		    struct proc_dir_entry *de)
2861da177e4SLinus Torvalds {
2871da177e4SLinus Torvalds 	int i;
2881da177e4SLinus Torvalds 
289f0c3b509SAl Viro 	if (!dir_emit_dots(file, ctx))
290f0c3b509SAl Viro 		return 0;
291f0c3b509SAl Viro 
2928d48b2e0SAlexey Dobriyan 	i = ctx->pos - 2;
293ecf1a3dfSWaiman Long 	read_lock(&proc_subdir_lock);
294710585d4SNicolas Dichtel 	de = pde_subdir_first(de);
2951da177e4SLinus Torvalds 	for (;;) {
2961da177e4SLinus Torvalds 		if (!de) {
297ecf1a3dfSWaiman Long 			read_unlock(&proc_subdir_lock);
298f0c3b509SAl Viro 			return 0;
2991da177e4SLinus Torvalds 		}
3001da177e4SLinus Torvalds 		if (!i)
3011da177e4SLinus Torvalds 			break;
302710585d4SNicolas Dichtel 		de = pde_subdir_next(de);
3031da177e4SLinus Torvalds 		i--;
3041da177e4SLinus Torvalds 	}
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds 	do {
30759cd0cbcSDarrick J. Wong 		struct proc_dir_entry *next;
308135d5655SAlexey Dobriyan 		pde_get(de);
309ecf1a3dfSWaiman Long 		read_unlock(&proc_subdir_lock);
310f0c3b509SAl Viro 		if (!dir_emit(ctx, de->name, de->namelen,
311f0c3b509SAl Viro 			    de->low_ino, de->mode >> 12)) {
312135d5655SAlexey Dobriyan 			pde_put(de);
313f0c3b509SAl Viro 			return 0;
31459cd0cbcSDarrick J. Wong 		}
315f0c3b509SAl Viro 		ctx->pos++;
3168d48b2e0SAlexey Dobriyan 		read_lock(&proc_subdir_lock);
317710585d4SNicolas Dichtel 		next = pde_subdir_next(de);
318135d5655SAlexey Dobriyan 		pde_put(de);
31959cd0cbcSDarrick J. Wong 		de = next;
3201da177e4SLinus Torvalds 	} while (de);
321ecf1a3dfSWaiman Long 	read_unlock(&proc_subdir_lock);
322fd3930f7SLinus Torvalds 	return 1;
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds 
325f0c3b509SAl Viro int proc_readdir(struct file *file, struct dir_context *ctx)
326e9720acdSPavel Emelyanov {
327f0c3b509SAl Viro 	struct inode *inode = file_inode(file);
3286814ef2dSAlexey Gladkov 	struct proc_fs_info *fs_info = proc_sb_info(inode->i_sb);
3296814ef2dSAlexey Gladkov 
3306814ef2dSAlexey Gladkov 	if (fs_info->pidonly == PROC_PIDONLY_ON)
3316814ef2dSAlexey Gladkov 		return 1;
332e9720acdSPavel Emelyanov 
33393ad5bc6SAlexey Dobriyan 	return proc_readdir_de(file, ctx, PDE(inode));
334e9720acdSPavel Emelyanov }
335e9720acdSPavel Emelyanov 
3361da177e4SLinus Torvalds /*
3371da177e4SLinus Torvalds  * These are the generic /proc directory operations. They
3381da177e4SLinus Torvalds  * use the in-memory "struct proc_dir_entry" tree to parse
3391da177e4SLinus Torvalds  * the /proc directory.
3401da177e4SLinus Torvalds  */
34100977a59SArjan van de Ven static const struct file_operations proc_dir_operations = {
342b4df2b92SAlexey Dobriyan 	.llseek			= generic_file_llseek,
3431da177e4SLinus Torvalds 	.read			= generic_read_dir,
344f50752eaSAl Viro 	.iterate_shared		= proc_readdir,
3451da177e4SLinus Torvalds };
3461da177e4SLinus Torvalds 
347c6c75dedSAlexey Dobriyan static int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags)
348c6c75dedSAlexey Dobriyan {
349c6c75dedSAlexey Dobriyan 	return 0;
350c6c75dedSAlexey Dobriyan }
351c6c75dedSAlexey Dobriyan 
352c6c75dedSAlexey Dobriyan const struct dentry_operations proc_net_dentry_ops = {
353c6c75dedSAlexey Dobriyan 	.d_revalidate	= proc_net_d_revalidate,
354c6c75dedSAlexey Dobriyan 	.d_delete	= always_delete_dentry,
355c6c75dedSAlexey Dobriyan };
356c6c75dedSAlexey Dobriyan 
3571da177e4SLinus Torvalds /*
3581da177e4SLinus Torvalds  * proc directories can do almost nothing..
3591da177e4SLinus Torvalds  */
360c5ef1c42SArjan van de Ven static const struct inode_operations proc_dir_inode_operations = {
3611da177e4SLinus Torvalds 	.lookup		= proc_lookup,
3622b579beeSMiklos Szeredi 	.getattr	= proc_getattr,
3631da177e4SLinus Torvalds 	.setattr	= proc_notify_change,
3641da177e4SLinus Torvalds };
3651da177e4SLinus Torvalds 
36661172eaeSChristoph Hellwig /* returns the registered entry, or frees dp and returns NULL on failure */
36761172eaeSChristoph Hellwig struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
36861172eaeSChristoph Hellwig 		struct proc_dir_entry *dp)
3691da177e4SLinus Torvalds {
37061172eaeSChristoph Hellwig 	if (proc_alloc_inum(&dp->low_ino))
37161172eaeSChristoph Hellwig 		goto out_free_entry;
37264a07bd8SSteven Rostedt 
373ecf1a3dfSWaiman Long 	write_lock(&proc_subdir_lock);
374710585d4SNicolas Dichtel 	dp->parent = dir;
375b208d54bSDebabrata Banerjee 	if (pde_subdir_insert(dir, dp) == false) {
37687ebdc00SAndrew Morton 		WARN(1, "proc_dir_entry '%s/%s' already registered\n",
377665020c3SAlexey Dobriyan 		     dir->name, dp->name);
378ecf1a3dfSWaiman Long 		write_unlock(&proc_subdir_lock);
37961172eaeSChristoph Hellwig 		goto out_free_inum;
380b208d54bSDebabrata Banerjee 	}
381e06689bfSAlexey Dobriyan 	dir->nlink++;
382ecf1a3dfSWaiman Long 	write_unlock(&proc_subdir_lock);
38399fc06dfSChangli Gao 
38461172eaeSChristoph Hellwig 	return dp;
38561172eaeSChristoph Hellwig out_free_inum:
38661172eaeSChristoph Hellwig 	proc_free_inum(dp->low_ino);
38761172eaeSChristoph Hellwig out_free_entry:
38861172eaeSChristoph Hellwig 	pde_free(dp);
38961172eaeSChristoph Hellwig 	return NULL;
3901da177e4SLinus Torvalds }
3911da177e4SLinus Torvalds 
3922d3a4e36SAlexey Dobriyan static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
3931da177e4SLinus Torvalds 					  const char *name,
394d161a13fSAl Viro 					  umode_t mode,
3951da177e4SLinus Torvalds 					  nlink_t nlink)
3961da177e4SLinus Torvalds {
3971da177e4SLinus Torvalds 	struct proc_dir_entry *ent = NULL;
398dbcdb504SAlexey Dobriyan 	const char *fn;
399dbcdb504SAlexey Dobriyan 	struct qstr qstr;
4001da177e4SLinus Torvalds 
4017cee4e00SAlexey Dobriyan 	if (xlate_proc_name(name, parent, &fn) != 0)
4021da177e4SLinus Torvalds 		goto out;
403dbcdb504SAlexey Dobriyan 	qstr.name = fn;
404dbcdb504SAlexey Dobriyan 	qstr.len = strlen(fn);
405dbcdb504SAlexey Dobriyan 	if (qstr.len == 0 || qstr.len >= 256) {
406dbcdb504SAlexey Dobriyan 		WARN(1, "name len %u\n", qstr.len);
407dbcdb504SAlexey Dobriyan 		return NULL;
408dbcdb504SAlexey Dobriyan 	}
409b77d70dbSAlexey Dobriyan 	if (qstr.len == 1 && fn[0] == '.') {
410b77d70dbSAlexey Dobriyan 		WARN(1, "name '.'\n");
411b77d70dbSAlexey Dobriyan 		return NULL;
412b77d70dbSAlexey Dobriyan 	}
413b77d70dbSAlexey Dobriyan 	if (qstr.len == 2 && fn[0] == '.' && fn[1] == '.') {
414b77d70dbSAlexey Dobriyan 		WARN(1, "name '..'\n");
415b77d70dbSAlexey Dobriyan 		return NULL;
416b77d70dbSAlexey Dobriyan 	}
417dbcdb504SAlexey Dobriyan 	if (*parent == &proc_root && name_to_int(&qstr) != ~0U) {
418dbcdb504SAlexey Dobriyan 		WARN(1, "create '/proc/%s' by hand\n", qstr.name);
419dbcdb504SAlexey Dobriyan 		return NULL;
420dbcdb504SAlexey Dobriyan 	}
421eb6d38d5SEric W. Biederman 	if (is_empty_pde(*parent)) {
422eb6d38d5SEric W. Biederman 		WARN(1, "attempt to add to permanently empty directory");
423eb6d38d5SEric W. Biederman 		return NULL;
424eb6d38d5SEric W. Biederman 	}
4251da177e4SLinus Torvalds 
426b4884f23SAlexey Dobriyan 	ent = kmem_cache_zalloc(proc_dir_entry_cache, GFP_KERNEL);
42717baa2a2Syan 	if (!ent)
42817baa2a2Syan 		goto out;
4291da177e4SLinus Torvalds 
43024074a35SDavid Howells 	if (qstr.len + 1 <= SIZEOF_PDE_INLINE_NAME) {
431b4884f23SAlexey Dobriyan 		ent->name = ent->inline_name;
432b4884f23SAlexey Dobriyan 	} else {
433b4884f23SAlexey Dobriyan 		ent->name = kmalloc(qstr.len + 1, GFP_KERNEL);
434b4884f23SAlexey Dobriyan 		if (!ent->name) {
435b4884f23SAlexey Dobriyan 			pde_free(ent);
436b4884f23SAlexey Dobriyan 			return NULL;
437b4884f23SAlexey Dobriyan 		}
438b4884f23SAlexey Dobriyan 	}
439b4884f23SAlexey Dobriyan 
440dbcdb504SAlexey Dobriyan 	memcpy(ent->name, fn, qstr.len + 1);
441dbcdb504SAlexey Dobriyan 	ent->namelen = qstr.len;
4421da177e4SLinus Torvalds 	ent->mode = mode;
4431da177e4SLinus Torvalds 	ent->nlink = nlink;
4444f113437SAlexey Dobriyan 	ent->subdir = RB_ROOT;
4459cdd83e3SAlexey Dobriyan 	refcount_set(&ent->refcnt, 1);
446786d7e16SAlexey Dobriyan 	spin_lock_init(&ent->pde_unload_lock);
447881adb85SAlexey Dobriyan 	INIT_LIST_HEAD(&ent->pde_openers);
448c110486fSDmitry Torokhov 	proc_set_user(ent, (*parent)->uid, (*parent)->gid);
449c110486fSDmitry Torokhov 
4501fde6f21SAlexey Dobriyan 	ent->proc_dops = &proc_misc_dentry_ops;
451*70551977SAlexey Dobriyan 	/* Revalidate everything under /proc/${pid}/net */
452*70551977SAlexey Dobriyan 	if ((*parent)->proc_dops == &proc_net_dentry_ops)
453*70551977SAlexey Dobriyan 		pde_force_lookup(ent);
4541fde6f21SAlexey Dobriyan 
4551da177e4SLinus Torvalds out:
4561da177e4SLinus Torvalds 	return ent;
4571da177e4SLinus Torvalds }
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds struct proc_dir_entry *proc_symlink(const char *name,
4601da177e4SLinus Torvalds 		struct proc_dir_entry *parent, const char *dest)
4611da177e4SLinus Torvalds {
4621da177e4SLinus Torvalds 	struct proc_dir_entry *ent;
4631da177e4SLinus Torvalds 
4642d3a4e36SAlexey Dobriyan 	ent = __proc_create(&parent, name,
4651da177e4SLinus Torvalds 			  (S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO),1);
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 	if (ent) {
4681da177e4SLinus Torvalds 		ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL);
4691da177e4SLinus Torvalds 		if (ent->data) {
4701da177e4SLinus Torvalds 			strcpy((char*)ent->data,dest);
471d443b9fdSAl Viro 			ent->proc_iops = &proc_link_inode_operations;
47261172eaeSChristoph Hellwig 			ent = proc_register(parent, ent);
4731da177e4SLinus Torvalds 		} else {
474b4884f23SAlexey Dobriyan 			pde_free(ent);
4751da177e4SLinus Torvalds 			ent = NULL;
4761da177e4SLinus Torvalds 		}
4771da177e4SLinus Torvalds 	}
4781da177e4SLinus Torvalds 	return ent;
4791da177e4SLinus Torvalds }
480587d4a17SHelight.Xu EXPORT_SYMBOL(proc_symlink);
4811da177e4SLinus Torvalds 
482c6c75dedSAlexey Dobriyan struct proc_dir_entry *_proc_mkdir(const char *name, umode_t mode,
483c6c75dedSAlexey Dobriyan 		struct proc_dir_entry *parent, void *data, bool force_lookup)
4841da177e4SLinus Torvalds {
4851da177e4SLinus Torvalds 	struct proc_dir_entry *ent;
4861da177e4SLinus Torvalds 
487270b5ac2SDavid Howells 	if (mode == 0)
488270b5ac2SDavid Howells 		mode = S_IRUGO | S_IXUGO;
489270b5ac2SDavid Howells 
4902d3a4e36SAlexey Dobriyan 	ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
4911da177e4SLinus Torvalds 	if (ent) {
492270b5ac2SDavid Howells 		ent->data = data;
493d56c0d45SAlexey Dobriyan 		ent->proc_dir_ops = &proc_dir_operations;
494d443b9fdSAl Viro 		ent->proc_iops = &proc_dir_inode_operations;
495c6c75dedSAlexey Dobriyan 		if (force_lookup) {
496c6c75dedSAlexey Dobriyan 			pde_force_lookup(ent);
497c6c75dedSAlexey Dobriyan 		}
49861172eaeSChristoph Hellwig 		ent = proc_register(parent, ent);
4991da177e4SLinus Torvalds 	}
5001da177e4SLinus Torvalds 	return ent;
5011da177e4SLinus Torvalds }
502c6c75dedSAlexey Dobriyan EXPORT_SYMBOL_GPL(_proc_mkdir);
503c6c75dedSAlexey Dobriyan 
504c6c75dedSAlexey Dobriyan struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
505c6c75dedSAlexey Dobriyan 		struct proc_dir_entry *parent, void *data)
506c6c75dedSAlexey Dobriyan {
507c6c75dedSAlexey Dobriyan 	return _proc_mkdir(name, mode, parent, data, false);
508c6c75dedSAlexey Dobriyan }
509270b5ac2SDavid Howells EXPORT_SYMBOL_GPL(proc_mkdir_data);
5101da177e4SLinus Torvalds 
511270b5ac2SDavid Howells struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
51278e92b99SDenis V. Lunev 				       struct proc_dir_entry *parent)
51378e92b99SDenis V. Lunev {
514270b5ac2SDavid Howells 	return proc_mkdir_data(name, mode, parent, NULL);
51578e92b99SDenis V. Lunev }
516270b5ac2SDavid Howells EXPORT_SYMBOL(proc_mkdir_mode);
51778e92b99SDenis V. Lunev 
5181da177e4SLinus Torvalds struct proc_dir_entry *proc_mkdir(const char *name,
5191da177e4SLinus Torvalds 		struct proc_dir_entry *parent)
5201da177e4SLinus Torvalds {
521270b5ac2SDavid Howells 	return proc_mkdir_data(name, 0, parent, NULL);
5221da177e4SLinus Torvalds }
523587d4a17SHelight.Xu EXPORT_SYMBOL(proc_mkdir);
5241da177e4SLinus Torvalds 
525eb6d38d5SEric W. Biederman struct proc_dir_entry *proc_create_mount_point(const char *name)
526eb6d38d5SEric W. Biederman {
527eb6d38d5SEric W. Biederman 	umode_t mode = S_IFDIR | S_IRUGO | S_IXUGO;
528eb6d38d5SEric W. Biederman 	struct proc_dir_entry *ent, *parent = NULL;
529eb6d38d5SEric W. Biederman 
530eb6d38d5SEric W. Biederman 	ent = __proc_create(&parent, name, mode, 2);
531eb6d38d5SEric W. Biederman 	if (ent) {
532eb6d38d5SEric W. Biederman 		ent->data = NULL;
533d56c0d45SAlexey Dobriyan 		ent->proc_dir_ops = NULL;
534eb6d38d5SEric W. Biederman 		ent->proc_iops = NULL;
53561172eaeSChristoph Hellwig 		ent = proc_register(parent, ent);
536eb6d38d5SEric W. Biederman 	}
537eb6d38d5SEric W. Biederman 	return ent;
538eb6d38d5SEric W. Biederman }
539f97df70bSSeth Forshee EXPORT_SYMBOL(proc_create_mount_point);
540eb6d38d5SEric W. Biederman 
5417aed53d1SChristoph Hellwig struct proc_dir_entry *proc_create_reg(const char *name, umode_t mode,
5427aed53d1SChristoph Hellwig 		struct proc_dir_entry **parent, void *data)
5432d3a4e36SAlexey Dobriyan {
5447aed53d1SChristoph Hellwig 	struct proc_dir_entry *p;
5457aed53d1SChristoph Hellwig 
5462d3a4e36SAlexey Dobriyan 	if ((mode & S_IFMT) == 0)
5472d3a4e36SAlexey Dobriyan 		mode |= S_IFREG;
5487aed53d1SChristoph Hellwig 	if ((mode & S_IALLUGO) == 0)
5497aed53d1SChristoph Hellwig 		mode |= S_IRUGO;
5507aed53d1SChristoph Hellwig 	if (WARN_ON_ONCE(!S_ISREG(mode)))
551b6cdc731SAl Viro 		return NULL;
5527aed53d1SChristoph Hellwig 
5537aed53d1SChristoph Hellwig 	p = __proc_create(parent, name, mode, 1);
5547aed53d1SChristoph Hellwig 	if (p) {
5557aed53d1SChristoph Hellwig 		p->proc_iops = &proc_file_inode_operations;
5567aed53d1SChristoph Hellwig 		p->data = data;
5572d3a4e36SAlexey Dobriyan 	}
5587aed53d1SChristoph Hellwig 	return p;
5597aed53d1SChristoph Hellwig }
5607aed53d1SChristoph Hellwig 
561d919b33dSAlexey Dobriyan static inline void pde_set_flags(struct proc_dir_entry *pde)
562d919b33dSAlexey Dobriyan {
563d919b33dSAlexey Dobriyan 	if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
564d919b33dSAlexey Dobriyan 		pde->flags |= PROC_ENTRY_PERMANENT;
565d919b33dSAlexey Dobriyan }
566d919b33dSAlexey Dobriyan 
5677aed53d1SChristoph Hellwig struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
5687aed53d1SChristoph Hellwig 		struct proc_dir_entry *parent,
569d56c0d45SAlexey Dobriyan 		const struct proc_ops *proc_ops, void *data)
5707aed53d1SChristoph Hellwig {
5717aed53d1SChristoph Hellwig 	struct proc_dir_entry *p;
5722d3a4e36SAlexey Dobriyan 
5737aed53d1SChristoph Hellwig 	p = proc_create_reg(name, mode, &parent, data);
5747aed53d1SChristoph Hellwig 	if (!p)
5752d3a4e36SAlexey Dobriyan 		return NULL;
576d56c0d45SAlexey Dobriyan 	p->proc_ops = proc_ops;
577d919b33dSAlexey Dobriyan 	pde_set_flags(p);
5787aed53d1SChristoph Hellwig 	return proc_register(parent, p);
5792d3a4e36SAlexey Dobriyan }
580587d4a17SHelight.Xu EXPORT_SYMBOL(proc_create_data);
581271a15eaSDavid Howells 
582855d9765SAlexey Dobriyan struct proc_dir_entry *proc_create(const char *name, umode_t mode,
583855d9765SAlexey Dobriyan 				   struct proc_dir_entry *parent,
584d56c0d45SAlexey Dobriyan 				   const struct proc_ops *proc_ops)
585855d9765SAlexey Dobriyan {
586d56c0d45SAlexey Dobriyan 	return proc_create_data(name, mode, parent, proc_ops, NULL);
587855d9765SAlexey Dobriyan }
588855d9765SAlexey Dobriyan EXPORT_SYMBOL(proc_create);
589855d9765SAlexey Dobriyan 
590fddda2b7SChristoph Hellwig static int proc_seq_open(struct inode *inode, struct file *file)
591fddda2b7SChristoph Hellwig {
592fddda2b7SChristoph Hellwig 	struct proc_dir_entry *de = PDE(inode);
593fddda2b7SChristoph Hellwig 
59444414d82SChristoph Hellwig 	if (de->state_size)
59544414d82SChristoph Hellwig 		return seq_open_private(file, de->seq_ops, de->state_size);
596fddda2b7SChristoph Hellwig 	return seq_open(file, de->seq_ops);
597fddda2b7SChristoph Hellwig }
598fddda2b7SChristoph Hellwig 
599877f919eSChunyu Hu static int proc_seq_release(struct inode *inode, struct file *file)
600877f919eSChunyu Hu {
601877f919eSChunyu Hu 	struct proc_dir_entry *de = PDE(inode);
602877f919eSChunyu Hu 
603877f919eSChunyu Hu 	if (de->state_size)
604877f919eSChunyu Hu 		return seq_release_private(inode, file);
605877f919eSChunyu Hu 	return seq_release(inode, file);
606877f919eSChunyu Hu }
607877f919eSChunyu Hu 
608d56c0d45SAlexey Dobriyan static const struct proc_ops proc_seq_ops = {
609d919b33dSAlexey Dobriyan 	/* not permanent -- can call into arbitrary seq_operations */
610d56c0d45SAlexey Dobriyan 	.proc_open	= proc_seq_open,
611b24c30c6SChristoph Hellwig 	.proc_read_iter	= seq_read_iter,
612d56c0d45SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
613d56c0d45SAlexey Dobriyan 	.proc_release	= proc_seq_release,
614fddda2b7SChristoph Hellwig };
615fddda2b7SChristoph Hellwig 
61644414d82SChristoph Hellwig struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode,
617fddda2b7SChristoph Hellwig 		struct proc_dir_entry *parent, const struct seq_operations *ops,
61844414d82SChristoph Hellwig 		unsigned int state_size, void *data)
619fddda2b7SChristoph Hellwig {
620fddda2b7SChristoph Hellwig 	struct proc_dir_entry *p;
621fddda2b7SChristoph Hellwig 
622fddda2b7SChristoph Hellwig 	p = proc_create_reg(name, mode, &parent, data);
623fddda2b7SChristoph Hellwig 	if (!p)
624fddda2b7SChristoph Hellwig 		return NULL;
625d56c0d45SAlexey Dobriyan 	p->proc_ops = &proc_seq_ops;
626fddda2b7SChristoph Hellwig 	p->seq_ops = ops;
62744414d82SChristoph Hellwig 	p->state_size = state_size;
628fddda2b7SChristoph Hellwig 	return proc_register(parent, p);
629fddda2b7SChristoph Hellwig }
63044414d82SChristoph Hellwig EXPORT_SYMBOL(proc_create_seq_private);
631fddda2b7SChristoph Hellwig 
6323f3942acSChristoph Hellwig static int proc_single_open(struct inode *inode, struct file *file)
6333f3942acSChristoph Hellwig {
6343f3942acSChristoph Hellwig 	struct proc_dir_entry *de = PDE(inode);
6353f3942acSChristoph Hellwig 
6363f3942acSChristoph Hellwig 	return single_open(file, de->single_show, de->data);
6373f3942acSChristoph Hellwig }
6383f3942acSChristoph Hellwig 
639d56c0d45SAlexey Dobriyan static const struct proc_ops proc_single_ops = {
640d919b33dSAlexey Dobriyan 	/* not permanent -- can call into arbitrary ->single_show */
641d56c0d45SAlexey Dobriyan 	.proc_open	= proc_single_open,
6427cfc630eSGreg Kroah-Hartman 	.proc_read_iter = seq_read_iter,
643d56c0d45SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
644d56c0d45SAlexey Dobriyan 	.proc_release	= single_release,
6453f3942acSChristoph Hellwig };
6463f3942acSChristoph Hellwig 
6473f3942acSChristoph Hellwig struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,
6483f3942acSChristoph Hellwig 		struct proc_dir_entry *parent,
6493f3942acSChristoph Hellwig 		int (*show)(struct seq_file *, void *), void *data)
6503f3942acSChristoph Hellwig {
6513f3942acSChristoph Hellwig 	struct proc_dir_entry *p;
6523f3942acSChristoph Hellwig 
6533f3942acSChristoph Hellwig 	p = proc_create_reg(name, mode, &parent, data);
6543f3942acSChristoph Hellwig 	if (!p)
6553f3942acSChristoph Hellwig 		return NULL;
656d56c0d45SAlexey Dobriyan 	p->proc_ops = &proc_single_ops;
6573f3942acSChristoph Hellwig 	p->single_show = show;
6583f3942acSChristoph Hellwig 	return proc_register(parent, p);
6593f3942acSChristoph Hellwig }
6603f3942acSChristoph Hellwig EXPORT_SYMBOL(proc_create_single_data);
6613f3942acSChristoph Hellwig 
662271a15eaSDavid Howells void proc_set_size(struct proc_dir_entry *de, loff_t size)
663271a15eaSDavid Howells {
664271a15eaSDavid Howells 	de->size = size;
665271a15eaSDavid Howells }
666271a15eaSDavid Howells EXPORT_SYMBOL(proc_set_size);
667271a15eaSDavid Howells 
668271a15eaSDavid Howells void proc_set_user(struct proc_dir_entry *de, kuid_t uid, kgid_t gid)
669271a15eaSDavid Howells {
670271a15eaSDavid Howells 	de->uid = uid;
671271a15eaSDavid Howells 	de->gid = gid;
672271a15eaSDavid Howells }
673271a15eaSDavid Howells EXPORT_SYMBOL(proc_set_user);
6742d3a4e36SAlexey Dobriyan 
675135d5655SAlexey Dobriyan void pde_put(struct proc_dir_entry *pde)
676135d5655SAlexey Dobriyan {
6779cdd83e3SAlexey Dobriyan 	if (refcount_dec_and_test(&pde->refcnt)) {
678b4884f23SAlexey Dobriyan 		proc_free_inum(pde->low_ino);
679b4884f23SAlexey Dobriyan 		pde_free(pde);
680b4884f23SAlexey Dobriyan 	}
681135d5655SAlexey Dobriyan }
682135d5655SAlexey Dobriyan 
6838ce584c7SAl Viro /*
6848ce584c7SAl Viro  * Remove a /proc entry and free it if it's not currently in use.
6858ce584c7SAl Viro  */
6868ce584c7SAl Viro void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
6878ce584c7SAl Viro {
6888ce584c7SAl Viro 	struct proc_dir_entry *de = NULL;
6898ce584c7SAl Viro 	const char *fn = name;
6908ce584c7SAl Viro 	unsigned int len;
6918ce584c7SAl Viro 
692ecf1a3dfSWaiman Long 	write_lock(&proc_subdir_lock);
6938ce584c7SAl Viro 	if (__xlate_proc_name(name, &parent, &fn) != 0) {
694ecf1a3dfSWaiman Long 		write_unlock(&proc_subdir_lock);
6958ce584c7SAl Viro 		return;
6968ce584c7SAl Viro 	}
6978ce584c7SAl Viro 	len = strlen(fn);
6988ce584c7SAl Viro 
699710585d4SNicolas Dichtel 	de = pde_subdir_find(parent, fn, len);
700e06689bfSAlexey Dobriyan 	if (de) {
701d919b33dSAlexey Dobriyan 		if (unlikely(pde_is_permanent(de))) {
702d919b33dSAlexey Dobriyan 			WARN(1, "removing permanent /proc entry '%s'", de->name);
703d919b33dSAlexey Dobriyan 			de = NULL;
704d919b33dSAlexey Dobriyan 		} else {
7054f113437SAlexey Dobriyan 			rb_erase(&de->subdir_node, &parent->subdir);
706d919b33dSAlexey Dobriyan 			if (S_ISDIR(de->mode))
707e06689bfSAlexey Dobriyan 				parent->nlink--;
708e06689bfSAlexey Dobriyan 		}
709e06689bfSAlexey Dobriyan 	}
710ecf1a3dfSWaiman Long 	write_unlock(&proc_subdir_lock);
7118ce584c7SAl Viro 	if (!de) {
7128ce584c7SAl Viro 		WARN(1, "name '%s'\n", name);
7138ce584c7SAl Viro 		return;
7148ce584c7SAl Viro 	}
7158ce584c7SAl Viro 
716866ad9a7SAl Viro 	proc_entry_rundown(de);
717881adb85SAlexey Dobriyan 
718710585d4SNicolas Dichtel 	WARN(pde_subdir_first(de),
719710585d4SNicolas Dichtel 	     "%s: removing non-empty directory '%s/%s', leaking at least '%s'\n",
720710585d4SNicolas Dichtel 	     __func__, de->parent->name, de->name, pde_subdir_first(de)->name);
721135d5655SAlexey Dobriyan 	pde_put(de);
7221da177e4SLinus Torvalds }
723587d4a17SHelight.Xu EXPORT_SYMBOL(remove_proc_entry);
7248ce584c7SAl Viro 
7258ce584c7SAl Viro int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
7268ce584c7SAl Viro {
7278ce584c7SAl Viro 	struct proc_dir_entry *root = NULL, *de, *next;
7288ce584c7SAl Viro 	const char *fn = name;
7298ce584c7SAl Viro 	unsigned int len;
7308ce584c7SAl Viro 
731ecf1a3dfSWaiman Long 	write_lock(&proc_subdir_lock);
7328ce584c7SAl Viro 	if (__xlate_proc_name(name, &parent, &fn) != 0) {
733ecf1a3dfSWaiman Long 		write_unlock(&proc_subdir_lock);
7348ce584c7SAl Viro 		return -ENOENT;
7358ce584c7SAl Viro 	}
7368ce584c7SAl Viro 	len = strlen(fn);
7378ce584c7SAl Viro 
738710585d4SNicolas Dichtel 	root = pde_subdir_find(parent, fn, len);
7398ce584c7SAl Viro 	if (!root) {
740ecf1a3dfSWaiman Long 		write_unlock(&proc_subdir_lock);
7418ce584c7SAl Viro 		return -ENOENT;
7428ce584c7SAl Viro 	}
743d919b33dSAlexey Dobriyan 	if (unlikely(pde_is_permanent(root))) {
744d919b33dSAlexey Dobriyan 		write_unlock(&proc_subdir_lock);
745d919b33dSAlexey Dobriyan 		WARN(1, "removing permanent /proc entry '%s/%s'",
746d919b33dSAlexey Dobriyan 			root->parent->name, root->name);
747d919b33dSAlexey Dobriyan 		return -EINVAL;
748d919b33dSAlexey Dobriyan 	}
7494f113437SAlexey Dobriyan 	rb_erase(&root->subdir_node, &parent->subdir);
750710585d4SNicolas Dichtel 
7518ce584c7SAl Viro 	de = root;
7528ce584c7SAl Viro 	while (1) {
753710585d4SNicolas Dichtel 		next = pde_subdir_first(de);
7548ce584c7SAl Viro 		if (next) {
755f4bf74d8SColin Ian King 			if (unlikely(pde_is_permanent(next))) {
756d919b33dSAlexey Dobriyan 				write_unlock(&proc_subdir_lock);
757d919b33dSAlexey Dobriyan 				WARN(1, "removing permanent /proc entry '%s/%s'",
758d919b33dSAlexey Dobriyan 					next->parent->name, next->name);
759d919b33dSAlexey Dobriyan 				return -EINVAL;
760d919b33dSAlexey Dobriyan 			}
7614f113437SAlexey Dobriyan 			rb_erase(&next->subdir_node, &de->subdir);
7628ce584c7SAl Viro 			de = next;
7638ce584c7SAl Viro 			continue;
7648ce584c7SAl Viro 		}
7658ce584c7SAl Viro 		next = de->parent;
7668ce584c7SAl Viro 		if (S_ISDIR(de->mode))
7678ce584c7SAl Viro 			next->nlink--;
768e06689bfSAlexey Dobriyan 		write_unlock(&proc_subdir_lock);
769e06689bfSAlexey Dobriyan 
770e06689bfSAlexey Dobriyan 		proc_entry_rundown(de);
7718ce584c7SAl Viro 		if (de == root)
7728ce584c7SAl Viro 			break;
7738ce584c7SAl Viro 		pde_put(de);
7748ce584c7SAl Viro 
775ecf1a3dfSWaiman Long 		write_lock(&proc_subdir_lock);
7768ce584c7SAl Viro 		de = next;
7778ce584c7SAl Viro 	}
7788ce584c7SAl Viro 	pde_put(root);
7798ce584c7SAl Viro 	return 0;
7808ce584c7SAl Viro }
7818ce584c7SAl Viro EXPORT_SYMBOL(remove_proc_subtree);
7824a520d27SDavid Howells 
7834a520d27SDavid Howells void *proc_get_parent_data(const struct inode *inode)
7844a520d27SDavid Howells {
7854a520d27SDavid Howells 	struct proc_dir_entry *de = PDE(inode);
7864a520d27SDavid Howells 	return de->parent->data;
7874a520d27SDavid Howells }
7884a520d27SDavid Howells EXPORT_SYMBOL_GPL(proc_get_parent_data);
789a8ca16eaSDavid Howells 
790a8ca16eaSDavid Howells void proc_remove(struct proc_dir_entry *de)
791a8ca16eaSDavid Howells {
792a8ca16eaSDavid Howells 	if (de)
793a8ca16eaSDavid Howells 		remove_proc_subtree(de->name, de->parent);
794a8ca16eaSDavid Howells }
795a8ca16eaSDavid Howells EXPORT_SYMBOL(proc_remove);
796c30480b9SDavid Howells 
797564def71SDavid Howells /*
798564def71SDavid Howells  * Pull a user buffer into memory and pass it to the file's write handler if
799564def71SDavid Howells  * one is supplied.  The ->write() method is permitted to modify the
800564def71SDavid Howells  * kernel-side buffer.
801564def71SDavid Howells  */
802564def71SDavid Howells ssize_t proc_simple_write(struct file *f, const char __user *ubuf, size_t size,
803564def71SDavid Howells 			  loff_t *_pos)
804564def71SDavid Howells {
805564def71SDavid Howells 	struct proc_dir_entry *pde = PDE(file_inode(f));
806564def71SDavid Howells 	char *buf;
807564def71SDavid Howells 	int ret;
808564def71SDavid Howells 
809564def71SDavid Howells 	if (!pde->write)
810564def71SDavid Howells 		return -EACCES;
811564def71SDavid Howells 	if (size == 0 || size > PAGE_SIZE - 1)
812564def71SDavid Howells 		return -EINVAL;
813564def71SDavid Howells 	buf = memdup_user_nul(ubuf, size);
814564def71SDavid Howells 	if (IS_ERR(buf))
815564def71SDavid Howells 		return PTR_ERR(buf);
816564def71SDavid Howells 	ret = pde->write(f, buf, size);
817564def71SDavid Howells 	kfree(buf);
818564def71SDavid Howells 	return ret == 0 ? size : ret;
819564def71SDavid Howells }
820