X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fcrypt_reencrypt.c;h=07cbbf3b793871122b32d58eeb61a683e2c3c2ac;hb=9d47892342ec24db322165cc1c7e9d716255aa98;hp=a3abbba9303bfb8ef33b71c66b5261590af57a3e;hpb=555d5f6e97f43f31a2af26b41f1376069321bbc8;p=platform%2Fupstream%2Fcryptsetup.git diff --git a/src/crypt_reencrypt.c b/src/crypt_reencrypt.c index a3abbba..07cbbf3 100644 --- a/src/crypt_reencrypt.c +++ b/src/crypt_reencrypt.c @@ -17,15 +17,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -/* The code works as follows: - * - create backup (detached) headers fo old and new device - * - mark original device unusable - * - maps two devices, one with old header one with new onto - * the _same_ underlying device - * - with direct-io reads old device and copy to new device in defined steps - * - keps simple off in file (allows restart) - * - there is several windows when corruption can happen - */ +#define _LARGEFILE64_SOURCE +#define _FILE_OFFSET_BITS 64 +#define SECTOR_SIZE 512 + #include #include #include @@ -36,11 +31,10 @@ #include #include #include +#include #include #include -#include #include -#include #include #include "cryptsetup.h" @@ -50,22 +44,33 @@ 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_directio = 0; +static int opt_write_log = 0; +static int opt_tries = 3; +static int opt_key_slot = CRYPT_ANY_SLOT; static const char **action_argv; -sigset_t signals_open; -struct { +static volatile int quit = 0; + +#define MAX_SLOT 8 +struct reenc_ctx { char *device; char *device_uuid; uint64_t device_size; uint64_t device_offset; + uint64_t device_shift; int in_progress:1; + enum { FORWARD = 0, BACKWARD = 1 } reencrypt_direction; char header_file_org[PATH_MAX]; char header_file_new[PATH_MAX]; @@ -74,11 +79,17 @@ struct { char crypt_path_org[PATH_MAX]; char crypt_path_new[PATH_MAX]; int log_fd; + char *log_buf; - char *password; - size_t passwordLen; + struct { + char *password; + size_t passwordLen; + } p[MAX_SLOT]; int keyslot; -} rnc; + + struct timeval start_time, end_time; + uint64_t restart_bytes; +}; char MAGIC[] = {'L','U','K','S', 0xba, 0xbe}; char NOMAGIC[] = {'L','U','K','S', 0xde, 0xad}; @@ -141,6 +152,39 @@ static void _quiet_log(int level, const char *msg, void *usrptr) _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); +} + +/* The difference in seconds between two times in "timeval" format. */ +static double time_diff(struct timeval start, struct timeval end) +{ + return (end.tv_sec - start.tv_sec) + + (end.tv_usec - start.tv_usec) / 1E6; +} + static int alignment(int fd) { int alignment; @@ -151,42 +195,42 @@ static int alignment(int fd) return alignment; } -static int device_magic(header_magic set_magic) +static int device_magic(struct reenc_ctx *rc, header_magic set_magic) { char *buf = NULL; - size_t block_size = 512; int r, devfd; ssize_t s; - devfd = open(rnc.device, O_RDWR | O_DIRECT); + devfd = open(rc->device, O_RDWR | O_DIRECT); if (devfd == -1) return errno == EBUSY ? -EBUSY : -EINVAL; - if (posix_memalign((void *)&buf, alignment(devfd), block_size)) { + if (posix_memalign((void *)&buf, alignment(devfd), SECTOR_SIZE)) { + log_err(_("Allocation of aligned memory failed.\n")); r = -ENOMEM; goto out; } - s = read(devfd, buf, block_size); - if (s < 0 || s != block_size) { - log_verbose(_("Cannot read device %s.\n"), rnc.device); + 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; } if (set_magic == MAKE_UNUSABLE && !memcmp(buf, MAGIC, MAGIC_L)) { - log_dbg("Marking LUKS device %s unusable.", rnc.device); + log_verbose(_("Marking LUKS device %s unusable.\n"), rc->device); memcpy(buf, NOMAGIC, MAGIC_L); r = 0; } else if (set_magic == MAKE_USABLE && !memcmp(buf, NOMAGIC, MAGIC_L)) { - log_dbg("Marking LUKS device %s usable.", rnc.device); + log_verbose(_("Marking LUKS device %s usable.\n"), rc->device); memcpy(buf, MAGIC, MAGIC_L); r = 0; } else if (set_magic == CHECK_UNUSABLE) { r = memcmp(buf, NOMAGIC, MAGIC_L) ? -EINVAL : 0; if (!r) - rnc.device_uuid = strndup(&buf[0xa8], 40); + rc->device_uuid = strndup(&buf[0xa8], 40); goto out; } else r = -EINVAL; @@ -194,26 +238,34 @@ static int device_magic(header_magic set_magic) if (!r) { if (lseek(devfd, 0, SEEK_SET) == -1) goto out; - s = write(devfd, buf, block_size); - if (s < 0 || s != block_size) { - log_verbose(_("Cannot write device %s.\n"), rnc.device); + s = write(devfd, buf, SECTOR_SIZE); + if (s < 0 || s != SECTOR_SIZE) { + log_err(_("Cannot write device %s.\n"), rc->device); r = -EIO; } } else - log_dbg("LUKS signature check failed for %s.", rnc.device); + log_dbg("LUKS signature check failed for %s.", rc->device); out: if (buf) - memset(buf, 0, block_size); + memset(buf, 0, SECTOR_SIZE); free(buf); close(devfd); return r; } -static int create_empty_header(const char *new_file, uint64_t size) +static int create_empty_header(const char *new_file, const char *old_file) { + struct stat st; + ssize_t size; int fd, r = 0; char *buf; + if (stat(old_file, &st) == -1 || + (st.st_mode & S_IFMT) != S_IFREG || + (st.st_size > 16 * 1024 * 1024)) + return -EINVAL; + size = st.st_size; + log_dbg("Creating empty file %s of size %lu.", new_file, (unsigned long)size); if (!(buf = malloc(size))) @@ -234,28 +286,35 @@ static int create_empty_header(const char *new_file, uint64_t size) return r; } -static int write_log(void) +static int write_log(struct reenc_ctx *rc) { - static char buf[512]; - - //log_dbg("Updating LUKS reencryption log offset %" PRIu64 ".", offset); - memset(buf, 0, sizeof(buf)); - snprintf(buf, sizeof(buf), "# LUKS reencryption log, DO NOT EDIT OR DELETE.\n" - "version = %d\nUUID = %s\noffset = %" PRIu64 "\n# EOF\n", - 1, rnc.device_uuid, rnc.device_offset); + static char buf[SECTOR_SIZE]; + ssize_t r; + + memset(buf, 0, SECTOR_SIZE); + snprintf(buf, SECTOR_SIZE, "# LUKS reencryption log, DO NOT EDIT OR DELETE.\n" + "version = %d\nUUID = %s\ndirection = %d\n" + "offset = %" PRIu64 "\nshift = %" PRIu64 "\n# EOF\n", + 1, rc->device_uuid, rc->reencrypt_direction, + rc->device_offset, rc->device_shift); + + lseek(rc->log_fd, 0, SEEK_SET); + r = write(rc->log_fd, buf, SECTOR_SIZE); + if (r < 0 || r != SECTOR_SIZE) { + log_err(_("Cannot write reencryption log file.\n")); + return -EIO; + } - lseek(rnc.log_fd, 0, SEEK_SET); - write(rnc.log_fd, buf, sizeof(buf)); return 0; } -static int parse_line_log(const char *line) +static int parse_line_log(struct reenc_ctx *rc, const char *line) { uint64_t u64; int i; char s[64]; - /* comment */ + /* whole line is comment */ if (*line == '#') return 0; @@ -265,36 +324,43 @@ static int parse_line_log(const char *line) return -EINVAL; } } else if (sscanf(line, "UUID = %40s", s) == 1) { - if (!rnc.device_uuid || strcmp(rnc.device_uuid, s)) { + if (!rc->device_uuid || strcmp(rc->device_uuid, s)) { log_dbg("Log: Unexpected UUID %s", s); return -EINVAL; } + } else if (sscanf(line, "direction = %d", &i) == 1) { + log_dbg("Log: direction = %i", i); + rc->reencrypt_direction = i; } else if (sscanf(line, "offset = %" PRIu64, &u64) == 1) { log_dbg("Log: offset = %" PRIu64, u64); - rnc.device_offset = u64; + rc->device_offset = u64; + } else if (sscanf(line, "shift = %" PRIu64, &u64) == 1) { + log_dbg("Log: shift = %" PRIu64, u64); + rc->device_shift = u64; } else return -EINVAL; return 0; } -static int parse_log(void) +static int parse_log(struct reenc_ctx *rc) { - static char buf[512]; char *start, *end; ssize_t s; - s = read(rnc.log_fd, buf, sizeof(buf)); - if (s == -1) + s = read(rc->log_fd, rc->log_buf, SECTOR_SIZE); + if (s == -1) { + log_err(_("Cannot read reencryption log file.\n")); return -EIO; + } - buf[511] = '\0'; - start = buf; + rc->log_buf[SECTOR_SIZE - 1] = '\0'; + start = rc->log_buf; do { end = strchr(start, '\n'); if (end) { *end++ = '\0'; - if (parse_line_log(start)) { + if (parse_line_log(rc, start)) { log_err("Wrong log format.\n"); return -EINVAL; } @@ -306,93 +372,108 @@ static int parse_log(void) return 0; } -static int open_log(void) +static void close_log(struct reenc_ctx *rc) { + log_dbg("Closing LUKS reencryption log file %s.", rc->log_file); + if (rc->log_fd != -1) + close(rc->log_fd); + free(rc->log_buf); + rc->log_buf = NULL; +} + +static int open_log(struct reenc_ctx *rc) +{ + int flags; struct stat st; - if(stat(rnc.log_file, &st) < 0) { - log_dbg("Creating LUKS reencryption log file %s.", rnc.log_file); - rnc.log_fd = open(rnc.log_file, O_RDWR|O_CREAT|O_DIRECT, S_IRUSR|S_IWUSR); - if (rnc.log_fd == -1) + if(stat(rc->log_file, &st) < 0) { + 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; - if (write_log() < 0) + if (write_log(rc) < 0) return -EIO; } else { - log_dbg("Log file %s exists, restarting.", rnc.log_file); - rnc.log_fd = open(rnc.log_file, O_RDWR|O_DIRECT); - if (rnc.log_fd == -1) + log_dbg("Log file %s exists, restarting.", 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; - rnc.in_progress = 1; + rc->in_progress = 1; } - /* Be sure it is correct format */ - return parse_log(); -} + 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; + } -static void close_log(void) -{ - log_dbg("Closing LUKS reencryption log file %s.", rnc.log_file); - if (rnc.log_fd != -1) - close(rnc.log_fd); + /* Be sure it is correct format */ + return parse_log(rc); } -static int activate_luks_headers(void) +static int activate_luks_headers(struct reenc_ctx *rc) { struct crypt_device *cd = NULL, *cd_new = NULL; int r; log_dbg("Activating LUKS devices from headers."); - if ((r = crypt_init(&cd, rnc.header_file_org)) || + if ((r = crypt_init(&cd, rc->header_file_org)) || (r = crypt_load(cd, CRYPT_LUKS1, NULL)) || - (r = crypt_set_data_device(cd, rnc.device))) + (r = crypt_set_data_device(cd, rc->device))) goto out; - if ((r = crypt_activate_by_passphrase(cd, rnc.header_file_org, - CRYPT_ANY_SLOT, rnc.password, rnc.passwordLen, - CRYPT_ACTIVATE_READONLY)) < 0) + 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) goto out; - if ((r = crypt_init(&cd_new, rnc.header_file_new)) || + if ((r = crypt_init(&cd_new, rc->header_file_new)) || (r = crypt_load(cd_new, CRYPT_LUKS1, NULL)) || - (r = crypt_set_data_device(cd_new, rnc.device))) + (r = crypt_set_data_device(cd_new, rc->device))) goto out; - if ((r = crypt_activate_by_passphrase(cd_new, rnc.header_file_new, - CRYPT_ANY_SLOT, rnc.password, rnc.passwordLen, - CRYPT_ACTIVATE_SHARED)) < 0) + 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) goto out; + r = 0; out: crypt_free(cd); crypt_free(cd_new); + if (r < 0) + log_err(_("Activation of temporary devices failed.\n")); return r; } -static int backup_luks_headers(void) +static int backup_luks_headers(struct reenc_ctx *rc) { struct crypt_device *cd = NULL, *cd_new = NULL; struct crypt_params_luks1 params = {0}; char cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; - int r; + int i, r; + + log_dbg("Creating LUKS header backup for device %s.", rc->device); - log_dbg("Creating LUKS header backup for device %s.", rnc.device); - if ((r = crypt_init(&cd, rnc.device)) || + if ((r = crypt_init(&cd, rc->device)) || (r = crypt_load(cd, CRYPT_LUKS1, NULL))) goto out; crypt_set_confirm_callback(cd, NULL, NULL); - if ((r = crypt_header_backup(cd, CRYPT_LUKS1, rnc.header_file_org))) + if ((r = crypt_header_backup(cd, CRYPT_LUKS1, rc->header_file_org))) goto out; + log_verbose(_("LUKS header backup of device %s created.\n"), rc->device); - if ((r = create_empty_header(rnc.header_file_new, - crypt_get_data_offset(cd) * 512))) + if ((r = create_empty_header(rc->header_file_new, rc->header_file_org))) goto out; params.hash = opt_hash ?: DEFAULT_LUKS1_HASH; params.data_alignment = crypt_get_data_offset(cd); - params.data_device = rnc.device; + params.data_device = rc->device; - if ((r = crypt_init(&cd_new, rnc.header_file_new))) + if ((r = crypt_init(&cd_new, rc->header_file_new))) goto out; if (opt_random) @@ -417,97 +498,241 @@ static int backup_luks_headers(void) crypt_get_uuid(cd), NULL, crypt_get_volume_key_size(cd), ¶ms))) goto out; + log_verbose(_("New LUKS header for device %s created.\n"), rc->device); - if ((r = crypt_keyslot_add_by_volume_key(cd_new, rnc.keyslot, - NULL, 0, rnc.password, rnc.passwordLen)) < 0) - goto out; - + for (i = 0; i < MAX_SLOT; i++) { + if (!rc->p[i].password) + continue; + if ((r = crypt_keyslot_add_by_volume_key(cd_new, i, + NULL, 0, rc->p[i].password, rc->p[i].passwordLen)) < 0) + goto out; + log_verbose(_("Activated keyslot %i.\n"), r); + r = 0; + } out: crypt_free(cd); crypt_free(cd_new); + if (r) + log_err(_("Creation of LUKS backup headers failed.\n")); return r; } -static void remove_headers(void) +static void remove_headers(struct reenc_ctx *rc) { struct crypt_device *cd = NULL; + log_dbg("Removing headers."); + if (crypt_init(&cd, NULL)) return; crypt_set_log_callback(cd, _quiet_log, NULL); - (void)crypt_deactivate(cd, rnc.header_file_org); - (void)crypt_deactivate(cd, rnc.header_file_new); + (void)crypt_deactivate(cd, rc->header_file_org); + (void)crypt_deactivate(cd, rc->header_file_new); crypt_free(cd); } -static int restore_luks_header(const char *backup) +static int restore_luks_header(struct reenc_ctx *rc) { struct crypt_device *cd = NULL; int r; - r = crypt_init(&cd, rnc.device); + log_dbg("Restoring header for %s from %s.", rc->device, rc->header_file_new); + r = crypt_init(&cd, rc->device); if (r == 0) { crypt_set_confirm_callback(cd, NULL, NULL); - r = crypt_header_restore(cd, CRYPT_LUKS1, backup); + r = crypt_header_restore(cd, CRYPT_LUKS1, rc->header_file_new); } crypt_free(cd); + if (r) + log_err(_("Cannot restore LUKS header on device %s.\n"), rc->device); + else + log_verbose(_("LUKS header on device %s restored.\n"), rc->device); return r; } -static int copy_data(void) +static void print_progress(struct reenc_ctx *rc, uint64_t bytes, int final) +{ + uint64_t mbytes = (bytes - rc->restart_bytes) / 1024 / 1024; + struct timeval now_time; + double tdiff; + + gettimeofday(&now_time, NULL); + if (!final && time_diff(rc->end_time, now_time) < 0.5) + return; + + rc->end_time = now_time; + + if (opt_batch_mode) + return; + + tdiff = time_diff(rc->start_time, rc->end_time); + if (!tdiff) + return; + + /* vt100 code clear line */ + log_err("\33[2K\r"); + log_err(_("Progress: %5.1f%%, time elapsed %4.0f seconds, " + "%4llu MiB written, speed %5.1f MiB/s%s"), + (double)bytes / rc->device_size * 100, + time_diff(rc->start_time, rc->end_time), + (unsigned long long)mbytes, (double)(mbytes) / tdiff, + final ? "\n" :""); +} + +static int copy_data_forward(struct reenc_ctx *rc, int fd_old, int fd_new, + size_t block_size, void *buf, uint64_t *bytes) { - int fd_old = -1, fd_new = -1, j; - size_t block_size = 1024 *1024; - int r = -EINVAL; - void *buf = NULL; ssize_t s1, s2; - fd_old = open(rnc.crypt_path_org, O_RDONLY | O_DIRECT); - if (fd_old == -1) - goto out; + log_dbg("Reencrypting in forward direction."); - fd_new = open(rnc.crypt_path_new, O_WRONLY | O_DIRECT); - if (fd_new == -1) - goto out; + if (lseek64(fd_old, rc->device_offset, SEEK_SET) < 0 || + lseek64(fd_new, rc->device_offset, SEEK_SET) < 0) { + log_err(_("Cannot seek to device offset.\n")); + return -EIO; + } + + rc->restart_bytes = *bytes = rc->device_offset; + + if (write_log(rc) < 0) + return -EIO; + + while (!quit && rc->device_offset < rc->device_size) { + s1 = read(fd_old, buf, block_size); + if (s1 < 0 || ((size_t)s1 != block_size && + (rc->device_offset + s1) != rc->device_size)) { + log_dbg("Read error, expecting %d, got %d.", + (int)block_size, (int)s1); + return -EIO; + } + s2 = write(fd_new, buf, s1); + if (s2 < 0) { + log_dbg("Write error, expecting %d, got %d.", + (int)block_size, (int)s2); + return -EIO; + } + rc->device_offset += s1; + if (opt_write_log && write_log(rc) < 0) + return -EIO; + + *bytes += (uint64_t)s2; + print_progress(rc, *bytes, 0); + } + + return quit ? -EAGAIN : 0; +} + +static int copy_data_backward(struct reenc_ctx *rc, int fd_old, int fd_new, + size_t block_size, void *buf, uint64_t *bytes) +{ + ssize_t s1, s2, working_block; + off64_t working_offset; + + log_dbg("Reencrypting in backward direction."); + + if (!rc->in_progress) { + rc->device_offset = rc->device_size; + rc->restart_bytes = 0; + *bytes = 0; + } else { + rc->restart_bytes = rc->device_size - rc->device_offset; + *bytes = rc->restart_bytes; + } + + if (write_log(rc) < 0) + return -EIO; + + while (!quit && rc->device_offset) { + if (rc->device_offset < block_size) { + working_offset = 0; + working_block = rc->device_offset; + } else { + working_offset = rc->device_offset - block_size; + working_block = block_size; + } + + if (lseek64(fd_old, working_offset, SEEK_SET) < 0 || + lseek64(fd_new, working_offset, SEEK_SET) < 0) { + log_err(_("Cannot seek to device offset.\n")); + return -EIO; + } + + s1 = read(fd_old, buf, working_block); + if (s1 < 0 || (s1 != working_block)) { + log_dbg("Read error, expecting %d, got %d.", + (int)block_size, (int)s1); + return -EIO; + } + s2 = write(fd_new, buf, working_block); + if (s2 < 0) { + log_dbg("Write error, expecting %d, got %d.", + (int)block_size, (int)s2); + return -EIO; + } + rc->device_offset -= s1; + if (opt_write_log && write_log(rc) < 0) + return -EIO; + + *bytes += (uint64_t)s2; + print_progress(rc, *bytes, 0); + } + + return quit ? -EAGAIN : 0; +} - if (lseek(fd_old, rnc.device_offset, SEEK_SET) == -1) +static int copy_data(struct reenc_ctx *rc) +{ + size_t block_size = opt_bsize * 1024 * 1024; + int fd_old = -1, fd_new = -1; + int r = -EINVAL; + void *buf = NULL; + uint64_t bytes = 0; + + log_dbg("Data copy preparation."); + + fd_old = open(rc->crypt_path_org, O_RDONLY | (opt_directio ? O_DIRECT : 0)); + if (fd_old == -1) { + log_err(_("Cannot open temporary LUKS header file.\n")); goto out; + } - if (lseek(fd_new, rnc.device_offset, SEEK_SET) == -1) + fd_new = open(rc->crypt_path_new, O_WRONLY | (opt_directio ? O_DIRECT : 0)); + if (fd_new == -1) { + log_err(_("Cannot open temporary LUKS header file.\n")); goto out; + } /* Check size */ - if (ioctl(fd_old, BLKGETSIZE64, &rnc.device_size) < 0) + if (ioctl(fd_old, BLKGETSIZE64, &rc->device_size) < 0) { + log_err(_("Cannot get device size.\n")); goto out; + } if (posix_memalign((void *)&buf, alignment(fd_new), block_size)) { + log_err(_("Allocation of aligned memory failed.\n")); r = -ENOMEM; goto out; } - log_err("Reencrypting ["); - j = 0; - while (rnc.device_offset < rnc.device_size) { - s1 = read(fd_old, buf, block_size); - if (s1 != block_size) - log_err("Read error, expecting %d, got %d.\n", (int)block_size, (int)s1); - if (s1 < 0) - goto out; - s2 = write(fd_new, buf, s1); - if (s2 != block_size) - log_err("Write error, expecting %d, got %d.\n", (int)block_size, (int)s2); - rnc.device_offset += s1; - write_log(); - if (rnc.device_offset > (j * (rnc.device_size / 10))) { - log_err("-"); - j++; - } - } - log_err("] Done.\n"); - r = 0; + set_int_handler(); + gettimeofday(&rc->start_time, NULL); + + if (rc->reencrypt_direction == FORWARD) + r = copy_data_forward(rc, fd_old, fd_new, block_size, buf, &bytes); + else + r = copy_data_backward(rc, fd_old, fd_new, block_size, buf, &bytes); + + set_int_block(1); + print_progress(rc, bytes, 1); + if (r == -EAGAIN) + log_err(_("Interrupted by a signal.\n")); + else if (r < 0) + log_err(_("IO error during reencryption.\n")); + + (void)write_log(rc); out: if (fd_old != -1) close(fd_old); @@ -517,146 +742,240 @@ out: return r; } -static int initialize_uuid(void) +static int initialize_uuid(struct reenc_ctx *rc) { struct crypt_device *cd = NULL; int r; + log_dbg("Initialising UUID."); + /* Try to load LUKS from device */ - if ((r = crypt_init(&cd, rnc.device))) + if ((r = crypt_init(&cd, rc->device))) return r; crypt_set_log_callback(cd, _quiet_log, NULL); r = crypt_load(cd, CRYPT_LUKS1, NULL); if (!r) - rnc.device_uuid = strdup(crypt_get_uuid(cd)); + rc->device_uuid = strdup(crypt_get_uuid(cd)); else /* Reencryption already in progress - magic header? */ - r = device_magic(CHECK_UNUSABLE); + r = device_magic(rc, CHECK_UNUSABLE); crypt_free(cd); return r; } -static int initialize_passphrase(const char *device) +static int init_passphrase1(struct reenc_ctx *rc, struct crypt_device *cd, + const char *msg, int slot_check) +{ + int r = -EINVAL, slot, retry_count; + + slot = (slot_check == CRYPT_ANY_SLOT) ? 0 : slot_check; + + retry_count = opt_tries ?: 1; + while (retry_count--) { + r = crypt_get_key(msg, &rc->p[slot].password, + &rc->p[slot].passwordLen, + 0, 0, NULL /*opt_key_file*/, + 0, 0, cd); + if (r < 0) + return r; + + r = crypt_activate_by_passphrase(cd, NULL, slot_check, + rc->p[slot].password, rc->p[slot].passwordLen, 0); + + if (r < 0) { + crypt_safe_free(rc->p[slot].password); + rc->p[slot].password = NULL; + rc->p[slot].passwordLen = 0; + } + if (r < 0 && r != -EPERM) + return r; + if (r >= 0) { + rc->keyslot = slot; + break; + } + log_err(_("No key available with this passphrase.\n")); + } + return r; +} + +static int init_keyfile(struct reenc_ctx *rc, struct crypt_device *cd, int slot_check) +{ + int r, slot; + + slot = (slot_check == CRYPT_ANY_SLOT) ? 0 : slot_check; + r = crypt_get_key(NULL, &rc->p[slot].password, &rc->p[slot].passwordLen, + opt_keyfile_offset, opt_keyfile_size, opt_key_file, 0, 0, cd); + if (r < 0) + return r; + + r = crypt_activate_by_passphrase(cd, NULL, slot_check, + rc->p[slot].password, rc->p[slot].passwordLen, 0); + + /* + * Allow keyslot only if it is last slot or if user explicitly + * specify whch slot to use (IOW others will be disabled). + */ + if (r >= 0 && opt_key_slot == CRYPT_ANY_SLOT && + crypt_keyslot_status(cd, r) != CRYPT_SLOT_ACTIVE_LAST) { + log_err(_("Key file can be used only with --key-slot or with " + "exactly one key slot active.\n")); + r = -EINVAL; + } + + if (r < 0) { + crypt_safe_free(rc->p[slot].password); + rc->p[slot].password = NULL; + rc->p[slot].passwordLen = 0; + if (r == -EPERM) + log_err(_("No key available with this passphrase.\n")); + return r; + } else + rc->keyslot = slot; + + return r; +} + +static int initialize_passphrase(struct reenc_ctx *rc, const char *device) { struct crypt_device *cd = NULL; - int r; + crypt_keyslot_info ki; + char msg[256]; + int i, r; + + log_dbg("Passhrases initialization."); if ((r = crypt_init(&cd, device)) || (r = crypt_load(cd, CRYPT_LUKS1, NULL)) || - (r = crypt_set_data_device(cd, rnc.device))) - goto out; - - if ((r = crypt_get_key(_("Enter LUKS passphrase: "), - &rnc.password, &rnc.passwordLen, - 0, 0, opt_key_file, - 0, 0, cd)) <0) - goto out; - - if ((r = crypt_activate_by_passphrase(cd, NULL, - CRYPT_ANY_SLOT, rnc.password, rnc.passwordLen, 0) < 0)) - goto out; + (r = crypt_set_data_device(cd, rc->device))) { + crypt_free(cd); + return r; + } - if (r >= 0) { - rnc.keyslot = r; - r = 0; + if (opt_key_file) { + r = init_keyfile(rc, cd, opt_key_slot); + } else if (rc->in_progress) { + r = init_passphrase1(rc, cd, _("Enter any LUKS passphrase: "), CRYPT_ANY_SLOT); + } else for (i = 0; i < MAX_SLOT; i++) { + ki = crypt_keyslot_status(cd, i); + if (ki != CRYPT_SLOT_ACTIVE && ki != CRYPT_SLOT_ACTIVE_LAST) + continue; + + snprintf(msg, sizeof(msg), _("Enter LUKS passphrase for key slot %u: "), i); + r = init_passphrase1(rc, cd, msg, i); + if (r < 0) + break; } -out: + crypt_free(cd); - return r; + return r > 0 ? 0 : r; } -static int initialize_context(const char *device) +static int initialize_context(struct reenc_ctx *rc, const char *device) { log_dbg("Initialising reencryption context."); - rnc.log_fd =-1; + rc->log_fd =-1; - if (!(rnc.device = strndup(device, PATH_MAX))) + if (!(rc->device = strndup(device, PATH_MAX))) return -ENOMEM; - if (initialize_uuid()) + if (initialize_uuid(rc)) { + log_err(_("Device %s is not a valid LUKS device.\n"), device); return -EINVAL; + } /* Prepare device names */ - if (snprintf(rnc.log_file, PATH_MAX, - "LUKS-%s.log", rnc.device_uuid) < 0) + if (snprintf(rc->log_file, PATH_MAX, + "LUKS-%s.log", rc->device_uuid) < 0) return -ENOMEM; - if (snprintf(rnc.header_file_org, PATH_MAX, - "LUKS-%s.org", rnc.device_uuid) < 0) + if (snprintf(rc->header_file_org, PATH_MAX, + "LUKS-%s.org", rc->device_uuid) < 0) return -ENOMEM; - if (snprintf(rnc.header_file_new, PATH_MAX, - "LUKS-%s.new", rnc.device_uuid) < 0) + if (snprintf(rc->header_file_new, PATH_MAX, + "LUKS-%s.new", rc->device_uuid) < 0) return -ENOMEM; /* Paths to encrypted devices */ - if (snprintf(rnc.crypt_path_org, PATH_MAX, - "%s/%s", crypt_get_dir(), rnc.header_file_org) < 0) + if (snprintf(rc->crypt_path_org, PATH_MAX, + "%s/%s", crypt_get_dir(), rc->header_file_org) < 0) return -ENOMEM; - if (snprintf(rnc.crypt_path_new, PATH_MAX, - "%s/%s", crypt_get_dir(), rnc.header_file_new) < 0) + if (snprintf(rc->crypt_path_new, PATH_MAX, + "%s/%s", crypt_get_dir(), rc->header_file_new) < 0) return -ENOMEM; - remove_headers(); + remove_headers(rc); - /* Block ctrl+c */ - // FIXME: add some routine to handle it - sigemptyset(&signals_open); - sigaddset(&signals_open, SIGINT); - sigprocmask(SIG_SETMASK, &signals_open, NULL); + if (open_log(rc) < 0) { + log_err(_("Cannot open reencryption log file.\n")); + return -EINVAL; + } - return open_log(); + if (!rc->in_progress) { + if (1 /*opt_new */) + rc->reencrypt_direction = FORWARD; + else { + rc->reencrypt_direction = BACKWARD; + rc->device_offset = (uint64_t)~0; + } + } + + return 0; } -static void destroy_context(void) +static void destroy_context(struct reenc_ctx *rc) { + int i; + log_dbg("Destroying reencryption context."); - close_log(); - remove_headers(); + close_log(rc); + remove_headers(rc); - if (rnc.device_offset == rnc.device_size) { - unlink(rnc.log_file); - unlink(rnc.header_file_org); - unlink(rnc.header_file_new); + if ((rc->reencrypt_direction == FORWARD && + rc->device_offset == rc->device_size) || + rc->device_offset == 0) { + unlink(rc->log_file); + unlink(rc->header_file_org); + unlink(rc->header_file_new); } - crypt_safe_free(rnc.password); + for (i = 0; i < MAX_SLOT; i++) + crypt_safe_free(rc->p[i].password); - free(rnc.device); - free(rnc.device_uuid); - - sigprocmask(SIG_UNBLOCK, &signals_open, NULL); + free(rc->device); + free(rc->device_uuid); } -int run_reencrypt(const char *device) +static int run_reencrypt(const char *device) { int r = -EINVAL; + struct reenc_ctx rc = {}; - if (initialize_context(device)) + if (initialize_context(&rc, device)) goto out; log_dbg("Running reencryption."); - if (!rnc.in_progress) { - if ((r = initialize_passphrase(rnc.device)) || - (r = backup_luks_headers()) || - (r = device_magic(MAKE_UNUSABLE))) + if (!rc.in_progress) { + if ((r = initialize_passphrase(&rc, rc.device)) || + (r = backup_luks_headers(&rc)) || + (r = device_magic(&rc, MAKE_UNUSABLE))) goto out; } else { - if ((r = initialize_passphrase(rnc.header_file_org))) + if ((r = initialize_passphrase(&rc, rc.header_file_new))) goto out; } - if ((r = activate_luks_headers())) + if ((r = activate_luks_headers(&rc))) goto out; - if ((r = copy_data())) + if ((r = copy_data(&rc))) goto out; - r = restore_luks_header(rnc.header_file_new); + r = restore_luks_header(&rc); out: - destroy_context(); + destroy_context(&rc); return r; } @@ -706,20 +1025,28 @@ int main(int argc, const char **argv) { "version", '\0', POPT_ARG_NONE, &opt_version_mode, 0, N_("Print package version"), NULL }, { "verbose", 'v', POPT_ARG_NONE, &opt_verbose, 0, N_("Shows more detailed error messages"), NULL }, { "debug", '\0', POPT_ARG_NONE, &opt_debug, 0, N_("Show debug messages"), NULL }, + { "block-size", 'B', POPT_ARG_INT, &opt_bsize, 0, N_("Reencryption block size"), N_("MiB") }, { "cipher", 'c', POPT_ARG_STRING, &opt_cipher, 0, N_("The cipher used to encrypt the disk (see /proc/crypto)"), NULL }, { "hash", 'h', POPT_ARG_STRING, &opt_hash, 0, N_("The hash used to create the encryption key from the passphrase"), NULL }, { "key-file", 'd', POPT_ARG_STRING, &opt_key_file, 0, N_("Read the key from a file."), NULL }, { "iter-time", 'i', POPT_ARG_INT, &opt_iteration_time, 0, N_("PBKDF2 iteration time for LUKS (in ms)"), N_("msecs") }, { "batch-mode", 'q', POPT_ARG_NONE, &opt_batch_mode, 0, N_("Do not ask for confirmation"), NULL }, + { "tries", 'T', POPT_ARG_INT, &opt_tries, 0, N_("How often the input of the passphrase can be retried"), NULL }, { "use-random", '\0', POPT_ARG_NONE, &opt_random, 0, N_("Use /dev/random for generating volume key."), NULL }, { "use-urandom", '\0', POPT_ARG_NONE, &opt_urandom, 0, N_("Use /dev/urandom for generating volume key."), NULL }, + { "use-directio", '\0', POPT_ARG_NONE, &opt_directio, 0, N_("Use direct-io when accesing devices."), NULL }, + { "write-log", '\0', POPT_ARG_NONE, &opt_write_log, 0, N_("Update log file after every block."), NULL }, + { "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") }, POPT_TABLEEND }; poptContext popt_context; int r; crypt_set_log_callback(NULL, _log, NULL); - log_err("WARNING: this is experimental code, it can completely break your data.\n"); + + set_int_block(1); setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); @@ -727,7 +1054,7 @@ int main(int argc, const char **argv) popt_context = poptGetContext(PACKAGE, argc, argv, popt_options, 0); poptSetOtherOptionHelp(popt_context, - N_("[OPTION...] ]")); + N_("[OPTION...] ]")); while((r = poptGetNextOpt(popt_context)) > 0) { if (r < 0) @@ -743,6 +1070,9 @@ int main(int argc, const char **argv) exit(EXIT_SUCCESS); } + if (!opt_batch_mode) + log_err(_("WARNING: this is experimental code, it can completely break your data.\n")); + action_argv = poptGetArgs(popt_context); if(!action_argv) usage(popt_context, EXIT_FAILURE, _("Argument required."), @@ -752,6 +1082,11 @@ int main(int argc, const char **argv) usage(popt_context, EXIT_FAILURE, _("Only one of --use-[u]random options is allowed."), 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_debug) { opt_verbose = 1; crypt_set_debug_level(-1); @@ -771,6 +1106,7 @@ int main(int argc, const char **argv) case -ENODEV: r = 4; break; case -ENOMEM: r = 3; break; case -EPERM: r = 2; break; + case -EAGAIN: case -EINVAL: case -ENOENT: case -ENOSYS: