From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 14772 invoked by alias); 3 Dec 2007 09:51:35 -0000 Received: (qmail 14752 invoked by uid 22791); 3 Dec 2007 09:51:28 -0000 X-Spam-Check-By: sourceware.org Received: from ics.u-strasbg.fr (HELO ics.u-strasbg.fr) (130.79.112.250) by sourceware.org (qpsmtpd/0.31) with ESMTP; Mon, 03 Dec 2007 09:51:14 +0000 Received: from ICSMULLER (laocoon.u-strasbg.fr [130.79.112.72]) by ics.u-strasbg.fr (Postfix) with ESMTP id D593918701E; Mon, 3 Dec 2007 10:55:33 +0100 (CET) From: "Pierre Muller" To: "'Daniel Jacobowitz'" , References: <20071029203042.GA26597@caradoc.them.org> In-Reply-To: <20071029203042.GA26597@caradoc.them.org> Subject: RE: [rfc] Remote file transfer support Date: Mon, 03 Dec 2007 09:51:00 -0000 Message-ID: <000001c83592$071fefd0$155fcf70$@u-strasbg.fr> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Mailer: Microsoft Office Outlook 12.0 Content-Language: en-us Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2007-12/txt/msg00044.txt.bz2 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 > > * 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 > > * 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 > > * 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 > > * 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 > +#include > +#include > +#include > + > +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 > . > + > +# 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 > . */ > + > +#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 > . > + > +# 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,