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

Pavel Grunt pgrunt at redhat.com
Wed May 17 11:48:05 UTC 2017


Hi,

On Wed, 2017-05-10 at 22:34 +0200, Jakub Janků wrote:
> 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.
> 
> Add send_file_xfer_status_detailed() which can send file xfer status
> messages with additional (error) data, could be used for reporting
> more file xfer errors.
> ---
>  src/vdagent/file-xfers.c | 34 ++++++++++++++++++++++++
>  src/vdagentd/vdagentd.c  | 68 +++++++++++++++++++++++++++++++++++
> -------------
>  2 files changed, 84 insertions(+), 18 deletions(-)
> 
> diff --git a/src/vdagent/file-xfers.c b/src/vdagent/file-xfers.c
> index b3937a4..3ed139a 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,17 @@ error:
>      return NULL;
>  }
>  
> +static uint64_t get_free_space_available(const char *path)
> +{
> +    struct statvfs stat;
> +    if (statvfs(path, &stat) != 0) {
> +        syslog(LOG_WARNING, "file-xfer: failed to get free space,
> statvfs error: %s",
> +               strerror(errno));
> +        return G_MAXUINT64;
> +    }
> +    return stat.f_bsize * stat.f_bavail;
> +}
> +
>  void vdagent_file_xfers_start(struct vdagent_file_xfers *xfers,
>      VDAgentFileXferStartMessage *msg)
>  {
> @@ -175,6 +187,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 +206,27 @@ 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) {
> +        gchar *free_space_str = g_format_size(free_space);

g_format_size is available since GLIB 2.30, however vdagent requires
2.28. You can simplify the error or use the GLIB_CHECK_VERSION macro
to compile the code conditionally.

> +        gchar *file_size_str = g_format_size(task->file_size);
> +        syslog(LOG_ERR, "file-xfer: not enough free space (%s to
> copy, %s free)",
> +               file_size_str, free_space_str);
> +        g_free(free_space_str);
> +        g_free(file_size_str);
> +
> +        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..39fdb04 100644
> --- a/src/vdagentd/vdagentd.c
> +++ b/src/vdagentd/vdagentd.c
> @@ -301,20 +301,45 @@ static void do_client_clipboard(struct
> vdagent_virtio_port *vport,
>                  data, size);
>  }
>  
> +static void send_file_xfer_status_detailed(struct
> vdagent_virtio_port *vport,
> +                                           const char *msg,
> +                                           uint32_t id, uint32_t
> xfer_status,
> +                                           uint8_t *data, uint32_t
> data_size)
> +{
> +    VDAgentFileXferStatusMessage *status;
> +
> +    /* Replace new detailed errors with older generic
> VD_AGENT_FILE_XFER_STATUS_ERROR
> +     * when not supported by client */
> +    if (xfer_status > VD_AGENT_FILE_XFER_STATUS_SUCCESS &&
> +        !VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
> +                                VD_AGENT_CAP_FILE_XFER_DETAILED_ERR
> ORS)) {
> +        xfer_status = VD_AGENT_FILE_XFER_STATUS_ERROR;
> +        data_size = 0;
> +    }
> +
> +    status = malloc(sizeof(*status) + data_size);
> +    status->id = GUINT32_TO_LE(id);
> +    status->result = GUINT32_TO_LE(xfer_status);
> +    if (data)
> +        memcpy(status->data, data, data_size);
> +
> +    if (msg)
> +        syslog(LOG_WARNING, msg, id);

I see you copied it, but in general it requires msg to always have
"%u". Imho is better to always prefix the msg with "file-xfer %u:". Or
to support format strings properly (overkill imo).

> +
> +    if (vport)
> +        vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
> +                                  VD_AGENT_FILE_XFER_STATUS, 0,
> +                                  (uint8_t *)status,
> sizeof(*status) + data_size);
> +
> +    free(status);                            
> +}
> +
>  /* To be used by vdagentd for failures in file-xfer such as when
> file-xfer was
>   * cancelled or an error happened */
>  static void send_file_xfer_status(struct vdagent_virtio_port
> *vport,
>                                    const char *msg, uint32_t id,
> uint32_t xfer_status)
>  {
> -    VDAgentFileXferStatusMessage status = {
> -        .id = GUINT32_TO_LE(id),
> -        .result = GUINT32_TO_LE(xfer_status),
> -    };
> -    syslog(LOG_WARNING, msg, id);
> -    if (vport)
> -        vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
> -                                  VD_AGENT_FILE_XFER_STATUS, 0,
> -                                  (uint8_t *)&status,
> sizeof(status));
> +    send_file_xfer_status_detailed(vport, msg, id, xfer_status,
> NULL, 0);
>  }
>  
>  static void do_client_file_xfer(struct vdagent_virtio_port *vport,
> @@ -903,17 +928,24 @@ 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);
> -        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),
> +        switch (header->arg2) {
> +            case VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE:
> +                send_file_xfer_status_detailed(virtio_port, "Not
> enough free space. "
> +                                               "Cancelling client
> file-xfer request %u",
> +                                               header->arg1,
> header->arg2,
> +                                               data,
> sizeof(uint64_t));
> +                break;
> +            default:
> +                send_file_xfer_status_detailed(virtio_port, NULL,
> header->arg1, header->arg2,
> +                                               NULL, 0);
You can use send_file_xfer_status() here.

> +        }
> +
> +        if (header->arg2 ==
> VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
> +            g_hash_table_insert(active_xfers,
> GUINT_TO_POINTER(GUINT32_TO_LE(header->arg1)),
>                                  *connp);
>          else
> -            g_hash_table_remove(active_xfers,
> GUINT_TO_POINTER(status.id));
> +            g_hash_table_remove(active_xfers,
> GUINT_TO_POINTER(GUINT32_TO_LE(header->arg1)));
> +
>          break;
>      }
>  

Take a look at the send_file_xfer_status() usage, it suggests other
status results for reporting to the client.

Thanks,
Pavel



More information about the Spice-devel mailing list