Commit cf9f151c authored by Eric W. Biederman's avatar Eric W. Biederman Committed by Linus Torvalds
Browse files

[PATCH] sysctl: simplify sysctl_uts_string



The binary interface to the namespace sysctls was never implemented resulting
in some really weird things if you attempted to use sys_sysctl to read your
hostname for example.

This patch series simples the code a little and implements the binary sysctl
interface.

In testing this patch series I discovered that our 32bit compatibility for the
binary sysctl interface is imperfect.  In particular KERN_SHMMAX and
KERN_SMMALL are size_t sized quantities and are returned as 8 bytes on to
32bit binaries using a x86_64 kernel.  However this has existing for a long
time so it is not a new regression with the namespace work.

Gads the whole sysctl thing needs work before it stops being easy to shoot
yourself in the foot.

Looking forward a little bit we need a better way to handle sysctls and
namespaces as our current technique will not work for the network namespace.
I think something based on the current overlapping sysctl trees will work but
the proc side needs to be redone before we can use it.

This patch:

Introduce get_uts() and put_uts() (used later) and remove most of the special
cases for when UTS namespace is compiled in.

Signed-off-by: default avatarEric W. Biederman <ebiederm@xmission.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 6b1b60f4
Loading
Loading
Loading
Loading
+26 −102
Original line number Diff line number Diff line
@@ -163,6 +163,28 @@ extern ctl_table inotify_table[];
int sysctl_legacy_va_layout;
#endif

static void *get_uts(ctl_table *table, int write)
{
	char *which = table->data;
#ifdef CONFIG_UTS_NS
	struct uts_namespace *uts_ns = current->nsproxy->uts_ns;
	which = (which - (char *)&init_uts_ns) + (char *)uts_ns;
#endif
	if (!write)
		down_read(&uts_sem);
	else
		down_write(&uts_sem);
	return which;
}

static void put_uts(ctl_table *table, int write, void *which)
{
	if (!write)
		up_read(&uts_sem);
	else
		up_write(&uts_sem);
}

/* /proc declarations: */

#ifdef CONFIG_PROC_SYSCTL
@@ -229,7 +251,6 @@ static ctl_table root_table[] = {
};

static ctl_table kern_table[] = {
#ifndef CONFIG_UTS_NS
	{
		.ctl_name	= KERN_OSTYPE,
		.procname	= "ostype",
@@ -275,54 +296,6 @@ static ctl_table kern_table[] = {
		.proc_handler	= &proc_do_uts_string,
		.strategy	= &sysctl_string,
	},
#else  /* !CONFIG_UTS_NS */
	{
		.ctl_name	= KERN_OSTYPE,
		.procname	= "ostype",
		.data		= NULL,
		/* could maybe use __NEW_UTS_LEN here? */
		.maxlen		= FIELD_SIZEOF(struct new_utsname, sysname),
		.mode		= 0444,
		.proc_handler	= &proc_do_uts_string,
		.strategy	= &sysctl_string,
	},
	{
		.ctl_name	= KERN_OSRELEASE,
		.procname	= "osrelease",
		.data		= NULL,
		.maxlen		= FIELD_SIZEOF(struct new_utsname, release),
		.mode		= 0444,
		.proc_handler	= &proc_do_uts_string,
		.strategy	= &sysctl_string,
	},
	{
		.ctl_name	= KERN_VERSION,
		.procname	= "version",
		.data		= NULL,
		.maxlen		= FIELD_SIZEOF(struct new_utsname, version),
		.mode		= 0444,
		.proc_handler	= &proc_do_uts_string,
		.strategy	= &sysctl_string,
	},
	{
		.ctl_name	= KERN_NODENAME,
		.procname	= "hostname",
		.data		= NULL,
		.maxlen		= FIELD_SIZEOF(struct new_utsname, nodename),
		.mode		= 0644,
		.proc_handler	= &proc_do_uts_string,
		.strategy	= &sysctl_string,
	},
	{
		.ctl_name	= KERN_DOMAINNAME,
		.procname	= "domainname",
		.data		= NULL,
		.maxlen		= FIELD_SIZEOF(struct new_utsname, domainname),
		.mode		= 0644,
		.proc_handler	= &proc_do_uts_string,
		.strategy	= &sysctl_string,
	},
#endif /* !CONFIG_UTS_NS */
	{
		.ctl_name	= KERN_PANIC,
		.procname	= "panic",
@@ -1754,65 +1727,16 @@ int proc_dostring(ctl_table *table, int write, struct file *filp,
 *	to observe. Should this be in kernel/sys.c ????
 */

#ifndef CONFIG_UTS_NS
static int proc_do_uts_string(ctl_table *table, int write, struct file *filp,
		  void __user *buffer, size_t *lenp, loff_t *ppos)
{
	int r;

	if (!write) {
		down_read(&uts_sem);
		r=proc_dostring(table,0,filp,buffer,lenp, ppos);
		up_read(&uts_sem);
	} else {
		down_write(&uts_sem);
		r=proc_dostring(table,1,filp,buffer,lenp, ppos);
		up_write(&uts_sem);
	}
	return r;
}
#else /* !CONFIG_UTS_NS */
static int proc_do_uts_string(ctl_table *table, int write, struct file *filp,
		  void __user *buffer, size_t *lenp, loff_t *ppos)
{
	int r;
	struct uts_namespace* uts_ns = current->nsproxy->uts_ns;
	char* which;

	switch (table->ctl_name) {
	case KERN_OSTYPE:
		which = uts_ns->name.sysname;
		break;
	case KERN_NODENAME:
		which = uts_ns->name.nodename;
		break;
	case KERN_OSRELEASE:
		which = uts_ns->name.release;
		break;
	case KERN_VERSION:
		which = uts_ns->name.version;
		break;
	case KERN_DOMAINNAME:
		which = uts_ns->name.domainname;
		break;
	default:
		r = -EINVAL;
		goto out;
	}

	if (!write) {
		down_read(&uts_sem);
		r=_proc_do_string(which,table->maxlen,0,filp,buffer,lenp, ppos);
		up_read(&uts_sem);
	} else {
		down_write(&uts_sem);
		r=_proc_do_string(which,table->maxlen,1,filp,buffer,lenp, ppos);
		up_write(&uts_sem);
	}
 out:
	void *which;
	which = get_uts(table, write);
	r = _proc_do_string(which, table->maxlen,write,filp,buffer,lenp, ppos);
	put_uts(table, write, which);
	return r;
}
#endif /* !CONFIG_UTS_NS */

static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp,
				 int *valp,