diff --git a/ChangeLog b/ChangeLog index da92ac53..6aae0e4f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ 2010-04-06 Milan Broz * Prefer some device paths in status display. + * Support device topology detectionfor data alignment. 2010-02-25 Milan Broz * Do not verify unlocking passphrase in luksAddKey command. diff --git a/lib/internal.h b/lib/internal.h index 1c16f161..3fe96e36 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -120,4 +120,9 @@ void debug_processes_using_device(const char *name); int crypt_memlock_inc(struct crypt_device *ctx); int crypt_memlock_dec(struct crypt_device *ctx); +void get_topology_alignment(const char *device, + unsigned long *required_alignment, /* bytes */ + unsigned long *alignment_offset, /* bytes */ + unsigned long default_alignment); + #endif /* INTERNAL_H */ diff --git a/lib/setup.c b/lib/setup.c index 6576032a..5b262013 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -1092,16 +1092,25 @@ static int _crypt_format_luks1(struct crypt_device *cd, struct crypt_params_luks1 *params) { int r; + unsigned long required_alignment = DEFAULT_ALIGNMENT; + unsigned long alignment_offset = 0; if (!cd->device) { log_err(cd, _("Can't format LUKS without device.\n")); return -EINVAL; } + if (params && params->data_alignment) + required_alignment = params->data_alignment * SECTOR_SIZE; + else + get_topology_alignment(cd->device, &required_alignment, + &alignment_offset, DEFAULT_ALIGNMENT); + r = LUKS_generate_phdr(&cd->hdr, cd->volume_key, cipher, cipher_mode, (params && params->hash) ? params->hash : "sha1", uuid, LUKS_STRIPES, - params ? params->data_alignment: DEFAULT_ALIGNMENT, + required_alignment / SECTOR_SIZE, + alignment_offset / SECTOR_SIZE, cd->iteration_time, &cd->PBKDF2_per_sec, cd); if(r < 0) return r; diff --git a/lib/utils.c b/lib/utils.c index 9a6069e6..508347b6 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -683,3 +683,57 @@ int crypt_memlock_dec(struct crypt_device *ctx) } return _memlock_count ? 1 : 0; } + +/* DEVICE TOPOLOGY */ + +/* block device topology ioctls, introduced in 2.6.32 */ +#ifndef BLKIOMIN +#define BLKIOMIN _IO(0x12,120) +#define BLKIOOPT _IO(0x12,121) +#define BLKALIGNOFF _IO(0x12,122) +#endif + +void get_topology_alignment(const char *device, + unsigned long *required_alignment, /* bytes */ + unsigned long *alignment_offset, /* bytes */ + unsigned long default_alignment) +{ + unsigned int dev_alignment_offset = 0; + unsigned long min_io_size = 0, opt_io_size = 0; + int fd; + + *required_alignment = default_alignment; + *alignment_offset = 0; + + fd = open(device, O_RDONLY); + if (fd == -1) + return; + + /* minimum io size */ + if (ioctl(fd, BLKIOMIN, &min_io_size) == -1) { + log_dbg("Topology info for %s not supported, using default offset %lu bytes.", + device, default_alignment); + return; + } + + /* optimal io size */ + if (ioctl(fd, BLKIOOPT, &opt_io_size) == -1) + opt_io_size = min_io_size; + + /* alignment offset, bogus -1 means misaligned/unknown */ + if (ioctl(fd, BLKALIGNOFF, &dev_alignment_offset) == -1 || (int)dev_alignment_offset < 0) + dev_alignment_offset = 0; + + if (*required_alignment < min_io_size) + *required_alignment = min_io_size; + + if (*required_alignment < opt_io_size) + *required_alignment = opt_io_size; + + *alignment_offset = (unsigned long)dev_alignment_offset; + + log_dbg("Topology: IO (%lu/%lu), offset = %lu; Required alignment is %lu bytes.", + min_io_size, opt_io_size, *alignment_offset, *required_alignment); + + (void)close(fd); +} diff --git a/luks/keymanage.c b/luks/keymanage.c index 7f676514..44cffe48 100644 --- a/luks/keymanage.c +++ b/luks/keymanage.c @@ -424,6 +424,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, const char *cipherName, const char *cipherMode, const char *hashSpec, const char *uuid, unsigned int stripes, unsigned int alignPayload, + unsigned int alignOffset, uint32_t iteration_time_ms, uint64_t *PBKDF2_per_sec, struct crypt_device *ctx) @@ -488,7 +489,8 @@ int LUKS_generate_phdr(struct luks_phdr *header, } currentSector = round_up_modulo(currentSector, alignPayload); - header->payloadOffset=currentSector; + /* alignOffset - offset from natural device alignment provided by topology info */ + header->payloadOffset = currentSector + alignOffset; if (uuid && !uuid_parse(uuid, partitionUuid)) { log_err(ctx, _("Wrong UUID format provided, generating new one.\n")); diff --git a/luks/luks.h b/luks/luks.h index 8b490936..9cc663d8 100644 --- a/luks/luks.h +++ b/luks/luks.h @@ -91,6 +91,7 @@ int LUKS_generate_phdr( const char *uuid, unsigned int stripes, unsigned int alignPayload, + unsigned int alignOffset, uint32_t iteration_time_ms, uint64_t *PBKDF2_per_sec, struct crypt_device *ctx); diff --git a/tests/align_test b/tests/align_test new file mode 100755 index 00000000..cb3e78ab --- /dev/null +++ b/tests/align_test @@ -0,0 +1,76 @@ +#!/bin/bash + +CRYPTSETUP="../src/cryptsetup" +DEV="" + +add_device() { + modprobe scsi_debug $@ + DEV=/dev/$(grep scsi_debug /sys/block/*/device/model | cut -f4 -d /) + sleep 2 + [ -b $DEV ] || exit 100 +} + +cleanup() { + udevadm settle + rmmod scsi_debug 2>/dev/null + sleep 2 +} + +format() # key_bits expected [forced] +{ + if [ -z "$3" ] ; then + echo -n "Formatting using topology info ($1 bits key)...." + echo xxx| $CRYPTSETUP luksFormat $DEV -q -s $1 + else + echo -n "Formatting using forced offset $3 ($1 bits key)..." + echo xxx| $CRYPTSETUP luksFormat $DEV -q -s $1 --align-payload=$2 + fi + + ALIGN=$($CRYPTSETUP luksDump $DEV |grep "Payload offset" | sed -e s/.*\\t//) + #echo "ALIGN = $ALIGN" + + if [ $ALIGN -ne $2 ] ; then + echo "FAIL" + echo "Expected alignment differs: expected $2 != detected $ALIGN" + exit 100 + fi + echo "PASSED" +} + +modprobe --dry-run scsi_debug || exit 0 +cleanup + +echo "# Create desktop-class 4K drive" +echo "# (logical_block_size=512, physical_block_size=4096, alignment_offset=0)" +add_device dev_size_mb=16 sector_size=512 physblk_exp=3 num_tgts=1 +format 256 2112 +format 128 1088 +format 256 8192 8192 +format 128 8192 8192 +cleanup + +echo "# Create desktop-class 4K drive w/ 63-sector DOS partition compensation" +echo "# (logical_block_size=512, physical_block_size=4096, alignment_offset=3584)" +add_device dev_size_mb=16 sector_size=512 physblk_exp=3 lowest_aligned=7 num_tgts=1 +format 256 2119 +format 128 1095 +cleanup + +echo "# Create enterprise-class 4K drive" +echo "# (logical_block_size=4096, physical_block_size=4096, alignment_offset=0)" +add_device dev_size_mb=16 sector_size=4096 num_tgts=1 +format 256 2560 +format 128 1536 +cleanup + +echo "# Create classic 512b drive and stack dm-linear" +echo "# (logical_block_size=512, physical_block_size=512, alignment_offset=0)" +add_device dev_size_mb=16 sector_size=512 num_tgts=1 +DEV2=$DEV +DEV=/dev/mapper/luks0xbabe +dmsetup create luks0xbabe --table "0 32768 linear $DEV2 0" +format 256 2112 +format 128 1088 +format 128 8192 8192 +dmsetup remove luks0xbabe +cleanup