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 <[email protected]> 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 <[email protected]> > --- > 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 > [email protected] > http://lists.busybox.net/mailman/listinfo/busybox _______________________________________________ busybox mailing list [email protected] http://lists.busybox.net/mailman/listinfo/busybox
