* 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. * 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. * 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. * gdb.server/file-transfer.exp, gdb.server/transfer.txt, gdb.mi/mi-file-transfer.exp: New.
This commit is contained in:
parent
fba57f8f38
commit
a6b151f187
19 changed files with 1828 additions and 7 deletions
517
gdb/gdbserver/hostio.c
Normal file
517
gdb/gdbserver/hostio.c
Normal file
|
@ -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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue