From 6124835dde5abfeeb8ac796813f2f18803b96117 Mon Sep 17 00:00:00 2001
From: tanyifeng <tanyifeng1@huawei.com>
Date: Tue, 15 Jan 2019 19:54:13 +0800
Subject: [PATCH 09/49] support mount squashfs in mount entry

Signed-off-by: LiFeng <lifeng68@huawei.com>
Signed-off-by: WangFengTu <wangfengtu@huawei.com>
---
 src/lxc/conf.c                  | 95 +++++++++++++++++++++++++++++++++++++++--
 src/lxc/storage/loop.c          | 36 +++++++++++++---
 src/lxc/storage/storage_utils.c | 36 +++++++++++++++-
 src/lxc/utils.c                 | 35 ++++++++++++++-
 src/lxc/utils.h                 |  1 +
 5 files changed, 191 insertions(+), 12 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 35488e0..1f779b9 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -58,6 +58,7 @@
 #include "storage/overlay.h"
 #include "syscall_wrappers.h"
 #include "terminal.h"
+#include "loop.h"
 #include "utils.h"
 #include "uuid.h"
 
@@ -2013,6 +2014,84 @@ static int mount_entry_create_dir_file(const struct mntent *mntent,
 	return 0;
 }
 
+#ifdef HAVE_ISULAD
+static int mount_entry_with_loop_dev(const char *src, const char *dest, const char *fstype,
+		char *mnt_opts, const char *rootfs)
+{
+	int srcfd = -1, destfd, ret, saved_errno;
+	char srcbuf[50], destbuf[50]; // only needs enough for /proc/self/fd/<fd>
+	const char *mntsrc = src;
+	int max_retry = 5;
+	struct lxc_storage loop;
+
+	if (!rootfs)
+		rootfs = "";
+
+	/* todo - allow symlinks for relative paths if 'allowsymlinks' option is passed */
+	if (src && src[0] != '/') {
+		INFO("this is a relative mount");
+		srcfd = open_without_symlink(src, NULL);
+		if (srcfd < 0)
+			return srcfd;
+		ret = snprintf(srcbuf, sizeof(srcbuf), "/proc/self/fd/%d", srcfd);
+		if (ret < 0 || ret > sizeof(srcbuf)) {
+			close(srcfd);
+			ERROR("Failed to print string");
+			return -EINVAL;
+		}
+		mntsrc = srcbuf;
+	}
+
+	destfd = open_without_symlink(dest, rootfs);
+	if (destfd < 0) {
+		if (srcfd != -1) {
+			saved_errno = errno;
+			close(srcfd);
+			errno = saved_errno;
+		}
+		return destfd;
+	}
+
+	ret = snprintf(destbuf, sizeof(destbuf), "/proc/self/fd/%d", destfd);
+	if (ret < 0 || ret > sizeof(destbuf)) {
+		if (srcfd != -1)
+			close(srcfd);
+		close(destfd);
+		ERROR("Out of memory");
+		return -EINVAL;
+	}
+
+retry:
+	loop.src = (char *)mntsrc;
+	loop.dest = destbuf;
+	loop.mntopts = mnt_opts;
+	loop.type = "loop";
+	loop.lofd = -1;
+	ret = loop_mount(&loop);
+	if (ret < 0) {
+		/* If loop is used by other program, mount may fail. So
+		 * we do retry to ensure mount ok */
+		if (max_retry > 0) {
+			max_retry--;
+			DEBUG("mount entry with loop dev failed, retry mount."
+			      "retry count left %d", max_retry);
+			goto retry;
+		}
+	}
+	if (loop.lofd != -1)
+		close(loop.lofd);
+	if (srcfd != -1)
+		close(srcfd);
+	close(destfd);
+	if (ret < 0) {
+		SYSERROR("Failed to mount %s onto %s", src, dest);
+		return ret;
+	}
+
+	return 0;
+}
+#endif
+
 /* rootfs, lxc_name, and lxc_path can be NULL when the container is created
  * without a rootfs. */
 static inline int mount_entry_on_generic(struct mntent *mntent,
@@ -2026,6 +2105,7 @@ static inline int mount_entry_on_generic(struct mntent *mntent,
 	char *rootfs_path = NULL;
 	int ret;
 	bool dev, optional, relative;
+        const char *dest = path;
 
 	optional = hasmntopt(mntent, "optional") != NULL;
 	dev = hasmntopt(mntent, "dev") != NULL;
@@ -2052,9 +2132,18 @@ static inline int mount_entry_on_generic(struct mntent *mntent,
 	if (ret < 0)
 		return ret;
 
-	ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type, mntflags,
-			  pflags, mntdata, optional, dev, relative, rootfs_path);
-
+#ifdef HAVE_ISULAD
+	// isulad: support squashfs
+	if (strcmp(mntent->mnt_type, "squashfs") == 0) {
+		ret = mount_entry_with_loop_dev(mntent->mnt_fsname, dest, mntent->mnt_type,
+						mntent->mnt_opts, rootfs_path);
+        } else {
+#endif
+	        ret = mount_entry(mntent->mnt_fsname, dest, mntent->mnt_type, mntflags,
+			          pflags, mntdata, optional, dev, relative, rootfs_path);
+#ifdef HAVE_ISULAD
+        }
+#endif
 	return ret;
 }
 
diff --git a/src/lxc/storage/loop.c b/src/lxc/storage/loop.c
index eebc1b6..345be50 100644
--- a/src/lxc/storage/loop.c
+++ b/src/lxc/storage/loop.c
@@ -21,6 +21,7 @@
 #include "memory_utils.h"
 #include "storage.h"
 #include "storage_utils.h"
+#include "lxclock.h"
 #include "utils.h"
 
 lxc_log_define(loop, lxc);
@@ -216,9 +217,11 @@ bool loop_detect(const char *path)
 
 int loop_mount(struct lxc_storage *bdev)
 {
-	int ret, loopfd;
+	int ret = 0;
+	int loopfd, lret;
 	char loname[PATH_MAX];
 	const char *src;
+	struct lxc_lock *l = NULL;
 
 	if (strcmp(bdev->type, "loop"))
 		return -22;
@@ -226,13 +229,29 @@ int loop_mount(struct lxc_storage *bdev)
 	if (!bdev->src || !bdev->dest)
 		return -22;
 
+	/* 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;
+	}
+
 	/* 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);
-		return -1;
+		ret = -1;
+		goto out;
 	}
 	DEBUG("Prepared loop device \"%s\"", loname);
 
@@ -241,14 +260,21 @@ 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);
-		return -1;
+		ret = -1;
+		goto out;
 	}
 
 	bdev->lofd = loopfd;
 	DEBUG("Mounted rootfs \"%s\" on \"%s\" via loop device \"%s\"",
 	      bdev->src, bdev->dest, loname);
-
-	return 0;
+out:
+	lret = lxcunlock(l);
+	if (lret) {
+		SYSERROR("try to unlock failed when mount fs");
+		ret = -1;
+	}
+	lxc_putlock(l);
+	return ret;
 }
 
 int loop_umount(struct lxc_storage *bdev)
diff --git a/src/lxc/storage/storage_utils.c b/src/lxc/storage/storage_utils.c
index bfbb782..07eee22 100644
--- a/src/lxc/storage/storage_utils.c
+++ b/src/lxc/storage/storage_utils.c
@@ -259,10 +259,14 @@ int is_blktype(struct lxc_storage *b)
 	return 0;
 }
 
+// isulad: recored error
+static char **mount_errors = NULL;
+
 int mount_unknown_fs(const char *rootfs, const char *target,
 		     const char *options)
 {
 	size_t i;
+	char *errs = NULL;
 	int ret;
 	struct cbarg {
 		const char *rootfs;
@@ -291,15 +295,30 @@ int mount_unknown_fs(const char *rootfs, const char *target,
 		ret = lxc_file_for_each_line(fsfile[i], find_fstype_cb, &cbarg);
 		if (ret < 0) {
 			ERROR("Failed to parse \"%s\"", fsfile[i]);
+			lxc_free_array((void**)mount_errors, free);
+			mount_errors = NULL;
 			return -1;
 		}
 
-		if (ret)
+		if (ret) {
+			lxc_free_array((void**)mount_errors, free);
+			mount_errors = NULL;
 			return 0;
+		}
+	}
+
+	if (mount_errors != NULL) {
+		errs = lxc_string_join("\n", (const char **)mount_errors, false);
+		if (errs == NULL) {
+			ERROR("failed to join mount errors");
+		}
 	}
 
-	ERROR("Failed to determine FSType for \"%s\"", rootfs);
+	ERROR("Failed to determine FSType for \"%s\": %s", rootfs, errs ? errs : "unknown reason");
 
+	free(errs);
+	lxc_free_array((void**)mount_errors, free);
+	mount_errors = NULL;
 	return -1;
 }
 
@@ -318,6 +337,8 @@ int find_fstype_cb(char *buffer, void *data)
 	unsigned long mntflags = 0;
 	char *mntdata = NULL;
 	char *fstype;
+	char mount_err[BUFSIZ] = {0};
+	int ret;
 
 	/* we don't try 'nodev' entries */
 	if (strstr(buffer, "nodev"))
@@ -342,6 +363,17 @@ int find_fstype_cb(char *buffer, void *data)
 #endif
 		SYSDEBUG("Failed to mount");
 		free(mntdata);
+		// isulad: recored error
+		ret = snprintf(mount_err, BUFSIZ, "\t\tmount %s onto %s with FSType %s failed: %s",
+						cbarg->rootfs, cbarg->target, fstype, strerror(errno));
+		if (ret < 0 || (size_t)ret >= BUFSIZ) {
+			ERROR("failed to format output mount error");
+			return 0;
+		}
+
+		if (lxc_append_string(&mount_errors, mount_err) < 0) {
+			ERROR("failed to append mount error");
+		}
 		return 0;
 	}
 
diff --git a/src/lxc/utils.c b/src/lxc/utils.c
index ebcdae0..90113e0 100644
--- a/src/lxc/utils.c
+++ b/src/lxc/utils.c
@@ -27,6 +27,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <sys/sysmacros.h>
 
 #include "config.h"
 #include "log.h"
@@ -1008,7 +1009,7 @@ static int open_if_safe(int dirfd, const char *nextpath)
  *
  * Return an open fd for the path, or <0 on error.
  */
-static int open_without_symlink(const char *target, const char *prefix_skip)
+int open_without_symlink(const char *target, const char *prefix_skip)
 {
 	int curlen = 0, dirfd, fulllen, i;
 	char *dup;
@@ -1425,6 +1426,9 @@ static int lxc_get_unused_loop_dev(char *name_loop)
 {
 	int loop_nr, ret;
 	int fd_ctl = -1, fd_tmp = -1;
+	// isulad: retry and try mknod
+	int max_retry = 200;
+	bool try_mknod = true;
 
 	fd_ctl = open("/dev/loop-control", O_RDWR | O_CLOEXEC);
 	if (fd_ctl < 0) {
@@ -1441,9 +1445,35 @@ static int lxc_get_unused_loop_dev(char *name_loop)
 	ret = snprintf(name_loop, LO_NAME_SIZE, "/dev/loop%d", loop_nr);
 	if (ret < 0 || ret >= LO_NAME_SIZE)
 		goto on_error;
-
+retry:
 	fd_tmp = open(name_loop, O_RDWR | O_CLOEXEC);
 	if (fd_tmp < 0) {
+#if HAVE_ISULAD
+		/* Success of LOOP_CTL_GET_FREE doesn't mean /dev/loop$i is ready,
+		* we try to make node by ourself to avoid wait. */
+		if (try_mknod) {
+			/* Do not check result of mknod because LOOP_CTL_GET_FREE
+			* alse do mknod, so this mknod may fail as node already
+			* exist. If we can open the node without error, we can
+			* say that it's be created successfully.
+			*
+			* note: 7 is the major device number of loopback devices
+			*	 in kernel.
+			*/
+			mknod(name_loop, S_IFBLK | 0640, makedev(7, loop_nr));
+			try_mknod = false;
+			goto retry;
+		}
+		/* we need to wait some time to make sure it's ready for open if
+		* it can't open even if we have already try to make node by ourself. */
+		if (max_retry > 0) {
+			max_retry--;
+			usleep(5000); /* 5 millisecond */
+			goto retry;
+		}
+		SYSERROR("Failed to open loop \"%s\"", name_loop);
+		goto on_error;
+#else
 		/* on Android loop devices are moved under /dev/block, give it a shot */
 		ret = snprintf(name_loop, LO_NAME_SIZE, "/dev/block/loop%d", loop_nr);
                 if (ret < 0 || ret >= LO_NAME_SIZE)
@@ -1452,6 +1482,7 @@ static int lxc_get_unused_loop_dev(char *name_loop)
 		fd_tmp = open(name_loop, O_RDWR | O_CLOEXEC);
 		if (fd_tmp < 0)
 			SYSERROR("Failed to open loop \"%s\"", name_loop);
+#endif
 	}
 
 on_error:
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index 11d6548..fbb0d55 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -215,6 +215,7 @@ extern bool cgns_supported(void);
 extern char *choose_init(const char *rootfs);
 extern bool switch_to_ns(pid_t pid, const char *ns);
 extern char *get_template_path(const char *t);
+extern int open_without_symlink(const char *target, const char *prefix_skip);
 extern int safe_mount(const char *src, const char *dest, const char *fstype,
 		      unsigned long flags, const void *data,
 		      const char *rootfs);
-- 
1.8.3.1

