- struct loop_info loopinfo;
-
- for (i = 0; i <= 7; i++) {
- sprintf(dev, "/dev/loop%d", i);
- if (stat(dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
- if ((fd = open(dev, O_RDONLY)) >= 0) {
- if (ioctl(fd, LOOP_GET_STATUS, &loopinfo) != 0) {
- if (errno == ENXIO) { /* probably free */
- close(fd);
- return strdup(dev);
- }
+ int i, 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;
+ }
+
+ /* 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);
+
+ IF_FEATURE_MOUNT_LOOP_CREATE(errno = 0;)
+ if (stat(try, &statbuf) != 0 || !S_ISBLK(statbuf.st_mode)) {
+ if (ENABLE_FEATURE_MOUNT_LOOP_CREATE
+ && errno == ENOENT
+ && try == dev
+ ) {
+ /* Node doesn't exist, try to create it. */
+ if (mknod(dev, S_IFBLK|0644, makedev(7, i)) == 0)
+ goto try_to_open;
+ }
+ /* Ran out of block devices, return failure. */
+ rc = -1;
+ break;
+ }
+ try_to_open:
+ /* 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) {
+ if (errno == ENXIO) {
+ /* Happens if loop module is not loaded */
+ rc = -1;
+ break;
+ }
+ goto try_again;
+ }
+
+ rc = ioctl(dfd, BB_LOOP_GET_STATUS, &loopinfo);
+
+ /* 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);