Hi tech@,

The below patch removes calls to realpath(3) when looking up a qcow2
base image. Previous thread:
https://marc.info/?t=161562496400002&r=1&w=2

In short, the calls were failing inside vmctl, because of unveil. The
other thread has alternative solutions but I think this is simplest.

I included a regression test demonstrating the vmctl bug, in case
there's interest. I tested vmd manually as described in the other
thread.

I also added a check in case dirname(3) fails --- I don't think it
currently can, but better safe than sorry, I figure. (Noticed by Dave
in the other thread.)

- James


diff --git a/regress/usr.sbin/Makefile b/regress/usr.sbin/Makefile
index 60e2178d3c7..146f9c9f322 100644
--- a/regress/usr.sbin/Makefile
+++ b/regress/usr.sbin/Makefile
@@ -15,6 +15,7 @@ SUBDIR += rpki-client
 SUBDIR += snmpd
 SUBDIR += switchd
 SUBDIR += syslogd
+SUBDIR += vmctl
 
 .if ${MACHINE} == "amd64" || ${MACHINE} == "i386"
 SUBDIR += vmd
diff --git a/regress/usr.sbin/vmctl/Makefile b/regress/usr.sbin/vmctl/Makefile
new file mode 100644
index 00000000000..8fa87f0f6f0
--- /dev/null
+++ b/regress/usr.sbin/vmctl/Makefile
@@ -0,0 +1,34 @@
+# $OpenBSD$
+
+REGRESS_TARGETS = run-regress-convert-with-base-path
+
+run-regress-convert-with-base-path:
+       # non-relative base path
+       rm -f *.qcow2
+       vmctl create -s 1m base.qcow2
+       vmctl create -b ${PWD}/base.qcow2 source.qcow2
+       vmctl create -i source.qcow2 dest.qcow2
+
+       # relative base path; two base images
+       rm -f *.qcow2
+       vmctl create -s 1m base0.qcow2
+       vmctl create -b base0.qcow2 base1.qcow2
+       vmctl create -b base1.qcow2 source.qcow2
+       vmctl create -i source.qcow2 dest.qcow2
+
+       # copy from a different directory
+       rm -rf dir *.qcow2
+       vmctl create -s 1m base.qcow2
+       vmctl create -b base.qcow2 source.qcow2
+       mkdir dir
+       cd dir; vmctl create -i ../source.qcow2 dest.qcow2
+
+       # base accessed through symlink
+       rm -rf dir sym *.qcow2
+       mkdir dir
+       cd dir; vmctl create -s 1m base.qcow2
+       cd dir; vmctl create -b base.qcow2 source.qcow2
+       ln -s dir sym
+       vmctl create -i sym/source.qcow2 dest.qcow2
+
+.include <bsd.regress.mk>
diff --git a/usr.sbin/vmd/vioqcow2.c b/usr.sbin/vmd/vioqcow2.c
index 34d0f116cc4..be8609f1644 100644
--- a/usr.sbin/vmd/vioqcow2.c
+++ b/usr.sbin/vmd/vioqcow2.c
@@ -145,8 +145,8 @@ virtio_qcow2_init(struct virtio_backing *file, off_t *szp, 
int *fd, size_t nfd)
 ssize_t
 virtio_qcow2_get_base(int fd, char *path, size_t npath, const char *dpath)
 {
+       char pathbuf[PATH_MAX];
        char dpathbuf[PATH_MAX];
-       char expanded[PATH_MAX];
        struct qcheader header;
        uint64_t backingoff;
        uint32_t backingsz;
@@ -180,27 +180,23 @@ virtio_qcow2_get_base(int fd, char *path, size_t npath, 
const char *dpath)
         * rather than relative to the directory vmd happens to be running in,
         * since this is the only userful interpretation.
         */
-       if (path[0] == '/') {
-               if (realpath(path, expanded) == NULL ||
-                   strlcpy(path, expanded, npath) >= npath) {
-                       log_warnx("unable to resolve %s", path);
+       if (path[0] != '/') {
+               if (strlcpy(pathbuf, path, sizeof(pathbuf)) >=
+                   sizeof(pathbuf)) {
+                       log_warnx("path too long: %s", path);
                        return -1;
                }
-       } else {
                if (strlcpy(dpathbuf, dpath, sizeof(dpathbuf)) >=
                    sizeof(dpathbuf)) {
                        log_warnx("path too long: %s", dpath);
                        return -1;
                }
-               s = dirname(dpathbuf);
-               if (snprintf(expanded, sizeof(expanded),
-                   "%s/%s", s, path) >= (int)sizeof(expanded)) {
-                       log_warnx("path too long: %s/%s", s, path);
+               if ((s = dirname(dpathbuf)) == NULL) {
+                       log_warn("dirname");
                        return -1;
                }
-               if (npath < PATH_MAX ||
-                   realpath(expanded, path) == NULL) {
-                       log_warnx("unable to resolve %s", path);
+               if (snprintf(path, npath, "%s/%s", s, pathbuf) >= (int)npath) {
+                       log_warnx("path too long: %s/%s", s, path);
                        return -1;
                }
        }

Reply via email to