io: add QIOChannelFile class
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>
This commit is contained in:
parent
559607ea17
commit
d6e48869a4
93
include/io/channel-file.h
Normal file
93
include/io/channel-file.h
Normal file
@ -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__ */
|
@ -1,4 +1,5 @@
|
|||||||
io-obj-y = channel.o
|
io-obj-y = channel.o
|
||||||
|
io-obj-y += channel-file.o
|
||||||
io-obj-y += channel-socket.o
|
io-obj-y += channel-socket.o
|
||||||
io-obj-y += channel-watch.o
|
io-obj-y += channel-watch.o
|
||||||
io-obj-y += task.o
|
io-obj-y += task.o
|
||||||
|
225
io/channel-file.c
Normal file
225
io/channel-file.c
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 off_t qio_channel_file_seek(QIOChannel *ioc,
|
||||||
|
off_t offset,
|
||||||
|
int whence,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
||||||
|
off_t ret;
|
||||||
|
|
||||||
|
ret = lseek(fioc->fd, offset, whence);
|
||||||
|
if (ret == (off_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);
|
2
tests/.gitignore
vendored
2
tests/.gitignore
vendored
@ -24,6 +24,8 @@ test-cutils
|
|||||||
test-hbitmap
|
test-hbitmap
|
||||||
test-int128
|
test-int128
|
||||||
test-iov
|
test-iov
|
||||||
|
test-io-channel-file
|
||||||
|
test-io-channel-file.txt
|
||||||
test-io-channel-socket
|
test-io-channel-socket
|
||||||
test-io-task
|
test-io-task
|
||||||
test-mul64
|
test-mul64
|
||||||
|
@ -86,6 +86,7 @@ check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF)
|
|||||||
check-unit-y += tests/test-timed-average$(EXESUF)
|
check-unit-y += tests/test-timed-average$(EXESUF)
|
||||||
check-unit-y += tests/test-io-task$(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-socket$(EXESUF)
|
||||||
|
check-unit-y += tests/test-io-channel-file$(EXESUF)
|
||||||
|
|
||||||
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
|
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
|
||||||
|
|
||||||
@ -475,6 +476,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-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
|
||||||
tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
|
tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
|
||||||
tests/io-channel-helpers.o $(test-io-obj-y)
|
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/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
|
||||||
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
|
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
|
||||||
|
100
tests/test-io-channel-file.c
Normal file
100
tests/test-io-channel-file.c
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* QEMU I/O channel file 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;
|
||||||
|
QIOChannelTest *test;
|
||||||
|
|
||||||
|
#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 = qio_channel_test_new();
|
||||||
|
qio_channel_test_run_writer(test, src);
|
||||||
|
qio_channel_test_run_reader(test, dst);
|
||||||
|
qio_channel_test_validate(test);
|
||||||
|
|
||||||
|
unlink(TEST_FILE);
|
||||||
|
object_unref(OBJECT(src));
|
||||||
|
object_unref(OBJECT(dst));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
static void test_io_channel_pipe(bool async)
|
||||||
|
{
|
||||||
|
QIOChannel *src, *dst;
|
||||||
|
QIOChannelTest *test;
|
||||||
|
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 = qio_channel_test_new();
|
||||||
|
qio_channel_test_run_threads(test, async, src, dst);
|
||||||
|
qio_channel_test_validate(test);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
@ -1836,3 +1836,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(void *ioc) "Socket accept start ioc=%p"
|
||||||
qio_channel_socket_accept_fail(void *ioc) "Socket accept fail 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"
|
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"
|
||||||
|
Loading…
Reference in New Issue
Block a user