mirror of
https://github.com/systemd/systemd.git
synced 2024-10-29 21:55:36 +03:00
loop-util: open loopback block device in loop_configure()
And make it return LoopDevice object on success. No functional changes, just refactoring.
This commit is contained in:
parent
bb273a5145
commit
da4fd28871
@ -217,24 +217,41 @@ static int loop_configure_fallback(int fd, const struct loop_config *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int loop_configure(
|
static int loop_configure(
|
||||||
sd_device *dev,
|
|
||||||
int fd,
|
|
||||||
int nr,
|
int nr,
|
||||||
|
int open_flags,
|
||||||
|
int lock_op,
|
||||||
const struct loop_config *c,
|
const struct loop_config *c,
|
||||||
uint64_t *ret_seqnum_not_before,
|
LoopDevice **ret) {
|
||||||
usec_t *ret_timestamp_not_before,
|
|
||||||
int *ret_lock_fd) {
|
|
||||||
|
|
||||||
static bool loop_configure_broken = false;
|
static bool loop_configure_broken = false;
|
||||||
|
|
||||||
_cleanup_close_ int lock_fd = -1;
|
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
||||||
uint64_t seqnum;
|
_cleanup_(cleanup_clear_loop_close) int loop_with_fd = -1; /* This must be declared before lock_fd. */
|
||||||
usec_t timestamp;
|
_cleanup_close_ int fd = -1, lock_fd = -1;
|
||||||
|
_cleanup_free_ char *node = NULL;
|
||||||
|
uint64_t diskseq = 0, seqnum = UINT64_MAX;
|
||||||
|
usec_t timestamp = USEC_INFINITY;
|
||||||
|
dev_t devno;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(fd >= 0);
|
|
||||||
assert(nr >= 0);
|
assert(nr >= 0);
|
||||||
assert(c);
|
assert(c);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
if (asprintf(&node, "/dev/loop%i", nr) < 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = sd_device_new_from_devname(&dev, node);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_device_get_devnum(dev, &devno);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
fd = sd_device_open(dev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
|
||||||
|
if (fd < 0)
|
||||||
|
return fd;
|
||||||
|
|
||||||
/* Let's lock the device before we do anything. We take the BSD lock on a second, separately opened
|
/* Let's lock the device before we do anything. We take the BSD lock on a second, separately opened
|
||||||
* fd for the device. udev after all watches for close() events (specifically IN_CLOSE_WRITE) on
|
* fd for the device. udev after all watches for close() events (specifically IN_CLOSE_WRITE) on
|
||||||
@ -295,9 +312,11 @@ static int loop_configure(
|
|||||||
|
|
||||||
loop_configure_broken = true;
|
loop_configure_broken = true;
|
||||||
} else {
|
} else {
|
||||||
r = loop_configure_verify(fd, c);
|
loop_with_fd = TAKE_FD(fd);
|
||||||
|
|
||||||
|
r = loop_configure_verify(loop_with_fd, c);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
return r;
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
/* LOOP_CONFIGURE doesn't work. Remember that. */
|
/* LOOP_CONFIGURE doesn't work. Remember that. */
|
||||||
loop_configure_broken = true;
|
loop_configure_broken = true;
|
||||||
@ -308,8 +327,7 @@ static int loop_configure(
|
|||||||
* good chance we cannot actually reuse the loopback device right-away. Hence
|
* good chance we cannot actually reuse the loopback device right-away. Hence
|
||||||
* let's assume it's busy, avoid the trouble and let the calling loop call us
|
* let's assume it's busy, avoid the trouble and let the calling loop call us
|
||||||
* again with a new, likely unused device. */
|
* again with a new, likely unused device. */
|
||||||
r = -EBUSY;
|
return -EBUSY;
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,26 +343,49 @@ static int loop_configure(
|
|||||||
if (ioctl(fd, LOOP_SET_FD, c->fd) < 0)
|
if (ioctl(fd, LOOP_SET_FD, c->fd) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
r = loop_configure_fallback(fd, c);
|
loop_with_fd = TAKE_FD(fd);
|
||||||
|
|
||||||
|
r = loop_configure_fallback(loop_with_fd, c);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret_seqnum_not_before)
|
r = fd_get_diskseq(loop_with_fd, &diskseq);
|
||||||
*ret_seqnum_not_before = seqnum;
|
if (r < 0 && r != -EOPNOTSUPP)
|
||||||
if (ret_timestamp_not_before)
|
return r;
|
||||||
*ret_timestamp_not_before = timestamp;
|
|
||||||
if (ret_lock_fd)
|
|
||||||
*ret_lock_fd = TAKE_FD(lock_fd);
|
|
||||||
|
|
||||||
|
switch (lock_op & ~LOCK_NB) {
|
||||||
|
case LOCK_EX: /* Already in effect */
|
||||||
|
break;
|
||||||
|
case LOCK_SH: /* Downgrade */
|
||||||
|
if (flock(lock_fd, lock_op) < 0)
|
||||||
|
return -errno;
|
||||||
|
break;
|
||||||
|
case LOCK_UN: /* Release */
|
||||||
|
lock_fd = safe_close(lock_fd);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
LoopDevice *d = new(LoopDevice, 1);
|
||||||
|
if (!d)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*d = (LoopDevice) {
|
||||||
|
.fd = TAKE_FD(loop_with_fd),
|
||||||
|
.lock_fd = TAKE_FD(lock_fd),
|
||||||
|
.node = TAKE_PTR(node),
|
||||||
|
.nr = nr,
|
||||||
|
.devno = devno,
|
||||||
|
.dev = TAKE_PTR(dev),
|
||||||
|
.diskseq = diskseq,
|
||||||
|
.uevent_seqnum_not_before = seqnum,
|
||||||
|
.timestamp_not_before = timestamp,
|
||||||
|
};
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(d);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
|
||||||
/* Close the lock fd explicitly before clearing the loopback block device, since an additional open
|
|
||||||
* fd would block the clearing to succeed */
|
|
||||||
lock_fd = safe_close(lock_fd);
|
|
||||||
(void) ioctl(fd, LOOP_CLR_FD);
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int loop_device_make_internal(
|
static int loop_device_make_internal(
|
||||||
@ -357,14 +398,11 @@ static int loop_device_make_internal(
|
|||||||
int lock_op,
|
int lock_op,
|
||||||
LoopDevice **ret) {
|
LoopDevice **ret) {
|
||||||
|
|
||||||
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||||
_cleanup_close_ int direct_io_fd = -1;
|
_cleanup_close_ int direct_io_fd = -1, control = -1;
|
||||||
_cleanup_free_ char *node = NULL, *backing_file = NULL;
|
_cleanup_free_ char *backing_file = NULL;
|
||||||
struct loop_config config;
|
struct loop_config config;
|
||||||
LoopDevice *d;
|
int r, f_flags;
|
||||||
uint64_t seqnum = UINT64_MAX;
|
|
||||||
usec_t timestamp = USEC_INFINITY;
|
|
||||||
int nr, r, f_flags;
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
@ -422,11 +460,6 @@ static int loop_device_make_internal(
|
|||||||
fd = direct_io_fd; /* From now on, operate on our new O_DIRECT fd */
|
fd = direct_io_fd; /* From now on, operate on our new O_DIRECT fd */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* On failure, lock_fd must be closed at first, otherwise LOOP_CLR_FD will fail. */
|
|
||||||
_cleanup_close_ int control = -1;
|
|
||||||
_cleanup_(cleanup_clear_loop_close) int loop_with_fd = -1;
|
|
||||||
_cleanup_close_ int lock_fd = -1;
|
|
||||||
|
|
||||||
control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
|
control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
|
||||||
if (control < 0)
|
if (control < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
@ -444,7 +477,7 @@ static int loop_device_make_internal(
|
|||||||
/* Loop around LOOP_CTL_GET_FREE, since at the moment we attempt to open the returned device it might
|
/* Loop around LOOP_CTL_GET_FREE, since at the moment we attempt to open the returned device it might
|
||||||
* be gone already, taken by somebody else racing against us. */
|
* be gone already, taken by somebody else racing against us. */
|
||||||
for (unsigned n_attempts = 0;;) {
|
for (unsigned n_attempts = 0;;) {
|
||||||
_cleanup_close_ int loop = -1;
|
int nr;
|
||||||
|
|
||||||
/* Let's take a lock on the control device first. On a busy system, where many programs
|
/* Let's take a lock on the control device first. On a busy system, where many programs
|
||||||
* attempt to allocate a loopback device at the same time, we might otherwise keep looping
|
* attempt to allocate a loopback device at the same time, we might otherwise keep looping
|
||||||
@ -464,31 +497,16 @@ static int loop_device_make_internal(
|
|||||||
if (nr < 0)
|
if (nr < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
node = mfree(node);
|
r = loop_configure(nr, open_flags, lock_op, &config, &d);
|
||||||
if (asprintf(&node, "/dev/loop%i", nr) < 0)
|
if (r >= 0)
|
||||||
return -ENOMEM;
|
break;
|
||||||
|
|
||||||
dev = sd_device_unref(dev);
|
/* -ENODEV or friends: Somebody might've gotten the same number from the kernel, used the
|
||||||
r = sd_device_new_from_devname(&dev, node);
|
* device, and called LOOP_CTL_REMOVE on it. Let's retry with a new number.
|
||||||
if (r < 0)
|
* -EBUSY: a file descriptor is already bound to the loopback block device.
|
||||||
return r;
|
* -EUCLEAN: some left-over partition devices that were cleaned up. */
|
||||||
|
if (!ERRNO_IS_DEVICE_ABSENT(errno) && !IN_SET(r, -EBUSY, -EUCLEAN))
|
||||||
loop = sd_device_open(dev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
|
return -errno;
|
||||||
if (loop < 0) {
|
|
||||||
/* Somebody might've gotten the same number from the kernel, used the device,
|
|
||||||
* and called LOOP_CTL_REMOVE on it. Let's retry with a new number. */
|
|
||||||
if (!ERRNO_IS_DEVICE_ABSENT(errno))
|
|
||||||
return -errno;
|
|
||||||
} else {
|
|
||||||
r = loop_configure(dev, loop, nr, &config, &seqnum, ×tamp, &lock_fd);
|
|
||||||
if (r >= 0) {
|
|
||||||
loop_with_fd = TAKE_FD(loop);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!IN_SET(r, -EBUSY, -EUCLEAN)) /* Busy, or some left-over partition devices that
|
|
||||||
* were cleaned up. */
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* OK, this didn't work, let's try again a bit later, but first release the lock on the
|
/* OK, this didn't work, let's try again a bit later, but first release the lock on the
|
||||||
* control device */
|
* control device */
|
||||||
@ -498,54 +516,13 @@ static int loop_device_make_internal(
|
|||||||
if (++n_attempts >= 64) /* Give up eventually */
|
if (++n_attempts >= 64) /* Give up eventually */
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
/* Now close the loop device explicitly. This will release any lock acquired by
|
|
||||||
* attach_empty_file() or similar, while we sleep below. */
|
|
||||||
loop = safe_close(loop);
|
|
||||||
|
|
||||||
/* Wait some random time, to make collision less likely. Let's pick a random time in the
|
/* Wait some random time, to make collision less likely. Let's pick a random time in the
|
||||||
* range 0ms…250ms, linearly scaled by the number of failed attempts. */
|
* range 0ms…250ms, linearly scaled by the number of failed attempts. */
|
||||||
(void) usleep(random_u64_range(UINT64_C(10) * USEC_PER_MSEC +
|
(void) usleep(random_u64_range(UINT64_C(10) * USEC_PER_MSEC +
|
||||||
UINT64_C(240) * USEC_PER_MSEC * n_attempts/64));
|
UINT64_C(240) * USEC_PER_MSEC * n_attempts/64));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fstat(loop_with_fd, &st) < 0)
|
d->backing_file = TAKE_PTR(backing_file);
|
||||||
return -errno;
|
|
||||||
assert(S_ISBLK(st.st_mode));
|
|
||||||
|
|
||||||
uint64_t diskseq = 0;
|
|
||||||
r = fd_get_diskseq(loop_with_fd, &diskseq);
|
|
||||||
if (r < 0 && r != -EOPNOTSUPP)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
switch (lock_op & ~LOCK_NB) {
|
|
||||||
case LOCK_EX: /* Already in effect */
|
|
||||||
break;
|
|
||||||
case LOCK_SH: /* Downgrade */
|
|
||||||
if (flock(lock_fd, lock_op) < 0)
|
|
||||||
return -errno;
|
|
||||||
break;
|
|
||||||
case LOCK_UN: /* Release */
|
|
||||||
lock_fd = safe_close(lock_fd);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert_not_reached();
|
|
||||||
}
|
|
||||||
|
|
||||||
d = new(LoopDevice, 1);
|
|
||||||
if (!d)
|
|
||||||
return -ENOMEM;
|
|
||||||
*d = (LoopDevice) {
|
|
||||||
.fd = TAKE_FD(loop_with_fd),
|
|
||||||
.lock_fd = TAKE_FD(lock_fd),
|
|
||||||
.node = TAKE_PTR(node),
|
|
||||||
.nr = nr,
|
|
||||||
.devno = st.st_rdev,
|
|
||||||
.dev = TAKE_PTR(dev),
|
|
||||||
.backing_file = TAKE_PTR(backing_file),
|
|
||||||
.diskseq = diskseq,
|
|
||||||
.uevent_seqnum_not_before = seqnum,
|
|
||||||
.timestamp_not_before = timestamp,
|
|
||||||
};
|
|
||||||
|
|
||||||
log_debug("Successfully acquired %s, devno=%u:%u, nr=%i, diskseq=%" PRIu64,
|
log_debug("Successfully acquired %s, devno=%u:%u, nr=%i, diskseq=%" PRIu64,
|
||||||
d->node,
|
d->node,
|
||||||
@ -553,8 +530,8 @@ static int loop_device_make_internal(
|
|||||||
d->nr,
|
d->nr,
|
||||||
d->diskseq);
|
d->diskseq);
|
||||||
|
|
||||||
*ret = d;
|
*ret = TAKE_PTR(d);
|
||||||
return d->fd;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t loop_flags_mangle(uint32_t loop_flags) {
|
static uint32_t loop_flags_mangle(uint32_t loop_flags) {
|
||||||
|
Loading…
Reference in New Issue
Block a user