diff --git a/src/lxc/bdev/lxcloop.c b/src/lxc/bdev/lxcloop.c index b322002df..a4633e405 100644 --- a/src/lxc/bdev/lxcloop.c +++ b/src/lxc/bdev/lxcloop.c @@ -35,19 +35,9 @@ #include "lxcloop.h" #include "utils.h" -#ifndef LO_FLAGS_AUTOCLEAR -#define LO_FLAGS_AUTOCLEAR 4 -#endif - -#ifndef LOOP_CTL_GET_FREE -#define LOOP_CTL_GET_FREE 0x4C82 -#endif - lxc_log_define(lxcloop, lxc); static int do_loop_create(const char *path, uint64_t size, const char *fstype); -static int find_free_loopdev_no_control(int *retfd, char *namep); -static int find_free_loopdev(int *retfd, char *namep); /* * No idea what the original blockdev will be called, but the copy will be @@ -174,47 +164,26 @@ int loop_detect(const char *path) int loop_mount(struct bdev *bdev) { - int lfd, ffd = -1, ret = -1; - struct loop_info64 lo; - char loname[100]; + int ret, loopfd; + char loname[MAXPATHLEN]; if (strcmp(bdev->type, "loop")) return -22; if (!bdev->src || !bdev->dest) return -22; - if (find_free_loopdev(&lfd, loname) < 0) - return -22; - ffd = open(bdev->src + 5, O_RDWR); - if (ffd < 0) { - SYSERROR("Error opening backing file %s", bdev->src); - goto out; - } - - if (ioctl(lfd, LOOP_SET_FD, ffd) < 0) { - SYSERROR("Error attaching backing file to loop dev"); - goto out; - } - memset(&lo, 0, sizeof(lo)); - lo.lo_flags = LO_FLAGS_AUTOCLEAR; - if (ioctl(lfd, LOOP_SET_STATUS64, &lo) < 0) { - SYSERROR("Error setting autoclear on loop dev"); - goto out; - } + loopfd = lxc_prepare_loop_dev(bdev->src + 5, loname, LO_FLAGS_AUTOCLEAR); + if (loopfd < 0) + return -1; + DEBUG("prepared loop device \"%s\"", loname); ret = mount_unknown_fs(loname, bdev->dest, bdev->mntopts); if (ret < 0) - ERROR("Error mounting %s", bdev->src); + ERROR("failed to mount rootfs \"%s\" onto \"%s\" via loop device \"%s\"", bdev->src, bdev->dest, loname); else - bdev->lofd = lfd; + bdev->lofd = loopfd; + DEBUG("mounted rootfs \"%s\" onto \"%s\" via loop device \"%s\"", bdev->src, bdev->dest, loname); -out: - if (ffd > -1) - close(ffd); - if (ret < 0) { - close(lfd); - bdev->lofd = -1; - } return ret; } @@ -266,63 +235,3 @@ static int do_loop_create(const char *path, uint64_t size, const char *fstype) return 0; } - -static int find_free_loopdev_no_control(int *retfd, char *namep) -{ - struct dirent *direntp; - struct loop_info64 lo; - DIR *dir; - int fd = -1; - - dir = opendir("/dev"); - if (!dir) { - SYSERROR("Error opening /dev"); - return -1; - } - while ((direntp = readdir(dir))) { - - if (!direntp) - break; - if (strncmp(direntp->d_name, "loop", 4) != 0) - continue; - fd = openat(dirfd(dir), direntp->d_name, O_RDWR); - if (fd < 0) - continue; - if (ioctl(fd, LOOP_GET_STATUS64, &lo) == 0 || errno != ENXIO) { - close(fd); - fd = -1; - continue; - } - // We can use this fd - snprintf(namep, 100, "/dev/%s", direntp->d_name); - break; - } - closedir(dir); - if (fd == -1) { - ERROR("No loop device found"); - return -1; - } - - *retfd = fd; - return 0; -} - -static int find_free_loopdev(int *retfd, char *namep) -{ - int rc, fd = -1; - int ctl = open("/dev/loop-control", O_RDWR); - if (ctl < 0) - return find_free_loopdev_no_control(retfd, namep); - rc = ioctl(ctl, LOOP_CTL_GET_FREE); - if (rc >= 0) { - snprintf(namep, 100, "/dev/loop%d", rc); - fd = open(namep, O_RDWR); - } - close(ctl); - if (fd == -1) { - ERROR("No loop device found"); - return -1; - } - *retfd = fd; - return 0; -} diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 5f5d34c1e..fc7fb183c 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -590,95 +590,21 @@ static int mount_rootfs_dir(const char *rootfs, const char *target, return ret; } -static int lxc_setup_lodev(const char *rootfs, int fd, struct loop_info64 *loinfo) -{ - int rfd; - - rfd = open(rootfs, O_RDWR); - if (rfd < 0) { - SYSERROR("failed to open '%s'", rootfs); - return -1; - } - - memset(loinfo, 0, sizeof(*loinfo)); - - if (ioctl(fd, LOOP_SET_FD, rfd)) { - SYSERROR("failed to LOOP_SET_FD"); - close(rfd); - return -1; - } - - loinfo->lo_flags = LO_FLAGS_AUTOCLEAR; - if (ioctl(fd, LOOP_SET_STATUS64, loinfo)) { - SYSERROR("failed to LOOP_SET_STATUS64"); - close(rfd); - return -1; - } - - return 0; -} - -static int mount_rootfs_file(const char *rootfs, const char *target, +static int lxc_mount_rootfs_file(const char *rootfs, const char *target, const char *options) { - struct dirent *direntp; - struct loop_info64 loinfo; - DIR *dir; + int ret, loopfd; char path[MAXPATHLEN]; - int ret = -1, fd = -1, rc; - dir = opendir("/dev"); - if (!dir) { - SYSERROR("failed to open '/dev'"); + loopfd = lxc_prepare_loop_dev(rootfs, path, LO_FLAGS_AUTOCLEAR); + if (loopfd < 0) return -1; - } + DEBUG("prepared loop device \"%s\"", path); - while ((direntp = readdir(dir))) { + ret = mount_unknown_fs(path, target, options); + close(loopfd); - if (!direntp) - break; - - if (!strcmp(direntp->d_name, ".")) - continue; - - if (!strcmp(direntp->d_name, "..")) - continue; - - if (strncmp(direntp->d_name, "loop", 4)) - continue; - - rc = snprintf(path, MAXPATHLEN, "/dev/%s", direntp->d_name); - if (rc < 0 || rc >= MAXPATHLEN) - continue; - - fd = open(path, O_RDWR); - if (fd < 0) - continue; - - if (ioctl(fd, LOOP_GET_STATUS64, &loinfo) == 0) { - close(fd); - continue; - } - - if (errno != ENXIO) { - WARN("unexpected error for ioctl on '%s': %m", - direntp->d_name); - close(fd); - continue; - } - - DEBUG("found '%s' free lodev", path); - - ret = lxc_setup_lodev(rootfs, fd, &loinfo); - if (!ret) - ret = mount_unknown_fs(path, target, options); - close(fd); - - break; - } - - if (closedir(dir)) - WARN("failed to close directory"); + DEBUG("mounted rootfs \"%s\" on loop device \"%s\" via loop device \"%s\"", rootfs, target, path); return ret; } @@ -911,7 +837,7 @@ static int mount_rootfs(const char *rootfs, const char *target, const char *opti } rtfs_type[] = { { S_IFDIR, mount_rootfs_dir }, { S_IFBLK, mount_rootfs_block }, - { S_IFREG, mount_rootfs_file }, + { S_IFREG, lxc_mount_rootfs_file }, }; if (!realpath(rootfs, absrootfs)) { diff --git a/src/lxc/utils.c b/src/lxc/utils.c index 1154d415a..d83e294fa 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -2070,3 +2070,126 @@ int lxc_setgroups(int size, gid_t list[]) return 0; } + +static int lxc_get_unused_loop_dev_legacy(char *loop_name) +{ + struct dirent *dp; + struct loop_info64 lo64; + DIR *dir; + int dfd = -1, fd = -1, ret = -1; + + dir = opendir("/dev"); + if (!dir) + return -1; + + while ((dp = readdir(dir))) { + if (!dp) + break; + + if (strncmp(dp->d_name, "loop", 4) != 0) + continue; + + dfd = dirfd(dir); + if (dfd < 0) + continue; + + fd = openat(dfd, dp->d_name, O_RDWR); + if (fd < 0) + continue; + + ret = ioctl(fd, LOOP_GET_STATUS64, &lo64); + if (ret < 0) { + if (ioctl(fd, LOOP_GET_STATUS64, &lo64) == 0 || + errno != ENXIO) { + close(fd); + fd = -1; + continue; + } + } + + ret = snprintf(loop_name, LO_NAME_SIZE, "/dev/%s", dp->d_name); + if (ret < 0 || ret >= LO_NAME_SIZE) { + close(fd); + fd = -1; + continue; + } + + break; + } + + closedir(dir); + + if (fd < 0) + return -1; + + return fd; +} + +static int lxc_get_unused_loop_dev(char *name_loop) +{ + int loop_nr, ret; + int fd_ctl = -1, fd_tmp = -1; + + fd_ctl = open("/dev/loop-control", O_RDWR | O_CLOEXEC); + if (fd_ctl < 0) + return -ENODEV; + + loop_nr = ioctl(fd_ctl, LOOP_CTL_GET_FREE); + if (loop_nr < 0) + goto on_error; + + ret = snprintf(name_loop, LO_NAME_SIZE, "/dev/loop%d", loop_nr); + if (ret < 0 || ret >= LO_NAME_SIZE) + goto on_error; + + fd_tmp = open(name_loop, O_RDWR | O_CLOEXEC); + if (fd_tmp < 0) + goto on_error; + +on_error: + close(fd_ctl); + return fd_tmp; +} + +int lxc_prepare_loop_dev(const char *source, char *loop_dev, int flags) +{ + int ret; + struct loop_info64 lo64; + int fd_img = -1, fret = -1, fd_loop = -1; + + fd_loop = lxc_get_unused_loop_dev(loop_dev); + if (fd_loop < 0) { + if (fd_loop == -ENODEV) + fd_loop = lxc_get_unused_loop_dev_legacy(loop_dev); + else + goto on_error; + } + + fd_img = open(source, O_RDWR | O_CLOEXEC); + if (fd_img < 0) + goto on_error; + + ret = ioctl(fd_loop, LOOP_SET_FD, fd_img); + if (ret < 0) + goto on_error; + + memset(&lo64, 0, sizeof(lo64)); + lo64.lo_flags = flags; + + ret = ioctl(fd_loop, LOOP_SET_STATUS64, &lo64); + if (ret < 0) + goto on_error; + + fret = 0; + +on_error: + if (fd_img >= 0) + close(fd_img); + + if (fret < 0 && fd_loop >= 0) { + close(fd_loop); + fd_loop = -1; + } + + return fd_loop; +} diff --git a/src/lxc/utils.h b/src/lxc/utils.h index 19caa6dad..b6fc7c5fa 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -23,15 +23,19 @@ #ifndef __LXC_UTILS_H #define __LXC_UTILS_H +/* Properly support loop devices on 32bit systems. */ +#define _FILE_OFFSET_BITS 64 + #include "config.h" #include #include #include #include +#include +#include #include #include -#include #include "initutils.h" @@ -164,6 +168,15 @@ static inline int signalfd(int fd, const sigset_t *mask, int flags) } #endif +/* loop devices */ +#ifndef LO_FLAGS_AUTOCLEAR +#define LO_FLAGS_AUTOCLEAR 4 +#endif + +#ifndef LOOP_CTL_GET_FREE +#define LOOP_CTL_GET_FREE 0x4C82 +#endif + /* Struct to carry child pid from lxc_popen() to lxc_pclose(). * Not an opaque struct to allow direct access to the underlying FILE * * (i.e., struct lxc_popen_FILE *file; fgets(buf, sizeof(buf), file->f)) @@ -332,4 +345,7 @@ int lxc_safe_long(const char *numstr, long int *converted); int lxc_switch_uid_gid(uid_t uid, gid_t gid); int lxc_setgroups(int size, gid_t list[]); +/* Find an unused loop device and associate it with source. */ +int lxc_prepare_loop_dev(const char *source, char *loop_dev, int flags); + #endif /* __LXC_UTILS_H */