[Spice-devel] [PATCH spice-vdagent_win] file-xfer: Add support for reporting on free space

Jakub Janků janku.jakub.jj at gmail.com
Wed May 24 15:07:26 UTC 2017


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



More information about the Spice-devel mailing list