#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
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;
}
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;
-}
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;
- }
-
- while ((direntp = readdir(dir))) {
-
- if (!direntp)
- break;
-
- if (!strcmp(direntp->d_name, "."))
- continue;
+ DEBUG("prepared loop device \"%s\"", path);
- 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;
- }
+ ret = mount_unknown_fs(path, target, options);
+ close(loopfd);
- 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;
}
} 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)) {
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;
+}
#ifndef __LXC_UTILS_H
#define __LXC_UTILS_H
+/* Properly support loop devices on 32bit systems. */
+#define _FILE_OFFSET_BITS 64
+
#include "config.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdbool.h>
+#include <unistd.h>
+#include <linux/loop.h>
#include <sys/syscall.h>
#include <sys/types.h>
-#include <unistd.h>
#include "initutils.h"
}
#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))
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 */