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:
Ondrej Kozina
2019-02-13 18:22:32 +01:00
committed by Milan Broz
parent 3023f26911
commit 88b3924132
3 changed files with 105 additions and 54 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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