[lxc-devel] [PATCH] add_device_node: act in a chroot

Stéphane Graber stgraber at ubuntu.com
Fri Feb 7 19:21:22 UTC 2014


On Fri, Feb 07, 2014 at 01:00:50PM -0600, Serge Hallyn wrote:
> The goal is to avoid an absolute symlink in the guest redirecting
> us to the host's /dev.  Thanks to the libvirt team for considering
> that possibility!
> 
> We want to work on kernels which do not support setns, so we simply
> chroot into the container before doing any rm/mknod.  If /dev/vda5
> is a symlink to /XXX, or /dev is a symlink to /etc, this is now
> correctly resolved locally in the chroot.
> 
> We would have preferred to use realpath() to check that the resolved
> path is not changed, but realpath across /proc/pid/root does not
> work as expected.
> 
> Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>

Acked-by: Stéphane Graber <stgraber at ubuntu.com>

> ---
>  src/lxc/lxccontainer.c | 110 +++++++++++++++++++++++++++++--------------------
>  1 file changed, 65 insertions(+), 45 deletions(-)
> 
> diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
> index 90f089c..ac92bf1 100644
> --- a/src/lxc/lxccontainer.c
> +++ b/src/lxc/lxccontainer.c
> @@ -3084,34 +3084,80 @@ static bool lxcapi_may_control(struct lxc_container *c)
>  	return lxc_try_cmd(c->name, c->config_path) == 0;
>  }
>  
> +static bool do_add_remove_node(pid_t init_pid, const char *path, bool add,
> +		struct stat *st)
> +{
> +	char chrootpath[MAXPATHLEN];
> +	char *directory_path = NULL;
> +	pid_t pid;
> +	int ret;
> +
> +	if ((pid = fork()) < 0) {
> +		SYSERROR("failed to fork a child helper");
> +		return false;
> +	}
> +	if (pid) {
> +		if (wait_for_pid(pid) != 0) {
> +			ERROR("Failed to create note in guest");
> +			return false;
> +		}
> +		return true;
> +	}
> +
> +	/* prepare the path */
> +	ret = snprintf(chrootpath, MAXPATHLEN, "/proc/%d/root", init_pid);
> +	if (ret < 0 || ret >= MAXPATHLEN)
> +		return false;
> +
> +	if (chdir(chrootpath) < 0)
> +		exit(1);
> +	if (chroot(".") < 0)
> +		exit(1);
> +	/* remove path if it exists */
> +	if(faccessat(AT_FDCWD, path, F_OK, AT_SYMLINK_NOFOLLOW) == 0) {
> +		if (unlink(path) < 0) {
> +			ERROR("unlink failed");
> +			exit(1);
> +		}
> +	}
> +	if (!add)
> +		exit(0);
> +
> +	/* create any missing directories */
> +	directory_path = dirname(strdup(path));
> +	if (mkdir_p(directory_path, 0755) < 0 && errno != EEXIST) {
> +		ERROR("failed to create directory");
> +		exit(1);
> +	}
> +
> +	/* create the device node */
> +	if (mknod(path, st->st_mode, st->st_rdev) < 0) {
> +		ERROR("mknod failed");
> +		exit(1);
> +	}
> +
> +	exit(0);
> +}
> +
>  static bool add_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path, bool add)
>  {
>  	int ret;
>  	struct stat st;
> -	char path[MAXPATHLEN];
>  	char value[MAX_BUFFER];
> -	char *directory_path = NULL;
>  	const char *p;
>  
>  	/* make sure container is running */
>  	if (!c->is_running(c)) {
>  		ERROR("container is not running");
> -		goto out;
> +		return false;
>  	}
>  
>  	/* use src_path if dest_path is NULL otherwise use dest_path */
>  	p = dest_path ? dest_path : src_path;
>  
> -	/* prepare the path */
> -	ret = snprintf(path, MAXPATHLEN, "/proc/%d/root/%s", c->init_pid(c), p);
> -	if (ret < 0 || ret >= MAXPATHLEN)
> -		goto out;
> -	remove_trailing_slashes(path);
> -
> -	p = add ? src_path : path;
>  	/* make sure we can access p */
>  	if(access(p, F_OK) < 0 || stat(p, &st) < 0)
> -		goto out;
> +		return false;
>  
>  	/* continue if path is character device or block device */
>  	if (S_ISCHR(st.st_mode))
> @@ -3119,55 +3165,29 @@ static bool add_remove_device_node(struct lxc_container *c, const char *src_path
>  	else if (S_ISBLK(st.st_mode))
>  		ret = snprintf(value, MAX_BUFFER, "b %d:%d rwm", major(st.st_rdev), minor(st.st_rdev));
>  	else
> -		goto out;
> +		return false;
>  
>  	/* check snprintf return code */
>  	if (ret < 0 || ret >= MAX_BUFFER)
> -		goto out;
> +		return false;
>  
> -	directory_path = dirname(strdup(path));
> -	/* remove path and directory_path (if empty) */
> -	if(access(path, F_OK) == 0) {
> -		if (unlink(path) < 0) {
> -			ERROR("unlink failed");
> -			goto out;
> -		}
> -		if (rmdir(directory_path) < 0 && errno != ENOTEMPTY) {
> -			ERROR("rmdir failed");
> -			goto out;
> -		}
> -	}
> +	if (!do_add_remove_node(c->init_pid(c), p, add, &st))
> +		return false;
>  
> +	/* add or remove device to/from cgroup access list */
>  	if (add) {
> -		/* create the missing directories */
> -		if (mkdir_p(directory_path, 0755) < 0) {
> -			ERROR("failed to create directory");
> -			goto out;
> -		}
> -
> -		/* create the device node */
> -		if (mknod(path, st.st_mode, st.st_rdev) < 0) {
> -			ERROR("mknod failed");
> -			goto out;
> -		}
> -
> -		/* add device node to device list */
>  		if (!c->set_cgroup_item(c, "devices.allow", value)) {
>  			ERROR("set_cgroup_item failed while adding the device node");
> -			goto out;
> +			return false;
>  		}
>  	} else {
> -		/* remove device node from device list */
>  		if (!c->set_cgroup_item(c, "devices.deny", value)) {
>  			ERROR("set_cgroup_item failed while removing the device node");
> -			goto out;
> +			return false;
>  		}
>  	}
> +
>  	return true;
> -out:
> -	if (directory_path)
> -		free(directory_path);
> -	return false;
>  }
>  
>  static bool lxcapi_add_device_node(struct lxc_container *c, const char *src_path, const char *dest_path)
> -- 
> 1.8.3.2
> 
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel

-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20140207/6ccafa92/attachment.pgp>


More information about the lxc-devel mailing list