diff options
Diffstat (limited to 'lib/loopdev.c')
| -rw-r--r-- | lib/loopdev.c | 86 |
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)); |
