/*
* /proc/sys support
*/
#include <linux/init.h>
#include <linux/sysctl.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/printk.h>
#include <linux/security.h>
#include <linux/sched.h>
#include <linux/namei.h>
#include <linux/mm.h>
#include <linux/module.h>
#include "internal.h"
static const struct dentry_operations proc_sys_dentry_operations;
static const struct file_operations proc_sys_file_operations;
static const struct inode_operations proc_sys_inode_operations;
static const struct file_operations proc_sys_dir_file_operations;
static const struct inode_operations proc_sys_dir_operations;
/* Support for permanently empty directories */
struct ctl_table sysctl_mount_point[] = {
{ }
};
static bool is_empty_dir(struct ctl_table_header *head)
{
return head->ctl_table[0].child == sysctl_mount_point;
}
static void set_empty_dir(struct ctl_dir *dir)
{
dir->header.ctl_table[0].child = sysctl_mount_point;
}
static void clear_empty_dir(struct ctl_dir *dir)
{
dir->header.ctl_table[0].child = NULL;
}
void proc_sys_poll_notify(struct ctl_table_poll *poll)
{
if (!poll)
return;
atomic_inc(&poll->event);
wake_up_interruptible(&poll->wait);
}
static struct ctl_table root_table[] = {
{
.procname = "",
.mode = S_IFDIR|S_IRUGO|S_IXUGO,
},
{ }
};
static struct ctl_table_root sysctl_table_root = {
.default_set.dir.header = {
{{.count = 1,
.nreg = 1,
.ctl_table = root_table }},
.ctl_table_arg = root_table,
.root = &sysctl_table_root,
.set = &sysctl_table_root.default_set,
},
};
static DEFINE_SPINLOCK(sysctl_lock);
static void drop_sysctl_table(struct ctl_table_header *header);
static int sysctl_follow_link(struct ctl_table_header **phead,
struct ctl_table **pentry);
static int insert_links(struct ctl_table_header *head);
static void put_links(struct ctl_table_header *header);
static void sysctl_print_dir(struct ctl_dir *dir)
{
if (dir->header.parent)
sysctl_print_dir(dir->header.parent);
pr_cont("%s/", dir->header.ctl_table[0].procname);
}
static int namecmp(const char *name1, int len1, const char *name2, int len2)
{
int minlen;
int cmp;
minlen = len1;
if (minlen > len2)
minlen = len2;
cmp = memcmp(name1, name2, minlen);
if (cmp == 0)
cmp = len1 - len2;
return cmp;
}
/* Called under sysctl_lock */
static struct ctl_table *find_entry(struct ctl_table_header **phead,
struct ctl_dir *dir, const char *name, int namelen)
{
struct ctl_table_header *head;
struct ctl_table *entry;
struct rb_node *node = dir->root.rb_node;
while (node)
{
struct ctl_node *ctl_node;
const char *procname;
int cmp;
ctl_node = rb_entry(node, struct ctl_node, node);
head = ctl_node->header;
entry = &head->ctl_table[ctl_node - head->node];
procname = entry->procname;
cmp = namecmp(name, namelen, procname, strlen(procname));
if (cmp < 0)
node = node->rb_left;
else if (cmp > 0)
node = node->rb_right;
else {
*phead = head;
return entry;
}
}
return NULL;
}
static int insert_entry(struct ctl_table_header *head, struct ctl_table *entry)
{
struct rb_node *node = &head->node[entry - head->ctl_table].node;
struct rb_node **p = &head->parent->root.rb_node;
struct rb_node *parent = NULL;
const char *name = entry->procname;
int namelen = strlen(name);
while (*p) {
struct ctl_table_header *parent_head;
struct ctl_table *parent_entry;
struct ctl_node *parent_node;
const char *parent_name;
int cmp;
parent = *p;
parent_node = rb_entry(parent, struct ctl_node, node);
parent_head = parent_node->header;
parent_entry = &parent_head->ctl_table[parent_node - parent_head->node];
parent_name = parent_entry->procname;
cmp = namecmp(name, namelen, parent_name, strlen(parent_name));
if (cmp < 0)
p = &(*p)->rb_left;
else if (cmp > 0)
p = &(*p)->rb_right;
else {
pr_err("sysctl duplicate entry: ");
sysctl_print_dir(head->parent);
pr_cont("/%s\n", entry->procname);
|