Add --device-size option for reencryption tool.
authorMilan Broz <gmazyland@gmail.com>
Mon, 25 Jun 2012 13:34:11 +0000 (15:34 +0200)
committerMilan Broz <gmazyland@gmail.com>
Mon, 25 Jun 2012 13:34:11 +0000 (15:34 +0200)
ChangeLog
lib/utils_crypt.c
lib/utils_crypt.h
man/cryptsetup-reencrypt.8
src/cryptsetup_reencrypt.c

index e8e2720..7cd9839 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+2012-06-25  Milan Broz  <gmazyland@gmail.com>
+       * Add --device-size option for reencryption tool.
+
 2012-06-20  Milan Broz  <gmazyland@gmail.com>
        * Version 1.5.0-rc2.
 
index dd7496a..4b1dc70 100644 (file)
@@ -23,6 +23,8 @@
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
+#include <ctype.h>
+#include <limits.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -423,3 +425,65 @@ ssize_t crypt_hex_to_bytes(const char *hex, char **result, int safe_alloc)
        *result = bytes;
        return i;
 }
+
+/*
+ * Device size string parsing, suffixes:
+ * s|S - 512 bytes sectors
+ * k  |K  |m  |M  |g  |G  |t  |T   - 1024 base
+ * kiB|KiB|miB|MiB|giB|GiB|tiB|TiB - 1024 base
+ * kb |KB |mM |MB |gB |GB |tB |TB  - 1000 base
+ */
+int crypt_string_to_size(struct crypt_device *cd, const char *s, uint64_t *size)
+{
+       char *endp = NULL;
+       size_t len;
+       uint64_t mult_base, mult, tmp;
+
+       *size = strtoull(s, &endp, 10);
+       if (!isdigit(s[0]) ||
+           (errno == ERANGE && *size == ULLONG_MAX) ||
+           (errno != 0 && *size == 0))
+               return -EINVAL;
+
+       if (!endp)
+               return 0;
+
+       len = strlen(endp);
+       /* Allow "B" and "iB" suffixes */
+       if (len > 3 ||
+          (len == 3 && (endp[1] != 'i' || endp[2] != 'B')) ||
+          (len == 2 && endp[1] != 'B'))
+               return -EINVAL;
+
+       if (len == 1 || len == 3)
+               mult_base = 1024;
+       else
+               mult_base = 1000;
+
+       mult = 1;
+       switch (endp[0]) {
+       case 's':
+       case 'S': mult = 512;
+               break;
+       case 't':
+       case 'T': mult *= mult_base;
+       case 'g':
+       case 'G': mult *= mult_base;
+       case 'm':
+       case 'M': mult *= mult_base;
+       case 'k':
+       case 'K': mult *= mult_base;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       tmp = *size * mult;
+       if ((tmp / *size) != mult) {
+               log_dbg("Device size overflow.");
+               return -EINVAL;
+       }
+
+       *size = tmp;
+       return 0;
+}
index 7c4cb1c..692264f 100644 (file)
@@ -44,5 +44,6 @@ void crypt_safe_free(void *data);
 void *crypt_safe_realloc(void *data, size_t size);
 
 ssize_t crypt_hex_to_bytes(const char *hex, char **result, int safe_alloc);
+int crypt_string_to_size(struct crypt_device *cd, const char *s, uint64_t *size);
 
 #endif /* _UTILS_CRYPT_H */
index a32315f..b3ac6c0 100644 (file)
@@ -124,6 +124,23 @@ Use with extreme care - shrinked filesystems are usually unrecoverable.
 
 You cannot shrink device more than by 64 MiB (131072 sectors).
 .TP
+.B "\-\-device-size \fIsize[units]\fR"
+Instead of real device size, use specified value.
+
+It means that only specified area (from the start of the device
+to the specified size) will be reencrypted.
+
+WARNING: This is destructive operation.
+
+If no unit suffix is specified, the size is in bytes.
+
+Unit suffix can be S for 512 byte sectors, K/M/G/T (or KiB,MiB,GiB,TiB)
+for units with 1024 base or KB/MB/GB/TB for 1000 base (SI scale).
+
+WARNING: This is destructive operation.
+
+You cannot shrink device more than by 64 MiB (131072 sectors).
+.TP
 .B "\-\-new, N"
 Create new header (encrypt not yet encrypted device).
 
index df937c1..c6b7e58 100644 (file)
@@ -67,6 +67,9 @@ static int opt_key_slot = CRYPT_ANY_SLOT;
 static int opt_key_size = 0;
 static int opt_new = 0;
 
+static const char *opt_device_size_str = NULL;
+static uint64_t opt_device_size = 0;
+
 static const char **action_argv;
 
 static volatile int quit = 0;
@@ -75,7 +78,8 @@ static volatile int quit = 0;
 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;
 
@@ -750,6 +754,10 @@ static int copy_data_forward(struct reenc_ctx *rc, int fd_old, int fd_new,
                        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.",
@@ -861,11 +869,13 @@ static int copy_data(struct reenc_ctx *rc)
        }
 
        /* 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;
@@ -1230,6 +1240,7 @@ int main(int argc, const char **argv)
                { "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") },
+               { "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
        };
@@ -1311,6 +1322,11 @@ int main(int argc, const char **argv)
                usage(popt_context, EXIT_FAILURE, _("Option --new must be used together with --reduce_device_size."),
                      poptGetInvocationName(popt_context));
 
+       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_debug) {
                opt_verbose = 1;
                crypt_set_debug_level(-1);