[Spice-devel] [PATCH spice-vdagent] file-xfer: Check free space before file transfer

Jakub Janků janku.jakub.jj at gmail.com
Wed May 3 14:27:30 UTC 2017


Add function get_free_space_available that retrieves amount of free
space in the given directory. The statvfs may fail even when there's
enough free space (e.g. when not supported by system), in this case
return G_MAXUINT64 so that the transfer isn't terminated groundlessly.

When the file is too big, send VDAgentFileXferStatusMessage with
result VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE and amount of free
space available, if the result isn't supported by client, send
VD_AGENT_FILE_XFER_STATUS_ERROR. Client then terminates the transfer.
---
 src/vdagent/file-xfers.c | 31 +++++++++++++++++++++++++++++++
 src/vdagentd/vdagentd.c  | 34 +++++++++++++++++++++++++++-------
 2 files changed, 58 insertions(+), 7 deletions(-)

diff --git a/src/vdagent/file-xfers.c b/src/vdagent/file-xfers.c
index b3937a4..49ccb87 100644
--- a/src/vdagent/file-xfers.c
+++ b/src/vdagent/file-xfers.c
@@ -32,6 +32,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <sys/types.h>
 #include <spice/vd_agent.h>
 #include <glib.h>
@@ -168,6 +169,18 @@ error:
     return NULL;
 }
 
+static uint64_t get_free_space_available(const char *path)
+{
+    struct statvfs stat;
+    int error = statvfs(path, &stat);
+    if (error != 0) {
+        syslog(LOG_WARNING, "file-xfer: failed to get free space, statvfs error %d", error);
+        return G_MAXUINT64;
+    } else {
+        return stat.f_bsize * stat.f_bavail;
+    }
+}
+
 void vdagent_file_xfers_start(struct vdagent_file_xfers *xfers,
     VDAgentFileXferStartMessage *msg)
 {
@@ -175,6 +188,7 @@ void vdagent_file_xfers_start(struct vdagent_file_xfers *xfers,
     char *dir = NULL, *path = NULL, *file_path = NULL;
     struct stat st;
     int i;
+    uint64_t free_space;
 
     g_return_if_fail(xfers != NULL);
 
@@ -193,6 +207,23 @@ void vdagent_file_xfers_start(struct vdagent_file_xfers *xfers,
 
     file_path = g_build_filename(xfers->save_dir, task->file_name, NULL);
 
+    free_space = get_free_space_available(xfers->save_dir);
+    if (task->file_size > free_space) {
+        syslog(LOG_ERR, "file-xfer: not enough free space (%lu B to copy, %lu B free)",
+               task->file_size, free_space);
+
+        udscs_write(xfers->vdagentd,
+                    VDAGENTD_FILE_XFER_STATUS,
+                    msg->id,
+                    VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE,
+                    (uint8_t *)&free_space,
+                    sizeof(free_space));
+        vdagent_file_xfer_task_free(task);
+        g_free(file_path);
+        g_free(dir);
+        return;
+    }
+
     dir = g_path_get_dirname(file_path);
     if (g_mkdir_with_parents(dir, S_IRWXU) == -1) {
         syslog(LOG_ERR, "file-xfer: Failed to create dir %s", dir);
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index f3ac606..c2da249 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -903,17 +903,37 @@ static void agent_read_complete(struct udscs_connection **connp,
         }
         break;
     case VDAGENTD_FILE_XFER_STATUS:{
-        VDAgentFileXferStatusMessage status;
-        status.id = GUINT32_TO_LE(header->arg1);
-        status.result = GUINT32_TO_LE(header->arg2);
+        VDAgentFileXferStatusMessage *status;
+        int status_size = sizeof(*status);
+        uint32_t result = GUINT32_TO_LE(header->arg2);
+
+        if (result == VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE &&
+            !VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
+                                     VD_AGENT_CAP_FILE_XFER_FREE_SPACE)) {
+            result = VD_AGENT_FILE_XFER_STATUS_ERROR;
+        }
+
+        if (result == VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE) {
+            status_size += sizeof(uint64_t);
+            status = malloc(status_size);
+            status->data[0] = *((uint64_t *)data);
+        } else {
+            status = malloc(status_size);
+        }
+
+        status->result = result;
+        status->id = GUINT32_TO_LE(header->arg1);
         vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
                                   VD_AGENT_FILE_XFER_STATUS, 0,
-                                  (uint8_t *)&status, sizeof(status));
-        if (status.result == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
-            g_hash_table_insert(active_xfers, GUINT_TO_POINTER(status.id),
+                                  (uint8_t *)status, status_size);
+
+        if (status->result == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
+            g_hash_table_insert(active_xfers, GUINT_TO_POINTER(status->id),
                                 *connp);
         else
-            g_hash_table_remove(active_xfers, GUINT_TO_POINTER(status.id));
+            g_hash_table_remove(active_xfers, GUINT_TO_POINTER(status->id));
+
+        free(status);
         break;
     }
 
-- 
2.12.2



More information about the Spice-devel mailing list