root_image,
FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+ LOCK_SH,
&loop_device);
if (r < 0)
return log_debug_errno(r, "Failed to create loop device for root image: %m");
- /* Make sure udevd won't issue BLKRRPART (which might flush out the loaded partition table)
- * while we are still trying to mount things */
- r = loop_device_flock(loop_device, LOCK_SH);
- if (r < 0)
- return log_debug_errno(r, "Failed to lock loopback device with LOCK_SH: %m");
-
r = dissect_image(
loop_device->fd,
&verity,
return log_error_errno(r, "Failed to get devname of block device " DEVNUM_FORMAT_STR ": %m",
DEVNUM_FORMAT_VAL(devno));
- r = loop_device_open(devname, 0, &d);
+ r = loop_device_open(devname, 0, LOCK_EX, &d);
if (r < 0)
return log_error_errno(r, "Failed to open loop device '%s': %m", devname);
- r = loop_device_flock(d, LOCK_EX);
- if (r < 0)
- return log_error_errno(r, "Failed to lock loop device '%s': %m", devname);
-
/* We've locked the loop device, now we're ready to unmount. To allow the unmount to succeed, we have
* to close the O_PATH fd we opened earlier. */
fd = safe_close(fd);
arg_image,
FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR,
FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+ LOCK_SH,
&d);
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
- /* Make sure udevd doesn't issue BLKRRPART underneath us thus making devices disappear in the middle,
- * that we assume already are there. */
- r = loop_device_flock(d, LOCK_SH);
- if (r < 0)
- return log_error_errno(r, "Failed to lock loopback device: %m");
-
r = dissect_image_and_warn(
d->fd,
arg_image,
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine backing device for DM %s.", setup->dm_name);
if (!setup->loop) {
- r = loop_device_open(n, O_RDWR, &setup->loop);
+ r = loop_device_open(n, O_RDWR, LOCK_UN, &setup->loop);
if (r < 0)
return log_error_errno(r, "Failed to open loopback device %s: %m", n);
}
return r;
}
- r = loop_device_make(setup->image_fd, O_RDWR, offset, size, 0, &setup->loop);
+ r = loop_device_make(setup->image_fd, O_RDWR, offset, size, 0, LOCK_UN, &setup->loop);
if (r == -ENOENT) {
log_error_errno(r, "Loopback block device support is not available on this system.");
return -ENOLINK; /* make recognizable */
log_info("Writing of partition table completed.");
- r = loop_device_make(setup->image_fd, O_RDWR, partition_offset, partition_size, 0, &setup->loop);
+ r = loop_device_make(setup->image_fd, O_RDWR, partition_offset, partition_size, 0, LOCK_EX, &setup->loop);
if (r < 0) {
if (r == -ENOENT) { /* this means /dev/loop-control doesn't exist, i.e. we are in a container
* or similar and loopback bock devices are not available, return a
return log_error_errno(r, "Failed to set up loopback device for %s: %m", setup->temporary_image_path);
}
- r = loop_device_flock(setup->loop, LOCK_EX); /* make sure udev won't read before we are done */
- if (r < 0)
- return log_error_errno(r, "Failed to take lock on loop device: %m");
-
log_info("Setting up loopback device %s completed.", setup->loop->node ?: ip);
r = luks_format(setup->loop->node,
arg_image,
arg_read_only ? O_RDONLY : O_RDWR,
FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+ LOCK_SH,
&loop);
if (r < 0) {
log_error_errno(r, "Failed to set up loopback block device: %m");
goto finish;
}
- /* Take a LOCK_SH lock on the device, so that udevd doesn't issue BLKRRPART in our back */
- r = loop_device_flock(loop, LOCK_SH);
- if (r < 0) {
- log_error_errno(r, "Failed to take lock on loopback block device: %m");
- goto finish;
- }
-
r = dissect_image_and_warn(
loop->fd,
arg_image,
assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
if (p->encrypt != ENCRYPT_OFF) {
- r = loop_device_make(whole_fd, O_RDWR, p->offset, p->new_size, 0, &d);
+ r = loop_device_make(whole_fd, O_RDWR, p->offset, p->new_size, 0, LOCK_EX, &d);
if (r < 0)
return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
- r = loop_device_flock(d, LOCK_EX);
- if (r < 0)
- return log_error_errno(r, "Failed to lock loopback device: %m");
-
r = partition_encrypt(context, p, d->node, &cd, &encrypted, &encrypted_dev_fd);
if (r < 0)
return log_error_errno(r, "Failed to encrypt device: %m");
/* Loopback block devices are not only useful to turn regular files into block devices, but
* also to cut out sections of block devices into new block devices. */
- r = loop_device_make(fd, O_RDWR, p->offset, p->new_size, 0, &d);
+ r = loop_device_make(fd, O_RDWR, p->offset, p->new_size, 0, LOCK_EX, &d);
if (r < 0)
return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
- r = loop_device_flock(d, LOCK_EX);
- if (r < 0)
- return log_error_errno(r, "Failed to lock loopback device: %m");
-
if (p->encrypt != ENCRYPT_OFF) {
r = partition_encrypt(context, p, d->node, &cd, &encrypted, &encrypted_dev_fd);
if (r < 0)
assert(path);
- r = loop_device_make_by_path(path, O_RDONLY, LO_FLAGS_PARTSCAN, &d);
+ r = loop_device_make_by_path(path, O_RDONLY, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
if (r == -EISDIR) {
_cleanup_free_ char *image_name = NULL;
/* We now have a loopback block device, let's fork off a child in its own mount namespace, mount it
* there, and extract the metadata we need. The metadata is sent from the child back to us. */
- r = loop_device_flock(d, LOCK_SH);
- if (r < 0)
- return log_debug_errno(r, "Failed to acquire lock on loopback block device: %m");
-
BLOCK_SIGNALS(SIGCHLD);
r = mkdtemp_malloc("/tmp/inspect-XXXXXX", &tmpdir);
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
- r = loop_device_make_by_path(i->path, O_RDONLY, LO_FLAGS_PARTSCAN, &d);
- if (r < 0)
- return r;
-
- /* Make sure udevd doesn't issue BLKRRPART in the background which might make our partitions
- * disappear temporarily. */
- r = loop_device_flock(d, LOCK_SH);
+ r = loop_device_make_by_path(i->path, O_RDONLY, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
if (r < 0)
return r;
image,
FLAGS_SET(flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR,
FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+ LOCK_SH,
&d);
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device for %s: %m", image);
- /* Make sure udevd doesn't issue BLKRRPART behind our backs */
- r = loop_device_flock(d, LOCK_SH);
- if (r < 0)
- return r;
-
r = dissect_image_and_warn(d->fd, image, &verity, NULL, d->diskseq, d->uevent_seqnum_not_before, d->timestamp_not_before, flags, &dissected_image);
if (r < 0)
return r;
src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
-1,
verity.data_path ? 0 : LO_FLAGS_PARTSCAN,
+ LOCK_SH,
&loop_device);
if (r < 0)
return log_debug_errno(r, "Failed to create loop device for image: %m");
- r = loop_device_flock(loop_device, LOCK_SH);
- if (r < 0)
- return log_debug_errno(r, "Failed to lock loop device: %m");
-
r = dissect_image(
loop_device->fd,
&verity,
return !!sd_device_enumerator_get_device_first(e);
}
+static int open_lock_fd(int primary_fd, int operation) {
+ int lock_fd;
+
+ assert(primary_fd >= 0);
+
+ lock_fd = fd_reopen(primary_fd, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+ if (lock_fd < 0)
+ return lock_fd;
+ if (flock(lock_fd, operation) < 0)
+ return -errno;
+
+ return lock_fd;
+}
+
static int loop_configure(
int fd,
int nr,
const struct loop_config *c,
bool *try_loop_configure,
uint64_t *ret_seqnum_not_before,
- usec_t *ret_timestamp_not_before) {
+ usec_t *ret_timestamp_not_before,
+ int *ret_lock_fd) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_free_ char *sysname = NULL;
* long time udev would possibly never run on it again, even though the fd is unlocked, simply
* because we never close() it. It also has the nice benefit we can use the _cleanup_close_ logic to
* automatically release the lock, after we are done. */
- lock_fd = fd_reopen(fd, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+ lock_fd = open_lock_fd(fd, LOCK_EX);
if (lock_fd < 0)
return lock_fd;
- if (flock(lock_fd, LOCK_EX) < 0)
- return -errno;
/* 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
goto fail;
}
- if (ret_seqnum_not_before)
- *ret_seqnum_not_before = seqnum;
- if (ret_timestamp_not_before)
- *ret_timestamp_not_before = timestamp;
-
- return 0;
+ goto success;
}
}
log_debug_errno(errno, "Failed to enable direct IO mode on loopback device /dev/loop%i, ignoring: %m", nr);
}
+success:
if (ret_seqnum_not_before)
*ret_seqnum_not_before = seqnum;
if (ret_timestamp_not_before)
*ret_timestamp_not_before = timestamp;
+ if (ret_lock_fd)
+ *ret_lock_fd = TAKE_FD(lock_fd);
return 0;
uint64_t offset,
uint64_t size,
uint32_t loop_flags,
+ int lock_op,
LoopDevice **ret) {
- _cleanup_close_ int direct_io_fd = -1;
+ _cleanup_close_ int direct_io_fd = -1, lock_fd = -1;
_cleanup_free_ char *loopdev = NULL;
bool try_loop_configure = true;
struct loop_config config;
return -errno;
if (S_ISBLK(st.st_mode)) {
+ if (lock_op != LOCK_UN) {
+ lock_fd = open_lock_fd(fd, lock_op);
+ if (lock_fd < 0)
+ return lock_fd;
+ }
+
if (ioctl(fd, LOOP_GET_STATUS64, &config.info) >= 0) {
/* Oh! This is a loopback device? That's interesting! */
return -ENOMEM;
*d = (LoopDevice) {
.fd = TAKE_FD(copy),
+ .lock_fd = TAKE_FD(lock_fd),
.nr = nr,
.node = TAKE_PTR(loopdev),
.relinquished = true, /* It's not allocated by us, don't destroy it when this object is freed */
if (!ERRNO_IS_DEVICE_ABSENT(errno))
return -errno;
} else {
- r = loop_configure(loop, nr, &config, &try_loop_configure, &seqnum, ×tamp);
+ r = loop_configure(loop, nr, &config, &try_loop_configure, &seqnum, ×tamp, &lock_fd);
if (r >= 0) {
loop_with_fd = TAKE_FD(loop);
break;
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(loopdev),
.nr = nr,
.devno = st.st_rdev,
uint64_t offset,
uint64_t size,
uint32_t loop_flags,
+ int lock_op,
LoopDevice **ret) {
assert(fd >= 0);
offset,
size,
loop_flags_mangle(loop_flags),
+ lock_op,
ret);
}
const char *path,
int open_flags,
uint32_t loop_flags,
+ int lock_op,
LoopDevice **ret) {
int r, basic_flags, direct_flags, rdwr_flags;
direct ? "enabled" : "disabled",
direct != (direct_flags != 0) ? " (O_DIRECT was requested but not supported)" : "");
- return loop_device_make_internal(fd, open_flags, 0, 0, loop_flags, ret);
+ return loop_device_make_internal(fd, open_flags, 0, 0, loop_flags, lock_op, ret);
}
LoopDevice* loop_device_unref(LoopDevice *d) {
if (!d)
return NULL;
+ d->lock_fd = safe_close(d->lock_fd);
+
if (d->fd >= 0) {
/* Implicitly sync the device, since otherwise in-flight blocks might not get written */
if (fsync(d->fd) < 0)
d->relinquished = false;
}
-int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret) {
- _cleanup_close_ int loop_fd = -1;
+int loop_device_open(
+ const char *loop_path,
+ int open_flags,
+ int lock_op,
+ LoopDevice **ret) {
+
+ _cleanup_close_ int loop_fd = -1, lock_fd = -1;
_cleanup_free_ char *p = NULL;
struct loop_info64 info;
struct stat st;
} else
nr = -1;
+ if ((lock_op & ~LOCK_NB) != LOCK_UN) {
+ lock_fd = open_lock_fd(loop_fd, lock_op);
+ if (lock_fd < 0)
+ return lock_fd;
+ }
+
p = strdup(loop_path);
if (!p)
return -ENOMEM;
*d = (LoopDevice) {
.fd = TAKE_FD(loop_fd),
+ .lock_fd = TAKE_FD(lock_fd),
.nr = nr,
.node = TAKE_PTR(p),
.relinquished = true, /* It's not ours, don't try to destroy it when this object is freed */
}
int loop_device_flock(LoopDevice *d, int operation) {
+ assert(IN_SET(operation & ~LOCK_NB, LOCK_UN, LOCK_SH, LOCK_EX));
assert(d);
- if (d->fd < 0)
- return -EBADF;
+ /* When unlocking just close the lock fd */
+ if ((operation & ~LOCK_NB) == LOCK_UN) {
+ d->lock_fd = safe_close(d->lock_fd);
+ return 0;
+ }
+
+ /* If we had no lock fd so far, create one and lock it right-away */
+ if (d->lock_fd < 0) {
+ assert(d->fd >= 0);
+
+ d->lock_fd = open_lock_fd(d->fd, operation);
+ if (d->lock_fd < 0)
+ return d->lock_fd;
+
+ return 0;
+ }
- return RET_NERRNO(flock(d->fd, operation));
+ /* Otherwise change the current lock mode on the existing fd */
+ return RET_NERRNO(flock(d->lock_fd, operation));
}
int loop_device_sync(LoopDevice *d) {
struct LoopDevice {
int fd;
+ int lock_fd;
int nr;
dev_t devno;
char *node;
usec_t timestamp_not_before; /* CLOCK_MONOTONIC timestamp taken immediately before attaching the loopback device, or USEC_INFINITY if we don't know */
};
-int loop_device_make(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t loop_flags, LoopDevice **ret);
-int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, LoopDevice **ret);
-int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret);
+int loop_device_make(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t loop_flags, int lock_op, LoopDevice **ret);
+int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, int lock_op, LoopDevice **ret);
+int loop_device_open(const char *loop_path, int open_flags, int lock_op, LoopDevice **ret);
LoopDevice* loop_device_unref(LoopDevice *d);
DEFINE_TRIVIAL_CLEANUP_FUNC(LoopDevice*, loop_device_unref);
img->path,
O_RDONLY,
FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+ LOCK_SH,
&d);
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device for %s: %m", img->path);
- r = loop_device_flock(d, LOCK_SH);
- if (r < 0)
- return log_error_errno(r, "Failed to lock loopback device: %m");
-
r = dissect_image_and_warn(
d->fd,
img->path,
assert_se(mkdtemp_malloc(NULL, &mounted) >= 0);
- r = loop_device_make(fd, O_RDONLY, 0, UINT64_MAX, LO_FLAGS_PARTSCAN, &loop);
+ r = loop_device_make(fd, O_RDONLY, 0, UINT64_MAX, LO_FLAGS_PARTSCAN, LOCK_SH, &loop);
if (r < 0)
log_error_errno(r, "Failed to allocate loopback device: %m");
assert_se(r >= 0);
log_notice("Acquired loop device %s, will mount on %s", loop->node, mounted);
- /* Let's make sure udev doesn't call BLKRRPART in the background, while we try to mount the device. */
- assert_se(loop_device_flock(loop, LOCK_SH) >= 0);
-
r = dissect_image(loop->fd, NULL, NULL, loop->diskseq, loop->uevent_seqnum_not_before, loop->timestamp_not_before, DISSECT_IMAGE_READ_ONLY, &dissected);
if (r < 0)
log_error_errno(r, "Failed dissect loopback device %s: %m", loop->node);
assert_se(pclose(sfdisk) == 0);
sfdisk = NULL;
- assert_se(loop_device_make(fd, O_RDWR, 0, UINT64_MAX, LO_FLAGS_PARTSCAN, &loop) >= 0);
+ assert_se(loop_device_make(fd, O_RDWR, 0, UINT64_MAX, LO_FLAGS_PARTSCAN, LOCK_EX, &loop) >= 0);
#if HAVE_BLKID
_cleanup_(dissected_image_unrefp) DissectedImage *dissected = NULL;
pthread_t threads[arg_n_threads];
sd_id128_t id;
- /* Take an explicit lock while we format the file systems, in accordance with
- * https://systemd.io/BLOCK_DEVICE_LOCKING/. We don't want udev to interfere and probe while we write
- * or even issue BLKRRPART or similar while we are working on this. */
- assert_se(loop_device_flock(loop, LOCK_EX) >= 0);
-
assert_se(dissect_image(loop->fd, NULL, NULL, loop->diskseq, loop->uevent_seqnum_not_before, loop->timestamp_not_before, 0, &dissected) >= 0);
assert_se(dissected->partitions[PARTITION_ESP].found);