[rfc] Remote file transfer support

Pierre Muller muller@ics.u-strasbg.fr
Mon Dec 3 09:51:00 GMT 2007


  Daniel,

  your patch breaks compilation of gdbserver on mingw32
host.
  The problem is that pread and pwrite do not seem
to be implemented:
gcc -Wall -g -O2    -I. -I../../../purecvs/gdb/gdbserver
-I../../../purecvs/gdb/gdbserver/../regformats
-I../../../purecvs/gdb/gdbserver/../../include -I../../bfd
-I../../../purecvs/gdb/gdbserver/../../bfd   -o gdbserver.exe inferiors.o
regcache.o remote-utils.o server.o signals.o target.o utils.o version.o
mem-break.o hostio.o  reg-i386.o win32-low.o win32-i386-low.o  \
           -lwsock32
hostio.o: In function
`handle_vFile':c:/cygwin/usr/local/src/cvs/build-mingw/gdb/gdbserver/../../.
./purecvs/gdb/gdbserver/hostio.c:380: undefined reference to `pread'
:c:/cygwin/usr/local/src/cvs/build-mingw/gdb/gdbserver/../../../purecvs/gdb/
gdbserver/hostio.c:422: undefined reference to `pwrite'
collect2: ld returned 1 exit status
make[2]: *** [gdbserver.exe] Error 1

  Can this be fixed?

Pierre

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Daniel Jacobowitz
> Sent: Monday, October 29, 2007 9:31 PM
> To: gdb-patches@sourceware.org
> Subject: [rfc] Remote file transfer support
> 
> The description of this patch was just posted as an RFC on gdb@:
>   http://sourceware.org/ml/gdb/2007-10/msg00287.html
> 
> Here's the code itself, tested on x86_64-linux.  I won't do anything
> else with this until I've waited for feedback on the protocol /
> commands (but comments on the patch are still welcome if you feel
> like reading through it :-).  Test cases are included.
> 
> I briefly implemented a gdbserver command line option to enable /
> disable file transfer, but I took it out of the final patch.  I
> don't think it's useful as any sort of security measure; if you
> can connect to a running gdbserver, you can do anything the debugged
> process could do.  This just makes it easier to do so.
> 
> --
> Daniel Jacobowitz
> CodeSourcery
> 
> 2007-10-29  Daniel Jacobowitz  <dan@codesourcery.com>
> 
> 	* remote.c (remote_cmdlist): New variable.
> 	(PACKET_vFile_open, PACKET_vFile_pread, PACKET_vFile_pwrite)
> 	(PACKET_vFile_close, PACKET_vFile_unlink): New constants.
> 	(remote_buffer_add_string, remote_buffer_add_bytes)
> 	(remote_buffer_add_int, remote_hostio_parse_result)
> 	(remote_hostio_send_command, remote_hostio_open,
> remote_hostio_pwrite)
> 	(remote_hostio_pread, remote_hostio_close, remote_hostio_unlink)
> 	(remote_fileio_errno_to_host, remote_hostio_error,
> fclose_cleanup)
> 	(remote_file_put, remote_file_get, remote_file_delete)
> 	(remote_put_command, remote_get_command, remote_delete_command)
> 	(remote_command): New functions.
> 	(_initialize_remote): Register new packets and commands.
> 	* Makefile.in (gdb_fileio_h): New variable.
> 	(remote.o): Update.
> 	(SUBDIR_MI_OBS): Add mi-cmd-target.o.
> 	(SUBDIR_MI_SRCS): Add mi/mi-cmd-target.c.
> 	(mi-cmd-target.o): New rule.
> 	* mi/mi-cmd-target.c: New file.
> 	* mi/mi-cmds.c (mi_cmds): Add target-file-delete, target-file-
> get,
> 	and target-file-put.
> 	* mi/mi-cmds.h (mi_cmd_target_file_get, mi_cmd_target_file_put)
> 	(mi_cmd_target_file_delete): Declare.
> 	* remote.h (remote_file_put, remote_file_get,
> remote_file_delete):
> 	Declare.
> 	* NEWS: Describe new file transfer support.
> 
> 2007-10-29  Daniel Jacobowitz  <dan@codesourcery.com>
> 
> 	* gdb.texinfo (Remote Debugging): Add File Transfer section.
> 	(Remote Configuration): Document Host I/O packets.
> 	(GDB/MI): Add GDB/MI File Transfer Commands section.
> 	(Remote Protocol): Add Host I/O Packets section.
> 	(Packets): Add vFile.
> 
> 2007-10-29  Daniel Jacobowitz  <dan@codesourcery.com>
> 
> 	* Makefile.in (OBS): Add hostio.o.
> 	(hostio.o): New rule.
> 	* server.h (handle_vFile): Declare.
> 	* hostio.c: New file.
> 	* server.c (handle_v_requests): Take packet_len and
> new_packet_len
> 	for binary packets.  Call handle_vFile.
> 	(main): Update call to handle_v_requests.
> 
> 2007-10-29  Daniel Jacobowitz  <dan@codesourcery.com>
> 
> 	* gdb.server/file-transfer.exp, gdb.server/transfer.txt,
> 	gdb.mi/mi-file-transfer.exp: New.
> 
> ---
>  gdb/Makefile.in                            |   11
>  gdb/NEWS                                   |   26 +
>  gdb/doc/gdb.texinfo                        |  235 ++++++++++
>  gdb/gdbserver/Makefile.in                  |    3
>  gdb/gdbserver/hostio.c                     |  517
> ++++++++++++++++++++++
>  gdb/gdbserver/server.c                     |   11
>  gdb/gdbserver/server.h                     |    3
>  gdb/mi/mi-cmd-target.c                     |  100 ++++
>  gdb/mi/mi-cmds.c                           |    3
>  gdb/mi/mi-cmds.h                           |    3
>  gdb/remote.c                               |  653
> +++++++++++++++++++++++++++++
>  gdb/remote.h                               |    6
>  gdb/testsuite/gdb.mi/mi-file-transfer.exp  |   99 ++++
>  gdb/testsuite/gdb.server/file-transfer.exp |   80 +++
>  gdb/testsuite/gdb.server/transfer.txt      |   12
>  15 files changed, 1756 insertions(+), 6 deletions(-)
> 
> Index: gdb/doc/gdb.texinfo
> ===================================================================
> --- gdb/doc/gdb.texinfo.orig	2007-10-26 09:50:44.000000000 -0400
> +++ gdb/doc/gdb.texinfo	2007-10-29 15:18:09.000000000 -0400
> @@ -12615,6 +12615,7 @@ configuration of @value{GDBN}; use @code
> 
>  @menu
>  * Connecting::                  Connecting to a remote target
> +* File Transfer::               Sending files to a remote system
>  * Server::	                Using the gdbserver program
>  * Remote Configuration::        Remote configuration
>  * Remote Stub::                 Implementing a remote stub
> @@ -12762,6 +12763,37 @@ can add new commands that only the exter
>  and implement.
>  @end table
> 
> +@node File Transfer
> +@section Sending files to a remote system
> +@cindex remote target, file transfer
> +@cindex file transfer
> +
> +Some remote targets offer the ability to transfer files over the same
> +connection used to communicate with @value{GDBN}.  This is convenient
> +for targets accessible through other means, e.g.@: GNU/Linux systems
> +running @code{gdbserver} over a network interface.  For other targets,
> +e.g.@: embedded devices with only a single serial port, this may be
> +the only way to upload or download files.
> +
> +Not all remote targets support these commands.
> +
> +@table @code
> +@kindex remote put
> +@item remote put @var{hostfile} @var{targetfile}
> +Copy @var{hostfile} from the host system (the machine running
> +@value{GDBN}) to the path @var{targetfile} on the target system.
> +
> +@kindex remote get
> +@item remote get @var{targetfile} @var{hostfile}
> +Copy @var{targetfile} from the target system to @var{hostfile}
> +on the host system.
> +
> +@kindex remote delete
> +@item remote delete @var{targetfile}
> +Delete @var{targetfile} from the target system.
> +
> +@end table
> +
>  @node Server
>  @section Using the @code{gdbserver} Program
> 
> @@ -13098,6 +13130,25 @@ are:
>  @tab @code{QPassSignals}
>  @tab @code{handle @var{signal}}
> 
> +@item @code{hostio-close-packet}
> +@tab @code{vFile:close}
> +@tab @code{remote get}, @code{remote put}
> +
> +@item @code{hostio-open-packet}
> +@tab @code{vFile:open}
> +@tab @code{remote get}, @code{remote put}
> +
> +@item @code{hostio-pread-packet}
> +@tab @code{vFile:pread}
> +@tab @code{remote get}, @code{remote put}
> +
> +@item @code{hostio-pwrite-packet}
> +@tab @code{vFile:pwrite}
> +@tab @code{remote get}, @code{remote put}
> +
> +@item @code{hostio-unlink-packet}
> +@tab @code{vFile:unlink}
> +@tab @code{remote delete}
>  @end multitable
> 
>  @node Remote Stub
> @@ -17248,6 +17299,7 @@ may repeat one or more times.
>  * GDB/MI Signal Handling Commands::
>  @end ignore
>  * GDB/MI Target Manipulation::
> +* GDB/MI File Transfer Commands::
>  * GDB/MI Miscellaneous Commands::
>  @end menu
> 
> @@ -21195,6 +21247,88 @@ The corresponding @value{GDBN} command i
>  @end smallexample
> 
>  @c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION
> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
> +@node GDB/MI File Transfer Commands
> +@section @sc{gdb/mi} File Transfer Commands
> +
> +
> +@subheading The @code{-target-file-put} Command
> +@findex -target-file-put
> +
> +@subsubheading Synopsis
> +
> +@smallexample
> + -target-file-put @var{hostfile} @var{targetfile}
> +@end smallexample
> +
> +Copy @var{hostfile} from the host system (the machine running
> +@value{GDBN}) to the path @var{targetfile} on the target system.
> +
> +@subsubheading @value{GDBN} Command
> +
> +The corresponding @value{GDBN} command is @samp{remote put}.
> +
> +@subsubheading Example
> +
> +@smallexample
> +(gdb)
> +-target-file-put localfile remotefile
> +^done
> +(gdb)
> +@end smallexample
> +
> +
> +@subheading The @code{-target-file-put} Command
> +@findex -target-file-get
> +
> +@subsubheading Synopsis
> +
> +@smallexample
> + -target-file-get @var{targetfile} @var{hostfile}
> +@end smallexample
> +
> +Copy @var{targetfile} from the target system to @var{hostfile}
> +on the host system.
> +
> +@subsubheading @value{GDBN} Command
> +
> +The corresponding @value{GDBN} command is @samp{remote get}.
> +
> +@subsubheading Example
> +
> +@smallexample
> +(gdb)
> +-target-file-get remotefile localfile
> +^done
> +(gdb)
> +@end smallexample
> +
> +
> +@subheading The @code{-target-file-delete} Command
> +@findex -target-file-delete
> +
> +@subsubheading Synopsis
> +
> +@smallexample
> + -target-file-delete @var{targetfile}
> +@end smallexample
> +
> +Delete @var{targetfile} from the target system.
> +
> +@subsubheading @value{GDBN} Command
> +
> +The corresponding @value{GDBN} command is @samp{remote delete}.
> +
> +@subsubheading Example
> +
> +@smallexample
> +(gdb)
> +-target-file-delete remotefile
> +^done
> +(gdb)
> +@end smallexample
> +
> +
> +@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION
> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>  @node GDB/MI Miscellaneous Commands
>  @section Miscellaneous @sc{gdb/mi} Commands
> 
> @@ -22819,6 +22953,7 @@ Show the current setting of the target w
>  * General Query Packets::
>  * Register Packet Format::
>  * Tracepoint Packets::
> +* Host I/O Packets::
>  * Interrupts::
>  * Examples::
>  * File-I/O Remote Protocol Extension::
> @@ -23322,6 +23457,11 @@ command in the @samp{vCont} packet.
>  The @samp{vCont} packet is not supported.
>  @end table
> 
> +@item vFile:@var{operation}:@var{parameter}@dots{}
> +@cindex @samp{vFile} packet
> +Perform a file operation on the target system.  For details,
> +@xref{Host I/O Packets}.
> +
>  @item vFlashErase:@var{addr},@var{length}
>  @cindex @samp{vFlashErase} packet
>  Direct the stub to erase @var{length} bytes of flash starting at
> @@ -24471,6 +24611,101 @@ There is a trace experiment running.
>  @end table
> 
> 
> +@node Host I/O Packets
> +@section Host I/O Packets
> +@cindex Host I/O, remote protocol
> +@cindex file transfer, remote protocol
> +
> +The @dfn{Host I/O} packets allow @value{GDBN} to perform I/O
> +operations on the far side of a remote link.  For example, Host I/O is
> +used to upload and download files to a remote target with its own
> +filesystem.  The protocol has some common features with the File-I/O
> +protocol, e.g.@: all constants and data structures are encoded in the
> +same way.  However, the packets are structured differently, because
> +requests are initiated by @value{GDBN}, and the target's memory is not
> +involved.  @xref{File-I/O Remote Protocol Extension}, for more details
> +on the target-initiated protocol.
> +
> +The Host I/O request packets all encode a single operation along with
> +its arguments.  They have this format:
> +
> +@table @samp
> +
> +@item vFile:@var{operation}: @var{parameter}@dots{}
> +@var{operation} is the name of the particular request; the target
> +should compare the entire packet name up to the second colon when
> checking
> +for a supported operation.  The format of @var{parameter} depends on
> +the operation.  Numbers are always passed in hexadecimal.  Negative
> +numbers have an explicit minus sign (i.e.@: two's complement is not
> +used).  Strings (e.g.@: filenames) are encoded as a series of
> +hexadecimal bytes.  The last argument to a system call may be a
> +buffer of escaped binary data (@pxref{Binary Data}).
> +
> +@end table
> +
> +The valid responses to Host I/O packets are:
> +
> +@table @samp
> +
> +@item F @var{result} [, @var{errno}] [; @var{attachment}]
> +@var{result} is the integer value returned by this operation, usually
> +non-negative for success and -1 for errors.  If an error has occured,
> +@var{errno} will be included in the result.  @var{errno} will have a
> +value defined by the File-I/O protocol (@pxref{Errno Values}).  For
> +operations which return data, @var{attachment} will be provided a
> +binary buffer.  Binary buffers in response packets are escaped in the
> +normal way (@pxref{Binary Data}).  See the individual packet
> +documentation for the interpretation of @var{result} and
> +@var{attachment}.
> +
> +@item
> +An empty response indicates that this operation is not recognized.
> +
> +@end table
> +
> +These are the supported Host I/O operations:
> +
> +@table @samp
> +@item vFile:open: @var{pathname}, @var{flags}, @var{mode}
> +Open a file at @var{pathname} and return a file descriptor for it, or
> +return -1 if an error occurs.  @var{pathname} is a string,
> +@var{flags} is an integer indicating a mask of open flags
> +(@pxref{Open Flags}), and @var{mode} is an integer indicating a mask
> +of mode bits to use if the file is created (@pxref{mode_t Values}).
> +
> +@item vFile:close: @var{fd}
> +Close the open file corresponding to @var{fd} and return 0, or
> +-1 if an error occurs.
> +
> +@item vFile:pread: @var{fd}, @var{count}, @var{offset}
> +Read data from the open file corresponding to @var{fd}.  Up to
> +@var{count} bytes will be read from the file, starting at @var{offset}
> +relative to the start of the file.  The target may read fewer bytes;
> +common reasons include packet size limits and an end-of-file
> +condition.  The number of bytes read is returned.  Zero should only be
> +returned for a successful read at the end of the file, or if
> +@var{count} was zero.
> +
> +The data read should be returned as a binary attachment on success,
> +even if zero bytes were read.  The return value is the number of
> +target bytes read; the binary attachment may be longer if some
> +characters were escaped.
> +
> +@item vFile:pwrite: @var{fd}, @var{offset}, @var{data}
> +Write @var{data} (a binary buffer) to the open file corresponding
> +to @var{fd}.  Start the write at @var{offset} from the start of the
> +file.  Unlike many @code{write} system calls, there is no
> +separate @var{count} argument; the length of @var{data} in the
> +packet is used.  @samp{vFile:write} returns the number of bytes
> written,
> +which may be shorter than the length of @var{data}, or -1 if an
> +error occurred.
> +
> +@item vFile:unlink: @var{pathname}
> +Delete the file at @var{pathname} on the target.  Return 0,
> +or -1 if an error occurs.  @var{pathname} is a string.
> +
> +@end table
> +
>  @node Interrupts
>  @section Interrupts
>  @cindex interrupts (remote protocol)
> Index: gdb/gdbserver/Makefile.in
> ===================================================================
> --- gdb/gdbserver/Makefile.in.orig	2007-10-26 09:50:44.000000000 -0400
> +++ gdb/gdbserver/Makefile.in	2007-10-26 09:50:57.000000000 -0400
> @@ -139,7 +139,7 @@ TAGFILES = $(SOURCES) ${HFILES} ${ALLPAR
> 
>  OBS = inferiors.o regcache.o remote-utils.o server.o signals.o
> target.o \
>  	utils.o version.o \
> -	mem-break.o \
> +	mem-break.o hostio.o \
>  	$(XML_BUILTIN) \
>  	$(DEPFILES)
>  GDBSERVER_LIBS = @GDBSERVER_LIBS@
> @@ -277,6 +277,7 @@ regcache_h = $(srcdir)/regcache.h
>  server_h = $(srcdir)/server.h $(regcache_h) config.h
> $(srcdir)/target.h \
>  		$(srcdir)/mem-break.h
> 
> +hostio.o: hostio.c $(server_h)
>  inferiors.o: inferiors.c $(server_h)
>  mem-break.o: mem-break.c $(server_h)
>  proc-service.o: proc-service.c $(server_h) $(gdb_proc_service_h)
> Index: gdb/gdbserver/server.h
> ===================================================================
> --- gdb/gdbserver/server.h.orig	2007-10-26 09:50:44.000000000 -0400
> +++ gdb/gdbserver/server.h	2007-10-26 14:18:32.000000000 -0400
> @@ -156,6 +156,9 @@ extern int pass_signals[];
> 
>  extern jmp_buf toplevel;
> 
> +/* Functions from hostio.c.  */
> +extern int handle_vFile (char *, int, int *);
> +
>  /* From remote-utils.c */
> 
>  extern int remote_debug;
> Index: gdb/gdbserver/hostio.c
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ gdb/gdbserver/hostio.c	2007-10-26 18:14:22.000000000 -0400
> @@ -0,0 +1,517 @@
> +/* Host file transfer support for gdbserver.
> +   Copyright (C) 2006
> +   Free Software Foundation, Inc.
> +
> +   Contributed by CodeSourcery.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or
> modify
> +   it under the terms of the GNU General Public License as published
> by
> +   the Free Software Foundation; either version 2 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program; if not, write to the Free Software
> +   Foundation, Inc., 51 Franklin Street, Fifth Floor,
> +   Boston, MA 02110-1301, USA.  */
> +
> +#include "server.h"
> +#include "gdb/fileio.h"
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +#include <unistd.h>
> +
> +extern int remote_debug;
> +
> +struct fd_list
> +{
> +  int fd;
> +  struct fd_list *next;
> +};
> +
> +static struct fd_list *open_fds;
> +
> +static int
> +safe_fromhex (char a, int *nibble)
> +{
> +  if (a >= '0' && a <= '9')
> +    *nibble = a - '0';
> +  else if (a >= 'a' && a <= 'f')
> +    *nibble = a - 'a' + 10;
> +  else if (a >= 'A' && a <= 'F')
> +    *nibble = a - 'A' + 10;
> +  else
> +    return -1;
> +
> +  return 0;
> +}
> +
> +static int
> +require_filename (char **pp, char *filename)
> +{
> +  int count;
> +  char *p;
> +
> +  p = *pp;
> +  count = 0;
> +
> +  while (*p && *p != ',')
> +    {
> +      int nib1, nib2;
> +
> +      /* Don't allow overflow.  */
> +      if (count >= PATH_MAX - 1)
> +	return -1;
> +
> +      if (safe_fromhex (p[0], &nib1)
> +	  || safe_fromhex (p[1], &nib2))
> +	return -1;
> +
> +      filename[count++] = nib1 * 16 + nib2;
> +      p += 2;
> +    }
> +
> +  filename[count] = '\0';
> +  *pp = p;
> +  return 0;
> +}
> +
> +static int
> +require_int (char **pp, int *value)
> +{
> +  char *p;
> +  int count;
> +
> +  p = *pp;
> +  *value = 0;
> +  count = 0;
> +
> +  while (*p && *p != ',')
> +    {
> +      int nib;
> +
> +      /* Don't allow overflow.  */
> +      if (count >= 7)
> +	return -1;
> +
> +      if (safe_fromhex (p[0], &nib))
> +	return -1;
> +      *value = *value * 16 + nib;
> +      p++;
> +      count++;
> +    }
> +
> +  *pp = p;
> +  return 0;
> +}
> +
> +static int
> +require_data (char *p, int p_len, char **data, int *data_len)
> +{
> +  int input_index, output_index, escaped;
> +
> +  *data = malloc (p_len);
> +
> +  output_index = 0;
> +  escaped = 0;
> +  for (input_index = 0; input_index < p_len; input_index++)
> +    {
> +      char b = p[input_index];
> +
> +      if (escaped)
> +	{
> +	  (*data)[output_index++] = b ^ 0x20;
> +	  escaped = 0;
> +	}
> +      else if (b == '}')
> +	escaped = 1;
> +      else
> +	(*data)[output_index++] = b;
> +    }
> +
> +  if (escaped)
> +    return -1;
> +
> +  *data_len = output_index;
> +  return 0;
> +}
> +
> +static int
> +require_comma (char **pp)
> +{
> +  if (**pp == ',')
> +    {
> +      (*pp)++;
> +      return 0;
> +    }
> +  else
> +    return -1;
> +}
> +
> +static int
> +require_end (char *p)
> +{
> +  if (*p == '\0')
> +    return 0;
> +  else
> +    return -1;
> +}
> +
> +static int
> +require_valid_fd (int fd)
> +{
> +  struct fd_list *fd_ptr;
> +
> +  for (fd_ptr = open_fds; fd_ptr != NULL; fd_ptr = fd_ptr->next)
> +    if (fd_ptr->fd == fd)
> +      return 0;
> +
> +  return -1;
> +}
> +
> +static int
> +errno_to_fileio_errno (int error)
> +{
> +  switch (error)
> +    {
> +    case EPERM:
> +      return FILEIO_EPERM;
> +    case ENOENT:
> +      return FILEIO_ENOENT;
> +    case EINTR:
> +      return FILEIO_EINTR;
> +    case EIO:
> +      return FILEIO_EIO;
> +    case EBADF:
> +      return FILEIO_EBADF;
> +    case EACCES:
> +      return FILEIO_EACCES;
> +    case EFAULT:
> +      return FILEIO_EFAULT;
> +    case EBUSY:
> +      return FILEIO_EBUSY;
> +    case EEXIST:
> +      return FILEIO_EEXIST;
> +    case ENODEV:
> +      return FILEIO_ENODEV;
> +    case ENOTDIR:
> +      return FILEIO_ENOTDIR;
> +    case EISDIR:
> +      return FILEIO_EISDIR;
> +    case EINVAL:
> +      return FILEIO_EINVAL;
> +    case ENFILE:
> +      return FILEIO_ENFILE;
> +    case EMFILE:
> +      return FILEIO_EMFILE;
> +    case EFBIG:
> +      return FILEIO_EFBIG;
> +    case ENOSPC:
> +      return FILEIO_ENOSPC;
> +    case ESPIPE:
> +      return FILEIO_ESPIPE;
> +    case EROFS:
> +      return FILEIO_EROFS;
> +    case ENOSYS:
> +      return FILEIO_ENOSYS;
> +    case ENAMETOOLONG:
> +      return FILEIO_ENAMETOOLONG;
> +    }
> +  return FILEIO_EUNKNOWN;
> +}
> +
> +static void
> +hostio_error (char *own_buf, int error)
> +{
> +  int fileio_error = errno_to_fileio_errno (error);
> +
> +  sprintf (own_buf, "F-1,%x", fileio_error);
> +}
> +
> +static void
> +hostio_packet_error (char *own_buf)
> +{
> +  hostio_error (own_buf, EINVAL);
> +}
> +
> +static void
> +hostio_reply (char *own_buf, int result)
> +{
> +  sprintf (own_buf, "F%x", result);
> +}
> +
> +static int
> +hostio_reply_with_data (char *own_buf, char *buffer, int len,
> +			int *new_packet_len)
> +{
> +  int input_index, output_index, out_maxlen;
> +
> +  sprintf (own_buf, "F%x;", len);
> +  output_index = strlen (own_buf);
> +
> +  out_maxlen = PBUFSIZ;
> +
> +  for (input_index = 0; input_index < len; input_index++)
> +    {
> +      char b = buffer[input_index];
> +
> +      if (b == '$' || b == '#' || b == '}' || b == '*')
> +	{
> +	  /* These must be escaped.  */
> +	  if (output_index + 2 > out_maxlen)
> +	    break;
> +	  own_buf[output_index++] = '}';
> +	  own_buf[output_index++] = b ^ 0x20;
> +	}
> +      else
> +	{
> +	  if (output_index + 1 > out_maxlen)
> +	    break;
> +	  own_buf[output_index++] = b;
> +	}
> +    }
> +
> +  *new_packet_len = output_index;
> +  return input_index;
> +}
> +
> +static int
> +fileio_open_flags_to_host (int fileio_open_flags, int *open_flags_p)
> +{
> +  int open_flags = 0;
> +
> +  if (fileio_open_flags & ~FILEIO_O_SUPPORTED)
> +    return -1;
> +
> +  if (fileio_open_flags & FILEIO_O_CREAT)
> +    open_flags |= O_CREAT;
> +  if (fileio_open_flags & FILEIO_O_EXCL)
> +    open_flags |= O_EXCL;
> +  if (fileio_open_flags & FILEIO_O_TRUNC)
> +    open_flags |= O_TRUNC;
> +  if (fileio_open_flags & FILEIO_O_APPEND)
> +    open_flags |= O_APPEND;
> +  if (fileio_open_flags & FILEIO_O_RDONLY)
> +    open_flags |= O_RDONLY;
> +  if (fileio_open_flags & FILEIO_O_WRONLY)
> +    open_flags |= O_WRONLY;
> +  if (fileio_open_flags & FILEIO_O_RDWR)
> +    open_flags |= O_RDWR;
> +/* On systems supporting binary and text mode, always open files in
> +   binary mode. */
> +#ifdef O_BINARY
> +  open_flags |= O_BINARY;
> +#endif
> +
> +  *open_flags_p = open_flags;
> +  return 0;
> +}
> +
> +static void
> +handle_open (char *own_buf)
> +{
> +  char filename[PATH_MAX];
> +  char *p;
> +  int fileio_flags, mode, flags, fd;
> +  struct fd_list *new_fd;
> +
> +  p = own_buf + strlen ("vFile:open:");
> +
> +  if (require_filename (&p, filename)
> +      || require_comma (&p)
> +      || require_int (&p, &fileio_flags)
> +      || require_comma (&p)
> +      || require_int (&p, &mode)
> +      || require_end (p)
> +      || fileio_open_flags_to_host (fileio_flags, &flags))
> +    {
> +      hostio_packet_error (own_buf);
> +      return;
> +    }
> +
> +  /* We do not need to convert MODE, since the fileio protocol
> +     uses the standard values.  */
> +  fd = open (filename, flags, mode);
> +
> +  if (fd == -1)
> +    {
> +      hostio_error (own_buf, errno);
> +      return;
> +    }
> +
> +  /* Record the new file descriptor.  */
> +  new_fd = malloc (sizeof (struct fd_list));
> +  new_fd->fd = fd;
> +  new_fd->next = open_fds;
> +  open_fds = new_fd;
> +
> +  hostio_reply (own_buf, fd);
> +}
> +
> +static void
> +handle_pread (char *own_buf, int *new_packet_len)
> +{
> +  int fd, ret, len, offset, bytes_sent;
> +  char *p, *data;
> +
> +  p = own_buf + strlen ("vFile:pread:");
> +
> +  if (require_int (&p, &fd)
> +      || require_comma (&p)
> +      || require_valid_fd (fd)
> +      || require_int (&p, &len)
> +      || require_comma (&p)
> +      || require_int (&p, &offset)
> +      || require_end (p))
> +    {
> +      hostio_packet_error (own_buf);
> +      return;
> +    }
> +
> +  data = malloc (len);
> +  ret = pread (fd, data, len, offset);
> +
> +  if (ret == -1)
> +    {
> +      hostio_error (own_buf, errno);
> +      free (data);
> +      return;
> +    }
> +
> +  bytes_sent = hostio_reply_with_data (own_buf, data, ret,
> new_packet_len);
> +
> +  /* If we were using read, and the data did not all fit in the reply,
> +     we would have to back up using lseek here.  With pread it does
> +     not matter.  But we still have a problem; the return value in the
> +     packet might be wrong, so we must fix it.  This time it will
> +     definitely fit.  */
> +  if (bytes_sent < ret)
> +    bytes_sent = hostio_reply_with_data (own_buf, data, bytes_sent,
> +					 new_packet_len);
> +
> +  free (data);
> +}
> +
> +static void
> +handle_pwrite (char *own_buf, int packet_len)
> +{
> +  int fd, ret, len, offset;
> +  char *p, *data;
> +
> +  p = own_buf + strlen ("vFile:pwrite:");
> +
> +  if (require_int (&p, &fd)
> +      || require_comma (&p)
> +      || require_valid_fd (fd)
> +      || require_int (&p, &offset)
> +      || require_comma (&p)
> +      || require_data (p, packet_len - (p - own_buf), &data, &len))
> +    {
> +      hostio_packet_error (own_buf);
> +      return;
> +    }
> +
> +  ret = pwrite (fd, data, len, offset);
> +
> +  if (ret == -1)
> +    {
> +      hostio_error (own_buf, errno);
> +      free (data);
> +      return;
> +    }
> +
> +  hostio_reply (own_buf, ret);
> +  free (data);
> +}
> +
> +static void
> +handle_close (char *own_buf)
> +{
> +  int fd, ret;
> +  char *p;
> +  struct fd_list **open_fd_p, *old_fd;
> +
> +  p = own_buf + strlen ("vFile:close:");
> +
> +  if (require_int (&p, &fd)
> +      || require_valid_fd (fd)
> +      || require_end (p))
> +    {
> +      hostio_packet_error (own_buf);
> +      return;
> +    }
> +
> +  ret = close (fd);
> +
> +  if (ret == -1)
> +    {
> +      hostio_error (own_buf, errno);
> +      return;
> +    }
> +
> +  open_fd_p = &open_fds;
> +  while (*open_fd_p && (*open_fd_p)->fd != fd)
> +    open_fd_p = &(*open_fd_p)->next;
> +
> +  old_fd = *open_fd_p;
> +  *open_fd_p = (*open_fd_p)->next;
> +  free (old_fd);
> +
> +  hostio_reply (own_buf, ret);
> +}
> +
> +static void
> +handle_unlink (char *own_buf)
> +{
> +  char filename[PATH_MAX];
> +  char *p;
> +  int ret;
> +
> +  p = own_buf + strlen ("vFile:unlink:");
> +
> +  if (require_filename (&p, filename)
> +      || require_end (p))
> +    {
> +      hostio_packet_error (own_buf);
> +      return;
> +    }
> +
> +  ret = unlink (filename);
> +
> +  if (ret == -1)
> +    {
> +      hostio_error (own_buf, errno);
> +      return;
> +    }
> +
> +  hostio_reply (own_buf, ret);
> +}
> +
> +/* Handle all the 'F' file transfer packets.  */
> +
> +int
> +handle_vFile (char *own_buf, int packet_len, int *new_packet_len)
> +{
> +  if (strncmp (own_buf, "vFile:open:", 11) == 0)
> +    handle_open (own_buf);
> +  else if (strncmp (own_buf, "vFile:pread:", 11) == 0)
> +    handle_pread (own_buf, new_packet_len);
> +  else if (strncmp (own_buf, "vFile:pwrite:", 12) == 0)
> +    handle_pwrite (own_buf, packet_len);
> +  else if (strncmp (own_buf, "vFile:close:", 12) == 0)
> +    handle_close (own_buf);
> +  else if (strncmp (own_buf, "vFile:unlink:", 13) == 0)
> +    handle_unlink (own_buf);
> +  else
> +    return 0;
> +
> +  return 1;
> +}
> Index: gdb/remote.c
> ===================================================================
> --- gdb/remote.c.orig	2007-10-26 09:50:44.000000000 -0400
> +++ gdb/remote.c	2007-10-29 16:11:43.000000000 -0400
> @@ -58,6 +58,7 @@
>  #include "gdbcore.h" /* for exec_bfd */
> 
>  #include "remote-fileio.h"
> +#include "gdb/fileio.h"
> 
>  #include "memory-map.h"
> 
> @@ -207,6 +208,10 @@ static void show_remote_protocol_packet_
> 
>  void _initialize_remote (void);
> 
> +/* For "remote".  */
> +
> +static struct cmd_list_element *remote_cmdlist;
> +
>  /* For "set remote" and "show remote".  */
> 
>  static struct cmd_list_element *remote_set_cmdlist;
> @@ -901,6 +906,11 @@ enum {
>    PACKET_Z2,
>    PACKET_Z3,
>    PACKET_Z4,
> +  PACKET_vFile_open,
> +  PACKET_vFile_pread,
> +  PACKET_vFile_pwrite,
> +  PACKET_vFile_close,
> +  PACKET_vFile_unlink,
>    PACKET_qXfer_auxv,
>    PACKET_qXfer_features,
>    PACKET_qXfer_libraries,
> @@ -6276,6 +6286,616 @@ remote_read_description (struct target_o
>    return NULL;
>  }
> 
> +/* Remote file transfer support.  This is host-initiated I/O, not
> +   target-initiated; for target-initiated, see remote-fileio.c.  */
> +
> +/* If *LEFT is at least the length of STRING, copy STRING to
> +   *BUFFER, update *BUFFER to point to the new end of the buffer, and
> +   decrease *LEFT.  Otherwise raise an error.  */
> +
> +static void
> +remote_buffer_add_string (char **buffer, int *left, char *string)
> +{
> +  int len = strlen (string);
> +
> +  if (len > *left)
> +    error (_("Packet too long for target."));
> +
> +  memcpy (*buffer, string, len);
> +  *buffer += len;
> +  *left -= len;
> +
> +  /* NUL-terminate the buffer as a convenience, if there is
> +     room.  */
> +  if (*left)
> +    **buffer = '\0';
> +}
> +
> +/* If *LEFT is large enough, hex encode LEN bytes from BYTES into
> +   *BUFFER, update *BUFFER to point to the new end of the buffer, and
> +   decrease *LEFT.  Otherwise raise an error.  */
> +
> +static void
> +remote_buffer_add_bytes (char **buffer, int *left, const gdb_byte
> *bytes,
> +			 int len)
> +{
> +  if (2 * len > *left)
> +    error (_("Packet too long for target."));
> +
> +  bin2hex (bytes, *buffer, len);
> +  *buffer += 2 * len;
> +  *left -= 2 * len;
> +
> +  /* NUL-terminate the buffer as a convenience, if there is
> +     room.  */
> +  if (*left)
> +    **buffer = '\0';
> +}
> +
> +/* If *LEFT is large enough, convert VALUE to hex and add it to
> +   *BUFFER, update *BUFFER to point to the new end of the buffer, and
> +   decrease *LEFT.  Otherwise raise an error.  */
> +
> +static void
> +remote_buffer_add_int (char **buffer, int *left, ULONGEST value)
> +{
> +  int len = hexnumlen (value);
> +
> +  if (len > *left)
> +    error (_("Packet too long for target."));
> +
> +  hexnumstr (*buffer, value);
> +  *buffer += len;
> +  *left -= len;
> +
> +  /* NUL-terminate the buffer as a convenience, if there is
> +     room.  */
> +  if (*left)
> +    **buffer = '\0';
> +}
> +
> +/* Parse an I/O result packet from BUFFER.  Set RETCODE to the return
> +   value, *REMOTE_ERRNO to the remote error number or zero if none
> +   was included, and *ATTACHMENT to point to the start of the annex
> +   if any.  The length of the packet isn't needed here; there may
> +   be NUL bytes in BUFFER, but they will be after *ATTACHMENT.
> +
> +   Return 0 if the packet could be parsed, -1 if it could not.  If
> +   -1 is returned, the other variables may not be initialized.  */
> +
> +static int
> +remote_hostio_parse_result (char *buffer, int *retcode,
> +			    int *remote_errno, char **attachment)
> +{
> +  char *p, *p2;
> +
> +  *remote_errno = 0;
> +  *attachment = NULL;
> +
> +  if (buffer[0] != 'F')
> +    return -1;
> +
> +  errno = 0;
> +  *retcode = strtol (&buffer[1], &p, 16);
> +  if (errno != 0 || p == &buffer[1])
> +    return -1;
> +
> +  /* Check for ",errno".  */
> +  if (*p == ',')
> +    {
> +      errno = 0;
> +      *remote_errno = strtol (p + 1, &p2, 16);
> +      if (errno != 0 || p + 1 == p2)
> +	return -1;
> +      p = p2;
> +    }
> +
> +  /* Check for ";attachment".  If there is no attachment, the
> +     packet should end here.  */
> +  if (*p == ';')
> +    {
> +      *attachment = p + 1;
> +      return 0;
> +    }
> +  else if (*p == '\0')
> +    return 0;
> +  else
> +    return -1;
> +}
> +
> +/* Send a prepared I/O packet to the target and read its response.
> +   The prepared packet is in the global RS->BUF before this function
> +   is called, and the answer is there when we return.
> +
> +   COMMAND_BYTES is the length of the request to send, which may
> include
> +   binary data.  WHICH_PACKET is the packet configuration to check
> +   before attempting a packet.  If an error occurs, *REMOTE_ERRNO
> +   is set to the error number and -1 is returned.  Otherwise the value
> +   returned by the function is returned.
> +
> +   ATTACHMENT and ATTACHMENT_LEN should be non-NULL if and only if an
> +   attachment is expected; an error will be reported if there's a
> +   mismatch.  If one is found, *ATTACHMENT will be set to point into
> +   the packet buffer and *ATTACHMENT_LEN will be set to the
> +   attachment's length.  */
> +
> +static int
> +remote_hostio_send_command (int command_bytes, int which_packet,
> +			    int *remote_errno, char **attachment,
> +			    int *attachment_len)
> +{
> +  struct remote_state *rs = get_remote_state ();
> +  int ret, bytes_read;
> +  char *attachment_tmp;
> +
> +  if (remote_protocol_packets[which_packet].support == PACKET_DISABLE)
> +    {
> +      *remote_errno = FILEIO_ENOSYS;
> +      return -1;
> +    }
> +
> +  putpkt_binary (rs->buf, command_bytes);
> +  bytes_read = getpkt_sane (&rs->buf, &rs->buf_size, 0);
> +
> +  /* If it timed out, something is wrong.  Don't try to parse the
> +     buffer.  */
> +  if (bytes_read < 0)
> +    {
> +      *remote_errno = FILEIO_EINVAL;
> +      return -1;
> +    }
> +
> +  switch (packet_ok (rs->buf, &remote_protocol_packets[which_packet]))
> +    {
> +    case PACKET_ERROR:
> +      *remote_errno = FILEIO_EINVAL;
> +      return -1;
> +    case PACKET_UNKNOWN:
> +      *remote_errno = FILEIO_ENOSYS;
> +      return -1;
> +    case PACKET_OK:
> +      break;
> +    }
> +
> +  if (remote_hostio_parse_result (rs->buf, &ret, remote_errno,
> +				  &attachment_tmp))
> +    {
> +      *remote_errno = FILEIO_EINVAL;
> +      return -1;
> +    }
> +
> +  /* Make sure we saw an attachment if and only if we expected one.
> */
> +  if ((attachment_tmp == NULL && attachment != NULL)
> +      || (attachment_tmp != NULL && attachment == NULL))
> +    {
> +      *remote_errno = FILEIO_EINVAL;
> +      return -1;
> +    }
> +
> +  /* If an attachment was found, it must point into the packet buffer;
> +     work out how many bytes there were.  */
> +  if (attachment_tmp != NULL)
> +    {
> +      *attachment = attachment_tmp;
> +      *attachment_len = bytes_read - (*attachment - rs->buf);
> +    }
> +
> +  return ret;
> +}
> +
> +/* Open FILENAME on the remote target, using FLAGS and MODE.  Return a
> +   remote file descriptor, or -1 if an error occurs (and set
> +   *REMOTE_ERRNO).  */
> +
> +static int
> +remote_hostio_open (const char *filename, int flags, int mode,
> +		    int *remote_errno)
> +{
> +  struct remote_state *rs = get_remote_state ();
> +  char *p = rs->buf;
> +  int left = get_remote_packet_size () - 1;
> +
> +  remote_buffer_add_string (&p, &left, "vFile:open:");
> +
> +  remote_buffer_add_bytes (&p, &left, (const gdb_byte *) filename,
> +			   strlen (filename));
> +  remote_buffer_add_string (&p, &left, ",");
> +
> +  remote_buffer_add_int (&p, &left, flags);
> +  remote_buffer_add_string (&p, &left, ",");
> +
> +  remote_buffer_add_int (&p, &left, mode);
> +
> +  return remote_hostio_send_command (p - rs->buf, PACKET_vFile_open,
> +				     remote_errno, NULL, NULL);
> +}
> +
> +/* Write up to LEN bytes from WRITE_BUF to FD on the remote target.
> +   Return the number of bytes written, or -1 if an error occurs (and
> +   set *REMOTE_ERRNO).  */
> +
> +static int
> +remote_hostio_pwrite (int fd, const gdb_byte *write_buf, int len,
> +		      ULONGEST offset, int *remote_errno)
> +{
> +  struct remote_state *rs = get_remote_state ();
> +  char *p = rs->buf;
> +  int left = get_remote_packet_size ();
> +  int out_len;
> +
> +  remote_buffer_add_string (&p, &left, "vFile:pwrite:");
> +
> +  remote_buffer_add_int (&p, &left, fd);
> +  remote_buffer_add_string (&p, &left, ",");
> +
> +  remote_buffer_add_int (&p, &left, offset);
> +  remote_buffer_add_string (&p, &left, ",");
> +
> +  p += remote_escape_output (write_buf, len, p, &out_len,
> +			     get_remote_packet_size () - (p - rs->buf));
> +
> +  return remote_hostio_send_command (p - rs->buf, PACKET_vFile_pwrite,
> +				     remote_errno, NULL, NULL);
> +}
> +
> +/* Read up to LEN bytes FD on the remote target into READ_BUF
> +   Return the number of bytes read, or -1 if an error occurs (and
> +   set *REMOTE_ERRNO).  */
> +
> +static int
> +remote_hostio_pread (int fd, gdb_byte *read_buf, int len,
> +		     ULONGEST offset, int *remote_errno)
> +{
> +  struct remote_state *rs = get_remote_state ();
> +  char *p = rs->buf;
> +  char *attachment;
> +  int left = get_remote_packet_size ();
> +  int ret, attachment_len;
> +  int read_len;
> +
> +  remote_buffer_add_string (&p, &left, "vFile:pread:");
> +
> +  remote_buffer_add_int (&p, &left, fd);
> +  remote_buffer_add_string (&p, &left, ",");
> +
> +  remote_buffer_add_int (&p, &left, len);
> +  remote_buffer_add_string (&p, &left, ",");
> +
> +  remote_buffer_add_int (&p, &left, offset);
> +
> +  ret = remote_hostio_send_command (p - rs->buf, PACKET_vFile_pread,
> +				    remote_errno, &attachment,
> +				    &attachment_len);
> +
> +  if (ret < 0)
> +    return ret;
> +
> +  read_len = remote_unescape_input (attachment, attachment_len,
> +				    read_buf, len);
> +  if (read_len != ret)
> +    error (_("Read returned %d, but %d bytes."), ret, (int) read_len);
> +
> +  return ret;
> +}
> +
> +/* Close FD on the remote target.  Return 0, or -1 if an error occurs
> +   (and set *REMOTE_ERRNO).  */
> +
> +static int
> +remote_hostio_close (int fd, int *remote_errno)
> +{
> +  struct remote_state *rs = get_remote_state ();
> +  char *p = rs->buf;
> +  int left = get_remote_packet_size () - 1;
> +
> +  remote_buffer_add_string (&p, &left, "vFile:close:");
> +
> +  remote_buffer_add_int (&p, &left, fd);
> +
> +  return remote_hostio_send_command (p - rs->buf, PACKET_vFile_close,
> +				     remote_errno, NULL, NULL);
> +}
> +
> +/* Unlink FILENAME on the remote target.  Return 0, or -1 if an error
> +   occurs (and set *REMOTE_ERRNO).  */
> +
> +static int
> +remote_hostio_unlink (const char *filename, int *remote_errno)
> +{
> +  struct remote_state *rs = get_remote_state ();
> +  char *p = rs->buf;
> +  int left = get_remote_packet_size () - 1;
> +
> +  remote_buffer_add_string (&p, &left, "vFile:unlink:");
> +
> +  remote_buffer_add_bytes (&p, &left, (const gdb_byte *) filename,
> +			   strlen (filename));
> +
> +  return remote_hostio_send_command (p - rs->buf, PACKET_vFile_unlink,
> +				     remote_errno, NULL, NULL);
> +}
> +
> +static int
> +remote_fileio_errno_to_host (int errnum)
> +{
> +  switch (errnum)
> +    {
> +      case FILEIO_EPERM:
> +        return EPERM;
> +      case FILEIO_ENOENT:
> +        return ENOENT;
> +      case FILEIO_EINTR:
> +        return EINTR;
> +      case FILEIO_EIO:
> +        return EIO;
> +      case FILEIO_EBADF:
> +        return EBADF;
> +      case FILEIO_EACCES:
> +        return EACCES;
> +      case FILEIO_EFAULT:
> +        return EFAULT;
> +      case FILEIO_EBUSY:
> +        return EBUSY;
> +      case FILEIO_EEXIST:
> +        return EEXIST;
> +      case FILEIO_ENODEV:
> +        return ENODEV;
> +      case FILEIO_ENOTDIR:
> +        return ENOTDIR;
> +      case FILEIO_EISDIR:
> +        return EISDIR;
> +      case FILEIO_EINVAL:
> +        return EINVAL;
> +      case FILEIO_ENFILE:
> +        return ENFILE;
> +      case FILEIO_EMFILE:
> +        return EMFILE;
> +      case FILEIO_EFBIG:
> +        return EFBIG;
> +      case FILEIO_ENOSPC:
> +        return ENOSPC;
> +      case FILEIO_ESPIPE:
> +        return ESPIPE;
> +      case FILEIO_EROFS:
> +        return EROFS;
> +      case FILEIO_ENOSYS:
> +        return ENOSYS;
> +      case FILEIO_ENAMETOOLONG:
> +        return ENAMETOOLONG;
> +    }
> +  return -1;
> +}
> +
> +static char *
> +remote_hostio_error (int errnum)
> +{
> +  int host_error = remote_fileio_errno_to_host (errnum);
> +
> +  if (host_error == -1)
> +    error (_("Unknown remote I/O error %d"), errnum);
> +  else
> +    error (_("Remote I/O error: %s"), safe_strerror (host_error));
> +}
> +
> +static void
> +fclose_cleanup (void *file)
> +{
> +  fclose (file);
> +}
> +
> +void
> +remote_file_put (const char *local_file, const char *remote_file, int
> from_tty)
> +{
> +  struct cleanup *back_to;
> +  int retcode, fd, remote_errno, bytes, io_size;
> +  FILE *file;
> +  gdb_byte *buffer;
> +  int bytes_in_buffer;
> +  int saw_eof;
> +  ULONGEST offset;
> +
> +  if (!remote_desc)
> +    error (_("command can only be used with remote target"));
> +
> +  file = fopen (local_file, "rb");
> +  if (file == NULL)
> +    perror_with_name (local_file);
> +  back_to = make_cleanup (fclose_cleanup, file);
> +
> +  fd = remote_hostio_open (remote_file, (FILEIO_O_WRONLY |
> FILEIO_O_CREAT
> +					 | FILEIO_O_TRUNC),
> +			   0700, &remote_errno);
> +  if (fd == -1)
> +    remote_hostio_error (remote_errno);
> +
> +  /* Send up to this many bytes at once.  They won't all fit in the
> +     remote packet limit, so we'll transfer slightly fewer.  */
> +  io_size = get_remote_packet_size ();
> +  buffer = xmalloc (io_size);
> +  make_cleanup (xfree, buffer);
> +
> +  bytes_in_buffer = 0;
> +  saw_eof = 0;
> +  offset = 0;
> +  while (bytes_in_buffer || !saw_eof)
> +    {
> +      if (!saw_eof)
> +	{
> +	  bytes = fread (buffer + bytes_in_buffer, 1, io_size -
> bytes_in_buffer,
> +			 file);
> +	  if (bytes == 0)
> +	    {
> +	      if (ferror (file))
> +		error (_("Error reading %s."), local_file);
> +	      else
> +		{
> +		  /* EOF.  Unless there is something still in the
> +		     buffer from the last iteration, we are done.  */
> +		  saw_eof = 1;
> +		  if (bytes_in_buffer == 0)
> +		    break;
> +		}
> +	    }
> +	}
> +      else
> +	bytes = 0;
> +
> +      bytes += bytes_in_buffer;
> +      bytes_in_buffer = 0;
> +
> +      retcode = remote_hostio_pwrite (fd, buffer, bytes, offset,
> &remote_errno);
> +
> +      if (retcode < 0)
> +	remote_hostio_error (remote_errno);
> +      else if (retcode == 0)
> +	error (_("Remote write of %d bytes returned 0!"), bytes);
> +      else if (retcode < bytes)
> +	{
> +	  /* Short write.  Save the rest of the read data for the next
> +	     write.  */
> +	  bytes_in_buffer = bytes - retcode;
> +	  memmove (buffer, buffer + retcode, bytes_in_buffer);
> +	}
> +
> +      offset += retcode;
> +    }
> +
> +  if (remote_hostio_close (fd, &remote_errno))
> +    remote_hostio_error (remote_errno);
> +
> +  if (from_tty)
> +    printf_filtered (_("Successfully sent file \"%s\".\n"),
> local_file);
> +  do_cleanups (back_to);
> +}
> +
> +void
> +remote_file_get (const char *remote_file, const char *local_file, int
> from_tty)
> +{
> +  struct cleanup *back_to;
> +  int retcode, fd, remote_errno, bytes, io_size;
> +  FILE *file;
> +  gdb_byte *buffer;
> +  ULONGEST offset;
> +
> +  if (!remote_desc)
> +    error (_("command can only be used with remote target"));
> +
> +  fd = remote_hostio_open (remote_file, FILEIO_O_RDONLY, 0,
> &remote_errno);
> +  if (fd == -1)
> +    remote_hostio_error (remote_errno);
> +
> +  file = fopen (local_file, "wb");
> +  if (file == NULL)
> +    perror_with_name (local_file);
> +  back_to = make_cleanup (fclose_cleanup, file);
> +
> +  /* Send up to this many bytes at once.  They won't all fit in the
> +     remote packet limit, so we'll transfer slightly fewer.  */
> +  io_size = get_remote_packet_size ();
> +  buffer = xmalloc (io_size);
> +  make_cleanup (xfree, buffer);
> +
> +  offset = 0;
> +  while (1)
> +    {
> +      bytes = remote_hostio_pread (fd, buffer, io_size, offset,
> &remote_errno);
> +      if (bytes == 0)
> +	/* Success, but no bytes, means end-of-file.  */
> +	break;
> +      if (bytes == -1)
> +	remote_hostio_error (remote_errno);
> +
> +      offset += bytes;
> +
> +      bytes = fwrite (buffer, 1, bytes, file);
> +      if (bytes == 0)
> +	perror_with_name (local_file);
> +    }
> +
> +  if (remote_hostio_close (fd, &remote_errno))
> +    remote_hostio_error (remote_errno);
> +
> +  if (from_tty)
> +    printf_filtered (_("Successfully fetched file \"%s\".\n"),
> remote_file);
> +  do_cleanups (back_to);
> +}
> +
> +void
> +remote_file_delete (const char *remote_file, int from_tty)
> +{
> +  int retcode, remote_errno;
> +
> +  if (!remote_desc)
> +    error (_("command can only be used with remote target"));
> +
> +  retcode = remote_hostio_unlink (remote_file, &remote_errno);
> +  if (retcode == -1)
> +    remote_hostio_error (remote_errno);
> +
> +  if (from_tty)
> +    printf_filtered (_("Successfully deleted file \"%s\".\n"),
> remote_file);
> +}
> +
> +static void
> +remote_put_command (char *args, int from_tty)
> +{
> +  struct cleanup *back_to;
> +  char **argv;
> +
> +  argv = buildargv (args);
> +  if (argv == NULL)
> +    nomem (0);
> +  back_to = make_cleanup_freeargv (argv);
> +  if (argv[0] == NULL || argv[1] == NULL || argv[2] != NULL)
> +    error (_("Invalid parameters to remote put"));
> +
> +  remote_file_put (argv[0], argv[1], from_tty);
> +
> +  do_cleanups (back_to);
> +}
> +
> +static void
> +remote_get_command (char *args, int from_tty)
> +{
> +  struct cleanup *back_to;
> +  char **argv;
> +
> +  argv = buildargv (args);
> +  if (argv == NULL)
> +    nomem (0);
> +  back_to = make_cleanup_freeargv (argv);
> +  if (argv[0] == NULL || argv[1] == NULL || argv[2] != NULL)
> +    error (_("Invalid parameters to remote get"));
> +
> +  remote_file_get (argv[0], argv[1], from_tty);
> +
> +  do_cleanups (back_to);
> +}
> +
> +static void
> +remote_delete_command (char *args, int from_tty)
> +{
> +  struct cleanup *back_to;
> +  char **argv;
> +
> +  argv = buildargv (args);
> +  if (argv == NULL)
> +    nomem (0);
> +  back_to = make_cleanup_freeargv (argv);
> +  if (argv[0] == NULL || argv[1] != NULL)
> +    error (_("Invalid parameters to remote delete"));
> +
> +  remote_file_delete (argv[0], from_tty);
> +
> +  do_cleanups (back_to);
> +}
> +
> +static void
> +remote_command (char *args, int from_tty)
> +{
> +  help_list (remote_cmdlist, "remote ", -1, gdb_stdout);
> +}
> +
>  static void
>  init_remote_ops (void)
>  {
> @@ -6719,6 +7339,21 @@ Show the maximum size of the address (in
>    add_packet_config_cmd (&remote_protocol_packets[PACKET_qSupported],
>  			 "qSupported", "supported-packets", 0);
> 
> +  add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_open],
> +			 "vFile:open", "hostio-open", 0);
> +
> +  add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_pread],
> +			 "vFile:pread", "hostio-pread", 0);
> +
> +  add_packet_config_cmd
> (&remote_protocol_packets[PACKET_vFile_pwrite],
> +			 "vFile:pwrite", "hostio-pwrite", 0);
> +
> +  add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_close],
> +			 "vFile:close", "hostio-close", 0);
> +
> +  add_packet_config_cmd
> (&remote_protocol_packets[PACKET_vFile_unlink],
> +			 "vFile:unlink", "hostio-unlink", 0);
> +
>    /* Keep the old ``set remote Z-packet ...'' working.  Each
> individual
>       Z sub-packet has its own set and show commands, but users may
>       have sets to this variable in their .gdbinit files (or in their
> @@ -6733,6 +7368,24 @@ packets."),
>  				show_remote_protocol_Z_packet_cmd, /* FIXME:
> i18n: Use of remote protocol `Z' packets is %s.  */
>  				&remote_set_cmdlist, &remote_show_cmdlist);
> 
> +  add_prefix_cmd ("remote", class_files, remote_command, _("\
> +Manipulate files on the remote system\n\
> +Transfer files to and from the remote target system."),
> +		  &remote_cmdlist, "remote ",
> +		  0 /* allow-unknown */, &cmdlist);
> +
> +  add_cmd ("put", class_files, remote_put_command,
> +	   _("Copy a local file to the remote system."),
> +	   &remote_cmdlist);
> +
> +  add_cmd ("get", class_files, remote_get_command,
> +	   _("Copy a remote file to the local system."),
> +	   &remote_cmdlist);
> +
> +  add_cmd ("delete", class_files, remote_delete_command,
> +	   _("Delete a remote file."),
> +	   &remote_cmdlist);
> +
>    /* Eventually initialize fileio.  See fileio.c */
>    initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist);
>  }
> Index: gdb/testsuite/gdb.server/file-transfer.exp
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ gdb/testsuite/gdb.server/file-transfer.exp	2007-10-29
> 14:14:09.000000000 -0400
> @@ -0,0 +1,80 @@
> +# This testcase is part of GDB, the GNU debugger.
> +# Copyright 2007 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see
> <http://www.gnu.org/licenses/>.
> +
> +# Test gdbserver monitor commands.
> +
> +load_lib gdbserver-support.exp
> +
> +set testfile "server"
> +set srcfile ${testfile}.c
> +set binfile ${objdir}/${subdir}/${testfile}
> +
> +if { [skip_gdbserver_tests] } {
> +    return 0
> +}
> +
> +if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}"
> executable {debug}] != "" } {
> +    untested file-transfer.exp
> +    return -1
> +}
> +
> +gdb_exit
> +gdb_start
> +gdb_load $binfile
> +gdb_reinitialize_dir $srcdir/$subdir
> +
> +gdbserver_run ""
> +
> +proc test_file_transfer { filename description } {
> +    gdb_test "remote put \"$filename\" down-server" \
> +	"Successfully sent .*" "put $description"
> +    gdb_test "remote get down-server up-server" \
> +	"Successfully fetched .*" "get $description"
> +
> +    if { ![is_remote target] } {
> +	# If we can check the target copy of the file, do that too.
> +	# This should catch symmetric errors in upload and download.
> +	set result [remote_exec host "cmp -s $filename down-server"]
> +	if { [lindex $result 0] == 0 } {
> +	    pass "compare intermediate $description"
> +	} else {
> +	    fail "compare intermediate $description"
> +	}
> +    }
> +
> +    set result [remote_exec host "cmp -s $filename up-server"]
> +    if { [lindex $result 0] == 0 } {
> +	pass "compare $description"
> +    } else {
> +	fail "compare $description"
> +    }
> +
> +    gdb_test "remote delete down-server" \
> +	"Successfully deleted .*" "deleted $description"
> +
> +    if { ![is_remote target] } {
> +	if { ! [remote_file target exists down-server] } {
> +	    pass "verified deleted $description"
> +	} else {
> +	    fail "verified deleted $description"
> +	}
> +    }
> +
> +    catch { file delete up-server }
> +}
> +
> +test_file_transfer "$binfile" "binary file"
> +test_file_transfer "$srcdir/$subdir/transfer.txt" "text file"
> Index: gdb/testsuite/gdb.server/transfer.txt
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ gdb/testsuite/gdb.server/transfer.txt	2007-10-26
10:20:56.000000000
> -0400
> @@ -0,0 +1,12 @@
> +This text file is a test input for GDB's file transfer commands.  It
> +contains some characters which need to be escaped in remote protocol
> +packets, like "*" and "}" and "$" and "#".  Actually, it contains
> +a good sampling of printable characters:
> +
> +!"#$%&'()*+,-./0123456789:;<=>?@
> +ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
> +abcdefghijklmnopqrstuvwxyz{|}~
> +
> +!"#$%&'()*+,-./0123456789:;<=>?@
> +ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
> +abcdefghijklmnopqrstuvwxyz{|}~
> Index: gdb/gdbserver/server.c
> ===================================================================
> --- gdb/gdbserver/server.c.orig	2007-10-26 14:10:14.000000000 -0400
> +++ gdb/gdbserver/server.c	2007-10-29 14:14:09.000000000 -0400
> @@ -772,7 +772,8 @@ err:
> 
>  /* Handle all of the extended 'v' packets.  */
>  void
> -handle_v_requests (char *own_buf, char *status, int *signal)
> +handle_v_requests (char *own_buf, char *status, int *signal,
> +		   int packet_len, int *new_packet_len)
>  {
>    if (strncmp (own_buf, "vCont;", 6) == 0)
>      {
> @@ -786,6 +787,10 @@ handle_v_requests (char *own_buf, char *
>        return;
>      }
> 
> +  if (strncmp (own_buf, "vFile:", 6) == 0
> +      && handle_vFile (own_buf, packet_len, new_packet_len))
> +    return;
> +
>    /* Otherwise we didn't know what packet it was.  Say we didn't
>       understand it.  */
>    own_buf[0] = 0;
> @@ -1218,8 +1223,10 @@ main (int argc, char *argv[])
>  		}
>  	    case 'v':
>  	      /* Extended (long) request.  */
> -	      handle_v_requests (own_buf, &status, &signal);
> +	      handle_v_requests (own_buf, &status, &signal,
> +				 packet_len, &new_packet_len);
>  	      break;
> +
>  	    default:
>  	      /* It is a request we don't understand.  Respond with an
>  	         empty packet so that gdb knows that we don't support
> this
> Index: gdb/Makefile.in
> ===================================================================
> --- gdb/Makefile.in.orig	2007-10-29 10:36:04.000000000 -0400
> +++ gdb/Makefile.in	2007-10-29 16:02:23.000000000 -0400
> @@ -184,7 +184,7 @@ SUBDIR_CLI_CFLAGS=
>  SUBDIR_MI_OBS = \
>  	mi-out.o mi-console.o \
>  	mi-cmds.o mi-cmd-env.o mi-cmd-var.o mi-cmd-break.o mi-cmd-stack.o
> \
> -	mi-cmd-file.o mi-cmd-disas.o mi-symbol-cmds.o \
> +	mi-cmd-file.o mi-cmd-disas.o mi-symbol-cmds.o mi-cmd-target.o \
>  	mi-interp.o \
>  	mi-main.o mi-parse.o mi-getopt.o mi-common.o
>  SUBDIR_MI_SRCS = \
> @@ -192,7 +192,7 @@ SUBDIR_MI_SRCS = \
>  	mi/mi-cmds.c mi/mi-cmd-env.c \
>  	mi/mi-cmd-var.c mi/mi-cmd-break.c mi/mi-cmd-stack.c \
>  	mi/mi-cmd-file.c mi/mi-cmd-disas.c mi/mi-symbol-cmds.c \
> -	mi/mi-interp.c \
> +	mi/mi-cmd-target.c mi/mi-interp.c \
>  	mi/mi-main.c mi/mi-parse.c mi/mi-getopt.c mi/mi-common.c
>  SUBDIR_MI_DEPS =
>  SUBDIR_MI_LDFLAGS=
> @@ -619,6 +619,7 @@ mep_desc_h =	$(OPCODES_SRC)/mep-desc.h
>  mep_opc_h =	$(OPCODES_SRC)/mep-opc.h
>  sh_opc_h = 	$(OPCODES_SRC)/sh-opc.h
>  gdb_callback_h = $(INCLUDE_DIR)/gdb/callback.h
> +gdb_fileio_h = $(INCLUDE_DIR)/gdb/fileio.h
>  gdb_sim_arm_h =	$(INCLUDE_DIR)/gdb/sim-arm.h
>  gdb_sim_frv_h = $(INCLUDE_DIR)/gdb/sim-frv.h
>  gdb_sim_m32c_h = $(INCLUDE_DIR)/gdb/sim-m32c.h
> @@ -2550,7 +2551,8 @@ remote.o: remote.c $(defs_h) $(gdb_strin
>  	$(gdb_stabs_h) $(gdbthread_h) $(remote_h) $(regcache_h)
> $(value_h) \
>  	$(gdb_assert_h) $(event_loop_h) $(event_top_h) $(inf_loop_h) \
>  	$(serial_h) $(gdbcore_h) $(remote_fileio_h) $(solib_h)
> $(observer_h) \
> -	$(cli_decode_h) $(cli_setshow_h) $(memory_map_h)
> $(target_descriptions_h)
> +	$(cli_decode_h) $(cli_setshow_h) $(memory_map_h) \
> +	$(target_descriptions_h) $(gdb_fileio_h)
>  remote-fileio.o: remote-fileio.c $(defs_h) $(gdb_string_h) $(gdbcmd_h)
> \
>  	$(remote_h) $(gdb_fileio_h) $(gdb_wait_h) $(gdb_stat_h) \
>  	$(exceptions_h) $(remote_fileio_h)
> @@ -3134,6 +3136,9 @@ mi-cmd-stack.o: $(srcdir)/mi/mi-cmd-stac
>  	$(value_h) $(mi_cmds_h) $(ui_out_h) $(symtab_h) $(block_h) \
>  	$(stack_h) $(dictionary_h) $(gdb_string_h)
>  	$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/mi/mi-cmd-stack.c
> +mi-cmd-target.o: $(srcdir)/mi/mi-cmd-target.c $(defs_h) $(mi_cmds_h) \
> +	$(mi_getopt_h) $(remote_h)
> +	$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/mi/mi-cmd-target.c
>  mi-cmd-var.o: $(srcdir)/mi/mi-cmd-var.c $(defs_h) $(mi_cmds_h)
> $(ui_out_h) \
>  	$(mi_out_h) $(varobj_h) $(value_h) $(gdb_string_h)
>  	$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/mi/mi-cmd-var.c
> Index: gdb/mi/mi-cmd-target.c
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ gdb/mi/mi-cmd-target.c	2007-10-29 10:36:49.000000000 -0400
> @@ -0,0 +1,100 @@
> +/* MI Command Set - target commands.
> +   Copyright (C) 2007 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or
> modify
> +   it under the terms of the GNU General Public License as published
> by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see
> <http://www.gnu.org/licenses/>.  */
> +
> +#include "defs.h"
> +#include "mi-cmds.h"
> +#include "mi-getopt.h"
> +#include "remote.h"
> +
> +/* Get a file from the target.  */
> +
> +enum mi_cmd_result
> +mi_cmd_target_file_get (char *command, char **argv, int argc)
> +{
> +  int optind = 0;
> +  char *optarg;
> +  const char *remote_file, *local_file;
> +  static struct mi_opt opts[] =
> +  {
> +    { 0, 0, 0 }
> +  };
> +  static const char *prefix = "mi_cmd_target_file_get";
> +
> +  if (mi_getopt (prefix, argc, argv, opts, &optind, &optarg) != -1
> +      || optind != argc - 2)
> +    error (_("mi_cmd_target_file_get: Usage: REMOTE_FILE
> LOCAL_FILE"));
> +
> +  remote_file = argv[optind];
> +  local_file = argv[optind + 1];
> +
> +  remote_file_get (remote_file, local_file, 0);
> +
> +  return MI_CMD_DONE;
> +}
> +
> +/* Send a file to the target.  */
> +
> +enum mi_cmd_result
> +mi_cmd_target_file_put (char *command, char **argv, int argc)
> +{
> +  int optind = 0;
> +  char *optarg;
> +  const char *remote_file, *local_file;
> +  static struct mi_opt opts[] =
> +  {
> +    { 0, 0, 0 }
> +  };
> +  static const char *prefix = "mi_cmd_target_file_put";
> +
> +  if (mi_getopt (prefix, argc, argv, opts, &optind, &optarg) != -1
> +      || optind != argc - 2)
> +    error (_("mi_cmd_target_file_put: Usage: LOCAL_FILE
> REMOTE_FILE"));
> +
> +  local_file = argv[optind];
> +  remote_file = argv[optind + 1];
> +
> +  remote_file_put (local_file, remote_file, 0);
> +
> +  return MI_CMD_DONE;
> +}
> +
> +/* Delete a file on the target.  */
> +
> +enum mi_cmd_result
> +mi_cmd_target_file_delete (char *command, char **argv, int argc)
> +{
> +  int optind = 0;
> +  char *optarg;
> +  const char *remote_file, *local_file;
> +  static struct mi_opt opts[] =
> +  {
> +    { 0, 0, 0 }
> +  };
> +  static const char *prefix = "mi_cmd_target_file_delete";
> +
> +  if (mi_getopt (prefix, argc, argv, opts, &optind, &optarg) != -1
> +      || optind != argc - 1)
> +    error (_("mi_cmd_target_file_delete: Usage: REMOTE_FILE"));
> +
> +  remote_file = argv[optind];
> +
> +  remote_file_delete (remote_file, 0);
> +
> +  return MI_CMD_DONE;
> +}
> +
> Index: gdb/mi/mi-cmds.c
> ===================================================================
> --- gdb/mi/mi-cmds.c.orig	2007-10-29 09:45:32.000000000 -0400
> +++ gdb/mi/mi-cmds.c	2007-10-29 10:38:32.000000000 -0400
> @@ -123,6 +123,9 @@ struct mi_cmd mi_cmds[] =
>    { "target-disconnect", { "disconnect", 0 }, 0 },
>    { "target-download", { NULL, 0 }, mi_cmd_target_download},
>    { "target-exec-status", { NULL, 0 }, NULL, NULL },
> +  { "target-file-delete", { NULL, 0 }, NULL, mi_cmd_target_file_delete
> },
> +  { "target-file-get", { NULL, 0 }, NULL, mi_cmd_target_file_get },
> +  { "target-file-put", { NULL, 0 }, NULL, mi_cmd_target_file_put },
>    { "target-list-available-targets", { NULL, 0 }, NULL, NULL },
>    { "target-list-current-targets", { NULL, 0 }, NULL, NULL },
>    { "target-list-parameters", { NULL, 0 }, NULL, NULL },
> Index: gdb/mi/mi-cmds.h
> ===================================================================
> --- gdb/mi/mi-cmds.h.orig	2007-10-29 10:37:56.000000000 -0400
> +++ gdb/mi/mi-cmds.h	2007-10-29 10:39:03.000000000 -0400
> @@ -100,6 +100,9 @@ extern mi_cmd_argv_ftype mi_cmd_stack_li
>  extern mi_cmd_argv_ftype mi_cmd_stack_select_frame;
>  extern mi_cmd_argv_ftype mi_cmd_symbol_list_lines;
>  extern mi_cmd_args_ftype mi_cmd_target_download;
> +extern mi_cmd_argv_ftype mi_cmd_target_file_get;
> +extern mi_cmd_argv_ftype mi_cmd_target_file_put;
> +extern mi_cmd_argv_ftype mi_cmd_target_file_delete;
>  extern mi_cmd_args_ftype mi_cmd_target_select;
>  extern mi_cmd_argv_ftype mi_cmd_thread_list_ids;
>  extern mi_cmd_argv_ftype mi_cmd_thread_select;
> Index: gdb/remote.h
> ===================================================================
> --- gdb/remote.h.orig	2007-10-29 10:24:08.000000000 -0400
> +++ gdb/remote.h	2007-10-29 10:24:56.000000000 -0400
> @@ -67,4 +67,10 @@ extern void (*deprecated_target_wait_loo
>  void register_remote_g_packet_guess (struct gdbarch *gdbarch, int
> bytes,
>  				     const struct target_desc *tdesc);
> 
> +void remote_file_put (const char *local_file, const char *remote_file,
> +		      int from_tty);
> +void remote_file_get (const char *remote_file, const char *local_file,
> +		      int from_tty);
> +void remote_file_delete (const char *remote_file, int from_tty);
> +
>  #endif
> Index: gdb/testsuite/gdb.mi/mi-file-transfer.exp
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ gdb/testsuite/gdb.mi/mi-file-transfer.exp	2007-10-29
> 14:14:09.000000000 -0400
> @@ -0,0 +1,99 @@
> +# This testcase is part of GDB, the GNU debugger.
> +# Copyright 2007 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see
> <http://www.gnu.org/licenses/>.
> +
> +# Test gdbserver monitor commands.
> +
> +load_lib gdbserver-support.exp
> +load_lib mi-support.exp
> +set MIFLAGS "-i=mi"
> +
> +if { [skip_gdbserver_tests] } {
> +    return 0
> +}
> +
> +set testfile "basics"
> +set srcfile ${testfile}.c
> +set binfile ${objdir}/${subdir}/${testfile}
> +if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}"
> executable {debug}] != "" } {
> +    untested mi-file-transfer.exp
> +    return -1
> +}
> +
> +gdb_exit
> +if [mi_gdb_start] {
> +    continue
> +}
> +mi_delete_breakpoints
> +mi_gdb_reinitialize_dir $srcdir/$subdir
> +mi_gdb_file_cmd ${binfile}
> +
> +proc mi_gdbserver_run { } {
> +    mi_gdb_test "kill" ".*" ""
> +
> +    set res [gdbserver_spawn ""]
> +    set protocol [lindex $res 0]
> +    set gdbport [lindex $res 1]
> +
> +    if { [mi_gdb_target_cmd $protocol $gdbport] != 0 } {
> +	return -1
> +    }
> +
> +    return 0
> +}
> +
> +proc test_file_transfer { filename description } {
> +    mi_gdb_test "-target-file-put \"$filename\" \"down-server\"" \
> +	"\\^done" "put $description"
> +    mi_gdb_test "-target-file-get \"down-server\" \"up-server\"" \
> +	"\\^done" "get $description"
> +
> +    if { ![is_remote target] } {
> +	# If we can check the target copy of the file, do that too.
> +	# This should catch symmetric errors in upload and download.
> +	set result [remote_exec host "cmp -s $filename down-server"]
> +	if { [lindex $result 0] == 0 } {
> +	    pass "compare intermediate $description"
> +	} else {
> +	    fail "compare intermediate $description"
> +	}
> +    }
> +
> +    set result [remote_exec host "cmp -s $filename up-server"]
> +    if { [lindex $result 0] == 0 } {
> +	pass "compare $description"
> +    } else {
> +	fail "compare $description"
> +    }
> +
> +    mi_gdb_test "-target-file-delete \"down-server\"" \
> +	"\\^done" "deleted $description"
> +
> +    if { ![is_remote target] } {
> +	if { ! [remote_file target exists down-server] } {
> +	    pass "verified deleted $description"
> +	} else {
> +	    fail "verified deleted $description"
> +	}
> +    }
> +
> +    catch { file delete up-server }
> +}
> +
> +mi_gdbserver_run
> +
> +test_file_transfer "$binfile" "binary file"
> +
> +mi_gdb_exit
> Index: gdb/NEWS
> ===================================================================
> --- gdb/NEWS.orig	2007-10-29 15:20:55.000000000 -0400
> +++ gdb/NEWS	2007-10-29 16:11:11.000000000 -0400
> @@ -21,11 +21,37 @@ registers on PowerPC targets.
>  * The GDB remote stub, gdbserver, now supports thread debugging on
> GNU/Linux
>  targets even when the libthread_db library is not available.
> 
> +* The GDB remote stub, gdbserver, now supports the new file transfer
> +commands (remote put, remote get, and remote delete).
> +
>  * hppa*64*-*-hpux11* target broken
>    The debugger is unable to start a program and fails with the
> following
>    error: "Error trying to get information about dynamic linker".
>    The gdb-6.7 release is also affected.
> 
> +* New commands
> +
> +remote put
> +remote get
> +remote delete
> +  Transfer files to and from a remote target, and delete remote files.
> +
> +* New MI commands
> +
> +-target-file-put
> +-target-file-get
> +-target-file-delete
> +  Transfer files to and from a remote target, and delete remote files.
> +
> +* New remote packets
> +
> +vFile:open:
> +vFile:close:
> +vFile:pread:
> +vFile:pwrite:
> +vFile:unlink:
> +  Open, close, read, write, and delete files on the remote system.
> +
>  *** Changes in GDB 6.7
> 
>  * Resolved 101 resource leaks, null pointer dereferences, etc. in gdb,





More information about the Gdb-patches mailing list