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