summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorori <ori@cvs.openbsd.org>2018-11-24 04:51:56 +0000
committerori <ori@cvs.openbsd.org>2018-11-24 04:51:56 +0000
commita0d4f4c72f83027e1d3ecaa3a3e89ab96177fe77 (patch)
treec7898d43d31da5af09808253536e197e030696a4 /usr.sbin
parente225a67e93a64bc955790a6d982d87ffcfa255c1 (diff)
Improve error handling and logging in qcow2
This turns most warn + returns that should never happen into hard failures, and improves the user directed error messages. ok @mlarkin, @reyk
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/vmd/config.c14
-rw-r--r--usr.sbin/vmd/vioqcow2.c186
2 files changed, 76 insertions, 124 deletions
diff --git a/usr.sbin/vmd/config.c b/usr.sbin/vmd/config.c
index 20a16f85442..5a3be110df1 100644
--- a/usr.sbin/vmd/config.c
+++ b/usr.sbin/vmd/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.55 2018/11/21 12:31:47 reyk Exp $ */
+/* $OpenBSD: config.c,v 1.56 2018/11/24 04:51:55 ori Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -311,8 +311,7 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
*/
if (kernfd == -1 && !vmboot &&
(kernfd = open(VM_DEFAULT_BIOS, O_RDONLY)) == -1) {
- log_warn("%s: can't open %s", __func__,
- VM_DEFAULT_BIOS);
+ log_warn("can't open %s", VM_DEFAULT_BIOS);
errno = VMD_BIOS_MISSING;
goto fail;
}
@@ -332,8 +331,7 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
/* Stat cdrom to ensure it is a regular file */
if ((cdromfd =
open(vcp->vcp_cdrom, O_RDONLY)) == -1) {
- log_warn("%s: can't open cdrom %s", __func__,
- vcp->vcp_cdrom);
+ log_warn("can't open cdrom %s", vcp->vcp_cdrom);
errno = VMD_CDROM_MISSING;
goto fail;
}
@@ -352,14 +350,14 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
for (i = 0 ; i < vcp->vcp_ndisks; i++) {
if (strlcpy(path, vcp->vcp_disks[i], sizeof(path))
>= sizeof(path))
- log_warnx("%s, disk path too long", __func__);
+ log_warnx("disk path %s too long", vcp->vcp_disks[i]);
memset(vmc->vmc_diskbases, 0, sizeof(vmc->vmc_diskbases));
oflags = O_RDWR|O_EXLOCK|O_NONBLOCK;
aflags = R_OK|W_OK;
for (j = 0; j < VM_MAX_BASE_PER_DISK; j++) {
/* Stat disk[i] to ensure it is a regular file */
if ((diskfds[i][j] = open(path, oflags)) == -1) {
- log_warn("%s: can't open disk %s", __func__,
+ log_warn("can't open disk %s",
vcp->vcp_disks[i]);
errno = VMD_DISK_MISSING;
goto fail;
@@ -514,7 +512,7 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
fail:
saved_errno = errno;
- log_warnx("%s: failed to start vm %s", __func__, vcp->vcp_name);
+ log_warnx("failed to start vm %s", vcp->vcp_name);
if (kernfd != -1)
close(kernfd);
diff --git a/usr.sbin/vmd/vioqcow2.c b/usr.sbin/vmd/vioqcow2.c
index 28b087df39a..be6964bb5bd 100644
--- a/usr.sbin/vmd/vioqcow2.c
+++ b/usr.sbin/vmd/vioqcow2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vioqcow2.c,v 1.10 2018/10/24 05:19:03 ori Exp $ */
+/* $OpenBSD: vioqcow2.c,v 1.11 2018/11/24 04:51:55 ori Exp $ */
/*
* Copyright (c) 2018 Ori Bernstein <ori@eigenstate.org>
@@ -102,9 +102,9 @@ struct qcdisk {
extern char *__progname;
static off_t xlate(struct qcdisk *, off_t, int *);
-static int copy_cluster(struct qcdisk *, struct qcdisk *, off_t, off_t);
+static void copy_cluster(struct qcdisk *, struct qcdisk *, off_t, off_t);
+static void inc_refs(struct qcdisk *, off_t, int);
static off_t mkcluster(struct qcdisk *, struct qcdisk *, off_t, off_t);
-static int inc_refs(struct qcdisk *, off_t, int);
static int qc2_open(struct qcdisk *, int *, size_t);
static ssize_t qc2_pread(void *, char *, size_t, off_t);
static ssize_t qc2_pwrite(void *, char *, size_t, off_t);
@@ -126,7 +126,7 @@ virtio_init_qcow2(struct virtio_backing *file, off_t *szp, int *fd, size_t nfd)
if (diskp == NULL)
return -1;
if (qc2_open(diskp, fd, nfd) == -1) {
- log_warnx("%s: could not open qcow2 disk", __func__);
+ log_warnx("could not open qcow2 disk");
return -1;
}
file->p = diskp;
@@ -137,6 +137,10 @@ virtio_init_qcow2(struct virtio_backing *file, off_t *szp, int *fd, size_t nfd)
return 0;
}
+/*
+ * Return the path to the base image given a disk image.
+ * Called from vmctl.
+ */
ssize_t
virtio_qcow2_get_base(int fd, char *path, size_t npath, const char *dpath)
{
@@ -147,11 +151,11 @@ virtio_qcow2_get_base(int fd, char *path, size_t npath, const char *dpath)
char *s = NULL;
if (pread(fd, &header, sizeof(header), 0) != sizeof(header)) {
- log_warnx("%s: short read on header", __func__);
+ log_warnx("short read on header");
return -1;
}
if (strncmp(header.magic, VM_MAGIC_QCOW, strlen(VM_MAGIC_QCOW)) != 0) {
- log_warn("%s: invalid magic numbers", __func__);
+ log_warnx("invalid magic numbers");
return -1;
}
backingoff = be64toh(header.backingoff);
@@ -160,12 +164,11 @@ virtio_qcow2_get_base(int fd, char *path, size_t npath, const char *dpath)
return 0;
if (backingsz >= npath - 1) {
- log_warn("%s: snapshot path too long", __func__);
+ log_warnx("snapshot path too long");
return -1;
}
if (pread(fd, path, backingsz, backingoff) != backingsz) {
- log_warnx("%s: could not read snapshot base name",
- __func__);
+ log_warnx("could not read snapshot base name");
return -1;
}
path[backingsz] = '\0';
@@ -178,20 +181,19 @@ virtio_qcow2_get_base(int fd, char *path, size_t npath, const char *dpath)
if (path[0] == '/') {
if (realpath(path, expanded) == NULL ||
strlcpy(path, expanded, npath) >= npath) {
- log_warn("unable to resolve %s", path);
+ log_warnx("unable to resolve %s", path);
return -1;
}
} else {
s = dirname(dpath);
if (snprintf(expanded, sizeof(expanded),
"%s/%s", s, path) >= (int)sizeof(expanded)) {
- log_warn("path too long: %s/%s",
- s, path);
+ log_warnx("path too long: %s/%s", s, path);
return -1;
}
if (npath < PATH_MAX ||
realpath(expanded, path) == NULL) {
- log_warn("unable to resolve %s", path);
+ log_warnx("unable to resolve %s", path);
return -1;
}
}
@@ -216,15 +218,10 @@ qc2_open(struct qcdisk *disk, int *fds, size_t nfd)
disk->base = NULL;
disk->l1 = NULL;
- if (pread(fd, &header, sizeof(header), 0) != sizeof(header)) {
- log_warn("%s: short read on header", __func__);
- goto error;
- }
- if (strncmp(header.magic,
- VM_MAGIC_QCOW, strlen(VM_MAGIC_QCOW)) != 0) {
- log_warn("%s: invalid magic numbers", __func__);
- goto error;
- }
+ if (pread(fd, &header, sizeof(header), 0) != sizeof(header))
+ fatalx("short read on header");
+ if (strncmp(header.magic, VM_MAGIC_QCOW, strlen(VM_MAGIC_QCOW)) != 0)
+ fatalx("invalid magic numbers");
disk->clustersz = (1ull << be32toh(header.clustershift));
disk->disksz = be64toh(header.disksz);
@@ -249,79 +246,59 @@ qc2_open(struct qcdisk *disk, int *fds, size_t nfd)
/*
* We only know about the dirty or corrupt bits here.
*/
- if (disk->incompatfeatures & ~(QCOW2_DIRTY|QCOW2_CORRUPT)) {
- log_warnx("%s: unsupported features %llx", __func__,
+ if (disk->incompatfeatures & ~(QCOW2_DIRTY|QCOW2_CORRUPT))
+ fatalx("unsupported features %llx",
disk->incompatfeatures & ~(QCOW2_DIRTY|QCOW2_CORRUPT));
- goto error;
- }
+ if (be32toh(header.reforder) != 4)
+ fatalx("unsupported refcount size\n");
disk->l1 = calloc(disk->l1sz, sizeof(*disk->l1));
if (!disk->l1)
- goto error;
+ fatal("%s: could not allocate l1 table", __func__);
if (pread(disk->fd, disk->l1, 8 * disk->l1sz, disk->l1off)
- != 8 * disk->l1sz) {
- log_warn("%s: unable to read qcow2 L1 table", __func__);
- goto error;
- }
+ != 8 * disk->l1sz)
+ fatalx("%s: unable to read qcow2 L1 table", __func__);
for (i = 0; i < disk->l1sz; i++)
disk->l1[i] = be64toh(disk->l1[i]);
version = be32toh(header.version);
- if (version != 2 && version != 3) {
- log_warn("%s: unknown qcow2 version %d", __func__, version);
- goto error;
- }
+ if (version != 2 && version != 3)
+ fatalx("%s: unknown qcow2 version %d", __func__, version);
backingoff = be64toh(header.backingoff);
backingsz = be32toh(header.backingsz);
if (backingsz != 0) {
if (backingsz >= sizeof(basepath) - 1) {
- log_warn("%s: snapshot path too long", __func__);
- goto error;
+ fatalx("%s: snapshot path too long", __func__);
}
if (pread(fd, basepath, backingsz, backingoff) != backingsz) {
- log_warn("%s: could not read snapshot base name",
+ fatalx("%s: could not read snapshot base name",
__func__);
- goto error;
}
basepath[backingsz] = 0;
if (nfd <= 1) {
- log_warnx("%s: missing base image %s", __func__,
+ fatalx("%s: missing base image %s", __func__,
basepath);
- goto error;
}
disk->base = calloc(1, sizeof(struct qcdisk));
if (!disk->base)
- goto error;
- if (qc2_open(disk->base, fds + 1, nfd - 1) == -1) {
- log_warn("%s: could not open %s", basepath, __func__);
- goto error;
- }
- if (disk->base->clustersz != disk->clustersz) {
- log_warn("%s: all disks must share clustersize",
+ fatal("%s: could not open %s", __func__, basepath);
+ if (qc2_open(disk->base, fds + 1, nfd - 1) == -1)
+ fatalx("%s: could not open %s", __func__, basepath);
+ if (disk->base->clustersz != disk->clustersz)
+ fatalx("%s: all disk parts must share clustersize",
__func__);
- goto error;
- }
- }
- if (fstat(fd, &st) == -1) {
- log_warn("%s: unable to stat disk", __func__);
- goto error;
}
+ if (fstat(fd, &st) == -1)
+ fatal("%s: unable to stat disk", __func__);
disk->end = st.st_size;
log_debug("%s: qcow2 disk version %d size %lld end %lld snap %d",
- __func__,
- version,
- disk->disksz,
- disk->end,
- disk->nsnap);
+ __func__, version, disk->disksz, disk->end, disk->nsnap);
return 0;
-error:
- qc2_close(disk, 0);
- return -1;
}
static ssize_t
@@ -345,7 +322,7 @@ qc2_pread(void *p, char *buf, size_t len, off_t off)
/* Break out into chunks. This handles
* three cases:
*
- * |----+====|========|====+ |
+ * |----+====|========|====+-----|
*
* Either we are at the start of the read,
* and the cluster has some leading bytes.
@@ -418,6 +395,8 @@ qc2_pwrite(void *p, char *buf, size_t len, off_t off)
phys_off = mkcluster(disk, d, off, phys_off);
if (phys_off == -1)
return -1;
+ if (phys_off < disk->clustersz)
+ fatalx("%s: writing reserved cluster", __func__);
if (pwrite(disk->fd, buf, sz, phys_off) != sz)
return -1;
off += sz;
@@ -487,10 +466,8 @@ xlate(struct qcdisk *disk, off_t off, int *inplace)
*/
if (inplace)
*inplace = !!(cluster & QCOW2_INPLACE);
- if (cluster & QCOW2_COMPRESSED) {
- log_warn("%s: compressed clusters unsupported", __func__);
- goto err;
- }
+ if (cluster & QCOW2_COMPRESSED)
+ fatalx("%s: compressed clusters unsupported", __func__);
pthread_rwlock_unlock(&disk->lock);
clusteroff = 0;
cluster &= ~QCOW2_INPLACE;
@@ -525,13 +502,8 @@ mkcluster(struct qcdisk *disk, struct qcdisk *base, off_t off, off_t src_phys)
l2sz = disk->clustersz / 8;
l1off = off / (disk->clustersz * l2sz);
if (l1off >= disk->l1sz)
- goto fail;
+ fatalx("l1 offset outside disk");
- /*
- * Align disk to cluster size, for ftruncate: Not strictly
- * required, but it easier to eyeball buggy write offsets,
- * and helps performance a bit.
- */
disk->end = (disk->end + disk->clustersz - 1) & ~(disk->clustersz - 1);
l2tab = disk->l1[l1off];
@@ -541,75 +513,63 @@ mkcluster(struct qcdisk *disk, struct qcdisk *base, off_t off, off_t src_phys)
orig = l2tab & ~QCOW2_INPLACE;
l2tab = disk->end;
disk->end += disk->clustersz;
- if (ftruncate(disk->fd, disk->end) == -1) {
- perror("ftruncate");
- goto fail;
- }
+ if (ftruncate(disk->fd, disk->end) == -1)
+ fatal("%s: ftruncate failed", __func__);
/*
* If we translated, found a L2 entry, but it needed to
* be copied, copy it.
*/
- if (orig != 0 && copy_cluster(disk, disk, l2tab, orig) == -1) {
- perror("move cluster");
- goto fail;
- }
+ if (orig != 0)
+ copy_cluster(disk, disk, l2tab, orig);
/* Update l1 -- we flush it later */
disk->l1[l1off] = l2tab | QCOW2_INPLACE;
- if (inc_refs(disk, l2tab, 1) == -1) {
- perror("refs");
- goto fail;
- }
+ inc_refs(disk, l2tab, 1);
}
l2tab &= ~QCOW2_INPLACE;
/* Grow the disk */
if (ftruncate(disk->fd, disk->end + disk->clustersz) < 0)
- goto fail;
+ fatalx("%s: could not grow disk", __func__);
if (src_phys > 0)
- if (copy_cluster(disk, base, disk->end, src_phys) == -1)
- goto fail;
+ copy_cluster(disk, base, disk->end, src_phys);
cluster = disk->end;
disk->end += disk->clustersz;
buf = htobe64(cluster | QCOW2_INPLACE);
if (pwrite(disk->fd, &buf, sizeof(buf), l2tab + l2off * 8) != 8)
- goto fail;
+ fatalx("%s: could not write cluster", __func__);
/* TODO: lazily sync: currently VMD doesn't close things */
buf = htobe64(disk->l1[l1off]);
if (pwrite(disk->fd, &buf, sizeof(buf), disk->l1off + 8 * l1off) != 8)
- goto fail;
- if (inc_refs(disk, cluster, 1) == -1)
- goto fail;
+ fatalx("%s: could not write l1", __func__);
+ inc_refs(disk, cluster, 1);
pthread_rwlock_unlock(&disk->lock);
clusteroff = off % disk->clustersz;
+ if (cluster + clusteroff < disk->clustersz)
+ fatalx("write would clobber header");
return cluster + clusteroff;
-
-fail:
- pthread_rwlock_unlock(&disk->lock);
- return -1;
}
/* Copies a cluster containing src to dst. Src and dst need not be aligned. */
-static int
+static void
copy_cluster(struct qcdisk *disk, struct qcdisk *base, off_t dst, off_t src)
{
char *scratch;
scratch = alloca(disk->clustersz);
if (!scratch)
- err(1, "out of memory");
+ fatal("out of memory");
src &= ~(disk->clustersz - 1);
dst &= ~(disk->clustersz - 1);
if (pread(base->fd, scratch, disk->clustersz, src) == -1)
- return -1;
+ fatal("%s: could not read cluster", __func__);
if (pwrite(disk->fd, scratch, disk->clustersz, dst) == -1)
- return -1;
- return 0;
+ fatal("%s: could not write cluster", __func__);
}
-static int
+static void
inc_refs(struct qcdisk *disk, off_t off, int newcluster)
{
off_t l1off, l1idx, l2idx, l2cluster;
@@ -623,34 +583,28 @@ inc_refs(struct qcdisk *disk, off_t off, int newcluster)
l2idx = (off / disk->clustersz) % nper;
l1off = disk->refoff + 8 * l1idx;
if (pread(disk->fd, &buf, sizeof(buf), l1off) != 8)
- return -1;
+ fatal("could not read refs");
l2cluster = be64toh(buf);
if (l2cluster == 0) {
l2cluster = disk->end;
disk->end += disk->clustersz;
- if (ftruncate(disk->fd, disk->end) < 0) {
- log_warn("%s: refs block grow fail", __func__);
- return -1;
- }
+ if (ftruncate(disk->fd, disk->end) < 0)
+ fatal("%s: failed to allocate ref block", __func__);
buf = htobe64(l2cluster);
- if (pwrite(disk->fd, &buf, sizeof(buf), l1off) != 8) {
- return -1;
- }
+ if (pwrite(disk->fd, &buf, sizeof(buf), l1off) != 8)
+ fatal("%s: failed to write ref block", __func__);
}
refs = 1;
if (!newcluster) {
if (pread(disk->fd, &refs, sizeof(refs),
l2cluster + 2 * l2idx) != 2)
- return -1;
+ fatal("could not read ref cluster");
refs = be16toh(refs) + 1;
}
refs = htobe16(refs);
- if (pwrite(disk->fd, &refs, sizeof(refs), l2cluster + 2 * l2idx) != 2) {
- log_warn("%s: could not write ref block", __func__);
- return -1;
- }
- return 0;
+ if (pwrite(disk->fd, &refs, sizeof(refs), l2cluster + 2 * l2idx) != 2)
+ fatal("%s: could not write ref block", __func__);
}