* [rfc] Remote file transfer support
@ 2007-10-29 20:32 Daniel Jacobowitz
2007-10-31 21:35 ` Eli Zaretskii
2007-12-03 9:51 ` Pierre Muller
0 siblings, 2 replies; 11+ messages in thread
From: Daniel Jacobowitz @ 2007-10-29 20:32 UTC (permalink / raw)
To: gdb-patches
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,
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Remote file transfer support
2007-10-29 20:32 [rfc] Remote file transfer support Daniel Jacobowitz
@ 2007-10-31 21:35 ` Eli Zaretskii
2007-11-30 18:37 ` Daniel Jacobowitz
2007-12-03 9:51 ` Pierre Muller
1 sibling, 1 reply; 11+ messages in thread
From: Eli Zaretskii @ 2007-10-31 21:35 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb-patches
> Date: Mon, 29 Oct 2007 16:30:42 -0400
> From: Daniel Jacobowitz <drow@false.org>
>
> 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.
I have a few comments for this part.
> +@node File Transfer
> +@section Sending files to a remote system
> +@cindex remote target, file transfer
> +@cindex file transfer
Please add a @cindex entry as follows:
@cindex sending files to remote systems
> +for targets accessible through other means, e.g.@: GNU/Linux systems
^^^^^^^^^
I believe we want to use @sc{gnu}/Linux.
> +@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.
GNU coding conventions frown on using "path" in the sense of a file
name or a fully qualified directory name. "Path" is reserved for
$PATH and other lists of directories, like the value of $MANPATH etc.
Also, I think it would help to say "file" at least once, to make sure
the reader understands that @var{hostfile} and other similar
parameters are file names:
Copy file @var{hostfile} from the host system ...
> +Perform a file operation on the target system. For details,
> +@xref{Host I/O Packets}.
@xref produces "See", with a capital `S', which looks as a typo in the
middle of a sentence. To avoid this, use @ref as follows:
For details, see @ref{Host I/O Packets}.
or reverse the order:
@xref{Host I/O Packets}, for details.
> 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.
I didn't understand this sentence. Can you explain what does it mean
to say?
> However, the packets are structured differently, because
> +requests are initiated by @value{GDBN}, and the target's memory is not
> +involved.
This is also unclear.
> For
> +operations which return data, @var{attachment} will be provided a
> +binary buffer.
Did you mean "as a binary buffer", perhaps?
> +The data read should be returned as a binary attachment on success,
> +even if zero bytes were read.
Ehm... how do you express a buffer of zero bytes?
> --- gdb/NEWS.orig 2007-10-29 15:20:55.000000000 -0400
> +++ gdb/NEWS 2007-10-29 16:11:11.000000000 -0400
This part is fine with me.
Thanks.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Remote file transfer support
2007-10-31 21:35 ` Eli Zaretskii
@ 2007-11-30 18:37 ` Daniel Jacobowitz
2007-11-30 21:22 ` Eli Zaretskii
2007-12-01 4:10 ` Pedro Alves
0 siblings, 2 replies; 11+ messages in thread
From: Daniel Jacobowitz @ 2007-11-30 18:37 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
Thanks for all the comments.
On Wed, Oct 31, 2007 at 10:40:17PM +0200, Eli Zaretskii wrote:
> > +for targets accessible through other means, e.g.@: GNU/Linux systems
> ^^^^^^^^^
> I believe we want to use @sc{gnu}/Linux.
I fixed this and the one other GNU/Linux that's crept in.
> > 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.
>
> I didn't understand this sentence. Can you explain what does it mean
> to say?
>
> > However, the packets are structured differently, because
> > +requests are initiated by @value{GDBN}, and the target's memory is not
> > +involved.
>
> This is also unclear.
Is this better?
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. Host I/O uses the same constant values and data structure
layout as the target-initiated File-I/O protocol. However, the
Host I/O packets are structured differently. The target-initiated
protocol relies on target memory to store parameters and buffers.
Host I/O 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 data read should be returned as a binary attachment on success,
> > +even if zero bytes were read.
>
> Ehm... how do you express a buffer of zero bytes?
As a trailing semicolon. Clarified now.
Here's my latest patch, which should correct all the problems you
found. I also fixed cleanup when a write error occurs.
--
Daniel Jacobowitz
CodeSourcery
2007-11-30 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_hostio_close_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-11-30 Daniel Jacobowitz <dan@codesourcery.com>
* gdb.texinfo (Debugging Programs with Multiple Processes): Correct
formatting.
(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 | 25 +
gdb/doc/gdb.texinfo | 241 ++++++++++
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 | 668 +++++++++++++++++++++++++++++
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, 1775 insertions(+), 7 deletions(-)
Index: gdb/doc/gdb.texinfo
===================================================================
--- gdb/doc/gdb.texinfo.orig 2007-11-30 13:34:01.000000000 -0500
+++ gdb/doc/gdb.texinfo 2007-11-30 13:34:40.000000000 -0500
@@ -2480,7 +2480,7 @@ the child process just like any other pr
On some systems, @value{GDBN} provides support for debugging programs that
create additional processes using the @code{fork} or @code{vfork} functions.
Currently, the only platforms with this feature are HP-UX (11.x and later
-only?) and GNU/Linux (kernel version 2.5.60 and later).
+only?) and @sc{gnu}/Linux (kernel version 2.5.60 and later).
By default, when a program forks, @value{GDBN} will continue to debug
the parent process and the child process will run unimpeded.
@@ -12670,6 +12670,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
@@ -12817,6 +12818,38 @@ 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
+@cindex sending files to remote systems
+
+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.@: @sc{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 file @var{hostfile} from the host system (the machine running
+@value{GDBN}) to @var{targetfile} on the target system.
+
+@kindex remote get
+@item remote get @var{targetfile} @var{hostfile}
+Copy file @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
@@ -13153,6 +13186,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
@@ -17349,6 +17401,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
@@ -21296,6 +21349,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 file @var{hostfile} from the host system (the machine running
+@value{GDBN}) to @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 file @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
@@ -22920,6 +23055,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::
@@ -23423,6 +23559,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,
+see @ref{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
@@ -24572,6 +24713,104 @@ 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. Host I/O uses the same constant values and data structure
+layout as the target-initiated File-I/O protocol. However, the
+Host I/O packets are structured differently. The target-initiated
+protocol relies on target memory to store parameters and buffers.
+Host I/O 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} supplies the data as 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}).
+@xref{open} for details of the open flags and mode 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.
+If zero bytes were read, the response should include an empty binary
+attachment (i.e.@: a trailing semicolon). 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-11-30 13:33:36.000000000 -0500
+++ gdb/gdbserver/Makefile.in 2007-11-30 13:34:40.000000000 -0500
@@ -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-11-30 13:33:36.000000000 -0500
+++ gdb/gdbserver/server.h 2007-11-30 13:34:40.000000000 -0500
@@ -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-11-30 13:34:40.000000000 -0500
@@ -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-11-30 13:33:36.000000000 -0500
+++ gdb/remote.c 2007-11-30 13:34:41.000000000 -0500
@@ -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,631 @@ 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);
+}
+
+static void
+remote_hostio_close_cleanup (void *opaque)
+{
+ int fd = *(int *) opaque;
+ int remote_errno;
+
+ remote_hostio_close (fd, &remote_errno);
+}
+
+void
+remote_file_put (const char *local_file, const char *remote_file, int from_tty)
+{
+ struct cleanup *back_to, *close_cleanup;
+ 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);
+
+ close_cleanup = make_cleanup (remote_hostio_close_cleanup, &fd);
+
+ 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;
+ }
+
+ discard_cleanups (close_cleanup);
+ 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, *close_cleanup;
+ 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);
+
+ close_cleanup = make_cleanup (remote_hostio_close_cleanup, &fd);
+
+ 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);
+ }
+
+ discard_cleanups (close_cleanup);
+ 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 +7354,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 +7383,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-11-30 13:34:41.000000000 -0500
@@ -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-11-30 13:34:41.000000000 -0500
@@ -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-11-30 13:33:36.000000000 -0500
+++ gdb/gdbserver/server.c 2007-11-30 13:34:41.000000000 -0500
@@ -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-11-30 13:33:51.000000000 -0500
+++ gdb/Makefile.in 2007-11-30 13:34:41.000000000 -0500
@@ -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=
@@ -675,6 +675,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
@@ -2602,7 +2603,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)
@@ -3186,6 +3188,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-11-30 13:34:41.000000000 -0500
@@ -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-11-30 13:33:36.000000000 -0500
+++ gdb/mi/mi-cmds.c 2007-11-30 13:34:41.000000000 -0500
@@ -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-11-30 13:33:36.000000000 -0500
+++ gdb/mi/mi-cmds.h 2007-11-30 13:34:41.000000000 -0500
@@ -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-11-30 13:33:36.000000000 -0500
+++ gdb/remote.h 2007-11-30 13:34:41.000000000 -0500
@@ -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-11-30 13:34:41.000000000 -0500
@@ -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-11-30 13:33:51.000000000 -0500
+++ gdb/NEWS 2007-11-30 13:36:00.000000000 -0500
@@ -28,6 +28,9 @@ 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".
@@ -37,6 +40,28 @@ targets even when the libthread_db libra
building a single GDB executable that supports multiple remote
target architectures.
+* 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
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Remote file transfer support
2007-11-30 18:37 ` Daniel Jacobowitz
@ 2007-11-30 21:22 ` Eli Zaretskii
2007-11-30 21:50 ` Daniel Jacobowitz
2007-12-01 4:10 ` Pedro Alves
1 sibling, 1 reply; 11+ messages in thread
From: Eli Zaretskii @ 2007-11-30 21:22 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb-patches
> Date: Fri, 30 Nov 2007 13:36:50 -0500
> From: Daniel Jacobowitz <drow@false.org>
> Cc: gdb-patches@sourceware.org
>
> Is this better?
>
> 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. Host I/O uses the same constant values and data structure
> layout as the target-initiated File-I/O protocol. However, the
> Host I/O packets are structured differently. The target-initiated
> protocol relies on target memory to store parameters and buffers.
> Host I/O 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.
Yes, thanks.
> Here's my latest patch, which should correct all the problems you
> found. I also fixed cleanup when a write error occurs.
Thanks, the documentation patch is approved.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Remote file transfer support
2007-11-30 21:22 ` Eli Zaretskii
@ 2007-11-30 21:50 ` Daniel Jacobowitz
0 siblings, 0 replies; 11+ messages in thread
From: Daniel Jacobowitz @ 2007-11-30 21:50 UTC (permalink / raw)
To: gdb-patches
On Fri, Nov 30, 2007 at 11:21:49PM +0200, Eli Zaretskii wrote:
> > Here's my latest patch, which should correct all the problems you
> > found. I also fixed cleanup when a write error occurs.
>
> Thanks, the documentation patch is approved.
Thanks! I've retested this and committed it.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Remote file transfer support
2007-11-30 18:37 ` Daniel Jacobowitz
2007-11-30 21:22 ` Eli Zaretskii
@ 2007-12-01 4:10 ` Pedro Alves
2007-12-01 5:00 ` Daniel Jacobowitz
1 sibling, 1 reply; 11+ messages in thread
From: Pedro Alves @ 2007-12-01 4:10 UTC (permalink / raw)
To: Eli Zaretskii, gdb-patches
Daniel Jacobowitz wrote:
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb/gdbserver/hostio.c 2007-11-30 13:34:40.000000000 -0500
@@ -0,0 +1,517 @@
+/* Host file transfer support for gdbserver.
+ Copyright (C) 2006
Still living in the past, heh? :-)
--
Pedro Alves
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Remote file transfer support
2007-12-01 4:10 ` Pedro Alves
@ 2007-12-01 5:00 ` Daniel Jacobowitz
0 siblings, 0 replies; 11+ messages in thread
From: Daniel Jacobowitz @ 2007-12-01 5:00 UTC (permalink / raw)
To: Pedro Alves; +Cc: Eli Zaretskii, gdb-patches
On Sat, Dec 01, 2007 at 04:10:27AM +0000, Pedro Alves wrote:
> Daniel Jacobowitz wrote:
>
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ gdb/gdbserver/hostio.c 2007-11-30 13:34:40.000000000 -0500
> @@ -0,0 +1,517 @@
> +/* Host file transfer support for gdbserver.
> + Copyright (C) 2006
>
> Still living in the past, heh? :-)
It's like a foreign country...
Thanks, fixed.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [rfc] Remote file transfer support
2007-10-29 20:32 [rfc] Remote file transfer support Daniel Jacobowitz
2007-10-31 21:35 ` Eli Zaretskii
@ 2007-12-03 9:51 ` Pierre Muller
2007-12-03 12:56 ` 'Daniel Jacobowitz'
1 sibling, 1 reply; 11+ messages in thread
From: Pierre Muller @ 2007-12-03 9:51 UTC (permalink / raw)
To: 'Daniel Jacobowitz', gdb-patches
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,
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Remote file transfer support
2007-12-03 9:51 ` Pierre Muller
@ 2007-12-03 12:56 ` 'Daniel Jacobowitz'
2007-12-03 13:41 ` Pierre Muller
0 siblings, 1 reply; 11+ messages in thread
From: 'Daniel Jacobowitz' @ 2007-12-03 12:56 UTC (permalink / raw)
To: Pierre Muller; +Cc: gdb-patches
On Mon, Dec 03, 2007 at 10:51:08AM +0100, Pierre Muller wrote:
> 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?
Sorry, I should have remembered that. Does this patch work?
--
Daniel Jacobowitz
CodeSourcery
2007-12-03 Daniel Jacobowitz <dan@codesourcery.com>
* config.in, configure: Regenerated.
* configure.ac: Check for pread and pwrite.
* hostio.c (handle_pread): Fall back to lseek and read.
(handle_pwrite): Fall back to lseek and write.
Index: config.in
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/config.in,v
retrieving revision 1.19
diff -u -p -r1.19 config.in
--- config.in 19 Sep 2007 14:41:50 -0000 1.19
+++ config.in 3 Dec 2007 12:55:11 -0000
@@ -53,6 +53,9 @@
/* Define to 1 if you have the <netinet/tcp.h> header file. */
#undef HAVE_NETINET_TCP_H
+/* Define to 1 if you have the `pread' function. */
+#undef HAVE_PREAD
+
/* Define to 1 if you have the `pread64' function. */
#undef HAVE_PREAD64
@@ -72,6 +75,9 @@
/* Define if the target supports PTRACE_GETREGS for register access. */
#undef HAVE_PTRACE_GETREGS
+/* Define to 1 if you have the `pwrite' function. */
+#undef HAVE_PWRITE
+
/* Define to 1 if you have the <sgtty.h> header file. */
#undef HAVE_SGTTY_H
Index: configure
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure,v
retrieving revision 1.30
diff -u -p -r1.30 configure
--- configure 15 Oct 2007 19:58:17 -0000 1.30
+++ configure 3 Dec 2007 12:55:11 -0000
@@ -3099,7 +3099,9 @@ fi
done
-for ac_func in pread64
+
+
+for ac_func in pread pwrite pread64
do
as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
echo "$as_me:$LINENO: checking for $ac_func" >&5
Index: configure.ac
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure.ac,v
retrieving revision 1.18
diff -u -p -r1.18 configure.ac
--- configure.ac 15 Oct 2007 19:58:17 -0000 1.18
+++ configure.ac 3 Dec 2007 12:55:11 -0000
@@ -41,7 +41,7 @@ AC_CHECK_HEADERS(sgtty.h termio.h termio
errno.h fcntl.h signal.h sys/file.h malloc.h dnl
sys/ioctl.h netinet/in.h sys/socket.h netdb.h dnl
netinet/tcp.h arpa/inet.h sys/wait.h)
-AC_CHECK_FUNCS(pread64)
+AC_CHECK_FUNCS(pread pwrite pread64)
have_errno=no
AC_MSG_CHECKING(for errno)
Index: hostio.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/hostio.c,v
retrieving revision 1.3
diff -u -p -r1.3 hostio.c
--- hostio.c 1 Dec 2007 05:00:04 -0000 1.3
+++ hostio.c 3 Dec 2007 12:55:11 -0000
@@ -377,7 +377,13 @@ handle_pread (char *own_buf, int *new_pa
}
data = malloc (len);
+#ifdef HAVE_PREAD
ret = pread (fd, data, len, offset);
+#else
+ ret = lseek (fd, offset, SEEK_SET);
+ if (ret != -1)
+ ret = read (fd, data, len);
+#endif
if (ret == -1)
{
@@ -419,7 +425,13 @@ handle_pwrite (char *own_buf, int packet
return;
}
+#ifdef HAVE_PWRITE
ret = pwrite (fd, data, len, offset);
+#else
+ ret = lseek (fd, offset, SEEK_SET);
+ if (ret != -1)
+ ret = write (fd, data, len);
+#endif
if (ret == -1)
{
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [rfc] Remote file transfer support
2007-12-03 12:56 ` 'Daniel Jacobowitz'
@ 2007-12-03 13:41 ` Pierre Muller
2007-12-16 21:54 ` 'Daniel Jacobowitz'
0 siblings, 1 reply; 11+ messages in thread
From: Pierre Muller @ 2007-12-03 13:41 UTC (permalink / raw)
To: 'Daniel Jacobowitz'; +Cc: gdb-patches
> Sorry, I should have remembered that. Does this patch work?
Yes, this fixes the compilation problem for mingw32, thanks.
I did not have the time to test the produced executable...
Pierre
> --
> Daniel Jacobowitz
> CodeSourcery
>
> 2007-12-03 Daniel Jacobowitz <dan@codesourcery.com>
>
> * config.in, configure: Regenerated.
> * configure.ac: Check for pread and pwrite.
> * hostio.c (handle_pread): Fall back to lseek and read.
> (handle_pwrite): Fall back to lseek and write.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Remote file transfer support
2007-12-03 13:41 ` Pierre Muller
@ 2007-12-16 21:54 ` 'Daniel Jacobowitz'
0 siblings, 0 replies; 11+ messages in thread
From: 'Daniel Jacobowitz' @ 2007-12-16 21:54 UTC (permalink / raw)
To: Pierre Muller; +Cc: gdb-patches
On Mon, Dec 03, 2007 at 02:41:07PM +0100, Pierre Muller wrote:
> > Sorry, I should have remembered that. Does this patch work?
>
> Yes, this fixes the compilation problem for mingw32, thanks.
> I did not have the time to test the produced executable...
Thanks. I finally checked it in.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2007-12-16 21:50 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-10-29 20:32 [rfc] Remote file transfer support Daniel Jacobowitz
2007-10-31 21:35 ` Eli Zaretskii
2007-11-30 18:37 ` Daniel Jacobowitz
2007-11-30 21:22 ` Eli Zaretskii
2007-11-30 21:50 ` Daniel Jacobowitz
2007-12-01 4:10 ` Pedro Alves
2007-12-01 5:00 ` Daniel Jacobowitz
2007-12-03 9:51 ` Pierre Muller
2007-12-03 12:56 ` 'Daniel Jacobowitz'
2007-12-03 13:41 ` Pierre Muller
2007-12-16 21:54 ` 'Daniel Jacobowitz'
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox