/* Let's see if the device is really detached, i.e. currently has no associated partition block
* devices. On various kernels (such as 5.8) it is possible to have a loopback block device that
- * superficially is detached but still has partition block devices associated for it. They only go
- * away when the device is reattached. (Yes, LOOP_CLR_FD doesn't work then, because officially
- * nothing is attached and LOOP_CTL_REMOVE doesn't either, since it doesn't care about partition
- * block devices. */
+ * superficially is detached but still has partition block devices associated for it. Let's then
+ * manually remove the partitions via BLKPG, and tell the caller we did that via EUCLEAN, so they try
+ * again. */
r = device_has_block_children(d);
if (r < 0)
return r;
if (r > 0)
return -EBUSY;
- return -EUCLEAN; /* Bound but children? Tell caller to reattach something so that the
- * partition block devices are gone too. */
+ /* Unbound but has children? Remove all partitions, and report this to the caller, to try
+ * again, and count this as an attempt. */
+
+ r = block_device_remove_all_partitions(fd);
+ if (r < 0)
+ return r;
+
+ return -EUCLEAN;
}
if (*try_loop_configure) {
return r;
}
-static int attach_empty_file(int loop, int nr) {
- _cleanup_close_ int fd = -1;
-
- /* So here's the thing: on various kernels (5.8 at least) loop block devices might enter a state
- * where they are detached but nonetheless have partitions, when used heavily. Accessing these
- * partitions results in immediatey IO errors. There's no pretty way to get rid of them
- * again. Neither LOOP_CLR_FD nor LOOP_CTL_REMOVE suffice (see above). What does work is to
- * reassociate them with a new fd however. This is what we do here hence: we associate the devices
- * with an empty file (i.e. an image that definitely has no partitions). We then immediately clear it
- * again. This suffices to make the partitions go away. Ugly but appears to work. */
-
- log_debug("Found unattached loopback block device /dev/loop%i with partitions. Attaching empty file to remove them.", nr);
-
- fd = open_tmpfile_unlinkable(NULL, O_RDONLY);
- if (fd < 0)
- return fd;
-
- if (flock(loop, LOCK_EX) < 0)
- return -errno;
-
- if (ioctl(loop, LOOP_SET_FD, fd) < 0)
- return -errno;
-
- if (ioctl(loop, LOOP_SET_STATUS64, &(struct loop_info64) {
- .lo_flags = LO_FLAGS_READ_ONLY|
- LO_FLAGS_AUTOCLEAR|
- LO_FLAGS_PARTSCAN, /* enable partscan, so that the partitions really go away */
- }) < 0)
- return -errno;
-
- if (ioctl(loop, LOOP_CLR_FD) < 0)
- return -errno;
-
- /* The caller is expected to immediately close the loopback device after this, so that the BSD lock
- * is released, and udev sees the changes. */
- return 0;
-}
-
static int loop_device_make_internal(
int fd,
int open_flags,
loop_with_fd = TAKE_FD(loop);
break;
}
- if (r == -EUCLEAN) {
- /* Make left-over partition disappear hack (see above) */
- r = attach_empty_file(loop, nr);
- if (r < 0 && r != -EBUSY)
- return r;
- } else if (r != -EBUSY)
+ if (!IN_SET(r, -EBUSY, -EUCLEAN)) /* Busy, or some left-over partition devices that
+ * were cleaned up. */
return r;
}