xref: /openbmc/linux/fs/proc/generic.c (revision 8ce584c7)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * proc/fs/generic.c --- generic routines for the proc-fs
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * This file contains generic proc-fs routines for handling
51da177e4SLinus Torvalds  * directories and files.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Copyright (C) 1991, 1992 Linus Torvalds.
81da177e4SLinus Torvalds  * Copyright (C) 1997 Theodore Ts'o
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #include <linux/errno.h>
121da177e4SLinus Torvalds #include <linux/time.h>
131da177e4SLinus Torvalds #include <linux/proc_fs.h>
141da177e4SLinus Torvalds #include <linux/stat.h>
151025774cSChristoph Hellwig #include <linux/mm.h>
161da177e4SLinus Torvalds #include <linux/module.h>
175a0e3ad6STejun Heo #include <linux/slab.h>
1887ebdc00SAndrew Morton #include <linux/printk.h>
191da177e4SLinus Torvalds #include <linux/mount.h>
201da177e4SLinus Torvalds #include <linux/init.h>
211da177e4SLinus Torvalds #include <linux/idr.h>
221da177e4SLinus Torvalds #include <linux/namei.h>
231da177e4SLinus Torvalds #include <linux/bitops.h>
2464a07bd8SSteven Rostedt #include <linux/spinlock.h>
25786d7e16SAlexey Dobriyan #include <linux/completion.h>
261da177e4SLinus Torvalds #include <asm/uaccess.h>
271da177e4SLinus Torvalds 
28fee781e6SAdrian Bunk #include "internal.h"
29fee781e6SAdrian Bunk 
3064a07bd8SSteven Rostedt DEFINE_SPINLOCK(proc_subdir_lock);
3164a07bd8SSteven Rostedt 
32312ec7e5SAlexey Dobriyan static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de)
331da177e4SLinus Torvalds {
341da177e4SLinus Torvalds 	if (de->namelen != len)
351da177e4SLinus Torvalds 		return 0;
361da177e4SLinus Torvalds 	return !memcmp(name, de->name, len);
371da177e4SLinus Torvalds }
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds /* buffer size is one page but our output routines use some slack for overruns */
401da177e4SLinus Torvalds #define PROC_BLOCK_SIZE	(PAGE_SIZE - 1024)
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds static ssize_t
433dec7f59SAlexey Dobriyan __proc_file_read(struct file *file, char __user *buf, size_t nbytes,
441da177e4SLinus Torvalds 	       loff_t *ppos)
451da177e4SLinus Torvalds {
46496ad9aaSAl Viro 	struct inode * inode = file_inode(file);
471da177e4SLinus Torvalds 	char 	*page;
481da177e4SLinus Torvalds 	ssize_t	retval=0;
491da177e4SLinus Torvalds 	int	eof=0;
501da177e4SLinus Torvalds 	ssize_t	n, count;
511da177e4SLinus Torvalds 	char	*start;
521da177e4SLinus Torvalds 	struct proc_dir_entry * dp;
538b90db0dSLinus Torvalds 	unsigned long long pos;
548b90db0dSLinus Torvalds 
558b90db0dSLinus Torvalds 	/*
568b90db0dSLinus Torvalds 	 * Gaah, please just use "seq_file" instead. The legacy /proc
578b90db0dSLinus Torvalds 	 * interfaces cut loff_t down to off_t for reads, and ignore
588b90db0dSLinus Torvalds 	 * the offset entirely for writes..
598b90db0dSLinus Torvalds 	 */
608b90db0dSLinus Torvalds 	pos = *ppos;
618b90db0dSLinus Torvalds 	if (pos > MAX_NON_LFS)
628b90db0dSLinus Torvalds 		return 0;
638b90db0dSLinus Torvalds 	if (nbytes > MAX_NON_LFS - pos)
648b90db0dSLinus Torvalds 		nbytes = MAX_NON_LFS - pos;
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds 	dp = PDE(inode);
67e12ba74dSMel Gorman 	if (!(page = (char*) __get_free_page(GFP_TEMPORARY)))
681da177e4SLinus Torvalds 		return -ENOMEM;
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	while ((nbytes > 0) && !eof) {
711da177e4SLinus Torvalds 		count = min_t(size_t, PROC_BLOCK_SIZE, nbytes);
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 		start = NULL;
748731f14dSAlexey Dobriyan 		if (dp->read_proc) {
751da177e4SLinus Torvalds 			/*
761da177e4SLinus Torvalds 			 * How to be a proc read function
771da177e4SLinus Torvalds 			 * ------------------------------
781da177e4SLinus Torvalds 			 * Prototype:
791da177e4SLinus Torvalds 			 *    int f(char *buffer, char **start, off_t offset,
801da177e4SLinus Torvalds 			 *          int count, int *peof, void *dat)
811da177e4SLinus Torvalds 			 *
821da177e4SLinus Torvalds 			 * Assume that the buffer is "count" bytes in size.
831da177e4SLinus Torvalds 			 *
841da177e4SLinus Torvalds 			 * If you know you have supplied all the data you
851da177e4SLinus Torvalds 			 * have, set *peof.
861da177e4SLinus Torvalds 			 *
871da177e4SLinus Torvalds 			 * You have three ways to return data:
881da177e4SLinus Torvalds 			 * 0) Leave *start = NULL.  (This is the default.)
891da177e4SLinus Torvalds 			 *    Put the data of the requested offset at that
901da177e4SLinus Torvalds 			 *    offset within the buffer.  Return the number (n)
911da177e4SLinus Torvalds 			 *    of bytes there are from the beginning of the
921da177e4SLinus Torvalds 			 *    buffer up to the last byte of data.  If the
931da177e4SLinus Torvalds 			 *    number of supplied bytes (= n - offset) is
941da177e4SLinus Torvalds 			 *    greater than zero and you didn't signal eof
951da177e4SLinus Torvalds 			 *    and the reader is prepared to take more data
961da177e4SLinus Torvalds 			 *    you will be called again with the requested
971da177e4SLinus Torvalds 			 *    offset advanced by the number of bytes
981da177e4SLinus Torvalds 			 *    absorbed.  This interface is useful for files
991da177e4SLinus Torvalds 			 *    no larger than the buffer.
1001da177e4SLinus Torvalds 			 * 1) Set *start = an unsigned long value less than
1011da177e4SLinus Torvalds 			 *    the buffer address but greater than zero.
1021da177e4SLinus Torvalds 			 *    Put the data of the requested offset at the
1031da177e4SLinus Torvalds 			 *    beginning of the buffer.  Return the number of
1041da177e4SLinus Torvalds 			 *    bytes of data placed there.  If this number is
1051da177e4SLinus Torvalds 			 *    greater than zero and you didn't signal eof
1061da177e4SLinus Torvalds 			 *    and the reader is prepared to take more data
1071da177e4SLinus Torvalds 			 *    you will be called again with the requested
1081da177e4SLinus Torvalds 			 *    offset advanced by *start.  This interface is
1091da177e4SLinus Torvalds 			 *    useful when you have a large file consisting
1101da177e4SLinus Torvalds 			 *    of a series of blocks which you want to count
1111da177e4SLinus Torvalds 			 *    and return as wholes.
1121da177e4SLinus Torvalds 			 *    (Hack by Paul.Russell@rustcorp.com.au)
1131da177e4SLinus Torvalds 			 * 2) Set *start = an address within the buffer.
1141da177e4SLinus Torvalds 			 *    Put the data of the requested offset at *start.
1151da177e4SLinus Torvalds 			 *    Return the number of bytes of data placed there.
1161da177e4SLinus Torvalds 			 *    If this number is greater than zero and you
1171da177e4SLinus Torvalds 			 *    didn't signal eof and the reader is prepared to
1181da177e4SLinus Torvalds 			 *    take more data you will be called again with the
1191da177e4SLinus Torvalds 			 *    requested offset advanced by the number of bytes
1201da177e4SLinus Torvalds 			 *    absorbed.
1211da177e4SLinus Torvalds 			 */
1221da177e4SLinus Torvalds 			n = dp->read_proc(page, &start, *ppos,
1231da177e4SLinus Torvalds 					  count, &eof, dp->data);
1241da177e4SLinus Torvalds 		} else
1251da177e4SLinus Torvalds 			break;
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 		if (n == 0)   /* end of file */
1281da177e4SLinus Torvalds 			break;
1291da177e4SLinus Torvalds 		if (n < 0) {  /* error */
1301da177e4SLinus Torvalds 			if (retval == 0)
1311da177e4SLinus Torvalds 				retval = n;
1321da177e4SLinus Torvalds 			break;
1331da177e4SLinus Torvalds 		}
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 		if (start == NULL) {
13687ebdc00SAndrew Morton 			if (n > PAGE_SIZE)	/* Apparent buffer overflow */
1371da177e4SLinus Torvalds 				n = PAGE_SIZE;
1381da177e4SLinus Torvalds 			n -= *ppos;
1391da177e4SLinus Torvalds 			if (n <= 0)
1401da177e4SLinus Torvalds 				break;
1411da177e4SLinus Torvalds 			if (n > count)
1421da177e4SLinus Torvalds 				n = count;
1431da177e4SLinus Torvalds 			start = page + *ppos;
1441da177e4SLinus Torvalds 		} else if (start < page) {
14587ebdc00SAndrew Morton 			if (n > PAGE_SIZE)	/* Apparent buffer overflow */
1461da177e4SLinus Torvalds 				n = PAGE_SIZE;
1471da177e4SLinus Torvalds 			if (n > count) {
1481da177e4SLinus Torvalds 				/*
1491da177e4SLinus Torvalds 				 * Don't reduce n because doing so might
1501da177e4SLinus Torvalds 				 * cut off part of a data block.
1511da177e4SLinus Torvalds 				 */
15287ebdc00SAndrew Morton 				pr_warn("proc_file_read: count exceeded\n");
1531da177e4SLinus Torvalds 			}
1541da177e4SLinus Torvalds 		} else /* start >= page */ {
1551da177e4SLinus Torvalds 			unsigned long startoff = (unsigned long)(start - page);
15687ebdc00SAndrew Morton 			if (n > (PAGE_SIZE - startoff))	/* buffer overflow? */
1571da177e4SLinus Torvalds 				n = PAGE_SIZE - startoff;
1581da177e4SLinus Torvalds 			if (n > count)
1591da177e4SLinus Torvalds 				n = count;
1601da177e4SLinus Torvalds 		}
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds  		n -= copy_to_user(buf, start < page ? page : start, n);
1631da177e4SLinus Torvalds 		if (n == 0) {
1641da177e4SLinus Torvalds 			if (retval == 0)
1651da177e4SLinus Torvalds 				retval = -EFAULT;
1661da177e4SLinus Torvalds 			break;
1671da177e4SLinus Torvalds 		}
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds 		*ppos += start < page ? (unsigned long)start : n;
1701da177e4SLinus Torvalds 		nbytes -= n;
1711da177e4SLinus Torvalds 		buf += n;
1721da177e4SLinus Torvalds 		retval += n;
1731da177e4SLinus Torvalds 	}
1741da177e4SLinus Torvalds 	free_page((unsigned long) page);
1751da177e4SLinus Torvalds 	return retval;
1761da177e4SLinus Torvalds }
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds static ssize_t
1793dec7f59SAlexey Dobriyan proc_file_read(struct file *file, char __user *buf, size_t nbytes,
1803dec7f59SAlexey Dobriyan 	       loff_t *ppos)
1813dec7f59SAlexey Dobriyan {
182496ad9aaSAl Viro 	struct proc_dir_entry *pde = PDE(file_inode(file));
1833dec7f59SAlexey Dobriyan 	ssize_t rv = -EIO;
1843dec7f59SAlexey Dobriyan 
1853dec7f59SAlexey Dobriyan 	spin_lock(&pde->pde_unload_lock);
1863dec7f59SAlexey Dobriyan 	if (!pde->proc_fops) {
1873dec7f59SAlexey Dobriyan 		spin_unlock(&pde->pde_unload_lock);
1883dec7f59SAlexey Dobriyan 		return rv;
1893dec7f59SAlexey Dobriyan 	}
1903dec7f59SAlexey Dobriyan 	pde->pde_users++;
1913dec7f59SAlexey Dobriyan 	spin_unlock(&pde->pde_unload_lock);
1923dec7f59SAlexey Dobriyan 
1933dec7f59SAlexey Dobriyan 	rv = __proc_file_read(file, buf, nbytes, ppos);
1943dec7f59SAlexey Dobriyan 
1953dec7f59SAlexey Dobriyan 	pde_users_dec(pde);
1963dec7f59SAlexey Dobriyan 	return rv;
1973dec7f59SAlexey Dobriyan }
1983dec7f59SAlexey Dobriyan 
1993dec7f59SAlexey Dobriyan static ssize_t
2001da177e4SLinus Torvalds proc_file_write(struct file *file, const char __user *buffer,
2011da177e4SLinus Torvalds 		size_t count, loff_t *ppos)
2021da177e4SLinus Torvalds {
203496ad9aaSAl Viro 	struct proc_dir_entry *pde = PDE(file_inode(file));
2043dec7f59SAlexey Dobriyan 	ssize_t rv = -EIO;
2051da177e4SLinus Torvalds 
2063dec7f59SAlexey Dobriyan 	if (pde->write_proc) {
2073dec7f59SAlexey Dobriyan 		spin_lock(&pde->pde_unload_lock);
2083dec7f59SAlexey Dobriyan 		if (!pde->proc_fops) {
2093dec7f59SAlexey Dobriyan 			spin_unlock(&pde->pde_unload_lock);
2103dec7f59SAlexey Dobriyan 			return rv;
2113dec7f59SAlexey Dobriyan 		}
2123dec7f59SAlexey Dobriyan 		pde->pde_users++;
2133dec7f59SAlexey Dobriyan 		spin_unlock(&pde->pde_unload_lock);
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds 		/* FIXME: does this routine need ppos?  probably... */
2163dec7f59SAlexey Dobriyan 		rv = pde->write_proc(file, buffer, count, pde->data);
2173dec7f59SAlexey Dobriyan 		pde_users_dec(pde);
2183dec7f59SAlexey Dobriyan 	}
2193dec7f59SAlexey Dobriyan 	return rv;
2201da177e4SLinus Torvalds }
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds static loff_t
2241da177e4SLinus Torvalds proc_file_lseek(struct file *file, loff_t offset, int orig)
2251da177e4SLinus Torvalds {
2268b90db0dSLinus Torvalds 	loff_t retval = -EINVAL;
2271da177e4SLinus Torvalds 	switch (orig) {
2281da177e4SLinus Torvalds 	case 1:
2298b90db0dSLinus Torvalds 		offset += file->f_pos;
2308b90db0dSLinus Torvalds 	/* fallthrough */
2318b90db0dSLinus Torvalds 	case 0:
2328b90db0dSLinus Torvalds 		if (offset < 0 || offset > MAX_NON_LFS)
2338b90db0dSLinus Torvalds 			break;
2348b90db0dSLinus Torvalds 		file->f_pos = retval = offset;
2351da177e4SLinus Torvalds 	}
2368b90db0dSLinus Torvalds 	return retval;
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds 
23976df0c25SAlexey Dobriyan static const struct file_operations proc_file_operations = {
24076df0c25SAlexey Dobriyan 	.llseek		= proc_file_lseek,
24176df0c25SAlexey Dobriyan 	.read		= proc_file_read,
24276df0c25SAlexey Dobriyan 	.write		= proc_file_write,
24376df0c25SAlexey Dobriyan };
24476df0c25SAlexey Dobriyan 
2451da177e4SLinus Torvalds static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
2461da177e4SLinus Torvalds {
2471da177e4SLinus Torvalds 	struct inode *inode = dentry->d_inode;
2481da177e4SLinus Torvalds 	struct proc_dir_entry *de = PDE(inode);
2491da177e4SLinus Torvalds 	int error;
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds 	error = inode_change_ok(inode, iattr);
2521da177e4SLinus Torvalds 	if (error)
2531025774cSChristoph Hellwig 		return error;
2541da177e4SLinus Torvalds 
2551025774cSChristoph Hellwig 	setattr_copy(inode, iattr);
2561025774cSChristoph Hellwig 	mark_inode_dirty(inode);
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 	de->uid = inode->i_uid;
2591da177e4SLinus Torvalds 	de->gid = inode->i_gid;
2601da177e4SLinus Torvalds 	de->mode = inode->i_mode;
2611025774cSChristoph Hellwig 	return 0;
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds 
2642b579beeSMiklos Szeredi static int proc_getattr(struct vfsmount *mnt, struct dentry *dentry,
2652b579beeSMiklos Szeredi 			struct kstat *stat)
2662b579beeSMiklos Szeredi {
2672b579beeSMiklos Szeredi 	struct inode *inode = dentry->d_inode;
2682b579beeSMiklos Szeredi 	struct proc_dir_entry *de = PROC_I(inode)->pde;
2692b579beeSMiklos Szeredi 	if (de && de->nlink)
270bfe86848SMiklos Szeredi 		set_nlink(inode, de->nlink);
2712b579beeSMiklos Szeredi 
2722b579beeSMiklos Szeredi 	generic_fillattr(inode, stat);
2732b579beeSMiklos Szeredi 	return 0;
2742b579beeSMiklos Szeredi }
2752b579beeSMiklos Szeredi 
276c5ef1c42SArjan van de Ven static const struct inode_operations proc_file_inode_operations = {
2771da177e4SLinus Torvalds 	.setattr	= proc_notify_change,
2781da177e4SLinus Torvalds };
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds /*
2811da177e4SLinus Torvalds  * This function parses a name such as "tty/driver/serial", and
2821da177e4SLinus Torvalds  * returns the struct proc_dir_entry for "/proc/tty/driver", and
2831da177e4SLinus Torvalds  * returns "serial" in residual.
2841da177e4SLinus Torvalds  */
285e17a5765SAlexey Dobriyan static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret,
286e17a5765SAlexey Dobriyan 			     const char **residual)
2871da177e4SLinus Torvalds {
2881da177e4SLinus Torvalds 	const char     		*cp = name, *next;
2891da177e4SLinus Torvalds 	struct proc_dir_entry	*de;
290312ec7e5SAlexey Dobriyan 	unsigned int		len;
2911da177e4SLinus Torvalds 
2927cee4e00SAlexey Dobriyan 	de = *ret;
2937cee4e00SAlexey Dobriyan 	if (!de)
2941da177e4SLinus Torvalds 		de = &proc_root;
2957cee4e00SAlexey Dobriyan 
2961da177e4SLinus Torvalds 	while (1) {
2971da177e4SLinus Torvalds 		next = strchr(cp, '/');
2981da177e4SLinus Torvalds 		if (!next)
2991da177e4SLinus Torvalds 			break;
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds 		len = next - cp;
3021da177e4SLinus Torvalds 		for (de = de->subdir; de ; de = de->next) {
3031da177e4SLinus Torvalds 			if (proc_match(len, cp, de))
3041da177e4SLinus Torvalds 				break;
3051da177e4SLinus Torvalds 		}
30612bac0d9SAlexey Dobriyan 		if (!de) {
30712bac0d9SAlexey Dobriyan 			WARN(1, "name '%s'\n", name);
308e17a5765SAlexey Dobriyan 			return -ENOENT;
30912bac0d9SAlexey Dobriyan 		}
3101da177e4SLinus Torvalds 		cp += len + 1;
3111da177e4SLinus Torvalds 	}
3121da177e4SLinus Torvalds 	*residual = cp;
3131da177e4SLinus Torvalds 	*ret = de;
314e17a5765SAlexey Dobriyan 	return 0;
315e17a5765SAlexey Dobriyan }
316e17a5765SAlexey Dobriyan 
317e17a5765SAlexey Dobriyan static int xlate_proc_name(const char *name, struct proc_dir_entry **ret,
318e17a5765SAlexey Dobriyan 			   const char **residual)
319e17a5765SAlexey Dobriyan {
320e17a5765SAlexey Dobriyan 	int rv;
321e17a5765SAlexey Dobriyan 
322e17a5765SAlexey Dobriyan 	spin_lock(&proc_subdir_lock);
323e17a5765SAlexey Dobriyan 	rv = __xlate_proc_name(name, ret, residual);
32464a07bd8SSteven Rostedt 	spin_unlock(&proc_subdir_lock);
325e17a5765SAlexey Dobriyan 	return rv;
3261da177e4SLinus Torvalds }
3271da177e4SLinus Torvalds 
3289a185409SAlexey Dobriyan static DEFINE_IDA(proc_inum_ida);
3291da177e4SLinus Torvalds static DEFINE_SPINLOCK(proc_inum_lock); /* protects the above */
3301da177e4SLinus Torvalds 
33167935df4SAlexey Dobriyan #define PROC_DYNAMIC_FIRST 0xF0000000U
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds /*
3341da177e4SLinus Torvalds  * Return an inode number between PROC_DYNAMIC_FIRST and
3351da177e4SLinus Torvalds  * 0xffffffff, or zero on failure.
3361da177e4SLinus Torvalds  */
33733d6dce6SEric W. Biederman int proc_alloc_inum(unsigned int *inum)
3381da177e4SLinus Torvalds {
33967935df4SAlexey Dobriyan 	unsigned int i;
3401da177e4SLinus Torvalds 	int error;
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds retry:
34333d6dce6SEric W. Biederman 	if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL))
34433d6dce6SEric W. Biederman 		return -ENOMEM;
3451da177e4SLinus Torvalds 
346dfb2ea45SEric W. Biederman 	spin_lock_irq(&proc_inum_lock);
3479a185409SAlexey Dobriyan 	error = ida_get_new(&proc_inum_ida, &i);
348dfb2ea45SEric W. Biederman 	spin_unlock_irq(&proc_inum_lock);
3491da177e4SLinus Torvalds 	if (error == -EAGAIN)
3501da177e4SLinus Torvalds 		goto retry;
3511da177e4SLinus Torvalds 	else if (error)
35233d6dce6SEric W. Biederman 		return error;
3531da177e4SLinus Torvalds 
35467935df4SAlexey Dobriyan 	if (i > UINT_MAX - PROC_DYNAMIC_FIRST) {
355dfb2ea45SEric W. Biederman 		spin_lock_irq(&proc_inum_lock);
3569a185409SAlexey Dobriyan 		ida_remove(&proc_inum_ida, i);
357dfb2ea45SEric W. Biederman 		spin_unlock_irq(&proc_inum_lock);
35833d6dce6SEric W. Biederman 		return -ENOSPC;
35933d6dce6SEric W. Biederman 	}
36033d6dce6SEric W. Biederman 	*inum = PROC_DYNAMIC_FIRST + i;
361cc996099SAlexey Dobriyan 	return 0;
36267935df4SAlexey Dobriyan }
3631da177e4SLinus Torvalds 
36433d6dce6SEric W. Biederman void proc_free_inum(unsigned int inum)
3651da177e4SLinus Torvalds {
366dfb2ea45SEric W. Biederman 	unsigned long flags;
367dfb2ea45SEric W. Biederman 	spin_lock_irqsave(&proc_inum_lock, flags);
3689a185409SAlexey Dobriyan 	ida_remove(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST);
369dfb2ea45SEric W. Biederman 	spin_unlock_irqrestore(&proc_inum_lock, flags);
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds 
372008b150aSAl Viro static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd)
3731da177e4SLinus Torvalds {
3741da177e4SLinus Torvalds 	nd_set_link(nd, PDE(dentry->d_inode)->data);
375008b150aSAl Viro 	return NULL;
3761da177e4SLinus Torvalds }
3771da177e4SLinus Torvalds 
378c5ef1c42SArjan van de Ven static const struct inode_operations proc_link_inode_operations = {
3791da177e4SLinus Torvalds 	.readlink	= generic_readlink,
3801da177e4SLinus Torvalds 	.follow_link	= proc_follow_link,
3811da177e4SLinus Torvalds };
3821da177e4SLinus Torvalds 
3831da177e4SLinus Torvalds /*
3841da177e4SLinus Torvalds  * As some entries in /proc are volatile, we want to
3851da177e4SLinus Torvalds  * get rid of unused dentries.  This could be made
3861da177e4SLinus Torvalds  * smarter: we could keep a "volatile" flag in the
3871da177e4SLinus Torvalds  * inode to indicate which ones to keep.
3881da177e4SLinus Torvalds  */
389fe15ce44SNick Piggin static int proc_delete_dentry(const struct dentry * dentry)
3901da177e4SLinus Torvalds {
3911da177e4SLinus Torvalds 	return 1;
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds 
394d72f71ebSAl Viro static const struct dentry_operations proc_dentry_operations =
3951da177e4SLinus Torvalds {
3961da177e4SLinus Torvalds 	.d_delete	= proc_delete_dentry,
3971da177e4SLinus Torvalds };
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds /*
4001da177e4SLinus Torvalds  * Don't create negative dentries here, return -ENOENT by hand
4011da177e4SLinus Torvalds  * instead.
4021da177e4SLinus Torvalds  */
403e9720acdSPavel Emelyanov struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
404e9720acdSPavel Emelyanov 		struct dentry *dentry)
4051da177e4SLinus Torvalds {
406d3d009cbSAl Viro 	struct inode *inode;
4071da177e4SLinus Torvalds 
40864a07bd8SSteven Rostedt 	spin_lock(&proc_subdir_lock);
4091da177e4SLinus Torvalds 	for (de = de->subdir; de ; de = de->next) {
4101da177e4SLinus Torvalds 		if (de->namelen != dentry->d_name.len)
4111da177e4SLinus Torvalds 			continue;
4121da177e4SLinus Torvalds 		if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
413135d5655SAlexey Dobriyan 			pde_get(de);
41464a07bd8SSteven Rostedt 			spin_unlock(&proc_subdir_lock);
4156d1b6e4eSAlexey Dobriyan 			inode = proc_get_inode(dir->i_sb, de);
416d3d009cbSAl Viro 			if (!inode)
417d3d009cbSAl Viro 				return ERR_PTR(-ENOMEM);
418fb045adbSNick Piggin 			d_set_d_op(dentry, &proc_dentry_operations);
4191da177e4SLinus Torvalds 			d_add(dentry, inode);
4201da177e4SLinus Torvalds 			return NULL;
4211da177e4SLinus Torvalds 		}
422d3d009cbSAl Viro 	}
423d3d009cbSAl Viro 	spin_unlock(&proc_subdir_lock);
424d3d009cbSAl Viro 	return ERR_PTR(-ENOENT);
4251da177e4SLinus Torvalds }
4261da177e4SLinus Torvalds 
427e9720acdSPavel Emelyanov struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
42800cd8dd3SAl Viro 		unsigned int flags)
429e9720acdSPavel Emelyanov {
430e9720acdSPavel Emelyanov 	return proc_lookup_de(PDE(dir), dir, dentry);
431e9720acdSPavel Emelyanov }
432e9720acdSPavel Emelyanov 
4331da177e4SLinus Torvalds /*
4341da177e4SLinus Torvalds  * This returns non-zero if at EOF, so that the /proc
4351da177e4SLinus Torvalds  * root directory can use this and check if it should
4361da177e4SLinus Torvalds  * continue with the <pid> entries..
4371da177e4SLinus Torvalds  *
4381da177e4SLinus Torvalds  * Note that the VFS-layer doesn't care about the return
4391da177e4SLinus Torvalds  * value of the readdir() call, as long as it's non-negative
4401da177e4SLinus Torvalds  * for success..
4411da177e4SLinus Torvalds  */
442e9720acdSPavel Emelyanov int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
443e9720acdSPavel Emelyanov 		filldir_t filldir)
4441da177e4SLinus Torvalds {
4451da177e4SLinus Torvalds 	unsigned int ino;
4461da177e4SLinus Torvalds 	int i;
447496ad9aaSAl Viro 	struct inode *inode = file_inode(filp);
4481da177e4SLinus Torvalds 	int ret = 0;
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds 	ino = inode->i_ino;
4511da177e4SLinus Torvalds 	i = filp->f_pos;
4521da177e4SLinus Torvalds 	switch (i) {
4531da177e4SLinus Torvalds 		case 0:
4541da177e4SLinus Torvalds 			if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
4551da177e4SLinus Torvalds 				goto out;
4561da177e4SLinus Torvalds 			i++;
4571da177e4SLinus Torvalds 			filp->f_pos++;
4581da177e4SLinus Torvalds 			/* fall through */
4591da177e4SLinus Torvalds 		case 1:
4601da177e4SLinus Torvalds 			if (filldir(dirent, "..", 2, i,
4612fddfeefSJosef "Jeff" Sipek 				    parent_ino(filp->f_path.dentry),
4621da177e4SLinus Torvalds 				    DT_DIR) < 0)
4631da177e4SLinus Torvalds 				goto out;
4641da177e4SLinus Torvalds 			i++;
4651da177e4SLinus Torvalds 			filp->f_pos++;
4661da177e4SLinus Torvalds 			/* fall through */
4671da177e4SLinus Torvalds 		default:
46864a07bd8SSteven Rostedt 			spin_lock(&proc_subdir_lock);
4691da177e4SLinus Torvalds 			de = de->subdir;
4701da177e4SLinus Torvalds 			i -= 2;
4711da177e4SLinus Torvalds 			for (;;) {
4721da177e4SLinus Torvalds 				if (!de) {
4731da177e4SLinus Torvalds 					ret = 1;
47464a07bd8SSteven Rostedt 					spin_unlock(&proc_subdir_lock);
4751da177e4SLinus Torvalds 					goto out;
4761da177e4SLinus Torvalds 				}
4771da177e4SLinus Torvalds 				if (!i)
4781da177e4SLinus Torvalds 					break;
4791da177e4SLinus Torvalds 				de = de->next;
4801da177e4SLinus Torvalds 				i--;
4811da177e4SLinus Torvalds 			}
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 			do {
48459cd0cbcSDarrick J. Wong 				struct proc_dir_entry *next;
48559cd0cbcSDarrick J. Wong 
48664a07bd8SSteven Rostedt 				/* filldir passes info to user space */
487135d5655SAlexey Dobriyan 				pde_get(de);
48864a07bd8SSteven Rostedt 				spin_unlock(&proc_subdir_lock);
4891da177e4SLinus Torvalds 				if (filldir(dirent, de->name, de->namelen, filp->f_pos,
49059cd0cbcSDarrick J. Wong 					    de->low_ino, de->mode >> 12) < 0) {
491135d5655SAlexey Dobriyan 					pde_put(de);
4921da177e4SLinus Torvalds 					goto out;
49359cd0cbcSDarrick J. Wong 				}
49464a07bd8SSteven Rostedt 				spin_lock(&proc_subdir_lock);
4951da177e4SLinus Torvalds 				filp->f_pos++;
49659cd0cbcSDarrick J. Wong 				next = de->next;
497135d5655SAlexey Dobriyan 				pde_put(de);
49859cd0cbcSDarrick J. Wong 				de = next;
4991da177e4SLinus Torvalds 			} while (de);
50064a07bd8SSteven Rostedt 			spin_unlock(&proc_subdir_lock);
5011da177e4SLinus Torvalds 	}
5021da177e4SLinus Torvalds 	ret = 1;
503b4df2b92SAlexey Dobriyan out:
5041da177e4SLinus Torvalds 	return ret;
5051da177e4SLinus Torvalds }
5061da177e4SLinus Torvalds 
507e9720acdSPavel Emelyanov int proc_readdir(struct file *filp, void *dirent, filldir_t filldir)
508e9720acdSPavel Emelyanov {
509496ad9aaSAl Viro 	struct inode *inode = file_inode(filp);
510e9720acdSPavel Emelyanov 
511e9720acdSPavel Emelyanov 	return proc_readdir_de(PDE(inode), filp, dirent, filldir);
512e9720acdSPavel Emelyanov }
513e9720acdSPavel Emelyanov 
5141da177e4SLinus Torvalds /*
5151da177e4SLinus Torvalds  * These are the generic /proc directory operations. They
5161da177e4SLinus Torvalds  * use the in-memory "struct proc_dir_entry" tree to parse
5171da177e4SLinus Torvalds  * the /proc directory.
5181da177e4SLinus Torvalds  */
51900977a59SArjan van de Ven static const struct file_operations proc_dir_operations = {
520b4df2b92SAlexey Dobriyan 	.llseek			= generic_file_llseek,
5211da177e4SLinus Torvalds 	.read			= generic_read_dir,
5221da177e4SLinus Torvalds 	.readdir		= proc_readdir,
5231da177e4SLinus Torvalds };
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds /*
5261da177e4SLinus Torvalds  * proc directories can do almost nothing..
5271da177e4SLinus Torvalds  */
528c5ef1c42SArjan van de Ven static const struct inode_operations proc_dir_inode_operations = {
5291da177e4SLinus Torvalds 	.lookup		= proc_lookup,
5302b579beeSMiklos Szeredi 	.getattr	= proc_getattr,
5311da177e4SLinus Torvalds 	.setattr	= proc_notify_change,
5321da177e4SLinus Torvalds };
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
5351da177e4SLinus Torvalds {
53694413d88SZhang Rui 	struct proc_dir_entry *tmp;
53733d6dce6SEric W. Biederman 	int ret;
5381da177e4SLinus Torvalds 
53933d6dce6SEric W. Biederman 	ret = proc_alloc_inum(&dp->low_ino);
54033d6dce6SEric W. Biederman 	if (ret)
54133d6dce6SEric W. Biederman 		return ret;
54264a07bd8SSteven Rostedt 
5431da177e4SLinus Torvalds 	if (S_ISDIR(dp->mode)) {
5441da177e4SLinus Torvalds 		if (dp->proc_iops == NULL) {
5451da177e4SLinus Torvalds 			dp->proc_fops = &proc_dir_operations;
5461da177e4SLinus Torvalds 			dp->proc_iops = &proc_dir_inode_operations;
5471da177e4SLinus Torvalds 		}
5481da177e4SLinus Torvalds 		dir->nlink++;
5491da177e4SLinus Torvalds 	} else if (S_ISLNK(dp->mode)) {
5501da177e4SLinus Torvalds 		if (dp->proc_iops == NULL)
5511da177e4SLinus Torvalds 			dp->proc_iops = &proc_link_inode_operations;
5521da177e4SLinus Torvalds 	} else if (S_ISREG(dp->mode)) {
5531da177e4SLinus Torvalds 		if (dp->proc_fops == NULL)
5541da177e4SLinus Torvalds 			dp->proc_fops = &proc_file_operations;
5551da177e4SLinus Torvalds 		if (dp->proc_iops == NULL)
5561da177e4SLinus Torvalds 			dp->proc_iops = &proc_file_inode_operations;
5571da177e4SLinus Torvalds 	}
55899fc06dfSChangli Gao 
55999fc06dfSChangli Gao 	spin_lock(&proc_subdir_lock);
56094413d88SZhang Rui 
56194413d88SZhang Rui 	for (tmp = dir->subdir; tmp; tmp = tmp->next)
56294413d88SZhang Rui 		if (strcmp(tmp->name, dp->name) == 0) {
56387ebdc00SAndrew Morton 			WARN(1, "proc_dir_entry '%s/%s' already registered\n",
564665020c3SAlexey Dobriyan 				dir->name, dp->name);
56594413d88SZhang Rui 			break;
56694413d88SZhang Rui 		}
56794413d88SZhang Rui 
56899fc06dfSChangli Gao 	dp->next = dir->subdir;
56999fc06dfSChangli Gao 	dp->parent = dir;
57099fc06dfSChangli Gao 	dir->subdir = dp;
57199fc06dfSChangli Gao 	spin_unlock(&proc_subdir_lock);
57299fc06dfSChangli Gao 
5731da177e4SLinus Torvalds 	return 0;
5741da177e4SLinus Torvalds }
5751da177e4SLinus Torvalds 
5762d3a4e36SAlexey Dobriyan static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
5771da177e4SLinus Torvalds 					  const char *name,
578d161a13fSAl Viro 					  umode_t mode,
5791da177e4SLinus Torvalds 					  nlink_t nlink)
5801da177e4SLinus Torvalds {
5811da177e4SLinus Torvalds 	struct proc_dir_entry *ent = NULL;
5821da177e4SLinus Torvalds 	const char *fn = name;
583312ec7e5SAlexey Dobriyan 	unsigned int len;
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds 	/* make sure name is valid */
58617baa2a2Syan 	if (!name || !strlen(name))
58717baa2a2Syan 		goto out;
5881da177e4SLinus Torvalds 
5897cee4e00SAlexey Dobriyan 	if (xlate_proc_name(name, parent, &fn) != 0)
5901da177e4SLinus Torvalds 		goto out;
5911da177e4SLinus Torvalds 
5921da177e4SLinus Torvalds 	/* At this point there must not be any '/' characters beyond *fn */
5931da177e4SLinus Torvalds 	if (strchr(fn, '/'))
5941da177e4SLinus Torvalds 		goto out;
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds 	len = strlen(fn);
5971da177e4SLinus Torvalds 
59817baa2a2Syan 	ent = kzalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
59917baa2a2Syan 	if (!ent)
60017baa2a2Syan 		goto out;
6011da177e4SLinus Torvalds 
60209570f91SDavid Howells 	memcpy(ent->name, fn, len + 1);
6031da177e4SLinus Torvalds 	ent->namelen = len;
6041da177e4SLinus Torvalds 	ent->mode = mode;
6051da177e4SLinus Torvalds 	ent->nlink = nlink;
6065a622f2dSAlexey Dobriyan 	atomic_set(&ent->count, 1);
607786d7e16SAlexey Dobriyan 	spin_lock_init(&ent->pde_unload_lock);
608881adb85SAlexey Dobriyan 	INIT_LIST_HEAD(&ent->pde_openers);
6091da177e4SLinus Torvalds out:
6101da177e4SLinus Torvalds 	return ent;
6111da177e4SLinus Torvalds }
6121da177e4SLinus Torvalds 
6131da177e4SLinus Torvalds struct proc_dir_entry *proc_symlink(const char *name,
6141da177e4SLinus Torvalds 		struct proc_dir_entry *parent, const char *dest)
6151da177e4SLinus Torvalds {
6161da177e4SLinus Torvalds 	struct proc_dir_entry *ent;
6171da177e4SLinus Torvalds 
6182d3a4e36SAlexey Dobriyan 	ent = __proc_create(&parent, name,
6191da177e4SLinus Torvalds 			  (S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO),1);
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds 	if (ent) {
6221da177e4SLinus Torvalds 		ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL);
6231da177e4SLinus Torvalds 		if (ent->data) {
6241da177e4SLinus Torvalds 			strcpy((char*)ent->data,dest);
6251da177e4SLinus Torvalds 			if (proc_register(parent, ent) < 0) {
6261da177e4SLinus Torvalds 				kfree(ent->data);
6271da177e4SLinus Torvalds 				kfree(ent);
6281da177e4SLinus Torvalds 				ent = NULL;
6291da177e4SLinus Torvalds 			}
6301da177e4SLinus Torvalds 		} else {
6311da177e4SLinus Torvalds 			kfree(ent);
6321da177e4SLinus Torvalds 			ent = NULL;
6331da177e4SLinus Torvalds 		}
6341da177e4SLinus Torvalds 	}
6351da177e4SLinus Torvalds 	return ent;
6361da177e4SLinus Torvalds }
637587d4a17SHelight.Xu EXPORT_SYMBOL(proc_symlink);
6381da177e4SLinus Torvalds 
639d161a13fSAl Viro struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
6401da177e4SLinus Torvalds 		struct proc_dir_entry *parent)
6411da177e4SLinus Torvalds {
6421da177e4SLinus Torvalds 	struct proc_dir_entry *ent;
6431da177e4SLinus Torvalds 
6442d3a4e36SAlexey Dobriyan 	ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
6451da177e4SLinus Torvalds 	if (ent) {
6461da177e4SLinus Torvalds 		if (proc_register(parent, ent) < 0) {
6471da177e4SLinus Torvalds 			kfree(ent);
6481da177e4SLinus Torvalds 			ent = NULL;
6491da177e4SLinus Torvalds 		}
6501da177e4SLinus Torvalds 	}
6511da177e4SLinus Torvalds 	return ent;
6521da177e4SLinus Torvalds }
653011159a0SAlexey Dobriyan EXPORT_SYMBOL(proc_mkdir_mode);
6541da177e4SLinus Torvalds 
65578e92b99SDenis V. Lunev struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name,
65678e92b99SDenis V. Lunev 		struct proc_dir_entry *parent)
65778e92b99SDenis V. Lunev {
65878e92b99SDenis V. Lunev 	struct proc_dir_entry *ent;
65978e92b99SDenis V. Lunev 
66078e92b99SDenis V. Lunev 	ent = __proc_create(&parent, name, S_IFDIR | S_IRUGO | S_IXUGO, 2);
66178e92b99SDenis V. Lunev 	if (ent) {
66278e92b99SDenis V. Lunev 		ent->data = net;
66378e92b99SDenis V. Lunev 		if (proc_register(parent, ent) < 0) {
66478e92b99SDenis V. Lunev 			kfree(ent);
66578e92b99SDenis V. Lunev 			ent = NULL;
66678e92b99SDenis V. Lunev 		}
66778e92b99SDenis V. Lunev 	}
66878e92b99SDenis V. Lunev 	return ent;
66978e92b99SDenis V. Lunev }
67078e92b99SDenis V. Lunev EXPORT_SYMBOL_GPL(proc_net_mkdir);
67178e92b99SDenis V. Lunev 
6721da177e4SLinus Torvalds struct proc_dir_entry *proc_mkdir(const char *name,
6731da177e4SLinus Torvalds 		struct proc_dir_entry *parent)
6741da177e4SLinus Torvalds {
6751da177e4SLinus Torvalds 	return proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
6761da177e4SLinus Torvalds }
677587d4a17SHelight.Xu EXPORT_SYMBOL(proc_mkdir);
6781da177e4SLinus Torvalds 
679d161a13fSAl Viro struct proc_dir_entry *create_proc_entry(const char *name, umode_t mode,
6801da177e4SLinus Torvalds 					 struct proc_dir_entry *parent)
6811da177e4SLinus Torvalds {
6821da177e4SLinus Torvalds 	struct proc_dir_entry *ent;
6831da177e4SLinus Torvalds 	nlink_t nlink;
6841da177e4SLinus Torvalds 
6851da177e4SLinus Torvalds 	if (S_ISDIR(mode)) {
6861da177e4SLinus Torvalds 		if ((mode & S_IALLUGO) == 0)
6871da177e4SLinus Torvalds 			mode |= S_IRUGO | S_IXUGO;
6881da177e4SLinus Torvalds 		nlink = 2;
6891da177e4SLinus Torvalds 	} else {
6901da177e4SLinus Torvalds 		if ((mode & S_IFMT) == 0)
6911da177e4SLinus Torvalds 			mode |= S_IFREG;
6921da177e4SLinus Torvalds 		if ((mode & S_IALLUGO) == 0)
6931da177e4SLinus Torvalds 			mode |= S_IRUGO;
6941da177e4SLinus Torvalds 		nlink = 1;
6951da177e4SLinus Torvalds 	}
6961da177e4SLinus Torvalds 
6972d3a4e36SAlexey Dobriyan 	ent = __proc_create(&parent, name, mode, nlink);
6981da177e4SLinus Torvalds 	if (ent) {
6991da177e4SLinus Torvalds 		if (proc_register(parent, ent) < 0) {
7001da177e4SLinus Torvalds 			kfree(ent);
7011da177e4SLinus Torvalds 			ent = NULL;
7021da177e4SLinus Torvalds 		}
7031da177e4SLinus Torvalds 	}
7041da177e4SLinus Torvalds 	return ent;
7051da177e4SLinus Torvalds }
706587d4a17SHelight.Xu EXPORT_SYMBOL(create_proc_entry);
7071da177e4SLinus Torvalds 
708d161a13fSAl Viro struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
7092d3a4e36SAlexey Dobriyan 					struct proc_dir_entry *parent,
71059b74351SDenis V. Lunev 					const struct file_operations *proc_fops,
71159b74351SDenis V. Lunev 					void *data)
7122d3a4e36SAlexey Dobriyan {
7132d3a4e36SAlexey Dobriyan 	struct proc_dir_entry *pde;
7142d3a4e36SAlexey Dobriyan 	nlink_t nlink;
7152d3a4e36SAlexey Dobriyan 
7162d3a4e36SAlexey Dobriyan 	if (S_ISDIR(mode)) {
7172d3a4e36SAlexey Dobriyan 		if ((mode & S_IALLUGO) == 0)
7182d3a4e36SAlexey Dobriyan 			mode |= S_IRUGO | S_IXUGO;
7192d3a4e36SAlexey Dobriyan 		nlink = 2;
7202d3a4e36SAlexey Dobriyan 	} else {
7212d3a4e36SAlexey Dobriyan 		if ((mode & S_IFMT) == 0)
7222d3a4e36SAlexey Dobriyan 			mode |= S_IFREG;
7232d3a4e36SAlexey Dobriyan 		if ((mode & S_IALLUGO) == 0)
7242d3a4e36SAlexey Dobriyan 			mode |= S_IRUGO;
7252d3a4e36SAlexey Dobriyan 		nlink = 1;
7262d3a4e36SAlexey Dobriyan 	}
7272d3a4e36SAlexey Dobriyan 
7282d3a4e36SAlexey Dobriyan 	pde = __proc_create(&parent, name, mode, nlink);
7292d3a4e36SAlexey Dobriyan 	if (!pde)
7302d3a4e36SAlexey Dobriyan 		goto out;
7312d3a4e36SAlexey Dobriyan 	pde->proc_fops = proc_fops;
73259b74351SDenis V. Lunev 	pde->data = data;
7332d3a4e36SAlexey Dobriyan 	if (proc_register(parent, pde) < 0)
7342d3a4e36SAlexey Dobriyan 		goto out_free;
7352d3a4e36SAlexey Dobriyan 	return pde;
7362d3a4e36SAlexey Dobriyan out_free:
7372d3a4e36SAlexey Dobriyan 	kfree(pde);
7382d3a4e36SAlexey Dobriyan out:
7392d3a4e36SAlexey Dobriyan 	return NULL;
7402d3a4e36SAlexey Dobriyan }
741587d4a17SHelight.Xu EXPORT_SYMBOL(proc_create_data);
7422d3a4e36SAlexey Dobriyan 
743135d5655SAlexey Dobriyan static void free_proc_entry(struct proc_dir_entry *de)
7441da177e4SLinus Torvalds {
74533d6dce6SEric W. Biederman 	proc_free_inum(de->low_ino);
7461da177e4SLinus Torvalds 
747fd2cbe48SAlexey Dobriyan 	if (S_ISLNK(de->mode))
7481da177e4SLinus Torvalds 		kfree(de->data);
7491da177e4SLinus Torvalds 	kfree(de);
7501da177e4SLinus Torvalds }
7511da177e4SLinus Torvalds 
752135d5655SAlexey Dobriyan void pde_put(struct proc_dir_entry *pde)
753135d5655SAlexey Dobriyan {
754135d5655SAlexey Dobriyan 	if (atomic_dec_and_test(&pde->count))
755135d5655SAlexey Dobriyan 		free_proc_entry(pde);
756135d5655SAlexey Dobriyan }
757135d5655SAlexey Dobriyan 
7588ce584c7SAl Viro static void entry_rundown(struct proc_dir_entry *de)
7591da177e4SLinus Torvalds {
760786d7e16SAlexey Dobriyan 	spin_lock(&de->pde_unload_lock);
761786d7e16SAlexey Dobriyan 	/*
762786d7e16SAlexey Dobriyan 	 * Stop accepting new callers into module. If you're
763786d7e16SAlexey Dobriyan 	 * dynamically allocating ->proc_fops, save a pointer somewhere.
764786d7e16SAlexey Dobriyan 	 */
765786d7e16SAlexey Dobriyan 	de->proc_fops = NULL;
766786d7e16SAlexey Dobriyan 	/* Wait until all existing callers into module are done. */
767786d7e16SAlexey Dobriyan 	if (de->pde_users > 0) {
768786d7e16SAlexey Dobriyan 		DECLARE_COMPLETION_ONSTACK(c);
769786d7e16SAlexey Dobriyan 
770786d7e16SAlexey Dobriyan 		if (!de->pde_unload_completion)
771786d7e16SAlexey Dobriyan 			de->pde_unload_completion = &c;
772786d7e16SAlexey Dobriyan 
773786d7e16SAlexey Dobriyan 		spin_unlock(&de->pde_unload_lock);
774786d7e16SAlexey Dobriyan 
775786d7e16SAlexey Dobriyan 		wait_for_completion(de->pde_unload_completion);
776786d7e16SAlexey Dobriyan 
777881adb85SAlexey Dobriyan 		spin_lock(&de->pde_unload_lock);
7783740a20cSAlexey Dobriyan 	}
7793740a20cSAlexey Dobriyan 
780881adb85SAlexey Dobriyan 	while (!list_empty(&de->pde_openers)) {
781881adb85SAlexey Dobriyan 		struct pde_opener *pdeo;
782881adb85SAlexey Dobriyan 
783881adb85SAlexey Dobriyan 		pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh);
784881adb85SAlexey Dobriyan 		list_del(&pdeo->lh);
785881adb85SAlexey Dobriyan 		spin_unlock(&de->pde_unload_lock);
786881adb85SAlexey Dobriyan 		pdeo->release(pdeo->inode, pdeo->file);
787881adb85SAlexey Dobriyan 		kfree(pdeo);
788881adb85SAlexey Dobriyan 		spin_lock(&de->pde_unload_lock);
789881adb85SAlexey Dobriyan 	}
790881adb85SAlexey Dobriyan 	spin_unlock(&de->pde_unload_lock);
7918ce584c7SAl Viro }
7928ce584c7SAl Viro 
7938ce584c7SAl Viro /*
7948ce584c7SAl Viro  * Remove a /proc entry and free it if it's not currently in use.
7958ce584c7SAl Viro  */
7968ce584c7SAl Viro void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
7978ce584c7SAl Viro {
7988ce584c7SAl Viro 	struct proc_dir_entry **p;
7998ce584c7SAl Viro 	struct proc_dir_entry *de = NULL;
8008ce584c7SAl Viro 	const char *fn = name;
8018ce584c7SAl Viro 	unsigned int len;
8028ce584c7SAl Viro 
8038ce584c7SAl Viro 	spin_lock(&proc_subdir_lock);
8048ce584c7SAl Viro 	if (__xlate_proc_name(name, &parent, &fn) != 0) {
8058ce584c7SAl Viro 		spin_unlock(&proc_subdir_lock);
8068ce584c7SAl Viro 		return;
8078ce584c7SAl Viro 	}
8088ce584c7SAl Viro 	len = strlen(fn);
8098ce584c7SAl Viro 
8108ce584c7SAl Viro 	for (p = &parent->subdir; *p; p=&(*p)->next ) {
8118ce584c7SAl Viro 		if (proc_match(len, fn, *p)) {
8128ce584c7SAl Viro 			de = *p;
8138ce584c7SAl Viro 			*p = de->next;
8148ce584c7SAl Viro 			de->next = NULL;
8158ce584c7SAl Viro 			break;
8168ce584c7SAl Viro 		}
8178ce584c7SAl Viro 	}
8188ce584c7SAl Viro 	spin_unlock(&proc_subdir_lock);
8198ce584c7SAl Viro 	if (!de) {
8208ce584c7SAl Viro 		WARN(1, "name '%s'\n", name);
8218ce584c7SAl Viro 		return;
8228ce584c7SAl Viro 	}
8238ce584c7SAl Viro 
8248ce584c7SAl Viro 	entry_rundown(de);
825881adb85SAlexey Dobriyan 
8261da177e4SLinus Torvalds 	if (S_ISDIR(de->mode))
8271da177e4SLinus Torvalds 		parent->nlink--;
8281da177e4SLinus Torvalds 	de->nlink = 0;
82987ebdc00SAndrew Morton 	WARN(de->subdir, "%s: removing non-empty directory "
830e93b4ea2SAlexey Dobriyan 			 "'%s/%s', leaking at least '%s'\n", __func__,
831e93b4ea2SAlexey Dobriyan 			 de->parent->name, de->name, de->subdir->name);
832135d5655SAlexey Dobriyan 	pde_put(de);
8331da177e4SLinus Torvalds }
834587d4a17SHelight.Xu EXPORT_SYMBOL(remove_proc_entry);
8358ce584c7SAl Viro 
8368ce584c7SAl Viro int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
8378ce584c7SAl Viro {
8388ce584c7SAl Viro 	struct proc_dir_entry **p;
8398ce584c7SAl Viro 	struct proc_dir_entry *root = NULL, *de, *next;
8408ce584c7SAl Viro 	const char *fn = name;
8418ce584c7SAl Viro 	unsigned int len;
8428ce584c7SAl Viro 
8438ce584c7SAl Viro 	spin_lock(&proc_subdir_lock);
8448ce584c7SAl Viro 	if (__xlate_proc_name(name, &parent, &fn) != 0) {
8458ce584c7SAl Viro 		spin_unlock(&proc_subdir_lock);
8468ce584c7SAl Viro 		return -ENOENT;
8478ce584c7SAl Viro 	}
8488ce584c7SAl Viro 	len = strlen(fn);
8498ce584c7SAl Viro 
8508ce584c7SAl Viro 	for (p = &parent->subdir; *p; p=&(*p)->next ) {
8518ce584c7SAl Viro 		if (proc_match(len, fn, *p)) {
8528ce584c7SAl Viro 			root = *p;
8538ce584c7SAl Viro 			*p = root->next;
8548ce584c7SAl Viro 			root->next = NULL;
8558ce584c7SAl Viro 			break;
8568ce584c7SAl Viro 		}
8578ce584c7SAl Viro 	}
8588ce584c7SAl Viro 	if (!root) {
8598ce584c7SAl Viro 		spin_unlock(&proc_subdir_lock);
8608ce584c7SAl Viro 		return -ENOENT;
8618ce584c7SAl Viro 	}
8628ce584c7SAl Viro 	de = root;
8638ce584c7SAl Viro 	while (1) {
8648ce584c7SAl Viro 		next = de->subdir;
8658ce584c7SAl Viro 		if (next) {
8668ce584c7SAl Viro 			de->subdir = next->next;
8678ce584c7SAl Viro 			next->next = NULL;
8688ce584c7SAl Viro 			de = next;
8698ce584c7SAl Viro 			continue;
8708ce584c7SAl Viro 		}
8718ce584c7SAl Viro 		spin_unlock(&proc_subdir_lock);
8728ce584c7SAl Viro 
8738ce584c7SAl Viro 		entry_rundown(de);
8748ce584c7SAl Viro 		next = de->parent;
8758ce584c7SAl Viro 		if (S_ISDIR(de->mode))
8768ce584c7SAl Viro 			next->nlink--;
8778ce584c7SAl Viro 		de->nlink = 0;
8788ce584c7SAl Viro 		if (de == root)
8798ce584c7SAl Viro 			break;
8808ce584c7SAl Viro 		pde_put(de);
8818ce584c7SAl Viro 
8828ce584c7SAl Viro 		spin_lock(&proc_subdir_lock);
8838ce584c7SAl Viro 		de = next;
8848ce584c7SAl Viro 	}
8858ce584c7SAl Viro 	pde_put(root);
8868ce584c7SAl Viro 	return 0;
8878ce584c7SAl Viro }
8888ce584c7SAl Viro EXPORT_SYMBOL(remove_proc_subtree);
889