Signed-off-by: Glauber Costa <[email protected]>
---
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}