From e7f83b7c8b21942051e86e094d867cc79ff93524 Mon Sep 17 00:00:00 2001 From: tanyifeng Date: Tue, 15 Jan 2019 19:54:13 +0800 Subject: [PATCH 033/122] support mount squashfs in mount entry Signed-off-by: LiFeng --- src/lxc/conf.c | 88 +++++++++++++++++++++++++++++++++++++++-- src/lxc/storage/loop.c | 36 ++++++++++++++--- src/lxc/storage/storage_utils.c | 36 ++++++++++++++++- src/lxc/utils.c | 33 ++++++++++++++-- src/lxc/utils.h | 1 + 5 files changed, 181 insertions(+), 13 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 54b967b..fea0f59 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -78,6 +78,7 @@ #include "storage/overlay.h" #include "syscall_wrappers.h" #include "terminal.h" +#include "loop.h" #include "path.h" #include "utils.h" @@ -2444,6 +2445,82 @@ static int mount_entry_create_dir_file(const struct mntent *mntent, return 0; } +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/ + 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; +} + /* 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, @@ -2502,8 +2579,14 @@ static inline int mount_entry_on_generic(struct mntent *mntent, return -1; } - ret = mount_entry(mntent->mnt_fsname, dest, mntent->mnt_type, mntflags, - pflags, mntdata, optional, dev, relative, rootfs_path); + // 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 { + ret = mount_entry(mntent->mnt_fsname, dest, mntent->mnt_type, mntflags, + pflags, mntdata, optional, dev, relative, rootfs_path); + } free(mntdata); free(rpath); @@ -3897,7 +3980,6 @@ static int setup_rootfs_mountopts(const struct lxc_rootfs *rootfs) return 0; } - int lxc_setup(struct lxc_handler *handler) { int ret; diff --git a/src/lxc/storage/loop.c b/src/lxc/storage/loop.c index 35cb13e..760def8 100644 --- a/src/lxc/storage/loop.c +++ b/src/lxc/storage/loop.c @@ -41,6 +41,7 @@ #include "loop.h" #include "storage.h" #include "storage_utils.h" +#include "lxclock.h" #include "utils.h" lxc_log_define(loop, lxc); @@ -236,9 +237,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; @@ -246,13 +249,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); @@ -261,14 +280,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 b4dcb57..0a87778 100644 --- a/src/lxc/storage/storage_utils.c +++ b/src/lxc/storage/storage_utils.c @@ -339,10 +339,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; @@ -371,15 +375,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; } @@ -399,6 +418,8 @@ int find_fstype_cb(char *buffer, void *data) unsigned long mntflags, pflags; char *mntdata; char *fstype; + char mount_err[BUFSIZ] = {0}; + int ret; /* we don't try 'nodev' entries */ if (strstr(buffer, "nodev")) @@ -419,6 +440,17 @@ int find_fstype_cb(char *buffer, void *data) if (mount(cbarg->rootfs, cbarg->target, fstype, (mntflags & ~MS_RDONLY), mntdata)) { 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 d1a22f7..120a13d 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -1053,7 +1053,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; @@ -1473,6 +1473,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) { @@ -1489,10 +1492,34 @@ 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 (fd_tmp < 0) { + /* 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; + } on_error: close(fd_ctl); diff --git a/src/lxc/utils.h b/src/lxc/utils.h index abc88ca..4313942 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -224,6 +224,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