diff options
author | Arnon Gilboa <agilboa@redhat.com> | 2013-06-27 14:23:49 +0300 |
---|---|---|
committer | Arnon Gilboa <agilboa@redhat.com> | 2013-06-27 14:23:49 +0300 |
commit | c751e8fda0b9dfc056d9e738d1ff32274a75f8dc (patch) | |
tree | a37bd391bbf4cf42b63a73d2057cb6446108e647 /vdagent | |
parent | f7968d50350dd23ea9df2de807bad2cfbfb64844 (diff) |
vdagent: add support for client -> guest file transfers
rhbz#956146
Diffstat (limited to 'vdagent')
-rw-r--r-- | vdagent/file_xfer.cpp | 186 | ||||
-rw-r--r-- | vdagent/file_xfer.h | 51 | ||||
-rw-r--r-- | vdagent/vdagent.cpp | 11 | ||||
-rw-r--r-- | vdagent/vdagent.vcproj | 8 |
4 files changed, 256 insertions, 0 deletions
diff --git a/vdagent/file_xfer.cpp b/vdagent/file_xfer.cpp new file mode 100644 index 0000000..0550882 --- /dev/null +++ b/vdagent/file_xfer.cpp @@ -0,0 +1,186 @@ +/* + Copyright (C) 2013 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <shlobj.h> +#include "file_xfer.h" + +FileXfer::~FileXfer() +{ + FileXferTasks::iterator iter;
+ FileXferTask* task;
+
+ for (iter = _tasks.begin(); iter != _tasks.end(); iter++) { + task = iter->second; + CloseHandle(task->handle); + DeleteFileA(task->name); + delete task; + } +} + +void FileXfer::handle_start(VDAgentFileXferStartMessage* start, + VDAgentFileXferStatusMessage* status) +{ + char* file_meta = (char*)start->data; + char file_path[MAX_PATH], file_name[MAX_PATH]; + ULARGE_INTEGER free_bytes;
+ FileXferTask* task;
+ uint64_t file_size; + HANDLE handle;
+ + status->id = start->id; + status->result = VD_AGENT_FILE_XFER_STATUS_ERROR; + if (!g_key_get_string(file_meta, "vdagent-file-xfer", "name", file_name) || + !g_key_get_uint64(file_meta, "vdagent-file-xfer", "size", &file_size)) { + vd_printf("file id %u meta parsing failed", start->id); + return; + } + vd_printf("%u %s (%llu)", start->id, file_name, file_size); + if (FAILED(SHGetFolderPathA(NULL, CSIDL_COMMON_DESKTOPDIRECTORY | CSIDL_FLAG_CREATE, NULL,
+ SHGFP_TYPE_CURRENT, file_path))) { + vd_printf("failed getting desktop path"); + return; + } + if (!GetDiskFreeSpaceExA(file_path, &free_bytes, NULL, NULL)) { + vd_printf("failed getting disk free space %lu", GetLastError()); + return; + } + if (free_bytes.QuadPart < file_size) { + vd_printf("insufficient disk space %llu", free_bytes.QuadPart); + return; + } + strcat_s(file_path, MAX_PATH, "\\"); + strcat_s(file_path, MAX_PATH, file_name); + handle = CreateFileA(file_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
+ if (handle == INVALID_HANDLE_VALUE) {
+ vd_printf("failed creating %s %lu", file_path, GetLastError()); + return;
+ }
+ task = new FileXferTask(handle, file_size, file_path); + _tasks[start->id] = task;
+ status->result = VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA; +} + +bool FileXfer::handle_data(VDAgentFileXferDataMessage* data, + VDAgentFileXferStatusMessage* status) +{ + FileXferTasks::iterator iter;
+ FileXferTask* task;
+ DWORD written; + + status->id = data->id; + status->result = VD_AGENT_FILE_XFER_STATUS_ERROR; + iter = _tasks.find(data->id); + if (iter == _tasks.end()) { + vd_printf("file id %u not found", data->id); + goto fin; + } + task = iter->second; + task->pos += data->size; + if (task->pos > task->size) { + vd_printf("file xfer is longer than expected"); + goto fin; + } + if (!WriteFile(task->handle, data->data, (DWORD)data->size, + &written, NULL) || written != data->size) { + vd_printf("file write failed %lu", GetLastError()); + goto fin; + } + if (task->pos < task->size) { + return false; + } + vd_printf("%u completed", iter->first); + status->result = VD_AGENT_FILE_XFER_STATUS_SUCCESS; +fin: + CloseHandle(task->handle); + if (status->result != VD_AGENT_FILE_XFER_STATUS_SUCCESS) { + DeleteFileA(task->name); + } + _tasks.erase(iter); + delete task; + return true; +} + +void FileXfer::handle_status(VDAgentFileXferStatusMessage* status) +{ + FileXferTasks::iterator iter;
+ FileXferTask* task;
+ + vd_printf("id %u result %u", status->id, status->result); + if (status->result != VD_AGENT_FILE_XFER_STATUS_CANCELLED) { + vd_printf("only cancel is premitted"); + return; + } + iter = _tasks.find(status->id); + if (iter == _tasks.end()) { + vd_printf("file id %u not found", status->id); + return; + } + task = iter->second; + CloseHandle(task->handle); + DeleteFileA(task->name); + _tasks.erase(iter); + delete task; +} + +bool FileXfer::dispatch(VDAgentMessage* msg, VDAgentFileXferStatusMessage* status) +{ + bool ret = false; + + switch (msg->type) { + case VD_AGENT_FILE_XFER_START: + handle_start((VDAgentFileXferStartMessage*)msg->data, status); + ret = true; + break; + case VD_AGENT_FILE_XFER_DATA: + ret = handle_data((VDAgentFileXferDataMessage*)msg->data, status); + break; + case VD_AGENT_FILE_XFER_STATUS: + handle_status((VDAgentFileXferStatusMessage*)msg->data); + break; + default: + vd_printf("unsupported message type %u size %u", msg->type, msg->size); + } + return ret; +} + +//minimal parsers for GKeyFile, supporting only key=value with no spaces. +#define G_KEY_MAX_LEN 256 + +bool FileXfer::g_key_get_string(char* data, const char* group, const char* key, char* value) +{ + char group_pfx[G_KEY_MAX_LEN], key_pfx[G_KEY_MAX_LEN]; + char *group_pos, *key_pos, *next_group_pos; + + sprintf_s(group_pfx, sizeof(group_pfx), "[%s]", group); + if (!(group_pos = strstr((char*)data, group_pfx))) return false; + + sprintf_s(key_pfx, sizeof(key_pfx), "\n%s=", key); + if (!(key_pos = strstr(group_pos, key_pfx))) return false; + + next_group_pos = strstr(group_pos + strlen(group_pfx), "["); + if (next_group_pos && key_pos > next_group_pos) return false; + + return !!sscanf_s(key_pos + strlen(key_pfx), "%s\n", value); +} + +bool FileXfer::g_key_get_uint64(char* data, const char* group, const char* key, uint64_t* value) +{ + char str[G_KEY_MAX_LEN]; + + if (!g_key_get_string(data, group, key, str)) return false; + return !!sscanf_s(str, "%llu", value); +} diff --git a/vdagent/file_xfer.h b/vdagent/file_xfer.h new file mode 100644 index 0000000..f2f397c --- /dev/null +++ b/vdagent/file_xfer.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2013 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _H_FILE_XFER +#define _H_FILE_XFER + +#include <map> +#include "vdcommon.h" + +typedef struct ALIGN_VC FileXferTask { + FileXferTask(HANDLE _handle, uint64_t _size, char* _name): + handle(_handle), size(_size), pos(0) { strcpy_s(name, MAX_PATH, _name); } + HANDLE handle; + uint64_t size; + uint64_t pos; + char name[MAX_PATH]; +} ALIGN_GCC FileXferTask; + +typedef std::map<uint32_t, FileXferTask*> FileXferTasks; + +class FileXfer { +public: + ~FileXfer(); + bool dispatch(VDAgentMessage* msg, VDAgentFileXferStatusMessage* status); + +private: + void handle_start(VDAgentFileXferStartMessage* start, VDAgentFileXferStatusMessage* status); + bool handle_data(VDAgentFileXferDataMessage* data, VDAgentFileXferStatusMessage* status); + void handle_status(VDAgentFileXferStatusMessage* status); + bool g_key_get_string(char* data, const char* group, const char* key, char* value); + bool g_key_get_uint64(char* data, const char* group, const char* key, uint64_t* value); + +private: + FileXferTasks _tasks; +}; + +#endif diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp index af99961..e5567f3 100644 --- a/vdagent/vdagent.cpp +++ b/vdagent/vdagent.cpp @@ -18,6 +18,7 @@ #include "vdcommon.h" #include "desktop_layout.h" #include "display_setting.h" +#include "file_xfer.h" #include "ximage.h" #undef max #undef min @@ -147,6 +148,7 @@ private: bool _desktop_switch; DesktopLayout* _desktop_layout; DisplaySetting _display_setting; + FileXfer _file_xfer; HANDLE _vio_serial; OVERLAPPED _read_overlapped; OVERLAPPED _write_overlapped; @@ -1237,6 +1239,15 @@ void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port) case VD_AGENT_ANNOUNCE_CAPABILITIES: res = handle_announce_capabilities((VDAgentAnnounceCapabilities*)msg->data, msg->size); break; + case VD_AGENT_FILE_XFER_START: + case VD_AGENT_FILE_XFER_STATUS: + case VD_AGENT_FILE_XFER_DATA: { + VDAgentFileXferStatusMessage status; + if (_file_xfer.dispatch(msg, &status)) { + write_message(VD_AGENT_FILE_XFER_STATUS, sizeof(status), &status); + } + break; + } case VD_AGENT_CLIENT_DISCONNECTED: vd_printf("Client disconnected, agent to be restarted"); set_control_event(CONTROL_STOP); diff --git a/vdagent/vdagent.vcproj b/vdagent/vdagent.vcproj index ed8c58d..6943e5e 100644 --- a/vdagent/vdagent.vcproj +++ b/vdagent/vdagent.vcproj @@ -350,6 +350,10 @@ >
</File>
<File
+ RelativePath=".\file_xfer.cpp"
+ >
+ </File>
+ <File
RelativePath=".\vdagent.cpp"
>
</File>
@@ -376,6 +380,10 @@ >
</File>
<File
+ RelativePath=".\file_xfer.h"
+ >
+ </File>
+ <File
RelativePath=".\resource.h"
>
</File>
|