mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-13 20:00:08 +01:00
Add arbitrary resource locking (named locks).
It's complementary to current device locking. It'll be used for mutual exclusion of two or more reencryption resume processes
This commit is contained in:
@@ -49,7 +49,8 @@ enum lock_type {
|
|||||||
|
|
||||||
enum lock_mode {
|
enum lock_mode {
|
||||||
DEV_LOCK_FILE = 0,
|
DEV_LOCK_FILE = 0,
|
||||||
DEV_LOCK_BDEV
|
DEV_LOCK_BDEV,
|
||||||
|
DEV_LOCK_NAME
|
||||||
};
|
};
|
||||||
|
|
||||||
struct crypt_lock_handle {
|
struct crypt_lock_handle {
|
||||||
@@ -61,9 +62,24 @@ struct crypt_lock_handle {
|
|||||||
struct {
|
struct {
|
||||||
dev_t devno;
|
dev_t devno;
|
||||||
} bdev;
|
} bdev;
|
||||||
|
struct {
|
||||||
|
char *name;
|
||||||
|
} name;
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int resource_by_name(char *res, size_t res_size, const char *name, bool fullpath)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (fullpath)
|
||||||
|
r = snprintf(res, res_size, "%s/LN_%s", DEFAULT_LUKS2_LOCK_PATH, name);
|
||||||
|
else
|
||||||
|
r = snprintf(res, res_size, "LN_%s", name);
|
||||||
|
|
||||||
|
return (r < 0 || (size_t)r >= res_size) ? -EINVAL : 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int resource_by_devno(char *res, size_t res_size, dev_t devno, unsigned fullpath)
|
static int resource_by_devno(char *res, size_t res_size, dev_t devno, unsigned fullpath)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
@@ -176,11 +192,48 @@ static int acquire_lock_handle(struct crypt_device *cd, struct device *device, s
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int acquire_lock_handle_by_name(struct crypt_device *cd, const char *name, struct crypt_lock_handle *h)
|
||||||
|
{
|
||||||
|
char res[PATH_MAX];
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
h->u.name.name = strdup(name);
|
||||||
|
if (!h->u.name.name)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (resource_by_name(res, sizeof(res), name, false)) {
|
||||||
|
free(h->u.name.name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open_resource(cd, res);
|
||||||
|
if (fd < 0) {
|
||||||
|
free(h->u.name.name);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
h->flock_fd = fd;
|
||||||
|
h->mode = DEV_LOCK_NAME;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void release_lock_handle(struct crypt_device *cd, struct crypt_lock_handle *h)
|
static void release_lock_handle(struct crypt_device *cd, struct crypt_lock_handle *h)
|
||||||
{
|
{
|
||||||
char res[PATH_MAX];
|
char res[PATH_MAX];
|
||||||
struct stat buf_a, buf_b;
|
struct stat buf_a, buf_b;
|
||||||
|
|
||||||
|
if ((h->mode == DEV_LOCK_NAME) && /* was it name lock */
|
||||||
|
!flock(h->flock_fd, LOCK_EX | LOCK_NB) && /* lock to drop the file */
|
||||||
|
!resource_by_name(res, sizeof(res), h->u.name.name, true) && /* acquire lock resource name */
|
||||||
|
!fstat(h->flock_fd, &buf_a) && /* read inode id referred by fd */
|
||||||
|
!stat(res, &buf_b) && /* does path file still exist? */
|
||||||
|
same_inode(buf_a, buf_b)) { /* is it same id as the one referenced by fd? */
|
||||||
|
/* coverity[toctou] */
|
||||||
|
if (unlink(res)) /* yes? unlink the file */
|
||||||
|
log_dbg(cd, "Failed to unlink resource file: %s", res);
|
||||||
|
}
|
||||||
|
|
||||||
if ((h->mode == DEV_LOCK_BDEV) && /* was it block device */
|
if ((h->mode == DEV_LOCK_BDEV) && /* was it block device */
|
||||||
!flock(h->flock_fd, LOCK_EX | LOCK_NB) && /* lock to drop the file */
|
!flock(h->flock_fd, LOCK_EX | LOCK_NB) && /* lock to drop the file */
|
||||||
!resource_by_devno(res, sizeof(res), h->u.bdev.devno, 1) && /* acquire lock resource name */
|
!resource_by_devno(res, sizeof(res), h->u.bdev.devno, 1) && /* acquire lock resource name */
|
||||||
@@ -192,6 +245,9 @@ static void release_lock_handle(struct crypt_device *cd, struct crypt_lock_handl
|
|||||||
log_dbg(cd, "Failed to unlink resource file: %s", res);
|
log_dbg(cd, "Failed to unlink resource file: %s", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (h->mode == DEV_LOCK_NAME)
|
||||||
|
free(h->u.name.name);
|
||||||
|
|
||||||
if (close(h->flock_fd))
|
if (close(h->flock_fd))
|
||||||
log_dbg(cd, "Failed to close lock resource fd (%d).", h->flock_fd);
|
log_dbg(cd, "Failed to close lock resource fd (%d).", h->flock_fd);
|
||||||
}
|
}
|
||||||
@@ -215,7 +271,13 @@ static int verify_lock_handle(const char *device_path, struct crypt_lock_handle
|
|||||||
if (h->mode == DEV_LOCK_FILE)
|
if (h->mode == DEV_LOCK_FILE)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (resource_by_devno(res, sizeof(res), h->u.bdev.devno, 1))
|
if (h->mode == DEV_LOCK_NAME) {
|
||||||
|
if (resource_by_name(res, sizeof(res), h->u.name.name, true))
|
||||||
|
return -EINVAL;
|
||||||
|
} else if (h->mode == DEV_LOCK_BDEV) {
|
||||||
|
if (resource_by_devno(res, sizeof(res), h->u.bdev.devno, true))
|
||||||
|
return -EINVAL;
|
||||||
|
} else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (fstat(h->flock_fd, &lck_st))
|
if (fstat(h->flock_fd, &lck_st))
|
||||||
@@ -236,27 +298,30 @@ static unsigned device_lock_dec(struct crypt_lock_handle *h)
|
|||||||
return --h->refcnt;
|
return --h->refcnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acquire_and_verify(struct crypt_device *cd, struct device *device, int flock_op, struct crypt_lock_handle **lock)
|
static int acquire_and_verify(struct crypt_device *cd, struct device *device, const char *resource, int flock_op, struct crypt_lock_handle **lock)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
struct crypt_lock_handle *h = malloc(sizeof(*h));
|
struct crypt_lock_handle *h = malloc(sizeof(*h));
|
||||||
|
|
||||||
|
if (device && resource)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (!h)
|
if (!h)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
r = acquire_lock_handle(cd, device, h);
|
r = device ? acquire_lock_handle(cd, device, h) : acquire_lock_handle_by_name(cd, resource, h);
|
||||||
if (r)
|
if (r)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (flock(h->flock_fd, flock_op)) {
|
if (flock(h->flock_fd, flock_op)) {
|
||||||
log_dbg(cd, "Flock on fd %d failed with errno %d.", h->flock_fd, errno);
|
log_dbg(cd, "Flock on fd %d failed with errno %d.", h->flock_fd, errno);
|
||||||
r = -EINVAL;
|
r = (errno == EWOULDBLOCK) ? -EBUSY : -EINVAL;
|
||||||
release_lock_handle(cd, h);
|
release_lock_handle(cd, h);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_dbg(cd, "Verifying lock handle for %s.", device_path(device));
|
log_dbg(cd, "Verifying lock handle for %s.", device ? device_path(device) : resource);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check whether another libcryptsetup process removed resource file before this
|
* check whether another libcryptsetup process removed resource file before this
|
||||||
@@ -298,7 +363,7 @@ int device_read_lock_internal(struct crypt_device *cd, struct device *device)
|
|||||||
|
|
||||||
log_dbg(cd, "Acquiring read lock for device %s.", device_path(device));
|
log_dbg(cd, "Acquiring read lock for device %s.", device_path(device));
|
||||||
|
|
||||||
r = acquire_and_verify(cd, device, LOCK_SH, &h);
|
r = acquire_and_verify(cd, device, NULL, LOCK_SH, &h);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@@ -329,7 +394,7 @@ int device_write_lock_internal(struct crypt_device *cd, struct device *device)
|
|||||||
|
|
||||||
log_dbg(cd, "Acquiring write lock for device %s.", device_path(device));
|
log_dbg(cd, "Acquiring write lock for device %s.", device_path(device));
|
||||||
|
|
||||||
r = acquire_and_verify(cd, device, LOCK_EX, &h);
|
r = acquire_and_verify(cd, device, NULL, LOCK_EX, &h);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@@ -342,23 +407,92 @@ int device_write_lock_internal(struct crypt_device *cd, struct device *device)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int crypt_read_lock(struct crypt_device *cd, const char *resource, bool blocking, struct crypt_lock_handle **lock)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct crypt_lock_handle *h;
|
||||||
|
|
||||||
|
if (!resource)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
log_dbg(cd, "Acquiring %sblocking read lock for resource %s.", blocking ? "" : "non", resource);
|
||||||
|
|
||||||
|
r = acquire_and_verify(cd, NULL, resource, LOCK_SH | (blocking ? 0 : LOCK_NB), &h);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
h->type = DEV_LOCK_READ;
|
||||||
|
h->refcnt = 1;
|
||||||
|
|
||||||
|
log_dbg(cd, "READ lock for resource %s taken.", resource);
|
||||||
|
|
||||||
|
*lock = h;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int crypt_write_lock(struct crypt_device *cd, const char *resource, bool blocking, struct crypt_lock_handle **lock)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct crypt_lock_handle *h;
|
||||||
|
|
||||||
|
if (!resource)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
log_dbg(cd, "Acquiring %sblocking write lock for resource %s.", blocking ? "" : "non", resource);
|
||||||
|
|
||||||
|
r = acquire_and_verify(cd, NULL, resource, LOCK_EX | (blocking ? 0 : LOCK_NB), &h);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
h->type = DEV_LOCK_WRITE;
|
||||||
|
h->refcnt = 1;
|
||||||
|
|
||||||
|
log_dbg(cd, "WRITE lock for resource %s taken.", resource);
|
||||||
|
|
||||||
|
*lock = h;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unlock_internal(struct crypt_device *cd, struct crypt_lock_handle *h)
|
||||||
|
{
|
||||||
|
if (flock(h->flock_fd, LOCK_UN))
|
||||||
|
log_dbg(cd, "flock on fd %d failed.", h->flock_fd);
|
||||||
|
release_lock_handle(cd, h);
|
||||||
|
free(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void crypt_unlock_internal(struct crypt_device *cd, struct crypt_lock_handle *h)
|
||||||
|
{
|
||||||
|
if (!h)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* nested locks are illegal */
|
||||||
|
assert(!device_lock_dec(h));
|
||||||
|
|
||||||
|
log_dbg(cd, "Unlocking %s lock for resource %s.",
|
||||||
|
device_locked_readonly(h) ? "READ" : "WRITE", h->u.name.name);
|
||||||
|
|
||||||
|
unlock_internal(cd, h);
|
||||||
|
}
|
||||||
|
|
||||||
void device_unlock_internal(struct crypt_device *cd, struct device *device)
|
void device_unlock_internal(struct crypt_device *cd, struct device *device)
|
||||||
{
|
{
|
||||||
|
bool readonly;
|
||||||
struct crypt_lock_handle *h = device_get_lock_handle(device);
|
struct crypt_lock_handle *h = device_get_lock_handle(device);
|
||||||
unsigned u = device_lock_dec(h);
|
unsigned u = device_lock_dec(h);
|
||||||
|
|
||||||
if (u)
|
if (u)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (flock(h->flock_fd, LOCK_UN))
|
readonly = device_locked_readonly(h);
|
||||||
log_dbg(cd, "flock on fd %d failed.", h->flock_fd);
|
|
||||||
|
|
||||||
release_lock_handle(cd, h);
|
unlock_internal(cd, h);
|
||||||
|
|
||||||
log_dbg(cd, "Device %s %s lock released.", device_path(device),
|
log_dbg(cd, "Device %s %s lock released.", device_path(device),
|
||||||
device_locked_readonly(h) ? "READ" : "WRITE");
|
readonly ? "READ" : "WRITE");
|
||||||
|
|
||||||
free(h);
|
|
||||||
device_set_lock_handle(device, NULL);
|
device_set_lock_handle(device, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ 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);
|
||||||
|
|
||||||
|
int crypt_read_lock(struct crypt_device *cd, const char *name, bool blocking, struct crypt_lock_handle **lock);
|
||||||
|
int crypt_write_lock(struct crypt_device *cd, const char *name, bool blocking, struct crypt_lock_handle **lock);
|
||||||
|
void crypt_unlock_internal(struct crypt_device *cd, struct crypt_lock_handle *h);
|
||||||
|
|
||||||
|
|
||||||
/* Used only in device internal allocation */
|
/* Used only in device internal allocation */
|
||||||
void device_set_lock_handle(struct device *device, struct crypt_lock_handle *h);
|
void device_set_lock_handle(struct device *device, struct crypt_lock_handle *h);
|
||||||
struct crypt_lock_handle *device_get_lock_handle(struct device *device);
|
struct crypt_lock_handle *device_get_lock_handle(struct device *device);
|
||||||
|
|||||||
Reference in New Issue
Block a user