lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Wed, 24 Aug 2016 18:00:51 -0400
From:   "Daniel P. Berrange" <berrange@...hat.com>
To:     Namhyung Kim <namhyung@...nel.org>
Cc:     virtio-dev@...ts.oasis-open.org, kvm@...r.kernel.org,
        qemu-devel@...gnu.org, virtualization@...ts.linux-foundation.org,
        LKML <linux-kernel@...r.kernel.org>,
        Paolo Bonzini <pbonzini@...hat.com>,
        Radim Krčmář <rkrcmar@...hat.com>,
        "Michael S. Tsirkin" <mst@...hat.com>,
        Anthony Liguori <aliguori@...zon.com>,
        Anton Vorontsov <anton@...msg.org>,
        Colin Cross <ccross@...roid.com>,
        Kees Cook <keescook@...omium.org>,
        Tony Luck <tony.luck@...el.com>,
        Steven Rostedt <rostedt@...dmis.org>,
        Ingo Molnar <mingo@...nel.org>,
        Minchan Kim <minchan@...nel.org>
Subject: Re: [PATCH 2/3] qemu: Implement virtio-pstore device


> diff --git a/hw/virtio/virtio-pstore.c b/hw/virtio/virtio-pstore.c
> new file mode 100644
> index 0000000..b8fb4be
> --- /dev/null
> +++ b/hw/virtio/virtio-pstore.c
> @@ -0,0 +1,699 @@
> +/*
> + * Virtio Pstore Device
> + *
> + * Copyright (C) 2016  LG Electronics
> + *
> + * Authors:
> + *  Namhyung Kim  <namhyung@...il.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#include <stdio.h>
> +
> +#include "qemu/osdep.h"
> +#include "qemu/iov.h"
> +#include "qemu-common.h"
> +#include "qemu/cutils.h"
> +#include "qemu/error-report.h"
> +#include "sysemu/kvm.h"
> +#include "qapi/visitor.h"
> +#include "qapi-event.h"
> +#include "io/channel-util.h"
> +#include "trace.h"
> +
> +#include "hw/virtio/virtio.h"
> +#include "hw/virtio/virtio-bus.h"
> +#include "hw/virtio/virtio-access.h"
> +#include "hw/virtio/virtio-pstore.h"
> +
> +#define PSTORE_DEFAULT_BUFSIZE   (16 * 1024)
> +#define PSTORE_DEFAULT_FILE_MAX  5
> +
> +/* the index should match to the type value */
> +static const char *virtio_pstore_file_prefix[] = {
> +    "unknown-",		/* VIRTIO_PSTORE_TYPE_UNKNOWN */
> +    "dmesg-",		/* VIRTIO_PSTORE_TYPE_DMESG */
> +};
> +
> +static char *virtio_pstore_to_filename(VirtIOPstore *s,
> +                                       struct virtio_pstore_req *req)
> +{
> +    const char *basename;
> +    unsigned long long id;
> +    unsigned int type = le16_to_cpu(req->type);
> +    unsigned int flags = le32_to_cpu(req->flags);
> +
> +    if (type < ARRAY_SIZE(virtio_pstore_file_prefix)) {
> +        basename = virtio_pstore_file_prefix[type];
> +    } else {
> +        basename = "unknown-";
> +    }
> +
> +    id = s->id++;
> +    return g_strdup_printf("%s/%s%llu%s", s->directory, basename, id,
> +                            flags & VIRTIO_PSTORE_FL_COMPRESSED ? ".enc.z" : "");
> +}
> +
> +static char *virtio_pstore_from_filename(VirtIOPstore *s, char *name,
> +                                         struct virtio_pstore_fileinfo *info)
> +{
> +    char *filename;
> +    unsigned int idx;
> +
> +    filename = g_strdup_printf("%s/%s", s->directory, name);
> +    if (filename == NULL)
> +        return NULL;
> +
> +    for (idx = 0; idx < ARRAY_SIZE(virtio_pstore_file_prefix); idx++) {
> +        if (g_str_has_prefix(name, virtio_pstore_file_prefix[idx])) {
> +            info->type = idx;
> +            name += strlen(virtio_pstore_file_prefix[idx]);
> +            break;
> +        }
> +    }
> +
> +    if (idx == ARRAY_SIZE(virtio_pstore_file_prefix)) {
> +        g_free(filename);
> +        return NULL;
> +    }
> +
> +    qemu_strtoull(name, NULL, 0, &info->id);
> +
> +    info->flags = 0;
> +    if (g_str_has_suffix(name, ".enc.z")) {
> +        info->flags |= VIRTIO_PSTORE_FL_COMPRESSED;
> +    }
> +
> +    return filename;
> +}
> +
> +static int prefix_idx;
> +static int prefix_count;
> +static int prefix_len;
> +
> +static int filter_pstore(const struct dirent *de)
> +{
> +    int i;
> +
> +    for (i = 0; i < prefix_count; i++) {
> +        const char *prefix = virtio_pstore_file_prefix[prefix_idx + i];
> +
> +        if (g_str_has_prefix(de->d_name, prefix)) {
> +            return 1;
> +        }
> +    }
> +    return 0;
> +}
> +
> +static int sort_pstore(const struct dirent **a, const struct dirent **b)
> +{
> +    uint64_t id_a, id_b;
> +
> +    qemu_strtoull((*a)->d_name + prefix_len, NULL, 0, &id_a);
> +    qemu_strtoull((*b)->d_name + prefix_len, NULL, 0, &id_b);
> +
> +    return id_a - id_b;
> +}
> +
> +static int rotate_pstore_file(VirtIOPstore *s, unsigned short type)

AFAIK you're not actually doing file rotation here - that implies a
fixed base filename, with .0, .1, .2, etc suffixes where we rename
files each time. It looks like you are assuming separate filenames,
and are merely deleting the oldest each time.

> +{
> +    int ret = 0;
> +    int i, num;
> +    char *filename;
> +    struct dirent **files;
> +
> +    if (type >= ARRAY_SIZE(virtio_pstore_file_prefix)) {
> +        type = VIRTIO_PSTORE_TYPE_UNKNOWN;
> +    }
> +
> +    prefix_idx = type;
> +    prefix_len = strlen(virtio_pstore_file_prefix[type]);
> +    prefix_count = 1;  /* only scan current type */
> +
> +    /* delete the oldest file in the same type */
> +    num = scandir(s->directory, &files, filter_pstore, sort_pstore);
> +    if (num < 0)
> +        return num;
> +    if (num < (int)s->file_max)
> +        goto out;
> +
> +    filename = g_strdup_printf("%s/%s", s->directory, files[0]->d_name);
> +    if (filename == NULL) {
> +        ret = -1;
> +        goto out;
> +    }
> +
> +    ret = unlink(filename);





> +static gboolean pstore_async_read_fn(QIOChannel *ioc, GIOCondition condition,
> +                                     gpointer data)
> +{
> +    struct pstore_read_arg *rarg = data;
> +    struct virtio_pstore_fileinfo *info = &rarg->info;
> +    VirtIOPstore *vps = rarg->vps;
> +    VirtQueueElement *elem = rarg->elem;
> +    struct virtio_pstore_res res;
> +    size_t offset = sizeof(res) + sizeof(*info);
> +    struct iovec *sg = elem->in_sg;
> +    unsigned int sg_num = elem->in_num;
> +    Error *err = NULL;
> +    ssize_t len;
> +    int ret;
> +
> +    /* skip res and fileinfo */
> +    iov_discard_front(&sg, &sg_num, sizeof(res) + sizeof(*info));
> +
> +    len = qio_channel_readv(rarg->ioc, sg, sg_num, &err);
> +    if (len < 0) {
> +        if (errno == EAGAIN) {
> +            len = 0;
> +        }
> +        ret = -1;
> +    } else {
> +        info->len = cpu_to_le32(len);
> +        ret = 0;
> +    }
> +
> +    res.cmd  = cpu_to_le16(VIRTIO_PSTORE_CMD_READ);
> +    res.type = cpu_to_le16(VIRTIO_PSTORE_TYPE_UNKNOWN);
> +    res.ret  = cpu_to_le32(ret);
> +
> +    /* now copy res and fileinfo */
> +    iov_from_buf(elem->in_sg, elem->in_num, 0, &res, sizeof(res));
> +    iov_from_buf(elem->in_sg, elem->in_num, sizeof(res), info, sizeof(*info));
> +
> +    len += offset;
> +    virtqueue_push(vps->rvq, elem, len);
> +    virtio_notify(VIRTIO_DEVICE(vps), vps->rvq);
> +
> +    return G_SOURCE_REMOVE;

G_SOURCE_REMOVE was added in glib 2.32, but QEMU only permits
stuff that is present in 2.22. Just use "FALSE" instead.

> +static ssize_t virtio_pstore_do_read(VirtIOPstore *s, VirtQueueElement *elem)
> +{
> +    char *filename = NULL;
> +    int fd, idx;
> +    struct stat stbuf;
> +    struct pstore_read_arg *rarg = NULL;
> +    Error *err = NULL;
> +    int ret = -1;
> +
> +    if (s->file_idx >= s->num_file) {
> +        return 0;
> +    }
> +
> +    rarg = g_malloc(sizeof(*rarg));
> +    if (rarg == NULL) {
> +        return -1;
> +    }
> +
> +    idx = s->file_idx++;
> +    filename = virtio_pstore_from_filename(s, s->files[idx]->d_name,
> +                                           &rarg->info);
> +    if (filename == NULL) {
> +        goto out;
> +    }
> +
> +    fd = open(filename, O_RDONLY);
> +    if (fd < 0) {
> +        error_report("cannot open %s", filename);
> +        goto out;
> +    }
> +
> +    if (fstat(fd, &stbuf) < 0) {
> +        goto out;
> +    }
> +
> +    rarg->vps            = s;
> +    rarg->elem           = elem;
> +    rarg->info.id        = cpu_to_le64(rarg->info.id);
> +    rarg->info.type      = cpu_to_le16(rarg->info.type);
> +    rarg->info.flags     = cpu_to_le32(rarg->info.flags);
> +    rarg->info.time_sec  = cpu_to_le64(stbuf.st_ctim.tv_sec);
> +    rarg->info.time_nsec = cpu_to_le32(stbuf.st_ctim.tv_nsec);
> +
> +    rarg->ioc = qio_channel_new_fd(fd, &err);

You should just use qio_channel_open_path() and avoid the earlier
call to open()

> +    if (err) {
> +        error_reportf_err(err, "cannot create io channel: ");
> +        goto out;
> +    }
> +
> +    qio_channel_set_blocking(rarg->ioc, false, &err);
> +    qio_channel_add_watch(rarg->ioc, G_IO_IN, pstore_async_read_fn, rarg,
> +                          free_rarg_fn);
> +    g_free(filename);
> +    return 1;
> +
> +out:
> +    g_free(filename);
> +    g_free(rarg);
> +
> +    return ret;
> +}


> +static ssize_t virtio_pstore_do_write(VirtIOPstore *s, VirtQueueElement *elem,
> +                                      struct virtio_pstore_req *req)
> +{
> +    unsigned short type = le16_to_cpu(req->type);
> +    char *filename = NULL;
> +    int fd;
> +    int flags = O_WRONLY | O_CREAT | O_TRUNC;
> +    struct pstore_write_arg *warg = NULL;
> +    Error *err = NULL;
> +    int ret = -1;
> +
> +    /* do not keep same type of files more than 'file-max' */
> +    rotate_pstore_file(s, type);
> +
> +    filename = virtio_pstore_to_filename(s, req);
> +    if (filename == NULL) {
> +        return -1;
> +    }
> +
> +    warg = g_malloc(sizeof(*warg));
> +    if (warg == NULL) {
> +        goto out;
> +    }
> +
> +    fd = open(filename, flags, 0644);
> +    if (fd < 0) {
> +        error_report("cannot open %s", filename);
> +        ret = fd;
> +        goto out;
> +    }
> +
> +    warg->vps            = s;
> +    warg->elem           = elem;
> +    warg->req            = req;
> +
> +    warg->ioc = qio_channel_new_fd(fd, &err);

Same point about using new_path() instead of new_fd()

> +    if (err) {
> +        error_reportf_err(err, "cannot create io channel: ");
> +        goto out;
> +    }
> +
> +    qio_channel_set_blocking(warg->ioc, false, &err);
> +    qio_channel_add_watch(warg->ioc, G_IO_OUT, pstore_async_write_fn, warg,
> +                          free_warg_fn);
> +    g_free(filename);
> +    return 1;
> +
> +out:
> +    g_free(filename);
> +    g_free(warg);
> +    return ret;
> +}
> +
> +static void virtio_pstore_handle_io(VirtIODevice *vdev, VirtQueue *vq)
> +{
> +    VirtIOPstore *s = VIRTIO_PSTORE(vdev);
> +    VirtQueueElement *elem;
> +    struct virtio_pstore_req req;
> +    struct virtio_pstore_res res;
> +    ssize_t len = 0;
> +    int ret;
> +
> +    for (;;) {
> +        elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
> +        if (!elem) {
> +            return;
> +        }
> +
> +        if (elem->out_num < 1 || elem->in_num < 1) {
> +            error_report("request or response buffer is missing");
> +            exit(1);
> +        }
> +
> +        if (elem->out_num > 2 || elem->in_num > 3) {
> +            error_report("invalid number of input/output buffer");
> +            exit(1);
> +        }
> +
> +        len = iov_to_buf(elem->out_sg, elem->out_num, 0, &req, sizeof(req));
> +        if (len != (ssize_t)sizeof(req)) {
> +            error_report("invalid request size: %ld", (long)len);
> +            exit(1);
> +        }
> +        res.cmd  = req.cmd;
> +        res.type = req.type;
> +
> +        switch (le16_to_cpu(req.cmd)) {
> +        case VIRTIO_PSTORE_CMD_OPEN:
> +            ret = virtio_pstore_do_open(s);
> +            break;
> +        case VIRTIO_PSTORE_CMD_CLOSE:
> +            ret = virtio_pstore_do_close(s);
> +            break;
> +        case VIRTIO_PSTORE_CMD_ERASE:
> +            ret = virtio_pstore_do_erase(s, &req);
> +            break;
> +        case VIRTIO_PSTORE_CMD_READ:
> +            ret = virtio_pstore_do_read(s, elem);
> +            if (ret == 1) {
> +                /* async channel io */
> +                continue;
> +            }
> +            break;
> +        case VIRTIO_PSTORE_CMD_WRITE:
> +            ret = virtio_pstore_do_write(s, elem, &req);
> +            if (ret == 1) {
> +                /* async channel io */
> +                continue;
> +            }
> +            break;
> +        default:
> +            ret = -1;
> +            break;
> +        }
> +
> +        res.ret = ret;
> +
> +        iov_from_buf(elem->in_sg, elem->in_num, 0, &res, sizeof(res));
> +        virtqueue_push(vq, elem, sizeof(res) + len);
> +
> +        virtio_notify(vdev, vq);
> +        g_free(elem);
> +
> +        if (ret < 0) {
> +            return;
> +        }
> +    }
> +}

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ