#define DEVICE_DIR "/dev"
#define CRYPT_TARGET "crypt"
-
-#define UDEVSETTLE "/sbin/udevsettle"
-
-static void run_udevsettle(void)
-{
- system(UDEVSETTLE);
-}
+#define RETRY_COUNT 5
static void set_dm_error(int level, const char *file, int line,
const char *f, ...)
dm_lib_release();
}
-static void flush_dm_workqueue(void)
-{
- /*
- * Unfortunately this is the only way to trigger libdevmapper's
- * update_nodes function
- */
- dm_exit();
- dm_init();
-}
-
static char *__lookup_dev(char *path, dev_t dev)
{
struct dirent *entry;
return params;
}
+/* DM helpers */
+static int _dm_simple(int task, const char *name)
+{
+ int r = 0;
+ struct dm_task *dmt;
+
+ if (!(dmt = dm_task_create(task)))
+ return 0;
+
+ if (!dm_task_set_name(dmt, name))
+ goto out;
+
+ r = dm_task_run(dmt);
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _error_device(struct crypt_options *options)
+{
+ struct dm_task *dmt;
+ int r = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
+ return 0;
+
+ if (!dm_task_set_name(dmt, options->name))
+ goto error;
+
+ if (!dm_task_add_target(dmt, UINT64_C(0), options->size, "error", ""))
+ goto error;
+
+ if (!dm_task_set_ro(dmt))
+ goto error;
+
+ if (!dm_task_no_open_count(dmt))
+ goto error;
+
+ if (!dm_task_run(dmt))
+ goto error;
+
+ if (!_dm_simple(DM_DEVICE_RESUME, options->name)) {
+ _dm_simple(DM_DEVICE_CLEAR, options->name);
+ goto error;
+ }
+
+ r = 1;
+
+error:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _dm_remove(struct crypt_options *options, int force)
+{
+ int r = -EINVAL;
+ int retries = force ? RETRY_COUNT : 1;
+
+ /* If force flag is set, replace device with error, read-only target.
+ * it should stop processes from reading it and also removed underlying
+ * 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,
+ * it is bug - no other process should try touch it (e.g. udev).
+ */
+ if (force) {
+ _error_device(options);
+ retries = RETRY_COUNT;
+ }
+
+ do {
+ r = _dm_simple(DM_DEVICE_REMOVE, options->name) ? 0 : -EINVAL;
+ if (--retries)
+ sleep(1);
+ } while (r == -EINVAL && retries);
+
+ dm_task_update_nodes();
+
+ return r;
+}
+
static int dm_create_device(int reload, struct crypt_options *options,
const char *key)
{
if (dmi.read_only)
options->flags |= CRYPT_FLAG_READONLY;
- /* run udevsettle to avoid a race in libdevmapper causing busy dm devices */
- run_udevsettle();
-
r = 0;
-
out:
if (r < 0 && !reload) {
char *error = (char *)get_error();
if (error)
error = strdup(error);
- if (dmt)
- dm_task_destroy(dmt);
- if (!(dmt = dm_task_create(DM_DEVICE_REMOVE)))
- goto out_restore_error;
- if (!dm_task_set_name(dmt, options->name))
- goto out_restore_error;
- if (!dm_task_run(dmt))
+ if (!_dm_remove(options, 0))
goto out_restore_error;
out_restore_error:
dm_task_destroy(dmt);
if(dmt_query)
dm_task_destroy(dmt_query);
- flush_dm_workqueue();
+ dm_task_update_nodes();
return r;
}
return r;
}
-static int dm_remove_device(struct crypt_options *options)
+static int dm_remove_device(int force, struct crypt_options *options)
{
- struct dm_task *dmt;
- int r = -EINVAL;
+ if (!options || !options->name)
+ return -EINVAL;
- if (!(dmt = dm_task_create(DM_DEVICE_REMOVE)))
- goto out;
- if (!dm_task_set_name(dmt, options->name))
- goto out;
- if (!dm_task_run(dmt))
- goto out;
-
- r = 0;
-
-out:
- if (dmt)
- dm_task_destroy(dmt);
- flush_dm_workqueue();
- return r;
+ return _dm_remove(options, force);;
}
return div_round_up(x, m) * m;
}
+static struct setup_backend *cleaner_backend=NULL;
+static const char *cleaner_name=NULL;
+static uint64_t cleaner_size = 0;
+static int devfd=-1;
+
static int setup_mapping(const char *cipher, const char *name,
const char *device, unsigned int payloadOffset,
const char *key, size_t keyLength,
struct setup_backend *backend,
int mode)
{
- struct crypt_options k;
+ struct crypt_options k = {0};
struct crypt_options *options = &k;
int device_sector_size = sector_size_for_device(device);
int r;
return -EINVAL;
}
options->size = round_up_modulo(srcLength,device_sector_size)/SECTOR_SIZE;
+ cleaner_size = options->size;
options->offset = sector;
options->cipher = cipher;
return r;
}
-static int clear_mapping(const char *name, struct setup_backend *backend)
+static int clear_mapping(const char *name, uint64_t size, struct setup_backend *backend)
{
- struct crypt_options options;
+ struct crypt_options options = {0};
options.name=name;
- return backend->remove(&options);
+ options.size = size;
+ return backend->remove(1, &options);
}
-/* I miss closures in C! */
-static struct setup_backend *cleaner_backend=NULL;
-static const char *cleaner_name=NULL;
-static int devfd=-1;
-
static void sigint_handler(int sig)
{
if(devfd >= 0)
close(devfd);
devfd = -1;
if(cleaner_backend && cleaner_name)
- clear_mapping(cleaner_name, cleaner_backend);
+ clear_mapping(cleaner_name, cleaner_size, cleaner_backend);
signal(SIGINT, SIG_DFL);
kill(getpid(), SIGINT);
}
close(devfd);
devfd = -1;
out2:
- clear_mapping(name,backend);
+ clear_mapping(cleaner_name, cleaner_size, cleaner_backend);
out1:
signal(SIGINT, SIG_DFL);
cleaner_name = NULL;
cleaner_backend = NULL;
+ cleaner_size = 0;
free(dmCipherSpec);
free(fullpath);
free(name);