diff --git a/lib/utils_device.c b/lib/utils_device.c index b3ea8ac3..99d6a7be 100644 --- a/lib/utils_device.c +++ b/lib/utils_device.c @@ -419,25 +419,22 @@ out: static int device_internal_prepare(struct crypt_device *cd, struct device *device) { - char *loop_device, *file_path = NULL; + char *loop_device = NULL, *file_path = NULL; int r, loop_fd, readonly = 0; if (device->init_done) return 0; - log_dbg("Allocating a free loop device."); - loop_device = crypt_loop_get_device(); - if (!loop_device) { - if (getuid() || geteuid()) - log_err(cd, _("Cannot use a loopback device, " - "running as non-root user.\n")); - else - log_err(cd, _("Cannot find a free loopback device.\n")); + if (getuid() || geteuid()) { + log_err(cd, _("Cannot use a loopback device, " + "running as non-root user.\n")); return -ENOTSUP; } + log_dbg("Allocating a free loop device."); + /* Keep the loop open, dettached on last close. */ - loop_fd = crypt_loop_attach(loop_device, device->path, 0, 1, &readonly); + 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")); diff --git a/lib/utils_loop.c b/lib/utils_loop.c index 38e60932..286de927 100644 --- a/lib/utils_loop.c +++ b/lib/utils_loop.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include #include #include @@ -73,7 +74,7 @@ static char *crypt_loop_get_device_old(void) return NULL; } -char *crypt_loop_get_device(void) +static char *crypt_loop_get_device(void) { char dev[64]; int i, loop_fd; @@ -99,13 +100,15 @@ char *crypt_loop_get_device(void) return strdup(dev); } -int crypt_loop_attach(const char *loop, const char *file, int offset, +int crypt_loop_attach(char **loop, const char *file, int offset, int autoclear, int *readonly) { struct loop_info64 lo64 = {0}; char *lo_file_name; int loop_fd = -1, file_fd = -1, r = 1; + *loop = NULL; + file_fd = open(file, (*readonly ? O_RDONLY : O_RDWR) | O_EXCL); if (file_fd < 0 && (errno == EROFS || errno == EACCES) && !*readonly) { *readonly = 1; @@ -114,9 +117,25 @@ int crypt_loop_attach(const char *loop, const char *file, int offset, if (file_fd < 0) goto out; - loop_fd = open(loop, *readonly ? O_RDONLY : O_RDWR); - if (loop_fd < 0) - goto out; + while (loop_fd < 0) { + *loop = crypt_loop_get_device(); + if (!*loop) + goto out; + + loop_fd = open(*loop, *readonly ? O_RDONLY : O_RDWR); + if (loop_fd < 0) + goto out; + + if (ioctl(loop_fd, LOOP_SET_FD, file_fd) < 0) { + if (errno != EBUSY) + goto out; + free(*loop); + *loop = NULL; + + close(loop_fd); + loop_fd = -1; + } + } lo_file_name = (char*)lo64.lo_file_name; lo_file_name[LO_NAME_SIZE-1] = '\0'; @@ -125,9 +144,6 @@ int crypt_loop_attach(const char *loop, const char *file, int offset, if (autoclear) 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; @@ -149,6 +165,10 @@ out: close(loop_fd); if (file_fd >= 0) close(file_fd); + if (r && *loop) { + free(*loop); + *loop = NULL; + } return r ? -1 : loop_fd; } diff --git a/lib/utils_loop.h b/lib/utils_loop.h index e8c2a5dd..5f2b0eb6 100644 --- a/lib/utils_loop.h +++ b/lib/utils_loop.h @@ -24,10 +24,9 @@ /* 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 crypt_loop_attach(char **loop, const char *file, int offset, int autoclear, int *readonly); int crypt_loop_detach(const char *loop); int crypt_loop_resize(const char *loop); diff --git a/tests/api-test.c b/tests/api-test.c index 0a8062bd..df5547b8 100644 --- a/tests/api-test.c +++ b/tests/api-test.c @@ -417,16 +417,8 @@ static int _setup(void) if (_system(cmd, 1)) return 1; - if (!THE_LOOP_DEV) - THE_LOOP_DEV = crypt_loop_get_device(); - if (!THE_LOOP_DEV) { - printf("Cannot find free loop device.\n"); - return 1; - } - if (crypt_loop_device(THE_LOOP_DEV)) { - fd = crypt_loop_attach(THE_LOOP_DEV, test_loop_file, 0, 0, &ro); - close(fd); - } + fd = crypt_loop_attach(&THE_LOOP_DEV, test_loop_file, 0, 0, &ro); + close(fd); tmp_file_1 = strdup(THE_LFILE_TEMPLATE); if ((fd=mkstemp(tmp_file_1)) == -1) { @@ -441,34 +433,15 @@ static int _setup(void) _system("dmsetup create " DEVICE_EMPTY_name " --table \"0 10000 zero\"", 1); _system("dmsetup create " DEVICE_ERROR_name " --table \"0 10000 error\"", 1); - if (!DEVICE_1) - DEVICE_1 = crypt_loop_get_device(); - if (!DEVICE_1) { - printf("Cannot find free loop device.\n"); - return 1; - } - if (crypt_loop_device(DEVICE_1)) { - _system(" [ ! -e " IMAGE1 " ] && bzip2 -dk " IMAGE1 ".bz2", 1); - fd = crypt_loop_attach(DEVICE_1, IMAGE1, 0, 0, &ro); - close(fd); - } - if (!DEVICE_2) - DEVICE_2 = crypt_loop_get_device(); - if (!DEVICE_2) { - printf("Cannot find free loop device.\n"); - return 1; - } - if (crypt_loop_device(DEVICE_2)) { - _system("dd if=/dev/zero of=" IMAGE_EMPTY " bs=1M count=4 2>/dev/null", 1); - fd = crypt_loop_attach(DEVICE_2, IMAGE_EMPTY, 0, 0, &ro); - close(fd); - } - if (!DEVICE_3) - DEVICE_3 = crypt_loop_get_device(); - if (!DEVICE_3) { - printf("Cannot find free loop device.\n"); - return 1; - } + + _system(" [ ! -e " IMAGE1 " ] && bzip2 -dk " IMAGE1 ".bz2", 1); + fd = crypt_loop_attach(&DEVICE_1, IMAGE1, 0, 0, &ro); + close(fd); + + _system("dd if=/dev/zero of=" IMAGE_EMPTY " bs=1M count=4 2>/dev/null", 1); + fd = crypt_loop_attach(&DEVICE_2, IMAGE_EMPTY, 0, 0, &ro); + close(fd); + /* Keymaterial offset is less than 8 sectors */ _system(" [ ! -e " EVL_HEADER_1 " ] && bzip2 -dk " EVL_HEADER_1 ".bz2", 1); /* keymaterial offset aims into payload area */ @@ -1522,7 +1495,7 @@ static void LuksHeaderBackup(void) crypt_free(cd); // exercise luksOpen using backup header on block device - fd = crypt_loop_attach(DEVICE_3, BACKUP_FILE, 0, 0, &ro); + fd = crypt_loop_attach(&DEVICE_3, BACKUP_FILE, 0, 0, &ro); close(fd); OK_(fd < 0); OK_(crypt_init(&cd, DEVICE_3));