Commit 32d5e59a authored by Milan Broz's avatar Milan Broz

Implement deferred removal of device.

This can be used in some automated systems and allows device
to be removed after the last user mapping closes it.
parent a58ed1ad
...@@ -294,6 +294,7 @@ LIBS=$saved_LIBS ...@@ -294,6 +294,7 @@ LIBS=$saved_LIBS
LIBS="$LIBS $DEVMAPPER_LIBS" LIBS="$LIBS $DEVMAPPER_LIBS"
AC_CHECK_DECLS([dm_task_secure_data], [], [], [#include <libdevmapper.h>]) AC_CHECK_DECLS([dm_task_secure_data], [], [], [#include <libdevmapper.h>])
AC_CHECK_DECLS([dm_task_retry_remove], [], [], [#include <libdevmapper.h>]) AC_CHECK_DECLS([dm_task_retry_remove], [], [], [#include <libdevmapper.h>])
AC_CHECK_DECLS([dm_task_deferred_remove], [], [], [#include <libdevmapper.h>])
AC_CHECK_DECLS([DM_UDEV_DISABLE_DISK_RULES_FLAG], [have_cookie=yes], [have_cookie=no], [#include <libdevmapper.h>]) AC_CHECK_DECLS([DM_UDEV_DISABLE_DISK_RULES_FLAG], [have_cookie=yes], [have_cookie=no], [#include <libdevmapper.h>])
if test "x$enable_udev" = xyes; then if test "x$enable_udev" = xyes; then
if test "x$have_cookie" = xno; then if test "x$have_cookie" = xno; then
......
...@@ -274,5 +274,5 @@ int INTEGRITY_format(struct crypt_device *cd, ...@@ -274,5 +274,5 @@ int INTEGRITY_format(struct crypt_device *cd,
if (r) if (r)
return r; return r;
return dm_remove_device(cd, tmp_name, 1, dmdi.size); return dm_remove_device(cd, tmp_name, CRYPT_DEACTIVATE_FORCE|CRYPT_DEACTIVATE_DEFERRED);
} }
...@@ -812,6 +812,11 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, ...@@ -812,6 +812,11 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
size_t volume_key_size, size_t volume_key_size,
uint32_t flags); uint32_t flags);
/** lazy deactivation - remove once last user releases it */
#define CRYPT_DEACTIVATE_DEFERRED (1 << 0)
/** force deactivation - if the device is busy, it is replaced by error device */
#define CRYPT_DEACTIVATE_FORCE (1 << 1)
/** /**
* Deactivate crypt device. This function tries to remove active device-mapper * Deactivate crypt device. This function tries to remove active device-mapper
* mapping from kernel. Also, sensitive data like the volume key are removed from * mapping from kernel. Also, sensitive data like the volume key are removed from
...@@ -819,10 +824,18 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, ...@@ -819,10 +824,18 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
* *
* @param cd crypt device handle, can be @e NULL * @param cd crypt device handle, can be @e NULL
* @param name name of device to deactivate * @param name name of device to deactivate
* @param flags deactivation flags
* *
* @return @e 0 on success or negative errno value otherwise. * @return @e 0 on success or negative errno value otherwise.
* *
*/ */
int crypt_deactivate_by_name(struct crypt_device *cd,
const char *name,
uint32_t flags);
/**
* Deactivate crypt device. See @ref crypt_deactivate_by_name with empty @e flags.
*/
int crypt_deactivate(struct crypt_device *cd, const char *name); int crypt_deactivate(struct crypt_device *cd, const char *name);
/** /**
......
...@@ -31,6 +31,7 @@ CRYPTSETUP_2.0 { ...@@ -31,6 +31,7 @@ CRYPTSETUP_2.0 {
crypt_activate_by_keyfile_offset; crypt_activate_by_keyfile_offset;
crypt_activate_by_volume_key; crypt_activate_by_volume_key;
crypt_deactivate; crypt_deactivate;
crypt_deactivate_by_name;
crypt_volume_key_get; crypt_volume_key_get;
crypt_volume_key_verify; crypt_volume_key_verify;
crypt_status; crypt_status;
......
...@@ -72,6 +72,8 @@ static int _dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t f ...@@ -72,6 +72,8 @@ static int _dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t f
static int _dm_udev_wait(uint32_t cookie) { return 0; }; static int _dm_udev_wait(uint32_t cookie) { return 0; };
#endif #endif
/* FIXME: We should use DM_UDEV_DISABLE_LIBRARY_FALLBACK */
static int _dm_use_udev(void) static int _dm_use_udev(void)
{ {
#ifdef USE_UDEV /* cannot be enabled if devmapper is too old */ #ifdef USE_UDEV /* cannot be enabled if devmapper is too old */
...@@ -642,7 +644,7 @@ static char *get_dm_integrity_params(struct crypt_dm_active_device *dmd, uint32_ ...@@ -642,7 +644,7 @@ static char *get_dm_integrity_params(struct crypt_dm_active_device *dmd, uint32_
} }
/* DM helpers */ /* DM helpers */
static int _dm_simple(int task, const char *name, int udev_wait) static int _dm_remove(const char *name, int udev_wait, int deferred)
{ {
int r = 0; int r = 0;
struct dm_task *dmt; struct dm_task *dmt;
...@@ -651,15 +653,18 @@ static int _dm_simple(int task, const char *name, int udev_wait) ...@@ -651,15 +653,18 @@ static int _dm_simple(int task, const char *name, int udev_wait)
if (!_dm_use_udev()) if (!_dm_use_udev())
udev_wait = 0; udev_wait = 0;
if (!(dmt = dm_task_create(task))) if (!(dmt = dm_task_create(DM_DEVICE_REMOVE)))
return 0; return 0;
if (name && !dm_task_set_name(dmt, name)) if (!dm_task_set_name(dmt, name))
goto out; goto out;
#if HAVE_DECL_DM_TASK_RETRY_REMOVE #if HAVE_DECL_DM_TASK_RETRY_REMOVE
/* Used only in DM_DEVICE_REMOVE */ if (!dm_task_retry_remove(dmt))
if (name && !dm_task_retry_remove(dmt)) goto out;
#endif
#if HAVE_DECL_DM_TASK_DEFERRED_REMOVE
if (deferred && !dm_task_deferred_remove(dmt))
goto out; goto out;
#endif #endif
if (udev_wait && !_dm_task_set_cookie(dmt, &cookie, 0)) if (udev_wait && !_dm_task_set_cookie(dmt, &cookie, 0))
...@@ -669,7 +674,33 @@ static int _dm_simple(int task, const char *name, int udev_wait) ...@@ -669,7 +674,33 @@ static int _dm_simple(int task, const char *name, int udev_wait)
if (udev_wait) if (udev_wait)
(void)_dm_udev_wait(cookie); (void)_dm_udev_wait(cookie);
out:
dm_task_destroy(dmt);
return r;
}
static int _dm_simple(int task, const char *name, int udev_wait)
{
int r = 0;
struct dm_task *dmt;
uint32_t cookie = 0;
if (!_dm_use_udev())
udev_wait = 0;
if (!(dmt = dm_task_create(task)))
return 0;
if (name && !dm_task_set_name(dmt, name))
goto out;
if (udev_wait && !_dm_task_set_cookie(dmt, &cookie, 0))
goto out;
r = dm_task_run(dmt);
if (udev_wait)
(void)_dm_udev_wait(cookie);
out: out:
dm_task_destroy(dmt); dm_task_destroy(dmt);
return r; return r;
...@@ -710,35 +741,39 @@ error: ...@@ -710,35 +741,39 @@ error:
return r; return r;
} }
int dm_remove_device(struct crypt_device *cd, const char *name, int dm_remove_device(struct crypt_device *cd, const char *name, uint32_t flags)
int force, uint64_t size)
{ {
struct crypt_dm_active_device dmd = {};
int r = -EINVAL; int r = -EINVAL;
int retries = force ? RETRY_COUNT : 1; int retries = (flags & CRYPT_DEACTIVATE_FORCE) ? RETRY_COUNT : 1;
int error_target = 0; int error_target = 0;
if (!name || (force && !size)) if (!name)
return -EINVAL; return -EINVAL;
if (dm_init_context(cd, DM_UNKNOWN)) if (dm_init_context(cd, DM_UNKNOWN))
return -ENOTSUP; return -ENOTSUP;
if (flags & CRYPT_DEACTIVATE_FORCE) {
r = dm_query_device(cd, name, 0, &dmd);
if (!r)
return r;
}
do { do {
r = _dm_simple(DM_DEVICE_REMOVE, name, 1) ? 0 : -EINVAL; r = _dm_remove(name, 1, flags & CRYPT_DEACTIVATE_DEFERRED) ? 0 : -EINVAL;
if (--retries && r) { if (--retries && r) {
log_dbg("WARNING: other process locked internal device %s, %s.", log_dbg("WARNING: other process locked internal device %s, %s.",
name, retries ? "retrying remove" : "giving up"); name, retries ? "retrying remove" : "giving up");
sleep(1); sleep(1);
if (force && !error_target) { if ((flags & CRYPT_DEACTIVATE_FORCE) && !error_target) {
/* If force flag is set, replace device with error, read-only target. /* If force flag is set, replace device with error, read-only target.
* it should stop processes from reading it and also removed underlying * it should stop processes from reading it and also removed underlying
* device from mapping, so it is usable again. * device from mapping, so it is usable again.
* Force flag should be used only for temporary devices, which are
* intended to work inside cryptsetup only!
* Anyway, if some process try to read temporary cryptsetup device, * Anyway, if some process try to read temporary cryptsetup device,
* it is bug - no other process should try touch it (e.g. udev). * it is bug - no other process should try touch it (e.g. udev).
*/ */
_error_device(name, size); _error_device(name, dmd.size);
error_target = 1; error_target = 1;
} }
} }
...@@ -888,7 +923,7 @@ out: ...@@ -888,7 +923,7 @@ out:
} }
if (r < 0 && !reload) if (r < 0 && !reload)
_dm_simple(DM_DEVICE_REMOVE, name, 1); _dm_remove(name, 1, 0);
out_no_removal: out_no_removal:
if (cookie && _dm_use_udev()) if (cookie && _dm_use_udev())
......
...@@ -124,7 +124,7 @@ static int LUKS_endec_template(char *src, size_t srcLength, ...@@ -124,7 +124,7 @@ static int LUKS_endec_template(char *src, size_t srcLength,
out: out:
if (devfd != -1) if (devfd != -1)
close(devfd); close(devfd);
dm_remove_device(ctx, name, 1, dmd.size); dm_remove_device(ctx, name, CRYPT_DEACTIVATE_FORCE|CRYPT_DEACTIVATE_DEFERRED);
return r; return r;
} }
......
...@@ -2265,7 +2265,7 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, ...@@ -2265,7 +2265,7 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
return r; return r;
} }
int crypt_deactivate(struct crypt_device *cd, const char *name) int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t flags)
{ {
struct crypt_device *fake_cd = NULL; struct crypt_device *fake_cd = NULL;
int r; int r;
...@@ -2286,9 +2286,9 @@ int crypt_deactivate(struct crypt_device *cd, const char *name) ...@@ -2286,9 +2286,9 @@ int crypt_deactivate(struct crypt_device *cd, const char *name)
case CRYPT_ACTIVE: case CRYPT_ACTIVE:
case CRYPT_BUSY: case CRYPT_BUSY:
if (isTCRYPT(cd->type)) if (isTCRYPT(cd->type))
r = TCRYPT_deactivate(cd, name); r = TCRYPT_deactivate(cd, name, flags);
else else
r = dm_remove_device(cd, name, 0, 0); r = dm_remove_device(cd, name, flags);
if (r < 0 && crypt_status(cd, name) == CRYPT_BUSY) { if (r < 0 && crypt_status(cd, name) == CRYPT_BUSY) {
log_err(cd, _("Device %s is still in use.\n"), name); log_err(cd, _("Device %s is still in use.\n"), name);
r = -EBUSY; r = -EBUSY;
...@@ -2308,6 +2308,11 @@ int crypt_deactivate(struct crypt_device *cd, const char *name) ...@@ -2308,6 +2308,11 @@ int crypt_deactivate(struct crypt_device *cd, const char *name)
return r; return r;
} }
int crypt_deactivate(struct crypt_device *cd, const char *name)
{
return crypt_deactivate_by_name(cd, name, 0);
}
int crypt_volume_key_get(struct crypt_device *cd, int crypt_volume_key_get(struct crypt_device *cd,
int keyslot, int keyslot,
char *volume_key, char *volume_key,
......
...@@ -831,7 +831,7 @@ int TCRYPT_activate(struct crypt_device *cd, ...@@ -831,7 +831,7 @@ int TCRYPT_activate(struct crypt_device *cd,
} }
static int TCRYPT_remove_one(struct crypt_device *cd, const char *name, static int TCRYPT_remove_one(struct crypt_device *cd, const char *name,
const char *base_uuid, int index) const char *base_uuid, int index, uint32_t flags)
{ {
struct crypt_dm_active_device dmd = {}; struct crypt_dm_active_device dmd = {};
char dm_name[PATH_MAX]; char dm_name[PATH_MAX];
...@@ -846,13 +846,13 @@ static int TCRYPT_remove_one(struct crypt_device *cd, const char *name, ...@@ -846,13 +846,13 @@ static int TCRYPT_remove_one(struct crypt_device *cd, const char *name,
r = dm_query_device(cd, dm_name, DM_ACTIVE_UUID, &dmd); r = dm_query_device(cd, dm_name, DM_ACTIVE_UUID, &dmd);
if (!r && !strncmp(dmd.uuid, base_uuid, strlen(base_uuid))) if (!r && !strncmp(dmd.uuid, base_uuid, strlen(base_uuid)))
r = dm_remove_device(cd, dm_name, 0, 0); r = dm_remove_device(cd, dm_name, flags);
free(CONST_CAST(void*)dmd.uuid); free(CONST_CAST(void*)dmd.uuid);
return r; return r;
} }
int TCRYPT_deactivate(struct crypt_device *cd, const char *name) int TCRYPT_deactivate(struct crypt_device *cd, const char *name, uint32_t flags)
{ {
struct crypt_dm_active_device dmd = {}; struct crypt_dm_active_device dmd = {};
int r; int r;
...@@ -863,15 +863,15 @@ int TCRYPT_deactivate(struct crypt_device *cd, const char *name) ...@@ -863,15 +863,15 @@ int TCRYPT_deactivate(struct crypt_device *cd, const char *name)
if (!dmd.uuid) if (!dmd.uuid)
return -EINVAL; return -EINVAL;
r = dm_remove_device(cd, name, 0, 0); r = dm_remove_device(cd, name, flags);
if (r < 0) if (r < 0)
goto out; goto out;
r = TCRYPT_remove_one(cd, name, dmd.uuid, 1); r = TCRYPT_remove_one(cd, name, dmd.uuid, 1, flags);
if (r < 0) if (r < 0)
goto out; goto out;
r = TCRYPT_remove_one(cd, name, dmd.uuid, 2); r = TCRYPT_remove_one(cd, name, dmd.uuid, 2, flags);
if (r < 0) if (r < 0)
goto out; goto out;
out: out:
......
...@@ -93,7 +93,8 @@ int TCRYPT_activate(struct crypt_device *cd, ...@@ -93,7 +93,8 @@ int TCRYPT_activate(struct crypt_device *cd,
uint32_t flags); uint32_t flags);
int TCRYPT_deactivate(struct crypt_device *cd, int TCRYPT_deactivate(struct crypt_device *cd,
const char *name); const char *name,
uint32_t flags);
uint64_t TCRYPT_get_data_offset(struct crypt_device *cd, uint64_t TCRYPT_get_data_offset(struct crypt_device *cd,
struct tcrypt_phdr *hdr, struct tcrypt_phdr *hdr,
......
...@@ -121,8 +121,7 @@ struct crypt_dm_active_device { ...@@ -121,8 +121,7 @@ struct crypt_dm_active_device {
void dm_backend_init(void); void dm_backend_init(void);
void dm_backend_exit(void); void dm_backend_exit(void);
int dm_remove_device(struct crypt_device *cd, const char *name, int dm_remove_device(struct crypt_device *cd, const char *name, uint32_t flags);
int force, uint64_t size);
int dm_status_device(struct crypt_device *cd, const char *name); int dm_status_device(struct crypt_device *cd, const char *name);
int dm_status_suspended(struct crypt_device *cd, const char *name); int dm_status_suspended(struct crypt_device *cd, const char *name);
int dm_status_verity_ok(struct crypt_device *cd, const char *name); int dm_status_verity_ok(struct crypt_device *cd, const char *name);
......
...@@ -903,6 +903,9 @@ password quality checking support. ...@@ -903,6 +903,9 @@ password quality checking support.
For more info about password quality check, see manual page For more info about password quality check, see manual page
for \fBpwquality.conf(5)\fR and \fBpasswdqc.conf(5)\fR. for \fBpwquality.conf(5)\fR and \fBpasswdqc.conf(5)\fR.
.TP .TP
.B "\-\-deferred"
Defers device removal in \fIclose\fR command until the last user closes it.
.TP
.B "\-\-version" .B "\-\-version"
Show the program version. Show the program version.
.TP .TP
......
...@@ -67,6 +67,7 @@ static int opt_tcrypt_backup = 0; ...@@ -67,6 +67,7 @@ static int opt_tcrypt_backup = 0;
static int opt_veracrypt = 0; static int opt_veracrypt = 0;
static int opt_veracrypt_pim = -1; static int opt_veracrypt_pim = -1;
static int opt_veracrypt_query_pim = 0; static int opt_veracrypt_query_pim = 0;
static int opt_deferred_remove = 0;
static const char **action_argv; static const char **action_argv;
static int action_argc; static int action_argc;
...@@ -421,11 +422,15 @@ out: ...@@ -421,11 +422,15 @@ out:
static int action_close(void) static int action_close(void)
{ {
struct crypt_device *cd = NULL; struct crypt_device *cd = NULL;
uint32_t flags = 0;
int r; int r;
if (opt_deferred_remove)
flags |= CRYPT_DEACTIVATE_DEFERRED;
r = crypt_init_by_name(&cd, action_argv[0]); r = crypt_init_by_name(&cd, action_argv[0]);
if (r == 0) if (r == 0)
r = crypt_deactivate(cd, action_argv[0]); r = crypt_deactivate_by_name(cd, action_argv[0], flags);
crypt_free(cd); crypt_free(cd);
return r; return r;
...@@ -1572,6 +1577,7 @@ int main(int argc, const char **argv) ...@@ -1572,6 +1577,7 @@ int main(int argc, const char **argv)
{ "force-password", '\0', POPT_ARG_NONE, &opt_force_password, 0, N_("Disable password quality check (if enabled)."), NULL }, { "force-password", '\0', POPT_ARG_NONE, &opt_force_password, 0, N_("Disable password quality check (if enabled)."), NULL },
{ "perf-same_cpu_crypt",'\0', POPT_ARG_NONE, &opt_perf_same_cpu_crypt, 0, N_("Use dm-crypt same_cpu_crypt performance compatibility option."), NULL }, { "perf-same_cpu_crypt",'\0', POPT_ARG_NONE, &opt_perf_same_cpu_crypt, 0, N_("Use dm-crypt same_cpu_crypt performance compatibility option."), NULL },
{ "perf-submit_from_crypt_cpus",'\0', POPT_ARG_NONE, &opt_perf_submit_from_crypt_cpus,0,N_("Use dm-crypt submit_from_crypt_cpus performance compatibility option."), NULL }, { "perf-submit_from_crypt_cpus",'\0', POPT_ARG_NONE, &opt_perf_submit_from_crypt_cpus,0,N_("Use dm-crypt submit_from_crypt_cpus performance compatibility option."), NULL },
{ "deferred", '\0', POPT_ARG_NONE, &opt_deferred_remove, 0, N_("Device removal is deferred until the last user closes it."), NULL },
POPT_TABLEEND POPT_TABLEEND
}; };
poptContext popt_context; poptContext popt_context;
...@@ -1699,6 +1705,11 @@ int main(int argc, const char **argv) ...@@ -1699,6 +1705,11 @@ int main(int argc, const char **argv)
/* FIXME: rewrite this from scratch */ /* FIXME: rewrite this from scratch */
if (opt_deferred_remove && strcmp(aname, "close"))
usage(popt_context, EXIT_FAILURE,
_("Option --deferred is allowed only for close command.\n"),
poptGetInvocationName(popt_context));
if (opt_shared && (strcmp(aname, "open") || strcmp(opt_type, "plain")) ) if (opt_shared && (strcmp(aname, "open") || strcmp(opt_type, "plain")) )
usage(popt_context, EXIT_FAILURE, usage(popt_context, EXIT_FAILURE,
_("Option --shared is allowed only for open of plain device.\n"), _("Option --shared is allowed only for open of plain device.\n"),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment