Commit 961682aa authored by Dave Reisner's avatar Dave Reisner Committed by Milan Broz

lib/utils_crypt: optimize seek to keyfile-offset

Avoid using unbuffered reads when "seeking" to a keyfile offset. This is
abysmally slow when the key is hidden at the end of a large device.
Instead, try to actually call lseek, falling back on reading in chunks
of BUFSIZ bytes until the desired offset is reached.

Command line:

  cryptsetup luksOpen /dev/vdc1 home \
      --keyfile /dev/vdd --keyfile-size 4096 --keyfile-offset 123456789

Before:

  real   0m25.589s
  user   0m7.030s
  sys    0m18.479s

After:

  real   0m4.464s
  user   0m4.253s
  sys    0m0.157s
parent 2f37cfe5
2012-08-27 Milan Broz <gmazyland@gmail.com>
* Optimize seek to keyfile-offset (Issue #135, thx to dreisner).
2012-08-12 Milan Broz <gmazyland@gmail.com>
* Allocate loop device late (only when real block device needed).
* Rework underlying device/file access functions.
......
......@@ -261,6 +261,49 @@ out_err:
return r;
}
/*
* A simple call to lseek(3) might not be possible for some inputs (e.g.
* reading from a pipe), so this function instead reads of up to BUFSIZ bytes
* at a time until the specified number of bytes. It returns -1 on read error
* or when it reaches EOF before the requested number of bytes have been
* discarded.
*/
static int keyfile_seek(int fd, size_t bytes)
{
char tmp[BUFSIZ];
size_t next_read;
ssize_t bytes_r;
off_t r;
r = lseek(fd, bytes, SEEK_CUR);
if (r > 0)
return 0;
if (r < 0 && errno != ESPIPE)
return -1;
while (bytes > 0) {
/* figure out how much to read */
next_read = bytes > sizeof(tmp) ? sizeof(tmp) : bytes;
bytes_r = read(fd, tmp, next_read);
if (bytes_r < 0) {
if (errno == EINTR)
continue;
/* read error */
return -1;
}
if (bytes_r == 0)
/* EOF */
break;
bytes -= bytes_r;
}
return bytes == 0 ? 0 : -1;
}
/*
* Note: --key-file=- is interpreted as a read from a binary file (stdin)
* key_size_max == 0 means detect maximum according to input type (tty/file)
......@@ -274,7 +317,7 @@ int crypt_get_key(const char *prompt,
{
int fd, regular_file, read_stdin, char_read, unlimited_read = 0;
int r = -EINVAL;
char *pass = NULL, tmp;
char *pass = NULL;
size_t buflen, i, file_read_size;
struct stat st;
......@@ -342,11 +385,10 @@ int crypt_get_key(const char *prompt,
}
/* Discard keyfile_offset bytes on input */
for(i = 0; i < keyfile_offset; i++)
if (read(fd, &tmp, 1) != 1) {
log_err(cd, _("Cannot seek to requested keyfile offset.\n"));
goto out_err;
}
if (keyfile_offset && keyfile_seek(fd, keyfile_offset) < 0) {
log_err(cd, _("Cannot seek to requested keyfile offset.\n"));
goto out_err;
}
for(i = 0; i < keyfile_size_max; i++) {
if(i == buflen) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment