diff --git a/lib/integrity/integrity.c b/lib/integrity/integrity.c index 32afe5ef..3fc82b98 100644 --- a/lib/integrity/integrity.c +++ b/lib/integrity/integrity.c @@ -40,7 +40,7 @@ static int INTEGRITY_read_superblock(struct crypt_device *cd, } if (read_lseek_blockwise(devfd, device_block_size(device), - sb, sizeof(*sb), offset) != sizeof(*sb) || + device_alignment(device), sb, sizeof(*sb), offset) != sizeof(*sb) || memcmp(sb->magic, SB_MAGIC, sizeof(sb->magic)) || sb->version != SB_VERSION) { log_std(cd, "No integrity superblock detected on %s.\n", diff --git a/lib/internal.h b/lib/internal.h index d668e329..c7d64bac 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -79,6 +79,7 @@ int device_open(struct device *device, int flags); void device_disable_direct_io(struct device *device); int device_is_identical(struct device *device1, struct device *device2); int device_is_rotational(struct device *device); +size_t device_alignment(struct device *device); enum devcheck { DEV_OK = 0, DEV_EXCL = 1, DEV_SHARED = 2 }; int device_block_adjust(struct crypt_device *cd, @@ -104,10 +105,10 @@ uint64_t crypt_dev_partition_offset(const char *dev_path); ssize_t write_buffer(int fd, const void *buf, size_t count); ssize_t read_buffer(int fd, void *buf, size_t count); -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, void *buf, size_t count, off_t offset); -ssize_t read_lseek_blockwise(int fd, int bsize, void *buf, size_t count, off_t offset); +ssize_t write_blockwise(int fd, int bsize, size_t alignment, void *orig_buf, size_t count); +ssize_t read_blockwise(int fd, int bsize, size_t alignment, void *buf, size_t count); +ssize_t write_lseek_blockwise(int fd, int bsize, size_t alignment, void *buf, size_t count, off_t offset); +ssize_t read_lseek_blockwise(int fd, int bsize, size_t alignment, void *buf, size_t count, off_t offset); unsigned crypt_getpagesize(void); int init_crypto(struct crypt_device *ctx); diff --git a/lib/luks1/keyencryption.c b/lib/luks1/keyencryption.c index 27e87637..4f5dff3d 100644 --- a/lib/luks1/keyencryption.c +++ b/lib/luks1/keyencryption.c @@ -46,7 +46,7 @@ static int LUKS_endec_template(char *src, size_t srcLength, const char *cipher, const char *cipher_mode, struct volume_key *vk, unsigned int sector, - ssize_t (*func)(int, int, void *, size_t), + ssize_t (*func)(int, int, size_t, void *, size_t), int mode, struct crypt_device *ctx) { @@ -65,11 +65,13 @@ static int LUKS_endec_template(char *src, size_t srcLength, } }; int r, bsize, devfd = -1; + size_t alignment; log_dbg("Using dmcrypt to access keyslot area."); bsize = device_block_size(dmd.data_device); - if (bsize <= 0) + alignment = device_alignment(dmd.data_device); + if (bsize <= 0 || !alignment) return -EINVAL; dmd.size = size_round_up(srcLength, bsize) / SECTOR_SIZE; @@ -113,7 +115,7 @@ static int LUKS_endec_template(char *src, size_t srcLength, goto out; } - r = func(devfd, bsize, src, srcLength); + r = func(devfd, bsize, alignment, src, srcLength); if (r < 0) { log_err(ctx, _("Failed to access temporary keystore device.\n")); r = -EIO; @@ -137,6 +139,7 @@ int LUKS_encrypt_to_storage(char *src, size_t srcLength, struct device *device = crypt_metadata_device(ctx); struct crypt_storage *s; int devfd = -1, bsize, r = 0; + size_t alignment; /* Only whole sector writes supported */ if (srcLength % SECTOR_SIZE) @@ -172,7 +175,8 @@ int LUKS_encrypt_to_storage(char *src, size_t srcLength, /* Write buffer to device */ bsize = device_block_size(device); - if (bsize <= 0) + alignment = device_alignment(device); + if (bsize <= 0 || !alignment) goto out; devfd = device_open(device, O_RDWR); @@ -180,7 +184,7 @@ int LUKS_encrypt_to_storage(char *src, size_t srcLength, goto out; if (lseek(devfd, sector * SECTOR_SIZE, SEEK_SET) == -1 || - write_blockwise(devfd, bsize, src, srcLength) == -1) + write_blockwise(devfd, bsize, alignment, src, srcLength) == -1) goto out; r = 0; @@ -203,6 +207,7 @@ int LUKS_decrypt_from_storage(char *dst, size_t dstLength, struct device *device = crypt_metadata_device(ctx); struct crypt_storage *s; int devfd = -1, bsize, r = 0; + size_t alignment; /* Only whole sector reads supported */ if (dstLength % SECTOR_SIZE) @@ -231,7 +236,8 @@ int LUKS_decrypt_from_storage(char *dst, size_t dstLength, /* Read buffer from device */ bsize = device_block_size(device); - if (bsize <= 0) + alignment = device_alignment(device); + if (bsize <= 0 || !alignment) goto bad; devfd = device_open(device, O_RDONLY); @@ -239,7 +245,7 @@ int LUKS_decrypt_from_storage(char *dst, size_t dstLength, goto bad; if (lseek(devfd, sector * SECTOR_SIZE, SEEK_SET) == -1 || - read_blockwise(devfd, bsize, dst, dstLength) == -1) + read_blockwise(devfd, bsize, alignment, dst, dstLength) == -1) goto bad; close(devfd); diff --git a/lib/luks1/keymanage.c b/lib/luks1/keymanage.c index af340357..b0deb778 100644 --- a/lib/luks1/keymanage.c +++ b/lib/luks1/keymanage.c @@ -182,7 +182,8 @@ int LUKS_hdr_backup(const char *backup_file, struct crypt_device *ctx) goto out; } - if (read_blockwise(devfd, device_block_size(device), buffer, hdr_size) < hdr_size) { + if (read_blockwise(devfd, device_block_size(device), device_alignment(device), + buffer, hdr_size) < hdr_size) { r = -EIO; goto out; } @@ -301,7 +302,8 @@ int LUKS_hdr_restore( goto out; } - if (write_blockwise(devfd, device_block_size(device), buffer, buffer_size) < buffer_size) { + if (write_blockwise(devfd, device_block_size(device), device_alignment(device), + buffer, buffer_size) < buffer_size) { r = -EIO; goto out; } @@ -537,7 +539,8 @@ int LUKS_read_phdr(struct luks_phdr *hdr, return -EINVAL; } - if (read_blockwise(devfd, device_block_size(device), hdr, hdr_size) < hdr_size) + if (read_blockwise(devfd, device_block_size(device), device_alignment(device), + hdr, hdr_size) < hdr_size) r = -EIO; else r = _check_and_convert_hdr(device_path(device), hdr, require_luks_device, @@ -602,7 +605,8 @@ int LUKS_write_phdr(struct luks_phdr *hdr, convHdr.keyblock[i].stripes = htonl(hdr->keyblock[i].stripes); } - r = write_blockwise(devfd, device_block_size(device), &convHdr, hdr_size) < hdr_size ? -EIO : 0; + r = write_blockwise(devfd, device_block_size(device), device_alignment(device), + &convHdr, hdr_size) < hdr_size ? -EIO : 0; if (r) log_err(ctx, _("Error during update of LUKS header on device %s.\n"), device_path(device)); close(devfd); diff --git a/lib/tcrypt/tcrypt.c b/lib/tcrypt/tcrypt.c index e8e7ac2d..56b7cbeb 100644 --- a/lib/tcrypt/tcrypt.c +++ b/lib/tcrypt/tcrypt.c @@ -601,6 +601,7 @@ int TCRYPT_read_phdr(struct crypt_device *cd, ssize_t hdr_size = sizeof(struct tcrypt_phdr); char *base_device_path; int devfd = 0, r, bs; + size_t alignment; assert(sizeof(struct tcrypt_phdr) == 512); @@ -608,6 +609,7 @@ int TCRYPT_read_phdr(struct crypt_device *cd, hdr_size, device_path(device)); bs = device_block_size(device); + alignment = device_alignment(device); if (bs < 0) return bs; @@ -635,28 +637,28 @@ int TCRYPT_read_phdr(struct crypt_device *cd, r = -EIO; if (params->flags & CRYPT_TCRYPT_SYSTEM_HEADER) { - if (read_lseek_blockwise(devfd, bs, hdr, hdr_size, + if (read_lseek_blockwise(devfd, bs, alignment, hdr, hdr_size, TCRYPT_HDR_SYSTEM_OFFSET) == hdr_size) { r = TCRYPT_init_hdr(cd, hdr, params); } } else if (params->flags & CRYPT_TCRYPT_HIDDEN_HEADER) { if (params->flags & CRYPT_TCRYPT_BACKUP_HEADER) { - if (read_lseek_blockwise(devfd, bs, hdr, hdr_size, + if (read_lseek_blockwise(devfd, bs, alignment, hdr, hdr_size, TCRYPT_HDR_HIDDEN_OFFSET_BCK) == hdr_size) r = TCRYPT_init_hdr(cd, hdr, params); } else { - if (read_lseek_blockwise(devfd, bs, hdr, hdr_size, + if (read_lseek_blockwise(devfd, bs, alignment, hdr, hdr_size, TCRYPT_HDR_HIDDEN_OFFSET) == hdr_size) r = TCRYPT_init_hdr(cd, hdr, params); - if (r && read_lseek_blockwise(devfd, bs, hdr, hdr_size, + if (r && read_lseek_blockwise(devfd, bs, alignment, hdr, hdr_size, TCRYPT_HDR_HIDDEN_OFFSET_OLD) == hdr_size) r = TCRYPT_init_hdr(cd, hdr, params); } } else if (params->flags & CRYPT_TCRYPT_BACKUP_HEADER) { - if (read_lseek_blockwise(devfd, bs, hdr, hdr_size, + if (read_lseek_blockwise(devfd, bs, alignment, hdr, hdr_size, TCRYPT_HDR_OFFSET_BCK) == hdr_size) r = TCRYPT_init_hdr(cd, hdr, params); - } else if (read_blockwise(devfd, bs, hdr, hdr_size) == hdr_size) + } else if (read_blockwise(devfd, bs, alignment, hdr, hdr_size) == hdr_size) r = TCRYPT_init_hdr(cd, hdr, params); close(devfd); diff --git a/lib/utils.c b/lib/utils.c index 5eaa8e44..e4b69c8e 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -36,18 +36,6 @@ unsigned crypt_getpagesize(void) return r < 0 ? DEFAULT_MEM_ALIGNMENT : r; } -static size_t get_alignment(int fd) -{ - long alignment = DEFAULT_MEM_ALIGNMENT; - -#ifdef _PC_REC_XFER_ALIGN - alignment = fpathconf(fd, _PC_REC_XFER_ALIGN); - if (alignment < 0) - alignment = DEFAULT_MEM_ALIGNMENT; -#endif - return (size_t)alignment; -} - ssize_t read_buffer(int fd, void *buf, size_t count) { size_t read_size = 0; @@ -94,19 +82,18 @@ ssize_t write_buffer(int fd, const void *buf, size_t count) return (ssize_t)write_size; } -ssize_t write_blockwise(int fd, int bsize, void *orig_buf, size_t count) +ssize_t write_blockwise(int fd, int bsize, size_t alignment, void *orig_buf, size_t count) { void *hangover_buf = NULL, *buf = NULL; int r; - size_t alignment, hangover, solid; + size_t hangover, solid; ssize_t ret = -1; - if (fd == -1 || !orig_buf || bsize <= 0) + if (fd == -1 || !orig_buf || bsize <= 0 || !alignment) return -1; hangover = count % bsize; solid = count - hangover; - alignment = get_alignment(fd); if ((size_t)orig_buf & (alignment - 1)) { if (posix_memalign(&buf, alignment, count)) @@ -149,19 +136,18 @@ out: return ret; } -ssize_t read_blockwise(int fd, int bsize, void *orig_buf, size_t count) +ssize_t read_blockwise(int fd, int bsize, size_t alignment, void *orig_buf, size_t count) { void *hangover_buf = NULL, *buf = NULL; int r; - size_t alignment, hangover, solid; + size_t hangover, solid; ssize_t ret = -1; - if (fd == -1 || !orig_buf || bsize <= 0) + if (fd == -1 || !orig_buf || bsize <= 0 || !alignment) return -1; hangover = count % bsize; solid = count - hangover; - alignment = get_alignment(fd); if ((size_t)orig_buf & (alignment - 1)) { if (posix_memalign(&buf, alignment, count)) @@ -198,7 +184,7 @@ 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, int bsize, void *buf, size_t count, off_t offset) +ssize_t write_lseek_blockwise(int fd, int bsize, size_t alignment, void *buf, size_t count, off_t offset) { void *frontPadBuf = NULL; int r, frontHang; @@ -220,7 +206,7 @@ ssize_t write_lseek_blockwise(int fd, int bsize, void *buf, size_t count, off_t return -1; if (frontHang) { - if (posix_memalign(&frontPadBuf, get_alignment(fd), bsize)) + if (posix_memalign(&frontPadBuf, alignment, bsize)) return -1; r = read_buffer(fd, frontPadBuf, bsize); @@ -244,7 +230,7 @@ ssize_t write_lseek_blockwise(int fd, int bsize, void *buf, size_t count, off_t count -= innerCount; } - ret = count ? write_blockwise(fd, bsize, buf, count) : 0; + ret = count ? write_blockwise(fd, bsize, alignment, buf, count) : 0; if (ret >= 0) ret += innerCount; out: @@ -252,7 +238,7 @@ out: return ret; } -ssize_t read_lseek_blockwise(int fd, int bsize, void *buf, size_t count, off_t offset) +ssize_t read_lseek_blockwise(int fd, int bsize, size_t alignment, void *buf, size_t count, off_t offset) { void *frontPadBuf = NULL; int r, frontHang; @@ -274,7 +260,7 @@ ssize_t read_lseek_blockwise(int fd, int bsize, void *buf, size_t count, off_t o return -1; if (frontHang) { - if (posix_memalign(&frontPadBuf, get_alignment(fd), bsize)) + if (posix_memalign(&frontPadBuf, alignment, bsize)) return -1; r = read_buffer(fd, frontPadBuf, bsize); @@ -291,7 +277,7 @@ ssize_t read_lseek_blockwise(int fd, int bsize, void *buf, size_t count, off_t o count -= innerCount; } - ret = read_blockwise(fd, bsize, buf, count); + ret = read_blockwise(fd, bsize, alignment, buf, count); if (ret >= 0) ret += innerCount; out: diff --git a/lib/utils_device.c b/lib/utils_device.c index 604acbb6..6994dc72 100644 --- a/lib/utils_device.c +++ b/lib/utils_device.c @@ -43,6 +43,9 @@ struct device { int o_direct:1; int init_done:1; + + /* cached values */ + size_t alignment; }; static int device_block_size_fd(int fd, size_t *min_size) @@ -79,15 +82,28 @@ static int device_block_size_fd(int fd, size_t *min_size) return bsize; } +static size_t device_alignment_fd(int devfd) +{ + long alignment = DEFAULT_MEM_ALIGNMENT; + +#ifdef _PC_REC_XFER_ALIGN + alignment = fpathconf(devfd, _PC_REC_XFER_ALIGN); + if (alignment < 0) + alignment = DEFAULT_MEM_ALIGNMENT; +#endif + return (size_t)alignment; +} + static int device_read_test(int devfd) { char buffer[512]; int blocksize, r = -EIO; - size_t minsize = 0; + size_t minsize = 0, alignment; blocksize = device_block_size_fd(devfd, &minsize); + alignment = device_alignment_fd(devfd); - if (blocksize < 0) + if (blocksize <= 0 || !alignment) return -EINVAL; if (minsize == 0) @@ -96,7 +112,7 @@ static int device_read_test(int devfd) if (minsize > sizeof(buffer)) minsize = sizeof(buffer); - if (read_blockwise(devfd, blocksize, buffer, minsize) == (ssize_t)minsize) + if (read_blockwise(devfd, blocksize, alignment, buffer, minsize) == (ssize_t)minsize) r = 0; crypt_memzero(buffer, sizeof(buffer)); @@ -148,6 +164,8 @@ static int device_ready(struct device *device, int check_directio) else if (!S_ISBLK(st.st_mode)) r = S_ISREG(st.st_mode) ? -ENOTBLK : -EINVAL; + device->alignment = device_alignment_fd(devfd); + close(devfd); return r; } @@ -566,3 +584,18 @@ int device_is_rotational(struct device *device) return crypt_dev_is_rotational(major(st.st_rdev), minor(st.st_rdev)); } + +size_t device_alignment(struct device *device) +{ + int devfd; + + if (!device->alignment) { + devfd = open(device_path(device), O_RDONLY); + if (devfd != -1) { + device->alignment = device_alignment_fd(devfd); + close(devfd); + } + } + + return device->alignment; +} diff --git a/lib/utils_wipe.c b/lib/utils_wipe.c index 06da9b27..a60d7a1d 100644 --- a/lib/utils_wipe.c +++ b/lib/utils_wipe.c @@ -52,7 +52,7 @@ static void wipeSpecial(char *buffer, size_t buffer_size, unsigned int turn) } } -static int crypt_wipe_special(int fd, int bsize, char *buffer, +static int crypt_wipe_special(int fd, int bsize, size_t alignment, char *buffer, uint64_t offset, size_t size) { int r; @@ -74,7 +74,8 @@ static int crypt_wipe_special(int fd, int bsize, char *buffer, if (r < 0) return r; - written = write_lseek_blockwise(fd, bsize, buffer, size, offset); + written = write_lseek_blockwise(fd, bsize, alignment, + buffer, size, offset); if (written < 0 || written != (ssize_t)size) return -EIO; } @@ -83,7 +84,7 @@ static int crypt_wipe_special(int fd, int bsize, char *buffer, if (crypt_random_get(NULL, buffer, size, CRYPT_RND_NORMAL) < 0) return -EINVAL; - written = write_lseek_blockwise(fd, bsize, buffer, size, offset); + written = write_lseek_blockwise(fd, bsize, alignment, buffer, size, offset); if (written < 0 || written != (ssize_t)size) return -EIO; @@ -91,13 +92,14 @@ static int crypt_wipe_special(int fd, int bsize, char *buffer, } static int wipe_block(int devfd, crypt_wipe_pattern pattern, char *sf, - size_t device_block_size, size_t wipe_block_size, - uint64_t offset, bool *need_block_init) + size_t device_block_size, size_t alignment, + size_t wipe_block_size, uint64_t offset, bool *need_block_init) { int r; if (pattern == CRYPT_WIPE_SPECIAL) - return crypt_wipe_special(devfd, device_block_size, sf, offset, wipe_block_size); + return crypt_wipe_special(devfd, device_block_size, alignment, + sf, offset, wipe_block_size); if (*need_block_init) { if (pattern == CRYPT_WIPE_ZERO) { @@ -118,7 +120,8 @@ static int wipe_block(int devfd, crypt_wipe_pattern pattern, char *sf, return r; } - if (write_blockwise(devfd, device_block_size, sf, wipe_block_size) == (ssize_t)wipe_block_size) + if (write_blockwise(devfd, device_block_size, alignment, sf, + wipe_block_size) == (ssize_t)wipe_block_size) return 0; return -EIO; @@ -134,13 +137,15 @@ int crypt_wipe_device(struct crypt_device *cd, void *usrptr) { int r, devfd = -1, bsize; + size_t alignment; char *sf = NULL; uint64_t dev_size; bool need_block_init = true; /* Note: LUKS1 calls it with wipe_block not aligned to multiple of bsize */ bsize = device_block_size(device); - if ((bsize <= 0) || (wipe_block_size < (size_t)bsize)) + alignment = device_alignment(device); + if ((bsize <= 0) || !alignment || (wipe_block_size < (size_t)bsize)) return -EINVAL; /* Everything must be aligned to SECTOR_SIZE */ @@ -163,7 +168,7 @@ int crypt_wipe_device(struct crypt_device *cd, dev_size = offset + length; } - r = posix_memalign((void **)&sf, crypt_getpagesize(), wipe_block_size); + r = posix_memalign((void **)&sf, alignment, wipe_block_size); if (r) goto out; @@ -189,7 +194,8 @@ int crypt_wipe_device(struct crypt_device *cd, //log_dbg("Wipe %012" PRIu64 "-%012" PRIu64 " bytes", offset, offset + wipe_block_size); - r = wipe_block(devfd, pattern, sf, bsize, wipe_block_size, offset, &need_block_init); + r = wipe_block(devfd, pattern, sf, bsize, alignment, + wipe_block_size, offset, &need_block_init); if (r) { log_err(cd, "Device wipe error, offset %" PRIu64 ".\n", offset); break; diff --git a/lib/verity/verity.c b/lib/verity/verity.c index e00652b2..4c257bb0 100644 --- a/lib/verity/verity.c +++ b/lib/verity/verity.c @@ -58,7 +58,6 @@ int VERITY_read_sb(struct crypt_device *cd, struct crypt_params_verity *params) { struct device *device = crypt_metadata_device(cd); - int bsize = device_block_size(device); struct verity_sb sb = {}; ssize_t hdr_size = sizeof(struct verity_sb); int devfd = 0, sb_version; @@ -84,7 +83,8 @@ int VERITY_read_sb(struct crypt_device *cd, } if (lseek(devfd, sb_offset, SEEK_SET) < 0 || - read_blockwise(devfd, bsize, &sb, hdr_size) < hdr_size) { + read_blockwise(devfd, device_block_size(device), device_alignment(device), + &sb, hdr_size) < hdr_size) { close(devfd); return -EIO; } @@ -156,7 +156,6 @@ int VERITY_write_sb(struct crypt_device *cd, struct crypt_params_verity *params) { struct device *device = crypt_metadata_device(cd); - int bsize = device_block_size(device); struct verity_sb sb = {}; ssize_t hdr_size = sizeof(struct verity_sb); char *algorithm; @@ -197,7 +196,8 @@ 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, bsize, (char*)&sb, hdr_size, sb_offset) < hdr_size ? -EIO : 0; + r = write_lseek_blockwise(devfd, device_block_size(device), device_alignment(device), + (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_path(device));