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