mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-12 20:32:21 +00:00
utils: use loop device helpers from LXD
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>
This commit is contained in:
parent
d435aae15b
commit
c6868a1f81
@ -35,19 +35,9 @@
|
|||||||
#include "lxcloop.h"
|
#include "lxcloop.h"
|
||||||
#include "utils.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);
|
lxc_log_define(lxcloop, lxc);
|
||||||
|
|
||||||
static int do_loop_create(const char *path, uint64_t size, const char *fstype);
|
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
|
* 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 loop_mount(struct bdev *bdev)
|
||||||
{
|
{
|
||||||
int lfd, ffd = -1, ret = -1;
|
int ret, loopfd;
|
||||||
struct loop_info64 lo;
|
char loname[MAXPATHLEN];
|
||||||
char loname[100];
|
|
||||||
|
|
||||||
if (strcmp(bdev->type, "loop"))
|
if (strcmp(bdev->type, "loop"))
|
||||||
return -22;
|
return -22;
|
||||||
if (!bdev->src || !bdev->dest)
|
if (!bdev->src || !bdev->dest)
|
||||||
return -22;
|
return -22;
|
||||||
if (find_free_loopdev(&lfd, loname) < 0)
|
|
||||||
return -22;
|
|
||||||
|
|
||||||
ffd = open(bdev->src + 5, O_RDWR);
|
loopfd = lxc_prepare_loop_dev(bdev->src + 5, loname, LO_FLAGS_AUTOCLEAR);
|
||||||
if (ffd < 0) {
|
if (loopfd < 0)
|
||||||
SYSERROR("Error opening backing file %s", bdev->src);
|
return -1;
|
||||||
goto out;
|
DEBUG("prepared loop device \"%s\"", loname);
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mount_unknown_fs(loname, bdev->dest, bdev->mntopts);
|
ret = mount_unknown_fs(loname, bdev->dest, bdev->mntopts);
|
||||||
if (ret < 0)
|
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
|
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 ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,63 +235,3 @@ static int do_loop_create(const char *path, uint64_t size, const char *fstype)
|
|||||||
|
|
||||||
return 0;
|
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;
|
|
||||||
}
|
|
||||||
|
@ -590,95 +590,21 @@ static int mount_rootfs_dir(const char *rootfs, const char *target,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lxc_setup_lodev(const char *rootfs, int fd, struct loop_info64 *loinfo)
|
static int lxc_mount_rootfs_file(const char *rootfs, const char *target,
|
||||||
{
|
|
||||||
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,
|
|
||||||
const char *options)
|
const char *options)
|
||||||
{
|
{
|
||||||
struct dirent *direntp;
|
int ret, loopfd;
|
||||||
struct loop_info64 loinfo;
|
|
||||||
DIR *dir;
|
|
||||||
char path[MAXPATHLEN];
|
char path[MAXPATHLEN];
|
||||||
int ret = -1, fd = -1, rc;
|
|
||||||
|
|
||||||
dir = opendir("/dev");
|
loopfd = lxc_prepare_loop_dev(rootfs, path, LO_FLAGS_AUTOCLEAR);
|
||||||
if (!dir) {
|
if (loopfd < 0)
|
||||||
SYSERROR("failed to open '/dev'");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
DEBUG("prepared loop device \"%s\"", path);
|
||||||
|
|
||||||
while ((direntp = readdir(dir))) {
|
|
||||||
|
|
||||||
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);
|
ret = mount_unknown_fs(path, target, options);
|
||||||
close(fd);
|
close(loopfd);
|
||||||
|
|
||||||
break;
|
DEBUG("mounted rootfs \"%s\" on loop device \"%s\" via loop device \"%s\"", rootfs, target, path);
|
||||||
}
|
|
||||||
|
|
||||||
if (closedir(dir))
|
|
||||||
WARN("failed to close directory");
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -911,7 +837,7 @@ static int mount_rootfs(const char *rootfs, const char *target, const char *opti
|
|||||||
} rtfs_type[] = {
|
} rtfs_type[] = {
|
||||||
{ S_IFDIR, mount_rootfs_dir },
|
{ S_IFDIR, mount_rootfs_dir },
|
||||||
{ S_IFBLK, mount_rootfs_block },
|
{ S_IFBLK, mount_rootfs_block },
|
||||||
{ S_IFREG, mount_rootfs_file },
|
{ S_IFREG, lxc_mount_rootfs_file },
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!realpath(rootfs, absrootfs)) {
|
if (!realpath(rootfs, absrootfs)) {
|
||||||
|
123
src/lxc/utils.c
123
src/lxc/utils.c
@ -2070,3 +2070,126 @@ int lxc_setgroups(int size, gid_t list[])
|
|||||||
|
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
@ -23,15 +23,19 @@
|
|||||||
#ifndef __LXC_UTILS_H
|
#ifndef __LXC_UTILS_H
|
||||||
#define __LXC_UTILS_H
|
#define __LXC_UTILS_H
|
||||||
|
|
||||||
|
/* Properly support loop devices on 32bit systems. */
|
||||||
|
#define _FILE_OFFSET_BITS 64
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/loop.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "initutils.h"
|
#include "initutils.h"
|
||||||
|
|
||||||
@ -164,6 +168,15 @@ static inline int signalfd(int fd, const sigset_t *mask, int flags)
|
|||||||
}
|
}
|
||||||
#endif
|
#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().
|
/* Struct to carry child pid from lxc_popen() to lxc_pclose().
|
||||||
* Not an opaque struct to allow direct access to the underlying FILE *
|
* 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))
|
* (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_switch_uid_gid(uid_t uid, gid_t gid);
|
||||||
int lxc_setgroups(int size, gid_t list[]);
|
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 */
|
#endif /* __LXC_UTILS_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user