📄 sysctl.c
字号:
* some sysctl variables are readonly even to root. */static int test_perm(int mode, int op){ if (!current->euid) mode >>= 6; else if (in_egroup_p(0)) mode >>= 3; if ((op & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0) return 0; return -EACCES;}int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op){ int error; int mode; error = security_sysctl(table, op & (MAY_READ | MAY_WRITE | MAY_EXEC)); if (error) return error; if (root->permissions) mode = root->permissions(root, current->nsproxy, table); else mode = table->mode; return test_perm(mode, op);}static void sysctl_set_parent(struct ctl_table *parent, struct ctl_table *table){ for (; table->ctl_name || table->procname; table++) { table->parent = parent; if (table->child) sysctl_set_parent(table, table->child); }}static __init int sysctl_init(void){ sysctl_set_parent(NULL, root_table);#ifdef CONFIG_SYSCTL_SYSCALL_CHECK { int err; err = sysctl_check_table(current->nsproxy, root_table); }#endif return 0;}core_initcall(sysctl_init);static struct ctl_table *is_branch_in(struct ctl_table *branch, struct ctl_table *table){ struct ctl_table *p; const char *s = branch->procname; /* branch should have named subdirectory as its first element */ if (!s || !branch->child) return NULL; /* ... and nothing else */ if (branch[1].procname || branch[1].ctl_name) return NULL; /* table should contain subdirectory with the same name */ for (p = table; p->procname || p->ctl_name; p++) { if (!p->child) continue; if (p->procname && strcmp(p->procname, s) == 0) return p; } return NULL;}/* see if attaching q to p would be an improvement */static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q){ struct ctl_table *to = p->ctl_table, *by = q->ctl_table; struct ctl_table *next; int is_better = 0; int not_in_parent = !p->attached_by; while ((next = is_branch_in(by, to)) != NULL) { if (by == q->attached_by) is_better = 1; if (to == p->attached_by) not_in_parent = 1; by = by->child; to = next->child; } if (is_better && not_in_parent) { q->attached_by = by; q->attached_to = to; q->parent = p; }}/** * __register_sysctl_paths - register a sysctl hierarchy * @root: List of sysctl headers to register on * @namespaces: Data to compute which lists of sysctl entries are visible * @path: The path to the directory the sysctl table is in. * @table: the top-level table structure * * Register a sysctl table hierarchy. @table should be a filled in ctl_table * array. A completely 0 filled entry terminates the table. * * The members of the &struct ctl_table structure are used as follows: * * ctl_name - This is the numeric sysctl value used by sysctl(2). The number * must be unique within that level of sysctl * * procname - the name of the sysctl file under /proc/sys. Set to %NULL to not * enter a sysctl file * * data - a pointer to data for use by proc_handler * * maxlen - the maximum size in bytes of the data * * mode - the file permissions for the /proc/sys file, and for sysctl(2) * * child - a pointer to the child sysctl table if this entry is a directory, or * %NULL. * * proc_handler - the text handler routine (described below) * * strategy - the strategy routine (described below) * * de - for internal use by the sysctl routines * * extra1, extra2 - extra pointers usable by the proc handler routines * * Leaf nodes in the sysctl tree will be represented by a single file * under /proc; non-leaf nodes will be represented by directories. * * sysctl(2) can automatically manage read and write requests through * the sysctl table. The data and maxlen fields of the ctl_table * struct enable minimal validation of the values being written to be * performed, and the mode field allows minimal authentication. * * More sophisticated management can be enabled by the provision of a * strategy routine with the table entry. This will be called before * any automatic read or write of the data is performed. * * The strategy routine may return * * < 0 - Error occurred (error is passed to user process) * * 0 - OK - proceed with automatic read or write. * * > 0 - OK - read or write has been done by the strategy routine, so * return immediately. * * There must be a proc_handler routine for any terminal nodes * mirrored under /proc/sys (non-terminals are handled by a built-in * directory handler). Several default handlers are available to * cover common cases - * * proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(), * proc_dointvec_userhz_jiffies(), proc_dointvec_minmax(), * proc_doulongvec_ms_jiffies_minmax(), proc_doulongvec_minmax() * * It is the handler's job to read the input buffer from user memory * and process it. The handler should return 0 on success. * * This routine returns %NULL on a failure to register, and a pointer * to the table header on success. */struct ctl_table_header *__register_sysctl_paths( struct ctl_table_root *root, struct nsproxy *namespaces, const struct ctl_path *path, struct ctl_table *table){ struct ctl_table_header *header; struct ctl_table *new, **prevp; unsigned int n, npath; struct ctl_table_set *set; /* Count the path components */ for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) ; /* * For each path component, allocate a 2-element ctl_table array. * The first array element will be filled with the sysctl entry * for this, the second will be the sentinel (ctl_name == 0). * * We allocate everything in one go so that we don't have to * worry about freeing additional memory in unregister_sysctl_table. */ header = kzalloc(sizeof(struct ctl_table_header) + (2 * npath * sizeof(struct ctl_table)), GFP_KERNEL); if (!header) return NULL; new = (struct ctl_table *) (header + 1); /* Now connect the dots */ prevp = &header->ctl_table; for (n = 0; n < npath; ++n, ++path) { /* Copy the procname */ new->procname = path->procname; new->ctl_name = path->ctl_name; new->mode = 0555; *prevp = new; prevp = &new->child; new += 2; } *prevp = table; header->ctl_table_arg = table; INIT_LIST_HEAD(&header->ctl_entry); header->used = 0; header->unregistering = NULL; header->root = root; sysctl_set_parent(NULL, header->ctl_table); header->count = 1;#ifdef CONFIG_SYSCTL_SYSCALL_CHECK if (sysctl_check_table(namespaces, header->ctl_table)) { kfree(header); return NULL; }#endif spin_lock(&sysctl_lock); header->set = lookup_header_set(root, namespaces); header->attached_by = header->ctl_table; header->attached_to = root_table; header->parent = &root_table_header; for (set = header->set; set; set = set->parent) { struct ctl_table_header *p; list_for_each_entry(p, &set->list, ctl_entry) { if (p->unregistering) continue; try_attach(p, header); } } header->parent->count++; list_add_tail(&header->ctl_entry, &header->set->list); spin_unlock(&sysctl_lock); return header;}/** * register_sysctl_table_path - register a sysctl table hierarchy * @path: The path to the directory the sysctl table is in. * @table: the top-level table structure * * Register a sysctl table hierarchy. @table should be a filled in ctl_table * array. A completely 0 filled entry terminates the table. * * See __register_sysctl_paths for more details. */struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, struct ctl_table *table){ return __register_sysctl_paths(&sysctl_table_root, current->nsproxy, path, table);}/** * register_sysctl_table - register a sysctl table hierarchy * @table: the top-level table structure * * Register a sysctl table hierarchy. @table should be a filled in ctl_table * array. A completely 0 filled entry terminates the table. * * See register_sysctl_paths for more details. */struct ctl_table_header *register_sysctl_table(struct ctl_table *table){ static const struct ctl_path null_path[] = { {} }; return register_sysctl_paths(null_path, table);}/** * unregister_sysctl_table - unregister a sysctl table hierarchy * @header: the header returned from register_sysctl_table * * Unregisters the sysctl table and all children. proc entries may not * actually be removed until they are no longer used by anyone. */void unregister_sysctl_table(struct ctl_table_header * header){ might_sleep(); if (header == NULL) return; spin_lock(&sysctl_lock); start_unregistering(header); if (!--header->parent->count) { WARN_ON(1); kfree(header->parent); } if (!--header->count) kfree(header); spin_unlock(&sysctl_lock);}int sysctl_is_seen(struct ctl_table_header *p){ struct ctl_table_set *set = p->set; int res; spin_lock(&sysctl_lock); if (p->unregistering) res = 0; else if (!set->is_seen) res = 1; else res = set->is_seen(set); spin_unlock(&sysctl_lock); return res;}void setup_sysctl_set(struct ctl_table_set *p, struct ctl_table_set *parent, int (*is_seen)(struct ctl_table_set *)){ INIT_LIST_HEAD(&p->list); p->parent = parent ? parent : &sysctl_table_root.default_set; p->is_seen = is_seen;}#else /* !CONFIG_SYSCTL */struct ctl_table_header *register_sysctl_table(struct ctl_table * table){ return NULL;}struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, struct ctl_table *table){ return NULL;}void unregister_sysctl_table(struct ctl_table_header * table){}void setup_sysctl_set(struct ctl_table_set *p, struct ctl_table_set *parent, int (*is_seen)(struct ctl_table_set *)){}void sysctl_head_put(struct ctl_table_header *head){}#endif /* CONFIG_SYSCTL *//* * /proc/sys support */#ifdef CONFIG_PROC_SYSCTLstatic int _proc_do_string(void* data, int maxlen, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos){ size_t len; char __user *p; char c; if (!data || !maxlen || !*lenp) { *lenp = 0; return 0; } if (write) { len = 0; p = buffer; while (len < *lenp) { if (get_user(c, p++)) return -EFAULT; if (c == 0 || c == '\n') break; len++; } if (len >= maxlen) len = maxlen-1; if(copy_from_user(data, buffer, len)) return -EFAULT; ((char *) data)[len] = 0; *ppos += *lenp; } else { len = strlen(data); if (len > maxlen) len = maxlen; if (*ppos > len) { *lenp = 0; return 0; } data += *ppos; len -= *ppos; if (len > *lenp) len = *lenp; if (len) if(copy_to_user(buffer, data, len)) return -EFAULT; if (len < *lenp) { if(put_user('\n', ((char __user *) buffer) + len)) return -EFAULT; len++; } *lenp = len; *ppos += len; } return 0;}/** * proc_dostring - read a string sysctl * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes a string from/to the user buffer. If the kernel * buffer provided is not large enough to hold the string, the * string is truncated. The copied string is %NULL-terminated. * If the string is being read by the user process, it is copied * and a newline '\n' is added. It is truncated if the buffer is * not large enough. * * Returns 0 on success. */int proc_dostring(struct ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos){ return _proc_do_string(table->data, table->maxlen, write, filp, buffer, lenp, ppos);}static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, int *valp, int write, void *data){ if (write) { *valp = *negp ? -*lvalp : *lvalp; } else { int val = *valp; if (val < 0) { *negp = -1; *lvalp = (unsigned long)-val; } else { *negp = 0; *lvalp = (unsigned long)val; } } return 0;}static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos, int (*conv)(int *negp, unsigned long *lvalp, int *valp, int write, void *data), void *data){#define TMPBUFLEN 21 int *i, vleft, first=1, neg, val; unsigned long lval; size_t left, len; char buf[TMPBUFLEN], *p; char __user *s = buffer; if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) { *lenp = 0; return 0; } i = (int *) tbl_data; vleft = table->maxlen / sizeof(*i); left = *lenp; if (!conv) conv = do_proc_dointvec_conv; for (; left && vleft--; i++, first=0) { if (write) { while (left) { char c; if (get_user(c, s)) return -EFAULT; if (!isspace(c)) break; left--; s++; } if (!left) break; neg = 0; len = left; if (len > sizeof(buf) - 1) len = sizeof(buf) - 1; if (copy_from_user(buf, s, len)) return -EFAULT; buf[len] = 0; p = buf; if (*p == '-' && left > 1) { neg = 1; p++; } if (*p < '0' || *p > '9') break; lval = simple_strtoul(p, &p, 0); len = p-buf; if ((len < left) && *p && !isspace(*p)) break; if (neg) val = -val; s += len; left -= len; if (conv(&neg, &lval, i, 1, data)) break; } else { p = buf; if (!first) *p++ = '\t'; if (conv(&neg, &lval, i, 0, data))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -