18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * proc/fs/generic.c --- generic routines for the proc-fs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This file contains generic proc-fs routines for handling 68c2ecf20Sopenharmony_ci * directories and files. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds. 98c2ecf20Sopenharmony_ci * Copyright (C) 1997 Theodore Ts'o 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/cache.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/time.h> 158c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 168c2ecf20Sopenharmony_ci#include <linux/stat.h> 178c2ecf20Sopenharmony_ci#include <linux/mm.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/namei.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/printk.h> 228c2ecf20Sopenharmony_ci#include <linux/mount.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci#include <linux/idr.h> 258c2ecf20Sopenharmony_ci#include <linux/bitops.h> 268c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 278c2ecf20Sopenharmony_ci#include <linux/completion.h> 288c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 298c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "internal.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(proc_subdir_lock); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct kmem_cache *proc_dir_entry_cache __ro_after_init; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_civoid pde_free(struct proc_dir_entry *pde) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci if (S_ISLNK(pde->mode)) 408c2ecf20Sopenharmony_ci kfree(pde->data); 418c2ecf20Sopenharmony_ci if (pde->name != pde->inline_name) 428c2ecf20Sopenharmony_ci kfree(pde->name); 438c2ecf20Sopenharmony_ci kmem_cache_free(proc_dir_entry_cache, pde); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int proc_match(const char *name, struct proc_dir_entry *de, unsigned int len) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci if (len < de->namelen) 498c2ecf20Sopenharmony_ci return -1; 508c2ecf20Sopenharmony_ci if (len > de->namelen) 518c2ecf20Sopenharmony_ci return 1; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return memcmp(name, de->name, len); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic struct proc_dir_entry *pde_subdir_first(struct proc_dir_entry *dir) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return rb_entry_safe(rb_first(&dir->subdir), struct proc_dir_entry, 598c2ecf20Sopenharmony_ci subdir_node); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return rb_entry_safe(rb_next(&dir->subdir_node), struct proc_dir_entry, 658c2ecf20Sopenharmony_ci subdir_node); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir, 698c2ecf20Sopenharmony_ci const char *name, 708c2ecf20Sopenharmony_ci unsigned int len) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct rb_node *node = dir->subdir.rb_node; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci while (node) { 758c2ecf20Sopenharmony_ci struct proc_dir_entry *de = rb_entry(node, 768c2ecf20Sopenharmony_ci struct proc_dir_entry, 778c2ecf20Sopenharmony_ci subdir_node); 788c2ecf20Sopenharmony_ci int result = proc_match(name, de, len); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (result < 0) 818c2ecf20Sopenharmony_ci node = node->rb_left; 828c2ecf20Sopenharmony_ci else if (result > 0) 838c2ecf20Sopenharmony_ci node = node->rb_right; 848c2ecf20Sopenharmony_ci else 858c2ecf20Sopenharmony_ci return de; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci return NULL; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic bool pde_subdir_insert(struct proc_dir_entry *dir, 918c2ecf20Sopenharmony_ci struct proc_dir_entry *de) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct rb_root *root = &dir->subdir; 948c2ecf20Sopenharmony_ci struct rb_node **new = &root->rb_node, *parent = NULL; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Figure out where to put new node */ 978c2ecf20Sopenharmony_ci while (*new) { 988c2ecf20Sopenharmony_ci struct proc_dir_entry *this = rb_entry(*new, 998c2ecf20Sopenharmony_ci struct proc_dir_entry, 1008c2ecf20Sopenharmony_ci subdir_node); 1018c2ecf20Sopenharmony_ci int result = proc_match(de->name, this, de->namelen); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci parent = *new; 1048c2ecf20Sopenharmony_ci if (result < 0) 1058c2ecf20Sopenharmony_ci new = &(*new)->rb_left; 1068c2ecf20Sopenharmony_ci else if (result > 0) 1078c2ecf20Sopenharmony_ci new = &(*new)->rb_right; 1088c2ecf20Sopenharmony_ci else 1098c2ecf20Sopenharmony_ci return false; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Add new node and rebalance tree. */ 1138c2ecf20Sopenharmony_ci rb_link_node(&de->subdir_node, parent, new); 1148c2ecf20Sopenharmony_ci rb_insert_color(&de->subdir_node, root); 1158c2ecf20Sopenharmony_ci return true; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int proc_notify_change(struct dentry *dentry, struct iattr *iattr) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct inode *inode = d_inode(dentry); 1218c2ecf20Sopenharmony_ci struct proc_dir_entry *de = PDE(inode); 1228c2ecf20Sopenharmony_ci int error; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci error = setattr_prepare(dentry, iattr); 1258c2ecf20Sopenharmony_ci if (error) 1268c2ecf20Sopenharmony_ci return error; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci setattr_copy(inode, iattr); 1298c2ecf20Sopenharmony_ci mark_inode_dirty(inode); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci proc_set_user(de, inode->i_uid, inode->i_gid); 1328c2ecf20Sopenharmony_ci de->mode = inode->i_mode; 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int proc_getattr(const struct path *path, struct kstat *stat, 1378c2ecf20Sopenharmony_ci u32 request_mask, unsigned int query_flags) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct inode *inode = d_inode(path->dentry); 1408c2ecf20Sopenharmony_ci struct proc_dir_entry *de = PDE(inode); 1418c2ecf20Sopenharmony_ci if (de) { 1428c2ecf20Sopenharmony_ci nlink_t nlink = READ_ONCE(de->nlink); 1438c2ecf20Sopenharmony_ci if (nlink > 0) { 1448c2ecf20Sopenharmony_ci set_nlink(inode, nlink); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci generic_fillattr(inode, stat); 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic const struct inode_operations proc_file_inode_operations = { 1538c2ecf20Sopenharmony_ci .setattr = proc_notify_change, 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* 1578c2ecf20Sopenharmony_ci * This function parses a name such as "tty/driver/serial", and 1588c2ecf20Sopenharmony_ci * returns the struct proc_dir_entry for "/proc/tty/driver", and 1598c2ecf20Sopenharmony_ci * returns "serial" in residual. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic int __xlate_proc_name(const char *name, struct proc_dir_entry **ret, 1628c2ecf20Sopenharmony_ci const char **residual) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci const char *cp = name, *next; 1658c2ecf20Sopenharmony_ci struct proc_dir_entry *de; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci de = *ret; 1688c2ecf20Sopenharmony_ci if (!de) 1698c2ecf20Sopenharmony_ci de = &proc_root; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci while (1) { 1728c2ecf20Sopenharmony_ci next = strchr(cp, '/'); 1738c2ecf20Sopenharmony_ci if (!next) 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci de = pde_subdir_find(de, cp, next - cp); 1778c2ecf20Sopenharmony_ci if (!de) { 1788c2ecf20Sopenharmony_ci WARN(1, "name '%s'\n", name); 1798c2ecf20Sopenharmony_ci return -ENOENT; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci cp = next + 1; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci *residual = cp; 1848c2ecf20Sopenharmony_ci *ret = de; 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int xlate_proc_name(const char *name, struct proc_dir_entry **ret, 1898c2ecf20Sopenharmony_ci const char **residual) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci int rv; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci read_lock(&proc_subdir_lock); 1948c2ecf20Sopenharmony_ci rv = __xlate_proc_name(name, ret, residual); 1958c2ecf20Sopenharmony_ci read_unlock(&proc_subdir_lock); 1968c2ecf20Sopenharmony_ci return rv; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic DEFINE_IDA(proc_inum_ida); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci#define PROC_DYNAMIC_FIRST 0xF0000000U 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* 2048c2ecf20Sopenharmony_ci * Return an inode number between PROC_DYNAMIC_FIRST and 2058c2ecf20Sopenharmony_ci * 0xffffffff, or zero on failure. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ciint proc_alloc_inum(unsigned int *inum) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci int i; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1, 2128c2ecf20Sopenharmony_ci GFP_KERNEL); 2138c2ecf20Sopenharmony_ci if (i < 0) 2148c2ecf20Sopenharmony_ci return i; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci *inum = PROC_DYNAMIC_FIRST + (unsigned int)i; 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_civoid proc_free_inum(unsigned int inum) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci ida_simple_remove(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int proc_misc_d_revalidate(struct dentry *dentry, unsigned int flags) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci if (flags & LOOKUP_RCU) 2288c2ecf20Sopenharmony_ci return -ECHILD; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (atomic_read(&PDE(d_inode(dentry))->in_use) < 0) 2318c2ecf20Sopenharmony_ci return 0; /* revalidate */ 2328c2ecf20Sopenharmony_ci return 1; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int proc_misc_d_delete(const struct dentry *dentry) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci return atomic_read(&PDE(d_inode(dentry))->in_use) < 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic const struct dentry_operations proc_misc_dentry_ops = { 2418c2ecf20Sopenharmony_ci .d_revalidate = proc_misc_d_revalidate, 2428c2ecf20Sopenharmony_ci .d_delete = proc_misc_d_delete, 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* 2468c2ecf20Sopenharmony_ci * Don't create negative dentries here, return -ENOENT by hand 2478c2ecf20Sopenharmony_ci * instead. 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_cistruct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry, 2508c2ecf20Sopenharmony_ci struct proc_dir_entry *de) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct inode *inode; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci read_lock(&proc_subdir_lock); 2558c2ecf20Sopenharmony_ci de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len); 2568c2ecf20Sopenharmony_ci if (de) { 2578c2ecf20Sopenharmony_ci pde_get(de); 2588c2ecf20Sopenharmony_ci read_unlock(&proc_subdir_lock); 2598c2ecf20Sopenharmony_ci inode = proc_get_inode(dir->i_sb, de); 2608c2ecf20Sopenharmony_ci if (!inode) 2618c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2628c2ecf20Sopenharmony_ci d_set_d_op(dentry, de->proc_dops); 2638c2ecf20Sopenharmony_ci return d_splice_alias(inode, dentry); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci read_unlock(&proc_subdir_lock); 2668c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistruct dentry *proc_lookup(struct inode *dir, struct dentry *dentry, 2708c2ecf20Sopenharmony_ci unsigned int flags) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct proc_fs_info *fs_info = proc_sb_info(dir->i_sb); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (fs_info->pidonly == PROC_PIDONLY_ON) 2758c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return proc_lookup_de(dir, dentry, PDE(dir)); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/* 2818c2ecf20Sopenharmony_ci * This returns non-zero if at EOF, so that the /proc 2828c2ecf20Sopenharmony_ci * root directory can use this and check if it should 2838c2ecf20Sopenharmony_ci * continue with the <pid> entries.. 2848c2ecf20Sopenharmony_ci * 2858c2ecf20Sopenharmony_ci * Note that the VFS-layer doesn't care about the return 2868c2ecf20Sopenharmony_ci * value of the readdir() call, as long as it's non-negative 2878c2ecf20Sopenharmony_ci * for success.. 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ciint proc_readdir_de(struct file *file, struct dir_context *ctx, 2908c2ecf20Sopenharmony_ci struct proc_dir_entry *de) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci int i; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (!dir_emit_dots(file, ctx)) 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci i = ctx->pos - 2; 2988c2ecf20Sopenharmony_ci read_lock(&proc_subdir_lock); 2998c2ecf20Sopenharmony_ci de = pde_subdir_first(de); 3008c2ecf20Sopenharmony_ci for (;;) { 3018c2ecf20Sopenharmony_ci if (!de) { 3028c2ecf20Sopenharmony_ci read_unlock(&proc_subdir_lock); 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci if (!i) 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci de = pde_subdir_next(de); 3088c2ecf20Sopenharmony_ci i--; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci do { 3128c2ecf20Sopenharmony_ci struct proc_dir_entry *next; 3138c2ecf20Sopenharmony_ci pde_get(de); 3148c2ecf20Sopenharmony_ci read_unlock(&proc_subdir_lock); 3158c2ecf20Sopenharmony_ci if (!dir_emit(ctx, de->name, de->namelen, 3168c2ecf20Sopenharmony_ci de->low_ino, de->mode >> 12)) { 3178c2ecf20Sopenharmony_ci pde_put(de); 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci ctx->pos++; 3218c2ecf20Sopenharmony_ci read_lock(&proc_subdir_lock); 3228c2ecf20Sopenharmony_ci next = pde_subdir_next(de); 3238c2ecf20Sopenharmony_ci pde_put(de); 3248c2ecf20Sopenharmony_ci de = next; 3258c2ecf20Sopenharmony_ci } while (de); 3268c2ecf20Sopenharmony_ci read_unlock(&proc_subdir_lock); 3278c2ecf20Sopenharmony_ci return 1; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ciint proc_readdir(struct file *file, struct dir_context *ctx) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 3338c2ecf20Sopenharmony_ci struct proc_fs_info *fs_info = proc_sb_info(inode->i_sb); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (fs_info->pidonly == PROC_PIDONLY_ON) 3368c2ecf20Sopenharmony_ci return 1; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return proc_readdir_de(file, ctx, PDE(inode)); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/* 3428c2ecf20Sopenharmony_ci * These are the generic /proc directory operations. They 3438c2ecf20Sopenharmony_ci * use the in-memory "struct proc_dir_entry" tree to parse 3448c2ecf20Sopenharmony_ci * the /proc directory. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_cistatic const struct file_operations proc_dir_operations = { 3478c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 3488c2ecf20Sopenharmony_ci .read = generic_read_dir, 3498c2ecf20Sopenharmony_ci .iterate_shared = proc_readdir, 3508c2ecf20Sopenharmony_ci}; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ciconst struct dentry_operations proc_net_dentry_ops = { 3588c2ecf20Sopenharmony_ci .d_revalidate = proc_net_d_revalidate, 3598c2ecf20Sopenharmony_ci .d_delete = always_delete_dentry, 3608c2ecf20Sopenharmony_ci}; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/* 3638c2ecf20Sopenharmony_ci * proc directories can do almost nothing.. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_cistatic const struct inode_operations proc_dir_inode_operations = { 3668c2ecf20Sopenharmony_ci .lookup = proc_lookup, 3678c2ecf20Sopenharmony_ci .getattr = proc_getattr, 3688c2ecf20Sopenharmony_ci .setattr = proc_notify_change, 3698c2ecf20Sopenharmony_ci}; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci/* returns the registered entry, or frees dp and returns NULL on failure */ 3728c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_register(struct proc_dir_entry *dir, 3738c2ecf20Sopenharmony_ci struct proc_dir_entry *dp) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci if (proc_alloc_inum(&dp->low_ino)) 3768c2ecf20Sopenharmony_ci goto out_free_entry; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci write_lock(&proc_subdir_lock); 3798c2ecf20Sopenharmony_ci dp->parent = dir; 3808c2ecf20Sopenharmony_ci if (pde_subdir_insert(dir, dp) == false) { 3818c2ecf20Sopenharmony_ci WARN(1, "proc_dir_entry '%s/%s' already registered\n", 3828c2ecf20Sopenharmony_ci dir->name, dp->name); 3838c2ecf20Sopenharmony_ci write_unlock(&proc_subdir_lock); 3848c2ecf20Sopenharmony_ci goto out_free_inum; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci dir->nlink++; 3878c2ecf20Sopenharmony_ci write_unlock(&proc_subdir_lock); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return dp; 3908c2ecf20Sopenharmony_ciout_free_inum: 3918c2ecf20Sopenharmony_ci proc_free_inum(dp->low_ino); 3928c2ecf20Sopenharmony_ciout_free_entry: 3938c2ecf20Sopenharmony_ci pde_free(dp); 3948c2ecf20Sopenharmony_ci return NULL; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent, 3988c2ecf20Sopenharmony_ci const char *name, 3998c2ecf20Sopenharmony_ci umode_t mode, 4008c2ecf20Sopenharmony_ci nlink_t nlink) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct proc_dir_entry *ent = NULL; 4038c2ecf20Sopenharmony_ci const char *fn; 4048c2ecf20Sopenharmony_ci struct qstr qstr; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (xlate_proc_name(name, parent, &fn) != 0) 4078c2ecf20Sopenharmony_ci goto out; 4088c2ecf20Sopenharmony_ci qstr.name = fn; 4098c2ecf20Sopenharmony_ci qstr.len = strlen(fn); 4108c2ecf20Sopenharmony_ci if (qstr.len == 0 || qstr.len >= 256) { 4118c2ecf20Sopenharmony_ci WARN(1, "name len %u\n", qstr.len); 4128c2ecf20Sopenharmony_ci return NULL; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci if (qstr.len == 1 && fn[0] == '.') { 4158c2ecf20Sopenharmony_ci WARN(1, "name '.'\n"); 4168c2ecf20Sopenharmony_ci return NULL; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci if (qstr.len == 2 && fn[0] == '.' && fn[1] == '.') { 4198c2ecf20Sopenharmony_ci WARN(1, "name '..'\n"); 4208c2ecf20Sopenharmony_ci return NULL; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci if (*parent == &proc_root && name_to_int(&qstr) != ~0U) { 4238c2ecf20Sopenharmony_ci WARN(1, "create '/proc/%s' by hand\n", qstr.name); 4248c2ecf20Sopenharmony_ci return NULL; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci if (is_empty_pde(*parent)) { 4278c2ecf20Sopenharmony_ci WARN(1, "attempt to add to permanently empty directory"); 4288c2ecf20Sopenharmony_ci return NULL; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci ent = kmem_cache_zalloc(proc_dir_entry_cache, GFP_KERNEL); 4328c2ecf20Sopenharmony_ci if (!ent) 4338c2ecf20Sopenharmony_ci goto out; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (qstr.len + 1 <= SIZEOF_PDE_INLINE_NAME) { 4368c2ecf20Sopenharmony_ci ent->name = ent->inline_name; 4378c2ecf20Sopenharmony_ci } else { 4388c2ecf20Sopenharmony_ci ent->name = kmalloc(qstr.len + 1, GFP_KERNEL); 4398c2ecf20Sopenharmony_ci if (!ent->name) { 4408c2ecf20Sopenharmony_ci pde_free(ent); 4418c2ecf20Sopenharmony_ci return NULL; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci memcpy(ent->name, fn, qstr.len + 1); 4468c2ecf20Sopenharmony_ci ent->namelen = qstr.len; 4478c2ecf20Sopenharmony_ci ent->mode = mode; 4488c2ecf20Sopenharmony_ci ent->nlink = nlink; 4498c2ecf20Sopenharmony_ci ent->subdir = RB_ROOT; 4508c2ecf20Sopenharmony_ci refcount_set(&ent->refcnt, 1); 4518c2ecf20Sopenharmony_ci spin_lock_init(&ent->pde_unload_lock); 4528c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ent->pde_openers); 4538c2ecf20Sopenharmony_ci proc_set_user(ent, (*parent)->uid, (*parent)->gid); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci ent->proc_dops = &proc_misc_dentry_ops; 4568c2ecf20Sopenharmony_ci /* Revalidate everything under /proc/${pid}/net */ 4578c2ecf20Sopenharmony_ci if ((*parent)->proc_dops == &proc_net_dentry_ops) 4588c2ecf20Sopenharmony_ci pde_force_lookup(ent); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ciout: 4618c2ecf20Sopenharmony_ci return ent; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_symlink(const char *name, 4658c2ecf20Sopenharmony_ci struct proc_dir_entry *parent, const char *dest) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct proc_dir_entry *ent; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci ent = __proc_create(&parent, name, 4708c2ecf20Sopenharmony_ci (S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO),1); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (ent) { 4738c2ecf20Sopenharmony_ci ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL); 4748c2ecf20Sopenharmony_ci if (ent->data) { 4758c2ecf20Sopenharmony_ci strcpy((char*)ent->data,dest); 4768c2ecf20Sopenharmony_ci ent->proc_iops = &proc_link_inode_operations; 4778c2ecf20Sopenharmony_ci ent = proc_register(parent, ent); 4788c2ecf20Sopenharmony_ci } else { 4798c2ecf20Sopenharmony_ci pde_free(ent); 4808c2ecf20Sopenharmony_ci ent = NULL; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci return ent; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_symlink); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistruct proc_dir_entry *_proc_mkdir(const char *name, umode_t mode, 4888c2ecf20Sopenharmony_ci struct proc_dir_entry *parent, void *data, bool force_lookup) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct proc_dir_entry *ent; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (mode == 0) 4938c2ecf20Sopenharmony_ci mode = S_IRUGO | S_IXUGO; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci ent = __proc_create(&parent, name, S_IFDIR | mode, 2); 4968c2ecf20Sopenharmony_ci if (ent) { 4978c2ecf20Sopenharmony_ci ent->data = data; 4988c2ecf20Sopenharmony_ci ent->proc_dir_ops = &proc_dir_operations; 4998c2ecf20Sopenharmony_ci ent->proc_iops = &proc_dir_inode_operations; 5008c2ecf20Sopenharmony_ci if (force_lookup) { 5018c2ecf20Sopenharmony_ci pde_force_lookup(ent); 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci ent = proc_register(parent, ent); 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci return ent; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(_proc_mkdir); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode, 5108c2ecf20Sopenharmony_ci struct proc_dir_entry *parent, void *data) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci return _proc_mkdir(name, mode, parent, data, false); 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(proc_mkdir_data); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode, 5178c2ecf20Sopenharmony_ci struct proc_dir_entry *parent) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci return proc_mkdir_data(name, mode, parent, NULL); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_mkdir_mode); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_mkdir(const char *name, 5248c2ecf20Sopenharmony_ci struct proc_dir_entry *parent) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci return proc_mkdir_data(name, 0, parent, NULL); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_mkdir); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create_mount_point(const char *name) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci umode_t mode = S_IFDIR | S_IRUGO | S_IXUGO; 5338c2ecf20Sopenharmony_ci struct proc_dir_entry *ent, *parent = NULL; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ent = __proc_create(&parent, name, mode, 2); 5368c2ecf20Sopenharmony_ci if (ent) { 5378c2ecf20Sopenharmony_ci ent->data = NULL; 5388c2ecf20Sopenharmony_ci ent->proc_dir_ops = NULL; 5398c2ecf20Sopenharmony_ci ent->proc_iops = NULL; 5408c2ecf20Sopenharmony_ci ent = proc_register(parent, ent); 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci return ent; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_create_mount_point); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create_reg(const char *name, umode_t mode, 5478c2ecf20Sopenharmony_ci struct proc_dir_entry **parent, void *data) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct proc_dir_entry *p; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if ((mode & S_IFMT) == 0) 5528c2ecf20Sopenharmony_ci mode |= S_IFREG; 5538c2ecf20Sopenharmony_ci if ((mode & S_IALLUGO) == 0) 5548c2ecf20Sopenharmony_ci mode |= S_IRUGO; 5558c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!S_ISREG(mode))) 5568c2ecf20Sopenharmony_ci return NULL; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci p = __proc_create(parent, name, mode, 1); 5598c2ecf20Sopenharmony_ci if (p) { 5608c2ecf20Sopenharmony_ci p->proc_iops = &proc_file_inode_operations; 5618c2ecf20Sopenharmony_ci p->data = data; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci return p; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic inline void pde_set_flags(struct proc_dir_entry *pde) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT) 5698c2ecf20Sopenharmony_ci pde->flags |= PROC_ENTRY_PERMANENT; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create_data(const char *name, umode_t mode, 5738c2ecf20Sopenharmony_ci struct proc_dir_entry *parent, 5748c2ecf20Sopenharmony_ci const struct proc_ops *proc_ops, void *data) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct proc_dir_entry *p; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci p = proc_create_reg(name, mode, &parent, data); 5798c2ecf20Sopenharmony_ci if (!p) 5808c2ecf20Sopenharmony_ci return NULL; 5818c2ecf20Sopenharmony_ci p->proc_ops = proc_ops; 5828c2ecf20Sopenharmony_ci pde_set_flags(p); 5838c2ecf20Sopenharmony_ci return proc_register(parent, p); 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_create_data); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create(const char *name, umode_t mode, 5888c2ecf20Sopenharmony_ci struct proc_dir_entry *parent, 5898c2ecf20Sopenharmony_ci const struct proc_ops *proc_ops) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci return proc_create_data(name, mode, parent, proc_ops, NULL); 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_create); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic int proc_seq_open(struct inode *inode, struct file *file) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct proc_dir_entry *de = PDE(inode); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (de->state_size) 6008c2ecf20Sopenharmony_ci return seq_open_private(file, de->seq_ops, de->state_size); 6018c2ecf20Sopenharmony_ci return seq_open(file, de->seq_ops); 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cistatic int proc_seq_release(struct inode *inode, struct file *file) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct proc_dir_entry *de = PDE(inode); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (de->state_size) 6098c2ecf20Sopenharmony_ci return seq_release_private(inode, file); 6108c2ecf20Sopenharmony_ci return seq_release(inode, file); 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic const struct proc_ops proc_seq_ops = { 6148c2ecf20Sopenharmony_ci /* not permanent -- can call into arbitrary seq_operations */ 6158c2ecf20Sopenharmony_ci .proc_open = proc_seq_open, 6168c2ecf20Sopenharmony_ci .proc_read_iter = seq_read_iter, 6178c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 6188c2ecf20Sopenharmony_ci .proc_release = proc_seq_release, 6198c2ecf20Sopenharmony_ci}; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode, 6228c2ecf20Sopenharmony_ci struct proc_dir_entry *parent, const struct seq_operations *ops, 6238c2ecf20Sopenharmony_ci unsigned int state_size, void *data) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct proc_dir_entry *p; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci p = proc_create_reg(name, mode, &parent, data); 6288c2ecf20Sopenharmony_ci if (!p) 6298c2ecf20Sopenharmony_ci return NULL; 6308c2ecf20Sopenharmony_ci p->proc_ops = &proc_seq_ops; 6318c2ecf20Sopenharmony_ci p->seq_ops = ops; 6328c2ecf20Sopenharmony_ci p->state_size = state_size; 6338c2ecf20Sopenharmony_ci return proc_register(parent, p); 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_create_seq_private); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic int proc_single_open(struct inode *inode, struct file *file) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct proc_dir_entry *de = PDE(inode); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return single_open(file, de->single_show, de->data); 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic const struct proc_ops proc_single_ops = { 6458c2ecf20Sopenharmony_ci /* not permanent -- can call into arbitrary ->single_show */ 6468c2ecf20Sopenharmony_ci .proc_open = proc_single_open, 6478c2ecf20Sopenharmony_ci .proc_read_iter = seq_read_iter, 6488c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 6498c2ecf20Sopenharmony_ci .proc_release = single_release, 6508c2ecf20Sopenharmony_ci}; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistruct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode, 6538c2ecf20Sopenharmony_ci struct proc_dir_entry *parent, 6548c2ecf20Sopenharmony_ci int (*show)(struct seq_file *, void *), void *data) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct proc_dir_entry *p; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci p = proc_create_reg(name, mode, &parent, data); 6598c2ecf20Sopenharmony_ci if (!p) 6608c2ecf20Sopenharmony_ci return NULL; 6618c2ecf20Sopenharmony_ci p->proc_ops = &proc_single_ops; 6628c2ecf20Sopenharmony_ci p->single_show = show; 6638c2ecf20Sopenharmony_ci return proc_register(parent, p); 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_create_single_data); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_civoid proc_set_size(struct proc_dir_entry *de, loff_t size) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci de->size = size; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_set_size); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_civoid proc_set_user(struct proc_dir_entry *de, kuid_t uid, kgid_t gid) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci de->uid = uid; 6768c2ecf20Sopenharmony_ci de->gid = gid; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_set_user); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_civoid pde_put(struct proc_dir_entry *pde) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&pde->refcnt)) { 6838c2ecf20Sopenharmony_ci proc_free_inum(pde->low_ino); 6848c2ecf20Sopenharmony_ci pde_free(pde); 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci/* 6898c2ecf20Sopenharmony_ci * Remove a /proc entry and free it if it's not currently in use. 6908c2ecf20Sopenharmony_ci */ 6918c2ecf20Sopenharmony_civoid remove_proc_entry(const char *name, struct proc_dir_entry *parent) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci struct proc_dir_entry *de = NULL; 6948c2ecf20Sopenharmony_ci const char *fn = name; 6958c2ecf20Sopenharmony_ci unsigned int len; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci write_lock(&proc_subdir_lock); 6988c2ecf20Sopenharmony_ci if (__xlate_proc_name(name, &parent, &fn) != 0) { 6998c2ecf20Sopenharmony_ci write_unlock(&proc_subdir_lock); 7008c2ecf20Sopenharmony_ci return; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci len = strlen(fn); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci de = pde_subdir_find(parent, fn, len); 7058c2ecf20Sopenharmony_ci if (de) { 7068c2ecf20Sopenharmony_ci if (unlikely(pde_is_permanent(de))) { 7078c2ecf20Sopenharmony_ci WARN(1, "removing permanent /proc entry '%s'", de->name); 7088c2ecf20Sopenharmony_ci de = NULL; 7098c2ecf20Sopenharmony_ci } else { 7108c2ecf20Sopenharmony_ci rb_erase(&de->subdir_node, &parent->subdir); 7118c2ecf20Sopenharmony_ci if (S_ISDIR(de->mode)) 7128c2ecf20Sopenharmony_ci parent->nlink--; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci write_unlock(&proc_subdir_lock); 7168c2ecf20Sopenharmony_ci if (!de) { 7178c2ecf20Sopenharmony_ci WARN(1, "name '%s'\n", name); 7188c2ecf20Sopenharmony_ci return; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci proc_entry_rundown(de); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci WARN(pde_subdir_first(de), 7248c2ecf20Sopenharmony_ci "%s: removing non-empty directory '%s/%s', leaking at least '%s'\n", 7258c2ecf20Sopenharmony_ci __func__, de->parent->name, de->name, pde_subdir_first(de)->name); 7268c2ecf20Sopenharmony_ci pde_put(de); 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(remove_proc_entry); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ciint remove_proc_subtree(const char *name, struct proc_dir_entry *parent) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci struct proc_dir_entry *root = NULL, *de, *next; 7338c2ecf20Sopenharmony_ci const char *fn = name; 7348c2ecf20Sopenharmony_ci unsigned int len; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci write_lock(&proc_subdir_lock); 7378c2ecf20Sopenharmony_ci if (__xlate_proc_name(name, &parent, &fn) != 0) { 7388c2ecf20Sopenharmony_ci write_unlock(&proc_subdir_lock); 7398c2ecf20Sopenharmony_ci return -ENOENT; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci len = strlen(fn); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci root = pde_subdir_find(parent, fn, len); 7448c2ecf20Sopenharmony_ci if (!root) { 7458c2ecf20Sopenharmony_ci write_unlock(&proc_subdir_lock); 7468c2ecf20Sopenharmony_ci return -ENOENT; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci if (unlikely(pde_is_permanent(root))) { 7498c2ecf20Sopenharmony_ci write_unlock(&proc_subdir_lock); 7508c2ecf20Sopenharmony_ci WARN(1, "removing permanent /proc entry '%s/%s'", 7518c2ecf20Sopenharmony_ci root->parent->name, root->name); 7528c2ecf20Sopenharmony_ci return -EINVAL; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci rb_erase(&root->subdir_node, &parent->subdir); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci de = root; 7578c2ecf20Sopenharmony_ci while (1) { 7588c2ecf20Sopenharmony_ci next = pde_subdir_first(de); 7598c2ecf20Sopenharmony_ci if (next) { 7608c2ecf20Sopenharmony_ci if (unlikely(pde_is_permanent(next))) { 7618c2ecf20Sopenharmony_ci write_unlock(&proc_subdir_lock); 7628c2ecf20Sopenharmony_ci WARN(1, "removing permanent /proc entry '%s/%s'", 7638c2ecf20Sopenharmony_ci next->parent->name, next->name); 7648c2ecf20Sopenharmony_ci return -EINVAL; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci rb_erase(&next->subdir_node, &de->subdir); 7678c2ecf20Sopenharmony_ci de = next; 7688c2ecf20Sopenharmony_ci continue; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci next = de->parent; 7718c2ecf20Sopenharmony_ci if (S_ISDIR(de->mode)) 7728c2ecf20Sopenharmony_ci next->nlink--; 7738c2ecf20Sopenharmony_ci write_unlock(&proc_subdir_lock); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci proc_entry_rundown(de); 7768c2ecf20Sopenharmony_ci if (de == root) 7778c2ecf20Sopenharmony_ci break; 7788c2ecf20Sopenharmony_ci pde_put(de); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci write_lock(&proc_subdir_lock); 7818c2ecf20Sopenharmony_ci de = next; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci pde_put(root); 7848c2ecf20Sopenharmony_ci return 0; 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(remove_proc_subtree); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_civoid *proc_get_parent_data(const struct inode *inode) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct proc_dir_entry *de = PDE(inode); 7918c2ecf20Sopenharmony_ci return de->parent->data; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(proc_get_parent_data); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_civoid proc_remove(struct proc_dir_entry *de) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci if (de) 7988c2ecf20Sopenharmony_ci remove_proc_subtree(de->name, de->parent); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(proc_remove); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_civoid *PDE_DATA(const struct inode *inode) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci return __PDE_DATA(inode); 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(PDE_DATA); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci/* 8098c2ecf20Sopenharmony_ci * Pull a user buffer into memory and pass it to the file's write handler if 8108c2ecf20Sopenharmony_ci * one is supplied. The ->write() method is permitted to modify the 8118c2ecf20Sopenharmony_ci * kernel-side buffer. 8128c2ecf20Sopenharmony_ci */ 8138c2ecf20Sopenharmony_cissize_t proc_simple_write(struct file *f, const char __user *ubuf, size_t size, 8148c2ecf20Sopenharmony_ci loff_t *_pos) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci struct proc_dir_entry *pde = PDE(file_inode(f)); 8178c2ecf20Sopenharmony_ci char *buf; 8188c2ecf20Sopenharmony_ci int ret; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (!pde->write) 8218c2ecf20Sopenharmony_ci return -EACCES; 8228c2ecf20Sopenharmony_ci if (size == 0 || size > PAGE_SIZE - 1) 8238c2ecf20Sopenharmony_ci return -EINVAL; 8248c2ecf20Sopenharmony_ci buf = memdup_user_nul(ubuf, size); 8258c2ecf20Sopenharmony_ci if (IS_ERR(buf)) 8268c2ecf20Sopenharmony_ci return PTR_ERR(buf); 8278c2ecf20Sopenharmony_ci ret = pde->write(f, buf, size); 8288c2ecf20Sopenharmony_ci kfree(buf); 8298c2ecf20Sopenharmony_ci return ret == 0 ? size : ret; 8308c2ecf20Sopenharmony_ci} 831