177b14db5SEric W. Biederman /* 277b14db5SEric W. Biederman * /proc/sys support 377b14db5SEric W. Biederman */ 477b14db5SEric W. Biederman 577b14db5SEric W. Biederman #include <linux/sysctl.h> 677b14db5SEric W. Biederman #include <linux/proc_fs.h> 777b14db5SEric W. Biederman #include <linux/security.h> 877b14db5SEric W. Biederman #include "internal.h" 977b14db5SEric W. Biederman 1077b14db5SEric W. Biederman static struct dentry_operations proc_sys_dentry_operations; 1177b14db5SEric W. Biederman static const struct file_operations proc_sys_file_operations; 1277b14db5SEric W. Biederman static struct inode_operations proc_sys_inode_operations; 1377b14db5SEric W. Biederman 1477b14db5SEric W. Biederman static void proc_sys_refresh_inode(struct inode *inode, struct ctl_table *table) 1577b14db5SEric W. Biederman { 1677b14db5SEric W. Biederman /* Refresh the cached information bits in the inode */ 1777b14db5SEric W. Biederman if (table) { 1877b14db5SEric W. Biederman inode->i_uid = 0; 1977b14db5SEric W. Biederman inode->i_gid = 0; 2077b14db5SEric W. Biederman inode->i_mode = table->mode; 2177b14db5SEric W. Biederman if (table->proc_handler) { 2277b14db5SEric W. Biederman inode->i_mode |= S_IFREG; 2377b14db5SEric W. Biederman inode->i_nlink = 1; 2477b14db5SEric W. Biederman } else { 2577b14db5SEric W. Biederman inode->i_mode |= S_IFDIR; 2677b14db5SEric W. Biederman inode->i_nlink = 0; /* It is too hard to figure out */ 2777b14db5SEric W. Biederman } 2877b14db5SEric W. Biederman } 2977b14db5SEric W. Biederman } 3077b14db5SEric W. Biederman 3177b14db5SEric W. Biederman static struct inode *proc_sys_make_inode(struct inode *dir, struct ctl_table *table) 3277b14db5SEric W. Biederman { 3377b14db5SEric W. Biederman struct inode *inode; 3477b14db5SEric W. Biederman struct proc_inode *dir_ei, *ei; 3577b14db5SEric W. Biederman int depth; 3677b14db5SEric W. Biederman 3777b14db5SEric W. Biederman inode = new_inode(dir->i_sb); 3877b14db5SEric W. Biederman if (!inode) 3977b14db5SEric W. Biederman goto out; 4077b14db5SEric W. Biederman 4177b14db5SEric W. Biederman /* A directory is always one deeper than it's parent */ 4277b14db5SEric W. Biederman dir_ei = PROC_I(dir); 4377b14db5SEric W. Biederman depth = dir_ei->fd + 1; 4477b14db5SEric W. Biederman 4577b14db5SEric W. Biederman ei = PROC_I(inode); 4677b14db5SEric W. Biederman ei->fd = depth; 4777b14db5SEric W. Biederman inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; 4877b14db5SEric W. Biederman inode->i_op = &proc_sys_inode_operations; 4977b14db5SEric W. Biederman inode->i_fop = &proc_sys_file_operations; 5077b14db5SEric W. Biederman proc_sys_refresh_inode(inode, table); 5177b14db5SEric W. Biederman out: 5277b14db5SEric W. Biederman return inode; 5377b14db5SEric W. Biederman } 5477b14db5SEric W. Biederman 5577b14db5SEric W. Biederman static struct dentry *proc_sys_ancestor(struct dentry *dentry, int depth) 5677b14db5SEric W. Biederman { 5777b14db5SEric W. Biederman for (;;) { 5877b14db5SEric W. Biederman struct proc_inode *ei; 5977b14db5SEric W. Biederman 6077b14db5SEric W. Biederman ei = PROC_I(dentry->d_inode); 6177b14db5SEric W. Biederman if (ei->fd == depth) 6277b14db5SEric W. Biederman break; /* found */ 6377b14db5SEric W. Biederman 6477b14db5SEric W. Biederman dentry = dentry->d_parent; 6577b14db5SEric W. Biederman } 6677b14db5SEric W. Biederman return dentry; 6777b14db5SEric W. Biederman } 6877b14db5SEric W. Biederman 6977b14db5SEric W. Biederman static struct ctl_table *proc_sys_lookup_table_one(struct ctl_table *table, 7077b14db5SEric W. Biederman struct qstr *name) 7177b14db5SEric W. Biederman { 7277b14db5SEric W. Biederman int len; 7377b14db5SEric W. Biederman for ( ; table->ctl_name || table->procname; table++) { 7477b14db5SEric W. Biederman 7577b14db5SEric W. Biederman if (!table->procname) 7677b14db5SEric W. Biederman continue; 7777b14db5SEric W. Biederman 7877b14db5SEric W. Biederman len = strlen(table->procname); 7977b14db5SEric W. Biederman if (len != name->len) 8077b14db5SEric W. Biederman continue; 8177b14db5SEric W. Biederman 8277b14db5SEric W. Biederman if (memcmp(table->procname, name->name, len) != 0) 8377b14db5SEric W. Biederman continue; 8477b14db5SEric W. Biederman 8577b14db5SEric W. Biederman /* I have a match */ 8677b14db5SEric W. Biederman return table; 8777b14db5SEric W. Biederman } 8877b14db5SEric W. Biederman return NULL; 8977b14db5SEric W. Biederman } 9077b14db5SEric W. Biederman 9177b14db5SEric W. Biederman static struct ctl_table *proc_sys_lookup_table(struct dentry *dentry, 9277b14db5SEric W. Biederman struct ctl_table *table) 9377b14db5SEric W. Biederman { 9477b14db5SEric W. Biederman struct dentry *ancestor; 9577b14db5SEric W. Biederman struct proc_inode *ei; 9677b14db5SEric W. Biederman int depth, i; 9777b14db5SEric W. Biederman 9877b14db5SEric W. Biederman ei = PROC_I(dentry->d_inode); 9977b14db5SEric W. Biederman depth = ei->fd; 10077b14db5SEric W. Biederman 10177b14db5SEric W. Biederman if (depth == 0) 10277b14db5SEric W. Biederman return table; 10377b14db5SEric W. Biederman 10477b14db5SEric W. Biederman for (i = 1; table && (i <= depth); i++) { 10577b14db5SEric W. Biederman ancestor = proc_sys_ancestor(dentry, i); 10677b14db5SEric W. Biederman table = proc_sys_lookup_table_one(table, &ancestor->d_name); 10777b14db5SEric W. Biederman if (table) 10877b14db5SEric W. Biederman table = table->child; 10977b14db5SEric W. Biederman } 11077b14db5SEric W. Biederman return table; 11177b14db5SEric W. Biederman 11277b14db5SEric W. Biederman } 11377b14db5SEric W. Biederman static struct ctl_table *proc_sys_lookup_entry(struct dentry *dparent, 11477b14db5SEric W. Biederman struct qstr *name, 11577b14db5SEric W. Biederman struct ctl_table *table) 11677b14db5SEric W. Biederman { 11777b14db5SEric W. Biederman table = proc_sys_lookup_table(dparent, table); 11877b14db5SEric W. Biederman if (table) 11977b14db5SEric W. Biederman table = proc_sys_lookup_table_one(table, name); 12077b14db5SEric W. Biederman return table; 12177b14db5SEric W. Biederman } 12277b14db5SEric W. Biederman 12377b14db5SEric W. Biederman static struct ctl_table *do_proc_sys_lookup(struct dentry *parent, 12477b14db5SEric W. Biederman struct qstr *name, 12577b14db5SEric W. Biederman struct ctl_table_header **ptr) 12677b14db5SEric W. Biederman { 12777b14db5SEric W. Biederman struct ctl_table_header *head; 12877b14db5SEric W. Biederman struct ctl_table *table = NULL; 12977b14db5SEric W. Biederman 13077b14db5SEric W. Biederman for (head = sysctl_head_next(NULL); head; 13177b14db5SEric W. Biederman head = sysctl_head_next(head)) { 13277b14db5SEric W. Biederman table = proc_sys_lookup_entry(parent, name, head->ctl_table); 13377b14db5SEric W. Biederman if (table) 13477b14db5SEric W. Biederman break; 13577b14db5SEric W. Biederman } 13677b14db5SEric W. Biederman *ptr = head; 13777b14db5SEric W. Biederman return table; 13877b14db5SEric W. Biederman } 13977b14db5SEric W. Biederman 14077b14db5SEric W. Biederman static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry, 14177b14db5SEric W. Biederman struct nameidata *nd) 14277b14db5SEric W. Biederman { 14377b14db5SEric W. Biederman struct ctl_table_header *head; 14477b14db5SEric W. Biederman struct inode *inode; 14577b14db5SEric W. Biederman struct dentry *err; 14677b14db5SEric W. Biederman struct ctl_table *table; 14777b14db5SEric W. Biederman 14877b14db5SEric W. Biederman err = ERR_PTR(-ENOENT); 14977b14db5SEric W. Biederman table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); 15077b14db5SEric W. Biederman if (!table) 15177b14db5SEric W. Biederman goto out; 15277b14db5SEric W. Biederman 15377b14db5SEric W. Biederman err = ERR_PTR(-ENOMEM); 15477b14db5SEric W. Biederman inode = proc_sys_make_inode(dir, table); 15577b14db5SEric W. Biederman if (!inode) 15677b14db5SEric W. Biederman goto out; 15777b14db5SEric W. Biederman 15877b14db5SEric W. Biederman err = NULL; 15977b14db5SEric W. Biederman dentry->d_op = &proc_sys_dentry_operations; 16077b14db5SEric W. Biederman d_add(dentry, inode); 16177b14db5SEric W. Biederman 16277b14db5SEric W. Biederman out: 16377b14db5SEric W. Biederman sysctl_head_finish(head); 16477b14db5SEric W. Biederman return err; 16577b14db5SEric W. Biederman } 16677b14db5SEric W. Biederman 16777b14db5SEric W. Biederman static ssize_t proc_sys_read(struct file *filp, char __user *buf, 16877b14db5SEric W. Biederman size_t count, loff_t *ppos) 16977b14db5SEric W. Biederman { 17077b14db5SEric W. Biederman struct dentry *dentry = filp->f_dentry; 17177b14db5SEric W. Biederman struct ctl_table_header *head; 17277b14db5SEric W. Biederman struct ctl_table *table; 17377b14db5SEric W. Biederman ssize_t error, res; 17477b14db5SEric W. Biederman 17577b14db5SEric W. Biederman table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); 17677b14db5SEric W. Biederman /* Has the sysctl entry disappeared on us? */ 17777b14db5SEric W. Biederman error = -ENOENT; 17877b14db5SEric W. Biederman if (!table) 17977b14db5SEric W. Biederman goto out; 18077b14db5SEric W. Biederman 18177b14db5SEric W. Biederman /* Has the sysctl entry been replaced by a directory? */ 18277b14db5SEric W. Biederman error = -EISDIR; 18377b14db5SEric W. Biederman if (!table->proc_handler) 18477b14db5SEric W. Biederman goto out; 18577b14db5SEric W. Biederman 18677b14db5SEric W. Biederman /* 18777b14db5SEric W. Biederman * At this point we know that the sysctl was not unregistered 18877b14db5SEric W. Biederman * and won't be until we finish. 18977b14db5SEric W. Biederman */ 19077b14db5SEric W. Biederman error = -EPERM; 19177b14db5SEric W. Biederman if (sysctl_perm(table, MAY_READ)) 19277b14db5SEric W. Biederman goto out; 19377b14db5SEric W. Biederman 19477b14db5SEric W. Biederman /* careful: calling conventions are nasty here */ 19577b14db5SEric W. Biederman res = count; 19677b14db5SEric W. Biederman error = table->proc_handler(table, 0, filp, buf, &res, ppos); 19777b14db5SEric W. Biederman if (!error) 19877b14db5SEric W. Biederman error = res; 19977b14db5SEric W. Biederman out: 20077b14db5SEric W. Biederman sysctl_head_finish(head); 20177b14db5SEric W. Biederman 20277b14db5SEric W. Biederman return error; 20377b14db5SEric W. Biederman } 20477b14db5SEric W. Biederman 20577b14db5SEric W. Biederman static ssize_t proc_sys_write(struct file *filp, const char __user *buf, 20677b14db5SEric W. Biederman size_t count, loff_t *ppos) 20777b14db5SEric W. Biederman { 20877b14db5SEric W. Biederman struct dentry *dentry = filp->f_dentry; 20977b14db5SEric W. Biederman struct ctl_table_header *head; 21077b14db5SEric W. Biederman struct ctl_table *table; 21177b14db5SEric W. Biederman ssize_t error, res; 21277b14db5SEric W. Biederman 21377b14db5SEric W. Biederman table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); 21477b14db5SEric W. Biederman /* Has the sysctl entry disappeared on us? */ 21577b14db5SEric W. Biederman error = -ENOENT; 21677b14db5SEric W. Biederman if (!table) 21777b14db5SEric W. Biederman goto out; 21877b14db5SEric W. Biederman 21977b14db5SEric W. Biederman /* Has the sysctl entry been replaced by a directory? */ 22077b14db5SEric W. Biederman error = -EISDIR; 22177b14db5SEric W. Biederman if (!table->proc_handler) 22277b14db5SEric W. Biederman goto out; 22377b14db5SEric W. Biederman 22477b14db5SEric W. Biederman /* 22577b14db5SEric W. Biederman * At this point we know that the sysctl was not unregistered 22677b14db5SEric W. Biederman * and won't be until we finish. 22777b14db5SEric W. Biederman */ 22877b14db5SEric W. Biederman error = -EPERM; 22977b14db5SEric W. Biederman if (sysctl_perm(table, MAY_WRITE)) 23077b14db5SEric W. Biederman goto out; 23177b14db5SEric W. Biederman 23277b14db5SEric W. Biederman /* careful: calling conventions are nasty here */ 23377b14db5SEric W. Biederman res = count; 23477b14db5SEric W. Biederman error = table->proc_handler(table, 1, filp, (char __user *)buf, 23577b14db5SEric W. Biederman &res, ppos); 23677b14db5SEric W. Biederman if (!error) 23777b14db5SEric W. Biederman error = res; 23877b14db5SEric W. Biederman out: 23977b14db5SEric W. Biederman sysctl_head_finish(head); 24077b14db5SEric W. Biederman 24177b14db5SEric W. Biederman return error; 24277b14db5SEric W. Biederman } 24377b14db5SEric W. Biederman 24477b14db5SEric W. Biederman 24577b14db5SEric W. Biederman static int proc_sys_fill_cache(struct file *filp, void *dirent, 24677b14db5SEric W. Biederman filldir_t filldir, struct ctl_table *table) 24777b14db5SEric W. Biederman { 24877b14db5SEric W. Biederman struct ctl_table_header *head; 24977b14db5SEric W. Biederman struct ctl_table *child_table = NULL; 25077b14db5SEric W. Biederman struct dentry *child, *dir = filp->f_path.dentry; 25177b14db5SEric W. Biederman struct inode *inode; 25277b14db5SEric W. Biederman struct qstr qname; 25377b14db5SEric W. Biederman ino_t ino = 0; 25477b14db5SEric W. Biederman unsigned type = DT_UNKNOWN; 25577b14db5SEric W. Biederman int ret; 25677b14db5SEric W. Biederman 25777b14db5SEric W. Biederman qname.name = table->procname; 25877b14db5SEric W. Biederman qname.len = strlen(table->procname); 25977b14db5SEric W. Biederman qname.hash = full_name_hash(qname.name, qname.len); 26077b14db5SEric W. Biederman 26177b14db5SEric W. Biederman /* Suppress duplicates. 26277b14db5SEric W. Biederman * Only fill a directory entry if it is the value that 26377b14db5SEric W. Biederman * an ordinary lookup of that name returns. Hide all 26477b14db5SEric W. Biederman * others. 26577b14db5SEric W. Biederman * 26677b14db5SEric W. Biederman * If we ever cache this translation in the dcache 26777b14db5SEric W. Biederman * I should do a dcache lookup first. But for now 26877b14db5SEric W. Biederman * it is just simpler not to. 26977b14db5SEric W. Biederman */ 27077b14db5SEric W. Biederman ret = 0; 27177b14db5SEric W. Biederman child_table = do_proc_sys_lookup(dir, &qname, &head); 27277b14db5SEric W. Biederman sysctl_head_finish(head); 27377b14db5SEric W. Biederman if (child_table != table) 27477b14db5SEric W. Biederman return 0; 27577b14db5SEric W. Biederman 27677b14db5SEric W. Biederman child = d_lookup(dir, &qname); 27777b14db5SEric W. Biederman if (!child) { 27877b14db5SEric W. Biederman struct dentry *new; 27977b14db5SEric W. Biederman new = d_alloc(dir, &qname); 28077b14db5SEric W. Biederman if (new) { 28177b14db5SEric W. Biederman inode = proc_sys_make_inode(dir->d_inode, table); 28277b14db5SEric W. Biederman if (!inode) 28377b14db5SEric W. Biederman child = ERR_PTR(-ENOMEM); 28477b14db5SEric W. Biederman else { 28577b14db5SEric W. Biederman new->d_op = &proc_sys_dentry_operations; 28677b14db5SEric W. Biederman d_add(new, inode); 28777b14db5SEric W. Biederman } 28877b14db5SEric W. Biederman if (child) 28977b14db5SEric W. Biederman dput(new); 29077b14db5SEric W. Biederman else 29177b14db5SEric W. Biederman child = new; 29277b14db5SEric W. Biederman } 29377b14db5SEric W. Biederman } 29477b14db5SEric W. Biederman if (!child || IS_ERR(child) || !child->d_inode) 29577b14db5SEric W. Biederman goto end_instantiate; 29677b14db5SEric W. Biederman inode = child->d_inode; 29777b14db5SEric W. Biederman if (inode) { 29877b14db5SEric W. Biederman ino = inode->i_ino; 29977b14db5SEric W. Biederman type = inode->i_mode >> 12; 30077b14db5SEric W. Biederman } 30177b14db5SEric W. Biederman dput(child); 30277b14db5SEric W. Biederman end_instantiate: 30377b14db5SEric W. Biederman if (!ino) 30477b14db5SEric W. Biederman ino= find_inode_number(dir, &qname); 30577b14db5SEric W. Biederman if (!ino) 30677b14db5SEric W. Biederman ino = 1; 30777b14db5SEric W. Biederman return filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type); 30877b14db5SEric W. Biederman } 30977b14db5SEric W. Biederman 31077b14db5SEric W. Biederman static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir) 31177b14db5SEric W. Biederman { 31277b14db5SEric W. Biederman struct dentry *dentry = filp->f_dentry; 31377b14db5SEric W. Biederman struct inode *inode = dentry->d_inode; 31477b14db5SEric W. Biederman struct ctl_table_header *head = NULL; 31577b14db5SEric W. Biederman struct ctl_table *table; 31677b14db5SEric W. Biederman unsigned long pos; 31777b14db5SEric W. Biederman int ret; 31877b14db5SEric W. Biederman 31977b14db5SEric W. Biederman ret = -ENOTDIR; 32077b14db5SEric W. Biederman if (!S_ISDIR(inode->i_mode)) 32177b14db5SEric W. Biederman goto out; 32277b14db5SEric W. Biederman 32377b14db5SEric W. Biederman ret = 0; 32477b14db5SEric W. Biederman /* Avoid a switch here: arm builds fail with missing __cmpdi2 */ 32577b14db5SEric W. Biederman if (filp->f_pos == 0) { 32677b14db5SEric W. Biederman if (filldir(dirent, ".", 1, filp->f_pos, 32777b14db5SEric W. Biederman inode->i_ino, DT_DIR) < 0) 32877b14db5SEric W. Biederman goto out; 32977b14db5SEric W. Biederman filp->f_pos++; 33077b14db5SEric W. Biederman } 33177b14db5SEric W. Biederman if (filp->f_pos == 1) { 33277b14db5SEric W. Biederman if (filldir(dirent, "..", 2, filp->f_pos, 33377b14db5SEric W. Biederman parent_ino(dentry), DT_DIR) < 0) 33477b14db5SEric W. Biederman goto out; 33577b14db5SEric W. Biederman filp->f_pos++; 33677b14db5SEric W. Biederman } 33777b14db5SEric W. Biederman pos = 2; 33877b14db5SEric W. Biederman 33977b14db5SEric W. Biederman /* - Find each instance of the directory 34077b14db5SEric W. Biederman * - Read all entries in each instance 34177b14db5SEric W. Biederman * - Before returning an entry to user space lookup the entry 34277b14db5SEric W. Biederman * by name and if I find a different entry don't return 34377b14db5SEric W. Biederman * this one because it means it is a buried dup. 34477b14db5SEric W. Biederman * For sysctl this should only happen for directory entries. 34577b14db5SEric W. Biederman */ 34677b14db5SEric W. Biederman for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) { 34777b14db5SEric W. Biederman table = proc_sys_lookup_table(dentry, head->ctl_table); 34877b14db5SEric W. Biederman 34977b14db5SEric W. Biederman if (!table) 35077b14db5SEric W. Biederman continue; 35177b14db5SEric W. Biederman 35277b14db5SEric W. Biederman for (; table->ctl_name || table->procname; table++, pos++) { 35377b14db5SEric W. Biederman /* Can't do anything without a proc name */ 35477b14db5SEric W. Biederman if (!table->procname) 35577b14db5SEric W. Biederman continue; 35677b14db5SEric W. Biederman 35777b14db5SEric W. Biederman if (pos < filp->f_pos) 35877b14db5SEric W. Biederman continue; 35977b14db5SEric W. Biederman 36077b14db5SEric W. Biederman if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0) 36177b14db5SEric W. Biederman goto out; 36277b14db5SEric W. Biederman filp->f_pos = pos + 1; 36377b14db5SEric W. Biederman } 36477b14db5SEric W. Biederman } 36577b14db5SEric W. Biederman ret = 1; 36677b14db5SEric W. Biederman out: 36777b14db5SEric W. Biederman sysctl_head_finish(head); 36877b14db5SEric W. Biederman return ret; 36977b14db5SEric W. Biederman } 37077b14db5SEric W. Biederman 37177b14db5SEric W. Biederman static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd) 37277b14db5SEric W. Biederman { 37377b14db5SEric W. Biederman /* 37477b14db5SEric W. Biederman * sysctl entries that are not writeable, 37577b14db5SEric W. Biederman * are _NOT_ writeable, capabilities or not. 37677b14db5SEric W. Biederman */ 37777b14db5SEric W. Biederman struct ctl_table_header *head; 37877b14db5SEric W. Biederman struct ctl_table *table; 37977b14db5SEric W. Biederman struct dentry *dentry; 38077b14db5SEric W. Biederman int mode; 38177b14db5SEric W. Biederman int depth; 38277b14db5SEric W. Biederman int error; 38377b14db5SEric W. Biederman 38477b14db5SEric W. Biederman head = NULL; 38577b14db5SEric W. Biederman depth = PROC_I(inode)->fd; 38677b14db5SEric W. Biederman 38777b14db5SEric W. Biederman /* First check the cached permissions, in case we don't have 38877b14db5SEric W. Biederman * enough information to lookup the sysctl table entry. 38977b14db5SEric W. Biederman */ 39077b14db5SEric W. Biederman error = -EACCES; 39177b14db5SEric W. Biederman mode = inode->i_mode; 39277b14db5SEric W. Biederman 39377b14db5SEric W. Biederman if (current->euid == 0) 39477b14db5SEric W. Biederman mode >>= 6; 39577b14db5SEric W. Biederman else if (in_group_p(0)) 39677b14db5SEric W. Biederman mode >>= 3; 39777b14db5SEric W. Biederman 39877b14db5SEric W. Biederman if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask) 39977b14db5SEric W. Biederman error = 0; 40077b14db5SEric W. Biederman 40177b14db5SEric W. Biederman /* If we can't get a sysctl table entry the permission 40277b14db5SEric W. Biederman * checks on the cached mode will have to be enough. 40377b14db5SEric W. Biederman */ 40477b14db5SEric W. Biederman if (!nd || !depth) 40577b14db5SEric W. Biederman goto out; 40677b14db5SEric W. Biederman 40777b14db5SEric W. Biederman dentry = nd->dentry; 40877b14db5SEric W. Biederman table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); 40977b14db5SEric W. Biederman 41077b14db5SEric W. Biederman /* If the entry does not exist deny permission */ 41177b14db5SEric W. Biederman error = -EACCES; 41277b14db5SEric W. Biederman if (!table) 41377b14db5SEric W. Biederman goto out; 41477b14db5SEric W. Biederman 41577b14db5SEric W. Biederman /* Use the permissions on the sysctl table entry */ 41677b14db5SEric W. Biederman error = sysctl_perm(table, mask); 41777b14db5SEric W. Biederman out: 41877b14db5SEric W. Biederman sysctl_head_finish(head); 41977b14db5SEric W. Biederman return error; 42077b14db5SEric W. Biederman } 42177b14db5SEric W. Biederman 42277b14db5SEric W. Biederman static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr) 42377b14db5SEric W. Biederman { 42477b14db5SEric W. Biederman struct inode *inode = dentry->d_inode; 42577b14db5SEric W. Biederman int error; 42677b14db5SEric W. Biederman 42777b14db5SEric W. Biederman if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) 42877b14db5SEric W. Biederman return -EPERM; 42977b14db5SEric W. Biederman 43077b14db5SEric W. Biederman error = inode_change_ok(inode, attr); 43177b14db5SEric W. Biederman if (!error) { 43277b14db5SEric W. Biederman error = security_inode_setattr(dentry, attr); 43377b14db5SEric W. Biederman if (!error) 43477b14db5SEric W. Biederman error = inode_setattr(inode, attr); 43577b14db5SEric W. Biederman } 43677b14db5SEric W. Biederman 43777b14db5SEric W. Biederman return error; 43877b14db5SEric W. Biederman } 43977b14db5SEric W. Biederman 44077b14db5SEric W. Biederman /* I'm lazy and don't distinguish between files and directories, 44177b14db5SEric W. Biederman * until access time. 44277b14db5SEric W. Biederman */ 44377b14db5SEric W. Biederman static const struct file_operations proc_sys_file_operations = { 44477b14db5SEric W. Biederman .read = proc_sys_read, 44577b14db5SEric W. Biederman .write = proc_sys_write, 44677b14db5SEric W. Biederman .readdir = proc_sys_readdir, 44777b14db5SEric W. Biederman }; 44877b14db5SEric W. Biederman 44977b14db5SEric W. Biederman static struct inode_operations proc_sys_inode_operations = { 45077b14db5SEric W. Biederman .lookup = proc_sys_lookup, 45177b14db5SEric W. Biederman .permission = proc_sys_permission, 45277b14db5SEric W. Biederman .setattr = proc_sys_setattr, 45377b14db5SEric W. Biederman }; 45477b14db5SEric W. Biederman 45577b14db5SEric W. Biederman static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd) 45677b14db5SEric W. Biederman { 45777b14db5SEric W. Biederman struct ctl_table_header *head; 45877b14db5SEric W. Biederman struct ctl_table *table; 45977b14db5SEric W. Biederman table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); 46077b14db5SEric W. Biederman proc_sys_refresh_inode(dentry->d_inode, table); 46177b14db5SEric W. Biederman sysctl_head_finish(head); 46277b14db5SEric W. Biederman return !!table; 46377b14db5SEric W. Biederman } 46477b14db5SEric W. Biederman 46577b14db5SEric W. Biederman static struct dentry_operations proc_sys_dentry_operations = { 46677b14db5SEric W. Biederman .d_revalidate = proc_sys_revalidate, 46777b14db5SEric W. Biederman }; 46877b14db5SEric W. Biederman 46977b14db5SEric W. Biederman static struct proc_dir_entry *proc_sys_root; 47077b14db5SEric W. Biederman 47177b14db5SEric W. Biederman int proc_sys_init(void) 47277b14db5SEric W. Biederman { 47377b14db5SEric W. Biederman proc_sys_root = proc_mkdir("sys", NULL); 47477b14db5SEric W. Biederman proc_sys_root->proc_iops = &proc_sys_inode_operations; 47577b14db5SEric W. Biederman proc_sys_root->proc_fops = &proc_sys_file_operations; 47677b14db5SEric W. Biederman proc_sys_root->nlink = 0; 47777b14db5SEric W. Biederman return 0; 47877b14db5SEric W. Biederman } 479