Add function create_status_message() that allocates new VDAgentFileXferStatusMessage with optional error data. If an error occurs in function handle_start(), or handle_data(), create appropriate status message - in case of lack of free space, send message with VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE status and info about amount of available free space. When the client doesn't feature VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS cap, send general error without any additional data. This can be extended to reporting more file-xfer errors in the future, e.g. locked session. --- spice-protocol | 2 +- vdagent/file_xfer.cpp | 75 ++++++++++++++++++++++++++++----------------------- vdagent/file_xfer.h | 9 ++++--- vdagent/vdagent.cpp | 37 ++++++++++++++++++------- 4 files changed, 76 insertions(+), 47 deletions(-) diff --git a/spice-protocol b/spice-protocol index 666b5c5..e667501 160000 --- a/spice-protocol +++ b/spice-protocol @@ -1 +1 @@ -Subproject commit 666b5c5780acf3176a9cff61ad549d30bb1b9824 +Subproject commit e66750172cc409060b8c1575caef997bacb4e4df diff --git a/vdagent/file_xfer.cpp b/vdagent/file_xfer.cpp index e877cca..f75f4f0 100644 --- a/vdagent/file_xfer.cpp +++ b/vdagent/file_xfer.cpp @@ -51,8 +51,22 @@ FileXfer::~FileXfer() reset(); } -void FileXfer::handle_start(VDAgentFileXferStartMessage* start, - VDAgentFileXferStatusMessage* status) +uint32_t FileXfer::create_status_message(VDAgentFileXferStatusMessage** status, + uint32_t id, uint32_t xfer_status, + uint8_t* data, uint32_t data_size) +{ + *status = (VDAgentFileXferStatusMessage *) malloc(sizeof(VDAgentFileXferStatusMessage) + data_size); + (*status)->id = id; + (*status)->result = xfer_status; + + if (data) + memcpy((*status)->data, data, data_size); + + return sizeof(VDAgentFileXferStatusMessage) + data_size; +} + +uint32_t FileXfer::handle_start(VDAgentFileXferStartMessage* start, + VDAgentFileXferStatusMessage** status) { char* file_meta = (char*)start->data; TCHAR file_path[MAX_PATH]; @@ -64,31 +78,29 @@ void FileXfer::handle_start(VDAgentFileXferStartMessage* start, AsUser as_user; int wlen; - 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, sizeof(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; + return create_status_message(status, start->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0); } vd_printf("%u %s (%" PRIu64 ")", start->id, file_name, file_size); if (!as_user.begin()) { vd_printf("as_user failed"); - return; + return create_status_message(status, start->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0); } - if (FAILED(SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, file_path))) { vd_printf("failed getting desktop path"); - return; + return create_status_message(status, start->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0); } if (!GetDiskFreeSpaceEx(file_path, &free_bytes, NULL, NULL)) { vd_printf("failed getting disk free space %lu", GetLastError()); - return; + return create_status_message(status, start->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0); } if (free_bytes.QuadPart < file_size) { vd_printf("insufficient disk space %" PRIu64, free_bytes.QuadPart); - return; + return create_status_message(status, start->id, VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE, + (uint8_t *)&(free_bytes.QuadPart), sizeof(uint64_t)); } wlen = _tcslen(file_path); @@ -96,7 +108,7 @@ void FileXfer::handle_start(VDAgentFileXferStartMessage* start, // (1 char for separator, 1 char for filename and 1 char for NUL terminator) if (wlen + 3 >= MAX_PATH) { vd_printf("error: file too long %ls\\%s", file_path, file_name); - return; + return create_status_message(status, start->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0); } file_path[wlen++] = TEXT('\\'); @@ -118,7 +130,7 @@ void FileXfer::handle_start(VDAgentFileXferStartMessage* start, } if ((MultiByteToWideChar(CP_UTF8, 0, dest_filename, -1, file_path + wlen, MAX_PATH - wlen)) == 0) { vd_printf("failed converting file_name:%s to WideChar", dest_filename); - return; + return create_status_message(status, start->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0); } handle = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); if (handle != INVALID_HANDLE_VALUE) { @@ -129,28 +141,27 @@ void FileXfer::handle_start(VDAgentFileXferStartMessage* start, // it's a different error, there's not much we can do. if (GetLastError() != ERROR_FILE_EXISTS) { vd_printf("Failed creating %ls %lu", file_path, GetLastError()); - return; + return create_status_message(status, start->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0); } } if (handle == INVALID_HANDLE_VALUE) { vd_printf("Failed creating %ls. More than 63 copies exist?", file_path); - return; + return create_status_message(status, start->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0); } task = new FileXferTask(handle, file_size, file_path); _tasks[start->id] = task; - status->result = VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA; + return create_status_message(status, start->id, VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA, NULL, 0); } -bool FileXfer::handle_data(VDAgentFileXferDataMessage* data, - VDAgentFileXferStatusMessage* status) +uint32_t FileXfer::handle_data(VDAgentFileXferDataMessage* data, + VDAgentFileXferStatusMessage** status) { FileXferTasks::iterator iter; FileXferTask* task = NULL; DWORD written; + uint32_t status_size = 0; - 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); @@ -168,22 +179,25 @@ bool FileXfer::handle_data(VDAgentFileXferDataMessage* data, goto fin; } if (task->pos < task->size) { - return false; + return 0; } vd_printf("%u completed", iter->first); - status->result = VD_AGENT_FILE_XFER_STATUS_SUCCESS; + status_size = create_status_message(status, data->id, VD_AGENT_FILE_XFER_STATUS_SUCCESS, NULL, 0); fin: if (task) { CloseHandle(task->handle); - if (status->result != VD_AGENT_FILE_XFER_STATUS_SUCCESS) { + if (status_size > 0 && (*status)->result != VD_AGENT_FILE_XFER_STATUS_SUCCESS) { DeleteFile(task->name); } _tasks.erase(iter); delete task; } - return true; + if (status_size > 0) + return status_size; + else + return create_status_message(status, data->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0); } void FileXferTask::cancel() @@ -213,25 +227,20 @@ void FileXfer::handle_status(VDAgentFileXferStatusMessage* status) delete task; } -bool FileXfer::dispatch(VDAgentMessage* msg, VDAgentFileXferStatusMessage* status) +uint32_t 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; + return handle_start((VDAgentFileXferStartMessage*)msg->data, status); case VD_AGENT_FILE_XFER_DATA: - ret = handle_data((VDAgentFileXferDataMessage*)msg->data, status); - break; + return handle_data((VDAgentFileXferDataMessage*)msg->data, status); case VD_AGENT_FILE_XFER_STATUS: handle_status((VDAgentFileXferStatusMessage*)msg->data); - break; + return 0; default: vd_printf("unsupported message type %u size %u", msg->type, msg->size); } - return ret; + return 0; } //minimal parsers for GKeyFile, supporting only key=value with no spaces. diff --git a/vdagent/file_xfer.h b/vdagent/file_xfer.h index 25cd5c2..3fae6d3 100644 --- a/vdagent/file_xfer.h +++ b/vdagent/file_xfer.h @@ -43,12 +43,15 @@ typedef std::map<uint32_t, FileXferTask*> FileXferTasks; class FileXfer { public: ~FileXfer(); - bool dispatch(VDAgentMessage* msg, VDAgentFileXferStatusMessage* status); + uint32_t dispatch(VDAgentMessage* msg, VDAgentFileXferStatusMessage** status); + uint32_t create_status_message(VDAgentFileXferStatusMessage** status, + uint32_t id, uint32_t xfer_status, + uint8_t* data, uint32_t data_size); void reset(); private: - void handle_start(VDAgentFileXferStartMessage* start, VDAgentFileXferStatusMessage* status); - bool handle_data(VDAgentFileXferDataMessage* data, VDAgentFileXferStatusMessage* status); + uint32_t handle_start(VDAgentFileXferStartMessage* start, VDAgentFileXferStatusMessage** status); + uint32_t 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, unsigned vsize); diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp index c25a077..1c89684 100644 --- a/vdagent/vdagent.cpp +++ b/vdagent/vdagent.cpp @@ -1305,24 +1305,41 @@ void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port) res = handle_announce_capabilities((VDAgentAnnounceCapabilities*)msg->data, msg->size); break; case VD_AGENT_FILE_XFER_START: { - VDAgentFileXferStatusMessage status; + VDAgentFileXferStatusMessage *status = NULL; + uint32_t status_size; + if (_session_is_locked) { VDAgentFileXferStartMessage *s = (VDAgentFileXferStartMessage *)msg->data; - status.id = s->id; - status.result = VD_AGENT_FILE_XFER_STATUS_ERROR; - vd_printf("Fail to start file-xfer %u due: Locked session", status.id); - write_message(VD_AGENT_FILE_XFER_STATUS, sizeof(status), &status); - } else if (_file_xfer.dispatch(msg, &status)) { - write_message(VD_AGENT_FILE_XFER_STATUS, sizeof(status), &status); + status_size = _file_xfer.create_status_message(&status, s->id, VD_AGENT_FILE_XFER_STATUS_ERROR, NULL, 0); + vd_printf("Fail to start file-xfer %u due: Locked session", status->id); + } else { + status_size = _file_xfer.dispatch(msg, &status); } + if (status_size) { + if (status->result > VD_AGENT_FILE_XFER_STATUS_SUCCESS && + !has_capability(VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS)) { + status->result = VD_AGENT_FILE_XFER_STATUS_ERROR; + status_size = sizeof(VDAgentFileXferStatusMessage); + } + write_message(VD_AGENT_FILE_XFER_STATUS, status_size, status); + } + free(status); break; } 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); + VDAgentFileXferStatusMessage *status = NULL; + uint32_t status_size = _file_xfer.dispatch(msg, &status); + + if (status_size) { + if (status->result > VD_AGENT_FILE_XFER_STATUS_SUCCESS && + !has_capability(VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS)) { + status->result = VD_AGENT_FILE_XFER_STATUS_ERROR; + status_size = sizeof(VDAgentFileXferStatusMessage); + } + write_message(VD_AGENT_FILE_XFER_STATUS, status_size, status); } + free(status); break; } case VD_AGENT_CLIENT_DISCONNECTED: -- 2.13.0 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel