From 481e3bf2c164d5303c6f827fc2bcbb67508d0ff5 Mon Sep 17 00:00:00 2001
From: LiFeng <lifeng68@huawei.com>
Date: Sat, 11 Apr 2020 17:12:44 +0800
Subject: [PATCH 03/49] confile: add lxc.isulad.populate.device interface

Signed-off-by: LiFeng <lifeng68@huawei.com>
---
 src/lxc/Makefile.am    |   9 +
 src/lxc/conf.c         | 126 ++++++++++
 src/lxc/conf.h         |  25 ++
 src/lxc/confile.c      | 120 ++++++++-
 src/lxc/isulad_utils.c |  99 ++++++++
 src/lxc/isulad_utils.h |  20 ++
 src/lxc/path.c         | 655 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/lxc/path.h         |  65 +++++
 src/lxc/utils.h        |   3 +
 9 files changed, 1121 insertions(+), 1 deletion(-)
 create mode 100644 src/lxc/isulad_utils.c
 create mode 100644 src/lxc/isulad_utils.h
 create mode 100644 src/lxc/path.c
 create mode 100644 src/lxc/path.h

diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index e7fc844..21441c0 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -52,6 +52,10 @@ noinst_HEADERS = api_extensions.h \
 		 utils.h \
 		 uuid.h
 
+#if HAVE_ISULAD
+noinst_HEADERS += isulad_utils.h path.h
+#endif
+
 if IS_BIONIC
 noinst_HEADERS += ../include/fexecve.h \
 		  ../include/lxcmntent.h \
@@ -154,6 +158,11 @@ liblxc_la_SOURCES = af_unix.c af_unix.h \
 		    version.h \
 		    $(LSM_SOURCES)
 
+#if HAVE_ISULAD
+liblxc_la_SOURCES += isulad_utils.c isulad_utils.h \
+			path.c path.h
+#endif
+
 if IS_BIONIC
 liblxc_la_SOURCES += ../include/fexecve.c ../include/fexecve.h \
 		     ../include/lxcmntent.c ../include/lxcmntent.h \
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 62a6979..e9c0a37 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -2564,6 +2564,11 @@ struct lxc_conf *lxc_conf_init(void)
 	memset(&new->ns_share, 0, sizeof(char *) * LXC_NS_MAX);
 	seccomp_conf_init(new);
 
+#ifdef HAVE_ISULAD
+	/* isulad add begin */
+	lxc_list_init(&new->populate_devs);
+#endif
+
 	return new;
 }
 
@@ -3274,6 +3279,99 @@ static int lxc_setup_boot_id(void)
 	return 0;
 }
 
+#ifdef HAVE_ISULAD
+/* isulad: setup devices which will be populated in the container.*/
+static int setup_populate_devs(const struct lxc_rootfs *rootfs, struct lxc_list *devs)
+{
+	int ret = 0;
+	char *pathdirname = NULL;
+	char path[MAXPATHLEN];
+	mode_t file_mode = 0;
+	struct lxc_populate_devs *dev_elem = NULL;
+	struct lxc_list *it = NULL;
+	mode_t cur_mask;
+
+	INFO("Populating devices into container");
+	cur_mask = umask(0000);
+	lxc_list_for_each(it, devs) {
+		ret = 0;
+		dev_elem = it->elem;
+
+		ret = snprintf(path, MAXPATHLEN, "%s/%s", rootfs->path ? rootfs->mount : "", dev_elem->name);
+		if (ret < 0 || ret >= MAXPATHLEN) {
+			ret = -1;
+			goto reset_umask;
+		}
+
+		/* create any missing directories */
+		pathdirname = safe_strdup(path);
+		pathdirname = dirname(pathdirname);
+		ret = mkdir_p(pathdirname, 0755);
+		free(pathdirname);
+		if (ret < 0) {
+			WARN("Failed to create target directory");
+			ret = -1;
+			goto reset_umask;
+		}
+
+		if (!strcmp(dev_elem->type, "c")) {
+			file_mode = dev_elem->file_mode | S_IFCHR;
+		} else if (!strcmp(dev_elem->type, "b")) {
+			file_mode = dev_elem->file_mode | S_IFBLK;
+		} else {
+			ERROR("Failed to parse devices type '%s'", dev_elem->type);
+			ret = -1;
+			goto reset_umask;
+		}
+
+		DEBUG("Try to mknod '%s':'%d':'%d':'%d'\n", path,
+		      file_mode, dev_elem->maj, dev_elem->min);
+
+		ret = mknod(path, file_mode, makedev(dev_elem->maj, dev_elem->min));
+		if (ret && errno != EEXIST) {
+			SYSERROR("Failed to mknod '%s':'%d':'%d':'%d'", dev_elem->name,
+			         file_mode, dev_elem->maj, dev_elem->min);
+
+			char hostpath[MAXPATHLEN];
+			FILE *pathfile = NULL;
+
+			// Unprivileged containers cannot create devices, so
+			// try to bind mount the device from the host
+			ret = snprintf(hostpath, MAXPATHLEN, "/dev/%s", dev_elem->name);
+			if (ret < 0 || ret >= MAXPATHLEN) {
+				ret = -1;
+				goto reset_umask;
+			}
+			pathfile = lxc_fopen(path, "wb");
+			if (!pathfile) {
+				SYSERROR("Failed to create device mount target '%s'", path);
+				ret = -1;
+				goto reset_umask;
+			}
+			fclose(pathfile);
+			if (safe_mount(hostpath, path, 0, MS_BIND, NULL,
+			                rootfs->path ? rootfs->mount : NULL) != 0) {
+				SYSERROR("Failed bind mounting device %s from host into container",
+				         dev_elem->name);
+				ret = -1;
+				goto reset_umask;
+			}
+		}
+		if (chown(path, dev_elem->uid, dev_elem->gid) < 0) {
+			ERROR("Error chowning %s", path);
+			ret = -1;
+			goto reset_umask;
+		}
+	}
+
+reset_umask:
+	(void)umask(cur_mask);
+
+	INFO("Populated devices into container /dev");
+	return ret;
+}
+#endif
+
 int lxc_setup(struct lxc_handler *handler)
 {
 	int ret;
@@ -3382,6 +3480,15 @@ int lxc_setup(struct lxc_handler *handler)
 			return log_error(-1, "Failed to populate \"/dev\"");
 	}
 
+#ifdef HAVE_ISULAD
+	/* isulad: setup devices which will be populated in the container. */
+	if (!lxc_list_empty(&lxc_conf->populate_devs)) {
+		if (setup_populate_devs(&lxc_conf->rootfs, &lxc_conf->populate_devs) != 0) {
+			return log_error(-1, "Failed to setup devices in the container");
+		}
+	}
+#endif
+
 	/* Make sure any start hooks are in the container */
 	if (!verify_start_hooks(lxc_conf))
 		return log_error(-1, "Failed to verify start hooks");
@@ -3837,6 +3944,7 @@ void lxc_conf_free(struct lxc_conf *conf)
 	free(conf->shmount.path_cont);
 #ifdef HAVE_ISULAD
 	lxc_clear_init_args(conf);
+	lxc_clear_populate_devices(conf);
 #endif
 	free(conf);
 }
@@ -4661,4 +4769,22 @@ int lxc_clear_init_args(struct lxc_conf *lxc_conf)
 
 	return 0;
 }
+
+/*isulad: clear populate devices*/
+int lxc_clear_populate_devices(struct lxc_conf *c)
+{
+	struct lxc_list *it = NULL;
+	struct lxc_list *next = NULL;
+
+	lxc_list_for_each_safe(it, &c->populate_devs, next) {
+		struct lxc_populate_devs *dev_elem = it->elem;
+		lxc_list_del(it);
+		free(dev_elem->name);
+		free(dev_elem->type);
+		free(dev_elem);
+		free(it);
+	}
+	return 0;
+}
+
 #endif
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 8a198e4..452458c 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -230,6 +230,27 @@ struct device_item {
 	int global_rule;
 };
 
+#ifdef HAVE_ISULAD
+/*
+ * iSulad: Defines a structure to store the devices which will
+ * be attached in container
+ * @name       : the target device name in container
+ * @type       : the type of target device "c" or "b"
+ * @mode       : file mode for the device
+ * @maj        : major number for the device
+ * @min        : minor number for the device
+ */
+struct lxc_populate_devs {
+	char *name;
+	char *type;
+	mode_t file_mode;
+	int maj;
+	int min;
+	uid_t uid;
+	gid_t gid;
+};
+#endif
+
 struct lxc_conf {
 	/* Pointer to the name of the container. Do not free! */
 	const char *name;
@@ -403,6 +424,9 @@ struct lxc_conf {
 	/* isulad add: init args used to repalce init_cmd*/
 	char **init_argv;
 	size_t init_argc;
+
+	/* populate devices*/
+	struct lxc_list populate_devs;
 #endif
 
 };
@@ -479,5 +503,6 @@ extern int userns_exec_minimal(const struct lxc_conf *conf,
 			       int (*fn_child)(void *), void *fn_child_data);
 #ifdef HAVE_ISULAD
 int lxc_clear_init_args(struct lxc_conf *lxc_conf);
+int lxc_clear_populate_devices(struct lxc_conf *c);
 #endif
 #endif /* __LXC_CONF_H */
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index e535beb..f0772f9 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -149,6 +149,7 @@ lxc_config_define(sysctl);
 lxc_config_define(proc);
 #ifdef HAVE_ISULAD
 lxc_config_define(init_args);
+lxc_config_define(populate_device);
 #endif
 
 /*
@@ -264,7 +265,7 @@ static struct lxc_config_t config_jump_table[] = {
 	{ "lxc.proc",                      set_config_proc,                        get_config_proc,                        clr_config_proc,                      },
 #ifdef HAVE_ISULAD
 	{ "lxc.isulad.init.args",          set_config_init_args,                   get_config_init_args,                   clr_config_init_args,                 },
-
+	{ "lxc.isulad.populate.device",    set_config_populate_device,             get_config_populate_device,             clr_config_populate_device,           },
 #endif
 };
 
@@ -6155,4 +6156,121 @@ static inline int clr_config_init_args(const char *key, struct lxc_conf *c,
 {
 	return lxc_clear_init_args(c);
 }
+
+/* isulad: set config for populate device */
+static int set_config_populate_device(const char *key, const char *value,
+                                      struct lxc_conf *lxc_conf, void *data)
+{
+	int ret = 0, major = 0, minor = 0;
+	uid_t uid = (uid_t)-1;
+	gid_t gid = (gid_t)-1;
+	char name[4096] = {0}; /* MAX dev path name */
+	char type[3] = {0};
+	char *replace_value = NULL;
+	mode_t filemode = 0;
+	struct lxc_list *iter = NULL;
+	struct lxc_list *dev_list = NULL;
+	struct lxc_populate_devs *dev_elem = NULL;
+
+	if (lxc_config_value_empty(value))
+		return lxc_clear_populate_devices(lxc_conf);
+
+	/* lxc.populate.device = PATH_IN_CONTAINER:DEVICETYPE:MAJOR:MINOR:MODE:UID:GID
+	 * For e.g. lxc.populate.device = /dev/sda:b:8:0:0666:0:0
+	 */
+	ret = sscanf(value, "%4095[^:]:%2[^:]:%i:%i:%i:%u:%u", name, type, &major, &minor, &filemode, &uid, &gid);
+	if (ret != 7)
+		return -1;
+
+	/* find existing list element */
+	lxc_list_for_each(iter, &lxc_conf->populate_devs) {
+		dev_elem = iter->elem;
+
+		if (strcmp(name, dev_elem->name) != 0)
+			continue;
+
+		replace_value = safe_strdup(type);
+
+		free(dev_elem->type);
+		dev_elem->type = replace_value;
+		dev_elem->file_mode = filemode;
+		dev_elem->maj = major;
+		dev_elem->min = minor;
+		dev_elem->uid = (uid_t)uid;
+		dev_elem->gid = (gid_t)gid;
+		return 0;
+	}
+
+	/* allocate list element */
+	dev_list = malloc(sizeof(*dev_list));
+	if (dev_list == NULL)
+		goto on_error;
+
+	lxc_list_init(dev_list);
+
+	dev_elem = malloc(sizeof(*dev_elem));
+	if (dev_elem == NULL)
+		goto on_error;
+	memset(dev_elem, 0, sizeof(*dev_elem));
+
+	dev_elem->name = safe_strdup(name);
+
+	dev_elem->type = safe_strdup(type);
+
+	dev_elem->file_mode = filemode;
+	dev_elem->maj = major;
+	dev_elem->min = minor;
+	dev_elem->uid = (uid_t)uid;
+	dev_elem->gid = (gid_t)gid;
+
+	lxc_list_add_elem(dev_list, dev_elem);
+
+	lxc_list_add_tail(&lxc_conf->populate_devs, dev_list);
+
+	return 0;
+
+on_error:
+	free(dev_list);
+	if (dev_elem) {
+		free(dev_elem->name);
+		free(dev_elem->type);
+		free(dev_elem);
+	}
+	return -1;
+}
+
+/* isulad: get config populate device
+ * If you ask for 'lxc.populate.device', then all populate device
+ * entries will be printed, in 'lxc.populate.device = path_in_container:type:major:minor:mode:uid:gid' format.
+ * For e.g. lxc.populate.device = /dev/sda:b:8:0:0666:0:0
+ */
+static int get_config_populate_device(const char *key, char *retv, int inlen,
+                                      struct lxc_conf *c, void *data)
+{
+	int len;
+	struct lxc_list *it = NULL;
+	int fulllen = 0;
+
+	if (!retv)
+		inlen = 0;
+	else
+		memset(retv, 0, inlen);
+
+	lxc_list_for_each(it, &c->populate_devs) {
+		struct lxc_populate_devs *elem = it->elem;
+		strprint(retv, inlen, "lxc.populate.device = %s:%s:%d:%d:%o:%u:%u\n",
+		         elem->name, elem->type, elem->maj,
+		         elem->min, elem->file_mode, elem->uid, elem->gid);
+	}
+
+	return fulllen;
+}
+
+/* isulad: clr config populate devices*/
+static inline int clr_config_populate_device(const char *key, struct lxc_conf *c,
+                void *data)
+{
+	return lxc_clear_populate_devices(c);
+}
+
 #endif
diff --git a/src/lxc/isulad_utils.c b/src/lxc/isulad_utils.c
new file mode 100644
index 0000000..b282404
--- /dev/null
+++ b/src/lxc/isulad_utils.c
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020. Allrights reserved
+ * Description: isulad utils
+ * Author: lifeng
+ * Create: 2020-04-11
+******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "isulad_utils.h"
+#include "log.h"
+#include "path.h"
+#include "file_utils.h"
+
+lxc_log_define(isulad_utils, lxc);
+
+void *lxc_common_calloc_s(size_t size)
+{
+	if (size == 0 || size > SIZE_MAX) {
+		return NULL;
+	}
+
+	return calloc((size_t)1, size);
+}
+
+int lxc_mem_realloc(void **newptr, size_t newsize, void *oldptr, size_t oldsize)
+{
+	void *tmp = NULL;
+
+	if (newsize == 0) {
+		goto err_out;
+	}
+
+	tmp = lxc_common_calloc_s(newsize);
+	if (tmp == NULL) {
+		ERROR("Failed to malloc memory");
+		goto err_out;
+	}
+
+	if (oldptr != NULL) {
+		memcpy(tmp, oldptr, (newsize < oldsize) ? newsize : oldsize);
+
+		memset(oldptr, 0, oldsize);
+
+		free(oldptr);
+	}
+
+	*newptr = tmp;
+	return 0;
+
+err_out:
+	return -1;
+}
+
+char *safe_strdup(const char *src)
+{
+	char *dst = NULL;
+
+	if (src == NULL) {
+		return NULL;
+	}
+
+	dst = strdup(src);
+	if (dst == NULL) {
+		abort();
+	}
+
+	return dst;
+}
+
+int lxc_open(const char *filename, int flags, mode_t mode)
+{
+	char rpath[PATH_MAX] = {0x00};
+
+	if (cleanpath(filename, rpath, sizeof(rpath)) == NULL) {
+		return -1;
+	}
+	if (mode) {
+		return open(rpath, (int)((unsigned int)flags | O_CLOEXEC), mode);
+	} else {
+		return open(rpath, (int)((unsigned int)flags | O_CLOEXEC));
+	}
+}
+
+FILE *lxc_fopen(const char *filename, const char *mode)
+{
+	char rpath[PATH_MAX] = {0x00};
+
+	if (cleanpath(filename, rpath, sizeof(rpath)) == NULL) {
+		return NULL;
+	}
+
+	return fopen_cloexec(rpath, mode);
+}
diff --git a/src/lxc/isulad_utils.h b/src/lxc/isulad_utils.h
new file mode 100644
index 0000000..7a6ab00
--- /dev/null
+++ b/src/lxc/isulad_utils.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020. Allrights reserved
+ * Description: isulad utils
+ * Author: lifeng
+ * Create: 2020-04-11
+******************************************************************************/
+#ifndef __iSULAD_UTILS_H
+#define __iSULAD_UTILS_H
+
+#include <stdio.h>
+
+extern int lxc_mem_realloc(void **newptr, size_t newsize, void *oldptr, size_t oldsize);
+extern void *lxc_common_calloc_s(size_t size);
+extern char *safe_strdup(const char *src);
+
+extern int lxc_open(const char *filename, int flags, mode_t mode);
+extern FILE *lxc_fopen(const char *filename, const char *mode);
+
+#endif
diff --git a/src/lxc/path.c b/src/lxc/path.c
new file mode 100644
index 0000000..65b8aad
--- /dev/null
+++ b/src/lxc/path.c
@@ -0,0 +1,655 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020. Allrights reserved
+ * Description: isulad utils
+ * Author: lifeng
+ * Create: 2020-04-11
+******************************************************************************/
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <libgen.h>
+
+#include "path.h"
+#include "log.h"
+#include "isulad_utils.h"
+
+lxc_log_define(lxc_path_ui, lxc);
+
+#define ISSLASH(C) ((C) == '/')
+#define IS_ABSOLUTE_FILE_NAME(F) (ISSLASH ((F)[0]))
+#define IS_RELATIVE_FILE_NAME(F) (! IS_ABSOLUTE_FILE_NAME (F))
+
+bool specify_current_dir(const char *path)
+{
+	char *basec = NULL, *bname = NULL;
+	bool res = false;
+
+	basec = safe_strdup(path);
+
+	bname = basename(basec);
+	if (bname == NULL) {
+		free(basec);
+		ERROR("Out of memory");
+		return false;
+	}
+	res = !strcmp(bname, ".");
+	free(basec);
+	return res;
+}
+
+bool has_traling_path_separator(const char *path)
+{
+	return path && strlen(path) && (path[strlen(path) - 1] == '/');
+}
+
+// PreserveTrailingDotOrSeparator returns the given cleaned path
+// and appends a trailing `/.` or `/` if its corresponding  original
+// path ends with a trailing `/.` or `/`. If the cleaned
+// path already ends in a `.` path segment, then another is not added. If the
+// clean path already ends in a path separator, then another is not added.
+char *preserve_trailing_dot_or_separator(const char *cleanedpath,
+                const char *originalpath)
+{
+	char *respath = NULL;
+	size_t len;
+
+	if (strlen(cleanedpath) > (SIZE_MAX - 3)) {
+		return NULL;
+	}
+
+	len = strlen(cleanedpath) + 3;
+	respath = malloc(len);
+	if (respath == NULL) {
+		ERROR("Out of memory");
+		return NULL;
+	}
+	memset(respath, 0x00, len);
+	strcat(respath, cleanedpath);
+
+	if (!specify_current_dir(cleanedpath) && specify_current_dir(originalpath)) {
+		if (!has_traling_path_separator(respath))
+			strcat(respath, "/");
+		strcat(respath, ".");
+	}
+
+	if (!has_traling_path_separator(respath) &&
+	                has_traling_path_separator(originalpath))
+		strcat(respath, "/");
+
+	return respath;
+}
+
+
+// Split splits path immediately following the final Separator,
+// separating it into a directory and file name component.
+// If there is no Separator in path, Split returns an empty dir
+// and file set to path.
+// The returned values have the property that path = dir+file.
+bool filepath_split(const char *path, char **dir, char **base)
+{
+	ssize_t i;
+	size_t len;
+
+	len = strlen(path);
+	if (len >= PATH_MAX) {
+		ERROR("Invalid path");
+		return false;
+	}
+	i = len - 1;
+	while (i >= 0 && path[i] != '/')
+		i--;
+
+	*dir = malloc(i + 2);
+	if (*dir == NULL) {
+		ERROR("Out of memory");
+		return false;
+	}
+	memcpy(*dir, path, i + 1);
+	*(*dir + i + 1) = '\0';
+
+	*base = safe_strdup(path + i + 1);
+
+	return true;
+}
+
+
+static bool do_clean_path_continue(const char *endpos, const char *stpos, const char *respath, char **dst)
+{
+	if (endpos - stpos == 1 && stpos[0] == '.') {
+		return true;
+	} else if (endpos - stpos == 2 && stpos[0] == '.' && stpos[1] == '.') {
+		char *dest = *dst;
+		if (dest <= respath + 1) {
+			return true;
+		}
+		for (--dest; dest > respath && !ISSLASH(dest[-1]); --dest) {
+			*dst = dest;
+			return true;
+		}
+		*dst = dest;
+		return true;
+	}
+	return false;
+}
+
+int do_clean_path(const char *respath, const char *limit_respath,
+                  const char *stpos, char **dst)
+{
+	char *dest = *dst;
+	const char *endpos = NULL;
+
+	for (endpos = stpos; *stpos; stpos = endpos) {
+		while (ISSLASH(*stpos)) {
+			++stpos;
+		}
+
+		for (endpos = stpos; *endpos && !ISSLASH(*endpos); ++endpos) {
+		}
+
+		if (endpos - stpos == 0) {
+			break;
+		} else if (do_clean_path_continue(endpos, stpos, respath, &dest)) {
+			continue;
+		}
+
+		if (!ISSLASH(dest[-1])) {
+			*dest++ = '/';
+		}
+
+		if (dest + (endpos - stpos) >= limit_respath) {
+			ERROR("Path is too long");
+			if (dest > respath + 1) {
+				dest--;
+			}
+			*dest = '\0';
+			return -1;
+		}
+
+		memcpy(dest, stpos, (size_t)(endpos - stpos));
+		dest += endpos - stpos;
+		*dest = '\0';
+	}
+	*dst = dest;
+	return 0;
+}
+
+char *cleanpath(const char *path, char *realpath, size_t realpath_len)
+{
+	char *respath = NULL;
+	char *dest = NULL;
+	const char *stpos = NULL;
+	const char *limit_respath = NULL;
+
+	if (path == NULL || path[0] == '\0' || \
+	                realpath == NULL || (realpath_len < PATH_MAX)) {
+		return NULL;
+	}
+
+	respath = realpath;
+
+	memset(respath, 0, realpath_len);
+	limit_respath = respath + PATH_MAX;
+
+	if (!IS_ABSOLUTE_FILE_NAME(path)) {
+		if (!getcwd(respath, PATH_MAX)) {
+			ERROR("Failed to getcwd");
+			respath[0] = '\0';
+			goto error;
+		}
+		dest = strchr(respath, '\0');
+		if (dest == NULL) {
+			ERROR("Failed to get the end of respath");
+			goto error;
+		}
+		if (strlen(path) > (PATH_MAX - strlen(respath) - 1)) {
+			ERROR("Path is too long");
+			goto error;
+		}
+		strcat(respath, path);
+		stpos = path;
+	} else {
+		dest = respath;
+		*dest++ = '/';
+		stpos = path;
+	}
+
+	if (do_clean_path(respath, limit_respath, stpos, &dest)) {
+		goto error;
+	}
+
+	if (dest > respath + 1 && ISSLASH(dest[-1])) {
+		--dest;
+	}
+	*dest = '\0';
+
+	return respath;
+
+error:
+	return NULL;
+}
+
+static int do_path_realloc(const char *start, const char *end,
+                           char **rpath, char **dest, const char **rpath_limit)
+{
+	long long dest_offset = *dest - *rpath;
+	char *new_rpath = NULL;
+	size_t new_size;
+	int nret = 0;
+	size_t gap = 0;
+
+	if (*dest + (end - start) < *rpath_limit) {
+		return 0;
+	}
+
+	gap = (size_t)(end - start) + 1;
+	new_size = (size_t)(*rpath_limit - *rpath);
+	if (new_size > SIZE_MAX - gap) {
+		ERROR("Out of range!");
+		return -1;
+	}
+
+	if (gap > PATH_MAX) {
+		new_size += gap;
+	} else {
+		new_size += PATH_MAX;
+	}
+	nret = lxc_mem_realloc((void **)&new_rpath, new_size, *rpath, PATH_MAX);
+	if (nret) {
+		ERROR("Failed to realloc memory for files limit variables");
+		return -1;
+	}
+	*rpath = new_rpath;
+	*rpath_limit = *rpath + new_size;
+
+	*dest = *rpath + dest_offset;
+
+	return 0;
+}
+
+static int do_get_symlinks_copy_buf(const char *buf, const char *prefix, size_t prefix_len,
+                                    char **rpath, char **dest)
+{
+	if (IS_ABSOLUTE_FILE_NAME(buf)) {
+		if (prefix_len) {
+			memcpy(*rpath, prefix, prefix_len);
+		}
+		*dest = *rpath + prefix_len;
+		*(*dest)++ = '/';
+	} else {
+		if (*dest > *rpath + prefix_len + 1) {
+			for (--(*dest); *dest > *rpath && !ISSLASH((*dest)[-1]); --(*dest)) {
+				continue;
+			}
+		}
+	}
+	return 0;
+}
+
+static int do_get_symlinks(const char **fullpath, const char *prefix, size_t prefix_len,
+                           char **rpath, char **dest, const char **end,
+                           int *num_links, char **extra_buf)
+{
+	char *buf = NULL;
+	size_t len;
+	ssize_t n;
+	int ret = -1;
+
+	if (++(*num_links) > MAXSYMLINKS) {
+		ERROR("Too many links in '%s'", *fullpath);
+		goto out;
+	}
+
+	buf = lxc_common_calloc_s(PATH_MAX);
+	if (buf == NULL) {
+		ERROR("Out of memory");
+		goto out;
+	}
+
+	n = readlink(*rpath, buf, PATH_MAX - 1);
+	if (n < 0) {
+		goto out;
+	}
+	buf[n] = '\0';
+
+	if (*extra_buf == NULL) {
+		*extra_buf = lxc_common_calloc_s(PATH_MAX);
+		if (*extra_buf == NULL) {
+			ERROR("Out of memory");
+			goto out;
+		}
+	}
+
+	len = strlen(*end);
+	if (len >= PATH_MAX - n) {
+		ERROR("Path is too long");
+		goto out;
+	}
+
+	memmove(&(*extra_buf)[n], *end, len + 1);
+	memcpy(*extra_buf, buf, (size_t)n);
+
+	*fullpath = *end = *extra_buf;
+
+	if (do_get_symlinks_copy_buf(buf, prefix, prefix_len, rpath, dest) != 0) {
+		goto out;
+	}
+
+	ret = 0;
+out:
+	free(buf);
+	return ret;
+}
+
+static bool do_eval_symlinks_in_scope_is_symlink(const char *path)
+{
+	struct stat st;
+
+	if (lstat(path, &st) < 0) {
+		return true;
+	}
+
+	if (!S_ISLNK(st.st_mode)) {
+		return true;
+	}
+	return false;
+}
+
+static void do_eval_symlinks_skip_slash(const char **start, const char **end)
+{
+	while (ISSLASH(**start)) {
+		++(*start);
+	}
+
+	for (*end = *start; **end && !ISSLASH(**end); ++(*end)) {
+	}
+}
+
+static inline void skip_dest_traling_slash(char **dest, char **rpath, size_t prefix_len)
+{
+	if (*dest > *rpath + prefix_len + 1) {
+		for (--(*dest); *dest > *rpath && !ISSLASH((*dest)[-1]); --(*dest)) {
+			continue;
+		}
+	}
+}
+
+static inline bool is_current_char(const char c)
+{
+	return c == '.';
+}
+
+static inline bool is_specify_current(const char *end, const char *start)
+{
+	return (end - start == 1) && is_current_char(start[0]);
+}
+
+static inline bool is_specify_parent(const char *end, const char *start)
+{
+	return (end - start == 2) && is_current_char(start[0]) && is_current_char(start[1]);
+}
+
+static int do_eval_symlinks_in_scope(const char *fullpath, const char *prefix,
+                                     size_t prefix_len,
+                                     char **rpath, char **dest, const char *rpath_limit)
+{
+	const char *start = NULL;
+	const char *end = NULL;
+	char *extra_buf = NULL;
+	int nret = 0;
+	int num_links = 0;
+
+	start = fullpath + prefix_len;
+	for (end = start; *start; start = end) {
+		do_eval_symlinks_skip_slash(&start, &end);
+		if (end - start == 0) {
+			break;
+		} else if (is_specify_current(end, start)) {
+			;
+		} else if (is_specify_parent(end, start)) {
+			skip_dest_traling_slash(dest, rpath, prefix_len);
+		} else {
+			if (!ISSLASH((*dest)[-1])) {
+				*(*dest)++ = '/';
+			}
+
+			nret = do_path_realloc(start, end, rpath, dest, &rpath_limit);
+			if (nret != 0) {
+				nret = -1;
+				goto out;
+			}
+
+			memcpy(*dest, start, (size_t)(end - start));
+			*dest += end - start;
+			**dest = '\0';
+
+			if (do_eval_symlinks_in_scope_is_symlink(*rpath)) {
+				continue;
+			}
+
+			nret = do_get_symlinks(&fullpath, prefix, prefix_len, rpath, dest, &end, &num_links, &extra_buf);
+			if (nret != 0) {
+				nret = -1;
+				goto out;
+			}
+		}
+	}
+out:
+	free(extra_buf);
+	return nret;
+}
+static char *eval_symlinks_in_scope(const char *fullpath, const char *rootpath)
+{
+	char resroot[PATH_MAX] = {0};
+	char *root = NULL;
+	char *rpath = NULL;
+	char *dest = NULL;
+	char *prefix = NULL;
+	const char *rpath_limit = NULL;
+	size_t prefix_len;
+
+	if (fullpath == NULL || rootpath == NULL) {
+		return NULL;
+	}
+
+	root = cleanpath(rootpath, resroot, sizeof(resroot));
+	if (root == NULL) {
+		ERROR("Failed to get cleaned path");
+		return NULL;
+	}
+
+	if (!strcmp(fullpath, root)) {
+		return safe_strdup(fullpath);
+	}
+
+	if (strstr(fullpath, root) == NULL) {
+		ERROR("Path '%s' is not in '%s'", fullpath, root);
+		return NULL;
+	}
+
+	rpath = lxc_common_calloc_s(PATH_MAX);
+	if (rpath == NULL) {
+		ERROR("Out of memory");
+		goto out;
+	}
+	rpath_limit = rpath + PATH_MAX;
+
+	prefix = root;
+	prefix_len = (size_t)strlen(prefix);
+	if (!strcmp(prefix, "/")) {
+		prefix_len = 0;
+	}
+
+	dest = rpath;
+	if (prefix_len) {
+		memcpy(rpath, prefix, prefix_len);
+		dest += prefix_len;
+	}
+	*dest++ = '/';
+
+	if (do_eval_symlinks_in_scope(fullpath, prefix, prefix_len, &rpath, &dest,
+	                              rpath_limit)) {
+		goto out;
+	}
+
+	if (dest > rpath + prefix_len + 1 && ISSLASH(dest[-1])) {
+		--dest;
+	}
+	*dest = '\0';
+	return rpath;
+
+out:
+	free(rpath);
+	return NULL;
+}
+
+// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an
+// absolute path. This function handles paths in a platform-agnostic manner.
+char *follow_symlink_in_scope(const char *fullpath, const char *rootpath)
+{
+	char resfull[PATH_MAX] = {0}, *full = NULL;
+	char resroot[PATH_MAX] = {0}, *root = NULL;
+
+	full = cleanpath(fullpath, resfull, PATH_MAX);
+	if (!full) {
+		ERROR("Failed to get cleaned path");
+		return NULL;
+	}
+
+	root = cleanpath(rootpath, resroot, PATH_MAX);
+	if (!root) {
+		ERROR("Failed to get cleaned path");
+		return NULL;
+	}
+
+	return eval_symlinks_in_scope(full, root);
+}
+
+// GetResourcePath evaluates `path` in the scope of the container's rootpath, with proper path
+// sanitisation. Symlinks are all scoped to the rootpath of the container, as
+// though the container's rootpath was `/`.
+//
+// The BaseFS of a container is the host-facing path which is bind-mounted as
+// `/` inside the container. This method is essentially used to access a
+// particular path inside the container as though you were a process in that
+// container.
+int get_resource_path(const char *rootpath, const char *path,
+                      char **scopepath)
+{
+	char resolved[PATH_MAX] = {0}, *cleanedpath = NULL;
+	char *fullpath = NULL;
+	size_t len;
+
+	if (!rootpath || !path || !scopepath)
+		return -1;
+
+	*scopepath = NULL;
+
+	cleanedpath = cleanpath(path, resolved, PATH_MAX);
+	if (!cleanedpath) {
+		ERROR("Failed to get cleaned path");
+		return -1;
+	}
+
+	len = strlen(rootpath) + strlen(cleanedpath) + 1;
+	fullpath = malloc(len);
+	if (!fullpath) {
+		ERROR("Out of memory");
+		return -1;
+	}
+	snprintf(fullpath, len, "%s%s", rootpath, cleanedpath);
+
+	*scopepath = follow_symlink_in_scope(fullpath, rootpath);
+
+	free(fullpath);
+	return 0;
+}
+
+// Rel returns a relative path that is lexically equivalent to targpath when
+// joined to basepath with an intervening separator. That is,
+// Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself.
+// On success, the returned path will always be relative to basepath,
+// even if basepath and targpath share no elements.
+// An error is returned if targpath can't be made relative to basepath or if
+// knowing the current working directory would be necessary to compute it.
+// Rel calls Clean on the result.
+char *path_relative(const char *basepath, const char *targpath)
+{
+	char resbase[PATH_MAX] = {0}, *base = NULL;
+	char restarg[PATH_MAX] = {0}, *targ = NULL;
+	size_t bl = 0, tl = 0, b0 = 0, bi = 0, t0 = 0, ti = 0;
+
+	base = cleanpath(basepath, resbase, PATH_MAX);
+	if (!base) {
+		ERROR("Failed to get cleaned path");
+		return NULL;
+	}
+
+	targ = cleanpath(targpath, restarg, PATH_MAX);
+	if (!targ) {
+		ERROR("Failed to get cleaned path");
+		return NULL;
+	}
+
+	if (strcmp(base, targ) == 0)
+		return safe_strdup(".");
+
+	bl = strlen(base);
+	tl = strlen(targ);
+	while(true) {
+		while(bi < bl && !ISSLASH(base[bi]))
+			bi++;
+		while(ti < tl && !ISSLASH(targ[ti]))
+			ti++;
+		//not the same string
+		if (((bi - b0) != (ti - t0)) || strncmp(base + b0, targ + t0, bi - b0))
+			break;
+		if (bi < bl)
+			bi++;
+		if (ti < tl)
+			ti++;
+		b0 = bi;
+		t0 = ti;
+	}
+
+	if (b0 != bl) {
+		// Base elements left. Must go up before going down.
+		int seps = 0, i;
+		size_t ncopyed = 0, seps_size;
+		char *buf = NULL;
+
+		for (bi = b0; bi < bl; bi++) {
+			if (ISSLASH(base[bi]))
+				seps++;
+		}
+		//strlen(..) + strlen(/..) + '\0'
+		seps_size = 2 + seps * 3 + 1;
+		if (t0 != tl)
+			seps_size += 1 + tl - t0;
+
+		buf = calloc(seps_size, 1);
+		if (!buf) {
+			ERROR("Out of memory");
+			return NULL;
+		}
+		buf[ncopyed++] = '.';
+		buf[ncopyed++] = '.';
+		for (i = 0; i < seps; i++) {
+			buf[ncopyed++] = '/';
+			buf[ncopyed++] = '.';
+			buf[ncopyed++] = '.';
+		}
+		if (t0 != tl) {
+			buf[ncopyed++] = '/';
+			memcpy(buf + ncopyed, targ + t0, tl - t0 + 1);
+		}
+		return buf;
+	}
+
+	return safe_strdup(targ + t0);
+}
diff --git a/src/lxc/path.h b/src/lxc/path.h
new file mode 100644
index 0000000..2c60fb9
--- /dev/null
+++ b/src/lxc/path.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020. Allrights reserved
+ * Description: isulad utils
+ * Author: lifeng
+ * Create: 2020-04-11
+******************************************************************************/
+#ifndef __ISULAD_PATH_H_
+#define __ISULAD_PATH_H_
+
+#include <stdbool.h>
+
+bool specify_current_dir(const char *path);
+
+bool has_traling_path_separator(const char *path);
+
+// PreserveTrailingDotOrSeparator returns the given cleaned path
+// and appends a trailing `/.` or `/` if its corresponding  original
+// path ends with a trailing `/.` or `/`. If the cleaned
+// path already ends in a `.` path segment, then another is not added. If the
+// clean path already ends in a path separator, then another is not added.
+char *preserve_trailing_dot_or_separator(const char *cleanedpath,
+                const char *originalpath);
+
+
+// Split splits path immediately following the final Separator,
+// separating it into a directory and file name component.
+// If there is no Separator in path, Split returns an empty dir
+// and file set to path.
+// The returned values have the property that path = dir+file.
+bool filepath_split(const char *path, char **dir, char **base);
+
+/*
+ * cleanpath is similar to realpath of glibc, but not expands symbolic links,
+ * and not check the existence of components of the path.
+ */
+char *cleanpath(const char *path, char *realpath, size_t realpath_len);
+
+
+// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an
+// absolute path. This function handles paths in a platform-agnostic manner.
+char *follow_symlink_in_scope(const char *fullpath, const char *rootpath);
+
+// GetResourcePath evaluates `path` in the scope of the container's rootpath, with proper path
+// sanitisation. Symlinks are all scoped to the rootpath of the container, as
+// though the container's rootpath was `/`.
+//
+// The BaseFS of a container is the host-facing path which is bind-mounted as
+// `/` inside the container. This method is essentially used to access a
+// particular path inside the container as though you were a process in that
+// container.
+int get_resource_path(const char *rootpath, const char *path,
+                      char **scopepath);
+
+// Rel returns a relative path that is lexically equivalent to targpath when
+// joined to basepath with an intervening separator. That is,
+// Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself.
+// On success, the returned path will always be relative to basepath,
+// even if basepath and targpath share no elements.
+// An error is returned if targpath can't be made relative to basepath or if
+// knowing the current working directory would be necessary to compute it.
+// Rel calls Clean on the result.
+char *path_relative(const char *basepath, const char *targpath);
+
+#endif
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index 339217c..7b36133 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -27,6 +27,9 @@
 #include "memory_utils.h"
 #include "raw_syscalls.h"
 #include "string_utils.h"
+#ifdef HAVE_ISULAD
+#include "isulad_utils.h"
+#endif
 
 /* returns 1 on success, 0 if there were any failures */
 extern int lxc_rmdir_onedev(const char *path, const char *exclude);
-- 
1.8.3.1

