[PATCH] losetup/mount: Revise to use /dev/loop-control and thereby fix 'losetup -f'.
Denys Vlasenko
vda.linux at googlemail.com
Sun Jun 9 21:22:18 UTC 2019
Looks too big. Please try current git, it has a smaller version.
Please describe unwanted behavior (which you are fixing) more clearly
On Tue, Mar 5, 2019 at 12:06 PM Nicolas Hüppelshäuser
<nicolas.hueppelshaeuser at emlix.com> wrote:
>
> Fixed 'losetup -f' and 'losetup -f <file>' issue
> =================================================
>
> With FEATURE_MOUNT_LOOP_CREATE:
>
> On a system with mounted devtmpfs /dev there might be less loop nodes in
> existence than required.
>
> (1) If all existing nodes are in use, 'losetup -f' would correctly return
> the next free node, but it would not create that node.
>
> (2) If all existing nodes are in use, 'losetup -f <file>' would also not
> create the required node file.
>
> Why revise to use /dev/loop-control
> ====================================
>
> (1) Because using /dev/loop-control puts the Linux kernel in charge to
> ensure the returned free node is an existing file and busybox does not
> need to create new files below /dev.
>
> (2) Because libbb/loop.c:set_loop() already contained a "to be done"
> note to switch from iterating the existing nodes to using
> /dev/loop-control.
>
> This fix/feature is activated via config option
> FEATURE_MOUNT_LOOP_LOOP_CONTROL (enabled by default).
>
> Signed-off-by: Nicolas Hüppelshäuser <nicolas.hueppelshaeuser at emlix.com>
> ---
> include/libbb.h | 3 +
> libbb/loop.c | 165 +++++++++++++++++++++++++++++++++++-------
> util-linux/Config.src | 14 +++-
> util-linux/losetup.c | 16 +++-
> 4 files changed, 169 insertions(+), 29 deletions(-)
>
> diff --git a/include/libbb.h b/include/libbb.h
> index daa96728b..12b73b7d3 100644
> --- a/include/libbb.h
> +++ b/include/libbb.h
> @@ -1447,6 +1447,9 @@ extern void bb_warn_ignoring_args(char *arg) FAST_FUNC;
> extern int get_linux_version_code(void) FAST_FUNC;
>
> extern char *query_loop(const char *device) FAST_FUNC;
> +#if ENABLE_FEATURE_MOUNT_LOOP_LOOP_CONTROL
> +extern char *get_loop(void) FAST_FUNC;
> +#endif
> extern int del_loop(const char *device) FAST_FUNC;
> /*
> * If *devname is not NULL, use that name, otherwise try to find free one,
> diff --git a/libbb/loop.c b/libbb/loop.c
> index c78535a20..3f7315ca1 100644
> --- a/libbb/loop.c
> +++ b/libbb/loop.c
> @@ -65,6 +65,28 @@ char* FAST_FUNC query_loop(const char *device)
> return dev;
> }
>
> +#if ENABLE_FEATURE_MOUNT_LOOP_LOOP_CONTROL
> +/* Obtain an unused loop device node */
> +char* FAST_FUNC get_loop(void)
> +{
> + int fd;
> + int loopdevno;
> +
> + fd = open("/dev/loop-control", O_RDWR | O_CLOEXEC);
> + if (fd == -1) {
> + return NULL;
> + }
> +
> + loopdevno = ioctl(fd, LOOP_CTL_GET_FREE);
> + close(fd);
> + if (loopdevno == -1) {
> + return NULL;
> + }
> +
> + return xasprintf(LOOP_FORMAT, loopdevno);
> +}
> +#endif
> +
> int FAST_FUNC del_loop(const char *device)
> {
> int fd, rc;
> @@ -78,6 +100,120 @@ int FAST_FUNC del_loop(const char *device)
> return rc;
> }
>
> +/*
> + * Local helper function used by both implementations of set_loop()
> + * to associate a file with a loop device.
> + * loopfd file descriptor of loop device
> + * filefd file descriptor of file to bind to loop device
> + * file file name of filefd
> + * flags flags used to open() file with
> + * offset to be fed into loop_info struct
> + * Returns 0 on success, -1 on error.
> + */
> +static int FAST_FUNC associate_with_loop(int loopfd, int filefd, const char *file, unsigned flags, unsigned long long offset)
> +{
> + bb_loop_info loopinfo;
> + int rc = -1;
> +
> + /* Associate free loop device with file. */
> + if (ioctl(loopfd, LOOP_SET_FD, filefd) == 0) {
> + memset(&loopinfo, 0, sizeof(loopinfo));
> + safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE);
> + loopinfo.lo_offset = offset;
> + /*
> + * Used by mount to set LO_FLAGS_AUTOCLEAR.
> + * LO_FLAGS_READ_ONLY is not set because RO is controlled by open type of the file.
> + * Note that closing LO_FLAGS_AUTOCLEARed loopfd before mount
> + * is wrong (would free the loop device!)
> + */
> + loopinfo.lo_flags = (flags & ~BB_LO_FLAGS_READ_ONLY);
> + rc = ioctl(loopfd, BB_LOOP_SET_STATUS, &loopinfo);
> + if (rc != 0 && (loopinfo.lo_flags & BB_LO_FLAGS_AUTOCLEAR)) {
> + /* Old kernel, does not support LO_FLAGS_AUTOCLEAR? */
> + /* (this code path is not tested) */
> + loopinfo.lo_flags -= BB_LO_FLAGS_AUTOCLEAR;
> + rc = ioctl(loopfd, BB_LOOP_SET_STATUS, &loopinfo);
> + }
> + if (rc != 0) {
> + ioctl(loopfd, LOOP_CLR_FD, 0);
> + }
> + }
> + return rc;
> +}
> +
> +/* two implementations of set_loop() depending on ENABLE_FEATURE_MOUNT_LOOP_LOOP_CONTROL: */
> +#if ENABLE_FEATURE_MOUNT_LOOP_LOOP_CONTROL
> +/* Returns opened fd to the loop device, <0 on error.
> + * *device is loop device to use, or if *device==NULL finds a loop device to
> + * mount it on and sets *device to a strdup of that loop device name.
> + */
> +int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset, unsigned flags)
> +{
> + char *try;
> + bb_loop_info loopinfo;
> + int dfd, ffd, mode, rc;
> +
> + rc = dfd = -1;
> +
> + /* Open the file. Barf if this doesn't work. */
> + mode = (flags & BB_LO_FLAGS_READ_ONLY) ? O_RDONLY : O_RDWR;
> + open_ffd:
> + ffd = open(file, mode);
> + if (ffd < 0) {
> + if (mode != O_RDONLY) {
> + mode = O_RDONLY;
> + goto open_ffd;
> + }
> + return -errno;
> + }
> +
> + /*
> + * If caller did not provide an explicit loop node file name
> + * we obtain a free loop node; otherwise there is no need
> + * to check for existence of the loop node since the Linux
> + * kernel should have provided one.
> + */
> + try = *device;
> + if (try == NULL) {
> + try = get_loop();
> + if (try == NULL) {
> + close(ffd);
> + return -1;
> + }
> + }
> +
> + /* Open the sucker and check its loopiness. */
> + dfd = open(try, mode);
> + if (dfd < 0 && errno == EROFS) {
> + mode = O_RDONLY;
> + dfd = open(try, mode);
> + }
> + if (dfd < 0) {
> + close(ffd);
> + return -1;
> + }
> +
> + /* If device is free, claim it (LOOP_GET_STATUS returns error number ENXIO in this case). */
> + rc = ioctl(dfd, BB_LOOP_GET_STATUS, &loopinfo);
> + if (rc && errno == ENXIO) {
> + rc = associate_with_loop(dfd, ffd, file, flags, offset);
> + } else {
> + rc = -1;
> + }
> +
> + close(ffd);
> + if (rc == 0) {
> + if (!*device)
> + *device = try;
> + return dfd;
> + } else {
> + close(dfd);
> + }
> + return rc;
> +}
> +
> +#else /* if not ENABLE_FEATURE_MOUNT_LOOP_LOOP_CONTROL */
> +
> /* Returns opened fd to the loop device, <0 on error.
> * *device is loop device to use, or if *device==NULL finds a loop device to
> * mount it on and sets *device to a strdup of that loop device name. This
> @@ -106,10 +242,6 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
> return -errno;
> }
>
> -//TODO: use LOOP_CTL_GET_FREE instead of trying every loopN in sequence? a-la:
> -// fd = open("/dev/loop-control", O_RDWR);
> -// loopN = ioctl(fd, LOOP_CTL_GET_FREE);
> -//
> /* Find a loop device. */
> try = *device ? *device : dev;
> /* 1048575 (0xfffff) is a max possible minor number in Linux circa 2010 */
> @@ -150,29 +282,7 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
>
> /* If device is free, claim it. */
> if (rc && errno == ENXIO) {
> - /* Associate free loop device with file. */
> - if (ioctl(dfd, LOOP_SET_FD, ffd) == 0) {
> - memset(&loopinfo, 0, sizeof(loopinfo));
> - safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE);
> - loopinfo.lo_offset = offset;
> - /*
> - * Used by mount to set LO_FLAGS_AUTOCLEAR.
> - * LO_FLAGS_READ_ONLY is not set because RO is controlled by open type of the file.
> - * Note that closing LO_FLAGS_AUTOCLEARed dfd before mount
> - * is wrong (would free the loop device!)
> - */
> - loopinfo.lo_flags = (flags & ~BB_LO_FLAGS_READ_ONLY);
> - rc = ioctl(dfd, BB_LOOP_SET_STATUS, &loopinfo);
> - if (rc != 0 && (loopinfo.lo_flags & BB_LO_FLAGS_AUTOCLEAR)) {
> - /* Old kernel, does not support LO_FLAGS_AUTOCLEAR? */
> - /* (this code path is not tested) */
> - loopinfo.lo_flags -= BB_LO_FLAGS_AUTOCLEAR;
> - rc = ioctl(dfd, BB_LOOP_SET_STATUS, &loopinfo);
> - }
> - if (rc != 0) {
> - ioctl(dfd, LOOP_CLR_FD, 0);
> - }
> - }
> + rc = associate_with_loop(dfd, ffd, file, flags, offset);
> } else {
> rc = -1;
> }
> @@ -190,3 +300,4 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
> }
> return rc;
> }
> +#endif /* if ENABLE_FEATURE_MOUNT_LOOP_LOOP_CONTROL */
> diff --git a/util-linux/Config.src b/util-linux/Config.src
> index 0fad3e5c0..5f5121ccf 100644
> --- a/util-linux/Config.src
> +++ b/util-linux/Config.src
> @@ -27,9 +27,21 @@ config FEATURE_MOUNT_LOOP
> specify an offset or cryptographic options to the loopback device.
> (If you don't want umount to free the loop device, use "umount -D".)
>
> +config FEATURE_MOUNT_LOOP_LOOP_CONTROL
> + bool "Use Linux kernel /dev/loop-control for handling loopback devices"
> + default y
> + depends on FEATURE_MOUNT_LOOP && !FEATURE_MOUNT_LOOP_CREATE
> + help
> + Linux kernels >= 3.1 provide the /dev/loop-control device,
> + which permits an application to dynamically find a free device, and
> + to add and remove loop devices from the system.
> +
> + This feature lets mount (and losetup) use /dev/loop-control to find
> + and add free loop devices.
> +
> config FEATURE_MOUNT_LOOP_CREATE
> bool "Create new loopback devices if needed"
> - default y
> + default n
> depends on FEATURE_MOUNT_LOOP
> help
> Linux kernels >= 2.6.24 support unlimited loopback devices. They are
> diff --git a/util-linux/losetup.c b/util-linux/losetup.c
> index bf480e9bf..8b7124d29 100644
> --- a/util-linux/losetup.c
> +++ b/util-linux/losetup.c
> @@ -99,8 +99,20 @@ int losetup_main(int argc UNUSED_PARAM, char **argv)
> /* contains -f */
> if (opt & OPT_f) {
> char *s;
> - int n = 0;
>
> +#if ENABLE_FEATURE_MOUNT_LOOP_LOOP_CONTROL
> + /* obtain free loop device */
> + s = get_loop();
> + if (s == NULL) {
> + bb_error_msg_and_die("no free loop devices");
> + }
> + if (strlen(s) >= sizeof(dev)) {
> + bb_error_msg_and_die("internal error: LOOP_NAMESIZE too small");
> + }
> + strcpy(dev, s);
> + free(s);
> +#else /* if not ENABLE_FEATURE_MOUNT_LOOP_LOOP_CONTROL */
> + int n = 0;
> do {
> if (n > MAX_LOOP_NUM)
> bb_error_msg_and_die("no free loop devices");
> @@ -108,6 +120,8 @@ int losetup_main(int argc UNUSED_PARAM, char **argv)
> s = query_loop(dev);
> free(s);
> } while (s);
> +#endif /* ENABLE_FEATURE_MOUNT_LOOP_LOOP_CONTROL */
> +
> /* now: dev is next free "/dev/loopN" */
> if ((opt == OPT_f) && !argv[0]) {
> puts(dev);
> --
> 2.17.1
>
> _______________________________________________
> busybox mailing list
> busybox at busybox.net
> http://lists.busybox.net/mailman/listinfo/busybox
More information about the busybox
mailing list