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
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJWVks1AAoJEDNTyc7xCLWE0/oH/3q9DtEGB5dDUMCn3BPo+FCO
 M7R4uL1MB2CSXHPZxpoueZL2ZNlCpWJgidOEfVTyVrPUI68qsEcPm2vmKAQu5y7e
 Dxu+yD+qA3wWqgNRhH6O7X7owq+e5cM8ZZFPZ2T89wgsQbs+TwyrOfJfypIP/cAW
 cjnowxVz9kB9f0qpOJViPN0lfbtc+n5pZOm2VNluHtEWCHS8X9tqvSDtHgIU/BKV
 39y/UeSKNmvl7UlTeRi6dVUbKPo1eZSExjvHoWZjXBOu3Ky75wineVMASxuC632y
 IQG+1YIBl5vv84he29U6O5lW2rcbld6vff8QqvR8HzNIxnD8kfykkpmxHlxWDIA=
 =bIkC
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Peter Maydell 2015-11-26 10:24:18 +00:00
commit b8b0ee0ea3
7 changed files with 197 additions and 11 deletions

View File

@ -440,10 +440,7 @@ endif
install: all $(if $(BUILD_DOCS),install-doc) \ install: all $(if $(BUILD_DOCS),install-doc) \
install-datadir install-localstatedir install-datadir install-localstatedir
ifneq ($(TOOLS),) ifneq ($(TOOLS),)
$(call install-prog,$(filter-out qemu-ga,$(TOOLS)),$(DESTDIR)$(bindir)) $(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
ifneq (,$(findstring qemu-ga,$(TOOLS)))
$(call install-prog,qemu-ga$(EXESUF),$(DESTDIR)$(bindir))
endif
endif endif
ifneq ($(CONFIG_MODULES),) ifneq ($(CONFIG_MODULES),)
$(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)" $(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)"

View File

@ -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 { typedef struct GuestFileHandle {
uint64_t id; uint64_t id;
FILE *fh; FILE *fh;
RwState state;
QTAILQ_ENTRY(GuestFileHandle) next; QTAILQ_ENTRY(GuestFileHandle) next;
} GuestFileHandle; } GuestFileHandle;
@ -460,6 +467,17 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
} }
fh = gfh->fh; 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); buf = g_malloc0(count+1);
read_count = fread(buf, 1, count, fh); read_count = fread(buf, 1, count, fh);
if (ferror(fh)) { if (ferror(fh)) {
@ -473,6 +491,7 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
if (read_count) { if (read_count) {
read_data->buf_b64 = g_base64_encode(buf, read_count); read_data->buf_b64 = g_base64_encode(buf, read_count);
} }
gfh->state = RW_STATE_READING;
} }
g_free(buf); g_free(buf);
clearerr(fh); clearerr(fh);
@ -496,6 +515,16 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
} }
fh = gfh->fh; 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); buf = g_base64_decode(buf_b64, &buf_len);
if (!has_count) { 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 = g_new0(GuestFileWrite, 1);
write_data->count = write_count; write_data->count = write_count;
write_data->eof = feof(fh); write_data->eof = feof(fh);
gfh->state = RW_STATE_WRITING;
} }
g_free(buf); g_free(buf);
clearerr(fh); 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, 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); GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
GuestFileSeek *seek_data = NULL; GuestFileSeek *seek_data = NULL;
FILE *fh; FILE *fh;
int ret; int ret;
int whence;
if (!gfh) { if (!gfh) {
return NULL; 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; fh = gfh->fh;
ret = fseek(fh, offset, whence); ret = fseek(fh, offset, whence);
if (ret == -1) { if (ret == -1) {
error_setg_errno(errp, errno, "failed to seek file"); 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 { } else {
seek_data = g_new0(GuestFileSeek, 1); seek_data = g_new0(GuestFileSeek, 1);
seek_data->position = ftell(fh); seek_data->position = ftell(fh);
seek_data->eof = feof(fh); seek_data->eof = feof(fh);
gfh->state = RW_STATE_NEW;
} }
clearerr(fh); clearerr(fh);
@ -562,6 +614,8 @@ void qmp_guest_file_flush(int64_t handle, Error **errp)
ret = fflush(fh); ret = fflush(fh);
if (ret == EOF) { if (ret == EOF) {
error_setg_errno(errp, errno, "failed to flush file"); error_setg_errno(errp, errno, "failed to flush file");
} else {
gfh->state = RW_STATE_NEW;
} }
} }

View File

@ -382,7 +382,7 @@ done:
} }
GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
int64_t whence, Error **errp) int64_t whence_code, Error **errp)
{ {
GuestFileHandle *gfh; GuestFileHandle *gfh;
GuestFileSeek *seek_data; GuestFileSeek *seek_data;
@ -390,11 +390,29 @@ GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
LARGE_INTEGER new_pos, off_pos; LARGE_INTEGER new_pos, off_pos;
off_pos.QuadPart = offset; off_pos.QuadPart = offset;
BOOL res; BOOL res;
int whence;
gfh = guest_file_handle_find(handle, errp); gfh = guest_file_handle_find(handle, errp);
if (!gfh) { if (!gfh) {
return NULL; 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; fh = gfh->fh;
res = SetFilePointerEx(fh, off_pos, &new_pos, whence); res = SetFilePointerEx(fh, off_pos, &new_pos, whence);
if (!res) { if (!res) {

View File

@ -15,6 +15,13 @@
#define QGA_READ_COUNT_DEFAULT 4096 #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 GAState GAState;
typedef struct GACommandState GACommandState; typedef struct GACommandState GACommandState;
extern GAState *ga_state; extern GAState *ga_state;

View File

@ -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"/> <File Id="qga_vss.tlb" Name="qga-vss.tlb" Source="$(env.BUILD_DIR)/qga/vss-win32/qga-vss.tlb" KeyPath="yes" DiskId="1"/>
</Component> </Component>
<?endif?> <?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}"> <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"/> <File Id="iconv.dll" Name="iconv.dll" Source="$(var.Mingw_bin)/iconv.dll" KeyPath="yes" DiskId="1"/>
</Component> </Component>
@ -148,6 +164,8 @@
<ComponentRef Id="qga_vss_dll" /> <ComponentRef Id="qga_vss_dll" />
<ComponentRef Id="qga_vss_tlb" /> <ComponentRef Id="qga_vss_tlb" />
<?endif?> <?endif?>
<ComponentRef Id="gspawn-helper-console" />
<ComponentRef Id="gspawn-helper" />
<ComponentRef Id="iconv" /> <ComponentRef Id="iconv" />
<ComponentRef Id="libgcc_arch_lib" /> <ComponentRef Id="libgcc_arch_lib" />
<ComponentRef Id="libglib" /> <ComponentRef Id="libglib" />

View File

@ -318,13 +318,13 @@
# #
# Seek to a position in the file, as with fseek(), and return the # Seek to a position in the file, as with fseek(), and return the
# current file position afterward. Also encapsulates ftell()'s # 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 # @handle: filehandle returned by guest-file-open
# #
# @offset: bytes to skip over in the file stream # @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. # Returns: @GuestFileSeek on success.
# #

View File

@ -13,6 +13,7 @@
#include "libqtest.h" #include "libqtest.h"
#include "config-host.h" #include "config-host.h"
#include "qga/guest-agent-core.h"
typedef struct { typedef struct {
char *test_dir; char *test_dir;
@ -352,10 +353,10 @@ static void test_qga_network_get_interfaces(gconstpointer fix)
static void test_qga_file_ops(gconstpointer fix) static void test_qga_file_ops(gconstpointer fix)
{ {
const TestFixture *fixture = fix; const TestFixture *fixture = fix;
const guchar helloworld[] = "Hello World!\n"; const unsigned char helloworld[] = "Hello World!\n";
const char *b64; const char *b64;
gchar *cmd, *path, *enc; gchar *cmd, *path, *enc;
guchar *dec; unsigned char *dec;
QDict *ret, *val; QDict *ret, *val;
int64_t id, eof; int64_t id, eof;
gsize count; gsize count;
@ -457,7 +458,7 @@ static void test_qga_file_ops(gconstpointer fix)
cmd = g_strdup_printf("{'execute': 'guest-file-seek'," cmd = g_strdup_printf("{'execute': 'guest-file-seek',"
" 'arguments': { 'handle': %" PRId64 ", " " 'arguments': { 'handle': %" PRId64 ", "
" 'offset': %d, 'whence': %d } }", " 'offset': %d, 'whence': %d } }",
id, 6, SEEK_SET); id, 6, QGA_SEEK_SET);
ret = qmp_fd(fixture->fd, cmd); ret = qmp_fd(fixture->fd, cmd);
qmp_assert_no_error(ret); qmp_assert_no_error(ret);
val = qdict_get_qdict(ret, "return"); val = qdict_get_qdict(ret, "return");
@ -496,6 +497,96 @@ static void test_qga_file_ops(gconstpointer fix)
g_free(cmd); 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) static void test_qga_get_time(gconstpointer fix)
{ {
const TestFixture *fixture = 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, g_test_add_data_func("/qga/get-memory-blocks", &fix,
test_qga_get_memory_blocks); 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-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/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/invalid-cmd", &fix, test_qga_invalid_cmd);
g_test_add_data_func("/qga/fsfreeze-status", &fix, g_test_add_data_func("/qga/fsfreeze-status", &fix,