#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
+#include <sys/ioctl.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <libdevmapper.h>
+#include <fcntl.h>
+#include <linux/fs.h>
#include "libcryptsetup.h"
#include "internal.h"
#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 __lookup_dev(buf, makedev(major, minor));
}
+static int _dev_read_ahead(const char *dev, uint32_t *read_ahead)
+{
+ int fd, r = 0;
+ long read_ahead_long;
+
+ if ((fd = open(dev, O_RDONLY)) < 0)
+ return 0;
+
+ r = ioctl(fd, BLKRAGET, &read_ahead_long) ? 0 : 1;
+ close(fd);
+
+ if (r)
+ *read_ahead = (uint32_t) read_ahead_long;
+
+ return r;
+}
+
static char *get_params(struct crypt_options *options, const char *key)
{
char *params;
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)
{
struct dm_task *dmt_query = NULL;
struct dm_info dmi;
char *params = NULL;
+ char *error = NULL;
int r = -EINVAL;
+ uint32_t read_ahead = 0;
params = get_params(options, key);
if (!params)
goto out;
if (!dm_task_add_target(dmt, 0, options->size, CRYPT_TARGET, params))
goto out;
+
+#ifdef DM_READ_AHEAD_MINIMUM_FLAG
+ if (_dev_read_ahead(options->device, &read_ahead) &&
+ !dm_task_set_read_ahead(dmt, read_ahead, DM_READ_AHEAD_MINIMUM_FLAG))
+ goto out;
+#endif
if (!dm_task_run(dmt))
goto out;
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))
- goto out_restore_error;
+ if (get_error())
+ error = strdup(get_error());
+
+ _dm_remove(options, 0);
-out_restore_error:
- set_error("%s", error);
- if (error)
+ if (error) {
+ set_error(error);
free(error);
+ }
}
out_no_removal:
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);;
}