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