mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-05 16:00:05 +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);
|
close(device->loop_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert (!device_locked(device->lh));
|
assert(!device_locked(device->lh));
|
||||||
|
|
||||||
free(device->file_path);
|
free(device->file_path);
|
||||||
free(device->path);
|
free(device->path);
|
||||||
@@ -829,21 +829,25 @@ size_t device_alignment(struct device *device)
|
|||||||
return device->alignment;
|
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)
|
int device_read_lock(struct crypt_device *cd, struct device *device)
|
||||||
{
|
{
|
||||||
if (!crypt_metadata_locking_enabled())
|
if (!crypt_metadata_locking_enabled())
|
||||||
return 0;
|
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));
|
return 0;
|
||||||
|
|
||||||
if (device_locked(device->lh)) {
|
|
||||||
log_dbg(cd, "Device %s READ lock taken.", device_path(device));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int device_write_lock(struct crypt_device *cd, struct device *device)
|
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())
|
if (!crypt_metadata_locking_enabled())
|
||||||
return 0;
|
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)) {
|
return 0;
|
||||||
log_dbg(cd, "Device %s WRITE lock taken.", device_path(device));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_read_unlock(struct crypt_device *cd, struct device *device)
|
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())
|
if (!crypt_metadata_locking_enabled())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(device_locked(device->lh) && device_locked_readonly(device->lh));
|
assert(device_locked(device->lh));
|
||||||
|
|
||||||
device_unlock_handle(cd, device->lh);
|
device_unlock_internal(cd, device);
|
||||||
|
|
||||||
log_dbg(cd, "Device %s READ lock released.", device_path(device));
|
|
||||||
|
|
||||||
device->lh = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_write_unlock(struct crypt_device *cd, struct device *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));
|
assert(device_locked(device->lh) && !device_locked_readonly(device->lh));
|
||||||
|
|
||||||
device_unlock_handle(cd, device->lh);
|
device_unlock_internal(cd, device);
|
||||||
|
|
||||||
log_dbg(cd, "Device %s WRITE lock released.", device_path(device));
|
|
||||||
|
|
||||||
device->lh = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool device_is_locked(struct device *device)
|
bool device_is_locked(struct device *device)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
# include <sys/sysmacros.h> /* for major, minor */
|
# include <sys/sysmacros.h> /* for major, minor */
|
||||||
#endif
|
#endif
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "utils_device_locking.h"
|
#include "utils_device_locking.h"
|
||||||
@@ -52,6 +53,7 @@ enum lock_type {
|
|||||||
|
|
||||||
struct crypt_lock_handle {
|
struct crypt_lock_handle {
|
||||||
dev_t devno;
|
dev_t devno;
|
||||||
|
unsigned refcnt;
|
||||||
int flock_fd;
|
int flock_fd;
|
||||||
enum lock_type type;
|
enum lock_type type;
|
||||||
__typeof__( ((struct stat*)0)->st_mode) mode;
|
__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))
|
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)
|
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;
|
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;
|
int r;
|
||||||
struct crypt_lock_handle *h = malloc(sizeof(*h));
|
struct crypt_lock_handle *h;
|
||||||
|
|
||||||
if (!h)
|
if (!device)
|
||||||
return NULL;
|
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 {
|
do {
|
||||||
r = acquire_lock_handle(cd, device_path, h);
|
r = acquire_lock_handle(cd, device_path(device), h);
|
||||||
if (r)
|
if (r)
|
||||||
break;
|
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)) {
|
if (flock(h->flock_fd, LOCK_SH)) {
|
||||||
log_dbg(cd, "Shared flock failed with errno %d.", errno);
|
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;
|
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
|
* check whether another libcryptsetup process removed resource file before this
|
||||||
* one managed to flock() it. See release_lock_handle() for details
|
* 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) {
|
if (r) {
|
||||||
flock(h->flock_fd, LOCK_UN);
|
flock(h->flock_fd, LOCK_UN);
|
||||||
release_lock_handle(cd, h);
|
release_lock_handle(cd, h);
|
||||||
@@ -255,28 +280,43 @@ struct crypt_lock_handle *device_read_lock_handle(struct crypt_device *cd, const
|
|||||||
|
|
||||||
if (r) {
|
if (r) {
|
||||||
free(h);
|
free(h);
|
||||||
return NULL;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
h->type = DEV_LOCK_READ;
|
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;
|
int r;
|
||||||
struct crypt_lock_handle *h = malloc(sizeof(*h));
|
struct crypt_lock_handle *h;
|
||||||
|
|
||||||
if (!h)
|
if (!device)
|
||||||
return NULL;
|
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 {
|
do {
|
||||||
r = acquire_lock_handle(cd, device_path, h);
|
r = acquire_lock_handle(cd, device_path(device), h);
|
||||||
if (r)
|
if (r)
|
||||||
break;
|
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)) {
|
if (flock(h->flock_fd, LOCK_EX)) {
|
||||||
log_dbg(cd, "Exclusive flock failed with errno %d.", errno);
|
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;
|
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
|
* check whether another libcryptsetup process removed resource file before this
|
||||||
* one managed to flock() it. See release_lock_handle() for details
|
* 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) {
|
if (r) {
|
||||||
flock(h->flock_fd, LOCK_UN);
|
flock(h->flock_fd, LOCK_UN);
|
||||||
release_lock_handle(cd, h);
|
release_lock_handle(cd, h);
|
||||||
@@ -301,22 +341,36 @@ struct crypt_lock_handle *device_write_lock_handle(struct crypt_device *cd, cons
|
|||||||
|
|
||||||
if (r) {
|
if (r) {
|
||||||
free(h);
|
free(h);
|
||||||
return NULL;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
h->type = DEV_LOCK_WRITE;
|
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))
|
if (flock(h->flock_fd, LOCK_UN))
|
||||||
log_dbg(cd, "flock on fd %d failed.", h->flock_fd);
|
log_dbg(cd, "flock on fd %d failed.", h->flock_fd);
|
||||||
|
|
||||||
release_lock_handle(cd, h);
|
release_lock_handle(cd, h);
|
||||||
|
|
||||||
|
log_dbg(cd, "Device %s %s lock released.", device_path(device),
|
||||||
|
device_locked_readonly(h) ? "READ" : "WRITE");
|
||||||
|
|
||||||
free(h);
|
free(h);
|
||||||
|
device_set_lock_handle(device, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int device_locked_verify(struct crypt_device *cd, int dev_fd, struct crypt_lock_handle *h)
|
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_device;
|
||||||
struct crypt_lock_handle;
|
struct crypt_lock_handle;
|
||||||
|
struct device;
|
||||||
|
|
||||||
int device_locked_readonly(struct crypt_lock_handle *h);
|
int device_locked_readonly(struct crypt_lock_handle *h);
|
||||||
int device_locked(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);
|
int device_read_lock_internal(struct crypt_device *cd, struct device *device);
|
||||||
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);
|
||||||
void device_unlock_handle(struct crypt_device *cd, struct crypt_lock_handle *h);
|
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);
|
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
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user