From 281d09f2dcd4dfd105b41d8da558d4a126ca190b Mon Sep 17 00:00:00 2001
From: WangFengTu <wangfengtu@huawei.com>
Date: Mon, 18 Jul 2022 14:14:14 +0800
Subject: [PATCH] refactor patch code of lxccontianer and so on

Signed-off-by: WangFengTu <wangfengtu@huawei.com>
---
 src/lxc/af_unix.h         |   6 +
 src/lxc/cgroups/cgroup.h  |  13 +
 src/lxc/confile.c         | 639 +++++++++++++++++++++++++++
 src/lxc/lxccontainer.c    | 898 +++++++++++++++++++++++++++++++++++++-
 src/lxc/lxclock.c         |  31 ++
 src/lxc/mainloop.c        |  16 +
 src/lxc/path.c            | 655 +++++++++++++++++++++++++++
 src/lxc/storage/dir.c     |  34 ++
 src/lxc/storage/loop.c    |  48 +-
 src/lxc/storage/storage.c |  31 +-
 10 files changed, 2367 insertions(+), 4 deletions(-)
 create mode 100644 src/lxc/path.c

diff --git a/src/lxc/af_unix.h b/src/lxc/af_unix.h
index 5a1482c..be26ff0 100644
--- a/src/lxc/af_unix.h
+++ b/src/lxc/af_unix.h
@@ -41,4 +41,10 @@ extern int lxc_unix_connect(struct sockaddr_un *addr);
 extern int lxc_unix_connect_type(struct sockaddr_un *addr, int type);
 extern int lxc_socket_set_timeout(int fd, int rcv_timeout, int snd_timeout);
 
+#ifdef HAVE_ISULAD
+extern int lxc_abstract_unix_recv_fds_timeout(int fd, int *recvfds, int num_recvfds,
+                                       void *data, size_t size, unsigned int timeout);
+extern int lxc_named_unix_open(const char *path, int type, int flags);
+extern int lxc_named_unix_connect(const char *path);
+#endif
 #endif /* __LXC_AF_UNIX_H */
diff --git a/src/lxc/cgroups/cgroup.h b/src/lxc/cgroups/cgroup.h
index c5bf794..4791112 100644
--- a/src/lxc/cgroups/cgroup.h
+++ b/src/lxc/cgroups/cgroup.h
@@ -109,6 +109,11 @@ struct cgroup_ops {
 	char *container_cgroup;
 	char *monitor_cgroup;
 
+#ifdef HAVE_ISULAD
+	int errfd;
+	bool no_controller;
+#endif
+
 	/* @hierarchies
 	 * - A NULL-terminated array of struct hierarchy, one per legacy
 	 *   hierarchy. No duplicates. First sufficient, writeable mounted
@@ -146,14 +151,22 @@ struct cgroup_ops {
 	 */
 	cgroup_layout_t cgroup_layout;
 
+#ifdef HAVE_ISULAD
+	int (*data_init)(struct cgroup_ops *ops, struct lxc_conf *conf);
+	bool (*payload_destroy)(struct cgroup_ops *ops, struct lxc_handler *handler);
+#else
 	int (*data_init)(struct cgroup_ops *ops);
 	void (*payload_destroy)(struct cgroup_ops *ops, struct lxc_handler *handler);
+#endif
 	void (*monitor_destroy)(struct cgroup_ops *ops, struct lxc_handler *handler);
 	bool (*monitor_create)(struct cgroup_ops *ops, struct lxc_handler *handler);
 	bool (*monitor_enter)(struct cgroup_ops *ops, struct lxc_handler *handler);
 	bool (*payload_create)(struct cgroup_ops *ops, struct lxc_handler *handler);
 	bool (*payload_enter)(struct cgroup_ops *ops, struct lxc_handler *handler);
 	const char *(*get_cgroup)(struct cgroup_ops *ops, const char *controller);
+#ifdef HAVE_ISULAD
+	const char *(*get_cgroup_full_path)(struct cgroup_ops *ops, const char *controller);
+#endif
 	bool (*escape)(const struct cgroup_ops *ops, struct lxc_conf *conf);
 	int (*num_hierarchies)(struct cgroup_ops *ops);
 	bool (*get_hierarchies)(struct cgroup_ops *ops, int n, char ***out);
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 4c27e7d..22d7255 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -147,6 +147,19 @@ lxc_config_define(tty_dir);
 lxc_config_define(uts_name);
 lxc_config_define(sysctl);
 lxc_config_define(proc);
+#ifdef HAVE_ISULAD
+lxc_config_define(init_args);
+lxc_config_define(init_groups);
+lxc_config_define(populate_device);
+lxc_config_define(umask);
+lxc_config_define(rootfs_masked_paths);
+lxc_config_define(rootfs_ro_paths);
+lxc_config_define(systemd);
+lxc_config_define(console_log_driver);
+lxc_config_define(console_syslog_tag);
+lxc_config_define(console_syslog_facility);
+lxc_config_define(selinux_mount_context);
+#endif
 
 /*
  * Important Note:
@@ -259,6 +272,19 @@ static struct lxc_config_t config_jump_table[] = {
 	{ "lxc.uts.name",                  set_config_uts_name,                    get_config_uts_name,                    clr_config_uts_name,                  },
 	{ "lxc.sysctl",                    set_config_sysctl,                      get_config_sysctl,                      clr_config_sysctl,                    },
 	{ "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.init.groups",        set_config_init_groups,                 get_config_init_groups,                 clr_config_init_groups,               },
+	{ "lxc.isulad.populate.device",    set_config_populate_device,             get_config_populate_device,             clr_config_populate_device,           },
+	{ "lxc.isulad.umask",              set_config_umask,                       get_config_umask,                       clr_config_umask,                     },
+	{ "lxc.isulad.rootfs.maskedpaths", set_config_rootfs_masked_paths,         get_config_rootfs_masked_paths,         clr_config_rootfs_masked_paths,       },
+	{ "lxc.isulad.rootfs.ropaths",     set_config_rootfs_ro_paths,             get_config_rootfs_ro_paths,             clr_config_rootfs_ro_paths,           },
+	{ "lxc.isulad.systemd",            set_config_systemd,                     get_config_systemd,                     clr_config_systemd,                   },
+	{ "lxc.console.logdriver",         set_config_console_log_driver,          get_config_console_log_driver,          clr_config_console_log_driver,        },
+	{ "lxc.console.syslog_tag",        set_config_console_syslog_tag,          get_config_console_syslog_tag,          clr_config_console_syslog_tag,        },
+	{ "lxc.console.syslog_facility",   set_config_console_syslog_facility,     get_config_console_syslog_facility,     clr_config_console_syslog_facility,   },
+	{ "lxc.selinux.mount_context",     set_config_selinux_mount_context,       get_config_selinux_mount_context,       clr_config_selinux_mount_context,     },
+#endif
 };
 
 static const size_t config_jump_table_size = sizeof(config_jump_table) / sizeof(struct lxc_config_t);
@@ -1348,6 +1374,10 @@ static int set_config_environment(const char *key, const char *value,
 {
 	struct lxc_list *list_item = NULL;
 
+#ifdef HAVE_ISULAD
+	char *replaced = NULL;
+#endif
+
 	if (lxc_config_value_empty(value))
 		return lxc_clear_environment(lxc_conf);
 
@@ -1368,7 +1398,16 @@ static int set_config_environment(const char *key, const char *value,
 		env_var[1] = env_val;
 		list_item->elem = lxc_string_join("=", env_var, false);
 	} else {
+#ifdef HAVE_ISULAD
+		/* isulad: recover space replaced by SPACE_MAGIC_STR */
+		replaced = lxc_string_replace(SPACE_MAGIC_STR, " ", value);
+		if(!replaced)
+			goto on_error;
+
+		list_item->elem = replaced;
+#else
 		list_item->elem = strdup(value);
+#endif
 	}
 
 	if (!list_item->elem)
@@ -2291,11 +2330,14 @@ static int set_config_console_rotate(const char *key, const char *value,
 	if (lxc_safe_uint(value, &lxc_conf->console.log_rotate) < 0)
 		return -1;
 
+#ifndef HAVE_ISULAD
+	/* isulad: support rotate muti-files */
 	if (lxc_conf->console.log_rotate > 1) {
 		ERROR("The \"lxc.console.rotate\" config key can only be set "
 		      "to 0 or 1");
 		return -1;
 	}
+#endif
 
 	return 0;
 }
@@ -2581,6 +2623,11 @@ static int set_config_rootfs_options(const char *key, const char *value,
 	int ret;
 	struct lxc_rootfs *rootfs = &lxc_conf->rootfs;
 
+#ifdef HAVE_ISULAD
+	ret = parse_mntopts(value, &mflags, &pflags, &mdata);
+	if (ret < 0)
+		return -EINVAL;
+#else
 	ret = parse_mntopts(value, &mflags, &mdata);
 	if (ret < 0)
 		return -EINVAL;
@@ -2590,6 +2637,7 @@ static int set_config_rootfs_options(const char *key, const char *value,
 		free(mdata);
 		return -EINVAL;
 	}
+#endif
 
 	ret = set_config_string_item(&opts, value);
 	if (ret < 0) {
@@ -2722,6 +2770,54 @@ struct parse_line_conf {
 	bool from_include;
 };
 
+#ifdef HAVE_ISULAD
+// escape_string_decode compress some escape characters
+static char *escape_string_decode(const char *src)
+{
+	size_t src_end = 0;
+	size_t dst_end = 0;
+	size_t len = 0;
+	char *dst = NULL;
+
+	if (src == NULL) {
+		return NULL;
+	}
+
+	len = strlen(src);
+	if (len == 0) {
+		return NULL;
+	}
+
+	dst = calloc(1, len + 1);
+	if (dst == NULL) {
+		ERROR("Out of memory");
+		return NULL;
+	}
+
+	while(src_end < len) {
+		if (src[src_end] == '\\') {
+			switch (src[++src_end])
+			{
+				case 'r': dst[dst_end] = '\r'; break;
+				case 'n': dst[dst_end] = '\n'; break;
+				case 'f': dst[dst_end] = '\f'; break;
+				case 'b': dst[dst_end] = '\b'; break;
+				case 't': dst[dst_end] = '\t'; break;
+				case '\\': dst[dst_end] = '\\'; break;
+				// default do not decode
+				default: dst[dst_end++] = '\\'; dst[dst_end] = src[src_end]; break;
+			}
+		} else {
+			dst[dst_end] = src[src_end];
+		}
+		dst_end++;
+		src_end++;
+	}
+
+	return dst;
+}
+#endif
+
 static int parse_line(char *buffer, void *data)
 {
 	char *dot, *key, *line, *linep, *value;
@@ -2730,6 +2826,9 @@ static int parse_line(char *buffer, void *data)
 	int ret = 0;
 	char *dup = buffer;
 	struct parse_line_conf *plc = data;
+#ifdef HAVE_ISULAD
+	char *value_decode = NULL;
+#endif
 
 	/* If there are newlines in the config file we should keep them. */
 	empty_line = lxc_is_line_empty(dup);
@@ -2796,10 +2895,21 @@ static int parse_line(char *buffer, void *data)
 		goto on_error;
 	}
 
+#ifdef HAVE_ISULAD
+	value_decode = escape_string_decode(value);
+	if (value_decode == NULL) {
+		ERROR("Value %s decode failed", value);
+	}
+	ret = config->set(key, value_decode ? value_decode: value, plc->conf, NULL);
+#else
 	ret = config->set(key, value, plc->conf, NULL);
+#endif
 
 on_error:
 	free(linep);
+#ifdef HAVE_ISULAD
+	free(value_decode);
+#endif
 
 	return ret;
 }
@@ -2912,6 +3022,12 @@ bool lxc_config_define_load(struct lxc_list *defines, struct lxc_container *c)
 
 	lxc_list_for_each(it, defines) {
 		struct new_config_item *new_item = it->elem;
+#ifdef HAVE_ISULAD
+		if (strcmp(new_item->key, LXC_IMAGE_OCI_KEY) == 0) {
+			c->set_oci_type(c, true);
+			continue;
+		}
+#endif
 		bret = c->set_config_item(c, new_item->key, new_item->val);
 		if (!bret)
 			break;
@@ -6098,3 +6214,526 @@ int lxc_list_net(struct lxc_conf *c, const char *key, char *retv, int inlen)
 
 	return fulllen;
 }
+
+#ifdef HAVE_ISULAD
+/* isulad: set config for init args */
+static int set_config_init_args(const char *key, const char *value,
+				 struct lxc_conf *lxc_conf, void *data)
+{
+	int ret = 0;
+	char **tmp = NULL;
+	char *new_value = NULL;
+
+	ret = set_config_string_item(&new_value, value);
+	if (ret || !new_value)
+		return ret;
+
+	tmp = (char **)realloc(lxc_conf->init_argv, (lxc_conf->init_argc + 1) * sizeof(char *));
+	if (!tmp) {
+		ERROR("Out of memory");
+		free(new_value);
+		return -1;
+	}
+
+	lxc_conf->init_argv = tmp;
+
+	lxc_conf->init_argv[lxc_conf->init_argc] = new_value;
+	lxc_conf->init_argc++;
+
+	return 0;
+}
+
+/* isulad: get config init args */
+static int get_config_init_args(const char *key, char *retv, int inlen,
+				  struct lxc_conf *c, void *data)
+{
+	int i, len, fulllen = 0;
+
+	if (!retv)
+		inlen = 0;
+	else
+		memset(retv, 0, inlen);
+
+	for (i = 0; i < c->init_argc; i++) {
+           strprint(retv, inlen, "%s", c->init_argv[i]);
+    }
+
+	return fulllen;
+}
+
+/* isulad: clr config init args*/
+static inline int clr_config_init_args(const char *key, struct lxc_conf *c,
+				   void *data)
+{
+	return lxc_clear_init_args(c);
+}
+
+/* isulad: set config for init groups */
+static int set_config_init_groups(const char *key, const char *value,
+                                  struct lxc_conf *lxc_conf, void *data)
+{
+	char *groups = NULL;
+	char *token = NULL;
+	int ret = -1;
+
+	if (lxc_config_value_empty(value))
+		return lxc_clear_init_groups(lxc_conf);
+
+	groups = strdup(value);
+	if (!groups)
+		return -1;
+
+	/* In case several capability keep is specified in a single line
+	 * split these caps in a single element for the list.
+	 */
+	lxc_iterate_parts(token, groups, " \t") {
+		gid_t *tmp = NULL;
+		if (lxc_mem_realloc((void **)&tmp, (lxc_conf->init_groups_len + 1) * sizeof(gid_t), lxc_conf->init_groups,
+		                    (lxc_conf->init_groups_len) * sizeof(gid_t)) != 0) {
+			ERROR("Out of memory");
+			goto on_error;
+		}
+		lxc_conf->init_groups = tmp;
+		tmp[lxc_conf->init_groups_len] = atoll(token);
+		lxc_conf->init_groups_len++;
+	}
+
+	ret = 0;
+
+on_error:
+	free(groups);
+
+	return ret;
+}
+
+/* isulad: get config init groups */
+static int get_config_init_groups(const char *key, char *retv, int inlen,
+                                  struct lxc_conf *c, void *data)
+{
+	int i, len, fulllen = 0;
+
+	if (!retv)
+		inlen = 0;
+	else
+		memset(retv, 0, inlen);
+
+	for (i = 0; i < c->init_groups_len; i++) {
+		strprint(retv, inlen, "%u\n", c->init_groups[i]);
+	}
+
+	return fulllen;
+}
+
+/* isulad: clr config init args*/
+static inline int clr_config_init_groups(const char *key, struct lxc_conf *c,
+                void *data)
+{
+	return lxc_clear_init_groups(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);
+}
+
+/* isulad: set config for umask */
+static int set_config_umask(const char *key, const char *value,
+			      struct lxc_conf *lxc_conf, void *data)
+{
+	if (lxc_config_value_empty(value)) {
+		ERROR("Empty umask");
+		return -1;
+	}
+
+	if (strcmp(value, "normal") == 0) {
+		lxc_conf->umask = 0022;
+		return 0;
+	} else if (strcmp(value, "secure") == 0) {
+		lxc_conf->umask = 0027;
+		return 0;
+	} else {
+		ERROR("Invalid native umask: %s", value);
+		return -1;
+	}
+}
+
+/* isulad add: get umask value*/
+static int get_config_umask(const char *key, char *retv, int inlen,
+			      struct lxc_conf *c, void *data)
+{
+	return lxc_get_conf_size_t(c, retv, inlen, c->umask);
+}
+
+/* isulad add: clear umask value */
+static inline int clr_config_umask(const char *key, struct lxc_conf *c,
+				     void *data)
+{
+	c->umask = 0027;
+	return 0;
+}
+
+/* isulad: set config for rootfs masked paths */
+static int set_config_rootfs_masked_paths(const char *key, const char *value,
+                struct lxc_conf *lxc_conf, void *data)
+{
+	struct lxc_list *list_item = NULL;
+
+	if (lxc_config_value_empty(value))
+		return lxc_clear_rootfs_masked_paths(lxc_conf);
+
+	list_item = malloc(sizeof(*list_item));
+	if (list_item == NULL)
+		goto on_error;
+
+	list_item->elem = safe_strdup(value);
+
+	lxc_list_add_tail(&lxc_conf->rootfs.maskedpaths, list_item);
+
+	return 0;
+
+on_error:
+	free(list_item);
+
+	return -1;
+}
+
+// isulad: get config rootfs masked paths
+static int get_config_rootfs_masked_paths(const char *key, char *retv, int inlen,
+                struct lxc_conf *c, void *data)
+{
+	int len, fulllen = 0;
+	struct lxc_list *it = NULL;
+
+	if (!retv)
+		inlen = 0;
+	else
+		memset(retv, 0, inlen);
+
+	lxc_list_for_each(it, &c->rootfs.maskedpaths) {
+		strprint(retv, inlen, "%s\n", (char *)it->elem);
+	}
+
+	return fulllen;
+}
+
+/* isulad: set config for rootfs ro paths */
+static int set_config_rootfs_ro_paths(const char *key, const char *value,
+                                      struct lxc_conf *lxc_conf, void *data)
+{
+	struct lxc_list *list_item = NULL;
+
+	if (lxc_config_value_empty(value))
+		return lxc_clear_rootfs_ro_paths(lxc_conf);
+
+	list_item = malloc(sizeof(*list_item));
+	if (list_item == NULL)
+		goto on_error;
+
+	list_item->elem = safe_strdup(value);
+
+	lxc_list_add_tail(&lxc_conf->rootfs.ropaths, list_item);
+
+	return 0;
+
+on_error:
+	free(list_item);
+
+	return -1;
+}
+
+// isulad: get config rootfs ro paths
+static int get_config_rootfs_ro_paths(const char *key, char *retv, int inlen,
+                                      struct lxc_conf *c, void *data)
+{
+	int len, fulllen = 0;
+	struct lxc_list *it = NULL;
+
+	if (!retv)
+		inlen = 0;
+	else
+		memset(retv, 0, inlen);
+
+	lxc_list_for_each(it, &c->rootfs.ropaths) {
+		strprint(retv, inlen, "%s\n", (char *)it->elem);
+	}
+
+	return fulllen;
+}
+
+/* isulad: clr config rootfs masked paths */
+static inline int clr_config_rootfs_masked_paths(const char *key, struct lxc_conf *c,
+                void *data)
+{
+	return lxc_clear_rootfs_masked_paths(c);
+}
+
+/* isulad: clr config rootfs ro paths */
+static inline int clr_config_rootfs_ro_paths(const char *key, struct lxc_conf *c,
+                void *data)
+{
+	return lxc_clear_rootfs_ro_paths(c);
+}
+
+/* isulad: set config for systemd */
+static int set_config_systemd(const char *key, const char *value,
+			      struct lxc_conf *lxc_conf, void *data)
+{
+	if (lxc_config_value_empty(value)) {
+		ERROR("Empty umask");
+		return -1;
+	}
+	lxc_conf->systemd = strdup(value);
+	return 0;
+}
+
+/* isulad add: get systemd value*/
+static int get_config_systemd(const char *key, char *retv, int inlen,
+			      struct lxc_conf *c, void *data)
+{
+	return lxc_get_conf_str(retv, inlen, c->systemd);
+}
+
+/* isulad add: clear systemd value */
+static inline int clr_config_systemd(const char *key, struct lxc_conf *c,
+				     void *data)
+{
+	free(c->systemd);
+	c->systemd = NULL;
+	return 0;
+}
+
+static int set_config_console_log_driver(const char *key, const char *value,
+        struct lxc_conf *lxc_conf, void *data)
+{
+	return set_config_string_item(&lxc_conf->console.log_driver, value);
+}
+
+static int set_config_console_syslog_tag(const char *key, const char *value,
+        struct lxc_conf *lxc_conf, void *data)
+{
+	if (value == NULL) {
+		return -1;
+	}
+	return set_config_string_item(&lxc_conf->console.log_syslog_tag, value);
+}
+
+static int parse_facility(const char *facility)
+{
+#define FACILITIES_LEN 20
+	const char *facility_keys[FACILITIES_LEN] = {
+		"kern", "user", "mail", "daemon", "auth",
+		"syslog", "lpr", "news", "uucp", "cron", "authpriv", "ftp",
+		"local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7"
+	};
+	const int facilities[FACILITIES_LEN] = {
+		LOG_KERN, LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG,
+		LOG_LPR, LOG_NEWS, LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_FTP,
+		LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4,
+		LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7
+	};
+	int i = 0;
+
+	if (facility == NULL) {
+		return -1;
+	}
+
+	for (; i < FACILITIES_LEN; i++) {
+		if (strcmp(facility, facility_keys[i]) == 0) {
+			return facilities[i];
+		}
+	}
+
+	return -1;
+}
+
+static int set_config_console_syslog_facility(const char *key, const char *value,
+        struct lxc_conf *lxc_conf, void *data)
+{
+	int facility;
+
+	facility = parse_facility(value);
+	if (facility < 0) {
+		NOTICE("Invalid facility: %s", value);
+		facility = LOG_DAEMON;
+	}
+
+	lxc_conf->console.log_syslog_facility = facility;
+	return 0;
+}
+
+static int set_config_selinux_mount_context(const char *key, const char *value,
+    struct lxc_conf *lxc_conf, void *data)
+{
+	if (value != NULL && strcmp(value, "unconfined_t") == 0) {
+		return set_config_string_item(&lxc_conf->lsm_se_mount_context, NULL);
+	}
+
+	return set_config_string_item(&lxc_conf->lsm_se_mount_context, value);
+}
+
+static int get_config_console_log_driver(const char *key, char *retv, int inlen,
+        struct lxc_conf *c, void *data)
+{
+	return lxc_get_conf_str(retv, inlen, c->console.log_driver);
+}
+
+static int get_config_console_syslog_tag(const char *key, char *retv, int inlen,
+        struct lxc_conf *c, void *data)
+{
+	return lxc_get_conf_str(retv, inlen, c->console.log_syslog_tag);
+}
+
+static int get_config_console_syslog_facility(const char *key, char *retv, int inlen,
+        struct lxc_conf *c, void *data)
+{
+	return lxc_get_conf_int(c, retv, inlen, c->console.log_syslog_facility);
+}
+
+static int get_config_selinux_mount_context(const char *key, char *retv, int inlen,
+    struct lxc_conf *c, void *data)
+{
+	return lxc_get_conf_str(retv, inlen, c->lsm_se_mount_context);
+}
+
+static inline int clr_config_console_log_driver(const char *key,
+                struct lxc_conf *c, void *data)
+{
+	free(c->console.log_driver);
+	c->console.log_driver = NULL;
+	return 0;
+}
+
+static inline int clr_config_console_syslog_tag(const char *key,
+                struct lxc_conf *c, void *data)
+{
+	free(c->console.log_syslog_tag);
+	c->console.log_syslog_tag= NULL;
+	return 0;
+}
+
+static inline int clr_config_console_syslog_facility(const char *key,
+                struct lxc_conf *c, void *data)
+{
+	c->console.log_syslog_facility = LOG_DAEMON;
+	return 0;
+}
+
+static inline int clr_config_selinux_mount_context(const char *key,
+    struct lxc_conf *c, void *data)
+{
+	free(c->lsm_se_mount_context);
+	c->lsm_se_mount_context = NULL;
+	return 0;
+}
+#endif
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index aac6214..3f75184 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -62,6 +62,10 @@
 #include "utils.h"
 #include "version.h"
 
+#ifdef HAVE_ISULAD
+#include "exec_commands.h"
+#endif
+
 #if HAVE_OPENSSL
 #include <openssl/evp.h>
 #endif
@@ -83,6 +87,11 @@
 
 lxc_log_define(lxccontainer, lxc);
 
+#ifdef HAVE_ISULAD
+typedef bool (*func_is_io_stat_read)(const char *value);
+typedef bool (*func_is_io_stat_write)(const char *value);
+#endif
+
 static bool do_lxcapi_destroy(struct lxc_container *c);
 static const char *lxcapi_get_config_path(struct lxc_container *c);
 #define do_lxcapi_get_config_path(c) lxcapi_get_config_path(c)
@@ -281,6 +290,13 @@ static void lxc_container_free(struct lxc_container *c)
 	free(c->config_path);
 	c->config_path = NULL;
 
+#ifdef HAVE_ISULAD
+	free(c->exit_fifo);
+	c->exit_fifo = NULL;
+	free(c->ocihookfile);
+	c->ocihookfile = NULL;
+#endif
+
 	free(c);
 }
 
@@ -519,6 +535,7 @@ static bool do_lxcapi_freeze(struct lxc_container *c)
 	return true;
 }
 
+
 WRAP_API(bool, lxcapi_freeze)
 
 static bool do_lxcapi_unfreeze(struct lxc_container *c)
@@ -623,6 +640,66 @@ static bool load_config_locked(struct lxc_container *c, const char *fname)
 	return true;
 }
 
+#ifdef HAVE_ISULAD
+static bool load_ocihooks_locked(struct lxc_container *c)
+{
+	parser_error err = NULL;
+	oci_runtime_spec_hooks *hooks = NULL;
+
+	if (!c->lxc_conf)
+		c->lxc_conf = lxc_conf_init();
+
+	if (!c->lxc_conf)
+		return false;
+
+	hooks = oci_runtime_spec_hooks_parse_file(c->ocihookfile, NULL, &err);
+	if (!hooks) {
+		fprintf(stderr, "parse oci hooks config failed: %s\n", err);
+		free(err);
+		return true;
+	}
+	c->lxc_conf->ocihooks = hooks;
+
+	if (err)
+		free(err);
+	return true;
+}
+
+/*
+ * isulad: set oci hook file path
+ * */
+static bool set_oci_hook_config_filename(struct lxc_container *c)
+{
+#define OCI_HOOK_JSON_FILE_NAME "ocihooks.json"
+	char *newpath = NULL;
+	int len, ret;
+
+	if (!c->config_path)
+		return false;
+
+	/* $lxc_path + "/" + c->name + "/" + "config" + '\0' */
+	if (strlen(c->config_path) + strlen(c->name) > SIZE_MAX - strlen(OCI_HOOK_JSON_FILE_NAME) - 3)
+		return false;
+	len = strlen(c->config_path) + strlen(c->name) + strlen(OCI_HOOK_JSON_FILE_NAME) + 3;
+
+	newpath = malloc(len);
+	if (newpath == NULL)
+		return false;
+
+	ret = snprintf(newpath, len, "%s/%s/%s", c->config_path, c->name, OCI_HOOK_JSON_FILE_NAME);
+	if (ret < 0 || ret >= len) {
+		fprintf(stderr, "Error printing out config file name\n");
+		free(newpath);
+		return false;
+	}
+
+	free(c->ocihookfile);
+	c->ocihookfile = newpath;
+
+	return true;
+}
+#endif
+
 static bool do_lxcapi_load_config(struct lxc_container *c, const char *alt_file)
 {
 	int lret;
@@ -656,6 +733,11 @@ static bool do_lxcapi_load_config(struct lxc_container *c, const char *alt_file)
 
 	ret = load_config_locked(c, fname);
 
+#ifdef HAVE_ISULAD
+	if (ret && file_exists(c->ocihookfile))
+		ret = load_ocihooks_locked(c);
+#endif
+
 	if (need_disklock)
 		container_disk_unlock(c);
 	else
@@ -855,6 +937,33 @@ static bool wait_on_daemonized_start(struct lxc_handler *handler, int pid)
 	return true;
 }
 
+#ifdef HAVE_ISULAD
+/* isulad: use init argv as init cmd */
+static char **use_init_args(char **init_argv, size_t init_args)
+{
+	size_t i;
+	int nargs = 0;
+	char **argv;
+
+	if (!init_argv)
+		return NULL;
+
+	do {
+		argv = malloc(sizeof(char *));
+	} while (!argv);
+
+	argv[0] = NULL;
+	for (i = 0; i < init_args; i++)
+		push_arg(&argv, init_argv[i], &nargs);
+
+	if (nargs == 0) {
+		free(argv);
+		return NULL;
+	}
+	return argv;
+}
+#endif
+
 static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const argv[])
 {
 	int ret;
@@ -865,6 +974,11 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
 		NULL,
 	};
 	char **init_cmd = NULL;
+#ifdef HAVE_ISULAD
+	int keepfds[] = {-1, -1, -1, -1, -1};
+	ssize_t size_read;
+	char errbuf[BUFSIZ + 1] = {0};
+#endif
 
 	/* container does exist */
 	if (!c)
@@ -911,6 +1025,16 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
 			argv = init_cmd = split_init_cmd(conf->init_cmd);
 	}
 
+#ifdef HAVE_ISULAD
+	if (!argv) {
+		argv = init_cmd = use_init_args(conf->init_argv, conf->init_argc);
+	}
+
+	if (c->image_type_oci) {
+		handler->image_type_oci = true;
+	}
+#endif
+
 	/* ... otherwise use default_args. */
 	if (!argv) {
 		if (useinit) {
@@ -930,10 +1054,23 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
 		char title[2048];
 		pid_t pid_first, pid_second;
 
+#ifdef HAVE_ISULAD
+		//isulad: pipdfd for get error message of child or grandchild process.
+		if (pipe2(conf->errpipe, O_CLOEXEC) != 0) {
+			SYSERROR("Failed to init errpipe");
+			free_init_cmd(init_cmd);
+			lxc_put_handler(handler);
+			return false;
+		}
+#endif
+
 		pid_first = fork();
 		if (pid_first < 0) {
 			free_init_cmd(init_cmd);
 			lxc_put_handler(handler);
+#ifdef HAVE_ISULAD
+			lxc_close_error_pipe(conf->errpipe);
+#endif
 			return false;
 		}
 
@@ -943,11 +1080,25 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
 			 * the PID file, child will do the free and unlink.
 			 */
 			c->pidfile = NULL;
+#ifdef HAVE_ISULAD
+			close(conf->errpipe[1]);
+			conf->errpipe[1] = -1;
+#endif
 
 			/* Wait for container to tell us whether it started
 			 * successfully.
 			 */
 			started = wait_on_daemonized_start(handler, pid_first);
+#ifdef HAVE_ISULAD
+			if (!started) {
+				size_read = read(conf->errpipe[0], errbuf, BUFSIZ);
+				if (size_read > 0) {
+					conf->errmsg = safe_strdup(errbuf);
+				}
+			}
+			close(conf->errpipe[0]);
+			conf->errpipe[0] = -1;
+#endif
 
 			free_init_cmd(init_cmd);
 			lxc_put_handler(handler);
@@ -983,6 +1134,9 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
 		if (pid_second != 0) {
 			free_init_cmd(init_cmd);
 			lxc_put_handler(handler);
+#ifdef HAVE_ISULAD
+			lxc_close_error_pipe(conf->errpipe);
+#endif
 			_exit(EXIT_SUCCESS);
 		}
 
@@ -995,7 +1149,18 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
 			_exit(EXIT_FAILURE);
 		}
 
+#ifdef HAVE_ISULAD
+		keepfds[0] = handler->conf->maincmd_fd;
+		keepfds[1] = handler->state_socket_pair[0];
+		keepfds[2] = handler->state_socket_pair[1];
+		keepfds[4] = conf->errpipe[1];
+		close(conf->errpipe[0]);
+		conf->errpipe[0] = -1;
+		ret = lxc_check_inherited(conf, true, keepfds,
+					  sizeof(keepfds) / sizeof(keepfds[0]));
+#else
 		ret = inherit_fds(handler, true);
+#endif
 		if (ret < 0)
 			_exit(EXIT_FAILURE);
 
@@ -1028,6 +1193,9 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
 		if (w < 0 || (size_t)w >= sizeof(pidstr)) {
 			free_init_cmd(init_cmd);
 			lxc_put_handler(handler);
+#ifdef HAVE_ISULAD
+			lxc_close_error_pipe(conf->errpipe);
+#endif
 
 			SYSERROR("Failed to write monitor pid to \"%s\"", c->pidfile);
 
@@ -1041,6 +1209,9 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
 		if (ret < 0) {
 			free_init_cmd(init_cmd);
 			lxc_put_handler(handler);
+#ifdef HAVE_ISULAD
+			lxc_close_error_pipe(conf->errpipe);
+#endif
 
 			SYSERROR("Failed to write monitor pid to \"%s\"", c->pidfile);
 
@@ -1051,6 +1222,19 @@ static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const a
 		}
 	}
 
+#ifdef HAVE_ISULAD
+	/* isulad: open exit fifo */
+	if (c->exit_fifo) {
+		conf->exit_fd = lxc_open(c->exit_fifo, O_WRONLY | O_NONBLOCK | O_CLOEXEC, 0);
+		if (conf->exit_fd < 0) {
+			ERROR("Failed to open exit fifo %s: %s.", c->exit_fifo, strerror(errno));
+			lxc_put_handler(handler);
+			ret = 1;
+			goto on_error;
+		}
+	}
+#endif
+
 	conf->reboot = REBOOT_NONE;
 
 	/* Unshare the mount namespace if requested */
@@ -1082,19 +1266,53 @@ reboot:
 		}
 	}
 
+#ifdef HAVE_ISULAD
+	keepfds[0] = handler->conf->maincmd_fd;
+	keepfds[1] = handler->state_socket_pair[0];
+	keepfds[2] = handler->state_socket_pair[1];
+
+	/* keep exit fifo fd */
+	if (conf->exit_fd >= 0) {
+		keepfds[3] = conf->exit_fd;
+	}
+	/* isulad: keep errpipe fd */
+	if (c->daemonize)
+		keepfds[4] = conf->errpipe[1];
+
+	ret = lxc_check_inherited(conf, c->daemonize, keepfds,
+				  sizeof(keepfds) / sizeof(keepfds[0]));
+	if (ret < 0) {
+		lxc_put_handler(handler);
+		ret = 1;
+		goto on_error;
+	}
+#else
 	ret = inherit_fds(handler, c->daemonize);
 	if (ret < 0) {
 		lxc_put_handler(handler);
 		ret = 1;
 		goto on_error;
 	}
+#endif
 
+#ifndef HAVE_ISULAD
 	if (useinit)
 		ret = lxc_execute(c->name, argv, 1, handler, c->config_path,
 				  c->daemonize, &c->error_num);
 	else
 		ret = lxc_start(argv, handler, c->config_path, c->daemonize,
 				&c->error_num);
+#else
+	if (useinit) {
+		ret = lxc_execute(c->name, argv, 1, handler, c->config_path,
+				  c->daemonize, &c->error_num, c->start_timeout);
+	} else {
+		handler->disable_pty = c->disable_pty;
+		handler->open_stdin = c->open_stdin;
+		ret = lxc_start(argv, handler, c->config_path, c->daemonize,
+				&c->error_num, c->start_timeout);
+	}
+#endif
 
 	if (conf->reboot == REBOOT_REQ) {
 		INFO("Container requested reboot");
@@ -1185,6 +1403,9 @@ WRAP_API(bool, lxcapi_stop)
 
 static int do_create_container_dir(const char *path, struct lxc_conf *conf)
 {
+#ifdef HAVE_ISULAD
+	__do_free char *p = NULL;
+#endif
 	int lasterr;
 	int ret = -1;
 
@@ -1200,8 +1421,16 @@ static int do_create_container_dir(const char *path, struct lxc_conf *conf)
 		ret = 0;
 	}
 
+#ifdef HAVE_ISULAD
+	p = must_copy_string(path);
+#endif
+
 	if (!lxc_list_empty(&conf->id_map)) {
+#ifdef HAVE_ISULAD
+		ret = chown_mapped_root(p, conf);
+#else
 		ret = chown_mapped_root(path, conf);
+#endif
 		if (ret < 0)
 			ret = -1;
 	}
@@ -2048,7 +2277,12 @@ WRAP_API_1(bool, lxcapi_reboot2, int)
 static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
 {
 	__do_close int pidfd = -EBADF, state_client_fd = -EBADF;
+#ifdef HAVE_ISULAD
+	// isulad: keep default signal the same as docker
+	int haltsignal = SIGTERM;
+#else
 	int haltsignal = SIGPWR;
+#endif
 	pid_t pid = -1;
 	lxc_state_t states[MAX_STATE] = {0};
 	int killret, ret;
@@ -2067,9 +2301,10 @@ static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
 	/* Detect whether we should send SIGRTMIN + 3 (e.g. systemd). */
 	if (c->lxc_conf && c->lxc_conf->haltsignal)
 		haltsignal = c->lxc_conf->haltsignal;
+#ifndef HAVE_ISULAD
 	else if (task_blocks_signal(pid, (SIGRTMIN + 3)))
 		haltsignal = (SIGRTMIN + 3);
-
+#endif
 
 	/*
 	 * Add a new state client before sending the shutdown signal so
@@ -2938,6 +3173,21 @@ static int lxc_unlink_exec_wrapper(void *data)
 	return unlink(arg);
 }
 
+#ifdef HAVE_ISULAD
+static void container_sock_dir_delete(const char *name)
+{
+	__do_free char *sock_dir = NULL;
+
+	sock_dir = generate_named_unix_sock_dir(name);
+	if (sock_dir == NULL) {
+		ERROR("Failed to generate exec unix sock dir");
+		return;
+	}
+
+	(void)lxc_rmdir_onedev(sock_dir, NULL);
+}
+#endif
+
 static bool container_destroy(struct lxc_container *c,
 			      struct lxc_storage *storage)
 {
@@ -2948,8 +3198,19 @@ static bool container_destroy(struct lxc_container *c,
 	bool bret = false;
 	int ret = 0;
 
+#ifdef HAVE_ISULAD
+	if (!c)
+		return false;
+	// isulad: if container is not defined, we need to remove disk lock file
+	// which is created in lxc_container_new.
+	if (!do_lxcapi_is_defined(c)) {
+		container_disk_removelock(c);
+		return false;
+	}
+#else
 	if (!c || !do_lxcapi_is_defined(c))
 		return false;
+#endif
 
 	conf = c->lxc_conf;
 	if (container_disk_lock(c))
@@ -3069,8 +3330,20 @@ static bool container_destroy(struct lxc_container *c,
 	if (ret < 0) {
 		ERROR("Failed to destroy directory \"%s\" for \"%s\"", path,
 		      c->name);
+#ifdef HAVE_ISULAD
+		char msg[BUFSIZ] = { 0 };
+		ret = snprintf(msg, BUFSIZ, "Failed to destroy directory \"%s\": %s", path, errno ? strerror(errno) : "error");
+		if (ret < 0 || ret >= BUFSIZ) {
+			ERROR("Sprintf failed");
+			goto out;
+		}
+		c->error_string = safe_strdup(msg);
+#endif
 		goto out;
 	}
+#ifdef HAVE_ISULAD
+	container_sock_dir_delete(c->name);
+#endif
 	INFO("Destroyed directory \"%s\" for \"%s\"", path, c->name);
 
 on_success:
@@ -3081,6 +3354,11 @@ out:
 		free(path);
 
 	container_disk_unlock(c);
+#ifdef HAVE_ISULAD
+	if (bret && container_disk_removelock(c)) {
+		bret = false;
+	}
+#endif
 	return bret;
 }
 
@@ -4030,8 +4308,13 @@ static int lxcapi_attach(struct lxc_container *c,
 
 	current_config = c->lxc_conf;
 
+#ifdef HAVE_ISULAD
+	ret = lxc_attach(c, exec_function, exec_payload, options,
+			 attached_process, &c->lxc_conf->errmsg);
+#else
 	ret = lxc_attach(c, exec_function, exec_payload, options,
 			 attached_process);
+#endif
 	current_config = NULL;
 	return ret;
 }
@@ -4051,7 +4334,11 @@ static int do_lxcapi_attach_run_wait(struct lxc_container *c,
 	command.program = (char *)program;
 	command.argv = (char **)argv;
 
+#ifdef HAVE_ISULAD
+	ret = lxc_attach(c, lxc_attach_run_command, &command, options, &pid, NULL);
+#else
 	ret = lxc_attach(c, lxc_attach_run_command, &command, options, &pid);
+#endif
 	if (ret < 0)
 		return ret;
 
@@ -5230,7 +5517,561 @@ static int do_lxcapi_seccomp_notify_fd(struct lxc_container *c)
 
 WRAP_API(int, lxcapi_seccomp_notify_fd)
 
+#ifdef HAVE_ISULAD
+/* isulad add set console fifos*/
+static bool do_lxcapi_set_terminal_default_fifos(struct lxc_container *c, const char *in, const char *out, const char *err)
+{
+	struct lxc_conf *conf = NULL;
+
+	if (!c || !c->lxc_conf)
+		return false;
+	if (container_mem_lock(c)) {
+		ERROR("Error getting mem lock");
+		return false;
+	}
+
+	conf = c->lxc_conf;
+	if (in) {
+		if (conf->console.init_fifo[0])
+			free(conf->console.init_fifo[0]);
+		conf->console.init_fifo[0] = safe_strdup(in);
+	}
+	if (out) {
+		if (conf->console.init_fifo[1])
+			free(conf->console.init_fifo[1]);
+		conf->console.init_fifo[1] = safe_strdup(out);
+	}
+	if (err) {
+		if (conf->console.init_fifo[2])
+			free(conf->console.init_fifo[2]);
+		conf->console.init_fifo[2] = safe_strdup(err);
+	}
+
+	container_mem_unlock(c);
+	return true;
+}
+
+WRAP_API_3(bool, lxcapi_set_terminal_default_fifos, const char *, const char *, const char *)
+
+/* isulad add set info file path */
+static bool do_lxcapi_set_container_info_file(struct lxc_container *c, const char *info_file)
+{
+	struct lxc_conf *conf = NULL;
+
+	if (!c || !c->lxc_conf || !info_file)
+		return false;
+	if (container_mem_lock(c)) {
+		ERROR("Error getting mem lock");
+		return false;
+	}
+
+	conf = c->lxc_conf;
+	if (conf->container_info_file)
+		free(conf->container_info_file);
+	conf->container_info_file = safe_strdup(info_file);
+
+	container_mem_unlock(c);
+	return true;
+}
+
+WRAP_API_1(bool, lxcapi_set_container_info_file, const char *)
+
+static bool do_lxcapi_want_disable_pty(struct lxc_container *c, bool state)
+{
+	if (!c || !c->lxc_conf)
+		return false;
+
+	if (container_mem_lock(c))
+		return false;
+
+	c->disable_pty = state;
+
+	container_mem_unlock(c);
+
+	return true;
+}
+
+WRAP_API_1(bool, lxcapi_want_disable_pty, bool)
+
+static bool do_lxcapi_want_open_stdin(struct lxc_container *c, bool state)
+{
+	if (!c || !c->lxc_conf)
+		return false;
+
+	if (container_mem_lock(c))
+		return false;
+
+	c->open_stdin = state;
+
+	container_mem_unlock(c);
+
+	return true;
+}
+
+WRAP_API_1(bool, lxcapi_want_open_stdin, bool)
+
+/* isulad add clean resources */
+static bool do_lxcapi_add_terminal_fifo(struct lxc_container *c, const char *in_fifo, const char *out_fifo, const char *err_fifo)
+{
+	bool ret = true;
+
+	if (!c || !c->lxc_conf)
+		return false;
+	if (container_mem_lock(c)) {
+		ERROR("Error getting mem lock");
+		return false;
+	}
+
+	if (lxc_cmd_set_terminal_fifos(c->name, c->config_path, in_fifo, out_fifo, err_fifo)) {
+		ERROR("Error set console fifos");
+		ret = false;
+	}
+
+	container_mem_unlock(c);
+	return ret;
+}
+
+WRAP_API_3(bool, lxcapi_add_terminal_fifo, const char *, const char *, const char *)
+
+static bool do_lxcapi_set_terminal_winch(struct lxc_container *c, unsigned int height, unsigned int width)
+{
+	bool ret = true;
+
+	if (!c || !c->lxc_conf)
+		return false;
+	if (container_mem_lock(c)) {
+		ERROR("Error getting mem lock");
+		return false;
+	}
+
+	if (lxc_cmd_set_terminal_winch(c->name, c->config_path, height, width)) {
+		ERROR("Error set terminal winch");
+		ret = false;
+	}
+
+	container_mem_unlock(c);
+	return ret;
+}
+
+WRAP_API_2(bool, lxcapi_set_terminal_winch, unsigned int, unsigned int)
+
+static bool do_lxcapi_set_exec_terminal_winch(struct lxc_container *c, const char *suffix, unsigned int height, unsigned int width)
+{
+	bool ret = true;
+
+	if (!c || !c->lxc_conf)
+		return false;
+	if (container_mem_lock(c)) {
+		ERROR("Error getting mem lock");
+		return false;
+	}
+
+	if (lxc_exec_cmd_set_terminal_winch(c->name, c->config_path, suffix, height, width)) {
+		ERROR("Error set terminal winch");
+		ret = false;
+	}
+
+	container_mem_unlock(c);
+	return ret;
+}
+
+WRAP_API_3(bool, lxcapi_set_exec_terminal_winch, const char *, unsigned int, unsigned int)
+
+/* isulad add clean resources */
+static bool do_lxcapi_clean_container_resource(struct lxc_container *c, pid_t pid)
+{
+	int ret;
+
+	if (!c)
+		return false;
+
+	ret = do_lxcapi_clean_resource(c->name, c->config_path, c->lxc_conf, pid);
+	if (ret)
+		ERROR("Failed to clean container %s resource", c->name);
+	return ret == 0;
+
+}
+
+WRAP_API_1(bool, lxcapi_clean_container_resource, pid_t)
+
+/* isulad get coantainer pids */
+static bool do_lxcapi_get_container_pids(struct lxc_container *c, pid_t **pids,size_t *pids_len)
+{
+	int ret;
+
+	if (!c)
+		return false;
+
+	ret = do_lxcapi_get_pids(c->name, c->config_path, c->lxc_conf, pids,pids_len);
+	if (ret)
+		ERROR("Failed to get container %s pids", c->name);
+	return ret == 0;
+
+}
+
+WRAP_API_2(bool, lxcapi_get_container_pids, pid_t **,size_t *)
+
+/* isulad add start timeout */
+static bool do_lxcapi_set_start_timeout(struct lxc_container *c,  unsigned int start_timeout)
+{
+	if (!c || !c->lxc_conf)
+		return false;
+	if (container_mem_lock(c)) {
+		ERROR("Error getting mem lock");
+		return false;
+	}
+	c->start_timeout = start_timeout;
+	container_mem_unlock(c);
+	return true;
+}
+
+WRAP_API_1(bool, lxcapi_set_start_timeout, unsigned int)
+
+/* isulad add set image type */
+static bool do_lxcapi_set_oci_type(struct lxc_container *c,  bool image_type_oci)
+{
+	if (!c || !c->lxc_conf)
+		return false;
+	if (container_mem_lock(c)) {
+		ERROR("Error getting mem lock");
+		return false;
+	}
+	c->image_type_oci = image_type_oci;
+	container_mem_unlock(c);
+	return true;
+}
+
+WRAP_API_1(bool, lxcapi_set_oci_type, bool)
+
+static uint64_t metrics_get_ull(struct lxc_container *c, struct cgroup_ops *cgroup_ops, const char *item)
+{
+    char buf[81] = {0};
+    int len = 0;
+    uint64_t val = 0;
+
+    len = cgroup_ops->get(cgroup_ops, item, buf, sizeof(buf) - 1, c->name, c->config_path);
+    if (len <= 0) {
+        DEBUG("unable to read cgroup item %s", item);
+        return 0;
+    }
+
+    val = strtoull(buf, NULL, 0);
+    return val;
+}
+
+static uint64_t metrics_get_ull_with_max(struct lxc_container *c, struct cgroup_ops *cgroup_ops, const char *item)
+{
+    char buf[81] = {0};
+    int len = 0;
+    uint64_t val = 0;
+
+    len = cgroup_ops->get(cgroup_ops, item, buf, sizeof(buf) - 1, c->name, c->config_path);
+    if (len <= 0) {
+        DEBUG("unable to read cgroup item %s", item);
+        return 0;
+    }
+
+    if (strcmp(buf, "max") == 0) {
+	return ULONG_MAX;
+    }
+
+    val = strtoull(buf, NULL, 0);
+    return val;
+}
+
+static inline bool is_blk_metrics_read(const char *value)
+{
+    return strcmp(value, "Read") == 0;
+}
+
+static inline bool is_blk_metrics_write(const char *value)
+{
+    return strcmp(value, "Write") == 0;
+}
+
+static inline bool is_blk_metrics_total(const char *value)
+{
+    return strcmp(value, "Total") == 0;
+}
+
+static void metrics_get_blk_stats(struct lxc_container *c, struct cgroup_ops *cgroup_ops, const char *item, struct lxc_blkio_metrics *stats)
+{
+    char *buf = NULL;
+    int i = 0;
+    int len = 0;
+    int ret = 0;
+    char **lines = NULL;
+    char **cols = NULL;
+
+    len = cgroup_ops->get(cgroup_ops, item, NULL, 0, c->name, c->config_path);
+    if (len <= 0) {
+        DEBUG("unable to read cgroup item %s", item);
+        return;
+    }
+
+    buf = malloc(len + 1);
+    (void)memset(buf, 0, len + 1);
+    ret = cgroup_ops->get(cgroup_ops, item, buf, len, c->name, c->config_path);
+    if (ret <= 0) {
+        DEBUG("unable to read cgroup item %s", item);
+        goto out;
+    }
+
+    lines = lxc_string_split_and_trim(buf, '\n');
+    if (lines == NULL) {
+        goto out;
+    }
+
+    (void)memset(stats, 0, sizeof(struct lxc_blkio_metrics));
+
+    for (i = 0; lines[i]; i++) {
+        cols = lxc_string_split_and_trim(lines[i], ' ');
+        if (cols == NULL) {
+            goto err_out;
+        }
+        if (lxc_array_len((void **)cols) == 3) {
+            if (is_blk_metrics_read(cols[1])) {
+                stats->read += strtoull(cols[2], NULL, 0);
+            } else if (is_blk_metrics_write(cols[1])) {
+                stats->write += strtoull(cols[2], NULL, 0);
+            }
+        }
+        if (lxc_array_len((void **)cols) == 2 && is_blk_metrics_total(cols[0])) {
+            stats->total = strtoull(cols[1], NULL, 0);
+        }
+
+        lxc_free_array((void **)cols, free);
+    }
+err_out:
+    lxc_free_array((void **)lines, free);
+out:
+    free(buf);
+    return;
+}
+
+static void metrics_get_io_stats_v2(struct lxc_container *c, struct cgroup_ops *cgroup_ops, const char *item, struct lxc_blkio_metrics *stats, func_is_io_stat_read is_io_stat_read, func_is_io_stat_write is_io_stat_write)
+{
+    char *buf = NULL;
+    int i = 0;
+    int j = 0;
+    int len = 0;
+    int ret = 0;
+    char **lines = NULL;
+    char **cols = NULL;
+    char **kv = NULL;
+
+    len = cgroup_ops->get(cgroup_ops, item, NULL, 0, c->name, c->config_path);
+    if (len <= 0) {
+        DEBUG("unable to read cgroup item %s", item);
+        return;
+    }
+
+    buf = malloc(len + 1);
+    (void)memset(buf, 0, len + 1);
+    ret = cgroup_ops->get(cgroup_ops, item, buf, len, c->name, c->config_path);
+    if (ret <= 0) {
+        DEBUG("unable to read cgroup item %s", item);
+        goto out;
+    }
+
+    lines = lxc_string_split_and_trim(buf, '\n');
+    if (lines == NULL) {
+        goto out;
+    }
+
+    (void)memset(stats, 0, sizeof(struct lxc_blkio_metrics));
+    // line example:
+    // 259:0 rbytes=0 wbytes=12288 rios=0 wios=4 dbytes=0 dios=0
+    for (i = 0; lines[i]; i++) {
+        cols = lxc_string_split_and_trim(lines[i], ' ');
+        if (cols == NULL || lxc_array_len((void **)cols) < 2) {
+            goto err_out;
+        }
+        len = lxc_array_len((void **)cols);
+        for (j = 1; j < len; j++) {
+            kv = lxc_string_split(cols[j], '=');
+            if (kv == NULL || lxc_array_len((void **)kv) != 2) {
+                lxc_free_array((void **)kv, free);
+                continue;
+            }
+            if (is_io_stat_read(kv[0])) {
+                stats->read += strtoull(kv[1], NULL, 0);
+            } else if (is_io_stat_write(kv[0])) {
+                stats->write += strtoull(kv[1], NULL, 0);
+            }
+            lxc_free_array((void **)kv, free);
+        }
+        lxc_free_array((void **)cols, free);
+    }
+
+    stats->total = stats->read + stats->write;
+
+err_out:
+    lxc_free_array((void **)lines, free);
+out:
+    free(buf);
+    return;
+}
+
+static uint64_t metrics_match_get_ull(struct lxc_container *c, struct cgroup_ops *cgroup_ops, const char *item, const char *match, int column)
+{
+#define BUFSIZE 4096
+    char buf[BUFSIZE] = {0};
+    int i = 0;
+    int j = 0;
+    int len = 0;
+    uint64_t val = 0;
+    char **lines = NULL;
+    char **cols = NULL;
+    size_t matchlen = 0;
+
+    len = cgroup_ops->get(cgroup_ops, item, buf, sizeof(buf) - 1, c->name, c->config_path);
+    if (len <= 0) {
+        DEBUG("unable to read cgroup item %s", item);
+        goto err_out;
+    }
+
+    lines = lxc_string_split_and_trim(buf, '\n');
+    if (lines == NULL) {
+        goto err_out;
+    }
+
+    matchlen = strlen(match);
+    for (i = 0; lines[i]; i++) {
+        if (strncmp(lines[i], match, matchlen) != 0) {
+            continue;
+        }
+
+        cols = lxc_string_split_and_trim(lines[i], ' ');
+        if (cols == NULL) {
+            goto err1;
+        }
+        for (j = 0; cols[j]; j++) {
+            if (j == column) {
+                val = strtoull(cols[j], NULL, 0);
+                break;
+            }
+        }
+        lxc_free_array((void **)cols, free);
+        break;
+    }
+err1:
+    lxc_free_array((void **)lines, free);
+err_out:
+    return val;
+}
+
+static bool is_io_stat_rbytes(const char *value)
+{
+    return strcmp(value, "rbytes") == 0;
+}
+
+static bool is_io_stat_wbytes(const char *value)
+{
+    return strcmp(value, "wbytes") == 0;
+}
+
+static bool is_io_stat_rios(const char *value)
+{
+    return strcmp(value, "rios") == 0;
+}
+
+static bool is_io_stat_wios(const char *value)
+{
+    return strcmp(value, "wios") == 0;
+}
+
+static bool unified_metrics_get(struct lxc_container *c, struct cgroup_ops *cgroup_ops, struct lxc_container_metrics *metrics)
+{
+	// cpu
+	metrics->cpu_use_nanos = metrics_match_get_ull(c, cgroup_ops, "cpu.stat", "usage_usec", 1) * 1000;
+	metrics->cpu_use_user = metrics_match_get_ull(c, cgroup_ops, "cpu.stat", "user_usec", 1) * 1000;
+	metrics->cpu_use_sys = metrics_match_get_ull(c, cgroup_ops, "cpu.stat", "system_usec", 1) * 1000;
+
+	// io
+	metrics_get_io_stats_v2(c, cgroup_ops, "io.stat", &metrics->io_service_bytes, is_io_stat_rbytes, is_io_stat_wbytes);
+	metrics_get_io_stats_v2(c, cgroup_ops, "io.stat", &metrics->io_serviced, is_io_stat_rios, is_io_stat_wios);
+
+	// memory
+	metrics->mem_used = metrics_get_ull(c, cgroup_ops, "memory.current");
+	metrics->mem_limit = metrics_get_ull_with_max(c, cgroup_ops, "memory.max");
+	metrics->inactive_file_total = metrics_match_get_ull(c, cgroup_ops, "memory.stat", "inactive_file", 1);
+	metrics->cache = metrics_match_get_ull(c, cgroup_ops, "memory.stat", "file", 1);
+	metrics->cache_total = metrics->cache;
+
+	// cgroup v2 does not support kernel memory
+	metrics->kmem_used = 0;
+	metrics->kmem_limit = 0;
+
+	// pids
+	metrics->pids_current = metrics_get_ull(c, cgroup_ops, "pids.current");
+
+	return true;
+}
+
+/* isulad add get container metrics */
+static bool do_lxcapi_get_container_metrics(struct lxc_container *c,  struct lxc_container_metrics *metrics)
+{
+	call_cleaner(cgroup_exit) struct cgroup_ops *cgroup_ops = NULL;
+	const char *state = NULL;
+	if (c == NULL || c->lxc_conf == NULL || metrics == NULL) {
+		return false;
+	}
+
+	state = c->state(c);
+	metrics->state = state;
+
+	if (!is_stopped(c)) {
+		metrics->init = c->init_pid(c);
+	} else {
+		metrics->init = -1;
+	}
+
+	cgroup_ops = cgroup_init(c->lxc_conf);
+	if (cgroup_ops == NULL) {
+		return false;
+	}
+
+	if (cgroup_ops->cgroup_layout == CGROUP_LAYOUT_UNIFIED) {
+		return unified_metrics_get(c, cgroup_ops, metrics);
+	}
+
+	metrics->cpu_use_nanos = metrics_get_ull(c, cgroup_ops, "cpuacct.usage");
+	metrics->pids_current = metrics_get_ull(c, cgroup_ops, "pids.current");
+
+	metrics->cpu_use_user = metrics_match_get_ull(c, cgroup_ops, "cpuacct.stat", "user", 1);
+	metrics->cpu_use_sys = metrics_match_get_ull(c, cgroup_ops, "cpuacct.stat", "system", 1);
+
+	// Try to read CFQ stats available on all CFQ enabled kernels first
+	metrics_get_blk_stats(c, cgroup_ops, "blkio.io_serviced_recursive", &metrics->io_serviced);
+	if (metrics->io_serviced.read == 0 && metrics->io_serviced.write == 0 && metrics->io_serviced.total == 0) {
+		metrics_get_blk_stats(c, cgroup_ops, "blkio.throttle.io_service_bytes", &metrics->io_service_bytes);
+		metrics_get_blk_stats(c, cgroup_ops, "blkio.throttle.io_serviced", &metrics->io_serviced);
+	} else {
+		metrics_get_blk_stats(c, cgroup_ops, "blkio.io_service_bytes_recursive", &metrics->io_service_bytes);
+	}
+
+	metrics->mem_used = metrics_get_ull(c, cgroup_ops, "memory.usage_in_bytes");
+	metrics->mem_limit = metrics_get_ull(c, cgroup_ops, "memory.limit_in_bytes");
+	metrics->kmem_used = metrics_get_ull(c, cgroup_ops, "memory.kmem.usage_in_bytes");
+	metrics->kmem_limit = metrics_get_ull(c, cgroup_ops, "memory.kmem.limit_in_bytes");
+
+	metrics->cache = metrics_match_get_ull(c, cgroup_ops, "memory.stat", "cache", 1);
+	metrics->cache_total = metrics_match_get_ull(c, cgroup_ops, "memory.stat", "total_cache", 1);
+	metrics->inactive_file_total = metrics_match_get_ull(c, cgroup_ops, "memory.stat", "total_inactive_file", 1);
+
+	return true;
+}
+
+WRAP_API_1(bool, lxcapi_get_container_metrics, struct lxc_container_metrics *)
+
+#endif
+
+#ifdef HAVE_ISULAD
+static struct lxc_container *do_lxc_container_new(const char *name, const char *configpath, bool load_config)
+#else
 struct lxc_container *lxc_container_new(const char *name, const char *configpath)
+#endif
 {
 	struct lxc_container *c;
 	size_t len;
@@ -5283,10 +6124,24 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
 		goto err;
 	}
 
+#ifdef HAVE_ISULAD
+	if (!set_oci_hook_config_filename(c)) {
+		fprintf(stderr, "Error allocating oci hooks file pathname\n");
+		goto err;
+	}
+
+	if (load_config && file_exists(c->configfile)) {
+		if (!lxcapi_load_config(c, NULL)) {
+			fprintf(stderr, "Failed to load config for %s\n", name);
+			goto err;
+		}
+	}
+#else
 	if (file_exists(c->configfile) && !lxcapi_load_config(c, NULL)) {
 		fprintf(stderr, "Failed to load config for %s\n", name);
 		goto err;
 	}
+#endif
 
 	rc = ongoing_create(c);
 	switch (rc) {
@@ -5310,6 +6165,9 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
 
 	c->daemonize = true;
 	c->pidfile = NULL;
+#ifdef HAVE_ISULAD
+	c->image_type_oci = false;
+#endif
 
 	/* Assign the member functions. */
 	c->is_defined = lxcapi_is_defined;
@@ -5371,7 +6229,20 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
 	c->mount = lxcapi_mount;
 	c->umount = lxcapi_umount;
 	c->seccomp_notify_fd = lxcapi_seccomp_notify_fd;
-
+#ifdef HAVE_ISULAD
+	c->set_container_info_file = lxcapi_set_container_info_file;
+	c->set_terminal_init_fifos = lxcapi_set_terminal_default_fifos;
+	c->add_terminal_fifos = lxcapi_add_terminal_fifo;
+	c->set_terminal_winch = lxcapi_set_terminal_winch;
+	c->set_exec_terminal_winch = lxcapi_set_exec_terminal_winch;
+	c->want_disable_pty = lxcapi_want_disable_pty;
+	c->want_open_stdin = lxcapi_want_open_stdin;
+	c->clean_container_resource = lxcapi_clean_container_resource;
+	c->get_container_pids = lxcapi_get_container_pids;
+	c->set_start_timeout = lxcapi_set_start_timeout;
+	c->set_oci_type = lxcapi_set_oci_type;
+	c->get_container_metrics = lxcapi_get_container_metrics;
+#endif
 	return c;
 
 err:
@@ -5379,6 +6250,19 @@ err:
 	return NULL;
 }
 
+#ifdef HAVE_ISULAD
+// isulad: new container without load config to save time
+struct lxc_container *lxc_container_without_config_new(const char *name, const char *configpath)
+{
+	return do_lxc_container_new(name, configpath, false);
+}
+
+struct lxc_container *lxc_container_new(const char *name, const char *configpath)
+{
+	return do_lxc_container_new(name, configpath, true);
+}
+#endif
+
 int lxc_get_wait_states(const char **states)
 {
 	int i;
@@ -5557,11 +6441,21 @@ int list_active_containers(const char *lxcpath, char ***nret,
 				continue;
 		}
 
+#ifdef HAVE_ISULAD
+		if (ct_name && ct_name_cnt) {
+			if (array_contains(&ct_name, p, ct_name_cnt)) {
+				if (is_hashed)
+					free(p);
+				continue;
+			}
+		}
+#else
 		if (array_contains(&ct_name, p, ct_name_cnt)) {
 			if (is_hashed)
 				free(p);
 			continue;
 		}
+#endif
 
 		if (!add_to_array(&ct_name, p, ct_name_cnt)) {
 			if (is_hashed)
diff --git a/src/lxc/lxclock.c b/src/lxc/lxclock.c
index 318e5bf..ce6f889 100644
--- a/src/lxc/lxclock.c
+++ b/src/lxc/lxclock.c
@@ -179,6 +179,10 @@ struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name)
 	l->u.f.fd = -1;
 
 on_error:
+#ifdef HAVE_ISULAD
+	if (l == NULL)
+		fprintf(stderr, "Failed to create lock for %s, path %s\n", name, lxcpath);
+#endif
 	return l;
 }
 
@@ -370,3 +374,30 @@ void container_disk_unlock(struct lxc_container *c)
 	lxcunlock(c->slock);
 	lxcunlock(c->privlock);
 }
+
+#ifdef HAVE_ISULAD
+static int lxc_removelock(struct lxc_lock *l)
+{
+	int ret = 0;
+
+	if (l->type == LXC_LOCK_FLOCK) {
+		ret = unlink(l->u.f.fname);
+		if (ret && errno != ENOENT) {
+			SYSERROR("Error unlink %s", l->u.f.fname);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+int container_disk_removelock(struct lxc_container *c)
+{
+	int ret;
+
+	ret = lxc_removelock(c->slock);
+	if (ret)
+		return ret;
+	return lxc_removelock(c->privlock);
+}
+#endif
diff --git a/src/lxc/mainloop.c b/src/lxc/mainloop.c
index d5ae2a6..c47f31b 100644
--- a/src/lxc/mainloop.c
+++ b/src/lxc/mainloop.c
@@ -150,3 +150,19 @@ void lxc_mainloop_close(struct lxc_epoll_descr *descr)
 
 	close_prot_errno_disarm(descr->epfd);
 }
+
+#ifdef HAVE_ISULAD
+int isulad_safe_mainloop(struct lxc_epoll_descr *descr, int timeout_ms)
+{
+    int ret;
+
+    ret = lxc_mainloop(descr, timeout_ms);
+
+    // There are stdout and stderr channels, and two epolls should be performed to prevent
+    // one of the channels from exiting first, causing the other channel to not receive data,
+    // resulting in data loss
+    (void)lxc_mainloop(descr, 100);
+
+    return ret;
+}
+#endif
diff --git a/src/lxc/path.c b/src/lxc/path.c
new file mode 100644
index 0000000..46256cb
--- /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);
+}
\ No newline at end of file
diff --git a/src/lxc/storage/dir.c b/src/lxc/storage/dir.c
index 18a10a4..dc642a4 100644
--- a/src/lxc/storage/dir.c
+++ b/src/lxc/storage/dir.c
@@ -94,6 +94,9 @@ int dir_create(struct lxc_storage *bdev, const char *dest, const char *n,
 
 int dir_destroy(struct lxc_storage *orig)
 {
+#ifdef HAVE_ISULAD
+	// isulad: do not destroy rootfs for directory, it should be managed by caller
+#else
 	int ret;
 	const char *src;
 
@@ -102,6 +105,7 @@ int dir_destroy(struct lxc_storage *orig)
 	ret = lxc_rmdir_onedev(src, NULL);
 	if (ret < 0)
 		return log_error_errno(ret, errno, "Failed to delete \"%s\"", src);
+#endif
 
 	return 0;
 }
@@ -124,6 +128,35 @@ bool dir_detect(const char *path)
 	return false;
 }
 
+#ifdef HAVE_ISULAD
+int dir_mount(struct lxc_storage *bdev)
+{
+	__do_free char *mntdata = NULL;
+	unsigned long mntflags = 0, pflags = 0;
+	int ret;
+	const char *src;
+
+	if (strcmp(bdev->type, "dir"))
+		return -22;
+
+	if (!bdev->src || !bdev->dest)
+		return -22;
+
+	ret = parse_mntopts(bdev->mntopts, &mntflags, &pflags, &mntdata);
+	if (ret < 0)
+		return log_error_errno(ret, errno, "Failed to parse mount options \"%s\"", bdev->mntopts);
+
+	src = lxc_storage_get_path(bdev->src, bdev->type);
+
+	ret = mount(src, bdev->dest, "bind", MS_BIND | MS_REC | (mntflags & ~MS_RDONLY) | pflags, mntdata);
+	if (ret < 0) {
+		return log_error_errno(-errno, errno, "Failed to mount \"%s\" on \"%s\"", src, bdev->dest);
+	}
+	TRACE("Mounted \"%s\" on \"%s\"", src, bdev->dest);
+
+	return 0;
+}
+#else
 int dir_mount(struct lxc_storage *bdev)
 {
 	__do_free char *mntdata = NULL;
@@ -166,6 +199,7 @@ int dir_mount(struct lxc_storage *bdev)
 	      src ? src : "(none)", bdev->dest ? bdev->dest : "(none)", mntdata, mflags, pflags);
 	return 0;
 }
+#endif
 
 int dir_umount(struct lxc_storage *bdev)
 {
diff --git a/src/lxc/storage/loop.c b/src/lxc/storage/loop.c
index eebc1b6..3a97c1d 100644
--- a/src/lxc/storage/loop.c
+++ b/src/lxc/storage/loop.c
@@ -21,6 +21,9 @@
 #include "memory_utils.h"
 #include "storage.h"
 #include "storage_utils.h"
+#ifdef HAVE_ISULAD
+#include "lxclock.h"
+#endif
 #include "utils.h"
 
 lxc_log_define(loop, lxc);
@@ -216,7 +219,13 @@ bool loop_detect(const char *path)
 
 int loop_mount(struct lxc_storage *bdev)
 {
+#ifdef HAVE_ISULAD
+	int ret = 0;
+	int loopfd, lret;
+	struct lxc_lock *l = NULL;
+#else
 	int ret, loopfd;
+#endif
 	char loname[PATH_MAX];
 	const char *src;
 
@@ -226,13 +235,35 @@ int loop_mount(struct lxc_storage *bdev)
 	if (!bdev->src || !bdev->dest)
 		return -22;
 
+#ifdef HAVE_ISULAD
+	/* isulad: do lock before mount, so we can avoid use loop which is used by
+	 * other starting contianers */
+	l = lxc_newlock("mount_lock", "mount_lock");
+	if (!l) {
+		SYSERROR("create file lock error when mount fs");
+		return -1;
+	}
+
+	lret = lxclock(l, 0);
+	if (lret) {
+		SYSERROR("try to lock failed when mount fs");
+		ret = -1;
+		goto out;
+	}
+#endif
+
 	/* skip prefix */
 	src = lxc_storage_get_path(bdev->src, bdev->type);
 
 	loopfd = lxc_prepare_loop_dev(src, loname, LO_FLAGS_AUTOCLEAR);
 	if (loopfd < 0) {
 		ERROR("Failed to prepare loop device for loop file \"%s\"", src);
+#ifdef HAVE_ISULAD
+		ret = -1;
+		goto out;
+#else
 		return -1;
+#endif
 	}
 	DEBUG("Prepared loop device \"%s\"", loname);
 
@@ -241,14 +272,29 @@ int loop_mount(struct lxc_storage *bdev)
 		ERROR("Failed to mount rootfs \"%s\" on \"%s\" via loop device \"%s\"",
 		      bdev->src, bdev->dest, loname);
 		close(loopfd);
+#ifdef HAVE_ISULAD
+		ret = -1;
+		goto out;
+#else
 		return -1;
+#endif
 	}
 
 	bdev->lofd = loopfd;
 	DEBUG("Mounted rootfs \"%s\" on \"%s\" via loop device \"%s\"",
 	      bdev->src, bdev->dest, loname);
-
+#ifdef HAVE_ISULAD
+out:
+	lret = lxcunlock(l);
+	if (lret) {
+		SYSERROR("try to unlock failed when mount fs");
+		ret = -1;
+	}
+	lxc_putlock(l);
+	return ret;
+#else
 	return 0;
+#endif
 }
 
 int loop_umount(struct lxc_storage *bdev)
diff --git a/src/lxc/storage/storage.c b/src/lxc/storage/storage.c
index 3f1b713..371513d 100644
--- a/src/lxc/storage/storage.c
+++ b/src/lxc/storage/storage.c
@@ -41,6 +41,9 @@
 #include "storage_utils.h"
 #include "utils.h"
 #include "zfs.h"
+#ifdef HAVE_ISULAD
+#include "block.h"
+#endif
 
 #ifndef HAVE_STRLCPY
 #include "include/strlcpy.h"
@@ -94,6 +97,22 @@ static const struct lxc_storage_ops loop_ops = {
     .can_backup = true,
 };
 
+#ifdef HAVE_ISULAD
+/* block */
+static const struct lxc_storage_ops blk_ops = {
+    .detect = &blk_detect,
+    .mount = &blk_mount,
+    .umount = &blk_umount,
+    .clone_paths = NULL,
+    .destroy = &blk_destroy,
+    .create = NULL,
+    .copy = NULL,
+    .snapshot = NULL,
+    .can_snapshot = false,
+    .can_backup = true,
+};
+#endif
+
 /* lvm */
 static const struct lxc_storage_ops lvm_ops = {
     .detect = &lvm_detect,
@@ -179,6 +198,10 @@ static const struct lxc_storage_type bdevs[] = {
 	{ .name = "overlayfs", .ops = &ovl_ops,   },
 	{ .name = "loop",      .ops = &loop_ops,  },
 	{ .name = "nbd",       .ops = &nbd_ops,   },
+#ifdef HAVE_ISULAD
+	//isulad: block device
+	{ .name = "blk",       .ops = &blk_ops,   }
+#endif
 };
 
 static const size_t numbdevs = sizeof(bdevs) / sizeof(struct lxc_storage_type);
@@ -570,9 +593,15 @@ bool storage_destroy(struct lxc_conf *conf)
 	int destroy_rv = 0;
 
 	r = storage_init(conf);
+#ifdef HAVE_ISULAD
+	if (r == NULL) {
+		WARN("%s 's storage init failed, the storage may be deleted already", conf->name);
+		return true;
+	}
+#else
 	if (!r)
 		return ret;
-
+#endif
 	destroy_rv = r->ops->destroy(r);
 	if (destroy_rv == 0)
 		ret = true;
-- 
2.25.1

