summaryrefslogtreecommitdiff
path: root/lib/loopdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/loopdev.c')
-rw-r--r--lib/loopdev.c86
1 files changed, 73 insertions, 13 deletions
diff --git a/lib/loopdev.c b/lib/loopdev.c
index 3b65b5d29..ebb7ee293 100644
--- a/lib/loopdev.c
+++ b/lib/loopdev.c
@@ -291,9 +291,8 @@ int loopcxt_get_fd(struct loopdev_cxt *lc)
if (lc->fd < 0) {
lc->mode = lc->flags & LOOPDEV_FL_RDWR ? O_RDWR : O_RDONLY;
lc->fd = open(lc->device, lc->mode | O_CLOEXEC);
- DBG(lc, loopdev_debug("open %s [%s]: %s", lc->device,
- lc->flags & LOOPDEV_FL_RDWR ? "rw" : "ro",
- lc->fd < 0 ? "failed" : "ok"));
+ DBG(lc, loopdev_debug("open %s [%s]: %m", lc->device,
+ lc->flags & LOOPDEV_FL_RDWR ? "rw" : "ro"));
}
return lc->fd;
}
@@ -1094,13 +1093,16 @@ static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd)
if (!lc->info.lo_offset && !lc->info.lo_sizelimit)
return 0;
- if (fstat(file_fd, &st))
+ if (fstat(file_fd, &st)) {
+ DBG(lc, loopdev_debug("failed to fstat backing file"));
return -errno;
-
+ }
if (S_ISBLK(st.st_mode)) {
if (blkdev_get_size(file_fd,
- (unsigned long long *) &expected_size))
+ (unsigned long long *) &expected_size)) {
+ DBG(lc, loopdev_debug("failed to determine device size"));
return -errno;
+ }
} else
expected_size = st.st_size;
@@ -1116,11 +1118,21 @@ static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd)
expected_size = lc->info.lo_sizelimit;
dev_fd = loopcxt_get_fd(lc);
- if (dev_fd < 0)
+ if (dev_fd < 0) {
+ DBG(lc, loopdev_debug("failed to get loop FD"));
return -errno;
+ }
- if (blkdev_get_size(dev_fd, (unsigned long long *) &size))
+ if (blkdev_get_size(dev_fd, (unsigned long long *) &size)) {
+ DBG(lc, loopdev_debug("failed to determine loopdev size"));
return -errno;
+ }
+
+ /* It's block device, so, align to 512-byte sectors */
+ if (expected_size % 512) {
+ DBG(lc, loopdev_debug("expected size misaligned to 512-byte sectors"));
+ expected_size = (expected_size >> 9) << 9;
+ }
if (expected_size != size) {
DBG(lc, loopdev_debug("warning: loopdev and expected "
@@ -1137,8 +1149,13 @@ static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd)
if (blkdev_get_size(dev_fd, (unsigned long long *) &size))
return -errno;
- if (expected_size != size)
- return -ERANGE;
+ if (expected_size != size) {
+ errno = ERANGE;
+ DBG(lc, loopdev_debug("failed to set loopdev size, "
+ "size: %ju, expected: %ju",
+ size, expected_size));
+ return -errno;
+ }
}
return 0;
@@ -1164,7 +1181,7 @@ static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd)
*/
int loopcxt_setup_device(struct loopdev_cxt *lc)
{
- int file_fd, dev_fd, mode = O_RDWR, rc = -1;
+ int file_fd, dev_fd, mode = O_RDWR, rc = -1, cnt = 0;
if (!lc || !*lc->device || !lc->filename)
return -EINVAL;
@@ -1204,7 +1221,19 @@ int loopcxt_setup_device(struct loopdev_cxt *lc)
lc->flags &= ~LOOPDEV_FL_RDONLY;
}
- dev_fd = loopcxt_get_fd(lc);
+ do {
+ errno = 0;
+ dev_fd = loopcxt_get_fd(lc);
+ if (dev_fd >= 0 || lc->control_ok == 0)
+ break;
+ if (errno != EACCES && errno != ENOENT)
+ break;
+ /* We have permissions to open /dev/loop-control, but open
+ * /dev/loopN failed with EACCES, it's probably because udevd
+ * does not applied chown yet. Let's wait a moment. */
+ usleep(25000);
+ } while (cnt++ < 16);
+
if (dev_fd < 0) {
rc = -errno;
goto err;
@@ -1234,7 +1263,6 @@ int loopcxt_setup_device(struct loopdev_cxt *lc)
goto err;
close(file_fd);
- file_fd = -1;
memset(&lc->info, 0, sizeof(lc->info));
lc->has_info = 0;
@@ -1286,6 +1314,37 @@ int loopcxt_delete_device(struct loopdev_cxt *lc)
return 0;
}
+int loopcxt_add_device(struct loopdev_cxt *lc)
+{
+ int rc = -EINVAL;
+ int ctl, nr = -1;
+ const char *p, *dev = loopcxt_get_device(lc);
+
+ if (!dev)
+ goto done;
+
+ if (!(lc->flags & LOOPDEV_FL_CONTROL)) {
+ rc = -ENOSYS;
+ goto done;
+ }
+
+ p = strrchr(dev, '/');
+ if (!p || (sscanf(p, "/loop%d", &nr) != 1 && sscanf(p, "/%d", &nr) != 1)
+ || nr < 0)
+ goto done;
+
+ ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
+ if (ctl >= 0) {
+ DBG(lc, loopdev_debug("add_device %d", nr));
+ rc = ioctl(ctl, LOOP_CTL_ADD, nr);
+ close(ctl);
+ }
+ lc->control_ok = rc >= 0 ? 1 : 0;
+done:
+ DBG(lc, loopdev_debug("add_device done [rc=%d]", rc));
+ return rc;
+}
+
/*
* Note that LOOP_CTL_GET_FREE ioctl is supported since kernel 3.1. In older
* kernels we have to check all loop devices to found unused one.
@@ -1309,6 +1368,7 @@ int loopcxt_find_unused(struct loopdev_cxt *lc)
rc = loopiter_set_device(lc, name);
}
+ lc->control_ok = ctl >= 0 && rc == 0 ? 1 : 0;
if (ctl >= 0)
close(ctl);
DBG(lc, loopdev_debug("find_unused by loop-control [rc=%d]", rc));