[lxc-devel] [PATCH v2 7/7] added the unmount-namespace hook

Wolfgang Bumiller w.bumiller at proxmox.com
Mon Oct 5 08:22:57 UTC 2015


I pass /proc/$monitor/fd/$number. I'm not sure when /proc/*/fd/ was
introduced, but it seems to be older than 2.6.12 which is where the
kernel's git history starts ;-)

The /proc/self/ns/* namespaces are opened via preserve_namespaces by
the monitor which was already in the code (I just added the pid
parameter), so as far as compatibility is concerned I don't see any
problem here, though I aldo don't work with such ancient systems,
so... ;-)

> On October 2, 2015 at 3:52 PM Serge Hallyn <serge.hallyn at ubuntu.com> wrote:
> 
> 
> Do kernels prior to 2.6.39 have /proc/self/ns/fd?
> 
> Quoting Wolfgang Bumiller (w.bumiller at proxmox.com):
> > As mentioned in the <LXC security issue> thread, I use O_PATH
> > in this patch which only exists since Linux 2.6.39, but it should be
> > optional and can be removed (it's just an optimization).
> > Will wait for the security thread to be resolved to mirror the changes
> > here.
> > 
> > > On September 30, 2015 at 12:18 PM Wolfgang Bumiller
> > > <w.bumiller at proxmox.com>
> > > wrote:
> > > 
> > > 
> > > Signed-off-by: Wolfgang Bumiller <w.bumiller at proxmox.com>
> > > ---
> > >  hooks/Makefile.am         |   6 ++
> > >  hooks/unmount-namespace.c | 180
> > > ++++++++++++++++++++++++++++++++++++++++++++++
> > >  2 files changed, 186 insertions(+)
> > >  create mode 100644 hooks/unmount-namespace.c
> > > 
> > > diff --git a/hooks/Makefile.am b/hooks/Makefile.am
> > > index be55601..ef82083 100644
> > > --- a/hooks/Makefile.am
> > > +++ b/hooks/Makefile.am
> > > @@ -6,4 +6,10 @@ hooks_SCRIPTS = \
> > >  	ubuntu-cloud-prep \
> > >  	squid-deb-proxy-client
> > >  
> > > +hooks_PROGRAMS = \
> > > +	unmount-namespace
> > > +
> > > +unmount_namespace_SOURCES = \
> > > +	unmount-namespace.c
> > > +
> > >  EXTRA_DIST=$(hooks_SCRIPTS)
> > > diff --git a/hooks/unmount-namespace.c b/hooks/unmount-namespace.c
> > > new file mode 100644
> > > index 0000000..aa64985
> > > --- /dev/null
> > > +++ b/hooks/unmount-namespace.c
> > > @@ -0,0 +1,180 @@
> > > +/*
> > > + * Copyright © 2015 Wolfgang Bumiller <w.bumiller at proxmox.com>.
> > > + * Copyright © 2015 Proxmox Server Solutions GmbH
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify
> > > + * it under the terms of the GNU General Public License version 2, as
> > > + * published by the Free Software Foundation.
> > > + *
> > > + * This program is distributed in the hope that it will be useful,
> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > + * GNU General Public License for more details.
> > > + *
> > > + * You should have received a copy of the GNU General Public License
> > > along
> > > + * with this program; if not, write to the Free Software Foundation,
> > > Inc.,
> > > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> > > + */
> > > +
> > > +#define _GNU_SOURCE    /* setns */
> > > +#include <stdio.h>     /* fdopen, getmntent, endmntent */
> > > +#include <stdlib.h>    /* malloc, qsort */
> > > +#include <unistd.h>    /* close */
> > > +#include <string.h>    /* strcmp, strncmp, strdup, strerror */
> > > +#include <sched.h>     /* setns */
> > > +#include <sys/mount.h> /* umount2 */
> > > +#include <sys/types.h> /* openat, open */
> > > +#include <sys/stat.h>  /* openat, open */
> > > +#include <fcntl.h>     /* openat, open */
> > > +#include <mntent.h>    /* getmntent, endmntent */
> > > +#include <errno.h>     /* errno */
> > > +
> > > +struct mount {
> > > +	char *src; /* currently not used */
> > > +	char *dst;
> > > +	char *fs; /* currently not used */
> > > +};
> > > +
> > > +static void mount_free(struct mount *mnt) {
> > > +	free(mnt->src);
> > > +	free(mnt->dst);
> > > +	free(mnt->fs);
> > > +}
> > > +
> > > +static int mount_cmp_dst(const void *a_, const void *b_) {
> > > +	struct mount *a = (struct mount*)a_;
> > > +	struct mount *b = (struct mount*)b_;
> > > +	return strcmp(b->dst, a->dst); /* swapped order */
> > > +}
> > > +
> > > +/* Unmounting /dev/pts fails, and  so /dev also fails, but /dev is not
> > > what
> > > + * we're interested in.
> > > + */
> > > +static int mount_should_error(const struct mount *mnt) {
> > > +	const char *dst = mnt->dst;
> > > +	return !(strncmp(dst, "/dev", 4) == 0 && (dst[4] == 0 || dst[4] ==
> > > '/'));
> > > +}
> > > +
> > > +/* Read mounts from 'self/mounts' relative to a directory filedescriptor.
> > > + * Before entering the container we open a handle to /proc on the host as
> > > we
> > > + * need to access /proc/self/mounts and the container's /proc doesn't
> > > contain
> > > + * our /self. We then use openat(2) to avoid having to mount a temporary
> > > /proc.
> > > + */
> > > +static int read_mounts(int procfd, struct mount **mp, size_t *countp) {
> > > +	int fd;
> > > +	struct mntent *ent;
> > > +	FILE *mf;
> > > +	size_t capacity = 32;
> > > +	size_t count = 0;
> > > +	struct mount *mounts = (struct mount*)malloc(capacity *
> > > sizeof(*mounts));
> > > +
> > > +	*mp = NULL;
> > > +	*countp = 0;
> > > +
> > > +	fd = openat(procfd, "self/mounts", O_RDONLY);
> > > +	if (fd < 0)
> > > +		return 0;
> > > +
> > > +	mf = fdopen(fd, "r");
> > > +	if (!mf) {
> > > +		int error = errno;
> > > +		close(fd);
> > > +		errno = error;
> > > +		return 0;
> > > +	}
> > > +	while ((ent = getmntent(mf))) {
> > > +		if (count == capacity) {
> > > +			capacity *= 2;
> > > +			mounts = (struct mount*)realloc(mounts, capacity * sizeof(*mounts));
> > > +		}
> > > +		mounts[count].src = strdup(ent->mnt_fsname);
> > > +		mounts[count].dst = strdup(ent->mnt_dir);
> > > +		mounts[count].fs  = strdup(ent->mnt_type);
> > > +		++count;
> > > +	}
> > > +	endmntent(mf);
> > > +
> > > +	*mp = mounts;
> > > +	*countp = count;
> > > +
> > > +	return 1;
> > > +}
> > > +
> > > +int main(int argc, char **argv) {
> > > +	int i, procfd, ctmntfd;
> > > +	struct mount *mounts;
> > > +	size_t zi, count = 0;
> > > +	const char *mntns = NULL;
> > > +
> > > +	if (argc < 4 || strcmp(argv[2], "lxc") != 0) {
> > > +		fprintf(stderr, "%s: usage error, expected LXC hook arguments\n",
> > > argv[0]);
> > > +		return 2;
> > > +	}
> > > +
> > > +	if (strcmp(argv[3], "stop") != 0)
> > > +		return 0;
> > > +
> > > +	for (i = 4; i != argc; ++i) {
> > > +		if (!strncmp(argv[i], "mnt:", 4))
> > > +			mntns = argv[i] + 4;
> > > +	}
> > > +
> > > +	if (!mntns) {
> > > +		fprintf(stderr, "%s: no mount namespace provided\n", argv[0]);
> > > +		return 3;
> > > +	}
> > > +
> > > +	/* Open a handle to /proc on the host as we need to access
> > > /proc/self/mounts
> > > +	 * and the container's /proc doesn't contain our /self. See
> > > read_mounts().
> > > +	 */
> > > +	procfd = open("/proc", O_RDONLY | O_DIRECTORY | O_PATH);
> > > +	if (procfd < 0) {
> > > +		fprintf(stderr, "%s: failed to open /proc: %s\n", argv[0],
> > > strerror(errno));
> > > +		return 4;
> > > +	}
> > > +
> > > +	/* Open the mount namespace and enter it. */
> > > +	ctmntfd = open(mntns, O_RDONLY);
> > > +	if (ctmntfd < 0) {
> > > +		fprintf(stderr, "%s: failed to open mount namespace: %s\n",
> > > +			argv[0], strerror(errno));
> > > +		close(procfd);
> > > +		return 5;
> > > +	}
> > > +
> > > +	if (setns(ctmntfd, CLONE_NEWNS) != 0) {
> > > +		fprintf(stderr, "%s: failed to attach to namespace: %s\n",
> > > +			argv[0], strerror(errno));
> > > +		close(ctmntfd);
> > > +		close(procfd);
> > > +		return 6;
> > > +	}
> > > +	close(ctmntfd);
> > > +
> > > +	/* Now read [[procfd]]/self/mounts */
> > > +	if (!read_mounts(procfd, &mounts, &count)) {
> > > +		fprintf(stderr, "%s: failed to read mountpoints: %s\n",
> > > +			argv[0], strerror(errno));
> > > +		close(procfd);
> > > +		return 7;
> > > +	}
> > > +	close(procfd);
> > > +
> > > +	/* Just sort to get a sane unmount-order... */
> > > +	qsort(mounts, count, sizeof(*mounts), &mount_cmp_dst);
> > > +
> > > +	for (zi = 0; zi != count; ++zi) {
> > > +		/* fprintf(stderr, "Unmount: %s\n", mounts[zi].dst); */
> > > +		if (umount2(mounts[zi].dst, 0) != 0) {
> > > +			int error = errno;
> > > +			if (mount_should_error(&mounts[zi])) {
> > > +				fprintf(stderr, "%s: failed to unmount %s: %s\n",
> > > +					argv[0], mounts[zi].dst, strerror(error));
> > > +			}
> > > +		}
> > > +		mount_free(&mounts[zi]);
> > > +	}
> > > +	free(mounts);
> > > +
> > > +	return 0;
> > > +}
> > > -- 
> > > 2.1.4
> > > 
> > > 
> > > _______________________________________________
> > > lxc-devel mailing list
> > > lxc-devel at lists.linuxcontainers.org
> > > http://lists.linuxcontainers.org/listinfo/lxc-devel
> > 
> > _______________________________________________
> > lxc-devel mailing list
> > lxc-devel at lists.linuxcontainers.org
> > http://lists.linuxcontainers.org/listinfo/lxc-devel
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel



More information about the lxc-devel mailing list