Browse Source

mount,losetup: use /dev/loop-control is it exists

function                                             old     new   delta
get_free_loop                                          -      58     +58
set_loop                                             597     649     +52
losetup_main                                         482     476      -6
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 1/1 up/down: 110/-6)            Total: 104 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Denys Vlasenko 4 years ago
parent
commit
3b69ba799f
3 changed files with 47 additions and 10 deletions
  1. 4 3
      include/libbb.h
  2. 36 6
      libbb/loop.c
  3. 7 1
      util-linux/losetup.c

+ 4 - 3
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

+ 36 - 6
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 {

+ 7 - 1
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");