From 65f975655cde3777d4e9effd9635ef6fe20b3057 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Sun, 12 Aug 2012 21:56:09 +0200 Subject: [PATCH] New device access backend. Allocate loop device late (only when real block device needed). Rework underlying device/file access functions. Move all device (and ioctl) access to utils_device.c. Allows using file where appropriate without allocation loop device. --- ChangeLog | 4 + lib/Makefile.am | 1 + lib/internal.h | 51 +++-- lib/libdevmapper.c | 69 +++---- lib/loopaes/loopaes.c | 7 +- lib/luks1/keyencryption.c | 47 ++--- lib/luks1/keymanage.c | 134 ++++++------ lib/luks1/luks.h | 14 +- lib/setup.c | 234 +++++++++------------ lib/utils.c | 295 ++------------------------ lib/utils_device.c | 420 ++++++++++++++++++++++++++++++++++++++ lib/utils_devpath.c | 77 ------- lib/utils_dm.h | 6 +- lib/utils_wipe.c | 46 +++-- lib/verity/verity.c | 26 ++- lib/verity/verity.h | 7 - lib/verity/verity_hash.c | 35 ++-- 17 files changed, 745 insertions(+), 728 deletions(-) create mode 100644 lib/utils_device.c diff --git a/ChangeLog b/ChangeLog index eabc66f1..0a8e9d3e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2012-08-12 Milan Broz + * Allocate loop device late (only when real block device needed). + * Rework underlying device/file access functions. + 2012-07-10 Milan Broz * Version 1.5.0. diff --git a/lib/Makefile.am b/lib/Makefile.am index 5da6a0ef..0b95c624 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -59,6 +59,7 @@ libcryptsetup_la_SOURCES = \ utils_wipe.c \ utils_fips.c \ utils_fips.h \ + utils_device.c \ libdevmapper.c \ utils_dm.h \ volumekey.c \ diff --git a/lib/internal.h b/lib/internal.h index c39c7ce9..2d7b5db2 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -63,29 +63,43 @@ struct volume_key *crypt_alloc_volume_key(unsigned keylength, const char *key); struct volume_key *crypt_generate_volume_key(struct crypt_device *cd, unsigned keylength); void crypt_free_volume_key(struct volume_key *vk); +/* Device backend */ +struct device; +int device_alloc(struct device **device, const char *path); +void device_free(struct device *device); +const char *device_path(const struct device *device); +const char *device_block_path(const struct device *device); +void device_topology_alignment(struct device *device, + unsigned long *required_alignment, /* bytes */ + unsigned long *alignment_offset, /* bytes */ + unsigned long default_alignment); +int device_block_size(struct device *device); +int device_read_ahead(struct device *device, uint32_t *read_ahead); +int device_size(struct device *device, uint64_t *size); + +enum devcheck { DEV_OK = 0, DEV_EXCL = 1, DEV_SHARED = 2 }; +int device_block_adjust(struct crypt_device *cd, + struct device *device, + enum devcheck device_check, + uint64_t device_offset, + uint64_t *size, + uint32_t *flags); + +/* Receive backend devices from context helpers */ +struct device *crypt_metadata_device(struct crypt_device *cd); +struct device *crypt_data_device(struct crypt_device *cd); + int crypt_confirm(struct crypt_device *cd, const char *msg); char *crypt_lookup_dev(const char *dev_id); -int crypt_sysfs_check_crypt_segment(const char *device, uint64_t offset, uint64_t size); int crypt_sysfs_get_rotational(int major, int minor, int *rotational); -int sector_size_for_device(const char *device); -int device_read_ahead(const char *dev, uint32_t *read_ahead); -ssize_t write_blockwise(int fd, void *buf, size_t count); -ssize_t read_blockwise(int fd, void *_buf, size_t count); -ssize_t write_lseek_blockwise(int fd, char *buf, size_t count, off_t offset); -int device_ready(struct crypt_device *cd, const char *device, int mode); -int device_size(const char *device, uint64_t *size); +ssize_t write_blockwise(int fd, int bsize, void *buf, size_t count); +ssize_t read_blockwise(int fd, int bsize, void *_buf, size_t count); +ssize_t write_lseek_blockwise(int fd, int bsize, char *buf, size_t count, off_t offset); unsigned crypt_getpagesize(void); -enum devcheck { DEV_OK = 0, DEV_EXCL = 1, DEV_SHARED = 2 }; -int device_check_and_adjust(struct crypt_device *cd, - const char *device, - enum devcheck device_check, - uint64_t *size, - uint64_t *offset, - uint32_t *flags); void logger(struct crypt_device *cd, int class, const char *file, int line, const char *format, ...); #define log_dbg(x...) logger(NULL, CRYPT_LOG_DEBUG, __FILE__, __LINE__, x) @@ -98,11 +112,6 @@ int crypt_get_debug_level(void); 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); - int crypt_random_init(struct crypt_device *ctx); int crypt_random_get(struct crypt_device *ctx, char *buf, size_t len, int quality); void crypt_random_exit(void); @@ -131,7 +140,7 @@ typedef enum { * random algorithm */ } crypt_wipe_type; -int crypt_wipe(const char *device, +int crypt_wipe(struct device *device, uint64_t offset, uint64_t sectors, crypt_wipe_type type, diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c index 86bca485..b2009d58 100644 --- a/lib/libdevmapper.c +++ b/lib/libdevmapper.c @@ -287,14 +287,16 @@ static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd) hex_key(hexkey, dmd->u.crypt.vk->keylength, dmd->u.crypt.vk->key); max_size = strlen(hexkey) + strlen(dmd->u.crypt.cipher) + - strlen(dmd->data_device) + strlen(features) + 64; + strlen(device_block_path(dmd->data_device)) + + strlen(features) + 64; params = crypt_safe_alloc(max_size); if (!params) goto out; r = snprintf(params, max_size, "%s %s %" PRIu64 " %s %" PRIu64 "%s", dmd->u.crypt.cipher, hexkey, dmd->u.crypt.iv_offset, - dmd->data_device, dmd->u.crypt.offset, features); + device_block_path(dmd->data_device), dmd->u.crypt.offset, + features); if (r < 0 || r >= max_size) { crypt_safe_free(params); params = NULL; @@ -328,8 +330,8 @@ static char *get_dm_verity_params(struct crypt_params_verity *vp, strncpy(hexsalt, "-", 2); max_size = strlen(hexroot) + strlen(hexsalt) + - strlen(dmd->data_device) + - strlen(dmd->u.verity.hash_device) + + strlen(device_block_path(dmd->data_device)) + + strlen(device_block_path(dmd->u.verity.hash_device)) + strlen(vp->hash_name) + 128; params = crypt_safe_alloc(max_size); @@ -338,8 +340,8 @@ static char *get_dm_verity_params(struct crypt_params_verity *vp, r = snprintf(params, max_size, "%u %s %s %u %u %" PRIu64 " %" PRIu64 " %s %s %s", - vp->hash_type, dmd->data_device, - dmd->u.verity.hash_device, + vp->hash_type, device_block_path(dmd->data_device), + device_block_path(dmd->u.verity.hash_device), vp->data_block_size, vp->hash_block_size, vp->data_size, dmd->u.verity.hash_offset, vp->hash_name, hexroot, hexsalt); @@ -491,7 +493,7 @@ static void dm_prepare_uuid(const char *name, const char *type, const char *uuid } static int _dm_create_device(const char *name, const char *type, - const char *device, uint32_t flags, + struct device *device, uint32_t flags, const char *uuid, uint64_t size, char *params, int reload) { @@ -708,6 +710,7 @@ static int _dm_query_crypt(uint32_t get_flags, uint64_t val64; char *rcipher, *key_, *rdevice, *endp, buffer[3], *arg; unsigned int i; + int r; memset(dmd, 0, sizeof(*dmd)); dmd->target = DM_CRYPT; @@ -730,8 +733,13 @@ static int _dm_query_crypt(uint32_t get_flags, /* device */ rdevice = strsep(¶ms, " "); - if (get_flags & DM_ACTIVE_DEVICE) - dmd->data_device = crypt_lookup_dev(rdevice); + if (get_flags & DM_ACTIVE_DEVICE) { + arg = crypt_lookup_dev(rdevice); + r = device_alloc(&dmd->data_device, arg); + free(arg); + if (r < 0 && r != -ENOTBLK) + return r; + } /*offset */ if (!params) @@ -805,6 +813,7 @@ static int _dm_query_verity(uint32_t get_flags, uint64_t val64; ssize_t len; char *str, *str2; + int r; if (get_flags & DM_ACTIVE_VERITY_PARAMS) vp = dmd->u.verity.vp; @@ -826,15 +835,25 @@ static int _dm_query_verity(uint32_t get_flags, str = strsep(¶ms, " "); if (!params) return -EINVAL; - if (get_flags & DM_ACTIVE_DEVICE) - dmd->data_device = crypt_lookup_dev(str); + if (get_flags & DM_ACTIVE_DEVICE) { + str2 = crypt_lookup_dev(str); + r = device_alloc(&dmd->data_device, str2); + free(str2); + if (r < 0 && r != -ENOTBLK) + return r; + } /* hash device */ str = strsep(¶ms, " "); if (!params) return -EINVAL; - if (get_flags & DM_ACTIVE_VERITY_HASH_DEVICE) - dmd->u.verity.hash_device = crypt_lookup_dev(str); + if (get_flags & DM_ACTIVE_VERITY_HASH_DEVICE) { + str2 = crypt_lookup_dev(str); + r = device_alloc(&dmd->u.verity.hash_device, str2); + free(str2); + if (r < 0 && r != -ENOTBLK) + return r; + } /* data block size*/ val32 = strtoul(params, ¶ms, 10); @@ -1072,27 +1091,3 @@ int dm_is_dm_kernel_name(const char *name) { return strncmp(name, "dm-", 3) ? 0 : 1; } - -int dm_check_segment(const char *name, uint64_t offset, uint64_t size) -{ - struct crypt_dm_active_device dmd; - int r; - - log_dbg("Checking segments for device %s.", name); - - r = dm_query_device(name, 0, &dmd); - if (r < 0) - return r; - - if (offset >= (dmd.u.crypt.offset + dmd.size) || - (offset + size) <= dmd.u.crypt.offset) - r = 0; - else - r = -EBUSY; - - log_dbg("seg: %" PRIu64 " - %" PRIu64 ", new %" PRIu64 " - %" PRIu64 "%s", - dmd.u.crypt.offset, dmd.u.crypt.offset + dmd.size, offset, offset + size, - r ? " (overlapping)" : " (ok)"); - - return r; -} diff --git a/lib/loopaes/loopaes.c b/lib/loopaes/loopaes.c index 35345ebf..202f3c99 100644 --- a/lib/loopaes/loopaes.c +++ b/lib/loopaes/loopaes.c @@ -196,7 +196,7 @@ int LOOPAES_activate(struct crypt_device *cd, .uuid = crypt_get_uuid(cd), .size = 0, .flags = flags, - .data_device = crypt_get_device_name(cd), + .data_device = crypt_data_device(cd), .u.crypt = { .cipher = NULL, .vk = vk, @@ -205,9 +205,8 @@ int LOOPAES_activate(struct crypt_device *cd, } }; - - r = device_check_and_adjust(cd, dmd.data_device, DEV_EXCL, - &dmd.size, &dmd.u.crypt.offset, &flags); + r = device_block_adjust(cd, dmd.data_device, DEV_EXCL, + dmd.u.crypt.offset, &dmd.size, &dmd.flags); if (r) return r; diff --git a/lib/luks1/keyencryption.c b/lib/luks1/keyencryption.c index c634b68f..4dc4e4b3 100644 --- a/lib/luks1/keyencryption.c +++ b/lib/luks1/keyencryption.c @@ -51,17 +51,16 @@ static uint64_t cleaner_size = 0; static int devfd=-1; static int setup_mapping(const char *cipher, const char *name, - const char *device, - struct volume_key *vk, + int bsize, struct volume_key *vk, unsigned int sector, size_t srcLength, int mode, struct crypt_device *ctx) { - int device_sector_size = sector_size_for_device(device); + struct device *device = crypt_metadata_device(ctx); struct crypt_dm_active_device dmd = { .target = DM_CRYPT, .uuid = NULL, - .size = 0, - .flags = 0, + .size = round_up_modulo(srcLength, bsize) / SECTOR_SIZE, + .flags = CRYPT_ACTIVATE_PRIVATE, .data_device = device, .u.crypt = { .cipher = cipher, @@ -70,22 +69,17 @@ static int setup_mapping(const char *cipher, const char *name, .iv_offset = 0, } }; + int r; - dmd.flags = CRYPT_ACTIVATE_PRIVATE; if (mode == O_RDONLY) dmd.flags |= CRYPT_ACTIVATE_READONLY; - /* - * we need to round this to nearest multiple of the underlying - * device's sector size, otherwise the mapping will be refused. - */ - if(device_sector_size < 0) { - log_err(ctx, _("Unable to obtain sector size for %s"), device); - return -EINVAL; - } - dmd.size = round_up_modulo(srcLength,device_sector_size)/SECTOR_SIZE; + r = device_block_adjust(ctx, dmd.data_device, DEV_OK, + dmd.u.crypt.offset, &dmd.size, &dmd.flags); + if (r < 0) + return r; + cleaner_size = dmd.size; - return dm_create_device(name, "TEMP", &dmd, 0); } @@ -116,9 +110,8 @@ static const char *_error_hint(char *cipherMode, size_t keyLength) static int LUKS_endec_template(char *src, size_t srcLength, struct luks_phdr *hdr, struct volume_key *vk, - const char *device, unsigned int sector, - ssize_t (*func)(int, void *, size_t), + ssize_t (*func)(int, int, void *, size_t), int mode, struct crypt_device *ctx) { @@ -127,6 +120,7 @@ static int LUKS_endec_template(char *src, size_t srcLength, char *dmCipherSpec = NULL; const char *dmDir = dm_get_dir(); int r = -1; + int bsize = device_block_size(crypt_metadata_device(ctx)); if(dmDir == NULL) { log_err(ctx, _("Failed to obtain device mapper directory.")); @@ -142,12 +136,11 @@ static int LUKS_endec_template(char *src, size_t srcLength, signal(SIGINT, sigint_handler); cleaner_name = name; - r = setup_mapping(dmCipherSpec, name, device, - vk, sector, srcLength, mode, ctx); + r = setup_mapping(dmCipherSpec, name, bsize, vk, sector, srcLength, mode, ctx); if(r < 0) { log_err(ctx, _("Failed to setup dm-crypt key mapping for device %s.\n" "Check that kernel supports %s cipher (check syslog for more info).\n%s"), - device, dmCipherSpec, + device_path(crypt_metadata_device(ctx)), dmCipherSpec, _error_hint(hdr->cipherMode, vk->keylength * 8)); r = -EIO; goto out1; @@ -160,7 +153,7 @@ static int LUKS_endec_template(char *src, size_t srcLength, goto out2; } - r = func(devfd,src,srcLength); + r = func(devfd, bsize, src, srcLength); if(r < 0) { log_err(ctx, _("Failed to access temporary keystore device.\n")); r = -EIO; @@ -186,21 +179,19 @@ static int LUKS_endec_template(char *src, size_t srcLength, int LUKS_encrypt_to_storage(char *src, size_t srcLength, struct luks_phdr *hdr, struct volume_key *vk, - const char *device, unsigned int sector, struct crypt_device *ctx) { - return LUKS_endec_template(src,srcLength,hdr,vk, device, - sector, write_blockwise, O_RDWR, ctx); + return LUKS_endec_template(src, srcLength, hdr, vk, sector, + write_blockwise, O_RDWR, ctx); } int LUKS_decrypt_from_storage(char *dst, size_t dstLength, struct luks_phdr *hdr, struct volume_key *vk, - const char *device, unsigned int sector, struct crypt_device *ctx) { - return LUKS_endec_template(dst,dstLength,hdr,vk, device, - sector, read_blockwise, O_RDONLY, ctx); + return LUKS_endec_template(dst, dstLength, hdr, vk, sector, + read_blockwise, O_RDONLY, ctx); } diff --git a/lib/luks1/keymanage.c b/lib/luks1/keymanage.c index 20dd6d1a..626229e1 100644 --- a/lib/luks1/keymanage.c +++ b/lib/luks1/keymanage.c @@ -63,16 +63,16 @@ static uint64_t LUKS_device_sectors(size_t keyLen) return sector; } -static int LUKS_check_device_size(struct crypt_device *ctx, const char *device, - size_t keyLength) +static int LUKS_check_device_size(struct crypt_device *ctx, size_t keyLength) { + struct device *device = crypt_metadata_device(ctx); uint64_t dev_sectors, hdr_sectors; if (!keyLength) return -EINVAL; if(device_size(device, &dev_sectors)) { - log_dbg("Cannot get device size for device %s.", device); + log_dbg("Cannot get device size for device %s.", device_path(device)); return -EIO; } @@ -82,7 +82,7 @@ static int LUKS_check_device_size(struct crypt_device *ctx, const char *device, PRIu64 " sectors.",keyLength, dev_sectors, hdr_sectors); if (hdr_sectors > dev_sectors) { - log_err(ctx, _("Device %s is too small.\n"), device); + log_err(ctx, _("Device %s is too small.\n"), device_path(device)); return -EINVAL; } @@ -144,10 +144,10 @@ static const char *dbg_slot_state(crypt_keyslot_info ki) int LUKS_hdr_backup( const char *backup_file, - const char *device, struct luks_phdr *hdr, struct crypt_device *ctx) { + struct device *device = crypt_metadata_device(ctx); int r = 0, devfd = -1; ssize_t buffer_size; char *buffer = NULL; @@ -158,7 +158,7 @@ int LUKS_hdr_backup( return -EINVAL; } - r = LUKS_read_phdr(device, hdr, 1, 0, ctx); + r = LUKS_read_phdr(hdr, 1, 0, ctx); if (r) return r; @@ -172,14 +172,14 @@ int LUKS_hdr_backup( log_dbg("Storing backup of header (%u bytes) and keyslot area (%u bytes).", sizeof(*hdr), buffer_size - LUKS_ALIGN_KEYSLOTS); - devfd = open(device, O_RDONLY | O_DIRECT | O_SYNC); + devfd = open(device_path(device), O_RDONLY | O_DIRECT | O_SYNC); if(devfd == -1) { - log_err(ctx, _("Device %s is not a valid LUKS device.\n"), device); + log_err(ctx, _("Device %s is not a valid LUKS device.\n"), device_path(device)); r = -EINVAL; goto out; } - if(read_blockwise(devfd, buffer, buffer_size) < buffer_size) { + if(read_blockwise(devfd, device_block_size(device), buffer, buffer_size) < buffer_size) { r = -EIO; goto out; } @@ -210,10 +210,10 @@ out: int LUKS_hdr_restore( const char *backup_file, - const char *device, struct luks_phdr *hdr, struct crypt_device *ctx) { + struct device *device = crypt_metadata_device(ctx); int r = 0, devfd = -1, diff_uuid = 0; ssize_t buffer_size = 0; char *buffer = NULL, msg[200]; @@ -225,7 +225,7 @@ int LUKS_hdr_restore( return -EINVAL; } - r = LUKS_read_phdr_backup(backup_file, device, &hdr_file, 0, ctx); + r = LUKS_read_phdr_backup(backup_file, &hdr_file, 0, ctx); if (!r) buffer_size = LUKS_device_sectors(hdr_file.keyBytes) << SECTOR_SHIFT; @@ -255,9 +255,9 @@ int LUKS_hdr_restore( } close(devfd); - r = LUKS_read_phdr(device, hdr, 0, 0, ctx); + r = LUKS_read_phdr(hdr, 0, 0, ctx); if (r == 0) { - log_dbg("Device %s already contains LUKS header, checking UUID and offset.", device); + log_dbg("Device %s already contains LUKS header, checking UUID and offset.", device_path(device)); if(hdr->payloadOffset != hdr_file.payloadOffset || hdr->keyBytes != hdr_file.keyBytes) { log_err(ctx, _("Data offset or key size differs on device and backup, restore failed.\n")); @@ -268,7 +268,7 @@ int LUKS_hdr_restore( diff_uuid = 1; } - if (snprintf(msg, sizeof(msg), _("Device %s %s%s"), device, + if (snprintf(msg, sizeof(msg), _("Device %s %s%s"), device_path(device), r ? _("does not contain LUKS header. Replacing header can destroy data on that device.") : _("already contains LUKS header. Replacing header will destroy existing keyslots."), diff_uuid ? _("\nWARNING: real device header has different UUID than backup!") : "") < 0) { @@ -282,23 +282,23 @@ int LUKS_hdr_restore( } log_dbg("Storing backup of header (%u bytes) and keyslot area (%u bytes) to device %s.", - sizeof(*hdr), buffer_size - LUKS_ALIGN_KEYSLOTS, device); + sizeof(*hdr), buffer_size - LUKS_ALIGN_KEYSLOTS, device_path(device)); - devfd = open(device, O_WRONLY | O_DIRECT | O_SYNC); + devfd = open(device_path(device), O_WRONLY | O_DIRECT | O_SYNC); if(devfd == -1) { - log_err(ctx, _("Cannot open device %s.\n"), device); + log_err(ctx, _("Cannot open device %s.\n"), device_path(device)); r = -EINVAL; goto out; } - if(write_blockwise(devfd, buffer, buffer_size) < buffer_size) { + if (write_blockwise(devfd, device_block_size(device), buffer, buffer_size) < buffer_size) { r = -EIO; goto out; } close(devfd); /* Be sure to reload new data */ - r = LUKS_read_phdr(device, hdr, 1, 0, ctx); + r = LUKS_read_phdr(hdr, 1, 0, ctx); out: if (devfd != -1) close(devfd); @@ -307,7 +307,7 @@ out: } /* This routine should do some just basic recovery for known problems. */ -static int _keyslot_repair(const char *device, struct luks_phdr *phdr, struct crypt_device *ctx) +static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx) { struct luks_phdr temp_phdr; const unsigned char *sector = (const unsigned char*)phdr; @@ -336,7 +336,7 @@ static int _keyslot_repair(const char *device, struct luks_phdr *phdr, struct cr phdr->hashSpec,phdr->uuid, LUKS_STRIPES, phdr->payloadOffset, 0, 1, &PBKDF2_per_sec, - "/dev/null", ctx); + 1, ctx); if (r < 0) { log_err(ctx, _("Repair failed.")); goto out; @@ -384,7 +384,7 @@ static int _keyslot_repair(const char *device, struct luks_phdr *phdr, struct cr if (need_write) { log_verbose(ctx, _("Writing LUKS header to disk.\n")); - r = LUKS_write_phdr(device, phdr, ctx); + r = LUKS_write_phdr(phdr, ctx); } out: crypt_free_volume_key(vk); @@ -441,7 +441,7 @@ static int _check_and_convert_hdr(const char *device, if (repair) { if (r == -EINVAL) - r = _keyslot_repair(device, hdr, ctx); + r = _keyslot_repair(hdr, ctx); else log_verbose(ctx, _("No known problems detected for LUKS header.\n")); } @@ -464,7 +464,6 @@ static void LUKS_fix_header_compatible(struct luks_phdr *header) } int LUKS_read_phdr_backup(const char *backup_file, - const char *device, struct luks_phdr *hdr, int require_luks_device, struct crypt_device *ctx) @@ -477,7 +476,7 @@ int LUKS_read_phdr_backup(const char *backup_file, devfd = open(backup_file, O_RDONLY); if(-1 == devfd) { - log_err(ctx, _("Cannot open file %s.\n"), device); + log_err(ctx, _("Cannot open file %s.\n"), backup_file); return -EINVAL; } @@ -493,12 +492,12 @@ int LUKS_read_phdr_backup(const char *backup_file, return r; } -int LUKS_read_phdr(const char *device, - struct luks_phdr *hdr, +int LUKS_read_phdr(struct luks_phdr *hdr, int require_luks_device, int repair, struct crypt_device *ctx) { + struct device *device = crypt_metadata_device(ctx); ssize_t hdr_size = sizeof(struct luks_phdr); int devfd = 0, r = 0; @@ -506,31 +505,31 @@ int LUKS_read_phdr(const char *device, return -EINVAL; log_dbg("Reading LUKS header of size %d from device %s", - hdr_size, device); + hdr_size, device_path(device)); - devfd = open(device,O_RDONLY | O_DIRECT | O_SYNC); - if(-1 == devfd) { - log_err(ctx, _("Cannot open device %s.\n"), device); + devfd = open(device_path(device), O_RDONLY | O_DIRECT | O_SYNC); + if (devfd == -1) { + log_err(ctx, _("Cannot open device %s.\n"), device_path(device)); return -EINVAL; } - if (read_blockwise(devfd, hdr, hdr_size) < hdr_size) + if (read_blockwise(devfd, device_block_size(device), hdr, hdr_size) < hdr_size) r = -EIO; else - r = _check_and_convert_hdr(device, hdr, require_luks_device, + r = _check_and_convert_hdr(device_path(device), hdr, require_luks_device, repair, ctx); if (!r) - r = LUKS_check_device_size(ctx, device, hdr->keyBytes); + r = LUKS_check_device_size(ctx, hdr->keyBytes); close(devfd); return r; } -int LUKS_write_phdr(const char *device, - struct luks_phdr *hdr, +int LUKS_write_phdr(struct luks_phdr *hdr, struct crypt_device *ctx) { + struct device *device = crypt_metadata_device(ctx); ssize_t hdr_size = sizeof(struct luks_phdr); int devfd = 0; unsigned int i; @@ -538,15 +537,15 @@ int LUKS_write_phdr(const char *device, int r; log_dbg("Updating LUKS header of size %d on device %s", - sizeof(struct luks_phdr), device); + sizeof(struct luks_phdr), device_path(device)); - r = LUKS_check_device_size(ctx, device, hdr->keyBytes); + r = LUKS_check_device_size(ctx, hdr->keyBytes); if (r) return r; - devfd = open(device,O_RDWR | O_DIRECT | O_SYNC); + devfd = open(device_path(device), O_RDWR | O_DIRECT | O_SYNC); if(-1 == devfd) { - log_err(ctx, _("Cannot open device %s.\n"), device); + log_err(ctx, _("Cannot open device %s.\n"), device_path(device)); return -EINVAL; } @@ -565,16 +564,17 @@ int LUKS_write_phdr(const char *device, convHdr.keyblock[i].stripes = htonl(hdr->keyblock[i].stripes); } - r = write_blockwise(devfd, &convHdr, hdr_size) < hdr_size ? -EIO : 0; + r = write_blockwise(devfd, device_block_size(device), &convHdr, hdr_size) < hdr_size ? -EIO : 0; if (r) - log_err(ctx, _("Error during update of LUKS header on device %s.\n"), device); + log_err(ctx, _("Error during update of LUKS header on device %s.\n"), device_path(device)); close(devfd); /* Re-read header from disk to be sure that in-memory and on-disk data are the same. */ if (!r) { - r = LUKS_read_phdr(device, hdr, 1, 0, ctx); + r = LUKS_read_phdr(hdr, 1, 0, ctx); if (r) - log_err(ctx, _("Error re-reading LUKS header after update on device %s.\n"), device); + log_err(ctx, _("Error re-reading LUKS header after update on device %s.\n"), + device_path(device)); } return r; @@ -603,7 +603,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, unsigned int alignOffset, uint32_t iteration_time_ms, uint64_t *PBKDF2_per_sec, - const char *metadata_device, + int detached_metadata_device, struct crypt_device *ctx) { unsigned int i=0; @@ -614,7 +614,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, char luksMagic[] = LUKS_MAGIC; /* For separate metadata device allow zero alignment */ - if (alignPayload == 0 && !metadata_device) + if (alignPayload == 0 && !detached_metadata_device) alignPayload = DEFAULT_DISK_ALIGNMENT / SECTOR_SIZE; if (PBKDF2_HMAC_ready(hashSpec) < 0) { @@ -679,7 +679,7 @@ int LUKS_generate_phdr(struct luks_phdr *header, LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE); } - if (metadata_device) { + if (detached_metadata_device) { /* for separate metadata device use alignPayload directly */ header->payloadOffset = alignPayload; } else { @@ -697,7 +697,6 @@ int LUKS_generate_phdr(struct luks_phdr *header, } int LUKS_hdr_uuid_set( - const char *device, struct luks_phdr *hdr, const char *uuid, struct crypt_device *ctx) @@ -713,10 +712,10 @@ int LUKS_hdr_uuid_set( uuid_unparse(partitionUuid, hdr->uuid); - return LUKS_write_phdr(device, hdr, ctx); + return LUKS_write_phdr(hdr, ctx); } -int LUKS_set_key(const char *device, unsigned int keyIndex, +int LUKS_set_key(unsigned int keyIndex, const char *password, size_t passwordLen, struct luks_phdr *hdr, struct volume_key *vk, uint32_t iteration_time_ms, @@ -791,14 +790,13 @@ int LUKS_set_key(const char *device, unsigned int keyIndex, if (r < 0) goto out; - log_dbg("Updating key slot %d [0x%04x] area on device %s.", keyIndex, - hdr->keyblock[keyIndex].keyMaterialOffset << 9, device); + log_dbg("Updating key slot %d [0x%04x] area.", keyIndex, + hdr->keyblock[keyIndex].keyMaterialOffset << 9); /* Encryption via dm */ r = LUKS_encrypt_to_storage(AfKey, AFEKSize, hdr, derived_key, - device, hdr->keyblock[keyIndex].keyMaterialOffset, ctx); if (r < 0) { @@ -811,7 +809,7 @@ int LUKS_set_key(const char *device, unsigned int keyIndex, if (r < 0) goto out; - r = LUKS_write_phdr(device, hdr, ctx); + r = LUKS_write_phdr(hdr, ctx); if (r < 0) goto out; @@ -841,8 +839,7 @@ int LUKS_verify_volume_key(const struct luks_phdr *hdr, } /* Try to open a particular key slot */ -static int LUKS_open_key(const char *device, - unsigned int keyIndex, +static int LUKS_open_key(unsigned int keyIndex, const char *password, size_t passwordLen, struct luks_phdr *hdr, @@ -883,7 +880,6 @@ static int LUKS_open_key(const char *device, AFEKSize, hdr, derived_key, - device, hdr->keyblock[keyIndex].keyMaterialOffset, ctx); if (r < 0) { @@ -904,8 +900,7 @@ out: return r; } -int LUKS_open_key_with_hdr(const char *device, - int keyIndex, +int LUKS_open_key_with_hdr(int keyIndex, const char *password, size_t passwordLen, struct luks_phdr *hdr, @@ -918,12 +913,12 @@ int LUKS_open_key_with_hdr(const char *device, *vk = crypt_alloc_volume_key(hdr->keyBytes, NULL); if (keyIndex >= 0) { - r = LUKS_open_key(device, keyIndex, password, passwordLen, hdr, *vk, ctx); + r = LUKS_open_key(keyIndex, password, passwordLen, hdr, *vk, ctx); return (r < 0) ? r : keyIndex; } for(i = 0; i < LUKS_NUMKEYS; i++) { - r = LUKS_open_key(device, i, password, passwordLen, hdr, *vk, ctx); + r = LUKS_open_key(i, password, passwordLen, hdr, *vk, ctx); if(r == 0) return i; @@ -937,15 +932,15 @@ int LUKS_open_key_with_hdr(const char *device, return -EPERM; } -int LUKS_del_key(const char *device, - unsigned int keyIndex, +int LUKS_del_key(unsigned int keyIndex, struct luks_phdr *hdr, struct crypt_device *ctx) { + struct device *device = crypt_metadata_device(ctx); unsigned int startOffset, endOffset, stripesLen; int r; - r = LUKS_read_phdr(device, hdr, 1, 0, ctx); + r = LUKS_read_phdr(hdr, 1, 0, ctx); if (r) return r; @@ -965,7 +960,7 @@ int LUKS_del_key(const char *device, (endOffset - startOffset) * SECTOR_SIZE, CRYPT_WIPE_DISK, 0); if (r) { - log_err(ctx, _("Cannot wipe device %s.\n"), device); + log_err(ctx, _("Cannot wipe device %s.\n"), device_path(device)); return r; } @@ -973,7 +968,7 @@ int LUKS_del_key(const char *device, memset(&hdr->keyblock[keyIndex].passwordSalt, 0, LUKS_SALTSIZE); hdr->keyblock[keyIndex].passwordIterations = 0; - r = LUKS_write_phdr(device, hdr, ctx); + r = LUKS_write_phdr(hdr, ctx); return r; } @@ -1048,7 +1043,7 @@ int LUKS1_activate(struct crypt_device *cd, .uuid = crypt_get_uuid(cd), .flags = flags, .size = 0, - .data_device = crypt_get_device_name(cd), + .data_device = crypt_data_device(cd), .u.crypt = { .cipher = NULL, .vk = vk, @@ -1062,9 +1057,8 @@ int LUKS1_activate(struct crypt_device *cd, else device_check = DEV_EXCL; - r = device_check_and_adjust(cd, dmd.data_device, device_check, - &dmd.size, &dmd.u.crypt.offset, - &dmd.flags); + r = device_block_adjust(cd, dmd.data_device, device_check, + dmd.u.crypt.offset, &dmd.size, &dmd.flags); if (r) return r; diff --git a/lib/luks1/luks.h b/lib/luks1/luks.h index 2472d17d..346c82e7 100644 --- a/lib/luks1/luks.h +++ b/lib/luks1/luks.h @@ -63,6 +63,7 @@ converted */ struct volume_key; +struct device_backend; struct luks_phdr { char magic[LUKS_MAGIC_L]; @@ -108,11 +109,10 @@ int LUKS_generate_phdr( unsigned int alignOffset, uint32_t iteration_time_ms, uint64_t *PBKDF2_per_sec, - const char *metadata_device, + int detached_metadata_device, struct crypt_device *ctx); int LUKS_read_phdr( - const char *device, struct luks_phdr *hdr, int require_luks_device, int repair, @@ -120,36 +120,30 @@ int LUKS_read_phdr( int LUKS_read_phdr_backup( const char *backup_file, - const char *device, struct luks_phdr *hdr, int require_luks_device, struct crypt_device *ctx); int LUKS_hdr_uuid_set( - const char *device, struct luks_phdr *hdr, const char *uuid, struct crypt_device *ctx); int LUKS_hdr_backup( const char *backup_file, - const char *device, struct luks_phdr *hdr, struct crypt_device *ctx); int LUKS_hdr_restore( const char *backup_file, - const char *device, struct luks_phdr *hdr, struct crypt_device *ctx); int LUKS_write_phdr( - const char *device, struct luks_phdr *hdr, struct crypt_device *ctx); int LUKS_set_key( - const char *device, unsigned int keyIndex, const char *password, size_t passwordLen, @@ -160,7 +154,6 @@ int LUKS_set_key( struct crypt_device *ctx); int LUKS_open_key_with_hdr( - const char *device, int keyIndex, const char *password, size_t passwordLen, @@ -169,7 +162,6 @@ int LUKS_open_key_with_hdr( struct crypt_device *ctx); int LUKS_del_key( - const char *device, unsigned int keyIndex, struct luks_phdr *hdr, struct crypt_device *ctx); @@ -183,7 +175,6 @@ int LUKS_encrypt_to_storage( char *src, size_t srcLength, struct luks_phdr *hdr, struct volume_key *vk, - const char *device, unsigned int sector, struct crypt_device *ctx); @@ -191,7 +182,6 @@ int LUKS_decrypt_from_storage( char *dst, size_t dstLength, struct luks_phdr *hdr, struct volume_key *vk, - const char *device, unsigned int sector, struct crypt_device *ctx); diff --git a/lib/setup.c b/lib/setup.c index 08165e6f..b9e43afb 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -35,12 +35,8 @@ struct crypt_device { char *type; - char *device; - char *metadata_device; - - /* loopback automatic detach helpers */ - int loop_device_fd; - int loop_metadata_device_fd; + struct device *device; + struct device *metadata_device; struct volume_key *volume_key; uint64_t timeout; @@ -156,11 +152,22 @@ void logger(struct crypt_device *cd, int level, const char *file, free(target); } -static const char *mdata_device(struct crypt_device *cd) +static const char *mdata_device_path(struct crypt_device *cd) +{ + return device_path(cd->metadata_device ?: cd->device); +} + +/* internal only */ +struct device *crypt_metadata_device(struct crypt_device *cd) { return cd->metadata_device ?: cd->device; } +struct device *crypt_data_device(struct crypt_device *cd) +{ + return cd->device; +} + static int init_crypto(struct crypt_device *ctx) { int r; @@ -307,7 +314,7 @@ int PLAIN_activate(struct crypt_device *cd, .uuid = crypt_get_uuid(cd), .size = size, .flags = flags, - .data_device = crypt_get_device_name(cd), + .data_device = crypt_data_device(cd), .u.crypt = { .cipher = NULL, .vk = vk, @@ -321,8 +328,8 @@ int PLAIN_activate(struct crypt_device *cd, else device_check = DEV_EXCL; - r = device_check_and_adjust(cd, dmd.data_device, device_check, - &dmd.size, &dmd.u.crypt.offset, &dmd.flags); + r = device_block_adjust(cd, dmd.data_device, device_check, + dmd.u.crypt.offset, &dmd.size, &dmd.flags); if (r) return r; @@ -417,7 +424,7 @@ static int volume_key_by_terminal_passphrase(struct crypt_device *cd, int keyslo if(r < 0) goto out; - r = LUKS_open_key_with_hdr(mdata_device(cd), keyslot, passphrase_read, + r = LUKS_open_key_with_hdr(keyslot, passphrase_read, passphrase_size_read, &cd->hdr, vk, cd); if (r == -EPERM) eperm = 1; @@ -504,44 +511,6 @@ const char *crypt_get_dir(void) return dm_get_dir(); } -static int set_device_or_loop(const char *device_org, char **device, int *loop_fd) -{ - int r, readonly = 0; - - if (!device_org) - return 0; - - r = device_ready(NULL, device_org, O_RDONLY); - if (r == -ENOTBLK) { - *device = crypt_loop_get_device(); - log_dbg("Not a block device, %s%s.", *device ? - "using free loop device " : "no free loop device found", - *device ?: ""); - if (!*device) { - log_err(NULL, _("Cannot find a free loopback device.\n")); - return -ENOSYS; - } - - /* Keep the loop open, dettached on last close. */ - *loop_fd = crypt_loop_attach(*device, device_org, 0, 1, &readonly); - if (*loop_fd == -1) { - log_err(NULL, _("Attaching loopback device failed " - "(loop device with autoclear flag is required).\n")); - return -EINVAL; - } - - r = device_ready(NULL, *device, O_RDONLY); - } - - if (r < 0) - return -ENOTBLK; - - if (!*device && device_org && !(*device = strdup(device_org))) - return -ENOMEM; - - return 0; -} - int crypt_init(struct crypt_device **cd, const char *device) { struct crypt_device *h = NULL; @@ -556,10 +525,8 @@ int crypt_init(struct crypt_device **cd, const char *device) return -ENOMEM; memset(h, 0, sizeof(*h)); - h->loop_device_fd = -1; - h->loop_metadata_device_fd = -1; - r = set_device_or_loop(device, &h->device, &h->loop_device_fd); + r = device_alloc(&h->device, device); if (r < 0) goto bad; @@ -575,12 +542,7 @@ int crypt_init(struct crypt_device **cd, const char *device) *cd = h; return 0; bad: - - if (h) { - if (h->loop_device_fd != -1) - close(h->loop_device_fd); - free(h->device); - } + device_free(h->device); free(h); return r; } @@ -593,13 +555,13 @@ static int crypt_check_data_device_size(struct crypt_device *cd) /* Check data device size, require at least one sector */ size_min = crypt_get_data_offset(cd) << SECTOR_SHIFT ?: SECTOR_SIZE; - r = device_size(crypt_get_device_name(cd), &size); + r = device_size(cd->device, &size); if (r < 0) return r; if (size < size_min) { log_err(cd, _("Header detected but device %s is too small.\n"), - crypt_get_device_name(cd)); + device_path(cd->device)); return -EINVAL; } @@ -608,8 +570,8 @@ static int crypt_check_data_device_size(struct crypt_device *cd) int crypt_set_data_device(struct crypt_device *cd, const char *device) { - char *data_device = NULL; - int r, loop_fd = -1; + struct device *dev = NULL; + int r; log_dbg("Setting ciphertext data device to %s.", device ?: "(none)"); @@ -622,21 +584,16 @@ int crypt_set_data_device(struct crypt_device *cd, const char *device) if (!cd->device || !device) return -EINVAL; - r = set_device_or_loop(device, &data_device, &loop_fd); + r = device_alloc(&dev, device); if (r < 0) return r; if (!cd->metadata_device) { cd->metadata_device = cd->device; - cd->loop_metadata_device_fd = cd->loop_device_fd; - } else { - free(cd->device); - if (cd->loop_device_fd != -1) - close(cd->loop_device_fd); - } + } else + device_free(cd->device); - cd->device = data_device; - cd->loop_device_fd = loop_fd; + cd->device = dev; return crypt_check_data_device_size(cd); } @@ -650,7 +607,7 @@ static int _crypt_load_luks1(struct crypt_device *cd, int require_header, int re if (r < 0) return r; - r = LUKS_read_phdr(mdata_device(cd), &hdr, require_header, repair, cd); + r = LUKS_read_phdr(&hdr, require_header, repair, cd); if (r < 0) return r; @@ -677,8 +634,7 @@ static int _crypt_load_verity(struct crypt_device *cd, struct crypt_params_verit if (params) sb_offset = params->hash_area_offset; - r = VERITY_read_sb(cd, mdata_device(cd), sb_offset, - &cd->verity_uuid, &cd->verity_hdr); + r = VERITY_read_sb(cd, sb_offset, &cd->verity_uuid, &cd->verity_hdr); if (r < 0) return r; @@ -740,7 +696,7 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name) cd->loopaes_key_size = dmd.u.crypt.vk->keylength / key_nums; } } else if (isLUKS(cd->type)) { - if (mdata_device(cd)) { + if (crypt_metadata_device(cd)) { r = _crypt_load_luks1(cd, 0, 0); if (r < 0) { log_dbg("LUKS device header does not match active device."); @@ -763,8 +719,8 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name) } out: crypt_free_volume_key(dmd.u.crypt.vk); + device_free(dmd.data_device); free(CONST_CAST(void*)dmd.u.crypt.cipher); - free(CONST_CAST(void*)dmd.data_device); free(CONST_CAST(void*)dmd.uuid); return r; } @@ -801,12 +757,10 @@ static int _init_by_name_verity(struct crypt_device *cd, const char *name) cd->verity_hdr.flags = params.flags; cd->verity_hdr.salt_size = params.salt_size; cd->verity_hdr.salt = params.salt; - if (!(cd->metadata_device = strdup(dmd.u.verity.hash_device))) - r = -ENOMEM; + cd->metadata_device = dmd.u.verity.hash_device; } out: - free(CONST_CAST(void*)dmd.u.verity.hash_device); - free(CONST_CAST(void*)dmd.data_device); + device_free(dmd.data_device); free(CONST_CAST(void*)dmd.uuid); return r; } @@ -839,7 +793,7 @@ int crypt_init_by_name_and_header(struct crypt_device **cd, if (header_device) { r = crypt_init(cd, header_device); } else { - r = crypt_init(cd, dmd.data_device); + r = crypt_init(cd, device_path(dmd.data_device)); /* Underlying device disappeared but mapping still active */ if (!dmd.data_device || r == -ENOTBLK) @@ -848,7 +802,7 @@ int crypt_init_by_name_and_header(struct crypt_device **cd, /* Underlying device is not readable but crypt mapping exists */ if (r == -ENOTBLK) { - free(CONST_CAST(void*)dmd.data_device); + device_free(dmd.data_device); dmd.data_device = NULL; r = crypt_init(cd, NULL); } @@ -872,7 +826,7 @@ int crypt_init_by_name_and_header(struct crypt_device **cd, log_dbg("Active device has no UUID set, some parameters are not set."); if (header_device) { - r = crypt_set_data_device(*cd, dmd.data_device); + r = crypt_set_data_device(*cd, device_path(dmd.data_device)); if (r < 0) goto out; } @@ -888,7 +842,7 @@ out: crypt_free(*cd); *cd = NULL; } - free(CONST_CAST(void*)dmd.data_device); + device_free(dmd.data_device); free(CONST_CAST(void*)dmd.uuid); return r; } @@ -954,7 +908,7 @@ static int _crypt_format_luks1(struct crypt_device *cd, unsigned long required_alignment = DEFAULT_DISK_ALIGNMENT; unsigned long alignment_offset = 0; - if (!mdata_device(cd)) { + if (!crypt_metadata_device(cd)) { log_err(cd, _("Can't format LUKS without device.\n")); return -EINVAL; } @@ -973,13 +927,15 @@ static int _crypt_format_luks1(struct crypt_device *cd, if (params && params->data_device) { cd->metadata_device = cd->device; - if (!(cd->device = strdup(params->data_device))) + cd->device = NULL; + if (device_alloc(&cd->device, params->data_device) < 0) return -ENOMEM; required_alignment = params->data_alignment * SECTOR_SIZE; } else if (params && params->data_alignment) { required_alignment = params->data_alignment * SECTOR_SIZE; } else - get_topology_alignment(cd->device, &required_alignment, + device_topology_alignment(cd->device, + &required_alignment, &alignment_offset, DEFAULT_DISK_ALIGNMENT); r = LUKS_generate_phdr(&cd->hdr, cd->volume_key, cipher, cipher_mode, @@ -988,24 +944,24 @@ static int _crypt_format_luks1(struct crypt_device *cd, required_alignment / SECTOR_SIZE, alignment_offset / SECTOR_SIZE, cd->iteration_time, &cd->PBKDF2_per_sec, - cd->metadata_device, cd); + cd->metadata_device ? 1 : 0, cd); if(r < 0) return r; /* Wipe first 8 sectors - fs magic numbers etc. */ - r = crypt_wipe(mdata_device(cd), 0, 8 * SECTOR_SIZE, CRYPT_WIPE_ZERO, 1); + r = crypt_wipe(crypt_metadata_device(cd), 0, 8 * SECTOR_SIZE, CRYPT_WIPE_ZERO, 1); if(r < 0) { if (r == -EBUSY) log_err(cd, _("Cannot format device %s which is still in use.\n"), - mdata_device(cd)); + mdata_device_path(cd)); else log_err(cd, _("Cannot wipe header on device %s.\n"), - mdata_device(cd)); + mdata_device_path(cd)); return r; } - r = LUKS_write_phdr(mdata_device(cd), &cd->hdr, cd); + r = LUKS_write_phdr(&cd->hdr, cd); return r; } @@ -1016,7 +972,7 @@ static int _crypt_format_loopaes(struct crypt_device *cd, size_t volume_key_size, struct crypt_params_loopaes *params) { - if (!mdata_device(cd)) { + if (!crypt_metadata_device(cd)) { log_err(cd, _("Can't format LOOPAES without device.\n")); return -EINVAL; } @@ -1052,7 +1008,7 @@ static int _crypt_format_verity(struct crypt_device *cd, int r = 0, hash_size; uint64_t data_device_size; - if (!mdata_device(cd)) { + if (!crypt_metadata_device(cd)) { log_err(cd, _("Can't format VERITY without device.\n")); return -EINVAL; } @@ -1123,7 +1079,7 @@ static int _crypt_format_verity(struct crypt_device *cd, return r; if (params->flags & CRYPT_VERITY_CREATE_HASH) { - r = VERITY_create(cd, &cd->verity_hdr, cd->device, mdata_device(cd), + r = VERITY_create(cd, &cd->verity_hdr, cd->verity_root_hash, cd->verity_root_hash_size); if (r) return r; @@ -1138,8 +1094,7 @@ static int _crypt_format_verity(struct crypt_device *cd, return r; } - r = VERITY_write_sb(cd, mdata_device(cd), - cd->verity_hdr.hash_area_offset, + r = VERITY_write_sb(cd, cd->verity_hdr.hash_area_offset, cd->verity_uuid, &cd->verity_hdr); } @@ -1165,7 +1120,7 @@ int crypt_format(struct crypt_device *cd, return -EINVAL; } - log_dbg("Formatting device %s as type %s.", mdata_device(cd) ?: "(none)", type); + log_dbg("Formatting device %s as type %s.", mdata_device_path(cd) ?: "(none)", type); r = init_crypto(cd); if (r < 0) @@ -1203,9 +1158,9 @@ int crypt_load(struct crypt_device *cd, int r; log_dbg("Trying to load %s crypt type from device %s.", - requested_type ?: "any", mdata_device(cd) ?: "(none)"); + requested_type ?: "any", mdata_device_path(cd) ?: "(none)"); - if (!mdata_device(cd)) + if (!crypt_metadata_device(cd)) return -EINVAL; if (!requested_type || isLUKS(requested_type)) { @@ -1234,9 +1189,9 @@ int crypt_repair(struct crypt_device *cd, int r; log_dbg("Trying to repair %s crypt type from device %s.", - requested_type ?: "any", mdata_device(cd) ?: "(none)"); + requested_type ?: "any", mdata_device_path(cd) ?: "(none)"); - if (!mdata_device(cd)) + if (!crypt_metadata_device(cd)) return -EINVAL; if (requested_type && !isLUKS(requested_type)) @@ -1282,8 +1237,8 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) goto out; } - r = device_check_and_adjust(cd, dmd.data_device, DEV_OK, &new_size, - &dmd.u.crypt.offset, &dmd.flags); + r = device_block_adjust(cd, dmd.data_device, DEV_OK, + dmd.u.crypt.offset, &new_size, &dmd.flags); if (r) goto out; @@ -1315,19 +1270,19 @@ int crypt_set_uuid(struct crypt_device *cd, const char *uuid) if (uuid && !strncmp(uuid, cd->hdr.uuid, sizeof(cd->hdr.uuid))) { log_dbg("UUID is the same as requested (%s) for device %s.", - uuid, mdata_device(cd)); + uuid, mdata_device_path(cd)); return 0; } if (uuid) - log_dbg("Requested new UUID change to %s for %s.", uuid, mdata_device(cd)); + log_dbg("Requested new UUID change to %s for %s.", uuid, mdata_device_path(cd)); else - log_dbg("Requested new UUID refresh for %s.", mdata_device(cd)); + log_dbg("Requested new UUID refresh for %s.", mdata_device_path(cd)); if (!crypt_confirm(cd, _("Do you really want to change UUID of device?"))) return -EPERM; - return LUKS_hdr_uuid_set(mdata_device(cd), &cd->hdr, uuid, cd); + return LUKS_hdr_uuid_set(&cd->hdr, uuid, cd); } int crypt_header_backup(struct crypt_device *cd, @@ -1344,9 +1299,9 @@ int crypt_header_backup(struct crypt_device *cd, return r; log_dbg("Requested header backup of device %s (%s) to " - "file %s.", mdata_device(cd), requested_type, backup_file); + "file %s.", mdata_device_path(cd), requested_type, backup_file); - return LUKS_hdr_backup(backup_file, mdata_device(cd), &cd->hdr, cd); + return LUKS_hdr_backup(backup_file, &cd->hdr, cd); } int crypt_header_restore(struct crypt_device *cd, @@ -1363,26 +1318,21 @@ int crypt_header_restore(struct crypt_device *cd, return r; log_dbg("Requested header restore to device %s (%s) from " - "file %s.", mdata_device(cd), requested_type, backup_file); + "file %s.", mdata_device_path(cd), requested_type, backup_file); - return LUKS_hdr_restore(backup_file, mdata_device(cd), &cd->hdr, cd); + return LUKS_hdr_restore(backup_file, &cd->hdr, cd); } void crypt_free(struct crypt_device *cd) { if (cd) { - log_dbg("Releasing crypt device %s context.", mdata_device(cd)); - - if (cd->loop_device_fd != -1) - close(cd->loop_device_fd); - if (cd->loop_metadata_device_fd != -1) - close(cd->loop_metadata_device_fd); + log_dbg("Releasing crypt device %s context.", mdata_device_path(cd)); dm_exit(); crypt_free_volume_key(cd->volume_key); - free(cd->device); - free(cd->metadata_device); + device_free(cd->device); + device_free(cd->metadata_device); free(cd->type); /* used in plain device only */ @@ -1477,8 +1427,8 @@ int crypt_resume_by_passphrase(struct crypt_device *cd, } if (passphrase) { - r = LUKS_open_key_with_hdr(mdata_device(cd), keyslot, passphrase, - passphrase_size, &cd->hdr, &vk, cd); + r = LUKS_open_key_with_hdr(keyslot, passphrase, passphrase_size, + &cd->hdr, &vk, cd); } else r = volume_key_by_terminal_passphrase(cd, keyslot, &vk); @@ -1534,7 +1484,7 @@ int crypt_resume_by_keyfile_offset(struct crypt_device *cd, if (r < 0) goto out; - r = LUKS_open_key_with_hdr(mdata_device(cd), keyslot, passphrase_read, + r = LUKS_open_key_with_hdr(keyslot, passphrase_read, passphrase_size_read, &cd->hdr, &vk, cd); if (r < 0) goto out; @@ -1596,7 +1546,7 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, } } else if (passphrase) { /* Passphrase provided, use it to unlock existing keyslot */ - r = LUKS_open_key_with_hdr(mdata_device(cd), CRYPT_ANY_SLOT, passphrase, + r = LUKS_open_key_with_hdr(CRYPT_ANY_SLOT, passphrase, passphrase_size, &cd->hdr, &vk, cd); } else { /* Passphrase not provided, ask first and use it to unlock existing keyslot */ @@ -1605,7 +1555,7 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, if (r < 0) goto out; - r = LUKS_open_key_with_hdr(mdata_device(cd), CRYPT_ANY_SLOT, password, + r = LUKS_open_key_with_hdr(CRYPT_ANY_SLOT, password, passwordLen, &cd->hdr, &vk, cd); crypt_safe_free(password); } @@ -1623,7 +1573,7 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, goto out; } - r = LUKS_set_key(mdata_device(cd), keyslot, new_password, new_passwordLen, + r = LUKS_set_key(keyslot, new_password, new_passwordLen, &cd->hdr, vk, cd->iteration_time, &cd->PBKDF2_per_sec, cd); if(r < 0) goto out; @@ -1682,7 +1632,7 @@ int crypt_keyslot_add_by_keyfile_offset(struct crypt_device *cd, if (r < 0) goto out; - r = LUKS_open_key_with_hdr(mdata_device(cd), CRYPT_ANY_SLOT, password, passwordLen, + r = LUKS_open_key_with_hdr(CRYPT_ANY_SLOT, password, passwordLen, &cd->hdr, &vk, cd); } @@ -1699,7 +1649,7 @@ int crypt_keyslot_add_by_keyfile_offset(struct crypt_device *cd, if (r < 0) goto out; - r = LUKS_set_key(mdata_device(cd), keyslot, new_password, new_passwordLen, + r = LUKS_set_key(keyslot, new_password, new_passwordLen, &cd->hdr, vk, cd->iteration_time, &cd->PBKDF2_per_sec, cd); out: crypt_safe_free(password); @@ -1765,7 +1715,7 @@ int crypt_keyslot_add_by_volume_key(struct crypt_device *cd, passphrase_size = new_passwordLen; } - r = LUKS_set_key(mdata_device(cd), keyslot, passphrase, passphrase_size, + r = LUKS_set_key(keyslot, passphrase, passphrase_size, &cd->hdr, vk, cd->iteration_time, &cd->PBKDF2_per_sec, cd); out: crypt_safe_free(new_password); @@ -1795,7 +1745,7 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot) return -EINVAL; } - return LUKS_del_key(mdata_device(cd), keyslot, &cd->hdr, cd); + return LUKS_del_key(keyslot, &cd->hdr, cd); } // activation/deactivation of device mapping @@ -1851,7 +1801,7 @@ int crypt_activate_by_passphrase(struct crypt_device *cd, } else if (isLUKS(cd->type)) { /* provided passphrase, do not retry */ if (passphrase) { - r = LUKS_open_key_with_hdr(mdata_device(cd), keyslot, passphrase, + r = LUKS_open_key_with_hdr(keyslot, passphrase, passphrase_size, &cd->hdr, &vk, cd); } else r = volume_key_by_terminal_passphrase(cd, keyslot, &vk); @@ -1923,7 +1873,7 @@ int crypt_activate_by_keyfile_offset(struct crypt_device *cd, &passphrase_size_read, keyfile, keyfile_offset, keyfile_size); if (r < 0) goto out; - r = LUKS_open_key_with_hdr(mdata_device(cd), keyslot, passphrase_read, + r = LUKS_open_key_with_hdr(keyslot, passphrase_read, passphrase_size_read, &cd->hdr, &vk, cd); if (r < 0) goto out; @@ -2033,8 +1983,7 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, return -EINVAL; } - r = VERITY_activate(cd, name, mdata_device(cd), - volume_key, volume_key_size, + r = VERITY_activate(cd, name, volume_key, volume_key_size, &cd->verity_hdr, CRYPT_ACTIVATE_READONLY); if (r == -EPERM) { @@ -2115,7 +2064,7 @@ int crypt_volume_key_get(struct crypt_device *cd, if (r < 0) log_err(cd, _("Cannot retrieve volume key for plain device.\n")); } else if (isLUKS(cd->type)) { - r = LUKS_open_key_with_hdr(mdata_device(cd), keyslot, passphrase, + r = LUKS_open_key_with_hdr(keyslot, passphrase, passphrase_size, &cd->hdr, &vk, cd); } else @@ -2243,7 +2192,7 @@ static int _luks_dump(struct crypt_device *cd) { int i; - log_std(cd, "LUKS header information for %s\n\n", mdata_device(cd)); + log_std(cd, "LUKS header information for %s\n\n", mdata_device_path(cd)); log_std(cd, "Version: \t%d\n", cd->hdr.version); log_std(cd, "Cipher name: \t%s\n", cd->hdr.cipherName); log_std(cd, "Cipher mode: \t%s\n", cd->hdr.cipherMode); @@ -2286,7 +2235,7 @@ static int _luks_dump(struct crypt_device *cd) static int _verity_dump(struct crypt_device *cd) { - log_std(cd, "VERITY header information for %s\n", mdata_device(cd)); + log_std(cd, "VERITY header information for %s\n", mdata_device_path(cd)); log_std(cd, "UUID: \t%s\n", cd->verity_uuid ?: ""); log_std(cd, "Hash type: \t%u\n", cd->verity_hdr.hash_type); log_std(cd, "Data blocks: \t%" PRIu64 "\n", cd->verity_hdr.data_size); @@ -2365,7 +2314,12 @@ const char *crypt_get_uuid(struct crypt_device *cd) const char *crypt_get_device_name(struct crypt_device *cd) { - return cd->device; + const char *path = device_block_path(cd->device); + + if (!path) + path = device_path(cd->device); + + return path; } int crypt_get_volume_key_size(struct crypt_device *cd) @@ -2442,8 +2396,8 @@ int crypt_get_verity_info(struct crypt_device *cd, if (!isVERITY(cd->type) || !vp) return -EINVAL; - vp->data_device = cd->device; - vp->hash_device = mdata_device(cd); + vp->data_device = device_path(cd->device); + vp->hash_device = mdata_device_path(cd); vp->hash_name = cd->verity_hdr.hash_name; vp->salt = cd->verity_hdr.salt; vp->salt_size = cd->verity_hdr.salt_size; diff --git a/lib/utils.c b/lib/utils.c index 2639341f..bd80348b 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -22,20 +22,10 @@ #include #include #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include #include -#include "libcryptsetup.h" #include "internal.h" unsigned crypt_getpagesize(void) @@ -74,52 +64,15 @@ static void *aligned_malloc(void **base, int size, int alignment) #endif } -int device_read_ahead(const char *dev, uint32_t *read_ahead) -{ - int fd, r = 0; - long read_ahead_long; - - if ((fd = open(dev, O_RDONLY)) < 0) - return 0; - - r = ioctl(fd, BLKRAGET, &read_ahead_long) ? 0 : 1; - close(fd); - - if (r) - *read_ahead = (uint32_t) read_ahead_long; - - return r; -} - -static int sector_size(int fd) -{ - int bsize; - if (ioctl(fd,BLKSSZGET, &bsize) < 0) - return -EINVAL; - else - return bsize; -} - -int sector_size_for_device(const char *device) -{ - int fd = open(device, O_RDONLY); - int r; - if(fd < 0) - return -EINVAL; - r = sector_size(fd); - close(fd); - return r; -} - -ssize_t write_blockwise(int fd, void *orig_buf, size_t count) +ssize_t write_blockwise(int fd, int bsize, void *orig_buf, size_t count) { void *hangover_buf, *hangover_buf_base = NULL; void *buf, *buf_base = NULL; - int r, hangover, solid, bsize, alignment; + int r, hangover, solid, alignment; ssize_t ret = -1; - if ((bsize = sector_size(fd)) < 0) - return bsize; + if (fd == -1 || !orig_buf || bsize <= 0) + return -1; hangover = count % bsize; solid = count - hangover; @@ -163,14 +116,14 @@ out: return ret; } -ssize_t read_blockwise(int fd, void *orig_buf, size_t count) { +ssize_t read_blockwise(int fd, int bsize, void *orig_buf, size_t count) { void *hangover_buf, *hangover_buf_base = NULL; void *buf, *buf_base = NULL; - int r, hangover, solid, bsize, alignment; + int r, hangover, solid, alignment; ssize_t ret = -1; - if ((bsize = sector_size(fd)) < 0) - return bsize; + if (fd == -1 || !orig_buf || bsize <= 0) + return -1; hangover = count % bsize; solid = count - hangover; @@ -213,15 +166,15 @@ out: * is implicitly included in the read/write offset, which can not be set to non-aligned * boundaries. Hence, we combine llseek with write. */ -ssize_t write_lseek_blockwise(int fd, char *buf, size_t count, off_t offset) { +ssize_t write_lseek_blockwise(int fd, int bsize, char *buf, size_t count, off_t offset) { char *frontPadBuf; void *frontPadBuf_base = NULL; - int r, bsize, frontHang; + int r, frontHang; size_t innerCount = 0; ssize_t ret = -1; - if ((bsize = sector_size(fd)) < 0) - return bsize; + if (fd == -1 || !buf || bsize <= 0) + return -1; frontHang = offset % bsize; @@ -255,7 +208,7 @@ ssize_t write_lseek_blockwise(int fd, char *buf, size_t count, off_t offset) { count -= innerCount; } - ret = count ? write_blockwise(fd, buf, count) : 0; + ret = count ? write_blockwise(fd, bsize, buf, count) : 0; if (ret >= 0) ret += innerCount; out: @@ -264,171 +217,6 @@ out: return ret; } -int device_ready(struct crypt_device *cd, const char *device, int mode) -{ - int devfd, r = 0; - ssize_t s; - struct stat st; - char buf[512]; - - if(stat(device, &st) < 0) { - log_err(cd, _("Device %s doesn't exist or access denied.\n"), device); - return -EINVAL; - } - - if (!S_ISBLK(st.st_mode)) - return -ENOTBLK; - - log_dbg("Trying to open and read device %s.", device); - devfd = open(device, mode | O_DIRECT | O_SYNC); - if(devfd < 0) { - log_err(cd, _("Cannot open device %s for %s%s access.\n"), device, - (mode & O_EXCL) ? _("exclusive ") : "", - (mode & O_RDWR) ? _("writable") : _("read-only")); - return -EINVAL; - } - - /* Try to read first sector */ - s = read_blockwise(devfd, buf, sizeof(buf)); - if (s < 0 || s != sizeof(buf)) { - log_verbose(cd, _("Cannot read device %s.\n"), device); - r = -EIO; - } - - memset(buf, 0, sizeof(buf)); - close(devfd); - - return r; -} - -int device_size(const char *device, uint64_t *size) -{ - int devfd, r = 0; - - devfd = open(device, O_RDONLY); - if(devfd == -1) - return -EINVAL; - - if (ioctl(devfd, BLKGETSIZE64, size) < 0) - r = -EINVAL; - - close(devfd); - return r; -} - -static int get_device_infos(const char *device, enum devcheck device_check, - int *readonly, uint64_t *size) -{ - struct stat st; - unsigned long size_small; - int fd, r = -1; - int flags = 0; - - *readonly = 0; - *size = 0; - - if (stat(device, &st) < 0) - return -EINVAL; - - /* never wipe header on mounted device */ - if (device_check == DEV_EXCL && S_ISBLK(st.st_mode)) - flags |= O_EXCL; - - /* Try to open read-write to check whether it is a read-only device */ - fd = open(device, O_RDWR | flags); - if (fd == -1 && errno == EROFS) { - *readonly = 1; - fd = open(device, O_RDONLY | flags); - } - - if (fd == -1 && device_check == DEV_EXCL && errno == EBUSY) - return -EBUSY; - - if (fd == -1) - return -EINVAL; - - /* If the device can be opened read-write, i.e. readonly is still 0, then - * check whether BKROGET says that it is read-only. E.g. read-only loop - * devices may be openend read-write but are read-only according to BLKROGET - */ - if (*readonly == 0 && (r = ioctl(fd, BLKROGET, readonly)) < 0) - goto out; - - if (ioctl(fd, BLKGETSIZE64, size) >= 0) { - *size >>= SECTOR_SHIFT; - r = 0; - goto out; - } - - if (ioctl(fd, BLKGETSIZE, &size_small) >= 0) { - *size = (uint64_t)size_small; - r = 0; - goto out; - } - - r = -EINVAL; -out: - close(fd); - return r; -} - -int device_check_and_adjust(struct crypt_device *cd, - const char *device, - enum devcheck device_check, - uint64_t *size, - uint64_t *offset, - uint32_t *flags) -{ - int r, real_readonly; - uint64_t real_size; - - if (!device) - return -ENOTBLK; - - r = get_device_infos(device, device_check, &real_readonly, &real_size); - if (r < 0) { - if (r == -EBUSY) - log_err(cd, _("Cannot use device %s which is in use " - "(already mapped or mounted).\n"), - device); - else - log_err(cd, _("Cannot get info about device %s.\n"), - device); - return r; - } - - if (*offset >= real_size) { - log_err(cd, _("Requested offset is beyond real size of device %s.\n"), - device); - return -EINVAL; - } - - if (!*size) { - *size = real_size; - if (!*size) { - log_err(cd, _("Device %s has zero size.\n"), device); - return -ENOTBLK; - } - *size -= *offset; - } - - /* in case of size is set by parameter */ - if ((real_size - *offset) < *size) { - log_dbg("Device %s: offset = %" PRIu64 " requested size = %" PRIu64 - ", backing device size = %" PRIu64, - device, *offset, *size, real_size); - log_err(cd, _("Device %s is too small.\n"), device); - return -EINVAL; - } - - if (real_readonly) - *flags |= CRYPT_ACTIVATE_READONLY; - - log_dbg("Calculated device size is %" PRIu64 " sectors (%s), offset %" PRIu64 ".", - *size, real_readonly ? "RO" : "RW", *offset); - return 0; -} - /* MEMLOCK */ #define DEFAULT_PROCESS_PRIORITY -18 @@ -467,60 +255,3 @@ 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) -{ - int dev_alignment_offset = 0; - unsigned int min_io_size = 0, opt_io_size = 0; - unsigned long temp_alignment = 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); - goto out; - } - - /* 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 || dev_alignment_offset < 0) - dev_alignment_offset = 0; - *alignment_offset = (unsigned long)dev_alignment_offset; - - temp_alignment = (unsigned long)min_io_size; - - if (temp_alignment < (unsigned long)opt_io_size) - temp_alignment = (unsigned long)opt_io_size; - - /* If calculated alignment is multiple of default, keep default */ - if (temp_alignment && (default_alignment % temp_alignment)) - *required_alignment = temp_alignment; - - log_dbg("Topology: IO (%u/%u), offset = %lu; Required alignment is %lu bytes.", - min_io_size, opt_io_size, *alignment_offset, *required_alignment); -out: - (void)close(fd); -} diff --git a/lib/utils_device.c b/lib/utils_device.c new file mode 100644 index 00000000..5618eb7d --- /dev/null +++ b/lib/utils_device.c @@ -0,0 +1,420 @@ +/* + * device backend utilities + * + * Copyright (C) 2004, Christophe Saout + * Copyright (C) 2004-2007, Clemens Fruhwirth + * Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +struct device { + char *path; + + char *file_path; + int loop_fd; + + int init_done:1; +}; + +static int device_ready(const char *device) +{ + int devfd, r = 0; + struct stat st; + + if(stat(device, &st) < 0) { + log_err(NULL, _("Device %s doesn't exist or access denied.\n"), device); + return -EINVAL; + } + + if (!S_ISBLK(st.st_mode)) + return S_ISREG(st.st_mode) ? -ENOTBLK : -EINVAL; + + log_dbg("Trying to open and read device %s.", device); + devfd = open(device, O_RDONLY | O_DIRECT | O_SYNC); + if(devfd < 0) { + log_err(NULL, _("Cannot open device %s.\n"), device); + return -EINVAL; + } + + close(devfd); + return r; +} + +int device_alloc(struct device **device, const char *path) +{ + struct device *dev; + int r; + + if (!path) { + *device = NULL; + return 0; + } + + dev = malloc(sizeof(struct device)); + if (!dev) + return -ENOMEM; + + memset(dev, 0, sizeof(struct device)); + dev->loop_fd = -1; + + r = device_ready(path); + if (!r) { + dev->init_done = 1; + } else if (r == -ENOTBLK) { + /* alloc loop later */ + } else if (r < 0) { + free(dev); + return -ENOTBLK; + } + + dev->path = strdup(path); + if (!dev->path) { + free(dev); + return -ENOMEM; + } + + *device = dev; + return 0; +} + +void device_free(struct device *device) +{ + if (!device) + return; + + if (device->loop_fd != -1) { + log_dbg("Closed loop %s (%s).", device->path, device->file_path); + close(device->loop_fd); + } + + free(device->file_path); + free(device->path); + free(device); +} + +/* Get block device path */ +const char *device_block_path(const struct device *device) +{ + if (!device || !device->init_done) + return NULL; + + return device->path; +} + +/* Get path to device / file */ +const char *device_path(const struct device *device) +{ + if (!device) + return NULL; + + if (device->file_path) + return device->file_path; + + return device->path; +} + +/* 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 device_topology_alignment(struct device *device, + unsigned long *required_alignment, /* bytes */ + unsigned long *alignment_offset, /* bytes */ + unsigned long default_alignment) +{ + int dev_alignment_offset = 0; + unsigned int min_io_size = 0, opt_io_size = 0; + unsigned long temp_alignment = 0; + int fd; + + *required_alignment = default_alignment; + *alignment_offset = 0; + + if (!device || !device->path) //FIXME + return; + + fd = open(device->path, 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->path, default_alignment); + goto out; + } + + /* 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 || dev_alignment_offset < 0) + dev_alignment_offset = 0; + *alignment_offset = (unsigned long)dev_alignment_offset; + + temp_alignment = (unsigned long)min_io_size; + + if (temp_alignment < (unsigned long)opt_io_size) + temp_alignment = (unsigned long)opt_io_size; + + /* If calculated alignment is multiple of default, keep default */ + if (temp_alignment && (default_alignment % temp_alignment)) + *required_alignment = temp_alignment; + + log_dbg("Topology: IO (%u/%u), offset = %lu; Required alignment is %lu bytes.", + min_io_size, opt_io_size, *alignment_offset, *required_alignment); +out: + (void)close(fd); +} + +int device_block_size(struct device *device) +{ + struct stat st; + int fd, bsize = 0, r = -EINVAL; + + if (!device) + return 0; + + fd = open(device->path, O_RDONLY); + if(fd < 0) + return -EINVAL; + + if (fstat(fd, &st) < 0) + goto out; + + if (S_ISREG(st.st_mode)) { + r = (int)crypt_getpagesize(); + goto out; + } + + if (ioctl(fd, BLKSSZGET, &bsize) >= 0) + r = bsize; +out: + close(fd); + return r; +} + +int device_read_ahead(struct device *device, uint32_t *read_ahead) +{ + int fd, r = 0; + long read_ahead_long; + + if (!device) + return 0; + + if ((fd = open(device->path, O_RDONLY)) < 0) + return 0; + + r = ioctl(fd, BLKRAGET, &read_ahead_long) ? 0 : 1; + close(fd); + + if (r) + *read_ahead = (uint32_t) read_ahead_long; + + return r; +} + +/* Get data size in bytes */ +int device_size(struct device *device, uint64_t *size) +{ + struct stat st; + int devfd, r = -EINVAL; + + devfd = open(device->path, O_RDONLY); + if(devfd == -1) + return -EINVAL; + + if (fstat(devfd, &st) < 0) + goto out; + + if (S_ISREG(st.st_mode)) { + *size = (uint64_t)st.st_size; + r = 0; + } else if (ioctl(devfd, BLKGETSIZE64, size) >= 0) + r = 0; +out: + close(devfd); + return r; +} + +static int device_info(struct device *device, + enum devcheck device_check, + int *readonly, uint64_t *size) +{ + struct stat st; + int fd, r = -EINVAL, flags = 0; + + *readonly = 0; + *size = 0; + + if (stat(device->path, &st) < 0) + return -EINVAL; + + /* never wipe header on mounted device */ + if (device_check == DEV_EXCL && S_ISBLK(st.st_mode)) + flags |= O_EXCL; + + /* Try to open read-write to check whether it is a read-only device */ + fd = open(device->path, O_RDWR | flags); + if (fd == -1 && errno == EROFS) { + *readonly = 1; + fd = open(device->path, O_RDONLY | flags); + } + + if (fd == -1 && device_check == DEV_EXCL && errno == EBUSY) + return -EBUSY; + + if (fd == -1) + return -EINVAL; + + if (S_ISREG(st.st_mode)) { + //FIXME: add readonly check + + *size = (uint64_t)st.st_size; + *size >>= SECTOR_SHIFT; + } else { + /* If the device can be opened read-write, i.e. readonly is still 0, then + * check whether BKROGET says that it is read-only. E.g. read-only loop + * devices may be openend read-write but are read-only according to BLKROGET + */ + if (*readonly == 0 && (r = ioctl(fd, BLKROGET, readonly)) < 0) + goto out; + + if (ioctl(fd, BLKGETSIZE64, size) >= 0) { + *size >>= SECTOR_SHIFT; + r = 0; + goto out; + } + } + r = -EINVAL; +out: + close(fd); + return r; +} + +static int device_internal_prepare(struct crypt_device *cd, struct device *device) +{ + char *loop_device; + int r, loop_fd, readonly = 0; + + if (device->init_done) + return 0; + + log_dbg("Allocating free loop device."); + loop_device = crypt_loop_get_device(); + if (!loop_device) { + log_err(cd, _("Cannot find a free loopback device.\n")); + return -EINVAL; + } + + /* Keep the loop open, dettached on last close. */ + loop_fd = crypt_loop_attach(loop_device, device->path, 0, 1, &readonly); + if (loop_fd == -1) { + log_err(cd, _("Attaching loopback device failed " + "(loop device with autoclear flag is required).\n")); + free(loop_device); + return -EINVAL; + } + + r = device_ready(loop_device); + if (r < 0) { + free(loop_device); + return r; + } + + device->loop_fd = loop_fd; + device->file_path = device->path; + device->path = loop_device; + device->init_done = 1; + + return 0; +} + +int device_block_adjust(struct crypt_device *cd, + struct device *device, + enum devcheck device_check, + uint64_t device_offset, + uint64_t *size, + uint32_t *flags) +{ + int r, real_readonly; + uint64_t real_size; + + if (!device) + return -ENOTBLK; + + r = device_internal_prepare(cd, device); + if (r) + return r; + + r = device_info(device, device_check, &real_readonly, &real_size); + if (r < 0) { + if (r == -EBUSY) + log_err(cd, _("Cannot use device %s which is in use " + "(already mapped or mounted).\n"), + device->path); + else + log_err(cd, _("Cannot get info about device %s.\n"), + device->path); + return r; + } + + if (device_offset >= real_size) { + log_err(cd, _("Requested offset is beyond real size of device %s.\n"), + device->path); + return -EINVAL; + } + + if (size && !*size) { + *size = real_size; + if (!*size) { + log_err(cd, _("Device %s has zero size.\n"), device->path); + return -ENOTBLK; + } + *size -= device_offset; + } + + /* in case of size is set by parameter */ + if (size && ((real_size - device_offset) < *size)) { + log_dbg("Device %s: offset = %" PRIu64 " requested size = %" PRIu64 + ", backing device size = %" PRIu64, + device->path, device_offset, *size, real_size); + log_err(cd, _("Device %s is too small.\n"), device->path); + return -EINVAL; + } + + if (flags && real_readonly) + *flags |= CRYPT_ACTIVATE_READONLY; + + if (size) + log_dbg("Calculated device size is %" PRIu64" sectors (%s), offset %" PRIu64 ".", + *size, real_readonly ? "RO" : "RW", device_offset); + return 0; +} diff --git a/lib/utils_devpath.c b/lib/utils_devpath.c index 31289eef..94728730 100644 --- a/lib/utils_devpath.c +++ b/lib/utils_devpath.c @@ -31,7 +31,6 @@ #include "utils_dm.h" char *crypt_lookup_dev(const char *dev_id); -int crypt_sysfs_check_crypt_segment(const char *device, uint64_t offset, uint64_t size); int crypt_sysfs_get_rotational(int major, int minor, int *rotational); static char *__lookup_dev(char *path, dev_t dev, int dir_level, const int max_level) @@ -168,82 +167,6 @@ char *crypt_lookup_dev(const char *dev_id) return devpath; } -static int crypt_sysfs_get_major_minor(const char *kname, int *major, int *minor) -{ - char path[PATH_MAX], tmp[64] = {0}; - int fd, r = 0; - - if (snprintf(path, sizeof(path), "/sys/block/%s/dev", kname) < 0) - return 0; - - if ((fd = open(path, O_RDONLY)) < 0) - return 0; - r = read(fd, tmp, sizeof(tmp)); - close(fd); - - if (r <= 0) - return 0; - - tmp[63] = '\0'; - if (sscanf(tmp, "%d:%d", major, minor) != 2) - return 0; - - return 1; -} - -static int crypt_sysfs_get_holders_dir(const char *device, char *path, int size) -{ - struct stat st; - - if (stat(device, &st) < 0 || !S_ISBLK(st.st_mode)) - return 0; - - if (snprintf(path, size, "/sys/dev/block/%d:%d/holders", - major(st.st_rdev), minor(st.st_rdev)) < 0) - return 0; - - return 1; -} - -int crypt_sysfs_check_crypt_segment(const char *device, uint64_t offset, uint64_t size) -{ - DIR *dir; - struct dirent *d; - char path[PATH_MAX], *dmname; - int major, minor, r = 0; - - if (!crypt_sysfs_get_holders_dir(device, path, sizeof(path))) - return -EINVAL; - - if (!(dir = opendir(path))) - return -EINVAL; - - while (!r && (d = readdir(dir))) { - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) - continue; - - if (!dm_is_dm_kernel_name(d->d_name)) { - r = -EBUSY; - break; - } - - if (!crypt_sysfs_get_major_minor(d->d_name, &major, &minor)) { - r = -EINVAL; - break; - } - - if (!(dmname = dm_device_path(NULL, major, minor))) { - r = -EINVAL; - break; - } - r = dm_check_segment(dmname, offset, size); - free(dmname); - } - closedir(dir); - - return r; -} - int crypt_sysfs_get_rotational(int major, int minor, int *rotational) { char path[PATH_MAX], tmp[64] = {0}; diff --git a/lib/utils_dm.h b/lib/utils_dm.h index ea64b9ed..3c3a3f6c 100644 --- a/lib/utils_dm.h +++ b/lib/utils_dm.h @@ -28,6 +28,7 @@ struct crypt_device; struct volume_key; struct crypt_params_verity; +struct device; /* Device mapper backend - kernel support flags */ #define DM_KEY_WIPE_SUPPORTED (1 << 0) /* key wipe message */ @@ -54,7 +55,7 @@ struct crypt_dm_active_device { uint64_t size; /* active device size */ uint32_t flags; /* activation flags */ const char *uuid; - const char *data_device; + struct device *data_device; union { struct { const char *cipher; @@ -67,7 +68,7 @@ struct crypt_dm_active_device { uint64_t iv_offset; /* IV initilisation sector */ } crypt; struct { - const char *hash_device; + struct device *hash_device; const char *root_hash; uint32_t root_hash_size; @@ -98,6 +99,5 @@ int dm_resume_and_reinstate_key(const char *name, char *dm_device_path(const char *prefix, int major, int minor); int dm_is_dm_device(int major, int minor); int dm_is_dm_kernel_name(const char *name); -int dm_check_segment(const char *name, uint64_t offset, uint64_t size); #endif /* _UTILS_DM_H */ diff --git a/lib/utils_wipe.c b/lib/utils_wipe.c index 24e45040..ef3883c3 100644 --- a/lib/utils_wipe.c +++ b/lib/utils_wipe.c @@ -34,18 +34,20 @@ #define MAXIMUM_WIPE_BYTES 1024 * 1024 * 32 /* 32 MiB */ -static ssize_t _crypt_wipe_zero(int fd, char *buffer, uint64_t offset, uint64_t size) +static ssize_t _crypt_wipe_zero(int fd, int bsize, char *buffer, + uint64_t offset, uint64_t size) { memset(buffer, 0, size); - return write_lseek_blockwise(fd, buffer, size, offset); + return write_lseek_blockwise(fd, bsize, buffer, size, offset); } -static ssize_t _crypt_wipe_random(int fd, char *buffer, uint64_t offset, uint64_t size) +static ssize_t _crypt_wipe_random(int fd, int bsize, char *buffer, + uint64_t offset, uint64_t size) { if (crypt_random_get(NULL, buffer, size, CRYPT_RND_NORMAL) < 0) return -EINVAL; - return write_lseek_blockwise(fd, buffer, size, offset); + return write_lseek_blockwise(fd, bsize, buffer, size, offset); } /* @@ -74,7 +76,8 @@ static void wipeSpecial(char *buffer, size_t buffer_size, unsigned int turn) } } -static ssize_t _crypt_wipe_disk(int fd, char *buffer, uint64_t offset, uint64_t size) +static ssize_t _crypt_wipe_disk(int fd, int bsize, char *buffer, + uint64_t offset, uint64_t size) { int r; unsigned int i; @@ -95,22 +98,23 @@ static ssize_t _crypt_wipe_disk(int fd, char *buffer, uint64_t offset, uint64_t if (r < 0) return r; - written = write_lseek_blockwise(fd, buffer, size, offset); + written = write_lseek_blockwise(fd, bsize, buffer, size, offset); if (written < 0 || written != (ssize_t)size) return written; } /* Rewrite it finally with random */ - return _crypt_wipe_random(fd, buffer, offset, size); + return _crypt_wipe_random(fd, bsize, buffer, offset, size); } -static ssize_t _crypt_wipe_ssd(int fd, char *buffer, uint64_t offset, uint64_t size) +static ssize_t _crypt_wipe_ssd(int fd, int bsize, char *buffer, + uint64_t offset, uint64_t size) { // FIXME: for now just rewrite it by random - return _crypt_wipe_random(fd, buffer, offset, size); + return _crypt_wipe_random(fd, bsize, buffer, offset, size); } -int crypt_wipe(const char *device, +int crypt_wipe(struct device *device, uint64_t offset, uint64_t size, crypt_wipe_type type, @@ -118,17 +122,17 @@ int crypt_wipe(const char *device, { struct stat st; char *buffer; - int devfd, flags, rotational; + int devfd, flags, rotational, bsize; ssize_t written; if (!size || size % SECTOR_SIZE || (size > MAXIMUM_WIPE_BYTES)) { log_dbg("Unsuported wipe size for device %s: %ld.", - device, (unsigned long)size); + device_path(device), (unsigned long)size); return -EINVAL; } - if (stat(device, &st) < 0) { - log_dbg("Device %s not found.", device); + if (stat(device_path(device), &st) < 0) { + log_dbg("Device %s not found.", device_path(device)); return -EINVAL; } @@ -142,6 +146,10 @@ int crypt_wipe(const char *device, type = CRYPT_WIPE_SSD; } + bsize = device_block_size(device); + if (bsize <= 0) + return -EINVAL; + buffer = malloc(size); if (!buffer) return -ENOMEM; @@ -152,7 +160,7 @@ int crypt_wipe(const char *device, if (exclusive && S_ISBLK(st.st_mode)) flags |= O_EXCL; - devfd = open(device, flags); + devfd = open(device_path(device), flags); if (devfd == -1) { free(buffer); return errno == EBUSY ? -EBUSY : -EINVAL; @@ -161,16 +169,16 @@ int crypt_wipe(const char *device, // FIXME: use fixed block size and loop here switch (type) { case CRYPT_WIPE_ZERO: - written = _crypt_wipe_zero(devfd, buffer, offset, size); + written = _crypt_wipe_zero(devfd, bsize, buffer, offset, size); break; case CRYPT_WIPE_DISK: - written = _crypt_wipe_disk(devfd, buffer, offset, size); + written = _crypt_wipe_disk(devfd, bsize, buffer, offset, size); break; case CRYPT_WIPE_SSD: - written = _crypt_wipe_ssd(devfd, buffer, offset, size); + written = _crypt_wipe_ssd(devfd, bsize, buffer, offset, size); break; case CRYPT_WIPE_RANDOM: - written = _crypt_wipe_random(devfd, buffer, offset, size); + written = _crypt_wipe_random(devfd, bsize, buffer, offset, size); break; default: log_dbg("Unsuported wipe type requested: (%d)", type); diff --git a/lib/verity/verity.c b/lib/verity/verity.c index 37cd52c7..ffe02557 100644 --- a/lib/verity/verity.c +++ b/lib/verity/verity.c @@ -52,11 +52,12 @@ struct verity_sb { /* Read verity superblock from disk */ int VERITY_read_sb(struct crypt_device *cd, - const char *device, uint64_t sb_offset, char **uuid_string, struct crypt_params_verity *params) { + const char *device = device_path(crypt_metadata_device(cd)); + int bsize = device_block_size(crypt_metadata_device(cd)); struct verity_sb sb = {}; ssize_t hdr_size = sizeof(struct verity_sb); int devfd = 0, sb_version; @@ -81,7 +82,7 @@ int VERITY_read_sb(struct crypt_device *cd, } if(lseek(devfd, sb_offset, SEEK_SET) < 0 || - read_blockwise(devfd, &sb, hdr_size) < hdr_size) { + read_blockwise(devfd, bsize, &sb, hdr_size) < hdr_size) { close(devfd); return -EIO; } @@ -144,11 +145,12 @@ int VERITY_read_sb(struct crypt_device *cd, /* Write verity superblock to disk */ int VERITY_write_sb(struct crypt_device *cd, - const char *device, uint64_t sb_offset, const char *uuid_string, struct crypt_params_verity *params) { + const char *device = device_path(crypt_metadata_device(cd)); + int bsize = device_block_size(crypt_metadata_device(cd)); struct verity_sb sb = {}; ssize_t hdr_size = sizeof(struct verity_sb); uuid_t uuid; @@ -184,7 +186,7 @@ int VERITY_write_sb(struct crypt_device *cd, memcpy(sb.salt, params->salt, params->salt_size); memcpy(sb.uuid, uuid, sizeof(sb.uuid)); - r = write_lseek_blockwise(devfd, (char*)&sb, hdr_size, sb_offset) < hdr_size ? -EIO : 0; + r = write_lseek_blockwise(devfd, bsize, (char*)&sb, hdr_size, sb_offset) < hdr_size ? -EIO : 0; if (r) log_err(cd, _("Error during update of verity header on device %s.\n"), device); close(devfd); @@ -220,14 +222,12 @@ int VERITY_UUID_generate(struct crypt_device *cd, char **uuid_string) /* Activate verity device in kernel device-mapper */ int VERITY_activate(struct crypt_device *cd, const char *name, - const char *hash_device, const char *root_hash, size_t root_hash_size, struct crypt_params_verity *verity_hdr, uint32_t activation_flags) { struct crypt_dm_active_device dmd; - uint64_t offset = 0; int r; log_dbg("Trying to activate VERITY device %s using hash %s.", @@ -236,7 +236,6 @@ int VERITY_activate(struct crypt_device *cd, if (verity_hdr->flags & CRYPT_VERITY_CHECK_HASH) { log_dbg("Verification of data in userspace required."); r = VERITY_verify(cd, verity_hdr, - crypt_get_device_name(cd), hash_device, root_hash, root_hash_size); if (r < 0) return r; @@ -246,8 +245,8 @@ int VERITY_activate(struct crypt_device *cd, return 0; dmd.target = DM_VERITY; - dmd.data_device = crypt_get_device_name(cd); - dmd.u.verity.hash_device = hash_device; + dmd.data_device = crypt_data_device(cd); + dmd.u.verity.hash_device = crypt_metadata_device(cd); dmd.u.verity.root_hash = root_hash; dmd.u.verity.root_hash_size = root_hash_size; dmd.u.verity.hash_offset = VERITY_hash_offset_block(verity_hdr), @@ -256,8 +255,13 @@ int VERITY_activate(struct crypt_device *cd, dmd.uuid = crypt_get_uuid(cd); dmd.u.verity.vp = verity_hdr; - r = device_check_and_adjust(cd, dmd.data_device, DEV_EXCL, - &dmd.size, &offset, &dmd.flags); + r = device_block_adjust(cd, dmd.u.verity.hash_device, DEV_OK, + 0, NULL, NULL); + if (r) + return r; + + r = device_block_adjust(cd, dmd.data_device, DEV_EXCL, + 0, &dmd.size, &dmd.flags); if (r) return r; diff --git a/lib/verity/verity.h b/lib/verity/verity.h index 671c1206..bdefd01e 100644 --- a/lib/verity/verity.h +++ b/lib/verity/verity.h @@ -31,20 +31,17 @@ struct crypt_device; struct crypt_params_verity; int VERITY_read_sb(struct crypt_device *cd, - const char *device, uint64_t sb_offset, char **uuid, struct crypt_params_verity *params); int VERITY_write_sb(struct crypt_device *cd, - const char *device, uint64_t sb_offset, const char *uuid_string, struct crypt_params_verity *params); int VERITY_activate(struct crypt_device *cd, const char *name, - const char *hash_device, const char *root_hash, size_t root_hash_size, struct crypt_params_verity *verity_hdr, @@ -52,15 +49,11 @@ int VERITY_activate(struct crypt_device *cd, int VERITY_verify(struct crypt_device *cd, struct crypt_params_verity *verity_hdr, - const char *data_device, - const char *hash_device, const char *root_hash, size_t root_hash_size); int VERITY_create(struct crypt_device *cd, struct crypt_params_verity *verity_hdr, - const char *data_device, - const char *hash_device, char *root_hash, size_t root_hash_size); diff --git a/lib/verity/verity_hash.c b/lib/verity/verity_hash.c index 74ea1322..d755f889 100644 --- a/lib/verity/verity_hash.c +++ b/lib/verity/verity_hash.c @@ -202,8 +202,8 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, int verify, int version, const char *hash_name, - const char *hash_device, - const char *data_device, + struct device *hash_device, + struct device *data_device, size_t hash_block_size, size_t data_block_size, off_t data_blocks, @@ -227,7 +227,8 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, log_dbg("Hash %s %s, data device %s, data blocks %" PRIu64 ", hash_device %s, offset %" PRIu64 ".", verify ? "verification" : "creation", hash_name, - data_device, data_blocks, hash_device, hash_position); + device_path(data_device), data_blocks, + device_path(hash_device), hash_position); if (data_blocks < 0 || hash_position < 0) { log_err(cd, _("Invalid size parameters for verity device.\n")); @@ -288,18 +289,21 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, log_dbg("Data device size required: %" PRIu64 " bytes.", data_device_size); - data_file = fopen(data_device, "r"); + data_file = fopen(device_path(data_device), "r"); if (!data_file) { - log_err(cd, _("Cannot open device %s.\n"), data_device); + log_err(cd, _("Cannot open device %s.\n"), + device_path(data_device) + ); r = -EIO; goto out; } log_dbg("Hash device size required: %" PRIu64 " bytes.", hash_device_size); - hash_file = fopen(hash_device, verify ? "r" : "r+"); + hash_file = fopen(device_path(hash_device), verify ? "r" : "r+"); if (!hash_file) { - log_err(cd, _("Cannot open device %s.\n"), hash_device); + log_err(cd, _("Cannot open device %s.\n"), + device_path(hash_device)); r = -EIO; goto out; } @@ -316,9 +320,10 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, if (r) goto out; } else { - hash_file_2 = fopen(hash_device, "r"); + hash_file_2 = fopen(device_path(hash_device), "r"); if (!hash_file_2) { - log_err(cd, _("Cannot open device %s.\n"), hash_device); + log_err(cd, _("Cannot open device %s.\n"), + device_path(hash_device)); r = -EIO; goto out; } @@ -378,16 +383,14 @@ out: /* Verify verity device using userspace crypto backend */ int VERITY_verify(struct crypt_device *cd, struct crypt_params_verity *verity_hdr, - const char *data_device, - const char *hash_device, const char *root_hash, size_t root_hash_size) { return VERITY_create_or_verify_hash(cd, 1, verity_hdr->hash_type, verity_hdr->hash_name, - hash_device, - data_device, + crypt_metadata_device(cd), + crypt_data_device(cd), verity_hdr->hash_block_size, verity_hdr->data_block_size, verity_hdr->data_size, @@ -401,8 +404,6 @@ int VERITY_verify(struct crypt_device *cd, /* Create verity hash */ int VERITY_create(struct crypt_device *cd, struct crypt_params_verity *verity_hdr, - const char *data_device, - const char *hash_device, char *root_hash, size_t root_hash_size) { @@ -418,8 +419,8 @@ int VERITY_create(struct crypt_device *cd, return VERITY_create_or_verify_hash(cd, 0, verity_hdr->hash_type, verity_hdr->hash_name, - hash_device, - data_device, + crypt_metadata_device(cd), + crypt_data_device(cd), verity_hdr->hash_block_size, verity_hdr->data_block_size, verity_hdr->data_size,