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