diff --git a/include/libbb.h b/include/libbb.h index 100d6b606..021100db1 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1461,14 +1461,15 @@ 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; -extern int del_loop(const char *device) FAST_FUNC; +char *query_loop(const char *device) FAST_FUNC; +int get_free_loop(void) FAST_FUNC; +int del_loop(const char *device) FAST_FUNC; /* * If *devname is not NULL, use that name, otherwise try to find free one, * malloc and return it in *devname. * return value is the opened fd to the loop device, or < on error */ -extern int set_loop(char **devname, const char *file, unsigned long long offset, unsigned flags) FAST_FUNC; +int set_loop(char **devname, const char *file, unsigned long long offset, unsigned flags) FAST_FUNC; /* These constants match linux/loop.h (without BB_ prefix): */ #define BB_LO_FLAGS_READ_ONLY 1 #define BB_LO_FLAGS_AUTOCLEAR 4 diff --git a/libbb/loop.c b/libbb/loop.c index c78535a20..ada0c7638 100644 --- a/libbb/loop.c +++ b/libbb/loop.c @@ -78,6 +78,24 @@ int FAST_FUNC del_loop(const char *device) return rc; } +/* Obtain an unused loop device number */ +int FAST_FUNC get_free_loop(void) +{ + int fd; + int loopdevno; + + fd = open("/dev/loop-control", O_RDWR | O_CLOEXEC); + if (fd == -1) + return fd - 1; /* -2: "no /dev/loop-control" */ + +#ifndef LOOP_CTL_GET_FREE +# define LOOP_CTL_GET_FREE 0x4C82 +#endif + loopdevno = ioctl(fd, LOOP_CTL_GET_FREE); + close(fd); + return loopdevno; /* can be -1 if error */ +} + /* 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,12 +124,24 @@ 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); -// + try = *device; + if (!try) { + i = get_free_loop(); + if (i == -2) { /* no /dev/loop-control */ + i = 0; + try = dev; + goto old_style; + } + if (i == -1) { + close(ffd); + return -1; /* no free loop devices */ + } + try = *device = xasprintf(LOOP_FORMAT, i); + goto try_to_open; + } + + old_style: /* Find a loop device. */ - try = *device ? *device : dev; /* 1048575 (0xfffff) is a max possible minor number in Linux circa 2010 */ for (i = 0; rc && i < 1048576; i++) { sprintf(dev, LOOP_FORMAT, i); @@ -170,7 +200,7 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse rc = ioctl(dfd, BB_LOOP_SET_STATUS, &loopinfo); } if (rc != 0) { - ioctl(dfd, LOOP_CLR_FD, 0); + ioctl(dfd, LOOP_CLR_FD, 0); // actually, 0 param is unnecessary } } } else { diff --git a/util-linux/losetup.c b/util-linux/losetup.c index b52d693ec..5dc757074 100644 --- a/util-linux/losetup.c +++ b/util-linux/losetup.c @@ -114,8 +114,14 @@ int losetup_main(int argc UNUSED_PARAM, char **argv) /* contains -f */ if (opt & OPT_f) { char *s; - int n = 0; + int n; + n = get_free_loop(); + if (n == -1) + bb_error_msg_and_die("no free loop devices"); + if (n < 0) /* n == -2: no /dev/loop-control, use legacy method */ + n = 0; + /* or: n >= 0: the number of next free loopdev, just verify it */ do { if (n > MAX_LOOP_NUM) bb_error_msg_and_die("no free loop devices");