diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2015-11-26 10:24:18 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2015-11-26 10:24:18 +0000 |
commit | b8b0ee0ea3d2789d8ee7372b9a173e7952e18087 (patch) | |
tree | afc5c64f963e57e406d4f32f909b65e0e4fb65c2 | |
parent | 7ef7ddf37674f7147ef23c311cbc3c37e908c8b0 (diff) | |
parent | 44c6e00c3fd85b9c496bd3e74108ace126813a59 (diff) |
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-- | Makefile | 5 | ||||
-rw-r--r-- | qga/commands-posix.c | 56 | ||||
-rw-r--r-- | qga/commands-win32.c | 20 | ||||
-rw-r--r-- | qga/guest-agent-core.h | 7 | ||||
-rw-r--r-- | qga/installer/qemu-ga.wxs | 18 | ||||
-rw-r--r-- | qga/qapi-schema.json | 4 | ||||
-rw-r--r-- | tests/test-qga.c | 98 |
7 files changed, 197 insertions, 11 deletions
@@ -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 0ebd47336a..c2ff97021f 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 41f6dd9fed..0654fe4fe7 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 e92c6abafb..238dc6b08d 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 6804f0279f..9473875723 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 78362e071d..01c9ee48d8 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 64738465c5..e6a84d17f0 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, |