mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-06 16:30:04 +01:00
Update LUKS2 locks for atomic operations.
Atomic operation requires to hold a lock for longer period than single metadata I/O. Update locks so that we can: - lock a device more than once (lock ref counting) - reaquire read lock on already held write lock (write lock is stronger than read lock)
This commit is contained in:
committed by
Milan Broz
parent
3023f26911
commit
88b3924132
@@ -349,7 +349,7 @@ void device_free(struct crypt_device *cd, struct device *device)
|
||||
close(device->loop_fd);
|
||||
}
|
||||
|
||||
assert (!device_locked(device->lh));
|
||||
assert(!device_locked(device->lh));
|
||||
|
||||
free(device->file_path);
|
||||
free(device->path);
|
||||
@@ -829,21 +829,25 @@ size_t device_alignment(struct device *device)
|
||||
return device->alignment;
|
||||
}
|
||||
|
||||
void device_set_lock_handle(struct device *device, struct crypt_lock_handle *h)
|
||||
{
|
||||
device->lh = h;
|
||||
}
|
||||
|
||||
struct crypt_lock_handle *device_get_lock_handle(struct device *device)
|
||||
{
|
||||
return device->lh;
|
||||
}
|
||||
|
||||
int device_read_lock(struct crypt_device *cd, struct device *device)
|
||||
{
|
||||
if (!crypt_metadata_locking_enabled())
|
||||
return 0;
|
||||
|
||||
assert(!device_locked(device->lh));
|
||||
if (device_read_lock_internal(cd, device))
|
||||
return -EBUSY;
|
||||
|
||||
device->lh = device_read_lock_handle(cd, device_path(device));
|
||||
|
||||
if (device_locked(device->lh)) {
|
||||
log_dbg(cd, "Device %s READ lock taken.", device_path(device));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_write_lock(struct crypt_device *cd, struct device *device)
|
||||
@@ -851,16 +855,12 @@ int device_write_lock(struct crypt_device *cd, struct device *device)
|
||||
if (!crypt_metadata_locking_enabled())
|
||||
return 0;
|
||||
|
||||
assert(!device_locked(device->lh));
|
||||
assert(!device_locked(device->lh) || !device_locked_readonly(device->lh));
|
||||
|
||||
device->lh = device_write_lock_handle(cd, device_path(device));
|
||||
if (device_write_lock_internal(cd, device))
|
||||
return -EBUSY;
|
||||
|
||||
if (device_locked(device->lh)) {
|
||||
log_dbg(cd, "Device %s WRITE lock taken.", device_path(device));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void device_read_unlock(struct crypt_device *cd, struct device *device)
|
||||
@@ -868,13 +868,9 @@ void device_read_unlock(struct crypt_device *cd, struct device *device)
|
||||
if (!crypt_metadata_locking_enabled())
|
||||
return;
|
||||
|
||||
assert(device_locked(device->lh) && device_locked_readonly(device->lh));
|
||||
assert(device_locked(device->lh));
|
||||
|
||||
device_unlock_handle(cd, device->lh);
|
||||
|
||||
log_dbg(cd, "Device %s READ lock released.", device_path(device));
|
||||
|
||||
device->lh = NULL;
|
||||
device_unlock_internal(cd, device);
|
||||
}
|
||||
|
||||
void device_write_unlock(struct crypt_device *cd, struct device *device)
|
||||
@@ -884,11 +880,7 @@ void device_write_unlock(struct crypt_device *cd, struct device *device)
|
||||
|
||||
assert(device_locked(device->lh) && !device_locked_readonly(device->lh));
|
||||
|
||||
device_unlock_handle(cd, device->lh);
|
||||
|
||||
log_dbg(cd, "Device %s WRITE lock released.", device_path(device));
|
||||
|
||||
device->lh = NULL;
|
||||
device_unlock_internal(cd, device);
|
||||
}
|
||||
|
||||
bool device_is_locked(struct device *device)
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
# include <sys/sysmacros.h> /* for major, minor */
|
||||
#endif
|
||||
#include <libgen.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "utils_device_locking.h"
|
||||
@@ -52,6 +53,7 @@ enum lock_type {
|
||||
|
||||
struct crypt_lock_handle {
|
||||
dev_t devno;
|
||||
unsigned refcnt;
|
||||
int flock_fd;
|
||||
enum lock_type type;
|
||||
__typeof__( ((struct stat*)0)->st_mode) mode;
|
||||
@@ -186,7 +188,7 @@ static void release_lock_handle(struct crypt_device *cd, struct crypt_lock_handl
|
||||
}
|
||||
|
||||
if (close(h->flock_fd))
|
||||
log_dbg(cd, "Failed to close resource fd (%d).", h->flock_fd);
|
||||
log_dbg(cd, "Failed to close lock resource fd (%d).", h->flock_fd);
|
||||
}
|
||||
|
||||
int device_locked(struct crypt_lock_handle *h)
|
||||
@@ -217,20 +219,43 @@ static int verify_lock_handle(const char *device_path, struct crypt_lock_handle
|
||||
return (stat(res, &res_st) || !same_inode(lck_st, res_st)) ? -EAGAIN : 0;
|
||||
}
|
||||
|
||||
struct crypt_lock_handle *device_read_lock_handle(struct crypt_device *cd, const char *device_path)
|
||||
static void device_lock_inc(struct crypt_lock_handle *h)
|
||||
{
|
||||
h->refcnt++;
|
||||
}
|
||||
|
||||
static unsigned device_lock_dec(struct crypt_lock_handle *h)
|
||||
{
|
||||
assert(h->refcnt);
|
||||
|
||||
return --h->refcnt;
|
||||
}
|
||||
|
||||
int device_read_lock_internal(struct crypt_device *cd, struct device *device)
|
||||
{
|
||||
int r;
|
||||
struct crypt_lock_handle *h = malloc(sizeof(*h));
|
||||
struct crypt_lock_handle *h;
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
|
||||
h = device_get_lock_handle(device);
|
||||
|
||||
if (device_locked(h)) {
|
||||
device_lock_inc(h);
|
||||
log_dbg(cd, "Device %s READ lock (or higher) already held.", device_path(device));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(h = malloc(sizeof(*h))))
|
||||
return -ENOMEM;
|
||||
|
||||
do {
|
||||
r = acquire_lock_handle(cd, device_path, h);
|
||||
r = acquire_lock_handle(cd, device_path(device), h);
|
||||
if (r)
|
||||
break;
|
||||
|
||||
log_dbg(cd, "Acquiring read lock for device %s.", device_path);
|
||||
log_dbg(cd, "Acquiring read lock for device %s.", device_path(device));
|
||||
|
||||
if (flock(h->flock_fd, LOCK_SH)) {
|
||||
log_dbg(cd, "Shared flock failed with errno %d.", errno);
|
||||
@@ -239,13 +264,13 @@ struct crypt_lock_handle *device_read_lock_handle(struct crypt_device *cd, const
|
||||
break;
|
||||
}
|
||||
|
||||
log_dbg(cd, "Verifying read lock handle for device %s.", device_path);
|
||||
log_dbg(cd, "Verifying read lock handle for device %s.", device_path(device));
|
||||
|
||||
/*
|
||||
* check whether another libcryptsetup process removed resource file before this
|
||||
* one managed to flock() it. See release_lock_handle() for details
|
||||
*/
|
||||
r = verify_lock_handle(device_path, h);
|
||||
r = verify_lock_handle(device_path(device), h);
|
||||
if (r) {
|
||||
flock(h->flock_fd, LOCK_UN);
|
||||
release_lock_handle(cd, h);
|
||||
@@ -255,28 +280,43 @@ struct crypt_lock_handle *device_read_lock_handle(struct crypt_device *cd, const
|
||||
|
||||
if (r) {
|
||||
free(h);
|
||||
return NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
h->type = DEV_LOCK_READ;
|
||||
h->refcnt = 1;
|
||||
device_set_lock_handle(device, h);
|
||||
|
||||
return h;
|
||||
log_dbg(cd, "Device %s READ lock taken.", device_path(device));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct crypt_lock_handle *device_write_lock_handle(struct crypt_device *cd, const char *device_path)
|
||||
int device_write_lock_internal(struct crypt_device *cd, struct device *device)
|
||||
{
|
||||
int r;
|
||||
struct crypt_lock_handle *h = malloc(sizeof(*h));
|
||||
struct crypt_lock_handle *h;
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
|
||||
h = device_get_lock_handle(device);
|
||||
|
||||
if (device_locked(h)) {
|
||||
device_lock_inc(h);
|
||||
log_dbg(cd, "Device %s WRITE lock already held.", device_path(device));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(h = malloc(sizeof(*h))))
|
||||
return -ENOMEM;
|
||||
|
||||
do {
|
||||
r = acquire_lock_handle(cd, device_path, h);
|
||||
r = acquire_lock_handle(cd, device_path(device), h);
|
||||
if (r)
|
||||
break;
|
||||
|
||||
log_dbg(cd, "Acquiring write lock for device %s.", device_path);
|
||||
log_dbg(cd, "Acquiring write lock for device %s.", device_path(device));
|
||||
|
||||
if (flock(h->flock_fd, LOCK_EX)) {
|
||||
log_dbg(cd, "Exclusive flock failed with errno %d.", errno);
|
||||
@@ -285,13 +325,13 @@ struct crypt_lock_handle *device_write_lock_handle(struct crypt_device *cd, cons
|
||||
break;
|
||||
}
|
||||
|
||||
log_dbg(cd, "Verifying write lock handle for device %s.", device_path);
|
||||
log_dbg(cd, "Verifying write lock handle for device %s.", device_path(device));
|
||||
|
||||
/*
|
||||
* check whether another libcryptsetup process removed resource file before this
|
||||
* one managed to flock() it. See release_lock_handle() for details
|
||||
*/
|
||||
r = verify_lock_handle(device_path, h);
|
||||
r = verify_lock_handle(device_path(device), h);
|
||||
if (r) {
|
||||
flock(h->flock_fd, LOCK_UN);
|
||||
release_lock_handle(cd, h);
|
||||
@@ -301,22 +341,36 @@ struct crypt_lock_handle *device_write_lock_handle(struct crypt_device *cd, cons
|
||||
|
||||
if (r) {
|
||||
free(h);
|
||||
return NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
h->type = DEV_LOCK_WRITE;
|
||||
h->refcnt = 1;
|
||||
device_set_lock_handle(device, h);
|
||||
|
||||
return h;
|
||||
log_dbg(cd, "Device %s WRITE lock taken.", device_path(device));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void device_unlock_handle(struct crypt_device *cd, struct crypt_lock_handle *h)
|
||||
void device_unlock_internal(struct crypt_device *cd, struct device *device)
|
||||
{
|
||||
struct crypt_lock_handle *h = device_get_lock_handle(device);
|
||||
unsigned u = device_lock_dec(h);
|
||||
|
||||
if (u)
|
||||
return;
|
||||
|
||||
if (flock(h->flock_fd, LOCK_UN))
|
||||
log_dbg(cd, "flock on fd %d failed.", h->flock_fd);
|
||||
|
||||
release_lock_handle(cd, h);
|
||||
|
||||
log_dbg(cd, "Device %s %s lock released.", device_path(device),
|
||||
device_locked_readonly(h) ? "READ" : "WRITE");
|
||||
|
||||
free(h);
|
||||
device_set_lock_handle(device, NULL);
|
||||
}
|
||||
|
||||
int device_locked_verify(struct crypt_device *cd, int dev_fd, struct crypt_lock_handle *h)
|
||||
|
||||
@@ -24,14 +24,19 @@
|
||||
|
||||
struct crypt_device;
|
||||
struct crypt_lock_handle;
|
||||
struct device;
|
||||
|
||||
int device_locked_readonly(struct crypt_lock_handle *h);
|
||||
int device_locked(struct crypt_lock_handle *h);
|
||||
|
||||
struct crypt_lock_handle *device_read_lock_handle(struct crypt_device *cd, const char *device_path);
|
||||
struct crypt_lock_handle *device_write_lock_handle(struct crypt_device *cd, const char *device_path);
|
||||
void device_unlock_handle(struct crypt_device *cd, struct crypt_lock_handle *h);
|
||||
int device_read_lock_internal(struct crypt_device *cd, struct device *device);
|
||||
int device_write_lock_internal(struct crypt_device *cd, struct device *device);
|
||||
void device_unlock_internal(struct crypt_device *cd, struct device *device);
|
||||
|
||||
int device_locked_verify(struct crypt_device *cd, int fd, struct crypt_lock_handle *h);
|
||||
|
||||
/* Used only in device internal allocation */
|
||||
void device_set_lock_handle(struct device *device, struct crypt_lock_handle *h);
|
||||
struct crypt_lock_handle *device_get_lock_handle(struct device *device);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user