From: "Daniel P. Berrange" <berrange@redhat.com>
To: qemu-devel@nongnu.org
Cc: Paolo Bonzini <pbonzini@redhat.com>,
"Dr. David Alan Gilbert" <dgilbert@redhat.com>,
Gerd Hoffmann <kraxel@redhat.com>
Subject: [Qemu-devel] [PATCH v1 12/16] io: add QIOChannelFile class
Date: Fri, 18 Sep 2015 14:19:06 +0100 [thread overview]
Message-ID: <1442582350-9179-13-git-send-email-berrange@redhat.com> (raw)
In-Reply-To: <1442582350-9179-1-git-send-email-berrange@redhat.com>
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 <berrange@redhat.com>
---
include/io/channel-file.h | 93 +++++++++++++++++
io/Makefile.objs | 1 +
io/channel-file.c | 237 +++++++++++++++++++++++++++++++++++++++++++
tests/.gitignore | 2 +
tests/Makefile | 3 +
tests/io-channel-helpers.c | 25 +++++
tests/io-channel-helpers.h | 3 +
tests/test-io-channel-file.c | 91 +++++++++++++++++
trace-events | 4 +
9 files changed, 459 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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..3e67b22
--- /dev/null
+++ b/io/channel-file.c
@@ -0,0 +1,237 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "io/channel-file.h"
+#include "io/channel-watch.h"
+#include "qemu/sockets.h"
+#include "trace.h"
+
+QIOChannelFile *
+qio_channel_file_new_fd(int fd)
+{
+ QIOChannelFile *ioc;
+
+ ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE));
+
+ ioc->fd = fd;
+
+ trace_qio_channel_file_new_fd(ioc, fd);
+
+ return ioc;
+}
+
+
+QIOChannelFile *
+qio_channel_file_new_path(const char *path,
+ int flags,
+ mode_t mode,
+ Error **errp)
+{
+ QIOChannelFile *ioc;
+
+ ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE));
+
+ if (flags & O_WRONLY) {
+ ioc->fd = open(path, flags, mode);
+ } else {
+ ioc->fd = open(path, flags);
+ }
+ if (ioc->fd < 0) {
+ object_unref(OBJECT(ioc));
+ error_setg_errno(errp, errno,
+ "Unable to open %s", path);
+ return NULL;
+ }
+
+ trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd);
+
+ return ioc;
+}
+
+
+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,
+ "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,
+ "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,
+ "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,
+ "Unable to write to file");
+ return -1;
+ }
+ return ret;
+}
+
+static int qio_channel_file_set_blocking(QIOChannel *ioc,
+ bool enabled,
+ Error **errp)
+{
+ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
+
+ if (enabled) {
+ qemu_set_block(fioc->fd);
+ } else {
+ qemu_set_nonblock(fioc->fd);
+ }
+ return 0;
+}
+
+
+static off64_t qio_channel_file_seek(QIOChannel *ioc,
+ off64_t offset,
+ int whence,
+ Error **errp)
+{
+ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
+ off64_t ret;
+
+ ret = lseek64(fioc->fd, offset, whence);
+ if (ret == (off64_t)-1) {
+ error_setg_errno(errp, errno,
+ "Unable to seek to offset %lld whence %d in file",
+ (long long int)offset, whence);
+ return -1;
+ }
+ return ret;
+}
+
+
+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,
+ "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_seek = qio_channel_file_seek;
+ 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 ca1846c..3b0b57b 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -82,6 +82,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
@@ -370,6 +371,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 c84b99f..91c44b4 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..97394f9
--- /dev/null
+++ b/tests/test-io-channel-file.c
@@ -0,0 +1,91 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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();
+}
diff --git a/trace-events b/trace-events
index 609dc61..cff3261 100644
--- a/trace-events
+++ b/trace-events
@@ -1713,3 +1713,7 @@ qio_channel_socket_dgram_complete(void *ioc, int fd) "Socket dgram complete ioc=
qio_channel_socket_accept(void *ioc) "Socket accept start ioc=%p"
qio_channel_socket_accept_fail(void *ioc) "Socket accept fail ioc=%p"
qio_channel_socket_accept_complete(void *ioc, void *cioc, int fd) "Socket accept complete ioc=%p cioc=%p fd=%d"
+
+# io/channel-file.c
+qio_channel_file_new_fd(void *ioc, int fd) "File new fd ioc=%p fd=%d"
+qio_channel_file_new_path(void *ioc, const char *path, int flags, int mode, int fd) "File new fd ioc=%p path=%s flags=%d mode=%d fd=%d"
--
2.4.3
next prev parent reply other threads:[~2015-09-18 13:58 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-18 13:18 [Qemu-devel] [PATCH v1 00/16] Introduce I/O channels framework Daniel P. Berrange
2015-09-18 13:18 ` [Qemu-devel] [PATCH v1 01/16] sockets: add helpers for creating SocketAddress from a socket Daniel P. Berrange
2015-09-18 13:18 ` [Qemu-devel] [PATCH v1 02/16] sockets: move qapi_copy_SocketAddress into qemu-sockets.c Daniel P. Berrange
2015-09-18 13:18 ` [Qemu-devel] [PATCH v1 03/16] sockets: allow port to be NULL when listening on IP address Daniel P. Berrange
2015-09-18 13:18 ` [Qemu-devel] [PATCH v1 04/16] ui: convert VNC startup code to use SocketAddress Daniel P. Berrange
2015-09-18 13:18 ` [Qemu-devel] [PATCH v1 05/16] osdep: add qemu_fork() wrapper for safely handling signals Daniel P. Berrange
2015-09-18 13:19 ` [Qemu-devel] [PATCH v1 06/16] coroutine: move into libqemuutil.a library Daniel P. Berrange
2015-09-22 12:07 ` Paolo Bonzini
2015-09-24 14:46 ` Daniel P. Berrange
2015-09-18 13:19 ` [Qemu-devel] [PATCH v1 07/16] io: add abstract QIOChannel classes Daniel P. Berrange
2015-09-22 12:19 ` Paolo Bonzini
2015-09-22 12:24 ` Daniel P. Berrange
2015-09-22 12:28 ` Paolo Bonzini
2015-09-24 14:47 ` Daniel P. Berrange
2015-09-18 13:19 ` [Qemu-devel] [PATCH v1 08/16] io: add helper module for creating watches on FDs Daniel P. Berrange
2015-09-18 13:19 ` [Qemu-devel] [PATCH v1 09/16] io: pull Buffer code out of VNC module Daniel P. Berrange
2015-09-22 12:04 ` Paolo Bonzini
2015-09-22 12:20 ` Daniel P. Berrange
2015-09-22 12:21 ` Paolo Bonzini
2015-09-18 13:19 ` [Qemu-devel] [PATCH v1 10/16] io: add QIOTask class for async operations Daniel P. Berrange
2015-09-18 13:19 ` [Qemu-devel] [PATCH v1 11/16] io: add QIOChannelSocket class Daniel P. Berrange
2015-09-18 13:19 ` Daniel P. Berrange [this message]
2015-09-18 13:19 ` [Qemu-devel] [PATCH v1 13/16] io: add QIOChannelTLS class Daniel P. Berrange
2015-09-18 13:19 ` [Qemu-devel] [PATCH v1 14/16] io: add QIOChannelWebsock class Daniel P. Berrange
2015-09-18 13:19 ` [Qemu-devel] [PATCH v1 15/16] io: add QIOChannelCommand class Daniel P. Berrange
2015-09-18 13:19 ` [Qemu-devel] [PATCH v1 16/16] io: add QIOChannelBuffer class Daniel P. Berrange
2015-09-25 10:15 ` Paolo Bonzini
2015-09-25 10:21 ` Daniel P. Berrange
2015-09-22 12:23 ` [Qemu-devel] [PATCH v1 00/16] Introduce I/O channels framework Paolo Bonzini
2015-09-24 14:51 ` Daniel P. Berrange
2015-09-25 10:18 ` Paolo Bonzini
2015-09-25 10:20 ` Daniel P. Berrange
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1442582350-9179-13-git-send-email-berrange@redhat.com \
--to=berrange@redhat.com \
--cc=dgilbert@redhat.com \
--cc=kraxel@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.