Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 076c3eed authored by Eric W. Biederman's avatar Eric W. Biederman
Browse files

sysctl: Rewrite proc_sys_lookup introducing find_entry and lookup_entry.



Replace the helpers that proc_sys_lookup uses with helpers that work
in terms of an entire sysctl directory.  This is worse for sysctl_lock
hold times but it is much better for code clarity and the code cleanups
to come.

find_in_table is no longer needed so it is removed.

find_entry a general helper to find entries in a directory is added.

lookup_entry is a simple wrapper around find_entry that takes the
sysctl_lock increases the use count if an entry is found and drops
the sysctl_lock.

Signed-off-by: default avatarEric W. Biederman <ebiederm@xmission.com>
parent a194558e
Loading
Loading
Loading
Loading
+76 −26
Original line number Original line Diff line number Diff line
@@ -49,6 +49,55 @@ static struct ctl_table_root sysctl_table_root = {


static DEFINE_SPINLOCK(sysctl_lock);
static DEFINE_SPINLOCK(sysctl_lock);


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;
}

static struct ctl_table *find_entry(struct ctl_table_header **phead,
	struct ctl_table_set *set,
	struct ctl_table_header *dir_head, struct ctl_table *dir,
	const char *name, int namelen)
{
	struct ctl_table_header *head;
	struct ctl_table *entry;

	if (dir_head->set == set) {
		for (entry = dir; entry->procname; entry++) {
			const char *procname = entry->procname;
			if (namecmp(procname, strlen(procname), name, namelen) == 0) {
				*phead = dir_head;
				return entry;
			}
		}
	}

	list_for_each_entry(head, &set->list, ctl_entry) {
		if (head->unregistering)
			continue;
		if (head->attached_to != dir)
			continue;
		for (entry = head->attached_by; entry->procname; entry++) {
			const char *procname = entry->procname;
			if (namecmp(procname, strlen(procname), name, namelen) == 0) {
				*phead = head;
				return entry;
			}
		}
	}
	return NULL;
}

static void init_header(struct ctl_table_header *head,
static void init_header(struct ctl_table_header *head,
	struct ctl_table_root *root, struct ctl_table_set *set,
	struct ctl_table_root *root, struct ctl_table_set *set,
	struct ctl_table *table)
	struct ctl_table *table)
@@ -168,6 +217,32 @@ lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces)
	return &set->list;
	return &set->list;
}
}


static struct ctl_table *lookup_entry(struct ctl_table_header **phead,
				      struct ctl_table_header *dir_head,
				      struct ctl_table *dir,
				      const char *name, int namelen)
{
	struct ctl_table_header *head;
	struct ctl_table *entry;
	struct ctl_table_root *root;
	struct ctl_table_set *set;

	spin_lock(&sysctl_lock);
	root = &sysctl_table_root;
	do {
		set = lookup_header_set(root, current->nsproxy);
		entry = find_entry(&head, set, dir_head, dir, name, namelen);
		if (entry && use_table(head))
			*phead = head;
		else
			entry = NULL;
		root = list_entry(root->root_list.next,
				  struct ctl_table_root, root_list);
	} while (!entry && root != &sysctl_table_root);
	spin_unlock(&sysctl_lock);
	return entry;
}

static struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces,
static struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces,
						struct ctl_table_header *prev)
						struct ctl_table_header *prev)
{
{
@@ -284,21 +359,6 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
	return inode;
	return inode;
}
}


static struct ctl_table *find_in_table(struct ctl_table *p, struct qstr *name)
{
	for ( ; p->procname; p++) {
		if (strlen(p->procname) != name->len)
			continue;

		if (memcmp(p->procname, name->name, name->len) != 0)
			continue;

		/* I have a match */
		return p;
	}
	return NULL;
}

static struct ctl_table_header *grab_header(struct inode *inode)
static struct ctl_table_header *grab_header(struct inode *inode)
{
{
	struct ctl_table_header *head = PROC_I(inode)->sysctl;
	struct ctl_table_header *head = PROC_I(inode)->sysctl;
@@ -328,17 +388,7 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,


	table = table ? table->child : &head->ctl_table[1];
	table = table ? table->child : &head->ctl_table[1];


	p = find_in_table(table, name);
	p = lookup_entry(&h, head, table, name->name, name->len);
	if (!p) {
		for (h = sysctl_head_next(NULL); h; h = sysctl_head_next(h)) {
			if (h->attached_to != table)
				continue;
			p = find_in_table(h->attached_by, name);
			if (p)
				break;
		}
	}

	if (!p)
	if (!p)
		goto out;
		goto out;