diff --git a/ChangeLog b/ChangeLog index b01852ec..f75f0952 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2011-04-11 Milan Broz + * Add loop manipulation code and support mapping of images in file. + 2011-04-05 Milan Broz * Add exception to COPYING for binary distribution linked with OpenSSL library. * Set secure data flag (wipe all ioclt buffers) if devmapper library supports it. diff --git a/lib/Makefile.am b/lib/Makefile.am index 903618cd..d136642f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -51,6 +51,7 @@ libcryptsetup_la_SOURCES = \ utils_crypt.c \ utils_crypt.h \ utils_debug.c \ + utils_loop.c \ libdevmapper.c \ volumekey.c \ random.c \ diff --git a/lib/internal.h b/lib/internal.h index 7b42082d..9484ad32 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -106,6 +106,12 @@ int device_check_and_adjust(struct crypt_device *cd, int *read_only); int wipe_device_header(const char *device, int sectors); +/* loopback device helpers */ +char *crypt_loop_get_device(void); +char *crypt_loop_backing_file(const char *loop); +int crypt_loop_device(const char *loop); +int crypt_loop_attach(const char *loop, const char *file, int offset, int *readonly); + 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) #define log_std(c, x...) logger(c, CRYPT_LOG_NORMAL, __FILE__, __LINE__, x) diff --git a/lib/setup.c b/lib/setup.c index a264fa2b..cc7423b5 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -15,6 +15,8 @@ struct crypt_device { char *type; char *device; + char *backing_file; + int loop_fd; struct volume_key *volume_key; uint64_t timeout; uint64_t iteration_time; @@ -432,7 +434,7 @@ static int key_from_terminal(struct crypt_device *cd, char *msg, char **key, *key = NULL; if(!msg && asprintf(&prompt, _("Enter passphrase for %s: "), - cd->device) < 0) + crypt_get_device_name(cd)) < 0) return -ENOMEM; if (!msg) @@ -961,32 +963,57 @@ const char *crypt_get_dir(void) int crypt_init(struct crypt_device **cd, const char *device) { struct crypt_device *h = NULL; + int r, readonly = 0; if (!cd) return -EINVAL; log_dbg("Allocating crypt device %s context.", device); - if (device && !device_ready(NULL, device, O_RDONLY)) - return -ENOTBLK; - if (!(h = malloc(sizeof(struct crypt_device)))) return -ENOMEM; memset(h, 0, sizeof(*h)); + h->loop_fd = -1; if (device) { - h->device = strdup(device); - if (!h->device) { - free(h); - return -ENOMEM; + r = device_ready(NULL, device, O_RDONLY); + if (r == -ENOTBLK) { + h->device = crypt_loop_get_device(); + log_dbg("Not a block device, %s%s.", + h->device ? "using free loop device " : + "no free loop device found", + h->device ?: ""); + if (!h->device) { + r = -ENOSYS; + goto bad; + } + + /* Keep the loop open, dettached on last close. */ + h->loop_fd = crypt_loop_attach(h->device, device, 0, &readonly); + if (h->loop_fd == -1) { + log_dbg("Attaching loop failed."); + r = -EINVAL; + goto bad; + } + + h->backing_file = crypt_loop_backing_file(h->device); + r = device_ready(NULL, h->device, O_RDONLY); } - } else - h->device = NULL; + if (r < 0) { + r = -ENOTBLK; + goto bad; + } + } + + if (!h->device && device && !(h->device = strdup(device))) { + r = -ENOMEM; + goto bad; + } if (dm_init(h, 1) < 0) { - free(h); - return -ENOSYS; + r = -ENOSYS; + goto bad; } h->iteration_time = 1000; @@ -995,6 +1022,16 @@ int crypt_init(struct crypt_device **cd, const char *device) h->rng_type = crypt_random_default_key_rng(); *cd = h; return 0; +bad: + + if (h) { + if (h->loop_fd != -1) + close(h->loop_fd); + free(h->device); + free(h->backing_file); + } + free(h); + return r; } int crypt_init_by_name(struct crypt_device **cd, const char *name) @@ -1043,6 +1080,13 @@ int crypt_init_by_name(struct crypt_device **cd, const char *name) goto out; /* Try to initialise basic parameters from active device */ + + if (!(*cd)->backing_file && device && crypt_loop_device(device) && + !((*cd)->backing_file = crypt_loop_backing_file(device))) { + r = -ENOMEM; + goto out; + } + if (device_uuid) { if (!strncmp(CRYPT_PLAIN, device_uuid, sizeof(CRYPT_PLAIN)-1)) { (*cd)->type = strdup(CRYPT_PLAIN); @@ -1427,10 +1471,14 @@ void crypt_free(struct crypt_device *cd) if (cd) { log_dbg("Releasing crypt device %s context.", cd->device); + if (cd->loop_fd != -1) + close(cd->loop_fd); + dm_exit(); crypt_free_volume_key(cd->volume_key); free(cd->device); + free(cd->backing_file); free(cd->type); /* used in plain device only */ @@ -2302,7 +2350,7 @@ const char *crypt_get_uuid(struct crypt_device *cd) const char *crypt_get_device_name(struct crypt_device *cd) { - return cd->device; + return cd->backing_file ?: cd->device; } int crypt_get_volume_key_size(struct crypt_device *cd) diff --git a/lib/utils.c b/lib/utils.c index a7408e35..615b1e5f 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -238,30 +238,33 @@ ssize_t write_lseek_blockwise(int fd, const char *buf, size_t count, off_t offse int device_ready(struct crypt_device *cd, const char *device, int mode) { - int devfd, r = 1; + 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 0; + 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 0; + 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 = 0; + r = -EIO; } memset(buf, 0, sizeof(buf)); diff --git a/lib/utils_loop.c b/lib/utils_loop.c new file mode 100644 index 00000000..d59ca075 --- /dev/null +++ b/lib/utils_loop.c @@ -0,0 +1,127 @@ +/* + * loopback block device utilities + * + * Copyright (C) 2011, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define LOOP_DEV_MAJOR 7 + +char *crypt_loop_get_device(void) +{ + char dev[20]; + int i, loop_fd; + struct stat st; + struct loop_info64 lo64 = {0}; + + for(i = 0; i < 256; i++) { + sprintf(dev, "/dev/loop%d", i); + if (stat(dev, &st) || !S_ISBLK(st.st_mode)) + return NULL; + + loop_fd = open(dev, O_RDONLY); + if (loop_fd < 0) + return NULL; + + if(ioctl(loop_fd, LOOP_GET_STATUS64, &lo64) && errno == ENXIO) { + close(loop_fd); + return strdup(dev); + } + close(loop_fd); + } + + return NULL; +} + +int crypt_loop_attach(const char *loop, const char *file, + int offset, int *readonly) +{ + struct loop_info64 lo64 = {0}; + int loop_fd = -1, file_fd = -1, r = 1; + + file_fd = open(file, (*readonly ? O_RDONLY : O_RDWR) | O_EXCL); + if (file_fd < 0 && errno == EROFS && !*readonly) { + *readonly = 1; + file_fd = open(file, O_RDONLY | O_EXCL); + } + if (file_fd < 0) + goto out; + + loop_fd = open(loop, *readonly ? O_RDONLY : O_RDWR); + if (loop_fd < 0) + goto out; + + strncpy((char*)lo64.lo_file_name, file, LO_NAME_SIZE); + lo64.lo_offset = offset; + lo64.lo_flags |= LO_FLAGS_AUTOCLEAR; + + if (ioctl(loop_fd, LOOP_SET_FD, file_fd) < 0) + goto out; + + if (ioctl(loop_fd, LOOP_SET_STATUS64, &lo64) < 0) { + (void)ioctl(loop_fd, LOOP_CLR_FD, 0); + goto out; + } + r = 0; +out: + if (r && loop_fd >= 0) + close(loop_fd); + if (file_fd >= 0) + close(file_fd); + return r ? -1 : loop_fd; +} + +char *crypt_loop_backing_file(const char *loop) +{ + struct loop_info64 lo64 = {0}; + int loop_fd; + + loop_fd = open(loop, O_RDONLY); + if (loop_fd < 0) + return NULL; + + if (ioctl(loop_fd, LOOP_GET_STATUS64, &lo64) < 0) { + close(loop_fd); + return NULL; + } + + lo64.lo_file_name[LO_NAME_SIZE-2] = '*'; + lo64.lo_file_name[LO_NAME_SIZE-1] = 0; + + close(loop_fd); + + return strdup((char*)lo64.lo_file_name); +} + +int crypt_loop_device(const char *loop) +{ + struct stat st; + + if (stat(loop, &st) || !S_ISBLK(st.st_mode) || + major(st.st_rdev) != LOOP_DEV_MAJOR) + return 0; + + return 1; +}