xref: /openbmc/linux/fs/proc/generic.c (revision 0d72b928)
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 
pde_free(struct proc_dir_entry * pde)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 
proc_match(const char * name,struct proc_dir_entry * de,unsigned int len)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 
pde_subdir_first(struct proc_dir_entry * dir)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 
pde_subdir_next(struct proc_dir_entry * dir)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 
pde_subdir_find(struct proc_dir_entry * dir,const char * name,unsigned int len)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 
pde_subdir_insert(struct proc_dir_entry * dir,struct proc_dir_entry * de)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 
proc_notify_change(struct mnt_idmap * idmap,struct dentry * dentry,struct iattr * iattr)118c1632a0fSChristian Brauner static int proc_notify_change(struct mnt_idmap *idmap,
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 
125c1632a0fSChristian Brauner 	error = setattr_prepare(&nop_mnt_idmap, dentry, iattr);
1261da177e4SLinus Torvalds 	if (error)
1271025774cSChristoph Hellwig 		return error;
1281da177e4SLinus Torvalds 
129c1632a0fSChristian Brauner 	setattr_copy(&nop_mnt_idmap, inode, iattr);
1301da177e4SLinus Torvalds 
131cdf7e8ddSRui Xiang 	proc_set_user(de, inode->i_uid, inode->i_gid);
1321da177e4SLinus Torvalds 	de->mode = inode->i_mode;
1331025774cSChristoph Hellwig 	return 0;
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds 
proc_getattr(struct mnt_idmap * idmap,const struct path * path,struct kstat * stat,u32 request_mask,unsigned int query_flags)136b74d24f7SChristian Brauner static int proc_getattr(struct mnt_idmap *idmap,
137549c7297SChristian Brauner 			const struct path *path, struct kstat *stat,
138a528d35eSDavid Howells 			u32 request_mask, unsigned int query_flags)
1392b579beeSMiklos Szeredi {
140a528d35eSDavid Howells 	struct inode *inode = d_inode(path->dentry);
1416bee55f9SAlexander Kuleshov 	struct proc_dir_entry *de = PDE(inode);
142e06689bfSAlexey Dobriyan 	if (de) {
143e06689bfSAlexey Dobriyan 		nlink_t nlink = READ_ONCE(de->nlink);
144e06689bfSAlexey Dobriyan 		if (nlink > 0) {
145e06689bfSAlexey Dobriyan 			set_nlink(inode, nlink);
146e06689bfSAlexey Dobriyan 		}
147e06689bfSAlexey Dobriyan 	}
1482b579beeSMiklos Szeredi 
149*0d72b928SJeff Layton 	generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
1502b579beeSMiklos Szeredi 	return 0;
1512b579beeSMiklos Szeredi }
1522b579beeSMiklos Szeredi 
153c5ef1c42SArjan van de Ven static const struct inode_operations proc_file_inode_operations = {
1541da177e4SLinus Torvalds 	.setattr	= proc_notify_change,
1551da177e4SLinus Torvalds };
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds /*
1581da177e4SLinus Torvalds  * This function parses a name such as "tty/driver/serial", and
1591da177e4SLinus Torvalds  * returns the struct proc_dir_entry for "/proc/tty/driver", and
1601da177e4SLinus Torvalds  * returns "serial" in residual.
1611da177e4SLinus Torvalds  */
__xlate_proc_name(const char * name,struct proc_dir_entry ** ret,const char ** residual)162e17a5765SAlexey Dobriyan static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret,
163e17a5765SAlexey Dobriyan 			     const char **residual)
1641da177e4SLinus Torvalds {
1651da177e4SLinus Torvalds 	const char     		*cp = name, *next;
1661da177e4SLinus Torvalds 	struct proc_dir_entry	*de;
1671da177e4SLinus Torvalds 
168b793cd9aSAlexey Dobriyan 	de = *ret ?: &proc_root;
169b793cd9aSAlexey Dobriyan 	while ((next = strchr(cp, '/')) != NULL) {
1705f6354eaSAlexey Dobriyan 		de = pde_subdir_find(de, cp, next - cp);
17112bac0d9SAlexey Dobriyan 		if (!de) {
17212bac0d9SAlexey Dobriyan 			WARN(1, "name '%s'\n", name);
173e17a5765SAlexey Dobriyan 			return -ENOENT;
17412bac0d9SAlexey Dobriyan 		}
1755f6354eaSAlexey Dobriyan 		cp = next + 1;
1761da177e4SLinus Torvalds 	}
1771da177e4SLinus Torvalds 	*residual = cp;
1781da177e4SLinus Torvalds 	*ret = de;
179e17a5765SAlexey Dobriyan 	return 0;
180e17a5765SAlexey Dobriyan }
181e17a5765SAlexey Dobriyan 
xlate_proc_name(const char * name,struct proc_dir_entry ** ret,const char ** residual)182e17a5765SAlexey Dobriyan static int xlate_proc_name(const char *name, struct proc_dir_entry **ret,
183e17a5765SAlexey Dobriyan 			   const char **residual)
184e17a5765SAlexey Dobriyan {
185e17a5765SAlexey Dobriyan 	int rv;
186e17a5765SAlexey Dobriyan 
187ecf1a3dfSWaiman Long 	read_lock(&proc_subdir_lock);
188e17a5765SAlexey Dobriyan 	rv = __xlate_proc_name(name, ret, residual);
189ecf1a3dfSWaiman Long 	read_unlock(&proc_subdir_lock);
190e17a5765SAlexey Dobriyan 	return rv;
1911da177e4SLinus Torvalds }
1921da177e4SLinus Torvalds 
1939a185409SAlexey Dobriyan static DEFINE_IDA(proc_inum_ida);
1941da177e4SLinus Torvalds 
19567935df4SAlexey Dobriyan #define PROC_DYNAMIC_FIRST 0xF0000000U
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds /*
1981da177e4SLinus Torvalds  * Return an inode number between PROC_DYNAMIC_FIRST and
1991da177e4SLinus Torvalds  * 0xffffffff, or zero on failure.
2001da177e4SLinus Torvalds  */
proc_alloc_inum(unsigned int * inum)20133d6dce6SEric W. Biederman int proc_alloc_inum(unsigned int *inum)
2021da177e4SLinus Torvalds {
203cde1b693SHeiner Kallweit 	int i;
2041da177e4SLinus Torvalds 
205cde1b693SHeiner Kallweit 	i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1,
206cde1b693SHeiner Kallweit 			   GFP_KERNEL);
207cde1b693SHeiner Kallweit 	if (i < 0)
208cde1b693SHeiner Kallweit 		return i;
2091da177e4SLinus Torvalds 
210cde1b693SHeiner Kallweit 	*inum = PROC_DYNAMIC_FIRST + (unsigned int)i;
211cc996099SAlexey Dobriyan 	return 0;
21267935df4SAlexey Dobriyan }
2131da177e4SLinus Torvalds 
proc_free_inum(unsigned int inum)21433d6dce6SEric W. Biederman void proc_free_inum(unsigned int inum)
2151da177e4SLinus Torvalds {
216cde1b693SHeiner Kallweit 	ida_simple_remove(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST);
2171da177e4SLinus Torvalds }
2181da177e4SLinus Torvalds 
proc_misc_d_revalidate(struct dentry * dentry,unsigned int flags)2191da4d377SAlexey Dobriyan static int proc_misc_d_revalidate(struct dentry *dentry, unsigned int flags)
2201da4d377SAlexey Dobriyan {
2211da4d377SAlexey Dobriyan 	if (flags & LOOKUP_RCU)
2221da4d377SAlexey Dobriyan 		return -ECHILD;
2231da4d377SAlexey Dobriyan 
2241da4d377SAlexey Dobriyan 	if (atomic_read(&PDE(d_inode(dentry))->in_use) < 0)
2251da4d377SAlexey Dobriyan 		return 0; /* revalidate */
2261da4d377SAlexey Dobriyan 	return 1;
2271da4d377SAlexey Dobriyan }
2281da4d377SAlexey Dobriyan 
proc_misc_d_delete(const struct dentry * dentry)2291da4d377SAlexey Dobriyan static int proc_misc_d_delete(const struct dentry *dentry)
2301da4d377SAlexey Dobriyan {
2311da4d377SAlexey Dobriyan 	return atomic_read(&PDE(d_inode(dentry))->in_use) < 0;
2321da4d377SAlexey Dobriyan }
2331da4d377SAlexey Dobriyan 
2341da4d377SAlexey Dobriyan static const struct dentry_operations proc_misc_dentry_ops = {
2351da4d377SAlexey Dobriyan 	.d_revalidate	= proc_misc_d_revalidate,
2361da4d377SAlexey Dobriyan 	.d_delete	= proc_misc_d_delete,
2371da4d377SAlexey Dobriyan };
2381da4d377SAlexey Dobriyan 
2391da177e4SLinus Torvalds /*
2401da177e4SLinus Torvalds  * Don't create negative dentries here, return -ENOENT by hand
2411da177e4SLinus Torvalds  * instead.
2421da177e4SLinus Torvalds  */
proc_lookup_de(struct inode * dir,struct dentry * dentry,struct proc_dir_entry * de)24393ad5bc6SAlexey Dobriyan struct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,
24493ad5bc6SAlexey Dobriyan 			      struct proc_dir_entry *de)
2451da177e4SLinus Torvalds {
246d3d009cbSAl Viro 	struct inode *inode;
2471da177e4SLinus Torvalds 
248ecf1a3dfSWaiman Long 	read_lock(&proc_subdir_lock);
249710585d4SNicolas Dichtel 	de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len);
250710585d4SNicolas Dichtel 	if (de) {
251135d5655SAlexey Dobriyan 		pde_get(de);
252ecf1a3dfSWaiman Long 		read_unlock(&proc_subdir_lock);
2536d1b6e4eSAlexey Dobriyan 		inode = proc_get_inode(dir->i_sb, de);
254d3d009cbSAl Viro 		if (!inode)
255d3d009cbSAl Viro 			return ERR_PTR(-ENOMEM);
2561fde6f21SAlexey Dobriyan 		d_set_d_op(dentry, de->proc_dops);
257888e2b03SAl Viro 		return d_splice_alias(inode, dentry);
2581da177e4SLinus Torvalds 	}
259ecf1a3dfSWaiman Long 	read_unlock(&proc_subdir_lock);
260d3d009cbSAl Viro 	return ERR_PTR(-ENOENT);
2611da177e4SLinus Torvalds }
2621da177e4SLinus Torvalds 
proc_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)263e9720acdSPavel Emelyanov struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
26400cd8dd3SAl Viro 		unsigned int flags)
265e9720acdSPavel Emelyanov {
2666814ef2dSAlexey Gladkov 	struct proc_fs_info *fs_info = proc_sb_info(dir->i_sb);
2676814ef2dSAlexey Gladkov 
2686814ef2dSAlexey Gladkov 	if (fs_info->pidonly == PROC_PIDONLY_ON)
2696814ef2dSAlexey Gladkov 		return ERR_PTR(-ENOENT);
2706814ef2dSAlexey Gladkov 
27193ad5bc6SAlexey Dobriyan 	return proc_lookup_de(dir, dentry, PDE(dir));
272e9720acdSPavel Emelyanov }
273e9720acdSPavel Emelyanov 
2741da177e4SLinus Torvalds /*
2751da177e4SLinus Torvalds  * This returns non-zero if at EOF, so that the /proc
2761da177e4SLinus Torvalds  * root directory can use this and check if it should
2771da177e4SLinus Torvalds  * continue with the <pid> entries..
2781da177e4SLinus Torvalds  *
2791da177e4SLinus Torvalds  * Note that the VFS-layer doesn't care about the return
2801da177e4SLinus Torvalds  * value of the readdir() call, as long as it's non-negative
2811da177e4SLinus Torvalds  * for success..
2821da177e4SLinus Torvalds  */
proc_readdir_de(struct file * file,struct dir_context * ctx,struct proc_dir_entry * de)28393ad5bc6SAlexey Dobriyan int proc_readdir_de(struct file *file, struct dir_context *ctx,
28493ad5bc6SAlexey Dobriyan 		    struct proc_dir_entry *de)
2851da177e4SLinus Torvalds {
2861da177e4SLinus Torvalds 	int i;
2871da177e4SLinus Torvalds 
288f0c3b509SAl Viro 	if (!dir_emit_dots(file, ctx))
289f0c3b509SAl Viro 		return 0;
290f0c3b509SAl Viro 
2918d48b2e0SAlexey Dobriyan 	i = ctx->pos - 2;
292ecf1a3dfSWaiman Long 	read_lock(&proc_subdir_lock);
293710585d4SNicolas Dichtel 	de = pde_subdir_first(de);
2941da177e4SLinus Torvalds 	for (;;) {
2951da177e4SLinus Torvalds 		if (!de) {
296ecf1a3dfSWaiman Long 			read_unlock(&proc_subdir_lock);
297f0c3b509SAl Viro 			return 0;
2981da177e4SLinus Torvalds 		}
2991da177e4SLinus Torvalds 		if (!i)
3001da177e4SLinus Torvalds 			break;
301710585d4SNicolas Dichtel 		de = pde_subdir_next(de);
3021da177e4SLinus Torvalds 		i--;
3031da177e4SLinus Torvalds 	}
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 	do {
30659cd0cbcSDarrick J. Wong 		struct proc_dir_entry *next;
307135d5655SAlexey Dobriyan 		pde_get(de);
308ecf1a3dfSWaiman Long 		read_unlock(&proc_subdir_lock);
309f0c3b509SAl Viro 		if (!dir_emit(ctx, de->name, de->namelen,
310f0c3b509SAl Viro 			    de->low_ino, de->mode >> 12)) {
311135d5655SAlexey Dobriyan 			pde_put(de);
312f0c3b509SAl Viro 			return 0;
31359cd0cbcSDarrick J. Wong 		}
314f0c3b509SAl Viro 		ctx->pos++;
3158d48b2e0SAlexey Dobriyan 		read_lock(&proc_subdir_lock);
316710585d4SNicolas Dichtel 		next = pde_subdir_next(de);
317135d5655SAlexey Dobriyan 		pde_put(de);
31859cd0cbcSDarrick J. Wong 		de = next;
3191da177e4SLinus Torvalds 	} while (de);
320ecf1a3dfSWaiman Long 	read_unlock(&proc_subdir_lock);
321fd3930f7SLinus Torvalds 	return 1;
3221da177e4SLinus Torvalds }
3231da177e4SLinus Torvalds 
proc_readdir(struct file * file,struct dir_context * ctx)324f0c3b509SAl Viro int proc_readdir(struct file *file, struct dir_context *ctx)
325e9720acdSPavel Emelyanov {
326f0c3b509SAl Viro 	struct inode *inode = file_inode(file);
3276814ef2dSAlexey Gladkov 	struct proc_fs_info *fs_info = proc_sb_info(inode->i_sb);
3286814ef2dSAlexey Gladkov 
3296814ef2dSAlexey Gladkov 	if (fs_info->pidonly == PROC_PIDONLY_ON)
3306814ef2dSAlexey Gladkov 		return 1;
331e9720acdSPavel Emelyanov 
33293ad5bc6SAlexey Dobriyan 	return proc_readdir_de(file, ctx, PDE(inode));
333e9720acdSPavel Emelyanov }
334e9720acdSPavel Emelyanov 
3351da177e4SLinus Torvalds /*
3361da177e4SLinus Torvalds  * These are the generic /proc directory operations. They
3371da177e4SLinus Torvalds  * use the in-memory "struct proc_dir_entry" tree to parse
3381da177e4SLinus Torvalds  * the /proc directory.
3391da177e4SLinus Torvalds  */
34000977a59SArjan van de Ven static const struct file_operations proc_dir_operations = {
341b4df2b92SAlexey Dobriyan 	.llseek			= generic_file_llseek,
3421da177e4SLinus Torvalds 	.read			= generic_read_dir,
343f50752eaSAl Viro 	.iterate_shared		= proc_readdir,
3441da177e4SLinus Torvalds };
3451da177e4SLinus Torvalds 
proc_net_d_revalidate(struct dentry * dentry,unsigned int flags)346c6c75dedSAlexey Dobriyan static int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags)
347c6c75dedSAlexey Dobriyan {
348c6c75dedSAlexey Dobriyan 	return 0;
349c6c75dedSAlexey Dobriyan }
350c6c75dedSAlexey Dobriyan 
351c6c75dedSAlexey Dobriyan const struct dentry_operations proc_net_dentry_ops = {
352c6c75dedSAlexey Dobriyan 	.d_revalidate	= proc_net_d_revalidate,
353c6c75dedSAlexey Dobriyan 	.d_delete	= always_delete_dentry,
354c6c75dedSAlexey Dobriyan };
355c6c75dedSAlexey Dobriyan 
3561da177e4SLinus Torvalds /*
3571da177e4SLinus Torvalds  * proc directories can do almost nothing..
3581da177e4SLinus Torvalds  */
359c5ef1c42SArjan van de Ven static const struct inode_operations proc_dir_inode_operations = {
3601da177e4SLinus Torvalds 	.lookup		= proc_lookup,
3612b579beeSMiklos Szeredi 	.getattr	= proc_getattr,
3621da177e4SLinus Torvalds 	.setattr	= proc_notify_change,
3631da177e4SLinus Torvalds };
3641da177e4SLinus Torvalds 
36561172eaeSChristoph Hellwig /* returns the registered entry, or frees dp and returns NULL on failure */
proc_register(struct proc_dir_entry * dir,struct proc_dir_entry * dp)36661172eaeSChristoph Hellwig struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
36761172eaeSChristoph Hellwig 		struct proc_dir_entry *dp)
3681da177e4SLinus Torvalds {
36961172eaeSChristoph Hellwig 	if (proc_alloc_inum(&dp->low_ino))
37061172eaeSChristoph Hellwig 		goto out_free_entry;
37164a07bd8SSteven Rostedt 
372ecf1a3dfSWaiman Long 	write_lock(&proc_subdir_lock);
373710585d4SNicolas Dichtel 	dp->parent = dir;
374b208d54bSDebabrata Banerjee 	if (pde_subdir_insert(dir, dp) == false) {
37587ebdc00SAndrew Morton 		WARN(1, "proc_dir_entry '%s/%s' already registered\n",
376665020c3SAlexey Dobriyan 		     dir->name, dp->name);
377ecf1a3dfSWaiman Long 		write_unlock(&proc_subdir_lock);
37861172eaeSChristoph Hellwig 		goto out_free_inum;
379b208d54bSDebabrata Banerjee 	}
380e06689bfSAlexey Dobriyan 	dir->nlink++;
381ecf1a3dfSWaiman Long 	write_unlock(&proc_subdir_lock);
38299fc06dfSChangli Gao 
38361172eaeSChristoph Hellwig 	return dp;
38461172eaeSChristoph Hellwig out_free_inum:
38561172eaeSChristoph Hellwig 	proc_free_inum(dp->low_ino);
38661172eaeSChristoph Hellwig out_free_entry:
38761172eaeSChristoph Hellwig 	pde_free(dp);
38861172eaeSChristoph Hellwig 	return NULL;
3891da177e4SLinus Torvalds }
3901da177e4SLinus Torvalds 
__proc_create(struct proc_dir_entry ** parent,const char * name,umode_t mode,nlink_t nlink)3912d3a4e36SAlexey Dobriyan static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
3921da177e4SLinus Torvalds 					  const char *name,
393d161a13fSAl Viro 					  umode_t mode,
3941da177e4SLinus Torvalds 					  nlink_t nlink)
3951da177e4SLinus Torvalds {
3961da177e4SLinus Torvalds 	struct proc_dir_entry *ent = NULL;
397dbcdb504SAlexey Dobriyan 	const char *fn;
398dbcdb504SAlexey Dobriyan 	struct qstr qstr;
3991da177e4SLinus Torvalds 
4007cee4e00SAlexey Dobriyan 	if (xlate_proc_name(name, parent, &fn) != 0)
4011da177e4SLinus Torvalds 		goto out;
402dbcdb504SAlexey Dobriyan 	qstr.name = fn;
403dbcdb504SAlexey Dobriyan 	qstr.len = strlen(fn);
404dbcdb504SAlexey Dobriyan 	if (qstr.len == 0 || qstr.len >= 256) {
405dbcdb504SAlexey Dobriyan 		WARN(1, "name len %u\n", qstr.len);
406dbcdb504SAlexey Dobriyan 		return NULL;
407dbcdb504SAlexey Dobriyan 	}
408b77d70dbSAlexey Dobriyan 	if (qstr.len == 1 && fn[0] == '.') {
409b77d70dbSAlexey Dobriyan 		WARN(1, "name '.'\n");
410b77d70dbSAlexey Dobriyan 		return NULL;
411b77d70dbSAlexey Dobriyan 	}
412b77d70dbSAlexey Dobriyan 	if (qstr.len == 2 && fn[0] == '.' && fn[1] == '.') {
413b77d70dbSAlexey Dobriyan 		WARN(1, "name '..'\n");
414b77d70dbSAlexey Dobriyan 		return NULL;
415b77d70dbSAlexey Dobriyan 	}
416dbcdb504SAlexey Dobriyan 	if (*parent == &proc_root && name_to_int(&qstr) != ~0U) {
417dbcdb504SAlexey Dobriyan 		WARN(1, "create '/proc/%s' by hand\n", qstr.name);
418dbcdb504SAlexey Dobriyan 		return NULL;
419dbcdb504SAlexey Dobriyan 	}
420eb6d38d5SEric W. Biederman 	if (is_empty_pde(*parent)) {
421eb6d38d5SEric W. Biederman 		WARN(1, "attempt to add to permanently empty directory");
422eb6d38d5SEric W. Biederman 		return NULL;
423eb6d38d5SEric W. Biederman 	}
4241da177e4SLinus Torvalds 
425b4884f23SAlexey Dobriyan 	ent = kmem_cache_zalloc(proc_dir_entry_cache, GFP_KERNEL);
42617baa2a2Syan 	if (!ent)
42717baa2a2Syan 		goto out;
4281da177e4SLinus Torvalds 
42924074a35SDavid Howells 	if (qstr.len + 1 <= SIZEOF_PDE_INLINE_NAME) {
430b4884f23SAlexey Dobriyan 		ent->name = ent->inline_name;
431b4884f23SAlexey Dobriyan 	} else {
432b4884f23SAlexey Dobriyan 		ent->name = kmalloc(qstr.len + 1, GFP_KERNEL);
433b4884f23SAlexey Dobriyan 		if (!ent->name) {
434b4884f23SAlexey Dobriyan 			pde_free(ent);
435b4884f23SAlexey Dobriyan 			return NULL;
436b4884f23SAlexey Dobriyan 		}
437b4884f23SAlexey Dobriyan 	}
438b4884f23SAlexey Dobriyan 
439dbcdb504SAlexey Dobriyan 	memcpy(ent->name, fn, qstr.len + 1);
440dbcdb504SAlexey Dobriyan 	ent->namelen = qstr.len;
4411da177e4SLinus Torvalds 	ent->mode = mode;
4421da177e4SLinus Torvalds 	ent->nlink = nlink;
4434f113437SAlexey Dobriyan 	ent->subdir = RB_ROOT;
4449cdd83e3SAlexey Dobriyan 	refcount_set(&ent->refcnt, 1);
445786d7e16SAlexey Dobriyan 	spin_lock_init(&ent->pde_unload_lock);
446881adb85SAlexey Dobriyan 	INIT_LIST_HEAD(&ent->pde_openers);
447c110486fSDmitry Torokhov 	proc_set_user(ent, (*parent)->uid, (*parent)->gid);
448c110486fSDmitry Torokhov 
4491fde6f21SAlexey Dobriyan 	ent->proc_dops = &proc_misc_dentry_ops;
45070551977SAlexey Dobriyan 	/* Revalidate everything under /proc/${pid}/net */
45170551977SAlexey Dobriyan 	if ((*parent)->proc_dops == &proc_net_dentry_ops)
45270551977SAlexey Dobriyan 		pde_force_lookup(ent);
4531fde6f21SAlexey Dobriyan 
4541da177e4SLinus Torvalds out:
4551da177e4SLinus Torvalds 	return ent;
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds 
proc_symlink(const char * name,struct proc_dir_entry * parent,const char * dest)4581da177e4SLinus Torvalds struct proc_dir_entry *proc_symlink(const char *name,
4591da177e4SLinus Torvalds 		struct proc_dir_entry *parent, const char *dest)
4601da177e4SLinus Torvalds {
4611da177e4SLinus Torvalds 	struct proc_dir_entry *ent;
4621da177e4SLinus Torvalds 
4632d3a4e36SAlexey Dobriyan 	ent = __proc_create(&parent, name,
4641da177e4SLinus Torvalds 			  (S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO),1);
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds 	if (ent) {
4671da177e4SLinus Torvalds 		ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL);
4681da177e4SLinus Torvalds 		if (ent->data) {
4691da177e4SLinus Torvalds 			strcpy((char*)ent->data,dest);
470d443b9fdSAl Viro 			ent->proc_iops = &proc_link_inode_operations;
47161172eaeSChristoph Hellwig 			ent = proc_register(parent, ent);
4721da177e4SLinus Torvalds 		} else {
473b4884f23SAlexey Dobriyan 			pde_free(ent);
4741da177e4SLinus Torvalds 			ent = NULL;
4751da177e4SLinus Torvalds 		}
4761da177e4SLinus Torvalds 	}
4771da177e4SLinus Torvalds 	return ent;
4781da177e4SLinus Torvalds }
479587d4a17SHelight.Xu EXPORT_SYMBOL(proc_symlink);
4801da177e4SLinus Torvalds 
_proc_mkdir(const char * name,umode_t mode,struct proc_dir_entry * parent,void * data,bool force_lookup)481c6c75dedSAlexey Dobriyan struct proc_dir_entry *_proc_mkdir(const char *name, umode_t mode,
482c6c75dedSAlexey Dobriyan 		struct proc_dir_entry *parent, void *data, bool force_lookup)
4831da177e4SLinus Torvalds {
4841da177e4SLinus Torvalds 	struct proc_dir_entry *ent;
4851da177e4SLinus Torvalds 
486270b5ac2SDavid Howells 	if (mode == 0)
487270b5ac2SDavid Howells 		mode = S_IRUGO | S_IXUGO;
488270b5ac2SDavid Howells 
4892d3a4e36SAlexey Dobriyan 	ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
4901da177e4SLinus Torvalds 	if (ent) {
491270b5ac2SDavid Howells 		ent->data = data;
492d56c0d45SAlexey Dobriyan 		ent->proc_dir_ops = &proc_dir_operations;
493d443b9fdSAl Viro 		ent->proc_iops = &proc_dir_inode_operations;
494c6c75dedSAlexey Dobriyan 		if (force_lookup) {
495c6c75dedSAlexey Dobriyan 			pde_force_lookup(ent);
496c6c75dedSAlexey Dobriyan 		}
49761172eaeSChristoph Hellwig 		ent = proc_register(parent, ent);
4981da177e4SLinus Torvalds 	}
4991da177e4SLinus Torvalds 	return ent;
5001da177e4SLinus Torvalds }
501c6c75dedSAlexey Dobriyan EXPORT_SYMBOL_GPL(_proc_mkdir);
502c6c75dedSAlexey Dobriyan 
proc_mkdir_data(const char * name,umode_t mode,struct proc_dir_entry * parent,void * data)503c6c75dedSAlexey Dobriyan struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
504c6c75dedSAlexey Dobriyan 		struct proc_dir_entry *parent, void *data)
505c6c75dedSAlexey Dobriyan {
506c6c75dedSAlexey Dobriyan 	return _proc_mkdir(name, mode, parent, data, false);
507c6c75dedSAlexey Dobriyan }
508270b5ac2SDavid Howells EXPORT_SYMBOL_GPL(proc_mkdir_data);
5091da177e4SLinus Torvalds 
proc_mkdir_mode(const char * name,umode_t mode,struct proc_dir_entry * parent)510270b5ac2SDavid Howells struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
51178e92b99SDenis V. Lunev 				       struct proc_dir_entry *parent)
51278e92b99SDenis V. Lunev {
513270b5ac2SDavid Howells 	return proc_mkdir_data(name, mode, parent, NULL);
51478e92b99SDenis V. Lunev }
515270b5ac2SDavid Howells EXPORT_SYMBOL(proc_mkdir_mode);
51678e92b99SDenis V. Lunev 
proc_mkdir(const char * name,struct proc_dir_entry * parent)5171da177e4SLinus Torvalds struct proc_dir_entry *proc_mkdir(const char *name,
5181da177e4SLinus Torvalds 		struct proc_dir_entry *parent)
5191da177e4SLinus Torvalds {
520270b5ac2SDavid Howells 	return proc_mkdir_data(name, 0, parent, NULL);
5211da177e4SLinus Torvalds }
522587d4a17SHelight.Xu EXPORT_SYMBOL(proc_mkdir);
5231da177e4SLinus Torvalds 
proc_create_mount_point(const char * name)524eb6d38d5SEric W. Biederman struct proc_dir_entry *proc_create_mount_point(const char *name)
525eb6d38d5SEric W. Biederman {
526eb6d38d5SEric W. Biederman 	umode_t mode = S_IFDIR | S_IRUGO | S_IXUGO;
527eb6d38d5SEric W. Biederman 	struct proc_dir_entry *ent, *parent = NULL;
528eb6d38d5SEric W. Biederman 
529eb6d38d5SEric W. Biederman 	ent = __proc_create(&parent, name, mode, 2);
530eb6d38d5SEric W. Biederman 	if (ent) {
531eb6d38d5SEric W. Biederman 		ent->data = NULL;
532d56c0d45SAlexey Dobriyan 		ent->proc_dir_ops = NULL;
533eb6d38d5SEric W. Biederman 		ent->proc_iops = NULL;
53461172eaeSChristoph Hellwig 		ent = proc_register(parent, ent);
535eb6d38d5SEric W. Biederman 	}
536eb6d38d5SEric W. Biederman 	return ent;
537eb6d38d5SEric W. Biederman }
538f97df70bSSeth Forshee EXPORT_SYMBOL(proc_create_mount_point);
539eb6d38d5SEric W. Biederman 
proc_create_reg(const char * name,umode_t mode,struct proc_dir_entry ** parent,void * data)5407aed53d1SChristoph Hellwig struct proc_dir_entry *proc_create_reg(const char *name, umode_t mode,
5417aed53d1SChristoph Hellwig 		struct proc_dir_entry **parent, void *data)
5422d3a4e36SAlexey Dobriyan {
5437aed53d1SChristoph Hellwig 	struct proc_dir_entry *p;
5447aed53d1SChristoph Hellwig 
5452d3a4e36SAlexey Dobriyan 	if ((mode & S_IFMT) == 0)
5462d3a4e36SAlexey Dobriyan 		mode |= S_IFREG;
5477aed53d1SChristoph Hellwig 	if ((mode & S_IALLUGO) == 0)
5487aed53d1SChristoph Hellwig 		mode |= S_IRUGO;
5497aed53d1SChristoph Hellwig 	if (WARN_ON_ONCE(!S_ISREG(mode)))
550b6cdc731SAl Viro 		return NULL;
5517aed53d1SChristoph Hellwig 
5527aed53d1SChristoph Hellwig 	p = __proc_create(parent, name, mode, 1);
5537aed53d1SChristoph Hellwig 	if (p) {
5547aed53d1SChristoph Hellwig 		p->proc_iops = &proc_file_inode_operations;
5557aed53d1SChristoph Hellwig 		p->data = data;
5562d3a4e36SAlexey Dobriyan 	}
5577aed53d1SChristoph Hellwig 	return p;
5587aed53d1SChristoph Hellwig }
5597aed53d1SChristoph Hellwig 
pde_set_flags(struct proc_dir_entry * pde)560d919b33dSAlexey Dobriyan static inline void pde_set_flags(struct proc_dir_entry *pde)
561d919b33dSAlexey Dobriyan {
562d919b33dSAlexey Dobriyan 	if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
563d919b33dSAlexey Dobriyan 		pde->flags |= PROC_ENTRY_PERMANENT;
564d919b33dSAlexey Dobriyan }
565d919b33dSAlexey Dobriyan 
proc_create_data(const char * name,umode_t mode,struct proc_dir_entry * parent,const struct proc_ops * proc_ops,void * data)5667aed53d1SChristoph Hellwig struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
5677aed53d1SChristoph Hellwig 		struct proc_dir_entry *parent,
568d56c0d45SAlexey Dobriyan 		const struct proc_ops *proc_ops, void *data)
5697aed53d1SChristoph Hellwig {
5707aed53d1SChristoph Hellwig 	struct proc_dir_entry *p;
5712d3a4e36SAlexey Dobriyan 
5727aed53d1SChristoph Hellwig 	p = proc_create_reg(name, mode, &parent, data);
5737aed53d1SChristoph Hellwig 	if (!p)
5742d3a4e36SAlexey Dobriyan 		return NULL;
575d56c0d45SAlexey Dobriyan 	p->proc_ops = proc_ops;
576d919b33dSAlexey Dobriyan 	pde_set_flags(p);
5777aed53d1SChristoph Hellwig 	return proc_register(parent, p);
5782d3a4e36SAlexey Dobriyan }
579587d4a17SHelight.Xu EXPORT_SYMBOL(proc_create_data);
580271a15eaSDavid Howells 
proc_create(const char * name,umode_t mode,struct proc_dir_entry * parent,const struct proc_ops * proc_ops)581855d9765SAlexey Dobriyan struct proc_dir_entry *proc_create(const char *name, umode_t mode,
582855d9765SAlexey Dobriyan 				   struct proc_dir_entry *parent,
583d56c0d45SAlexey Dobriyan 				   const struct proc_ops *proc_ops)
584855d9765SAlexey Dobriyan {
585d56c0d45SAlexey Dobriyan 	return proc_create_data(name, mode, parent, proc_ops, NULL);
586855d9765SAlexey Dobriyan }
587855d9765SAlexey Dobriyan EXPORT_SYMBOL(proc_create);
588855d9765SAlexey Dobriyan 
proc_seq_open(struct inode * inode,struct file * file)589fddda2b7SChristoph Hellwig static int proc_seq_open(struct inode *inode, struct file *file)
590fddda2b7SChristoph Hellwig {
591fddda2b7SChristoph Hellwig 	struct proc_dir_entry *de = PDE(inode);
592fddda2b7SChristoph Hellwig 
59344414d82SChristoph Hellwig 	if (de->state_size)
59444414d82SChristoph Hellwig 		return seq_open_private(file, de->seq_ops, de->state_size);
595fddda2b7SChristoph Hellwig 	return seq_open(file, de->seq_ops);
596fddda2b7SChristoph Hellwig }
597fddda2b7SChristoph Hellwig 
proc_seq_release(struct inode * inode,struct file * file)598877f919eSChunyu Hu static int proc_seq_release(struct inode *inode, struct file *file)
599877f919eSChunyu Hu {
600877f919eSChunyu Hu 	struct proc_dir_entry *de = PDE(inode);
601877f919eSChunyu Hu 
602877f919eSChunyu Hu 	if (de->state_size)
603877f919eSChunyu Hu 		return seq_release_private(inode, file);
604877f919eSChunyu Hu 	return seq_release(inode, file);
605877f919eSChunyu Hu }
606877f919eSChunyu Hu 
607d56c0d45SAlexey Dobriyan static const struct proc_ops proc_seq_ops = {
608d919b33dSAlexey Dobriyan 	/* not permanent -- can call into arbitrary seq_operations */
609d56c0d45SAlexey Dobriyan 	.proc_open	= proc_seq_open,
610b24c30c6SChristoph Hellwig 	.proc_read_iter	= seq_read_iter,
611d56c0d45SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
612d56c0d45SAlexey Dobriyan 	.proc_release	= proc_seq_release,
613fddda2b7SChristoph Hellwig };
614fddda2b7SChristoph Hellwig 
proc_create_seq_private(const char * name,umode_t mode,struct proc_dir_entry * parent,const struct seq_operations * ops,unsigned int state_size,void * data)61544414d82SChristoph Hellwig struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode,
616fddda2b7SChristoph Hellwig 		struct proc_dir_entry *parent, const struct seq_operations *ops,
61744414d82SChristoph Hellwig 		unsigned int state_size, void *data)
618fddda2b7SChristoph Hellwig {
619fddda2b7SChristoph Hellwig 	struct proc_dir_entry *p;
620fddda2b7SChristoph Hellwig 
621fddda2b7SChristoph Hellwig 	p = proc_create_reg(name, mode, &parent, data);
622fddda2b7SChristoph Hellwig 	if (!p)
623fddda2b7SChristoph Hellwig 		return NULL;
624d56c0d45SAlexey Dobriyan 	p->proc_ops = &proc_seq_ops;
625fddda2b7SChristoph Hellwig 	p->seq_ops = ops;
62644414d82SChristoph Hellwig 	p->state_size = state_size;
627fddda2b7SChristoph Hellwig 	return proc_register(parent, p);
628fddda2b7SChristoph Hellwig }
62944414d82SChristoph Hellwig EXPORT_SYMBOL(proc_create_seq_private);
630fddda2b7SChristoph Hellwig 
proc_single_open(struct inode * inode,struct file * file)6313f3942acSChristoph Hellwig static int proc_single_open(struct inode *inode, struct file *file)
6323f3942acSChristoph Hellwig {
6333f3942acSChristoph Hellwig 	struct proc_dir_entry *de = PDE(inode);
6343f3942acSChristoph Hellwig 
6353f3942acSChristoph Hellwig 	return single_open(file, de->single_show, de->data);
6363f3942acSChristoph Hellwig }
6373f3942acSChristoph Hellwig 
638d56c0d45SAlexey Dobriyan static const struct proc_ops proc_single_ops = {
639d919b33dSAlexey Dobriyan 	/* not permanent -- can call into arbitrary ->single_show */
640d56c0d45SAlexey Dobriyan 	.proc_open	= proc_single_open,
6417cfc630eSGreg Kroah-Hartman 	.proc_read_iter = seq_read_iter,
642d56c0d45SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
643d56c0d45SAlexey Dobriyan 	.proc_release	= single_release,
6443f3942acSChristoph Hellwig };
6453f3942acSChristoph Hellwig 
proc_create_single_data(const char * name,umode_t mode,struct proc_dir_entry * parent,int (* show)(struct seq_file *,void *),void * data)6463f3942acSChristoph Hellwig struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,
6473f3942acSChristoph Hellwig 		struct proc_dir_entry *parent,
6483f3942acSChristoph Hellwig 		int (*show)(struct seq_file *, void *), void *data)
6493f3942acSChristoph Hellwig {
6503f3942acSChristoph Hellwig 	struct proc_dir_entry *p;
6513f3942acSChristoph Hellwig 
6523f3942acSChristoph Hellwig 	p = proc_create_reg(name, mode, &parent, data);
6533f3942acSChristoph Hellwig 	if (!p)
6543f3942acSChristoph Hellwig 		return NULL;
655d56c0d45SAlexey Dobriyan 	p->proc_ops = &proc_single_ops;
6563f3942acSChristoph Hellwig 	p->single_show = show;
6573f3942acSChristoph Hellwig 	return proc_register(parent, p);
6583f3942acSChristoph Hellwig }
6593f3942acSChristoph Hellwig EXPORT_SYMBOL(proc_create_single_data);
6603f3942acSChristoph Hellwig 
proc_set_size(struct proc_dir_entry * de,loff_t size)661271a15eaSDavid Howells void proc_set_size(struct proc_dir_entry *de, loff_t size)
662271a15eaSDavid Howells {
663271a15eaSDavid Howells 	de->size = size;
664271a15eaSDavid Howells }
665271a15eaSDavid Howells EXPORT_SYMBOL(proc_set_size);
666271a15eaSDavid Howells 
proc_set_user(struct proc_dir_entry * de,kuid_t uid,kgid_t gid)667271a15eaSDavid Howells void proc_set_user(struct proc_dir_entry *de, kuid_t uid, kgid_t gid)
668271a15eaSDavid Howells {
669271a15eaSDavid Howells 	de->uid = uid;
670271a15eaSDavid Howells 	de->gid = gid;
671271a15eaSDavid Howells }
672271a15eaSDavid Howells EXPORT_SYMBOL(proc_set_user);
6732d3a4e36SAlexey Dobriyan 
pde_put(struct proc_dir_entry * pde)674135d5655SAlexey Dobriyan void pde_put(struct proc_dir_entry *pde)
675135d5655SAlexey Dobriyan {
6769cdd83e3SAlexey Dobriyan 	if (refcount_dec_and_test(&pde->refcnt)) {
677b4884f23SAlexey Dobriyan 		proc_free_inum(pde->low_ino);
678b4884f23SAlexey Dobriyan 		pde_free(pde);
679b4884f23SAlexey Dobriyan 	}
680135d5655SAlexey Dobriyan }
681135d5655SAlexey Dobriyan 
6828ce584c7SAl Viro /*
6838ce584c7SAl Viro  * Remove a /proc entry and free it if it's not currently in use.
6848ce584c7SAl Viro  */
remove_proc_entry(const char * name,struct proc_dir_entry * parent)6858ce584c7SAl Viro void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
6868ce584c7SAl Viro {
6878ce584c7SAl Viro 	struct proc_dir_entry *de = NULL;
6888ce584c7SAl Viro 	const char *fn = name;
6898ce584c7SAl Viro 	unsigned int len;
6908ce584c7SAl Viro 
691ecf1a3dfSWaiman Long 	write_lock(&proc_subdir_lock);
6928ce584c7SAl Viro 	if (__xlate_proc_name(name, &parent, &fn) != 0) {
693ecf1a3dfSWaiman Long 		write_unlock(&proc_subdir_lock);
6948ce584c7SAl Viro 		return;
6958ce584c7SAl Viro 	}
6968ce584c7SAl Viro 	len = strlen(fn);
6978ce584c7SAl Viro 
698710585d4SNicolas Dichtel 	de = pde_subdir_find(parent, fn, len);
699e06689bfSAlexey Dobriyan 	if (de) {
700d919b33dSAlexey Dobriyan 		if (unlikely(pde_is_permanent(de))) {
701d919b33dSAlexey Dobriyan 			WARN(1, "removing permanent /proc entry '%s'", de->name);
702d919b33dSAlexey Dobriyan 			de = NULL;
703d919b33dSAlexey Dobriyan 		} else {
7044f113437SAlexey Dobriyan 			rb_erase(&de->subdir_node, &parent->subdir);
705d919b33dSAlexey Dobriyan 			if (S_ISDIR(de->mode))
706e06689bfSAlexey Dobriyan 				parent->nlink--;
707e06689bfSAlexey Dobriyan 		}
708e06689bfSAlexey Dobriyan 	}
709ecf1a3dfSWaiman Long 	write_unlock(&proc_subdir_lock);
7108ce584c7SAl Viro 	if (!de) {
7118ce584c7SAl Viro 		WARN(1, "name '%s'\n", name);
7128ce584c7SAl Viro 		return;
7138ce584c7SAl Viro 	}
7148ce584c7SAl Viro 
715866ad9a7SAl Viro 	proc_entry_rundown(de);
716881adb85SAlexey Dobriyan 
717710585d4SNicolas Dichtel 	WARN(pde_subdir_first(de),
718710585d4SNicolas Dichtel 	     "%s: removing non-empty directory '%s/%s', leaking at least '%s'\n",
719710585d4SNicolas Dichtel 	     __func__, de->parent->name, de->name, pde_subdir_first(de)->name);
720135d5655SAlexey Dobriyan 	pde_put(de);
7211da177e4SLinus Torvalds }
722587d4a17SHelight.Xu EXPORT_SYMBOL(remove_proc_entry);
7238ce584c7SAl Viro 
remove_proc_subtree(const char * name,struct proc_dir_entry * parent)7248ce584c7SAl Viro int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
7258ce584c7SAl Viro {
7268ce584c7SAl Viro 	struct proc_dir_entry *root = NULL, *de, *next;
7278ce584c7SAl Viro 	const char *fn = name;
7288ce584c7SAl Viro 	unsigned int len;
7298ce584c7SAl Viro 
730ecf1a3dfSWaiman Long 	write_lock(&proc_subdir_lock);
7318ce584c7SAl Viro 	if (__xlate_proc_name(name, &parent, &fn) != 0) {
732ecf1a3dfSWaiman Long 		write_unlock(&proc_subdir_lock);
7338ce584c7SAl Viro 		return -ENOENT;
7348ce584c7SAl Viro 	}
7358ce584c7SAl Viro 	len = strlen(fn);
7368ce584c7SAl Viro 
737710585d4SNicolas Dichtel 	root = pde_subdir_find(parent, fn, len);
7388ce584c7SAl Viro 	if (!root) {
739ecf1a3dfSWaiman Long 		write_unlock(&proc_subdir_lock);
7408ce584c7SAl Viro 		return -ENOENT;
7418ce584c7SAl Viro 	}
742d919b33dSAlexey Dobriyan 	if (unlikely(pde_is_permanent(root))) {
743d919b33dSAlexey Dobriyan 		write_unlock(&proc_subdir_lock);
744d919b33dSAlexey Dobriyan 		WARN(1, "removing permanent /proc entry '%s/%s'",
745d919b33dSAlexey Dobriyan 			root->parent->name, root->name);
746d919b33dSAlexey Dobriyan 		return -EINVAL;
747d919b33dSAlexey Dobriyan 	}
7484f113437SAlexey Dobriyan 	rb_erase(&root->subdir_node, &parent->subdir);
749710585d4SNicolas Dichtel 
7508ce584c7SAl Viro 	de = root;
7518ce584c7SAl Viro 	while (1) {
752710585d4SNicolas Dichtel 		next = pde_subdir_first(de);
7538ce584c7SAl Viro 		if (next) {
754f4bf74d8SColin Ian King 			if (unlikely(pde_is_permanent(next))) {
755d919b33dSAlexey Dobriyan 				write_unlock(&proc_subdir_lock);
756d919b33dSAlexey Dobriyan 				WARN(1, "removing permanent /proc entry '%s/%s'",
757d919b33dSAlexey Dobriyan 					next->parent->name, next->name);
758d919b33dSAlexey Dobriyan 				return -EINVAL;
759d919b33dSAlexey Dobriyan 			}
7604f113437SAlexey Dobriyan 			rb_erase(&next->subdir_node, &de->subdir);
7618ce584c7SAl Viro 			de = next;
7628ce584c7SAl Viro 			continue;
7638ce584c7SAl Viro 		}
7648ce584c7SAl Viro 		next = de->parent;
7658ce584c7SAl Viro 		if (S_ISDIR(de->mode))
7668ce584c7SAl Viro 			next->nlink--;
767e06689bfSAlexey Dobriyan 		write_unlock(&proc_subdir_lock);
768e06689bfSAlexey Dobriyan 
769e06689bfSAlexey Dobriyan 		proc_entry_rundown(de);
7708ce584c7SAl Viro 		if (de == root)
7718ce584c7SAl Viro 			break;
7728ce584c7SAl Viro 		pde_put(de);
7738ce584c7SAl Viro 
774ecf1a3dfSWaiman Long 		write_lock(&proc_subdir_lock);
7758ce584c7SAl Viro 		de = next;
7768ce584c7SAl Viro 	}
7778ce584c7SAl Viro 	pde_put(root);
7788ce584c7SAl Viro 	return 0;
7798ce584c7SAl Viro }
7808ce584c7SAl Viro EXPORT_SYMBOL(remove_proc_subtree);
7814a520d27SDavid Howells 
proc_get_parent_data(const struct inode * inode)7824a520d27SDavid Howells void *proc_get_parent_data(const struct inode *inode)
7834a520d27SDavid Howells {
7844a520d27SDavid Howells 	struct proc_dir_entry *de = PDE(inode);
7854a520d27SDavid Howells 	return de->parent->data;
7864a520d27SDavid Howells }
7874a520d27SDavid Howells EXPORT_SYMBOL_GPL(proc_get_parent_data);
788a8ca16eaSDavid Howells 
proc_remove(struct proc_dir_entry * de)789a8ca16eaSDavid Howells void proc_remove(struct proc_dir_entry *de)
790a8ca16eaSDavid Howells {
791a8ca16eaSDavid Howells 	if (de)
792a8ca16eaSDavid Howells 		remove_proc_subtree(de->name, de->parent);
793a8ca16eaSDavid Howells }
794a8ca16eaSDavid Howells EXPORT_SYMBOL(proc_remove);
795c30480b9SDavid Howells 
796564def71SDavid Howells /*
797564def71SDavid Howells  * Pull a user buffer into memory and pass it to the file's write handler if
798564def71SDavid Howells  * one is supplied.  The ->write() method is permitted to modify the
799564def71SDavid Howells  * kernel-side buffer.
800564def71SDavid Howells  */
proc_simple_write(struct file * f,const char __user * ubuf,size_t size,loff_t * _pos)801564def71SDavid Howells ssize_t proc_simple_write(struct file *f, const char __user *ubuf, size_t size,
802564def71SDavid Howells 			  loff_t *_pos)
803564def71SDavid Howells {
804564def71SDavid Howells 	struct proc_dir_entry *pde = PDE(file_inode(f));
805564def71SDavid Howells 	char *buf;
806564def71SDavid Howells 	int ret;
807564def71SDavid Howells 
808564def71SDavid Howells 	if (!pde->write)
809564def71SDavid Howells 		return -EACCES;
810564def71SDavid Howells 	if (size == 0 || size > PAGE_SIZE - 1)
811564def71SDavid Howells 		return -EINVAL;
812564def71SDavid Howells 	buf = memdup_user_nul(ubuf, size);
813564def71SDavid Howells 	if (IS_ERR(buf))
814564def71SDavid Howells 		return PTR_ERR(buf);
815564def71SDavid Howells 	ret = pde->write(f, buf, size);
816564def71SDavid Howells 	kfree(buf);
817564def71SDavid Howells 	return ret == 0 ? size : ret;
818564def71SDavid Howells }
819