1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-01-26 10:03:40 +03:00

loop-util: drop code to attach empty file

Back when I wrote this code I wasn't aware of BLKPG and what it can do.
Hence I came up with this hack to attach an empty file to delete all
partitions. But today we can do better with BLKPG: let's just explicitly
remove all partitions, and then try again.
This commit is contained in:
Lennart Poettering 2022-09-01 15:17:01 +02:00
parent 7f52206a2b
commit 247738b4f5

View File

@ -173,10 +173,9 @@ static int loop_configure(
/* 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;
@ -187,8 +186,14 @@ static int loop_configure(
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) {
@ -340,44 +345,6 @@ fail:
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,
@ -543,12 +510,8 @@ static int loop_device_make_internal(
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;
}