diff --git a/ChangeLog b/ChangeLog index e8e27205..7cd9839d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2012-06-25 Milan Broz + * Add --device-size option for reencryption tool. + 2012-06-20 Milan Broz * Version 1.5.0-rc2. diff --git a/lib/utils_crypt.c b/lib/utils_crypt.c index dd7496a3..4b1dc70c 100644 --- a/lib/utils_crypt.c +++ b/lib/utils_crypt.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -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; +} diff --git a/lib/utils_crypt.h b/lib/utils_crypt.h index 7c4cb1c1..692264f8 100644 --- a/lib/utils_crypt.h +++ b/lib/utils_crypt.h @@ -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 */ diff --git a/man/cryptsetup-reencrypt.8 b/man/cryptsetup-reencrypt.8 index a32315fb..b3ac6c0e 100644 --- a/man/cryptsetup-reencrypt.8 +++ b/man/cryptsetup-reencrypt.8 @@ -122,6 +122,23 @@ partition (so last sectors contains no data). WARNING: This is destructive operation and cannot be reverted. 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" diff --git a/src/cryptsetup_reencrypt.c b/src/cryptsetup_reencrypt.c index df937c12..c6b7e586 100644 --- a/src/cryptsetup_reencrypt.c +++ b/src/cryptsetup_reencrypt.c @@ -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);