summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libbb.h7
-rw-r--r--libbb/loop.c42
-rw-r--r--util-linux/losetup.c8
3 files changed, 47 insertions, 10 deletions
diff --git a/include/libbb.h b/include/libbb.h
index 100d6b6..021100d 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 c78535a..ada0c76 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 b52d693..5dc7570 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");