/*
* cryptsetup-reencrypt - crypt utility for offline re-encryption
*
- * Copyright (C) 2012 Milan Broz All rights reserved.
+ * Copyright (C) 2012, Milan Broz All rights reserved.
* Copyright (C) 2012, Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#define PACKAGE_REENC "crypt_reencrypt"
-
-#define _LARGEFILE64_SOURCE
-#define _FILE_OFFSET_BITS 64
-#define SECTOR_SIZE 512
-#define NO_UUID "cafecafe-cafe-cafe-cafe-cafecafeeeee"
-#define MAX_BCK_SECTORS 8192
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdarg.h>
-#include <inttypes.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/stat.h>
+#include "cryptsetup.h"
#include <sys/ioctl.h>
#include <sys/time.h>
#include <linux/fs.h>
#include <arpa/inet.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <popt.h>
-#include "cryptsetup.h"
+#define PACKAGE_REENC "crypt_reencrypt"
+
+#define NO_UUID "cafecafe-cafe-cafe-cafe-cafecafeeeee"
+#define MAX_BCK_SECTORS 8192
-static int opt_verbose = 0;
-static int opt_debug = 0;
static const char *opt_cipher = NULL;
static const char *opt_hash = NULL;
static const char *opt_key_file = NULL;
static long opt_keyfile_size = 0;
static long opt_keyfile_offset = 0;
static int opt_iteration_time = 1000;
-static int opt_batch_mode = 0;
static int opt_version_mode = 0;
static int opt_random = 0;
static int opt_urandom = 0;
static int opt_bsize = 4;
-static int opt_reduce_device_size = 0;
static int opt_directio = 0;
static int opt_fsync = 0;
static int opt_write_log = 0;
static int opt_key_size = 0;
static int opt_new = 0;
-static const char **action_argv;
+static const char *opt_reduce_size_str = NULL;
+static uint64_t opt_reduce_size = 0;
-static volatile int quit = 0;
+static const char *opt_device_size_str = NULL;
+static uint64_t opt_device_size = 0;
+
+static const char **action_argv;
#define MAX_SLOT 8
struct reenc_ctx {
char *device;
char *device_uuid;
- uint64_t device_size;
+ uint64_t device_size; /* overrided by parameter */
+ uint64_t device_size_real;
uint64_t device_offset;
uint64_t device_shift;
int keyslot;
struct timeval start_time, end_time;
- uint64_t restart_bytes;
+ uint64_t resume_bytes;
};
char MAGIC[] = {'L','U','K','S', 0xba, 0xbe};
CHECK_OPEN,
} header_magic;
-__attribute__((format(printf, 5, 6)))
-static void clogger(struct crypt_device *cd, int level, const char *file,
- int line, const char *format, ...)
-{
- va_list argp;
- char *target = NULL;
-
- va_start(argp, format);
-
- if (vasprintf(&target, format, argp) > 0) {
- if (level >= 0) {
- crypt_log(cd, level, target);
- } else if (opt_debug)
- printf("# %s\n", target);
- }
-
- va_end(argp);
- free(target);
-}
-
-static void _log(int level, const char *msg, void *usrptr __attribute__((unused)))
-{
- switch(level) {
-
- case CRYPT_LOG_NORMAL:
- fputs(msg, stdout);
- break;
- case CRYPT_LOG_VERBOSE:
- if (opt_verbose)
- fputs(msg, stdout);
- break;
- case CRYPT_LOG_ERROR:
- fputs(msg, stderr);
- break;
- case CRYPT_LOG_DEBUG:
- if (opt_debug)
- printf("# %s\n", msg);
- break;
- default:
- fprintf(stderr, "Internal error on logging class for msg: %s", msg);
- break;
- }
-}
-
static void _quiet_log(int level, const char *msg, void *usrptr)
{
if (!opt_debug)
return;
- _log(level, msg, usrptr);
-}
-
-static void int_handler(int sig __attribute__((__unused__)))
-{
- quit++;
-}
-
-static void set_int_block(int block)
-{
- sigset_t signals_open;
-
- sigemptyset(&signals_open);
- sigaddset(&signals_open, SIGINT);
- sigaddset(&signals_open, SIGTERM);
- sigprocmask(block ? SIG_SETMASK : SIG_UNBLOCK, &signals_open, NULL);
-}
-
-static void set_int_handler(void)
-{
- struct sigaction sigaction_open;
-
- memset(&sigaction_open, 0, sizeof(struct sigaction));
- sigaction_open.sa_handler = int_handler;
- sigaction(SIGINT, &sigaction_open, 0);
- sigaction(SIGTERM, &sigaction_open, 0);
- set_int_block(0);
+ tool_log(level, msg, usrptr);
}
/* The difference in seconds between two times in "timeval" format. */
s = read(devfd, buf, SECTOR_SIZE);
if (s < 0 || s != SECTOR_SIZE) {
log_err(_("Cannot read device %s.\n"), rc->device);
- close(devfd);
- return -EIO;
+ r = -EIO;
+ goto out;
}
/* Be sure that we do not process new version of header */
size = data_sector * SECTOR_SIZE;
/* if reducing size, be sure we have enough space */
- if (opt_reduce_device_size)
- size += (opt_reduce_device_size * SECTOR_SIZE);
+ if (opt_reduce_size)
+ size += opt_reduce_size;
log_dbg("Creating empty file %s of size %lu.", new_file, (unsigned long)size);
- if (!(buf = malloc(size)))
+ if (!size || !(buf = malloc(size)))
return -ENOMEM;
memset(buf, 0, size);
1, rc->device_uuid, rc->reencrypt_direction,
rc->device_offset, rc->device_shift);
- lseek(rc->log_fd, 0, SEEK_SET);
+ if (lseek(rc->log_fd, 0, SEEK_SET) == -1)
+ return -EIO;
+
r = write(rc->log_fd, rc->log_buf, SECTOR_SIZE);
if (r < 0 || r != SECTOR_SIZE) {
log_err(_("Cannot write reencryption log file.\n"));
static int open_log(struct reenc_ctx *rc)
{
- int flags, create_new;
- struct stat st;
-
- if (!stat(rc->log_file, &st))
- create_new = 0;
- else if (errno == ENOENT)
- create_new = 1;
- else
- return -EINVAL;
-
- if (create_new) {
- log_dbg("Creating LUKS reencryption log file %s.", rc->log_file);
- flags = opt_directio ? O_RDWR|O_CREAT|O_DIRECT : O_RDWR|O_CREAT;
- rc->log_fd = open(rc->log_file, flags, S_IRUSR|S_IWUSR);
- if (rc->log_fd == -1)
- return -EINVAL;
- } else {
- log_std(_("Log file %s exists, restarting reencryption.\n"), rc->log_file);
- flags = opt_directio ? O_RDWR|O_DIRECT : O_RDWR;
- rc->log_fd = open(rc->log_file, flags);
- if (rc->log_fd == -1)
- return -EINVAL;
+ int flags = opt_directio ? O_DIRECT : 0;
+
+ rc->log_fd = open(rc->log_file, O_RDWR|O_EXCL|O_CREAT|flags, S_IRUSR|S_IWUSR);
+ if (rc->log_fd != -1) {
+ log_dbg("Created LUKS reencryption log file %s.", rc->log_file);
+ } else if (errno == EEXIST) {
+ log_std(_("Log file %s exists, resuming reencryption.\n"), rc->log_file);
+ rc->log_fd = open(rc->log_file, O_RDWR|flags);
rc->in_progress = 1;
}
+ if (rc->log_fd == -1)
+ return -EINVAL;
+
if (posix_memalign((void *)&rc->log_buf, alignment(rc->log_fd), SECTOR_SIZE)) {
log_err(_("Allocation of aligned memory failed.\n"));
close_log(rc);
return -ENOMEM;
}
- if (create_new && write_log(rc) < 0) {
+ if (!rc->in_progress && write_log(rc) < 0) {
close_log(rc);
return -EIO;
}
(r = crypt_set_data_device(cd, rc->device)))
goto out;
+ log_verbose(_("Activating temporary device using old LUKS header.\n"));
if ((r = crypt_activate_by_passphrase(cd, rc->header_file_org,
opt_key_slot, rc->p[rc->keyslot].password, rc->p[rc->keyslot].passwordLen,
CRYPT_ACTIVATE_READONLY|CRYPT_ACTIVATE_PRIVATE)) < 0)
(r = crypt_set_data_device(cd_new, rc->device)))
goto out;
+ log_verbose(_("Activating temporary device using new LUKS header.\n"));
if ((r = crypt_activate_by_passphrase(cd_new, rc->header_file_new,
opt_key_slot, rc->p[rc->keyslot].password, rc->p[rc->keyslot].passwordLen,
CRYPT_ACTIVATE_SHARED|CRYPT_ACTIVATE_PRIVATE)) < 0)
params.hash = opt_hash ?: DEFAULT_LUKS1_HASH;
params.data_alignment = crypt_get_data_offset(cd);
- params.data_alignment += opt_reduce_device_size;
+ params.data_alignment += ROUND_SECTOR(opt_reduce_size);
params.data_device = rc->device;
if (opt_cipher) {
if (r < 0)
goto out;
- params.data_alignment = opt_reduce_device_size;
+ params.data_alignment = ROUND_SECTOR(opt_reduce_size);
r = create_new_header(rc,
opt_cipher ? cipher : DEFAULT_LUKS1_CIPHER,
opt_cipher ? cipher_mode : DEFAULT_LUKS1_MODE,
if (!tdiff)
return;
- mbytes = (bytes - rc->restart_bytes) / 1024 / 1024;
+ mbytes = (bytes - rc->resume_bytes) / 1024 / 1024;
mib = (double)(mbytes) / tdiff;
if (!mib)
return;
+ /* FIXME: calculate this from last minute only and remaining space */
eta = (unsigned long long)(rc->device_size / 1024 / 1024 / mib - tdiff);
/* vt100 code clear line */
return -EIO;
}
- rc->restart_bytes = *bytes = rc->device_offset;
+ rc->resume_bytes = *bytes = rc->device_offset;
if (write_log(rc) < 0)
return -EIO;
return -EIO;
}
+ /* If device_size is forced, never write more than limit */
+ if ((s1 + rc->device_offset) > rc->device_size)
+ s1 = rc->device_size - rc->device_offset;
+
s2 = write(fd_new, buf, s1);
if (s2 < 0) {
log_dbg("Write error, expecting %d, got %d.",
if (!rc->in_progress) {
rc->device_offset = rc->device_size;
- rc->restart_bytes = 0;
+ rc->resume_bytes = 0;
*bytes = 0;
} else {
- rc->restart_bytes = rc->device_size - rc->device_offset;
- *bytes = rc->restart_bytes;
+ rc->resume_bytes = rc->device_size - rc->device_offset;
+ *bytes = rc->resume_bytes;
}
if (write_log(rc) < 0)
}
/* Check size */
- if (ioctl(fd_new, BLKGETSIZE64, &rc->device_size) < 0) {
+ if (ioctl(fd_new, BLKGETSIZE64, &rc->device_size_real) < 0) {
log_err(_("Cannot get device size.\n"));
goto out;
}
+ rc->device_size = opt_device_size ?: rc->device_size_real;
+
if (posix_memalign((void *)&buf, alignment(fd_new), block_size)) {
log_err(_("Allocation of aligned memory failed.\n"));
r = -ENOMEM;
goto out;
}
- set_int_handler();
+ set_int_handler(0);
gettimeofday(&rc->start_time, NULL);
if (rc->reencrypt_direction == FORWARD)
retry_count = opt_tries ?: 1;
while (retry_count--) {
- set_int_handler();
+ set_int_handler(0);
r = crypt_get_key(msg, &rc->p[slot].password,
&rc->p[slot].passwordLen,
0, 0, NULL /*opt_key_file*/,
}
if (!rc->in_progress) {
- if (!opt_reduce_device_size)
+ if (!opt_reduce_size)
rc->reencrypt_direction = FORWARD;
else {
rc->reencrypt_direction = BACKWARD;
return r;
}
-static __attribute__ ((noreturn)) void usage(poptContext popt_context,
- int exitcode, const char *error,
- const char *more)
-{
- poptPrintUsage(popt_context, stderr, 0);
- if (error)
- log_err("%s: %s\n", more, error);
- poptFreeContext(popt_context);
- exit(exitcode);
-}
-
static void help(poptContext popt_context,
enum poptCallbackReason reason __attribute__((unused)),
struct poptOption *key,
usage(popt_context, EXIT_SUCCESS, NULL, NULL);
}
-static void _dbg_version_and_cmd(int argc, const char **argv)
-{
- int i;
-
- log_std("# %s %s processing \"", PACKAGE_REENC, PACKAGE_VERSION);
- for (i = 0; i < argc; i++) {
- if (i)
- log_std(" ");
- log_std("%s", argv[i]);
- }
- log_std("\"\n");
-}
-
int main(int argc, const char **argv)
{
static struct poptOption popt_help_options[] = {
{ "key-slot", 'S', POPT_ARG_INT, &opt_key_slot, 0, N_("Use only this slot (others will be disabled)."), NULL },
{ "keyfile-offset", '\0', POPT_ARG_LONG, &opt_keyfile_offset, 0, N_("Number of bytes to skip in keyfile"), N_("bytes") },
{ "keyfile-size", 'l', POPT_ARG_LONG, &opt_keyfile_size, 0, N_("Limits the read from keyfile"), N_("bytes") },
- { "reduce-device-size",'\0', POPT_ARG_INT, &opt_reduce_device_size, 0, N_("Reduce data device size (move data offset). DANGEROUS!"), N_("SECTORS") },
+ { "reduce-device-size",'\0', POPT_ARG_STRING, &opt_reduce_size_str, 0, N_("Reduce data device size (move data offset). DANGEROUS!"), N_("bytes") },
+ { "device-size", '\0', POPT_ARG_STRING, &opt_device_size_str, 0, N_("Use only specified device size (ignore rest of device). DANGEROUS!"), N_("bytes") },
{ "new", 'N', POPT_ARG_NONE,&opt_new, 0, N_("Create new header on not encrypted device."), NULL },
POPT_TABLEEND
};
poptContext popt_context;
int r;
- crypt_set_log_callback(NULL, _log, NULL);
+ crypt_set_log_callback(NULL, tool_log, NULL);
set_int_block(1);
popt_context = poptGetContext(PACKAGE, argc, argv, popt_options, 0);
poptSetOtherOptionHelp(popt_context,
- N_("[OPTION...] <device>"));
+ _("[OPTION...] <device>"));
while((r = poptGetNextOpt(popt_context)) > 0) ;
if (r < -1)
poptGetInvocationName(popt_context));
if (opt_bsize < 0 || opt_key_size < 0 || opt_iteration_time < 0 ||
- opt_tries < 0 || opt_keyfile_offset < 0 || opt_key_size < 0 ||
- opt_reduce_device_size < 0) {
+ opt_tries < 0 || opt_keyfile_offset < 0 || opt_key_size < 0) {
usage(popt_context, EXIT_FAILURE,
_("Negative number for option not permitted."),
poptGetInvocationName(popt_context));
if (opt_bsize < 1 || opt_bsize > 64)
usage(popt_context, EXIT_FAILURE,
- _("Only values between 1MiB and 64 MiB allowed for reencryption block size."),
- poptGetInvocationName(popt_context));
-
- if (opt_reduce_device_size > (64 * 1024 * 1024 / SECTOR_SIZE))
- usage(popt_context, EXIT_FAILURE,
- _("Maximum device reduce size is 64 MiB."),
+ _("Only values between 1 MiB and 64 MiB allowed for reencryption block size."),
poptGetInvocationName(popt_context));
if (opt_key_size % 8)
usage(popt_context, EXIT_FAILURE, _("Only one of --use-[u]random options is allowed."),
poptGetInvocationName(popt_context));
- if (opt_new && !opt_reduce_device_size)
- usage(popt_context, EXIT_FAILURE, _("Option --new must be used together with --reduce_device_size."),
+ if (opt_device_size_str &&
+ crypt_string_to_size(NULL, opt_device_size_str, &opt_device_size))
+ usage(popt_context, EXIT_FAILURE, _("Invalid device size specification."),
+ poptGetInvocationName(popt_context));
+
+ if (opt_reduce_size_str &&
+ crypt_string_to_size(NULL, opt_reduce_size_str, &opt_reduce_size))
+ usage(popt_context, EXIT_FAILURE, _("Invalid device size specification."),
+ poptGetInvocationName(popt_context));
+ if (opt_reduce_size > 64 * 1024 * 1024)
+ usage(popt_context, EXIT_FAILURE, _("Maximum device reduce size is 64 MiB."),
+ poptGetInvocationName(popt_context));
+ if (opt_reduce_size % SECTOR_SIZE)
+ usage(popt_context, EXIT_FAILURE, _("Reduce size must be multiple of 512 bytes sector."),
+ poptGetInvocationName(popt_context));
+
+ if (opt_new && !opt_reduce_size)
+ usage(popt_context, EXIT_FAILURE, _("Option --new must be used together with --reduce-device-size."),
poptGetInvocationName(popt_context));
if (opt_debug) {
opt_verbose = 1;
crypt_set_debug_level(-1);
- _dbg_version_and_cmd(argc, argv);
+ dbg_version_and_cmd(argc, argv);
}
r = run_reencrypt(action_argv[0]);
poptFreeContext(popt_context);
- /* Translate exit code to simple codes */
- switch (r) {
- case 0: r = EXIT_SUCCESS; break;
- case -EEXIST:
- case -EBUSY: r = 5; break;
- case -ENOTBLK:
- case -ENODEV: r = 4; break;
- case -ENOMEM: r = 3; break;
- case -EPERM: r = 2; break;
- default: r = EXIT_FAILURE;
- }
- return r;
+ return translate_errno(r);
}