[Spice-devel] [PATCH win-vdagent v2] Support file transfer for existing files

Jonathon Jongsma jjongsma at redhat.com
Tue May 2 19:48:40 UTC 2017


On Fri, 2017-04-28 at 05:55 -0400, Frediano Ziglio wrote:
> > 
> > On Thu, 2017-04-27 at 15:20 -0500, Jonathon Jongsma wrote:
> > > Previously, if the user attempted to transfer a file that had the
> > > same
> > > name as a file that already exists on the guest, we would just
> > > fail
> > > the
> > > transfer. This patch tries to match the behavior of the linux
> > > vdagent
> > > where we try to append an integer onto the name of the new file
> > > to
> > > make
> > > it unique. For example, if you tried to transfer 'file.doc' to
> > > the
> > > guest
> > > and that file already existed, it would try to create 'file
> > > (1).doc'
> > > instead. If that also failed, it would attempt 'file (2).doc',
> > > etc,
> > > up
> > > to 63.
> > > 
> > > Resolves: rhbz#1410181
> > > Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
> > > ---
> > > Changes since v1:
> > >  - change from "file.doc (1)" to "file (1).doc"
> > >  - retain debug output for other CreateFile errors
> > > 
> > >  vdagent/file_xfer.cpp | 50
> > > ++++++++++++++++++++++++++++++++++++++++++++------
> > >  1 file changed, 44 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/vdagent/file_xfer.cpp b/vdagent/file_xfer.cpp
> > > index f763ed3..24eb00a 100644
> > > --- a/vdagent/file_xfer.cpp
> > > +++ b/vdagent/file_xfer.cpp
> > > @@ -61,6 +61,7 @@ void
> > > FileXfer::handle_start(VDAgentFileXferStartMessage* start,
> > >      HANDLE handle;
> > >      AsUser as_user;
> > >      int wlen;
> > > +    int attempt = 0;
> > >  
> > >      status->id = start->id;
> > >      status->result = VD_AGENT_FILE_XFER_STATUS_ERROR;
> > > @@ -99,15 +100,52 @@ void
> > > FileXfer::handle_start(VDAgentFileXferStartMessage* start,
> > >  
> > >      file_path[wlen++] = TEXT('\\');
> > >      file_path[wlen] = TEXT('\0');
> > > -    if((wlen = MultiByteToWideChar(CP_UTF8, 0, file_name, -1,
> > > file_path + wlen, MAX_PATH - wlen)) == 0){
> > > -        vd_printf("failed converting file_name:%s to WideChar",
> > > file_name);
> > > -        return;
> > > +
> > > +    const int MAX_ATTEMPTS = 64; // matches behavior of linux
> > > vdagent
> > > +    const size_t POSTFIX_LEN = 6; // up to 2 digits in
> > > parentheses
> > > and final NULL: " (xx)"
> > > +    size_t name_len = strlen(file_name);
> > > +    char *extension = strrchr(file_name, '.');
> > > +
> > > +    for (attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
> > > +        char dest_filename[MAX_PATH];
> > > +        if (attempt == 0) {
> > > +            strcpy(dest_filename, file_name);
> > > +        } else {
> > > +            if (name_len + POSTFIX_LEN > MAX_PATH) {
> > > +                vd_printf("failed creating %ls. Postfix makes
> > > name
> > > too long for the buffer.", file_path);
> > > +                return;
> > > +            }
> > > +
> > > +            int basename_len = (extension != NULL) ? (extension
> > > -
> > > file_name) : name_len;
> > > +            memcpy(dest_filename, file_name, basename_len);
> > > +            // append postfix
> > > +            int postfix_len = snprintf(dest_filename +
> > > basename_len, POSTFIX_LEN, " (%d)", attempt);
> > > +            if (extension != NULL) {
> > > +                strcpy(dest_filename + basename_len +
> > > postfix_len,
> > > extension);
> > > +            }
> > > +        }
> > > +        if((MultiByteToWideChar(CP_UTF8, 0, dest_filename, -1,
> > 
> > missing space^
> > > file_path + wlen, MAX_PATH - wlen)) == 0){
> > 
> >                                   and here ^
> > > +            vd_printf("failed converting file_name:%s to
> > > WideChar",
> > > dest_filename);
> > > +            return;
> > > +        }
> > > +        handle = CreateFile(file_path, GENERIC_WRITE, 0, NULL,
> > > CREATE_NEW, 0, NULL);
> > > +        if (handle != INVALID_HANDLE_VALUE) {
> > > +            break;
> > > +        }
> > > +
> > > +        // If the file already exists, we can re-try with a new
> > > filename. If
> > > +        // 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;
> > > +        }
> > >      }
> > > -    handle = CreateFile(file_path, GENERIC_WRITE, 0, NULL,
> > > CREATE_NEW, 0, NULL);
> > > -    if (handle == INVALID_HANDLE_VALUE) {
> > > -        vd_printf("failed creating %ls %lu", file_path,
> > > GetLastError());
> > > +
> > > +    if (attempt == MAX_ATTEMPTS) {
> > > +        vd_printf("Failed creating %ls. More than 63 copies
> > > exist?", file_path);
> > >          return;
> > >      }
> > > +
> > >      task = new FileXferTask(handle, file_size, file_path);
> > >      _tasks[start->id] = task;
> > >      status->result = VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA;
> > 
> > looks good, ack
> > 
> > Pavel
> > 
> 
> I would propose a version like
> 
> 
> diff --git a/vdagent/file_xfer.cpp b/vdagent/file_xfer.cpp
> index f763ed3..de1aea1 100644
> --- a/vdagent/file_xfer.cpp
> +++ b/vdagent/file_xfer.cpp
> @@ -28,6 +28,8 @@
>  #endif // compiler specific definitions
>  
>  #include <stdio.h>
> +#include <spice/macros.h>
> +
>  #include "file_xfer.h"
>  #include "as_user.h"
>  
> @@ -99,13 +101,40 @@ void
> FileXfer::handle_start(VDAgentFileXferStartMessage* start,
>  
>      file_path[wlen++] = TEXT('\\');
>      file_path[wlen] = TEXT('\0');
> -    if((wlen = MultiByteToWideChar(CP_UTF8, 0, file_name, -1,
> file_path + wlen, MAX_PATH - wlen)) == 0){
> -        vd_printf("failed converting file_name:%s to WideChar",
> file_name);
> -        return;
> +
> +    const int MAX_ATTEMPTS = 64; // matches behavior of linux
> vdagent
> +    const size_t POSTFIX_LEN = 6; // up to 2 digits in parentheses
> and final NULL: " (xx)"
> +    char *extension = strrchr(file_name, '.');
> +    if (!extension)
> +        extension = strchr(file_name, 0);
> +
> +    for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
> +        char dest_filename[SPICE_N_ELEMENTS(file_name) +
> POSTFIX_LEN];
> +        if (attempt == 0) {
> +            strcpy(dest_filename, file_name);
> +        } else {
> +            sprintf(dest_filename, "%.*s (%d)%s", int(extension -
> file_name), file_name,
> +                    attempt, extension);
> +        }
> +        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;
> +        }
> +        handle = CreateFile(file_path, GENERIC_WRITE, 0, NULL,
> CREATE_NEW, 0, NULL);
> +        if (handle != INVALID_HANDLE_VALUE) {
> +            break;
> +        }
> +
> +        // If the file already exists, we can re-try with a new
> filename. If
> +        // 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;
> +        }
>      }
> -    handle = CreateFile(file_path, GENERIC_WRITE, 0, NULL,
> CREATE_NEW, 0, NULL);
> +
>      if (handle == INVALID_HANDLE_VALUE) {
> -        vd_printf("failed creating %ls %lu", file_path,
> GetLastError());
> +        vd_printf("Failed creating %ls. More than 63 copies exist?",
> file_path);
>          return;
>      }
>      task = new FileXferTask(handle, file_size, file_path);
> 
> 
> is more similar to the way is proposed in Unix version of the agent


I agree that this is a nicer approach and is more consistent with the
linux agent. ACK

Jonathon



More information about the Spice-devel mailing list