aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMilan Broz <gmazyland@gmail.com>2015-03-18 15:01:53 +0100
committerMilan Broz <gmazyland@gmail.com>2015-03-18 15:01:53 +0100
commit0bc437d92c30945991881c8b8ca245a016fc1236 (patch)
tree9b207f0c569965b8f49ebf8ed25397a5f1d460f2
parent6b10f30eb925123b8e46bbf2cbd9f0dde60c1955 (diff)
downloadcryptsetup-0bc437d92c30945991881c8b8ca245a016fc1236.tar.gz
Detect if O_DIRECT is usable on device allocation.
Try to read the first sector of a device when allocating device context. Should fix issue#247.
-rw-r--r--lib/utils_device.c158
1 files changed, 120 insertions, 38 deletions
diff --git a/lib/utils_device.c b/lib/utils_device.c
index e66e380..acfbfc6 100644
--- a/lib/utils_device.c
+++ b/lib/utils_device.c
@@ -3,8 +3,8 @@
*
* Copyright (C) 2004, Jana Saout <jana@saout.de>
* Copyright (C) 2004-2007, Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2012, Milan Broz
+ * Copyright (C) 2009-2015, Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2015, Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -38,23 +38,109 @@ struct device {
char *file_path;
int loop_fd;
+ int o_direct:1;
int init_done:1;
};
-static int device_ready(const char *device)
+static int device_block_size_fd(int fd, size_t *min_size)
{
- int devfd, r = 0;
struct stat st;
+ int bsize = 0, r = -EINVAL;
- //FIXME: check if device allows to use O_DIRECT
- // not only on open but also on read (with offset 0)
+ if (fstat(fd, &st) < 0)
+ return -EINVAL;
+
+ if (S_ISREG(st.st_mode))
+ r = (int)crypt_getpagesize();
+ else if (ioctl(fd, BLKSSZGET, &bsize) >= 0)
+ r = bsize;
+ else
+ r = -EINVAL;
+
+ if (r < 0 || !min_size)
+ return r;
+
+ if (S_ISREG(st.st_mode)) {
+ /* file can be empty as well */
+ if (st.st_size > bsize)
+ *min_size = bsize;
+ else
+ *min_size = st.st_size;
+ } else {
+ /* block device must have at least one block */
+ *min_size = bsize;
+ }
+
+ return bsize;
+}
+
+static int device_read_test(int devfd)
+{
+ char buffer[512];
+ int blocksize, r = -EIO;
+ size_t minsize = 0;
+
+ blocksize = device_block_size_fd(devfd, &minsize);
+
+ if (blocksize < 0)
+ return -EINVAL;
+
+ if (minsize == 0)
+ return 0;
+
+ if (minsize > sizeof(buffer))
+ minsize = sizeof(buffer);
+
+ if (read_blockwise(devfd, blocksize, buffer, minsize) == (ssize_t)minsize)
+ r = 0;
+
+ crypt_memzero(buffer, sizeof(buffer));
+ return r;
+}
+
+/*
+ * The direct-io is always preferred. The header is usually mapped to the same
+ * device and can be accessed when the rest of device is mapped to data device.
+ * Using dirct-io encsures that we do not mess with data in cache.
+ * (But proper alignment should prevent this in the first place.)
+ * The read test is needed to detect broken configurations (seen with remote
+ * block devices) that allow open with direct-io but then fails on read.
+ */
+static int device_ready(struct device *device, int check_directio)
+{
+ int devfd = -1, r = 0;
+ struct stat st;
+
+ if (check_directio) {
+ log_dbg("Trying to open and read device %s with direct-io.",
+ device_path(device));
+ devfd = open(device_path(device), O_RDONLY | O_DIRECT);
+ if (devfd >= 0 && device_read_test(devfd) == 0) {
+ device->o_direct = 1;
+ } else {
+ close(devfd);
+ devfd = -1;
+ }
+ }
- log_dbg("Trying to open and read device %s.", device);
- devfd = open(device, O_RDONLY);
if (devfd < 0) {
- log_err(NULL, _("Device %s doesn't exist or access denied.\n"), device);
+ log_dbg("Trying to open and read device %s without direct-io.",
+ device_path(device));
+ devfd = open(device_path(device), O_RDONLY);
+ if (devfd >= 0 && device_read_test(devfd) == 0) {
+ device->o_direct = 0;
+ } else {
+ close(devfd);
+ devfd = -1;
+ }
+ }
+
+ if (devfd < 0) {
+ log_err(NULL, _("Device %s doesn't exist or access denied.\n"),
+ device_path(device));
return -EINVAL;
}
+
if (fstat(devfd, &st) < 0)
r = -EINVAL;
else if (!S_ISBLK(st.st_mode))
@@ -68,12 +154,11 @@ int device_open(struct device *device, int flags)
{
int devfd;
- devfd = open(device_path(device), flags | O_DIRECT | O_SYNC);
- if (devfd < 0 && errno == EINVAL) {
- log_dbg("Trying to open device %s without direct-io.",
- device_path(device));
- devfd = open(device_path(device), flags | O_SYNC);
- }
+ flags |= O_SYNC;
+ if (device->o_direct)
+ flags |= O_DIRECT;
+
+ devfd = open(device_path(device), flags);
if (devfd < 0)
log_dbg("Cannot open device %s.", device_path(device));
@@ -96,24 +181,24 @@ int device_alloc(struct device **device, const char *path)
return -ENOMEM;
memset(dev, 0, sizeof(struct device));
+ dev->path = strdup(path);
+ if (!dev->path) {
+ free(dev);
+ return -ENOMEM;
+ }
dev->loop_fd = -1;
- r = device_ready(path);
+ r = device_ready(dev, 1);
if (!r) {
dev->init_done = 1;
} else if (r == -ENOTBLK) {
/* alloc loop later */
} else if (r < 0) {
+ free(dev->path);
free(dev);
return -ENOTBLK;
}
- dev->path = strdup(path);
- if (!dev->path) {
- free(dev);
- return -ENOMEM;
- }
-
*device = dev;
return 0;
}
@@ -214,27 +299,20 @@ out:
int device_block_size(struct device *device)
{
- struct stat st;
- int fd, bsize = 0, r = -EINVAL;
+ int fd, r = -EINVAL;
if (!device)
return 0;
+ if (device->file_path)
+ return (int)crypt_getpagesize();
+
fd = open(device->path, O_RDONLY);
if(fd < 0)
return -EINVAL;
- if (fstat(fd, &st) < 0)
- goto out;
-
- if (S_ISREG(st.st_mode) || device->file_path) {
- r = (int)crypt_getpagesize();
- goto out;
- }
+ r = device_block_size_fd(fd, NULL);
- if (ioctl(fd, BLKSSZGET, &bsize) >= 0)
- r = bsize;
-out:
if (r <= 0)
log_dbg("Cannot get block size for device %s.", device_path(device));
@@ -342,7 +420,7 @@ out:
static int device_internal_prepare(struct crypt_device *cd, struct device *device)
{
- char *loop_device;
+ char *loop_device, *file_path = NULL;
int r, loop_fd, readonly = 0;
if (device->init_done)
@@ -368,15 +446,19 @@ static int device_internal_prepare(struct crypt_device *cd, struct device *devic
return -EINVAL;
}
- r = device_ready(loop_device);
+ file_path = device->path;
+ device->path = loop_device;
+
+ r = device_ready(device, device->o_direct);
if (r < 0) {
+ device->path = file_path;
+ crypt_loop_detach(loop_device);
free(loop_device);
return r;
}
device->loop_fd = loop_fd;
- device->file_path = device->path;
- device->path = loop_device;
+ device->file_path = file_path;
device->init_done = 1;
return 0;