]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
utils: use loop device helpers from LXD 1520/head
authorChristian Brauner <christian.brauner@ubuntu.com>
Sat, 22 Apr 2017 12:01:59 +0000 (14:01 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Sat, 22 Apr 2017 12:04:34 +0000 (14:04 +0200)
Use the loop device helpers I wrote for LXD in LXC as well. They should be more
efficient.

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/bdev/lxcloop.c
src/lxc/conf.c
src/lxc/utils.c
src/lxc/utils.h

index b322002df5c0c5ccc854532330522de515cd7c1c..a4633e405e89c431f80c81abd56f0fa53175a084 100644 (file)
 #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;
-}
index 5f5d34c1e8068ddc5ed7f09c5f8af72fe9ae5477..fc7fb183c7385ae08edcea403f7a62ea9c5b1f93 100644 (file)
@@ -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;
-       }
-
-       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;
 }
@@ -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)) {
index 1154d415ab36f0bc1154981a7e9c5907223bc729..d83e294fae12b74361e31e1ad8d03be705c2b26a 100644 (file)
@@ -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;
+}
index 19caa6dad936503c0c174c8f37d506f0cd01a389..b6fc7c5fa7672f5fdb13b362388ed5858f7c83fe 100644 (file)
 #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"
 
@@ -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 */