Add experimental LUKS offline reencryption utility.
[platform/upstream/cryptsetup.git] / src / crypt_reencrypt.c
1 /*
2  * crypt_reencrypt - crypt utility for offline reencryption
3  *
4  * Copyright (C) 2012 Milan Broz All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * version 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 /* The code works as follows:
21  *  - create backup (detached) headers fo old and new device
22  *  - mark original device unusable
23  *  - maps two devices, one with old header one with new onto
24  *    the _same_ underlying device
25  *  - with direct-io reads old device and copy to new device in defined steps
26  *  - keps simple off in file (allows restart)
27  *  - there is several windows when corruption can happen
28  */
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdint.h>
33 #include <stdarg.h>
34 #include <inttypes.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <sys/stat.h>
38 #include <sys/ioctl.h>
39 #include <linux/fs.h>
40 #include <fcntl.h>
41 #include <limits.h>
42 #include <signal.h>
43 #include <libcryptsetup.h>
44 #include <popt.h>
45
46 #include "cryptsetup.h"
47
48 static int opt_verbose = 0;
49 static int opt_debug = 0;
50 static const char *opt_cipher = NULL;
51 static const char *opt_hash = NULL;
52 static const char *opt_key_file = NULL;
53 static int opt_iteration_time = 1000;
54 static int opt_batch_mode = 0;
55 static int opt_version_mode = 0;
56 static int opt_random = 0;
57 static int opt_urandom = 0;
58
59 static const char **action_argv;
60 sigset_t signals_open;
61
62 struct {
63         char *device;
64         char *device_uuid;
65         uint64_t device_size;
66         uint64_t device_offset;
67
68         int in_progress:1;
69
70         char header_file_org[PATH_MAX];
71         char header_file_new[PATH_MAX];
72         char log_file[PATH_MAX];
73
74         char crypt_path_org[PATH_MAX];
75         char crypt_path_new[PATH_MAX];
76         int log_fd;
77
78         char *password;
79         size_t passwordLen;
80         int keyslot;
81 } rnc;
82
83 char MAGIC[]   = {'L','U','K','S', 0xba, 0xbe};
84 char NOMAGIC[] = {'L','U','K','S', 0xde, 0xad};
85 int  MAGIC_L = 6;
86
87 typedef enum {
88         MAKE_UNUSABLE,
89         MAKE_USABLE,
90         CHECK_UNUSABLE
91 } header_magic;
92
93 __attribute__((format(printf, 5, 6)))
94 static void clogger(struct crypt_device *cd, int level, const char *file,
95                    int line, const char *format, ...)
96 {
97         va_list argp;
98         char *target = NULL;
99
100         va_start(argp, format);
101
102         if (vasprintf(&target, format, argp) > 0) {
103                 if (level >= 0) {
104                         crypt_log(cd, level, target);
105                 } else if (opt_debug)
106                         printf("# %s\n", target);
107         }
108
109         va_end(argp);
110         free(target);
111 }
112
113 static void _log(int level, const char *msg, void *usrptr __attribute__((unused)))
114 {
115         switch(level) {
116
117         case CRYPT_LOG_NORMAL:
118                 fputs(msg, stdout);
119                 break;
120         case CRYPT_LOG_VERBOSE:
121                 if (opt_verbose)
122                         fputs(msg, stdout);
123                 break;
124         case CRYPT_LOG_ERROR:
125                 fputs(msg, stderr);
126                 break;
127         case CRYPT_LOG_DEBUG:
128                 if (opt_debug)
129                         printf("# %s\n", msg);
130                 break;
131         default:
132                 fprintf(stderr, "Internal error on logging class for msg: %s", msg);
133                 break;
134         }
135 }
136
137 static void _quiet_log(int level, const char *msg, void *usrptr)
138 {
139         if (!opt_verbose && (level == CRYPT_LOG_ERROR || level == CRYPT_LOG_NORMAL))
140                 level = CRYPT_LOG_VERBOSE;
141         _log(level, msg, usrptr);
142 }
143
144 static int alignment(int fd)
145 {
146         int alignment;
147
148         alignment = fpathconf(fd, _PC_REC_XFER_ALIGN);
149         if (alignment < 0)
150                 alignment = 4096;
151         return alignment;
152 }
153
154 static int device_magic(header_magic set_magic)
155 {
156         char *buf = NULL;
157         size_t block_size = 512;
158         int r, devfd;
159         ssize_t s;
160
161         devfd = open(rnc.device, O_RDWR | O_DIRECT);
162         if (devfd == -1)
163                 return errno == EBUSY ? -EBUSY : -EINVAL;
164
165         if (posix_memalign((void *)&buf, alignment(devfd), block_size)) {
166                 r = -ENOMEM;
167                 goto out;
168         }
169
170         s = read(devfd, buf, block_size);
171         if (s < 0 || s != block_size) {
172                 log_verbose(_("Cannot read device %s.\n"), rnc.device);
173                 close(devfd);
174                 return -EIO;
175         }
176
177         if (set_magic == MAKE_UNUSABLE && !memcmp(buf, MAGIC, MAGIC_L)) {
178                 log_dbg("Marking LUKS device %s unusable.", rnc.device);
179                 memcpy(buf, NOMAGIC, MAGIC_L);
180                 r = 0;
181
182         } else if (set_magic == MAKE_USABLE && !memcmp(buf, NOMAGIC, MAGIC_L)) {
183                 log_dbg("Marking LUKS device %s usable.", rnc.device);
184                 memcpy(buf, MAGIC, MAGIC_L);
185                 r = 0;
186         } else if (set_magic == CHECK_UNUSABLE) {
187                 r = memcmp(buf, NOMAGIC, MAGIC_L) ? -EINVAL : 0;
188                 if (!r)
189                         rnc.device_uuid = strndup(&buf[0xa8], 40);
190                 goto out;
191         } else
192                 r = -EINVAL;
193
194         if (!r) {
195                 if (lseek(devfd, 0, SEEK_SET) == -1)
196                         goto out;
197                 s = write(devfd, buf, block_size);
198                 if (s < 0 || s != block_size) {
199                         log_verbose(_("Cannot write device %s.\n"), rnc.device);
200                         r = -EIO;
201                 }
202         } else
203                 log_dbg("LUKS signature check failed for %s.", rnc.device);
204 out:
205         if (buf)
206                 memset(buf, 0, block_size);
207         free(buf);
208         close(devfd);
209         return r;
210 }
211
212 static int create_empty_header(const char *new_file, uint64_t size)
213 {
214         int fd, r = 0;
215         char *buf;
216
217         log_dbg("Creating empty file %s of size %lu.", new_file, (unsigned long)size);
218
219         if (!(buf = malloc(size)))
220                 return -ENOMEM;
221         memset(buf, 0, size);
222
223         fd = creat(new_file, S_IRUSR|S_IWUSR);
224         if(fd == -1) {
225                 free(buf);
226                 return -EINVAL;
227         }
228
229         if (write(fd, buf, size) < size)
230                 r = -EIO;
231
232         close(fd);
233         free(buf);
234         return r;
235 }
236
237 static int write_log(void)
238 {
239         static char buf[512];
240
241         //log_dbg("Updating LUKS reencryption log offset %" PRIu64 ".", offset);
242         memset(buf, 0, sizeof(buf));
243         snprintf(buf, sizeof(buf), "# LUKS reencryption log, DO NOT EDIT OR DELETE.\n"
244                 "version = %d\nUUID = %s\noffset = %" PRIu64 "\n# EOF\n",
245                 1, rnc.device_uuid, rnc.device_offset);
246
247         lseek(rnc.log_fd, 0, SEEK_SET);
248         write(rnc.log_fd, buf, sizeof(buf));
249         return 0;
250 }
251
252 static int parse_line_log(const char *line)
253 {
254         uint64_t u64;
255         int i;
256         char s[64];
257
258         /* comment */
259         if (*line == '#')
260                 return 0;
261
262         if (sscanf(line, "version = %d", &i) == 1) {
263                 if (i != 1) {
264                         log_dbg("Log: Unexpected version = %i", i);
265                         return -EINVAL;
266                 }
267         } else if (sscanf(line, "UUID = %40s", s) == 1) {
268                 if (!rnc.device_uuid || strcmp(rnc.device_uuid, s)) {
269                         log_dbg("Log: Unexpected UUID %s", s);
270                         return -EINVAL;
271                 }
272         } else if (sscanf(line, "offset = %" PRIu64, &u64) == 1) {
273                 log_dbg("Log: offset = %" PRIu64, u64);
274                 rnc.device_offset = u64;
275         } else
276                 return -EINVAL;
277
278         return 0;
279 }
280
281 static int parse_log(void)
282 {
283         static char buf[512];
284         char *start, *end;
285         ssize_t s;
286
287         s = read(rnc.log_fd, buf, sizeof(buf));
288         if (s == -1)
289                 return -EIO;
290
291         buf[511] = '\0';
292         start = buf;
293         do {
294                 end = strchr(start, '\n');
295                 if (end) {
296                         *end++ = '\0';
297                         if (parse_line_log(start)) {
298                                 log_err("Wrong log format.\n");
299                                 return -EINVAL;
300                         }
301                 }
302
303                 start = end;
304         } while (start);
305
306         return 0;
307 }
308
309 static int open_log(void)
310 {
311         struct stat st;
312
313         if(stat(rnc.log_file, &st) < 0) {
314                 log_dbg("Creating LUKS reencryption log file %s.", rnc.log_file);
315                 rnc.log_fd = open(rnc.log_file, O_RDWR|O_CREAT|O_DIRECT, S_IRUSR|S_IWUSR);
316                 if (rnc.log_fd == -1)
317                         return -EINVAL;
318                 if (write_log() < 0)
319                         return -EIO;
320         } else {
321                 log_dbg("Log file %s exists, restarting.", rnc.log_file);
322                 rnc.log_fd = open(rnc.log_file, O_RDWR|O_DIRECT);
323                 if (rnc.log_fd == -1)
324                         return -EINVAL;
325                 rnc.in_progress = 1;
326         }
327
328         /* Be sure it is correct format */
329         return parse_log();
330 }
331
332 static void close_log(void)
333 {
334         log_dbg("Closing LUKS reencryption log file %s.", rnc.log_file);
335         if (rnc.log_fd != -1)
336                 close(rnc.log_fd);
337 }
338
339 static int activate_luks_headers(void)
340 {
341         struct crypt_device *cd = NULL, *cd_new = NULL;
342         int r;
343
344         log_dbg("Activating LUKS devices from headers.");
345
346         if ((r = crypt_init(&cd, rnc.header_file_org)) ||
347             (r = crypt_load(cd, CRYPT_LUKS1, NULL)) ||
348             (r = crypt_set_data_device(cd, rnc.device)))
349                 goto out;
350
351         if ((r = crypt_activate_by_passphrase(cd, rnc.header_file_org,
352                 CRYPT_ANY_SLOT, rnc.password, rnc.passwordLen,
353                 CRYPT_ACTIVATE_READONLY)) < 0)
354                 goto out;
355
356         if ((r = crypt_init(&cd_new, rnc.header_file_new)) ||
357             (r = crypt_load(cd_new, CRYPT_LUKS1, NULL)) ||
358             (r = crypt_set_data_device(cd_new, rnc.device)))
359                 goto out;
360
361         if ((r = crypt_activate_by_passphrase(cd_new, rnc.header_file_new,
362                 CRYPT_ANY_SLOT, rnc.password, rnc.passwordLen,
363                 CRYPT_ACTIVATE_SHARED)) < 0)
364                 goto out;
365 out:
366         crypt_free(cd);
367         crypt_free(cd_new);
368         return r;
369 }
370
371 static int backup_luks_headers(void)
372 {
373         struct crypt_device *cd = NULL, *cd_new = NULL;
374         struct crypt_params_luks1 params = {0};
375         char cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
376         int r;
377
378         log_dbg("Creating LUKS header backup for device %s.", rnc.device);
379         if ((r = crypt_init(&cd, rnc.device)) ||
380             (r = crypt_load(cd, CRYPT_LUKS1, NULL)))
381                 goto out;
382
383         crypt_set_confirm_callback(cd, NULL, NULL);
384         if ((r = crypt_header_backup(cd, CRYPT_LUKS1, rnc.header_file_org)))
385                 goto out;
386
387         if ((r = create_empty_header(rnc.header_file_new,
388                                      crypt_get_data_offset(cd) * 512)))
389                 goto out;
390
391         params.hash = opt_hash ?: DEFAULT_LUKS1_HASH;
392         params.data_alignment = crypt_get_data_offset(cd);
393         params.data_device = rnc.device;
394
395         if ((r = crypt_init(&cd_new, rnc.header_file_new)))
396                 goto out;
397
398         if (opt_random)
399                 crypt_set_rng_type(cd_new, CRYPT_RNG_RANDOM);
400         else if (opt_urandom)
401                 crypt_set_rng_type(cd_new, CRYPT_RNG_URANDOM);
402
403         if (opt_iteration_time)
404                 crypt_set_iteration_time(cd_new, opt_iteration_time);
405
406         if (opt_cipher) {
407                 r = crypt_parse_name_and_mode(opt_cipher, cipher, NULL, cipher_mode);
408                 if (r < 0) {
409                         log_err(_("No known cipher specification pattern detected.\n"));
410                         goto out;
411                 }
412         }
413
414         if ((r = crypt_format(cd_new, CRYPT_LUKS1,
415                         opt_cipher ? cipher : crypt_get_cipher(cd),
416                         opt_cipher ? cipher_mode : crypt_get_cipher_mode(cd),
417                         crypt_get_uuid(cd),
418                         NULL, crypt_get_volume_key_size(cd), &params)))
419                 goto out;
420
421         if ((r = crypt_keyslot_add_by_volume_key(cd_new, rnc.keyslot,
422                                 NULL, 0, rnc.password, rnc.passwordLen)) < 0)
423                 goto out;
424
425 out:
426         crypt_free(cd);
427         crypt_free(cd_new);
428         return r;
429 }
430
431 static void remove_headers(void)
432 {
433         struct crypt_device *cd = NULL;
434
435         if (crypt_init(&cd, NULL))
436                 return;
437         crypt_set_log_callback(cd, _quiet_log, NULL);
438         (void)crypt_deactivate(cd, rnc.header_file_org);
439         (void)crypt_deactivate(cd, rnc.header_file_new);
440         crypt_free(cd);
441 }
442
443 static int restore_luks_header(const char *backup)
444 {
445         struct crypt_device *cd = NULL;
446         int r;
447
448         r = crypt_init(&cd, rnc.device);
449
450         if (r == 0) {
451                 crypt_set_confirm_callback(cd, NULL, NULL);
452                 r = crypt_header_restore(cd, CRYPT_LUKS1, backup);
453         }
454
455         crypt_free(cd);
456         return r;
457 }
458
459 static int copy_data(void)
460 {
461         int fd_old = -1, fd_new = -1, j;
462         size_t block_size = 1024 *1024;
463         int r = -EINVAL;
464         void *buf = NULL;
465         ssize_t s1, s2;
466
467         fd_old = open(rnc.crypt_path_org, O_RDONLY | O_DIRECT);
468         if (fd_old == -1)
469                 goto out;
470
471         fd_new = open(rnc.crypt_path_new, O_WRONLY | O_DIRECT);
472         if (fd_new == -1)
473                 goto out;
474
475         if (lseek(fd_old, rnc.device_offset, SEEK_SET) == -1)
476                 goto out;
477
478         if (lseek(fd_new, rnc.device_offset, SEEK_SET) == -1)
479                 goto out;
480
481         /* Check size */
482         if (ioctl(fd_old, BLKGETSIZE64, &rnc.device_size) < 0)
483                 goto out;
484
485         if (posix_memalign((void *)&buf, alignment(fd_new), block_size)) {
486                 r = -ENOMEM;
487                 goto out;
488         }
489
490         log_err("Reencrypting [");
491         j = 0;
492         while (rnc.device_offset < rnc.device_size) {
493                 s1 = read(fd_old, buf, block_size);
494                 if (s1 != block_size)
495                         log_err("Read error, expecting %d, got %d.\n", (int)block_size, (int)s1);
496                 if (s1 < 0)
497                         goto out;
498                 s2 = write(fd_new, buf, s1);
499                 if (s2 != block_size)
500                         log_err("Write error, expecting %d, got %d.\n", (int)block_size, (int)s2);
501                 rnc.device_offset += s1;
502                 write_log();
503                 if (rnc.device_offset > (j * (rnc.device_size / 10))) {
504                         log_err("-");
505                         j++;
506                 }
507         }
508         log_err("] Done.\n");
509         r = 0;
510
511 out:
512         if (fd_old != -1)
513                 close(fd_old);
514         if (fd_new != -1)
515                 close(fd_new);
516         free(buf);
517         return r;
518 }
519
520 static int initialize_uuid(void)
521 {
522         struct crypt_device *cd = NULL;
523         int r;
524
525         /* Try to load LUKS from device */
526         if ((r = crypt_init(&cd, rnc.device)))
527                 return r;
528         crypt_set_log_callback(cd, _quiet_log, NULL);
529         r = crypt_load(cd, CRYPT_LUKS1, NULL);
530         if (!r)
531                 rnc.device_uuid = strdup(crypt_get_uuid(cd));
532         else
533                 /* Reencryption already in progress - magic header? */
534                 r = device_magic(CHECK_UNUSABLE);
535
536         crypt_free(cd);
537         return r;
538 }
539
540 static int initialize_passphrase(const char *device)
541 {
542         struct crypt_device *cd = NULL;
543         int r;
544
545         if ((r = crypt_init(&cd, device)) ||
546             (r = crypt_load(cd, CRYPT_LUKS1, NULL)) ||
547             (r = crypt_set_data_device(cd, rnc.device)))
548                 goto out;
549
550         if ((r = crypt_get_key(_("Enter LUKS passphrase: "),
551                           &rnc.password, &rnc.passwordLen,
552                           0, 0, opt_key_file,
553                           0, 0, cd)) <0)
554                 goto out;
555
556         if ((r = crypt_activate_by_passphrase(cd, NULL,
557                 CRYPT_ANY_SLOT, rnc.password, rnc.passwordLen, 0) < 0))
558                 goto out;
559
560         if (r >= 0) {
561                 rnc.keyslot = r;
562                 r = 0;
563         }
564 out:
565         crypt_free(cd);
566         return r;
567 }
568
569 static int initialize_context(const char *device)
570 {
571         log_dbg("Initialising reencryption context.");
572
573         rnc.log_fd =-1;
574
575         if (!(rnc.device = strndup(device, PATH_MAX)))
576                 return -ENOMEM;
577
578         if (initialize_uuid())
579                 return -EINVAL;
580
581         /* Prepare device names */
582         if (snprintf(rnc.log_file, PATH_MAX,
583                      "LUKS-%s.log", rnc.device_uuid) < 0)
584                 return -ENOMEM;
585         if (snprintf(rnc.header_file_org, PATH_MAX,
586                      "LUKS-%s.org", rnc.device_uuid) < 0)
587                 return -ENOMEM;
588         if (snprintf(rnc.header_file_new, PATH_MAX,
589                      "LUKS-%s.new", rnc.device_uuid) < 0)
590                 return -ENOMEM;
591
592         /* Paths to encrypted devices */
593         if (snprintf(rnc.crypt_path_org, PATH_MAX,
594                      "%s/%s", crypt_get_dir(), rnc.header_file_org) < 0)
595                 return -ENOMEM;
596         if (snprintf(rnc.crypt_path_new, PATH_MAX,
597                      "%s/%s", crypt_get_dir(), rnc.header_file_new) < 0)
598                 return -ENOMEM;
599
600         remove_headers();
601
602         /* Block ctrl+c */
603         // FIXME: add some routine to handle it
604         sigemptyset(&signals_open);
605         sigaddset(&signals_open, SIGINT);
606         sigprocmask(SIG_SETMASK, &signals_open, NULL);
607
608         return open_log();
609 }
610
611 static void destroy_context(void)
612 {
613         log_dbg("Destroying reencryption context.");
614
615         close_log();
616         remove_headers();
617
618         if (rnc.device_offset == rnc.device_size) {
619                 unlink(rnc.log_file);
620                 unlink(rnc.header_file_org);
621                 unlink(rnc.header_file_new);
622         }
623
624         crypt_safe_free(rnc.password);
625
626         free(rnc.device);
627         free(rnc.device_uuid);
628
629         sigprocmask(SIG_UNBLOCK, &signals_open, NULL);
630 }
631
632 int run_reencrypt(const char *device)
633 {
634         int r = -EINVAL;
635
636         if (initialize_context(device))
637                 goto out;
638
639         log_dbg("Running reencryption.");
640
641         if (!rnc.in_progress) {
642                 if ((r = initialize_passphrase(rnc.device)) ||
643                     (r = backup_luks_headers()) ||
644                     (r = device_magic(MAKE_UNUSABLE)))
645                         goto out;
646         } else {
647                 if ((r = initialize_passphrase(rnc.header_file_org)))
648                         goto out;
649         }
650
651         if ((r = activate_luks_headers()))
652                 goto out;
653
654         if ((r = copy_data()))
655                 goto out;
656
657         r = restore_luks_header(rnc.header_file_new);
658 out:
659         destroy_context();
660         return r;
661 }
662
663 static __attribute__ ((noreturn)) void usage(poptContext popt_context,
664                                              int exitcode, const char *error,
665                                              const char *more)
666 {
667         poptPrintUsage(popt_context, stderr, 0);
668         if (error)
669                 log_err("%s: %s\n", more, error);
670         poptFreeContext(popt_context);
671         exit(exitcode);
672 }
673
674 static void help(poptContext popt_context,
675                  enum poptCallbackReason reason __attribute__((unused)),
676                  struct poptOption *key,
677                  const char *arg __attribute__((unused)),
678                  void *data __attribute__((unused)))
679 {
680         usage(popt_context, EXIT_SUCCESS, NULL, NULL);
681 }
682
683 static void _dbg_version_and_cmd(int argc, const char **argv)
684 {
685         int i;
686
687         log_std("# %s %s processing \"", PACKAGE_NAME, PACKAGE_VERSION);
688         for (i = 0; i < argc; i++) {
689                 if (i)
690                         log_std(" ");
691                 log_std("%s", argv[i]);
692         }
693         log_std("\"\n");
694 }
695
696 int main(int argc, const char **argv)
697 {
698         static struct poptOption popt_help_options[] = {
699                 { NULL,    '\0', POPT_ARG_CALLBACK, help, 0, NULL,                         NULL },
700                 { "help",  '?',  POPT_ARG_NONE,     NULL, 0, N_("Show this help message"), NULL },
701                 { "usage", '\0', POPT_ARG_NONE,     NULL, 0, N_("Display brief usage"),    NULL },
702                 POPT_TABLEEND
703         };
704         static struct poptOption popt_options[] = {
705                 { NULL,                '\0', POPT_ARG_INCLUDE_TABLE, popt_help_options, 0, N_("Help options:"), NULL },
706                 { "version",           '\0', POPT_ARG_NONE, &opt_version_mode,          0, N_("Print package version"), NULL },
707                 { "verbose",           'v',  POPT_ARG_NONE, &opt_verbose,               0, N_("Shows more detailed error messages"), NULL },
708                 { "debug",             '\0', POPT_ARG_NONE, &opt_debug,                 0, N_("Show debug messages"), NULL },
709                 { "cipher",            'c',  POPT_ARG_STRING, &opt_cipher,              0, N_("The cipher used to encrypt the disk (see /proc/crypto)"), NULL },
710                 { "hash",              'h',  POPT_ARG_STRING, &opt_hash,                0, N_("The hash used to create the encryption key from the passphrase"), NULL },
711                 { "key-file",          'd',  POPT_ARG_STRING, &opt_key_file,            0, N_("Read the key from a file."), NULL },
712                 { "iter-time",         'i',  POPT_ARG_INT, &opt_iteration_time,         0, N_("PBKDF2 iteration time for LUKS (in ms)"), N_("msecs") },
713                 { "batch-mode",        'q',  POPT_ARG_NONE, &opt_batch_mode,            0, N_("Do not ask for confirmation"), NULL },
714                 { "use-random",        '\0', POPT_ARG_NONE, &opt_random,                0, N_("Use /dev/random for generating volume key."), NULL },
715                 { "use-urandom",       '\0', POPT_ARG_NONE, &opt_urandom,               0, N_("Use /dev/urandom for generating volume key."), NULL },
716                 POPT_TABLEEND
717         };
718         poptContext popt_context;
719         int r;
720
721         crypt_set_log_callback(NULL, _log, NULL);
722         log_err("WARNING: this is experimental code, it can completely break your data.\n");
723
724         setlocale(LC_ALL, "");
725         bindtextdomain(PACKAGE, LOCALEDIR);
726         textdomain(PACKAGE);
727
728         popt_context = poptGetContext(PACKAGE, argc, argv, popt_options, 0);
729         poptSetOtherOptionHelp(popt_context,
730                                N_("[OPTION...] <action> <action-specific>]"));
731
732         while((r = poptGetNextOpt(popt_context)) > 0) {
733                 if (r < 0)
734                         break;
735         }
736
737         if (r < -1)
738                 usage(popt_context, EXIT_FAILURE, poptStrerror(r),
739                       poptBadOption(popt_context, POPT_BADOPTION_NOALIAS));
740         if (opt_version_mode) {
741                 log_std("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
742                 poptFreeContext(popt_context);
743                 exit(EXIT_SUCCESS);
744         }
745
746         action_argv = poptGetArgs(popt_context);
747         if(!action_argv)
748                 usage(popt_context, EXIT_FAILURE, _("Argument required."),
749                       poptGetInvocationName(popt_context));
750
751         if (opt_random && opt_urandom)
752                 usage(popt_context, EXIT_FAILURE, _("Only one of --use-[u]random options is allowed."),
753                       poptGetInvocationName(popt_context));
754
755         if (opt_debug) {
756                 opt_verbose = 1;
757                 crypt_set_debug_level(-1);
758                 _dbg_version_and_cmd(argc, argv);
759         }
760
761         r = run_reencrypt(action_argv[0]);
762
763         poptFreeContext(popt_context);
764
765         /* Translate exit code to simple codes */
766         switch (r) {
767         case 0:         r = EXIT_SUCCESS; break;
768         case -EEXIST:
769         case -EBUSY:    r = 5; break;
770         case -ENOTBLK:
771         case -ENODEV:   r = 4; break;
772         case -ENOMEM:   r = 3; break;
773         case -EPERM:    r = 2; break;
774         case -EINVAL:
775         case -ENOENT:
776         case -ENOSYS:
777         default:        r = EXIT_FAILURE;
778         }
779         return r;
780 }