summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-11-26 10:24:18 +0000
committerPeter Maydell <peter.maydell@linaro.org>2015-11-26 10:24:18 +0000
commitb8b0ee0ea3d2789d8ee7372b9a173e7952e18087 (patch)
treeafc5c64f963e57e406d4f32f909b65e0e4fb65c2
parent7ef7ddf37674f7147ef23c311cbc3c37e908c8b0 (diff)
parent44c6e00c3fd85b9c496bd3e74108ace126813a59 (diff)
downloadhqemu-b8b0ee0ea3d2789d8ee7372b9a173e7952e18087.zip
hqemu-b8b0ee0ea3d2789d8ee7372b9a173e7952e18087.tar.gz
Merge remote-tracking branch 'remotes/mdroth/tags/qga-pull-2015-11-25-v2-tag' into staging
qemu-ga patch queue for 2.5 * include additional w32 MSI install components needed for guest-exec * fix 'make install' when compiling with --disable-tools * fix potential data corruption/loss when accessing files bi-directionally via guest-file-{read,write} * explicitly document how integer args for guest-file-seek map to SEEK_SET/SEEK_CUR/etc to avoid platform-specific differences v2: * fixed missing SoB # gpg: Signature made Wed 25 Nov 2015 23:58:45 GMT using RSA key ID F108B584 # gpg: Good signature from "Michael Roth <flukshun@gmail.com>" # gpg: aka "Michael Roth <mdroth@utexas.edu>" # gpg: aka "Michael Roth <mdroth@linux.vnet.ibm.com>" * remotes/mdroth/tags/qga-pull-2015-11-25-v2-tag: qga: added another non-interactive gspawn() helper file. qga: Better mapping of SEEK_* in guest-file-seek tests: add file-write-read test qga: flush explicitly when needed qga: gspawn() console helper to Windows guest agent msi build makefile: fix qemu-ga make install for --disable-tools Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--Makefile5
-rw-r--r--qga/commands-posix.c56
-rw-r--r--qga/commands-win32.c20
-rw-r--r--qga/guest-agent-core.h7
-rw-r--r--qga/installer/qemu-ga.wxs18
-rw-r--r--qga/qapi-schema.json4
-rw-r--r--tests/test-qga.c98
7 files changed, 197 insertions, 11 deletions
diff --git a/Makefile b/Makefile
index c7fa427..930ac27 100644
--- a/Makefile
+++ b/Makefile
@@ -440,10 +440,7 @@ endif
install: all $(if $(BUILD_DOCS),install-doc) \
install-datadir install-localstatedir
ifneq ($(TOOLS),)
- $(call install-prog,$(filter-out qemu-ga,$(TOOLS)),$(DESTDIR)$(bindir))
-ifneq (,$(findstring qemu-ga,$(TOOLS)))
- $(call install-prog,qemu-ga$(EXESUF),$(DESTDIR)$(bindir))
-endif
+ $(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
endif
ifneq ($(CONFIG_MODULES),)
$(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)"
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 0ebd473..c2ff970 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -216,9 +216,16 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
}
}
+typedef enum {
+ RW_STATE_NEW,
+ RW_STATE_READING,
+ RW_STATE_WRITING,
+} RwState;
+
typedef struct GuestFileHandle {
uint64_t id;
FILE *fh;
+ RwState state;
QTAILQ_ENTRY(GuestFileHandle) next;
} GuestFileHandle;
@@ -460,6 +467,17 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
}
fh = gfh->fh;
+
+ /* explicitly flush when switching from writing to reading */
+ if (gfh->state == RW_STATE_WRITING) {
+ int ret = fflush(fh);
+ if (ret == EOF) {
+ error_setg_errno(errp, errno, "failed to flush file");
+ return NULL;
+ }
+ gfh->state = RW_STATE_NEW;
+ }
+
buf = g_malloc0(count+1);
read_count = fread(buf, 1, count, fh);
if (ferror(fh)) {
@@ -473,6 +491,7 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
if (read_count) {
read_data->buf_b64 = g_base64_encode(buf, read_count);
}
+ gfh->state = RW_STATE_READING;
}
g_free(buf);
clearerr(fh);
@@ -496,6 +515,16 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
}
fh = gfh->fh;
+
+ if (gfh->state == RW_STATE_READING) {
+ int ret = fseek(fh, 0, SEEK_CUR);
+ if (ret == -1) {
+ error_setg_errno(errp, errno, "failed to seek file");
+ return NULL;
+ }
+ gfh->state = RW_STATE_NEW;
+ }
+
buf = g_base64_decode(buf_b64, &buf_len);
if (!has_count) {
@@ -515,6 +544,7 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
write_data = g_new0(GuestFileWrite, 1);
write_data->count = write_count;
write_data->eof = feof(fh);
+ gfh->state = RW_STATE_WRITING;
}
g_free(buf);
clearerr(fh);
@@ -523,25 +553,47 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
}
struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
- int64_t whence, Error **errp)
+ int64_t whence_code, Error **errp)
{
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
GuestFileSeek *seek_data = NULL;
FILE *fh;
int ret;
+ int whence;
if (!gfh) {
return NULL;
}
+ /* We stupidly exposed 'whence':'int' in our qapi */
+ switch (whence_code) {
+ case QGA_SEEK_SET:
+ whence = SEEK_SET;
+ break;
+ case QGA_SEEK_CUR:
+ whence = SEEK_CUR;
+ break;
+ case QGA_SEEK_END:
+ whence = SEEK_END;
+ break;
+ default:
+ error_setg(errp, "invalid whence code %"PRId64, whence_code);
+ return NULL;
+ }
+
fh = gfh->fh;
ret = fseek(fh, offset, whence);
if (ret == -1) {
error_setg_errno(errp, errno, "failed to seek file");
+ if (errno == ESPIPE) {
+ /* file is non-seekable, stdio shouldn't be buffering anyways */
+ gfh->state = RW_STATE_NEW;
+ }
} else {
seek_data = g_new0(GuestFileSeek, 1);
seek_data->position = ftell(fh);
seek_data->eof = feof(fh);
+ gfh->state = RW_STATE_NEW;
}
clearerr(fh);
@@ -562,6 +614,8 @@ void qmp_guest_file_flush(int64_t handle, Error **errp)
ret = fflush(fh);
if (ret == EOF) {
error_setg_errno(errp, errno, "failed to flush file");
+ } else {
+ gfh->state = RW_STATE_NEW;
}
}
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 41f6dd9..0654fe4 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -382,7 +382,7 @@ done:
}
GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
- int64_t whence, Error **errp)
+ int64_t whence_code, Error **errp)
{
GuestFileHandle *gfh;
GuestFileSeek *seek_data;
@@ -390,11 +390,29 @@ GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
LARGE_INTEGER new_pos, off_pos;
off_pos.QuadPart = offset;
BOOL res;
+ int whence;
+
gfh = guest_file_handle_find(handle, errp);
if (!gfh) {
return NULL;
}
+ /* We stupidly exposed 'whence':'int' in our qapi */
+ switch (whence_code) {
+ case QGA_SEEK_SET:
+ whence = SEEK_SET;
+ break;
+ case QGA_SEEK_CUR:
+ whence = SEEK_CUR;
+ break;
+ case QGA_SEEK_END:
+ whence = SEEK_END;
+ break;
+ default:
+ error_setg(errp, "invalid whence code %"PRId64, whence_code);
+ return NULL;
+ }
+
fh = gfh->fh;
res = SetFilePointerEx(fh, off_pos, &new_pos, whence);
if (!res) {
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index e92c6ab..238dc6b 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -15,6 +15,13 @@
#define QGA_READ_COUNT_DEFAULT 4096
+/* Mapping of whence codes used by guest-file-seek. */
+enum {
+ QGA_SEEK_SET = 0,
+ QGA_SEEK_CUR = 1,
+ QGA_SEEK_END = 2,
+};
+
typedef struct GAState GAState;
typedef struct GACommandState GACommandState;
extern GAState *ga_state;
diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs
index 6804f02..9473875 100644
--- a/qga/installer/qemu-ga.wxs
+++ b/qga/installer/qemu-ga.wxs
@@ -91,6 +91,22 @@
<File Id="qga_vss.tlb" Name="qga-vss.tlb" Source="$(env.BUILD_DIR)/qga/vss-win32/qga-vss.tlb" KeyPath="yes" DiskId="1"/>
</Component>
<?endif?>
+ <?if $(var.Arch) = "32"?>
+ <Component Id="gspawn-helper-console" Guid="{446185B3-87BE-43D2-96B8-0FEFD9E8696D}">
+ <File Id="gspawn-win32-helper-console.exe" Name="gspawn-win32-helper-console.exe" Source="$(var.Mingw_bin)/gspawn-win32-helper-console.exe" KeyPath="yes" DiskId="1"/>
+ </Component>
+ <Component Id="gspawn-helper" Guid="{CD67A5A3-2DB1-4DA1-A67A-8D71E797B466}">
+ <File Id="gspawn-win32-helper.exe" Name="gspawn-win32-helper.exe" Source="$(var.Mingw_bin)/gspawn-win32-helper.exe" KeyPath="yes" DiskId="1"/>
+ </Component>
+ <?endif?>
+ <?if $(var.Arch) = "64"?>
+ <Component Id="gspawn-helper-console" Guid="{9E615A9F-349A-4992-A5C2-C10BAD173660}">
+ <File Id="gspawn-win64-helper-console.exe" Name="gspawn-win64-helper-console.exe" Source="$(var.Mingw_bin)/gspawn-win64-helper-console.exe" KeyPath="yes" DiskId="1"/>
+ </Component>
+ <Component Id="gspawn-helper" Guid="{D201AD22-1846-4E4F-B6E1-C7A908ED2457}">
+ <File Id="gspawn-win64-helper.exe" Name="gspawn-win64-helper.exe" Source="$(var.Mingw_bin)/gspawn-win64-helper.exe" KeyPath="yes" DiskId="1"/>
+ </Component>
+ <?endif?>
<Component Id="iconv" Guid="{35EE3558-D34B-4F0A-B8BD-430FF0775246}">
<File Id="iconv.dll" Name="iconv.dll" Source="$(var.Mingw_bin)/iconv.dll" KeyPath="yes" DiskId="1"/>
</Component>
@@ -148,6 +164,8 @@
<ComponentRef Id="qga_vss_dll" />
<ComponentRef Id="qga_vss_tlb" />
<?endif?>
+ <ComponentRef Id="gspawn-helper-console" />
+ <ComponentRef Id="gspawn-helper" />
<ComponentRef Id="iconv" />
<ComponentRef Id="libgcc_arch_lib" />
<ComponentRef Id="libglib" />
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 78362e0..01c9ee4 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -318,13 +318,13 @@
#
# Seek to a position in the file, as with fseek(), and return the
# current file position afterward. Also encapsulates ftell()'s
-# functionality, just Set offset=0, whence=SEEK_CUR.
+# functionality, with offset=0 and whence=1.
#
# @handle: filehandle returned by guest-file-open
#
# @offset: bytes to skip over in the file stream
#
-# @whence: SEEK_SET, SEEK_CUR, or SEEK_END, as with fseek()
+# @whence: 0 for SEEK_SET, 1 for SEEK_CUR, or 2 for SEEK_END
#
# Returns: @GuestFileSeek on success.
#
diff --git a/tests/test-qga.c b/tests/test-qga.c
index 6473846..e6a84d1 100644
--- a/tests/test-qga.c
+++ b/tests/test-qga.c
@@ -13,6 +13,7 @@
#include "libqtest.h"
#include "config-host.h"
+#include "qga/guest-agent-core.h"
typedef struct {
char *test_dir;
@@ -352,10 +353,10 @@ static void test_qga_network_get_interfaces(gconstpointer fix)
static void test_qga_file_ops(gconstpointer fix)
{
const TestFixture *fixture = fix;
- const guchar helloworld[] = "Hello World!\n";
+ const unsigned char helloworld[] = "Hello World!\n";
const char *b64;
gchar *cmd, *path, *enc;
- guchar *dec;
+ unsigned char *dec;
QDict *ret, *val;
int64_t id, eof;
gsize count;
@@ -457,7 +458,7 @@ static void test_qga_file_ops(gconstpointer fix)
cmd = g_strdup_printf("{'execute': 'guest-file-seek',"
" 'arguments': { 'handle': %" PRId64 ", "
" 'offset': %d, 'whence': %d } }",
- id, 6, SEEK_SET);
+ id, 6, QGA_SEEK_SET);
ret = qmp_fd(fixture->fd, cmd);
qmp_assert_no_error(ret);
val = qdict_get_qdict(ret, "return");
@@ -496,6 +497,96 @@ static void test_qga_file_ops(gconstpointer fix)
g_free(cmd);
}
+static void test_qga_file_write_read(gconstpointer fix)
+{
+ const TestFixture *fixture = fix;
+ const unsigned char helloworld[] = "Hello World!\n";
+ const char *b64;
+ gchar *cmd, *enc;
+ QDict *ret, *val;
+ int64_t id, eof;
+ gsize count;
+
+ /* open */
+ ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open',"
+ " 'arguments': { 'path': 'foo', 'mode': 'w+' } }");
+ g_assert_nonnull(ret);
+ qmp_assert_no_error(ret);
+ id = qdict_get_int(ret, "return");
+ QDECREF(ret);
+
+ enc = g_base64_encode(helloworld, sizeof(helloworld));
+ /* write */
+ cmd = g_strdup_printf("{'execute': 'guest-file-write',"
+ " 'arguments': { 'handle': %" PRId64 ","
+ " 'buf-b64': '%s' } }", id, enc);
+ ret = qmp_fd(fixture->fd, cmd);
+ g_assert_nonnull(ret);
+ qmp_assert_no_error(ret);
+
+ val = qdict_get_qdict(ret, "return");
+ count = qdict_get_int(val, "count");
+ eof = qdict_get_bool(val, "eof");
+ g_assert_cmpint(count, ==, sizeof(helloworld));
+ g_assert_cmpint(eof, ==, 0);
+ QDECREF(ret);
+ g_free(cmd);
+
+ /* read (check implicit flush) */
+ cmd = g_strdup_printf("{'execute': 'guest-file-read',"
+ " 'arguments': { 'handle': %" PRId64 "} }",
+ id);
+ ret = qmp_fd(fixture->fd, cmd);
+ val = qdict_get_qdict(ret, "return");
+ count = qdict_get_int(val, "count");
+ eof = qdict_get_bool(val, "eof");
+ b64 = qdict_get_str(val, "buf-b64");
+ g_assert_cmpint(count, ==, 0);
+ g_assert(eof);
+ g_assert_cmpstr(b64, ==, "");
+ QDECREF(ret);
+ g_free(cmd);
+
+ /* seek to 0 */
+ cmd = g_strdup_printf("{'execute': 'guest-file-seek',"
+ " 'arguments': { 'handle': %" PRId64 ", "
+ " 'offset': %d, 'whence': %d } }",
+ id, 0, QGA_SEEK_SET);
+ ret = qmp_fd(fixture->fd, cmd);
+ qmp_assert_no_error(ret);
+ val = qdict_get_qdict(ret, "return");
+ count = qdict_get_int(val, "position");
+ eof = qdict_get_bool(val, "eof");
+ g_assert_cmpint(count, ==, 0);
+ g_assert(!eof);
+ QDECREF(ret);
+ g_free(cmd);
+
+ /* read */
+ cmd = g_strdup_printf("{'execute': 'guest-file-read',"
+ " 'arguments': { 'handle': %" PRId64 "} }",
+ id);
+ ret = qmp_fd(fixture->fd, cmd);
+ val = qdict_get_qdict(ret, "return");
+ count = qdict_get_int(val, "count");
+ eof = qdict_get_bool(val, "eof");
+ b64 = qdict_get_str(val, "buf-b64");
+ g_assert_cmpint(count, ==, sizeof(helloworld));
+ g_assert(eof);
+ g_assert_cmpstr(b64, ==, enc);
+ QDECREF(ret);
+ g_free(cmd);
+ g_free(enc);
+
+ /* close */
+ cmd = g_strdup_printf("{'execute': 'guest-file-close',"
+ " 'arguments': {'handle': %" PRId64 "} }",
+ id);
+ ret = qmp_fd(fixture->fd, cmd);
+ QDECREF(ret);
+ g_free(cmd);
+}
+
static void test_qga_get_time(gconstpointer fix)
{
const TestFixture *fixture = fix;
@@ -762,6 +853,7 @@ int main(int argc, char **argv)
g_test_add_data_func("/qga/get-memory-blocks", &fix,
test_qga_get_memory_blocks);
g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops);
+ g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read);
g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time);
g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd);
g_test_add_data_func("/qga/fsfreeze-status", &fix,
OpenPOWER on IntegriCloud