From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:35705) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZXWcY-00041N-4Q for qemu-devel@nongnu.org; Thu, 03 Sep 2015 11:40:16 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZXWcV-0007aw-Qw for qemu-devel@nongnu.org; Thu, 03 Sep 2015 11:40:14 -0400 Received: from mx1.redhat.com ([209.132.183.28]:41906) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZXWcV-0007aU-IQ for qemu-devel@nongnu.org; Thu, 03 Sep 2015 11:40:11 -0400 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (Postfix) with ESMTPS id 4A317A4A11 for ; Thu, 3 Sep 2015 15:40:11 +0000 (UTC) From: "Daniel P. Berrange" Date: Thu, 3 Sep 2015 16:38:53 +0100 Message-Id: <1441294768-8712-12-git-send-email-berrange@redhat.com> In-Reply-To: <1441294768-8712-1-git-send-email-berrange@redhat.com> References: <1441294768-8712-1-git-send-email-berrange@redhat.com> Subject: [Qemu-devel] [PATCH FYI 11/46] io: add QIOChannelFile class List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Juan Quintela , "Dr. David Alan Gilbert" , Gerd Hoffmann , Amit Shah , Paolo Bonzini Add a QIOChannel subclass that is capable of operating on things that are files, such as plain files, pipes, character/block devices, but notably not sockets. Signed-off-by: Daniel P. Berrange --- include/io/channel-file.h | 93 +++++++++++++++++++ io/Makefile.objs | 1 + io/channel-file.c | 209 +++++++++++++++++++++++++++++++++++++++++++ tests/.gitignore | 2 + tests/Makefile | 3 + tests/io-channel-helpers.c | 25 ++++++ tests/io-channel-helpers.h | 3 + tests/test-io-channel-file.c | 89 ++++++++++++++++++ 8 files changed, 425 insertions(+) create mode 100644 include/io/channel-file.h create mode 100644 io/channel-file.c create mode 100644 tests/test-io-channel-file.c diff --git a/include/io/channel-file.h b/include/io/channel-file.h new file mode 100644 index 0000000..308e6d4 --- /dev/null +++ b/include/io/channel-file.h @@ -0,0 +1,93 @@ +/* + * QEMU I/O channels files driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#ifndef QIO_CHANNEL_FILE_H__ +#define QIO_CHANNEL_FILE_H__ + +#include "io/channel.h" + +#define TYPE_QIO_CHANNEL_FILE "qio-channel-file" +#define QIO_CHANNEL_FILE(obj) \ + OBJECT_CHECK(QIOChannelFile, (obj), TYPE_QIO_CHANNEL_FILE) + +typedef struct QIOChannelFile QIOChannelFile; + +/** + * QIOChannelFile: + * + * The QIOChannelFile object provides a channel implementation + * that is able to perform I/O on block devices, character + * devices, FIFOs, pipes and plain files. While it is technically + * able to work on sockets too on the UNIX platform, this is not + * portable to Windows and lacks some extra sockets specific + * functionality. So the QIOChannelSocket object is recommended + * for that use case. + * + */ + +struct QIOChannelFile { + QIOChannel parent; + int fd; +}; + + +/** + * qio_channel_file_new_fd: + * @fd: the file descriptor + * + * Create a new IO channel object for a file represented + * by the @fd parameter. @fd can be associated with a + * block device, character device, fifo, pipe, or a + * regular file. For sockets, the QIOChannelSocket class + * should be used instead, as this provides greater + * functionality and cross platform portability. + * + * The channel will own the passed in file descriptor + * and will take responsibility for closing it, so the + * caller must not close it. If appropriate the caller + * should dup() its FD before opening the channel. + * + * Returns: the new channel object + */ +QIOChannelFile * +qio_channel_file_new_fd(int fd); + +/** + * qio_channel_file_new_path: + * @fd: the file descriptor + * @flags: the open flags (O_RDONLY|O_WRONLY|O_RDWR, etc) + * @mode: the file creation mode if O_WRONLY is set in @flags + * @errp: pointer to initialized error object + * + * Create a new IO channel object for a file represented + * by the @path parameter. @path can point to any + * type of file on which sequential I/O can be + * performed, whether it be a plain file, character + * device or block device. + * + * Returns: the new channel object + */ +QIOChannelFile * +qio_channel_file_new_path(const char *path, + int flags, + mode_t mode, + Error **errp); + +#endif /* QIO_CHANNEL_FILE_H__ */ diff --git a/io/Makefile.objs b/io/Makefile.objs index f12ec26..9eb0fd9 100644 --- a/io/Makefile.objs +++ b/io/Makefile.objs @@ -3,3 +3,4 @@ io-obj-y += task.o io-obj-y += channel.o io-obj-y += channel-watch.o io-obj-y += channel-socket.o +io-obj-y += channel-file.o diff --git a/io/channel-file.c b/io/channel-file.c new file mode 100644 index 0000000..4695e28 --- /dev/null +++ b/io/channel-file.c @@ -0,0 +1,209 @@ +/* + * QEMU I/O channels files driver + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include + +#include "io/channel-file.h" +#include "io/channel-watch.h" +#include "qemu/sockets.h" + +QIOChannelFile * +qio_channel_file_new_fd(int fd) +{ + QIOChannelFile *ioc; + + ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE)); + + ioc->fd = fd; + + return ioc; +} + + +QIOChannelFile * +qio_channel_file_new_path(const char *path, + int flags, + mode_t mode, + Error **errp) +{ + int fd; + + if (flags & O_WRONLY) { + fd = open(path, flags, mode); + } else { + fd = open(path, flags); + } + if (fd < 0) { + error_setg_errno(errp, errno, + "Unable to open %s", path); + return NULL; + } + + return qio_channel_file_new_fd(fd); +} + + +static void qio_channel_file_init(Object *obj) +{ + QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj); + ioc->fd = -1; +} + +static void qio_channel_file_finalize(Object *obj) +{ + QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj); + if (ioc->fd != -1) { + close(ioc->fd); + ioc->fd = -1; + } +} + + +static ssize_t qio_channel_file_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + Error **errp) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + ssize_t ret; + + if (fds || nfds) { + error_setg_errno(errp, EINVAL, "%s", + _("Channel does not support file descriptor passing")); + return -1; + } + + retry: + ret = readv(fioc->fd, iov, niov); + if (ret < 0) { + if (errno == EAGAIN || + errno == EWOULDBLOCK) { + return QIO_CHANNEL_ERR_BLOCK; + } + if (errno == EINTR) { + goto retry; + } + + error_setg_errno(errp, errno, "%s", + _("Unable to read from file")); + return -1; + } + + return ret; +} + +static ssize_t qio_channel_file_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + Error **errp) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + ssize_t ret; + + if (fds || nfds) { + error_setg_errno(errp, EINVAL, "%s", + _("Channel does not support file descriptor passing")); + return -1; + } + + retry: + ret = writev(fioc->fd, iov, niov); + if (ret <= 0) { + if (errno == EAGAIN || + errno == EWOULDBLOCK) { + return QIO_CHANNEL_ERR_BLOCK; + } + if (errno == EINTR) { + goto retry; + } + error_setg_errno(errp, errno, "%s", + _("Unable to write to file")); + return -1; + } + return ret; +} + +static void qio_channel_file_set_blocking(QIOChannel *ioc, + bool enabled) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + + if (enabled) { + qemu_set_block(fioc->fd); + } else { + qemu_set_nonblock(fioc->fd); + } +} + + +static int qio_channel_file_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + + if (close(fioc->fd) < 0) { + error_setg_errno(errp, errno, "%s", + _("Unable to close file")); + return -1; + } + return 0; +} + + +static GSource *qio_channel_file_create_watch(QIOChannel *ioc, + GIOCondition condition) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + return qio_channel_create_fd_watch(ioc, + fioc->fd, + condition); +} + +static void qio_channel_file_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_file_writev; + ioc_klass->io_readv = qio_channel_file_readv; + ioc_klass->io_set_blocking = qio_channel_file_set_blocking; + ioc_klass->io_close = qio_channel_file_close; + ioc_klass->io_create_watch = qio_channel_file_create_watch; +} + +static const TypeInfo qio_channel_file_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_FILE, + .instance_size = sizeof(QIOChannelFile), + .instance_init = qio_channel_file_init, + .instance_finalize = qio_channel_file_finalize, + .class_init = qio_channel_file_class_init, +}; + +static void qio_channel_file_register_types(void) +{ + type_register_static(&qio_channel_file_info); +} + +type_init(qio_channel_file_register_types); diff --git a/tests/.gitignore b/tests/.gitignore index ac891c6..bb66d94 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -23,6 +23,8 @@ test-cutils test-hbitmap test-int128 test-iov +test-io-channel-file +test-io-channel-file.txt test-io-channel-socket test-io-task test-mul64 diff --git a/tests/Makefile b/tests/Makefile index 37bde1a..f896051 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -80,6 +80,7 @@ check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF) check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF) check-unit-y += tests/test-io-task$(EXESUF) check-unit-y += tests/test-io-channel-socket$(EXESUF) +check-unit-y += tests/test-io-channel-file$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh @@ -363,6 +364,8 @@ tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ + tests/io-channel-helpers.o $(test-io-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o diff --git a/tests/io-channel-helpers.c b/tests/io-channel-helpers.c index 946c9b9..9fc827f 100644 --- a/tests/io-channel-helpers.c +++ b/tests/io-channel-helpers.c @@ -220,3 +220,28 @@ void test_io_channel_comms(bool blocking, g_free(data->output); g_free(data); } + + +void test_io_channel_comms_serial(QIOChannel *src, + QIOChannel *dst) +{ + struct TestIOData *data; + + data = test_load_io_data("libqemuutil.a"); + data->src = src; + data->dst = dst; + data->blocking = true; + + test_io_thread_writer(data); + test_io_thread_reader(data); + + g_assert_cmpint(memcmp(data->input, + data->output, + data->len), ==, 0); + g_assert(data->readerr == NULL); + g_assert(data->writeerr == NULL); + + g_free(data->input); + g_free(data->output); + g_free(data); +} diff --git a/tests/io-channel-helpers.h b/tests/io-channel-helpers.h index d0085a2..7edbaad 100644 --- a/tests/io-channel-helpers.h +++ b/tests/io-channel-helpers.h @@ -27,4 +27,7 @@ void test_io_channel_comms(bool blocking, QIOChannel *src, QIOChannel *dst); +void test_io_channel_comms_serial(QIOChannel *src, + QIOChannel *dst); + #endif /* TEST_IO_CHANNEL_HELPERS */ diff --git a/tests/test-io-channel-file.c b/tests/test-io-channel-file.c new file mode 100644 index 0000000..c0a2009 --- /dev/null +++ b/tests/test-io-channel-file.c @@ -0,0 +1,89 @@ +/* + * QEMU I/O channel sockets test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "io/channel-file.h" +#include "io-channel-helpers.h" + + +static void test_io_channel_file(void) +{ + QIOChannel *src, *dst; + +#define TEST_FILE "tests/test-io-channel-file.txt" + unlink(TEST_FILE); + src = QIO_CHANNEL(qio_channel_file_new_path( + TEST_FILE, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600, + &error_abort)); + dst = QIO_CHANNEL(qio_channel_file_new_path( + TEST_FILE, O_RDONLY | O_BINARY, 0, + &error_abort)); + test_io_channel_comms_serial(src, dst); + + unlink(TEST_FILE); + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); +} + + +#ifndef _WIN32 +static void test_io_channel_pipe(bool async) +{ + QIOChannel *src, *dst; + int fd[2]; + + if (pipe(fd) < 0) { + perror("pipe"); + abort(); + } + + src = QIO_CHANNEL(qio_channel_file_new_fd(fd[1])); + dst = QIO_CHANNEL(qio_channel_file_new_fd(fd[0])); + test_io_channel_comms(async, src, dst); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); +} + + +static void test_io_channel_pipe_async(void) +{ + test_io_channel_pipe(true); +} + +static void test_io_channel_pipe_sync(void) +{ + test_io_channel_pipe(false); +} +#endif /* ! _WIN32 */ + + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/io/channel/file", test_io_channel_file); +#ifndef _WIN32 + g_test_add_func("/io/channel/pipe/sync", test_io_channel_pipe_sync); + g_test_add_func("/io/channel/pipe/async", test_io_channel_pipe_async); +#endif + return g_test_run(); +} -- 2.4.3