[Devel] [PATCH v2 4/6] modify tar extraction to account for user namespace

Glauber Costa glommer at parallels.com
Tue Mar 12 02:58:36 PDT 2013


If we are running upstream with user namespaces, we need to create the
container filesystem not with the ownership preserved, but reflecting the
mapping we need to apply.

The long term goal is to patch the tar utility to allow us to specify an offset
that will be applied during extraction. However, even if tar is modified, the
legacy base of old tar utilities is still too big. We can employ a trick to
allow container creation right now, as well as to avoid compatibility problems:
we will resort to LD_PRELOAD to load a schim that captures calls to the chown
family of system calls and applies the offset manually.

Signed-off-by: Glauber Costa <glommer at parallels.com>
---
 scripts/vps-create.in   | 19 ++++++++++
 src/lib/Makefile.am     |  3 ++
 src/lib/chown_preload.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/lib/create.c        | 21 +++++++----
 vzctl.spec              |  2 +-
 5 files changed, 130 insertions(+), 8 deletions(-)
 create mode 100644 src/lib/chown_preload.c

diff --git a/scripts/vps-create.in b/scripts/vps-create.in
index 126f048..53f6643 100755
--- a/scripts/vps-create.in
+++ b/scripts/vps-create.in
@@ -22,11 +22,27 @@
 # Required parameters:
 #   VE_PRVT		- path to root of CT private areas
 #   PRIVATE_TEMPLATE	- path to private template used as a source for copying
+#
+# Optional parameters:
+#   UID_OFFSET		- offset to be added to all tar UIDs
+#   GID_OFFSET		- offset to be added to all tar GIDs
 
 . @SCRIPTDIR@/vps-functions
 
 vzcheckvar VE_PRVT PRIVATE_TEMPLATE
 
+chown_preload_if_needed()
+{
+	[ -z "$UID_OFFSET" -o -z "$GID_OFFSET" ] && return 
+
+	# TODO use tar with appropriate option when it becomes available 
+	#
+	# The goal is to get this merged into tar so we no longer
+	# need to do this preload. Whenever it happens, we will include
+	# a version check here as well
+	export LD_PRELOAD=libvzchown.so
+}
+
 create_prvt()
 {
 	local TMP AVAIL NEEDED HEADER OPT
@@ -75,6 +91,9 @@ create_prvt()
 	[ "$AVAIL" -ge "$NEEDED" ] ||
 		vzerror "Insufficient disk space in $VE_PRVT; available: $AVAIL, needed: $NEEDED" ${VZ_FS_NO_DISK_SPACE}
 	CAT=cat
+
+	chown_preload_if_needed
+
 	# Use pv to show nice progress bar if we can
 	pv -V >/dev/null 2>&1 && CAT=pv
 	chmod 700 "$VE_PRVT"
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 42a7ed6..44c8a69 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -70,6 +70,9 @@ libvzctl_la_LIBADD = $(XML_LIBS) $(CGROUP_LIBS) $(DL_LIBS)
 
 if HAVE_CGROUP
 libvzctl_la_SOURCES += cgroup.c hooks_ct.c
+
+lib_LTLIBRARIES += libvzchown.la
+libvzchown_la_SOURCES = chown_preload.c
 endif
 
 if HAVE_VZ_KERNEL
diff --git a/src/lib/chown_preload.c b/src/lib/chown_preload.c
new file mode 100644
index 0000000..4e2be4a
--- /dev/null
+++ b/src/lib/chown_preload.c
@@ -0,0 +1,93 @@
+/*
+ *  Copyright (C) 2013, Parallels, Inc. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Authors: Kir Kolyshkin and Glauber Costa
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <dlfcn.h>
+
+static int (*real_chown)(const char *path, uid_t owner, gid_t group) = NULL;
+static int (*real_fchown)(int fd, uid_t owner, gid_t group) = NULL;
+static int (*real_lchown)(const char *path, uid_t owner, gid_t group) = NULL;
+static int (*real_fchownat)(int dirfd, const char *pathname,
+		uid_t owner, gid_t group, int flags) = NULL;
+
+uid_t uid_offset = 0;
+gid_t gid_offset = 0;
+
+static void __init(void)
+{
+	char *uidstr, *gidstr;
+
+	uidstr = getenv("UID_OFFSET");
+	gidstr = getenv("GID_OFFSET");
+	if (!uidstr || !gidstr) {
+		fprintf(stderr, "Environment variables UID_OFFSET "
+				"and GID_OFFSET are required -- aborting\n");
+		exit(33);
+	}
+	uid_offset = strtol(uidstr, NULL, 10);
+	gid_offset = strtol(gidstr, NULL, 10);
+
+	real_chown = dlsym(RTLD_NEXT, "chown");
+	real_fchown = dlsym(RTLD_NEXT, "fchown");
+	real_lchown = dlsym(RTLD_NEXT, "lchown");
+	real_fchownat = dlsym(RTLD_NEXT, "fchownat");
+
+	if (!real_chown || !real_fchown || !real_lchown || !real_fchownat) {
+		fprintf(stderr, "dlsym failed: %s\n", dlerror());
+		exit(34);
+	}
+}
+
+int chown(const char *path, uid_t owner, gid_t group)
+{
+	if (!real_chown)
+		__init();
+
+	return real_chown(path, owner + uid_offset, group + gid_offset);
+}
+
+int fchown(int fd, uid_t owner, gid_t group)
+{
+	if (!real_fchown)
+		__init();
+
+	return real_fchown(fd, owner + uid_offset, group + gid_offset);
+}
+
+int lchown(const char *path, uid_t owner, gid_t group)
+{
+	if (!real_lchown)
+		__init();
+
+	return real_lchown(path, owner + uid_offset, group + gid_offset);
+}
+
+int fchownat(int dirfd, const char *pathname,
+		uid_t owner, gid_t group, int flags)
+{
+	if (!real_fchownat)
+		__init();
+
+	return real_fchownat(dirfd, pathname,
+			owner + uid_offset, group + gid_offset, flags);
+}
diff --git a/src/lib/create.c b/src/lib/create.c
index 0a0330f..7e28628 100644
--- a/src/lib/create.c
+++ b/src/lib/create.c
@@ -98,7 +98,7 @@ static int fs_create(envid_t veid, vps_handler *h, fs_param *fs,
 	char buf[PATH_LEN];
 	int ret;
 	char *arg[2];
-	char *env[4];
+	char *env[6];
 	int quota = 0;
 	int i;
 	char *dst;
@@ -106,8 +106,8 @@ static int fs_create(envid_t veid, vps_handler *h, fs_param *fs,
 	const char *errmsg_ext = "[.gz|.bz2|.xz]";
 	dq_param *dq = &vps_p->res.dq;
 	int layout = vps_p->opt.layout;
-	unsigned int uid_offset = vps_p->res.misc.local_uid;
-	unsigned int gid_offset = vps_p->res.misc.local_gid;
+	unsigned long uid_offset = *vps_p->res.misc.local_uid;
+	unsigned long gid_offset = *vps_p->res.misc.local_gid;
 	int ploop = (layout == VE_LAYOUT_PLOOP);
 
 	if (ploop && (!dq->diskspace || dq->diskspace[1] <= 0)) {
@@ -196,11 +196,18 @@ find:
 	arg[0] = VPS_CREATE;
 	arg[1] = NULL;
 	snprintf(buf, sizeof(buf), "PRIVATE_TEMPLATE=%s", tarball);
-	env[0] = strdup(buf);
+	i = 0;
+	env[i++] = strdup(buf);
 	snprintf(buf, sizeof(buf), "VE_PRVT=%s", dst);
-	env[1] = strdup(buf);
-	env[2] = strdup(ENV_PATH);
-	env[3] = NULL;
+	env[i++] = strdup(buf);
+	if (!is_vz_kernel(h) && h->can_join_userns) {
+		snprintf(buf, sizeof(buf), "UID_OFFSET=%lu", uid_offset);
+		env[i++] = strdup(buf);
+		snprintf(buf, sizeof(buf), "GID_OFFSET=%lu", gid_offset);
+		env[i++] = strdup(buf);
+	}
+	env[i++] = strdup(ENV_PATH);
+	env[i] = NULL;
 	logger(0, 0, "Creating container private area (%s)", tmpl->ostmpl);
 	ret = run_script(VPS_CREATE, arg, env, 0);
 	free_arg(env);
diff --git a/vzctl.spec b/vzctl.spec
index f77d326..0a68427 100644
--- a/vzctl.spec
+++ b/vzctl.spec
@@ -182,7 +182,7 @@ Requires: wget
 OpenVZ containers control utility core package
 
 %files core
-%{_libdir}/libvzctl-*.so
+%{_libdir}/libvz*.so
 %dir %{_lockdir}
 %dir %{_dumpdir}
 %dir %{_privdir}
-- 
1.7.11.7




More information about the Devel mailing list